📄 printf.def
字号:
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 modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong 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]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 outputFORMAT is a character string which contains three types of objects: plaincharacters, which are simply copied to standard output; character escapesequences, which are converted and copied to the standard output; andformat specifications, each of which causes printing of the next successiveargument.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 assignmenterror occurs.$END#include <config.h>#include "../bashtypes.h"#include <errno.h>#if defined (HAVE_LIMITS_H)# include <limits.h>#else /* Assume 32-bit ints. */# define INT_MAX 2147483647# define INT_MIN (-2147483647-1)#endif#if defined (PREFER_STDARG)# include <stdarg.h>#else# include <varargs.h>#endif#include <stdio.h>#include <chartypes.h>#ifdef HAVE_INTTYPES_H# 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"#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_ASPRINTFextern int asprintf __P((char **, const char *, ...)) __attribute__((__format__ (printf, 2, 3)));#endif#if !HAVE_VSNPRINTFextern int vsnprintf __P((char *, size_t, const char *, va_list)) __attribute__((__format__ (printf, 3, 0)));#endifstatic 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#elsetypedef double floatmax_t;# define FLOATMAX_CONV ""# define strtofltmax strtod#endifstatic 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;intprintf_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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -