summaryrefslogtreecommitdiff
path: root/builtins/printf.def
diff options
context:
space:
mode:
Diffstat (limited to 'builtins/printf.def')
-rw-r--r--builtins/printf.def203
1 files changed, 155 insertions, 48 deletions
diff --git a/builtins/printf.def b/builtins/printf.def
index 0e1d4aa..e447633 100644
--- a/builtins/printf.def
+++ b/builtins/printf.def
@@ -1,39 +1,49 @@
This file is printf.def, from which is created printf.c.
It implements the builtin "printf" in Bash.
-Copyright (C) 1997-2005 Free Software Foundation, Inc.
+Copyright (C) 1997-2009 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
-Bash is free software; you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
-version.
+Bash is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
-Bash is distributed in the hope that it will be useful, but WITHOUT ANY
-WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-for more details.
+Bash is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
-You should have received a copy of the GNU General Public License along
-with Bash; see the file COPYING. If not, write to the Free Software
-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+You should have received a copy of the GNU General Public License
+along with Bash. If not, see <http://www.gnu.org/licenses/>.
$PRODUCES printf.c
$BUILTIN printf
$FUNCTION printf_builtin
$SHORT_DOC printf [-v var] format [arguments]
-printf formats and prints ARGUMENTS under control of the FORMAT. FORMAT
-is a character string which contains three types of objects: plain
-characters, which are simply copied to standard output, character escape
-sequences which are converted and copied to the standard output, and
+Formats and prints ARGUMENTS under control of the FORMAT.
+
+Options:
+ -v var assign the output to shell variable VAR rather than
+ display it on the standard output
+
+FORMAT is a character string which contains three types of objects: plain
+characters, which are simply copied to standard output; character escape
+sequences, which are converted and copied to the standard output; and
format specifications, each of which causes printing of the next successive
-argument. In addition to the standard printf(1) formats, %b means to
-expand backslash escape sequences in the corresponding argument, and %q
-means to quote the argument in a way that can be reused as shell input.
-If the -v option is supplied, the output is placed into the value of the
-shell variable VAR rather than being sent to the standard output.
+argument.
+
+In addition to the standard format specifications described in printf(1)
+and printf(3), printf interprets:
+
+ %b expand backslash escape sequences in the corresponding argument
+ %q quote the argument in a way that can be reused as shell input
+
+Exit Status:
+Returns success unless an invalid option is given or a write or assignment
+error occurs.
$END
#include <config.h>
@@ -49,6 +59,12 @@ $END
# define INT_MIN (-2147483647-1)
#endif
+#if defined (PREFER_STDARG)
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
#include <stdio.h>
#include <chartypes.h>
@@ -60,10 +76,15 @@ $END
#include "../bashintl.h"
#include "../shell.h"
+#include "shmbutil.h"
#include "stdc.h"
#include "bashgetopt.h"
#include "common.h"
+#if defined (PRI_MACROS_BROKEN)
+# undef PRIdMAX
+#endif
+
#if !defined (PRIdMAX)
# if HAVE_LONG_LONG
# define PRIdMAX "lld"
@@ -89,31 +110,22 @@ extern int errno;
#define PF(f, func) \
do { \
- char *b = 0; \
int nw; \
clearerr (stdout); \
if (have_fieldwidth && have_precision) \
- nw = asprintf(&b, f, fieldwidth, precision, func); \
+ nw = vflag ? vbprintf (f, fieldwidth, precision, func) : printf (f, fieldwidth, precision, func); \
else if (have_fieldwidth) \
- nw = asprintf(&b, f, fieldwidth, func); \
+ nw = vflag ? vbprintf (f, fieldwidth, func) : printf (f, fieldwidth, func); \
else if (have_precision) \
- nw = asprintf(&b, f, precision, func); \
+ nw = vflag ? vbprintf (f, precision, func) : printf (f, fieldwidth, func); \
else \
- nw = asprintf(&b, f, func); \
+ nw = vflag ? vbprintf (f, func) : printf (f, func); \
tw += nw; \
- if (b) \
+ if (ferror (stdout)) \
{ \
- if (vflag) \
- (void)vbadd (b, nw); \
- else \
- (void)fputs (b, stdout); \
- if (ferror (stdout)) \
- { \
- sh_wrerror (); \
- clearerr (stdout); \
- return (EXECUTION_FAILURE); \
- } \
- free (b); \
+ sh_wrerror (); \
+ clearerr (stdout); \
+ return (EXECUTION_FAILURE); \
} \
} while (0)
@@ -123,7 +135,7 @@ extern int errno;
{ \
if (vflag) \
{ \
- bind_variable (vname, vbuf, 0); \
+ bind_printf_variable (vname, vbuf, 0); \
stupidly_hack_special_variables (vname); \
} \
if (conv_bufsize > 4096 ) \
@@ -138,9 +150,13 @@ extern int errno;
vbsize = 0; \
vbuf = 0; \
} \
+ else if (vbuf) \
+ vbuf[0] = 0; \
+ terminate_immediately--; \
fflush (stdout); \
if (ferror (stdout)) \
{ \
+ sh_wrerror (); \
clearerr (stdout); \
return (EXECUTION_FAILURE); \
} \
@@ -151,17 +167,27 @@ extern int errno;
#define SKIP1 "#'-+ 0"
#define LENMODS "hjlLtz"
+#if !HAVE_ASPRINTF
+extern int asprintf __P((char **, const char *, ...)) __attribute__((__format__ (printf, 2, 3)));
+#endif
+
+#if !HAVE_VSNPRINTF
+extern int vsnprintf __P((char *, size_t, const char *, ...)) __attribute__((__format__ (printf, 3, 4)));
+#endif
+
static void printf_erange __P((char *));
static int printstr __P((char *, char *, int, int, int));
static int tescape __P((char *, char *, int *));
static char *bexpand __P((char *, int, int *, int *));
static char *vbadd __P((char *, int));
+static int vbprintf __P((const char *, ...)) __attribute__((__format__ (printf, 1, 2)));
static char *mklong __P((char *, char *, size_t));
static int getchr __P((void));
static char *getstr __P((void));
static int getint __P((void));
static intmax_t getintmax __P((void));
static uintmax_t getuintmax __P((void));
+static SHELL_VAR *bind_printf_variable __P((char *, char *, int));
#if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD && !defined(STRTOLD_BROKEN)
typedef long double floatmax_t;
@@ -174,7 +200,7 @@ typedef double floatmax_t;
#endif
static floatmax_t getfloatmax __P((void));
-static int asciicode __P((void));
+static intmax_t asciicode __P((void));
static WORD_LIST *garglist;
static int retval;
@@ -210,10 +236,17 @@ printf_builtin (list)
switch (ch)
{
case 'v':
- if (legal_identifier (vname = list_optarg))
+ vname = list_optarg;
+#if defined (ARRAY_VARS)
+ if (legal_identifier (vname) || valid_array_reference (vname))
+#else
+ if (legal_identifier (vname))
+#endif
{
vflag = 1;
vblen = 0;
+ if (vbuf)
+ vbuf[0] = 0;
}
else
{
@@ -245,6 +278,8 @@ printf_builtin (list)
/* If the format string is empty after preprocessing, return immediately. */
if (format == 0 || *format == 0)
return (EXECUTION_SUCCESS);
+
+ terminate_immediately++;
/* Basic algorithm is to scan the format string for conversion
specifications -- once one is found, find out if the field
@@ -540,7 +575,7 @@ static void
printf_erange (s)
char *s;
{
- builtin_error ("warning: %s: %s", s, strerror(ERANGE));
+ builtin_error (_("warning: %s: %s"), s, strerror(ERANGE));
}
/* We duplicate a lot of what printf(3) does here. */
@@ -563,7 +598,7 @@ printstr (fmt, string, len, fieldwidth, precision)
#else
if (string == 0 || len == 0)
#endif
- return;
+ return 0;
#if 0
s = fmt;
@@ -821,7 +856,7 @@ vbadd (buf, blen)
if (blen == 1)
vbuf[vblen++] = buf[0];
- else
+ else if (blen > 1)
{
FASTCOPY (buf, vbuf + vblen, blen);
vblen += blen;
@@ -836,6 +871,44 @@ vbadd (buf, blen)
return vbuf;
}
+static int
+#if defined (PREFER_STDARG)
+vbprintf (const char *format, ...)
+#else
+vbprintf (format, va_alist)
+ const char *format;
+ va_dcl
+#endif
+{
+ va_list args;
+ size_t nlen;
+ int blen;
+
+ SH_VA_START (args, format);
+ blen = vsnprintf (vbuf + vblen, vbsize - vblen, format, args);
+ va_end (args);
+
+ nlen = vblen + blen + 1;
+ if (nlen >= vbsize)
+ {
+ vbsize = ((nlen + 63) >> 6) << 6;
+ vbuf = (char *)xrealloc (vbuf, vbsize);
+ SH_VA_START (args, format);
+ blen = vsnprintf (vbuf + vblen, vbsize - vblen, format, args);
+ va_end (args);
+ }
+
+ vblen += blen;
+ vbuf[vblen] = '\0';
+
+#ifdef DEBUG
+ if (strlen (vbuf) != vblen)
+ internal_error ("printf:vbadd: vblen (%d) != strlen (vbuf) (%d)", vblen, (int)strlen (vbuf));
+#endif
+
+ return (blen);
+}
+
static char *
mklong (str, modifiers, mlen)
char *str;
@@ -931,7 +1004,9 @@ getintmax ()
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;
+#if 0
+ ret = 0; /* return partially-converted value from strtoimax */
+#endif
conversion_error = 1;
}
else if (errno == ERANGE)
@@ -1000,12 +1075,44 @@ getfloatmax ()
}
/* NO check is needed for garglist here. */
-static int
+static intmax_t
asciicode ()
{
- register int ch;
+ register intmax_t ch;
+#if defined (HANDLE_MULTIBYTE)
+ wchar_t wc;
+ size_t mblength, slen;
+#endif
+ DECLARE_MBSTATE;
+
+#if defined (HANDLE_MULTIBYTE)
+ slen = strlen (garglist->word->word+1);
+ mblength = MBLEN (garglist->word->word+1, slen);
+ if (mblength > 1)
+ {
+ mblength = mbtowc (&wc, garglist->word->word+1, slen);
+ ch = wc; /* XXX */
+ }
+ else
+#endif
+ ch = (unsigned char)garglist->word->word[1];
- ch = garglist->word->word[1];
garglist = garglist->next;
return (ch);
}
+
+static SHELL_VAR *
+bind_printf_variable (name, value, flags)
+ char *name;
+ char *value;
+ int flags;
+{
+#if defined (ARRAY_VARS)
+ if (valid_array_reference (name) == 0)
+ return (bind_variable (name, value, flags));
+ else
+ return (assign_array_element (name, value, flags));
+#else /* !ARRAY_VARS */
+ return bind_variable (name, value, flags);
+#endif /* !ARRAY_VARS */
+}