lib/7.8/modules/Filesystem.pmod/System.pike
#pike 7.8
//! Implements an abstraction of the normal filesystem.
inherit Filesystem.Base;
// Some notes about NT code:
// Paths are assumed to follow one of two different patterns:
// 1. /foo/bar/gazonk indicates a path on the current drive
// 2. D:/foo/bar/gazonk a path on the drive indicated by D.
//
// It's possible to change the root from /foo/bar to d:/foo/bar/something,
// but it's not possible to change from c:/foo/bar to d:/foo/bar
//
// Generally speaing, paths will almost always follow the second
// pattern, since getcwd() is used to get the initial value of the
// 'wd' variable unless a different path is specified.
//
protected Filesystem.Base parent; // parent filesystem
protected string root = ""; // Note: Can now include leading "/"
protected string wd; // never trailing "/"
//! @decl void create(void|string directory, void|string root, void|int fast, void|Filesystem.Base parent)
//! Instanciate a new object representing the filesystem.
//! @param directory
//! The directory (in the real filesystem) that should become
//! the root of the filesystemobject.
//! @param root
//! Internal
//! @param fast
//! Internal
//! @param parent
//! Internal
protected void create(void|string directory, // default: cwd
void|string _root, // internal: root
void|int fast, // internal: fast mode (no check)
void|Filesystem.Base _parent)
// internal: parent filesystem
{
if( _root )
{
#ifdef __NT__
_root = replace( _root, "\\", "/" );
#endif
sscanf(reverse(_root), "%*[/]%s", root);
root = reverse( root ); // do not remove leading '/':es.
}
if(!fast)
{
Stdio.Stat a;
if(!directory || directory=="" || directory[0]!='/')
directory = combine_path(getcwd(), directory||"");
#ifdef __NT__
directory = replace( directory, "\\", "/" );
string p;
if( sizeof(root) )
{
if( sscanf( directory, "%s:/%s", p, directory ) )
{
if( sizeof( root ) )
{
if( !has_value( root, ":/" ) )
root = p+":"+ (root[0] == '/' ?"":"/") + root;
else
error( "Cannot change directory to above"+root+"\n" );
}
else
root = p+":/";
}
}
#endif
while( sizeof(directory) && directory[0] == '/' )
directory = directory[1..];
while( sizeof(directory) && directory[-1] == '/' )
directory = directory[..<1];
#ifdef __NT__
if( sizeof( directory ) != 2 || directory[1] != ':' )
#endif
if(!(a = file_stat(combine_path("/",root,directory))) || !a->isdir)
error("Not a directory\n");
}
while( sizeof(directory) && directory[0] == '/' )
directory = directory[1..];
while( sizeof(directory) && directory[-1] == '/' )
directory = directory[..<1];
wd = directory;
}
protected string _sprintf(int t)
{
return t=='O' && sprintf("%O(/* root=%O, wd=%O */)", this_program, root, wd);
}
Filesystem.Base cd(string directory)
{
Filesystem.Stat st = stat(directory);
#ifdef __NT__
directory = replace( directory, "\\", "/" );
#endif
if(!st) return 0;
if(st->isdir()) // stay
return this_program(combine_path(wd, directory),
root, 1, parent);
return st->cd(); // try something else
}
Filesystem.Base cdup()
{
return cd("..");
}
string cwd()
{
return wd;
}
Filesystem.Base chroot(void|string directory)
{
if(directory)
{
Filesystem.Base new = cd(directory);
if(!new) return 0;
return new->chroot();
}
return this_program("", combine_path("/",root,wd), 1, parent);
}
Filesystem.Stat stat(string file, int|void lstat)
{
Stdio.Stat a;
#ifdef __NT__
file = replace( file, "\\", "/" );
while( sizeof(file) && file[-1] == '/' )
file = file[..<1];
#endif
string full = combine_path(wd, file);
if ( full!="" && full[0]=='/') full=full[1..];
if((a = file_stat(combine_path("/",root,full), lstat)))
{
Filesystem.Stat s = Filesystem.Stat();
s->fullpath = sprintf("/%s", full);
s->name = file;
s->filesystem = this;
s->attach_statobject(a);
return s;
}
else
return 0;
}
array(string) get_dir(void|string directory, void|string|array(string) globs)
{
#ifdef __NT__
if(directory)
{
directory = replace( directory, "\\", "/" );
while( sizeof(directory) && directory[-1] == '/' )
directory = directory[..<1];
}
#endif
directory = directory ? combine_path(wd, directory) : wd;
array(string) y = predef::get_dir(combine_path("/",root,directory));
if(!globs)
return y;
else if(stringp(globs))
return glob(globs, y);
else
{
array(string) p = ({});
foreach(globs, string g)
{
array(string) z;
p += (z = glob(g, y));
y -= z;
}
return p;
}
}
array(Filesystem.Stat) get_stats(void|string directory,
void|string|array(string) globs)
{
Filesystem.Base z = this;
#ifdef __NT__
if(directory)
{
directory = replace( directory, "\\", "/" );
while( sizeof(directory) && directory[-1] == '/' )
directory = directory[..<1];
}
#endif
if(directory &&
!(z = z->cd(directory)))
return 0;
array(string) a = z->get_dir("", globs);
if(!a) return 0;
return map(a, z->stat, 1)-({0});
}
Stdio.File open(string filename, string mode)
{
#ifdef __NT__
filename = replace( filename, "\\", "/" );
#endif
filename = combine_path(wd, filename);
if ( filename!="" && filename[0]=='/') filename=filename[1..];
Stdio.File f = Stdio.File();
if( !f->open( combine_path("/",root,filename), mode) )
return 0;
return f;
}
// int access(string filename, string mode)
// {
// return 1; // sure
// }
int rm(string filename)
{
#ifdef __NT__
filename = replace( filename, "\\", "/" );
#endif
filename = combine_path(wd, filename);
return predef::rm(combine_path("/",root,filename));
}
void chmod(string filename, int|string mode)
{
#ifdef __NT__
filename = replace( filename, "\\", "/" );
#endif
filename = combine_path(wd, filename);
if(stringp(mode))
{
Filesystem.Stat st = stat(filename); // call to self
if(!st) return 0;
mode = Filesystem.parse_mode(st->mode, mode);
}
predef::chmod(combine_path("/",root,filename), mode);
}
void chown(string filename, int|object owner, int|object group)
{
#if constant(chown)
#ifdef __NT__
filename = replace( filename, "\\", "/" );
#endif
if(objectp(group))
error("user objects not supported (yet)\n");
if(objectp(owner))
error("user objects not supported (yet)\n");
filename = combine_path(wd, filename);
predef::chown(combine_path("/",root,wd), owner, group);
#else
error("system does not have a chown"); // system does not have a chown()
#endif
}
array find(void|function(Filesystem.Stat, mixed|void...:int) mask,
mixed|void ... extra)
{
array(Filesystem.Stat) res = ({});
array(Filesystem.Stat) d = get_stats() || ({});
array(Filesystem.Stat) r = filter(d, "isdir");
if(mask)
res += filter(d-r, mask, @extra);
else
res += d-r;
foreach(r, Filesystem.Stat dir)
{
if(!mask || mask(dir, @extra))
res += ({ dir });
if(dir->name=="." || dir->name=="..")
continue;
res += dir->cd()->find(mask, @extra);
}
return res;
}