1 /*
2  * Copyright (c) 2004, 2005, 2008, 2010, 2011 Tama Communications Corporation
3  *
4  * This file is part of GNU GLOBAL.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 #include <stdio.h>
23 #ifdef STDC_HEADERS
24 #include <stdlib.h>
25 #endif
26 #ifdef HAVE_STRING_H
27 #include <string.h>
28 #else
29 #include <strings.h>
30 #endif
31 #include <ctype.h>
32 #include <sys/types.h>
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 #ifdef HAVE_FCNTL_H
37 #include <fcntl.h>
38 #else
39 #include <sys/file.h>
40 #endif
41 
42 #include "global.h"
43 #include "anchor.h"
44 #include "common.h"
45 #include "htags.h"
46 #include "path2url.h"
47 
48 /*
49  * Tag definitions
50  */
51 const char *html_begin		= "<html xmlns='http://www.w3.org/1999/xhtml'>";
52 const char *html_end		= "</html>";
53 const char *html_head_begin	= "<head>";
54 const char *html_head_end	= "</head>";
55 const char *html_title_begin	= "<title>";
56 const char *html_title_end	= "</title>";
57 const char *body_begin		= "<body>";
58 const char *body_end		= "</body>";
59 const char *title_begin		= "<h1 class='title'>";
60 const char *title_end		= "</h1>";
61 const char *header_begin	= "<h2 class='header'>";
62 const char *header_end		= "</h2>";
63 const char *poweredby_begin	= "<div class='poweredby'>";
64 const char *poweredby_end	= "</div>";
65 const char *cvslink_begin	= "<span class='cvs'>";
66 const char *cvslink_end		= "</span>";
67 const char *caution_begin	= "<div class='caution'>";
68 const char *caution_end		= "</div>";
69 const char *list_begin		= "<ol>";
70 const char *list_end		= "</ol>";
71 const char *item_begin		= "<li>";
72 const char *item_end		= "</li>";
73 const char *flist_begin		= "<table class='flist'>";
74 const char *flist_end		= "</table>";
75 const char *fline_begin		= "<tr class='flist'>";
76 const char *fline_end		= "</tr>";
77 const char *fitem_begin		= "<td class='flist'>";
78 const char *fitem_end		= "</td>";
79 const char *define_list_begin	= "<dl>";
80 const char *define_list_end	= "</dl>";
81 const char *define_term_begin	= "<dt>";
82 const char *define_term_end	= "</dt>";
83 const char *define_desc_begin	= "<dd>";
84 const char *define_desc_end	= "</dd>";
85 const char *table_begin		= "<table>";
86 const char *table_end		= "</table>";
87 const char *comment_begin	= "<em class='comment'>";
88 const char *comment_end		= "</em>";
89 const char *sharp_begin		= "<em class='sharp'>";
90 const char *sharp_end		= "</em>";
91 const char *brace_begin		= "<em class='brace'>";
92 const char *brace_end		= "</em>";
93 const char *verbatim_begin	= "<pre>";
94 const char *verbatim_end	= "</pre>";
95 const char *reserved_begin	= "<strong class='reserved'>";
96 const char *reserved_end	= "</strong>";
97 const char *position_begin	= "<input type='text' readonly onfocus='this.select();'";
98 const char *position_end	= " />";
99 const char *warned_line_begin	= "<em class='warned'>";
100 const char *warned_line_end	= "</em>";
101 const char *current_line_begin	= "<span class='curline'>";
102 const char *current_line_end	= "</span>";
103 const char *current_row_begin	= "<tr class='curline'>";
104 const char *current_row_end	= "</tr>";
105 const char *error_begin		= "<h2 class='error'>";
106 const char *error_end		= "</h2>";
107 const char *message_begin	= "<h3 class='message'>";
108 const char *message_end		= "</h3>";
109 const char *string_begin	= "<em class='string'>";
110 const char *string_end		= "</em>";
111 const char *quote_great		= "&gt;";
112 const char *quote_little	= "&lt;";
113 const char *quote_amp		= "&amp;";
114 const char *quote_space		= "&nbsp;";
115 const char *hr			= "<hr />";
116 const char *br			= "<br />";
117 const char *empty_element	= " /";
118 const char *noframes_begin	= "<noframes>";
119 const char *noframes_end	= "</noframes>";
120 
121 /* tree view tag (--tree-view) */
122 const char *tree_control	= "<div id='control'>All <a href='#'>close</a> | <a href='#'>open</a></div>";
123 const char *tree_loading	= "<span id='init' class='loading'>Under construction...</span>";
124 const char *tree_begin		= "<ul id='tree'>";
125 const char *tree_begin_using	= "<ul id='tree' class='%s'>";
126 const char *tree_end		= "</ul>";
127 const char *dir_begin		= "<li><span class='folder'>";
128 const char *dir_title_end	= "</span>";
129 const char *dir_end		= "</li>";
130 const char *file_begin		= "<li><span class='file'>";
131 const char *file_end		= "</span></li>";
132 
133 /* fixed guide tag (--fixed-guide) */
134 const char *guide_begin		= "<div id='guide'><ul>";
135 const char *guide_end		= "</ul></div>";
136 const char *guide_unit_begin	= "<li>";
137 const char *guide_unit_end	= "</li>";
138 const char *guide_path_begin	= "<li class='standout'><span>";
139 const char *guide_path_end	= "</span></li>";
140 
141 static const char *fix_attr_value(const char *);
142 
143 /*
144  * print string and new line.
145  *
146  * This function is a replacement of fprintf(op, "%s\n", s) in htags.
147  */
148 int
fputs_nl(const char * s,FILE * op)149 fputs_nl(const char *s, FILE *op)
150 {
151 	fputs(s, op);
152 	putc('\n', op);
153 	return 0;
154 }
155 /*
156  * These methods are used to tell lex() the current path information.
157  */
158 static char current_path[MAXPATHLEN];
159 static char current_dir[MAXPATHLEN];
160 static char current_file[MAXPATHLEN];
161 
162 /**
163  * save path infomation
164  */
165 void
save_current_path(const char * path)166 save_current_path(const char *path)
167 {
168 	char *startp, *p;
169 
170 	strlimcpy(current_path, path, sizeof(current_path));
171 	/* Extract directory name and file name from path */
172 	strlimcpy(current_dir, path, sizeof(current_path));
173 	startp = current_dir;
174 	p = startp + strlen(current_dir);
175 	while (p > startp) {
176 		if (*--p == '/') {
177 			*p = '\0';
178 			strlimcpy(current_file, p + 1, sizeof(current_file));
179 			return;
180 		}
181 	}
182 	/* It seems that the path doesn't include '/' */
183 	strlimcpy(current_file, path, sizeof(current_file));
184 }
185 char *
get_current_dir(void)186 get_current_dir(void)
187 {
188 	return current_dir;
189 }
190 char *
get_current_file(void)191 get_current_file(void)
192 {
193 	return current_file;
194 }
195 
196 /**
197  * Generate upper directory.
198  *
199  * Just returns the parent path of dir. (Adds "../" to it).
200  */
201 const char *
upperdir(const char * dir)202 upperdir(const char *dir)
203 {
204 	STATIC_STRBUF(sb);
205 
206 	strbuf_clear(sb);
207 	strbuf_sprintf(sb, "../%s", dir);
208 	return strbuf_value(sb);
209 }
210 /*
211  * Load text from file with replacing @PARENT_DIR@ macro.
212  * Macro @PARENT_DIR@ is replaced with the parent directory
213  * of the "HTML" directory.
214  */
215 static const char *
sed(FILE * ip,int place)216 sed(FILE *ip, int place)
217 {
218 	STATIC_STRBUF(sb);
219 	const char *parent_dir = (place == SUBDIR) ? "../.." : "..";
220 	int c, start_position = -1;
221 
222 	strbuf_clear(sb);
223 	while ((c = fgetc(ip)) != EOF) {
224 		strbuf_putc(sb, c);
225 		if (c == '@') {
226 			int curpos = strbuf_getlen(sb);
227 			if (start_position == -1) {
228 				start_position = curpos - 1;
229 			} else {
230 				if (!strncmp("@PARENT_DIR@",
231 					strbuf_value(sb) + start_position,
232 					curpos - start_position))
233 				{
234 					strbuf_setlen(sb, start_position);
235 					strbuf_puts(sb, parent_dir);
236 					start_position = -1;
237 				} else {
238 					start_position = curpos - 1;
239 				}
240 			}
241 		} else if (!isalpha(c) && c != '_') {
242 			if (start_position != -1)
243 				start_position = -1;
244 		}
245 	}
246 	return strbuf_value(sb);
247 }
248 /**
249  * Generate custom header.
250  */
251 const char *
gen_insert_header(int place)252 gen_insert_header(int place)
253 {
254 	static FILE *ip;
255 
256 	if (ip != NULL) {
257 		rewind(ip);
258 	} else {
259 		ip = fopen(insert_header, "r");
260 		if (ip == NULL)
261 			die("cannot open include header file '%s'.", insert_header);
262 	}
263 	return sed(ip, place);
264 }
265 /**
266  * Generate custom footer.
267  */
268 const char *
gen_insert_footer(int place)269 gen_insert_footer(int place)
270 {
271 	static FILE *ip;
272 
273 	if (ip != NULL) {
274 		rewind(ip);
275 	} else {
276 		ip = fopen(insert_footer, "r");
277 		if (ip == NULL)
278 			die("cannot open include footer file '%s'.", insert_footer);
279 	}
280 	return sed(ip, place);
281 }
282 /**
283  * Generate beginning of generic page
284  *
285  *	@param[in]	title	title of this page
286  *	@param[in]	place	SUBDIR: this page is in sub directory,
287  *			TOPDIR: this page is in the top directory
288  *	@param[in]	use_frameset
289  *			use frameset document type or not
290  *	@param[in]	header_item
291  *			item which should be inserted into the header
292  */
293 static const char *
gen_page_generic_begin(const char * title,int place,int use_frameset,const char * header_item)294 gen_page_generic_begin(const char *title, int place, int use_frameset, const char *header_item)
295 {
296 	STATIC_STRBUF(sb);
297 	const char *dir = NULL;
298 
299 	switch (place) {
300 	case TOPDIR:
301 		dir = "";
302 		break;
303 	case SUBDIR:
304 		 dir = "../";
305 		break;
306 	case CGIDIR:
307 		 dir = "$basedir/";	/* decided by the CGI script */
308 		break;
309 	}
310 	strbuf_clear(sb);
311 	if (enable_xhtml) {
312 		/*
313 		 * If the --frame option are specified then we take
314 		 * 'XHTML 1.0 Frameset' for index.html
315 		 * and 'XHTML 1.0 Transitional' for other files.
316 		 */
317 		if (use_frameset)
318 			strbuf_puts_nl(sb, "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Frameset//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd'>");
319 		else
320 			strbuf_puts_nl(sb, "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>");
321 	}
322 	strbuf_puts_nl(sb, html_begin);
323 	strbuf_puts_nl(sb, html_head_begin);
324 	strbuf_puts(sb, html_title_begin);
325 	strbuf_puts(sb, title);
326 	strbuf_puts_nl(sb, html_title_end);
327 	strbuf_sprintf(sb, "<meta name='robots' content='noindex,nofollow'%s>\n", empty_element);
328 	strbuf_sprintf(sb, "<meta name='generator' content='GLOBAL-%s'%s>\n", get_version(), empty_element);
329 	if (enable_xhtml) {
330 		strbuf_sprintf(sb, "<meta http-equiv='Content-Style-Type' content='text/css'%s>\n", empty_element);
331 		strbuf_sprintf(sb, "<link rel='stylesheet' type='text/css' href='%sstyle.css'%s>\n", dir, empty_element);
332 	}
333 	if (header_item)
334 		strbuf_puts(sb, header_item);		/* internal use */
335 	if (html_header)
336 		strbuf_puts(sb, html_header);		/* --html-header=file */
337 	strbuf_puts(sb, html_head_end);
338 	return strbuf_value(sb);
339 }
340 /**
341  * Generate beginning of normal page
342  *
343  *	@param[in]	title	title of this page
344  *	@param[in]	place	SUBDIR: this page is in sub directory,
345  *			TOPDIR: this page is in the top directory
346  */
347 const char *
gen_page_begin(const char * title,int place)348 gen_page_begin(const char *title, int place)
349 {
350 	return gen_page_generic_begin(title, place, 0, NULL);
351 }
352 /**
353  * beginning of normal page for index page
354  *
355  *	@param[in]	title	title of this page
356  *	@param[in]	header_item	an item which should be inserted into the header
357  */
358 const char *
gen_page_index_begin(const char * title,const char * header_item)359 gen_page_index_begin(const char *title, const char *header_item)
360 {
361 	return gen_page_generic_begin(title, TOPDIR, 0, header_item);
362 }
363 /*
364  * Generate beginning of frameset page (<frameset>)
365  *
366  *	@param[in]	title	title of this page
367  */
368 const char *
gen_page_frameset_begin(const char * title)369 gen_page_frameset_begin(const char *title)
370 {
371 	return gen_page_generic_begin(title, TOPDIR, 1, NULL);
372 }
373 /*
374  * Generate end of page (</html>)
375  */
376 const char *
gen_page_end(void)377 gen_page_end(void)
378 {
379 	return html_end;
380 }
381 
382 /*
383  * Generate image tag (<img>)
384  *
385  *	@param[in]	where	Where is the icon directory?
386  *			CURRENT: current directory,
387  *			PARENT: parent directory
388  *	@param[in]	file	icon file without suffix.
389  *	@param[in]	alt	alt string (the 'alt' attribute is always added)
390  *
391  *	[Note] Images are assumed to be in the "icons" or "../icons" directory, only.
392  */
393 const char *
gen_image(int where,const char * file,const char * alt)394 gen_image(int where, const char *file, const char *alt)
395 {
396 	STATIC_STRBUF(sb);
397 	const char *dir = (where == PARENT) ? "../icons" : "icons";
398 
399 	strbuf_clear(sb);
400 	if (enable_xhtml)
401 		strbuf_sprintf(sb, "<img class='icon' src='%s/%s.%s' alt='[%s]'%s>",
402 			dir, file, icon_suffix, fix_attr_value(alt), empty_element);
403 	else
404 		strbuf_sprintf(sb, "<img src='%s/%s.%s' alt='[%s]' %s%s>",
405 			dir, file, icon_suffix, fix_attr_value(alt), icon_spec, empty_element);
406 	return strbuf_value(sb);
407 }
408 /**
409  * Generate name tag.
410  */
411 const char *
gen_name_number(int number)412 gen_name_number(int number)
413 {
414 	static char buf[32];
415 
416 	snprintf(buf, sizeof(buf), "L%d", number);
417 	return gen_name_string(buf);
418 }
419 /*
420  * Generate name tag (<a name='xxx'>).
421  *
422  * Uses attribute 'id', if is XHTML.
423  */
424 const char *
gen_name_string(const char * name)425 gen_name_string(const char *name)
426 {
427 	STATIC_STRBUF(sb);
428 
429 	strbuf_clear(sb);
430 	if (enable_xhtml) {
431 		/*
432 		 * Since some browser cannot understand "<a id='xxx' />",
433 		 * we put both of 'id=' and 'name='.
434 		 */
435 		strbuf_sprintf(sb, "<a id='%s' name='%s'></a>", name, name);
436 	} else {
437 		strbuf_sprintf(sb, "<a name='%s'></a>", name);
438 	}
439 	return strbuf_value(sb);
440 }
441 /*
442  * Generate anchor begin tag (<a href='dir/file.suffix#key'>).
443  * (complete format)
444  *
445  *	@param[in]	dir	directory
446  *	@param[in]	file	file
447  *	@param[in]	suffix	suffix (file extension e.g. '.txt'). A '.' (dot) will be added.
448  *	@param[in]	key	key
449  *	@param[in]	title	title='xxx' attribute; if NULL, doesn't add it.
450  *	@param[in]	target	target='xxx' attribute; if NULL, doesn't add it.
451  *	@return		generated anchor tag
452  *
453  *	[Note] dir, file, suffix, key, target and title may be NULL.
454  *	[Note] Single quote (') characters are used to delimit the attribute values.
455  */
456 const char *
gen_href_begin_with_title_target(const char * dir,const char * file,const char * suffix,const char * key,const char * title,const char * target)457 gen_href_begin_with_title_target(const char *dir, const char *file, const char *suffix, const char *key, const char *title, const char *target)
458 {
459 	STATIC_STRBUF(sb);
460 
461 	strbuf_clear(sb);
462 	/*
463 	 * Construct URL.
464 	 * href='dir/file.suffix#key'
465 	 */
466 	strbuf_puts(sb, "<a href='");
467 	if (file) {
468 		if (dir) {
469 			strbuf_puts(sb, dir);
470 			strbuf_putc(sb, '/');
471 		}
472 		strbuf_puts(sb, file);
473 		if (suffix) {
474 			strbuf_putc(sb, '.');
475 			strbuf_puts(sb, suffix);
476 		}
477 	}
478 	if (key) {
479 		strbuf_putc(sb, '#');
480 		/*
481 		 * If the key starts with a digit, it assumed line number.
482 		 * XHTML 1.1 profibits number as an anchor.
483 		 */
484 		if (isdigit(*key))
485 			strbuf_putc(sb, 'L');
486 		strbuf_puts(sb, key);
487 	}
488 	strbuf_putc(sb, '\'');
489 	if (Fflag && target)
490 		strbuf_sprintf(sb, " target='%s'", fix_attr_value(target));
491 	if (title)
492 		strbuf_sprintf(sb, " title='%s'", fix_attr_value(title));
493 	strbuf_putc(sb, '>');
494 	return strbuf_value(sb);
495 }
496 /**
497  * Generate simple anchor begin tag.
498  *
499  * Uses: gen_href_begin_with_title_target()
500  */
501 const char *
gen_href_begin_simple(const char * file)502 gen_href_begin_simple(const char *file)
503 {
504 	return gen_href_begin_with_title_target(NULL, file, NULL, NULL, NULL, NULL);
505 }
506 /**
507  * Generate anchor begin tag without title and target.
508  *
509  * Uses: gen_href_begin_with_title_target()
510  */
511 const char *
gen_href_begin(const char * dir,const char * file,const char * suffix,const char * key)512 gen_href_begin(const char *dir, const char *file, const char *suffix, const char *key)
513 {
514 	return gen_href_begin_with_title_target(dir, file, suffix, key, NULL, NULL);
515 }
516 /**
517  * Generate anchor begin tag without target.
518  *
519  * Uses: gen_href_begin_with_title_target()
520  */
521 const char *
gen_href_begin_with_title(const char * dir,const char * file,const char * suffix,const char * key,const char * title)522 gen_href_begin_with_title(const char *dir, const char *file, const char *suffix, const char *key, const char *title)
523 {
524 	return gen_href_begin_with_title_target(dir, file, suffix, key, title, NULL);
525 }
526 /*
527  * Generate anchor end tag (</a>).
528  */
529 const char *
gen_href_end(void)530 gen_href_end(void)
531 {
532 	return "</a>";
533 }
534 /**
535  * Generate list begin tag.
536  */
537 const char *
gen_list_begin(void)538 gen_list_begin(void)
539 {
540 	STATIC_STRBUF(sb);
541 
542 	if (strbuf_empty(sb)) {
543 		strbuf_clear(sb);
544 		if (table_list) {
545 			if (enable_xhtml) {
546 				strbuf_sprintf(sb, "%s\n%s%s%s%s",
547 					table_begin,
548 					"<tr><th class='tag'>tag</th>",
549 					"<th class='line'>line</th>",
550 					"<th class='file'>file</th>",
551 					"<th class='code'>source code</th></tr>");
552 			} else {
553 				strbuf_sprintf(sb, "%s\n%s%s%s%s",
554 					table_begin,
555 					"<tr><th nowrap='nowrap' align='left'>tag</th>",
556 					"<th nowrap='nowrap' align='right'>line</th>",
557 					"<th nowrap='nowrap' align='center'>file</th>",
558 					"<th nowrap='nowrap' align='left'>source code</th></tr>");
559 			}
560 		} else {
561 			strbuf_puts(sb, verbatim_begin);
562 		}
563 	}
564 	return strbuf_value(sb);
565 }
566 /**
567  * Generate list body.
568  *
569  * ctags_x with the --encode-path=" \t"
570  */
571 const char *
gen_list_body(const char * srcdir,const char * ctags_x,const char * fid)572 gen_list_body(const char *srcdir, const char *ctags_x, const char *fid)	/* virtually const */
573 {
574 	STATIC_STRBUF(sb);
575 	char path[MAXPATHLEN];
576 	const char *p;
577 	SPLIT ptable;
578 
579 	strbuf_clear(sb);
580 	if (split((char *)ctags_x, 4, &ptable) < 4) {
581 		recover(&ptable);
582 		die("too small number of parts in list_body().\n'%s'", ctags_x);
583 	}
584 	strlimcpy(path, decode_path(ptable.part[PART_PATH].start + 2), sizeof(path));
585 	if (fid == NULL)
586 		fid = path2fid(path);
587 	if (table_list) {
588 		strbuf_puts(sb, current_row_begin);
589 		if (enable_xhtml) {
590 			strbuf_puts(sb, "<td class='tag'>");
591 			strbuf_puts(sb, gen_href_begin(srcdir, fid, HTML, ptable.part[PART_LNO].start));
592 			strbuf_puts(sb, ptable.part[PART_TAG].start);
593 			strbuf_puts(sb, gen_href_end());
594 			strbuf_sprintf(sb, "</td><td class='line'>%s</td><td class='file'>%s</td><td class='code'>",
595 				ptable.part[PART_LNO].start, path);
596 		} else {
597 			strbuf_puts(sb, "<td nowrap='nowrap'>");
598 			strbuf_puts(sb, gen_href_begin(srcdir, fid, HTML, ptable.part[PART_LNO].start));
599 			strbuf_puts(sb, ptable.part[PART_TAG].start);
600 			strbuf_puts(sb, gen_href_end());
601 			strbuf_sprintf(sb, "</td><td nowrap='nowrap' align='right'>%s</td>"
602 				       "<td nowrap='nowrap' align='left'>%s</td><td nowrap='nowrap'>",
603 				ptable.part[PART_LNO].start, path);
604 		}
605 		for (p = ptable.part[PART_LINE].start; *p; p++) {
606 			unsigned char c = *p;
607 
608 			if (c == '&')
609 				strbuf_puts(sb, quote_amp);
610 			else if (c == '<')
611 				strbuf_puts(sb, quote_little);
612 			else if (c == '>')
613 				strbuf_puts(sb, quote_great);
614 			else if (c == ' ')
615 				strbuf_puts(sb, quote_space);
616 			else if (c == '\t') {
617 				strbuf_puts(sb, quote_space);
618 				strbuf_puts(sb, quote_space);
619 			} else
620 				strbuf_putc(sb, c);
621 		}
622 		strbuf_puts(sb, "</td>");
623 		strbuf_puts(sb, current_row_end);
624 		recover(&ptable);
625 	} else {
626 		/* print tag name with anchor */
627 		strbuf_puts(sb, current_line_begin);
628 		strbuf_puts(sb, gen_href_begin(srcdir, fid, HTML, ptable.part[PART_LNO].start));
629 		strbuf_puts(sb, ptable.part[PART_TAG].start);
630 		strbuf_puts(sb, gen_href_end());
631 		recover(&ptable);
632 
633 		/* print line number */
634 		for (p = ptable.part[PART_TAG].end; p < ptable.part[PART_PATH].start; p++)
635 			strbuf_putc(sb, *p);
636 		/* print file name */
637 		strbuf_puts(sb, path);
638 		/* print the rest */
639 		for (p = ptable.part[PART_PATH].end; *p; p++) {
640 			unsigned char c = *p;
641 
642 			if (c == '&')
643 				strbuf_puts(sb, quote_amp);
644 			else if (c == '<')
645 				strbuf_puts(sb, quote_little);
646 			else if (c == '>')
647 				strbuf_puts(sb, quote_great);
648 			else
649 				strbuf_putc(sb, c);
650 		}
651 		strbuf_puts(sb, current_line_end);
652 	}
653 	return strbuf_value(sb);
654 }
655 /**
656  * Generate list end tag.
657  */
658 const char *
gen_list_end(void)659 gen_list_end(void)
660 {
661 	return table_list ? table_end : verbatim_end;
662 }
663 /*
664  * Generate beginning of form (<form>)
665  *
666  *	@param[in]	target	target attribute or NULL for no target.
667  */
668 const char *
gen_form_begin(const char * target)669 gen_form_begin(const char *target)
670 {
671 	STATIC_STRBUF(sb);
672 
673 	strbuf_clear(sb);
674 	strbuf_sprintf(sb, "<form method='get' action='%s'", fix_attr_value(action));
675 	if (Fflag && target)
676 		strbuf_sprintf(sb, " target='%s'", fix_attr_value(target));
677 	strbuf_puts(sb, ">");
678 	return strbuf_value(sb);
679 }
680 /*
681  * Generate end of form (</form>)
682  */
683 const char *
gen_form_end(void)684 gen_form_end(void)
685 {
686 	return "</form>";
687 }
688 /*
689  * Generate input tag (<input>)
690  *
691  * Uses: gen_input_with_title_checked()
692  */
693 const char *
gen_input(const char * name,const char * value,const char * type)694 gen_input(const char *name, const char *value, const char *type)
695 {
696 	return gen_input_with_title_checked(name, value, type, 0, NULL);
697 }
698 /*
699  * Generate input radiobox tag (<input type='radio'>)
700  *
701  * Uses: gen_input_with_title_checked()
702  */
703 const char *
gen_input_radio(const char * name,const char * value,int checked,const char * title)704 gen_input_radio(const char *name, const char *value, int checked, const char *title)
705 {
706 	return gen_input_with_title_checked(name, value, "radio", checked, title);
707 }
708 /*
709  * Generate input checkbox tag (<input type='checkbox'>)
710  *
711  * Uses: gen_input_with_title_checked()
712  */
713 const char *
gen_input_checkbox(const char * name,const char * value,const char * title)714 gen_input_checkbox(const char *name, const char *value, const char *title)
715 {
716 	return gen_input_with_title_checked(name, value, "checkbox", 0, title);
717 }
718 /*
719  * Generate input radio tag (<input>)
720  *
721  *	[Note] name, value, type and title may be NULL, thus only those
722  *		with a non-NULL value will have there attribute added.
723  *		The argument names are the same as the corresponding HTML attribute names.
724  *	[Note] Single quote (') characters are used to delimit the attribute values.
725  */
726 const char *
gen_input_with_title_checked(const char * name,const char * value,const char * type,int checked,const char * title)727 gen_input_with_title_checked(const char *name, const char *value, const char *type, int checked, const char *title)
728 {
729 	STATIC_STRBUF(sb);
730 
731 	strbuf_clear(sb);
732 	strbuf_puts(sb, "<input");
733 	if (type)
734 		strbuf_sprintf(sb, " type='%s'", type);
735 	if (name)
736 		strbuf_sprintf(sb, " name='%s' id='%s'", name, name);
737 	if (value)
738 		strbuf_sprintf(sb, " value='%s'", fix_attr_value(value));
739 	if (checked) {
740 		if (enable_xhtml)
741 			strbuf_puts(sb, " checked='checked'");
742 		else
743 			strbuf_puts(sb, " checked");
744 	}
745 	if (title)
746 		strbuf_sprintf(sb, " title='%s'", fix_attr_value(title));
747 	strbuf_sprintf(sb, "%s>", empty_element);
748 	return strbuf_value(sb);
749 }
750 /*
751  * Generate beginning of frameset (<frameset>)
752  *
753  *	@param[in]	contents	target
754  */
755 const char *
gen_frameset_begin(const char * contents)756 gen_frameset_begin(const char *contents)
757 {
758 	STATIC_STRBUF(sb);
759 
760 	strbuf_clear(sb);
761 	strbuf_sprintf(sb, "<frameset %s>", contents);
762 	return strbuf_value(sb);
763 }
764 /*
765  * Generate end of frameset (</frameset>)
766  */
767 const char *
gen_frameset_end(void)768 gen_frameset_end(void)
769 {
770 	return "</frameset>";
771 }
772 /*
773  * Generate beginning of frame (<frame>)
774  *
775  *	@param[in]	name	target (value for name and id attributes)
776  *	@param[in]	src	value for src attribute
777  */
778 const char *
gen_frame(const char * name,const char * src)779 gen_frame(const char *name, const char *src)
780 {
781 	STATIC_STRBUF(sb);
782 
783 	strbuf_clear(sb);
784 	strbuf_sprintf(sb, "<frame name='%s' id='%s' src='%s'%s>", name, name, src, empty_element);
785 	return strbuf_value(sb);
786 }
787 
788 
789 /** HTML attribute delimiter character ( ' or " only) */
790 #define ATTR_DELIM '\''
791 
792 /*
793  * Check and fix an attribute's value; convert all ' (single quote) characters
794  * into &#39; within it.
795  */
796 static const char *
fix_attr_value(const char * value)797 fix_attr_value(const char *value)
798 {
799 	STATIC_STRBUF(sb);
800 	char c;
801 	const char *cptr;
802 
803 	strbuf_clear(sb);
804 	cptr = value;
805 
806 	while((c = *cptr) != '\0') {
807 		if(c == ATTR_DELIM)
808 			strbuf_puts(sb, "&#39;");
809 		else
810 			strbuf_putc(sb, c);
811 		++cptr;
812 	}
813 	return strbuf_value(sb);
814 }
815