ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/disk/prep/pedit.b/
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;
}
}
}
return waserr;
}