Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <ctype.h>
4
#include <bio.h>
5
 
6
enum
7
{
8
	SSIZE = 10,
9
 
10
	Maxnh=	8,		/* highest NH level */
11
	HH=	4,		/* heading level used for SH and NH */
12
	Maxmstack=	10,	/* deepest macro/string nesting */
13
	Narg=	20,		/* max args to a macro */
14
	Maxsstack=	5,	/* deepest nesting of .so's */
15
	Nline=	1024,
16
	Maxget= 10,
17
	Maxif = 20,
18
	Maxfsp = 100,
19
 
20
	/* list types */
21
	Lordered = 1,
22
	Lunordered,
23
	Ldef,
24
	Lother,
25
};
26
 
27
char *delim = "$$";
28
char *basename;
29
char *title;
30
int eqnmode;
31
 
32
int 	quiet;
33
float	indent; /* from .in */
34
Biobuf	bout;
35
int	isup;
36
int	isdown;
37
int	debug;
38
 
39
int nh[Maxnh];
40
int ifwastrue[Maxif];
41
 
42
int list, listnum, example;
43
int hangingau, hangingdt, hanginghead, hangingcenter;
44
int indirective, paragraph, sol, titleseen, ignore_nl, weBref;
45
void dohangingcenter(void);
46
 
47
typedef struct Goobie Goobie;
48
typedef struct Goobieif Goobieif;
49
struct Goobie
50
{
51
	char *name;
52
	void (*f)(int, char**);
53
};
54
 
55
typedef void F(int, char**);
56
typedef void Fif(char*, char*);
57
 
58
struct Goobieif
59
{
60
	char *name;
61
	Fif *f;
62
};
63
 
64
/* if, ie */
65
Fif g_as, g_ds, g_el, g_ie, g_if;
66
Goobieif gtabif[] = {
67
	{ "as", g_as },
68
	{ "ds", g_ds },
69
	{ "if", g_if },
70
	{ "ie", g_ie },
71
	{ "el", g_el },
72
	{ nil, nil },
73
	};
74
 
75
/* pseudo ops */
76
F g_notyet, g_ignore, g_hrule, g_startgif;
77
 
78
/* ms macros */
79
F g_AU, g_B, g_BI, g_CW, g_I, g_IP, g_LP, g_PP, g_SH, g_NH;
80
F g_P1, g_P2, g_TL, g_R, g_AB, g_AE, g_EQ, g_TS, g_TE, g_FS, g_FE;
81
F g_PY, g_IH, g_MH, g_HO, g_BX, g_QS, g_QE, g_RS, g_RE;
82
 
83
/* pictures macro */
84
F g_BP;
85
 
86
/* real troff */
87
F g_br, g_ft, g_sp, g_de, g_lf, g_so, g_rm, g_in;
88
F g_nr, g_ig, g_RT, g_BS, g_BE, g_LB, g_ta;
89
 
90
/* macros to include ML in output */
91
F g__H, g__T;
92
 
93
Goobie gtab[] =
94
{
95
	{ "_T", g__T, },
96
	{ "_H", g__H, },
97
	{ "1C",	g_ignore, },
98
	{ "2C",	g_ignore, },
99
	{ "AB", g_AB, },
100
	{ "AE", g_AE, },
101
	{ "AI", g_ignore, },
102
	{ "AU", g_AU, },
103
	{ "B",	g_B, },
104
	{ "B1", g_hrule, },
105
	{ "B2", g_hrule, },
106
	{ "BI",	g_BI, },
107
	{ "BP",	g_BP, },
108
	{ "BT",	g_ignore, },
109
	{ "BX",	g_BX, },
110
	{ "CW",	g_CW, },
111
	{ "CT",	g_ignore, },
112
	{ "DA",	g_ignore, },
113
	{ "DE",	g_P2, },
114
	{ "DS",	g_P1, },
115
	{ "EG",	g_ignore, },
116
	{ "EN",	g_ignore, },
117
	{ "EQ",	g_startgif, },
118
	{ "FE",	g_FE, },
119
	{ "FP",	g_ignore, },
120
	{ "FS",	g_FS, },
121
	{ "HO",	g_HO, },
122
	{ "I",	g_I, },
123
	{ "IH",	g_IH, },
124
	{ "IM",	g_ignore, },
125
	{ "IP",	g_IP, },
126
	{ "KE",	g_ignore, },
127
	{ "KF",	g_ignore, },
128
	{ "KS",	g_ignore, },
129
	{ "LG",	g_ignore, },
130
	{ "LP",	g_LP, },
131
	{ "LT",	g_ignore, },
132
	{ "MF",	g_ignore, },
133
	{ "MH",	g_MH, },
134
	{ "MR",	g_ignore, },
135
	{ "ND",	g_ignore, },
136
	{ "NH",	g_NH, },
137
	{ "NL",	g_ignore, },
138
	{ "P1",	g_P1, },
139
	{ "P2",	g_P2, },
140
	{ "PE",	g_ignore, },
141
	{ "PF",	g_ignore, },
142
	{ "PP",	g_PP, },
143
	{ "PS",	g_startgif, },
144
	{ "PY",	g_PY, },
145
	{ "QE",	g_QE, },
146
	{ "QP",	g_QS, },
147
	{ "QS",	g_QS, },
148
	{ "R",		g_R, },
149
	{ "RE",	g_RE, },
150
	{ "RP",	g_ignore, },
151
	{ "RS",	g_RS, },
152
	{ "SG",	g_ignore, },
153
	{ "SH",	g_SH, },
154
	{ "SM",	g_ignore, },
155
	{ "TA",	g_ignore, },
156
	{ "TE",	g_ignore, },
157
	{ "TH",	g_TL, },
158
	{ "TL",	g_TL, },
159
	{ "TM",	g_ignore, },
160
	{ "TR",	g_ignore, },
161
	{ "TS",	g_startgif, },
162
	{ "UL",	g_I, },
163
	{ "UX",	g_ignore, },
164
	{ "WH",	g_ignore, },
165
	{ "RT", 	g_RT, },
166
 
167
	{ "br",	g_br, },
168
	{ "ti",		g_br, },
169
	{ "nf",	g_P1, },
170
	{ "fi",		g_P2, },
171
	{ "ft",		g_ft, },
172
	{ "sp", 	g_sp, },
173
	{ "rm", 	g_rm, },
174
	{ "de", 	g_de, },
175
	{ "am", 	g_de, },
176
	{ "lf", 	g_lf, },
177
	{ "so", 	g_so, },
178
	{ "ps", 	g_ignore },
179
	{ "vs", 	g_ignore },
180
	{ "nr", 	g_nr },
181
	{ "in", 	g_in },
182
	{ "ne", 	g_ignore },
183
	{ "ig", 	g_ig },
184
	{ "BS", 	g_BS },
185
	{ "BE", 	g_BE },
186
	{ "LB", 	g_LB },
187
	{ nil, nil },
188
};
189
 
190
typedef struct Entity Entity;
191
struct Entity
192
{
193
	char *name;
194
	int value;
195
};
196
 
