home *** CD-ROM | disk | FTP | other *** search
/ Meeting Pearls 3 / Meeting_Pearls_III.iso / Pearls / texmf / source / SpecHost / asyncio.c next >
C/C++ Source or Header  |  1994-03-06  |  23KB  |  699 lines

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