2 static char sccsid
[] = "@(#)printf.c (U of Maryland) FLB 6-Jan-1987";
3 static char RCSid
[] = "@(#)$Header$";
7 * Printf - Duplicate the C library routine of the same name, but from
8 * the shell command level.
10 * Fred Blonder <fred@Mimsy.umd.edu>
13 % cc -s -O printf.c -o printf
16 * Revision 1.1 2005/04/21 14:55:31 beng
19 * Revision 1.1.1.1 2005/04/20 13:33:30 beng
20 * Initial import of minix 2.0.4
22 * Revision 1.4 87/01/29 20:52:30 fred
23 * Re-installed backslash-notation conversion for string & char arguments.
25 * Revision 1.3 87/01/29 20:44:23 fred
26 * Converted to portable algorithm.
27 * Added Roman format for integers.
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)
45 #define atoi(a) strtoul((a), NULL, 0)
47 /****************************************************************************/
49 int main(int argc
, char *argv
[])
51 register char *cp
, *conv_spec
, **argp
, **ep
;
56 "printf: Usage: printf <format-string> [ arg1 . . . ]\n");
60 argp
= &argv
[2]; /* Point at first arg (if any) beyond format string. */
61 ep
= &argv
[argc
]; /* Point beyond last arg. */
63 ctrl(argv
[1]); /* Change backslash notation to control chars in fmt string. */
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
;
70 /* Look for next conversion spec. */
71 while (*cp
&& *cp
!= '%') {
75 if (!*cp
) /* End of format string */
78 dynamic_count
= 0; /* Begin counting dynamic field width specs. */
79 conv_spec
= cp
++; /* Remember where this conversion begins. */
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''. */
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':
91 case '*': /* Dynamic field-width spec */
95 case 's': /* String */
96 if (&argp
[dynamic_count
] >= ep
) {
98 "printf: Not enough args for format.\n"
103 (void) strncpy(conv_buf
, conv_spec
,
104 conv_len
= cp
- conv_spec
+ 1);
105 conv_buf
[conv_len
] = '\0';
107 switch (dynamic_count
) {
110 printf(conv_buf
, *argp
++);
119 printf(conv_buf
, a1
, *argp
++);
130 printf(conv_buf
, a1
, a2
, *argp
++);
138 if (&argp
[dynamic_count
] >= ep
) {
140 "printf: Not enough args for format.\n"
145 (void) strncpy(conv_buf
, conv_spec
,
146 conv_len
= cp
- conv_spec
+ 1);
147 conv_buf
[conv_len
] = '\0';
149 switch (dynamic_count
) {
152 printf(conv_buf
, **argp
++);
161 printf(conv_buf
, a1
, **argp
++);
172 printf(conv_buf
, a1
, a2
, **argp
++);
178 case 'd': /* Integer */
183 if (&argp
[dynamic_count
] >= ep
) {
185 "printf: Not enough args for format.\n"
190 (void) strncpy(conv_buf
, conv_spec
,
191 conv_len
= cp
- conv_spec
+ 1);
192 conv_buf
[conv_len
] = '\0';
194 switch (dynamic_count
) {
196 printf(conv_buf
, atoi(*argp
++));
204 printf(conv_buf
, a1
, atoi(*argp
++));
214 printf(conv_buf
, a1
, a2
, atoi(*argp
++));
224 if (&argp
[dynamic_count
] >= ep
) {
226 "printf: Not enough args for format.\n"
231 (void) strncpy(conv_buf
, conv_spec
,
232 conv_len
= cp
- conv_spec
+ 1);
233 conv_buf
[conv_len
] = '\0';
235 switch (dynamic_count
) {
237 printf(conv_buf
, atof(*argp
++));
245 printf(conv_buf
, a1
, atof(*argp
++));
255 printf(conv_buf
, a1
, a2
, atof(*argp
++));
262 case 'r': /* Roman (Well, why not?) */
263 if (&argp
[dynamic_count
] >= ep
) {
265 "printf: Not enough args for format.\n"
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';
275 switch (dynamic_count
) {
278 ctor(atoi(*argp
++)));
287 ctor(atoi(*argp
++)));
297 printf(conv_buf
, a1
, a2
,
298 ctor(atoi(*argp
++)));
305 case '%': /* Boring */
309 default: /* Probably an error, but let user
320 /****************************************************************************/
322 /* Convert backslash notation to control characters, in place. */
329 for (op
= s
; *s
; s
++)
332 case '\0': /* End-of-string: user goofed */
335 case '\\': /* Backslash */
339 case 'n': /* newline */
343 case 't': /* horizontal tab */
347 case 'r': /* carriage-return */
351 case 'f': /* form-feed */
355 case 'b': /* backspace */
359 case 'v': /* vertical tab */
363 case 'a': /* WARNING! DANGER! DANGER! DANGER! */
367 case '0': case '1': case '2': case '3':
368 case '4': case '5': case '6': case '7':
369 { /* octal constant */
373 (void) sscanf(s
, "%3o", &val
);
375 for (digits
= 3; s
[1] &&
376 strchr("01234567", s
[1])
382 case 'x': /* hex constant */
389 (void) sscanf(s
, "%3x", &val
);
391 for (digits
= 3; *s
&& s
[1] &&
392 strchr("0123456789abcdefABCDEF",
408 /****************************************************************************/
410 /* Convert integer to Roman Numerals. (Have have you survived without it?) */
414 char r_units
, r_fives
;
416 { 1000, 'M', '\0', },
424 register struct roman
*mp
;
425 static char buf
[BUFSIZ
];
426 register char *cp
= buf
;
428 /* I've never actually seen a roman numeral with a minus-sign.
429 Probably ought to print out some appropriate latin phrase instead. */
435 for (mp
= roman
; x
; mp
++) {
436 register unsigned units
;
438 units
= x
/ mp
->r_mag
;
441 if (cp
> &buf
[BUFSIZ
-2])
444 if (units
== 9 && mp
> roman
) { /* Do inverse notation: Eg: ``IX''. */
446 *cp
++ = mp
[-1].r_units
;
448 else if (units
== 4 && mp
->r_fives
) {
449 /* Inverse notation for half-decades: Eg: ``IV'' */
453 else { /* Additive notation */
454 if (units
>= 5 && mp
->r_fives
) {
460 if (cp
> &buf
[BUFSIZ
-5])
471 /****************************************************************************/