/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #include #include #include #include #include #include #include #include char *dj_strlwr(char *s) { char *p = s; while (*s) { if ((*s >= 'A') && (*s <= 'Z')) *s += 'a'-'A'; s++; } return p; } struct TreeNode; struct Node; int count_nodes = 0; typedef void (*TFunc)(Node *); #define NUM_PORT_TARGETS 3 #define PORT_UNKNOWN 0 #define PORT_NO 1 #define PORT_PARTIAL 2 #define PORT_YES 3 /* Tokens for use in .txh files */ char *port_target[NUM_PORT_TARGETS] = { "ansi", "posix", "unix98" }; /* Strings to output in .txi files */ char *port_target_string[NUM_PORT_TARGETS] = { "ANSI", "POSIX", "Unix98" }; struct Tree { TreeNode *nodes; Tree(); void add(Node *n); static void Traverse(TreeNode *tn, TFunc tf); void Traverse(TFunc tf); Node *find(char *name); }; struct Line { struct Line *next; char *line; }; struct PortNote { struct PortNote *next; int target; int number; char *note; }; struct Node { Node *prev, *next; char *name; char *sname; char *cat; Line *lines; Line *lastline; Tree subnodes; char *filename; int port_info[NUM_PORT_TARGETS]; PortNote *port_notes; PortNote *last_port_note; Node(char *name, char *cat); void add(char *line); void process(char *line); void error(char *str, ...); void extend_portability_note(char *str); void read_portability_note(char *str); void read_portability(char *str); void write_portability(); }; struct TreeNode { TreeNode *before, *after; Node *node; TreeNode(Node *n); }; Tree categories; Tree nodes; //----------------------------------------------------------------------------- Node::Node(char *Pname, char *Pcat) { char *cp; name = strdup(Pname); for (cp=name; *cp == '_'; cp++); sname = strdup(cp); dj_strlwr(sname); cat = strdup(Pcat); lines = 0; lastline = 0; for (int i = 0; i < NUM_PORT_TARGETS; i++) port_info[i] = PORT_UNKNOWN; port_notes = NULL; last_port_note = NULL; } void Node::add(char *l) { Line *lp = new Line; lp->next = 0; lp->line = strdup(l); if (lastline) lastline->next = lp; if (!lines) lines = lp; lastline = lp; } void Node::error (char *str, ...) { va_list arg; char s[1024]; va_start (arg, str); vsprintf (s, str, arg); va_end (arg); fprintf (stderr, "Error (file %s, node %s): %s\n", filename, name, s); } void Node::extend_portability_note(char *str) { int newsize = strlen (last_port_note->note) + strlen (str) + 1; char *newstring = (char *) realloc (last_port_note->note, newsize); strcat (newstring, str); last_port_note->note = newstring; } void Node::read_portability_note(char *str) { char *work_str = strdup (str); char *s = work_str; char *target; int targ_num; while (isspace(*s)) s++; target = s; while (*s && !isspace(*s)) s++; if (*s) *s++ = 0; while (isspace(*s)) s++; dj_strlwr (target); for (targ_num = 0; targ_num < NUM_PORT_TARGETS; targ_num++) if (!strcmp (target, port_target[targ_num])) break; if (targ_num == NUM_PORT_TARGETS) { error ("unrecognised portability note target `%s' ignored.\n", target); } else { PortNote *p = new PortNote; p->next = NULL; p->number = 0; p->target = targ_num; p->note = strdup (""); if (port_notes) { last_port_note->next = p; } else { port_notes = p; } last_port_note = p; if (*s) extend_portability_note (s); } free (work_str); } void Node::read_portability(char *str) { char *targets = dj_strlwr (strdup (str)); char *x, *target = targets; int type,i; while (isspace (*target)) target++; while (*target) { type = PORT_YES; if (*target == '~') { type = PORT_PARTIAL; target++; } else if (*target == '!') { type = PORT_NO; target++; } for (x = target; *x && !isspace(*x) && (*x != ','); x++); if (*x) *x++ = 0; for (i = 0; i < NUM_PORT_TARGETS; i++) if (!strcmp (target, port_target[i])) break; if (i < NUM_PORT_TARGETS) port_info[i] = type; else error ("unrecognised portability target `%s' ignored.\n", target); target = x; while (isspace (*target)) target++; } free (targets); } void Node::write_portability() { char buffer[1024] = { 0 }; int note_number = 1; for (int i = 0; i < NUM_PORT_TARGETS; i++) { switch (port_info[i]) { case PORT_NO: strcat (buffer, "not "); strcat (buffer, port_target_string[i]); break; case PORT_YES: strcat (buffer, port_target_string[i]); break; case PORT_PARTIAL: strcat (buffer, "partially "); strcat (buffer, port_target_string[i]); break; } if (port_info[i] != PORT_UNKNOWN) { for (PortNote *p = port_notes; p; p = p->next) { if (p->target == i) { char smallbuffer[20]; p->number = note_number++; sprintf (smallbuffer, " (see note %d)", p->number); strcat (buffer, smallbuffer); break; } } strcat (buffer, ", "); } } { char *ch = strchr (buffer, 0) - 2; if (*ch == ',') *ch = 0; else strcpy (buffer, "Unknown."); } strcat (buffer, "\n\n"); add(buffer); if (note_number > 1) { add("@noindent\n"); add("Notes:\n"); add("\n"); add("@enumerate\n"); for (int i = 1; i < note_number; i++) { add("@item\n"); for (PortNote *p = port_notes; p; p = p->next) if (p->number == i) add(p->note); } add("@end enumerate\n"); } /* Now free the portability notes */ while (port_notes) { PortNote *p = port_notes; port_notes = p->next; free (p->note); delete p; } last_port_note = NULL; } void Node::process(char *line) { if (line[0] == '@') { if ((strncmp (line, "@portability", 12) == 0) && isspace (line[12])) { read_portability(line+13); write_portability(); return; } else if ((strncmp (line, "@port-note", 10) == 0) && isspace (line[10])) { read_portability_note(line+11); return; } } /* If `last_port_note' is not NULL, we're in the middle of a note */ if (last_port_note) { extend_portability_note(line); } else { add(line); } } //----------------------------------------------------------------------------- TreeNode::TreeNode(Node *n) { before = after = 0; node = n; } Tree::Tree() { nodes = 0; } void Tree::add(Node *n) { TreeNode *tp = new TreeNode(n); TreeNode **np = &nodes; while (*np) { if (strcmp((*np)->node->sname, n->sname) < 0) np = &((*np)->after); else np = &((*np)->before); } *np = tp; } Node *set_np_prev = 0; void set_np(Node *n) { n->prev = set_np_prev; n->next = 0; if (set_np_prev) set_np_prev->next = n; set_np_prev = n; } void Tree::Traverse(TreeNode *tn, TFunc tf) { if (!tn) return; Traverse(tn->before, tf); tf(tn->node); Traverse(tn->after, tf); } void Tree::Traverse(TFunc tf) { set_np_prev = 0; Traverse(nodes, set_np); Traverse(nodes, tf); } Node * Tree::find(char *name) { char *sname; for (sname = name; *sname == '_'; sname++); sname = strdup(sname); dj_strlwr(sname); TreeNode *tn = nodes; while (tn) { if (strcmp(tn->node->sname, sname) == 0) { return tn->node; } if (strcmp(sname, tn->node->sname) < 0) tn = tn->before; else tn = tn->after; } Node *n = new Node(name, ""); add(n); return n; } //----------------------------------------------------------------------------- FILE *co; int print_filenames = 0; void pnode(Node *n, char *up) { fprintf(co, "@c -----------------------------------------------------------------------------\n"); fprintf(co, "@node %s, %s, %s, %s\n", n->name, n->next ? n->next->name : "", n->prev ? n->prev->name : "", up); fprintf(co, "@unnumberedsec %s\n", n->name); if (print_filenames) fprintf(co, "@c From file %s\n", n->filename); } void cprint1(Node *n) { fprintf(co, "* %s::\n", n->name); } void cprint2b(Node *n) { fprintf(co, "* %s::\n", n->name); } void cprint2(Node *n) { pnode(n, "Functional Categories"); fprintf(co, "@menu\n"); n->subnodes.Traverse(cprint2b); fprintf(co, "@end menu\n"); } void nprint1(Node *n) { fprintf(co, "* %s::\n", n->name); } void nprint2(Node *n) { pnode(n, "Alphabetical List"); Line *l; for (l=n->lines; l; l=l->next) { fputs(l->line, co); if (strncmp(l->line, "@heading ", 9) == 0) fprintf(co, "@iftex\n@donoderef()\n@end iftex\n"); } } int is_directory(char *name) { struct stat statbuf; int result; result = stat(name, &statbuf); if(result < 0) return 0; if(S_ISDIR(statbuf.st_mode)) return 1; else return 0; } //----------------------------------------------------------------------------- void scan_directory(char *which) { Node *curnode; DIR *d = opendir(which); struct dirent *de; while (de = readdir(d)) { if (de->d_name[0] == '.') continue; char buf[4000]; sprintf(buf, "%s/%s", which, de->d_name); int buflen = strlen(buf); #ifdef D_OK if (access(buf, D_OK) == 0) { scan_directory(buf); } #else /* determine if a directory */ if(is_directory(buf)) { scan_directory(buf); } #endif else if (strcmp(buf+buflen-4, ".txh") == 0 && !strchr(buf, '~') && !strchr(buf, '#')) { char *filename = new char[strlen(buf)+1]; strcpy(filename, buf); FILE *ci = fopen(buf, "r"); if (!ci) { perror(buf); continue; } curnode = 0; while (fgets(buf, 4000, ci)) { if (strncmp(buf, "@c ---", 6) == 0) { } else if (strncmp(buf, "@node ", 6) == 0) { char name[1000]; char cat[1000]; name[0] = 0; cat[0] = 0; sscanf(buf, "%*s %[^,\n], %[^\n]", name, cat); strcat(cat, " functions"); curnode = new Node(name, cat); count_nodes ++; curnode->filename = filename; nodes.add(curnode); Node *catn = categories.find(cat); catn->filename = filename; catn->subnodes.add(curnode); } else { if (curnode) curnode->process(buf); } } fclose(ci); } } } //----------------------------------------------------------------------------- main(int argc, char **argv) { if (argc < 3) { fprintf(stderr, "Usage: mkdoc \n"); return 1; } scan_directory(argv[1]); co = fopen(argv[2], "w"); // Functional Categories fprintf(co, "@c -----------------------------------------------------------------------------\n"); fprintf(co, "@node Functional Categories, Alphabetical List, Introduction, Top\n"); fprintf(co, "@unnumbered Functional Categories\n"); fprintf(co, "\n"); fprintf(co, "@menu\n"); categories.Traverse(cprint1); fprintf(co, "@end menu\n"); categories.Traverse(cprint2); // Alphabetical List fprintf(co, "@c -----------------------------------------------------------------------------\n"); fprintf(co, "@node Alphabetical List, Unimplemented, Functional Categories, Top\n"); fprintf(co, "@unnumbered Alphabetical List\n"); fprintf(co, "\n"); fprintf(co, "@menu\n"); nodes.Traverse(nprint1); fprintf(co, "@end menu\n"); print_filenames = 1; nodes.Traverse(nprint2); printf("%d nodes processed\n", count_nodes); fclose(co); return 0; }