This file is printf.def, from which is created printf.c. It implements the builtin "printf" in Bash. Copyright (C) 1997-2010 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 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. You should have received a copy of the GNU General Public License along with Bash. If not, see . $PRODUCES printf.c $BUILTIN printf $FUNCTION printf_builtin $SHORT_DOC printf [-v var] format [arguments] 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 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 %(fmt)T output the date-time string resulting from using FMT as a format string for strftime(3) Exit Status: Returns success unless an invalid option is given or a write or assignment error occurs. $END #include #include "../bashtypes.h" #include #if defined (HAVE_LIMITS_H) # include #else /* Assume 32-bit ints. */ # define INT_MAX 2147483647 # define INT_MIN (-2147483647-1) #endif #if defined (PREFER_STDARG) # include #else # include #endif #include #include #ifdef HAVE_INTTYPES_H # include #endif #include "posixtime.h" #include "../bashansi.h" #include "../bashintl.h" #define NEED_STRFTIME_DECL #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" # else # define PRIdMAX "ld" # endif #endif #if !defined (errno) extern int errno; #endif #define PC(c) \ do { \ char b[2]; \ tw++; \ b[0] = c; b[1] = '\0'; \ if (vflag) \ vbadd (b, 1); \ else \ putchar (c); \ } while (0) #define PF(f, func) \ do { \ int nw; \ clearerr (stdout); \ if (have_fieldwidth && have_precision) \ nw = vflag ? vbprintf (f, fieldwidth, precision, func) : printf (f, fieldwidth, precision, func); \ 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, precision, func); \ else \ nw = vflag ? vbprintf (f, func) : printf (f, func); \ tw += nw; \ if (ferror (stdout)) \ { \ sh_wrerror (); \ clearerr (stdout); \ return (EXECUTION_FAILURE); \ } \ } while (0) /* We free the buffer used by mklong() if it's `too big'. */ #define PRETURN(value) \ do \ { \ if (vflag) \ { \ bind_printf_variable (vname, vbuf, 0); \ stupidly_hack_special_variables (vname); \ } \ if (conv_bufsize > 4096 ) \ { \ free (conv_buf); \ conv_bufsize = 0; \ conv_buf = 0; \ } \ if (vbsize > 4096) \ { \ free (vbuf); \ vbsize = 0; \ vbuf = 0; \ } \ else if (vbuf) \ vbuf[0] = 0; \ terminate_immediately--; \ fflush (stdout); \ if (ferror (stdout)) \ { \ sh_wrerror (); \ clearerr (stdout); \ return (EXECUTION_FAILURE); \ } \ return (value); \ } \ while (0) #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 *, 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 *, 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; # 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 intmax_t asciicode __P((void)); static WORD_LIST *garglist; static int retval; static int conversion_error; /* printf -v var support */ static int vflag = 0; static char *vbuf, *vname; static size_t vbsize; static int vblen; static intmax_t tw; static char *conv_buf; static size_t conv_bufsize; int printf_builtin (list) WORD_LIST *list; { 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; vflag = 0; reset_internal_getopt (); while ((ch = internal_getopt (list, "v:")) != -1) { switch (ch) { case 'v': 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 { sh_invalidid (vname); return (EX_USAGE); } break; default: builtin_usage (); return (EX_USAGE); } } list = loptend; /* skip over possible `--' */ if (list == 0) { builtin_usage (); return (EX_USAGE); } if (list->word->word == 0 || list->word->word[0] == '\0') return (EXECUTION_SUCCESS); format = list->word->word; tw = 0; garglist = list->next; /* 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 width or precision is a '*'; if it is, gather up value. Note, format strings are reused as necessary to use up the provided arguments, arguments of zero/null string are provided to use up the format string. */ do { tw = 0; /* find next format specification */ for (fmt = format; *fmt; fmt++) { precision = fieldwidth = 0; have_fieldwidth = have_precision = 0; if (*fmt == '\\') { fmt++; /* A NULL third argument to tescape means to bypass the special processing for arguments to %b. */ #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; } if (*fmt != '%') { PC (*fmt); continue; } /* ASSERT(*fmt == '%') */ start = fmt++; if (*fmt == '%') /* %% prints a % */ { PC ('%'); continue; } /* found format specification, skip to field width */ for (; *fmt && strchr(SKIP1, *fmt); ++fmt) ; /* Skip optional field width. */ if (*fmt == '*') { fmt++; have_fieldwidth = 1; fieldwidth = getint (); } else while (DIGIT (*fmt)) fmt++; /* Skip optional '.' and precision */ if (*fmt == '.') { ++fmt; if (*fmt == '*') { fmt++; have_precision = 1; precision = getint (); } else { /* Negative precisions are allowed but treated as if the precision were missing; I would like to allow a leading `+' in the precision number as an extension, but lots of asprintf/fprintf implementations get this wrong. */ #if 0 if (*fmt == '-' || *fmt == '+') #else if (*fmt == '-') #endif fmt++; while (DIGIT (*fmt)) fmt++; } } /* skip possible format modifiers */ modstart = fmt; while (*fmt && strchr (LENMODS, *fmt)) fmt++; if (*fmt == 0) { builtin_error (_("`%s': missing format character"), start); PRETURN (EXECUTION_FAILURE); } convch = *fmt; thisch = modstart[0]; nextch = modstart[1]; modstart[0] = convch; modstart[1] = '\0'; switch(convch) { case 'c': { char p; p = getchr (); PF(start, p); break; } case 's': { char *p; p = getstr (); PF(start, p); 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 */ arg = getintmax (); if (arg == -1) secs = NOW; /* roughly date +%s */ else if (arg == -2) secs = shell_start_time; /* roughly $SECONDS */ else secs = arg; tm = localtime (&secs); n = strftime (timebuf, sizeof (timebuf), timefmt, tm); 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) { sh_wrerror (); clearerr (stdout); PRETURN (EXECUTION_FAILURE); } break; } case 'n': { char *var; var = getstr (); if (var && *var) { if (legal_identifier (var)) bind_var_to_int (var, tw); else { sh_invalidid (var); PRETURN (EXECUTION_FAILURE); } } break; } case 'b': /* expand escapes in argument */ { char *p, *xp; int rlen, r; p = getstr (); ch = rlen = r = 0; xp = bexpand (p, strlen (p), &ch, &rlen); if (xp) { /* Have to use printstr because of possible NUL bytes in XP -- printf does not handle that well. */ r = printstr (start, xp, rlen, fieldwidth, precision); if (r < 0) { sh_wrerror (); clearerr (stdout); retval = EXECUTION_FAILURE; } free (xp); } if (ch || r < 0) PRETURN (retval); break; } case 'q': /* print with shell quoting */ { char *p, *xp; int r; r = 0; p = getstr (); if (p && *p == 0) /* XXX - getstr never returns null */ xp = savestring ("''"); else 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. */ r = printstr (start, xp, strlen (xp), fieldwidth, precision); if (r < 0) { sh_wrerror (); clearerr (stdout); } free (xp); } if (r < 0) PRETURN (EXECUTION_FAILURE); break; } case 'd': case 'i': { char *f; long p; intmax_t pp; p = pp = getintmax (); if (p != pp) { f = mklong (start, PRIdMAX, sizeof (PRIdMAX) - 2); PF (f, pp); } else { /* 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 'o': case 'u': case 'x': case 'X': { char *f; unsigned long p; uintmax_t pp; p = pp = getuintmax (); if (p != pp) { f = mklong (start, PRIdMAX, sizeof (PRIdMAX) - 2); PF (f, pp); } else { f = mklong (start, "l", 1); PF (f, p); } break; } case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': #if defined (HAVE_PRINTF_A_FORMAT) case 'a': case 'A': #endif { char *f; floatmax_t p; p = getfloatmax (); f = mklong (start, FLOATMAX_CONV, sizeof(FLOATMAX_CONV) - 1); PF (f, p); break; } /* We don't output unrecognized format characters; we print an error message and return a failure exit status. */ default: builtin_error (_("`%c': invalid format character"), convch); PRETURN (EXECUTION_FAILURE); } modstart[0] = thisch; modstart[1] = nextch; } if (ferror (stdout)) { sh_wrerror (); clearerr (stdout); PRETURN (EXECUTION_FAILURE); } } while (garglist && garglist != list->next); if (conversion_error) retval = EXECUTION_FAILURE; 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 int printstr (fmt, string, len, fieldwidth, precision) char *fmt; /* format */ char *string; /* expanded string argument */ int len; /* length of expanded string */ int fieldwidth; /* argument for width of `*' */ int precision; /* argument for precision of `*' */ { #if 0 char *s; #endif int padlen, nc, ljust, i; int fw, pr; /* fieldwidth and precision */ #if 0 if (string == 0 || *string == '\0') #else if (string == 0 || len == 0) #endif return 0; #if 0 s = fmt; #endif if (*fmt == '%') fmt++; ljust = fw = 0; pr = -1; /* skip flags */ while (strchr (SKIP1, *fmt)) { if (*fmt == '-') ljust = 1; fmt++; } /* get fieldwidth, if present */ if (*fmt == '*') { fmt++; fw = fieldwidth; if (fw < 0) { fw = -fw; ljust = 1; } } else if (DIGIT (*fmt)) { fw = *fmt++ - '0'; while (DIGIT (*fmt)) fw = (fw * 10) + (*fmt++ - '0'); } /* get precision, if present */ if (*fmt == '.') { fmt++; if (*fmt == '*') { fmt++; pr = precision; } else if (DIGIT (*fmt)) { pr = *fmt++ - '0'; while (DIGIT (*fmt)) pr = (pr * 10) + (*fmt++ - '0'); } } #if 0 /* If we remove this, get rid of `s'. */ if (*fmt != 'b' && *fmt != 'q') { internal_error ("format parsing problem: %s", s); fw = pr = 0; } #endif /* chars from string to print */ nc = (pr >= 0 && pr <= len) ? pr : len; padlen = fw - nc; if (padlen < 0) padlen = 0; if (ljust) padlen = -padlen; /* leading pad characters */ for (; padlen > 0; padlen--) PC (' '); /* output NC characters from STRING */ for (i = 0; i < nc; i++) PC (string[i]); /* output any necessary trailing padding */ for (; padlen < 0; padlen++) PC (' '); return (ferror (stdout) ? -1 : 0); } /* Convert STRING by expanding the escape sequences specified by the POSIX standard for printf's `%b' format string. If SAWC is non-null, perform the processing appropriate for %b arguments. In particular, recognize `\c' and use that as a string terminator. If we see \c, set *SAWC to 1 before returning. LEN is the length of STRING. */ /* Translate a single backslash-escape sequence starting at ESTART (the character after the backslash) and return the number of characters consumed by the sequence. CP is the place to return the translated value. *SAWC is set to 1 if the escape sequence was \c, since that means to short-circuit the rest of the processing. If SAWC is null, we don't 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, lenp, sawc) char *estart; char *cp; int *lenp, *sawc; { register char *p; int temp, c, evalue; unsigned long uvalue; p = estart; if (lenp) *lenp = 1; switch (c = *p++) { #if defined (__STDC__) case 'a': *cp = '\a'; break; #else case 'a': *cp = '\007'; break; #endif case 'b': *cp = '\b'; break; case 'e': case 'E': *cp = '\033'; break; /* ESC -- non-ANSI */ case 'f': *cp = '\f'; break; case 'n': *cp = '\n'; break; case 'r': *cp = '\r'; break; case 't': *cp = '\t'; break; case 'v': *cp = '\v'; break; /* The octal escape sequences are `\0' followed by up to three octal digits (if SAWC), or `\' followed by up to three octal digits (if !SAWC). As an extension, we allow the latter form even if SAWC. */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': evalue = OCTVALUE (c); for (temp = 2 + (!evalue && !!sawc); ISOCTAL (*p) && temp--; p++) evalue = (evalue * 8) + OCTVALUE (*p); *cp = evalue & 0xFF; break; /* And, as another extension, we allow \xNN, where each N is a hex digit. */ case 'x': for (temp = 2, evalue = 0; ISXDIGIT ((unsigned char)*p) && temp--; p++) evalue = (evalue * 16) + HEXVALUE (*p); if (p == estart + 1) { builtin_error (_("missing hex digit for \\x")); *cp = '\\'; return 0; } *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 <= UCHAR_MAX) *cp = uvalue; else { temp = u32cconv (uvalue, cp); cp[temp] = '\0'; if (lenp) *lenp = temp; } break; #endif case '\\': /* \\ -> \ */ *cp = c; break; /* SAWC == 0 means that \', \", and \? are recognized as escape sequences, though the only processing performed is backslash removal. */ case '\'': case '"': case '?': if (!sawc) *cp = c; else { *cp = '\\'; return 0; } break; case 'c': if (sawc) { *sawc = 1; break; } /* other backslash escapes are passed through unaltered */ default: *cp = '\\'; return 0; } return (p - estart); } static char * bexpand (string, len, sawc, lenp) char *string; int len, *sawc, *lenp; { int temp; char *ret, *r, *s, c; #if defined (HANDLE_MULTIBYTE) char mbch[25]; int mbind, mblen; #endif if (string == 0 || len == 0) { if (sawc) *sawc = 0; if (lenp) *lenp = 0; return ((char *)NULL); } ret = (char *)xmalloc (len + 1); for (r = ret, s = string; s && *s; ) { c = *s++; if (c != '\\' || *s == '\0') { *r++ = c; continue; } temp = 0; #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) *sawc = 1; break; } #if defined (HANDLE_MULTIBYTE) for (mbind = 0; mbind < mblen; mbind++) *r++ = mbch[mbind]; #else *r++ = c; #endif } *r = '\0'; if (lenp) *lenp = r - ret; return ret; } static char * vbadd (buf, blen) char *buf; int blen; { size_t nlen; nlen = vblen + blen + 1; if (nlen >= vbsize) { vbsize = ((nlen + 63) >> 6) << 6; vbuf = (char *)xrealloc (vbuf, vbsize); } if (blen == 1) vbuf[vblen++] = buf[0]; else if (blen > 1) { FASTCOPY (buf, vbuf + vblen, blen); 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 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; char *modifiers; size_t mlen; { size_t len, slen; slen = strlen (str); len = slen + mlen + 1; if (len > conv_bufsize) { conv_bufsize = (((len + 1023) >> 10) << 10); conv_buf = (char *)xrealloc (conv_buf, conv_bufsize); } FASTCOPY (str, conv_buf, slen - 1); FASTCOPY (modifiers, conv_buf + slen - 1, mlen); conv_buf[len - 2] = str[slen - 1]; conv_buf[len - 1] = '\0'; return (conv_buf); } static int getchr () { int ret; if (garglist == 0) return ('\0'); ret = (int)garglist->word->word[0]; garglist = garglist->next; return ret; } static char * getstr () { char *ret; if (garglist == 0) return (""); ret = garglist->word->word; garglist = garglist->next; return ret; } static int getint () { intmax_t ret; ret = getintmax (); if (ret > INT_MAX) { printf_erange (garglist->word->word); ret = INT_MAX; } else if (ret < INT_MIN) { printf_erange (garglist->word->word); ret = INT_MIN; } return ((int)ret); } static intmax_t getintmax () { intmax_t ret; char *ep; if (garglist == 0) return (0); if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"') return asciicode (); errno = 0; ret = strtoimax (garglist->word->word, &ep, 0); if (*ep) { 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 value accumulated at the time the error was detected to standard output.'' Yecch. */ #if 0 ret = 0; /* return partially-converted value from strtoimax */ #endif conversion_error = 1; } else if (errno == ERANGE) printf_erange (garglist->word->word); garglist = garglist->next; return (ret); } static uintmax_t getuintmax () { uintmax_t ret; char *ep; if (garglist == 0) return (0); if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"') return asciicode (); errno = 0; ret = strtoumax (garglist->word->word, &ep, 0); if (*ep) { sh_invalidnum (garglist->word->word); /* Same POSIX.2 conversion error requirements as getintmax(). */ ret = 0; conversion_error = 1; } else if (errno == ERANGE) printf_erange (garglist->word->word); garglist = garglist->next; return (ret); } static floatmax_t getfloatmax () { floatmax_t ret; char *ep; if (garglist == 0) return (0); if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"') return asciicode (); errno = 0; ret = strtofltmax (garglist->word->word, &ep); if (*ep) { sh_invalidnum (garglist->word->word); /* Same thing about POSIX.2 conversion error requirements. */ ret = 0; conversion_error = 1; } else if (errno == ERANGE) printf_erange (garglist->word->word); garglist = garglist->next; return (ret); } /* NO check is needed for garglist here. */ static intmax_t asciicode () { 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]; 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 */ }