/* * Alas, doprnt is not documented, so people feel they have license to * change the parameters. The 3B implementation does not even look * callable from C, and without documentation is not comprehensible. * Since many systems have their own doprnt which is written in assembly * language, and thus faster, but callable from C, we try to use the * standard system version where possible. The ifdefs below attempt * to decide where it is possible - it may need to be updated later. */ #ifdef u3b #define NEEDDOPRNT #endif #ifdef NEEDDOPRNT /* @(#) doprnt.c: 1.1 10/15/83 (1.4 10/27/82) */ /* @(#)doprnt.c 2.4 */ /*LINTLIBRARY*/ /* * _doprnt: common code for printf, fprintf, sprintf */ #include <stdio.h> #include <ctype.h> #include <varargs.h> #include <values.h> /* @(#)print.h 1.3 */ /* Maximum number of digits in any integer representation */ #define MAXDIGS 11 /* Maximum total number of digits in E format */ #define MAXECVT 17 /* Maximum number of digits after decimal point in F format */ #define MAXFCVT 60 /* Maximum significant figures in a floating-point number */ #define MAXFSIG 17 /* Maximum number of characters in an exponent */ #if u3b #define MAXESIZ 5 #else #define MAXESIZ 4 #endif /* Maximum (positive) exponent */ #if u3b #define MAXEXP 310 #else #define MAXEXP 40 #endif /* Data type for flags */ typedef char bool; /* Convert a digit character to the corresponding number */ #define tonumber(x) ((x)-'0') /* Convert a number between 0 and 9 to the corresponding digit */ #define todigit(x) ((x)+'0') /* Max and Min macros */ #define max(a,b) ((a) > (b)? (a): (b)) #define min(a,b) ((a) < (b)? (a): (b)) /* end of print.h */ static char * memcpy(dest, src, n) register char *dest, *src; register int n; { register char *rv = dest; while (n--) *dest++ = *src++; return rv; } #define PUT(p, n) if (n == 1) (void) putc(*p, iop); \ else if (iop->_file == _NFILE) \ iop->_ptr = (unsigned char *) \ memcpy((char *)iop->_ptr, p, n) +n; \ else (void) fwrite(p, 1, n, iop) /* * C-Library routines for floating conversion */ extern char *fcvt(), *ecvt(); extern int strlen(), fwrite(); int _doprnt(format, args, iop) register char *format; va_list args; register FILE *iop; { /* This variable counts output characters. */ int count = 0; /* Starting and ending points for value to be printed */ register char *bp, *p; /* Field width and precision */ int width, prec; /* Format code */ register int fcode; /* Number of padding zeroes required on the left and right */ int lzero, rzero; /* Flags - nonzero if corresponding character is in format */ int length; /* l */ int fplus; /* + */ int fminus; /* - */ int fblank; /* blank */ int fsharp; /* # */ /* Values are developed in this buffer */ char buf[max(MAXDIGS, 1+max(MAXFCVT+MAXEXP, MAXECVT))]; /* Pointer to sign, "0x", "0X", or empty */ char *prefix; /* Exponent or empty */ char *suffix; /* Buffer to create exponent */ char expbuf[MAXESIZ + 1]; /* The value being converted, if integer */ long val; /* The value being converted, if real */ double dval; /* Output values from fcvt and ecvt */ int decpt, sign; /* Pointer to a translate table for digits of whatever radix */ char *tab; /* Work variables */ int k, n, hradix, lowbit; expbuf[MAXESIZ] = '\0'; /* * The main loop -- this loop goes through one iteration * for each string of ordinary characters or format specification. */ for ( ; ; ) { for (bp = format; (fcode = *format) != '\0' && fcode != '%'; format++) ; if (n = format - bp) { /* ordinary non-% characters */ count += n; PUT(bp, n); } if (fcode == '\0') /* end of format; normal return */ return (ferror(iop) ? EOF : count); /* * % has been found. * First, parse the format specification. */ fplus = fminus = fblank = fsharp = lzero = 0; for ( ; ; ) { /* Scan the <flags> */ switch (fcode = *++format) { case '+': fplus++; continue; case '-': fminus++; continue; case ' ': fblank++; continue; case '#': fsharp++; continue; } break; } /* Scan the field width */ if (fcode == '*') { width = va_arg(args, int); if (width < 0) { width = -width; fminus++; } format++; } else { if (fcode == '0') /* obsolescent spec. */ lzero++; /* pad with leading zeros */ for (width = 0; isdigit(fcode = *format); format++) width = width * 10 + tonumber(fcode); } /* Scan the precision */ if (*format != '.') prec = lzero ? width : -1; else if (*++format == '*') { /* '*' instead of digits? */ prec = va_arg(args, int); format++; } else for (prec = 0; isdigit(fcode = *format); format++) prec = prec * 10 + tonumber(fcode); /* Scan the length modifier */ length = 0; switch (*format) { case 'l': length++; /* No break */ case 'h': format++; } /* * The character addressed by format must be * the format letter -- there is nothing * left for it to be. * * The status of the +, -, #, and blank * flags are reflected in the variables * "fplus", "fminus", "fsharp", and * "fblank". "width" and "prec" contain * numbers corresponding to the digit * strings before and after the decimal * point, respectively. If there was no * decimal point, "prec" is -1. * * The following switch sets things up * for printing. What ultimately gets * printed will be padding blanks, a * prefix, left padding zeroes, a value, * right padding zeroes, a suffix, and * more padding blanks. Padding blanks * will not appear simultaneously on both * the left and the right. Each case in * this switch will compute the value, and * leave in several variables the informa- * tion necessary to construct what is to * be printed. * * The prefix is a sign, a blank, "0x", * "0X", or null, and is addressed by * "prefix". * * The suffix is either null or an * exponent, and is addressed by "suffix". * * The value to be printed starts at "bp" * and continues up to and not including * "p". * * "lzero" and "rzero" will contain the * number of padding zeroes required on * the left and right, respectively. If * either of these variables is negative, * it will be treated as if it were zero. * * The number of padding blanks, and * whether they go on the left or the * right, will be computed on exit from * the switch. */ prefix = ""; suffix = &expbuf[MAXESIZ]; lzero = rzero = 0; switch (fcode = *format++) { /* * fixed point representations * * "hradix" is half the radix for the * conversion. Conversion is unsigned * unless fcode is 'd'. HIBITL is 100...000 * binary, and is equal to the maximum * negative number. * We assume a 2's complement machine */ case 'd': case 'u': hradix = 10/2; goto fixed; case 'o': hradix = 8/2; goto fixed; case 'X': case 'x': hradix = 16/2; fixed: /* Establish default precision */ if (prec < 0) prec = 1; /* Fetch the argument to be printed */ if(length) val = va_arg(args, long); else if(fcode == 'd') val = va_arg(args, int); else val = va_arg(args, unsigned); /* If signed conversion, make sign */ if(fcode == 'd') { if(val < 0) { prefix = "-"; /* * Negate, checking in * advance for possible * overflow. */ if(val != HIBITL) val = -val; } else if (fplus) prefix = "+"; else if (fblank) prefix = " "; } /* Set translate table for digits */ tab = (fcode == 'X') ? "0123456789ABCDEF" : "0123456789abcdef"; /* Develop the digits of the value */ p = bp = buf + MAXDIGS; while (val != 0) { lowbit = val & 1; val = (val >> 1) & ~HIBITL; *--bp = tab[val % hradix * 2 + lowbit]; val /= hradix; } /* Calculate padding zero requirement */ lzero = bp - p + prec; /* Handle the # flag */ if (fsharp && bp != p) switch (fcode) { case 'o': if (lzero < 1) lzero = 1; break; case 'x': prefix = "0x"; break; case 'X': prefix = "0X"; break; } break; case 'E': case 'e': /* * E-format. The general strategy * here is fairly easy: we take * what ecvt gives us and re-format it. */ /* Establish default precision */ if (prec < 0) prec = 6; /* Fetch the value */ dval = va_arg(args, double); /* Develop the mantissa */ bp = ecvt(dval, min(prec + 1, MAXECVT), &decpt, &sign); /* Determine the prefix */ e_merge: if (sign) prefix = "-"; else if (fplus) prefix = "+"; else if (fblank) prefix = " "; /* Place the first digit in the buffer*/ p = &buf[0]; *p++ = (*bp != '\0') ? *bp++ : '0'; /* Put in a decimal point if needed */ if (prec != 0 || fsharp) *p++ = '.'; /* Create the rest of the mantissa */ for (rzero = prec; rzero > 0 && *bp != '\0'; --rzero) *p++ = *bp++; bp = &buf[0]; /* Create the exponent */ if (dval != 0) { n = decpt - 1; if (n < 0) n = -n; for ( ; n != 0; n /= 10) *--suffix = todigit(n % 10); } /* Prepend leading zeroes to the exponent */ while (suffix > &expbuf[MAXESIZ - 2]) *--suffix = '0'; /* Put in the exponent sign */ *--suffix = (decpt > 0 || dval == 0) ? '+' : '-'; /* Put in the e */ *--suffix = isupper(fcode) ? 'E' : 'e'; break; case 'f': /* * F-format floating point. This is a * good deal less simple than E-format. * The overall strategy will be to call * fcvt, reformat its result into buf, * and calculate how many trailing * zeroes will be required. There will * never be any leading zeroes needed. */ /* Establish default precision */ if (prec < 0) prec = 6; /* Fetch the value */ dval = va_arg(args, double); /* Do the conversion */ bp = fcvt(dval, min(prec, MAXFCVT), &decpt, &sign); /* Determine the prefix */ f_merge: if (sign && decpt > -prec && *bp != '0') prefix = "-"; else if (fplus) prefix = "+"; else if (fblank) prefix = " "; /* Initialize buffer pointer */ p = &buf[0]; /* Emit the digits before the decimal point */ n = decpt; k = 0; do { *p++ = (n <= 0 || *bp == '\0' || k >= MAXFSIG) ? '0' : (k++, *bp++); } while (--n > 0); /* Decide whether we need a decimal point */ if (fsharp || prec > 0) *p++ = '.'; /* Digits (if any) after the decimal point */ n = min(prec, MAXFCVT); rzero = prec - n; while (--n >= 0) *p++ = (++decpt <= 0 || *bp == '\0' || k >= MAXFSIG) ? '0' : (k++, *bp++); bp = &buf[0]; break; case 'G': case 'g': /* * g-format. We play around a bit * and then jump into e or f, as needed. */ /* Establish default precision */ if (prec < 0) prec = 6; /* Fetch the value */ dval = va_arg(args, double); /* Do the conversion */ bp = ecvt(dval, min(prec, MAXECVT), &decpt, &sign); if (dval == 0) decpt = 1; k = prec; if (!fsharp) { n = strlen(bp); if (n < k) k = n; while (k >= 1 && bp[k-1] == '0') --k; } if (decpt < -3 || decpt > prec) { prec = k - 1; goto e_merge; } prec = k - decpt; goto f_merge; /* case '%': */ default: buf[0] = fcode; goto c_merge; case 'c': buf[0] = va_arg(args, int); c_merge: p = (bp = &buf[0]) + 1; break; case 's': p = bp = va_arg(args, char *); if (prec < 0) p += strlen(bp); else { /* a strnlen function would be useful here! */ while (*p++ != '\0' && --prec >= 0) ; --p; } break; case '\0': /* unexpected end of format; return error */ return (EOF); } /* Calculate number of padding blanks */ if (lzero < 0) lzero = 0; if (rzero < 0) rzero = 0; k = (n = p - bp) + lzero + rzero + (prefix[0] == '\0' ? 0 : prefix[1] == '\0' ? 1 : 2) + (&expbuf[MAXESIZ] - suffix); count += (width > k) ? width : k; /* Blanks on left if required */ if (!fminus) while (--width >= k) (void) putc(' ', iop); /* Prefix, if any */ while (*prefix != '\0') (void) putc(*prefix++, iop); /* Zeroes on the left */ while (--lzero >= 0) (void) putc('0', iop); /* The value itself */ if (n > 0) PUT(bp, n); /* Zeroes on the right */ while (--rzero >= 0) (void) putc('0', iop); /* The suffix */ while (*suffix != '\0') (void) putc(*suffix++, iop); /* Blanks on the right if required */ while (--width >= k) (void) putc(' ', iop); } } #else /* This is just to keep from getting a diagnostic from ranlib. */ __dpdummy__() { } #endif NEEDDOPRNT