home *** CD-ROM | disk | FTP | other *** search
- /************************************************************************
- * *
- * Copyright (c) 1982, Fred Fish *
- * All Rights Reserved *
- * *
- * This software and/or documentation is released for public *
- * distribution for personal, non-commercial use only. *
- * Limited rights to use, modify, and redistribute are hereby *
- * granted for non-commercial purposes, provided that all *
- * copyright notices remain intact and all changes are clearly *
- * documented. The author makes no warranty of any kind with *
- * respect to this product and explicitly disclaims any implied *
- * warranties of merchantability or fitness for any particular *
- * purpose. *
- * *
- ************************************************************************
- */
-
-
- /*
- * FILE
- *
- * dex0.c main routines for documentation extraction utility
- *
- * DESCRIPTION
- *
- * Contains the main routines and misc utility routines for dex.
- *
- * FUNCTIONS
- *
- * main entry point for dex utility
- * tolower convert alpha characters to lower case
- * options process options list on cmd line
- * usage give usage message and abort
- * indentation set indentation level
- * nofill turn off fill mode
- * fill turn on fill mode
- * asterisks form box top or bottom
- * emit_bline emit cmds for blank line
- * set_section lookup and set current section
- * reset_section reset current section (no section)
- * emit_filled emit text in filled mode
- * emit_unfilled emit text in unfilled mode
- * set_output switch output stream
- *
- * AUTHOR
- *
- * Fred Fish
- *
- */
-
- /*
- * TOOL
- *
- * dex documentation extraction utility
- *
- * SYNOPSIS
- *
- * dex [-dhtv] [-r rcfile] file1 file2 file3 ...
- *
- * d => enable debug mode
- * h => print internal help message
- * t => enable trace mode
- * v => enable verbose mode
- *
- * r => use reconfiguration file <rcfile>
- *
- * DESCRIPTION
- *
- * Dex extracts internal documentation from program source
- * files, compiling it into a form suitable for input
- * to the nroff text formatter. It was developed primarily
- * to aid the author in maintaining "up to date" documentation
- * for each function in the portable math library (PML).
- *
- * For further information consult the document
- * "DEX - Documentation Extraction Utility".
- *
- * EXAMPLE
- *
- * The output of dex is typically collected in one
- * or more files to be inserted in the appropriate
- * places in other "skeleton" files during nroff
- * processing.
- *
- * For example, in the portable math library usage,
- * the following sequence of commands will rebuild
- * the library documentation to reflect the current
- * state of the library.
- *
- * cd /usr/public/pml
- * dex *
- * nroff -mm pml.r >pml.doc
- *
- * The command "dex *" extracts function and file documentation
- * from all the files in the library, and builds two output
- * files, "funcs.r" and "files.r". The "funcs.r" file
- * contains documentation on each of the library functions.
- * The "files.r" file contains documentation on each of the
- * files in the portable math library directory. The "pml.r"
- * file is an nroff file containing information common
- * to all files and functions. At the points where the
- * "funcs.r" and "files.r" files are to be inserted, there
- * is a ".so <filename>" nroff command.
- *
- */
-
- /*
- * FILES
- *
- * ".dexrc" dex reconfiguration file [default]
- *
- * BUGS
- *
- * Items are currently collected as encountered in the files
- * specified in the command line. A planned future enhancement
- * is to sort the collected documentation regions by name.
- *
- * Lines which being with a "." character, such as the
- * line in the "FILES" section above, cause nroff to
- * occasionally do unintended things.
- *
- * The dynamic reconfiguration capability is relatively
- * primitive. Once the requirements are more clear,
- * a future version will be more flexible.
- *
- * AUTHOR
- *
- * Fred Fish
- *
- */
-
- #include <stdio.h>
- #include "hashtbl.h"
- #include "dex.h"
-
- /*
- * Some misc global variables.
- */
-
- struct tbl_data *section=NULL; /* Current section being processed */
- char *infile = "stdin"; /* Input file name if not stdin */
- char *rcfile = ".dexrc"; /* Reconfiguration file */
- int filled = TRUE; /* Flag for fill mode on */
- int trace = FALSE; /* Trace flag */
- int vflag = FALSE; /* Verbose flag */
- int debug = FALSE; /* Debug flag */
- extern int yydebug; /* YACC internal debug flag */
- int indent_level = 0; /* Current indentation level */
- int linenum = 1; /* Current input file line number */
- FILE *infp = stdin; /* Lexical analysis input stream */
- FILE *fp = stdout; /* Current output stream */
- char title[128]; /* Title of current region */
-
- /*
- * Some misc local variables.
- */
-
- static int suppress = FALSE; /* Flag for region suppression */
- static int box_emitted = FALSE; /* Flag for box has been emitted for region */
-
-
- /*
- * The following documentation will be printed out when DEX is
- * invoked with the -h argument.
- */
-
- static char *documentation[] = {
- "",
- "DESCRIPTION",
- "",
- "\tDex is a program for extracting documentation from program",
- "\tsource files, building files suitable for input to a",
- "\ttext formatter (such as nroff).",
- "",
- "USAGE",
- "",
- "\tdex [-dhtv] [-f rcfile] file1 file2 ...",
- "",
- "\t\t-d enable debug output (implies -v)",
- "\t\t-h print this help info",
- "\t\t-t enable trace output",
- "\t\t-v verbose mode",
- "",
- "\t\t-f use specified reconfiguration file",
- "\t\t (default \".dexrc\")",
- "",
- "OUTPUTS",
- "",
- "\tBy default DEX writes all output to stdout.",
- "\tThis can be changed via the reconfiguration file.",
- "",
- 0
- };
-
-
- /*
- * FUNCTION
- *
- * main DEX entry point
- *
- * KEY WORDS
- *
- * dex entry point
- * main
- *
- * SYNOPSIS
- *
- * main(argc,argv)
- * int argc;
- * char *argv[];
- *
- * DESCRIPTION
- *
- * This is where DEX starts executing. All argument list
- * switches are processed first, then all the specified
- * files are processed.
- *
- */
-
- /*
- * PSEUDO CODE
- *
- * Begin main
- * Process command line options.
- * For each argument list field
- * If field was not erased during option processing
- * If there is a previously processed file open
- * Close the previous file.
- * End if
- * If the current file cannot be opened then
- * Print error message.
- * Else
- * Save input file name for future use.
- * Reset current line number to 1.
- * Do any reconfiguration requested.
- * If verbose then tell user file name.
- * Process the input file.
- * Close the input file.
- * End if
- * End if
- * End for
- * Exit with "normal" status.
- * End main
- *
- */
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- FILE *fopen();
- char *argp;
- int argnum;
-
- options(argc,argv);
- for (argnum = 1; argnum < argc; argnum++) {
- if ((argp = argv[argnum]) != NULL) {
- if (infp != NULL) {
- fclose(infp);
- }
- if ((infp = fopen(argp,"r")) == NULL) {
- perror(argp);
- } else {
- infile = argp;
- linenum = 1;
- reconfig(argp,rcfile);
- if (debug) {dump_tbl();}
- if (vflag) {printf("dex: processing \"%s\"\n",argp);}
- yyparse();
- fclose(infp);
- }
- }
- }
- if (infp == stdin) {
- linenum = 1;
- reconfig(".",rcfile);
- yyparse();
- }
- exit(NORMAL_EXIT);
- }
-
- /*
- * FUNCTION
- *
- * tolower force alphabetic characters to lower case
- *
- * SYNOPSIS
- *
- * char tolower(ch)
- * char ch;
- *
- * DESCRIPTION
- *
- * Tolower forces alphabetic upper case characters to lower case.
- * If the input character is already lower case, or is not
- * alphabetic, then it is simply returned unchanged.
- *
- * BUGS
- *
- * Currently works only if native character set is ASCII.
- *
- */
-
- /*
- * PSEUDO CODE
- *
- * Begin tolower
- * If character is an upper case alphabetic then
- * Convert character to lower case and return it.
- * Else
- * Return character unchanged.
- * End if
- * End tolower
- *
- */
-
- #ifndef tolower
- char tolower(ch)
- char ch;
- {
- if ('A' <= ch && ch <= 'Z') {
- return(ch + 040);
- } else {
- return(ch);
- }
- }
- #endif
-
- /*
- * FUNCTION
- *
- * options process command line options
- *
- * SYNOPSIS
- *
- * options(argc,argv)
- * int argc;
- * char *argv[];
- *
- * DESCRIPTION
- *
- * Scans argument list, processing each switch as it is
- * found. The pointer to each switch string is then
- * replaced with a NULL to effectively erase the switch
- * argument.
- *
- */
-
- /*
- * PSEUDO CODE
- *
- * Begin options
- * For each argument in the argument list
- * Get pointer to first char of argument.
- * If the argument is a switch then
- * Replace argument pointer with NULL.
- * Look at next argument character.
- * While there is another argument character
- * Switch on the argument character
- * Case "verbose":
- * Set verbose flag.
- * Break out of switch.
- * Case "debug":
- * Set debug flag.
- * Set YACC debug flag.
- * Break out of switch.
- * Case "reconfigure":
- * If reconfig file is next arg
- * If there is no next arg
- * Abort with usage message.
- * Else
- * Save file name.
- * If file is actually switch
- * Abort with usage message.
- * Else
- * Erase reconfig switch
- * End if
- * End if
- * Else
- * Save pntr to reconfig filename.
- * End if
- * Break out of switch.
- * Case "trace":
- * Set trace flag.
- * Break out of switch.
- * Default:
- * Abort with usage message.
- * End switch
- * End while
- * End if
- * End for
- * End options
- *
- */
-
- options(argc, argv)
- int argc;
- char *argv[];
- {
- int i;
- char c; /* 1st char of current command-line argument */
- char *cp; /* current argument pointer */
-
- for (i=1; i<argc; i++) {
- cp = argv[i];
- if (*cp == '-') {
- argv[i] = NULL;
- cp++;
- while (c = *cp++) {
- switch (tolower(c)) {
- case 'v':
- vflag++;
- break;
- case 'd':
- debug++;
- yydebug++;
- vflag++;
- break;
- case 'f':
- if (*cp == NULL) {
- if (++i >= argc) {
- usage(c);
- } else {
- rcfile = argv[i];
- if (*rcfile == '-') {
- usage(c);
- } else {
- argv[i] = NULL;
- }
- }
- } else {
- rcfile = cp;
- }
- break;
- case 't':
- trace++;
- break;
- default:
- usage(c);
- }
- }
- }
- }
- }
-
- /*
- * FUNCTION
- *
- * usage give usage message and abort
- *
- * KEY WORDS
- *
- * usage
- * help processing
- * abort locations
- *
- * SYNOPSIS
- *
- * usage(ch)
- * char ch; (is h for explicit help)
- *
- * DESCRIPTION
- *
- * Usage is typically called when a problem has been
- * detected in the argument list, or usage help has
- * been explicitly requested (via the -h flag).
- * It prints an appropriate usage message and then aborts.
- *
- */
-
- /*
- * PSEUDO CODE
- *
- * Begin usage
- * If character is not the explicit help character then
- * Print a brief usage message.
- * Print how to get more help.
- * Else
- * Initialize internal documentation pointer.
- * While there is a line of documentation to print
- * Print the line of documentation.
- * End while
- * End if
- * Exit with error status.
- * End usage
- *
- */
-
- usage(c)
- char c; /* The char of the option */
- {
- register char **dp;
-
- if (c != 'h') {
- printf("Usage: dex [-dhv] [-f rcfile]\n");
- printf(" dex -h for help.\n");
- } else {
- dp = documentation;
- while (*dp != NULL) {
- printf("%s\n",*dp++);
- }
- }
- exit(ERROR_EXIT);
- }
-
- /*
- * FUNCTION
- *
- * indentation set indentation to specified level
- *
- * KEY WORDS
- *
- * indent level
- * emit functions
- *
- * SYNOPSIS
- *
- * indentation(where)
- * int where;
- *
- * DESCRIPTION
- *
- * Checks requested indentation level against current
- * indentation level. If they do not match then
- * emits appropriate nroff command to set indentation
- * to the specified level.
- *
- */
-
- /*
- * PSEUDO CODE
- *
- * Begin indentation
- * If current level is not that requested then
- * Emit command to change indentation.
- * Update the current indentation level.
- * End if
- * End indentation
- *
- */
-
- indentation(where)
- int where;
- {
- char buffer[16];
-
- if (indent_level != where) {
- sprintf(buffer,".in %d",where);
- emit(buffer,NULL);
- indent_level = where;
- }
- }
-
- /*
- * FUNCTION
- *
- * nofill if filled mode is on, turn it off
- *
- * KEY WORDS
- *
- * fill mode
- * emit functions
- *
- * SYNOPSIS
- *
- * nofill()
- *
- * DESCRIPTION
- *
- * Forces fill mode to be off. If fill mode is currently
- * on then emits suitable command to turn it off.
- *
- */
-
- /*
- * PSEUDO CODE
- *
- * Begin nofill
- * If filled mode is on then
- * Emit command to turn it off.
- * Remember that it is now off.
- * End if
- * End nofill
- *
- */
-
- nofill()
- {
- if (filled) {
- emit(".nf",NULL);
- filled = FALSE;
- }
- }
-
- /*
- * FUNCTION
- *
- * fill if fill mode is off, turn it on
- *
- * KEY WORDS
- *
- * fill mode
- * emit functions
- *
- * SYNOPSIS
- *
- * fill()
- *
- * DESCRIPTION
- *
- * Forces fill mode to be on. If fill mode is currently
- * off then emits suitable command to turn it on.
- *
- */
-
- /*
- * PSEUDO CODE
- *
- * Begin fill
- * If filled mode is off then
- * Emit command to turn it on.
- * Remember that it is now on.
- * End if
- * End fill
- *
- */
-
- fill()
- {
- if (!filled) {
- emit(".fi",NULL);
- filled = TRUE;
- }
- }
-
- /*
- * FUNCTION
- *
- * asterisks emit command for centered asterisks line
- *
- * KEY WORDS
- *
- * emit functions
- * box construction
- *
- * SYNOPSIS
- *
- * asterisks(how_many)
- * int how_many;
- *
- * DESCRIPTION
- *
- * Emits a command to center a line with the specified
- * number of asterisks. This is used to form the top
- * and bottom of a boxed item in the output.
- *
- */
-
- /*
- * PSEUDO CODE
- *
- * Begin asterisks
- * For the specified number of asterisks
- * Accumulate asterisk in buffer.
- * End for
- * Null terminate buffer string.
- * Emit command to cause line to be centered.
- * Emit asterisks buffer.
- * End asterisks
- *
- */
-
- asterisks(how_many)
- int how_many;
- {
- int count;
- char buffer[128];
- char *bp;
-
- for (bp = buffer, count = 0 ; count < how_many; count++) {
- *bp++ = '*';
- }
- *bp = NULL;
- emit(".ce",NULL);
- emit(buffer,'\\');
- }
-
- /*
- * FUNCTION
- *
- * emit_bline emit command to form blank line
- *
- * KEY WORDS
- *
- * blank line
- * emit functions
- *
- * SYNOPSIS
- *
- * emit_bline()
- *
- * DESCRIPTION
- *
- * Emits a suitable command to form a blank line in the
- * output. Checks first to verify that output is not
- * being suppressed and the the current section is
- * enabled for processing. Finally, the current section
- * must be enabled for text emission.
- *
- */
-
- /*
- * PSEUDO CODE
- *
- * Begin emit_bline
- * If there is a current section being processed then
- * If output is not suppressed and processing is enabled
- * If the section is enabled for text emission then
- * Emit command to form blank line.
- * End if
- * End if
- * End if
- * End emit_bline
- *
- */
-
- emit_bline ()
- {
- if (section != NULL) {
- if (!suppress && (section->flags & PROCESS)) {
- if (section->flags & EMITTEXT) {
- emit(".sp 1",NULL);
- }
- }
- }
- }
-
- /*
- * FUNCTION
- *
- * set_section lookup and set current section
- *
- * KEY WORDS
- *
- * emit functions
- * section selection
- * region suppression
- *
- * SYNOPSIS
- *
- * set_section(name)
- * char *name;
- *
- * DESCRIPTION
- *
- * Looks up the specified section and makes it the
- * current section. Resets the region
- * "box-emitted" and "suppress processing" flags.
- * Also sets the indentation level back to the left
- * margin and emits commands to print the section
- * name (underlined).
- *
- */
-
- /*
- * PSEUDO CODE
- *
- * Begin set_section
- * Reset the box emitted flag for region.
- * Lookup the section and make it current.
- * If section name was found in table then
- * If the section begins a region then
- * If the section is to be processed
- * The region is processed also.
- * Switch output stream.
- * Emit cmd for indentation.
- * Set indentation to zero.
- * Emit cmd for filled mode.
- * Set filled mode.
- * Else
- * The region is suppressed.
- * End if
- * End if
- * If not suppressed and can be processed
- * Set indentation level back.
- * If new page is desired then
- * Emit command to start new page.
- * End if
- * If text emission is enabled then
- * If name is to be underlined
- * Emit underline command.
- * End if
- * Emit section name.
- * End if
- * End if
- * End if
- * End set_section
- *
- */
-
- set_section(name)
- char *name;
- {
- struct tbl_data *tbl_find();
-
- box_emitted = FALSE;
- if (debug) {printf("set_section: looking for \"%s\"\n",name);}
- section = tbl_find(name);
- if (section != NULL) {
- if (section->flags & REGION) {
- if (debug) {printf("set_section: REGION flag set\n");}
- if (section->flags & PROCESS) {
- if (debug) {printf("set_section: PROCESS flag set\n");}
- suppress = FALSE;
- set_output(section);
- emit(".in 0",NULL);
- indent_level = 0;
- emit(".fi",NULL);
- filled = TRUE;
- } else {
- suppress = TRUE;
- }
- }
- if (!suppress && (section->flags & PROCESS)) {
- if (debug) {printf("set_section: PROCESS flag set\n");}
- indentation(0);
- if (section->flags & EMITBP) {
- emit(".bp",NULL);
- }
- if (section->flags & EMITTEXT) {
- if (debug) {printf("set_section: EMITTEXT flag set\n");}
- if (section->flags & EMITUL) {
- emit(".ul 1",NULL);
- }
- emit(name,'\\');
- }
- }
- }
- }
-
- /*
- * FUNCTION
- *
- * reset_section reset current section
- *
- * KEY WORDS
- *
- * sections
- *
- * SYNOPSIS
- *
- * reset_section();
- *
- * DESCRIPTION
- *
- * Resets current section so that there is no section selected.
- * This is usually used when a non documentation line
- * (I.E code) is encountered. It terminates processing of the
- * current section.
- *
- */
-
- /*
- * PSEUDO CODE
- *
- * Begin reset_section
- * Set current section to NULL.
- * End reset section
- *
- */
-
- reset_section()
- {
- section = NULL;
- }
-
- /*
- * FUNCTION
- *
- * emit_filled emit text in filled mode
- *
- * KEY WORDS
- *
- * emit functions
- * text emission
- *
- * SYNOPSIS
- *
- * emit_filled(text)
- * char *text;
- *
- * DESCRIPTION
- *
- * Attempts to emit text in filled mode. If filled
- * mode is not enabled for the section then emits
- * the text in unfilled mode. Also attempts
- * to print the first field of the text in a box,
- * if the box output is enabled and has not yet been
- * emitted for the current documentation section.
- *
- */
-
- /*
- * PSEUDO CODE
- *
- * Begin emit_filled
- * If there is a current section then
- * If section is not suppressed and can be processed
- * If box emission is enabled for section
- * If box has not yet been emitted then
- * Get string to box.
- * Emit command for blank lines.
- * Emit commands for box top.
- * Emit commands to center string.
- * Emit commands for box bottom.
- * Emit commands for blank lines.
- * Set the box emitted flag.
- * End if
- * End if
- * If text emission is enabled then
- * Set indentation level.
- * If section is enabled for fill then
- * Emit commands for fill.
- * Else
- * Emit commands for no fill.
- * End if
- * Emit the text.
- * End if
- * End if
- * End if
- * End emit_filled
- *
- */
-
- emit_filled(text)
- char *text;
- {
- char buffer[128];
-
- if (section != NULL) {
- if (!suppress && (section->flags & PROCESS)) {
- if (section->flags & EMITBOX) {
- if (!box_emitted) {
- xfield(title,text);
- emit(".sp 3",NULL);
- asterisks(strlen(title)+4);
- emit(".ce",NULL);
- sprintf(buffer,"* %s *",title);
- emit(buffer,'\\');
- asterisks(strlen(title)+4);
- emit(".sp 3",NULL);
- box_emitted = TRUE;
- }
- }
- if (section->flags & EMITTEXT) {
- indentation(8);
- if (section->flags & EMITFILL) {
- fill();
- } else {
- nofill();
- }
- emit(text,'\\');
- }
- }
- }
- }
-
- /*
- * FUNCTION
- *
- * emit_unfilled emit text in unfilled mode
- *
- * KEY WORDS
- *
- * text emission
- * emit functions
- *
- * SYNOPSIS
- *
- * emit_unfilled(text)
- * char *text;
- *
- * DESCRIPTION
- *
- * Emits text in unfilled mode providing there is
- * a current section, it is not being suppressed,
- * and it is enabled for processing and text emission.
- *
- */
-
- /*
- * PSEUDO CODE
- *
- * Begin emit_unfilled
- * If there is a current section then
- * If section is not suppressed and can be processed
- * If section is enabled for text emission
- * Set nofill mode.
- * Set indentation level.
- * Emit text.
- * End if
- * End if
- * End if
- * End emit_unfilled
- *
- */
-
- emit_unfilled(text)
- char *text;
- {
- if (section != NULL) {
- if (!suppress && (section->flags & PROCESS)) {
- if (section->flags & EMITTEXT) {
- nofill();
- indentation(16);
- emit(text,'\\');
- }
- }
- }
- }
-
- /*
- * FUNCTION
- *
- * set_output switch output stream
- *
- * KEY WORDS
- *
- * output switching
- *
- * SYNOPSIS
- *
- * set_output(region)
- * struct tbl_data *region;
- *
- * DESCRIPTION
- *
- * Tests to see if the region output has been redirected
- * to a file. If so, opens the file if necessary and
- * remembers the file pointer in the table entry structure.
- *
- * If no file is specified then the output goes to the
- * standard output.
- *
- * Initializes the global output pointer to point to
- * the stream where output goes.
- *
- */
-
- /*
- * PSEUDO CODE
- *
- * Begin set_output
- * If the table entry pointer is valid then
- * If the region has no output file
- * Direct output to standard out.
- * Else
- * If the file is open then
- * Set output to go to file.
- * Else
- * If file open succeeds
- * Remember file pointer.
- * Else
- * Set output to stdout.
- * End if
- * End if
- * End if
- * End if
- * End set_output
- *
- */
-
- set_output(region)
- struct tbl_data *region;
- {
- if (region != NULL) {
- if (region->out == NULL) {
- fp = stdout;
- } else {
- if (region->ofp != NULL) {
- fp = region->ofp;
- } else {
- if ((fp = fopen(region->out,"w")) != NULL) {
- region->ofp = fp;
- } else {
- fp = stdout;
- }
- }
- }
- }
- }
-
- /*
- * FUNCTION
- *
- * emit do actual output to current output stream
- *
- * KEY WORDS
- *
- * I/O functions
- * emit
- *
- * SYNOPSIS
- *
- * emit(text,quote)
- * char *text;
- * char quote;
- *
- * DESCRIPTION
- *
- * Calls appropriate operating system and/or library
- * functions to do the actual I/O. Is localized here
- * for both logical and portability reasons.
- *
- * Emit will automatically append a newline to the output
- * of each string. Also, if the quote character is not
- * null then all '\' and '.' characters will be preceeded by a
- * '\' character.
- *
- */
-
- /*
- * PSEUDO CODE
- *
- * Begin emit
- * If text pointer is not invalid pointer then
- * While there is another character to emit
- * If quote flag set and char needs quoting
- * Emit quote character.
- * End if
- * Emit character.
- * End while
- * Emit newline character.
- * End if
- * End emit
- *
- */
-
- emit(text,quote)
- char *text;
- char quote;
- {
- if (text != NULL) {
- while (*text != NULL) {
- if (quote != NULL && (*text == '.' || *text == '\\')) {
- fputc(quote,fp);
- }
- fputc(*text++,fp);
- }
- fputc('\n',fp);
- }
- }
-
- #ifndef unix
- perror (err)
- char *err;
- {
- if (err && *err) {
- fprintf (stderr, "%s: ", err);
- }
- fprintf (stderr, "<unknown error!>\n");
- }
- #endif unix
-