blob: 41ab2e4426e6c4ad88f157843ecc6ea138ed6465 [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>
54#include <getopt.h>
55#include <sys/stat.h>
56#include <string.h>
57#include <errno.h>
58#include <stdlib.h>
59#include <fcntl.h>
60#include <ctype.h>
61#include <libintl.h>
62
63
64#ifndef S_IFMT
65# define S_IFMT 0170000
66#endif
67#if !defined(S_ISBLK) && defined(S_IFBLK)
68# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
69#endif
70#if !defined(S_ISCHR) && defined(S_IFCHR)
71# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
72#endif
73#if !defined(S_ISDIR) && defined(S_IFDIR)
74# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
75#endif
76#if !defined(S_ISREG) && defined(S_IFREG)
77# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
78#endif
79#if !defined(S_ISFIFO) && defined(S_IFIFO)
80# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
81#endif
82#if !defined(S_ISLNK) && defined(S_IFLNK)
83# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
84#endif
85#if !defined(S_ISSOCK) && defined(S_IFSOCK)
86# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
87#endif
Erik Andersene49d5ec2000-02-08 19:58:47 +000088#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */
Eric Andersencc8ed391999-10-05 16:24:54 +000089# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
90# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
91#endif
Erik Andersene49d5ec2000-02-08 19:58:47 +000092#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */
Eric Andersencc8ed391999-10-05 16:24:54 +000093# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
94#endif
95
96#define IN_CTYPE_DOMAIN(c) 1
97
98#ifdef isblank
99# define ISBLANK(c) (IN_CTYPE_DOMAIN (c) && isblank (c))
100#else
101# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
102#endif
103#ifdef isgraph
104# define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isgraph (c))
105#else
106# define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isprint (c) && !isspace (c))
107#endif
108
109#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c))
110#define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum (c))
111#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
112#define ISCNTRL(c) (IN_CTYPE_DOMAIN (c) && iscntrl (c))
113#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
114#define ISPUNCT(c) (IN_CTYPE_DOMAIN (c) && ispunct (c))
115#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
116#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
117#define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit (c))
118#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
119#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
120
121#define isodigit(c) ((c) >= '0' && (c) <= '7')
122#define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0')
123#define octtobin(c) ((c) - '0')
124
Erik Andersene49d5ec2000-02-08 19:58:47 +0000125static double xstrtod __P((char *s));
126static int print_esc __P((char *escstart));
127static int print_formatted __P((char *format, int argc, char **argv));
128static long xstrtol __P((char *s));
129static unsigned long xstrtoul __P((char *s));
130static void print_direc
131__P(
132
133 (char *start, size_t length, int field_width, int precision,
134 char *argument));
135static void print_esc_char __P((int c));
136static void print_esc_string __P((char *str));
137static void verify __P((char *s, char *end));
Eric Andersencc8ed391999-10-05 16:24:54 +0000138
139/* The value to return to the calling program. */
140static int exit_status;
141
Erik Andersene49d5ec2000-02-08 19:58:47 +0000142static const char printf_usage[] = "printf format [argument...]\n";
Eric Andersencc8ed391999-10-05 16:24:54 +0000143
Erik Andersene49d5ec2000-02-08 19:58:47 +0000144int printf_main(int argc, char **argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000145{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000146 char *format;
147 int args_used;
Eric Andersencc8ed391999-10-05 16:24:54 +0000148
Erik Andersene49d5ec2000-02-08 19:58:47 +0000149 exit_status = 0;
150 if (argc <= 1 || **(argv + 1) == '-') {
151 usage(printf_usage);
152 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000153
Erik Andersene49d5ec2000-02-08 19:58:47 +0000154 format = argv[1];
155 argc -= 2;
156 argv += 2;
Eric Andersencc8ed391999-10-05 16:24:54 +0000157
Erik Andersene49d5ec2000-02-08 19:58:47 +0000158 do {
159 args_used = print_formatted(format, argc, argv);
160 argc -= args_used;
161 argv += args_used;
162 }
163 while (args_used > 0 && argc > 0);
Eric Andersencc8ed391999-10-05 16:24:54 +0000164
165/*
166 if (argc > 0)
167 fprintf(stderr, "excess args ignored");
168*/
169
Erik Andersene49d5ec2000-02-08 19:58:47 +0000170 exit(exit_status);
Eric Andersencc8ed391999-10-05 16:24:54 +0000171}
172
173/* Print the text in FORMAT, using ARGV (with ARGC elements) for
174 arguments to any `%' directives.
175 Return the number of elements of ARGV used. */
176
Erik Andersene49d5ec2000-02-08 19:58:47 +0000177static int print_formatted(char *format, int argc, char **argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000178{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000179 int save_argc = argc; /* Preserve original value. */
180 char *f; /* Pointer into `format'. */
181 char *direc_start; /* Start of % directive. */
182 size_t direc_length; /* Length of % directive. */
183 int field_width; /* Arg to first '*', or -1 if none. */
184 int precision; /* Arg to second '*', or -1 if none. */
Eric Andersencc8ed391999-10-05 16:24:54 +0000185
Erik Andersene49d5ec2000-02-08 19:58:47 +0000186 for (f = format; *f; ++f) {
187 switch (*f) {
188 case '%':
189 direc_start = f++;
190 direc_length = 1;
191 field_width = precision = -1;
192 if (*f == '%') {
193 putchar('%');
194 break;
195 }
196 if (*f == 'b') {
197 if (argc > 0) {
198 print_esc_string(*argv);
199 ++argv;
200 --argc;
201 }
202 break;
203 }
204 if (strchr("-+ #", *f)) {
205 ++f;
206 ++direc_length;
207 }
208 if (*f == '*') {
209 ++f;
210 ++direc_length;
211 if (argc > 0) {
212 field_width = xstrtoul(*argv);
213 ++argv;
214 --argc;
215 } else
216 field_width = 0;
217 } else
218 while (ISDIGIT(*f)) {
219 ++f;
220 ++direc_length;
221 }
222 if (*f == '.') {
223 ++f;
224 ++direc_length;
225 if (*f == '*') {
226 ++f;
227 ++direc_length;
228 if (argc > 0) {
229 precision = xstrtoul(*argv);
230 ++argv;
231 --argc;
232 } else
233 precision = 0;
234 } else
235 while (ISDIGIT(*f)) {
236 ++f;
237 ++direc_length;
238 }
239 }
240 if (*f == 'l' || *f == 'L' || *f == 'h') {
241 ++f;
242 ++direc_length;
243 }
244 /*
245 if (!strchr ("diouxXfeEgGcs", *f))
246 fprintf(stderr, "%%%c: invalid directive", *f);
247 */
248 ++direc_length;
249 if (argc > 0) {
250 print_direc(direc_start, direc_length, field_width,
251 precision, *argv);
252 ++argv;
253 --argc;
254 } else
255 print_direc(direc_start, direc_length, field_width,
256 precision, "");
257 break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000258
Erik Andersene49d5ec2000-02-08 19:58:47 +0000259 case '\\':
260 f += print_esc(f);
261 break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000262
Erik Andersene49d5ec2000-02-08 19:58:47 +0000263 default:
264 putchar(*f);
265 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000266 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000267
Erik Andersene49d5ec2000-02-08 19:58:47 +0000268 return save_argc - argc;
Eric Andersencc8ed391999-10-05 16:24:54 +0000269}
270
271/* Print a \ escape sequence starting at ESCSTART.
272 Return the number of characters in the escape sequence
273 besides the backslash. */
274
Erik Andersene49d5ec2000-02-08 19:58:47 +0000275static int print_esc(char *escstart)
Eric Andersencc8ed391999-10-05 16:24:54 +0000276{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000277 register char *p = escstart + 1;
278 int esc_value = 0; /* Value of \nnn escape. */
279 int esc_length; /* Length of \nnn escape. */
Eric Andersencc8ed391999-10-05 16:24:54 +0000280
Erik Andersene49d5ec2000-02-08 19:58:47 +0000281 /* \0ooo and \xhhh escapes have maximum length of 3 chars. */
282 if (*p == 'x') {
283 for (esc_length = 0, ++p;
284 esc_length < 3 && ISXDIGIT(*p); ++esc_length, ++p)
285 esc_value = esc_value * 16 + hextobin(*p);
Eric Andersencc8ed391999-10-05 16:24:54 +0000286/* if (esc_length == 0)
287 fprintf(stderr, "missing hex in esc");
288*/
Erik Andersene49d5ec2000-02-08 19:58:47 +0000289 putchar(esc_value);
290 } else if (*p == '0') {
291 for (esc_length = 0, ++p;
292 esc_length < 3 && isodigit(*p); ++esc_length, ++p)
293 esc_value = esc_value * 8 + octtobin(*p);
294 putchar(esc_value);
295 } else if (strchr("\"\\abcfnrtv", *p))
296 print_esc_char(*p++);
Eric Andersencc8ed391999-10-05 16:24:54 +0000297/* else
298 fprintf(stderr, "\\%c: invalid esc", *p);
299*/
Erik Andersene49d5ec2000-02-08 19:58:47 +0000300 return p - escstart - 1;
Eric Andersencc8ed391999-10-05 16:24:54 +0000301}
302
303/* Output a single-character \ escape. */
304
Erik Andersene49d5ec2000-02-08 19:58:47 +0000305static void print_esc_char(int c)
Eric Andersencc8ed391999-10-05 16:24:54 +0000306{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000307 switch (c) {
308 case 'a': /* Alert. */
309 putchar(7);
310 break;
311 case 'b': /* Backspace. */
312 putchar(8);
313 break;
314 case 'c': /* Cancel the rest of the output. */
315 exit(0);
316 break;
317 case 'f': /* Form feed. */
318 putchar(12);
319 break;
320 case 'n': /* New line. */
321 putchar(10);
322 break;
323 case 'r': /* Carriage return. */
324 putchar(13);
325 break;
326 case 't': /* Horizontal tab. */
327 putchar(9);
328 break;
329 case 'v': /* Vertical tab. */
330 putchar(11);
331 break;
332 default:
333 putchar(c);
334 break;
335 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000336}
337
338/* Print string STR, evaluating \ escapes. */
339
Erik Andersene49d5ec2000-02-08 19:58:47 +0000340static void print_esc_string(char *str)
Eric Andersencc8ed391999-10-05 16:24:54 +0000341{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000342 for (; *str; str++)
343 if (*str == '\\')
344 str += print_esc(str);
345 else
346 putchar(*str);
Eric Andersencc8ed391999-10-05 16:24:54 +0000347}
348
349static void
Erik Andersene49d5ec2000-02-08 19:58:47 +0000350print_direc(char *start, size_t length, int field_width, int precision,
351 char *argument)
Eric Andersencc8ed391999-10-05 16:24:54 +0000352{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000353 char *p; /* Null-terminated copy of % directive. */
Eric Andersencc8ed391999-10-05 16:24:54 +0000354
Erik Andersene49d5ec2000-02-08 19:58:47 +0000355 p = xmalloc((unsigned) (length + 1));
356 strncpy(p, start, length);
357 p[length] = 0;
Eric Andersencc8ed391999-10-05 16:24:54 +0000358
Erik Andersene49d5ec2000-02-08 19:58:47 +0000359 switch (p[length - 1]) {
360 case 'd':
361 case 'i':
362 if (field_width < 0) {
363 if (precision < 0)
364 printf(p, xstrtol(argument));
365 else
366 printf(p, precision, xstrtol(argument));
367 } else {
368 if (precision < 0)
369 printf(p, field_width, xstrtol(argument));
370 else
371 printf(p, field_width, precision, xstrtol(argument));
372 }
373 break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000374
Erik Andersene49d5ec2000-02-08 19:58:47 +0000375 case 'o':
376 case 'u':
377 case 'x':
378 case 'X':
379 if (field_width < 0) {
380 if (precision < 0)
381 printf(p, xstrtoul(argument));
382 else
383 printf(p, precision, xstrtoul(argument));
384 } else {
385 if (precision < 0)
386 printf(p, field_width, xstrtoul(argument));
387 else
388 printf(p, field_width, precision, xstrtoul(argument));
389 }
390 break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000391
Erik Andersene49d5ec2000-02-08 19:58:47 +0000392 case 'f':
393 case 'e':
394 case 'E':
395 case 'g':
396 case 'G':
397 if (field_width < 0) {
398 if (precision < 0)
399 printf(p, xstrtod(argument));
400 else
401 printf(p, precision, xstrtod(argument));
402 } else {
403 if (precision < 0)
404 printf(p, field_width, xstrtod(argument));
405 else
406 printf(p, field_width, precision, xstrtod(argument));
407 }
408 break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000409
Erik Andersene49d5ec2000-02-08 19:58:47 +0000410 case 'c':
411 printf(p, *argument);
412 break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000413
Erik Andersene49d5ec2000-02-08 19:58:47 +0000414 case 's':
415 if (field_width < 0) {
416 if (precision < 0)
417 printf(p, argument);
418 else
419 printf(p, precision, argument);
420 } else {
421 if (precision < 0)
422 printf(p, field_width, argument);
423 else
424 printf(p, field_width, precision, argument);
425 }
426 break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000427 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000428
Erik Andersene49d5ec2000-02-08 19:58:47 +0000429 free(p);
Eric Andersencc8ed391999-10-05 16:24:54 +0000430}
431
Erik Andersene49d5ec2000-02-08 19:58:47 +0000432static unsigned long xstrtoul(char *s)
Eric Andersencc8ed391999-10-05 16:24:54 +0000433{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000434 char *end;
435 unsigned long val;
Eric Andersencc8ed391999-10-05 16:24:54 +0000436
Erik Andersene49d5ec2000-02-08 19:58:47 +0000437 errno = 0;
438 val = strtoul(s, &end, 0);
439 verify(s, end);
440 return val;
Eric Andersencc8ed391999-10-05 16:24:54 +0000441}
442
Erik Andersene49d5ec2000-02-08 19:58:47 +0000443static long xstrtol(char *s)
Eric Andersencc8ed391999-10-05 16:24:54 +0000444{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000445 char *end;
446 long val;
Eric Andersencc8ed391999-10-05 16:24:54 +0000447
Erik Andersene49d5ec2000-02-08 19:58:47 +0000448 errno = 0;
449 val = strtol(s, &end, 0);
450 verify(s, end);
451 return val;
Eric Andersencc8ed391999-10-05 16:24:54 +0000452}
453
Erik Andersene49d5ec2000-02-08 19:58:47 +0000454static double xstrtod(char *s)
Eric Andersencc8ed391999-10-05 16:24:54 +0000455{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000456 char *end;
457 double val;
Eric Andersencc8ed391999-10-05 16:24:54 +0000458
Erik Andersene49d5ec2000-02-08 19:58:47 +0000459 errno = 0;
460 val = strtod(s, &end);
461 verify(s, end);
462 return val;
Eric Andersencc8ed391999-10-05 16:24:54 +0000463}
464
Erik Andersene49d5ec2000-02-08 19:58:47 +0000465static void verify(char *s, char *end)
Eric Andersencc8ed391999-10-05 16:24:54 +0000466{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000467 if (errno) {
468 fprintf(stderr, "%s", s);
469 exit_status = 1;
470 } else if (*end) {
471 /*
472 if (s == end)
473 fprintf(stderr, "%s: expected numeric", s);
474 else
475 fprintf(stderr, "%s: not completely converted", s);
476 */
477 exit_status = 1;
478 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000479}