🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: alt.sources.patches
1 messages
1 total messages Started by Graham Stoney Mon, 17 Aug 1992 07:51
c2man: generate man pages from C comments; patch v1.3 -> v1.4
#7
Author: Graham Stoney
Date: Mon, 17 Aug 1992 07:51
2221 lines
60282 bytes
Archive-name: c2man/patch.1.3-1.4
Submitted-by: greyham@research.canon.oz.au

This official patch brings c2man v1.3 up to v1.4.

You should remove the file:
	example.sed
After applying this patch.

This patch fixes a few bugs, and adds a -G option for grouping functions from
the same file together in the one manual page, ala ctype(3).

See the CHANGES file for more details.

As before, any feedback, comments or tips are greatly appreciated.

Graham

*** 1.4	1992/07/28 06:34:30
--- CHANGES	1992/08/17 06:45:26
***************
*** 39,41 ****
--- 39,62 ----
  	enum tag/description spacing in output varies automatically.
  
  	Sprinkle output heavily with Bold for troff.
+ 
+ Version 1.4:
+ 	Recognise that an end-of-line comment after a function definition or
+ 	declaration applies to the last parameter of a function, so we handle:
+ 
+ 	    /* try to do something that requires 2 parameters */
+ 	    int try(int a,	/* description of first parameter */
+ 		    int b);	/* description of second parameter */
+ 
+ 	Fix comment lexing problem that sometimes rejected the '# ...' lines
+ 	that cpp generates on the line immediately after a comment.
+ 
+ 	Don't capitalise n/troff dot commands.
+ 
+ 	make -V option act verbosely.
+ 
+ 	By popular demand, added -G option to group manual pages from the
+ 	same file together, ala ctype(3).
+ 
+ 	Output function prototype on a single line for all functions with one
+ 	or less parameters (ignoring -f...).
*** 1.5	1992/07/28 06:34:30
--- Makefile	1992/08/17 06:45:27
***************
*** 1,4 ****
! # $Id: Makefile,v 1.5 1992/07/28 06:34:30 greyham Exp $
  #
  # UNIX makefile for manual page generator
  #
--- 1,4 ----
! # $Id: Makefile,v 1.8 1992/08/17 06:21:01 greyham Exp $
  #
  # UNIX makefile for manual page generator
  #
***************
*** 44,50 ****
  #WARN=-Wall
  
  # Pick whatever other compiler defines are needed.
! #DEFINES= -g
  # -DYYDEBUG
  # -DDEBUG
  
--- 44,50 ----
  #WARN=-Wall
  
  # Pick whatever other compiler defines are needed.
! #DEFINES=-g
  # -DYYDEBUG
  # -DDEBUG
  
***************
*** 59,66 ****
  #LIBS=-lMallocDebug -lsys_s
  #DEFINES=-g -DMALLOC_DEBUG
  
! DIST1 =		README CHANGES c2man.1 Makefile example.h example.sed \
! 		flatten.sed $(OSOURCES)
  DIST2 =		$(DCSOURCES)
  OSOURCES =	lex.l grammar.y \
  		config.h c2man.h semantic.h symbol.h strconcat.h \
--- 59,66 ----
  #LIBS=-lMallocDebug -lsys_s
  #DEFINES=-g -DMALLOC_DEBUG
  
! DIST1 =		README CHANGES INSTALL c2man.1 Makefile example.h \
! 		flatten.sed ctype.h ctype.terse $(OSOURCES)
  DIST2 =		$(DCSOURCES)
  OSOURCES =	lex.l grammar.y \
  		config.h c2man.h semantic.h symbol.h strconcat.h \
***************
*** 73,79 ****
  OBJECTS =	c2man.o semantic.o string.o symbol.o y.tab.o strconcat.o \
  		manpage.o enum.o
  GENERATED =	c2man c2man.man example.inc y.tab.c lex.yy.c \
! 		c2man.sh1 c2man.sh2 dowork.3 y.output
  
  # be lenient on y.tab.c, but harsh on the rest
  NOWARN=$(BSD) $(DEFINES)
--- 73,79 ----
  OBJECTS =	c2man.o semantic.o string.o symbol.o y.tab.o strconcat.o \
  		manpage.o enum.o
  GENERATED =	c2man c2man.man example.inc y.tab.c lex.yy.c \
! 		c2man.sh1 c2man.sh2 dowork.3 ctype.out ctype.inc y.output
  
  # be lenient on y.tab.c, but harsh on the rest
  NOWARN=$(BSD) $(DEFINES)
***************
*** 91,97 ****
  	$(CC) $(CFLAGS) -o $@ $(OBJECTS) $(LIBS)
  
  y.tab.c: grammar.y
! 	@echo Expect 28 shift/reduce conflicts.
  	$(YACC) grammar.y
  
  # don't compile y.tab.c with all warnings; yacc/bison are not up to it.
--- 91,97 ----
  	$(CC) $(CFLAGS) -o $@ $(OBJECTS) $(LIBS)
  
  y.tab.c: grammar.y
! 	@echo Expect 29 shift/reduce conflicts.
  	$(YACC) grammar.y
  
  # don't compile y.tab.c with all warnings; yacc/bison are not up to it.
***************
*** 101,113 ****
  lex.yy.c: lex.l
  	$(LEX) lex.l
  
! c2man.man: c2man.1 example.inc
  	nroff -man c2man.1 >$@
  
  example.inc: c2man example.h
  	./c2man example.h
  	(echo ".ll -1i"; cat dowork.3) | nroff -man | cat -s | \
! 	    sed -f example.sed > example.inc
  	
  TAGS: $(SOURCES)
  	etags -t $(SOURCES)
--- 101,118 ----
  lex.yy.c: lex.l
  	$(LEX) lex.l
  
! c2man.man: c2man.1 example.inc ctype.inc
  	nroff -man c2man.1 >$@
  
  example.inc: c2man example.h
  	./c2man example.h
  	(echo ".ll -1i"; cat dowork.3) | nroff -man | cat -s | \
! 	    sed -e '2s/           /      /g' > example.inc
! 
! ctype.inc: c2man ctype.h ctype.terse
! 	./c2man -o- -G"`cat ctype.terse`" ctype.h > ctype.out
! 	(echo ".ll -1i"; cat ctype.out) | nroff -man | cat -s | \
! 	    sed -e '2s/           /      /g' > ctype.inc
  	
  TAGS: $(SOURCES)
  	etags -t $(SOURCES)
***************
*** 126,132 ****
  	shar $(DIST2) >c2man.sh2
  
  ci:
! 	ci $(DIST1) $(DIST2)
  
  depend:
  	makedepend $(CSOURCES)
--- 131,138 ----
  	shar $(DIST2) >c2man.sh2
  
  ci:
! 	-ci $(DIST1) $(DIST2)
! 	co $(DIST1) $(DIST2)
  
  depend:
  	makedepend $(CSOURCES)
*** 1.4	1992/07/28 06:34:30
--- README	1992/08/17 06:45:26
***************
*** 1,4 ****
! This is c2man (Version 1.3), a program for generating Unix style manual pages
  in nroff/troff -man format directly from ordinary comments embedded in C source
  code. It owes a huge debt to the public domain program cproto, by Chin Huang,
  from which much of the code is derived.
--- 1,4 ----
! This is c2man (Version 1.4), a program for generating Unix style manual pages
  in nroff/troff -man format directly from ordinary comments embedded in C source
  code. It owes a huge debt to the public domain program cproto, by Chin Huang,
  from which much of the code is derived.
***************
*** 27,64 ****
  using it, the more time I am likely to spend on bug fixes and enhancements;
  plus I can let you know about new releases if you wish. Comments and bug
  reports (especially those with patches :-) are welcome, but make sure you
! include the c2man version number (go: c2man -V).
  
  If anyone wants to volunteer to write c++2man, please drop me a line! Such a
  tool would be incredibly useful, and shouldn't be too hard to add to c2man.
  
  Graham Stoney
  greyham@research.canon.oz.au
