]> git.ipfire.org Git - thirdparty/bash.git/blame - builtins/printf.def
Imported from ../bash-2.04.tar.gz.
[thirdparty/bash.git] / builtins / printf.def
CommitLineData
cce855bc
JA
1This file is printf.def, from which is created printf.c.
2It implements the builtin "printf" in Bash.
3
4Copyright (C) 1997 Free Software Foundation, Inc.
5
6This file is part of GNU Bash, the Bourne Again SHell.
7
8Bash is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
bb70624e 10Software Foundation; either version 2, or (at your option) any later
cce855bc
JA
11version.
12
13Bash is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with Bash; see the file COPYING. If not, write to the Free Software
20Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
21
22$PRODUCES printf.c
23
24$BUILTIN printf
25$FUNCTION printf_builtin
26$SHORT_DOC printf format [arguments]
27printf formats and prints ARGUMENTS under control of the FORMAT. FORMAT
28is a character string which contains three types of objects: plain
29characters, which are simply copied to standard output, character escape
30sequences which are converted and copied to the standard output, and
31format specifications, each of which causes printing of the next successive
32argument. In addition to the standard printf(1) formats, %b means to
33expand backslash escape sequences in the corresponding argument, and %q
34means to quote the argument in a way that can be reused as shell input.
35$END
36
37#include <config.h>
38
39#include "../bashtypes.h"
40
41#include <errno.h>
42#if defined (HAVE_LIMITS_H)
43# include <limits.h>
44#else
45 /* Assume 32-bit ints and longs. */
46# define LONG_MAX 2147483647L
47# define LONG_MIN (-2147483647L-1)
48# define INT_MAX 2147483647
49# define INT_MIN (-2147483647-1)
50#endif
51
52#include <stdio.h>
53#include <ctype.h>
54
55#include "../bashansi.h"
56#include "../shell.h"
bb70624e 57#include "stdc.h"
cce855bc
JA
58#include "bashgetopt.h"
59
60#if !defined (errno)
61extern int errno;
62#endif
63
64#define PF(f, func) \
65 do { \
66 if (fieldwidth && precision) \
67 (void)printf(f, fieldwidth, precision, func); \
68 else if (fieldwidth && precision == 0) \
69 (void)printf(f, fieldwidth, func); \
70 else if (precision) \
71 (void)printf(f, precision, func); \
72 else \
73 (void)printf(f, func); \
74 } while (0)
75
76#define PRETURN(value) \
bb70624e 77 do { /* free (format); */ fflush (stdout); return (value); } while (0)
cce855bc
JA
78
79#define SKIP1 "#-+ 0"
80#define SKIP2 "*0123456789"
81
82static void printstr __P((char *, char *, int, int, int));
bb70624e 83static int tescape __P((char *, int, char *, int *));
cce855bc
JA
84static char *bexpand __P((char *, int, int *, int *));
85static char *mklong __P((char *, int));
86static int getchr __P((void));
87static char *getstr __P((void));
88static int getint __P((void));
89static int getlong __P((long *));
90static int getulong __P((unsigned long *));
91static double getdouble __P((void));
92static int asciicode __P((void));
93
94static WORD_LIST *garglist;
95static int retval;
96
97extern char *backslash_quote ();
98
99int
100printf_builtin (list)
101 WORD_LIST *list;
102{
b72432fd 103 int ch, end, fieldwidth, precision, foundmod, fmtlen;
cce855bc
JA
104 char convch, nextch, *format, *fmt, *start;
105
106 retval = EXECUTION_SUCCESS;
107 reset_internal_getopt ();
108 while ((ch = internal_getopt (list, "")) != -1)
109 {
110 switch (ch)
111 {
112 case '?':
113 default:
114 builtin_usage();
115 return (EX_USAGE);
116 }
117 }
118 list = loptend;
119
120 if (list == 0)
121 {
122 builtin_usage ();
123 return (EX_USAGE);
124 }
125
126 if (list->word->word == 0 || list->word->word[0] == '\0')
127 return (EXECUTION_SUCCESS);
128
bb70624e 129 format = list->word->word;
cce855bc
JA
130
131 garglist = list->next;
132
bc4cd23c 133 /* If the format string is empty after preprocessing, return immediately. */
bb70624e 134 if (format == 0 || *format == 0)
bc4cd23c
JA
135 return (EXECUTION_SUCCESS);
136
cce855bc
JA
137 /* Basic algorithm is to scan the format string for conversion
138 specifications -- once one is found, find out if the field
139 width or precision is a '*'; if it is, gather up value. Note,
140 format strings are reused as necessary to use up the provided
141 arguments, arguments of zero/null string are provided to use
142 up the format string. */
143
144 do
145 {
146 /* find next format specification */
bb70624e 147 for (fmt = format; *fmt; fmt++)
cce855bc
JA
148 {
149 precision = fieldwidth = foundmod = 0;
150
bb70624e
JA
151 if (*fmt == '\\')
152 {
153 fmt++;
154 /* A NULL third argument to tescape means to not do special
155 processing for \c. */
156 fmt += tescape (fmt, 1, &nextch, (int *)NULL);
157 putchar (nextch);
158 fmt--; /* for loop will increment it for us again */
159 continue;
160 }
161
cce855bc
JA
162 if (*fmt != '%')
163 {
164 putchar (*fmt);
165 continue;
166 }
167
168 /* ASSERT(*fmt == '%') */
169 start = fmt++;
170
171 if (*fmt == '%') /* %% prints a % */
172 {
173 putchar ('%');
174 continue;
175 }
176
177 /* found format specification, skip to field width */
178 for (; *fmt && strchr(SKIP1, *fmt); ++fmt)
179 ;
180 fieldwidth = (*fmt == '*') ? getint () : 0;
181
182 /* skip to possible '.', get following precision */
183 for (; *fmt && strchr(SKIP2, *fmt); ++fmt)
184 ;
185 if (*fmt == '.')
186 {
187 ++fmt;
188 precision = (*fmt == '*') ? getint () : 0;
189 }
190
191 /* skip to conversion char */
192 for (; *fmt && strchr(SKIP2, *fmt); ++fmt)
193 ;
cce855bc
JA
194
195 /* skip possible format modifiers */
196 if (*fmt == 'l' || *fmt == 'L' || *fmt == 'h')
197 {
198 fmt++;
199 foundmod = 1;
200 }
201
b72432fd
JA
202 if (*fmt == 0)
203 {
204 builtin_error ("`%s': missing format character", start);
205 PRETURN (EXECUTION_FAILURE);
206 }
207
cce855bc
JA
208 convch = *fmt;
209 nextch = fmt[1];
210 fmt[1] = '\0';
211 switch(convch)
212 {
213 case 'c':
214 {
215 char p;
216
217 p = getchr ();
218 PF(start, p);
219 break;
220 }
221
222 case 's':
223 {
224 char *p;
225
226 p = getstr ();
227 PF(start, p);
228 break;
229 }
230
231 case 'b': /* expand escapes in argument */
232 {
233 char *p, *xp;
234 int rlen;
235
236 p = getstr ();
237 ch = rlen = 0;
238 xp = bexpand (p, strlen (p), &ch, &rlen);
239
240 if (xp)
241 {
242 /* Have to use printstr because of possible NUL bytes
243 in XP -- printf does not handle that well. */
244 printstr (start, xp, rlen, fieldwidth, precision);
245 free (xp);
246 }
247
248 if (ch)
249 PRETURN (retval);
250 break;
251 }
252
253 case 'q': /* print with shell quoting */
254 {
255 char *p, *xp;
256
257 p = getstr ();
258 xp = backslash_quote (p);
259 if (xp)
260 {
261 /* Use printstr to get fieldwidth and precision right. */
262 printstr (start, xp, strlen (xp), fieldwidth, precision);
263 free (xp);
264 }
265 break;
266 }
267
268 case 'd':
269 case 'i':
270 {
271 long p;
272 char *f;
273
274 if (foundmod == 0 && ((f = mklong (start, convch)) == NULL))
275 PRETURN (EXECUTION_FAILURE);
276 else
277 f = start;
278 if (getlong (&p))
279 PRETURN (EXECUTION_FAILURE);
280 PF(f, p);
281 break;
282 }
283
284 case 'o':
285 case 'u':
286 case 'x':
287 case 'X':
288 {
289 unsigned long p;
290 char *f;
291
292 if (foundmod == 0 && ((f = mklong (start, convch)) == NULL))
293 PRETURN (EXECUTION_FAILURE);
294 else
295 f = start;
296 if (getulong (&p))
297 PRETURN (EXECUTION_FAILURE);
298 PF (f, p);
299 break;
300 }
301
302 case 'e':
303 case 'E':
304 case 'f':
305 case 'g':
306 case 'G':
307 {
308 double p;
309
310 p = getdouble ();
311 PF(start, p);
312 break;
313 }
314
bb70624e
JA
315 /* We don't output unrecognized format characters; we print an
316 error message and return a failure exit status. */
cce855bc 317 default:
bb70624e 318 builtin_error ("`%c': invalid format character", convch);
cce855bc
JA
319 PRETURN (EXECUTION_FAILURE);
320 }
321
322 fmt[1] = nextch;
323 }
324 }
bc4cd23c 325 while (garglist && garglist != list->next);
cce855bc
JA
326
327 PRETURN (retval);
328}
329
330/* We duplicate a lot of what printf(3) does here. */
331static void
332printstr (fmt, string, len, fieldwidth, precision)
333 char *fmt; /* format */
334 char *string; /* expanded string argument */
335 int len; /* length of expanded string */
336 int fieldwidth; /* argument for width of `*' */
337 int precision; /* argument for precision of `*' */
338{
339#if 0
340 char *s;
341#endif
342 int padlen, nc, ljust, i;
343 int fw, pr; /* fieldwidth and precision */
344
345 if (string == 0 || *string == '\0')
346 return;
347
348#if 0
349 s = fmt;
350#endif
351 if (*fmt == '%')
352 fmt++;
353
354 ljust = fw = pr = 0;
355
356 /* skip flags */
357 while (*fmt == '#' || *fmt == '-' || *fmt == '+' || *fmt == ' ' || *fmt == '0')
358 {
359 if (*fmt == '-')
360 ljust = 1;
361 fmt++;
362 }
363
364 /* get fieldwidth, if present */
365 if (*fmt == '*')
366 {
367 fmt++;
368 fw = fieldwidth;
369 }
370 else if (isdigit (*fmt))
371 {
372 fw = *fmt++ - '0';
373 while (isdigit (*fmt))
374 fw = (fw * 10) + (*fmt++ - '0');
375 }
376
377 /* get precision, if present */
378 if (*fmt == '.')
379 {
380 fmt++;
381 if (*fmt == '*')
382 {
383 fmt++;
384 pr = precision;
385 }
386 else if (isdigit (*fmt))
387 {
388 pr = *fmt++ - '0';
389 while (isdigit (*fmt))
390 pr = (pr * 10) + (*fmt++ - '0');
391 }
392 }
393
394#if 0
395 /* If we remove this, get rid of `s'. */
396 if (*fmt != 'b' && *fmt != 'q')
397 {
398 internal_error ("format parsing problem: %s", s);
399 fw = pr = 0;
400 }
401#endif
402
403 /* chars from string to print */
404 nc = (pr > 0 && pr <= len) ? pr : len;
405
406 padlen = fw - nc;
407 if (padlen < 0)
408 padlen = 0;
409 if (ljust)
410 padlen = -padlen;
411
412 /* leading pad characters */
413 for (; padlen > 0; padlen--)
414 putchar (' ');
415
416 /* output NC characters from STRING */
417 for (i = 0; i < nc; i++)
418 putchar (string[i]);
419
420 /* output any necessary trailing padding */
421 for (; padlen < 0; padlen++)
422 putchar (' ');
423}
424
425/* Convert STRING by expanding the escape sequences specified by the
426 POSIX standard for printf's `%b' format string. If SAWC is non-null,
427 recognize `\c' and use that as a string terminator. If we see \c, set
428 *SAWC to 1 before returning. LEN is the length of STRING. */
429
430#ifdef isoctal
431#undef isoctal
432#endif
433
434#define isoctal(c) ((c) >= '0' && (c) <= '7')
435
436#define OCTVALUE(c) ((c) - '0')
437
438#ifndef isxdigit
439# define isxdigit(c) (isdigit((c)) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
440#endif
441
442#define HEXVALUE(c) \
443 ((c) >= 'a' && (c) <= 'f' ? (c)-'a'+10 : (c) >= 'A' && (c) <= 'F' ? (c)-'A'+10 : (c)-'0')
bb70624e
JA
444
445/* Translate a single backslash-escape sequence starting at ESTART (the
446 character after the backslash) and return the number of characters
447 consumed by the sequence. CP is the place to return the translated
448 value. *SAWC is set to 1 if the escape sequence was \c, since that means
449 to short-circuit the rest of the processing. If SAWC is null, we don't
450 do the \c short-circuiting, and \c is treated as an unrecognized escape
451 sequence. */
452static int
453tescape (estart, trans_squote, cp, sawc)
454 char *estart;
455 int trans_squote;
456 char *cp;
457 int *sawc;
458{
459 register char *p;
460 int temp, c, evalue;
461
462 p = estart;
463
464 switch (c = *p++)
465 {
466#if defined (__STDC__)
467 case 'a': *cp = '\a'; break;
468#else
469 case 'a': *cp = '\007'; break;
470#endif
471
472 case 'b': *cp = '\b'; break;
473
474 case 'e': *cp = '\033'; break; /* ESC -- non-ANSI */
475
476 case 'f': *cp = '\f'; break;
477
478 case 'n': *cp = '\n'; break;
479
480 case 'r': *cp = '\r'; break;
481
482 case 't': *cp = '\t'; break;
483
484 case 'v': *cp = '\v'; break;
485
486 /* %b octal constants are `\0' followed by one, two, or three
487 octal digits... */
488 case '0':
489 for (temp = 3, evalue = 0; isoctal (*p) && temp--; p++)
490 evalue = (evalue * 8) + OCTVALUE (*p);
491 *cp = evalue;
492 break;
493
494 /* but, as an extension, the other echo-like octal escape
495 sequences are supported as well. */
496 case '1': case '2': case '3': case '4':
497 case '5': case '6': case '7':
498 for (temp = 2, evalue = c - '0'; isoctal (*p) && temp--; p++)
499 evalue = (evalue * 8) + OCTVALUE (*p);
500 *cp = evalue;
501 break;
502
503 /* And, as another extension, we allow \xNNN, where each N is a
504 hex digit. */
505 case 'x':
506 for (temp = 3, evalue = 0; isxdigit (*p) && temp--; p++)
507 evalue = (evalue * 16) + HEXVALUE (*p);
508 if (temp == 3)
509 {
510 builtin_error ("missing hex digit for \\x");
511 *cp = '\\';
512 return 0;
513 }
514 *cp = evalue;
515 break;
516
517 case '\\': /* \\ -> \ */
518 *cp = c;
519 break;
520
521 case '\'': /* TRANS_SQUOTE != 0 means \' -> ' */
522 if (trans_squote)
523 *cp = c;
524 else
525 {
526 *cp = '\\';
527 return 0;
528 }
529 break;
530
531 case 'c':
532 if (sawc)
533 {
534 *sawc = 1;
535 break;
536 }
537 /* other backslash escapes are passed through unaltered */
538 default:
539 *cp = '\\';
540 return 0;
541 }
542 return (p - estart);
543}
544
cce855bc
JA
545static char *
546bexpand (string, len, sawc, lenp)
547 char *string;
548 int len, *sawc, *lenp;
549{
bb70624e
JA
550 int temp;
551 char *ret, *r, *s, c;
cce855bc
JA
552
553 if (string == 0 || *string == '\0')
554 {
555 if (sawc)
556 *sawc = 0;
557 if (lenp)
558 *lenp = 0;
559 return ((char *)NULL);
560 }
561
562 ret = xmalloc (len + 1);
563 for (r = ret, s = string; s && *s; )
564 {
565 c = *s++;
566 if (c != '\\' || *s == '\0')
567 {
568 *r++ = c;
569 continue;
570 }
bb70624e
JA
571 temp = 0;
572 s += tescape (s, 0, &c, &temp);
573 if (temp)
cce855bc 574 {
cce855bc
JA
575 if (sawc)
576 *sawc = 1;
bb70624e 577 break;
cce855bc
JA
578 }
579
580 *r++ = c;
581 }
582
583 *r = '\0';
584 if (lenp)
585 *lenp = r - ret;
586 return ret;
587}
588
589static char *
590mklong (str, ch)
591 char *str;
592 int ch;
593{
594 static char copy[64];
595 int len;
596
597 len = strlen (str) + 2;
598 FASTCOPY (str, copy, len - 3);
599 copy[len - 3] = 'l';
600 copy[len - 2] = ch;
601 copy[len - 1] = '\0';
602 return (copy);
603}
604
605static int
606getchr ()
607{
608 int ret;
609
610 if (garglist == 0)
611 return ('\0');
612
613 ret = (int)garglist->word->word[0];
614 garglist = garglist->next;
615 return ret;
616}
617
618static char *
619getstr ()
620{
621 char *ret;
622
623 if (garglist == 0)
624 return ("");
625
626 ret = garglist->word->word;
627 garglist = garglist->next;
628 return ret;
629}
630
631static int
632getint ()
633{
634 long ret;
635
636 if (getlong (&ret))
637 return (0);
638
639 if (ret > INT_MAX)
640 {
641 builtin_error ("%s: %s", garglist->word->word, strerror(ERANGE));
642 return (0);
643 }
644
645 return ((int)ret);
646}
647
648static int
649getlong (lp)
650 long *lp;
651{
652 long ret;
bb70624e 653 char *ep;
cce855bc
JA
654
655 if (garglist == 0)
656 {
657 *lp = 0L;
658 return (0);
659 }
660
661 if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"')
662 {
663 *lp = (long)asciicode ();
664 return (0);
665 }
666
667 errno = 0;
bb70624e
JA
668 /* If we use 0 as the third argument, we can handle octal and hex, which
669 legal_number does not. (This was
670 if (legal_number (garglist->word->word, &ret) == 0)
671 ) */
672 ret = strtol (garglist->word->word, &ep, 0);
673 if (*ep != '\0')
cce855bc 674 {
bb70624e 675 builtin_error ("%s: invalid number", garglist->word->word);
cce855bc
JA
676 return (1);
677 }
678 else if (errno == ERANGE)
679 {
680 builtin_error ("%s: %s", garglist->word->word, strerror(ERANGE));
681 return (1);
682 }
683
684 *lp = ret;
685 garglist = garglist->next;
686 return (0);
687}
688
689static int
690getulong (ulp)
691 unsigned long *ulp;
692{
693 unsigned long ret;
694 char *ep;
695
696 if (garglist == 0)
697 {
698 *ulp = (unsigned long)0;
699 return (0);
700 }
701
702 if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"')
703 {
704 *ulp = (unsigned long)asciicode ();
705 return (0);
706 }
707
708 errno = 0;
709 ret = strtoul (garglist->word->word, &ep, 0);
710
711 if (*ep)
712 {
bb70624e 713 builtin_error ("%s: invalid number", garglist->word->word);
cce855bc
JA
714 return (1);
715 }
716 else if (errno == ERANGE)
717 {
718 builtin_error ("%s: %s", garglist->word->word, strerror(ERANGE));
719 return (1);
720 }
721
722 *ulp = ret;
723 garglist = garglist->next;
724 return (0);
725}
726
727static double
728getdouble ()
729{
730 double ret;
731
732 if (garglist == 0)
733 return ((double)0);
734
735 if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"')
736 return ((double)asciicode ());
737
738 /* This should use strtod if it is available. */
739 ret = atof (garglist->word->word);
740 garglist = garglist->next;
741 return (ret);
742}
743
744/* NO check is needed for garglist here. */
745static int
746asciicode ()
747{
748 register int ch;
749
750 ch = garglist->word->word[1];
751 garglist = garglist->next;
752 return (ch);
753}