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