Implement RESIZE-FILE
authorHelmut Eller <eller.helmut@gmail.com>
Tue, 3 Jan 2017 09:31:36 +0000 (10:31 +0100)
committerHelmut Eller <eller.helmut@gmail.com>
Tue, 3 Jan 2017 09:31:36 +0000 (10:31 +0100)
On Unix, the easiest way to implement RESIZE-FILE would be to call
ftruncate, but in ANSI C alone it's quite difficult.  As this takes a
bit more code, I created a new file pf_fileio_stdio.c and moved it
there.  The idea is that somebody could replace that with say a
pf_fileio_posix.c and use ftruncate there.

Because there's a new file I had to change the Makefiles.  I did not
fix the VisualStudio project file, as I don't know how to do that.

* csrc/stdio/pf_fileio_stdio.c: New file.

* csrc/pf_guts.h (cforth_primitive_ids): ID_FILE_RESIZE added,
ID_RESERVED10 removed.
(THROW_RESIZE_FILE): New.

* csrc/pf_io.h (sdResizeFile): New prototype.

* csrc/pf_io.c (sdResizeFile): Define stub,
(sdDeleteFile): Fix type.

* csrc/pf_inner.c (pfCatch): Add case for ID_FILE_RESIZE.
* csrc/pfcompil.c (pfBuildDictionary): Define RESIZE-FILE.

* build/unix/Makefile,
build/linux-crossbuild-amiga/Makefile
build/mingw-crossbuild-linux/Makefile: Compile and link with pf_fileio_stdio.

* fth/t_file.fth: Remove stub.

build/linux-crossbuild-amiga/Makefile
build/mingw-crossbuild-linux/Makefile
build/unix/Makefile
csrc/pf_guts.h
csrc/pf_inner.c
csrc/pf_io.c
csrc/pf_io.h
csrc/pfcompil.c
csrc/stdio/pf_fileio_stdio.c [new file with mode: 0644]
fth/t_file.fth

index 1c10eeb..c37f5fe 100644 (file)
@@ -44,7 +44,7 @@ FULL_WARNINGS =  \
 CCOPTS = $(WIDTHOPT) -x c -O2 $(FULL_WARNINGS) $(EXTRA_CCOPTS) $(DEBUGOPTS)
 
 #IO_SOURCE = pf_io_posix.c
-IO_SOURCE = pf_io_stdio.c
+IO_SOURCE = pf_io_stdio.c pf_fileio_stdio.c
 #IO_SOURCE = pf_io_win32_console.c
 
 EMBCCOPTS = -DPF_STATIC_DIC
index 3305fd9..a6d3161 100644 (file)
@@ -45,7 +45,7 @@ CCOPTS = $(WIDTHOPT) -x c -O2 $(FULL_WARNINGS) $(EXTRA_CCOPTS) $(DEBUGOPTS)
 
 #IO_SOURCE = pf_io_posix.c
 #IO_SOURCE = pf_io_stdio.c
-IO_SOURCE = pf_io_win32_console.c
+IO_SOURCE = pf_io_win32_console.c pf_fileio_stdio.c
 
 EMBCCOPTS = -DPF_STATIC_DIC
 
index 42819ab..3e9f3e7 100644 (file)
@@ -40,10 +40,10 @@ FULL_WARNINGS =  \
 DEBUGOPTS = -g
 CCOPTS = $(WIDTHOPT) -x c -O2 $(FULL_WARNINGS) $(EXTRA_CCOPTS) $(DEBUGOPTS)
 
-IO_SOURCE = pf_io_posix.c
+IO_SOURCE = pf_io_posix.c pf_fileio_stdio.c
 #IO_SOURCE = pf_io_stdio.c
 
-EMBCCOPTS = -DPF_STATIC_DIC
+EMBCCOPTS = -DPF_STATIC_DIC #-DPF_NO_FILEIO
 
 #######################################
 PFINCLUDES = pf_all.h pf_cglue.h pf_clib.h pf_core.h pf_float.h \
index 5339294..709b537 100644 (file)
@@ -283,6 +283,7 @@ enum cforth_primitive_ids
     ID_FILE_DELETE,
     ID_FILE_FLUSH,             /* FLUSH-FILE */
     ID_FILE_RENAME,            /* (RENAME-FILE) */
+    ID_FILE_RESIZE,            /* RESIZE-FILE */
 /* If you add a word here, take away one reserved word below. */
 #ifdef PF_SUPPORT_FP
 /* Only reserve space if we are adding FP so that we can detect
@@ -298,7 +299,6 @@ enum cforth_primitive_ids
     ID_RESERVED08,
     ID_RESERVED09,
     ID_RESERVED10,
-    ID_RESERVED11,
     ID_FP_D_TO_F,
     ID_FP_FSTORE,
     ID_FP_FTIMES,
@@ -369,6 +369,7 @@ enum cforth_primitive_ids
 #define THROW_FLOAT_STACK_UNDERFLOW  ( -45)
 #define THROW_QUIT            (-56)
 #define THROW_FLUSH_FILE      (-68)
+#define THROW_RESIZE_FILE     (-74)
 
 /* THROW codes unique to pForth */
 #define THROW_BYE            (-256) /* Exit program. */
