[alt.religion.computers] ANSI C prototypes

boyd@necisa.ho.necisa.oz (Boyd Roberts) (10/31/90)

In article <1005@christopher-robin.cs.bham.ac.uk> ptf@uk.ac.bham.cs (Paul Flinders <FlindersPT>) writes:
>
>in foo.h:
>	extern void ddprintf(const char *fmt, ...);
>
>BUT in foo.c:
>
>	void ddprintf(va_alist)
>	va_dcl;
>
>this causes problems.

Right on!  The function prototypes are just stupid.  I shouldn't have to go:

    extern gore	*good(const char *vomit);

to get the functionality of type checking of function arguments.
The compiler and loader should do it.  The computer is there
to do the tedious stuff for you.  The computer is (was) your friend.

There should be enough stuff in the `.o' to specify all the calls
to my function good() complete with the types of all the arguments.
The `.o' that contains good() should contain extra information of the
types of the arguments to good().  Then the loader should cross
check all the calls with the declaration and then _refuse_ to load 
unless everything check out just right.  Yes, that's right -- _refuse_.

Now this would remove function prototyping once and for all.

But, before you say `what about var args'.  Things that are
var arged should be specified as such on the command line.  You
say how many arguments to check.

Now, I've seen it done and it's good.  My old mate brucee did
it `back in the hippy days' and it runs at the Labs.  Ever heard
of `cyntax'?  Check out cyntax(1) in the Ninth Edition manual.


Boyd Roberts			boyd@necisa.ho.necisa.oz.au

``When the going gets wierd, the weird turn pro...''

rjc@uk.ac.ed.cstr (Richard Caley) (11/01/90)

In article <1906@necisa.ho.necisa.oz> boyd@necisa.ho.necisa.oz (Boyd Roberts) writes:

    Right on!  The function prototypes are just stupid.  I shouldn't have to go:

	extern gore	*good(const char *vomit);

    to get the functionality of type checking of function arguments.
    The compiler and loader should do it.  


Ok, so what happens to code which never sees the loader?

I want my errors when I compile things, not when some poor guy tries
to use it.

Even in less extreme cases, having the loader do all the checking
would mean you got no error checking until you did a full compile and
link. For even fairly small programs that will be unacceptable ``Well,
yeah, we really do need the cray, we have to recompile the entire
system every three minutes during development.''

Which is not to say that the linker shouldn't _also_ check the types.
So should the run time system and the disc driver, if I had my way :-)

--
rjc@uk.ac.ed.cstr	Paranoid of Leith

salomon@ccu.umanitoba.ca (Dan Salomon) (11/02/90)

In article <RJC.90Nov1145211@brodie.uk.ac.ed.cstr> rjc@uk.ac.ed.cstr (Richard Caley) writes:
>In article <1906@necisa.ho.necisa.oz> boyd@necisa.ho.necisa.oz (Boyd Roberts) writes:
>
> > Right on!  The function prototypes are just stupid.
> > I shouldn't have to type:
> >      extern gore	*good(const char *vomit);
> > to get the functionality of type checking of function arguments.
> > The compiler and loader should do it.  
>
> I want my errors when I compile things, not when some poor guy tries
> to use it.
>

You are both right.  Repeatedly typing function prototypes is a tedious
waste of time, but we really would like type errors reported at compile
time.

A possible solution would be to have a utility for building libraries
of function prototypes from source code.  The compiler could check
source code usage against usage in this library.  The linker would have
to repeat the check to make sure that the prototype library didn't get
out of synch with the object library.
-- 

Dan Salomon -- salomon@ccu.UManitoba.CA
               Dept. of Computer Science / University of Manitoba
	       Winnipeg, Manitoba, Canada  R3T 2N2 / (204) 275-6682

peter@ficc.ferranti.com (Peter da Silva) (11/03/90)

In article <1990Nov2.030556.27759@ccu.umanitoba.ca> salomon@ccu.umanitoba.ca (Dan Salomon) writes:
> You are both right.  Repeatedly typing function prototypes is a tedious
> waste of time, but we really would like type errors reported at compile
> time.

Ever hear of include files?

> A possible solution would be to have a utility for building libraries
> of function prototypes from source code.

The Manx compiler for the Amiga has a flag that lets you build include files
from the source.
-- 
Peter da Silva.   `-_-'
+1 713 274 5180.   'U`
peter@ferranti.com

stever@Octopus.COM (Steve Resnick ) (11/03/90)

In article <1990Nov2.030556.27759@ccu.umanitoba.ca> salomon@ccu.umanitoba.ca (Dan Salomon) writes:
>In article <RJC.90Nov1145211@brodie.uk.ac.ed.cstr> rjc@uk.ac.ed.cstr (Richard Caley) writes:
>>In article <1906@necisa.ho.necisa.oz> boyd@necisa.ho.necisa.oz (Boyd Roberts) writes:
>>
>> > Right on!  The function prototypes are just stupid.
>> > I shouldn't have to type:
>> >      extern gore	*good(const char *vomit);
>> > to get the functionality of type checking of function arguments.
>> > The compiler and loader should do it.  
>>
>> I want my errors when I compile things, not when some poor guy tries
>> to use it.
>>
>
>You are both right.  Repeatedly typing function prototypes is a tedious
>waste of time, but we really would like type errors reported at compile
>time.
>
>A possible solution would be to have a utility for building libraries
>of function prototypes from source code.  The compiler could check
>source code usage against usage in this library.  The linker would have
>to repeat the check to make sure that the prototype library didn't get
>out of synch with the object library.
>-- 
>

	Some C compilers do this for you. Microsoft C 6.0 will, as an option
	generate declarations (prototypes) to stdout when asked to. This has
	saved me the tedium of typing all those prototypes in myself! :)


