#include /* size_t */ #include /* va_list */ #include #include #include "_putf.h" #define BUF 16 #define PADSIZE 16 static const char blanks[PADSIZE] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }; static const char zeroes[PADSIZE] = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0' }; static void _putpad(int _putb(void *, const char *, size_t), void *base, const char *padch, int count) { while (count > PADSIZE) { _putb(base, padch, PADSIZE); count -= PADSIZE; } if (count > 0) _putb(base, padch, count); } /* * Flags used during conversion. */ #define ALT 0x01 /* alternate form */ #define LADJUST 0x04 /* left adjustment */ #define LONGINT 0x08 /* long integer */ #define ZEROPAD 0x10 /* zero (as opposed to blank) pad */ int _putf(int _putb(void *, const char *, size_t), void *base, const char *fmt, va_list ap) { int rc = 0; /* return value accumulator */ for (;;) { unsigned char ch; /* character from fmt */ int size; /* size of converted field or string */ char *cp = (char *)fmt; while ((ch = *fmt) && (ch != '%')) fmt++; int n = fmt - cp; if (n) { _putb(base, cp, n); rc += n; } if (ch == 0) break; fmt++; char flags = 0; /* flags as above */ char sign = 0; /* sign prefix (' ', '+', '-', or \0) */ int width = 0; /* width from format (%8d), or 0 */ int prec = -1; /* precision from format (%.3d), or -1 */ int dprec = 0; /* a copy of prec if [diouxX], 0 otherwise */ for (;;) { ch = *fmt++; if (ch == ' ') { if (!sign) sign = ' '; } else if (ch == '+') { sign = '+'; } else if (ch == '-') { flags |= LADJUST; } else if (ch == '#') { flags |= ALT; } else if (ch == '0') { flags |= ZEROPAD; } else if (ch == 'l') { flags |= LONGINT; } else if (ch == 'z') { if (sizeof(size_t) > sizeof(int)) flags |= LONGINT; } else if (ch == '*') { width = va_arg(ap, int); if (width < 0) { flags |= LADJUST; width = -width; } } else if (ch == '.') { if (*fmt == '*') { fmt++; prec = va_arg(ap, int); } else { prec = 0; while (*fmt >= '0' && *fmt <= '9') prec = 10 * prec + (*fmt++ - '0'); } if (prec < 0) prec = -1; } else if (ch >= '1' && ch <= '9') { width = ch - '0'; while (*fmt >= '0' && *fmt <= '9') width = 10 * width + (*fmt++ - '0'); } else { break; } } unsigned long ulval; /* integer arguments %[diouxX] */ char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ switch (ch) { case 'c': *(cp = buf) = va_arg(ap, int); size = 1; sign = 0; break; case 'P': case 's': cp = va_arg(ap, char *); if (cp == 0) cp = "(null)"; if (prec >= 0) { char *p = memchr(cp, 0, (size_t) prec); if (p) { size = p - cp; if (size > prec) size = prec; } else { size = prec; } } else { size = strlen(cp); sign = 0; } break; case 'u': sign = 0; case 'd': case 'i': if (flags & LONGINT) { ulval = va_arg(ap, unsigned long); } else if (ch == 'u') { ulval = va_arg(ap, unsigned int); } else { ulval = va_arg(ap, int); } if (ch != 'u' && (long) ulval < 0) { ulval = (unsigned long) (-((long) ulval)); sign = '-'; } if ((dprec = prec) >= 0) flags &= ~ZEROPAD; cp = buf + BUF; if (ulval || prec) { if (ulval < 10) { *--cp = (char) ulval + '0'; } else { do { *--cp = (char) (ulval % 10) + '0'; ulval /= 10; } while (ulval); } } size = buf + BUF - cp; break; case 'o': ulval = (flags & LONGINT) ? va_arg(ap, unsigned long) : va_arg(ap, unsigned int); sign = 0; if ((dprec = prec) >= 0) flags &= ~ZEROPAD; cp = buf + BUF; if (ulval || prec) { do { *--cp = (char) (ulval & 7) + '0'; ulval >>= 3; } while (ulval); if ((flags & ALT) != 0 && *cp != '0') *--cp = '0'; } size = buf + BUF - cp; break; case 'p': case 'X': case 'x': if (ch == 'p') { ulval = (unsigned long) va_arg(ap, void *); flags |= ALT; ch = 'x'; } else { ulval = (flags & LONGINT) ? va_arg(ap, unsigned long) : va_arg(ap, unsigned int); } sign = 0; if ((dprec = prec) >= 0) flags &= ~ZEROPAD; char *xdigs = (ch == 'X') ? "0123456789ABCDEF" : "0123456789abcdef"; cp = buf + BUF; do { *--cp = xdigs[ulval & 0x0f]; ulval >>= 4; } while (ulval); if (flags & ALT) { *--cp = ch; *--cp = '0'; } size = buf + BUF - cp; break; case 'g': case 'G': case 'e': case 'E': case 'f': (void) va_arg(ap, long); default: if (ch == 0) return rc; cp = buf; *cp = ch; size = 1; sign = '\0'; break; } int realsz = dprec > size ? dprec : size; if (sign) realsz++; if ((flags & (LADJUST | ZEROPAD)) == 0) _putpad(_putb, base, blanks, width - realsz); if (sign) _putb(base, &sign, 1); if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD) _putpad(_putb, base, zeroes, width - realsz); _putpad(_putb, base, zeroes, dprec - size); if (size) _putb(base, cp, size); if (flags & LADJUST) _putpad(_putb, base, blanks, width - realsz); rc += (width >= realsz) ? width : realsz; } return rc; }