]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/htmlroff/t7.c
ndb/dnsquery, ndb/csquery: handle long lines
[plan9front.git] / sys / src / cmd / htmlroff / t7.c
1 /*
2  * 7.  Macros, strings, diversion, and position traps.
3  *
4  *      macros can override builtins
5  *      builtins can be renamed or removed!
6  */
7
8 #include "a.h"
9
10 enum
11 {
12         MAXARG = 10,
13         MAXMSTACK = 40
14 };
15
16 /* macro invocation frame */
17 typedef struct Mac Mac;
18 struct Mac
19 {
20         int argc;
21         Rune *argv[MAXARG];
22 };
23
24 Mac             mstack[MAXMSTACK];
25 int             nmstack;
26 void            emitdi(void);
27 void            flushdi(void);
28
29 /*
30  * Run a user-defined macro.
31  */
32 void popmacro(void);
33 int
34 runmacro(int dot, int argc, Rune **argv)
35 {
36         Rune *p;
37         int i;
38         Mac *m;
39         
40 if(verbose && isupperrune(argv[0][0])) fprint(2, "run: %S\n", argv[0]);
41         p = getds(argv[0]);
42         if(p == nil){
43                 if(verbose)
44                         warn("ignoring unknown request %C%S", dot, argv[0]);
45                 if(verbose > 1){
46                         for(i=0; i<argc; i++)
47                                 fprint(2, " %S", argv[i]);
48                         fprint(2, "\n");
49                 }
50                 return -1;
51         }
52         if(nmstack >= nelem(mstack)){
53                 fprint(2, "%L: macro stack overflow:");
54                 for(i=0; i<nmstack; i++)
55                         fprint(2, " %S", mstack[i].argv[0]);
56                 fprint(2, "\n");
57                 return -1;
58         }
59         m = &mstack[nmstack++];
60         m->argc = argc;
61         for(i=0; i<argc; i++)
62                 m->argv[i] = erunestrdup(argv[i]);
63         pushinputstring(p);
64         nr(L(".$"), argc-1);
65         inputnotify(popmacro);
66         return 0;
67 }
68
69 void
70 popmacro(void)
71 {
72         int i;
73         Mac *m;
74         
75         if(--nmstack < 0){
76                 fprint(2, "%L: macro stack underflow\n");
77                 return;
78         }
79         m = &mstack[nmstack];
80         for(i=0; i<m->argc; i++)
81                 free(m->argv[i]);
82         if(nmstack > 0)
83                 nr(L(".$"), mstack[nmstack-1].argc-1);
84         else
85                 nr(L(".$"), 0);
86 }
87
88 void popmacro1(void);
89 jmp_buf runjb[10];
90 int nrunjb;
91
92 void
93 runmacro1(Rune *name)
94 {
95         Rune *argv[2];
96         int obol;
97         
98 if(verbose) fprint(2, "outcb %p\n", outcb);
99         obol = bol;
100         argv[0] = name;
101         argv[1] = nil;
102         bol = 1;
103         if(runmacro('.', 1, argv) >= 0){
104                 inputnotify(popmacro1);
105                 if(!setjmp(runjb[nrunjb++]))
106                         runinput();
107                 else
108                         if(verbose) fprint(2, "finished %S\n", name);
109         }
110         bol = obol;
111 }
112
113 void
114 popmacro1(void)
115 {
116         popmacro();
117         if(nrunjb >= 0)
118                 longjmp(runjb[--nrunjb], 1);
119 }
120
121 /*
122  * macro arguments
123  *
124  *      "" means " inside " "
125  *      "" empty string
126  *      \newline can be done
127  *      argument separator is space (not tab)
128  *      number register .$ = number of arguments
129  *      no arguments outside macros or in strings
130  *
131  *      arguments copied in copy mode
132  */
133
134 /*
135  * diversions
136  *
137  *      processed output diverted 
138  *      dn dl registers vertical and horizontal size of last diversion
139  *      .z - current diversion name
140  */
141
142 /*
143  * traps
144  *
145  *      skip most
146  *      .t register - distance to next trap
147  */
148 static Rune *trap0;
149
150 void
151 outtrap(void)
152 {
153         Rune *t;
154
155         if(outcb)
156                 return;
157         if(trap0){
158 if(verbose) fprint(2, "trap: %S\n", trap0);
159                 t = trap0;
160                 trap0 = nil;
161                 runmacro1(t);
162                 free(t);
163         }
164 }
165
166 /* .wh - install trap */
167 void
168 r_wh(int argc, Rune **argv)
169 {
170         int i;
171
172         if(argc < 2)
173                 return;
174
175         i = eval(argv[1]);
176         if(argc == 2){
177                 if(i == 0){
178                         free(trap0);
179                         trap0 = nil;
180                 }else
181                         if(verbose)
182                                 warn("not removing trap at %d", i);
183         }
184         if(argc > 2){
185                 if(i == 0){
186                         free(trap0);
187                         trap0 = erunestrdup(argv[2]);
188                 }else
189                         if(verbose)
190                                 warn("not installing %S trap at %d", argv[2], i);
191         }
192 }
193
194 void
195 r_ch(int argc, Rune **argv)
196 {
197         int i;
198         
199         if(argc == 2){
200                 if(trap0 && runestrcmp(argv[1], trap0) == 0){
201                         free(trap0);
202                         trap0 = nil;
203                 }else
204                         if(verbose)
205                                 warn("not removing %S trap", argv[1]);
206                 return;
207         }
208         if(argc >= 3){
209                 i = eval(argv[2]);
210                 if(i == 0){
211                         free(trap0);
212                         trap0 = erunestrdup(argv[1]);
213                 }else
214                         if(verbose)
215                                 warn("not moving %S trap to %d", argv[1], i);
216         }
217 }
218
219 void
220 r_dt(int argc, Rune **argv)
221 {
222         USED(argc);
223         USED(argv);
224         warn("ignoring diversion trap");
225 }
226
227 /* define macro - .de, .am, .ig */
228 void
229 r_de(int argc, Rune **argv)
230 {
231         Rune *end, *p;
232         Fmt fmt;
233         int ignore, len;
234
235         delreq(argv[1]);
236         delraw(argv[1]);
237         ignore = runestrcmp(argv[0], L("ig")) == 0;
238         if(!ignore)
239                 runefmtstrinit(&fmt);
240         end = L("..");
241         if(argc >= 3)
242                 end = argv[2];
243         if(runestrcmp(argv[0], L("am")) == 0 && (p=getds(argv[1])) != nil)
244                 fmtrunestrcpy(&fmt, p);
245         len = runestrlen(end);
246         while((p = readline(CopyMode)) != nil){
247                 if(runestrncmp(p, end, len) == 0 
248                 && (p[len]==' ' || p[len]==0 || p[len]=='\t'
249                         || (p[len]=='\\' && p[len+1]=='}'))){
250                         free(p);
251                         goto done;
252                 }
253                 if(!ignore)
254                         fmtprint(&fmt, "%S\n", p);
255                 free(p);
256         }
257         warn("eof in %C%S %S - looking for %#Q", dot, argv[0], argv[1], end);
258 done:
259         if(ignore)
260                 return;
261         p = runefmtstrflush(&fmt);
262         if(p == nil)
263                 sysfatal("out of memory");
264         ds(argv[1], p);
265         free(p);
266 }
267
268 /* define string .ds .as */
269 void
270 r_ds(Rune *cmd)
271 {
272         Rune *name, *line, *p;
273         
274         name = copyarg();
275         line = readline(CopyMode);
276         if(name == nil || line == nil){
277                 free(name);
278                 return;
279         }
280         p = line;
281         if(*p == '"')
282                 p++;
283         if(cmd[0] == 'd')
284                 ds(name, p);
285         else
286                 as(name, p);
287         free(name);
288         free(line);
289 }
290
291 /* remove request, macro, or string */
292 void
293 r_rm(int argc, Rune **argv)
294 {
295         int i;
296
297         emitdi();
298         for(i=1; i<argc; i++){
299                 delreq(argv[i]);
300                 delraw(argv[i]);
301                 ds(argv[i], nil);
302         }
303 }
304
305 /* .rn - rename request, macro, or string */
306 void
307 r_rn(int argc, Rune **argv)
308 {
309         USED(argc);
310         renreq(argv[1], argv[2]);
311         renraw(argv[1], argv[2]);
312         ds(argv[2], getds(argv[1]));
313         ds(argv[1], nil);
314 }
315
316 /* .di - divert output to macro xx */
317 /* .da - divert, appending to macro */
318 /* page offsetting is not done! */
319 Fmt difmt;
320 int difmtinit;
321 Rune di[20][100];
322 int ndi;
323
324 void
325 emitdi(void)
326 {
327         flushdi();
328         runefmtstrinit(&difmt);
329         difmtinit = 1;
330         fmtrune(&difmt, Uformatted);
331 }
332
333 void
334 flushdi(void)
335 {
336         int n;
337         Rune *p;
338         
339         if(ndi == 0 || difmtinit == 0)
340                 return;
341         fmtrune(&difmt, Uunformatted);
342         p = runefmtstrflush(&difmt);
343         memset(&difmt, 0, sizeof difmt);
344         difmtinit = 0;
345         if(p == nil)
346                 warn("out of memory in diversion %C%S", dot, di[ndi-1]);
347         else{
348                 n = runestrlen(p);
349                 if(n > 0 && p[n-1] != '\n'){
350                         p = runerealloc(p, n+2);
351                         p[n] = '\n';
352                         p[n+1] = 0;
353                 }
354         }
355         as(di[ndi-1], p);
356         free(p);
357 }
358
359 void
360 outdi(Rune r)
361 {
362 if(!difmtinit) abort();
363         if(r == Uempty)
364                 return;
365         fmtrune(&difmt, r);
366 }
367
368 /* .di, .da */
369 void
370 r_di(int argc, Rune **argv)
371 {
372         br();
373         if(argc > 2)
374                 warn("extra arguments to %C%S", dot, argv[0]);
375         if(argc == 1){
376                 /* end diversion */
377                 if(ndi <= 0){
378                         // warn("unmatched %C%S", dot, argv[0]);
379                         return;
380                 }
381                 flushdi();
382                 if(--ndi == 0){
383                         _nr(L(".z"), nil);
384                         outcb = nil;
385                 }else{
386                         _nr(L(".z"), di[ndi-1]);
387                         runefmtstrinit(&difmt);
388                         fmtrune(&difmt, Uformatted);
389                         difmtinit = 1;
390                 }
391                 return;
392         }
393         /* start diversion */
394         /* various register state should be saved, but it's all useless to us */
395         flushdi();
396         if(ndi >= nelem(di))
397                 sysfatal("%Cdi overflow", dot);
398         if(argv[0][1] == 'i')
399                 ds(argv[1], nil);
400         _nr(L(".z"), argv[1]);
401         runestrcpy(di[ndi++], argv[1]);
402         runefmtstrinit(&difmt);
403         fmtrune(&difmt, Uformatted);
404         difmtinit = 1;
405         outcb = outdi;
406 }
407
408 /* .wh - install trap */
409 /* .ch - change trap */
410 /* .dt - install diversion trap */
411
412 /* set input-line count trap */
413 int itrapcount;
414 int itrapwaiting;
415 Rune *itrapname;
416
417 void
418 r_it(int argc, Rune **argv)
419 {
420         if(argc < 3){
421                 itrapcount = 0;
422                 return;
423         }
424         itrapcount = eval(argv[1]);
425         free(itrapname);
426         itrapname = erunestrdup(argv[2]);
427 }
428
429 void
430 itrap(void)
431 {
432         itrapset();
433         if(itrapwaiting){
434                 itrapwaiting = 0;
435                 runmacro1(itrapname);
436         }
437 }
438
439 void
440 itrapset(void)
441 {
442         if(itrapcount > 0 && --itrapcount == 0)
443                 itrapwaiting = 1;
444 }
445
446 /* .em - invoke macro when all input is over */
447 void
448 r_em(int argc, Rune **argv)
449 {
450         Rune buf[20];
451         
452         USED(argc);
453         runesnprint(buf, nelem(buf), ".%S\n", argv[1]);
454         as(L("eof"), buf);
455 }
456
457 int
458 e_star(void)
459 {
460         Rune *p;
461         
462         p = getds(getname());
463         if(p)
464                 pushinputstring(p);
465         return 0;
466 }
467
468 int
469 e_t(void)
470 {
471         if(inputmode&CopyMode)
472                 return '\t';
473         return 0;
474 }
475
476 int
477 e_a(void)
478 {
479         if(inputmode&CopyMode)
480                 return '\a';
481         return 0;
482 }
483
484 int
485 e_backslash(void)
486 {
487         if(inputmode&ArgMode)
488                 ungetrune('\\');
489         return backslash;
490 }
491
492 int
493 e_dot(void)
494 {
495         return '.';
496 }
497
498 int
499 e_dollar(void)
500 {
501         int c;
502
503         c = getnext();
504         if(c < '1' || c > '9'){
505                 ungetnext(c);
506                 return 0;
507         }
508         c -= '0';
509         if(nmstack <= 0 || mstack[nmstack-1].argc <= c)
510                 return 0;
511         pushinputstring(mstack[nmstack-1].argv[c]);
512         return 0;
513 }
514
515 void
516 t7init(void)
517 {       
518         addreq(L("de"), r_de, -1);
519         addreq(L("am"), r_de, -1);
520         addreq(L("ig"), r_de, -1);
521         addraw(L("ds"), r_ds);
522         addraw(L("as"), r_ds);
523         addreq(L("rm"), r_rm, -1);
524         addreq(L("rn"), r_rn, -1);
525         addreq(L("di"), r_di, -1);
526         addreq(L("da"), r_di, -1);
527         addreq(L("it"), r_it, -1);
528         addreq(L("em"), r_em, 1);
529         addreq(L("wh"), r_wh, -1);
530         addreq(L("ch"), r_ch, -1);
531         addreq(L("dt"), r_dt, -1);
532         
533         addesc('$', e_dollar, CopyMode|ArgMode|HtmlMode);
534         addesc('*', e_star, CopyMode|ArgMode|HtmlMode);
535         addesc('t', e_t, CopyMode|ArgMode);
536         addesc('a', e_a, CopyMode|ArgMode);
537         addesc('\\', e_backslash, ArgMode|CopyMode);
538         addesc('.', e_dot, CopyMode|ArgMode);
539         
540         ds(L("eof"), L(".sp 0.5i\n"));
541         ds(L(".."), L(""));
542 }
543