/* ConsolePrintFormat.cc * vim: set tw=80: * Eryn Wells */ /** * Implementation of Console::printFormat, a printf style method. */ #include #include "Console.hh" #include "kstd/ASCII.hh" #include "kstd/CString.hh" namespace { void itoa(int value, char* buffer, int base); struct PrintfSpec { enum Size { SizeNormal, SizeDoubleShort, SizeShort, SizeLong, SizeDoubleLong, }; enum Type { TypeInt, TypeHex, TypeChar, TypeString }; union Value { int d; char hhd; short int hd; long int ld; long long int lld; unsigned int x; unsigned char hhx; unsigned short int hx; unsigned long int lx; unsigned long long int llx; char c; char* s; }; bool zero; int width; Size size; Type type; Value value; void clear(); int print(kernel::Console& console); }; void PrintfSpec::clear() { zero = false; width = 0; size = PrintfSpec::SizeNormal; type = PrintfSpec::TypeInt; } int PrintfSpec::print(kernel::Console& console) { int nchars = 0; int length = 0; char buf[32]; char pad = ' '; char *str; /* * TypeChar is a special case because it will always be a single character * in length and there is no \0 to terminate it. */ if (type == TypeChar) { if (width == 0) { width = 1; } for (int i = 1; i < width; i++) { console.printChar(' '); } console.printChar(value.c); return width; } if (type == TypeInt || type == TypeHex) { if (zero) { pad = '0'; } if (type == TypeInt) { switch (size) { case SizeNormal: itoa(value.d, buf, 10); break; case SizeDoubleShort: itoa(value.hhd, buf, 10); break; case SizeShort: itoa(value.hd, buf, 10); break; case SizeLong: itoa(value.ld, buf, 10); break; case SizeDoubleLong: itoa(value.lld, buf, 10); break; } } else { switch (size) { case SizeNormal: itoa(value.x, buf, 16); break; case SizeDoubleShort: itoa(value.hhx, buf, 16); break; case SizeShort: itoa(value.hx, buf, 16); break; case SizeLong: itoa(value.lx, buf, 16); break; case SizeDoubleLong: itoa(value.llx, buf, 16); break; } } length = kstd::CString::length(buf); if (width < length) { width = length; } str = buf; } else if (type == TypeString) { length = kstd::CString::length(value.s); if (width < length) { width = length; } str = value.s; } else { // Don't know how to print this. return 0; } for (int i = width; i > 0; i--) { if (i <= length) { console.printChar(str[length - i]); } else if (i <= width) { console.printChar(pad); } } return nchars; } void itoa(int value, char* buffer, int base) { const bool neg = base == 10 && value < 0; if (neg) { value *= -1; } char* p = buffer; int place; do { place = value % base; value /= base; *p++ = place < 10 ? place + '0' : (place - 10) + 'a'; } while (value != 0); if (neg) { *p++ = '-'; } *p-- = '\0'; // Reverse the string. char* t = buffer; char c; while (p > t) { c = *p; *p-- = *t; *t++ = c; } } inline bool is_size(char c) { return c == 'h' || c == 'l'; } inline bool is_specifier(char c) { return c == 'd' || c == 'x' || c == 'X' || c == 'c' || c == 's'; } } /* anonymous namespace */ namespace kernel { int Console::printFormat(const char* fmt, ...) { enum { StateDefault = 0, StatePercent, StateWidth, StateSize } state = StateDefault; va_list vl; va_start(vl, fmt); int nchars = 0; PrintfSpec spec; for (const char* p = fmt; *p != 0; p++) { switch (state) { case StateDefault: if (*p == '%') { state = StatePercent; spec.clear(); } else { printChar(*p); nchars++; } break; case StatePercent: if (*p == '%') { state = StateDefault; printChar(*p); nchars++; } else if (kstd::Char::isDigit(*p)) { if (*p == '0' && !spec.zero) { spec.zero = true; } else { state = StateWidth; spec.width = *p - '0'; } } else if (is_size(*p)) { goto state_size; } else if (is_specifier(*p)) { goto state_specifier; } break; case StateWidth: if (kstd::Char::isDigit(*p)) { spec.width = 10 * spec.width + (*p - '0'); } else if (is_size(*p)) { state = StateSize; goto state_size; } else if (is_specifier(*p)) { goto state_specifier; } break; case StateSize: if (is_size(*p)) { goto state_size; } else if (is_specifier(*p)) { goto state_specifier; } break; default: printChar(*p); nchars++; break; } continue; state_size: if (*p == 'h') { spec.size = (spec.size == PrintfSpec::SizeShort) ? PrintfSpec::SizeDoubleShort : PrintfSpec::SizeShort; } else if (*p == 'l') { spec.size = (spec.size == PrintfSpec::SizeLong) ? PrintfSpec::SizeDoubleLong : PrintfSpec::SizeLong; } continue; state_specifier: state = StateDefault; switch (*p) { case 'd': case 'i': switch (spec.size) { case PrintfSpec::SizeNormal: spec.value.d = va_arg(vl, int); break; case PrintfSpec::SizeDoubleShort: spec.value.hhd = (signed char)va_arg(vl, int); break; case PrintfSpec::SizeShort: spec.value.hd = (short int)va_arg(vl, int); break; case PrintfSpec::SizeLong: spec.value.ld = va_arg(vl, long int); break; case PrintfSpec::SizeDoubleLong: spec.value.lld = va_arg(vl, long long int); break; } spec.type = PrintfSpec::TypeInt; break; case 'x': case 'X': switch (spec.size) { case PrintfSpec::SizeNormal: spec.value.x = va_arg(vl, unsigned int); break; case PrintfSpec::SizeDoubleShort: spec.value.hhx = (unsigned char)va_arg(vl, unsigned int); break; case PrintfSpec::SizeShort: spec.value.hx = (unsigned short int)va_arg(vl, unsigned int); break; case PrintfSpec::SizeLong: spec.value.lx = va_arg(vl, unsigned long int); break; case PrintfSpec::SizeDoubleLong: spec.value.llx = va_arg(vl, unsigned long long int); break; } spec.type = PrintfSpec::TypeHex; break; case 'c': spec.value.c = va_arg(vl, int); spec.type = PrintfSpec::TypeChar; break; case 's': spec.value.s = va_arg(vl, char*); spec.type = PrintfSpec::TypeString; break; } nchars += spec.print(*this); continue; } va_end(vl); return nchars; } } /* namespace kernel */