clock interrupt mechanism added.
[mit-jos.git] / lib / printfmt.c
blobab05c04b006dc8f8fa543184ae6157594ad25292
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",
30 "env is not recving",
31 "unexpected end of file",
35 * Print a number (base <= 16) in reverse order,
36 * using specified putch function and associated pointer putdat.
38 static void
39 printnum(void (*putch)(int, void*), void *putdat,
40 unsigned long long num, unsigned base, int width, int padc)
42 // first recursively print all preceding (more significant) digits
43 if (num >= base) {
44 printnum(putch, putdat, num / base, base, width - 1, padc);
45 } else {
46 // print any needed pad characters before first digit
47 while (--width > 0)
48 putch(padc, putdat);
51 // then print this (the least significant) digit
52 putch("0123456789abcdef"[num % base], putdat);
55 // Get an unsigned int of various possible sizes from a varargs list,
56 // depending on the lflag parameter.
57 static unsigned long long
58 getuint(va_list *ap, int lflag)
60 if (lflag >= 2)
61 return va_arg(*ap, unsigned long long);
62 else if (lflag)
63 return va_arg(*ap, unsigned long);
64 else
65 return va_arg(*ap, unsigned int);
68 // Same as getuint but signed - can't use getuint
69 // because of sign extension
70 static long long
71 getint(va_list *ap, int lflag)
73 if (lflag >= 2)
74 return va_arg(*ap, long long);
75 else if (lflag)
76 return va_arg(*ap, long);
77 else
78 return va_arg(*ap, int);
82 // Main function to format and print a string.
83 void printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...);
85 void
86 vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap)
88 register const char *p;
89 register int ch, err;
90 unsigned long long num;
91 int base, lflag, width, precision, altflag;
92 char padc;
94 while (1) {
95 while ((ch = *(unsigned char *) fmt++) != '%') {
96 if (ch == '\0')
97 return;
98 putch(ch, putdat);
101 // Process a %-escape sequence
102 padc = ' ';
103 width = -1;
104 precision = -1;
105 lflag = 0;
106 altflag = 0;
107 reswitch:
108 switch (ch = *(unsigned char *) fmt++) {
110 // flag to pad on the right
111 case '-':
112 padc = '-';
113 goto reswitch;
115 // flag to pad with 0's instead of spaces
116 case '0':
117 padc = '0';
118 goto reswitch;
120 // width field
121 case '1':
122 case '2':
123 case '3':
124 case '4':
125 case '5':
126 case '6':
127 case '7':
128 case '8':
129 case '9':
130 for (precision = 0; ; ++fmt) {
131 precision = precision * 10 + ch - '0';
132 ch = *fmt;
133 if (ch < '0' || ch > '9')
134 break;
136 goto process_precision;
138 case '*':
139 precision = va_arg(ap, int);
140 goto process_precision;
142 case '.':
143 if (width < 0)
144 width = 0;
145 goto reswitch;
147 case '#':
148 altflag = 1;
149 goto reswitch;
151 process_precision:
152 if (width < 0)
153 width = precision, precision = -1;
154 goto reswitch;
156 // long flag (doubled for long long)
157 case 'l':
158 lflag++;
159 goto reswitch;
161 // character
162 case 'c':
163 putch(va_arg(ap, int), putdat);
164 break;
166 // error message
167 case 'e':
168 err = va_arg(ap, int);
169 if (err < 0)
170 err = -err;
171 if (err > MAXERROR || (p = error_string[err]) == NULL)
172 printfmt(putch, putdat, "error %d", err);
173 else
174 printfmt(putch, putdat, "%s", p);
175 break;
177 // string
178 case 's':
179 if ((p = va_arg(ap, char *)) == NULL)
180 p = "(null)";
181 if (width > 0 && padc != '-')
182 for (width -= strnlen(p, precision); width > 0; width--)
183 putch(padc, putdat);
184 for (; (ch = *p++) != '\0' && (precision < 0 || --precision >= 0); width--)
185 if (altflag && (ch < ' ' || ch > '~'))
186 putch('?', putdat);
187 else
188 putch(ch, putdat);
189 for (; width > 0; width--)
190 putch(' ', putdat);
191 break;
193 // (signed) decimal
194 case 'd':
195 num = getint(&ap, lflag);
196 if ((long long) num < 0) {
197 putch('-', putdat);
198 num = -(long long) num;
200 base = 10;
201 goto number;
203 // unsigned decimal
204 case 'u':
205 num = getuint(&ap, lflag);
206 base = 10;
207 goto number;
209 // (unsigned) octal
210 case 'o':
211 // Replace this with your code.
212 num = getuint(&ap, lflag);
213 base = 8;
214 goto number;
216 // pointer
217 case 'p':
218 putch('0', putdat);
219 putch('x', putdat);
220 num = (unsigned long long)
221 (uintptr_t) va_arg(ap, void *);
222 base = 16;
223 goto number;
225 // (unsigned) hexadecimal
226 case 'x':
227 num = getuint(&ap, lflag);
228 base = 16;
229 number:
230 printnum(putch, putdat, num, base, width, padc);
231 break;
233 // escaped '%' character
234 case '%':
235 putch(ch, putdat);
236 break;
238 // unrecognized escape sequence - just print it literally
239 default:
240 putch('%', putdat);
241 for (fmt--; fmt[-1] != '%'; fmt--)
242 /* do nothing */;
243 break;
248 void
249 printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...)
251 va_list ap;
253 va_start(ap, fmt);
254 vprintfmt(putch, putdat, fmt, ap);
255 va_end(ap);
258 struct sprintbuf {
259 char *buf;
260 char *ebuf;
261 int cnt;
264 static void
265 sprintputch(int ch, struct sprintbuf *b)
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((void*)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;