index 0a6c003..f673075 100644 (file)
@@ -1106,6 +1106,15 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql ));
            }
            endcase;
 
+       case ID_FILE_RESIZE: /* ( ud fileid -- ior ) */
+           {
+               FileStream *File = (FileStream *) TOS;
+               ucell_t SizeHi = (ucell_t) M_POP;
+               ucell_t SizeLo = (ucell_t) M_POP;
+               TOS = sdResizeFile( File, SizeLo, SizeHi );
+           }
+           endcase;
+
         case ID_FILL: /* ( caddr num charval -- ) */
             {
                 register char *DstPtr;
index 819dc9f..343a02d 100644 (file)
@@ -215,11 +215,11 @@ cell_t sdCloseFile( FileStream * Stream )
     return 0;
 }
 
-FileStream *sdDeleteFile( const char *FileName )
+cell_t sdDeleteFile( const char *FileName )
 {
     UNIMPLEMENTED("sdDeleteFile");
     TOUCH(FileName);
-    return NULL;
+    return -1;
 }
 
 cell_t sdRenameFile( const char *OldName, const char *NewName )
@@ -229,5 +229,15 @@ cell_t sdRenameFile( const char *OldName, const char *NewName )
     TOUCH(NewName);
     return -1;
 }
+
+ThrowCode sdResizeFile( FileStream * File, ucell_t SizeLo, ucell_t SizeHi )
+{
+    UNIMPLEMENTED("sdResizeFile");
+    TOUCH(File);
+    TOUCH(SizeLo);
+    TOUCH(SizeHi);
+    return THROW_RESIZE_FILE;
+}
+
 #endif
 
index 7cf4310..cbd45ec 100644 (file)
@@ -86,6 +86,8 @@ void ioTerm( void );
     cell_t sdWriteFile( void *ptr, cell_t Size, int32_t nItems, FileStream * Stream  );
     cell_t sdSeekFile( FileStream * Stream, off_t Position, int32_t Mode );
     cell_t sdRenameFile( const char *OldName, const char *NewName );
+    cell_t sdDeleteFile( const char *FileName );
+    ThrowCode sdResizeFile( FileStream *, ucell_t SizeLo, ucell_t SizeHi );
     off_t sdTellFile( FileStream * Stream );
     cell_t sdCloseFile( FileStream * Stream );
     cell_t sdInputChar( FileStream *stream );
@@ -115,7 +117,7 @@ void ioTerm( void );
         typedef FILE FileStream;
 
         #define sdOpenFile      fopen
-        #define sdDeleteFile      remove
+        #define sdDeleteFile    remove
         #define sdFlushFile     fflush
         #define sdReadFile      fread
         #define sdWriteFile     fwrite
@@ -138,6 +140,8 @@ void ioTerm( void );
         #define  PF_SEEK_CUR   (SEEK_CUR)
         #define  PF_SEEK_END   (SEEK_END)
 
