Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > a printf implementation

Reply
Thread Tools

a printf implementation

 
 
Jordan Abel
Guest
Posts: n/a
 
      12-07-2005
I have written this function and was wondering if anyone else could
point out if there's anything wrong with it. The purpose is to
substitute for printf when in a situation where low-level [beyond the
scope of this newsgroup] I/O has to be used for whatever reason, and
memory allocation cannot be used [for risk of it failing or whatever
else] - mainly it's become for me an exercise in interpreting
printf-like format strings, and i thought i'd post it here to see if
anyone can point out any bugs in it - i.e. anything that would cause
undefined behavior, and any valid format strings it might fail to
interpret [there's functionality it doesn't implement, but it should at
least pop off all the same arguments for any given valid format string
that a real printf function will.]

[is this the appropriate newsgroup for this kind of thing?]
______

#include <limits.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wchar.h>

int write(int fd,char*buf,size_t len);

#if 0 /*if your system doesn't have this platform-specific function*/
static FILE * fdtab[] = { stdin, stdout, stderr, 0, 0, 0, 0, 0, 0, 0 };
int write(int fd,char *buf,size_t len) {
if(fd > 0 || !fdtab[fd]) {
return -1;
}
return fwrite(buf,1,len,fdtab[fd]);
}
#endif

#define LENGTH 64

static char buf[LENGTH];
static int pos;
static char const udigit[16] = "0123456789ABCDEF";
static char const ldigit[16] = "0123456789abcdef";
static const char *digits;

static void flush(int fd) {
write(fd,buf,pos);
pos=0;
}

static inline void eputc(int fd, int c) {
buf[pos++]=c;
if(pos==LENGTH) flush(fd);
}

static inline void eputwc(int fd, wint_t c) {
int x = wctob(c); eputc(fd,x<0?'?');
}

static void eputws(int fd, wchar_t *s) {
while(*s) { eputwc(fd,*s); s++; }
}

static void eputs(int fd, char *s) {
while(*s) { eputc(fd,*s); s++; }
}

static char * uimaxtoa(uintmax_t n, int base) {
static char buf[CHAR_BIT*sizeof(n)+2];
char *endptr=buf+(sizeof buf)-2;
do {
*endptr--=digits[n%base];
n/=base;
} while(n);
return endptr+1;
}

static char * pcvt(void *p) {
static char buf[sizeof(void *)*CHAR_BIT];
/* well, we can always hope this will fit - anyone know a more
* portable way to implement %p? */
sprintf(buf,"%p",p);
return buf;
}

static char * tcvt(time_t t) {
static char buf[] = "YYYY-MM-DD HH:MM:SS";
strftime(buf,sizeof buf,"%Y-%m-%d %H:%M:%S",gmtime(&t));
return buf;
}

