fix O_RDWR bug.
[mit-jos.git] / lib / printfmt.c
blob382cb0a43c1c8b32381772306a31fc7a4da1d996
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",
32 "no free space on disk",
33 "too many files are open",
34 "file or block not found",
35 "invalid path",
36 "file already exists",
37 "file is not a valid executable",
41 * Print a number (base <= 16) in reverse order,
42 * using specified putch function and associated pointer putdat.
44 static void
45 printnum(void (*putch)(int, void*), void *putdat,
46 unsigned long long num, unsigned base, int width, int padc)
48 // first recursively print all preceding (more significant) digits
49 if (num >= base) {
50 printnum(putch, putdat, num / base, base, width - 1, padc);
51 } else {
52 // print any needed pad characters before first digit
53 while (--width > 0)
54 putch(padc, putdat);
57 // then print this (the least significant) digit
58 putch("0123456789abcdef"[num % base], putdat);
61 // Get an unsigned int of various possible sizes from a varargs list,
62 // depending on the lflag parameter.
63 static unsigned long long
64 getuint(va_list *ap, int lflag)
66 if (lflag >= 2)
67 return va_arg(*ap, unsigned long long);
68 else if (lflag)
69 return va_arg(*ap, unsigned long);
70 else
71 return va_arg(*ap, unsigned int);
74 // Same as getuint but signed - can't use getuint
75 // because of sign extension
76 static long long
77 getint(va_list *ap, int lflag)
79 if (lflag >= 2)
80 return va_arg(*ap, long long);
81 else if (lflag)
82 return va_arg(*ap, long);
83 else
84 return va_arg(*ap, int);
88 // Main function to format and print a string.
89 void printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...);
91 void
92 vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap)
94 register const char *p;
95 register int ch, err;
96 unsigned long long num;
97 int base, lflag, width, precision, altflag;
98 char padc;
100 while (1) {
101 while ((ch = *(unsigned char *) fmt++) != '%') {
102 if (ch == '\0')
103 return;
104 putch(ch, putdat);
107 // Process a %-escape sequence
108 padc = ' ';
109 width = -1;
110 precision = -1;
111 lflag = 0;
112 altflag = 0;
113 reswitch:
114 switch (ch = *(unsigned char *) fmt++) {
116 // flag to pad on the right
117 case '-':
118 padc = '-';
119 goto reswitch;
121 // flag to pad with 0's instead of spaces
122 case '0':
123 padc = '0';
124 goto reswitch;
126 // width field
127 case '1':
128 case '2':
129 case '3':
130 case '4':
131 case '5':
132 case '6':
133 case '7':
134 case '8':
135 case '9':
136 for (precision = 0; ; ++fmt) {
137 precision = precision * 10 + ch - '0';
138 ch = *fmt;
139 if (ch < '0' || ch > '9')
140 break;
142 goto process_precision;
144 case '*':
145 precision = va_arg(ap, int);
146 goto process_precision;
148 case '.':
149 if (width < 0)
150 width = 0;
151 goto reswitch;
153 case '#':
154 altflag = 1;
155 goto reswitch;
157 process_precision:
158 if (width < 0)
159 width = precision, precision = -1;
160 goto reswitch;
162 // long flag (doubled for long long)
163 case 'l':
164 lflag++;
165 goto reswitch;
167 // character
168 case 'c':
169 putch(va_arg(ap, int), putdat);
170 break;
172 // error message
173 case 'e':
174 err = va_arg(ap, int);
175 if (err < 0)
176 err = -err;
177 if (err > MAXERROR || (p = error_string[err]) == NULL)
178 printfmt(putch, putdat, "error %d", err);
179 else
180 printfmt(putch, putdat, "%s", p);
181 break;
183 // string
184 case 's':
185 if ((p = va_arg(ap, char *)) == NULL)
186 p = "(null)";
187 if (width > 0 && padc != '-')
188 for (width -= strnlen(p, precision); width > 0; width--)
189 putch(padc, putdat);
190 for (; (ch = *p++) != '\0' && (precision < 0 || --precision >= 0); width--)
191 if (altflag && (ch < ' ' || ch > '~'))
192 putch('?', putdat);
193 else
194 putch(ch, putdat);
195 for (; width > 0; width--)
196 putch(' ', putdat);
197 break;
199 // (signed) decimal
200 case 'd':
201 num = getint(&ap, lflag);
202 if ((long long) num < 0) {
203 putch('-', putdat);
204 num = -(long long) num;
206 base = 10;
207 goto number;
209 // unsigned decimal
210 case 'u':
211 num = getuint(&ap, lflag);
212 base = 10;
213 goto number;
215 // (unsigned) octal
216 case 'o':
217 // Replace this with your code.
218 num = getuint(&ap, lflag);
219 base = 8;
220 goto number;
222 // pointer
223 case 'p':
224 putch('0', putdat);
225 putch('x', putdat);
226 num = (unsigned long long)
227 (uintptr_t) va_arg(ap, void *);
228 base = 16;
229 goto number;
231 // (unsigned) hexadecimal
232 case 'x':
233 num = getuint(&ap, lflag);
234 base = 16;
235 number:
236 printnum(putch, putdat, num, base, width, padc);
237 break;
239 // escaped '%' character
240 case '%':
241 putch(ch, putdat);
242 break;
244 // unrecognized escape sequence - just print it literally
245 default:
246 putch('%', putdat);
247 for (fmt--; fmt[-1] != '%'; fmt--)
248 /* do nothing */;
249 break;
254 void
255 printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...)
257 va_list ap;
259 va_start(ap, fmt);
260 vprintfmt(putch, putdat, fmt, ap);
261 va_end(ap);
264 struct sprintbuf {
265 char *buf;
266 char *ebuf;
267 int cnt;
270 static void
271 sprintputch(int ch, struct sprintbuf *b)
273 b->cnt++;
274 if (b->buf < b->ebuf)
275 *b->buf++ = ch;
279 vsnprintf(char *buf, int n, const char *fmt, va_list ap)
281 struct sprintbuf b = {buf, buf+n-1, 0};
283 if (buf == NULL || n < 1)
284 return -E_INVAL;
286 // print the string to the buffer
287 vprintfmt((void*)sprintputch, &b, fmt, ap);
289 // null terminate the buffer
290 *b.buf = '\0';
292 return b.cnt;
296 snprintf(char *buf, int n, const char *fmt, ...)
298 va_list ap;
299 int rc;
301 va_start(ap, fmt);
302 rc = vsnprintf(buf, n, fmt, ap);
303 va_end(ap);
305 return rc;