home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / os2prgc.zip / file_io.c < prev    next >
C/C++ Source or Header  |  1995-03-06  |  16KB  |  469 lines

  1. /*****************************************************************************
  2.  
  3.   FILE_IO.C -- Sample code for handling FILE I/O with Dos...() API functions.
  4.   Copyright (C) 1993,94,95 by Craig Morrison, All Rights Reserved.
  5.  
  6.   You may use this code in your own projects, regardless of renumeration.
  7.   All I ask is that you prominently display the above copyright notice.
  8.  
  9.   Should you need assistance, I can be contacted at the following addresses:
  10.  
  11.         Fidonet:        Craig Morrison, 1:201/60@fidonet.org
  12.         Internet:       cam@wpc.cioe.com
  13.         Post:           Craig Morrison
  14.                         1316 Ferry St.
  15.                         Lafayette, IN 47901-1533
  16.                         USA
  17.  
  18.   NOTES:
  19.  
  20.     You'll notice the complete lack of any references to run-time
  21.     library functions. This was done on purpose so that *I* could
  22.     control what happens when a thread gets killed. This package
  23.     contains just about everything you'll need to do port/file I/O,
  24.     string manipulation and ordinal number conversions.
  25.  
  26.     The basic thrust behind this module is to allow you to have a
  27.     basic working set of functions that mock C "streams". In a nutshell
  28.     you use these functions just like the C stream functions, with one
  29.     important exception; THESE ARE NOT C STREAMS. They operate on file
  30.     handles that the Dos...() file API functions understand. What I have
  31.     done is put a translation layer in between the API functions and
  32.     you. When you open a file, you get a "handle" for the file which is
  33.     actually an offset into a lookup table of the real file handles and
  34.     a lookup table containing the buffers used during file I/O.
  35.  
  36.     When a file is opened, the offset into the lookup tables is returned
  37.     with 1 added to it. This allows for easy testing of a valid return,
  38.     if 0 is returned something went wrong and the file didn't get
  39.     opened. Don't be concerned with what is returned other than to make
  40.     sure you get a non-zero value. All the functions in this module take
  41.     the above behaviour into account and adjust the offset for you. When
  42.     calling a function after opening a file, use the returned value as
  43.     is and everything will work just as planned.
  44.  
  45.     Another important difference is that there is no "mode" for an open
  46.     file. BlockRead(), BlockWrite(), LineRead() and LineWrite() calls
  47.     can be intermixed at will with an open file using this file I/O
  48.     interface.
  49.  
  50.     As of this writing, the buffers are only used when the LineRead()
  51.     and LineWrite() functions are called. They are used to provide
  52.     someplace in memory to do the line ending translations. LineRead()
  53.     translates "\r\n" and "\n\r" pairs to "\n". LineWrite() translates
  54.     "\n" to an "\r\n" pair for output. This behaviour should be familiar
  55.     to anyone who has used files with the C stream functions in text
  56.     mode.
  57.  
  58.     DosRead() usually returns a 0 error result and 0 bytes read to
  59.     indicate an EOF condition, when using LineRead() to read lines
  60.     ERROR_HANDLE_EOF will be returned when the end of file is reached.
  61.  
  62.     DosWrite() usually returns a 0 error code and 0 bytes written to
  63.     indicate a disk full condition, when using LineWrite() to write
  64.     lines ERROR_HANDLE_EOF is returned to signal disk full.
  65.  
  66.     When time permits I will implement buffered I/O, but right now it
  67.     isn't that important of an issue considering caching is an integral
  68.     part of OS/2.
  69.  
  70.     Several of the functions in this module work as wrappers around the
  71.     actual Dos...() API functions. The wrapper functions use many of the
  72.     same parameters that the corresponding Dos...() functions do, for
  73.     those parameters that are the same please refer to the Control
  74.     Program Reference Guide that comes with the OS/2 ToolKit or the
  75.     equivalent documentation for your compiler for their possible
  76.     values.
  77.  
  78.     The real reason for all this is so that I can use DosCreateThread()
  79.     and DosKillThread() to manipulate threads and not have to worry
  80.     about the run-time library having a fit because it couldn't clean up
  81.     a thread after I was done with it. Granted, it isn't very portable
  82.     but at this point in time portability isn't an issue. I'm hedging my
  83.     bets with OS/2 and I am in this for the long haul.
  84.  
  85.  *****************************************************************************/
  86. #define INCL_BASE
  87. #define INCL_NOPM
  88. #define INCL_DOS
  89. #define INCL_DOSERRORS
  90.  
  91. #include <os2.h>
  92. #include <stdio.h>
  93. #include "str.h"
  94. #include "dosmem.h"
  95. #include "file_io.h"
  96.  
  97. typedef unsigned char *PBYTE;
  98.  
  99. #define ALLOC_BUFFER_PAGE_SIZE      (4096*2)    /* we may not use all the */
  100.                                                 /* buffer at once, but I  */
  101.                                                 /* wanted a safety margin */
  102.                                                 /* to avoid traps easily. */
  103. #define MAX_FILES                   15
  104.  
  105. static HMTX ioMutexSem = NULLHANDLE;
  106. static INT openFiles = 0;
  107. static PBYTE fileBuffers[MAX_FILES] = {NULL, NULL, NULL, NULL, NULL,
  108.                                        NULL, NULL, NULL, NULL, NULL,
  109.                                        NULL, NULL, NULL, NULL, NULL};
  110. static HFILE fileTable[MAX_FILES] =   {0, 0, 0, 0, 0,
  111.                                        0, 0, 0, 0, 0,
  112.                                        0, 0, 0, 0, 0};
  113.  
  114. /*****************************************************************************
  115.  
  116.   Initializes our file I/O handler by creating a mutex semaphore that is
  117.   used to ensure no changes are made to the lookup tables that could be
  118.   hazardous and setting all the lookup table values to a known state.
  119.  
  120.   ** CALL THIS FUNCTION BEFORE DOING ANY FILE I/O WITH THESE FUNCTIONS! **
  121.  
  122.  *****************************************************************************/
  123. BOOL InitFileIO(VOID)
  124. {
  125.     INT i;
  126.  
  127.     if (ioMutexSem)
  128.         return TRUE;
  129.     else if (!DosCreateMutexSem(NULL, &ioMutexSem, DC_SEM_SHARED, FALSE))
  130.     {
  131.         openFiles = 0;
  132.         for (i=0; i<MAX_FILES; i++)
  133.         {
  134.             fileBuffers[i] = NULL;
  135.             fileTable[i] = NULLHANDLE;
  136.         }
  137.         return TRUE;
  138.     }
  139.     else
  140.         return FALSE;
  141. }
  142.  
  143. /*****************************************************************************
  144.  
  145.   Closes all open files, frees associated memory and closes the mutex
  146.   semaphore used to manage resource contention.
  147.  
  148.   This function should be called just before the last thread in your code
  149.   terminates so, if nothing else, the mutex semaphore can be closed and the
  150.   system resources for it can be free'd.
  151.  
  152.  *****************************************************************************/
  153. VOID CleanUpFileIO(VOID)
  154. {
  155.     CloseAllFiles();
  156.     DosCloseMutexSem(ioMutexSem);
  157. }
  158.  
  159. /*****************************************************************************
  160.  
  161.   Find an empty slot in the lookup tables and return that offset+1.
  162.  
  163.  *****************************************************************************/
  164. INT FindFileOffset(VOID)
  165. {
  166.     INT i;
  167.  
  168.     DosRequestMutexSem(ioMutexSem, SEM_INDEFINITE_WAIT);
  169.     for(i=0; i<MAX_FILES; i++)
  170.         if (!fileTable[i])
  171.             break;
  172.     DosReleaseMutexSem(ioMutexSem);
  173.  
  174.     return(i<MAX_FILES ? i+1 : 0);
  175. }
  176.  
  177. /*****************************************************************************
  178.  
  179.   Returns the "real" file handle associated with the passed offset.
  180.  
  181.  *****************************************************************************/
  182. HFILE GetHandleFromInt(INT fIndex)
  183. {
  184.     if ((fIndex) && (fIndex<=MAX_FILES))
  185.         return( fileTable[fIndex-1] );
  186.     else
  187.         return NULLHANDLE;
  188. }
  189.  
  190. /*****************************************************************************
  191.  
  192.   Returns the buffer pointer associated with the passed offset.
  193.  
  194.  *****************************************************************************/
  195. PVOID GetPtrFromInt(INT fIndex)
  196. {
  197.     if ((fIndex) && (fIndex<=MAX_FILES))
  198.         return( fileBuffers[fIndex-1] );
  199.     else
  200.         return NULL;
  201. }
  202.  
  203. /*****************************************************************************
  204.  
  205.   Open a file with the passed parameters.
  206.  
  207.   The file handle and an allocated buffer are inserted into their
  208.   respective tables.
  209.  
  210.   The table offset+1 is returned, 0 signifies an error.
  211.  
  212.  *****************************************************************************/
  213. INT OpenFile(PSZ pszFileName, PULONG pulAction, ULONG cbFile,
  214.              ULONG ulAttribute, ULONG fsOpenFlags, ULONG fsOpenMode,
  215.              PEAOP2 peaop2)
  216. {
  217.     HFILE f = NULLHANDLE;
  218.     INT fIndex = 0;
  219.     APIRET rc;
  220.  
  221.     if (openFiles<MAX_FILES)
  222.     {
  223.         rc = DosOpen(pszFileName, &f, pulAction, cbFile, ulAttribute,
  224.                      fsOpenFlags, fsOpenMode, peaop2);
  225.         if (!rc)
  226.         {
  227.             if ((fIndex = FindFileOffset())!=0)
  228.             {
  229.                 DosRequestMutexSem(ioMutexSem, SEM_INDEFINITE_WAIT);
  230.  
  231.                 fileBuffers[fIndex-1] = AllocMem(ALLOC_BUFFER_PAGE_SIZE,
  232.                                                  PAG_WRITE | PAG_COMMIT);
  233.                 if (fileBuffers[fIndex-1])
  234.                 {
  235.                     setmem(fileBuffers[fIndex-1], 0, ALLOC_BUFFER_PAGE_SIZE);
  236.                     fileTable[fIndex-1] = f;
  237.                     openFiles++;
  238.                 }
  239.                 else
  240.                 {
  241.                     DosClose(f);
  242.                     fIndex = 0;
  243.                 }
  244.                 DosReleaseMutexSem(ioMutexSem);
  245.             }
  246.             else
  247.                 DosClose(f);
  248.         }
  249.     }
  250.     return(fIndex);
  251. }
  252.  
  253. /*****************************************************************************
  254.  
  255.   Closes a file and frees the buffers associated with it.
  256.  
  257.  *****************************************************************************/
  258. VOID CloseFile(INT fIndex)
  259. {
  260.     DosRequestMutexSem(ioMutexSem, SEM_INDEFINITE_WAIT);
  261.     if (fileTable[fIndex-1])
  262.     {
  263.         DosClose(fileTable[fIndex-1]);
  264.         fileTable[fIndex-1] = 0;
  265.         FreeMem(fileBuffers[fIndex-1]);
  266.         fileBuffers[fIndex-1] = NULL;
  267.         openFiles--;
  268.     }
  269.     DosReleaseMutexSem(ioMutexSem);
  270. }
  271.  
  272. /*****************************************************************************
  273.  
  274.   Closes all open files and frees their buffers.
  275.  
  276.  *****************************************************************************/
  277. VOID CloseAllFiles(VOID)
  278. {
  279.     INT i;
  280.     HFILE f;
  281.  
  282.     DosRequestMutexSem(ioMutexSem, SEM_INDEFINITE_WAIT);
  283.     for(i=1; i<=MAX_FILES; i++)
  284.     {
  285.         if ((f = GetHandleFromInt(i))!=NULLHANDLE)
  286.         {
  287.             DosClose(f);
  288.             FreeMem(GetPtrFromInt(i));
  289.             openFiles--;
  290.         }
  291.     }
  292.     DosReleaseMutexSem(ioMutexSem);
  293. }
  294.  
  295. /*****************************************************************************
  296.  
  297.   Performs a block read from a file.
  298.  
  299.  *****************************************************************************/
  300. APIRET BlockRead(INT fIndex, PVOID p, PULONG cbBuffer)
  301. {
  302.     if (GetHandleFromInt(fIndex))
  303.         return(DosRead(GetHandleFromInt(fIndex), p, *cbBuffer, cbBuffer));
  304.     else
  305.         return ERROR_INVALID_HANDLE;
  306. }
  307.  
  308. /*****************************************************************************
  309.  
  310.   Performs a block write to a file.
  311.  
  312.  *****************************************************************************/
  313. APIRET BlockWrite(INT fIndex, PVOID p, PULONG cbBuffer)
  314. {
  315.     if (GetHandleFromInt(fIndex))
  316.         return(DosWrite(GetHandleFromInt(fIndex), p, *cbBuffer, cbBuffer));
  317.     else
  318.         return ERROR_INVALID_HANDLE;
  319. }
  320.  
  321. /*****************************************************************************
  322.  
  323.   Reads a single line from a file, performing line ending translations.
  324.  
  325.     "This is a line.\r\n"
  326.  
  327.   becomes:
  328.  
  329.     "This is a line.\n"
  330.  
  331.  *****************************************************************************/
  332. APIRET LineRead(INT fIndex, PSZ pszBuf)
  333. {
  334.     PSZ pz = (PSZ)GetPtrFromInt(fIndex);
  335.     PSZ p;
  336.     HFILE f = GetHandleFromInt(fIndex);
  337.     ULONG cbRead, cbRetreat;
  338.     APIRET rc;
  339.  
  340.     if (f)
  341.     {
  342.         rc = DosRead(f, pz, ALLOC_BUFFER_PAGE_SIZE, &cbRead);
  343.         if (!rc)
  344.         {
  345.             if (cbRead)
  346.             {
  347.                 if (cbRead==ALLOC_BUFFER_PAGE_SIZE)
  348.                     pz[cbRead-1] = 0;
  349.                 else
  350.                     pz[cbRead] = 0;
  351.  
  352.                 p = charinstr(pz, '\n');
  353.                 if (!p)
  354.                     p = charinstr(pz, '\r');
  355.                 *p = 0;
  356.                 cbRetreat = cbRead-(lenstr(pz)+1);
  357.                 if (cbRetreat)
  358.                     DosSetFilePtr(f, -cbRetreat, FILE_CURRENT, &cbRead);
  359.                 if (lenstr(pz))
  360.                 {
  361.                     p--;
  362.                     if (*p=='\r')
  363.                         *p = '\n';
  364.                     else if (*p=='\n')
  365.                         *p = 0;
  366.                 }
  367.                 cpystr(pszBuf, pz);
  368.                 return 0;
  369.             }
  370.             else
  371.             {
  372.                 pszBuf[0] = 0;
  373.                 return ERROR_HANDLE_EOF;
  374.             }
  375.         }
  376.         else
  377.             return(rc);
  378.     }
  379.  
  380.     return ERROR_INVALID_HANDLE;
  381. }
  382.  
  383. /*****************************************************************************
  384.  
  385.   Performs the magic for LineWrite() and translates `\n' to `\r\n'.
  386.  
  387.  *****************************************************************************/
  388. PSZ TranslateEndings(PSZ pz)
  389. {
  390.     PSZ t;
  391.  
  392.     t = pz;
  393.     while ((pz = charinstr(pz, '\n'))!=NULL)
  394.     {
  395.         movemem(pz+1, pz, lenstr(pz)+1);
  396.         *pz = '\r';
  397.         pz += 2;
  398.     }
  399.     return(t);
  400. }
  401.  
  402. /*****************************************************************************
  403.  
  404.   Writes text line(s) to a file performing line ending translations.
  405.  
  406.     "This is a line.\n"
  407.  
  408.   becomes:
  409.  
  410.     "This is a line.\r\n"
  411.  
  412.   on output to the file.
  413.  
  414.   You can write multiple lines at once with this function by concatenating
  415.   them together.
  416.  
  417.  *****************************************************************************/
  418. APIRET LineWrite(INT fIndex, PSZ pszBuf)
  419. {
  420.     PSZ pz = (PSZ)GetPtrFromInt(fIndex);
  421.     HFILE f = GetHandleFromInt(fIndex);
  422.     ULONG cbWritten,
  423.           cbLen = lenstr(pszBuf);
  424.     APIRET rc;
  425.  
  426.     if (f)
  427.     {
  428.         rc = 0;
  429.         while ((!rc) && (cbLen>3072))
  430.         {
  431.             cpystrn(pz, pszBuf, 1024);
  432.             pszBuf += 1024;
  433.             TranslateEndings(pz);
  434.             rc = DosWrite(f, pz, lenstr(pz), &cbWritten);
  435.             if ((!rc) && (!cbWritten))
  436.                 return ERROR_HANDLE_EOF;
  437.         }
  438.         if (!rc)
  439.         {
  440.             cpystr(pz, pszBuf);
  441.             TranslateEndings(pz);
  442.             rc = DosWrite(f, pz, lenstr(pz), &cbWritten);
  443.             if ((!rc) && (!cbWritten))
  444.                 return ERROR_HANDLE_EOF;
  445.  
  446.             return(rc);
  447.         }
  448.         else
  449.             return(rc);
  450.     }
  451.  
  452.     return ERROR_INVALID_HANDLE;
  453. }
  454.  
  455. /*****************************************************************************
  456.  
  457.   Positions the file pointer for a file.
  458.  
  459.  *****************************************************************************/
  460. APIRET FileSeek(INT fIndex, LONG *plMove, ULONG ulDirection)
  461. {
  462.     HFILE f = GetHandleFromInt(fIndex);
  463.  
  464.     if (f)
  465.         return(DosSetFilePtr(f, *plMove, ulDirection, (PULONG)plMove));
  466.     else
  467.         return ERROR_INVALID_HANDLE;
  468. }
  469.