- 
- 
- INSTALLATION:
- 1. You will need a C compiler that can handle ANSI constructs (prototypes,
-    string concatenation, free(NULL), etc), or an ANSI to K&R converter.
-    Don't put the compiler in strict ANSI mode though, since c2man uses a few
-    UNIX-isms that may disappear from your header files if you do that
-    (eg: popen). Don't use -ansi on gcc for example.
- 
-    Don't use -Wall with gcc unless your system header files have basic stuff
-    like fprintf in stdio.h (SunOS Release 4.1.2 for example!).
- 
-    Sun yacc outputs incorrect prototypes for malloc and free (says they return
-    char * instead of void *), which gcc 2.2 or later errors on; use bison
-    instead, or just delete the incorrect prototypes on the first line of
-    y.tab.c when the make errors.
- 
-    If using gcc on a Sun, you may need GNU libc to get stdarg.h
- 
- 2. Look at the Makefile, and check that CC, DEFINES, WARN, LIBS and BSD are
-    set if necessary. Optionally, set LEX and YACC too.
- 
- 3. Type "make"
- 
- 4. Look at the manual page. The example in it is automatically generated, so
-    check that it looks OK.
- 
- 5. As root, do "make install"
--- 27,38 ----
  using it, the more time I am likely to spend on bug fixes and enhancements;
  plus I can let you know about new releases if you wish. Comments and bug
  reports (especially those with patches :-) are welcome, but make sure you
! include the c2man version number (go: c2man -V /dev/null).
  
  If anyone wants to volunteer to write c++2man, please drop me a line! Such a
  tool would be incredibly useful, and shouldn't be too hard to add to c2man.
  
+ See the file INSTALL for installation instructions.
+ 
  Graham Stoney
  greyham@research.canon.oz.au
*** 1.5	1992/07/28 06:34:30
--- c2man.1	1992/08/17 06:45:26
***************
*** 1,4 ****
! .\" $Id: c2man.1,v 1.5 1992/07/28 06:34:30 greyham Exp $
  .de EX          \"Begin example
  .ne 5
  .if n .sp 1
--- 1,4 ----
! .\" $Id: c2man.1,v 1.8 1992/08/17 06:44:52 greyham Exp $
  .de EX          \"Begin example
  .ne 5
  .if n .sp 1
***************
*** 12,17 ****
--- 12,50 ----
  .if n .sp 1
  .if t .sp .5
  ..
+ .de CS		\"Begin shell command
+ .br
+ .if n .sp 1
+ .if t .sp .5
+ .in +.5i
+ %
+ .ft B
+ ..
+ .de CE		\"End shell command
+ .br
+ .ft R
+ .in -.5i
+ .if n .sp 1
+ .if t .sp .5
+ ..
+ .de CD		\"Shell command
+ .CS
+ \\$1 \\$2 \\$3 \\$4 \\$5 \\$6
+ .CE
+ ..
+ .\"	The IF & IE macros cannot be combined due to flatten.sed
+ .de IF		\"Begin Include a file
+ .if n .sp 1
+ .if t .sp .5
+ .nf
+ .RS 0.25in
+ ..
+ .de IE		\"End Include a file
+ .RE
+ .if n .sp 1
+ .if t .sp .5
+ .fi
+ ..
  .TH C2MAN 1 "April 21, 1992"
  .BY "CISRA"
  .SH NAME
***************
*** 38,64 ****
  This is potentially a huge win for most programmers that just love documenting
  their functions, and updating the documentation every time it changes.
  Here's an example, named example.h:
! .sp
! .nf
! .RS 0.25in
  .so example.h
! .RE
! .fi
  .LP
! When
! .B "c2man example.h"
  is run,
  this produces a file named
  .B dowork.3
! which can be used as
! .B "nroff -man dowork.3"
  to produce:
! .sp
! .nf
! .RS 0.25in
  .so example.inc
! .RE
! .fi
  .LP
  The manual page starts with a
  .B NAME
--- 71,91 ----
  This is potentially a huge win for most programmers that just love documenting
  their functions, and updating the documentation every time it changes.
  Here's an example, named example.h:
! .IF
  .so example.h
! .IE
  .LP
! When:
! .CD c2man example.h
  is run,
  this produces a file named
  .B dowork.3
! which can be processed by man(1) or used as:
! .CD "nroff -man dowork.3"
  to produce:
! .IF
  .so example.inc
! .IE
  .LP
  The manual page starts with a
  .B NAME
***************
*** 83,92 ****
  separates that parameter from the next, if the comment is the only remaining
  thing on the line.
  .LP
  If a parameter is an
  .B enumerated
  .BR type ,
! All the possible values it can take are output, along with their descriptions.
  These descriptions are gleaned from the comments surrounding the
  .B enum
  identifiers where the type was defined.
--- 110,126 ----
  separates that parameter from the next, if the comment is the only remaining
  thing on the line.
  .LP
+ If the manual page is for a group of functions (ie:
+ .B \-G
+ option),
+ identical parameters (in both name and type) common to more than one function
+ are described only once if only one has a comment (as in the ctype
+ example below).
+ .LP
  If a parameter is an
  .B enumerated
  .BR type ,
! all the possible values it can take are output, along with their descriptions.
  These descriptions are gleaned from the comments surrounding the
  .B enum
  identifiers where the type was defined.
***************
*** 96,102 ****
  parameters.
  .B enum
  identifiers that begin with an underscore are ignored, which is useful for
! padding or NUMBER_OF_... values which aren't normally used by someone calling
  the function.
  If none of the identifiers in an enumerated type has a comment,
  .B c2man
--- 130,138 ----
  parameters.
  .B enum
  identifiers that begin with an underscore are ignored, which is useful for
! padding or
! .I NUMBER_OF_...
! values which aren't normally used by someone calling
  the function.
  If none of the identifiers in an enumerated type has a comment,
  .B c2man
***************
*** 108,114 ****
  after the first line or sentence
  of the comment immediately before the declaration/definition,
  up until the word
! .RB `` return[s] ''
  at the start of a line, matched case-insensitively and optionally followed by
  some non-alphanumerics.
  .LP
--- 144,150 ----
  after the first line or sentence
  of the comment immediately before the declaration/definition,
  up until the word
! .RB `` return [ s ] ''
  at the start of a line, matched case-insensitively and optionally followed by
  some non-alphanumerics.
  .LP
***************
*** 141,147 ****
  When looking at comments,
  .B c2man
  strips the comment delimiters,
! ignores both leading whitespace and leading asterisks on a
  line, and ignores all surrounding lines thus rendered blank.
  This makes it very flexible in supporting popular comment boxing.
  .LP
--- 177,183 ----
  When looking at comments,
  .B c2man
  strips the comment delimiters,
! ignores both leading white-space and leading asterisks on a
  line, and ignores all surrounding lines thus rendered blank.
  This makes it very flexible in supporting popular comment boxing.
  .LP
***************
*** 196,205 ****
  But the following example is
  .B NOT
  equivalent because the commas are between the identifier and the its
! associated comment, and the comment is on a different line. It will generate an
  error because
  .B COUNTRY
