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.
7 #include <inc/string.h>
8 #include <inc/stdarg.h>
12 * Space or zero padding and a field width are supported for the numeric
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] =
28 "out of environments",
33 * Print a number (base <= 16) in reverse order,
34 * using specified putch function and associated pointer putdat.
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
42 printnum(putch
, putdat
, num
/ base
, base
, width
- 1, padc
);
44 // print any needed pad characters before first digit
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
)
59 return va_arg(*ap
, unsigned long long);
61 return va_arg(*ap
, unsigned long);
63 return va_arg(*ap
, unsigned int);
66 // Same as getuint but signed - can't use getuint
67 // because of sign extension
69 getint(va_list *ap
, int lflag
)
72 return va_arg(*ap
, long long);
74 return va_arg(*ap
, long);
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
, ...);
84 vprintfmt(void (*putch
)(int, void*), void *putdat
, const char *fmt
, va_list ap
)
86 register const char *p
;
88 unsigned long long num
;
89 int base
, lflag
, width
, precision
, altflag
;
93 while ((ch
= *(unsigned char *) fmt
++) != '%') {
99 // Process a %-escape sequence
106 switch (ch
= *(unsigned char *) fmt
++) {
108 // flag to pad on the right
113 // flag to pad with 0's instead of spaces
128 for (precision
= 0; ; ++fmt
) {
129 precision
= precision
* 10 + ch
- '0';
131 if (ch
< '0' || ch
> '9')
134 goto process_precision
;
137 precision
= va_arg(ap
, int);
138 goto process_precision
;
151 width
= precision
, precision
= -1;
154 // long flag (doubled for long long)
161 putch(va_arg(ap
, int), putdat
);
166 err
= va_arg(ap
, int);
169 if (err
> MAXERROR
|| (p
= error_string
[err
]) == NULL
)
170 printfmt(putch
, putdat
, "error %d", err
);
172 printfmt(putch
, putdat
, "%s", p
);
177 if ((p
= va_arg(ap
, char *)) == NULL
)
179 if (width
> 0 && padc
!= '-')
180 for (width
-= strnlen(p
, precision
); width
> 0; width
--)
182 for (; (ch
= *p
++) != '\0' && (precision
< 0 || --precision
>= 0); width
--)
183 if (altflag
&& (ch
< ' ' || ch
> '~'))
187 for (; width
> 0; width
--)
193 num
= getint(&ap
, lflag
);
194 if ((long long) num
< 0) {
196 num
= -(long long) num
;
203 num
= getuint(&ap
, lflag
);
209 // Replace this with your code.
219 num
= (unsigned long long)
220 (uintptr_t) va_arg(ap
, void *);
224 // (unsigned) hexadecimal
226 num
= getuint(&ap
, lflag
);
229 printnum(putch
, putdat
, num
, base
, width
, padc
);
232 // escaped '%' character
237 // unrecognized escape sequence - just print it literally
240 for (fmt
--; fmt
[-1] != '%'; fmt
--)
248 printfmt(void (*putch
)(int, void*), void *putdat
, const char *fmt
, ...)
253 vprintfmt(putch
, putdat
, fmt
, ap
);
264 sprintputch(int ch
, void *ptr
)
266 struct sprintbuf
*b
= (struct sprintbuf
*) ptr
;
268 if (b
->buf
< b
->ebuf
)
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)
280 // print the string to the buffer
281 vprintfmt(sprintputch
, &b
, fmt
, ap
);
283 // null terminate the buffer
290 snprintf(char *buf
, int n
, const char *fmt
, ...)
296 rc
= vsnprintf(buf
, n
, fmt
, ap
);