rickf@pnet02.cts.com (Rick Flower) (10/15/88)
I've been trying to get the following code to work.. I've tried just about everything that I could think of (along the lines of modifications). All I'm trying to do is write a different printf routine to route information through a window text printing routine... If anyone can help me, let me know.. ----------------------------------------------------------------------------- void Test(fmt,args) char *fmt; unsigned *args; { char buff[129]; sprintf(buff,fmt,args); printf("%s\n",buff); } main() { Test("%d %d %d %d",10,20,30,40); } ------------------------------------------------------------------------------ I would think that it would just insert the 10 and 20 and 30 and 40 in the appropriate places in the fmt line.. but it only does the 1st two numbers.. the other two are garbage values.. Thanks in advance.. =============================================================================== I Thought So... UUCP: {ames!elroy, <backbone>}!gryphon!pnet02!rickf INET: rickf@pnet02.cts.com ===============================================================================
chris@mimsy.UUCP (Chris Torek) (10/16/88)
In article <7778@gryphon.CTS.COM> rickf@pnet02.cts.com (Rick Flower) writes: >void Test(fmt,args) >char *fmt; >unsigned *args; >{ >char buff[129]; > > sprintf(buff,fmt,args); > printf("%s\n",buff); >} > >main() >{ > Test("%d %d %d %d",10,20,30,40); >} > I would think that it would just insert the 10 and 20 and 30 and >40 in the appropriate places in the fmt line.. but it only does the 1st two >numbers.. the other two are garbage values.. It is amazing that you get even the first *two* numbers (unless perhaps you are on a 16-bit-int 32-bit-pointer machine, such as a PDP-11 or IBM PC using large model). *Anyone* should be able to give you at least two reasons why it does not work: imprimis, you told sprintf to print four `int's (via %d), yet you passed it one pointer to unsigned; and secundus, Test() takes two arguments, the second being type `unsigned *', yet you passed five, the second being type `int'. You may not arbitrarily ignore either the types of arguments or the number of arguments to any function, save in one case: if the function being called is variadic. [Now that you have been thoroughly chastised :-) ...] The question now is how to write it so that it *will* work. Since what you want is a variadic function that acts much like printf, you must first declare a variadic function. There are two `standard' ways to do this, one the de facto standard, the other from the draft proposed ANSI standard for C. The two are not only different, they are also quite thoroughly incompatible---a mistake, as this was not really necessary (despite the cries of anguish from certain compiler-writers). Method 1. De facto standard. #include <stdio.h> #include <varargs.h> int my_printf(va_alist) va_dcl /* N.B.: no semicolon */ { int ret; char *fmt; va_list ap; char buf[129]; /* rather small, but might perhaps suffice */ va_start(ap); fmt = va_arg(ap, char *); ---> ret = <some_call>(buf, fmt, <some_args>); va_end(ap); <do something with buf> return (ret); } Everything except the line marked by the arrow is setup mechanics. The line with the arrow is the key to invoking *printf properly. So what goes in the <call> and <args> fields? ret = vsprintf(buf, fmt, ap); vsprintf is a version of sprintf that takes a `va_list' rather than an actual parameter list. It should now be obvious that sprintf itself could be written as int sprintf(va_alist) va_dcl { int ret; char *fmt, *buf; va_list ap; va_start(ap); buf = va_arg(ap, char *); fmt = va_arg(ap, char *); ret = vsprintf(buf, fmt, ap); va_end(ap); return (ret); } There are three assumptions about your system here, and one obvious one. The obvious one is that you have <varargs.h> and can use the de facto standard varargs mechanism. The other assumptions are that you have vsprintf, and that you have a post-V7/4.3BSD sprintf that returns `int' rather than `char *'. Method 2: The dpANS standard. Method 2 is just like method one except that some of the names are changed, and some arguments are permitted and others required: #include <stdio.h> #include <stdarg.h> int my_printf(char *fmt, ...) /* the `...'s are part of the syntax */ { int ret; va_list ap; char buf[128]; /* same comment as before */ va_start(ap, fmt); ret = vsprintf(buf, fmt, ap); va_end(ap); <do something with buf> return (ret); } The `fmt' argument is both permitted and required: `...' may only appear after `,', and va_start must name the same parameter that appeared just before the `...'. If there were more fixed parameters, those, too, could be listed (so long as the va_start names the one closest to the three dots). -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
vch@attibr.UUCP (Vincent C. Hatem) (10/18/88)
In article <7778@gryphon.CTS.COM%, rickf@pnet02.cts.com (Rick Flower) writes:
% I've been trying to get the following code to work.. I've tried just about
% everything that I could think of (along the lines of modifications). All I'm
% trying to do is write a different printf routine to route information through
% a window text printing routine... If anyone can help me, let me know..
%
% -----------------------------------------------------------------------------
% void Test(fmt,args)
% char *fmt;
% unsigned *args;
% {
% char buff[129];
%
% sprintf(buff,fmt,args);
% printf("%s\n",buff);
% }
%
% main()
% {
% Test("%d %d %d %d",10,20,30,40);
% }
You aren't passing a POINTER to the arguments, as you seem to think you are.
this should be (if I'm not mistaken... i haven't tried this lately)
unsigned *args;
sprintf(buff,fmt,&args);
^
!
-Vince
--
Vincent C. Hatem | att ---->\ (available from any
AT&T International | ulysses ->\ Action Central site)
International Operations Technical Support | bellcore ->\
1200 Mt Kemble Ave, Basking Ridge, NJ 07920 | ihnp4 ----->\__ !attibr!vch
thomas@uplog.se (Thomas Hameenaho) (10/18/88)
In article <7778@gryphon.CTS.COM> rickf@pnet02.cts.com (Rick Flower) writes: >I've been trying to get the following code to work.. I've tried just about >everything that I could think of (along the lines of modifications). All I'm >trying to do is write a different printf routine to route information through >a window text printing routine... If anyone can help me, let me know.. > >----------------------------------------------------------------------------- >void Test(fmt,args) >char *fmt; >unsigned *args; >{ >char buff[129]; > > sprintf(buff,fmt,args); > printf("%s\n",buff); >} > >main() >{ > Test("%d %d %d %d",10,20,30,40); >} > >------------------------------------------------------------------------------ The problem is that the stack doesn't look the way you would expect on entry to sprintf(). For a normal 68k machine the stack layout would be: +---------------+ fp -> | frame pointer |--+ +---------------+ | | ret to Test | | +---------------+ | | ptr to buff | | +---------------+ | | ptr to fmt str| | +---------------+ | | 10 | | +---------------+ | | | | . buff (132) . | | | | +---------------+ | +--| frame pointer |<-+ | +---------------+ | | ret to main | | +---------------+ | | ptr to fmt str| | +---------------+ | | 10 | | +---------------+ | | 20 | | +---------------+ | | 30 | | +---------------+ | | 40 | | +---------------+ +->| frame pointer | +---------------+ | ret from main | +---------------+ As you can see the arguments to sprintf are just the three first. If you want to do something like what I think you are aiming at you have to do it some other way. Possibly manipulating the stackpointer in assembler! -- Real life: Thomas Hameenaho Email: thomas@uplog.{se,uucp} Snail mail: TeleLOGIC Uppsala AB Phone: +46 18 189406 Box 1218 Fax: +46 18 132039 S - 751 42 Uppsala, Sweden
mikpe@mina.liu.se (Mikael Pettersson) (10/19/88)
In article <7778@gryphon.CTS.COM> rickf@pnet02.cts.com (Rick Flower) writes: >I've been trying to get the following code to work.. > [Rick wants to write his own printf(3s) clone] > [buggy def of Test() omitted] >main() >{ > Test("%d %d %d %d",10,20,30,40); >} The answer to your problem is simple: use varargs(). With varargs, you can either parse the format string just like printf(3s) does, or you can be a little lazy and let vsprintf(3s) do the formatting for you before sending the result to whoever wants it. I'll give you an example: ---snip-snip-snip--- #include <stdio.h> #include <varargs.h> void Test(va_alist) va_dcl /* n.b. NO ; */ { va_list ap; char *fmt, buf[BUFSIZ]; va_start(ap); /* init */ fmt = va_arg(ap, char *); /* get 1st arg: a char * */ vsprintf(buf, fmt, ap); /* do the formatting */ fputs(buf, stdio); /* dump it on stdout */ va_end(ap); /* cleanup */ } ---snip-snip-snip--- Check the man pages for more detail. Any decent C book (like H&S-2) will probably also have some examples. In <315@uplog.se> thomas@uplog.UUCP (Thomas Hameenaho) responds: >The problem is that the stack doesn't look the way you would expect on entry >to sprintf(). > > [ lots of machine AND compiler specific detail omitted ] >... >If you want to do something like what I think you are aiming at you have to >do it some other way. Possibly manipulating the stackpointer in assembler! Noooo! The great advantage in using varargs(3) is PORTABILITY. It's guaranteed to work in any conforming ANSI C implementation (where it's called <stdarg.h>) and it will work in virtually all existing UNIX C compilers as well. Assumptions about stack layout, parameter passing and order of evaluation will only lead to bugprone and non-portable code. /Mike -- Mikael Pettersson ! Internet:mpe@ida.liu.se Dept of Comp & Info Science ! UUCP: mpe@liuida.uucp -or- University of Linkoping ! {mcvax,munnari,uunet}!enea!liuida!mpe Sweden ! ARPA: mpe%ida.liu.se@uunet.uu.net
thomas@uplog.se (Thomas Hameenaho) (10/19/88)
In article <982@mina.liu.se> mikpe@mina.liu.se (Mikael Pettersson) writes: >Noooo! >The great advantage in using varargs(3) is PORTABILITY. It's guaranteed >to work in any conforming ANSI C implementation (where it's called <stdarg.h>) >and it will work in virtually all existing UNIX C compilers as well. >Assumptions about stack layout, parameter passing and order of evaluation >will only lead to bugprone and non-portable code. I agree fully with you. However not THAT many systems have v{sf}printf() (BSD systems doesn't, at least not 4.3) and my response was an attempt to explain what was wrong with the code. I would definately NOT try to manipulate the sp. -- Real life: Thomas Hameenaho Email: thomas@uplog.{se,uucp} Snail mail: TeleLOGIC Uppsala AB Phone: +46 18 189406 Box 1218 Fax: +46 18 132039 S - 751 42 Uppsala, Sweden
chris@mimsy.UUCP (Chris Torek) (10/21/88)
>In article <982@mina.liu.se> mikpe@mina.liu.se (Mikael Pettersson) writes: >>The great advantage in using varargs(3) is PORTABILITY. ... In article <316@uplog.se> thomas@uplog.se (Thomas Hameenaho) writes: >I agree fully with you. However not THAT many systems have v{sf}printf() >(BSD systems doesn't, at least not 4.3) and my response was an attempt >to explain what was wrong with the code. 4.3BSD-tahoe does have v*printf. Here they are. Install in src/lib/libc/stdio. Note that vsprintf has the same bug that sprintf has (it will scribble on a random fd if you print more than 32767 characters). ---------------------------------vsprintf------------------------------ #include <stdio.h> #include <varargs.h> int vsprintf(str, fmt, ap) char *str, *fmt; va_list ap; { FILE f; int len; f._flag = _IOWRT+_IOSTRG; f._ptr = str; f._cnt = 32767; len = _doprnt(fmt, ap, &f); *f._ptr = 0; return (len); } ---------------------------------vfprintf------------------------------ #include <stdio.h> #include <varargs.h> int vfprintf(iop, fmt, ap) FILE *iop; char *fmt; va_list ap; { int len; len = _doprnt(fmt, ap, &f); return (ferror(iop) ? EOF : len); } ---------------------------------vprintf------------------------------- #include <stdio.h> #include <varargs.h> int vprintf(fmt, ap) char *fmt; va_list ap; { int len; len = _doprnt(fmt, ap, stdout); return (ferror(stdout) ? EOF : len); } -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
henry@utzoo.uucp (Henry Spencer) (10/23/88)
In article <982@mina.liu.se> mikpe@mina.liu.se (Mikael Pettersson) writes: >The great advantage in using varargs(3) is PORTABILITY. It's guaranteed >to work in any conforming ANSI C implementation (where it's called <stdarg.h>) Beware: varargs.h and stdarg.h are not quite the same. Unfortunately. -- The meek can have the Earth; | Henry Spencer at U of Toronto Zoology the rest of us have other plans.|uunet!attcan!utzoo!henry henry@zoo.toronto.edu