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