History

20090417-2329

Charles.Forsyth committed 17 years ago

Commit cef63c51 parent 391d9036 files · +1141 −2Browse files

Unified · Split

6 files
  1. CHANGES +2 −0
  2. appl/cmd/m4.b +899 −0
  3. appl/cmd/mkfile +6 −1
  4. dis/m4.dis +0 −0
  5. lib/proto/inferno +2 −2
  6. man/1/m4 +233 −0
CHANGES+2−0
@@ -1,3 +1,5 @@
1+20090417
2+ add m4(1) in its initial form (might split the macro processing proper off into a library module)
13 20090409
24 tools/odbc/odbc.c data read should return 0 if no columns or rows (issue 170)
35 20090408
appl/cmd/m4.b+899−0
@@ -0,0 +1,899 @@
1+implement M4;
2+
3+include "sys.m";
4+ sys: Sys;
5+
6+include "draw.m";
7+
8+include "bufio.m";
9+ bufio: Bufio;
10+ Iobuf: import bufio;
11+
12+include "arg.m";
13+
14+M4: module
15+{
16+ init: fn(nil: ref Draw->Context, nil: list of string);
17+};
18+
19+NHASH: con 131;
20+
21+Name: adt {
22+ name: string;
23+ repl: string;
24+ impl: ref fn(nil: array of string);
25+ dol: int; # repl contains $[0-9]
26+
27+ text: fn(n: self ref Name): string;
28+};
29+
30+names := array[NHASH] of list of ref Name;
31+
32+File: adt {
33+ name: string;
34+ line: int;
35+ fp: ref Iobuf;
36+};
37+
38+Param: adt {
39+ s: string;
40+};
41+
42+pushedback: string;
43+pushedp := 0; # next available index in pushedback
44+diverted := array[10] of string;
45+curdiv := 0;
46+curarg: ref Param; # non-nil if collecting argument string
47+instack: list of ref File;
48+lquote := '`';
49+rquote := '\'';
50+initcom := "#";
51+endcom := "\n";
52+bout: ref Iobuf;
53+
54+init(nil: ref Draw->Context, args: list of string)
55+{
56+ sys = load Sys Sys->PATH;
57+ bufio = load Bufio Bufio->PATH;
58+
59+ bout = bufio->fopen(sys->fildes(1), Sys->OWRITE);
60+
61+ define("inferno", "inferno");
62+
63+ builtin("changecom", dochangecom);
64+ builtin("changequote", dochangequote);
65+ builtin("define", dodefine);
66+ builtin("divert", dodivert);
67+ builtin("divnum", dodivnum);
68+ builtin("dnl", dodnl);
69+ builtin("dumpdef", dodumpdef);
70+ builtin("errprint", doerrprint);
71+ builtin("eval", doeval);
72+ builtin("ifdef", doifdef);
73+ builtin("ifelse", doifelse);
74+ builtin("include", doinclude);
75+ builtin("incr", doincr);
76+ builtin("index", doindex);
77+ builtin("len", dolen);
78+ builtin("maketemp", domaketemp);
79+ builtin("sinclude", dosinclude);
80+ builtin("substr", dosubstr);
81+ builtin("translit", dotranslit);
82+ builtin("undefine", doundefine);
83+ builtin("undivert", doundivert);
84+
85+ arg := load Arg Arg->PATH;
86+ arg->setusage("m4 [-Dname[=value]] [-Uname] [file ...]");
87+ arg->init(args);
88+
89+ while((o := arg->opt()) != 0){
90+ case o {
91+ 'D' =>
92+ s := arg->earg();
93+ for(i := 0; i < len s; i++)
94+ if(s[i] == '='){
95+ define(s[0: i], s[i+1:]);
96+ break;
97+ }
98+ if(i == len s)
99+ define(s[0: i], "");
100+ 'U' =>
101+ undefine(arg->earg());
102+ * =>
103+ arg->usage();
104+ }
105+ }
106+ args = arg->argv();
107+ arg = nil;
108+
109+ if(args != nil){
110+ for(; args != nil; args = tl args){
111+ f := bufio->open(hd args, Sys->OREAD);
112+ if(f == nil)
113+ error(sys->sprint("can't open %s: %r", hd args));
114+ pushfile(hd args, f);
115+ scan();
116+ }
117+ }else{
118+ pushfile("standard input", bufio->fopen(sys->fildes(0), Sys->OREAD));
119+ scan();
120+ }
121+ bout.flush();
122+}
123+
124+scan()
125+{
126+ while((c := getc()) >= 0){
127+ if(isalpha(c))
128+ called(c);
129+ else if(c == lquote)
130+ quoted();
131+ else if(initcom != nil && initcom[0] == c)
132+ comment();
133+ else
134+ putc(c);
135+ }
136+}
137+
138+error(s: string)
139+{
140+ where := "";
141+ if(instack != nil){
142+ ios := hd instack;
143+ where = sys->sprint(" %s:%d:", ios.name, ios.line);
144+ }
145+ sys->fprint(sys->fildes(2), "m4:%s %s\n", where, s);
146+ raise "fail:error";
147+}
148+
149+pushfile(name: string, fp: ref Iobuf)
150+{
151+ instack = ref File(name, 1, fp) :: instack;
152+}
153+
154+called(c: int)
155+{
156+ tok: string;
157+ do{
158+ tok[len tok] = c;
159+ c = getc();
160+ }while(isalpha(c) || c >= '0' && c <= '9');
161+ def := lookup(tok);
162+ if(def == nil){
163+ pushc(c);
164+ puts(tok);
165+ return;
166+ }
167+ if(c != '('){ # no parameters
168+ pushc(c);
169+ expand(def, array[] of {tok});
170+ return;
171+ }
172+ # collect arguments, allowing for nested parentheses;
173+ # on ')' expand definition, further expanding $n references therein
174+ argstack := def.name :: nil; # $0
175+ savearg := curarg; # save parameter (if any) for outer call
176+ curarg = ref Param("");
177+ nesting := 0; # () depth
178+ skipws();
179+ for(;;){
180+ if((c = getc()) < 0)
181+ error("EOF in parameters");
182+ if(isalpha(c))
183+ called(c);
184+ else if(c == lquote)
185+ quoted();
186+ else{
187+ if(c == '(')
188+ nesting++;
189+ if(nesting > 0){
190+ if(c == ')')
191+ nesting--;
192+ putc(c);
193+ }else if(c == ','){
194+ argstack = curarg.s :: argstack;
195+ curarg = ref Param("");
196+ skipws();
197+ }else if(c == ')')
198+ break;
199+ else
200+ putc(c);
201+ }
202+ }
203+ argstack = curarg.s :: argstack;
204+ curarg = savearg; # restore outer parameter (if any)
205+ # build arguments
206+ narg := len argstack;
207+ args := array[narg] of string;
208+ for(; argstack != nil; argstack = tl argstack)
209+ args[--narg] = hd argstack;
210+ expand(def, args);
211+}
212+
213+quoted()
214+{
215+ nesting :=0;
216+ while((c := getc()) != rquote || nesting > 0){
217+ if(c < 0)
218+ error("EOF in string");
219+ if(c == rquote)
220+ nesting--;
221+ else if(c == lquote)
222+ nesting++;
223+ putc(c);
224+ }
225+}
226+
227+comment()
228+{
229+ for(i := 1; i < len initcom; i++){
230+ if((c := getc()) != initcom[i]){
231+ if(c < 0)
232+ error("EOF in comment");
233+ pushc(c);
234+ pushs(initcom[1: i]);
235+ putc(initcom[0]);
236+ return;
237+ }
238+ }
239+ puts(initcom);
240+ for(i = 0; i < len endcom;){
241+ c := getc();
242+ if(c < 0)
243+ error("EOF in comment");
244+ putc(c);
245+ if(c == endcom[i])
246+ i++;
247+ else
248+ i = c == endcom[0];
249+ }
250+}
251+
252+skipws()
253+{
254+ while(isspace(c := getc()))
255+ {}
256+ pushc(c);
257+}
258+
259+isspace(c: int): int
260+{
261+ return c == ' ' || c == '\t' || c == '\n' || c == '\r';
262+}
263+
264+isname(s: string): int
265+{
266+ if(s == nil || !isalpha(s[0]))
267+ return 0;
268+ for(i := 1; i < len s; i++)
269+ if(!(isalpha(s[i]) || s[i]>='0' && s[i]<='9'))
270+ return 0;
271+ return 1;
272+}
273+
274+isalpha(c: int): int
275+{
276+ return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_' || c > 16r80;
277+}
278+
279+hash(name: string): int
280+{
281+ h := 0;
282+ for(i := 0; i < len name; i++)
283+ h = h*65599 + name[i];
284+ return (h & ~(1<<31)) % NHASH;
285+}
286+
287+builtin(name: string, impl: ref fn(nil: array of string))
288+{
289+ h := hash(name);
290+ n := ref Name(name, nil, impl, 0);
291+ names[h] = n :: names[h];
292+}
293+
294+define(name: string, repl: string)
295+{
296+ h := hash(name);
297+ dol := hasdol(repl);
298+ for(l := names[h]; l != nil; l = tl l){
299+ n := hd l;
300+ if(n.name == name){
301+ n.impl = nil;
302+ n.repl = repl;
303+ n.dol = dol;
304+ return;
305+ }
306+ }
307+ n := ref Name(name, repl, nil, dol);
308+ names[h] = n :: names[h];
309+}
310+
311+lookup(name: string): ref Name
312+{
313+ h := hash(name);
314+ for(l := names[h]; l != nil; l = tl l)
315+ if((hd l).name == name)
316+ return hd l;
317+ return nil;
318+}
319+
320+undefine(name: string)
321+{
322+ h := hash(name);
323+ rl: list of ref Name;
324+ for(l := names[h]; l != nil; l = tl l){
325+ if((hd l).name == name){
326+ l = tl l;
327+ for(; rl != nil; rl = tl rl)
328+ l = hd rl :: l;
329+ names[h] = l;
330+ return;
331+ }else
332+ rl = hd l :: rl;
333+ }
334+}
335+
336+Name.text(n: self ref Name): string
337+{
338+ if(n.impl != nil)
339+ return sys->sprint("builtin %q", n.name);
340+ return sys->sprint("%c%s%c", lquote, n.repl, rquote);
341+}
342+
343+dodumpdef(args: array of string)
344+{
345+ if(len args > 1){
346+ for(i := 1; i < len args; i++)
347+ if((n := lookup(args[i])) != nil)
348+ sys->fprint(sys->fildes(2), "%q %s\n", n.name, n.text());
349+ }else{
350+ for(i := 0; i < len names; i++)
351+ for(l := names[i]; l != nil; l = tl l)
352+ sys->fprint(sys->fildes(2), "%q %s\n", (hd l).name, (hd l).text());
353+ }
354+}
355+
356+pushs(s: string)
357+{
358+ for(i := len s; --i >= 0;)
359+ pushedback[pushedp++] = s[i];
360+}
361+
362+pushc(c: int)
363+{
364+ if(c >= 0)
365+ pushedback[pushedp++] = c;
366+}
367+
368+getc(): int
369+{
370+ if(pushedp > 0)
371+ return pushedback[--pushedp];
372+ for(; instack != nil; instack = tl instack){
373+ ios := hd instack;
374+ c := ios.fp.getc();
375+ if(c >= 0){
376+ if(c == '\n')
377+ ios.line++;
378+ return c;
379+ }
380+ }
381+ return -1;
382+}
383+
384+puts(s: string)
385+{
386+ if(curarg != nil)
387+ curarg.s += s;
388+ else if(curdiv > 0)
389+ diverted[curdiv] += s;
390+ else if(curdiv == 0)
391+ bout.puts(s);
392+}
393+
394+putc(c: int)
395+{
396+ if(curarg != nil){
397+ # stow in argument collection buffer
398+ curarg.s[len curarg.s] = c;
399+ }else if(curdiv > 0){
400+ l := len diverted[curdiv];
401+ diverted[curdiv][l] = c;
402+ }else if(curdiv == 0)
403+ bout.putc(c);
404+}
405+
406+expand(def: ref Name, args: array of string)
407+{
408+ if(def.impl != nil){
409+ def.impl(args);
410+ return;
411+ }
412+ if(def.repl == def.name || def.repl == "$0"){
413+ puts(def.name);
414+ return;
415+ }
416+ if(!def.dol || def.repl == nil){
417+ pushs(def.repl);
418+ return;
419+ }
420+ # expand $n
421+ s := def.repl;
422+ for(i := len s; --i >= 1;){
423+ if(s[i-1] == '$' && (c := s[i]-'0') >= 0 && c <= 9){
424+ if(c < len args)
425+ pushs(args[c]);
426+ i--;
427+ }else
428+ pushc(s[i]);
429+ }
430+ if(i >= 0)
431+ pushc(s[0]);
432+}
433+
434+hasdol(s: string): int
435+{
436+ for(i := 0; i < len s; i++)
437+ if(s[i] == '$')
438+ return 1;
439+ return 0;
440+}
441+
442+dodefine(args: array of string)
443+{
444+ if(len args > 2)
445+ define(args[1], args[2]);
446+ else if(len args > 1)
447+ define(args[1], "");
448+}
449+
450+doundefine(args: array of string)
451+{
452+ for(i := 1; i < len args; i++)
453+ undefine(args[i]);
454+}
455+
456+doeval(args: array of string)
457+{
458+ if(len args > 1)
459+ pushs(string eval(args[1]));
460+}
461+
462+dodivert(args: array of string)
463+{
464+ if(len args > 1){
465+ n := int args[1];
466+ if(n < 0 || n >= len diverted)
467+ n = -1;
468+ curdiv = n;
469+ }else
470+ curdiv = 0;
471+}
472+
473+dodivnum(nil: array of string)
474+{
475+ pushs(string curdiv);
476+}
477+
478+doundivert(args: array of string)
479+{
480+ if(len args <= 1){ # do all but current, in order
481+ for(i := 1; i < len diverted; i++){
482+ if(i != curdiv){
483+ puts(diverted[i]);
484+ diverted[i] = nil;
485+ }
486+ }
487+ }else{ # do those specified
488+ for(i := 1; i < len args; i++){
489+ n := int args[i];
490+ if(n > 0 && n < len diverted && n != curdiv){
491+ puts(diverted[n]);
492+ diverted[n] = nil;
493+ }
494+ }
495+ }
496+}
497+
498+doifdef(args: array of string)
499+{
500+ if(len args < 2)
501+ return;
502+ n := lookup(args[1]);
503+ if(n != nil)
504+ pushs(args[2]);
505+ else if(len args > 2)
506+ pushs(args[3]);
507+}
508+
509+doifelse(args: array of string)
510+{
511+ for(i := 1; i+2 < len args; i += 3){
512+ if(args[i] == args[i+1]){
513+ pushs(args[i+2]);
514+ return;
515+ }
516+ }
517+ if(i > 2 && i == len args-1)
518+ pushs(args[i]);
519+}
520+
521+doincr(args: array of string)
522+{
523+ if(len args > 1)
524+ pushs(string (int args[1] + 1));
525+}
526+
527+doindex(args: array of string)
528+{
529+ if(len args > 2){
530+ a := args[1];
531+ b := args[2];
532+ for(i := 0; i+len b <= len a; i++){
533+ if(a[i: i+len b] == b){
534+ pushs(string i);
535+ return;
536+ }
537+ }
538+ pushs("-1");
539+ }
540+}
541+
542+doinclude(args: array of string)
543+{
544+ for(i := len args; --i >= 1;){
545+ fp := bufio->open(args[i], Sys->OREAD);
546+ if(fp == nil)
547+ error(sys->sprint("can't open %s: %r", args[i]));
548+ pushfile(args[i], fp);
549+ }
550+}
551+
552+dosinclude(args: array of string)
553+{
554+ for(i := len args; --i >= 1;){
555+ fp := bufio->open(args[i], Sys->OREAD);
556+ if(fp != nil)
557+ pushfile(args[i], fp);
558+ }
559+}
560+
561+clip(v, l, u: int): int
562+{
563+ if(v < l)
564+ return l;
565+ if(v > u)
566+ return u;
567+ return v;
568+}
569+
570+dosubstr(args: array of string)
571+{
572+ if(len args > 2){
573+ l := len args[1];
574+ o := clip(int args[2], 0, l);
575+ n := l;
576+ if(len args > 3)
577+ n = clip(int args[3], 0, l);
578+ if((n += o) > l)
579+ n = l;
580+ pushs(args[1][o: n]);
581+ }
582+}
583+
584+cindex(s: string, c: int): int
585+{
586+ for(i := 0; i < len s; i++)
587+ if(s[i] == c)
588+ return i;
589+ return -1;
590+}
591+
592+dotranslit(args: array of string)
593+{
594+ if(len args < 3)
595+ return;
596+ s := args[1];
597+ f := args[2];
598+ t := "";
599+ if(len args > 3)
600+ t = args[3];
601+ o := "";
602+ for(i := 0; i < len s; i++){
603+ if((j := cindex(f, s[i])) >= 0){
604+ if(j < len t)
605+ o[len o] = t[j];
606+ }else
607+ o[len o] = s[i];
608+ }
609+ pushs(o);
610+}
611+
612+doerrprint(args: array of string)
613+{
614+ s := "";
615+ for(i := 1; i < len args; i++)
616+ s += " "+args[i];
617+ if(s != nil)
618+ sys->fprint(sys->fildes(2), "m4:%s\n", s);
619+}
620+
621+dolen(args: array of string)
622+{
623+ if(len args > 1)
624+ puts(string len args[1]);
625+}
626+
627+dochangecom(args: array of string)
628+{
629+ case len args {
630+ 1 =>
631+ initcom = "";
632+ endcom = "";
633+ 2 =>
634+ initcom = args[1];
635+ endcom = "\n";
636+ * =>
637+ initcom = args[1];
638+ endcom = args[2];
639+ if(endcom == "")
640+ endcom = "\n";
641+ }
642+}
643+
644+dochangequote(args: array of string)
645+{
646+ case len args {
647+ 1 =>
648+ lquote = '`';
649+ rquote = '\'';
650+ 2 =>
651+ if(args[1] != nil)
652+ lquote = rquote = args[1][0];
653+ * =>
654+ if(args[1] != nil)
655+ lquote = args[1][0];
656+ if(args[2] != nil)
657+ rquote = args[2][0];
658+ }
659+}
660+
661+dodnl(nil: array of string)
662+{
663+ while((c := getc()) >= 0 && c != '\n')
664+ {}
665+}
666+
667+domaketemp(args: array of string)
668+{
669+ if(len args > 1)
670+ pushs(mktemp(args[1]));
671+}
672+
673+sysname: string;
674+
675+mktemp(s: string): string
676+{
677+ if(sysname == nil)
678+ sysname = readfile("/dev/sysname", "m4");
679+ # trim trailing X's
680+ for (x := len s; --x >= 0;)
681+ if(s[x] == 'X'){
682+ while(x > 0 && s[x-1] == 'X')
683+ x--;
684+ s = s[0: x];
685+ break;
686+ }
687+ # add system name, process ID and 'a'
688+ s += sys->sprint(".%s.%.10uda", sysname, sys->pctl(0, nil));
689+ while(sys->stat(s).t0 >= 0){
690+ if(s[len s-1] == 'z')
691+ error("out of temp files: "+s);
692+ s[len s-1]++;
693+ }
694+ return s;
695+}
696+
697+readfile(name: string, default: string): string
698+{
699+ fd := sys->open(name, Sys->OREAD);
700+ if(fd == nil)
701+ return default;
702+ buf := array[Sys->NAMEMAX] of byte;
703+ n := sys->read(fd, buf, len buf);
704+ if(n <= 0)
705+ return default;
706+ return string buf[0: n];
707+}
708+
709+#
710+# expressions provided use Limbo operators (C with signed shift and **),
711+# instead of original m4 ones (where | and & were || and &&, and ^ was power),
712+# but that's true of later unix m4 implementations too
713+#
714+
715+Oeof, Ogok, Oge, Ole, One, Oeq, Opow, Oand, Oor, Orsh, Olsh, Odigits: con 'a'+iota;
716+Syntax, Badeval: exception;
717+evalin: string;
718+evalp := 0;
719+
720+eval(s: string): int
721+{
722+ evalin = s;
723+ evalp = 0;
724+ looked = -1;
725+ {
726+ v := expr(1);
727+ if(evalp < len evalin)
728+ raise Syntax;
729+ return v;
730+ }exception{
731+ Syntax =>
732+ error(sys->sprint("syntax error: %q %q", evalin[0: evalp], evalin[evalp:]));
733+ return 0;
734+ Badeval =>
735+ error(sys->sprint("zero divide in %q", evalin));
736+ return 0;
737+ }
738+}
739+
740+eval1(op: int, v1, v2: int): int raises Badeval
741+{
742+ case op{
743+ '+' => return v1 + v2;
744+ '-' => return v1 - v2;
745+ '*' => return v1 * v2;
746+ '%' =>
747+ if(v2 == 0)
748+ raise Badeval; # division by zero
749+ return v1 % v2;
750+ '/' =>
751+ if(v2 == 0)
752+ raise Badeval; # division by zero
753+ return v1 / v2;
754+ Opow =>
755+ if(v2 < 0)
756+ raise Badeval;
757+ return v1 ** v2;
758+ '&' => return v1 & v2;
759+ '|' => return v1 | v2;
760+ '^' => return v1 ^ v2;
761+ Olsh => return v1 << v2;
762+ Orsh => return v1 >> v2;
763+ Oand => return v1 && v2;
764+ Oor => return v1 || v2;
765+ '<' => return v1 < v2;
766+ '>' => return v1 > v2;
767+ Ole => return v1 <= v2;
768+ Oge => return v1 >= v2;
769+ One => return v1 != v2;
770+ Oeq => return v1 == v2;
771+ * =>
772+ sys->print("unknown op: %c\n", op); # shouldn't happen
773+ raise Badeval;
774+ }
775+}
776+
777+priority(c: int): int
778+{
779+ case c {
780+ Oor => return 1;
781+ Oand => return 2;
782+ '|' => return 3;
783+ '^' => return 4;
784+ '&' => return 5;
785+ Oeq or One => return 6;
786+ '<' or '>' or Oge or Ole => return 7;
787+ Olsh or Orsh => return 8;
788+ '+' or '-' => return 9;
789+ '*' or '/' or '%' => return 10;
790+ Opow => return 11;
791+ * => return 0;
792+ }
793+}
794+
795+rightassoc(c: int): int
796+{
797+ return c == Opow;
798+}
799+
800+expr(prec: int): int raises(Syntax, Badeval)
801+{
802+ {
803+ v := primary();
804+ while(priority(look()) >= prec){
805+ op := lex();
806+ r := priority(op) + !rightassoc(op);
807+ v = eval1(op, v, expr(r));
808+ }
809+ return v;
810+ }exception{
811+ Syntax or Badeval =>
812+ raise;
813+ }
814+}
815+
816+primary(): int raises Syntax
817+{
818+ {
819+ case lex() {
820+ '(' =>
821+ v := expr(1);
822+ if(lex() != ')')
823+ raise Syntax;
824+ return v;
825+ '+' =>
826+ return primary();
827+ '-' =>
828+ return -primary();
829+ '!' =>
830+ return !primary();
831+ '~' =>
832+ return ~primary();
833+ Odigits =>
834+ return yylval;
835+ * =>
836+ raise Syntax;
837+ }
838+ }exception{
839+ Syntax =>
840+ raise;
841+ }
842+}
843+
844+yylval := 0;
845+looked := -1;
846+
847+look(): int
848+{
849+ looked = lex();
850+ return looked;
851+}
852+
853+lex(): int
854+{
855+ if((c := looked) >= 0){
856+ looked = -1;
857+ return c; # if Odigits, assumes yylval untouched
858+ }
859+ while(evalp < len evalin && isspace(evalin[evalp]))
860+ evalp++;
861+ if(evalp >= len evalin)
862+ return Oeof;
863+ case c = evalin[evalp++] {
864+ '*' =>
865+ return ifnext('*', Opow, '*');
866+ '>' =>
867+ return ifnext('=', Oge, ifnext('>', Orsh, '>'));
868+ '<' =>
869+ return ifnext('=', Ole, ifnext('<', Olsh, '<'));
870+ '=' =>
871+ return ifnext('=', Oeq, Oeq);
872+ '!' =>
873+ return ifnext('=', One, '!');
874+ '|' =>
875+ return ifnext('|', Oor, '|');
876+ '&' =>
877+ return ifnext('&', Oand, '&');
878+ '0' to '9' =>
879+ evalp--;
880+ n := 0;
881+ while(evalp < len evalin && (c = evalin[evalp]) >= '0' && c <= '9'){
882+ n = n*10 + (c-'0');
883+ evalp++;
884+ }
885+ yylval = n;
886+ return Odigits;
887+ * =>
888+ return c;
889+ }
890+}
891+
892+ifnext(a, t, f: int): int
893+{
894+ if(evalp < len evalin && evalin[evalp] == a){
895+ evalp++;
896+ return t;
897+ }
898+ return f;
899+}
appl/cmd/mkfile+6−1
@@ -5,6 +5,7 @@ DIRS=\ · 4 unmodified lines
55 auxi\
66 avr\
77 disk\
8+ fs\
89 install\
910 ip\
1011 lego\
@@ -13,8 +14,11 @@ DIRS=\ · 2 unmodified lines
1314 mk\
1415 mpc\
1516 ndb\
17+ owen\
18+ scheduler\
1619 sh\
1720 spki\
21+ ssh\
1822 usb\
1923
2024 TARG=\
@@ -88,6 +92,7 @@ TARG=\ · 67 unmodified lines
8892 look.dis\
8993 ls.dis\
9094 lstar.dis\
95+ m4.dis\
9196 man2html.dis\
9297 man2txt.dis\
9398 mc.dis\
@@ -153,6 +158,7 @@ TARG=\ · 59 unmodified lines
153158 touchcal.dis\
154159 tokenize.dis\
155160 tr.dis\
161+ trfs.dis\
156162 tsort.dis\
157163 unicode.dis\
158164 units.dis\
@@ -171,7 +177,6 @@ TARG=\ · 12 unmodified lines
171177 wmimport.dis\
172178 xargs.dis\
173179 xd.dis\
174− xmount.dis\
175180 yacc.dis\
176181 zeros.dis\
177182
dis/m4.dis+0−0

