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