Bug in utoi(), which made it ignore a leading minus sign when
[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;
62a4b06b 46 int list_fonts;
675958c3 47 int input_charset;
d7482997 48 int debug;
43341922 49 int backendbits, prebackbits;
c8fb54d2 50 int k, b;
6a0b9d08 51 paragraph *cfg, *cfg_tail;
43341922 52 void *pre_backend_data[16];
d7482997 53
740a7d6a 54 /*
55 * Use the specified locale everywhere. It'll be used for
56 * output of error messages, and as the default character set
57 * for input files if one is not explicitly specified.
58 *
59 * However, we need to use standard numeric formatting for
60 * output of things like PDF.
61 */
7e976207 62 setlocale(LC_ALL, "");
740a7d6a 63 setlocale(LC_NUMERIC, "C");
7e976207 64
d7482997 65 /*
66 * Set up initial (default) parameters.
67 */
f1530049 68 infiles = snewn(argc, char *);
d7482997 69 nfiles = 0;
70 nogo = errs = FALSE;
71 reportcols = 0;
62a4b06b 72 list_fonts = 0;
675958c3 73 input_charset = CS_ASCII;
d7482997 74 debug = 0;
c8fb54d2 75 backendbits = 0;
6a0b9d08 76 cfg = cfg_tail = NULL;
d7482997 77
78 if (argc == 1) {
79 usage();
80 exit(EXIT_SUCCESS);
81 }
82
83 /*
84 * Parse command line arguments.
85 */
86 while (--argc) {
87 char *p = *++argv;
d26171a6 88 if (*p == '-' && p[1]) {
d7482997 89 /*
90 * An option.
91 */
92 while (p && *++p) {
93 char c = *p;
94 switch (c) {
95 case '-':
96 /*
97 * Long option.
98 */
99 {
100 char *opt, *val;
101 opt = p++; /* opt will have _one_ leading - */
102 while (*p && *p != '=')
103 p++; /* find end of option */
104 if (*p == '=') {
105 *p++ = '\0';
106 val = p;
107 } else
108 val = NULL;
c8fb54d2 109
110 assert(opt[0] == '-');
111 for (k = 0; k < (int)lenof(backends); k++)
112 if (!strcmp(opt+1, backends[k].name)) {
113 backendbits |= backends[k].bitfield;
ba9c1487 114 if (val) {
115 paragraph *p = backends[k].filename(val);
116 assert(p);
117 if (cfg_tail)
118 cfg_tail->next = p;
119 else
120 cfg = p;
121 while (p->next)
122 p = p->next;
123 cfg_tail = p;
124 }
c8fb54d2 125 break;
126 }
127 if (k < (int)lenof(backends)) {
128 /* do nothing */;
675958c3 129 } else if (!strcmp(opt, "-input-charset")) {
130 if (!val) {
131 errs = TRUE, error(err_optnoarg, opt);
132 } else {
133 int charset = charset_from_localenc(val);
134 if (charset == CS_NONE) {
135 errs = TRUE, error(err_cmdcharset, val);
136 } else {
137 input_charset = charset;
138 }
139 }
c8fb54d2 140 } else if (!strcmp(opt, "-help")) {
d7482997 141 help();
142 nogo = TRUE;
143 } else if (!strcmp(opt, "-version")) {
144 showversion();
145 nogo = TRUE;
146 } else if (!strcmp(opt, "-licence") ||
147 !strcmp(opt, "-license")) {
148 licence();
149 nogo = TRUE;
f336fa9a 150 } else if (!strcmp(opt, "-list-charsets")) {
151 listcharsets();
152 nogo = TRUE;
62a4b06b 153 } else if (!strcmp(opt, "-list-fonts")) {
154 list_fonts = TRUE;
d7482997 155 } else if (!strcmp(opt, "-precise")) {
156 reportcols = 1;
157 } else {
158 errs = TRUE, error(err_nosuchopt, opt);
159 }
160 }
161 p = NULL;
162 break;
163 case 'h':
164 case 'V':
165 case 'L':
166 case 'P':
167 case 'd':
168 /*
169 * Option requiring no parameter.
170 */
171 switch (c) {
172 case 'h':
173 help();
174 nogo = TRUE;
175 break;
176 case 'V':
177 showversion();
178 nogo = TRUE;
179 break;
180 case 'L':
181 licence();
182 nogo = TRUE;
183 break;
184 case 'P':
185 reportcols = 1;
186 break;
187 case 'd':
188 debug = TRUE;
189 break;
190 }
191 break;
6a0b9d08 192 case 'C':
d7482997 193 /*
194 * Option requiring parameter.
195 */
196 p++;
197 if (!*p && argc > 1)
198 --argc, p = *++argv;
199 else if (!*p) {
200 char opt[2];
201 opt[0] = c;
202 opt[1] = '\0';
203 errs = TRUE, error(err_optnoarg, opt);
204 }
205 /*
206 * Now c is the option and p is the parameter.
207 */
208 switch (c) {
6a0b9d08 209 case 'C':
210 /*
211 * -C means we split our argument up into
212 * colon-separated chunks and assemble them
213 * into a config paragraph.
214 */
215 {
e4ea58f8 216 char *s = dupstr(p), *q, *r;
6a0b9d08 217 paragraph *para;
218
e4ea58f8 219 para = cmdline_cfg_new();
6a0b9d08 220
e4ea58f8 221 q = r = s;
6a0b9d08 222 while (*q) {
223 if (*q == ':') {
e4ea58f8 224 *r = '\0';
675958c3 225 /* XXX ad-hoc diagnostic */
226 if (!strcmp(s, "input-charset"))
227 error(err_futileopt, "Cinput-charset",
228 "; use --input-charset");
e4ea58f8 229 cmdline_cfg_add(para, s);
230 r = s;
6a0b9d08 231 } else {
232 if (*q == '\\' && q[1])
233 q++;
e4ea58f8 234 *r++ = *q;
6a0b9d08 235 }
236 q++;
237 }
57e17355 238 *r = '\0';
e4ea58f8 239 cmdline_cfg_add(para, s);
6a0b9d08 240
241 if (cfg_tail)
242 cfg_tail->next = para;
243 else
244 cfg = para;
245 cfg_tail = para;
246 }
d7482997 247 break;
248 }
249 p = NULL; /* prevent continued processing */
250 break;
251 default:
252 /*
253 * Unrecognised option.
254 */
255 {
256 char opt[2];
257 opt[0] = c;
258 opt[1] = '\0';
259 errs = TRUE, error(err_nosuchopt, opt);
260 }
261 }
262 }
263 } else {
264 /*
265 * A non-option argument.
266 */
d26171a6 267 if (!strcmp(p, "-"))
268 infiles[nfiles++] = NULL; /* special case: read stdin */
269 else
270 infiles[nfiles++] = p;
d7482997 271 }
272 }
273
274 if (errs)
275 exit(EXIT_FAILURE);
276 if (nogo)
277 exit(EXIT_SUCCESS);
278
279 /*
280 * Do the work.
281 */
62a4b06b 282 if (nfiles == 0 && !list_fonts) {
d7482997 283 error(err_noinput);
284 usage();
285 exit(EXIT_FAILURE);
286 }
287
288 {
289 input in;
290 paragraph *sourceform, *p;
291 indexdata *idx;
292 keywordlist *keywords;
293
294 in.filenames = infiles;
295 in.nfiles = nfiles;
296 in.currfp = NULL;
297 in.currindex = 0;
298 in.npushback = in.pushbacksize = 0;
299 in.pushback = NULL;
300 in.reportcols = reportcols;
301 in.stack = NULL;
675958c3 302 in.defcharset = input_charset;
d7482997 303
304 idx = make_index();
305
306 sourceform = read_input(&in, idx);
62a4b06b 307 if (list_fonts) {
308 listfonts();
309 exit(EXIT_SUCCESS);
310 }
d7482997 311 if (!sourceform)
312 exit(EXIT_FAILURE);
313
6a0b9d08 314 /*
315 * Append the config directives acquired from the command
316 * line.
317 */
318 {
319 paragraph *end;
320
321 end = sourceform;
322 while (end && end->next)
323 end = end->next;
324 assert(end);
325
326 end->next = cfg;
327 }
328
d7482997 329 sfree(in.pushback);
330
d7482997 331 sfree(infiles);
332
333 keywords = get_keywords(sourceform);
334 if (!keywords)
335 exit(EXIT_FAILURE);
336 gen_citations(sourceform, keywords);
337 subst_keywords(sourceform, keywords);
338
339 for (p = sourceform; p; p = p->next)
340 if (p->type == para_IM)
f4551933 341 index_merge(idx, TRUE, p->keyword, p->words, &p->fpos);
d7482997 342
343 build_index(idx);
344
bb9e7835 345 /*
346 * Set up attr_First / attr_Last / attr_Always, in the main
347 * document and in the index entries.
348 */
349 for (p = sourceform; p; p = p->next)
350 mark_attr_ends(p->words);
351 {
352 int i;
353 indexentry *entry;
354
355 for (i = 0; (entry = index234(idx->entries, i)) != NULL; i++)
356 mark_attr_ends(entry->text);
357 }
358
d7482997 359 if (debug) {
360 index_debug(idx);
361 dbg_prtkws(keywords);
362 dbg_prtsource(sourceform);
363 }
364
c8fb54d2 365 /*
43341922 366 * Select and run the pre-backends.
367 */
368 prebackbits = 0;
369 for (k = 0; k < (int)lenof(backends); k++)
370 if (backendbits == 0 || (backendbits & backends[k].bitfield))
371 prebackbits |= backends[k].prebackend_bitfield;
372 for (k = 0; k < (int)lenof(pre_backends); k++)
373 if (prebackbits & pre_backends[k].bitfield) {
374 assert(k < (int)lenof(pre_backend_data));
375 pre_backend_data[k] =
376 pre_backends[k].func(sourceform, keywords, idx);
377 }
378
379 /*
c8fb54d2 380 * Run the selected set of backends.
381 */
382 for (k = b = 0; k < (int)lenof(backends); k++)
383 if (b != backends[k].bitfield) {
384 b = backends[k].bitfield;
43341922 385 if (backendbits == 0 || (backendbits & b)) {
386 void *pbd = NULL;
387 int pbb = backends[k].prebackend_bitfield;
388 int m;
389
390 for (m = 0; m < (int)lenof(pre_backends); m++)
391 if (pbb & pre_backends[m].bitfield) {
392 assert(m < (int)lenof(pre_backend_data));
393 pbd = pre_backend_data[m];
394 break;
395 }
396
397 backends[k].func(sourceform, keywords, idx, pbd);
398 }
c8fb54d2 399 }
d7482997 400
401 free_para_list(sourceform);
402 free_keywords(keywords);
403 cleanup_index(idx);
404 }
405
406 return 0;
407}
408
409static void dbg_prtsource(paragraph *sourceform) {
410 /*
411 * Output source form in debugging format.
412 */
413
414 paragraph *p;
415 for (p = sourceform; p; p = p->next) {
416 wchar_t *wp;
417 printf("para %d ", p->type);
418 if (p->keyword) {
419 wp = p->keyword;
420 while (*wp) {
421 putchar('\"');
422 for (; *wp; wp++)
423 putchar(*wp);
424 putchar('\"');
425 if (*++wp)
426 printf(", ");
427 }
428 } else
429 printf("(no keyword)");
430 printf(" {\n");
431 dbg_prtwordlist(1, p->words);
432 printf("}\n");
433 }
434}
435
436static void dbg_prtkws(keywordlist *kws) {
437 /*
438 * Output keywords in debugging format.
439 */
440
441 int i;
442 keyword *kw;
443
444 for (i = 0; (kw = index234(kws->keys, i)) != NULL; i++) {
445 wchar_t *wp;
446 printf("keyword ");
447 wp = kw->key;
448 while (*wp) {
449 putchar('\"');
450 for (; *wp; wp++)
451 putchar(*wp);
452 putchar('\"');
453 if (*++wp)
454 printf(", ");
455 }
456 printf(" {\n");
457 dbg_prtwordlist(1, kw->text);
458 printf("}\n");
459 }
460}
461
462static void dbg_prtwordlist(int level, word *w) {
463 for (; w; w = w->next) {
464 wchar_t *wp;
465 printf("%*sword %d ", level*4, "", w->type);
466 if (w->text) {
467 printf("\"");
468 for (wp = w->text; *wp; wp++)
469 putchar(*wp);
470 printf("\"");
471 } else
472 printf("(no text)");
14b072e2 473 if (w->breaks)
474 printf(" [breaks]");
d7482997 475 if (w->alt) {
476 printf(" alt = {\n");
477 dbg_prtwordlist(level+1, w->alt);
478 printf("%*s}", level*4, "");
479 }
480 printf("\n");
481 }
482}