Cheers!
Steve


-- 
----------------------------------------------------------------------------
steve.resnick@f105.n143.z1.FIDONET.ORG - or - apple!camphq!105!steve.resnick
Flames, grammar errors, spelling errrors >/dev/nul
The Asylum OS/2 BBS - (408)263-8017 IFNA 1:143/105.0

salomon@ccu.umanitoba.ca (Dan Salomon) (11/03/90)

In article <MKU65ZE@xds13.ferranti.com> peter@ficc.ferranti.com (Peter da Silva) writes:
>In article <1990Nov2.030556.27759@ccu.umanitoba.ca> salomon@ccu.umanitoba.ca (Dan Salomon) writes:
>> You are both right.  Repeatedly typing function prototypes is a tedious
>> waste of time, but we really would like type errors reported at compile
>> time.
>> A possible solution would be to have a utility for building libraries
>> of function prototypes from source code.
>
>Ever hear of include files?
>

Actually, include files aren't quite the same as a library of function
prototypes.  The distinction is the same as between an object library,
and a file full of object modules.  In addition to the problem of
keeping the include files up to date,  there is also the problem of
filling up your symbol table with dozens of unused symbols.
Processing all the prototypes in an include file is slower than a search
for and processing of only the ones actually needed.

This is especially a problem when a large collection of functions are
grouped together in a common directory, but in separate files.  Such an
organization speeds up the compilation of large projects, by allowing
the recompilation of only the out-of-date functions.  If each of the
files has to include a large number of prototypes, the some of the
speed up is lost.

Once your set of functions is running, what tool do you use to assemble
all the prototypes into up-to-date include files?  Such tools exists for
object modules, perhaps similar tools are needed for function
prototypes.
-- 

Dan Salomon -- salomon@ccu.UManitoba.CA
               Dept. of Computer Science / University of Manitoba
	       Winnipeg, Manitoba, Canada  R3T 2N2 / (204) 275-6682

rjc@uk.ac.ed.cstr (Richard Caley) (11/03/90)

In article <1990Nov2.030556.27759@ccu.umanitoba.ca> salomon@ccu.umanitoba.ca (Dan Salomon) writes:

    In article <RJC.90Nov1145211@brodie.uk.ac.ed.cstr> rjc@uk.ac.ed.cstr (Richard Caley) writes:

    > I want my errors when I compile things, not when some poor guy tries
    > to use it.

    You are both right.  Repeatedly typing function prototypes is a tedious
    waste of time, but we really would like type errors reported at compile
    time.

Since there is, as far as I know, no difference between the prototype
and the definition except for the `;', there is no extra typing to do.
Just copy the definition. Now I realise there are some brain dead
editors out there and I have upon occasion written C using `cat', but
I assume that anything anyone is willing to use for more than trivial
coding can manage a textual copy.

--
rjc@uk.ac.ed.cstr

peter@ficc.ferranti.com (Peter da Silva) (11/04/90)

In article <1990Nov3.122432.24738@ccu.umanitoba.ca> salomon@ccu.umanitoba.ca (Dan Salomon) writes:
> Actually, include files aren't quite the same as a library of function
> prototypes.  The distinction is the same as between an object library,
> and a file full of object modules.

True, there is an efficiency consideration. I don't tend to have a single
module or collection of modules that require more than a handful  of files,
so this hasn't been a problem.

Plus, who says include files have to be stored in files? The C standard
doesn't. This is an implementation detail. :-> Yeh, it's a problem, but
it's not a show stopper.

> In addition to the problem of
> keeping the include files up to date,

That is the real problem. You need a tool to extract the prototypes from
a file. Beyond that, make will do the rest.

> Processing all the prototypes in an include file is slower than a search
> for and processing of only the ones actually needed.

Precompiled include files. Some compilers have 'em. I've never had to use
'em myself, but my home machine has plenty of MIPs and IoStones.
-- 
Peter da Silva.   `-_-'
+1 713 274 5180.   'U`
peter@ferranti.com

Markku.Savela@tel.vtt.fi (Markku Savela) (11/04/90)

In article <-BV6Q16@xds13.ferranti.com> peter@ficc.ferranti.com (Peter da Silva) writes:
>> In addition to the problem of
>> keeping the include files up to date,

>That is the real problem. You need a tool to extract the prototypes from
>a file. Beyond that, make will do the rest.

  Yes, but this is a bad habit. The include file should be the external
specification of the module. Any changes in external specification
shouldn't be so frequent, if you have the code done right.
--
Markku Savela (savela@tel.vtt.fi), Technical Research Centre of Finland
Telecommunications Laboratory, Otakaari 7 B, SF-02150 ESPOO, Finland

jaakola@cc.helsinki.fi (11/05/90)

