diff --git a/hpcgap/lib/hpc/consoleui.g b/hpcgap/lib/hpc/consoleui.g index 34a14c0ccf..b93f37cce6 100644 --- a/hpcgap/lib/hpc/consoleui.g +++ b/hpcgap/lib/hpc/consoleui.g @@ -1022,7 +1022,7 @@ end); BindGlobal("OutputLoop@", function() local packet, threadid, prefix, text, stdout, newlines, eol, last_thread, p, last, line, prompt; - stdout := OUTPUT_TEXT_FILE("*stdout*", false); + stdout := OUTPUT_TEXT_FILE("*stdout*", false, false); ControlThread@ := true; last_thread := false; eol := true; diff --git a/lib/streams.gd b/lib/streams.gd index 7da5fa0902..052c860dee 100644 --- a/lib/streams.gd +++ b/lib/streams.gd @@ -726,14 +726,15 @@ DeclareOperation( "OutputTextString", [ IsList, IsBool ] ); ## <#GAPDoc Label="OutputTextFile"> ## ## +## ## ## ## OutputTextFile( filename, append ) returns an output stream in the ## category IsOutputTextFile that writes received characters to the file ## filename. If append is false, then the file is emptied first, ## otherwise received characters are added at the end of the file. -## If filename ends in .gz then the file will be -## written with gzip compression. +## OutputGzipFile acts identically to OutputTextFile, except it compresses +## the output with gzip. ##

## # use a temporary directory @@ -766,6 +767,7 @@ DeclareOperation( "OutputTextString", [ IsList, IsBool ] ); ## <#/GAPDoc> ## DeclareOperation( "OutputTextFile", [ IsString, IsBool ] ); +DeclareOperation( "OutputGzipFile", [ IsString, IsBool ] ); ############################################################################# diff --git a/lib/streams.gi b/lib/streams.gi index 5cba671d2e..83d1b088bc 100644 --- a/lib/streams.gi +++ b/lib/streams.gi @@ -1084,14 +1084,14 @@ fi; #M OutputTextFile( , ) ## InstallMethod( OutputTextFile, - "output text stream from file", + "output text stream to file", [ IsString, IsBool ], function( str, append ) local fid; str := UserHomeExpand(str); - fid := OUTPUT_TEXT_FILE( str, append ); + fid := OUTPUT_TEXT_FILE( str, append, false ); if fid = fail then return fail; else @@ -1110,6 +1110,37 @@ InstallOtherMethod( OutputTextFile, Error("Usage OutputTextFile( , )"); end ); +############################################################################# +## +#M OutputGzipFile( , ) +## +InstallMethod( OutputGzipFile, + "output gzipped text to file", + [ IsString, + IsBool ], +function( str, append ) + local fid; + str := UserHomeExpand(str); + + fid := OUTPUT_TEXT_FILE( str, append, true ); + if fid = fail then + return fail; + else + atomic OutputTextFileStillOpen do + AddSet( OutputTextFileStillOpen, fid ); + od; + return Objectify( OutputTextFileType, [fid, Immutable(str), true] ); + fi; +end ); + +InstallOtherMethod( OutputGzipFile, + "error catching method, append not given", + [ IsString ], + -SUM_FLAGS, # as low as possible + function( str ) + Error("Usage OutputGzipFile( , )"); +end ); + ############################################################################# ## #M CloseStream( ) diff --git a/src/io.c b/src/io.c index 864524e2e1..7eb8f101bf 100644 --- a/src/io.c +++ b/src/io.c @@ -360,7 +360,7 @@ UInt OpenInput(TypInputFile * input, const Char * filename) #endif /* try to open the input file */ - file = SyFopen( filename, "r" ); + file = SyFopen(filename, "r", TRUE); if ( file == -1 ) return 0; @@ -503,7 +503,7 @@ UInt OpenLog ( return 0; /* try to open the file */ - IO()->OutputLogFileOrStream.file = SyFopen(filename, "w"); + IO()->OutputLogFileOrStream.file = SyFopen(filename, "w", FALSE); IO()->OutputLogFileOrStream.isstream = FALSE; if (IO()->OutputLogFileOrStream.file == -1) return 0; @@ -597,7 +597,7 @@ UInt OpenInputLog ( return 0; /* try to open the file */ - IO()->InputLogFileOrStream.file = SyFopen(filename, "w"); + IO()->InputLogFileOrStream.file = SyFopen(filename, "w", FALSE); IO()->InputLogFileOrStream.isstream = FALSE; if (IO()->InputLogFileOrStream.file == -1) return 0; @@ -694,7 +694,7 @@ UInt OpenOutputLog ( /* try to open the file */ memset(&IO()->OutputLogFileOrStream, 0, sizeof(TypOutputFile)); IO()->OutputLogFileOrStream.isstream = FALSE; - IO()->OutputLogFileOrStream.file = SyFopen(filename, "w"); + IO()->OutputLogFileOrStream.file = SyFopen(filename, "w", FALSE); if (IO()->OutputLogFileOrStream.file == -1) return 0; @@ -817,7 +817,7 @@ UInt OpenOutput(TypOutputFile * output, const Char * filename, BOOL append) #endif /* try to open the file */ - Int file = SyFopen(filename, append ? "a" : "w"); + Int file = SyFopen(filename, append ? "a" : "w", FALSE); if ( file == -1 ) return 0; diff --git a/src/saveload.c b/src/saveload.c index cd35f7378a..1e52473237 100644 --- a/src/saveload.c +++ b/src/saveload.c @@ -58,12 +58,11 @@ static Int OpenForSave( Obj fname ) Pr("Already saving\n", 0, 0); return 1; } - SaveFile = SyFopen(CONST_CSTR_STRING(fname), "wb"); - if (SaveFile == -1) - { - Pr("Couldn't open file %s to save workspace\n", - (UInt)CONST_CSTR_STRING(fname), 0); - return 1; + SaveFile = SyFopen(CONST_CSTR_STRING(fname), "wb", TRUE); + if (SaveFile == -1) { + Pr("Couldn't open file %s to save workspace\n", + (UInt)CONST_CSTR_STRING(fname), 0); + return 1; } LBPointer = LoadBuffer; LBEnd = LBPointer+sizeof(LoadBuffer); @@ -89,11 +88,10 @@ static void OpenForLoad( const Char *fname ) { Panic("Internal error -- this should never happen"); } - LoadFile = SyFopen(fname, "rb"); - if (LoadFile == -1) - { - Pr("Couldn't open saved workspace %s\n",(Int)fname, 0); - SyExit(1); + LoadFile = SyFopen(fname, "rb", TRUE); + if (LoadFile == -1) { + Pr("Couldn't open saved workspace %s\n", (Int)fname, 0); + SyExit(1); } } diff --git a/src/streams.c b/src/streams.c index ba8626db3f..45af9ba49b 100644 --- a/src/streams.c +++ b/src/streams.c @@ -1264,8 +1264,8 @@ static Obj FuncINPUT_TEXT_FILE(Obj self, Obj filename) /* call the system dependent function */ SyClearErrorNo(); - fid = SyFopen( CONST_CSTR_STRING(filename), "r" ); - if ( fid == - 1) + fid = SyFopen(CONST_CSTR_STRING(filename), "r", TRUE); + if (fid == -1) SySetErrorNo(); return fid == -1 ? Fail : INTOBJ_INT(fid); } @@ -1286,24 +1286,26 @@ static Obj FuncIS_END_OF_FILE(Obj self, Obj fid) /**************************************************************************** ** -*F FuncOUTPUT_TEXT_FILE( , , ) . . . . . open a stream +*F FuncOUTPUT_TEXT_FILE( , , , ) . open a stream */ -static Obj FuncOUTPUT_TEXT_FILE(Obj self, Obj filename, Obj append) +static Obj FuncOUTPUT_TEXT_FILE(Obj self, Obj filename, Obj append, Obj comp) { Int fid; RequireStringRep(SELF_NAME, filename); RequireTrueOrFalse(SELF_NAME, append); + RequireTrueOrFalse(SELF_NAME, comp); + Int compbool = (comp == True); /* call the system dependent function */ SyClearErrorNo(); if ( append == True ) { - fid = SyFopen( CONST_CSTR_STRING(filename), "a" ); + fid = SyFopen(CONST_CSTR_STRING(filename), "a", compbool); } else { - fid = SyFopen( CONST_CSTR_STRING(filename), "w" ); + fid = SyFopen(CONST_CSTR_STRING(filename), "w", compbool); } - if ( fid == - 1) + if (fid == -1) SySetErrorNo(); return fid == -1 ? Fail : INTOBJ_INT(fid); } @@ -1773,7 +1775,7 @@ static StructGVarFunc GVarFuncs[] = { GVAR_FUNC_1ARGS(LIST_DIR, dirname), GVAR_FUNC_1ARGS(CLOSE_FILE, fid), GVAR_FUNC_1ARGS(INPUT_TEXT_FILE, filename), - GVAR_FUNC_2ARGS(OUTPUT_TEXT_FILE, filename, append), + GVAR_FUNC_3ARGS(OUTPUT_TEXT_FILE, filename, append, compress), GVAR_FUNC_1ARGS(IS_END_OF_FILE, fid), GVAR_FUNC_1ARGS(POSITION_FILE, fid), GVAR_FUNC_1ARGS(READ_BYTE_FILE, fid), diff --git a/src/sysfiles.c b/src/sysfiles.c index bf6e160d0a..b7afbab5c8 100644 --- a/src/sysfiles.c +++ b/src/sysfiles.c @@ -237,7 +237,7 @@ Int4 SyGAPCRC( const Char * name ) Int seen_nl; /* the CRC of a non existing file is 0 */ - fid = SyFopen( name, "r" ); + fid = SyFopen(name, "r", TRUE); if ( fid == -1 ) { return 0; } @@ -585,7 +585,8 @@ void SyBufSetEOF(Int fid) /**************************************************************************** ** -*F SyFopen( , ) . . . . . . . . open the file with name +*F SyFopen( , , ) +*F open the file with name ** ** The function 'SyFopen' is called to open the file with the name . ** If is "r" it is opened for reading, in this case it must exist. @@ -596,21 +597,22 @@ void SyBufSetEOF(Int fid) ** 'SyFopen' returns -1 if it cannot open the file. ** ** The following standard files names and file identifiers are guaranteed: -** 'SyFopen( "*stdin*", "r")' returns 0 identifying the standard input file. -** 'SyFopen( "*stdout*","w")' returns 1 identifying the standard outpt file. -** 'SyFopen( "*errin*", "r")' returns 2 identifying the brk loop input file. -** 'SyFopen( "*errout*","w")' returns 3 identifying the error messages file. +** 'SyFopen( "*stdin*", "r", ..)' returns 0, the standard input file. +** 'SyFopen( "*stdout*","w", ..)' returns 1, the standard outpt file. +** 'SyFopen( "*errin*", "r", ..)' returns 2, the brk loop input file. +** 'SyFopen( "*errout*","w", ..)' returns 3, the error messages file. ** ** If it is necessary to adjust the filename this should be done here, the ** filename convention used in GAP is that '/' is the directory separator. ** ** Right now GAP does not read nonascii files, but if this changes sometimes ** 'SyFopen' must adjust the mode argument to open the file in binary mode. +** +** If is TRUE, files with names ending '.gz' will be +** automatically compressed/decompressed using gzip. */ -Int SyFopen ( - const Char * name, - const Char * mode ) +Int SyFopen(const Char * name, const Char * mode, BOOL transparent_compress) { Int fid; Char namegz [1024]; @@ -666,16 +668,19 @@ Int SyFopen ( #endif /* try to open the file */ - if (endsgz && (syBuf[fid].gzfp = gzopen(name, mode))) { + if (endsgz && transparent_compress && + (syBuf[fid].gzfp = gzopen(name, mode))) { syBuf[fid].type = gzip_socket; syBuf[fid].fp = -1; syBuf[fid].bufno = -1; - } else if (0 <= (syBuf[fid].fp = open(name, flags, 0644))) { + } + else if (0 <= (syBuf[fid].fp = open(name, flags, 0644))) { syBuf[fid].type = raw_socket; syBuf[fid].echo = syBuf[fid].fp; syBuf[fid].bufno = -1; } - else if (strncmp(mode, "r", 1) == 0 && SyIsReadableFile(namegz) == 0 && + else if (strncmp(mode, "r", 1) == 0 && transparent_compress && + SyIsReadableFile(namegz) == 0 && (syBuf[fid].gzfp = gzopen(namegz, mode))) { syBuf[fid].type = gzip_socket; syBuf[fid].fp = -1; diff --git a/src/sysfiles.h b/src/sysfiles.h index 5a6c3ff4c6..717c5b2274 100644 --- a/src/sysfiles.h +++ b/src/sysfiles.h @@ -94,7 +94,8 @@ void SyBufSetEOF(Int fid); /**************************************************************************** ** -*F SyFopen( , ) . . . . . . . . open the file with name +*F SyFopen( , , ) +*F open the file with name ** ** The function 'SyFopen' is called to open the file with the name . ** If is "r" it is opened for reading, in this case it must exist. @@ -105,16 +106,21 @@ void SyBufSetEOF(Int fid); ** 'SyFopen' returns -1 if it cannot open the file. ** ** The following standard files names and file identifiers are guaranteed: -** 'SyFopen( "*stdin*", "r")' returns 0 identifying the standard input file. -** 'SyFopen( "*stdout*","w")' returns 1 identifying the standard outpt file. -** 'SyFopen( "*errin*", "r")' returns 2 identifying the brk loop input file. -** 'SyFopen( "*errout*","w")' returns 3 identifying the error messages file. +** 'SyFopen( "*stdin*", "r", ..)' returns 0, the standard input file. +** 'SyFopen( "*stdout*","w", ..)' returns 1, the standard outpt file. +** 'SyFopen( "*errin*", "r", ..)' returns 2, the brk loop input file. +** 'SyFopen( "*errout*","w", ..)' returns 3, the error messages file. +** +** If it is necessary to adjust the filename this should be done here, the +** filename convention used in GAP is that '/' is the directory separator. ** -** If it is necessary to adjust the filename this should be done here. ** Right now GAP does not read nonascii files, but if this changes sometimes ** 'SyFopen' must adjust the mode argument to open the file in binary mode. +** +** If is TRUE, files with names ending '.gz' will be +** automatically compressed/decompressed using gzip. */ -Int SyFopen(const Char * name, const Char * mode); +Int SyFopen(const Char * name, const Char * mode, BOOL transparent_compress); /**************************************************************************** diff --git a/tst/testinstall/compressed.tst b/tst/testinstall/compressed.tst index 690551a7f6..4a523a93ae 100644 --- a/tst/testinstall/compressed.tst +++ b/tst/testinstall/compressed.tst @@ -1,30 +1,49 @@ -#@local dir,fname,isGzippedFile,stream,str +#@local o,dir,fname,rawfname,checkGzippedFile,stream,str gap> START_TEST("compressed.tst"); gap> dir := DirectoryTemporary();; gap> fname := Filename(dir, "test.g.gz");; +gap> rawfname := Filename(dir, "rawtest.g.gz");; # Let us check when we have written a compressed file by checking the gzip header -gap> isGzippedFile := function(dir, name) -> local out, str,prog; -> str := ""; -> out := OutputTextString(str, true); -> Process(dir, Filename(DirectoriesSystemPrograms(),"cat"), InputTextNone(), out, [name]); -> return str{[1..2]} = "\037\213"; +# We need raw file access to do this, so we use 'IO'. We stub out this part of the +# test if IO is not loaded +gap> checkGzippedFile := function(fname, expected) +> local ins, str; +> if not IsPackageLoaded("IO") then return true; fi; +> # Use 'ValueGlobal' to avoid warnings about undefined functions +> ins := ValueGlobal("IO_File")(fname, "r"); +> str := ValueGlobal("IO_Read")(ins, 2);; +> # All gzipped files should start with these two characters +> return (str = [CharInt(31),CharInt(139)]) = expected; > end;; gap> str := "hello\ngoodbye\n";; -# Write a compressed file -gap> FileString( fname, str ) = Length(str); +# Write an uncompressed file +gap> FileString( rawfname, str ) = Length(str); true +# Write a compressed file, using OutputGzipFile +gap> o := OutputGzipFile(fname, false);; +gap> WriteAll(o, str); +true +gap> CloseStream(o); + # Check file really is compressed -gap> isGzippedFile(dir, "test.g.gz"); +gap> checkGzippedFile(fname, true); +true + +# Check file really is NOT compressed +gap> checkGzippedFile(rawfname, false); true # Check reading compressed file gap> StringFile( fname ) = str; true +# Check reading uncompressed file +gap> StringFile( rawfname ) = str; +true + # Check gz is added transparently gap> StringFile( Filename(dir, "test.g") ) = str; true @@ -68,7 +87,7 @@ true gap> CloseStream(stream); # Test multiple writes -gap> stream := OutputTextFile( fname, false );; +gap> stream := OutputGzipFile( fname, false );; gap> PrintTo( stream, "1"); gap> AppendTo( stream, "2"); gap> PrintTo( stream, "3"); @@ -77,7 +96,7 @@ true gap> CloseStream(stream); gap> stream; closed-stream -gap> isGzippedFile(dir, "test.g.gz"); +gap> checkGzippedFile(fname, true); true # verify it @@ -113,7 +132,7 @@ gap> stream; closed-stream # append to initial data -gap> stream := OutputTextFile( fname, true );; +gap> stream := OutputGzipFile( fname, true );; gap> PrintTo( stream, "4"); gap> CloseStream(stream); @@ -126,7 +145,7 @@ gap> stream; closed-stream # overwrite initial data -gap> stream := OutputTextFile( fname, false );; +gap> stream := OutputGzipFile( fname, false );; gap> PrintTo( stream, "new content"); gap> CloseStream(stream); @@ -145,7 +164,7 @@ gap> ReadAll(stream, 3); gap> CloseStream(stream); # test PrintFormattingStatus -gap> stream := OutputTextFile( fname, false );; +gap> stream := OutputGzipFile( fname, false );; gap> PrintFormattingStatus(stream); true gap> PrintTo( stream, "a very long line that GAP is going to wrap at 80 chars by default if we don't do anything about it\n"); @@ -153,7 +172,7 @@ gap> CloseStream(stream); gap> StringFile(fname); "a very long line that GAP is going to wrap at 80 chars by default if we don't\ \\\ndo anything about it\n" -gap> stream := OutputTextFile( fname, false );; +gap> stream := OutputGzipFile( fname, false );; gap> SetPrintFormattingStatus(stream, false); gap> PrintFormattingStatus(stream); false diff --git a/tst/testinstall/kernel/streams.tst b/tst/testinstall/kernel/streams.tst index ce620b9e6d..b8fd9d8c53 100644 --- a/tst/testinstall/kernel/streams.tst +++ b/tst/testinstall/kernel/streams.tst @@ -142,11 +142,14 @@ gap> IS_END_OF_FILE(0); false gap> IS_END_OF_FILE(254); fail -gap> OUTPUT_TEXT_FILE(fail, fail); +gap> OUTPUT_TEXT_FILE(fail, fail, fail); Error, OUTPUT_TEXT_FILE: must be a string (not the value 'fail') -gap> OUTPUT_TEXT_FILE("test", fail); +gap> OUTPUT_TEXT_FILE("test", fail, fail); Error, OUTPUT_TEXT_FILE: must be 'true' or 'false' (not the value 'fa\ il') +gap> OUTPUT_TEXT_FILE("test", true, fail); +Error, OUTPUT_TEXT_FILE: must be 'true' or 'false' (not the value 'fail\ +') # gap> POSITION_FILE(fail);