xref: /inferno-os/appl/cmd/mc.b (revision 9f620229775d41f76096d9039c46ff368fd44d47)
1implement Mc;
2
3include "sys.m";
4	sys: Sys;
5	open, read, fprint, fildes, tokenize,
6	ORDWR, OREAD, OWRITE: import sys;
7include "draw.m";
8	draw: Draw;
9	Font: import draw;
10include "bufio.m";
11	bufio: Bufio;
12	Iobuf: import bufio;
13include "arg.m";
14
15font: ref Font;
16columns := 65;
17tabwid := 0;
18mintab := 1;
19
20Mc: module{
21	init: fn(nil: ref Draw->Context, argv: list of string);
22};
23
24init(ctxt: ref Draw->Context, argv: list of string)
25{
26	sys = load Sys Sys->PATH;
27	if((bufio = load Bufio Bufio->PATH) == nil)
28		fatal("can't load " + Bufio->PATH);
29	draw = load Draw Draw->PATH;
30	if((arg := load Arg Arg->PATH) == nil)
31		fatal("can't load " + Arg->PATH);
32
33	getwidth(ctxt);
34	arg->init(argv);
35	arg->setusage("mc [-c columns] [file ...]");
36	while((c:=arg->opt()) != 0)
37		case c {
38		'c' =>	columns = int arg->earg() * mintab;
39		* =>		arg->usage();
40		}
41	argv = arg->argv();
42	if(len argv == 0)
43		argv = "/fd/0" :: nil;
44
45	a := array[1024] of (string, int);
46	n := 0;
47	maxwidth := 0;
48	for(; argv!=nil; argv=tl argv){
49		if((bin:=bufio->open(hd argv, OREAD)) == nil){
50			fprint(fildes(2), "mc: can't open %s: %r\n", hd argv);
51			continue;
52		}
53		while((s:=bin.gets('\n')) != nil){
54			if(s[len s-1] == '\n')
55				s = s[0:len s-1];
56			if(n == len a)
57				a = (array[n+1024] of (string, int))[0:] = a;
58			a[n].t0 = s;
59			a[n].t1 = wordsize(s);
60			if(a[n].t1 > maxwidth)
61				maxwidth = a[n].t1;
62			n++;
63		}
64		bin.close();
65	}
66	outcols(a[:n], maxwidth);
67}
68
69outcols(words: array of (string, int), maxwidth: int)
70{
71	maxwidth = nexttab(maxwidth+mintab-1);
72	numcols := columns / maxwidth;
73	if(numcols <= 0)
74		numcols = 1;
75	nwords := len words;
76	nlines := (nwords+numcols-1) / numcols;
77	bout := bufio->fopen(fildes(1), OWRITE);
78	for(i := 0; i < nlines; i++){
79		col := endcol := 0;
80		for(j:=i; j<nwords; j+=nlines){
81			endcol += maxwidth;
82			bout.puts(words[j].t0);
83			col += words[j].t1;
84			if(j+nlines < nwords){
85				while(col < endcol){
86					if(tabwid)
87						bout.putc('\t');
88					else
89						bout.putc(' ');
90					col = nexttab(col);
91				}
92			}
93		}
94		bout.putc('\n');
95	}
96	bout.close();
97}
98
99wordsize(s: string): int
100{
101	if(font != nil)
102		return font.width(s);
103	return len s;
104}
105
106nexttab(col: int): int
107{
108	if(tabwid){
109		col += tabwid;
110		col -= col%tabwid;
111		return col;
112	}
113	return col+1;
114}
115
116getwidth(ctxt: ref Draw->Context)
117{
118	if(ctxt == nil || draw == nil)
119		return;
120	if((wid := rf("/env/acmewin")) == nil)
121		return;
122	if((fd := open("/chan/" + wid + "/ctl", ORDWR)) == nil)
123		return;
124	buf := array[256] of byte;
125	if((n := read(fd, buf, len buf)) <= 0)
126		return;
127	(nf, f) := tokenize(string buf[:n], " ");
128	if(nf != 8)
129		return;
130	f0 := tl tl tl tl tl f;
131	if((font = Font.open(ctxt.display, hd tl f0)) == nil)
132		return;
133	tabwid = int hd tl tl f0;
134	mintab = font.width("0");
135	columns = int hd f0;
136}
137
138fatal(s: string)
139{
140	fprint(fildes(2), "mc: %s: %r\n", s);
141	raise "fail:"+s;
142}
143
144rf(f: string): string
145{
146	fd := sys->open(f, Sys->OREAD);
147	if(fd == nil)
148		return nil;
149	b := array[Sys->NAMEMAX] of byte;
150	n := sys->read(fd, b, len b);
151	if(n < 0)
152		return nil;
153	return string b[0:n];
154}
155