In article <RJC.90Nov3153129@brodie.uk.ac.ed.cstr>, rjc@uk.ac.ed.cstr (Richard Caley) writes:
> Since there is, as far as I know, no difference between the prototype
> and the definition except for the `;', there is no extra typing to do.
> Just copy the definition.

Why do *I* have to do the copying for all my functions, while there's
the compiler which a) is supposed to work for me and not vice versa
             and   b) should know ANSI peculiarities better than I do?

It's okay for me to make an include file, where I declare functions that
are visible to other modules. But consider the following example:

	static void auxiliary_func(a) /* private helper-function */
	int a;
	{
		...
	}

	void visible_fun(b)
	int b;
	{
		...
		auxiliary_func(b+1);   /* should be checked */
		...
	}

The point is, why should I have to tell the compiler twice the type of
auxiliary_func? The prototype should be necessary only if I make a
forward reference to make it easier for the compiler to check types in a
single pass. I think the function definition tells unambiguously the
types, so I should not have to watch warnings such as "function
definition used as a prototype" (Microsoft C 5.1).

Ok, I could turn off such warnings in Microsoft C 5.1, but this turns
off some useful checks as a side-effect! Please, Microsoft, make a
"prototypes-off" switch into Microsoft C for me!

I do not want to bother keeping prototypes of such axiliary
helper-functions up-to-date. "You could use an automatic prototype
builder," I hear you say. Yeah, I could, but why couldn't that builder
be built into the compiler, so that I didn't have to burden my brains to
use it? After all, those helper-functions tend to change much when I
fine-tune the implementation of my module.

Another ANSI-misfeature is the ability to make declarations such as:

	int foo(int b, char *s)

I like much more the old style

	int foo(b,s)
	int b;      /* the b means ... blahblahblah */
	char *s;    /* the s means ... */

This is better, because you can have function name and args on the same
line, so that you can query all function names by grep-like tools (grep
outputs only single lines, which is very compact and - I think -
readable format). If you have much args, this way you can still put all
of them on a single line.

After the first line I tell the types of each arg and use comments after
each args as shown above.

hurtta@cs.Helsinki.FI (Kari Hurtta) (11/05/90)

In article <3933.27353319@cc.helsinki.fi> jaakola@cc.helsinki.fi writes:

   It's okay for me to make an include file, where I declare functions that
   are visible to other modules. But consider the following example:

	   static void auxiliary_func(a) /* private helper-function */
	   int a;
	   {
		   ...
	   }

	   void visible_fun(b)
	   int b;
	   {
		   ...
		   auxiliary_func(b+1);   /* should be checked */
		   ...
	   }

   The point is, why should I have to tell the compiler twice the type of
   auxiliary_func? The prototype should be necessary only if I make a
   forward reference to make it easier for the compiler to check types in a
   single pass. I think the function definition tells unambiguously the
   types, so I should not have to watch warnings such as "function
   definition used as a prototype" (Microsoft C 5.1).

   Ok, I could turn off such warnings in Microsoft C 5.1, but this turns
   off some useful checks as a side-effect! Please, Microsoft, make a
   "prototypes-off" switch into Microsoft C for me!

Why not:
	   static void auxiliary_func(int a)
	   {
		   ...
	   }

	   void visible_fun(int b)
	   {
		   ...
		   auxiliary_func(b+1);  
		   ...
	   }

- K E H
( hurtta@cc.Helsinki.FI
  hurtta@cs.Helsinki.FI
  HURTTA@FINUH.BITNET
)

peter@ficc.ferranti.com (Peter da Silva) (11/06/90)

In article <3933.27353319@cc.helsinki.fi> jaakola@cc.helsinki.fi writes:
> 	static void auxiliary_func(a) /* private helper-function */
> 	int a;
> 	{
> 		...
> 	}

If you do this:

	static void auxilary_func(int a)
	{
		...
	}

Everything will work fine. You only need declare it once. Just do it right
the first time and you won't have to do it again (my father always used to
tell me that).
-- 
Peter da Silva.   `-_-'
+1 713 274 5180.   'U`
peter@ferranti.com

rmartin@clear.com (Bob Martin) (11/06/90)

In article <1906@necisa.ho.necisa.oz> 
boyd@necisa.ho.necisa.oz (Boyd Roberts) writes:
>
>    Right on!  The function prototypes are just stupid.  I shouldn't have to go:
>
>	extern gore	*good(const char *vomit);
>
>    to get the functionality of type checking of function arguments.
>    The compiler and loader should do it.  
>

The C++ compiler does this.  If you declare a function one way
and use it in another, say with different type arguments, then the
program won't link.  C++ does this by mangling the return type and
argument types into the function name.  Thus when you declare
a function:  int x(int y)  the compiler generates a new name
for the function which is what the loader sees.  This name would
specify that the function's name was x, that it returned an int and
that it had a single int parameter.

So if you screw up your declarations, or attempt to use implicit
declarations (bad style), you cannot get a runtime error becuase
of a type mismatch.  The linker will catch the problem.

--

By the way, this technique of mangling the function names means that

	int x(int y);
		and
	double x(double y);

are completely different functions.  You can declare both of them,
define them separately and call the proper function by making sure
that the types are correct.

+----------------------+---------------------------------------------+
| Robert C. Martin     | The authors opinions are his own and do not |
| rmartin@clear.com    | represent the opinions of his employer.     |
| uunet!clrcom!rmartin |                                             |
+----------------------+---------------------------------------------+


-- 