197
Entity entity[] =
198
{
199
	{ "&#SPACE;",	L' ', },
200
	{ "&#RS;",		L'\n', },
201
	{ "&#RE;",		L'\r', },
202
	{ "&quot;",		L'"', },
203
	{ "&AElig;",	L'Æ', },
204
	{ "&Aacute;",	L'Á', },
205
	{ "&Acirc;",	L'Â', },
206
	{ "&Agrave;",	L'À', },
207
	{ "&Aring;",	L'Å', },
208
	{ "&Atilde;",	L'Ã', },
209
	{ "&Auml;",	L'Ä', },
210
	{ "&Ccedil;",	L'Ç', },
211
	{ "&ETH;",		L'Ð', },
212
	{ "&Eacute;",	L'É', },
213
	{ "&Ecirc;",	L'Ê', },
214
	{ "&Egrave;",	L'È', },
215
	{ "&Euml;",	L'Ë', },
216
	{ "&Iacute;",	L'Í', },
217
	{ "&Icirc;",		L'Î', },
218
	{ "&Igrave;",	L'Ì', },
219
	{ "&Iuml;",		L'Ï', },
220
	{ "&Ntilde;",	L'Ñ', },
221
	{ "&Oacute;",	L'Ó', },
222
	{ "&Ocirc;",	L'Ô', },
223
	{ "&Ograve;",	L'Ò', },
224
	{ "&Oslash;",	L'Ø', },
225
	{ "&Otilde;",	L'Õ', },
226
	{ "&Ouml;",	L'Ö', },
227
	{ "&THORN;",	L'Þ', },
228
	{ "&Uacute;",	L'Ú', },
229
	{ "&Ucirc;",	L'Û', },
230
	{ "&Ugrave;",	L'Ù', },
231
	{ "&Uuml;",	L'Ü', },
232
	{ "&Yacute;",	L'Ý', },
233
	{ "&aacute;",	L'á', },
234
	{ "&acirc;",	L'â', },
235
	{ "&aelig;",	L'æ', },
236
	{ "&agrave;",	L'à', },
237
	{ "&amp;",		L'&', },
238
	{ "&aring;",	L'å', },
239
	{ "&atilde;",	L'ã', },
240
	{ "&auml;",	L'ä', },
241
	{ "&ccedil;",	L'ç', },
242
	{ "&eacute;",	L'é', },
243
	{ "&ecirc;",	L'ê', },
244
	{ "&egrave;",	L'è', },
245
	{ "&eth;",		L'ð', },
246
	{ "&euml;",	L'ë', },
247
	{ "&gt;",		L'>', },
248
	{ "&iacute;",	L'í', },
249
	{ "&icirc;",		L'î', },
250
	{ "&igrave;",	L'ì', },
251
	{ "&iuml;",		L'ï', },
252
	{ "&lt;",		L'<', },
253
	{ "&ntilde;",	L'ñ', },
254
	{ "&oacute;",	L'ó', },
255
	{ "&ocirc;",	L'ô', },
256
	{ "&ograve;",	L'ò', },
257
	{ "&oslash;",	L'ø', },
258
	{ "&otilde;",	L'õ', },
259
	{ "&ouml;",	L'ö', },
260
	{ "&szlig;",		L'ß', },
261
	{ "&thorn;",	L'þ', },
262
	{ "&uacute;",	L'ú', },
263
	{ "&ucirc;",	L'û', },
264
	{ "&ugrave;",	L'ù', },
265
	{ "&uuml;",	L'ü', },
266
	{ "&yacute;",	L'ý', },
267
	{ "&yuml;",	L'ÿ', },
268
	{ "&#161;",	L'¡', },
269
	{ "&#162;",	L'¢', },
270
	{ "&#163;",	L'£', },
271
	{ "&#164;",	L'¤', },
272
	{ "&#165;",	L'¥', },
273
	{ "&#166;",	L'¦', },
274
	{ "&#167;",	L'§', },
275
	{ "&#168;",	L'¨', },
276
	{ "&#169;",	L'©', },
277
	{ "&#170;",	L'ª', },
278
	{ "&#171;",	L'«', },
279
	{ "&#172;",	L'¬', },
280
	{ "&#173;",	L'­', },
281
	{ "&#174;",	L'®', },
282
	{ "&#175;",	L'¯', },
283
	{ "&#176;",	L'°', },
284
	{ "&#177;",	L'±', },
285
	{ "&#178;",	L'²', },
286
	{ "&#179;",	L'³', },
287
	{ "&#180;",	L'´', },
288
	{ "&#181;",	L'µ', },
289
	{ "&#182;",	L'¶', },
290
	{ "&#183;",	L'·', },
291
	{ "&#184;",	L'¸', },
292
	{ "&#185;",	L'¹', },
293
	{ "&#186;",	L'º', },
294
	{ "&#187;",	L'»', },
295
	{ "&#188;",	L'¼', },
296
	{ "&#189;",	L'½', },
297
	{ "&#190;",	L'¾', },
298
	{ "&#191;",	L'¿', },
299
 
300
	{ "*",			L'•', },
301
	{ "&#164;",	L'□', },
302
	{ "&#186;",	L'◊', },
303
	{ "(tm)",		L'™', },
304
	{"&#913;",		L'Α',},
305
	{"&#914;",		L'Β',},
306
	{"&#915;",		L'Γ',},
307
	{"&#916;",		L'Δ',},
308
	{"&#917;",		L'Ε',},
309
	{"&#918;",		L'Ζ',},
310
	{"&#919;",		L'Η',},
311
	{"&#920;",		L'Θ',},
312
	{"&#921;",		L'Ι',},
313
	{"&#922;",		L'Κ',},
314
	{"&#923;",		L'Λ',},
315
	{"&#924;",		L'Μ',},
316
	{"&#925;",		L'Ν',},
317
	{"&#926;",		L'Ξ',},
318
	{"&#927;",		L'Ο',},
319
	{"&#928;",		L'Π',},
320
	{"&#929;",		L'Ρ',},
321
	{"&#930;",		L'΢',},
322
	{"&#931;",		L'Σ',},
323
	{"&#932;",		L'Τ',},
324
	{"&#933;",		L'Υ',},
325
	{"&#934;",		L'Φ',},
326
	{"&#935;",		L'Χ',},
327
	{"&#936;",		L'Ψ',},
328
	{"&#937;",		L'Ω',},
329
	{"&#945;",		L'α',},
330
	{"&#946;",		L'β',},
331
	{"&#947;",		L'γ',},
332
	{"&#948;",		L'δ',},
333
	{"&#949;",		L'ε',},
334
	{"&#950;",		L'ζ',},
335
	{"&#951;",		L'η',},
336
	{"&#952;",		L'θ',},
337
	{"&#953;",		L'ι',},
338
	{"&#954;",		L'κ',},
339
	{"&#955;",		L'λ',},
340
	{"&#956;",		L'μ',},
341
	{"&#957;",		L'ν',},
342
	{"&#958;",		L'ξ',},
343
	{"&#959;",		L'ο',},
344
	{"&#960;",		L'π',},
345
	{"&#961;",		L'ρ',},
346
	{"&#962;",		L'ς',},
347
	{"&#963;",		L'σ',},
348
	{"&#964;",		L'τ',},
349
	{"&#965;",		L'υ',},
350
	{"&#966;",		L'φ',},
351
	{"&#967;",		L'χ',},
352
	{"&#968;",		L'ψ',},
353
	{"&#969;",		L'ω',},
354
 
355
	{ "<-",		L'←', },
356
	{ "^",			L'↑', },
357
	{ "->",		L'→', },
358
	{ "v",			L'↓', },
359
	{ "!=",		L'≠', },
360
	{ "<=",		L'≤', },
361
	{ "...",		L'⋯', },
362
	{"&isin;",		L'∈', },
363
 
364
	{"&#8211;",	L'–', },
365
	{"&#8212;",	L'—', },
366
 
367
	{ "CYRILLIC XYZZY",	L'й', },
368
	{ "CYRILLIC XYZZY",	L'ъ', },
369
	{ "CYRILLIC Y",		L'ь', },
370
	{ "CYRILLIC YA",	L'я', },
371
	{ "CYRILLIC YA",	L'ё', },
372
	{ "&#191;",		L'ℱ', },
373
 
374
	{ nil, 0 },
375
};
376
 
377
typedef struct Troffspec Troffspec;
378
struct Troffspec
379
{
380
	char *name;
381
	char *value;
382
};
383
 
384
Troffspec tspec[] =
385
{
386
	{ "A*", "&Aring;", },
387
	{ "o\"", "&ouml;", },
388
	{ "ff", "ff", },
389
	{ "fi", "fi", },
390
	{ "fl", "fl", },
391
	{ "Fi", "ffi", },
392
	{ "ru", "_", },
393
	{ "em", "&#173;", },
394
	{ "14", "&#188;", },
395
	{ "12", "&#189;", },
396
	{ "co", "&#169;", },
397
	{ "de", "&#176;", },
398
	{ "dg", "&#161;", },
399
	{ "fm", "&#180;", },
400
	{ "rg", "&#174;", },
401
	{ "bu", "*", },
402
	{ "sq", "&#164;", },
403
	{ "hy", "-", },
404
	{ "pl", "+", },
405
	{ "mi", "-", },
406
	{ "mu", "&#215;", },
407
	{ "di", "&#247;", },
408
	{ "eq", "=", },
409
	{ "==", "==", },
410
	{ ">=", ">=", },
411
	{ "<=", "<=", },
412
	{ "!=", "!=", },
413
	{ "+-", "&#177;", },
414
	{ "no", "&#172;", },
415
	{ "sl", "/", },
416
	{ "ap", "&", },
417
	{ "~=", "~=", },
418
	{ "pt", "oc", },
419
	{ "gr", "GRAD", },
420
	{ "->", "->", },
421
	{ "<-", "<-", },
422
	{ "ua", "^", },
423
	{ "da", "v", },
424
	{ "is", "Integral", },
425
	{ "pd", "DIV", },
426
	{ "if", "oo", },
427
	{ "sr", "-/", },
428
	{ "sb", "(~", },
429
	{ "sp", "~)", },
430
	{ "cu", "U", },
431
	{ "ca", "(^)", },
432
	{ "ib", "(=", },
433
	{ "ip", "=)", },
434
	{ "mo", "C", },
435
	{ "es", "&Oslash;", },
436
	{ "aa", "&#180;", },
437
	{ "ga", "`", },
438
	{ "ci", "O", },
439
	{ "L1", "DEATHSTAR", },
440
	{ "sc", "&#167;", },
441
	{ "dd", "++", },
442
	{ "lh", "<=", },
443
	{ "rh", "=>", },
444
	{ "lt", "(", },
445
	{ "rt", ")", },
446
	{ "lc", "|", },
447
	{ "rc", "|", },
448
	{ "lb", "(", },
449
	{ "rb", ")", },
450
	{ "lf", "|", },
451
	{ "rf", "|", },
452
	{ "lk", "|", },
453
	{ "rk", "|", },
454
	{ "bv", "|", },
455
	{ "ts", "s", },
456
	{ "br", "|", },
457
	{ "or", "|", },
458
	{ "ul", "_", },
459
	{ "rn", " ", },
460
	{ "**", "*", },
461
	{ "tm", "&#153", },
462
	{ nil, nil, },
463
};
464
 
465
typedef struct Font Font;
466
struct Font
467
{
468
	char	*start;
469
	char	*end;
470
};
471
Font bfont = { "<B>", "</B>" };
472
Font ifont = { "<I>", "</I>" };
473
Font bifont = { "<B><I>", "</I></B>" };
474
Font cwfont = { "<TT>", "</TT>" };
475
Font *fstack[Maxfsp];
476
int fsp = -1;
477
 
478
typedef struct String String;
479
struct String
480
{
481
	String *next;
482
	char *name;
483
	char *val;
484
};
485
String *numregs, *strings;
486
char *strstack[Maxmstack];
487
char *mustfree[Maxmstack];
488
int strsp = -1;
489
int elsetop = -1;
490
 
491
typedef struct Mstack Mstack;
492
struct Mstack
493
{
494
	char *ptr;
495
	char *argv[Narg+1];
496
};
497
String *macros;
498
Mstack mstack[Maxmstack];
499
int msp = -1;
500
 
501
typedef struct Srcstack Srcstack;
502
struct Srcstack
503
{
504
	char	filename[256];
505
	int	fd;
506
	int	lno;
507
	int	rlno;
508
	Biobuf	in;
509
};
510
Srcstack sstack[Maxsstack];
511
Srcstack *ssp = &sstack[-1];
512
 