! has comments before and after which apply to it.
  .EX
  enum Place
  {
--- 232,242 ----
  But the following example is
  .B NOT
  equivalent because the commas are between the identifier and the its
! associated comment, and the comment is on a different line.
! Each comment actually applies to the wrong identifier, and it will generate an
  error because
  .B COUNTRY
! has comments both before and after which apply to it.
  .EX
  enum Place
  {
***************
*** 221,226 ****
--- 258,281 ----
  will usually know which object a comment is associated with, unless you do
  something truly bizarre.
  .LP
+ Simple, closely related objects can be grouped together onto a single page with the
+ .B \-G
+ option; for example, if ctype.h contains:
+ .IF
+ .so ctype.h
+ .IE
+ .LP
+ then using:
+ .CS
+ c2man -G"\c
+ .so ctype.terse
+ " ctype.h
+ .CE
+ yields:
+ .IF
+ .so ctype.inc
+ .IE
+ .LP
  Since
  .B c2man
  will accept either function definitions or prototypes, it can be used on
***************
*** 281,286 ****
--- 336,348 ----
  .BI \-M name
  Set the name of the manual in which the page will go.
  .TP
+ .BI \-G terse
+ Group all info generated from the same file together into a single
+ page (ala ctype(3)) with the specified terse description in the
+ .B NAME
+ section. A link to
+ the output file is made for each object documented.
+ .TP
  .BI \-i dirprefix
  Use
  .B dirprefix
***************
*** 291,302 ****
  section.
  .TP
  .BI \-F string
! Set the format used to output the prototype in each manual page.
  The string is a template in the form
  .EX
  " int main ( a, b )"
  .EE
! where each space in the string may be replaced with whitespace characters.
  For example, the option
  .EX
  -F"int main(\\n\\ta,\\n\\tb\\n\\t)"
--- 353,367 ----
  section.
  .TP
  .BI \-F string
! Set the format used to output the prototype for functions with more than 1
! parameter
! in each manual page;
! functions with zero or 1 parameters are always output as one line.
  The string is a template in the form
  .EX
  " int main ( a, b )"
  .EE
! where each space in the string may be replaced with white-space characters.
  For example, the option
  .EX
  -F"int main(\\n\\ta,\\n\\tb\\n\\t)"
***************
*** 332,338 ****
  .I #include.
  .TP
  .B \-V
! Print version information.
  .SH "SEE ALSO"
  man(1),
  apropos(1),
--- 397,403 ----
  .I #include.
  .TP
  .B \-V
! Print version information and cpp parameters.
  .SH "SEE ALSO"
  man(1),
  apropos(1),
*** 1.3	1992/07/28 06:34:30
--- c2man.c	1992/08/17 06:45:30
***************
*** 1,10 ****
! /* $Id: c2man.c,v 1.3 1992/07/28 06:34:30 greyham Exp $
   *
   * C Manual page.
   * Reads C source code and outputs manual pages.
   */
  #ifndef lint
! static char *rcsid = "$Id: c2man.c,v 1.3 1992/07/28 06:34:30 greyham Exp $";
  #endif
  #include <stdio.h>
  #include <ctype.h>
--- 1,10 ----
! /* $Id: c2man.c,v 1.4 1992/08/17 06:18:38 greyham Exp $
   *
   * C Manual page.
   * Reads C source code and outputs manual pages.
   */
  #ifndef lint
! static char *rcsid = "$Id: c2man.c,v 1.4 1992/08/17 06:18:38 greyham Exp $";
  #endif
  #include <stdio.h>
  #include <ctype.h>
***************
*** 69,74 ****
--- 69,77 ----
  /* prefix for generated #include lines */
  char *include_prefix = NULL;
  
+ /* do we group related stuff into one file? if so, its terse description? */
+ char *group_terse = NULL;
+ 
  /* Include file directories */
  #ifdef MSDOS
  int num_inc_dir = 1;
***************
*** 81,87 ****
--- 84,94 ----
  /* total number of errors encountered */
  int errors;
  
+ #ifndef NO_CPP
  char *cpp_cmd;
+ #endif
+ 
+ boolean verbose = FALSE;
  
  /* Output an error message along with the current line number in the
   * source file.
***************
*** 143,148 ****
--- 150,156 ----
      fputs("  -m nam  set name of macro guarding prototypes\n", stderr);
      fputs("  -d      omit prototype macro definition\n", stderr);
      fputs("  -i prefix specify prefix for #include in SYNOPSIS\n", stderr);
+     fputs("  -G terse Group info from each file into a single page\n", stderr);
      fputs("  -M name set name of the manual in which the page goes\n", stderr);
      fputs("  -D name[=value]\n", stderr);
      fputs("  -U name\n", stderr);
***************
*** 150,164 ****
      fputs("          C preprocessor options\n", stderr);
      fputs("  -F fmt  set prototype template in the form \"int main (a, b)\"\n",
  	stderr);
!     fputs("  -V      print version information\n", stderr);
      exit(1);
  }
  
  int process_file(char *name)
  {
!     char *cmd = name ? strconcat(cpp_cmd, " ", name, NULL) : cpp_cmd;
!     
  #ifdef NO_CPP
      if (name && freopen(name, "r", yyin) == NULL)
      {
  	fprintf(stderr, "%s: cannot open file %s\n", progname, argv[i]);
--- 158,176 ----
      fputs("          C preprocessor options\n", stderr);
      fputs("  -F fmt  set prototype template in the form \"int main (a, b)\"\n",
  	stderr);
!     fputs("  -V      be verbose and print version information\n", stderr);
      exit(1);
  }
  
  int process_file(char *name)
  {
! #ifndef NO_CPP
!     char *cmd;
! #endif
!     strcpy(cur_file, name ? name : "stdin");
  #ifdef NO_CPP
+     if (verbose)	fprintf(stderr,"reading %s\n", cur_file);
+ 
      if (name && freopen(name, "r", yyin) == NULL)
      {
  	fprintf(stderr, "%s: cannot open file %s\n", progname, argv[i]);
***************
*** 165,176 ****
  	return 0;
      }
  #else
      if ((yyin = popen(cmd, "r")) == NULL) {
  	fprintf(stderr, "%s: error running %s\n", progname, cmd);
  	return 0;
      }
  #endif
-     strcpy(cur_file, name ? name : "stdin");
  
      parse_file();
  #ifdef NO_CPP
--- 177,191 ----
  	return 0;
      }
  #else
+     cmd = name ? strconcat(cpp_cmd, " ", name, NULL) : cpp_cmd;
+ 
+     if (verbose)	fprintf(stderr,"%s\n", cmd);
+ 
      if ((yyin = popen(cmd, "r")) == NULL) {
  	fprintf(stderr, "%s: error running %s\n", progname, cmd);
  	return 0;
      }
  #endif
  
      parse_file();
  #ifdef NO_CPP
***************
*** 196,202 ****
      cpp_cmd = strconcat(CPP, NULL);
      
      /* Scan command line options. */
!     while ((c = getopt(argc, argv, "D:F:I:psU:Vvo:M:i:")) != EOF) {
  	switch (c) {
  	case 'I':
  	case 'D':
--- 211,217 ----
      cpp_cmd = strconcat(CPP, NULL);
      
      /* Scan command line options. */
!     while ((c = getopt(argc, argv, "D:F:I:psU:Vvo:M:i:G:")) != EOF) {
  	switch (c) {
  	case 'I':
  	case 'D':
***************
*** 206,211 ****
--- 221,229 ----
  	    free(cpp_cmd);
  	    cpp_cmd = new_cmd;
  	    break;
+ 	case 'G':
+ 	    group_terse = optarg;
+ 	    break;
  	case 'F':
  	    s = escape_string(optarg);
  
***************
*** 254,259 ****
--- 272,278 ----
  	    static_out = TRUE;
  	    break;
  	case 'V':
+ 	    verbose = TRUE;
  	    fprintf(stderr, "%s\n", rcsid);
  	    break;
  	case 'v':
*** 1.2	1992/07/17 06:41:33
--- c2man.h	1992/08/17 06:45:29
***************
*** 1,4 ****
! /* $Id: c2man.h,v 1.2 1992/07/17 06:41:33 greyham Exp $
   *
   * Definitions for C language manual page generator
   */
--- 1,4 ----
! /* $Id: c2man.h,v 1.3 1992/08/17 06:18:38 greyham Exp $
   *
   * Definitions for C language manual page generator
   */
***************
*** 71,76 ****
--- 71,78 ----
  typedef struct _parameter {
      DeclSpec		decl_spec;
      Declarator		declarator;
+     boolean		suppress:1;	/* don't print in grouped page */
+     boolean		duplicate:1;	/* mention fn in grouped page */
      struct _parameter	*next;		/* next parameter in list */
  } Parameter;
  
***************
*** 100,105 ****
--- 102,109 ----
  extern char *manual_name;
  extern char *progname;
  extern char *include_prefix;
+ extern char *group_terse;
+ extern char *output_dir;
  
  /* Global declarations */
  extern int line_num;
*** 1.1	1992/07/07 09:24:32
--- flatten.sed	1992/08/17 06:45:27
***************
*** 7,9 ****
--- 7,24 ----
  r example.inc
  d
  }
+ 
+ /^.so *ctype.h$/{
+ r ctype.h
+ d
+ }
+ 
+ /^.so *ctype.inc$/{
+ r ctype.inc
+ d
+ }
+ 
+ /^.so *ctype.terse$/{
+ r ctype.terse
+ d
+ }
*** 1.2	1992/07/17 06:41:33
--- grammar.y	1992/08/17 06:45:28
***************
*** 1,4 ****
! /* $Id: grammar.y,v 1.2 1992/07/17 06:41:33 greyham Exp $
   *
   * yacc grammar for C prototype generator
   * This was derived from the grammar given in Appendix A of
--- 1,4 ----
! /* $Id: grammar.y,v 1.3 1992/08/12 00:35:35 greyham Exp $
   *
   * yacc grammar for C prototype generator
   * This was derived from the grammar given in Appendix A of
***************
*** 119,125 ****
  	;
  
  function_definition
! 	: opt_comment declaration_specifiers declarator
  	{
  	    if ($3.func_def == FUNC_NONE) {
  		yyerror("syntax errorA");
--- 119,125 ----
  	;
  
  function_definition
! 	: opt_comment declaration_specifiers declarator opt_eolcomment
  	{
  	    if ($3.func_def == FUNC_NONE) {
  		yyerror("syntax errorA");
***************
*** 127,132 ****
--- 127,134 ----
  	    }
  	    cur_params = &($3.params);
  	    func_params = TRUE;
+             if ($4 && !comment_last_parameter(&$3.params, $4))
+                 YYERROR;
  	}
  	  opt_declaration_list T_BRACES
  	{
***************
*** 133,139 ****
  	    func_params = FALSE;
  	    new_manual_page($1,&$2, &$3);
  	}
! 	| opt_comment declarator
  	{
  	    if ($2.func_def == FUNC_NONE) {
  		yyerror("syntax errorB");
--- 135,141 ----
  	    func_params = FALSE;
  	    new_manual_page($1,&$2, &$3);
  	}
! 	| opt_comment declarator opt_eolcomment
  	{
  	    if ($2.func_def == FUNC_NONE) {
  		yyerror("syntax errorB");
***************
*** 141,146 ****
--- 143,150 ----
  	    }
  	    cur_params = &($2.params);
  	    func_params = TRUE;
+             if ($3 && !comment_last_parameter(&$2.params, $3))
+                 YYERROR;
  	}
  	  opt_declaration_list T_BRACES
  	{
*** 1.5	1992/07/28 06:34:30
--- lex.l	1992/08/17 06:45:28
***************
*** 1,5 ****
  %{
! /* $Id: lex.l,v 1.5 1992/07/28 06:34:30 greyham Exp $
   *
   * C manual page generator
   * Lexical analyzer specification
--- 1,5 ----
  %{
! /* $Id: lex.l,v 1.7 1992/08/17 06:18:38 greyham Exp $
   *
   * C manual page generator
   * Lexical analyzer specification
***************
*** 42,47 ****
--- 42,48 ----
  static void do_include();
  static void new_dynbuf(void);
  static void add_dynbuf(char c);
+ static char *return_dynbuf(void);
  static int get_comment(boolean ateol, boolean cpp);
  static void get_cpp_directive();
  
***************
*** 271,281 ****
  	switch (state)
  	{
  	case FIRSLASH:
! 	    if (c != '/' && !isspace(c)) { unput(c); goto leave; }
  	    state = SECSLASH;
  	    continue;
  	case SECSLASH:
! 	    if (c != '/') { unput(c); unput('/'); goto leave; }
  	    state = laststate;
  	    continue;
  	}
--- 272,283 ----
  	switch (state)
  	{
  	case FIRSLASH:
! 	    if (c != '/' && !isspace(c)) { unput(c); c = '\n'; goto leave; }
! 	    line_num++;		/* we have handled the \n ok, not lex */
  	    state = SECSLASH;
  	    continue;
  	case SECSLASH:
! 	    if (c != '/') { unput(c); c = '/'; goto leave; }
  	    state = laststate;
  	    continue;
  	}
***************
*** 283,289 ****
  	switch (c)
  	{
  	case '\n':
- 	    line_num++;
  	    if (cpp && ateol)	goto leave;
  
  	    switch (state)
--- 285,290 ----
***************
*** 300,306 ****
  	    default:	break;	/* don't add it in START */
  	    }
  
! 	    if (cpp) { laststate = state; state = FIRSLASH; }
  	    break;
  
  	case '*':
--- 301,308 ----
  	    default:	break;	/* don't add it in START */
  	    }
  
! 	    if (cpp) { laststate = state; state = FIRSLASH; break; }
! 	    line_num++;	/* only if we handled it (rather than lex) */
  	    break;
  
  	case '*':
***************
*** 308,314 ****
  	    {
  	    case ASTER:	if (laststate == TEXT && remember) add_dynbuf('*');
  	    		break;
! 	    case END:	unput(c); ateol = 0; goto leave;
  	    default:	laststate = state; state = ASTER;
  	    }
  	    break;
--- 310,316 ----
  	    {
  	    case ASTER:	if (laststate == TEXT && remember) add_dynbuf('*');
  	    		break;
! 	    case END:	ateol = 0; goto leave;
  	    default:	laststate = state; state = ASTER;
  	    }
  	    break;
***************
*** 316,325 ****
  	case '/':
  	    switch (state)
  	    {
! 	    case ASTER: if (!ateol)	goto leave;
  			state = END; continue;
  	    case TEXT:	break;
! 	    case END:	unput(c); ateol = 0; goto leave;
  	    default:	continue;
  	    }
  	/* FALLTHROUGH if state == TEXT */
--- 318,327 ----
  	case '/':
  	    switch (state)
  	    {
! 	    case ASTER: if (!ateol)	{ c=0; goto leave; } /* dont unput */
  			state = END; continue;
  	    case TEXT:	break;
! 	    case END:	ateol = 0; goto leave;
  	    default:	continue;
  	    }
  	/* FALLTHROUGH if state == TEXT */
***************
*** 348,354 ****
  		    if (remember) { add_dynbuf(c); end = dynbuf_current; }
  		}
  		break;
! 	    case END:	unput(c); ateol = 0; goto leave;
  	    }
  	    break;
  	}
