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