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 = ">";
112 const char *quote_little = "<";
113 const char *quote_amp = "&";
114 const char *quote_space = " ";
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 ' 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, "'");
809 else
810 strbuf_putc(sb, c);
811 ++cptr;
812 }
813 return strbuf_value(sb);
814 }
815