void eprintf(int fd, const char *fmt, ...) {
int c;
va_list ap;
int islong=0;
uintmax_t uarg;
intmax_t iarg;

va_start(ap,fmt);

loop:
for(;*fmt&&*fmt!='%';fmt++)
eputc(fd,*fmt);
if(!*fmt) goto end;
if(*fmt++ == '%') {
islong=0;
digits=ldigit;
top:
c = *fmt++;
retry:
switch(c) {
case 'c':
if(islong) eputwc(fd,va_arg(ap,wint_t));
else eputc(fd,va_arg(ap,int));
break;
/* obsolete, but it doesn't conflict with anything. */
case 'D': case 'O': case 'U': case 'C': case 'S':
islong=1; c=tolower(c); goto retry;
case 'A': case 'E': case 'F': case 'G': case 'X':
digits=udigit; c = tolower(c); goto retry;
case 'd': case 'i':
switch(islong) {
case 0: iarg=va_arg(ap,int); break;
case 1: iarg=va_arg(ap,long int); break;
case 2: iarg=va_arg(ap,long long int); break;
// this is wrong on non-twos-complement systems
case 3: iarg=va_arg(ap,size_t); break;
case 4: iarg=va_arg(ap,ptrdiff_t); break;
case 5: iarg=va_arg(ap,intmax_t); break;
}
if(iarg<0) {
eputc(fd,'-');
uarg=-iarg;
} else {
uarg=iarg;
}
goto cvt;
case 'o': case 'u': case 'x':
switch(islong) {
case 0: uarg=va_arg(ap,unsigned); break;
case 1: uarg=va_arg(ap,long unsigned); break;
case 2: uarg=va_arg(ap,long long unsigned); break;
case 3: uarg=va_arg(ap,size_t); break;
// this is wrong on non-twos-complement systems.
case 4: uarg=va_arg(ap,ptrdiff_t); break;
case 5: uarg=va_arg(ap,uintmax_t); break;
}
cvt:
eputs(fd,uimaxtoa(uarg,c=='o'?8c=='x'?16:10)));
break;
case 'p':
eputs(fd,pcvt(va_arg(ap,void*))); break;
case 's':
if(islong)eputws(fd,va_arg(ap,wchar_t *));
else eputs(fd,va_arg(ap,char *));
break;
/* my own extensions, they print an int representing an errno value and
* a time_t, respectively. %! calls strerror(). */
case '!':
eputs(fd,strerror(va_arg(ap,int))); break;
case 'T':
eputs(fd,tcvt(va_arg(ap,time_t))); break;
case 0:
goto end;
default:
eputs(fd,"%?"); /*fall*/
case '%':
write(fd,&c,1); break;
case 'l': islong++; if(islong > 2) islong=2; goto top;
case 'j': islong=5; goto top;
case 't': islong=4; goto top;
case 'z': islong=3; goto top;
// here's the stuff we don't implement
case 'n':
switch(islong) {
case 0: (void)va_arg(ap,int *); break;
case 1: (void)va_arg(ap,long *); break;
case 2: (void)va_arg(ap,long long *); break;
case 3: (void)va_arg(ap,size_t *); break;
// this is wrong on non-twos-complement systems.
case 4: (void)va_arg(ap,ptrdiff_t *); break;
case 5: (void)va_arg(ap,intmax_t *); break;
} break;
case '*':
(void)va_arg(ap,int);
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '.': case '#': case '+': case '-': case ' ':
case '\'':
goto top;
case '$':
/* this, i can't even pretend to support - eh, well, it's a posix thing,
* rather than a C thing, anyway. */
eputs(fd,"%$?????\n");
flush(fd);
return;
/* I don't feel like messing with floating point right now */
case 'L':
islong=2; goto top;
case 'a': case 'e': case 'f': case 'g':
if(islong == 2) (void)va_arg(ap, long double);
else if(islong == 2) (void)va_arg(ap, double);
eputs(fd,"%FLT?");
break;
}
}
goto loop;
end:
flush(fd);
}
 
Reply With Quote
 
 
 
 
pete
Guest
Posts: n/a
 
      12-07-2005
Jordan Abel wrote:

> va_start(ap,fmt);


There should probably be a
va_end(ap);
somewhere.

--
pete
 
Reply With Quote
 
 
 
 
pete
Guest
Posts: n/a
 
      12-07-2005
Jordan Abel wrote:

> int write(int fd,char*buf,size_t len);


> int c;


> write(fd,&c,1);


That should be

write(fd, (char *)&c, 1);

--
pete
 
Reply With Quote
 
pete
Guest
Posts: n/a
 
      12-07-2005
pete wrote:
>
> Jordan Abel wrote:
>
> > int write(int fd,char*buf,size_t len);

>
> > int c;

>
> > write(fd,&c,1);

>
> That should be
>
> write(fd, (char *)&c, 1);


.... or maybe c should be type char.

--
pete
 
Reply With Quote
 
Kenneth Brody
Guest
Posts: n/a
 
      12-07-2005
pete wrote:
>
> Jordan Abel wrote:
>
> > int write(int fd,char*buf,size_t len);

>
> > int c;

>
> > write(fd,&c,1);

>
> That should be
>
> write(fd, (char *)&c, 1);


