home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 326.lha / MRARPFile / MRARPFile.c < prev    next >
C/C++ Source or Header  |  1989-12-28  |  21KB  |  678 lines

  1. /*  File support routines to complement ARP.
  2.  *  Author: Mark R. Rinfret
  3.  *          Usenet:     mrr@amanpt1.Newport.RI.US
  4.  *          BIX:        markr
  5.  *          CIS:        72017, 136 (good luck!)
  6.  *
  7.  *          348 Indian Avenue
  8.  *          Portsmouth, RI 02871
  9.  *          401-846-7639 (home)
  10.  *          401-849-9390 (work)       
  11.  *
  12.  *  This package was written primarily because of _one_ missing element
  13.  *  in ARP: FGets. ARGH! ARPFFFT! It extends ARP by adding buffering to
  14.  *  the basic file type (FileHandle) and defining a new type, named
  15.  *  ARPFileHandle (hope this is OK with the ARP guys). Also, I've used the
  16.  *  convention of embedding ARP (vs. Arp in MicroSmith's stuff) in all type
  17.  *  and function names to (hopefully) avoid naming collisions.
  18.  *
  19.  *  This package (as far as I am concerned) is public domain. Use it any
  20.  *  way you like. Some comments on the "MR" prefix: it isn't short for
  21.  *  "Mister", it comprises the initials of my first and last names;
  22.  *  I use it primarily to avoid name collisions with stuff by other
  23.  *  authors. If any other authors whose initials are "MR" start doing
  24.  *  this, we'll probably have to appeal to Electronic Arts to dole out
  25.  *  "author codes" along the lines of those issued for IFF :-). I hereby
  26.  *  stake a claim to 'MRR_'.
  27.  *
  28.  *  A word of warning:
  29.  *
  30.  *  DO NOT INTERMIX STANDARD AMIGADOS FILE SUPPORT CALLS WITH CALLS TO
  31.  *  THIS PACKAGE ON FILES USING THIS FILE METHOD!
  32.  *
  33.  *  Obviously, the system doesn't know about the buffering added here
  34.  *  and will cause unpredictable results (unless, of course, you
  35.  *  take care to maintain the ARPFileHandle information).
  36.  */
  37.  
  38. /*  Note: define DEBUG if you want the test program at the end. */
  39.  
  40. /*  Embedded documentation template (cut & paste): */
  41.  
  42. /*  FUNCTION
  43.  *
  44.  *  SYNOPSIS
  45.  *
  46.  *  DESCRIPTION
  47.  *
  48.  */
  49.  
  50. #include "MRARPFile.h"
  51. #include <exec/memory.h>
  52. #include <functions.h>
  53.  
  54. #ifndef min
  55. #define min(a,b) ((a) <= (b) ? (a) : (b))
  56. #define max(a,b) ((a) >= (b) ? (a) : (b))
  57. #endif
  58.  
  59. /* StoreTracker is my attempt to fix an apparent bug in ARP 1.3. I have
  60.  * found that the LastTracker kludge (via IoErr()) doesn't work reliably.
  61.  * StoreTracker simply stuffs A1 into D0 and returns . It *MUST* be
  62.  * called immediately after any ARP call which allocates a tracker to
  63.  * assure that the value is correct.
  64.  */
  65.  
  66. struct DefaultTracker *StoreTracker();  /* asm routine */
  67.  
  68. static LONG FillARPFileBuffer();
  69.  
  70. /*  FUNCTION
  71.  *      FGetsARP - get string from a buffered ARP file.
  72.  *
  73.  *  SYNOPSIS
  74.  *      char *FGetsARP(s, length, file)
  75.  *                     UBYTE         *s;
  76.  *                     LONG           length;
  77.  *                     ARPFileHandle *file;
  78.  *
  79.  *  DESCRIPTION
  80.  *      FGetsARP models the behavior of the "standard" fgets, except that
  81.  *      it is used with a buffered ARP file. The data is read from <file>
  82.  *      and transfered to string <s>. Up to length-1 characters will be
  83.  *      read. Reading is also terminated upon receipt of a newline
  84.  *      or detection of end-of-file. 
  85.  *
  86.  *      If the <file> was opened without a buffer, one of MaxInputBuf
  87.  *      bytes will be allocated.
  88.  *
  89.  *      If end of file or an error is detected, the return value will be
  90.  *      NULL. Otherwise, the return value will be <s>. <s> will be 
  91.  *      terminated with a null byte.
  92.  */
  93. char *
  94. FGetsARP(s, length, file)
  95.     char            *s;
  96.     LONG            length;
  97.     ARPFileHandle   *file;
  98. {
  99.     LONG    bytesNeeded = length - 1;
  100.     LONG    bytesRead = 0;
  101.     char    c;
  102.     char    *s1 = s;
  103.     struct DefaultTracker *tracker;
  104.  
  105.     /* Set string initially empty to protect user from failure to check
  106.      * return value.
  107.      */
  108.     *s1 = '\0';    
  109.     if (file->mode != MODE_OLDFILE)
  110.         file->lastError = ERROR_READ_PROTECTED;
  111.          
  112.     if (file->lastError) {
  113. dangit:
  114.         return NULL;
  115.     }
  116.     if (! file->buf ) {                 /* Ohmigosh! No buffer? */
  117.         file->buf = ArpAllocMem(MaxInputBuf, MEMF_CLEAR|MEMF_PUBLIC);
  118.         tracker = StoreTracker();
  119.         if (! file->buf ) {
  120.             file->lastError = ERROR_NO_FREE_STORE;
  121.             goto dangit;
  122.         }
  123.         file->bufSize = MaxInputBuf;    /* bufLength, bufPos are zero. */
  124.         file->bufTracker = tracker;
  125.     }
  126.     while (bytesNeeded) {
  127.         if (file->bufPos >= file->bufLength) {
  128.             if (FillARPFileBuffer(file) < 0) goto dangit;
  129.             if (file->bufLength == 0) break;
  130.         }
  131.         c = file->buf[file->bufPos++];
  132.         ++bytesRead;
  133.         if (c == '\n') break;
  134.         *s1++ = c;
  135.         --bytesNeeded; 
  136.     }
  137.     *s1 = '\0';
  138.     return (bytesRead ? s : NULL);
  139. }
  140.  
  141. /*  FUNCTION
  142.  *      FillARPFileBuffer - read data into ARPFile buffer.
  143.  *
  144.  *  SYNOPSIS
  145.  *      static LONG FillARPFileBuffer(file)
  146.  *                                    ARPFileHandle *file;
  147.  *
  148.  *  DESCRIPTION
  149.  *      Attempt to fill the buffer associated with <file> by reading
  150.  *      data from the associated external file. The return value will
  151.  *      be one of the following:
  152.  *          >0  => number of bytes read
  153.  *           0  => end of file
  154.  *          -1  => a Bad Thing happened (error code in file->lastError) 
  155.  *
  156.  *      Note: this is a local routine and is thus declared as "static".
  157.  *      Please inform the author if this is a hardship.
  158.  */
  159. static LONG
  160. FillARPFileBuffer(file)
  161.     ARPFileHandle *file;
  162. {
  163.     /* Remember where we were. The user might want to try error
  164.      * recovery. Of course, this probably isn't enough info, but
  165.      * it's a start.
  166.      */
  167.     file->lastPosition = Seek(file->fh, 0L, OFFSET_CURRENT);
  168.     file->bufPos = 0;
  169.     file->bufLength = Read(file->fh, file->buf, file->bufSize);
  170.     if (file->bufLength == -1) {    /* We got trubble! */
  171.         file->lastError = IoErr();
  172.     }
  173.     else if (file->bufLength == 0) {
  174.         file->endOfFile = TRUE;
  175.     }
  176.     return file->bufLength;
  177.  
  178. /*  FUNCTION
  179.  *      FlushARPFileBuffer - write file buffer contents to disk.
  180.  *
  181.  *  SYNOPSIS
  182.  *      static LONG FlushARPFileBuffer(file)
  183.  *                                     ARPFileHandle *file;
  184.  *  DESCRIPTION
  185.  *      FlushARPFileBuffer writes the contents of <file>'s buffer
  186.  *      (if any) to disk and resets the buffer information. The
  187.  *      return value may be any of:
  188.  *
  189.  *          >0 =>   number of bytes written
  190.  *           0 =>   nothing in buffer
  191.  *          <0 =>   negated error code
  192.  *
  193.  *      Note: it is assumed that this function will only be used locally
  194.  *      and therefore need not be public. If you disagree, please contact
  195.  *      the author.
  196.  */
  197.  
  198. static LONG
  199. FlushARPFileBuffer(file)
  200.     ARPFileHandle *file;
  201. {
  202.     LONG    bytesWritten = 0;
  203.  
  204.     /* This operation is only allowed for output files. */
  205.  
  206.     if (file->mode != MODE_NEWFILE) {
  207.         file->lastError = ERROR_WRITE_PROTECTED;
  208. badstuff:
  209.         return -file->lastError;
  210.     }
  211.  
  212.     if (file->lastError) goto badstuff; /* Residual error? */
  213.  
  214.     if (file->bufLength) {
  215.         file->lastPosition = Seek(file->fh, 0L, OFFSET_CURRENT);
  216.         bytesWritten = Write(file->fh, file->buf, file->bufLength);
  217.         if (bytesWritten != file->bufLength) {
  218.             file->lastError = IoErr();
  219.             goto badstuff;
  220.         }
  221.         else {
  222.             file->bufLength = 0;
  223.             file->bufPos = 0;
  224.         }
  225.     }
  226.     return bytesWritten;
  227. }
  228.  
  229. /*  FUNCTION
  230.  *      FPutsARP - write a string to a buffered ARP file.
  231.  *
  232.  *  SYNOPSIS
  233.  *      LONG FPutsARP(s, file)
  234.  *                    char          *s;
  235.  *                    ARPFileHandle *file;
  236.  *
  237.  *  DESCRIPTION
  238.  *      FPutsARP writes the contents of string <s> to the specified <file>.
  239.  *      If successful, it returns 0. On failure, it returns the negated
  240.  *      system error code. 
  241.  */
  242. LONG
  243. FPutsARP(s, file)
  244.     char          *s;
  245.     ARPFileHandle *file;
  246. {
  247.     LONG    bytesLeft, bytesUsed;
  248.     char    *s1 = s;
  249.  
  250.     if (file->mode != MODE_NEWFILE) 
  251.         file->lastError = ERROR_WRITE_PROTECTED;
  252.  
  253.     if (file->lastError) {
  254. shucks:
  255.         return -file->lastError;
  256.     }
  257.  
  258.     bytesLeft = strlen(s);
  259.  
  260.     /* Attempt to be smart about this transfer. Copy the string to the
  261.      * buffer in chunks. There is a possibility that the string is bigger
  262.      * than the size of the buffer.
  263.      */
  264.     while (bytesLeft) {
  265.         if (file->bufPos >= file->bufSize) {
  266.             if (FlushARPFileBuffer(file) <= 0) goto shucks;
  267.         }
  268.         bytesUsed = min(file->bufSize - file->bufPos, bytesLeft);
  269.         CopyMem(s1, &file->buf[file->bufPos], bytesUsed);
  270.         s1 += bytesUsed;
  271.         file->bufLength = (file->bufPos += bytesUsed);
  272.         bytesLeft -= bytesUsed;
  273.     }
  274.     return 0;
  275. }
  276.  
  277. /*  FUNCTION
  278.  *      ReadARPFile - read from a buffered ARP file.
  279.  *
  280.  *  SYNOPSIS
  281.  *      LONG ReadARPFile(file, buffer, length)
  282.  *                       ARPFile *file;
  283.  *                       UBYTE   *buffer;
  284.  *                       LONG    length;
  285.  *
  286.  *  DESCRIPTION
  287.  *      ReadARPFile attempts to read <length> bytes from <file>, transferring
  288.  *      the data to <buffer>. 
  289.  *
  290.  *      The return value may be any of the following:
  291.  *
  292.  *          >0      number of bytes transferred
  293.  *          0       end of file
  294.  *         <0       negated error code
  295.  *
  296.  *      Note: if the lastError field of the <file> descriptor contains a
  297.  *            non-zero value, its negated value will be returned and no
  298.  *            attempt will be made to read the file. If you attempt error
  299.  *            recovery, you must clear this field to zero.
  300.  */
  301. LONG
  302. ReadARPFile(file, buffer, length)
  303.     ARPFileHandle   *file;
  304.     UBYTE           *buffer;
  305.     LONG            length;
  306. {
  307.     LONG            bytesLeft;
  308.     LONG            bytesRead = 0;
  309.     LONG            needBytes = length;
  310.     LONG            pos = 0;
  311.     LONG            usedBytes = 0;
  312.  
  313.     /* Prevent read if this file opened for writing. */
  314.  
  315.     if (file->mode != MODE_OLDFILE)
  316.         file->lastError = ERROR_READ_PROTECTED;
  317.  
  318.     if (file->lastError) {          /* Have residual error? */
  319. boofar:
  320.         return -file->lastError;
  321.     }
  322.  
  323.     if ( ! file->buf ) {            /* No buffer? */
  324.         bytesRead = Read(file->fh, buffer, length);
  325.         if (bytesRead == -1) {
  326.             file->lastError = IoErr();
  327.             goto boofar;
  328.         }
  329.     }
  330.     else while (needBytes) {
  331.         if (file->bufLength - file->bufPos <= 0) {
  332.             if (FillARPFileBuffer(file) == -1) goto boofar;
  333.             if (file->bufLength == 0) break;
  334.         }
  335.         bytesLeft = file->bufLength - file->bufPos;
  336.         usedBytes = min(bytesLeft, length);
  337.         CopyMem(&file->buf[file->bufPos], &buffer[pos], usedBytes);
  338.         file->bufPos += usedBytes;
  339.         pos += usedBytes;
  340.         bytesRead += usedBytes;
  341.         needBytes -= usedBytes;
  342.     }
  343.     return bytesRead;   
  344. }
  345.  
  346. /*  FUNCTION
  347.  *      CloseARPFile - close buffered ARP file.
  348.  *
  349.  *  SYNOPSIS
  350.  *      LONG CloseARPFile(file)
  351.  *                        ARPFileHandle *file;
  352.  *
  353.  *  DESCRIPTION
  354.  *      CloseARPFile closes the file described by <file> which MUST have
  355.  *      been opened by OpenARPFile. It releases all tracked items
  356.  *      associated with <file>, as well.
  357.  *
  358.  *      The return value is only meaningful for output files. If, upon
  359.  *      flushing the buffer, a write error is detected, a system error
  360.  *      code (ERROR_DISK_FULL, etc.) will be returned.
  361.  */
  362.  
  363. LONG
  364. CloseARPFile(file)
  365.     ARPFileHandle *file;
  366. {
  367.     LONG        result = 0;
  368.  
  369.     if (file) {                     /* Just in case... */
  370.         if (file->fileTracker) {
  371.             /* Any left-over stuff in the buffer? If so, we must flush
  372.              * it to disk. However, if an error was detected in the
  373.              * previous operation, punt. 
  374.              */
  375.             if ( ( file->mode == MODE_NEWFILE) && 
  376.                    ! file->lastError &&
  377.                    file->bufLength) {
  378.                 if (Write(file->fh, file->buf, file->bufLength) !=
  379.                     file->bufLength)
  380.                     result = IoErr();
  381.             }
  382.             FreeTrackedItem(file->fileTracker);
  383.         }
  384.  
  385.         if (file->bufTracker)
  386.             FreeTrackedItem(file->bufTracker);
  387.  
  388.         FreeTrackedItem(file->myTracker);
  389.     }
  390.     return result;
  391. }
  392.  
  393. /*  FUNCTION
  394.  *      OpenARPFile - open a buffered ARP file
  395.  *
  396.  *  SYNOPSIS
  397.  *      struct MRARPFile *OpenARPFile(name, accessMode, bytes)
  398.  *                                    char *name;
  399.  *                                    LONG accessMode, bytes;
  400.  *
  401.  *  DESCRIPTION
  402.  *      OpenARPFile opens the file <name>, with the given <accessMode>
  403.  *      (MODE_OLDFILE, MODE_NEWFILE only!) for buffered access. The
  404.  *      size of the local buffer is specified by <bytes>.
  405.  *
  406.  *      A zero value for <bytes> is OK and is sometimes appropriate, as
  407.  *      when performing file copy operations, etc. However, if a file
  408.  *      opened with a zero length buffer is then passed to the
  409.  *      FGetsARP function, "spontaneous buffer allocation" will occur.
  410.  *      No biggy, really, just something you should know. The ARP constant,
  411.  *      MaxInputBuf will be used for the buffer size, in this case.
  412.  *
  413.  *      If successful, a pointer to the file tracking structure is 
  414.  *      returned. Otherwise, the return value will be NULL.
  415.  *
  416.  *      OpenARPFile uses full resource tracking for all information
  417.  *      associated with the file.
  418.  *
  419.  */
  420.  
  421. ARPFileHandle *
  422. OpenARPFile(name, accessMode, bytes)
  423.     char *name;
  424.     LONG accessMode, bytes;
  425. {
  426.     BPTR                  fh;
  427.     ARPFileHandle         *theFile = NULL;
  428.     struct DefaultTracker *lastTracker;
  429.  
  430.     /* This package does not support READ/WRITE access! */
  431.  
  432.     if ( (accessMode != MODE_OLDFILE) && (accessMode != MODE_NEWFILE))
  433.         return NULL;
  434.  
  435.     /* 
  436.      * Note: I'm using ArpAllocMem vs. ArpAlloc here because it appears
  437.      * that ArpAlloc gives me a bad tracker pointer (?).
  438.      */
  439.     theFile = ArpAllocMem((LONG) sizeof(ARPFileHandle), 
  440.                           MEMF_PUBLIC|MEMF_CLEAR);
  441.     lastTracker = StoreTracker();
  442.     if (theFile) {
  443.         theFile->myTracker = lastTracker;
  444.         theFile->mode = accessMode;
  445.         fh = ArpOpen(name, accessMode);
  446.         lastTracker = StoreTracker();
  447.         if (!fh) {
  448. fungu:
  449.             CloseARPFile(theFile);  /* Don't worry - it's "smart". */
  450.             theFile = NULL;
  451.         }
  452.         theFile->fileTracker = lastTracker;
  453.         theFile->fh = fh;
  454.         if ( bytes) {               /* Does user want a buffer? */
  455.             theFile->buf = ArpAllocMem(bytes, MEMF_PUBLIC|MEMF_CLEAR);
  456.             lastTracker = StoreTracker();
  457.             if (!theFile->buf) goto fungu;
  458.             theFile->bufSize = bytes;
  459.             theFile->bufTracker = lastTracker;
  460.         }
  461.     }
  462.     return theFile;  
  463. }
  464.  
  465. /*  FUNCTION
  466.  *      SeekARPFile - move to new logical position in file.
  467.  *
  468.  *  SYNOPSIS
  469.  *      LONG SeekARPFile(file, position, mode)
  470.  *                       ARPFileHandle *file;
  471.  *                       LONG           position;
  472.  *                       LONG           mode;
  473.  *
  474.  *  DESCRIPTION
  475.  *      SeekARPFile attempts to position the <file> to a new logical
  476.  *      position or report it's current position. The <position>
  477.  *      parameter represets a signed offset. The <mode> parameter may
  478.  *      be one of:
  479.  *
  480.  *          OFFSET_BEGINNING (-1) => from beginning of file
  481.  *          OFFSET_CURRENT (0)    => from current position
  482.  *          OFFSET_END (-1)       => from end of file
  483.  *
  484.  *      On output files, the current buffer contents, if any, are
  485.  *      written to disk.
  486.  *
  487.  *      The return value will either be a positive displacement >=0) or
  488.  *      a negated system error code.
  489.  */
  490. LONG
  491. SeekARPFile(file, position, mode)
  492.             ARPFileHandle *file;
  493.             LONG           position;
  494.             LONG           mode;
  495. {
  496.     LONG    newPosition;
  497.  
  498.     if (file->mode == MODE_NEWFILE && file->bufLength) {
  499.         if (FlushARPFileBuffer(file) < 0) {
  500. farboo:
  501.             return -file->lastError;
  502.         }
  503.     }
  504.     /* Remember our last position. */
  505.     file->lastPosition = Seek(file, 0L, OFFSET_CURRENT);
  506.     if ((newPosition = Seek(file->fh, position, mode)) == -1) {
  507.         file->lastError = IoErr();
  508.         goto farboo;
  509.     }
  510.     return newPosition;
  511. }  
  512.  
  513. /*  FUNCTION
  514.  *      WriteARPFile - write data to a buffered ARP file.
  515.  *
  516.  *  SYNOPSIS
  517.  *      LONG WriteARPFile(file, buffer, length)
  518.  *                        ARPFileHandle *file;
  519.  *                        UBYTE         *buffer;
  520.  *                        LONG          length;
  521.  *
  522.  *  DESCRIPTION
  523.  *      WriteARPFile attempts to write <length> bytes from <buffer> to
  524.  *      the buffered ARP file, <file>. The file MUST have been opened
  525.  *      with OpenARPFile for MODE_NEWFILE access.
  526.  *
  527.  *      WriteARPFile will not write to a file if the lastError field is
  528.  *      non-zero, or if the file was opened for input.
  529.  *
  530.  *      If successful, WriteARPFile will return the <length> parameter.
  531.  *      Otherwise, it will return a negated system error code.
  532.  */
  533. LONG
  534. WriteARPFile(file, buffer, length)
  535.     ARPFileHandle   *file;
  536.     UBYTE           *buffer;
  537.     LONG            length;
  538. {
  539.     LONG    bufferBytes;
  540.     LONG    bytesLeft = length;
  541.     LONG    pos = 0;
  542.     LONG    usedBytes = 0;
  543.  
  544.     if (file->mode != MODE_NEWFILE)
  545.         file->lastError = ERROR_WRITE_PROTECTED;
  546.  
  547.     if (file->lastError) {           /* Catches mode and residual errors. */
  548. sumbidge:
  549.         return -file->lastError;
  550.     }
  551.  
  552.     if ( ! file->buf ) {             /* No buffer? */
  553.         if (Write(file->fh, buffer, length) != length) {
  554.             file->lastError = IoErr();
  555.             goto sumbidge;
  556.         }
  557.     }
  558.     else while (bytesLeft) {
  559.         /* Need to flush the file's buffer? */
  560.         if (file->bufPos >= file->bufSize) {
  561.             if (FlushARPFileBuffer(file) < 0) {
  562.                 goto sumbidge;
  563.             }
  564.         }
  565.         bufferBytes = file->bufSize - file->bufPos;
  566.         usedBytes = min(bufferBytes, bytesLeft);
  567.         CopyMem(&buffer[pos], &file->buf[file->bufPos], usedBytes);
  568.         file->bufLength = (file->bufPos += usedBytes);
  569.         pos += usedBytes;
  570.         bytesLeft -= usedBytes;
  571.     }
  572.     return length;
  573. }
  574.  
  575. #ifdef DEBUG
  576. /*  Short test program. */
  577.  
  578. struct ArpBase          *ArpBase;
  579. struct IntuitionBase    *IntuitionBase;
  580. struct GfxBase          *GfxBase;
  581.  
  582. ARPFileHandle *
  583. GetFile(prompt, mode, bufferSize)
  584.     char    *prompt;
  585.     LONG    mode, bufferSize;
  586. {
  587.     BOOL            keepTrying;
  588.     ARPFileHandle   *theFile = NULL;
  589.     char            fileName[MaxInputBuf+1];
  590.  
  591.     for (keepTrying = TRUE; keepTrying; ) {
  592.         Puts("\nA null response aborts this test.");
  593.         Puts(prompt);
  594.         ReadLine(fileName);
  595.         if ( ! *fileName ) break;   /* Null response? */
  596.         theFile = OpenARPFile(fileName, mode, bufferSize);
  597.         if (theFile == NULL) {
  598.             Puts("That file failed to open. Try again.\n");
  599.         }
  600.         else
  601.             keepTrying = FALSE;
  602.     }
  603.     return theFile;
  604. }
  605.  
  606. main()
  607. {
  608. #define BUFFER_SIZE     16384L
  609.  
  610. static char line[81];
  611.  
  612.     UBYTE           *buffer;
  613.     LONG            count;
  614.     ARPFileHandle   *file1, *file2;
  615.     int             keepTesting;
  616.     LONG            totalBytes;
  617.  
  618.     ArpBase = (struct ArpBase *) OpenLibrary(ArpName, ArpVersion);
  619.     if (ArpBase == NULL) {
  620.         exit(1);
  621.     }
  622.     GfxBase = (struct GfxBase *) ArpBase->GfxBase;
  623.     IntuitionBase = (struct IntuitionBase *) ArpBase->IntuiBase;
  624.  
  625.     Puts("MRARPFile package test.\n\n");
  626.  
  627. FGetsARPTest:
  628.     Puts("FGetsARP test - echo file to console.");
  629.     file1 = GetFile("Enter the name of a text file:", MODE_OLDFILE, 4192L);
  630.     if ( file1) {
  631.         while (FGetsARP(line, (LONG) sizeof(line), file1))
  632.             Puts(line);
  633.         CloseARPFile(file1);
  634.     }
  635.  
  636. CopyFileTest:
  637.     buffer = ArpAllocMem(BUFFER_SIZE, MEMF_PUBLIC);
  638.     if ( ! buffer ) {
  639.         Printf("I couldn't allocate a %ld byte buffer!\n", BUFFER_SIZE);
  640.         goto die;
  641.     }
  642.     for (keepTesting = TRUE; keepTesting; ) {
  643.         Puts("ReadARPFile/WriteARPFile test.");
  644.         Puts("For this test, I will copy a file.");
  645.         file1 = GetFile("Enter input file name:", MODE_OLDFILE, 512L);
  646.         if (!file1) break;
  647.         file2 = GetFile("Enter output file name:", MODE_NEWFILE, 8192L);
  648.         if (!file2) {
  649.             CloseARPFile(file1);
  650.             break;
  651.         }
  652.         keepTesting = FALSE;        /* This is it... */
  653.         totalBytes = 0;
  654.         while (count = ReadARPFile(file1, buffer, BUFFER_SIZE)) {
  655.             totalBytes += count;
  656.             if (WriteARPFile(file2, buffer, count) != count) {
  657.                 Printf("Error on output file: %ld\n", file2->lastError);
  658.                 break;
  659.             }
  660.             if (count < BUFFER_SIZE) break;
  661.         }
  662.         if (file1->lastError)
  663.             Printf("Error on input file: %ld\n", file1->lastError);
  664.         CloseARPFile(file1);
  665.         CloseARPFile(file2);
  666.         Printf("Bytes transferred: %ld\n", totalBytes);
  667.         /* Note: leave the buffer for ARP to free up. You should check
  668.          * available memory before/after running this test to make sure
  669.          * that ARP does proper housecleaning.
  670.          */
  671.     }
  672. die:
  673.     CloseLibrary(ArpBase);
  674.     exit(0);
  675. }
  676.  
  677. #endif