513
char token[128];
514
 
515
void	closel(void);
516
void	closefont(void);
517
 
518
void*
519
emalloc(uint n)
520
{
521
	void *p;
522
 
523
	p = mallocz(n, 1);
524
	if(p == nil){
525
		fprint(2, "ms2html: malloc failed: %r\n");
526
		exits("malloc");
527
	}
528
	return p;
529
}
530
 
531
 
532
/* define a string variable */
533
void
534
dsnr(char *name, char *val, String **l)
535
{
536
	String *s;
537
 
538
	for(s = *l; s != nil; s = *l){
539
		if(strcmp(s->name, name) == 0)
540
			break;
541
		l = &s->next;
542
	}
543
	if(s == nil){
544
		s = emalloc(sizeof(String));
545
		*l = s;
546
		s->name = strdup(name);
547
	} else
548
		free(s->val);
549
	s->val = strdup(val);
550
}
551
 
552
void
553
ds(char *name, char *val)
554
{
555
	dsnr(name, val, &strings);
556
}
557
 
558
/* look up a defined string */
559
char*
560
getds(char *name)
561
{
562
	String *s;
563
 
564
	for(s = strings; s != nil; s = s->next)
565
		if(strcmp(name, s->name) == 0)
566
			break;
567
	if(s != nil)
568
		return s->val;
569
	return "";
570
}
571
 
572
char *
573
getnr(char *name)
574
{
575
	String *s;
576
 
577
	for(s = numregs; s != nil; s = s->next)
578
		if(strcmp(name, s->name) == 0)
579
			break;
580
	if(s != nil)
581
		return s->val;
582
	return "0";
583
}
584
 
585
void
586
pushstr(char *p)
587
{
588
	if(p == nil)
589
		return;
590
	if(strsp >= Maxmstack - 1)
591
		return;
592
	strstack[++strsp] = p;
593
}
594
 
595
 
596
/* lookup a defined macro */
597
char*
598
getmacro(char *name)
599
{
600
	String *s;
601
 
602
	for(s = macros; s != nil; s = s->next)
603
		if(strcmp(name, s->name) == 0)
604
			return s->val;
605
	return nil;
606
}
607
 
608
enum
609
{
610
	Dstring,
611
	Macro,
612
	Input,
613
};
614
int lastsrc;
615
 
616
void
617
pushsrc(char *name)
618
{
619
	Dir *d;
620
	int fd;
621
 
622
	if(ssp == &sstack[Maxsstack-1]){
623
		fprint(2, "ms2html: .so's too deep\n");
624
		return;
625
	}
626
	d = nil;
627
	if(name == nil){
628
		d = dirfstat(0);
629
		if(d == nil){
630
			fprint(2, "ms2html: can't stat %s: %r\n", name);
631
			return;
632
		}
633
		name = d->name;
634
		fd = 0;
635
	} else {
636
		fd = open(name, OREAD);
637
		if(fd < 0){
638
			fprint(2, "ms2html: can't open %s: %r\n", name);
639
			return;
640
		}
641
	}
642
	ssp++;
643
	ssp->fd = fd;
644
	Binit(&ssp->in, fd, OREAD);
645
	snprint(ssp->filename, sizeof(ssp->filename), "%s", name);
646
	ssp->lno = ssp->rlno = 1;
647
	free(d);
648
}
649
 
650
/* get next logical byte.  from stdin or a defined string */
651
int
652
getrune(void)
653
{
654
	int i;
655
	Rune r;
656
	int c;
657
	Mstack *m;
658
 
659
	while(strsp >= 0){
660
		i = chartorune(&r, strstack[strsp]);
661
		if(r != 0){
662
			strstack[strsp] += i;
663
			lastsrc = Dstring;
664
			return r;
665
		}
666
		if (mustfree[strsp]) {
667
			free(mustfree[strsp]);
668
			mustfree[strsp] = nil;
669
		}
670
		strsp--;
671
 	}
672
	while(msp >= 0){
673
		m = &mstack[msp];
674
		i = chartorune(&r, m->ptr);
675
		if(r != 0){
676
			m->ptr += i;
677
			lastsrc = Macro;
678
			return r;
679
		}
680
		for(i = 0; m->argv[i] != nil; i++)
681
			free(m->argv[i]);
682
		msp--;
683
 	}
684
 
685
	lastsrc = Input;
686
	for(;;) {
687
		if(ssp < sstack)
688
			return -1;
689
		c = Bgetrune(&ssp->in);
690
		if(c >= 0){
691
			r = c;
692
			break;
693
		}
694
		close(ssp->fd);
695
		ssp--;
696
	}
697
 
698
	return r;
699
}
700
 
701
void
702
ungetrune(void)
703
{
704
	switch(lastsrc){
705
	case Dstring:
706
		if(strsp >= 0)
707
			strstack[strsp]--;
708
		break;
709
	case Macro:
710
		if(msp >= 0)
711
			mstack[msp].ptr--;
712
		break;
713
	case Input:
714
		if(ssp >= sstack)
715
			Bungetrune(&ssp->in);
716
		break;
717
	}
718
}
719
 
720
int vert;
721
 
722
char*
723
changefont(Font *f)
724
{
725
	token[0] = 0;
726
	if(fsp == Maxfsp)
727
		return token;
728
	if(fsp >= 0 && fstack[fsp])
729
		strcpy(token, fstack[fsp]->end);
730
	if(f != nil)
731
		strcat(token, f->start);
732
	fstack[++fsp] = f;
733
	return token;
734
}
735
 
736
char*
737
changebackfont(void)
738
{
739
	token[0] = 0;
740
	if(fsp >= 0){
741
		if(fstack[fsp])
742
			strcpy(token, fstack[fsp]->end);
743
		fsp--;
744
	}
745
	if(fsp >= 0 && fstack[fsp])
746
		strcat(token, fstack[fsp]->start);
747
	return token;
748
}
749
 
750
char*
751
changesize(int amount)
752
{
753
	static int curamount;
754
	static char buf[200];
755
	int i;
756
 
757
	buf[0] = 0;
758
	if (curamount >= 0)
759
		for (i = 0; i < curamount; i++)
760
			strcat(buf, "</big>");
761
	else
762
		for (i = 0; i < -curamount; i++)
763
			strcat(buf, "</small>");
764
	curamount = 0;
765
	if (amount >= 0)
766
		for (i = 0; i < amount; i++)
767
			strcat(buf, "<big>");
768
	else
769
		for (i = 0; i < -amount; i++)
770
			strcat(buf, "<small>");
771
	curamount = amount;
772
	return buf;
773
}
774
 
