/* */
This source file includes following definitions.
- getpath
- ungetpath
- extract_lastname
- lastpart
- dirpart
- localpath
- appendslash
- removedotslash
- insert_comma
- print_directory
- print_directory_header
- print_directory_footer
- print_file_name
- print_directory_name
- makefileindex
- makeincludeindex
1 /*
2 * Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 * 2006, 2010
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 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 #include <ctype.h>
25 #include <stdio.h>
26 #ifdef STDC_HEADERS
27 #include <stdlib.h>
28 #endif
29 #ifdef HAVE_STRING_H
30 #include <string.h>
31 #else
32 #include <strings.h>
33 #endif
34
35 #include "global.h"
36 #include "incop.h"
37 #include "htags.h"
38 #include "path2url.h"
39 #include "common.h"
40
41 /*----------------------------------------------------------------------*/
42 /* Find list procedures */
43 /*----------------------------------------------------------------------*/
44 static const char *getpath(void);
45 static void ungetpath(void);
46 static GFIND *gp;
47 static int retry;
48 /**
49 * get a path from input stream.
50 *
51 * @note Each path name must start with @CODE{"./"}.
52 */
53 static const char *
54 getpath(void)
55 {
56 static const char *buff;
57
58 if (!retry) {
59 /* skip README or ChangeLog unless the -o option specified. */
60 do {
61 buff = gfind_read(gp);
62 } while (buff && gp->type == GPATH_OTHER && !other_files);
63 }
64 retry = 0;
65 return buff;
66 }
67 /**
68 * push back a path name.
69 */
70 static void
71 ungetpath(void)
72 {
73 retry = 1;
74 }
75 /*----------------------------------------------------------------------*/
76 /* Path name operation procedures */
77 /*----------------------------------------------------------------------*/
78 static const char *extract_lastname(const char *, int);
79 static const char *lastpart(const char *);
80 static const char *dirpart(const char *, char *);
81 static const char *localpath(const char *, char *);
82 static const char *appendslash(const char *);
83 static const char *insert_comma(unsigned int);
84
85 /**
86 * extract_lastname: extract the last name of include line.
87 *
88 * @param[in] image source image of include
89 * @param[in] is_php 1: is @NAME{PHP} source
90 * @return last name or @VAR{NULL} on error.
91 */
92 static const char *
93 extract_lastname(const char *image, int is_php)
94 {
95 static char buf[MAXBUFLEN];
96 const char *p;
97 char *q;
98 int sep;
99
100 /*
101 * C: #include <xxx/yyy/zzz.h>
102 * #include "xxx/yyy/zzz.h"
103 * PHP: include('xxx/yyy/zzz');
104 */
105 p = image;
106 while (*p && isspace((unsigned char)*p)) /* skip space */
107 p++;
108 if (!*p)
109 return NULL;
110 if (*p == '#') {
111 if (is_php)
112 return NULL;
113 p++;
114 while (*p && isspace((unsigned char)*p)) /* skip space */
115 p++;
116 if (!*p)
117 return NULL;
118 }
119 /*
120 * If match to one of the include keywords then points
121 * the following character of the keyword.
122 * p
123 * v
124 * ... include ....
125 */
126 if (is_php) {
127 if ((p = locatestring(p, "include", MATCH_AT_FIRST)) == NULL)
128 return NULL;
129 } else {
130 char *q;
131
132 if (((q = locatestring(p, "include_next", MATCH_AT_FIRST)) == NULL) &&
133 ((q = locatestring(p, "import", MATCH_AT_FIRST)) == NULL) &&
134 ((q = locatestring(p, "include", MATCH_AT_FIRST)) == NULL))
135 return NULL;
136 p = q;
137 }
138 while (*p && isspace((unsigned char)*p)) /* skip space */
139 p++;
140 if (is_php && *p == '(') {
141 p++;
142 while (*p && isspace((unsigned char)*p)) /* skip space */
143 p++;
144 }
145 sep = *p;
146 if (is_php) {
147 if (sep != '\'' && sep != '"')
148 return NULL;
149 } else {
150 if (sep != '<' && sep != '"')
151 return NULL;
152 }
153 if (sep == '<')
154 sep = '>';
155 p++;
156 if (!*p)
157 return NULL;
158 q = buf;
159 while (*p && *p != '\n' && *p != sep)
160 *q++ = *p++;
161 *q = '\0';
162 if (*p == sep) {
163 q = locatestring(buf, "/", MATCH_LAST);
164 if (q)
165 q++;
166 else
167 q = buf;
168 return q;
169 }
170 return NULL;
171 }
172 /**
173 * get the last part of the @a path.
174 *
175 * @param[in] path path name
176 * @return last part
177 *
178 * @par Examples:
179 * @code
180 * lastpart("a/b") => "b"
181 * lastpart("a") => "a"
182 * @endcode
183 */
184 static const char *
185 lastpart(const char *path)
186 {
187 const char *p = strrchr(path, '/');
188
189 return p ? p + 1 : path;
190 }
191 /**
192 * get the directory part of the @a path.
193 *
194 * @param[in] path path name
195 * @param[out] result result buffer
196 * @return directory part
197 *
198 * @par Examples:
199 * @code
200 * dirpart("a/b/c") => "a/b"
201 * @endcode
202 */
203 static const char *
204 dirpart(const char *path, char *result)
205 {
206 char *p = result;
207 const char *q = path, *limit = strrchr(path, '/');
208
209 while (q < limit)
210 *p++ = *q++;
211 *p = '\0';
212 return result;
213 }
214 /**
215 * get the local path name if the path is under the @a dir.
216 *
217 * @param[in] path path name
218 * @param[in] dir directory name
219 * @return local path name or @VAR{NULL}
220 *
221 * @par Examples:
222 * @code
223 * localpath("a/b/c", "a/b") => "c"
224 * localpath("a/b/c/d", "a/b") => "c/d"
225 * localpath("a/b/c", "a/d") => NULL
226 * @endcode
227 */
228 static const char *
229 localpath(const char *path, char *dir)
230 {
231 int length = strlen(dir);
232
233 if (!strncmp(path, dir, length) && *(path + length) == '/')
234 return path + length + 1;
235 return NULL;
236 }
237 /**
238 * append @CODE{'/'} after the path name
239 *
240 * @param[in] path path name
241 * @return appended path name
242 *
243 * @note Doesn't check if ends with a @CODE{'/'} already.
244 *
245 * @par Examples:
246 * @code
247 * appendslash("a") => "a/"
248 * @endcode
249 */
250 static const char *
251 appendslash(const char *path)
252 {
253 STATIC_STRBUF(sb);
254
255 strbuf_clear(sb);
256 strbuf_puts(sb, path);
257 strbuf_putc(sb, '/');
258 return strbuf_value(sb);
259 }
260 /**
261 * remove @CODE{"./"} at the head of the path name
262 *
263 * @param[in] path path name
264 * @return removed path name
265 *
266 * @par Examples:
267 * @code
268 * removedotslash("./a") => "a"
269 * @endcode
270 */
271 static const char *
272 removedotslash(const char *path)
273 {
274 return (*path == '.' && *(path + 1) == '/') ? path + 2 : path;
275 }
276 /**
277 * insert_comma: insert comma to the number.
278 *
279 * @param[in] n number
280 * @return edited string
281 *
282 * @par Examples:
283 * @code
284 * 10000 => 10,000
285 * @endcode
286 */
287 static const char *
288 insert_comma(unsigned int n)
289 {
290 #define RESULTSIZE 15
291 #define INTERVAL 3
292 static char result[RESULTSIZE];
293 int i = RESULTSIZE;
294 int count = 0;
295
296 if (n == 0)
297 return (const char *)"0";
298 if (n > 1000000000)
299 goto giveup;
300 result[--i] = '\0';
301 for (; n > 0; n = n / 10) {
302 /*
303 * Buffer overflow. This is not important even if occurring.
304 */
305 if (i <= 0)
306 goto giveup;
307 if (count && count % INTERVAL == 0)
308 result[--i] = ',';
309 result[--i] = n % 10 + '0';
310 count++;
311 }
312 return (const char *)&result[i];
313 giveup:
314 snprintf(result, sizeof(result), "*****");
315 return (const char *)result;
316 }
317 /*----------------------------------------------------------------------*/
318 /* Generating file index */
319 /*----------------------------------------------------------------------*/
320 static int print_directory(int, char *);
321 static void print_directory_header(FILE *, int, const char *);
322 static void print_directory_footer(FILE *, int, const char *);
323 static const char *print_file_name(int, const char *);
324 static const char *print_directory_name(int, const char *, int);
325
326 FILE *FILEMAP;
327 STRBUF *files;
328 const char *indexlink;
329 regex_t is_include_file;
330 int src_count;
331
332 /**
333 * @fn static int print_directory(int level, char *basedir)
334 *
335 * print contents of one directory and the descendant. (@STRONG{recursively called})
336 *
337 * @param[in] level directory nest level
338 * @param[in,out] basedir current directory
339 *
340 * This function read find style records, and print directory tree.
341 */
342 /**
343 * File list of the top level directory (when @CODE{level == 0}) is not written
344 * to a file directly. Instead, it is written to string buffer, because
345 * it appears in some places.
346 */
347 #define PUT(s) do { \
348 if (level == 0) { \
349 if (tree_view == 0) \
350 strbuf_puts(files, s); \
351 } else \
352 fputs(s, op); \
353 } while (0)
354
355 static int
356 print_directory(int level, char *basedir)
357 {
358 const char *path;
359 FILEOP *fileop = NULL;
360 FILE *op = NULL;
361 int flist_items = 0;
362 int count = 0;
363
364 if (level > 0) {
365 char name[MAXPATHLEN];
366
367 snprintf(name, sizeof(name), "%s/files/%s.%s", distpath, path2fid(basedir), HTML);
368 fileop = open_output_file(name, cflag);
369 op = get_descripter(fileop);
370 print_directory_header(op, level, basedir);
371 if (tree_view) {
372 char *target = (Fflag) ? "mains" : "_top";
373
374 strbuf_puts(files, dir_begin);
375 strbuf_puts(files, gen_href_begin_with_title_target("files", path2fid(basedir), HTML, NULL, NULL, target));
376 strbuf_puts(files, full_path ? removedotslash(basedir) : lastpart(basedir));
377 strbuf_puts(files, gen_href_end());
378 strbuf_puts(files, dir_end);
379 strbuf_puts(files, "\n<ul>\n");
380 }
381 }
382 while ((path = getpath()) != NULL) {
383 const char *p, *local = localpath(path, basedir);
384
385 /*
386 * Path is outside of basedir.
387 */
388 if (local == NULL) {
389 ungetpath(); /* read again by upper level print_directory(). */
390 break;
391 }
392 /*
393 * Path is inside of basedir.
394 */
395 else {
396 char *slash = strchr(local, '/');
397
398 if (table_flist && flist_items++ % flist_fields == 0)
399 PUT(fline_begin);
400 /*
401 * Print directory.
402 */
403 if (slash) {
404 int baselen = strlen(basedir);
405 char *q, *last = basedir + baselen;
406 int subcount;
407
408 if (baselen + 1 + (slash - local) > MAXPATHLEN) {
409 fprintf(stderr, "Too long path name.\n");
410 exit(1);
411 }
412 /*
413 * Append new directory to the basedir.
414 */
415 p = local;
416 q = last;
417 *q++ = '/';
418 while (p < slash)
419 *q++ = *p++;
420 *q = '\0';
421 /*
422 * print tree for this directory.
423 */
424 ungetpath(); /* read again by lower level print_directory(). */
425 subcount = print_directory(level + 1, basedir);
426 PUT(print_directory_name(level, basedir, subcount));
427 count += subcount;
428 /*
429 * Shrink the basedir.
430 */
431 *last = '\0';
432 }
433 /*
434 * Print file.
435 */
436 else {
437 const char *file_name = print_file_name(level, path);
438
439 if (tree_view) {
440 int size = filesize(path);
441 char *target = (Fflag) ? "mains" : "_top";
442 char tips[80];
443
444 if (size > 1)
445 snprintf(tips, sizeof(tips), "%s bytes", insert_comma(size));
446 else
447 snprintf(tips, sizeof(tips), "%s byte", insert_comma(size));
448 strbuf_sprintf(files, "%s%s%s%s%s\n",
449 file_begin,
450 gen_href_begin_with_title_target(SRCS, path2fid(path), HTML, NULL, tips, target),
451 full_path ? removedotslash(path) : lastpart(path),
452 gen_href_end(),
453 file_end);
454 }
455 PUT(file_name);
456 if (filemap_file)
457 fprintf(FILEMAP, "%s\t%s/%s.%s\n", removedotslash(path), SRCS, path2fid(path), HTML);
458 count++;
459 }
460 if (table_flist && flist_items % flist_fields == 0)
461 PUT(fline_end);
462 }
463 }
464 if (flist_items % flist_fields != 0)
465 PUT(fline_end);
466 if (level > 0) {
467 print_directory_footer(op, level, basedir);
468 close_file(fileop);
469 if (tree_view)
470 strbuf_puts(files, "</ul>\n");
471 }
472 html_count++;
473 return count;
474 }
475 /**
476 * print directory header.
477 *
478 * @param[in] op file index
479 * @param[in] level 1,2...
480 * @param[in] dir directory name
481 */
482 static void
483 print_directory_header(FILE *op, int level, const char *dir)
484 {
485 STATIC_STRBUF(sb);
486 const char *top = (Fflag && !tree_view) ? "../files" : "../mains";
487
488 if (level == 0)
489 die("print_directory_header: internal error.");
490 strbuf_clear(sb);
491 strbuf_puts(sb, removedotslash(dir));
492 strbuf_putc(sb, '/');
493 fputs_nl(gen_page_begin(strbuf_value(sb), SUBDIR), op);
494 fputs_nl(body_begin, op);
495
496 strbuf_clear(sb);
497 strbuf_sprintf(sb, "%s%sroot%s/", header_begin, gen_href_begin(NULL, top, normal_suffix, NULL), gen_href_end());
498 fputs(strbuf_value(sb), op);
499 {
500 char path[MAXPATHLEN];
501 char *p, *q;
502
503 strlimcpy(path, dir, sizeof(path));
504 for (p = path + 1; p != NULL; p = strchr(p, '/')) {
505 int save = 0;
506
507 q = ++p;
508 while (*q && *q != '/')
509 q++;
510 save = *q;
511 if (*q == '/')
512 *q = '\0';
513 if (save == '/')
514 fputs(gen_href_begin(NULL, path2fid(path), HTML, NULL), op);
515 fputs(p, op);
516 if (save == '/')
517 fputs(gen_href_end(), op);
518 *q = save;
519 fputc('/', op);
520 }
521 }
522 fputs_nl(header_end, op);
523 {
524 char parentdir[MAXPATHLEN];
525 const char *suffix, *parent;
526
527 (void)dirpart(dir, parentdir);
528 if (level == 1) {
529 parent = top;
530 suffix = normal_suffix;
531 } else {
532 parent = path2fid(parentdir);
533 suffix = HTML;
534 }
535 fputs(gen_href_begin_with_title(NULL, parent, suffix, NULL, "Parent Directory"), op);
536 }
537 if (Iflag)
538 fputs(gen_image(PARENT, back_icon, ".."), op);
539 else
540 fputs("[..]", op);
541 fputs_nl(gen_href_end(), op);
542 if (table_flist)
543 fputs_nl(flist_begin, op);
544 else if (!no_order_list)
545 fputs_nl(list_begin, op);
546 else {
547 fputs(br, op);
548 fputs_nl(br, op);
549 }
550 }
551 /**
552 * print directory footer.
553 *
554 * @param[in] op file index
555 * @param[in] level 1,2...
556 * @param[in] dir directory name
557 */
558 static void
559 print_directory_footer(FILE *op, int level, const char *dir)
560 {
561 const char *parent, *suffix;
562 char parentdir[MAXPATHLEN];
563 const char *top = (Fflag && !tree_view) ? "../files" : "../mains";
564
565 if (level == 0)
566 die("print_directory_footer: internal error.");
567 (void)dirpart(dir, parentdir);
568 if (level == 1) {
569 parent = top;
570 suffix = normal_suffix;
571 } else {
572 parent = path2fid(parentdir);
573 suffix = HTML;
574 }
575 if (table_flist)
576 fputs_nl(flist_end, op);
577 else if (!no_order_list)
578 fputs_nl(list_end, op);
579 else
580 fputs_nl(br, op);
581 fputs(gen_href_begin_with_title(NULL, parent, suffix, NULL, "Parent Directory"), op);
582 if (Iflag)
583 fputs(gen_image(PARENT, back_icon, ".."), op);
584 else
585 fputs("[..]", op);
586 fputs_nl(gen_href_end(), op);
587 fputs_nl(body_end, op);
588 fputs_nl(gen_page_end(), op);
589 }
590 /**
591 * print file name.
592 *
593 * @param[in] level 0,1,2...
594 * @param[in] path path of the file
595 */
596 static const char *
597 print_file_name(int level, const char *path)
598 {
599 STATIC_STRBUF(sb);
600 char *target = (Fflag) ? "mains" : "_top";
601 int size = filesize(path);
602 char tips[80];
603
604 message(" [%d] adding %s", ++src_count, removedotslash(path));
605 /*
606 * We assume the file which has one of the following suffixes
607 * as a candidate of include file.
608 *
609 * C: .h
610 * C++: .hxx, .hpp, .H, .hh
611 * PHP: .inc.php
612 */
613 if (regexec(&is_include_file, path, 0, 0, 0) == 0)
614 put_inc(lastpart(path), path, src_count);
615 strbuf_clear(sb);
616 if (table_flist)
617 strbuf_puts(sb, fitem_begin);
618 else if (!no_order_list)
619 strbuf_puts(sb, item_begin);
620 if (size > 1)
621 snprintf(tips, sizeof(tips), "%s bytes", insert_comma(size));
622 else
623 snprintf(tips, sizeof(tips), "%s byte", insert_comma(size));
624 strbuf_puts(sb, gen_href_begin_with_title_target(level == 0 ? SRCS: upperdir(SRCS),
625 path2fid(path), HTML, NULL, tips, target));
626 if (Iflag) {
627 const char *lang, *suffix, *text_icon;
628
629 if ((suffix = locatestring(path, ".", MATCH_LAST)) != NULL
630 && (lang = decide_lang(suffix)) != NULL
631 && (strcmp(lang, "c") == 0 || strcmp(lang, "cpp") == 0
632 || strcmp(lang, "yacc") == 0))
633 text_icon = c_icon;
634 else
635 text_icon = file_icon;
636 strbuf_puts(sb, gen_image(level == 0 ? CURRENT : PARENT, text_icon, removedotslash(path)));
637 strbuf_puts(sb, quote_space);
638 }
639 strbuf_puts(sb, full_path ? removedotslash(path) : lastpart(path));
640 strbuf_puts(sb, gen_href_end());
641 if (table_flist)
642 strbuf_puts(sb, fitem_end);
643 else if (!no_order_list)
644 strbuf_puts(sb, item_end);
645 else
646 strbuf_puts(sb, br);
647 strbuf_putc(sb, '\n');
648 return (const char *)strbuf_value(sb);
649 }
650 /**
651 * print directory name.
652 *
653 * @param[in] level 0,1,2...
654 * @param[in] path path of the directory
655 * @param[in] count number of files in this directory
656 */
657 static const char *
658 print_directory_name(int level, const char *path, int count)
659 {
660 STATIC_STRBUF(sb);
661 char tips[80];
662
663 if (count > 1)
664 snprintf(tips, sizeof(tips), "%d files", count);
665 else
666 snprintf(tips, sizeof(tips), "%d file", count);
667 path = removedotslash(path);
668 strbuf_clear(sb);
669 if (table_flist)
670 strbuf_puts(sb, fitem_begin);
671 else if (!no_order_list)
672 strbuf_puts(sb, item_begin);
673 strbuf_puts(sb, gen_href_begin_with_title(level == 0 ? "files" : NULL,
674 path2fid(path), HTML, NULL, tips));
675 if (Iflag) {
676 strbuf_puts(sb, gen_image(level == 0 ? CURRENT : PARENT, dir_icon, appendslash(path)));
677 strbuf_puts(sb, quote_space);
678 }
679 strbuf_sprintf(sb, "%s/%s", lastpart(path), gen_href_end());
680 if (table_flist)
681 strbuf_puts(sb, fitem_end);
682 else if (!no_order_list)
683 strbuf_puts(sb, item_end);
684 else
685 strbuf_puts(sb, br);
686 strbuf_putc(sb, '\n');
687 return (const char *)strbuf_value(sb);
688 }
689 /**
690 * makefileindex: make file index.
691 *
692 * @param[in] file output file name
693 * @param[out] a_files top level file index
694 */
695 int
696 makefileindex(const char *file, STRBUF *a_files)
697 {
698 STATIC_STRBUF(sb);
699 FILE *filesop;
700 int flags = REG_EXTENDED;
701 /*
702 * Basedir is a directory to which we are paying attention on each
703 * occasion. It starts with ".", grows and shrink according to the
704 * progress of processing. It isn't copied each every recursive call
705 * not to waste the stack.
706 */
707 char basedir[MAXPATHLEN];
708
709 /*
710 * Initialize data.
711 */
712 indexlink = (Fflag) ? "../files" : "../mains";
713 src_count = 0;
714
715 gp = gfind_open(dbpath, NULL, other_files ? GPATH_BOTH : GPATH_SOURCE);
716 /*
717 * for collecting include files.
718 */
719 if (w32)
720 flags |= REG_ICASE;
721 strbuf_clear(sb);
722 strbuf_puts(sb, "\\.(");
723 {
724 const char *p = include_file_suffixes;
725 int c;
726
727 while ((c = (unsigned char)*p++) != '\0') {
728 if (isregexchar(c))
729 strbuf_putc(sb, '\\');
730 else if (c == ',')
731 c = '|';
732 strbuf_putc(sb, c);
733 }
734 }
735 strbuf_puts(sb, ")$");
736 if (regcomp(&is_include_file, strbuf_value(sb), flags) != 0)
737 die("cannot compile regular expression '%s'.", strbuf_value(sb));
738
739 /*
740 * Write to files.html.
741 */
742 if ((filesop = fopen(makepath(distpath, file, NULL), "w")) == NULL)
743 die("cannot open file '%s'.", file);
744 fputs_nl(gen_page_index_begin(title_file_index, jscode), filesop);
745 fputs_nl(body_begin, filesop);
746 fputs(header_begin, filesop);
747 fputs(gen_href_begin(NULL, "files", normal_suffix, NULL), filesop);
748 fputs(title_file_index, filesop);
749 fputs(gen_href_end(), filesop);
750 fputs_nl(header_end, filesop);
751 if (tree_view) {
752 fputs_nl(tree_control, filesop);
753 if (tree_view_type) {
754 fprintf(filesop, tree_begin_using, tree_view_type);
755 fputc('\n', filesop);
756 } else {
757 fputs_nl(tree_begin, filesop);
758 }
759 } else if (table_flist)
760 fputs_nl(flist_begin, filesop);
761 else if (!no_order_list)
762 fputs_nl(list_begin, filesop);
763 FILEMAP = NULL;
764 if (filemap_file) {
765 if (!(FILEMAP = fopen(makepath(distpath, "FILEMAP", NULL), "w")))
766 die("cannot open '%s'.", makepath(distpath, "FILEMAP", NULL));
767 }
768 /*
769 * print whole directory tree.
770 */
771 files = a_files;
772 strcpy(basedir, ".");
773
774 (void)print_directory(0, basedir);
775 if (tree_view)
776 strbuf_puts(files, tree_end);
777
778 if (filemap_file)
779 fclose(FILEMAP);
780 gfind_close(gp);
781 regfree(&is_include_file);
782
783 fputs(strbuf_value(files), filesop);
784 if (tree_view)
785 fputs_nl(tree_end, filesop);
786 else if (table_flist)
787 fputs_nl(flist_end, filesop);
788 else if (!no_order_list)
789 fputs_nl(list_end, filesop);
790 fputs_nl(body_end, filesop);
791 fputs_nl(gen_page_end(), filesop);
792 fclose(filesop);
793 html_count++;
794 return src_count;
795 }
796 /*----------------------------------------------------------------------*/
797 /* Main body of generating include file index */
798 /*----------------------------------------------------------------------*/
799 void
800 makeincludeindex(void)
801 {
802 FILE *PIPE;
803 STRBUF *input = strbuf_open(0);
804 char *ctags_x;
805 struct data *inc;
806 char *target = (Fflag) ? "mains" : "_top";
807 char command[MAXFILLEN];
808
809 /*
810 * Pick up include pattern.
811 *
812 * C: #include "xxx.h"
813 * PHP: include("xxx.inc.php");
814 */
815 /*
816 * Unlike Perl regular expression, POSIX regular expression doesn't support C-style escape sequence.
817 * Therefore, we can not use "\\t" here.
818 */
819 snprintf(command, sizeof(command), PQUOTE "%s -gnx --encode-path=\" \t\" \"^[ \t]*(#[ \t]*(import|include)|include[ \t]*\\()\"" PQUOTE, quote_shell(global_path));
820 if ((PIPE = popen(command, "r")) == NULL)
821 die("cannot fork.");
822 strbuf_reset(input);
823 while ((ctags_x = strbuf_fgets(input, PIPE, STRBUF_NOCRLF)) != NULL) {
824 SPLIT ptable;
825 char buf[MAXBUFLEN];
826 int is_php = 0;
827 const char *last, *lang, *suffix;
828
829 if (split(ctags_x, 4, &ptable) < 4) {
830 recover(&ptable);
831 die("too small number of parts in makefileindex().");
832 }
833 if ((suffix = locatestring(ptable.part[PART_PATH].start, ".", MATCH_LAST)) != NULL
834 && (lang = decide_lang(suffix)) != NULL
835 && strcmp(lang, "php") == 0)
836 is_php = 1;
837 last = extract_lastname(ptable.part[PART_LINE].start, is_php);
838 if (last == NULL || (inc = get_inc(last)) == NULL)
839 continue;
840 recover(&ptable);
841 /*
842 * s/^[^ \t]+/$last/;
843 */
844 {
845 const char *p;
846 char *q = buf;
847
848 for (p = last; *p; p++)
849 *q++ = *p;
850 for (p = ctags_x; *p && *p != ' ' && *p != '\t'; p++)
851 ;
852 for (; *p; p++)
853 *q++ = *p;
854 *q = '\0';
855 }
856 put_included(inc, buf);
857 }
858 if (pclose(PIPE) != 0)
859 die("terminated abnormally.");
860
861 for (inc = first_inc(); inc; inc = next_inc()) {
862 const char *last = inc->name;
863 int no = inc->id;
864 FILEOP *fileop_INCLUDE;
865 FILE *INCLUDE;
866
867 if (inc->count > 1) {
868 char path[MAXPATHLEN];
869
870 snprintf(path, sizeof(path), "%s/%s/%d.%s", distpath, INCS, no, HTML);
871 fileop_INCLUDE = open_output_file(path, cflag);
872 INCLUDE = get_descripter(fileop_INCLUDE);
873 fputs_nl(gen_page_begin(last, SUBDIR), INCLUDE);
874 fputs_nl(body_begin, INCLUDE);
875 fputs_nl(verbatim_begin, INCLUDE);
876 {
877 const char *filename = strbuf_value(inc->contents);
878 int count = inc->count;
879
880 for (; count; filename += strlen(filename) + 1, count--) {
881 fputs(gen_href_begin_with_title_target(upperdir(SRCS), path2fid(filename), HTML, NULL, NULL, target), INCLUDE);
882 fputs(removedotslash(filename), INCLUDE);
883 fputs_nl(gen_href_end(), INCLUDE);
884 }
885 }
886 fputs_nl(verbatim_end, INCLUDE);
887 fputs_nl(body_end, INCLUDE);
888 fputs_nl(gen_page_end(), INCLUDE);
889 close_file(fileop_INCLUDE);
890 html_count++;
891 /*
892 * inc->contents == NULL means that information already
893 * written to file.
894 */
895 strbuf_close(inc->contents);
896 inc->contents = NULL;
897 }
898 if (!inc->ref_count)
899 continue;
900 if (inc->ref_count == 1) {
901 SPLIT ptable;
902 char buf[1024];
903
904 if (split(strbuf_value(inc->ref_contents), 4, &ptable) < 4) {
905 recover(&ptable);
906 die("too small number of parts in makefileindex().");
907 }
908 snprintf(buf, sizeof(buf), "%s %s", ptable.part[PART_LNO].start, decode_path(ptable.part[PART_PATH].start));
909 recover(&ptable);
910 strbuf_reset(inc->ref_contents);
911 strbuf_puts(inc->ref_contents, buf);
912 } else {
913 char path[MAXPATHLEN];
914
915 snprintf(path, sizeof(path), "%s/%s/%d.%s", distpath, INCREFS, no, HTML);
916 fileop_INCLUDE = open_output_file(path, cflag);
917 INCLUDE = get_descripter(fileop_INCLUDE);
918 fputs_nl(gen_page_begin(last, SUBDIR), INCLUDE);
919 fputs_nl(body_begin, INCLUDE);
920 fputs_nl(gen_list_begin(), INCLUDE);
921 {
922 const char *line = strbuf_value(inc->ref_contents);
923 int count = inc->ref_count;
924
925 for (; count; line += strlen(line) + 1, count--)
926 fputs_nl(gen_list_body(upperdir(SRCS), line, NULL), INCLUDE);
927 }
928 fputs_nl(gen_list_end(), INCLUDE);
929 fputs_nl(body_end, INCLUDE);
930 fputs_nl(gen_page_end(), INCLUDE);
931 close_file(fileop_INCLUDE);
932 html_count++;
933 /*
934 * inc->ref_contents == NULL means that information already
935 * written to file.
936 */
937 strbuf_close(inc->ref_contents);
938 inc->ref_contents = NULL;
939 }
940 }
941 strbuf_close(input);
942 }
/* */