/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
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
/*
* Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
* 2006
* Tama Communications Corporation
*
* This file is part of GNU GLOBAL.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <ctype.h>
#include <stdio.h>
#ifdef STDC_HEADERS
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#include "global.h"
#include "incop.h"
#include "htags.h"
#include "path2url.h"
#include "common.h"
/*----------------------------------------------------------------------*/
/* Find list procedures */
/*----------------------------------------------------------------------*/
static const char *getpath(void);
static void ungetpath(void);
static GFIND *gp;
static int retry;
/*
* get a path from input stream.
*
* Each path name must start with "./".
*/
static const char *
getpath(void)
{
static const char *buff;
if (!retry) {
/* skip README or ChangeLog unless the -o option specified. */
do {
buff = gfind_read(gp);
} while (buff && gp->type == GPATH_OTHER && !other_files);
}
retry = 0;
return buff;
}
/*
* push back a path name.
*/
static void
ungetpath(void)
{
retry = 1;
}
/*----------------------------------------------------------------------*/
/* Path name operation procedures */
/*----------------------------------------------------------------------*/
static const char *extract_lastname(const char *, int);
static const char *lastpart(const char *);
static const char *dirpart(const char *, char *);
static const char *localpath(const char *, char *);
static const char *appendslash(const char *);
static const char *insert_comma(unsigned int);
/*
* extract_lastname: extract the last name of include line.
*
* i) image source image of include
* i) is_php 1: is PHP source
* r) last name
*/
static const char *
extract_lastname(const char *image, int is_php)
{
static char buf[MAXBUFLEN];
const char *p;
char *q;
int sep;
/*
* C: #include <xxx/yyy/zzz.h>
* #include "xxx/yyy/zzz.h"
* PHP: include('xxx/yyy/zzz');
*/
p = image;
while (*p && isspace((unsigned char)*p)) /* skip space */
p++;
if (!*p)
return NULL;
if (*p == '#') {
if (is_php)
return NULL;
p++;
while (*p && isspace((unsigned char)*p)) /* skip space */
p++;
if (!*p)
return NULL;
}
/*
* If match to one of the include keywords then points
* the following character of the keyword.
* p
* v
* ... include ....
*/
if (is_php) {
if ((p = locatestring(p, "include", MATCH_AT_FIRST)) == NULL)
return NULL;
} else {
char *q;
if (((q = locatestring(p, "include_next", MATCH_AT_FIRST)) == NULL) &&
((q = locatestring(p, "import", MATCH_AT_FIRST)) == NULL) &&
((q = locatestring(p, "include", MATCH_AT_FIRST)) == NULL))
return NULL;
p = q;
}
while (*p && isspace((unsigned char)*p)) /* skip space */
p++;
if (is_php && *p == '(') {
p++;
while (*p && isspace((unsigned char)*p)) /* skip space */
p++;
}
sep = *p;
if (is_php) {
if (sep != '\'' && sep != '"')
return NULL;
} else {
if (sep != '<' && sep != '"')
return NULL;
}
if (sep == '<')
sep = '>';
p++;
if (!*p)
return NULL;
q = buf;
while (*p && *p != '\n' && *p != sep)
*q++ = *p++;
*q = '\0';
if (*p == sep) {
q = locatestring(buf, "/", MATCH_LAST);
if (q)
q++;
else
q = buf;
return q;
}
return NULL;
}
/*
* get the last part of the path.
*
* i) path path name
* r) last part
* Ex.
* lastpart("a/b") => "b"
* lastpart("a") => "a"
*/
static const char *
lastpart(const char *path)
{
const char *p = strrchr(path, '/');
return p ? p + 1 : path;
}
/*
* get the directory part of the path.
*
* i) path path name
* o) result result buffer
* r) directory part
* Ex.
* dirpart("a/b/c") => "a/b"
*/
static const char *
dirpart(const char *path, char *result)
{
char *p = result;
const char *q = path, *limit = strrchr(path, '/');
while (q < limit)
*p++ = *q++;
*p = '\0';
return result;
}
/*
* get the local path name if the path is under the dir.
*
* i) path path name
* i) dir directory name
* r) local path name
*
* Ex.
* localpath("a/b/c", "a/b") => "c"
* localpath("a/b/c/d", "a/b") => "c/d"
* localpath("a/b/c", "a/d") => NULL
*/
static const char *
localpath(const char *path, char *dir)
{
int length = strlen(dir);
if (!strncmp(path, dir, length) && *(path + length) == '/')
return path + length + 1;
return NULL;
}
/*
* append '/' after the path name
*
* i) path path name
* r) appended path name
*
* Ex.
* appendslash("a") => "a/"
*/
static const char *
appendslash(const char *path)
{
STATIC_STRBUF(sb);
strbuf_clear(sb);
strbuf_puts(sb, path);
strbuf_putc(sb, '/');
return strbuf_value(sb);
}
/*
* remove './' at the head of the path name
*
* i) path path name
* r) removed path name
*
* Ex.
* removedotslash("./a") => "a"
*/
static const char *
removedotslash(const char *path)
{
return (*path == '.' && *(path + 1) == '/') ? path + 2 : path;
}
/*
* insert_comma: insert comma to the number.
*
* i) n number
* r) edited string
*
* Ex.
* 10000 => 10,000
*/
static const char *
insert_comma(unsigned int n)
{
#define RESULTSIZE 15
#define INTERVAL 3
static char result[RESULTSIZE];
int i = RESULTSIZE;
int count = 0;
if (n == 0)
return (const char *)"0";
if (n > 1000000000)
goto giveup;
result[--i] = '\0';
for (; n > 0; n = n / 10) {
/*
* Buffer overflow. This is not important even if occurring.
*/
if (i <= 0)
goto giveup;
if (count && count % INTERVAL == 0)
result[--i] = ',';
result[--i] = n % 10 + '0';
count++;
}
return (const char *)&result[i];
giveup:
snprintf(result, sizeof(result), "*****");
return (const char *)result;
}
/*----------------------------------------------------------------------*/
/* Generating file index */
/*----------------------------------------------------------------------*/
static int print_directory(int, char *);
static void print_directory_header(FILE *, int, const char *);
static void print_directory_footer(FILE *, int, const char *);
static const char *print_file_name(int, const char *);
static const char *print_directory_name(int, const char *, int);
FILE *FILEMAP;
STRBUF *files;
const char *indexlink;
regex_t is_include_file;
int src_count;
/*
* print contents of one directory and the descendant. (recursively called)
*
* i) level directory nest level
* io) basedir current directory
*
* This function read find style records, and print directory tree.
*/
/*
* File list of the top level directory (when level == 0) is not written
* to a file directly. Instead, it is written to string buffer, because
* it appears in some places.
*/
#define PUT(s) do { \
if (level == 0) \
strbuf_puts(files, s); \
else \
fputs(s, op); \
} while (0)
static int
print_directory(int level, char *basedir)
{
const char *path;
FILEOP *fileop = NULL;
FILE *op = NULL;
int flist_items = 0;
int count = 0;
if (level > 0) {
char name[MAXPATHLEN+1];
snprintf(name, sizeof(name), "%s/files/%s.%s", distpath, path2fid(basedir), HTML);
fileop = open_output_file(name, cflag);
op = get_descripter(fileop);
print_directory_header(op, level, basedir);
}
while ((path = getpath()) != NULL) {
const char *p, *local = localpath(path, basedir);
/*
* Path is outside of basedir.
*/
if (local == NULL) {
ungetpath(); /* read again by upper level print_directory(). */
break;
}
/*
* Path is inside of basedir.
*/
else {
char *slash = strchr(local, '/');
if (table_flist && flist_items++ % flist_fields == 0)
PUT(fline_begin);
/*
* Print directory.
*/
if (slash) {
int baselen = strlen(basedir);
char *q, *last = basedir + baselen;
int subcount;
if (baselen + 1 + (slash - local) > MAXPATHLEN) {
fprintf(stderr, "Too long path name.\n");
exit(1);
}
/*
* Append new directory to the basedir.
*/
p = local;
q = last;
*q++ = '/';
while (p < slash)
*q++ = *p++;
*q = '\0';
/*
* print tree for this directory.
*/
ungetpath(); /* read again by lower level print_directory(). */
subcount = print_directory(level + 1, basedir);
PUT(print_directory_name(level, basedir, subcount));
count += subcount;
/*
* Shrink the basedir.
*/
*last = '\0';
}
/*
* Print file.
*/
else {
PUT(print_file_name(level, path));
count++;
}
if (table_flist && flist_items % flist_fields == 0)
PUT(fline_end);
}
}
if (flist_items % flist_fields != 0)
PUT(fline_end);
if (level > 0) {
print_directory_footer(op, level, basedir);
close_file(fileop);
}
html_count++;
return count;
}
/*
* print directory header.
*
* i) op file index
* i) level 1,2...
* i) dir directory name
*/
static void
print_directory_header(FILE *op, int level, const char *dir)
{
STATIC_STRBUF(sb);
if (level == 0)
die("print_directory_header: internal error.");
strbuf_clear(sb);
strbuf_puts(sb, removedotslash(dir));
strbuf_putc(sb, '/');
fputs_nl(gen_page_begin(strbuf_value(sb), SUBDIR), op);
fputs_nl(body_begin, op);
strbuf_clear(sb);
strbuf_sprintf(sb, "%s%sroot%s/", header_begin, gen_href_begin(NULL, indexlink, normal_suffix, NULL), gen_href_end());
fputs(strbuf_value(sb), op);
{
char path[MAXPATHLEN+1];
char *p, *q;
strlimcpy(path, dir, sizeof(path));
for (p = path + 1; p != NULL; p = strchr(p, '/')) {
int save = 0;
q = ++p;
while (*q && *q != '/')
q++;
save = *q;
if (*q == '/')
*q = '\0';
if (save == '/')
fputs(gen_href_begin(NULL, path2fid(path), HTML, NULL), op);
fputs(p, op);
if (save == '/')
fputs(gen_href_end(), op);
*q = save;
fputc('/', op);
}
}
fputs_nl(header_end, op);
{
char parentdir[MAXPATHLEN+1];
const char *suffix, *parent;
(void)dirpart(dir, parentdir);
if (level == 1) {
parent = indexlink;
suffix = normal_suffix;
} else {
parent = path2fid(parentdir);
suffix = HTML;
}
fputs(gen_href_begin_with_title(NULL, parent, suffix, NULL, "Parent Directory"), op);
}
if (Iflag)
fputs(gen_image(PARENT, back_icon, ".."), op);
else
fputs("[..]", op);
fputs_nl(gen_href_end(), op);
if (table_flist)
fputs_nl(flist_begin, op);
else if (!no_order_list)
fputs_nl(list_begin, op);
else {
fputs(br, op);
fputs_nl(br, op);
}
}
/*
* print directory footer.
*
* i) op file index
* i) level 1,2...
* i) dir directory name
*/
static void
print_directory_footer(FILE *op, int level, const char *dir)
{
const char *parent, *suffix;
char parentdir[MAXPATHLEN+1];
if (level == 0)
die("print_directory_footer: internal error.");
(void)dirpart(dir, parentdir);
if (level == 1) {
parent = indexlink;
suffix = normal_suffix;
} else {
parent = path2fid(parentdir);
suffix = HTML;
}
if (table_flist)
fputs_nl(flist_end, op);
else if (!no_order_list)
fputs_nl(list_end, op);
else
fputs_nl(br, op);
fputs(gen_href_begin_with_title(NULL, parent, suffix, NULL, "Parent Directory"), op);
if (Iflag)
fputs(gen_image(PARENT, back_icon, ".."), op);
else
fputs("[..]", op);
fputs_nl(gen_href_end(), op);
fputs_nl(body_end, op);
fputs_nl(gen_page_end(), op);
}
/*
* print file name.
*
* i) level 0,1,2...
* i) path path of the file
*/
static const char *
print_file_name(int level, const char *path)
{
STATIC_STRBUF(sb);
char *target = (Fflag) ? "mains" : "_top";
int size = filesize(path);
char tips[80];
message(" [%d] adding %s", ++src_count, removedotslash(path));
/*
* We assume the file which has one of the following suffixes
* as a candidate of include file.
*
* C: .h
* C++: .hxx, .hpp, .H
* PHP: .inc.php
*/
if (regexec(&is_include_file, path, 0, 0, 0) == 0)
put_inc(lastpart(path), path, src_count);
strbuf_clear(sb);
if (table_flist)
strbuf_puts(sb, fitem_begin);
else if (!no_order_list)
strbuf_puts(sb, item_begin);
if (size > 1)
snprintf(tips, sizeof(tips), "%s bytes", insert_comma(size));
else
snprintf(tips, sizeof(tips), "%s byte", insert_comma(size));
strbuf_puts(sb, gen_href_begin_with_title_target(level == 0 ? SRCS: upperdir(SRCS),
path2fid(path), HTML, NULL, tips, target));
if (Iflag) {
const char *lang, *suffix, *text_icon;
if ((suffix = locatestring(path, ".", MATCH_LAST)) != NULL
&& (lang = decide_lang(suffix)) != NULL
&& (strcmp(lang, "c") == 0 || strcmp(lang, "cpp") == 0
|| strcmp(lang, "yacc") == 0))
text_icon = c_icon;
else
text_icon = file_icon;
strbuf_puts(sb, gen_image(level == 0 ? CURRENT : PARENT, text_icon, removedotslash(path)));
strbuf_puts(sb, quote_space);
}
strbuf_puts(sb, full_path ? removedotslash(path) : lastpart(path));
strbuf_puts(sb, gen_href_end());
if (table_flist)
strbuf_puts(sb, fitem_end);
else if (!no_order_list)
strbuf_puts(sb, item_end);
else
strbuf_puts(sb, br);
strbuf_putc(sb, '\n');
if (map_file)
fprintf(FILEMAP, "%s\t%s/%s.%s\n", removedotslash(path), SRCS, path2fid(path), HTML);
return (const char *)strbuf_value(sb);
}
/*
* print directory name.
*
* i) level 0,1,2...
* i) path path of the directory
* i) count number of files in this directory
*/
static const char *
print_directory_name(int level, const char *path, int count)
{
STATIC_STRBUF(sb);
char tips[80];
if (count > 1)
snprintf(tips, sizeof(tips), "%d files", count);
else
snprintf(tips, sizeof(tips), "%d file", count);
path = removedotslash(path);
strbuf_clear(sb);
if (table_flist)
strbuf_puts(sb, fitem_begin);
else if (!no_order_list)
strbuf_puts(sb, item_begin);
strbuf_puts(sb, gen_href_begin_with_title(level == 0 ? "files" : NULL,
path2fid(path), HTML, NULL, tips));
if (Iflag) {
strbuf_puts(sb, gen_image(level == 0 ? CURRENT : PARENT, dir_icon, appendslash(path)));
strbuf_puts(sb, quote_space);
}
strbuf_sprintf(sb, "%s/%s", lastpart(path), gen_href_end());
if (table_flist)
strbuf_puts(sb, fitem_end);
else if (!no_order_list)
strbuf_puts(sb, item_end);
else
strbuf_puts(sb, br);
strbuf_putc(sb, '\n');
return (const char *)strbuf_value(sb);
}
/*
* makefileindex: make file index.
*
* i) file output file name
* o) files top level file index
*/
int
makefileindex(const char *file, STRBUF *a_files)
{
STATIC_STRBUF(sb);
FILE *filesop;
int flags = REG_EXTENDED;
/*
* Basedir is a directory to which we are paying attention on each
* occasion. It starts with ".", grows and shrink according to the
* progress of processing. It isn't copied each every recursive call
* not to waste the stack.
*/
char basedir[MAXPATHLEN+1];
/*
* Initialize data.
*/
indexlink = (Fflag) ? "../files" : "../mains";
src_count = 0;
gp = gfind_open(dbpath, NULL, other_files ? GPATH_BOTH : GPATH_SOURCE);
/*
* for collecting include files.
*/
if (w32)
flags |= REG_ICASE;
strbuf_clear(sb);
strbuf_puts(sb, "\\.(");
{
const char *p = include_file_suffixes;
int c;
while ((c = (unsigned char)*p++) != '\0') {
if (isregexchar(c))
strbuf_putc(sb, '\\');
else if (c == ',')
c = '|';
strbuf_putc(sb, c);
}
}
strbuf_puts(sb, ")$");
if (regcomp(&is_include_file, strbuf_value(sb), flags) != 0)
die("cannot compile regular expression '%s'.", strbuf_value(sb));
/*
* Write to files.html.
*/
if ((filesop = fopen(makepath(distpath, file, NULL), "w")) == NULL)
die("cannot open file '%s'.", file);
fputs_nl(gen_page_begin(title_file_index, TOPDIR), filesop);
fputs_nl(body_begin, filesop);
fputs(header_begin, filesop);
fputs(gen_href_begin(NULL, "files", normal_suffix, NULL), filesop);
fputs(title_file_index, filesop);
fputs(gen_href_end(), filesop);
fputs_nl(header_end, filesop);
if (table_flist) {
fputs_nl(flist_begin, filesop);
} else if (!no_order_list) {
fputs_nl(list_begin, filesop);
}
FILEMAP = NULL;
if (map_file) {
if (!(FILEMAP = fopen(makepath(distpath, "FILEMAP", NULL), "w")))
die("cannot open '%s'.", makepath(distpath, "FILEMAP", NULL));
}
/*
* print whole directory tree.
*/
files = a_files;
strcpy(basedir, ".");
(void)print_directory(0, basedir);
if (map_file)
fclose(FILEMAP);
gfind_close(gp);
regfree(&is_include_file);
fputs(strbuf_value(files), filesop);
if (table_flist)
fputs_nl(flist_end, filesop);
else if (!no_order_list)
fputs_nl(list_end, filesop);
else
fputs_nl(br, filesop);
fputs_nl(body_end, filesop);
fputs_nl(gen_page_end(), filesop);
fclose(filesop);
html_count++;
return src_count;
}
/*----------------------------------------------------------------------*/
/* Main body of generating include file index */
/*----------------------------------------------------------------------*/
void
makeincludeindex(void)
{
FILE *PIPE;
STRBUF *input = strbuf_open(0);
char *ctags_x;
struct data *inc;
char *target = (Fflag) ? "mains" : "_top";
char command[MAXFILLEN];
/*
* Pick up include pattern.
*
* C: #include "xxx.h"
* PHP: include("xxx.inc.php");
*/
/*
* Unlike Perl regular expression, POSIX regular expression doesn't support C-style escape sequence.
* Therefore, we can not use "\\t" here.
*/
snprintf(command, sizeof(command), "%s -gnx \"^[ \t]*(#[ \t]*(import|include)|include[ \t]*\\()\"", global_path);
if ((PIPE = popen(command, "r")) == NULL)
die("cannot fork.");
strbuf_reset(input);
while ((ctags_x = strbuf_fgets(input, PIPE, STRBUF_NOCRLF)) != NULL) {
SPLIT ptable;
char buf[MAXBUFLEN];
int is_php = 0;
const char *last, *lang, *suffix;
if (split(ctags_x, 4, &ptable) < 4) {
recover(&ptable);
die("too small number of parts in makefileindex().");
}
if ((suffix = locatestring(ptable.part[PART_PATH].start, ".", MATCH_LAST)) != NULL
&& (lang = decide_lang(suffix)) != NULL
&& strcmp(lang, "php") == 0)
is_php = 1;
last = extract_lastname(ptable.part[PART_LINE].start, is_php);
if (last == NULL || (inc = get_inc(last)) == NULL)
continue;
recover(&ptable);
/*
* s/^[^ \t]+/$last/;
*/
{
const char *p;
char *q = buf;
for (p = last; *p; p++)
*q++ = *p;
for (p = ctags_x; *p && *p != ' ' && *p != '\t'; p++)
;
for (; *p; p++)
*q++ = *p;
*q = '\0';
}
put_included(inc, buf);
}
if (pclose(PIPE) != 0)
die("terminated abnormally.");
for (inc = first_inc(); inc; inc = next_inc()) {
const char *last = inc->name;
int no = inc->id;
FILEOP *fileop_INCLUDE;
FILE *INCLUDE;
if (inc->count > 1) {
char path[MAXPATHLEN];
snprintf(path, sizeof(path), "%s/%s/%d.%s", distpath, INCS, no, HTML);
fileop_INCLUDE = open_output_file(path, cflag);
INCLUDE = get_descripter(fileop_INCLUDE);
fputs_nl(gen_page_begin(last, SUBDIR), INCLUDE);
fputs_nl(body_begin, INCLUDE);
fputs_nl(verbatim_begin, INCLUDE);
{
const char *filename = strbuf_value(inc->contents);
int count = inc->count;
for (; count; filename += strlen(filename) + 1, count--) {
fputs(gen_href_begin_with_title_target(upperdir(SRCS), path2fid(filename), HTML, NULL, NULL, target), INCLUDE);
fputs(removedotslash(filename), INCLUDE);
fputs_nl(gen_href_end(), INCLUDE);
}
}
fputs_nl(verbatim_end, INCLUDE);
fputs_nl(body_end, INCLUDE);
fputs_nl(gen_page_end(), INCLUDE);
close_file(fileop_INCLUDE);
html_count++;
/*
* inc->contents == NULL means that information already
* written to file.
*/
strbuf_close(inc->contents);
inc->contents = NULL;
}
if (!inc->ref_count)
continue;
if (inc->ref_count == 1) {
SPLIT ptable;
char buf[1024];
if (split(strbuf_value(inc->ref_contents), 4, &ptable) < 4) {
recover(&ptable);
die("too small number of parts in makefileindex().");
}
snprintf(buf, sizeof(buf), "%s %s", ptable.part[PART_LNO].start, ptable.part[PART_PATH].start);
recover(&ptable);
strbuf_reset(inc->ref_contents);
strbuf_puts(inc->ref_contents, buf);
} else {
char path[MAXPATHLEN];
snprintf(path, sizeof(path), "%s/%s/%d.%s", distpath, INCREFS, no, HTML);
fileop_INCLUDE = open_output_file(path, cflag);
INCLUDE = get_descripter(fileop_INCLUDE);
fputs_nl(gen_page_begin(last, SUBDIR), INCLUDE);
fputs_nl(body_begin, INCLUDE);
fputs_nl(gen_list_begin(), INCLUDE);
{
const char *line = strbuf_value(inc->ref_contents);
int count = inc->ref_count;
for (; count; line += strlen(line) + 1, count--)
fputs_nl(gen_list_body(upperdir(SRCS), line), INCLUDE);
}
fputs_nl(gen_list_end(), INCLUDE);
fputs_nl(body_end, INCLUDE);
fputs_nl(gen_page_end(), INCLUDE);
close_file(fileop_INCLUDE);
html_count++;
/*
* inc->ref_contents == NULL means that information already
* written to file.
*/
strbuf_close(inc->ref_contents);
inc->ref_contents = NULL;
}
}
strbuf_close(input);
}