Given that c is of type "int", such a construct would work only on a
little-endian platform. (Or, I suppose, a platform where sizeof(int)==1.)

Better to make c a char instead, given that it's value is obtained by
dereferencing a char*.

--
+-------------------------+--------------------+-----------------------------+
| Kenneth J. Brody | www.hvcomputer.com | |
| kenbrody/at\spamcop.net | www.fptech.com | #include <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------------+
Don't e-mail me at: <(E-Mail Removed)>


 
Reply With Quote
 
Jordan Abel
Guest
Posts: n/a
 
      12-07-2005
On 2005-12-07, pete <(E-Mail Removed)> wrote:
> pete wrote:
>>
>> Jordan Abel wrote:
>>
>> > int write(int fd,char*buf,size_t len);

>>
>> > int c;

>>
>> > write(fd,&c,1);

>>
>> That should be
>>
>> write(fd, (char *)&c, 1);

>
> ... or maybe c should be type char.


Or maybe this is something i forgot to convert to eputc(fd,c);

I probably wouldn't have spotted that.
 
Reply With Quote
 
Ben Pfaff
Guest
Posts: n/a
 
      12-07-2005
Jordan Abel <(E-Mail Removed)> writes:

> I have written this function and was wondering if anyone else could
> point out if there's anything wrong with it. The purpose is to
> substitute for printf when in a situation where low-level [beyond the
> scope of this newsgroup] I/O has to be used for whatever reason, and
> memory allocation cannot be used [for risk of it failing or whatever
> else]


If you want to look at a simple, but more complete, version of
printf(), then you could look at the version that I wrote that is
incorporated in Pintos:
http://www.stanford.edu/class/cs140/.../pintos.tar.gz

The printf() implementation is in pintos/src/lib/stdio.c.
--
"To get the best out of this book, I strongly recommend that you read it."
--Richard Heathfield
 
Reply With Quote
 
Flash Gordon
Guest
Posts: n/a
 
      12-07-2005
Jordan Abel wrote:
> I have written this function and was wondering if anyone else could
> point out if there's anything wrong with it. The purpose is to
> substitute for printf when in a situation where low-level [beyond the
> scope of this newsgroup] I/O has to be used for whatever reason, and
> memory allocation cannot be used [for risk of it failing or whatever
> else] - mainly it's become for me an exercise in interpreting
> printf-like format strings, and i thought i'd post it here to see if
> anyone can point out any bugs in it - i.e. anything that would cause
> undefined behavior, and any valid format strings it might fail to
> interpret [there's functionality it doesn't implement, but it should at
> least pop off all the same arguments for any given valid format string
> that a real printf function will.]
>
> [is this the appropriate newsgroup for this kind of thing?]


Seems a reasonable place to me, especially as you have provided a
standard C implementation of the one otherwise non-standard function you
use.

Other valid reason for writing such a function iare implementing sprintf
on an embedded system without it or implementing an extended fprintf
like function.

> ______
>
> #include <limits.h>
> #include <stdarg.h>
> #include <stddef.h>
> #include <stdint.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <time.h>
> #include <wchar.h>
>
> int write(int fd,char*buf,size_t len);
>
> #if 0 /*if your system doesn't have this platform-specific function*/
> static FILE * fdtab[] = { stdin, stdout, stderr, 0, 0, 0, 0, 0, 0, 0 };
> int write(int fd,char *buf,size_t len) {
> if(fd > 0 || !fdtab[fd]) {
> return -1;


This looks wrong to me. Don't you mean
if (fd < 0 || fd >= ((sizeof fdtab)/sizeof *fdtab)) ||
!fdtab[fd]) {
return -1;
> }
> return fwrite(buf,1,len,fdtab[fd]);
> }
> #endif
>
> #define LENGTH 64
>
> static char buf[LENGTH];
> static int pos;
> static char const udigit[16] = "0123456789ABCDEF";
> static char const ldigit[16] = "0123456789abcdef";
> static const char *digits;


