[net.unix] Need help on VARARGS and RCS

chinn@butler.UUCP (David Chinn) (04/15/86)

/****************/

We have a copy of RCS lying around which which I cannot
get to work.  It core dumps when you do a  'ci f.c'.
Using dbx I have been able to determine that the coredump
occurs during a routine called "diagnose", invoked
as follows:

        diagnose("%s  <--  %s", RCSfilename,workfilename);

where RCSfilename and workfilename are both char *.

The routine diagnose looks like:

diagnose(va_alist)
va_dcl
{
        if (!quietflag) {
                fprintf(stderr,va_alist);
                putc('\n',stderr);
        }
}

It is in the file "rcslex.c", and crashes when executing the fprintf.

The question is, how is this supposed to work? I found a mention
of 'va_alist' under varargs, but the usage example grabs the 
arguments in the called routine.  My problem seems to occur with
va_list being passed straight through to fprintf.   Do the printf 
family of routines have to be built special to handle varargs?
Is there any way to get around this?  I have no sources.

Unfortunately, I am not good enough with dbx to discern what va_alist
thinks it is pointing to when it is passed to fprintf.

We are running ULTRIX 1.0 on a VAX-11/750.

Any information or pointers would be gladly appreciated.

					thanks in advance

    ... uw-beaver                                david m chinn
	   !{tikal,teltone}                      box 639
	       !dataio!butler!chinn     	 redmond,  wash 98073

guy@sun.uucp (Guy Harris) (04/17/86)

> We have a copy of RCS lying around which which I cannot
> get to work.  It core dumps when you do a  'ci f.c'.
> Using dbx I have been able to determine that the coredump
> occurs during a routine called "diagnose" ...
> 
> The routine diagnose looks like:
> 
> diagnose(va_alist)
> va_dcl
> {
>         if (!quietflag) {
>                 fprintf(stderr,va_alist);

The version of RCS that I've always seen is more like

	diagnose(e,e1,e2,e3...)
	char *e, *e1, *e2, *e3...
	{
		if (!quietflag) {
			fprintf(stderr,e,e1,e2,e3,...);

It looks like the version you have was hacked by some well-intentioned
person who didn't really understand the "varargs" package.  "va_alist" is,
in general, a magic cookie, to be handed only to routines which expect it;
"fprintf" is not one of them.  In many implementations, the address of
"va_alist" is also the address of the first argument to the routine (or, if
you declare a routine like "foo(fixed_arg1, fixed_arg2, va_alist)", for
instance, it will be the address of the third argument, the first two
arguments being required arguments of particular types).  (Note, however,
that there is NO guarantee that this is the case; if an implementation gives
you the "varargs" stuff, you are supposed to use it in the way the manual
says, and no other way.)

As such, "diagnose" was, in effect, passing the value of its first argument
as the second argument to "fprintf".  It was *not*, however, passing its
entire argument list through, which is what was intended; the control string
was calling for two arguments, but they weren't being passed, so it used
whatever garbage happened to be on the stack - it's not surprising it died.

You can probably get away with

	/*
	 * Note that "diagnose" has a "printf"-like calling sequence.
	 * This means that it must be called with at least one argument;
	 * that argument is a pointer to "char", and points to the control
	 * string.
	 */
	diagnose(format, va_alist)
	char *format;
	va_dcl
	{
		va_list args;

		va_start(args);
		if (!quietflag) {
			_doprnt(format, args, stderr);
			putc('\n', stderr);
		}
		va_end(args);
	}

(Yes, I realize this isn't *exactly* the way it's used in the manual page.
I believe you can have the "variable argument list starts here" argument at
the end of an arbitrarily-long argument list, not just as the sole argument.
The ANSI C draft indicates that this is the case, which is nice considering
they support the notion of a routine with an arbitrary set of "fixed"
arguments followed by a variable-length list of arguments.)

(I also know that "_doprnt" is documented as taking a second argument of
type "va_list *"; the documentation lies.  It is of type "va_list"; a
"va_list" is supposed to be a *pointer* to an element of an argument list,
so a "va_list *" isn't a very interesting object.)

4.2BSD documents "_doprnt"; many other implementations of the standard I/O
library also have such a routine (VAX System V, for one), but there is no
guarantee that a given standard I/O library implementation, even under UNIX,
has one.  System V, Release 2 documents a similar routine called "vfprintf";
unfortunately, this isn't guaranteed to be present, either; System V,
Release 1 didn't have it (although System III had it, although it wasn't
documented).  The current ANSI C draft specifies a System V-style
"vfprintf", so at some point in the future you may be more likely to find
it.  It's fairly straightforward to implement on machines like the VAX and
the 68K family; it would be useful if Berkeley put it into 4.3BSD and
undocumented "_doprnt" to discourage people from using it.  It would be even
more useful if there was a "vscanf" family to go along with it....)
-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.arpa

chris@umcp-cs.UUCP (Chris Torek) (04/17/86)

In article <198@butler.UUCP> chinn@butler.UUCP (David Chinn) writes:
>We have a copy of RCS [which] core dumps when you do a  'ci f.c'.
>[...] the coredump occurs during a routine called "diagnose",
>[which] looks like:
>
>diagnose(va_alist)
>va_dcl
>{
>        if (!quietflag) {
>                fprintf(stderr,va_alist);
>                putc('\n',stderr);
>        }
>}
>
>The question is, how is this supposed to work? I found a mention
>of 'va_alist' under varargs, but the usage example grabs the 
>arguments in the called routine.

Passing the list to fprintf (or printf) is incorrect usage.  In
System V, there is a `vfprintf', which does indeed take a variable
argument list.

>We are running ULTRIX 1.0 on a VAX-11/750.

On a Vax running 4.2BSD-equivalent code (Ultrix) the following
version of `diagnose' will work.  Please note that this is *not*
portable.

diagnose(fmt, arg)
	char *fmt;
{

	if (!quietflag) {
		_doprnt(fmt, &arg, stderr);	/* magic */
		(void) putc('\n', stderr);
	}
}
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1415)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu