1 /*
2  * Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3  *		2006, 2008, 2010, 2011
4  *	Tama Communications Corporation
5  *
6  * This file is part of GNU GLOBAL.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdio.h>
26 #ifdef STDC_HEADERS
27 #include <stdlib.h>
28 #endif
29 #include <ctype.h>
30 
31 #include "global.h"
32 #include "anchor.h"
33 #include "cache.h"
34 #include "common.h"
35 #include "incop.h"
36 #include "path2url.h"
37 #include "htags.h"
38 
39 /*----------------------------------------------------------------------*/
40 /* Parser switch							*/
41 /*----------------------------------------------------------------------*/
42 /**
43  * This is the linkage section of each parsers.
44  * If you want to support new language, you must define two procedures:
45  *	1. Initializing procedure (init_proc).
46  *		Called once first with an input file descripter.
47  *	2. Executing procedure (exec_proc).
48  *		Called repeatedly until returning EOF.
49  *		It should read from above descripter and write HTML
50  *		using output procedures in this module.
51  */
52 struct lang_entry {
53 	const char *lang_name;
54 	void (*init_proc)(FILE *);		/**< initializing procedure */
55 	int (*exec_proc)(void);			/**< executing procedure */
56 };
57 
58 /*
59  * initializing procedures
60  */
61 void c_parser_init(FILE *);
62 void yacc_parser_init(FILE *);
63 void cpp_parser_init(FILE *);
64 void java_parser_init(FILE *);
65 void php_parser_init(FILE *);
66 void asm_parser_init(FILE *);
67 
68 /*
69  * executing procedures
70  */
71 int c_lex(void);
72 int cpp_lex(void);
73 int java_lex(void);
74 int php_lex(void);
75 int asm_lex(void);
76 
77 /**
78  * The first entry is default language.
79  */
80 struct lang_entry lang_switch[] = {
81 	/* lang_name	init_proc 		exec_proc */
82 	{"c",		c_parser_init,		c_lex},		/* DEFAULT */
83 	{"yacc",	yacc_parser_init,	c_lex},
84 	{"cpp",		cpp_parser_init,	cpp_lex},
85 	{"java",	java_parser_init,	java_lex},
86 	{"php",		php_parser_init,	php_lex},
87 	{"asm",		asm_parser_init,	asm_lex}
88 };
89 #define DEFAULT_ENTRY &lang_switch[0]
90 
91 /**
92  * get language entry.
93  *
94  * If the specified language (lang) is not found, it assumes the
95  * default language, which is C.
96  *
97  *	@param[in]	lang	language name (NULL means 'not specified'.)
98  *	@return		language entry
99  */
100 static struct lang_entry *
get_lang_entry(const char * lang)101 get_lang_entry(const char *lang)
102 {
103 	int i, size = sizeof(lang_switch) / sizeof(struct lang_entry);
104 
105 	/*
106 	 * if language not specified, it assumes default language.
107 	 */
108 	if (lang == NULL)
109 		return DEFAULT_ENTRY;
110 	for (i = 0; i < size; i++)
111 		if (!strcmp(lang, lang_switch[i].lang_name))
112 			return &lang_switch[i];
113 	/*
114 	 * if specified language not found, it assumes default language.
115 	 */
116 	return DEFAULT_ENTRY;
117 }
118 /*----------------------------------------------------------------------*/
119 /* Input/Output								*/
120 /*----------------------------------------------------------------------*/
121 /*
122  * Input/Output descriptor.
123  */
124 static FILEOP *fileop_out;
125 static FILEOP *fileop_in;
126 static FILE *out;
127 static FILE *in;
128 
129 STATIC_STRBUF(outbuf);
130 static const char *curpfile;
131 static int warned;
132 static int last_lineno;
133 
134 /*
135  * Put a character to HTML as is.
136  *
137  * [Note] You should use this function to put a control character.
138  *
139  * [Note] No escaping of '<', '>' and '&' is performed.
140  *
141  * See put_char()
142  */
143 void
echoc(int c)144 echoc(int c)
145 {
146         strbuf_putc(outbuf, c);
147 }
148 /*
149  * Put a string to HTML as is.
150  *
151  * [Note] You should use this function to put a control sequence.
152  *
153  * [Note] No escaping of '<', '>' and '&' is performed.
154  *
155  * See put_string()
156  */
157 void
echos(const char * s)158 echos(const char *s)
159 {
160         strbuf_puts(outbuf, s);
161 }
162 /*----------------------------------------------------------------------*/
163 /* HTML output								*/
164 /*----------------------------------------------------------------------*/
165 /*
166  * Quote character with HTML's way.
167  *
168  * (Fixes '<', '>' and '&' for HTML).
169  */
170 static const char *
HTML_quoting(int c)171 HTML_quoting(int c)
172 {
173 	if (c == '<')
174 		return quote_little;
175 	else if (c == '>')
176 		return quote_great;
177 	else if (c == '&')
178 		return quote_amp;
179 	return NULL;
180 }
181 /*
182  * fill_anchor: fill anchor into file name
183  *
184  *       @param[in]      root   $root or index page
185  *       @param[in]      path   $path name
186  *       @return              hypertext file name string
187  */
188 const char *
fill_anchor(const char * root,const char * path)189 fill_anchor(const char *root, const char *path)
190 {
191 	STATIC_STRBUF(sb);
192 	char buf[MAXBUFLEN], *limit, *p;
193 
194 	strbuf_clear(sb);
195 	strlimcpy(buf, path, sizeof(buf));
196 	for (p = buf; *p; p++)
197 		if (*p == sep)
198 			*p = '\0';
199 	limit = p;
200 
201 	if (root != NULL)
202 		strbuf_sprintf(sb, "%sroot%s/", gen_href_begin_simple(root), gen_href_end());
203 	for (p = buf; p < limit; p += strlen(p) + 1) {
204 		const char *path = buf;
205 		const char *unit = p;
206 		const char *next = p + strlen(p) + 1;
207 
208 		if (next > limit) {
209 			strbuf_puts(sb, unit);
210 			break;
211 		}
212 		if (p > buf)
213 			*(p - 1) = sep;
214 		strbuf_puts(sb, gen_href_begin("../files", path2fid(path), HTML, NULL));
215 		strbuf_puts(sb, unit);
216 		strbuf_puts(sb, gen_href_end());
217 		strbuf_putc(sb, '/');
218 	}
219         return strbuf_value(sb);
220 }
221 
222 /**
223  * link_format: make hypertext from anchor array.
224  *
225  *	@param[in]	ref	(previous, next, first, last, top, bottom)
226  *		-1: top, -2: bottom, other: line number
227  *	@return	HTML
228  */
229 const char *
link_format(int ref[A_SIZE])230 link_format(int ref[A_SIZE])
231 {
232 	STATIC_STRBUF(sb);
233 	const char **label = Iflag ? anchor_comment : anchor_label;
234 	const char **icons = anchor_icons;
235 	int i;
236 
237 	strbuf_clear(sb);
238 	for (i = 0; i < A_LIMIT; i++) {
239 		if (i == A_INDEX) {
240 			strbuf_puts(sb, gen_href_begin("..", "mains", normal_suffix, NULL));
241 		} else if (i == A_HELP) {
242 			strbuf_puts(sb, gen_href_begin("..", "help", normal_suffix, NULL));
243 		} else if (ref[i]) {
244 			char tmp[32], *key = tmp;
245 
246 			if (ref[i] == -1)
247 				key = "TOP";
248 			else if (ref[i] == -2)
249 				key = "BOTTOM";
250 			else
251 				snprintf(tmp, sizeof(tmp), "%d", ref[i]);
252 			strbuf_puts(sb, gen_href_begin(NULL, NULL, NULL, key));
253 		}
254 		if (Iflag) {
255 			char tmp[MAXPATHLEN];
256 			snprintf(tmp, sizeof(tmp), "%s%s", (i != A_INDEX && i != A_HELP && ref[i] == 0) ? "n_" : "", icons[i]);
257 			strbuf_puts(sb, gen_image(PARENT, tmp, label[i]));
258 		} else {
259 			strbuf_sprintf(sb, "[%s]", label[i]);
260 		}
261 		if (i == A_INDEX || i == A_HELP || ref[i] != 0)
262 			strbuf_puts(sb, gen_href_end());
263 	}
264         return strbuf_value(sb);
265 }
266 /**
267  * fixed_guide_link_format: make fixed guide
268  *
269  *	@param[in]	ref	(previous, next, first, last, top, bottom)
270  *		-1: top, -2: bottom, other: line number
271  *	@param[in]	anchors
272  *	@return	HTML
273  */
274 const char *
fixed_guide_link_format(int ref[A_LIMIT],const char * anchors)275 fixed_guide_link_format(int ref[A_LIMIT], const char *anchors)
276 {
277 	int i = 0;
278 	STATIC_STRBUF(sb);
279 
280 	strbuf_clear(sb);
281 	strbuf_puts(sb, "<!-- beginning of fixed guide -->\n");
282 	strbuf_puts(sb, guide_begin);
283 	strbuf_putc(sb, '\n');
284 	for (i = 0; i < A_LIMIT; i++) {
285 		if (i == A_PREV || i == A_NEXT)
286 			continue;
287 		strbuf_puts(sb, guide_unit_begin);
288 		switch (i) {
289 		case A_FIRST:
290 		case A_LAST:
291 			if (ref[i] == 0)
292 				strbuf_puts(sb, gen_href_begin(NULL, NULL, NULL, (i == A_FIRST) ? "TOP" : "BOTTOM"));
293 			else {
294 				char lineno[32];
295 				snprintf(lineno, sizeof(lineno), "%d", ref[i]);
296 				strbuf_puts(sb, gen_href_begin(NULL, NULL, NULL, lineno));
297 			}
298 			break;
299 		case A_TOP:
300 			strbuf_puts(sb, gen_href_begin(NULL, NULL, NULL, "TOP"));
301 			break;
302 		case A_BOTTOM:
303 			strbuf_puts(sb, gen_href_begin(NULL, NULL, NULL, "BOTTOM"));
304 			break;
305 		case A_INDEX:
306 			strbuf_puts(sb, gen_href_begin("..", "mains", normal_suffix, NULL));
307 			break;
308 		case A_HELP:
309 			strbuf_puts(sb, gen_href_begin("..", "help", normal_suffix, NULL));
310 			break;
311 		default:
312 			die("fixed_guide_link_format: something is wrong.(%d)", i);
313 			break;
314 		}
315 		if (Iflag)
316 			strbuf_puts(sb, gen_image(PARENT, anchor_icons[i], anchor_label[i]));
317 		else
318 			strbuf_sprintf(sb, "[%s]", anchor_label[i]);
319 		strbuf_puts(sb, gen_href_end());
320 		strbuf_puts(sb, guide_unit_end);
321 		strbuf_putc(sb, '\n');
322 	}
323 	strbuf_puts(sb, guide_path_begin);
324 	strbuf_puts(sb, anchors);
325 	strbuf_puts(sb, guide_path_end);
326 	strbuf_putc(sb, '\n');
327 	strbuf_puts(sb, guide_end);
328 	strbuf_putc(sb, '\n');
329 	strbuf_puts(sb, "<!-- end of fixed guide -->\n");
330 
331 	return strbuf_value(sb);
332 }
333 /**
334  * generate_guide: generate guide string for definition line.
335  *
336  *	@param[in]	lineno	line number
337  *	@return		guide string
338  */
339 const char *
generate_guide(int lineno)340 generate_guide(int lineno)
341 {
342 	STATIC_STRBUF(sb);
343 	int i = 0;
344 
345 	strbuf_clear(sb);
346 	if (definition_header == RIGHT_HEADER)
347 		i = 4;
348 	else if (nflag)
349 		i = ncol + 1;
350 	if (i > 0)
351 		for (; i > 0; i--)
352 			strbuf_putc(sb, ' ');
353 	strbuf_sprintf(sb, "%s/* ", comment_begin);
354 	strbuf_puts(sb, link_format(anchor_getlinks(lineno)));
355 	if (show_position) {
356 		strbuf_sprintf(sb, "%s%s value='+%d %s' %s",
357 			quote_space, position_begin, lineno, curpfile, position_end);
358 	}
359 	strbuf_sprintf(sb, " */%s", comment_end);
360 
361 	return strbuf_value(sb);
362 }
363 /**
364  * tooltip: generate tooltip string
365  *
366  *	@param[in]	type	'I': 'Included from',
367  *			'R': 'Defined at',
368  *			'Y': 'Used at',
369  *			'D', 'M': 'Referred from'
370  *	@param[in]	lno	line number
371  *	@param[in]	opt
372  *	@return		tooltip string
373  */
374 const char *
tooltip(int type,int lno,const char * opt)375 tooltip(int type, int lno, const char *opt)
376 {
377 	STATIC_STRBUF(sb);
378 
379 	strbuf_clear(sb);
380 	if (lno > 0) {
381 		if (type == 'I')
382 			strbuf_puts(sb, "Included from");
383 		else if (type == 'R')
384 			strbuf_puts(sb, "Defined at");
385 		else if (type == 'Y')
386 			strbuf_puts(sb, "Used at");
387 		else
388 			strbuf_puts(sb, "Referred from");
389 		strbuf_putc(sb, ' ');
390 		strbuf_putn(sb, lno);
391 		if (opt) {
392 			strbuf_puts(sb, " in ");
393 			strbuf_puts(sb, opt);
394 		}
395 	} else {
396 		strbuf_puts(sb, "Multiple ");
397 		if (type == 'I')
398 			strbuf_puts(sb, "included from");
399 		else if (type == 'R')
400 			strbuf_puts(sb, "defined in");
401 		else if (type == 'Y')
402 			strbuf_puts(sb, "used in");
403 		else
404 			strbuf_puts(sb, "referred from");
405 		strbuf_putc(sb, ' ');
406 		strbuf_puts(sb, opt);
407 		strbuf_putc(sb, ' ');
408 		strbuf_puts(sb, "places");
409 	}
410 	strbuf_putc(sb, '.');
411 	return strbuf_value(sb);
412 }
413 /**
414  * put_anchor: output HTML anchor.
415  *
416  *	@param[in]	name	tag
417  *	@param[in]	type	tag type. 'R': GTAGS,
418  *			'Y': GSYMS,
419  *			'D', 'M', 'T': GRTAGS
420  *	@param[in]	lineno	current line no
421  */
422 void
put_anchor(char * name,int type,int lineno)423 put_anchor(char *name, int type, int lineno)
424 {
425 	const char *line;
426 	int db;
427 
428 	if (type == 'R')
429 		db = GTAGS;
430 	else if (type == 'Y')
431 		db = GSYMS;
432 	else	/* 'D', 'M' or 'T' */
433 		db = GRTAGS;
434 	line = cache_get(db, name);
435 	if (line == NULL) {
436 		if ((type == 'R' || type == 'Y') && wflag) {
437 			warning("%s %d %s(%c) found but not defined.",
438 				curpfile, lineno, name, type);
439 			if (colorize_warned_line)
440 				warned = 1;
441 		}
442 		strbuf_puts(outbuf, name);
443 	} else {
444 		/*
445 		 * About the format of 'line', please see the head comment of cache.c.
446 		 */
447 		if (*line == ' ') {
448 			const char *fid = line + 1;
449 			const char *count = nextstring(fid);
450 			const char *dir, *file, *suffix = NULL;
451 
452 			if (dynamic) {
453 				STATIC_STRBUF(sb);
454 
455 				strbuf_clear(sb);
456 				strbuf_puts(sb, action);
457 				strbuf_putc(sb, '?');
458 				strbuf_puts(sb, "pattern=");
459 				strbuf_puts(sb, name);
460 				strbuf_puts(sb, quote_amp);
461 				strbuf_puts(sb, "type=");
462 				if (db == GTAGS)
463 					strbuf_puts(sb, "definitions");
464 				else if (db == GRTAGS)
465 					strbuf_puts(sb, "reference");
466 				else
467 					strbuf_puts(sb, "symbol");
468 				file = strbuf_value(sb);
469 				dir = (*action == '/') ? NULL : "..";
470 			} else {
471 				if (type == 'R')
472 					dir = upperdir(DEFS);
473 				else if (type == 'Y')
474 					dir = upperdir(SYMS);
475 				else	/* 'D', 'M' or 'T' */
476 					dir = upperdir(REFS);
477 				file = fid;
478 				suffix = HTML;
479 			}
480 			strbuf_puts(outbuf, gen_href_begin_with_title(dir, file, suffix, NULL, tooltip(type, -1, count)));
481 			strbuf_puts(outbuf, name);
482 			strbuf_puts(outbuf, gen_href_end());
483 		} else {
484 			const char *lno = line;
485 			const char *fid = nextstring(line);
486 			const char *path = gpath_fid2path(fid, NULL);
487 
488 			path += 2;              /* remove './' */
489 			/*
490 			 * Don't make a link which refers to itself.
491 			 * Being used only once means that it is a self link.
492 			 */
493 			if (db == GSYMS) {
494 				strbuf_puts(outbuf, name);
495 				return;
496 			}
497 			strbuf_puts(outbuf, gen_href_begin_with_title(upperdir(SRCS), fid, HTML, lno, tooltip(type, atoi(lno), path)));
498 			strbuf_puts(outbuf, name);
499 			strbuf_puts(outbuf, gen_href_end());
500 		}
501 	}
502 }
503 /**
504  * put_anchor_force: output HTML anchor without warning.
505  *
506  *	@param[in]	name	tag
507  *	@param[in]	length
508  *	@param[in]	lineno	current line no
509  *
510  * The tag type is fixed at 'R' (GTAGS)
511  */
512 void
put_anchor_force(char * name,int length,int lineno)513 put_anchor_force(char *name, int length, int lineno)
514 {
515 	STATIC_STRBUF(sb);
516 	int saveflag = wflag;
517 
518 	strbuf_clear(sb);
519 	strbuf_nputs(sb, name, length);
520 	wflag = 0;
521 	put_anchor(strbuf_value(sb), 'R', lineno);
522 	wflag = saveflag;
523 }
524 /**
525  * put_include_anchor: output HTML anchor.
526  *
527  *	@param[in]	inc	inc structure
528  *	@param[in]	path	path name for display
529  */
530 void
put_include_anchor(struct data * inc,const char * path)531 put_include_anchor(struct data *inc, const char *path)
532 {
533 	if (inc->count == 1)
534 		strbuf_puts(outbuf, gen_href_begin(NULL, path2fid(strbuf_value(inc->contents)), HTML, NULL));
535 	else {
536 		char id[32];
537 		snprintf(id, sizeof(id), "%d", inc->id);
538 		strbuf_puts(outbuf, gen_href_begin(upperdir(INCS), id, HTML, NULL));
539 	}
540 	strbuf_puts(outbuf, path);
541 	strbuf_puts(outbuf, gen_href_end());
542 }
543 /**
544  * put_include_anchor_direct: output HTML anchor.
545  *
546  *	@param[in]	file	normalized path
547  *	@param[in]	path	path name for display
548  */
549 void
put_include_anchor_direct(const char * file,const char * path)550 put_include_anchor_direct(const char *file, const char *path)
551 {
552 	strbuf_puts(outbuf, gen_href_begin(NULL, path2fid(file), HTML, NULL));
553 	strbuf_puts(outbuf, path);
554 	strbuf_puts(outbuf, gen_href_end());
555 }
556 /**
557  * Put a reserved word (if, while, ...)
558  */
559 void
put_reserved_word(const char * word)560 put_reserved_word(const char *word)
561 {
562 	strbuf_puts(outbuf, reserved_begin);
563 	strbuf_puts(outbuf, word);
564 	strbuf_puts(outbuf, reserved_end);
565 }
566 /*
567  * Put a macro (#define, #undef, ...)
568  */
569 void
put_macro(const char * word)570 put_macro(const char *word)
571 {
572 	strbuf_puts(outbuf, sharp_begin);
573 	strbuf_puts(outbuf, word);
574 	strbuf_puts(outbuf, sharp_end);
575 }
576 /**
577  * Print warning message when unknown preprocessing directive is found.
578  */
579 void
unknown_preprocessing_directive(const char * word,int lineno)580 unknown_preprocessing_directive(const char *word, int lineno)
581 {
582 	word = strtrim(word, TRIM_ALL, NULL);
583 	warning("unknown preprocessing directive '%s'. [+%d %s]", word, lineno, curpfile);
584 	if (colorize_warned_line)
585 		warned = 1;
586 }
587 /**
588  * Print warning message when unexpected eof.
589  */
590 void
unexpected_eof(int lineno)591 unexpected_eof(int lineno)
592 {
593 	warning("unexpected eof. [+%d %s]", lineno, curpfile);
594 	if (colorize_warned_line)
595 		warned = 1;
596 }
597 /**
598  * Print warning message when unknown yacc directive is found.
599  */
600 void
unknown_yacc_directive(const char * word,int lineno)601 unknown_yacc_directive(const char *word, int lineno)
602 {
603 	warning("unknown yacc directive '%s'. [+%d %s]", word, lineno, curpfile);
604 	if (colorize_warned_line)
605 		warned = 1;
606 }
607 /**
608  * Print warning message when unmatched brace is found.
609  */
610 void
missing_left(const char * word,int lineno)611 missing_left(const char *word, int lineno)
612 {
613 	warning("missing left '%s'. [+%d %s]", word, lineno, curpfile);
614 	if (colorize_warned_line)
615 		warned = 1;
616 }
617 /*
618  * Put a character with HTML quoting.
619  *
620  * [Note] If you want to put '<', '>' or '&', you
621  * 		should echoc() instead, as this function escapes (fixes) those
622  *		characters for HTML.
623  */
624 void
put_char(int c)625 put_char(int c)
626 {
627 	const char *quoted = HTML_quoting(c);
628 
629 	if (quoted)
630 		strbuf_puts(outbuf, quoted);
631 	else
632 		strbuf_putc(outbuf, c);
633 }
634 /*
635  * Put a string with HTML quoting.
636  *
637  * [Note] If you want to put HTML tag itself, you should echos() instead,
638  *		as this function escapes (fixes) the characters '<',
639  *		'>' and '&' for HTML.
640  */
641 void
put_string(const char * s)642 put_string(const char *s)
643 {
644 	for (; *s; s++)
645 		put_char(*s);
646 }
647 /**
648  * Put brace ('{', '}')
649  */
650 void
put_brace(const char * text)651 put_brace(const char *text)
652 {
653 	strbuf_puts(outbuf, brace_begin);
654 	strbuf_puts(outbuf, text);
655 	strbuf_puts(outbuf, brace_end);
656 }
657 
658 /*
659  * common procedure for line control.
660  */
661 static char lineno_format[32];
662 static const char *guide = NULL;
663 
664 /**
665  * Begin of line processing.
666  */
667 void
put_begin_of_line(int lineno)668 put_begin_of_line(int lineno)
669 {
670         if (definition_header != NO_HEADER) {
671                 if (define_line(lineno))
672                         guide = generate_guide(lineno);
673                 else
674                         guide = NULL;
675         }
676         if (guide && definition_header == BEFORE_HEADER) {
677 		fputs_nl(guide, out);
678                 guide = NULL;
679         }
680 }
681 /**
682  * End of line processing.
683  *
684  *	@param[in]	lineno	current line number
685  *	Globals used (input):
686  *		outbuf, HTML line image
687  *
688  * The outbuf (string buffer) has HTML image of the line.
689  * This function flush and clear it.
690  */
691 void
put_end_of_line(int lineno)692 put_end_of_line(int lineno)
693 {
694 	fputs(gen_name_number(lineno), out);
695         if (nflag)
696                 fprintf(out, lineno_format, lineno);
697 	if (warned)
698 		fputs(warned_line_begin, out);
699 
700 	/* flush output buffer */
701 	fputs(strbuf_value(outbuf), out);
702 	strbuf_reset(outbuf);
703 
704 	if (warned)
705 		fputs(warned_line_end, out);
706 	if (guide == NULL)
707 		fputc('\n', out);
708 	else {
709 		if (definition_header == RIGHT_HEADER)
710 			fputs(guide, out);
711 		fputc('\n', out);
712 		if (definition_header == AFTER_HEADER) {
713 			fputs_nl(guide, out);
714 		}
715 		guide = NULL;
716 	}
717 	warned = 0;
718 
719 	/* save for the other job in this module */
720 	last_lineno = lineno;
721 }
722 /**
723  * Encode URL.
724  *
725  *	@param[out]	sb	encoded URL
726  *	@param[in]	url	URL
727  */
728 static void
encode(STRBUF * sb,const char * url)729 encode(STRBUF *sb, const char *url)
730 {
731 	int c;
732 
733 	while ((c = (unsigned char)*url++) != '\0') {
734 		if (isurlchar(c)) {
735 			strbuf_putc(sb, c);
736 		} else {
737 			strbuf_putc(sb, '%');
738 			strbuf_putc(sb, "0123456789abcdef"[c >> 4]);
739 			strbuf_putc(sb, "0123456789abcdef"[c & 0x0f]);
740 		}
741 	}
742 }
743 /**
744  * get_cvs_module: return CVS module of source file.
745  *
746  *	@param[in]	file		source path
747  *	@param[out]	basename	If basename is not NULL, store pointer to
748  *				the last component of source path.
749  *	@return		!=NULL : relative path from repository top,
750  *			==NULL : CVS/Repository is not readable.
751  */
752 static const char *
get_cvs_module(const char * file,const char ** basename)753 get_cvs_module(const char *file, const char **basename)
754 {
755 	const char *p;
756 	STATIC_STRBUF(dir);
757 	static char prev_dir[MAXPATHLEN];
758 	STATIC_STRBUF(module);
759 	FILE *ip;
760 
761 	strbuf_clear(dir);
762 	p = locatestring(file, "/", MATCH_LAST);
763 	if (p != NULL) {
764 		strbuf_nputs(dir, file, p - file);
765 		p++;
766 	} else {
767 		strbuf_putc(dir, '.');
768 		p = file;
769 	}
770 	if (basename != NULL)
771 		*basename = p;
772 	if (strcmp(strbuf_value(dir), prev_dir) != 0) {
773 		strlimcpy(prev_dir, strbuf_value(dir), sizeof(prev_dir));
774 		strbuf_clear(module);
775 		strbuf_puts(dir, "/CVS/Repository");
776 		ip = fopen(strbuf_value(dir), "r");
777 		if (ip != NULL) {
778 			strbuf_fgets(module, ip, STRBUF_NOCRLF);
779 			fclose(ip);
780 		}
781 	}
782 	if (strbuf_getlen(module) > 0)
783 		return strbuf_value(module);
784 	return NULL;
785 }
786 /**
787  *
788  * src2html: convert source code into HTML
789  *
790  *       @param[in]      src   source file     - Read from
791  *       @param[in]      html  HTML file       - Write to
792  *       @param[in]      notsource 1: isn't source, 0: source.
793  */
794 void
src2html(const char * src,const char * html,int notsource)795 src2html(const char *src, const char *html, int notsource)
796 {
797 	char indexlink[128];
798 
799 	/*
800 	 * setup lineno format.
801 	 */
802 	snprintf(lineno_format, sizeof(lineno_format), "%%%dd ", ncol);
803 
804 	fileop_in  = open_input_file(src);
805 	in = get_descripter(fileop_in);
806         curpfile = src;
807         warned = 0;
808 
809 	fileop_out = open_output_file(html, 0);
810 	out = get_descripter(fileop_out);
811 	strbuf_clear(outbuf);
812 
813 	snprintf(indexlink, sizeof(indexlink), "../mains.%s", normal_suffix);
814 	fputs_nl(gen_page_begin(src, SUBDIR), out);
815 	fputs_nl(body_begin, out);
816 	/*
817          * print fixed guide
818          */
819 	if (fixed_guide)
820 		fputs(fixed_guide_link_format(anchor_getlinks(0), fill_anchor(NULL, src)), out);
821 	/*
822          * print the header
823          */
824 	if (insert_header)
825 		fputs(gen_insert_header(SUBDIR), out);
826 	fputs(gen_name_string("TOP"), out);
827 	fputs(header_begin, out);
828 	fputs(fill_anchor(indexlink, src), out);
829 	if (cvsweb_url) {
830 		STATIC_STRBUF(sb);
831 		const char *module, *basename;
832 
833 		strbuf_clear(sb);
834 		strbuf_puts(sb, cvsweb_url);
835 		if (use_cvs_module
836 		 && (module = get_cvs_module(src, &basename)) != NULL) {
837 			encode(sb, module);
838 			strbuf_putc(sb, '/');
839 			encode(sb, basename);
840 		} else {
841 			encode(sb, src);
842 		}
843 		strbuf_puts(sb, "?view=log");
844 		if (cvsweb_cvsroot) {
845 			strbuf_puts(sb, quote_amp);
846 			strbuf_puts(sb, "cvsroot=");
847 			strbuf_puts(sb, cvsweb_cvsroot);
848 		}
849 		fputs(quote_space, out);
850 		fputs(gen_href_begin_simple(strbuf_value(sb)), out);
851 		fputs(cvslink_begin, out);
852 		fputs("[CVS]", out);
853 		fputs(cvslink_end, out);
854 		fputs_nl(gen_href_end(), out);
855 		/* doesn't close string buffer */
856 	}
857 	fputs_nl(header_end, out);
858 	fputs(comment_begin, out);
859 	fputs("/* ", out);
860 
861 	fputs(link_format(anchor_getlinks(0)), out);
862 	if (show_position)
863 		fprintf(out, "%s%s value='+1 %s' %s", quote_space, position_begin, src, position_end);
864 	fputs(" */", out);
865 	fputs_nl(comment_end, out);
866 	fputs_nl(hr, out);
867         /*
868          * It is not source file.
869          */
870         if (notsource) {
871 		STRBUF *sb = strbuf_open(0);
872 		const char *_;
873 
874 		fputs_nl(verbatim_begin, out);
875 		last_lineno = 0;
876 		while ((_ = strbuf_fgets(sb, in, STRBUF_NOCRLF)) != NULL) {
877 			fputs(gen_name_number(++last_lineno), out);
878 			detab_replacing(out, _, HTML_quoting);
879 		}
880 		fputs_nl(verbatim_end, out);
881 		strbuf_close(sb);
882         }
883 	/*
884 	 * It's source code.
885 	 */
886 	else {
887 		const char *basename;
888 		struct data *incref;
889 		struct anchor *ancref;
890 		STATIC_STRBUF(define_index);
891 
892                 /*
893                  * INCLUDED FROM index.
894                  */
895 		basename = locatestring(src, "/", MATCH_LAST);
896 		if (basename)
897 			basename++;
898 		else
899 			basename = src;
900 		incref = get_included(basename);
901 		if (incref) {
902 			char s_id[32];
903 			const char *dir, *file, *suffix, *key, *title;
904 
905 			fputs(header_begin, out);
906 			if (incref->ref_count > 1) {
907 				char s_count[32];
908 
909 				snprintf(s_count, sizeof(s_count), "%d", incref->ref_count);
910 				snprintf(s_id, sizeof(s_id), "%d", incref->id);
911 				dir = upperdir(INCREFS);
912 				file = s_id;
913 				suffix = HTML;
914 				key = NULL;
915 				title = tooltip('I', -1, s_count);
916 			} else {
917 				const char *p = strbuf_value(incref->ref_contents);
918 				const char *lno = strmake(p, " ");
919 				const char *filename;
920 
921 				p = locatestring(p, " ", MATCH_FIRST);
922 				if (p == NULL)
923 					die("internal error.(incref->ref_contents)");
924 				filename = p + 1;
925 				if (filename[0] == '.' && filename[1] == '/')
926 					filename += 2;
927 				dir = NULL;
928 				file = path2fid(filename);
929 				suffix = HTML;
930 				key = lno;
931 				title = tooltip('I', atoi(lno), filename);
932 			}
933 			fputs(gen_href_begin_with_title(dir, file, suffix, key, title), out);
934 			fputs(title_included_from, out);
935 			fputs(gen_href_end(), out);
936 			fputs_nl(header_end, out);
937 			fputs_nl(hr, out);
938 		}
939 		/*
940 		 * DEFINITIONS index.
941 		 */
942 		strbuf_clear(define_index);
943 		for (ancref = anchor_first(); ancref; ancref = anchor_next()) {
944 			if (ancref->type == 'D') {
945 				char tmp[32];
946 				snprintf(tmp, sizeof(tmp), "%d", ancref->lineno);
947 				strbuf_puts(define_index, item_begin);
948 				strbuf_puts(define_index, gen_href_begin_with_title(NULL, NULL, NULL, tmp, tooltip('R', ancref->lineno, NULL)));
949 				strbuf_puts(define_index, gettag(ancref));
950 				strbuf_puts(define_index, gen_href_end());
951 				strbuf_puts_nl(define_index, item_end);
952 			}
953 		}
954 		if (strbuf_getlen(define_index) > 0) {
955 			fputs(header_begin, out);
956 			fputs(title_define_index, out);
957 			fputs_nl(header_end, out);
958 			fputs_nl("This source file includes following definitions.", out);
959 			fputs_nl(list_begin, out);
960 			fputs(strbuf_value(define_index), out);
961 			fputs_nl(list_end, out);
962 			fputs_nl(hr, out);
963 		}
964 		/*
965 		 * print source code
966 		 */
967 		fputs_nl(verbatim_begin, out);
968 		{
969 			const char *suffix = locatestring(src, ".", MATCH_LAST);
970 			const char *lang = NULL;
971 			struct lang_entry *ent;
972 
973 			/*
974 			 * Decide language.
975 			 */
976 			if (suffix)
977 				lang = decide_lang(suffix);
978 			/*
979 			 * Select parser.
980 			 * If lang == NULL then default parser is selected.
981 			 */
982 			ent = get_lang_entry(lang);
983 			/*
984 			 * Initialize parser.
985 			 */
986 			ent->init_proc(in);
987 			/*
988 			 * Execute parser.
989 			 * Exec_proc() is called repeatedly until returning EOF.
990 			 */
991 			while (ent->exec_proc())
992 				;
993 		}
994 		fputs_nl(verbatim_end, out);
995 	}
996 	fputs_nl(hr, out);
997 	fputs_nl(gen_name_string("BOTTOM"), out);
998 	fputs(comment_begin, out);
999 	fputs("/* ", out);
1000 	fputs(link_format(anchor_getlinks(-1)), out);
1001 	if (show_position)
1002 		fprintf(out, "%s%s value='+%d %s' %s", quote_space, position_begin, last_lineno, src, position_end);
1003 	fputs(" */", out);
1004 	fputs_nl(comment_end, out);
1005 	if (insert_footer) {
1006 		fputs(br, out);
1007 		fputs(gen_insert_footer(SUBDIR), out);
1008 	}
1009 	fputs_nl(body_end, out);
1010 	fputs_nl(gen_page_end(), out);
1011 	if (!notsource)
1012 		anchor_unload();
1013 	close_file(fileop_out);
1014 	close_file(fileop_in);
1015 }
1016