//----------------------------------------------------------------------------// // GNU GPL OS/K // // // // Desc: Buffer library // // // // // // Copyright © 2018-2019 The OS/K Team // // // // This file is part of OS/K. // // // // OS/K is free software: you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // // the Free Software Foundation, either version 3 of the License, or // // any later version. // // // // OS/K is distributed in the hope that it will be useful, // // but WITHOUT ANY WARRANTY//without even the implied warranty of // // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // // GNU General Public License for more details. // // // // You should have received a copy of the GNU General Public License // // along with OS/K. If not, see . // //----------------------------------------------------------------------------// #include extern error_t bputc(Buffer_t *buf, uchar ch); error_t vbprintf(Buffer_t *buf, const char *fmt, va_list ap); // // Prints formatted string on buf according to fmt // error_t BPrintOnBuf(Buffer_t *buf, const char *fmt, ...) { error_t rc; va_list ap; va_start(ap, fmt); ExAcquireLock(&buf->lock); rc = vbprintf(buf, fmt, ap); ExReleaseLock(&buf->lock); va_end(ap); return rc; } error_t BPrintOnBufV(Buffer_t *buf, const char *fmt, va_list ap) { error_t rc; ExAcquireLock(&buf->lock); rc = vbprintf(buf, fmt, ap); ExReleaseLock(&buf->lock); return rc; } // // Prints 0 for octal, 0x for hexadecimal, 0b for binary // static error_t bprinthash(Buffer_t *buf, int base, int cap) { error_t rc; if (base != 2 && base != 8 && base != 16) { return EOK; } rc = bputc(buf, '0'); if (!rc && base != 8) { rc = bputc(buf, (base==2 ? 'b' : (cap ? 'X' : 'x'))); } return rc; } static error_t bdopadding(Buffer_t *buf, size_t width, size_t len, char filler) { error_t rc = EOK; for (; !rc && width > len ; width--) { rc = bputc(buf, filler); } return rc; } #define CONVBUFSIZE 100 // // Actually does BPrintOnBuf's job; doesn't lock anything // Quite a long function // error_t vbprintf(Buffer_t *buf, const char *fmt, va_list ap) { error_t rc = 0; int width, prec, len; char type; uchar uch; char *s; // Conversion buffer char convbuf[CONVBUFSIZE]; // Flags int plus, minus, space, zero, hash; // Length modifiers int l, h, hh; // Signed bool sgn; // Capital digits bool cap; // Base int base; assert(buf && buf->initDone == INITOK); if (!buf) return EINVAL; if (buf->state != BS_RDWR && buf->state != BS_WRONLY) { return EBADF; } // Progress in format string while (*fmt && !rc) { // Deal with all non-'%' characters if (*fmt != '%') { rc = bputc(buf, *fmt); fmt++; continue; } // // %[parameter][flags][width|*][.precision|*][length]type // We aren't dealing with parameters and floating stuff just yet // // Skip the '%' fmt++; // "%%" modifier if (*fmt == '%') { rc = bputc(buf, '%'); fmt++; continue; } // Reset everything cap = sgn = 0; l = h = hh = 0; width = prec = 0; plus = minus = space = zero = hash = 0; // // Flags field // while (*fmt) { if (*fmt == '#') { fmt++; hash++; } else if (*fmt == '0') { fmt++; zero++; } else if (*fmt == '+') { fmt++; plus++; } else if (*fmt == '-') { fmt++; minus++; } else if (*fmt == ' ') { fmt++; space++; } // Next field else break; } // // Width & precision fields // A width field of zero is ignored // // Extracts either width or precision #define bextractwp(name) \ do { \ if (*fmt == '*') { \ fmt++; \ name = va_arg(ap, int); \ } else { \ while (isdigit(*fmt) && name < CONVBUFSIZE) { \ name = 10 * name + (*fmt - '0'); \ fmt++; \ } \ } \ } while (0) // Extract width bextractwp(width); // A width below 0 activates the "minus" flag if (width < 0) { width = -width; minus++; } // Extract precision if (*fmt == '.') { fmt++; bextractwp(prec); } // Make sure they're not too big if (width > CONVBUFSIZE || prec > CONVBUFSIZE) { rc = EINVAL; break; } // // Length field // while (*fmt) { if (*fmt == 'l' || *fmt == 'z' || *fmt == 't') { fmt++; l++; } else if (*fmt == 'h') { fmt++; h++; } // Next field else break; } // Consistency check assert(!(l > 0 && h > 0)); assert(!(l > 2 || h > 1)); // // The type field // type = *fmt++; // Characters if (type == 'c') { uch = (uchar)va_arg(ap, int); bputc(buf, uch); continue; } // Strings else if (type == 's') { s = va_arg(ap, char *); if (s == NULL) s = "(null)"; // For strings, the precision field gives the maximum // amount of characters to be read from the stream // Zero/nonspecified precision means unlimited amount if (prec == 0) prec = INT_MAX; for (; *s && prec-- ; s++) { rc = bputc(buf, (uchar)*s); } continue; } // Decimal, unsigned decimal, hexadecimal, octal and binary numbers else if (type == 'd' || type == 'i') { base = 10; sgn = 1; } else if (type == 'X') { base = 16; cap = 1; } else if (type == 'x') { base = 16; } else if (type == 'u') { base = 10; } else if (type == 'o') { base = 8; } else if (type == 'b') { base = 2; } // Pointers: %p = %#012lx // (48-bit pointers have width 12 at least) else if (type == 'p') { type = 'x'; base = 16; zero++; hash++; l++; if (width < 12) width = 12; } // Unknown/unsupported modifier // Note: a '\0' after length field falls here else { rc = EINVAL; break; } // // Numerical conversions // // We use s to iterate convbuf s = convbuf; // FIXME: this works, but is ugly as hell #define bdoconvrt(pref, type, vtype) \ do { \ type i_##type = (type)va_arg(ap, vtype); \ pref##toa(i_##type, s, base); \ } while (0) if (!l) { if (sgn) { if (h == 0) bdoconvrt(i, int, int); if (h == 1) bdoconvrt(i, short, int); } else { if (h == 0) bdoconvrt(u, uint, uint); if (h == 1) bdoconvrt(u, ushort, uint); } } else { if (sgn) bdoconvrt(l, long, long); else bdoconvrt(ul, ulong, ulong); } // // Implement flags and %X, and prints // // Capital letter digits if (base > 10 && cap) { for (; *s ; s++) if (islower(*s)) *s = toupper(*s); // We use this "opportunity" to compute the length of s len = s - convbuf; // Reset s s = convbuf; } // Compute s's length // It will always fit in an int else len = (int)strlen(s); // Adjust width if (sgn && (plus || space)) width--; else if (hash) width -= (base==8 ? 1 : ((base==2||base==16) ? 2 : 0)); // When padding with spaces, we pad before +/-'s etc if (!minus && !zero && width > len) bdopadding(buf, width, len, ' '); // Deal with signs and the hash flag if (*s == '-') { rc = bputc(buf, '-'); s++, len--; } else if (sgn && plus) rc = bputc(buf, '+'); else if (sgn && space) rc = bputc(buf, ' '); else bprinthash(buf, base, cap); // Deal with padding by zeroes // The 'minus' flag makes no sense with the 'zero' one if (zero && width > len) bdopadding(buf, width, len, '0'); // // Output the actual number // for (; !rc && *s ; s++) { rc = bputc(buf, (uchar)*s); } // 'minus' padding, only with spaces if (minus && !zero && width > len) bdopadding(buf, width, base, ' '); // Carry on to next modifier } // Assert rc for debugging purposes assert(!rc); return rc; }