Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/html/troff2html.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1996-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* *
19
***********************************************************************/
20
#pragma prototyped
21
/*
22
* Glenn Fowler
23
* AT&T Research
24
*
25
* troff to html filter
26
*
27
* NOTE: not handled in state.groff mode
28
*
29
* \?anything\?
30
*/
31
32
static const char usage[] =
33
"[-?\n@(#)$Id: troff2html (AT&T Research) 2004-04-26 $\n]"
34
USAGE_LICENSE
35
"[+NAME?troff2html - convert troff/groff input to html]"
36
"[+DESCRIPTION?\btroff2html\b converts \btroff\b(1) (or \bgroff\b(1),"
37
" depending on the processing mode) input documents to an \bhtml\b"
38
" document on the standard output. Although a full \atroff\a parse"
39
" is done, many features are ignored in the mapping to \bhtml\b.]"
40
"[+?The \atroff\a \bt\b condition test evaluates \btrue\b, the \bn\b"
41
" condition tests evaluates \bfalse\b, and the \agroff\a compatibility"
42
" register \b\\\\n[.C]]\b evaluates to 0 (enable \agroff\a parsing)"
43
" if it is referenced before the first \agroff\a \b.cp\b (compatibility"
44
" mode) request.]"
45
"[+?The generated \bhtml\b has properly nested begin/end tags, even though most"
46
" browsers don't care.]"
47
48
"[i:identify?Reads identification options from \afile\a. Unknown options"
49
" are silently ignored. See the \b.xx\b request below for a description"
50
" of the options.]:[file]"
51
"[I:include?Appends \adirectory\a to the list of directories searched"
52
" for \b--macros\b and \b.so\b request files.]:[directory]"
53
"[m:macros?Locates and reads the macro package file \apackage\a. In order"
54
" to accomodate different \atroff\a installation styles the file search"
55
" order is fairly involved:]:[package]{"
56
" [+./package?]"
57
" [+directory/package?for all \b--include\b directories]"
58
" [+directory/tmac.package?for all \b--include\b directories]"
59
" [+../lib/tmac/tmac.package?for all directories on \b$PATH\b]"
60
" [+../lib/html/package?for all directories on \b$PATH\b]"
61
" [+../lib/html/mpackage.tr?for all directories on \b$PATH\b]"
62
" [+../share/groff/tmac/tmac.package?for all directories on \b$PATH\b]"
63
" [+../groff/share/groff/tmac/tmac.package?for all directories on \b$PATH\b]"
64
"}"
65
"[r:register?Initializes the number \aregister\a to \aN\a.]:[registerN]"
66
"[s:script?Reads \ascript\a as if it came from a file. \b--script='.nr A 123'\b"
67
" is equivalent to \b-rA123\b.]:[script]"
68
"[v:verbose?Enables verbose error and warning messages. Following \atroff\a"
69
" tradition, \btroff2html\b by default does not warn about unknown"
70
" requests; \b--verbose\b enables such warnings.]"
71
72
"[+EXTENSIONS?\b.xx\b \aname\a[=\avalue\a]] is a special \btroff2html\b"
73
" request that handles program tracing, \bhtml\b extensions and \atroff\a"
74
" macro package magic that went way past the author's willingness"
75
" to understand. Supported operations are:]{"
76
" [+author=text?Specifies the contact information for the document"
77
" HEAD section.]"
78
" [+background=URL?Specifies the document background URL.]"
79
" [+debug=level?The debug trace \alevel\a; higher levels produce"
80
" more output.]"
81
" [+get=+-register?Traces each \bget\b for the named number"
82
" \aregister\a. \b-\b turns tracing off.]"
83
" [+hot='word ...'?Adds (\b+\b) or removes (\b-\b) \aword\a ... from"
84
" the hot-word list. Constructs that match (\ahot-word\a ..."
85
" \aSWITCH-FONT\a text \aSWITCH-FONT\a ... ) adds \atext\a as"
86
" a hot link to another portion of the document. \brefer\b and"
87
" \bsee\b are the default hot words. Case is ignored when"
88
" matching hot words.]"
89
" [+logo=URL?Specifies the logo/banner image URL that is centered"
90
" at the top of the document.]"
91
" [+mailto=address?Sets the email \aaddress\a to send comments and"
92
" suggestions.]"
93
" [+meta.name?Emits the \bhtml\b tag \b<META name=\b\"\aname\a\""
94
" \bcontent=\b\"\acontent\a\"\b>\b.]"
95
" [+package=text?\atext\a is prepended to the \bhtml\b document title.]"
96
" [+set=+-register?Traces each \bset\b for the named number"
97
" \aregister\a. \b-\b turns tracing off.]"
98
" [+title=text?Sets the document title.]"
99
"}"
100
"[+?Local URL links are generated for all top level headings. These can be"
101
" referenced by embedding the benign (albeit convoluted) \atroff\a"
102
" construct \\h'0*\\w\"label\"'text\\g'0', where \alabel\a is the"
103
" local link label and \atext\a is the hot link text. If \alabel\a"
104
" and \atext\a are the same then use \\h'0*1'text\\h'0'.]"
105
106
"\n"
107
"\n[ file ... ]\n"
108
"\n"
109
"[+SEE ALSO?\bgroff\b(1), \bhtml2rtf\b(1), \bman\b(1), \bmm\b(1),"
110
" \bmm2html\b(1), \btroff\b(1)]"
111
;
112
113
#include "troff2html.h"
114
115
#include <error.h>
116
#include <hashkey.h>
117
#include <ls.h>
118
#include <tm.h>
119
120
/*
121
* intermediate code
122
*
123
* CODE_0
124
* CODE_1 <op>
125
* CODE_2 <data> <op>
126
* CODE_n <length> <op> <length-data>
127
*/
128
129
#define DEFAULT_cc '.'
130
#define DEFAULT_c2 '\''
131
#define DEFAULT_ec '\\'
132
#define DEFAULT_ft 1
133
#define DEFAULT_pc '%'
134
#define DEFAULT_ps 10
135
#define DEFAULT_vs 12
136
137
#define CODE_1 1
138
#define CODE_2 2
139
#define CODE_n 3
140
#define CODE_0 4
141
142
#define END(x) ((x)|OP_END)
143
#define LABEL(x) ((x)|OP_LABEL)
144
#define OP(x) ((x)&077)
145
146
#define OP_END 0200
147
#define OP_LABEL 0100
148
149
#define OP_a 1
150
#define OP_body 2
151
#define OP_br 3
152
#define OP_cc 4
153
#define OP_center 5
154
#define OP_comment 6
155
#define OP_dd 7
156
#define OP_div 8
157
#define OP_dl 9
158
#define OP_dt 10
159
#define OP_fn 11
160
#define OP_ft1 12
161
#define OP_ft2 13
162
#define OP_ft3 14
163
#define OP_ft4 15
164
#define OP_ft5 16
165
#define OP_h2 17
166
#define OP_h3 18
167
#define OP_h4 19
168
#define OP_head 20
169
#define OP_hr 21
170
#define OP_html 22
171
#define OP_link 23
172
#define OP_p 24
173
#define OP_pre 25
174
#define OP_ps 26
175
#define OP_sub 27
176
#define OP_sup 28
177
#define OP_ta 29
178
#define OP_tab 30
179
#define OP_tab_data 31
180
#define OP_tab_head 32
181
#define OP_tab_row 33
182
#define OP_title 34
183
#define OP_RAW 35
184
185
#define OP_ft (OP_ft1-1)
186
187
#define ARG_ALIGN(a) (((a)&3)-1)
188
#define ARG_ATTR(a) ((a)&0x0fff)
189
190
#define ARG_left 0x0001
191
#define ARG_center 0x0002
192
#define ARG_right 0x0003
193
194
#define ARG_compact 0x0010
195
#define ARG_wide 0x0020
196
197
#define ATT_INDEX(a) (((a)&0x0fff)-1)
198
199
#define ATT_NUMBER 0x8000
200
201
#define ATT_background 1
202
#define ATT_href 2
203
#define ATT_lref 3
204
#define ATT_id 4
205
#define ATT_name 5
206
#define ATT_size (ATT_NUMBER|6)
207
#define ATT_src 7
208
209
#define LINE 0x2000
210
#define LIST 0x4000
211
#define STACK 0x8000
212
213
#define DIVERTED() (state.out_top>state.out_stack)
214
#undef EOF
215
#define EOF 0
216
#define INDEX(a,b) (((a)<<8)|(b))
217
#define ISFILE() ((state.in_top-1)->ip)
218
#define GETC() (*state.in++)
219
#define GROFFINIT() do{if(!state.groff_init)state.groff|=state.groff_init=1;}while(0)
220
#define UNGETC(c) (*--state.in=(c))
221
222
#define UNGET_MAX (SF_BUFSIZE/8)
223
224
State_t state;
225
226
/*
227
* sfstruse(sp) with fatal error check
228
*/
229
230
static char*
231
use(Sfio_t* sp)
232
{
233
char* s;
234
235
if (!(s = sfstruse(sp)))
236
error(ERROR_SYSTEM|3, "out of space");
237
return s;
238
}
239
240
/*
241
* push input file/string
242
*/
243
244
static void
245
pushin(char* name, int line, Sfio_t* ip, char* data, Arg_t* arg)
246
{
247
register Pushin_t* pp;
248
int n;
249
250
if (state.in_top >= &state.in_stack[elementsof(state.in_stack)])
251
error(3, "input stack overflow");
252
pp = state.in_top++;
253
pp->in = state.in;
254
if (!(pp->ip = ip))
255
{
256
if (!(state.in = (unsigned char*)data) || !*state.in)
257
{
258
state.in = pp->in;
259
--state.in_top;
260
return;
261
}
262
}
263
else if (!(pp->buf = oldof(0, unsigned char, SF_BUFSIZE, UNGET_MAX + 1)))
264
error(ERROR_SYSTEM|3, "out of space [file pushin]");
265
else
266
{
267
pp->buf += UNGET_MAX;
268
if ((n = sfread(ip, pp->buf, SF_BUFSIZE)) <= 0)
269
{
270
if (ip && ip != sfstdin)
271
sfclose(ip);
272
state.in_top--;
273
return;
274
}
275
pp->end = pp->buf + n;
276
*pp->end++ = EOF;
277
state.in = pp->buf;
278
}
279
pp->file = error_info.file;
280
if (name)
281
{
282
if (!state.original.file)
283
{
284
state.original.file = error_info.file;
285
state.original.line = error_info.line;
286
}
287
error_info.file = name;
288
}
289
pp->line = error_info.line;
290
if (line < 0)
291
state.noline++;
292
else
293
{
294
if (line > 0)
295
error_info.line = line;
296
if (state.noline > 0)
297
state.noline++;
298
}
299
if (arg)
300
{
301
pp->mac = state.mac;
302
state.mac = arg;
303
if (pp->top.sp)
304
sfstrseek(pp->top.sp, 0, SEEK_SET);
305
else if (!(pp->top.sp = sfstropen()))
306
error(ERROR_SYSTEM|3, "out of space [macro call]");
307
pp->tag = state.tag;
308
state.tag = &pp->top;
309
}
310
else
311
pp->tag = 0;
312
}
313
314
static long expression(char*, char**, int);
315
316
/*
317
* return the next expression operand
318
*/
319
320
static long
321
operand(register char* s, char** e, int scale)
322
{
323
register int c;
324
register long n;
325
register long f;
326
int abs;
327
int neg;
328
long d;
329
char* x;
330
331
while (isspace(*s))
332
s++;
333
if (abs = *s == '|')
334
s++;
335
if (neg = *s == '-')
336
s++;
337
else if (*s == '+')
338
s++;
339
d = 1;
340
if (*s == '(')
341
{
342
n = (state.groff && isalpha(*(s + 1)) && *(s + 2) == ';') ?
343
expression(s + 3, &x, *(s + 1)) :
344
expression(s + 1, &x, scale);
345
s = x;
346
if (*s == ')')
347
s++;
348
c = *s++;
349
}
350
else
351
{
352
n = 0;
353
while ((c = *s++) >= '0' && c <= '9')
354
n = n * 10 + c - '0';
355
if (c == '.')
356
{
357
f = 0;
358
while ((c = *s++) >= '0' && c <= '9')
359
{
360
d *= 10;
361
n *= 10;
362
f = f * 10 + c - '0';
363
}
364
n += f;
365
}
366
}
367
for (;;)
368
{
369
switch (c)
370
{
371
case 'P':
372
n *= 72;
373
break;
374
case 'c':
375
n *= 170;
376
break;
377
case 'i':
378
n *= 432;
379
break;
380
case 'm':
381
n *= 8 * state.env->ps.current;
382
break;
383
case 'n':
384
case 'w':
385
n *= 6 * state.env->ps.current;
386
break;
387
case 'p':
388
n *= 6;
389
break;
390
case 's':
391
case 'u':
392
case 'z':
393
break;
394
case 'v':
395
n *= 6 * state.env->vs.current;
396
break;
397
default:
398
s--;
399
if (c = scale)
400
{
401
scale = 0;
402
continue;
403
}
404
break;
405
}
406
break;
407
}
408
n /= d;
409
if (e)
410
{
411
while (isspace(*s))
412
s++;
413
*e = s;
414
}
415
if (abs)
416
return 1;
417
if (neg)
418
n = -n;
419
return n;
420
}
421
422
/*
423
* convert units to scale
424
*/
425
426
static long
427
convert(long n, int up, int scale)
428
{
429
long m;
430
431
m = n;
432
switch (scale)
433
{
434
case 'M':
435
if (up)
436
n *= state.env->ps.current / 12;
437
else
438
n /= state.env->ps.current / 12;
439
break;
440
case 'P':
441
if (up)
442
n *= 72;
443
else
444
n /= 72;
445
break;
446
case 'c':
447
if (up)
448
n *= 170;
449
else
450
n /= 170;
451
break;
452
case 'i':
453
if (up)
454
n *= 432;
455
else
456
n /= 432;
457
break;
458
case 'm':
459
if (up)
460
n *= 8 * state.env->ps.current;
461
else
462
n /= 8 * state.env->ps.current;
463
break;
464
case 'n':
465
case 'w':
466
if (up)
467
n *= 6 * state.env->ps.current;
468
else
469
n /= 6 * state.env->ps.current;
470
break;
471
case 'p':
472
if (up)
473
n *= 6;
474
else
475
n /= 6;
476
break;
477
case 's':
478
case 'u':
479
case 'z':
480
break;
481
case 'v':
482
if (up)
483
n *= 6 * state.env->vs.current;
484
else
485
n /= 6 * state.env->vs.current;
486
break;
487
}
488
return n ? n : (m > 0) ? 1 : 0;
489
}
490
491
/*
492
* evaluate numeric expression in s
493
*/
494
495
static long
496
expression(char* s, char** e, int scale)
497
{
498
long n;
499
long m;
500
501
n = operand(s, &s, scale);
502
for (;;)
503
{
504
switch (*s++)
505
{
506
case CODE_2:
507
s++;
508
/*FALLTHROUGH*/
509
case CODE_1:
510
s++;
511
/*FALLTHROUGH*/
512
case CODE_0:
513
continue;
514
case '+':
515
n += operand(s, &s, scale);
516
continue;
517
case '-':
518
n -= operand(s, &s, scale);
519
continue;
520
case '/':
521
if (m = operand(s, &s, scale))
522
n /= m;
523
else
524
n = 0;
525
continue;
526
case '*':
527
n *= operand(s, &s, scale);
528
continue;
529
case '%':
530
if (m = operand(s, &s, scale))
531
n %= m;
532
else
533
n = 0;
534
continue;
535
case '<':
536
if (state.groff && *s == '?')
537
{
538
m = operand(s + 1, &s, scale);
539
if (m < n)
540
n = m;
541
}
542
else if (*s == '=')
543
n = n <= operand(s + 1, &s, scale);
544
else
545
n = n < operand(s, &s, scale);
546
continue;
547
case '>':
548
if (state.groff && *s == '?')
549
{
550
m = operand(s + 1, &s, scale);
551
if (m > n)
552
n = m;
553
}
554
else if (*s == '=')
555
n = n >= operand(s + 1, &s, scale);
556
else
557
n = n > operand(s, &s, scale);
558
continue;
559
case '=':
560
if (*s == '=')
561
s++;
562
n = n == operand(s, &s, scale);
563
continue;
564
case '&':
565
if (*s == '&')
566
s++;
567
n = (operand(s, &s, scale) > 0) && (n > 0);
568
continue;
569
case ':':
570
if (*s == ':')
571
s++;
572
n = (operand(s, &s, scale) > 0) || (n > 0);
573
continue;
574
default:
575
s--;
576
break;
577
}
578
break;
579
}
580
if (e)
581
{
582
while (isspace(*s))
583
s++;
584
*e = s;
585
}
586
if (scale)
587
n = convert(n, 0, scale);
588
return n;
589
}
590
591
/*
592
* evaluate a conditional expression
593
*/
594
595
static long
596
cond(register char* s, char** e)
597
{
598
register int c;
599
register int i;
600
register int q;
601
register long v;
602
register char* t;
603
char* u;
604
605
while (isspace(*s))
606
s++;
607
if (i = *s == '!')
608
s++;
609
while (isspace(*s))
610
s++;
611
switch (c = *s)
612
{
613
case '0':
614
case '1':
615
case '2':
616
case '3':
617
case '4':
618
case '5':
619
case '6':
620
case '7':
621
case '8':
622
case '9':
623
case '-':
624
case '+':
625
case '(':
626
v = expression(s, &u, 0) > 0;
627
s = u;
628
break;
629
case 'c':
630
s++;
631
if (*s == 'c' && *(s + 1) == 'h')
632
s += 2;
633
v = 0;
634
break;
635
case 'd':
636
case 'r':
637
t = s;
638
while (*++s && !isspace(*s));
639
q = *s;
640
*s = 0;
641
*t = c == 'd' ? '.' : 'n';
642
v = hashget(state.symbols, t) != 0;
643
*t = c;
644
*s = q;
645
break;
646
case 'g':
647
GROFFINIT();
648
v = state.groff != 0;
649
s++;
650
break;
651
case 'o':
652
case 't':
653
v = 1;
654
s++;
655
break;
656
default:
657
if (isalpha(c))
658
{
659
v = 0;
660
s++;
661
}
662
else
663
{
664
for (u = ++s; *s && *s != c; s++);
665
if (*s)
666
{
667
*s++ = 0;
668
for (t = s; *s && *s != c; s++);
669
if (*s)
670
*s++ = 0;
671
v = !strcmp(u, t);
672
}
673
else
674
v = 0;
675
}
676
break;
677
}
678
if (i)
679
v = !v;
680
while (isspace(*s))
681
s++;
682
if (e)
683
*e = s;
684
return v;
685
}
686
687
static void expand(Sfio_t*, char*);
688
689
/*
690
* pop input stream
691
* return non-0 on EOF
692
*/
693
694
static int
695
popin(void)
696
{
697
register Pushin_t* pp;
698
int n;
699
700
if (state.in_top <= state.in_stack)
701
{
702
state.in--;
703
return 1;
704
}
705
pp = state.in_top;
706
if (pp->loop)
707
{
708
state.in = (unsigned char*)sfstrbase(pp->loop);
709
return 0;
710
}
711
if (--pp == state.in_stack)
712
{
713
if (state.cond.level > 1 && state.verbose)
714
error(1, "%d missing .el request%s", state.cond.level - 1, state.cond.level == 2 ? "" : "s");
715
state.cond.level = 0;
716
}
717
if (pp->ip)
718
{
719
if (state.in < pp->end)
720
return 0;
721
if ((n = sfread(pp->ip, pp->buf, SF_BUFSIZE)) > 0)
722
{
723
pp->end = pp->buf + n;
724
*pp->end++ = EOF;
725
state.in = pp->buf;
726
return 0;
727
}
728
if (pp->ip && pp->ip != sfstdin)
729
sfclose(pp->ip);
730
free(pp->buf - UNGET_MAX);
731
}
732
state.in_top = pp;
733
if (state.noline > 0)
734
state.noline--;
735
if ((error_info.file = pp->file) == state.original.file)
736
{
737
state.original.file = 0;
738
state.original.line = 0;
739
}
740
error_info.line = pp->line;
741
if (pp->tag)
742
{
743
state.tag = pp->tag;
744
state.mac = pp->mac;
745
}
746
state.in = pp->in;
747
return 0;
748
}
749
750
/*
751
* copy <type><name> into buf of size n
752
*/
753
754
static char*
755
nam(int type, register char* name, char* buf, size_t n)
756
{
757
register char* t = buf;
758
register char* e = t + n - 1;
759
760
*t++ = type;
761
if ((*t++ = *name++) && (*t++ = *name))
762
{
763
if (state.groff)
764
while (t < e && (*t++ = *++name));
765
else
766
*t = 0;
767
}
768
return buf;
769
}
770
771
/*
772
* return number register pointer given name
773
* pointer created if not found
774
*/
775
776
static Num_t*
777
num(register char* s)
778
{
779
register Num_t* np;
780
char buf[MAXNAME];
781
782
if (!(np = (Num_t*)hashget(state.symbols, nam('n', s, buf, sizeof(buf)))))
783
{
784
if (!(np = newof(0, Num_t, 1, 0)))
785
error(ERROR_SYSTEM|3, "out of space [number]");
786
np->name = hashput(state.symbols, NiL, np);
787
np->format = '1';
788
np->increment = 1;
789
}
790
return np;
791
}
792
793
/*
794
* lookup or create (force!=0) io stream handle
795
*/
796
797
static Stream_t*
798
iop(char* s, int force)
799
{
800
register Stream_t* sp;
801
char buf[MAXNAME];
802
803
if (!(sp = (Stream_t*)hashget(state.symbols, nam('s', s, buf, sizeof(buf)))))
804
{
805
if (!force) error(1, "iop %s lookup failed", buf);
806
if (!force)
807
return 0;
808
if (!(sp = newof(0, Stream_t, 1, 0)))
809
error(ERROR_SYSTEM|3, "out of space [stream]");
810
hashput(state.symbols, NiL, sp);
811
}
812
return sp;
813
}
814
815
/*
816
* push output stream np
817
*/
818
819
static void
820
pushout(Sfio_t* np)
821
{
822
if (state.out_top >= &state.out_stack[elementsof(state.out_stack)])
823
error(3, "output stack overflow");
824
*state.out_top++ = state.out;
825
iop("stdout", 1)->sp = state.out = np;
826
}
827
828
/*
829
* pop ouput stream
830
* if retain>0 then strdup() buffer
831
* append an extra 0 for easy arg '\n' append by if/ie/el
832
*/
833
834
static char*
835
popout(void)
836
{
837
char* s;
838
839
if (!DIVERTED())
840
return 0;
841
if (state.out == state.tag->sp)
842
sfputc(state.out, 0);
843
if (state.out == state.nul)
844
s = 0;
845
else
846
s = use(state.out);
847
iop("stdout", 1)->sp = state.out = *--state.out_top;
848
return s;
849
}
850
851
/*
852
* return next character with possible line join
853
*/
854
855
static int
856
nextchar(void)
857
{
858
int c;
859
int n;
860
861
for (;;)
862
{
863
if ((c = GETC()) == EOF)
864
{
865
if (popin())
866
break;
867
continue;
868
}
869
if (c == state.ec)
870
{
871
escape:
872
switch (n = GETC())
873
{
874
case EOF:
875
if (popin())
876
break;
877
goto escape;
878
case '\n':
879
error_info.line++;
880
continue;
881
case 't':
882
c = '\t';
883
break;
884
case '"':
885
for (;;)
886
{
887
switch (c = GETC())
888
{
889
case EOF:
890
if (popin())
891
break;
892
continue;
893
case '\n':
894
break;
895
default:
896
if (c == state.ec)
897
{
898
escape_2:
899
switch (n = GETC())
900
{
901
case EOF:
902
if (popin())
903
break;
904
goto escape_2;
905
case '\n':
906
error_info.line++;
907
continue;
908
default:
909
continue;
910
}
911
break;
912
}
913
continue;
914
}
915
break;
916
}
917
break;
918
default:
919
UNGETC(n);
920
break;
921
}
922
break;
923
}
924
break;
925
}
926
return c;
927
}
928
929
/*
930
* call a macro
931
*/
932
933
static void
934
macro(Tag_t* tp, Arg_t* ap)
935
{
936
if (tp->body)
937
pushin(tp->file, tp->line, NiL, tp->body, ap);
938
}
939
940
/*
941
* set macro value
942
*/
943
944
static void
945
set(Tag_t* mp, char* value, int append)
946
{
947
int n;
948
949
if (append && mp->body)
950
{
951
sfputr(state.tmp, mp->body, -1);
952
sfputr(state.tmp, value, -1);
953
value = use(state.tmp);
954
}
955
n = strlen(value) + 1;
956
if (mp->size < n)
957
{
958
if (mp->size)
959
free(mp->body);
960
mp->size = n = roundof(n, 64);
961
if (!(mp->body = newof(0, char, n, 0)))
962
error(ERROR_SYSTEM|3, "out of space [set value]");
963
}
964
strcpy(mp->body, value);
965
mp->call = macro;
966
mp->flags |= TAG_PASS;
967
if (!mp->line)
968
{
969
mp->line = error_info.line;
970
mp->file = error_info.file;
971
}
972
if (mp->flags & TAG_TRACE_SET)
973
error(2, "set macro %s `%s'", mp->name + 1, mp->body);
974
}
975
976
/*
977
* return macro pointer given name
978
* pointer created if not found
979
*/
980
981
static Tag_t*
982
mac(char* name)
983
{
984
register Tag_t* mp;
985
char buf[MAXNAME];
986
987
if (!(mp = (Tag_t*)hashget(state.symbols, nam('.', name, buf, sizeof(buf)))))
988
{
989
if (!(mp = newof(0, Tag_t, 1, 0)))
990
error(ERROR_SYSTEM|3, "out of space [mac]");
991
mp->name = hashput(state.symbols, NiL, mp);
992
}
993
return mp;
994
}
995
996
static Env_t*
997
env(char* s)
998
{
999
register Env_t* v;
1000
char* e;
1001
long n;
1002
char buf[MAXNAME];
1003
1004
n = expression(s, &e, 0);
1005
if (*e)
1006
sfsprintf(buf, sizeof(buf), "E%s", s);
1007
else
1008
sfsprintf(buf, sizeof(buf), "E%d", n);
1009
if (!(v = (Env_t*)hashget(state.symbols, buf)))
1010
{
1011
if (!(v = newof(0, Env_t, 1, 0)))
1012
error(ERROR_SYSTEM|3, "out of space [environment]");
1013
v->name = hashput(state.symbols, NiL, v);
1014
}
1015
if (v->generation < state.generation)
1016
{
1017
v->generation = state.generation;
1018
v->c2 = DEFAULT_c2;
1019
v->cc = DEFAULT_cc;
1020
v->ft.current = v->ft.previous = DEFAULT_ft;
1021
v->nf = 0;
1022
v->ps.current = v->ps.previous = DEFAULT_ps;
1023
v->ss = 0;
1024
v->vs.current = v->vs.previous = DEFAULT_vs;
1025
}
1026
return v;
1027
}
1028
1029
/*
1030
* open name for reading
1031
* looking in state.dirs if needed
1032
* verbose<0 for -m option
1033
*/
1034
1035
static Sfio_t*
1036
find(char* name, char** found, int verbose)
1037
{
1038
register char* s;
1039
register Sfio_t* sp;
1040
char* path;
1041
char* t;
1042
char* x;
1043
int i;
1044
Dir_t* dp;
1045
char buf[PATH_MAX];
1046
1047
static const char* pathdirs[] =
1048
{
1049
"lib/tmac/tmac.%s",
1050
"lib/tmac/%s",
1051
"lib/html/%s",
1052
"lib/html/m%s.tr",
1053
"share/groff/tmac/tmac.%s",
1054
"share/groff/tmac/%s",
1055
"groff/share/groff/tmac/tmac.%s",
1056
"groff/share/groff/tmac/%s",
1057
};
1058
1059
if (verbose >= 0 && (sp = sfopen(NiL, name, "r")))
1060
{
1061
sfprintf(state.tmp, "/%s", name);
1062
path = use(state.tmp);
1063
goto hit;
1064
}
1065
if (*name != '/')
1066
{
1067
if (*name == '.' && (x = getenv("HOME")))
1068
{
1069
sfprintf(state.tmp, "/%s/%s", x, name);
1070
path = use(state.tmp);
1071
if (sp = sfopen(NiL, path + 1, "r"))
1072
goto hit;
1073
}
1074
for (dp = state.dirs; dp; dp = dp->next)
1075
{
1076
if (verbose >= 0)
1077
{
1078
sfprintf(state.tmp, "/%s/%s", dp->name, name);
1079
path = use(state.tmp);
1080
if (sp = sfopen(NiL, path + 1, "r"))
1081
goto hit;
1082
}
1083
sfprintf(state.tmp, "/%s/tmac.%s", dp->name, name);
1084
path = use(state.tmp);
1085
if (sp = sfopen(NiL, path + 1, "r"))
1086
goto hit;
1087
}
1088
for (i = 0; i < elementsof(pathdirs); i++)
1089
{
1090
sfprintf(state.tmp, pathdirs[i], name);
1091
path = use(state.tmp);
1092
if (pathpath(path, "", PATH_REGULAR|PATH_READ, buf + 1, sizeof(buf) - 1) && (sp = sfopen(NiL, buf + 1, "r")))
1093
{
1094
*(path = buf) = '/';
1095
goto hit;
1096
}
1097
}
1098
}
1099
if (verbose > 0)
1100
{
1101
/*
1102
* some systems provide
1103
* /usr/lib/tmac.mXXX
1104
* that references
1105
* /usr/lib/macros/mXXX[tn]
1106
* that is not provided
1107
*/
1108
1109
if (*(s = name) == '/')
1110
while (s = strchr(s, '/'))
1111
if (!strncmp(++s, "macros/", 7) && *(s += 7))
1112
{
1113
t = s + strlen(s) - 1;
1114
x = strchr(s, '.') ? "" : ".tr";
1115
while (*s)
1116
{
1117
sfprintf(state.tmp, "lib/html/%s%s", s, x);
1118
path = use(state.tmp);
1119
if (pathpath(path, "", PATH_REGULAR|PATH_READ, buf + 1, sizeof(buf) - 1) && (sp = sfopen(NiL, buf + 1, "r")))
1120
{
1121
*(path = buf) = '/';
1122
goto hit;
1123
}
1124
if (*t != 't')
1125
break;
1126
*t-- = 0;
1127
}
1128
}
1129
error(ERROR_SYSTEM|2, "%s: cannot read", name);
1130
}
1131
else if (verbose < 0)
1132
error(ERROR_SYSTEM|3, "-m%s: cannot find macro package", name);
1133
return 0;
1134
hit:
1135
message((-1, "FIND %s => %s", name, path + 1));
1136
if (found)
1137
*found = hashput(state.symbols, path, path + 1) + 1;
1138
s = path + strlen(path);
1139
while (s > path + 2)
1140
if (*--s == '/')
1141
{
1142
if (!strcmp(s, "/lib"))
1143
{
1144
*s = 0;
1145
set(mac(".P"), path + 1, 0);
1146
break;
1147
}
1148
*s = 0;
1149
}
1150
return sp;
1151
}
1152
1153
/*
1154
* generate intermediate CODE_0
1155
*/
1156
1157
static void
1158
code_0(void)
1159
{
1160
sfputc(state.out, CODE_0);
1161
}
1162
1163
/*
1164
* generate intermediate CODE_1
1165
*/
1166
1167
static void
1168
code_1(int code)
1169
{
1170
sfputc(state.out, CODE_1);
1171
sfputc(state.out, code);
1172
}
1173
1174
/*
1175
* generate intermediate CODE_2
1176
*/
1177
1178
static void
1179
code_2(int code, int data)
1180
{
1181
sfputc(state.out, CODE_2);
1182
sfputc(state.out, data);
1183
sfputc(state.out, code);
1184
}
1185
1186
/*
1187
* generate intermediate CODE_n
1188
*/
1189
1190
static void
1191
code_n(int code, char* s)
1192
{
1193
int n;
1194
1195
sfputc(state.out, CODE_n);
1196
n = s ? (strlen(s) + 1) : 1;
1197
if (n > 0177)
1198
sfputc(state.out, ((n >> 8) & 0177) | 0200);
1199
sfputc(state.out, n & 0177);
1200
sfputc(state.out, code);
1201
if (s)
1202
sfputr(state.out, s, 0);
1203
else
1204
sfputc(state.out, 0);
1205
}
1206
1207
/*
1208
* join all args from n on into 1 arg
1209
*/
1210
1211
static char*
1212
join(Arg_t* ap, int n)
1213
{
1214
if (n < 0 || ap->argc < n)
1215
return 0;
1216
n++;
1217
while (ap->argc >= n)
1218
*(ap->argv[ap->argc--] - 1) = ' ';
1219
return ap->argv[n - 1];
1220
}
1221
1222
/*
1223
* output error message line
1224
*/
1225
1226
static void
1227
notify(register char* s)
1228
{
1229
register int c;
1230
Sfoff_t p;
1231
1232
p = sftell(sfstderr);
1233
for (;;)
1234
{
1235
switch (c = *s++)
1236
{
1237
case 0:
1238
break;
1239
case CODE_0:
1240
continue;
1241
case CODE_1:
1242
s++;
1243
continue;
1244
case CODE_2:
1245
c = *s++;
1246
if (*s++ == OP_cc)
1247
sfputc(sfstderr, c);
1248
continue;
1249
default:
1250
sfputc(sfstderr, c);
1251
continue;
1252
}
1253
break;
1254
}
1255
if (sftell(sfstderr) != p)
1256
sfputc(sfstderr, '\n');
1257
}
1258
1259
/*
1260
* add trap s to xp
1261
*/
1262
1263
static void
1264
trap(Trap_t** xp, char* s)
1265
{
1266
Trap_t* ip;
1267
Trap_t* pp;
1268
1269
for (pp = 0, ip = *xp; ip; pp = ip, ip = ip->next)
1270
if (streq(ip->name + 1, s))
1271
return;
1272
if (!(ip = newof(0, Trap_t, 1, strlen(s) + 1)))
1273
error(ERROR_SYSTEM|3, "out of space [trap]");
1274
*ip->name = '.';
1275
strcpy(ip->name + 1, s);
1276
if (pp)
1277
pp->next = ip;
1278
else
1279
*xp = ip;
1280
}
1281
1282
/*
1283
* trigger traps on xp
1284
*/
1285
1286
static void
1287
trigger(Trap_t** xp)
1288
{
1289
Trap_t* ip;
1290
Trap_t* pp;
1291
1292
if (!DIVERTED() && (ip = *xp))
1293
{
1294
state.t = 1;
1295
do
1296
{
1297
sfputr(state.req, ip->name, '\n');
1298
pp = ip;
1299
ip = ip->next;
1300
free(pp);
1301
} while (ip);
1302
*xp = 0;
1303
pushin(xp == &state.fini ? "[*EOF*]" : "[*TRAP*]", 1, NiL, use(state.req), NiL);
1304
}
1305
}
1306
1307
/*
1308
* return value for register name with type
1309
* and increment/decrement i={0,'+','-'}
1310
*/
1311
1312
static char*
1313
value(char* name, int type, int i)
1314
{
1315
register Num_t* np;
1316
register char* b;
1317
register char* x;
1318
register long n;
1319
register long m;
1320
Tag_t* tp;
1321
char buf[8];
1322
1323
b = hashget(state.symbols, name);
1324
switch (type)
1325
{
1326
case 'g':
1327
if (!(np = (Num_t*)b))
1328
return "";
1329
b = np->value;
1330
if (isalnum(np->format))
1331
*b++ = np->format;
1332
else
1333
{
1334
for (n = 0; n < np->format; n++)
1335
*b++ = '0';
1336
*b++ = '1';
1337
}
1338
*b = 0;
1339
return np->value;
1340
case 'j':
1341
return "";
1342
case 'n':
1343
break;
1344
case '.':
1345
if (!(tp = (Tag_t*)b))
1346
return 0;
1347
if (tp->flags & TAG_TRACE_GET)
1348
error(2, "get macro %s `%s'", tp->name + 1, tp->body);
1349
return tp->body;
1350
default:
1351
return b;
1352
}
1353
if (!b)
1354
{
1355
name = strcpy(buf, "n");
1356
if (!(b = (char*)hashget(state.symbols, name)))
1357
return "0";
1358
}
1359
np = (Num_t*)b;
1360
if (np->flags & TAG_TRACE_GET)
1361
error(2, "get register %s %d %d", np->name + 1, np->number, np->increment);
1362
if (np->internal)
1363
{
1364
switch (INDEX(name[1], name[2]))
1365
{
1366
case INDEX('%',0):
1367
n = np->number;
1368
break;
1369
case INDEX('.','$'):
1370
n = state.mac ? state.mac->argc : 0;
1371
break;
1372
case INDEX('.','C'):
1373
GROFFINIT();
1374
n = state.groff == 0;
1375
break;
1376
case INDEX('.','F'):
1377
return state.original.file ? state.original.file : error_info.file;
1378
case INDEX('.','H'):
1379
n = 3000;
1380
break;
1381
case INDEX('.','L'):
1382
n = 1;
1383
break;
1384
case INDEX('.','P'):
1385
n = 1;
1386
break;
1387
case INDEX('.','R'):
1388
n = 256;
1389
break;
1390
case INDEX('.','V'):
1391
n = 6000;
1392
break;
1393
case INDEX('.','c'):
1394
if (name[3] == 'e')
1395
{
1396
n = state.it.center;
1397
break;
1398
}
1399
/*FALLTHROUGH*/
1400
case INDEX('c','.'):
1401
n = state.original.line ? state.original.line : error_info.line;
1402
break;
1403
case INDEX('.','e'):
1404
if (name[3] == 'v')
1405
n = state.it.center; /*GROFF: string valued?*/
1406
break;
1407
case INDEX('.','f'):
1408
n = state.env->ft.current;
1409
break;
1410
case INDEX('.','g'):
1411
GROFFINIT();
1412
n = state.groff != 0;
1413
break;
1414
case INDEX('.','i'):
1415
n = state.env->in.current;
1416
break;
1417
case INDEX('.','k'):
1418
n = 100;
1419
break;
1420
case INDEX('.','l'):
1421
n = state.env->ll.current;
1422
break;
1423
case INDEX('.','n'):
1424
n = state.n;
1425
break;
1426
case INDEX('.','p'):
1427
if (name[3] == 'n')
1428
n = 2;
1429
else if (name[3] == 's' && name[4] == 'r')
1430
n = state.env->ps.previous;
1431
else
1432
n = state.env->ps.current;
1433
break;
1434
case INDEX('.','r'):
1435
n = 0;
1436
break;
1437
case INDEX('.','s'):
1438
if (name[3] == 'r')
1439
n = state.env->ps.previous;
1440
else
1441
n = state.env->ps.current;
1442
break;
1443
case INDEX('.','t'):
1444
n = state.t;
1445
break;
1446
case INDEX('.','u'):
1447
n = !state.env->nf;
1448
break;
1449
case INDEX('.','v'):
1450
if (name[3] == 'p')
1451
n = 1;
1452
else
1453
n = 6 * state.env->vs.current;
1454
break;
1455
case INDEX('.','w'):
1456
if (name[3] == 'a')
1457
n = 0;
1458
else
1459
n = 6 * state.env->ps.current;
1460
break;
1461
case INDEX('.','x'):
1462
n = 19980401;
1463
break;
1464
case INDEX('.','y'):
1465
n = 1;
1466
break;
1467
case INDEX('.','z'):
1468
return state.divert ? state.divert->tag->name : "";
1469
case INDEX('d','l'):
1470
if (state.test & 0x1000) n = 0; else
1471
n = state.dl;
1472
break;
1473
case INDEX('d','n'):
1474
if (state.test & 0x2000) n = 0; else
1475
n = state.dn;
1476
break;
1477
case INDEX('l','n'):
1478
if (state.test & 0x4000) n = 0; else
1479
n = state.ln;
1480
break;
1481
case INDEX('n','l'):
1482
if (!(state.test & 0x8000)) n = 0; else
1483
n = state.nl;
1484
break;
1485
default:
1486
n = 0;
1487
break;
1488
}
1489
np->number = n;
1490
}
1491
if (i)
1492
{
1493
switch (i)
1494
{
1495
case '+':
1496
np->number += np->increment;
1497
break;
1498
case '-':
1499
np->number -= np->increment;
1500
break;
1501
}
1502
if (np->flags & TAG_TRACE_SET)
1503
error(2, "set register %s %d %d", np->name + 1, np->number, np->increment);
1504
}
1505
n = np->number;
1506
b = np->value;
1507
switch (np->format)
1508
{
1509
case '1':
1510
sfsprintf(b, sizeof(np->value), "%ld", np->number);
1511
break;
1512
case 'A':
1513
case 'a':
1514
x = islower(np->format) ? "abcdefghijklmnopqrstuvwxyz" : "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1515
if (n < 0)
1516
{
1517
n = -n;
1518
*b++ = '-';
1519
}
1520
for (m = 1; m > 0 && m < n; m *= 26);
1521
if (m <= 0)
1522
sfsprintf(b, sizeof(buf) - 1, "<%ld>", n);
1523
else
1524
{
1525
for (; m > 0; m /= 26)
1526
{
1527
i = n / m;
1528
n -= m * i;
1529
*b++ = x[i];
1530
}
1531
*b = 0;
1532
}
1533
break;
1534
case 'I':
1535
case 'i':
1536
x = islower(np->format) ? "zwmdclxvi" : "ZWMDCLXVI";
1537
if (n <= -40000 || n >= 40000)
1538
sfsprintf(b, sizeof(buf), "<%ld>", n);
1539
else if (!n)
1540
sfsprintf(b, sizeof(buf), "0");
1541
else
1542
{
1543
if (n < 0)
1544
{
1545
n = -n;
1546
*b++ = '-';
1547
}
1548
while (n >= 10000)
1549
{
1550
n -= 10000;
1551
*b++ = x[0];
1552
}
1553
for (i = 1000; i > 0; i /= 10, x += 2)
1554
{
1555
m = n / i;
1556
n -= m * i;
1557
switch (m)
1558
{
1559
case 9:
1560
*b++ = x[2];
1561
*b++ = x[0];
1562
break;
1563
case 8:
1564
*b++ = x[1];
1565
*b++ = x[2];
1566
*b++ = x[2];
1567
*b++ = x[2];
1568
break;
1569
case 7:
1570
*b++ = x[1];
1571
*b++ = x[2];
1572
*b++ = x[2];
1573
break;
1574
case 6:
1575
*b++ = x[1];
1576
*b++ = x[2];
1577
break;
1578
case 5:
1579
*b++ = x[1];
1580
break;
1581
case 4:
1582
*b++ = x[2];
1583
*b++ = x[1];
1584
break;
1585
case 3:
1586
*b++ = x[2];
1587
/*FALLTHROUGH*/
1588
case 2:
1589
*b++ = x[2];
1590
/*FALLTHROUGH*/
1591
case 1:
1592
*b++ = x[2];
1593
break;
1594
}
1595
}
1596
*b = 0;
1597
}
1598
break;
1599
default:
1600
sfsprintf(b, sizeof(np->value), "%0*ld", np->format, np->number);
1601
break;
1602
}
1603
return np->value;
1604
}
1605
1606
/*
1607
* return the value for typed register
1608
*/
1609
1610
static char*
1611
interpolate(int type)
1612
{
1613
register int c;
1614
register char* b;
1615
register char* x;
1616
register char* t;
1617
int i;
1618
int k;
1619
char buf[MAXNAME];
1620
1621
i = k = 0;
1622
b = buf;
1623
*b++ = (type == 'g' || type == 'j') ? 'f' : type;
1624
x = buf + 2;
1625
do
1626
{
1627
switch (c = GETC())
1628
{
1629
case EOF:
1630
if (popin())
1631
return 0;
1632
continue;
1633
case '\n':
1634
error_info.line++;
1635
break;
1636
case '(':
1637
if (x == buf + 2)
1638
{
1639
x++;
1640
continue;
1641
}
1642
break;
1643
case '[':
1644
if (state.groff && x == buf + 2)
1645
{
1646
x = buf + sizeof(buf) - 2;
1647
k = 1;
1648
continue;
1649
}
1650
break;
1651
case ']':
1652
if (k)
1653
goto done;
1654
break;
1655
case '-':
1656
case '+':
1657
if (type == 'n' && b == buf + 1)
1658
{
1659
i = c;
1660
continue;
1661
}
1662
break;
1663
default:
1664
if (c == state.ec)
1665
{
1666
escape:
1667
switch (c = GETC())
1668
{
1669
case EOF:
1670
if (popin())
1671
return 0;
1672
goto escape;
1673
case '\n':
1674
error_info.line++;
1675
continue;
1676
case '*':
1677
c = '.';
1678
/*FALLTHROUGH*/
1679
case 'n':
1680
if (t = interpolate(c))
1681
while (b < x && (*b = *t++))
1682
b++;
1683
continue;
1684
}
1685
}
1686
break;
1687
}
1688
*b++ = c;
1689
} while (b < x);
1690
done:
1691
*b = 0;
1692
return value(buf, type, i);
1693
}
1694
1695
/*
1696
* switch to font s
1697
*/
1698
1699
static void
1700
ft(char* s)
1701
{
1702
int c;
1703
1704
if (!s || (c = *s) < '0' || c > '5')
1705
c = 0;
1706
else
1707
c -= '0';
1708
if (!c)
1709
c = state.env->ft.previous;
1710
state.env->ft.previous = state.env->ft.current;
1711
state.env->ft.current = c;
1712
code_1(OP_ft + c);
1713
}
1714
1715
/*
1716
* switch to point size n
1717
*/
1718
1719
static void
1720
ps(int n)
1721
{
1722
state.env->ps.previous = state.env->ps.current;
1723
state.env->ps.current = n;
1724
code_2(OP_ps, n);
1725
}
1726
1727
/*
1728
* sync list state
1729
*/
1730
1731
static void
1732
li(int force)
1733
{
1734
List_t* list = state.list;
1735
1736
state.it.dc = 0;
1737
if (state.divert || state.env->nf)
1738
{
1739
if (state.env->nf)
1740
state.it.dl = 0;
1741
return;
1742
}
1743
if (force)
1744
{
1745
while (state.env->in.current < state.list->in)
1746
{
1747
message((-2, "DROP %d %d %d:%d %d:%d", state.list - state.list_stack, state.list->dl, state.list->ti, state.list->in, force ? state.env->ti.current : 0, state.env->in.current));
1748
if (state.list->dl)
1749
code_1(END(OP_dl));
1750
state.list--;
1751
}
1752
}
1753
else
1754
message((-2, "TEST %d %d %d:%d %d:%d ops=%d df=%d dc=%d dl=%d", state.list - state.list_stack, state.list->dl, state.list->ti, state.list->in, force ? state.env->ti.current : 0, state.env->in.current, state.list - list, force, state.it.dc, state.it.dl));
1755
if (state.env->in.current > state.list->in || force > 0 && state.env->ti.current > state.list->in)
1756
{
1757
if (state.list >= &state.list_stack[elementsof(state.list_stack)-1])
1758
error(2, "list stack overflow");
1759
else
1760
state.list++;
1761
state.list->dl = 0;
1762
state.list->in = state.env->in.current;
1763
state.list->ti = state.env->in.current > state.list->in ? state.env->in.current : state.env->ti.current;
1764
state.it.dl++;
1765
}
1766
else if (!force && state.env->in.current < state.list->in)
1767
state.it.dc = 1;
1768
message((-2, "LIST %d %d %d:%d %d:%d ops=%d df=%d dc=%d dl=%d", state.list - state.list_stack, state.list->dl, state.list->ti, state.list->in, force ? state.env->ti.current : 0, state.env->in.current, state.list - list, force, state.it.dc, state.it.dl));
1769
}
1770
1771
/*
1772
* pop inline traps
1773
*/
1774
1775
static void
1776
it(void)
1777
{
1778
register List_t* p;
1779
1780
if (state.env->nf)
1781
{
1782
if (state.env->in.current > 0)
1783
sfputc(state.out, '\t');
1784
}
1785
else if (!DIVERTED())
1786
{
1787
if (state.it.dc && ISFILE())
1788
li(-1);
1789
p = state.list;
1790
while (state.it.dl > 0)
1791
{
1792
state.it.dl--;
1793
for (; p >= state.list_stack; p--)
1794
if (!p->dl)
1795
{
1796
p->dl = 1;
1797
code_1(OP_dl);
1798
break;
1799
}
1800
}
1801
if (state.it.dt)
1802
{
1803
state.it.dt = 0;
1804
code_1((state.list - state.list_stack) <= 1 ? LABEL(OP_dt) : OP_dt);
1805
state.it.dd = 1;
1806
}
1807
else if (state.it.dd)
1808
{
1809
state.it.dd = 0;
1810
code_1(OP_dd);
1811
}
1812
}
1813
}
1814
1815
/*
1816
* initialize number register
1817
*/
1818
1819
static void
1820
nr(char* s, int v, int format, int internal)
1821
{
1822
register Num_t* np;
1823
1824
np = num(s);
1825
np->number = v;
1826
np->format = format;
1827
if (internal > 0)
1828
np->internal = internal;
1829
}
1830
1831
/*
1832
* set time vars to t
1833
*/
1834
1835
static void
1836
tm(time_t t)
1837
{
1838
Tm_t* xp;
1839
1840
state.date = t;
1841
xp = tmmake(&t);
1842
nr("dw", xp->tm_wday + 1, 0, 0);
1843
nr("dy", xp->tm_mday, 0, 0);
1844
nr("mo", xp->tm_mon + 1, 0, 0);
1845
nr("yr", xp->tm_year % 100, 2, 0);
1846
nr("YR", 1900 + xp->tm_year, 4, 0);
1847
}
1848
1849
/*
1850
* expand if expression s into op
1851
*/
1852
1853
static void
1854
expand(register Sfio_t* op, register char* s)
1855
{
1856
int c;
1857
int d;
1858
int q;
1859
int v;
1860
int w;
1861
int z;
1862
char* t;
1863
char* b;
1864
char buf[MAXNAME];
1865
1866
for (;;)
1867
{
1868
switch (c = *s++)
1869
{
1870
case 0:
1871
break;
1872
case CODE_2:
1873
sfputc(op, c);
1874
c = *s++;
1875
/*FALLTHROUGH*/
1876
case CODE_1:
1877
sfputc(op, c);
1878
c = *s++;
1879
/*FALLTHROUGH*/
1880
case CODE_0:
1881
sfputc(op, c);
1882
continue;
1883
default:
1884
if (c == state.ec)
1885
switch (w = c = *s++)
1886
{
1887
case 0:
1888
s--;
1889
break;
1890
case 'A':
1891
case 'C':
1892
case 'L':
1893
case 'N':
1894
case 'R':
1895
case 'V':
1896
case 'X':
1897
case 'Y':
1898
case 'Z':
1899
case 'b':
1900
case 'h':
1901
case 'l':
1902
case 'o':
1903
case 'w':
1904
case 'x':
1905
v = 0;
1906
c = *s++;
1907
if (c == '(')
1908
z = 2;
1909
else
1910
{
1911
z = -1;
1912
if (state.groff && c == '[')
1913
c = ']';
1914
}
1915
b = s;
1916
for (;;)
1917
{
1918
switch (d = *s++)
1919
{
1920
case 0:
1921
s--;
1922
break;
1923
case CODE_2:
1924
s++;
1925
/*FALLTHROUGH*/
1926
case CODE_1:
1927
if (*s++ == OP_cc)
1928
v++;
1929
/*FALLTHROUGH*/
1930
case CODE_0:
1931
continue;
1932
default:
1933
if (d == state.ec)
1934
{
1935
switch (d = *s++)
1936
{
1937
case 0:
1938
s--;
1939
break;
1940
case 'A':
1941
case 'C':
1942
case 'L':
1943
case 'N':
1944
case 'R':
1945
case 'V':
1946
case 'X':
1947
case 'Y':
1948
case 'Z':
1949
case 'b':
1950
case 'h':
1951
case 'l':
1952
case 'o':
1953
case 'w':
1954
case 'x':
1955
q = *s++;
1956
for (;;)
1957
{
1958
switch (d = *s++)
1959
{
1960
case 0:
1961
s--;
1962
break;
1963
case CODE_2:
1964
s++;
1965
/*FALLTHROUGH*/
1966
case CODE_1:
1967
s++;
1968
/*FALLTHROUGH*/
1969
case CODE_0:
1970
continue;
1971
default:
1972
if (d == q)
1973
break;
1974
continue;
1975
}
1976
break;
1977
}
1978
v++;
1979
break;
1980
case 'f':
1981
case 's':
1982
if (*s)
1983
s++;
1984
break;
1985
case 'g':
1986
case 'j':
1987
t = buf;
1988
*t++ = 'n';
1989
if ((*t++ = *s++) == '(')
1990
{
1991
*(t - 1) = *s++;
1992
*t++ = *s++;
1993
}
1994
*t = 0;
1995
if (t = value(buf, d, 0))
1996
v += strlen(t);
1997
break;
1998
}
1999
}
2000
else if (z > 0)
2001
z--;
2002
else if (d == c || z == 0)
2003
break;
2004
v++;
2005
continue;
2006
}
2007
break;
2008
}
2009
switch (w)
2010
{
2011
case 'A':
2012
sfprintf(op, "1");
2013
break;
2014
case 'R':
2015
case 'X':
2016
case 'V':
2017
c = *(s - 1);
2018
*(s - 1) = 0;
2019
switch (w)
2020
{
2021
case 'R':
2022
/*HERE*/
2023
break;
2024
case 'V':
2025
if (t = getenv(b))
2026
sfprintf(op, "%s", t);
2027
break;
2028
case 'X':
2029
/*HERE*/
2030
break;
2031
}
2032
*(s - 1) = c;
2033
break;
2034
case 'w':
2035
sfprintf(op, "%d", convert(v, 1, w));
2036
break;
2037
}
2038
break;
2039
case 'g':
2040
case 'j':
2041
t = buf;
2042
*t++ = 'n';
2043
if ((*t++ = *s++) == '(')
2044
{
2045
*(t - 1) = *s++;
2046
*t++ = *s++;
2047
}
2048
*t = 0;
2049
if (t = value(buf, c, 0))
2050
sfputr(op, t, -1);
2051
break;
2052
default:
2053
sfputc(op, state.ec);
2054
sfputc(op, c);
2055
break;
2056
}
2057
else
2058
sfputc(op, c);
2059
continue;
2060
}
2061
break;
2062
}
2063
}
2064
2065
/*
2066
* remove macro/register s
2067
*/
2068
2069
static void
2070
rm(char* s)
2071
{
2072
register Tag_t* mp;
2073
char buf[MAXNAME];
2074
2075
if (mp = (Tag_t*)hashget(state.symbols, nam('.', s, buf, sizeof(buf))))
2076
{
2077
if (mp->flags & TAG_TRACE_SET)
2078
error(2, "remove macro %s", mp->name + 1);
2079
if (mp->size)
2080
free(mp->body);
2081
if (!(mp->flags & TAG_STATIC))
2082
free(mp);
2083
hashput(state.symbols, NiL, NiL);
2084
}
2085
if (mp = (Tag_t*)hashget(state.symbols, nam('n', s, buf, sizeof(buf))))
2086
{
2087
if (mp->flags & TAG_TRACE_SET)
2088
error(2, "remove register %s", mp->name + 1);
2089
if (mp->size)
2090
free(mp->body);
2091
if (!(mp->flags & TAG_STATIC))
2092
free(mp);
2093
hashput(state.symbols, NiL, NiL);
2094
}
2095
}
2096
2097
/*
2098
* skip one conditional block
2099
* if op!=0 then copy block data to s
2100
*/
2101
2102
static void
2103
skip(char* s, int f, register Sfio_t* op)
2104
{
2105
register int c;
2106
register int n;
2107
2108
n = (f & COND_BLOCK) != 0;
2109
if (s)
2110
pushin(NiL, 0, NiL, s, NiL);
2111
else if (!n)
2112
return;
2113
for (;;)
2114
{
2115
switch (c = GETC())
2116
{
2117
case EOF:
2118
if (s && n <= 0)
2119
break;
2120
if (popin())
2121
break;
2122
continue;
2123
case CODE_2:
2124
if (op)
2125
sfputc(op, c);
2126
c = GETC();
2127
/*FALLTHROUGH*/
2128
case CODE_1:
2129
if (op)
2130
sfputc(op, c);
2131
c = GETC();
2132
/*FALLTHROUGH*/
2133
case CODE_0:
2134
if (op)
2135
sfputc(op, c);
2136
continue;
2137
case '\n':
2138
if (op)
2139
sfputc(op, c);
2140
error_info.line++;
2141
if (n <= 0)
2142
break;
2143
continue;
2144
default:
2145
if (op)
2146
sfputc(op, c);
2147
if (c == state.ec)
2148
{
2149
escape:
2150
switch (c = GETC())
2151
{
2152
case EOF:
2153
if (popin())
2154
break;
2155
goto escape;
2156
case '\n':
2157
if (op)
2158
sfputc(op, c);
2159
error_info.line++;
2160
continue;
2161
case '{':
2162
if (op)
2163
sfputc(op, c);
2164
n++;
2165
continue;
2166
case '}':
2167
if (op)
2168
sfputc(op, c);
2169
if (--n <= 0)
2170
{
2171
switch (c = nextchar())
2172
{
2173
case '\n':
2174
if (op)
2175
sfputc(op, c);
2176
error_info.line++;
2177
break;
2178
default:
2179
UNGETC(c);
2180
break;
2181
}
2182
break;
2183
}
2184
continue;
2185
default:
2186
if (op)
2187
sfputc(op, c);
2188
continue;
2189
}
2190
break;
2191
}
2192
continue;
2193
}
2194
break;
2195
}
2196
if (n > 0 && state.verbose)
2197
error(2, "unbalanced \\{ ... \\} block");
2198
}
2199
2200
/*
2201
* internal groff requests
2202
*/
2203
2204
static void
2205
groff_aln(Tag_t* tp, Arg_t* ap)
2206
{
2207
Tag_t* xp;
2208
char buf[MAXNAME];
2209
2210
if (ap->argc != 2)
2211
error(1, "%s: two arguments expected", ap->argv[0]);
2212
else if (!(xp = (Tag_t*)hashget(state.symbols, nam('n', ap->argv[2], buf, sizeof(buf)))))
2213
error(1, "%s: not defined", buf);
2214
else
2215
hashput(state.symbols, nam('n', ap->argv[1], buf, sizeof(buf)), xp);
2216
}
2217
2218
static void
2219
groff_als(Tag_t* tp, Arg_t* ap)
2220
{
2221
Tag_t* xp;
2222
char buf[MAXNAME];
2223
2224
if (ap->argc != 2)
2225
error(1, "%s: two arguments expected", ap->argv[0]);
2226
else if (!(xp = (Tag_t*)hashget(state.symbols, nam('.', ap->argv[2], buf, sizeof(buf)))))
2227
error(1, "%s: not defined", buf);
2228
else
2229
hashput(state.symbols, nam('.', ap->argv[1], buf, sizeof(buf)), xp);
2230
}
2231
2232
static void
2233
groff_asciify(Tag_t* tp, Arg_t* ap)
2234
{
2235
static int warned;
2236
2237
if (!warned++)
2238
error(1, "%s: not implemented yet", tp->name);
2239
}
2240
2241
static void
2242
groff_break(Tag_t* tp, Arg_t* ap)
2243
{
2244
register Pushin_t* pp;
2245
2246
for (pp = state.in_top; pp >= state.in_stack; pp--)
2247
if (pp->loop)
2248
{
2249
sfclose(pp->loop);
2250
pp->loop = 0;
2251
return;
2252
}
2253
error(1, "%s: outside of loop", tp->name);
2254
}
2255
2256
static void
2257
groff_chop(Tag_t* tp, Arg_t* ap)
2258
{
2259
register char* s;
2260
register Tag_t* mp;
2261
2262
if (ap->argc >= 1)
2263
{
2264
mp = mac(ap->argv[1]);
2265
if ((s = mp->body) && *s)
2266
*(s + strlen(s) - 1) = 0;
2267
}
2268
}
2269
2270
static void
2271
groff_close(Tag_t* tp, Arg_t* ap)
2272
{
2273
Stream_t* sp;
2274
2275
if (ap->argc < 1)
2276
{
2277
error(1, "%s: one argument expected", ap->argv[0]);
2278
return;
2279
}
2280
if (!(sp = iop(ap->argv[1], 0)))
2281
{
2282
error(1, "%s: %s: stream not open", ap->argv[0], ap->argv[1]);
2283
return;
2284
}
2285
sfclose(sp->sp);
2286
sp->sp = 0;
2287
}
2288
2289
static void
2290
groff_continue(Tag_t* tp, Arg_t* ap)
2291
{
2292
register Pushin_t* pp;
2293
2294
for (pp = state.in_top; pp >= state.in_stack; pp--)
2295
if (pp->loop)
2296
{
2297
pp->in = (unsigned char*)sfstrbase(pp->loop);
2298
return;
2299
}
2300
error(1, "%s: outside of loop", tp->name);
2301
}
2302
2303
static void
2304
groff_cp(Tag_t* tp, Arg_t* ap)
2305
{
2306
NoP(tp);
2307
if (ap->argc < 1 || expression(ap->argv[1], NiL, 0))
2308
state.groff &= 2;
2309
else
2310
state.groff |= 1;
2311
}
2312
2313
static void
2314
groff_open(Tag_t* tp, Arg_t* ap)
2315
{
2316
char* path;
2317
char* mode;
2318
Stream_t* sp;
2319
2320
if (ap->argc < 2)
2321
{
2322
error(1, "%s: two arguments expected", ap->argv[0]);
2323
return;
2324
}
2325
mode = strlen(ap->argv[0]) > 5 ? (ap->argv[0] + 5) : "w";
2326
path = ap->argv[2];
2327
sp = iop(ap->argv[1], 1);
2328
if (*mode == 'w' || !streq(sp->path, path))
2329
{
2330
if (sp->sp)
2331
{
2332
sfclose(sp->sp);
2333
sp->sp = 0;
2334
}
2335
if (sp->path)
2336
free(sp->path);
2337
if (!(sp->path = strdup(path)))
2338
error(ERROR_SYSTEM|3, "out of space [stream path]");
2339
}
2340
if (!sp->sp && !(sp->sp = sfopen(NiL, path, mode)))
2341
error(ERROR_SYSTEM|2, "%s: %s: \"%s\" mode open error", ap->argv[0], path, mode);
2342
}
2343
2344
static void
2345
groff_pso(Tag_t* tp, Arg_t* ap)
2346
{
2347
char* s;
2348
Sfio_t* sp;
2349
2350
if (s = join(ap, 1))
2351
{
2352
if (!(sp = sfpopen(NiL, s, "r")))
2353
error(ERROR_SYSTEM|2, "%s: %s: command error", ap->argv[0], s);
2354
else
2355
pushin(NiL, 0, sp, NiL, NiL);
2356
}
2357
}
2358
2359
static void
2360
groff_rnn(Tag_t* tp, Arg_t* ap)
2361
{
2362
Num_t* mp;
2363
char* s;
2364
2365
NoP(tp);
2366
if (ap->argc >= 2)
2367
{
2368
s = ap->argv[2];
2369
rm(s);
2370
*--s = 'n';
2371
mp = num(ap->argv[1]);
2372
hashput(state.symbols, NiL, NiL);
2373
mp->name = hashput(state.symbols, s, mp);
2374
}
2375
}
2376
2377
static void
2378
groff_shift(Tag_t* tp, Arg_t* ap)
2379
{
2380
register int n;
2381
register int i;
2382
register Arg_t* pp;
2383
2384
if (pp = state.mac)
2385
{
2386
n = (ap->argc >= 1) ? expression(ap->argv[1], NiL, 0) : 1;
2387
i = 0;
2388
while (n < pp->argc)
2389
pp->argv[++i] = pp->argv[++n];
2390
pp->argc = i;
2391
}
2392
}
2393
2394
static void
2395
groff_sy(Tag_t* tp, Arg_t* ap)
2396
{
2397
char* s;
2398
2399
nr("systat", (long)((s = join(ap, 1)) ? system(s) : 0), 0, 0);
2400
}
2401
2402
static void
2403
groff_while(Tag_t* tp, Arg_t* ap)
2404
{
2405
register char* s;
2406
register char* t;
2407
Sfio_t* op;
2408
2409
if (s = join(ap, 1))
2410
{
2411
if (!(op = sfstropen()))
2412
error(ERROR_SYSTEM|3, "out of space [%s]", tp->name);
2413
sfputr(op, ".ie", ' ');
2414
sfputr(op, s, '\n');
2415
t = s + strlen(s);
2416
while (t > s && isspace(*--t));
2417
if (t > s && *t == '{' && *(t - 1) == state.ec)
2418
skip(NiL, COND_BLOCK, op);
2419
sfputr(op, "\n.el .break\n", 0);
2420
pushin(NiL, 0, NiL, sfstrbase(op), NiL);
2421
state.in_top->loop = op;
2422
}
2423
}
2424
2425
static void
2426
groff_write(Tag_t* tp, Arg_t* ap)
2427
{
2428
char* s;
2429
Stream_t* sp;
2430
2431
if (ap->argc < 1)
2432
{
2433
error(1, "%s: at least one argument expected", ap->argv[0]);
2434
return;
2435
}
2436
if (!(sp = iop(ap->argv[1], 0)))
2437
{
2438
error(1, "%s: %s: stream not open", ap->argv[0], ap->argv[1]);
2439
return;
2440
}
2441
if (!(s = join(ap, 2)))
2442
s = "";
2443
if (*s == '"')
2444
s++;
2445
if (sfputr(sp->sp, s, '\n') < 0)
2446
error(ERROR_SYSTEM|2, "%s: %s: write error", ap->argv[0], ap->argv[1]);
2447
}
2448
2449
/*
2450
* internal troff requests
2451
*/
2452
2453
static void
2454
troff_ab(Tag_t* tp, Arg_t* ap)
2455
{
2456
char* s;
2457
2458
NoP(tp);
2459
if (!(s = join(ap, 1)))
2460
s = "User abort";
2461
notify(s);
2462
exit(1);
2463
}
2464
2465
static void
2466
troff_ad(Tag_t* tp, Arg_t* ap)
2467
{
2468
NoP(tp);
2469
if (ap->argc >= 1)
2470
switch (ap->argv[1][0])
2471
{
2472
case 'b':
2473
case 'B':
2474
case 'n':
2475
case 'N':
2476
break;
2477
case 'c':
2478
case 'C':
2479
break;
2480
case 'l':
2481
case 'L':
2482
break;
2483
case 'r':
2484
case 'R':
2485
break;
2486
}
2487
}
2488
2489
static void
2490
troff_af(Tag_t* tp, Arg_t* ap)
2491
{
2492
Num_t* np;
2493
char* s;
2494
2495
NoP(tp);
2496
if (ap->argc >= 2)
2497
{
2498
np = num(ap->argv[1]);
2499
switch (*(s = ap->argv[2]))
2500
{
2501
case '0':
2502
for (np->format = 0; *s++ == '0'; np->format++);
2503
break;
2504
case 'a':
2505
case 'A':
2506
case 'i':
2507
case 'I':
2508
np->format = *s;
2509
break;
2510
case '1':
2511
default:
2512
np->format = '1';
2513
break;
2514
}
2515
}
2516
}
2517
2518
static void
2519
troff_br(Tag_t* tp, Arg_t* ap)
2520
{
2521
NoP(tp);
2522
NoP(ap);
2523
if (state.it.dd)
2524
{
2525
state.it.dd = 0;
2526
code_1(OP_dd);
2527
}
2528
code_1(OP_br);
2529
}
2530
2531
static void
2532
troff_c2(Tag_t* tp, Arg_t* ap)
2533
{
2534
NoP(tp);
2535
state.env->c2 = (ap->argc >= 1) ? ap->argv[1][0] : DEFAULT_c2;
2536
}
2537
2538
static void
2539
troff_cc(Tag_t* tp, Arg_t* ap)
2540
{
2541
NoP(tp);
2542
state.env->cc = (ap->argc >= 1) ? ap->argv[1][0] : DEFAULT_cc;
2543
}
2544
2545
static void
2546
troff_ce(Tag_t* tp, Arg_t* ap)
2547
{
2548
int n;
2549
int r;
2550
2551
NoP(tp);
2552
n = (ap->argc >= 1) ? expression(ap->argv[1], NiL, 0) : 1;
2553
r = *(ap->argv[0] + 1) == 'r';
2554
if (n >= 1)
2555
{
2556
if (state.it.center && state.it.right != r)
2557
{
2558
state.it.center = 0;
2559
code_1(state.it.right ? END(OP_p) : END(OP_center));
2560
}
2561
if (!state.it.center)
2562
{
2563
if (r)
2564
code_2(OP_p, ARG_right);
2565
else
2566
code_1(OP_center);
2567
}
2568
}
2569
else
2570
{
2571
if (state.it.right)
2572
code_1(END(OP_p));
2573
else if (state.it.center)
2574
code_1(END(OP_center));
2575
n = 0;
2576
r = 0;
2577
}
2578
state.it.center = n;
2579
state.it.right = r;
2580
}
2581
2582
static void
2583
troff_cf(Tag_t* tp, Arg_t* ap)
2584
{
2585
Sfio_t* sp;
2586
2587
if (ap->argc >= 1 && (sp = find(ap->argv[1], NiL, 1)))
2588
{
2589
sfmove(sp, state.out, SF_UNBOUND, -1);
2590
sfclose(sp);
2591
}
2592
}
2593
2594
static void troff_wh(Tag_t*, Arg_t*);
2595
2596
static void
2597
troff_ch(Tag_t* tp, Arg_t* ap)
2598
{
2599
char* s;
2600
Trap_t* xp;
2601
2602
NoP(tp);
2603
NoP(ap);
2604
if (ap->argc == 1)
2605
{
2606
for (xp = state.trap; xp; xp = xp->next)
2607
if (streq(xp->name + 1, ap->argv[1]))
2608
xp->name[0] = 0;
2609
}
2610
else if (ap->argc >= 2)
2611
{
2612
s = ap->argv[1];
2613
ap->argv[1] = (state.test & 1) ? "1" : ap->argv[2];
2614
ap->argv[2] = s;
2615
troff_wh(tp, ap);
2616
}
2617
}
2618
2619
static void
2620
troff_de(Tag_t* tp, Arg_t* ap)
2621
{
2622
register Tag_t* mp;
2623
2624
if (ap->argc >= 1)
2625
{
2626
mp = mac(ap->argv[1]);
2627
if (mp->body && tp->name[1] == 'a')
2628
sfputr(state.tmp, mp->body, -1);
2629
mp->file = error_info.file;
2630
mp->line = error_info.line;
2631
mp->flags |= TAG_PASS;
2632
mp->call = macro;
2633
state.define = mp;
2634
state.end = (Tag_t*)hashget(state.symbols, "..");
2635
state.pass = 1;
2636
pushout(state.tmp);
2637
}
2638
}
2639
2640
static void
2641
troff_di(Tag_t* tp, Arg_t* ap)
2642
{
2643
Divert_t* dp;
2644
int n;
2645
2646
if (!ap->argc)
2647
{
2648
if (dp = state.divert)
2649
{
2650
state.dl = state.env->dl;
2651
state.dn = state.env->dn;
2652
if (dp->env->ft.current != state.env->ft.current)
2653
code_1(OP_ft + dp->env->ft.current);
2654
if (dp->env->ps.current != state.env->ps.current)
2655
code_2(OP_ps, dp->env->ps.current);
2656
state.env = dp->env;
2657
state.pass = 0;
2658
state.divert = dp->next;
2659
if ((n = sfstrtell(state.out)) > 0 && *(sfstrbase(state.out) + n - 1) != '\n')
2660
sfputc(state.out, '\n');
2661
set(dp->tag, popout(), 0);
2662
message((-2, "%s: pop diversion `%s'", dp->tag->name, dp->tag->body));
2663
sfstrclose(dp->sp);
2664
free(dp);
2665
}
2666
}
2667
else if (!(dp = newof(0, Divert_t, 1, 0)) || !(dp->sp = sfstropen()))
2668
error(ERROR_SYSTEM|3, "out of space [divert]");
2669
else
2670
{
2671
dp->tag = mac(ap->argv[1]);
2672
if (dp->tag->body && tp->name[2] == 'a')
2673
sfputr(dp->sp, dp->tag->body, -1);
2674
dp->next = state.divert;
2675
state.divert = dp;
2676
state.pass = 1;
2677
pushout(dp->sp);
2678
dp->env = state.env;
2679
dp->environment = *state.env;
2680
state.env = &dp->environment;
2681
state.env->dl = 0;
2682
state.env->dn = 0;
2683
}
2684
}
2685
2686
static void
2687
troff_ds(Tag_t* tp, Arg_t* ap)
2688
{
2689
char* v;
2690
2691
if (v = join(ap, 2))
2692
set(mac(ap->argv[1]), v, tp->name[1] == 'a');
2693
}
2694
2695
static void
2696
troff_ec(Tag_t* tp, Arg_t* ap)
2697
{
2698
NoP(tp);
2699
state.ec = state.eo = (ap->argc >= 1) ? ap->argv[1][0] : DEFAULT_ec;
2700
}
2701
2702
static void
2703
troff_eo(Tag_t* tp, Arg_t* ap)
2704
{
2705
NoP(tp);
2706
if (state.ec)
2707
{
2708
state.eo = state.ec;
2709
state.ec = 0;
2710
}
2711
}
2712
2713
static void
2714
troff_ev(Tag_t* tp, Arg_t* ap)
2715
{
2716
register Env_t* oe;
2717
register Env_t* ne;
2718
2719
if (ap->argc < 1)
2720
{
2721
if (state.env_sp <= state.env_stack)
2722
error(2, "%s: stack underflow", tp->name);
2723
else
2724
state.env_sp--;
2725
}
2726
else if (state.env_sp >= &state.env_stack[elementsof(state.env_stack)])
2727
error(2, "%s: stack overflow", tp->name);
2728
else
2729
*++state.env_sp = env(ap->argv[1]);
2730
oe = state.env;
2731
ne = state.env = *state.env_sp;
2732
if (oe != ne)
2733
{
2734
if (oe->ft.current != ne->ft.current)
2735
code_1(OP_ft + ne->ft.current);
2736
if (oe->nf != ne->nf)
2737
code_1(ne->nf ? OP_pre : END(OP_pre));
2738
if (oe->ps.current != ne->ps.current)
2739
code_2(OP_ps, ne->ps.current);
2740
}
2741
}
2742
2743
static void
2744
troff_em(Tag_t* tp, Arg_t* ap)
2745
{
2746
int i;
2747
2748
NoP(tp);
2749
for (i = 1; i <= ap->argc; i++)
2750
trap(&state.fini, ap->argv[i]);
2751
}
2752
2753
static void
2754
troff_fi(Tag_t* tp, Arg_t* ap)
2755
{
2756
NoP(tp);
2757
NoP(ap);
2758
if (state.env->nf)
2759
{
2760
state.env->nf = 0;
2761
code_1(END(OP_pre));
2762
}
2763
}
2764
2765
static void
2766
troff_fp(Tag_t* tp, Arg_t* ap)
2767
{
2768
char* s;
2769
char* t;
2770
2771
NoP(tp);
2772
if (ap->argc >= 2)
2773
{
2774
s = ap->argv[1];
2775
*--s = 'f';
2776
t = ap->argv[1];
2777
*--t = 'f';
2778
hashput(state.symbols, s, hashget(state.symbols, t));
2779
}
2780
}
2781
2782
static void
2783
troff_ft(Tag_t* tp, Arg_t* ap)
2784
{
2785
char* s;
2786
2787
NoP(tp);
2788
if (ap->argc >= 1)
2789
{
2790
s = ap->argv[1];
2791
*--s = 'f';
2792
s = (char*)hashget(state.symbols, s);
2793
}
2794
else s = 0;
2795
ft(s);
2796
}
2797
2798
static void
2799
troff_ie(Tag_t* tp, Arg_t* ap)
2800
{
2801
int f;
2802
int q;
2803
long v;
2804
char* s;
2805
char* t;
2806
char* e;
2807
2808
if (tp->name[1] == 'e')
2809
{
2810
if (!state.cond.level || !(state.cond.flags[state.cond.level] & COND_IE))
2811
{
2812
if (state.verbose)
2813
error(2, "%s: no matching .ie", tp->name);
2814
if (!state.cond.level)
2815
state.cond.level++;
2816
state.cond.flags[state.cond.level] = COND_IE|COND_KEPT;
2817
}
2818
f = COND_EL;
2819
}
2820
else
2821
{
2822
if (state.cond.level >= elementsof(state.cond.flags) - 1)
2823
error(3, "%s: conditional stack too deep", tp->name);
2824
f = tp->name[2] == 'f' ? COND_IF : COND_IE;
2825
}
2826
if (error_info.trace <= -5)
2827
{
2828
int g = state.cond.flags[state.cond.level];
2829
2830
error(-5, "IE +++ %s level=%d |%s%s%s%s%s", tp->name, state.cond.level, (g & COND_IF) ? "IF|" : "", (g & COND_IE) ? "IE|" : "", (g & COND_BLOCK) ? "BLOCK|" : "", (g & COND_SKIP) ? "SKIP|" : "", (g & COND_KEPT) ? "KEPT|" : "");
2831
}
2832
if (s = join(ap, 1))
2833
{
2834
if (state.ec)
2835
{
2836
t = s;
2837
while (t = strchr(t, state.ec))
2838
if (*++t != '{' && *t != '}')
2839
break;
2840
if (t)
2841
{
2842
if (state.test & 0x100)
2843
error(2, "EXPAND +++ `%s'", s);
2844
expand(state.arg, s);
2845
sfputr(ap->sp, use(state.arg), 0);
2846
s = use(ap->sp);
2847
if (state.test & 0x100)
2848
error(2, "EXPAND --- `%s'", s);
2849
}
2850
}
2851
for (;;)
2852
{
2853
if (f & COND_EL)
2854
v = !(state.cond.flags[state.cond.level] & COND_KEPT);
2855
else
2856
{
2857
v = cond(s, &e);
2858
s = e;
2859
}
2860
if (v || s[0] != state.env->cc && s[0] != state.env->c2 || s[1] != 'i')
2861
break;
2862
q = f;
2863
if (s[2] == 'e')
2864
f = COND_IE|COND_KEPT|COND_SKIP;
2865
else if (s[2] == 'f')
2866
f = COND_IF|COND_SKIP;
2867
else
2868
break;
2869
if (q & COND_EL)
2870
state.cond.level--;
2871
}
2872
if (*s == state.ec && state.ec && *(s + 1) == '{')
2873
{
2874
s += 2;
2875
f |= COND_BLOCK;
2876
}
2877
while (isspace(*s))
2878
s++;
2879
if (!*s)
2880
s = 0;
2881
else
2882
*(s + strlen(s)) = '\n';
2883
}
2884
if (!v)
2885
{
2886
f |= COND_SKIP;
2887
skip(s, f, NiL);
2888
}
2889
else
2890
{
2891
if (s)
2892
{
2893
pushin(NiL, 0, NiL, s, ap);
2894
error_info.line--;
2895
}
2896
if (f & (COND_EL|COND_IE))
2897
f |= COND_KEPT;
2898
}
2899
if (f & COND_EL)
2900
{
2901
if ((f & (COND_SKIP|COND_BLOCK)) == COND_BLOCK)
2902
state.cond.flags[state.cond.level] = COND_IE|COND_EL|COND_KEPT|COND_BLOCK;
2903
else
2904
state.cond.level--;
2905
}
2906
else if ((f & COND_IE) || (f & (COND_BLOCK|COND_SKIP)) == COND_BLOCK)
2907
state.cond.flags[++state.cond.level] = f;
2908
if (error_info.trace <= -5)
2909
{
2910
int g = state.cond.flags[state.cond.level];
2911
2912
error(-5, "IE --- %s level=%d |%s%s%s%s%s", tp->name, state.cond.level, (g & COND_IF) ? "IF|" : "", (g & COND_IE) ? "IE|" : "", (g & COND_BLOCK) ? "BLOCK|" : "", (g & COND_SKIP) ? "SKIP|" : "", (g & COND_KEPT) ? "KEPT|" : "");
2913
}
2914
}
2915
2916
static void
2917
troff_ignore(Tag_t* tp, Arg_t* ap)
2918
{
2919
char* s;
2920
2921
if (!state.end)
2922
{
2923
*tp->name = 'e';
2924
state.end = (s = (char*)hashget(state.symbols, tp->name)) ? (Tag_t*)hashget(state.symbols, s) : (Tag_t*)0;
2925
*tp->name = '.';
2926
if (state.end)
2927
{
2928
sfprintf(state.out, "<BR>%s ... %s OMITTED<BR>\n", tp->name, state.end->name);
2929
state.pass = 1;
2930
pushout(state.nul);
2931
}
2932
}
2933
}
2934
2935
static void
2936
troff_in(Tag_t* tp, Arg_t* ap)
2937
{
2938
int n;
2939
2940
NoP(tp);
2941
if (ap->argc < 1)
2942
n = state.env->in.previous;
2943
else
2944
{
2945
n = expression(ap->argv[1], NiL, 'u');
2946
switch (ap->argv[1][0])
2947
{
2948
case '-':
2949
case '+':
2950
n += state.env->in.current;
2951
break;
2952
}
2953
}
2954
state.env->in.previous = state.env->in.current;
2955
state.env->in.current = n;
2956
li(0);
2957
}
2958
2959
static void
2960
troff_it(Tag_t* tp, Arg_t* ap)
2961
{
2962
char* s;
2963
char* t;
2964
2965
NoP(tp);
2966
state.it.count = 0;
2967
if (ap->argc >= 2 && (state.it.count = expression(ap->argv[1], NiL, 0)) > 0 && *(s = ap->argv[2]))
2968
{
2969
t = state.it.trap;
2970
*t++ = '.';
2971
*t++ = *s++;
2972
if (*s)
2973
{
2974
*t++ = *s;
2975
if (state.groff)
2976
while (t < &state.it.trap[sizeof(state.it.trap)-2] && (*t = *++s))
2977
t++;
2978
}
2979
*t++ = '\n';
2980
*t = 0;
2981
}
2982
}
2983
2984
static void
2985
troff_ll(Tag_t* tp, Arg_t* ap)
2986
{
2987
int n;
2988
2989
NoP(tp);
2990
if (ap->argc < 1)
2991
n = state.env->ll.previous;
2992
else
2993
{
2994
n = expression(ap->argv[1], NiL, 'u');
2995
switch (ap->argv[1][0])
2996
{
2997
case '-':
2998
case '+':
2999
n += state.env->ll.current;
3000
break;
3001
}
3002
}
3003
state.env->ll.previous = state.env->ll.current;
3004
state.env->ll.current = n;
3005
li(0);
3006
}
3007
3008
static void
3009
troff_ne(Tag_t* tp, Arg_t* ap)
3010
{
3011
NoP(tp);
3012
state.t = (ap->argc < 1) ? 1 : INT_MAX;
3013
}
3014
3015
static void
3016
troff_nf(Tag_t* tp, Arg_t* ap)
3017
{
3018
NoP(tp);
3019
NoP(ap);
3020
if (!state.env->nf)
3021
{
3022
if (!state.it.interrupt)
3023
it();
3024
li(-1);
3025
state.env->nf = 1;
3026
code_1(OP_pre);
3027
}
3028
}
3029
3030
static void
3031
troff_nr(Tag_t* tp, Arg_t* ap)
3032
{
3033
int i;
3034
long n;
3035
char* s;
3036
Num_t* np;
3037
3038
NoP(tp);
3039
if (ap->argc >= 2)
3040
{
3041
np = num(ap->argv[1]);
3042
if ((i = *(s = ap->argv[2])) == '+' || i == '-')
3043
s++;
3044
n = expression(s, NiL, 0);
3045
switch (i)
3046
{
3047
case '-':
3048
n = np->number - n;
3049
break;
3050
case '+':
3051
n = np->number + n;
3052
break;
3053
}
3054
np->number = n;
3055
if (np->internal)
3056
switch (INDEX(np->name[1], np->name[2]))
3057
{
3058
case INDEX('c','.'):
3059
if (state.original.line)
3060
state.original.line = n;
3061
else
3062
error_info.line = n;
3063
break;
3064
case INDEX('d','l'):
3065
state.dl = n;
3066
break;
3067
case INDEX('d','n'):
3068
state.dn = n;
3069
break;
3070
case INDEX('l','n'):
3071
state.ln = n;
3072
break;
3073
case INDEX('n','l'):
3074
state.nl = n;
3075
break;
3076
}
3077
if (ap->argc >= 3)
3078
np->increment = expression(ap->argv[3], NiL, 0);
3079
if (np->flags & TAG_TRACE_SET)
3080
error(2, "set register %s %d %d", np->name + 1, np->number, np->increment);
3081
}
3082
}
3083
3084
static void
3085
troff_pc(Tag_t* tp, Arg_t* ap)
3086
{
3087
NoP(tp);
3088
state.pc = (ap->argc >= 1) ? ap->argv[1][0] : 0;
3089
}
3090
3091
static void
3092
troff_ps(Tag_t* tp, Arg_t* ap)
3093
{
3094
int n;
3095
3096
NoP(tp);
3097
if (ap->argc < 1)
3098
n = state.env->ps.previous;
3099
else
3100
{
3101
n = expression(ap->argv[1], NiL, 'p');
3102
switch (ap->argv[1][0])
3103
{
3104
case '-':
3105
case '+':
3106
n += state.env->ps.current;
3107
break;
3108
default:
3109
if (!n)
3110
n = state.env->ps.previous;
3111
break;
3112
}
3113
}
3114
if (n > 0)
3115
ps(n);
3116
}
3117
3118
static void
3119
troff_rm(Tag_t* tp, Arg_t* ap)
3120
{
3121
int i;
3122
3123
NoP(tp);
3124
for (i = 1; i <= ap->argc; i++)
3125
rm(ap->argv[i]);
3126
}
3127
3128
static void
3129
troff_rn(Tag_t* tp, Arg_t* ap)
3130
{
3131
Tag_t* mp;
3132
char* s;
3133
3134
NoP(tp);
3135
if (ap->argc >= 2)
3136
{
3137
s = ap->argv[2];
3138
rm(s);
3139
*--s = '.';
3140
mp = mac(ap->argv[1]);
3141
hashput(state.symbols, NiL, NiL);
3142
mp->name = hashput(state.symbols, s, mp);
3143
}
3144
}
3145
3146
static void
3147
rr(char* name)
3148
{
3149
register Num_t* np;
3150
char buf[MAXNAME];
3151
3152
if (np = (Num_t*)hashget(state.symbols, nam('.', name, buf, sizeof(buf))))
3153
{
3154
if (np->internal)
3155
return;
3156
if (np->flags & TAG_TRACE_SET)
3157
error(2, "remove register %s", np->name + 1);
3158
free(np);
3159
hashput(state.symbols, NiL, NiL);
3160
}
3161
}
3162
3163
static void
3164
troff_rr(Tag_t* tp, Arg_t* ap)
3165
{
3166
int i;
3167
3168
NoP(tp);
3169
for (i = 1; i <= ap->argc; i++)
3170
rr(ap->argv[i]);
3171
}
3172
3173
static void
3174
troff_so(Tag_t* tp, Arg_t* ap)
3175
{
3176
Sfio_t* sp;
3177
char* path;
3178
3179
NoP(tp);
3180
if (ap->argc >= 1 && (sp = find(ap->argv[1], &path, 1)))
3181
pushin(path, 1, sp, NiL, NiL);
3182
}
3183
3184
static void
3185
troff_sp(Tag_t* tp, Arg_t* ap)
3186
{
3187
NoP(tp);
3188
if (state.it.dd)
3189
{
3190
state.it.dd = 0;
3191
code_1(OP_dd);
3192
}
3193
code_1((ap->argc < 1 || expression(ap->argv[1], NiL, 'v') > 0) ? OP_p : OP_br);
3194
}
3195
3196
static void
3197
troff_ta(Tag_t* tp, Arg_t* ap)
3198
{
3199
int i;
3200
unsigned char ta[elementsof(state.ta)];
3201
3202
NoP(tp);
3203
if (ap->argc < 1)
3204
{
3205
state.ta[0] = 8;
3206
i = 1;
3207
}
3208
else
3209
{
3210
if (ap->argc >= elementsof(ta))
3211
ap->argc = elementsof(ta) - 1;
3212
for (i = 0; i < ap->argc; i++)
3213
ta[i] = expression(ap->argv[i+1], NiL, 'u');
3214
}
3215
ta[i] = 0;
3216
code_n(OP_ta, (char*)ta);
3217
}
3218
3219
static void
3220
troff_ti(Tag_t* tp, Arg_t* ap)
3221
{
3222
int n;
3223
3224
NoP(tp);
3225
if (ap->argc < 1)
3226
n = state.env->ti.previous;
3227
else
3228
{
3229
n = expression(ap->argv[1], NiL, 'u');
3230
switch (ap->argv[1][0])
3231
{
3232
case '-':
3233
case '+':
3234
n += state.env->ti.current;
3235
break;
3236
}
3237
}
3238
state.env->ti.previous = state.env->ti.current;
3239
state.env->ti.current = n;
3240
li(1);
3241
if (state.list > state.list_stack && state.env->ti.current < state.list->in)
3242
state.it.dt = 1;
3243
}
3244
3245
static void
3246
troff_tl(Tag_t* tp, Arg_t* ap)
3247
{
3248
register int c;
3249
register int q;
3250
register char* s;
3251
register char* t;
3252
int n;
3253
3254
NoP(tp);
3255
if (s = join(ap, 1))
3256
{
3257
if (state.head)
3258
state.footer = 1;
3259
else
3260
{
3261
state.head = 1;
3262
code_1(END(OP_head));
3263
code_1(OP_body);
3264
}
3265
code_1(OP_h3);
3266
code_2(OP_tab, ARG_wide);
3267
code_1(OP_tab_row);
3268
if (q = *s++)
3269
for (n = 1; n <= 3; n++)
3270
{
3271
code_2(OP_tab_head, n);
3272
if (n == 1)
3273
code_2(OP_cc, ' ');
3274
while ((c = *s++) != q)
3275
{
3276
if (!c)
3277
{
3278
s--;
3279
break;
3280
}
3281
if (c != state.pc)
3282
sfputc(state.out, c);
3283
else if (t = value("n%", 'n', 0))
3284
sfputr(state.out, t, -1);
3285
}
3286
if (n == 1)
3287
code_2(OP_cc, ' ');
3288
}
3289
code_1(END(OP_tab));
3290
code_1(END(OP_h3));
3291
sfputc(state.out, '\n');
3292
}
3293
}
3294
3295
static void
3296
troff_tm(Tag_t* tp, Arg_t* ap)
3297
{
3298
char* s;
3299
3300
NoP(tp);
3301
if (s = join(ap, 1))
3302
notify(s);
3303
}
3304
3305
static void
3306
troff_vs(Tag_t* tp, Arg_t* ap)
3307
{
3308
int n;
3309
3310
NoP(tp);
3311
if (ap->argc < 1)
3312
n = state.env->vs.previous;
3313
else
3314
{
3315
n = expression(ap->argv[1], NiL, 'p');
3316
switch (ap->argv[1][0])
3317
{
3318
case '-':
3319
case '+':
3320
n += state.env->vs.current;
3321
break;
3322
default:
3323
if (!n)
3324
n = state.env->vs.previous;
3325
break;
3326
}
3327
}
3328
if (n > 0)
3329
{
3330
state.env->vs.previous = state.env->vs.current;
3331
state.env->vs.current = n;
3332
}
3333
}
3334
3335
/*
3336
* return 1 if any non-diverted text escaped
3337
*/
3338
3339
static int
3340
text(void)
3341
{
3342
register char* s;
3343
register char* e;
3344
3345
if (state.it.text)
3346
return 1;
3347
s = sfstrbase(state.out);
3348
e = sfstrseek(state.out, 0, SEEK_CUR);
3349
while (s < e)
3350
{
3351
switch (*s++)
3352
{
3353
case CODE_2:
3354
s++;
3355
/*FALLTHROUGH*/
3356
case CODE_1:
3357
s++;
3358
/*FALLTHROUGH*/
3359
case ' ':
3360
case '\t':
3361
case '\n':
3362
case CODE_0:
3363
continue;
3364
default:
3365
message((-9, "TEXT begin"));
3366
return 1;
3367
}
3368
}
3369
return 0;
3370
}
3371
3372
static void
3373
troff_wh(Tag_t* tp, Arg_t* ap)
3374
{
3375
int i;
3376
Trap_t** xp;
3377
3378
if (ap->argc > 1)
3379
{
3380
if (!(i = expression(ap->argv[1], NiL, 'u')))
3381
{
3382
if (!state.it.text && text())
3383
state.it.text = 1;
3384
if (state.it.text)
3385
i = -1;
3386
}
3387
xp = (i < 0) ? &state.fini : &state.trap;
3388
for (i = 2; i <= ap->argc; i++)
3389
trap(xp, ap->argv[i]);
3390
}
3391
}
3392
3393
static void
3394
hot(register char* s, int add)
3395
{
3396
register Dir_t* x;
3397
register Dir_t* p;
3398
3399
for (p = 0, x = state.hot; x; p = x, x = x->next)
3400
if (streq(s, x->name))
3401
{
3402
if (!add)
3403
{
3404
if (p)
3405
p->next = x->next;
3406
else
3407
state.hot = state.hot->next;
3408
free(x);
3409
}
3410
return;
3411
}
3412
if (add)
3413
{
3414
if (!(x = newof(0, Dir_t, 1, strlen(s) + 1)))
3415
error(ERROR_SYSTEM|3, "out of space [hot]");
3416
strcpy(x->name = (char*)x + sizeof(*x), s);
3417
x->next = state.hot;
3418
state.hot = x;
3419
}
3420
}
3421
3422
#define OPT_SWITCH 1
3423
#define OPT_begin 2
3424
#define OPT_debug 3
3425
#define OPT_end 4
3426
#define OPT_footnote 5
3427
#define OPT_get 6
3428
#define OPT_hot 7
3429
#define OPT_label 8
3430
#define OPT_link 9
3431
#define OPT_test 10
3432
#define OPT_toolbar 11
3433
#define OPT_set 12
3434
3435
typedef struct
3436
{
3437
const char* name;
3438
char** value;
3439
int index;
3440
} Option_t;
3441
3442
static Option_t options[] =
3443
{
3444
"author", &state.author, 0,
3445
"background", &state.background, 0,
3446
"begin", 0, OPT_begin,
3447
"company", &state.company, 0,
3448
"corporation", &state.corporation, 0,
3449
"debug", 0, OPT_debug,
3450
"end", 0, OPT_end,
3451
"footnote", 0, OPT_footnote,
3452
"get", 0, OPT_get,
3453
"hot", 0, OPT_hot,
3454
"label", 0, OPT_label,
3455
"link", 0, OPT_link,
3456
"location", &state.location, 0,
3457
"logo", &state.logo, 0,
3458
"mailto", &state.mailto, 0,
3459
"organization", &state.organization, 0,
3460
"package", &state.package, 0,
3461
"set", 0, OPT_set,
3462
"test", 0, OPT_test,
3463
"title", &state.title, 0,
3464
"toolbar", &state.toolbar, OPT_toolbar,
3465
"verbose", (char**)&state.verbose, OPT_SWITCH,
3466
0, 0, 0
3467
};
3468
3469
/*
3470
* called by stropt() to set name=value
3471
*/
3472
3473
static int
3474
setopt(void* a, const void* x, register int n, const char* v)
3475
{
3476
register char* s;
3477
register char* t;
3478
register Option_t* p = (Option_t*)x;
3479
register int f;
3480
3481
if (p)
3482
switch (p->index)
3483
{
3484
case OPT_begin:
3485
sfprintf(state.tmp, "<!-- %s -->", v + n);
3486
code_n(OP_RAW, use(state.tmp));
3487
break;
3488
case OPT_debug:
3489
error_info.trace = n ? -expression((char*)v, NiL, 'u') : 0;
3490
break;
3491
case OPT_end:
3492
sfprintf(state.tmp, "<!-- /%s -->", v + n);
3493
code_n(OP_RAW, use(state.tmp));
3494
break;
3495
case OPT_footnote:
3496
if (!state.out)
3497
error(1, "%s: option valid from document body only", p->name);
3498
else
3499
{
3500
/*
3501
* NOTE: mm .FS/.FE is so convoluted that I
3502
* punted to this; trying to decipher
3503
* it prompted .xx [debug|get|set] in
3504
* the first place; I'll get back to a
3505
* real solution on a meetingless day.
3506
*/
3507
3508
if (n)
3509
{
3510
register char* b;
3511
long m;
3512
3513
m = sfstrtell(state.out);
3514
b = sfstrbase(state.out);
3515
s = b + m;
3516
while (s > b)
3517
if (!isspace(*--s))
3518
{
3519
s++;
3520
break;
3521
}
3522
t = s;
3523
while (s > b)
3524
if (isspace(*--s))
3525
{
3526
s++;
3527
break;
3528
}
3529
sfprintf(state.tmp, "FN%ld\t%-.*s", m, t - s, s);
3530
sfstrseek(state.out, s - b, SEEK_SET);
3531
code_n(OP_link, use(state.tmp));
3532
sfprintf(state.tmp, "FN%ld", m);
3533
code_n(OP_fn, use(state.tmp));
3534
}
3535
else
3536
code_1(END(OP_fn));
3537
}
3538
break;
3539
case OPT_get:
3540
case OPT_hot:
3541
case OPT_set:
3542
switch (p->index)
3543
{
3544
case OPT_get:
3545
f = TAG_TRACE_GET;
3546
break;
3547
case OPT_hot:
3548
f = 0;
3549
break;
3550
case OPT_set:
3551
f = TAG_TRACE_SET;
3552
break;
3553
}
3554
s = (char*)v;
3555
do
3556
{
3557
while (isspace(*s))
3558
s++;
3559
for (t = s;; t++)
3560
if (!*t)
3561
{
3562
t = 0;
3563
break;
3564
}
3565
else if (isspace(*t))
3566
{
3567
*t++ = 0;
3568
break;
3569
}
3570
if (!s[0])
3571
break;
3572
if (s[0] == '+' && !s[1])
3573
n = 1;
3574
else if (s[0] == '-' && !s[1])
3575
n = 0;
3576
else if (!f)
3577
hot(s, n);
3578
else if (n)
3579
{
3580
num(s)->flags |= f;
3581
mac(s)->flags |= f;
3582
}
3583
else
3584
{
3585
num(s)->flags &= ~f;
3586
mac(s)->flags &= ~f;
3587
}
3588
} while (s = t);
3589
break;
3590
case OPT_label:
3591
if (!state.out)
3592
error(1, "%s: option valid from document body only", p->name);
3593
else if (n)
3594
{
3595
sfprintf(state.tmp, "%s\t", v);
3596
code_n(LABEL(OP_link), use(state.tmp));
3597
}
3598
break;
3599
case OPT_link:
3600
if (!state.out)
3601
error(1, "%s: option valid from document body only", p->name);
3602
else if (n)
3603
code_n(OP_link, (char*)v);
3604
break;
3605
case OPT_test:
3606
if (n)
3607
state.test |= expression((char*)v, NiL, 'u');
3608
else
3609
state.test &= ~expression((char*)v, NiL, 'u');
3610
break;
3611
case OPT_SWITCH:
3612
*((int*)p->value) = n && expression((char*)v, NiL, 'u');
3613
break;
3614
default:
3615
if (*p->value)
3616
free(*p->value);
3617
*p->value = n && v ? strdup(v) : (char*)0;
3618
break;
3619
}
3620
else if (a)
3621
{
3622
if (strneq(v, "meta.", 5))
3623
{
3624
sfprintf(state.tmp, "<META name=\"%s\" content=\"%s\">", v + 5, v + n);
3625
code_n(OP_RAW, use(state.tmp));
3626
}
3627
else
3628
error(1, "%s: unknown option", v);
3629
}
3630
return 0;
3631
}
3632
3633
static void
3634
troff_xx(Tag_t* tp, Arg_t* ap)
3635
{
3636
NoP(tp);
3637
stropt(join(ap, 1), options, sizeof(*options), setopt, options);
3638
}
3639
3640
#define COMMENT 001
3641
#define COPY 002
3642
#define EAT 004
3643
#define RAW 010
3644
#define STRING 020
3645
3646
#define ONE() \
3647
do \
3648
{\
3649
if (cc <= 0) \
3650
{ \
3651
if (cc < 0) \
3652
cc++; \
3653
if (!argc && !state.it.interrupt) \
3654
it(); \
3655
} \
3656
cc++; \
3657
} while (0)
3658
3659
/*
3660
* convert troff file ip to intermediate file op
3661
*/
3662
3663
static void
3664
process(char* file, Sfio_t* ip, Sfio_t* op)
3665
{
3666
register int c;
3667
register int cc;
3668
register int lastc;
3669
register char* s;
3670
int argc;
3671
int quote;
3672
int n;
3673
int m;
3674
unsigned char* map;
3675
Tag_t* tp;
3676
int argv[ARGS];
3677
struct stat st;
3678
char buf[MAXNAME];
3679
3680
pushin(file, 1, ip, NiL, NiL);
3681
state.generation++;
3682
*(state.env_sp = state.env_stack) = state.env = env("0");
3683
state.it.dd = state.it.dl = state.it.dt = 0;
3684
state.link = 0;
3685
iop("stdout", 1)->sp = state.out = op;
3686
state.pass = 0;
3687
if (ip && !fstat(sffileno(ip), &st))
3688
tm(st.st_mtime);
3689
argc = 0;
3690
argv[0] = 0;
3691
cc = 0;
3692
lastc = 0;
3693
quote = 0;
3694
map = ccmap(CC_NATIVE, CC_ASCII);
3695
for (;;)
3696
{
3697
switch (c = GETC())
3698
{
3699
case EOF:
3700
if (popin())
3701
goto done;
3702
continue;
3703
case CODE_n:
3704
if (ISFILE())
3705
{
3706
cc++;
3707
code_2(OP_cc, c);
3708
}
3709
else
3710
{
3711
sfputc(state.out, c);
3712
n = GETC();
3713
sfputc(state.out, n);
3714
if (n & 0200)
3715
{
3716
n = (n & 0177) << 8;
3717
c = GETC();
3718
sfputc(state.out, c);
3719
n |= c;
3720
}
3721
do
3722
{
3723
c = GETC();
3724
sfputc(state.out, c);
3725
} while (n-- > 0);
3726
}
3727
continue;
3728
case CODE_2:
3729
if (ISFILE())
3730
{
3731
cc++;
3732
code_2(OP_cc, c);
3733
}
3734
else
3735
{
3736
m = GETC();
3737
n = GETC();
3738
if (cc <= 0 && (n == OP_cc || n >= OP_ft1 && n <= OP_ft5 || n == OP_ps))
3739
ONE();
3740
sfputc(state.out, c);
3741
sfputc(state.out, m);
3742
sfputc(state.out, n);
3743
}
3744
continue;
3745
case CODE_1:
3746
if (ISFILE())
3747
{
3748
cc++;
3749
code_2(OP_cc, c);
3750
}
3751
else
3752
{
3753
sfputc(state.out, c);
3754
switch (c = GETC())
3755
{
3756
case OP_br:
3757
case OP_p:
3758
cc = 0;
3759
break;
3760
}
3761
sfputc(state.out, c);
3762
}
3763
continue;
3764
case CODE_0:
3765
if (ISFILE())
3766
{
3767
cc++;
3768
code_2(OP_cc, c);
3769
}
3770
else
3771
{
3772
ONE();
3773
sfputc(state.out, c);
3774
}
3775
continue;
3776
case '"':
3777
if (argc && !(quote & RAW))
3778
{
3779
lastc = c;
3780
switch ((quote & STRING) ? (c = nextchar()) : EOF)
3781
{
3782
case '"':
3783
break;
3784
default:
3785
UNGETC(c);
3786
/*FALLTHROUGH*/
3787
case EOF:
3788
quote ^= STRING;
3789
continue;
3790
}
3791
}
3792
break;
3793
case '\n':
3794
if (state.noline)
3795
{
3796
if (isspace(lastc))
3797
continue;
3798
c = ' ';
3799
break;
3800
}
3801
if (quote & COMMENT)
3802
{
3803
quote &= ~COMMENT;
3804
if (quote & EAT)
3805
{
3806
quote &= ~EAT;
3807
c = ' ';
3808
}
3809
popout();
3810
}
3811
if (argc)
3812
{
3813
cc = 0;
3814
lastc = c;
3815
quote = 0;
3816
state.groff &= 1;
3817
state.pass = 0;
3818
if (!(s = popout()))
3819
{
3820
argc = 0;
3821
continue;
3822
}
3823
if (!s[argv[--argc]] && argc > 0)
3824
argc--;
3825
for (state.tag->argc = argc; argc >= 0; argc--)
3826
state.tag->argv[argc] = s + argv[argc];
3827
argc = 0;
3828
if (error_info.trace <= -4)
3829
{
3830
if (state.end)
3831
sfprintf(state.tmp, " ");
3832
sfprintf(state.tmp, "%s", state.tag->argv[0]);
3833
if (!tp)
3834
sfprintf(state.tmp, " [UNKNOWN]");
3835
for (n = 1; n <= state.tag->argc; n++)
3836
sfprintf(state.tmp, " `%s'", state.tag->argv[n]);
3837
error(-4, "%s:%d: %s", error_info.file, error_info.line, use(state.tmp));
3838
}
3839
if (tp)
3840
{
3841
error_info.line++;
3842
if (tp->call)
3843
(*tp->call)(tp, state.tag);
3844
if (tp->flags & TAG_TRIGGER)
3845
{
3846
tp->flags &= ~TAG_TRIGGER;
3847
trigger(&state.trap);
3848
}
3849
}
3850
else if (state.verbose)
3851
{
3852
switch (state.tag->argv[0][1])
3853
{
3854
case 0:
3855
break;
3856
case '#':
3857
if (!state.tag->argv[0][2])
3858
break;
3859
/*FALLTHROUGH*/
3860
default:
3861
if (error_info.trace >= 0)
3862
error(1, "%s: unknown request", state.tag->argv[0]);
3863
sfprintf(state.tmp, "UNKNOWN REQUEST %s", join(state.tag, 0));
3864
code_n(OP_comment, use(state.tmp));
3865
break;
3866
}
3867
error_info.line++;
3868
}
3869
continue;
3870
}
3871
error_info.line++;
3872
n = 6 * state.env->vs.current;
3873
state.nl += n;
3874
state.env->dn += n;
3875
n = 6 * state.env->ps.current;
3876
if (n > state.env->dl)
3877
state.env->dl = n;
3878
if (!DIVERTED())
3879
{
3880
state.ln++;
3881
if (state.it.text || cc > 1)
3882
state.it.text++;
3883
else
3884
{
3885
cc = 0;
3886
continue;
3887
}
3888
}
3889
state.it.interrupt = 0;
3890
if (state.it.center > 0)
3891
{
3892
if (!--state.it.center)
3893
code_1(state.it.right ? END(OP_p) : END(OP_center));
3894
else
3895
code_1(OP_br);
3896
}
3897
if (state.it.count > 0 && !--state.it.count)
3898
{
3899
sfputc(state.out, '\n');
3900
pushin(NiL, 0, NiL, state.it.trap, NiL);
3901
cc = 0;
3902
continue;
3903
}
3904
cc = -2;
3905
break;
3906
case ' ':
3907
case '\t':
3908
if (argc)
3909
{
3910
if (!quote || (quote == RAW || quote == (COPY|RAW)) && argc == 1)
3911
{
3912
if (lastc != ' ' && argc < elementsof(argv))
3913
{
3914
sfputc(state.out, 0);
3915
argv[argc++] = sftell(state.out);
3916
lastc = ' ';
3917
}
3918
continue;
3919
}
3920
}
3921
break;
3922
default:
3923
if (c == state.ec)
3924
{
3925
escape:
3926
switch (c = GETC())
3927
{
3928
case EOF:
3929
if (popin())
3930
{
3931
c = '\n';
3932
break;
3933
}
3934
goto escape;
3935
case '\n':
3936
if (ISFILE())
3937
error_info.line++;
3938
else
3939
UNGETC(c);
3940
continue;
3941
case 'a':
3942
if (state.pass)
3943
goto passthrough;
3944
c = 1;
3945
break;
3946
case 'c':
3947
if (state.pass || (quote & RAW))
3948
goto passthrough;
3949
if ((c = nextchar()) == '\n')
3950
{
3951
message((-9, "INTERRUPT %s:%d: it.center=%d it.count=%d it.dt", error_info.file, error_info.line, state.it.center, state.it.count));
3952
state.it.interrupt = 1;
3953
error_info.line++;
3954
cc = 0;
3955
continue;
3956
}
3957
UNGETC(c);
3958
continue;
3959
case 'd':
3960
if (state.env->ss <= 0)
3961
{
3962
state.env->ss--;
3963
code_1(OP_sub);
3964
}
3965
else
3966
{
3967
state.env->ss++;
3968
code_1(END(OP_sup));
3969
}
3970
continue;
3971
case 'e':
3972
case 'E':
3973
if (state.pass)
3974
goto passthrough;
3975
ONE();
3976
code_2(OP_cc, state.eo);
3977
lastc = state.eo;
3978
continue;
3979
case 'f':
3980
if (state.pass || (quote & RAW))
3981
goto passthrough;
3982
ft(interpolate('f'));
3983
continue;
3984
case 'g':
3985
case 'j':
3986
if (state.pass || (quote & RAW))
3987
goto passthrough;
3988
pushin(NiL, 0, NiL, interpolate(c), NiL);
3989
continue;
3990
case 'k':
3991
nextchar();
3992
continue;
3993
case '*':
3994
c = '.';
3995
goto interp;
3996
case '[':
3997
if (!state.groff)
3998
goto passthrough;
3999
case '(':
4000
if (state.pass)
4001
goto passthrough;
4002
UNGETC(c);
4003
/*FALLTHROUGH*/
4004
case 'n':
4005
interp:
4006
if (quote & COPY)
4007
goto passthrough;
4008
if (s = interpolate(c))
4009
pushin(NiL, -1, NiL, s, NiL);
4010
continue;
4011
case 'p':
4012
case 'r':
4013
case 'z':
4014
continue;
4015
case 's':
4016
if (state.pass || (quote & RAW))
4017
goto passthrough;
4018
switch (c = nextchar())
4019
{
4020
case '-':
4021
case '+':
4022
m = c;
4023
c = nextchar();
4024
break;
4025
default:
4026
m = 0;
4027
break;
4028
}
4029
switch (c)
4030
{
4031
case '(':
4032
n = 1;
4033
sfputc(state.tmp, c);
4034
break;
4035
case '\'':
4036
n = c;
4037
goto size_long;
4038
case '[':
4039
n = ']';
4040
size_long:
4041
c = nextchar();
4042
if (!m)
4043
switch (c)
4044
{
4045
case '-':
4046
case '+':
4047
m = c;
4048
c = nextchar();
4049
break;
4050
}
4051
while (c != EOF && c != n)
4052
{
4053
sfputc(state.tmp, c);
4054
c = nextchar();
4055
}
4056
goto size_eval;
4057
default:
4058
n = 0;
4059
break;
4060
}
4061
if (c == '0' && m)
4062
{
4063
if ((n = nextchar()) == '\'')
4064
{
4065
while ((c = nextchar()) != EOF && c != '\'')
4066
sfputc(state.tmp, c);
4067
code_n(m == '+' ? OP_link : LABEL(OP_link), use(state.tmp));
4068
continue;
4069
}
4070
UNGETC(n);
4071
n = 0;
4072
}
4073
if (c != EOF)
4074
{
4075
if (n)
4076
{
4077
for (;; c = nextchar())
4078
{
4079
switch (c)
4080
{
4081
case EOF:
4082
break;
4083
case '(':
4084
sfputc(state.tmp, c);
4085
n++;
4086
continue;
4087
case ')':
4088
sfputc(state.tmp, c);
4089
if (--n <= 0)
4090
break;
4091
continue;
4092
default:
4093
sfputc(state.tmp, c);
4094
continue;
4095
}
4096
break;
4097
}
4098
size_eval:
4099
n = expression(use(state.tmp), NiL, 'p');
4100
}
4101
else
4102
n = isdigit(c) ? (c - '0') : 0;
4103
if (!n)
4104
n = state.env->ps.previous;
4105
else switch (m)
4106
{
4107
case '-':
4108
n = state.env->ps.current - n;
4109
break;
4110
case '+':
4111
n = state.env->ps.current + n;
4112
break;
4113
}
4114
if (n > 0)
4115
ps(n);
4116
}
4117
continue;
4118
case 't':
4119
if (state.pass)
4120
goto passthrough;
4121
c = '\t';
4122
break;
4123
case 'u':
4124
if (state.env->ss >= 0)
4125
{
4126
state.env->ss++;
4127
code_1(OP_sup);
4128
}
4129
else
4130
{
4131
state.env->ss--;
4132
code_1(END(OP_sub));
4133
}
4134
continue;
4135
case 'v':
4136
case 'w':
4137
if (state.pass || (quote & RAW))
4138
goto passthrough;
4139
/*FALLTHROUGH*/
4140
case 'b':
4141
case 'h':
4142
case 'l':
4143
case 'L':
4144
case 'o':
4145
case 'x':
4146
if ((n = nextchar()) != EOF)
4147
{
4148
while ((m = nextchar()) != n)
4149
sfputc(state.arg, m);
4150
s = use(state.arg);
4151
switch (c)
4152
{
4153
case 'h':
4154
if (*s++ == '0')
4155
{
4156
/*UNDENT...*/
4157
if ((c = *s++) == '*' || c == '/')
4158
{
4159
state.link = c == '*' ? OP_link : LABEL(OP_link);
4160
if (*s++ == '\\' && *s++ == 'w' && *s++ == '"')
4161
{
4162
if ((c = strlen(s)) > 0 && s[--c] == '"')
4163
s[c] = 0;
4164
sfputr(state.ref, s, '\t');
4165
}
4166
pushout(state.ref);
4167
}
4168
else if (!c && state.link)
4169
{
4170
code_n(state.link, popout());
4171
state.link = 0;
4172
}
4173
/*...INDENT*/
4174
}
4175
/* yep, this is a grade A hack, even for this file */
4176
if ((c = nextchar()) == state.ec)
4177
{
4178
if ((n = nextchar()) == 'c')
4179
break;
4180
UNGETC(n);
4181
}
4182
UNGETC(c);
4183
break;
4184
case 'v':
4185
c = expression(s, NiL, 0) >= 0 ? 'd' : 'u';
4186
sfprintf(state.arg, "%c%c", state.ec, c);
4187
pushin(NiL, 0, NiL, use(state.arg), NiL);
4188
break;
4189
case 'w':
4190
n = convert(strlen(s), 1, 'n');
4191
sfprintf(state.arg, "%d", n);
4192
pushin(NiL, 0, NiL, use(state.arg), NiL);
4193
break;
4194
}
4195
}
4196
continue;
4197
case '$':
4198
if (state.mac)
4199
{
4200
c = nextchar();
4201
if (c == '(')
4202
{
4203
if ((c = nextchar()) != EOF)
4204
{
4205
sfputc(state.tmp, c);
4206
if ((c = nextchar()) != EOF)
4207
sfputc(state.tmp, c);
4208
}
4209
goto arg_eval;
4210
}
4211
else if (c == '[')
4212
{
4213
while ((c = nextchar()) != EOF && c != ']')
4214
sfputc(state.tmp, c);
4215
arg_eval:
4216
c = expression(use(state.tmp), NiL, 0);
4217
if (c >= 0 && c <= state.mac->argc)
4218
pushin(NiL, -argc, NiL, state.mac->argv[c], NiL);
4219
}
4220
else if (c == '@')
4221
{
4222
for (c = 1; c < state.mac->argc; c++)
4223
sfprintf(state.tmp, "\"%s\" ", state.mac->argv[c]);
4224
if (c == state.mac->argc)
4225
sfprintf(state.tmp, "\"%s\"", state.mac->argv[c]);
4226
pushin(NiL, -argc, NiL, use(state.tmp), NiL);
4227
}
4228
else if (c < '0' || c > '9')
4229
{
4230
for (c = 1; c < state.mac->argc; c++)
4231
sfputr(state.tmp, state.mac->argv[c], ' ');
4232
if (c == state.mac->argc)
4233
sfputr(state.tmp, state.mac->argv[c], -1);
4234
pushin(NiL, -argc, NiL, use(state.tmp), NiL);
4235
}
4236
else if ((c -= '0') <= state.mac->argc)
4237
pushin(NiL, -argc, NiL, state.mac->argv[c], NiL);
4238
}
4239
continue;
4240
case '{':
4241
for (;;)
4242
{
4243
if ((n = GETC()) == EOF)
4244
{
4245
if (popin())
4246
break;
4247
continue;
4248
}
4249
if (n == state.ec)
4250
{
4251
escape_splice:
4252
switch (m = GETC())
4253
{
4254
case EOF:
4255
if (popin())
4256
break;
4257
goto escape_splice;
4258
case '\n':
4259
UNGETC(m);
4260
break;
4261
default:
4262
UNGETC(m);
4263
UNGETC(n);
4264
break;
4265
}
4266
break;
4267
}
4268
else
4269
UNGETC(n);
4270
break;
4271
}
4272
if (state.pass)
4273
goto passthrough;
4274
continue;
4275
case '}':
4276
if (state.end || !(state.cond.flags[state.cond.level] & COND_BLOCK))
4277
goto passthrough;
4278
if (state.cond.flags[state.cond.level] & (COND_EL|COND_IF))
4279
state.cond.level--;
4280
else
4281
state.cond.flags[state.cond.level] &= ~COND_BLOCK;
4282
continue;
4283
case '0':
4284
case '|':
4285
case '^':
4286
case ' ':
4287
case '/':
4288
case ',':
4289
case '~':
4290
if (state.pass)
4291
goto passthrough;
4292
ONE();
4293
code_2(OP_cc, ' ');
4294
continue;
4295
case '&':
4296
if (state.pass || (quote & RAW))
4297
goto passthrough;
4298
if (!cc)
4299
cc = -1;
4300
code_0();
4301
continue;
4302
case '-':
4303
case '%':
4304
if (state.pass)
4305
goto passthrough;
4306
ONE();
4307
code_2(OP_cc, 45);
4308
continue;
4309
case '"':
4310
if (!quote)
4311
{
4312
quote |= COMMENT;
4313
pushout(state.nul);
4314
}
4315
cc++;
4316
continue;
4317
case '#':
4318
if (!quote)
4319
{
4320
quote |= COMMENT|EAT;
4321
pushout(state.nul);
4322
}
4323
cc++;
4324
continue;
4325
case '!':
4326
if (!cc)
4327
{
4328
cc++;
4329
continue;
4330
}
4331
break;
4332
default:
4333
if (c == state.ec)
4334
break;
4335
if ((c == state.env->cc || c == state.env->c2) && !cc)
4336
goto request;
4337
if (state.pass)
4338
goto passthrough;
4339
break;
4340
}
4341
break;
4342
passthrough:
4343
ONE();
4344
sfputc(state.out, state.ec);
4345
break;
4346
}
4347
else if ((c == state.env->cc || c == state.env->c2) && !cc)
4348
{
4349
request:
4350
n = c;
4351
s = buf;
4352
*s++ = '.';
4353
if ((c = nextchar()) != EOF)
4354
{
4355
while (c == ' ' || c == '\t')
4356
c = nextchar();
4357
if (c == state.ec || c == '\n')
4358
UNGETC(c);
4359
else if (c != EOF)
4360
{
4361
*s++ = c;
4362
while (s < &buf[sizeof(buf)-1] && (c = nextchar()) != EOF)
4363
{
4364
if (c == state.ec || isspace(c))
4365
{
4366
UNGETC(c);
4367
break;
4368
}
4369
*s++ = c;
4370
if (!state.groff)
4371
{
4372
if ((c = nextchar()) != EOF)
4373
{
4374
UNGETC(c);
4375
if (!isspace(c))
4376
pushin(NiL, 0, NiL, " ", NiL);
4377
}
4378
break;
4379
}
4380
}
4381
}
4382
}
4383
*s = 0;
4384
tp = (Tag_t*)hashget(state.symbols, buf);
4385
if (tp && (tp->flags & TAG_DO))
4386
{
4387
state.groff |= 2;
4388
c = n;
4389
goto request;
4390
}
4391
if (state.end)
4392
{
4393
if (tp == state.end)
4394
{
4395
state.end = 0;
4396
state.pass = 0;
4397
s = popout();
4398
if (tp = state.define)
4399
{
4400
state.define = 0;
4401
set(tp, s, 0);
4402
}
4403
quote |= COMMENT;
4404
pushout(state.nul);
4405
}
4406
else
4407
{
4408
pushin(NiL, 0, NiL, buf + 1, NiL);
4409
c = n;
4410
}
4411
break;
4412
}
4413
if (tp)
4414
{
4415
if ((tp->flags & TAG_BREAK) && n == state.env->cc)
4416
{
4417
if (!DIVERTED() && (state.it.text || text()))
4418
state.it.text++;
4419
if (state.it.interrupt)
4420
{
4421
message((-9, "BREAK %s:%d: it.center=%d it.count=%d", error_info.file, error_info.line, state.it.center, state.it.count));
4422
state.it.interrupt = 0;
4423
sfputc(state.out, '\n');
4424
ONE();
4425
}
4426
tp->flags |= TAG_TRIGGER;
4427
}
4428
if (tp->flags & TAG_COPY)
4429
quote |= COPY|RAW;
4430
else if (tp->flags & TAG_RAW)
4431
quote |= RAW;
4432
if (tp->flags & TAG_PASS)
4433
state.pass = 1;
4434
}
4435
else
4436
state.pass = 1;
4437
argc = 1;
4438
pushout(state.tag->sp);
4439
sfputr(state.out, buf, -1);
4440
cc = s - buf;
4441
continue;
4442
}
4443
else if ((n = ccmapchr(map, c)) > 0177)
4444
{
4445
ONE();
4446
code_2(OP_cc, n & 0377);
4447
continue;
4448
}
4449
break;
4450
}
4451
ONE();
4452
sfputc(state.out, c);
4453
lastc = c;
4454
}
4455
done:
4456
if (state.end)
4457
{
4458
if (state.define)
4459
{
4460
error(2, "%s macro definition end tag %s not found", state.define->name, state.end->name);
4461
state.define = 0;
4462
}
4463
else
4464
error(2, "group end tag %s not found", state.end->name);
4465
state.end = 0;
4466
}
4467
while (DIVERTED())
4468
popout();
4469
}
4470
4471
static Tag_t tags[] =
4472
{
4473
".", 0, 0, 0,0,0,0,
4474
".'", 0, 0, 0,0,0,0,
4475
".''", 0, 0, 0,0,0,0,
4476
"..", 0, 0, 0,0,0,0,
4477
".EN", 0, 0, 0,0,0,0,
4478
".EQ", troff_ignore, 0, 0,0,0,0,
4479
".TE", 0, 0, 0,0,0,0,
4480
".TS", troff_ignore, 0, 0,0,0,0,
4481
".\"", 0, 0, 0,0,0,0,
4482
".\\\"",0, 0, 0,0,0,0,
4483
".ab", troff_ab, 0, 0,0,0,0,
4484
".ad", troff_ad, TAG_PASS, 0,0,0,0,
4485
".af", troff_af, 0, 0,0,0,0,
4486
".al", 0, 0, 0,0,0,0,
4487
".aln", groff_aln, 0, 0,0,0,0,
4488
".als", groff_als, 0, 0,0,0,0,
4489
".am", troff_de, TAG_PASS, 0,0,0,0,
4490
".as", troff_ds, TAG_PASS, 0,0,0,0,
4491
".asciify", groff_asciify, 0, 0,0,0,0,
4492
".backtrace", 0, 0, 0,0,0,0,
4493
".bd", 0, 0, 0,0,0,0,
4494
".blm", 0, 0, 0,0,0,0,
4495
".bp", 0, TAG_BREAK, 0,0,0,0,
4496
".br", troff_br, TAG_BREAK, 0,0,0,0,
4497
".break", groff_break, 0, 0,0,0,0,
4498
".c2", troff_c2, 0, 0,0,0,0,
4499
".cc", troff_cc, 0, 0,0,0,0,
4500
".ce", troff_ce, TAG_BREAK, 0,0,0,0,
4501
".cf", troff_cf, 0, 0,0,0,0,
4502
".cflags", 0, 0, 0,0,0,0,
4503
".ch", troff_ch, 0, 0,0,0,0,
4504
".char", 0, 0, 0,0,0,0,
4505
".chop", groff_chop, 0, 0,0,0,0,
4506
".close", groff_close, 0, 0,0,0,0,
4507
".continue", groff_continue, 0, 0,0,0,0,
4508
".cp", groff_cp, 0, 0,0,0,0,
4509
".cs", 0, 0, 0,0,0,0,
4510
".cu", 0, 0, 0,0,0,0,
4511
".da", troff_di, TAG_PASS, 0,0,0,0,
4512
".de", troff_de, TAG_PASS, 0,0,0,0,
4513
".di", troff_di, TAG_PASS, 0,0,0,0,
4514
".do", 0, TAG_DO, 0,0,0,0,
4515
".ds", troff_ds, TAG_PASS, 0,0,0,0,
4516
".dt", troff_wh, TAG_PASS, 0,0,0,0,
4517
".ec", troff_ec, 0, 0,0,0,0,
4518
".eo", troff_eo, 0, 0,0,0,0,
4519
".el", troff_ie, TAG_PASS|TAG_RAW, 0,0,0,0,
4520
".em", troff_em, TAG_PASS, 0,0,0,0,
4521
".ev", troff_ev, TAG_PASS, 0,0,0,0,
4522
".fam", 0, 0, 0,0,0,0,
4523
".fc", 0, 0, 0,0,0,0,
4524
".fi", troff_fi, TAG_BREAK, 0,0,0,0,
4525
".fl", 0, TAG_BREAK, 0,0,0,0,
4526
".fp", troff_fp, 0, 0,0,0,0,
4527
".fspecial", 0, 0, 0,0,0,0,
4528
".ft", troff_ft, 0, 0,0,0,0,
4529
".ftr", 0, 0, 0,0,0,0,
4530
".hcode", 0, 0, 0,0,0,0,
4531
".hla", 0, 0, 0,0,0,0,
4532
".hlm", 0, 0, 0,0,0,0,
4533
".hpf", 0, 0, 0,0,0,0,
4534
".hw", 0, 0, 0,0,0,0,
4535
".hy", 0, 0, 0,0,0,0,
4536
".hym", 0, 0, 0,0,0,0,
4537
".hys", 0, 0, 0,0,0,0,
4538
".ie", troff_ie, TAG_PASS|TAG_RAW, 0,0,0,0,
4539
".if", troff_ie, TAG_PASS|TAG_RAW, 0,0,0,0,
4540
".ig", troff_ignore, TAG_PASS, 0,0,0,0,
4541
".in", troff_in, TAG_BREAK, 0,0,0,0,
4542
".it", troff_it, 0, 0,0,0,0,
4543
".kern", 0, 0, 0,0,0,0,
4544
".lc", 0, 0, 0,0,0,0,
4545
".lf", 0, 0, 0,0,0,0,
4546
".ll", troff_ll, 0, 0,0,0,0,
4547
".ls", 0, 0, 0,0,0,0,
4548
".lt", 0, 0, 0,0,0,0,
4549
".mk", 0, 0, 0,0,0,0,
4550
".mso", troff_so, 0, 0,0,0,0,
4551
".na", 0, 0, 0,0,0,0,
4552
".ne", troff_ne, 0, 0,0,0,0,
4553
".nf", troff_nf, TAG_BREAK, 0,0,0,0,
4554
".nh", 0, 0, 0,0,0,0,
4555
".nm", 0, 0, 0,0,0,0,
4556
".nn", 0, 0, 0,0,0,0,
4557
".nr", troff_nr, 0, 0,0,0,0,
4558
".nroff", 0, 0, 0,0,0,0,
4559
".ns", 0, 0, 0,0,0,0,
4560
".open", groff_open, 0, 0,0,0,0,
4561
".opena", groff_open, 0, 0,0,0,0,
4562
".os", 0, 0, 0,0,0,0,
4563
".pc", troff_pc, 0, 0,0,0,0,
4564
".pl", 0, 0, 0,0,0,0,
4565
".pm", 0, 0, 0,0,0,0,
4566
".pn", 0, 0, 0,0,0,0,
4567
".pnr", 0, 0, 0,0,0,0,
4568
".po", 0, 0, 0,0,0,0,
4569
".ps", troff_ps, 0, 0,0,0,0,
4570
".pso", groff_pso, 0, 0,0,0,0,
4571
".ptr", 0, 0, 0,0,0,0,
4572
".rchar", 0, 0, 0,0,0,0,
4573
".rj", troff_ce, 0, 0,0,0,0,
4574
".rm", troff_rm, 0, 0,0,0,0,
4575
".rn", troff_rn, 0, 0,0,0,0,
4576
".rnn", groff_rnn, 0, 0,0,0,0,
4577
".rr", troff_rr, 0, 0,0,0,0,
4578
".rs", 0, 0, 0,0,0,0,
4579
".rt", 0, 0, 0,0,0,0,
4580
".shc", 0, 0, 0,0,0,0,
4581
".shift", groff_shift, 0, 0,0,0,0,
4582
".so", troff_so, 0, 0,0,0,0,
4583
".sp", troff_sp, TAG_BREAK, 0,0,0,0,
4584
".special", 0, 0, 0,0,0,0,
4585
".ss", 0, 0, 0,0,0,0,
4586
".sty", 0, 0, 0,0,0,0,
4587
".sv", 0, 0, 0,0,0,0,
4588
".sy", groff_sy, 0, 0,0,0,0,
4589
".ta", troff_ta, TAG_BREAK, 0,0,0,0,
4590
".tc", 0, 0, 0,0,0,0,
4591
".ti", troff_ti, TAG_BREAK, 0,0,0,0,
4592
".tkf", 0, 0, 0,0,0,0,
4593
".tl", troff_tl, 0, 0,0,0,0,
4594
".tm", troff_tm, 0, 0,0,0,0,
4595
".tr", 0, 0, 0,0,0,0,
4596
".trf", 0, 0, 0,0,0,0,
4597
".trnt", 0, 0, 0,0,0,0,
4598
".troff", 0, 0, 0,0,0,0,
4599
".uf", 0, 0, 0,0,0,0,
4600
".ul", 0, 0, 0,0,0,0,
4601
".vpt", 0, 0, 0,0,0,0,
4602
".vs", troff_vs, 0, 0,0,0,0,
4603
".warn", 0, 0, 0,0,0,0,
4604
".wh", troff_wh, TAG_PASS, 0,0,0,0,
4605
".while", groff_while, TAG_PASS|TAG_COPY, 0,0,0,0,
4606
".write", groff_write, 0, 0,0,0,0,
4607
".xx", troff_xx, TAG_RAW, 0,0,0,0,
4608
};
4609
4610
static Var_t vars[] =
4611
{
4612
"(**", "*",
4613
"(+-", "\261",
4614
"(ap", "~",
4615
"(bu", "\267",
4616
"(bv", "|",
4617
"(co", "\251",
4618
"(dg", "\247",
4619
"(fm", "'",
4620
"(lq", "``",
4621
"(rg", "\256",
4622
"(rq", "''",
4623
"(sq", "\244",
4624
"eEQ", ".EN",
4625
"eTS", ".TE",
4626
"eig", "..",
4627
"f", "",
4628
"f1", "1",
4629
"f2", "2",
4630
"f3", "3",
4631
"f5", "5",
4632
"fB", "3",
4633
"fCW", "5",
4634
"fF", "5",
4635
"fI", "2",
4636
"fL", "5",
4637
"fM", "5",
4638
"fR", "1",
4639
"fX", "5",
4640
};
4641
4642
static Dir_t dot =
4643
{
4644
0, ".",
4645
};
4646
4647
/*
4648
* initialize the global data
4649
*/
4650
4651
static void
4652
init(void)
4653
{
4654
register int i;
4655
4656
state.groff |= 2;
4657
state.tag = &state.top;
4658
if (!(state.top.sp = sfstropen()))
4659
error(ERROR_SYSTEM|3, "out of space [top buffer]");
4660
if (!(state.arg = sfstropen()))
4661
error(ERROR_SYSTEM|3, "out of space [arg buffer]");
4662
if (!(state.nul = sfopen(NiL, "/dev/null", "w")))
4663
error(ERROR_SYSTEM|3, "out of space [nul buffer]");
4664
if (!(state.ref = sfstropen()))
4665
error(ERROR_SYSTEM|3, "out of space [ref buffer]");
4666
if (!(state.req = sfstropen()))
4667
error(ERROR_SYSTEM|3, "out of space [req buffer]");
4668
if (!(state.tmp = sfstropen()))
4669
error(ERROR_SYSTEM|3, "out of space [tmp buffer]");
4670
if (!(state.symbols = hashalloc(NiL, HASH_name, "symbols", 0)))
4671
error(ERROR_SYSTEM|3, "out of space [symbol hash]");
4672
for (i = 0; i < elementsof(vars); i++)
4673
if (!hashput(state.symbols, vars[i].name, vars[i].value))
4674
error(ERROR_SYSTEM|3, "out of space [var hash put]");
4675
for (i = 0; i < elementsof(tags); i++)
4676
{
4677
tags[i].flags |= TAG_STATIC;
4678
if (!hashput(state.symbols, tags[i].name, &tags[i]))
4679
error(ERROR_SYSTEM|3, "out of space [tag hash put]");
4680
}
4681
hashset(state.symbols, HASH_ALLOCATE);
4682
nr("%", 1, 0, 1);
4683
nr(".$", 0, 0, 1);
4684
nr(".A", 0, 0, 1);
4685
nr(".C", 0, 0, 1);
4686
nr(".F", 0, 0, 1);
4687
nr(".H", 0, 0, 1);
4688
nr(".L", 0, 0, 1);
4689
nr(".P", 0, 0, 1);
4690
nr(".T", 0, 0, 1);
4691
nr(".R", 0, 0, 1);
4692
nr(".V", 0, 0, 1);
4693
nr(".a", 0, 0, 1);
4694
nr(".b", 0, 0, 1);
4695
nr(".c", 0, 0, 1);
4696
nr(".ce", 0, 0, 1);
4697
nr(".d", 0, 0, 1);
4698
nr(".ev", 0, 0, 1);
4699
nr(".f", 0, 0, 1);
4700
nr(".g", 0, 0, 1);
4701
nr(".i", 0, 0, 1);
4702
nr(".in", 0, 0, 1);
4703
nr(".j", 0, 0, 1);
4704
nr(".k", 0, 0, 1);
4705
nr(".l", 0, 0, 1);
4706
nr(".ll", 0, 0, 1);
4707
nr(".n", 0, 0, 1);
4708
nr(".pn", 0, 0, 1);
4709
nr(".ps", 0, 0, 1);
4710
nr(".psr", 0, 0, 1);
4711
nr(".s", 0, 0, 1);
4712
nr(".sr", 0, 0, 1);
4713
nr(".t", 0, 0, 1);
4714
nr(".u", 0, 0, 1);
4715
nr(".v", 0, 0, 1);
4716
nr(".vpt", 0, 0, 1);
4717
nr(".w", 0, 0, 1);
4718
nr(".warn", 0, 0, 1);
4719
nr(".x", 0, 0, 1);
4720
nr(".y", 0, 0, 1);
4721
nr(".z", 0, 0, 1);
4722
nr("c.", 0, 0, 1);
4723
nr("dl", 0, 0, 1);
4724
nr("dn", 0, 0, 1);
4725
nr("ln", 0, 0, 1);
4726
nr("nl", 0, 0, 1);
4727
nr("systat", 0, 0, 0);
4728
tm(NiL);
4729
hot("see", 1);
4730
hot("refer", 1);
4731
state.ec = state.eo = DEFAULT_ec;
4732
state.in = (unsigned char*)"";
4733
state.in_top = state.in_stack;
4734
state.out_top = state.out_stack;
4735
state.tag_top = state.tag_stack;
4736
state.pc = DEFAULT_pc;
4737
state.list = state.list_stack;
4738
state.ta[0] = 8;
4739
state.ta[1] = 0;
4740
state.t = 1;
4741
iop("stderr", 1)->sp = sfstderr;
4742
state.groff &= 1;
4743
}
4744
4745
/*
4746
* convert intermediate code in s to html on op
4747
*/
4748
4749
static const char* opt_align[] =
4750
{
4751
"LEFT", "CENTER", "RIGHT",
4752
};
4753
4754
static const char* opt_attribute[] =
4755
{
4756
"BACKGROUND", "HREF", "HREF", "ID", "NAME", "SIZE", "SRC",
4757
};
4758
4759
static const char* tag_name[] =
4760
{
4761
0, /* unknown */
4762
"A", /* OP_a */
4763
"BODY", /* OP_body */
4764
"BR", /* OP_br */
4765
0, /* OP_cc */
4766
"CENTER", /* OP_center */
4767
0, /* OP_comment */
4768
"DD", /* OP_dd */
4769
"DIV", /* OP_div */
4770
"DL", /* OP_dl */
4771
"DT", /* OP_dt */
4772
"FN", /* OP_fn */
4773
0, /* OP_ft1 */
4774
"EM", /* OP_ft2 */
4775
"STRONG", /* OP_ft3 */
4776
0, /* OP_ft4 */
4777
"TT", /* OP_ft5 */
4778
"H2", /* OP_h2 */
4779
"H3", /* OP_h3 */
4780
"H4", /* OP_h4 */
4781
"HEAD", /* OP_head */
4782
"HR", /* OP_hr */
4783
"HTML", /* OP_html */
4784
0, /* OP_link */
4785
"P", /* OP_p */
4786
"PRE", /* OP_pre */
4787
"FONT", /* OP_ps */
4788
"SUB", /* OP_sub */
4789
"SUP", /* OP_sup */
4790
"TABLE", /* OP_tab */
4791
"TD", /* OP_tab_data */
4792
"TH", /* OP_tab_head */
4793
"TR", /* OP_tab_row */
4794
"TITLE", /* OP_title */
4795
};
4796
4797
/*
4798
* emit tag and optionally check stack
4799
*/
4800
4801
static void
4802
tag(Sfio_t* op, int index, register int flags, int att, char* att_str, int att_num)
4803
{
4804
register int n;
4805
register int m;
4806
register unsigned char* sp;
4807
4808
if (index & OP_END)
4809
{
4810
index |= OP_LABEL;
4811
sp = state.tag_top;
4812
m = 1;
4813
for (;;)
4814
{
4815
if (sp <= state.tag_stack)
4816
{
4817
error(2, "tag stack underflow trying to match <%s>", tag_name[OP(index)]);
4818
sfprintf(sfstderr, "stack contents:\n");
4819
sp = state.tag_top;
4820
while (--sp >= state.tag_stack)
4821
sfprintf(sfstderr, "\t<%s%s%s>\n", (*sp & OP_END) ? "/" : "", tag_name[OP(*sp)], (*sp & OP_LABEL) ? " label=1" : "");
4822
return;
4823
}
4824
n = *--sp;
4825
if (!(n & OP_END))
4826
{
4827
if (n == OP_pre && OP(index) != OP_pre)
4828
{
4829
m = 0;
4830
break;
4831
}
4832
n |= OP_END|OP_LABEL;
4833
if (tag_name[OP(n)])
4834
{
4835
flags |= LIST;
4836
sfprintf(op, "</%s>", tag_name[OP(n)]);
4837
}
4838
*sp = n;
4839
}
4840
if (n == index)
4841
{
4842
*sp &= ~OP_LABEL;
4843
break;
4844
}
4845
m = 0;
4846
}
4847
if (m)
4848
{
4849
if ((flags & (LINE|LIST)) == (LINE|LIST))
4850
sfputc(op, '\n');
4851
state.tag_top = sp;
4852
}
4853
}
4854
else
4855
{
4856
if (flags & STACK)
4857
{
4858
if (state.tag_top >= &state.tag_stack[elementsof(state.tag_stack)])
4859
error(3, "tag stack overflow");
4860
*state.tag_top++ = tag_name[index] ? index : END(index);
4861
}
4862
if (tag_name[index])
4863
{
4864
sfprintf(op, "<%s", tag_name[index]);
4865
if (att && ((att & ATT_NUMBER) || att_str))
4866
{
4867
sfprintf(op, " %s=", opt_attribute[ATT_INDEX(att)]);
4868
if (att & ATT_NUMBER)
4869
{
4870
if (att == ATT_size)
4871
{
4872
if (att_num < 0)
4873
{
4874
att_num = -att_num;
4875
sfputc(op, '-');
4876
}
4877
else
4878
sfputc(op, '+');
4879
att_num = (att_num + 5) / 6;
4880
}
4881
sfprintf(op, "%d", att_num);
4882
}
4883
else
4884
sfprintf(op, "\"%s%s\"", att == ATT_lref ? "#" : "", att_str);
4885
}
4886
if (ARG_ATTR(flags))
4887
{
4888
if ((n = ARG_ALIGN(flags)) >= 0)
4889
sfprintf(op, " align=%s", opt_align[n]);
4890
if (flags & ARG_compact)
4891
sfputr(op, " COMPACT", -1);
4892
if (flags & ARG_wide)
4893
sfputr(op, " width=100%", -1);
4894
}
4895
if (index == OP_body)
4896
{
4897
if (state.background)
4898
sfprintf(op, " background=\"%s\"", state.background);
4899
if (state.logo)
4900
sfprintf(op, ">\n<CENTER><IMG src=\"%s\"></CENTER", state.logo);
4901
}
4902
sfputc(op, '>');
4903
if (flags & LINE)
4904
sfputc(op, '\n');
4905
}
4906
}
4907
}
4908
4909
/*
4910
* if OP_dl follows OP_ft,OP_ps then do it now
4911
*/
4912
4913
static void
4914
peeklist(Sfio_t* op, register char* s)
4915
{
4916
for (;;)
4917
{
4918
switch (*s++)
4919
{
4920
case 0:
4921
break;
4922
case CODE_0:
4923
continue;
4924
case CODE_2:
4925
s++;
4926
/*FALLTHROUGH*/
4927
case CODE_1:
4928
switch (*s++)
4929
{
4930
case OP_ft1:
4931
case OP_ft2:
4932
case OP_ft3:
4933
case OP_ft4:
4934
case OP_ft5:
4935
case OP_ps:
4936
continue;
4937
case OP_dl:
4938
*--s = CODE_0;
4939
*--s = CODE_0;
4940
tag(op, OP_dl, STACK|ARG_compact, 0, NiL, 0);
4941
continue;
4942
}
4943
break;
4944
case ' ':
4945
case '\t':
4946
case '\n':
4947
continue;
4948
default:
4949
break;
4950
}
4951
break;
4952
}
4953
}
4954
4955
#define P() \
4956
col = br = p = 0
4957
4958
#define DATA() \
4959
do \
4960
{ \
4961
if (li) \
4962
{ \
4963
if (li == 1) \
4964
li = 0; \
4965
P(); \
4966
} \
4967
else if (p) \
4968
{ \
4969
P(); \
4970
sfputr(op, "<P>", '\n'); \
4971
} \
4972
else if (br) \
4973
{ \
4974
P(); \
4975
sfputr(op, "<BR>", '\n'); \
4976
} \
4977
} while (0)
4978
4979
static void
4980
html(register unsigned char* s, Sfio_t* op)
4981
{
4982
register int c;
4983
register int br = 0;
4984
register int col = 0;
4985
register int li = 0;
4986
register int p = 0;
4987
register int nf = 0;
4988
register unsigned char* v;
4989
unsigned char* t;
4990
unsigned char* u;
4991
int n;
4992
int m;
4993
int ta;
4994
int ts;
4995
int ft = 1;
4996
int hot = 0;
4997
int label = 0;
4998
int ps = DEFAULT_ps;
4999
int ps_set = 0;
5000
Dir_t* x;
5001
Sfio_t* subject;
5002
char a[2];
5003
5004
sfputr(op, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">", '\n');
5005
tag(op, OP_html, STACK|LINE, 0, NiL, 0);
5006
tag(op, OP_head, STACK|LINE, 0, NiL, 0);
5007
t = (unsigned char*)strchr(usage, '\n') + 5;
5008
sfprintf(op, "<META name=\"generator\" content=\"%-.*s", strchr((char*)t, '\n') - (char*)t, t);
5009
for (x = state.macros; x; x = x->next)
5010
sfprintf(op, " -m%s", x->name);
5011
sfputr(op, "\">\n", -1);
5012
tag(op, OP_title, STACK, 0, NiL, 0);
5013
if (!(subject = sfstropen()))
5014
error(ERROR_SYSTEM|3, "out of space [subject]");
5015
if (state.package)
5016
sfputr(subject, state.package, ' ');
5017
if (state.title)
5018
sfputr(subject, state.title, -1);
5019
else
5020
{
5021
if (state.input)
5022
{
5023
if (t = (unsigned char*)strrchr(state.input, '/'))
5024
t++;
5025
else
5026
t = (unsigned char*)state.input;
5027
sfputr(subject, (char*)t, -1);
5028
}
5029
if (state.macros)
5030
sfprintf(subject, " m%s document", state.macros->name);
5031
}
5032
sfputr(op, use(subject), -1);
5033
tag(op, END(OP_title), STACK|LINE, 0, NiL, 0);
5034
if (state.author)
5035
sfprintf(op, "<AUTHOR>%s</AUTHOR>\n", state.author);
5036
if (!state.head)
5037
{
5038
tag(op, END(OP_head), STACK|LINE, 0, NiL, 0);
5039
tag(op, OP_body, STACK|LINE, 0, NiL, 0);
5040
}
5041
for (;;)
5042
{
5043
switch (*s++)
5044
{
5045
case 0:
5046
break;
5047
case '&':
5048
DATA();
5049
col++;
5050
sfputr(op, "&amp;", -1);
5051
continue;
5052
case '<':
5053
DATA();
5054
col++;
5055
sfputr(op, "&lt;", -1);
5056
continue;
5057
case '>':
5058
DATA();
5059
col++;
5060
sfputr(op, "&gt;", -1);
5061
continue;
5062
case CODE_0:
5063
continue;
5064
case CODE_2:
5065
c = *s++;
5066
/*FALLTHROUGH*/
5067
case CODE_1:
5068
if (!nf)
5069
{
5070
n = *s ^ OP_END;
5071
for (v = s + 1;;)
5072
{
5073
switch (*v++)
5074
{
5075
case '\n':
5076
case ' ':
5077
case '\t':
5078
continue;
5079
case CODE_0:
5080
continue;
5081
case CODE_1:
5082
if ((m = *v++) == n)
5083
{
5084
n = 0;
5085
s = v;
5086
break;
5087
}
5088
else if (m != OP_br && m != OP_p)
5089
break;
5090
continue;
5091
case CODE_2:
5092
if (*v++ != ' ' || *v++ != OP_cc)
5093
break;
5094
continue;
5095
default:
5096
break;
5097
}
5098
break;
5099
}
5100
if (!n)
5101
continue;
5102
}
5103
switch (m = *s++)
5104
{
5105
case END(OP_a):
5106
tag(op, m, STACK, 0, NiL, 0);
5107
*--s = a[1];
5108
*--s = a[0];
5109
break;
5110
case OP_body:
5111
tag(op, m, STACK|LINE, 0, NiL, 0);
5112
tag(op, OP_hr, LINE, 0, NiL, 0);
5113
col = 0;
5114
goto compact;
5115
case OP_br:
5116
while (*s == ' ')
5117
s++;
5118
if (!li)
5119
{
5120
if (nf)
5121
goto compact;
5122
else
5123
br++;
5124
}
5125
break;
5126
case OP_cc:
5127
DATA();
5128
sfputc(op, '&');
5129
switch (c)
5130
{
5131
case '&':
5132
sfputr(op, "amp", -1);
5133
break;
5134
case '<':
5135
sfputr(op, "lt", -1);
5136
break;
5137
case '>':
5138
sfputr(op, "gt", -1);
5139
break;
5140
case ' ':
5141
sfputr(op, "nbsp", -1);
5142
break;
5143
default:
5144
sfprintf(op, "#%03d", c);
5145
break;
5146
}
5147
sfputc(op, ';');
5148
col++;
5149
break;
5150
case OP_center:
5151
if (col)
5152
{
5153
col = 0;
5154
sfputc(op, '\n');
5155
}
5156
tag(op, m, STACK|c, 0, NiL, 0);
5157
if (!state.head)
5158
{
5159
tag(op, OP_h2, STACK, 0, NiL, 0);
5160
for (;;)
5161
{
5162
switch (n = *s++)
5163
{
5164
case 0:
5165
case '\n':
5166
case CODE_1:
5167
case CODE_2:
5168
case CODE_n:
5169
s--;
5170
break;
5171
default:
5172
sfputc(op, n);
5173
continue;
5174
}
5175
break;
5176
}
5177
tag(op, END(OP_h2), STACK, 0, NiL, 0);
5178
tag(op, OP_h4, STACK, 0, NiL, 0);
5179
}
5180
break;
5181
case END(OP_center):
5182
col = 0;
5183
if (!state.head)
5184
{
5185
state.head = 1;
5186
tag(op, END(OP_h4), STACK, 0, NiL, 0);
5187
}
5188
tag(op, m, STACK, 0, NiL, 0);
5189
break;
5190
case LABEL(OP_dd):
5191
tag(op, END(OP_a), STACK, 0, NiL, 0);
5192
tag(op, END(OP_h3), STACK, 0, NiL, 0);
5193
/*FALLTHROUGH*/
5194
case OP_dd:
5195
li = 1;
5196
if (col)
5197
{
5198
col = 0;
5199
sfputc(op, '\n');
5200
}
5201
tag(op, OP_dd, 0, 0, NiL, 0);
5202
break;
5203
case OP_div:
5204
if (col)
5205
{
5206
col = 0;
5207
sfputc(op, '\n');
5208
}
5209
tag(op, m, STACK|c, 0, NiL, 0);
5210
break;
5211
case END(OP_div):
5212
col = 0;
5213
tag(op, m, STACK, 0, NiL, 0);
5214
break;
5215
case OP_dl:
5216
li = 0;
5217
if (col)
5218
{
5219
col = 0;
5220
sfputc(op, '\n');
5221
}
5222
tag(op, m, STACK|LINE|ARG_compact, 0, NiL, 0);
5223
break;
5224
case END(OP_dl):
5225
v = s;
5226
for (;;)
5227
{
5228
switch (*v++)
5229
{
5230
case CODE_0:
5231
continue;
5232
case CODE_1:
5233
switch (*v++)
5234
{
5235
case OP_dl:
5236
*(v - 2) = *(v - 1) = CODE_0;
5237
v = 0;
5238
break;
5239
default:
5240
continue;
5241
}
5242
break;
5243
case CODE_2:
5244
v += 2;
5245
continue;
5246
default:
5247
break;
5248
}
5249
break;
5250
}
5251
if (v)
5252
{
5253
li = 0;
5254
col = 0;
5255
tag(op, m, STACK|LINE, 0, NiL, 0);
5256
}
5257
break;
5258
case LABEL(OP_dt):
5259
label = 1;
5260
n = 1;
5261
/*FALLTHROUGH*/
5262
case OP_dt:
5263
if (col)
5264
{
5265
col = 0;
5266
sfputc(op, '\n');
5267
}
5268
if (p)
5269
{
5270
P();
5271
tag(op, OP_p, LINE, 0, NiL, 0);
5272
}
5273
v = s;
5274
for (;;)
5275
{
5276
switch (*v++)
5277
{
5278
case CODE_0:
5279
continue;
5280
case CODE_2:
5281
v++;
5282
/*FALLTHROUGH*/
5283
case CODE_1:
5284
switch (*v++)
5285
{
5286
case OP_dl:
5287
*(v - 2) = *(v - 1) = CODE_0;
5288
tag(op, OP_dl, STACK|LINE|ARG_compact, 0, NiL, 0);
5289
if (!label)
5290
break;
5291
continue;
5292
case OP_dd:
5293
if (label)
5294
*(v - 1) = LABEL(OP_dd);
5295
break;
5296
default:
5297
continue;
5298
}
5299
break;
5300
case ' ':
5301
case '\t':
5302
case '\n':
5303
if (!label)
5304
break;
5305
if (!n)
5306
{
5307
n = 1;
5308
sfputc(state.tmp, ' ');
5309
}
5310
continue;
5311
case '"':
5312
if (!label)
5313
break;
5314
continue;
5315
default:
5316
if (!label)
5317
break;
5318
n = 0;
5319
sfputc(state.tmp, *(v - 1));
5320
continue;
5321
}
5322
break;
5323
}
5324
li = 2;
5325
tag(op, OP_dt, LINE, 0, NiL, 0);
5326
if (label)
5327
{
5328
label = 0;
5329
n = sfstrtell(state.tmp);
5330
v = (unsigned char*)use(state.tmp);
5331
while (--n > 0 && (isspace(v[n]) || v[n] == '.'));
5332
v[n + 1] = 0;
5333
if (isdigit(*v))
5334
tag(op, OP_hr, LINE, 0, NiL, 0);
5335
tag(op, OP_h3, STACK, 0, NiL, 0);
5336
tag(op, OP_a, STACK, ATT_name, (char*)v, 0);
5337
}
5338
break;
5339
case END(OP_fn):
5340
tag(op, m, STACK, 0, NiL, 0);
5341
break;
5342
case OP_ft1:
5343
case OP_ft2:
5344
case OP_ft3:
5345
case OP_ft4:
5346
case OP_ft5:
5347
if (hot)
5348
{
5349
int ob = 0;
5350
int cb = 0;
5351
int p = 0;
5352
int r = ATT_lref;
5353
int z = 0;
5354
5355
v = s;
5356
for (;;)
5357
{
5358
switch (n = *v++)
5359
{
5360
case 0:
5361
hot = 0;
5362
break;
5363
case CODE_0:
5364
continue;
5365
case CODE_2:
5366
v++;
5367
/*FALLTHROUGH*/
5368
case CODE_1:
5369
n = *v++;
5370
if (!p && n >= OP_ft1 && n <= OP_ft5)
5371
p = -1;
5372
continue;
5373
case '(':
5374
case '[':
5375
case '{':
5376
case '<':
5377
if (n == ob)
5378
p++;
5379
else if (p <= 0)
5380
{
5381
r = ATT_href;
5382
p = 1;
5383
switch (ob = n)
5384
{
5385
case '(':
5386
cb = ')';
5387
break;
5388
case '[':
5389
cb = ']';
5390
break;
5391
case '{':
5392
cb = '}';
5393
break;
5394
case '<':
5395
cb = '>';
5396
break;
5397
}
5398
}
5399
z = 0;
5400
sfputc(state.tmp, n);
5401
continue;
5402
case ')':
5403
case ']':
5404
case '}':
5405
case '>':
5406
if (p <= 0)
5407
{
5408
v--;
5409
break;
5410
}
5411
z = 0;
5412
sfputc(state.tmp, n);
5413
if (n == cb && !--p)
5414
break;
5415
continue;
5416
case ' ':
5417
case '\t':
5418
case '\n':
5419
if (!z)
5420
{
5421
z = 1;
5422
sfputc(state.tmp, ' ');
5423
}
5424
continue;
5425
case '"':
5426
continue;
5427
default:
5428
if (p < 0)
5429
{
5430
v--;
5431
break;
5432
}
5433
z = 0;
5434
sfputc(state.tmp, n);
5435
continue;
5436
}
5437
break;
5438
}
5439
n = sfstrtell(state.tmp);
5440
if (!*(t = (unsigned char*)use(state.tmp)))
5441
hot = 0;
5442
if (hot)
5443
{
5444
hot = 0;
5445
while (--n > 0 && (isspace(t[n]) || t[n] == '.'));
5446
t[n + 1] = 0;
5447
tag(op, OP_a, STACK, r, (char*)t, 0);
5448
a[0] = v[0];
5449
v[0] = CODE_1;
5450
a[1] = v[1];
5451
v[1] = END(OP_a);
5452
}
5453
}
5454
c = m - OP_ft;
5455
if (c != ft)
5456
{
5457
peeklist(op, (char*)s);
5458
if (ft != 1)
5459
tag(op, END(OP_ft + ft), STACK, 0, NiL, 0);
5460
ft = c;
5461
if (ft != 1)
5462
tag(op, OP_ft + ft, STACK, 0, NiL, 0);
5463
}
5464
break;
5465
case OP_h2:
5466
case OP_h3:
5467
case OP_h4:
5468
case END(OP_h2):
5469
case END(OP_h3):
5470
case END(OP_h4):
5471
tag(op, m, STACK, 0, NiL, 0);
5472
break;
5473
case OP_head:
5474
case END(OP_head):
5475
col = 0;
5476
tag(op, m, STACK|LINE, 0, NiL, 0);
5477
goto compact;
5478
case OP_hr:
5479
col = 0;
5480
tag(op, OP_hr, LINE, 0, NiL, 0);
5481
goto compact;
5482
case OP_p:
5483
while (*s == ' ')
5484
s++;
5485
if (c)
5486
{
5487
if (col)
5488
{
5489
col = 0;
5490
sfputc(op, '\n');
5491
}
5492
P();
5493
tag(op, m, STACK|c, 0, NiL, 0);
5494
}
5495
else if (!li)
5496
{
5497
if (nf)
5498
goto compact;
5499
else
5500
p++;
5501
}
5502
break;
5503
case END(OP_p):
5504
col = 0;
5505
tag(op, m, STACK, 0, NiL, 0);
5506
break;
5507
case OP_pre:
5508
if (!nf)
5509
{
5510
nf = 03;
5511
tag(op, m, STACK, 0, NiL, 0);
5512
}
5513
goto compact;
5514
case END(OP_pre):
5515
if (nf)
5516
{
5517
nf = 02;
5518
tag(op, m, STACK, 0, NiL, 0);
5519
}
5520
goto compact;
5521
case OP_ps:
5522
if (c != ps)
5523
{
5524
peeklist(op, (char*)s);
5525
if (ps_set)
5526
{
5527
ps = ps_set;
5528
ps_set = 0;
5529
tag(op, END(OP_ps), STACK, 0, NiL, 0);
5530
}
5531
if (n = c - ps)
5532
{
5533
ps_set = ps;
5534
ps = c;
5535
tag(op, OP_ps, STACK, ATT_size, NiL, n);
5536
}
5537
}
5538
break;
5539
case OP_sub:
5540
case END(OP_sub):
5541
case OP_sup:
5542
case END(OP_sup):
5543
tag(op, m, STACK, 0, NiL, 0);
5544
break;
5545
case OP_tab:
5546
if (col)
5547
{
5548
col = 0;
5549
sfputc(op, '\n');
5550
}
5551
tag(op, m, STACK|c, 0, NiL, 0);
5552
goto compact;
5553
case END(OP_tab):
5554
tag(op, m, STACK|LINE, 0, NiL, 0);
5555
break;
5556
case OP_tab_data:
5557
case OP_tab_head:
5558
case OP_tab_row:
5559
tag(op, m, c, 0, NiL, 0);
5560
break;
5561
default:
5562
if (!(v = (unsigned char*)tag_name[OP(m)]))
5563
{
5564
sfprintf(state.tmp, "(%d)", OP(m));
5565
v = (unsigned char*)use(state.tmp);
5566
}
5567
error(2, "internal error: <%s%s%s %d> ignored", (m & OP_END) ? "/" : "", v, (m & OP_LABEL) ? " label=" : "", c);
5568
break;
5569
compact:
5570
if (col)
5571
sfputc(op, '\n');
5572
P();
5573
v = s;
5574
for (;;)
5575
{
5576
switch (*s++)
5577
{
5578
case 0:
5579
break;
5580
case '\n':
5581
p++;
5582
col = 0;
5583
v = s;
5584
continue;
5585
case ' ':
5586
case '\t':
5587
if (!nf)
5588
break;
5589
col = 1;
5590
continue;
5591
case CODE_0:
5592
continue;
5593
case CODE_1:
5594
switch (*s)
5595
{
5596
case OP_pre:
5597
if (!nf)
5598
break;
5599
/*FALLTHROUGH*/
5600
case OP_br:
5601
case OP_p:
5602
s++;
5603
p++;
5604
col = 0;
5605
v = s;
5606
continue;
5607
case OP_body:
5608
p = 0;
5609
col = 0;
5610
break;
5611
}
5612
break;
5613
}
5614
if (col)
5615
{
5616
col = 0;
5617
s = v;
5618
}
5619
else
5620
s--;
5621
break;
5622
}
5623
if (nf > 1)
5624
nf &= 01;
5625
else if (nf && p)
5626
sfputc(op, '\n');
5627
p = 0;
5628
break;
5629
}
5630
c = 0;
5631
continue;
5632
case CODE_n:
5633
n = *s++;
5634
if (n & 0200)
5635
{
5636
n = (n & 0177) << 8;
5637
n |= *s++;
5638
}
5639
c = *s++;
5640
v = s;
5641
s += n;
5642
switch (c)
5643
{
5644
case OP_comment:
5645
if (col)
5646
{
5647
col = 0;
5648
sfputc(op, '\n');
5649
}
5650
sfprintf(op, "<!--\"%s\"-->\n", v);
5651
break;
5652
case OP_fn:
5653
tag(op, c, STACK, ATT_id, (char*)v, 0);
5654
break;
5655
case OP_link:
5656
DATA();
5657
if (u = (unsigned char*)strchr((char*)v, '\t'))
5658
*u++ = 0;
5659
else
5660
u = 0;
5661
t = v;
5662
while (isdigit(*v))
5663
v++;
5664
while (isalpha(*v))
5665
v++;
5666
if (*v == ':' || *v == '/' || *v == '.' || *(v + 1) == '/')
5667
{
5668
if (!u)
5669
u = v + 1;
5670
v = (unsigned char*)"";
5671
}
5672
else
5673
{
5674
if (!u)
5675
u = t;
5676
v = (unsigned char*)"#";
5677
}
5678
sfprintf(op, "<A href=\"%s%s\">%s</A>", v, t, u);
5679
break;
5680
case LABEL(OP_link):
5681
DATA();
5682
if (u = (unsigned char*)strchr((char*)v, '\t'))
5683
*u++ = 0;
5684
else
5685
u = v;
5686
sfprintf(op, "<A name=\"%s\">%s</A>", v, u);
5687
break;
5688
case OP_ta:
5689
strcpy((char*)state.ta, (char*)v);
5690
break;
5691
case OP_RAW:
5692
DATA();
5693
if (col)
5694
{
5695
col = 0;
5696
sfputc(op, '\n');
5697
}
5698
sfputr(op, (char*)v, '\n');
5699
break;
5700
}
5701
continue;
5702
case ' ':
5703
if (nf)
5704
{
5705
col++;
5706
sfputc(op, *(s - 1));
5707
}
5708
else
5709
{
5710
while (isspace(*s))
5711
s++;
5712
if (col >= 70)
5713
{
5714
col = 0;
5715
sfputc(op, '\n');
5716
}
5717
else if (col > 0)
5718
{
5719
col++;
5720
sfputc(op, ' ');
5721
}
5722
}
5723
continue;
5724
case '\t':
5725
if (nf)
5726
{
5727
ta = state.ta[ts = 0];
5728
while (col >= ta)
5729
{
5730
if (state.ta[ts+1])
5731
ts++;
5732
ta += state.ta[ts];
5733
}
5734
do
5735
{
5736
sfputc(op, ' ');
5737
} while (++col < ta);
5738
}
5739
else
5740
{
5741
col++;
5742
sfputc(op, '\t');
5743
}
5744
continue;
5745
case '\n':
5746
if (nf)
5747
{
5748
v = s;
5749
col = 0;
5750
for (;;)
5751
{
5752
switch (*v++)
5753
{
5754
case 0:
5755
break;
5756
case '\n':
5757
continue;
5758
case ' ':
5759
case '\t':
5760
continue;
5761
case CODE_0:
5762
continue;
5763
case CODE_1:
5764
switch (*v++)
5765
{
5766
case OP_br:
5767
case OP_p:
5768
case OP_pre:
5769
continue;
5770
case END(OP_pre):
5771
col = 1;
5772
s = v - 2;
5773
break;
5774
}
5775
break;
5776
}
5777
break;
5778
}
5779
if (col)
5780
{
5781
col = 0;
5782
continue;
5783
}
5784
sfputc(op, '\n');
5785
}
5786
else
5787
{
5788
while (isspace(*s))
5789
s++;
5790
if (col >= 70)
5791
{
5792
col = 0;
5793
sfputc(op, '\n');
5794
}
5795
else if (col > 0)
5796
{
5797
col++;
5798
sfputc(op, ' ');
5799
}
5800
}
5801
continue;
5802
case '(':
5803
if (hot)
5804
hot = 0;
5805
else
5806
for (x = state.hot; x; x = x->next)
5807
{
5808
v = s;
5809
u = (unsigned char*)x->name;
5810
do
5811
{
5812
if (!*u)
5813
{
5814
if (isspace(*v))
5815
{
5816
hot = 1;
5817
goto data;
5818
}
5819
break;
5820
}
5821
} while (*u++ == *v++);
5822
}
5823
goto data;
5824
case ')':
5825
hot = 0;
5826
goto data;
5827
case '.':
5828
if (!nf && isspace(*s))
5829
{
5830
while (isspace(*s))
5831
s++;
5832
col = 0;
5833
sfputc(op, '.');
5834
sfputc(op, '\n');
5835
continue;
5836
}
5837
/*FALLTHROUGH*/
5838
default:
5839
data:
5840
DATA();
5841
col++;
5842
sfputc(op, *(s - 1));
5843
continue;
5844
}
5845
break;
5846
}
5847
if (col)
5848
sfputc(op, '\n');
5849
if (state.mailto)
5850
sfprintf(op, "<P>Send comments and suggestions to <A href=\"mailto:%s?subject=%s\">%s</A>.\n", state.mailto, state.mailto, sfstrbase(subject));
5851
if (state.author || state.corporation || state.company || state.location)
5852
{
5853
t = (unsigned char*)"<P>";
5854
u = (unsigned char*)"<BR>";
5855
if (state.author)
5856
{
5857
sfprintf(op, "%s%s\n", t, state.author);
5858
t = u;
5859
}
5860
if (state.organization)
5861
{
5862
sfprintf(op, "%s%s\n", t, state.organization);
5863
t = u;
5864
}
5865
if (state.corporation || state.company)
5866
{
5867
sfputr(op, (char*)t, -1);
5868
t = u;
5869
if (state.corporation)
5870
sfputr(op, state.corporation, state.company ? ' ' : '\n');
5871
if (state.company)
5872
sfputr(op, state.company, '\n');
5873
}
5874
if (state.address)
5875
{
5876
sfprintf(op, "%s%s\n", t, state.address);
5877
t = u;
5878
}
5879
if (state.location)
5880
{
5881
sfprintf(op, "%s%s\n", t, state.location);
5882
t = u;
5883
}
5884
if (state.phone)
5885
{
5886
sfprintf(op, "%s%s\n", t, state.phone);
5887
t = u;
5888
}
5889
}
5890
sfstrclose(subject);
5891
if (!state.footer)
5892
sfprintf(op, "<P>%s\n", fmttime("%B %d, %Y", state.date));
5893
if (state.toolbar && (subject = find(state.toolbar, NiL, 1)))
5894
{
5895
sfmove(subject, sfstdout, SF_UNBOUND, -1);
5896
sfclose(subject);
5897
}
5898
tag(op, END(OP_body), STACK, 0, NiL, 0);
5899
tag(op, END(OP_html), STACK, 0, NiL, 0);
5900
sfputc(op, '\n');
5901
}
5902
5903
int
5904
main(int argc, char** argv)
5905
{
5906
register int n;
5907
register char* s;
5908
char* v;
5909
Dir_t* x;
5910
Dir_t* lastdir;
5911
Dir_t* lastmac;
5912
Sfio_t* ip;
5913
Sfio_t* op;
5914
Sfio_t* script;
5915
5916
NoP(argc);
5917
error_info.id = "troff2html";
5918
state.dirs = lastdir = &dot;
5919
init();
5920
script = 0;
5921
for (;;)
5922
{
5923
switch (optget(argv, usage))
5924
{
5925
case 0:
5926
break;
5927
case 'i':
5928
if (!(op = sfopen(NiL, opt_info.arg, "r")))
5929
error(ERROR_SYSTEM|2, "%s: cannot read", opt_info.arg);
5930
else
5931
{
5932
if (!(s = sfreserve(op, SF_UNBOUND, 0)) || (n = sfvalue(op)) <= 0 || s[n - 1] != '\n')
5933
error(1, "%s: invalid info file", opt_info.arg);
5934
else
5935
{
5936
s[n] = 0;
5937
stropt(s, options, sizeof(*options), setopt, NiL);
5938
}
5939
sfclose(op);
5940
}
5941
continue;
5942
case 'm':
5943
if (!(x = newof(0, Dir_t, 1, 0)))
5944
error(ERROR_SYSTEM|3, "out of space [macros]");
5945
x->name = opt_info.arg;
5946
if (state.macros)
5947
lastmac = lastmac->next = x;
5948
else
5949
state.macros = lastmac = x;
5950
continue;
5951
case 'r':
5952
if (*(s = opt_info.arg))
5953
{
5954
opt_info.num = expression(s + 1, NiL, 0);
5955
s[1] = 0;
5956
nr(s, opt_info.num, 0, 0);
5957
}
5958
continue;
5959
case 's':
5960
if (!script && !(script = sfstropen()))
5961
error(ERROR_SYSTEM|3, "out of space [script]");
5962
sfputr(script, opt_info.arg, '\n');
5963
continue;
5964
case 'v':
5965
state.verbose = 1;
5966
continue;
5967
case 'I':
5968
if (streq(opt_info.arg, "-"))
5969
dot.name[0] = 0;
5970
else if (!(x = newof(0, Dir_t, 1, 0)))
5971
error(ERROR_SYSTEM|3, "out of space [dir]");
5972
else
5973
{
5974
x->name = opt_info.arg;
5975
lastdir = lastdir->next = x;
5976
}
5977
continue;
5978
case '?':
5979
error(ERROR_USAGE|4, "%s", opt_info.arg);
5980
continue;
5981
case ':':
5982
if (opt_info.name[1] != '-')
5983
{
5984
error(2, "%s", opt_info.arg);
5985
continue;
5986
}
5987
if (!(v = strchr(argv[opt_info.index - 1], '=')))
5988
v = opt_info.name + 2;
5989
else if (!(v = sfprints("%s=%s", opt_info.name + 2, fmtquote(v + 1, "\"", "\"", strlen(v + 1), FMT_ALWAYS))))
5990
error(ERROR_SYSTEM|3, "out of space");
5991
stropt(v, options, sizeof(*options), setopt, options);
5992
continue;
5993
}
5994
break;
5995
}
5996
if (!dot.name[0])
5997
state.dirs = state.dirs->next;
5998
argv += opt_info.index;
5999
if (error_info.errors)
6000
error(ERROR_USAGE|4, "%s", optusage(NiL));
6001
if (!(op = sfstropen()))
6002
error(ERROR_SYSTEM|3, "out of space [output]");
6003
if (script)
6004
{
6005
pushin("script", 1, NiL, use(script), NiL);
6006
process(NiL, NiL, op);
6007
sfstrclose(script);
6008
}
6009
for (x = state.macros; x; x = x->next)
6010
if (ip = find(x->name, &v, -1))
6011
process(v, ip, op);
6012
if (!(state.input = *argv))
6013
process(NiL, sfstdin, op);
6014
else
6015
while (s = *argv++)
6016
if (ip = find(s, &v, 1))
6017
process(v, ip, op);
6018
if (state.out)
6019
{
6020
if (state.it.center)
6021
{
6022
state.it.center = 0;
6023
code_1(END(OP_center));
6024
}
6025
while (state.list > state.list_stack)
6026
{
6027
if (state.list->dl)
6028
code_1(END(OP_dl));
6029
state.list--;
6030
}
6031
code_1(OP_hr);
6032
trigger(&state.fini);
6033
process(NiL, NiL, op);
6034
html((unsigned char*)use(op), sfstdout);
6035
}
6036
exit(error_info.errors != 0);
6037
}
6038
6039