ack/lang/cem/libcc.ansi/sys/stdio/doprnt.c
David Given 9109d7af7f First stage in modularising FILE*. Refactor so that printf/scanf don't rely on
FILE* innards; allow plats to replace the entire emulated FILE* system.
2019-06-15 13:07:10 +02:00

400 lines
6.6 KiB
C

/*
* doprnt.c - print formatted output
*/
/* $Id$ */
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "loc_incl.h"
#if ACKCONF_WANT_STDIO
/* gnum() is used to get the width and precision fields of a format. */
static const char*
gnum(register const char* f, int* ip, va_list* app)
{
register int i, c;
if (*f == '*')
{
*ip = va_arg((*app), int);
f++;
}
else
{
i = 0;
while ((c = *f - '0') >= 0 && c <= 9)
{
i = i * 10 + c;
f++;
}
*ip = i;
}
return f;
}
#if _EM_WSIZE == _EM_PSIZE
#define set_pointer(flags) /* nothing */
#elif _EM_LSIZE == _EM_PSIZE
#define set_pointer(flags) (flags |= FL_LONG)
#else
#error garbage pointer size
#define set_pointer(flags) /* compilation might continue */
#endif
#define PUTC(c) \
do \
{ \
int i = putc(c, stream); \
if (i == EOF) \
{ \
if (ferror(stream)) \
return -1; \
} \
} while (0)
/* print an ordinal number */
static char*
o_print(va_list* ap, int flags, char* s, char c, int precision, int is_signed)
{
long signed_val;
unsigned long unsigned_val;
char* old_s = s;
int base;
switch (flags & (FL_SHORT | FL_LONG))
{
case FL_SHORT:
if (is_signed)
{
signed_val = (short)va_arg(*ap, int);
}
else
{
unsigned_val = (unsigned short)va_arg(*ap, unsigned);
}
break;
case FL_LONG:
if (is_signed)
{
signed_val = va_arg(*ap, long);
}
else
{
unsigned_val = va_arg(*ap, unsigned long);
}
break;
default:
if (is_signed)
{
signed_val = va_arg(*ap, int);
}
else
{
unsigned_val = va_arg(*ap, unsigned int);
}
break;
}
if (is_signed)
{
if (signed_val < 0)
{
*s++ = '-';
signed_val = -signed_val;
}
else if (flags & FL_SIGN)
*s++ = '+';
else if (flags & FL_SPACE)
*s++ = ' ';
unsigned_val = signed_val;
}
if ((flags & FL_ALT) && (c == 'o'))
*s++ = '0';
if (!unsigned_val)
{
if (!precision)
return s;
}
else if (((flags & FL_ALT) && (c == 'x' || c == 'X'))
|| c == 'p')
{
*s++ = '0';
*s++ = (c == 'X' ? 'X' : 'x');
}
switch (c)
{
case 'b':
base = 2;
break;
case 'o':
base = 8;
break;
case 'd':
case 'i':
case 'u':
base = 10;
break;
case 'x':
case 'X':
case 'p':
base = 16;
break;
}
s = _i_compute(unsigned_val, base, s, precision);
if (c == 'X')
while (old_s != s)
{
*old_s = toupper(*old_s);
old_s++;
}
return s;
}
int _doprnt(register const char* fmt, va_list ap, void (*put)(int))
{
register char* s;
register int j;
int i, c, width, precision, zfill, flags, between_fill;
int nrchars = 0;
const char* oldfmt;
char *s1, buf[1025];
while (c = *fmt++)
{
if (c != '%')
{
#ifdef CPM
if (c == '\n')
{
PUTC('\r');
}
#endif
put(c);
nrchars++;
continue;
}
flags = 0;
do
{
switch (*fmt)
{
case '-':
flags |= FL_LJUST;
break;
case '+':
flags |= FL_SIGN;
break;
case ' ':
flags |= FL_SPACE;
break;
case '#':
flags |= FL_ALT;
break;
case '0':
flags |= FL_ZEROFILL;
break;
default:
flags |= FL_NOMORE;
continue;
}
fmt++;
} while (!(flags & FL_NOMORE));
oldfmt = fmt;
fmt = gnum(fmt, &width, &ap);
if (fmt != oldfmt)
flags |= FL_WIDTHSPEC;
if (*fmt == '.')
{
fmt++;
oldfmt = fmt;
fmt = gnum(fmt, &precision, &ap);
if (precision >= 0)
flags |= FL_PRECSPEC;
}
if ((flags & FL_WIDTHSPEC) && width < 0)
{
width = -width;
flags |= FL_LJUST;
}
if (!(flags & FL_WIDTHSPEC))
width = 0;
if (flags & FL_SIGN)
flags &= ~FL_SPACE;
if (flags & FL_LJUST)
flags &= ~FL_ZEROFILL;
s = s1 = buf;
switch (*fmt)
{
case 'h':
flags |= FL_SHORT;
fmt++;
break;
case 'l':
flags |= FL_LONG;
fmt++;
break;
case 'L':
flags |= FL_LONGDOUBLE;
fmt++;
break;
}
switch (c = *fmt++)
{
default:
#ifdef CPM
if (c == '\n')
{
PUTC('\r');
nrchars++;
}
#endif
put(c);
nrchars++;
continue;
case 'n':
if (flags & FL_SHORT)
*va_arg(ap, short*) = (short)nrchars;
else if (flags & FL_LONG)
*va_arg(ap, long*) = (long)nrchars;
else
*va_arg(ap, int*) = (int)nrchars;
continue;
case 's':
s1 = va_arg(ap, char*);
if (s1 == NULL)
s1 = "(null)";
s = s1;
while (precision || !(flags & FL_PRECSPEC))
{
if (*s == '\0')
break;
s++;
precision--;
}
break;
case 'p':
set_pointer(flags);
/* fallthrough */
case 'b':
case 'o':
case 'u':
case 'x':
case 'X':
if (!(flags & FL_PRECSPEC))
precision = 1;
else if (c != 'p')
flags &= ~FL_ZEROFILL;
s = o_print(&ap, flags, s, c, precision, 0);
break;
case 'd':
case 'i':
flags |= FL_SIGNEDCONV;
if (!(flags & FL_PRECSPEC))
precision = 1;
else
flags &= ~FL_ZEROFILL;
s = o_print(&ap, flags, s, c, precision, 1);
break;
case 'c':
*s++ = va_arg(ap, int);
break;
#if ACKCONF_WANT_STDIO_FLOAT
case 'G':
case 'g':
if ((flags & FL_PRECSPEC) && (precision == 0))
precision = 1;
case 'f':
case 'E':
case 'e':
if (!(flags & FL_PRECSPEC))
precision = 6;
if (precision >= sizeof(buf))
precision = sizeof(buf) - 1;
flags |= FL_SIGNEDCONV;
s = _f_print(&ap, flags, s, c, precision);
break;
#endif
case 'r':
ap = va_arg(ap, va_list);
fmt = va_arg(ap, char*);
continue;
}
zfill = ' ';
if (flags & FL_ZEROFILL)
zfill = '0';
j = s - s1;
/* between_fill is true under the following conditions:
* 1- the fill character is '0'
* and
* 2a- the number is of the form 0x... or 0X...
* or
* 2b- the number contains a sign or space
*/
between_fill = 0;
if ((flags & FL_ZEROFILL)
&& (((c == 'x' || c == 'X') && (flags & FL_ALT))
|| (c == 'p')
|| ((flags & FL_SIGNEDCONV)
&& (*s1 == '+' || *s1 == '-' || *s1 == ' '))))
between_fill++;
if ((i = width - j) > 0)
if (!(flags & FL_LJUST))
{ /* right justify */
nrchars += i;
if (between_fill)
{
if (flags & FL_SIGNEDCONV)
{
j--;
nrchars++;
put(*s1++);
}
else
{
j -= 2;
nrchars += 2;
put(*s1++);
put(*s1++);
}
}
do
{
put(zfill);
} while (--i);
}
nrchars += j;
while (--j >= 0)
{
put(*s1++);
}
if (i > 0)
nrchars += i;
while (--i >= 0)
put(zfill);
}
return nrchars;
}
#endif