summaryrefslogtreecommitdiff
path: root/c2man.c
diff options
context:
space:
mode:
Diffstat (limited to 'c2man.c')
-rw-r--r--c2man.c928
1 files changed, 928 insertions, 0 deletions
diff --git a/c2man.c b/c2man.c
new file mode 100644
index 0000000..0250d11
--- /dev/null
+++ b/c2man.c
@@ -0,0 +1,928 @@
+/* $Id: c2man.c,v 1.1 2004-05-03 05:17:48 behdad Exp $
+ *
+ * C Manual page generator.
+ * Reads C source code and outputs manual pages.
+ */
+#include <ctype.h>
+#include <errno.h>
+
+#include "c2man.h"
+#include "enum.h"
+#include "strconcat.h"
+#include "strappend.h"
+#include "manpage.h"
+#include "output.h"
+#include "patchlevel.h"
+
+#ifdef I_FCNTL
+#include <fcntl.h>
+#endif
+
+#ifdef I_SYS_FILE
+#include <sys/file.h>
+#endif
+
+#include <sys/stat.h>
+#include <signal.h>
+
+/* getopt declarations */
+extern int getopt();
+extern char *optarg;
+extern int optind;
+
+/* lex declarations */
+extern FILE *yyin; /* lex input stream */
+
+/* Name of the program */
+const char *progname = "c2man";
+
+/* Program options */
+
+/* TRUE if static declarations are also output. */
+boolean static_out = FALSE;
+
+/* TRUE if variable declarations are output. */
+boolean variables_out = FALSE;
+
+/* TRUE if formal parameter promotion is enabled. */
+boolean promote_param = TRUE;
+
+/* String output before prototype declaration specifiers */
+const char *decl_spec_prefix = "";
+
+/* String output before prototype declarator */
+const char *declarator_prefix = " ";
+
+/* String output after prototype declarator */
+const char *declarator_suffix = "\n";
+
+/* String output before the first parameter in a function prototype */
+const char *first_param_prefix = "\n\t";
+
+/* String output before each subsequent parameter in a function prototype */
+const char *middle_param_prefix = "\n\t";
+
+/* String output after the last parameter in a function prototype */
+const char *last_param_suffix = "\n";
+
+/* Directory to write output files in */
+char *output_dir = NULL;
+
+/* Name of the manual */
+char *manual_name = NULL;
+
+/* Section for manual page */
+const char *manual_section = NULL;
+
+/* prefix for generated #include lines */
+char *header_prefix = NULL;
+
+/* list of include file specified by user */
+IncludeFile *first_include;
+static IncludeFile **last_next_include = &first_include;
+
+/* list of excluded sections specified by user */
+ExcludeSection *first_excluded_section;
+static ExcludeSection **last_next_excluded_section = &first_excluded_section;
+
+/* TRUE if c2man should attempt to fixup comment sections */
+boolean fixup_comments = TRUE;
+
+/* do we group related stuff into one file? */
+boolean group_together;
+
+/* was terse description read from file or command line option? */
+boolean terse_specified;
+
+/* terse description when grouped together */
+char *group_terse = NULL;
+
+/* should we always document parameters, even if it's only "Not Documented" */
+boolean always_document_params = TRUE;
+
+/* look for a function def comment at the start of the function body */
+boolean look_at_body_start = FALSE;
+
+/* only look for a function def comment at the start of the function body */
+boolean body_start_only = FALSE;
+
+/* default output info for each object type */
+struct Output_Object_Info output_object[_OBJECT_NUM] =
+{
+#if 0
+ {'c', "class"},
+ {'s', "struct"},
+ {'e', "enum"},
+ {'t', "typedef"},
+#endif
+ {'f', "function"},
+ {'v', "variable"},
+ {'F', "static function"},
+ {'V', "static variable"}
+};
+
+/* Include file directories */
+#ifdef MSDOS
+int num_inc_dir = 1;
+const char *inc_dir[MAX_INC_DIR] = { ".\\" };
+#else
+#ifdef AMIGA
+int num_inc_dir = 1;
+const char *inc_dir[MAX_INC_DIR] = { "include:" };
+#else
+int num_inc_dir = 2;
+const char *inc_dir[MAX_INC_DIR] = { "./", "/usr/include/" };
+#endif
+#endif
+
+/* total number of errors encountered */
+int errors;
+
+/* name of the base file being processed; NULL = stdin */
+const char *basefile;
+Time_t basetime; /* modification time of base file */
+boolean inbasefile; /* are we parsing in that base file? */
+
+/* is the base file a header file? */
+boolean header_file;
+
+#ifdef AMIGA
+struct Output *output = &autodoc_output;
+const char *default_section = "doc";
+#else
+/* use nroff output by default */
+struct Output *output = &nroff_output;
+const char *default_section = "3";
+#endif
+
+/* should we generate the output file named after the input file? */
+boolean use_input_name = FALSE;
+
+/* should we generate embeddable files? */
+boolean make_embeddable = FALSE;
+
+#define USE_CPP
+#ifdef USE_CPP
+const char *cpp_cmd = CPP_FILE_COM;
+#if defined(MSDOS)
+#include "popen.h"
+#define popen(c,m) os_popen(c,m)
+#define pclose(f) os_pclose(f)
+#else
+#if defined (_MSC_VER)
+#define popen(c,m) _popen(c,m)
+#define pclose(f) _pclose(f)
+#endif
+#endif
+#endif
+
+boolean verbose = FALSE;
+
+/* can cpp read standard input? */
+static boolean cppcanstdin
+#ifdef CPP_CAN_STDIN
+ = 1
+#endif
+;
+/* does cpp ignore header files */
+static boolean cppignhdrs
+#ifdef CPP_IGN_HDRS
+ = 1
+#endif
+;
+
+/* nifty little function for handling I/O errors */
+void my_perror(action, filename)
+const char *action, *filename;
+{
+ int err = errno;
+ fprintf(stderr,"%s: %s ", progname, action);
+ errno = err;
+ perror(filename);
+}
+
+/* write the #include lines as specified by the user */
+void print_includes(f)
+FILE *f;
+{
+ IncludeFile *incfile;
+
+ for (incfile = first_include; incfile; incfile=incfile->next)
+ {
+ char *name = incfile->name;
+ boolean surrounded = *name == '"' || *name == '<';
+
+ fputs("#include ", f);
+ if (!surrounded) fputc('<',f);
+ fputs(name, f);
+ if (!surrounded) fputc('>',f);
+ fputc('\n',f);
+ }
+}
+
+void outmem()
+{
+ fprintf(stderr,"%s: Out of memory!\n", progname);
+ exit(1);
+}
+
+#ifndef DBMALLOC
+void *safe_malloc(size)
+size_t size;
+{
+ void *mem;
+
+ if ((mem = (void *)malloc(size)) == NULL)
+ outmem();
+
+ return mem;
+}
+#endif
+
+/* Replace any character escape sequences in a string with the actual
+ * characters. Return a pointer to malloc'ed memory containing the result.
+ * This function knows only a few escape sequences.
+ */
+static char *
+escape_string (src)
+char *src;
+{
+ char *result, *get, *put;
+
+ result = strduplicate(src);
+ put = result;
+ get = src;
+ while (*get != '\0') {
+ if (*get == '\\') {
+ switch (*(++get)) {
+ case 'n':
+ *put++ = '\n';
+ ++get;
+ break;
+ case 't':
+ *put++ = '\t';
+ ++get;
+ break;
+ default:
+ if (*get != '\0')
+ *put++ = *get++;
+ }
+ } else {
+ *put++ = *get++;
+ }
+ }
+ *put = *get;
+ return result;
+}
+
+/* Output usage message and exit.
+ */
+static void
+usage ()
+{
+ int i;
+
+ fprintf(stderr, "usage: %s [ option ... ] [ file ... ]\n", progname);
+ fputs(" -o directory\twrite output files in directory\n",stderr);
+ fputs(" -p\t\tdisable prototype promotion\n", stderr);
+ fputs(" -s\t\toutput static declarations\n", stderr);
+ fputs(" -v\t\toutput variable declarations\n", stderr);
+ fputs(" -k\t\tdon't attempt to fixup comments\n", stderr);
+ fputs(" -b\t\tlook for descriptions at top of function bodies\n", stderr);
+ fputs(" -B\t\tonly look for descriptions by applying -b\n", stderr);
+ fputc('\n', stderr);
+ fputs(" -i incfile\n", stderr);
+ fputs(" -i \"incfile\"\n", stderr);
+ fputs(" -i <incfile>\tadd #include for incfile to SYNOPSIS\n",
+ stderr);
+ fputc('\n', stderr);
+ fputs(" -H prefix\tspecify prefix for #include in SYNOPSIS\n", stderr);
+ fputc('\n', stderr);
+ fputs(" -g\n", stderr);
+ fputs(" -G terse\tgroup info from each file into a single page\n",
+ stderr);
+ fputs(" -e\t\tmake embeddable files\n", stderr);
+ fputc('\n', stderr);
+ fputs(" -l ", stderr);
+#ifdef HAS_LINK
+ fputs("h|", stderr);
+#endif
+#ifdef HAS_SYMLINK
+ fputs("s|", stderr);
+#endif
+ fputs("f|n|r\t", stderr);
+ fputs("linking for grouped pages: ", stderr);
+#ifdef HAS_LINK
+ fputs("hard, ", stderr);
+#endif
+#ifdef HAS_SYMLINK
+ fputs("soft, ", stderr);
+#endif
+ fputs("file, none or remove\n", stderr);
+ fputs(" -n\t\tName output file after input source file\n", stderr);
+ fputs(" -L\t\tLazy: Be silent about undocumented parameters\n",
+ stderr);
+
+ fputs(" -T n|l|h|t|a[,options]\tselect typesetting output format: nroff, LaTeX, HTML ,TeXinfo or AutoDoc\n",
+ stderr);
+ nroff_output.print_options();
+ latex_output.print_options();
+ html_output.print_options();
+ texinfo_output.print_options();
+ autodoc_output.print_options();
+
+ fputs(" -M name\tset name of the manual in which the page goes\n",
+ stderr);
+ fputs(" -x section\texclude section from ouput\n", stderr);
+ fputc('\n', stderr);
+ fputs(" -D name[=value]\n", stderr);
+ fputs(" -U name\n", stderr);
+ fputs(" -I directory\tC preprocessor options\n", stderr);
+ fputc('\n', stderr);
+ fputs(" -F template\tset prototype template in the form ", stderr);
+ fputs("\"int f (a, b)\"\n",stderr);
+ fputs(" -P preprocessor\tAlternate C preprocessor ", stderr);
+ fputs("(e.g., \"gcc -E -C\")\n", stderr);
+ fputs(" -V\t\tbe verbose and print version information\n", stderr);
+ fputs(" -S section\tset the section for the manual page (default = 3)\n",
+ stderr);
+ fputs(" -O ", stderr);
+ for (i = 0; i < _OBJECT_NUM; i++)
+ fputc(output_object[i].flag, stderr);
+ fputs("[subdir][.ext]", stderr);
+ fputs("\tOutput control over different object types:\n\t\t", stderr);
+ for (i = 0; i < _OBJECT_NUM; i++)
+ {
+ fputs(output_object[i].name, stderr);
+ if (i <= _OBJECT_NUM - 2)
+ fprintf(stderr,i == _OBJECT_NUM-2 ? " or " : ", ");
+ }
+ fputs(".\n", stderr);
+ exit(1);
+}
+
+/* name of the temporary file; kept here so we can blast it if hit with ctrl-C
+ */
+static char temp_name[20];
+Signal_t (*old_interrupt_handler)();
+
+/* ctrl-C signal handler for use when we have a temporary file */
+static Signal_t interrupt_handler(sig)
+int sig;
+{
+ unlink(temp_name);
+ exit(128 + sig);
+}
+
+/* open a unique temporary file.
+ * To be universally accepted by cpp's, the file's name must end in .c; so we
+ * can't use mktemp, tmpnam or tmpfile.
+ * returns an open stream & sets ret_name to the name.
+ */
+FILE *open_temp_file()
+{
+ int fd;
+ long n = getpid();
+ FILE *tempf;
+ boolean remove_temp_file();
+
+ /* keep generating new names until we hit one that does not exist */
+ do
+ {
+ /* ideally we'd like to put the temporary file in /tmp, but it must go
+ * in the current directory because when cpp processes a #include, it
+ * looks in the same directory as the file doing the include; so if we
+ * use /tmp/blah.c to fake reading fred.h via `#include "fred.h"', cpp
+ * will look for /tmp/fred.h, and fail.
+ */
+ sprintf(temp_name,"c2man%ld.c",n++ % 1000000);
+ }
+ while((fd =
+#ifdef HAS_OPEN3
+ open(temp_name,O_WRONLY|O_CREAT|O_EXCL,0666)
+#else
+ creat(temp_name,O_EXCL|0666) /* do it the old way */
+#endif
+ ) == -1
+ && errno == EEXIST);
+
+ /* install interrupt handler to remove the temporary file */
+ old_interrupt_handler = signal(SIGINT, interrupt_handler);
+
+ /* convert it to a stream */
+ if ((fd == -1 && errno != EEXIST) || (tempf = fdopen(fd, "w")) == NULL)
+ {
+ my_perror("error fdopening temp file",temp_name);
+ remove_temp_file();
+ return NULL;
+ }
+
+ return tempf;
+}
+
+/* remove the temporary file & restore ctrl-C handler.
+ * returns FALSE in the event of failure.
+ */
+boolean remove_temp_file()
+{
+ int ok = unlink(temp_name) == 0; /* this should always succeed */
+ signal(SIGINT, old_interrupt_handler);
+ return ok;
+}
+
+/* process the specified source file through the pre-processor.
+ * This is a lower level routine called by both process_stdin and process_file
+ * to actually get the work done once any required temporary files have been
+ * generated.
+ */
+int process_file_directly(base_cpp_cmd, name)
+const char *base_cpp_cmd;
+const char *name;
+{
+ char *full_cpp_cmd;
+
+#ifdef DEBUG
+ fprintf(stderr,"process_file_directly: %s, %s\n", base_cpp_cmd, name);
+#endif
+
+#ifdef USE_CPP
+ full_cpp_cmd = strconcat(base_cpp_cmd, " ", name, NULLCP);
+ if (verbose)
+ fprintf(stderr,"%s: running `%s'\n", progname, full_cpp_cmd);
+
+ if ((yyin = popen(full_cpp_cmd, "r")) == NULL) {
+ my_perror("error running", base_cpp_cmd);
+ free(full_cpp_cmd);
+ return 0;
+ }
+#else
+ if (verbose) fprintf(stderr,"%s: reading %s\n", progname, name);
+ if (name && freopen(name, "r", yyin) == NULL)
+ {
+ my_perror("cannot open", name);
+ return 0;
+ }
+#endif
+
+ parse_file(name);
+
+#ifdef USE_CPP
+ free(full_cpp_cmd);
+ if (pclose(yyin) & 0xFF00)
+ return 0;
+#else
+ if (fclose(yyin))
+ {
+ my_perror("error closing", name);
+ return 0;
+ }
+#endif
+
+ return !errors;
+}
+
+/* process a specified file */
+int process_file(base_cpp_cmd, name)
+const char *base_cpp_cmd;
+const char *name;
+{
+ char *period;
+ struct stat statbuf;
+
+#ifdef DEBUG
+ fprintf(stderr,"process_file: %s, %s\n", base_cpp_cmd, name);
+#endif
+ basefile = name;
+ header_file = (period = strrchr(name,'.')) &&
+ (period[1] == 'h' || period[1] == 'H');
+
+ /* use the file's date as the date in the manual page */
+ if (stat(name,&statbuf) != 0)
+ {
+ my_perror("can't stat", name);
+ return 0;
+ }
+ basetime = statbuf.st_mtime;
+
+ /* should we do this via a temporary file?
+ * Only if it's a header file and either CPP ignores them, or the user
+ * has specified files to include.
+ *
+ * For HP/Apollo (SR10.3, CC 6.8), we must always use a temporary file,
+ * because its compiler recognizes the special macro "__attribute(p)",
+ * which we cannot redefine in the command line because it has parameters.
+ */
+#ifndef apollo
+ if (header_file && (cppignhdrs || first_include))
+#endif
+ {
+ FILE *tempf;
+ int ret;
+
+ if (verbose)
+ fprintf(stderr, "%s: preprocessing via temporary file\n", progname);
+
+ if ((tempf = open_temp_file()) == NULL)
+ return 0;
+
+ print_includes(tempf);
+ if (verbose) print_includes(stderr);
+
+#ifdef apollo
+ fprintf(tempf,"#define __attribute(p)\n", basefile);
+#endif
+ fprintf(tempf,"#include \"%s\"\n", basefile);
+ if (verbose) fprintf(stderr,"#include \"%s\"\n", basefile);
+
+ if (fclose(tempf) == EOF)
+ {
+ my_perror("error closing temp file", temp_name);
+ remove_temp_file();
+ return 0;
+ }
+
+ /* since we're using a temporary file, it's not the base file */
+ inbasefile = 0;
+ ret = process_file_directly(base_cpp_cmd, temp_name);
+ remove_temp_file();
+ return ret;
+ }
+
+ /* otherwise, process it directly */
+ inbasefile = 1;
+
+ return process_file_directly(base_cpp_cmd,name);
+}
+
+/* process the thing on the standard input */
+int process_stdin(base_cpp_cmd)
+const char *base_cpp_cmd;
+{
+ if (isatty(fileno(stdin)))
+ fprintf(stderr,"%s: reading standard input\n", progname);
+
+ header_file = 0; /* assume it's not since it's from stdin */
+ basefile = NULL;
+
+ /* use the current date in the man page */
+ basetime = time((Time_t *)NULL);
+
+ inbasefile = 1; /* reading stdin, we start in the base file */
+
+ /* always use a temp file if the preprocessor can't read stdin, otherwise
+ * only use one if the user specified files for inclusion.
+ */
+ if (!cppcanstdin || first_include) /* did user specify include files? */
+ {
+ FILE *tempf;
+ int c, ret;
+
+ if (verbose)
+ fprintf(stderr,"%s: reading stdin to a temporary file\n", progname);
+
+ if ((tempf = open_temp_file()) == NULL)
+ return 0;
+
+ print_includes(tempf);
+ if (verbose) print_includes(stderr);
+ fprintf(tempf,"#line 1 \"stdin\"\n");
+
+ while ((c = getchar()) != EOF)
+ putc(c,tempf);
+
+ if (fclose(tempf) == EOF)
+ {
+ my_perror("error closing temp file", temp_name);
+ remove_temp_file();
+ return 0;
+ }
+ ret = process_file_directly(base_cpp_cmd, temp_name);
+ remove_temp_file();
+ return ret;
+ }
+ else
+ {
+ char *full_cpp_cmd = strconcat(base_cpp_cmd," ", CPP_STDIN_FLAGS,
+ NULLCP);
+
+ if (verbose)
+ fprintf(stderr,"%s: running `%s'\n", progname, full_cpp_cmd);
+
+ if ((yyin = popen(full_cpp_cmd, "r")) == NULL) {
+ my_perror("error running", full_cpp_cmd);
+ return 0;
+ }
+
+ parse_file(basefile);
+
+ free(full_cpp_cmd);
+ if (pclose(yyin) & 0xFF00)
+ return 0;
+
+ return !errors;
+ }
+}
+
+int
+main (argc, argv)
+int argc;
+char **argv;
+{
+ int i, c, ok = 0;
+ char *s, cbuf[2];
+ const char *base_cpp_cmd;
+ IncludeFile *includefile;
+ ExcludeSection *excludesection;
+ char *cpp_opts;
+#ifdef HAS_LINK
+ enum LinkType link_type = LINK_HARD; /* for -g/G */
+#else
+ enum LinkType link_type = LINK_FILE;
+#endif
+
+#ifdef YYDEBUG
+ extern int yydebug;
+#endif
+
+ /* initialise CPP options with -D__C2MAN__ */
+ cbuf[0] = VERSION + '0';
+ cbuf[1] = '\0';
+#ifdef VMS
+ cpp_opts = strconcat("-\"D__C2MAN__=", cbuf, "\"",NULLCP);
+#else
+ cpp_opts = strconcat("-D__C2MAN__=", cbuf, NULLCP);
+#ifdef NeXT
+ cpp_opts = strappend(cpp_opts, " -D_NEXT_SOURCE", NULLCP);
+#endif /* !NeXT */
+#endif /* !VMS */
+
+ /* Scan command line options. */
+ while ((c = getopt(argc, argv, "P:D:F:I:psU:Vvo:eM:H:G:gi:x:S:l:LT:nO:kbB"))
+ != EOF)
+ {
+ switch (c) {
+ case 'I':
+ case 'D':
+ case 'U':
+ cbuf[0] = c; cbuf[1] = '\0';
+ if (cpp_opts)
+ cpp_opts = strappend(cpp_opts," -",cbuf,optarg,NULLCP);
+ else
+ cpp_opts = strconcat("-",cbuf,optarg,NULLCP);
+ break;
+ case 'P':
+ cpp_cmd = optarg;
+
+ /* with no better info to go on, we have to assume that this
+ * preprocessor is minimally capable.
+ */
+ cppcanstdin = 0;
+ cppignhdrs = 1;
+ break;
+ case 'G':
+ group_terse = optarg;
+ terse_specified = TRUE;
+ /* FALLTHROUGH */
+ case 'g':
+ group_together = TRUE;
+ break;
+ case 'F':
+ s = escape_string(optarg);
+
+ decl_spec_prefix = s;
+ while (*s != '\0' && isascii(*s) && !isalnum(*s)) ++s;
+ if (*s == '\0') usage();
+ *s++ = '\0';
+ while (*s != '\0' && isascii(*s) && isalnum(*s)) ++s;
+ if (*s == '\0') usage();
+
+ declarator_prefix = s;
+ while (*s != '\0' && isascii(*s) && !isalnum(*s)) ++s;
+ if (*s == '\0') usage();
+ *s++ = '\0';
+ while (*s != '\0' && isascii(*s) && isalnum(*s)) ++s;
+ if (*s == '\0') usage();
+
+ declarator_suffix = s;
+ while (*s != '\0' && *s != '(') ++s;
+ if (*s == '\0') usage();
+ *s++ = '\0';
+
+ first_param_prefix = s;
+ while (*s != '\0' && isascii(*s) && !isalnum(*s)) ++s;
+ if (*s == '\0') usage();
+ *s++ = '\0';
+ while (*s != '\0' && *s != ',') ++s;
+ if (*s == '\0') usage();
+
+ middle_param_prefix = ++s;
+ while (*s != '\0' && isascii(*s) && !isalnum(*s)) ++s;
+ if (*s == '\0') usage();
+ *s++ = '\0';
+ while (*s != '\0' && isascii(*s) && isalnum(*s)) ++s;
+ if (*s == '\0') usage();
+
+ last_param_suffix = s;
+ while (*s != '\0' && *s != ')') ++s;
+ *s = '\0';
+
+ break;
+ case 'p':
+ promote_param = FALSE;
+ break;
+ case 's':
+ static_out = TRUE;
+ break;
+ case 'V':
+ verbose = TRUE;
+ fprintf(stderr, "%s: Version %d, Patchlevel %d\n",
+ progname, VERSION, PATCHLEVEL);
+ break;
+ case 'v':
+ variables_out = TRUE;
+ break;
+ case 'k':
+ fixup_comments = FALSE;
+ break;
+ case 'o':
+ output_dir = optarg;
+ break;
+ case 'M':
+ manual_name = optarg;
+ break;
+ case 'H':
+ header_prefix = optarg;
+ break;
+ case 'i':
+ *last_next_include = includefile =
+ (IncludeFile *)safe_malloc(sizeof *includefile);
+ includefile->name = optarg;
+ includefile->next = NULL;
+ last_next_include = &includefile->next;
+ break;
+ case 'x':
+ *last_next_excluded_section = excludesection =
+ (ExcludeSection *)safe_malloc(sizeof *excludesection);
+ excludesection->name = optarg;
+ excludesection->next = NULL;
+ last_next_excluded_section = &excludesection->next;
+ break;
+ case 'S':
+ manual_section = optarg;
+ break;
+ case 'l':
+ switch(optarg[0])
+ {
+#ifdef HAS_LINK
+ case 'h': link_type = LINK_HARD; break;
+#endif
+#ifdef HAS_SYMLINK
+ case 's': link_type = LINK_SOFT; break;
+#endif
+ case 'f': link_type = LINK_FILE; break;
+ case 'n': link_type = LINK_NONE; break;
+ case 'r': link_type = LINK_REMOVE;break;
+ default: usage();
+ }
+ break;
+ case 'e':
+ make_embeddable = TRUE;
+ break;
+ case 'n':
+ use_input_name = TRUE;
+ break;
+ case 'L':
+ always_document_params = FALSE;
+ break;
+ case 'T':
+ switch(optarg[0])
+ {
+ case 'n': output = &nroff_output; default_section = "3";
+ break;
+ case 'l': output = &latex_output; default_section = "tex";
+ break;
+ case 't': output = &texinfo_output; default_section = "texi";
+ break;
+ case 'h': output = &html_output; default_section = "html";
+ break;
+ case 'a': output = &autodoc_output; default_section = "doc";
+ break;
+ default: usage();
+ }
+ s = strtok(&optarg[1], ",");
+ if (s && *output->parse_option == NULL) usage();
+ while(s)
+ {
+ if (output->parse_option(s)) usage();
+ s = strtok(NULL, ",");
+ }
+ break;
+ case 'O':
+ for (i = 0; i < _OBJECT_NUM; i++)
+ if (output_object[i].flag == optarg[0])
+ break;
+
+ if (i == _OBJECT_NUM)
+ {
+ fprintf(stderr,"%s: -O option must specify one of:\n\t",
+ progname);
+ for (i = 0; i < _OBJECT_NUM; i++)
+ {
+ fprintf(stderr,"%c (%s)", output_object[i].flag,
+ output_object[i].name);
+ if (i <= _OBJECT_NUM - 2)
+ fprintf(stderr,i == _OBJECT_NUM-2 ? " or " : ", ");
+ }
+ fprintf(stderr, ".\n");
+ exit(1);
+ }
+
+ if ((s = strchr(++optarg,'.'))) /* look for an extension */
+ {
+ output_object[i].subdir = alloc_string(optarg, s);
+ output_object[i].extension = strduplicate(s+1);
+ }
+ else
+ output_object[i].subdir = strduplicate(optarg);
+
+ break;
+ case 'b':
+ look_at_body_start = TRUE;
+ break;
+ case 'B':
+ body_start_only = TRUE;
+ look_at_body_start = TRUE;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ /* make sure we have a manual section */
+ if (manual_section == NULL) manual_section = default_section;
+
+#ifdef MALLOC_DEBUG
+ getchar(); /* wait so we can start up NeXT MallocDebug tool */
+#endif
+#ifdef YYDEBUG
+ yydebug = 1;
+#endif
+
+ if (cpp_opts)
+ {
+ base_cpp_cmd = strconcat(cpp_cmd, " ", cpp_opts, NULLCP);
+ free(cpp_opts);
+ }
+ else
+ base_cpp_cmd = cpp_cmd;
+
+ if (optind == argc) {
+ if (use_input_name)
+ {
+ fprintf(stderr,"%s: %s\n", progname,
+ "cannot name output after input file if there isn't one!");
+ usage();
+ }
+ ok = process_stdin(base_cpp_cmd);
+ }
+ else
+ for (i = optind; i < argc; ++i)
+ if (!(ok = process_file(base_cpp_cmd,argv[i]))) break;
+
+ if (ok && firstpage)
+ output_manual_pages(firstpage,argc - optind, link_type);
+ free_manual_pages(firstpage);
+ destroy_enum_lists();
+
+ if (cpp_opts) free((char *)base_cpp_cmd);
+
+ for (includefile = first_include; includefile;)
+ {
+ IncludeFile *next = includefile->next;
+ free(includefile);
+ includefile = next;
+ }
+
+ for (excludesection = first_excluded_section; excludesection;)
+ {
+ ExcludeSection *next = excludesection->next;
+ free(excludesection);
+ excludesection = next;
+ }
+
+ for (i = 0; i < _OBJECT_NUM; i++)
+ {
+ safe_free(output_object[i].subdir);
+ safe_free(output_object[i].extension);
+ }
+
+#ifdef DBMALLOC
+ malloc_dump(2);
+ malloc_chain_check(1);
+#endif
+#ifdef MALLOC_DEBUG
+ sleep(1000000);
+#endif
+ return !ok;
+}