775
/* get next logical character.  expand it with escapes */
776
char*
777
getnext(void)
778
{
779
	int r;
780
	Entity *e;
781
	Troffspec *t;
782
	Rune R;
783
	char str[4];
784
	static char buf[8];
785
 
786
	r = getrune();
787
	if(r < 0)
788
		return nil;
789
	if(r > 128 || r == '<' || r == '>'){
790
		for(e = entity; e->name; e++)
791
			if(e->value == r)
792
				return e->name;
793
		sprint(buf, "&#%d;", r);
794
		return buf;
795
	}
796
 
797
	if (r == delim[eqnmode]){
798
		if (eqnmode == 0){
799
			eqnmode = 1;
800
			return changefont(&ifont);
801
		}
802
		eqnmode = 0;
803
		return changebackfont();
804
	}
805
	switch(r){
806
	case '\\':
807
		r = getrune();
808
		if(r < 0)
809
			return nil;
810
		switch(r){
811
		case ' ':
812
			return " ";
813
 
814
		/* chars to ignore */
815
		case '&':
816
		case '|':
817
		case '%':
818
			return "";
819
 
820
		/* small space in troff, nothing in nroff */
821
		case '^':
822
			return getnext();
823
 
824
		/* ignore arg */
825
		case 'k':
826
			getrune();
827
			return getnext();
828
 
829
		/* comment */
830
		case '"':
831
			while(getrune() != '\n')
832
				;
833
			return "\n";
834
		/* ignore line */
835
		case '!':
836
			while(getrune() != '\n')
837
				;
838
			ungetrune();
839
			return getnext();
840
 
841
		/* defined strings */
842
		case '*':
843
			r = getrune();
844
			if(r == '('){
845
				str[0] = getrune();
846
				str[1] = getrune();
847
				str[2] = 0;
848
			} else {
849
				str[0] = r;
850
				str[1] = 0;
851
			}
852
			pushstr(getds(str));
853
			return getnext();
854
 
855
		/* macro args */
856
		case '$':
857
			r = getrune();
858
			if(r < '1' || r > '9'){
859
				token[0] = '\\';
860
				token[1] = '$';
861
				token[2] = r;
862
				token[3] = 0;
863
				return token;
864
			}
865
			r -= '0';
866
			if(msp >= 0) 
867
				pushstr(mstack[msp].argv[r]);
868
			return getnext();
869
 
870
		/* special chars */
871
		case '(':
872
			token[0] = getrune();
873
			token[1] = getrune();
874
			token[2] = 0;
875
			for(t = tspec; t->name; t++)
876
				if(strcmp(token, t->name) == 0)
877
					return t->value;
878
			return "&#191;";
879
 
880
		/* ignore immediately following newline */
881
		case 'c':
882
			r = getrune();
883
			if (r == '\n') {
884
				sol = ignore_nl = 1;
885
				if (indirective)
886
					break;
887
				}
888
			else
889
				ungetrune();
890
			return getnext();
891
 
892
		/* escape backslash */
893
		case 'e':
894
			return "\\";
895
 
896
		/* font change */
897
		case 'f':
898
			r = getrune();
899
			switch(r){
900
			case '(':
901
				str[0] = getrune();
902
				str[1] = getrune();
903
				str[2] = 0;
904
				token[0] = 0;
905
				if(strcmp("BI", str) == 0)
906
					return changefont(&bifont);
907
				else if(strcmp("CW", str) == 0)
908
					return changefont(&cwfont);
909
				else
910
					return changefont(nil);
911
			case '3':
912
			case 'B':
913
				return changefont(&bfont);
914
			case '2':
915
			case 'I':
916
				return changefont(&ifont);
917
			case '4':
918
				return changefont(&bifont);
919
			case '5':
920
				return changefont(&cwfont);
921
			case 'P':
922
				return changebackfont();
923
			case 'R':
924
			default:
925
				return changefont(nil);
926
			}
927
 
928
		/* number register */
929
		case 'n':
930
			r = getrune();
931
			if (r == '(') /*)*/ {
932
				r = getrune();
933
				if (r < 0)
934
					return nil;
935
				str[0] = r;
936
				r = getrune();
937
				if (r < 0)
938
					return nil;
939
				str[1] = r;
940
				str[2] = 0;
941
				}
942
			else {
943
				str[0] = r;
944
				str[1] = 0;
945
				}
946
			pushstr(getnr(str));
947
			return getnext();
948
 
949
		/* font size */
950
		case 's':
951
			r = getrune();
952
			switch(r){
953
			case '0':
954
				return changesize(0);
955
			case '-':
956
				r = getrune();
957
				if (!isdigit(r))
958
					return getnext();
959
				return changesize(-(r - '0'));
960
			case '+':
961
				r = getrune();
962
				if (!isdigit(r))
963
					return getnext();
964
				return changesize(r - '0');
965
			}
966
			return getnext();
967
		/* vertical movement */
968
		case 'v':
969
			r = getrune();
970
			if(r != '\''){
971
				ungetrune();
972
				return getnext();
973
			}
974
			r = getrune();
975
			if(r != '-')
976
				vert--;
977
			else
978
				vert++;
979
			while(r != '\'' && r != '\n')
980
				r = getrune();
981
			if(r != '\'')
982
				ungetrune();
983
 
984
			if(vert > 0)
985
				return "^";
986
			return getnext();
987
 
988
 
989
		/* horizontal line */
990
		case 'l':
991
			r = getrune();
992
			if(r != '\''){
993
				ungetrune();
994
				return "<HR>";
995
			}
996
			while(getrune() != '\'')
997
				;
998
			return "<HR>";
999
 
1000
		/* character height and slant */
1001
		case 'S':
1002
		case 'H':
1003
			r = getrune();
1004
			if(r != '\''){
1005
				ungetrune();
1006
				return "<HR>";
1007
			}
1008
			while(getrune() != '\'')
1009
				;
1010
			return getnext();
1011
 
1012
		/* digit-width space */
1013
		case '0':
1014
			return " ";
1015
 
1016
		/*for .if, .ie, .el */
1017
		case '{':
1018
			return "\\{"; /*}*/
1019
		case '}':
1020
			return "";
1021
		/* up and down */
1022
		case 'u':
1023
			if (isdown) {
1024
				isdown = 0;
1025
				return "</sub>";
1026
			}
1027
			isup = 1;
1028
			return "<sup>";
1029
		case 'd':
1030
			if (isup) {
1031
				isup = 0;
1032
				return "</sup>";
1033
			}
1034
			isdown = 1;
1035
			return "<sub>";
1036
		}
1037
		break;
1038
	case '&':
1039
		if(msp >= 0 || strsp >= 0)
1040
			return "&";
1041
		return "&amp;";
1042
	case '<':
1043
		if(msp >= 0 || strsp >= 0)
1044
			return "<";
1045
		return "&#60;";
1046
	case '>':
1047
		if(msp >= 0 || strsp >= 0)
1048
			return ">";
1049
		return "&#62;";
1050
	}
1051
	if (r < Runeself) {
1052
		token[0] = r;
1053
		token[1] = 0;
1054
		}
1055
	else {
1056
		R = r;
1057
		token[runetochar(token,&R)] = 0;
1058
	}
1059
	return token;
1060
}
1061
 
1062
/* if arg0 is set, read up to (and expand) to the next whitespace, else to the end of line */
1063
char*
1064
copyline(char *p, char *e, int arg0)
1065
{
1066
	int c;
1067
	Rune r;
1068
	char *p1;
1069
 
1070
	while((c = getrune()) == ' ' || c == '\t')
1071
		;
1072
	for(indirective = 1; p < e; c = getrune()) {
1073
		if (c < 0)
1074
			goto done;
1075
		switch(c) {
1076
		case '\\':
1077
			break;
1078
		case '\n':
1079
			if (arg0)
1080
				ungetrune();
1081
			goto done;
1082
		case ' ':
1083
		case '\t':
1084
			if (arg0)
1085
				goto done;
1086
		default:
1087
			r = c;
1088
			p += runetochar(p,&r);
1089
			continue;
1090
		}
1091
		ungetrune();
1092
		p1 = getnext();
1093
		if (p1 == nil)
1094
			goto done;
1095
		if (*p1 == '\n') {
1096
			if (arg0)
1097
				ungetrune();
1098
			break;
1099
		}
1100
		while((*p = *p1++) && p < e)
1101
			p++;
1102
	}
1103
done:
1104
	indirective = 0;
1105
	*p++ = 0;
1106
	return p;
1107
}
1108
 
1109
char*
1110
copyarg(char *p, char *e, int *nullarg)
1111
{
1112
	int c, quoted, last;
1113
	Rune r;
1114
 
1115
	*nullarg = 0;
1116
	quoted = 0;
1117
	do{
1118
		c = getrune();
1119
	} while(c == ' ' || c == '\t');
1120
 
1121
	if(c == '"'){
1122
		quoted = 1;
1123
		*nullarg = 1;
1124
		c = getrune();
1125
	}
1126
 
1127
	if(c == '\n')
1128
		goto done;
1129
 
1130
	last = 0;
1131
	for(; p < e; c = getrune()) {
1132
		if (c < 0)
1133
			break;
1134
		switch(c) {
1135
		case '\n':
1136
			ungetrune();
1137
			goto done;
1138
		case '\\':
1139
			r = c;
1140
			p += runetochar(p,&r);
1141
			if(last == '\\')
1142
				r = 0;
1143
			break;
1144
		case ' ':
1145
		case '\t':
1146
			if(!quoted && last != '\\')
1147
				goto done;
1148
			r = c;
1149
			p += runetochar(p,&r);
1150
			break;
1151
		case '"':
1152
			if(quoted && last != '\\')
1153
				goto done;
1154
			r = c;
1155
			p += runetochar(p,&r);
1156
			break;
1157
		default:
1158
			r = c;
1159
			p += runetochar(p,&r);
1160
			break;
1161
		}
1162
		last = r;
1163
	}
1164
done:
1165
	*p++ = 0;
1166
	return p;
1167
 
1168
}
1169
 
1170
int
1171
parseargs(char *p, char *e, char **argv)
1172
{
1173
	int argc;
1174
	char *np;
1175
	int nullarg;
1176
 
1177
	indirective = 1;
1178
	*p++ = 0;
1179
	for(argc = 1; argc < Narg; argc++){
1180
		np = copyarg(p, e, &nullarg);
1181
		if(nullarg==0 && np == p+1)
1182
			break;
1183
		argv[argc] = p;
1184
		p = np;
1185
	}
1186
	argv[argc] = nil;
1187
	indirective = 0;
1188
 
1189
 
1190
	return argc;
1191
}
1192
 
1193
void
1194
dodirective(void)
1195
{
1196
	char *p, *e;
1197
	Goobie *g;
1198
	Goobieif *gif;
1199
	char line[Nline], *line1;
1200
	int i, argc;
1201
	char *argv[Narg];
1202
	Mstack *m;
1203
 
1204
	/* read line, translate special bytes */
1205
	e = line + sizeof(line) - UTFmax - 1;
1206
	line1 = copyline(line, e, 1);
1207
	if (!line[0])
1208
		return;
1209
	argv[0] = line;
1210
 
1211
	/* first look through user defined macros */
1212
	p = getmacro(argv[0]);
1213
	if(p != nil){
1214
		if(msp == Maxmstack-1){
1215
			fprint(2, "ms2html: macro stack overflow\n");
1216
			return;
1217
		}
1218
		argc = parseargs(line1, e, argv);
1219
		m = &mstack[++msp];
1220
		m->ptr = p;
1221
		memset(m->argv, 0, sizeof(m->argv));
1222
		for(i = 0; i < argc; i++)
1223
			m->argv[i] = strdup(argv[i]);
1224
		return;
1225
	}
1226
 
1227
	/* check for .if or .ie */
1228
	for(gif = gtabif; gif->name; gif++)
1229
		if(strcmp(gif->name, argv[0]) == 0){
1230
			(*gif->f)(line1, e);
1231
			return;
1232
		}
1233
 
1234
	argc = parseargs(line1, e, argv);
1235
 
1236
	/* try standard ms macros */
1237
	for(g = gtab; g->name; g++)
1238
		if(strcmp(g->name, argv[0]) == 0){
1239
			(*g->f)(argc, argv);
1240
			return;
1241
		}
1242
 
1243
	if(debug)
1244
		fprint(2, "stdin %d(%s:%d): unknown directive %s\n",
1245
			ssp->lno, ssp->filename, ssp->rlno, line);
1246
}
1247
 
1248
void
1249
printarg(char *a)
1250
{
1251
	char *e, *p;
1252
 
1253
	e = a + strlen(a);
1254
	pushstr(a);
1255
	while(strsp >= 0 && strstack[strsp] >= a && strstack[strsp] < e){
1256
		p = getnext();
1257
		if(p == nil)
1258
			return;
1259
		Bprint(&bout, "%s", p);
1260
	}
1261
}
1262
 
