|  | // Stripped-down primitive printf-style formatting routines, | 
|  | // used in common by printf, sprintf, fprintf, etc. | 
|  | // This code is also used by both the kernel and user programs. | 
|  | #include <ros/common.h> | 
|  | #include <error.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <stdarg.h> | 
|  | #include <kthread.h> | 
|  | #include <syscall.h> | 
|  | #include <ns.h> | 
|  |  | 
|  | /* Print a number (base <= 16) in reverse order, | 
|  | * using specified putch function and associated pointer putdat. */ | 
|  | void printnum(void (*putch)(int, void**), void **putdat, | 
|  | unsigned long long num, unsigned base, int width, int padc) | 
|  | { | 
|  | unsigned long long temp = num; | 
|  | int nr_digits = 1; | 
|  |  | 
|  | /* Determine how many leading zeros we need. | 
|  | * For every digit/nibble beyond base, we do one less width padding */ | 
|  | while ((temp /= base)) { | 
|  | nr_digits++; | 
|  | width--; | 
|  | } | 
|  | /* And another one less, since we'll always print the last digit */ | 
|  | while (--width > 0) | 
|  | putch(padc, putdat); | 
|  | for (int i = nr_digits; i > 0; i--) { | 
|  | temp = num; | 
|  | /* To get digit i, we only div (i-1) times */ | 
|  | for (int j = 0; j < i - 1; j++) { | 
|  | temp /= base; | 
|  | } | 
|  | putch("0123456789abcdef"[temp % base], putdat); | 
|  | } | 
|  | } | 
|  |  | 
|  | void printfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...); | 
|  |  | 
|  | void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt, | 
|  | va_list ap) | 
|  | { | 
|  | const char *s; | 
|  | const char *last_fmt; | 
|  | register int ch, err; | 
|  | unsigned long long num; | 
|  | int base, lflag, width, precision, altflag; | 
|  | char padc; | 
|  | uint8_t *mac, *ip, *mask; | 
|  | struct Gas *g; | 
|  | int i; | 
|  | uint32_t *lp; | 
|  | void *p; | 
|  |  | 
|  | while (1) { | 
|  | while ((ch = *(unsigned char *) fmt) != '%') { | 
|  | if (ch == '\0') | 
|  | return; | 
|  | fmt++; | 
|  | putch(ch, putdat); | 
|  | } | 
|  | fmt++; | 
|  |  | 
|  | // Process a %-escape sequence | 
|  | last_fmt = fmt; | 
|  | padc = ' '; | 
|  | width = -1; | 
|  | precision = -1; | 
|  | lflag = 0; | 
|  | altflag = 0; | 
|  | reswitch: | 
|  | switch (ch = *(unsigned char *) fmt++) { | 
|  |  | 
|  | // flag to pad on the right | 
|  | case '-': | 
|  | padc = '-'; | 
|  | goto reswitch; | 
|  |  | 
|  | // flag to pad with 0's instead of spaces | 
|  | case '0': | 
|  | padc = '0'; | 
|  | goto reswitch; | 
|  |  | 
|  | // width field | 
|  | case '1': | 
|  | case '2': | 
|  | case '3': | 
|  | case '4': | 
|  | case '5': | 
|  | case '6': | 
|  | case '7': | 
|  | case '8': | 
|  | case '9': | 
|  | for (precision = 0; ; ++fmt) { | 
|  | precision = precision * 10 + ch - '0'; | 
|  | ch = *fmt; | 
|  | if (ch < '0' || ch > '9') | 
|  | break; | 
|  | } | 
|  | goto process_precision; | 
|  |  | 
|  | case '*': | 
|  | precision = va_arg(ap, int); | 
|  | goto process_precision; | 
|  |  | 
|  | case '.': | 
|  | if (width < 0) | 
|  | width = 0; | 
|  | goto reswitch; | 
|  |  | 
|  | case '#': | 
|  | altflag = 1; | 
|  | goto reswitch; | 
|  |  | 
|  | process_precision: | 
|  | if (width < 0) | 
|  | width = precision, precision = -1; | 
|  | goto reswitch; | 
|  |  | 
|  | // long flag (doubled for long long) | 
|  | case 'l': | 
|  | lflag++; | 
|  | goto reswitch; | 
|  |  | 
|  | // chan | 
|  | case 'C': | 
|  | p = va_arg(ap, void*); | 
|  | warn_on_user_ptr(p); | 
|  | printchan(putch, putdat, p); | 
|  | break; | 
|  |  | 
|  | // character | 
|  | case 'c': | 
|  | putch(va_arg(ap, int), putdat); | 
|  | break; | 
|  |  | 
|  | case 'E': // ENET MAC | 
|  | mac = va_arg(ap, uint8_t *); | 
|  | warn_on_user_ptr(mac); | 
|  | if (!mac) { | 
|  | char *s = "00:00:00:00:00:00"; | 
|  |  | 
|  | while (*s) | 
|  | putch(*s++, putdat); | 
|  | } | 
|  | printemac(putch, putdat, mac); | 
|  | break; | 
|  | case 'i': | 
|  | /* what to do if they screw up? */ | 
|  | lp = va_arg(ap, uint32_t *); | 
|  | warn_on_user_ptr(lp); | 
|  | if (lp) { | 
|  | uint32_t hostfmt; | 
|  |  | 
|  | for (i = 0; i < 4; i++) { | 
|  | hnputl(&hostfmt, lp[i]); | 
|  | printfmt(putch, putdat, "%08lx", | 
|  | hostfmt); | 
|  | } | 
|  | } | 
|  | break; | 
|  | case 'I': | 
|  | /* what to do if they screw up? */ | 
|  | ip = va_arg(ap, uint8_t *); | 
|  | warn_on_user_ptr(ip); | 
|  | if (ip) | 
|  | printip(putch, putdat, ip); | 
|  | break; | 
|  | case 'M': | 
|  | /* what to do if they screw up? */ | 
|  | mask = va_arg(ap, uint8_t *); | 
|  | warn_on_user_ptr(mask); | 
|  | if (mask) | 
|  | printipmask(putch, putdat, mask); | 
|  | break; | 
|  | case 'V': | 
|  | /* what to do if they screw up? */ | 
|  | ip = va_arg(ap, uint8_t *); | 
|  | warn_on_user_ptr(ip); | 
|  | if (ip) | 
|  | printipv4(putch, putdat, ip); | 
|  | break; | 
|  |  | 
|  | // string | 
|  | case 's': | 
|  | s = va_arg(ap, char *); | 
|  | warn_on_user_ptr(s); | 
|  | if (!s) | 
|  | s = "(null)"; | 
|  | if (width > 0 && padc != '-') | 
|  | for (width -= strnlen(s, precision); | 
|  | width > 0; | 
|  | width--) | 
|  | putch(padc, putdat); | 
|  | for (; | 
|  | (ch = *s) != '\0' && (precision < 0 | 
|  | || --precision >= 0); | 
|  | width--) { | 
|  | if (altflag && (ch < ' ' || ch > '~')) | 
|  | putch('?', putdat); | 
|  | else | 
|  | putch(ch, putdat); | 
|  | // zra: make sure *s isn't '\0' before inc'ing | 
|  | s++; | 
|  | } | 
|  | for (; width > 0; width--) | 
|  | putch(' ', putdat); | 
|  | break; | 
|  |  | 
|  | case 'd': /* (signed) decimal */ | 
|  | if (lflag >= 2) | 
|  | num = va_arg(ap, long long); | 
|  | else if (lflag) | 
|  | num = va_arg(ap, long); | 
|  | else | 
|  | num = va_arg(ap, int); | 
|  | if ((long long) num < 0) { | 
|  | putch('-', putdat); | 
|  | num = -(long long) num; | 
|  | } | 
|  | base = 10; | 
|  | goto number; | 
|  |  | 
|  | case 'u': /* unsigned decimal */ | 
|  | case 'o': /* (unsigned) octal */ | 
|  | case 'x': /* (unsigned) hexadecimal */ | 
|  | if (lflag >= 2) | 
|  | num = va_arg(ap, unsigned long long); | 
|  | else if (lflag) | 
|  | num = va_arg(ap, unsigned long); | 
|  | else | 
|  | num = va_arg(ap, unsigned int); | 
|  | if (ch == 'u') | 
|  | base = 10; | 
|  | else if (ch == 'o') | 
|  | base = 8; | 
|  | else	/* x */ | 
|  | base = 16; | 
|  | goto number; | 
|  |  | 
|  | // pointer | 
|  | case 'p': | 
|  | putch('0', putdat); | 
|  | putch('x', putdat); | 
|  | /* automatically zero-pad pointers, out to the length of | 
|  | * a ptr */ | 
|  | padc = '0'; | 
|  | /* 8 bits per byte / 4 bits per char */ | 
|  | width = sizeof(void*) * 2; | 
|  | num = (unsigned long long) | 
|  | (uintptr_t) va_arg(ap, void *); | 
|  | base = 16; | 
|  | goto number; | 
|  |  | 
|  | // qid | 
|  | case 'Q': | 
|  | p = va_arg(ap, void*); | 
|  | warn_on_user_ptr(p); | 
|  | printqid(putch, putdat, p); | 
|  | break; | 
|  | number: | 
|  | printnum(putch, putdat, num, base, width, padc); | 
|  | break; | 
|  |  | 
|  | // escaped '%' character | 
|  | case '%': | 
|  | putch(ch, putdat); | 
|  | break; | 
|  |  | 
|  | // unrecognized escape sequence - just print it literally | 
|  | default: | 
|  | putch('%', putdat); | 
|  | fmt = last_fmt; | 
|  | //for (fmt--; fmt[-1] != '%'; fmt--) | 
|  | /* do nothing */; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void printfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...) | 
|  | { | 
|  | va_list ap; | 
|  |  | 
|  | va_start(ap, fmt); | 
|  | vprintfmt(putch, putdat, fmt, ap); | 
|  | va_end(ap); | 
|  | } | 
|  |  | 
|  | typedef struct sprintbuf { | 
|  | char *buf; | 
|  | char *ebuf; | 
|  | int cnt; | 
|  | } sprintbuf_t; | 
|  |  | 
|  | static void sprintputch(int ch, sprintbuf_t **b) | 
|  | { | 
|  | if ((*b)->buf < (*b)->ebuf) | 
|  | *((*b)->buf++) = ch; | 
|  | (*b)->cnt++; | 
|  | } | 
|  |  | 
|  | int vsnprintf(char *buf, size_t n, const char *fmt, va_list ap) | 
|  | { | 
|  | sprintbuf_t b;// = {buf, buf+n-1, 0}; | 
|  | sprintbuf_t *bp = &b; | 
|  |  | 
|  | /* this isn't quite the snprintf 'spec', but errors aren't helpful */ | 
|  | assert(buf); | 
|  | /* We might get large, 'negative' values for code that repeatedly calls | 
|  | * snprintf(), e.g.: | 
|  | * 		len += snprintf(buf + len, bufsz - len, "foo"); | 
|  | * 		len += snprintf(buf + len, bufsz - len, "bar"); | 
|  | * If len > bufsz, that will appear as a large value.  This is not quite | 
|  | * the glibc semantics (we aren't returning the size we would have | 
|  | * printed), but it short circuits the rest of the function and avoids | 
|  | * potential errors in the putch() functions. */ | 
|  | if (!n || (n > INT32_MAX)) | 
|  | return 0; | 
|  |  | 
|  | b.buf = NULL; // zra : help out the Deputy optimizer a bit | 
|  | b.ebuf = buf+n-1; | 
|  | b.cnt = 0; | 
|  | b.buf = buf; | 
|  |  | 
|  | vprintfmt((void*)sprintputch, (void*)&bp, fmt, ap); | 
|  |  | 
|  | // null terminate the buffer | 
|  | *b.buf = '\0'; | 
|  |  | 
|  | return b.cnt; | 
|  | } | 
|  |  | 
|  | int snprintf(char *buf, size_t n, const char *fmt, ...) | 
|  | { | 
|  | va_list ap; | 
|  | int rc; | 
|  |  | 
|  | va_start(ap, fmt); | 
|  | rc = vsnprintf(buf, n, fmt, ap); | 
|  | va_end(ap); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | /* Convenience function: do a print, return the pointer to the null at the end. | 
|  | * | 
|  | * Unlike snprintf(), when we overflow, this doesn't return the 'end' where we | 
|  | * would have written to.  Instead, we'll return 'end - 1', which is the last | 
|  | * byte, and enforce the null-termination.  */ | 
|  | char *seprintf(char *buf, char *end, const char *fmt, ...) | 
|  | { | 
|  | va_list ap; | 
|  | int rc; | 
|  | size_t n = end - buf; | 
|  |  | 
|  | va_start(ap, fmt); | 
|  | rc = vsnprintf(buf, n, fmt, ap); | 
|  | va_end(ap); | 
|  |  | 
|  | /* Some error - leave them where they were. */ | 
|  | if (rc < 0) | 
|  | return buf; | 
|  | /* Overflow - put them at the end */ | 
|  | if (rc >= n) { | 
|  | *(end - 1) = '\0'; | 
|  | return end - 1; | 
|  | } | 
|  | assert(buf[rc] == '\0'); | 
|  | return buf + rc; | 
|  | } |