+----------------------+---------------------------------------------+
| Robert C. Martin     | The authors opinions are his own and do not |
| rmartin@clear.com    | represent the opinions of his employer.     |

rjc@uk.ac.ed.cstr (Richard Caley) (11/06/90)

In article <3933.27353319@cc.helsinki.fi> jaakola@cc.helsinki.fi writes:

    It's okay for me to make an include file, where I declare functions that
    are visible to other modules. But consider the following example:

	    static void auxiliary_func(a) /* private helper-function */
	    int a;
	    {
		    ...
	    }

	    void visible_fun(b)
	    int b;
	    {
		    ...
		    auxiliary_func(b+1);   /* should be checked */
		    ...
	    }

    The point is, why should I have to tell the compiler twice the type of
    auxiliary_func?

You don't. In the above case you havn't even told it once. If you
did...
	
	static void auxiliary_func(int a)
	
	{
	...
	}

you wouldn't need to type anything more. Or if you did that would be a
massive compiler misfeature. This even saves you a couple of
characters :-)


    Ok, I could turn off such warnings in Microsoft C 5.1, but this turns
    off some useful checks as a side-effect! Please, Microsoft, make a
    "prototypes-off" switch into Microsoft C for me!

If the compiler really does complain in the case where you give the
full type in the function definition then I agree that the writers had
better start watching out for mobs of irate users coming after them
with pitchforks and flaming torches. Someone must make a working C
compiler for your machine...

--
rjc@uk.ac.ed.cstr

darcy@druid.uucp (D'Arcy J.M. Cain) (11/06/90)

In article <3933.27353319@cc.helsinki.fi> jaakola@cc.helsinki.fi writes:
>Why do *I* have to do the copying for all my functions, while there's
>the compiler which a) is supposed to work for me and not vice versa
>             and   b) should know ANSI peculiarities better than I do?
>
>	static void auxiliary_func(a) /* private helper-function */
>	int a;
>	{
>		...
>	}
>
>	void visible_fun(b)
>	int b;
>	{
>		...
>		auxiliary_func(b+1);   /* should be checked */
>		...
>	}
>
>The point is, why should I have to tell the compiler twice the type of
>auxiliary_func? The prototype should be necessary only if I make a
All ANSI compilers that I have used do exactly that.  You do of course
have to declare the function in an ANSI compliant way but once you do
it is effectively prototyped from that point on.  In the above example
replace the first two lines with:

	static void auxiliary_func(int a) /* private helper-function */

The only time that I have to retype (or copy) a prototype is when the
function is used in a different module.

>
>Another ANSI-misfeature is the ability to make declarations such as:
>
>	int foo(int b, char *s)
>
>I like much more the old style
>
>	int foo(b,s)
>	int b;      /* the b means ... blahblahblah */
>	char *s;    /* the s means ... */
>
>This is better, because you can have function name and args on the same
>line, so that you can query all function names by grep-like tools (grep
>outputs only single lines, which is very compact and - I think -
>readable format). If you have much args, this way you can still put all
>of them on a single line.
So that just means we need smarter tools, not that we should stand still
for fear of disturbing something.

>
>After the first line I tell the types of each arg and use comments after
>each args as shown above.
Where suitable I do the same thing:
	int foo(
	int b,      /* the b means ... blahblahblah */
	char *s)    /* the s means ... */

-- 
D'Arcy J.M. Cain (darcy@druid)     |
D'Arcy Cain Consulting             |   I support gun control.
West Hill, Ontario, Canada         |   Let's start with the government!
+ 416 281 6094                     |

brad@SSD.CSD.HARRIS.COM (Brad Appleton) (11/09/90)

I grabbed a PD prototype generator utility written in C (that I think works
for both ANSI and K&R) not too long ago - its pretty small - here it is:

I have a 10 line awk script that does it too but the C program is obviously
a wee bit more portable ;-)

Its only two files (a .c and a .h file). Actually its only one file - it can
create its own .h file but you need to get it running first :-)

Anyway - im sure some of you will find some use for it!
Its written in K&R but Im sure it would be no problem to convert to ANSI.


______________________ "And miles to go before I sleep." ______________________
 Brad Appleton           brad@ssd.csd.harris.com       Harris Computer Systems
                             uunet!hcx1!brad           Fort Lauderdale, FL USA
~~~~~~~~~~~~~~~~~~~~ Disclaimer: I said it, not my company! ~~~~~~~~~~~~~~~~~~~


------------------------------ cut here ---------------------------------------

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by brad on Thu Nov  8 11:41:53 EST 1990
# Contents:  mkproto.c mkproto.h
 
echo x - mkproto.c
sed 's/^@//' > "mkproto.c" <<'@//E*O*F mkproto.c//'
/* Program to extract function declarations from C source code */
/* Written by Eric R. Smith and placed in the public domain    */
/* Thanks are due to Jwahar R. Bammi for fixing several bugs   */
/* and providing the Unix makefiles. S. Manoharan included a   */
/* Getopt() function and modified the code to handle C++ style */
/* comments and member functions.                              */

#if defined(__STDC__) && !defined(minix)
#include <stddef.h>
#include <stdlib.h>
#else
#define EXIT_SUCCESS  0
#define EXIT_FAILURE  1
extern char *malloc();
#endif

#include <stdio.h>
#include <ctype.h>
#include <string.h>