Obviously not going for thread safety.

I'm not sure about your logic on which functions you are specifying
inline for.

> static void flush(int fd) {
> write(fd,buf,pos);
> pos=0;
> }
>
> static inline void eputc(int fd, int c) {
> buf[pos++]=c;


buf is a char array, so if c is outside the range of char (say char is
signed on your implementation) you have a problem. See below...

> if(pos==LENGTH) flush(fd);
> }
>
> static inline void eputwc(int fd, wint_t c) {
> int x = wctob(c); eputc(fd,x<0?'?');


Horribly formatting, IMHO, but that's not the problem.
wtoc returns the character in the range of unsigned char (or EOF), so if
plain char is signed this can give you a number outside the range eputc
is defined for. How about making buf an array of unsigned char?

Alternatively, use wctob and handle it fully, this (uncompiled so
probably broken) code is a start, but moving on and handling shift state
properly would be better:
int i;
char wcbuf[MB_CUR_MAX];
int nc=wctomb(wcbuf,c);
if (nc<0) {
eputc(fd,'?');
}
else {
for (i=0; i<nc; i++) {
eputc(fd,wcbuf[i]);
}
}
> }
>
> static void eputws(int fd, wchar_t *s) {
> while(*s) { eputwc(fd,*s); s++; }
> }
>
> static void eputs(int fd, char *s) {
> while(*s) { eputc(fd,*s); s++; }
> }
>
> static char * uimaxtoa(uintmax_t n, int base) {
> static char buf[CHAR_BIT*sizeof(n)+2];
> char *endptr=buf+(sizeof buf)-2;
> do {
> *endptr--=digits[n%base];
> n/=base;
> } while(n);
> return endptr+1;
> }
>
> static char * pcvt(void *p) {
> static char buf[sizeof(void *)*CHAR_BIT];
> /* well, we can always hope this will fit - anyone know a more
> * portable way to implement %p? */
> sprintf(buf,"%p",p);


Yuk. I would suggest fully implementing the %p yourself, producing a
string like "0xaabbccdd" with the length obviously dependant on
sizeof(void*). You could even check for a null pointer and output
something like "(null)" for that.

> return buf;
> }
>
> static char * tcvt(time_t t) {
> static char buf[] = "YYYY-MM-DD HH:MM:SS";


As a matter of style I prefer not naming a local variable the same as a
file scope variable.

> strftime(buf,sizeof buf,"%Y-%m-%d %H:%M:%S",gmtime(&t));
> return buf;
> }
>
> void eprintf(int fd, const char *fmt, ...) {
> int c;
> va_list ap;
> int islong=0;
> uintmax_t uarg;
> intmax_t iarg;
>
> va_start(ap,fmt);
>
> loop:
> for(;*fmt&&*fmt!='%';fmt++)
> eputc(fd,*fmt);
> if(!*fmt) goto end;
> if(*fmt++ == '%') {
> islong=0;
> digits=ldigit;
> top:
> c = *fmt++;
> retry:
> switch(c) {
> case 'c':
> if(islong) eputwc(fd,va_arg(ap,wint_t));
> else eputc(fd,va_arg(ap,int));
> break;
> /* obsolete, but it doesn't conflict with anything. */
> case 'D': case 'O': case 'U': case 'C': case 'S':
> islong=1; c=tolower(c); goto retry;
> case 'A': case 'E': case 'F': case 'G': case 'X':
> digits=udigit; c = tolower(c); goto retry;
> case 'd': case 'i':
> switch(islong) {
> case 0: iarg=va_arg(ap,int); break;
> case 1: iarg=va_arg(ap,long int); break;
> case 2: iarg=va_arg(ap,long long int); break;
> // this is wrong on non-twos-complement systems
> case 3: iarg=va_arg(ap,size_t); break;


Use a typedef for the correct type which can be customised for each system?

> case 4: iarg=va_arg(ap,ptrdiff_t); break;
> case 5: iarg=va_arg(ap,intmax_t); break;
> }
> if(iarg<0) {
> eputc(fd,'-');
> uarg=-iarg;


This can overflow for maximum negative value of the largest signed type
on a 2s complement system.

> } else {
> uarg=iarg;
> }
> goto cvt;
> case 'o': case 'u': case 'x':
> switch(islong) {
> case 0: uarg=va_arg(ap,unsigned); break;
> case 1: uarg=va_arg(ap,long unsigned); break;
> case 2: uarg=va_arg(ap,long long unsigned); break;
> case 3: uarg=va_arg(ap,size_t); break;
> // this is wrong on non-twos-complement systems.
> case 4: uarg=va_arg(ap,ptrdiff_t); break;


Use a typedef for the correct type which can be customised for each system?

> case 5: uarg=va_arg(ap,uintmax_t); break;
> }
> cvt:
> eputs(fd,uimaxtoa(uarg,c=='o'?8c=='x'?16:10)));
> break;
> case 'p':
> eputs(fd,pcvt(va_arg(ap,void*))); break;
> case 's':
> if(islong)eputws(fd,va_arg(ap,wchar_t *));
> else eputs(fd,va_arg(ap,char *));
> break;
> /* my own extensions, they print an int representing an errno value and
> * a time_t, respectively. %! calls strerror(). */
> case '!':
> eputs(fd,strerror(va_arg(ap,int))); break;
> case 'T':
> eputs(fd,tcvt(va_arg(ap,time_t))); break;
> case 0:
> goto end;
> default:
> eputs(fd,"%?"); /*fall*/
> case '%':
> write(fd,&c,1); break;


As pete pointed out, the above is wrong. Why not use your eputc
function? That's what it is there for, after all

> case 'l': islong++; if(islong > 2) islong=2; goto top;
> case 'j': islong=5; goto top;
> case 't': islong=4; goto top;
> case 'z': islong=3; goto top;
> // here's the stuff we don't implement
> case 'n':
> switch(islong) {
> case 0: (void)va_arg(ap,int *); break;
> case 1: (void)va_arg(ap,long *); break;
> case 2: (void)va_arg(ap,long long *); break;
> case 3: (void)va_arg(ap,size_t *); break;
> // this is wrong on non-twos-complement systems.
> case 4: (void)va_arg(ap,ptrdiff_t *); break;
> case 5: (void)va_arg(ap,intmax_t *); break;
> } break;
> case '*':
> (void)va_arg(ap,int);
> case '0': case '1': case '2': case '3': case '4':
> case '5': case '6': case '7': case '8': case '9':
> case '.': case '#': case '+': case '-': case ' ':
> case '\'':
> goto top;


This will mean you do something silly with a format specifier with a
field width in it.

> case '$':
> /* this, i can't even pretend to support - eh, well, it's a posix thing,
> * rather than a C thing, anyway. */
> eputs(fd,"%$?????\n");
> flush(fd);
> return;
> /* I don't feel like messing with floating point right now */
> case 'L':
> islong=2; goto top;
> case 'a': case 'e': case 'f': case 'g':
> if(islong == 2) (void)va_arg(ap, long double);
> else if(islong == 2) (void)va_arg(ap, double);
> eputs(fd,"%FLT?");
> break;
> }
> }
> goto loop;


