Need help on VARARGS and RCS

Guy Harris guy at sun.uucp
Thu Apr 17 17:06:53 AEST 1986


> 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 at sun.arpa



More information about the Comp.unix.wizards mailing list