diff options
author | behdad <behdad> | 2004-05-03 05:17:48 +0000 |
---|---|---|
committer | behdad <behdad> | 2004-05-03 05:17:48 +0000 |
commit | 577ed4095383ef5284225d45709e6b5f0598a064 (patch) | |
tree | 6c7d0ce55124a688b4d7050e684d9d7a1e3aa71d |
-rw-r--r-- | C++autodoc | 292 | ||||
-rw-r--r-- | CHANGES | 127 | ||||
-rw-r--r-- | ChangeLog | 29 | ||||
-rwxr-xr-x | Configure | 4822 | ||||
-rw-r--r-- | FAQ | 204 | ||||
-rw-r--r-- | INSTALL | 74 | ||||
-rw-r--r-- | MANIFEST | 64 | ||||
-rwxr-xr-x | Makefile.SH | 172 | ||||
-rw-r--r-- | README | 112 | ||||
-rw-r--r-- | autodoc.c | 554 | ||||
-rw-r--r-- | c2man.c | 928 | ||||
-rw-r--r-- | c2man.h | 284 | ||||
-rw-r--r-- | c2man.man | 906 | ||||
-rw-r--r-- | catalog | 44 | ||||
-rw-r--r-- | config_h.SH | 353 | ||||
-rw-r--r-- | confmagic.h | 27 | ||||
-rw-r--r-- | ctype_ex.h | 38 | ||||
-rw-r--r-- | eg/boxcomment.c | 41 | ||||
-rw-r--r-- | eg/ccomment.h | 41 | ||||
-rw-r--r-- | eg/commentaft.c | 1 | ||||
-rw-r--r-- | eg/cppcomment.h | 32 | ||||
-rw-r--r-- | eg/dash.h | 2 | ||||
-rw-r--r-- | eg/ellipsis.c | 15 | ||||
-rw-r--r-- | eg/grouped.c | 23 | ||||
-rw-r--r-- | eg/multidecl.c | 3 | ||||
-rw-r--r-- | eg/namedash.h | 9 | ||||
-rw-r--r-- | eg/oldstyle.c | 24 | ||||
-rw-r--r-- | eg/retdecl.c | 10 | ||||
-rw-r--r-- | eg/returnerr.h | 16 | ||||
-rw-r--r-- | eg/returnlist.h | 16 | ||||
-rw-r--r-- | eg/sections.c | 17 | ||||
-rw-r--r-- | eg/simplesect.c | 13 | ||||
-rw-r--r-- | eg/surround.c | 6 | ||||
-rw-r--r-- | eg/underscore.h | 21 | ||||
-rw-r--r-- | eg/variable.c | 5 | ||||
-rw-r--r-- | enum.c | 179 | ||||
-rw-r--r-- | enum.h | 41 | ||||
-rw-r--r-- | example.h | 18 | ||||
-rw-r--r-- | fixexamp.in | 60 | ||||
-rw-r--r-- | flatten.SH | 51 | ||||
-rw-r--r-- | grammar.y | 947 | ||||
-rw-r--r-- | html.c | 453 | ||||
-rw-r--r-- | latex.c | 356 | ||||
-rw-r--r-- | lex.l | 621 | ||||
-rw-r--r-- | libc/COPYING | 339 | ||||
-rw-r--r-- | libc/README.libc | 10 | ||||
-rw-r--r-- | libc/alloca.c | 186 | ||||
-rw-r--r-- | libc/getopt.c | 735 | ||||
-rw-r--r-- | libc/getopt.h | 129 | ||||
-rw-r--r-- | libc/getopt1.c | 176 | ||||
-rw-r--r-- | manpage.c | 1347 | ||||
-rw-r--r-- | manpage.h | 64 | ||||
-rw-r--r-- | nroff.c | 424 | ||||
-rw-r--r-- | output.h | 149 | ||||
-rw-r--r-- | patchlevel.h | 2 | ||||
-rw-r--r-- | semantic.c | 732 | ||||
-rw-r--r-- | semantic.h | 121 | ||||
-rw-r--r-- | strappend.c | 64 | ||||
-rw-r--r-- | strappend.h | 6 | ||||
-rw-r--r-- | strconcat.c | 74 | ||||
-rw-r--r-- | strconcat.h | 6 | ||||
-rw-r--r-- | string.c | 82 | ||||
-rw-r--r-- | symbol.c | 119 | ||||
-rw-r--r-- | symbol.h | 41 | ||||
-rw-r--r-- | texinfo.c | 465 |
65 files changed, 17322 insertions, 0 deletions
diff --git a/C++autodoc b/C++autodoc new file mode 100644 index 0000000..468f1dd --- /dev/null +++ b/C++autodoc @@ -0,0 +1,292 @@ +From greyham Thu Oct 28 18:42:35 1993 +Newsgroups: comp.lang.c++,comp.programming.literate +Subject: An Automatic C++ documentation compilation project. +Summary: Anyone willing to add C++ support to c2man? +Keywords: c2man, C, C++, Literate Programming, Documentation + +Copyright 1993, 1994 by Graham Stoney. +This may be freely redistributed or quoted, so long as it's attributed to me. + +Writing and maintaining documentation has often been a thorn in the side of the +Software Engineer and Programmer. After spending a great deal of time and +effort writing documentation about a program or software system, the code +invariably changes, quickly rendering the documentation out of date. The +documentation becomes misleading, gets neglected, and quickly becomes useless. + +"Literate Programming" is one approach to solving this problem. It effectively +introduces a whole new (typesetting) language, requires a quite radical shift +on the part of the "non-literate" programmer and still requires a good deal of +effort on the part of the programmer[1]. + +I'd like to suggest a different approach which lies considerably closer to +more traditional programming practices, and can offer quite immediate benefits +when functional interface documentation is the main documentation required. + +The primary philosophy here is to use the programming language as far as +possible to express the programmer's intentions, and to use comments only when +the programming language is not sufficiently expressive. A comment can then +become part of the language grammar which is recognised by a "documentation +compiler". This tool parses a superset of the programming language and can +automatically generate documentation in human-readable form by associating the +programmer's comments with the objects in the code by their context. + +Whilst the idea of extracting documentation from comments in source code is by +no means new, the difference here is that the comments actually form part of +the grammar of the language recognised by the documentation compiler[2]. + +Comments should not repeat information that is already represented in the +program code; for instance, a comment describing a function argument should not +repeat the name and type of that argument (since that information has already +been included, for the compiler), but should appear near the argument. + +For example, in C, the programmer should write this: + + /* include an example in the article */ + enum Result example(int page /* page it appears on */); + +Rather than this: + + /* include an example in the article + * + * PARAMETERS: + * int page page it appears on + * + * RETURNS: + * RESULT_YES The readers agreed + * RESULT_NO The readers disagreed + * RESULT_YOURE_JOKING The readers disagreed strongly + * RESULT_BLANK_LOOKS The readers didn't understand + */ + enum Result example(int page); + + +Also in this example, the documentation compiler knows the possible enumerated +values that the function can return (as does the "real" compiler), so it is +unnecessary for the programmer to restate them. The comments need simply be +included in the definition for "enum Result" for the "RETURNS" information to +be generated automatically: + + enum Result { + RESULT_YES, /* The readers agreed */ + RESULT_NO, /* The readers disagreed */ + RESULT_YOURE_JOKING, /* The readers disagreed strongly */ + RESULT_BLANK_LOOKS /* The readers didn't understand */ + }; + +Critics have suggested that the latter style in the example is easier to read +for someone wishing to call the function in question. Of course, this is a +style question which depends on each person's tastes; but the criticism is tied +to the notion that the source code needs to look "beautiful" because it is the +primary reference for someone wishing to use that function. This becomes much +less significant once documentation is available which is known to _always_ be +up to date. Of course, the latter style takes longer to write and maintain, +and can become out of date should the name or type of the parameter be +changed, yet the comment get neglected. + +I have implemented one such documentation compiler for the C language called +"c2man", which is freely available[3]. The response from users has been +extremely encouraging; I suspect this is partly because of the wide variety of +styles of comment placement that are recognised: it often correctly recognises +comments that weren't written with c2man in mind at all. While it's use is +focused solely on functional interface documentation and it doesn't have +anywhere near the power of a full Literate Programming system, the focus is on +reducing the effort required by the programmer to the absolute minimum, and +seeing how much documentation we can get essentially "for free". + +Many people have requested C++ support be added to c2man, and I suspect that +this philosophy would be even more suitable and powerful for documenting +interfaces to C++ classes automatically. + +Here is an example of how I envisage this philosophy would work when applied to +C++. It's interesting to note that this code was written a couple of years ago +exactly as you see it here, without the idea of generating documentation from +it in mind at all: + + + // generic Timer class + class Timer + { + private: + static int numactive; // number of constructed timers. + static Timer *first; // first one in list. + Timer *next; // next one in linked list. + Time ticksdiff; // ticks we take to expire once at front. + + enum + { + INACTIVE, // timer is not in chain. + STARTED, // one-shot + RUNNING // continuous. + } state; + + // original interrupt vector value. + static void interrupt (far *old_vector)(...); + + void (*timeout_function)(int); // function called when we time out + int timeout_parameter; // gets passed to timeout_function + Time duration; // timer length (ticks) + + static void interrupt far tick(...); // clock tick routine. + + void insert(); // add into active chain. + void remove(); // remove from active chain. + void set(Time milliseconds); // set duration from ms. + + public: + // constructor + Timer(Time time=0, // milliseconds + void (*function)(int)=0, // called at timeout + int param=-1); // param for function + + // destructor + ~Timer(); + + // start (or restart) a timer running. + void Start(); + void Start(Time duration); // how long to run for + + // start a timer running continuous. + void Run(); + + // stop a timer. + void Stop(); + + // is a timer active? + boolean Active() const { return state != INACTIVE; }; + }; + + +Processing this class declaration could generate the following automatically: + + NAME + Timer - generic timer class + + SYNOPSIS + class Timer + { + public: + Timer(Time time=0, + void (*function)(int)=0, + int param=-1); + ~Timer(); + void Start(); + void Start(Time duration); + void Run(); + void Stop(); + boolean Active() const; + }; + + PARAMETERS + Time time + Milliseconds + + void (*function)(int) + called at timeout. + + int param + Param for function. + + Time duration + How long to run for. + + DESCRIPTION + Timer + Constructor + + ~Timer + Destructor + + Start + Start (or restart) a timer running. + + Run + Start a timer running continuous. + + Stop + Stop a timer. + + Active + Is a timer active?. + + +It should also be possible to extract this information from the implementation +of the class (rather than the declaration), if that's where the user prefers to +put the comments describing each member function and their parameters. + +The ideal tool should: +1. Avoid imposing a style on the programmer. +2. Work out section names (NAME, SYNOPSIS etc) without the programmer having + to specify them explicitly. +3. Handle C++ and C style code equally well. +4. Not require the programmer to restate information which is already expressed + in the syntax of the programming language. +5. Work reasonably well with existing code. +6. Flatten the class hierarchy so that the documentation for each class + includes virtually everything the user needs to know about it. + +A number of tools already exist which attempt to tackle this problem, such as +class2man, genman, classdoc and docclass. They vary in sophistication, +utility, and the demands they place on the programmer; however, none as yet +meet all the criteria set out above, and no one tool will suit the tastes of +all programmers. + +Pouring lots of effort into a really ``smart'' documentation generator makes +sense because once it's done, you get a payback for every document you +generate. Every little feature added to the documentation generator to make +things easier for the programmer pays off multiple times, and minimising the +effort required by the programmer is the key. + +The logical starting point would be to graft Jim Roskind's C++ grammar[4] into +c2man, modifying it to recognise comments in the relevant places, and adding +all the necessary structures to hold the information from the parser that will +get included in the output. Very little functional change should be needed in +the lexer, which already recognises C++ comments. + +Unfortunately, at present I do not have sufficient spare time to make the +additions to c2man required to support C++. It would be a great contribution to +the C++ community, not to mention the documentation time saved by themselves, +for someone involved in C++ work to add this support and release the result[5]. + +If you work with a team developing C++ code, please consider having one of your +developers on a ``Usenet Sabbatical'' to extend this philosophy to C++, and +start reaping the benefits in documentation time savings. + +It could also make an ideal Computer Science student compiler project. + +Please contact me via E-mail if you are interested in undertaking such a +project. + + +Graham Stoney +greyham@research.canon.com.au + +Footnotes: +1. Advocates of Literate Programming would argue that Literate Programming is + much more than snazzy documents and that it encourages this extra effort to + focus early on in the design of the software, which pays off later. + +2. To get a better idea, see the file grammar.y in the c2man distribution. + +3. c2man has been posted to comp.sources.misc. It should be available from: +location: ftp from any comp.sources.misc archive, in volume42 + (the version in the comp.sources.reviewed archive is obsolete) + ftp /pub/Unix/Util/c2man-2.0.*.tar.gz from dnpap.et.tudelft.nl + Australia: ftp /usenet/comp.sources.misc/volume42/c2man-2.0/* + from archie.au + N.America: ftp /usenet/comp.sources.misc/volume42/c2man-2.0/* + from ftp.wustl.edu + Europe: ftp /News/comp.sources.misc/volume42/c2man-2.0/* + from ftp.irisa.fr + Japan: ftp /pub/NetNews/comp.sources.misc/volume42/c2man-2.0/* + from ftp.iij.ad.jp + Patches: ftp pub/netnews/sources.bugs/volume93/sep/c2man* from lth.se + +4. Jim Roskind's yaccable C++ grammar is available via ftp from + ics.uci.edu in the ftp/pub directory as: + + c++grammar2.0.tar.Z + byacc1.8.tar.Z + +5. c2man's copyright requires that all derivative works remain freely + available. + @@ -0,0 +1,127 @@ +Version 1.1: + This was the first release, so there were no changes yet. + +Version 1.2: + The README in 1.1 incorrectly stated that c2man was public domain. It + is in fact copyright, but freely redistributable. See the updated + README for details. + + You can use flex instead of lex now. + + Comments on parameters in old-style function definitions work OK now. + + Generates man pages for global variables (with -v) as well as + functions. + + Major mods to parser to support comments at the end of lines; this + makes it much more tolerant about comment placement. + + c2man will now parse its own source!. The manual pages it generates are + pretty bad since the comments haven't been strategically placed, but + it's better than you might expect without trying. + + Rewrote a lot of the manual page to make it comprehensible. + +Version 1.3: + Ignores all comments in included files, except those in enum + specifications; this should virtually eliminate trouble with errors in + files you may have no control over, like system headers. + + Add "-o -" functionality to write manual pages to stdout. + + Run /lib/cpp when reading from stdin too. + + Rewrite comment parsing to be neater & easier to modify. + + Replace -DSYSV option with its inverse -DBSD so ANSI string functions + are used by default. + + 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...). + +Version 1.5: + Recognise end-of-line comments after K&R style function parameters. + + Specify C-preprocessor command in Makefile and add -P option to specify + a different preprocessor at runtime. + + Add -g option like -G, but read the terse description from the file. + + Don't disable nroff line filling in SYNOPSIS section so very long lines + still get broken. + + Require a white-space after a period before capitalising the next + letter. i.e. Don't damage "i.e.". + +Version 1.6: + Don't reduce pointer-to-pointers down to a single indirection level; + eg: "char **argv" got output as "char *argv". Ugh!. + +Version 1.7: + Don't try to free a string constant right at the very end!. + + Rename old -i option to -H, and add new -i option so user can specify + extra #include lines for the SYNOPSIS section, and prerequisites for .h + file compilation. + + Work around Sun acc -E problem where .h files were ignored. + + Semantic change: multiple input files all get cross-referenced in the + SEE ALSO section (or grouped together if -g or -G). + +Version 1.8: + Add -lPW I'd dropped from HPUX configuration. + + Fix multiple -i arguments appearing on the one line. + +Version 1.9: + Fix silly missing fprintf arguments in c2man.c + + Ignore trailing spaces after a comment at the end of a line. + + Don't error on "typedef int NewType; NewType a;" + + -Ssection option added by jerry@kesa24.kesa.com (Jerry E. Dunmire). + +Version 1.10: + Accept comments around a final ellipsis parameter. + + Make parameter declarations in the prototype and the PARAMETERS section + identical. + +Version 2.0: + Major modifications to use Configure for easy installation. + + Many, many portability fixes; converted to K&R 1 C. + + Accept stdin even if preprocessor doesn't. + + Don't error on: int fred() { + } /* fred */ + + Allow extra manual page sections not recognized by c2man. + +From version 2.0 onwards, the change log for each patch is contained in the +top of the patch itself. This file is no longer updated. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..0dc65b1 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,29 @@ +Wed Apr 24 12:08:09 EST 1996 Graham Stoney <greyham@research.canon.com.au> + +. Description: + + -k doesn't take an option, reported by J.Thompson@swansea.ac.uk. + + Add Marco Nijdam to the awards list. + + Handle RETURNS comment next to declarator, from Marco Nijdam. + Also add an example of its usage in the eg directory. + + Allow spaces in a section heading, from Marco Nijdam. + + Make #include more flexible, from Steven E. Haehn. + + Handle comments boxed with ---- and =====. + + Update the README a bit. + + Add ChangeLog file, to track changes. + + Update my E-mail address. + The '.oz.au' in my address has now officially changed to '.com.au', + although the old address will continue to work indefinitely. + + Fix a couple of minor manual page things. + + Update the Free Compilers Catalog entry. + diff --git a/Configure b/Configure new file mode 100755 index 0000000..87e16cd --- /dev/null +++ b/Configure @@ -0,0 +1,4822 @@ +#! /bin/sh +# +# If these # comments don't work, trim them. Don't worry about any other +# shell scripts, Configure will trim # comments from them for you. +# +# (If you are trying to port this package to a machine without sh, +# I would suggest you have a look at the prototypical config_h.SH file +# and edit it to reflect your system. Some packages may include samples +# of config.h for certain machines, so you might look for one of those.) +# +# Yes, you may rip this off to use in other distribution packages. This +# script belongs to the public domain and cannot be copyrighted. +# +# (Note: this Configure script was generated automatically. Rather than +# working with this copy of Configure, you may wish to get metaconfig. +# The dist-3.0 package (which contains metaconfig) was posted in +# comp.sources.misc and is available on CPAN under authors/id/RAM so +# you may fetch it yourself from your nearest archive site.) +# + +# $Id: Configure,v 1.1 2004-05-03 05:17:48 behdad Exp $ +# +# Generated on Thu Feb 19 19:32:00 CST 2004 [metaconfig 3.0 PL70] + +cat >/tmp/c1$$ <<EOF +ARGGGHHHH!!!!! + +SCO csh still thinks true is false. Write to SCO today and tell them that next +year Configure ought to "rm /bin/csh" unless they fix their blasted shell. :-) + +(Actually, Configure ought to just patch csh in place. Hmm. Hmmmmm. All +we'd have to do is go in and swap the && and || tokens, wherever they are.) + +[End of diatribe. We now return you to your regularly scheduled programming...] +EOF +cat >/tmp/c2$$ <<EOF + +OOPS! You naughty creature! You didn't run Configure with sh! +I will attempt to remedy the situation by running sh for you... +EOF + +true || cat /tmp/c1$$ /tmp/c2$$ +true || exec sh $0 $argv:q + +(exit $?0) || cat /tmp/c2$$ +(exit $?0) || exec sh $0 $argv:q +rm -f /tmp/c1$$ /tmp/c2$$ + +: compute my invocation name +me=$0 +case "$0" in +*/*) + me=`echo $0 | sed -e 's!.*/\(.*\)!\1!' 2>/dev/null` + test "$me" || me=$0 + ;; +esac + +: Proper PATH separator +p_=: +: On OS/2 this directory should exist if this is not floppy only system :-] +if test -d c:/.; then + p_=\; + PATH=`cmd /c "echo %PATH%" | tr '\\\\' / ` + OS2_SHELL=`cmd /c "echo %OS2_SHELL%" | tr '\\\\' / | tr '[A-Z]' '[a-z]'` +fi + +: Proper PATH setting +paths='/bin /usr/bin /usr/local/bin /usr/ucb /usr/local /usr/lbin' +paths="$paths /opt/bin /opt/local/bin /opt/local /opt/lbin" +paths="$paths /usr/5bin /etc /usr/gnu/bin /usr/new /usr/new/bin /usr/nbin" +paths="$paths /opt/gnu/bin /opt/new /opt/new/bin /opt/nbin" +paths="$paths /sys5.3/bin /sys5.3/usr/bin /bsd4.3/bin /bsd4.3/usr/ucb" +paths="$paths /bsd4.3/usr/bin /usr/bsd /bsd43/bin /usr/ccs/bin" +paths="$paths /etc /usr/lib /usr/ucblib /lib /usr/ccs/lib" +paths="$paths /sbin /usr/sbin /usr/libexec" + +for p in $paths +do + case "$p_$PATH$p_" in + *$p_$p$p_*) ;; + *) test -d $p && PATH=$PATH$p_$p ;; + esac +done + +PATH=.$p_$PATH +export PATH + +: shall we be using ksh? +inksh='' +needksh='' +avoidksh='' +newsh=/bin/ksh +changesh='' +if (PATH=.; alias -x) >/dev/null 2>&1; then + inksh=true +fi +if test -f /hp-ux -a -f /bin/ksh; then + needksh='to avoid sh bug in "here document" expansion' +fi +if test -d /usr/lpp -a -f /usr/bin/bsh -a -f /usr/bin/uname; then + if test X`/usr/bin/uname -v` = X4; then + avoidksh="to avoid AIX 4's /bin/sh" + newsh=/usr/bin/bsh + fi +fi +case "$inksh/$needksh" in +/[a-z]*) + unset ENV + changesh=true + reason="$needksh" + ;; +esac +case "$inksh/$avoidksh" in +true/[a-z]*) + changesh=true + reason="$avoidksh" + ;; +esac +case "$inksh/$needksh-$avoidksh-" in +true/--) + cat <<EOM +(I see you are using the Korn shell. Some ksh's blow up on $me, +mainly on older exotic systems. If yours does, try the Bourne shell instead.) +EOM + ;; +esac +case "$changesh" in +true) + echo "(Feeding myself to $newsh $reason.)" + case "$0" in + Configure|*/Configure) exec $newsh $0 "$@";; + *) exec $newsh Configure "$@";; + esac + ;; +esac + +: Configure runs within the UU subdirectory +test -d UU || mkdir UU +unset CDPATH +cd UU && rm -f ./* + +d_bsd='' +d_eunice='' +d_xenix='' +eunicefix='' +Mcc='' +ar='' +awk='' +bash='' +bison='' +byacc='' +cat='' +chgrp='' +chmod='' +chown='' +comm='' +compress='' +cp='' +cpio='' +cpp='' +csh='' +date='' +echo='' +egrep='' +emacs='' +expr='' +find='' +flex='' +gcc='' +grep='' +gzip='' +inews='' +ksh='' +less='' +line='' +lint='' +ln='' +lp='' +lpr='' +ls='' +mail='' +mailx='' +make='' +mkdir='' +more='' +mv='' +nroff='' +perl='' +pg='' +pmake='' +pr='' +rm='' +rmail='' +sed='' +sendmail='' +shar='' +sleep='' +smail='' +sort='' +submit='' +tail='' +tar='' +tbl='' +test='' +touch='' +tr='' +troff='' +uname='' +uniq='' +uuname='' +vi='' +zcat='' +zip='' +hint='' +myuname='' +osname='' +osvers='' +Author='' +Date='' +Header='' +Id='' +Locker='' +Log='' +RCSfile='' +Revision='' +Source='' +State='' +archobjs='' +firstmakefile='' +afs='' +bin='' +binexp='' +installbin='' +cc='' +gccversion='' +ccflags='' +cppflags='' +ldflags='' +lkflags='' +locincpth='' +optimize='' +cf_by='' +cf_time='' +contains='' +cppfilecom='' +cppstdinflags='' +d_cppcanstdin='' +d_cppignhdrs='' +cpplast='' +cppminus='' +cpprun='' +cppstdin='' +d_access='' +d_attribut='' +d_const='' +d_flexfnam='' +d_gnulibc='' +d_link='' +d_open3='' +d_portable='' +d_index='' +d_strchr='' +d_strftime='' +d_strstr='' +d_symlink='' +d_time='' +timetype='' +d_voidsig='' +signal_t='' +d_volatile='' +h_fcntl='' +h_sysfile='' +i_fcntl='' +i_stddef='' +i_stdlib='' +i_string='' +strings='' +i_sysfile='' +i_systypes='' +i_systime='' +i_systimek='' +i_time='' +timeincl='' +i_unistd='' +i_stdarg='' +i_varargs='' +i_varhdr='' +lex='' +lexflags='' +libc='' +glibpth='' +libpth='' +loclibpth='' +plibpth='' +xlibpth='' +libs='' +libyacc='' +lns='' +installmansrc='' +manext='' +mansrc='' +mansrcexp='' +mkdep='' +c='' +n='' +package='' +spackage='' +prefix='' +prefixexp='' +installprivlib='' +privlib='' +privlibexp='' +prototype='' +sh='' +so='' +sharpbang='' +shsharp='' +spitshell='' +src='' +startsh='' +sysman='' +nm_opt='' +nm_so_opt='' +runnm='' +usenm='' +incpath='' +mips='' +mips_type='' +usrinc='' +vaproto='' +defvoidused='' +voidflags='' +warnflags='' +yacc='' +yaccflags='' +CONFIG='' + +define='define' +undef='undef' +smallmach='pdp11 i8086 z8000 i80286 iAPX286' +rmlist='' + +: We must find out about Eunice early +eunicefix=':' +if test -f /etc/unixtovms; then + eunicefix=/etc/unixtovms +fi +if test -f /etc/unixtovms.exe; then + eunicefix=/etc/unixtovms.exe +fi + +: No trailing extension on UNIX executables +_exe='' +: Extra object files, if any, needed on this platform. +archobjs='' +gccversion='' +: Possible local include directories to search. +: Set locincpth to "" in a hint file to defeat local include searches. +locincpth="/usr/local/include /opt/local/include /usr/gnu/include" +locincpth="$locincpth /opt/gnu/include /usr/GNU/include /opt/GNU/include" +: +: no include file wanted by default +inclwanted='' + +i_sysselct='' +: change the next line if compiling for Xenix/286 on Xenix/386 +xlibpth='/usr/lib/386 /lib/386' + +: Possible local library directories to search. +loclibpth="/usr/local/lib /opt/local/lib /usr/gnu/lib" +loclibpth="$loclibpth /opt/gnu/lib /usr/GNU/lib /opt/GNU/lib" + +: general looking path for locating libraries +glibpth="/shlib /usr/shlib /lib/pa1.1 /usr/lib/large" +glibpth="$glibpth /lib /usr/lib $xlibpth" +glibpth="$glibpth /lib/large /usr/lib/small /lib/small" +glibpth="$glibpth /usr/ccs/lib /usr/ucblib /usr/local/lib" + +: Private path used by Configure to find libraries. Its value +: is prepended to libpth. This variable takes care of special +: machines, like the mips. Usually, it should be empty. +plibpth='' + +: default library list +libswanted='' +large='' +: full support for void wanted by default +defvoidused=15 + + +: Find the basic shell for Bourne shell scripts +case "$sh" in +'') + case "$SYSTYPE" in + *bsd*|sys5*) xxx="/$SYSTYPE/bin/sh";; + *) xxx='/bin/sh';; + esac + if test -f "$xxx"; then + sh="$xxx" + else + : Build up a list and do a single loop so we can 'break' out. + pth=`echo $PATH | sed -e "s/$p_/ /g"` + for xxx in sh bash ksh pdksh ash; do + for p in $pth; do + try="$try ${p}/${xxx}" + done + done + for xxx in $try; do + if test -f "$xxx"; then + sh="$xxx"; + break + elif test -f "$xxx.exe"; then + sh="$xxx"; + break + fi + done + fi + ;; +esac + +case "$sh" in +'') cat <<EOM >&2 +$me: Fatal Error: I can't find a Bourne Shell anywhere. + +Usually it's in /bin/sh. How did you even get this far? +Please contact me (Manoj Srivastava) at srivasta@debian.org and +we'll try to straighten this all out. +EOM + exit 1 + ;; +esac + +: see if sh knows # comments +if `$sh -c '#' >/dev/null 2>&1`; then + shsharp=true + spitshell=cat + xcat=/bin/cat + test -f $xcat || xcat=/usr/bin/cat + echo "#!$xcat" >try + $eunicefix try + chmod +x try + ./try > today + if test -s today; then + sharpbang='#!' + else + echo "#! $xcat" > try + $eunicefix try + chmod +x try + ./try > today + if test -s today; then + sharpbang='#! ' + else + sharpbang=': use ' + fi + fi +else + echo " " + echo "Your $sh doesn't grok # comments--I will strip them later on." + shsharp=false + cd .. + echo "exec grep -v '^[ ]*#'" >spitshell + chmod +x spitshell + $eunicefix spitshell + spitshell=`pwd`/spitshell + cd UU + echo "I presume that if # doesn't work, #! won't work either!" + sharpbang=': use ' +fi +rm -f try today + +: figure out how to guarantee sh startup +case "$startsh" in +'') startsh=${sharpbang}${sh} ;; +*) +esac +cat >try <<EOSS +$startsh +set abc +test "$?abc" != 1 +EOSS + +chmod +x try +$eunicefix try +if ./try; then + : echo "Yup, it does." +else + echo "Hmm... '$startsh' does not guarantee sh startup..." + echo "You may have to fix up the shell scripts to make sure $sh runs them." +fi +rm -f try + +: produce awk script to parse command line options +cat >options.awk <<'EOF' +BEGIN { + optstr = "dD:eEf:hKOrsSU:V"; # getopt-style specification + + len = length(optstr); + for (i = 1; i <= len; i++) { + c = substr(optstr, i, 1); + if (i < len) a = substr(optstr, i + 1, 1); else a = ""; + if (a == ":") { + arg[c] = 1; + i++; + } + opt[c] = 1; + } +} +{ + expect = 0; + str = $0; + if (substr(str, 1, 1) != "-") { + printf("'%s'\n", str); + next; + } + len = length($0); + for (i = 2; i <= len; i++) { + c = substr(str, i, 1); + if (!opt[c]) { + printf("-%s\n", substr(str, i)); + next; + } + printf("-%s\n", c); + if (arg[c]) { + if (i < len) + printf("'%s'\n", substr(str, i + 1)); + else + expect = 1; + next; + } + } +} +END { + if (expect) + print "?"; +} +EOF + +: process the command line options +set X `for arg in "$@"; do echo "X$arg"; done | + sed -e s/X// | awk -f options.awk` +eval "set $*" +shift +rm -f options.awk + +: set up default values +fastread='' +reuseval=false +config_sh='' +alldone='' +error='' +silent='' +extractsh='' +override='' +knowitall='' +rm -f optdef.sh +cat >optdef.sh <<EOS +$startsh +EOS + + +: option parsing +while test $# -gt 0; do + case "$1" in + -d) shift; fastread=yes;; + -e) shift; alldone=cont;; + -f) + shift + cd .. + if test -r "$1"; then + config_sh="$1" + else + echo "$me: cannot read config file $1." >&2 + error=true + fi + cd UU + shift;; + -h) shift; error=true;; + -r) shift; reuseval=true;; + -s) shift; silent=true; realsilent=true;; + -E) shift; alldone=exit;; + -K) shift; knowitall=true;; + -O) shift; override=true;; + -S) shift; silent=true; extractsh=true;; + -D) + shift + case "$1" in + *=) + echo "$me: use '-U symbol=', not '-D symbol='." >&2 + echo "$me: ignoring -D $1" >&2 + ;; + *=*) echo "$1" | \ + sed -e "s/'/'\"'\"'/g" -e "s/=\(.*\)/='\1'/" >> optdef.sh;; + *) echo "$1='define'" >> optdef.sh;; + esac + shift + ;; + -U) + shift + case "$1" in + *=) echo "$1" >> optdef.sh;; + *=*) + echo "$me: use '-D symbol=val', not '-U symbol=val'." >&2 + echo "$me: ignoring -U $1" >&2 + ;; + *) echo "$1='undef'" >> optdef.sh;; + esac + shift + ;; + -V) echo "$me generated by metaconfig 3.0 PL70." >&2 + exit 0;; + --) break;; + -*) echo "$me: unknown option $1" >&2; shift; error=true;; + *) break;; + esac +done + +case "$error" in +true) + cat >&2 <<EOM +Usage: $me [-dehrsEKOSV] [-f config.sh] [-D symbol] [-D symbol=value] + [-U symbol] [-U symbol=] + -d : use defaults for all answers. + -e : go on without questioning past the production of config.sh. + -f : specify an alternate default configuration file. + -h : print this help message and exit (with an error status). + -r : reuse C symbols value if possible (skips costly nm extraction). + -s : silent mode, only echoes questions and essential information. + -D : define symbol to have some value: + -D symbol symbol gets the value 'define' + -D symbol=value symbol gets the value 'value' + -E : stop at the end of questions, after having produced config.sh. + -K : do not use unless you know what you are doing. + -O : let -D and -U override definitions from loaded configuration file. + -S : perform variable substitutions on all .SH files (can mix with -f) + -U : undefine symbol: + -U symbol symbol gets the value 'undef' + -U symbol= symbol gets completely empty + -V : print version number and exit (with a zero status). +EOM + exit 1 + ;; +esac + +: Sanity checks +case "$fastread$alldone" in +yescont|yesexit) ;; +*) + if test ! -t 0; then + echo "Say 'sh Configure', not 'sh <Configure'" + exit 1 + fi + ;; +esac + +exec 4>&1 +case "$silent" in +true) exec 1>/dev/null;; +esac + +: run the defines and the undefines, if any, but leave the file out there... +touch optdef.sh +. ./optdef.sh + +: set package name +package=c2man +first=`echo $package | sed -e 's/^\(.\).*/\1/'` +last=`echo $package | sed -e 's/^.\(.*\)/\1/'` +case "`echo AbyZ | tr '[:lower:]' '[:upper:]' 2>/dev/null`" in +ABYZ) spackage=`echo $first | tr '[:lower:]' '[:upper:]'`$last;; +*) spackage=`echo $first | tr '[a-z]' '[A-Z]'`$last;; +esac + +: Some greps do not return status, grrr. +echo "grimblepritz" >grimble +if grep blurfldyick grimble >/dev/null 2>&1 ; then + contains=contains +elif grep grimblepritz grimble >/dev/null 2>&1 ; then + contains=grep +else + contains=contains +fi +rm -f grimble +: the following should work in any shell +case "$contains" in +contains*) + echo " " + echo "AGH! Grep doesn't return a status. Attempting remedial action." + cat >contains <<'EOSS' +grep "$1" "$2" >.greptmp && cat .greptmp && test -s .greptmp +EOSS +chmod +x contains +esac + +: first determine how to suppress newline on echo command +echo " " +echo "Checking echo to see how to suppress newlines..." +(echo "hi there\c" ; echo " ") >.echotmp +if $contains c .echotmp >/dev/null 2>&1 ; then + echo "...using -n." + n='-n' + c='' +else + cat <<'EOM' +...using \c +EOM + n='' + c='\c' +fi +echo $n "The star should be here-->$c" +echo '*' +rm -f .echotmp + +: compute the number of columns on the terminal for proper question formatting +case "$COLUMNS" in +'') COLUMNS='80';; +esac + +: set up the echo used in my read +myecho="case \"\$xxxm\" in +'') echo $n \"\$rp $c\" >&4;; +*) case \"\$rp\" in + '') echo $n \"[\$xxxm] $c\";; + *) + if test \`echo \"\$rp [\$xxxm] \" | wc -c\` -ge $COLUMNS; then + echo \"\$rp\" >&4 + echo $n \"[\$xxxm] $c\" >&4 + else + echo $n \"\$rp [\$xxxm] $c\" >&4 + fi + ;; + esac;; +esac" + +: now set up to do reads with possible shell escape and default assignment +cat <<EOSC >myread +$startsh +xxxm=\$dflt +$myecho +ans='!' +case "\$fastread" in +yes) case "\$dflt" in + '') ;; + *) ans=''; + case "\$silent-\$rp" in + true-) ;; + *) echo " " >&4;; + esac;; + esac;; +*) case "\$silent" in + true) case "\$rp" in + '') ans='';; + esac;; + esac;; +esac +while expr "X\$ans" : "X!" >/dev/null; do + read answ + set x \$xxxm + shift + aok=''; eval "ans=\\"\$answ\\"" && aok=y + case "\$answ" in + "!") + sh 1>&4 + echo " " + $myecho + ;; + !*) + set x \`expr "X\$ans" : "X!\(.*\)\$"\` + shift + sh 1>&4 -c "\$*" + echo " " + $myecho + ;; + "\$ans") + case "\$ans" in + \\&*) + set x \`expr "X\$ans" : "X&\(.*\)\$"\` + shift + case "\$1" in + -d) + fastread=yes + echo "(OK, I'll run with -d after this question.)" >&4 + ;; + -*) + echo "*** Sorry, \$1 not supported yet." >&4 + ;; + esac + $myecho + ans=! + ;; + esac;; + *) + case "\$aok" in + y) + echo "*** Substitution done -- please confirm." + xxxm="\$ans" + ans=\`echo $n "\$ans$c" | tr '\012' ' '\` + xxxm="\$ans" + ans=! + ;; + *) + echo "*** Error -- try again." + ans=! + ;; + esac + $myecho + ;; + esac + case "\$ans\$xxxm\$nostick" in + '') + ans=! + $myecho + ;; + esac +done +case "\$ans" in +'') ans="\$xxxm";; +esac +EOSC + +: Find the path to the source tree +case "$src" in +'') src=`echo $0 | sed -e 's%/[^/][^/]*$%%'`;; +esac +case "$src" in +'') + src=. + rsrc=.. + ;; +/*) rsrc="$src/..";; +*) rsrc="../$src";; +esac +if test -f $rsrc/Configure && \ + $contains "^package=$package" $rsrc/Configure >/dev/null 2>&1 +then + : found it, so we are ok. +else + rsrc='' + for src in . .. ../.. ../../.. ../../../..; do + if test -f ../$src/Configure && \ + $contains "^package=$package" ../$src/Configure >/dev/null 2>&1 + then + rsrc=../$src + break + fi + done +fi +case "$rsrc" in +'') + echo " " + dflt= + rp="Directory where sources for $package are located?" + . ./myread + src="$ans" + rsrc="$src" + if test -f $rsrc/Configure && \ + $contains "^package=$package" $rsrc/Configure >/dev/null 2>&1 + then + echo "Ok, I've found them under $src" + else + echo "Sorry, I can't seem to be able to locate $package sources." >&4 + exit 1 + fi + ;; +../.) ;; +*) + echo " " + echo "Sources for $package found in $src" >&4 + ;; +esac + +: script used to extract .SH files with variable substitutions +cat >extract <<'EOS' +CONFIG=true +echo "Doing variable substitutions on .SH files..." +if test -f $src/MANIFEST; then + set x `awk '{print $1}' <$src/MANIFEST | grep '\.SH'` +else + echo "(Looking for .SH files under the source directory.)" + set x `(cd $src; find . -name "*.SH" -print)` +fi +shift +case $# in +0) set x `(cd $src; echo *.SH)`; shift;; +esac +if test ! -f $src/$1; then + shift +fi +mkdir_p=' +name=$1; +create=""; +while test $name; do + if test ! -d "$name"; then + create="$name $create"; + name=`echo $name | sed -e "s|^[^/]*$||"`; + name=`echo $name | sed -e "s|\(.*\)/.*|\1|"`; + else + name=""; + fi; +done; +for file in $create; do + mkdir $file; +done +' +for file in $*; do + case "$src" in + ".") + case "$file" in + */*) + dir=`expr X$file : 'X\(.*\)/'` + file=`expr X$file : 'X.*/\(.*\)'` + (cd $dir && . ./$file) + ;; + *) + . ./$file + ;; + esac + ;; + *) + case "$file" in + */*) + dir=`expr X$file : 'X\(.*\)/'` + file=`expr X$file : 'X.*/\(.*\)'` + (set x $dir; shift; eval $mkdir_p) + sh <$src/$dir/$file + ;; + *) + sh <$src/$file + ;; + esac + ;; + esac +done +if test -f $src/config_h.SH; then + if test ! -f config.h; then + : oops, they left it out of MANIFEST, probably, so do it anyway. + . $src/config_h.SH + fi +fi +EOS + +: extract files and exit if asked to do so +case "$extractsh" in +true) + case "$realsilent" in + true) ;; + *) exec 1>&4;; + esac + case "$config_sh" in + '') config_sh='config.sh'; config="$rsrc/config.sh";; + /*) config="$config_sh";; + *) config="$rsrc/$config_sh";; + esac + echo " " + echo "Fetching answers from $config_sh..." + . $config + test "$override" && . ./optdef.sh + echo " " + cd .. + . UU/extract + rm -rf UU + echo "Done." + exit 0 + ;; +esac + +: Eunice requires " " instead of "", can you believe it +echo " " +: Here we go... +echo "Beginning of configuration questions for $package." + +trap 'echo " "; test -d ../UU && rm -rf X $rmlist; exit 1' 1 2 3 15 + +: Now test for existence of everything in MANIFEST +echo " " +if test -f $rsrc/MANIFEST; then + echo "First let's make sure your kit is complete. Checking..." >&4 + awk '$1 !~ /PACK[A-Z]+/ {print $1}' $rsrc/MANIFEST | split -l -50 + rm -f missing + tmppwd=`pwd` + for filelist in x??; do + (cd $rsrc; ls `cat $tmppwd/$filelist` >/dev/null 2>>$tmppwd/missing) + done + if test -s missing; then + cat missing >&4 + cat >&4 <<'EOM' + +THIS PACKAGE SEEMS TO BE INCOMPLETE. + +You have the option of continuing the configuration process, despite the +distinct possibility that your kit is damaged, by typing 'y'es. If you +do, don't blame me if something goes wrong. I advise you to type 'n'o +and contact the author (srivasta@debian.org). + +EOM + echo $n "Continue? [n] $c" >&4 + read ans + case "$ans" in + y*) + echo "Continuing..." >&4 + rm -f missing + ;; + *) + echo "ABORTING..." >&4 + kill $$ + ;; + esac + else + echo "Looks good..." + fi +else + echo "There is no MANIFEST file. I hope your kit is complete !" +fi +rm -f missing x?? + +: create .config dir to save info across Configure sessions +test -d ../.config || mkdir ../.config +cat >../.config/README <<EOF +This directory created by Configure to save information that should +persist across sessions for $package. + +You may safely delete it if you wish. +EOF + +: general instructions +needman=true +firsttime=true +user=`(logname) 2>/dev/null` +case "$user" in +'') user=`whoami 2>&1`;; +esac +if $contains "^$user\$" ../.config/instruct >/dev/null 2>&1; then + firsttime=false + echo " " + rp='Would you like to see the instructions?' + dflt=n + . ./myread + case "$ans" in + [yY]*) ;; + *) needman=false;; + esac +fi +if $needman; then + cat <<EOH + +This installation shell script will examine your system and ask you questions +to determine how the c2man package should be installed. If you get +stuck on a question, you may use a ! shell escape to start a subshell or +execute a command. Many of the questions will have default answers in square +brackets; typing carriage return will give you the default. + +On some of the questions which ask for file or directory names you are allowed +to use the ~name construct to specify the login directory belonging to "name", +even if you don't have a shell which knows about that. Questions where this is +allowed will be marked "(~name ok)". + +EOH + rp='' + dflt='Type carriage return to continue' + . ./myread + cat <<'EOH' + +The prompter used in this script allows you to use shell variables and +backticks in your answers. You may use $1, $2, etc... to refer to the words +in the default answer, as if the default line was a set of arguments given to a +script shell. This means you may also use $* to repeat the whole default line, +so you do not have to re-type everything to add something to the default. + +Everytime there is a substitution, you will have to confirm. If there is an +error (e.g. an unmatched backtick), the default answer will remain unchanged +and you will be prompted again. + +If you are in a hurry, you may run 'Configure -d'. This will bypass nearly all +the questions and use the computed defaults (or the previous answers if there +was already a config.sh file). Type 'Configure -h' for a list of options. +You may also start interactively and then answer '& -d' at any prompt to turn +on the non-interactive behaviour for the remaining of the execution. + +EOH + . ./myread + cat <<EOH + +Much effort has been expended to ensure that this shell script will run on any +Unix system. If despite that it blows up on yours, your best bet is to edit +Configure and run it again. If you can't run Configure for some reason, +you'll have to generate a config.sh file by hand. Whatever problems you +have, let me (srivasta@debian.org) know how I blew it. + +This installation script affects things in two ways: + +1) it may do direct variable substitutions on some of the files included + in this kit. +2) it builds a config.h file for inclusion in C programs. You may edit + any of these files as the need arises after running this script. + +If you make a mistake on a question, there is no easy way to back up to it +currently. The easiest thing to do is to edit config.sh and rerun all the SH +files. Configure will offer to let you do this before it runs the SH files. + +EOH + dflt='Type carriage return to continue' + . ./myread + case "$firsttime" in + true) echo $user >>../.config/instruct;; + esac +fi + +: find out where common programs are +echo " " +echo "Locating common programs..." >&4 +cat <<EOSC >loc +$startsh +case \$# in +0) exit 1;; +esac +thing=\$1 +shift +dflt=\$1 +shift +for dir in \$*; do + case "\$thing" in + .) + if test -d \$dir/\$thing; then + echo \$dir + exit 0 + fi + ;; + *) + for thisthing in \$dir/\$thing; do + : just loop through to pick last item + done + if test -f \$thisthing; then + echo \$thisthing + exit 0 + elif test -f \$dir/\$thing.exe; then + : on Eunice apparently + echo \$dir/\$thing + exit 0 + fi + ;; + esac +done +echo \$dflt +exit 1 +EOSC +chmod +x loc +$eunicefix loc +loclist=" +awk +cat +cp +echo +expr +grep +mv +rm +sed +sort +touch +tr +uniq +" +trylist=" +Mcc +bison +byacc +cpp +date +flex +ln +nroff +test +uname +" +pth=`echo $PATH | sed -e "s/$p_/ /g"` +pth="$pth /lib /usr/lib" +for file in $loclist; do + eval xxx=\$$file + case "$xxx" in + /*|?:[\\/]*) + if test -f "$xxx"; then + : ok + else + echo "WARNING: no $xxx -- ignoring your setting for $file." >&4 + xxx=`./loc $file $file $pth` + fi + ;; + '') xxx=`./loc $file $file $pth`;; + *) xxx=`./loc $xxx $xxx $pth`;; + esac + eval $file=$xxx + eval _$file=$xxx + case "$xxx" in + /*) + echo $file is in $xxx. + ;; + ?:[\\/]*) + echo $file is in $xxx. + ;; + *) + echo "I don't know where '$file' is, and my life depends on it." >&4 + echo "Go find a public domain implementation or fix your PATH setting!" >&4 + exit 1 + ;; + esac +done +echo " " +echo "Don't worry if any of the following aren't found..." +say=offhand +for file in $trylist; do + eval xxx=\$$file + case "$xxx" in + /*|?:[\\/]*) + if test -f "$xxx"; then + : ok + else + echo "WARNING: no $xxx -- ignoring your setting for $file." >&4 + xxx=`./loc $file $file $pth` + fi + ;; + '') xxx=`./loc $file $file $pth`;; + *) xxx=`./loc $xxx $xxx $pth`;; + esac + eval $file=$xxx + eval _$file=$xxx + case "$xxx" in + /*) + echo $file is in $xxx. + ;; + ?:[\\/]*) + echo $file is in $xxx. + ;; + *) + echo "I don't see $file out there, $say." + say=either + ;; + esac +done +case "$egrep" in +egrep) + echo "Substituting grep for egrep." + egrep=$grep + ;; +esac +case "$ln" in +ln) + echo "Substituting cp for ln." + ln=$cp + ;; +esac +case "$test" in +test) + echo "Hopefully test is built into your sh." + ;; +*) + if `sh -c "PATH= test true" >/dev/null 2>&1`; then + echo "Using the test built into your sh." + test=test + _test=test + fi + ;; +esac +case "$echo" in +echo) + echo "Hopefully echo is built into your sh." + ;; +'') ;; +*) + echo " " +echo "Checking compatibility between $echo and builtin echo (if any)..." >&4 + $echo $n "hi there$c" >foo1 + echo $n "hi there$c" >foo2 + if cmp foo1 foo2 >/dev/null 2>&1; then + echo "They are compatible. In fact, they may be identical." + else + case "$n" in + '-n') n='' c='\c';; + *) n='-n' c='';; + esac + cat <<FOO +They are not compatible! You are probably running ksh on a non-USG system. +I'll have to use $echo instead of the builtin, since Bourne shell doesn't +have echo built in and we may have to run some Bourne shell scripts. That +means I'll have to use '$n$c' to suppress newlines now. Life is ridiculous. + +FOO + $echo $n "The star should be here-->$c" + $echo "*" + fi + $rm -f foo1 foo2 + ;; +esac + +: determine whether symbolic links are supported +echo " " +$touch blurfl +if $ln -s blurfl sym > /dev/null 2>&1 ; then + echo "Symbolic links are supported." >&4 + lns="$ln -s" +else + echo "Symbolic links are NOT supported." >&4 + lns="$ln" +fi +$rm -f blurfl sym + +: see whether [:lower:] and [:upper:] are supported character classes +echo " " +up='[A-Z]' +low='[a-z]' +case "`echo AbyZ | $tr '[:lower:]' '[:upper:]' 2>/dev/null`" in +ABYZ) + echo "Good, your tr supports [:lower:] and [:upper:] to convert case." >&4 + up='[:upper:]' + low='[:lower:]' + ;; +*) + echo "Your tr only supports [a-z] and [A-Z] to convert case." >&4 + ;; +esac +: set up the translation script tr, must be called with ./tr of course +cat >tr <<EOSC +$startsh +case "\$1\$2" in +'[A-Z][a-z]') exec $tr '$up' '$low';; +'[a-z][A-Z]') exec $tr '$low' '$up';; +esac +exec $tr "\$@" +EOSC +chmod +x tr +$eunicefix tr + +: Try to determine whether config.sh was made on this system +case "$config_sh" in +'') +myuname=`( ($uname -a) 2>/dev/null || hostname) 2>&1` +myuname=`echo $myuname | $sed -e 's/^[^=]*=//' -e 's/\///g' | \ + ./tr '[A-Z]' '[a-z]' | tr '\012' ' '` +newmyuname="$myuname" +dflt=n +case "$knowitall" in +'') + if test -f ../config.sh; then + if $contains myuname= ../config.sh >/dev/null 2>&1; then + eval "`grep myuname= ../config.sh`" + fi + if test "X$myuname" = "X$newmyuname"; then + dflt=y + fi + fi + ;; +*) dflt=y;; +esac + +: Get old answers, if there is a config file out there +hint=default +hintfile='' +if test -f ../config.sh; then + echo " " + rp="I see a config.sh file. Shall I use it to set the defaults?" + . ./myread + case "$ans" in + n*|N*) echo "OK, I'll ignore it.";; + *) echo "Fetching default answers from your old config.sh file..." >&4 + tmp_n="$n" + tmp_c="$c" + . ../config.sh + cp ../config.sh . + n="$tmp_n" + c="$tmp_c" + hint=previous + ;; + esac +fi +;; +*) + echo " " + echo "Fetching default answers from $config_sh..." >&4 + tmp_n="$n" + tmp_c="$c" + cd .. + cp $config_sh config.sh 2>/dev/null + chmod +w config.sh + . ./config.sh + cd UU + cp ../config.sh . + n="$tmp_n" + c="$tmp_c" + hint=previous + ;; +esac +test "$override" && . ./optdef.sh +myuname="$newmyuname" + +: Restore computed paths +for file in $loclist $trylist; do + eval $file="\$_$file" +done + +: who configured the system +cf_time=`$date 2>&1` +cf_by=`(logname) 2>/dev/null` +case "$cf_by" in +"") + cf_by=`(whoami) 2>/dev/null` + case "$cf_by" in + "") cf_by=unknown ;; + esac ;; +esac + +: decide how portable to be +case "$d_portable" in +"$define") dflt=y;; +*) dflt=n;; +esac +$cat <<'EOH' + +I can set things up so that your shell scripts and binaries are more portable, +at what may be a noticable cost in performance. In particular, if you +ask to be portable, the following happens: + + 1) Shell scripts will rely on the PATH variable rather than using + the paths derived above. + 2) ~username interpretations will be done at run time rather than + by Configure. + +EOH +rp="Do you expect to run these scripts and binaries on multiple machines?" +. ./myread +case "$ans" in + y*) d_portable="$define" + ;; + *) d_portable="$undef" ;; +esac + +: set up shell script to do ~ expansion +cat >filexp <<EOSS +$startsh +: expand filename +case "\$1" in + ~/*|~) + echo \$1 | $sed "s|~|\${HOME-\$LOGDIR}|" + ;; + ~*) + if $test -f /bin/csh; then + /bin/csh -f -c "glob \$1" + failed=\$? + echo "" + exit \$failed + else + name=\`$expr x\$1 : '..\([^/]*\)'\` + dir=\`$sed -n -e "/^\${name}:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\).*"'\$'"/\1/" -e p -e q -e '}' </etc/passwd\` + if $test ! -d "\$dir"; then + me=\`basename \$0\` + echo "\$me: can't locate home directory for: \$name" >&2 + exit 1 + fi + case "\$1" in + */*) + echo \$dir/\`$expr x\$1 : '..[^/]*/\(.*\)'\` + ;; + *) + echo \$dir + ;; + esac + fi + ;; +*) + echo \$1 + ;; +esac +EOSS +chmod +x filexp +$eunicefix filexp + +: now set up to get a file name +cat <<EOS >getfile +$startsh +EOS +cat <<'EOSC' >>getfile +tilde='' +fullpath='' +already='' +skip='' +none_ok='' +exp_file='' +nopath_ok='' +orig_rp="$rp" +orig_dflt="$dflt" + +case "$fn" in +*\(*) + expr $fn : '.*(\(.*\)).*' | tr ',' '\012' >getfile.ok + fn=`echo $fn | sed 's/(.*)//'` + ;; +esac + +case "$fn" in +*:*) + loc_file=`expr $fn : '.*:\(.*\)'` + fn=`expr $fn : '\(.*\):.*'` + ;; +esac + +case "$fn" in +*~*) tilde=true;; +esac +case "$fn" in +*/*) fullpath=true;; +esac +case "$fn" in +*+*) skip=true;; +esac +case "$fn" in +*n*) none_ok=true;; +esac +case "$fn" in +*e*) exp_file=true;; +esac +case "$fn" in +*p*) nopath_ok=true;; +esac + +case "$fn" in +*f*) type='File';; +*d*) type='Directory';; +*l*) type='Locate';; +esac + +what="$type" +case "$what" in +Locate) what='File';; +esac + +case "$exp_file" in +'') + case "$d_portable" in + "$define") ;; + *) exp_file=true;; + esac + ;; +esac + +cd .. +while test "$type"; do + redo='' + rp="$orig_rp" + dflt="$orig_dflt" + case "$tilde" in + true) rp="$rp (~name ok)";; + esac + . UU/myread + if test -f UU/getfile.ok && \ + $contains "^$ans\$" UU/getfile.ok >/dev/null 2>&1 + then + value="$ans" + ansexp="$ans" + break + fi + case "$ans" in + none) + value='' + ansexp='' + case "$none_ok" in + true) type='';; + esac + ;; + *) + case "$tilde" in + '') value="$ans" + ansexp="$ans";; + *) + value=`UU/filexp $ans` + case $? in + 0) + if test "$ans" != "$value"; then + echo "(That expands to $value on this system.)" + fi + ;; + *) value="$ans";; + esac + ansexp="$value" + case "$exp_file" in + '') value="$ans";; + esac + ;; + esac + case "$fullpath" in + true) + case "$ansexp" in + /*) value="$ansexp" ;; + *) + redo=true + case "$already" in + true) + echo "I shall only accept a full path name, as in /bin/ls." >&4 + echo "Use a ! shell escape if you wish to check pathnames." >&4 + ;; + *) + echo "Please give a full path name, starting with slash." >&4 + case "$tilde" in + true) + echo "Note that using ~name is ok provided it expands well." >&4 + already=true + ;; + esac + esac + ;; + esac + ;; + esac + case "$redo" in + '') + case "$type" in + File) + if test -f "$ansexp"; then + type='' + elif test -r "$ansexp" || (test -h "$ansexp") >/dev/null 2>&1 + then + echo "($value is not a plain file, but that's ok.)" + type='' + fi + ;; + Directory) + if test -d "$ansexp"; then + type='' + fi + ;; + Locate) + if test -d "$ansexp"; then + echo "(Looking for $loc_file in directory $value.)" + value="$value/$loc_file" + ansexp="$ansexp/$loc_file" + fi + if test -f "$ansexp"; then + type='' + fi + case "$nopath_ok" in + true) case "$value" in + */*) ;; + *) echo "Assuming $value will be in people's path." + type='' + ;; + esac + ;; + esac + ;; + esac + + case "$skip" in + true) type=''; + esac + + case "$type" in + '') ;; + *) + if test "$fastread" = yes; then + dflt=y + else + dflt=n + fi + rp="$what $value doesn't exist. Use that name anyway?" + . UU/myread + dflt='' + case "$ans" in + y*) type='';; + *) echo " ";; + esac + ;; + esac + ;; + esac + ;; + esac +done +cd UU +ans="$value" +rp="$orig_rp" +dflt="$orig_dflt" +rm -f getfile.ok +EOSC + +: determine root of directory hierarchy where package will be installed. +case "$prefix" in +'') + dflt=`./loc . /usr/local /usr/local /local /opt /usr` + ;; +*) + dflt="$prefix" + ;; +esac +$cat <<EOM + +By default, $package will be installed in $dflt/bin, manual +pages under $dflt/man, etc..., i.e. with $dflt as prefix for +all installation directories. Typically set to /usr/local, but you +may choose /usr if you wish to install $package among your system +binaries. If you wish to have binaries under /bin but manual pages +under /usr/local/man, that's ok: you will be prompted separately +for each of the installation directories, the prefix being only used +to set the defaults. + +EOM +fn=d~ +rp='Installation prefix to use?' +. ./getfile +oldprefix='' +case "$prefix" in +'') ;; +*) + case "$ans" in + "$prefix") ;; + *) oldprefix="$prefix";; + esac + ;; +esac +prefix="$ans" +prefixexp="$ansexp" + +: set the prefixit variable, to compute a suitable default value +prefixit='case "$3" in +""|none) + case "$oldprefix" in + "") eval "$1=\"\$$2\"";; + *) + case "$3" in + "") eval "$1=";; + none) + eval "tp=\"\$$2\""; + case "$tp" in + ""|" ") eval "$1=\"\$$2\"";; + *) eval "$1=";; + esac;; + esac;; + esac;; +*) + eval "tp=\"$oldprefix-\$$2-\""; eval "tp=\"$tp\""; + case "$tp" in + --|/*--|\~*--) eval "$1=\"$prefix/$3\"";; + /*-$oldprefix/*|\~*-$oldprefix/*) + eval "$1=\`echo \$$2 | sed \"s,^$oldprefix,$prefix,\"\`";; + *) eval "$1=\"\$$2\"";; + esac;; +esac' + +: is AFS running? +echo " " +case "$afs" in +$define|true) afs=true ;; +$undef|false) afs=false ;; +*) if test -d /afs; then + afs=true + else + afs=false + fi + ;; +esac +if $afs; then + echo "AFS may be running... I'll be extra cautious then..." >&4 +else + echo "AFS does not seem to be running..." >&4 +fi + +: determine where public executables go +echo " " +set dflt bin bin +eval $prefixit +fn=d~ +rp='Pathname where the public executables will reside?' +. ./getfile +if $test "X$ansexp" != "X$binexp"; then + installbin='' +fi +bin="$ans" +binexp="$ansexp" +if $afs; then + $cat <<EOM + +Since you are running AFS, I need to distinguish the directory in which +executables reside from the directory in which they are installed (and from +which they are presumably copied to the former directory by occult means). + +EOM + case "$installbin" in + '') dflt=`echo $binexp | sed 's#^/afs/#/afs/.#'`;; + *) dflt="$installbin";; + esac + fn=de~ + rp='Where will public executables be installed?' + . ./getfile + installbin="$ans" +else + installbin="$binexp" +fi + +: determine where private executables go +set dflt privlib lib/$package +eval $prefixit +$cat <<EOM + +There are some auxiliary files for $package that need to be put into a +private library directory that is accessible by everyone. + +EOM +fn=d~+ +rp='Pathname where the private library files will reside?' +. ./getfile +if $test "X$privlibexp" != "X$ansexp"; then + installprivlib='' +fi +privlib="$ans" +privlibexp="$ansexp" +if $afs; then + $cat <<EOM + +Since you are running AFS, I need to distinguish the directory in which +private files reside from the directory in which they are installed (and from +which they are presumably copied to the former directory by occult means). + +EOM + case "$installprivlib" in + '') dflt=`echo $privlibexp | sed 's#^/afs/#/afs/.#'`;; + *) dflt="$installprivlib";; + esac + fn=de~ + rp='Where will private files be installed?' + . ./getfile + installprivlib="$ans" +else + installprivlib="$privlibexp" +fi + +: determine where manual pages are on this system +echo " " +case "$sysman" in +'') + syspath='/usr/man/man1 /usr/man/mann /usr/man/manl /usr/man/local/man1' + syspath="$syspath /usr/man/u_man/man1 /usr/share/man/man1" + syspath="$syspath /usr/catman/u_man/man1 /usr/man/l_man/man1" + syspath="$syspath /usr/local/man/u_man/man1 /usr/local/man/l_man/man1" + syspath="$syspath /usr/man/man.L /local/man/man1 /usr/local/man/man1" + sysman=`./loc . /usr/man/man1 $syspath` + ;; +esac +if $test -d "$sysman"; then + echo "System manual is in $sysman." >&4 +else + echo "Could not find manual pages in source form." >&4 +fi + +: set the prefixup variable, to restore leading tilda escape +prefixup='case "$prefixexp" in +"$prefix") ;; +*) eval "$1=\`echo \$$1 | sed \"s,^$prefixexp,$prefix,\"\`";; +esac' + +: determine where manual pages go +set mansrc mansrc none +eval $prefixit +$cat <<EOM + +$spackage has manual pages available in source form. +EOM +case "$nroff" in +nroff) + echo "However, you don't have nroff, so they're probably useless to you." + case "$mansrc" in + '') mansrc="none";; + esac;; +esac +echo "If you don't want the manual sources installed, answer 'none'." +case "$mansrc" in +'') + lookpath="$prefixexp/man/man1 $prefixexp/man/u_man/man1" + lookpath="$lookpath $prefixexp/man/l_man/man1" + lookpath="$lookpath /usr/local/man/man1 /opt/man/man1 /usr/man/manl" + lookpath="$lookpath /usr/man/local/man1 /usr/man/l_man/man1" + lookpath="$lookpath /usr/local/man/u_man/man1 /usr/local/man/l_man/man1" + lookpath="$lookpath /usr/man/man.L" + mansrc=`./loc . $prefixexp/man/man1 $lookpath` + if $test -d "$mansrc"; then + dflt="$mansrc" + else + dflt="$sysman" + fi + set dflt + eval $prefixup + ;; +' ') dflt=none;; +*) dflt="$mansrc" + ;; +esac +echo " " +fn=dn~ +rp='Where do the manual pages (source) go?' +. ./getfile +if test "X$mansrcexp" != "X$ansexp"; then + installmansrc='' +fi +mansrc="$ans" +mansrcexp="$ansexp" +case "$mansrc" in +'') mansrc=' ' + installmansrc='';; +esac +if $afs && $test "$mansrc"; then + $cat <<EOM + +Since you are running AFS, I need to distinguish the directory in which +manual pages reside from the directory in which they are installed (and from +which they are presumably copied to the former directory by occult means). + +EOM + case "$installmansrc" in + '') dflt=`echo $mansrcexp | sed 's#^/afs/#/afs/.#'`;; + *) dflt="$installmansrc";; + esac + fn=de~ + rp='Where will man pages be installed?' + . ./getfile + installmansrc="$ans" +else + installmansrc="$mansrcexp" +fi + +case "$mansrc" in +' ') manext='0';; +*l) manext=l;; +*n) manext=n;; +*o) manext=l;; +*p) manext=n;; +*C) manext=C;; +*L) manext=L;; +*L1) manext=L1;; +*) manext=1;; +esac + +: make some quick guesses about what we are up against +echo " " +$echo $n "Hmm... $c" +echo exit 1 >bsd +echo exit 1 >usg +echo exit 1 >v7 +echo exit 1 >osf1 +echo exit 1 >eunice +echo exit 1 >xenix +echo exit 1 >venix +echo exit 1 >os2 +d_bsd="$undef" +$cat /usr/include/signal.h /usr/include/sys/signal.h >foo 2>/dev/null +if test -f /osf_boot || $contains 'OSF/1' /usr/include/ctype.h >/dev/null 2>&1 +then + echo "Looks kind of like an OSF/1 system, but we'll see..." + echo exit 0 >osf1 +elif test `echo abc | tr a-z A-Z` = Abc ; then + xxx=`./loc addbib blurfl $pth` + if $test -f $xxx; then + echo "Looks kind of like a USG system with BSD features, but we'll see..." + echo exit 0 >bsd + echo exit 0 >usg + else + if $contains SIGTSTP foo >/dev/null 2>&1 ; then + echo "Looks kind of like an extended USG system, but we'll see..." + else + echo "Looks kind of like a USG system, but we'll see..." + fi + echo exit 0 >usg + fi +elif $contains SIGTSTP foo >/dev/null 2>&1 ; then + echo "Looks kind of like a BSD system, but we'll see..." + d_bsd="$define" + echo exit 0 >bsd +else + echo "Looks kind of like a Version 7 system, but we'll see..." + echo exit 0 >v7 +fi +case "$eunicefix" in +*unixtovms*) + $cat <<'EOI' +There is, however, a strange, musty smell in the air that reminds me of +something...hmm...yes...I've got it...there's a VMS nearby, or I'm a Blit. +EOI + echo exit 0 >eunice + d_eunice="$define" +: it so happens the Eunice I know will not run shell scripts in Unix format + ;; +*) + echo " " + echo "Congratulations. You aren't running Eunice." + d_eunice="$undef" + ;; +esac +case "$p_" in +:) ;; +*) + $cat <<'EOI' +I have the feeling something is not exactly right, however...don't tell me... +lemme think...does HAL ring a bell?...no, of course, you're only running OS/2! +EOI + echo exit 0 >os2 + ;; +esac +if test -f /xenix; then + echo "Actually, this looks more like a XENIX system..." + echo exit 0 >xenix + d_xenix="$define" +else + echo " " + echo "It's not Xenix..." + d_xenix="$undef" +fi +chmod +x xenix +$eunicefix xenix +if test -f /venix; then + echo "Actually, this looks more like a VENIX system..." + echo exit 0 >venix +else + echo " " + if ./xenix; then + : null + else + echo "Nor is it Venix..." + fi +fi +chmod +x bsd usg v7 osf1 eunice xenix venix os2 +$eunicefix bsd usg v7 osf1 eunice xenix venix os2 +$rm -f foo + +: see if we need a special compiler +echo " " +if ./usg; then + case "$cc" in + '') case "$Mcc" in + /*) dflt='Mcc';; + *) case "$large" in + -M*) dflt='cc';; + *) if $contains '\-M' $sysman/cc.1 >/dev/null 2>&1 ; then + if $contains '\-M' $sysman/cpp.1 >/dev/null 2>&1; then + dflt='cc' + else + dflt='cc -M' + fi + else + dflt='cc' + fi;; + esac;; + esac;; + *) dflt="$cc";; + esac + $cat <<'EOM' +On some systems the default C compiler will not resolve multiple global +references that happen to have the same name. On some such systems the "Mcc" +command may be used to force these to be resolved. On other systems a "cc -M" +command is required. (Note that the -M flag on other systems indicates a +memory model to use!) If you have the Gnu C compiler, you might wish to use +that instead. + +EOM + rp="What command will force resolution on this system?" + . ./myread + cc="$ans" +else + case "$cc" in + '') dflt=cc;; + *) dflt="$cc";; + esac + rp="Use which C compiler?" + . ./myread + cc="$ans" +fi +echo " " +echo "Checking for GNU cc in disguise and/or its version number..." >&4 +$cat >gccvers.c <<EOM +#include <stdio.h> +int main() { +#ifdef __GNUC__ +#ifdef __VERSION__ + printf("%s\n", __VERSION__); +#else + printf("%s\n", "1"); +#endif +#endif + exit(0); +} +EOM +if $cc -o gccvers gccvers.c >/dev/null 2>&1; then + gccversion=`./gccvers` + case "$gccversion" in + '') echo "You are not using GNU cc." ;; + *) echo "You are using GNU cc $gccversion." ;; + esac +else + echo " " + echo "*** WHOA THERE!!! ***" >&4 + echo " Your C compiler \"$cc\" doesn't seem to be working!" >&4 + case "$knowitall" in + '') + echo " You'd better start hunting for one and let me know about it." >&4 + exit 1 + ;; + esac +fi +$rm -f gccvers* +case "$gccversion" in +1*) cpp=`./loc gcc-cpp $cpp $pth` ;; +esac + +: What should the include directory be ? +echo " " +$echo $n "Hmm... $c" +dflt='/usr/include' +incpath='' +mips_type='' +if $test -f /bin/mips && /bin/mips; then + echo "Looks like a MIPS system..." + $cat >usr.c <<'EOCP' +#ifdef SYSTYPE_BSD43 +/bsd43 +#endif +EOCP + if $cc -E usr.c > usr.out && $contains / usr.out >/dev/null 2>&1; then + dflt='/bsd43/usr/include' + incpath='/bsd43' + mips_type='BSD 4.3' + else + mips_type='System V' + fi + $rm -f usr.c usr.out + echo "and you're compiling with the $mips_type compiler and libraries." + xxx_prompt=y + echo "exit 0" >mips +else + echo "Doesn't look like a MIPS system." + xxx_prompt=n + echo "exit 1" >mips +fi +chmod +x mips +$eunicefix mips +case "$usrinc" in +'') ;; +*) dflt="$usrinc";; +esac +case "$xxx_prompt" in +y) fn=d/ + echo " " + rp='Where are the include files you want to use?' + . ./getfile + usrinc="$ans" + ;; +*) usrinc="$dflt" + ;; +esac + +: see how we invoke the C preprocessor +echo " " +echo "Now, how can we feed standard input to your C preprocessor..." >&4 +cat <<'EOT' >testcpp.c +#define ABC abc +#define XYZ xyz +ABC.XYZ +EOT +cd .. +echo 'cat >.$$.c; '"$cc"' -E ${1+"$@"} .$$.c; rm .$$.c' >cppstdin +chmod 755 cppstdin +wrapper=`pwd`/cppstdin +ok='false' +cd UU + +if $test "X$cppstdin" != "X" && \ + $cppstdin $cppminus <testcpp.c >testcpp.out 2>&1 && \ + $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 +then + echo "You used to use $cppstdin $cppminus so we'll use that again." + case "$cpprun" in + '') echo "But let's see if we can live without a wrapper..." ;; + *) + if $cpprun $cpplast <testcpp.c >testcpp.out 2>&1 && \ + $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 + then + echo "(And we'll use $cpprun $cpplast to preprocess directly.)" + ok='true' + else + echo "(However, $cpprun $cpplast does not work, let's see...)" + fi + ;; + esac +else + case "$cppstdin" in + '') ;; + *) + echo "Good old $cppstdin $cppminus does not seem to be of any help..." + ;; + esac +fi + +if $ok; then + : nothing +elif echo 'Maybe "'"$cc"' -E" will work...'; \ + $cc -E <testcpp.c >testcpp.out 2>&1; \ + $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "Yup, it does." + x_cpp="$cc -E" + x_minus=''; +elif echo 'Nope...maybe "'"$cc"' -E -" will work...'; \ + $cc -E - <testcpp.c >testcpp.out 2>&1; \ + $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "Yup, it does." + x_cpp="$cc -E" + x_minus='-'; +elif echo 'Nope...maybe "'"$cc"' -P" will work...'; \ + $cc -P <testcpp.c >testcpp.out 2>&1; \ + $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "Yipee, that works!" + x_cpp="$cc -P" + x_minus=''; +elif echo 'Nope...maybe "'"$cc"' -P -" will work...'; \ + $cc -P - <testcpp.c >testcpp.out 2>&1; \ + $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "At long last!" + x_cpp="$cc -P" + x_minus='-'; +elif echo 'No such luck, maybe "'$cpp'" will work...'; \ + $cpp <testcpp.c >testcpp.out 2>&1; \ + $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "It works!" + x_cpp="$cpp" + x_minus=''; +elif echo 'Nixed again...maybe "'$cpp' -" will work...'; \ + $cpp - <testcpp.c >testcpp.out 2>&1; \ + $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "Hooray, it works! I was beginning to wonder." + x_cpp="$cpp" + x_minus='-'; +elif echo 'Uh-uh. Time to get fancy. Trying a wrapper...'; \ + $wrapper <testcpp.c >testcpp.out 2>&1; \ + $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + x_cpp="$wrapper" + x_minus='' + echo "Eureka!" +else + dflt='' + rp="No dice. I can't find a C preprocessor. Name one:" + . ./myread + x_cpp="$ans" + x_minus='' + $x_cpp <testcpp.c >testcpp.out 2>&1 + if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "OK, that will do." >&4 + else +echo "Sorry, I can't get that to work. Go find one and rerun Configure." >&4 + exit 1 + fi +fi + +case "$ok" in +false) + cppstdin="$x_cpp" + cppminus="$x_minus" + cpprun="$x_cpp" + cpplast="$x_minus" + set X $x_cpp + shift + case "$1" in + "$cpp") + echo "Perhaps can we force $cc -E using a wrapper..." + if $wrapper <testcpp.c >testcpp.out 2>&1; \ + $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 + then + echo "Yup, we can." + cppstdin="$wrapper" + cppminus=''; + else + echo "Nope, we'll have to live without it..." + fi + ;; + esac + case "$cpprun" in + "$wrapper") + cpprun='' + cpplast='' + ;; + esac + ;; +esac + +case "$cppstdin" in +"$wrapper") ;; +*) $rm -f $wrapper;; +esac +$rm -f testcpp.c testcpp.out + +: Set private lib path +case "$plibpth" in +'') if ./mips; then + plibpth="$incpath/usr/lib /usr/local/lib /usr/ccs/lib" + fi;; +esac +case "$libpth" in +' ') dlist='';; +'') dlist="$loclibpth $plibpth $glibpth";; +*) dlist="$libpth";; +esac + +: Now check and see which directories actually exist, avoiding duplicates +libpth='' +for xxx in $dlist +do + if $test -d $xxx; then + case " $libpth " in + *" $xxx "*) ;; + *) libpth="$libpth $xxx";; + esac + fi +done +$cat <<'EOM' + +Some systems have incompatible or broken versions of libraries. Among +the directories listed in the question below, please remove any you +know not to be holding relevant libraries, and add any that are needed. +Say "none" for none. + +EOM +case "$libpth" in +'') dflt='none';; +*) + set X $libpth + shift + dflt=${1+"$@"} + ;; +esac +rp="Directories to use for library searches?" +. ./myread +case "$ans" in +none) libpth=' ';; +*) libpth="$ans";; +esac + +: determine optimize, if desired, or use for debug flag also +case "$optimize" in +' ') dflt='none';; +'') dflt='-O';; +*) dflt="$optimize";; +esac +$cat <<EOH + +Some C compilers have problems with their optimizers. By default, $package +compiles with the -O flag to use the optimizer. Alternately, you might want +to use the symbolic debugger, which uses the -g flag (on traditional Unix +systems). Either flag can be specified here. To use neither flag, specify +the word "none". + +EOH +rp="What optimizer/debugger flag should be used?" +. ./myread +optimize="$ans" +case "$optimize" in +'none') optimize=" ";; +esac + +dflt='' +: We will not override a previous value, but we might want to +: augment a hint file +case "$hint" in +none|recommended) + case "$gccversion" in + 1*) dflt='-fpcc-struct-return' ;; + esac + case "$optimize" in + *-g*) dflt="$dflt -DDEBUGGING";; + esac + case "$gccversion" in + 2*) if test -d /etc/conf/kconfig.d && + $contains _POSIX_VERSION $usrinc/sys/unistd.h >/dev/null 2>&1 + then + dflt="$dflt -posix" + fi + ;; + esac + ;; +esac + +case "$mips_type" in +*BSD*|'') inclwanted="$locincpth $usrinc";; +*) inclwanted="$locincpth $inclwanted $usrinc/bsd";; +esac +for thisincl in $inclwanted; do + if $test -d $thisincl; then + if $test x$thisincl != x$usrinc; then + case "$dflt" in + *$thisincl*);; + *) dflt="$dflt -I$thisincl";; + esac + fi + fi +done + +inctest='if $contains $2 $usrinc/$1 >/dev/null 2>&1; then + xxx=true; +elif $contains $2 $usrinc/sys/$1 >/dev/null 2>&1; then + xxx=true; +else + xxx=false; +fi; +if $xxx; then + case "$dflt" in + *$2*);; + *) dflt="$dflt -D$2";; + esac; +fi' + +if ./osf1; then + set signal.h __LANGUAGE_C__; eval $inctest +else + set signal.h LANGUAGE_C; eval $inctest +fi + +case "$hint" in +none|recommended) dflt="$ccflags $dflt" ;; +*) dflt="$ccflags";; +esac + +case "$dflt" in +''|' ') dflt=none;; +esac +$cat <<EOH + +Your C compiler may want other flags. For this question you should include +-I/whatever and -DWHATEVER flags and any other flags used by the C compiler, +but you should NOT include libraries or ld flags like -lwhatever. If you +want $package to honor its debug switch, you should include -DDEBUG here. + +To use no flags, specify the word "none". + +EOH +set X $dflt +shift +dflt=${1+"$@"} +rp="Any additional cc flags?" +. ./myread +case "$ans" in +none) ccflags='';; +*) ccflags="$ans";; +esac + +: the following weeds options from ccflags that are of no interest to cpp +cppflags="$ccflags" +case "$gccversion" in +1*) cppflags="$cppflags -D__GNUC__" +esac +case "$mips_type" in +'');; +*BSD*) cppflags="$cppflags -DSYSTYPE_BSD43";; +esac +case "$cppflags" in +'');; +*) + echo " " + echo "Let me guess what the preprocessor flags are..." >&4 + set X $cppflags + shift + cppflags='' + $cat >cpp.c <<'EOM' +#define BLURFL foo + +BLURFL xx LFRULB +EOM + previous='' + for flag in $* + do + case "$flag" in + -*) ftry="$flag";; + *) ftry="$previous $flag";; + esac + if $cppstdin -DLFRULB=bar $ftry $cppminus <cpp.c \ + >cpp1.out 2>/dev/null && \ + $cpprun -DLFRULB=bar $ftry $cpplast <cpp.c \ + >cpp2.out 2>/dev/null && \ + $contains 'foo.*xx.*bar' cpp1.out >/dev/null 2>&1 && \ + $contains 'foo.*xx.*bar' cpp2.out >/dev/null 2>&1 + then + cppflags="$cppflags $ftry" + previous='' + else + previous="$flag" + fi + done + set X $cppflags + shift + cppflags=${1+"$@"} + case "$cppflags" in + *-*) echo "They appear to be: $cppflags";; + esac + $rm -f cpp.c cpp?.out + ;; +esac + +: flags used in final linking phase +case "$ldflags" in +'') if ./venix; then + dflt='-i -z' + else + dflt='' + fi + case "$ccflags" in + *-posix*) dflt="$dflt -posix" ;; + esac + ;; +*) dflt="$ldflags";; +esac + +: Try to guess additional flags to pick up local libraries. +for thislibdir in $libpth; do + case " $loclibpth " in + *" $thislibdir "*) + case "$dflt " in + *"-L$thislibdir "*) ;; + *) dflt="$dflt -L$thislibdir" ;; + esac + ;; + esac +done + +case "$dflt" in +'') dflt='none' ;; +esac + +$cat <<EOH + +Your C linker may need flags. For this question you should +include -L/whatever and any other flags used by the C linker, but you +should NOT include libraries like -lwhatever. + +Make sure you include the appropriate -L/path flags if your C linker +does not normally search all of the directories you specified above, +namely + $libpth +To use no flags, specify the word "none". + +EOH + +rp="Any additional ld flags (NOT including libraries)?" +. ./myread +case "$ans" in +none) ldflags='';; +*) ldflags="$ans";; +esac +rmlist="$rmlist pdp11" + +: coherency check +echo " " +echo "Checking your choice of C compiler and flags for coherency..." >&4 +set X $cc $optimize $ccflags $ldflags try.c -o try +shift +$cat >try.msg <<EOM +I've tried to compile and run a simple program with: + + $* + ./try + +and I got the following output: + +EOM +$cat > try.c <<'EOF' +#include <stdio.h> +main() { exit(0); } +EOF +dflt=y +if sh -c "$cc $optimize $ccflags try.c -o try $ldflags" >>try.msg 2>&1; then + if sh -c './try' >>try.msg 2>&1; then + dflt=n + else + echo "The program compiled OK, but exited with status $?." >>try.msg + rp="You have a problem. Shall I abort Configure" + dflt=y + fi +else + echo "I can't compile the test program." >>try.msg + rp="You have a BIG problem. Shall I abort Configure" + dflt=y +fi +case "$dflt" in +y) + $cat try.msg + case "$knowitall" in + '') + echo "(The supplied flags might be incorrect with this C compiler.)" + ;; + *) dflt=n;; + esac + echo " " + . ./myread + case "$ans" in + n*|N*) ;; + *) echo "Ok. Stopping Configure." >&4 + exit 1 + ;; + esac + ;; +n) echo "OK, that should do.";; +esac +$rm -f try try.* core + +: Initialize h_fcntl +h_fcntl=false + +: Initialize h_sysfile +h_sysfile=false + +: compute shared library extension +case "$so" in +'') + if xxx=`./loc libc.sl X $libpth`; $test -f "$xxx"; then + dflt='sl' + else + dflt='so' + fi + ;; +*) dflt="$so";; +esac +$cat <<EOM + +On some systems, shared libraries may be available. Answer 'none' if +you want to suppress searching of shared libraries for the remaining +of this configuration. + +EOM +rp='What is the file extension used for shared libraries?' +. ./myread +so="$ans" + +: Define several unixisms. +: Hints files or command line option can be used to override them. +case "$_a" in +'') _a='.a';; +esac +case "$_o" in +'') _o='.o';; +esac + +: Looking for optional libraries +echo " " +echo "Checking for optional libraries..." >&4 +case "$libs" in +' '|'') dflt='';; +*) dflt="$libs";; +esac +case "$libswanted" in +'') libswanted='c_s';; +esac +for thislib in $libswanted; do + + if xxx=`./loc lib$thislib.$so.[0-9]'*' X $libpth`; $test -f "$xxx"; then + echo "Found -l$thislib (shared)." + case " $dflt " in + *"-l$thislib "*);; + *) dflt="$dflt -l$thislib";; + esac + elif xxx=`./loc lib$thislib.$so X $libpth` ; $test -f "$xxx"; then + echo "Found -l$thislib (shared)." + case " $dflt " in + *"-l$thislib "*);; + *) dflt="$dflt -l$thislib";; + esac + elif xxx=`./loc lib$thislib$_a X $libpth`; $test -f "$xxx"; then + echo "Found -l$thislib." + case " $dflt " in + *"-l$thislib "*);; + *) dflt="$dflt -l$thislib";; + esac + elif xxx=`./loc $thislib$_a X $libpth`; $test -f "$xxx"; then + echo "Found -l$thislib." + case " $dflt " in + *"-l$thislib "*);; + *) dflt="$dflt -l$thislib";; + esac + elif xxx=`./loc lib${thislib}_s$_a X $libpth`; $test -f "$xxx"; then + echo "Found -l${thislib}_s." + case " $dflt " in + *"-l$thislib "*);; + *) dflt="$dflt -l${thislib}_s";; + esac + elif xxx=`./loc Slib$thislib$_a X $xlibpth`; $test -f "$xxx"; then + echo "Found -l$thislib." + case " $dflt " in + *"-l$thislib "*);; + *) dflt="$dflt -l$thislib";; + esac + else + echo "No -l$thislib." + fi +done +set X $dflt +shift +dflt="$*" +case "$libs" in +'') dflt="$dflt";; +*) dflt="$libs";; +esac +case "$dflt" in +' '|'') dflt='none';; +esac + +$cat <<EOM + +Some versions of Unix support shared libraries, which make executables smaller +but make load time slightly longer. + +On some systems, mostly System V Release 3's, the shared library is included +by putting the option "-lc_s" as the last thing on the cc command line when +linking. Other systems use shared libraries by default. There may be other +libraries needed to compile $package on your machine as well. If your system +needs the "-lc_s" option, include it here. Include any other special libraries +here as well. Say "none" for none. +EOM + +echo " " +rp="Any additional libraries?" +. ./myread +case "$ans" in +none) libs=' ';; +*) libs="$ans";; +esac + +: set up the script used to warn in case of inconsistency +cat <<EOS >whoa +$startsh +EOS +cat <<'EOSC' >>whoa +dflt=y +echo " " +echo "*** WHOA THERE!!! ***" >&4 +echo " The $hint value for \$$var on this machine was \"$was\"!" >&4 +rp=" Keep the $hint value?" +. ./myread +case "$ans" in +y) td=$was; tu=$was;; +esac +EOSC + +: function used to set $1 to $val +setvar='var=$1; eval "was=\$$1"; td=$define; tu=$undef; +case "$val$was" in +$define$undef) . ./whoa; eval "$var=\$td";; +$undef$define) . ./whoa; eval "$var=\$tu";; +*) eval "$var=$val";; +esac' + +echo " " +echo "Checking for GNU C Library..." >&4 +cat >gnulibc.c <<EOM +int +main() +{ + return __libc_main(); +} +EOM +if $cc $ccflags $ldflags -o gnulibc gnulibc.c $libs >/dev/null 2>&1 && \ + ./gnulibc | $contains '^GNU C Library' >/dev/null 2>&1; then + val="$define" + echo "You are using the GNU C Library" +else + val="$undef" + echo "You are not using the GNU C Library" +fi +$rm -f gnulibc* +set d_gnulibc +eval $setvar + +: see if nm is to be used to determine whether a symbol is defined or not +case "$usenm" in +'') + case "$d_gnulibc" in + $define) + dflt=n + ;; + *) + dflt=`egrep 'inlibc|csym' ../Configure | wc -l 2>/dev/null` + if $test $dflt -gt 20; then + dflt=y + else + dflt=n + fi + ;; + esac + ;; +*) + case "$usenm" in + true) dflt=y;; + *) dflt=n;; + esac + ;; +esac +$cat <<EOM + +I can use 'nm' to extract the symbols from your C libraries. This is a time +consuming task which may generate huge output on the disk (up to 3 megabytes) +but that should make the symbols extraction faster. The alternative is to skip +the 'nm' extraction part and to compile a small test program instead to +determine whether each symbol is present. If you have a fast C compiler and/or +if your 'nm' output cannot be parsed, this may be the best solution. +You shouldn't let me use 'nm' if you have the GNU C Library. + +EOM +rp='Shall I use nm to extract C symbols from the libraries?' +. ./myread +case "$ans" in +n|N) usenm=false;; +*) usenm=true;; +esac + +runnm=$usenm +case "$reuseval" in +true) runnm=false;; +esac + +: nm options which may be necessary +case "$nm_opt" in +'') if $test -f /mach_boot; then + nm_opt='' + elif $test -d /usr/ccs/lib; then + nm_opt='-p' + elif $test -f /dgux; then + nm_opt='-p' + else + nm_opt='' + fi;; +esac + +: nm options which may be necessary for shared libraries but illegal +: for archive libraries. Thank you, Linux. +case "$nm_so_opt" in +'') case "$myuname" in + *linux*) + if nm --help | $grep 'dynamic' > /dev/null 2>&1; then + nm_so_opt='--dynamic' + fi + ;; + esac + ;; +esac + +case "$runnm" in +true) +: get list of predefined functions in a handy place +echo " " +case "$libc" in +'') libc=unknown + case "$libs" in + *-lc_s*) libc=`./loc libc_s$_a $libc $libpth` + esac + ;; +esac +libnames=''; +case "$libs" in +'') ;; +*) for thislib in $libs; do + case "$thislib" in + -lc|-lc_s) + : Handle C library specially below. + ;; + -l*) + thislib=`echo $thislib | $sed -e 's/^-l//'` + if try=`./loc lib$thislib.$so.'*' X $libpth`; $test -f "$try"; then + : + elif try=`./loc lib$thislib.$so X $libpth`; $test -f "$try"; then + : + elif try=`./loc lib$thislib$_a X $libpth`; $test -f "$try"; then + : + elif try=`./loc $thislib$_a X $libpth`; $test -f "$try"; then + : + elif try=`./loc lib$thislib X $libpth`; $test -f "$try"; then + : + elif try=`./loc $thislib X $libpth`; $test -f "$try"; then + : + elif try=`./loc Slib$thislib$_a X $xlibpth`; $test -f "$try"; then + : + else + try='' + fi + libnames="$libnames $try" + ;; + *) libnames="$libnames $thislib" ;; + esac + done + ;; +esac +xxx=normal +case "$libc" in +unknown) + set /lib/libc.$so + for xxx in $libpth; do + $test -r $1 || set $xxx/libc.$so + $test -r $1 || \ + set `echo blurfl; echo /usr/lib/libc.$so.[0-9]* | \ + tr ' ' '\012' | egrep -v '\.[A-Za-z]*$' | $sed -e ' + h + s/[0-9][0-9]*/0000&/g + s/0*\([0-9][0-9][0-9][0-9][0-9]\)/\1/g + G + s/\n/ /' | \ + sort | $sed -e 's/^.* //'` + eval set \$$# + done + $test -r $1 || set /usr/ccs/lib/libc.$so + $test -r $1 || set /lib/libsys_s$_a + ;; +*) + set blurfl + ;; +esac +if $test -r "$1"; then + echo "Your (shared) C library seems to be in $1." + libc="$1" +elif $test -r /lib/libc && $test -r /lib/clib; then + echo "Your C library seems to be in both /lib/clib and /lib/libc." + xxx=apollo + libc='/lib/clib /lib/libc' + if $test -r /lib/syslib; then + echo "(Your math library is in /lib/syslib.)" + libc="$libc /lib/syslib" + fi +elif $test -r "$libc" || (test -h "$libc") >/dev/null 2>&1; then + echo "Your C library seems to be in $libc, as you said before." +elif $test -r $incpath/usr/lib/libc$_a; then + libc=$incpath/usr/lib/libc$_a; + echo "Your C library seems to be in $libc. That's fine." +elif $test -r /lib/libc$_a; then + libc=/lib/libc$_a; + echo "Your C library seems to be in $libc. You're normal." +else + if tans=`./loc libc$_a blurfl/dyick $libpth`; $test -r "$tans"; then + : + elif tans=`./loc libc blurfl/dyick $libpth`; $test -r "$tans"; then + libnames="$libnames "`./loc clib blurfl/dyick $libpth` + elif tans=`./loc clib blurfl/dyick $libpth`; $test -r "$tans"; then + : + elif tans=`./loc Slibc$_a blurfl/dyick $xlibpth`; $test -r "$tans"; then + : + elif tans=`./loc Mlibc$_a blurfl/dyick $xlibpth`; $test -r "$tans"; then + : + else + tans=`./loc Llibc$_a blurfl/dyick $xlibpth` + fi + if $test -r "$tans"; then + echo "Your C library seems to be in $tans, of all places." + libc=$tans + else + libc='blurfl' + fi +fi +if $test $xxx = apollo -o -r "$libc" || (test -h "$libc") >/dev/null 2>&1; then + dflt="$libc" + cat <<EOM + +If the guess above is wrong (which it might be if you're using a strange +compiler, or your machine supports multiple models), you can override it here. + +EOM +else + dflt='' + echo $libpth | tr ' ' '\012' | sort | uniq > libpath + cat >&4 <<EOM +I can't seem to find your C library. I've looked in the following places: + +EOM + $sed 's/^/ /' libpath + cat <<EOM + +None of these seems to contain your C library. I need to get its name... + +EOM +fi +fn=f +rp='Where is your C library?' +. ./getfile +libc="$ans" + +echo " " +echo $libc $libnames | tr ' ' '\012' | sort | uniq > libnames +set X `cat libnames` +shift +xxx=files +case $# in 1) xxx=file; esac +echo "Extracting names from the following $xxx for later perusal:" >&4 +echo " " +$sed 's/^/ /' libnames >&4 +echo " " +$echo $n "This may take a while...$c" >&4 + +for file in $*; do + case $file in + *$so*) nm $nm_so_opt $nm_opt $file 2>/dev/null;; + *) nm $nm_opt $file 2>/dev/null;; + esac +done >libc.tmp + +$echo $n ".$c" +$grep fprintf libc.tmp > libc.ptf +xscan='eval "<libc.ptf $com >libc.list"; $echo $n ".$c" >&4' +xrun='eval "<libc.tmp $com >libc.list"; echo "done" >&4' +xxx='[ADTSIW]' +if com="$sed -n -e 's/__IO//' -e 's/^.* $xxx *_[_.]*//p' -e 's/^.* $xxx *//p'";\ + eval $xscan;\ + $contains '^fprintf$' libc.list >/dev/null 2>&1; then + eval $xrun +elif com="$sed -n -e 's/^__*//' -e 's/^\([a-zA-Z_0-9$]*\).*xtern.*/\1/p'";\ + eval $xscan;\ + $contains '^fprintf$' libc.list >/dev/null 2>&1; then + eval $xrun +elif com="$sed -n -e '/|UNDEF/d' -e '/FUNC..GL/s/^.*|__*//p'";\ + eval $xscan;\ + $contains '^fprintf$' libc.list >/dev/null 2>&1; then + eval $xrun +elif com="$sed -n -e 's/^.* D __*//p' -e 's/^.* D //p'";\ + eval $xscan;\ + $contains '^fprintf$' libc.list >/dev/null 2>&1; then + eval $xrun +elif com="$sed -n -e 's/^_//' -e 's/^\([a-zA-Z_0-9]*\).*xtern.*text.*/\1/p'";\ + eval $xscan;\ + $contains '^fprintf$' libc.list >/dev/null 2>&1; then + eval $xrun +elif com="$sed -n -e 's/^.*|FUNC |GLOB .*|//p'";\ + eval $xscan;\ + $contains '^fprintf$' libc.list >/dev/null 2>&1; then + eval $xrun +elif com="$grep '|' | $sed -n -e '/|COMMON/d' -e '/|DATA/d' \ + -e '/ file/d' -e 's/^\([^ ]*\).*/\1/p'";\ + eval $xscan;\ + $contains '^fprintf$' libc.list >/dev/null 2>&1; then + eval $xrun +elif com="$sed -n -e 's/^.*|FUNC |GLOB .*|//p' -e 's/^.*|FUNC |WEAK .*|//p'";\ + eval $xscan;\ + $contains '^fprintf$' libc.list >/dev/null 2>&1; then + eval $xrun +elif com="$sed -n -e 's/^__//' -e '/|Undef/d' -e '/|Proc/s/ .*//p'";\ + eval $xscan;\ + $contains '^fprintf$' libc.list >/dev/null 2>&1; then + eval $xrun +elif com="$sed -n -e '/Def. Text/s/.* \([^ ]*\)\$/\1/p'";\ + eval $xscan;\ + $contains '^fprintf$' libc.list >/dev/null 2>&1; then + eval $xrun +elif com="$sed -n -e 's/^[-0-9a-f ]*_\(.*\)=.*/\1/p'";\ + eval $xscan;\ + $contains '^fprintf$' libc.list >/dev/null 2>&1; then + eval $xrun +elif com="$sed -n -e 's/.*\.text n\ \ \ \.//p'";\ + eval $xscan;\ + $contains '^fprintf$' libc.list >/dev/null 2>&1; then + eval $xrun +else + nm -p $* 2>/dev/null >libc.tmp + $grep fprintf libc.tmp > libc.ptf + if com="$sed -n -e 's/^.* [ADTSIW] *_[_.]*//p' -e 's/^.* [ADTSIW] //p'";\ + eval $xscan; $contains '^fprintf$' libc.list >/dev/null 2>&1 + then + nm_opt='-p' + eval $xrun + else + echo " " + echo "nm didn't seem to work right. Trying ar instead..." >&4 + com='' + if ar t $libc > libc.tmp; then + for thisname in $libnames; do + ar t $thisname >>libc.tmp + done + $sed -e "s/\\$_o\$//" < libc.tmp > libc.list + echo "Ok." >&4 + else + echo "ar didn't seem to work right." >&4 + echo "Maybe this is a Cray...trying bld instead..." >&4 + if bld t $libc | $sed -e 's/.*\///' -e "s/\\$_o:.*\$//" > libc.list + then + for thisname in $libnames; do + bld t $libnames | \ + $sed -e 's/.*\///' -e "s/\\$_o:.*\$//" >>libc.list + ar t $thisname >>libc.tmp + done + echo "Ok." >&4 + else + echo "That didn't work either. Giving up." >&4 + exit 1 + fi + fi + fi +fi +nm_extract="$com" +if $test -f /lib/syscalls.exp; then + echo " " + echo "Also extracting names from /lib/syscalls.exp for good ole AIX..." >&4 + $sed -n 's/^\([^ ]*\)[ ]*syscall$/\1/p' /lib/syscalls.exp >>libc.list +fi +;; +esac +$rm -f libnames libpath + +: is a C symbol defined? +csym='tlook=$1; +case "$3" in +-v) tf=libc.tmp; tc=""; tdc="";; +-a) tf=libc.tmp; tc="[0]"; tdc="[]";; +*) tlook="^$1\$"; tf=libc.list; tc="()"; tdc="()";; +esac; +tx=yes; +case "$reuseval-$4" in +true-) ;; +true-*) tx=no; eval "tval=\$$4"; case "$tval" in "") tx=yes;; esac;; +esac; +case "$tx" in +yes) + case "$runnm" in + true) + if $contains $tlook $tf >/dev/null 2>&1; + then tval=true; + else tval=false; + fi;; + *) + echo "main() { extern short $1$tdc; printf(\"%hd\", $1$tc); }" > t.c; + if $cc $ccflags $ldflags -o t t.c $libs >/dev/null 2>&1; + then tval=true; + else tval=false; + fi; + $rm -f t t.c;; + esac;; +*) + case "$tval" in + $define) tval=true;; + *) tval=false;; + esac;; +esac; +eval "$2=$tval"' + +: define an is-in-libc? function +inlibc='echo " "; td=$define; tu=$undef; +sym=$1; var=$2; eval "was=\$$2"; +tx=yes; +case "$reuseval$was" in +true) ;; +true*) tx=no;; +esac; +case "$tx" in +yes) + set $sym tres -f; + eval $csym; + case "$tres" in + true) + echo "$sym() found." >&4; + case "$was" in $undef) . ./whoa; esac; eval "$var=\$td";; + *) + echo "$sym() NOT found." >&4; + case "$was" in $define) . ./whoa; esac; eval "$var=\$tu";; + esac;; +*) + case "$was" in + $define) echo "$sym() found." >&4;; + *) echo "$sym() NOT found." >&4;; + esac;; +esac' + +: determine filename position in cpp output +echo " " +echo "Computing filename position in cpp output for #include directives..." >&4 +echo '#include <stdio.h>' > foo.c +$cat >fieldn <<EOF +$startsh +$cppstdin $cppflags $cppminus <foo.c 2>/dev/null | \ +$grep '^[ ]*#.*stdio\.h' | \ +while read cline; do + pos=1 + set \$cline + while $test \$# -gt 0; do + if $test -r \`echo \$1 | $tr -d '"'\`; then + echo "\$pos" + exit 0 + fi + shift + pos=\`expr \$pos + 1\` + done +done +EOF +chmod +x fieldn +fieldn=`./fieldn` +$rm -f foo.c fieldn +case $fieldn in +'') pos='???';; +1) pos=first;; +2) pos=second;; +3) pos=third;; +*) pos="${fieldn}th";; +esac +echo "Your cpp writes the filename in the $pos field of the line." + +: locate header file +$cat >findhdr <<EOF +$startsh +wanted=\$1 +name='' +if test -f $usrinc/\$wanted; then + echo "$usrinc/\$wanted" + exit 0 +fi +awkprg='{ print \$$fieldn }' +echo "#include <\$wanted>" > foo\$\$.c +$cppstdin $cppminus $cppflags < foo\$\$.c 2>/dev/null | \ +$grep "^[ ]*#.*\$wanted" | \ +while read cline; do + name=\`echo \$cline | $awk "\$awkprg" | $tr -d '"'\` + case "\$name" in + */\$wanted) echo "\$name"; exit 0;; + *) name='';; + esac; +done; +$rm -f foo\$\$.c; +case "\$name" in +'') exit 1;; +esac +EOF +chmod +x findhdr + +: access call always available on UNIX +set access d_access +eval $inlibc + +: locate the flags for 'access()' +case "$d_access" in +"$define") + echo " " + $cat >access.c <<'EOCP' +#include <sys/types.h> +#ifdef I_FCNTL +#include <fcntl.h> +#endif +#ifdef I_SYS_FILE +#include <sys/file.h> +#endif +#ifdef I_UNISTD +#include <unistd.h> +#endif +main() { + exit(R_OK); +} +EOCP + : check sys/file.h first, no particular reason here + if $test `./findhdr sys/file.h` && \ + $cc $cppflags -DI_SYS_FILE access.c -o access >/dev/null 2>&1 ; then + h_sysfile=true; + echo "<sys/file.h> defines the *_OK access constants." >&4 + elif $test `./findhdr fcntl.h` && \ + $cc $cppflags -DI_FCNTL access.c -o access >/dev/null 2>&1 ; then + h_fcntl=true; + echo "<fcntl.h> defines the *_OK access constants." >&4 + elif $test `./findhdr unistd.h` && \ + $cc $cppflags -DI_UNISTD access.c -o access >/dev/null 2>&1 ; then + echo "<unistd.h> defines the *_OK access constants." >&4 + else + echo "I can't find the four *_OK access constants--I'll use mine." >&4 + fi + ;; +esac +$rm -f access* + +: Look for GNU-cc style attribute checking +echo " " +echo "Checking whether your compiler can handle __attribute__ ..." >&4 +$cat >attrib.c <<'EOCP' +#include <stdio.h> +void croak (char* pat,...) __attribute__((format(printf,1,2),noreturn)); +EOCP +if $cc $ccflags -c attrib.c >attrib.out 2>&1 ; then + if $contains 'warning' attrib.out >/dev/null 2>&1; then + echo "Your C compiler doesn't fully support __attribute__." + val="$undef" + else + echo "Your C compiler supports __attribute__." + val="$define" + fi +else + echo "Your C compiler doesn't seem to understand __attribute__ at all." + val="$undef" +fi +set d_attribut +eval $setvar +$rm -f attrib* + +: check for const keyword +echo " " +echo 'Checking to see if your C compiler knows about "const"...' >&4 +$cat >const.c <<'EOCP' +typedef struct spug { int drokk; } spug; +main() +{ + const char *foo; + const spug y; +} +EOCP +if $cc -c $ccflags const.c >/dev/null 2>&1 ; then + val="$define" + echo "Yup, it does." +else + val="$undef" + echo "Nope, it doesn't." +fi +set d_const +eval $setvar + +: see how we invoke the C preprocessor +$cat <<EOM + +$package needs to be able to preprocess its input files in a mode which +preserves comments, which is often not the default behaviour. It should run +the C preprocessor you will use when compiling your own source code, which +should be ISO/ANSI C compliant if you want $package to handle the latest +standard C. I will try to guess, but I might guess wrongly because it is not +necessarily the same preprocessor used to build $package. + +EOM +$cat <<'EOT' >testcpp.c +#define ABC abc +#define XYZ xyz +ABC.XYZ +/* comment */ +EOT +: +if $test "X$cppfilecom" != "X" && \ + $cppfilecom testcpp.c </dev/null >testcpp.out 2>/dev/null && \ + $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 && \ + $contains comment testcpp.out >/dev/null 2>&1 +then + echo "You used to use $cppfilecom so we'll use that again." +elif echo 'Maybe "'$cc' -E -C" will work...' && \ + $cc -E -C testcpp.c </dev/null >testcpp.out 2>/dev/null && \ + $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 && \ + $contains comment testcpp.out >/dev/null 2>&1 +then + echo "It works!" + cppfilecom="$cc -E -C" +elif echo 'Nope...maybe "'"$cc"' -P -C" will work...' && \ + $cc -P -C testcpp.c </dev/null >testcpp.out 2>/dev/null && \ + $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 && \ + $contains comment testcpp.out >/dev/null 2>&1 +then + echo "Yup, that does." + cppfilecom="$cc -P -C" +elif echo 'No such luck, maybe "'"$cpp"' -C" will work...' && \ + $cpp -C testcpp.c </dev/null >testcpp.out 2>/dev/null && \ + $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 && \ + $contains comment testcpp.out >/dev/null 2>&1 +then + echo "Yup, it does." + cppfilecom="$cpp -C" +else + cppfilecom='' + $cat <<'EOM' +I can't find a C preprocessor that will preserve comments. Please name one. +EOM +fi +: +dflt="$cppfilecom" +cont=true +while $test "$cont" ; do + echo " " + rp="How should $package run your preprocessor preserving comments?" + . ./myread + cppfilecom="$ans" + $cppfilecom testcpp.c >testcpp.out 2>&1 + if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 && \ + $contains comment testcpp.out >/dev/null 2>&1 + then + echo "OK, that will do." + cont='' + else + echo "Sorry, I can't get that to work." + fi +done + +: Now see if it ignores header files. +cp testcpp.c testcpp.h +$cppfilecom testcpp.h >testcpp.out 2>&1 +if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 && \ + $contains comment testcpp.out >/dev/null 2>&1 +then + echo "Terrific; it processes .h files passed on the command line too." + val="$undef" +else + echo "It ignores .h files on the command line; pity." + val="$define" +fi +set d_cppignhdrs +eval $setvar + +: Now see how to send stdin to it. +echo " " +cp testcpp.c testcpp.h +$cppfilecom <testcpp.h >testcpp.out 2>&1 +if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 && \ + $contains comment testcpp.out >/dev/null 2>&1 +then + echo "Great; and it will read stdin if passed no arguments." + val="$define" + cppstdinflags='' +else + $cppfilecom - <testcpp.h >testcpp.out 2>&1 + if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 && \ + $contains comment testcpp.out >/dev/null 2>&1 + then + echo "Great; and it can read stdin by passing it '-'." + val="$define" + cppstdinflags='-' + else + $cat <<FOO +Unfortunately, I can't find an easy way to get that preprocessor to read from +standard input. Do you know any flags I can pass it to get it to do so? +If that preprocessor can't read directly form standard input, answer 'none'. + +FOO + val='dunno' + while $test "$val" = "dunno"; do + rp='Flags to get preprocessor to read stdin?' + dflt='none' + . ./myread + if $test $ans = 'none'; then + echo "Oh well, if $package wants it done, it will do it for itself." + val="$undef" + else + $cppfilecom $ans <testcpp.h >testcpp.out 2>&1 + if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 && \ + $contains comment testcpp.out >/dev/null 2>&1 + then + echo "Good; that works fine." + val="$define" + cppstdinflags="$ans" + else + echo "Sorry, I couldn't get that to work." + fi + fi + done + fi +fi +set d_cppcanstdin +eval $setvar + +: cleanup cpp test files anyway +$rm -f testcpp.* + +: see if we can have long filenames +echo " " +rmlist="$rmlist /tmp/cf$$" +$test -d /tmp/cf$$ || mkdir /tmp/cf$$ +first=123456789abcdef +second=/tmp/cf$$/$first +$rm -f $first $second +if (echo hi >$first) 2>/dev/null; then + if $test -f 123456789abcde; then + echo 'You cannot have filenames longer than 14 characters. Sigh.' >&4 + val="$undef" + else + if (echo hi >$second) 2>/dev/null; then + if $test -f /tmp/cf$$/123456789abcde; then + $cat <<'EOM' +That's peculiar... You can have filenames longer than 14 characters, but only +on some of the filesystems. Maybe you are using NFS. Anyway, to avoid problems +I shall consider your system cannot support long filenames at all. +EOM + val="$undef" + else + echo 'You can have filenames longer than 14 characters.' >&4 + val="$define" + fi + else + $cat <<'EOM' +How confusing! Some of your filesystems are sane enough to allow filenames +longer than 14 characters but some others like /tmp can't even think about them. +So, for now on, I shall assume your kernel does not allow them at all. +EOM + val="$undef" + fi + fi +else + $cat <<'EOM' +You can't have filenames longer than 14 chars. You can't even think about them! +EOM + val="$undef" +fi +set d_flexfnam +eval $setvar +$rm -rf /tmp/cf$$ 123456789abcde* + +: see which of string.h or strings.h is needed +echo " " +strings=`./findhdr string.h` +if $test "$strings" && $test -r "$strings"; then + echo "Using <string.h> instead of <strings.h>." >&4 + val="$define" +else + val="$undef" + strings=`./findhdr strings.h` + if $test "$strings" && $test -r "$strings"; then + echo "Using <strings.h> instead of <string.h>." >&4 + else + echo "No string header found -- You'll surely have problems." >&4 + fi +fi +set i_string +eval $setvar +case "$i_string" in +"$undef") strings=`./findhdr strings.h`;; +*) strings=`./findhdr string.h`;; +esac + +: index or strchr +echo " " +if set index val -f; eval $csym; $val; then + if set strchr val -f d_strchr; eval $csym; $val; then + if $contains strchr "$strings" >/dev/null 2>&1 ; then + val="$define" + vali="$undef" + echo "strchr() found." >&4 + else + val="$undef" + vali="$define" + echo "index() found." >&4 + fi + else + val="$undef" + vali="$define" + echo "index() found." >&4 + fi +else + if set strchr val -f d_strchr; eval $csym; $val; then + val="$define" + vali="$undef" + echo "strchr() found." >&4 + else + echo "No index() or strchr() found!" >&4 + val="$undef" + vali="$undef" + fi +fi +set d_strchr; eval $setvar +val="$vali" +set d_index; eval $setvar + +: see if link exists +set link d_link +eval $inlibc + +: Locate the flags for 'open()' +echo " " +$cat >open3.c <<'EOCP' +#include <sys/types.h> +#ifdef I_FCNTL +#include <fcntl.h> +#endif +#ifdef I_SYS_FILE +#include <sys/file.h> +#endif +main() { + if(O_RDONLY); +#ifdef O_TRUNC + exit(0); +#else + exit(1); +#endif +} +EOCP +: check sys/file.h first to get FREAD on Sun +if $test `./findhdr sys/file.h` && \ + $cc $cppflags "-DI_SYS_FILE" open3.c -o open3 >/dev/null 2>&1 ; then + h_sysfile=true; + echo "<sys/file.h> defines the O_* constants..." >&4 + if ./open3; then + echo "and you have the 3 argument form of open()." >&4 + val="$define" + else + echo "but not the 3 argument form of open(). Oh, well." >&4 + val="$undef" + fi +elif $test `./findhdr fcntl.h` && \ + $cc "-DI_FCNTL" open3.c -o open3 >/dev/null 2>&1 ; then + h_fcntl=true; + echo "<fcntl.h> defines the O_* constants..." >&4 + if ./open3; then + echo "and you have the 3 argument form of open()." >&4 + val="$define" + else + echo "but not the 3 argument form of open(). Oh, well." >&4 + val="$undef" + fi +else + val="$undef" + echo "I can't find the O_* constant definitions! You got problems." >&4 +fi +set d_open3 +eval $setvar +$rm -f open3* + +: see if strftime exists +set strftime d_strftime +eval $inlibc + +: see if strstr exists +set strstr d_strstr +eval $inlibc + +: see if symlink exists +set symlink d_symlink +eval $inlibc + +: check for volatile keyword +echo " " +echo 'Checking to see if your C compiler knows about "volatile"...' >&4 +$cat >try.c <<'EOCP' +main() +{ + typedef struct _goo_struct goo_struct; + goo_struct * volatile goo = ((goo_struct *)0); + struct _goo_struct { + long long_int; + int reg_int; + char char_var; + }; + typedef unsigned short foo_t; + char *volatile foo; + volatile int bar; + volatile foo_t blech; + foo = foo; +} +EOCP +if $cc -c $ccflags try.c >/dev/null 2>&1 ; then + val="$define" + echo "Yup, it does." +else + val="$undef" + echo "Nope, it doesn't." +fi +set d_volatile +eval $setvar +$rm -f try.* + +: preserve RCS keywords in files with variable substitution, grrr +Id='$Id' + +: check for void type +echo " " +echo "Checking to see how well your C compiler groks the void type..." >&4 +echo " " +$cat >&4 <<EOM + Support flag bits are: + 1: basic void declarations. + 2: arrays of pointers to functions returning void. + 4: operations between pointers to and addresses of void functions. + 8: generic void pointers. +EOM +echo " " +case "$voidflags" in +'') + $cat >try.c <<'EOCP' +#if TRY & 1 +void sub() { +#else +sub() { +#endif + extern void moo(); /* function returning void */ + void (*goo)(); /* ptr to func returning void */ +#if TRY & 8 + void *hue; /* generic ptr */ +#endif +#if TRY & 2 + void (*foo[10])(); +#endif + +#if TRY & 4 + if(goo == moo) { + exit(0); + } +#endif + exit(0); +} +main() { sub(); } +EOCP + if $cc $ccflags -c -DTRY=$defvoidused try.c >.out 2>&1 ; then + voidflags=$defvoidused + echo "It appears to support void to the level $package wants ($defvoidused)." + if $contains warning .out >/dev/null 2>&1; then + echo "However, you might get some warnings that look like this:" + $cat .out + fi + else +echo "Hmm, your compiler has some difficulty with void. Checking further..." >&4 + if $cc $ccflags -c -DTRY=1 try.c >/dev/null 2>&1; then + echo "It supports 1..." + if $cc $ccflags -c -DTRY=3 try.c >/dev/null 2>&1; then + echo "It also supports 2..." + if $cc $ccflags -c -DTRY=7 try.c >/dev/null 2>&1; then + voidflags=7 + echo "And it supports 4 but not 8 definitely." + else + echo "It doesn't support 4..." + if $cc $ccflags -c -DTRY=11 try.c >/dev/null 2>&1; then + voidflags=11 + echo "But it supports 8." + else + voidflags=3 + echo "Neither does it support 8." + fi + fi + else + echo "It does not support 2..." + if $cc $ccflags -c -DTRY=13 try.c >/dev/null 2>&1; then + voidflags=13 + echo "But it supports 4 and 8." + else + if $cc $ccflags -c -DTRY=5 try.c >/dev/null 2>&1; then + voidflags=5 + echo "And it supports 4 but has not heard about 8." + else + echo "However it supports 8 but not 4." + fi + fi + fi + else + echo "There is no support at all for void." + voidflags=0 + fi + fi +esac +case "$voidflags" in +"$defvoidused") ;; +*) + dflt="$voidflags"; + rp="Your void support flags add up to what?" + . ./myread + voidflags="$ans" + ;; +esac +$rm -f try.* .out + +: determine lexical analyser generator +case "$lex" in +'') + dflt=lex;; +*) + dflt="$lex";; +esac +echo " " +if $test -f "$flex"; then + rp='Which lexical analyser generator (lex or flex) shall I use?' +else + rp='Which lexical analyser generator shall I use?' +fi +. ./myread +lex="$ans" + +: if using lex this will normally be useless, but flex frequently takes args +echo " " +case "$lexflags" in +'') dflt='none';; +*) dflt="$lexflags";; +esac +rp="What flags should be given to $lex?" +. ./myread +case "$ans" in +none) lexflags='';; +*) lexflags="$ans";; +esac + +: determine compiler compiler +case "$yacc" in +'') + dflt=yacc;; +*) + dflt="$yacc";; +esac +echo " " +comp='yacc' +if $test -f "$byacc"; then + dflt="$byacc" + comp="byacc or $comp" +fi +if $test -f "$bison"; then + comp="$comp or bison -y" +fi +rp="Which compiler compiler ($comp) shall I use?" +. ./myread +yacc="$ans" +case "$yacc" in +*bis*) + case "$yacc" in + *-y*) ;; + *) + yacc="$yacc -y" + echo "(Adding -y option to bison to get yacc-compatible behaviour.)" + ;; + esac + ;; +esac + +: see if we need extra yacc flags +dflt="$yaccflags" +case "$dflt" in +'') dflt=none;; +esac +$cat <<EOH + +Your yacc program may need extra flags to normally process the parser sources. +Do NOT specify any -d or -v flags here, since those are explicitely known +by the various Makefiles. However, if your machine has strange/undocumented +options (like -Sr# on SCO to specify the maximum number of grammar rules), then +please add them here. To use no flags, specify the word "none". + +EOH +rp="Any additional yacc flags?" +. ./myread +case "$ans" in +none) yaccflags='';; +*) yaccflags="$ans";; +esac + +: see if we should include -ly +echo " " +case "$yacc" in +*byacc*) + echo "You are using byacc, so I won't look for a yacc library." >&4 + libyacc='' + ;; +*yacc) + xxx=`./loc liby$_a x $libpth` + case "$xxx" in + x) + echo "No yacc library found." >&4 + libyacc='' + ;; + *) + echo "yacc library found in $xxx." >&4 + libyacc="$xxx" + ;; + esac + ;; +*bison*) + echo "You are using bison, so I won't look for a yacc library." >&4 + libyacc='' + ;; +*) +echo "You don't seem to have yacc, so I won't look for the yacc library." >&4 + libyacc='' + ;; +esac + +: find out how to generate dependencies +echo " " +echo "Checking how to generate makefile dependencies on your machine..." >&4 +toplev=`cd ..;pwd` +$cat >dep.c <<'EOCP' +#include "dep.h" +EOCP +$cat >dep.h <<'EOCP' + +EOCP +takeflags='flags="" +case "$@" in +*--*) + for arg + do + shift + case "$arg" in + --) break;; + *) flags="$flags $arg";; + esac + done;; +esac' +case "$mkdep" in +'') + ;; +*) + if test -x "$mkdep" && + $mkdep dep.c >dep.out 2>/dev/null && + $contains "dep$_o:.*dep\.h" dep.out >/dev/null 2>&1 + then + echo "$mkdep works." + else + mkdep= + fi +esac + +case "$mkdep" in +'') + $spitshell > ../mkdep <<EOM +$startsh +$takeflags +for srcfile +do + $cpp -M -I. $cppflags \$flags \$srcfile 2>/dev/null +done +exit 0 +EOM + mkdep=$toplev/mkdep + chmod +x $mkdep + $eunicefix $mkdep + if $mkdep dep.c >dep.out 2>/dev/null && + $contains "dep$_o:.*dep\.h" dep.out >/dev/null 2>&1 + then + echo "Looks like we can use $cpp -M." + else + mkdep= + fi + ;; +esac + +case "$mkdep" in +'') + $spitshell > ../mkdep <<EOM +$startsh +$takeflags +for srcfile +do + $cc -MM -I. $cppflags \$flags \$srcfile 2>/dev/null +done +exit 0 +EOM + mkdep=$toplev/mkdep + chmod +x $mkdep + $eunicefix $mkdep + if $mkdep dep.c >dep.out 2>/dev/null && + $contains "dep$_o: dep.h" dep.out >/dev/null 2>&1 + then + echo "Looks like we can use $cc -MM." + else + mkdep= + fi + ;; +esac + +case "$mkdep" in +'') + $spitshell >../mkdep <<EOS +$startsh +$takeflags +for srcfile +do + case "\$srcfile" in + *.c) c='.c';; + *.y) c='.y';; + *.l) c='.l';; + esac + filebase=\`basename \$srcfile \$c\` + <\$srcfile $cpp $cppminus $cppflags -I. \$flags 2>/dev/null | \\ + $sed -e '/^# *[0-9]/!d' \\ + -e 's/^.*"\(.*\)".*\$/'\$filebase'$_o: \1/' \\ + -e 's|: \./|: |' \\ + -e 's|: *$|: '\$srcfile'|' | \\ + $grep -v '^#' | $sort | $uniq +done +exit 0 +EOS + mkdep=$toplev/mkdep + chmod +x $mkdep + $eunicefix $mkdep + if $mkdep dep.c >dep.out 2>/dev/null && + $contains "dep$_o:.*dep\.h" dep.out >/dev/null 2>&1 + then + echo "A shell script using $cpp does the trick." + else + echo "$cpp doesn't seem to be any use at all." + $spitshell >../mkdep <<EOS +$startsh +$takeflags +files="\$@" +set X \$flags +shift +inc='.' +while test \$# -gt 0 +do + case "\$1" in + -I) + shift + inc="\$inc:\$1" + ;; + -I*) + dir=\`echo \$1 | sed -e 's/^-I//'\` + inc="\$inc:\$dir" + ;; + esac + shift +done +set X \$files +shift +trap "$rm -f /tmp/mkdep\$\$; exit 1" 1 2 3 15 +for srcfile +do + case "\$srcfile" in + *.c) c='.c';; + *.y) c='.y';; + *.l) c='.l';; + esac + filebase=\`basename \$srcfile \$c\` + echo \$filebase$_o: \$srcfile + $grep '^#[ ]*include' \$srcfile /dev/null | \ + $sed -n -e 's/#[ ]*include[ ]*//' \\ + -e '/<\(.*\)>/ d' \\ + -e 's/:[^"]*"\([^"]*\)".*/: \1/' \\ + -e "s/\\.c:/$_o:/p" > /tmp/mkdep\$\$ + IFS=': ' + while read file dep; do + for dir in \$inc; do + if $test -f "\$dir/\$dep"; then + dep="\$dir/\$dep" + break + fi + done + echo "\$file: \$dep" | $sed -e 's,: \./,: ,' + done </tmp/mkdep\$\$ + IFS=' ' + $rm -f /tmp/mkdep\$\$ +done +exit 0 +EOS + mkdep=$toplev/mkdep + chmod +x $mkdep + $eunicefix $mkdep + if $mkdep dep.c >dep.out 2>/dev/null && + $contains "dep$_o:.*dep\.h" dep.out >/dev/null 2>&1 + then + cat << EOM + +I can use a script with grep instead, but it will make some incorrect +dependencies, since it doesn't understand about conditional compilation. +Moreover, some dependencies may be missing, because scanning won't be +a recursive process. +If you have a program which generates makefile dependencies, you may want +to use it. If not, you can use the script and edit the Makefile by hand +if you need to. +EOM + else + mkdep= + cat << EOM + +I can't seem to generate makefile dependencies at all! Perhaps you have a +program that does? If you don't, you might look at the mkdep script to +see if you can create one which works. +EOM + fi + fi +esac +dflt="$mkdep" +fn=f~/ +rp="Name of program to make makefile dependencies?" +. ./getfile +mkdep="$ans" +$rm -f dep.c dep.h dep$_o dep.out + +: Cruising for prototypes +echo " " +echo "Checking out function prototypes..." >&4 +$cat >prototype.c <<'EOCP' +main(int argc, char *argv[]) { + exit(0);} +EOCP +if $cc $ccflags -c prototype.c >prototype.out 2>&1 ; then + echo "Your C compiler appears to support function prototypes." + val="$define" +else + echo "Your C compiler doesn't seem to understand function prototypes." + val="$undef" +fi +set prototype +eval $setvar +$rm -f prototype* + +: see if signal is declared as pointer to function returning int or void +echo " " +xxx=`./findhdr signal.h` +$test "$xxx" && $cppstdin $cppminus $cppflags < $xxx >$$.tmp 2>/dev/null +if $contains 'int.*\*[ ]*signal' $$.tmp >/dev/null 2>&1 ; then + echo "You have int (*signal())() instead of void." >&4 + val="$undef" +elif $contains 'void.*\*[ ]*signal' $$.tmp >/dev/null 2>&1 ; then + echo "You have void (*signal())() instead of int." >&4 + val="$define" +elif $contains 'extern[ ]*[(\*]*signal' $$.tmp >/dev/null 2>&1 ; then + echo "You have int (*signal())() instead of void." >&4 + val="$undef" +else + case "$d_voidsig" in + '') + echo "I can't determine whether signal handler returns void or int..." >&4 + dflt=void + rp="What type does your signal handler return?" + . ./myread + case "$ans" in + v*) val="$define";; + *) val="$undef";; + esac;; + "$define") + echo "As you already told me, signal handler returns void." >&4;; + *) + echo "As you already told me, signal handler returns int." >&4;; + esac +fi +set d_voidsig +eval $setvar +case "$d_voidsig" in +"$define") signal_t="void";; +*) signal_t="int";; +esac +$rm -f $$.tmp + +: define an is-a-typedef? function +typedef='type=$1; var=$2; def=$3; shift; shift; shift; inclist=$@; +case "$inclist" in +"") inclist="sys/types.h";; +esac; +eval "varval=\$$var"; +case "$varval" in +"") + $rm -f temp.c; + for inc in $inclist; do + echo "#include <$inc>" >>temp.c; + done; + $cppstdin $cppflags $cppminus < temp.c >temp.E 2>/dev/null; + if $contains $type temp.E >/dev/null 2>&1; then + eval "$var=\$type"; + else + eval "$var=\$def"; + fi; + $rm -f temp.?;; +*) eval "$var=\$varval";; +esac' + +: see if time exists +echo " " +if set time val -f d_time; eval $csym; $val; then + echo 'time() found.' >&4 + val="$define" + set time_t timetype long stdio.h sys/types.h + eval $typedef + dflt="$timetype" + echo " " + rp="What type is returned by time() on this system?" + . ./myread + timetype="$ans" +else + echo 'time() not found, hope that will do.' >&4 + val="$undef" + timetype='int'; +fi +set d_time +eval $setvar + +: see if stdarg is available +echo " " +if $test `./findhdr stdarg.h`; then + echo "<stdarg.h> found." >&4 + valstd="$define" +else + echo "<stdarg.h> NOT found." >&4 + valstd="$undef" +fi + +: see if varags is available +echo " " +if $test `./findhdr varargs.h`; then + echo "<varargs.h> found." >&4 +else + echo "<varargs.h> NOT found, but that's ok (I hope)." >&4 +fi + +: set up the varargs testing programs +$cat > varargs.c <<EOP +#ifdef I_STDARG +#include <stdarg.h> +#endif +#ifdef I_VARARGS +#include <varargs.h> +#endif + +#ifdef I_STDARG +int f(char *p, ...) +#else +int f(va_alist) +va_dcl +#endif +{ + va_list ap; +#ifndef I_STDARG + char *p; +#endif +#ifdef I_STDARG + va_start(ap,p); +#else + va_start(ap); + p = va_arg(ap, char *); +#endif + va_end(ap); +} +EOP +$cat > varargs <<EOP +$startsh +if $cc -c $ccflags -D\$1 varargs.c >/dev/null 2>&1; then + echo "true" +else + echo "false" +fi +$rm -f varargs$_o +EOP +chmod +x varargs + +: now check which varargs header should be included +echo " " +i_varhdr='' +case "$valstd" in +"$define") + if `./varargs I_STDARG`; then + val='stdarg.h' + elif `./varargs I_VARARGS`; then + val='varargs.h' + fi + ;; +*) + if `./varargs I_VARARGS`; then + val='varargs.h' + fi + ;; +esac +case "$val" in +'') +echo "I could not find the definition for va_dcl... You have problems..." >&4 + val="$undef"; set i_stdarg; eval $setvar + val="$undef"; set i_varargs; eval $setvar + ;; +*) + set i_varhdr + eval $setvar + case "$i_varhdr" in + stdarg.h) + val="$define"; set i_stdarg; eval $setvar + val="$undef"; set i_varargs; eval $setvar + ;; + varargs.h) + val="$undef"; set i_stdarg; eval $setvar + val="$define"; set i_varargs; eval $setvar + ;; + esac + echo "We'll include <$i_varhdr> to get va_dcl definition." >&4;; +esac +$rm -f varargs* + +: see if prototypes support variable argument declarations +echo " " +case "$prototype$i_stdarg" in +$define$define) + echo "It appears we'll be able to prototype varargs functions." >&4 + val="$define" + ;; +*) + echo "Too bad... We won't be using prototyped varargs functions..." >&4 + val="$undef" + ;; +esac +set vaproto +eval $setvar + +$cat <<EOH + +You may wish to compile with extra compiler warnings enabled. +Note that doing so enhances your chance of receiving your free set of steak +knives, particularly if you find any bugs and report them. +If you don't want extra warnings, answer "none". + +EOH +case "$warnflags" in +'') case "$cc" in + *gcc*) + dflt="-Wall -Wno-comment" + if $contains 'fprintf' `./findhdr stdio.h` >/dev/null 2>&1; then + : + else + dflt="$dflt -Wno-implicit" + fi + ;; + *) dflt="none";; + esac + ;; +' ') dflt="none";; +*) dflt="$warnflags";; +esac + +rp="Any $cc flags to enable warnings?" +. ./myread +case "$ans" in +none) warnflags=' ';; +*) warnflags="$ans";; +esac + +: define an alternate in-header-list? function +inhdr='echo " "; td=$define; tu=$undef; yyy=$@; +cont=true; xxf="echo \"<\$1> found.\" >&4"; +case $# in 2) xxnf="echo \"<\$1> NOT found.\" >&4";; +*) xxnf="echo \"<\$1> NOT found, ...\" >&4";; +esac; +case $# in 4) instead=instead;; *) instead="at last";; esac; +while $test "$cont"; do + xxx=`./findhdr $1` + var=$2; eval "was=\$$2"; + if $test "$xxx" && $test -r "$xxx"; + then eval $xxf; + eval "case \"\$$var\" in $undef) . ./whoa; esac"; eval "$var=\$td"; + cont=""; + else eval $xxnf; + eval "case \"\$$var\" in $define) . ./whoa; esac"; eval "$var=\$tu"; fi; + set $yyy; shift; shift; yyy=$@; + case $# in 0) cont="";; + 2) xxf="echo \"but I found <\$1> $instead.\" >&4"; + xxnf="echo \"and I did not find <\$1> either.\" >&4";; + *) xxf="echo \"but I found <\$1\> instead.\" >&4"; + xxnf="echo \"there is no <\$1>, ...\" >&4";; + esac; +done; +while $test "$yyy"; +do set $yyy; var=$2; eval "was=\$$2"; + eval "case \"\$$var\" in $define) . ./whoa; esac"; eval "$var=\$tu"; + set $yyy; shift; shift; yyy=$@; +done' + +: see if this is a sys/file.h system +val='' +set sys/file.h val +eval $inhdr + +: do we need to include sys/file.h ? +case "$val" in +"$define") + echo " " + if $h_sysfile; then + val="$define" + echo "We'll be including <sys/file.h>." >&4 + else + val="$undef" + echo "We won't be including <sys/file.h>." >&4 + fi + ;; +*) + h_sysfile=false + ;; +esac +set i_sysfile +eval $setvar + +: see if fcntl.h is there +val='' +set fcntl.h val +eval $inhdr + +: see if we can include fcntl.h +case "$val" in +"$define") + echo " " + if $h_fcntl; then + val="$define" + echo "We'll be including <fcntl.h>." >&4 + else + val="$undef" + if $h_sysfile; then + echo "We don't need to include <fcntl.h> if we include <sys/file.h>." >&4 + else + echo "We won't be including <fcntl.h>." >&4 + fi + fi + ;; +*) + h_fcntl=false + val="$undef" + ;; +esac +set i_fcntl +eval $setvar + +: see if stddef is available +set stddef.h i_stddef +eval $inhdr + +: see if stdlib is available +set stdlib.h i_stdlib +eval $inhdr + +: see if we should include time.h, sys/time.h, or both +echo " " +echo "Testing to see if we should include <time.h>, <sys/time.h> or both." >&4 +$echo $n "I'm now running the test program...$c" +$cat >try.c <<'EOCP' +#include <sys/types.h> +#ifdef I_TIME +#include <time.h> +#endif +#ifdef I_SYSTIME +#ifdef SYSTIMEKERNEL +#define KERNEL +#endif +#include <sys/time.h> +#endif +#ifdef I_SYSSELECT +#include <sys/select.h> +#endif +main() +{ + struct tm foo; +#ifdef S_TIMEVAL + struct timeval bar; +#endif +#ifdef S_TIMEZONE + struct timezone tzp; +#endif + if (foo.tm_sec == foo.tm_sec) + exit(0); +#ifdef S_TIMEVAL + if (bar.tv_sec == bar.tv_sec) + exit(0); +#endif + exit(1); +} +EOCP +flags='' +s_timezone='' +sysselect='' +for s_timeval in '-DS_TIMEVAL' ''; do +for i_systimek in '' '-DSYSTIMEKERNEL'; do +for i_time in '' '-DI_TIME'; do +for i_systime in '-DI_SYSTIME' ''; do + case "$flags" in + '') $echo $n ".$c" + if $cc $ccflags \ + $i_time $i_systime $i_systimek $sysselect $s_timeval $s_timezone \ + try.c -o try >/dev/null 2>&1 ; then + set X $i_time $i_systime $i_systimek $sysselect $s_timeval + shift + flags="$*" + echo " " + $echo $n "Succeeded with $flags$c" + fi + ;; + esac +done +done +done +done +timeincl='' +echo " " +case "$flags" in +*SYSTIMEKERNEL*) i_systimek="$define" + timeincl=`./findhdr sys/time.h` + echo "We'll include <sys/time.h> with KERNEL defined." >&4;; +*) i_systimek="$undef";; +esac +case "$flags" in +*I_TIME*) i_time="$define" + timeincl=`./findhdr time.h`" $timeincl" + echo "We'll include <time.h>." >&4;; +*) i_time="$undef";; +esac +case "$flags" in +*I_SYSTIME*) i_systime="$define" + timeincl=`./findhdr sys/time.h`" $timeincl" + echo "We'll include <sys/time.h>." >&4;; +*) i_systime="$undef";; +esac +$rm -f try.c try + +: see if sys/types.h has to be included +set sys/types.h i_systypes +eval $inhdr + +: see if this is a unistd.h system +set unistd.h i_unistd +eval $inhdr + +: end of configuration questions +echo " " +echo "End of configuration questions." +echo " " + +: back to where it started +if test -d ../UU; then + cd .. +fi + +: configuration may be patched via a 'config.over' file +if $test -f config.over; then + echo " " + dflt=y + rp='I see a config.over file. Do you wish to load it?' + . UU/myread + case "$ans" in + n*) echo "OK, I'll ignore it.";; + *) . ./config.over + echo "Configuration override changes have been loaded." + ;; + esac +fi + +: in case they want portability, strip down executable paths +case "$d_portable" in +"$define") + echo " " + echo "Stripping down executable paths..." >&4 + for file in $loclist $trylist; do + eval $file="\$file" + done + ;; +esac + +: create config.sh file +echo " " +echo "Creating config.sh..." >&4 +$spitshell <<EOT >config.sh +$startsh +# +# This file was produced by running the Configure script. It holds all the +# definitions figured out by Configure. Should you modify one of these values, +# do not forget to propagate your changes by running "Configure -der". You may +# instead choose to run each of the .SH files by yourself, or "Configure -S". +# + +# Package name : $package +# Source directory : $src +# Configuration time: $cf_time +# Configured by : $cf_by +# Target system : $myuname + +Author='$Author' +Date='$Date' +Header='$Header' +Id='$Id' +Locker='$Locker' +Log='$Log' +Mcc='$Mcc' +RCSfile='$RCSfile' +Revision='$Revision' +Source='$Source' +State='$State' +afs='$afs' +ar='$ar' +archobjs='$archobjs' +awk='$awk' +bash='$bash' +bin='$bin' +binexp='$binexp' +bison='$bison' +byacc='$byacc' +c='$c' +cat='$cat' +cc='$cc' +ccflags='$ccflags' +cf_by='$cf_by' +cf_time='$cf_time' +chgrp='$chgrp' +chmod='$chmod' +chown='$chown' +comm='$comm' +compress='$compress' +contains='$contains' +cp='$cp' +cpio='$cpio' +cpp='$cpp' +cppfilecom='$cppfilecom' +cppflags='$cppflags' +cpplast='$cpplast' +cppminus='$cppminus' +cpprun='$cpprun' +cppstdin='$cppstdin' +cppstdinflags='$cppstdinflags' +csh='$csh' +d_access='$d_access' +d_attribut='$d_attribut' +d_bsd='$d_bsd' +d_const='$d_const' +d_cppcanstdin='$d_cppcanstdin' +d_cppignhdrs='$d_cppignhdrs' +d_eunice='$d_eunice' +d_flexfnam='$d_flexfnam' +d_gnulibc='$d_gnulibc' +d_index='$d_index' +d_link='$d_link' +d_open3='$d_open3' +d_portable='$d_portable' +d_strchr='$d_strchr' +d_strftime='$d_strftime' +d_strstr='$d_strstr' +d_symlink='$d_symlink' +d_time='$d_time' +d_voidsig='$d_voidsig' +d_volatile='$d_volatile' +d_xenix='$d_xenix' +date='$date' +defvoidused='$defvoidused' +echo='$echo' +egrep='$egrep' +emacs='$emacs' +eunicefix='$eunicefix' +expr='$expr' +find='$find' +firstmakefile='$firstmakefile' +flex='$flex' +gcc='$gcc' +gccversion='$gccversion' +glibpth='$glibpth' +grep='$grep' +gzip='$gzip' +h_fcntl='$h_fcntl' +h_sysfile='$h_sysfile' +hint='$hint' +i_fcntl='$i_fcntl' +i_stdarg='$i_stdarg' +i_stddef='$i_stddef' +i_stdlib='$i_stdlib' +i_string='$i_string' +i_sysfile='$i_sysfile' +i_systime='$i_systime' +i_systimek='$i_systimek' +i_systypes='$i_systypes' +i_time='$i_time' +i_unistd='$i_unistd' +i_varargs='$i_varargs' +i_varhdr='$i_varhdr' +incpath='$incpath' +inews='$inews' +installbin='$installbin' +installmansrc='$installmansrc' +installprivlib='$installprivlib' +ksh='$ksh' +ldflags='$ldflags' +less='$less' +lex='$lex' +lexflags='$lexflags' +libc='$libc' +libpth='$libpth' +libs='$libs' +libyacc='$libyacc' +line='$line' +lint='$lint' +lkflags='$lkflags' +ln='$ln' +lns='$lns' +locincpth='$locincpth' +loclibpth='$loclibpth' +lp='$lp' +lpr='$lpr' +ls='$ls' +mail='$mail' +mailx='$mailx' +make='$make' +manext='$manext' +mansrc='$mansrc' +mansrcexp='$mansrcexp' +mips='$mips' +mips_type='$mips_type' +mkdep='$mkdep' +mkdir='$mkdir' +more='$more' +mv='$mv' +myuname='$myuname' +n='$n' +nm_opt='$nm_opt' +nm_so_opt='$nm_so_opt' +nroff='$nroff' +optimize='$optimize' +osname='$osname' +osvers='$osvers' +package='$package' +perl='$perl' +pg='$pg' +plibpth='$plibpth' +pmake='$pmake' +pr='$pr' +prefix='$prefix' +prefixexp='$prefixexp' +privlib='$privlib' +privlibexp='$privlibexp' +prototype='$prototype' +rm='$rm' +rmail='$rmail' +runnm='$runnm' +sed='$sed' +sendmail='$sendmail' +sh='$sh' +shar='$shar' +sharpbang='$sharpbang' +shsharp='$shsharp' +signal_t='$signal_t' +sleep='$sleep' +smail='$smail' +so='$so' +sort='$sort' +spackage='$spackage' +spitshell='$spitshell' +src='$src' +startsh='$startsh' +strings='$strings' +submit='$submit' +sysman='$sysman' +tail='$tail' +tar='$tar' +tbl='$tbl' +test='$test' +timeincl='$timeincl' +timetype='$timetype' +touch='$touch' +tr='$tr' +troff='$troff' +uname='$uname' +uniq='$uniq' +usenm='$usenm' +usrinc='$usrinc' +uuname='$uuname' +vaproto='$vaproto' +vi='$vi' +voidflags='$voidflags' +warnflags='$warnflags' +xlibpth='$xlibpth' +yacc='$yacc' +yaccflags='$yaccflags' +zcat='$zcat' +zip='$zip' +EOT + +: add special variables +$test -f $src/patchlevel.h && \ +awk '/^#define/ {printf "%s=%s\n",$2,$3}' $src/patchlevel.h >>config.sh +echo "CONFIG=true" >>config.sh + +: propagate old symbols +if $test -f UU/config.sh; then + <UU/config.sh sort | uniq >UU/oldconfig.sh + sed -n 's/^\([a-zA-Z_0-9]*\)=.*/\1/p' config.sh config.sh UU/oldconfig.sh |\ + sort | uniq -u >UU/oldsyms + set X `cat UU/oldsyms` + shift + case $# in + 0) ;; + *) + cat <<EOM +Hmm...You had some extra variables I don't know about...I'll try to keep 'em... +EOM + echo "# Variables propagated from previous config.sh file." >>config.sh + for sym in `cat UU/oldsyms`; do + echo " Propagating $hint variable "'$'"$sym..." + eval 'tmp="$'"${sym}"'"' + echo "$tmp" | \ + sed -e "s/'/'\"'\"'/g" -e "s/^/$sym='/" -e "s/$/'/" >>config.sh + done + ;; + esac +fi + +: Finish up by extracting the .SH files +case "$alldone" in +exit) + $rm -rf UU + echo "Done." + exit 0 + ;; +cont) + ;; +'') + dflt='' + nostick=true + $cat <<EOM + +If you'd like to make any changes to the config.sh file before I begin +to configure things, do it as a shell escape now (e.g. !vi config.sh). + +EOM + rp="Press return or use a shell escape to edit config.sh:" + . UU/myread + nostick='' + case "$ans" in + '') ;; + *) : in case they cannot read + sh 1>&4 -c "$ans";; + esac + ;; +esac + +: if this fails, just run all the .SH files by hand +. ./config.sh + +echo " " +exec 1>&4 +. ./UU/extract + +if $contains '^depend:' [Mm]akefile >/dev/null 2>&1; then + dflt=y + case "$silent" in + true) ;; + *) + $cat <<EOM + +Now you need to generate make dependencies by running "make depend". +You might prefer to run it in background: "make depend > makedepend.out &" +It can take a while, so you might not want to run it right now. + +EOM + ;; + esac + rp="Run make depend now?" + . UU/myread + case "$ans" in + y*) + make depend && echo "Now you must run a make." + ;; + *) + echo "You must run 'make depend' then 'make'." + ;; + esac +elif test -f [Mm]akefile; then + echo " " + echo "Now you must run a make." +else + echo "Done." +fi + +$rm -f kit*isdone ark*isdone +$rm -rf UU + +: End of Configure + @@ -0,0 +1,204 @@ +Frequently Asked Questions about c2man +-------------------------------------- +By Graham Stoney (greyham@research.canon.com.au) +Last-modified: $Date: 2004-05-03 05:17:49 $ + +Q. Where can I get an up-to-date already patched copy of c2man? + +A. You can usually get the latest version via ftp: + ftp /pub/Unix/Util/c2man-2.0.*.tar.gz from dnpap.et.tudelft.nl + + Many thanks to Richard Kooijman <R.Kooijman@et.tudelft.nl> for providing + the ftp site, and keeping it up to date. The very latest version probably + won't be available until a short time after each patch is issued, since + we're relying on Richard's kindness to apply the patches and keep the tar + file up to date manually. + + You can also retrieve the latest version via the author's mail server, by + mailing to greyham@research.canon.com.au with a message like: + + Subject: Command + @PACK shar + @SH maildist - c2man 2.0 + + In reply, you can expect on the order of 9 or so mail messages, each around + 55 K long. You'll also have the extra hassle of unpacking it. Detailed + instructions on using the mail server can be obtained by mailing to the + same with address a message like: + + Subject: Command + @SH mailhelp - + + It should also be available in your local comp.sources.misc archive. The + versions in the alt.sources and comp.sources.reviewed archives are + obsolete. One day soon I'll be able to put it up for ftp here directly. + + +Q. How do I remove myself from the c2man discussion list? + +A. You should have received instructions on how to do this when you joined the + list. It's always wise to save a copy of the instructions you receive when + you join a mailing list, so that you can refer to them in future when you + wish to leave. + + To remove yourself from the c2man discussion list, send E-mail to: + listserv@research.canon.com.au + + With the message: + Subject: + unsubscribe c2man + + You should also be able to unsubscribe be running Configure again, and + asking it to unsubscribe you when it asks if you wish to subscribe to, or + unsubscribe from the mailing list. + + Note that the discussion list and the automatic patch notification/update + list are completely independant. + + +Q. How do I remove myself from the c2man automatic patch notification/update + list? + +A. If you don't wish to receive future notification or mailing of new patches, + you need to send a message to the author's personal mail server. In other + words, you send E-mail to: + greyham@research.canon.com.au + + With the message: + Subject: Command + @SH package - c2man + + You can also achieve the same effect by running Configure and asking that + future patches not be sent to you. You may also alter your notification + vs update preference via Configure. + + +Q. Can c2man handle C++? Is anyone working on a version that can? + +A. No, it can't handle C++, although there are other tools that do a similar + job for C++. A few people have expressed interest, but I don't know of + anyone who is actively working on it. If you look in the file C++autodoc in + the distribution, you'll get all the gory details. This file is always kept + up-to-date with the current state of play. + + +Q. How do I apply the official patches to c2man? + +A. You need the patch program, by Larry Wall. Chances are that it's already + installed on your system; if not, you could ask your system administrator + about getting it, or search for it using archie if you know how. Once + you've got it, follow the instructions in each of the patch headers. For + example, patch 10 says: + + Fix: From rn, say "| patch -p -N -d DIR", where DIR is your c2man source + directory. Outside of rn, say "cd DIR; patch -p -N <thisarticle". + If you don't have the patch program, apply the following by hand, + or get patch (version 2.0, latest patchlevel). + + In other words, 'cd' to your c2man directory and type: + patch -p -N < patch10 + + That should feed the patch into the patch program, which will apply it. You + also need to take care to follow the "After Patching" instructions in each + patch, and repeat this procedure for each patch. In general, you can apply + all the patches without having to re-run Configure after every patch set, + although you must run Configure after patch30, because it renames a few + files. You can apply patches 10 through 30 without having to run Configure + though. The "After patching" instructions assume that you apply patches as + they are issued; you don't generally have to do them multiple times if + you're applying a whole group of patch sets. + + +Q. Can c2man document structure fields automatically, like it does for enums? + +A. In short, No. + + This is a very logical extension; so much so in fact that I'd say it's + absence in the current version is a real deficiency. I'm not sure if you + always want the structure contents listed in the manual page for the + functions that use the type though, since some interfaces use "opaque" + types, where the structure contents is deliberately hidden from the user; + eg: the stdio FILE type. My thoughts were to generate a separate manual + page for each struct or type definition, with perhaps an option to include + the info in all the functional manual pages using that type, as now happens + with enum types now. + + Adding struct support to the current grammar should be fairly easy; mainly + by copying what it does with enum's; and it would certainly be very + worthwhile. I'd encourage anyone who'd like to see greater functionality in + c2man to feel free to add it in, and send me the diffs. + + Sorry, this doesn't work at present. It's probably the most serious + omission from the program; lots of people have asked about this. At + present, I don't know when it's likely to be fixed. I might suddenly get + really keen one day and do it -- otherwise, someone else will have to + implement it and send me the diffs. It shouldn't be too hard, but it's a + matter of making it a priority and getting around to actually doing it... + + +Q. Why do my functions using ``FILE *'' get documented with this weird + ``struct _iobuf'' type? + +A. Unfortunately, many systems use a #define for ``FILE'' in stdio.h to rename + it to ``struct _iobuf'', rather than using a typedef. Since c2man sees the + output of the pre-processor, ``FILE'' is long gone before it reads the + code, and so the documentation that it generates is misleading. + + Some stdio.h's perform this #define conditionally, so it may be possible to + pass certain -D flags to c2man which will cause stdio.h to use a typedef + instead, although this will be very system-dependant. + + I don't know of a really good solution to this problem, but I'd be + interested to hear if anyone has one. + + +Q. How can I get c2man recognise and document #defines? + +A. c2man doesn't recognise #defines at all, but you can rewrite it as an enum. + You do need to be able to change the type passed to an enum though, to get + c2man to recognise that it's what's being passed. C is very loose with its + enums, so it's still just as flexible as the #define case, and the types + look more self-documenting in the code too: + + enum Bits + { + BIT0 = 1, /* The first bit */ + BIT1 = 2, /* The next bit */ + BIT2 = 4 /* The last bit */ + }; + + /* set the bits! */ + void set_bits( enum Bits bits ); + + Gives you: + + set_bits(3) UNIX Programmer's Manual set_bits(3) + + NAME + set_bits - set the bits! + + SYNOPSIS + #include <set_bits.h> + + void set_bits(enum Bits bits); + + PARAMETERS + enum Bits bits + Possible values for an enum Bits are as follows: + BIT0 The first bit. + BIT1 The next bit. + BIT2 The last bit. + + DESCRIPTION + Set the bits!. + + +Q. But what if my function returns an errno-like code which can take many + values, but I'd like the documentation to list only those that the function + in question can actually generate? + +A. c2man makes a crude attempt at identifying token/value lists in the RETURNS + section from the comment describing the function. If the first thing on a + line in the RETURNS section is a single token followed a ':' or TAB + character, c2man interprets it as a token list and attempts to format it + accordingly. Have a look at the file "eg/reterrno.h" for an exmple. @@ -0,0 +1,74 @@ +INSTALLING c2man: + +1. For OS/2 and MSDOS, see the README.pc file in the pc directory. + + For all flavours of Unix, run Configure and answer the questions. + Most of the time the default answers should do the trick, but here's some + tips; If you're using... + +ANSI-C: Don't put the compiler in strict ISO/ANSI mode, 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. + +gcc: Do yourself a favour and use the latest version of gcc (>= 2.3.3). + If you use -Wall, also use -Wno-implicit if your system header files + don't declare basic stuff like fprintf in stdio.h (SunOS Release 4.1.2 + for example!). + +bison: If you get link errors reporting `alloca' as undefined, try specifying + -lPW when Configure asks `Any additional libraries?'. + +flex: You may need to specify -lfl when asked `Any additional libraries?'. + Flex versions 2.3 and older will cause c2man to hang if it encounters + an 8 bit character in the input file. If you require 8-bit character + support, use the flex flag '-8' (this is the default for flex 2.4). + +Sun: Sun yacc outputs old prototypes for malloc and free (says they return + char * instead of void *), which conflict with the correct ones flex 2.3 + generates if you use gcc. To make matters worse, gcc 2.2 errors on the + old prototypes even if you don't use flex. Use bison instead, or just + delete the incorrect prototypes on the first line of y.tab.c when the + make errors. If you use gcc 2.2, just upgrade your gcc. + +HPUX: When using cc, don't specify -Aa. Adding -D_HPUX_SOURCE may help too. + If you use the optional c89 compiler instead of standard cc, specify + -D_INCLUDE_POSIX_SOURCE -D_INCLUDE_XOPEN_SOURCE as your compiler flags. + +dbmalloc: + Specify a compiler flag of -DDBMALLOC (and optimization -g if you like) + and a library of -ldbmalloc for maximum checking. Don't use dbmalloc + with gcc and flex, or y.tab.c will fail to build; just use lex instead. + Don't use the shared version of your C library (eg: -lc_s), as + Configure will by default, lest you get multiply defined symbols. + +Interactive Unix 2.2: + If you use gcc (which requires -posix) ignore the warnings about + popen/pclose not being defined - apparently they aren't in POSIX. + +NeXT: In NeXTstep 3.1 and greater, `cc -C -E' is broken; tell Configure to + run the preprocessor as `/lib/cpp -C' or `cc -C -E -traditional-cpp'. + On NeXTstep 3.2, don't link with -lposix. It's busted. Sigh. + +Otherwise, If you get any errors or warnings, please report them to me (with +fixes if possible!). Don't worry too much about warnings from y.tab.c or +lex.yy.c though, since they're generally at the mercy of your yacc & lex. + +2. run make depend, if you didn't do it at the end of Configure. + +3. run make + +4. format (and read) the manual page in c2man.1. + The examples in it are automatically generated, so check that it looks OK. + +5. Optionally, run "make test > /dev/null" if you don't want to wade though + heaps of output, or "make test | nroff -man | more" if you do. This runs + c2man over the examples, and on its own source. The test is assumed to pass + if it doesn't error or dump core. + + If the preprocessor you told Configure that c2man is to use is different to + the one used to when compiling c2man itself, the test may fail because the + definitions in config.h may not match the preprocessor c2man will run + during the test and in normal use. + +6. As root, do "make install". This will install the c2man binary, the example + files and the manual page. diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..cb3907e --- /dev/null +++ b/MANIFEST @@ -0,0 +1,64 @@ +README Start here. +ChangeLog Log of recent changes. +INSTALL How to install it. +FAQ Frequently Asked Questions about c2man, with Answers. +C++autodoc An article describing how C++ support could be added. +MANIFEST This list of all files in the package. +Configure Automated installation script. +c2man.man Source for the manual pages; needs flattening. +Makefile.SH Produces Makefile. +catalog Free Compilers Catalog entry for c2man. +example.h First example header file for the manual page. +ctype_ex.h Second example header file for the manual page. +flatten.SH Produces sed script to flatten c2man.1 for installation. +fixexamp.in Commented sed script to format man page examples. +lex.l Lexical analyser. +grammar.y Parser. +config_h.SH Produces config.h, the portability header. +patchlevel.h The current patch level. +confmagic.h Further portability magic brought to you by metaconfig. +c2man.h Source code... +semantic.h +symbol.h +strconcat.h +manpage.h +enum.h +c2man.c +semantic.c +string.c +symbol.c +strconcat.c +manpage.c +enum.c +strappend.c +strappend.h +output.h Format-independant interface to backends. +nroff.c Backend for nroff output. +texinfo.c Backend for texinfo output. +latex.c Backend for LaTeX output. +html.c Backend for HTML output. +autodoc.c Backend for AutoDoc output. +libc/README.libc Info about library routines which may help. +libc/COPYING GPL to cover the GNU-derived library routines. +libc/getopt.c getopt support for OS/2, MSDOS & VMS. +libc/getopt.h ... +libc/getopt1.c ... +libc/alloca.c Portable alloca() routine. +eg/boxcomment.c Example input files... +eg/ccomment.h +eg/commentaft.c +eg/cppcomment.h +eg/dash.h +eg/ellipsis.c +eg/grouped.c +eg/multidecl.c +eg/namedash.h +eg/oldstyle.c +eg/returnerr.h +eg/returnlist.h +eg/sections.c +eg/simplesect.c +eg/surround.c +eg/underscore.h +eg/variable.c +eg/retdecl.c diff --git a/Makefile.SH b/Makefile.SH new file mode 100755 index 0000000..5f427cd --- /dev/null +++ b/Makefile.SH @@ -0,0 +1,172 @@ +case $CONFIG in +'') + if test ! -f config.sh; then + ln ../config.sh . || \ + ln ../../config.sh . || \ + ln ../../../config.sh . || \ + (echo "Can't find config.sh."; exit 1) + fi 2>/dev/null + . ./config.sh + ;; +esac +: This forces SH files to create target in same directory as SH file. +: This is so that make depend always knows where to find SH derivatives. +case "$0" in +*/*) cd `expr X$0 : 'X\(.*\)/'` ;; +esac +echo "Extracting Makefile (with variable substitutions)" +: This section of the file will have variable substitutions done on it. +: Move anything that needs config subs from !NO!SUBS! section to !GROK!THIS!. +: Protect any dollar signs and backticks that you do not want interpreted +: by putting a backslash in front. You may delete these comments. +$spitshell >Makefile <<!GROK!THIS! +# $Id: Makefile.SH,v 1.1 2004-05-03 05:17:48 behdad Exp $ +# +# UNIX makefile for manual page generator +# Note: any changes made here will be lost next time Configure is run!. + +CC=$cc $optimize +YACC=$yacc $yaccflags +LEX=$lex $lexflags +SED=$sed +CAT=$cat +RM=$rm +MV=$mv +CP=$cp +ECHO=$echo +MKDEP=$mkdep + +# where we get installed +bin=$binexp +privlib=$installprivlib + +mansrc=$mansrc +manext=$manext +CFLAGS=$ccflags +WARNFLAGS=$warnflags +LDFLAGS=$ldflags +LIBS=$libs $liblex $libyacc +!GROK!THIS! + +: In the following dollars and backticks do not need the extra backslash. +$spitshell >>Makefile <<'!NO!SUBS!' + +# As Larry said, "Grrrr" +SHELL=/bin/sh + +OSOURCES = config.h c2man.h semantic.h symbol.h strconcat.h \ + strappend.h manpage.h enum.h output.h lex.l grammar.y +DCSOURCES = c2man.c semantic.c string.c symbol.c strconcat.c \ + strappend.c manpage.c enum.c nroff.c texinfo.c latex.c html.c \ + autodoc.c +SOURCES = $(OSOURCES) $(DCSOURCES) +CSOURCES = $(DCSOURCES) y.tab.c +OBJECTS = c2man.o semantic.o string.o symbol.o y.tab.o strconcat.o \ + strappend.o manpage.o enum.o nroff.o texinfo.o latex.o html.o \ + autodoc.o +GENERATED = c2man example.inc ctype_ex.inc y.tab.c lex.yy.c y.output \ + fixexamp.sed flatten.sed + + +all: c2man c2man.1 + +install: all + $(CP) c2man $(bin) + $(CP) c2man.1 $(mansrc)/c2man.$(manext) + -mkdir $(privlib) + chmod 755 $(privlib) + -mkdir $(privlib)/eg + chmod 755 $(privlib)/eg + $(CP) eg/*.[chly] $(privlib)/eg + chmod 755 $(privlib)/eg/* + +uninstall: + $(RM) -f $(bin)/c2man $(mansrc)/c2man.$(manext) + $(RM) -f $(privlib)/eg/* + rmdir $(privlib)/eg + rmdir $(privlib) + +c2man: $(OBJECTS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LIBS) + +c2man.1: c2man.man c2man example.inc ctype_ex.inc flatten.sed + $(SED) -f flatten.sed < c2man.man > c2man.1 + +.c.o: + $(CC) $(CFLAGS) $(WARNFLAGS) -c $< + +y.tab.c: grammar.y + @$(ECHO) Expect 61 shift/reduce conflicts. + $(YACC) grammar.y + +# don't compile y.tab.c with all warnings; yacc/bison are not up to it. +y.tab.o: y.tab.c lex.yy.c + $(CC) $(CFLAGS) -c y.tab.c + +lex.yy.c: lex.l + $(LEX) -n lex.l + +example.inc: c2man example.h fixexamp.sed + ./c2man -o- example.h | $(SED) -f fixexamp.sed >example.inc + +ctype_ex.inc: c2man ctype_ex.h fixexamp.sed + ./c2man -o- -g ctype_ex.h | $(SED) -f fixexamp.sed >ctype_ex.inc + +fixexamp.sed: fixexamp.in + $(SED) -e '/^#/d' fixexamp.in > fixexamp.sed + +flatten.sed: flatten.SH config.sh + sh flatten.SH + +Makefile: Makefile.SH config.sh + sh Makefile.SH + +config.h: config_h.SH config.sh + sh config_h.SH + +TAGS: $(SOURCES) + etags -t $(SOURCES) + +clean: + $(RM) -f *.o *.s *.bak *~ *.log $(GENERATED) core + +distclean realclean: clean + $(RM) -f Makefile config.sh mkdep c2man.kit? + +lint: + lint -b $(CFLAGS) $(CSOURCES) + +print: + cpr $(SOURCES) | lpr -J'c2man' + +test: c2man + @echo "Running c2man over the examples..." 1>&2 + @for file in eg/*.[chly]; do ./c2man -v -o- $$file; done + @echo "Running c2man over its own source code..." 1>&2 + @for file in $(DCSOURCES); do ./c2man -v -o- $$file; done + @echo "Hmmm, test seemed to go OK." 1>&2 + +depend: + $(SED) -e '1,/^# DO NOT/!d' < Makefile > Makefile.new + $(MKDEP) $(DCSOURCES) >> Makefile.new + - test ! -f y.tab.c -o ! -f lex.yy.c || $(MKDEP) y.tab.c >> Makefile.new + $(MV) -f Makefile.new Makefile + +# y.tab.c dependancies updated manually since it won't exist yet when make +# depend is first run. +y.tab.o: c2man.h +y.tab.o: config.h +y.tab.o: confmagic.h +y.tab.o: enum.h +y.tab.o: manpage.h +y.tab.o: semantic.h +y.tab.o: strappend.h +y.tab.o: strconcat.h +y.tab.o: symbol.h + +# DO NOT DELETE THIS LINE! make depend DEPENDS ON IT! +c2man.o: + @echo "You must run a make depend first."; exit 1 +!NO!SUBS! +chmod 755 Makefile +$eunicefix Makefile @@ -0,0 +1,112 @@ + + c2man, Version 2 + by Graham Stoney + + Copyright (c) 1992-1996 + Canon Information Systems Research Australia + All rights reserved. + +C2man is an automatic documentation tool that extracts comments from C source +code to generate functional interface documentation in the same format as +sections 2 & 3 of the Unix Programmer's Manual. It requires minimal effort from +the programmer by looking for comments in the usual places near the objects +they document, rather than imposing a rigid function-comment syntax or +requiring that the programmer learn and use a typesetting language. Acceptable +documentation can often be generated from existing code with no modifications. + +The program can generate nroff/troff -man, TeXinfo, LaTeX or HTML output +directly, and should run on virtually any Unix-like system, OS/2, VMS, MSDOS or +Amiga systems. + +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. + +The manual page includes some automatically generated examples, which will be +missing if you try to read it before doing a make. Running make will generate +the complete manual page, which you can then copy around freely. + +c2man does not currently support C++, but if you think this would be worth +while, look in the file "C++autodoc" for information on how I envisage C++ +support could be added, and get ready to volunteer. Note that this isn't +related to the Commodore Amiga AutoDoc backend; the name's just a coincidence. + +The file "FAQ" in the c2man distribution contains answers to a number of +Frequently Asked Questions about c2man. + +By popular demand, there are a few trivial examples of different comment +styles in the "eg" directory. I'm open to submissions from users too. + + +There is a mailing list for c2man users; it is very low volume and has a very +low noise content. This is the preferred place to ask questions about the +program and discuss modifications and additions with the author and other +users, but please check in the file "FAQ" first before asking questions on the +list, in case I've already answered it. You are encouraged to join by sending +mail with no Subject: line to <listserv@research.canon.com.au> containing: + + SUBSCRIBE c2man Your name + +Where `Your name' should be replaced with your real name. +Messages for distribution to everyone on the list should be sent to: +<c2man@research.canon.com.au>. + + +The time I have available for c2man support is rather limited, but if it lacks +any features you require, feel free to Email me (preferably to the mailing list +address above) asking about it. Unless you request otherwise, I will probably +cc: to the list replies to any questions that I get mailed, to save me +answering them again for other people. I encourage you to go ahead and make +any changes you like and send me the diffs for inclusion in the next patch, but +it's a good idea to ask first in case someone already has the feature you want +in the works. In order for me to integrate your changes, they need to be +reasonably "clean", and you'll need to update manual page as appropriate. + +Please try to remember to include the c2man version number in any bug reports. +You can find it by running: c2man -V /dev/null + +If you'd like to be notified automatically about new releases and patches, +answer yes to the Configure question about sending mail to the author. + + +Special thanks for their direct and indirect contributions to c2man go to: + Larry Wall, Raphael Manfredi, Harlan Stenn and the "dist" team, for writing + various bits of metaconfig, which generated the Configure script. + + Darrel Hankerson for the OS/2 and MSDOS ports. + Rick Flower for the VMS port. + Stefan Ruppert for the Amiga port, and AutoDoc backend. + + Richard Kooijman for the LaTeX backend, and for fixing the TeXinfo backend. + Diab Jerius too, for more work on the TeXinfo backend. + Frank P.J. Ooms for the HTML backend. + + Vern Paxson for his suggestions on how to handle comment lexing better. + +Thanks to the following people for suggestions & bug fixes is long overdue: + Peter (P.) Barszczewski, Carlo Tarantola, Dennis Allison, + Philip Yzarn de Louraille, Jerry Lieberthal, Mats Ohrman, Stefan Zimmermann, + Dolf Grunbauer, Lele Gaifax, Carl R. Crawford, Jhon Honce, Chris Borchert, + Jerry E. Dunmire, Marty Leisner, Dan Forrest, Ken Weinert, Ken Poppleton, + Michael Hamilton, Thomas E. Dickey, Marco Nijdam. + +Finally, c2man owes a huge debt to the public domain program cproto, by +Chin Huang, from which the original code was derived. + +(Hmmm. This is beginning to sound like an Academy Awards night...) + + +See the file "INSTALL" for Unix installation instructions. + + +Graham Stoney greyham@research.canon.com.au +Mailing List for general c2man Questions & Answers c2man@research.canon.com.au diff --git a/autodoc.c b/autodoc.c new file mode 100644 index 0000000..0291fc2 --- /dev/null +++ b/autodoc.c @@ -0,0 +1,554 @@ +/* +** $PROJECT: +** +** $VER: autodoc.c 2.2 (25.01.95) +** +** by +** +** Stefan Ruppert , Windthorststra_e 5 , 65439 Flvrsheim , GERMANY +** +** (C) Copyright 1995 +** All Rights Reserved ! +** +** $HISTORY: +** +** 25.01.95 : 002.002 : changed to patchlevel 33 +** 22.01.95 : 000.001 : initial +*/ + +#include "c2man.h" +#include "manpage.h" +#include "output.h" +#include <ctype.h> + +#ifdef DEBUG +#define D(x) x +#else +#define D(x) +#endif + +#define MAX_TAG 10 + +static const int linelength = 79; +static const int tablength = 4; +static const int indentlength = 4; + +static int indent = 4; +static int list_indent = 0; +static int column = 0; +static int newline = FALSE; +static int breakline = FALSE; +static int see_also = FALSE; +static int fileend = FALSE; +static int acttable = -1; +static int tablemaxtag[MAX_TAG]; + +void autodoc_format(text) +const char *text; +{ + if(see_also) + { + if(column + ((text) ? strlen(text) + 2 : 1) > linelength) + { + putchar('\n'); + newline = TRUE; + } + } + + if(newline) + { + int i; + column = i = indent + list_indent; + for(; i ; i--) + putchar(' '); + newline = FALSE; + } +} + +void autodoc_text(text) +const char *text; +{ + int br = 1; + autodoc_format(text); + + if(!see_also || (br = strcmp(text,",\n"))) + { + if(see_also < 2) + { + put_string(text); + column += strlen(text); + } + } else if(!br) + { + column += 2; + put_string(", "); + } +} + +void autodoc_char(c) +const int c; +{ + if(c != '\f') + { + autodoc_format(NULL); + + if(c == '\t') + { + int i = tablength - (column % tablength); + column += i; + for(; i ; i--) + putchar(' '); + } else + { + if(see_also) + { + if(c == '(') + see_also++; + else if(c == ')') + see_also--; + } + + putchar(c); + column++; + } + + if((newline = (c == '\n'))) + column = 0; + } +} + +void autodoc_comment() { } + +void autodoc_header(firstpage, input_files, grouped, name, terse, section) +ManualPage *firstpage; +int input_files; +boolean grouped; +const char *name; +const char *terse; +const char *section; +{ + const char *basename = strrchr(firstpage->sourcefile, '/'); + int len; + int spc; + + fileend = FALSE; + + if(basename && *basename == '/') + basename++; + + len = ((basename) ? strlen(basename) + 1 : 0) + strlen(name); + spc = linelength - 2 * len; + + see_also = FALSE; + + if(basename) + { + autodoc_text(basename); + autodoc_char('/'); + } + autodoc_text(name); + + if(spc > 0) + { + while(spc) + { + autodoc_char(' '); + spc--; + } + if(basename) + { + autodoc_text(basename); + autodoc_char('/'); + } + autodoc_text(name); + } else + { + const char *ptr = name; + len = linelength - 1 - len; + + while(len) + { + if(basename && *basename) + { + autodoc_char(*basename); + basename++; + } else + { + if(ptr == name && basename) + autodoc_char('/'); + else + { + autodoc_char(*ptr); + ptr++; + } + } + len--; + } + } + + put_string("\n"); +} + +void autodoc_dash() { put_string("-"); } + +void autodoc_section(name) +const char *name; +{ + D((fprintf(stderr,"section : %s\n",name))); + newline = FALSE; + see_also = FALSE; + put_string("\n"); + if(!strcmp(name,"DESCRIPTION")) + name = "FUNCTION"; + else if(!strcmp(name,"PARAMETERS")) + name = "INPUTS"; + else if(!strcmp(name,"RETURNS")) + name = "RESULT"; + else if(!strcmp(name,"SEE ALSO")) + see_also = TRUE; + + put_string(" "); + autodoc_text(name); + indent = 8; + list_indent = 0; + autodoc_char('\n'); +} + +void autodoc_sub_section(name) +const char *name; +{ + autodoc_text(name); + indent = 12; +} + +void autodoc_break_line() +{ + breakline = TRUE; +} + +void autodoc_blank_line() +{ + autodoc_char('\n'); +} + +void autodoc_code_start() { } +void autodoc_code_end() { } + +void autodoc_code(text) +const char *text; +{ + autodoc_text(text); +} + +void autodoc_tag_entry_start() +{ + if(list_indent > 0) + { + autodoc_char('\n'); + list_indent -= indentlength; + } +} +void autodoc_tag_entry_start_extra() +{ + if(list_indent > 0) + { + autodoc_char('\n'); + list_indent -= indentlength; + } +} +void autodoc_tag_entry_end() +{ + list_indent += indentlength; + autodoc_char('\n'); +} +void autodoc_tag_entry_end_extra(text) +const char *text; +{ + put_string("\" \"\t("); + autodoc_text(text); + put_string(")\"\n"); + list_indent += indentlength; +} + +void autodoc_table_start(longestag) +const char *longestag; +{ + if(acttable < MAX_TAG - 1) + { + acttable++; + tablemaxtag[acttable] = strlen(longestag); + } + + indent += indentlength; + newline = TRUE; +} + +void autodoc_table_entry(name, description) +const char *name; +const char *description; +{ + int i = tablemaxtag[acttable] - strlen(name) + 1; + + autodoc_code(name); + while(i > 0) + { + putchar(' '); + i--; + } + putchar('-'); + putchar(' '); + + if (description) + output_comment(description); + else + autodoc_char('\n'); +} + +void autodoc_table_end() +{ + if(acttable > -1) + acttable--; + + autodoc_char('\n'); + indent -= indentlength; + if(list_indent > 0) + list_indent -= indentlength; +} + +void autodoc_indent() +{ + int i; + for(i = indent + list_indent; i ; i--) + autodoc_char(' '); +} + +void autodoc_list_start() +{ + indent += indentlength; + newline = TRUE; +} + +void autodoc_list_entry(name) +const char *name; +{ + autodoc_code(name); +} + +void autodoc_list_separator() { put_string(" ,"); } +void autodoc_list_end() { autodoc_char('\n'); autodoc_table_end(); } + +void autodoc_include(filename) +const char *filename; +{ + +} + +void autodoc_terse_sep() +{ + autodoc_char(' '); + autodoc_dash(); + autodoc_char(' '); +} + +void autodoc_name(name) +const char *name; +{ + if(name) + autodoc_text(name); + else + autodoc_section("NAME"); +} + +void autodoc_file_end() +{ + if(!fileend) + putchar('\f'); + fileend = TRUE; + newline = FALSE; +} + +/* ideally, this should be made aware of embedded autodoc commands */ +void autodoc_description(text) +const char *text; +{ + enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE; + boolean new_line = TRUE; + + /* correct punctuation a bit as it goes out */ + for (;*text;text++) + { + int c = *text; + + if (new_line && (c == '-' || c == '*')) + { + output->break_line(); + state = CAPITALISE; + } + else if (c == '.') + state = PERIOD; + else if (isspace(c) && state == PERIOD) + state = CAPITALISE; + else if (isalnum(c)) + { + if (islower(c) && state == CAPITALISE) c = toupper(c); + state = TEXT; + } + + output->character(c); + new_line = c == '\n'; + } + + /* do a full stop if there wasn't one */ + if (state == TEXT) output->character('.'); +} + +/* ideally, this should be made aware of embedded autodoc commands */ +void +autodoc_returns(comment) +const char *comment; +{ + enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE; + char lastchar = '\n'; + boolean tag_list_started = FALSE; + + /* for each line... */ + while (*comment) + { + boolean tagged = FALSE; + + { + const char *c = comment; + + /* search along until the end of a word */ + while (*c && *c != ':' && !isspace(*c)) + c++; + + /* skip all spaces or tabs after the first word */ + while (*c && *c != '\n') + { + if (*c == '\t' || *c == ':') + { + tagged = TRUE; + break; + } + else if (!isspace(*c)) + break; + + c++; + } + } + + /* is it tagged?; explicitly reject dot commands */ + if (tagged) + { + /* output lingering newline if necessary */ + if (lastchar != '\n') + { + if (state == TEXT && !ispunct(lastchar)) output->character('.'); + output->character(lastchar = '\n'); + } + + if (!tag_list_started) + { + output->tag_list_start(); + tag_list_started = TRUE; + } + + /* output the taggy bit */ + output->tag_entry_start(); + while (*comment && *comment != ':' && !isspace(*comment)) + output->character(*comment++); + output->tag_entry_end(); + + /* skip any extra tabs or spaces */ + while (*comment == ':' || (isspace(*comment) && *comment != '\n')) + comment++; + + state = CAPITALISE; + } + + /* terminate the previous line if necessary */ + if (lastchar != '\n') output->character(lastchar = '\n'); + + /* correct punctuation a bit as the line goes out */ + for (;*comment && *comment != '\n'; comment++) + { + char c = *comment; + + if (c == '.') + state = PERIOD; + else if (isspace(c) && state == PERIOD) + state = CAPITALISE; + else if (isalnum(c)) + { + if (islower(c) && state == CAPITALISE && fixup_comments) + c = toupper(c); + state = TEXT; + } + + output->character(lastchar = c); + } + + /* if it ended in punctuation, just output the nl straight away. */ + if (ispunct(lastchar)) + { + if (lastchar == '.') state = CAPITALISE; + output->character(lastchar = '\n'); + } + + if (*comment) comment++; + } + + /* output lingering newline if necessary */ + if (lastchar != '\n') + { + if (state == TEXT && !ispunct(lastchar) && fixup_comments) + output->character('.'); + output->character('\n'); + } + + if (tag_list_started) + output->tag_list_end(); +} + + +struct Output autodoc_output = +{ + autodoc_comment, + autodoc_header, + autodoc_dash, + autodoc_section, + autodoc_sub_section, + autodoc_break_line, + autodoc_blank_line, + autodoc_code_start, + autodoc_code_end, + autodoc_code, + dummy, /* autodoc_tag_list_start */ + dummy, /* autodoc_tag_list_end */ + autodoc_tag_entry_start, + autodoc_tag_entry_start_extra, + autodoc_tag_entry_end, + autodoc_tag_entry_end_extra, + autodoc_table_start, + autodoc_table_entry, + autodoc_table_end, + autodoc_indent, + autodoc_list_start, + autodoc_code, /* autodoc_list_entry */ + autodoc_list_separator, + autodoc_list_end, + autodoc_include, + autodoc_file_end, /* autodoc_file_end */ + autodoc_text, + autodoc_char, + NULL, /* autodoc_parse_option */ + dummy, /* autodoc_print_options */ + autodoc_name, + autodoc_terse_sep, + autodoc_text, /* autodoc_reference */ + autodoc_text, /* autodoc_emphasized */ + autodoc_description, + autodoc_returns +}; + @@ -0,0 +1,928 @@ +/* $Id: c2man.c,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * + * C Manual page generator. + * Reads C source code and outputs manual pages. + */ +#include <ctype.h> +#include <errno.h> + +#include "c2man.h" +#include "enum.h" +#include "strconcat.h" +#include "strappend.h" +#include "manpage.h" +#include "output.h" +#include "patchlevel.h" + +#ifdef I_FCNTL +#include <fcntl.h> +#endif + +#ifdef I_SYS_FILE +#include <sys/file.h> +#endif + +#include <sys/stat.h> +#include <signal.h> + +/* getopt declarations */ +extern int getopt(); +extern char *optarg; +extern int optind; + +/* lex declarations */ +extern FILE *yyin; /* lex input stream */ + +/* Name of the program */ +const char *progname = "c2man"; + +/* Program options */ + +/* TRUE if static declarations are also output. */ +boolean static_out = FALSE; + +/* TRUE if variable declarations are output. */ +boolean variables_out = FALSE; + +/* TRUE if formal parameter promotion is enabled. */ +boolean promote_param = TRUE; + +/* String output before prototype declaration specifiers */ +const char *decl_spec_prefix = ""; + +/* String output before prototype declarator */ +const char *declarator_prefix = " "; + +/* String output after prototype declarator */ +const char *declarator_suffix = "\n"; + +/* String output before the first parameter in a function prototype */ +const char *first_param_prefix = "\n\t"; + +/* String output before each subsequent parameter in a function prototype */ +const char *middle_param_prefix = "\n\t"; + +/* String output after the last parameter in a function prototype */ +const char *last_param_suffix = "\n"; + +/* Directory to write output files in */ +char *output_dir = NULL; + +/* Name of the manual */ +char *manual_name = NULL; + +/* Section for manual page */ +const char *manual_section = NULL; + +/* prefix for generated #include lines */ +char *header_prefix = NULL; + +/* list of include file specified by user */ +IncludeFile *first_include; +static IncludeFile **last_next_include = &first_include; + +/* list of excluded sections specified by user */ +ExcludeSection *first_excluded_section; +static ExcludeSection **last_next_excluded_section = &first_excluded_section; + +/* TRUE if c2man should attempt to fixup comment sections */ +boolean fixup_comments = TRUE; + +/* do we group related stuff into one file? */ +boolean group_together; + +/* was terse description read from file or command line option? */ +boolean terse_specified; + +/* terse description when grouped together */ +char *group_terse = NULL; + +/* should we always document parameters, even if it's only "Not Documented" */ +boolean always_document_params = TRUE; + +/* look for a function def comment at the start of the function body */ +boolean look_at_body_start = FALSE; + +/* only look for a function def comment at the start of the function body */ +boolean body_start_only = FALSE; + +/* default output info for each object type */ +struct Output_Object_Info output_object[_OBJECT_NUM] = +{ +#if 0 + {'c', "class"}, + {'s', "struct"}, + {'e', "enum"}, + {'t', "typedef"}, +#endif + {'f', "function"}, + {'v', "variable"}, + {'F', "static function"}, + {'V', "static variable"} +}; + +/* Include file directories */ +#ifdef MSDOS +int num_inc_dir = 1; +const char *inc_dir[MAX_INC_DIR] = { ".\\" }; +#else +#ifdef AMIGA +int num_inc_dir = 1; +const char *inc_dir[MAX_INC_DIR] = { "include:" }; +#else +int num_inc_dir = 2; +const char *inc_dir[MAX_INC_DIR] = { "./", "/usr/include/" }; +#endif +#endif + +/* total number of errors encountered */ +int errors; + +/* name of the base file being processed; NULL = stdin */ +const char *basefile; +Time_t basetime; /* modification time of base file */ +boolean inbasefile; /* are we parsing in that base file? */ + +/* is the base file a header file? */ +boolean header_file; + +#ifdef AMIGA +struct Output *output = &autodoc_output; +const char *default_section = "doc"; +#else +/* use nroff output by default */ +struct Output *output = &nroff_output; +const char *default_section = "3"; +#endif + +/* should we generate the output file named after the input file? */ +boolean use_input_name = FALSE; + +/* should we generate embeddable files? */ +boolean make_embeddable = FALSE; + +#define USE_CPP +#ifdef USE_CPP +const char *cpp_cmd = CPP_FILE_COM; +#if defined(MSDOS) +#include "popen.h" +#define popen(c,m) os_popen(c,m) +#define pclose(f) os_pclose(f) +#else +#if defined (_MSC_VER) +#define popen(c,m) _popen(c,m) +#define pclose(f) _pclose(f) +#endif +#endif +#endif + +boolean verbose = FALSE; + +/* can cpp read standard input? */ +static boolean cppcanstdin +#ifdef CPP_CAN_STDIN + = 1 +#endif +; +/* does cpp ignore header files */ +static boolean cppignhdrs +#ifdef CPP_IGN_HDRS + = 1 +#endif +; + +/* nifty little function for handling I/O errors */ +void my_perror(action, filename) +const char *action, *filename; +{ + int err = errno; + fprintf(stderr,"%s: %s ", progname, action); + errno = err; + perror(filename); +} + +/* write the #include lines as specified by the user */ +void print_includes(f) +FILE *f; +{ + IncludeFile *incfile; + + for (incfile = first_include; incfile; incfile=incfile->next) + { + char *name = incfile->name; + boolean surrounded = *name == '"' || *name == '<'; + + fputs("#include ", f); + if (!surrounded) fputc('<',f); + fputs(name, f); + if (!surrounded) fputc('>',f); + fputc('\n',f); + } +} + +void outmem() +{ + fprintf(stderr,"%s: Out of memory!\n", progname); + exit(1); +} + +#ifndef DBMALLOC +void *safe_malloc(size) +size_t size; +{ + void *mem; + + if ((mem = (void *)malloc(size)) == NULL) + outmem(); + + return mem; +} +#endif + +/* Replace any character escape sequences in a string with the actual + * characters. Return a pointer to malloc'ed memory containing the result. + * This function knows only a few escape sequences. + */ +static char * +escape_string (src) +char *src; +{ + char *result, *get, *put; + + result = strduplicate(src); + put = result; + get = src; + while (*get != '\0') { + if (*get == '\\') { + switch (*(++get)) { + case 'n': + *put++ = '\n'; + ++get; + break; + case 't': + *put++ = '\t'; + ++get; + break; + default: + if (*get != '\0') + *put++ = *get++; + } + } else { + *put++ = *get++; + } + } + *put = *get; + return result; +} + +/* Output usage message and exit. + */ +static void +usage () +{ + int i; + + fprintf(stderr, "usage: %s [ option ... ] [ file ... ]\n", progname); + fputs(" -o directory\twrite output files in directory\n",stderr); + fputs(" -p\t\tdisable prototype promotion\n", stderr); + fputs(" -s\t\toutput static declarations\n", stderr); + fputs(" -v\t\toutput variable declarations\n", stderr); + fputs(" -k\t\tdon't attempt to fixup comments\n", stderr); + fputs(" -b\t\tlook for descriptions at top of function bodies\n", stderr); + fputs(" -B\t\tonly look for descriptions by applying -b\n", stderr); + fputc('\n', stderr); + fputs(" -i incfile\n", stderr); + fputs(" -i \"incfile\"\n", stderr); + fputs(" -i <incfile>\tadd #include for incfile to SYNOPSIS\n", + stderr); + fputc('\n', stderr); + fputs(" -H prefix\tspecify prefix for #include in SYNOPSIS\n", stderr); + fputc('\n', stderr); + fputs(" -g\n", stderr); + fputs(" -G terse\tgroup info from each file into a single page\n", + stderr); + fputs(" -e\t\tmake embeddable files\n", stderr); + fputc('\n', stderr); + fputs(" -l ", stderr); +#ifdef HAS_LINK + fputs("h|", stderr); +#endif +#ifdef HAS_SYMLINK + fputs("s|", stderr); +#endif + fputs("f|n|r\t", stderr); + fputs("linking for grouped pages: ", stderr); +#ifdef HAS_LINK + fputs("hard, ", stderr); +#endif +#ifdef HAS_SYMLINK + fputs("soft, ", stderr); +#endif + fputs("file, none or remove\n", stderr); + fputs(" -n\t\tName output file after input source file\n", stderr); + fputs(" -L\t\tLazy: Be silent about undocumented parameters\n", + stderr); + + fputs(" -T n|l|h|t|a[,options]\tselect typesetting output format: nroff, LaTeX, HTML ,TeXinfo or AutoDoc\n", + stderr); + nroff_output.print_options(); + latex_output.print_options(); + html_output.print_options(); + texinfo_output.print_options(); + autodoc_output.print_options(); + + fputs(" -M name\tset name of the manual in which the page goes\n", + stderr); + fputs(" -x section\texclude section from ouput\n", stderr); + fputc('\n', stderr); + fputs(" -D name[=value]\n", stderr); + fputs(" -U name\n", stderr); + fputs(" -I directory\tC preprocessor options\n", stderr); + fputc('\n', stderr); + fputs(" -F template\tset prototype template in the form ", stderr); + fputs("\"int f (a, b)\"\n",stderr); + fputs(" -P preprocessor\tAlternate C preprocessor ", stderr); + fputs("(e.g., \"gcc -E -C\")\n", stderr); + fputs(" -V\t\tbe verbose and print version information\n", stderr); + fputs(" -S section\tset the section for the manual page (default = 3)\n", + stderr); + fputs(" -O ", stderr); + for (i = 0; i < _OBJECT_NUM; i++) + fputc(output_object[i].flag, stderr); + fputs("[subdir][.ext]", stderr); + fputs("\tOutput control over different object types:\n\t\t", stderr); + for (i = 0; i < _OBJECT_NUM; i++) + { + fputs(output_object[i].name, stderr); + if (i <= _OBJECT_NUM - 2) + fprintf(stderr,i == _OBJECT_NUM-2 ? " or " : ", "); + } + fputs(".\n", stderr); + exit(1); +} + +/* name of the temporary file; kept here so we can blast it if hit with ctrl-C + */ +static char temp_name[20]; +Signal_t (*old_interrupt_handler)(); + +/* ctrl-C signal handler for use when we have a temporary file */ +static Signal_t interrupt_handler(sig) +int sig; +{ + unlink(temp_name); + exit(128 + sig); +} + +/* open a unique temporary file. + * To be universally accepted by cpp's, the file's name must end in .c; so we + * can't use mktemp, tmpnam or tmpfile. + * returns an open stream & sets ret_name to the name. + */ +FILE *open_temp_file() +{ + int fd; + long n = getpid(); + FILE *tempf; + boolean remove_temp_file(); + + /* keep generating new names until we hit one that does not exist */ + do + { + /* ideally we'd like to put the temporary file in /tmp, but it must go + * in the current directory because when cpp processes a #include, it + * looks in the same directory as the file doing the include; so if we + * use /tmp/blah.c to fake reading fred.h via `#include "fred.h"', cpp + * will look for /tmp/fred.h, and fail. + */ + sprintf(temp_name,"c2man%ld.c",n++ % 1000000); + } + while((fd = +#ifdef HAS_OPEN3 + open(temp_name,O_WRONLY|O_CREAT|O_EXCL,0666) +#else + creat(temp_name,O_EXCL|0666) /* do it the old way */ +#endif + ) == -1 + && errno == EEXIST); + + /* install interrupt handler to remove the temporary file */ + old_interrupt_handler = signal(SIGINT, interrupt_handler); + + /* convert it to a stream */ + if ((fd == -1 && errno != EEXIST) || (tempf = fdopen(fd, "w")) == NULL) + { + my_perror("error fdopening temp file",temp_name); + remove_temp_file(); + return NULL; + } + + return tempf; +} + +/* remove the temporary file & restore ctrl-C handler. + * returns FALSE in the event of failure. + */ +boolean remove_temp_file() +{ + int ok = unlink(temp_name) == 0; /* this should always succeed */ + signal(SIGINT, old_interrupt_handler); + return ok; +} + +/* process the specified source file through the pre-processor. + * This is a lower level routine called by both process_stdin and process_file + * to actually get the work done once any required temporary files have been + * generated. + */ +int process_file_directly(base_cpp_cmd, name) +const char *base_cpp_cmd; +const char *name; +{ + char *full_cpp_cmd; + +#ifdef DEBUG + fprintf(stderr,"process_file_directly: %s, %s\n", base_cpp_cmd, name); +#endif + +#ifdef USE_CPP + full_cpp_cmd = strconcat(base_cpp_cmd, " ", name, NULLCP); + if (verbose) + fprintf(stderr,"%s: running `%s'\n", progname, full_cpp_cmd); + + if ((yyin = popen(full_cpp_cmd, "r")) == NULL) { + my_perror("error running", base_cpp_cmd); + free(full_cpp_cmd); + return 0; + } +#else + if (verbose) fprintf(stderr,"%s: reading %s\n", progname, name); + if (name && freopen(name, "r", yyin) == NULL) + { + my_perror("cannot open", name); + return 0; + } +#endif + + parse_file(name); + +#ifdef USE_CPP + free(full_cpp_cmd); + if (pclose(yyin) & 0xFF00) + return 0; +#else + if (fclose(yyin)) + { + my_perror("error closing", name); + return 0; + } +#endif + + return !errors; +} + +/* process a specified file */ +int process_file(base_cpp_cmd, name) +const char *base_cpp_cmd; +const char *name; +{ + char *period; + struct stat statbuf; + +#ifdef DEBUG + fprintf(stderr,"process_file: %s, %s\n", base_cpp_cmd, name); +#endif + basefile = name; + header_file = (period = strrchr(name,'.')) && + (period[1] == 'h' || period[1] == 'H'); + + /* use the file's date as the date in the manual page */ + if (stat(name,&statbuf) != 0) + { + my_perror("can't stat", name); + return 0; + } + basetime = statbuf.st_mtime; + + /* should we do this via a temporary file? + * Only if it's a header file and either CPP ignores them, or the user + * has specified files to include. + * + * For HP/Apollo (SR10.3, CC 6.8), we must always use a temporary file, + * because its compiler recognizes the special macro "__attribute(p)", + * which we cannot redefine in the command line because it has parameters. + */ +#ifndef apollo + if (header_file && (cppignhdrs || first_include)) +#endif + { + FILE *tempf; + int ret; + + if (verbose) + fprintf(stderr, "%s: preprocessing via temporary file\n", progname); + + if ((tempf = open_temp_file()) == NULL) + return 0; + + print_includes(tempf); + if (verbose) print_includes(stderr); + +#ifdef apollo + fprintf(tempf,"#define __attribute(p)\n", basefile); +#endif + fprintf(tempf,"#include \"%s\"\n", basefile); + if (verbose) fprintf(stderr,"#include \"%s\"\n", basefile); + + if (fclose(tempf) == EOF) + { + my_perror("error closing temp file", temp_name); + remove_temp_file(); + return 0; + } + + /* since we're using a temporary file, it's not the base file */ + inbasefile = 0; + ret = process_file_directly(base_cpp_cmd, temp_name); + remove_temp_file(); + return ret; + } + + /* otherwise, process it directly */ + inbasefile = 1; + + return process_file_directly(base_cpp_cmd,name); +} + +/* process the thing on the standard input */ +int process_stdin(base_cpp_cmd) +const char *base_cpp_cmd; +{ + if (isatty(fileno(stdin))) + fprintf(stderr,"%s: reading standard input\n", progname); + + header_file = 0; /* assume it's not since it's from stdin */ + basefile = NULL; + + /* use the current date in the man page */ + basetime = time((Time_t *)NULL); + + inbasefile = 1; /* reading stdin, we start in the base file */ + + /* always use a temp file if the preprocessor can't read stdin, otherwise + * only use one if the user specified files for inclusion. + */ + if (!cppcanstdin || first_include) /* did user specify include files? */ + { + FILE *tempf; + int c, ret; + + if (verbose) + fprintf(stderr,"%s: reading stdin to a temporary file\n", progname); + + if ((tempf = open_temp_file()) == NULL) + return 0; + + print_includes(tempf); + if (verbose) print_includes(stderr); + fprintf(tempf,"#line 1 \"stdin\"\n"); + + while ((c = getchar()) != EOF) + putc(c,tempf); + + if (fclose(tempf) == EOF) + { + my_perror("error closing temp file", temp_name); + remove_temp_file(); + return 0; + } + ret = process_file_directly(base_cpp_cmd, temp_name); + remove_temp_file(); + return ret; + } + else + { + char *full_cpp_cmd = strconcat(base_cpp_cmd," ", CPP_STDIN_FLAGS, + NULLCP); + + if (verbose) + fprintf(stderr,"%s: running `%s'\n", progname, full_cpp_cmd); + + if ((yyin = popen(full_cpp_cmd, "r")) == NULL) { + my_perror("error running", full_cpp_cmd); + return 0; + } + + parse_file(basefile); + + free(full_cpp_cmd); + if (pclose(yyin) & 0xFF00) + return 0; + + return !errors; + } +} + +int +main (argc, argv) +int argc; +char **argv; +{ + int i, c, ok = 0; + char *s, cbuf[2]; + const char *base_cpp_cmd; + IncludeFile *includefile; + ExcludeSection *excludesection; + char *cpp_opts; +#ifdef HAS_LINK + enum LinkType link_type = LINK_HARD; /* for -g/G */ +#else + enum LinkType link_type = LINK_FILE; +#endif + +#ifdef YYDEBUG + extern int yydebug; +#endif + + /* initialise CPP options with -D__C2MAN__ */ + cbuf[0] = VERSION + '0'; + cbuf[1] = '\0'; +#ifdef VMS + cpp_opts = strconcat("-\"D__C2MAN__=", cbuf, "\"",NULLCP); +#else + cpp_opts = strconcat("-D__C2MAN__=", cbuf, NULLCP); +#ifdef NeXT + cpp_opts = strappend(cpp_opts, " -D_NEXT_SOURCE", NULLCP); +#endif /* !NeXT */ +#endif /* !VMS */ + + /* Scan command line options. */ + while ((c = getopt(argc, argv, "P:D:F:I:psU:Vvo:eM:H:G:gi:x:S:l:LT:nO:kbB")) + != EOF) + { + switch (c) { + case 'I': + case 'D': + case 'U': + cbuf[0] = c; cbuf[1] = '\0'; + if (cpp_opts) + cpp_opts = strappend(cpp_opts," -",cbuf,optarg,NULLCP); + else + cpp_opts = strconcat("-",cbuf,optarg,NULLCP); + break; + case 'P': + cpp_cmd = optarg; + + /* with no better info to go on, we have to assume that this + * preprocessor is minimally capable. + */ + cppcanstdin = 0; + cppignhdrs = 1; + break; + case 'G': + group_terse = optarg; + terse_specified = TRUE; + /* FALLTHROUGH */ + case 'g': + group_together = TRUE; + break; + case 'F': + s = escape_string(optarg); + + decl_spec_prefix = s; + while (*s != '\0' && isascii(*s) && !isalnum(*s)) ++s; + if (*s == '\0') usage(); + *s++ = '\0'; + while (*s != '\0' && isascii(*s) && isalnum(*s)) ++s; + if (*s == '\0') usage(); + + declarator_prefix = s; + while (*s != '\0' && isascii(*s) && !isalnum(*s)) ++s; + if (*s == '\0') usage(); + *s++ = '\0'; + while (*s != '\0' && isascii(*s) && isalnum(*s)) ++s; + if (*s == '\0') usage(); + + declarator_suffix = s; + while (*s != '\0' && *s != '(') ++s; + if (*s == '\0') usage(); + *s++ = '\0'; + + first_param_prefix = s; + while (*s != '\0' && isascii(*s) && !isalnum(*s)) ++s; + if (*s == '\0') usage(); + *s++ = '\0'; + while (*s != '\0' && *s != ',') ++s; + if (*s == '\0') usage(); + + middle_param_prefix = ++s; + while (*s != '\0' && isascii(*s) && !isalnum(*s)) ++s; + if (*s == '\0') usage(); + *s++ = '\0'; + while (*s != '\0' && isascii(*s) && isalnum(*s)) ++s; + if (*s == '\0') usage(); + + last_param_suffix = s; + while (*s != '\0' && *s != ')') ++s; + *s = '\0'; + + break; + case 'p': + promote_param = FALSE; + break; + case 's': + static_out = TRUE; + break; + case 'V': + verbose = TRUE; + fprintf(stderr, "%s: Version %d, Patchlevel %d\n", + progname, VERSION, PATCHLEVEL); + break; + case 'v': + variables_out = TRUE; + break; + case 'k': + fixup_comments = FALSE; + break; + case 'o': + output_dir = optarg; + break; + case 'M': + manual_name = optarg; + break; + case 'H': + header_prefix = optarg; + break; + case 'i': + *last_next_include = includefile = + (IncludeFile *)safe_malloc(sizeof *includefile); + includefile->name = optarg; + includefile->next = NULL; + last_next_include = &includefile->next; + break; + case 'x': + *last_next_excluded_section = excludesection = + (ExcludeSection *)safe_malloc(sizeof *excludesection); + excludesection->name = optarg; + excludesection->next = NULL; + last_next_excluded_section = &excludesection->next; + break; + case 'S': + manual_section = optarg; + break; + case 'l': + switch(optarg[0]) + { +#ifdef HAS_LINK + case 'h': link_type = LINK_HARD; break; +#endif +#ifdef HAS_SYMLINK + case 's': link_type = LINK_SOFT; break; +#endif + case 'f': link_type = LINK_FILE; break; + case 'n': link_type = LINK_NONE; break; + case 'r': link_type = LINK_REMOVE;break; + default: usage(); + } + break; + case 'e': + make_embeddable = TRUE; + break; + case 'n': + use_input_name = TRUE; + break; + case 'L': + always_document_params = FALSE; + break; + case 'T': + switch(optarg[0]) + { + case 'n': output = &nroff_output; default_section = "3"; + break; + case 'l': output = &latex_output; default_section = "tex"; + break; + case 't': output = &texinfo_output; default_section = "texi"; + break; + case 'h': output = &html_output; default_section = "html"; + break; + case 'a': output = &autodoc_output; default_section = "doc"; + break; + default: usage(); + } + s = strtok(&optarg[1], ","); + if (s && *output->parse_option == NULL) usage(); + while(s) + { + if (output->parse_option(s)) usage(); + s = strtok(NULL, ","); + } + break; + case 'O': + for (i = 0; i < _OBJECT_NUM; i++) + if (output_object[i].flag == optarg[0]) + break; + + if (i == _OBJECT_NUM) + { + fprintf(stderr,"%s: -O option must specify one of:\n\t", + progname); + for (i = 0; i < _OBJECT_NUM; i++) + { + fprintf(stderr,"%c (%s)", output_object[i].flag, + output_object[i].name); + if (i <= _OBJECT_NUM - 2) + fprintf(stderr,i == _OBJECT_NUM-2 ? " or " : ", "); + } + fprintf(stderr, ".\n"); + exit(1); + } + + if ((s = strchr(++optarg,'.'))) /* look for an extension */ + { + output_object[i].subdir = alloc_string(optarg, s); + output_object[i].extension = strduplicate(s+1); + } + else + output_object[i].subdir = strduplicate(optarg); + + break; + case 'b': + look_at_body_start = TRUE; + break; + case 'B': + body_start_only = TRUE; + look_at_body_start = TRUE; + break; + case '?': + default: + usage(); + } + } + + /* make sure we have a manual section */ + if (manual_section == NULL) manual_section = default_section; + +#ifdef MALLOC_DEBUG + getchar(); /* wait so we can start up NeXT MallocDebug tool */ +#endif +#ifdef YYDEBUG + yydebug = 1; +#endif + + if (cpp_opts) + { + base_cpp_cmd = strconcat(cpp_cmd, " ", cpp_opts, NULLCP); + free(cpp_opts); + } + else + base_cpp_cmd = cpp_cmd; + + if (optind == argc) { + if (use_input_name) + { + fprintf(stderr,"%s: %s\n", progname, + "cannot name output after input file if there isn't one!"); + usage(); + } + ok = process_stdin(base_cpp_cmd); + } + else + for (i = optind; i < argc; ++i) + if (!(ok = process_file(base_cpp_cmd,argv[i]))) break; + + if (ok && firstpage) + output_manual_pages(firstpage,argc - optind, link_type); + free_manual_pages(firstpage); + destroy_enum_lists(); + + if (cpp_opts) free((char *)base_cpp_cmd); + + for (includefile = first_include; includefile;) + { + IncludeFile *next = includefile->next; + free(includefile); + includefile = next; + } + + for (excludesection = first_excluded_section; excludesection;) + { + ExcludeSection *next = excludesection->next; + free(excludesection); + excludesection = next; + } + + for (i = 0; i < _OBJECT_NUM; i++) + { + safe_free(output_object[i].subdir); + safe_free(output_object[i].extension); + } + +#ifdef DBMALLOC + malloc_dump(2); + malloc_chain_check(1); +#endif +#ifdef MALLOC_DEBUG + sleep(1000000); +#endif + return !ok; +} @@ -0,0 +1,284 @@ +/* $Id: c2man.h,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * + * Definitions for C language manual page generator + */ +#ifndef _C2MAN_H +#define _C2MAN_H + +#include "config.h" +#include "symbol.h" + +#ifdef I_SYS_TYPES +#include <sys/types.h> +#endif + +#ifdef I_STDLIB +#include <stdlib.h> +#endif + +#ifdef I_STRING +#include <string.h> +#else +#include <strings.h> +#endif + +#include <stdio.h> + +#ifdef I_UNISTD +#include <unistd.h> +#endif + +#ifdef I_STDDEF +#include <stddef.h> +#endif + +#ifdef I_TIME +#include <time.h> +#endif +#ifdef I_SYS_TIME +#include <sys/time.h> +#endif + +#ifdef NeXT +#include <libc.h> +#undef ECHO /* lex generates ECHO */ +#endif + +#ifdef DBMALLOC +#include </usr/local/debug_include/malloc.h> +#endif + +#include "confmagic.h" + +/* number of spaces in a tab */ +#define NUM_TAB_SPACES 4 + +/* maximum include file nesting */ +#define MAX_INC_DEPTH 15 + +/* maximum number of include directories */ +#define MAX_INC_DIR 15 + +/* maximum number of characters in a text buffer */ +#define MAX_TEXT_LENGTH 256 + +/* Boolean type */ +typedef int boolean; +#ifndef TRUE +#define FALSE 0 +#define TRUE 1 +#endif + +/* NULL char *, useful for passing NULL to functions wanting a char * */ +#define NULLCP ((char *)0) + +/* This is a list of function parameters. */ +typedef struct _parameter_list { + struct _parameter *first; /* pointer to first parameter in list */ + struct _parameter *last; /* pointer to last parameter in list */ +} ParameterList; + +/* Declaration specifier flags */ +#define DS_NONE 0 /* default */ +#define DS_EXTERN 1 /* contains "extern" specifier */ +#define DS_STATIC 2 /* contains "static" specifier */ +#define DS_CHAR 4 /* contains "char" type specifier */ +#define DS_SHORT 8 /* contains "short" type specifier */ +#define DS_FLOAT 16 /* contains "float" type specifier */ +#define DS_JUNK 32 /* we're not interested in this declaration */ +#define DS_INLINE 64 /* makes static look interesting */ + +/* This structure stores information about a declaration specifier. */ +typedef struct _decl_spec { + unsigned short flags; /* flags defined above */ + char *text; /* source text */ + struct _enumerator_list *enum_list; /* associated enum (if any) */ +} DeclSpec; + +/* Styles of declaration/definition */ +typedef enum { + DECL_SIMPLE, /* simple declaration */ + DECL_COMPOUND, /* compound declaration */ + DECL_FUNCTION, /* function declaration (prototype) */ + DECL_FUNCDEF /* an actual function definition */ +} DeclType; + +/* This structure stores information about a declarator. */ +typedef struct _declarator { + char *name; /* name of variable or function */ + char *text; /* source text */ + DeclType type; /* style of function declaration */ + ParameterList params; /* function parameters */ + char *comment; /* description of param or variable */ + char *retcomment; /* description of return value */ + struct _declarator *head; /* head function declarator */ + struct _declarator *func_stack; /* stack of function declarators */ + struct _declarator *next; /* next declarator in list */ +} Declarator; + +/* This is a list of declarators. */ +typedef struct _declarator_list { + Declarator *first; /* pointer to first declarator in list */ + Declarator *last; /* pointer to last declarator in list */ +} DeclaratorList; + +/* This structure stores information about a declaration. */ +typedef struct _declaration { + DeclSpec decl_spec; + DeclaratorList decl_list; +} Declaration; + +/* this structure store information about an enumerator */ +typedef struct _enumerator { + char *name; /* name of enum entry */ + char *comment; /* description of entry */ + char *group_comment; /* general descr. for next few enums in list */ + struct _enumerator *next; /* next enumerator in list */ +} Enumerator; + +/* This is a list of enumerators. */ +typedef struct _enumerator_list { + Enumerator *first; /* pointer to first enumerator in list */ + Enumerator *last; /* pointer to last enumerator in list */ + struct _enumerator_list *next; /* next list in a list-of-lists */ +} EnumeratorList; + + +/* This structure stores information about a function parameter. */ +typedef struct _parameter { + DeclSpec decl_spec; + Declarator *declarator; + boolean suppress; /* don't print in grouped page */ + boolean duplicate; /* mention fn in grouped page */ + struct _parameter *next; /* next parameter in list */ +} Parameter; + +/* this is an identifier, with surrounding comments (if any) */ +typedef struct _identifier { + char *name; + char *comment_before, *comment_after; +} Identifier; + +/* parser stack entry type */ +typedef union { + char *text; + DeclSpec decl_spec; + Parameter parameter; + ParameterList param_list; + Declarator *declarator; + DeclaratorList decl_list; + Declaration declaration; + Enumerator enumerator; + EnumeratorList *enum_list; + Identifier identifier; + boolean boolean; +} yystype; + +/* include files specified by user */ +typedef struct _includefile +{ + char *name; + struct _includefile *next; +} IncludeFile; + +/* output object types */ +enum Output_Object +{ +#if 0 /* C++ stuff */ + OBJECT_CLASS, + OBJECT_STRUCT, + OBJECT_ENUM, + OBJECT_TYPEDEF, +#endif + OBJECT_FUNCTION, + OBJECT_VARIABLE, + OBJECT_STATIC_FUNCTION, + OBJECT_STATIC_VARIABLE, + _OBJECT_NUM +}; + +struct Output_Object_Info +{ + char flag; /* -O flag used to set it */ + char *name; /* descriptive name for usage() */ + char *extension; /* file extension */ + char *subdir; /* subdirectory */ +}; + +/* list of sections to exclude */ +typedef struct ExcludeSection +{ + char *name; + struct ExcludeSection *next; +} ExcludeSection; + +#define YYSTYPE yystype + +/* Program options */ +extern boolean static_out; +extern boolean variables_out; +extern boolean promote_param; +extern boolean look_at_body_start; +extern boolean body_start_only; +extern const char *decl_spec_prefix, *declarator_prefix, *declarator_suffix; +extern const char *first_param_prefix, *middle_param_prefix, *last_param_suffix; +extern int num_inc_dir; +extern const char *inc_dir[]; +extern char *manual_name; +extern const char *progname; +extern char *header_prefix; +extern IncludeFile *first_include; +extern ExcludeSection *first_excluded_section; + +extern boolean fixup_comments; + +extern char *group_terse; +extern boolean group_together; +extern boolean terse_specified; +extern boolean always_document_params; + +extern char *output_dir; + +/* Global declarations */ +extern int line_num; +extern const char *basefile; +extern Time_t basetime; +extern boolean inbasefile; +extern boolean header_file; +extern SymbolTable *typedef_names; +extern void output_error(); +extern void parse_file _((const char *start_file)); +extern int errors; +extern const char *manual_section; +extern boolean use_input_name; +extern boolean make_embeddable; +extern struct Output_Object_Info output_object[_OBJECT_NUM]; + + + +/* Output a string to standard output. */ +#define put_string(s) fputs(s, stdout) + +/* a malloc that handles errors, and a free that handles NULL */ +#ifndef DBMALLOC +void *safe_malloc _((size_t size)); +#else +/* use macro so dbmalloc tells us where safe_malloc is called from */ +#define safe_malloc(s) malloc(s) +#endif +#define safe_free(p) do { if (p) free(p); p = NULL; } while(0) + +void outmem(); +void print_includes _((FILE *f));/* write #include lines */ + +void yyerror _V((const char *fmt, ...)); + +char *strduplicate _((const char *s)); +int strncmpi _((const char *s1, const char *s2, size_t n)); +char *strtoupper _((char *s)); + +void my_perror _((const char *action, const char *filename)); + +char *alloc_string _((const char *start, const char *end)); + +#endif diff --git a/c2man.man b/c2man.man new file mode 100644 index 0000000..ad0d20e --- /dev/null +++ b/c2man.man @@ -0,0 +1,906 @@ +.\" $Id: c2man.man,v 1.1 2004-05-03 05:17:49 behdad Exp $ +.de EX \"Begin example +.br +.if \\$1 .ne \\$1 +.if !"\\$2"" \{ +.if n .sp 1 +.if t .sp .5 +\\$2 +\} +.if n .sp 1 +.if t .sp .5 +.nf +.cs R 24 +.vs \n(.vu-2p +.in +.5i +.. +.de EE \"End example +.br +.vs \n(.vu+2p +.cs R +.fi +.in -.5i +.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 \\$7 \\$8 \\$9 +.CE +.. +.\" The IF & IE macros cannot be combined due to flatten.sed +.de IF \"Begin Include a source file +.br +.if n .sp 1 +.if t .sp .5 +.ne 10 +.nf +.cs R 24 +.vs \n(.vu-2p +.RS 0.25in +.. +.de IE \"End Include a source file +.br +.RE +.if n .sp 1 +.if t .sp .5 +.vs \n(.vu+2p +.cs R +.fi +.. +.\" The OF & OE macros cannot be combined due to flatten.sed +.de OF \"Begin Include a fixed nroff output file +.br +.if n .sp 1 +.if t .sp .5 +.ne 10 +.nf +.RS 0.25in +.. +.de OE \"End Include a fixed nroff output file +.br +.RE +.if n .sp 1 +.if t .sp .5 +.fi +.. +.de NA \"Begin name and Email address stuff +.br +.if \\$1 .ne \\$1 +.if !"\\$2"" \{ +.if n .sp 1 +.if t .sp .5 +\\$2 +\} +.if n .sp 1 +.if t .sp .5 +.nf +.in +.5i +.. +.de NE \"End name +.fi +.in -.5i +.if n .sp 1 +.if t .sp .5 +.. +.TH C2MAN 1 "March 2, 1995" +.BY "CISRA" +.SH NAME +c2man \- generate manual pages from C source code +.SH SYNOPSIS +.B c2man +[ +.I option \fP...\fI +] [ +.I file \fP...\fI +] +.SH DESCRIPTION +.B c2man +reads C source code files in which comments have been strategically placed, +and outputs manual page(s) documenting each function defined or declared (via +a prototype), and optionally each variable with global scope. +Function definitions and declarations may be in the old style or ISO/ANSI style. +If no +.I file +argument is given, +.B c2man +takes its input from the standard input. +.PP +If a +.B .h +file is written as a formal interface description when preparing an +interface spec, +.B c2man +can generate all the manual pages required for the spec at one fell swoop, +and then keep them up to date automatically as the interface changes. +.PP +Since +.B c2man +will accept either function definitions or prototypes, it can be used on +either +.B .c +or +.B .h +files. +If the input is a +.B header +file, any files specified by +.B \-i +options are +assumed to be prerequisites, and get parsed before the input file. +(Any file whose extension begins with +.RB `` h '', +matched case-insensitively, +is considered a +.B header +file.) +.PP +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 +.PP +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: +.OF +.ne 34 +.so example.inc +.OE +.SS "Output Generation" +By default, a separate output file is generated for each global identifier +(i.e. function or variable) documented by c2man. +.PP +Much of +.BR c2man 's +information is extracted from the comment placed immediately before the +declaration/definition of the identifier being documented; this comment +is taken to describe the identifier and +.B must +be present, or the identifier will be ignored entirely. +In the case of a variable declaration/definition, this comment may instead be +placed after it starting on the same line. +.PP +Global variables are not documented, unless the +.B \-v +option is used. +.PP +Identifiers declared +.B static +are ignored by default unless the file is a +.B header +file (which is most useful with +.B inline +functions) or the +.B -s +option is used. +.PP +Declarations with the +.B extern +keyword are ignored unless they appear in a +.B header +file; note that this does not include function definitions. +.SS "Sections Generated Automatically" +Each manual page starts with a +.B NAME +section, listing the name(s) of the identifier(s) documented, along with a +terse description. +By default, this description is the first line or sentence of the +comment describing the identifier. +With the +.B \-g +option, it is found after the first dash +.RB ( \- ) +in the first comment of the file, and the +.B \-G +option specifies it explicitly. +.PP +The +.B SYNOPSIS +section +begins with an +.B #include +line +if the source file is a +.BR header . +After this is an external declaration for the +identifier(s) being documented. +.PP +Information in the +.B PARAMETERS +section is gleaned from the comments immediately before or after each +parameter declaration. A comment after a parameter can follow the comma that +separates that parameter from the next, if the comment starts on the same line +and is the only remaining thing on that line. Leading underscores in a +parameter name are stripped when printed in the manual page. +.PP +If the manual page is for a group of functions (ie: +.B \-g +or +.B \-G +options), +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 Xexample below). +.PP +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. +Comments describing +.B enum +identifiers are placed in a similar manner to those that describe function +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 +will bunch them together to save space. +.PP +The +.B DESCRIPTION +section contains everything +after the first line or sentence +of the comment describing the identifier, +up until the word +.RB `` returns '' +at the start of a line, matched case-insensitively and optionally followed by +a colon +.RB ( : ). +In the case of a variable of +.B enumerated +.BR type , +it will also list all the values it can hold. +.PP +The +.B RETURNS +section contains anything after that. Any of these lines that begin with a +single word followed by a colon or a tab generate tagged paragraphs so that +lists of possible return values and error codes look neat. +If the function is void, don't put anything like "Returns: nothing" in +the comment, since it's a waste of space. If the identifier is a function +returning an +.B enumerated +.BR type , +its possible values will be listed here. +.PP +The +.B RETURNS +section is also added if there is a comment after the function return type. +.EX 5 "For example:" +/* Sample function */ +char * /* NULL if failed string otherwise */ +sample_function() +{ +} +.EE +The +.B RETURNS +section will contain the full contents of the comment (stripping the optional +leading asterisk). It is not possible to use both methods to specify a +description for the return value. In that case the comment after the +return type supersedes whatever was specified for the return value in +the comment above the function. +.PP +Finally, a +.B "SEE ALSO" +section is generated, referencing all the other manual pages generated, if any. +.PP +The +.BR RETURNS , +.B PARAMETERS +and +.B "SEE ALSO" +sections are omitted entirely if they aren't needed. +.SS "Comment Style and Placement" +Both +.B C +and +.B C++ +style comments are recognized, +with seperate consecutive single-line comments coalesced into a single block. +When looking at comments, +.B c2man +ignores everything before the first alpha-numeric character. After that, it +ignores leading white-space, leading asterisks and leading slashes +on all subsequent +lines, and ignores all trailing lines thus rendered blank. If that leaves +nothing, the comment is ignored entirely. +This makes it very flexible in supporting popular comment boxing. +.PP +Comments can be placed with considerable flexibility so that most commenting +styles are supported. +.EX 13 "The following variations of the enum definition in the \fBdowork.h\fR\ + example are all equivalent:" +/* commas after the comments. */ +enum Place +{ + HOME /* Home, Sweet Home */, + WORK /* where I spend lots of time */, + MOVIES /* Saturday nights mainly */, + CITY /* New York, New York */, + COUNTRY /* Bob's Country Bunker */ +}; +.EE +.EX 16 +/* the comment needn't go on the same line, + * if the comma goes after the comment. + */ +enum Place +{ + HOME + /* Home, Sweet Home */, + WORK + /* where I spend lots of time */, + MOVIES + /* Saturday nights mainly */, + CITY + /* New York, New York */, + COUNTRY + /* Bob's Country Bunker */ +}; +.EE +.EX 14 +/* the comment can go before it too. */ +enum Place +{ + /* Home, Sweet Home */ + HOME, + /* where I spend lots of time */ + WORK, + /* Saturday nights mainly */ + MOVIES, + /* New York, New York */ + CITY, + /* Bob's Country Bunker */ + COUNTRY +}; +.EE +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, so this will result in +very misleading output. +.EX 16 "Don't do this:" +enum Place +{ + HOME, + /* Home, Sweet Home */ + WORK, + /* where I spend lots of time */ + MOVIES, + /* Saturday nights mainly */ + CITY, + /* New York, New York */ + COUNTRY + /* Bob's Country Bunker */ +}; +.EE +.PP +Since enum identifiers sometimes fall into logical groups, a comment before +such an identifier will be taken to apply to the next few in the list, +provided that the comments describing each individual identifier +are placed after them. Also, there must be a blank line separating the comment +describing the next logical group and the comment at the end of the previous +line, or the two will be coalesced and incorrectly treated as a single comment +for the previous enumerator. +.EX 17 "In other words, you can go:" +/* include logical grouping comments. */ +enum Place +{ + /* These take up most of the week */ + HOME, /* Home, Sweet Home */ + WORK, /* where I spend lots of time */ + + /* More for special occasions */ + MOVIES, /* Saturday nights mainly */ + CITY, /* New York, New York */ + + /* The real favourite */ + COUNTRY /* Bob's Country Bunker */ +}; +.EE +.PP +That may all sound a bit complex, but the upshot is that +.B c2man +will usually know which identifier a comment is associated with, unless you do +something truly bizarre. +.SS "Processing of Comment Contents" +Basic punctuation and capitalisation corrections are made in each section for +neatness, and the typesetting program used to process the output will generally +reformat line breaks according to the width of the output device. Blank lines +in a comment will be preserved, and lines starting with a dash +.RB ( \- ), +an asterisk +.RB ( * ), +or a numbered point +.RB ( (n) , +.B n) +.RB or\ n. ), +will cause a line break, allowing simple bulleted or numbered lists. +.PP +Typesetter specific commands may be included for more complex processing, +although this isn't recommended since it ties you to a particular typesetter. +.SS "Grouped Manual Pages" +Simple, closely related objects can be grouped together onto a single page with the +.B \-g +or +.B \-G +options. By default, this results in a single output file with multiple links +so that it can be accessed by the name of the input file, or of any identifier +documented. +For example, if ctype.h contains: +.IF +.so ctype_ex.h +.IE +.PP +then using: +.CD c2man -g ctype.h +yields: +.OF +.so ctype_ex.inc +.OE +.SS "Extra Sections" +Additional sections not otherwise recognized by +.B c2man +can be included in the manual page by including them in the comment +describing the identifier. +A section heading is preceded in the comment by an empty line (after +removal of leading asterisks), and is the only word on it's line, or is +a word followed by a colon +.RB ( : ), +or is a line ending with a colon, so section names with spaces are allowed, +like "Return value:". +.P +Section heading names are capitalized, and the names +.BR DESCRIPTION , +.B RETURNS +and +.B NAME +are recognized specially so you can name them explicitly if you like. +.BR FUNCTION , +.B PROCEDURE +and +.B ROUTINE +are also recognised, and treated identically to +.BR NAME . +.EX 9 "For example:" +/* + * Have a quick puff. + * + * Warning: Smoking causes lung cancer + */ +void go_for_a_smoke(); +.EE +Generates a manual page with a +.B WARNING +section. +.EE +.SH OPTIONS +.TP +.BI \-o dir +Write generated files into directory +.B dir +rather than the current directory. +If +.B dir +is specified as +.BR \- , +generated pages are written to the standard output, separated by form-feeds. +.TP +.B \-v +Also output declarations for variables defined in the file. +.TP +.B \-s +Output manual pages for all +.B static +identifiers. +.TP +.B \-g +Group all the info generated together into a single +page (ala ctype(3)), reading the single-line terse description for the +.B NAME +section from the line of the first comment in the file. +If this first line contains a dash +.RB ( \- ) +surrounded by whitespace, the terse description is taken starting after the +dash. +If multiple files are specified, +the first such suitable comment encountered is used. A link to +the output file is made for each identifier documented, according to the +.B \-l +option. +.TP +.BI \-G terse +Like +.BR \-g , +but using the specified terse description rather than reading it from the +file. +.TP +.B \-k +Don't attempt to fix up capitalization and punctuation. +.TP +.B \-b +If a function lacks a preceding comment, look for one immediately following +the curly-brace at the top of the function body. +The comment must appear before anything else. +.TP +.B \-B +Apply +.B \-b +strictly. Only look for the description of a function at +the top of its body. +.TP +.B \-l h|s|f|n|r +Select how the output for a grouped manual page is linked to files named after +all identifiers documented on the page. +Hard link +.RB ( h ) +is the default, as it uses the least space. +Soft link +.RB ( s ), +where supported, allows a +.BR find (1) +command with +.RB `` "\-type f" '' +to easily skip the duplicated pages. +Separate file +.RB ( f ) +containing a file include +directive is the traditional +.SM UNIX +method. +No link +.RB ( n ) +is useful for generating printed documentation without duplicated pages; only +a single file, named according to the +.B \-n +option, is generated. +Remove +.RB ( r ) +is like No link, but also removes any previously generated links/files named +after the identifiers documented. Useful for cleaning up after accidents with +the other link options. +.sp +In all cases, any existing links will be removed before being rewritten. +.TP +.B \-n +Name the documentation output file after the input file. +When generating grouped manual pages, this will be the file to which others +are linked. +For non-grouped manual pages, if documentation for more than one +identifier is generated, information about the last identifier will overwrite +information about all the previous ones. +.PP +.BI \-i file +.PP +\fB\-i\fI\{"file"\} +.TP +.BI \-i <file> +Insert a +.B #include +line referencing the specified file in the +.B SYNOPSIS +section, using the ``<file>'' form by default. +Any number of +.B \-i +options may be specified to build up a list of prerequisites. +If using the second form, you may need to quote the quotation marks, lest they +get removed by the shell. +.TP +.BI \-x sectionname +Exclude +.I sectionname +from the generated manpages. This option may be repeated to exclude a number +of sections. +.TP +.BI \-H header-path +Prepend +.B header-path +to the name of the +.B header +file when an +.B #include +line is automatically generated in the +.B SYNOPSIS +section. +.TP +.BI \-L +Lazy option: Only list parameters in the +.B PARAMETERS +section if they are documented by a comment in the source. By default, +parameters with no comment are described as ``Not Documented.'', to encourage +the programmer to comment them. +.TP +.BI \-Tn|l|t|h|a[, options ] +Set the output typesetting language as well as language specific +options. +.I options +is a comma delimited list of options. +.B Nroff +.RB ( n ) +is the default, +.B LaTeX +.RB ( l ) +, +.B Texinfo +.RB ( t ) +, +.B HTML +.RB ( h ) +, or +.B AutoDoc +.RB ( a ). +.B Texinfo +specific options are +.BR s , +.BR t , +.BR n , +and +.BR C . + +In +.B Texinfo +mode, each section is normally coded as a ``heading'' rather than a +``section''. This prevents the section name from appearing in the +table of contents. If the option +.B t +is given, the name of the manpage is used for the title of the +.B NAME +section, and is encoded as a ``section'', placing it in +the table of contents. Subsequent sections are encoded as ``headings''. +.B Texinfo +supports multiple levels of headings; the desired level may be +specified via the +.BI s n +option, where +.I n +starts at 0 for the ``chapter level'' and works down. A +top level node is created for the manpage, except when in embedded +mode +(the +.B c2man \-e +option). If the +.B n +option is specified, a node is created in embedded mode, but +without Up, Previous, or Next pointers; these must be filled in +.BR ( Texinfo +mode in +.B emacs +does a good job of it). The +.B C +option capitalizes the section titles. Usually they are printed +as specified (which is usually upper case). +.TP +.BI \-e +Prepares the output so it can be embedded in texts of the output typesetting +language. +.TP +.BI \-M name +Set the name of the manual in which the page will go. +.TP +.BI \-S section +Set the default manual section, used as the extension on the output files. +.I section +defaults to ``3'' for +.BR nroff , +``texi'' for +.B Texinfo , +``html'' for +.B HTML +and ``tex'' for +.B LaTeX +output, as specified via the +.B \-T +option. +This setting can be overridden by the +.BI \-O? .ext +options for finer control. +.TP +.BI \-Of|v|F|V[ subdir ][. ext ] +Provides for finer control of the output files, allowing a different output +subdirectory and extension to be specified for these different classes of +objects: +functions +.RB ( f ), +variables +.RB ( v ), +static functions +.RB ( F ) +and static variables +.RB ( V ). +.sp +If +.I subdir +is specified, the selected class of output will be written in that +subdirectory under the directory given by the +.B \-o +option if specified, otherwise under the current directory. +.sp +If +.I .ext +is specified, it will be used as the extension on the output files of the +selected class, instead of the default based on the +.B \-S +option (if specified), or the typesetting output format specified by the +.B \-T +option. +.sp +For example, the following command will generate +.BR nroff (1) +style output under the /usr/local/man hierarchy, documenting functions in +section 3 (/usr/local/man/man3/*.3), global variables in section 3v +(/usr/local/man/man3/*.3v), static functions in section 9 +(/usr/local/man/man9/*.9) and +static variables in section 9v (/usr/local/man/man9/*.9v): +.CD c2man -o/usr/local/man -v -s -Ofman3.3 -Ovman3.3v -OFman9.9 -OVman9.9v input.c +The +.B \-O +options will have no effect if +.B \-o- +is used to write to standard output, and +.BR \-Ov , +.B \-OF +and +.B \-OV +will have no effect unless their classes of output are enabled via the +appropriate +.B \-v +and +.B \-s +options. +.TP +.BI \-F template +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 format is specified by a template in the form +.EX +" int f ( a, b )" +.EE +but you may replace each space in this string with any number of +whitespace characters. +For example, the option +.EX +-F"int f(\\n\\ta,\\n\\tb\\n\\t)" +.EE +.EX 5 "will produce:" +int main( + int argc, + char *argv[] + ) +.EE +.EX 5 "The default output format is:" +int main +( + int argc, + char *argv[] +); +.EE +.TP +.BI \-P preprocessor +Run a different C preprocessor than normal (use +.B \-V +to determine the configured default). +You must include any options required to prevent it from stripping comments, +which is normally the default preprocessor behaviour. +For example, to use +.BR gcc 's +cpp instead: +.CD c2man -P \{"gcc -E -C"\} +.TP +.BI \-D name[=value] +This option is passed through to the preprocessor and is used to define +symbols for use with conditionals such as +.I #ifdef. +.TP +.BI \-U name +This option is passed through to the preprocessor and is used to remove +any definitions of this symbol. +.TP +.BI \-I directory +This option is passed through to the preprocessor and is used to specify +a directory to search for files that are referenced with +.I #include. +.TP +.B \-V +Print version information and cpp parameters. +.SH FILES +.TP +$(privlib)/eg/*.[ch] +A few example input files, showing different commenting styles. +.SH "SEE ALSO" +man(1), +apropos(1), +catman(8), +cproto(1), +cc(1), +cpp(1) +.SH DIAGNOSTICS +.BR c2man 's +error messages are not very helpful, so make sure your code compiles before +trying +.BR c2man . +If the code compiles OK but +.B c2man +rejects it, it may be because a comment is in a position +.B c2man +does not accept, or you are using a compiler extension not strictly conforming +to standard C. +.B c2man +defines the preprocessor symbol +.B __C2MAN__ +with its major version number +to allow you to work around such problems by surrounding them with +.BR "#ifndef __C2MAN__" . +.PP +An error at the very end of a function may indicate that the comments at the +beginning are badly placed. +.SH HISTORY +.B c2man +was originally written by: +.NA 4 +Graham Stoney +Canon Information Systems Research Australia +greyham@research.canon.com.au +(please send bug reports here) +.NE +Many thanks are due to the many other Internet contributors since then, and to +Chin Huang, the author of +.B cproto +from which it was originally derived. +.SH BUGS +The +.B \-F +option only interprets the following +character escape sequences: +.EX 2 +\\n newline +\\t tab +.EE +.PP +A comment before a preprocessor directive will be considered to apply +to the identifier that immediately follows, if it has no +comment of its own. +This is because the preprocessor directive gets removed by cpp before +c2man looks at it. +.PP +Comments aren't legal in some of the more obscure places that they are in C. +.PP +Heavy use of +.B #define +in a program may yield somewhat obscure manual pages. +.PP +.BR c2man 's +output backends may not be entirely consistent, but then users of +different formatters tend to have different tastes. @@ -0,0 +1,44 @@ +language: C, nroff, texinfo, latex, html, autodoc +package: c2man +version: 2.0 patchlevel 40 +parts: documentation generator (C -> nroff -man, -> texinfo, -> latex, + -> html, -> autodoc) +author: Graham Stoney <greyham@research.canon.com.au> +location: ftp from any comp.sources.misc archive, in volume42 + (the version in the comp.sources.reviewed archive is obsolete) + ftp /pub/Unix/Util/c2man-2.0.*.tar.gz from dnpap.et.tudelft.nl + Australia: ftp /usenet/comp.sources.misc/volume42/c2man-2.0/* + from archie.au + N.America: ftp /usenet/comp.sources.misc/volume42/c2man-2.0/* + from ftp.wustl.edu + Europe: ftp /News/comp.sources.misc/volume42/c2man-2.0/* + from ftp.irisa.fr + Japan: ftp /pub/NetNews/comp.sources.misc/volume42/c2man-2.0/* + from ftp.iij.ad.jp + Patches: ftp pub/netnews/sources.bugs/volume93/sep/c2man* from lth.se +description: c2man is an automatic documentation tool that extracts comments + from C source code to generate functional interface + documentation in the same format as sections 2 & 3 of the Unix + Programmer's Manual. It requires minimal effort from the + programmer by looking for comments in the usual places near the + objects they document, rather than imposing a rigid + function-comment syntax or requiring that the programmer learn + and use a typesetting language. Acceptable documentation can + often be generated from existing code with no modifications. +conformance: supports both K&R and ISO/ANSI C coding styles +features: + generates output in nroff -man, TeXinfo, LaTeX or HTML format + + handles comments as part of the language grammar + + automagically documents enum parameter & return values + + handles C (/* */) and C++ (//) style comments + - doesn't handle C++ grammar (yet) +requires: yacc/byacc/bison, lex/flex, and nroff/groff/texinfo/LaTeX. +ports: Unix, OS/2, MSDOS, VMS, Amiga. +portability: very high for unix, via Configure +status: actively developed; contributions by users are encouraged. +discussion: via a mailing list: send "subscribe c2man <Your Name>" (in the + message body) to listserv@research.canon.com.au +help: from the author and other users on the mailing list: + c2man@research.canon.com.au +announcements: patches appear first in comp.sources.bugs, and then in + comp.sources.misc. +updated: 1996/03/21 diff --git a/config_h.SH b/config_h.SH new file mode 100644 index 0000000..cf2fa77 --- /dev/null +++ b/config_h.SH @@ -0,0 +1,353 @@ +case $CONFIG in +'') + if test -f config.sh; then TOP=.; + elif test -f ../config.sh; then TOP=..; + elif test -f ../../config.sh; then TOP=../..; + elif test -f ../../../config.sh; then TOP=../../..; + elif test -f ../../../../config.sh; then TOP=../../../..; + else + echo "Can't find config.sh."; exit 1 + fi + . $TOP/config.sh + ;; +esac +case "$0" in +*/*) cd `expr X$0 : 'X\(.*\)/'` ;; +esac +echo "Extracting config.h (with variable substitutions)" +sed <<!GROK!THIS! >config.h -e 's!^#undef\(.*/\)\*!/\*#define\1 \*!' -e 's!^#un-def!#undef!' +/* + * This file was produced by running the config_h.SH script, which + * gets its values from config.sh, which is generally produced by + * running Configure. + * + * Feel free to modify any of this as the need arises. Note, however, + * that running config_h.SH again will wipe out any changes you've made. + * For a more permanent change edit config.sh and rerun config_h.SH. + * + * \$Id: config_h.SH,v 1.1 2004-05-03 05:17:48 behdad Exp $ + */ + +/* + * Package name : $package + * Source directory : $src + * Configuration time: $cf_time + * Configured by : $cf_by + * Target system : $myuname + */ + +#ifndef _config_h_ +#define _config_h_ + +/* VMS: + * This symbol, if defined, indicates that the program is running under + * VMS. It is currently only set in conjunction with the EUNICE symbol. + */ +/* BSD: + * This symbol, if defined, indicates that the program is running under + * a BSD system. + */ +#$d_eunice VMS /**/ +#$d_bsd BSD /**/ + +/* CPP_FILE_COM: + * This symbol contains the first part of the string which will invoke + * the C preprocessor a file and produce to standard output, preserving + * comments. Typical value of "cc -E -C" or "/lib/cpp -C". + */ +/* CPP_STDIN_FLAGS: + * This variable contains any flags necessary to get CPP_FILE_COM to + * read from the standard input. + */ +/* CPP_IGN_HDRS: + * This symbol is defined if CPP_FILE_COM ignores *.h files. + */ +/* CPP_CAN_STDIN: + * This symbol is defined if CPP_FILE_COM can read standard input + * directly. + */ +#define CPP_FILE_COM "$cppfilecom" +#define CPP_STDIN_FLAGS "$cppstdinflags" +#$d_cppignhdrs CPP_IGN_HDRS /* does CPP ignore .h files? */ +#$d_cppcanstdin CPP_CAN_STDIN /* can CPP read stdin directly? */ + +/* HAS_ACCESS: + * This manifest constant lets the C program know that the access() + * system call is available to check for accessibility using real UID/GID. + * (always present on UNIX.) + */ +#$d_access HAS_ACCESS /**/ + +/* HASATTRIBUTE: + * This symbol indicates the C compiler can check for function attributes, + * such as printf formats. This is normally only supported by GNU cc. + */ +#$d_attribut HASATTRIBUTE /**/ +#ifndef HASATTRIBUTE +#define __attribute__(_arg_) +#endif + +/* HASCONST: + * This symbol, if defined, indicates that this C compiler knows about + * the const type. There is no need to actually test for that symbol + * within your programs. The mere use of the "const" keyword will + * trigger the necessary tests. + */ +#$d_const HASCONST /**/ +#ifndef HASCONST +#define const +#endif + +/* FLEXFILENAMES: + * This symbol, if defined, indicates that the system supports filenames + * longer than 14 characters. + */ +#$d_flexfnam FLEXFILENAMES /**/ + +/* HAS_LINK: + * This symbol, if defined, indicates that the link routine is + * available to create hard links. + */ +#$d_link HAS_LINK /**/ + +/* HAS_OPEN3: + * This manifest constant lets the C program know that the three + * argument form of open(2) is available. + */ +#$d_open3 HAS_OPEN3 /**/ + +/* HAS_STRCHR: + * This symbol is defined to indicate that the strchr()/strrchr() + * functions are available for string searching. If not, try the + * index()/rindex() pair. + */ +/* HAS_INDEX: + * This symbol is defined to indicate that the index()/rindex() + * functions are available for string searching. + */ +#$d_strchr HAS_STRCHR /**/ +#$d_index HAS_INDEX /**/ + +/* HAS_STRFTIME: + * This symbol, if defined, indicates that the strftime routine is + * available to format locale-specific times. + */ +#$d_strftime HAS_STRFTIME /**/ + +/* HAS_STRSTR: + * This symbol, if defined, indicates that the strstr routine is + * available to find substrings. + */ +#$d_strstr HAS_STRSTR /**/ + +/* HAS_SYMLINK: + * This symbol, if defined, indicates that the symlink routine is available + * to create symbolic links. + */ +#$d_symlink HAS_SYMLINK /**/ + +/* Time_t: + * This symbol holds the type returned by time(). It can be long, + * or time_t on BSD sites (in which case <sys/types.h> should be + * included). + */ +#define Time_t $timetype /* Time type */ + +/* Signal_t: + * This symbol's value is either "void" or "int", corresponding to the + * appropriate return type of a signal handler. Thus, you can declare + * a signal handler using "Signal_t (*handler)()", and define the + * handler using "Signal_t handler(sig)". + */ +#define Signal_t $signal_t /* Signal handler's return type */ + +/* HASVOLATILE: + * This symbol, if defined, indicates that this C compiler knows about + * the volatile declaration. + */ +#$d_volatile HASVOLATILE /**/ +#ifndef HASVOLATILE +#define volatile +#endif + +/* I_FCNTL: + * This manifest constant tells the C program to include <fcntl.h>. + */ +#$i_fcntl I_FCNTL /**/ + +/* I_STDDEF: + * This symbol, if defined, indicates that <stddef.h> exists and should + * be included. + */ +#$i_stddef I_STDDEF /**/ + +/* I_STDLIB: + * This symbol, if defined, indicates that <stdlib.h> exists and should + * be included. + */ +#$i_stdlib I_STDLIB /**/ + +/* I_STRING: + * This symbol, if defined, indicates to the C program that it should + * include <string.h> (USG systems) instead of <strings.h> (BSD systems). + */ +#$i_string I_STRING /**/ + +/* I_SYS_FILE: + * This symbol, if defined, indicates to the C program that it should + * include <sys/file.h> to get definition of R_OK and friends. + */ +#$i_sysfile I_SYS_FILE /**/ + +/* I_SYS_TYPES: + * This symbol, if defined, indicates to the C program that it should + * include <sys/types.h>. + */ +#$i_systypes I_SYS_TYPES /**/ + +/* I_TIME: + * This symbol, if defined, indicates to the C program that it should + * include <time.h>. + */ +/* I_SYS_TIME: + * This symbol, if defined, indicates to the C program that it should + * include <sys/time.h>. + */ +#$i_time I_TIME /**/ +#$i_systime I_SYS_TIME /**/ + +/* I_UNISTD: + * This symbol, if defined, indicates to the C program that it should + * include <unistd.h>. + */ +#$i_unistd I_UNISTD /**/ + +/* I_STDARG: + * This symbol, if defined, indicates that <stdarg.h> exists and should + * be included. + */ +/* I_VARARGS: + * This symbol, if defined, indicates to the C program that it should + * include <varargs.h>. + */ +#$i_stdarg I_STDARG /**/ +#$i_varargs I_VARARGS /**/ + +/* CAN_PROTOTYPE: + * If defined, this macro indicates that the C compiler can handle + * function prototypes. + */ +/* DOTS: + * This macro is used to specify the ... in function prototypes which + * have arbitrary additional arguments. + */ +/* NXT_ARG: + * This macro is used to separate arguments in the declared argument list. + */ +/* P_FUNC: + * This macro is used to declare "private" (static) functions. + * It takes three arguments: the function type and name, a parenthesized + * traditional (comma separated) argument list, and the declared argument + * list (in which arguments are separated with NXT_ARG, and additional + * arbitrary arguments are specified with DOTS). For example: + * + * P_FUNC(int foo, (bar, baz), int bar NXT_ARG char *baz[]) + */ +/* P_FUNC_VOID: + * This macro is used to declare "private" (static) functions that have + * no arguments. The macro takes one argument: the function type and name. + * For example: + * + * P_FUNC_VOID(int subr) + */ +/* V_FUNC: + * This macro is used to declare "public" (non-static) functions. + * It takes three arguments: the function type and name, a parenthesized + * traditional (comma separated) argument list, and the declared argument + * list (in which arguments are separated with NXT_ARG, and additional + * arbitrary arguments are specified with DOTS). For example: + * + * V_FUNC(int main, (argc, argv), int argc NXT_ARG char *argv[]) + */ +/* V_FUNC_VOID: + * This macro is used to declare "public" (non-static) functions that have + * no arguments. The macro takes one argument: the function type and name. + * For example: + * + * V_FUNC_VOID(int fork) + */ +/* _: + * This macro is used to declare function parameters for folks who want + * to make declarations with prototypes using a different style than + * the above macros. Use double parentheses. For example: + * + * int main _((int argc, char *argv[])); + */ +#$prototype CAN_PROTOTYPE /**/ +#ifdef CAN_PROTOTYPE +#define NXT_ARG , +#define DOTS , ... +#define V_FUNC(name, arglist, args)name(args) +#define P_FUNC(name, arglist, args)static name(args) +#define V_FUNC_VOID(name)name(void) +#define P_FUNC_VOID(name)static name(void) +#define _(args) args +#else +#define NXT_ARG ; +#define DOTS +#define V_FUNC(name, arglist, args)name arglist args; +#define P_FUNC(name, arglist, args)static name arglist args; +#define V_FUNC_VOID(name)name() +#define P_FUNC_VOID(name)static name() +#define _(args) () +#endif + +/* CAN_VAPROTO: + * This variable is defined on systems supporting prototype declaration + * of functions with a variable number of arguments. + */ +/* _V: + * This macro is used to declare function parameters in prototypes for + * functions with a variable number of parameters. Use double parentheses. + * For example: + * + * int printf _V((char *fmt, ...)); + * + * Remember to use the plain simple _() macro when declaring a function + * with no variable number of arguments, since it might be possible to + * have a non-effect _V() macro and still get prototypes via _(). + */ +#$vaproto CAN_VAPROTO /**/ +#ifdef CAN_VAPROTO +#define _V(args) args +#else +#define _V(args) () +#endif + +/* VOIDFLAGS: + * This symbol indicates how much support of the void type is given by this + * compiler. What various bits mean: + * + * 1 = supports declaration of void + * 2 = supports arrays of pointers to functions returning void + * 4 = supports comparisons between pointers to void functions and + * addresses of void functions + * 8 = suports declaration of generic void pointers + * + * The package designer should define VOIDUSED to indicate the requirements + * of the package. This can be done either by #defining VOIDUSED before + * including config.h, or by defining defvoidused in Myinit.U. If the + * latter approach is taken, only those flags will be tested. If the + * level of void support necessary is not present, defines void to int. + */ +#ifndef VOIDUSED +#define VOIDUSED $defvoidused +#endif +#define VOIDFLAGS $voidflags +#if (VOIDFLAGS & VOIDUSED) != VOIDUSED +#define void int /* is void to be avoided? */ +#define M_VOID /* Xenix strikes again */ +#endif + +#endif +!GROK!THIS! diff --git a/confmagic.h b/confmagic.h new file mode 100644 index 0000000..5cf0a4a --- /dev/null +++ b/confmagic.h @@ -0,0 +1,27 @@ +/* + * This file was produced by running metaconfig and is intended to be included + * after config.h and after all the other needed includes have been dealt with. + * + * This file may be empty, and should not be edited. Rerun metaconfig instead. + * If you wish to get rid of this magic, remove this file and rerun metaconfig + * without the -M option. + * + * $Id: confmagic.h,v 1.1 2004-05-03 05:17:48 behdad Exp $ + */ + +#ifndef _confmagic_h_ +#define _confmagic_h_ + +#ifndef HAS_INDEX +#ifndef index +#define index strchr +#endif +#endif + +#ifndef HAS_INDEX +#ifndef rindex +#define rindex strrchr +#endif +#endif + +#endif diff --git a/ctype_ex.h b/ctype_ex.h new file mode 100644 index 0000000..5719a7f --- /dev/null +++ b/ctype_ex.h @@ -0,0 +1,38 @@ +/* ctype.h - character classification functions */ + +/* 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); diff --git a/eg/boxcomment.c b/eg/boxcomment.c new file mode 100644 index 0000000..f5f0379 --- /dev/null +++ b/eg/boxcomment.c @@ -0,0 +1,41 @@ +/*************************************************************************** + * C style box comment. + * + * This function has a very pretty C style box comment. + ***************************************************************************/ +void boxcomm_c1(void); + +/*************************************************************************** +/ Fancier C style box comment. +/ +/ This function has a very pretty C style box comment. +/***************************************************************************/ +void boxcomm_c2(void); + +/*************************************************************************** +/*** Even Fancier C style box comment. +/*** +/*** This function has a very pretty C style box comment. +/***************************************************************************/ +void boxcomm_c3(void); + +/*------------------------------------------------------------------------- + * C style box comment. + * + * This function has a dashed C style box comment. + *-------------------------------------------------------------------------*/ +void boxcomm_c4(void); + +//////////////////////////////////////////////////////////////////////////// +// C++ style box comment. +// +// This function has a very pretty C++ style box comment. +//////////////////////////////////////////////////////////////////////////// +void boxcomm_cpp1(void); + +//////////////////////////////////////////////////////////////////////////// +//// Fancier C++ style box comment. +//// +//// This function has a very pretty C++ style box comment. +//////////////////////////////////////////////////////////////////////////// +void boxcomm_cpp2(void); diff --git a/eg/ccomment.h b/eg/ccomment.h new file mode 100644 index 0000000..86bb7cd --- /dev/null +++ b/eg/ccomment.h @@ -0,0 +1,41 @@ +/* +** function starting with a C comment +*/ +void ccomment( + /* single line before */ + int single_before, + + /* + * multiple + * lines before + */ + int multiple_before, + + int end_of_line, /* end of the line */ + + int multiple_eol, /* + * multiple lines + * starting at + * the EOL + */ + + int single_eol_before_comma /* end of line, but before comma */, + + int multiple_eol_before_comma /* + * multiple lines after, at the EOL and + * before comma. + * can't imagine anyone coding this. + */, + + int single_after + /* single line after */ + , + + int multiple_after + /* + * multiple lines + * after. + * can't imagine anyone coding like this. + */ +); + diff --git a/eg/commentaft.c b/eg/commentaft.c new file mode 100644 index 0000000..29e86d8 --- /dev/null +++ b/eg/commentaft.c @@ -0,0 +1 @@ +int commentafter; /* you'll need -v to get this documented */ diff --git a/eg/cppcomment.h b/eg/cppcomment.h new file mode 100644 index 0000000..2f4ff90 --- /dev/null +++ b/eg/cppcomment.h @@ -0,0 +1,32 @@ +// +// function starting with a C++ comment +// +void cppcomment( + // single line before + int single_before, + + // + // multiple + // lines before + // + int multiple_before, + + int end_of_line, // end of the line + + int multiple_eol, // + // multiple lines + // starting at + // the EOL + // + + int single_after + // single line after + , + int multiple_after + // + // multiple lines + // after. + // cant imagine anyone coding like this. + // +); + diff --git a/eg/dash.h b/eg/dash.h new file mode 100644 index 0000000..bc3502b --- /dev/null +++ b/eg/dash.h @@ -0,0 +1,2 @@ +/* dash - comment with a dash. */ +int dash(int x); diff --git a/eg/ellipsis.c b/eg/ellipsis.c new file mode 100644 index 0000000..0842f6a --- /dev/null +++ b/eg/ellipsis.c @@ -0,0 +1,15 @@ +int pr(int,...); + +void main(void) +{ + pr(3, 'a', 'b', 'c'); +} + +/* ellipsis test function */ +int pr( + int nitems, /* number of items */ + ... /* items */ +) +{ + /* blah, blah, blah, blah, blah! */ +} diff --git a/eg/grouped.c b/eg/grouped.c new file mode 100644 index 0000000..05f3a27 --- /dev/null +++ b/eg/grouped.c @@ -0,0 +1,23 @@ +/* + * grouped functions + * + */ + +/* my grouped1 does something weird */ +grouped1(a,b) +int a; /* the a parameter */ +char b; /* the b parameter */ +{ + + /* do nothing */ + return(1); +} +/* my grouped2 does also something weird */ +grouped2(a,b) +int a; /* the a parameter */ +char *b;/* the b parameter */ +{ + + /* do nothing */ + return(1); +} diff --git a/eg/multidecl.c b/eg/multidecl.c new file mode 100644 index 0000000..70ffa3f --- /dev/null +++ b/eg/multidecl.c @@ -0,0 +1,3 @@ +int multidecl1, /* the first one */ + multidecl2, /* the second one */ + multidecl3; /* the last one */ diff --git a/eg/namedash.h b/eg/namedash.h new file mode 100644 index 0000000..6d05848 --- /dev/null +++ b/eg/namedash.h @@ -0,0 +1,9 @@ +/* + * NAME + * namedash - name section with dash. + * + * DESCRIPTION + * This function goes way over the top and includes a NAME section that + * has a dash in it. + */ +int namedash(); diff --git a/eg/oldstyle.c b/eg/oldstyle.c new file mode 100644 index 0000000..ec4bb8b --- /dev/null +++ b/eg/oldstyle.c @@ -0,0 +1,24 @@ +/* +** old-style function starting with a C comment +*/ +void oldstyle(single_before, multiple_before, end_of_line, multiple_eol) + +/* single line before */ +int single_before; + +/* + * multiple + * lines before + */ +int multiple_before; + +int end_of_line; /* end of the line */ + +int multiple_eol; /* + * multiple lines + * starting at + * the EOL + */ +{ + /* blah, blah, blah, blah, blah! */ +} diff --git a/eg/retdecl.c b/eg/retdecl.c new file mode 100644 index 0000000..6675c8d --- /dev/null +++ b/eg/retdecl.c @@ -0,0 +1,10 @@ +/* return near the declarator + * This is an example of embedding the `returns' information in near the + * declarator in a function declaration. + */ +char * /* a string, or NULL if it failed */ +retdecl() +{ + /* reference implementation that barely meets the interface spec */ + return NULL; +} diff --git a/eg/returnerr.h b/eg/returnerr.h new file mode 100644 index 0000000..2292585 --- /dev/null +++ b/eg/returnerr.h @@ -0,0 +1,16 @@ +/* + * Do an operating system operation + * This is an example of a function which performs some sort of operating + * system operation, returning an errno-like indication of its success or + * failure. We'd like the documentation to list all the possible failure + * indications that this function can return, which is only a tiny subset of + * all the possible errno values. Hence, we don't define it as returning an + * enum of some sort, since that would cause c2man to list every possible value + * that type can have. + * Returns an indication of success or failure, as follows: + * EOK: Success + * EIO: I/O error + * ENOMEM: Out of memory + * EIEIO: Old Macdonald error + */ +int reterrno(); diff --git a/eg/returnlist.h b/eg/returnlist.h new file mode 100644 index 0000000..819f652 --- /dev/null +++ b/eg/returnlist.h @@ -0,0 +1,16 @@ +/* generate a lot of silly values + * Returns: Quite a number of things, as follows: + * 0 nothing + * 1 something small + * 2 a little larger + * 3 done in triplicate + * 4 getting larger + * 5 quintuplets + * 6 that'll do now, although I think this one is going to get + * very long and even cover multiple lines. let's just hope that + * it gets it right, including the capitalisation. maybe this is + * not a super thorough test, so including some + * .B bold + * stuff might help + */ +int returnlist(); diff --git a/eg/sections.c b/eg/sections.c new file mode 100644 index 0000000..8a84a00 --- /dev/null +++ b/eg/sections.c @@ -0,0 +1,17 @@ +/* + * NAME + * sections - multisection function comment. + * + * DESCRIPTION + * Only a mental cripple would be as verbose as this. + * + * WARNING + * Now here's a legitimate excuse for it. + */ +int sections( + int fred, /* Mary's boyfriend */ + int world /* real crazy */ +) +{ + what the heck, all this stuff just gets ignored +} diff --git a/eg/simplesect.c b/eg/simplesect.c new file mode 100644 index 0000000..8d6f4ed --- /dev/null +++ b/eg/simplesect.c @@ -0,0 +1,13 @@ +/* + * calculate some sections. + * Note: this becomes the description. + * + * Warning: This becomes a section. + */ +int simplesect( + int fred, /* Mary's boyfriend */ + int world /* real crazy */ +) +{ + what the heck, all this stuff just gets ignored +} diff --git a/eg/surround.c b/eg/surround.c new file mode 100644 index 0000000..e050056 --- /dev/null +++ b/eg/surround.c @@ -0,0 +1,6 @@ +/* surrounded by comments. + * this is the first one, and only it should be output. + * + * note: you'll need -v to get output from this. + */ +int surround; /* this should NOT be output. */ diff --git a/eg/underscore.h b/eg/underscore.h new file mode 100644 index 0000000..e57e6d5 --- /dev/null +++ b/eg/underscore.h @@ -0,0 +1,21 @@ +/* parameter names have underscores + * the underscores get removed in the documentation + */ +void underscore( + /* an int */ + int __x, + + /* a pointer to an int */ + int *__y, + + /* another pointer to an int, funnily enough */ + int _z[], + + /* a char, with a strange bit legal name */ + char _, + + /* an anonymous double */ + double, + + /* pointer to a function */ + float (*__funcptr)(int *__a1, char __a2[])); diff --git a/eg/variable.c b/eg/variable.c new file mode 100644 index 0000000..a788e82 --- /dev/null +++ b/eg/variable.c @@ -0,0 +1,5 @@ +/* a variable, not a function. + * + * Note: you'll need to use -v to get this to work. + */ +int variable; @@ -0,0 +1,179 @@ +/* $Id: enum.c,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * enumerator operations + */ +#include "c2man.h" +#include "strconcat.h" +#include "enum.h" +#include "manpage.h" + +SymbolTable *enum_table; /* enum symbol table */ + +/* we have to keep a list of EnumeratorLists because: + * - unnamed EnumeratorLists can't go in the symbol table. + * - a single EnumeratorList can be typedef'ed or enum'ed to more than one + * symbol, in which case it is referenced from the typedef_table or + * enum_table repectively more than once. + */ +EnumeratorList *first_list = NULL, **last_next_list = &first_list; + +/* Initialize a list of enumerators.*/ +EnumeratorList * +new_enumerator_list (enumerator) + Enumerator *enumerator; +{ + Enumerator *p; + EnumeratorList *list; + + list = (EnumeratorList *)safe_malloc(sizeof *list); + *last_next_list = list; + last_next_list = &list->next; + list->next = NULL; + + p = (Enumerator *)safe_malloc(sizeof(Enumerator)); + *p = *enumerator; + + list->first = list->last = p; + p->next = NULL; + return list; +} + +/* Add the enumerator to the list. */ +void +add_enumerator_list (list, enumerator) + EnumeratorList *list; + Enumerator *enumerator; +{ + Enumerator *p; + + p = (Enumerator *)safe_malloc((unsigned)sizeof(Enumerator)); + *p = *enumerator; + + list->last->next = p; + list->last = p; + p->next = NULL; +} + +/* Free storage used by the elements in the enumerator list. */ +void +free_enumerator_list (enumerator_list) + EnumeratorList *enumerator_list; +{ + Enumerator *p, *next; + + p = enumerator_list->first; + while (p != NULL) { + next = p->next; + free_enumerator(p); + free(p); + p = next; + } +} + + +void +new_enumerator(e, name, comment_before, comment_after) + Enumerator *e; + char *name; + char *comment_before; + char *comment_after; +{ + e-> name = name; + e-> comment = comment_after ? comment_after : comment_before; + e-> group_comment = comment_before && comment_after ? comment_before : NULL; +} + +/* Free the storage used by the enumerator.*/ +void +free_enumerator (param) + Enumerator *param; +{ + free(param->name); + safe_free(param->comment); + safe_free(param->group_comment); +} + +/* add a comment to the last enumerator in the list */ +int +comment_last_enumerator(list, comment) + EnumeratorList *list; + char *comment; +{ + if (list->last->comment) + { + if (list->last->group_comment) + { + enumerator_error(list->last->name); + free(comment); + return 0; + } + + list->last->group_comment = list->last->comment; + } + + list->last->comment = comment; + return 1; +} + +/* enum namespace management */ +void add_enum_symbol(name, enum_list) + char *name; + EnumeratorList *enum_list; +{ + Symbol *entry = new_symbol(enum_table, name, DS_NONE); + + if (entry) + { + entry->value.enum_list = enum_list; + entry->valtype = SYMVAL_ENUM; + } +} + +/* look for the Enumerator list associated with the symbol */ +EnumeratorList *find_enum_symbol(name) + char *name; +{ + Symbol *entry = find_symbol(enum_table, name); + + if (entry) + return entry->value.enum_list; + else + return NULL; +} + +void destroy_enum_lists() +{ + EnumeratorList *list, *next; + + /* free all the enumerator lists */ + for (list = first_list; list; list = next) + { + next = list->next; + free_enumerator_list(list); + free(list); + } +} + +/* create new typedef symbols */ +void new_typedef_symbols(decl_spec, decl_list) + DeclSpec *decl_spec; + DeclaratorList *decl_list; +{ + Declarator *d; + + for (d = decl_list->first; d; d = d-> next) + { + Symbol *s = new_symbol(typedef_names, d->name, DS_NONE); + + if (s && decl_spec->enum_list) + { + s->value.enum_list = decl_spec->enum_list; + s->valtype = SYMVAL_ENUM; + } + } +} + +void enumerator_error(name) + char *name; +{ + yyerror("enumerator '%s' has multiple comments", name); +} @@ -0,0 +1,41 @@ +/* $Id: enum.h,v 1.1 2004-05-03 05:17:48 behdad Exp $ */ +#include "config.h" + +extern SymbolTable *enum_table; /* enum symbol table */ + +/* Initialize a list of enumerators.*/ +EnumeratorList * +new_enumerator_list _((Enumerator *enumerator)); + +/* Add the enumerator to the list. */ +void +add_enumerator_list _((EnumeratorList *list, Enumerator *enumerator)); + +/* Free storage used by the elements in the enumerator list. */ +void +free_enumerator_list _((EnumeratorList *enumerator_list)); + +void +new_enumerator _((Enumerator *e, char *name, + char *comment_before, char *comment_after)); + +/* Free the storage used by the enumerator.*/ +void +free_enumerator _((Enumerator *param)); + +/* add a comment to the last enumeralor in the list */ +int +comment_last_enumerator _((EnumeratorList *enum_list, char *comment)); + +/* enum namespace management */ +void add_enum_symbol _((char *name, EnumeratorList *first_enum)); + +/* look for the first enumerator associated with the symbol */ +EnumeratorList *find_enum_symbol _((char *name)); + +void destroy_enum_lists(); + +/* create new typedef symbols */ +void new_typedef_symbols _((DeclSpec *decl_spec, DeclaratorList *decl_list)); + +void enumerator_error _((char *name)); diff --git a/example.h b/example.h new file mode 100644 index 0000000..61ac8d5 --- /dev/null +++ b/example.h @@ -0,0 +1,18 @@ +enum Place +{ + HOME, /* Home, Sweet Home */ + WORK, /* where I spend lots of time */ + MOVIES, /* Saturday nights mainly */ + CITY, /* New York, New York */ + COUNTRY /* Bob's Country Bunker */ +}; + +/* + * do some useful work for a change. + * This function will actually get some productive + * work done, if you are really lucky. + * returns the number of milliseconds in a second. + */ +int dowork(int count, /* how much work to do */ + enum Place where, /* where to do the work */ + long fiveoclock /* when to knock off */); diff --git a/fixexamp.in b/fixexamp.in new file mode 100644 index 0000000..7131705 --- /dev/null +++ b/fixexamp.in @@ -0,0 +1,60 @@ +# $Id: fixexamp.in,v 1.1 2004-05-03 05:17:49 behdad Exp $ +# Fix up c2man output for inclusion of examples in the c2man manual page. +# This file gets processed to remove comments because some sed's can't handle +# them. Can you believe it?. + +# The double backslashes in variable interpolations are because sed consumes +# one, not because we're defining macros here (we aren't). + +# special hack: rename ctype_ex to ctype just to make that example neat. +s/ctype_ex/ctype/ + +# replace the .TH line with a title line. +# we only alter the title length just as the title line is output so an example +# that goes over a page break doesn't damage the real manual page's header. +# Also, calculate and remember the shortened line length: groff doesn't reset +# .ll in .SH & .TP macros, so we use an absolute value later rather than +# relative changes. +/^\.TH /{ +s/^\.TH "\([^"]*\)" \([^ ]*\) .*/.ds [H \1\\|(\\|\2\\|)/ +a\ +.ds [D UNIX Programmer's Manual\ +.po +1i\ +.lt -1.5i\ +.tl @\\*([H@\\*([D@\\*([H@\ +.lt +1.5i\ +.po -1i\ +.RS +1i\ +.nr CL \\n(.l-0.5i +} + +# surround all the .SH and .SS lines with page offset indents +# the .ne line will force a page break before the .po is evaluated, in the case +# where a .SH in an example is at the very bottom of the page. Otherwise the .SH +# could cause the break, resulting in the wrong .po setting for the new page's +# title line. +/^\.S[SH] /{ +i\ +.br\ +.ne 3\ +.RE\ +.po +1i +a\ +.po -1i\ +.RS +1i\ +.ll \\n(CLu +} + +# handle tagged paragraphs; .TP retains .RS setting, but resets .ll +# since the parameter to .TP is on the next line, we read the next line in first +/^.TP/{ +n +a\ +.ll \\n(CLu +} + +# up the line length again at the end of the example file +$a\ +.ll \\n(CLu+0.5i\ +.RE + diff --git a/flatten.SH b/flatten.SH new file mode 100644 index 0000000..4dafdc3 --- /dev/null +++ b/flatten.SH @@ -0,0 +1,51 @@ +case $CONFIG in +'') + if test -f config.sh; then TOP=.; + elif test -f ../config.sh; then TOP=..; + elif test -f ../../config.sh; then TOP=../..; + elif test -f ../../../config.sh; then TOP=../../..; + elif test -f ../../../../config.sh; then TOP=../../../..; + else + echo "Can't find config.sh."; exit 1 + fi + . $TOP/config.sh + ;; +esac +: This forces SH files to create target in same directory as SH file. +: This is so that make depend always knows where to find SH derivatives. +case "$0" in +*/*) cd `expr X$0 : 'X\(.*\)/'` ;; +esac +echo "Extracting flatten.sed (with variable substitutions)" +: This section of the file will have variable substitutions done on it. +: Move anything that needs config subs from !NO!SUBS! section to !GROK!THIS!. +: Protect any dollar signs and backticks that you do not want interpreted +: by putting a backslash in front. You may delete these comments. +$spitshell >flatten.sed <<!GROK!THIS! +s!\$(privlib)!$privlib! +!GROK!THIS! + +: In the following dollars and backticks do not need the extra backslash. +$spitshell >>flatten.sed <<'!NO!SUBS!' +/^.so *example.h$/{ +r example.h +d +} + +/^.so *example.inc$/{ +r example.inc +d +} + +/^.so *ctype_ex.h$/{ +r ctype_ex.h +d +} + +/^.so *ctype_ex.inc$/{ +r ctype_ex.inc +d +} +!NO!SUBS! +chmod 755 flatten.sed +$eunicefix flatten.sed diff --git a/grammar.y b/grammar.y new file mode 100644 index 0000000..9721dbe --- /dev/null +++ b/grammar.y @@ -0,0 +1,947 @@ +/* $Id: grammar.y,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * + * yacc grammar for C manual page generator + * This was derived from the grammar given in Appendix A of + * "The C Programming Language" by Kernighan and Ritchie. + */ + +/* identifiers that are not reserved words */ +%token T_IDENTIFIER T_TYPEDEF_NAME + +/* storage class */ +%token T_AUTO T_EXTERN T_REGISTER T_STATIC T_TYPEDEF +/* This keyword included for compatibility with C++. */ +%token T_INLINE + +/* type specifiers */ +%token T_CHAR T_DOUBLE T_FLOAT T_INT T_VOID +%token T_LONG T_SHORT T_SIGNED T_UNSIGNED +%token T_ENUM T_STRUCT T_UNION + +/* type qualifiers */ +%token T_CONST T_VOLATILE +/* These keywords included for compatibility with MSDOS C compilers. */ +%token T_CDECL T_FAR T_HUGE T_INTERRUPT T_NEAR T_PASCAL + +/* paired braces and everything between them: { ... } */ +%token T_BRACES + +/* paired square brackets and everything between them: [ ... ] */ +%token T_BRACKETS + +/* three periods */ +%token T_ELLIPSIS + +/* equal sign followed by constant expression or stuff between braces */ +%token T_INITIALIZER + +/* string literal */ +%token T_STRING_LITERAL + +/* text inside a regular comment, and one at the end of a non-empty line */ +%token T_COMMENT T_EOLCOMMENT + +%type <declaration> declaration +%type <parameter> function_definition +%type <decl_spec> declaration_specifiers declaration_specifier +%type <decl_spec> storage_class type_specifier type_qualifier +%type <decl_spec> struct_or_union_specifier enum_specifier +%type <decl_list> declarator_list init_declarator_list +%type <declarator> init_declarator declarator direct_declarator +%type <declarator> parameter_declarator abstract_parameter_declarator +%type <declarator> abstract_declarator direct_abstract_declarator +%type <param_list> parameter_type_list parameter_list +%type <parameter> parameter_declaration +%type <param_list> opt_identifier_list identifier_list +%type <enumerator> enumerator +%type <enum_list> enumerator_list +%type <identifier> identifier +%type <text> struct_or_union +%type <text> pointer type_qualifier_list +%type <text> opt_comment opt_eolcomment +%type <text> any_id T_IDENTIFIER T_TYPEDEF_NAME +%type <text> T_BRACKETS +%type <text> T_COMMENT T_EOLCOMMENT T_STRING_LITERAL + +%{ +#include "c2man.h" +#include "semantic.h" +#include "strconcat.h" +#include "strappend.h" +#include "manpage.h" +#include "enum.h" + +#ifdef I_STDARG +#include <stdarg.h> +#endif +#ifdef I_VARARGS +#include <varargs.h> +#endif + +int yylex(); + +#define YYMAXDEPTH 150 + +/* where are we up to scanning through an enum? */ +static enum { NOENUM, KEYWORD, BRACES } enum_state = NOENUM; + +/* Pointer to parameter list for the current function definition. */ +static ParameterList *func_params; + +/* Table of typedef names */ +SymbolTable *typedef_names; + +boolean first_comment; /* are we still looking for the first comment? */ +static char *body_comment = NULL;/* last comment found at start of func body */ +%} +%% + +program + : /* empty */ + | translation_unit + ; + +translation_unit + : external_declaration + | translation_unit external_declaration + ; + +external_declaration + : declaration opt_eolcomment + { + remember_declarations(NULL, &$1.decl_spec, &$1.decl_list, $2); + } + | T_COMMENT declaration opt_eolcomment + { + remember_declarations($1, &$2.decl_spec, &$2.decl_list, $3); + } + | function_definition opt_eolcomment + { + if (look_at_body_start && body_comment) { + /* Use the body comment */ + new_manual_page(body_comment,&$1.decl_spec,$1.declarator); + body_comment = NULL; /* Prevent it being free'ed */ + } else { + free_declarator($1.declarator); + free_decl_spec(&$1.decl_spec); + } + } + | T_COMMENT function_definition opt_eolcomment + { + if (body_start_only) { + if (body_comment) { + new_manual_page(body_comment,&$2.decl_spec,$2.declarator); + body_comment = NULL; /* Prevent it being free'ed */ + } else { + free_declarator($2.declarator); + free_decl_spec(&$2.decl_spec); + safe_free($1); + } + } else { + new_manual_page($1,&$2.decl_spec,$2.declarator); + } + safe_free($3); + } + | function_definition ';' opt_eolcomment + { + if (look_at_body_start && body_comment) { + new_manual_page(body_comment,&$1.decl_spec,$1.declarator); + body_comment = NULL; /* Prevent it being free'ed */ + } else { + free_declarator($1.declarator); + free_decl_spec(&$1.decl_spec); + } + safe_free($3); + } + | T_COMMENT function_definition ';' opt_eolcomment + { + if (body_start_only) { + if (body_comment) { + new_manual_page(body_comment,&$2.decl_spec,$2.declarator); + body_comment = NULL; /* Prevent it being free'ed */ + } else { + free_declarator($2.declarator); + free_decl_spec(&$2.decl_spec); + safe_free($1); + } + } else { + new_manual_page($1,&$2.decl_spec,$2.declarator); + } + safe_free($4); + } + | linkage_specification + | T_COMMENT T_EOLCOMMENT + { + free($1); + free($2); + } + | T_COMMENT + { + if (inbasefile && first_comment) + { + remember_terse($1); + first_comment = FALSE; + } + free($1); + } + | T_EOLCOMMENT + { + free($1); + } + | error ';' + { + yyerrok; + } + ; + +linkage_specification + : T_EXTERN T_STRING_LITERAL T_BRACES + { + /* Provide an empty action here so bison will not complain about + * incompatible types in the default action it normally would + * have generated. + */ + } + | T_EXTERN T_STRING_LITERAL declaration + { + /* empty */ + } + ; + +function_definition + : declaration_specifiers declarator opt_eolcomment + { + if ($2->type != DECL_FUNCTION) { + yyerror("syntax error"); + YYERROR; + } + func_params = &($2->head->params); + if ($3) comment_last_parameter(&$2->head->params, $3); + } + opt_declaration_list T_BRACES + { + func_params = NULL; + $2->type = DECL_FUNCDEF; + + $$.decl_spec = $1; + $$.declarator = $2; + } + | declarator opt_eolcomment + { + if ($1->type != DECL_FUNCTION) { + yyerror("syntax error"); + YYERROR; + } + func_params = &($1->head->params); + if ($2) comment_last_parameter(&$1->head->params, $2); + } + opt_declaration_list T_BRACES + { + DeclSpec decl_spec; + + func_params = NULL; + $1->type = DECL_FUNCDEF; + + new_decl_spec(&$$.decl_spec, "int", DS_NONE); + $$.declarator = $1; + } + ; + +declaration + : declaration_specifiers ';' + { + $$.decl_spec = $1; + $$.decl_list.first = NULL; + } + | declaration_specifiers init_declarator_list ';' + { + $$.decl_spec = $1; + $$.decl_list = $2; + } + | T_TYPEDEF declaration_specifiers declarator_list ';' + { + new_typedef_symbols(&$2,&$3); + $$.decl_spec = $2; + $$.decl_list = $3; + } + ; + +declarator_list + : declarator + { + new_decl_list(&$$, $1); + } + | declarator_list ',' opt_eolcomment declarator + { + if ($3) comment_last_decl(&$1, $3); + add_decl_list(&$$, &$1, $4); + } + | declarator_list opt_eolcomment + { + $$ = $1; + if ($2) comment_last_decl(&$1, $2); + } + ; + +opt_declaration_list + : /* empty */ + | declaration_list + | declaration_list T_ELLIPSIS + ; + +declaration_list + : opt_comment declaration opt_eolcomment + { + set_param_types(func_params, &$2.decl_spec, &$2.decl_list, $1, $3); + } + | declaration_list opt_comment declaration opt_eolcomment + { + set_param_types(func_params, &$3.decl_spec, &$3.decl_list, $2, $4); + } + ; + +declaration_specifiers + : declaration_specifier + | declaration_specifiers declaration_specifier + { + join_decl_specs(&$$, &$1, &$2); + } + ; + +declaration_specifier + : storage_class + | type_specifier + | type_qualifier + ; + +storage_class + : T_AUTO + { + new_decl_spec(&$$, "auto", DS_NONE); + } + | T_EXTERN + { + new_decl_spec(&$$, "extern", DS_EXTERN); + } + | T_REGISTER + { + new_decl_spec(&$$, "register", DS_NONE); + } + | T_STATIC + { + new_decl_spec(&$$, "static", DS_STATIC); + } + | T_INLINE + { + new_decl_spec(&$$, "inline", DS_INLINE); + } + ; + +type_specifier + : T_CHAR + { + new_decl_spec(&$$, "char", DS_CHAR); + } + | T_DOUBLE + { + new_decl_spec(&$$, "double", DS_NONE); + } + | T_FLOAT + { + new_decl_spec(&$$, "float", DS_FLOAT); + } + | T_INT + { + new_decl_spec(&$$, "int", DS_NONE); + } + | T_LONG + { + new_decl_spec(&$$, "long", DS_NONE); + } + | T_SHORT + { + new_decl_spec(&$$, "short", DS_SHORT); + } + | T_SIGNED + { + new_decl_spec(&$$, "signed", DS_NONE); + } + | T_UNSIGNED + { + new_decl_spec(&$$, "unsigned", DS_NONE); + } + | T_VOID + { + new_decl_spec(&$$, "void", DS_NONE); + } + | struct_or_union_specifier + | enum_specifier + | T_TYPEDEF_NAME + { + Symbol *s = find_symbol(typedef_names, $1); + + new_enum_decl_spec(&$$, $1, s->flags, + s->valtype == SYMVAL_ENUM ? s->value.enum_list + : (EnumeratorList *)NULL); + } + ; + +type_qualifier + : T_CONST + { + new_decl_spec(&$$, "const", DS_NONE); + } + | T_VOLATILE + { + new_decl_spec(&$$, "volatile", DS_NONE); + } + | T_CDECL + { + new_decl_spec(&$$, "cdecl", DS_NONE); + } + | T_INTERRUPT + { + new_decl_spec(&$$, "interrupt", DS_NONE); + } + | T_FAR + { + new_decl_spec(&$$, "far", DS_NONE); + } + | T_HUGE + { + new_decl_spec(&$$, "huge", DS_NONE); + } + | T_NEAR + { + new_decl_spec(&$$, "near", DS_NONE); + } + | T_PASCAL + { + new_decl_spec(&$$, "pascal", DS_NONE); + } + ; + +struct_or_union_specifier + : struct_or_union any_id T_BRACES + { + dyn_decl_spec(&$$, strconcat($1, " ",$2," {}",NULLCP), DS_NONE); + free($2); + } + | struct_or_union T_BRACES + { + dyn_decl_spec(&$$, strconcat($1," {}",NULLCP), DS_NONE); + } + | struct_or_union any_id + { + dyn_decl_spec(&$$, strconcat($1, " ",$2,NULLCP), DS_NONE); + free($2); + } + ; + +struct_or_union + : T_STRUCT + { + $$ = "struct"; + } + | T_UNION + { + $$ = "union"; + } + ; + +init_declarator_list + : init_declarator + { + new_decl_list(&$$, $1); + } + | init_declarator_list ',' opt_eolcomment init_declarator + { + if ($3) comment_last_decl(&$1, $3); + add_decl_list(&$$, &$1, $4); + } + | init_declarator_list opt_eolcomment + { + $$ = $1; + if ($2) comment_last_decl(&$1, $2); + } + ; + +init_declarator + : declarator + | declarator T_INITIALIZER + ; + +enum_specifier + : T_ENUM any_id '{' opt_eolcomment enumerator_list '}' + { + add_enum_symbol($2, $5); + new_enum_decl_spec(&$$, strconcat("enum ",$2," {}",NULLCP), + DS_NONE, $5); + free($2); + safe_free($4); + enum_state = NOENUM; + } + | T_ENUM '{' opt_eolcomment enumerator_list '}' + { + new_enum_decl_spec(&$$, strduplicate("enum {}"), DS_NONE, $4); + safe_free($3); + enum_state = NOENUM; + } + | T_ENUM any_id + { + new_enum_decl_spec(&$$, strconcat("enum ",$2,NULLCP), DS_NONE, + find_enum_symbol($2)); + free($2); + enum_state = NOENUM; + } + ; + +enumerator_list + : enumerator + { + $$ = new_enumerator_list(&$1); + } + | enumerator_list ',' opt_eolcomment enumerator + { + $$ = $1; + if ($3) comment_last_enumerator($$, $3); + add_enumerator_list($$, &$4); + } + | enumerator_list opt_eolcomment + { + $$ = $1; + if ($2) comment_last_enumerator($$, $2); + } + | enumerator_list ',' opt_eolcomment + { + $$ = $1; + if ($3) comment_last_enumerator($$, $3); + } + ; + +enumerator + : identifier + { + new_enumerator(&$$,$1.name,$1.comment_before,$1.comment_after); + } + ; + +any_id + : T_IDENTIFIER + | T_TYPEDEF_NAME + | any_id T_COMMENT + { + $$ = $1; + free($2); + } + | any_id T_EOLCOMMENT + { + $$ = $1; + free($2); + } + ; + +declarator + : pointer T_EOLCOMMENT direct_declarator + { + char *newtext = strappend($1,$3->text,NULLCP); + free($3->text); + $$ = $3; + $$->text = newtext; + if ($$->type == DECL_SIMPLE) + $$->type = DECL_COMPOUND; + $$->retcomment = $2; + } + | pointer direct_declarator + { + char *newtext = strappend($1,$2->text,NULLCP); + free($2->text); + $$ = $2; + $$->text = newtext; + if ($$->type == DECL_SIMPLE) + $$->type = DECL_COMPOUND; + } + | T_EOLCOMMENT direct_declarator + { + $$ = $2; + $$->retcomment = $1; + } + | direct_declarator + { + $$ = $1; + } + ; + +parameter_declarator + : pointer direct_declarator + { + char *newtext = strappend($1,$2->text,NULLCP); + free($2->text); + $$ = $2; + $$->text = newtext; + if ($$->type == DECL_SIMPLE) + $$->type = DECL_COMPOUND; + } + | direct_declarator + { + $$ = $1; + } + ; + +direct_declarator + : T_IDENTIFIER + { + $$ = new_declarator($1, strduplicate($1)); + } + | '(' declarator ')' + { + char *newtext = strconcat("(",$2->text,")",NULLCP); + free($2->text); + $$ = $2; + $$->text = newtext; + } + | direct_declarator T_BRACKETS + { + $$ = $1; + $$->text = strappend($1->text,$2,NULLCP); + free($2); + } + | direct_declarator '(' parameter_type_list ')' + { + $$ = new_declarator(strduplicate("%s()"), strduplicate($1->name)); + $$->params = $3; + $$->func_stack = $1; + $$->head = ($1->func_stack == NULL) ? $$ : $1->head; + $$->type = ($1->type == DECL_SIMPLE) ? DECL_FUNCTION : $1->type; + } + | direct_declarator '(' opt_identifier_list ')' + { + $$ = new_declarator(strduplicate("%s()"), strduplicate($1->name)); + $$->params = $3; + $$->func_stack = $1; + $$->head = ($1->func_stack == NULL) ? $$ : $1->head; + $$->type = ($1->type == DECL_SIMPLE) ? DECL_FUNCTION : $1->type; + } + ; + +pointer + : '*' type_qualifier_list + { + $$ = strconcat("*",$2, NULLCP); + safe_free($2); + } + | '*' type_qualifier_list pointer + { + $$ = $2 ? strconcat("*",$2, $3, NULLCP) + : strconcat("*", $3, NULLCP); + safe_free($2); + free($3); + } + ; + +type_qualifier_list + : /* empty */ + { + $$ = NULL; + } + | type_qualifier_list type_qualifier + { + $$ = $1 ? strconcat($1," ",$2.text," ",NULLCP) + : strconcat($2.text," ",NULLCP); + safe_free($1); + free_decl_spec(&$2); + } + ; + +parameter_type_list + : parameter_list opt_eolcomment + { + $$ = $1; + if ($2) comment_last_parameter(&$1, $2); + } + | parameter_list ',' opt_eolcomment + opt_comment T_ELLIPSIS opt_comment opt_eolcomment + { + Identifier ellipsis; + + if ($3) comment_last_parameter(&$1, $3); + ellipsis.name = strduplicate("..."); + + if ($4 && $6 && $7) + { + yyerror("ellipsis parameter has multiple comments"); + free($7); + free($6); + free($4); + ellipsis.comment_before = ellipsis.comment_after = NULL; + } + else + { + ellipsis.comment_before = $4; + ellipsis.comment_after = $6 ? $6 : $7; + } + + add_ident_list(&$$, &$1, &ellipsis); + } + ; + +parameter_list + : parameter_declaration + { + new_param_list(&$$, &$1); + } + | parameter_list ',' opt_eolcomment parameter_declaration + { + if ($3) comment_last_parameter(&$1, $3); + add_param_list(&$$, &$1, &$4); + } + ; + +parameter_declaration + : opt_comment declaration_specifiers parameter_declarator opt_comment + { + new_parameter(&$$, &$2, $3, $1, $4); + } + | opt_comment declaration_specifiers abstract_parameter_declarator opt_comment + { + new_parameter(&$$, &$2, $3, $1, $4); + } + | opt_comment declaration_specifiers opt_comment + { + new_parameter(&$$, &$2, (Declarator *)NULL, $1, $3); + } + ; + +opt_identifier_list + : /* empty */ + { + new_ident_list(&$$); + } + | identifier_list opt_eolcomment + { + $$ = $1; + if ($2) comment_last_parameter(&$1, $2); + } + ; + +identifier_list + : identifier + { + new_ident_list(&$$); + add_ident_list(&$$, &$$, &$1); + } + | identifier_list ',' opt_eolcomment identifier + { + if ($3) comment_last_parameter(&$1, $3); + add_ident_list(&$$, &$1, &$4); + } + ; + +identifier + : opt_comment T_IDENTIFIER opt_comment + { + $$.comment_before = $1; + $$.comment_after = $3; + $$.name = $2; + } + +abstract_declarator + : pointer + { + $$ = new_declarator($1, NULLCP); + } + | pointer T_EOLCOMMENT direct_abstract_declarator + { + char *newtext = strappend($1,$3->text,NULLCP); + free($3->text); + $$ = $3; + $$->text = newtext; + if ($$->type == DECL_SIMPLE) + $$->type = DECL_COMPOUND; + $$->retcomment = $2; + } + | pointer direct_abstract_declarator + { + char *newtext = strappend($1,$2->text,NULLCP); + free($2->text); + $$ = $2; + $$->text = newtext; + if ($$->type == DECL_SIMPLE) + $$->type = DECL_COMPOUND; + } + | T_EOLCOMMENT direct_abstract_declarator + { + $$ = $2; + $$->retcomment = $1; + } + | direct_abstract_declarator + { + $$ = $1; + } + ; + +abstract_parameter_declarator + : pointer + { + $$ = new_declarator($1, NULLCP); + } + | pointer direct_abstract_declarator + { + char *newtext = strappend($1,$2->text,NULLCP); + free($2->text); + $$ = $2; + $$->text = newtext; + if ($$->type == DECL_SIMPLE) + $$->type = DECL_COMPOUND; + } + | direct_abstract_declarator + { + $$ = $1; + } + ; + +direct_abstract_declarator + : '(' abstract_declarator ')' + { + char *newtext = strconcat("(",$2->text,")",NULLCP); + free($2->text); + $$ = $2; + $$->text = newtext; + } + | direct_abstract_declarator T_BRACKETS + { + $$ = $1; + $$->text = strappend($1->text,$2,NULLCP); + free($2); + } + | T_BRACKETS + { + $$ = new_declarator($1, NULLCP); + } + | direct_abstract_declarator '(' parameter_type_list ')' + { + $$ = new_declarator(strduplicate("%s()"), NULLCP); + $$->params = $3; + $$->func_stack = $1; + $$->head = ($1->func_stack == NULL) ? $$ : $1->head; + $$->type = ($1->type == DECL_SIMPLE) ? DECL_FUNCTION : $1->type; + } + | direct_abstract_declarator '(' ')' + { + $$ = new_declarator(strduplicate("%s()"), NULLCP); + $$->func_stack = $1; + $$->head = ($1->func_stack == NULL) ? $$ : $1->head; + $$->type = ($1->type == DECL_SIMPLE) ? DECL_FUNCTION : $1->type; + } + | '(' parameter_type_list ')' + { + Declarator *d; + + d = new_declarator(NULL, NULL); + $$ = new_declarator(strduplicate("%s()"), NULLCP); + $$->params = $2; + $$->func_stack = d; + $$->head = $$; + } + | '(' ')' + { + Declarator *d; + + d = new_declarator(NULL, NULL); + $$ = new_declarator(strduplicate("%s()"), NULLCP); + $$->func_stack = d; + $$->head = $$; + } + ; + +opt_comment + : /* empty */ + { + $$ = NULL; + } + | T_COMMENT + ; + +opt_eolcomment + : /* empty */ + { + $$ = NULL; + } + | T_EOLCOMMENT + ; + +%% +#ifdef MSDOS +#include "lex_yy.c" +#else +#ifdef VMS +#include "lexyy.c" +#else +#include "lex.yy.c" +#endif /* !VMS */ +#endif /* !MSDOS */ + +#ifdef I_STDARG +void yyerror(const char *format, ...) +#else +void yyerror(va_alist) + va_dcl +#endif +{ +#ifndef I_STDARG + const char *format; +#endif + va_list args; + + output_error(); + +#ifdef I_STDARG + va_start(args, format); +#else + va_start(args); + format = va_arg(args, char *); +#endif + + vfprintf(stderr, format, args); + va_end(args); + putc('.',stderr); + putc('\n',stderr); +} + +void +parse_file (start_file) +const char *start_file; +{ + const char *s; +#ifdef FLEX_SCANNER + static boolean restart = FALSE; +#endif + + cur_file = start_file ? strduplicate(start_file) : NULL; + + if (basefile && strlen(basefile) > 2) { + s = basefile + strlen(basefile) - 2; + if (strcmp(s, ".l") == 0 || strcmp(s, ".y") == 0) + BEGIN LEXYACC; + } + + typedef_names = create_symbol_table(); + enum_table = create_symbol_table(); + + line_num = 1; + ly_count = 0; + first_comment = group_together && !terse_specified; + + /* flex needs a yyrestart before every file but the first */ +#ifdef FLEX_SCANNER + if (restart) yyrestart(yyin); + restart = TRUE; +#endif + + yyparse(); + + destroy_symbol_table(enum_table); + destroy_symbol_table(typedef_names); + + safe_free(cur_file); +} @@ -0,0 +1,453 @@ +/* $Id: html.c,v 1.1 2004-05-03 05:17:49 behdad Exp $ + * functions for html style output. + */ +#include "c2man.h" +#include "manpage.h" +#include "output.h" +#include <ctype.h> + +static int html_in_code = 0; + +void html_terse_sep(); +void html_description _((const char *text)); + +void html_char(c) +const int c; +{ + switch (c) + { + case '<': + put_string("<"); + break; + case '>': + put_string(">"); + break; + case '&': + put_string("&"); + break; + case '"': + put_string("""); + break; + default: + putchar(c); + break; + } +} + +void html_text(text) +const char *text; +{ + while(*text) + { + html_char(*text++); + } +} + + +void html_comment() +{ + put_string("<!"); +} + +void html_header(firstpage, input_files, grouped, name, terse, section) + ManualPage *firstpage; + int input_files; + boolean grouped; + const char *name; + const char *terse; + const char *section; +{ + + output_warning(); + put_string("<header>\n"); + put_string("<title>"); + html_text(name); + html_terse_sep(); + html_text(terse); + put_string("</title>\n"); + put_string("</header>\n"); + put_string("<body>\n"); +} + +void html_file_end() +{ + put_string("\n</body>\n"); +} + +void html_dash() +{ + put_string("-"); +} + +void html_section(name) +const char *name; +{ + put_string("<h1>"); + html_text(name); + put_string("</h1>\n"); +} + +void html_sub_section(name) +const char *name; +{ + put_string("<h2>"); + html_text(name); + put_string("</h2>"); +} + +void html_break_line() +{ + if (!html_in_code) + { + put_string("<br>\n"); + } +} + +void html_blank_line() +{ + if (!html_in_code) + { + put_string("<p>\n"); + } + else + { + putchar('\n'); + } +} + +void html_code_start() +{ + put_string("<pre>"); + html_in_code = 1; +} + +void html_code_end() +{ + put_string("</pre>\n"); + html_in_code = 0; +} + +void html_code(text) +const char *text; +{ + html_code_start(); + html_text(text); + html_code_end(); +} + +void html_tag_list_start() +{ + put_string("<dl>"); +} + +void html_tag_list_end() +{ + put_string("</dl>\n"); +} + +void html_tag_entry_start() +{ + put_string("<dt>\n"); +} + +void html_tag_entry_start_extra() +{ + put_string("<dt>\n"); +} + +void html_tag_entry_end() +{ + put_string("<dd>\n"); +} + +void html_tag_entry_end_extra(text) +const char *text; +{ + put_string(" <em>"); + put_string(text); + put_string("</em>)"); + put_string("<dd>\n"); +} + +void html_table_start(longestag) +const char *longestag; +{ + put_string("<ul>"); +} + +void html_table_entry(name, description) +const char *name; +const char *description; +{ + put_string("<li>"); + html_text(name); + if (description) + { + html_terse_sep(); + html_description(description); + } + put_string("<p>\n"); +} + +void html_table_end() +{ + put_string("</ul>"); +} + +void html_indent() +{ + put_string("\t"); +} + +void html_list_start() +{ + put_string("<ul>"); +} + + +void html_list_end() +{ + put_string("</ul>"); +} + +void html_list_entry(name) +const char *name; +{ + put_string("<li>"); + put_string(name); + put_string("\n"); +} + +void html_list_separator() +{ + put_string(",\n"); +} + +void html_include(filename) +const char *filename; +{ + printf(".so %s\n", filename); +} + +void html_name(name) +const char *name; +{ + if (name) + html_text(name); + else + html_section("NAME"); +} + +void html_terse_sep() +{ + html_char(' '); + html_dash(); + html_char(' '); +} + +void html_reference(name) +const char *name; +{ + put_string("<a href="); + put_string(name); + put_string(".html>"); + put_string(name); + put_string("</a>\n"); +} + +void html_emphasized(text) +const char *text; +{ + put_string("<em>"); + put_string(text); + put_string("</em>"); +} + +/* ideally, this should be made aware of embedded html commands */ +void html_description(text) +const char *text; +{ + enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE; + boolean new_line = TRUE; + + /* correct punctuation a bit as it goes out */ + for (;*text;text++) + { + int c = *text; + + if (new_line && (c == '-' || c == '*' || c == '\n' || + is_numbered(text))) + { + output->break_line(); + state = CAPITALISE; + } + else if (new_line && c == '\n') { /* Two newlines - Paragraph break */ + output->blank_line(); + } + else if (c == '.') + state = PERIOD; + else if (isspace(c) && state == PERIOD) + state = CAPITALISE; + else if (isalnum(c)) + { + if (islower(c) && state == CAPITALISE) c = toupper(c); + state = TEXT; + } + + output->character(c); + new_line = c == '\n'; + } + + /* do a full stop if there wasn't one */ + if (state == TEXT) output->character('.'); +} + +/* ideally, this should be made aware of embedded html commands */ +void +html_returns(comment) +const char *comment; +{ + enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE; + char lastchar = '\n'; + boolean tag_list_started = FALSE; + + /* for each line... */ + while (*comment) + { + boolean tagged = FALSE; + + { + const char *c = comment; + + /* search along until the end of a word */ + while (*c && *c != ':' && !isspace(*c)) + c++; + + /* skip all spaces or tabs after the first word */ + while (*c && *c != '\n') + { + if (*c == '\t' || *c == ':') + { + tagged = TRUE; + break; + } + else if (!isspace(*c)) + break; + + c++; + } + } + + /* is it tagged?; explicitly reject dot commands */ + if (tagged) + { + /* output lingering newline if necessary */ + if (lastchar != '\n') + { + if (state == TEXT && !ispunct(lastchar)) output->character('.'); + output->character(lastchar = '\n'); + } + + if (!tag_list_started) + { + output->tag_list_start(); + tag_list_started = TRUE; + } + + /* output the taggy bit */ + output->tag_entry_start(); + while (*comment && *comment != ':' && !isspace(*comment)) + output->character(*comment++); + output->tag_entry_end(); + + /* skip any extra tabs or spaces */ + while (*comment == ':' || (isspace(*comment) && *comment != '\n')) + comment++; + + state = CAPITALISE; + } + + /* terminate the previous line if necessary */ + if (lastchar != '\n') output->character(lastchar = '\n'); + + /* correct punctuation a bit as the line goes out */ + for (;*comment && *comment != '\n'; comment++) + { + char c = *comment; + + if (c == '.') + state = PERIOD; + else if (isspace(c) && state == PERIOD) + state = CAPITALISE; + else if (isalnum(c)) + { + if (islower(c) && state == CAPITALISE && fixup_comments) + c = toupper(c); + state = TEXT; + } + + output->character(lastchar = c); + } + + /* if it ended in punctuation, just output the nl straight away. */ + if (ispunct(lastchar)) + { + if (lastchar == '.') state = CAPITALISE; + output->character(lastchar = '\n'); + } + + if (*comment) comment++; + } + + /* output lingering newline if necessary */ + if (lastchar != '\n') + { + if (state == TEXT && !ispunct(lastchar) && fixup_comments) + output->character('.'); + output->character('\n'); + } + + if (tag_list_started) + output->tag_list_end(); +} + + +struct Output html_output = +{ + html_comment, + html_header, + html_dash, + html_section, + html_sub_section, + html_break_line, + html_blank_line, + html_code_start, + html_code_end, + html_code, + html_tag_list_start, + html_tag_list_end, + html_tag_entry_start, + html_tag_entry_start_extra, + html_tag_entry_end, + html_tag_entry_end_extra, + html_table_start, + html_table_entry, + html_table_end, + html_indent, + html_list_start, + html_list_entry, + html_list_separator, + html_list_end, + html_include, + html_file_end, + html_text, + html_char, + NULL, /* html_parse_option */ + dummy, /* html_print_options */ + html_name, + html_terse_sep, + html_reference, + html_emphasized, + html_description, + html_returns + }; @@ -0,0 +1,356 @@ +/* $Id: latex.c,v 1.1 2004-05-03 05:17:49 behdad Exp $ + * functions for LaTeX style output. + */ +#include "c2man.h" +#include "manpage.h" +#include "output.h" +#include <ctype.h> + +static boolean verbatim = FALSE; /* skip quoting in verbatim section */ + +void latex_char(c) +const int c; +{ + int i; + + switch (c) + { + case '$': + case '#': + case '&': + case '^': + case '_': + if (!verbatim) + putchar('\\'); + putchar(c); + break; + case '>': + case '<': + if (!verbatim) + putchar('$'); + putchar(c); + if (!verbatim) + putchar('$'); + break; + case '\t': + for (i = 0; i < NUM_TAB_SPACES; i++) + putchar(' '); + break; + default: + putchar(c); + break; + } +} + +void latex_text(text) +const char *text; +{ + while(*text) + latex_char(*text++); +} + +void latex_comment() { put_string("% "); } + +void latex_header(firstpage, input_files, grouped, name, terse, section) +ManualPage *firstpage; +int input_files; +boolean grouped; +const char *name; +const char *terse; +const char *section; +{ + if (make_embeddable) return; + + put_string("\\documentstyle{article}\n"); + output_warning(); + put_string("\\begin{document}\n"); +} + +void latex_dash() { put_string("---"); } + +void latex_section(name) +const char *name; +{ + put_string("\\section*{"); + latex_text(name); + put_string("}\n"); +} + +void latex_sub_section(name) +const char *name; +{ + put_string("\\subsection*{"); + latex_text(name); + put_string("}\n"); +} + +void latex_break_line() { /* put_string("\\newline\n"); */ } +void latex_blank_line() { put_string("\n"); } + +void latex_code_start() { put_string("\\begin{verbatim}\n"); verbatim = TRUE; } +void latex_code_end() { put_string("\\end{verbatim}\n"); verbatim = FALSE; } + +void latex_code(text) +const char *text; +{ + put_string("\\verb`"); + put_string(text); + put_string("`"); +} + +void latex_tag_list_start() { put_string("\\begin{description}\n"); } +void latex_tag_entry_start() { put_string("\\item["); } +void latex_tag_entry_end() { put_string("]\\hfill\\newline\n"); } +void latex_tag_entry_end_extra(text) +const char *text; +{ + put_string("("); + latex_text(text); + put_string(")"); + latex_tag_entry_end(); +} +void latex_tag_list_end() { put_string("\\end{description}\n"); } + +void latex_table_start(longestag) +const char *longestag; +{ put_string("\\begin{description}\n"); } + +void latex_table_entry(name, description) +const char *name; +const char *description; +{ + put_string("\\item["); + latex_text(name); + put_string("]\n"); + if (description) + output_comment(description); + else + latex_char('\n'); +} + +void latex_table_end() { put_string("\\end{description}\n"); } + +void latex_list_entry(text) +const char *text; +{ + latex_text(text); +} +void latex_list_separator() { put_string(",\n"); } +void latex_list_end() { latex_char('\n'); } + +void latex_include(filename) +const char *filename; +{ + put_string("\\include{"); + latex_text(filename); + put_string("}\n"); +} + +void latex_file_end() { put_string("\\end{document}\n"); } + +void latex_name(name) +const char *name; +{ + if (name) latex_text(name); + else latex_section("NAME"); +} + +void latex_terse_sep() +{ + latex_char(' '); + latex_dash(); + latex_char(' '); +} + +void latex_reference(text) +const char *text; +{ + latex_text(text); + latex_char('('); + latex_text(manual_section); + latex_char(')'); +} + +/* ideally, this should be made aware of embedded latex commands */ +void latex_description(text) +const char *text; +{ + enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE; + boolean new_line = TRUE; + + /* correct punctuation a bit as it goes out */ + for (;*text;text++) + { + int c = *text; + + if (new_line && (c == '-' || c == '*')) + { + output->break_line(); + state = CAPITALISE; + } + else if (c == '.') + state = PERIOD; + else if (isspace(c) && state == PERIOD) + state = CAPITALISE; + else if (isalnum(c)) + { + if (islower(c) && state == CAPITALISE) c = toupper(c); + state = TEXT; + } + + output->character(c); + new_line = c == '\n'; + } + + /* do a full stop if there wasn't one */ + if (state == TEXT) output->character('.'); +} + +/* ideally, this should be made aware of embedded latex commands */ +void +latex_returns(comment) +const char *comment; +{ + enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE; + char lastchar = '\n'; + boolean tag_list_started = FALSE; + + /* for each line... */ + while (*comment) + { + boolean tagged = FALSE; + + { + const char *c = comment; + + /* search along until the end of a word */ + while (*c && *c != ':' && !isspace(*c)) + c++; + + /* skip all spaces or tabs after the first word */ + while (*c && *c != '\n') + { + if (*c == '\t' || *c == ':') + { + tagged = TRUE; + break; + } + else if (!isspace(*c)) + break; + + c++; + } + } + + /* is it tagged?; explicitly reject dot commands */ + if (tagged) + { + /* output lingering newline if necessary */ + if (lastchar != '\n') + { + if (state == TEXT && !ispunct(lastchar)) output->character('.'); + output->character(lastchar = '\n'); + } + + if (!tag_list_started) + { + output->tag_list_start(); + tag_list_started = TRUE; + } + + /* output the taggy bit */ + output->tag_entry_start(); + while (*comment && *comment != ':' && !isspace(*comment)) + output->character(*comment++); + output->tag_entry_end(); + + /* skip any extra tabs or spaces */ + while (*comment == ':' || (isspace(*comment) && *comment != '\n')) + comment++; + + state = CAPITALISE; + } + + /* terminate the previous line if necessary */ + if (lastchar != '\n') output->character(lastchar = '\n'); + + /* correct punctuation a bit as the line goes out */ + for (;*comment && *comment != '\n'; comment++) + { + char c = *comment; + + if (c == '.') + state = PERIOD; + else if (isspace(c) && state == PERIOD) + state = CAPITALISE; + else if (isalnum(c)) + { + if (islower(c) && state == CAPITALISE && fixup_comments) + c = toupper(c); + state = TEXT; + } + + output->character(lastchar = c); + } + + /* if it ended in punctuation, just output the nl straight away. */ + if (ispunct(lastchar)) + { + if (lastchar == '.') state = CAPITALISE; + output->character(lastchar = '\n'); + } + + if (*comment) comment++; + } + + /* output lingering newline if necessary */ + if (lastchar != '\n') + { + if (state == TEXT && !ispunct(lastchar) && fixup_comments) + output->character('.'); + output->character('\n'); + } + + if (tag_list_started) + output->tag_list_end(); +} + +struct Output latex_output = +{ + latex_comment, + latex_header, + latex_dash, + latex_section, + latex_sub_section, + latex_break_line, + latex_blank_line, + latex_code_start, + latex_code_end, + latex_code, + latex_tag_list_start, + latex_tag_list_end, + latex_tag_entry_start, + latex_tag_entry_start, /* entry_start_extra */ + latex_tag_entry_end, + latex_tag_entry_end_extra, + latex_table_start, + latex_table_entry, + latex_table_end, + dummy, /* latex_indent */ + dummy, /* latex_list_start */ + latex_list_entry, + latex_list_separator, + latex_list_end, + latex_include, + latex_file_end, + latex_text, + latex_char, + NULL, /* latex_parse_option */ + dummy, /* latex_print_option */ + latex_name, + latex_terse_sep, + latex_reference, + latex_text, + latex_description, + latex_returns +}; @@ -0,0 +1,621 @@ +%{ +/* $Id: lex.l,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * + * C manual page generator + * Lexical analyzer specification + */ + +#include <ctype.h> + +extern boolean inbasefile; /* Steven Haehn Mar 19, 1996 */ + +static char *cur_file; /* current file name (malloced) */ +int line_num = 1; /* current line number in file */ +static int curly = 0; /* number of curly brace nesting levels */ +static int square = 0; /* number of square bracket nesting levels */ +static int ly_count = 0; /* number of occurrences of %% */ +static int embedded = 0; /* flag for embedded compiler directives */ + +/* temporary string buffer */ +static char buf[MAX_TEXT_LENGTH]; + +#define DYNBUF_ALLOC 240 /* size of increment of dynamic buf */ +static char *dynbuf; /* start of dynamic buf */ +static int dynbuf_size; /* number of bytes allocated */ +static int dynbuf_current; /* current end of buffer */ + +static boolean comment_ateol; /* does comment start & end at end of a line? */ +static boolean comment_remember;/* remember contents of current comment? */ +static boolean comment_caller; /* state we were in before */ +static boolean body_start = FALSE; /* At the start of a function body */ + +typedef struct { +#ifdef FLEX_SCANNER + YY_BUFFER_STATE buffer; +#else + FILE *fp; +#endif + char *file; + int line_num; +} IncludeStack; + +static int inc_depth = 0; /* include nesting level */ +static IncludeStack inc_stack[MAX_INC_DEPTH]; /* stack of included files */ + +static void update_line_num _((void)); +static void do_include _((char *filename, int sysinc)); +static void new_dynbuf(); +static void add_dynbuf _((int c)); +static char *return_dynbuf(); +static void get_cpp_directive(); +static boolean process_line_directive _((const char *new_file)); + +/* + * The initial comment processing is done primarily by the rather complex lex + * rules in the various comment start states, the main functions being removal + * of leading *'s, /'s and whitespace on a line, the removal of trailing + * whitespace on a line, and the coalescing of separate comments on adjacent + * lines. The remaining bits of textual content are collected by the following + * functions, which simply strip leading and trailing blank lines. + */ +void start_comment _((boolean ateol)); +int end_comment _((boolean ateol)); +void add_comment _((const char *s)); +void newline_comment _((void)); + +static int comment_newlines; /* number of newlines hit in comment */ +static boolean comment_started; /* have preceding empty lines been skipped */ + +#ifdef FLEX_SCANNER /* flex uses YY_START instead of YYSTATE */ +#define YYSTATE YY_START +#ifndef YY_START /* flex 2.3.8 & before didn't support it at all */ +#define YY_START ((yy_start - 1) / 2) +#endif +#endif + +#undef yywrap /* for flex */ + +/* SKIP skipping value assignment in an enum */ +%} + +WS [ \t] +WLF [ \t\n\f]* +LETTER [A-Za-z_] +DIGIT [0-9] +ID {LETTER}({LETTER}|{DIGIT})* +STRING \"(\\.|\\\n|[^"\\])*\" +QUOTED ({STRING}|\'(\\\'|[^'\n])*\'|\\.) + +%p 5000 +%e 2000 +%s CPP1 INIT1 INIT2 CURLY SQUARE LEXYACC SKIP COMMENT COMMLINE CPPCOMMENT EMBEDDED +%% + + +<LEXYACC>^"%%" { + if (++ly_count >= 2) + BEGIN INITIAL; + } +<LEXYACC>^"%{" BEGIN INITIAL; +<LEXYACC>{QUOTED} update_line_num(); +<LEXYACC>. ; +<INITIAL>^"%}" BEGIN LEXYACC; + +<INITIAL>^{WS}*#{WS}* BEGIN CPP1; + +<CPP1>define{WS}+{ID} { + sscanf(yytext, "define %s", buf); + get_cpp_directive(); + new_symbol(typedef_names, buf, DS_EXTERN); + } + +<CPP1>include{WS}*\"[^"]+\" { + sscanf(yytext, "include \"%[^\"]\"", buf); + get_cpp_directive(); + do_include(buf, FALSE); + } +<CPP1>include{WS}*\<[^>]+\> { + sscanf(yytext, "include <%[^>]>", buf); + get_cpp_directive(); + do_include(buf, TRUE); + } + +<CPP1>line{WS}+[0-9]+{WS}+\".*$ { + sscanf(yytext, "line %d \"%[^\"]\"", + &line_num, buf); + --line_num; + BEGIN INITIAL; + + if (process_line_directive(buf)) + inbasefile = yylval.boolean; + } +<CPP1>[0-9]+{WS}+\".*$ { + sscanf(yytext, "%d \"%[^\"]\"", &line_num, buf); + --line_num; + BEGIN INITIAL; + + if (process_line_directive(buf)) + inbasefile = yylval.boolean; + } +<CPP1>[0-9]+.*$ { + sscanf(yytext, "%d ", &line_num); + --line_num; + BEGIN INITIAL; + } + +<CPP1>. get_cpp_directive(); + +<INITIAL>"(" return '('; +<INITIAL>")" return ')'; +<INITIAL>"*" return '*'; +<INITIAL,SKIP>"," { + BEGIN INITIAL; /* stop skipping */ + return ','; + } +<INITIAL>";" return ';'; +<INITIAL>"..." return T_ELLIPSIS; +<INITIAL>{STRING} { update_line_num(); return T_STRING_LITERAL; } + +<INITIAL>auto return T_AUTO; +<INITIAL>extern return T_EXTERN; +<INITIAL>register return T_REGISTER; +<INITIAL>static return T_STATIC; +<INITIAL>typedef return T_TYPEDEF; +<INITIAL>char return T_CHAR; +<INITIAL>double return T_DOUBLE; +<INITIAL>float return T_FLOAT; +<INITIAL>int return T_INT; +<INITIAL>void return T_VOID; +<INITIAL>long return T_LONG; +<INITIAL>short return T_SHORT; +<INITIAL>signed return T_SIGNED; +<INITIAL>__signed__ return T_SIGNED; +<INITIAL>__signed return T_SIGNED; +<INITIAL>unsigned return T_UNSIGNED; +<INITIAL>enum { enum_state = KEYWORD; return T_ENUM; } +<INITIAL>struct return T_STRUCT; +<INITIAL>union return T_UNION; +<INITIAL>const return T_CONST; +<INITIAL>__const__ return T_CONST; +<INITIAL>__const return T_CONST; +<INITIAL>volatile return T_VOLATILE; +<INITIAL>__volatile__ return T_VOLATILE; +<INITIAL>__volatile return T_VOLATILE; +<INITIAL>inline return T_INLINE; +<INITIAL>__inline__ return T_INLINE; +<INITIAL>__inline return T_INLINE; +<INITIAL>cdecl return T_CDECL; +<INITIAL>far return T_FAR; +<INITIAL>huge return T_HUGE; +<INITIAL>interrupt return T_INTERRUPT; +<INITIAL>near return T_NEAR; +<INITIAL>pascal return T_PASCAL; +<INITIAL>__extension__ ; + +<INITIAL>__attribute__ { + BEGIN EMBEDDED; + } +<EMBEDDED>"(" ++embedded; +<EMBEDDED>")" { + if (--embedded == 0) + BEGIN INITIAL; + } +<EMBEDDED>{ID}|","|{DIGIT}+|{WS} ; +<EMBEDDED>{QUOTED} update_line_num(); + +<INITIAL>{ID} { + if (enum_state == BRACES) BEGIN SKIP; + yylval.text = strduplicate(yytext); + if (is_typedef_name(yytext)) + return T_TYPEDEF_NAME; + else + return T_IDENTIFIER; + } + +<INITIAL>"=" BEGIN INIT1; +<INIT1>"{" { curly = 1; BEGIN INIT2; } +<INIT1>[,;] { + unput(yytext[yyleng-1]); + BEGIN INITIAL; + return T_INITIALIZER; + } +<INIT1>{QUOTED} update_line_num(); +<INIT1>. ; + +<INIT2>"{" ++curly; +<INIT2>"}" { + if (--curly == 0) { + BEGIN INITIAL; + return T_INITIALIZER; + } + } +<INIT2>{QUOTED} update_line_num(); +<INIT2>. ; + +<INITIAL,SKIP>"{" { + if (enum_state == KEYWORD) + { + enum_state = BRACES; + return '{'; + } + else + { + curly = 1; + BEGIN CURLY; + body_start = TRUE; /* Look for first comment + * in the func body. + */ + safe_free(body_comment); + body_comment = NULL; + } + } +<INITIAL,SKIP>"}" { + BEGIN INITIAL; /* stop skipping */ + return '}'; + } + +<CURLY>"{" ++curly; +<CURLY>"}" { + if (--curly == 0) { + BEGIN INITIAL; + return T_BRACES; + } + } +<CURLY,SKIP>{QUOTED} update_line_num(); +<CURLY,SKIP>. body_start = FALSE; + +<INITIAL>"[" { + new_dynbuf(); add_dynbuf(yytext[0]); + square = 1; BEGIN SQUARE; + } +<SQUARE>"[" { ++square; add_dynbuf(yytext[0]); } +<SQUARE>"]" { + add_dynbuf(yytext[0]); + if (--square == 0) { + BEGIN INITIAL; + yylval.text = return_dynbuf(); + return T_BRACKETS; + } + } +<SQUARE>{QUOTED}|. { + int i; + for (i = 0; i < yyleng; ++i) + { + if (yytext[i] == '\n') ++line_num; + add_dynbuf(yytext[i]); + } + } + +<INITIAL,INIT1,INIT2,CURLY,SQUARE,LEXYACC,SKIP,EMBEDDED>^{WS}*"/*"[*=-]*{WS}+ { + comment_caller = YYSTATE; + start_comment(FALSE); + BEGIN COMMENT; } +<INITIAL,INIT1,INIT2,CURLY,SQUARE,LEXYACC,SKIP,EMBEDDED>^{WS}*"/*"[*=-]*[^/] { + yyless(yyleng-1); + comment_caller = YYSTATE; + start_comment(FALSE); + BEGIN COMMENT; } +<INITIAL,INIT1,INIT2,CURLY,SQUARE,LEXYACC,SKIP,EMBEDDED>"/*"[*=-]*{WS}+ { + comment_caller = YYSTATE; + start_comment(TRUE); + BEGIN COMMENT; } +<INITIAL,INIT1,INIT2,CURLY,SQUARE,LEXYACC,SKIP,EMBEDDED>"/*"[*=-]*[^/] { + yyless(yyleng-1); + comment_caller = YYSTATE; + start_comment(TRUE); + BEGIN COMMENT; } +<COMMLINE>^{WS}*"/"+{WS}* | +<COMMLINE>^{WS}*"/"*"*"*{WS}+ BEGIN COMMENT; +<COMMLINE>^{WS}*"/"*"*"*[^/] { yyless(yyleng-1); BEGIN COMMENT; } +<COMMLINE>. { yyless(0); BEGIN COMMENT; } +<COMMLINE>\n newline_comment(); +<COMMENT>{WS}*"*"+"/"{WS}*\n{WS}*"/*""*"*{WS}+ newline_comment(); +<COMMENT>{WS}*"*"+"/"{WS}*\n{WS}*"/*""*"*[^/] { + yyless(yyleng-1); newline_comment(); } +<COMMENT>{WS}*[*=-]*"*/"{WS}*$ { int ret = end_comment(TRUE); + BEGIN comment_caller; + if (ret) return ret; } +<COMMENT>{WS}*[*=-]*"*/" { int ret = end_comment(FALSE); + BEGIN comment_caller; + if (ret) return ret; } +<COMMENT>[^*\n \t]* | +<COMMENT>{WS}* | +<COMMENT>"*"+[^*/\n]* add_comment(yytext); +<COMMENT>{WS}*\n { newline_comment(); BEGIN COMMLINE; } + +<INITIAL,INIT1,INIT2,CURLY,SQUARE,LEXYACC,SKIP,EMBEDDED>^{WS}*"//"[/*=-]*{WS}* { + comment_caller = YYSTATE; + start_comment(FALSE); + BEGIN CPPCOMMENT; } +<INITIAL,INIT1,INIT2,CURLY,SQUARE,LEXYACC,SKIP,EMBEDDED>"//"[/*=-]*{WS}* { + comment_caller = YYSTATE; + start_comment(TRUE); + BEGIN CPPCOMMENT; } +<CPPCOMMENT>.* add_comment(yytext); +<CPPCOMMENT>\n{WS}*"//"[/*=-]*{WS}* newline_comment(); +<CPPCOMMENT>\n { int ret = end_comment(TRUE); + ++line_num; + BEGIN comment_caller; + if (ret) return ret; } + +[ \t\f]+ ; +\n ++line_num; + +. { + output_error(); + fprintf(stderr, "bad character '%c'\n", yytext[0]); + } +%% + +/* If the matched text contains any new line characters, then update the + * current line number. + */ +static void +update_line_num () +{ + const char *p = (const char *)yytext; + while (*p != '\0') { + if (*p++ == '\n') + line_num++; + } +} + +void start_comment(ateol) +boolean ateol; /* does comment start at end of an existing line? */ +{ + comment_remember = (look_at_body_start && body_start) || + ((comment_caller == INITIAL || comment_caller == SKIP) && + (inbasefile || enum_state == BRACES)); + + if (comment_remember) + { + comment_ateol = ateol; + comment_newlines = 0; + comment_started = FALSE; + new_dynbuf(); + } +} + +int end_comment(ateol) +boolean ateol; /* does comment end at end of line? */ +{ + if (comment_remember) + { + if (!ateol) comment_ateol = FALSE; + yylval.text = return_dynbuf(); + if (yylval.text[0] == '\0' || + /* ignore lint directives entirely */ + strcmp("EMPTY", yylval.text) == 0 || + strcmp("FALLTHROUGH", yylval.text) == 0 || + strcmp("FALLTHRU", yylval.text) == 0 || + strcmp("LINTED", yylval.text) == 0 || + strcmp("LINTLIBRARY", yylval.text) == 0 || + strcmp("LINTSTDLIB", yylval.text) == 0 || + strcmp("NOTDEFINED", yylval.text) == 0 || + strcmp("NOTREACHED", yylval.text) == 0 || + strcmp("NOTUSED", yylval.text) == 0 || + strncmp("ARGSUSED", yylval.text, 8) == 0 || + strncmp("PRINTFLIKE", yylval.text, 10) == 0 || + strncmp("SCANFLIKE", yylval.text, 9) == 0 || + strncmp("VARARGS", yylval.text, 7) == 0) + { + free(yylval.text); + return 0; + } + if (body_start) { /* first comment at start of func body */ + safe_free(body_comment); + body_comment = yylval.text; + body_start = FALSE; + return 0; + } +#ifdef DEBUG + fprintf(stderr,"`%s'\n", yylval.text); +#endif + return comment_ateol ? T_EOLCOMMENT : T_COMMENT; + } + return 0; +} + +/* add a newline to the comment, deferring to remove trailing ones */ +void newline_comment() +{ + ++line_num; + + if (!comment_remember || !comment_started) return; + + comment_newlines++; +} + +/* add some true text to the comment */ +void add_comment(s) +const char *s; +{ +#ifdef DEBUG + fprintf(stderr,"`%s'\n", s); +#endif + if (!comment_remember) return; + + comment_started = TRUE; + + while (comment_newlines) + { + add_dynbuf('\n'); + comment_newlines--; + } + + while(*s) + add_dynbuf(*s++); +} + +/* Scan rest of preprocessor statement. + */ +static void +get_cpp_directive () +{ + int c, lastc = '\0'; + + while ((c = input()) > 0) { + switch (c) { + case '\n': + if (lastc != '\\') { + unput(c); + BEGIN INITIAL; + return; + } + line_num++; + break; + case '*': + if (lastc == '/') + { + /* might be able to attach comments to #defines one day */ + comment_caller = YYSTATE; + start_comment(TRUE); + BEGIN COMMENT; + } + break; + case '/': + if (lastc == '/') + { + /* might be able to attach comments to #defines one day */ + comment_caller = YYSTATE; + start_comment(TRUE); + BEGIN CPPCOMMENT; + } + break; + } + lastc = c; + } +} + +/* Process include directive. + */ +static void +do_include (filename, sysinc) +char *filename; /* file name */ +int sysinc; /* 1 = do not search current directory */ +{ + char path[MAX_TEXT_LENGTH]; + int i; + FILE *fp; + IncludeStack *sp; + + if (inc_depth >= MAX_INC_DEPTH) { + output_error(); + fprintf(stderr, "includes too deeply nested\n"); + return; + } + + for (i = sysinc != 0; i < num_inc_dir; ++i) { + strcpy(path, inc_dir[i]); + strcat(path, filename); + if ((fp = fopen(path, "r")) != NULL) { + sp = inc_stack + inc_depth; + sp->file = cur_file; + sp->line_num = line_num; +#ifdef FLEX_SCANNER + sp->buffer = YY_CURRENT_BUFFER; + yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE)); +#else + sp->fp = yyin; + yyin = fp; +#endif + ++inc_depth; + cur_file = strduplicate(filename); + line_num = 0; + return; + } + } +} + +/* returns TRUE if the basefile status has changed */ +static boolean process_line_directive(new_file) +const char *new_file; +{ + boolean new_stdin; + + /* strip leading ./ that Sun acc prepends */ + if (!strncmp(new_file,"./",2)) + new_file += 2; + + new_stdin = new_file[0] == '\0' || !strcmp(new_file,"stdin"); + + /* return BASEFILE token only when file changes */ + if ((cur_file == NULL && !new_stdin) || + (cur_file != NULL &&strcmp(cur_file, new_file))) + { + safe_free(cur_file); + cur_file = new_stdin ? NULL : strduplicate(new_file); + yylval.boolean = basefile ? !strcmp(cur_file,basefile) : + cur_file == basefile; + return TRUE; + } + return FALSE; +} + +/* When the end of the current input file is reached, pop any + * nested includes. + */ +int +yywrap () +{ + IncludeStack *sp; + + if (inc_depth > 0) { + --inc_depth; + sp = inc_stack + inc_depth; + fclose(yyin); +#ifdef FLEX_SCANNER + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_switch_to_buffer(sp->buffer); +#else + yyin = sp->fp; +#endif + safe_free(cur_file); + cur_file = sp->file; + line_num = sp->line_num + 1; + return 0; + } else { + return 1; + } +} + + +static void new_dynbuf() +{ + if ((dynbuf = malloc(dynbuf_size = DYNBUF_ALLOC)) == 0) + outmem(); + + dynbuf_current = 0; +} + +static void add_dynbuf(c) +int c; +{ + if (dynbuf_current == dynbuf_size && + ((dynbuf = realloc(dynbuf,dynbuf_size += DYNBUF_ALLOC)) == 0)) + outmem(); + + dynbuf[dynbuf_current++] = c; +} + +static char *return_dynbuf() +{ + add_dynbuf('\0'); + + /* chop it back to size */ + if ((dynbuf = realloc(dynbuf,dynbuf_current)) == 0) + outmem(); + + return dynbuf; +} + +/* Output an error message along with the current line number in the + * source file. + */ +void +output_error () +{ + errors++; + fprintf(stderr, "%s:%d: ", cur_file ? cur_file : "stdin", line_num); + fprintf(stderr, "\n(%s) ", yytext); +} diff --git a/libc/COPYING b/libc/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/libc/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/libc/README.libc b/libc/README.libc new file mode 100644 index 0000000..0f0260d --- /dev/null +++ b/libc/README.libc @@ -0,0 +1,10 @@ +This directory contains sources for library routines which some users have +found to be missing on their machines. + +Since some of this code is covered by the GNU General Public License, a copy +is included in the file COPYING. Note that only those files in the c2man +distribution which explicitly state that they fall under the terms of this +license actually do so. + +The getopt routines (needed for MSC on the PC, and for VMS) are adapted from +the GNU versions. diff --git a/libc/alloca.c b/libc/alloca.c new file mode 100644 index 0000000..9e6a3a2 --- /dev/null +++ b/libc/alloca.c @@ -0,0 +1,186 @@ +/* + alloca -- (mostly) portable public-domain implementation -- D A Gwyn + + This implementation of the PWB library alloca() function, + which is used to allocate space off the run-time stack so + that it is automatically reclaimed upon procedure exit, + was inspired by discussions with J. Q. Johnson of Cornell. + + It should work under any C implementation that uses an + actual procedure stack (as opposed to a linked list of + frames). There are some preprocessor constants that can + be defined when compiling for your specific system, for + improved efficiency; however, the defaults should be okay. + + The general concept of this implementation is to keep + track of all alloca()-allocated blocks, and reclaim any + that are found to be deeper in the stack than the current + invocation. This heuristic does not reclaim storage as + soon as it becomes invalid, but it will do so eventually. + + As a special case, alloca(0) reclaims storage without + allocating any. It is a good idea to use alloca(0) in + your main control loop, etc. to force garbage collection. +*/ +#ifndef lint +static char SCCSid[] = "@(#)alloca.c 1.1"; /* for the "what" utility */ +#endif + +#ifdef emacs +#include "config.h" +#ifdef static +/* actually, only want this if static is defined as "" + -- this is for usg, in which emacs must undefine static + in order to make unexec workable + */ +#ifndef STACK_DIRECTION +you +lose +-- must know STACK_DIRECTION at compile-time +#endif /* STACK_DIRECTION undefined */ +#endif static +#endif emacs + +#ifdef X3J11 +typedef void *pointer; /* generic pointer type */ +#else +typedef char *pointer; /* generic pointer type */ +#endif + +#define NULL 0 /* null pointer constant */ + +extern void free(); +extern pointer malloc(); + +/* + Define STACK_DIRECTION if you know the direction of stack + growth for your system; otherwise it will be automatically + deduced at run-time. + + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown +*/ + +#ifndef STACK_DIRECTION +#define STACK_DIRECTION 0 /* direction unknown */ +#endif + +#if STACK_DIRECTION != 0 + +#define STACK_DIR STACK_DIRECTION /* known at compile-time */ + +#else /* STACK_DIRECTION == 0; need run-time code */ + +static int stack_dir; /* 1 or -1 once known */ +#define STACK_DIR stack_dir + +static void +find_stack_direction (/* void */) +{ + static char *addr = NULL; /* address of first + `dummy', once known */ + auto char dummy; /* to get stack address */ + + if (addr == NULL) + { /* initial entry */ + addr = &dummy; + + find_stack_direction (); /* recurse once */ + } + else /* second entry */ + if (&dummy > addr) + stack_dir = 1; /* stack grew upward */ + else + stack_dir = -1; /* stack grew downward */ +} + +#endif /* STACK_DIRECTION == 0 */ + +/* + An "alloca header" is used to: + (a) chain together all alloca()ed blocks; + (b) keep track of stack depth. + + It is very important that sizeof(header) agree with malloc() + alignment chunk size. The following default should work okay. +*/ + +#ifndef ALIGN_SIZE +#define ALIGN_SIZE sizeof(double) +#endif + +typedef union hdr +{ + char align[ALIGN_SIZE]; /* to force sizeof(header) */ + struct + { + union hdr *next; /* for chaining headers */ + char *deep; /* for stack depth measure */ + } h; +} header; + +/* + alloca( size ) returns a pointer to at least `size' bytes of + storage which will be automatically reclaimed upon exit from + the procedure that called alloca(). Originally, this space + was supposed to be taken from the current stack frame of the + caller, but that method cannot be made to work for some + implementations of C, for example under Gould's UTX/32. +*/ + +static header *last_alloca_header = NULL; /* -> last alloca header */ + +pointer +alloca (size) /* returns pointer to storage */ + unsigned size; /* # bytes to allocate */ +{ + auto char probe; /* probes stack depth: */ + register char *depth = &probe; + +#if STACK_DIRECTION == 0 + if (STACK_DIR == 0) /* unknown growth direction */ + find_stack_direction (); +#endif + + /* Reclaim garbage, defined as all alloca()ed storage that + was allocated from deeper in the stack than currently. */ + + { + register header *hp; /* traverses linked list */ + + for (hp = last_alloca_header; hp != NULL;) + if (STACK_DIR > 0 && hp->h.deep > depth + || STACK_DIR < 0 && hp->h.deep < depth) + { + register header *np = hp->h.next; + + free ((pointer) hp); /* collect garbage */ + + hp = np; /* -> next header */ + } + else + break; /* rest are not deeper */ + + last_alloca_header = hp; /* -> last valid storage */ + } + + if (size == 0) + return NULL; /* no allocation required */ + + /* Allocate combined header + user data storage. */ + + { + register pointer new = malloc (sizeof (header) + size); + /* address of header */ + + ((header *)new)->h.next = last_alloca_header; + ((header *)new)->h.deep = depth; + + last_alloca_header = (header *)new; + + /* User storage begins just after header. */ + + return (pointer)((char *)new + sizeof(header)); + } +} diff --git a/libc/getopt.c b/libc/getopt.c new file mode 100644 index 0000000..479d4cc --- /dev/null +++ b/libc/getopt.c @@ -0,0 +1,735 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* NOTE!!! AIX requires this to be the first thing in the file. + Do not put ANYTHING before it! */ +#if !defined (__GNUC__) && defined (_AIX) + #pragma alloca +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else /* not __GNUC__ */ +#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__)))) +#include <alloca.h> +#else +#ifdef _MSC_VER +#include <malloc.h> +#else +#ifndef _AIX +char *alloca (); +#endif +#endif +#endif /* alloca.h */ +#endif /* not __GNUC__ */ + +#if !__STDC__ && !defined(const) && IN_GCC +#define const +#endif + +/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#include <stdio.h> + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#undef alloca +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include <stdlib.h> +#else /* Not GNU C library. */ +#define __alloca alloca +#endif /* GNU C library. */ + +/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a + long-named option. Because this is not POSIX.2 compliant, it is + being phased out. */ +/* #define GETOPT_COMPAT */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include <string.h> +#define my_index strchr +#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n)) +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +char *getenv (); + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +static void +my_bcopy (from, to, size) + const char *from; + char *to; + int size; +{ + int i; + for (i = 0; i < size; i++) + to[i] = from[i]; +} +#endif /* GNU C library. */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (argv) + char **argv; +{ + int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); + char **temp = (char **) __alloca (nonopts_size); + + /* Interchange the two blocks of data in ARGV. */ + + my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size); + my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt], + (optind - last_nonopt) * sizeof (char *)); + my_bcopy ((char *) temp, + (char *) &argv[first_nonopt + optind - last_nonopt], + nonopts_size); + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int option_index; + + optarg = 0; + + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (getenv ("POSIXLY_CORRECT") != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == NULL || *nextchar == '\0') + { + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + optind++; + last_nonopt = optind; + } + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + if (longopts != NULL + && ((argv[optind][0] == '-' + && (argv[optind][1] == '-' || long_only)) +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + )) + { + const struct option *p; + char *s = nextchar; + int exact = 0; + int ambig = 0; + const struct option *pfound = NULL; + int indfound; + + while (*s && *s != '=') + s++; + + /* Test all options for either exact match or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; + p++, option_index++) + if (!strncmp (p->name, nextchar, s - nextchar)) + { + if (s - nextchar == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*s) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = s + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return '?'; + } + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { +#if 0 + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); +#endif + } + optopt = c; + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = 0; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { +#if 0 + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/libc/getopt.h b/libc/getopt.h new file mode 100644 index 0000000..45541f5 --- /dev/null +++ b/libc/getopt.h @@ -0,0 +1,129 @@ +/* Declarations for getopt. + Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if __STDC__ +#if defined(__GNU_LIBRARY__) +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ +extern int getopt (); +#endif /* not __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* not __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/libc/getopt1.c b/libc/getopt1.c new file mode 100644 index 0000000..a32615c --- /dev/null +++ b/libc/getopt1.c @@ -0,0 +1,176 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "getopt.h" + +#if !__STDC__ && !defined(const) && IN_GCC +#define const +#endif + +#include <stdio.h> + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include <stdlib.h> +#else +char *getenv (); +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +#include <stdio.h> + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == EOF) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/manpage.c b/manpage.c new file mode 100644 index 0000000..8f6c047 --- /dev/null +++ b/manpage.c @@ -0,0 +1,1347 @@ +/* $Id: manpage.c,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * stuff to do with manual page outputing + */ + +/* + * This file has been modified by Manoj Srivastava <srivasta@debian.org> + * to incorporate a patch that was also submitted to the author. The change + * shall be incorporated upstream in due course. + */ + +#include "c2man.h" + +#include <errno.h> +#include <ctype.h> + +#include "manpage.h" +#include "strconcat.h" +#include "strappend.h" +#include "semantic.h" +#include "output.h" + +#ifdef I_SYS_FILE +#include <sys/file.h> +#endif + +/* list of manual pages */ +ManualPage *firstpage = NULL; +ManualPage **lastpagenext = &firstpage; + +void dummy() {} + +void +new_manual_page(comment, decl_spec, declarator) + char *comment; + DeclSpec *decl_spec; + Declarator *declarator; +{ + ManualPage *newpage; + + /* check that we really want a man page for this */ + if ((!comment) || + !inbasefile || + (!variables_out && !is_function_declarator(declarator)) || + (decl_spec->flags & DS_JUNK) || + (!static_out && (decl_spec->flags & DS_STATIC) && !header_file) || + + /* only document extern stuff if it's in a header file, or includes a + * function definition. + */ + ((decl_spec->flags & DS_EXTERN) && !header_file && + declarator->type != DECL_FUNCDEF)) + { + free_decl_spec(decl_spec); + free_declarator(declarator); + safe_free(comment); + return; + } + + declarator->comment = comment; + + newpage = (ManualPage *)safe_malloc(sizeof *newpage); + newpage->decl_spec = (DeclSpec *)safe_malloc(sizeof *newpage->decl_spec); + newpage->declarator = declarator; + + *newpage->decl_spec = *decl_spec; + newpage->sourcefile = strduplicate(basefile); + newpage->sourcetime = basetime; + + *lastpagenext = newpage; + newpage->next = NULL; + lastpagenext = &newpage->next; +} + +void free_manual_page(page) + ManualPage *page; +{ + free_decl_spec(page->decl_spec); + free(page->decl_spec); + free_declarator(page->declarator); + safe_free(page->sourcefile); +} + +/* free the list of manual pages */ +void free_manual_pages(first) + ManualPage *first; +{ + ManualPage *page, *next; + + /* free any terse description read from the file */ + if (group_terse && !terse_specified) + { + free(group_terse); + group_terse = NULL; + } + + for (page = first;page;page = next) + { + next = page->next; + free_manual_page(page); + free(page); + } +} + +/* allocate a substring starting at start, ending at end (NOT including *end) */ +char *alloc_string(start, end) + const char *start; + const char *end; +{ + int len = end - start; + char *ret; + if (len == 0) return NULL; + + ret = (char *)safe_malloc((size_t)len+1); + + strncpy(ret,start,len); + ret[len] = '\0'; + + return ret; +} + +/* remember the terse description from the first comment in a file */ +void remember_terse(comment) + char *comment; +{ + char *c, *d; + + enum { STUFF, LEADSPACE, DASH, TRAILSPACE, FOUND } state = STUFF; + + /* if we've found a terse comment in a previous file, or one was + * specified on the command line, forget it. + */ + if (group_terse) return; + + /* look for a whitespace surrounded sequence of dashes to skip */ + for (c = comment;*c && state != FOUND;c++) + { + switch (state) + { + case STUFF: if (isspace(*c)) state = LEADSPACE; + break; + case LEADSPACE: if (*c == '-') state = DASH; + else if (!isspace(*c)) state = STUFF; + break; + case DASH: if (isspace(*c)) state = TRAILSPACE; + else if (*c != '-') state = STUFF; + break; + case TRAILSPACE:if (!isspace(*c)) { c--; state = FOUND; } + break; + case FOUND: break; + } + } + + /* if no dashes were found, go back to the start */ + if (state != FOUND) c = comment; + + d = c + 1; + + while (*d && *d != '\n') + d++; + + group_terse = alloc_string(c,d); +} + +/* output a comment in man page form, followed by a newline */ +void +output_comment(comment) +const char *comment; +{ + if (!comment || !comment[0]) + output->text("Not Documented."); + else if (fixup_comments) + output->description(comment); + else + output->text(comment); + + output->character('\n'); +} + +/* output the phrase "a[n] <type name>" */ +void output_conjunction(text) +char *text; +{ + output->character('a'); + if (strchr("aAeEiIoOuU",text[0])) output->character('n'); + output->character(' '); + output->code(text); +} + +/* output the description for an identifier; be it return value or param */ +static void output_identifier_description(comment, outfunc, + decl_spec, declarator) + const char *comment; /* comment for this identifier */ + void (*outfunc) _((const char *)); /* function to output comment */ + const DeclSpec *decl_spec; + const Declarator *declarator; +{ + /* one day, this may document the contents of structures too */ + + /* output list of possible enum values, if any */ + if (decl_spec->enum_list) + { + int maxtaglen = 0; + char *longestag = NULL; + int descriptions = 0; + int entries = 0; + Enumerator *e; + int is_first = 1; + boolean started = FALSE; + + /* don't output the "Not Doc." message for enums */ + if (comment) + { + (*outfunc)(comment); + output->blank_line(); + } + + /* see if any have descriptions */ + for (e = decl_spec->enum_list->first; e; e = e->next) + if (e->name[0] != '_') + { + int taglen = strlen(e->name); + if (taglen > maxtaglen) + { + maxtaglen = taglen; + longestag = e->name; + } + if (e->comment) descriptions = 1; + entries++; + } + + /* if there are a lot of them, the list may be automatically generated, + * and probably isn't wanted in every manual page. + */ + if (entries > 20) + { + char entries_s[15]; + sprintf(entries_s, "%d", entries); + output->text("Since there are "); + output->text(entries_s); + output->text(" possible values for "); + output_conjunction(decl_spec->text); + output->text(", they are not all listed here.\n"); + } + else if (entries > 0) /* skip the pathological case */ + { + /* the number of possibilities is reasonable; list them all */ + output->text("Possible values for "); + output_conjunction(decl_spec->text); + output->text(" are as follows:\n"); + + for (e = decl_spec->enum_list->first; e; e = e->next) + { + /* don't print names with a leading underscore! */ + if (e->name[0] == '_') continue; + + if (e->group_comment) + { + /* break out of table mode for the group comment */ + if (started) + { + if (descriptions) + output->table_end(); + else + output->list_end(); + started = FALSE; + } + output->indent(); + output_comment(e->group_comment); + } + + if (!started) + { + if (descriptions) + output->table_start(longestag); + else + output->list_start(); + started = TRUE; + } + + if (descriptions) + output->table_entry(e->name, e->comment); + else + { + if (!is_first) + output->list_separator(); + is_first = 0; + output->list_entry(e->name); + } + } + + if (started) + { + if (descriptions) + output->table_end(); + else + output->list_end(); + } + } + } + else + (*outfunc)(comment); +} + +/* is there automatic documentation here? */ +static boolean auto_documented(page) +const ManualPage *page; +{ + /* one day we may handle structs too */ + return + page->decl_spec->enum_list != NULL; /* enums are self-documenting. */ +} + +/* decide if a manual page needs a RETURNS section. + * If this is true, then output_identifier_description must be able to generate + * sensible output for it. + */ +static boolean needs_returns_section(page) +const ManualPage *page; +{ + return + (page->returns && page->returns[0]) || + (auto_documented(page) && is_function_declarator(page->declarator)); +} + +/* does this declarator have documented parameters? */ +boolean has_documented_parameters(d) +const Declarator *d; +{ + if (has_parameters(d)) + { + Parameter *p; + + for (p = d->head->params.first; p != NULL; p = p->next) + if (p->declarator->comment || always_document_params) + return TRUE; + } + return FALSE; +} + +/* Output the list of function parameter descriptions. + */ +void +output_parameter_descriptions (params, function) +ParameterList *params; +char *function; +{ + Parameter *p; + boolean tag_list_started = FALSE; + + for (p = params->first; p != NULL; p = p->next) + { + if (p->suppress || + (!always_document_params && p->declarator->comment == NULL)) + continue; + + if (!tag_list_started) + { + output->tag_list_start(); + tag_list_started = TRUE; + } + + if (p->duplicate) + output->tag_entry_start_extra(); + else + output->tag_entry_start(); + + output_parameter(p); + + /* include function name if it's a duplicate */ + if (p->duplicate) + output->tag_entry_end_extra(function); + else + output->tag_entry_end(); + + output_identifier_description(p->declarator->comment, output_comment, + &p->decl_spec, p->declarator); + } + + if (tag_list_started) + output->tag_list_end(); +} + +/* split out the 'Returns:' section of a function comment */ +boolean +split_returns_comment(comment, description, returns) + char *comment; + char **description; + char **returns; +{ + char *retstart; + + for (retstart = comment; + retstart; + retstart = strchr(retstart,'\n')) + { + if (*retstart == '\n') retstart++; /* skip the newline */ + + if (!strncmpi(retstart, "returns",(size_t)7)) + { + 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 = + descend > comment ? alloc_string(comment,descend+1) : NULL; + + retstart += 7; + + while (*retstart == ':' || isspace(*retstart)) + retstart++; + + if (*retstart) + *returns = strduplicate(retstart); + else + *returns = NULL; + return TRUE; + } + } + + *description = comment; + *returns = NULL; + return FALSE; +} + +/* skip to past the dash on the first line, if there is one + * The dash must be surrounded by whitespace, so hyphens are not skipped. + */ +const char *skipdash(c) +const char *c; +{ + const char *d; + + /* ignore anything on the first line, up to a dash (if any) */ + for (d = c + 1; *d && *d != '\n' && *d != '-'; d++) + ; + + if (isspace(d[-1]) && d[0] == '-' && isspace(d[1])) + { + do + d++; + while (*d && *d != '\n' && isspace(*d)); + + if (*d && *d != '\n') c = d; + } + return c; +} + +/* split the function comment into manual page format. + * returns TRUE if the DESCRIPTION field was explicit. + */ +boolean +split_function_comment(comment, identifier_name, + terse, description, returns, extra_sections) + const char *comment; + const char *identifier_name; + char **terse; + char **description; + char **returns; + Section **extra_sections; +{ + const char *c, *start_text = NULL, *end_text = NULL; + char **put_ptr = NULL; + Section *first_section, **lastnextsection = &first_section; + boolean explicit_description = FALSE; + boolean lastblank = TRUE; + boolean skip_dash = FALSE; + + *description = *returns = NULL; + if (terse) *terse = NULL; + + /* for each line... */ + for (c = comment; *c;) + { + const char *start_line = c; + boolean section_heading; + /* remember if it's a blank line */ + if (*c == '\n') + { + lastblank = TRUE; + c++; + continue; + } + + /* if the last one was blank, perhaps this one is a section heading + */ + if (lastblank) + { + boolean need_colon = FALSE; + + /* see if we've found the start of a SECTION */ + while (isalnum(*c) || *c == ' ' || *c == '/') + { + if (isspace(*c)) need_colon = TRUE; + c++; + } + + section_heading = (!need_colon && *c == '\n') || + (*c == ':' && (!need_colon || *(c+1) == '\n')) || + (!need_colon && *c == '\0' && start_line == comment); + } + else + section_heading = FALSE; + + lastblank = FALSE; /* this one's not blank; for next time */ + + if (section_heading) + { + size_t section_len = c - start_line; /* length of section name */ + + /* yes, we've found a SECTION; store the previous one (if any) */ + if (put_ptr && start_text) + { + if (skip_dash) start_text = skipdash(start_text); + *put_ptr = alloc_string(start_text,end_text); + } + + skip_dash = FALSE; + + /* check for comments that start with the name of the identifier */ + if (start_line == comment && + !strncmp(start_line, identifier_name, section_len)) + { + put_ptr = description; + } + + /* only accept NAME if not grouped */ + else if (terse && + (!strncmpi(start_line,"NAME", section_len) || + !strncmpi(start_line,"FUNCTION", section_len) || + !strncmpi(start_line,"PROCEDURE", section_len) || + !strncmpi(start_line,"ROUTINE", section_len)) + ) + + { + put_ptr = terse; + skip_dash = TRUE; + } + else if (!strncmpi(start_line,"DESCRIPTION", section_len)) + { + explicit_description = TRUE; + put_ptr = description; + } + else if (!strncmpi(start_line,"RETURNS", section_len)) + { + put_ptr = returns; + } + else + { + /* allocate a new section */ + Section *new_section = + (Section *)safe_malloc(sizeof *new_section); + + *lastnextsection = new_section; + lastnextsection = &new_section->next; + + new_section->name = alloc_string(start_line,c); + strtoupper(new_section->name); + new_section->text = NULL; + new_section->been_output = FALSE; /* not been output yet */ + put_ptr = &new_section->text; + } + + /* defer decision about where text starts till we find some */ + start_text = NULL; + + if (*c == ':') /* skip the terminating : */ + { + c++; + + /* skip forward to the start of the text */ + while (*c && *c != '\n' && isspace(*c)) + c++; + + /* if we find the text here, then we've got it */ + if (*c && *c != '\n') + start_text = c; + } + } + else + { + /* are we looking at the top of the function comment? */ + if (start_line == comment) + { + /* only look for terse comment if not grouped together */ + if (terse) + { + const char *endterse, *afterdash = skipdash(start_line); + + /* find the end of the terse comment */ + while (*c && *c != '\n') + { + c++; + /* '.' ends terse description only if it ends sentence */ + if (*(c-1)=='.' && *c && isspace(*c)) + break; + } + + endterse = c; + *terse = alloc_string( + afterdash < endterse ? afterdash : start_line, + endterse); + + /* skip it if it's a ., and any trailing spaces */ + if (*c == '.') + do c++; while (*c && *c != '\n' && isspace(*c)); + + start_text = NULL; /* look for it */ + + if (*c && *c != '\n') + /* actually, it's a description, starting here */ + start_text = c; + } + /* must be a description starting at the beginning of the line. + */ + else + start_text = start_line; + + put_ptr = description; + } + else + /* have we just located the first real text in a section? */ + if (put_ptr && !start_text) start_text = start_line; + } + + /* skip the line */ + if (*c && *c != '\n') + while (*c && *c != '\n') c++; + + end_text = c; /* so far, the text ends at the end of this line */ + if (*c) c++; + } + + /* store the last one */ + if (put_ptr && start_text) + { + if (skip_dash) start_text = skipdash(start_text); + *put_ptr = alloc_string(start_text,end_text); + } + + /* terminate (or nuke) section list */ + *lastnextsection = NULL; + + *extra_sections = first_section; + + return explicit_description; +} + +/* see if two parameters are declared identically */ +boolean params_identical(first, second) + Parameter *first; + Parameter *second; +{ + return + first->decl_spec.flags == second->decl_spec.flags && + + /* there may be no decl_spec.text if it's an ellipsis arg */ + ((!first->decl_spec.text && !second->decl_spec.text) || + (first->decl_spec.text && second->decl_spec.text && + !strcmp(first->decl_spec.text, second->decl_spec.text))) && + + ((!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(firstpage) + 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->head->params.first; param; + param = param->next) + { + ManualPage *otherpage; + Parameter *otherparam; + + if (always_document_params || param->declarator->comment) + any = TRUE; + + for (otherpage = page->next; otherpage; + otherpage = otherpage->next) + { + if (has_parameters(otherpage->declarator)) + for (otherparam = otherpage->declarator->head->params.first; + otherparam; + otherparam = otherparam->next) + { + /* do these two look the same? */ + if (params_identical(param, otherparam)) + { + /* order is important for bit positions */ + 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; +} + +/* output a formatting string so that it works with filling on */ +void output_format_string(fmt) +const char *fmt; +{ + while (*fmt) + { + output->character(*fmt); + + if (*fmt++ == '\n') + output->break_line(); /* break the line */ + } +} + +/* write the warning for the header */ +void output_warning() +{ + output->comment(); + output->text("WARNING! THIS FILE WAS GENERATED AUTOMATICALLY BY "); + output->text(progname); + output->text("!\n"); + output->comment(); + output->text("DO NOT EDIT! CHANGES MADE TO THIS FILE WILL BE LOST!\n"); +} + +void output_includes() +{ + IncludeFile *incfile; + + for (incfile = first_include; incfile; incfile=incfile->next) + { + char *name = incfile->name; + boolean surrounded = *name == '"' || *name == '<'; + + output->text("#include "); + if (!surrounded) output->character('<'); + output->text(name); + if (!surrounded) output->character('>'); + output->text("\n"); + output->break_line(); + } +} + +int exclude_section(section) +const char *section; +{ + ExcludeSection *exclude; + + for (exclude = first_excluded_section ; exclude ; exclude = exclude->next) + if (!strcmp(section, exclude->name)) return 1; + + return 0; +} + + +/* Writes the entire contents of the manual page specified by basepage. */ +void +output_manpage(firstpage, basepage, input_files, title, section) + /* the first page in the list of all manual pages. This is used to build + * the SEE ALSO section of related pages when group_together is false. + */ + ManualPage *firstpage; + + /* the base page from which the output manual page will be generated. if + * group_together indicates that the user wanted grouped pages, basepage + * will always be the same as firstpage, and all the ManualPage's in the + * list will be grouped together into the one output page. + */ + ManualPage *basepage; + + int input_files; + const char *title; + const char *section; +{ + ManualPage *page; + boolean need_returns; + char *terseout, *terse = NULL; + boolean exclude_description = exclude_section("DESCRIPTION"); + + /* check if there's more than one page in the group */ + boolean grouped = group_together && firstpage->next; + + /* split up all the function comments for this page */ + for (page = basepage; page; page = page->next) + { + boolean explicit_description = + split_function_comment(page->declarator->comment, + page->declarator->name, + group_together ? (char **)NULL : &terse, + &page->description,&page->returns,&page->first_section); + + /* we may need to look harder if RETURNS wasn't easy to find in the + * function comment. + */ + if (page->returns == NULL) + { + /* if there was a retcomment supplied by the declarator, use it if + * we couldn't split anything from the function comment. + */ + if (page->declarator->retcomment) + { + page->returns = page->declarator->retcomment; + + /* page->returns now owns the string */ + page->declarator->retcomment = NULL; + } + else + /* if there wasn't a RETURNS section, and the DESCRIPTION field + * was not explicit, see if we can split one out of the + * description field. + */ + if (!explicit_description) + { + char *newdesc; + if (split_returns_comment(page->description, &newdesc, + &page->returns)) + { + free(page->description); + page->description = newdesc; + } + } + } + + if (!group_together) break; + } + + /* work out what we'll actually print as a terse description */ + terseout = group_terse ? group_terse : (terse ? terse : "Not Described"); + + output->header(basepage, input_files, grouped, + title ? title : basepage->declarator->name, terseout, section); + + output->name(NULL); + /* output the names of all the stuff documented on this page */ + for (page = basepage; page; page = page->next) + { + output->name(page->declarator->name); + + if (!group_together) break; + + if (page->next) output->text(",\n"); + } + + output->terse_sep(); + output->text(terseout); + output->character('\n'); + + if (!exclude_section("SYNOPSIS")) + { + output->section("SYNOPSIS"); + + output->code_start(); + + /* list the include files the user asked us to */ + output_includes(); + + /* if it's a header file, say to #include it */ + if (header_file) + { + output->text("#include <"); + if (header_prefix) + { + output->text(header_prefix); + output->character('/'); + } + output->text(basefile); + output->text(">\n"); + } + + /* can't just use .PP; that may reset our font */ + if (first_include || header_file) output->blank_line(); + + for (page = basepage; page; page = page->next) + { + output_format_string(decl_spec_prefix); + + /* make sure variables are prefixed extern */ + if (!(page->decl_spec->flags & DS_STATIC) && + !is_function_declarator(page->declarator) && + !strstr(page->decl_spec->text, "extern")) + output->text("extern "); + + output_decl_spec(page->decl_spec); + output_format_string(declarator_prefix); + + /* format it nicely if there's more than one parameter */ + output_declarator(page->declarator, + page->declarator->head->params.first != + page->declarator->head->params.last); + + output->text(";\n"); + + if (!grouped) break; + if (page->next) output->blank_line(); + } + + output->code_end(); + } + + /* only output paramaters if there actually are some, + * not including merely (void) + */ + if (!exclude_section("PARAMETERS") && + ((grouped && mark_duplicate_parameters(basepage)) || + (!grouped && has_documented_parameters(basepage->declarator)))) + { + output->section("PARAMETERS"); + + for (page = basepage; page; page = page->next) + { + if (has_parameters(page->declarator)) + output_parameter_descriptions(&page->declarator->head->params, + page->declarator->name); + if (!grouped) break; /* only do first page */ + } + } + + if (!exclude_description) + output->section("DESCRIPTION"); + + if (grouped) + { + need_returns = FALSE; + for (page = basepage; page; page = page->next) + { + if (needs_returns_section(page)) need_returns = TRUE; + + if (!exclude_description) + { + /* enum variables are documented in DESCRIPTION */ + if (auto_documented(page) && + !is_function_declarator(page->declarator)) + { + output->sub_section(page->declarator->name); + output_identifier_description(page->description, + output_comment, page->decl_spec, page->declarator); + } + else if (page->description) + { + output->sub_section(page->declarator->name); + output_comment(page->description); + } + } + + safe_free(page->description); + } + } + else + { + need_returns = needs_returns_section(basepage); + + if (!exclude_description) + { + const char *descr = basepage->description + ? basepage->description : terseout; + + if (auto_documented(page) && + !is_function_declarator(page->declarator)) + output_identifier_description(descr, output_comment, + page->decl_spec, page->declarator); + else + output_comment(descr); + } + + safe_free(basepage->description); + } + + /* terse can now never be a static string */ + safe_free(terse); + + if (need_returns && !exclude_section("RETURNS")) + { + output->section("RETURNS"); + + for (page = basepage; page; page = page->next) + { + if (needs_returns_section(page)) + { + if (grouped) output->sub_section(page->declarator->name); + + output_identifier_description(page->returns, output->returns, + page->decl_spec, page->declarator); + safe_free(page->returns); + } + + if (!grouped) break; + } + } + + /* output any other sections */ + for (page = basepage; page; page = page->next) + { + Section *section, *next; + + for (section = page->first_section; section; section = next) + { + next = section->next; + + if (!section->been_output && section->text && + strncmpi(section->text,"none",4) && + !exclude_section(section->name)) + { + output->section(section->name); + if (grouped) output->sub_section(page->declarator->name); + output_comment(section->text); + section->been_output = TRUE; + + if (grouped && page->next) + { + ManualPage *other_page = page->next; + + /* look through all the other pages for matching sections */ + for (; other_page; other_page = other_page->next) + { + Section *other_section = other_page->first_section; + for (;other_section; other_section = + other_section->next) + { + if (other_section->been_output || + strcmp(other_section->name, section->name)) + continue; + + output->sub_section(other_page->declarator->name); + output_comment(other_section->text); + other_section->been_output = TRUE; + } + } + } + } + + + /* free this section */ + free(section->name); + safe_free(section->text); + free(section); + } + + if (!grouped) break; + } + + /* only output SEE ALSO if not grouped */ + if (!group_together) + { + ManualPage *also; + + /* add the SEE ALSO section */ + /* look for any other functions to refer to */ + for (also = firstpage; also && also == basepage; also = also->next) + ; + + if (also && !exclude_section("SEE ALSO")) /* did we find at least one? */ + { + int isfirst = 1; + + output->section("SEE ALSO"); + + for (also = firstpage; also; also = also->next) + { + if (also == basepage) continue; + + if (!isfirst) + output->text(",\n"); + else + isfirst = 0; + + output->reference(also->declarator->name); + } + + output->character('\n'); + } + } + + if (!make_embeddable) + output->file_end(); +} + + +/* generate output filename based on a string */ +char *page_file_name(based_on, object_type, extension) + /* string to base the name on; this will be the name of an identifier or + * the base of the input file name. + */ + const char *based_on; + enum Output_Object object_type; /* class of object documented */ + const char *extension; /* file extension to use */ +{ + char *filename; + const char *subdir = output_object[object_type].subdir; + +#ifndef FLEXFILENAMES + char *basename; + int chopoff = 14 - strlen(extension) - 1; + + basename = strduplicate(based_on); + if (strlen(basename) > chopoff) + basename[chopoff] = '\0'; +#else + const char *basename = based_on; +#endif + + filename = strduplicate(output_dir); + + if (subdir) + { + if (filename) filename = strappend(filename, "/", NULLCP); + filename = strappend(filename, subdir, NULLCP); + } + + if (filename) filename = strappend(filename, "/", NULLCP); + filename = strappend(filename, basename,".",extension, NULLCP); + +#ifndef FLEXFILENAMES + free(basename); +#endif + return filename; +} + +/* determine the output page type from a declaration */ +enum Output_Object page_output_type(decl_spec, declarator) +const DeclSpec *decl_spec; +const Declarator *declarator; +{ + boolean is_static = decl_spec->flags & DS_STATIC; + return is_function_declarator(declarator) + ? (is_static ? OBJECT_STATIC_FUNCTION : OBJECT_FUNCTION) + : (is_static ? OBJECT_STATIC_VARIABLE : OBJECT_VARIABLE); +} + +/* determine the extension/section from an output type */ +const char *page_manual_section(output_type) +enum Output_Object output_type; +{ + return output_object[output_type].extension ? + output_object[output_type].extension : manual_section; +} + +/* remove an existing file, if it exists & we have write permission to it */ +int remove_old_file(name) +const char *name; +{ +#ifdef HAS_ACCESS + /* check that we have write premission before blasting it */ + if (access(name,W_OK) == -1) + { + if (errno != ENOENT) + { + my_perror("can't access output file", name); + return FALSE; + } + } + else +#endif + { + /* if it exists, blast it */ + if (unlink(name) == -1 && errno != ENOENT) + { + my_perror("error unlinking old link file", name); + return FALSE; + } + } + return TRUE; +} + +/* output all the manual pages in a list */ +void output_manual_pages(first, input_files, link_type) + ManualPage *first; + int input_files; /* number of different input files */ + enum LinkType link_type; /* how grouped pages will be linked */ +{ + ManualPage *page; + int tostdout = output_dir && !strcmp(output_dir,"-"); + + char *filename = NULL; + + /* output each page, in turn */ + for (page = first; page; page = page->next) + { + char *input_file_base = NULL; + enum Output_Object output_type = + page_output_type(page->decl_spec, page->declarator); + + /* the manual name is used as the output file extension, and also in + * the nroff output header. + */ + const char *section = page_manual_section(output_type); + + /* work out the base name of the file this was generated from */ + if (page->sourcefile) + { + const char *base = strrchr(firstpage->sourcefile, '/'); + const char *last; + + /* use the file name as the manual page title */ + if (base == NULL) + base = firstpage->sourcefile; + else + base++; + last = strrchr(base, '.'); + if (last == NULL) + last = base + strlen(base); + + input_file_base = alloc_string(base, last); + } + + if (!tostdout) + { + safe_free(filename); /* free previous, if any */ + filename = page_file_name( + use_input_name && input_file_base + ? input_file_base : page->declarator->name, + output_type, section); + fprintf(stderr,"generating: %s\n",filename); + + /* a previous run may have left links, so nuke old file first */ + if (!remove_old_file(filename)) exit(1); + + if (freopen(filename, "w", stdout) == NULL) + { + my_perror("error opening output file", filename); + free(filename); + exit(1); + } + } + + /* do the page itself */ + output_manpage(first, page, input_files, + group_together && input_file_base ? input_file_base + : page->declarator->name, + group_together ? manual_section : section); + + safe_free(input_file_base); + + /* don't continue if grouped, because all info went into this page */ + if (group_together) break; + + if (tostdout && page->next) output->character('\f'); + } + + /* close the last output file if there was one */ + if (!tostdout && fclose(stdout) == EOF) + { + my_perror("error linking closing file", filename); + exit(1); + } + + /* if pages are grouped, just link the rest to the first */ + if (group_together && !tostdout && link_type != LINK_NONE) + { + for (page=use_input_name && first->sourcefile ? first : first->next; + page; page = page->next) + { + enum Output_Object output_type = + page_output_type(page->decl_spec, page->declarator); + const char *extension = page_manual_section(output_type); + char *linkname = page_file_name(page->declarator->name, + output_type, extension); + int result = 0; + + /* we may have a function with the same name as the sourcefile */ + if (!strcmp(filename, linkname)) + { + free(linkname); + continue; + } + + fprintf(stderr,"%s: %s\n", + link_type == LINK_REMOVE ? "removing" : "linking", linkname); + + /* always nuke old output file, since it may be linked to the one + * we've just generated, so LINK_FILE may trash it. + */ + if (!remove_old_file(linkname)) exit(1); + + switch(link_type) + { +#ifdef HAS_LINK + case LINK_HARD: + result = link(filename, linkname); + break; +#endif +#ifdef HAS_SYMLINK + case LINK_SOFT: + result = symlink(filename, linkname); + break; +#endif + case LINK_FILE: + if (freopen(linkname, "w", stdout) == NULL) + { + result = -1; + break; + } + output_warning(); + output->include(filename); + if (fclose(stdout) == EOF) + result = -1; + break; + case LINK_NONE: + case LINK_REMOVE: + break; + } + + /* check it went OK */ + if (result == -1) + { + my_perror("error linking output file", linkname); + exit(1); + } + free(linkname); + } + } + + safe_free(filename); +} diff --git a/manpage.h b/manpage.h new file mode 100644 index 0000000..8fdcb09 --- /dev/null +++ b/manpage.h @@ -0,0 +1,64 @@ +/* $Id: manpage.h,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * stuff to do with manual page outputing + */ +#ifndef MANPAGE_H +#define MANPAGE_H + +#include "c2man.h" + +typedef struct Section Section; +struct Section +{ + Section *next; + char *name; + char *text; + boolean been_output; +}; + +typedef struct ManualPage ManualPage; +struct ManualPage +{ + DeclSpec *decl_spec; + Declarator *declarator; + ManualPage *next; + Section *first_section; + char *description; + char *returns; + char *sourcefile; + Time_t sourcetime; +}; + +enum LinkType +{ +#ifdef HAS_LINK + LINK_HARD, /* filesystem hard link */ +#endif +#ifdef HAS_SYMLINK + LINK_SOFT, /* filesystem soft link */ +#endif + LINK_FILE, /* nroff file with .so directive */ + LINK_NONE, /* don't create extra links for it */ + LINK_REMOVE /* don't create extra links & remove existing ones */ +}; + +/* list of manual pages */ +extern ManualPage *firstpage; + +void +new_manual_page _((char *comment, DeclSpec *decl_spec, Declarator *declarator)); + +/* remember the terse description from the first comment in a file */ +void remember_terse _((char *comment)); + +void output_manual_pages _((ManualPage *first, int num_input_files, + enum LinkType link_type)); + +void free_manual_pages _((ManualPage *first)); + +void output_format_string _((const char *fmt)); + +void output_warning _((void)); + +void output_comment _((const char *comment)); + +#endif @@ -0,0 +1,424 @@ +/* $Id: nroff.c,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * functions for nroff style output. + */ +#include "c2man.h" +#include "manpage.h" +#include "output.h" +#include "semantic.h" +#include <ctype.h> + +void nroff_text(text) +const char *text; +{ + put_string(text); +} + +void nroff_char(c) +const int c; +{ + putchar(c); +} + +void nroff_comment() { put_string(".\\\" "); } + +void nroff_header(firstpage, input_files, grouped, name, terse, section) +ManualPage *firstpage; +int input_files; +boolean grouped; +const char *name; +const char *terse; +const char *section; +{ +#ifdef HAS_STRFTIME + char month[20]; +#else + char *month; + static char *month_list[] = + { "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" }; +#endif + Time_t raw_time; + struct tm *filetime; + + if (make_embeddable) return; + + output_warning(); + put_string(".TH \""); + + /* if lots of files contributed, use the current time; otherwise use the + * time of the source file they came from. + */ + raw_time = (grouped && input_files > 1) ? time((Time_t *)NULL) + : firstpage->sourcetime; + + filetime = localtime(&raw_time); + +#ifdef HAS_STRFTIME + /* generate the date format string */ + strftime(month, sizeof month,"%B",filetime); +#else + month = month_list[filetime->tm_mon]; +#endif + + nroff_text(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 HENRYS_TH /* As per Henry Spencer's "How to write a manual page" */ + if (manual_name) printf(" \"%s\"", manual_name); + put_string("\n.BY"); +#endif + printf(" \"%s",progname); + if ((input_files <= 1 || !grouped) && firstpage->sourcefile) + { + const char *basename = strrchr(firstpage->sourcefile, '/'); + if (basename == NULL) + basename = firstpage->sourcefile; + else + basename++; + printf(" %s", basename); + } +#ifndef HENRYS_TH + if (manual_name) printf("\" \"%s", manual_name); +#endif + put_string("\"\n"); + +#ifdef NeXT + /* define our own .SS on packages (such as NeXT's) where it doesn't move + * left a little. Sorry, awf doesn't support .SS. + */ + put_string(".de SS\n.}X .25i \"\" \"\"\n.nr )E 2\n"); + put_string("\\&\\\\$1\n.br\n..\n"); +#endif +} + +void nroff_dash() { put_string("\\-"); } + +void nroff_section(name) +const char *name; +{ + put_string(".SH \""); + nroff_text(name); + put_string("\"\n"); +} + +void nroff_sub_section(name) +const char *name; +{ + put_string(".SS \""); + nroff_text(name); + put_string("\"\n"); +} + +void nroff_break_line() { put_string(".br\n"); } +void nroff_blank_line() { put_string(".sp\n"); } + +void nroff_code_start() { put_string(".ft B\n"); } +void nroff_code_end() { put_string(".ft R\n"); } + +void nroff_code(text) +const char *text; +{ + put_string("\\fB"); + nroff_text(text); + put_string("\\fR"); +} + +void nroff_tag_entry_start() { put_string(".TP\n.B \""); } +void nroff_tag_entry_start_extra() { put_string(".TP\n.BR \""); } +void nroff_tag_entry_end() { put_string("\"\n"); } +void nroff_tag_entry_end_extra(text) +const char *text; +{ + put_string("\" \"\t("); + nroff_text(text); + put_string(")\"\n"); +} + +void nroff_table_start(longestag) +const char *longestag; +{ + void nroff_list_start(); + nroff_list_start(); + + /* We measure the length of the longest tag in the table by changing to the + * code font, taking it's width with \w'string' and adding a little for + * the space between the tag and description. This gets stored in the TL + * number register, where the nroff_table_entry can find it. + * This isn't foolproof, because a shorter tag may be longer if it contains + * wider characters, but the extra space gives a little head room anyway. + */ + nroff_code_start(); + printf(".nr TL \\w'%s'u+0.2i\n", longestag); + nroff_code_end(); +} + +void nroff_table_entry(name, description) +const char *name; +const char *description; +{ + put_string(".TP \\n(TLu\n"); + + nroff_code(name); + nroff_char('\n'); + if (description) + output_comment(description); + else + nroff_char('\n'); +} + +void nroff_table_end() { put_string(".RE\n.PD\n"); } + +void nroff_indent() { put_string(".IP\n"); } + +void nroff_list_start() { put_string(".RS 0.75in\n.PD 0\n"); } + +void nroff_list_entry(name) +const char *name; +{ + nroff_code(name); +} + +void nroff_list_separator() { put_string(",\n"); } +void nroff_list_end() { nroff_char('\n'); nroff_table_end(); } + +void nroff_include(filename) +const char *filename; +{ + printf(".so %s\n", filename); +} + +void nroff_name(name) +const char *name; +{ + if (name) nroff_text(name); + else nroff_section("NAME"); +} + +void nroff_terse_sep() +{ + nroff_char(' '); + nroff_dash(); + nroff_char(' '); +} + +void nroff_emphasized(text) +const char *text; +{ + put_string("\\fI"); + nroff_text(text); + put_string("\\fR"); +} + +void nroff_reference(text) +const char *text; +{ + nroff_text(text); + nroff_char('('); + nroff_text(manual_section); + nroff_char(')'); +} + +void nroff_description(text) +const char *text; +{ + enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE; + boolean new_line = TRUE; + boolean dot_command = FALSE; + + /* correct punctuation a bit as it goes out */ + for (;*text;text++) + { + int c = *text; + + if (dot_command) + { + if (c == '\n') dot_command = FALSE; + } + else if (new_line && c == '.') + dot_command = TRUE; + else if (new_line && (c == '-' || c == '*' || is_numbered(text))) + { + output->break_line(); + state = CAPITALISE; + } + else if (c == '.') + state = PERIOD; + else if (isspace(c) && state == PERIOD) + state = CAPITALISE; + else if (isalnum(c)) + { + if (islower(c) && state == CAPITALISE) c = toupper(c); + state = TEXT; + } + + output->character(c); + new_line = c == '\n'; + } + + /* do a full stop if there wasn't one */ + if (!dot_command && state == TEXT) output->character('.'); +} + +void +nroff_returns(comment) +const char *comment; +{ + enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE; + char lastchar = '\n'; + boolean tag_list_started = FALSE; + + /* for each line... */ + while (*comment) + { + boolean tagged = FALSE; + + /* explicitly reject dot commands */ + if (*comment && *comment != '.') + { + const char *c = comment; + + /* search along until the end of a word */ + while (*c && *c != ':' && !isspace(*c)) + c++; + + /* skip all spaces or tabs after the first word */ + while (*c && *c != '\n') + { + if (*c == '\t' || *c == ':') + { + tagged = TRUE; + break; + } + else if (!isspace(*c)) + break; + + c++; + } + } + + /* is it tagged?; explicitly reject dot commands */ + if (tagged) + { + /* output lingering newline if necessary */ + if (lastchar != '\n') + { + if (state == TEXT && !ispunct(lastchar)) output->character('.'); + output->character(lastchar = '\n'); + } + + if (!tag_list_started) + { + output->tag_list_start(); + tag_list_started = TRUE; + } + + /* output the taggy bit */ + output->tag_entry_start(); + while (*comment && *comment != ':' && !isspace(*comment)) + output->character(*comment++); + output->tag_entry_end(); + + /* skip any extra tabs or spaces */ + while (*comment == ':' || (isspace(*comment) && *comment != '\n')) + comment++; + + state = CAPITALISE; + } + + /* terminate the previous line if necessary */ + if (lastchar != '\n') output->character(lastchar = '\n'); + + /* dot commands go out unaltered */ + if (*comment == '.') + { + for (;*comment && *comment != '\n'; comment++) + output->character(*comment); + output->character('\n'); + } + else + { + /* correct punctuation a bit as the line goes out */ + for (;*comment && *comment != '\n'; comment++) + { + char c = *comment; + + if (c == '.') + state = PERIOD; + else if (isspace(c) && state == PERIOD) + state = CAPITALISE; + else if (isalnum(c)) + { + if (islower(c) && state == CAPITALISE && fixup_comments) + c = toupper(c); + state = TEXT; + } + + output->character(lastchar = c); + } + + /* if it ended in punctuation, just output the nl straight away. */ + if (ispunct(lastchar)) + { + if (lastchar == '.') state = CAPITALISE; + output->character(lastchar = '\n'); + } + } + + if (*comment) comment++; + } + + /* output lingering newline if necessary */ + if (lastchar != '\n') + { + if (state == TEXT && !ispunct(lastchar) && fixup_comments) + output->character('.'); + output->character('\n'); + } + + if (tag_list_started) + output->tag_list_end(); +} + + +struct Output nroff_output = +{ + nroff_comment, + nroff_header, + nroff_dash, + nroff_section, + nroff_sub_section, + nroff_break_line, + nroff_blank_line, + nroff_code_start, + nroff_code_end, + nroff_code, + dummy, /* nroff_tag_list_start */ + dummy, /* nroff_tag_list_end */ + nroff_tag_entry_start, + nroff_tag_entry_start_extra, + nroff_tag_entry_end, + nroff_tag_entry_end_extra, + nroff_table_start, + nroff_table_entry, + nroff_table_end, + nroff_indent, + nroff_list_start, + nroff_code, /* nroff_list_entry */ + nroff_list_separator, + nroff_list_end, + nroff_include, + dummy, /* nroff_file_end */ + nroff_text, + nroff_char, + NULL, /* nroff_parse_option */ + dummy, /* nroff_print_options */ + nroff_name, + nroff_terse_sep, + nroff_reference, + nroff_emphasized, + nroff_description, + nroff_returns +}; diff --git a/output.h b/output.h new file mode 100644 index 0000000..a968401 --- /dev/null +++ b/output.h @@ -0,0 +1,149 @@ +/* $Id: output.h,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * format-independant output interface. + */ +#ifndef OUTPUT_H +#define OUTPUT_H + +/* To add a new output format: + * 1. Add the new -Tx suboption to the manual page. + * 2. Add handling for the new suboption to c2man.c, including the default + * output file extension. + * 3. Copy nroff.c to xxx.c and change the xxx_... output functions and the + * pointers in the xxx_output structure so the new xxx_output object + * generates the correct output constructs. Try to do this without modifying + * manpage.c if possible; add new output functions only if necessary. + * 4. Add the new xxx_output structure to the declaration of output structures + * at the end of this file. + */ + +/* Output object defines what type of output is being generated. + * This contains pointers to functions that generate each type of output + * construct. + */ +struct Output +{ + /* comment until the end of the line */ + void (*comment) _((void)); + + /* header and introduction to the file */ + void (*header) _((ManualPage *firstpage, int input_files, boolean grouped, + const char *name, const char *terse, const char *section)); + + /* a dash */ + void (*dash) _((void)); + + /* start of a main section */ + void (*section) _((const char *name)); + + /* start of a sub section */ + void (*sub_section) _((const char * name)); + + /* break the current line here */ + void (*break_line) _((void)); + + /* a blank line */ + void (*blank_line) _((void)); + + /* switch into the mode to include declarations like in program code */ + void (*code_start) _((void)); + + /* switch back from code mode to normal */ + void (*code_end) _((void)); + + /* output a single string in code font */ + void (*code) _((const char *text)); + + /* start a list of tagged paragraphs */ + void (*tag_list_start) _((void)); + + /* end a list of tagged paragraph */ + void (*tag_list_end) _((void)); + + /* start a tagged paragraph: the tag should go straight after this */ + void (*tag_entry_start) _((void)); + + /* start a tagged paragraph that has an extra non-code bit at the end + * of the tag: the tag should go straight after this + */ + void (*tag_entry_start_extra) _((void)); + + /* end the tag on a tagged paragraph */ + void (*tag_entry_end) _((void)); + + /* end the tag on a tagged paragraph with an extra non-code bit at the end + * of the tag. + */ + void (*tag_entry_end_extra) _((const char *text)); + + /* start a name/value pair table */ + void (*table_start) _((const char *longestag)); + + /* an entry in the name/value pair table */ + void (*table_entry) _((const char *name, const char * description)); + + /* end the name/value pair table */ + void (*table_end) _((void)); + + /* an indented paragraph */ + void (*indent) _((void)); + + /* start a list */ + void (*list_start) _((void)); + + /* an entry in the list */ + void (*list_entry) _((const char *name)); + + /* the seperator between one entry in a list and the next */ + void (*list_separator) _((void)); + + /* end the list */ + void (*list_end) _((void)); + + /* include another file in the output */ + void (*include) _((const char *filename)); + + /* end the file */ + void (*file_end) _((void)); + + /* output string, quoted to protect against formatter controls */ + void (*text) _((const char *text)); + + /* output char, quoted to protect against formatter controls */ + void (*character) _((const int c)); + + /* parse formatter specific option. set to NULL if not available */ + int (*parse_option) _((const char *option)); + + /* print formatter specific options to stderr. */ + void (*print_options) _((void)); + + /* output NAME section header and section names */ + void (*name) _((const char *name)); + + /* output separators between section name and terse description */ + void (*terse_sep) _((void)); + + /* output string, making it a hypertext reference */ + void (*reference) _((const char *text)); + + /* output string, displaying it emphasized (usually italic) */ + void (*emphasized) _((const char *text)); + + /* output description, fixing punctuation but leaving formatter commands */ + void (*description) _((const char *text)); + + /* output returns text, fixing punct. but leaving formatter commands */ + void (*returns) _((const char *text)); +}; + +/* pointer to the relevant output structure */ +extern struct Output *output; + +/* output structures for all formats we support */ +extern struct Output nroff_output, texinfo_output, latex_output, html_output, + autodoc_output; + +/* dummy routine which does nothing */ +void dummy _((void)); + +#endif diff --git a/patchlevel.h b/patchlevel.h new file mode 100644 index 0000000..2d23afc --- /dev/null +++ b/patchlevel.h @@ -0,0 +1,2 @@ +#define VERSION 2 +#define PATCHLEVEL 41 diff --git a/semantic.c b/semantic.c new file mode 100644 index 0000000..1dac9b5 --- /dev/null +++ b/semantic.c @@ -0,0 +1,732 @@ +/* $Id: semantic.c,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * + * C manual page generator + * These routines implement the semantic actions executed by the yacc parser. + */ +#include "c2man.h" + +#include <ctype.h> +#include <errno.h> +#include "semantic.h" +#include "enum.h" +#include "manpage.h" +#include "strconcat.h" +#include "output.h" + +/* Return TRUE if the given identifier is really a typedef name. + * Search the symbol table for the identifier. + */ +boolean +is_typedef_name (name) +char *name; +{ + return (boolean)(find_symbol(typedef_names, name) != NULL); +} + +/* Initialize a new declaration specifier part. + */ +void +new_decl_spec (decl_spec, text, flags) +DeclSpec *decl_spec; +char *text; +int flags; +{ + decl_spec->text = text ? strduplicate(text) : NULL; + decl_spec->flags = flags; + decl_spec->enum_list = NULL; +} + +/* Free storage used by a declaration specifier part. + */ +void +free_decl_spec (decl_spec) +DeclSpec *decl_spec; +{ + safe_free(decl_spec->text); /* could be an ellipsis you know */ +} + +/* Initialize a new declaration specifier part, including an enum part. + */ +void +new_enum_decl_spec (decl_spec, text, flags, enum_list) +DeclSpec *decl_spec; +char *text; +int flags; +EnumeratorList *enum_list; +{ + decl_spec->text = text; + decl_spec->flags = flags; + decl_spec->enum_list = enum_list; +} + +/* Initialize a new declaration specifier part, but don't strdup the text. */ +void +dyn_decl_spec (decl_spec, text, flags) +DeclSpec *decl_spec; +char *text; +unsigned int flags; +{ + decl_spec->text = text; + decl_spec->flags = flags; + decl_spec->enum_list = NULL; +} + +/* Append two declaration specifier parts together. + */ +void +join_decl_specs (result, a, b) +DeclSpec *result, *a, *b; +{ + if (a->text) + { + if (b->text) + { + result->text = strconcat(a->text, " ", b->text, NULLCP); + free(a->text); + free(b->text); + } + else + result->text = a->text; + } + else + result->text = b->text; + + result->flags = a->flags | b->flags; + + /* only one of the decl specs should have an enum list! */ + result->enum_list = a->enum_list ? a->enum_list : b->enum_list; +} + +/* Allocate and initialize a declarator. */ +Declarator * +new_declarator (text, name) +char *name, *text; +{ + Declarator *d; + + d = (Declarator *)safe_malloc(sizeof(Declarator)); + d->text = text; + d->name = name; + d->type = DECL_SIMPLE; + d->comment = NULL; + d->retcomment = NULL; + new_ident_list(&d->params); + d->head = d; + d->func_stack = NULL; + return d; +} + +/* Free storage used by a declarator. + */ +void +free_declarator (d) +Declarator *d; +{ +#ifdef DEBUG + fprintf(stderr,"free_declarator: decl = %lx, name = %s, text = %s\n", + (long)d, d->name?d->name:"NULL", d->text?d->text:"NULL"); +#endif + safe_free(d->name); /* could be an ellipsis (ie: no name) */ + safe_free(d->text); /* ellipsis is marked by no text too */ + safe_free(d->comment); + safe_free(d->retcomment); + free_param_list(&(d->params)); + if (d->func_stack != NULL) + free_declarator(d->func_stack); + free(d); +} + +/* add a comment to the last declarator in the list */ +int +comment_last_decl(list, comment) +DeclaratorList *list; +char *comment; +{ + if (list->last->comment) + { + declarator_error(list->last); + free(comment); + return 0; + } + else + list->last->comment = comment; + return 1; +} + +/* Initialize a declarator list and add the given declarator to it. + */ +void +new_decl_list (decl_list, declarator) +DeclaratorList *decl_list; +Declarator *declarator; +{ + decl_list->first = decl_list->last = declarator; + declarator->next = NULL; +} + +/* Free storage used by the declarators in the declarator list. + */ +void +free_decl_list (decl_list) +DeclaratorList *decl_list; +{ + Declarator *d, *next; +#ifdef DEBUG + fprintf(stderr,"free_decl_list: decl_list = %lx, first = %lx\n", + (long)decl_list, (long)decl_list->first); +#endif + d = decl_list->first; + while (d != NULL) { + next = d->next; + free_declarator(d); + d = next; + } +} + +/* Add the declarator to the declarator list. + */ +void +add_decl_list (to, from, declarator) +DeclaratorList *to, *from; +Declarator *declarator; +{ + to->first = from->first; + from->last->next = declarator; + to->last = declarator; + to->last->next = NULL; +} + +/* Initialize the parameter structure. + */ +void +new_parameter (param, decl_spec, declarator, comment_before, comment_after) +Parameter *param; /* pointer to structure to be initialized */ +DeclSpec *decl_spec; /* declaration specifier structure */ +Declarator *declarator; /* declarator structure */ +char *comment_before; /* comment before the param */ +char *comment_after; /* comment after the param */ +{ + + if (decl_spec == NULL) { + new_decl_spec(&(param->decl_spec), NULLCP, DS_JUNK); + } else { + param->decl_spec = *decl_spec; + } + + if (declarator == NULL) { + declarator = new_declarator(NULLCP, NULLCP); + } + param->declarator = declarator; + + if (comment_before && comment_after) + { + parameter_error(param); + free(comment_after); /* comment_before will go in Parameter */ + } + + param->declarator->comment = + comment_before ? comment_before : comment_after; + param->suppress = FALSE; + param->duplicate = FALSE; +} + +/* Free the storage used by the parameter. + */ +void +free_parameter (param) +Parameter *param; +{ + free_decl_spec(&(param->decl_spec)); + free_declarator(param->declarator); +} + +/* add a comment to the last parameter in the list */ +int +comment_last_parameter(list, comment) +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 + list->last->declarator->comment = comment; + return 1; +} + +/* Initialize a list of function parameters. + */ +void +new_param_list (param_list, param) +ParameterList *param_list; +Parameter *param; +{ + Parameter *p; + + p = (Parameter *)safe_malloc((unsigned)sizeof(Parameter)); + *p = *param; + + param_list->first = param_list->last = p; + p->next = NULL; +} + +/* Free storage used by the elements in the function parameter list. + */ +void +free_param_list (param_list) +ParameterList *param_list; +{ + Parameter *p, *next; + + p = param_list->first; + while (p != NULL) { + next = p->next; + free_parameter(p); + free(p); + p = next; + } +} + +/* Add the function parameter declaration to the list. + */ +void +add_param_list (to, from, param) +ParameterList *to, *from; +Parameter *param; +{ + Parameter *p; + + p = (Parameter *)safe_malloc((unsigned)sizeof(Parameter)); + *p = *param; + + to->first = from->first; + from->last->next = p; + to->last = p; + p->next = NULL; +} + +/* Initialize an empty list of function parameter names. + */ +void +new_ident_list (param_list) +ParameterList *param_list; +{ + param_list->first = param_list->last = NULL; +} + +/* Add an item to the list of function parameter declarations but set only + * the parameter name field and the comments. + */ +void +add_ident_list (to, from, ident) +ParameterList *to, *from; +Identifier *ident; +{ + Parameter *p; + Declarator *declarator; + + p = (Parameter *)safe_malloc(sizeof(Parameter)); + declarator = new_declarator(ident->name, strduplicate(ident->name)); + new_parameter(p, (DeclSpec *)NULL, declarator, ident->comment_before, + ident->comment_after); + + to->first = from->first; + if (to->first == NULL) { + to->first = p; + } else { + from->last->next = p; + } + to->last = p; + p->next = NULL; +} + +/* Search the list of parameters for a matching parameter name. + * Return a pointer to the matching parameter or NULL if not found. + */ +static Parameter * +search_parameter_list (params, name) +ParameterList *params; +char *name; +{ + Parameter *p; + + for (p = params->first; p != NULL; p = p->next) { + if (p->declarator->name && strcmp(p->declarator->name, name) == 0) + return p; + } + return (Parameter *)NULL; +} + +/* This routine is called to generate function prototypes from traditional + * style function definitions. For each parameter name in the declarator + * list, find the matching parameter name in the parameter list and set + * that parameter's declaration specifier. + * This is also where we promote formal parameters. Parameters of type + * "char", "unsigned char", "short", or "unsigned short" get promoted to + * "int". Parameters of type "float" are promoted to "double". + */ +void +set_param_types (params, decl_spec, declarators, comment, eolcomment) +ParameterList *params; +DeclSpec *decl_spec; +DeclaratorList *declarators; +char *comment; +char *eolcomment; +{ + Declarator *d; + Parameter *p; + + if (comment && eolcomment) + { + yyerror("parameter declaration has multiple comments"); + return; + } + + if (!comment) comment = eolcomment; + + for (d = declarators->first; d != NULL; d = d->next) { + /* Search the parameter list for a matching name. */ + if ((p = search_parameter_list(params, d->name)) == NULL) { + output_error(); + fprintf(stderr, "declared argument \"%s\" is missing\n", d->name); + } else { + char *decl_spec_text = decl_spec->text; + if (promote_param && strcmp(d->text, d->name) == 0) { + if (decl_spec->flags & (DS_CHAR | DS_SHORT)) + decl_spec_text = "int"; + else if (decl_spec->flags & DS_FLOAT) + decl_spec_text = "double"; + } + safe_free(p->decl_spec.text); /* there shouldn't be one, but...*/ + p->decl_spec.text = strduplicate(decl_spec_text); + if (p->decl_spec.flags != decl_spec->flags) + { + if (p->decl_spec.flags & DS_JUNK) + p->decl_spec.flags = decl_spec->flags; + else + parameter_error(p); + } + if (p->decl_spec.enum_list != decl_spec->enum_list) + { + if (p->decl_spec.enum_list == NULL) + p->decl_spec.enum_list = decl_spec->enum_list; + else + parameter_error(p); + } + + free_declarator(p->declarator); + p->declarator = d; + + if (comment) + { + if (p->declarator->comment) + parameter_error(p); + else + p->declarator->comment = strduplicate(comment); + } + } + } + + free_decl_spec(decl_spec); + safe_free(comment); +} + +/* Output a declaration specifier for an external declaration. + */ +void +output_decl_spec (decl_spec) +DeclSpec *decl_spec; +{ + output->text(decl_spec->text); +} + +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(d) +const Declarator *d; +{ + Parameter *first = d->head->params.first; + + return (first != NULL && + (first->declarator->text != NULL || + strcmp(first->decl_spec.text, "void"))); +} + +/* output a declarator name, stripping leading underscores if necessary */ +void output_decl_text(text, keep_underscores) +char *text; +boolean keep_underscores; +{ + if (!keep_underscores) + { + /* skip leading stuff before the actual name */ + while (*text && *text != '_' && !isalnum(*text)) + output->character(*text++); + while (text[0] == '_' && text[1]) text++; + } + output->text(text); +} + +/* Output a function declarator. + */ +static void +output_func_declarator (declarator, format) +Declarator *declarator; +boolean format; +{ + char *s, *t, *decl_text; + + /* Output declarator text before function declarator place holder. */ + if ((s = strstr(declarator->text, "%s")) == NULL) + return; + *s = '\0'; + output->text(declarator->text); + + /* Substitute place holder with function declarator. */ + if (!is_function_declarator(declarator->func_stack)) { + + decl_text = declarator->func_stack->text; + if (declarator->name == NULL || declarator->name[0] == '\0') { + output->text(decl_text); + } else { + + /* Output the declarator text before the declarator name. */ + if ((t = strstr(decl_text, declarator->name)) == NULL) + return; + *t = '\0'; + output->text(decl_text); + *t = declarator->name[0]; + + if (format && strcmp(declarator_prefix, " ") != 0) + output_format_string(declarator_prefix); + + /* Output the declarator name. */ + output_decl_text(declarator->name, format); + + /* Output the remaining declarator text. */ + output->text(t + strlen(declarator->name)); + + /* Output the declarator suffix. */ + if (format) output_format_string(declarator_suffix); + } + } else { + output_func_declarator(declarator->func_stack,format); + } + *s = '%'; + s += 2; + + /* Output declarator text up to but before parameters place holder. */ + if ((t = strstr(s, "()")) == NULL) + return; + *t = '\0'; + output->text(s); + + /* Substitute place holder with function parameters. */ + output->character(*t++ = '('); + output_parameters(declarator, format); + output->text(t); +} + +/* Output a declarator. + */ +void +output_declarator (d, format) +Declarator *d; +boolean format; +{ + if (d->func_stack) { + output_func_declarator(d, format); + } else { + output_decl_text(d->text, format); + } +} + +/* Output a function parameter. + */ +void +output_parameter (p) +Parameter *p; +{ + if (p->decl_spec.text) + output->text(p->decl_spec.text); + else + /* 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". + */ + if (p->declarator->text && strcmp(p->declarator->text, "...") != 0) + output->text("int "); + + /* not all parameters must have declarators: might be a prototype */ + if (p->declarator->text) { + if (p->decl_spec.text) + output->character(' '); + /* don't format parameters; keep it all on one line */ + output_declarator(p->declarator, FALSE); + } +} + +/* Output the list of function parameters. + */ +static void +output_parameters (d, format) +Declarator *d; +boolean format; +{ + if (has_parameters(d)) { + Parameter *p = d->params.first; + if (format) output_format_string(first_param_prefix); + output_parameter(p); + p = p->next; + while (p != NULL) { + output->character(','); + if (format) output_format_string(middle_param_prefix); + output_parameter(p); + p = p->next; + } + if (format) output_format_string(last_param_suffix); + } + else + output->text("void"); +} + +/* remember variable and function declarations. */ +int +remember_declarations (comment, decl_spec, decl_list, eolcomment) +char *comment; /* comment before */ +DeclSpec *decl_spec; /* declaration specifier */ +DeclaratorList *decl_list; /* list of declared variables */ +char *eolcomment; /* eol comment after */ +{ + Declarator *d, *next; + int ret = 1; + + /* attach EOL comment to last one in list */ + if (eolcomment) + { + Declarator *attach; + + /* if it's a function, attach it to the last parameter */ + if (is_function_declarator(decl_list->last) && + decl_list->last->head->params.last) + attach = decl_list->last->head->params.last->declarator; + else + attach = decl_list->last; + + if (attach->comment) + { + declarator_error(attach); + free(eolcomment); + ret = 0; + } + else + attach->comment = eolcomment; + } + + /* special case of a single declarator handled efficiently */ + if (decl_list->first && decl_list->first->next == NULL) + { + d = decl_list->first; + /* free the declarator comment if it isn't going to get used */ + if (comment) + safe_free(d->comment); + else + comment = d->comment; + + /* and nuke it from the declarator so free_declarator won't free it + * (since safe_free will do that) if new_manual_page decides to throw + * it away. + */ + d->comment = NULL; + + new_manual_page(comment, decl_spec, d); + } + else + { + for (d = decl_list->first; d != NULL; d = next) + { + DeclSpec spec_copy; + char *comment_copy; + + next = d->next; +#ifdef DEBUG + fprintf(stderr, + "remember_declarations: text=%s name=%s\ncomment: %s\n", + d->text,d->name, comment ? comment : "NULL"); +#endif + spec_copy = *decl_spec; + spec_copy.text = strduplicate(decl_spec->text); + comment_copy = d->comment ? d->comment : + (comment ? strduplicate(comment) : NULL); + d->comment = NULL; + new_manual_page(comment_copy, &spec_copy,d); + } + + /* free 'em up */ + free_decl_spec(decl_spec); + safe_free(comment); + } + + return ret; +} + +void parameter_error(param) +Parameter *param; +{ + yyerror("parameter '%s' has multiple comments", param->declarator->name); +} + +void declarator_error(decl) +Declarator *decl; +{ + yyerror("declarator '%s' has multiple comments", decl->name); +} + +/* is a declarator for a function? (as opposed to a variable) */ +boolean is_function_declarator(decl) +const Declarator *decl; +{ + return decl->type == DECL_FUNCTION || decl->type == DECL_FUNCDEF; +} + +/* is a comment a start of a numbered list item */ +boolean is_numbered(text) +const char *text; +{ + char *next = NULL; + + if (*text == '(') { + ++text; + errno = 0; + strtol(text, &next, 0); + if (errno) + return FALSE; + if (*next == ')') + return TRUE; + } + else { + errno = 0; + strtol(text, &next, 0); + if (errno) + return FALSE; + if (*next == '.' || *next == ')') + return TRUE; + } + return FALSE ; +} + diff --git a/semantic.h b/semantic.h new file mode 100644 index 0000000..6518dd4 --- /dev/null +++ b/semantic.h @@ -0,0 +1,121 @@ +/* $Id: semantic.h,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * + * Declarations for semantic action routines + */ +#include "config.h" + +extern boolean is_typedef_name _(( + char *name + )); +void +new_decl_spec _(( +DeclSpec *decl_spec, +char *text, +int flags)); + +extern void join_decl_specs _(( + DeclSpec *result, + DeclSpec *a, + DeclSpec *b + )); +extern void free_decl_spec _(( + DeclSpec *decl_spec + )); +void +new_parameter _(( +Parameter *param, /* pointer to structure to be initialized */ +DeclSpec *decl_spec, /* declaration specifier structure */ +Declarator *declarator, /* declarator structure */ +char *comment_before, /* comment before the param */ +char *comment_after)); /* comment after the param */ + +extern void free_parameter _(( + Parameter *param + )); + +/* add a comment to the last parameter in the list */ +int +comment_last_parameter _((ParameterList *list, char *comment)); + +extern void new_param_list _(( + ParameterList *param_list, + Parameter *param + )); + +extern void add_param_list _(( + ParameterList *to, + ParameterList *from, + Parameter *param + )); +extern void free_param_list _(( + ParameterList *param_list + )); +extern void new_ident_list _(( + ParameterList *param_list + )); +extern void add_ident_list _(( + ParameterList *to, + ParameterList *from, + Identifier *ident + )); +extern Declarator * new_declarator _(( + char *text, + char *name + )); +extern void free_declarator _(( + Declarator *d + )); +extern void new_decl_list _(( + DeclaratorList *decl_list, + Declarator *declarator + )); +extern void add_decl_list _(( + DeclaratorList *to, + DeclaratorList *from, + Declarator *declarator + )); +extern void free_decl_list _(( + DeclaratorList *decl_list + )); +extern void set_param_types _(( + ParameterList *params, + DeclSpec *decl_spec, + DeclaratorList *declarators, + char *comment, + char *eolcomment + )); + +/* Output a function parameter.*/ +void output_parameter _((Parameter *p)); + +int +remember_declarations _(( +char *comment, /* comment associated */ +DeclSpec *decl_spec, /* declaration specifier */ +DeclaratorList *decl_list, /* list of declared variables */ +char *eolcomment)); /* eol comment after */ + +void +dyn_decl_spec _(( +DeclSpec *decl_spec, +char *text, +unsigned int flags)); + +void +new_enum_decl_spec _(( +DeclSpec *decl_spec, +char *text, +int flags, +EnumeratorList *enum_list)); + +void +output_decl_spec _((DeclSpec *decl_spec)); + +void +output_declarator _((Declarator *d, boolean format)); + +void parameter_error _((Parameter *param)); +void declarator_error _((Declarator *decl)); + +boolean has_parameters _((const Declarator *d)); +boolean is_function_declarator _((const Declarator *d)); diff --git a/strappend.c b/strappend.c new file mode 100644 index 0000000..c8cc997 --- /dev/null +++ b/strappend.c @@ -0,0 +1,64 @@ +/* $Id: strappend.c,v 1.1 2004-05-03 05:17:48 behdad Exp $ + */ +#include "c2man.h" +#include "strappend.h" + +#ifdef I_STDARG +#include <stdarg.h> +#endif +#ifdef I_VARARGS +#include <varargs.h> +#endif + +extern void outmem(); + +/* + * append a list of strings to another, storing them in a malloc'ed region. + * The first string may be NULL, in which case the rest are simply concatenated. + */ +#ifdef I_STDARG +char *strappend(char *first, ...) +#else +char *strappend(va_alist) + va_dcl +#endif +{ + size_t totallen; + va_list argp; + char *s, *retstring; +#ifndef I_STDARG + char *first; +#endif + /* add up the total length */ +#ifdef I_STDARG + va_start(argp,first); +#else + va_start(argp); + first = va_arg(argp, char *); +#endif + totallen = first ? strlen(first) : 0; + while ((s = va_arg(argp,char *)) != NULL) + totallen += strlen(s); + va_end(argp); + + /* malloc the memory */ + totallen++; /* add space for the nul terminator */ + if ((retstring = first ? realloc(first,totallen) : malloc(totallen)) == 0) + outmem(); + + if (first == NULL) *retstring = '\0'; + +#ifdef I_STDARG + va_start(argp,first); +#else + va_start(argp); + first = va_arg(argp, char *); /* skip the first arg */ +#endif + + while ((s = va_arg(argp,char *)) != NULL) + strcat(retstring,s); + + va_end(argp); + + return retstring; +} diff --git a/strappend.h b/strappend.h new file mode 100644 index 0000000..585df68 --- /dev/null +++ b/strappend.h @@ -0,0 +1,6 @@ +/* $Id: strappend.h,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * concatenate a list of strings, storing them in a malloc'ed region + */ +#include "config.h" + +char *strappend _V((char *first, ...)); diff --git a/strconcat.c b/strconcat.c new file mode 100644 index 0000000..c9caff6 --- /dev/null +++ b/strconcat.c @@ -0,0 +1,74 @@ +/* $Id: strconcat.c,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * concatenate a list of strings, storing them in a malloc'ed region + */ +#include "c2man.h" +#include "strconcat.h" + +#ifdef I_STDARG +#include <stdarg.h> +#endif +#ifdef I_VARARGS +#include <varargs.h> +#endif + +extern void outmem(); + +#ifdef I_STDARG +char *strconcat(const char *first, ...) +#else +char *strconcat(va_alist) + va_dcl +#endif +{ + size_t totallen; + va_list argp; + char *s, *retstring; +#ifndef I_STDARG + char *first; +#endif + /* add up the total length */ +#ifdef I_STDARG + va_start(argp,first); +#else + va_start(argp); + first = va_arg(argp, char *); +#endif +#ifdef DEBUG + fprintf(stderr,"strconcat: \"%s\"",first); +#endif + totallen = strlen(first); + while ((s = va_arg(argp,char *)) != NULL) + { + totallen += strlen(s); +#ifdef DEBUG + fprintf(stderr,",\"%s\"",s); +#endif + } +#ifdef DEBUG + fprintf(stderr,"\nstrlen = %ld\n",(long)totallen); +#endif + va_end(argp); + + /* malloc the memory */ + if ((retstring = malloc(totallen + 1)) == 0) + outmem(); + +#ifdef I_STDARG + va_start(argp,first); +#else + va_start(argp); + first = va_arg(argp, char *); +#endif + /* copy the stuff in */ + strcpy(retstring,first); + + while ((s = va_arg(argp,char *)) != NULL) + strcat(retstring,s); + + va_end(argp); + +#ifdef DEBUG + fprintf(stderr,"strconcat returns \"%s\"\n",retstring); +#endif + return retstring; +} diff --git a/strconcat.h b/strconcat.h new file mode 100644 index 0000000..021d451 --- /dev/null +++ b/strconcat.h @@ -0,0 +1,6 @@ +/* $Id: strconcat.h,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * concatenate a list of strings, storing them in a malloc'ed region + */ +#include "config.h" + +char *strconcat _V((const char *first, ...)); diff --git a/string.c b/string.c new file mode 100644 index 0000000..5af082b --- /dev/null +++ b/string.c @@ -0,0 +1,82 @@ +/* $Id: string.c,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * + * Some string handling routines + */ +#include "c2man.h" +#include <ctype.h> + +/* Copy the string into an allocated memory block. + * Return a pointer to the copy. + */ +char * +strduplicate (s) +const char *s; /* The string to copy. May be NULL */ +{ + char *dest; + + if (!s) return NULL; + + if ((dest = malloc((unsigned)(strlen(s)+1))) == NULL) + outmem(); + + strcpy(dest, s); + return dest; +} + +#ifndef HAS_STRSTR + +/* Return a pointer to the first occurence of the substring + * within the string, or NULL if not found. + */ +char * +strstr (src, key) +const char *src, *key; +{ + char *s; + int keylen; + + keylen = strlen(key); + s = strchr(src, *key); + while (s != NULL) { + if (strncmp(s, key, keylen) == 0) + return s; + s = strchr(s+1, *key); + } + return NULL; +} + +#endif + +/* compare two strings case insensitively, for up to n chars */ +int strncmpi(s1, s2, n) +const char *s1, *s2; +size_t n; +{ + while(n--) + { + char c1 = *s1, c2 = *s2; + + if (c1 == '\0' && c2 == '\0') break; + + if (isalpha(c1) && isupper(c1)) c1 = tolower(c1); + if (isalpha(c2) && isupper(c2)) c2 = tolower(c2); + + if (c1 < c2) return -1; + if (c1 > c2) return 1; + s1++; s2++; + } + return 0; +} + +/* convert string to upper case */ +char *strtoupper(in) +char *in; +{ + char *s; + + for (s = in; *s; s++) + { + if (islower(*s)) *s = toupper(*s); + } + return in; +} diff --git a/symbol.c b/symbol.c new file mode 100644 index 0000000..85be039 --- /dev/null +++ b/symbol.c @@ -0,0 +1,119 @@ +/* $Id: symbol.c,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * + * Symbol table maintenance. Implements an abstract data type called + * the symbol table. + */ +#include "c2man.h" +#include "symbol.h" + +/* Create a symbol table. + * Return a pointer to the symbol table or NULL if an error occurs. + */ +SymbolTable * +create_symbol_table () +{ + SymbolTable *symtab; + int i; + + symtab = (SymbolTable *)safe_malloc(sizeof(SymbolTable)); + + for (i = 0; i < SYM_MAX_HASH; ++i) + symtab->bucket[i] = NULL; + + return symtab; +} + + +/* Free the memory allocated to the symbol table. + */ +void +destroy_symbol_table (symtab) +SymbolTable *symtab; +{ + int i; + Symbol *sym, *next; + + for (i = 0; i < SYM_MAX_HASH; ++i) { + sym = symtab->bucket[i]; + while (sym != NULL) { + next = sym->next; + free(sym->name); + free(sym); + sym = next; + } + } + free(symtab); +} + + +/* This is a simple hash function mapping a symbol name to a hash bucket. */ + +static unsigned int +hash (name) +char *name; +{ + char *s; + unsigned int h; + + h = 0; + s = name; + while (*s != '\0') + h = (h << 1) ^ *s++; + return h % SYM_MAX_HASH; +} + + +/* Search the list of symbols <list> for the symbol <name>. + * Return a pointer to the symbol or NULL if not found. + */ +static Symbol * +search_symbol_list (list, name) +Symbol *list; +char *name; +{ + Symbol *sym; + + for (sym = list; sym != NULL; sym = sym->next) { + if (strcmp(sym->name, name) == 0) + return sym; + } + return NULL; +} + + +/* Look for symbol <name> in symbol table <symtab>. + * Return a pointer to the symbol or NULL if not found. + */ +Symbol * +find_symbol (symtab, name) +SymbolTable *symtab; +char *name; +{ + return search_symbol_list(symtab->bucket[hash(name)], name); +} + + +/* If the symbol <name> does not already exist in symbol table <symtab>, + * then add the symbol to the symbol table. + * Return a pointer to the symbol or NULL on an error. + */ +Symbol * +new_symbol (symtab, name, flags) +SymbolTable *symtab; /* symbol table */ +char *name; /* symbol name */ +int flags; /* symbol attributes */ +{ + Symbol *sym; + int i; + + if ((sym = find_symbol(symtab, name)) == NULL) { + sym = (Symbol *)safe_malloc(sizeof(Symbol)); + sym->name = strduplicate(name); + sym->flags = flags; + sym->valtype = SYMVAL_NONE; + i = hash(name); + sym->next = symtab->bucket[i]; + symtab->bucket[i] = sym; + } + return sym; +} diff --git a/symbol.h b/symbol.h new file mode 100644 index 0000000..2ce0f5f --- /dev/null +++ b/symbol.h @@ -0,0 +1,41 @@ +/* $Id: symbol.h,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * + * Definitions for a symbol table + */ +#include "config.h" + +#ifndef _SYMBOL_H +#define _SYMBOL_H + +typedef struct _symbol { + struct _symbol *next; /* next symbol in list */ + char *name; /* name of symbol */ + unsigned short flags; /* symbol attributes */ + + enum { SYMVAL_NONE, SYMVAL_ENUM } valtype; + + union { + struct _enumerator_list *enum_list; + } value; +} Symbol; + +/* The hash table length should be a prime number. */ +#define SYM_MAX_HASH 251 + +typedef struct _symbol_table { + Symbol *bucket[SYM_MAX_HASH]; /* hash buckets */ +} SymbolTable; + +/* Create symbol table */ +extern SymbolTable *create_symbol_table(); + +/* destroy symbol table */ +extern void destroy_symbol_table _((SymbolTable *symtab)); + +/* Lookup symbol name */ +extern Symbol *find_symbol _((SymbolTable *symtab, char *name)); + +/* Define new symbol */ +extern Symbol *new_symbol _((SymbolTable *symtab, char *name, int flags)); + +#endif diff --git a/texinfo.c b/texinfo.c new file mode 100644 index 0000000..1b464b6 --- /dev/null +++ b/texinfo.c @@ -0,0 +1,465 @@ +/* $Id: texinfo.c,v 1.1 2004-05-03 05:17:48 behdad Exp $ + * functions for texinfo style output. + */ +#include "c2man.h" +#include "manpage.h" +#include "output.h" +#include <ctype.h> + +static char *heading_not_in_contents[] = + {"@chapheading ", "@heading ", "@subheading ", "@subsubheading "}; +static char *heading_in_contents[] = + {"@chapter ", "@section ", "@subsection ", "@subsubsection "}; + +#define n_levels (sizeof(heading_not_in_contents) / sizeof(char *)) + +#define level(n) ((n) >= n_levels ? n_levels - 1 : (n)) + +/* section level for man page entry */ +static int top_level = 1; + +/* always output node for manpage, even if embedded */ +static int embed_node_info = 0; + +/* use title as name of section, rather than "NAME" */ +static int title_name = 0; + +/* the section title, filled in by texinfo_header */ +static const char *title = "INTERNAL ERROR, BOGUS TITLE DUDE!"; + +/* do section titles get capitalized? */ +static int capitalize_sections = 0; + +void texinfo_char(c) +const int c; +{ + int i; + + switch(c) + { + case '\t': + for (i = 0; i < NUM_TAB_SPACES; i++) + putchar(' '); + break; + default: + putchar(c); + break; + } +} + +void texinfo_text(text) +const char *text; +{ + while (*text) + texinfo_char(*text++); +} + +void put_section(text) +const char *text; +{ + + if (capitalize_sections) + { + int first_letter = 1; + + for ( ; *text ; text++) + { + texinfo_char(first_letter ? toupper(*text) : tolower(*text)); + first_letter = isspace(*text); + } + } + else + texinfo_text(text); +} + +void texinfo_comment() { put_string("@c "); } + +void texinfo_header(firstpage, input_files, grouped, name, terse, section) +ManualPage *firstpage; +int input_files; +boolean grouped; +const char *name; +const char *terse; +const char *section; +{ + if (! make_embeddable) + { + put_string("\\input texinfo @c -*-texinfo-*-\n"); + output_warning(); + put_string("@c %**start of header\n"); + put_string("@setfilename "); + texinfo_text(name); + put_string(".info\n@settitle "); + texinfo_text(name); + putchar('\n'); + put_string("@c %**end of header\n"); + + put_string("@node Top, "); + texinfo_text(name); + put_string(", (dir), (dir)\n"); + } + + if (! make_embeddable || embed_node_info) + { + put_string("@node "); + texinfo_text(name); + put_string(", (dir), Top, (dir)\n"); + } + + title = name; +} + +void texinfo_dash() { put_string("---"); } + +void texinfo_section(name) +const char *name; +{ + put_string(heading_not_in_contents[level(top_level)]); + put_section(name); + putchar('\n'); + put_string("@noindent\n"); +} + +void texinfo_section_in_contents(name) +const char *name; +{ + put_string(heading_in_contents[level(top_level)]); + put_section(name); + putchar('\n'); + put_string("@noindent\n"); +} + +void texinfo_sub_section(name) +const char *name; +{ + put_string(heading_not_in_contents[level(top_level+1)]); + put_section(name); + putchar('\n'); + put_string("@noindent\n"); +} + +void texinfo_break_line() { /* put_string("@*\n"); */ } +void texinfo_blank_line() { put_string("@sp 1\n"); } + +void texinfo_code_start() { put_string("@example\n"); } +void texinfo_code_end() { put_string("@end example\n"); } + +void texinfo_code(text) +const char *text; +{ + put_string("@code{"); + texinfo_text(text); + put_string("}"); +} + +void texinfo_tag_list_start() { put_string("@quotation\n@table @code\n"); } +void texinfo_tag_entry_start() { put_string("@item "); } +void texinfo_tag_entry_end() { putchar('\n'); } + +void texinfo_tag_entry_end_extra(text) +const char *text; +{ + putchar('('); + texinfo_text(text); + putchar(')'); + texinfo_tag_entry_end(); +} +void texinfo_tag_list_end() { put_string("@end table\n@end quotation\n"); } + +void texinfo_table_start(longestag) +const char *longestag; +{ put_string("@quotation\n@table @code\n"); } + +void texinfo_table_entry(name, description) +const char *name; +const char *description; +{ + put_string("@item "); + texinfo_text(name); + putchar('\n'); + if (description) + output_comment(description); + else + putchar('\n'); +} + +void texinfo_table_end() { put_string("@end table\n@end quotation\n"); } + +void texinfo_list_start() { } +void texinfo_list_entry(text) +const char *text; +{ + texinfo_code(text); +} +void texinfo_list_separator() { put_string(",\n"); } +void texinfo_list_end() { putchar('\n'); } + +void texinfo_include(filename) +const char *filename; +{ + put_string("@include "); + texinfo_text(filename); + put_string("\n"); +} + +void texinfo_file_end() { put_string("@bye\n"); } + +static first_name = 1; +void texinfo_name(name) +const char *name; +{ + if (name) + { + if (!first_name || !title_name || strcmp(title,name)) + texinfo_text(name); + first_name = 0; + } + else + { + first_name = 1; + if (title_name) + { + /* don't muck around with capitalization of title */ + int capitalize_sections_save = capitalize_sections; + capitalize_sections = 0; + + texinfo_section_in_contents(title); + capitalize_sections = capitalize_sections_save; + } + else + texinfo_section("NAME"); + } +} + +void texinfo_terse_sep() +{ + if (!title_name || group_together) + { + texinfo_char(' '); + texinfo_dash(); + texinfo_char(' '); + } +} + +void texinfo_reference(text) +const char *text; +{ + texinfo_text(text); + texinfo_char('('); + texinfo_text(manual_section); + texinfo_char(')'); +} + +/* ideally, this should be made aware of embedded texinfo commands */ +void texinfo_description(text) +const char *text; +{ + enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE; + boolean new_line = TRUE; + + /* correct punctuation a bit as it goes out */ + for (;*text;text++) + { + int c = *text; + + if (new_line && (c == '-' || c == '*' || is_numbered(text))) + { + output->break_line(); + state = CAPITALISE; + } + else if (c == '.') + state = PERIOD; + else if (isspace(c) && state == PERIOD) + state = CAPITALISE; + else if (isalnum(c)) + { + if (islower(c) && state == CAPITALISE) c = toupper(c); + state = TEXT; + } + + output->character(c); + new_line = c == '\n'; + } + + /* do a full stop if there wasn't one */ + if (state == TEXT) output->character('.'); +} + +/* ideally, this should be made aware of embedded texinfo commands */ +void +texinfo_returns(comment) +const char *comment; +{ + enum { TEXT, PERIOD, CAPITALISE } state = CAPITALISE; + char lastchar = '\n'; + boolean tag_list_started = FALSE; + + /* for each line... */ + while (*comment) + { + boolean tagged = FALSE; + + { + const char *c = comment; + + /* search along until the end of a word */ + while (*c && *c != ':' && !isspace(*c)) + c++; + + /* skip all spaces or tabs after the first word */ + while (*c && *c != '\n') + { + if (*c == '\t' || *c == ':') + { + tagged = TRUE; + break; + } + else if (!isspace(*c)) + break; + + c++; + } + } + + /* is it tagged?; explicitly reject dot commands */ + if (tagged) + { + /* output lingering newline if necessary */ + if (lastchar != '\n') + { + if (state == TEXT && !ispunct(lastchar)) output->character('.'); + output->character(lastchar = '\n'); + } + + if (!tag_list_started) + { + output->tag_list_start(); + tag_list_started = TRUE; + } + + /* output the taggy bit */ + output->tag_entry_start(); + while (*comment && *comment != ':' && !isspace(*comment)) + output->character(*comment++); + output->tag_entry_end(); + + /* skip any extra tabs or spaces */ + while (*comment == ':' || (isspace(*comment) && *comment != '\n')) + comment++; + + state = CAPITALISE; + } + + /* terminate the previous line if necessary */ + if (lastchar != '\n') output->character(lastchar = '\n'); + + /* correct punctuation a bit as the line goes out */ + for (;*comment && *comment != '\n'; comment++) + { + char c = *comment; + + if (c == '.') + state = PERIOD; + else if (isspace(c) && state == PERIOD) + state = CAPITALISE; + else if (isalnum(c)) + { + if (islower(c) && state == CAPITALISE && fixup_comments) + c = toupper(c); + state = TEXT; + } + + output->character(lastchar = c); + } + + /* if it ended in punctuation, just output the nl straight away. */ + if (ispunct(lastchar)) + { + if (lastchar == '.') state = CAPITALISE; + output->character(lastchar = '\n'); + } + + if (*comment) comment++; + } + + /* output lingering newline if necessary */ + if (lastchar != '\n') + { + if (state == TEXT && !ispunct(lastchar) && fixup_comments) + output->character('.'); + output->character('\n'); + } + + if (tag_list_started) + output->tag_list_end(); +} + + +int texinfo_parse_option(option) +const char *option; +{ + if (option[0] == 't') + title_name = 1; + else if (option[0] == 'n') + embed_node_info = 1; + else if (option[0] == 's') + { + top_level = atoi(&option[1]); + if (top_level < 0) return 1; + } + else if (option[0] == 'C') + capitalize_sections = 1; + else return 1; + + return 0; +} + +void texinfo_print_options() +{ + fputs("\ttexinfo options:\n", stderr); + fputs("\tt\tuse manpage title as NAME title\n", stderr); + fputs("\tn\toutput node info if embedded output\n", stderr); + fputs("\ts<n>\tset top heading level to <n>\n", stderr); + fputs("\tC\tcaptialize section titles\n", stderr); +} + + +struct Output texinfo_output = +{ + texinfo_comment, + texinfo_header, + texinfo_dash, + texinfo_section, + texinfo_sub_section, + texinfo_break_line, + texinfo_blank_line, + texinfo_code_start, + texinfo_code_end, + texinfo_code, + texinfo_tag_list_start, + texinfo_tag_list_end, + texinfo_tag_entry_start, + texinfo_tag_entry_start, /* entry_start_extra */ + texinfo_tag_entry_end, + texinfo_tag_entry_end_extra, + texinfo_table_start, + texinfo_table_entry, + texinfo_table_end, + dummy, /* texinfo_indent */ + texinfo_list_start, + texinfo_list_entry, + texinfo_list_separator, + texinfo_list_end, + texinfo_include, + texinfo_file_end, + texinfo_text, + texinfo_char, + texinfo_parse_option, + texinfo_print_options, + texinfo_name, + texinfo_terse_sep, + texinfo_reference, + texinfo_text, + texinfo_description, + texinfo_returns +}; |