Wouldn't using a loop construct be clearer?

> end:
> flush(fd);


You need to use va_end when you have finished, as pete pointed out.

> }


Not a bad start IMHO.
--
Flash Gordon
Living in interesting times.
Although my email address says spam, it is real and I read it.
 
Reply With Quote
 
Jordan Abel
Guest
Posts: n/a
 
      12-07-2005
On 2005-12-07, Ben Pfaff <(E-Mail Removed)> wrote:
> Jordan Abel <(E-Mail Removed)> writes:
>
>> I have written this function and was wondering if anyone else could
>> point out if there's anything wrong with it. The purpose is to
>> substitute for printf when in a situation where low-level [beyond the
>> scope of this newsgroup] I/O has to be used for whatever reason, and
>> memory allocation cannot be used [for risk of it failing or whatever
>> else]

>
> If you want to look at a simple, but more complete, version of
> printf(), then you could look at the version that I wrote that is
> incorporated in Pintos:
> http://www.stanford.edu/class/cs140/.../pintos.tar.gz
>
> The printf() implementation is in pintos/src/lib/stdio.c.


interesting - mine makes an effort to at least eat the appropriate
arguments for floating types, %n, etc. And it doesn't recursively call
itself for errors, because as far as i can tell it's not re-entrant
[mainly due to my manual buffering of stuff]
 
