Annotation of mandoc/term.c, Revision 1.16
1.16 ! kristaps 1: /* $Id: term.c,v 1.15 2009/02/24 15:01:15 kristaps Exp $ */
1.1 kristaps 2: /*
1.10 kristaps 3: * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se>
1.1 kristaps 4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the
7: * above copyright notice and this permission notice appear in all
8: * copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11: * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12: * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13: * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14: * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15: * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16: * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17: * PERFORMANCE OF THIS SOFTWARE.
18: */
19: #include <assert.h>
20: #include <stdlib.h>
21: #include <string.h>
22:
1.10 kristaps 23: #include "term.h"
24:
25: #define INDENT 4
26:
27: /*
28: * Performs actions on nodes of the abstract syntax tree. Both pre- and
29: * post-fix operations are defined here.
30: */
31:
32: /* FIXME: indent/tab. */
1.12 kristaps 33: /* FIXME: handle nested lists. */
1.10 kristaps 34:
35: #define TTYPE_PROG 0
36: #define TTYPE_CMD_FLAG 1
37: #define TTYPE_CMD_ARG 2
38: #define TTYPE_SECTION 3
39: #define TTYPE_FUNC_DECL 4
40: #define TTYPE_VAR_DECL 5
41: #define TTYPE_FUNC_TYPE 6
42: #define TTYPE_FUNC_NAME 7
43: #define TTYPE_FUNC_ARG 8
44: #define TTYPE_LINK 9
45: #define TTYPE_SSECTION 10
46: #define TTYPE_FILE 11
1.11 kristaps 47: #define TTYPE_EMPH 12
1.14 kristaps 48: #define TTYPE_CONFIG 13
49: #define TTYPE_CMD 14
50: #define TTYPE_INCLUDE 15
51: #define TTYPE_NMAX 16
1.10 kristaps 52:
53: /*
54: * These define "styles" for element types, like command arguments or
55: * executable names. This is useful when multiple macros must decorate
56: * the same thing (like .Ex -std cmd and .Nm cmd).
57: */
58:
59: const int ttypes[TTYPE_NMAX] = {
60: TERMP_BOLD, /* TTYPE_PROG */
61: TERMP_BOLD, /* TTYPE_CMD_FLAG */
62: TERMP_UNDERLINE, /* TTYPE_CMD_ARG */
63: TERMP_BOLD, /* TTYPE_SECTION */
64: TERMP_BOLD, /* TTYPE_FUNC_DECL */
65: TERMP_UNDERLINE, /* TTYPE_VAR_DECL */
66: TERMP_UNDERLINE, /* TTYPE_FUNC_TYPE */
67: TERMP_BOLD, /* TTYPE_FUNC_NAME */
68: TERMP_UNDERLINE, /* TTYPE_FUNC_ARG */
69: TERMP_UNDERLINE, /* TTYPE_LINK */
70: TERMP_BOLD, /* TTYPE_SSECTION */
1.11 kristaps 71: TERMP_UNDERLINE, /* TTYPE_FILE */
1.14 kristaps 72: TERMP_UNDERLINE, /* TTYPE_EMPH */
73: TERMP_BOLD, /* TTYPE_CONFIG */
74: TERMP_BOLD, /* TTYPE_CMD */
75: TERMP_BOLD /* TTYPE_INCLUDE */
1.10 kristaps 76: };
1.7 kristaps 77:
1.10 kristaps 78: static int arg_hasattr(int, size_t,
79: const struct mdoc_arg *);
80: static int arg_getattr(int, size_t,
81: const struct mdoc_arg *);
1.12 kristaps 82: static size_t arg_offset(const struct mdoc_arg *);
83: static size_t arg_width(const struct mdoc_arg *);
1.10 kristaps 84:
85: /*
86: * What follows describes prefix and postfix operations for the abstract
87: * syntax tree descent.
88: */
1.1 kristaps 89:
1.10 kristaps 90: #define DECL_ARGS \
91: struct termp *p, \
92: const struct mdoc_meta *meta, \
93: const struct mdoc_node *node
94:
95: #define DECL_PRE(name) \
96: static int name##_pre(DECL_ARGS)
97: #define DECL_POST(name) \
98: static void name##_post(DECL_ARGS)
99: #define DECL_PREPOST(name) \
100: DECL_PRE(name); \
101: DECL_POST(name);
102:
103: DECL_PREPOST(termp_aq);
104: DECL_PREPOST(termp_ar);
1.12 kristaps 105: DECL_PREPOST(termp_bd);
1.15 kristaps 106: DECL_PREPOST(termp_bq);
1.14 kristaps 107: DECL_PREPOST(termp_cd);
108: DECL_PREPOST(termp_cm);
1.10 kristaps 109: DECL_PREPOST(termp_d1);
110: DECL_PREPOST(termp_dq);
1.11 kristaps 111: DECL_PREPOST(termp_em);
1.10 kristaps 112: DECL_PREPOST(termp_fa);
113: DECL_PREPOST(termp_fd);
114: DECL_PREPOST(termp_fl);
115: DECL_PREPOST(termp_fn);
1.16 ! kristaps 116: DECL_PREPOST(termp_fo);
1.10 kristaps 117: DECL_PREPOST(termp_ft);
1.14 kristaps 118: DECL_PREPOST(termp_ic);
119: DECL_PREPOST(termp_in);
1.10 kristaps 120: DECL_PREPOST(termp_it);
121: DECL_PREPOST(termp_nm);
122: DECL_PREPOST(termp_op);
123: DECL_PREPOST(termp_pa);
124: DECL_PREPOST(termp_pf);
1.15 kristaps 125: DECL_PREPOST(termp_pq);
1.10 kristaps 126: DECL_PREPOST(termp_qq);
127: DECL_PREPOST(termp_sh);
128: DECL_PREPOST(termp_ss);
129: DECL_PREPOST(termp_sq);
130: DECL_PREPOST(termp_sx);
131: DECL_PREPOST(termp_va);
132: DECL_PREPOST(termp_vt);
133:
1.14 kristaps 134: DECL_PRE(termp_at);
1.15 kristaps 135: DECL_PRE(termp_bsx);
1.10 kristaps 136: DECL_PRE(termp_bx);
137: DECL_PRE(termp_ex);
1.16 ! kristaps 138: DECL_PRE(termp_fx);
1.10 kristaps 139: DECL_PRE(termp_nd);
140: DECL_PRE(termp_ns);
141: DECL_PRE(termp_nx);
142: DECL_PRE(termp_ox);
143: DECL_PRE(termp_pp);
1.14 kristaps 144: DECL_PRE(termp_rv);
145: DECL_PRE(termp_st);
1.10 kristaps 146: DECL_PRE(termp_ud);
1.16 ! kristaps 147: DECL_PRE(termp_ux);
1.10 kristaps 148: DECL_PRE(termp_xr);
149:
150: DECL_POST(termp_bl);
151:
152: const struct termact __termacts[MDOC_MAX] = {
153: { NULL, NULL }, /* \" */
154: { NULL, NULL }, /* Dd */
155: { NULL, NULL }, /* Dt */
156: { NULL, NULL }, /* Os */
157: { termp_sh_pre, termp_sh_post }, /* Sh */
158: { termp_ss_pre, termp_ss_post }, /* Ss */
159: { termp_pp_pre, NULL }, /* Pp */
160: { termp_d1_pre, termp_d1_post }, /* D1 */
161: { NULL, NULL }, /* Dl */
1.12 kristaps 162: { termp_bd_pre, termp_bd_post }, /* Bd */
1.10 kristaps 163: { NULL, NULL }, /* Ed */
164: { NULL, termp_bl_post }, /* Bl */
165: { NULL, NULL }, /* El */
166: { termp_it_pre, termp_it_post }, /* It */
167: { NULL, NULL }, /* Ad */
168: { NULL, NULL }, /* An */
169: { termp_ar_pre, termp_ar_post }, /* Ar */
1.14 kristaps 170: { termp_cd_pre, termp_cd_post }, /* Cd */
171: { termp_cm_pre, termp_cm_post }, /* Cm */
1.10 kristaps 172: { NULL, NULL }, /* Dv */
173: { NULL, NULL }, /* Er */
174: { NULL, NULL }, /* Ev */
175: { termp_ex_pre, NULL }, /* Ex */
176: { termp_fa_pre, termp_fa_post }, /* Fa */
177: { termp_fd_pre, termp_fd_post }, /* Fd */
178: { termp_fl_pre, termp_fl_post }, /* Fl */
179: { termp_fn_pre, termp_fn_post }, /* Fn */
180: { termp_ft_pre, termp_ft_post }, /* Ft */
1.14 kristaps 181: { termp_ic_pre, termp_ic_post }, /* Ic */
182: { termp_in_pre, termp_in_post }, /* In */
1.10 kristaps 183: { NULL, NULL }, /* Li */
184: { termp_nd_pre, NULL }, /* Nd */
185: { termp_nm_pre, termp_nm_post }, /* Nm */
186: { termp_op_pre, termp_op_post }, /* Op */
187: { NULL, NULL }, /* Ot */
188: { termp_pa_pre, termp_pa_post }, /* Pa */
1.14 kristaps 189: { termp_rv_pre, NULL }, /* Rv */
190: { termp_st_pre, NULL }, /* St */
1.10 kristaps 191: { termp_va_pre, termp_va_post }, /* Va */
192: { termp_vt_pre, termp_vt_post }, /* Vt */
193: { termp_xr_pre, NULL }, /* Xr */
194: { NULL, NULL }, /* %A */
195: { NULL, NULL }, /* %B */
196: { NULL, NULL }, /* %D */
197: { NULL, NULL }, /* %I */
198: { NULL, NULL }, /* %J */
199: { NULL, NULL }, /* %N */
200: { NULL, NULL }, /* %O */
201: { NULL, NULL }, /* %P */
202: { NULL, NULL }, /* %R */
203: { NULL, NULL }, /* %T */
204: { NULL, NULL }, /* %V */
205: { NULL, NULL }, /* Ac */
1.14 kristaps 206: { termp_aq_pre, termp_aq_post }, /* Ao */
1.10 kristaps 207: { termp_aq_pre, termp_aq_post }, /* Aq */
1.14 kristaps 208: { termp_at_pre, NULL }, /* At */
1.10 kristaps 209: { NULL, NULL }, /* Bc */
210: { NULL, NULL }, /* Bf */
1.15 kristaps 211: { termp_bq_pre, termp_bq_post }, /* Bo */
212: { termp_bq_pre, termp_bq_post }, /* Bq */
213: { termp_bsx_pre, NULL }, /* Bsx */
1.10 kristaps 214: { termp_bx_pre, NULL }, /* Bx */
215: { NULL, NULL }, /* Db */
216: { NULL, NULL }, /* Dc */
1.15 kristaps 217: { termp_dq_pre, termp_dq_post }, /* Do */
1.10 kristaps 218: { termp_dq_pre, termp_dq_post }, /* Dq */
219: { NULL, NULL }, /* Ec */
220: { NULL, NULL }, /* Ef */
1.11 kristaps 221: { termp_em_pre, termp_em_post }, /* Em */
1.10 kristaps 222: { NULL, NULL }, /* Eo */
1.16 ! kristaps 223: { termp_fx_pre, NULL }, /* Fx */
1.10 kristaps 224: { NULL, NULL }, /* Ms */
225: { NULL, NULL }, /* No */
226: { termp_ns_pre, NULL }, /* Ns */
227: { termp_nx_pre, NULL }, /* Nx */
228: { termp_ox_pre, NULL }, /* Ox */
229: { NULL, NULL }, /* Pc */
230: { termp_pf_pre, termp_pf_post }, /* Pf */
1.15 kristaps 231: { termp_pq_pre, termp_pq_post }, /* Po */
232: { termp_pq_pre, termp_pq_post }, /* Pq */
1.10 kristaps 233: { NULL, NULL }, /* Qc */
1.16 ! kristaps 234: { termp_sq_pre, termp_sq_post }, /* Ql */
1.15 kristaps 235: { termp_qq_pre, termp_qq_post }, /* Qo */
1.10 kristaps 236: { termp_qq_pre, termp_qq_post }, /* Qq */
237: { NULL, NULL }, /* Re */
238: { NULL, NULL }, /* Rs */
239: { NULL, NULL }, /* Sc */
1.15 kristaps 240: { termp_sq_pre, termp_sq_post }, /* So */
1.10 kristaps 241: { termp_sq_pre, termp_sq_post }, /* Sq */
242: { NULL, NULL }, /* Sm */
243: { termp_sx_pre, termp_sx_post }, /* Sx */
244: { NULL, NULL }, /* Sy */
245: { NULL, NULL }, /* Tn */
1.16 ! kristaps 246: { termp_ux_pre, NULL }, /* Ux */
1.10 kristaps 247: { NULL, NULL }, /* Xc */
248: { NULL, NULL }, /* Xo */
1.16 ! kristaps 249: { termp_fo_pre, termp_fo_post }, /* Fo */
1.10 kristaps 250: { NULL, NULL }, /* Fc */
1.16 ! kristaps 251: { termp_op_pre, termp_op_post }, /* Oo */
1.10 kristaps 252: { NULL, NULL }, /* Oc */
253: { NULL, NULL }, /* Bk */
254: { NULL, NULL }, /* Ek */
255: { NULL, NULL }, /* Bt */
256: { NULL, NULL }, /* Hf */
257: { NULL, NULL }, /* Fr */
258: { termp_ud_pre, NULL }, /* Ud */
1.2 kristaps 259: };
260:
1.10 kristaps 261: const struct termact *termacts = __termacts;
262:
263:
264: static size_t
1.12 kristaps 265: arg_width(const struct mdoc_arg *arg)
1.10 kristaps 266: {
1.12 kristaps 267:
268: /* TODO */
269: assert(*arg->value);
270: return(strlen(*arg->value));
271: }
272:
273:
274: static size_t
275: arg_offset(const struct mdoc_arg *arg)
276: {
277:
278: /* TODO */
279: assert(*arg->value);
280: if (0 == strcmp(*arg->value, "indent"))
1.10 kristaps 281: return(INDENT);
1.12 kristaps 282: if (0 == strcmp(*arg->value, "indent-two"))
1.10 kristaps 283: return(INDENT * 2);
284:
1.12 kristaps 285: return(strlen(*arg->value));
1.10 kristaps 286: }
287:
1.3 kristaps 288:
1.10 kristaps 289: static int
290: arg_hasattr(int arg, size_t argc, const struct mdoc_arg *argv)
1.2 kristaps 291: {
292:
1.10 kristaps 293: return(-1 != arg_getattr(arg, argc, argv));
294: }
295:
296:
297: static int
298: arg_getattr(int arg, size_t argc, const struct mdoc_arg *argv)
299: {
300: int i;
301:
302: for (i = 0; i < (int)argc; i++)
303: if (argv[i].arg == arg)
304: return(i);
305: return(-1);
306: }
307:
308:
309: /* ARGSUSED */
310: static int
311: termp_dq_pre(DECL_ARGS)
312: {
313:
314: if (MDOC_BODY != node->type)
315: return(1);
316:
317: word(p, "``");
318: p->flags |= TERMP_NOSPACE;
319: return(1);
320: }
321:
322:
323: /* ARGSUSED */
324: static void
325: termp_dq_post(DECL_ARGS)
326: {
327:
328: if (MDOC_BODY != node->type)
329: return;
1.3 kristaps 330:
1.10 kristaps 331: p->flags |= TERMP_NOSPACE;
332: word(p, "''");
333: }
1.2 kristaps 334:
1.3 kristaps 335:
1.10 kristaps 336: /* ARGSUSED */
337: static void
338: termp_it_post(DECL_ARGS)
339: {
340: const struct mdoc_node *n, *it;
341: const struct mdoc_block *bl;
342: int i;
1.12 kristaps 343: size_t width, offset;
1.2 kristaps 344:
1.8 kristaps 345: /*
1.10 kristaps 346: * This (and termp_it_pre()) are the most complicated functions
347: * here. They must account for a considerable number of
348: * switches that completely change the output behaviour, like
349: * -tag versus -column. Yech.
1.8 kristaps 350: */
1.10 kristaps 351:
352: switch (node->type) {
353: case (MDOC_BODY):
354: /* FALLTHROUGH */
355: case (MDOC_HEAD):
356: break;
357: default:
1.8 kristaps 358: return;
359: }
360:
1.10 kristaps 361: it = node->parent;
362: assert(MDOC_BLOCK == it->type);
363: assert(MDOC_It == it->tok);
364:
365: n = it->parent;
366: assert(MDOC_BODY == n->type);
367: assert(MDOC_Bl == n->tok);
368: n = n->parent;
369: bl = &n->data.block;
370:
371: /* If `-tag', adjust our margins accordingly. */
372:
373: if (arg_hasattr(MDOC_Tag, bl->argc, bl->argv)) {
1.12 kristaps 374: flushln(p);
375:
376: /* FIXME: this should auto-size. */
1.10 kristaps 377: i = arg_getattr(MDOC_Width, bl->argc, bl->argv);
1.12 kristaps 378: width = i >= 0 ? arg_width(&bl->argv[i]) : 10;
379:
380: /* FIXME: nesting! Should happen at block. */
381: i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
382: offset = i >= 0 ? arg_width(&bl->argv[i]) : 0;
1.10 kristaps 383:
384: if (MDOC_HEAD == node->type) {
385: p->rmargin = p->maxrmargin;
1.12 kristaps 386: p->offset -= offset;
1.10 kristaps 387: p->flags &= ~TERMP_NOBREAK;
388: } else {
1.12 kristaps 389: p->offset -= width;
1.10 kristaps 390: p->flags &= ~TERMP_NOLPAD;
1.2 kristaps 391: }
1.10 kristaps 392: }
1.2 kristaps 393:
1.10 kristaps 394: if (arg_hasattr(MDOC_Ohang, bl->argc, bl->argv)) {
395: i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
1.12 kristaps 396: offset = i >= 0 ? arg_offset(&bl->argv[i]) : 0;
1.3 kristaps 397:
1.10 kristaps 398: flushln(p);
1.12 kristaps 399: p->offset -= offset;
1.10 kristaps 400: return;
1.2 kristaps 401: }
1.10 kristaps 402: }
403:
404:
405: /* ARGSUSED */
406: static int
407: termp_it_pre(DECL_ARGS)
408: {
409: const struct mdoc_node *n, *it;
410: const struct mdoc_block *bl;
411: int i;
1.12 kristaps 412: size_t width, offset;
1.2 kristaps 413:
1.3 kristaps 414: /*
1.10 kristaps 415: * Also see termp_it_post() for general comments.
1.3 kristaps 416: */
417:
1.10 kristaps 418: switch (node->type) {
419: case (MDOC_BODY):
420: /* FALLTHROUGH */
421: case (MDOC_HEAD):
422: it = node->parent;
423: break;
424: case (MDOC_BLOCK):
425: it = node;
426: break;
427: default:
428: return(1);
429: }
430:
431: assert(MDOC_BLOCK == it->type);
432: assert(MDOC_It == it->tok);
433:
434: n = it->parent;
435: assert(MDOC_BODY == n->type);
436: assert(MDOC_Bl == n->tok);
437: n = n->parent;
438: bl = &n->data.block;
439:
440: /* If `-compact', don't assert vertical space. */
441:
442: if (MDOC_BLOCK == node->type) {
443: if (arg_hasattr(MDOC_Compact, bl->argc, bl->argv))
444: newln(p);
445: else
446: vspace(p);
447: return(1);
448: }
449:
450: assert(MDOC_HEAD == node->type
451: || MDOC_BODY == node->type);
452:
1.12 kristaps 453: /* FIXME: see termp_it_post(). */
454:
1.10 kristaps 455: /* If `-tag', adjust our margins accordingly. */
456:
457: if (arg_hasattr(MDOC_Tag, bl->argc, bl->argv)) {
1.12 kristaps 458: p->flags |= TERMP_NOSPACE;
459:
1.10 kristaps 460: i = arg_getattr(MDOC_Width, bl->argc, bl->argv);
1.12 kristaps 461: width = i >= 0 ? arg_width(&bl->argv[i]) : 10;
1.10 kristaps 462:
1.12 kristaps 463: i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
464: offset = i >= 0 ? arg_offset(&bl->argv[i]) : 0;
1.10 kristaps 465:
466: if (MDOC_HEAD == node->type) {
467: p->flags |= TERMP_NOBREAK;
1.12 kristaps 468: p->offset += offset;
1.10 kristaps 469: p->rmargin = p->offset + width;
470: } else {
471: p->flags |= TERMP_NOSPACE;
472: p->flags |= TERMP_NOLPAD;
1.12 kristaps 473: p->offset += width;
1.10 kristaps 474: }
475: return(1);
476: }
477:
478: /* If `-ohang', adjust left-margin. */
479:
480: if (arg_hasattr(MDOC_Ohang, bl->argc, bl->argv)) {
481: i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
1.12 kristaps 482: offset = i >= 0 ? arg_offset(&bl->argv[i]) : 0;
1.10 kristaps 483:
484: p->flags |= TERMP_NOSPACE;
1.12 kristaps 485: p->offset += offset;
1.10 kristaps 486: return(1);
487: }
1.3 kristaps 488:
1.10 kristaps 489: return(1);
1.2 kristaps 490: }
491:
492:
1.10 kristaps 493: /* ARGSUSED */
494: static void
495: termp_nm_post(DECL_ARGS)
496: {
497:
498: p->flags &= ~ttypes[TTYPE_PROG];
499: }
500:
501:
502: /* ARGSUSED */
503: static void
504: termp_fl_post(DECL_ARGS)
505: {
506:
507: p->flags &= ~ttypes[TTYPE_CMD_FLAG];
508: }
509:
510:
511: /* ARGSUSED */
512: static int
513: termp_ar_pre(DECL_ARGS)
514: {
515:
516: p->flags |= ttypes[TTYPE_CMD_ARG];
517: if (NULL == node->child)
518: word(p, "...");
519: return(1);
520: }
521:
522:
523: /* ARGSUSED */
524: static int
525: termp_nm_pre(DECL_ARGS)
526: {
527:
528: p->flags |= ttypes[TTYPE_PROG];
529: if (NULL == node->child)
530: word(p, meta->name);
531: return(1);
532: }
533:
534:
535: /* ARGSUSED */
536: static int
537: termp_ns_pre(DECL_ARGS)
1.2 kristaps 538: {
539:
540: p->flags |= TERMP_NOSPACE;
1.10 kristaps 541: return(1);
542: }
543:
544:
545: /* ARGSUSED */
546: static int
547: termp_pp_pre(DECL_ARGS)
548: {
549:
550: vspace(p);
551: return(1);
552: }
553:
554:
555: /* ARGSUSED */
556: static void
557: termp_ar_post(DECL_ARGS)
558: {
559:
560: p->flags &= ~ttypes[TTYPE_CMD_ARG];
561: }
562:
563:
564: /* ARGSUSED */
565: static int
1.14 kristaps 566: termp_st_pre(DECL_ARGS)
567: {
568: const char *tp;
569:
570: assert(1 == node->data.elem.argc);
571:
572: tp = mdoc_st2a(node->data.elem.argv[0].arg);
573: word(p, tp);
574:
575: return(1);
576: }
577:
578:
579: /* ARGSUSED */
580: static int
581: termp_rv_pre(DECL_ARGS)
582: {
583: int i;
584:
585: i = arg_getattr(MDOC_Std, node->data.elem.argc,
586: node->data.elem.argv);
587: assert(i >= 0);
588:
589: newln(p);
590: word(p, "The");
591:
592: p->flags |= ttypes[TTYPE_FUNC_NAME];
593: word(p, *node->data.elem.argv[i].value);
594: p->flags &= ~ttypes[TTYPE_FUNC_NAME];
595:
596: word(p, "() function returns the value 0 if successful;");
597: word(p, "otherwise the value -1 is returned and the");
598: word(p, "global variable");
599:
600: p->flags |= ttypes[TTYPE_VAR_DECL];
601: word(p, "errno");
602: p->flags &= ~ttypes[TTYPE_VAR_DECL];
603:
604: word(p, "is set to indicate the error.");
605:
606: return(1);
607: }
608:
609:
610: /* ARGSUSED */
611: static int
1.10 kristaps 612: termp_ex_pre(DECL_ARGS)
613: {
614: int i;
615:
616: i = arg_getattr(MDOC_Std, node->data.elem.argc,
617: node->data.elem.argv);
618: assert(i >= 0);
619:
620: word(p, "The");
621: p->flags |= ttypes[TTYPE_PROG];
622: word(p, *node->data.elem.argv[i].value);
623: p->flags &= ~ttypes[TTYPE_PROG];
624: word(p, "utility exits 0 on success, and >0 if an error occurs.");
625:
626: return(1);
627: }
628:
629:
630: /* ARGSUSED */
631: static int
632: termp_nd_pre(DECL_ARGS)
633: {
634:
635: word(p, "\\-");
636: return(1);
637: }
638:
639:
640: /* ARGSUSED */
641: static void
642: termp_bl_post(DECL_ARGS)
643: {
644:
645: if (MDOC_BLOCK == node->type)
646: newln(p);
647: }
648:
649:
650: /* ARGSUSED */
651: static void
652: termp_op_post(DECL_ARGS)
653: {
654:
655: if (MDOC_BODY != node->type)
1.2 kristaps 656: return;
1.10 kristaps 657: p->flags |= TERMP_NOSPACE;
658: word(p, "\\(rB");
659: }
660:
661:
662: /* ARGSUSED */
663: static void
664: termp_sh_post(DECL_ARGS)
665: {
666:
667: switch (node->type) {
668: case (MDOC_HEAD):
669: p->flags &= ~ttypes[TTYPE_SECTION];
670: newln(p);
671: break;
672: case (MDOC_BODY):
673: newln(p);
674: p->offset = 0;
675: break;
676: default:
677: break;
678: }
679: }
680:
681:
682: /* ARGSUSED */
683: static int
684: termp_xr_pre(DECL_ARGS)
685: {
686: const struct mdoc_node *n;
687:
688: n = node->child;
689: assert(n);
690:
691: assert(MDOC_TEXT == n->type);
692: word(p, n->data.text.string);
693:
694: if (NULL == (n = n->next))
695: return(0);
696:
697: assert(MDOC_TEXT == n->type);
698: p->flags |= TERMP_NOSPACE;
699: word(p, "(");
700: p->flags |= TERMP_NOSPACE;
701: word(p, n->data.text.string);
702: p->flags |= TERMP_NOSPACE;
703: word(p, ")");
704:
705: return(0);
1.2 kristaps 706: }
707:
708:
1.10 kristaps 709: /* ARGSUSED */
710: static int
711: termp_vt_pre(DECL_ARGS)
1.2 kristaps 712: {
713:
1.10 kristaps 714: /* FIXME: this can be "type name". */
715: p->flags |= ttypes[TTYPE_VAR_DECL];
716: return(1);
1.2 kristaps 717: }
718:
719:
1.10 kristaps 720: /* ARGSUSED */
1.2 kristaps 721: static void
1.10 kristaps 722: termp_vt_post(DECL_ARGS)
723: {
724:
725: p->flags &= ~ttypes[TTYPE_VAR_DECL];
726: if (node->sec == SEC_SYNOPSIS)
727: vspace(p);
728: }
729:
730:
731: /* ARGSUSED */
732: static int
733: termp_fd_pre(DECL_ARGS)
1.2 kristaps 734: {
735:
1.10 kristaps 736: /*
737: * FIXME: this naming is bad. This value is used, in general,
738: * for the #include header or other preprocessor statement.
739: */
740: p->flags |= ttypes[TTYPE_FUNC_DECL];
741: return(1);
1.2 kristaps 742: }
743:
744:
1.10 kristaps 745: /* ARGSUSED */
1.2 kristaps 746: static void
1.10 kristaps 747: termp_fd_post(DECL_ARGS)
1.2 kristaps 748: {
749:
1.10 kristaps 750: p->flags &= ~ttypes[TTYPE_FUNC_DECL];
751: if (node->sec == SEC_SYNOPSIS)
752: vspace(p);
753:
754: }
755:
756:
757: /* ARGSUSED */
758: static int
759: termp_sh_pre(DECL_ARGS)
760: {
1.2 kristaps 761:
1.10 kristaps 762: switch (node->type) {
763: case (MDOC_HEAD):
764: vspace(p);
765: p->flags |= ttypes[TTYPE_SECTION];
1.2 kristaps 766: break;
1.10 kristaps 767: case (MDOC_BODY):
768: p->offset = INDENT;
1.2 kristaps 769: break;
1.10 kristaps 770: default:
771: break;
772: }
773: return(1);
774: }
775:
776:
777: /* ARGSUSED */
778: static int
779: termp_op_pre(DECL_ARGS)
780: {
781:
782: switch (node->type) {
783: case (MDOC_BODY):
784: word(p, "\\(lB");
785: p->flags |= TERMP_NOSPACE;
1.2 kristaps 786: break;
787: default:
1.10 kristaps 788: break;
1.2 kristaps 789: }
1.10 kristaps 790: return(1);
791: }
792:
793:
794: /* ARGSUSED */
795: static int
796: termp_ud_pre(DECL_ARGS)
797: {
798:
799: word(p, "currently under development.");
800: return(1);
801: }
802:
803:
804: /* ARGSUSED */
805: static int
806: termp_fl_pre(DECL_ARGS)
807: {
808:
809: p->flags |= ttypes[TTYPE_CMD_FLAG];
810: word(p, "\\-");
811: p->flags |= TERMP_NOSPACE;
812: return(1);
813: }
814:
815:
816: /* ARGSUSED */
817: static int
818: termp_d1_pre(DECL_ARGS)
819: {
820:
821: if (MDOC_BODY != node->type)
822: return(1);
823: newln(p);
824: p->offset += INDENT;
825: return(1);
1.2 kristaps 826: }
827:
828:
1.10 kristaps 829: /* ARGSUSED */
1.2 kristaps 830: static void
1.10 kristaps 831: termp_d1_post(DECL_ARGS)
832: {
833:
834: if (MDOC_BODY != node->type)
835: return;
836: newln(p);
837: p->offset -= INDENT;
838: }
839:
840:
841: /* ARGSUSED */
842: static int
843: termp_aq_pre(DECL_ARGS)
1.6 kristaps 844: {
845:
1.10 kristaps 846: if (MDOC_BODY != node->type)
847: return(1);
848: word(p, "<");
849: p->flags |= TERMP_NOSPACE;
850: return(1);
851: }
1.6 kristaps 852:
853:
1.10 kristaps 854: /* ARGSUSED */
855: static void
856: termp_aq_post(DECL_ARGS)
857: {
1.6 kristaps 858:
1.10 kristaps 859: if (MDOC_BODY != node->type)
1.6 kristaps 860: return;
1.10 kristaps 861: p->flags |= TERMP_NOSPACE;
862: word(p, ">");
863: }
1.6 kristaps 864:
1.10 kristaps 865:
866: /* ARGSUSED */
867: static int
868: termp_ft_pre(DECL_ARGS)
869: {
870:
871: p->flags |= ttypes[TTYPE_FUNC_TYPE];
872: return(1);
1.6 kristaps 873: }
874:
875:
1.10 kristaps 876: /* ARGSUSED */
1.6 kristaps 877: static void
1.10 kristaps 878: termp_ft_post(DECL_ARGS)
1.2 kristaps 879: {
880:
1.10 kristaps 881: p->flags &= ~ttypes[TTYPE_FUNC_TYPE];
882: if (node->sec == SEC_SYNOPSIS)
883: newln(p);
884:
885: }
1.2 kristaps 886:
887:
1.10 kristaps 888: /* ARGSUSED */
889: static int
890: termp_fn_pre(DECL_ARGS)
891: {
892: const struct mdoc_node *n;
893:
894: assert(node->child);
895: assert(MDOC_TEXT == node->child->type);
1.2 kristaps 896:
1.10 kristaps 897: /* FIXME: can be "type funcname" "type varname"... */
1.2 kristaps 898:
1.10 kristaps 899: p->flags |= ttypes[TTYPE_FUNC_NAME];
900: word(p, node->child->data.text.string);
901: p->flags &= ~ttypes[TTYPE_FUNC_NAME];
902:
903: word(p, "(");
904:
905: p->flags |= TERMP_NOSPACE;
906: for (n = node->child->next; n; n = n->next) {
907: assert(MDOC_TEXT == n->type);
908: p->flags |= ttypes[TTYPE_FUNC_ARG];
909: word(p, n->data.text.string);
910: p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1.16 ! kristaps 911: if (n->next)
1.10 kristaps 912: word(p, ",");
1.6 kristaps 913: }
1.2 kristaps 914:
1.10 kristaps 915: word(p, ")");
916:
917: if (SEC_SYNOPSIS == node->sec)
918: word(p, ";");
919:
920: return(0);
1.2 kristaps 921: }
922:
923:
1.10 kristaps 924: /* ARGSUSED */
925: static void
926: termp_fn_post(DECL_ARGS)
1.2 kristaps 927: {
928:
1.10 kristaps 929: if (node->sec == SEC_SYNOPSIS)
930: vspace(p);
931:
932: }
1.2 kristaps 933:
934:
1.10 kristaps 935: /* ARGSUSED */
936: static int
937: termp_sx_pre(DECL_ARGS)
938: {
1.8 kristaps 939:
1.10 kristaps 940: p->flags |= ttypes[TTYPE_LINK];
941: return(1);
1.2 kristaps 942: }
943:
944:
1.10 kristaps 945: /* ARGSUSED */
1.1 kristaps 946: static void
1.10 kristaps 947: termp_sx_post(DECL_ARGS)
1.2 kristaps 948: {
949:
1.10 kristaps 950: p->flags &= ~ttypes[TTYPE_LINK];
951: }
1.2 kristaps 952:
1.4 kristaps 953:
1.10 kristaps 954: /* ARGSUSED */
955: static int
956: termp_fa_pre(DECL_ARGS)
957: {
1.16 ! kristaps 958: struct mdoc_node *n;
! 959:
! 960: if (node->parent->tok != MDOC_Fo) {
! 961: p->flags |= ttypes[TTYPE_FUNC_ARG];
! 962: return(1);
! 963: }
! 964:
! 965: for (n = node->child; n; n = n->next) {
! 966: assert(MDOC_TEXT == n->type);
! 967:
! 968: p->flags |= ttypes[TTYPE_FUNC_ARG];
! 969: word(p, n->data.text.string);
! 970: p->flags &= ~ttypes[TTYPE_FUNC_ARG];
! 971:
! 972: if (n->next)
! 973: word(p, ",");
! 974: }
! 975:
! 976: if (node->next && node->next->tok == MDOC_Fa)
! 977: word(p, ",");
1.2 kristaps 978:
1.16 ! kristaps 979: return(0);
1.10 kristaps 980: }
1.2 kristaps 981:
982:
1.10 kristaps 983: /* ARGSUSED */
984: static void
985: termp_fa_post(DECL_ARGS)
986: {
987:
988: p->flags &= ~ttypes[TTYPE_FUNC_ARG];
989: }
1.2 kristaps 990:
991:
1.10 kristaps 992: /* ARGSUSED */
993: static int
994: termp_va_pre(DECL_ARGS)
995: {
1.2 kristaps 996:
1.10 kristaps 997: p->flags |= ttypes[TTYPE_VAR_DECL];
998: return(1);
1.2 kristaps 999: }
1000:
1001:
1.10 kristaps 1002: /* ARGSUSED */
1.2 kristaps 1003: static void
1.10 kristaps 1004: termp_va_post(DECL_ARGS)
1.1 kristaps 1005: {
1006:
1.10 kristaps 1007: p->flags &= ~ttypes[TTYPE_VAR_DECL];
1008: }
1.1 kristaps 1009:
1.7 kristaps 1010:
1.10 kristaps 1011: /* ARGSUSED */
1012: static int
1013: termp_bd_pre(DECL_ARGS)
1014: {
1015: const struct mdoc_block *bl;
1016: const struct mdoc_node *n;
1.12 kristaps 1017: int i;
1.1 kristaps 1018:
1.10 kristaps 1019: if (MDOC_BLOCK == node->type) {
1020: vspace(p);
1021: return(1);
1022: } else if (MDOC_BODY != node->type)
1023: return(1);
1024:
1025: assert(MDOC_BLOCK == node->parent->type);
1026:
1027: bl = &node->parent->data.block;
1.12 kristaps 1028:
1029: i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
1030: if (-1 != i) {
1031: assert(1 == bl->argv[i].sz);
1032: p->offset += arg_offset(&bl->argv[i]);
1033: }
1034:
1.10 kristaps 1035: if ( ! arg_hasattr(MDOC_Literal, bl->argc, bl->argv))
1036: return(1);
1037:
1038: p->flags |= TERMP_LITERAL;
1039:
1040: for (n = node->child; n; n = n->next) {
1041: assert(MDOC_TEXT == n->type); /* FIXME */
1042: if ((*n->data.text.string)) {
1043: word(p, n->data.text.string);
1044: flushln(p);
1045: } else
1046: vspace(p);
1.1 kristaps 1047:
1.10 kristaps 1048: }
1.1 kristaps 1049:
1.10 kristaps 1050: p->flags &= ~TERMP_LITERAL;
1051: return(0);
1052: }
1.1 kristaps 1053:
1054:
1.10 kristaps 1055: /* ARGSUSED */
1.12 kristaps 1056: static void
1057: termp_bd_post(DECL_ARGS)
1058: {
1059: int i;
1060: const struct mdoc_block *bl;
1061:
1062: if (MDOC_BODY != node->type)
1063: return;
1064:
1065: assert(MDOC_BLOCK == node->parent->type);
1066: bl = &node->parent->data.block;
1067:
1068: i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
1069: if (-1 != i) {
1070: assert(1 == bl->argv[i].sz);
1071: p->offset -= arg_offset(&bl->argv[i]);
1072: }
1073: }
1074:
1075:
1076: /* ARGSUSED */
1.10 kristaps 1077: static int
1078: termp_qq_pre(DECL_ARGS)
1079: {
1.1 kristaps 1080:
1.10 kristaps 1081: if (MDOC_BODY != node->type)
1082: return(1);
1083: word(p, "\"");
1084: p->flags |= TERMP_NOSPACE;
1085: return(1);
1.1 kristaps 1086: }
1087:
1088:
1.10 kristaps 1089: /* ARGSUSED */
1.1 kristaps 1090: static void
1.10 kristaps 1091: termp_qq_post(DECL_ARGS)
1.1 kristaps 1092: {
1093:
1.10 kristaps 1094: if (MDOC_BODY != node->type)
1095: return;
1096: p->flags |= TERMP_NOSPACE;
1097: word(p, "\"");
1098: }
1099:
1100:
1101: /* ARGSUSED */
1102: static int
1.15 kristaps 1103: termp_bsx_pre(DECL_ARGS)
1104: {
1105:
1106: word(p, "BSDI BSD/OS");
1107: return(1);
1108: }
1109:
1110:
1111: /* ARGSUSED */
1112: static int
1.10 kristaps 1113: termp_bx_pre(DECL_ARGS)
1114: {
1.1 kristaps 1115:
1.10 kristaps 1116: word(p, "BSD");
1117: return(1);
1118: }
1119:
1120:
1121: /* ARGSUSED */
1122: static int
1123: termp_ox_pre(DECL_ARGS)
1124: {
1125:
1126: word(p, "OpenBSD");
1127: return(1);
1128: }
1129:
1130:
1131: /* ARGSUSED */
1132: static int
1.16 ! kristaps 1133: termp_ux_pre(DECL_ARGS)
! 1134: {
! 1135:
! 1136: word(p, "UNIX");
! 1137: return(1);
! 1138: }
! 1139:
! 1140:
! 1141: /* ARGSUSED */
! 1142: static int
! 1143: termp_fx_pre(DECL_ARGS)
! 1144: {
! 1145:
! 1146: word(p, "FreeBSD");
! 1147: return(1);
! 1148: }
! 1149:
! 1150:
! 1151: /* ARGSUSED */
! 1152: static int
1.10 kristaps 1153: termp_nx_pre(DECL_ARGS)
1154: {
1155:
1156: word(p, "NetBSD");
1157: return(1);
1158: }
1159:
1160:
1161: /* ARGSUSED */
1162: static int
1163: termp_sq_pre(DECL_ARGS)
1164: {
1165:
1166: if (MDOC_BODY != node->type)
1167: return(1);
1.13 kristaps 1168: word(p, "\'");
1.10 kristaps 1169: p->flags |= TERMP_NOSPACE;
1170: return(1);
1171: }
1.1 kristaps 1172:
1173:
1.10 kristaps 1174: /* ARGSUSED */
1175: static void
1176: termp_sq_post(DECL_ARGS)
1177: {
1178:
1179: if (MDOC_BODY != node->type)
1180: return;
1181: p->flags |= TERMP_NOSPACE;
1182: word(p, "\'");
1183: }
1.2 kristaps 1184:
1185:
1.10 kristaps 1186: /* ARGSUSED */
1187: static int
1188: termp_pf_pre(DECL_ARGS)
1189: {
1.1 kristaps 1190:
1.10 kristaps 1191: p->flags |= TERMP_IGNDELIM;
1192: return(1);
1193: }
1.1 kristaps 1194:
1195:
1.10 kristaps 1196: /* ARGSUSED */
1197: static void
1198: termp_pf_post(DECL_ARGS)
1199: {
1.1 kristaps 1200:
1.10 kristaps 1201: p->flags &= ~TERMP_IGNDELIM;
1202: p->flags |= TERMP_NOSPACE;
1203: }
1.1 kristaps 1204:
1205:
1.10 kristaps 1206: /* ARGSUSED */
1207: static int
1208: termp_ss_pre(DECL_ARGS)
1209: {
1.1 kristaps 1210:
1.10 kristaps 1211: switch (node->type) {
1212: case (MDOC_HEAD):
1213: vspace(p);
1214: p->flags |= ttypes[TTYPE_SSECTION];
1215: p->offset = INDENT / 2;
1216: break;
1217: default:
1218: break;
1219: }
1.1 kristaps 1220:
1.10 kristaps 1221: return(1);
1.1 kristaps 1222: }
1223:
1224:
1.10 kristaps 1225: /* ARGSUSED */
1226: static void
1227: termp_ss_post(DECL_ARGS)
1.1 kristaps 1228: {
1229:
1.10 kristaps 1230: switch (node->type) {
1231: case (MDOC_HEAD):
1232: p->flags &= ~ttypes[TTYPE_SSECTION];
1233: newln(p);
1234: p->offset = INDENT;
1235: break;
1236: default:
1237: break;
1238: }
1239: }
1.2 kristaps 1240:
1241:
1.10 kristaps 1242: /* ARGSUSED */
1243: static int
1244: termp_pa_pre(DECL_ARGS)
1245: {
1.2 kristaps 1246:
1.10 kristaps 1247: p->flags |= ttypes[TTYPE_FILE];
1248: return(1);
1.1 kristaps 1249: }
1250:
1251:
1.10 kristaps 1252: /* ARGSUSED */
1253: static void
1254: termp_pa_post(DECL_ARGS)
1255: {
1256:
1257: p->flags &= ~ttypes[TTYPE_FILE];
1258: }
1.11 kristaps 1259:
1260:
1261: /* ARGSUSED */
1262: static int
1263: termp_em_pre(DECL_ARGS)
1264: {
1265:
1266: p->flags |= ttypes[TTYPE_EMPH];
1267: return(1);
1268: }
1269:
1270:
1271: /* ARGSUSED */
1272: static void
1273: termp_em_post(DECL_ARGS)
1274: {
1275:
1276: p->flags &= ~ttypes[TTYPE_EMPH];
1277: }
1.14 kristaps 1278:
1279:
1280: /* ARGSUSED */
1281: static int
1282: termp_cd_pre(DECL_ARGS)
1283: {
1284:
1285: p->flags |= ttypes[TTYPE_CONFIG];
1286: return(1);
1287: }
1288:
1289:
1290: /* ARGSUSED */
1291: static void
1292: termp_cd_post(DECL_ARGS)
1293: {
1294:
1295: p->flags &= ~ttypes[TTYPE_CONFIG];
1296: }
1297:
1298:
1299: /* ARGSUSED */
1300: static int
1301: termp_cm_pre(DECL_ARGS)
1302: {
1303:
1304: p->flags |= ttypes[TTYPE_CMD_FLAG];
1305: return(1);
1306: }
1307:
1308:
1309: /* ARGSUSED */
1310: static void
1311: termp_cm_post(DECL_ARGS)
1312: {
1313:
1314: p->flags &= ~ttypes[TTYPE_CMD_FLAG];
1315: }
1316:
1317:
1318: /* ARGSUSED */
1319: static int
1320: termp_ic_pre(DECL_ARGS)
1321: {
1322:
1323: p->flags |= ttypes[TTYPE_CMD];
1324: return(1);
1325: }
1326:
1327:
1328: /* ARGSUSED */
1329: static void
1330: termp_ic_post(DECL_ARGS)
1331: {
1332:
1333: p->flags &= ~ttypes[TTYPE_CMD];
1334: }
1335:
1336:
1337: /* ARGSUSED */
1338: static int
1339: termp_in_pre(DECL_ARGS)
1340: {
1341:
1342: p->flags |= ttypes[TTYPE_INCLUDE];
1343: return(1);
1344: }
1345:
1346:
1347: /* ARGSUSED */
1348: static void
1349: termp_in_post(DECL_ARGS)
1350: {
1351:
1352: p->flags &= ~ttypes[TTYPE_INCLUDE];
1353: }
1354:
1355:
1356: /* ARGSUSED */
1357: static int
1358: termp_at_pre(DECL_ARGS)
1359: {
1360: enum mdoc_att c;
1361:
1362: c = ATT_DEFAULT;
1363: if (node->child) {
1364: assert(MDOC_TEXT == node->child->type);
1365: c = mdoc_atoatt(node->child->data.text.string);
1366: }
1367:
1368: word(p, mdoc_att2a(c));
1369: return(0);
1370: }
1.15 kristaps 1371:
1372:
1373: /* ARGSUSED */
1374: static int
1375: termp_bq_pre(DECL_ARGS)
1376: {
1377:
1378: if (MDOC_BODY != node->type)
1379: return(1);
1380: word(p, "[");
1381: p->flags |= TERMP_NOSPACE;
1382: return(1);
1383: }
1384:
1385:
1386: /* ARGSUSED */
1387: static void
1388: termp_bq_post(DECL_ARGS)
1389: {
1390:
1391: if (MDOC_BODY != node->type)
1392: return;
1393: word(p, "]");
1394: }
1395:
1396:
1397: /* ARGSUSED */
1398: static int
1399: termp_pq_pre(DECL_ARGS)
1400: {
1401:
1402: if (MDOC_BODY != node->type)
1403: return(1);
1404: word(p, "(");
1405: p->flags |= TERMP_NOSPACE;
1406: return(1);
1407: }
1408:
1409:
1410: /* ARGSUSED */
1411: static void
1412: termp_pq_post(DECL_ARGS)
1413: {
1414:
1415: if (MDOC_BODY != node->type)
1416: return;
1417: word(p, ")");
1418: }
1419:
1420:
1.16 ! kristaps 1421: /* ARGSUSED */
! 1422: static int
! 1423: termp_fo_pre(DECL_ARGS)
! 1424: {
! 1425: const struct mdoc_node *n;
! 1426:
! 1427: if (MDOC_BODY == node->type) {
! 1428: word(p, "(");
! 1429: p->flags |= TERMP_NOSPACE;
! 1430: return(1);
! 1431: } else if (MDOC_HEAD != node->type)
! 1432: return(1);
! 1433:
! 1434: p->flags |= ttypes[TTYPE_FUNC_NAME];
! 1435: for (n = node->child; n; n = n->next) {
! 1436: assert(MDOC_TEXT == n->type);
! 1437: word(p, n->data.text.string);
! 1438: }
! 1439: p->flags &= ~ttypes[TTYPE_FUNC_NAME];
! 1440:
! 1441: return(0);
! 1442: }
! 1443:
! 1444:
! 1445: /* ARGSUSED */
! 1446: static void
! 1447: termp_fo_post(DECL_ARGS)
! 1448: {
! 1449:
! 1450: if (MDOC_BODY != node->type)
! 1451: return;
! 1452: word(p, ")");
! 1453: word(p, ";");
! 1454: newln(p);
! 1455: }
! 1456:
! 1457:
CVSweb