[9] | 1 | #if ever
|
---|
| 2 | static char sccsid[] = "@(#)printf.c (U of Maryland) FLB 6-Jan-1987";
|
---|
| 3 | static char RCSid[] = "@(#)$Header: /cvsup/minix/src/commands/simple/printf.c,v 1.1.1.1 2005/04/21 14:55:31 beng Exp $";
|
---|
| 4 | #endif
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
| 7 | * Printf - Duplicate the C library routine of the same name, but from
|
---|
| 8 | * the shell command level.
|
---|
| 9 | *
|
---|
| 10 | * Fred Blonder <fred@Mimsy.umd.edu>
|
---|
| 11 | *
|
---|
| 12 | * To Compile:
|
---|
| 13 | % cc -s -O printf.c -o printf
|
---|
| 14 | *
|
---|
| 15 | * $Log: printf.c,v $
|
---|
| 16 | * Revision 1.1.1.1 2005/04/21 14:55:31 beng
|
---|
| 17 | * Initial import of pre-3.0.1
|
---|
| 18 | *
|
---|
| 19 | * Revision 1.1.1.1 2005/04/20 13:33:30 beng
|
---|
| 20 | * Initial import of minix 2.0.4
|
---|
| 21 | *
|
---|
| 22 | * Revision 1.4 87/01/29 20:52:30 fred
|
---|
| 23 | * Re-installed backslash-notation conversion for string & char arguments.
|
---|
| 24 | *
|
---|
| 25 | * Revision 1.3 87/01/29 20:44:23 fred
|
---|
| 26 | * Converted to portable algorithm.
|
---|
| 27 | * Added Roman format for integers.
|
---|
| 28 | * 29-Jan-87 FLB
|
---|
| 29 | *
|
---|
| 30 | * Revision 1.2 87/01/09 19:10:57 fred
|
---|
| 31 | * Fixed bug in argument-count error-checking.
|
---|
| 32 | * Changed backslash escapes within strings to correspond to ANSII C
|
---|
| 33 | * draft standard. (9-Jan-87 FLB)
|
---|
| 34 | *
|
---|
| 35 | */
|
---|
| 36 |
|
---|
| 37 | #include <stdio.h>
|
---|
| 38 | #include <stdlib.h>
|
---|
| 39 | #include <string.h>
|
---|
| 40 | #define EX_OK 0
|
---|
| 41 | #define EX_USAGE 1
|
---|
| 42 |
|
---|
| 43 | int ctrl(char *s);
|
---|
| 44 |
|
---|
| 45 | #define atoi(a) strtoul((a), NULL, 0)
|
---|
| 46 |
|
---|
| 47 | /****************************************************************************/
|
---|
| 48 |
|
---|
| 49 | int main(int argc, char *argv[])
|
---|
| 50 | {
|
---|
| 51 | register char *cp, *conv_spec, **argp, **ep;
|
---|
| 52 | char *ctor(int x);
|
---|
| 53 |
|
---|
| 54 | if (argc < 2) {
|
---|
| 55 | fprintf(stderr,
|
---|
| 56 | "printf: Usage: printf <format-string> [ arg1 . . . ]\n");
|
---|
| 57 | exit(EX_USAGE);
|
---|
| 58 | }
|
---|
| 59 |
|
---|
| 60 | argp = &argv[2]; /* Point at first arg (if any) beyond format string. */
|
---|
| 61 | ep = &argv[argc]; /* Point beyond last arg. */
|
---|
| 62 |
|
---|
| 63 | ctrl(argv[1]); /* Change backslash notation to control chars in fmt string. */
|
---|
| 64 |
|
---|
| 65 | /* Scan format string for conversion specifications, and do appropriate
|
---|
| 66 | conversion on the corresponding argument. */
|
---|
| 67 | for (cp = argv[1]; *cp; cp++) {
|
---|
| 68 | register int dynamic_count;
|
---|
| 69 |
|
---|
| 70 | /* Look for next conversion spec. */
|
---|
| 71 | while (*cp && *cp != '%') {
|
---|
| 72 | putchar(*cp++);
|
---|
| 73 | }
|
---|
| 74 |
|
---|
| 75 | if (!*cp) /* End of format string */
|
---|
| 76 | break;
|
---|
| 77 |
|
---|
| 78 | dynamic_count = 0; /* Begin counting dynamic field width specs. */
|
---|
| 79 | conv_spec = cp++; /* Remember where this conversion begins. */
|
---|
| 80 |
|
---|
| 81 | for (;*cp; cp++) { /* Scan until conversion character. */
|
---|
| 82 | char conv_buf[BUFSIZ]; /* Save conversion string here. */
|
---|
| 83 | register int conv_len; /* Length of ``conv_buf''. */
|
---|
| 84 |
|
---|
| 85 | switch (*cp) { /* Field-width spec.: Keep scanning. */
|
---|
| 86 | case '.': case '0': case '1': case '2': case '3':
|
---|
| 87 | case '4': case '5': case '6': case '7': case '8':
|
---|
| 88 | case '9':
|
---|
| 89 | continue;
|
---|
| 90 |
|
---|
| 91 | case '*': /* Dynamic field-width spec */
|
---|
| 92 | dynamic_count++;
|
---|
| 93 | continue;
|
---|
| 94 |
|
---|
| 95 | case 's': /* String */
|
---|
| 96 | if (&argp[dynamic_count] >= ep) {
|
---|
| 97 | fprintf(stderr,
|
---|
| 98 | "printf: Not enough args for format.\n"
|
---|
| 99 | );
|
---|
| 100 | exit(EX_USAGE);
|
---|
| 101 | }
|
---|
| 102 |
|
---|
| 103 | (void) strncpy(conv_buf, conv_spec,
|
---|
| 104 | conv_len = cp - conv_spec + 1);
|
---|
| 105 | conv_buf[conv_len] = '\0';
|
---|
| 106 |
|
---|
| 107 | switch (dynamic_count) {
|
---|
| 108 | case 0:
|
---|
| 109 | ctrl(*argp);
|
---|
| 110 | printf(conv_buf, *argp++);
|
---|
| 111 | break;
|
---|
| 112 |
|
---|
| 113 | case 1:
|
---|
| 114 | {
|
---|
| 115 | register int a1;
|
---|
| 116 |
|
---|
| 117 | a1 = atoi(*argp++);
|
---|
| 118 | ctrl(*argp);
|
---|
| 119 | printf(conv_buf, a1, *argp++);
|
---|
| 120 | }
|
---|
| 121 | break;
|
---|
| 122 |
|
---|
| 123 | case 2:
|
---|
| 124 | {
|
---|
| 125 | register int a1, a2;
|
---|
| 126 |
|
---|
| 127 | a1 = atoi(*argp++);
|
---|
| 128 | a2 = atoi(*argp++);
|
---|
| 129 | ctrl(*argp);
|
---|
| 130 | printf(conv_buf, a1, a2, *argp++);
|
---|
| 131 | }
|
---|
| 132 | break;
|
---|
| 133 |
|
---|
| 134 | }
|
---|
| 135 | goto out;
|
---|
| 136 |
|
---|
| 137 | case 'c': /* Char */
|
---|
| 138 | if (&argp[dynamic_count] >= ep) {
|
---|
| 139 | fprintf(stderr,
|
---|
| 140 | "printf: Not enough args for format.\n"
|
---|
| 141 | );
|
---|
| 142 | exit(EX_USAGE);
|
---|
| 143 | }
|
---|
| 144 |
|
---|
| 145 | (void) strncpy(conv_buf, conv_spec,
|
---|
| 146 | conv_len = cp - conv_spec + 1);
|
---|
| 147 | conv_buf[conv_len] = '\0';
|
---|
| 148 |
|
---|
| 149 | switch (dynamic_count) {
|
---|
| 150 | case 0:
|
---|
| 151 | ctrl(*argp);
|
---|
| 152 | printf(conv_buf, **argp++);
|
---|
| 153 | break;
|
---|
| 154 |
|
---|
| 155 | case 1:
|
---|
| 156 | {
|
---|
| 157 | register int a1;
|
---|
| 158 |
|
---|
| 159 | a1 = atoi(*argp++);
|
---|
| 160 | ctrl(*argp);
|
---|
| 161 | printf(conv_buf, a1, **argp++);
|
---|
| 162 | }
|
---|
| 163 | break;
|
---|
| 164 |
|
---|
| 165 | case 2:
|
---|
| 166 | {
|
---|
| 167 | register int a1, a2;
|
---|
| 168 |
|
---|
| 169 | a1 = atoi(*argp++);
|
---|
| 170 | a2 = atoi(*argp++);
|
---|
| 171 | ctrl(*argp);
|
---|
| 172 | printf(conv_buf, a1, a2, **argp++);
|
---|
| 173 | }
|
---|
| 174 | break;
|
---|
| 175 | }
|
---|
| 176 | goto out;
|
---|
| 177 |
|
---|
| 178 | case 'd': /* Integer */
|
---|
| 179 | case 'o':
|
---|
| 180 | case 'x':
|
---|
| 181 | case 'X':
|
---|
| 182 | case 'u':
|
---|
| 183 | if (&argp[dynamic_count] >= ep) {
|
---|
| 184 | fprintf(stderr,
|
---|
| 185 | "printf: Not enough args for format.\n"
|
---|
| 186 | );
|
---|
| 187 | exit(EX_USAGE);
|
---|
| 188 | }
|
---|
| 189 |
|
---|
| 190 | (void) strncpy(conv_buf, conv_spec,
|
---|
| 191 | conv_len = cp - conv_spec + 1);
|
---|
| 192 | conv_buf[conv_len] = '\0';
|
---|
| 193 |
|
---|
| 194 | switch (dynamic_count) {
|
---|
| 195 | case 0:
|
---|
| 196 | printf(conv_buf, atoi(*argp++));
|
---|
| 197 | break;
|
---|
| 198 |
|
---|
| 199 | case 1:
|
---|
| 200 | {
|
---|
| 201 | register int a1;
|
---|
| 202 |
|
---|
| 203 | a1 = atoi(*argp++);
|
---|
| 204 | printf(conv_buf, a1, atoi(*argp++));
|
---|
| 205 | }
|
---|
| 206 | break;
|
---|
| 207 |
|
---|
| 208 | case 2:
|
---|
| 209 | {
|
---|
| 210 | register int a1, a2;
|
---|
| 211 |
|
---|
| 212 | a1 = atoi(*argp++);
|
---|
| 213 | a2 = atoi(*argp++);
|
---|
| 214 | printf(conv_buf, a1, a2, atoi(*argp++));
|
---|
| 215 | }
|
---|
| 216 | break;
|
---|
| 217 |
|
---|
| 218 | }
|
---|
| 219 | goto out;
|
---|
| 220 |
|
---|
| 221 | case 'f': /* Real */
|
---|
| 222 | case 'e':
|
---|
| 223 | case 'g':
|
---|
| 224 | if (&argp[dynamic_count] >= ep) {
|
---|
| 225 | fprintf(stderr,
|
---|
| 226 | "printf: Not enough args for format.\n"
|
---|
| 227 | );
|
---|
| 228 | exit(EX_USAGE);
|
---|
| 229 | }
|
---|
| 230 |
|
---|
| 231 | (void) strncpy(conv_buf, conv_spec,
|
---|
| 232 | conv_len = cp - conv_spec + 1);
|
---|
| 233 | conv_buf[conv_len] = '\0';
|
---|
| 234 |
|
---|
| 235 | switch (dynamic_count) {
|
---|
| 236 | case 0:
|
---|
| 237 | printf(conv_buf, atof(*argp++));
|
---|
| 238 | break;
|
---|
| 239 |
|
---|
| 240 | case 1:
|
---|
| 241 | {
|
---|
| 242 | register int a1;
|
---|
| 243 |
|
---|
| 244 | a1 = atoi(*argp++);
|
---|
| 245 | printf(conv_buf, a1, atof(*argp++));
|
---|
| 246 | }
|
---|
| 247 | break;
|
---|
| 248 |
|
---|
| 249 | case 2:
|
---|
| 250 | {
|
---|
| 251 | register int a1, a2;
|
---|
| 252 |
|
---|
| 253 | a1 = atoi(*argp++);
|
---|
| 254 | a2 = atoi(*argp++);
|
---|
| 255 | printf(conv_buf, a1, a2, atof(*argp++));
|
---|
| 256 | }
|
---|
| 257 | break;
|
---|
| 258 |
|
---|
| 259 | }
|
---|
| 260 | goto out;
|
---|
| 261 |
|
---|
| 262 | case 'r': /* Roman (Well, why not?) */
|
---|
| 263 | if (&argp[dynamic_count] >= ep) {
|
---|
| 264 | fprintf(stderr,
|
---|
| 265 | "printf: Not enough args for format.\n"
|
---|
| 266 | );
|
---|
| 267 | exit(EX_USAGE);
|
---|
| 268 | }
|
---|
| 269 |
|
---|
| 270 | (void) strncpy(conv_buf, conv_spec,
|
---|
| 271 | conv_len = cp - conv_spec + 1);
|
---|
| 272 | conv_buf[conv_len] = '\0';
|
---|
| 273 | conv_buf[conv_len - 1] = 's';
|
---|
| 274 |
|
---|
| 275 | switch (dynamic_count) {
|
---|
| 276 | case 0:
|
---|
| 277 | printf(conv_buf,
|
---|
| 278 | ctor(atoi(*argp++)));
|
---|
| 279 | break;
|
---|
| 280 |
|
---|
| 281 | case 1:
|
---|
| 282 | {
|
---|
| 283 | register int a1;
|
---|
| 284 |
|
---|
| 285 | a1 = atoi(*argp++);
|
---|
| 286 | printf(conv_buf, a1,
|
---|
| 287 | ctor(atoi(*argp++)));
|
---|
| 288 | }
|
---|
| 289 | break;
|
---|
| 290 |
|
---|
| 291 | case 2:
|
---|
| 292 | {
|
---|
| 293 | register int a1, a2;
|
---|
| 294 |
|
---|
| 295 | a1 = atoi(*argp++);
|
---|
| 296 | a2 = atoi(*argp++);
|
---|
| 297 | printf(conv_buf, a1, a2,
|
---|
| 298 | ctor(atoi(*argp++)));
|
---|
| 299 | }
|
---|
| 300 | break;
|
---|
| 301 |
|
---|
| 302 | }
|
---|
| 303 | goto out;
|
---|
| 304 |
|
---|
| 305 | case '%': /* Boring */
|
---|
| 306 | putchar('%');
|
---|
| 307 | break;
|
---|
| 308 |
|
---|
| 309 | default: /* Probably an error, but let user
|
---|
| 310 | have his way. */
|
---|
| 311 | continue;
|
---|
| 312 | }
|
---|
| 313 | }
|
---|
| 314 | out: ;
|
---|
| 315 | }
|
---|
| 316 |
|
---|
| 317 | exit(EX_OK);
|
---|
| 318 | }
|
---|
| 319 |
|
---|
| 320 | /****************************************************************************/
|
---|
| 321 |
|
---|
| 322 | /* Convert backslash notation to control characters, in place. */
|
---|
| 323 |
|
---|
| 324 | int ctrl(char *s)
|
---|
| 325 | {
|
---|
| 326 | register char *op;
|
---|
| 327 | static int val;
|
---|
| 328 |
|
---|
| 329 | for (op = s; *s; s++)
|
---|
| 330 | if (*s == '\\')
|
---|
| 331 | switch (*++s) {
|
---|
| 332 | case '\0': /* End-of-string: user goofed */
|
---|
| 333 | goto out;
|
---|
| 334 |
|
---|
| 335 | case '\\': /* Backslash */
|
---|
| 336 | *op++ = '\\';
|
---|
| 337 | break;
|
---|
| 338 |
|
---|
| 339 | case 'n': /* newline */
|
---|
| 340 | *op++ = '\n';
|
---|
| 341 | break;
|
---|
| 342 |
|
---|
| 343 | case 't': /* horizontal tab */
|
---|
| 344 | *op++ = '\t';
|
---|
| 345 | break;
|
---|
| 346 |
|
---|
| 347 | case 'r': /* carriage-return */
|
---|
| 348 | *op++ = '\r';
|
---|
| 349 | break;
|
---|
| 350 |
|
---|
| 351 | case 'f': /* form-feed */
|
---|
| 352 | *op++ = '\f';
|
---|
| 353 | break;
|
---|
| 354 |
|
---|
| 355 | case 'b': /* backspace */
|
---|
| 356 | *op++ = '\b';
|
---|
| 357 | break;
|
---|
| 358 |
|
---|
| 359 | case 'v': /* vertical tab */
|
---|
| 360 | *op++ = '\13';
|
---|
| 361 | break;
|
---|
| 362 |
|
---|
| 363 | case 'a': /* WARNING! DANGER! DANGER! DANGER! */
|
---|
| 364 | *op++ = '\7';
|
---|
| 365 | break;
|
---|
| 366 |
|
---|
| 367 | case '0': case '1': case '2': case '3':
|
---|
| 368 | case '4': case '5': case '6': case '7':
|
---|
| 369 | { /* octal constant */
|
---|
| 370 | register int digits;
|
---|
| 371 |
|
---|
| 372 | val = 0;
|
---|
| 373 | (void) sscanf(s, "%3o", &val);
|
---|
| 374 | *op++ = val;
|
---|
| 375 | for (digits = 3; s[1] &&
|
---|
| 376 | strchr("01234567", s[1])
|
---|
| 377 | && --digits > 0;
|
---|
| 378 | s++);
|
---|
| 379 | }
|
---|
| 380 | break;
|
---|
| 381 |
|
---|
| 382 | case 'x': /* hex constant */
|
---|
| 383 | case 'X':
|
---|
| 384 | s++;
|
---|
| 385 | {
|
---|
| 386 | register int digits;
|
---|
| 387 |
|
---|
| 388 | val = 0;
|
---|
| 389 | (void) sscanf(s, "%3x", &val);
|
---|
| 390 | *op++ = val;
|
---|
| 391 | for (digits = 3; *s && s[1] &&
|
---|
| 392 | strchr("0123456789abcdefABCDEF",
|
---|
| 393 | s[1])
|
---|
| 394 | && --digits > 0;
|
---|
| 395 | s++);
|
---|
| 396 | }
|
---|
| 397 | break;
|
---|
| 398 |
|
---|
| 399 | }
|
---|
| 400 | else
|
---|
| 401 | *op++ = *s;
|
---|
| 402 |
|
---|
| 403 | out:
|
---|
| 404 |
|
---|
| 405 | *op = '\0';
|
---|
| 406 | }
|
---|
| 407 |
|
---|
| 408 | /****************************************************************************/
|
---|
| 409 |
|
---|
| 410 | /* Convert integer to Roman Numerals. (Have have you survived without it?) */
|
---|
| 411 |
|
---|
| 412 | struct roman {
|
---|
| 413 | unsigned r_mag;
|
---|
| 414 | char r_units, r_fives;
|
---|
| 415 | } roman[] = {
|
---|
| 416 | { 1000, 'M', '\0', },
|
---|
| 417 | { 100, 'C', 'D', },
|
---|
| 418 | { 10, 'X', 'L', },
|
---|
| 419 | { 1, 'I', 'V', },
|
---|
| 420 | };
|
---|
| 421 |
|
---|
| 422 | char *ctor(int x)
|
---|
| 423 | {
|
---|
| 424 | register struct roman *mp;
|
---|
| 425 | static char buf[BUFSIZ];
|
---|
| 426 | register char *cp = buf;
|
---|
| 427 |
|
---|
| 428 | /* I've never actually seen a roman numeral with a minus-sign.
|
---|
| 429 | Probably ought to print out some appropriate latin phrase instead. */
|
---|
| 430 | if (x < 0) {
|
---|
| 431 | *cp++ = '-';
|
---|
| 432 | x = -x;
|
---|
| 433 | }
|
---|
| 434 |
|
---|
| 435 | for (mp = roman; x; mp++) {
|
---|
| 436 | register unsigned units;
|
---|
| 437 |
|
---|
| 438 | units = x / mp->r_mag;
|
---|
| 439 | x = x % mp->r_mag;
|
---|
| 440 |
|
---|
| 441 | if (cp > &buf[BUFSIZ-2])
|
---|
| 442 | return "???";
|
---|
| 443 |
|
---|
| 444 | if (units == 9 && mp > roman) { /* Do inverse notation: Eg: ``IX''. */
|
---|
| 445 | *cp++ = mp->r_units;
|
---|
| 446 | *cp++ = mp[-1].r_units;
|
---|
| 447 | }
|
---|
| 448 | else if (units == 4 && mp->r_fives) {
|
---|
| 449 | /* Inverse notation for half-decades: Eg: ``IV'' */
|
---|
| 450 | *cp++ = mp->r_units;
|
---|
| 451 | *cp++ = mp->r_fives;
|
---|
| 452 | }
|
---|
| 453 | else { /* Additive notation */
|
---|
| 454 | if (units >= 5 && mp->r_fives) {
|
---|
| 455 | *cp++ = mp->r_fives;
|
---|
| 456 | units -= 5;
|
---|
| 457 | }
|
---|
| 458 | while (units--) {
|
---|
| 459 | *cp++ = mp->r_units;
|
---|
| 460 | if (cp > &buf[BUFSIZ-5])
|
---|
| 461 | return "???";
|
---|
| 462 | }
|
---|
| 463 | }
|
---|
| 464 | }
|
---|
| 465 |
|
---|
| 466 | *cp = '\0';
|
---|
| 467 |
|
---|
| 468 | return buf;
|
---|
| 469 | }
|
---|
| 470 |
|
---|
| 471 | /****************************************************************************/
|
---|