Reply With Quote
 
Jordan Abel
Guest
Posts: n/a
 
      12-07-2005
On 2005-12-07, Flash Gordon <(E-Mail Removed)> wrote:
> Jordan Abel wrote:
>> I have written this function and was wondering if anyone else could
>> point out if there's anything wrong with it. The purpose is to
>> substitute for printf when in a situation where low-level [beyond the
>> scope of this newsgroup] I/O has to be used for whatever reason, and
>> memory allocation cannot be used [for risk of it failing or whatever
>> else] - mainly it's become for me an exercise in interpreting
>> printf-like format strings, and i thought i'd post it here to see if
>> anyone can point out any bugs in it - i.e. anything that would cause
>> undefined behavior, and any valid format strings it might fail to
>> interpret [there's functionality it doesn't implement, but it should at
>> least pop off all the same arguments for any given valid format string
>> that a real printf function will.]
>>
>> [is this the appropriate newsgroup for this kind of thing?]

>
> Seems a reasonable place to me, especially as you have provided a
> standard C implementation of the one otherwise non-standard function you
> use.
>
> Other valid reason for writing such a function iare implementing sprintf
> on an embedded system without it or implementing an extended fprintf
> like function.
>
>> ______
>>
>> #include <limits.h>
>> #include <stdarg.h>
>> #include <stddef.h>
>> #include <stdint.h>
>> #include <stdio.h>
>> #include <stdlib.h>
>> #include <string.h>
>> #include <time.h>
>> #include <wchar.h>
>>
>> int write(int fd,char*buf,size_t len);
>>
>> #if 0 /*if your system doesn't have this platform-specific function*/
>> static FILE * fdtab[] = { stdin, stdout, stderr, 0, 0, 0, 0, 0, 0, 0 };
>> int write(int fd,char *buf,size_t len) {
>> if(fd > 0 || !fdtab[fd]) {
>> return -1;

>
> This looks wrong to me. Don't you mean
> if (fd < 0 || fd >= ((sizeof fdtab)/sizeof *fdtab)) ||
> !fdtab[fd]) {
> return -1;


yeah.

>> }
>> return fwrite(buf,1,len,fdtab[fd]);
>> }
>> #endif
>>
>> #define LENGTH 64
>>
>> static char buf[LENGTH];
>> static int pos;
>> static char const udigit[16] = "0123456789ABCDEF";
>> static char const ldigit[16] = "0123456789abcdef";
>> static const char *digits;

>
> Obviously not going for thread safety.


