]> git.cameronkatri.com Git - mandoc.git/blob - macro.c
*** empty log message ***
[mandoc.git] / macro.c
1 /* $Id: macro.c,v 1.6 2008/12/23 05:30:49 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
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 <ctype.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 #include "private.h"
26
27 #define _CC(p) ((const char **)p)
28
29 static int scope_rewind_exp(struct mdoc *, int, int, int);
30 static int append_text(struct mdoc *, int,
31 int, int, char *[]);
32 static int append_scoped(struct mdoc *, int, int, int,
33 const char *[], int, const struct mdoc_arg *);
34 static int args_next(struct mdoc *, int,
35 int *, char *, char **);
36 static int argv_next(struct mdoc *, int,
37 int *, char *, struct mdoc_arg *);
38 static int args_next_quoted(struct mdoc *, int,
39 int *, char *, char **);
40
41
42 static int
43 args_next_quoted(struct mdoc *mdoc, int tok,
44 int *pos, char *buf, char **v)
45 {
46
47 if (0 == buf[*pos])
48 return(0);
49
50 assert( ! isspace(buf[*pos]));
51
52 if ('\"' != buf[*pos])
53 return(args_next(mdoc, tok, pos, buf, v));
54
55 *v = &buf[++(*pos)];
56
57 while (buf[*pos] && '\"' != buf[*pos])
58 (*pos)++;
59
60 if (0 == buf[*pos]) {
61 (void)mdoc_err(mdoc, tok, *pos, ERR_SYNTAX_UNQUOTE);
62 return(-1);
63 }
64
65 buf[(*pos)++] = 0;
66 if (0 == buf[*pos])
67 return(1);
68
69 while (buf[*pos] && isspace(buf[*pos]))
70 (*pos)++;
71
72 if (0 == buf[*pos])
73 if ( ! mdoc_warn(mdoc, tok, *pos, WARN_SYNTAX_WS_EOLN))
74 return(-1);
75
76 return(1);
77 }
78
79
80 static int
81 scope_rewind_exp(struct mdoc *mdoc, int ppos, int tok, int dst)
82 {
83 struct mdoc_node *n;
84
85 /* LINTED */
86 for (n = mdoc->last; n; n = n->parent) {
87 if (MDOC_BLOCK != n->type)
88 continue;
89 if (dst == n->data.block.tok)
90 break;
91 return(mdoc_err(mdoc, tok, ppos, ERR_SCOPE_BREAK));
92 }
93
94 if (NULL == (mdoc->last = n))
95 return(mdoc_err(mdoc, tok, ppos, ERR_SCOPE_NOCTX));
96
97 mdoc_msg(mdoc, ppos, "scope: rewound `%s' to `%s'",
98 mdoc_macronames[tok], mdoc_macronames[dst]);
99
100 return(1);
101 }
102
103
104 static int
105 argv_next(struct mdoc *mdoc, int tok,
106 int *pos, char *buf, struct mdoc_arg *v)
107 {
108 char *argv;
109 int i, val;
110
111 if (0 == buf[*pos])
112 return(0);
113
114 assert( ! isspace(buf[*pos]));
115
116 if ('-' != buf[*pos]) {
117 (void)mdoc_err(mdoc, tok, *pos, ERR_SYNTAX_ARGS);
118 return(-1);
119 }
120
121 i = *pos;
122 argv = &buf[++(*pos)];
123
124 while (buf[*pos] && ! isspace(buf[*pos]))
125 (*pos)++;
126
127 if (buf[*pos])
128 buf[(*pos)++] = 0;
129
130 /*
131 * XXX This is a little bit ugly. The mdoc_argv structure
132 * points to a pointer array, which we allocate on-the-fly in
133 * this function. If there's any failure, we need to release
134 * this memory, which is done by the caller of this function
135 * with mdoc_argv_free. Ew. This should be simpler.
136 */
137
138 if (MDOC_ARG_MAX == (val = mdoc_argv_lookup(tok, argv))) {
139 (void)mdoc_err(mdoc, tok, i, ERR_SYNTAX_BADARG);
140 return(-1);
141 }
142
143 while (buf[*pos] && isspace(buf[*pos]))
144 (*pos)++;
145
146 if ( ! mdoc_argv_parse(mdoc, tok, val, v, pos, buf))
147 return(-1);
148
149 return(1);
150 }
151
152
153 static int
154 args_next(struct mdoc *mdoc, int tok,
155 int *pos, char *buf, char **v)
156 {
157
158 if (0 == buf[*pos])
159 return(0);
160
161 assert( ! isspace(buf[*pos]));
162
163 if ('\"' == buf[*pos]) {
164 (void)mdoc_err(mdoc, tok, *pos, ERR_SYNTAX_QUOTE);
165 return(-1);
166 }
167
168 *v = &buf[*pos];
169
170 /* Scan ahead to end of token. */
171
172 while (buf[*pos] && ! isspace(buf[*pos]))
173 (*pos)++;
174
175 if (buf[*pos] && buf[*pos + 1] && '\\' == buf[*pos]) {
176 (void)mdoc_err(mdoc, tok, *pos, ERR_SYNTAX_WS);
177 return(-1);
178 }
179
180 if (0 == buf[*pos])
181 return(1);
182
183 /* Scan ahead over trailing whitespace. */
184
185 buf[(*pos)++] = 0;
186 while (buf[*pos] && isspace(buf[*pos]))
187 (*pos)++;
188
189 if (0 == buf[*pos])
190 if ( ! mdoc_warn(mdoc, tok, *pos, WARN_SYNTAX_WS_EOLN))
191 return(-1);
192
193 return(1);
194 }
195
196
197 static int
198 append_scoped(struct mdoc *mdoc, int tok, int pos,
199 int sz, const char *args[],
200 int argc, const struct mdoc_arg *argv)
201 {
202 enum mdoc_sec sec;
203
204 switch (tok) {
205 /* ======= ADD MORE MACRO CHECKS BELOW. ======= */
206 case (MDOC_Sh):
207 if (0 == sz)
208 return(mdoc_err(mdoc, tok, pos, ERR_ARGS_GE1));
209
210 sec = mdoc_atosec((size_t)sz, _CC(args));
211 if (SEC_CUSTOM != sec && sec < mdoc->sec_lastn)
212 if ( ! mdoc_warn(mdoc, tok, pos, WARN_SEC_OO))
213 return(0);
214
215 if (SEC_BODY == mdoc->sec_last && SEC_NAME != sec)
216 return(mdoc_err(mdoc, tok, pos, ERR_SEC_NAME));
217
218 if (SEC_CUSTOM != sec)
219 mdoc->sec_lastn = sec;
220 mdoc->sec_last = sec;
221 break;
222
223 case (MDOC_Ss):
224 if (0 == sz)
225 return(mdoc_err(mdoc, tok, pos, ERR_ARGS_GE1));
226 break;
227
228 case (MDOC_Bl):
229 break;
230
231 /* ======= ADD MORE MACRO CHECKS ABOVE. ======= */
232 default:
233 abort();
234 /* NOTREACHED */
235 }
236
237 mdoc_block_alloc(mdoc, pos, tok, (size_t)argc, argv);
238 mdoc_head_alloc(mdoc, pos, tok, (size_t)sz, _CC(args));
239 mdoc_body_alloc(mdoc, pos, tok);
240 return(1);
241 }
242
243
244 static int
245 append_text(struct mdoc *mdoc, int tok,
246 int pos, int sz, char *args[])
247 {
248
249 assert(sz >= 0);
250 args[sz] = NULL;
251
252 switch (tok) {
253 /* ======= ADD MORE MACRO CHECKS BELOW. ======= */
254 case (MDOC_Ft):
255 /* FALLTHROUGH */
256 case (MDOC_Li):
257 /* FALLTHROUGH */
258 case (MDOC_Ms):
259 /* FALLTHROUGH */
260 case (MDOC_Pa):
261 /* FALLTHROUGH */
262 case (MDOC_Tn):
263 if (0 < sz)
264 break;
265 if ( ! mdoc_warn(mdoc, tok, pos, WARN_ARGS_GE1))
266 return(0);
267 break;
268 case (MDOC_Ar):
269 /* FALLTHROUGH */
270 case (MDOC_Cm):
271 /* FALLTHROUGH */
272 case (MDOC_Fl):
273 break;
274 case (MDOC_Ad):
275 /* FALLTHROUGH */
276 case (MDOC_Em):
277 /* FALLTHROUGH */
278 case (MDOC_Er):
279 /* FALLTHROUGH */
280 case (MDOC_Ev):
281 /* FALLTHROUGH */
282 case (MDOC_Fa):
283 /* FALLTHROUGH */
284 case (MDOC_Dv):
285 /* FALLTHROUGH */
286 case (MDOC_Ic):
287 /* FALLTHROUGH */
288 case (MDOC_Va):
289 /* FALLTHROUGH */
290 case (MDOC_Vt):
291 if (0 < sz)
292 break;
293 return(mdoc_err(mdoc, tok, pos, ERR_ARGS_GE1));
294 /* ======= ADD MORE MACRO CHECKS ABOVE. ======= */
295 default:
296 abort();
297 /* NOTREACHED */
298 }
299
300 mdoc_elem_alloc(mdoc, pos, tok, 0,
301 NULL, (size_t)sz, _CC(args));
302 return(1);
303 }
304
305
306 int
307 macro_text(MACRO_PROT_ARGS)
308 {
309 int lastarg, c, lasttok, lastpunct, j;
310 char *args[MDOC_LINEARG_MAX], *p;
311
312 lasttok = ppos;
313 lastpunct = 0;
314 j = 0;
315
316 if (SEC_PROLOGUE == mdoc->sec_lastn)
317 return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
318
319 again:
320 lastarg = *pos;
321
322 if (j == MDOC_LINEARG_MAX)
323 return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
324 c = args_next(mdoc, tok, pos, buf, &args[j]);
325
326 if (-1 == c)
327 return(0);
328 if (0 == c && ! lastpunct)
329 return(append_text(mdoc, tok, lasttok, j, args));
330 else if (0 == c)
331 return(1);
332
333 /* Command found. */
334
335 if (MDOC_MAX != (c = mdoc_find(mdoc, args[j]))) {
336 if ( ! lastpunct)
337 if ( ! append_text(mdoc, tok, lasttok, j, args))
338 return(0);
339 return(mdoc_macro(mdoc, c, lastarg, pos, buf));
340 }
341
342 /* Word found. */
343
344 if ( ! mdoc_isdelim(args[j])) {
345 j++;
346 goto again;
347 }
348
349 /* Punctuation found. */
350
351 p = args[j]; /* Save argument (NULL-ified in append). */
352
353 if ( ! lastpunct)
354 if ( ! append_text(mdoc, tok, lasttok, j, args))
355 return(0);
356
357 args[j] = p;
358
359 mdoc_word_alloc(mdoc, lastarg, args[j]);
360 lastpunct = 1;
361 j = 0;
362
363 goto again;
364
365 /* NOTREACHED */
366 }
367
368
369 int
370 macro_prologue_dtitle(MACRO_PROT_ARGS)
371 {
372 int c, lastarg, j;
373 char *args[MDOC_LINEARG_MAX];
374
375 if (SEC_PROLOGUE != mdoc->sec_lastn)
376 return(mdoc_err(mdoc, tok, ppos, ERR_SEC_NPROLOGUE));
377 if (0 == mdoc->meta.date)
378 return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_OO));
379 if (mdoc->meta.title[0])
380 return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_REP));
381
382 j = -1;
383
384 again:
385 lastarg = *pos;
386
387 if (j == MDOC_LINEARG_MAX)
388 return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
389 c = args_next(mdoc, tok, pos, buf, &args[++j]);
390
391 if (0 == c) {
392 if (mdoc->meta.title)
393 return(1);
394 if ( ! mdoc_warn(mdoc, tok, ppos, WARN_ARGS_GE1))
395 return(0);
396 (void)xstrlcpy(mdoc->meta.title,
397 "UNTITLED", META_TITLE_SZ);
398 return(1);
399 } else if (-1 == c)
400 return(0);
401
402 if (MDOC_MAX != mdoc_find(mdoc, args[j]) && ! mdoc_warn
403 (mdoc, tok, lastarg, WARN_SYNTAX_MACLIKE))
404 return(0);
405
406 if (0 == j) {
407 if (xstrlcpy(mdoc->meta.title, args[0], META_TITLE_SZ))
408 goto again;
409 return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGS));
410
411 } else if (1 == j) {
412 mdoc->meta.msec = mdoc_atomsec(args[1]);
413 if (MSEC_DEFAULT != mdoc->meta.msec)
414 goto again;
415 return(mdoc_err(mdoc, tok, -1, ERR_SYNTAX_ARGS));
416
417 } else if (2 == j) {
418 mdoc->meta.vol = mdoc_atovol(args[2]);
419 if (VOL_DEFAULT != mdoc->meta.vol)
420 goto again;
421 mdoc->meta.arch = mdoc_atoarch(args[2]);
422 if (ARCH_DEFAULT != mdoc->meta.arch)
423 goto again;
424 return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGS));
425 }
426
427 return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
428 }
429
430
431 int
432 macro_prologue_os(MACRO_PROT_ARGS)
433 {
434 int c, lastarg, j;
435 char *args[MDOC_LINEARG_MAX];
436
437 if (SEC_PROLOGUE != mdoc->sec_lastn)
438 return(mdoc_err(mdoc, tok, ppos, ERR_SEC_NPROLOGUE));
439 if (0 == mdoc->meta.title[0])
440 return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_OO));
441 if (mdoc->meta.os[0])
442 return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_REP));
443
444 j = -1;
445
446 again:
447 lastarg = *pos;
448
449 if (j == MDOC_LINEARG_MAX)
450 return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
451 c = args_next_quoted(mdoc, tok, pos, buf, &args[++j]);
452
453 if (0 == c) {
454 mdoc->sec_lastn = mdoc->sec_last = SEC_BODY;
455 return(1);
456 } else if (-1 == c)
457 return(0);
458
459 if ( ! xstrlcat(mdoc->meta.os, args[j], sizeof(mdoc->meta.os)))
460 return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGS));
461 if ( ! xstrlcat(mdoc->meta.os, " ", sizeof(mdoc->meta.os)))
462 return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGS));
463
464 goto again;
465 /* NOTREACHED */
466 }
467
468
469 int
470 macro_prologue_ddate(MACRO_PROT_ARGS)
471 {
472 int c, lastarg, j;
473 char *args[MDOC_LINEARG_MAX], date[64];
474
475 if (SEC_PROLOGUE != mdoc->sec_lastn)
476 return(mdoc_err(mdoc, tok, ppos, ERR_SEC_NPROLOGUE));
477 if (mdoc->meta.title[0])
478 return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_OO));
479 if (mdoc->meta.date)
480 return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_REP));
481
482 j = -1;
483 date[0] = 0;
484
485 again:
486 lastarg = *pos;
487
488 if (j == MDOC_LINEARG_MAX)
489 return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
490 c = args_next(mdoc, tok, pos, buf, &args[++j]);
491
492 if (0 == c) {
493 if (mdoc->meta.date)
494 return(1);
495 mdoc->meta.date = mdoc_atotime(date);
496 if (mdoc->meta.date)
497 return(1);
498 return(mdoc_err(mdoc, tok, ppos, ERR_SYNTAX_ARGS));
499 } else if (-1 == c)
500 return(0);
501
502 if (MDOC_MAX != mdoc_find(mdoc, args[j]) && ! mdoc_warn
503 (mdoc, tok, lastarg, WARN_SYNTAX_MACLIKE))
504 return(0);
505
506 if (0 == j) {
507 if (xstrcmp("$Mdocdate: December 23 2008 $", args[j])) {
508 mdoc->meta.date = time(NULL);
509 goto again;
510 } else if (xstrcmp("$Mdocdate:", args[j]))
511 goto again;
512 } else if (4 == j)
513 if ( ! xstrcmp("$", args[j]))
514 goto again;
515
516 if ( ! xstrlcat(date, args[j], sizeof(date)))
517 return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGS));
518 if ( ! xstrlcat(date, " ", sizeof(date)))
519 return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGS));
520
521 goto again;
522 /* NOTREACHED */
523 }
524
525
526 int
527 macro_scoped_explicit(MACRO_PROT_ARGS)
528 {
529 int c, lastarg, j;
530 struct mdoc_arg argv[MDOC_LINEARG_MAX];
531
532 if (SEC_PROLOGUE == mdoc->sec_lastn)
533 return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
534
535 /*
536 * First close out the explicit scope. The `end' tags (such as
537 * `.El' to `.Bl' don't cause anything to happen: we merely
538 * readjust our last parse point.
539 */
540
541 switch (tok) {
542 case (MDOC_El):
543 return(scope_rewind_exp(mdoc, ppos, tok, MDOC_Bl));
544 default:
545 break;
546 }
547
548 assert(MDOC_EXPLICIT & mdoc_macros[tok].flags);
549
550 lastarg = *pos;
551
552 for (j = 0; j < MDOC_LINEARG_MAX; j++) {
553 lastarg = *pos;
554 c = argv_next(mdoc, tok, pos, buf, &argv[j]);
555 if (0 == c)
556 break;
557 else if (1 == c)
558 continue;
559
560 mdoc_argv_free(j, argv);
561 return(0);
562 }
563
564 if (MDOC_LINEARG_MAX == j) {
565 mdoc_argv_free(j, argv);
566 return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
567 }
568
569 c = append_scoped(mdoc, tok, ppos, 0, NULL, j, argv);
570 mdoc_argv_free(j, argv);
571 return(c);
572 }
573
574
575 int
576 macro_scoped_implicit(MACRO_PROT_ARGS)
577 {
578 int t, c, lastarg, j;
579 char *args[MDOC_LINEARG_MAX];
580 struct mdoc_node *n;
581
582 assert( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags));
583
584 if (SEC_PROLOGUE == mdoc->sec_lastn)
585 return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
586
587 /* FIXME: put into scope_rewind_imp(). */
588
589 /* LINTED */
590 for (n = mdoc->last; n; n = n->parent) {
591 if (MDOC_BLOCK != n->type)
592 continue;
593 if (tok == (t = n->data.block.tok))
594 break;
595 if ( ! (MDOC_EXPLICIT & mdoc_macros[t].flags))
596 continue;
597 return(mdoc_err(mdoc, tok, ppos, ERR_SCOPE_BREAK));
598 }
599
600 if (n) {
601 mdoc->last = n;
602 mdoc_msg(mdoc, ppos, "scope: rewound `%s'",
603 mdoc_macronames[tok]);
604 } else
605 mdoc_msg(mdoc, ppos, "scope: new `%s'",
606 mdoc_macronames[tok]);
607
608 j = 0;
609
610 again:
611 lastarg = *pos;
612
613 if (j == MDOC_LINEARG_MAX)
614 return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
615 c = args_next(mdoc, tok, pos, buf, &args[j]);
616
617 if (-1 == c)
618 return(0);
619 if (0 == c)
620 return(append_scoped(mdoc, tok, ppos,
621 j, _CC(args), 0, NULL));
622
623 /* Command found. */
624
625 if (MDOC_MAX != (c = mdoc_find(mdoc, args[j])))
626 if ( ! mdoc_warn(mdoc, tok, lastarg, WARN_SYNTAX_MACLIKE))
627 return(0);
628
629 /* Word found. */
630
631 j++;
632 goto again;
633
634 /* NOTREACHED */
635 }
636