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,siz e_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,le n,fdtab[fd]);
}
#endif
#define LENGTH 64
static char buf[LENGTH];
static int pos;
static char const udigit[16] = "0123456789ABCD EF";
static char const ldigit[16] = "0123456789abcd ef";
static const char *digits;
static void flush(int fd) {
write(fd,buf,po s);
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?'? ':x);
}
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(uintma x_t n, int base) {
static char buf[CHAR_BIT*sizeof (n)+2];
char *endptr=buf+(si zeof 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,si zeof buf,"%Y-%m-%d %H:%M:%S",gmtim e(&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_ar g(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,uimaxt oa(uarg,c=='o'? 8:(c=='x'?16:10 )));
break;
case 'p':
eputs(fd,pcvt(v a_arg(ap,void*) )); break;
case 's':
if(islong)eputw s(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,strerr or(va_arg(ap,in t))); break;
case 'T':
eputs(fd,tcvt(v a_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);
}
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,siz e_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,le n,fdtab[fd]);
}
#endif
#define LENGTH 64
static char buf[LENGTH];
static int pos;
static char const udigit[16] = "0123456789ABCD EF";
static char const ldigit[16] = "0123456789abcd ef";
static const char *digits;
static void flush(int fd) {
write(fd,buf,po s);
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?'? ':x);
}
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(uintma x_t n, int base) {
static char buf[CHAR_BIT*sizeof (n)+2];
char *endptr=buf+(si zeof 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,si zeof buf,"%Y-%m-%d %H:%M:%S",gmtim e(&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_ar g(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,uimaxt oa(uarg,c=='o'? 8:(c=='x'?16:10 )));
break;
case 'p':
eputs(fd,pcvt(v a_arg(ap,void*) )); break;
case 's':
if(islong)eputw s(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,strerr or(va_arg(ap,in t))); break;
case 'T':
eputs(fd,tcvt(v a_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);
}
Comment