//----------------------------------------------------------------------------// // OS on Kaleid // // // // Desc: *s*printf() family // // // // // // Copyright © 2018-2021 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 // // XXX // For now vsnprintf won't support n > 4096 // vsnprintf() is implemented using the libbuf, which // does not support dynamically sized buffers (yet) // (because we need realloc() for that, and we don't have it yet) // So for now we have to create a buffer of size n which is allocated // hard, with no paging etc. // Once libbuf is supports dynamic buffers, the only changes necessary // will be to change the value below to and add a line to vsnprintf() // #define VSNPRINTF_MAX 4096 // // Format str according to fmt using ellipsed arguments // size_t sprintf(char *str, const char *fmt, ...) { size_t ret; va_list ap; assert(!"sprintf() is deprecated, used snprintf() for more safety"); va_start(ap, fmt); ret = vsnprintf(str, VSNPRINTF_MAX, fmt, ap); va_end(ap); return ret; } size_t vsprintf(char *str, const char *fmt, va_list ap) { return vsnprintf(str, VSNPRINTF_MAX, fmt, ap); } // // (v)sprintf() but with a size limit: no more than n bytes are written in str // Always null-terminate str // size_t snprintf(char *str, size_t n, const char *fmt, ...) { size_t ret; va_list ap; va_start(ap, fmt); ret = vsnprintf(str, n, fmt, ap); va_end(ap); return ret; } size_t vsnprintf(char *str, size_t n, const char *fmt, va_list ap) { error_t rc; size_t ret, sz; Buffer_t *buf = NULL; assert(str && fmt); if (n == 0) return 0; if (n > VSNPRINTF_MAX) { assert(!"Not yet..."); goto fail; } rc = BOpenPureBuf(&buf, BS_WRONLY, n-1); // n-1 to leave place for the '\0' if (rc != EOK) { goto fail; } sz = vbprintf(buf, fmt, ap); // We don't mind EOFs, just just return how much was successfully written if (sz == 0 && !(buf->flags & BF_EOF)) goto fail; ret = (size_t)buf->wp - (size_t)buf->buf; if (ret > 0) memmove(str, (char *)buf->buf, ret); str[ret++] = 0; assert(ret <= n); BCloseBuf(buf); return ret; fail: KeStartPanic("vsnprintf() failure\nRC: %d\nbuf->flags & BF_EOF: %d\n", rc, buf->flags & BF_EOF); *str = 0; return 0; } // // Old code // #if 0 // Size of the buffer is for convertions #define CONVBUF 64 size_t vsnprintf(char *str, size_t n, const char *fmt, va_list ap) { size_t ret = 0; bool lflag = 0, hflag = 0, hhflag = 0, altflag = 0; int base; char mod; char convbuf[CONVBUF] = { 0 }; char c, *s; int d; uint u; assert(str && fmt); if (n == 0) return 0; // For aesthetic reasons... n--; // Go through the format string while (*fmt && ret < n) { // Regular character if (*fmt != '%') { *str++ = *fmt++; ret++; continue; } // Found a '%' while (1) { mod = *++fmt; if (mod == '%') { *str++ = '%'; ret++; break; } if (mod == '#') { altflag = 1; continue; } if (mod == 'l' || mod == 'z' || mod == 't') { // 'll'/'z'/'t' aliased to 'l' lflag = 1; continue; } if (mod == 'h') { if (hflag) hhflag = 1; else hflag = 1; continue; } if (mod == 'c') { c = (char)va_arg(ap, int); *str++ = c; ret++; break; } if (mod == 's') { s = va_arg(ap, char *); while (*s && ret < n) { *str++ = *s++; ret++; } break; } if (mod == 'i') { mod = 'd'; goto numeric; } if (mod == 'p') { mod = 'x'; lflag = 1; altflag = 1; goto numeric; } if (mod == 'd' || mod == 'u' || mod == 'b' || mod == 'o' || mod == 'x' || mod == 'X') { // Label to avoid some useless tests numeric: if (altflag && mod != 'd' && mod != 'u') { // #d / #u is undefined behaviour *str++ = '0'; if (++ret >= n) break; if (mod != 'o') { *str++ = mod; ret++; } } // "%d" is a special snowflake, deal with it separately if (mod == 'd') { if (lflag) { ltoa(va_arg(ap, long), convbuf, 10); } else { d = va_arg(ap, int); if (hhflag) d &= 0xff; // char-ify else if (hflag) d &= 0xffff; // short-ify itoa(d, convbuf, 10); } } // All unsigned mods else { base = (mod == 'u' ? 10 : (mod == 'b' ? 2 : (mod == 'o' ? 8 : 16))); // Every other mod here is unsigned if (lflag) { ultoa(va_arg(ap, ulong), convbuf, base); } else { u = va_arg(ap, uint); if (hhflag) u &= 0xff; // char-ify else if (hflag) u &= 0xffff; // short-ify utoa(u, convbuf, base); } // "abcdef" => "ABCDEF" if (mod == 'X') { // Re-use s as an iterator for convbuf s = convbuf; while (*s) { if (islower(*s)) *s = toupper(*s); s++; } } } s = convbuf; // Convertions happened, now we write into str while (*s && ret < n) { *str++ = *s++; ret++; } // We're done dealing with this modifier break; } // Unknown/unsupported modifier :| *str++ = mod; ret++; break; } // We fallthrough here from the "while (1)" lflag = hflag = hhflag = altflag = 0; fmt++; } // Null-terminate no matter what *str = 0; return ret + 1; } #endif