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