#define DEBUG(s) { if (dodebug) fputs(s, stderr); } /* */
/*#define DEBUG(s) /* */

#define ISCSYM(x) ((x) > 0 && (isalnum(x) || (x) == '_'))
#define ABORTED ( (Word *) -1 )
#define MAXPARAM 20       /* max. number of parameters to a function */
#define NEWBUFSIZ (20480*sizeof(char)) /* new buffer size */

typedef enum { false = 0, true = 1 } Boolean;

Boolean newline_seen = true;  /* are we at the start of a line */
Boolean dostatic = false;  /* do static functions? */
Boolean donum    = false;  /* print line numbers? */
Boolean dopromote = true;  /* promote certain sc-specifiers */
Boolean dohead   = true;   /* do file headers? */
Boolean dodebug = false;   /* do debugging? */
Boolean docond   = false;  /* conditionalize for non-ANSI compilers */

long linenum  = 1L;        /* line number in current file */
int glastc   = ' ';        /* last char. seen by getsym() */
int inquote = 0;           /* in a quote?? */

typedef struct word {
   struct word *next;
   char   string[1];
} Word;

#include "mkproto.h"

/*
 * Routines for manipulating lists of words.
 */

Word *word_alloc(s)
char *s;
{
   Word *w;

   w = (Word *) malloc((unsigned)sizeof(Word) + strlen(s) + 1);
		/* ++jrb */
   (void)strcpy(w->string, s);
   w->next = NULL;
   return w;
}

void word_free(w)
Word *w;
{
   Word *oldw;
   while (w) {
      oldw = w;
      w = w->next;
      free((char *)oldw);
   }
}

/* return the length of a list; empty words are not counted */
int List_len(w)
Word *w;
{
   int count = 0;

   while (w) {
      if (*w->string) count++;
      w = w->next;
   }
   return count;
}

/* Append two lists, and return the result */

Word *word_append(w1, w2)
Word *w1, *w2;
{
   Word *r, *w;

   r = w = word_alloc("");

   while (w1) {
      w->next = word_alloc(w1->string);
      w = w->next;
      w1 = w1->next;
   }
   while (w2) {
      w->next = word_alloc(w2->string);
      w = w->next;
      w2 = w2->next;
   }

   return r;
}

/* see if the last entry in w2 is in w1 */

int foundin(w1, w2)
Word *w1, *w2;
{
   while (w2->next)
      w2 = w2->next;

   while (w1) {
      if (!strcmp(w1->string, w2->string))
         return 1;
      w1 = w1->next;
   }
   return 0;
}

/* add the string s to the given list of words */

void addword(w, s)
Word *w; char *s;
{
   while (w->next) w = w->next;
   w->next = word_alloc(s);
}

/* given a list representing a type and a variable name, extract just
 * the base type, e.g. "struct word *x" would yield "struct word"
 */

Word *typelist(p)
Word *p;
{
   Word *w, *r;

   r = w = word_alloc("");
   while (p && p->next) {
      if (p->string[0] && !ISCSYM(p->string[0]))
         break;
      w->next = word_alloc(p->string);
      w = w->next;
      p = p->next;
   }
   return r;
}

/* typefixhack: promote formal parameters of type "char", 
 * "unsigned char", "short", or "unsigned short" to "int".
 */

void typefixhack(w)
Word *w;
{
   Word *oldw = 0;

   while (w) {
      if (*w->string) {
         if ( (!strcmp(w->string, "char") ||
             !strcmp(w->string, "short") )
             && (List_len(w->next) < 2) )
         {
            if (oldw && !strcmp(oldw->string, "unsigned")) {
               oldw->next = w->next;
               free((char *)w);
               w = oldw;
            }
            (void)strcpy(w->string, "int");
         }
      }
      w = w->next;
   }
}

/* read a character: if it's a newline, increment the line count */

int ngetc(f)
FILE *f;
{
   int c;

   c = getc(f);
   if (c == '\n') linenum++;

   return c;
}

/* read the next character from the file. If the character is '\' then
 * read and skip the next character. Any comment sequence is converted
 * to a blank. [ Both C and C++ style comments are considered - sam ]
 */

int fnextch(f)
FILE *f;
{
   int c, lastc, incomment;

   c = ngetc(f);
   while (c == '\\') {
      DEBUG("fnextch: in backslash loop\n");
      c = ngetc(f);   /* skip a character */
      c = ngetc(f);
   }
   if (c == '/' && !inquote) {
      c = ngetc(f);
      if (c == '*') {  /* C comments */
         incomment = 1;
         c = ' ';
         DEBUG("fnextch: comment seen\n");
         while (incomment) {
            lastc = c;
            c = ngetc(f);
            if (lastc == '*' && c == '/')
               incomment = 0;
            else if (c < 0)
               return c;
         }
         return fnextch(f);
      }
      else if ( c == '/' ) {  /* C++ comments */
         incomment = 1;
         c = ' ';
         DEBUG("fnextch: C++ comment seen\n");
         while ( c != '\n' ) {
            c = ngetc(f);
         }
	 incomment = 0;
         return fnextch(f);
      }
      else {
         if (c == '\n') linenum--;
         (void)ungetc(c, f);
         return '/';
      }
   }
   return c;
}


/* Get the next "interesting" character. Comments are skipped, and 
 * strings are converted to "0". Also, if a line starts with "#" 
 * it is skipped.
 */

