Revamp of the Halibut error handling mechanism.
[sgt/halibut] / input.c
CommitLineData
d7482997 1/*
2 * input.c: read the source form
3 */
4
5#include <stdio.h>
6#include <assert.h>
7#include <time.h>
8#include "halibut.h"
9
10#define TAB_STOP 8 /* for column number tracking */
11
12static void setpos(input *in, char *fname) {
13 in->pos.filename = fname;
14 in->pos.line = 1;
15 in->pos.col = (in->reportcols ? 1 : -1);
16}
17
18static void unget(input *in, int c, filepos *pos) {
19 if (in->npushback >= in->pushbacksize) {
20 in->pushbacksize = in->npushback + 16;
f1530049 21 in->pushback = sresize(in->pushback, in->pushbacksize, pushback);
d7482997 22 }
23 in->pushback[in->npushback].chr = c;
24 in->pushback[in->npushback].pos = *pos; /* structure copy */
25 in->npushback++;
26}
27
28/* ---------------------------------------------------------------------- */
29/*
30 * Macro subsystem
31 */
32typedef struct macro_Tag macro;
33struct macro_Tag {
34 wchar_t *name, *text;
35};
36struct macrostack_Tag {
37 macrostack *next;
38 wchar_t *text;
39 int ptr, npushback;
40 filepos pos;
41};
42static int macrocmp(void *av, void *bv) {
43 macro *a = (macro *)av, *b = (macro *)bv;
44 return ustrcmp(a->name, b->name);
45}
46static void macrodef(tree234 *macros, wchar_t *name, wchar_t *text,
47 filepos fpos) {
f1530049 48 macro *m = snew(macro);
d7482997 49 m->name = name;
50 m->text = text;
51 if (add234(macros, m) != m) {
05e9c3c2 52 err_macroexists(&fpos, name);
d7482997 53 sfree(name);
54 sfree(text);
55 }
56}
57static int macrolookup(tree234 *macros, input *in, wchar_t *name,
58 filepos *pos) {
59 macro m, *gotit;
60 m.name = name;
61 gotit = find234(macros, &m, NULL);
62 if (gotit) {
f1530049 63 macrostack *expansion = snew(macrostack);
d7482997 64 expansion->next = in->stack;
65 expansion->text = gotit->text;
66 expansion->pos = *pos; /* structure copy */
67 expansion->ptr = 0;
68 expansion->npushback = in->npushback;
69 in->stack = expansion;
70 return TRUE;
71 } else
72 return FALSE;
73}
74static void macrocleanup(tree234 *macros) {
75 int ti;
76 macro *m;
77 for (ti = 0; (m = (macro *)index234(macros, ti)) != NULL; ti++) {
78 sfree(m->name);
79 sfree(m->text);
80 sfree(m);
81 }
82 freetree234(macros);
83}
84
e34ba5c3 85static void input_configure(input *in, paragraph *cfg) {
86 assert(cfg->type == para_Config);
87
88 if (!ustricmp(cfg->keyword, L"input-charset")) {
0960a3d8 89 in->charset = charset_from_ustr(&cfg->fpos, uadv(cfg->keyword));
e34ba5c3 90 }
91}
92
d7482997 93/*
94 * Can return EOF
95 */
e4ea58f8 96static int get(input *in, filepos *pos, rdstringc *rsc) {
d7482997 97 int pushbackpt = in->stack ? in->stack->npushback : 0;
98 if (in->npushback > pushbackpt) {
99 --in->npushback;
100 if (pos)
101 *pos = in->pushback[in->npushback].pos; /* structure copy */
102 return in->pushback[in->npushback].chr;
103 }
104 else if (in->stack) {
105 wchar_t c = in->stack->text[in->stack->ptr];
086907e6 106 if (pos)
107 *pos = in->stack->pos;
d7482997 108 if (in->stack->text[++in->stack->ptr] == L'\0') {
109 macrostack *tmp = in->stack;
110 in->stack = tmp->next;
111 sfree(tmp);
112 }
113 return c;
114 }
115 else if (in->currfp) {
d7482997 116
e34ba5c3 117 while (in->wcpos >= in->nwc) {
118
119 int c = getc(in->currfp);
120
121 if (c == EOF) {
d26171a6 122 if (in->wantclose)
123 fclose(in->currfp);
e34ba5c3 124 in->currfp = NULL;
125 return EOF;
126 }
e4ea58f8 127
128 if (rsc)
129 rdaddc(rsc, c);
130
e34ba5c3 131 /* Track line numbers, for error reporting */
132 if (pos)
133 *pos = in->pos;
134 if (in->reportcols) {
135 switch (c) {
136 case '\t':
137 in->pos.col = 1 + (in->pos.col + TAB_STOP-1) % TAB_STOP;
138 break;
139 case '\n':
140 in->pos.col = 1;
141 in->pos.line++;
142 break;
143 default:
144 in->pos.col++;
145 break;
146 }
147 } else {
148 in->pos.col = -1;
149 if (c == '\n')
150 in->pos.line++;
151 }
152
153 /*
154 * Do input character set translation, so that we return
155 * Unicode.
156 */
157 {
158 char buf[1];
159 char const *p;
160 int inlen;
161
162 buf[0] = (char)c;
163 p = buf;
164 inlen = 1;
165
166 in->nwc = charset_to_unicode(&p, &inlen,
167 in->wc, lenof(in->wc),
168 in->charset, &in->csstate,
169 NULL, 0);
170 assert(p == buf+1 && inlen == 0);
171
172 in->wcpos = 0;
d7482997 173 }
d7482997 174 }
e34ba5c3 175
176 return in->wc[in->wcpos++];
177
d7482997 178 } else
179 return EOF;
180}
181
182/*
183 * Lexical analysis of source files.
184 */
185typedef struct token_Tag token;
186struct token_Tag {
187 int type;
188 int cmd, aux;
189 wchar_t *text;
e4ea58f8 190 char *origtext;
d7482997 191 filepos pos;
192};
193enum {
194 tok_eof, /* end of file */
195 tok_eop, /* end of paragraph */
196 tok_white, /* whitespace */
197 tok_word, /* a word or word fragment */
198 tok_cmd, /* \command */
199 tok_lbrace, /* { */
200 tok_rbrace /* } */
201};
202
203/* Halibut command keywords. */
204enum {
205 c__invalid, /* invalid command */
206 c__comment, /* comment command (\#) */
207 c__escaped, /* escaped character */
672e4626 208 c__nop, /* no-op */
d7482997 209 c__nbsp, /* nonbreaking space */
210 c_A, /* appendix heading */
211 c_B, /* bibliography entry */
212 c_BR, /* bibliography rewrite */
213 c_C, /* chapter heading */
214 c_H, /* heading */
215 c_I, /* invisible index mark */
216 c_IM, /* index merge/rewrite */
217 c_K, /* capitalised cross-reference */
218 c_S, /* aux field is 0, 1, 2, ... */
219 c_U, /* unnumbered-chapter heading */
220 c_W, /* Web hyperlink */
221 c_b, /* bulletted list */
222 c_c, /* code */
223 c_cfg, /* configuration directive */
224 c_copyright, /* copyright statement */
b7747050 225 c_cq, /* quoted code (sugar for \q{\cw{x}}) */
d7482997 226 c_cw, /* weak code */
227 c_date, /* document processing date */
7136a6c7 228 c_dd, /* description list: description */
d7482997 229 c_define, /* macro definition */
7136a6c7 230 c_dt, /* description list: described thing */
d7482997 231 c_e, /* emphasis */
232 c_i, /* visible index mark */
233 c_ii, /* uncapitalised visible index mark */
234 c_k, /* uncapitalised cross-reference */
7136a6c7 235 c_lcont, /* continuation para(s) for list item */
d7482997 236 c_n, /* numbered list */
237 c_nocite, /* bibliography trickery */
8902e0ed 238 c_preamble, /* (obsolete) preamble text */
d7482997 239 c_q, /* quote marks */
2614b01d 240 c_quote, /* block-quoted paragraphs */
d7482997 241 c_rule, /* horizontal rule */
242 c_title, /* document title */
243 c_u, /* aux field is char code */
244 c_versionid /* document RCS id */
245};
246
247/* Perhaps whitespace should be defined in a more Unicode-friendly way? */
248#define iswhite(c) ( (c)==32 || (c)==9 || (c)==13 || (c)==10 )
249#define isnl(c) ( (c)==10 )
250#define isdec(c) ( ((c)>='0'&&(c)<='9') )
251#define fromdec(c) ( (c)-'0' )
252#define ishex(c) ( ((c)>='0'&&(c)<='9') || ((c)>='A'&&(c)<='F') || ((c)>='a'&&(c)<='f'))
253#define fromhex(c) ( (c)<='9' ? (c)-'0' : ((c)&0xDF) - ('A'-10) )
254#define iscmd(c) ( ((c)>='0'&&(c)<='9') || ((c)>='A'&&(c)<='Z') || ((c)>='a'&&(c)<='z'))
255
256/*
257 * Keyword comparison function. Like strcmp, but between a wchar_t *
258 * and a char *.
259 */
260static int kwcmp(wchar_t const *p, char const *q) {
261 int i;
262 do {
263 i = *p - *q;
264 } while (*p++ && *q++ && !i);
265 return i;
266}
267
268/*
269 * Match a keyword.
270 */
271static void match_kw(token *tok) {
272 /*
273 * FIXME. The ids are explicit in here so as to allow long-name
274 * equivalents to the various very short keywords.
275 */
276 static const struct { char const *name; int id; } keywords[] = {
277 {"#", c__comment}, /* comment command (\#) */
278 {"-", c__escaped}, /* nonbreaking hyphen */
672e4626 279 {".", c__nop}, /* no-op */
d7482997 280 {"A", c_A}, /* appendix heading */
281 {"B", c_B}, /* bibliography entry */
282 {"BR", c_BR}, /* bibliography rewrite */
283 {"C", c_C}, /* chapter heading */
284 {"H", c_H}, /* heading */
285 {"I", c_I}, /* invisible index mark */
286 {"IM", c_IM}, /* index merge/rewrite */
287 {"K", c_K}, /* capitalised cross-reference */
288 {"U", c_U}, /* unnumbered-chapter heading */
289 {"W", c_W}, /* Web hyperlink */
290 {"\\", c__escaped}, /* escaped backslash (\\) */
291 {"_", c__nbsp}, /* nonbreaking space (\_) */
292 {"b", c_b}, /* bulletted list */
293 {"c", c_c}, /* code */
294 {"cfg", c_cfg}, /* configuration directive */
295 {"copyright", c_copyright}, /* copyright statement */
b7747050 296 {"cq", c_cq}, /* quoted code (sugar for \q{\cw{x}}) */
d7482997 297 {"cw", c_cw}, /* weak code */
298 {"date", c_date}, /* document processing date */
7136a6c7 299 {"dd", c_dd}, /* description list: description */
d7482997 300 {"define", c_define}, /* macro definition */
7136a6c7 301 {"dt", c_dt}, /* description list: described thing */
d7482997 302 {"e", c_e}, /* emphasis */
303 {"i", c_i}, /* visible index mark */
304 {"ii", c_ii}, /* uncapitalised visible index mark */
305 {"k", c_k}, /* uncapitalised cross-reference */
7136a6c7 306 {"lcont", c_lcont}, /* continuation para(s) for list item */
d7482997 307 {"n", c_n}, /* numbered list */
308 {"nocite", c_nocite}, /* bibliography trickery */
8902e0ed 309 {"preamble", c_preamble}, /* (obsolete) preamble text */
d7482997 310 {"q", c_q}, /* quote marks */
2614b01d 311 {"quote", c_quote}, /* block-quoted paragraphs */
d7482997 312 {"rule", c_rule}, /* horizontal rule */
313 {"title", c_title}, /* document title */
314 {"versionid", c_versionid}, /* document RCS id */
315 {"{", c__escaped}, /* escaped lbrace (\{) */
316 {"}", c__escaped}, /* escaped rbrace (\}) */
317 };
318 int i, j, k, c;
319
320 /*
321 * Special cases: \S{0,1,2,...} and \uABCD. If the syntax
322 * doesn't match correctly, we just fall through to the
323 * binary-search phase.
324 */
325 if (tok->text[0] == 'S') {
326 /* We expect numeric characters thereafter. */
327 wchar_t *p = tok->text+1;
328 int n;
329 if (!*p)
330 n = 1;
331 else {
332 n = 0;
333 while (*p && isdec(*p)) {
334 n = 10 * n + fromdec(*p);
335 p++;
336 }
337 }
338 if (!*p) {
339 tok->cmd = c_S;
340 tok->aux = n;
341 return;
342 }
343 } else if (tok->text[0] == 'u') {
344 /* We expect hex characters thereafter. */
345 wchar_t *p = tok->text+1;
346 int n = 0;
347 while (*p && ishex(*p)) {
348 n = 16 * n + fromhex(*p);
349 p++;
350 }
351 if (!*p) {
352 tok->cmd = c_u;
353 tok->aux = n;
354 return;
355 }
356 }
357
358 i = -1;
359 j = sizeof(keywords)/sizeof(*keywords);
360 while (j-i > 1) {
361 k = (i+j)/2;
362 c = kwcmp(tok->text, keywords[k].name);
363 if (c < 0)
364 j = k;
365 else if (c > 0)
366 i = k;
367 else /* c == 0 */ {
368 tok->cmd = keywords[k].id;
369 return;
370 }
371 }
372
373 tok->cmd = c__invalid;
374}
375
376
377/*
378 * Read a token from the input file, in the normal way (`normal' in
379 * the sense that code paragraphs work a different way).
380 */
381token get_token(input *in) {
382 int c;
383 int nls;
e4ea58f8 384 int prevpos;
d7482997 385 token ret;
386 rdstring rs = { 0, 0, NULL };
e4ea58f8 387 rdstringc rsc = { 0, 0, NULL };
d7482997 388 filepos cpos;
389
390 ret.text = NULL; /* default */
e4ea58f8 391 ret.origtext = NULL; /* default */
392 if (in->pushback_chars) {
393 rdaddsc(&rsc, in->pushback_chars);
394 sfree(in->pushback_chars);
395 in->pushback_chars = NULL;
396 }
397 c = get(in, &cpos, &rsc);
d7482997 398 ret.pos = cpos;
399 if (iswhite(c)) { /* tok_white or tok_eop */
400 nls = 0;
e4ea58f8 401 prevpos = 0;
d7482997 402 do {
403 if (isnl(c))
404 nls++;
e4ea58f8 405 prevpos = rsc.pos;
406 } while ((c = get(in, &cpos, &rsc)) != EOF && iswhite(c));
d7482997 407 if (c == EOF) {
408 ret.type = tok_eof;
e4ea58f8 409 sfree(rsc.text);
d7482997 410 return ret;
411 }
e4ea58f8 412 if (rsc.text) {
413 in->pushback_chars = dupstr(rsc.text + prevpos);
414 sfree(rsc.text);
415 }
d7482997 416 unget(in, c, &cpos);
417 ret.type = (nls > 1 ? tok_eop : tok_white);
418 return ret;
419 } else if (c == EOF) { /* tok_eof */
420 ret.type = tok_eof;
e4ea58f8 421 sfree(rsc.text);
d7482997 422 return ret;
423 } else if (c == '\\') { /* tok_cmd */
e4ea58f8 424 rsc.pos = prevpos = 0;
425 c = get(in, &cpos, &rsc);
d7482997 426 if (c == '-' || c == '\\' || c == '_' ||
672e4626 427 c == '#' || c == '{' || c == '}' || c == '.') {
d7482997 428 /* single-char command */
429 rdadd(&rs, c);
126239bf 430 prevpos = rsc.pos;
d7482997 431 } else if (c == 'u') {
432 int len = 0;
433 do {
434 rdadd(&rs, c);
435 len++;
e4ea58f8 436 prevpos = rsc.pos;
437 c = get(in, &cpos, &rsc);
d7482997 438 } while (ishex(c) && len < 5);
439 unget(in, c, &cpos);
440 } else if (iscmd(c)) {
441 do {
442 rdadd(&rs, c);
e4ea58f8 443 prevpos = rsc.pos;
444 c = get(in, &cpos, &rsc);
d7482997 445 } while (iscmd(c));
446 unget(in, c, &cpos);
447 }
448 /*
449 * Now match the command against the list of available
450 * ones.
451 */
452 ret.type = tok_cmd;
453 ret.text = ustrdup(rs.text);
e4ea58f8 454 if (rsc.text) {
455 in->pushback_chars = dupstr(rsc.text + prevpos);
456 rsc.text[prevpos] = '\0';
457 ret.origtext = dupstr(rsc.text);
458 } else {
459 ret.origtext = dupstr("");
460 }
d7482997 461 match_kw(&ret);
462 sfree(rs.text);
e4ea58f8 463 sfree(rsc.text);
d7482997 464 return ret;
465 } else if (c == '{') { /* tok_lbrace */
466 ret.type = tok_lbrace;
e4ea58f8 467 sfree(rsc.text);
d7482997 468 return ret;
469 } else if (c == '}') { /* tok_rbrace */
470 ret.type = tok_rbrace;
e4ea58f8 471 sfree(rsc.text);
d7482997 472 return ret;
473 } else { /* tok_word */
474 /*
475 * Read a word: the longest possible contiguous sequence of
476 * things other than whitespace, backslash, braces and
477 * hyphen. A hyphen terminates the word but is returned as
478 * part of it; everything else is pushed back for the next
479 * token. The `aux' field contains TRUE if the word ends in
480 * a hyphen.
481 */
482 ret.aux = FALSE; /* assumed for now */
e4ea58f8 483 prevpos = 0;
d7482997 484 while (1) {
485 if (iswhite(c) || c=='{' || c=='}' || c=='\\' || c==EOF) {
486 /* Put back the character that caused termination */
487 unget(in, c, &cpos);
488 break;
489 } else {
490 rdadd(&rs, c);
491 if (c == '-') {
e4ea58f8 492 prevpos = rsc.pos;
d7482997 493 ret.aux = TRUE;
494 break; /* hyphen terminates word */
495 }
496 }
e4ea58f8 497 prevpos = rsc.pos;
498 c = get(in, &cpos, &rsc);
d7482997 499 }
500 ret.type = tok_word;
501 ret.text = ustrdup(rs.text);
e4ea58f8 502 if (rsc.text) {
503 in->pushback_chars = dupstr(rsc.text + prevpos);
504 rsc.text[prevpos] = '\0';
505 ret.origtext = dupstr(rsc.text);
506 } else {
507 ret.origtext = dupstr("");
508 }
d7482997 509 sfree(rs.text);
e4ea58f8 510 sfree(rsc.text);
d7482997 511 return ret;
512 }
513}
514
515/*
516 * Determine whether the next input character is an open brace (for
517 * telling code paragraphs from paragraphs which merely start with
518 * code).
519 */
520int isbrace(input *in) {
521 int c;
522 filepos cpos;
523
e4ea58f8 524 c = get(in, &cpos, NULL);
d7482997 525 unget(in, c, &cpos);
526 return (c == '{');
527}
528
529/*
530 * Read the rest of a line that starts `\c'. Including nothing at
531 * all (tok_word with empty text).
532 */
533token get_codepar_token(input *in) {
534 int c;
535 token ret;
536 rdstring rs = { 0, 0, NULL };
537 filepos cpos;
538
539 ret.type = tok_word;
e4ea58f8 540 ret.origtext = NULL;
541 c = get(in, &cpos, NULL); /* expect (and discard) one space */
d7482997 542 ret.pos = cpos;
543 if (c == ' ') {
e4ea58f8 544 c = get(in, &cpos, NULL);
d7482997 545 ret.pos = cpos;
546 }
547 while (!isnl(c) && c != EOF) {
548 int c2 = c;
e4ea58f8 549 c = get(in, &cpos, NULL);
d7482997 550 /* Discard \r just before \n. */
551 if (c2 != 13 || !isnl(c))
552 rdadd(&rs, c2);
553 }
554 unget(in, c, &cpos);
555 ret.text = ustrdup(rs.text);
556 sfree(rs.text);
557 return ret;
558}
559
560/*
561 * Adds a new word to a linked list
562 */
563static word *addword(word newword, word ***hptrptr) {
564 word *mnewword;
565 if (!hptrptr)
566 return NULL;
f1530049 567 mnewword = snew(word);
d7482997 568 *mnewword = newword; /* structure copy */
569 mnewword->next = NULL;
570 **hptrptr = mnewword;
571 *hptrptr = &mnewword->next;
572 return mnewword;
573}
574
575/*
576 * Adds a new paragraph to a linked list
577 */
578static paragraph *addpara(paragraph newpara, paragraph ***hptrptr) {
f1530049 579 paragraph *mnewpara = snew(paragraph);
d7482997 580 *mnewpara = newpara; /* structure copy */
581 mnewpara->next = NULL;
582 **hptrptr = mnewpara;
583 *hptrptr = &mnewpara->next;
584 return mnewpara;
585}
586
587/*
588 * Destructor before token is reassigned; should catch most memory
589 * leaks
590 */
e4ea58f8 591#define dtor(t) ( sfree(t.text), sfree(t.origtext) )
d7482997 592
593/*
594 * Reads a single file (ie until get() returns EOF)
595 */
41848b3e 596static void read_file(paragraph ***ret, input *in, indexdata *idx,
597 tree234 *macros) {
d7482997 598 token t;
599 paragraph par;
600 word wd, **whptr, **idximplicit;
d7482997 601 wchar_t utext[2], *wdtext;
602 int style, spcstyle;
603 int already;
604 int iswhite, seenwhite;
605 int type;
7136a6c7 606 int prev_para_type;
d7482997 607 struct stack_item {
608 enum {
609 stack_nop = 0, /* do nothing (for error recovery) */
610 stack_ualt = 1, /* \u alternative */
611 stack_style = 2, /* \e, \c, \cw */
612 stack_idx = 4, /* \I, \i, \ii */
613 stack_hyper = 8, /* \W */
ee90d1f0 614 stack_quote = 16 /* \q */
d7482997 615 } type;
616 word **whptr; /* to restore from \u alternatives */
617 word **idximplicit; /* to restore from \u alternatives */
f4551933 618 filepos fpos;
6ff15f2b 619 int in_code;
d7482997 620 } *sitem;
621 stack parsestk;
7136a6c7 622 struct crossparaitem {
2614b01d 623 int type; /* currently c_lcont, c_quote or -1 */
624 int seen_lcont, seen_quote;
7136a6c7 625 };
626 stack crossparastk;
d7482997 627 word *indexword, *uword, *iword;
628 word *idxwordlist;
629 rdstring indexstr;
630 int index_downcase, index_visible, indexing;
631 const rdstring nullrs = { 0, 0, NULL };
632 wchar_t uchr;
633
634 t.text = NULL;
e4ea58f8 635 t.origtext = NULL;
b3c80b25 636 already = FALSE;
d7482997 637
7136a6c7 638 crossparastk = stk_new();
639
d7482997 640 /*
641 * Loop on each paragraph.
642 */
643 while (1) {
b3c80b25 644 int start_cmd = c__invalid;
d7482997 645 par.words = NULL;
646 par.keyword = NULL;
e4ea58f8 647 par.origkeyword = NULL;
d7482997 648 whptr = &par.words;
649
650 /*
651 * Get a token.
652 */
7136a6c7 653 do {
654 if (!already) {
655 dtor(t), t = get_token(in);
656 }
657 already = FALSE;
658 } while (t.type == tok_eop);
d7482997 659 if (t.type == tok_eof)
677e18a2 660 break;
d7482997 661
662 /*
663 * Parse code paragraphs separately.
664 */
665 if (t.type == tok_cmd && t.cmd == c_c && !isbrace(in)) {
4b3c5afb 666 int wtype = word_WeakCode;
667
d7482997 668 par.type = para_Code;
669 par.fpos = t.pos;
670 while (1) {
671 dtor(t), t = get_codepar_token(in);
4b3c5afb 672 wd.type = wtype;
d7482997 673 wd.breaks = FALSE; /* shouldn't need this... */
674 wd.text = ustrdup(t.text);
675 wd.alt = NULL;
676 wd.fpos = t.pos;
677 addword(wd, &whptr);
678 dtor(t), t = get_token(in);
679 if (t.type == tok_white) {
680 /*
681 * The newline after a code-paragraph line
682 */
683 dtor(t), t = get_token(in);
684 }
780f45d4 685 if (t.type == tok_eop || t.type == tok_eof ||
686 t.type == tok_rbrace) { /* might be } terminating \lcont */
687 if (t.type == tok_rbrace)
688 already = TRUE;
d7482997 689 break;
780f45d4 690 } else if (t.type == tok_cmd && t.cmd == c_c) {
4b3c5afb 691 wtype = word_WeakCode;
780f45d4 692 } else if (t.type == tok_cmd && t.cmd == c_e &&
693 wtype == word_WeakCode) {
4b3c5afb 694 wtype = word_Emph;
780f45d4 695 } else {
05e9c3c2 696 err_brokencodepara(&t.pos);
7136a6c7 697 prev_para_type = par.type;
d7482997 698 addpara(par, ret);
699 while (t.type != tok_eop) /* error recovery: */
700 dtor(t), t = get_token(in); /* eat rest of paragraph */
701 goto codeparabroken; /* ick, but such is life */
702 }
703 }
7136a6c7 704 prev_para_type = par.type;
d7482997 705 addpara(par, ret);
706 codeparabroken:
707 continue;
708 }
709
710 /*
7136a6c7 711 * Spot the special commands that define a grouping of more
712 * than one paragraph, and also the closing braces that
713 * finish them.
714 */
715 if (t.type == tok_cmd &&
2614b01d 716 (t.cmd == c_lcont || t.cmd == c_quote)) {
7136a6c7 717 struct crossparaitem *sitem, *stop;
2614b01d 718 int cmd = t.cmd;
7136a6c7 719
720 /*
721 * Expect, and swallow, an open brace.
722 */
723 dtor(t), t = get_token(in);
724 if (t.type != tok_lbrace) {
05e9c3c2 725 err_explbr(&t.pos);
7136a6c7 726 continue;
727 }
728
780f45d4 729 /*
730 * Also expect, and swallow, any whitespace after that
731 * (a newline before a code paragraph wouldn't be
732 * surprising).
733 */
734 do {
735 dtor(t), t = get_token(in);
736 } while (t.type == tok_white);
737 already = TRUE;
738
2614b01d 739 if (cmd == c_lcont) {
740 /*
741 * \lcont causes a continuation of a list item into
742 * multiple paragraphs (which may in turn contain
743 * nested lists, code paras etc). Hence, the previous
744 * paragraph must be of a list type.
745 */
f1530049 746 sitem = snew(struct crossparaitem);
2614b01d 747 stop = (struct crossparaitem *)stk_top(crossparastk);
748 if (stop)
749 *sitem = *stop;
750 else
751 sitem->seen_quote = sitem->seen_lcont = 0;
752
753 if (prev_para_type == para_Bullet ||
754 prev_para_type == para_NumberedList ||
755 prev_para_type == para_Description) {
756 sitem->type = c_lcont;
757 sitem->seen_lcont = 1;
758 par.type = para_LcontPush;
759 prev_para_type = par.type;
760 addpara(par, ret);
761 } else {
762 /*
763 * Push a null item on the cross-para stack so that
764 * when we see the corresponding closing brace we
765 * don't give a cascade error.
766 */
767 sitem->type = -1;
05e9c3c2 768 err_misplacedlcont(&t.pos);
2614b01d 769 }
7136a6c7 770 } else {
771 /*
2614b01d 772 * \quote causes a group of paragraphs to be
773 * block-quoted (typically they will be indented a
774 * bit).
7136a6c7 775 */
f1530049 776 sitem = snew(struct crossparaitem);
2614b01d 777 stop = (struct crossparaitem *)stk_top(crossparastk);
778 if (stop)
779 *sitem = *stop;
780 else
781 sitem->seen_quote = sitem->seen_lcont = 0;
782 sitem->type = c_quote;
783 sitem->seen_quote = 1;
784 par.type = para_QuotePush;
785 prev_para_type = par.type;
786 addpara(par, ret);
7136a6c7 787 }
788 stk_push(crossparastk, sitem);
789 continue;
790 } else if (t.type == tok_rbrace) {
791 struct crossparaitem *sitem = stk_pop(crossparastk);
792 if (!sitem)
05e9c3c2 793 err_unexbrace(&t.pos);
7136a6c7 794 else {
795 switch (sitem->type) {
796 case c_lcont:
797 par.type = para_LcontPop;
798 prev_para_type = par.type;
799 addpara(par, ret);
800 break;
2614b01d 801 case c_quote:
802 par.type = para_QuotePop;
803 prev_para_type = par.type;
804 addpara(par, ret);
805 break;
7136a6c7 806 }
807 sfree(sitem);
808 }
809 continue;
810 }
811
41848b3e 812 while (t.type == tok_cmd &&
813 macrolookup(macros, in, t.text, &t.pos)) {
814 dtor(t), t = get_token(in);
815 }
816
7136a6c7 817 /*
d7482997 818 * This token begins a paragraph. See if it's one of the
819 * special commands that define a paragraph type.
820 *
821 * (note that \# is special in a way, and \nocite takes no
822 * text)
823 */
824 par.type = para_Normal;
825 if (t.type == tok_cmd) {
826 int needkw;
827 int is_macro = FALSE;
828
829 par.fpos = t.pos;
830 switch (t.cmd) {
831 default:
832 needkw = -1;
833 break;
834 case c__invalid:
05e9c3c2 835 err_badparatype(t.text, &t.pos);
d7482997 836 needkw = 4;
837 break;
838 case c__comment:
b049296e 839 if (isbrace(in)) {
840 needkw = -1;
d7482997 841 break; /* `\#{': isn't a comment para */
b049296e 842 }
d7482997 843 do {
844 dtor(t), t = get_token(in);
845 } while (t.type != tok_eop && t.type != tok_eof);
846 continue; /* next paragraph */
847 /*
848 * `needkw' values:
849 *
850 * 1 -- exactly one keyword
851 * 2 -- at least one keyword
852 * 4 -- any number of keywords including zero
853 * 8 -- at least one keyword and then nothing else
854 * 16 -- nothing at all! no keywords, no body
855 * 32 -- no keywords at all
856 */
857 case c_A: needkw = 2; par.type = para_Appendix; break;
858 case c_B: needkw = 2; par.type = para_Biblio; break;
b3c80b25 859 case c_BR: needkw = 1; par.type = para_BR;
860 start_cmd = c_BR; break;
d7482997 861 case c_C: needkw = 2; par.type = para_Chapter; break;
862 case c_H: needkw = 2; par.type = para_Heading;
863 par.aux = 0;
864 break;
b3c80b25 865 case c_IM: needkw = 2; par.type = para_IM;
866 start_cmd = c_IM; break;
d7482997 867 case c_S: needkw = 2; par.type = para_Subsect;
868 par.aux = t.aux; break;
869 case c_U: needkw = 32; par.type = para_UnnumberedChapter; break;
870 /* For \b and \n the keyword is optional */
871 case c_b: needkw = 4; par.type = para_Bullet; break;
7136a6c7 872 case c_dt: needkw = 4; par.type = para_DescribedThing; break;
873 case c_dd: needkw = 4; par.type = para_Description; break;
d7482997 874 case c_n: needkw = 4; par.type = para_NumberedList; break;
b3c80b25 875 case c_cfg: needkw = 8; par.type = para_Config;
876 start_cmd = c_cfg; break;
d7482997 877 case c_copyright: needkw = 32; par.type = para_Copyright; break;
878 case c_define: is_macro = TRUE; needkw = 1; break;
879 /* For \nocite the keyword is _everything_ */
880 case c_nocite: needkw = 8; par.type = para_NoCite; break;
8902e0ed 881 case c_preamble: needkw = 32; par.type = para_Normal; break;
d7482997 882 case c_rule: needkw = 16; par.type = para_Rule; break;
883 case c_title: needkw = 32; par.type = para_Title; break;
884 case c_versionid: needkw = 32; par.type = para_VersionID; break;
885 }
886
7136a6c7 887 if (par.type == para_Chapter ||
888 par.type == para_Heading ||
889 par.type == para_Subsect ||
890 par.type == para_Appendix ||
891 par.type == para_UnnumberedChapter) {
892 struct crossparaitem *sitem = stk_top(crossparastk);
2614b01d 893 if (sitem && (sitem->seen_lcont || sitem->seen_quote)) {
05e9c3c2 894 err_sectmarkerinblock( &t.pos,
2614b01d 895 (sitem->seen_lcont ? "lcont" : "quote"));
7136a6c7 896 }
897 }
898
d7482997 899 if (needkw > 0) {
900 rdstring rs = { 0, 0, NULL };
e4ea58f8 901 rdstringc rsc = { 0, 0, NULL };
d7482997 902 int nkeys = 0;
903 filepos fp;
904
905 /* Get keywords. */
906 dtor(t), t = get_token(in);
907 fp = t.pos;
056ab80e 908 while (t.type == tok_lbrace ||
909 (t.type == tok_white && (needkw & 24))) {
910 /*
911 * In paragraph types which can't accept any
912 * body text (such as \cfg), we are lenient
913 * about whitespace between keywords. This is
914 * important for \cfg in particular since it
915 * can often have many keywords which are long
916 * pieces of text, so it's useful to permit the
917 * user to wrap the line between them.
918 */
919 if (t.type == tok_white) {
920 dtor(t), t = get_token(in); /* eat the space */
921 continue;
922 }
d7482997 923 /* This is a keyword. */
924 nkeys++;
925 /* FIXME: there will be bugs if anyone specifies an
926 * empty keyword (\foo{}), so trap this case. */
927 while (dtor(t), t = get_token(in),
928 t.type == tok_word ||
929 t.type == tok_white ||
930 (t.type == tok_cmd && t.cmd == c__nbsp) ||
db662ca1 931 (t.type == tok_cmd && t.cmd == c__escaped) ||
932 (t.type == tok_cmd && t.cmd == c_u)) {
d7482997 933 if (t.type == tok_white ||
e4ea58f8 934 (t.type == tok_cmd && t.cmd == c__nbsp)) {
d7482997 935 rdadd(&rs, ' ');
e4ea58f8 936 rdaddc(&rsc, ' ');
db662ca1 937 } else if (t.type == tok_cmd && t.cmd == c_u) {
938 rdadd(&rs, t.aux);
939 rdaddc(&rsc, '\\');
940 rdaddsc(&rsc, t.origtext);
e4ea58f8 941 } else {
d7482997 942 rdadds(&rs, t.text);
e4ea58f8 943 rdaddsc(&rsc, t.origtext);
944 }
d7482997 945 }
946 if (t.type != tok_rbrace) {
05e9c3c2 947 err_kwunclosed(&t.pos);
d7482997 948 continue;
949 }
950 rdadd(&rs, 0); /* add string terminator */
e4ea58f8 951 rdaddc(&rsc, 0); /* add string terminator */
d7482997 952 dtor(t), t = get_token(in); /* eat right brace */
953 }
954
e4ea58f8 955 rdadd(&rs, 0); /* add string terminator */
956 rdaddc(&rsc, 0); /* add string terminator */
d7482997 957
958 /* See whether we have the right number of keywords. */
959 if ((needkw & 48) && nkeys > 0)
05e9c3c2 960 err_kwillegal(&fp);
d7482997 961 if ((needkw & 11) && nkeys == 0)
05e9c3c2 962 err_kwexpected(&fp);
d7482997 963 if ((needkw & 5) && nkeys > 1)
05e9c3c2 964 err_kwtoomany(&fp);
d7482997 965
966 if (is_macro) {
967 /*
968 * Macro definition. Get the rest of the line
969 * as a code-paragraph token, repeatedly until
970 * there's nothing more left of it. Separate
971 * with newlines.
972 */
973 rdstring macrotext = { 0, 0, NULL };
974 while (1) {
975 dtor(t), t = get_codepar_token(in);
976 if (macrotext.pos > 0)
977 rdadd(&macrotext, L'\n');
978 rdadds(&macrotext, t.text);
979 dtor(t), t = get_token(in);
afd63062 980 if (t.type == tok_eop || t.type == tok_eof)
981 break;
d7482997 982 }
983 macrodef(macros, rs.text, macrotext.text, fp);
984 continue; /* next paragraph */
985 }
986
987 par.keyword = rdtrim(&rs);
e4ea58f8 988 par.origkeyword = rdtrimc(&rsc);
d7482997 989
990 /* Move to EOP in case of needkw==8 or 16 (no body) */
991 if (needkw & 24) {
b3c80b25 992 /* We allow whitespace even when we expect no para body */
993 while (t.type == tok_white)
994 dtor(t), t = get_token(in);
995 if (t.type != tok_eop && t.type != tok_eof &&
996 (start_cmd == c__invalid ||
997 t.type != tok_cmd || t.cmd != start_cmd)) {
05e9c3c2 998 err_bodyillegal(&t.pos);
d7482997 999 /* Error recovery: eat the rest of the paragraph */
b3c80b25 1000 while (t.type != tok_eop && t.type != tok_eof &&
1001 (start_cmd == c__invalid ||
1002 t.type != tok_cmd || t.cmd != start_cmd))
d7482997 1003 dtor(t), t = get_token(in);
1004 }
b3c80b25 1005 if (t.type == tok_cmd)
1006 already = TRUE;/* inhibit get_token at top of loop */
7136a6c7 1007 prev_para_type = par.type;
d7482997 1008 addpara(par, ret);
e34ba5c3 1009
1010 if (par.type == para_Config) {
1011 input_configure(in, &par);
1012 }
d7482997 1013 continue; /* next paragraph */
1014 }
1015 }
1016 }
1017
1018 /*
1019 * Now read the actual paragraph, word by word, adding to
1020 * the paragraph list.
1021 *
1022 * Mid-paragraph commands:
1023 *
1024 * \K \k
b7747050 1025 * \c \cw \cq
d7482997 1026 * \e
1027 * \i \ii
1028 * \I
b7747050 1029 * \q
d7482997 1030 * \u
1031 * \W
1032 * \date
1033 * \\ \{ \}
1034 */
1035 parsestk = stk_new();
1036 style = word_Normal;
1037 spcstyle = word_WhiteSpace;
1038 indexing = FALSE;
1039 seenwhite = TRUE;
1040 while (t.type != tok_eop && t.type != tok_eof) {
1041 iswhite = FALSE;
1042 already = FALSE;
b3c80b25 1043
1044 /* Handle implicit paragraph breaks after \IM, \BR etc */
1045 if (start_cmd != c__invalid &&
1046 t.type == tok_cmd && t.cmd == start_cmd) {
1047 already = TRUE; /* inhibit get_token at top of loop */
1048 break;
1049 }
1050
672e4626 1051 if (t.type == tok_cmd && t.cmd == c__nop) {
1052 dtor(t), t = get_token(in);
1053 continue; /* do nothing! */
1054 }
1055
d7482997 1056 if (t.type == tok_cmd && t.cmd == c__escaped) {
1057 t.type = tok_word; /* nice and simple */
1058 t.aux = 0; /* even if `\-' - nonbreaking! */
1059 }
1060 if (t.type == tok_cmd && t.cmd == c__nbsp) {
1061 t.type = tok_word; /* nice and simple */
1062 sfree(t.text);
1063 t.text = ustrdup(L" "); /* text is ` ' not `_' */
1064 t.aux = 0; /* (nonbreaking) */
1065 }
1066 switch (t.type) {
1067 case tok_white:
1068 if (whptr == &par.words)
1069 break; /* strip whitespace at start of para */
1070 wd.text = NULL;
1071 wd.type = spcstyle;
1072 wd.alt = NULL;
1073 wd.aux = 0;
1074 wd.fpos = t.pos;
1075 wd.breaks = FALSE;
b3c80b25 1076
1077 /*
1078 * Inhibit use of whitespace if it's (probably the
1079 * newline) before a repeat \IM / \BR type
1080 * directive.
1081 */
1082 if (start_cmd != c__invalid) {
1083 dtor(t), t = get_token(in);
1084 already = TRUE;
1085 if (t.type == tok_cmd && t.cmd == start_cmd)
1086 break;
1087 }
1088
d7482997 1089 if (indexing)
1090 rdadd(&indexstr, ' ');
1091 if (!indexing || index_visible)
1092 addword(wd, &whptr);
1093 if (indexing)
1094 addword(wd, &idximplicit);
1095 iswhite = TRUE;
1096 break;
1097 case tok_word:
1098 if (indexing)
1099 rdadds(&indexstr, t.text);
1100 wd.type = style;
1101 wd.alt = NULL;
1102 wd.aux = 0;
1103 wd.fpos = t.pos;
1104 wd.breaks = t.aux;
1105 if (!indexing || index_visible) {
1106 wd.text = ustrdup(t.text);
1107 addword(wd, &whptr);
1108 }
1109 if (indexing) {
1110 wd.text = ustrdup(t.text);
1111 addword(wd, &idximplicit);
1112 }
1113 break;
1114 case tok_lbrace:
05e9c3c2 1115 err_unexbrace(&t.pos);
d7482997 1116 /* Error recovery: push nop */
f1530049 1117 sitem = snew(struct stack_item);
d7482997 1118 sitem->type = stack_nop;
f4551933 1119 sitem->fpos = t.pos;
d7482997 1120 stk_push(parsestk, sitem);
1121 break;
1122 case tok_rbrace:
1123 sitem = stk_pop(parsestk);
7136a6c7 1124 if (!sitem) {
1125 /*
1126 * This closing brace could have been an
1127 * indication that the cross-paragraph stack
1128 * wants popping. Accordingly, we treat it here
1129 * as an indication that the paragraph is over.
1130 */
1131 already = TRUE;
1132 goto finished_para;
1133 } else {
d7482997 1134 if (sitem->type & stack_ualt) {
1135 whptr = sitem->whptr;
1136 idximplicit = sitem->idximplicit;
1137 }
1138 if (sitem->type & stack_style) {
1139 style = word_Normal;
1140 spcstyle = word_WhiteSpace;
1141 }
1142 if (sitem->type & stack_idx) {
1143 indexword->text = ustrdup(indexstr.text);
8b7d993d 1144 if (index_downcase) {
1145 word *w;
1146
d7482997 1147 ustrlow(indexword->text);
8b7d993d 1148 ustrlow(indexstr.text);
1149
1150 for (w = idxwordlist; w; w = w->next)
1151 if (w->text)
1152 ustrlow(w->text);
1153 }
d7482997 1154 indexing = FALSE;
1155 rdadd(&indexstr, L'\0');
f4551933 1156 index_merge(idx, FALSE, indexstr.text,
1157 idxwordlist, &sitem->fpos);
d7482997 1158 sfree(indexstr.text);
1159 }
1160 if (sitem->type & stack_hyper) {
1161 wd.text = NULL;
1162 wd.type = word_HyperEnd;
1163 wd.alt = NULL;
1164 wd.aux = 0;
1165 wd.fpos = t.pos;
1166 wd.breaks = FALSE;
1167 if (!indexing || index_visible)
1168 addword(wd, &whptr);
1169 if (indexing)
1170 addword(wd, &idximplicit);
1171 }
1172 if (sitem->type & stack_quote) {
1173 wd.text = NULL;
1174 wd.type = toquotestyle(style);
1175 wd.alt = NULL;
1176 wd.aux = quote_Close;
1177 wd.fpos = t.pos;
1178 wd.breaks = FALSE;
1179 if (!indexing || index_visible)
1180 addword(wd, &whptr);
1181 if (indexing) {
1182 rdadd(&indexstr, L'"');
1183 addword(wd, &idximplicit);
1184 }
1185 }
1186 }
1187 sfree(sitem);
1188 break;
1189 case tok_cmd:
1190 switch (t.cmd) {
1191 case c__comment:
1192 /*
1193 * In-paragraph comment: \#{ balanced braces }
1194 *
1195 * Anything goes here; even tok_eop. We should
1196 * eat whitespace after the close brace _if_
1197 * there was whitespace before the \#.
1198 */
1199 dtor(t), t = get_token(in);
1200 if (t.type != tok_lbrace) {
05e9c3c2 1201 err_explbr(&t.pos);
d7482997 1202 } else {
1203 int braces = 1;
1204 while (braces > 0) {
1205 dtor(t), t = get_token(in);
1206 if (t.type == tok_lbrace)
1207 braces++;
1208 else if (t.type == tok_rbrace)
1209 braces--;
1210 else if (t.type == tok_eof) {
05e9c3c2 1211 err_commenteof(&t.pos);
d7482997 1212 break;
1213 }
1214 }
1215 }
1216 if (seenwhite) {
1217 already = TRUE;
1218 dtor(t), t = get_token(in);
1219 if (t.type == tok_white) {
1220 iswhite = TRUE;
1221 already = FALSE;
1222 }
1223 }
1224 break;
1225 case c_q:
b7747050 1226 case c_cq:
1227 type = t.cmd;
d7482997 1228 dtor(t), t = get_token(in);
1229 if (t.type != tok_lbrace) {
05e9c3c2 1230 err_explbr(&t.pos);
d7482997 1231 } else {
6ff15f2b 1232 /*
1233 * Enforce that \q may not be used anywhere
1234 * within \c. (It shouldn't be necessary
1235 * since the whole point of \c should be
1236 * that the user wants to exercise exact
1237 * control over the glyphs used, and
1238 * forbidding it has the useful effect of
1239 * relieving some backends of having to
1240 * make difficult decisions.)
1241 */
1242 int stype;
1243
1244 if (style != word_Code && style != word_WeakCode) {
1245 wd.text = NULL;
1246 wd.type = toquotestyle(style);
1247 wd.alt = NULL;
1248 wd.aux = quote_Open;
1249 wd.fpos = t.pos;
1250 wd.breaks = FALSE;
1251 if (!indexing || index_visible)
1252 addword(wd, &whptr);
1253 if (indexing) {
1254 rdadd(&indexstr, L'"');
1255 addword(wd, &idximplicit);
1256 }
1257 stype = stack_quote;
1258 } else {
05e9c3c2 1259 err_codequote(&t.pos);
6ff15f2b 1260 stype = stack_nop;
d7482997 1261 }
f1530049 1262 sitem = snew(struct stack_item);
f4551933 1263 sitem->fpos = t.pos;
6ff15f2b 1264 sitem->type = stype;
b7747050 1265 if (type == c_cq) {
1266 if (style != word_Normal) {
05e9c3c2 1267 err_nestedstyles(&t.pos);
b7747050 1268 } else {
1269 style = word_WeakCode;
1270 spcstyle = tospacestyle(style);
1271 sitem->type |= stack_style;
1272 }
1273 }
d7482997 1274 stk_push(parsestk, sitem);
1275 }
1276 break;
1277 case c_K:
1278 case c_k:
1279 case c_W:
1280 case c_date:
1281 /*
1282 * Keyword, hyperlink, or \date. We expect a
1283 * left brace, some text, and then a right
1284 * brace. No nesting; no arguments.
1285 */
1286 wd.fpos = t.pos;
1287 wd.breaks = FALSE;
1288 if (t.cmd == c_K)
1289 wd.type = word_UpperXref;
1290 else if (t.cmd == c_k)
1291 wd.type = word_LowerXref;
1292 else if (t.cmd == c_W)
1293 wd.type = word_HyperLink;
1294 else
1295 wd.type = word_Normal;
1296 dtor(t), t = get_token(in);
1297 if (t.type != tok_lbrace) {
1298 if (wd.type == word_Normal) {
1299 time_t thetime = time(NULL);
1300 struct tm *broken = localtime(&thetime);
1301 already = TRUE;
1302 wdtext = ustrftime(NULL, broken);
1303 wd.type = style;
1304 } else {
05e9c3c2 1305 err_explbr(&t.pos);
d7482997 1306 wdtext = NULL;
1307 }
1308 } else {
1309 rdstring rs = { 0, 0, NULL };
1310 while (dtor(t), t = get_token(in),
1311 t.type == tok_word || t.type == tok_white) {
1312 if (t.type == tok_white)
1313 rdadd(&rs, ' ');
1314 else
1315 rdadds(&rs, t.text);
1316 }
1317 if (wd.type == word_Normal) {
1318 time_t thetime = time(NULL);
1319 struct tm *broken = localtime(&thetime);
1320 wdtext = ustrftime(rs.text, broken);
1321 wd.type = style;
1322 } else {
1323 wdtext = ustrdup(rs.text);
1324 }
1325 sfree(rs.text);
1326 if (t.type != tok_rbrace) {
05e9c3c2 1327 err_kwexprbr(&t.pos);
d7482997 1328 }
1329 }
1330 wd.alt = NULL;
1331 wd.aux = 0;
1332 if (!indexing || index_visible) {
1333 wd.text = ustrdup(wdtext);
1334 addword(wd, &whptr);
1335 }
1336 if (indexing) {
1337 wd.text = ustrdup(wdtext);
1338 addword(wd, &idximplicit);
1339 }
1340 sfree(wdtext);
1341 if (wd.type == word_HyperLink) {
1342 /*
1343 * Hyperlinks are different: they then
1344 * expect another left brace, to begin
1345 * delimiting the text marked by the link.
1346 */
1347 dtor(t), t = get_token(in);
f1530049 1348 sitem = snew(struct stack_item);
f4551933 1349 sitem->fpos = wd.fpos;
d7482997 1350 sitem->type = stack_hyper;
b80802ba 1351 /*
1352 * Special cases: \W{}\i, \W{}\ii
1353 */
1354 if (t.type == tok_cmd &&
1355 (t.cmd == c_i || t.cmd == c_ii)) {
1356 if (indexing) {
05e9c3c2 1357 err_nestedindex(&t.pos);
b80802ba 1358 } else {
1359 /* Add an index-reference word with no
1360 * text as yet */
1361 wd.type = word_IndexRef;
1362 wd.text = NULL;
1363 wd.alt = NULL;
1364 wd.aux = 0;
1365 wd.breaks = FALSE;
1366 indexword = addword(wd, &whptr);
1367 /* Set up a rdstring to read the
1368 * index text */
1369 indexstr = nullrs;
1370 /* Flags so that we do the Right
1371 * Things with text */
1372 index_visible = (type != c_I);
1373 index_downcase = (type == c_ii);
1374 indexing = TRUE;
1375 idxwordlist = NULL;
1376 idximplicit = &idxwordlist;
1377
1378 sitem->type |= stack_idx;
1379 }
1380 dtor(t), t = get_token(in);
1381 }
1382 /*
1383 * Special cases: \W{}\c, \W{}\e, \W{}\cw
1384 */
d7482997 1385 if (t.type == tok_cmd &&
1386 (t.cmd == c_e || t.cmd == c_c || t.cmd == c_cw)) {
1387 if (style != word_Normal)
05e9c3c2 1388 err_nestedstyles(&t.pos);
d7482997 1389 else {
1390 style = (t.cmd == c_c ? word_Code :
1391 t.cmd == c_cw ? word_WeakCode :
1392 word_Emph);
1393 spcstyle = tospacestyle(style);
1394 sitem->type |= stack_style;
1395 }
1396 dtor(t), t = get_token(in);
1397 }
1398 if (t.type != tok_lbrace) {
05e9c3c2 1399 err_explbr(&t.pos);
d7482997 1400 sfree(sitem);
1401 } else {
1402 stk_push(parsestk, sitem);
1403 }
1404 }
1405 break;
1406 case c_c:
1407 case c_cw:
1408 case c_e:
1409 type = t.cmd;
1410 if (style != word_Normal) {
05e9c3c2 1411 err_nestedstyles(&t.pos);
d7482997 1412 /* Error recovery: eat lbrace, push nop. */
1413 dtor(t), t = get_token(in);
f1530049 1414 sitem = snew(struct stack_item);
f4551933 1415 sitem->fpos = t.pos;
d7482997 1416 sitem->type = stack_nop;
1417 stk_push(parsestk, sitem);
1418 }
1419 dtor(t), t = get_token(in);
1420 if (t.type != tok_lbrace) {
05e9c3c2 1421 err_explbr(&t.pos);
d7482997 1422 } else {
1423 style = (type == c_c ? word_Code :
1424 type == c_cw ? word_WeakCode :
1425 word_Emph);
1426 spcstyle = tospacestyle(style);
f1530049 1427 sitem = snew(struct stack_item);
f4551933 1428 sitem->fpos = t.pos;
d7482997 1429 sitem->type = stack_style;
1430 stk_push(parsestk, sitem);
1431 }
1432 break;
1433 case c_i:
1434 case c_ii:
1435 case c_I:
1436 type = t.cmd;
1437 if (indexing) {
05e9c3c2 1438 err_nestedindex(&t.pos);
d7482997 1439 /* Error recovery: eat lbrace, push nop. */
1440 dtor(t), t = get_token(in);
f1530049 1441 sitem = snew(struct stack_item);
f4551933 1442 sitem->fpos = t.pos;
d7482997 1443 sitem->type = stack_nop;
1444 stk_push(parsestk, sitem);
1445 }
f1530049 1446 sitem = snew(struct stack_item);
f4551933 1447 sitem->fpos = t.pos;
d7482997 1448 sitem->type = stack_idx;
1449 dtor(t), t = get_token(in);
1450 /*
1451 * Special cases: \i\c, \i\e, \i\cw
1452 */
1453 wd.fpos = t.pos;
1454 if (t.type == tok_cmd &&
1455 (t.cmd == c_e || t.cmd == c_c || t.cmd == c_cw)) {
1456 if (style != word_Normal)
05e9c3c2 1457 err_nestedstyles(&t.pos);
d7482997 1458 else {
1459 style = (t.cmd == c_c ? word_Code :
1460 t.cmd == c_cw ? word_WeakCode :
1461 word_Emph);
1462 spcstyle = tospacestyle(style);
1463 sitem->type |= stack_style;
1464 }
1465 dtor(t), t = get_token(in);
1466 }
1467 if (t.type != tok_lbrace) {
1468 sfree(sitem);
05e9c3c2 1469 err_explbr(&t.pos);
d7482997 1470 } else {
1471 /* Add an index-reference word with no text as yet */
1472 wd.type = word_IndexRef;
1473 wd.text = NULL;
1474 wd.alt = NULL;
1475 wd.aux = 0;
1476 wd.breaks = FALSE;
1477 indexword = addword(wd, &whptr);
1478 /* Set up a rdstring to read the index text */
1479 indexstr = nullrs;
1480 /* Flags so that we do the Right Things with text */
1481 index_visible = (type != c_I);
1482 index_downcase = (type == c_ii);
1483 indexing = TRUE;
1484 idxwordlist = NULL;
1485 idximplicit = &idxwordlist;
1486 /* Stack item to close the indexing on exit */
1487 stk_push(parsestk, sitem);
1488 }
1489 break;
1490 case c_u:
1491 uchr = t.aux;
1492 utext[0] = uchr; utext[1] = 0;
1493 wd.type = style;
1494 wd.breaks = FALSE;
1495 wd.alt = NULL;
1496 wd.aux = 0;
1497 wd.fpos = t.pos;
1498 if (!indexing || index_visible) {
1499 wd.text = ustrdup(utext);
1500 uword = addword(wd, &whptr);
1501 } else
1502 uword = NULL;
1503 if (indexing) {
1504 wd.text = ustrdup(utext);
1505 iword = addword(wd, &idximplicit);
1506 } else
1507 iword = NULL;
1508 dtor(t), t = get_token(in);
1509 if (t.type == tok_lbrace) {
1510 /*
1511 * \u with a left brace. Until the brace
1512 * closes, all further words go on a
1513 * sidetrack from the main thread of the
1514 * paragraph.
1515 */
f1530049 1516 sitem = snew(struct stack_item);
f4551933 1517 sitem->fpos = t.pos;
d7482997 1518 sitem->type = stack_ualt;
1519 sitem->whptr = whptr;
1520 sitem->idximplicit = idximplicit;
1521 stk_push(parsestk, sitem);
1522 whptr = uword ? &uword->alt : NULL;
1523 idximplicit = iword ? &iword->alt : NULL;
1524 } else {
1525 if (indexing)
1526 rdadd(&indexstr, uchr);
1527 already = TRUE;
1528 }
1529 break;
1530 default:
1531 if (!macrolookup(macros, in, t.text, &t.pos))
05e9c3c2 1532 err_badmidcmd(t.text, &t.pos);
d7482997 1533 break;
1534 }
1535 }
1536 if (!already)
1537 dtor(t), t = get_token(in);
1538 seenwhite = iswhite;
1539 }
7136a6c7 1540 finished_para:
d7482997 1541 /* Check the stack is empty */
7136a6c7 1542 if (stk_top(parsestk)) {
1543 while ((sitem = stk_pop(parsestk)))
d7482997 1544 sfree(sitem);
05e9c3c2 1545 err_missingrbrace(&t.pos);
d7482997 1546 }
1547 stk_free(parsestk);
7136a6c7 1548 prev_para_type = par.type;
176f9548 1549 /*
1550 * Before we add the paragraph to the output list, we
1551 * should check that there was any text in it at all; there
1552 * might not be if (for example) the paragraph contained
1553 * nothing but an unrecognised command sequence, and if we
1554 * put an empty paragraph on the list it may confuse the
1555 * back ends later on.
1556 */
1557 if (par.words) {
1558 addpara(par, ret);
176f9548 1559 }
7136a6c7 1560 if (t.type == tok_eof)
1561 already = TRUE;
1562 }
1563
1564 if (stk_top(crossparastk)) {
1565 void *p;
1566
05e9c3c2 1567 err_missingrbrace2(&t.pos);
7136a6c7 1568 while ((p = stk_pop(crossparastk)))
1569 sfree(p);
d7482997 1570 }
677e18a2 1571
1572 /*
1573 * We break to here rather than returning, because otherwise
1574 * this cleanup doesn't happen.
1575 */
d7482997 1576 dtor(t);
7136a6c7 1577
1578 stk_free(crossparastk);
d7482997 1579}
1580
f9c20de4 1581struct {
1582 char const *magic;
1583 size_t nmagic;
c437c4c4 1584 int binary;
f9c20de4 1585 void (*reader)(input *);
1586} magics[] = {
c437c4c4 1587 { "%!FontType1-", 12, FALSE, &read_pfa_file },
1588 { "%!PS-AdobeFont-", 15, FALSE, &read_pfa_file },
1589 { "\x80\x01", 2, TRUE, &read_pfb_file },
1590 { "StartFontMetrics", 16, FALSE, &read_afm_file },
1591 { "\x00\x01\x00\x00", 4, TRUE, &read_sfnt_file },
1592 { "true", 4, TRUE, &read_sfnt_file },
f9c20de4 1593};
1594
d7482997 1595paragraph *read_input(input *in, indexdata *idx) {
1596 paragraph *head = NULL;
1597 paragraph **hptr = &head;
41848b3e 1598 tree234 *macros;
f9c20de4 1599 char mag[16];
1600 size_t len, i;
c437c4c4 1601 int binary;
f9c20de4 1602 void (*reader)(input *);
41848b3e 1603
1604 macros = newtree234(macrocmp);
d7482997 1605
1606 while (in->currindex < in->nfiles) {
d26171a6 1607 setpos(in, in->filenames[in->currindex]);
1608 in->charset = in->defcharset;
1609 in->csstate = charset_init_state;
1610 in->wcpos = in->nwc = 0;
1611 in->pushback_chars = NULL;
1612
1613 if (!in->filenames[in->currindex]) {
1614 in->currfp = stdin;
1615 in->wantclose = FALSE; /* don't fclose stdin */
1616 /*
1617 * When reading standard input, we always expect to see
1618 * an actual Halibut file and not any of the unusual
1619 * input types like fonts.
1620 */
f9c20de4 1621 reader = NULL;
d26171a6 1622 } else {
c437c4c4 1623 /*
1624 * Open the file in binary mode to look for magic
1625 * numbers. We'll switch to text mode if we find we're
1626 * looking at a text file type.
1627 */
1628 in->currfp = fopen(in->filenames[in->currindex], "rb");
1629 binary = FALSE; /* default to Halibut source, which is text */
d26171a6 1630 if (in->currfp) {
1631 in->wantclose = TRUE;
1632 reader = NULL;
1633 len = fread(mag, 1, sizeof(mag), in->currfp);
1634 for (i = 0; i < lenof(magics); i++) {
1635 if (len >= magics[i].nmagic &&
1636 memcmp(mag, magics[i].magic, magics[i].nmagic) == 0) {
1637 reader = magics[i].reader;
c437c4c4 1638 binary = magics[i].binary;
d26171a6 1639 break;
1640 }
f9c20de4 1641 }
d26171a6 1642 rewind(in->currfp);
f9c20de4 1643 }
c437c4c4 1644 if (!binary) {
1645 fclose(in->currfp);
1646 in->currfp = fopen(in->filenames[in->currindex], "r");
1647 }
d26171a6 1648 }
1649 if (in->currfp) {
c437c4c4 1650 if (reader == NULL) {
ba0fe3ec 1651 read_file(&hptr, in, idx, macros);
c437c4c4 1652 } else {
f9c20de4 1653 (*reader)(in);
c437c4c4 1654 }
d7482997 1655 }
1656 in->currindex++;
1657 }
1658
41848b3e 1659 macrocleanup(macros);
1660
d7482997 1661 return head;
1662}