blob: e79843c80ababd3964e9cbd16f7f5e1b70c5a1a8 [file] [log] [blame]
Eric Andersencc8ed391999-10-05 16:24:54 +00001// I may still need some more cleaning...fix my error checking
2
3#include "internal.h"
4#ifdef BB_PRINTF
5
6/* printf - format and print data
7 Copyright (C) 90, 91, 92, 93, 94, 95, 1996 Free Software Foundation, Inc.
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software Foundation,
21 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22
23/* Usage: printf format [argument...]
24
25 A front end to the printf function that lets it be used from the shell.
26
27 Backslash escapes:
28
29 \" = double quote
30 \\ = backslash
31 \a = alert (bell)
32 \b = backspace
33 \c = produce no further output
34 \f = form feed
35 \n = new line
36 \r = carriage return
37 \t = horizontal tab
38 \v = vertical tab
39 \0ooo = octal number (ooo is 0 to 3 digits)
40 \xhhh = hexadecimal number (hhh is 1 to 3 digits)
41
42 Additional directive:
43
44 %b = print an argument string, interpreting backslash escapes
45
46 The `format' argument is re-used as many times as necessary
47 to convert all of the given arguments.
48
49 David MacKenzie <djm@gnu.ai.mit.edu> */
50
51
52// 19990508 Busy Boxed! Dave Cinege
53
54#include <unistd.h>
55#include <stdio.h>
56#include <sys/types.h>
57#include <getopt.h>
58#include <sys/stat.h>
59#include <string.h>
60#include <errno.h>
61#include <stdlib.h>
62#include <fcntl.h>
63#include <ctype.h>
64#include <libintl.h>
65
66
67#ifndef S_IFMT
68# define S_IFMT 0170000
69#endif
70#if !defined(S_ISBLK) && defined(S_IFBLK)
71# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
72#endif
73#if !defined(S_ISCHR) && defined(S_IFCHR)
74# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
75#endif
76#if !defined(S_ISDIR) && defined(S_IFDIR)
77# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
78#endif
79#if !defined(S_ISREG) && defined(S_IFREG)
80# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
81#endif
82#if !defined(S_ISFIFO) && defined(S_IFIFO)
83# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
84#endif
85#if !defined(S_ISLNK) && defined(S_IFLNK)
86# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
87#endif
88#if !defined(S_ISSOCK) && defined(S_IFSOCK)
89# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
90#endif
91#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */
92# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
93# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
94#endif
95#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */
96# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
97#endif
98
99#define IN_CTYPE_DOMAIN(c) 1
100
101#ifdef isblank
102# define ISBLANK(c) (IN_CTYPE_DOMAIN (c) && isblank (c))
103#else
104# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
105#endif
106#ifdef isgraph
107# define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isgraph (c))
108#else
109# define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isprint (c) && !isspace (c))
110#endif
111
112#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c))
113#define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum (c))
114#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
115#define ISCNTRL(c) (IN_CTYPE_DOMAIN (c) && iscntrl (c))
116#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
117#define ISPUNCT(c) (IN_CTYPE_DOMAIN (c) && ispunct (c))
118#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
119#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
120#define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit (c))
121#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
122#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
123
124#define isodigit(c) ((c) >= '0' && (c) <= '7')
125#define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0')
126#define octtobin(c) ((c) - '0')
127
128char *xmalloc ();
129
130static double xstrtod __P ((char *s));
131static int print_esc __P ((char *escstart));
132static int print_formatted __P ((char *format, int argc, char **argv));
133static long xstrtol __P ((char *s));
134static unsigned long xstrtoul __P ((char *s));
135static void print_direc __P ((char *start, size_t length, int field_width, int precision, char *argument));
136static void print_esc_char __P ((int c));
137static void print_esc_string __P ((char *str));
138static void verify __P ((char *s, char *end));
139
140/* The value to return to the calling program. */
141static int exit_status;
142
143const char printf_usage[] = "Usage: printf format [argument...]\n";
144
145int
146printf_main(struct FileInfo * i, int argc, char * * argv)
147{
148 char *format;
149 int args_used;
150
151 exit_status = 0;
152
153 format = argv[1];
154 argc -= 2;
155 argv += 2;
156
157 do
158 {
159 args_used = print_formatted (format, argc, argv);
160 argc -= args_used;
161 argv += args_used;
162 }
163 while (args_used > 0 && argc > 0);
164
165/*
166 if (argc > 0)
167 fprintf(stderr, "excess args ignored");
168*/
169
170 exit (exit_status);
171}
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
177static int
178print_formatted (char *format, int argc, char **argv)
179{
180 int save_argc = argc; /* Preserve original value. */
181 char *f; /* Pointer into `format'. */
182 char *direc_start; /* Start of % directive. */
183 size_t direc_length; /* Length of % directive. */
184 int field_width; /* Arg to first '*', or -1 if none. */
185 int precision; /* Arg to second '*', or -1 if none. */
186
187 for (f = format; *f; ++f)
188 {
189 switch (*f)
190 {
191 case '%':
192 direc_start = f++;
193 direc_length = 1;
194 field_width = precision = -1;
195 if (*f == '%')
196 {
197 putchar ('%');
198 break;
199 }
200 if (*f == 'b')
201 {
202 if (argc > 0)
203 {
204 print_esc_string (*argv);
205 ++argv;
206 --argc;
207 }
208 break;
209 }
210 if (strchr ("-+ #", *f))
211 {
212 ++f;
213 ++direc_length;
214 }
215 if (*f == '*')
216 {
217 ++f;
218 ++direc_length;
219 if (argc > 0)
220 {
221 field_width = xstrtoul (*argv);
222 ++argv;
223 --argc;
224 }
225 else
226 field_width = 0;
227 }
228 else
229 while (ISDIGIT (*f))
230 {
231 ++f;
232 ++direc_length;
233 }
234 if (*f == '.')
235 {
236 ++f;
237 ++direc_length;
238 if (*f == '*')
239 {
240 ++f;
241 ++direc_length;
242 if (argc > 0)
243 {
244 precision = xstrtoul (*argv);
245 ++argv;
246 --argc;
247 }
248 else
249 precision = 0;
250 }
251 else
252 while (ISDIGIT (*f))
253 {
254 ++f;
255 ++direc_length;
256 }
257 }
258 if (*f == 'l' || *f == 'L' || *f == 'h')
259 {
260 ++f;
261 ++direc_length;
262 }
263 /*
264 if (!strchr ("diouxXfeEgGcs", *f))
265 fprintf(stderr, "%%%c: invalid directive", *f);
266 */
267 ++direc_length;
268 if (argc > 0)
269 {
270 print_direc (direc_start, direc_length, field_width,
271 precision, *argv);
272 ++argv;
273 --argc;
274 }
275 else
276 print_direc (direc_start, direc_length, field_width,
277 precision, "");
278 break;
279
280 case '\\':
281 f += print_esc (f);
282 break;
283
284 default:
285 putchar (*f);
286 }
287 }
288
289 return save_argc - argc;
290}
291
292/* Print a \ escape sequence starting at ESCSTART.
293 Return the number of characters in the escape sequence
294 besides the backslash. */
295
296static int
297print_esc (char *escstart)
298{
299 register char *p = escstart + 1;
300 int esc_value = 0; /* Value of \nnn escape. */
301 int esc_length; /* Length of \nnn escape. */
302
303 /* \0ooo and \xhhh escapes have maximum length of 3 chars. */
304 if (*p == 'x')
305 {
306 for (esc_length = 0, ++p;
307 esc_length < 3 && ISXDIGIT (*p);
308 ++esc_length, ++p)
309 esc_value = esc_value * 16 + hextobin (*p);
310/* if (esc_length == 0)
311 fprintf(stderr, "missing hex in esc");
312*/
313 putchar (esc_value);
314 }
315 else if (*p == '0')
316 {
317 for (esc_length = 0, ++p;
318 esc_length < 3 && isodigit (*p);
319 ++esc_length, ++p)
320 esc_value = esc_value * 8 + octtobin (*p);
321 putchar (esc_value);
322 }
323 else if (strchr ("\"\\abcfnrtv", *p))
324 print_esc_char (*p++);
325/* else
326 fprintf(stderr, "\\%c: invalid esc", *p);
327*/
328 return p - escstart - 1;
329}
330
331/* Output a single-character \ escape. */
332
333static void
334print_esc_char (int c)
335{
336 switch (c)
337 {
338 case 'a': /* Alert. */
339 putchar (7);
340 break;
341 case 'b': /* Backspace. */
342 putchar (8);
343 break;
344 case 'c': /* Cancel the rest of the output. */
345 exit (0);
346 break;
347 case 'f': /* Form feed. */
348 putchar (12);
349 break;
350 case 'n': /* New line. */
351 putchar (10);
352 break;
353 case 'r': /* Carriage return. */
354 putchar (13);
355 break;
356 case 't': /* Horizontal tab. */
357 putchar (9);
358 break;
359 case 'v': /* Vertical tab. */
360 putchar (11);
361 break;
362 default:
363 putchar (c);
364 break;
365 }
366}
367
368/* Print string STR, evaluating \ escapes. */
369
370static void
371print_esc_string (char *str)
372{
373 for (; *str; str++)
374 if (*str == '\\')
375 str += print_esc (str);
376 else
377 putchar (*str);
378}
379
380static void
381print_direc (char *start, size_t length, int field_width, int precision, char *argument)
382{
383 char *p; /* Null-terminated copy of % directive. */
384
385 p = xmalloc ((unsigned) (length + 1));
386 strncpy (p, start, length);
387 p[length] = 0;
388
389 switch (p[length - 1])
390 {
391 case 'd':
392 case 'i':
393 if (field_width < 0)
394 {
395 if (precision < 0)
396 printf (p, xstrtol (argument));
397 else
398 printf (p, precision, xstrtol (argument));
399 }
400 else
401 {
402 if (precision < 0)
403 printf (p, field_width, xstrtol (argument));
404 else
405 printf (p, field_width, precision, xstrtol (argument));
406 }
407 break;
408
409 case 'o':
410 case 'u':
411 case 'x':
412 case 'X':
413 if (field_width < 0)
414 {
415 if (precision < 0)
416 printf (p, xstrtoul (argument));
417 else
418 printf (p, precision, xstrtoul (argument));
419 }
420 else
421 {
422 if (precision < 0)
423 printf (p, field_width, xstrtoul (argument));
424 else
425 printf (p, field_width, precision, xstrtoul (argument));
426 }
427 break;
428
429 case 'f':
430 case 'e':
431 case 'E':
432 case 'g':
433 case 'G':
434 if (field_width < 0)
435 {
436 if (precision < 0)
437 printf (p, xstrtod (argument));
438 else
439 printf (p, precision, xstrtod (argument));
440 }
441 else
442 {
443 if (precision < 0)
444 printf (p, field_width, xstrtod (argument));
445 else
446 printf (p, field_width, precision, xstrtod (argument));
447 }
448 break;
449
450 case 'c':
451 printf (p, *argument);
452 break;
453
454 case 's':
455 if (field_width < 0)
456 {
457 if (precision < 0)
458 printf (p, argument);
459 else
460 printf (p, precision, argument);
461 }
462 else
463 {
464 if (precision < 0)
465 printf (p, field_width, argument);
466 else
467 printf (p, field_width, precision, argument);
468 }
469 break;
470 }
471
472 free (p);
473}
474
475static unsigned long
476xstrtoul (char *s)
477{
478 char *end;
479 unsigned long val;
480
481 errno = 0;
482 val = strtoul (s, &end, 0);
483 verify (s, end);
484 return val;
485}
486
487static long
488xstrtol (char *s)
489{
490 char *end;
491 long val;
492
493 errno = 0;
494 val = strtol (s, &end, 0);
495 verify (s, end);
496 return val;
497}
498
499static double
500xstrtod (char *s)
501{
502 char *end;
503 double val;
504
505 errno = 0;
506 val = strtod (s, &end);
507 verify (s, end);
508 return val;
509}
510
511static void
512verify (char *s, char *end)
513{
514 if (errno)
515 {
516 fprintf(stderr, "%s", s);
517 exit_status = 1;
518 }
519 else if (*end)
520 {
521 /*
522 if (s == end)
523 fprintf(stderr, "%s: expected numeric", s);
524 else
525 fprintf(stderr, "%s: not completely converted", s);
526 */
527 exit_status = 1;
528 }
529}
530
531#endif