Annotation of src/usr.bin/mandoc/mdoc_macro.c, Revision 1.46
1.46 ! schwarze 1: /* $Id: mdoc_macro.c,v 1.45 2010/06/06 18:08:41 schwarze Exp $ */
1.1 kristaps 2: /*
1.2 schwarze 3: * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
1.1 kristaps 4: *
5: * Permission to use, copy, modify, and distribute this software for any
1.2 schwarze 6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 8: *
1.2 schwarze 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 kristaps 16: */
17: #include <assert.h>
18: #include <ctype.h>
19: #include <stdlib.h>
20: #include <stdio.h>
21: #include <string.h>
1.26 schwarze 22: #include <time.h>
1.1 kristaps 23:
1.44 schwarze 24: #include "mandoc.h"
1.1 kristaps 25: #include "libmdoc.h"
1.39 schwarze 26: #include "libmandoc.h"
1.1 kristaps 27:
1.35 schwarze 28: enum rew {
29: REWIND_REWIND,
30: REWIND_NOHALT,
31: REWIND_HALT
32: };
1.1 kristaps 33:
1.36 schwarze 34: static int blk_full(MACRO_PROT_ARGS);
35: static int blk_exp_close(MACRO_PROT_ARGS);
36: static int blk_part_exp(MACRO_PROT_ARGS);
37: static int blk_part_imp(MACRO_PROT_ARGS);
38: static int ctx_synopsis(MACRO_PROT_ARGS);
39: static int in_line_eoln(MACRO_PROT_ARGS);
40: static int in_line_argn(MACRO_PROT_ARGS);
41: static int in_line(MACRO_PROT_ARGS);
42: static int obsolete(MACRO_PROT_ARGS);
1.46 ! schwarze 43: static int phrase_ta(MACRO_PROT_ARGS);
1.36 schwarze 44:
45: static int append_delims(struct mdoc *,
46: int, int *, char *);
47: static enum mdoct lookup(enum mdoct, const char *);
48: static enum mdoct lookup_raw(const char *);
1.46 ! schwarze 49: static int phrase(struct mdoc *, int, int, char *);
1.36 schwarze 50: static enum mdoct rew_alt(enum mdoct);
51: static int rew_dobreak(enum mdoct,
52: const struct mdoc_node *);
53: static enum rew rew_dohalt(enum mdoct, enum mdoc_type,
54: const struct mdoc_node *);
55: static int rew_elem(struct mdoc *, enum mdoct);
56: static int rew_last(struct mdoc *,
57: const struct mdoc_node *);
58: static int rew_sub(enum mdoc_type, struct mdoc *,
59: enum mdoct, int, int);
60: static int swarn(struct mdoc *, enum mdoc_type, int,
61: int, const struct mdoc_node *);
1.1 kristaps 62:
63: const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
1.4 schwarze 64: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ap */
1.1 kristaps 65: { in_line_eoln, MDOC_PROLOGUE }, /* Dd */
66: { in_line_eoln, MDOC_PROLOGUE }, /* Dt */
67: { in_line_eoln, MDOC_PROLOGUE }, /* Os */
68: { blk_full, 0 }, /* Sh */
69: { blk_full, 0 }, /* Ss */
1.16 schwarze 70: { in_line_eoln, 0 }, /* Pp */
1.1 kristaps 71: { blk_part_imp, MDOC_PARSED }, /* D1 */
72: { blk_part_imp, MDOC_PARSED }, /* Dl */
73: { blk_full, MDOC_EXPLICIT }, /* Bd */
74: { blk_exp_close, MDOC_EXPLICIT }, /* Ed */
75: { blk_full, MDOC_EXPLICIT }, /* Bl */
76: { blk_exp_close, MDOC_EXPLICIT }, /* El */
77: { blk_full, MDOC_PARSED }, /* It */
78: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */
1.3 schwarze 79: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* An */
1.1 kristaps 80: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */
1.15 schwarze 81: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Cd */
1.1 kristaps 82: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */
83: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Dv */
84: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Er */
85: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ev */
86: { in_line_eoln, 0 }, /* Ex */
87: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fa */
88: { in_line_eoln, 0 }, /* Fd */
89: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fl */
90: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fn */
1.3 schwarze 91: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ft */
1.1 kristaps 92: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ic */
1.11 schwarze 93: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* In */
1.1 kristaps 94: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Li */
1.12 schwarze 95: { blk_full, 0 }, /* Nd */
1.1 kristaps 96: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */
97: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Op */
98: { obsolete, 0 }, /* Ot */
99: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pa */
100: { in_line_eoln, 0 }, /* Rv */
101: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* St */
102: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Va */
1.28 schwarze 103: { ctx_synopsis, MDOC_CALLABLE | MDOC_PARSED }, /* Vt */
104: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Xr */
1.1 kristaps 105: { in_line_eoln, 0 }, /* %A */
106: { in_line_eoln, 0 }, /* %B */
107: { in_line_eoln, 0 }, /* %D */
108: { in_line_eoln, 0 }, /* %I */
109: { in_line_eoln, 0 }, /* %J */
110: { in_line_eoln, 0 }, /* %N */
111: { in_line_eoln, 0 }, /* %O */
112: { in_line_eoln, 0 }, /* %P */
113: { in_line_eoln, 0 }, /* %R */
114: { in_line_eoln, 0 }, /* %T */
115: { in_line_eoln, 0 }, /* %V */
116: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ac */
117: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Ao */
118: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Aq */
119: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* At */
120: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Bc */
121: { blk_full, MDOC_EXPLICIT }, /* Bf */
122: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Bo */
123: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Bq */
124: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bsx */
125: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bx */
126: { in_line_eoln, 0 }, /* Db */
127: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Dc */
128: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Do */
129: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Dq */
130: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ec */
131: { blk_exp_close, MDOC_EXPLICIT }, /* Ef */
132: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Em */
133: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Eo */
134: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Fx */
1.3 schwarze 135: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ms */
1.1 kristaps 136: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* No */
137: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ns */
138: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Nx */
139: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ox */
140: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Pc */
1.35 schwarze 141: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_IGNDELIM }, /* Pf */
1.1 kristaps 142: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Po */
143: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Pq */
144: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Qc */
145: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Ql */
146: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Qo */
147: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Qq */
148: { blk_exp_close, MDOC_EXPLICIT }, /* Re */
149: { blk_full, MDOC_EXPLICIT }, /* Rs */
150: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Sc */
151: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* So */
152: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Sq */
153: { in_line_eoln, 0 }, /* Sm */
154: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Sx */
155: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Sy */
156: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Tn */
157: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ux */
158: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Xc */
159: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Xo */
160: { blk_full, MDOC_EXPLICIT | MDOC_CALLABLE }, /* Fo */
161: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Fc */
162: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Oo */
163: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Oc */
164: { blk_full, MDOC_EXPLICIT }, /* Bk */
165: { blk_exp_close, MDOC_EXPLICIT }, /* Ek */
166: { in_line_eoln, 0 }, /* Bt */
167: { in_line_eoln, 0 }, /* Hf */
168: { obsolete, 0 }, /* Fr */
169: { in_line_eoln, 0 }, /* Ud */
1.45 schwarze 170: { in_line, 0 }, /* Lb */
1.16 schwarze 171: { in_line_eoln, 0 }, /* Lp */
1.3 schwarze 172: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Lk */
173: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Mt */
1.1 kristaps 174: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Brq */
175: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Bro */
176: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Brc */
177: { in_line_eoln, 0 }, /* %C */
178: { obsolete, 0 }, /* Es */
179: { obsolete, 0 }, /* En */
180: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Dx */
181: { in_line_eoln, 0 }, /* %Q */
1.13 schwarze 182: { in_line_eoln, 0 }, /* br */
183: { in_line_eoln, 0 }, /* sp */
1.25 schwarze 184: { in_line_eoln, 0 }, /* %U */
1.46 ! schwarze 185: { phrase_ta, MDOC_CALLABLE | MDOC_PARSED }, /* Ta */
1.1 kristaps 186: };
187:
188: const struct mdoc_macro * const mdoc_macros = __mdoc_macros;
189:
190:
191: static int
192: swarn(struct mdoc *mdoc, enum mdoc_type type,
193: int line, int pos, const struct mdoc_node *p)
194: {
195: const char *n, *t, *tt;
1.46 ! schwarze 196: enum mandocerr ec;
1.1 kristaps 197:
198: n = t = "<root>";
199: tt = "block";
200:
201: switch (type) {
202: case (MDOC_BODY):
203: tt = "multi-line";
204: break;
205: case (MDOC_HEAD):
206: tt = "line";
207: break;
208: default:
209: break;
210: }
211:
212: switch (p->type) {
213: case (MDOC_BLOCK):
214: n = mdoc_macronames[p->tok];
215: t = "block";
216: break;
217: case (MDOC_BODY):
218: n = mdoc_macronames[p->tok];
219: t = "multi-line";
220: break;
221: case (MDOC_HEAD):
222: n = mdoc_macronames[p->tok];
223: t = "line";
224: break;
225: default:
226: break;
227: }
228:
1.46 ! schwarze 229: ec = (MDOC_IGN_SCOPE & mdoc->pflags) ?
! 230: MANDOCERR_SCOPE : MANDOCERR_SYNTSCOPE;
1.44 schwarze 231:
1.46 ! schwarze 232: return(mdoc_vmsg(mdoc, ec, line, pos,
! 233: "%s scope breaks %s of %s",
! 234: tt, t, n));
1.1 kristaps 235: }
236:
237:
238: /*
239: * This is called at the end of parsing. It must traverse up the tree,
240: * closing out open [implicit] scopes. Obviously, open explicit scopes
241: * are errors.
242: */
243: int
1.22 schwarze 244: mdoc_macroend(struct mdoc *m)
1.1 kristaps 245: {
246: struct mdoc_node *n;
247:
248: /* Scan for open explicit scopes. */
249:
1.22 schwarze 250: n = MDOC_VALID & m->last->flags ? m->last->parent : m->last;
1.1 kristaps 251:
252: for ( ; n; n = n->parent) {
253: if (MDOC_BLOCK != n->type)
254: continue;
255: if ( ! (MDOC_EXPLICIT & mdoc_macros[n->tok].flags))
256: continue;
1.44 schwarze 257: mdoc_nmsg(m, n, MANDOCERR_SYNTSCOPE);
258: return(0);
1.1 kristaps 259: }
260:
1.22 schwarze 261: /* Rewind to the first. */
262:
263: return(rew_last(m, m->first));
1.1 kristaps 264: }
265:
1.22 schwarze 266:
267: /*
268: * Look up a macro from within a subsequent context.
269: */
1.36 schwarze 270: static enum mdoct
271: lookup(enum mdoct from, const char *p)
1.21 schwarze 272: {
1.24 schwarze 273: /* FIXME: make -diag lists be un-PARSED. */
1.21 schwarze 274:
275: if ( ! (MDOC_PARSED & mdoc_macros[from].flags))
276: return(MDOC_MAX);
1.24 schwarze 277: return(lookup_raw(p));
1.21 schwarze 278: }
279:
280:
1.22 schwarze 281: /*
282: * Lookup a macro following the initial line macro.
283: */
1.36 schwarze 284: static enum mdoct
1.24 schwarze 285: lookup_raw(const char *p)
1.1 kristaps 286: {
1.36 schwarze 287: enum mdoct res;
1.1 kristaps 288:
1.24 schwarze 289: if (MDOC_MAX == (res = mdoc_hash_find(p)))
1.21 schwarze 290: return(MDOC_MAX);
291: if (MDOC_CALLABLE & mdoc_macros[res].flags)
1.1 kristaps 292: return(res);
293: return(MDOC_MAX);
294: }
295:
296:
297: static int
1.22 schwarze 298: rew_last(struct mdoc *mdoc, const struct mdoc_node *to)
1.1 kristaps 299: {
300:
301: assert(to);
302: mdoc->next = MDOC_NEXT_SIBLING;
303:
304: /* LINTED */
305: while (mdoc->last != to) {
306: if ( ! mdoc_valid_post(mdoc))
307: return(0);
308: if ( ! mdoc_action_post(mdoc))
309: return(0);
310: mdoc->last = mdoc->last->parent;
311: assert(mdoc->last);
312: }
313:
314: if ( ! mdoc_valid_post(mdoc))
315: return(0);
316: return(mdoc_action_post(mdoc));
317: }
318:
319:
1.22 schwarze 320: /*
321: * Return the opening macro of a closing one, e.g., `Ec' has `Eo' as its
322: * matching pair.
323: */
1.34 schwarze 324: static enum mdoct
325: rew_alt(enum mdoct tok)
1.1 kristaps 326: {
327: switch (tok) {
328: case (MDOC_Ac):
329: return(MDOC_Ao);
330: case (MDOC_Bc):
331: return(MDOC_Bo);
332: case (MDOC_Brc):
333: return(MDOC_Bro);
334: case (MDOC_Dc):
335: return(MDOC_Do);
336: case (MDOC_Ec):
337: return(MDOC_Eo);
338: case (MDOC_Ed):
339: return(MDOC_Bd);
340: case (MDOC_Ef):
341: return(MDOC_Bf);
342: case (MDOC_Ek):
343: return(MDOC_Bk);
344: case (MDOC_El):
345: return(MDOC_Bl);
346: case (MDOC_Fc):
347: return(MDOC_Fo);
348: case (MDOC_Oc):
349: return(MDOC_Oo);
350: case (MDOC_Pc):
351: return(MDOC_Po);
352: case (MDOC_Qc):
353: return(MDOC_Qo);
354: case (MDOC_Re):
355: return(MDOC_Rs);
356: case (MDOC_Sc):
357: return(MDOC_So);
358: case (MDOC_Xc):
359: return(MDOC_Xo);
360: default:
361: break;
362: }
363: abort();
364: /* NOTREACHED */
365: }
366:
367:
368: /*
369: * Rewind rules. This indicates whether to stop rewinding
370: * (REWIND_HALT) without touching our current scope, stop rewinding and
371: * close our current scope (REWIND_REWIND), or continue (REWIND_NOHALT).
372: * The scope-closing and so on occurs in the various rew_* routines.
373: */
1.35 schwarze 374: static enum rew
1.34 schwarze 375: rew_dohalt(enum mdoct tok, enum mdoc_type type,
376: const struct mdoc_node *p)
1.1 kristaps 377: {
378:
379: if (MDOC_ROOT == p->type)
380: return(REWIND_HALT);
381: if (MDOC_VALID & p->flags)
382: return(REWIND_NOHALT);
383:
384: switch (tok) {
385: case (MDOC_Aq):
386: /* FALLTHROUGH */
387: case (MDOC_Bq):
388: /* FALLTHROUGH */
389: case (MDOC_Brq):
390: /* FALLTHROUGH */
391: case (MDOC_D1):
392: /* FALLTHROUGH */
393: case (MDOC_Dl):
394: /* FALLTHROUGH */
395: case (MDOC_Dq):
396: /* FALLTHROUGH */
397: case (MDOC_Op):
398: /* FALLTHROUGH */
399: case (MDOC_Pq):
400: /* FALLTHROUGH */
401: case (MDOC_Ql):
402: /* FALLTHROUGH */
403: case (MDOC_Qq):
404: /* FALLTHROUGH */
405: case (MDOC_Sq):
1.28 schwarze 406: /* FALLTHROUGH */
407: case (MDOC_Vt):
1.1 kristaps 408: assert(MDOC_TAIL != type);
409: if (type == p->type && tok == p->tok)
410: return(REWIND_REWIND);
411: break;
412: case (MDOC_It):
413: assert(MDOC_TAIL != type);
414: if (type == p->type && tok == p->tok)
415: return(REWIND_REWIND);
416: if (MDOC_BODY == p->type && MDOC_Bl == p->tok)
417: return(REWIND_HALT);
418: break;
419: case (MDOC_Sh):
420: if (type == p->type && tok == p->tok)
421: return(REWIND_REWIND);
422: break;
1.12 schwarze 423: case (MDOC_Nd):
424: /* FALLTHROUGH */
1.1 kristaps 425: case (MDOC_Ss):
426: assert(MDOC_TAIL != type);
427: if (type == p->type && tok == p->tok)
428: return(REWIND_REWIND);
429: if (MDOC_BODY == p->type && MDOC_Sh == p->tok)
430: return(REWIND_HALT);
431: break;
432: case (MDOC_Ao):
433: /* FALLTHROUGH */
434: case (MDOC_Bd):
435: /* FALLTHROUGH */
436: case (MDOC_Bf):
437: /* FALLTHROUGH */
438: case (MDOC_Bk):
439: /* FALLTHROUGH */
440: case (MDOC_Bl):
441: /* FALLTHROUGH */
442: case (MDOC_Bo):
443: /* FALLTHROUGH */
444: case (MDOC_Bro):
445: /* FALLTHROUGH */
446: case (MDOC_Do):
447: /* FALLTHROUGH */
448: case (MDOC_Eo):
449: /* FALLTHROUGH */
450: case (MDOC_Fo):
451: /* FALLTHROUGH */
452: case (MDOC_Oo):
453: /* FALLTHROUGH */
454: case (MDOC_Po):
455: /* FALLTHROUGH */
456: case (MDOC_Qo):
457: /* FALLTHROUGH */
458: case (MDOC_Rs):
459: /* FALLTHROUGH */
460: case (MDOC_So):
461: /* FALLTHROUGH */
462: case (MDOC_Xo):
463: if (type == p->type && tok == p->tok)
464: return(REWIND_REWIND);
465: break;
466: /* Multi-line explicit scope close. */
467: case (MDOC_Ac):
468: /* FALLTHROUGH */
469: case (MDOC_Bc):
470: /* FALLTHROUGH */
471: case (MDOC_Brc):
472: /* FALLTHROUGH */
473: case (MDOC_Dc):
474: /* FALLTHROUGH */
475: case (MDOC_Ec):
476: /* FALLTHROUGH */
477: case (MDOC_Ed):
478: /* FALLTHROUGH */
479: case (MDOC_Ek):
480: /* FALLTHROUGH */
481: case (MDOC_El):
482: /* FALLTHROUGH */
483: case (MDOC_Fc):
484: /* FALLTHROUGH */
485: case (MDOC_Ef):
486: /* FALLTHROUGH */
487: case (MDOC_Oc):
488: /* FALLTHROUGH */
489: case (MDOC_Pc):
490: /* FALLTHROUGH */
491: case (MDOC_Qc):
492: /* FALLTHROUGH */
493: case (MDOC_Re):
494: /* FALLTHROUGH */
495: case (MDOC_Sc):
496: /* FALLTHROUGH */
497: case (MDOC_Xc):
498: if (type == p->type && rew_alt(tok) == p->tok)
499: return(REWIND_REWIND);
500: break;
501: default:
502: abort();
503: /* NOTREACHED */
504: }
505:
506: return(REWIND_NOHALT);
507: }
508:
509:
510: /*
511: * See if we can break an encountered scope (the rew_dohalt has returned
512: * REWIND_NOHALT).
513: */
514: static int
1.34 schwarze 515: rew_dobreak(enum mdoct tok, const struct mdoc_node *p)
1.1 kristaps 516: {
517:
518: assert(MDOC_ROOT != p->type);
519: if (MDOC_ELEM == p->type)
520: return(1);
521: if (MDOC_TEXT == p->type)
522: return(1);
523: if (MDOC_VALID & p->flags)
524: return(1);
525:
526: switch (tok) {
527: case (MDOC_It):
528: return(MDOC_It == p->tok);
1.12 schwarze 529: case (MDOC_Nd):
530: return(MDOC_Nd == p->tok);
1.1 kristaps 531: case (MDOC_Ss):
532: return(MDOC_Ss == p->tok);
533: case (MDOC_Sh):
1.12 schwarze 534: if (MDOC_Nd == p->tok)
535: return(1);
1.1 kristaps 536: if (MDOC_Ss == p->tok)
537: return(1);
538: return(MDOC_Sh == p->tok);
539: case (MDOC_El):
540: if (MDOC_It == p->tok)
541: return(1);
542: break;
543: case (MDOC_Oc):
544: if (MDOC_Op == p->tok)
545: return(1);
546: break;
547: default:
548: break;
549: }
550:
551: if (MDOC_EXPLICIT & mdoc_macros[tok].flags)
552: return(p->tok == rew_alt(tok));
553: else if (MDOC_BLOCK == p->type)
554: return(1);
555:
556: return(tok == p->tok);
557: }
558:
559:
560: static int
1.34 schwarze 561: rew_elem(struct mdoc *mdoc, enum mdoct tok)
1.1 kristaps 562: {
563: struct mdoc_node *n;
564:
565: n = mdoc->last;
566: if (MDOC_ELEM != n->type)
567: n = n->parent;
568: assert(MDOC_ELEM == n->type);
569: assert(tok == n->tok);
570:
571: return(rew_last(mdoc, n));
572: }
573:
574:
575: static int
1.22 schwarze 576: rew_sub(enum mdoc_type t, struct mdoc *m,
1.34 schwarze 577: enum mdoct tok, int line, int ppos)
1.1 kristaps 578: {
579: struct mdoc_node *n;
1.35 schwarze 580: enum rew c;
1.1 kristaps 581:
582: /* LINTED */
1.22 schwarze 583: for (n = m->last; n; n = n->parent) {
584: c = rew_dohalt(tok, t, n);
585: if (REWIND_HALT == c) {
586: if (MDOC_BLOCK != t)
587: return(1);
588: if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags))
589: return(1);
1.44 schwarze 590: /* FIXME: shouldn't raise an error */
591: mdoc_pmsg(m, line, ppos, MANDOCERR_SYNTNOSCOPE);
592: return(0);
1.22 schwarze 593: }
1.1 kristaps 594: if (REWIND_REWIND == c)
595: break;
596: else if (rew_dobreak(tok, n))
597: continue;
1.22 schwarze 598: if ( ! swarn(m, t, line, ppos, n))
1.1 kristaps 599: return(0);
600: }
601:
602: assert(n);
1.29 schwarze 603: if ( ! rew_last(m, n))
604: return(0);
605:
606: /*
1.34 schwarze 607: * The current block extends an enclosing block beyond a line
608: * break. Now that the current block ends, close the enclosing
609: * block, too.
1.29 schwarze 610: */
1.34 schwarze 611: if (NULL != (n = n->pending)) {
1.29 schwarze 612: assert(MDOC_HEAD == n->type);
613: if ( ! rew_last(m, n))
614: return(0);
615: if ( ! mdoc_body_alloc(m, n->line, n->pos, n->tok))
616: return(0);
617: }
618: return(1);
1.1 kristaps 619: }
620:
621:
622: static int
1.42 schwarze 623: append_delims(struct mdoc *m, int line, int *pos, char *buf)
1.1 kristaps 624: {
1.42 schwarze 625: int la;
1.36 schwarze 626: enum margserr ac;
1.1 kristaps 627: char *p;
628:
1.42 schwarze 629: if ('\0' == buf[*pos])
1.1 kristaps 630: return(1);
631:
632: for (;;) {
1.42 schwarze 633: la = *pos;
634: ac = mdoc_zargs(m, line, pos, buf, ARGS_NOWARN, &p);
1.1 kristaps 635:
1.36 schwarze 636: if (ARGS_ERROR == ac)
1.1 kristaps 637: return(0);
1.36 schwarze 638: else if (ARGS_EOLN == ac)
1.1 kristaps 639: break;
1.42 schwarze 640:
1.40 schwarze 641: assert(DELIM_NONE != mdoc_isdelim(p));
1.42 schwarze 642: if ( ! mdoc_word_alloc(m, line, la, p))
1.1 kristaps 643: return(0);
1.43 schwarze 644:
1.42 schwarze 645: /*
646: * If we encounter end-of-sentence symbols, then trigger
647: * the double-space.
648: *
649: * XXX: it's easy to allow this to propogate outward to
650: * the last symbol, such that `. )' will cause the
651: * correct double-spacing. However, (1) groff isn't
652: * smart enough to do this and (2) it would require
653: * knowing which symbols break this behaviour, for
654: * example, `. ;' shouldn't propogate the double-space.
655: */
656: if (mandoc_eos(p, strlen(p)))
657: m->last->flags |= MDOC_EOS;
1.1 kristaps 658: }
659:
660: return(1);
661: }
662:
663:
664: /*
665: * Close out block partial/full explicit.
666: */
667: static int
668: blk_exp_close(MACRO_PROT_ARGS)
669: {
1.37 schwarze 670: int j, lastarg, maxargs, flushed, nl;
1.36 schwarze 671: enum margserr ac;
672: enum mdoct ntok;
1.1 kristaps 673: char *p;
674:
1.37 schwarze 675: nl = MDOC_NEWLINE & m->flags;
676:
1.1 kristaps 677: switch (tok) {
678: case (MDOC_Ec):
679: maxargs = 1;
680: break;
681: default:
682: maxargs = 0;
683: break;
684: }
685:
686: if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) {
1.44 schwarze 687: /* FIXME: do this in validate */
1.14 schwarze 688: if (buf[*pos])
1.44 schwarze 689: if ( ! mdoc_pmsg(m, line, ppos, MANDOCERR_ARGSLOST))
1.1 kristaps 690: return(0);
1.14 schwarze 691:
1.22 schwarze 692: if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos))
1.14 schwarze 693: return(0);
1.22 schwarze 694: return(rew_sub(MDOC_BLOCK, m, tok, line, ppos));
1.1 kristaps 695: }
696:
1.22 schwarze 697: if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos))
1.1 kristaps 698: return(0);
699:
1.22 schwarze 700: if (maxargs > 0)
701: if ( ! mdoc_tail_alloc(m, line, ppos, rew_alt(tok)))
1.1 kristaps 702: return(0);
703:
1.20 schwarze 704: for (flushed = j = 0; ; j++) {
1.1 kristaps 705: lastarg = *pos;
706:
707: if (j == maxargs && ! flushed) {
1.22 schwarze 708: if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
1.1 kristaps 709: return(0);
710: flushed = 1;
711: }
712:
1.36 schwarze 713: ac = mdoc_args(m, line, pos, buf, tok, &p);
1.1 kristaps 714:
1.36 schwarze 715: if (ARGS_ERROR == ac)
1.1 kristaps 716: return(0);
1.36 schwarze 717: if (ARGS_PUNCT == ac)
1.1 kristaps 718: break;
1.36 schwarze 719: if (ARGS_EOLN == ac)
1.1 kristaps 720: break;
721:
1.36 schwarze 722: ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
723:
724: if (MDOC_MAX == ntok) {
725: if ( ! mdoc_word_alloc(m, line, lastarg, p))
1.1 kristaps 726: return(0);
1.36 schwarze 727: continue;
728: }
1.1 kristaps 729:
1.36 schwarze 730: if ( ! flushed) {
731: if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
732: return(0);
733: flushed = 1;
734: }
735: if ( ! mdoc_macro(m, ntok, line, lastarg, pos, buf))
1.1 kristaps 736: return(0);
1.36 schwarze 737: break;
1.1 kristaps 738: }
739:
1.22 schwarze 740: if ( ! flushed && ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
1.1 kristaps 741: return(0);
742:
1.37 schwarze 743: if ( ! nl)
1.1 kristaps 744: return(1);
1.22 schwarze 745: return(append_delims(m, line, pos, buf));
1.1 kristaps 746: }
747:
748:
749: static int
750: in_line(MACRO_PROT_ARGS)
751: {
1.45 schwarze 752: int la, scope, cnt, nc, nl;
1.36 schwarze 753: enum margverr av;
754: enum mdoct ntok;
755: enum margserr ac;
1.40 schwarze 756: enum mdelim d;
1.36 schwarze 757: struct mdoc_arg *arg;
758: char *p;
1.1 kristaps 759:
1.37 schwarze 760: nl = MDOC_NEWLINE & m->flags;
761:
1.1 kristaps 762: /*
763: * Whether we allow ignored elements (those without content,
764: * usually because of reserved words) to squeak by.
765: */
1.32 schwarze 766:
1.1 kristaps 767: switch (tok) {
1.19 schwarze 768: case (MDOC_An):
769: /* FALLTHROUGH */
770: case (MDOC_Ar):
1.1 kristaps 771: /* FALLTHROUGH */
772: case (MDOC_Fl):
773: /* FALLTHROUGH */
1.3 schwarze 774: case (MDOC_Lk):
1.18 schwarze 775: /* FALLTHROUGH */
1.19 schwarze 776: case (MDOC_Nm):
777: /* FALLTHROUGH */
1.18 schwarze 778: case (MDOC_Pa):
1.1 kristaps 779: nc = 1;
780: break;
781: default:
782: nc = 0;
783: break;
784: }
785:
1.20 schwarze 786: for (arg = NULL;; ) {
1.1 kristaps 787: la = *pos;
1.36 schwarze 788: av = mdoc_argv(m, line, tok, &arg, pos, buf);
1.1 kristaps 789:
1.36 schwarze 790: if (ARGV_WORD == av) {
1.1 kristaps 791: *pos = la;
792: break;
793: }
1.36 schwarze 794: if (ARGV_EOLN == av)
1.1 kristaps 795: break;
1.36 schwarze 796: if (ARGV_ARG == av)
1.1 kristaps 797: continue;
798:
799: mdoc_argv_free(arg);
800: return(0);
801: }
802:
1.45 schwarze 803: for (cnt = scope = 0;; ) {
1.1 kristaps 804: la = *pos;
1.36 schwarze 805: ac = mdoc_args(m, line, pos, buf, tok, &p);
1.1 kristaps 806:
1.36 schwarze 807: if (ARGS_ERROR == ac)
1.1 kristaps 808: return(0);
1.36 schwarze 809: if (ARGS_EOLN == ac)
1.1 kristaps 810: break;
1.36 schwarze 811: if (ARGS_PUNCT == ac)
1.1 kristaps 812: break;
813:
1.36 schwarze 814: ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
1.1 kristaps 815:
816: /*
817: * In this case, we've located a submacro and must
818: * execute it. Close out scope, if open. If no
819: * elements have been generated, either create one (nc)
820: * or raise a warning.
821: */
822:
1.36 schwarze 823: if (MDOC_MAX != ntok) {
1.45 schwarze 824: if (scope && ! rew_elem(m, tok))
1.1 kristaps 825: return(0);
826: if (nc && 0 == cnt) {
1.22 schwarze 827: if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg))
1.1 kristaps 828: return(0);
1.22 schwarze 829: if ( ! rew_last(m, m->last))
1.3 schwarze 830: return(0);
1.1 kristaps 831: } else if ( ! nc && 0 == cnt) {
832: mdoc_argv_free(arg);
1.44 schwarze 833: if ( ! mdoc_pmsg(m, line, ppos, MANDOCERR_MACROEMPTY))
1.1 kristaps 834: return(0);
835: }
1.36 schwarze 836: if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
1.1 kristaps 837: return(0);
1.37 schwarze 838: if ( ! nl)
1.1 kristaps 839: return(1);
1.22 schwarze 840: return(append_delims(m, line, pos, buf));
1.21 schwarze 841: }
1.1 kristaps 842:
843: /*
844: * Non-quote-enclosed punctuation. Set up our scope, if
845: * a word; rewind the scope, if a delimiter; then append
846: * the word.
847: */
848:
1.40 schwarze 849: d = ARGS_QWORD == ac ? DELIM_NONE : mdoc_isdelim(p);
1.1 kristaps 850:
1.45 schwarze 851: if (DELIM_NONE != d) {
852: /*
853: * If we encounter closing punctuation, no word
854: * has been omitted, no scope is open, and we're
855: * allowed to have an empty element, then start
856: * a new scope. `Ar', `Fl', and `Li', only do
857: * this once per invocation. There may be more
858: * of these (all of them?).
859: */
860: if (0 == cnt && (nc || MDOC_Li == tok) &&
861: DELIM_CLOSE == d && ! scope) {
862: if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg))
863: return(0);
864: if (MDOC_Ar == tok || MDOC_Li == tok ||
865: MDOC_Fl == tok)
866: cnt++;
867: scope = 1;
868: }
869: /*
870: * Close out our scope, if one is open, before
871: * any punctuation.
872: */
873: if (scope && ! rew_elem(m, tok))
1.1 kristaps 874: return(0);
1.45 schwarze 875: scope = 0;
876: } else if ( ! scope) {
1.22 schwarze 877: if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg))
1.1 kristaps 878: return(0);
1.45 schwarze 879: scope = 1;
1.1 kristaps 880: }
881:
1.40 schwarze 882: if (DELIM_NONE == d)
1.1 kristaps 883: cnt++;
1.22 schwarze 884: if ( ! mdoc_word_alloc(m, line, la, p))
1.1 kristaps 885: return(0);
1.27 schwarze 886:
887: /*
888: * `Fl' macros have their scope re-opened with each new
889: * word so that the `-' can be added to each one without
890: * having to parse out spaces.
891: */
1.45 schwarze 892: if (scope && MDOC_Fl == tok) {
1.27 schwarze 893: if ( ! rew_elem(m, tok))
894: return(0);
1.45 schwarze 895: scope = 0;
1.27 schwarze 896: }
1.1 kristaps 897: }
898:
1.45 schwarze 899: if (scope && ! rew_elem(m, tok))
1.1 kristaps 900: return(0);
901:
902: /*
903: * If no elements have been collected and we're allowed to have
904: * empties (nc), open a scope and close it out. Otherwise,
905: * raise a warning.
906: */
1.32 schwarze 907:
1.1 kristaps 908: if (nc && 0 == cnt) {
1.22 schwarze 909: if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg))
1.1 kristaps 910: return(0);
1.22 schwarze 911: if ( ! rew_last(m, m->last))
1.3 schwarze 912: return(0);
913: } else if ( ! nc && 0 == cnt) {
1.1 kristaps 914: mdoc_argv_free(arg);
1.44 schwarze 915: if ( ! mdoc_pmsg(m, line, ppos, MANDOCERR_MACROEMPTY))
1.1 kristaps 916: return(0);
917: }
918:
1.37 schwarze 919: if ( ! nl)
1.1 kristaps 920: return(1);
1.22 schwarze 921: return(append_delims(m, line, pos, buf));
1.1 kristaps 922: }
923:
924:
925: static int
926: blk_full(MACRO_PROT_ARGS)
927: {
1.43 schwarze 928: int la, nl;
1.1 kristaps 929: struct mdoc_arg *arg;
1.32 schwarze 930: struct mdoc_node *head; /* save of head macro */
1.34 schwarze 931: struct mdoc_node *body; /* save of body macro */
1.32 schwarze 932: struct mdoc_node *n;
1.46 ! schwarze 933: enum mdoc_type mtt;
1.36 schwarze 934: enum mdoct ntok;
1.37 schwarze 935: enum margserr ac, lac;
1.36 schwarze 936: enum margverr av;
1.1 kristaps 937: char *p;
938:
1.43 schwarze 939: nl = MDOC_NEWLINE & m->flags;
940:
1.32 schwarze 941: /* Close out prior implicit scope. */
1.12 schwarze 942:
1.1 kristaps 943: if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) {
1.22 schwarze 944: if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos))
1.1 kristaps 945: return(0);
1.22 schwarze 946: if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
1.1 kristaps 947: return(0);
948: }
949:
1.32 schwarze 950: /*
951: * This routine accomodates implicitly- and explicitly-scoped
952: * macro openings. Implicit ones first close out prior scope
953: * (seen above). Delay opening the head until necessary to
954: * allow leading punctuation to print. Special consideration
955: * for `It -column', which has phrase-part syntax instead of
956: * regular child nodes.
957: */
958:
1.1 kristaps 959: for (arg = NULL;; ) {
1.32 schwarze 960: la = *pos;
1.36 schwarze 961: av = mdoc_argv(m, line, tok, &arg, pos, buf);
1.1 kristaps 962:
1.36 schwarze 963: if (ARGV_WORD == av) {
1.32 schwarze 964: *pos = la;
1.1 kristaps 965: break;
966: }
967:
1.36 schwarze 968: if (ARGV_EOLN == av)
1.1 kristaps 969: break;
1.36 schwarze 970: if (ARGV_ARG == av)
1.1 kristaps 971: continue;
972:
973: mdoc_argv_free(arg);
974: return(0);
975: }
976:
1.22 schwarze 977: if ( ! mdoc_block_alloc(m, line, ppos, tok, arg))
1.1 kristaps 978: return(0);
979:
1.34 schwarze 980: head = body = NULL;
1.1 kristaps 981:
1.32 schwarze 982: /*
983: * The `Nd' macro has all arguments in its body: it's a hybrid
984: * of block partial-explicit and full-implicit. Stupid.
985: */
1.12 schwarze 986:
1.32 schwarze 987: if (MDOC_Nd == tok) {
988: if ( ! mdoc_head_alloc(m, line, ppos, tok))
989: return(0);
990: head = m->last;
1.22 schwarze 991: if ( ! rew_sub(MDOC_HEAD, m, tok, line, ppos))
1.12 schwarze 992: return(0);
1.22 schwarze 993: if ( ! mdoc_body_alloc(m, line, ppos, tok))
1.12 schwarze 994: return(0);
1.34 schwarze 995: body = m->last;
1.12 schwarze 996: }
997:
1.37 schwarze 998: ac = ARGS_ERROR;
999:
1.41 schwarze 1000: for ( ; ; ) {
1.32 schwarze 1001: la = *pos;
1.46 ! schwarze 1002: /* Initialise last-phrase-type with ARGS_PEND. */
! 1003: lac = ARGS_ERROR == ac ? ARGS_PEND : ac;
1.36 schwarze 1004: ac = mdoc_args(m, line, pos, buf, tok, &p);
1.1 kristaps 1005:
1.36 schwarze 1006: if (ARGS_ERROR == ac)
1.1 kristaps 1007: return(0);
1.46 ! schwarze 1008:
! 1009: if (ARGS_EOLN == ac) {
! 1010: if (ARGS_PPHRASE != lac && ARGS_PHRASE != lac)
! 1011: break;
! 1012: /*
! 1013: * This is necessary: if the last token on a
! 1014: * line is a `Ta' or tab, then we'll get
! 1015: * ARGS_EOLN, so we must be smart enough to
! 1016: * reopen our scope if the last parse was a
! 1017: * phrase or partial phrase.
! 1018: */
! 1019: if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos))
! 1020: return(0);
! 1021: if ( ! mdoc_body_alloc(m, line, ppos, tok))
! 1022: return(0);
! 1023: body = m->last;
1.1 kristaps 1024: break;
1.41 schwarze 1025: }
1026:
1.46 ! schwarze 1027: /*
! 1028: * Emit leading punctuation (i.e., punctuation before
! 1029: * the MDOC_HEAD) for non-phrase types.
! 1030: */
1.32 schwarze 1031:
1.37 schwarze 1032: if (NULL == head &&
1.46 ! schwarze 1033: ARGS_PEND != ac &&
1.37 schwarze 1034: ARGS_PHRASE != ac &&
1.36 schwarze 1035: ARGS_PPHRASE != ac &&
1036: ARGS_QWORD != ac &&
1.40 schwarze 1037: DELIM_OPEN == mdoc_isdelim(p)) {
1.32 schwarze 1038: if ( ! mdoc_word_alloc(m, line, la, p))
1039: return(0);
1040: continue;
1041: }
1042:
1.46 ! schwarze 1043: /* Open a head if one hasn't been opened. */
1.32 schwarze 1044:
1.46 ! schwarze 1045: if (NULL == head) {
1.32 schwarze 1046: if ( ! mdoc_head_alloc(m, line, ppos, tok))
1.1 kristaps 1047: return(0);
1.29 schwarze 1048: head = m->last;
1.32 schwarze 1049: }
1050:
1.46 ! schwarze 1051: if (ARGS_PHRASE == ac ||
! 1052: ARGS_PEND == ac ||
! 1053: ARGS_PPHRASE == ac) {
! 1054: /*
! 1055: * If we haven't opened a body yet, rewind the
! 1056: * head; if we have, rewind that instead.
! 1057: */
! 1058:
! 1059: mtt = body ? MDOC_BODY : MDOC_HEAD;
! 1060: if ( ! rew_sub(mtt, m, tok, line, ppos))
! 1061: return(0);
! 1062:
! 1063: /* Then allocate our body context. */
! 1064:
! 1065: if ( ! mdoc_body_alloc(m, line, ppos, tok))
! 1066: return(0);
! 1067: body = m->last;
! 1068:
! 1069: /*
! 1070: * Process phrases: set whether we're in a
! 1071: * partial-phrase (this effects line handling)
! 1072: * then call down into the phrase parser.
! 1073: */
! 1074:
1.41 schwarze 1075: if (ARGS_PPHRASE == ac)
1076: m->flags |= MDOC_PPHRASE;
1.46 ! schwarze 1077: if (ARGS_PEND == ac && ARGS_PPHRASE == lac)
! 1078: m->flags |= MDOC_PPHRASE;
! 1079:
! 1080: if ( ! phrase(m, line, la, buf))
1.1 kristaps 1081: return(0);
1.46 ! schwarze 1082:
1.41 schwarze 1083: m->flags &= ~MDOC_PPHRASE;
1.1 kristaps 1084: continue;
1085: }
1086:
1.36 schwarze 1087: ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
1088:
1089: if (MDOC_MAX == ntok) {
1090: if ( ! mdoc_word_alloc(m, line, la, p))
1.1 kristaps 1091: return(0);
1.36 schwarze 1092: continue;
1.32 schwarze 1093: }
1.36 schwarze 1094:
1095: if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
1.32 schwarze 1096: return(0);
1.36 schwarze 1097: break;
1.32 schwarze 1098: }
1.1 kristaps 1099:
1.32 schwarze 1100: if (NULL == head) {
1101: if ( ! mdoc_head_alloc(m, line, ppos, tok))
1.1 kristaps 1102: return(0);
1.32 schwarze 1103: head = m->last;
1.1 kristaps 1104: }
1105:
1.43 schwarze 1106: if (nl && ! append_delims(m, line, pos, buf))
1.1 kristaps 1107: return(0);
1.12 schwarze 1108:
1.34 schwarze 1109: /* If we've already opened our body, exit now. */
1.32 schwarze 1110:
1.34 schwarze 1111: if (NULL != body)
1.46 ! schwarze 1112: goto out;
1.29 schwarze 1113:
1114: /*
1.34 schwarze 1115: * If there is an open (i.e., unvalidated) sub-block requiring
1116: * explicit close-out, postpone switching the current block from
1117: * head to body until the rew_sub() call closing out that
1118: * sub-block.
1.29 schwarze 1119: */
1120: for (n = m->last; n && n != head; n = n->parent) {
1.34 schwarze 1121: if (MDOC_BLOCK == n->type &&
1122: MDOC_EXPLICIT & mdoc_macros[n->tok].flags &&
1123: ! (MDOC_VALID & n->flags)) {
1124: assert( ! (MDOC_ACTED & n->flags));
1.29 schwarze 1125: n->pending = head;
1126: return(1);
1127: }
1128: }
1.32 schwarze 1129: /* Close out scopes to remain in a consistent state. */
1.12 schwarze 1130:
1.22 schwarze 1131: if ( ! rew_sub(MDOC_HEAD, m, tok, line, ppos))
1.1 kristaps 1132: return(0);
1.22 schwarze 1133: if ( ! mdoc_body_alloc(m, line, ppos, tok))
1.1 kristaps 1134: return(0);
1135:
1.46 ! schwarze 1136: out:
! 1137: if ( ! (MDOC_FREECOL & m->flags))
! 1138: return(1);
! 1139:
! 1140: if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos))
! 1141: return(0);
! 1142: if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
! 1143: return(0);
! 1144:
! 1145: m->flags &= ~MDOC_FREECOL;
1.1 kristaps 1146: return(1);
1147: }
1148:
1149:
1150: static int
1151: blk_part_imp(MACRO_PROT_ARGS)
1152: {
1.43 schwarze 1153: int la, nl;
1.36 schwarze 1154: enum mdoct ntok;
1155: enum margserr ac;
1.1 kristaps 1156: char *p;
1.32 schwarze 1157: struct mdoc_node *blk; /* saved block context */
1158: struct mdoc_node *body; /* saved body context */
1159: struct mdoc_node *n;
1.1 kristaps 1160:
1.43 schwarze 1161: nl = MDOC_NEWLINE & m->flags;
1162:
1.32 schwarze 1163: /*
1164: * A macro that spans to the end of the line. This is generally
1165: * (but not necessarily) called as the first macro. The block
1166: * has a head as the immediate child, which is always empty,
1167: * followed by zero or more opening punctuation nodes, then the
1168: * body (which may be empty, depending on the macro), then zero
1169: * or more closing punctuation nodes.
1170: */
1.22 schwarze 1171:
1172: if ( ! mdoc_block_alloc(m, line, ppos, tok, NULL))
1173: return(0);
1.32 schwarze 1174:
1.22 schwarze 1175: blk = m->last;
1.32 schwarze 1176:
1.22 schwarze 1177: if ( ! mdoc_head_alloc(m, line, ppos, tok))
1.1 kristaps 1178: return(0);
1.22 schwarze 1179: if ( ! rew_sub(MDOC_HEAD, m, tok, line, ppos))
1.1 kristaps 1180: return(0);
1181:
1.32 schwarze 1182: /*
1183: * Open the body scope "on-demand", that is, after we've
1184: * processed all our the leading delimiters (open parenthesis,
1185: * etc.).
1186: */
1.1 kristaps 1187:
1.32 schwarze 1188: for (body = NULL; ; ) {
1.22 schwarze 1189: la = *pos;
1.36 schwarze 1190: ac = mdoc_args(m, line, pos, buf, tok, &p);
1.32 schwarze 1191:
1.36 schwarze 1192: if (ARGS_ERROR == ac)
1.1 kristaps 1193: return(0);
1.36 schwarze 1194: if (ARGS_EOLN == ac)
1.32 schwarze 1195: break;
1.36 schwarze 1196: if (ARGS_PUNCT == ac)
1.1 kristaps 1197: break;
1198:
1.36 schwarze 1199: if (NULL == body && ARGS_QWORD != ac &&
1.40 schwarze 1200: DELIM_OPEN == mdoc_isdelim(p)) {
1.22 schwarze 1201: if ( ! mdoc_word_alloc(m, line, la, p))
1.1 kristaps 1202: return(0);
1203: continue;
1204: }
1205:
1.32 schwarze 1206: if (NULL == body) {
1207: if ( ! mdoc_body_alloc(m, line, ppos, tok))
1208: return(0);
1209: body = m->last;
1210: }
1211:
1.36 schwarze 1212: ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
1213:
1214: if (MDOC_MAX == ntok) {
1215: if ( ! mdoc_word_alloc(m, line, la, p))
1.32 schwarze 1216: return(0);
1.36 schwarze 1217: continue;
1218: }
1.32 schwarze 1219:
1.36 schwarze 1220: if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
1.32 schwarze 1221: return(0);
1.36 schwarze 1222: break;
1.32 schwarze 1223: }
1224:
1225: /* Clean-ups to leave in a consistent state. */
1226:
1227: if (NULL == body) {
1228: if ( ! mdoc_body_alloc(m, line, ppos, tok))
1.1 kristaps 1229: return(0);
1.32 schwarze 1230: body = m->last;
1.39 schwarze 1231: }
1232:
1233: for (n = body->child; n && n->next; n = n->next)
1234: /* Do nothing. */ ;
1235:
1236: /*
1237: * End of sentence spacing: if the last node is a text node and
1238: * has a trailing period, then mark it as being end-of-sentence.
1239: */
1240:
1241: if (n && MDOC_TEXT == n->type && n->string)
1242: if (mandoc_eos(n->string, strlen(n->string)))
1243: n->flags |= MDOC_EOS;
1244:
1245: /* Up-propogate the end-of-space flag. */
1246:
1247: if (n && (MDOC_EOS & n->flags)) {
1248: body->flags |= MDOC_EOS;
1249: body->parent->flags |= MDOC_EOS;
1.1 kristaps 1250: }
1251:
1.22 schwarze 1252: /*
1253: * If we can't rewind to our body, then our scope has already
1254: * been closed by another macro (like `Oc' closing `Op'). This
1255: * is ugly behaviour nodding its head to OpenBSD's overwhelming
1.34 schwarze 1256: * crufty use of `Op' breakage.
1.1 kristaps 1257: */
1.22 schwarze 1258: for (n = m->last; n; n = n->parent)
1.1 kristaps 1259: if (body == n)
1260: break;
1.32 schwarze 1261:
1.44 schwarze 1262: if (NULL == n && ! mdoc_nmsg(m, body, MANDOCERR_SCOPE))
1.22 schwarze 1263: return(0);
1.32 schwarze 1264:
1.22 schwarze 1265: if (n && ! rew_last(m, body))
1266: return(0);
1.1 kristaps 1267:
1.22 schwarze 1268: /* Standard appending of delimiters. */
1.1 kristaps 1269:
1.43 schwarze 1270: if (nl && ! append_delims(m, line, pos, buf))
1.1 kristaps 1271: return(0);
1272:
1.22 schwarze 1273: /* Rewind scope, if applicable. */
1.1 kristaps 1274:
1.22 schwarze 1275: if (n && ! rew_last(m, blk))
1.1 kristaps 1276: return(0);
1277:
1278: return(1);
1279: }
1280:
1281:
1282: static int
1283: blk_part_exp(MACRO_PROT_ARGS)
1284: {
1.37 schwarze 1285: int la, nl;
1.36 schwarze 1286: enum margserr ac;
1.32 schwarze 1287: struct mdoc_node *head; /* keep track of head */
1288: struct mdoc_node *body; /* keep track of body */
1.1 kristaps 1289: char *p;
1.36 schwarze 1290: enum mdoct ntok;
1.1 kristaps 1291:
1.37 schwarze 1292: nl = MDOC_NEWLINE & m->flags;
1293:
1.32 schwarze 1294: /*
1295: * The opening of an explicit macro having zero or more leading
1296: * punctuation nodes; a head with optional single element (the
1297: * case of `Eo'); and a body that may be empty.
1298: */
1.22 schwarze 1299:
1300: if ( ! mdoc_block_alloc(m, line, ppos, tok, NULL))
1.1 kristaps 1301: return(0);
1.22 schwarze 1302:
1.32 schwarze 1303: for (head = body = NULL; ; ) {
1.22 schwarze 1304: la = *pos;
1.36 schwarze 1305: ac = mdoc_args(m, line, pos, buf, tok, &p);
1.1 kristaps 1306:
1.36 schwarze 1307: if (ARGS_ERROR == ac)
1.1 kristaps 1308: return(0);
1.36 schwarze 1309: if (ARGS_PUNCT == ac)
1.1 kristaps 1310: break;
1.36 schwarze 1311: if (ARGS_EOLN == ac)
1.1 kristaps 1312: break;
1313:
1.32 schwarze 1314: /* Flush out leading punctuation. */
1315:
1.36 schwarze 1316: if (NULL == head && ARGS_QWORD != ac &&
1.40 schwarze 1317: DELIM_OPEN == mdoc_isdelim(p)) {
1.32 schwarze 1318: assert(NULL == body);
1319: if ( ! mdoc_word_alloc(m, line, la, p))
1320: return(0);
1321: continue;
1322: }
1323:
1324: if (NULL == head) {
1325: assert(NULL == body);
1326: if ( ! mdoc_head_alloc(m, line, ppos, tok))
1.1 kristaps 1327: return(0);
1.32 schwarze 1328: head = m->last;
1.1 kristaps 1329: }
1330:
1.32 schwarze 1331: /*
1332: * `Eo' gobbles any data into the head, but most other
1333: * macros just immediately close out and begin the body.
1334: */
1335:
1336: if (NULL == body) {
1337: assert(head);
1338: /* No check whether it's a macro! */
1339: if (MDOC_Eo == tok)
1340: if ( ! mdoc_word_alloc(m, line, la, p))
1341: return(0);
1342:
1.22 schwarze 1343: if ( ! rew_sub(MDOC_HEAD, m, tok, line, ppos))
1.1 kristaps 1344: return(0);
1.22 schwarze 1345: if ( ! mdoc_body_alloc(m, line, ppos, tok))
1.1 kristaps 1346: return(0);
1.32 schwarze 1347: body = m->last;
1348:
1349: if (MDOC_Eo == tok)
1350: continue;
1351: }
1352:
1353: assert(NULL != head && NULL != body);
1354:
1.36 schwarze 1355: ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
1356:
1357: if (MDOC_MAX == ntok) {
1358: if ( ! mdoc_word_alloc(m, line, la, p))
1.32 schwarze 1359: return(0);
1.36 schwarze 1360: continue;
1.1 kristaps 1361: }
1.32 schwarze 1362:
1.36 schwarze 1363: if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
1.1 kristaps 1364: return(0);
1.36 schwarze 1365: break;
1.1 kristaps 1366: }
1367:
1.32 schwarze 1368: /* Clean-up to leave in a consistent state. */
1369:
1370: if (NULL == head) {
1371: if ( ! mdoc_head_alloc(m, line, ppos, tok))
1372: return(0);
1373: head = m->last;
1374: }
1.22 schwarze 1375:
1.32 schwarze 1376: if (NULL == body) {
1.22 schwarze 1377: if ( ! rew_sub(MDOC_HEAD, m, tok, line, ppos))
1.1 kristaps 1378: return(0);
1.22 schwarze 1379: if ( ! mdoc_body_alloc(m, line, ppos, tok))
1.1 kristaps 1380: return(0);
1.32 schwarze 1381: body = m->last;
1.1 kristaps 1382: }
1383:
1.22 schwarze 1384: /* Standard appending of delimiters. */
1385:
1.37 schwarze 1386: if ( ! nl)
1.1 kristaps 1387: return(1);
1.22 schwarze 1388: return(append_delims(m, line, pos, buf));
1.1 kristaps 1389: }
1390:
1391:
1.37 schwarze 1392: /* ARGSUSED */
1.1 kristaps 1393: static int
1394: in_line_argn(MACRO_PROT_ARGS)
1395: {
1.37 schwarze 1396: int la, flushed, j, maxargs, nl;
1.36 schwarze 1397: enum margserr ac;
1398: enum margverr av;
1399: struct mdoc_arg *arg;
1400: char *p;
1401: enum mdoct ntok;
1.1 kristaps 1402:
1.37 schwarze 1403: nl = MDOC_NEWLINE & m->flags;
1404:
1.32 schwarze 1405: /*
1406: * A line macro that has a fixed number of arguments (maxargs).
1407: * Only open the scope once the first non-leading-punctuation is
1408: * found (unless MDOC_IGNDELIM is noted, like in `Pf'), then
1409: * keep it open until the maximum number of arguments are
1410: * exhausted.
1411: */
1.1 kristaps 1412:
1413: switch (tok) {
1414: case (MDOC_Ap):
1415: /* FALLTHROUGH */
1416: case (MDOC_No):
1417: /* FALLTHROUGH */
1418: case (MDOC_Ns):
1419: /* FALLTHROUGH */
1420: case (MDOC_Ux):
1421: maxargs = 0;
1422: break;
1.28 schwarze 1423: case (MDOC_Xr):
1424: maxargs = 2;
1425: break;
1.1 kristaps 1426: default:
1427: maxargs = 1;
1428: break;
1429: }
1430:
1.32 schwarze 1431: for (arg = NULL; ; ) {
1.22 schwarze 1432: la = *pos;
1.36 schwarze 1433: av = mdoc_argv(m, line, tok, &arg, pos, buf);
1.1 kristaps 1434:
1.36 schwarze 1435: if (ARGV_WORD == av) {
1.22 schwarze 1436: *pos = la;
1.1 kristaps 1437: break;
1438: }
1439:
1.36 schwarze 1440: if (ARGV_EOLN == av)
1.1 kristaps 1441: break;
1.36 schwarze 1442: if (ARGV_ARG == av)
1.1 kristaps 1443: continue;
1444:
1445: mdoc_argv_free(arg);
1446: return(0);
1447: }
1448:
1.32 schwarze 1449: for (flushed = j = 0; ; ) {
1.22 schwarze 1450: la = *pos;
1.36 schwarze 1451: ac = mdoc_args(m, line, pos, buf, tok, &p);
1.1 kristaps 1452:
1.36 schwarze 1453: if (ARGS_ERROR == ac)
1.1 kristaps 1454: return(0);
1.36 schwarze 1455: if (ARGS_PUNCT == ac)
1.1 kristaps 1456: break;
1.36 schwarze 1457: if (ARGS_EOLN == ac)
1.1 kristaps 1458: break;
1459:
1.32 schwarze 1460: if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) &&
1.36 schwarze 1461: ARGS_QWORD != ac &&
1.40 schwarze 1462: 0 == j && DELIM_OPEN == mdoc_isdelim(p)) {
1.32 schwarze 1463: if ( ! mdoc_word_alloc(m, line, la, p))
1464: return(0);
1465: continue;
1.40 schwarze 1466: } else if (0 == j)
1.32 schwarze 1467: if ( ! mdoc_elem_alloc(m, line, la, tok, arg))
1468: return(0);
1469:
1470: if (j == maxargs && ! flushed) {
1471: if ( ! rew_elem(m, tok))
1472: return(0);
1473: flushed = 1;
1474: }
1475:
1.36 schwarze 1476: ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
1477:
1478: if (MDOC_MAX != ntok) {
1.22 schwarze 1479: if ( ! flushed && ! rew_elem(m, tok))
1.1 kristaps 1480: return(0);
1481: flushed = 1;
1.36 schwarze 1482: if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
1.1 kristaps 1483: return(0);
1.32 schwarze 1484: j++;
1.1 kristaps 1485: break;
1486: }
1487:
1488: if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) &&
1.36 schwarze 1489: ARGS_QWORD != ac &&
1.40 schwarze 1490: ! flushed &&
1491: DELIM_NONE != mdoc_isdelim(p)) {
1.22 schwarze 1492: if ( ! rew_elem(m, tok))
1.1 kristaps 1493: return(0);
1494: flushed = 1;
1495: }
1.28 schwarze 1496:
1497: /*
1498: * XXX: this is a hack to work around groff's ugliness
1499: * as regards `Xr' and extraneous arguments. It should
1500: * ideally be deprecated behaviour, but because this is
1501: * code is no here, it's unlikely to be removed.
1502: */
1503: if (MDOC_Xr == tok && j == maxargs) {
1.32 schwarze 1504: if ( ! mdoc_elem_alloc(m, line, la, MDOC_Ns, NULL))
1.28 schwarze 1505: return(0);
1506: if ( ! rew_elem(m, MDOC_Ns))
1507: return(0);
1508: }
1509:
1.22 schwarze 1510: if ( ! mdoc_word_alloc(m, line, la, p))
1.1 kristaps 1511: return(0);
1.32 schwarze 1512: j++;
1.1 kristaps 1513: }
1514:
1.32 schwarze 1515: if (0 == j && ! mdoc_elem_alloc(m, line, la, tok, arg))
1516: return(0);
1517:
1518: /* Close out in a consistent state. */
1.22 schwarze 1519:
1520: if ( ! flushed && ! rew_elem(m, tok))
1.1 kristaps 1521: return(0);
1.37 schwarze 1522: if ( ! nl)
1.1 kristaps 1523: return(1);
1.22 schwarze 1524: return(append_delims(m, line, pos, buf));
1.1 kristaps 1525: }
1526:
1527:
1528: static int
1529: in_line_eoln(MACRO_PROT_ARGS)
1530: {
1.36 schwarze 1531: int la;
1532: enum margserr ac;
1533: enum margverr av;
1534: struct mdoc_arg *arg;
1535: char *p;
1536: enum mdoct ntok;
1.1 kristaps 1537:
1538: assert( ! (MDOC_PARSED & mdoc_macros[tok].flags));
1539:
1.22 schwarze 1540: /* Parse macro arguments. */
1.1 kristaps 1541:
1.22 schwarze 1542: for (arg = NULL; ; ) {
1.1 kristaps 1543: la = *pos;
1.36 schwarze 1544: av = mdoc_argv(m, line, tok, &arg, pos, buf);
1.1 kristaps 1545:
1.36 schwarze 1546: if (ARGV_WORD == av) {
1.1 kristaps 1547: *pos = la;
1548: break;
1549: }
1.36 schwarze 1550: if (ARGV_EOLN == av)
1.1 kristaps 1551: break;
1.36 schwarze 1552: if (ARGV_ARG == av)
1.1 kristaps 1553: continue;
1554:
1555: mdoc_argv_free(arg);
1556: return(0);
1557: }
1558:
1.22 schwarze 1559: /* Open element scope. */
1560:
1561: if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg))
1.1 kristaps 1562: return(0);
1563:
1.22 schwarze 1564: /* Parse argument terms. */
1.1 kristaps 1565:
1566: for (;;) {
1567: la = *pos;
1.36 schwarze 1568: ac = mdoc_args(m, line, pos, buf, tok, &p);
1.1 kristaps 1569:
1.36 schwarze 1570: if (ARGS_ERROR == ac)
1.1 kristaps 1571: return(0);
1.36 schwarze 1572: if (ARGS_EOLN == ac)
1.1 kristaps 1573: break;
1574:
1.36 schwarze 1575: ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
1.1 kristaps 1576:
1.36 schwarze 1577: if (MDOC_MAX == ntok) {
1578: if ( ! mdoc_word_alloc(m, line, la, p))
1.1 kristaps 1579: return(0);
1.36 schwarze 1580: continue;
1581: }
1.1 kristaps 1582:
1.36 schwarze 1583: if ( ! rew_elem(m, tok))
1.1 kristaps 1584: return(0);
1.36 schwarze 1585: return(mdoc_macro(m, ntok, line, la, pos, buf));
1.1 kristaps 1586: }
1587:
1.22 schwarze 1588: /* Close out (no delimiters). */
1589:
1590: return(rew_elem(m, tok));
1.28 schwarze 1591: }
1592:
1593:
1594: /* ARGSUSED */
1595: static int
1596: ctx_synopsis(MACRO_PROT_ARGS)
1597: {
1.37 schwarze 1598: int nl;
1599:
1600: nl = MDOC_NEWLINE & m->flags;
1.28 schwarze 1601:
1602: /* If we're not in the SYNOPSIS, go straight to in-line. */
1603: if (SEC_SYNOPSIS != m->lastsec)
1604: return(in_line(m, tok, line, ppos, pos, buf));
1605:
1606: /* If we're a nested call, same place. */
1.37 schwarze 1607: if ( ! nl)
1.28 schwarze 1608: return(in_line(m, tok, line, ppos, pos, buf));
1609:
1610: /*
1611: * XXX: this will open a block scope; however, if later we end
1612: * up formatting the block scope, then child nodes will inherit
1613: * the formatting. Be careful.
1614: */
1615:
1616: return(blk_part_imp(m, tok, line, ppos, pos, buf));
1.1 kristaps 1617: }
1618:
1619:
1620: /* ARGSUSED */
1621: static int
1622: obsolete(MACRO_PROT_ARGS)
1623: {
1624:
1.44 schwarze 1625: return(mdoc_pmsg(m, line, ppos, MANDOCERR_MACROOBS));
1.1 kristaps 1626: }
1627:
1628:
1.17 schwarze 1629: /*
1630: * Phrases occur within `Bl -column' entries, separated by `Ta' or tabs.
1631: * They're unusual because they're basically free-form text until a
1632: * macro is encountered.
1633: */
1.1 kristaps 1634: static int
1.46 ! schwarze 1635: phrase(struct mdoc *m, int line, int ppos, char *buf)
1.1 kristaps 1636: {
1.36 schwarze 1637: int la, pos;
1.46 ! schwarze 1638: enum margserr ac;
1.36 schwarze 1639: enum mdoct ntok;
1640: char *p;
1.1 kristaps 1641:
1.17 schwarze 1642: for (pos = ppos; ; ) {
1643: la = pos;
1.1 kristaps 1644:
1.46 ! schwarze 1645: ac = mdoc_zargs(m, line, &pos, buf, 0, &p);
1.1 kristaps 1646:
1.46 ! schwarze 1647: if (ARGS_ERROR == ac)
1.17 schwarze 1648: return(0);
1.46 ! schwarze 1649: if (ARGS_EOLN == ac)
1.17 schwarze 1650: break;
1.1 kristaps 1651:
1.46 ! schwarze 1652: ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup_raw(p);
1.1 kristaps 1653:
1.36 schwarze 1654: if (MDOC_MAX == ntok) {
1655: if ( ! mdoc_word_alloc(m, line, la, p))
1.1 kristaps 1656: return(0);
1.36 schwarze 1657: continue;
1658: }
1.1 kristaps 1659:
1.36 schwarze 1660: if ( ! mdoc_macro(m, ntok, line, la, &pos, buf))
1.1 kristaps 1661: return(0);
1.36 schwarze 1662: return(append_delims(m, line, &pos, buf));
1.1 kristaps 1663: }
1664:
1665: return(1);
1666: }
1.17 schwarze 1667:
1668:
1.46 ! schwarze 1669: /* ARGSUSED */
! 1670: static int
! 1671: phrase_ta(MACRO_PROT_ARGS)
! 1672: {
! 1673: int la;
! 1674: enum mdoct ntok;
! 1675: enum margserr ac;
! 1676: char *p;
! 1677:
! 1678: /*
! 1679: * FIXME: this is overly restrictive: if the `Ta' is unexpected,
! 1680: * it should simply error out with ARGSLOST.
! 1681: */
! 1682:
! 1683: if ( ! rew_sub(MDOC_BODY, m, MDOC_It, line, ppos))
! 1684: return(0);
! 1685: if ( ! mdoc_body_alloc(m, line, ppos, MDOC_It))
! 1686: return(0);
! 1687:
! 1688: for (;;) {
! 1689: la = *pos;
! 1690: ac = mdoc_zargs(m, line, pos, buf, 0, &p);
! 1691:
! 1692: if (ARGS_ERROR == ac)
! 1693: return(0);
! 1694: if (ARGS_EOLN == ac)
! 1695: break;
! 1696:
! 1697: ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup_raw(p);
! 1698:
! 1699: if (MDOC_MAX == ntok) {
! 1700: if ( ! mdoc_word_alloc(m, line, la, p))
! 1701: return(0);
! 1702: continue;
! 1703: }
! 1704:
! 1705: if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
! 1706: return(0);
! 1707: return(append_delims(m, line, pos, buf));
! 1708: }
! 1709:
! 1710: return(1);
! 1711: }