414 lines
9.0 KiB
C
414 lines
9.0 KiB
C
/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
|
|
/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
|
|
/* All Rights Reserved */
|
|
|
|
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF */
|
|
/* UNIX System Laboratories, Inc. */
|
|
/* The copyright notice above does not evidence any */
|
|
/* actual or intended publication of such source code. */
|
|
|
|
/* The pwb version this is based on */
|
|
static char *printf_id = "@(#) printf.c:2.2 6/5/79";
|
|
/* The local sccs version within ex */
|
|
#ident "@(#)vi:port/printf.c 1.6.1.2"
|
|
#include <varargs.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
extern short putoctal;
|
|
/*
|
|
* This version of printf is compatible with the Version 7 C
|
|
* printf. The differences are only minor except that this
|
|
* printf assumes it is to print through putchar. Version 7
|
|
* printf is more general (and is much larger) and includes
|
|
* provisions for floating point.
|
|
*/
|
|
|
|
|
|
#define MAXOCT 11 /* Maximum octal digits in a long */
|
|
#define MAXINT 32767 /* largest normal length positive integer */
|
|
#define BIG 1000000000 /* largest power of 10 less than an unsigned long */
|
|
#define MAXDIGS 10 /* number of digits in BIG */
|
|
|
|
static int width, sign, fill;
|
|
|
|
unsigned char *_p_dconv();
|
|
|
|
mprintf(id, fmt, va_alist)
|
|
char *id, *fmt;
|
|
va_dcl
|
|
{
|
|
va_list args;
|
|
va_start(args);
|
|
_printf(mesg(gettxt(id, fmt)), args);
|
|
va_end(args);
|
|
}
|
|
|
|
gprintf(id, fmt, va_alist)
|
|
char *id, *fmt;
|
|
va_dcl
|
|
{
|
|
va_list args;
|
|
va_start(args);
|
|
_printf(gettxt(id, fmt), args);
|
|
va_end(args);
|
|
}
|
|
|
|
printf(fmt, va_alist)
|
|
char *fmt;
|
|
va_dcl
|
|
{
|
|
va_list ap;
|
|
va_start(ap);
|
|
_printf(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
_printf(fmt, ap)
|
|
char *fmt;
|
|
va_list ap;
|
|
{
|
|
unsigned char fcode;
|
|
int prec;
|
|
int length,mask1,nbits,n;
|
|
long int mask2, num;
|
|
register unsigned char *bptr;
|
|
unsigned char *ptr;
|
|
unsigned char buf[134];
|
|
|
|
/* New variables required by BUG FIX. */
|
|
wchar_t wc_fcode;
|
|
int ret;
|
|
|
|
for (;;) {
|
|
/* process format string first */
|
|
ret = mbtowc(&wc_fcode, fmt, MB_LEN_MAX);
|
|
while( wc_fcode != '%' ) {
|
|
/* ordinary (non-%) character */
|
|
if( ret == 0 )
|
|
return;
|
|
if( ret == -1 ) {
|
|
/* illegal multi byte char. What to do?
|
|
Print Octal value */
|
|
putoctal = 1;
|
|
putchar((unsigned char)*fmt++);
|
|
putoctal = 0;
|
|
}
|
|
else {
|
|
/* Good. We have a valid wide char */
|
|
fmt += ret;
|
|
putchar( wc_fcode );
|
|
}
|
|
ret = mbtowc(&wc_fcode, fmt, MB_LEN_MAX);
|
|
}
|
|
fmt++;
|
|
fcode = wc_fcode; /* This is quite harmless */
|
|
|
|
/* length modifier: -1 for h, 1 for l, 0 for none */
|
|
length = 0;
|
|
/* check for a leading - sign */
|
|
sign = 0;
|
|
if (*fmt == '-') {
|
|
sign++;
|
|
fmt++;
|
|
}
|
|
/* a '0' may follow the - sign */
|
|
/* this is the requested fill character */
|
|
fill = 1;
|
|
if (*fmt == '0') {
|
|
fill--;
|
|
fmt++;
|
|
}
|
|
|
|
/* Now comes a digit string which may be a '*' */
|
|
if (*fmt == '*') {
|
|
width = va_arg(ap, int);
|
|
if (width < 0) {
|
|
width = -width;
|
|
sign = !sign;
|
|
}
|
|
fmt++;
|
|
}
|
|
else {
|
|
width = 0;
|
|
while (*fmt>='0' && *fmt<='9')
|
|
width = width * 10 + (*fmt++ - '0');
|
|
}
|
|
|
|
/* maybe a decimal point followed by more digits (or '*') */
|
|
if (*fmt=='.') {
|
|
if (*++fmt == '*') {
|
|
prec = va_arg(ap, int);
|
|
fmt++;
|
|
}
|
|
else {
|
|
prec = 0;
|
|
while (*fmt>='0' && *fmt<='9')
|
|
prec = prec * 10 + (*fmt++ - '0');
|
|
}
|
|
}
|
|
else
|
|
prec = -1;
|
|
|
|
/*
|
|
* At this point, "sign" is nonzero if there was
|
|
* a sign, "fill" is 0 if there was a leading
|
|
* zero and 1 otherwise, "width" and "prec"
|
|
* contain numbers corresponding to the digit
|
|
* strings before and after the decimal point,
|
|
* respectively, and "fmt" addresses the next
|
|
* character after the whole mess. If there was
|
|
* no decimal point, "prec" will be -1.
|
|
*/
|
|
switch (*fmt) {
|
|
case 'L':
|
|
case 'l':
|
|
length = 2;
|
|
/* no break!! */
|
|
case 'h':
|
|
case 'H':
|
|
length--;
|
|
fmt++;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* At exit from the following switch, we will
|
|
* emit the characters starting at "bptr" and
|
|
* ending at "ptr"-1, unless fcode is '\0'.
|
|
*/
|
|
switch (fcode = *fmt++) {
|
|
/* process characters and strings first */
|
|
case 'c':
|
|
buf[0] = va_arg(ap, int);
|
|
ptr = bptr = &buf[0];
|
|
if (buf[0] != '\0')
|
|
ptr++;
|
|
break;
|
|
case 's':
|
|
bptr = va_arg(ap,unsigned char *);
|
|
if (bptr==0)
|
|
bptr = (unsigned char *)
|
|
gettxt(":270", "(null pointer)");
|
|
if (prec < 0)
|
|
prec = MAXINT;
|
|
for (n=0; *bptr++ && n < prec; n++) ;
|
|
ptr = --bptr;
|
|
bptr -= n;
|
|
break;
|
|
case 'O':
|
|
length = 1;
|
|
fcode = 'o';
|
|
/* no break */
|
|
case 'o':
|
|
case 'X':
|
|
case 'x':
|
|
if (length > 0)
|
|
num = va_arg(ap,long);
|
|
else
|
|
num = (unsigned)va_arg(ap,int);
|
|
if (fcode=='o') {
|
|
mask1 = 0x7;
|
|
mask2 = 0x1fffffffL;
|
|
nbits = 3;
|
|
}
|
|
else {
|
|
mask1 = 0xf;
|
|
mask2 = 0x0fffffffL;
|
|
nbits = 4;
|
|
}
|
|
n = (num!=0);
|
|
bptr = buf + MAXOCT + 3;
|
|
/* shift and mask for speed */
|
|
do
|
|
if (((int) num & mask1) < 10)
|
|
*--bptr = ((int) num & mask1) + 060;
|
|
else
|
|
*--bptr = ((int) num & mask1) + 0127;
|
|
while (num = (num >> nbits) & mask2);
|
|
|
|
if (fcode=='o') {
|
|
if (n)
|
|
*--bptr = '0';
|
|
}
|
|
else
|
|
if (!sign && fill <= 0) {
|
|
putchar('0');
|
|
putchar(fcode);
|
|
width -= 2;
|
|
}
|
|
else {
|
|
*--bptr = fcode;
|
|
*--bptr = '0';
|
|
}
|
|
ptr = buf + MAXOCT + 3;
|
|
break;
|
|
case 'D':
|
|
case 'U':
|
|
case 'I':
|
|
length = 1;
|
|
fcode = fcode + 'a' - 'A';
|
|
/* no break */
|
|
case 'd':
|
|
case 'i':
|
|
case 'u':
|
|
if (length > 0)
|
|
num = va_arg(ap,long);
|
|
else {
|
|
n = va_arg(ap,int);
|
|
if (fcode=='u')
|
|
num = (unsigned) n;
|
|
else
|
|
num = (long) n;
|
|
}
|
|
if (n = (fcode != 'u' && num < 0))
|
|
num = -num;
|
|
/* now convert to digits */
|
|
bptr = _p_dconv(num, buf);
|
|
if (n)
|
|
*--bptr = '-';
|
|
if (fill == 0)
|
|
fill = -1;
|
|
ptr = buf + MAXDIGS + 1;
|
|
break;
|
|
default:
|
|
/* not a control character,
|
|
* print it.
|
|
*/
|
|
ptr = bptr = &fcode;
|
|
ptr++;
|
|
break;
|
|
}
|
|
if (fcode != '\0')
|
|
_p_emit(bptr,ptr);
|
|
}
|
|
}
|
|
|
|
/* _p_dconv converts the unsigned long integer "value" to
|
|
* printable decimal and places it in "buffer", right-justified.
|
|
* The value returned is the address of the first non-zero character,
|
|
* or the address of the last character if all are zero.
|
|
* The result is NOT null terminated, and is MAXDIGS characters long,
|
|
* starting at buffer[1] (to allow for insertion of a sign).
|
|
*
|
|
* This program assumes it is running on 2's complement machine
|
|
* with reasonable overflow treatment.
|
|
*/
|
|
unsigned char *
|
|
_p_dconv(value, buffer)
|
|
long value;
|
|
unsigned char *buffer;
|
|
{
|
|
register unsigned char *bp;
|
|
register int svalue;
|
|
int n;
|
|
long lval;
|
|
|
|
bp = buffer;
|
|
|
|
/* zero is a special case */
|
|
if (value == 0) {
|
|
bp += MAXDIGS;
|
|
*bp = '0';
|
|
return(bp);
|
|
}
|
|
|
|
/* develop the leading digit of the value in "n" */
|
|
n = 0;
|
|
while (value < 0) {
|
|
value -= BIG; /* will eventually underflow */
|
|
n++;
|
|
}
|
|
while ((lval = value - BIG) >= 0) {
|
|
value = lval;
|
|
n++;
|
|
}
|
|
|
|
/* stash it in buffer[1] to allow for a sign */
|
|
bp[1] = n + '0';
|
|
/*
|
|
* Now develop the rest of the digits. Since speed counts here,
|
|
* we do it in two loops. The first gets "value" down until it
|
|
* is no larger than MAXINT. The second one uses integer divides
|
|
* rather than long divides to speed it up.
|
|
*/
|
|
bp += MAXDIGS + 1;
|
|
while (value > MAXINT) {
|
|
*--bp = (int)(value % 10) + '0';
|
|
value /= 10;
|
|
}
|
|
|
|
/* cannot lose precision */
|
|
svalue = value;
|
|
while (svalue > 0) {
|
|
*--bp = (svalue % 10) + '0';
|
|
svalue /= 10;
|
|
}
|
|
|
|
/* fill in intermediate zeroes if needed */
|
|
if (buffer[1] != '0') {
|
|
while (bp > buffer + 2)
|
|
*--bp = '0';
|
|
--bp;
|
|
}
|
|
return(bp);
|
|
}
|
|
|
|
/*
|
|
* This program sends string "s" to putchar. The character after
|
|
* the end of "s" is given by "send". This allows the size of the
|
|
* field to be computed; it is stored in "alen". "width" contains the
|
|
* user specified length. If width<alen, the width will be taken to
|
|
* be alen. "sign" is zero if the string is to be right-justified
|
|
* in the field, nonzero if it is to be left-justified. "fill" is
|
|
* 0 if the string is to be padded with '0', positive if it is to be
|
|
* padded with ' ', and negative if an initial '-' should appear before
|
|
* any padding in right-justification (to avoid printing "-3" as
|
|
* "000-3" where "-0003" was intended).
|
|
*/
|
|
_p_emit(s, send)
|
|
register unsigned char *s;
|
|
unsigned char *send;
|
|
{
|
|
unsigned char cfill;
|
|
register int alen;
|
|
int npad, length;
|
|
wchar_t wchar;
|
|
|
|
alen = send - s;
|
|
if (alen > width)
|
|
width = alen;
|
|
cfill = fill>0? ' ': '0';
|
|
|
|
/* we may want to print a leading '-' before anything */
|
|
if (*s == '-' && fill < 0) {
|
|
putchar(*s++);
|
|
alen--;
|
|
width--;
|
|
}
|
|
npad = width - alen;
|
|
|
|
/* emit any leading pad characters */
|
|
if (!sign)
|
|
while (--npad >= 0)
|
|
putchar(cfill);
|
|
|
|
/* emit the string itself */
|
|
while (--alen >= 0) {
|
|
length = mbtowc(&wchar, (char *)s, MB_LEN_MAX);
|
|
if(length <= 0) {
|
|
putoctal = 1;
|
|
putchar((unsigned char)*s++);
|
|
putoctal = 0;
|
|
} else {
|
|
putchar(wchar);
|
|
s += length;
|
|
alen = alen - length + 1;
|
|
}
|
|
}
|
|
/* emit trailing pad characters */
|
|
if (sign)
|
|
while (--npad >= 0)
|
|
putchar(cfill);
|
|
}
|