1263
void
1264
printargs(int argc, char **argv)
1265
{
1266
	argc--;
1267
	argv++;
1268
	while(--argc > 0){
1269
		printarg(*argv++);
1270
		Bprint(&bout, " ");
1271
	}
1272
	if(argc == 0)
1273
		printarg(*argv);
1274
}
1275
 
1276
void
1277
dohangingdt(void)
1278
{
1279
	switch(hangingdt){
1280
	case 3:
1281
		hangingdt--;
1282
		break;
1283
	case 2:
1284
		Bprint(&bout, "<dd>");
1285
		hangingdt = 0;
1286
		break;
1287
	}
1288
 
1289
}
1290
 
1291
void
1292
dohangingau(void)
1293
{
1294
	if(hangingau == 0)
1295
		return;
1296
	Bprint(&bout, "</I></DL>\n");
1297
	hangingau = 0;
1298
}
1299
 
1300
void
1301
dohanginghead(void)
1302
{
1303
	if(hanginghead == 0)
1304
		return;
1305
	Bprint(&bout, "</H%d>\n", hanginghead);
1306
	hanginghead = 0;
1307
}
1308
 
1309
/*
1310
 *  convert a man page to html and output
1311
 */
1312
void
1313
doconvert(void)
1314
{
1315
	char c, *p;
1316
	Tm *t;
1317
 
1318
	pushsrc(nil);
1319
 
1320
	sol = 1;
1321
	Bprint(&bout, "<html>\n");
1322
	Bflush(&bout);
1323
	for(;;){
1324
		p = getnext();
1325
		if(p == nil)
1326
			break;
1327
		c = *p;
1328
		if(c == '.' && sol){
1329
			dodirective();
1330
			dohangingdt();
1331
			ssp->lno++;
1332
			ssp->rlno++;
1333
			sol = 1;
1334
		} else if(c == '\n'){
1335
			if (ignore_nl)
1336
				ignore_nl = 0;
1337
			else {
1338
				if(hangingau)
1339
					Bprint(&bout, "<br>\n");
1340
				else
1341
					Bprint(&bout, "%s", p);
1342
				dohangingdt();
1343
				}
1344
			ssp->lno++;
1345
			ssp->rlno++;
1346
			sol = 1;
1347
		} else{
1348
			Bprint(&bout, "%s", p);
1349
			ignore_nl = sol = 0;
1350
		}
1351
	}
1352
	dohanginghead();
1353
	dohangingdt();
1354
	closel();
1355
	if(fsp >= 0 && fstack[fsp])
1356
		Bprint(&bout, "%s", fstack[fsp]->end);
1357
	Bprint(&bout, "<br>&#32;<br>\n");
1358
	Bprint(&bout, "<A href=http://www.lucent.com/copyright.html>\n");
1359
	t = localtime(time(nil));
1360
	Bprint(&bout, "Copyright</A> &#169; %d Alcatel-Lucent Inc.  All rights reserved.\n",
1361
			t->year+1900);
1362
	Bprint(&bout, "</body></html>\n");
1363
}
1364
 
1365
static void
1366
usage(void)
1367
{
1368
	sysfatal("usage: ms2html [-q] [-b basename] [-d '$$'] [-t title]");
1369
}
1370
 
1371
void
1372
main(int argc, char **argv)
1373
{
1374
	quiet = 1;
1375
	ARGBEGIN {
1376
	case 't':
1377
		title = EARGF(usage());
1378
		break;
1379
	case 'b':
1380
		basename = EARGF(usage());
1381
		break;
1382
	case 'q':
1383
		quiet = 0;
1384
		break;
1385
	case 'd':
1386
		delim = EARGF(usage());
1387
		break;
1388
	case '?':
1389
	default:
1390
		usage();
1391
	} ARGEND;
1392
 
1393
	Binit(&bout, 1, OWRITE);
1394
 
1395
	ds("R", "&#174;");
1396
 
1397
	doconvert();
1398
	exits(nil);
1399
}
1400
 
1401
void
1402
g_notyet(int, char **argv)
1403
{
1404
	fprint(2, "ms2html: .%s not yet supported\n", argv[0]);
1405
}
1406
 
1407
void
1408
g_ignore(int, char **argv)
1409
{
1410
	if(quiet)
1411
		return;
1412
	fprint(2, "ms2html: line %d: ignoring .%s\n", ssp->lno, argv[0]);
1413
}
1414
 
1415
void
1416
g_PP(int, char**)
1417
{
1418
	dohanginghead();
1419
	closel();
1420
	closefont();
1421
	Bprint(&bout, "<P>\n");
1422
	paragraph = 1;
1423
}
1424
 
1425
void
1426
g_LP(int, char**)
1427
{
1428
	dohanginghead();
1429
	closel();
1430
	closefont();
1431
	Bprint(&bout, "<br>&#32;<br>\n");
1432
}
1433
 
1434
/* close a list */
1435
void
1436
closel(void)
1437
{
1438
	g_P2(1, nil);
1439
	dohangingau();
1440
	if(paragraph){
1441
		Bprint(&bout, "</P>\n");
1442
		paragraph = 0;
1443
	}
1444
	switch(list){
1445
	case Lordered:
1446
		Bprint(&bout, "</ol>\n");
1447
		break;
1448
	case Lunordered:
1449
		Bprint(&bout, "</ul>\n");
1450
		break;
1451
	case Lother:
1452
	case Ldef:
1453
		Bprint(&bout, "</dl>\n");
1454
		break;
1455
	}
1456
	list = 0;
1457
 
1458
}
1459
 
1460
 
1461
void
1462
g_IP(int argc, char **argv)
1463
{
1464
	dohanginghead();
1465
	switch(list){
1466
	default:
1467
		closel();
1468
		if(argc > 1){
1469
			if(strcmp(argv[1], "1") == 0){
1470
				list = Lordered;
1471
				listnum = 1;
1472
				Bprint(&bout, "<OL>\n");
1473
			} else if(strcmp(argv[1], "\\(bu") == 0){
1474
				list = Lunordered;
1475
				Bprint(&bout, "<UL>\n");
1476
			} else {
1477
				list = Lother;
1478
				Bprint(&bout, "<DL COMPACT>\n");
1479
			}
1480
		} else {
1481
			list = Lother;
1482
			Bprint(&bout, "<DL>\n");
1483
		}
1484
		break;
1485
	case Lother:
1486
	case Lordered:
1487
	case Lunordered:
1488
		break;
1489
	}
1490
 
1491
	switch(list){
1492
	case Lother:
1493
		Bprint(&bout, "<DT>");
1494
		if(argc > 1)
1495
			printarg(argv[1]);
1496
		else
1497
			Bprint(&bout, "<DT>&#32;");
1498
		Bprint(&bout, "<DD>\n");
1499
		break;
1500
	case Lordered:
1501
	case Lunordered:
1502
		Bprint(&bout, "<LI>\n");
1503
		break;
1504
	}
1505
}
1506
 
1507
/*
1508
 *  .5i is one <DL><DT><DD>
1509
 */
1510
void
1511
g_in(int argc, char **argv)
1512
{
1513
	float	f;
1514
	int	delta, x;
1515
	char	*p;
1516
 
1517
	f = indent/0.5;
1518
	delta = f;
1519
	if(argc <= 1){
1520
		indent = 0.0;
1521
	} else {
1522
		f = strtod(argv[1], &p);
1523
		switch(*p){
1524
		case 'i':
1525
			break;
1526
		case 'c':
1527
			f = f / 2.54;
1528
			break;
1529
		case 'P':
1530
			f = f / 6;
1531
			break;
1532
		default:
1533
		case 'u':
1534
		case 'm':
1535
			f = f * (12 / 72);
1536
			break;
1537
		case 'n':
1538
			f = f * (6 / 72);
1539
			break;
1540
		case 'p':
1541
			f = f / 72.0;
1542
			break;
1543
		}
1544
		switch(argv[1][0]){
1545
		case '+':
1546
		case '-':
1547
			indent += f;
1548
			break;
1549
		default:
1550
			indent = f;
1551
			break;
1552
		}
1553
	}
1554
	if(indent < 0.0)
1555
		indent = 0.0;
1556
	f = (indent/0.5);
1557
	x = f;
1558
	delta = x - delta;
1559
	while(delta < 0){
1560
		Bprint(&bout, "</DL>\n");
1561
		delta++;
1562
	}
1563
	while(delta > 0){
1564
		Bprint(&bout, "<DL><DT><DD>\n");
1565
		delta--;
1566
	}
1567
}
1568
 
1569
void
1570
g_HP(int, char**)
1571
{
1572
	switch(list){
1573
	default:
1574
		closel();
1575
		list = Ldef;
1576
		hangingdt = 1;
1577
		Bprint(&bout, "<DL><DT>\n");
1578
		break;
1579
	case Ldef:
1580
		if(hangingdt)
1581
			Bprint(&bout, "<DD>");
1582
		Bprint(&bout, "<DT>");
1583
		hangingdt = 1;
1584
		break;
1585
	}
1586
}
1587
 
1588
void
1589
g_SH(int, char**)
1590
{
1591
	dohanginghead();
1592
	dohangingcenter();
1593
	closel();
1594
	closefont();
1595
	Bprint(&bout, "<H%d>", HH);
1596
	hanginghead = HH;
1597
}
1598
 