I was going to add a lock, eventually. Such things are hardly standard
C. [It's not signal-safe either, but, then, what is, in stdc]

> I'm not sure about your logic on which functions you are specifying
> inline for.
>
>> static void flush(int fd) {
>> write(fd,buf,pos);
>> pos=0;
>> }
>>
>> static inline void eputc(int fd, int c) {
>> buf[pos++]=c;

>
> buf is a char array, so if c is outside the range of char (say char is
> signed on your implementation) you have a problem. See below...
>
>> if(pos==LENGTH) flush(fd);
>> }
>>
>> static inline void eputwc(int fd, wint_t c) {
>> int x = wctob(c); eputc(fd,x<0?'?');

>
> Horribly formatting, IMHO, but that's not the problem.
> wtoc returns the character in the range of unsigned char (or EOF), so if
> plain char is signed this can give you a number outside the range eputc
> is defined for. How about making buf an array of unsigned char?


that would probably be the best solution

> Alternatively, use wctob and handle it fully,


The code as-is is just to do something sensible with %ls and %lc

> this (uncompiled so probably broken) code is a start, but moving on
> and handling shift state properly would be better:
> int i;
> char wcbuf[MB_CUR_MAX];
> int nc=wctomb(wcbuf,c);
> if (nc<0) {
> eputc(fd,'?');
> }
> else {
> for (i=0; i<nc; i++) {
> eputc(fd,wcbuf[i]);
> }
> }


>> static char * pcvt(void *p) {
>> static char buf[sizeof(void *)*CHAR_BIT];
>> /* well, we can always hope this will fit - anyone know a more
>> * portable way to implement %p? */
>> sprintf(buf,"%p",p);

>
> Yuk. I would suggest fully implementing the %p yourself, producing a
> string like "0xaabbccdd" with the length obviously dependant on
> sizeof(void*). You could even check for a null pointer and output
> something like "(null)" for that.


My other thought was to rely on the presence of intptr_t. I don't want
to just dump bytes in front of an 0x, since that will yield a result
that's "wrong" [for some values of "wrong"] on little-endian platforms

>> return buf;
>> }
>>
>> static char * tcvt(time_t t) {
>> static char buf[] = "YYYY-MM-DD HH:MM:SS";

>
> As a matter of style I prefer not naming a local variable the same as a
> file scope variable.


I didn't notice that - they weren't named the same originally, but at
some point i changed the name of the global one.

>> // this is wrong on non-twos-complement systems
>> case 3: iarg=va_arg(ap,size_t); break;

>
> Use a typedef for the correct type which can be customised for each system?


I thought about leaving this out entirely and limiting it to systems on
which size_t is the same as either unsigned, unsigned long, or unsigned
long long.

>> if(iarg<0) {
>> eputc(fd,'-');
>> uarg=-iarg;

>
> This can overflow for maximum negative value of the largest signed type
> on a 2s complement system.


Can it? Even assigning to an unsigned variable? I can't think how i
_would_ do it properly. Are you sure the signed-unsigned conversion as
defined in the standard wouldn't "magically" fix this issue?

Do I have to do something silly like if(iarg!=INTMAX_MIN)? How do I deal
with systems that _aren't_ twos-complement in that case?

>> // this is wrong on non-twos-complement systems.
>> case 4: uarg=va_arg(ap,ptrdiff_t); break;

>
> Use a typedef for the correct type which can be customised for each system?


yeah, yeah

>> case '%':
>> write(fd,&c,1); break;

>
> As pete pointed out, the above is wrong. Why not use your eputc
> function? That's what it is there for, after all


Yeah, I already fixed it - this was leftover from an earlier
implementation that didn't do buffering.

>> case '0': case '1': case '2': case '3': case '4':
>> case '5': case '6': case '7': case '8': case '9':
>> case '.': case '#': case '+': case '-': case ' ':
>> case '\'':
>> goto top;

>
> This will mean you do something silly with a format specifier with a
> field width in it.


it'll mean i ignore the field width [along with those other modifiers],
won't it? "goto top" still advances to the next character in the format
string. "goto retry" stays on the same character or a reassigned one.

>> goto loop;

>
> Wouldn't using a loop construct be clearer?


I didn't want to add another level of indentation.

>
>> end:
>> flush(fd);

>
> You need to use va_end when you have finished, as pete pointed out.


yeah, curse this traditional-unix-like implementation where it's not a
problem.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
printf affects following printf/s azza C Programming 0 10-17-2010 09:43 AM
Knowing the implementation, are all undefined behaviours become implementation-defined behaviours? Michael Tsang C++ 32 03-01-2010 09:15 PM
Extracting printf(...) from (void) printf(....) guru Perl Misc 8 02-03-2009 10:37 PM
(void) printf vs printf whatluo C Programming 29 09-08-2005 05:42 PM
bus error with printf line included, error without printf line? ben C Programming 4 06-26-2004 04:42 PM



Advertisments