Charles.Forsyth200612195f2b45819 years agoHistory
856 lines · 18.6 KB · Brainfuck
RawDownload
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.