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