--- 350,356 ----
  		    if (remember) { add_dynbuf(c); end = dynbuf_current; }
  		}
  		break;
! 	    case END:	ateol = 0; goto leave;
  	    }
  	    break;
  	}
***************
*** 355,360 ****
--- 357,364 ----
      }			
  
  leave:
+     if (c)	unput(c)	/* let lex see the terminating character */
+ 
      if (remember)
      {
  	if (end == 0)
***************
*** 361,368 ****
  	    free(dynbuf);
  	else
  	{
! 	    dynbuf[end] = '\0';
! 	    yylval.text = dynbuf;
  #ifdef DEBUG
  	    fprintf(stderr,"ateol %d, comment '%s'\n",ateol,dynbuf);
  #endif
--- 365,372 ----
  	    free(dynbuf);
  	else
  	{
! 	    dynbuf_current = end;
! 	    yylval.text = return_dynbuf();
  #ifdef DEBUG
  	    fprintf(stderr,"ateol %d, comment '%s'\n",ateol,dynbuf);
  #endif
***************
*** 476,479 ****
--- 480,494 ----
  	    outmem();
  	
      dynbuf[dynbuf_current++] = c;
+ }
+ 
+ static char *return_dynbuf(void)
+ {
+     add_dynbuf('\0');
+ 
+     /* chop it back to size */
+     if ((dynbuf = realloc(dynbuf,dynbuf_current)) == 0)
+ 	outmem();
+ 
+     return dynbuf;	
  }
*** 1.7	1992/07/28 06:34:30
--- manpage.c	1992/08/17 06:45:32
***************
*** 1,14 ****
! /* $Id: manpage.c,v 1.7 1992/07/28 06:34:30 greyham Exp $
   * stuff to do with manual page outputing
   */
  
- #define section	"3"
- 
  #include <stdio.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <time.h>
  #include <stdlib.h>
  #include <ctype.h>
  #include "c2man.h"
  #include "manpage.h"
--- 1,18 ----
! /* $Id: manpage.c,v 1.9 1992/08/17 06:18:38 greyham Exp $
   * stuff to do with manual page outputing
   */
  
  #include <stdio.h>
  #include <sys/types.h>
  #include <sys/stat.h>
+ #include <errno.h>
  #include <time.h>
  #include <stdlib.h>
+ #ifdef NeXT
+ #include <libc.h>
+ #else
+ #include <unistd.h>
+ #endif
  #include <ctype.h>
  #include "c2man.h"
  #include "manpage.h"
***************
*** 15,20 ****
--- 19,26 ----
  #include "strconcat.h"
  #include "semantic.h"
  
+ const char *section = "3";
+ 
  /* list of manual pages */
  ManualPage *firstpage = NULL;
  ManualPage **lastpagenext = &firstpage;
***************
*** 106,112 ****
  output_comment(comment)
  char *comment;
  {
!     char lastchar = '.';
      
      if (!comment || !comment[0])
      {
--- 112,120 ----
  output_comment(comment)
  char *comment;
  {
!     boolean new_line = TRUE;
!     boolean capitalise = TRUE;
!     boolean dot_command = FALSE;
      
      if (!comment || !comment[0])
      {
***************
*** 114,151 ****
  	return;
      }
  
!     /* correct punctuation as it goes out */
      for (;*comment;comment++)
      {
! 	if (lastchar == '.' && islower(*comment))
! 	    lastchar = toupper(*comment);
! 	else
! 	    lastchar = *comment;
! 	    
! 	putchar(lastchar);
      }
  
      /* do a full stop if there wasn't one */
!     if (lastchar != '.')	putchar('.');
  }
  
  /* Output the list of function parameter descriptions.
   */
  void
! output_parameter_descriptions (params)
  ParameterList *params;
  {
      Parameter *p;
  
      for (p = params->first; p != NULL; p = p->next)
      {
  	/* use the declarator comment if we can */
  	char *comment = p->declarator.comment;
! 	
!     	put_string(".TP\n.B \"");
  	output_decl_spec(&p->decl_spec);
! 	putchar(' ');
! 	output_declarator(&p->declarator, FALSE);	/* all on one line */
  	put_string("\"\n");
  
  	/* output list of possible enum values, if any */
--- 122,200 ----
  	return;
      }
  
!     /* correct punctuation a bit as it goes out */
      for (;*comment;comment++)
      {
! 	int c = *comment;
! 
! 	if (dot_command)
! 	{
! 	    if (c == '\n')	dot_command = FALSE;
! 	}
! 	else if (new_line && c == '.')
! 	    dot_command = TRUE;
! 	else if (c == '.')
! 	    capitalise = TRUE;
! 	else if (!isspace(c) && capitalise)
! 	{
! 	    if (islower(c))	c = toupper(c);
! 	    capitalise = FALSE;
! 	}
!            
! 	putchar(c);
! 	new_line = c == '\n';
      }
  
      /* do a full stop if there wasn't one */
!     if (!dot_command && !capitalise)	putchar('.');
  }
  
  /* Output the list of function parameter descriptions.
   */
  void
! output_parameter_descriptions (params, function)
  ParameterList *params;
+ char *function;
  {
      Parameter *p;
  
+     /* Check for parameter names with no declaration specifiers.  This
+      * happens when a parameter name appears in the identifier list of a
+      * function definition but does not appear in the parameter declaration
+      * part.  The default type in this cause is "int".
+      */
+     for (p = params->first; p != NULL; p = p->next) {
+ 	if (strlen(p->decl_spec.text) == 0 &&
+ 	    strcmp(p->declarator.text, "...") != 0) {
+ 	    free(p->decl_spec.text);
+ 	    p->decl_spec.text = strdup("int");
+ 	}
+     }
+ 
      for (p = params->first; p != NULL; p = p->next)
      {
  	/* use the declarator comment if we can */
  	char *comment = p->declarator.comment;
! 
! 	if (p->suppress)	continue;
! 	 
!     	put_string(".TP\n.BR \"");
  	output_decl_spec(&p->decl_spec);
! 	
! 	/* not all parameters must have declarators: might be a prototype */
! 	if (p->declarator.text)
! 	{
! 	    putchar(' ');
! 	    output_declarator(&p->declarator, FALSE);	/* all on one line */
! 	}
! 	
! 	/* include function name if it's a duplicate */
! 	if (p->duplicate)
! 	{
! 	    put_string("\" \"\t(");
! 	    put_string(function);
! 	    putchar(')');
! 	}
  	put_string("\"\n");
  
  	/* output list of possible enum values, if any */
***************
*** 253,258 ****
--- 302,353 ----
      return ret;
  }
  
+ /* split out the 'Returns:' section of a function comment */
+ void
+ split_returns_comment(char *comment,
+ 		       char **description,	/* remaining description */
+ 		       char **returns   	/* return value */)
+ {
+     char *retstart;
+ 
+     for (retstart = strchr(comment,'\n');
+ 	 retstart;
+ 	 retstart = strchr(retstart,'\n'))
+     {
+ 	retstart++;	/* skip the newline */
+ 	if (!strncmp(retstart, "return",6) || !strncmp(retstart, "Return",6) ||
+ 	    !strncmp(retstart, "RETURN",6))
+ 	{	    
+ 	    char *descend = retstart - 2;	/* back before newline */
+ 
+ 	    /* go back to the end of the description in case there were
+ 	     * linefeeds before the returns.
+ 	     */
+ 	    while (descend > comment && isspace(*descend))
+ 		descend--;
+ 
+ 	    *description = alloc_string(comment,descend);
+ 
+ 	    retstart += 6;
+ 	    if (*retstart == 's' || *retstart == 'S')
+ 		retstart++;
+ 		
+ 	    while (*retstart == ':' || isspace(*retstart))
+ 		retstart++;
+ 
+ 	    if (*retstart)
+ 		*returns = strdup(retstart);
+ 	    else
+ 	    	*returns = NULL;
+ 	    return;
+ 	}
+     }
+ 
+     *description = strdup(comment);
+     *returns = NULL;
+     return;
+ }
+ 
  /* split the function comment into manual page format */
  void
  split_function_comment(char *comment,
***************
*** 260,266 ****
  		       char **description,/* full description */
  		       char **returns   /* return value */)
  {
!     char *firstdot, *firsteol, *retstart, *descstart;
  
      /* find the end of the terse comment */
      firstdot = strchr(comment,'.');
--- 355,361 ----
  		       char **description,/* full description */
  		       char **returns   /* return value */)
  {
!     char *firstdot, *firsteol, *descstart;
  
      /* find the end of the terse comment */
      firstdot = strchr(comment,'.');
***************
*** 287,293 ****
  	 descstart++;
  
      /* leave others out if not included */
!     if (!descstart)
      {
  	*description = NULL;
  	*returns = NULL;
--- 382,388 ----
  	 descstart++;
  
      /* leave others out if not included */
!     if (!*descstart)
      {
  	*description = NULL;
  	*returns = NULL;
***************
*** 294,383 ****
  	return;
      }
  
!     for (retstart = strchr(descstart,'\n');
! 	 retstart;
! 	 retstart = strchr(retstart,'\n'))
!     {
! 	retstart++;	/* skip the newline */
! 	if (!strncmp(retstart, "return",6) || !strncmp(retstart, "Return",6) ||
! 	    !strncmp(retstart, "RETURN",6))
! 	{	    
! 	    char *descend = retstart - 2;	/* back before newline */
  
! 	    /* go back to the end of the description in case there were
! 	     * linefeeds before the returns.
! 	     */
! 	    while (descend > descstart && isspace(*descend))
! 		descend--;
  
! 	    *description = alloc_string(descstart,descend);
  
! 	    retstart += 6;
! 	    if (*retstart == 's' || *retstart == 'S')
! 		retstart++;
! 		
! 	    while (*retstart == ':' || isspace(*retstart))
! 		retstart++;
  
! 	    if (*retstart)
! 		*returns = strdup(retstart);
! 	    else
! 	    	*returns = NULL;
! 	    return;
  	}
      }
  
!     *description = strdup(descstart);
!     *returns = NULL;
!     return;
  }
  
  /* Output a manual page describing the function.
   */
  void
! output_manpage (decl_spec, declarator)
! DeclSpec *decl_spec;
! Declarator *declarator;
  {
!     Parameter *p;
!     char *terse, *returns, *description;
!     struct stat statbuf;
!     struct tm *filetime;
!     static char month[20];
!       
!     /* Check for parameter names with no declaration specifiers.  This
!      * happens when a parameter name appears in the identifier list of a
!      * function definition but does not appear in the parameter declaration
!      * part.  The default type in this cause is "int".
!      */
!     for (p = declarator->params.first; p != NULL; p = p->next) {
! 	if (strlen(p->decl_spec.text) == 0 &&
! 	    strcmp(p->declarator.text, "...") != 0) {
! 	    free(p->decl_spec.text);
! 	    p->decl_spec.text = strdup("int");
! 	}
!     }
  
!     split_function_comment(
! 	declarator->comment,
! 	&terse,&description,&returns);
!         
!     /* stat the file to check the relevant date */
!     if (stat(cur_file,&statbuf) != 0)
      {
!     	perror(cur_file);
! 	exit(-1);
      }
  
!     filetime = localtime(&statbuf.st_mtime);
! 	
!     /* generate the date format string */
!     strftime(month, sizeof month,"%B",filetime);
!     
!     printf(".\\\" WARNING! THIS FILE WAS GENERATED AUTOMATICALLY BY c2man!\n");
!     printf(".\\\" DO NOT EDIT! CHANGES MADE TO THIS FILE WILL BE LOST!\n");
!     printf(".TH %s 3 \"%d %s %d\"",declarator->name,filetime->tm_mday,month,
!     	filetime->tm_year+1900);
  
  /* I have conflicting info about how the .TH macro works.... */
  #ifdef HENRY	/* As per Henry Spencer's "How to write a manual page" */
--- 389,516 ----
  	return;
      }
  
!     split_returns_comment(descstart, description, returns);
! }
  
! /* see if two parameters are declared identically */
! boolean params_identical(Parameter *first, Parameter *second)
! {
!     return
! 	(first->decl_spec.flags == second->decl_spec.flags &&
! 	 !strcmp(first->decl_spec.text, second->decl_spec.text) &&
! 	 first->decl_spec.enum_list == second->decl_spec.enum_list &&
! 	    ((!first->declarator.text && !second->declarator.text) ||
! 	     (first->declarator.text && second->declarator.text &&
! 	      !strcmp(first->declarator.text, second->declarator.text))
! 	    )
! 	);
! }
  
! /* search all the parameters in this grouped manual page for redundancies */
! boolean mark_duplicate_parameters(ManualPage *firstpage)
! {
!     Parameter *param;
!     boolean any = FALSE;
!     ManualPage *page;
  
!     for (page = firstpage; page; page = page->next)
!     {
! 	if (has_parameters(page->declarator))
! 	for (param = page->declarator->params.first; param;
! 							param = param->next)
! 	{
! 	    ManualPage *otherpage;
! 	    Parameter *otherparam;
! 	    
! 	    any = TRUE;
  
! 	    for (otherpage = page->next; otherpage;
! 						otherpage = otherpage->next)
! 	    {
! 		if (has_parameters(otherpage->declarator))
! 		for (otherparam = otherpage->declarator->params.first;
! 		     otherparam;
! 		     otherparam = otherparam->next)
! 		{
! 		    /* do these two look the same? */
! 		    if (params_identical(param, otherparam))
! 		    {
! 			enum { NEITHER, US, THEM, BOTH } has_comm = NEITHER;
! 			
! 			/* work out who has the comment */
! 			if (param->declarator.comment)	has_comm |= US;
! 			if (otherparam->declarator.comment) has_comm |= THEM;
! 
! 			switch(has_comm)
! 			{
! 			case NEITHER:
! 			case US:
! 			    otherparam->suppress = TRUE;
! 			    break;
! 			case THEM:
! 			    param->suppress = TRUE;
! 			    break;
! 			case BOTH:
! 			    if (!strcmp(param->declarator.comment,
! 					    otherparam->declarator.comment))
! 				otherparam->suppress = TRUE;
! 			    else
! 			    {
! 				param->duplicate = TRUE;
! 				otherparam->duplicate = TRUE;
! 			    }
! 			    break;
! 			}
! 		    }
! 		}
! 	    }
  	}
      }
+     return any;
+ }
  
! void sub_section(char *name)
! {
! #ifdef NeXT	/* NeXT's SS macro doesn't move to the left a little */
!     put_string(".}X \\n()Ru+\\n(INu\n.ti -0.25i\n");
!     put_string(name);
!     put_string("\n.br\n");
! #else
!     put_string(".SS \"");
!     put_string(name);
!     put_string("\"\n");
! #endif
  }
  
  /* Output a manual page describing the function.
   */
  void
! output_manpage (ManualPage *firstpage, struct tm *filetime, char *month)
  {
!     ManualPage *page;
!     boolean need_returns;
!     char *terse = NULL;
!     
!     /* check if there's more than one page in the group */
!     boolean grouped = group_terse && firstpage->next;
!     
!     printf(".\\\" WARNING! THIS FILE WAS GENERATED AUTOMATICALLY BY c2man!\n");
!     printf(".\\\" DO NOT EDIT! CHANGES MADE TO THIS FILE WILL BE LOST!\n");
!     put_string(".TH \"");
  
!     if (grouped)
      {
! 	char *c, *last = strrchr(cur_file, '.');
! 
! 	/* use the file name as the manual page name */
! 	for (c = cur_file; *c && c != last;c++)
! 	    putchar(*c);
      }
+     else
+ 	put_string(firstpage->declarator->name);
  
!     printf("\" %s \"%d %s %d\"",
! 		    section,filetime->tm_mday,month,filetime->tm_year+1900);
  
  /* I have conflicting info about how the .TH macro works.... */
  #ifdef HENRY	/* As per Henry Spencer's "How to write a manual page" */
***************
*** 389,407 ****
      if (manual_name) printf(" \"%s\"", manual_name);
      putchar('\n');    
  #endif
! 
!     printf(".SH NAME\n.B %s\n\\- ",declarator->name);
! 
! #if 0
!     for (s = declarator->text; *s && *s != '(';s++)
!     	putchar(*s);
! #endif	
!     if (terse) put_string(terse);
!     putchar('\n');
      
      printf(".SH SYNOPSIS\n");
  
!     put_string(".nf\n\\fB");
      
      /* if it's a header file, say to #include it */
      if (header_file())
--- 522,555 ----
      if (manual_name) printf(" \"%s\"", manual_name);
      putchar('\n');    
  #endif
!     put_string(".SH NAME\n");
!     
!     if (group_terse)
!     {
! 	for (page = firstpage; page; page = page->next)
! 	{
! 	    split_returns_comment(page->declarator->comment,
! 					&page->description, &page->returns);
! 	    put_string(page->next ? ".BR " : ".B ");
! 	    put_string(page->declarator->name);
! 	    if (page->next)	put_string(" ,");
! 	    putchar('\n');
! 	}
! 	
! 	put_string("\\- ");
! 	put_string(group_terse);
! 	putchar('\n');
!     }
!     else
!     {
! 	split_function_comment(firstpage->declarator->comment,
! 			&terse,&firstpage->description,&firstpage->returns);
! 	printf(".B %s\n\\- %s\n",firstpage->declarator->name, terse);
!     }
      
      printf(".SH SYNOPSIS\n");
  
!     put_string(".nf\n.ft B\n");
      
      /* if it's a header file, say to #include it */
      if (header_file())
***************
*** 411,454 ****
  	printf("%s>\n\n",cur_file);
      }
      
!     put_string(decl_spec_prefix);
!     
!     /* make sure variables are prefixed extern */
!     if (!(decl_spec->flags & DE_STATIC) &&
! 	declarator->func_def == FUNC_NONE &&
! 	!strstr(decl_spec->text, "extern"))
! 	put_string("extern ");
  	
!     output_decl_spec(decl_spec);
!     put_string(declarator_prefix);
!     output_declarator(declarator, TRUE);	/* format it nicely */
!     put_string(";\\fR\n.fi\n");
      
      /* only output paramaters if there actually are some,
       * not including merely (void)
       */
!     if (declarator->params.first && declarator->params.first->declarator.text)
      {
! 	printf(".SH PARAMETERS\n");
! 	output_parameter_descriptions(&(declarator->params));
!     }
  
!     printf(".SH DESCRIPTION\n");
!     if (description)	output_comment(description);
!     else		output_comment(terse);
  
!     putchar('\n');
      
!     if (returns)
      {
  	printf(".SH RETURNS\n");
! 	output_comment(returns);
! 	putchar('\n');
      }
  
!     if (terse)		free(terse);
!     if (description)	free(description);
!     if (returns)	free(returns);
  }
  
  /* output all the manual pages in a list */
--- 559,671 ----
  	printf("%s>\n\n",cur_file);
      }
      
!     for (page = firstpage; page; page = page->next)
!     {
! 	put_string(decl_spec_prefix);
  	
! 	/* make sure variables are prefixed extern */
! 	if (!(page->decl_spec->flags & DE_STATIC) &&
! 	    page->declarator->func_def == FUNC_NONE &&
! 	    !strstr(page->decl_spec->text, "extern"))
! 	    put_string("extern ");
! 	    
! 	output_decl_spec(page->decl_spec);
! 	put_string(declarator_prefix);
! 
! 	/* format it nicely if there's more than one parameter */
! 	output_declarator(page->declarator, 
! 	    page->declarator->params.first != page->declarator->params.last);
! 
! 	put_string(";");
      
+ 	if (!grouped)	break;
+ 	if (page->next)		put_string("\n\n");
+     }
+ 
+     put_string("\n.ft R\n.fi\n");
+ 
      /* only output paramaters if there actually are some,
       * not including merely (void)
       */
!     if ((grouped && mark_duplicate_parameters(firstpage)) ||
!         (!grouped && has_parameters(firstpage->declarator)))
      {
! 	put_string(".SH PARAMETERS\n");
  
! 	for (page = firstpage; page; page = page->next)
! 	{
! 	    if (has_parameters(page->declarator))
! 		output_parameter_descriptions(&page->declarator->params,
! 						    page->declarator->name);
! 	    if (!grouped)	break;	/* only do first page */
! 	}
!     }
  
!     put_string(".SH DESCRIPTION\n");
!     if (grouped)
!     {
! 	need_returns = FALSE;
! 	for (page = firstpage; page; page = page->next)
! 	{
! 	    if (page->returns)	need_returns = TRUE;
! 	    if (page->description)
! 	    {
! 		sub_section(page->declarator->name);
! 		output_comment(page->description);
! 		putchar('\n');
! 		free(page->description);
! 	    }
! 	}
!     }
!     else
!     {
! 	need_returns = firstpage->returns != NULL;
! 	output_comment(firstpage->description ? firstpage->description
! 					      : terse);
! 	putchar('\n');
! 	if (firstpage->description)	free(firstpage->description);
! 	if (terse)			free(terse);
!     }
      
!     if (need_returns)
      {
  	printf(".SH RETURNS\n");
! 
! 	if (grouped)
! 	{
! 	    for (page = firstpage; page; page = page->next)
! 	    {
! 		if (page->returns)
! 		{
! 		    sub_section(page->declarator->name);
! 		    output_comment(page->returns);
! 		    putchar('\n');
! 		    free(page->returns);
! 		}
! 	    }
! 	}
! 	else
! 	{
! 	    output_comment(firstpage->returns);
! 	    putchar('\n');
! 	    free(firstpage->returns);
! 	}
      }
+ }
  
! 
! /* generate output filename for a specified page */
! char *page_file_name(ManualPage *page)
! {
!     char *filename;
!     
!     if (output_dir)
! 	filename = strconcat(output_dir,"/",
! 			    page->declarator->name,".",section,NULL);
!     else
! 	filename = strconcat(page->declarator->name,".",section,NULL);
! 	
!     return filename;
  }
  
  /* output all the manual pages in a list */
***************
*** 455,490 ****
  void output_manual_pages(ManualPage *first)
  {
      ManualPage *page;
-     extern char *output_dir;
      int tostdout = output_dir && !strcmp(output_dir,"-");
  
      /* output each page, in turn */
      for (page = first; page; page = page->next)
      {
  	ManualPage *also;
- 	char *filename = NULL;
  
! 	if (!tostdout)
  	{
! 	    /* open the output file */
! 	    if (output_dir)
! 		filename = strconcat(output_dir,"/",
! 	    			 page->declarator->name,".",section,NULL);
! 	    else
! 		filename = strconcat(page->declarator->name,".",section,NULL);
! 
  	    fprintf(stderr,"generating: %s\n",filename);
  	    if (freopen(filename, "w", stdout) == NULL)
  	    {
- 		free(filename);
  		perror(filename);
  		exit(1);
  	    }
  	}
  
  	/* do the page itself */
! 	output_manpage(page->decl_spec,page->declarator);
! 	
  	/* add the SEE ALSO section */
  	/* look for any other functions to refer to */
  	for (also = first; also && also == page; also = also->next)
--- 672,726 ----
  void output_manual_pages(ManualPage *first)
  {
      ManualPage *page;
      int tostdout = output_dir && !strcmp(output_dir,"-");
+     struct stat statbuf;
+     struct tm *filetime;
+     static char month[20];
+     char *filename = NULL;
+ 
+     /* stat the file to check the relevant date */
+     if (stat(cur_file,&statbuf) != 0)
+     {
+     	perror(cur_file);
+ 	exit(-1);
+     }
  
+     filetime = localtime(&statbuf.st_mtime);
+ 	
+     /* generate the date format string */
+     strftime(month, sizeof month,"%B",filetime);
+     
      /* output each page, in turn */
      for (page = first; page; page = page->next)
      {
  	ManualPage *also;
  
! 	if (tostdout)
  	{
! 	    /* don't FormFeed before the first manual page */
! 	    static boolean first_page = TRUE;
!     
! 	    if (!first_page)	putchar('\f');
! 	    first_page = FALSE;
! 	}
! 	else
! 	{
! 	    filename = page_file_name(page);
  	    fprintf(stderr,"generating: %s\n",filename);
  	    if (freopen(filename, "w", stdout) == NULL)
  	    {
  		perror(filename);
+ 		free(filename);
  		exit(1);
  	    }
  	}
  
  	/* do the page itself */
! 	output_manpage(page, filetime, month);
! 
! 	/* no SEE ALSO if grouped */
! 	if (group_terse)	break;
! 		    
  	/* add the SEE ALSO section */
  	/* look for any other functions to refer to */
  	for (also = first; also && also == page; also = also->next)
***************
*** 497,503 ****
  	    
  	    for (also = first; also; also = also->next)
  	    {
! 	    	if (also == page)	continue;
  		
  		if (!isfirst)
  		    fputs(", ",stdout);
--- 733,739 ----
  	    
  	    for (also = first; also; also = also->next)
  	    {
! 		if (also == page)	continue;
  		
  		if (!isfirst)
  		    fputs(", ",stdout);
***************
*** 509,520 ****
  	
  	    putchar('\n');
  	}
  
! 	if (tostdout)
  	{
! 	    if (page->next)	putchar('\f');
  	}
! 	else
! 	    free(filename);	
      }
  }
--- 745,773 ----
  	
  	    putchar('\n');
  	}
+ 	free(filename);
+     }
  
!     /* if pages are grouped, just link the rest to the first */
!     if (group_terse && !tostdout)
!     {
! 	for (page=page->next;page;page = page->next)
  	{
! 	    char *linkname = page_file_name(page);
! 	    
! 	    fprintf(stderr,"linking: %s\n",linkname);
! 	    if (link(filename, linkname) == -1)
! 	    {
! 		/* if it already exists, nuke it & relink */
! 		if (errno == EEXIST &&
! 		    unlink(linkname) == 0 && link(filename, linkname) == 0)
! 		    continue;
! 
! 		perror(linkname);
! 	    }
! 	    free(linkname);
! 	    continue;
  	}
! 	free(filename);
      }
  }
*** 1.2	1992/07/17 06:41:33
--- manpage.h	1992/08/17 06:45:29
***************
*** 1,4 ****
! /* $Id: manpage.h,v 1.2 1992/07/17 06:41:33 greyham Exp $
   * stuff to do with manual page outputing
   */
  
--- 1,4 ----
! /* $Id: manpage.h,v 1.3 1992/08/17 06:18:38 greyham Exp $
   * stuff to do with manual page outputing
   */
  
***************
*** 8,13 ****
--- 8,15 ----
      DeclSpec *decl_spec;
      Declarator *declarator;
      ManualPage *next;
+     char *description;
+     char *returns;
  };
  
  /* list of manual pages */
*** 1.3	1992/07/21 11:47:42
--- semantic.c	1992/08/17 06:45:31
***************
*** 1,4 ****
! /* $Id: semantic.c,v 1.3 1992/07/21 11:47:42 greyham Exp $
   *
   * C manual page generator
   * These routines implement the semantic actions executed by the yacc parser.
--- 1,4 ----
! /* $Id: semantic.c,v 1.5 1992/08/17 06:18:38 greyham Exp $
   *
   * C manual page generator
   * These routines implement the semantic actions executed by the yacc parser.
***************
*** 126,131 ****
--- 126,133 ----
  
      param->declarator.comment =
  			comment_before ? comment_before : comment_after;
+     param->suppress = FALSE;
+     param->duplicate = FALSE;
  
      return 1;
  }
***************
*** 144,152 ****
--- 146,162 ----
  int
  comment_last_parameter(ParameterList *list, char *comment)
  {
+     if (list->last == NULL)
+     {
+ 	yyerror("comment '%s' applies to non-existent parameter", comment);
+ 	free(comment);
+ 	return 0;
+     }
+ 
      if (list->last->declarator.comment)
      {
  	parameter_error(list->last);
+ 	free(comment);
  	return 0;
      }
      else
***************
*** 271,277 ****
      free_param_list(&(d->params));
  }
  
! /* add a comment to the last enumerator in the list */
  int
  comment_last_decl(DeclaratorList *list, char *comment)
  {
--- 281,287 ----
      free_param_list(&(d->params));
  }
  
! /* add a comment to the last declarator in the list */
  int
  comment_last_decl(DeclaratorList *list, char *comment)
  {
***************
*** 410,423 ****
  }
  
  static void
! output_parameters (ParameterList *params, boolean format);
  
! /* no parameters in function? */
! int no_parameters(Parameter *firstparam)
  {
!     return (firstparam == NULL ||
! 	   (strcmp(firstparam->decl_spec.text, "void") == 0 &&
! 	    firstparam->declarator.text == NULL));
  }
  
  /* Output a declarator.
--- 420,437 ----
  }
  
  static void
! output_parameters (Declarator *d, boolean format);
  
! /* does a function have any parameters?
!  * This accounts for both fn() and fn(void)
!  */
! boolean has_parameters(Declarator *d)
  {
!     Parameter *first = d->params.first;
! 
!     return (first != NULL &&
! 	   (first->declarator.text != NULL ||
! 	    strcmp(first->decl_spec.text, "void")));
  }
  
  /* Output a declarator.
***************
*** 433,442 ****
  	if ((s = strstr(d->text, "()")) != NULL) {
  	    *s = '\0';
  	    put_string(d->text);
! 	    if (format && !no_parameters(d->params.first))
  		put_string(declarator_suffix);
  	    fputc(*s++ = '(', stdout);
! 	    output_parameters(&(d->params), format);
  	    fputc(*s++, stdout);
  	    put_string(s);
  	}
--- 447,456 ----
  	if ((s = strstr(d->text, "()")) != NULL) {
  	    *s = '\0';
  	    put_string(d->text);
! 	    if (format && has_parameters(d))
  		put_string(declarator_suffix);
  	    fputc(*s++ = '(', stdout);
! 	    output_parameters(d, format);
  	    fputc(*s++, stdout);
  	    put_string(s);
  	}
***************
*** 461,474 ****
  /* Output the list of function parameters.
   */
  static void
! output_parameters (ParameterList *params, boolean format)
  {
!     Parameter *p;
! 
!     p = params->first;
!     if (no_parameters(p)) {
! 	put_string("void");
!     } else {
  	if (format) put_string(first_param_prefix);
  	output_parameter(p);
  	p = p->next;
--- 475,484 ----
  /* Output the list of function parameters.
   */
  static void
! output_parameters (Declarator *d, boolean format)
  {
!     if (has_parameters(d)) {
!         Parameter *p = d->params.first;
  	if (format) put_string(first_param_prefix);
  	output_parameter(p);
  	p = p->next;
***************
*** 480,485 ****
--- 490,497 ----
  	}
  	if (format) put_string(last_param_suffix);
      }
+     else
+ 	put_string("void");
  }
  
  /* remember function declarations.
***************
*** 497,509 ****
      /* attach EOL comment to last one in list */
      if (eolcomment)
      {
! 	if (decl_list->last->comment)
  	{
! 	    declarator_error(decl_list->last);
  	    return 0;
  	}
  	else
! 	    decl_list->last->comment = eolcomment;
      }
      
      /* special case of a single declarator handled efficiently */
--- 509,530 ----
      /* attach EOL comment to last one in list */
      if (eolcomment)
      {
! 	Declarator *attach;
! 
! 	/* if it's a function, attach it to the last parameter */
! 	if (decl_list->last->func_def != FUNC_NONE &&
! 	    decl_list->last->params.last)
! 	    attach = &decl_list->last->params.last->declarator;
! 	else
! 	    attach = decl_list->last;
! 	    
! 	if (attach->comment)
  	{
! 	    declarator_error(attach);
  	    return 0;
  	}
  	else
! 	    attach->comment = eolcomment;
      }
      
      /* special case of a single declarator handled efficiently */
*** 1.2	1992/07/17 06:41:33
--- semantic.h	1992/08/17 06:45:29
***************
*** 1,4 ****
! /* $Id: semantic.h,v 1.2 1992/07/17 06:41:33 greyham Exp $
   *
   * Declarations for semantic action routines
   */
--- 1,4 ----
! /* $Id: semantic.h,v 1.3 1992/08/17 06:18:38 greyham Exp $
   *
   * Declarations for semantic action routines
   */
***************
*** 123,125 ****
--- 123,127 ----
  
  void parameter_error(Parameter *param);
  void declarator_error(Declarator *decl);
+ 
+ boolean has_parameters(Declarator *d);
*** /dev/null	Mon Aug 17 12:36:26 1992
--- INSTALL	Mon Aug 17 16:45:26 1992
***************
*** 0 ****
--- 1,28 ----
+ INSTALLING c2man:
+ 
+ 1. You will need a C compiler that can handle ANSI constructs (prototypes,
+    string concatenation, free(NULL), etc), or an ANSI to K&R converter.
+    Don't put the compiler in strict ANSI mode though, since c2man uses a few
+    UNIX-isms that may disappear from your header files if you do that
+    (eg: popen). Don't use -ansi on gcc for example.
+ 
+    Don't use -Wall with gcc unless your system header files have basic stuff
+    like fprintf in stdio.h (SunOS Release 4.1.2 for example!).
+ 
+    Sun yacc outputs incorrect prototypes for malloc and free (says they return
+    char * instead of void *), which gcc 2.2 or later errors on; use bison
+    instead, or just delete the incorrect prototypes on the first line of
+    y.tab.c when the make errors.
+ 
+    If using gcc on a Sun, you may need GNU libc to get stdarg.h
+ 
+ 2. Look at the Makefile, and check that CC, DEFINES, WARN, LIBS and BSD are
+    set if necessary. Optionally, set LEX and YACC too.
+ 
+ 3. Type "make"
+ 
+ 4. Look at the manual page. The example in it is automatically generated, so
+    check that it looks OK.
+ 
+ 5. As root, do "make install". This will copy the c2man binary to
+    /usr/local/bin, and flatten the manual page into /usr/local/man/man1
*** /dev/null	Mon Aug 17 12:36:26 1992
--- ctype.h	Mon Aug 17 16:45:27 1992
***************
*** 0 ****
--- 1,36 ----
+ /* character is alphanumeric
+  * returns 0 if the character doesn't fit the
+  * classification; non-zero (but not necessarily 1)
+  * if it does.
+  */
+ inline int isalnum(int c	/* the character to classify */);
+ 
+ /* character is a letter */
+ inline int isalpha(int c);
+ 
+ /* character is a control character */
+ inline int iscntrl(int c);
+ 
+ /* character is a digit */
+ inline int isdigit(int c);
+ 
+ /* character is a graphic */
+ inline int isgraph(int c);
+ 
+ /* character is a lower case letter */
+ inline int islower(int c);
+ 
+ /* character is printable */
+ inline int isprint(int c);
+ 
+ /* character is punctuation */
+ inline int ispunct(int c);
+ 
+ /* character is a a form of whitespace */
+ inline int isspace(int c);
+ 
+ /* character is an upper case letter */
+ inline int isupper(int c);
+ 
+ /* character is a hexadecimal digit */
+ inline int isxdigit(int c);
*** /dev/null	Mon Aug 17 12:36:26 1992
--- ctype.terse	Mon Aug 17 16:45:28 1992
***************
*** 0 ****
--- 1 ----
+ character classification functions
-- 
Graham Stoney                    | "a Perl script is correct if it's halfway
Flip Dibner fan club, "Hi Flip!" |  readable and gets the job done before your
Ph: +61 2 805-2909  Fax: -2929   |  boss fires you." L. Wall & R. Schwartz
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads