Initial work on PS and PDF output. Because these two backends share
[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 backendbits |= backends[k].bitfield;
98 if (val) {
99 paragraph *p = backends[k].filename(val);
100 assert(p);
101 if (cfg_tail)
102 cfg_tail->next = p;
103 else
104 cfg = p;
105 while (p->next)
106 p = p->next;
107 cfg_tail = p;
108 }
109 break;
110 }
111 if (k < (int)lenof(backends)) {
112 /* do nothing */;
113 } else if (!strcmp(opt, "-help")) {
114 help();
115 nogo = TRUE;
116 } else if (!strcmp(opt, "-version")) {
117 showversion();
118 nogo = TRUE;
119 } else if (!strcmp(opt, "-licence") ||
120 !strcmp(opt, "-license")) {
121 licence();
122 nogo = TRUE;
123 } else if (!strcmp(opt, "-precise")) {
124 reportcols = 1;
125 } else {
126 errs = TRUE, error(err_nosuchopt, opt);
127 }
128 }
129 p = NULL;
130 break;
131 case 'h':
132 case 'V':
133 case 'L':
134 case 'P':
135 case 'd':
136 /*
137 * Option requiring no parameter.
138 */
139 switch (c) {
140 case 'h':
141 help();
142 nogo = TRUE;
143 break;
144 case 'V':
145 showversion();
146 nogo = TRUE;
147 break;
148 case 'L':
149 licence();
150 nogo = TRUE;
151 break;
152 case 'P':
153 reportcols = 1;
154 break;
155 case 'd':
156 debug = TRUE;
157 break;
158 }
159 break;
160 case 'C':
161 /*
162 * Option requiring parameter.
163 */
164 p++;
165 if (!*p && argc > 1)
166 --argc, p = *++argv;
167 else if (!*p) {
168 char opt[2];
169 opt[0] = c;
170 opt[1] = '\0';
171 errs = TRUE, error(err_optnoarg, opt);
172 }
173 /*
174 * Now c is the option and p is the parameter.
175 */
176 switch (c) {
177 case 'C':
178 /*
179 * -C means we split our argument up into
180 * colon-separated chunks and assemble them
181 * into a config paragraph.
182 */
183 {
184 wchar_t *keywords;
185 char *q;
186 wchar_t *u;
187 paragraph *para;
188
189 keywords = mknewa(wchar_t, 2+strlen(p));
190
191 u = keywords;
192 q = p;
193
194 while (*q) {
195 if (*q == ':') {
196 *u++ = L'\0';
197 } else {
198 if (*q == '\\' && q[1])
199 q++;
200 /* FIXME: lacks charset flexibility */
201 *u++ = *q;
202 }
203 q++;
204 }
205 *u = L'\0';
206
207 para = mknew(paragraph);
208 memset(para, 0, sizeof(*para));
209 para->type = para_Config;
210 para->keyword = keywords;
211 para->next = NULL;
212 para->fpos.filename = "<command line>";
213 para->fpos.line = para->fpos.col = -1;
214
215 if (cfg_tail)
216 cfg_tail->next = para;
217 else
218 cfg = para;
219 cfg_tail = para;
220 }
221 break;
222 }
223 p = NULL; /* prevent continued processing */
224 break;
225 default:
226 /*
227 * Unrecognised option.
228 */
229 {
230 char opt[2];
231 opt[0] = c;
232 opt[1] = '\0';
233 errs = TRUE, error(err_nosuchopt, opt);
234 }
235 }
236 }
237 } else {
238 /*
239 * A non-option argument.
240 */
241 infiles[nfiles++] = p;
242 }
243 }
244
245 if (errs)
246 exit(EXIT_FAILURE);
247 if (nogo)
248 exit(EXIT_SUCCESS);
249
250 /*
251 * Do the work.
252 */
253 if (nfiles == 0) {
254 error(err_noinput);
255 usage();
256 exit(EXIT_FAILURE);
257 }
258
259 {
260 input in;
261 paragraph *sourceform, *p;
262 indexdata *idx;
263 keywordlist *keywords;
264
265 in.filenames = infiles;
266 in.nfiles = nfiles;
267 in.currfp = NULL;
268 in.currindex = 0;
269 in.npushback = in.pushbacksize = 0;
270 in.pushback = NULL;
271 in.reportcols = reportcols;
272 in.stack = NULL;
273
274 idx = make_index();
275
276 sourceform = read_input(&in, idx);
277 if (!sourceform)
278 exit(EXIT_FAILURE);
279
280 /*
281 * Append the config directives acquired from the command
282 * line.
283 */
284 {
285 paragraph *end;
286
287 end = sourceform;
288 while (end && end->next)
289 end = end->next;
290 assert(end);
291
292 end->next = cfg;
293 }
294
295 sfree(in.pushback);
296
297 mark_attr_ends(sourceform);
298
299 sfree(infiles);
300
301 keywords = get_keywords(sourceform);
302 if (!keywords)
303 exit(EXIT_FAILURE);
304 gen_citations(sourceform, keywords);
305 subst_keywords(sourceform, keywords);
306
307 for (p = sourceform; p; p = p->next)
308 if (p->type == para_IM)
309 index_merge(idx, TRUE, p->keyword, p->words, &p->fpos);
310
311 build_index(idx);
312
313 if (debug) {
314 index_debug(idx);
315 dbg_prtkws(keywords);
316 dbg_prtsource(sourceform);
317 }
318
319 /*
320 * Select and run the pre-backends.
321 */
322 prebackbits = 0;
323 for (k = 0; k < (int)lenof(backends); k++)
324 if (backendbits == 0 || (backendbits & backends[k].bitfield))
325 prebackbits |= backends[k].prebackend_bitfield;
326 for (k = 0; k < (int)lenof(pre_backends); k++)
327 if (prebackbits & pre_backends[k].bitfield) {
328 assert(k < (int)lenof(pre_backend_data));
329 pre_backend_data[k] =
330 pre_backends[k].func(sourceform, keywords, idx);
331 }
332
333 /*
334 * Run the selected set of backends.
335 */
336 for (k = b = 0; k < (int)lenof(backends); k++)
337 if (b != backends[k].bitfield) {
338 b = backends[k].bitfield;
339 if (backendbits == 0 || (backendbits & b)) {
340 void *pbd = NULL;
341 int pbb = backends[k].prebackend_bitfield;
342 int m;
343
344 for (m = 0; m < (int)lenof(pre_backends); m++)
345 if (pbb & pre_backends[m].bitfield) {
346 assert(m < (int)lenof(pre_backend_data));
347 pbd = pre_backend_data[m];
348 break;
349 }
350
351 backends[k].func(sourceform, keywords, idx, pbd);
352 }
353 }
354
355 free_para_list(sourceform);
356 free_keywords(keywords);
357 cleanup_index(idx);
358 }
359
360 return 0;
361 }
362
363 static void dbg_prtsource(paragraph *sourceform) {
364 /*
365 * Output source form in debugging format.
366 */
367
368 paragraph *p;
369 for (p = sourceform; p; p = p->next) {
370 wchar_t *wp;
371 printf("para %d ", p->type);
372 if (p->keyword) {
373 wp = p->keyword;
374 while (*wp) {
375 putchar('\"');
376 for (; *wp; wp++)
377 putchar(*wp);
378 putchar('\"');
379 if (*++wp)
380 printf(", ");
381 }
382 } else
383 printf("(no keyword)");
384 printf(" {\n");
385 dbg_prtwordlist(1, p->words);
386 printf("}\n");
387 }
388 }
389
390 static void dbg_prtkws(keywordlist *kws) {
391 /*
392 * Output keywords in debugging format.
393 */
394
395 int i;
396 keyword *kw;
397
398 for (i = 0; (kw = index234(kws->keys, i)) != NULL; i++) {
399 wchar_t *wp;
400 printf("keyword ");
401 wp = kw->key;
402 while (*wp) {
403 putchar('\"');
404 for (; *wp; wp++)
405 putchar(*wp);
406 putchar('\"');
407 if (*++wp)
408 printf(", ");
409 }
410 printf(" {\n");
411 dbg_prtwordlist(1, kw->text);
412 printf("}\n");
413 }
414 }
415
416 static void dbg_prtwordlist(int level, word *w) {
417 for (; w; w = w->next) {
418 wchar_t *wp;
419 printf("%*sword %d ", level*4, "", w->type);
420 if (w->text) {
421 printf("\"");
422 for (wp = w->text; *wp; wp++)
423 putchar(*wp);
424 printf("\"");
425 } else
426 printf("(no text)");
427 if (w->alt) {
428 printf(" alt = {\n");
429 dbg_prtwordlist(level+1, w->alt);
430 printf("%*s}", level*4, "");
431 }
432 printf("\n");
433 }
434 }