int nextch(f)
FILE *f;
{
   int c;

   c = fnextch(f);
   if (newline_seen && c == '#') {
      do {
         c = fnextch(f);
      } while (c >= 0 && c != '\n');
      if (c < 0)
         return c;
   }
   newline_seen = (c == '\n') ? true : false;

   if (c == '\'' || c == '\"') {
      DEBUG("nextch: in a quote\n");
      inquote = c;
      while ( (c = fnextch(f)) >= 0 ) {
         if (c == inquote) {
            inquote = 0;
            DEBUG("nextch: out of quote\n");
            return '0';
         }
      }
      DEBUG("nextch: EOF in a quote\n");
   }
   return c;
}

/*
 * Get the next symbol from the file, skipping blanks.
 * Return 0 if OK, -1 for EOF.
 * Also collapses everything between { and }
 */

int getsym(buf, f)
char *buf; FILE *f;
{
   register int c;
   int inbrack = 0;

   DEBUG("in getsym\n");
   c = glastc;
   while ((c > 0) && isspace(c)) {
      c = nextch(f);
   }
   DEBUG("getsym: spaces skipped\n");
   if (c < 0) {
      DEBUG("EOF read in getsym\n");
      return -1;
   }
   if (c == '{') {
      inbrack = 1;
      DEBUG("getsym: in bracket\n");
      while (inbrack) {
         c = nextch(f);
         if (c < 0) {
            DEBUG("getsym: EOF seen in bracket loop\n");
            glastc = c;
            return c;
         }
         if (c == '{') inbrack++;
         else if (c == '}') inbrack--;
      }
      (void)strcpy(buf, "{}");
      glastc = nextch(f);
      DEBUG("getsym: out of in bracket loop\n");
      return 0;
   }
   if (!ISCSYM(c)) {
      *buf++ = c;
      *buf = 0;
      glastc = nextch(f);
      DEBUG("getsym: returning special symbol\n");
      return 0;
   }
   while (ISCSYM(c)) {
      *buf++ = c;
      c = nextch(f);
   }
   *buf = 0;
   glastc = c;
   DEBUG("getsym: returning word\n");
   return 0;
}

/*
 * skipit: skip until a ";" or the end of a function declaration is seen
 */

int skipit(buf, f)
char *buf; FILE *f;
{
   int i;

   do {
      DEBUG("in skipit loop\n");
      i = getsym(buf, f);
      if (i < 0) return i;
   } while (*buf != ';' && *buf != '{');

   return 0;
}

/*
 * Get a parameter list; when this is called the next symbol in line
 * should be the first thing in the list.
 */

Word *getparamlist(f)
FILE *f;
{
   static Word *pname[MAXPARAM]; /* parameter names */
   Word   *tlist,      /* type name */
   *plist;      /* temporary */
   int     np = 0;      /* number of parameters */
   int     typed[MAXPARAM];  /* parameter has been given a type */
   int   tlistdone;   /* finished finding the type name */
   int   sawsomething;
   int     i;
   int   inparen = 0;
   char buf[80];

   DEBUG("in getparamlist\n");
   for (i = 0; i < MAXPARAM; i++)
      typed[i] = 0;

   plist = word_alloc("");

   /* first, get the stuff inside brackets (if anything) */

   sawsomething = 0;   /* gets set nonzero when we see an arg */
   for (;;) {
      if (getsym(buf, f) < 0) return NULL;
      if (*buf == ')' && (--inparen < 0)) {
         if (sawsomething) {   /* if we've seen an arg */
            pname[np] = plist;
            plist = word_alloc("");
            np++;
         }
         break;
      }
      if (*buf == ';') {   /* something weird */
         return ABORTED;
      }
      sawsomething = 1;   /* there's something in the arg. list */
      if (*buf == ',' && inparen == 0) {
         pname[np] = plist;
         plist = word_alloc("");
         np++;
      }
      else {
         addword(plist, buf);
         if (*buf == '(') inparen++;
      }
   }

   /* next, get the declarations after the function header */

   inparen = 0;

   tlist = word_alloc("");
   plist = word_alloc("");
   tlistdone = 0;
   sawsomething = 0;
   for(;;) {
      if (getsym(buf, f) < 0) return NULL;

      /* handle a list like "int x,y,z" */
      if (*buf == ',' && !inparen) {
         if (!sawsomething)
            return NULL;
         for (i = 0; i < np; i++) {
            if (!typed[i] && foundin(plist, pname[i])) {
               typed[i] = 1;
               word_free(pname[i]);
               pname[i] = word_append(tlist, plist);
               /* promote types */
               if ( dopromote ) typefixhack(pname[i]);
               break;
            }
         }
         if (!tlistdone) {
            tlist = typelist(plist);
            tlistdone = 1;
         }
         word_free(plist);
         plist = word_alloc("");
      }
      /* handle the end of a list */
      else if (*buf == ';') {
         if (!sawsomething)
            return ABORTED;
         for (i = 0; i < np; i++) {
            if (!typed[i] && foundin(plist, pname[i])) {
               typed[i] = 1;
               word_free(pname[i]);
               pname[i] = word_append(tlist, plist);
               /* promote types */
               if ( dopromote ) typefixhack(pname[i]);
               break;
            }
         }
         tlistdone = 0;
         word_free(tlist); 
         word_free(plist);
         tlist = word_alloc("");
         plist = word_alloc("");
      }
      /* handle the  beginning of the function */
      else if (!strcmp(buf, "{}")) break;
      /* otherwise, throw the word into the list 
			    (except for "register") */
      else if (strcmp(buf, "register")) {
         sawsomething = 1;
         addword(plist, buf);
         if (*buf == '(') inparen++;
         if (*buf == ')') inparen--;
      }
   }

   /* Now take the info we have and build a prototype list */

   /* empty parameter list means "void" */
   if (np == 0)
      return word_alloc("void");

   plist = tlist = word_alloc("");
   for (i = 0; i < np; i++) {

      /* If no type provided, make it an "int" */
      if ( !(pname[i]->next) ||
          (!(pname[i]->next->next)&&strcmp(pname[i]->next->string,
	  "void"))) {
         addword(tlist, "int");
      }
      while (tlist->next) tlist = tlist->next;
      tlist->next = pname[i];
      if (i < np - 1)
         addword(tlist, ", ");
   }
   return plist;
}

