1 /*
2  * Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3  *      2006, 2007, 2008, 2010, 2011, 2016
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 #include <errno.h>
27 #ifdef STDC_HEADERS
28 #include <stdlib.h>
29 #endif
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
33 #ifdef HAVE_FCNTL_H
34 #include <fcntl.h>
35 #else
36 #include <sys/file.h>
37 #endif
38 #include <signal.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/param.h>
42 #include <errno.h>
43 
44 #include "getopt.h"
45 #include "regex.h"
46 #include "global.h"
47 #include "anchor.h"
48 #include "cache.h"
49 #include "common.h"
50 #include "htags.h"
51 #include "incop.h"
52 #include "path2url.h"
53 #include "const.h"
54 
55 /*
56  * htags - generate hypertext (XHTML or HTML) pages from a set of source files.
57  */
58 
59 void src2html(const char *, const char *, int);
60 int makedupindex(void);
61 int makedefineindex(const char *, int, STRBUF *);
62 int makefileindex(const char *, STRBUF *);
63 void makeincludeindex(void);
64 int makecflowindex(const char *, const char *);
65 
66 #if defined(_WIN32) && !defined(__CYGWIN__)
67 #define mkdir(path,mode) mkdir(path)
68 #define link(one,two) (-1)
69 #endif
70 
71 /*
72  * Global data.
73  */
74 int w32 = W32;				/**< Windows32 environment	*/
75 const char *www = "http://www.gnu.org/software/global/";
76 int html_count = 0;
77 int sep = '/';
78 const char *save_config;
79 const char *save_argv;
80 
81 char cwdpath[MAXPATHLEN];
82 char dbpath[MAXPATHLEN];
83 char distpath[MAXPATHLEN];
84 char gtagsconf[MAXPATHLEN];
85 char datadir[MAXPATHLEN];
86 char localstatedir[MAXPATHLEN];
87 
88 char gtags_path[MAXFILLEN];
89 char global_path[MAXFILLEN];
90 int gtags_exist[GTAGLIM];
91 const char *null_device = NULL_DEVICE;
92 const char *tmpdir = "/tmp";
93 
94 /**
95  * Order of items in the top page (This should be customisable variable in the future).
96  *
97  * 'c': caution
98  * 's': search form
99  * 'm': mains
100  * 'd': definitions
101  * 'f': files
102  * 't': call tree
103  */
104 char *item_order = "csmdft";
105 /*
106  * options
107  */
108 int aflag;				/**< --alphabet(-a) option	*/
109 int fflag;				/**< --form(-f) option		*/
110 int Fflag;				/**< --frame(-F) option		*/
111 int gflag;				/**< --gtags(-g) option		*/
112 int Iflag;				/**< --icon(-I) option		*/
113 int nflag;				/**< --line-number(-n) option	*/
114 int qflag;
115 int vflag;				/**< --verbose(-v) option		*/
116 int wflag;				/**< --warning(-w) option		*/
117 int debug;				/**< --debug option		*/
118 
119 int show_help;				/**< --help command		*/
120 int show_version;			/**< --version command		*/
121 int caution;				/**< --caution option		*/
122 int dynamic;				/**< --dynamic(-D) option		*/
123 int symbol;				/**< --symbol(-s) option          */
124 int suggest;				/**< --suggest option		*/
125 int suggest2;				/**< --suggest2 option		*/
126 int auto_completion;			/**< --auto-completion		*/
127 int tree_view;				/**< --tree-view			*/
128 int fixed_guide;			/**< --fixed-guide		*/
129 const char *tree_view_type;		/**< --type-view=[type]		*/
130 char *auto_completion_limit = "0";	/**< --auto-completion=limit	*/
131 int statistics = STATISTICS_STYLE_NONE;	/**< --statistics option		*/
132 
133 int no_order_list;			/**< 1: doesn't use order list	*/
134 int other_files;			/**< 1: list other files		*/
135 int enable_grep = 1;			/**< 1: enable grep		*/
136 int enable_idutils = 1;			/**< 1: enable idutils		*/
137 int enable_xhtml = 1;			/**< 1: enable XHTML		*/
138 
139 const char *main_func = "main";
140 const char *cvsweb_url;
141 int use_cvs_module;
142 const char *cvsweb_cvsroot;
143 const char *gtagslabel;
144 const char *title;
145 const char *insert_header;		/* --insert-header=<file>	*/
146 const char *insert_footer;		/* --insert-footer=<file>	*/
147 const char *html_header;		/* --html-header=<file>		*/
148 const char *jscode;			/**< javascript code		*/
149 /*
150  * Constant values.
151  */
152 const char *title_define_index = "DEFINITIONS";
153 const char *title_file_index = "FILES";
154 const char *title_call_tree = "CALL TREE";
155 const char *title_callee_tree = "CALLEE TREE";
156 const char *title_included_from = "INCLUDED FROM";
157 /*
158  * Function header items.
159  */
160 const char *anchor_label[] = {
161 	"&lt;",
162 	"&gt;",
163 	"^",
164 	"v",
165 	"top",
166 	"bottom",
167 	"index",
168 	"help"
169 };
170 const char *anchor_icons[] = {
171 	"left",
172 	"right",
173 	"first",
174 	"last",
175 	"top",
176 	"bottom",
177 	"index",
178 	"help"
179 };
180 const char *anchor_comment[] = {
181 	"previous",
182 	"next",
183 	"first",
184 	"last",
185 	"top",
186 	"bottom",
187 	"index",
188 	"help"
189 };
190 const char *anchor_msg[] = {
191 	"Previous definition.",
192 	"Next definition.",
193 	"First definition in this file.",
194 	"Last definition in this file.",
195 	"Top of this file.",
196 	"Bottom of this file.",
197 	"Return to index page.",
198 	"You are seeing now."
199 };
200 const char *back_icon = "back";
201 const char *dir_icon  = "dir";
202 const char *c_icon = "c";
203 const char *file_icon = "text";
204 
205 const char *icon_files[] = {
206 	"first",
207 	"last",
208 	"left",
209 	"right",
210 	"top",
211 	"bottom",
212 	"n_first",
213 	"n_last",
214 	"n_left",
215 	"n_right",
216 	"n_top",
217 	"n_bottom",
218 	"index",
219 	"help",
220 	"back",
221 	"dir",
222 	"c",
223 	"text",
224 	"pglobe"
225 };
226 /*
227  * Configuration parameters.
228  */
229 int ncol = 4;				/**< columns of line number	*/
230 int tabs = 8;				/**< tab skip			*/
231 int flist_fields = 5;			/**< fields number of file list	*/
232 int full_path = 0;			/**< file index format		*/
233 int map_file = 0;			/**< 1: create MAP file		*/
234 int filemap_file = 1;			/**< 1: create FILEMAP file	*/
235 const char *icon_suffix = "png";	/**< icon suffix (jpg, png etc)	*/
236 const char *icon_spec = "border='0' align='top'"; /**< parameter in IMG tag*/
237 const char *prolog_script = NULL;	/**< include script at first	*/
238 const char *epilog_script = NULL;	/**< include script at last	*/
239 const char *call_file = NULL;		/**< file name of cflow output	*/
240 const char *callee_file = NULL;		/**< file name of cflow output	*/
241 int show_position = 0;			/**< show current position	*/
242 int table_list = 0;			/**< tag list using table tag	*/
243 int table_flist = 0;			/**< file list using table tag	*/
244 int colorize_warned_line = 0;		/**< colorize warned line		*/
245 const char *normal_suffix = "html";	/**< suffix of normal html file	*/
246 const char *HTML;					/**< HTML */
247 const char *action = "cgi-bin/global.cgi"; /**< default action		*/
248 const char *completion_action = "cgi-bin/completion.cgi";	/**< completion_action */
249 int definition_header=NO_HEADER;	/**< (NO|BEFORE|RIGHT|AFTER)_HEADER */
250 const char *include_file_suffixes = DEFAULTINCLUDEFILESUFFIXES;	/**< include_file_suffixes */
251 static const char *langmap = DEFAULTLANGMAP;	/**< langmap */
252 int grtags_is_empty = 0;						/**< grtags_is_empty */
253 
254 const char *short_options = "acC:d:DfFghIm:nNoqst:Tvwx";
255 struct option const long_options[] = {
256 	/*
257 	 * These options have long name and short name.
258 	 * We throw them to the processing of short options.
259 	 */
260         {"alphabet", no_argument, NULL, 'a'},
261         {"directory", required_argument, NULL, 'C'},
262         {"dbpath", required_argument, NULL, 'd'},
263         {"dynamic", no_argument, NULL, 'D'},
264         {"form", no_argument, NULL, 'f'},
265         {"frame", no_argument, NULL, 'F'},
266         {"func-header", optional_argument, NULL, 'h'},
267         {"gtags", no_argument, NULL, 'g'},
268         {"icon", no_argument, NULL, 'I'},
269         {"line-number", optional_argument, NULL, 'n'},
270         {"main-func", required_argument, NULL, 'm'},
271         {"other", no_argument, NULL, 'o'},
272         {"symbol", no_argument, NULL, 's'},
273         {"table-flist", optional_argument, NULL, 'T'},
274         {"title", required_argument, NULL, 't'},
275         {"verbose", no_argument, NULL, 'v'},
276         {"warning", no_argument, NULL, 'w'},
277 
278         /*
279 	 * The following are long name only.
280 	 */
281 	/* flag value */
282         {"caution", no_argument, &caution, 1},
283         {"colorize-warned-line", no_argument, &colorize_warned_line, 1},
284         {"debug", no_argument, &debug, 1},
285         {"disable-grep", no_argument, &enable_grep, 0},
286         {"disable-idutils", no_argument, &enable_idutils, 0},
287         {"full-path", no_argument, &full_path, 1},
288         {"fixed-guide",  no_argument, &fixed_guide, 1},
289         {"map-file", no_argument, &map_file, 1},
290         {"no-order-list", no_argument, &no_order_list, 1},
291         {"show-position", no_argument, &show_position, 1},
292         {"statistics", no_argument, &statistics, STATISTICS_STYLE_TABLE},
293         {"suggest", no_argument, &suggest, 1},
294         {"suggest2", no_argument, &suggest2, 1},
295         {"table-list", no_argument, &table_list, 1},
296         {"version", no_argument, &show_version, 1},
297         {"help", no_argument, &show_help, 1},
298 
299 	/* accept value */
300 #define OPT_CVSWEB		128
301 #define OPT_CVSWEB_CVSROOT	129
302 #define OPT_NCOL		130
303 #define OPT_INSERT_FOOTER	131
304 #define OPT_INSERT_HEADER	132
305 #define OPT_ITEM_ORDER		133
306 #define OPT_TABS		134
307 #define OPT_CFLOW		135
308 #define OPT_AUTO_COMPLETION	136
309 #define OPT_TREE_VIEW		137
310 #define OPT_HTML_HEADER		138
311 #define OPT_CALL_TREE		139
312 #define OPT_CALLEE_TREE		140
313         {"auto-completion", optional_argument, NULL, OPT_AUTO_COMPLETION},
314         {"call-tree", required_argument, NULL, OPT_CALL_TREE},
315         {"callee-tree", required_argument, NULL, OPT_CALLEE_TREE},
316         {"cflow", required_argument, NULL, OPT_CFLOW},
317         {"cvsweb", required_argument, NULL, OPT_CVSWEB},
318         {"cvsweb-cvsroot", required_argument, NULL, OPT_CVSWEB_CVSROOT},
319         {"gtagsconf", required_argument, NULL, OPT_GTAGSCONF},
320         {"gtagslabel", required_argument, NULL, OPT_GTAGSLABEL},
321         {"html-header", required_argument,NULL, OPT_HTML_HEADER},
322         {"ncol", required_argument, NULL, OPT_NCOL},
323         {"insert-footer", required_argument, NULL, OPT_INSERT_FOOTER},
324         {"insert-header", required_argument, NULL, OPT_INSERT_HEADER},
325         {"item-order", required_argument, NULL, OPT_ITEM_ORDER},
326 	{"tabs", required_argument, NULL, OPT_TABS},
327         {"tree-view",  optional_argument, NULL, OPT_TREE_VIEW},
328         { 0 }
329 };
330 
331 static void
usage(void)332 usage(void)
333 {
334         if (!qflag)
335                 fputs(usage_const, stderr);
336         exit(2);
337 }
338 static void
help(void)339 help(void)
340 {
341         fputs(usage_const, stdout);
342         fputs(help_const, stdout);
343         exit(0);
344 }
345 /**
346  * Htags catch signal even if the parent ignore it.
347  */
348 void
clean(void)349 clean(void)
350 {
351 	unload_gpath();
352 	cache_close();
353 }
354 /**
355  * Signal handler.
356  *
357  * This handler is set up in signal_setup().
358  */
359 static void
suddenly(int signo)360 suddenly(int signo)
361 {
362         signo = 0;      /* to satisfy compiler */
363 
364 	clean();
365 	exit(1);
366 }
367 
368 /**
369  * Setup signal hander.
370  *
371  * Makes signals SIGINT, SIGTERM, SIGHUP and SIGQUIT
372  * call suddenly() if triggered.
373  */
374 static void
signal_setup(void)375 signal_setup(void)
376 {
377         signal(SIGINT, suddenly);
378         signal(SIGTERM, suddenly);
379 #ifdef SIGHUP
380         signal(SIGHUP, suddenly);
381 #endif
382 #ifdef SIGQUIT
383         signal(SIGQUIT, suddenly);
384 #endif
385 }
386 
387 /**
388  * make directory in the dist (distpath) directory.
389  *
390  *     @param[in]      name    name of directory to create.
391  *
392  * Creates a file called "index.html" in the new directory.
393  *
394  * mkdir() creates the directory in mode 0775, if doesn't exist.
395  */
396 static void
make_directory_in_distpath(const char * name)397 make_directory_in_distpath(const char *name)
398 {
399 	char path[MAXPATHLEN];
400 	FILE *op;
401 
402 	strlimcpy(path, makepath(distpath, name, NULL), sizeof(path));
403 	if (!test("d", path))
404 		if (mkdir(path, 0775))
405 			die("cannot make directory '%s'.", path);
406 	/*
407 	 * Not to publish the directory list.
408 	 */
409 	op = fopen(makepath(path, "index.html", NULL), "w");
410 	if (op == NULL)
411 		die("cannot make file '%s'.", makepath(path, "index.html", NULL));
412 	fputs(html_begin, op);
413 	fputs(html_end, op);
414 	fputc('\n', op);
415 	fclose(op);
416 }
417 /**
418  * Load file.
419  */
420 void
loadfile(const char * file,STRBUF * result)421 loadfile(const char *file, STRBUF *result)
422 {
423 	STRBUF *sb = strbuf_open(0);
424 	FILE *ip = fopen(file, "r");
425 	if (!ip)
426 		die("file '%s' not found.", file);
427 	while (strbuf_fgets(sb, ip, STRBUF_NOCRLF) != NULL)
428 		strbuf_puts_nl(result, strbuf_value(sb));
429 	fclose(ip);
430 	strbuf_close(sb);
431 }
432 /**
433  * makeprogram: make CGI program
434  */
435 static void
makeprogram(const char * cgidir,const char * file,int perm)436 makeprogram(const char *cgidir, const char *file, int perm)
437 {
438 	char src[MAXPATHLEN];
439 	const char *dst = makepath(cgidir, file, NULL);
440 
441 	snprintf(src, sizeof(src), "%s/gtags/%s", datadir, file);
442 	copyfile(src, dst);
443 	if (chmod(dst, perm) < 0)
444 		die("cannot chmod CGI program (%s).", dst);
445 	html_count++;
446 }
447 /**
448  * makerebuild: make rebuild script
449  */
450 static void
makerebuild(const char * file)451 makerebuild(const char *file)
452 {
453 	FILE *op;
454 
455 	op = fopen(makepath(distpath, file, NULL), "w");
456 	if (!op)
457 		die("cannot make rebuild script.");
458 	fputs_nl("#!/bin/sh", op);
459 	fputs_nl("#", op);
460 	fputs_nl("# rebuild.sh: rebuild hypertext with the previous context.", op);
461 	fputs_nl("#", op);
462 	fputs_nl("# Usage:", op);
463 	fputs_nl("#\t% sh rebuild.sh", op);
464 	fputs_nl("#", op);
465 	fprintf(op, "cd %s && GTAGSCONF='%s' htags%s\n", cwdpath, save_config, save_argv);
466         fclose(op);
467 }
468 /**
469  * makehelp: make help file
470  */
471 static void
makehelp(const char * file)472 makehelp(const char *file)
473 {
474 	const char **label = Iflag ? anchor_comment : anchor_label;
475 	const char **icons = anchor_icons;
476 	const char **msg   = anchor_msg;
477 	int n, last = 7;
478 	FILE *op;
479 
480 	op = fopen(makepath(distpath, file, NULL), "w");
481 	if (!op)
482 		die("cannot make help file.");
483 	fputs_nl(gen_page_begin("HELP", TOPDIR), op);
484 	fputs_nl(body_begin, op);
485 	fputs(header_begin, op);
486 	fputs("Usage of Links", op);
487 	fputs_nl(header_end, op);
488 	if (!Iflag)
489 		fputs(verbatim_begin, op);
490 	fputs("/* ", op);
491 	for (n = 0; n <= last; n++) {
492 		if (Iflag) {
493 			fputs(gen_image(CURRENT, icons[n], label[n]), op);
494 			if (n < last)
495 				fputc(' ', op);
496 		} else {
497 			fprintf(op, "[%s]", label[n]);
498 		}
499 	}
500 	if (show_position)
501 		fprintf(op, "%s%s value='+<line number> <file>' %s", quote_space, position_begin, position_end);
502 	fputs(" */", op);
503 	if (!Iflag)
504 		fputs_nl(verbatim_end, op);
505 	else
506 		fputc('\n', op);
507 	fputs_nl(define_list_begin, op);
508 	for (n = 0; n <= last; n++) {
509 		fputs(define_term_begin, op);
510 		if (Iflag) {
511 			fputs(gen_image(CURRENT, icons[n], label[n]), op);
512 		} else {
513 			fprintf(op, "[%s]", label[n]);
514 		}
515 		fputs(define_term_end, op);
516 		fputs(define_desc_begin, op);
517 		fputs(msg[n], op);
518 		fputs_nl(define_desc_end, op);
519 	}
520 	if (show_position) {
521 		fputs(define_term_begin, op);
522 		fprintf(op, "%s%s value='+<line number> <file>' %s", quote_space, position_begin, position_end);
523 		fputs(define_term_end, op);
524 		fputs(define_desc_begin, op);
525 		fputs("The current position (line number and file name).", op);
526 		fputs_nl(define_desc_end, op);
527 	}
528 	fputs_nl(define_list_end, op);
529 	fputs_nl(body_end, op);
530 	fputs_nl(gen_page_end(), op);
531 	fclose(op);
532 	html_count++;
533 }
534 /*
535  * makesearchpart: make search part
536  *
537  *	@param[in]	target	$target
538  *	@return		html
539  */
540 static char *
makesearchpart(const char * target)541 makesearchpart(const char *target)
542 {
543 	STATIC_STRBUF(sb);
544 
545 	strbuf_clear(sb);
546 	strbuf_puts(sb, header_begin);
547 	if (Fflag)
548 		strbuf_puts(sb, gen_href_begin(NULL, "search", normal_suffix, NULL));
549 	strbuf_puts(sb, "SEARCH");
550 	if (Fflag)
551 		strbuf_puts(sb, gen_href_end());
552 	strbuf_puts_nl(sb, header_end);
553 	if (!target) {
554 		strbuf_puts(sb, "Please input object name and select [Search]. POSIX's regular expression is allowed.");
555 		strbuf_puts_nl(sb, br);
556 	}
557 	strbuf_puts_nl(sb, gen_form_begin(target));
558 	strbuf_puts_nl(sb, gen_input("pattern", NULL, NULL));
559 	strbuf_puts_nl(sb, gen_input(NULL, "Search", "submit"));
560 	strbuf_puts(sb, gen_input(NULL, "Reset", "reset"));
561 	strbuf_puts_nl(sb, br);
562 	strbuf_puts(sb, gen_input_radio("type", "definition", 1, "Retrieve the definition place of the specified symbol."));
563 	strbuf_puts_nl(sb, target ? "Def" : "Definition");
564 	strbuf_puts(sb, gen_input_radio("type", "reference", 0, "Retrieve the reference place of the specified symbol."));
565 	strbuf_puts_nl(sb, target ? "Ref" : "Reference");
566 	strbuf_puts(sb, gen_input_radio("type", "symbol", 0, "Retrieve the place of the specified symbol is used."));
567 	strbuf_puts_nl(sb, target ? "Sym" : "Other symbol");
568 	strbuf_puts(sb, gen_input_radio("type", "path", 0, "Look for path name which matches to the specified pattern."));
569 	strbuf_puts_nl(sb, target ? "Path" : "Path name");
570 	if (enable_grep) {
571 		strbuf_puts(sb, gen_input_radio("type", "grep", 0, "Retrieve lines which matches to the specified pattern."));
572 		strbuf_puts_nl(sb, target ? "Grep" : "Grep pattern");
573 	}
574 	if (enable_idutils && test("f", makepath(dbpath, "ID", NULL))) {
575 		strbuf_puts(sb, gen_input_radio("type", "idutils", 0, "Retrieve lines which matches to the specified pattern using idutils(1)."));
576 		strbuf_puts_nl(sb, target ? "Id" : "Id pattern");
577 	}
578 	strbuf_puts_nl(sb, br);
579 	strbuf_puts(sb, gen_input_checkbox("icase", NULL, "Ignore case distinctions in the pattern."));
580 	strbuf_puts_nl(sb, target ? "Icase" : "Ignore case");
581 	if (other_files) {
582 		strbuf_puts(sb, gen_input_checkbox("other", NULL, "Files other than the source code are also retrieved."));
583 		strbuf_puts_nl(sb, target ? "Other" : "Other files");
584 	}
585 	if (other_files && !target) {
586 		strbuf_puts_nl(sb, br);
587 		strbuf_puts(sb, "('Other files' is effective only to 'Path name'");
588 		if (enable_grep)
589 			strbuf_puts(sb, " and 'Grep pattern'");
590 		strbuf_puts_nl(sb, ".)");
591 	}
592 	strbuf_puts_nl(sb, gen_form_end());
593 	return strbuf_value(sb);
594 }
595 /**
596  * makeindex: make index file
597  *
598  *	@param[in]	file	file name
599  *	@param[in]	title	title of index file
600  *	@param[in]	index	common part
601  */
602 static void
makeindex(const char * file,const char * title,const char * index)603 makeindex(const char *file, const char *title, const char *index)
604 {
605 	FILE *op;
606 
607 	op = fopen(makepath(distpath, file, NULL), "w");
608 	if (!op)
609 		die("cannot make file '%s'.", file);
610 	if (Fflag) {
611 		fputs_nl(gen_page_frameset_begin(title), op);
612 		fputs_nl(gen_frameset_begin("cols='200,*'"), op);
613 		if (fflag) {
614 			fputs_nl(gen_frameset_begin("rows='33%,33%,*'"), op);
615 			fputs_nl(gen_frame("search", makepath(NULL, "search", normal_suffix)), op);
616 		} else {
617 			fputs_nl(gen_frameset_begin("rows='50%,*'"), op);
618 		}
619 		/*
620 		 * id='xxx' for XHTML
621 		 * name='xxx' for HTML
622 		 */
623 		fputs_nl(gen_frame("defines", makepath(NULL, "defines", normal_suffix)), op);
624 		fputs_nl(gen_frame("files", makepath(NULL, "files", normal_suffix)), op);
625 		fputs_nl(gen_frameset_end(), op);
626 		fputs_nl(gen_frame("mains", makepath(NULL, "mains", normal_suffix)), op);
627 		fputs_nl(noframes_begin, op);
628 		fputs_nl(body_begin, op);
629 		fputs(index, op);
630 		fputs_nl(body_end, op);
631 		fputs_nl(noframes_end, op);
632 		fputs_nl(gen_frameset_end(), op);
633 		fputs_nl(gen_page_end(), op);
634 	} else {
635 		fputs_nl(gen_page_index_begin(title, jscode), op);
636 		fputs_nl(body_begin, op);
637 		if (insert_header)
638 			fputs(gen_insert_header(TOPDIR), op);
639 		fputs(index, op);
640 		if (insert_footer)
641 			fputs(gen_insert_footer(TOPDIR), op);
642 		fputs_nl(body_end, op);
643 		fputs_nl(gen_page_end(), op);
644 	}
645 	fclose(op);
646 	html_count++;
647 }
648 /**
649  * makemainindex: make main index
650  *
651  *	@param[in]	file	file name
652  *	@param[in]	index	common part
653  */
654 static void
makemainindex(const char * file,const char * index)655 makemainindex(const char *file, const char *index)
656 {
657 	FILE *op;
658 
659 	op = fopen(makepath(distpath, file, NULL), "w");
660 	if (!op)
661 		die("cannot make file '%s'.", file);
662 	fputs_nl(gen_page_index_begin(title, jscode), op);
663 	fputs_nl(body_begin, op);
664 	if (insert_header)
665 		fputs(gen_insert_header(TOPDIR), op);
666 	fputs(index, op);
667 	if (insert_footer)
668 		fputs(gen_insert_footer(TOPDIR), op);
669 	fputs_nl(body_end, op);
670 	fputs_nl(gen_page_end(), op);
671 	fclose(op);
672 	html_count++;
673 }
674 /**
675  * makesearchindex: make search html
676  *
677  *	@param[in]	file	file name
678  */
679 static void
makesearchindex(const char * file)680 makesearchindex(const char *file)
681 {
682 	FILE *op;
683 
684 	op = fopen(makepath(distpath, file, NULL), "w");
685 	if (!op)
686 		die("cannot create file '%s'.", file);
687 	fputs_nl(gen_page_index_begin("SEARCH", jscode), op);
688 	fputs_nl(body_begin, op);
689 	fputs(makesearchpart("mains"), op);
690 	fputs_nl(body_end, op);
691 	fputs_nl(gen_page_end(), op);
692 	fclose(op);
693 	html_count++;
694 }
695 /**
696  * makehtaccess: make ".htaccess" skeleton file.
697  */
698 static void
makehtaccess(const char * file,int perm)699 makehtaccess(const char *file, int perm)
700 {
701 	char src[MAXPATHLEN];
702 	const char *dst = makepath(distpath, file, NULL);
703 
704 	snprintf(src, sizeof(src), "%s/gtags/dot_htaccess", datadir);
705 	copyfile(src, dst);
706 	if (chmod(dst, perm) < 0)
707 		die("cannot chmod .htaccess skeleton.");
708 }
709 /**
710  * makehtml: make html files
711  *
712  *	@param[in]	total	number of files.
713  */
714 static void
makehtml(int total)715 makehtml(int total)
716 {
717 	GFIND *gp;
718 	FILE *anchor_stream;
719 	const char *path;
720 	int count = 0;
721 
722 	/*
723 	 * Create anchor stream for anchor_load().
724 	 */
725 	anchor_stream = tmpfile();
726 #if defined(_WIN32) && !defined(__CYGWIN__)
727 	/*
728 	 * tmpfile is created in the root, which user's can't write on Vista+.
729 	 * Use _tempnam and open it directly.
730 	 */
731 	if (anchor_stream == NULL) {
732 		char *name = _tempnam(tmpdir, "htags");
733 		anchor_stream = fopen(name, "w+bD");
734 		free(name);
735 	}
736 #endif
737 	gp = gfind_open(dbpath, NULL, other_files ? GPATH_BOTH : GPATH_SOURCE, 0);
738 	while ((path = gfind_read(gp)) != NULL) {
739 		if (gp->type == GPATH_OTHER)
740 			fputc(' ', anchor_stream);
741 		fputs(path, anchor_stream);
742 		fputc('\n', anchor_stream);
743 	}
744 	gfind_close(gp);
745 	/*
746 	 * Prepare anchor stream for anchor_load().
747 	 */
748 	anchor_prepare(anchor_stream);
749 	/*
750 	 * For each path in GPATH, convert the path into HTML file.
751 	 */
752 	gp = gfind_open(dbpath, NULL, other_files ? GPATH_BOTH : GPATH_SOURCE, 0);
753 	while ((path = gfind_read(gp)) != NULL) {
754 		char html[MAXPATHLEN];
755 
756 		if (gp->type == GPATH_OTHER && !other_files)
757 			continue;
758 		/*
759 		 * load tags belonging to the path.
760 		 * The path must be start "./".
761 		 */
762 		anchor_load(path);
763 		/*
764 		 * inform the current path name to lex() function.
765 		 */
766 		save_current_path(path);
767 		count++;
768 		path += 2;		/* remove './' at the head */
769 		message(" [%d/%d] converting %s", count, total, path);
770 		snprintf(html, sizeof(html), "%s/%s/%s.%s", distpath, SRCS, path2fid(path), HTML);
771 		src2html(path, html, gp->type == GPATH_OTHER);
772 	}
773 	gfind_close(gp);
774 }
775 /**
776  * makecommonpart: make a common part for "mains.html" and "index.html"
777  *
778  *	@param[in]	title
779  *	@param[in]	defines
780  *	@param[in]	files
781  *	@return	index	common part
782  */
783 static char *
makecommonpart(const char * title,const char * defines,const char * files)784 makecommonpart(const char *title, const char *defines, const char *files)
785 {
786 	FILE *ip;
787 	STRBUF *sb = strbuf_open(0);
788 	STRBUF *ib = strbuf_open(0);
789 	char buf[MAXFILLEN];
790 	const char *tips = "Go to the GLOBAL project page.";
791 	const char *_, *item;
792 
793 	strbuf_puts(sb, title_begin);
794 	strbuf_puts(sb, title);
795 	strbuf_puts_nl(sb, title_end);
796 	strbuf_puts_nl(sb, poweredby_begin);
797 	strbuf_sprintf(sb, "Last updated %s%s\n", now(), br);
798 	if (Iflag) {
799 		snprintf(buf, sizeof(buf), "Powered by GLOBAL-%s.", get_version());
800 		strbuf_puts(sb, gen_href_begin_with_title_target(NULL, www, NULL, NULL, tips,"_top"));
801 		strbuf_puts(sb, gen_image(CURRENT, "pglobe", buf));
802 		strbuf_puts(sb, gen_href_end());
803 		strbuf_puts(sb, br);
804 	} else {
805 		strbuf_sprintf(sb, "Powered by %sGLOBAL-%s%s.%s\n",
806 			gen_href_begin_with_title_target(NULL, www, NULL, NULL, tips, "_top"),
807 			get_version(),
808 			gen_href_end(),
809 			br);
810 	}
811 	strbuf_puts_nl(sb, poweredby_end);
812 	strbuf_puts_nl(sb, hr);
813 	/*
814 	 * Print items according to the value of variable 'item_order'.
815 	 */
816 	for (item = item_order; *item; item++) {
817 		switch (*item) {
818 		case 'c':
819 			if (caution) {
820 				strbuf_puts_nl(sb, caution_begin);
821 				strbuf_sprintf(sb, "<font size='+2' color='red'>CAUTION</font>%s\n", br);
822 				strbuf_sprintf(sb, "This hypertext consists of %d files.\n", html_count);
823 				strbuf_puts_nl(sb, "Please don't download the whole hypertext using a hypertext copy tool.");
824 				strbuf_puts_nl(sb, "Our network cannot afford such traffic.");
825 				strbuf_puts_nl(sb, "Instead, you can generate the same thing in your computer using");
826 				strbuf_puts(sb, gen_href_begin_with_title_target(NULL, www, NULL, NULL, NULL, "_top"));
827 				strbuf_puts(sb, "GLOBAL source code tag system");
828 				strbuf_puts_nl(sb, gen_href_end());
829 				strbuf_puts_nl(sb, "Thank you.");
830 				strbuf_puts_nl(sb, caution_end);
831 				strbuf_sprintf(sb, "\n%s\n", hr);
832 			}
833 			break;
834 		case 's':
835 			if (fflag) {
836 				strbuf_puts(sb, makesearchpart(NULL));
837 				strbuf_puts_nl(sb, hr);
838 			}
839 			break;
840 		case 't':
841 			if (call_file || callee_file) {
842 				strbuf_puts(sb, header_begin);
843 				if (call_file) {
844 					strbuf_puts(sb, gen_href_begin(NULL, "call", normal_suffix, NULL));
845 					strbuf_puts(sb, title_call_tree);
846 					strbuf_puts(sb, gen_href_end());
847 				}
848 				if (call_file && callee_file)
849 					strbuf_puts(sb, " / ");
850 				if (callee_file) {
851 					strbuf_puts(sb, gen_href_begin(NULL, "callee", normal_suffix, NULL));
852 					strbuf_puts(sb, title_callee_tree);
853 					strbuf_puts(sb, gen_href_end());
854 				}
855 				strbuf_puts_nl(sb, header_end);
856 				strbuf_puts_nl(sb, hr);
857 			}
858 			break;
859 		case 'm':
860 			strbuf_sprintf(sb, "%sMAINS%s\n", header_begin, header_end);
861 
862 			snprintf(buf, sizeof(buf), PQUOTE "%s --result=ctags-xid --encode-path=\" \t\" --nofilter=path %s" PQUOTE, quote_shell(global_path), main_func);
863 			ip = popen(buf, "r");
864 			if (!ip)
865 				die("cannot execute '%s'.", buf);
866 			strbuf_puts_nl(sb, gen_list_begin());
867 			while ((_ = strbuf_fgets(ib, ip, STRBUF_NOCRLF)) != NULL) {
868 				char fid[MAXFIDLEN];
869 				const char *ctags_x = parse_xid(_, fid, NULL);
870 
871 				strbuf_puts_nl(sb, gen_list_body(SRCS, ctags_x, fid));
872 			}
873 			strbuf_puts_nl(sb, gen_list_end());
874 			if (pclose(ip) != 0)
875 				die("terminated abnormally '%s' (errno = %d).", buf, errno);
876 			strbuf_puts_nl(sb, hr);
877 			break;
878 		case 'd':
879 			if (aflag && !Fflag) {
880 				strbuf_puts(sb, header_begin);
881 				strbuf_puts(sb, title_define_index);
882 				strbuf_puts_nl(sb, header_end);
883 				strbuf_puts(sb, defines);
884 			} else {
885 				strbuf_puts(sb, header_begin);
886 				strbuf_puts(sb, gen_href_begin(NULL, "defines", normal_suffix, NULL));
887 				strbuf_puts(sb, title_define_index);
888 				strbuf_puts(sb, gen_href_end());
889 				strbuf_puts_nl(sb, header_end);
890 			}
891 			strbuf_puts_nl(sb, hr);
892 			break;
893 		case 'f':
894 			if (Fflag) {
895 				strbuf_puts(sb, header_begin);
896 				strbuf_puts(sb, gen_href_begin(NULL, "files", normal_suffix, NULL));
897 				strbuf_puts(sb, title_file_index);
898 				strbuf_puts(sb, gen_href_end());
899 				strbuf_puts_nl(sb, header_end);
900 			} else {
901 				strbuf_puts(sb, header_begin);
902 				strbuf_puts(sb, title_file_index);
903 				strbuf_puts_nl(sb, header_end);
904 				if (tree_view) {
905 					strbuf_puts_nl(sb, tree_control);
906 					strbuf_puts_nl(sb, tree_loading);
907 					if (tree_view_type) {
908 						strbuf_sprintf(sb, tree_begin_using, tree_view_type);
909 						strbuf_putc(sb, '\n');
910 					} else {
911 						strbuf_puts_nl(sb, tree_begin);
912 					}
913 				} else if (table_flist)
914 					strbuf_puts_nl(sb, flist_begin);
915 				else if (!no_order_list)
916 					strbuf_puts_nl(sb, list_begin);
917 				strbuf_puts(sb, files);
918 				if (tree_view)
919 					strbuf_puts_nl(sb, tree_end);
920 				else if (table_flist)
921 					strbuf_puts_nl(sb, flist_end);
922 				else if (!no_order_list)
923 					strbuf_puts_nl(sb, list_end);
924 				else
925 					strbuf_puts_nl(sb, br);
926 			}
927 			strbuf_puts_nl(sb, hr);
928 			break;
929 		default:
930 			warning("unknown item '%c'. (Ignored)", *item);
931 			break;
932 		}
933 	}
934 	strbuf_close(ib);
935 
936 	return strbuf_value(sb);
937 	/* doesn't close string buffer */
938 }
939 /**
940  * basic check.
941  */
942 static void
basic_check(void)943 basic_check(void)
944 {
945 	const char *p;
946 
947 	/*
948 	 * COMMAND EXISTENCE CHECK
949 	 */
950 	if (!(p = usable("gtags")))
951 		die("gtags command required but not found.");
952 	strlimcpy(gtags_path, p, sizeof(gtags_path));
953 	if (!(p = usable("global")))
954 		die("global command required but not found.");
955 	strlimcpy(global_path, p, sizeof(global_path));
956 	/*
957 	 * Temporary directory.
958 	 */
959 	if ((p = getenv("TMPDIR")) == NULL)
960 		p = getenv("TMP");
961 	if (p != NULL && test("d", p))
962 		tmpdir = p;
963 }
964 /**
965  * load configuration variables.
966  */
967 static void
configuration(void)968 configuration(void)
969 {
970 	STRBUF *sb = strbuf_open(0);
971 
972 	/*
973 	 * Config variables.
974 	 */
975 	strbuf_reset(sb);
976 	if (!getconfs("datadir", sb))
977 		die("cannot get datadir directory name.");
978 	strlimcpy(datadir, strbuf_value(sb), sizeof(datadir));
979 	strbuf_reset(sb);
980 	if (!getconfs("localstatedir", sb))
981 		die("cannot get localstatedir directory name.");
982 	strlimcpy(localstatedir, strbuf_value(sb), sizeof(localstatedir));
983 	strbuf_reset(sb);
984 	if (getconfs("prolog_script", sb))
985 		prolog_script = check_strdup(strbuf_value(sb));
986 	strbuf_reset(sb);
987 	if (getconfs("epilog_script", sb))
988 		epilog_script = check_strdup(strbuf_value(sb));
989 	if (getconfb("colorize_warned_line"))
990 		colorize_warned_line = 1;
991 	strbuf_reset(sb);
992 	if (getconfs("include_file_suffixes", sb))
993 		include_file_suffixes = check_strdup(strbuf_value(sb));
994 	strbuf_reset(sb);
995 	if (getconfs("langmap", sb))
996 		langmap = check_strdup(strbuf_value(sb));
997 	strbuf_close(sb);
998 }
999 /**
1000  * save_environment: save configuration data and arguments.
1001  */
1002 static void
save_environment(int argc,char * const * argv)1003 save_environment(int argc, char *const *argv)
1004 {
1005 	char command[MAXFILLEN];
1006 	STRBUF *sb = strbuf_open(0);
1007 	STRBUF *save_c = strbuf_open(0);
1008 	STRBUF *save_a = strbuf_open(0);
1009 	int i;
1010 	const char *p;
1011 	FILE *ip;
1012 
1013 	/*
1014 	 * save config values.
1015 	 */
1016 	snprintf(command, sizeof(command), PQUOTE "%s --config" PQUOTE, quote_shell(gtags_path));
1017 	if ((ip = popen(command, "r")) == NULL)
1018 		die("cannot execute '%s'.", command);
1019 	while (strbuf_fgets(sb, ip, STRBUF_NOCRLF) != NULL) {
1020 		for (p = strbuf_value(sb); *p; p++) {
1021 			if (*p == '\'') {
1022 				strbuf_putc(save_c, '\'');
1023 				strbuf_putc(save_c, '"');
1024 				strbuf_putc(save_c, '\'');
1025 				strbuf_putc(save_c, '"');
1026 				strbuf_putc(save_c, '\'');
1027 			} else
1028 				strbuf_putc(save_c, *p);
1029 		}
1030 	}
1031 	if (pclose(ip) != 0)
1032 		die("terminated abnormally '%s' (errno = %d).", command, errno);
1033 	strbuf_close(sb);
1034 	save_config = strbuf_value(save_c);
1035 	/* doesn't close string buffer for save config. */
1036 	/* strbuf_close(save_c); */
1037 
1038 	/*
1039 	 * save arguments.
1040 	 */
1041 	{
1042 		char *opt_gtagsconf = "--gtagsconf";
1043 
1044 		for (i = 1; i < argc; i++) {
1045 			char *blank;
1046 
1047 			/*
1048 			 * skip --gtagsconf because it is already read
1049 			 * as config value.
1050 			 */
1051 			if ((p = locatestring(argv[i], opt_gtagsconf, MATCH_AT_FIRST))) {
1052 				if (*p == '\0')
1053 					i++;
1054 				continue;
1055 			}
1056 			blank = locatestring(argv[i], " ", MATCH_FIRST);
1057 			strbuf_putc(save_a, ' ');
1058 			if (blank)
1059 				strbuf_putc(save_a, '\'');
1060 			strbuf_puts(save_a, argv[i]);
1061 			if (blank)
1062 				strbuf_putc(save_a, '\'');
1063 		}
1064 	}
1065 	save_argv = strbuf_value(save_a);
1066 	/* doesn't close string buffer for save arguments. */
1067 	/* strbuf_close(save_a); */
1068 }
1069 
1070 int
main(int argc,char ** argv)1071 main(int argc, char **argv)
1072 {
1073 	const char *av = NULL;
1074 	int func_total, file_total;
1075         char arg_dbpath[MAXPATHLEN];
1076 	const char *index = NULL;
1077 	int optchar;
1078         int option_index = 0;
1079 	STATISTICS_TIME *tim;
1080 
1081 	/*
1082 	 * pick up --gtagsconf, --gtagslabel and --directory (-C).
1083 	 */
1084 	if (preparse_options(argc, argv) < 0)
1085 		usage();
1086 
1087 	arg_dbpath[0] = 0;
1088 	basic_check();
1089 	/*
1090 	 * Load configuration values.
1091 	 */
1092 	if (!vgetcwd(cwdpath, sizeof(cwdpath)))
1093 		die("cannot get current directory.");
1094 	openconf(cwdpath);
1095 	configuration();
1096 	/*
1097 	 * setup_langmap() is needed to use decide_lang().
1098 	 */
1099 	setup_langmap(langmap);
1100 	save_environment(argc, argv);
1101 	/*
1102 	 * insert htags_options at the head of argv.
1103 	 */
1104 	setenv_from_config();
1105 	{
1106 		char *env = getenv("HTAGS_OPTIONS");
1107 		if (env && *env)
1108 			argv = prepend_options(&argc, argv, env);
1109 	}
1110 	while ((optchar = getopt_long(argc, argv, short_options, long_options, &option_index)) != EOF) {
1111 		switch (optchar) {
1112 		case 0:
1113 			/* already flags set */
1114 			break;
1115 		case OPT_AUTO_COMPLETION:
1116 			auto_completion = 1;
1117 			if (optarg) {
1118 				if (atoi(optarg) > 0)
1119 					auto_completion_limit = optarg;
1120 				else
1121 					die("The option value of --auto-completion must be numeric.");
1122 			}
1123 			break;
1124 		case OPT_CFLOW:
1125 			call_file = optarg;
1126 			break;
1127 		case OPT_CALL_TREE:
1128 			call_file = optarg;
1129 			break;
1130 		case OPT_CALLEE_TREE:
1131 			callee_file = optarg;
1132 			break;
1133 		case OPT_CVSWEB:
1134 			cvsweb_url = optarg;
1135 			break;
1136 		case OPT_CVSWEB_CVSROOT:
1137 			cvsweb_cvsroot = optarg;
1138 			break;
1139 		case OPT_GTAGSCONF:
1140 		case OPT_GTAGSLABEL:
1141 		case 'C':
1142 			/* These options are already parsed in preparse_options(). */
1143 			break;
1144 		case OPT_INSERT_FOOTER:
1145 			insert_footer = optarg;
1146 			break;
1147 		case OPT_INSERT_HEADER:
1148 			insert_header = optarg;
1149 			break;
1150 		case OPT_HTML_HEADER:
1151 			{
1152 				STATIC_STRBUF(sb);
1153 				if (!test("r", optarg))
1154 					die("file '%s' not found.", optarg);
1155 				strbuf_clear(sb);
1156 				loadfile(optarg, sb);
1157 				html_header = strbuf_value(sb);
1158 			}
1159 			break;
1160 		case OPT_ITEM_ORDER:
1161 			item_order = optarg;
1162 			break;
1163 		case OPT_TABS:
1164 			if (atoi(optarg) > 0)
1165 				tabs = atoi(optarg);
1166 			else
1167 				die("--tabs option requires numeric value.");
1168                         break;
1169 		case OPT_NCOL:
1170 			if (atoi(optarg) > 0)
1171 				ncol = atoi(optarg);
1172 			else
1173 				die("--ncol option requires numeric value.");
1174                         break;
1175 		case OPT_TREE_VIEW:
1176 			tree_view = 1;
1177 			if (optarg)
1178 				tree_view_type = optarg;
1179 			break;
1180                 case 'a':
1181                         aflag++;
1182                         break;
1183                 case 'd':
1184 			strlimcpy(arg_dbpath, optarg, sizeof(arg_dbpath));
1185                         break;
1186                 case 'D':
1187 			dynamic = 1;
1188                         break;
1189                 case 'f':
1190                         fflag++;
1191                         break;
1192                 case 'F':
1193                         Fflag++;
1194                         break;
1195                 case 'g':
1196                         gflag++;
1197                         break;
1198                 case 'h':
1199 			definition_header = AFTER_HEADER;
1200 			if (optarg) {
1201 				if (!strcmp(optarg, "before"))
1202 					definition_header = BEFORE_HEADER;
1203 				else if (!strcmp(optarg, "right"))
1204 					definition_header = RIGHT_HEADER;
1205 				else if (!strcmp(optarg, "after"))
1206 					definition_header = AFTER_HEADER;
1207 				else
1208 					die("The option value of --func-header must be one of 'before', 'right' and 'after'.");
1209 			}
1210                         break;
1211                 case 'I':
1212                         Iflag++;
1213                         break;
1214                 case 'm':
1215 			main_func = optarg;
1216                         break;
1217                 case 'n':
1218                         nflag++;
1219 			if (optarg) {
1220 				if (atoi(optarg) > 0)
1221 					ncol = atoi(optarg);
1222 				else
1223 					die("The option value of --line-number must be numeric.");
1224 			}
1225                         break;
1226                 case 'o':
1227 			other_files = 1;
1228                         break;
1229                 case 's':
1230 			symbol = 1;
1231                         break;
1232                 case 'T':
1233 			table_flist = 1;
1234 			if (optarg) {
1235 				if (atoi(optarg) > 0)
1236 					flist_fields = atoi(optarg);
1237 				else
1238 					die("The option value of the --table-flist must be numeric.");
1239 			}
1240                         break;
1241                 case 't':
1242 			title = optarg;
1243                         break;
1244                 case 'q':
1245                         qflag++;
1246                         break;
1247                 case 'v':
1248                         vflag++;
1249                         break;
1250                 case 'w':
1251                         wflag++;
1252                         break;
1253                 default:
1254                         usage();
1255                         break;
1256 		}
1257 	}
1258 	if (qflag) {
1259 		vflag = 0;
1260 		setquiet();
1261 	}
1262 	if (vflag)
1263 		setverbose();
1264 	/*
1265 	 * Leaving everything to htags.
1266 	 * Htags selects popular options for you.
1267 	 */
1268 	if (suggest2)
1269 		suggest = 1;
1270 	if (suggest) {
1271 		int gtags_not_found = 0;
1272 		char dbpath[MAXPATHLEN];
1273 
1274 		aflag = Iflag = nflag = vflag = 1;
1275 		setverbose();
1276 		definition_header = AFTER_HEADER;
1277 		other_files = symbol = show_position = table_flist = fixed_guide = 1;
1278 		if (arg_dbpath[0]) {
1279 			if (!test("f", makepath(arg_dbpath, dbname(GTAGS), NULL)))
1280 				gtags_not_found = 1;
1281 		} else if (gtagsexist(".", dbpath, sizeof(dbpath), 0) == 0) {
1282 			gtags_not_found = 1;
1283 		}
1284 		if (gtags_not_found)
1285 			gflag = 1;
1286 	}
1287 	if (suggest2) {
1288 		Fflag = 1;				/* uses frame */
1289 		fflag = dynamic = 1;			/* needs a HTTP server */
1290 		auto_completion = tree_view = 1;	/* needs javascript */
1291 	}
1292 	if (call_file && !test("fr", call_file))
1293 		die("cflow file not found. '%s'", call_file);
1294 	if (callee_file && !test("fr", callee_file))
1295 		die("cflow file not found. '%s'", callee_file);
1296 	if (insert_header && !test("fr", insert_header))
1297 		die("page header file '%s' not found.", insert_header);
1298 	if (insert_footer && !test("fr", insert_footer))
1299 		die("page footer file '%s' not found.", insert_footer);
1300 	if (!fflag)
1301 		auto_completion = 0;
1302         argc -= optind;
1303         argv += optind;
1304         if (!av)
1305                 av = (argc > 0) ? *argv : NULL;
1306 
1307 	if (debug)
1308 		setdebug();
1309 	settabs(tabs);					/* setup tab skip */
1310         if (qflag) {
1311                 setquiet();
1312 		vflag = 0;
1313 	}
1314         if (show_version)
1315                 version(av, vflag);
1316         if (show_help)
1317                 help();
1318 	/*
1319 	 * Invokes gtags beforehand.
1320 	 */
1321 	if (gflag) {
1322 		STRBUF *sb = strbuf_open(0);
1323 
1324 		strbuf_puts(sb, gtags_path);
1325 		if (vflag)
1326 			strbuf_puts(sb, " -v");
1327 		if (wflag)
1328 			strbuf_puts(sb, " -w");
1329 		/*
1330 		 * Please see the release note of global-6.6.4.
1331 		if (suggest2 && enable_idutils && usable("mkid"))
1332 			strbuf_puts(sb, " -I");
1333 		*/
1334 		if (arg_dbpath[0]) {
1335 			strbuf_putc(sb, ' ');
1336 			strbuf_puts(sb, arg_dbpath);
1337 		}
1338 		if (system(strbuf_value(sb)))
1339 			die("cannot execute gtags(1) command.");
1340 		strbuf_close(sb);
1341 	}
1342 	/*
1343 	 * get dbpath.
1344 	 */
1345 	if (arg_dbpath[0]) {
1346 		strlimcpy(dbpath, arg_dbpath, sizeof(dbpath));
1347 	} else {
1348 		int status = setupdbpath(0);
1349 		if (status < 0)
1350 			die_with_code(-status, "%s", gtags_dbpath_error);
1351 		strlimcpy(dbpath, get_dbpath(), sizeof(dbpath));
1352 	}
1353 	if (!title) {
1354 		char *p = strrchr(cwdpath, sep);
1355 		title = p ? p + 1 : cwdpath;
1356 	}
1357 	if (cvsweb_url && test("d", "CVS"))
1358 		use_cvs_module = 1;
1359 	/*
1360 	 * decide directory in which we make hypertext.
1361 	 */
1362 	if (av) {
1363 		char realpath[MAXPATHLEN];
1364 
1365 		if (!test("dw", av))
1366 			die("'%s' is not writable directory.", av);
1367 		if (chdir(av) < 0)
1368 			die("directory '%s' not found.", av);
1369 		if (!vgetcwd(realpath, sizeof(realpath)))
1370 			die("cannot get current directory");
1371 		if (chdir(cwdpath) < 0)
1372 			die("cannot return to original directory.");
1373 		snprintf(distpath, sizeof(distpath), "%s/HTML", realpath);
1374 	} else {
1375 		snprintf(distpath, sizeof(distpath), "%s/HTML", cwdpath);
1376 	}
1377 	/*
1378 	 * Existence check of tag files.
1379 	 */
1380 	{
1381 		int i;
1382 		const char *path;
1383 		GTOP *gtop;
1384 
1385 		for (i = GPATH; i < GTAGLIM; i++) {
1386 			path = makepath(dbpath, dbname(i), NULL);
1387 			gtags_exist[i] = test("fr", path);
1388 		}
1389 		/*
1390 		 * Real GRTAGS includes virtual GSYMS.
1391 		 */
1392 		gtags_exist[GSYMS] = symbol ? 1 : 0;
1393 		if (!gtags_exist[GPATH] || !gtags_exist[GTAGS] || !gtags_exist[GRTAGS])
1394 			die("GPATH, GTAGS and/or GRTAGS not found. Please reexecute htags with the -g option.");
1395 		/*
1396 		 * version check.
1397 		 * Do nothing, but the version of tag file will be checked.
1398 		 */
1399 		gtop = gtags_open(dbpath, cwdpath, GTAGS, GTAGS_READ, 0);
1400 		gtags_close(gtop);
1401 		/*
1402 		 * Check whether GRTAGS is empty.
1403 		 */
1404 		gtop = gtags_open(dbpath, cwdpath, GRTAGS, GTAGS_READ, 0);
1405 		if (gtags_first(gtop, NULL, 0) == NULL)
1406 			grtags_is_empty = 1;
1407 		gtags_close(gtop);
1408 	}
1409 	/*
1410 	 * make dbpath absolute.
1411 	 */
1412 	{
1413 		char buf[MAXPATHLEN];
1414 		if (realpath(dbpath, buf) == NULL)
1415 			die("cannot get realpath of dbpath.");
1416 		strlimcpy(dbpath, buf, sizeof(dbpath));
1417 	}
1418 	/*
1419 	 * The older version (4.8.7 or former) of GPATH doesn't have files
1420          * other than source file. The oflag requires new version of GPATH.
1421 	 */
1422 	if (other_files) {
1423 		GFIND *gp = gfind_open(dbpath, NULL, 0, 0);
1424 		if (gp->version < 2)
1425 			die("GPATH is old format. Please remake it by invoking gtags(1).");
1426 		gfind_close(gp);
1427 	}
1428 	/*
1429 	 * for global(1) and gtags(1).
1430 	 */
1431 	set_env("GTAGSROOT", cwdpath);
1432 	set_env("GTAGSDBPATH", dbpath);
1433 	set_env("GTAGSLIBPATH", "");
1434 	/*------------------------------------------------------------------
1435 	 * MAKE FILES
1436 	 *------------------------------------------------------------------
1437 	 *       HTML/cgi-bin/global.cgi ... CGI program (1)
1438 	 *       HTML/cgi-bin/ghtml.cgi  ... unzip script (1)
1439 	 *       HTML/.htaccess          ... skeleton of .htaccess (1)
1440 	 *       HTML/help.html          ... help file (2)
1441 	 *       HTML/R/                 ... references (3)
1442 	 *       HTML/D/                 ... definitions (3)
1443 	 *       HTML/search.html        ... search index (4)
1444 	 *       HTML/defines.html       ... definitions index (5)
1445 	 *       HTML/defines/           ... definitions index (5)
1446 	 *       HTML/files/             ... file index (6)
1447 	 *       HTML/index.html         ... index file (7)
1448 	 *       HTML/mains.html         ... main index (8)
1449 	 *       HTML/null.html          ... main null html (8)
1450 	 *       HTML/S/                 ... source files (9)
1451 	 *       HTML/I/                 ... include file index (9)
1452 	 *       HTML/rebuild.sh         ... rebuild script (10)
1453 	 *       HTML/style.css          ... style sheet (11)
1454 	 *------------------------------------------------------------------
1455 	 */
1456 	/* for clean up */
1457 	signal_setup();
1458 	sethandler(clean);
1459 
1460         HTML = normal_suffix;
1461 
1462 	message("[%s] Htags started", now());
1463 	init_statistics();
1464 	/*
1465 	 * (#) check if GTAGS, GRTAGS is the latest.
1466 	 */
1467 	if (get_dbpath())
1468 		message(" Using %s/GTAGS.", get_dbpath());
1469 	if (grtags_is_empty)
1470 		message(" GRTAGS is empty.");
1471 	if (gpath_open(dbpath, 0) < 0)
1472 		die("GPATH not found.");
1473 	if (!w32) {
1474 		/* UNDER CONSTRUCTION */
1475 	}
1476 	if (auto_completion || tree_view) {
1477 		STATIC_STRBUF(sb);
1478 		strbuf_clear(sb);
1479 		strbuf_puts_nl(sb, "<script type='text/javascript' src='js/jquery.js'></script>");
1480 		if (auto_completion)
1481 			loadfile(makepath(datadir, "gtags/jscode_suggest", NULL), sb);
1482 		if (tree_view)
1483 			loadfile(makepath(datadir, "gtags/jscode_treeview", NULL), sb);
1484 		jscode = strbuf_value(sb);
1485 	}
1486 	/*
1487 	 * (0) make directories
1488 	 */
1489 	message("[%s] (0) making directories ...", now());
1490 	if (!test("d", distpath))
1491 		if (mkdir(distpath, 0777) < 0)
1492 			die("cannot make directory '%s'.", distpath);
1493 	make_directory_in_distpath("files");
1494 	make_directory_in_distpath("defines");
1495 	make_directory_in_distpath(SRCS);
1496 	make_directory_in_distpath(INCS);
1497 	make_directory_in_distpath(INCREFS);
1498 	if (!dynamic) {
1499 		make_directory_in_distpath(DEFS);
1500 		make_directory_in_distpath(REFS);
1501 		if (symbol)
1502 			make_directory_in_distpath(SYMS);
1503 	}
1504 	if (fflag || dynamic)
1505 		make_directory_in_distpath("cgi-bin");
1506 	if (Iflag)
1507 		make_directory_in_distpath("icons");
1508 	if (auto_completion || tree_view)
1509 		 make_directory_in_distpath("js");
1510 	/*
1511 	 * (1) make CGI program
1512 	 */
1513 	if (fflag || dynamic) {
1514 		char cgidir[MAXPATHLEN];
1515 
1516 		snprintf(cgidir, sizeof(cgidir), "%s/cgi-bin", distpath);
1517 		message("[%s] (1) making CGI program ...", now());
1518 		if (fflag || dynamic)
1519 			makeprogram(cgidir, "global.cgi", 0755);
1520 		if (auto_completion)
1521 			makeprogram(cgidir, "completion.cgi", 0755);
1522 		makehtaccess(".htaccess", 0644);
1523 	} else {
1524 		message("[%s] (1) making CGI program ...(skipped)", now());
1525 	}
1526 	if (av) {
1527 		const char *path = makepath(distpath, "GTAGSROOT", NULL);
1528 		FILE *op = fopen(path, "w");
1529 		if (op == NULL)
1530 			die("cannot make file '%s'.", path);
1531 		fputs(cwdpath, op);
1532 		fputc('\n', op);
1533 		fclose(op);
1534 	}
1535 	/*
1536 	 * (2) make help file
1537 	 */
1538 	message("[%s] (2) making help.html ...", now());
1539 	makehelp("help.html");
1540 	/*
1541 	 * (#) load GPATH
1542 	 */
1543 	load_gpath(dbpath);
1544 
1545 	/*
1546 	 * (3) make function entries (D/ and R/)
1547 	 *     MAKING TAG CACHE
1548 	 */
1549 	message("[%s] (3) making tag lists ...", now());
1550 	cache_open();
1551 	tim = statistics_time_start("Time of making tag lists");
1552 	func_total = makedupindex();
1553 	statistics_time_end(tim);
1554 	message("Total %d functions.", func_total);
1555 	/*
1556 	 * (4) search index. (search.html)
1557 	 */
1558 	if (Fflag && fflag) {
1559 		message("[%s] (4) making search index ...", now());
1560 		makesearchindex("search.html");
1561 	}
1562 	{
1563 		STRBUF *defines = strbuf_open(0);
1564 		STRBUF *files = strbuf_open(0);
1565 
1566 		/*
1567 		 * (5) make definition index (defines.html and defines/)
1568 		 *     PRODUCE @defines
1569 		 */
1570 		message("[%s] (5) making definition index ...", now());
1571 		tim = statistics_time_start("Time of making definition index");
1572 		func_total = makedefineindex("defines.html", func_total, defines);
1573 		statistics_time_end(tim);
1574 		message("Total %d functions.", func_total);
1575 		/*
1576 		 * (6) make file index (files.html and files/)
1577 		 *     PRODUCE @files, %includes
1578 		 */
1579 		message("[%s] (6) making file index ...", now());
1580 		init_inc();
1581 		tim = statistics_time_start("Time of making file index");
1582 		file_total = makefileindex("files.html", files);
1583 		statistics_time_end(tim);
1584 		message("Total %d files.", file_total);
1585 		html_count += file_total;
1586 		/*
1587 		 * (7) make call tree using cflow(1)'s output (cflow.html)
1588 		 */
1589 		if (call_file || callee_file) {
1590 			message("[%s] (7) making cflow index ...", now());
1591 			tim = statistics_time_start("Time of making cflow index");
1592 			if (call_file)
1593 				if (makecflowindex("call.html", call_file) < 0)
1594 					call_file = NULL;
1595 			if (callee_file)
1596 				if (makecflowindex("callee.html", callee_file) < 0)
1597 					callee_file = NULL;
1598 			statistics_time_end(tim);
1599 		}
1600 		/*
1601 		 * [#] make include file index.
1602 		 */
1603 		message("[%s] (#) making include file index ...", now());
1604 		tim = statistics_time_start("Time of making include file index");
1605 		makeincludeindex();
1606 		statistics_time_end(tim);
1607 		/*
1608 		 * [#] make a common part for mains.html and index.html
1609 		 *     USING @defines @files
1610 		 */
1611 		message("[%s] (#) making a common part ...", now());
1612 		index = makecommonpart(title, strbuf_value(defines), strbuf_value(files));
1613 
1614 		strbuf_close(defines);
1615 		strbuf_close(files);
1616 	}
1617 	/*
1618 	 * (7)make index file (index.html)
1619 	 */
1620 	message("[%s] (7) making index file ...", now());
1621 	makeindex("index.html", title, index);
1622 	/*
1623 	 * (8) make main index (mains.html)
1624 	 */
1625 	message("[%s] (8) making main index ...", now());
1626 	makemainindex("mains.html", index);
1627 	/*
1628 	 * (9) make HTML files (SRCS/)
1629 	 *     USING TAG CACHE, %includes and anchor database.
1630 	 */
1631 	message("[%s] (9) making hypertext from source code ...", now());
1632 	tim = statistics_time_start("Time of making hypertext");
1633 	makehtml(file_total);
1634 	statistics_time_end(tim);
1635 	/*
1636 	 * (10) rebuild script. (rebuild.sh)
1637 	 *
1638 	 * Don't grant execute permission to rebuild script.
1639 	 */
1640 	makerebuild("rebuild.sh");
1641 	if (chmod(makepath(distpath, "rebuild.sh", NULL), 0640) < 0)
1642 		die("cannot chmod rebuild script.");
1643 	/*
1644 	 * (11) style sheet file (style.css)
1645 	 */
1646 	if (enable_xhtml) {
1647 		char src[MAXPATHLEN];
1648 		char dist[MAXPATHLEN];
1649 		snprintf(src, sizeof(src), "%s/gtags/style.css", datadir);
1650 		snprintf(dist, sizeof(dist), "%s/style.css", distpath);
1651 		copyfile(src, dist);
1652 	}
1653 	if (auto_completion || tree_view) {
1654 		char src[MAXPATHLEN];
1655 		char dist[MAXPATHLEN];
1656 
1657 		snprintf(src, sizeof(src), "%s/gtags/jquery", datadir);
1658 		snprintf(dist, sizeof(dist), "%s/js", distpath);
1659 		copydirectory(src, dist);
1660 		snprintf(src, sizeof(src), "%s/gtags/jquery/images", datadir);
1661 		snprintf(dist, sizeof(dist), "%s/js/images", distpath);
1662 		copydirectory(src, dist);
1663 	}
1664 	message("[%s] Done.", now());
1665 	if (vflag && (fflag || dynamic || auto_completion)) {
1666 		message("\n[Information]\n");
1667 		message(" o Htags was invoked with the -f, -c, -D or --auto-completion option. You should");
1668 		message("   start http server so that cgi-bin/*.cgi is executed as a CGI script.");
1669 		message("   Use of htags-server(1) is recommended.");
1670  		message("\n If you are using Apache, 'HTML/.htaccess' might be helpful for you.\n");
1671 		message(" Good luck!\n");
1672 	}
1673 	if (Iflag) {
1674 		char src[MAXPATHLEN];
1675 		char dist[MAXPATHLEN];
1676 
1677 		snprintf(src, sizeof(src), "%s/gtags/icons", datadir);
1678 		snprintf(dist, sizeof(dist), "%s/icons", distpath);
1679 		copydirectory(src, dist);
1680 	}
1681 	gpath_close();
1682 	/*
1683 	 * Print statistics information.
1684 	 */
1685 	print_statistics(statistics);
1686 	clean();
1687 	return 0;
1688 }
1689