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 | /****************************************************************************/
|
---|