xref: /inferno-os/appl/cmd/sh/csv.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1*37da2899SCharles.Forsythimplement Shellbuiltin;
2*37da2899SCharles.Forsyth
3*37da2899SCharles.Forsyth# parse/generate comma-separated values.
4*37da2899SCharles.Forsyth
5*37da2899SCharles.Forsythinclude "sys.m";
6*37da2899SCharles.Forsyth	sys: Sys;
7*37da2899SCharles.Forsythinclude "draw.m";
8*37da2899SCharles.Forsythinclude "sh.m";
9*37da2899SCharles.Forsyth	sh: Sh;
10*37da2899SCharles.Forsyth	Listnode, Context: import sh;
11*37da2899SCharles.Forsyth	myself: Shellbuiltin;
12*37da2899SCharles.Forsythinclude "bufio.m";
13*37da2899SCharles.Forsyth	bufio: Bufio;
14*37da2899SCharles.Forsyth	Iobuf: import bufio;
15*37da2899SCharles.Forsyth
16*37da2899SCharles.Forsythinitbuiltin(ctxt: ref Context, shmod: Sh): string
17*37da2899SCharles.Forsyth{
18*37da2899SCharles.Forsyth	sys = load Sys Sys->PATH;
19*37da2899SCharles.Forsyth	sh = shmod;
20*37da2899SCharles.Forsyth	myself = load Shellbuiltin "$self";
21*37da2899SCharles.Forsyth	if (myself == nil)
22*37da2899SCharles.Forsyth		ctxt.fail("bad module", sys->sprint("csv: cannot load self: %r"));
23*37da2899SCharles.Forsyth	bufio = load Bufio Bufio->PATH;
24*37da2899SCharles.Forsyth	if (bufio == nil)
25*37da2899SCharles.Forsyth		ctxt.fail("bad module",
26*37da2899SCharles.Forsyth			sys->sprint("csv: cannot load: %s: %r", Bufio->PATH));
27*37da2899SCharles.Forsyth	ctxt.addbuiltin("getcsv", myself);
28*37da2899SCharles.Forsyth	ctxt.addsbuiltin("csv", myself);
29*37da2899SCharles.Forsyth	return nil;
30*37da2899SCharles.Forsyth}
31*37da2899SCharles.Forsyth
32*37da2899SCharles.Forsythwhatis(nil: ref Sh->Context, nil: Sh, nil: string, nil: int): string
33*37da2899SCharles.Forsyth{
34*37da2899SCharles.Forsyth	return nil;
35*37da2899SCharles.Forsyth}
36*37da2899SCharles.Forsyth
37*37da2899SCharles.Forsythgetself(): Shellbuiltin
38*37da2899SCharles.Forsyth{
39*37da2899SCharles.Forsyth	return myself;
40*37da2899SCharles.Forsyth}
41*37da2899SCharles.Forsyth
42*37da2899SCharles.Forsythrunbuiltin(c: ref Sh->Context, nil: Sh,
43*37da2899SCharles.Forsyth			cmd: list of ref Sh->Listnode, last: int): string
44*37da2899SCharles.Forsyth{
45*37da2899SCharles.Forsyth	return builtin_getcsv(c, cmd, last);
46*37da2899SCharles.Forsyth}
47*37da2899SCharles.Forsyth
48*37da2899SCharles.Forsythrunsbuiltin(c: ref Sh->Context, nil: Sh,
49*37da2899SCharles.Forsyth			cmd: list of ref Sh->Listnode): list of ref Listnode
50*37da2899SCharles.Forsyth{
51*37da2899SCharles.Forsyth	return sbuiltin_csv(c, cmd);
52*37da2899SCharles.Forsyth}
53*37da2899SCharles.Forsyth
54*37da2899SCharles.Forsythbuiltin_getcsv(ctxt: ref Context, argv: list of ref Listnode, nil: int) : string
55*37da2899SCharles.Forsyth{
56*37da2899SCharles.Forsyth	n := len argv;
57*37da2899SCharles.Forsyth	if (n != 2 || !iscmd(hd tl argv))
58*37da2899SCharles.Forsyth		builtinusage(ctxt, "getcsv {cmd}");
59*37da2899SCharles.Forsyth	cmd := hd tl argv :: ctxt.get("*");
60*37da2899SCharles.Forsyth	stdin := bufio->fopen(sys->fildes(0), Sys->OREAD);
61*37da2899SCharles.Forsyth	if (stdin == nil)
62*37da2899SCharles.Forsyth		ctxt.fail("bad input", sys->sprint("getcsv: cannot open stdin: %r"));
63*37da2899SCharles.Forsyth	status := "";
64*37da2899SCharles.Forsyth	ctxt.push();
65*37da2899SCharles.Forsyth	for(;;){
66*37da2899SCharles.Forsyth		{
67*37da2899SCharles.Forsyth			for (;;) {
68*37da2899SCharles.Forsyth				line: list of ref Listnode = nil;
69*37da2899SCharles.Forsyth				sl := readcsvline(stdin);
70*37da2899SCharles.Forsyth				if (sl == nil)
71*37da2899SCharles.Forsyth					break;
72*37da2899SCharles.Forsyth				for (; sl != nil; sl = tl sl)
73*37da2899SCharles.Forsyth					line = ref Listnode(nil, hd sl) :: line;
74*37da2899SCharles.Forsyth				ctxt.setlocal("line", line);
75*37da2899SCharles.Forsyth				status = setstatus(ctxt, ctxt.run(cmd, 0));
76*37da2899SCharles.Forsyth			}
77*37da2899SCharles.Forsyth			ctxt.pop();
78*37da2899SCharles.Forsyth			return status;
79*37da2899SCharles.Forsyth		}
80*37da2899SCharles.Forsyth		exception e{
81*37da2899SCharles.Forsyth			"fail:*" =>
82*37da2899SCharles.Forsyth				ctxt.pop();
83*37da2899SCharles.Forsyth				if (loopexcept(e) == BREAK)
84*37da2899SCharles.Forsyth					return status;
85*37da2899SCharles.Forsyth				ctxt.push();
86*37da2899SCharles.Forsyth		}
87*37da2899SCharles.Forsyth	}
88*37da2899SCharles.Forsyth}
89*37da2899SCharles.Forsyth
90*37da2899SCharles.ForsythCONTINUE, BREAK: con iota;
91*37da2899SCharles.Forsythloopexcept(ename: string): int
92*37da2899SCharles.Forsyth{
93*37da2899SCharles.Forsyth	case ename[5:] {
94*37da2899SCharles.Forsyth	"break" =>
95*37da2899SCharles.Forsyth		return BREAK;
96*37da2899SCharles.Forsyth	"continue" =>
97*37da2899SCharles.Forsyth		return CONTINUE;
98*37da2899SCharles.Forsyth	* =>
99*37da2899SCharles.Forsyth		raise ename;
100*37da2899SCharles.Forsyth	}
101*37da2899SCharles.Forsyth	return 0;
102*37da2899SCharles.Forsyth}
103*37da2899SCharles.Forsyth
104*37da2899SCharles.Forsythiscmd(n: ref Listnode): int
105*37da2899SCharles.Forsyth{
106*37da2899SCharles.Forsyth	return n.cmd != nil || (n.word != nil && n.word[0] == '{');
107*37da2899SCharles.Forsyth}
108*37da2899SCharles.Forsyth
109*37da2899SCharles.Forsythbuiltinusage(ctxt: ref Context, s: string)
110*37da2899SCharles.Forsyth{
111*37da2899SCharles.Forsyth	ctxt.fail("usage", "usage: " + s);
112*37da2899SCharles.Forsyth}
113*37da2899SCharles.Forsyth
114*37da2899SCharles.Forsythsetstatus(ctxt: ref Context, val: string): string
115*37da2899SCharles.Forsyth{
116*37da2899SCharles.Forsyth	ctxt.setlocal("status", ref Listnode(nil, val) :: nil);
117*37da2899SCharles.Forsyth	return val;
118*37da2899SCharles.Forsyth}
119*37da2899SCharles.Forsyth
120*37da2899SCharles.Forsyth# in csv format, is it possible to distinguish between a line containing
121*37da2899SCharles.Forsyth# one empty field and a line containing no fields at all?
122*37da2899SCharles.Forsyth# what does each one look like?
123*37da2899SCharles.Forsythreadcsvline(iob: ref Iobuf): list of string
124*37da2899SCharles.Forsyth{
125*37da2899SCharles.Forsyth	sl: list of string;
126*37da2899SCharles.Forsyth
127*37da2899SCharles.Forsyth	for(;;) {
128*37da2899SCharles.Forsyth		(s, eof) := readcsvword(iob);
129*37da2899SCharles.Forsyth		if (sl == nil && s == nil && eof)
130*37da2899SCharles.Forsyth			return nil;
131*37da2899SCharles.Forsyth
132*37da2899SCharles.Forsyth		c := Bufio->EOF;
133*37da2899SCharles.Forsyth		if (!eof)
134*37da2899SCharles.Forsyth			c = iob.getc();
135*37da2899SCharles.Forsyth		sl = s :: sl;
136*37da2899SCharles.Forsyth		if (c == '\n' || c == Bufio->EOF)
137*37da2899SCharles.Forsyth			return sl;
138*37da2899SCharles.Forsyth	}
139*37da2899SCharles.Forsyth}
140*37da2899SCharles.Forsyth
141*37da2899SCharles.Forsythsbuiltin_csv(nil: ref Context, val: list of ref Listnode): list of ref Listnode
142*37da2899SCharles.Forsyth{
143*37da2899SCharles.Forsyth	val = tl val;
144*37da2899SCharles.Forsyth	if (val == nil)
145*37da2899SCharles.Forsyth		return nil;
146*37da2899SCharles.Forsyth	s := s2qv(word(hd val));
147*37da2899SCharles.Forsyth	for (val = tl val; val != nil; val = tl val)
148*37da2899SCharles.Forsyth		s += "," + s2qv(word(hd val));
149*37da2899SCharles.Forsyth	return ref Listnode(nil, s) :: nil;
150*37da2899SCharles.Forsyth}
151*37da2899SCharles.Forsyth
152*37da2899SCharles.Forsyths2qv(s: string): string
153*37da2899SCharles.Forsyth{
154*37da2899SCharles.Forsyth	needquote := 0;
155*37da2899SCharles.Forsyth	needscan := 0;
156*37da2899SCharles.Forsyth	for (i := 0; i < len s; i++) {
157*37da2899SCharles.Forsyth		c := s[i];
158*37da2899SCharles.Forsyth		if (c == '\n' || c == ',')
159*37da2899SCharles.Forsyth			needquote = 1;
160*37da2899SCharles.Forsyth		else if (c == '"') {
161*37da2899SCharles.Forsyth			needquote = 1;
162*37da2899SCharles.Forsyth			needscan = 1;
163*37da2899SCharles.Forsyth		}
164*37da2899SCharles.Forsyth	}
165*37da2899SCharles.Forsyth	if (!needquote)
166*37da2899SCharles.Forsyth		return s;
167*37da2899SCharles.Forsyth	if (!needscan)
168*37da2899SCharles.Forsyth		return "\"" + s + "\"";
169*37da2899SCharles.Forsyth	r := "\"";
170*37da2899SCharles.Forsyth	for (i = 0; i < len s; i++) {
171*37da2899SCharles.Forsyth		c := s[i];
172*37da2899SCharles.Forsyth		if (c == '"')
173*37da2899SCharles.Forsyth			r[len r] = c;
174*37da2899SCharles.Forsyth		r[len r] = c;
175*37da2899SCharles.Forsyth	}
176*37da2899SCharles.Forsyth	r[len r] = '"';
177*37da2899SCharles.Forsyth	return r;
178*37da2899SCharles.Forsyth}
179*37da2899SCharles.Forsyth
180*37da2899SCharles.Forsythreadcsvword(iob: ref Iobuf): (string, int)
181*37da2899SCharles.Forsyth{
182*37da2899SCharles.Forsyth	s := "";
183*37da2899SCharles.Forsyth	case c := iob.getc() {
184*37da2899SCharles.Forsyth	'"' =>
185*37da2899SCharles.Forsyth		for (;;) {
186*37da2899SCharles.Forsyth			case c = iob.getc() {
187*37da2899SCharles.Forsyth			Bufio->EOF =>
188*37da2899SCharles.Forsyth				return (s, 1);
189*37da2899SCharles.Forsyth			'"' =>
190*37da2899SCharles.Forsyth				case c = iob.getc() {
191*37da2899SCharles.Forsyth				'"' =>
192*37da2899SCharles.Forsyth					s[len s] = '"';
193*37da2899SCharles.Forsyth				'\n' or
194*37da2899SCharles.Forsyth				',' =>
195*37da2899SCharles.Forsyth					iob.ungetc();
196*37da2899SCharles.Forsyth					return (s, 0);
197*37da2899SCharles.Forsyth				Bufio->EOF =>
198*37da2899SCharles.Forsyth					return (s, 1);
199*37da2899SCharles.Forsyth				* =>
200*37da2899SCharles.Forsyth					# illegal
201*37da2899SCharles.Forsyth					iob.ungetc();
202*37da2899SCharles.Forsyth					(t, eof) := readcsvword(iob);
203*37da2899SCharles.Forsyth					return (s + t, eof);
204*37da2899SCharles.Forsyth				}
205*37da2899SCharles.Forsyth			* =>
206*37da2899SCharles.Forsyth				s[len s] = c;
207*37da2899SCharles.Forsyth			}
208*37da2899SCharles.Forsyth		}
209*37da2899SCharles.Forsyth	',' or
210*37da2899SCharles.Forsyth	'\n' =>
211*37da2899SCharles.Forsyth		iob.ungetc();
212*37da2899SCharles.Forsyth		return (s, 0);
213*37da2899SCharles.Forsyth	Bufio->EOF =>
214*37da2899SCharles.Forsyth		return (nil, 1);
215*37da2899SCharles.Forsyth	* =>
216*37da2899SCharles.Forsyth		s[len s] = c;
217*37da2899SCharles.Forsyth		for (;;) {
218*37da2899SCharles.Forsyth			case c = iob.getc() {
219*37da2899SCharles.Forsyth			',' or
220*37da2899SCharles.Forsyth			'\n' =>
221*37da2899SCharles.Forsyth				iob.ungetc();
222*37da2899SCharles.Forsyth				return (s, 0);
223*37da2899SCharles.Forsyth			'"' =>
224*37da2899SCharles.Forsyth				# illegal
225*37da2899SCharles.Forsyth				iob.ungetc();
226*37da2899SCharles.Forsyth				(t, eof) := readcsvword(iob);
227*37da2899SCharles.Forsyth				return (s + t, eof);
228*37da2899SCharles.Forsyth			Bufio->EOF =>
229*37da2899SCharles.Forsyth				return (s, 1);
230*37da2899SCharles.Forsyth			* =>
231*37da2899SCharles.Forsyth				s[len s] = c;
232*37da2899SCharles.Forsyth			}
233*37da2899SCharles.Forsyth		}
234*37da2899SCharles.Forsyth	}
235*37da2899SCharles.Forsyth}
236*37da2899SCharles.Forsyth
237*37da2899SCharles.Forsythword(n: ref Listnode): string
238*37da2899SCharles.Forsyth{
239*37da2899SCharles.Forsyth	if (n.word != nil)
240*37da2899SCharles.Forsyth		return n.word;
241*37da2899SCharles.Forsyth	if (n.cmd != nil)
242*37da2899SCharles.Forsyth		n.word = sh->cmd2string(n.cmd);
243*37da2899SCharles.Forsyth	return n.word;
244*37da2899SCharles.Forsyth}
245