|
#############################################################################
##
## This file is part of GAP, a system for computational discrete algebra.
## This file's authors include Frank Celler.
##
## Copyright of GAP belongs to its developers, whose names are too numerous
## to list here. Please refer to the COPYRIGHT file for details.
##
## SPDX-License-Identifier: GPL-2.0-or-later
##
## This file contains the methods for files and directories.
##
Unbind(InfoTempDirectories);
DeclareInfoClass("InfoTempDirectories");
SetInfoLevel(InfoTempDirectories,1);
#############################################################################
##
#R IsDirectoryRep . . . . . . . . . . default representation of a directory
##
if IsHPCGAP then
DeclareRepresentation( "IsDirectoryRep", IsReadOnlyPositionalObjectRep, [] );
else
DeclareRepresentation( "IsDirectoryRep", IsPositionalObjectRep, [] );
fi;
#############################################################################
##
#V DirectoryType . . . . . . . . . . . . . . . . default type of a directory
##
BindGlobal( "DirectoryType", NewType(
DirectoriesFamily,
IsDirectory and IsDirectoryRep ) );
#############################################################################
##
#M Directory( <str> ) . . . . . . . . . . . . create a new directory object
##
InstallMethod( Directory,
"string",
[ IsString ],
function( str )
str := UserHomeExpand(str);
#
# ':' or '\\' probably are untranslated MS-DOS or MacOS path
# separators, but ':' in position 2 may be OK
#
if '\\' in str or (':' in str and str[2] <> ':') then
Error( "<str> must not contain '\\' or ':'" );
fi;
if Length( str ) > 0 and Last(str) = '/' then
str := Immutable(str);
else
str := Immutable( Concatenation( str, "/" ) );
fi;
return Objectify( DirectoryType, [str] );
end );
# Make Directory() idempotent, like String() and Int()
InstallOtherMethod( Directory, "directory", [ IsDirectory ], IdFunc );
#############################################################################
##
#M EQ( <dir1>, <dir2> ) . . . . . . . . . . . equality for directory objects
##
InstallMethod( EQ,
"for two directories",
[ IsDirectory, IsDirectory ],
function( d1, d2 ) return d1![1] = d2![1]; end );
#############################################################################
##
#M ViewObj( <directory> ) . . . . . . . . . . . . . view a directory object
##
InstallMethod( ViewObj,
"default directory rep",
[ IsDirectoryRep ],
function( obj )
Print( "dir(\"", obj![1] ,"\")" );
end );
#############################################################################
##
#M PrintObj( <directory> ) . . . . . . . . . . . . print a directory object
##
InstallMethod( PrintObj,
"default directory rep",
[ IsDirectoryRep ],
function( obj )
Print( "Directory(\"", obj![1] ,"\")" );
end );
#############################################################################
##
#M Filename( <directory>, <string> ) . . . . . . . . . . . create a filename
##
InstallMethod( Filename,
"for a directory and a string",
[ IsDirectory,
IsString ],
function( dir, name )
if '\\' in name or ':' in name then
Error( "<name> must not contain '\\' or ':'" );
fi;
return Immutable( Concatenation( dir![1], name ) );
end );
#############################################################################
##
#M Filename( <directories>, <string> ) . . . . . . . . search for a filename
##
InstallMethod( Filename,
"for a list and a string",
[ IsList, IsString ],
function( dirs, name )
local dir, new, newgz;
for dir in dirs do
new := Filename( dir, name );
newgz := Concatenation(new,".gz");
if IsExistingFile(new) = true or IsExistingFile(newgz) = true then
return new;
fi;
od;
return fail;
end );
#############################################################################
##
#M ExternalFilename( <directory>, <string> )
#M ExternalFilename( <directories>, <string> )
##
BindGlobal("MakeExternalFilename",
function(name)
local path, prefix;
if ARCH_IS_WINDOWS() and name <> fail then
prefix := First( [ "/proc/cygdrive/", "/cygdrive/" ], s -> StartsWith( name, s ) );
if prefix <> fail then
path := Concatenation("C:",name{[Length(prefix)+2..Length(name)]});
path[1] := name[Length(prefix)+1]; # drive name
return ReplacedString(path,"/","\\");
else
return ReplacedString(name,"/","\\");
fi;
else
return name;
fi;
end);
InstallMethod( ExternalFilename, "for a directory and a string",
[ IsDirectory, IsString ],
function( d, s )
return MakeExternalFilename(Filename(d,s));
end );
InstallMethod( ExternalFilename, "for a directory list and a string",
[ IsList, IsString ],
function( d, s )
return MakeExternalFilename(Filename(d,s));
end );
#############################################################################
##
#F DirectoryContents(<name>)
##
InstallGlobalFunction(DirectoryContents, function(dirname)
if IsDirectory(dirname) then
dirname := dirname![1];
else
# to make ~/mydir work
dirname := UserHomeExpand(dirname);
fi;
return LIST_DIR(dirname);
end);
#############################################################################
##
#M Read( <filename> ) . . . . . . . . . . . . . . . . . . . read in a file
##
READ_INDENT := "";
if IsHPCGAP then
MakeThreadLocal("READ_INDENT");
fi;
InstallMethod( Read,
"string",
[ IsString ],
function ( name )
local readIndent;
if GAPInfo.CommandLineOptions.D then
readIndent := SHALLOW_COPY_OBJ( READ_INDENT );
APPEND_LIST_INTR( READ_INDENT, " " );
Print( "#I", READ_INDENT, "Read( \"", name, "\" )\n" );
fi;
if not READ(UserHomeExpand(name)) then
Error( "file \"", name, "\" must exist and be readable" );
fi;
if GAPInfo.CommandLineOptions.D then
READ_INDENT := readIndent;
if READ_INDENT = "" then
Print( "#I Read( \"", name, "\" ) done\n" );
fi;
fi;
end );
#############################################################################
##
#M ReadAsFunction( <filename> ) . . . . . . . . . . read a file as function
##
InstallMethod( ReadAsFunction,
"string",
[ IsString ],
name -> READ_AS_FUNC( UserHomeExpand( name ) ) );
#############################################################################
##
#M Edit( <filename> ) . . . . . . . . . . . . . . . . . edit and read file
##
# The editor can be specified at startup time via a user preference.
DeclareUserPreference( rec(
name:= [ "Editor", "EditorOptions" ],
description:= [
"Determines the editor and options (used by &GAP;'s <Ref Func=\"Edit\"/> \
command). \
Under macOS, the value <C>\"open\"</C> for <C>Editor</C> will work. \
For further options, \
see the &GAP; help for <Ref Func=\"Edit\"/>. \
If you want to use the editor defined in your (shell) environment then \
leave the <C>Editor</C> and <C>EditorOptions</C> preferences empty."
],
default:= function() # copied from GAPInfo.READENVPAGEREDITOR
local str, sp;
if IsBound(GAPInfo.KernelInfo.ENVIRONMENT.EDITOR) then
str := GAPInfo.KernelInfo.ENVIRONMENT.EDITOR;
sp := SplitStringInternal(str, "", " \n\t\r");
if Length(sp) > 0 then
return [ sp[1], sp{[2..Length(sp)]} ];
fi;
fi;
return [ "vi", [] ];
end,
check:= function( editor, editoroptions )
return IsString( editor ) and IsList( editoroptions )
and ForAll( editoroptions, IsString );
end,
) );
InstallGlobalFunction( Edit, function( name )
local editor, ret;
name := UserHomeExpand(name);
editor := PathSystemProgram( UserPreference("Editor") );
if editor = fail then
Error( "cannot locate editor `", UserPreference("Editor"),
"' (reset via SetUserPreference(\"Editor\", ...))" );
fi;
ret := Process( DirectoryCurrent(), editor, InputTextUser(),
OutputTextUser(), Concatenation(
UserPreference("EditorOptions"), [ name ]) );
if ret <> 0 then
Error( "editor returned ", ret );
fi;
Read(name);
end );
# try to find the HOME directory in the environment.
BindGlobal("StringHOMEPath",function()
local env;
if IsBound(GAPInfo.UserHome) then
return GAPInfo.UserHome;
fi;
env:=GAPInfo.SystemEnvironment;
if IsRecord(env) then
env:=env.HOME;
else
env:=First(env,x->Length(x)>5 and x{[1..5]}="HOME=");
env:=env{[6..Length(env)]};
fi;
return env;
end);
InstallGlobalFunction(DirectoryHome,function()
local a,h,d;
h:=StringHOMEPath();
if h = fail then
return fail;
elif ARCH_IS_WINDOWS() then
d:=List(DirectoryContents(h),LowercaseString);
a:=First(["My Documents", #en
"Documents", #en-win8
"Eigene Dateien", #de
"Documenti", #it
"Mes documents", #fr
"Mijn documenten", #nl
"Meus documentos", #pt
"Mis documentos", #es
"Mina dokument", #sv
"Mine dokumenter", #no
"Dokumentumok", #hu
"Dokumenty", #cz
"Moje dokumenty", #po
"Omat tiedostot", #fi
"Τα ÎγγÏαφά μου", #gr
"Мои Документы", #ru
],x->LowercaseString(x) in d);
if a<>fail then
if Last(h)<>'/' then
h := Concatenation(h,"/");
fi;
return Directory(Concatenation(h,a));
fi;
fi;
return Directory(h);
end);
InstallGlobalFunction(DirectoryDesktop,function()
local a,h,d;
h:=StringHOMEPath();
if h = fail then
return fail;
elif ARCH_IS_WINDOWS() then
d:=List(DirectoryContents(h),LowercaseString);
a:=First(["Desktop",
"Bureau", #fr
"Bureaublad", #nl
"Escritorio", #es
"Επιφάνεια εÏγασίας", #gr
],x->LowercaseString(x) in d);
if a<>fail then
if Last(h)<>'/' then
h := Concatenation(h,"/");
fi;
return Directory(Concatenation(h,a));
fi;
else
d:=List(DirectoryContents(h),LowercaseString);
a:=First(["Desktop",
"Bureau", #fr
"Bureaublad", #nl
"Escritorio", #es
],x->LowercaseString(x) in d);
if a<>fail then
if Last(h)<>'/' then
h := Concatenation(h,"/");
fi;
return Directory(Concatenation(h,a));
fi;
fi;
return Directory(h);
end);
InstallGlobalFunction(RemoveDirectoryRecursively,
function(dirname)
# dirname must be a string
local Dowork;
if not IS_DIR(dirname) then
Error("dirname must be a directory");
return fail;
fi;
dirname := ShallowCopy(dirname);
while Length(dirname) > 0 and Last(dirname) = '/' do
Remove(dirname);
od;
if Length(dirname) = 0 then
Error("dirname must be nonempty");
return fail;
fi;
Dowork := function(pathname)
# pathname does not end in a / and is known to be a proper directory
local c,f,fullname;
c := DirectoryContents(pathname);
for f in c do
if f <> "." and f <> ".." then
fullname := Concatenation(pathname,"/",f);
if IS_DIR(fullname) then
Dowork(fullname);
else
RemoveFile(fullname);
fi;
fi;
od;
return RemoveDir(pathname);
end;
return Dowork(dirname);
end );
InstallGlobalFunction( HexSHA256,
function(str)
local s, res;
if IsString(str) then
str := CopyToStringRep(str);
elif IsInputStream(str) then
str := ReadAll(str);
# TODO: instead o reading the complete stream at once (which might be
# huge), it would be better to read it in chunks, say 16kb at a time.
# Alas, our streams API currently offers no way to do that.
else
ErrorNoReturn("<str> has to be a string or an input stream");
fi;
s := GAP_SHA256_INIT();
GAP_SHA256_UPDATE(s, str);
res := GAP_SHA256_FINAL(s);
res := Sum([0..7], i -> res[8-i]*2^(32*i));;
return LowercaseString(HexStringInt(res));
end);
[ Dauer der Verarbeitung: 0.42 Sekunden
(vorverarbeitet)
]
|