Shouldn't consider para_Title to be a heading when going through
[sgt/halibut] / main.c
CommitLineData
d7482997 1/*
2 * main.c: command line parsing and top level
3 */
4
c8fb54d2 5#include <assert.h>
7e976207 6#include <locale.h>
d7482997 7#include <stdio.h>
8#include <stdlib.h>
9#include "halibut.h"
10
11static void dbg_prtsource(paragraph *sourceform);
12static void dbg_prtwordlist(int level, word *w);
13static void dbg_prtkws(keywordlist *kws);
14
43341922 15static const struct pre_backend {
16 void *(*func)(paragraph *, keywordlist *, indexdata *);
17 int bitfield;
18} pre_backends[] = {
19 {paper_pre_backend, 0x0001}
20};
21
c8fb54d2 22static const struct backend {
23 char *name;
43341922 24 void (*func)(paragraph *, keywordlist *, indexdata *, void *);
ba9c1487 25 paragraph *(*filename)(char *filename);
43341922 26 int bitfield, prebackend_bitfield;
c8fb54d2 27} backends[] = {
43341922 28 {"text", text_backend, text_config_filename, 0x0001, 0},
78c73085 29 {"xhtml", html_backend, html_config_filename, 0x0002, 0},
30 {"html", html_backend, html_config_filename, 0x0002, 0},
43341922 31 {"hlp", whlp_backend, whlp_config_filename, 0x0004, 0},
32 {"whlp", whlp_backend, whlp_config_filename, 0x0004, 0},
33 {"winhelp", whlp_backend, whlp_config_filename, 0x0004, 0},
34 {"man", man_backend, man_config_filename, 0x0008, 0},
35 {"info", info_backend, info_config_filename, 0x0010, 0},
36 {"ps", ps_backend, ps_config_filename, 0x0020, 0x0001},
37 {"pdf", pdf_backend, pdf_config_filename, 0x0040, 0x0001},
c8fb54d2 38};
39
d7482997 40int main(int argc, char **argv) {
41 char **infiles;
d7482997 42 int nfiles;
43 int nogo;
44 int errs;
45 int reportcols;
46 int debug;
43341922 47 int backendbits, prebackbits;
c8fb54d2 48 int k, b;
6a0b9d08 49 paragraph *cfg, *cfg_tail;
43341922 50 void *pre_backend_data[16];
d7482997 51
7e976207 52 setlocale(LC_ALL, "");
53
d7482997 54 /*
55 * Set up initial (default) parameters.
56 */
f1530049 57 infiles = snewn(argc, char *);
d7482997 58 nfiles = 0;
59 nogo = errs = FALSE;
60 reportcols = 0;
61 debug = 0;
c8fb54d2 62 backendbits = 0;
6a0b9d08 63 cfg = cfg_tail = NULL;
d7482997 64
65 if (argc == 1) {
66 usage();
67 exit(EXIT_SUCCESS);
68 }
69
70 /*
71 * Parse command line arguments.
72 */
73 while (--argc) {
74 char *p = *++argv;
75 if (*p == '-') {
76 /*
77 * An option.
78 */
79 while (p && *++p) {
80 char c = *p;
81 switch (c) {
82 case '-':
83 /*
84 * Long option.
85 */
86 {
87 char *opt, *val;
88 opt = p++; /* opt will have _one_ leading - */
89 while (*p && *p != '=')
90 p++; /* find end of option */
91 if (*p == '=') {
92 *p++ = '\0';
93 val = p;
94 } else
95 val = NULL;
c8fb54d2 96
97 assert(opt[0] == '-');
98 for (k = 0; k < (int)lenof(backends); k++)
99 if (!strcmp(opt+1, backends[k].name)) {
100 backendbits |= backends[k].bitfield;
ba9c1487 101 if (val) {
102 paragraph *p = backends[k].filename(val);
103 assert(p);
104 if (cfg_tail)
105 cfg_tail->next = p;
106 else
107 cfg = p;
108 while (p->next)
109 p = p->next;
110 cfg_tail = p;
111 }
c8fb54d2 112 break;
113 }
114 if (k < (int)lenof(backends)) {
115 /* do nothing */;
116 } else if (!strcmp(opt, "-help")) {
d7482997 117 help();
118 nogo = TRUE;
119 } else if (!strcmp(opt, "-version")) {
120 showversion();
121 nogo = TRUE;
122 } else if (!strcmp(opt, "-licence") ||
123 !strcmp(opt, "-license")) {
124 licence();
125 nogo = TRUE;
d7482997 126 } else if (!strcmp(opt, "-precise")) {
127 reportcols = 1;
128 } else {
129 errs = TRUE, error(err_nosuchopt, opt);
130 }
131 }
132 p = NULL;
133 break;
134 case 'h':
135 case 'V':
136 case 'L':
137 case 'P':
138 case 'd':
139 /*
140 * Option requiring no parameter.
141 */
142 switch (c) {
143 case 'h':
144 help();
145 nogo = TRUE;
146 break;
147 case 'V':
148 showversion();
149 nogo = TRUE;
150 break;
151 case 'L':
152 licence();
153 nogo = TRUE;
154 break;
155 case 'P':
156 reportcols = 1;
157 break;
158 case 'd':
159 debug = TRUE;
160 break;
161 }
162 break;
6a0b9d08 163 case 'C':
d7482997 164 /*
165 * Option requiring parameter.
166 */
167 p++;
168 if (!*p && argc > 1)
169 --argc, p = *++argv;
170 else if (!*p) {
171 char opt[2];
172 opt[0] = c;
173 opt[1] = '\0';
174 errs = TRUE, error(err_optnoarg, opt);
175 }
176 /*
177 * Now c is the option and p is the parameter.
178 */
179 switch (c) {
6a0b9d08 180 case 'C':
181 /*
182 * -C means we split our argument up into
183 * colon-separated chunks and assemble them
184 * into a config paragraph.
185 */
186 {
e4ea58f8 187 char *s = dupstr(p), *q, *r;
6a0b9d08 188 paragraph *para;
189
e4ea58f8 190 para = cmdline_cfg_new();
6a0b9d08 191
e4ea58f8 192 q = r = s;
6a0b9d08 193 while (*q) {
194 if (*q == ':') {
e4ea58f8 195 *r = '\0';
196 cmdline_cfg_add(para, s);
197 r = s;
6a0b9d08 198 } else {
199 if (*q == '\\' && q[1])
200 q++;
e4ea58f8 201 *r++ = *q;
6a0b9d08 202 }
203 q++;
204 }
57e17355 205 *r = '\0';
e4ea58f8 206 cmdline_cfg_add(para, s);
6a0b9d08 207
208 if (cfg_tail)
209 cfg_tail->next = para;
210 else
211 cfg = para;
212 cfg_tail = para;
213 }
d7482997 214 break;
215 }
216 p = NULL; /* prevent continued processing */
217 break;
218 default:
219 /*
220 * Unrecognised option.
221 */
222 {
223 char opt[2];
224 opt[0] = c;
225 opt[1] = '\0';
226 errs = TRUE, error(err_nosuchopt, opt);
227 }
228 }
229 }
230 } else {
231 /*
232 * A non-option argument.
233 */
234 infiles[nfiles++] = p;
235 }
236 }
237
238 if (errs)
239 exit(EXIT_FAILURE);
240 if (nogo)
241 exit(EXIT_SUCCESS);
242
243 /*
244 * Do the work.
245 */
246 if (nfiles == 0) {
247 error(err_noinput);
248 usage();
249 exit(EXIT_FAILURE);
250 }
251
252 {
253 input in;
254 paragraph *sourceform, *p;
255 indexdata *idx;
256 keywordlist *keywords;
257
258 in.filenames = infiles;
259 in.nfiles = nfiles;
260 in.currfp = NULL;
261 in.currindex = 0;
262 in.npushback = in.pushbacksize = 0;
263 in.pushback = NULL;
264 in.reportcols = reportcols;
265 in.stack = NULL;
e5cd393f 266 in.defcharset = charset_from_locale();
d7482997 267
268 idx = make_index();
269
270 sourceform = read_input(&in, idx);
271 if (!sourceform)
272 exit(EXIT_FAILURE);
273
6a0b9d08 274 /*
275 * Append the config directives acquired from the command
276 * line.
277 */
278 {
279 paragraph *end;
280
281 end = sourceform;
282 while (end && end->next)
283 end = end->next;
284 assert(end);
285
286 end->next = cfg;
287 }
288
d7482997 289 sfree(in.pushback);
290
d7482997 291 sfree(infiles);
292
293 keywords = get_keywords(sourceform);
294 if (!keywords)
295 exit(EXIT_FAILURE);
296 gen_citations(sourceform, keywords);
297 subst_keywords(sourceform, keywords);
298
299 for (p = sourceform; p; p = p->next)
300 if (p->type == para_IM)
f4551933 301 index_merge(idx, TRUE, p->keyword, p->words, &p->fpos);
d7482997 302
303 build_index(idx);
304
bb9e7835 305 /*
306 * Set up attr_First / attr_Last / attr_Always, in the main
307 * document and in the index entries.
308 */
309 for (p = sourceform; p; p = p->next)
310 mark_attr_ends(p->words);
311 {
312 int i;
313 indexentry *entry;
314
315 for (i = 0; (entry = index234(idx->entries, i)) != NULL; i++)
316 mark_attr_ends(entry->text);
317 }
318
d7482997 319 if (debug) {
320 index_debug(idx);
321 dbg_prtkws(keywords);
322 dbg_prtsource(sourceform);
323 }
324
c8fb54d2 325 /*
43341922 326 * Select and run the pre-backends.
327 */
328 prebackbits = 0;
329 for (k = 0; k < (int)lenof(backends); k++)
330 if (backendbits == 0 || (backendbits & backends[k].bitfield))
331 prebackbits |= backends[k].prebackend_bitfield;
332 for (k = 0; k < (int)lenof(pre_backends); k++)
333 if (prebackbits & pre_backends[k].bitfield) {
334 assert(k < (int)lenof(pre_backend_data));
335 pre_backend_data[k] =
336 pre_backends[k].func(sourceform, keywords, idx);
337 }
338
339 /*
c8fb54d2 340 * Run the selected set of backends.
341 */
342 for (k = b = 0; k < (int)lenof(backends); k++)
343 if (b != backends[k].bitfield) {
344 b = backends[k].bitfield;
43341922 345 if (backendbits == 0 || (backendbits & b)) {
346 void *pbd = NULL;
347 int pbb = backends[k].prebackend_bitfield;
348 int m;
349
350 for (m = 0; m < (int)lenof(pre_backends); m++)
351 if (pbb & pre_backends[m].bitfield) {
352 assert(m < (int)lenof(pre_backend_data));
353 pbd = pre_backend_data[m];
354 break;
355 }
356
357 backends[k].func(sourceform, keywords, idx, pbd);
358 }
c8fb54d2 359 }
d7482997 360
361 free_para_list(sourceform);
362 free_keywords(keywords);
363 cleanup_index(idx);
364 }
365
366 return 0;
367}
368
369static void dbg_prtsource(paragraph *sourceform) {
370 /*
371 * Output source form in debugging format.
372 */
373
374 paragraph *p;
375 for (p = sourceform; p; p = p->next) {
376 wchar_t *wp;
377 printf("para %d ", p->type);
378 if (p->keyword) {
379 wp = p->keyword;
380 while (*wp) {
381 putchar('\"');
382 for (; *wp; wp++)
383 putchar(*wp);
384 putchar('\"');
385 if (*++wp)
386 printf(", ");
387 }
388 } else
389 printf("(no keyword)");
390 printf(" {\n");
391 dbg_prtwordlist(1, p->words);
392 printf("}\n");
393 }
394}
395
396static void dbg_prtkws(keywordlist *kws) {
397 /*
398 * Output keywords in debugging format.
399 */
400
401 int i;
402 keyword *kw;
403
404 for (i = 0; (kw = index234(kws->keys, i)) != NULL; i++) {
405 wchar_t *wp;
406 printf("keyword ");
407 wp = kw->key;
408 while (*wp) {
409 putchar('\"');
410 for (; *wp; wp++)
411 putchar(*wp);
412 putchar('\"');
413 if (*++wp)
414 printf(", ");
415 }
416 printf(" {\n");
417 dbg_prtwordlist(1, kw->text);
418 printf("}\n");
419 }
420}
421
422static void dbg_prtwordlist(int level, word *w) {
423 for (; w; w = w->next) {
424 wchar_t *wp;
425 printf("%*sword %d ", level*4, "", w->type);
426 if (w->text) {
427 printf("\"");
428 for (wp = w->text; *wp; wp++)
429 putchar(*wp);
430 printf("\"");
431 } else
432 printf("(no text)");
433 if (w->alt) {
434 printf(" alt = {\n");
435 dbg_prtwordlist(level+1, w->alt);
436 printf("%*s}", level*4, "");
437 }
438 printf("\n");
439 }
440}