374 lines
9.7 KiB
C
374 lines
9.7 KiB
C
|
//----------------------------------------------------------------------------//
|
||
|
// 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 <https://www.gnu.org/licenses/>. //
|
||
|
//----------------------------------------------------------------------------//
|
||
|
|
||
|
#include <kernel/buf.h>
|
||
|
|
||
|
#include <kernel/term.h> //XXX
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
// 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 *s;
|
||
|
uchar uch;
|
||
|
|
||
|
// Conversion buffer
|
||
|
uchar convbuf[100] = {0};
|
||
|
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 (; !rc && *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
|
||
|
//
|
||
|
|
||
|
//----------------------------------------------------------------------------//
|
||
|
|
||
|
//
|
||
|
// 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, finally!
|
||
|
//
|
||
|
type = *fmt++;
|
||
|
|
||
|
// Characters
|
||
|
if (type == 'c') {
|
||
|
uch = (uchar)va_arg(ap, int);
|
||
|
bputc(buf, uch);
|
||
|
|
||
|
goto loop;
|
||
|
}
|
||
|
|
||
|
// Strings
|
||
|
else if (type == 's') {
|
||
|
s = (uchar *)va_arg(ap, char *);
|
||
|
|
||
|
for (; !rc && *s ; s++) {
|
||
|
rc = bputc(buf, *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++;
|
||
|
if (width < 12) width = 12;
|
||
|
}
|
||
|
|
||
|
// Unknown/unsupported modifier
|
||
|
else {
|
||
|
rc = EINVAL;
|
||
|
goto leave;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------//
|
||
|
|
||
|
//
|
||
|
// Numerical conversions
|
||
|
//
|
||
|
|
||
|
// We re-use s to iterate convbuf
|
||
|
s = convbuf;
|
||
|
|
||
|
// Deal with signed conversions
|
||
|
if (sgn) {
|
||
|
if (l) ltoa(va_arg(ap, long), (char *)s, base);
|
||
|
else if (h == 0) itoa(va_arg(ap, int), (char *)s, base);
|
||
|
else if (h == 1) itoa((short)va_arg(ap, int), (char *)s, base);
|
||
|
else /* h == 2 */ itoa((char)va_arg(ap, int), (char *)s, base);
|
||
|
}
|
||
|
|
||
|
// Deal with unsigned conversions
|
||
|
else {
|
||
|
if (l) ultoa((ulong)va_arg(ap, long), (char *)s, base);
|
||
|
else if (h == 0) utoa((uint)va_arg(ap, int), (char *)s, base);
|
||
|
else if (h == 1) utoa((ushort)va_arg(ap, int), (char *)s, base);
|
||
|
else /* h == 2 */ utoa((uchar)va_arg(ap, int), (char *)s, base);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------//
|
||
|
|
||
|
//
|
||
|
// Implement flags and %X
|
||
|
//
|
||
|
|
||
|
// 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((char *)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, *s);
|
||
|
}
|
||
|
|
||
|
// 'minus' padding, only with spaces
|
||
|
if (minus && !zero && width > len)
|
||
|
bdopadding(buf, width, base, ' ');
|
||
|
|
||
|
if (rc > 0) goto leave;
|
||
|
|
||
|
|
||
|
//----------------------------------------------------------------------------//
|
||
|
|
||
|
// Make sure we leave convbuf filled with NULs
|
||
|
memzero(convbuf, sizeof(convbuf));
|
||
|
|
||
|
// Continue parsing fmt
|
||
|
goto loop;
|
||
|
|
||
|
//----------------------------------------------------------------------------//
|
||
|
|
||
|
leave:
|
||
|
ExReleaseLock(&buf->lock);
|
||
|
return rc;
|
||
|
}
|
||
|
|