source: trunk/minix/commands/simple/printf.c@ 15

Last change on this file since 15 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 9.1 KB
Line 
1#if ever
2static char sccsid[] = "@(#)printf.c (U of Maryland) FLB 6-Jan-1987";
3static 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
43int ctrl(char *s);
44
45#define atoi(a) strtoul((a), NULL, 0)
46
47/****************************************************************************/
48
49int main(int argc, char *argv[])
50{
51register char *cp, *conv_spec, **argp, **ep;
52char *ctor(int x);
53
54if (argc < 2) {
55 fprintf(stderr,
56 "printf: Usage: printf <format-string> [ arg1 . . . ]\n");
57 exit(EX_USAGE);
58 }
59
60argp = &argv[2]; /* Point at first arg (if any) beyond format string. */
61ep = &argv[argc]; /* Point beyond last arg. */
62
63ctrl(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. */
67for (cp = argv[1]; *cp; cp++) {
68register 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
317exit(EX_OK);
318}
319
320/****************************************************************************/
321
322/* Convert backslash notation to control characters, in place. */
323
324int ctrl(char *s)
325{
326register char *op;
327static int val;
328
329for (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
403out:
404
405*op = '\0';
406}
407
408/****************************************************************************/
409
410/* Convert integer to Roman Numerals. (Have have you survived without it?) */
411
412struct 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
422char *ctor(int x)
423{
424register struct roman *mp;
425static char buf[BUFSIZ];
426register 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. */
430if (x < 0) {
431 *cp++ = '-';
432 x = -x;
433 }
434
435for (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
468return buf;
469}
470
471/****************************************************************************/
Note: See TracBrowser for help on using the repository browser.