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