ServerJS/API/file
Jump to navigation
Jump to search
Alright. The task of the month is to ratify a File API. There's been relatively little discussion on this topic. There's not much opportunity to advance the state of the art with yet another file API, which means that this will inevitably degenerate into shed painting, but it must be done! Let's just slog through this shall we? I've composed all of the functions from Ruby, Python, and Java into a very long list here. Please note which names you prefer, whether you feel a particular function ought to not be part of the standard API, any functions that are missing, and of course comment and discuss semantics. I will attempt to reserve my opinions for a follow up post. Let's begin with some high level preferences. There are a few naming choices that can be applied throughout the following document with rigor. Rather than note every occurrence of the offending word, please state your general preference: 1. make vs create make: Wes Garland (for dirs) create: George Mosochovitis, Wes Garland (for file mode) 2. dir vs directory dir: directory: Tom Robinson, Kevin Dangoor, George Mosochovitis, Wes Garland 3. cur vs current cur: current: Tom Robinson, Kevin Dangoor, Wes Garland, Kris Kowal 4. canonize, realize, normalize, qualify, canon, real, normal, qualified, absolute, canonicalize remove . and ..: normalize: Tom Robinson, Kevin Dangoor, Kris Kowal, George Mosochovitis qualify: Wes Garland remove . and .., start at /: absolute: Tom Robinson, Kevin Dangoor, Kris Kowal remove . and .., start at /, follow symlinks: canonical: Tom Robinson, Kevin Dangoor, Kris Kowal real: Tom Robinson, Kevin Dangoor Could apply for either of the two latter: absolute: George Moscochovitis, Wes Garland qualified: Wes Garland 5. constantCase vs CONSTANT_CASE vs kConstantCase There's a lot of legacy Netscape code with the pseudo Hungarian kConstantCase. CONSTANT_CASE: Tom Robinson, Kevin Dangoor constantCase: Kris Kowal, Wes Garland 6. Should "static" File methods be placed in require('file').File or : Tom Robinson, George Mosochovitis require('file') : Kris Kowal, Wes Garland e.g., var files = require('file'); files.move(a, b) vs. var File = require('file').File; File.move(a, b); Tom Robinson: should we use encoding here? Wes Garland: "b" should be stricken Wes Garland: encoding should actually be an encode function Kris Kowal: encoding should be the name of an encoding module 7. Choose: a.) setX/getX vs a.) For all functions for which it would make sense to have a setter and a getter. (It would not make sense to have a setSize, so size() would be permissible) b.) For all functions that get things (getSize, getMtime, setMtime, getBaseName) b.) setX/X Kris Kowal c.) pick and choose; no overarching convention d.) X property with getters and setters for: Davey Waterson Wes Garland Daniel Friesen against: Tom Robinson Kris Kowal exists(path) - Wes Garland Python: exists (uses stat(path) and checks for exception) Ruby: exist? exists? linkExists(path) Python: lexists Returns whether a file exists, even if it ultimately refers to a broken symbolic link. Java: not provided Wes Garland: exists(path, true) Kris Kowal: linkExists(path) move(source, target) Ruby: move rename(source, target) Ruby: rename copy(source, target) Ruby: copy systemCopy(source, target): Ruby: syscopy Copies a file from to to. If to is a directory, copies from to to/from. unnecessary - Wes Garland makeDirectory(path) Unix: mkdir createDirectory makePath(path) Ruby: makedirs Unix: mkdir -p mkpath, mkdirs, createPath, createDirectories Wes Garland: makeDirectory(path, true) Kris Kowal: makePath(path) makeTempFile(prefix, suffix) Java: createTempFile Python: mktemp mktemp(prefix, suffix), makeTemporary, makeTemp Wes Garland: this should be ommitted in favor of an alternate File constructor remove(path) Ruby: unlink, delete Python: unlink, remove Java: delete Posix: unlink Unix: rm (remove) Tom Robinson, Kevin Dangoor: remove, definitely not delete Wes Garland: unlink, but remove and delete are fine symlink(source, target) Ruby: symlink(old, new) link(source, target) Creates a hard link Ruby: File.link(old_name, new_name) => 0 Creates a new name for an existing file using a hard link. Will not overwrite new_name if it already exists (raising a subclass of SystemCallError). Not available on all platforms. Wes Garland: link(source, target, hard:Boolean) Kris Kowal: symlink, link truncate(file_name, length) create(path) Java: create Posix: creat lock(path, "shared|exculsive") - Wes Garland writeLock(path), readLock(path) tryLock(path, "shared|exclusive") - Wes Garland unlock(path) - Wes Garland opens a lock dup(fileno) - unnecessary Wes Garland dup2(fileno, fileno) - unnecessary Wes Garland Weights and Measures ==================== [new] Stat(path) returns a Stat object (more later) stat(path) returns an Array Posix: stat Java: omitted updateStat(:Stat) - Wes Garland setStat(:Stat) linkStat(path): Python: lstat Wes Garland: stat(path, true) Kris Kowal: linkStat(path) atime(path) - Tom Robinson, Kevin Dangoor Java: omitted Python: getatime Ruby: atime lastAccessed mtime(path) - Tom Robinson, Kevin Dangoor Java: lastModified Python: getmtime Ruby: mtime stat(path).st_mtime lastModified touch(path) - Kris Kowal setLastModified(path, date) setMtime(path, date) ctime(path) - Tom Robinson, Kevin Dangoor Unix: touch Java: omitted Python: getctime Ruby: ctime lastNodeModified size(path) - Tom Robinson, Kevin Dangoor Ruby: size? Java: length Python: getsize Posix: size: stat(path).st_size getLength, length, getSize, size Ownership ========= getOwner(path) getGroupOwner(path) setOwner(path, user, [group]) chown setGroupOwner(path, group) chown setLinkOwner(path, owner, [group]) Ruby: file.lchown(owner_int, group_int, file_name,..) => integer Equivalent to File::chown, but does not follow symbolic links (so it will change the owner associated with the link, not the file referenced by the link). Often not available. Returns number of files in the argument list. Wes Garland: use SetOwner(path, owner, group, true) setLinkGroupOwner(path, group) isOwned() Ruby: owned? Returns true if the named file exists and the effective used id of the calling process is the owner of the file. isGroupOwned() Ruby: grpowned? Returns true if the named file exists and the effective group id of the calling process is the owner of the file. Returns false on Windows. Please Sir, Can I Have Some More ================================ isReadable(path) Java: canRead Ruby: readable isReallyReadable(path) Ruby: readable_real? returns whether the file is readable by the current process owner. isWritable(path) Java: canWrite Ruby: writable isReallyWritable(path) Ruby: writable_real? isExecutable(path) Ruby: executable? Python: omitted isReallyExecutable(path) Ruby: executable_real? isSticky(path) returns whether the sticky bit is set, which is to say that anyone can create a file in the directory but cannot delete those created by other users. executesAsOwner(path) returns whether the file has the SID bit set. executesAsGroupOwner(path) returns whether the file has the GID bit set. setMode(path, mode) Posix: chmod Unix: chmod Ruby: chmod getMode(path) setLinkMode(path, mode) Ruby: File.lchmod(mode_int, file_name, ...) => integer Equivalent to File::chmod, but does not follow symbolic links (so it will change the permissions associated with the link, not the file referenced by the link). Often not available. setReadOnly(true|false) getModeMask() Ruby: umask() setModeMask(umask) Ruby: umask(umask) What is My Name =============== dirName(path) Python: dirname Returns the directory that contains the given path Ruby: File.dirname(file_name) Wes Garland: dirname Kris Kowal: dirName baseName(path) Java: File(fileName).getName() Python: basename(file_name) Ruby: basename(file_name, [suffix]) Unix: basename name [suffix] Wes Garland: basename Kris Kowal: baseName extension(path) Ruby: extname extensionName extension: George Mosochovitis, Wes Garland, Kris Kowal split Ruby: split Python: split Split a path in head (everything up to the last '/') and tail (the rest). If the path ends in '/', tail will be empty. If there is no '/' in the path, head will be empty. Trailing '/'es are stripped from head unless it is the root. pathComponents(path) Wes Garland splitExtension Python: splitext Split a path in root and extension. The extension is everything starting at the last dot in the last pathname component; the root is everything before that. It is always true that root + ext == p. splitDrive Python: splitDrive Split a pathname into a drive specification and the rest of the path. Useful on DOS/Windows/NT; on Unix, the drive is always empty. Where Did I Come From ===================== getType(fileName) Ruby: ftype(file_name) Identifies the type of the named file; the return string is one of ``file’’, ``directory’’, ``characterSpecial’’, ``blockSpecial’’, ``fifo’’, ``link’’, ``socket’’, or ``unknown’’. Wes Garland: type or osType isFile Python: isfile Java: isFile Ruby: file? isDirectory Java: isDirectory Python: isdir Ruby: directory? isLink Java: omitted Python: islink Ruby: symlink? isSymlink would be more accurate since it doesn't test hard links, but everything's a hard link so it's not like the name "isLink" will ever be needed for that purpose. isMount Python: ismount with some question of inter-unix compatibility undesirable - Wes Garland isSocket Ruby: socket? isPipe Ruby: pipe? isBlockDevice Ruby: blockdev?(file_name) Where Am I ========== cwd Python: os.getcwd Unix: pwd getCwd, workingDir, presentWorkingDir, currentWorkingDir Projection ---------- join(base, path) Python: join(base, rel) Java: File(parent, child) resolve(path, [base=cwd]) the path to a given path from another Python: resolve(rel, abs = '.') -> join(abs, rel) Python: abspath(path) -> normpath(join(cwd(), path)) unnecessary - Wes Garland relative(source, target) the relative path from source to target parent(path) Java: getParent Wes Garland: needs to be clear whether it's the lexical parent on the path or the parent link in the file system concatName(base, rel) Ruby: catname(from, to) If to is a valid directory, from will be appended to to, adding and escaping backslashes as necessary. Otherwise, to will be returned. Useful for appending from to to only if the filename was not specified in to. unnecessary - Wes Garland Identification -------------- absolute(path) follows symlinks from the root and produces a canonical, normal form of a path Python: realpath Unix: realpath realize, realPath, canon, canonize, canonical, getCanonical, normalize isAbsolute(path) Java: isAbsolute Python: isabs normalize(path) Python: normpath Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B. It should be understood that this may change the meaning of the path if it contains symbolic links! Jack: canonicalize canon, canonize, canonical, normal, getCanonical, getNormal normalizeCase(path) Python: normcase Normalize the case of a pathname. Trivial in Posix, string.lower on Mac. On MS-DOS this may also turn slashes into backslashes; however, other normalizations (such as optimizing '../' away) are not allowed (another function should be defined to do that). unnecessary - Wes Garland samePath(a, b) Python: samefile Ruby: identical? sameFile(a, b) Python: sameopenfile sameStat(a, b) Python: sameStat Returns whether the inode and dev are the same Platform Constants ------------------ selfDirectory ('.') Python: curdir parentDirectory ('..') Python: pardir parDir extensionSeparator Java: separator Python: extsep extsep, extSep, extensionSeparator, EXTENSION_SEPARATOR separator Java: pathSeparator Python: sep sep, separator, SEPARATOR alternateSeparator Python: altsep altsep alternateSeparator ALTERNATE_SEPARATOR deviceNull Python: devnull devNull DEV_NULL: unnecessary - Wes Garland path Python: defpath = ':/bin:/usr/bin' PATH unnecessary - Wes Garland supportsUnicodeFileName Python: supports_unicode_filenames What is a "Phone Book" ====================== match(pattern, fileName) Ruby: fnmatch(pattern, file_name, [flags]) glob(pattern) - Wes Garland, Kris Kowal Ruby: Dir[pattern] list(path) Python: os.listdir Ruby: Dir[path_glob] listdir, Dir listFiles, listDirs listIter next prev listRoots List the available filesystem roots. expand Python: expandUser -> expandVars -> normalize -> real Ruby: expand_path expandUser Python: expanduser expands '~user' expandVars Python: expandvars expands shell variables like $var and ${var} unnecessary - Wes Garland Is That Really Necessary ======================== isHidden Java: isHidden no - Wes Garland isZero(fileName) Ruby: zero? Perl: -z returns whether the file exists and is of zero length no - Wes Garland compare(source, target) Ruby: compare no - Wes Garland install(from, to, [mode], [verbose = false]) Ruby: install If src is not the same as dest, copies it and changes the permission mode to mode. If dest is a directory, destination is dest/src. If mode is not set, default is used. If verbose is set to true, the name of each file copied will be printed. no - Wes Garland url(path) Java: toURL no - Wes Garland uri(path) Java: toURI no - Wes Garland walk Python: Walk a directory tree. no - Kris Kowal commonPrefix return the longest common prefix of a list of paths. no - Kris Kowal File Type =========== [new] File(path, "+rwab", encoding) File(fileNo:Number) File(path:String, mode, encoding) File({path, mode, buffering, encoding, newLine}) File({directory, prefix}) - temp file File({command}) Mode: +, update a, append r, read w, write x, exclusive (lock) c, canon (nonblocking) construction implicitly opens does the javascript GC call a destructor function? needs to. - Kris Kowal, Wes Garland Java: FileStream Python: open is also file. file is generally preferred read() actual:Number reads to eof and returns binary read(length) actual:Number Tom Robinson, Kevin Dangoor, Wes Garland, Kris Kowal: should return Binary and Binary should have .toString(encoding=default) readLine() :String next() readInto(buffer:Binary) actual:Number write(:Binary) actual:Number seek(pos:Number, whence:Number code) tell() pos:Cookie truncate([pos:Cookie]) close() undefined readable() whether it was opened for reading writable() whether it was opened for writing seekable() whether there is random access fileNo undefined if the underlying implementation does not use file descriptors raw() raw is undefined if there is no underlying unbuffered file object readLine() :String returns "" if EOF hit immediately alternately input() to distinguish from read next() next line throws if EOF hit immediately iter() returns self (since it implements next()) forEach(relation) each(relation) readLines() writeLine() writeln, print writeLines(lines) isEOF isEof - Kris Kowal atEOF - Wes Garland, maybe fcntl - not recommended , use ioctl - Wes Garland getFileNo - not recommended, - Wes Garland toNumber an alias for `getFileNo` - not recommeded - Wes Garland getLineNo - encoding dependent, not recommended - Wes Garland isTty isTTY - Wes Garland rewind flush seek stat select fsync ioctl truncate writeLock readLock unlock dup() stat lastAccessed lastModified setLastModified lastNodeModified getSize setPos Posix/Python: seek getPos Posix/Python: tell isReadable isReallyReadable isWritable isReallyWritable isExecutable isReallyExecutable executesAsOwner executesAsGroupOwner setMode getMode setReadOnly pathComponents - Wes Garland drive extensions baseName dirName absolute fullPath - Wes Garland Directory Type ============== list forEach each iter isSticky remove match glob Stat Type ========= permissions Permissions inode Number // optional linkCount Number // optional owner String group String // optional size Number atime Date mtime Date ctime Date Permissions Type ================ owner ["read", "write"] group ["read"] world [] sticky setuid setgid Binary Type =========== Binary(:Array, [width, endian]) Binary(:String, [ecoding]) Binary.fromArray(:Array, [width, endian]) Binary.fromString(:String, [encoding]) Binary.createBuffer(length, [fill = 0]) Binary.prototype.toString(encoding) Array.prototype.toBinary([width, endian]) Todo ==== Split File into File and IO base (Tom Robinson, Kevin Dangoor) Clarify difference between canRead, isReadable, and select Resolve the default encoding for Binary.toString() Note distinction between read():Binary and input():String, as well as write(:Binary) and print(String) getEncoding setEncoding George Mosochovitis: divide among FileSystem, Path, and File References ========== http://www.python.org/dev/peps/pep-3116/ http://www.erights.org/javadoc/java/io/File.html http://java.sun.com/j2se/1.4.2/docs/api/java/io/File.html http://java.sun.com/j2se/1.4.2/docs/api/java/io/FileOutputStream.html http://java.sun.com/j2se/1.4.2/docs/api/java/io/FileInputStream.html http://www.cs.berkeley.edu/~daw/joe-e/api/org/joe_e/file/Filesystem.html http://docs.python.org/library/os.path.html http://www.ruby-doc.org/core/classes/File.html http://www.ruby-doc.org/core/classes/IO.html