date and time created 80/07/31 23:01:16 by mark
authorMark Horton <mark@ucbvax.Berkeley.EDU>
Fri, 1 Aug 1980 14:01:16 +0000 (06:01 -0800)
committerMark Horton <mark@ucbvax.Berkeley.EDU>
Fri, 1 Aug 1980 14:01:16 +0000 (06:01 -0800)
SCCS-vsn: usr.bin/ex/printf.c 1.1

usr/src/usr.bin/ex/printf.c [new file with mode: 0644]

diff --git a/usr/src/usr.bin/ex/printf.c b/usr/src/usr.bin/ex/printf.c
new file mode 100644 (file)
index 0000000..be24ebe
--- /dev/null
@@ -0,0 +1,340 @@
+/* char printf_id[] = "@(#) printf.c:2.2 6/5/79";*/
+#include "varargs.h"
+/*
+ * 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;
+
+char *_p_dconv();
+
+printf(va_alist)
+       va_dcl
+{
+       va_list ap;
+       register char *fmt;
+       char fcode;
+       int prec;
+       int length,mask1,nbits,n;
+       long int mask2, num;
+       register char *bptr;
+       char *ptr;
+       char buf[134];
+
+       va_start(ap);
+       fmt = va_arg(ap,char *);
+       for (;;) {
+               /* process format string first */
+               while ((fcode = *fmt++)!='%') {
+                       /* ordinary (non-%) character */
+                       if (fcode=='\0')
+                               return;
+                       putchar(fcode);
+               }
+               /* 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,char *);
+                               if (bptr==0)
+                                       bptr = "(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);
+       }
+       va_end(ap);
+}
+
+/* _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.
+ */
+char *
+_p_dconv(value, buffer)
+       long value;
+       char *buffer;
+{
+       register 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 char *s;
+       char *send;
+{
+       char cfill;
+       register int alen;
+       int npad;
+       
+       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)
+               putchar(*s++);
+               
+       /* emit trailing pad characters */
+       if (sign)
+               while (--npad >= 0)
+                       putchar(cfill);
+}