home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD v1.2 / amidev_cd_12.iso / reference / amiga_mail_vol2 / ii-107 / asyncio.c < prev    next >
C/C++ Source or Header  |  1996-01-30  |  22KB  |  667 lines

  1. ;/* ASyncIO.c - Execute me to compile with SAS/C 6.56
  2. sc data=near nominc strmer streq nostkchk saveds ign=73 AsyncIO.c
  3. quit
  4. */
  5.  
  6. /* (c)  Copyright 1993 Commodore-Amiga, Inc.   All rights reserved. */
  7. /* The information contained herein is subject to change without    */
  8. /* notice, and is provided "as is" without warranty of any kind,    */
  9. /* either expressed or implied.  The entire risk as to the use of   */
  10. /* this information is assumed by the user.                         */
  11.  
  12. #include <exec/types.h>
  13. #include <exec/memory.h>
  14. #include <dos/dos.h>
  15. #include <dos/dosextens.h>
  16.  
  17. #include <clib/exec_protos.h>
  18. #include <clib/dos_protos.h>
  19.  
  20. #include <pragmas/exec_pragmas.h>
  21. #include <pragmas/dos_pragmas.h>
  22.  
  23. #include "asyncio.h"
  24.  
  25.  
  26. /*****************************************************************************/
  27.  
  28.  
  29. extern struct Library *DOSBase;
  30. extern struct Library *SysBase;
  31.  
  32.  
  33. /*****************************************************************************/
  34.  
  35.  
  36. /* this macro lets us long-align structures on the stack */
  37. #define D_S(type,name) char a_##name[sizeof(type)+3]; \
  38.                        type *name = (type *)((LONG)(a_##name+3) & ~3);
  39.  
  40.  
  41. /*****************************************************************************/
  42.  
  43.  
  44. /* send out an async packet to the file system. */
  45. static VOID SendPacket(struct AsyncFile *file, APTR arg2)
  46. {
  47.     file->af_Packet.sp_Pkt.dp_Port = &file->af_PacketPort;
  48.     file->af_Packet.sp_Pkt.dp_Arg2 = (LONG)arg2;
  49.     PutMsg(file->af_Handler, &file->af_Packet.sp_Msg);
  50.     file->af_PacketPending = TRUE;
  51. }
  52.  
  53.  
  54. /*****************************************************************************/
  55.  
  56.  
  57. /* this function waits for a packet to come back from the file system. If no
  58.  * packet is pending, state from the previous packet is returned. This ensures
  59.  * that once an error occurs, it state is maintained for the rest of the life
  60.  * of the file handle.
  61.  *
  62.  * This function also deals with IO errors, bringing up the needed DOS
  63.  * requesters to let the user retry an operation or cancel it.
  64.  */
  65. static LONG WaitPacket(struct AsyncFile *file)
  66. {
  67. LONG bytes;
  68.  
  69.     if (file->af_PacketPending)
  70.     {
  71.         /* mark packet as no longer pending since we are going to get it */
  72.         file->af_PacketPending = FALSE;
  73.  
  74.         while (TRUE)
  75.         {
  76.             /* This enables signalling when a packet comes back to the port */
  77.             file->af_PacketPort.mp_Flags = PA_SIGNAL;
  78.  
  79.             /* Wait for the packet to come back, and remove it from the message
  80.              * list. Since we know no other packets can come in to the port, we can
  81.              * safely use Remove() instead of GetMsg(). If other packets could come in,
  82.              * we would have to use GetMsg(), which correctly arbitrates access in such
  83.              * a case
  84.              */
  85.             Remove((struct Node *)WaitPort(&file->af_PacketPort));
  86.  
  87.             /* set the port type back to PA_IGNORE so we won't be bothered with
  88.              * spurious signals
  89.              */
  90.             file->af_PacketPort.mp_Flags = PA_IGNORE;
  91.  
  92.             bytes = file->af_Packet.sp_Pkt.dp_Res1;
  93.             if (bytes >= 0)
  94.             {
  95.                 /* packet didn't report an error, so bye... */
  96.                 return(bytes);
  97.             }
  98.  
  99.             /* see if the user wants to try again... */
  100.             if (ErrorReport(file->af_Packet.sp_Pkt.dp_Res2,
  101.                             REPORT_STREAM,
  102.                             file->af_File,NULL))
  103.                 return(-1);
  104.  
  105.             /* user wants to try again, resend the packet */
  106.             SendPacket(file,file->af_Buffers[file->af_CurrentBuf]);
  107.         }
  108.     }
  109.  
  110.     /* last packet's error code, or 0 if packet was never sent */
  111.     SetIoErr(file->af_Packet.sp_Pkt.dp_Res2);
  112.  
  113.     return(file->af_Packet.sp_Pkt.dp_Res1);
  114. }
  115.  
  116.  
  117. /*****************************************************************************/
  118.  
  119.  
  120. /* this function puts the packet back on the message list of our
  121.  * message port.
  122.  */
  123. static VOID RequeuePacket(struct AsyncFile *file)
  124. {
  125.     AddHead(&file->af_PacketPort.mp_MsgList,&file->af_Packet.sp_Msg.mn_Node);
  126.     file->af_PacketPending = TRUE;
  127. }
  128.  
  129.  
  130. /*****************************************************************************/
  131.  
  132.  
  133. /* this function records a failure from a synchronous DOS call into the
  134.  * packet so that it gets picked up by the other IO routines in this module
  135.  */
  136. VOID RecordSyncFailure(struct AsyncFile *file)
  137. {
  138.     file->af_Packet.sp_Pkt.dp_Res1 = -1;
  139.     file->af_Packet.sp_Pkt.dp_Res2 = IoErr();
  140. }
  141.  
  142.  
  143. /*****************************************************************************/
  144.  
  145.  
  146. struct AsyncFile *OpenAsync(const STRPTR fileName, UBYTE accessMode, LONG bufferSize)
  147. {
  148. struct AsyncFile  *file;
  149. struct FileHandle *fh;
  150. BPTR               handle;
  151. BPTR               lock;
  152. LONG               blockSize;
  153. D_S(struct InfoData,infoData);
  154.  
  155.     handle = NULL;
  156.     file   = NULL;
  157.     lock   = NULL;
  158.  
  159.     if (accessMode == MODE_READ)
  160.     {
  161.         if (handle = Open(fileName,MODE_OLDFILE))
  162.             lock = DupLockFromFH(handle);
  163.     }
  164.     else
  165.     {
  166.         if (accessMode == MODE_WRITE)
  167.         {
  168.             handle = Open(fileName,MODE_NEWFILE);
  169.         }
  170.         else if (accessMode == MODE_APPEND)
  171.         {
  172.             /* in append mode, we open for writing, and then seek to the
  173.              * end of the file. That way, the initial write will happen at
  174.              * the end of the file, thus extending it
  175.              */
  176.  
  177.             if (handle = Open(fileName,MODE_READWRITE))
  178.             {
  179.                 if (Seek(handle,0,OFFSET_END) < 0)
  180.                 {
  181.                     Close(handle);
  182.                     handle = NULL;
  183.                 }
  184.             }
  185.         }
  186.  
  187.         /* we want a lock on the same device as where the file is. We can't
  188.          * use DupLockFromFH() for a write-mode file though. So we get sneaky
  189.          * and get a lock on the parent of the file
  190.          */
  191.         if (handle)
  192.             lock = ParentOfFH(handle);
  193.     }
  194.  
  195.     if (handle)
  196.     {
  197.         /* if it was possible to obtain a lock on the same device as the
  198.          * file we're working on, get the block size of that device and
  199.          * round up our buffer size to be a multiple of the block size.
  200.          * This maximizes DMA efficiency.
  201.          */
  202.  
  203.         blockSize = 512;
  204.         if (lock)
  205.         {
  206.             if (Info(lock,infoData))
  207.             {
  208.                 blockSize = infoData->id_BytesPerBlock;
  209.                 bufferSize =
  210.                     (((bufferSize + blockSize - 1) / blockSize) * blockSize) * 2;
  211.             }
  212.             UnLock(lock);
  213.         }
  214.  
  215.         /* now allocate the ASyncFile structure, as well as the read buffers.
  216.          * Add 15 bytes to the total size in order to allow for later
  217.          * quad-longword alignement of the buffers
  218.          */
  219.  
  220.         if (file = AllocVec(sizeof(struct AsyncFile) + bufferSize + 15,MEMF_ANY))
  221.         {
  222.             file->af_File      = handle;
  223.             file->af_ReadMode  = (accessMode == MODE_READ);
  224.             file->af_BlockSize = blockSize;
  225.  
  226.             /* initialize the ASyncFile structure. We do as much as we can here,
  227.              * in order to avoid doing it in more critical sections
  228.              *
  229.              * Note how the two buffers used are quad-longword aligned. This
  230.              * helps performance on 68040 systems with copyback cache. Aligning
  231.              * the data avoids a nasty side-effect of the 040 caches on DMA.
  232.              * Not aligning the data causes the device driver to have to do
  233.              * some magic to avoid the cache problem. This magic will generally
  234.              * involve flushing the CPU caches. This is very costly on an 040.
  235.              * Aligning things avoids the need for magic, at the cost of at
  236.              * most 15 bytes of ram.
  237.              */
  238.  
  239.             fh                     = BADDR(file->af_File);
  240.             file->af_Handler       = fh->fh_Type;
  241.             file->af_BufferSize    = bufferSize / 2;
  242.             file->af_Buffers[0]
  243.                 = (APTR)(((ULONG)file + sizeof(struct AsyncFile) + 15) & 0xfffffff0);
  244.             file->af_Buffers[1]
  245.                 = (APTR)((ULONG)file->af_Buffers[0] + file->af_BufferSize);
  246.             file->af_Offset        = file->af_Buffers[0];
  247.             file->af_CurrentBuf    = 0;
  248.             file->af_SeekOffset    = 0;
  249.             file->af_PacketPending = FALSE;
  250.  
  251.             /* this is the port used to get the packets we send out back.
  252.              * It is initialized to PA_IGNORE, which means that no signal is
  253.              * generated when a message comes in to the port. The signal bit
  254.              * number is initialized to SIGB_SINGLE, which is the special bit
  255.              * that can be used for one-shot signalling. The signal will never
  256.              * be set, since the port is of type PA_IGNORE. We'll change the
  257.              * type of the port later on to PA_SIGNAL whenever we need to wait
  258.              * for a message to come in.
  259.              *
  260.              * The trick used here avoids the need to allocate an extra signal
  261.              * bit for the port. It is quite efficient.
  262.              */
  263.  
  264.             file->af_PacketPort.mp_MsgList.lh_Head
  265.                 = (struct Node *)&file->af_PacketPort.mp_MsgList.lh_Tail;
  266.             file->af_PacketPort.mp_MsgList.lh_Tail     = NULL;
  267.             file->af_PacketPort.mp_MsgList.lh_TailPred
  268.                 = (struct Node *)&file->af_PacketPort.mp_MsgList.lh_Head;
  269.             file->af_PacketPort.mp_Node.ln_Type        = NT_MSGPORT;
  270.             file->af_PacketPort.mp_Flags               = PA_IGNORE;
  271.             file->af_PacketPort.mp_SigBit              = SIGB_SINGLE;
  272.             file->af_PacketPort.mp_SigTask             = FindTask(NULL);
  273.  
  274.             file->af_Packet.sp_Pkt.dp_Link          = &file->af_Packet.sp_Msg;
  275.             file->af_Packet.sp_Pkt.dp_Arg1          = fh->fh_Arg1;
  276.             file->af_Packet.sp_Pkt.dp_Arg3          = file->af_BufferSize;
  277.             file->af_Packet.sp_Pkt.dp_Res1          = 0;
  278.             file->af_Packet.sp_Pkt.dp_Res2          = 0;
  279.             file->af_Packet.sp_Msg.mn_Node.ln_Name  = (STRPTR)&file->af_Packet.sp_Pkt;
  280.             file->af_Packet.sp_Msg.mn_Node.ln_Type  = NT_MESSAGE;
  281.             file->af_Packet.sp_Msg.mn_Length        = sizeof(struct StandardPacket);
  282.  
  283.             if (accessMode == MODE_READ)
  284.             {
  285.                 /* if we are in read mode, send out the first read packet to
  286.                  * the file system. While the application is getting ready to
  287.                  * read data, the file system will happily fill in this buffer
  288.                  * with DMA transfers, so that by the time the application
  289.                  * needs the data, it will be in the buffer waiting
  290.                  */
  291.  
  292.                 file->af_Packet.sp_Pkt.dp_Type = ACTION_READ;
  293.                 file->af_BytesLeft             = 0;
  294.                 if (file->af_Handler)
  295.                     SendPacket(file,file->af_Buffers[0]);
  296.             }
  297.             else
  298.             {
  299.                 file->af_Packet.sp_Pkt.dp_Type = ACTION_WRITE;
  300.                 file->af_BytesLeft             = file->af_BufferSize;
  301.             }
  302.         }
  303.         else
  304.         {
  305.             Close(handle);
  306.         }
  307.     }
  308.  
  309.     return(file);
  310. }
  311.  
  312.  
  313. /*****************************************************************************/
  314.  
  315.  
  316. LONG CloseAsync(struct AsyncFile *file)
  317. {
  318. LONG result;
  319.  
  320.     if (file)
  321.     {
  322.         result = WaitPacket(file);
  323.         if (result >= 0)
  324.         {
  325.             if (!file->af_ReadMode)
  326.             {
  327.                 /* this will flush out any pending data in the write buffer */
  328.                 result = Write(file->af_File,
  329.                                file->af_Buffers[file->af_CurrentBuf],
  330.                                file->af_BufferSize - file->af_BytesLeft);
  331.             }
  332.         }
  333.  
  334.         Close(file->af_File);
  335.         FreeVec(file);
  336.     }
  337.     else
  338.     {
  339.         SetIoErr(ERROR_INVALID_LOCK);
  340.         result = -1;
  341.     }
  342.  
  343.     return(result);
  344. }
  345.  
  346.  
  347. /*****************************************************************************/
  348.  
  349.  
  350. LONG ReadAsync(struct AsyncFile *file, APTR buffer, LONG numBytes)
  351. {
  352. LONG totalBytes;
  353. LONG bytesArrived;
  354.  
  355.     totalBytes = 0;
  356.  
  357.     /* if we need more bytes than there are in the current buffer, enter the
  358.      * read loop
  359.      */
  360.  
  361.     while (numBytes > file->af_BytesLeft)
  362.     {
  363.         /* drain buffer */
  364.         CopyMem(file->af_Offset,buffer,file->af_BytesLeft);
  365.  
  366.         numBytes           -= file->af_BytesLeft;
  367.         buffer              = (APTR)((ULONG)buffer + file->af_BytesLeft);
  368.         totalBytes         += file->af_BytesLeft;
  369.         file->af_BytesLeft  = 0;
  370.  
  371.         bytesArrived = WaitPacket(file);
  372.         if (bytesArrived <= 0)
  373.         {
  374.             if (bytesArrived == 0)
  375.                 return(totalBytes);
  376.  
  377.             return(-1);
  378.         }
  379.  
  380.         /* ask that the buffer be filled */
  381.         SendPacket(file,file->af_Buffers[1-file->af_CurrentBuf]);
  382.  
  383.         if (file->af_SeekOffset > bytesArrived)
  384.             file->af_SeekOffset = bytesArrived;
  385.  
  386.         file->af_Offset      = (APTR)((ULONG)file->af_Buffers[file->af_CurrentBuf]
  387.                                    + file->af_SeekOffset);
  388.         file->af_CurrentBuf  = 1 - file->af_CurrentBuf;
  389.         file->af_BytesLeft   = bytesArrived - file->af_SeekOffset;
  390.         file->af_SeekOffset  = 0;
  391.     }
  392.  
  393.     CopyMem(file->af_Offset,buffer,numBytes);
  394.     file->af_BytesLeft -= numBytes;
  395.     file->af_Offset     = (APTR)((ULONG)file->af_Offset + numBytes);
  396.  
  397.     return (totalBytes + numBytes);
  398. }
  399.  
  400.  
  401. /*****************************************************************************/
  402.  
  403.  
  404. LONG ReadCharAsync(struct AsyncFile *file)
  405. {
  406. unsigned char ch;
  407.  
  408.     if (file->af_BytesLeft)
  409.     {
  410.         /* if there is at least a byte left in the current buffer, get it
  411.          * directly. Also update all counters
  412.          */
  413.  
  414.         ch = *(char *)file->af_Offset;
  415.         file->af_BytesLeft--;
  416.         file->af_Offset = (APTR)((ULONG)file->af_Offset + 1);
  417.  
  418.         return((LONG)ch);
  419.     }
  420.  
  421.     /* there were no characters in the current buffer, so call the main read
  422.      * routine. This has the effect of sending a request to the file system to
  423.      * have the current buffer refilled. After that request is done, the
  424.      * character is extracted for the alternate buffer, which at that point
  425.      * becomes the "current" buffer
  426.      */
  427.  
  428.     if (ReadAsync(file,&ch,1) > 0)
  429.         return((LONG)ch);
  430.  
  431.     /* We couldn't read above, so fail */
  432.  
  433.     return(-1);
  434. }
  435.  
  436.  
  437. /*****************************************************************************/
  438.  
  439.  
  440. LONG WriteAsync(struct AsyncFile *file, APTR buffer, LONG numBytes)
  441. {
  442. LONG totalBytes;
  443.  
  444.     totalBytes = 0;
  445.  
  446.     while (numBytes > file->af_BytesLeft)
  447.     {
  448.         /* this takes care of NIL: */
  449.         if (!file->af_Handler)
  450.         {
  451.             file->af_Offset    = file->af_Buffers[0];
  452.             file->af_BytesLeft = file->af_BufferSize;
  453.             return(numBytes);
  454.         }
  455.  
  456.         if (file->af_BytesLeft)
  457.         {
  458.             CopyMem(buffer,file->af_Offset,file->af_BytesLeft);
  459.  
  460.             numBytes   -= file->af_BytesLeft;
  461.             buffer      = (APTR)((ULONG)buffer + file->af_BytesLeft);
  462.             totalBytes += file->af_BytesLeft;
  463.         }
  464.  
  465.         if (WaitPacket(file) < 0)
  466.             return(-1);
  467.  
  468.         /* send the current buffer out to disk */
  469.         SendPacket(file,file->af_Buffers[file->af_CurrentBuf]);
  470.  
  471.         file->af_CurrentBuf = 1 - file->af_CurrentBuf;
  472.         file->af_Offset     = file->af_Buffers[file->af_CurrentBuf];
  473.         file->af_BytesLeft  = file->af_BufferSize;
  474.     }
  475.  
  476.     CopyMem(buffer,file->af_Offset,numBytes);
  477.     file->af_BytesLeft -= numBytes;
  478.     file->af_Offset     = (APTR)((ULONG)file->af_Offset + numBytes);
  479.  
  480.     return (totalBytes + numBytes);
  481. }
  482.  
  483.  
  484. /*****************************************************************************/
  485.  
  486.  
  487. LONG WriteCharAsync(struct AsyncFile *file, UBYTE ch)
  488. {
  489.     if (file->af_BytesLeft)
  490.     {
  491.         /* if there's any room left in the current buffer, directly write
  492.          * the byte into it, updating counters and stuff.
  493.          */
  494.  
  495.         *(UBYTE *)file->af_Offset = ch;
  496.         file->af_BytesLeft--;
  497.         file->af_Offset = (APTR)((ULONG)file->af_Offset + 1);
  498.  
  499.         /* one byte written */
  500.         return(1);
  501.     }
  502.  
  503.     /* there was no room in the current buffer, so call the main write
  504.      * routine. This will effectively send the current buffer out to disk,
  505.      * wait for the other buffer to come back, and then put the byte into
  506.      * it.
  507.      */
  508.  
  509.     return(WriteAsync(file,&ch,1));
  510. }
  511.  
  512.  
  513. /*****************************************************************************/
  514.  
  515.  
  516. LONG SeekAsync(struct AsyncFile *file, LONG position, BYTE mode)
  517. {
  518. LONG  current, target;
  519. LONG  minBuf, maxBuf;
  520. LONG  bytesArrived;
  521. LONG  diff;
  522. LONG  filePos;
  523. LONG  roundTarget;
  524. D_S(struct FileInfoBlock,fib);
  525.  
  526.     bytesArrived = WaitPacket(file);
  527.  
  528.     if (bytesArrived < 0)
  529.         return(-1);
  530.  
  531.     if (file->af_ReadMode)
  532.     {
  533.         /* figure out what the actual file position is */
  534.         filePos = Seek(file->af_File,OFFSET_CURRENT,0);
  535.         if (filePos < 0)
  536.         {
  537.             RecordSyncFailure(file);
  538.             return(-1);
  539.         }
  540.  
  541.         /* figure out what the caller's file position is */
  542.         current = filePos - (file->af_BytesLeft+bytesArrived);
  543.  
  544.         /* figure out the absolute offset within the file where we must seek to */
  545.         if (mode == MODE_CURRENT)
  546.         {
  547.             target = current + position;
  548.         }
  549.         else if (mode == MODE_START)
  550.         {
  551.             target = position;
  552.         }
  553.         else /* if (mode == MODE_END) */
  554.         {
  555.             if (!ExamineFH(file->af_File,fib))
  556.             {
  557.                 RecordSyncFailure(file);
  558.                 return(-1);
  559.             }
  560.  
  561.             target = fib->fib_Size + position;
  562.         }
  563.  
  564.         /* figure out what range of the file is currently in our buffers */
  565.         minBuf = current - (LONG)((ULONG)file->af_Offset -
  566.                      (ULONG)file->af_Buffers[1 - file->af_CurrentBuf]);
  567.         maxBuf = current + file->af_BytesLeft
  568.                      + bytesArrived;  /* WARNING: this is one too big */
  569.  
  570.         diff = target - current;
  571.  
  572.         if ((target < minBuf) || (target >= maxBuf))
  573.         {
  574.             /* the target seek location isn't currently in our buffers, so
  575.              * move the actual file pointer to the desired location, and then
  576.              * restart the async read thing...
  577.              */
  578.  
  579.             /* this is to keep our file reading block-aligned on the device.
  580.              * block-aligned reads are generally quite a bit faster, so it is
  581.              * worth the trouble to keep things aligned
  582.              */
  583.             roundTarget = (target / file->af_BlockSize) * file->af_BlockSize;
  584.  
  585.             if (Seek(file->af_File,roundTarget-filePos,OFFSET_CURRENT) < 0)
  586.             {
  587.                 RecordSyncFailure(file);
  588.                 return(-1);
  589.             }
  590.  
  591.             SendPacket(file,file->af_Buffers[0]);
  592.  
  593.             file->af_SeekOffset = target-roundTarget;
  594.             file->af_BytesLeft  = 0;
  595.             file->af_CurrentBuf = 0;
  596.         }
  597.         else if ((target < current) || (diff <= file->af_BytesLeft))
  598.         {
  599.             /* one of the two following things is true:
  600.              *
  601.              * 1. The target seek location is within the current read buffer,
  602.              * but before the current location within the buffer. Move back
  603.              * within the buffer and pretend we never got the pending packet,
  604.              * just to make life easier, and faster, in the read routine.
  605.              *
  606.              * 2. The target seek location is ahead within the current
  607.              * read buffer. Advance to that location. As above, pretend to
  608.              * have never received the pending packet.
  609.              */
  610.  
  611.             RequeuePacket(file);
  612.  
  613.             file->af_BytesLeft -= diff;
  614.             file->af_Offset     = (APTR)((ULONG)file->af_Offset + diff);
  615.         }
  616.         else
  617.         {
  618.             /* at this point, we know the target seek location is within
  619.              * the buffer filled in by the packet that we just received
  620.              * at the start of this function. Throw away all the bytes in the
  621.              * current buffer, send a packet out to get the async thing going
  622.              * again, readjust buffer pointers to the seek location, and return
  623.              * with a grin on your face... :-)
  624.              */
  625.  
  626.             diff -= file->af_BytesLeft;
  627.  
  628.             SendPacket(file,file->af_Buffers[1-file->af_CurrentBuf]);
  629.  
  630.             file->af_Offset
  631.                 = (APTR)((ULONG)file->af_Buffers[file->af_CurrentBuf] + diff);
  632.             file->af_CurrentBuf = 1 - file->af_CurrentBuf;
  633.             file->af_BytesLeft  = bytesArrived - diff;
  634.         }
  635.     }
  636.     else
  637.     {
  638.         if (Write(file->af_File,
  639.                   file->af_Buffers[file->af_CurrentBuf],
  640.                   file->af_BufferSize - file->af_BytesLeft) < 0)
  641.         {
  642.             RecordSyncFailure(file);
  643.             return(-1);
  644.         }
  645.  
  646.         /* this will unfortunately generally result in non block-aligned file
  647.          * access. We could be sneaky and try to resync our file pos at a
  648.          * later time, but we won't bother. Seeking in write-only files is
  649.          * relatively rare (except when writing IFF files with unknown chunk
  650.          * sizes, where the chunk size has to be written after the chunk data)
  651.          */
  652.  
  653.         current = Seek(file->af_File,position,mode);
  654.  
  655.         if (current < 0)
  656.         {
  657.             RecordSyncFailure(file);
  658.             return(-1);
  659.         }
  660.  
  661.         file->af_BytesLeft  = file->af_BufferSize;
  662.         file->af_CurrentBuf = 0;
  663.     }
  664.  
  665.     return(current);
  666. }
  667.