blob: f572897595b5a65cb1fe7d9986795fce5904fa83 [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Eric Andersencc8ed391999-10-05 16:24:54 +00002/* printf - format and print data
3 Copyright (C) 90, 91, 92, 93, 94, 95, 1996 Free Software Foundation, Inc.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18
19/* Usage: printf format [argument...]
20
21 A front end to the printf function that lets it be used from the shell.
22
23 Backslash escapes:
24
25 \" = double quote
26 \\ = backslash
27 \a = alert (bell)
28 \b = backspace
29 \c = produce no further output
30 \f = form feed
31 \n = new line
32 \r = carriage return
33 \t = horizontal tab
34 \v = vertical tab
35 \0ooo = octal number (ooo is 0 to 3 digits)
36 \xhhh = hexadecimal number (hhh is 1 to 3 digits)
37
38 Additional directive:
39
40 %b = print an argument string, interpreting backslash escapes
41
42 The `format' argument is re-used as many times as necessary
43 to convert all of the given arguments.
44
45 David MacKenzie <djm@gnu.ai.mit.edu> */
Erik Andersene49d5ec2000-02-08 19:58:47 +000046
Eric Andersencc8ed391999-10-05 16:24:54 +000047
48// 19990508 Busy Boxed! Dave Cinege
49
Eric Andersenb0e9a701999-10-18 22:28:26 +000050#include "internal.h"
Eric Andersencc8ed391999-10-05 16:24:54 +000051#include <unistd.h>
52#include <stdio.h>
53#include <sys/types.h>
Eric Andersencc8ed391999-10-05 16:24:54 +000054#include <sys/stat.h>
55#include <string.h>
56#include <errno.h>
57#include <stdlib.h>
58#include <fcntl.h>
59#include <ctype.h>
Eric Andersencc8ed391999-10-05 16:24:54 +000060
61
62#ifndef S_IFMT
63# define S_IFMT 0170000
64#endif
65#if !defined(S_ISBLK) && defined(S_IFBLK)
66# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
67#endif
68#if !defined(S_ISCHR) && defined(S_IFCHR)
69# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
70#endif
71#if !defined(S_ISDIR) && defined(S_IFDIR)
72# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
73#endif
74#if !defined(S_ISREG) && defined(S_IFREG)
75# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
76#endif
77#if !defined(S_ISFIFO) && defined(S_IFIFO)
78# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
79#endif
80#if !defined(S_ISLNK) && defined(S_IFLNK)
81# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
82#endif
83#if !defined(S_ISSOCK) && defined(S_IFSOCK)
84# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
85#endif
Erik Andersene49d5ec2000-02-08 19:58:47 +000086#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */
Eric Andersencc8ed391999-10-05 16:24:54 +000087# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
88# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
89#endif
Erik Andersene49d5ec2000-02-08 19:58:47 +000090#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */
Eric Andersencc8ed391999-10-05 16:24:54 +000091# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
92#endif
93
94#define IN_CTYPE_DOMAIN(c) 1
95
96#ifdef isblank
97# define ISBLANK(c) (IN_CTYPE_DOMAIN (c) && isblank (c))
98#else
99# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
100#endif
101#ifdef isgraph
102# define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isgraph (c))
103#else
104# define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isprint (c) && !isspace (c))
105#endif
106
107#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c))
108#define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum (c))
109#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
110#define ISCNTRL(c) (IN_CTYPE_DOMAIN (c) && iscntrl (c))
111#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
112#define ISPUNCT(c) (IN_CTYPE_DOMAIN (c) && ispunct (c))
113#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
114#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
115#define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit (c))
116#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
117#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
118
119#define isodigit(c) ((c) >= '0' && (c) <= '7')
120#define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0')
121#define octtobin(c) ((c) - '0')
122
Erik Andersene49d5ec2000-02-08 19:58:47 +0000123static double xstrtod __P((char *s));
124static int print_esc __P((char *escstart));
125static int print_formatted __P((char *format, int argc, char **argv));
126static long xstrtol __P((char *s));
127static unsigned long xstrtoul __P((char *s));
128static void print_direc
129__P(
130
131 (char *start, size_t length, int field_width, int precision,
132 char *argument));
133static void print_esc_char __P((int c));
134static void print_esc_string __P((char *str));
135static void verify __P((char *s, char *end));
Eric Andersencc8ed391999-10-05 16:24:54 +0000136
137/* The value to return to the calling program. */
138static int exit_status;
139
Erik Andersen7ab9c7e2000-05-12 19:41:47 +0000140static const char printf_usage[] = "printf FORMAT [ARGUMENT...]\n"
141#ifndef BB_FEATURE_TRIVIAL_HELP
142 "\nFormats and prints ARGUMENT(s) according to FORMAT,\n"
143 "Where FORMAT controls the output exactly as in C printf.\n"
144#endif
145 ;
Eric Andersencc8ed391999-10-05 16:24:54 +0000146
Erik Andersene49d5ec2000-02-08 19:58:47 +0000147int printf_main(int argc, char **argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000148{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000149 char *format;
150 int args_used;
Eric Andersencc8ed391999-10-05 16:24:54 +0000151
Erik Andersene49d5ec2000-02-08 19:58:47 +0000152 exit_status = 0;
153 if (argc <= 1 || **(argv + 1) == '-') {
154 usage(printf_usage);
155 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000156
Erik Andersene49d5ec2000-02-08 19:58:47 +0000157 format = argv[1];
158 argc -= 2;
159 argv += 2;
Eric Andersencc8ed391999-10-05 16:24:54 +0000160
Erik Andersene49d5ec2000-02-08 19:58:47 +0000161 do {
162 args_used = print_formatted(format, argc, argv);
163 argc -= args_used;
164 argv += args_used;
165 }
166 while (args_used > 0 && argc > 0);
Eric Andersencc8ed391999-10-05 16:24:54 +0000167
168/*
169 if (argc > 0)
170 fprintf(stderr, "excess args ignored");
171*/
172
Eric Andersenb7362e32000-06-19 17:52:17 +0000173 return(exit_status);
Eric Andersencc8ed391999-10-05 16:24:54 +0000174}
175
176/* Print the text in FORMAT, using ARGV (with ARGC elements) for
177 arguments to any `%' directives.
178 Return the number of elements of ARGV used. */
179
Erik Andersene49d5ec2000-02-08 19:58:47 +0000180static int print_formatted(char *format, int argc, char **argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000181{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000182 int save_argc = argc; /* Preserve original value. */
183 char *f; /* Pointer into `format'. */
184 char *direc_start; /* Start of % directive. */
185 size_t direc_length; /* Length of % directive. */
186 int field_width; /* Arg to first '*', or -1 if none. */
187 int precision; /* Arg to second '*', or -1 if none. */
Eric Andersencc8ed391999-10-05 16:24:54 +0000188
Erik Andersene49d5ec2000-02-08 19:58:47 +0000189 for (f = format; *f; ++f) {
190 switch (*f) {
191 case '%':
192 direc_start = f++;
193 direc_length = 1;
194 field_width = precision = -1;
195 if (*f == '%') {
196 putchar('%');
197 break;
198 }
199 if (*f == 'b') {
200 if (argc > 0) {
201 print_esc_string(*argv);
202 ++argv;
203 --argc;
204 }
205 break;
206 }
207 if (strchr("-+ #", *f)) {
208 ++f;
209 ++direc_length;
210 }
211 if (*f == '*') {
212 ++f;
213 ++direc_length;
214 if (argc > 0) {
215 field_width = xstrtoul(*argv);
216 ++argv;
217 --argc;
218 } else
219 field_width = 0;
220 } else
221 while (ISDIGIT(*f)) {
222 ++f;
223 ++direc_length;
224 }
225 if (*f == '.') {
226 ++f;
227 ++direc_length;
228 if (*f == '*') {
229 ++f;
230 ++direc_length;
231 if (argc > 0) {
232 precision = xstrtoul(*argv);
233 ++argv;
234 --argc;
235 } else
236 precision = 0;
237 } else
238 while (ISDIGIT(*f)) {
239 ++f;
240 ++direc_length;
241 }
242 }
243 if (*f == 'l' || *f == 'L' || *f == 'h') {
244 ++f;
245 ++direc_length;
246 }
247 /*
248 if (!strchr ("diouxXfeEgGcs", *f))
249 fprintf(stderr, "%%%c: invalid directive", *f);
250 */
251 ++direc_length;
252 if (argc > 0) {
253 print_direc(direc_start, direc_length, field_width,
254 precision, *argv);
255 ++argv;
256 --argc;
257 } else
258 print_direc(direc_start, direc_length, field_width,
259 precision, "");
260 break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000261
Erik Andersene49d5ec2000-02-08 19:58:47 +0000262 case '\\':
263 f += print_esc(f);
264 break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000265
Erik Andersene49d5ec2000-02-08 19:58:47 +0000266 default:
267 putchar(*f);
268 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000269 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000270
Erik Andersene49d5ec2000-02-08 19:58:47 +0000271 return save_argc - argc;
Eric Andersencc8ed391999-10-05 16:24:54 +0000272}
273
274/* Print a \ escape sequence starting at ESCSTART.
275 Return the number of characters in the escape sequence
276 besides the backslash. */
277
Erik Andersene49d5ec2000-02-08 19:58:47 +0000278static int print_esc(char *escstart)
Eric Andersencc8ed391999-10-05 16:24:54 +0000279{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000280 register char *p = escstart + 1;
281 int esc_value = 0; /* Value of \nnn escape. */
282 int esc_length; /* Length of \nnn escape. */
Eric Andersencc8ed391999-10-05 16:24:54 +0000283
Erik Andersene49d5ec2000-02-08 19:58:47 +0000284 /* \0ooo and \xhhh escapes have maximum length of 3 chars. */
285 if (*p == 'x') {
286 for (esc_length = 0, ++p;
287 esc_length < 3 && ISXDIGIT(*p); ++esc_length, ++p)
288 esc_value = esc_value * 16 + hextobin(*p);
Eric Andersencc8ed391999-10-05 16:24:54 +0000289/* if (esc_length == 0)
290 fprintf(stderr, "missing hex in esc");
291*/
Erik Andersene49d5ec2000-02-08 19:58:47 +0000292 putchar(esc_value);
293 } else if (*p == '0') {
294 for (esc_length = 0, ++p;
295 esc_length < 3 && isodigit(*p); ++esc_length, ++p)
296 esc_value = esc_value * 8 + octtobin(*p);
297 putchar(esc_value);
298 } else if (strchr("\"\\abcfnrtv", *p))
299 print_esc_char(*p++);
Eric Andersencc8ed391999-10-05 16:24:54 +0000300/* else
301 fprintf(stderr, "\\%c: invalid esc", *p);
302*/
Erik Andersene49d5ec2000-02-08 19:58:47 +0000303 return p - escstart - 1;
Eric Andersencc8ed391999-10-05 16:24:54 +0000304}
305
306/* Output a single-character \ escape. */
307
Erik Andersene49d5ec2000-02-08 19:58:47 +0000308static void print_esc_char(int c)
Eric Andersencc8ed391999-10-05 16:24:54 +0000309{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000310 switch (c) {
311 case 'a': /* Alert. */
312 putchar(7);
313 break;
314 case 'b': /* Backspace. */
315 putchar(8);
316 break;
317 case 'c': /* Cancel the rest of the output. */
318 exit(0);
319 break;
320 case 'f': /* Form feed. */
321 putchar(12);
322 break;
323 case 'n': /* New line. */
324 putchar(10);
325 break;
326 case 'r': /* Carriage return. */
327 putchar(13);
328 break;
329 case 't': /* Horizontal tab. */
330 putchar(9);
331 break;
332 case 'v': /* Vertical tab. */
333 putchar(11);
334 break;
335 default:
336 putchar(c);
337 break;
338 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000339}
340
341/* Print string STR, evaluating \ escapes. */
342
Erik Andersene49d5ec2000-02-08 19:58:47 +0000343static void print_esc_string(char *str)
Eric Andersencc8ed391999-10-05 16:24:54 +0000344{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000345 for (; *str; str++)
346 if (*str == '\\')
347 str += print_esc(str);
348 else
349 putchar(*str);
Eric Andersencc8ed391999-10-05 16:24:54 +0000350}
351
352static void
Erik Andersene49d5ec2000-02-08 19:58:47 +0000353print_direc(char *start, size_t length, int field_width, int precision,
354 char *argument)
Eric Andersencc8ed391999-10-05 16:24:54 +0000355{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000356 char *p; /* Null-terminated copy of % directive. */
Eric Andersencc8ed391999-10-05 16:24:54 +0000357
Erik Andersene49d5ec2000-02-08 19:58:47 +0000358 p = xmalloc((unsigned) (length + 1));
359 strncpy(p, start, length);
360 p[length] = 0;
Eric Andersencc8ed391999-10-05 16:24:54 +0000361
Erik Andersene49d5ec2000-02-08 19:58:47 +0000362 switch (p[length - 1]) {
363 case 'd':
364 case 'i':
365 if (field_width < 0) {
366 if (precision < 0)
367 printf(p, xstrtol(argument));
368 else
369 printf(p, precision, xstrtol(argument));
370 } else {
371 if (precision < 0)
372 printf(p, field_width, xstrtol(argument));
373 else
374 printf(p, field_width, precision, xstrtol(argument));
375 }
376 break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000377
Erik Andersene49d5ec2000-02-08 19:58:47 +0000378 case 'o':
379 case 'u':
380 case 'x':
381 case 'X':
382 if (field_width < 0) {
383 if (precision < 0)
384 printf(p, xstrtoul(argument));
385 else
386 printf(p, precision, xstrtoul(argument));
387 } else {
388 if (precision < 0)
389 printf(p, field_width, xstrtoul(argument));
390 else
391 printf(p, field_width, precision, xstrtoul(argument));
392 }
393 break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000394
Erik Andersene49d5ec2000-02-08 19:58:47 +0000395 case 'f':
396 case 'e':
397 case 'E':
398 case 'g':
399 case 'G':
400 if (field_width < 0) {
401 if (precision < 0)
402 printf(p, xstrtod(argument));
403 else
404 printf(p, precision, xstrtod(argument));
405 } else {
406 if (precision < 0)
407 printf(p, field_width, xstrtod(argument));
408 else
409 printf(p, field_width, precision, xstrtod(argument));
410 }
411 break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000412
Erik Andersene49d5ec2000-02-08 19:58:47 +0000413 case 'c':
414 printf(p, *argument);
415 break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000416
Erik Andersene49d5ec2000-02-08 19:58:47 +0000417 case 's':
418 if (field_width < 0) {
419 if (precision < 0)
420 printf(p, argument);
421 else
422 printf(p, precision, argument);
423 } else {
424 if (precision < 0)
425 printf(p, field_width, argument);
426 else
427 printf(p, field_width, precision, argument);
428 }
429 break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000430 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000431
Erik Andersene49d5ec2000-02-08 19:58:47 +0000432 free(p);
Eric Andersencc8ed391999-10-05 16:24:54 +0000433}
434
Erik Andersene49d5ec2000-02-08 19:58:47 +0000435static unsigned long xstrtoul(char *s)
Eric Andersencc8ed391999-10-05 16:24:54 +0000436{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000437 char *end;
438 unsigned long val;
Eric Andersencc8ed391999-10-05 16:24:54 +0000439
Erik Andersene49d5ec2000-02-08 19:58:47 +0000440 errno = 0;
441 val = strtoul(s, &end, 0);
442 verify(s, end);
443 return val;
Eric Andersencc8ed391999-10-05 16:24:54 +0000444}
445
Erik Andersene49d5ec2000-02-08 19:58:47 +0000446static long xstrtol(char *s)
Eric Andersencc8ed391999-10-05 16:24:54 +0000447{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000448 char *end;
449 long val;
Eric Andersencc8ed391999-10-05 16:24:54 +0000450
Erik Andersene49d5ec2000-02-08 19:58:47 +0000451 errno = 0;
452 val = strtol(s, &end, 0);
453 verify(s, end);
454 return val;
Eric Andersencc8ed391999-10-05 16:24:54 +0000455}
456
Erik Andersene49d5ec2000-02-08 19:58:47 +0000457static double xstrtod(char *s)
Eric Andersencc8ed391999-10-05 16:24:54 +0000458{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000459 char *end;
460 double val;
Eric Andersencc8ed391999-10-05 16:24:54 +0000461
Erik Andersene49d5ec2000-02-08 19:58:47 +0000462 errno = 0;
463 val = strtod(s, &end);
464 verify(s, end);
465 return val;
Eric Andersencc8ed391999-10-05 16:24:54 +0000466}
467
Erik Andersene49d5ec2000-02-08 19:58:47 +0000468static void verify(char *s, char *end)
Eric Andersencc8ed391999-10-05 16:24:54 +0000469{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000470 if (errno) {
471 fprintf(stderr, "%s", s);
472 exit_status = 1;
473 } else if (*end) {
474 /*
475 if (s == end)
476 fprintf(stderr, "%s: expected numeric", s);
477 else
478 fprintf(stderr, "%s: not completely converted", s);
479 */
480 exit_status = 1;
481 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000482}