Charles.Forsyth20070714-11427a6ecc419 years agoHistory
503 lines · 9.8 KB · Brainfuck
RawDownload
implement Pedit; ## disk partition editor# include "sys.m";	sys: Sys; include "bufio.m";	bufio: Bufio;	Iobuf: import bufio; include "disks.m";	disks: Disks;	Disk: import disks; include "draw.m";include "calc.tab.m";	calc: Calc; include "pedit.m"; Cmd: adt {	c: int;	f:	ref fn(e: ref Edit, a: array of string): string;}; cmds: array of Cmd; bin: ref Iobuf; init(){	sys = load Sys Sys->PATH;	calc = load Calc "/dis/disk/calc.tab.dis";	bufio = load Bufio Bufio->PATH;	disks = load Disks Disks->PATH;	disks->init(); 	bin = bufio->fopen(sys->fildes(0), Bufio->OREAD);	cmds = array[] of {		('.',	editdot),		('a',	editadd),		('d',	editdel),		('?',	edithelp),		('h',	edithelp),		('P',	editctlprint),		('p',	editprint),		('w',	editwrite),		('q',	editquit),	};} Edit.mk(unit: string): ref Edit{	e := ref Edit;	e.unit = unit;	e.dot = big 0;	e.end = big 0;	e.changed = 0;	e.warned = 0;	e.lastcmd = 0;	return e;} Edit.getline(edit: self ref Edit): string{	p := bin.gets('\n');	if(p == nil){		if(edit.changed)			sys->fprint(sys->fildes(2), "?warning: changes not written\n");		exit;	}	for(i := 0; i < len p; i++)		if(!isspace(p[i]))			break;	if(i)		return p[i:];	return p;} Edit.findpart(edit: self ref Edit, name: string): ref Part{	for(i:=0; i<len edit.part; i++)		if(edit.part[i].name == name)			return edit.part[i];	return nil;} okname(edit: ref Edit, name: string): string{	if(name[0] == '\0')		return "partition has no name"; 	for(i:=0; i<len edit.part; i++) {		if(name == edit.part[i].name)			return sys->sprint("already have partition with name '%s'", name);	}	return nil;} Edit.addpart(edit: self ref Edit, p: ref Part): string{	if((err := okname(edit, p.name)) != nil)		return err; 	for(i:=0; i<len edit.part; i++) {		if(p.start < edit.part[i].end && edit.part[i].start < p.end) {			msg := sys->sprint("\"%s\" %bd-%bd overlaps with \"%s\" %bd-%bd",				p.name, p.start, p.end,				edit.part[i].name, edit.part[i].start, edit.part[i].end);		#	return msg;		}	} 	if(len edit.part >= Maxpart)		return "too many partitions"; 	pa := array[i+1] of ref Part;	pa[0:] = edit.part;	edit.part = pa; 	edit.part[i] = p;	for(; i > 0 && p.start < edit.part[i-1].start; i--) {		edit.part[i] = edit.part[i-1];		edit.part[i-1] = p;	} 	if(p.changed)		edit.changed = 1;	return nil;} Edit.delpart(edit: self ref Edit, p: ref Part): string{	n := len edit.part;	for(i:=0; i<n; i++)		if(edit.part[i] == p)			break;	if(i >= n)		raise "internal error: Part not found";	n--;	pa := array[n] of ref Part;	if(n){		pa[0:] = edit.part[0:i];		if(i != n)			pa[i:] = edit.part[i+1:];	}	edit.part = pa;	edit.changed = 1;	return nil;} editdot(edit: ref Edit, argv: array of string): string{	if(len argv == 1) {		sys->print("\t. %bd\n", edit.dot);		return nil;	} 	if(len argv > 2)		return "args"; 	(ndot, err) := calc->parseexpr(argv[1], edit.dot, edit.end, edit.end);	if(err != nil)		return err; 	edit.dot = ndot;	return nil;} editadd(edit: ref Edit, argv: array of string): string{	if(len argv < 2)		return "args"; 	name := argv[1];	if((err := okname(edit, name)) != nil || edit.okname != nil && (err = edit.okname(edit, name)) != nil)		return err; 	if(len argv >= 3)		q := argv[2];	else {		sys->fprint(sys->fildes(2), "start %s: ", edit.unit);		q = edit.getline();	}	start: big;	(start, err) = calc->parseexpr(q, edit.dot, edit.end, edit.end);	if(err != nil)		return err; 	if(start < big 0 || start >= edit.end)		return "start out of range"; 	for(i:=0; i < len edit.part; i++) {		if(edit.part[i].start <= start && start < edit.part[i].end)			return sys->sprint("start %s in partition '%s'", edit.unit, edit.part[i].name);	} 	maxend := edit.end;	for(i=0; i < len edit.part; i++)		if(start < edit.part[i].start && edit.part[i].start < maxend)			maxend = edit.part[i].start; 	if(len argv >= 4)		q = argv[3];	else {		sys->fprint(sys->fildes(2), "end [%bd..%bd] ", start, maxend);		q = edit.getline();	}	end: big;	(end, err) = calc->parseexpr(q, edit.dot, maxend, edit.end);	if(err != nil)		return err; 	if(start == end)		return "size zero partition"; 	if(end <= start || end > maxend)		return "end out of range"; 	if(len argv > 4)		return "args"; 	if((err = edit.add(edit, name, start, end)) != nil)		return err; 	edit.dot = end;	return nil;} editdel(edit: ref Edit, argv: array of string): string{	if(len argv != 2)		return "args"; 	if((p := edit.findpart(argv[1])) == nil)		return "no such partition"; 	return edit.del(edit, p);} helptext :=	". [newdot] - display or set value of dot\n"+	"a name [start [end]] - add partition\n"+	"d name - delete partition\n"+	"h - sys->print help message\n"+	"p - sys->print partition table\n"+	"P - sys->print commands to update sd(3) device\n"+	"w - write partition table\n"+	"q - quit\n"; edithelp(edit: ref Edit, nil: array of string): string{	sys->print("%s", helptext);	if(edit.help != nil)		return edit.help(edit);	return nil;} editprint(edit: ref Edit, argv: array of string): string{	if(len argv != 1)		return "args"; 	lastend := big 0;	part := edit.part;	for(i:=0; i<len edit.part; i++) {		if(lastend < part[i].start)			edit.sum(edit, nil, lastend, part[i].start);		edit.sum(edit, part[i], part[i].start, part[i].end);		lastend = part[i].end;	}	if(lastend < edit.end)		edit.sum(edit, nil, lastend, edit.end);	return nil;} editwrite(edit: ref Edit, argv: array of string): string{	if(len argv != 1)		return "args"; 	if(edit.disk.rdonly)		return "read only"; 	err := edit.write(edit);	if(err != nil)		return err;	for(i:=0; i<len edit.part; i++)		edit.part[i].changed = 0;	edit.changed = 0;	return nil;} editquit(edit: ref Edit, argv: array of string): string{	if(len argv != 1) {		edit.warned = 0;		return "args";	} 	if(edit.changed && (!edit.warned || edit.lastcmd != 'q')) {		edit.warned = 1;		return "changes unwritten";	} 	exit;} editctlprint(edit: ref Edit, argv: array of string): string{	if(len argv != 1)		return "args"; 	if(edit.printctl != nil)		edit.printctl(edit, sys->fildes(1));	else		edit.ctldiff(sys->fildes(1));	return nil;} Edit.runcmd(edit: self ref Edit, cmd: string){	(nf, fl) := sys->tokenize(cmd, " \t\n\r");	if(nf < 1)		return;	f := array[nf] of string;	for(nf = 0; fl != nil; fl = tl fl)		f[nf++] = hd fl;	if(len f[0] != 1) {		sys->fprint(sys->fildes(2), "?\n");		return;	} 	err := "";	for(i:=0; i<len cmds; i++) {		if(cmds[i].c == f[0][0]) {			op := cmds[i].f;			err = op(edit, f);			break;		}	}	if(i == len cmds){		if(edit.ext != nil)			err = edit.ext(edit, f);		else			err = "unknown command";	}	if(err != nil) 		sys->fprint(sys->fildes(2), "?%s\n", err);	edit.lastcmd = f[0][0];} isspace(c: int): int{	return c == ' ' || c == '\t' || c == '\n' || c == '\r';} ctlmkpart(name: string, start: big, end: big, changed: int): ref Part{	p := ref Part;	p.name = name;	p.ctlname = name;	p.start = start;	p.end = end;	p.ctlstart = big 0;	p.ctlend = big 0;	p.changed = changed;	return p;} rdctlpart(edit: ref Edit){	disk := edit.disk;	edit.ctlpart = array[0] of ref Part;	sys->seek(disk.ctlfd, big 0, 0);	buf := array[4096] of byte;	if(sys->readn(disk.ctlfd, buf, len buf) <= 0)		return;	for(i := 0; i < len buf; i++)		if(buf[i] == byte 0)			break; 	(nline, lines) := sys->tokenize(string buf[0:i], "\n\r");	edit.ctlpart = array[nline] of ref Part;	# upper bound	npart := 0;	for(i=0; i<nline; i++){		line := hd lines;		lines = tl lines;		if(len line < 5 || line[0:5] != "part ")			continue; 		(nf, f) := sys->tokenize(line, " \t");		if(nf != 4 || hd f != "part")			break; 		a := big hd tl tl f;		b := big hd tl tl tl f; 		if(a >= b)			break; 		# only gather partitions contained in the disk partition we are editing		if(a < disk.offset ||  disk.offset+disk.secs < b)			continue; 		a -= disk.offset;		b -= disk.offset; 		# the partition we are editing does not count		if(hd tl f == disk.part)			continue; 		edit.ctlpart[npart++] = ctlmkpart(hd tl f, a, b, 0);	}	if(npart != len edit.ctlpart)		edit.ctlpart = edit.ctlpart[0:npart];} ctlstart(p: ref Part): big{	if(p.ctlstart != big 0)		return p.ctlstart;	return p.start;} ctlend(p: ref Part): big{	if(p.ctlend != big 0)		return p.ctlend;	return p.end;} areequiv(p: ref Part, q: ref Part): int{	if(p.ctlname == nil || q.ctlname == nil)		return 0;	return p.ctlname == q.ctlname &&			ctlstart(p) == ctlstart(q) && ctlend(p) == ctlend(q);} unchange(edit: ref Edit, p: ref Part){	for(i:=0; i<len edit.ctlpart; i++) {		q := edit.ctlpart[i];		if(p.start <= q.start && q.end <= p.end)			q.changed = 0;	}	if(p.changed)		raise "internal error: Part unchanged";} Edit.ctldiff(edit: self ref Edit, ctlfd: ref Sys->FD): int{	rdctlpart(edit); 	# everything is bogus until we prove otherwise	for(i:=0; i<len edit.ctlpart; i++)		edit.ctlpart[i].changed = 1; 	#	# partitions with same info have not changed,	# and neither have partitions inside them.	#	for(i=0; i<len edit.ctlpart; i++)		for(j:=0; j<len edit.part; j++)			if(areequiv(edit.ctlpart[i], edit.part[j])) {				unchange(edit, edit.ctlpart[i]);				break;			} 	waserr := 0;	#	# delete all the changed partitions except data (we'll add them back if necessary) 	#	for(i=0; i<len edit.ctlpart; i++) {		p := edit.ctlpart[i];		if(p.changed)		if(sys->fprint(ctlfd, "delpart %s\n", p.ctlname)<0) {			sys->fprint(sys->fildes(2), "delpart failed: %s: %r\n", p.ctlname);			waserr = -1;		}	} 	#	# add all the partitions from the real list;	# this is okay since adding a partition with	# information identical to what is there is a no-op.	#	offset := edit.disk.offset;	for(i=0; i<len edit.part; i++) {		p := edit.part[i];		if(p.ctlname != nil) {			if(sys->fprint(ctlfd, "part %s %bd %bd\n", p.ctlname, offset+ctlstart(p), offset+ctlend(p)) < 0) {				sys->fprint(sys->fildes(2), "adding part failed: %s: %r\n", p.ctlname);				waserr = -1;			}		}

Lines 1–500 of 503.