vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

printfmt.c (3746B)


      1 // Stripped-down primitive printf-style formatting routines,
      2 // used in common by printf, sprintf, fprintf, etc.
      3 // This code is also used by both the kernel and user programs.
      4 //
      5 // Space or zero padding and a field width are supported for the numeric
      6 // formats only. 
      7 
      8 
      9 #include <stdio.h>
     10 #include <stdarg.h>
     11 #include <inttypes.h>
     12 
     13 #include "ioprivate.h"
     14 
     15 
     16 typedef unsigned char		uchar;
     17 typedef unsigned long long	ull;
     18 
     19 /*
     20  * Print a number (base <= 16) in reverse order,
     21  * using specified putch function and associated pointer putdat.
     22  */
     23 static int
     24 printnum(int (*putch)(int, void*), void *putdat,
     25 	 ull num, unsigned base, int width, int padc)
     26 {
     27 	int nout;
     28 
     29 	// first recursively print all preceding (more significant) digits
     30 	if (num >= base) {
     31 		nout = printnum(putch, putdat, num / base, base, width-1,
     32 				padc);
     33 		if (nout < 0)
     34 			return -1;
     35 	} else {
     36 		// print any needed pad characters before first digit
     37 		nout = 0;
     38 		while (--width > 0) {
     39 			if (putch(padc, putdat) < 0)
     40 				return -1;
     41 			nout++;
     42 		}
     43 	}
     44 
     45 	// then print this (the least significant) digit
     46 	if (putch("0123456789abcdef"[num % base], putdat) < 0)
     47 		return -1;
     48 	nout++;
     49 
     50 	return nout;
     51 }
     52 
     53 // Main function to format and print a string.
     54 int
     55 vprintfmt(int (*putch)(int, void*), void *putdat, const char *fmt, va_list ap)
     56 {
     57 	register char *p;
     58 	register int ch;
     59 	int nout = 0;
     60 
     61 	for (;;) {
     62 		while ((ch = *(uchar *) fmt++) != '%') {
     63 			if (ch == '\0')
     64 				return nout;
     65 			if (putch(ch, putdat) < 0)
     66 				return -1;
     67 			nout++;
     68 		}
     69 
     70 		// Process a %-escape sequence
     71 		int padc = ' ';
     72 		int width = 0;
     73 		int length = 0;
     74 		int base = 10;
     75 		ull num;
     76 
     77 	reswitch:
     78 		switch (ch = *(uchar *) fmt++) {
     79 
     80 		// flag to pad with 0's instead of spaces
     81 		case '0':
     82 			padc = '0';
     83 			goto reswitch;
     84 
     85 		// width field
     86 		case '1':
     87 		case '2':
     88 		case '3':
     89 		case '4':
     90 		case '5':
     91 		case '6':
     92 		case '7':
     93 		case '8':
     94 		case '9':
     95 			for (width = 0;; ++fmt) {
     96 				width = width * 10 + ch - '0';
     97 				ch = *fmt;
     98 				if (ch < '0' || ch > '9')
     99 					break;
    100 			}
    101 			goto reswitch;
    102 
    103 		case 'l':
    104 			length++;
    105 			goto reswitch;
    106 
    107 		// character
    108 		case 'c':
    109 			if (putch(va_arg(ap, int), putdat) < 0)
    110 				return -1;
    111 			nout++;
    112 			break;
    113 
    114 		// string
    115 		case 's':
    116 			if ((p = va_arg(ap, char *)) == NULL)
    117 					p = "(null)";
    118 			while ((ch = *p++) != '\0') {
    119 				if (putch(ch, putdat) < 0)
    120 					return -1;
    121 				nout++;
    122 			}
    123 			break;
    124 
    125 		// signed decimal
    126 		case 'd': ;
    127 			// Pick up an appropriate-sized signed input value
    128 			long long snum;
    129 			if (length == 0)
    130 				snum = va_arg(ap, int);
    131 			else if (length == 1)
    132 				snum = va_arg(ap, long);
    133 			else
    134 				snum = va_arg(ap, long long);
    135 
    136 			// Output the minus sign if appropriate
    137 			if (snum < 0) {
    138 				if (putch('-', putdat) < 0)
    139 					return -1;
    140 				nout++;
    141 				num = -snum;
    142 			} else
    143 				num = snum;
    144 			goto number;
    145 
    146 		// unsigned hexadecimal
    147 		case 'x':
    148 			base = 16;
    149 			// fall thru...
    150 
    151 		// unsigned decimal
    152 		case 'u':
    153 			// Pick up the appropriate-sized input argument
    154 			if (length == 0)
    155 				num = va_arg(ap, unsigned);
    156 			else if (length == 1)
    157 				num = va_arg(ap, unsigned long);
    158 			else
    159 				num = va_arg(ap, unsigned long long);
    160 
    161 		number: ;
    162 			// Print the number in the appropriate base
    163 			int rc = printnum(putch, putdat, num, base, width,
    164 						padc);
    165 			if (rc < 0)
    166 				return -1;
    167 			nout += rc;
    168 			break;
    169 
    170 		// unrecognized escape sequence - just print it literally
    171 		default:
    172 			if (putch('%', putdat) < 0)
    173 				return -1;
    174 			nout++;
    175 			/* FALLTHROUGH */
    176 
    177 		// escaped '%' character
    178 		case '%':
    179 			if (putch(ch, putdat) < 0)
    180 				return -1;
    181 			nout++;
    182 		}
    183 	}
    184 }
    185 
    186 int
    187 printfmt(int (*putch)(int, void*), void *putdat, const char *fmt, ...)
    188 {
    189 	va_list ap;
    190 	va_start(ap, fmt);
    191 	int rc = vprintfmt(putch, putdat, fmt, ap);
    192 	va_end(ap);
    193 	return rc;
    194 }
    195