1599
void
1600
g_NH(int argc, char **argv)
1601
{
1602
	int i, level;
1603
 
1604
	closel();
1605
	closefont();
1606
 
1607
	dohanginghead();
1608
	dohangingcenter();
1609
	if(argc == 1)
1610
		level = 0;
1611
	else {
1612
		level = atoi(argv[1])-1;
1613
		if(level < 0 || level >= Maxnh)
1614
			level = Maxnh - 1;
1615
	}
1616
	nh[level]++;
1617
 
1618
	Bprint(&bout, "<H%d>", HH);
1619
	hanginghead = HH;
1620
 
1621
	Bprint(&bout, "%d", nh[0]);
1622
	for(i = 1; i <= level; i++)
1623
		Bprint(&bout, ".%d", nh[i]);
1624
	Bprint(&bout, " ");
1625
 
1626
	for(i = level+1; i < Maxnh; i++)
1627
		nh[i] = 0;
1628
}
1629
 
1630
void
1631
g_TL(int, char**)
1632
{
1633
	char *p, *np;
1634
	char name[128];
1635
 
1636
	closefont();
1637
 
1638
	if(!titleseen){
1639
		if(!title){
1640
			/* get base part of filename */
1641
			p = strrchr(ssp->filename, '/');
1642
			if(p == nil)
1643
				p = ssp->filename;
1644
			else
1645
				p++;
1646
			strncpy(name, p, sizeof(name));
1647
			name[sizeof(name)-1] = 0;
1648
 
1649
			/* dump any extensions */
1650
			np = strchr(name, '.');
1651
			if(np)
1652
				*np = 0;
1653
			title = p;
1654
		}
1655
		Bprint(&bout, "<title>\n");
1656
		Bprint(&bout, "%s\n", title);
1657
		Bprint(&bout, "</title>\n");
1658
		Bprint(&bout, "<body BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#0000FF\" VLINK=\"#330088\" ALINK=\"#FF0044\">\n");
1659
		titleseen = 1;
1660
	}
1661
 
1662
	Bprint(&bout, "<center>");
1663
	hangingcenter = 1;
1664
	Bprint(&bout, "<H%d>", 1);
1665
	hanginghead = 1;
1666
}
1667
 
1668
void
1669
dohangingcenter(void)
1670
{
1671
	if(hangingcenter){
1672
		Bprint(&bout, "</center>");
1673
		hangingcenter = 1;
1674
	}
1675
}
1676
 
1677
void
1678
g_AU(int, char**)
1679
{
1680
	closel();
1681
	dohanginghead();
1682
	Bprint(&bout, "<DL><DD><I>");
1683
	hangingau = 1;
1684
}
1685
 
1686
void
1687
pushfont(Font *f)
1688
{
1689
	if(fsp == Maxfsp)
1690
		return;
1691
	if(fsp >= 0 && fstack[fsp])
1692
		Bprint(&bout, "%s", fstack[fsp]->end);
1693
	if(f != nil)
1694
		Bprint(&bout, "%s", f->start);
1695
	fstack[++fsp] = f;
1696
}
1697
 
1698
void
1699
popfont(void)
1700
{
1701
	if(fsp >= 0){
1702
		if(fstack[fsp])
1703
			Bprint(&bout, "%s", fstack[fsp]->end);
1704
		fsp--;
1705
	}
1706
}
1707
 
1708
/*
1709
 *  for 3 args print arg3 \fxarg1\fP arg2
1710
 *  for 2 args print arg1 \fxarg2\fP
1711
 *  for 1 args print \fxarg1\fP
1712
 */
1713
void
1714
font(Font *f, int argc, char **argv)
1715
{
1716
	if(argc == 1){
1717
		pushfont(nil);
1718
		return;
1719
	}
1720
	if(argc > 3)
1721
		printarg(argv[3]);
1722
	pushfont(f);
1723
	printarg(argv[1]);
1724
	popfont();
1725
	if(argc > 2)
1726
		printarg(argv[2]);
1727
	Bprint(&bout, "\n");
1728
}
1729
 
1730
void
1731
closefont(void)
1732
{
1733
	if(fsp >= 0 && fstack[fsp])
1734
		Bprint(&bout, "%s", fstack[fsp]->end);
1735
	fsp = -1;
1736
}
1737
 
1738
void
1739
g_B(int argc, char **argv)
1740
{
1741
	font(&bfont, argc, argv);
1742
}
1743
 
1744
void
1745
g_R(int argc, char **argv)
1746
{
1747
	font(nil, argc, argv);
1748
}
1749
 
1750
void
1751
g_BI(int argc, char **argv)
1752
{
1753
	font(&bifont, argc, argv);
1754
}
1755
 
1756
void
1757
g_CW(int argc, char **argv)
1758
{
1759
	font(&cwfont, argc, argv);
1760
}
1761
 
1762
char*
1763
lower(char *p)
1764
{
1765
	char *x;
1766
 
1767
	for(x = p; *x; x++)
1768
		if(*x >= 'A' && *x <= 'Z')
1769
			*x -= 'A' - 'a';
1770
	return p;
1771
}
1772
 
1773
void
1774
g_I(int argc, char **argv)
1775
{
1776
	int anchor;
1777
	char *p;
1778
 
1779
	anchor = 0;
1780
	if(argc > 2){
1781
		p = argv[2];
1782
		if(p[0] == '(')
1783
		if(p[1] >= '0' && p[1] <= '9')
1784
		if(p[2] == ')'){
1785
			anchor = 1;
1786
			Bprint(&bout, "<A href=\"/magic/man2html/%c/%s\">",
1787
				p[1], lower(argv[1]));
1788
		}
1789
	}
1790
	font(&ifont, argc, argv);
1791
	if(anchor)
1792
		Bprint(&bout, "</A>");
1793
}
1794
 
1795
void
1796
g_br(int, char**)
1797
{
1798
	if(hangingdt){
1799
		Bprint(&bout, "<dd>");
1800
		hangingdt = 0;
1801
	}else
1802
		Bprint(&bout, "<br>\n");
1803
}
1804
 
1805
void
1806
g_P1(int, char**)
1807
{
1808
	if(example == 0){
1809
		example = 1;
1810
		Bprint(&bout, "<DL><DT><DD><TT><PRE>\n");
1811
	}
1812
}
1813
 
1814
void
1815
g_P2(int, char**)
1816
{
1817
	if(example){
1818
		example = 0;
1819
		Bprint(&bout, "</PRE></TT></DL>\n");
1820
	}
1821
}
1822
 
1823
void
1824
g_SM(int, char **argv)
1825
{
1826
	Bprint(&bout, "%s", argv[1]);
1827
}
1828
 
1829
void
1830
g_ft(int argc, char **argv)
1831
{
1832
	if(argc < 2){
1833
		pushfont(nil);
1834
		return;
1835
	}
1836
 
1837
	switch(argv[1][0]){
1838
	case '3':
1839
	case 'B':
1840
		pushfont(&bfont);
1841
		break;
1842
	case '2':
1843
	case 'I':
1844
		pushfont(&ifont);
1845
		break;
1846
	case '4':
1847
		pushfont(&bifont);
1848
		break;
1849
	case '5':
1850
		pushfont(&cwfont);
1851
		break;
1852
	case 'P':
1853
		popfont();
1854
		break;
1855
	case 'R':
1856
	default:
1857
		pushfont(nil);
1858
		break;
1859
	}
1860
}
1861
 
1862
void
1863
g_sp(int argc, char **argv)
1864
{
1865
	int n;
1866
 
1867
	n = 1;
1868
	if(argc > 1){
1869
		n = atoi(argv[1]);
1870
		if(n < 1)
1871
			n = 1;
1872
		if(argv[1][strlen(argv[1])-1] == 'i')
1873
			n *= 4;
1874
	}
1875
	if(n > 5){
1876
		Bprint(&bout, "<br>&#32;<br>\n");
1877
		Bprint(&bout, "<HR>\n");
1878
		Bprint(&bout, "<br>&#32;<br>\n");
1879
	} else
1880
		for(; n > 0; n--)
1881
			Bprint(&bout, "<br>&#32;<br>\n");
1882
}
1883
 
1884
 void
1885
rm_loop(char *name, String **l)
1886
{
1887
	String *s;
1888
	for(s = *l; s != nil; s = *l){
1889
		if(strcmp(name, s->name) == 0){
1890
			*l = s->next;
1891
			free(s->name);
1892
			free(s->val);
1893
			free(s);
1894
			break;
1895
			}
1896
		l = &s->next;
1897
		}
1898
	}
1899
 
1900
void
1901
g_rm(int argc, char **argv)
1902
{
1903
	Goobie *g;
1904
	char *name;
1905
	int i;
1906
 
1907
	for(i = 1; i < argc; i++) {
1908
		name = argv[i];
1909
		rm_loop(name, &strings);
1910
		rm_loop(name, &macros);
1911
		for(g = gtab; g->name; g++)
1912
			if (strcmp(g->name, name) == 0) {
1913
				g->f = g_ignore;
1914
				break;
1915
				}
1916
		}
1917
	}
1918
 
1919
void
1920
g_AB(int, char**)
1921
{
1922
	closel();
1923
	dohangingcenter();
1924
	Bprint(&bout, "<center><H4>ABSTRACT</H4></center><DL><DD>\n");
1925
}
1926
 
1927
void
1928
g_AE(int, char**)
1929
{
1930
	Bprint(&bout, "</DL>\n");
1931
}
1932
 
1933
void
1934
g_FS(int, char **)
1935
{
1936
	char *argv[3];
1937
 
1938
	argv[0] = "IP";
1939
	argv[1] = nil;
1940
	argv[2] = nil;
1941
	g_IP(1, argv);
1942
	Bprint(&bout, "NOTE:<I> ");
1943
}
1944
 
1945
void
1946
g_FE(int, char **)
1947
{
1948
	Bprint(&bout, "</I><DT>&#32;<DD>");
1949
	closel();
1950
	Bprint(&bout, "<br>\n");
1951
}
1952
 
