This file is printf.def, from which is created printf.c.
It implements the builtin "printf" in Bash.
-Copyright (C) 1997-2009 Free Software Foundation, Inc.
+Copyright (C) 1997-2010 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
format specifications, each of which causes printing of the next successive
argument.
-In addition to the standard format specifications described in printf(1)
-and printf(3), printf interprets:
+In addition to the standard format specifications described in printf(1),
+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
+ %(fmt)T output the date-time string resulting from using FMT as a format
+ string for strftime(3)
+
+The format is re-used as necessary to consume all of the arguments. If
+there are fewer arguments than the format requires, extra format
+specifications behave as if a zero value or null string, as appropriate,
+had been supplied.
Exit Status:
Returns success unless an invalid option is given or a write or assignment
# include <inttypes.h>
#endif
+#include "posixtime.h"
#include "../bashansi.h"
#include "../bashintl.h"
+#define NEED_STRFTIME_DECL
+
#include "../shell.h"
#include "shmbutil.h"
#include "stdc.h"
else if (have_fieldwidth) \
nw = vflag ? vbprintf (f, fieldwidth, func) : printf (f, fieldwidth, func); \
else if (have_precision) \
- nw = vflag ? vbprintf (f, precision, func) : printf (f, fieldwidth, func); \
+ nw = vflag ? vbprintf (f, precision, func) : printf (f, precision, func); \
else \
nw = vflag ? vbprintf (f, func) : printf (f, func); \
tw += nw; \
else if (vbuf) \
vbuf[0] = 0; \
terminate_immediately--; \
- fflush (stdout); \
+ if (ferror (stdout) == 0) \
+ fflush (stdout); \
if (ferror (stdout)) \
{ \
sh_wrerror (); \
#define SKIP1 "#'-+ 0"
#define LENMODS "hjlLtz"
+extern time_t shell_start_time;
+
#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)));
+extern int vsnprintf __P((char *, size_t, const char *, va_list)) __attribute__((__format__ (printf, 3, 0)));
#endif
static void printf_erange __P((char *));
static int printstr __P((char *, char *, int, int, int));
-static int tescape __P((char *, char *, int *));
+static int tescape __P((char *, char *, int *, 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)));
int ch, fieldwidth, precision;
int have_fieldwidth, have_precision;
char convch, thisch, nextch, *format, *modstart, *fmt, *start;
+#if defined (HANDLE_MULTIBYTE)
+ char mbch[25]; /* 25 > MB_LEN_MAX, plus can handle 4-byte UTF-8 and large Unicode characters*/
+ int mbind, mblen;
+#endif
conversion_error = 0;
retval = EXECUTION_SUCCESS;
#endif
{
vflag = 1;
+ if (vbsize == 0)
+ vbuf = xmalloc (vbsize = 16);
vblen = 0;
if (vbuf)
vbuf[0] = 0;
fmt++;
/* A NULL third argument to tescape means to bypass the
special processing for arguments to %b. */
- fmt += tescape (fmt, &nextch, (int *)NULL);
+#if defined (HANDLE_MULTIBYTE)
+ /* Accommodate possible use of \u or \U, which can result in
+ multibyte characters */
+ memset (mbch, '\0', sizeof (mbch));
+ fmt += tescape (fmt, mbch, &mblen, (int *)NULL);
+ for (mbind = 0; mbind < mblen; mbind++)
+ PC (mbch[mbind]);
+#else
+ fmt += tescape (fmt, &nextch, (int *)NULL, (int *)NULL);
PC (nextch);
+#endif
fmt--; /* for loop will increment it for us again */
continue;
}
break;
}
+ case '(':
+ {
+ char *timefmt, timebuf[128], *t;
+ int n;
+ intmax_t arg;
+ time_t secs;
+ struct tm *tm;
+
+ modstart[1] = nextch; /* restore char after left paren */
+ timefmt = xmalloc (strlen (fmt) + 3);
+ fmt++; /* skip over left paren */
+ for (t = timefmt, n = 1; *fmt; )
+ {
+ if (*fmt == '(')
+ n++;
+ else if (*fmt == ')')
+ n--;
+ if (n == 0)
+ break;
+ *t++ = *fmt++;
+ }
+ *t = '\0';
+ if (*++fmt != 'T')
+ {
+ builtin_warning (_("`%c': invalid time format specification"), *fmt);
+ fmt = start;
+ free (timefmt);
+ PC (*fmt);
+ continue;
+ }
+ if (timefmt[0] == '\0')
+ {
+ timefmt[0] = '%';
+ timefmt[1] = 'X'; /* locale-specific current time - should we use `+'? */
+ timefmt[2] = '\0';
+ }
+ /* argument is seconds since the epoch with special -1 and -2 */
+ /* default argument is equivalent to -1; special case */
+ arg = garglist ? getintmax () : -1;
+ if (arg == -1)
+ secs = NOW; /* roughly date +%s */
+ else if (arg == -2)
+ secs = shell_start_time; /* roughly $SECONDS */
+ else
+ secs = arg;
+#if defined (HAVE_TZSET)
+ sv_tz ("TZ"); /* XXX -- just make sure */
+#endif
+ tm = localtime (&secs);
+ if (tm == 0)
+ {
+ secs = 0;
+ tm = localtime (&secs);
+ }
+ n = tm ? strftime (timebuf, sizeof (timebuf), timefmt, tm) : 0;
+ free (timefmt);
+ if (n == 0)
+ timebuf[0] = '\0';
+ else
+ timebuf[sizeof(timebuf) - 1] = '\0';
+ /* convert to %s format that preserves fieldwidth and precision */
+ modstart[0] = 's';
+ modstart[1] = '\0';
+ n = printstr (start, timebuf, strlen (timebuf), fieldwidth, precision); /* XXX - %s for now */
+ if (n < 0)
+ {
+ if (ferror (stdout) == 0)
+ {
+ sh_wrerror ();
+ clearerr (stdout);
+ }
+ PRETURN (EXECUTION_FAILURE);
+ }
+ break;
+ }
+
case 'n':
{
char *var;
r = printstr (start, xp, rlen, fieldwidth, precision);
if (r < 0)
{
- sh_wrerror ();
- clearerr (stdout);
+ if (ferror (stdout) == 0)
+ {
+ sh_wrerror ();
+ clearerr (stdout);
+ }
retval = EXECUTION_FAILURE;
}
free (xp);
else if (ansic_shouldquote (p))
xp = ansic_quote (p, 0, (int *)0);
else
- xp = sh_backslash_quote (p);
+ xp = sh_backslash_quote (p, 0, 1);
if (xp)
{
/* Use printstr to get fieldwidth and precision right. */
if (ferror (stdout))
{
- sh_wrerror ();
- clearerr (stdout);
+ /* PRETURN will print error message. */
PRETURN (EXECUTION_FAILURE);
}
}
#endif
int padlen, nc, ljust, i;
int fw, pr; /* fieldwidth and precision */
+ intmax_t mfw, mpr;
-#if 0
- if (string == 0 || *string == '\0')
-#else
if (string == 0 || len == 0)
-#endif
return 0;
#if 0
ljust = fw = 0;
pr = -1;
+ mfw = 0;
+ mpr = -1;
/* skip flags */
while (strchr (SKIP1, *fmt))
fmt++;
}
- /* get fieldwidth, if present */
+ /* get fieldwidth, if present. rely on caller to clamp fieldwidth at INT_MAX */
if (*fmt == '*')
{
fmt++;
}
else if (DIGIT (*fmt))
{
- fw = *fmt++ - '0';
+ mfw = *fmt++ - '0';
while (DIGIT (*fmt))
- fw = (fw * 10) + (*fmt++ - '0');
+ mfw = (mfw * 10) + (*fmt++ - '0');
+ /* Error if fieldwidth > INT_MAX here? */
+ fw = (mfw < 0 || mfw > INT_MAX) ? INT_MAX : mfw;
}
/* get precision, if present */
}
else if (DIGIT (*fmt))
{
- pr = *fmt++ - '0';
+ mpr = *fmt++ - '0';
while (DIGIT (*fmt))
- pr = (pr * 10) + (*fmt++ - '0');
+ mpr = (mpr * 10) + (*fmt++ - '0');
+ /* Error if precision > INT_MAX here? */
+ pr = (mpr < 0 || mpr > INT_MAX) ? INT_MAX : mpr;
}
}
/* If we remove this, get rid of `s'. */
if (*fmt != 'b' && *fmt != 'q')
{
- internal_error ("format parsing problem: %s", s);
+ internal_error (_("format parsing problem: %s"), s);
fw = pr = 0;
}
#endif
do the \c short-circuiting, and \c is treated as an unrecognized escape
sequence; we also bypass the other processing specific to %b arguments. */
static int
-tescape (estart, cp, sawc)
+tescape (estart, cp, lenp, sawc)
char *estart;
char *cp;
- int *sawc;
+ int *lenp, *sawc;
{
register char *p;
int temp, c, evalue;
+ unsigned long uvalue;
p = estart;
+ if (lenp)
+ *lenp = 1;
switch (c = *p++)
{
*cp = evalue & 0xFF;
break;
- /* And, as another extension, we allow \xNNN, where each N is a
+ /* And, as another extension, we allow \xNN, where each N is a
hex digit. */
case 'x':
-#if 0
- for (evalue = 0; ISXDIGIT ((unsigned char)*p); p++)
-#else
for (temp = 2, evalue = 0; ISXDIGIT ((unsigned char)*p) && temp--; p++)
-#endif
evalue = (evalue * 16) + HEXVALUE (*p);
if (p == estart + 1)
{
*cp = evalue & 0xFF;
break;
+#if defined (HANDLE_MULTIBYTE)
+ case 'u':
+ case 'U':
+ temp = (c == 'u') ? 4 : 8; /* \uNNNN \UNNNNNNNN */
+ for (uvalue = 0; ISXDIGIT ((unsigned char)*p) && temp--; p++)
+ uvalue = (uvalue * 16) + HEXVALUE (*p);
+ if (p == estart + 1)
+ {
+ builtin_error (_("missing unicode digit for \\%c"), c);
+ *cp = '\\';
+ return 0;
+ }
+ if (uvalue <= 0x7f) /* <= 0x7f translates directly */
+ *cp = uvalue;
+ else
+ {
+ temp = u32cconv (uvalue, cp);
+ cp[temp] = '\0';
+ if (lenp)
+ *lenp = temp;
+ }
+ break;
+#endif
+
case '\\': /* \\ -> \ */
*cp = c;
break;
{
int temp;
char *ret, *r, *s, c;
+#if defined (HANDLE_MULTIBYTE)
+ char mbch[25];
+ int mbind, mblen;
+#endif
-#if 0
- if (string == 0 || *string == '\0')
-#else
if (string == 0 || len == 0)
-#endif
{
if (sawc)
*sawc = 0;
continue;
}
temp = 0;
- s += tescape (s, &c, &temp);
+#if defined (HANDLE_MULTIBYTE)
+ memset (mbch, '\0', sizeof (mbch));
+ s += tescape (s, mbch, &mblen, &temp);
+#else
+ s += tescape (s, &c, (int *)NULL, &temp);
+#endif
if (temp)
{
if (sawc)
break;
}
+#if defined (HANDLE_MULTIBYTE)
+ for (mbind = 0; mbind < mblen; mbind++)
+ *r++ = mbch[mbind];
+#else
*r++ = c;
+#endif
}
*r = '\0';
ret = getintmax ();
+ if (garglist == 0)
+ return ret;
+
if (ret > INT_MAX)
{
printf_erange (garglist->word->word);
char *value;
int flags;
{
+ SHELL_VAR *v;
+
#if defined (ARRAY_VARS)
if (valid_array_reference (name) == 0)
- return (bind_variable (name, value, flags));
+ v = bind_variable (name, value, flags);
else
- return (assign_array_element (name, value, flags));
+ v = assign_array_element (name, value, flags);
#else /* !ARRAY_VARS */
- return bind_variable (name, value, flags);
+ v = bind_variable (name, value, flags);
#endif /* !ARRAY_VARS */
+
+ if (v && readonly_p (v) == 0 && noassign_p (v) == 0)
+ VUNSETATTR (v, att_invisible);
+
+ return v;
}