Binary file not shown.

lib/proto/inferno+2−2
@@ -614,6 +614,7 @@ appl · 613 unmodified lines
614614 look.b
615615 ls.b
616616 lstar.b
617+ m4.b
617618 man2html.b
618619 man2txt.b
619620 manufacture.b
@@ -1242,8 +1243,6 @@ appl · 622 unmodified lines
12421243 spider.b
12431244 spit.b
12441245 whist.b
1245− man
1246− +
12471246 lib
12481247 allow.b
12491248 allow.m
@@ -1755,6 +1754,7 @@ dis · 505 unmodified lines
17551754 lookman
17561755 ls.dis
17571756 lstar.dis
1757+ m4.dis
17581758 man
17591759 man2html.dis
17601760 man2txt.dis
man/1/m4+233−0
@@ -0,0 +1,233 @@
1+.TH M4 1
2+.SH NAME
3+m4 \- macro processor
4+.SH SYNOPSIS
5+.B m4
6+[
7+.BI -D name = value
8+] [
9+.BI -U name
10+] [
11+.I file
12+\&...
13+]
14+.SH DESCRIPTION
15+.I M4
16+is a general-purpose macro processor.
17+It copies text from each of the input
18+.I files
19+in order (or standard input by default), and writes the processed text to the standard output.
20+.PP
21+Macro calls
22+have the form
23+.IP
24+.IB name ( arg1,\ arg2,\ ...,\ argn )
25+.PP
26+The `(' must immediately follow the name of the macro.
27+If a defined macro name is not followed by a `(',
28+it is deemed to have no arguments.
29+Leading unquoted blanks, tabs, and newlines are ignored while collecting arguments.
30+A comma within a nested parenthesis is part of an argument value, not an argument separator.
31+Potential macro names consist of alphabetic letters, Unicode characters,
32+digits, and underscore `\_', where the first character is not a digit.
33+.PP
34+The left and right single quotes(ie, grave and acute accents \`\|\' ) are used to quote strings.
35+Because the left and right quotes are distinct, quoted strings may nest.
36+The value of a quoted string is the string stripped of the outermost quotes.
37+.PP
38+When
39+.I m4
40+recognises a macro name, followed by a `(', it collects arguments up to a matching right parenthesis.
41+Macro evaluation proceeds normally during this collection, and the text produced by those macro calls
42+is interpreted exactly as if it had been in the original input stream (in place of the corresponding macro call).
43+Thus, any commas or right parentheses within the value of a nested
44+call are as effective as those in the original input text.
45+(Remember however that commas within
46+.I nested
47+parentheses are not argument separators.)
48+After argument collection,
49+the value of the macro is pushed back onto the input stream
50+and rescanned.
51+.PP
52+.I M4
53+makes available the following built-in macros.
54+They may be redefined, but once this is done the original meaning is lost.
55+Their values are null unless otherwise stated.
56+.TF changequote
57+.TP
58+changecom
59+Change the starting and ending delimiters for subsequent comments to the first and second arguments.
60+If the second argument is missing or an empty string, comments will be ended by newline.
61+If there are no arguments, there are no comments.
62+.TP
63+changequote
64+Change quote characters to the first and second arguments.
65+.I Changequote
66+without arguments restores the original values of
67+.BR `\|' .
68+.TP
69+define
70+The second argument is installed as the value of the macro
71+whose name is the first argument.
72+When the macro is later called (expanded),
73+each occurrence in the replacement text of
74+.BI $ n,
75+where
76+.I n
77+is a digit,
78+is replaced by the
79+.IR n -th
80+argument of that macro call.
81+Argument 0 is the name of the macro;
82+missing arguments are replaced by the null string.
83+If the macro value is the same as its name, or the value is simply
84+.BR $0 ,
85+the result is the macro name.
86+.TP
87+divert
88+.I M4
89+maintains 10 output streams,
90+numbered 0-9.
91+The final output is the concatenation of the streams
92+in numerical order;
93+initially stream 0 is the current stream.
94+The
95+.I divert
96+macro changes the current output stream to its (digit-string)
97+argument.
98+Output diverted to a stream other than 0 through 9
99+is discarded.
100+.TP
101+divnum
102+Returns the value of the current output stream.
103+.TP
104+dnl
105+Reads and discards characters up to and including the next newline.
106+.TP
107+dumpdef
108+Prints current names and definitions,
109+for the named items, or for all if no arguments are given.
110+.TP
111+errprint
112+Prints its argument
113+on the diagnostic output file.
114+.TP
115+eval
116+Evaluates its argument as an arithmetic expression, using 32-bit arithmetic.
117+Operators are those of Limbo: the binary operators
118+.BR || ,
119+.BR && ,
120+.BR | ,
121+.BR ^ ,
122+.BR & ,
123+.BR "== !=" ,
124+.BR "< > >= <=" ,
125+.B "<< >>"
126+(arithmetic shifts),
127+.BR "+ -" ,
128+.BR "* / %" ,
129+.BR "**" " (power)";
130+the unary operators
131+.BR + ,
132+.BR - ,
133+.BR ~ ,
134+.BR ! ;
135+and parenthesis.
136+The operator precedence is the same as in Limbo.
137+Right shifts are signed.
138+.TP
139+ifdef
140+If the first argument is defined, the value is the second argument, otherwise the third.
141+If there is no third argument, the value is null.
142+The word
143+.B inferno
144+is predefined with
145+.RB ` inferno '
146+as its replacement text.
147+.TP
148+ifelse
149+Has three or more arguments.
150+If the first argument is the same string as the second,
151+then the value is the third argument.
152+If not, the process is repeated with arguments 4, 5, 6 and so on, in groups of three.
153+If no match is found, the result is the remaining argument (not part of a group of three),
154+or null if none is present.
155+.TP
156+include
157+Returns the contents of the file named in the argument.
158+.TP
159+incr
160+Returns the value of its argument incremented by 1.
161+The value of the argument is calculated
162+by interpreting an initial digit-string as a decimal number.
163+.TP
164+index
165+Returns the position in its first argument where the second argument begins (zero origin),
166+or \-1 if the second argument does not occur.
167+.TP
168+len
169+Returns the number of characters in its argument.
170+.TP
171+maketemp
172+Returns its first argument after replacing any trailing XXXs by the current host name, process ID, and a unique letter.
173+Normally used to create unique temporary file names.
174+.TP
175+sinclude
176+The same as
177+.I include,
178+except that it
179+says nothing if the file is inaccessible.
180+.TP
181+substr
182+Returns a substring of its first argument.
183+The second argument is a zero origin
184+number selecting the first character;
185+the third argument indicates the length of the substring.
186+A missing third argument is taken to be large enough to extend to
187+the end of the first string.
188+\" .TP
189+\" syscmd
190+\" executes the shell command given in the first argument.
191+\" No value is returned.
192+.TP
193+translit
194+Transliterates the characters in its first argument
195+from the set given by the second argument to the set given by the third.
196+No abbreviations are permitted.
197+.TP
198+undefine
199+Removes the definition of the macro named in its argument.
200+.TP
201+undivert
202+Causes immediate output of text from diversions named as
203+arguments, or all diversions if no argument.
204+Text may be undiverted into another diversion.
205+Undiverting discards the diverted text.
206+.PD
207+.PP
208+.I M4
209+interprets the
210+.B -D
211+and
212+.B -U
213+command line options after installing the predefined macro set.
214+The
215+.B -D
216+option defines
217+.I name
218+as a macro with the given
219+.IR value .
220+The
221+.B -U
222+option
223+.I undefines
224+the given macro
225+.IR name ,
226+which may be one of the predefined macros.
227+.PP
228+.I M4
229+in Inferno is more closely related to the original version in Seventh Edition UNIX
230+than the more elaborate relatives in System V and POSIX.
231+.SH "SEE ALSO"
232+B. W. Kernighan and D. M. Ritchie,
233+.I The M4 Macro Processor