623 lines
13 KiB
C
623 lines
13 KiB
C
|
/* doctool.c
|
||
|
|
||
|
Copyright 1998 Cygnus Solutions.
|
||
|
|
||
|
This file is part of Cygwin.
|
||
|
|
||
|
This software is a copyrighted work licensed under the terms of the
|
||
|
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
||
|
details. */
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <dirent.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <utime.h>
|
||
|
|
||
|
/* Building native in a cross-built directory is tricky. Be careful,
|
||
|
and beware that you don't have the full portability stuff available to
|
||
|
you (like libiberty) */
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
/* The list of extensions that may contain SGML snippets. We check
|
||
|
both cases in case the file system isn't case sensitive enough. */
|
||
|
|
||
|
struct {
|
||
|
char *upper;
|
||
|
char *lower;
|
||
|
int is_sgml;
|
||
|
} extensions[] = {
|
||
|
{ ".C", ".c", 0 },
|
||
|
{ ".CC", ".cc", 0 },
|
||
|
{ ".H", ".h", 0 },
|
||
|
{ ".SGML", ".sgml", 1 },
|
||
|
{ 0, 0, 0 }
|
||
|
};
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
void
|
||
|
show_help()
|
||
|
{
|
||
|
printf("Usage: doctool [-m] [-i] [-d dir] [-o outfile] [-s prefix] \\\n");
|
||
|
printf(" [-b book_id] infile\n");
|
||
|
printf(" -m means to adjust Makefile to include new dependencies\n");
|
||
|
printf(" -i means to include internal snippets\n");
|
||
|
printf(" -d means to recursively scan directory for snippets\n");
|
||
|
printf(" -o means to output to file (else stdout)\n");
|
||
|
printf(" -s means to suppress source dir prefix\n");
|
||
|
printf(" -b means to change the <book id=\"book_id\">\n");
|
||
|
printf("\n");
|
||
|
printf("doctool looks for DOCTOOL-START and DOCTOOL-END lines in source,\n");
|
||
|
printf("saves <foo id=\"bar\"> blocks, and looks for DOCTOOL-INSERT-bar\n");
|
||
|
printf("commands to insert selected sections. IDs starting with int-\n");
|
||
|
printf("are internal only, add- are added at the end of relevant sections\n");
|
||
|
printf("or add-int- for both. Inserted sections are chosen by prefix,\n");
|
||
|
printf("and sorted when inserted.\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
typedef struct Section {
|
||
|
struct Section *next;
|
||
|
struct OneFile *file;
|
||
|
char *name;
|
||
|
char internal;
|
||
|
char addend;
|
||
|
char used;
|
||
|
char **lines;
|
||
|
int num_lines;
|
||
|
int max_lines;
|
||
|
} Section;
|
||
|
|
||
|
typedef struct OneFile {
|
||
|
struct OneFile *next;
|
||
|
char *filename;
|
||
|
int enable_scan;
|
||
|
int used;
|
||
|
Section *sections;
|
||
|
} OneFile;
|
||
|
|
||
|
OneFile *file_list = 0;
|
||
|
|
||
|
char *output_name = 0;
|
||
|
FILE *output_file = 0;
|
||
|
|
||
|
char *source_dir_prefix = "";
|
||
|
char *book_id = 0;
|
||
|
|
||
|
int internal_flag = 0;
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
char *
|
||
|
has_string(char *line, char *string)
|
||
|
{
|
||
|
int i;
|
||
|
while (*line)
|
||
|
{
|
||
|
for (i=0; line[i]; i++)
|
||
|
{
|
||
|
if (!string[i])
|
||
|
return line;
|
||
|
if (line[i] != string[i])
|
||
|
break;
|
||
|
}
|
||
|
line++;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
starts_with(char *line, char *string)
|
||
|
{
|
||
|
int i=0;
|
||
|
while (1)
|
||
|
{
|
||
|
if (!string[i])
|
||
|
return 1;
|
||
|
if (!line[i] || line[i] != string[i])
|
||
|
return 0;
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
#ifdef S_ISLNK
|
||
|
#define STAT lstat
|
||
|
#else
|
||
|
#define STAT stat
|
||
|
#endif
|
||
|
|
||
|
void
|
||
|
scan_directory(dirname)
|
||
|
char *dirname;
|
||
|
{
|
||
|
struct stat st;
|
||
|
char *name;
|
||
|
struct dirent *de;
|
||
|
DIR *dir = opendir(dirname);
|
||
|
if (!dir)
|
||
|
return;
|
||
|
while (de = readdir(dir))
|
||
|
{
|
||
|
if (strcmp(de->d_name, ".") == 0
|
||
|
|| strcmp(de->d_name, "..") == 0)
|
||
|
continue;
|
||
|
|
||
|
name = (char *)malloc(strlen(dirname)+strlen(de->d_name)+3);
|
||
|
strcpy(name, dirname);
|
||
|
strcat(name, "/");
|
||
|
strcat(name, de->d_name);
|
||
|
|
||
|
STAT(name, &st);
|
||
|
|
||
|
if (S_ISDIR(st.st_mode))
|
||
|
{
|
||
|
scan_directory(name);
|
||
|
}
|
||
|
|
||
|
else if (S_ISREG(st.st_mode))
|
||
|
{
|
||
|
char *dot = strrchr(de->d_name, '.');
|
||
|
int i;
|
||
|
|
||
|
if (dot)
|
||
|
{
|
||
|
for (i=0; extensions[i].upper; i++)
|
||
|
if (strcmp(dot, extensions[i].upper) == 0
|
||
|
|| strcmp(dot, extensions[i].lower) == 0)
|
||
|
{
|
||
|
OneFile *one = (OneFile *)malloc(sizeof(OneFile));
|
||
|
one->next = file_list;
|
||
|
file_list = one;
|
||
|
one->filename = name;
|
||
|
one->enable_scan = ! extensions[i].is_sgml;
|
||
|
one->used = 0;
|
||
|
one->sections = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
closedir (dir);
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
void
|
||
|
scan_file(OneFile *one)
|
||
|
{
|
||
|
FILE *f = fopen(one->filename, "r");
|
||
|
int enabled = ! one->enable_scan;
|
||
|
char line[1000], *tag=0, *id=0, *tmp;
|
||
|
int taglen = 0;
|
||
|
Section *section = 0;
|
||
|
Section **prev_section_ptr = &(one->sections);
|
||
|
|
||
|
if (!f)
|
||
|
{
|
||
|
perror(one->filename);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
while (fgets(line, 1000, f))
|
||
|
{
|
||
|
if (one->enable_scan)
|
||
|
{
|
||
|
/* source files have comment-embedded docs, check for them */
|
||
|
if (has_string(line, "DOCTOOL-START"))
|
||
|
enabled = 1;
|
||
|
if (has_string(line, "DOCTOOL-END"))
|
||
|
enabled = 0;
|
||
|
}
|
||
|
if (!enabled)
|
||
|
continue;
|
||
|
|
||
|
/* DOCTOOL-START
|
||
|
|
||
|
<sect1 id="dt-tags">
|
||
|
this is the doctool tags section.
|
||
|
</sect1>
|
||
|
|
||
|
DOCTOOL-END */
|
||
|
|
||
|
if (!tag && line[0] == '<')
|
||
|
{
|
||
|
tag = (char *)malloc(strlen(line)+1);
|
||
|
id = (char *)malloc(strlen(line)+1);
|
||
|
if (sscanf(line, "<%s id=\"%[^\"]\">", tag, id) == 2)
|
||
|
{
|
||
|
if (strcmp(tag, "book") == 0 || strcmp(tag, "BOOK") == 0)
|
||
|
{
|
||
|
/* Don't want to "scan" these */
|
||
|
return;
|
||
|
}
|
||
|
taglen = strlen(tag);
|
||
|
section = (Section *)malloc(sizeof(Section));
|
||
|
/* We want chunks within single files to appear in that order */
|
||
|
section->next = 0;
|
||
|
section->file = one;
|
||
|
*prev_section_ptr = section;
|
||
|
prev_section_ptr = &(section->next);
|
||
|
section->internal = 0;
|
||
|
section->addend = 0;
|
||
|
section->used = 0;
|
||
|
section->name = id;
|
||
|
if (starts_with(section->name, "add-"))
|
||
|
{
|
||
|
section->addend = 1;
|
||
|
section->name += 4;
|
||
|
}
|
||
|
if (starts_with(section->name, "int-"))
|
||
|
{
|
||
|
section->internal = 1;
|
||
|
section->name += 4;
|
||
|
}
|
||
|
section->lines = (char **)malloc(10*sizeof(char *));
|
||
|
section->num_lines = 0;
|
||
|
section->max_lines = 10;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
free(tag);
|
||
|
free(id);
|
||
|
tag = id = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (tag && section)
|
||
|
{
|
||
|
if (section->num_lines >= section->max_lines)
|
||
|
{
|
||
|
section->max_lines += 10;
|
||
|
section->lines = (char **)realloc(section->lines,
|
||
|
section->max_lines * sizeof (char *));
|
||
|
}
|
||
|
section->lines[section->num_lines] = (char *)malloc(strlen(line)+1);
|
||
|
strcpy(section->lines[section->num_lines], line);
|
||
|
section->num_lines++;
|
||
|
|
||
|
if (line[0] == '<' && line[1] == '/'
|
||
|
&& memcmp(line+2, tag, taglen) == 0
|
||
|
&& (isspace(line[2+taglen]) || line[2+taglen] == '>'))
|
||
|
{
|
||
|
/* last line! */
|
||
|
tag = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
fclose(f);
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
Section **
|
||
|
enumerate_matching_sections(char *name_prefix, int internal, int addend, int *count_ret)
|
||
|
{
|
||
|
Section **rv = (Section **)malloc(12*sizeof(Section *));
|
||
|
int count = 0, max=10, prefix_len = strlen(name_prefix);
|
||
|
OneFile *one;
|
||
|
int wildcard = 0;
|
||
|
|
||
|
if (name_prefix[strlen(name_prefix)-1] == '-')
|
||
|
wildcard = 1;
|
||
|
|
||
|
for (one=file_list; one; one=one->next)
|
||
|
{
|
||
|
Section *s;
|
||
|
for (s=one->sections; s; s=s->next)
|
||
|
{
|
||
|
int matches = 0;
|
||
|
if (wildcard)
|
||
|
{
|
||
|
if (starts_with(s->name, name_prefix))
|
||
|
matches = 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (strcmp(s->name, name_prefix) == 0)
|
||
|
matches = 1;
|
||
|
}
|
||
|
if (s->internal <= internal
|
||
|
&& s->addend == addend
|
||
|
&& matches
|
||
|
&& ! s->used)
|
||
|
{
|
||
|
s->used = 1;
|
||
|
if (count >= max)
|
||
|
{
|
||
|
max += 10;
|
||
|
rv = (Section **)realloc(rv, max*sizeof(Section *));
|
||
|
}
|
||
|
rv[count++] = s;
|
||
|
rv[count] = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (count_ret)
|
||
|
*count_ret = count;
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
#define ID_CHARS "~@$%&()_-+[]{}:."
|
||
|
|
||
|
void include_section(char *name, int addend);
|
||
|
|
||
|
char *
|
||
|
unprefix(char *fn)
|
||
|
{
|
||
|
int l = strlen(source_dir_prefix);
|
||
|
if (memcmp(fn, source_dir_prefix, l) == 0)
|
||
|
{
|
||
|
fn += l;
|
||
|
while (*fn == '/' || *fn == '\\')
|
||
|
fn++;
|
||
|
return fn;
|
||
|
}
|
||
|
return fn;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
parse_line(char *line, char *filename)
|
||
|
{
|
||
|
char *cmd = has_string(line, "DOCTOOL-INSERT-");
|
||
|
char *sname, *send, *id, *save;
|
||
|
if (!cmd)
|
||
|
{
|
||
|
if (book_id
|
||
|
&& (starts_with(line, "<book") || starts_with(line, "<BOOK")))
|
||
|
{
|
||
|
cmd = strchr(line, '>');
|
||
|
if (cmd)
|
||
|
{
|
||
|
cmd++;
|
||
|
fprintf(output_file, "<book id=\"%s\">", book_id);
|
||
|
fputs(cmd, output_file);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
fputs(line, output_file);
|
||
|
return;
|
||
|
}
|
||
|
if (cmd != line)
|
||
|
fwrite(line, cmd-line, 1, output_file);
|
||
|
save = (char *)malloc(strlen(line)+1);
|
||
|
strcpy(save, line);
|
||
|
line = save;
|
||
|
|
||
|
sname = cmd + 15; /* strlen("DOCTOOL-INSERT-") */
|
||
|
for (send = sname;
|
||
|
*send && isalnum(*send) || strchr(ID_CHARS, *send);
|
||
|
send++);
|
||
|
id = (char *)malloc(send-sname+2);
|
||
|
memcpy(id, sname, send-sname);
|
||
|
id[send-sname] = 0;
|
||
|
include_section(id, 0);
|
||
|
|
||
|
fprintf(output_file, "<!-- %s -->\n", unprefix(filename));
|
||
|
|
||
|
fputs(send, output_file);
|
||
|
free(save);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
section_sort(const void *va, const void *vb)
|
||
|
{
|
||
|
Section *a = *(Section **)va;
|
||
|
Section *b = *(Section **)vb;
|
||
|
int rv = strcmp(a->name, b->name);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
return a->internal - b->internal;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
include_section(char *name, int addend)
|
||
|
{
|
||
|
Section **sections, *s;
|
||
|
int count, i, l;
|
||
|
|
||
|
sections = enumerate_matching_sections(name, internal_flag, addend, &count);
|
||
|
|
||
|
qsort(sections, count, sizeof(sections[0]), section_sort);
|
||
|
for (i=0; i<count; i++)
|
||
|
{
|
||
|
s = sections[i];
|
||
|
s->file->used = 1;
|
||
|
fprintf(output_file, "<!-- %s -->\n", unprefix(s->file->filename));
|
||
|
for (l=addend; l<s->num_lines-1; l++)
|
||
|
parse_line(s->lines[l], s->file->filename);
|
||
|
if (!addend)
|
||
|
{
|
||
|
include_section(s->name, 1);
|
||
|
parse_line(s->lines[l], s->file->filename);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
free(sections);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
parse_sgml(FILE *in, char *input_name)
|
||
|
{
|
||
|
static char line[1000];
|
||
|
while (fgets(line, 1000, in))
|
||
|
{
|
||
|
parse_line(line, input_name);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
void
|
||
|
fix_makefile(char *output_name)
|
||
|
{
|
||
|
FILE *in, *out;
|
||
|
char line[1000];
|
||
|
int oname_len = strlen(output_name);
|
||
|
OneFile *one;
|
||
|
int used_something = 0;
|
||
|
struct stat st;
|
||
|
struct utimbuf times;
|
||
|
|
||
|
stat("Makefile", &st);
|
||
|
|
||
|
in = fopen("Makefile", "r");
|
||
|
if (!in)
|
||
|
{
|
||
|
perror("Makefile");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
out = fopen("Makefile.new", "w");
|
||
|
if (!out)
|
||
|
{
|
||
|
perror("Makefile.new");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
while (fgets(line, 1000, in))
|
||
|
{
|
||
|
if (starts_with(line, output_name)
|
||
|
&& strcmp(line+oname_len, ": \\\n") == 0)
|
||
|
{
|
||
|
/* this is the old dependency */
|
||
|
while (fgets(line, 1000, in))
|
||
|
{
|
||
|
if (strcmp(line+strlen(line)-2, "\\\n"))
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
fputs(line, out);
|
||
|
}
|
||
|
fclose(in);
|
||
|
|
||
|
for (one=file_list; one; one=one->next)
|
||
|
if (one->used)
|
||
|
{
|
||
|
used_something = 1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (used_something)
|
||
|
{
|
||
|
fprintf(out, "%s:", output_name);
|
||
|
for (one=file_list; one; one=one->next)
|
||
|
if (one->used)
|
||
|
fprintf(out, " \\\n\t%s", one->filename);
|
||
|
fprintf(out, "\n");
|
||
|
}
|
||
|
|
||
|
fclose(out);
|
||
|
|
||
|
times.actime = st.st_atime;
|
||
|
times.modtime = st.st_mtime;
|
||
|
utime("Makefile.new", ×);
|
||
|
|
||
|
if (rename("Makefile", "Makefile.old"))
|
||
|
return;
|
||
|
if (rename("Makefile.new", "Makefile"))
|
||
|
rename("Makefile.old", "Makefile");
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
int
|
||
|
main(argc, argv)
|
||
|
int argc;
|
||
|
char **argv;
|
||
|
{
|
||
|
int i;
|
||
|
OneFile *one;
|
||
|
FILE *input_file;
|
||
|
int fix_makefile_flag = 0;
|
||
|
|
||
|
while (argc > 1 && argv[1][0] == '-')
|
||
|
{
|
||
|
if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)
|
||
|
{
|
||
|
show_help();
|
||
|
}
|
||
|
else if (strcmp(argv[1], "-i") == 0)
|
||
|
{
|
||
|
internal_flag = 1;
|
||
|
}
|
||
|
else if (strcmp(argv[1], "-m") == 0)
|
||
|
{
|
||
|
fix_makefile_flag = 1;
|
||
|
}
|
||
|
else if (strcmp(argv[1], "-d") == 0 && argc > 2)
|
||
|
{
|
||
|
scan_directory(argv[2]);
|
||
|
argc--;
|
||
|
argv++;
|
||
|
}
|
||
|
else if (strcmp(argv[1], "-o") == 0 && argc > 2)
|
||
|
{
|
||
|
output_name = argv[2];
|
||
|
argc--;
|
||
|
argv++;
|
||
|
}
|
||
|
else if (strcmp(argv[1], "-s") == 0 && argc > 2)
|
||
|
{
|
||
|
source_dir_prefix = argv[2];
|
||
|
argc--;
|
||
|
argv++;
|
||
|
}
|
||
|
else if (strcmp(argv[1], "-b") == 0 && argc > 2)
|
||
|
{
|
||
|
book_id = argv[2];
|
||
|
argc--;
|
||
|
argv++;
|
||
|
}
|
||
|
|
||
|
argc--;
|
||
|
argv++;
|
||
|
}
|
||
|
|
||
|
for (one=file_list; one; one=one->next)
|
||
|
{
|
||
|
scan_file(one);
|
||
|
}
|
||
|
|
||
|
input_file = fopen(argv[1], "r");
|
||
|
if (!input_file)
|
||
|
{
|
||
|
perror(argv[1]);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (output_name)
|
||
|
{
|
||
|
output_file = fopen(output_name, "w");
|
||
|
if (!output_file)
|
||
|
{
|
||
|
perror(output_name);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
output_file = stdout;
|
||
|
output_name = "<stdout>";
|
||
|
}
|
||
|
|
||
|
parse_sgml(input_file, argv[1]);
|
||
|
|
||
|
if (output_file != stdout)
|
||
|
fclose(output_file);
|
||
|
|
||
|
if (fix_makefile_flag)
|
||
|
fix_makefile(output_name);
|
||
|
|
||
|
return 0;
|
||
|
}
|