00001
00038 #include <assert.h>
00039 #include <interrupt.h>
00040 #include <stream.h>
00041 #include <stdarg.h>
00042 #include <string.h>
00043 #include <util.h>
00044
00058 enum conversion_state {
00060 STATE_NORMAL,
00062 STATE_FLAG,
00064 STATE_WIDTH,
00066 STATE_PERIOD,
00068 STATE_PRECISION,
00070 STATE_LENGTH,
00072 STATE_CONVSPEC,
00073 };
00074
00079 struct printf_conversion {
00081 int width;
00083 int precision;
00085 char length;
00087 char spec;
00089 char pad_char;
00091 union {
00093 long d;
00095 unsigned long u;
00097 double f;
00099 const char *s;
00101 void *p;
00103 int *n;
00104 } arg;
00105 };
00106
00115 static void stream_priv_write(struct stream *stream,
00116 const char *data, size_t len)
00117 {
00118 size_t partial;
00119 unsigned int head;
00120 irqflags_t iflags;
00121
00122 while (len) {
00123 if (stream_buf_unused(stream) < len) {
00124 if (!stream->ops->make_room(stream, len))
00125 return;
00126 }
00127
00128 iflags = cpu_irq_save();
00129 head = stream_buf_head(stream);
00130 partial = min_u(len, stream_buf_unused_before_end(stream));
00131 memcpy(&stream->data[head], data, partial);
00132 ring_insert_entries(&stream->ring, partial);
00133 cpu_irq_restore(iflags);
00134
00135 data += partial;
00136 len -= partial;
00137 }
00138 }
00139
00147 static int stream_priv_putchar(struct stream *stream, char c)
00148 {
00149 if (c == '\n')
00150 stream_priv_putchar(stream, '\r');
00151
00152 stream_priv_write(stream, &c, 1);
00153
00154 return c;
00155 }
00156
00164 static int stream_priv_putstr(struct stream *stream, const char *str)
00165 {
00166 int len;
00167
00168 len = strlen(str);
00169 stream_priv_write(stream, str, len);
00170
00171 return len;
00172 }
00173
00183 static void stream_priv_commit(struct stream *stream)
00184 {
00185 stream->ops->commit(stream);
00186 }
00187
00198 static int stream_priv_print_signed(struct stream *stream,
00199 struct printf_conversion *conv)
00200 {
00201 char buf[32];
00202 long number = conv->arg.d;
00203 bool negative = false;
00204 int i = sizeof(buf);
00205 int len;
00206 char c;
00207
00208 if (number == 0)
00209 buf[--i] = '0';
00210
00211 if (number < 0) {
00212 negative = true;
00213 number = -number;
00214 }
00215
00216 while (number) {
00217 c = '0' + number % 10;
00218 number /= 10;
00219 buf[--i] = c;
00220 }
00221
00222 if (negative)
00223 buf[--i] = '-';
00224
00225 if (conv->width > sizeof(buf))
00226 conv->width = sizeof(buf);
00227
00228 while ((sizeof(buf) - i) < conv->width)
00229 buf[--i] = conv->pad_char;
00230
00231 len = sizeof(buf) - i;
00232 stream_priv_write(stream, buf + i, len);
00233
00234 return len;
00235 }
00236
00247 static int stream_priv_print_unsigned(struct stream *stream,
00248 struct printf_conversion *conv)
00249 {
00250 char buf[32];
00251 unsigned long number = conv->arg.u;
00252 int i = sizeof(buf);
00253 int len;
00254 char c;
00255
00256 if (number == 0)
00257 buf[--i] = '0';
00258
00259 switch (conv->spec) {
00260 case 'o':
00261 while (number) {
00262 c = '0' + (number & 7);
00263 number >>= 3;
00264 buf[--i] = c;
00265 }
00266 break;
00267 case 'u':
00268 while (number) {
00269 c = '0' + (number % 10);
00270 number /= 10;
00271 buf[--i] = c;
00272 }
00273 break;
00274 case 'x':
00275 while (number) {
00276 if ((number & 15) > 9)
00277 c = 'a' - 10 + (number & 15);
00278 else
00279 c = '0' + (number & 15);
00280 number >>= 4;
00281 buf[--i] = c;
00282 }
00283 break;
00284 case 'X':
00285 while (number) {
00286 if ((number & 15) > 9)
00287 c = 'A' - 10 + (number & 15);
00288 else
00289 c = '0' + (number & 15);
00290 number >>= 4;
00291 buf[--i] = c;
00292 }
00293 break;
00294 }
00295
00296 if (conv->width > sizeof(buf))
00297 conv->width = sizeof(buf);
00298
00299 while ((sizeof(buf) - i) < conv->width)
00300 buf[--i] = conv->pad_char;
00301
00302 len = sizeof(buf) - i;
00303 stream_priv_write(stream, buf + i, len);
00304
00305 return len;
00306 }
00307
00309
00323 int stream_putstr(struct stream *stream, const char *str)
00324 {
00325 int len;
00326
00327 len = stream_priv_putstr(stream, str);
00328 stream_priv_commit(stream);
00329
00330 return len;
00331 }
00332
00341 int stream_putchar(struct stream *stream, int c)
00342 {
00343 c = stream_priv_putchar(stream, c);
00344 stream_priv_commit(stream);
00345
00346 return c;
00347 }
00348
00365 int stream_vprintf(struct stream *stream, const char *format, va_list ap)
00366 {
00367 int state = STATE_NORMAL;
00368 struct printf_conversion conv;
00369 int n = 0;
00370 char c;
00371
00372 while ((c = *format++)) {
00373 switch (state) {
00374 case STATE_NORMAL:
00375 if (c == '%') {
00376 state = STATE_FLAG;
00377 conv.width = 0;
00378 conv.precision = 0;
00379 conv.length = 0;
00380 conv.pad_char = ' ';
00381 } else {
00382 stream_priv_putchar(stream, c);
00383 n++;
00384 }
00385 break;
00386
00387 case STATE_FLAG:
00388 state = STATE_WIDTH;
00389
00390
00391 switch (c) {
00392 case '0':
00393 conv.pad_char = '0';
00394 break;
00395 case '#':
00396 case '-':
00397 case ' ':
00398 case '+':
00399 break;
00400
00401 case '%':
00402
00403 stream_priv_putchar(stream, c);
00404 n++;
00405 state = STATE_NORMAL;
00406 break;
00407
00408 default:
00409 goto state_width;
00410 }
00411 break;
00412
00413 state_width:
00414 case STATE_WIDTH:
00415 if (isdigit(c) && (c != '0' || conv.width != 0)) {
00416 conv.width *= 10;
00417 conv.width += c - '0';
00418 break;
00419 }
00420
00421 state = STATE_PERIOD;
00422
00423
00424 case STATE_PERIOD:
00425 if (c != '.') {
00426 state = STATE_LENGTH;
00427 goto state_length;
00428 }
00429 state = STATE_PRECISION;
00430 break;
00431
00432 case STATE_PRECISION:
00433
00434 if (isdigit(c))
00435 break;
00436
00437 state = STATE_LENGTH;
00438
00439
00440 state_length:
00441 case STATE_LENGTH:
00442
00443 if (c == 'h' || c == 'l' || c == 'L') {
00444 conv.length = c;
00445 break;
00446 } else if (c == 'z') {
00447 if (sizeof(size_t) == sizeof(long))
00448 conv.length = 'l';
00449 break;
00450 }
00451
00452 state = STATE_CONVSPEC;
00453
00454
00455 case STATE_CONVSPEC:
00456 conv.spec = c;
00457
00458 switch (c) {
00459 case 'd':
00460 case 'i':
00461 if (conv.length == 'l')
00462 conv.arg.d = va_arg(ap, long);
00463 else
00464 conv.arg.d = va_arg(ap, int);
00465 n += stream_priv_print_signed(stream, &conv);
00466 break;
00467 case 'o':
00468 case 'u':
00469 case 'x':
00470 case 'X':
00471 if (conv.length == 'l')
00472 conv.arg.u = va_arg(ap, unsigned long);
00473 else
00474 conv.arg.u = va_arg(ap, unsigned int);
00475 n += stream_priv_print_unsigned(stream, &conv);
00476 break;
00477 case 'c':
00478 conv.arg.d = va_arg(ap, int);
00479 stream_priv_putchar(stream, conv.arg.d);
00480 n++;
00481 break;
00482
00483
00484
00485 case 's':
00486 conv.arg.s = va_arg(ap, const char *);
00487 n += stream_priv_putstr(stream, conv.arg.s);
00488 break;
00489 case 'p':
00490 conv.arg.p = va_arg(ap, void *);
00491 stream_priv_write(stream, "0x", 2);
00492 n += 2;
00493 conv.spec = 'x';
00494 n += stream_priv_print_unsigned(stream, &conv);
00495 break;
00496 case 'n':
00497 conv.arg.n = va_arg(ap, int *);
00498 *conv.arg.n = n;
00499 break;
00500 }
00501
00502 state = STATE_NORMAL;
00503 break;
00504 }
00505 }
00506
00507 stream_priv_commit(stream);
00508
00509 return n;
00510 }
00511
00518 int stream_printf(struct stream *stream, const char *format, ...)
00519 {
00520 int n;
00521 va_list ap;
00522
00523 va_start(ap, format);
00524 n = stream_vprintf(stream, format, ap);
00525 va_end(ap);
00526
00527 return n;
00528 }
00529