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