home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume42
/
c2man-2.0
/
part01
next >
Wrap
Internet Message Format
|
1994-05-06
|
52KB
From: greyham@research.canon.oz.au (Graham Stoney)
Newsgroups: comp.sources.misc
Subject: v42i055: c2man-2.0 - automatic C documentation generator, Part01/09
Date: 25 Apr 1994 22:29:25 -0500
Organization: Canon Information Systems Research Australia
Sender: kent@sparky.sterling.com
Approved: kent@sparky.sterling.com
Message-ID: <csm-v42i055=c2man-2.0.222803@sparky.sterling.com>
Summary: a complete posting of the latest c2man: version 2.0 patchlevel 27
X-Md4-Signature: 12aa964efe2a5224cb5b1dca080d0745
Submitted-by: greyham@research.canon.oz.au (Graham Stoney)
Posting-number: Volume 42, Issue 55
Archive-name: c2man-2.0/part01
Environment: UNIX, DOS, OS/2, lex, yacc
Note: c2man was previously posted in comp.sources.reviewed, but the patches
were lagging months and months behind and then the moderator recently resigned,
so unfortunately I've been forced to switch to comp.sources.misc.
c2man, Version 2
by Graham Stoney
Copyright (c) 1992, 1993, 1994 by Canon Information Systems Research Australia
All rights reserved.
This is c2man, a program for generating Unix style manual pages in nroff/troff
-man, TeXinfo or LaTeX format directly from ordinary comments embedded in C
source code. It should run on virtually any Unix-like system, OS/2 or MSDOS.
You will need lex or flex, plus yacc or bison, and a C compiler (traditional
K&R 1 or ISO/ANSI will do) to build the program. You'll also need a text
formatter to format its output.
This version of c2man is copyright, but may be freely redistributed and modified
so long as:
1. The names of all contributing authors remain on the documentation,
2. All derivative works are clearly documented as such,
3. All derivative works remain freely redistributable under the same conditions.
As such, there is no warranty.
#! /bin/sh
#
# This is c2man version 2.0 at patchlevel 27.
# Make a new directory for the c2man sources, cd to it, and run kits 1 up
# to 9 through sh. When all 9 kits have been run, read README.
#
echo " "
cat <<EOM
This is c2man 2.0 at patchlevel 27, kit 1 (of 9):
If this shell archive is complete, the line "End of kit 1 (of 9)"
will echo at the end.
EOM
export PATH || (echo "Please use sh to unpack this archive." ; kill $$)
mkdir eg pc 2>/dev/null
echo Extracting README
sed >README <<'!STUFFY!FUNK!' -e 's/X//'
X
X c2man, Version 2
X by Graham Stoney
X
X Copyright (c) 1992, 1993, 1994 by Canon Information Systems Research Australia
X All rights reserved.
X
XThis is c2man, a program for generating Unix style manual pages in nroff/troff
X-man, TeXinfo or LaTeX format directly from ordinary comments embedded in C
Xsource code. It should run on virtually any Unix-like system, OS/2 or MSDOS.
X
XYou will need lex or flex, plus yacc or bison, and a C compiler (traditional
XK&R 1 or ISO/ANSI will do) to build the program. You'll also need a text
Xformatter to format its output.
X
XThis version of c2man is copyright, but may be freely redistributed and modified
Xso long as:
X
X1. The names of all contributing authors remain on the documentation,
X2. All derivative works are clearly documented as such,
X3. All derivative works remain freely redistributable under the same conditions.
X
XAs such, there is no warranty.
X
Xc2man owes a huge debt to the public domain program cproto, by Chin Huang, from
Xwhich much of the code is derived.
X
XThe manual page must be installed with 'make install'; it includes an
Xautomatically generated example and needs to be "flattened" when installed, so
Xyou can't just cp it into place. As a further consequence, the examples will be
Xmissing from the manual page if you try to read it before doing a make.
X
Xc2man does not currently support C++, but if you think this would be worth
Xwhile, look in the file C++autodoc for information on how I envisage C++
Xsupport could be added, and get ready to volunteer.
X
XBy popular demand, there are a few trivial examples of different comment
Xstyles in the eg directory. I'm open to submissions from users too.
X
X
XThere is a mailing list for c2man users; it is very low volume and has a very
Xlow noise content. This is the preferred place to ask questions about the
Xprogram and discuss modifications and additions with the author and other
Xusers. You are encouraged to join by sending mail with no Subject: line to
X<listserv@research.canon.oz.au> containing:
X
X SUBSCRIBE c2man Your name
X
XWhere `Your name' should be replaced with your real name.
XMessages for distribution to everyone on the list should be sent to:
X<c2man@research.canon.oz.au>.
X
X
XThe time I have available for c2man support is rather limited, but if it lacks
Xany features you require, feel free to Email me (preferably to the mailing list
Xaddress above) asking about it. Unless you request otherwise, I will probably
Xcc: to the list replies to any questions that I get mailed, to save me
Xanswering them again for other people. I encourage you to go ahead and make
Xany changes you like and send me the diffs for inclusion in the next patch, but
Xit's a good idea to ask first in case someone already has the feature you want
Xin the works. Changes need to be reasonably "clean" for me to integrate them.
X
XPlease try to remember to include the c2man version number in any bug reports.
XYou can find it by running: c2man -V /dev/null
X
XIf you'd like to be notified automatically about new releases and patches,
Xanswer yes to the Configure question about sending mail to the author.
X
X
XSpecial thanks for their direct and indirect contributions to c2man go to:
X Larry Wall, Raphael Manfredi and Harlan Stenn for writing various bits of
X metaconfig, which generated the Configure script.
X
X Darrel Hankerson for the OS/2 and MSDOS ports.
X
X Richard Kooijman for the LaTeX backend, and for fixing the TeXinfo backend.
X
X Vern Paxson for his suggestions on how to handle comments lexing better.
X
XThanks to the following people for suggestions & bug fixes is long overdue:
X Peter (P.) Barszczewski, Carlo Tarantola, Dennis Allison,
X Philip Yzarn de Louraille, Jerry Lieberthal, Mats Ohrman, Stefan Zimmermann,
X Dolf Grunbauer, Lele Gaifax, Carl R. Crawford, Jhon Honce, Chris Borchert,
X Jerry E. Dunmire, Marty Leisner, Dan Forrest, Ken Weinert, Ken Poppleton.
X
X(Hmmm. This is beginning to sound like an Academy Awards night...)
X
X
XSee the file INSTALL for Unix installation instructions.
XOS/2 and MSDOS users should see the README.pc file in the pc directory.
X
X
XGraham Stoney greyham@research.canon.oz.au
XMailing List for general c2man Questions & Answers c2man@research.canon.oz.au
!STUFFY!FUNK!
echo Extracting pc/README.pc
sed >pc/README.pc <<'!STUFFY!FUNK!' -e 's/X//'
XThis directory contains sources for compiling c2man for OS/2 and
XDOS, using EMX/gcc or Microsoft C. The popen.c routines are adapted
Xfrom the versions supplied with GNU awk, and are needed for
Xexecutables which run under DOS. The getopt routines (needed for MSC)
Xare adapted from the GNU versions.
X
XTo install, copy these files to the directory containing the
Xsources for c2man, and use
X make -f Makefile.pc
Xto see a list of targets.
X
XThis version is still in beta-test.
X
X--
XDarrel Hankerson hankedr@mail.auburn.edu
X23 August 1993
!STUFFY!FUNK!
echo Extracting manpage.c
sed >manpage.c <<'!STUFFY!FUNK!' -e 's/X//'
X/* $Id: manpage.c,v 2.0.1.41 1994/02/23 04:38:28 greyham Exp $
X * stuff to do with manual page outputing
X */
X
X#include "c2man.h"
X
X#include <errno.h>
X#include <ctype.h>
X
X#include "manpage.h"
X#include "strconcat.h"
X#include "strappend.h"
X#include "semantic.h"
X#include "output.h"
X
X#ifdef I_SYS_FILE
X#include <sys/file.h>
X#endif
X
X/* list of manual pages */
XManualPage *firstpage = NULL;
XManualPage **lastpagenext = &firstpage;
X
Xvoid dummy() {}
X
Xvoid
Xnew_manual_page(comment, decl_spec, declarator)
X char *comment;
X DeclSpec *decl_spec;
X Declarator *declarator;
X{
X ManualPage *newpage;
X
X /* check that we really want a man page for this */
X if ((!comment) ||
X !inbasefile ||
X (!variables_out && !is_function_declarator(declarator)) ||
X (decl_spec->flags & DS_JUNK) ||
X (!static_out && (decl_spec->flags & DS_STATIC) && !header_file) ||
X
X /* only document extern stuff if it's in a header file, or includes a
X * function definition.
X */
X ((decl_spec->flags & DS_EXTERN) && !header_file &&
X declarator->type != DECL_FUNCDEF))
X {
X free_decl_spec(decl_spec);
X free_declarator(declarator);
X safe_free(comment);
X return;
X }
X
X declarator->comment = comment;
X
X newpage = (ManualPage *)safe_malloc(sizeof *newpage);
X newpage->decl_spec = (DeclSpec *)safe_malloc(sizeof *newpage->decl_spec);
X newpage->declarator = declarator;
X
X *newpage->decl_spec = *decl_spec;
X newpage->sourcefile = strduplicate(basefile);
X newpage->sourcetime = basetime;
X
X *lastpagenext = newpage;
X newpage->next = NULL;
X lastpagenext = &newpage->next;
X}
X
Xvoid free_manual_page(page)
X ManualPage *page;
X{
X free_decl_spec(page->decl_spec);
X free(page->decl_spec);
X free_declarator(page->declarator);
X safe_free(page->sourcefile);
X}
X
X/* free the list of manual pages */
Xvoid free_manual_pages(first)
X ManualPage *first;
X{
X ManualPage *page, *next;
X
X /* free any terse description read from the file */
X if (group_terse && !terse_specified)
X {
X free(group_terse);
X group_terse = NULL;
X }
X
X for (page = first;page;page = next)
X {
X next = page->next;
X free_manual_page(page);
X free(page);
X }
X}
X
X/* allocate a substring starting at start, ending at end (NOT including *end) */
Xchar *alloc_string(start, end)
X const char *start;
X const char *end;
X{
X int len = end - start;
X char *ret;
X if (len == 0) return NULL;
X
X ret = (char *)safe_malloc((size_t)len+1);
X
X strncpy(ret,start,len);
X ret[len] = '\0';
X
X return ret;
X}
X
X/* remember the terse description from the first comment in a file */
Xvoid remember_terse(comment)
X char *comment;
X{
X char *c, *d;
X
X enum { STUFF, LEADSPACE, DASH, TRAILSPACE, FOUND } state = STUFF;
X
X /* if we've found a terse comment in a previous file, or one was
X * specified on the command line, forget it.
X */
X if (group_terse) return;
X
X /* look for a whitespace surrounded sequence of dashes to skip */
X for (c = comment;*c && state != FOUND;c++)
X {
X switch (state)
X {
X case STUFF: if (isspace(*c)) state = LEADSPACE;
X break;
X case LEADSPACE: if (*c == '-') state = DASH;
X else if (!isspace(*c)) state = STUFF;
X break;
X case DASH: if (isspace(*c)) state = TRAILSPACE;
X else if (*c != '-') state = STUFF;
X break;
X case TRAILSPACE:if (!isspace(*c)) { c--; state = FOUND; }
X break;
X case FOUND: break;
X }
X }
X
X /* if no dashes were found, go back to the start */
X if (state != FOUND) c = comment;
X
X d = c + 1;
X
X while (*d && *d != '\n')
X d++;
X
X group_terse = alloc_string(c,d);
X}
X
X/* output a comment in man page form, followed by a newline */
Xvoid
Xoutput_comment(comment)
Xconst char *comment;
X{
X enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE;
X boolean new_line = TRUE;
X boolean dot_command = FALSE;
X
X if (!comment || !comment[0])
X {
X output->text("Not Documented.\n");
X return;
X }
X
X /* correct punctuation a bit as it goes out */
X for (;*comment;comment++)
X {
X int c = *comment;
X
X if (dot_command)
X {
X if (c == '\n') dot_command = FALSE;
X }
X else if (new_line && c == '.')
X dot_command = TRUE;
X else if (new_line && !isalpha(c) && c != '"' && c != '\'' && c != '`')
X {
X output->break_line();
X state = CAPITALISE;
X }
X else if (c == '.')
X state = PERIOD;
X else if (isspace(c) && state == PERIOD)
X state = CAPITALISE;
X else if (isalnum(c) && state == CAPITALISE)
X {
X if (islower(c)) c = toupper(c);
X state = TEXT;
X }
X
X output->character(c);
X new_line = c == '\n';
X }
X
X /* do a full stop if there wasn't one */
X if (!dot_command && state == TEXT) output->character('.');
X
X output->character('\n');
X}
X
X/* output the returns section in man page form, followed by a newline */
Xvoid
Xoutput_returns(comment)
Xchar *comment;
X{
X enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE;
X char lastchar = '\n';
X boolean tag_list_started = FALSE;
X
X /* for each line... */
X while (*comment)
X {
X boolean tagged = FALSE;
X
X /* explicitly reject dot commands */
X if (*comment && *comment != '.')
X {
X char *c = comment;
X
X /* search along until the end of a word */
X while (*c && *c != ':' && !isspace(*c))
X c++;
X
X /* skip all spaces or tabs after the first word */
X while (*c && *c != '\n')
X {
X if (*c == '\t' || *c == ':')
X {
X tagged = TRUE;
X break;
X }
X else if (!isspace(*c))
X break;
X
X c++;
X }
X }
X
X /* is it tagged?; explicitly reject dot commands */
X if (tagged)
X {
X /* output lingering newline if necessary */
X if (lastchar != '\n')
X {
X if (state == TEXT && !ispunct(lastchar)) output->character('.');
X output->character(lastchar = '\n');
X }
X
X if (!tag_list_started)
X {
X output->tag_list_start();
X tag_list_started = TRUE;
X }
X
X /* output the taggy bit */
X output->tag_entry_start();
X while (*comment && *comment != ':' && !isspace(*comment))
X output->character(*comment++);
X output->tag_entry_end();
X
X /* skip any extra tabs or spaces */
X while (*comment == ':' || (isspace(*comment) && *comment != '\n'))
X comment++;
X
X state = CAPITALISE;
X }
X
X /* terminate the previous line if necessary */
X if (lastchar != '\n') output->character(lastchar = '\n');
X
X /* dot commands go out unaltered */
X if (*comment == '.')
X {
X for (;*comment && *comment != '\n'; comment++)
X output->character(*comment);
X output->character('\n');
X }
X else
X {
X /* correct punctuation a bit as the line goes out */
X for (;*comment && *comment != '\n'; comment++)
X {
X char c = *comment;
X
X if (c == '.')
X state = PERIOD;
X else if (isspace(c) && state == PERIOD)
X state = CAPITALISE;
X else if (isalnum(c) && state == CAPITALISE)
X {
X if (islower(c)) c = toupper(c);
X state = TEXT;
X }
X
X output->character(lastchar = c);
X }
X
X /* if it ended in punctuation, just output the nl straight away. */
X if (ispunct(lastchar))
X {
X if (lastchar == '.') state = CAPITALISE;
X output->character(lastchar = '\n');
X }
X }
X
X if (*comment) comment++;
X }
X
X /* output lingering newline if necessary */
X if (lastchar != '\n')
X {
X if (state == TEXT && !ispunct(lastchar)) output->character('.');
X output->character('\n');
X }
X
X if (tag_list_started)
X output->tag_list_end();
X
X}
X
X/* output the description for an identifier; be it return value or param */
Xstatic void output_identifier_description(comment, outfunc,
X decl_spec, declarator)
X const char *comment; /* comment for this identifier */
X void (*outfunc) _((const char *)); /* function to output comment */
X const DeclSpec *decl_spec;
X const Declarator *declarator;
X{
X /* one day, this may document the contents of structures too */
X
X /* output list of possible enum values, if any */
X if (decl_spec->enum_list)
X {
X int maxtaglen = 0;
X int descriptions = 0;
X Enumerator *e;
X int is_first = 1;
X boolean started = FALSE;
X
X /* don't output the "Not Doc." message for enums */
X if (comment)
X {
X (*outfunc)(comment);
X output->blank_line();
X }
X
X /* see if any have descriptions */
X for (e = decl_spec->enum_list->first; e; e = e->next)
X if (e->name[0] != '_')
X {
X int taglen = strlen(e->name);
X if (taglen > maxtaglen) maxtaglen = taglen;
X if (e->comment) descriptions = 1;
X }
X
X output->text("Possible values for a");
X if (strchr("aAeEiIoOuU",decl_spec->text[0])) output->character('n');
X output->character(' ');
X output->code(decl_spec->text);
X output->text(" are as follows:\n");
X
X for (e = decl_spec->enum_list->first; e; e = e->next)
X {
X /* don't print names with a leading underscore! */
X if (e->name[0] == '_') continue;
X
X if (e->group_comment)
X {
X /* break out of table mode for the group comment */
X if (started)
X {
X if (descriptions)
X output->table_end();
X else
X output->list_end();
X started = FALSE;
X }
X output->indent();
X output_comment(e->group_comment);
X }
X
X if (!started)
X {
X if (descriptions)
X output->table_start();
X else
X output->list_start();
X started = TRUE;
X }
X
X if (descriptions)
X output->table_entry(e->name, maxtaglen, e->comment);
X else
X {
X if (!is_first)
X output->list_separator();
X is_first = 0;
X output->list_entry(e->name);
X }
X }
X
X if (started)
X {
X if (descriptions)
X output->table_end();
X else
X output->list_end();
X }
X }
X else
X (*outfunc)(comment);
X}
X
X/* is there automatic documentation here? */
Xstatic boolean auto_documented(page)
Xconst ManualPage *page;
X{
X /* one day we may handle structs too */
X return
X page->decl_spec->enum_list != NULL; /* enums are self-documenting. */
X}
X
X/* decide if a manual page needs a RETURNS section.
X * If this is true, then output_identifier_description must be able to generate
X * sensible output for it.
X */
Xstatic boolean needs_returns_section(page)
Xconst ManualPage *page;
X{
X return
X (page->returns && page->returns[0]) ||
X (auto_documented(page) && is_function_declarator(page->declarator));
X}
X
X/* does this declarator have documented parameters? */
Xboolean has_documented_parameters(d)
Xconst Declarator *d;
X{
X if (has_parameters(d))
X {
X Parameter *p;
X
X for (p = d->head->params.first; p != NULL; p = p->next)
X if (p->declarator->comment || always_document_params)
X return TRUE;
X }
X return FALSE;
X}
X
X/* Output the list of function parameter descriptions.
X */
Xvoid
Xoutput_parameter_descriptions (params, function)
XParameterList *params;
Xchar *function;
X{
X Parameter *p;
X boolean tag_list_started = FALSE;
X
X for (p = params->first; p != NULL; p = p->next)
X {
X if (p->suppress ||
X (!always_document_params && p->declarator->comment == NULL))
X continue;
X
X if (!tag_list_started)
X {
X output->tag_list_start();
X tag_list_started = TRUE;
X }
X
X if (p->duplicate)
X output->tag_entry_start_extra();
X else
X output->tag_entry_start();
X
X output_parameter(p);
X
X /* include function name if it's a duplicate */
X if (p->duplicate)
X output->tag_entry_end_extra(function);
X else
X output->tag_entry_end();
X
X output_identifier_description(p->declarator->comment, output_comment,
X &p->decl_spec, &p->declarator);
X }
X
X if (tag_list_started)
X output->tag_list_end();
X}
X
X/* split out the 'Returns:' section of a function comment */
Xboolean
Xsplit_returns_comment(comment, description, returns)
X char *comment;
X char **description;
X char **returns;
X{
X char *retstart;
X
X for (retstart = comment;
X retstart;
X retstart = strchr(retstart,'\n'))
X {
X if (*retstart == '\n') retstart++; /* skip the newline */
X
X if (!strncmpi(retstart, "returns",(size_t)7))
X {
X char *descend = retstart - 2; /* back before newline */
X
X /* go back to the end of the description in case there were
X * linefeeds before the returns.
X */
X while (descend > comment && isspace(*descend))
X descend--;
X
X *description =
X descend > comment ? alloc_string(comment,descend+1) : NULL;
X
X retstart += 7;
X
X while (*retstart == ':' || isspace(*retstart))
X retstart++;
X
X if (*retstart)
X *returns = strduplicate(retstart);
X else
X *returns = NULL;
X return TRUE;
X }
X }
X
X *description = comment;
X *returns = NULL;
X return FALSE;
X}
X
X/* skip to past the dash on the first line, if there is one
X * The dash must be surrounded by whitespace, so hyphens are not skipped.
X */
Xconst char *skipdash(c)
Xconst char *c;
X{
X const char *d;
X
X /* ignore anything on the first line, up to a dash (if any) */
X for (d = c + 1; *d && *d != '\n' && *d != '-'; d++)
X ;
X
X if (isspace(d[-1]) && d[0] == '-' && isspace(d[1]))
X {
X do
X d++;
X while (*d && *d != '\n' && isspace(*d));
X
X if (*d && *d != '\n') c = d;
X }
X return c;
X}
X
X/* split the function comment into manual page format */
Xvoid
Xsplit_function_comment(comment, identifier_name,
X terse, description, returns, extra_sections)
X const char *comment;
X const char *identifier_name;
X char **terse;
X char **description;
X char **returns;
X Section **extra_sections;
X{
X const char *c, *start_text = NULL, *end_text = NULL;
X char **put_ptr = NULL;
X Section *first_section, **lastnextsection = &first_section;
X boolean explicit_description = FALSE;
X boolean lastblank = TRUE;
X boolean skip_dash = FALSE;
X
X *description = *returns = NULL;
X if (terse) *terse = NULL;
X
X /* for each line... */
X for (c = comment; *c;)
X {
X const char *start_line = c;
X boolean section_heading;
X /* remember if it's a blank line */
X if (*c == '\n')
X {
X lastblank = TRUE;
X c++;
X continue;
X }
X
X /* if the last one was blank, perhaps this one is a section heading
X */
X if (lastblank)
X {
X /* see if we've found the start of a SECTION */
X while (isalpha(*c))
X c++;
X
X section_heading = *c == '\n' || *c == ':' ||
X (*c == '\0' && start_line == comment);
X }
X else
X section_heading = FALSE;
X
X lastblank = FALSE; /* this one's not blank; for next time */
X
X if (section_heading)
X {
X size_t section_len = c - start_line; /* length of section name */
X
X /* yes, we've found a SECTION; store the previous one (if any) */
X if (put_ptr && start_text)
X {
X if (skip_dash) start_text = skipdash(start_text);
X *put_ptr = alloc_string(start_text,end_text);
X }
X
X skip_dash = FALSE;
X
X /* check for comments that start with the name of the identifier */
X if (start_line == comment &&
X !strncmp(start_line, identifier_name, section_len))
X {
X put_ptr = description;
X }
X
X /* only accept NAME if not grouped */
X else if (terse &&
X (!strncmpi(start_line,"NAME", section_len) ||
X !strncmpi(start_line,"FUNCTION", section_len) ||
X !strncmpi(start_line,"PROCEDURE", section_len) ||
X !strncmpi(start_line,"ROUTINE", section_len))
X )
X
X {
X put_ptr = terse;
X skip_dash = TRUE;
X }
X else if (!strncmpi(start_line,"DESCRIPTION", section_len))
X {
X explicit_description = TRUE;
X put_ptr = description;
X }
X else if (!strncmpi(start_line,"RETURNS", section_len))
X {
X put_ptr = returns;
X }
X else
X {
X /* allocate a new section */
X Section *new_section =
X (Section *)safe_malloc(sizeof *new_section);
X
X *lastnextsection = new_section;
X lastnextsection = &new_section->next;
X
X new_section->name = alloc_string(start_line,c);
X strtoupper(new_section->name);
X new_section->text = NULL;
X new_section->been_output = FALSE; /* not been output yet */
X put_ptr = &new_section->text;
X }
X
X /* defer decision about where text starts till we find some */
X start_text = NULL;
X
X if (*c == ':') /* skip the terminating : */
X {
X c++;
X
X /* skip forward to the start of the text */
X while (*c && *c != '\n' && isspace(*c))
X c++;
X
X /* if we find the text here, then we've got it */
X if (*c && *c != '\n')
X start_text = c;
X }
X }
X else
X {
X /* are we looking at the top of the function comment? */
X if (start_line == comment)
X {
X /* only look for terse comment if not grouped together */
X if (terse)
X {
X const char *endterse, *afterdash = skipdash(start_line);
X
X /* find the end of the terse comment */
X while (*c && *c != '.' && *c != '\n')
X c++;
X
X endterse = *c == '.' ? c+1 : c;
X *terse = alloc_string(
X afterdash < endterse ? afterdash : start_line,
X endterse);
X
X /* skip it if it's a ., and any trailing spaces */
X if (*c == '.')
X do c++; while (*c && *c != '\n' && isspace(*c));
X
X start_text = NULL; /* look for it */
X
X if (*c && *c != '\n')
X /* actually, it's a description, starting here */
X start_text = c;
X }
X /* must be a description starting at the beginning of the line.
X */
X else
X start_text = start_line;
X
X put_ptr = description;
X }
X else
X /* have we just located the first real text in a section? */
X if (put_ptr && !start_text) start_text = start_line;
X }
X
X /* skip the line */
X if (*c && *c != '\n')
X while (*c && *c != '\n') c++;
X
X end_text = c; /* so far, the text ends at the end of this line */
X if (*c) c++;
X }
X
X /* store the last one */
X if (put_ptr && start_text)
X {
X if (skip_dash) start_text = skipdash(start_text);
X *put_ptr = alloc_string(start_text,end_text);
X }
X
X /* terminate (or nuke) section list */
X *lastnextsection = NULL;
X
X /* if there wasn't a RETURNS section, and the DESCRIPTION field was not
X * explicit, see if we can split one out of the description field.
X */
X if (*returns == NULL && !explicit_description)
X {
X char *olddesc = *description;
X if (split_returns_comment(olddesc, description, returns))
X free(olddesc);
X }
X
X *extra_sections = first_section;
X}
X
X/* see if two parameters are declared identically */
Xboolean params_identical(first, second)
X Parameter *first;
X Parameter *second;
X{
X return
X first->decl_spec.flags == second->decl_spec.flags &&
X
X /* there may be no decl_spec.text if it's an ellipsis arg */
X ((!first->decl_spec.text && !second->decl_spec.text) ||
X (first->decl_spec.text && second->decl_spec.text &&
X !strcmp(first->decl_spec.text, second->decl_spec.text))) &&
X
X ((!first->declarator->text && !second->declarator->text) ||
X (first->declarator->text && second->declarator->text &&
X !strcmp(first->declarator->text, second->declarator->text)));
X}
X
X/* search all the parameters in this grouped manual page for redundancies */
Xboolean mark_duplicate_parameters(firstpage)
X ManualPage *firstpage;
X{
X Parameter *param;
X boolean any = FALSE;
X ManualPage *page;
X
X for (page = firstpage; page; page = page->next)
X {
X if (has_parameters(page->declarator))
X for (param = page->declarator->head->params.first; param;
X param = param->next)
X {
X ManualPage *otherpage;
X Parameter *otherparam;
X
X if (always_document_params || param->declarator->comment)
X any = TRUE;
X
X for (otherpage = page->next; otherpage;
X otherpage = otherpage->next)
X {
X if (has_parameters(otherpage->declarator))
X for (otherparam = otherpage->declarator->head->params.first;
X otherparam;
X otherparam = otherparam->next)
X {
X /* do these two look the same? */
X if (params_identical(param, otherparam))
X {
X /* order is important for bit positions */
X enum { NEITHER, US, THEM, BOTH } has_comm = NEITHER;
X
X /* work out who has the comment */
X if (param->declarator->comment) has_comm |= US;
X if (otherparam->declarator->comment) has_comm |= THEM;
X
X switch(has_comm)
X {
X case NEITHER:
X case US:
X otherparam->suppress = TRUE;
X break;
X case THEM:
X param->suppress = TRUE;
X break;
X case BOTH:
X if (!strcmp(param->declarator->comment,
X otherparam->declarator->comment))
X otherparam->suppress = TRUE;
X else
X {
X param->duplicate = TRUE;
X otherparam->duplicate = TRUE;
X }
X break;
X }
X }
X }
X }
X }
X }
X return any;
X}
X
X/* output a formatting string so that it works with filling on */
Xvoid output_format_string(fmt)
Xconst char *fmt;
X{
X while (*fmt)
X {
X output->character(*fmt);
X
X if (*fmt++ == '\n')
X output->break_line(); /* break the line */
X }
X}
X
X/* write the warning for the header */
Xvoid output_warning()
X{
X output->comment();
X output->text("WARNING! THIS FILE WAS GENERATED AUTOMATICALLY BY ");
X output->text(progname);
X output->text("!\n");
X output->comment();
X output->text("DO NOT EDIT! CHANGES MADE TO THIS FILE WILL BE LOST!\n");
X}
X
Xvoid output_includes()
X{
X IncludeFile *incfile;
X
X for (incfile = first_include; incfile; incfile=incfile->next)
X {
X char *name = incfile->name;
X boolean surrounded = *name == '"' || *name == '<';
X
X output->text("#include ");
X if (!surrounded) output->character('<');
X output->text(name);
X if (!surrounded) output->character('>');
X output->text("\n");
X output->break_line();
X }
X}
X
X/* Writes the entire contents of the manual page specified by basepage. */
Xvoid
Xoutput_manpage(firstpage, basepage, input_files, title, section)
X /* the first page in the list of all manual pages. This is used to build
X * the SEE ALSO section of related pages when group_together is false.
X */
X ManualPage *firstpage;
X
X /* the base page from which the output manual page will be generated. if
X * group_together indicates that the user wanted grouped pages, basepage
X * will always be the same as firstpage, and all the ManualPage's in the
X * list will be grouped together into the one output page.
X */
X ManualPage *basepage;
X
X int input_files;
X const char *title;
X const char *section;
X{
X ManualPage *page;
X boolean need_returns;
X char *terseout, *terse = NULL;
X
X /* check if there's more than one page in the group */
X boolean grouped = group_together && firstpage->next;
X
X /* split up all the function comments for this page */
X for (page = basepage; page; page = page->next)
X {
X split_function_comment(page->declarator->comment,
X page->declarator->name,
X group_together ? (char **)NULL : &terse,
X &page->description,&page->returns,&page->first_section);
X if (!group_together) break;
X }
X
X /* work out what we'll actually print as a terse description */
X terseout = group_terse ? group_terse : (terse ? terse : "Not Described");
X
X if (!make_embeddable)
X {
X output->header(basepage, input_files, grouped,
X title ? title : basepage->declarator->name, section);
X }
X output->section("NAME");
X
X /* output the names of all the stuff documented on this page */
X for (page = basepage; page; page = page->next)
X {
X output->text(page->declarator->name);
X
X if (!group_together) break;
X
X if (page->next) output->text(",\n");
X }
X
X output->character(' ');
X output->dash();
X output->character(' ');
X output->text(terseout);
X output->character('\n');
X
X output->section("SYNOPSIS");
X
X output->code_start();
X
X /* list the include files the user asked us to */
X output_includes();
X
X /* if it's a header file, say to #include it */
X if (header_file)
X {
X output->text("#include <");
X if (header_prefix)
X {
X output->text(header_prefix);
X output->character('/');
X }
X output->text(basefile);
X output->text(">\n");
X }
X
X /* can't just use .PP; that may reset our font */
X if (first_include || header_file) output->blank_line();
X
X for (page = basepage; page; page = page->next)
X {
X output_format_string(decl_spec_prefix);
X
X /* make sure variables are prefixed extern */
X if (!(page->decl_spec->flags & DS_STATIC) &&
X !is_function_declarator(page->declarator) &&
X !strstr(page->decl_spec->text, "extern"))
X output->text("extern ");
X
X output_decl_spec(page->decl_spec);
X output_format_string(declarator_prefix);
X
X /* format it nicely if there's more than one parameter */
X output_declarator(page->declarator,
X page->declarator->head->params.first !=
X page->declarator->head->params.last);
X
X output->text(";\n");
X
X if (!grouped) break;
X if (page->next) output->blank_line();
X }
X
X output->code_end();
X
X /* only output paramaters if there actually are some,
X * not including merely (void)
X */
X if ((grouped && mark_duplicate_parameters(basepage)) ||
X (!grouped && has_documented_parameters(basepage->declarator)))
X {
X output->section("PARAMETERS");
X
X for (page = basepage; page; page = page->next)
X {
X if (has_parameters(page->declarator))
X output_parameter_descriptions(&page->declarator->head->params,
X page->declarator->name);
X if (!grouped) break; /* only do first page */
X }
X }
X
X output->section("DESCRIPTION");
X
X if (grouped)
X {
X need_returns = FALSE;
X for (page = basepage; page; page = page->next)
X {
X if (needs_returns_section(page)) need_returns = TRUE;
X
X /* enum variables are documented in DESCRIPTION */
X if (auto_documented(page) &&
X !is_function_declarator(page->declarator))
X {
X output->sub_section(page->declarator->name);
X output_identifier_description(page->description,
X output_comment, page->decl_spec, page->declarator);
X }
X else if (page->description)
X {
X output->sub_section(page->declarator->name);
X output_comment(page->description);
X }
X
X safe_free(page->description);
X }
X }
X else
X {
X const char *descr = basepage->description ? basepage->description
X : terseout;
X
X need_returns = needs_returns_section(basepage);
X
X if (auto_documented(page) && !is_function_declarator(page->declarator))
X output_identifier_description(descr, output_comment,
X page->decl_spec, page->declarator);
X else
X output_comment(descr);
X
X safe_free(basepage->description);
X
X }
X
X /* terse can now never be a static string */
X safe_free(terse);
X
X if (need_returns)
X {
X output->section("RETURNS");
X
X for (page = basepage; page; page = page->next)
X {
X if (needs_returns_section(page))
X {
X if (grouped) output->sub_section(page->declarator->name);
X
X output_identifier_description(page->returns, output_returns,
X page->decl_spec, page->declarator);
X safe_free(page->returns);
X }
X
X if (!grouped) break;
X }
X }
X
X /* output any other sections */
X for (page = basepage; page; page = page->next)
X {
X Section *section, *next;
X
X for (section = page->first_section; section; section = next)
X {
X next = section->next;
X
X if (!section->been_output && section->text)
X {
X output->section(section->name);
X if (grouped) output->sub_section(page->declarator->name);
X output_comment(section->text);
X section->been_output = TRUE;
X
X if (grouped && page->next)
X {
X ManualPage *other_page = page->next;
X
X /* look through all the other pages for matching sections */
X for (; other_page; other_page = other_page->next)
X {
X Section *other_section = other_page->first_section;
X for (;other_section; other_section =
X other_section->next)
X {
X if (other_section->been_output ||
X strcmp(other_section->name, section->name))
X continue;
X
X output->sub_section(other_page->declarator->name);
X output_comment(other_section->text);
X other_section->been_output = TRUE;
X }
X }
X }
X }
X
X
X /* free this section */
X free(section->name);
X safe_free(section->text);
X free(section);
X }
X
X if (!grouped) break;
X }
X
X /* only output SEE ALSO if not grouped */
X if (!group_together)
X {
X ManualPage *also;
X
X /* add the SEE ALSO section */
X /* look for any other functions to refer to */
X for (also = firstpage; also && also == basepage; also = also->next)
X ;
X
X if (also) /* did we find at least one? */
X {
X int isfirst = 1;
X
X output->section("SEE ALSO");
X
X for (also = firstpage; also; also = also->next)
X {
X if (also == basepage) continue;
X
X if (!isfirst)
X output->text(",\n");
X else
X isfirst = 0;
X
X output->text(also->declarator->name);
X output->character('(');
X output->text(manual_section);
X output->character(')');
X }
X
X output->character('\n');
X }
X }
X
X if (!make_embeddable)
X output->file_end();
X}
X
X
X/* generate output filename based on a string */
Xchar *page_file_name(based_on, object_type, extension)
X /* string to base the name on; this will be the name of an identifier or
X * the base of the input file name.
X */
X const char *based_on;
X enum Output_Object object_type; /* class of object documented */
X const char *extension; /* file extension to use */
X{
X char *filename;
X const char *subdir = output_object[object_type].subdir;
X
X#ifndef FLEXFILENAMES
X char *basename;
X int chopoff = 14 - strlen(extension) - 1;
X
X basename = strduplicate(based_on);
X if (strlen(basename) > chopoff)
X basename[chopoff] = '\0';
X#else
X const char *basename = based_on;
X#endif
X
X filename = strduplicate(output_dir);
X
X if (subdir)
X {
X if (filename) filename = strappend(filename, "/", NULLCP);
X filename = strappend(filename, subdir, NULLCP);
X }
X
X if (filename) filename = strappend(filename, "/", NULLCP);
X filename = strappend(filename, basename,".",extension, NULLCP);
X
X#ifndef FLEXFILENAMES
X free(basename);
X#endif
X return filename;
X}
X
X/* determine the output page type from a declaration */
Xenum Output_Object page_output_type(decl_spec, declarator)
Xconst DeclSpec *decl_spec;
Xconst Declarator *declarator;
X{
X boolean is_static = decl_spec->flags & DS_STATIC;
X return is_function_declarator(declarator)
X ? (is_static ? OBJECT_STATIC_FUNCTION : OBJECT_FUNCTION)
X : (is_static ? OBJECT_STATIC_VARIABLE : OBJECT_VARIABLE);
X}
X
X/* determine the extension/section from an output type */
Xconst char *page_manual_section(output_type)
Xenum Output_Object output_type;
X{
X return output_object[output_type].extension ?
X output_object[output_type].extension : manual_section;
X}
X
X/* remove an existing file, if it exists & we have write permission to it */
Xint remove_old_file(name)
Xconst char *name;
X{
X#ifdef HAS_ACCESS
X /* check that we have write premission before blasting it */
X if (access(name,W_OK) == -1)
X {
X if (errno != ENOENT)
X {
X my_perror("can't access output file", name);
X return FALSE;
X }
X }
X else
X#endif
X {
X /* if it exists, blast it */
X if (unlink(name) == -1 && errno != ENOENT)
X {
X my_perror("error unlinking old link file", name);
X return FALSE;
X }
X }
X return TRUE;
X}
X
X/* output all the manual pages in a list */
Xvoid output_manual_pages(first, input_files, link_type)
X ManualPage *first;
X int input_files; /* number of different input files */
X enum LinkType link_type; /* how grouped pages will be linked */
X{
X ManualPage *page;
X int tostdout = output_dir && !strcmp(output_dir,"-");
X
X char *filename = NULL;
X
X /* output each page, in turn */
X for (page = first; page; page = page->next)
X {
X char *input_file_base = NULL;
X enum Output_Object output_type =
X page_output_type(page->decl_spec, page->declarator);
X
X /* the manual name is used as the output file extension, and also in
X * the nroff output header.
X */
X const char *section = page_manual_section(output_type);
X
X /* work out the base name of the file this was generated from */
X if (page->sourcefile)
X {
X const char *base = strrchr(firstpage->sourcefile, '/');
X const char *last;
X
X /* use the file name as the manual page title */
X if (base == NULL)
X base = firstpage->sourcefile;
X else
X base++;
X last = strrchr(base, '.');
X if (last == NULL)
X last = base + strlen(base);
X
X input_file_base = alloc_string(base, last);
X }
X
X if (!tostdout)
X {
X safe_free(filename); /* free previous, if any */
X filename = page_file_name(
X use_input_name && input_file_base
X ? input_file_base : page->declarator->name,
X output_type, section);
X fprintf(stderr,"generating: %s\n",filename);
X
X /* a previous run may have left links, so nuke old file first */
X if (!remove_old_file(filename)) exit(1);
X
X if (freopen(filename, "w", stdout) == NULL)
X {
X my_perror("error opening output file", filename);
X free(filename);
X exit(1);
X }
X }
X
X /* do the page itself */
X output_manpage(first, page, input_files,
X group_together && input_file_base ? input_file_base
X : page->declarator->name,
X group_together ? manual_section : section);
X
X safe_free(input_file_base);
X
X /* don't continue if grouped, because all info went into this page */
X if (group_together) break;
X
X if (tostdout && page->next) output->character('\f');
X }
X
X /* close the last output file if there was one */
X if (!tostdout && fclose(stdout) == EOF)
X {
X my_perror("error linking closing file", filename);
X exit(1);
X }
X
X /* if pages are grouped, just link the rest to the first */
X if (group_together && !tostdout && link_type != LINK_NONE)
X {
X for (page=use_input_name && first->sourcefile ? first : first->next;
X page; page = page->next)
X {
X enum Output_Object output_type =
X page_output_type(page->decl_spec, page->declarator);
X const char *extension = page_manual_section(output_type);
X char *linkname = page_file_name(page->declarator->name,
X output_type, extension);
X int result = 0;
X
X /* we may have a function with the same name as the sourcefile */
X if (!strcmp(filename, linkname))
X {
X free(linkname);
X continue;
X }
X
X fprintf(stderr,"%s: %s\n",
X link_type == LINK_REMOVE ? "removing" : "linking", linkname);
X
X /* always nuke old output file, since it may be linked to the one
X * we've just generated, so LINK_FILE may trash it.
X */
X if (!remove_old_file(linkname)) exit(1);
X
X switch(link_type)
X {
X#ifdef HAS_LINK
X case LINK_HARD:
X result = link(filename, linkname);
X break;
X#endif
X#ifdef HAS_SYMLINK
X case LINK_SOFT:
X result = symlink(filename, linkname);
X break;
X#endif
X case LINK_FILE:
X if (freopen(linkname, "w", stdout) == NULL)
X {
X result = -1;
X break;
X }
X output_warning();
X output->include(filename);
X if (fclose(stdout) == EOF)
X result = -1;
X break;
X case LINK_NONE:
X case LINK_REMOVE:
X break;
X }
X
X /* check it went OK */
X if (result == -1)
X {
X my_perror("error linking output file", linkname);
X exit(1);
X }
X free(linkname);
X }
X }
X
X safe_free(filename);
X}
!STUFFY!FUNK!
echo Extracting c2man.h
sed >c2man.h <<'!STUFFY!FUNK!' -e 's/X//'
X/* $Id: c2man.h,v 2.0.1.15 1994/01/07 07:04:30 greyham Exp $
X *
X * Definitions for C language manual page generator
X */
X#ifndef _C2MAN_H
X#define _C2MAN_H
X
X#include "config.h"
X#include "symbol.h"
X
X#ifdef I_SYS_TYPES
X#include <sys/types.h>
X#endif
X
X#ifdef I_STDLIB
X#include <stdlib.h>
X#endif
X
X#ifdef I_STRING
X#include <string.h>
X#else
X#include <strings.h>
X#endif
X
X#include <stdio.h>
X
X#ifdef I_UNISTD
X#include <unistd.h>
X#endif
X
X#ifdef I_STDDEF
X#include <stddef.h>
X#endif
X
X#ifdef I_TIME
X#include <time.h>
X#endif
X#ifdef I_SYS_TIME
X#include <sys/time.h>
X#endif
X
X#ifdef NeXT
X#include <libc.h>
X#undef ECHO /* lex generates ECHO */
X#endif
X
X#ifdef DBMALLOC
X#include </usr/local/debug_include/malloc.h>
X#endif
X
X#include "confmagic.h"
X
X/* number of spaces in a tab */
X#define NUM_TAB_SPACES 4
X
X/* maximum include file nesting */
X#define MAX_INC_DEPTH 15
X
X/* maximum number of include directories */
X#define MAX_INC_DIR 15
X
X/* maximum number of characters in a text buffer */
X#define MAX_TEXT_LENGTH 256
X
X/* Boolean type */
Xtypedef int boolean;
X#ifndef TRUE
X#define FALSE 0
X#define TRUE 1
X#endif
X
X/* NULL char *, useful for passing NULL to functions wanting a char * */
X#define NULLCP ((char *)0)
X
X/* This is a list of function parameters. */
Xtypedef struct _parameter_list {
X struct _parameter *first; /* pointer to first parameter in list */
X struct _parameter *last; /* pointer to last parameter in list */
X} ParameterList;
X
X/* Declaration specifier flags */
X#define DS_NONE 0 /* default */
X#define DS_EXTERN 1 /* contains "extern" specifier */
X#define DS_STATIC 2 /* contains "static" specifier */
X#define DS_CHAR 4 /* contains "char" type specifier */
X#define DS_SHORT 8 /* contains "short" type specifier */
X#define DS_FLOAT 16 /* contains "float" type specifier */
X#define DS_JUNK 32 /* we're not interested in this declaration */
X#define DS_INLINE 64 /* makes static look interesting */
X
X/* This structure stores information about a declaration specifier. */
Xtypedef struct _decl_spec {
X unsigned short flags; /* flags defined above */
X char *text; /* source text */
X struct _enumerator_list *enum_list; /* associated enum (if any) */
X} DeclSpec;
X
X/* Styles of declaration/definition */
Xtypedef enum {
X DECL_SIMPLE, /* simple declaration */
X DECL_COMPOUND, /* compound declaration */
X DECL_FUNCTION, /* function declaration (prototype) */
X DECL_FUNCDEF /* an actual function definition */
X} DeclType;
X
X/* This structure stores information about a declarator. */
Xtypedef struct _declarator {
X char *name; /* name of variable or function */
X char *text; /* source text */
X DeclType type; /* style of function declaration */
X ParameterList params; /* function parameters */
X char *comment; /* description of param or variable */
X struct _declarator *head; /* head function declarator */
X struct _declarator *func_stack; /* stack of function declarators */
X struct _declarator *next; /* next declarator in list */
X} Declarator;
X
X/* This is a list of declarators. */
Xtypedef struct _declarator_list {
X Declarator *first; /* pointer to first declarator in list */
X Declarator *last; /* pointer to last declarator in list */
X} DeclaratorList;
X
X/* This structure stores information about a declaration. */
Xtypedef struct _declaration {
X DeclSpec decl_spec;
X DeclaratorList decl_list;
X} Declaration;
X
X/* this structure store information about an enumerator */
Xtypedef struct _enumerator {
X char *name; /* name of enum entry */
X char *comment; /* description of entry */
X char *group_comment; /* general descr. for next few enums in list */
X struct _enumerator *next; /* next enumerator in list */
X} Enumerator;
X
X/* This is a list of enumerators. */
Xtypedef struct _enumerator_list {
X Enumerator *first; /* pointer to first enumerator in list */
X Enumerator *last; /* pointer to last enumerator in list */
X struct _enumerator_list *next; /* next list in a list-of-lists */
X} EnumeratorList;
X
X
X/* This structure stores information about a function parameter. */
Xtypedef struct _parameter {
X DeclSpec decl_spec;
X Declarator *declarator;
X boolean suppress; /* don't print in grouped page */
X boolean duplicate; /* mention fn in grouped page */
X struct _parameter *next; /* next parameter in list */
X} Parameter;
X
X/* this is an identifier, with surrounding comments (if any) */
Xtypedef struct _identifier {
X char *name;
X char *comment_before, *comment_after;
X} Identifier;
X
X/* parser stack entry type */
Xtypedef union {
X char *text;
X DeclSpec decl_spec;
X Parameter parameter;
X ParameterList param_list;
X Declarator *declarator;
X DeclaratorList decl_list;
X Declaration declaration;
X Enumerator enumerator;
X EnumeratorList *enum_list;
X Identifier identifier;
X boolean boolean;
X} yystype;
X
X/* include files specified by user */
Xtypedef struct _includefile
X{
X char *name;
X struct _includefile *next;
X} IncludeFile;
X
X/* output object types */
Xenum Output_Object
X{
X#if 0 /* C++ stuff */
X OBJECT_CLASS,
X OBJECT_STRUCT,
X OBJECT_ENUM,
X OBJECT_TYPEDEF,
X#endif
X OBJECT_FUNCTION,
X OBJECT_VARIABLE,
X OBJECT_STATIC_FUNCTION,
X OBJECT_STATIC_VARIABLE,
X _OBJECT_NUM
X};
X
Xstruct Output_Object_Info
X{
X char flag; /* -O flag used to set it */
X char *name; /* descriptive name for usage() */
X char *extension; /* file extension */
X char *subdir; /* subdirectory */
X};
X
X#define YYSTYPE yystype
X
X/* Program options */
Xextern boolean static_out;
Xextern boolean variables_out;
Xextern boolean promote_param;
Xextern const char *decl_spec_prefix, *declarator_prefix, *declarator_suffix;
Xextern const char *first_param_prefix, *middle_param_prefix, *last_param_suffix;
Xextern int num_inc_dir;
Xextern const char *inc_dir[];
Xextern char *manual_name;
Xextern const char *progname;
Xextern char *header_prefix;
Xextern IncludeFile *first_include;
X
Xextern char *group_terse;
Xextern boolean group_together;
Xextern boolean terse_specified;
Xextern boolean always_document_params;
X
Xextern char *output_dir;
X
X/* Global declarations */
Xextern int line_num;
Xextern const char *basefile;
Xextern Time_t basetime;
Xextern boolean inbasefile;
Xextern boolean header_file;
Xextern SymbolTable *typedef_names;
Xextern void output_error();
Xextern void parse_file _((const char *start_file));
Xextern int errors;
Xextern const char *manual_section;
Xextern boolean use_input_name;
Xextern boolean make_embeddable;
Xextern struct Output_Object_Info output_object[_OBJECT_NUM];
X
X/* Output a string to standard output. */
X#define put_string(s) fputs(s, stdout)
X
X/* a malloc that handles errors, and a free that handles NULL */
X#ifndef DBMALLOC
Xvoid *safe_malloc _((size_t size));
X#else
X/* use macro so dbmalloc tells us where safe_malloc is called from */
X#define safe_malloc(s) malloc(s)
X#endif
X#define safe_free(p) do { if (p) free(p); } while(0)
X
Xvoid outmem();
Xvoid print_includes _((FILE *f));/* write #include lines */
X
Xvoid yyerror _V((const char *fmt, ...));
X
Xchar *strduplicate _((const char *s));
Xint strncmpi _((const char *s1, const char *s2, size_t n));
Xchar *strtoupper _((char *s));
X
Xvoid my_perror _((const char *action, const char *filename));
X
Xchar *alloc_string _((const char *start, const char *end));
X
X#endif
!STUFFY!FUNK!
echo Extracting eg/namedash.h
sed >eg/namedash.h <<'!STUFFY!FUNK!' -e 's/X//'
X/*
X * NAME
X * namedash - name section with dash.
X *
X * DESCRIPTION
X * This function goes way over the top and includes a NAME section that
X * has a dash in it.
X */
Xint namedash();
!STUFFY!FUNK!
echo "End of kit 1 (of 9)"
echo " "
cat /dev/null >kit1isdone
run=''
config=''
for iskit in 1 2 3 4 5 6 7 8 9; do
if test -f kit${iskit}isdone; then
run="$run $iskit"
else
todo="$todo $iskit"
fi
done
case $todo in
'')
echo "You have run all your kits."
if test -f PACKNOTES; then
sh PACKNOTES
else
echo "You have to rebuild split files by hand (see PACKLIST)."
fi
echo "Please read README and then type Configure."
chmod 755 Configure
rm -f kit*isdone
;;
*) echo "You have run$run."
echo "You still need to run$todo."
;;
esac
: Someone might mail this, so exit before signature...
exit 0
--
Graham Stoney, Hardware/Software Engineer
Canon Information Systems Research Australia
Ph: + 61 2 805 2909 Fax: + 61 2 805 2929
exit 0 # Just in case...