implement Ar; ## ar - portable (ascii) format version# include "sys.m"; sys: Sys; include "draw.m"; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "daytime.m"; daytime: Daytime; include "string.m"; str: String; Ar: module{ init: fn(nil: ref Draw->Context, nil: list of string);}; ARMAG: con "!<arch>\n";SARMAG: con len ARMAG;ARFMAG0: con byte '`';ARFMAG1: con byte '\n';SARNAME: con 16; # ancient limit ## printable archive header# name[SARNAME] date[12] uid[6] gid[6] mode[8] size[10] fmag[2]#Oname: con 0;Lname: con SARNAME;Odate: con Oname+Lname;Ldate: con 12;Ouid: con Odate+Ldate;Luid: con 6;Ogid: con Ouid+Luid;Lgid: con 6;Omode: con Ogid+Lgid;Lmode: con 8;Osize: con Omode+Lmode;Lsize: con 10;Ofmag: con Osize+Lsize;Lfmag: con 2;SAR_HDR: con Ofmag+Lfmag; # 60 ## The algorithm uses up to 3 temp files. The "pivot contents" is the# archive contents specified by an a, b, or i option. The temp files are# astart - contains existing contentss up to and including the pivot contents.# amiddle - contains new files moved or inserted behind the pivot.# aend - contains the existing contentss that follow the pivot contents.# When all contentss have been processed, function 'install' streams the# temp files, in order, back into the archive.# Armember: adt { # one per archive contents name: string; # trimmed length: int; date: int; uid: int; gid: int; mode: int; size: int; contents: array of byte; fd: ref Sys->FD; # if contents is nil and fd is not nil, fd has contents next: cyclic ref Armember; new: fn(name: string, fd: ref Sys->FD): ref Armember; rdhdr: fn(b: ref Iobuf): ref Armember; read: fn(m: self ref Armember, b: ref Iobuf): int; wrhdr: fn(m: self ref Armember, fd: ref Sys->FD); write: fn(m: self ref Armember, fd: ref Sys->FD); skip: fn(m: self ref Armember, b: ref Iobuf); replace: fn(m: self ref Armember, name: string, fd: ref Sys->FD); copyout: fn(m: self ref Armember, b: ref Iobuf, destfd: ref Sys->FD);}; Arfile: adt { # one per tempfile fd: ref Sys->FD; # paging file descriptor, nil if none allocated head: ref Armember; tail: ref Armember; new: fn(): ref Arfile; copy: fn(ar: self ref Arfile, b: ref Iobuf, mem: ref Armember); insert: fn(ar: self ref Arfile, mem: ref Armember); stream: fn(ar: self ref Arfile, fd: ref Sys->FD); page: fn(ar: self ref Arfile): int;}; File: adt { name: string; trimmed: string; found: int;}; man := "mrxtdpq";opt := "uvnbailo"; aflag := 0;bflag := 0;cflag := 0;oflag := 0;uflag := 0;vflag := 0; pivotname: string;bout: ref Iobuf;stderr: ref Sys->FD;parts: array of ref Arfile; comfun: ref fn(a: string, f: array of ref File); init(nil: ref Draw->Context, args: list of string){ sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; daytime = load Daytime Daytime->PATH; str = load String String->PATH; stderr = sys->fildes(2); bout = bufio->fopen(sys->fildes(1), Sys->OWRITE); if(len args < 3) usage(); args = tl args; s := hd args; args = tl args; for(i := 0; i < len s; i++){ case s[i] { 'a' => aflag = 1; 'b' => bflag = 1; 'c' => cflag = 1; 'd' => setcom(dcmd); 'i' => bflag = 1; 'l' => ; # ignored 'm' => setcom(mcmd); 'o' => oflag = 1; 'p' => setcom(pcmd); 'q' => setcom(qcmd); 'r' => setcom(rcmd); 't' => setcom(tcmd); 'u' => uflag = 1; 'v' => vflag = 1; 'x' => setcom(xcmd); * => sys->fprint(stderr, "ar: bad option `%c'\n", s[i]); usage(); } } if(aflag && bflag){ sys->fprint(stderr, "ar: only one of 'a' and 'b' can be specified\n"); usage(); } if(aflag || bflag){ pivotname = trim(hd args); args = tl args; if(len args < 2) usage(); } if(comfun == nil){ if(uflag == 0){ sys->fprint(stderr, "ar: one of [%s] must be specified\n", man); usage(); } setcom(rcmd); } cp := hd args; args = tl args; files := array[len args] of ref File; for(i = 0; args != nil; args = tl args) files[i++] = ref File(hd args, trim(hd args), 0); comfun(cp, files); # do the command for(i = 0; i < len files; i++) if(!files[i].found){ sys->fprint(stderr, "ar: %s not found\n", files[i].name); cp = "error"; } bout.flush(); if(cp != nil) raise "fail:"+cp;} ## select a command#setcom(fun: ref fn(s: string, f: array of ref File)){ if(comfun != nil){ sys->fprint(stderr, "ar: only one of [%s] allowed\n", man); usage(); } comfun = fun;} ## perform the 'r' and 'u' commands#rcmd(arname: string, files: array of ref File){ bar := openar(arname, Sys->ORDWR, 1); parts = array[2] of {Arfile.new(), nil}; ap := parts[0]; if(bar != nil){ while((mem := Armember.rdhdr(bar)) != nil){ if(bamatch(mem.name, pivotname)) # check for pivot ap = parts[1] = Arfile.new(); f := match(files, mem.name); if(f == nil){ ap.copy(bar, mem); continue; } f.found = 1; dfd := sys->open(f.name, Sys->OREAD); if(dfd == nil){ if(len files > 0) sys->fprint(stderr, "ar: cannot open %s: %r\n", f.name); ap.copy(bar, mem); continue; } if(uflag){ (ok, d) := sys->fstat(dfd); if(ok < 0 || d.mtime <= mem.date){ if(ok < 0) sys->fprint(stderr, "ar: cannot stat %s: %r\n", f.name); ap.copy(bar, mem); continue; } } mem.skip(bar); mesg('r', f.name); mem.replace(f.name, dfd); ap.insert(mem); dfd = nil; } } # copy in remaining files named on command line for(i := 0; i < len files; i++){ f := files[i]; if(f.found) continue; f.found = 1; dfd := sys->open(f.name, Sys->OREAD); if(dfd != nil){ mesg('a', f.name); parts[0].insert(Armember.new(f.trimmed, dfd)); }else sys->fprint(stderr, "ar: cannot open %s: %r\n", f.name); } if(bar == nil && !cflag) install(arname, parts, 1); # issue 'creating' msg else install(arname, parts, 0);} dcmd(arname: string, files: array of ref File){ if(len files == 0) return; changed := 0; parts = array[] of {Arfile.new()}; bar := openar(arname, Sys->ORDWR, 0); while((mem := Armember.rdhdr(bar)) != nil){ if(match(files, mem.name) != nil){ mesg('d', mem.name); mem.skip(bar); changed = 1; }else parts[0].copy(bar, mem); mem = nil; # conserves memory } if(changed) install(arname, parts, 0);} xcmd(arname: string, files: array of ref File){ bar := openar(arname, Sys->OREAD, 0); i := 0; while((mem := Armember.rdhdr(bar)) != nil){ if((f := match(files, mem.name)) != nil){ f.found = 1; fd := sys->create(f.name, Sys->OWRITE, mem.mode & 8r777); if(fd == nil){ sys->fprint(stderr, "ar: cannot create %s: %r\n", f.name); mem.skip(bar); }else{ mesg('x', f.name); mem.copyout(bar, fd); if(oflag){ dx := sys->nulldir; dx.atime = mem.date; dx.mtime = mem.date; if(sys->fwstat(fd, dx) < 0) sys->fprint(stderr, "ar: can't set times on %s: %r", f.name); } fd = nil; mem = nil; } if(len files > 0 && ++i >= len files) break; }else mem.skip(bar); }} pcmd(arname: string, files: array of ref File){ bar := openar(arname, Sys->OREAD, 0); i := 0; while((mem := Armember.rdhdr(bar)) != nil){ if((f := match(files, mem.name)) != nil){ if(vflag) sys->print("\n<%s>\n\n", f.name); mem.copyout(bar, sys->fildes(1)); if(len files > 0 && ++i >= len files) break; }else mem.skip(bar); mem = nil; # we no longer need the contents }} mcmd(arname: string, files: array of ref File){ if(len files == 0) return; parts = array[3] of {Arfile.new(), Arfile.new(), nil}; bar := openar(arname, Sys->ORDWR, 0); ap := parts[0]; while((mem := Armember.rdhdr(bar)) != nil){ if(bamatch(mem.name, pivotname)) ap = parts[2] = Arfile.new(); if((f := match(files, mem.name)) != nil){ mesg('m', f.name); parts[1].copy(bar, mem); }else ap.copy(bar, mem); } if(pivotname != nil && parts[2] == nil) sys->fprint(stderr, "ar: %s not found - files moved to end\n", pivotname); install(arname, parts, 0);} tcmd(arname: string, files: array of ref File){ bar := openar(arname, Sys->OREAD, 0); while((mem := Armember.rdhdr(bar)) != nil){ if((f := match(files, mem.name)) != nil){ longls := ""; if(vflag) longls = longtext(mem)+" "; bout.puts(longls+f.trimmed+"\n"); } mem.skip(bar); mem = nil; }} qcmd(arname: string, files: array of ref File){ if(aflag || bflag){ sys->fprint(stderr, "ar: abi not allowed with q\n"); raise "fail:usage"; } fd := openrawar(arname, Sys->ORDWR, 1); if(fd == nil){ if(!cflag) sys->fprint(stderr, "ar: creating %s\n", arname); fd = arcreate(arname); } # leave note group behind when writing archive; i.e. sidestep interrupts sys->seek(fd, big 0, 2); # append for(i := 0; i < len files; i++){ f := files[i]; f.found = 1; dfd := sys->open(f.name, Sys->OREAD); if(dfd != nil){ mesg('q', f.name); mem := Armember.new(f.trimmed, dfd); if(mem != nil){ mem.write(fd); mem = nil; } }else sys->fprint(stderr, "ar: cannot open %s: %r\n", f.name); }} ## open an archive and validate its header#openrawar(arname: string, mode: int, errok: int): ref Sys->FD{ fd := sys->open(arname, mode); if(fd == nil){ if(!errok){ sys->fprint(stderr, "ar: cannot open %s: %r\n", arname); raise "fail:error"; } return nil; } mbuf := array[SARMAG] of byte; if(sys->read(fd, mbuf, SARMAG) != SARMAG || string mbuf != ARMAG){ sys->fprint(stderr, "ar: %s not in archive format\n", arname); raise "fail:error"; } return fd;} openar(arname: string, mode: int, errok: int): ref Iobuf{ fd := openrawar(arname, mode, errok); if(fd == nil) return nil; bfd := bufio->fopen(fd, mode); bfd.seek(big SARMAG, 0); return bfd;} ## create an archive and set its header#arcreate(arname: string): ref Sys->FD{ fd := sys->create(arname, Sys->OWRITE, 8r666); if(fd == nil){ sys->fprint(stderr, "ar: cannot create %s: %r\n", arname); raise "fail:create"; } a := array of byte ARMAG; mustwrite(fd, a, len a); return fd;} ## error handling#wrerr(){ sys->fprint(stderr, "ar: write error: %r\n"); raise "fail:write error";} rderr(){ sys->fprint(stderr, "ar: read error: %r\n"); raise "fail:read error";} phaseerr(offset: big){ sys->fprint(stderr, "ar: phase error at offset %bd\n", offset); raise "fail:phase error";} usage(){ sys->fprint(stderr, "usage: ar [%s][%s] archive files ...\n", opt, man); raise "fail:usage";} ## concatenate the several sequences of members into one archive#install(arname: string, seqs: array of ref Arfile, createflag: int){ # leave process group behind when copying back; i.e. sidestep interrupts sys->pctl(Sys->NEWPGRP, nil); if(createflag) sys->fprint(stderr, "ar: creating %s\n", arname); fd := arcreate(arname); for(i := 0; i < len seqs; i++) if((ap := seqs[i]) != nil) ap.stream(fd);} ## return the command line File matching a given name#match(files: array of ref File, file: string): ref File{ if(len files == 0) return ref File(file, file, 0); # empty list always matches for(i := 0; i < len files; i++) if(!files[i].found && files[i].trimmed == file){ files[i].found = 1; return files[i]; } return nil;} ## is `file' the pivot member's name and is the archive positioned# at the correct point wrt after or before options? return true if so.#Lines 1–500 of 856. Load more
implement Ar; # # ar - portable (ascii) format version # include "sys.m"; sys: Sys; include "draw.m"; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "daytime.m"; daytime: Daytime; include "string.m"; str: String; Ar: module { init: fn(nil: ref Draw->Context, nil: list of string); }; ARMAG: con "!<arch>\n"; SARMAG: con len ARMAG; ARFMAG0: con byte '`'; ARFMAG1: con byte '\n'; SARNAME: con 16; # ancient limit # # printable archive header # name[SARNAME] date[12] uid[6] gid[6] mode[8] size[10] fmag[2] # Oname: con 0; Lname: con SARNAME; Odate: con Oname+Lname; Ldate: con 12; Ouid: con Odate+Ldate; Luid: con 6; Ogid: con Ouid+Luid; Lgid: con 6; Omode: con Ogid+Lgid; Lmode: con 8; Osize: con Omode+Lmode; Lsize: con 10; Ofmag: con Osize+Lsize; Lfmag: con 2; SAR_HDR: con Ofmag+Lfmag; # 60 # # The algorithm uses up to 3 temp files. The "pivot contents" is the # archive contents specified by an a, b, or i option. The temp files are # astart - contains existing contentss up to and including the pivot contents. # amiddle - contains new files moved or inserted behind the pivot. # aend - contains the existing contentss that follow the pivot contents. # When all contentss have been processed, function 'install' streams the # temp files, in order, back into the archive. # Armember: adt { # one per archive contents name: string; # trimmed length: int; date: int; uid: int; gid: int; mode: int; size: int; contents: array of byte; fd: ref Sys->FD; # if contents is nil and fd is not nil, fd has contents next: cyclic ref Armember; new: fn(name: string, fd: ref Sys->FD): ref Armember; rdhdr: fn(b: ref Iobuf): ref Armember; read: fn(m: self ref Armember, b: ref Iobuf): int; wrhdr: fn(m: self ref Armember, fd: ref Sys->FD); write: fn(m: self ref Armember, fd: ref Sys->FD); skip: fn(m: self ref Armember, b: ref Iobuf); replace: fn(m: self ref Armember, name: string, fd: ref Sys->FD); copyout: fn(m: self ref Armember, b: ref Iobuf, destfd: ref Sys->FD); }; Arfile: adt { # one per tempfile fd: ref Sys->FD; # paging file descriptor, nil if none allocated head: ref Armember; tail: ref Armember; new: fn(): ref Arfile; copy: fn(ar: self ref Arfile, b: ref Iobuf, mem: ref Armember); insert: fn(ar: self ref Arfile, mem: ref Armember); stream: fn(ar: self ref Arfile, fd: ref Sys->FD); page: fn(ar: self ref Arfile): int; }; File: adt { name: string; trimmed: string; found: int; }; man := "mrxtdpq"; opt := "uvnbailo"; aflag := 0; bflag := 0; cflag := 0; oflag := 0; uflag := 0; vflag := 0; pivotname: string; bout: ref Iobuf; stderr: ref Sys->FD; parts: array of ref Arfile; comfun: ref fn(a: string, f: array of ref File); init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; daytime = load Daytime Daytime->PATH; str = load String String->PATH; stderr = sys->fildes(2); bout = bufio->fopen(sys->fildes(1), Sys->OWRITE); if(len args < 3) usage(); args = tl args; s := hd args; args = tl args; for(i := 0; i < len s; i++){ case s[i] { 'a' => aflag = 1; 'b' => bflag = 1; 'c' => cflag = 1; 'd' => setcom(dcmd); 'i' => bflag = 1; 'l' => ; # ignored 'm' => setcom(mcmd); 'o' => oflag = 1; 'p' => setcom(pcmd); 'q' => setcom(qcmd); 'r' => setcom(rcmd); 't' => setcom(tcmd); 'u' => uflag = 1; 'v' => vflag = 1; 'x' => setcom(xcmd); * => sys->fprint(stderr, "ar: bad option `%c'\n", s[i]); usage(); } } if(aflag && bflag){ sys->fprint(stderr, "ar: only one of 'a' and 'b' can be specified\n"); usage(); } if(aflag || bflag){ pivotname = trim(hd args); args = tl args; if(len args < 2) usage(); } if(comfun == nil){ if(uflag == 0){ sys->fprint(stderr, "ar: one of [%s] must be specified\n", man); usage(); } setcom(rcmd); } cp := hd args; args = tl args; files := array[len args] of ref File; for(i = 0; args != nil; args = tl args) files[i++] = ref File(hd args, trim(hd args), 0); comfun(cp, files); # do the command for(i = 0; i < len files; i++) if(!files[i].found){ sys->fprint(stderr, "ar: %s not found\n", files[i].name); cp = "error"; } bout.flush(); if(cp != nil) raise "fail:"+cp; } # # select a command # setcom(fun: ref fn(s: string, f: array of ref File)) { if(comfun != nil){ sys->fprint(stderr, "ar: only one of [%s] allowed\n", man); usage(); } comfun = fun; } # # perform the 'r' and 'u' commands # rcmd(arname: string, files: array of ref File) { bar := openar(arname, Sys->ORDWR, 1); parts = array[2] of {Arfile.new(), nil}; ap := parts[0]; if(bar != nil){ while((mem := Armember.rdhdr(bar)) != nil){ if(bamatch(mem.name, pivotname)) # check for pivot ap = parts[1] = Arfile.new(); f := match(files, mem.name); if(f == nil){ ap.copy(bar, mem); continue; } f.found = 1; dfd := sys->open(f.name, Sys->OREAD); if(dfd == nil){ if(len files > 0) sys->fprint(stderr, "ar: cannot open %s: %r\n", f.name); ap.copy(bar, mem); continue; } if(uflag){ (ok, d) := sys->fstat(dfd); if(ok < 0 || d.mtime <= mem.date){ if(ok < 0) sys->fprint(stderr, "ar: cannot stat %s: %r\n", f.name); ap.copy(bar, mem); continue; } } mem.skip(bar); mesg('r', f.name); mem.replace(f.name, dfd); ap.insert(mem); dfd = nil; } } # copy in remaining files named on command line for(i := 0; i < len files; i++){ f := files[i]; if(f.found) continue; f.found = 1; dfd := sys->open(f.name, Sys->OREAD); if(dfd != nil){ mesg('a', f.name); parts[0].insert(Armember.new(f.trimmed, dfd)); }else sys->fprint(stderr, "ar: cannot open %s: %r\n", f.name); } if(bar == nil && !cflag) install(arname, parts, 1); # issue 'creating' msg else install(arname, parts, 0); } dcmd(arname: string, files: array of ref File) { if(len files == 0) return; changed := 0; parts = array[] of {Arfile.new()}; bar := openar(arname, Sys->ORDWR, 0); while((mem := Armember.rdhdr(bar)) != nil){ if(match(files, mem.name) != nil){ mesg('d', mem.name); mem.skip(bar); changed = 1; }else parts[0].copy(bar, mem); mem = nil; # conserves memory } if(changed) install(arname, parts, 0); } xcmd(arname: string, files: array of ref File) { bar := openar(arname, Sys->OREAD, 0); i := 0; while((mem := Armember.rdhdr(bar)) != nil){ if((f := match(files, mem.name)) != nil){ f.found = 1; fd := sys->create(f.name, Sys->OWRITE, mem.mode & 8r777); if(fd == nil){ sys->fprint(stderr, "ar: cannot create %s: %r\n", f.name); mem.skip(bar); }else{ mesg('x', f.name); mem.copyout(bar, fd); if(oflag){ dx := sys->nulldir; dx.atime = mem.date; dx.mtime = mem.date; if(sys->fwstat(fd, dx) < 0) sys->fprint(stderr, "ar: can't set times on %s: %r", f.name); } fd = nil; mem = nil; } if(len files > 0 && ++i >= len files) break; }else mem.skip(bar); } } pcmd(arname: string, files: array of ref File) { bar := openar(arname, Sys->OREAD, 0); i := 0; while((mem := Armember.rdhdr(bar)) != nil){ if((f := match(files, mem.name)) != nil){ if(vflag) sys->print("\n<%s>\n\n", f.name); mem.copyout(bar, sys->fildes(1)); if(len files > 0 && ++i >= len files) break; }else mem.skip(bar); mem = nil; # we no longer need the contents } } mcmd(arname: string, files: array of ref File) { if(len files == 0) return; parts = array[3] of {Arfile.new(), Arfile.new(), nil}; bar := openar(arname, Sys->ORDWR, 0); ap := parts[0]; while((mem := Armember.rdhdr(bar)) != nil){ if(bamatch(mem.name, pivotname)) ap = parts[2] = Arfile.new(); if((f := match(files, mem.name)) != nil){ mesg('m', f.name); parts[1].copy(bar, mem); }else ap.copy(bar, mem); } if(pivotname != nil && parts[2] == nil) sys->fprint(stderr, "ar: %s not found - files moved to end\n", pivotname); install(arname, parts, 0); } tcmd(arname: string, files: array of ref File) { bar := openar(arname, Sys->OREAD, 0); while((mem := Armember.rdhdr(bar)) != nil){ if((f := match(files, mem.name)) != nil){ longls := ""; if(vflag) longls = longtext(mem)+" "; bout.puts(longls+f.trimmed+"\n"); } mem.skip(bar); mem = nil; } } qcmd(arname: string, files: array of ref File) { if(aflag || bflag){ sys->fprint(stderr, "ar: abi not allowed with q\n"); raise "fail:usage"; } fd := openrawar(arname, Sys->ORDWR, 1); if(fd == nil){ if(!cflag) sys->fprint(stderr, "ar: creating %s\n", arname); fd = arcreate(arname); } # leave note group behind when writing archive; i.e. sidestep interrupts sys->seek(fd, big 0, 2); # append for(i := 0; i < len files; i++){ f := files[i]; f.found = 1; dfd := sys->open(f.name, Sys->OREAD); if(dfd != nil){ mesg('q', f.name); mem := Armember.new(f.trimmed, dfd); if(mem != nil){ mem.write(fd); mem = nil; } }else sys->fprint(stderr, "ar: cannot open %s: %r\n", f.name); } } # # open an archive and validate its header # openrawar(arname: string, mode: int, errok: int): ref Sys->FD { fd := sys->open(arname, mode); if(fd == nil){ if(!errok){ sys->fprint(stderr, "ar: cannot open %s: %r\n", arname); raise "fail:error"; } return nil; } mbuf := array[SARMAG] of byte; if(sys->read(fd, mbuf, SARMAG) != SARMAG || string mbuf != ARMAG){ sys->fprint(stderr, "ar: %s not in archive format\n", arname); raise "fail:error"; } return fd; } openar(arname: string, mode: int, errok: int): ref Iobuf { fd := openrawar(arname, mode, errok); if(fd == nil) return nil; bfd := bufio->fopen(fd, mode); bfd.seek(big SARMAG, 0); return bfd; } # # create an archive and set its header # arcreate(arname: string): ref Sys->FD { fd := sys->create(arname, Sys->OWRITE, 8r666); if(fd == nil){ sys->fprint(stderr, "ar: cannot create %s: %r\n", arname); raise "fail:create"; } a := array of byte ARMAG; mustwrite(fd, a, len a); return fd; } # # error handling # wrerr() { sys->fprint(stderr, "ar: write error: %r\n"); raise "fail:write error"; } rderr() { sys->fprint(stderr, "ar: read error: %r\n"); raise "fail:read error"; } phaseerr(offset: big) { sys->fprint(stderr, "ar: phase error at offset %bd\n", offset); raise "fail:phase error"; } usage() { sys->fprint(stderr, "usage: ar [%s][%s] archive files ...\n", opt, man); raise "fail:usage"; } # # concatenate the several sequences of members into one archive # install(arname: string, seqs: array of ref Arfile, createflag: int) { # leave process group behind when copying back; i.e. sidestep interrupts sys->pctl(Sys->NEWPGRP, nil); if(createflag) sys->fprint(stderr, "ar: creating %s\n", arname); fd := arcreate(arname); for(i := 0; i < len seqs; i++) if((ap := seqs[i]) != nil) ap.stream(fd); } # # return the command line File matching a given name # match(files: array of ref File, file: string): ref File { if(len files == 0) return ref File(file, file, 0); # empty list always matches for(i := 0; i < len files; i++) if(!files[i].found && files[i].trimmed == file){ files[i].found = 1; return files[i]; } return nil; } # # is `file' the pivot member's name and is the archive positioned # at the correct point wrt after or before options? return true if so. #