* same with xv6
[mascara-docs.git] / i386 / ucla / src / lab2 / lib / printfmt.c
blob9f4c7cacccc1b279fde2d133728cedf4bb5ad406
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.
5 #include <inc/types.h>
6 #include <inc/stdio.h>
7 #include <inc/string.h>
8 #include <inc/stdarg.h>
9 #include <inc/error.h>
12 * Space or zero padding and a field width are supported for the numeric
13 * formats only.
15 * The special format %e takes an integer error code
16 * and prints a string describing the error.
17 * The integer may be positive or negative,
18 * so that -E_NO_MEM and E_NO_MEM are equivalent.
21 static const char * const error_string[MAXERROR + 1] =
23 NULL,
24 "unspecified error",
25 "bad environment",
26 "invalid parameter",
27 "out of memory",
28 "out of environments",
29 "segmentation fault",
33 * Print a number (base <= 16) in reverse order,
34 * using specified putch function and associated pointer putdat.
36 static void
37 printnum(void (*putch)(int, void*), void *putdat,
38 unsigned long long num, unsigned base, int width, int padc)
40 // first recursively print all preceding (more significant) digits
41 if (num >= base) {
42 printnum(putch, putdat, num / base, base, width - 1, padc);
43 } else {
44 // print any needed pad characters before first digit
45 while (--width > 0)
46 putch(padc, putdat);
49 // then print this (the least significant) digit
50 putch("0123456789abcdef"[num % base], putdat);
53 // Get an unsigned int of various possible sizes from a varargs list,
54 // depending on the lflag parameter.
55 static unsigned long long
56 getuint(va_list *ap, int lflag)
58 if (lflag >= 2)
59 return va_arg(*ap, unsigned long long);
60 else if (lflag)
61 return va_arg(*ap, unsigned long);
62 else
63 return va_arg(*ap, unsigned int);
66 // Same as getuint but signed - can't use getuint
67 // because of sign extension
68 static long long
69 getint(va_list *ap, int lflag)
71 if (lflag >= 2)
72 return va_arg(*ap, long long);
73 else if (lflag)
74 return va_arg(*ap, long);
75 else
76 return va_arg(*ap, int);
80 // Main function to format and print a string.
81 void printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...);
83 void
84 vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap)
86 register const char *p;
87 register int ch, err;
88 unsigned long long num;
89 int base, lflag, width, precision, altflag;
90 char padc;
92 while (1) {
93 while ((ch = *(unsigned char *) fmt++) != '%') {
94 if (ch == '\0')
95 return;
96 putch(ch, putdat);
99 // Process a %-escape sequence
100 padc = ' ';
101 width = -1;
102 precision = -1;
103 lflag = 0;
104 altflag = 0;
105 reswitch:
106 switch (ch = *(unsigned char *) fmt++) {
108 // flag to pad on the right
109 case '-':
110 padc = '-';
111 goto reswitch;
113 // flag to pad with 0's instead of spaces
114 case '0':
115 padc = '0';
116 goto reswitch;
118 // width field
119 case '1':
120 case '2':
121 case '3':
122 case '4':
123 case '5':
124 case '6':
125 case '7':
126 case '8':
127 case '9':
128 for (precision = 0; ; ++fmt) {
129 precision = precision * 10 + ch - '0';
130 ch = *fmt;
131 if (ch < '0' || ch > '9')
132 break;
134 goto process_precision;
136 case '*':
137 precision = va_arg(ap, int);
138 goto process_precision;
140 case '.':
141 if (width < 0)
142 width = 0;
143 goto reswitch;
145 case '#':
146 altflag = 1;
147 goto reswitch;
149 process_precision:
150 if (width < 0)
151 width = precision, precision = -1;
152 goto reswitch;
154 // long flag (doubled for long long)
155 case 'l':
156 lflag++;
157 goto reswitch;
159 // character
160 case 'c':
161 putch(va_arg(ap, int), putdat);
162 break;
164 // error message
165 case 'e':
166 err = va_arg(ap, int);
167 if (err < 0)
168 err = -err;
169 if (err > MAXERROR || (p = error_string[err]) == NULL)
170 printfmt(putch, putdat, "error %d", err);
171 else
172 printfmt(putch, putdat, "%s", p);
173 break;
175 // string
176 case 's':
177 if ((p = va_arg(ap, char *)) == NULL)
178 p = "(null)";
179 if (width > 0 && padc != '-')
180 for (width -= strnlen(p, precision); width > 0; width--)
181 putch(padc, putdat);
182 for (; (ch = *p++) != '\0' && (precision < 0 || --precision >= 0); width--)
183 if (altflag && (ch < ' ' || ch > '~'))
184 putch('?', putdat);
185 else
186 putch(ch, putdat);
187 for (; width > 0; width--)
188 putch(' ', putdat);
189 break;
191 // (signed) decimal
192 case 'd':
193 num = getint(&ap, lflag);
194 if ((long long) num < 0) {
195 putch('-', putdat);
196 num = -(long long) num;
198 base = 10;
199 goto number;
201 // unsigned decimal
202 case 'u':
203 num = getuint(&ap, lflag);
204 base = 10;
205 goto number;
207 // (unsigned) octal
208 case 'o':
209 // Replace this with your code.
210 putch('X', putdat);
211 putch('X', putdat);
212 putch('X', putdat);
213 break;
215 // pointer
216 case 'p':
217 putch('0', putdat);
218 putch('x', putdat);
219 num = (unsigned long long)
220 (uintptr_t) va_arg(ap, void *);
221 base = 16;
222 goto number;
224 // (unsigned) hexadecimal
225 case 'x':
226 num = getuint(&ap, lflag);
227 base = 16;
228 number:
229 printnum(putch, putdat, num, base, width, padc);
230 break;
232 // escaped '%' character
233 case '%':
234 putch(ch, putdat);
235 break;
237 // unrecognized escape sequence - just print it literally
238 default:
239 putch('%', putdat);
240 for (fmt--; fmt[-1] != '%'; fmt--)
241 /* do nothing */;
242 break;
247 void
248 printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...)
250 va_list ap;
252 va_start(ap, fmt);
253 vprintfmt(putch, putdat, fmt, ap);
254 va_end(ap);
257 struct sprintbuf {
258 char *buf;
259 char *ebuf;
260 int cnt;
263 static void
264 sprintputch(int ch, void *ptr)
266 struct sprintbuf *b = (struct sprintbuf *) ptr;
267 b->cnt++;
268 if (b->buf < b->ebuf)
269 *b->buf++ = ch;
273 vsnprintf(char *buf, int n, const char *fmt, va_list ap)
275 struct sprintbuf b = {buf, buf+n-1, 0};
277 if (buf == NULL || n < 1)
278 return -E_INVAL;
280 // print the string to the buffer
281 vprintfmt(sprintputch, &b, fmt, ap);
283 // null terminate the buffer
284 *b.buf = '\0';
286 return b.cnt;
290 snprintf(char *buf, int n, const char *fmt, ...)
292 va_list ap;
293 int rc;
295 va_start(ap, fmt);
296 rc = vsnprintf(buf, n, fmt, ap);
297 va_end(ap);
299 return rc;