1953
void
1954
g_de(int argc, char **argv)
1955
{
1956
	int r;
1957
	char *p, *cp;
1958
	String *m;
1959
	int len;
1960
 
1961
	if(argc < 2)
1962
		return;
1963
 
1964
	m = nil;
1965
	len = 0;
1966
	if(strcmp(argv[0], "am") == 0){
1967
		for(m = macros; m != nil; m = m->next)
1968
			if(strcmp(argv[1], m->name) == 0){
1969
				len = strlen(m->val);
1970
				break;
1971
			}
1972
 
1973
		if(m == nil){
1974
			/* nothing to append to */
1975
			for(;;){
1976
				p = Brdline(&ssp->in, '\n');
1977
				if(p == nil)
1978
					break;
1979
				p[Blinelen(&ssp->in)-1] = 0;
1980
				if(strcmp(p, "..") == 0)
1981
					break;
1982
			}
1983
			return;
1984
		}
1985
	}
1986
 
1987
	if(m == nil){
1988
		m = emalloc(sizeof(*m));
1989
		m->next = macros;
1990
		macros = m;
1991
		m->name = strdup(argv[1]);
1992
		m->val = nil;
1993
		len = 0;
1994
	}
1995
 
1996
	/* read up to a .. removing double backslashes */
1997
	for(;;){
1998
		p = Brdline(&ssp->in, '\n');
1999
		if(p == nil)
2000
			break;
2001
		p[Blinelen(&ssp->in)-1] = 0;
2002
		if(strcmp(p, "..") == 0)
2003
			break;
2004
		m->val = realloc(m->val, len + Blinelen(&ssp->in)+1);
2005
		cp = m->val + len;
2006
		while(*p){
2007
			r = *p++;
2008
			if(r == '\\' && *p == '\\')
2009
				p++;
2010
			*cp++ = r;
2011
		}
2012
		*cp++ = '\n';
2013
		len = cp - m->val;
2014
		*cp = 0;
2015
	}
2016
}
2017
 
2018
void
2019
g_hrule(int, char**)
2020
{
2021
	Bprint(&bout, "<HR>\n");
2022
}
2023
 
2024
void
2025
g_BX(int argc, char **argv)
2026
{
2027
	Bprint(&bout, "<HR>\n");
2028
	printargs(argc, argv);
2029
	Bprint(&bout, "<HR>\n");
2030
}
2031
 
2032
void
2033
g_IH(int, char**)
2034
{
2035
	Bprint(&bout, "Bell Laboratories, Naperville, Illinois, 60540\n");
2036
}
2037
 
2038
void
2039
g_MH(int, char**)
2040
{
2041
	Bprint(&bout, "Bell Laboratories, Murray Hill, NJ, 07974\n");
2042
}
2043
 
2044
void
2045
g_PY(int, char**)
2046
{
2047
	Bprint(&bout, "Bell Laboratories, Piscataway, NJ, 08854\n");
2048
}
2049
 
2050
void
2051
g_HO(int, char**)
2052
{
2053
	Bprint(&bout, "Bell Laboratories, Holmdel, NJ, 07733\n");
2054
}
2055
 
2056
void
2057
g_QS(int, char**)
2058
{
2059
	Bprint(&bout, "<BLOCKQUOTE>\n");
2060
}
2061
 
2062
void
2063
g_QE(int, char**)
2064
{
2065
	Bprint(&bout, "</BLOCKQUOTE>\n");
2066
}
2067
 
2068
void
2069
g_RS(int, char**)
2070
{
2071
	Bprint(&bout, "<DL><DD>\n");
2072
}
2073
 
2074
void
2075
g_RE(int, char**)
2076
{
2077
	Bprint(&bout, "</DL>\n");
2078
}
2079
 
2080
int gif;
2081
 
2082
void
2083
g_startgif(int, char **argv)
2084
{
2085
	int fd;
2086
	int pfd[2];
2087
	char *e, *p;
2088
	char name[32];
2089
	Dir *d;
2090
 
2091
	if(strcmp(argv[0], "EQ") == 0)
2092
		e = ".EN";
2093
	else if(strcmp(argv[0], "TS") == 0)
2094
		e = ".TE";
2095
	else if(strcmp(argv[0], "PS") == 0)
2096
		e = ".PE";
2097
	else
2098
		return;
2099
 
2100
	if(basename)
2101
		p = basename;
2102
	else{
2103
		p = strrchr(sstack[0].filename, '/');
2104
		if(p != nil)
2105
			p++;
2106
		else
2107
			p = sstack[0].filename;
2108
	}
2109
	snprint(name, sizeof(name), "%s.%d.gif", p, gif++);
2110
	fd = create(name, OWRITE, 0664);
2111
	if(fd < 0){
2112
		fprint(2, "ms2html: can't create %s: %r\n", name);
2113
		return;
2114
	}
2115
 
2116
	if(pipe(pfd) < 0){
2117
		fprint(2, "ms2html: can't create pipe: %r\n");
2118
		close(fd);
2119
		return;
2120
	}
2121
	switch(rfork(RFFDG|RFPROC)){
2122
	case -1:
2123
		fprint(2, "ms2html: can't fork: %r\n");
2124
		close(fd);
2125
		return;
2126
	case 0:
2127
		dup(fd, 1);
2128
		close(fd);
2129
		dup(pfd[0], 0);
2130
		close(pfd[0]);
2131
		close(pfd[1]);
2132
		execl("/bin/troff2gif", "troff2gif", nil);
2133
		fprint(2, "ms2html: couldn't exec troff2gif: %r\n");
2134
		_exits(nil);
2135
	default:
2136
		close(fd);
2137
		close(pfd[0]);
2138
		fprint(pfd[1], ".ll 7i\n");
2139
	/*	fprint(pfd[1], ".EQ\ndelim %s\n.EN\n", delim); */
2140
	/*	fprint(pfd[1], ".%s\n", argv[0]); */
2141
		for(;;){
2142
			p = Brdline(&ssp->in, '\n');
2143
			if(p == nil)
2144
				break;
2145
			ssp->lno++;
2146
			ssp->rlno++;
2147
			if(write(pfd[1], p, Blinelen(&ssp->in)) < 0)
2148
				break;
2149
			if(strncmp(p, e, 3) == 0)
2150
				break;
2151
		}
2152
		close(pfd[1]);
2153
		waitpid();
2154
		d = dirstat(name);
2155
		if(d == nil)
2156
			break;
2157
		if(d->length == 0){
2158
			remove(name);
2159
			free(d);
2160
			break;
2161
		}
2162
		free(d);
2163
		fprint(2, "ms2html: created auxiliary file %s\n", name);
2164
		Bprint(&bout, "<br><img src=\"%s\"><br>\n", name);
2165
		break;
2166
	}
2167
}
2168
 
2169
void
2170
g_lf(int argc, char **argv)
2171
{
2172
	if(argc > 2)
2173
		snprint(ssp->filename, sizeof(ssp->filename), argv[2]);
2174
	if(argc > 1)
2175
		ssp->rlno = atoi(argv[1]);
2176
}
2177
 
2178
void
2179
g_so(int argc, char **argv)
2180
{
2181
	ssp->lno++;
2182
	ssp->rlno++;
2183
	if(argc > 1)
2184
		pushsrc(argv[1]);
2185
}
2186
 
2187
 
2188
void
2189
g_BP(int argc, char **argv)
2190
{
2191
	int fd;
2192
	char *p, *ext;
2193
	char name[32];
2194
	Dir *d;
2195
 
2196
	if(argc < 2)
2197
		return;
2198
 
2199
	p = strrchr(argv[1], '/');
2200
	if(p != nil)
2201
		p++;
2202
	else
2203
		p = argv[1];
2204
 
2205
 
2206
	ext = strrchr(p, '.');
2207
	if(ext){
2208
		if(strcmp(ext, ".jpeg") == 0
2209
		|| strcmp(ext, ".gif") == 0){
2210
			Bprint(&bout, "<br><img src=\"%s\"><br>\n", argv[1]);
2211
			return;
2212
		}
2213
	}
2214
 
2215
 
2216
	snprint(name, sizeof(name), "%s.%d%d.gif", p, getpid(), gif++);
2217
	fd = create(name, OWRITE, 0664);
2218
	if(fd < 0){
2219
		fprint(2, "ms2html: can't create %s: %r\n", name);
2220
		return;
2221
	}
2222
 
2223
	switch(rfork(RFFDG|RFPROC)){
2224
	case -1:
2225
		fprint(2, "ms2html: can't fork: %r\n");
2226
		close(fd);
2227
		return;
2228
	case 0:
2229
		dup(fd, 1);
2230
		close(fd);
2231
		execl("/bin/ps2gif", "ps2gif", argv[1], nil);
2232
		fprint(2, "ms2html: couldn't exec ps2gif: %r\n");
2233
		_exits(nil);
2234
	default:
2235
		close(fd);
2236
		waitpid();
2237
		d = dirstat(name);
2238
		if(d == nil)
2239
			break;
2240
		if(d->length == 0){
2241
			remove(name);
2242
			free(d);
2243
			break;
2244
		}
2245
		free(d);
2246
		fprint(2, "ms2html: created auxiliary file %s\n", name);
2247
		Bprint(&bout, "<br><img src=\"%s\"><br>\n", name);
2248
		break;
2249
	}
2250
}
2251
 
2252
/* insert straight HTML into output */
2253
void
2254
g__H(int argc, char **argv)
2255
{
2256
	int i;
2257
 
2258
	for(i = 1; i < argc; i++)
2259
		Bprint(&bout, "%s ", argv[i]);
2260
	Bprint(&bout, "\n");
2261
}
2262
 
2263
/* HTML page title */
2264
void
2265
g__T(int argc, char **argv)
2266
{
2267
	if(titleseen)
2268
		return;
2269
 
2270
	Bprint(&bout, "<title>\n");
2271
	printargs(argc, argv);
2272
	Bprint(&bout, "</title></head><body>\n");
2273
	titleseen = 1;
2274
}
2275
 
