/* doctool.c Copyright 1998,1999,2000,2001 Red Hat, Inc. 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 #include #include #include #include #include #include /* 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 \n"); printf("\n"); printf("doctool looks for DOCTOOL-START and DOCTOOL-END lines in source,\n"); printf("saves 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 this is the doctool tags section. 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, "'); if (cmd) { cmd++; fprintf(output_file, "", 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, "\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; ifile->used = 1; fprintf(output_file, "\n", unprefix(s->file->filename)); for (l=addend; lnum_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 = ""; } parse_sgml(input_file, argv[1]); if (output_file != stdout) fclose(output_file); if (fix_makefile_flag) fix_makefile(output_name); return 0; }