printfmt.c - vx32 - Local 9vx git repository for patches.
(HTM) git clone git://r-36.net/vx32
(DIR) Log
(DIR) Files
(DIR) 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