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 "<",
162 ">",
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