#ifdef SHLIB
#include "shlib.h"
#endif
#include "defs.h"
#include <ctype.h>
#include <stdarg.h>
#define MAXOCT 11
#define MAXDIGS 10
#define MAXFPPREC 25
#define FILL_MINUS -1
#define FILL_ZERO 0
#define FILL_BLANK 1
#define SIGN_MINUS -1
#define SIGN_SPACE 0
#define SIGN_ALWAYS 1
#define LEN_INT 0
#define LEN_LONGINT 1
#define LEN_SHORTINT 2
#define LEN_LONGDOUBLE 3
void _doprnt();
static char *_p_dconv();
static char *_ffmt(va_list *app, char *buf, int prec, int alt);
static char *_efmt(va_list *app, char *buf, int ndigit, int fcode, int alt);
static char *_gfmt(va_list *app, char *buf, int prec, int fcode, int alt, int sign_type);
extern int isnan(double num);
extern int isinf(double num);
extern char *ecvt(double x, int ndigits, int *decimal, int *sign);
extern char *fcvt(double x, int ndigits, int *decimal, int *sign);
typedef struct _PrintfProcData {
char special;
NXPrintfProc *proc;
void *procData;
} PrintfProcData;
static PrintfProcData *printfProcTable = NULL;
static unsigned sizePrintfProcTable = 0;
void NXRegisterPrintfProc(char special, NXPrintfProc *proc, void *procData) {
printfProcTable = realloc(printfProcTable, (sizePrintfProcTable+1) * sizeof(PrintfProcData));
printfProcTable[sizePrintfProcTable].special = special;
printfProcTable[sizePrintfProcTable].proc = proc;
printfProcTable[sizePrintfProcTable].procData = procData;
sizePrintfProcTable++;
}
static int isRegisteredPrintfChar(char fcode, NXPrintfProc **proc, void **procData) {
unsigned index = sizePrintfProcTable;
while (index--) {
if (printfProcTable[index].special == fcode) {
*proc = printfProcTable[index].proc;
*procData = printfProcTable[index].procData;
return 1;
}
}
return 0;
}
void
NXVPrintf(NXStream *stream, register const char *fmt, va_list ap)
{
register char *begp, *endp;
register long int num;
char buf[400];
register char fcode;
int alt, sign, match;
int prec;
int length;
int mask, nbits, hexcase, digit;
int neg;
int width, leftadj, fill;
NXPrintfProc *proc;
void *procData;
_NXVerifyStream(stream);
for (;;) {
while ((fcode = *fmt++)!='%') {
if (fcode=='\0')
return;
NXPutc(stream, fcode);
}
length = LEN_INT;
leftadj = 0;
fill = FILL_BLANK;
alt = 0;
sign = SIGN_MINUS;
width = 0;
prec = -1;
match = 1;
fcode = *fmt++;
postprefix:
switch (fcode) {
case 'c':
buf[0] = va_arg(ap, int);
endp = begp = &buf[0];
#ifdef notdef
if (buf[0] != '\0')
#endif notdef
endp++;
break;
case 's':
begp = va_arg(ap,char *);
if (begp==0)
begp = "(null pointer)";
for (endp = begp; *endp && prec-- != 0; endp++)
continue;
break;
case 'O':
if (match) goto prefix;
alt = 1;
fcode = 'o';
case 'o':
case 'X':
case 'x':
hexcase = isupper(fcode) ? 'A' : 'a';
switch (length) {
default:
case LEN_INT:
num = (unsigned)va_arg(ap,int);
break;
case LEN_LONGINT:
num = va_arg(ap,long);
break;
case LEN_SHORTINT:
num = (unsigned short)va_arg(ap,int);
break;
}
if (fcode=='o') {
mask = 0x7;
nbits = 3;
} else {
mask = 0xf;
nbits = 4;
}
endp = begp = buf + MAXOCT + 3;
do {
digit = (int)num & mask;
if (digit < 10)
*--begp = digit + '0';
else
*--begp = digit - 10 + hexcase;
} while (num = (unsigned)num >> nbits);
if (alt) {
if (fcode=='o') {
if (*begp != '0')
*--begp = '0';
} else if (!leftadj
&& (fill == FILL_ZERO ||
fill == FILL_MINUS)) {
NXPutc(stream, '0');
NXPutc(stream, fcode);
if ((width-=2)<0) width=0;
} else {
*--begp = fcode;
*--begp = '0';
}
}
break;
case 'D':
case 'U':
case 'I':
length = LEN_LONGINT;
fcode = tolower(fcode);
case 'd':
case 'i':
case 'u':
switch (length) {
default:
case LEN_INT:
if (fcode == 'u')
num = (unsigned)va_arg(ap,int);
else
num = (long)va_arg(ap,int);
break;
case LEN_LONGINT:
num = va_arg(ap,long);
break;
case LEN_SHORTINT:
if (fcode == 'u')
num = (unsigned short)va_arg(ap,int);
else
num = (short)va_arg(ap,int);
break;
break;
}
if (neg = (fcode != 'u' && num < 0))
num = -num;
begp = _p_dconv(num, buf);
if (neg)
*--begp = '-';
if (fill == FILL_ZERO)
fill = FILL_MINUS;
endp = buf + MAXDIGS + 1;
break;
case 'e':
case 'E':
if (prec < 0)
prec = 6;
endp = _efmt(&ap, buf, prec+1, fcode, alt);
begp = buf;
break;
case 'g':
case 'G':
if (prec < 0)
prec = 6;
endp = _gfmt(&ap, buf, prec, fcode, alt, -1);
begp = buf;
break;
case 'f':
if (prec < 0)
prec = 6;
endp = _ffmt(&ap, buf, prec, alt);
begp = buf;
break;
default:
if (isRegisteredPrintfChar(fcode, &proc, &procData)) {
proc(stream, va_arg(ap, void *), procData);
begp = endp = "";
} else if (match) {
prefix:
for (;;) {
switch (fcode) {
case '-':
leftadj = 1;
break;
case '+':
sign = SIGN_ALWAYS;
break;
case ' ':
if (sign != SIGN_ALWAYS)
sign = SIGN_SPACE;
break;
case '#':
alt = 1;
break;
default:
match = 0;
break;
}
if (!match) break;
fcode = *fmt++;
}
if (fcode == '0') {
fill = FILL_ZERO;
fcode = *fmt++;
}
if (fcode == '*') {
fcode = *fmt++;
width = va_arg(ap, int);
if (width < 0) {
width = -width;
leftadj = !leftadj;
}
} else {
while (isdigit(fcode)) {
width = width * 10 + (fcode - '0');
fcode = *fmt++;
}
}
if (fcode=='.') {
fcode = *fmt++;
if (fcode == '*') {
prec = va_arg(ap, int);
fcode = *fmt++;
} else {
prec = 0;
while (isdigit(fcode)) {
prec = prec * 10 + (fcode - '0');
fcode = *fmt++;
}
}
}
if (fcode == '#') {
alt = 1;
fcode = *fmt++;
}
switch (fcode) {
case 'l':
length = LEN_LONGINT;
fcode = *fmt++;
break;
case 'L':
length = LEN_LONGDOUBLE;
fcode = *fmt++;
break;
case 'h':
length = LEN_SHORTINT;
fcode = *fmt++;
break;
}
goto postprefix;
} else {
buf[0] = fcode;
endp = begp = buf;
endp++;
break;
}
}
if (fcode != '\0') {
char cfill;
register int alen;
int npad;
alen = endp - begp;
if (alen >= width) {
while (--alen >= 0)
NXPutc(stream, *begp++);
} else {
npad = width - alen;
cfill = (fill == FILL_BLANK) ? ' ': '0';
if (!leftadj) {
if (*begp == '-' && fill == FILL_MINUS) {
NXPutc(stream, '-');
begp++;
alen--;
}
while (--npad >= 0)
NXPutc(stream, cfill);
}
while (--alen >= 0)
NXPutc(stream, *begp++);
if (leftadj)
while (--npad >= 0)
NXPutc(stream, cfill);
}
}
}
}
static char *
_p_dconv(val, buf)
register unsigned long val;
char *buf;
{
register char *bp;
bp = buf + MAXDIGS + 1;
do {
*--bp = (val % 10) + '0';
val /= 10;
} while (val != 0);
return(bp);
}
static char *_efmt(va_list *app, char *buf, int ndigit, int fcode, int alt)
{
int sign, decpt;
register char *p1, *p2;
register i;
char ebuf[MAXDIGS+1];
double number;
number = va_arg(*app, double);
if (i = isnan(number)) {
if (i > 0)
strcpy(buf, "NaN");
else
strcpy(buf, "SNaN");
return(&buf[strlen(buf)]);
}
if (i = isinf(number)) {
if (i > 0)
strcpy(buf, "+Infinity");
else
strcpy(buf, "-Infinity");
return(&buf[strlen(buf)]);
}
if (ndigit > MAXFPPREC)
ndigit = MAXFPPREC;
p1 = ecvt(number, ndigit, &decpt, &sign);
p2 = buf;
if (*p1 != '0') {
decpt--;
if (sign)
*p2++ = '-';
}
*p2++ = *p1++;
*p2++ = '.';
while (--ndigit > 0)
*p2++ = *p1++;
if (!alt && p2[-1]=='.')
p2--;
*p2++ = fcode;
if (decpt < 0) {
decpt = -decpt;
*p2++ = '-';
} else
*p2++ = '+';
p1 = _p_dconv(decpt, ebuf);
if (&ebuf[sizeof(ebuf)] - p1 == 1)
*p2++ = '0';
while (p1 < &ebuf[sizeof(ebuf)])
*p2++ = *p1++;
*p2 = '\0';
return(p2);
}
static char *_ffmt(va_list *app, char *buf, int prec, int alt)
{
int sign, decpt;
register char *p1, *p2;
register i;
double number;
number = va_arg(*app, double);
if (i = isnan(number)) {
if (i > 0)
strcpy(buf, "NaN");
else
strcpy(buf, "SNaN");
return(&buf[strlen(buf)]);
}
if (i = isinf(number)) {
if (i > 0)
strcpy(buf, "+Infinity");
else
strcpy(buf, "-Infinity");
return(&buf[strlen(buf)]);
}
if (prec > MAXFPPREC)
prec = MAXFPPREC;
p1 = fcvt(number, prec, &decpt, &sign);
p2 = buf;
if (*p1 != '0' && sign && -decpt < prec)
*p2++ = '-';
if (decpt <= 0) {
*p2++ = '0';
*p2++ = '.';
while (decpt < 0 && prec > 0) {
*p2++ = '0';
decpt++;
prec--;
}
} else {
while (decpt > 0) {
*p2++ = *p1++;
decpt--;
}
*p2++ = '.';
}
while (prec > 0) {
*p2++ = *p1++;
prec--;
}
if (!alt && p2[-1] == '.')
p2--;
*p2 = '\0';
return(p2);
}
static char *btod(unsigned long val, char *buf)
{
register char *bp;
bp = buf + MAXDIGS + 1;
do {
*--bp = (val % 10) + '0';
val /= 10;
} while (val != 0);
return(bp);
}
static char *_gfmt_common(double number, char *buf, int prec, int fcode, int alt, int sign_type)
{
int is_neg, decpt;
register char *p1, *p2;
register i;
char ebuf[MAXDIGS+1];
if (i = isnan(number)) {
if (i > 0)
strcpy(buf, "NaN");
else
strcpy(buf, "SNaN");
return(&buf[strlen(buf)]);
}
if (i = isinf(number)) {
if (i > 0)
strcpy(buf, "+Infinity");
else
strcpy(buf, "-Infinity");
return(&buf[strlen(buf)]);
}
if (prec > MAXFPPREC)
prec = MAXFPPREC;
p1 = ecvt(number, prec+1, &decpt, &is_neg);
p2 = buf;
if (decpt > prec || decpt < -3) {
if (is_neg && *p1 != '0')
*p2++ = '-';
else if (sign_type == SIGN_ALWAYS)
*p2++ = '+';
else if (sign_type == SIGN_SPACE)
*p2++ = ' ';
if (*p1 != '0')
decpt--;
*p2++ = *p1++;
*p2++ = '.';
while (prec - 1 > 0) {
*p2++ = *p1++;
prec--;
}
if (! alt) {
while (p2[-1] == '0')
p2--;
if (p2[-1] == '.')
p2--;
}
*p2++ = isupper(fcode) ? 'E' : 'e';
if (decpt < 0) {
decpt = -decpt;
*p2++ = '-';
} else
*p2++ = '+';
p1 = btod(decpt, ebuf);
if (&ebuf[sizeof(ebuf)] - p1 == 1)
*p2++ = '0';
while (p1 < &ebuf[sizeof(ebuf)])
*p2++ = *p1++;
} else {
p1 = fcvt(number, prec, &decpt, &is_neg);
if (*p1 != '0' && is_neg && -decpt < prec)
*p2++ = '-';
else if (sign_type == SIGN_ALWAYS)
*p2++ = '+';
else if (sign_type == SIGN_SPACE)
*p2++ = ' ';
if (decpt <= 0) {
*p2++ = '0';
*p2++ = '.';
while (decpt < 0) {
*p2++ = '0';
decpt++;
prec--;
}
} else {
while (decpt > 0) {
*p2++ = *p1++;
decpt--;
}
*p2++ = '.';
}
while (prec > 0 && *p1) {
*p2++ = *p1++;
prec--;
}
if (! alt) {
while (p2[-1] == '0')
p2--;
if (p2[-1] == '.')
p2--;
}
}
*p2 = '\0';
return(p2);
}
static char *_gfmt(va_list *app, char *buf, int prec, int fcode, int alt, int sign_type)
{
double number;
number = va_arg(*app, double);
return _gfmt_common(number, buf, prec, fcode, alt, sign_type);
}