os-k/kaleid/libbuf/bprint.c

414 lines
12 KiB
C
Raw Normal View History

2019-03-25 17:33:51 +01:00
//----------------------------------------------------------------------------//
// GNU GPL OS/K //
// //
// Desc: Buffer library //
// //
// //
// Copyright © 2018-2020 The OS/K Team //
2019-03-25 17:33:51 +01:00
// //
// 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 <https://www.gnu.org/licenses/>. //
//----------------------------------------------------------------------------//
2020-02-19 22:19:58 +01:00
#include <libbuf.h>
2019-03-25 17:33:51 +01:00
2019-05-18 22:02:16 +02:00
#ifdef _KALEID_KERNEL
#include <io/vga.h>
#endif
2019-03-25 17:33:51 +01:00
//
// Prints formatted string on buf according to fmt
//
2020-02-11 11:16:24 +01:00
size_t BPrintOnBuf(Buffer_t *buf, const char *fmt, ...)
2019-03-25 17:33:51 +01:00
{
2020-02-11 11:16:24 +01:00
size_t sz;
2019-03-25 17:33:51 +01:00
va_list ap;
va_start(ap, fmt);
2019-04-04 18:41:39 +02:00
ExAcquireLock(&buf->lock);
2020-02-11 11:16:24 +01:00
sz = vbprintf(buf, fmt, ap);
2019-04-04 18:41:39 +02:00
ExReleaseLock(&buf->lock);
2019-03-25 17:33:51 +01:00
va_end(ap);
2020-02-11 11:16:24 +01:00
return sz;
2019-03-25 17:33:51 +01:00
}
2019-04-04 18:41:39 +02:00
2020-02-11 11:16:24 +01:00
size_t BPrintOnBufV(Buffer_t *buf, const char *fmt, va_list ap)
2019-04-04 18:41:39 +02:00
{
2020-02-11 11:16:24 +01:00
size_t sz;
2019-04-04 18:41:39 +02:00
ExAcquireLock(&buf->lock);
2020-02-11 11:16:24 +01:00
sz = vbprintf(buf, fmt, ap);
2019-04-04 18:41:39 +02:00
ExReleaseLock(&buf->lock);
2020-02-11 11:16:24 +01:00
return sz;
2019-04-04 18:41:39 +02:00
}
2020-02-11 11:16:24 +01:00
size_t bprintf(Buffer_t *buf, const char *fmt, ...)
{
2020-02-11 11:16:24 +01:00
error_t sz;
va_list ap;
va_start(ap, fmt);
2020-02-11 11:16:24 +01:00
sz = vbprintf(buf, fmt, ap);
va_end(ap);
2020-02-11 11:16:24 +01:00
return sz;
2019-03-25 17:33:51 +01:00
}
2019-04-04 20:17:50 +02:00
#define CONVBUFSIZE 100
2019-03-25 17:33:51 +01:00
//
2019-04-04 18:41:39 +02:00
// Actually does BPrintOnBuf's job; doesn't lock anything
2019-03-25 17:33:51 +01:00
// Quite a long function
//
2020-02-11 11:16:24 +01:00
size_t vbprintf(Buffer_t *buf, const char *fmt, va_list ap)
2019-03-25 17:33:51 +01:00
{
2019-04-04 20:17:50 +02:00
error_t rc = 0;
2020-02-11 11:16:24 +01:00
size_t written = 0;
2019-04-04 20:17:50 +02:00
2019-05-18 22:53:57 +02:00
ssize_t width, prec, len;
2019-03-25 17:33:51 +01:00
char type;
uchar uch;
char *s;
2019-03-25 17:33:51 +01:00
// Conversion buffer
2019-04-04 20:17:50 +02:00
char convbuf[CONVBUFSIZE];
2019-03-25 17:33:51 +01:00
// Flags
int plus, minus, space, zero, hash;
// Length modifiers
int l, h, hh;
// Signed
bool sgn;
// Capital digits
bool cap;
// Base
int base;
2020-02-11 11:16:24 +01:00
if (!buf || !fmt) { seterrno(EINVAL); return 0; }
if (buf->flags & (BF_EOF|BF_ERR)) { seterrno(EENDF); return 0; }
2019-03-25 17:33:51 +01:00
if (buf->state != BS_RDWR && buf->state != BS_WRONLY) {
2020-02-11 11:16:24 +01:00
seterrno(EBADF);
return 0;
2019-03-25 17:33:51 +01:00
}
2019-04-04 20:17:50 +02:00
// Progress in format string
while (*fmt && !rc) {
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
// Deal with all non-'%' characters
if (*fmt != '%') {
rc = bputc(buf, *fmt);
2020-02-11 11:16:24 +01:00
written++, fmt++;
2019-04-04 20:17:50 +02:00
continue;
2019-03-25 17:33:51 +01:00
}
2019-04-04 20:17:50 +02:00
//
2020-02-11 11:16:24 +01:00
// %[flags][width|*][.precision|*][length]type
2019-04-04 20:17:50 +02:00
//
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
// Skip the '%'
fmt++;
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
// "%%" modifier
if (*fmt == '%') {
rc = bputc(buf, '%');
2020-02-11 11:16:24 +01:00
written++, fmt++;
2019-04-04 20:17:50 +02:00
continue;
}
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
// 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;
}
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
//
// 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 { \
2019-05-18 22:53:57 +02:00
while (isdigit(*fmt)) { \
2019-04-04 20:17:50 +02:00
name = 10 * name + (*fmt - '0'); \
fmt++; \
} \
} \
} while (0)
// Extract width
bextractwp(width);
2019-03-25 17:33:51 +01:00
// A width below 0 activates the "minus" flag
2019-04-04 20:17:50 +02:00
if (width < 0) {
width = -width;
2019-03-25 17:33:51 +01:00
minus++;
}
2019-04-04 20:17:50 +02:00
// Extract precision
if (*fmt == '.') {
fmt++;
bextractwp(prec);
2019-03-25 17:33:51 +01:00
}
2019-04-04 20:17:50 +02:00
//
// Length field
//
while (*fmt) {
if (*fmt == 'l' || *fmt == 'z' || *fmt == 't') {
fmt++;
l++;
}
else if (*fmt == 'h') {
fmt++;
h++;
}
// Next field
else break;
}
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
// Consistency check
assert(!(l > 0 && h > 0));
assert(!(l > 2 || h > 1));
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
//
// The type field
//
type = *fmt++;
2019-04-04 20:17:50 +02:00
// Characters
if (type == 'c') {
uch = (uchar)va_arg(ap, int);
2019-05-18 22:02:16 +02:00
rc = bputc(buf, uch);
2020-02-11 11:16:24 +01:00
written++;
2019-05-18 22:02:16 +02:00
continue;
}
2019-03-25 17:33:51 +01:00
2019-05-18 22:02:16 +02:00
#ifdef _KALEID_KERNEL
if (type == 'C') {
base = va_arg(ap, int);
2020-02-11 11:16:24 +01:00
if (!(base < 0 || base > VGA_COLOR_WHITE)) {
2019-05-18 22:02:16 +02:00
rc = bputc(buf, RtlColorToChar(base));
2020-02-11 11:16:24 +01:00
written++;
}
2019-04-04 20:17:50 +02:00
continue;
2019-03-25 17:33:51 +01:00
}
2019-05-18 22:02:16 +02:00
#endif
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
// Strings
2019-05-18 22:53:57 +02:00
if (type == 's') {
2019-04-04 20:17:50 +02:00
s = va_arg(ap, char *);
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
if (s == NULL) s = "(null)";
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
// For strings, the precision field gives the maximum
// amount of characters to be read from the stream
// Zero/nonspecified precision means unlimited amount
2019-05-14 11:56:42 +02:00
if (prec == 0) prec = INT_MAX;
2019-04-04 20:17:50 +02:00
2020-02-11 11:16:24 +01:00
for (; !rc && *s && prec-- ; s++) {
2019-04-04 20:17:50 +02:00
rc = bputc(buf, (uchar)*s);
2020-02-11 11:16:24 +01:00
written++;
2019-04-04 20:17:50 +02:00
}
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
continue;
}
2019-05-18 22:53:57 +02:00
// Make sure width and prec aren't too big
// (We didn't do that earlier because %s uses width)
if (width > CONVBUFSIZE || prec > CONVBUFSIZE) {
2020-02-11 11:16:24 +01:00
written++; // Work around "if (rc) return written - 1;"
2019-05-18 22:53:57 +02:00
rc = EINVAL;
break;
}
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
// Decimal, unsigned decimal, hexadecimal, octal and binary numbers
2019-05-18 22:53:57 +02:00
if (type == 'd' || type == 'i') { base = 10; sgn = 1; }
2019-04-04 20:17:50 +02:00
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;
}
2019-03-25 17:33:51 +01:00
2019-04-13 09:27:36 +02:00
// End of string too soon
else if (type == '\0') {
bputc(buf, '%');
2020-02-11 11:16:24 +01:00
written++; // Work around "if (rc) return written - 1;"
2019-04-04 20:17:50 +02:00
rc = EINVAL;
break;
}
2019-03-25 17:33:51 +01:00
2019-04-13 09:27:36 +02:00
// Unknown/unsupported modifier
else {
2020-02-11 11:16:24 +01:00
rc = bputc(buf, '%');
written++;
2019-04-13 09:27:36 +02:00
continue;
}
2019-04-04 20:17:50 +02:00
//
// Numerical conversions
//
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
// We use s to iterate convbuf
s = convbuf;
2019-03-25 17:33:51 +01:00
2019-04-05 10:20:10 +02:00
#define bdoconvrt(pref, type, vtype) \
do { \
type i_##type = (type)va_arg(ap, vtype); \
pref##toa(i_##type, s, base); \
2019-04-04 20:17:50 +02:00
} 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);
}
}
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
else {
if (sgn) bdoconvrt(l, long, long);
else bdoconvrt(ul, ulong, ulong);
}
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
//
// Implement flags and %X, and prints
//
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
// Capital letter digits
if (base > 10 && cap) {
for (; *s ; s++)
if (islower(*s)) *s = toupper(*s);
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
// We use this "opportunity" to compute the length of s
len = s - convbuf;
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
// 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--;
2019-05-14 12:05:59 +02:00
else if (hash) {
width -= (base == 8 ? 1 :
((base == 2 || base == 16) ? 2 : 0));
}
2019-04-04 20:17:50 +02:00
// When padding with spaces, we pad before +/-'s etc
2020-02-11 11:16:24 +01:00
if (!minus && !zero && width > len) {
for (; !rc && width > len ; width--) {
rc = bputc(buf, ' ');
written++;
}
}
2019-04-04 20:17:50 +02:00
// Deal with signs and the hash flag
2020-02-11 11:16:24 +01:00
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++;
}
}
2019-04-04 20:17:50 +02:00
// Deal with padding by zeroes
// The 'minus' flag makes no sense with the 'zero' one
2020-02-11 11:16:24 +01:00
if (zero && width > len) {
for (; !rc && width > len ; width--) {
rc = bputc(buf, '0');
written++;
}
}
2019-04-04 20:17:50 +02:00
// Output the actual number
for (; !rc && *s ; s++) {
rc = bputc(buf, (uchar)*s);
2020-02-11 11:16:24 +01:00
written++;
2019-04-04 20:17:50 +02:00
}
2019-03-25 17:33:51 +01:00
2019-04-04 20:17:50 +02:00
// 'minus' padding, only with spaces
2020-02-11 11:16:24 +01:00
if (minus && !zero && width > len) {
for (; !rc && width > len ; width--) {
rc = bputc(buf, ' ');
written++;
}
}
2019-04-04 20:17:50 +02:00
// Carry on to next modifier
2020-02-11 11:16:24 +01:00
}
// For debugging purposes
assert(!rc && "vbprintf() error");
seterrno(rc);
2019-04-04 20:17:50 +02:00
2020-02-11 11:16:24 +01:00
if (rc)
return written - 1; // "- 1" because last bputc() must have failed
else
return written;
2019-03-25 17:33:51 +01:00
}