1 /* 2 * File: printk.c 3 * Purpose: The standard C library routine printf(), but without 4 * all the baggage. 5 */ 6 7 #include <stdarg.h> 8 /********************************************************************/ 9 typedef struct 10 { 11 int dest; 12 void (*func)( char ); 13 char * loc; 14 } PRINTK_INFO; 15 int printk( PRINTK_INFO *, const char *, va_list ); 16 /********************************************************************/ 17 #define DEST_CONSOLE (1) 18 #define DEST_STRING (2) 19 #define FLAGS_MINUS (0x01) 20 #define FLAGS_PLUS (0x02) 21 #define FLAGS_SPACE (0x04) 22 #define FLAGS_ZERO (0x08) 23 #define FLAGS_POUND (0x10) 24 #define IS_FLAG_MINUS(a) (a & FLAGS_MINUS) 25 #define IS_FLAG_PLUS(a) (a & FLAGS_PLUS) 26 #define IS_FLAG_SPACE(a) (a & FLAGS_SPACE) 27 #define IS_FLAG_ZERO(a) (a & FLAGS_ZERO) 28 #define IS_FLAG_POUND(a) (a & FLAGS_POUND) 29 #define LENMOD_h (0x01) 30 #define LENMOD_l (0x02) 31 #define LENMOD_L (0x04) 32 #define IS_LENMOD_h(a) (a & LENMOD_h) 33 #define IS_LENMOD_l(a) (a & LENMOD_l) 34 #define IS_LENMOD_L(a) (a & LENMOD_L) 35 #define FMT_d (0x0001) 36 #define FMT_o (0x0002) 37 #define FMT_x (0x0004) 38 #define FMT_X (0x0008) 39 #define FMT_u (0x0010) 40 #define FMT_c (0x0020) 41 #define FMT_s (0x0040) 42 #define FMT_p (0x0080) 43 #define FMT_n (0x0100) 44 #define IS_FMT_d(a) (a & FMT_d) 45 #define IS_FMT_o(a) (a & FMT_o) 46 #define IS_FMT_x(a) (a & FMT_x) 47 #define IS_FMT_X(a) (a & FMT_X) 48 #define IS_FMT_u(a) (a & FMT_u) 49 #define IS_FMT_c(a) (a & FMT_c) 50 #define IS_FMT_s(a) (a & FMT_s) 51 #define IS_FMT_p(a) (a & FMT_p) 52 #define IS_FMT_n(a) (a & FMT_n) 53 /********************************************************************/ 54 static void printk_putc( int c, int * count, PRINTK_INFO * info ) 55 { 56 switch ( info->dest ) 57 { 58 case DEST_CONSOLE: 59 info->func( (char) c ); 60 break; 61 case DEST_STRING: 62 *( info->loc ) = (unsigned char) c; 63 ++( info->loc ); 64 break; 65 default: 66 break; 67 } 68 *count += 1; 69 } 70 /********************************************************************/ 71 static int printk_mknumstr( char * numstr, void * nump, int neg, int radix ) 72 { 73 int a, b, c; 74 unsigned int ua, ub, uc; 75 int nlen; 76 char * nstrp; 77 nlen = 0; 78 nstrp = numstr; 79 *nstrp++ = '\0'; 80 if ( neg ) 81 { 82 a = *(int*) nump; 83 if ( a == 0 ) 84 { 85 *nstrp = '0'; 86 ++nlen; 87 goto done; 88 } 89 while ( a != 0 ) 90 { 91 b = (int) a / (int) radix; 92 c = (int) a - ( (int) b * (int) radix ); 93 if ( c < 0 ) 94 { 95 c = ~c + 1 + '0'; 96 } 97 else 98 { 99 c = c + '0'; 100 } 101 a = b; 102 *nstrp++ = (char) c; 103 ++nlen; 104 } 105 } 106 else 107 { 108 ua = *(unsigned int*) nump; 109 if ( ua == 0 ) 110 { 111 *nstrp = '0'; 112 ++nlen; 113 goto done; 114 } 115 while ( ua != 0 ) 116 { 117 ub = (unsigned int) ua / (unsigned int) radix; 118 uc = (unsigned int) ua - ( (unsigned int) ub * (unsigned int) radix ); 119 if ( uc < 10 ) 120 { 121 uc = uc + '0'; 122 } 123 else 124 { 125 uc = uc - 10 + 'A'; 126 } 127 ua = ub; 128 *nstrp++ = (char) uc; 129 ++nlen; 130 } 131 } 132 done: return nlen; 133 } 134 /********************************************************************/ 135 static void printk_pad_zero( int curlen, int field_width, int * count, 136 PRINTK_INFO * info ) 137 { 138 int i; 139 for ( i = curlen; i < field_width; i++ ) 140 { 141 printk_putc( '0', count, info ); 142 } 143 } 144 /********************************************************************/ 145 static void printk_pad_space( int curlen, int field_width, int * count, 146 PRINTK_INFO * info ) 147 { 148 int i; 149 for ( i = curlen; i < field_width; i++ ) 150 { 151 printk_putc( ' ', count, info ); 152 } 153 } 154 /********************************************************************/ 155 int printk( PRINTK_INFO * info, const char * fmt, va_list ap ) 156 { 157 /* va_list ap; */ 158 char * p; 159 int c; 160 char vstr[ 33 ]; 161 char * vstrp; 162 int vlen; 163 int done; 164 int count = 0; 165 int flags_used; 166 int field_width; 167 #if 0 168 int precision_used; 169 int precision_width; 170 int length_modifier; 171 #endif 172 int ival; 173 int schar, dschar; 174 int * ivalp; 175 char * sval; 176 int cval; 177 unsigned int uval; 178 /* 179 * Start parsing apart the format string and display appropriate 180 * formats and data. 181 */ 182 for ( p = (char*) fmt; ( c = *p ) != 0; p++ ) 183 { 184 /* 185 * All formats begin with a '%' marker. Special chars like 186 * '\n' or '\t' are normally converted to the appropriate 187 * character by the __compiler__. Thus, no need for this 188 * routine to account for the '\' character. 189 */ 190 if ( c != '%' ) 191 { 192 /* 193 * This needs to be replaced with something like 194 * 'out_char()' or call an OS routine. 195 */ 196 #ifndef UNIX_DEBUG 197 if ( c != '\n' ) 198 { 199 printk_putc( c, &count, info ); 200 } 201 else 202 { 203 printk_putc( 0x0D /* CR */, &count, info ); 204 printk_putc( 0x0A /* LF */, &count, info ); 205 } 206 #else 207 printk_putc( c, &count, info ); 208 #endif 209 /* 210 * By using 'continue', the next iteration of the loop 211 * is used, skipping the code that follows. 212 */ 213 continue; 214 } 215 /* 216 * First check for specification modifier flags. 217 */ 218 flags_used = 0; 219 done = FALSE; 220 while ( !done ) 221 { 222 switch ( /* c = */*++p ) 223 { 224 case '-': 225 flags_used |= FLAGS_MINUS; 226 break; 227 case '+': 228 flags_used |= FLAGS_PLUS; 229 break; 230 case ' ': 231 flags_used |= FLAGS_SPACE; 232 break; 233 case '0': 234 flags_used |= FLAGS_ZERO; 235 break; 236 case '#': 237 flags_used |= FLAGS_POUND; 238 break; 239 default: 240 /* we've gone one char too far */ 241 --p; 242 done = TRUE; 243 break; 244 } 245 } 246 /* 247 * Next check for minimum field width. 248 */ 249 field_width = 0; 250 done = FALSE; 251 while ( !done ) 252 { 253 switch ( c = *++p ) 254 { 255 case '0': 256 case '1': 257 case '2': 258 case '3': 259 case '4': 260 case '5': 261 case '6': 262 case '7': 263 case '8': 264 case '9': 265 field_width = ( field_width * 10 ) + ( c - '0' ); 266 break; 267 default: 268 /* we've gone one char too far */ 269 --p; 270 done = TRUE; 271 break; 272 } 273 } 274 /* 275 * Next check for the width and precision field separator. 276 */ 277 if ( /* (c = *++p) */*++p == '.' ) 278 { 279 /* precision_used = TRUE; */ 280 /* 281 * Must get precision field width, if present. 282 */ 283 /* precision_width = 0; */ 284 done = FALSE; 285 while ( !done ) 286 { 287 switch ( /* c = uncomment if used below */*++p ) 288 { 289 case '0': 290 case '1': 291 case '2': 292 case '3': 293 case '4': 294 case '5': 295 case '6': 296 case '7': 297 case '8': 298 case '9': 299 #if 0 300 precision_width = ( precision_width * 10 ) + ( c - '0' ); 301 #endif 302 break; 303 default: 304 /* we've gone one char too far */ 305 --p; 306 done = TRUE; 307 break; 308 } 309 } 310 } 311 else 312 { 313 /* we've gone one char too far */ 314 --p; 315 #if 0 316 precision_used = FALSE; 317 precision_width = 0; 318 #endif 319 } 320 /* 321 * Check for the length modifier. 322 */ 323 /* length_modifier = 0; */ 324 switch ( /* c = */*++p ) 325 { 326 case 'h': 327 /* length_modifier |= LENMOD_h; */ 328 break; 329 case 'l': 330 /* length_modifier |= LENMOD_l; */ 331 break; 332 case 'L': 333 /* length_modifier |= LENMOD_L; */ 334 break; 335 default: 336 /* we've gone one char too far */ 337 --p; 338 break; 339 } 340 /* 341 * Now we're ready to examine the format. 342 */ 343 switch ( c = *++p ) 344 { 345 case 'd': 346 case 'i': 347 ival = ( int )va_arg( ap, int ); 348 vlen = printk_mknumstr( vstr, &ival, TRUE, 10 ); 349 vstrp = &vstr[ vlen ]; 350 if ( ival < 0 ) 351 { 352 schar = '-'; 353 ++vlen; 354 } 355 else 356 { 357 if ( IS_FLAG_PLUS( flags_used ) ) 358 { 359 schar = '+'; 360 ++vlen; 361 } 362 else 363 { 364 if ( IS_FLAG_SPACE( flags_used ) ) 365 { 366 schar = ' '; 367 ++vlen; 368 } 369 else 370 { 371 schar = 0; 372 } 373 } 374 } 375 dschar = FALSE; 376 /* 377 * do the ZERO pad. 378 */ 379 if ( IS_FLAG_ZERO( flags_used ) ) 380 { 381 if ( schar ) 382 printk_putc( schar, &count, info ); 383 dschar = TRUE; 384 printk_pad_zero( vlen, field_width, &count, info ); 385 vlen = field_width; 386 } 387 else 388 { 389 if ( !IS_FLAG_MINUS( flags_used ) ) 390 { 391 printk_pad_space( vlen, field_width, &count, info ); 392 if ( schar ) 393 printk_putc( schar, &count, info ); 394 dschar = TRUE; 395 } 396 } 397 /* the string was built in reverse order, now display in */ 398 /* correct order */ 399 if ( !dschar && schar ) 400 { 401 printk_putc( schar, &count, info ); 402 } 403 goto cont_xd; 404 case 'x': 405 case 'X': 406 uval = ( unsigned int )va_arg( ap, unsigned int ); 407 vlen = printk_mknumstr( vstr, &uval, FALSE, 16 ); 408 vstrp = &vstr[ vlen ]; 409 dschar = FALSE; 410 if ( IS_FLAG_ZERO( flags_used ) ) 411 { 412 if ( IS_FLAG_POUND( flags_used ) ) 413 { 414 printk_putc( '0', &count, info ); 415 printk_putc( 'x', &count, info ); 416 /*vlen += 2;*/ 417 dschar = TRUE; 418 } 419 printk_pad_zero( vlen, field_width, &count, info ); 420 vlen = field_width; 421 } 422 else 423 { 424 if ( !IS_FLAG_MINUS( flags_used ) ) 425 { 426 if ( IS_FLAG_POUND( flags_used ) ) 427 { 428 vlen += 2; 429 } 430 printk_pad_space( vlen, field_width, &count, info ); 431 if ( IS_FLAG_POUND( flags_used ) ) 432 { 433 printk_putc( '0', &count, info ); 434 printk_putc( 'x', &count, info ); 435 dschar = TRUE; 436 } 437 } 438 } 439 if ( ( IS_FLAG_POUND( flags_used ) ) && !dschar ) 440 { 441 printk_putc( '0', &count, info ); 442 printk_putc( 'x', &count, info ); 443 vlen += 2; 444 } 445 goto cont_xd; 446 case 'o': 447 uval = ( unsigned int )va_arg( ap, unsigned int ); 448 vlen = printk_mknumstr( vstr, &uval, FALSE, 8 ); 449 goto cont_u; 450 case 'b': 451 uval = ( unsigned int )va_arg( ap, unsigned int ); 452 vlen = printk_mknumstr( vstr, &uval, FALSE, 2 ); 453 goto cont_u; 454 case 'p': 455 uval = ( unsigned int )va_arg( ap, void* ); 456 vlen = printk_mknumstr( vstr, &uval, FALSE, 16 ); 457 goto cont_u; 458 case 'u': 459 uval = ( unsigned int )va_arg( ap, unsigned int ); 460 vlen = printk_mknumstr( vstr, &uval, FALSE, 10 ); 461 cont_u: vstrp = &vstr[ vlen ]; 462 if ( IS_FLAG_ZERO( flags_used ) ) 463 { 464 printk_pad_zero( vlen, field_width, &count, info ); 465 vlen = field_width; 466 } 467 else 468 { 469 if ( !IS_FLAG_MINUS( flags_used ) ) 470 { 471 printk_pad_space( vlen, field_width, &count, info ); 472 } 473 } 474 cont_xd: while ( *vstrp ) 475 printk_putc( *vstrp--, &count, info ); 476 if ( IS_FLAG_MINUS( flags_used ) ) 477 { 478 printk_pad_space( vlen, field_width, &count, info ); 479 } 480 break; 481 case 'c': 482 cval = ( char )va_arg( ap, unsigned int ); 483 printk_putc( cval, &count, info ); 484 break; 485 case 's': 486 sval = ( char* )va_arg( ap, char* ); 487 if ( sval ) 488 { 489 vlen = strlen( sval ); 490 if ( !IS_FLAG_MINUS( flags_used ) ) 491 { 492 printk_pad_space( vlen, field_width, &count, info ); 493 } 494 while ( *sval ) 495 printk_putc( *sval++, &count, info ); 496 if ( IS_FLAG_MINUS( flags_used ) ) 497 { 498 printk_pad_space( vlen, field_width, &count, info ); 499 } 500 } 501 break; 502 case 'n': 503 ivalp = ( int* )va_arg( ap, int* ); 504 *ivalp = count; 505 break; 506 default: 507 printk_putc( c, &count, info ); 508 break; 509 } 510 } 511 return count; 512 } 513 /********************************************************************/ 514 int printf( const char * fmt, ... ) 515 { 516 va_list ap; 517 int rvalue; 518 PRINTK_INFO info; 519 info.dest = DEST_CONSOLE; 520 info.func = &out_char; 521 /* 522 * Initialize the pointer to the variable length argument list. 523 */ 524 va_start( ap, fmt ); 525 rvalue = printk( &info, fmt, ap ); 526 /* 527 * Cleanup the variable length argument list. 528 */ 529 va_end( ap ); 530 return rvalue; 531 } 532 /********************************************************************/ 533 int sprintf( char * s, const char * fmt, ... ) 534 { 535 va_list ap; 536 int rvalue = 0; 537 PRINTK_INFO info; 538 /* 539 * Initialize the pointer to the variable length argument list. 540 */ 541 if ( s != 0 ) 542 { 543 info.dest = DEST_STRING; 544 info.loc = s; 545 va_start( ap, fmt ); 546 rvalue = printk( &info, fmt, ap ); 547 *info.loc = '\0'; 548 va_end( ap ); 549 } 550 return rvalue; 551 } 552 /********************************************************************/