1 /* $Id: chilight.c,v 1.41 2001/07/15 15:03:25 ncvs Exp $ */
2
3 /*
4 * Copyright (c) 1995-2001 Sandro Sigala. All rights reserved.
5 * Copyright (c) 2001 Jukka A. Ukkonen <jau@iki.fi>. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <ctype.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33 #include <unistd.h>
34 #include <err.h>
35
36 #include "config.h"
37 #include "tokens.h"
38
39 enum {
40 OPT_FORMAT_ANSI_COLOR,
41 OPT_FORMAT_ANSI_BOLD,
42 OPT_FORMAT_HTML_COLOR,
43 OPT_FORMAT_HTML_FONT,
44 OPT_FORMAT_ROFF,
45 OPT_FORMAT_TTY
46 };
47
48 static int opt_format = OPT_FORMAT_TTY;
49 static char *opt_title = NULL;
50 static unsigned long line_col = 0;
51 static unsigned long opt_tab_width = 8;
52
53 /* From lexer.c */
54 #ifdef YYTEXT_POINTER
55 extern char *yytext;
56 #else
57 extern char yytext[];
58 #endif
59 extern char *token_buffer;
60 extern FILE *yyin;
61 extern int yylex(void);
62 extern void init_lex(void);
63 extern void done_lex(void);
64
65 static FILE *output_file;
66
67 static struct {
68 int defined;
69 char *before_text;
70 char *after_text;
71 char *text;
72 } format[MAX_TOKENS];
73
74 #define MAKE_ANSI_ATTR(color) "\033[" #color "m"
75
76 #define ANSI_ATTR_NORMAL MAKE_ANSI_ATTR(0)
77 #define ANSI_ATTR_BOLD MAKE_ANSI_ATTR(1)
78
79 #define ANSI_ATTR_BLACK MAKE_ANSI_ATTR(30)
80 #define ANSI_ATTR_RED MAKE_ANSI_ATTR(31)
81 #define ANSI_ATTR_GREEN MAKE_ANSI_ATTR(32)
82 #define ANSI_ATTR_BROWN MAKE_ANSI_ATTR(33)
83 #define ANSI_ATTR_BLUE MAKE_ANSI_ATTR(34)
84 #define ANSI_ATTR_MAGENTA MAKE_ANSI_ATTR(35)
85 #define ANSI_ATTR_CYAN MAKE_ANSI_ATTR(36)
86 #define ANSI_ATTR_WHITE MAKE_ANSI_ATTR(37)
87
88 #define ENTRY(tk, b, t, a) \
89 format[tk].defined = 1; \
90 format[tk].before_text = b; \
91 format[tk].after_text = a; \
92 format[tk].text = t
93
94 static int new_line = 0;
95
register_format(void)96 static void register_format(void)
97 {
98 switch (opt_format) {
99 case OPT_FORMAT_ANSI_COLOR:
100 ENTRY(ALL, ANSI_ATTR_NORMAL, NULL, NULL);
101 ENTRY(IDENTIFIER, ANSI_ATTR_NORMAL, NULL, NULL);
102 ENTRY(CONSTANT, ANSI_ATTR_CYAN, NULL, NULL);
103 ENTRY(KEYWORD, ANSI_ATTR_MAGENTA, NULL, NULL);
104 ENTRY(TYPE, ANSI_ATTR_MAGENTA, NULL, NULL);
105 ENTRY(COMMENT, ANSI_ATTR_RED, NULL, NULL);
106 ENTRY(DIRECTIVE, ANSI_ATTR_BLUE, NULL, NULL);
107 ENTRY(STRING, ANSI_ATTR_GREEN, NULL, NULL);
108 ENTRY(CHARACTER, ANSI_ATTR_GREEN, NULL, NULL);
109 break;
110 case OPT_FORMAT_ANSI_BOLD:
111 ENTRY(ALL, ANSI_ATTR_NORMAL, NULL, NULL);
112 ENTRY(KEYWORD, ANSI_ATTR_BOLD, NULL, NULL);
113 ENTRY(TYPE, ANSI_ATTR_BOLD, NULL, NULL);
114 break;
115 case OPT_FORMAT_HTML_COLOR:
116 ENTRY(KEYWORD, "<b><font color=black>", NULL, "</font></b>");
117 ENTRY(TYPE, "<font color=\"#b682b4\">", NULL, "</font>");
118 ENTRY(COMMENT, "<font color=\"#00008b\">", NULL, "</font>");
119 ENTRY(DIRECTIVE, "<font color=\"#0000cd\">", NULL, "</font>");
120 ENTRY(STRING, "<font color=\"#8b8b00\">", NULL, "</font>");
121 ENTRY(CHARACTER, "<font color=\"#8b8b00\">", NULL, "</font>");
122 break;
123 case OPT_FORMAT_HTML_FONT:
124 ENTRY(KEYWORD, "<b>", NULL, "</b>");
125 ENTRY(TYPE, "<b>", NULL, "</b>");
126 ENTRY(COMMENT, "<i>", NULL, "</i>");
127 ENTRY(DIRECTIVE, "<i>", NULL, "</i>");
128 break;
129 case OPT_FORMAT_ROFF:
130 ENTRY(STRING, "\\fI", NULL, "\\fP");
131 ENTRY(CHARACTER, "\\fI", NULL, "\\fP");
132 ENTRY(KEYWORD, "\\fB", NULL, "\\fP");
133 ENTRY(TYPE, "\\fB", NULL, "\\fP");
134 ENTRY(COMMENT, "\\f(NR", NULL, "\\fP");
135 ENTRY(DIRECTIVE, "\\fB", NULL, "\\fP");
136 break;
137 case OPT_FORMAT_TTY:
138 ENTRY(STRING, "I", NULL, NULL);
139 ENTRY(CHARACTER, "I", NULL, NULL);
140 ENTRY(KEYWORD, "B", NULL, NULL);
141 ENTRY(TYPE, "B", NULL, NULL);
142 ENTRY(COMMENT, "I", NULL, NULL);
143 ENTRY(DIRECTIVE, "B", NULL, NULL);
144 break;
145 }
146 }
147
148 static char *filter_buf;
149 static int max_buf;
150
next_tab(unsigned long col)151 static unsigned long next_tab(unsigned long col)
152 {
153 return col + (opt_tab_width - col % opt_tab_width);
154 }
155
fputs_backtick(const char * s,FILE * stream)156 static void fputs_backtick(const char *s, FILE *stream)
157 {
158 unsigned i;
159 unsigned long tab_pos;
160 unsigned long tab_len;
161
162 for (; *s != '\0'; ++s)
163 switch (*s) {
164 case '\t':
165 tab_pos = next_tab(line_col);
166 tab_len = tab_pos - line_col;
167 for (i = 0; i < tab_len; ++i)
168 fputc(' ', stream);
169 line_col = tab_pos;
170 break;
171 case '\\':
172 fputs("\\\\", stream);
173 ++line_col;
174 break;
175 case '\n':
176 case '\r':
177 case '\f':
178 fputc(*s, stream);
179 line_col = 0;
180 break;
181 default:
182 fputc(*s, stream);
183 ++line_col;
184 }
185 }
186
fputs_bold(const char * s,FILE * stream)187 static void fputs_bold(const char *s, FILE *stream)
188 {
189 for (; *s != '\0'; ++s) {
190 if (!isspace(*s)) {
191 fputc(*s, stream);
192 fputc('\b', stream);
193 }
194 fputc(*s, stream);
195 }
196 }
197
fputs_emphasis(const char * s,FILE * stream)198 static void fputs_emphasis(const char *s, FILE *stream)
199 {
200 for (; *s != '\0'; ++s) {
201 if (!isspace(*s)) {
202 fputc('_', stream);
203 fputc('\b', stream);
204 }
205 fputc(*s, stream);
206 }
207 }
208
extend_filter_buf(char * p)209 static char *extend_filter_buf(char *p)
210 {
211 int offset = p - filter_buf;
212
213 max_buf = max_buf * 2 + 10;
214 filter_buf = (char *)xrealloc(filter_buf, max_buf + 2);
215
216 return filter_buf + offset;
217 }
218
filter_html_markups(char * s)219 static char *filter_html_markups(char *s)
220 {
221 char *p;
222
223 if (filter_buf == NULL) {
224 max_buf = strlen(s) + 10;
225 filter_buf = (char *)xmalloc(max_buf);
226 }
227 p = filter_buf;
228
229 for (; *s != '\0'; ++s) {
230 if (p >= filter_buf + max_buf)
231 p = extend_filter_buf(p);
232 switch (*s) {
233 case '<':
234 *p++ = '&';
235 *p++ = 'l';
236 *p++ = 't';
237 *p++ = ';';
238 break;
239 case '>':
240 *p++ = '&';
241 *p++ = 'g';
242 *p++ = 't';
243 *p++ = ';';
244 break;
245 default:
246 *p++ = *s;
247 }
248 }
249 *p = '\0';
250
251 return filter_buf;
252 }
253
process_token(int token,char * buf)254 static void process_token(int token, char *buf)
255 {
256 static int last_token = -1;
257
258 if ((opt_format == OPT_FORMAT_HTML_COLOR ||
259 opt_format == OPT_FORMAT_HTML_FONT) && token != ALL)
260 buf = filter_html_markups(buf);
261 if (opt_format == OPT_FORMAT_ROFF && new_line) {
262 fputs("\\&", output_file);
263 new_line = 0;
264 }
265
266 if (opt_format == OPT_FORMAT_TTY) {
267 if (!format[token].defined)
268 fputs(format[token].text != NULL ?
269 format[token].text : buf, output_file);
270 else if (format[token].before_text) {
271 if (format[token].before_text[0] == 'I')
272 fputs_emphasis(format[token].text != NULL ?
273 format[token].text : buf,
274 output_file);
275 else if (format[token].before_text[0] == 'B')
276 fputs_bold(format[token].text != NULL ?
277 format[token].text : buf,
278 output_file);
279 else
280 fputs(format[token].text != NULL ?
281 format[token].text : buf, output_file);
282 }
283 } else if (format[token].defined || token == ALL) {
284 if (last_token != -1 && last_token != token &&
285 format[last_token].after_text != NULL)
286 fputs(format[last_token].after_text, output_file);
287
288 if (last_token != token && format[token].before_text != NULL)
289 fputs(format[token].before_text, output_file);
290
291 if (opt_format == OPT_FORMAT_ROFF)
292 fputs_backtick(format[token].text != NULL ?
293 format[token].text : buf, output_file);
294 else
295 fputs(format[token].text != NULL ?
296 format[token].text : buf, output_file);
297
298 last_token = token;
299 } else
300 process_token(ALL, buf);
301
302 new_line = (token == '\n') ? 1 : 0;
303 }
304
parse(void)305 static void parse(void)
306 {
307 int tk;
308
309 while ((tk = yylex()) != 0)
310 switch (tk) {
311 case COMMENT:
312 case DIRECTIVE:
313 case STRING:
314 process_token(tk, token_buffer);
315 break;
316 default:
317 process_token(tk, yytext);
318 break;
319 }
320 }
321
process_file(char * filename)322 static void process_file(char *filename)
323 {
324 if (filename != NULL && strcmp(filename, "-") != 0) {
325 if ((yyin = fopen(filename, "r")) == NULL)
326 err(1, "%s", filename);
327 } else
328 yyin = stdin;
329
330 new_line = 1;
331 line_col = 0;
332
333 init_lex();
334 parse();
335 done_lex();
336
337 if (yyin != stdin)
338 fclose(yyin);
339 }
340
341 /*
342 * Output the program syntax then exit.
343 */
usage(void)344 static void usage(void)
345 {
346 fprintf(stderr, "\
347 usage: chilight [-V] [-f format] [-o file] [-t title] [-w width] [file ...]\n\
348 \n\
349 Format can be one of:\n\
350 ansi_color, ansi_bold, html_color, html_font, roff, tty.\n");
351 exit(1);
352 }
353
354 /*
355 * Used by the err() functions.
356 */
357 char *progname;
358
main(int argc,char ** argv)359 int main(int argc, char **argv)
360 {
361 int c;
362
363 progname = argv[0];
364 output_file = stdout;
365
366 while ((c = getopt(argc, argv, "f:o:t:w:V")) != -1)
367 switch (c) {
368 case 'f':
369 if (!strcmp(optarg, "ansi_color"))
370 opt_format = OPT_FORMAT_ANSI_COLOR;
371 else if (!strcmp(optarg, "ansi_bold"))
372 opt_format = OPT_FORMAT_ANSI_BOLD;
373 else if (!strcmp(optarg, "html_color"))
374 opt_format = OPT_FORMAT_HTML_COLOR;
375 else if (!strcmp(optarg, "html_font"))
376 opt_format = OPT_FORMAT_HTML_FONT;
377 else if (!strcmp(optarg, "roff"))
378 opt_format = OPT_FORMAT_ROFF;
379 else if (!strcmp(optarg, "tty"))
380 opt_format = OPT_FORMAT_TTY;
381 else
382 errx(1, "invalid format `%s'", optarg);
383 break;
384 case 'o':
385 if (output_file != stdout)
386 fclose(output_file);
387 if ((output_file = fopen(optarg, "w")) == NULL)
388 err(1, "%s", optarg);
389 break;
390 case 't':
391 opt_title = optarg;
392 break;
393 case 'w':
394 opt_tab_width = atoi(optarg);
395 if (opt_tab_width < 2 || opt_tab_width > 16)
396 errx(1, "invalid tab width `%s'", optarg);
397 break;
398 case 'V':
399 fprintf(stderr, "%s\n", CUTILS_VERSION);
400 exit(0);
401 case '?':
402 default:
403 usage();
404 /* NOTREACHED */
405 }
406 argc -= optind;
407 argv += optind;
408
409 register_format();
410
411 switch (opt_format) {
412 case OPT_FORMAT_HTML_COLOR:
413 case OPT_FORMAT_HTML_FONT:
414 fprintf(output_file, "<html>\n"
415 "<head><title>%s</title></head>\n"
416 "<body>\n"
417 "<pre>\n", opt_title != NULL ? opt_title : "");
418 break;
419 case OPT_FORMAT_ROFF:
420 fprintf(output_file, ".ft C\n.nf\n");
421 break;
422 }
423
424 if (argc < 1)
425 process_file(NULL);
426 else
427 while (*argv)
428 process_file(*argv++);
429
430 switch (opt_format) {
431 case OPT_FORMAT_HTML_COLOR:
432 case OPT_FORMAT_HTML_FONT: {
433 time_t t = time(NULL);
434 fprintf(output_file, "</pre>\n"
435 "<hr>\n"
436 "Generated by <a href=\"http://www.sigala.it/sandro/\">" CUTILS_VERSION "</a> chilight - %s"
437 "</body>\n", ctime(&t));
438 break;
439 }
440 case OPT_FORMAT_ANSI_COLOR:
441 fprintf(output_file, "\033[0m");
442 break;
443 case OPT_FORMAT_ROFF:
444 fprintf(output_file, ".fi\n");
445 break;
446 }
447
448 if (output_file != stdout)
449 fclose(output_file);
450
451 return 0;
452 }
453