akaros/kern/src/printfmt.c
<<
>>
Prefs
   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.
   4#include <ros/common.h>
   5#include <error.h>
   6#include <stdio.h>
   7#include <string.h>
   8#include <stdarg.h>
   9#include <kthread.h>
  10#include <syscall.h>
  11#include <ns.h>
  12
  13/* Print a number (base <= 16) in reverse order,
  14 * using specified putch function and associated pointer putdat. */
  15void printnum(void (*putch)(int, void**), void **putdat,
  16              unsigned long long num, unsigned base, int width, int padc)
  17{
  18        unsigned long long temp = num;
  19        int nr_digits = 1;
  20
  21        /* Determine how many leading zeros we need.
  22         * For every digit/nibble beyond base, we do one less width padding */
  23        while ((temp /= base)) {
  24                nr_digits++;
  25                width--;
  26        }
  27        /* And another one less, since we'll always print the last digit */
  28        while (--width > 0)
  29                putch(padc, putdat);
  30        for (int i = nr_digits; i > 0; i--) {
  31                temp = num;
  32                /* To get digit i, we only div (i-1) times */
  33                for (int j = 0; j < i - 1; j++) {
  34                        temp /= base;
  35                }
  36                putch("0123456789abcdef"[temp % base], putdat);
  37        }
  38}
  39
  40void printfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...);
  41
  42void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt,
  43               va_list ap)
  44{
  45        const char *s;
  46        const char *last_fmt;
  47        register int ch, err;
  48        unsigned long long num;
  49        int base, lflag, width, precision, altflag;
  50        char padc;
  51        uint8_t *mac, *ip, *mask;
  52        struct Gas *g;
  53        int i;
  54        uint32_t *lp;
  55        void *p;
  56
  57        while (1) {
  58                while ((ch = *(unsigned char *) fmt) != '%') {
  59                        if (ch == '\0')
  60                                return;
  61                        fmt++;
  62                        putch(ch, putdat);
  63                }
  64                fmt++;
  65
  66                // Process a %-escape sequence
  67                last_fmt = fmt;
  68                padc = ' ';
  69                width = -1;
  70                precision = -1;
  71                lflag = 0;
  72                altflag = 0;
  73        reswitch:
  74                switch (ch = *(unsigned char *) fmt++) {
  75
  76                // flag to pad on the right
  77                case '-':
  78                        padc = '-';
  79                        goto reswitch;
  80
  81                // flag to pad with 0's instead of spaces
  82                case '0':
  83                        padc = '0';
  84                        goto reswitch;
  85
  86                // width field
  87                case '1':
  88                case '2':
  89                case '3':
  90                case '4':
  91                case '5':
  92                case '6':
  93                case '7':
  94                case '8':
  95                case '9':
  96                        for (precision = 0; ; ++fmt) {
  97                                precision = precision * 10 + ch - '0';
  98                                ch = *fmt;
  99                                if (ch < '0' || ch > '9')
 100                                        break;
 101                        }
 102                        goto process_precision;
 103
 104                case '*':
 105                        precision = va_arg(ap, int);
 106                        goto process_precision;
 107
 108                case '.':
 109                        if (width < 0)
 110                                width = 0;
 111                        goto reswitch;
 112
 113                case '#':
 114                        altflag = 1;
 115                        goto reswitch;
 116
 117                process_precision:
 118                        if (width < 0)
 119                                width = precision, precision = -1;
 120                        goto reswitch;
 121
 122                // long flag (doubled for long long)
 123                case 'l':
 124                        lflag++;
 125                        goto reswitch;
 126
 127                // chan
 128                case 'C':
 129                        p = va_arg(ap, void*);
 130                        warn_on_user_ptr(p);
 131                        printchan(putch, putdat, p);
 132                        break;
 133
 134                // character
 135                case 'c':
 136                        putch(va_arg(ap, int), putdat);
 137                        break;
 138
 139                case 'E': // ENET MAC
 140                        mac = va_arg(ap, uint8_t *);
 141                        warn_on_user_ptr(mac);
 142                        if (!mac) {
 143                                char *s = "00:00:00:00:00:00";
 144
 145                                while (*s)
 146                                        putch(*s++, putdat);
 147                        }
 148                        printemac(putch, putdat, mac);
 149                        break;
 150                case 'i':
 151                        /* what to do if they screw up? */
 152                        lp = va_arg(ap, uint32_t *);
 153                        warn_on_user_ptr(lp);
 154                        if (lp) {
 155                                uint32_t hostfmt;
 156
 157                                for (i = 0; i < 4; i++) {
 158                                        hnputl(&hostfmt, lp[i]);
 159                                        printfmt(putch, putdat, "%08lx",
 160                                                 hostfmt);
 161                                }
 162                        }
 163                        break;
 164                case 'I':
 165                        /* what to do if they screw up? */
 166                        ip = va_arg(ap, uint8_t *);
 167                        warn_on_user_ptr(ip);
 168                        if (ip)
 169                                printip(putch, putdat, ip);
 170                        break;
 171                case 'M':
 172                        /* what to do if they screw up? */
 173                        mask = va_arg(ap, uint8_t *);
 174                        warn_on_user_ptr(mask);
 175                        if (mask)
 176                                printipmask(putch, putdat, mask);
 177                        break;
 178                case 'V':
 179                        /* what to do if they screw up? */
 180                        ip = va_arg(ap, uint8_t *);
 181                        warn_on_user_ptr(ip);
 182                        if (ip)
 183                                printipv4(putch, putdat, ip);
 184                        break;
 185
 186                // string
 187                case 's':
 188                        s = va_arg(ap, char *);
 189                        warn_on_user_ptr(s);
 190                        if (!s)
 191                                s = "(null)";
 192                        if (width > 0 && padc != '-')
 193                                for (width -= strnlen(s, precision);
 194                                     width > 0;
 195                                     width--)
 196                                        putch(padc, putdat);
 197                        for (;
 198                             (ch = *s) != '\0' && (precision < 0
 199                                                   || --precision >= 0);
 200                             width--) {
 201                                if (altflag && (ch < ' ' || ch > '~'))
 202                                        putch('?', putdat);
 203                                else
 204                                        putch(ch, putdat);
 205                                // zra: make sure *s isn't '\0' before inc'ing
 206                                s++;
 207                        }
 208                        for (; width > 0; width--)
 209                                putch(' ', putdat);
 210                        break;
 211
 212                case 'd': /* (signed) decimal */
 213                        if (lflag >= 2)
 214                                num = va_arg(ap, long long);
 215                        else if (lflag)
 216                                num = va_arg(ap, long);
 217                        else
 218                                num = va_arg(ap, int);
 219                        if ((long long) num < 0) {
 220                                putch('-', putdat);
 221                                num = -(long long) num;
 222                        }
 223                        base = 10;
 224                        goto number;
 225
 226                case 'u': /* unsigned decimal */
 227                case 'o': /* (unsigned) octal */
 228                case 'x': /* (unsigned) hexadecimal */
 229                        if (lflag >= 2)
 230                                num = va_arg(ap, unsigned long long);
 231                        else if (lflag)
 232                                num = va_arg(ap, unsigned long);
 233                        else
 234                                num = va_arg(ap, unsigned int);
 235                        if (ch == 'u')
 236                                base = 10;
 237                        else if (ch == 'o')
 238                                base = 8;
 239                        else    /* x */
 240                                base = 16;
 241                        goto number;
 242
 243                // pointer
 244                case 'p':
 245                        putch('0', putdat);
 246                        putch('x', putdat);
 247                        /* automatically zero-pad pointers, out to the length of
 248                         * a ptr */
 249                        padc = '0';
 250                        /* 8 bits per byte / 4 bits per char */
 251                        width = sizeof(void*) * 2;
 252                        num = (unsigned long long)
 253                                (uintptr_t) va_arg(ap, void *);
 254                        base = 16;
 255                        goto number;
 256
 257                // qid
 258                case 'Q':
 259                        p = va_arg(ap, void*);
 260                        warn_on_user_ptr(p);
 261                        printqid(putch, putdat, p);
 262                        break;
 263                number:
 264                        printnum(putch, putdat, num, base, width, padc);
 265                        break;
 266
 267                // escaped '%' character
 268                case '%':
 269                        putch(ch, putdat);
 270                        break;
 271
 272                // unrecognized escape sequence - just print it literally
 273                default:
 274                        putch('%', putdat);
 275                        fmt = last_fmt;
 276                        //for (fmt--; fmt[-1] != '%'; fmt--)
 277                                /* do nothing */;
 278                        break;
 279                }
 280        }
 281}
 282
 283void printfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...)
 284{
 285        va_list ap;
 286
 287        va_start(ap, fmt);
 288        vprintfmt(putch, putdat, fmt, ap);
 289        va_end(ap);
 290}
 291
 292typedef struct sprintbuf {
 293        char *buf;
 294        char *ebuf;
 295        int cnt;
 296} sprintbuf_t;
 297
 298static void sprintputch(int ch, sprintbuf_t **b)
 299{
 300        if ((*b)->buf < (*b)->ebuf)
 301                *((*b)->buf++) = ch;
 302        (*b)->cnt++;
 303}
 304
 305int vsnprintf(char *buf, size_t n, const char *fmt, va_list ap)
 306{
 307        sprintbuf_t b;// = {buf, buf+n-1, 0};
 308        sprintbuf_t *bp = &b;
 309
 310        /* this isn't quite the snprintf 'spec', but errors aren't helpful */
 311        assert(buf);
 312        /* We might get large, 'negative' values for code that repeatedly calls
 313         * snprintf(), e.g.:
 314         *              len += snprintf(buf + len, bufsz - len, "foo");
 315         *              len += snprintf(buf + len, bufsz - len, "bar");
 316         * If len > bufsz, that will appear as a large value.  This is not quite
 317         * the glibc semantics (we aren't returning the size we would have
 318         * printed), but it short circuits the rest of the function and avoids
 319         * potential errors in the putch() functions. */
 320        if (!n || (n > INT32_MAX))
 321                return 0;
 322
 323        b.buf = NULL; // zra : help out the Deputy optimizer a bit
 324        b.ebuf = buf+n-1;
 325        b.cnt = 0;
 326        b.buf = buf;
 327
 328        vprintfmt((void*)sprintputch, (void*)&bp, fmt, ap);
 329
 330        // null terminate the buffer
 331        *b.buf = '\0';
 332
 333        return b.cnt;
 334}
 335
 336int snprintf(char *buf, size_t n, const char *fmt, ...)
 337{
 338        va_list ap;
 339        int rc;
 340
 341        va_start(ap, fmt);
 342        rc = vsnprintf(buf, n, fmt, ap);
 343        va_end(ap);
 344
 345        return rc;
 346}
 347
 348/* Convenience function: do a print, return the pointer to the null at the end.
 349 *
 350 * Unlike snprintf(), when we overflow, this doesn't return the 'end' where we
 351 * would have written to.  Instead, we'll return 'end - 1', which is the last
 352 * byte, and enforce the null-termination.  */
 353char *seprintf(char *buf, char *end, const char *fmt, ...)
 354{
 355        va_list ap;
 356        int rc;
 357        size_t n = end - buf;
 358
 359        va_start(ap, fmt);
 360        rc = vsnprintf(buf, n, fmt, ap);
 361        va_end(ap);
 362
 363        /* Some error - leave them where they were. */
 364        if (rc < 0)
 365                return buf;
 366        /* Overflow - put them at the end */
 367        if (rc >= n) {
 368                *(end - 1) = '\0';
 369                return end - 1;
 370        }
 371        assert(buf[rc] == '\0');
 372        return buf + rc;
 373}
 374