physfs.c
changeset 508 0e75524a96af
parent 504 3420d82f9b01
child 533 a01da4bcee68
--- a/physfs.c	Sun Dec 01 11:17:56 2002 +0000
+++ b/physfs.c	Sun Dec 01 11:21:27 2002 +0000
@@ -1227,12 +1227,22 @@
             if (h->funcs->isSymLink(h, str, &retval))
             {
                 __PHYSFS_setError(ERR_SYMLINK_DISALLOWED);
-                retval = 0;
+                free(str);
+                return(0); /* insecure. */
             } /* if */
 
-            /* break out early if path element is missing or it's a symlink. */
+            /* break out early if path element is missing. */
             if (!retval)
+            {
+                /*
+                 * We need to clear it if it's the last element of the path,
+                 *  since this might be a non-existant file we're opening
+                 *  for writing...
+                 */
+                if (end == NULL)
+                    retval = 1;
                 break;
+            } /* if */
         } /* if */
 
         if (end == NULL)
@@ -1569,6 +1579,9 @@
         free(list);
     else
     {
+        rc->buffer = NULL;  /* just in case. */
+        rc->buffill = rc->bufpos = rc->bufsize = 0;  /* just in case. */
+        rc->forReading = 0;
         list->handle.opaque = (void *) rc;
         list->next = openWriteList;
         openWriteList = list;
@@ -1615,13 +1628,17 @@
     BAIL_IF_MACRO_MUTEX(rc == NULL, NULL, stateLock, NULL);
 
     list = (FileHandleList *) malloc(sizeof (FileHandleList));
-    BAIL_IF_MACRO(!list, ERR_OUT_OF_MEMORY, NULL);
+    BAIL_IF_MACRO_MUTEX(!list, ERR_OUT_OF_MEMORY, stateLock, NULL);
     list->handle.opaque = (void *) rc;
     list->next = openReadList;
     openReadList = list;
     retval = &(list->handle);
+    __PHYSFS_platformReleaseMutex(stateLock);
 
-    __PHYSFS_platformReleaseMutex(stateLock);
+    rc->buffer = NULL;  /* just in case. */
+    rc->buffill = rc->bufpos = rc->bufsize = 0;  /* just in case. */
+    rc->forReading = 1;
+
     return(retval);
 } /* PHYSFS_openRead */
 
@@ -1631,16 +1648,22 @@
     FileHandle *h = (FileHandle *) handle->opaque;
     FileHandleList *prev = NULL;
     FileHandleList *i;
