]> git.saurik.com Git - apple/shell_cmds.git/blame - printf/printf.c
shell_cmds-17.1.tar.gz
[apple/shell_cmds.git] / printf / printf.c
CommitLineData
44bd5ea7
A
1/* $NetBSD: printf.c,v 1.19 1998/02/03 03:10:15 perry Exp $ */
2
3/*
4 * Copyright (c) 1989, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include <sys/cdefs.h>
37#ifndef lint
38#if !defined(BUILTIN) && !defined(SHELL)
39__COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
40 The Regents of the University of California. All rights reserved.\n");
41#endif
42#endif
43
44#ifndef lint
45#if 0
46static char sccsid[] = "@(#)printf.c 8.2 (Berkeley) 3/22/95";
47#else
48__RCSID("$NetBSD: printf.c,v 1.19 1998/02/03 03:10:15 perry Exp $");
49#endif
50#endif /* not lint */
51
52#include <sys/types.h>
53
54#include <ctype.h>
55#include <err.h>
56#include <errno.h>
57#include <limits.h>
58#include <locale.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <unistd.h>
62#include <string.h>
63#include <unistd.h>
64
65static int print_escape_str __P((const char *));
66static size_t print_escape __P((const char *));
67
68static int getchr __P((void));
69static double getdouble __P((void));
70static int getint __P((void));
71static long getlong __P((void));
72static unsigned long getulong __P ((void));
73static char *getstr __P((void));
74static char *mklong __P((const char *, int));
75static void check_conversion __P((const char *, const char *));
76static void usage __P((void));
77
78static int rval;
79static char **gargv;
80
81#ifdef BUILTIN
82int progprintf __P((int, char **));
83#else
84int main __P((int, char **));
85#endif
86
87#define isodigit(c) ((c) >= '0' && (c) <= '7')
88#define octtobin(c) ((c) - '0')
89#define hextobin(c) ((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0')
90
91#ifdef SHELL
92#define main printfcmd
93#ifdef __APPLE__
94#include "bltin/bltin.h"
95#else
96#include "../../bin/sh/bltin/bltin.h"
97#endif
98
99#ifdef __STDC__
100#include <stdarg.h>
101#else
102#include <vararg.h>
103#endif
104
105static void warnx __P((const char *fmt, ...));
106
107static void
108#ifdef __STDC__
109warnx(const char *fmt, ...)
110#else
111warnx(fmt, va_alist)
112 const char *fmt;
113 va_dcl
114#endif
115{
116
117 char buf[64];
118 va_list ap;
119
120#ifdef __STDC__
121 va_start(ap, fmt);
122#else
123 va_start(ap);
124#endif
125 vsprintf(buf, fmt, ap);
126 va_end(ap);
127
128 error(buf);
129}
130#endif /* SHELL */
131
132#define PF(f, func) { \
133 if (fieldwidth) \
134 if (precision) \
135 (void)printf(f, fieldwidth, precision, func); \
136 else \
137 (void)printf(f, fieldwidth, func); \
138 else if (precision) \
139 (void)printf(f, precision, func); \
140 else \
141 (void)printf(f, func); \
142}
143
144int
145#ifdef BUILTIN
146progprintf(argc, argv)
147#else
148main(argc, argv)
149#endif
150 int argc;
151 char *argv[];
152{
153 char *fmt, *start;
154 int fieldwidth, precision;
155 char convch, nextch;
156 char *format;
157 int ch;
158
159#if !defined(SHELL) && !defined(BUILTIN)
160 (void)setlocale (LC_ALL, "");
161#endif
162
163 while ((ch = getopt(argc, argv, "")) != -1) {
164 switch (ch) {
165 case '?':
166 default:
167 usage();
168 return (1);
169 }
170 }
171 argc -= optind;
172 argv += optind;
173
174 if (argc < 1) {
175 usage();
176 return (1);
177 }
178
179 format = *argv;
180 gargv = ++argv;
181
182#define SKIP1 "#-+ 0"
183#define SKIP2 "*0123456789"
184 do {
185 /*
186 * Basic algorithm is to scan the format string for conversion
187 * specifications -- once one is found, find out if the field
188 * width or precision is a '*'; if it is, gather up value.
189 * Note, format strings are reused as necessary to use up the
190 * provided arguments, arguments of zero/null string are
191 * provided to use up the format string.
192 */
193
194 /* find next format specification */
195 for (fmt = format; *fmt; fmt++) {
196 switch (*fmt) {
197 case '%':
198 start = fmt++;
199
200 if (*fmt == '%') {
201 (void)putchar('%');
202 break;
203 } else if (*fmt == 'b') {
204 char *p = getstr();
205 if (print_escape_str(p)) {
206 return (rval);
207 }
208 break;
209 }
210
211 /* skip to field width */
212 for (; strchr(SKIP1, *fmt); ++fmt) ;
213 fieldwidth = *fmt == '*' ? getint() : 0;
214
215 /* skip to possible '.', get following precision */
216 for (; strchr(SKIP2, *fmt); ++fmt) ;
217 if (*fmt == '.')
218 ++fmt;
219 precision = *fmt == '*' ? getint() : 0;
220
221 for (; strchr(SKIP2, *fmt); ++fmt) ;
222 if (!*fmt) {
223 warnx ("missing format character");
224 return(1);
225 }
226
227 convch = *fmt;
228 nextch = *(fmt + 1);
229 *(fmt + 1) = '\0';
230 switch(convch) {
231 case 'c': {
232 char p = getchr();
233 PF(start, p);
234 break;
235 }
236 case 's': {
237 char *p = getstr();
238 PF(start, p);
239 break;
240 }
241 case 'd':
242 case 'i': {
243 char *f = mklong(start, convch);
244 long p = getlong();
245 PF(f, p);
246 break;
247 }
248 case 'o':
249 case 'u':
250 case 'x':
251 case 'X': {
252 char *f = mklong(start, convch);
253 unsigned long p = getulong();
254 PF(f, p);
255 break;
256 }
257 case 'e':
258 case 'E':
259 case 'f':
260 case 'g':
261 case 'G': {
262 double p = getdouble();
263 PF(start, p);
264 break;
265 }
266 default:
267 warnx ("%s: invalid directive", start);
268 return(1);
269 }
270 *(fmt + 1) = nextch;
271 break;
272
273 case '\\':
274 fmt += print_escape(fmt);
275 break;
276
277 default:
278 (void)putchar(*fmt);
279 break;
280 }
281 }
282 } while (gargv > argv && *gargv);
283
284 return (rval);
285}
286
287
288/*
289 * Print SysV echo(1) style escape string
290 * Halts processing string and returns 1 if a \c escape is encountered.
291 */
292static int
293print_escape_str(str)
294 const char *str;
295{
296 int value;
297 int c;
298
299 while (*str) {
300 if (*str == '\\') {
301 str++;
302 /*
303 * %b string octal constants are not like those in C.
304 * They start with a \0, and are followed by 0, 1, 2,
305 * or 3 octal digits.
306 */
307 if (*str == '0') {
308 str++;
309 for (c = 3, value = 0; c-- && isodigit(*str); str++) {
310 value <<= 3;
311 value += octtobin(*str);
312 }
313 (void)putchar(value);
314 str--;
315 } else if (*str == 'c') {
316 return 1;
317 } else {
318 str--;
319 str += print_escape(str);
320 }
321 } else {
322 (void)putchar(*str);
323 }
324 str++;
325 }
326
327 return 0;
328}
329
330/*
331 * Print "standard" escape characters
332 */
333static size_t
334print_escape(str)
335 const char *str;
336{
337 const char *start = str;
338 int value;
339 int c;
340
341 str++;
342
343 switch (*str) {
344 case '0': case '1': case '2': case '3':
345 case '4': case '5': case '6': case '7':
346 for (c = 3, value = 0; c-- && isodigit(*str); str++) {
347 value <<= 3;
348 value += octtobin(*str);
349 }
350 (void)putchar(value);
351 return str - start - 1;
352 /* NOTREACHED */
353
354 case 'x':
355 str++;
356 for (value = 0; isxdigit(*str); str++) {
357 value <<= 4;
358 value += hextobin(*str);
359 }
360 if (value > UCHAR_MAX) {
361 warnx ("escape sequence out of range for character");
362 rval = 1;
363 }
364 (void)putchar (value);
365 return str - start - 1;
366 /* NOTREACHED */
367
368 case '\\': /* backslash */
369 (void)putchar('\\');
370 break;
371
372 case '\'': /* single quote */
373 (void)putchar('\'');
374 break;
375
376 case '"': /* double quote */
377 (void)putchar('"');
378 break;
379
380 case 'a': /* alert */
381#ifdef __STDC__
382 (void)putchar('\a');
383#else
384 (void)putchar(007);
385#endif
386 break;
387
388 case 'b': /* backspace */
389 (void)putchar('\b');
390 break;
391
392 case 'e': /* escape */
393#ifdef __GNUC__
394 (void)putchar('\e');
395#else
396 (void)putchar(033);
397#endif
398 break;
399
400 case 'f': /* form-feed */
401 (void)putchar('\f');
402 break;
403
404 case 'n': /* newline */
405 (void)putchar('\n');
406 break;
407
408 case 'r': /* carriage-return */
409 (void)putchar('\r');
410 break;
411
412 case 't': /* tab */
413 (void)putchar('\t');
414 break;
415
416 case 'v': /* vertical-tab */
417 (void)putchar('\v');
418 break;
419
420 default:
421 (void)putchar(*str);
422 warnx("unknown escape sequence `\\%c'", *str);
423 rval = 1;
424 break;
425 }
426
427 return 1;
428}
429
430static char *
431mklong(str, ch)
432 const char *str;
433 char ch;
434{
435 static char copy[64];
436 size_t len;
437
438 len = strlen(str) + 2;
439 (void)memmove(copy, str, len - 3);
440 copy[len - 3] = 'l';
441 copy[len - 2] = ch;
442 copy[len - 1] = '\0';
443 return (copy);
444}
445
446static int
447getchr()
448{
449 if (!*gargv)
450 return ('\0');
451 return ((int)**gargv++);
452}
453
454static char *
455getstr()
456{
457 if (!*gargv)
458 return ("");
459 return (*gargv++);
460}
461
462static char *Number = "+-.0123456789";
463static int
464getint()
465{
466 if (!*gargv)
467 return(0);
468
469 if (strchr(Number, **gargv))
470 return(atoi(*gargv++));
471
472 return 0;
473}
474
475static long
476getlong()
477{
478 long val;
479 char *ep;
480
481 if (!*gargv)
482 return(0L);
483
484 if (**gargv == '\"' || **gargv == '\'')
485 return (long) *((*gargv++)+1);
486
487 errno = 0;
488 val = strtol (*gargv, &ep, 0);
489 check_conversion(*gargv++, ep);
490 return val;
491}
492
493static unsigned long
494getulong()
495{
496 unsigned long val;
497 char *ep;
498
499 if (!*gargv)
500 return(0UL);
501
502 if (**gargv == '\"' || **gargv == '\'')
503 return (unsigned long) *((*gargv++)+1);
504
505 errno = 0;
506 val = strtoul (*gargv, &ep, 0);
507 check_conversion(*gargv++, ep);
508 return val;
509}
510
511static double
512getdouble()
513{
514 double val;
515 char *ep;
516
517 if (!*gargv)
518 return(0.0);
519
520 if (**gargv == '\"' || **gargv == '\'')
521 return (double) *((*gargv++)+1);
522
523 errno = 0;
524 val = strtod (*gargv, &ep);
525 check_conversion(*gargv++, ep);
526 return val;
527}
528
529static void
530check_conversion(s, ep)
531 const char *s;
532 const char *ep;
533{
534 if (*ep) {
535 if (ep == s)
536 warnx ("%s: expected numeric value", s);
537 else
538 warnx ("%s: not completely converted", s);
539 rval = 1;
540 } else if (errno == ERANGE) {
541 warnx ("%s: %s", s, strerror(ERANGE));
542 rval = 1;
543 }
544}
545
546static void
547usage()
548{
549 (void)fprintf(stderr, "usage: printf format [arg ...]\n");
550}