/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:doprnt.c 12.0$ */
/* $ACIS:doprnt.c 12.0$ */
/* $Source: /ibm/acis/usr/src/lib/libc/ca/stdio/RCS/doprnt.c,v $ */

#ifndef lint
static char *rcsid = "$Header:doprnt.c 12.0$";
#endif

#ifdef BOOTSTRAP
# include "stdio h t"
#else
#include <stdio.h>
#endif
#include <ctype.h>

#define WIDTHLIMIT 30000
#define STRWIDTHLIMIT 30000
#define INTPRECLIMIT 100
#define FLOATPRECLIMIT 500
#define STRING0 '1'		/* flag for zero-fill for string conversions */
#define MAGIC_STRLEN	7

static _strout(count, string, adjust, file, fillch)
register count;
register char *string;
register int adjust;
register struct _iobuf *file;
register int fillch;
{
	register int c;
	register int zerostrfill;

	zerostrfill = (fillch==STRING0);
	if (zerostrfill) fillch = '0';
	for (; adjust < 0; ++adjust) {
		while ((!zerostrfill) && fillch=='0' && (count > 0) &&
			((c = *string)=='-' || c == '+' || c == ' ' ||
				c == '0' || c == 'x' || c == 'X')) {
			putc(c, file);
			count--;
			++string;
		}
		putc(fillch, file);
	}
	if(count >= MAGIC_STRLEN) {
		fwrite(string, 1, count, file); /* fwrite is faster than loop */
	}
	else {
		while (--count>=0)
			putc(*string++, file);
	}
	for (; adjust; --adjust)
		putc(fillch, file);
}

