//----------------------------------------------------------------------------// // 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); // // 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); rc = BPrintOnBufV(buf, fmt, ap); va_end(ap); 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 bdoconvrt(pref, type, vtype) do { \ type i_##type = (type)va_arg(ap, vtype); \ pref##toa(i_##type, s, base); \ } while (0) // Increase fmt while parsing a modifier #define fmtnext() do{fmt++;if(*fmt==0){rc=EINVAL;goto leave;}}while(0) // // Actually does BPrintOnBuf's job // Quite a long function // error_t BPrintOnBufV(Buffer_t *buf, const char *fmt, va_list ap) { error_t rc = EOK; int tmpwidth; size_t width; char type; uchar uch; char *s; // Conversion buffer char convbuf[100]; size_t len; // 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; } ExAcquireLock(&buf->lock); //----------------------------------------------------------------------------// // We come back here after dealing with a modifier loop: // Deal with all non-'%' characters for (; *fmt && *fmt != '%' ; fmt++) { rc = bputc(buf, *fmt); continue; } // Job's done / something bad happened if (rc || !*fmt) goto leave; //----------------------------------------------------------------------------// // // %[parameter][flags][width|*][.precision][length]type // We aren't dealing with parameters and float stuff just yet // // Skip the '%' fmtnext(); // "%%" modifier if (*fmt == '%') { rc = bputc(buf, '%'); if (rc > 0) goto leave; else { fmt++; goto loop; } } // Reset everything width = 0; cap = sgn = 0; l = h = hh = 0; plus = minus = space = zero = hash = 0; //----------------------------------------------------------------------------// // // Flags field // while (1) { if (*fmt == '#') hash++; else if (*fmt == '0') zero++; else if (*fmt == '+') plus++; else if (*fmt == '-') minus++; else if (*fmt == ' ') space++; else break; fmtnext(); } //----------------------------------------------------------------------------// // // Width field // A width field of zero is ignored // // '*' means we should extract it from the argument list if (*fmt == '*') { fmtnext(); tmpwidth = va_arg(ap, int); // A width below 0 activates the "minus" flag if (tmpwidth < 0) { width = -tmpwidth; minus++; } else { width = tmpwidth; } } else { // Extract width field from fmt while (isdigit(*fmt) && width < sizeof(convbuf)-10) { width = 10 * width + (*fmt - '0'); fmtnext(); } } if (width > sizeof(convbuf)) { rc = EINVAL; goto leave; } //----------------------------------------------------------------------------// // // Precision field // Ignored until floats are implemented // TODO before floats: %.XXXs // //----------------------------------------------------------------------------// // // Length field // while (1) { if (*fmt == 'l' || *fmt == 'z') l++; else if (*fmt == 'h') h++; else break; fmtnext(); } // Consistency check assert(!(l > 0 && h > 0)); assert(!(l > 2 || h > 2)); //----------------------------------------------------------------------------// // // The type field // type = *fmt++; // Characters if (type == 'c') { uch = (uchar)va_arg(ap, int); bputc(buf, uch); goto loop; } // Strings else if (type == 's') { s = va_arg(ap, char *); if (s == NULL) s = "(null)"; for (; !rc && *s ; s++) { rc = bputc(buf, (uchar)*s); } if (rc > 0) goto leave; goto loop; } // 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 = %#012x // (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 else { rc = EINVAL; goto leave; } //----------------------------------------------------------------------------// // // Numerical conversions // // We re-use s to iterate convbuf s = convbuf; if (!l) { if (sgn) { if (h == 0) bdoconvrt(i, int, int); if (h == 1) bdoconvrt(i, short, int); if (h == 2) bdoconvrt(i, char, int); } else { if (h == 0) bdoconvrt(u, uint, uint); if (h == 1) bdoconvrt(u, ushort, uint); if (h == 2) bdoconvrt(u, uchar, 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; } else len = 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, ' '); if (rc > 0) goto leave; // Continue parsing fmt goto loop; //----------------------------------------------------------------------------// leave: ExReleaseLock(&buf->lock); return rc; }