//----------------------------------------------------------------------------// // OS on Kaleid // // // // Desc: Buffer library // // // // // // Copyright © 2018-2020 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 #ifdef _KALEID_KERNEL #include #endif // // Prints formatted string on buf according to fmt // size_t BPrintOnBuf(Buffer_t *buf, const char *fmt, ...) { size_t sz; va_list ap; va_start(ap, fmt); ExAcquireLock(&buf->lock); sz = vbprintf(buf, fmt, ap); ExReleaseLock(&buf->lock); va_end(ap); return sz; } size_t BPrintOnBufV(Buffer_t *buf, const char *fmt, va_list ap) { size_t sz; ExAcquireLock(&buf->lock); sz = vbprintf(buf, fmt, ap); ExReleaseLock(&buf->lock); return sz; } size_t bprintf(Buffer_t *buf, const char *fmt, ...) { error_t sz; va_list ap; va_start(ap, fmt); sz = vbprintf(buf, fmt, ap); va_end(ap); return sz; } #define CONVBUFSIZE 100 // // Actually does BPrintOnBuf's job; doesn't lock anything // Quite a long function // size_t vbprintf(Buffer_t *buf, const char *fmt, va_list ap) { error_t rc = 0; size_t written = 0; ssize_t 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; if (!buf || !fmt) { seterrno(EINVAL); return 0; } if (buf->flags & (BF_EOF|BF_ERR)) { seterrno(EENDF); return 0; } if (buf->state != BS_RDWR && buf->state != BS_WRONLY) { seterrno(EBADF); return 0; } // Progress in format string while (*fmt && !rc) { // Deal with all non-'%' characters if (*fmt != '%') { rc = bputc(buf, *fmt); written++, fmt++; continue; } // // %[flags][width|*][.precision|*][length]type // // Skip the '%' fmt++; // "%%" modifier if (*fmt == '%') { rc = bputc(buf, '%'); written++, 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 = 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); } // // 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); rc = bputc(buf, uch); written++; continue; } #ifdef _KALEID_KERNEL if (type == 'C') { base = va_arg(ap, int); if (!(base < 0 || base > VGA_COLOR_WHITE)) { rc = bputc(buf, RtlColorToChar(base)); written++; } continue; } #endif // Strings 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 (; !rc && *s && prec-- ; s++) { rc = bputc(buf, (uchar)*s); written++; } continue; } // Make sure width and prec aren't too big // (We didn't do that earlier because %s uses width) if (width > CONVBUFSIZE || prec > CONVBUFSIZE) { written++; // Work around "if (rc) return written - 1;" rc = EINVAL; break; } // Decimal, unsigned decimal, hexadecimal, octal and binary numbers 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; } // End of string too soon else if (type == '\0') { bputc(buf, '%'); written++; // Work around "if (rc) return written - 1;" rc = EINVAL; break; } // Unknown/unsupported modifier else { rc = bputc(buf, '%'); written++; continue; } // // Numerical conversions // // We use s to iterate convbuf s = convbuf; #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) { for (; !rc && width > len ; width--) { rc = bputc(buf, ' '); written++; } } // Deal with signs and the hash flag if (*s == '-') { rc = bputc(buf, '-'); s++, len--; written++; } else if (sgn && plus) { rc = bputc(buf, '+'); written++; } else if (sgn && space) { rc = bputc(buf, ' '); written++; } // Print 0 for octal, 0x for hexadecimal, 0b for binary else if (hash && (base == 2 || base == 8 || base == 16)) { rc = bputc(buf, '0'); written++; if (!rc && base != 8) { rc = bputc(buf, (base == 2 ? 'b' : (cap ? 'X' : 'x'))); written++; } } // Deal with padding by zeroes // The 'minus' flag makes no sense with the 'zero' one if (zero && width > len) { for (; !rc && width > len ; width--) { rc = bputc(buf, '0'); written++; } } // Output the actual number for (; !rc && *s ; s++) { rc = bputc(buf, (uchar)*s); written++; } // 'minus' padding, only with spaces if (minus && !zero && width > len) { for (; !rc && width > len ; width--) { rc = bputc(buf, ' '); written++; } } // Carry on to next modifier } // For debugging purposes assert(!rc && "vbprintf() error"); seterrno(rc); if (rc) return written - 1; // "- 1" because last bputc() must have failed else return written; }