-    int rc;
+    int rc = 1;
 
     for (i = *list; i != NULL; i = i->next)
     {
         if (&i->handle == handle)  /* handle is in this list? */
         {
-            rc = h->funcs->fileClose(h);
+            PHYSFS_uint8 *tmp = h->buffer;
+            rc = PHYSFS_flush(handle);
+            if (rc)
+                rc = h->funcs->fileClose(h);
             if (!rc)
                 return(-1);
 
+            if (tmp != NULL)  /* free any associated buffer. */
+                free(tmp);
+
             if (prev == NULL)
                 *list = i->next;
             else
@@ -1677,18 +1700,92 @@
 } /* PHYSFS_close */
 
 
+static PHYSFS_sint64 doBufferedRead(PHYSFS_file *handle, void *buffer,
+                                    PHYSFS_uint32 objSize,
+                                    PHYSFS_uint32 objCount)
+{
+    FileHandle *h = (FileHandle *) handle->opaque;
+    PHYSFS_sint64 retval = 0;
+    PHYSFS_uint32 remainder = 0;
+
+    while (objCount > 0)
+    {
+        PHYSFS_uint64 buffered = h->buffill - h->bufpos;
+        PHYSFS_uint64 mustread = (objSize * objCount) - remainder;
+        PHYSFS_uint32 copied;
+
+        if (buffered == 0) /* need to refill buffer? */
+        {
+            PHYSFS_sint64 rc = h->funcs->read(h, h->buffer, 1, h->bufsize);
+            if (rc <= 0)
+            {
+                h->bufpos -= remainder;
+                return(((rc == -1) && (retval == 0)) ? -1 : retval);
+            } /* if */
+
+            buffered = h->buffill = rc;
+            h->bufpos = 0;
+        } /* if */
+
+        if (buffered > mustread)
+            buffered = mustread;
+
+        memcpy(buffer, h->buffer + h->bufpos, (size_t) buffered);
+        buffer = ((PHYSFS_uint8 *) buffer) + buffered;
+        h->bufpos += buffered;
+        buffered += remainder;  /* take remainder into account. */
+        copied = (buffered / objSize);
+        remainder = (buffered % objSize);
+        retval += copied;
+        objCount -= copied;
+    } /* while */
+
+    return(retval);
+} /* doBufferedRead */
+
+
 PHYSFS_sint64 PHYSFS_read(PHYSFS_file *handle, void *buffer,
                           PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
 {
     FileHandle *h = (FileHandle *) handle->opaque;
+
+    BAIL_IF_MACRO(!h->forReading, ERR_FILE_ALREADY_OPEN_W, -1);
+    if (h->buffer != NULL)
+        return(doBufferedRead(handle, buffer, objSize, objCount));
+
     return(h->funcs->read(h, buffer, objSize, objCount));
 } /* PHYSFS_read */
 
 
+static PHYSFS_sint64 doBufferedWrite(PHYSFS_file *handle, const void *buffer,
+                                     PHYSFS_uint32 objSize,
+                                     PHYSFS_uint32 objCount)
+{
+    FileHandle *h = (FileHandle *) handle->opaque;
+
+    /* whole thing fits in the buffer? */
+    if (h->buffill + (objSize * objCount) < h->bufsize)
+    {
+        memcpy(h->buffer + h->buffill, buffer, objSize * objCount);
+        h->buffill += (objSize * objCount);
+        return(objCount);
+    } /* if */
+
+    /* would overflow buffer. Flush and then write the new objects, too. */
+    BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, -1);
+    return(h->funcs->write(h, buffer, objSize, objCount));
+} /* doBufferedWrite */
+
+
 PHYSFS_sint64 PHYSFS_write(PHYSFS_file *handle, const void *buffer,
                            PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
 {
     FileHandle *h = (FileHandle *) handle->opaque;
+
+    BAIL_IF_MACRO(h->forReading, ERR_FILE_ALREADY_OPEN_R, -1);
+    if (h->buffer != NULL)
+        return(doBufferedWrite(handle, buffer, objSize, objCount));
+
     return(h->funcs->write(h, buffer, objSize, objCount));
 } /* PHYSFS_write */
 
@@ -1696,20 +1793,29 @@
 int PHYSFS_eof(PHYSFS_file *handle)
 {
     FileHandle *h = (FileHandle *) handle->opaque;
-    return(h->funcs->eof(h));
+
+    if (!h->forReading)  /* never EOF on files opened for write/append. */
+        return(0);
+
+    /* eof if buffer is empty and archiver says so. */
+    return((h->bufpos == h->buffill) && (h->funcs->eof(h)));
 } /* PHYSFS_eof */
 
 
 PHYSFS_sint64 PHYSFS_tell(PHYSFS_file *handle)
 {
     FileHandle *h = (FileHandle *) handle->opaque;
-    return(h->funcs->tell(h));
+    PHYSFS_sint64 retval = h->forReading ?
+                            (h->funcs->tell(h) - h->buffill) + h->bufpos :
+                            (h->funcs->tell(h) + h->buffill);
+    return(retval);
 } /* PHYSFS_tell */
 
 
 int PHYSFS_seek(PHYSFS_file *handle, PHYSFS_uint64 pos)
 {
     FileHandle *h = (FileHandle *) handle->opaque;
+    BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, 0);
     return(h->funcs->seek(h, pos));
 } /* PHYSFS_seek */
 
@@ -1721,6 +1827,65 @@
 } /* PHYSFS_filelength */
 
 
+int PHYSFS_setBuffer(PHYSFS_file *handle, PHYSFS_uint64 bufsize)
+{
+    FileHandle *h = (FileHandle *) handle->opaque;
+
+    BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, 0);
+
+    /*
+     * For reads, we need to move the file pointer to where it would be
+     *  if we weren't buffering, so that the next read will get the
+     *  right chunk of stuff from the file. PHYSFS_flush() handles writes.
+     */
+    if ((h->forReading) && (h->buffill != h->bufpos))
+    {
+        PHYSFS_uint64 pos;
+        PHYSFS_sint64 curpos = h->funcs->tell(h);
+        BAIL_IF_MACRO(curpos == -1, NULL, 0);
+        pos = ((curpos - h->buffill) + h->bufpos);
+        BAIL_IF_MACRO(!h->funcs->seek(h, pos), NULL, 0);
+    } /* if */
+
+    if (bufsize == 0)  /* delete existing buffer. */
+    {
+        if (h->buffer != NULL)
+        {
+            free(h->buffer);
+            h->buffer = NULL;
+        } /* if */
+    } /* if */
+
+    else
+    {
+        PHYSFS_uint8 *newbuf = realloc(h->buffer, bufsize);
+        BAIL_IF_MACRO(newbuf == NULL, ERR_OUT_OF_MEMORY, 0);
+        h->buffer = newbuf;
+    } /* else */
+
+    h->bufsize = bufsize;
+    h->buffill = h->bufpos = 0;
+    return(1);
+} /* PHYSFS_setBuffer */
+
+
+int PHYSFS_flush(PHYSFS_file *handle)
+{
+    FileHandle *h = (FileHandle *) handle->opaque;
+    PHYSFS_sint64 rc;
+
+    if ((h->forReading) || (h->bufpos == h->buffill))
+        return(1);  /* open for read or buffer empty are successful no-ops. */
+
+    /* dump buffer to disk. */
+    rc = h->funcs->write(h, h->buffer + h->bufpos, h->buffill - h->bufpos, 1);
+    BAIL_IF_MACRO(rc <= 0, NULL, 0);
+
+    h->bufpos = h->buffill = 0;
+    return(1);
+} /* PHYSFS_flush */
+
+
 LinkedStringList *__PHYSFS_addToLinkedStringList(LinkedStringList *retval,
                                                  LinkedStringList **prev,
                                                  const char *str,