+        ThrowCode sdResizeFile( FileStream *, ucell_t SizeLo, ucell_t SizeHi );
+
         /*
         ** printf() is only used for debugging purposes.
         ** It is not required for normal operation.
index a3f0121..a03e823 100644 (file)
@@ -260,6 +260,7 @@ PForthDictionary pfBuildDictionary( cell_t HeaderSize, cell_t CodeSize )
     CreateDicEntryC( ID_FILE_REPOSITION, "REPOSITION-FILE",  0 );
     CreateDicEntryC( ID_FILE_FLUSH, "FLUSH-FILE",  0 );
     CreateDicEntryC( ID_FILE_RENAME, "(RENAME-FILE)",  0 );
+    CreateDicEntryC( ID_FILE_RESIZE, "RESIZE-FILE",  0 );
     CreateDicEntryC( ID_FILE_RO, "R/O",  0 );
     CreateDicEntryC( ID_FILE_RW, "R/W",  0 );
     CreateDicEntryC( ID_FILE_WO, "W/O",  0 );
diff --git a/csrc/stdio/pf_fileio_stdio.c b/csrc/stdio/pf_fileio_stdio.c
new file mode 100644 (file)
index 0000000..9a25092
--- /dev/null
@@ -0,0 +1,137 @@
+/***************************************************************
+** File access routines based on ANSI C (no Unix stuff).
+**
+** This file is part of pForth
+**
+** The pForth software code is dedicated to the public domain,
+** and any third party may reproduce, distribute and modify
+** the pForth software code or any derivative works thereof
+** without any compensation or license.  The pForth software
+** code is provided on an "as is" basis without any warranty
+** of any kind, including, without limitation, the implied
+** warranties of merchantability and fitness for a particular
+** purpose and their equivalents under the laws of any jurisdiction.
+**
+****************************************************************/
+
+#include "../pf_all.h"
+
+#ifndef PF_NO_FILEIO
+
+#include <limits.h>            /* For LONG_MAX */
+
+typedef int bool_t;
+
+/* Copy SIZE bytes from File FROM to File TO.  Return non-FALSE on error. */
+static bool_t CopyFile( FileStream *From, FileStream *To, long Size)
+{
+    bool_t Error = TRUE;
+    size_t Diff = Size;
+    size_t BufSize = 512;
+    char *Buffer = pfAllocMem( BufSize );
+    if( Buffer != 0 )
+    {
+       while( Diff > 0 )
+       {
+           size_t N = MIN( Diff, BufSize );
+           if( fread( Buffer, 1, N, From ) < N ) goto cleanup;
+           if( fwrite( Buffer, 1, N, To ) < N ) goto cleanup;
+           Diff -= N;
+       }
+       Error = FALSE;
+
+      cleanup:
+       pfFreeMem( Buffer );
+    }
+    return Error;
+}
+
+/* Shrink the file FILE to NEWSIZE.  Return non-FALSE on error.
+ *
+ * There's no direct way to do this in ANSI C.  The closest thing we
+ * have is freopen(3), which truncates a file to zero length if we use
+ * "w+b" as mode argument.  So we do this:
+ *
+ *   1. copy original content to temporary file
+ *   2. re-open and truncate FILE
+ *   3. copy the temporary file to FILE
+ *
+ * Unfortunately, "w+b" may not be the same mode as the original mode
+ * of FILE.  I don't see a away to avoid this, though.
+ *
+ * We call freopen with NULL as path argument, because we don't know
+ * the actual file-name.  It seems that the trick with path=NULL is
+ * not part of C89 but it's in C99.
+ */
+static bool_t TruncateFile( FileStream *File, long Newsize )
+{
+    bool_t Error = TRUE;
+    if( fseek( File, 0, SEEK_SET ) == 0)
+    {
+       FileStream *TmpFile = tmpfile();
+       if( TmpFile != NULL )
+       {
+           if( CopyFile( File, TmpFile, Newsize )) goto cleanup;
+           if( fseek( TmpFile, 0, SEEK_SET ) != 0 ) goto cleanup;
+           if( freopen( NULL, "w+b", File ) == NULL ) goto cleanup;
+           if( CopyFile( TmpFile, File, Newsize )) goto cleanup;
+           Error = FALSE;
+
+         cleanup:
+           fclose( TmpFile );
+       }
+    }
+    return Error;
+}
+
+/* Write DIFF 0 bytes to FILE. Return non-FALSE on error. */
+static bool_t ExtendFile( FileStream *File, size_t Diff )
+{
+    bool_t Error = TRUE;
+    size_t BufSize = 512;
+    char * Buffer = pfAllocMem( BufSize );
+    if( Buffer != 0 )
+    {
+       pfSetMemory( Buffer, 0, BufSize );
+       while( Diff > 0 )
+       {
+           size_t N = MIN( Diff, BufSize );
+           if( fwrite( Buffer, 1, N, File ) < N ) goto cleanup;
+           Diff -= N;
+       }
+       Error = FALSE;
+      cleanup:
+       pfFreeMem( Buffer );
+    }
+    return Error;
+}
+
+/* Return non-FALSE if the double-cell unsigned number LO/HI
+ * is greater then LONG_MAX.
+ */
+static bool_t IsGreaterThanLongMax( ucell_t Lo, ucell_t Hi )
+{
+    return (Hi != 0) || (Lo > LONG_MAX);
+}
+
+ThrowCode sdResizeFile( FileStream *File, ucell_t SizeLo, ucell_t SizeHi )
+{
+    bool_t Error = TRUE;
+    if( !IsGreaterThanLongMax( SizeLo, SizeHi ) )
+    {
+       long Newsize = (long) SizeLo;
+       if( fseek( File, 0, SEEK_END ) == 0 )
+       {
+           long Oldsize = ftell( File );
+           if( Oldsize != -1L )
+           {
+               Error = ( Oldsize <= Newsize
+                         ? ExtendFile( File, Newsize - Oldsize )
+                         : TruncateFile( File, Newsize ));
+           }
+       }
+    }
+    return Error ? THROW_RESIZE_FILE : 0;
+}
+
+#endif /* !PF_NO_FILEIO */
index f024926..e6ba0bd 100644 (file)
@@ -74,7 +74,6 @@ true value verbose
 : $" state IF postpone s" else ['] s" execute THEN ; immediate
 
 \ FIXME: stubs for missing definitions
-: resize-file drop 2drop -1 ;
 : file-status 2drop 0 -1 ;
 
 TESTING File Access word set