This file is printf.def, from which is created printf.c.
It implements the builtin "printf" in Bash.
-Copyright (C) 1997 Free Software Foundation, Inc.
+Copyright (C) 1997-2002 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#include "../bashansi.h"
-#define NEED_STRTOIMAX_DECL
-
#include "../shell.h"
#include "stdc.h"
#include "bashgetopt.h"
#include "common.h"
-/* This should use the ISO C constant format strings; I'll do that later. */
-#if SIZEOF_LONG < SIZEOF_LONG_LONG
-# define INTMAX_CONV "ll"
-#else
-# define INTMAX_CONV "l"
+#if !defined (PRIdMAX)
+# if HAVE_LONG_LONG
+# define PRIdMAX "lld"
+# else
+# define PRIdMAX "ld"
+# endif
#endif
#if !defined (errno)
#define SKIP1 "#'-+ 0"
#define LENMODS "hjlLtz"
+static void printf_erange __P((char *));
static void printstr __P((char *, char *, int, int, int));
static int tescape __P((char *, int, char *, int *));
static char *bexpand __P((char *, int, int *, int *));
-static char *mklong __P((char *, char *));
+static char *mklong __P((char *, char *, size_t));
static int getchr __P((void));
static char *getstr __P((void));
static int getint __P((void));
-static long getlong __P((void));
-static unsigned long getulong __P((void));
-#if defined (HAVE_LONG_LONG)
-static long long getllong __P((void));
-static unsigned long long getullong __P((void));
-#endif
static intmax_t getintmax __P((void));
static uintmax_t getuintmax __P((void));
-static double getdouble __P((void));
+
#if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD
-static long double getldouble __P((void));
+typedef long double floatmax_t;
+# define FLOATMAX_CONV "L"
+# define strtofltmax strtold
+#else
+typedef double floatmax_t;
+# define FLOATMAX_CONV ""
+# define strtofltmax strtod
#endif
+static floatmax_t getfloatmax __P((void));
+
static int asciicode __P((void));
static WORD_LIST *garglist;
{
int ch, fieldwidth, precision;
int have_fieldwidth, have_precision;
- long tw;
+ intmax_t tw;
char convch, thisch, nextch, *format, *modstart, *fmt, *start;
conversion_error = 0;
retval = EXECUTION_SUCCESS;
- reset_internal_getopt ();
- if (internal_getopt (list, "") != -1)
- {
- builtin_usage();
- return (EX_USAGE);
- }
- list = loptend;
+
+ if (no_options (list))
+ return (EX_USAGE);
+ list = loptend; /* skip over possible `--' */
if (list == 0)
{
bind_var_to_int (var, tw);
else
{
- builtin_error ("%s: invalid variable name", var);
+ sh_invalidid (var);
PRETURN (EXECUTION_FAILURE);
}
}
char *p, *xp;
p = getstr ();
- xp = sh_backslash_quote (p);
+ if (ansic_shouldquote (p))
+ xp = ansic_quote (p, 0, (int *)0);
+ else
+ xp = sh_backslash_quote (p);
if (xp)
{
/* Use printstr to get fieldwidth and precision right. */
case 'i':
{
char *f;
-#if defined (HAVE_LONG_LONG)
- if (thisch == 'l' && nextch == 'l')
- {
- long long p;
+ long p;
+ intmax_t pp;
- p = getllong ();
- f = mklong (start, "ll");
- PF(f, p);
- }
- else
-#endif
- if (thisch == 'j')
+ p = pp = getintmax ();
+ if (p != pp)
{
- intmax_t p;
-
- p = getintmax ();
- f = mklong (start, INTMAX_CONV);
- PF(f, p);
+ f = mklong (start, PRIdMAX, sizeof (PRIdMAX) - 2);
+ PF (f, pp);
}
else
{
- long p;
-
- p = getlong ();
- f = mklong (start, "l");
- PF(f, p);
+ /* Optimize the common case where the integer fits
+ in "long". This also works around some long
+ long and/or intmax_t library bugs in the common
+ case, e.g. glibc 2.2 x86. */
+ f = mklong (start, "l", 1);
+ PF (f, p);
}
break;
}
case 'X':
{
char *f;
-#if defined (HAVE_LONG_LONG)
- if (thisch == 'l' && nextch == 'l')
- {
- unsigned long long p;
+ unsigned long p;
+ uintmax_t pp;
- p = getullong ();
- f = mklong (start, "ll");
- PF(f, p);
- }
- else
-#endif
- if (thisch == 'j')
+ p = pp = getuintmax ();
+ if (p != pp)
{
- uintmax_t p;
-
- p = getuintmax ();
- f = mklong (start, INTMAX_CONV);
- PF(f, p);
+ f = mklong (start, PRIdMAX, sizeof (PRIdMAX) - 2);
+ PF (f, pp);
}
else
{
- unsigned long p;
-
- p = getulong ();
- f = mklong (start, "l");
+ f = mklong (start, "l", 1);
PF (f, p);
}
break;
#endif
{
char *f;
-#if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD
- if (thisch == 'L')
- {
- long double p;
-
- p = getldouble ();
- f = mklong (start, "L");
- PF (f, p);
- }
- else
-#endif
- {
- double p;
+ floatmax_t p;
- p = getdouble ();
- f = mklong (start, "");
- PF (f, p);
- }
+ p = getfloatmax ();
+ f = mklong (start, FLOATMAX_CONV, sizeof(FLOATMAX_CONV) - 1);
+ PF (f, p);
break;
}
PRETURN (retval);
}
+static void
+printf_erange (s)
+ char *s;
+{
+ builtin_error ("warning: %s: %s", s, strerror(ERANGE));
+}
+
/* We duplicate a lot of what printf(3) does here. */
static void
printstr (fmt, string, len, fieldwidth, precision)
/* %b octal constants are `\0' followed by one, two, or three
octal digits... */
case '0':
- for (temp = 3, evalue = 0; ISOCTAL (*p) && temp--; p++)
- evalue = (evalue * 8) + OCTVALUE (*p);
- *cp = evalue & 0xFF;
- break;
-
/* but, as an extension, the other echo-like octal escape
sequences are supported as well. */
case '1': case '2': case '3': case '4':
case '5': case '6': case '7':
- for (temp = 2, evalue = c - '0'; ISOCTAL (*p) && temp--; p++)
+ for (temp = 2+(c=='0'), evalue = c - '0'; ISOCTAL (*p) && temp--; p++)
evalue = (evalue * 8) + OCTVALUE (*p);
*cp = evalue & 0xFF;
break;
}
static char *
-mklong (str, modifiers)
+mklong (str, modifiers, mlen)
char *str;
char *modifiers;
+ size_t mlen;
{
- size_t len, slen, mlen;
+ size_t len, slen;
slen = strlen (str);
- mlen = strlen (modifiers);
len = slen + mlen + 1;
if (len > conv_bufsize)
static int
getint ()
{
- long ret;
+ intmax_t ret;
- ret = getlong ();
+ ret = getintmax ();
if (ret > INT_MAX)
{
- builtin_error ("warning: %s: %s", garglist->word->word, strerror(ERANGE));
+ printf_erange (garglist->word->word);
ret = INT_MAX;
}
else if (ret < INT_MIN)
{
- builtin_error ("warning: %s: %s", garglist->word->word, strerror(ERANGE));
+ printf_erange (garglist->word->word);
ret = INT_MIN;
}
return ((int)ret);
}
-static long
-getlong ()
-{
- long ret;
- char *ep;
-
- if (garglist == 0)
- return (0);
-
- if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"')
- return asciicode ();
-
- errno = 0;
- ret = strtol (garglist->word->word, &ep, 0);
-
- if (*ep)
- {
- builtin_error ("%s: invalid number", garglist->word->word);
- /* POSIX.2 says ``...a diagnostic message shall be written to standard
- error, and the utility shall not exit with a zero exit status, but
- shall continue processing any remaining operands and shall write the
- value accumulated at the time the error was detected to standard
- output.'' Yecch. */
- ret = 0;
- conversion_error = 1;
- }
- else if (errno == ERANGE)
- builtin_error ("warning: %s: %s", garglist->word->word, strerror(ERANGE));
-
- garglist = garglist->next;
- return (ret);
-}
-
-static unsigned long
-getulong ()
-{
- unsigned long ret;
- char *ep;
-
- if (garglist == 0)
- return (0);
-
- if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"')
- return asciicode ();
-
- errno = 0;
- ret = strtoul (garglist->word->word, &ep, 0);
-
- if (*ep)
- {
- builtin_error ("%s: invalid number", garglist->word->word);
- /* Same thing about POSIX.2 conversion error requirements as getlong(). */
- ret = 0;
- conversion_error = 1;
- }
- else if (errno == ERANGE)
- builtin_error ("warning: %s: %s", garglist->word->word, strerror(ERANGE));
-
- garglist = garglist->next;
- return (ret);
-}
-
-#if defined (HAVE_LONG_LONG)
-
-static long long
-getllong ()
-{
- long long ret;
- char *ep;
-
- if (garglist == 0)
- return (0);
-
- if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"')
- return asciicode ();
-
- errno = 0;
- ret = strtoll (garglist->word->word, &ep, 0);
-
- if (*ep)
- {
- builtin_error ("%s: invalid number", garglist->word->word);
- /* POSIX.2 says ``...a diagnostic message shall be written to standard
- error, and the utility shall not exit with a zero exit status, but
- shall continue processing any remaining operands and shall write the
- value accumulated at the time the error was detected to standard
- output.'' Yecch. */
- ret = 0;
- conversion_error = 1;
- }
- else if (errno == ERANGE)
- builtin_error ("warning: %s: %s", garglist->word->word, strerror(ERANGE));
-
- garglist = garglist->next;
- return (ret);
-}
-
-static unsigned long long
-getullong ()
-{
- unsigned long long ret;
- char *ep;
-
- if (garglist == 0)
- return (0);
-
- if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"')
- return asciicode ();
-
- errno = 0;
- ret = strtoull (garglist->word->word, &ep, 0);
-
- if (*ep)
- {
- builtin_error ("%s: invalid number", garglist->word->word);
- /* Same thing about POSIX.2 conversion error requirements as getlong(). */
- ret = 0;
- conversion_error = 1;
- }
- else if (errno == ERANGE)
- builtin_error ("warning: %s: %s", garglist->word->word, strerror(ERANGE));
-
- garglist = garglist->next;
- return (ret);
-}
-
-#endif /* HAVE_LONG_LONG */
-
static intmax_t
getintmax ()
{
if (*ep)
{
- builtin_error ("%s: invalid number", garglist->word->word);
+ sh_invalidnum (garglist->word->word);
/* POSIX.2 says ``...a diagnostic message shall be written to standard
error, and the utility shall not exit with a zero exit status, but
shall continue processing any remaining operands and shall write the
conversion_error = 1;
}
else if (errno == ERANGE)
- builtin_error ("warning: %s: %s", garglist->word->word, strerror(ERANGE));
+ printf_erange (garglist->word->word);
garglist = garglist->next;
return (ret);
if (*ep)
{
- builtin_error ("%s: invalid number", garglist->word->word);
- /* Same thing about POSIX.2 conversion error requirements as getlong(). */
+ sh_invalidnum (garglist->word->word);
+ /* Same POSIX.2 conversion error requirements as getintmax(). */
ret = 0;
conversion_error = 1;
}
else if (errno == ERANGE)
- builtin_error ("warning: %s: %s", garglist->word->word, strerror(ERANGE));
+ printf_erange (garglist->word->word);
garglist = garglist->next;
return (ret);
}
-static double
-getdouble ()
+static floatmax_t
+getfloatmax ()
{
- double ret;
+ floatmax_t ret;
char *ep;
if (garglist == 0)
return asciicode ();
errno = 0;
- ret = strtod (garglist->word->word, &ep);
-
- if (*ep)
- {
- builtin_error ("%s: invalid number", garglist->word->word);
- /* Same thing about POSIX.2 conversion error requirements. */
- ret = 0;
- conversion_error = 1;
- }
- else if (errno == ERANGE)
- builtin_error ("warning: %s: %s", garglist->word->word, strerror(ERANGE));
-
- garglist = garglist->next;
- return (ret);
-}
-
-#if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD
-static long double
-getldouble ()
-{
- long double ret;
- char *ep;
-
- if (garglist == 0)
- return (0);
-
- if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"')
- return (asciicode ());
-
- errno = 0;
- ret = strtold (garglist->word->word, &ep);
+ ret = strtofltmax (garglist->word->word, &ep);
if (*ep)
{
- builtin_error ("%s: invalid number", garglist->word->word);
+ sh_invalidnum (garglist->word->word);
/* Same thing about POSIX.2 conversion error requirements. */
ret = 0;
conversion_error = 1;
}
else if (errno == ERANGE)
- builtin_error ("warning: %s: %s", garglist->word->word, strerror(ERANGE));
+ printf_erange (garglist->word->word);
garglist = garglist->next;
return (ret);
}
-#endif /* HAVE_LONG_DOUBLE && HAVE_DECL_STRTOLD */
/* NO check is needed for garglist here. */
static int