/*
 * emit a function declaration. The attributes and name of the function
 * are in wlist; the parameters are in plist.
 */

void emit(wlist, plist, startline)
Word *wlist, *plist;
long  startline;
{
   Word *w;
   int count = 0;

   DEBUG("emit called\n");
   if (donum)
      (void)printf("/*%8ld */ ", startline);

   for (w = wlist; w; w = w->next) {
      if ( w->string[0] == ':' )
	 return; /* C++ member function detected */
      if (w->string[0])
         count ++;
   }

   if (count < 2)
      (void)printf("int ");

   for (w = wlist; w; w = w->next) {
      (void)printf("%s", w->string);
      if (ISCSYM(w->string[0]))
         (void)printf(" ");
   }
   if (docond)
      (void)printf("PROTO((");
   else
      (void)printf("( ");
   for (w = plist; w; w = w->next) {
      (void)printf("%s", w->string);
      if (ISCSYM(w->string[0]))
         (void)printf(" ");
   }
   if (docond)
      (void)printf("));\n");
   else
      (void)printf(");\n");
}

/*
 * get all the function declarations
 */

void getdecl(f)
FILE *f;
{
   Word *plist, *wlist = NULL;
   char buf[80];
   int sawsomething;
   long startline;      /* line where declaration started */
   int oktoprint;
again:
   word_free(wlist);
   wlist = word_alloc("");
   sawsomething = 0;
   oktoprint = 1;

   for(;;) {
      DEBUG("main getdecl loop\n");
      if (getsym(buf,f) < 0) {
         DEBUG("EOF in getdecl loop\n");
         return;
      }
      /* try to guess when a declaration is not an external 
		    function definition */
      if (!strcmp(buf, ",") || !strcmp(buf, "{}") ||
          !strcmp(buf, "=") || !strcmp(buf, "typedef") ||
          !strcmp(buf, "extern")) {
         (void)skipit(buf, f);
         goto again;
      }
      if (!dostatic && !strcmp(buf, "static")) {
         oktoprint = 0;
      }
      /* for the benefit of compilers that allow "inline" 
		  declarations */
      if (!strcmp(buf, "inline") && !sawsomething)
         continue;
      if (!strcmp(buf, ";")) goto again;

      /* A left parenthesis *might* indicate a function definition */
      if (!strcmp(buf, "(")) {
         startline = linenum;
         if (!sawsomething || !(plist = getparamlist(f))) {
            (void)skipit(buf, f);
            goto again;
         }
         if (plist == ABORTED)
            goto again;

         /* It seems to have been what we wanted */
         if (oktoprint)
            emit(wlist, plist, startline);
         word_free(plist);
         goto again;
      }
      addword(wlist, buf);
      sawsomething = 1;
   }
}

void main(argc, argv)
int argc; 
char *argv[];
{
   FILE *f;
   char *iobuf;
   char *title = "UNSPECIFIEDHEADERTITLE";
   extern void Usage();
   int opch; extern int Optind; extern char *Optarg;


   while ( ( opch = Getopt(argc,argv,"h:snpPD") ) != -1 )
      switch ( opch ) {
      case 'h'   :
	 (void)strcpy(title, Optarg);
	 break;
      case 's'   :
	 dostatic = true;
	 break;
      case 'n'   :
	 donum = true;
	 break;
      case 'p'   :
	 docond = true;
	 break;
      case 'P'   :
	 dopromote = false;
	 break;
      case 'D'   :
	 dodebug = true;
	 break;
      default    :
	 Usage(argv[0]);
	 exit(0);
      } /* ensw */

   iobuf = malloc((unsigned)NEWBUFSIZ);

   (void)printf("#ifndef %s\n", title);
   (void)printf("#define %s\n", title);

   if (docond) {
      (void)printf("#ifdef __STDC__\n");
      (void)printf("# define\tPROTO(s) s\n");
      (void)printf("#else\n");
      (void)printf("# define PROTO(s) ()\n");
      (void)printf("#endif\n\n");
   }
   if ( Optind > argc )
      getdecl(stdin);
   else
      for ( ; Optind < argc; ++Optind ) {
         DEBUG("trying a new file\n");
         if ( ( f = fopen(argv[Optind],"r") ) == (FILE *)0 ) {
	    (void)fprintf(stderr,"%s: cannot open %s\n",
	       argv[0], argv[Optind]);
	    continue;
         }

         /* do the file operations here */
	 /*
         if (iobuf)
            (void)setvbuf(f, iobuf, _IOFBF, NEWBUFSIZ); /* */
         if (dohead)
            (void)printf("\n/* %s */\n", argv[Optind]);
         linenum = 1;
         newline_seen = true;
         glastc = ' ';
         DEBUG("calling getdecl\n");
         getdecl(f);
         DEBUG("back from getdecl\n");
         DEBUG("back from fclose\n");

         (void)fclose(f);
      } /* enfo */

   if (docond) {
      (void)printf("\n#undef PROTO\n");   /* clean up namespace */
   }
   (void)printf("\n#endif\n");
   exit(EXIT_SUCCESS);
}