2276
void
2277
g_nr(int argc, char **argv)
2278
{
2279
	char *val;
2280
 
2281
	if (argc > 1) {
2282
		if (argc == 2)
2283
			val = "0";
2284
		else
2285
			val = argv[2];
2286
		dsnr(argv[1], val, &numregs);
2287
	}
2288
}
2289
 
2290
void
2291
zerodivide(void)
2292
{
2293
	fprint(2, "stdin %d(%s:%d): division by 0\n",
2294
		ssp->lno, ssp->filename, ssp->rlno);
2295
}
2296
 
2297
int
2298
numval(char **pline, int recur)
2299
{
2300
	char *p;
2301
	int neg, x, y;
2302
 
2303
	x = neg = 0;
2304
	p = *pline;
2305
	while(*p == '-') {
2306
		neg = 1 - neg;
2307
		p++;
2308
	}
2309
	if (*p == '(') {
2310
		p++;
2311
		x = numval(&p, 1);
2312
		if (*p != ')')
2313
			goto done;
2314
		p++;
2315
	}
2316
	else while(*p >= '0' && *p <= '9')
2317
		x = 10*x + *p++ - '0';
2318
	if (neg)
2319
		x = -x;
2320
	if (recur)
2321
	    for(;;) {
2322
		switch(*p++) {
2323
		case '+':
2324
			x += numval(&p, 0);
2325
			continue;
2326
		case '-':
2327
			x -= numval(&p, 0);
2328
			continue;
2329
		case '*':
2330
			x *= numval(&p, 0);
2331
			continue;
2332
		case '/':
2333
			y = numval(&p, 0);
2334
			if (y == 0) {
2335
				zerodivide();
2336
				x = 0;
2337
				goto done;
2338
			}
2339
			x /= y;
2340
			continue;
2341
		case '<':
2342
			if (*p == '=') {
2343
				p++;
2344
				x = x <= numval(&p, 0);
2345
				continue;
2346
			}
2347
			x = x < numval(&p, 0);
2348
			continue;
2349
		case '>':
2350
			if (*p == '=') {
2351
				p++;
2352
				x = x >= numval(&p, 0);
2353
				continue;
2354
			}
2355
			x = x > numval(&p, 0);
2356
			continue;
2357
		case '=':
2358
			if (*p == '=')
2359
				p++;
2360
			x = x == numval(&p, 0);
2361
			continue;
2362
		case '&':
2363
			x &= numval(&p, 0);
2364
			continue;
2365
		case ':':
2366
			x |= numval(&p, 0);
2367
			continue;
2368
		case '%':
2369
			y = numval(&p, 0);
2370
			if (!y) {
2371
				zerodivide();
2372
				goto done;
2373
			}
2374
			x %= y;
2375
			continue;
2376
		}
2377
		--p;
2378
		break;
2379
	}
2380
 done:
2381
	*pline = p;
2382
	return x;
2383
}
2384
 
2385
int
2386
iftest(char *p, char **bp)
2387
{
2388
	char *p1;
2389
	int c, neg, rv;
2390
 
2391
	rv = neg = 0;
2392
	if (*p == '!') {
2393
		neg = 1;
2394
		p++;
2395
	}
2396
	c = *p;
2397
	if (c >= '0' && c <= '9' || c == '+' || c == '-' || c == '('/*)*/) {
2398
		if (numval(&p,1) >= 1)
2399
			rv = 1;
2400
		goto done;
2401
	}
2402
	switch(c) {
2403
	case 't':
2404
	case 'o':
2405
		rv = 1;
2406
	case 'n':
2407
	case 'e':
2408
		p++;
2409
		goto done;
2410
	}
2411
	for(p1 = ++p; *p != c; p++)
2412
		if (!*p)
2413
			goto done;
2414
	for(p++;;) {
2415
		if (*p != *p1++) {
2416
			while(*p && *p++ != c);
2417
			goto done;
2418
		}
2419
		if (*p++ == c)
2420
			break;
2421
	}
2422
	rv = 1;
2423
 done:
2424
	if (neg)
2425
		rv = 1 - rv;
2426
	while(*p == ' ' || *p == '\t')
2427
		p++;
2428
	*bp = p;
2429
	return rv;
2430
}
2431
 
2432
void
2433
scanline(char *p, char *e, int wantnl)
2434
{
2435
	int c;
2436
	Rune r;
2437
 
2438
	while((c = getrune()) == ' ' || c == '\t') ;
2439
	while(p < e) {
2440
		if (c < 0)
2441
			break;
2442
		if (c < Runeself) {
2443
			if (c == '\n') {
2444
				if (wantnl)
2445
					*p++ = c;
2446
				break;
2447
			}
2448
			*p++ = c;
2449
		}
2450
		else {
2451
			r = c;
2452
			p += runetochar(p, &r);
2453
		}
2454
		c = getrune();
2455
	}
2456
	*p = 0;
2457
}
2458
 
2459
void
2460
pushbody(char *line)
2461
{
2462
	char *b;
2463
 
2464
	if (line[0] == '\\' && line[1] == '{' /*}*/ )
2465
		line += 2;
2466
	if (strsp < Maxmstack - 1) {
2467
		pushstr(b = strdup(line));
2468
		mustfree[strsp] = b;
2469
	}
2470
}
2471
 
2472
void
2473
skipbody(char *line)
2474
{
2475
	int c, n;
2476
 
2477
	if (line[0] != '\\' || line[1] != '{' /*}*/ )
2478
		return;
2479
	for(n = 1;;) {
2480
		while((c = getrune()) != '\\')
2481
			if (c < 0)
2482
				return;
2483
		c = getrune();
2484
		if (c == '{')
2485
			n++;
2486
		else if ((c == '}' && (c = getrune()) == '\n' && !--n)
2487
			|| c < 0)
2488
			return;
2489
	}
2490
}
2491
 
2492
int
2493
ifstart(char *line, char *e, char **bp)
2494
{
2495
	int it;
2496
	char *b;
2497
 
2498
	b = copyline(line, e, 1);
2499
	ungetrune();
2500
	b[-1] = getrune();
2501
	scanline(b, e, 1);
2502
	it = iftest(line, bp);
2503
	return it;
2504
}
2505
 
2506
void
2507
g_ie(char *line, char *e)
2508
{
2509
	char *b;
2510
 
2511
	if (elsetop >= Maxif-1) {
2512
		fprint(2, "ms2html: .ie's too deep\n");
2513
		return;
2514
	}
2515
	if (ifwastrue[++elsetop] = ifstart(line, e, &b))
2516
		pushbody(b);
2517
	else
2518
		skipbody(b);
2519
}
2520
 
2521
void
2522
g_if(char *line, char *e)
2523
{
2524
	char *b;
2525
 
2526
	if (ifstart(line, e, &b))
2527
		pushbody(b);
2528
	else
2529
		skipbody(b);
2530
}
2531
 
2532
void
2533
g_el(char *line, char *e)
2534
{
2535
	if (elsetop < 0)
2536
		return;
2537
	scanline(line, e, 1);
2538
	if (ifwastrue[elsetop--])
2539
		skipbody(line);
2540
	else
2541
		pushbody(line);
2542
}
2543
 
2544
void
2545
g_ig(int argc, char **argv)
2546
{
2547
	char *p, *s;
2548
 
2549
	s = "..";
2550
	if (argc > 1)
2551
		s = argv[1];
2552
	for(;;) {
2553
		p = Brdline(&ssp->in, '\n');
2554
		if(p == nil)
2555
			break;
2556
		p[Blinelen(&ssp->in)-1] = 0;
2557
		if(strcmp(p, s) == 0)
2558
			break;
2559
	}
2560
}
2561
 
2562
void
2563
g_ds(char *line, char *e)
2564
{
2565
	char *b;
2566
 
2567
	b = copyline(line, e, 1);
2568
	if (b > line) {
2569
		copyline(b, e, 0);
2570
		if (*b == '"')
2571
			b++;
2572
		ds(line, b);
2573
	}
2574
}
2575
 
2576
void
2577
g_as(char *line, char *e)
2578
{
2579
	String *s;
2580
	char *b;
2581
 
2582
	b = copyline(line, e, 1);
2583
	if (b == line)
2584
		return;
2585
	copyline(b, e, 0);
2586
	if (*b == '"')
2587
		b++;
2588
	for(s = strings; s != nil; s = s->next)
2589
		if(strcmp(line, s->name) == 0)
2590
			break;
2591
 
2592
	if(s == nil){
2593
		ds(line, b);
2594
		return;
2595
	}
2596
 
2597
	s->val = realloc(s->val, strlen(s->val) + strlen(b) + 1);
2598
	strcat(s->val, b);
2599
}
2600
 
2601
void
2602
g_BS(int argc, char **argv)
2603
{
2604
	int i;
2605
 
2606
	if (argc > 1 && !weBref) {
2607
		Bprint(&bout, "<a href=\"%s\"", argv[1]);
2608
		for(i = 2; i < argc; i++)
2609
			Bprint(&bout, " %s", argv[i]);
2610
		Bprint(&bout, ">");
2611
		weBref = 1;
2612
	}
2613
}
2614
 
2615
void
2616
g_BE(int, char**)
2617
{
2618
	if (weBref) {
2619
		Bprint(&bout, "</a>");
2620
		weBref = 0;
2621
	}
2622
}
2623
 
2624
void
2625
g_LB(int argc, char **argv)
2626
{
2627
	if (argc > 1) {
2628
		if (weBref)
2629
			g_BE(0,nil);
2630
		Bprint(&bout, "<a name=\"%s\"></a>", argv[1]);
2631
	}
2632
}
2633
 
2634
void
2635
g_RT(int, char**)
2636
{
2637
	g_BE(0,nil);
2638
	dohanginghead();
2639
	closel();
2640
	closefont();
2641
}