From e0701bfb3e9047a565191d1e582f7f5d97a79f5f Mon Sep 17 00:00:00 2001 From: Helmut Eller Date: Tue, 3 Jan 2017 10:31:36 +0100 Subject: [PATCH] Implement RESIZE-FILE 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 | 2 +- build/mingw-crossbuild-linux/Makefile | 2 +- build/unix/Makefile | 4 +- csrc/pf_guts.h | 3 +- csrc/pf_inner.c | 9 ++ csrc/pf_io.c | 14 ++- csrc/pf_io.h | 6 +- csrc/pfcompil.c | 1 + csrc/stdio/pf_fileio_stdio.c | 137 ++++++++++++++++++++++++++ fth/t_file.fth | 1 - 10 files changed, 170 insertions(+), 9 deletions(-) create mode 100644 csrc/stdio/pf_fileio_stdio.c diff --git a/build/linux-crossbuild-amiga/Makefile b/build/linux-crossbuild-amiga/Makefile index 1c10eeb..c37f5fe 100644 --- a/build/linux-crossbuild-amiga/Makefile +++ b/build/linux-crossbuild-amiga/Makefile @@ -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 diff --git a/build/mingw-crossbuild-linux/Makefile b/build/mingw-crossbuild-linux/Makefile index 3305fd9..a6d3161 100644 --- a/build/mingw-crossbuild-linux/Makefile +++ b/build/mingw-crossbuild-linux/Makefile @@ -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 diff --git a/build/unix/Makefile b/build/unix/Makefile index 42819ab..3e9f3e7 100644 --- a/build/unix/Makefile +++ b/build/unix/Makefile @@ -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 \ diff --git a/csrc/pf_guts.h b/csrc/pf_guts.h index 5339294..709b537 100644 --- a/csrc/pf_guts.h +++ b/csrc/pf_guts.h @@ -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. */ diff --git a/csrc/pf_inner.c b/csrc/pf_inner.c index 0a6c003..f673075 100644 --- a/csrc/pf_inner.c +++ b/csrc/pf_inner.c @@ -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; diff --git a/csrc/pf_io.c b/csrc/pf_io.c index 819dc9f..343a02d 100644 --- a/csrc/pf_io.c +++ b/csrc/pf_io.c @@ -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 diff --git a/csrc/pf_io.h b/csrc/pf_io.h index 7cf4310..cbd45ec 100644 --- a/csrc/pf_io.h +++ b/csrc/pf_io.h @@ -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. diff --git a/csrc/pfcompil.c b/csrc/pfcompil.c index a3f0121..a03e823 100644 --- a/csrc/pfcompil.c +++ b/csrc/pfcompil.c @@ -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 index 0000000..9a25092 --- /dev/null +++ b/csrc/stdio/pf_fileio_stdio.c @@ -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 /* 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 */ diff --git a/fth/t_file.fth b/fth/t_file.fth index f024926..e6ba0bd 100644 --- a/fth/t_file.fth +++ b/fth/t_file.fth @@ -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 -- 2.20.1