_doprnt(fmt, argp, file)
register char *fmt;
register int *argp;
register FILE *file;
{      register char c, *p, *q;
       register int d, width, ndigit, radix;
       register unsigned n;
       int sign, decpt;
       unsigned dotfnd;		/* is there dot in format code? */
       int d1;			/* save UCase/LCase for hex conversion */
       int c1,c2,c3;		/* floating-point print counters */
       char t[128], digits[15], ljust, zfill, plus, spacef, pound; 
       char *ecvt(), *fcvt();

       p = &t[0];
       while (c = *fmt++)
               if (c != '%') {
                       *p++ = c;
		       if (p >= t+sizeof(t)) {
			       _strout(p-t, t, 0, file);
			       p = &t[0];
		       }
	       }
               else {
                       if (p != &t[0]) {
                               _strout(p-t, t, 0, file);
                               p = &t[0];
                       }
		       pound= spacef= plus= ljust= 0;
		       ndigit= 0;		/* ndigit is not char */
		       zfill= ' ';
		       while (1) {
			   switch (*fmt++) {
			       case '-':
				   ljust= 1;
				   continue; /* repeat the switch */
	   		       case '0':
				   zfill= '0';
				   continue;
	    		       case '+':
				   plus= 1;
				   continue;
	    		       case ' ': 
				   spacef= 1;
				   continue;
	    		       case '#':
				   pound= 1;
				   continue;
	    		       default:
				   fmt--;	
			   }
		       break;	/* only if default (fail) switch is used */
    		       }
                       if(*fmt != '*') {
                               width = 0;
                               while (isdigit(d = *fmt++))
                                       width = width*10 + (d - '0');
                       }
                       else {
                               fmt++;
                               d = *fmt++;
                               width = *argp++;
			       if (width < 0) {
				   width = -width;
				   ljust = 1;
			       }
                       }
		       if (width > WIDTHLIMIT) width = WIDTHLIMIT;
                       if (dotfnd = (d == '.')) {
                           if(*fmt != '*') {
                               while (isdigit(d = *fmt++)) {
                                       ndigit = ndigit*10 + (d - '0');
                               }
                           }
                           else {
                               fmt++;
                               d = *fmt++;
                               ndigit = *argp++;
			       if (ndigit < 0) ndigit = 0;
			       /* Treatment of huge precision depends on type */
                           }
                       }
		       while (d == 'l' || d == 'h') d = *fmt++;
                       switch (d) {
                       case 'o':
		       case 'O':	/* archaism, for VAX compatibility */
                               radix = 8;
			       n = *argp++;
			       if ((n) && (pound))
				       *p++ = '0';
				       /* no leading zero added for n=0 */
                               goto compute;
                       case 'x':
                       case 'X':
			       radix = 16;
			       d1 = d;
			       if ((n = *argp++) && (pound)) {
				       *p++ = '0';
				       *p++ = (d == 'x') ? 'x' : 'X';
			       }
			       goto compute;
                       case 'd':
		       case 'D':	/* archaism, for VAX compatibility */
                               radix = 10;
                               if ((d1 = *argp++) < 0) {
                                       *p++ = '-';
				       n = -d1;
                               } else {
				       n = d1;
				       if (plus) *p++ = '+';
				       else if (spacef) *p++ = ' ';
			       }
                               goto compute;
                       case 'u':
		       case 'U':	/* archaism, for VAX compatibility */
		               n = *argp++;
                               radix = 10;
                       compute:
			       if (!dotfnd) ndigit = 1;
			       if (ndigit > INTPRECLIMIT) ndigit = INTPRECLIMIT;
			       if ((!n)&& (zfill=='0')&& (ndigit)&& (d!='d')&&
				   (!ljust)) {
				       putc('0',file);
				       n= (width>ndigit ? width : ndigit) - 1; 
				       while (n-- > 0) putc('0',file);
				       break;
                               }
			       q = &digits[0];
                               if (n == 0 && ndigit > 0) {
                                       *q++ = '0';
				       goto shovel;
				}
                               else if (radix == 10) {
				       	_cvd10(q,n);
					while(*q == '0')
						 ++q; /* find 1st !0. */
					d = ndigit - (&digits[10] - q);
				        while (--d >= 0) *p++ = '0'; /* precision 0's */
                      			while (q < &digits[10])
						*p++ = *q++;
					
				}
                               else {
			       		/* XXX c is register, use it. */
				      d1 = (d1 == 'x') ? 'a' - 10 : 'A' - 10;
			       	      c = (radix==8) ? 3 : 4;
				      --radix; /* Make a mask out of radix. */
				      
			       	      for (; n != 0; n >>= c){
						d = n & radix;
						*q++ = d + (d<10 ? '0': d1);
					}
		       shovel:
				       d= ndigit - (q - &digits[0]);
				       while (--d >= 0) *p++ = '0'; /* precision 0's */
                      			while (q > &digits[0])
	                              		*p++ = *--q;
				}
                               goto prbuf;
                       case 'c':
                               *p++ = *argp++; 
			       if (zfill == '0') zfill = STRING0;
                       prbuf:  q = &t[0];
                       prstr:  if ((d = width - (p - q)) < 0)
                                       d = 0;
                               if (ljust == 0)
                                       d = -d;
			       if (ljust) zfill = ' ';
                               _strout(p-q, q, d, file, zfill);
                               p = &t[0];
                               break;
                       case 's':
                               q = (char *)*argp++;
                               if (dotfnd) d = ndigit;
                               else d = STRWIDTHLIMIT;
                               for (p=q; *p!=0 && --d>=0; p++);
			       if (zfill == '0') zfill = STRING0;
                               goto prstr;
#ifdef notdef 
                       case 'r':
                               argp = (int *)*argp;
                               fmt = (char *)*argp++;
                               break;
#endif
                       case 'f':
			       if (ndigit>FLOATPRECLIMIT) ndigit=FLOATPRECLIMIT;
			       c1= c2= c3= 0;
                               if (!dotfnd)
                                       ndigit = 6;
                               q = fcvt(*((double *)argp), ndigit,
                                       &decpt, &sign); argp += 2;
                               if (sign)
                                       *p++ = '-';
			       else if (plus)
				       *p++ = '+';
			       else if (spacef)
				       *p++ = ' ';
			       if (!isdigit(*q)) {	/* INF, NAN() */
				   zfill = ' ';
				   while (*p = *q++) p++;
				   goto prbuf;
			       }
                               if ((d = decpt) <= 0)
                                       *p++ = '0';
                               else {
				   *p++ = *q ? *q++ : '0';
				   while ((--d > 0) && (*q))
				       *p++ = *q++;
				   c1= d;
			       }
                               if ((d = ndigit) || (pound))
                                       *p++ = '.';
                               if ((decpt = - decpt) > 0)
                                       while (--d >= 0) {
                                               c2++;
                                               if (--decpt <= 0)
                                                       break;
                                       }
                               while (--d >= 0) {
                                   if (*q) *p++ = *q++;
				   else c3++;
			       }
			       *p++= 0;
			       q= &t[0];
			       d1= width - (c1 + c2 + c3 + p - q - 1);
			       if ((!ljust) && (zfill==' '))
				   for (d=1; d<=d1; d++) putc(zfill,file);
			       if ((*q=='+') || (*q=='-') || (*q==' '))
				   putc(*q++,file);
			       if ((!ljust) && (zfill=='0'))
				   for (d=1; d<=d1; d++) putc(zfill,file);
			       while ((*q != '.') && (*q != 0)) putc(*q++,file);
			       for (d=1; d<=c1; d++) putc ('0',file);
			       if (*q=='.') putc (*q++,file);
			       for (d=1; d<=c2; d++) putc ('0',file);
			       while (*q != 0) putc (*q++,file);
			       for (d=1; d<=c3; d++) putc ('0',file);
			       if (ljust) for (d=1; d<=d1; d++) putc(' ',file);
			       p= &t[0];
			       break;
                       case 'e':
		       case 'E':
			       c1= 0;
			       if (ndigit>FLOATPRECLIMIT) ndigit=FLOATPRECLIMIT;
                               if (!dotfnd)
                                       ndigit = 7;
                               else
                                       ndigit += 1;
                               q = ecvt(*((double *)argp), ndigit,
                                       &decpt, &sign); argp += 2;
                               if (sign)
                                       *p++ = '-';
			       else if (plus)
				       *p++ = '+';
			       else if (spacef)
				       *p++ = ' ';
			       if (!isdigit(*q)) {	/* INF, NAN() */
				   zfill = ' ';
				   while (*p = *q++) p++;
				   goto eprt;
			       }
                               if (*q == '0')
                                       decpt += 1;
                               *p++ = *q++;
                               if ((ndigit != 1) || (pound))
			           *p++ = '.';
			       while (--ndigit) {
				  if (*q) *p++ = *q++;
				  else c1++;
			       }
                               *p++ = ((d=='E') ? 'E':'e');
                               decpt -= 1;
                               if (decpt >= 0)
                                       *p++ = '+';
                               else {
                                       *p++ = '-';
                                       decpt = -decpt;
                               }
			       if (decpt>99) {
				   *p++ = decpt/100 + '0';
				   decpt = decpt%100;
			       }
			       *p++ = decpt/10 + '0';
			       *p++ = decpt%10 + '0';
		       eprt:   *p++ = 0;
			       q= &t[0];
			       d1= width - (c1 + p - q - 1);
			       if ((!ljust) && (zfill==' '))
				   for (d=1; d<=d1; d++) putc(zfill,file);
			       if ((*q=='+') || (*q=='-') || (*q==' '))
				   putc(*q++,file);
			       if ((!ljust) && (zfill=='0'))
				   for (d=1; d<=d1; d++) putc(zfill,file);
			       while ((*q != 'e') && (*q != 'E') && (*q))
				   putc(*q++,file);
			       for (d=1; d<=c1; d++) putc ('0',file);
			       while (*q) putc (*q++,file);
			       if (ljust) for (d=1; d<=d1; d++) putc(' ',file);
			       p= &t[0];
			       break;
                       case 'g':
		       case 'G':
			       if (ndigit>FLOATPRECLIMIT) ndigit=FLOATPRECLIMIT;
			       c1= c2= 0;
                               if (!dotfnd)
                                       ndigit = 6;
			       if (ndigit < 1) ndigit = 1;  /* g format specs */
                               gcvt(*((double *)argp), ndigit, p); argp +=2;
			       {   register char *base;
				   base= q= p;
				   if (*base=='I' || *base=='N') /* INF,NAN() */
				       zfill = ' ';
				   while (*p++ != 0);
			           if (*q != '-') {  /* value is nonnegative */
				       if (spacef || plus) {
					   q = p; 
				           *q = 0;
					   while (--q > base) *q = *(q-1);
					   *q = ((plus) ? '+': ' ');
					   ndigit++; /* precision filling */
				       }
				       else p -= 1;
				   }
				   else {
				       p -= 1;
				       ndigit++;  /* precision filling */
				   }
				   if ((*(p-4) == 'e') || (*(p-5) == 'e')) {
				       if (pound) {
					   if (*(p-5) == 'e') ndigit++;
					   c2= base + ndigit + 5 - p;
					   if ((*(base+1)!='.') &&
					       (*(base+2)!='.')) { 
					       c1= 1;
					       c2--;
					   }
				       }
				       if (d == 'G') {
					   if (*(p-4) == 'e') *(p-4) = 'E';
					   else *(p-5) = 'E';
				       }		   
			               q= &t[0];
			               d1= width - (c1 + c2 + p - q);
			               if ((!ljust) && (zfill==' '))
				           for (d=1; d<=d1; d++)
					       putc(zfill,file);
			               if ((*q=='+') || (*q=='-') || (*q==' '))
				           putc(*q++,file);
			               if ((!ljust) && (zfill=='0'))
				           for (d=1; d<=d1; d++)
					       putc(zfill,file);
			               while ((*q !='e') && (*q !='E') && (*q))
				           putc(*q++,file);
				       if (c1) putc('.',file);
			               for (d=1; d<=c2; d++) putc('0',file);
			               while (*q) putc(*q++,file);
			               if (ljust) for (d=1; d<=d1; d++)
				           putc(' ',file);
			               p= &t[0];
			               break;
				   }
				   else if ((pound) && (p < base+ndigit+1)) {
				       q = base;
				       while (*q++) {
					   if (*q == '.') {
					       ndigit++;
					       break;
					   }
				       }
				       if (*(q-1) == 0) *p++ = '.';
				       c1= base + ndigit - p + 1;
				       *p = 0;
				   }
			           q= &t[0];
			           d1= width - (c1 + p - q);
			           if ((!ljust) && (zfill==' '))
				       for (d=1; d<=d1; d++)
				   	   putc(zfill,file);
			           if ((*q=='+') || (*q=='-') || (*q==' '))
				       putc(*q++,file);
			           if ((!ljust) && (zfill=='0'))
				       for (d=1; d<=d1; d++) putc(zfill,file);
			           while (*q != 0) putc(*q++,file);
			           for (d=1; d<=c1; d++) putc ('0',file);
			           if (ljust)
				       for (d=1; d<=d1; d++) putc(' ',file);
			           p= &t[0];
			           break;
			       }
                       case '%':
                               *p++= '%';
                               goto prbuf;
                       }
       }
       if (p != &t[0])
               _strout(p-t, t, 0, file);
}