void Usage(progname)
char *progname;
{
   (void)fprintf(stderr,"Usage: %s [options][files ...]\n",progname);
   (void)fprintf(stderr,
      "\t-n: put line numbers of declarations as comments\n");
   (void)fprintf(stderr,
      "\t-s: include declarations for static functions\n");
   (void)fprintf(stderr,
      "\t-p: make header files readable by non-ANSI compilers\n");
   (void)fprintf(stderr,
      "\t-P: don't promote any sc-specifiers\n");
   (void)fprintf(stderr,
      "\t-h HeaderTitle: use HeaderTitle to title the header file\n");
   (void)fprintf(stderr,
      "\t-D: operate on debug mode\n");
   exit(EXIT_FAILURE);
}



#ifndef lint
static char *scid = "s. manoharan edinburgh univ";
#endif

#include<stdio.h>

char *Optarg; int Optind;


int Getopt(argc,argv,options)
int argc; char **argv; char *options;
{
   char *str, *ptr; char opch; char *Strchr();
   static int flag = 0; static int Argc; static char **Argv;

   if (  flag == 0 ) {
      Argc = argc; Argv = argv; flag = 1; Optind = 1;
   }

   if ( Argc <= 1 ) return -1;

   if ( --Argc >= 1 ) {
      str = *++Argv;
      if (*str != '-') return -1; /* argument is not an option   */
      else {  /* argument is an option */
	 if ( ( ptr = Strchr(options, opch = *++str) ) != (char *) 0 ) {
	    ++Optind;
            Optarg = ++str; /* point to rest of argument if any  */
            if ( ( *++ptr == ':' ) && ( *Optarg == '\0' ) ) {
               if (--Argc <= 0) return '?';
               Optarg = *++Argv; ++Optind;
            }
	    return opch;
         }
	 else if ( opch == '-' ) { /* end of options */
	    ++Optind;
	    return -1;
	 }
         else return '?';
      }
   }
   return 0; /* this will never be reached */

} /* EnGetopt */

char *Strchr(s,c)
char *s; char c;
{
   while ( *s != '\0' ) {
      if ( *s == c ) return s;
      else ++s;
   }
   return ( (char *) 0 );
} /* EnStrchr */
@//E*O*F mkproto.c//
chmod u=rw,g=,o= mkproto.c
 
echo x - mkproto.h
sed 's/^@//' > "mkproto.h" <<'@//E*O*F mkproto.h//'
#ifdef __STDC__
# define	PROTO(s) s
#else
# define PROTO(s) ()
#endif


/* mkproto.c */
Word *word_alloc PROTO((char *s ));
void word_free PROTO((Word *w ));
int List_len PROTO((Word *w ));
Word *word_append PROTO((Word *w1 , Word *w2 ));
int foundin PROTO((Word *w1 , Word *w2 ));
void addword PROTO((Word *w , char *s ));
Word *typelist PROTO((Word *p ));
void typefixhack PROTO((Word *w ));
int ngetc PROTO((FILE *f ));
int fnextch PROTO((FILE *f ));
int nextch PROTO((FILE *f ));
int getsym PROTO((char *buf , FILE *f ));
int skipit PROTO((char *buf , FILE *f ));
Word *getparamlist PROTO((FILE *f ));
void emit PROTO((Word *wlist , Word *plist , long startline ));
void getdecl PROTO((FILE *f ));
void main PROTO((int argc , char **argv ));
void Usage PROTO((void ));

#undef PROTO
@//E*O*F mkproto.h//
chmod u=rw,g=,o= mkproto.h
 
exit 0
______________________ "And miles to go before I sleep." ______________________
 Brad Appleton           brad@ssd.csd.harris.com       Harris Computer Systems
                             uunet!hcx1!brad           Fort Lauderdale, FL USA
~~~~~~~~~~~~~~~~~~~~ Disclaimer: I said it, not my company! ~~~~~~~~~~~~~~~~~~~

utility@quiche.cs.mcgill.ca (Ronald BODKIN) (11/24/90)

In article <1906@necisa.ho.necisa.oz> boyd@necisa.ho.necisa.oz (Boyd Roberts) writes:
>Right on!  The function prototypes are just stupid.  I shouldn't have to go:

>    extern gore	*good(const char *vomit);

>to get the functionality of type checking of function arguments.
>The compiler and loader should do it.
	However, one prototype will allow you to automatically coerce
things like ints to longs/floats/doubles or "near" pointers to "far"
pointers, etc.  Also, it is much faster to catch a prototype problem
before linking a large application.
	Ron