diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | Doc/Zsh/builtins.yo | 18 | ||||
-rw-r--r-- | Src/builtin.c | 174 |
3 files changed, 197 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog index 0cf854663..2dbfd4335 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2001-10-05 Oliver Kiddle <opk@zsh.org> + + * 15851, 15948: Src/builtin.c, Doc/Zsh/builtins.yo: add printf + builtin with basic POSIX functionality + 2001-10-05 Sven Wischnowsky <wischnow@zsh.org> * 15946: Src/Zle/compresult.c: fix calculation of number of diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 479d398c9..eceb3258b 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -716,6 +716,24 @@ ifnzman(noderef(Prompt Expansion))\ ) enditem() ) +findex(printf) +item(tt(printf) var(format) [ var(arg) ... ])( +Print the arguments according to the format specification. Formatting +rules are the same as used in C. The same escape sequences as for tt(echo) +are recognised in the format. All C format specifications ending in one of +csdiouxXeEfgG are handled. In addition to this, `tt(%b)' can be used +instead of `tt(%s)' to cause escape sequences in the argument to be +recognised and `tt(%q)' can be used to quote the argument in such a way +that allows it to be reused as shell input. With the numeric format +specifiers, if the corresponding argument starts with a quote character, +the numeric value of the following character is used as the number to +print. + +If arguments remain unused after formatting, the format string is reused +until all arguments have been consumed. If more arguments are required by +the format than have been specified, the behaviour is as if zero or an +empty string had been specified as the argument. +) findex(pushd) pindex(PUSHD_TO_HOME, use of) pindex(PUSHD_MINUS, use of) diff --git a/Src/builtin.c b/Src/builtin.c index 9f5afc32e..6db40006a 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -92,6 +92,7 @@ static struct builtin builtins[] = BUILTIN("popd", 0, bin_cd, 0, 2, BIN_POPD, NULL, NULL), BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "RDPbnrslzNu0123456789pioOcm-", NULL), + BUILTIN("printf", 0, bin_printf, 1, -1, 0, NULL, NULL), BUILTIN("pushd", 0, bin_cd, 0, 2, BIN_PUSHD, NULL, NULL), BUILTIN("pushln", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, NULL, "-nz"), BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL), @@ -3053,6 +3054,179 @@ bin_print(char *name, char **args, char *ops, int func) return 0; } +/* printf */ + +#define print_val(VAL) \ + if (width >= 0) { \ + if (prec >= 0) \ + printf(start, width, prec, VAL); \ + else \ + printf(start, width, VAL); \ + } else { \ + if (prec >= 0) \ + printf(start, prec, VAL); \ + else \ + printf(start, VAL); \ + } + +/**/ +int +bin_printf(char *name, char **args, char *ops, int func) +{ + int len, nnl, width, prec, type, ret = 0; + char *start, *endptr, *c, *fmt = getkeystring(*args, &len, 0, &nnl); + char **first = ++args, nullstr = '\0', save = '\0'; + + double doubleval; + int intval; + unsigned int uintval; + char *stringval; + + do { + + for (c = fmt;c-fmt < len;c++) { + type = prec = width = -1; + + if (*c != '%') { + putchar(*c); + continue; + } + + start = c++; + if (*c == '%') { + putchar('%'); + continue; + } + + if (strchr("+- #", *c)) c++; + + if (*c == '*') { + width = (*args) ? strtoul(*args++, NULL, 0) : 0; + c++; + } else { + while (idigit(*c)) c++; + } + + if (*c == '.') { + c++; + if (*c == '*') { + prec = (*args) ? strtoul(*args++, NULL, 0) : 0; + c++; + } else { + while (idigit(*c)) c++; + } + } + + if (*c == 'l' || *c == 'L' || *c == 'h') c++; + + if (*c) { + save = c[1]; + c[1] = '\0'; + } + switch (*c) { + case 'c': + if (*args) { + if (**args == Meta) + intval = (*args)[1] ^ 32; + else + intval = **args; + args++; + } else + intval = 0; + print_val(intval); + break; + case 's': + if (*args) + stringval = unmetafy(*args++, NULL); + else + stringval = &nullstr; + print_val(stringval); + break; + case 'b': + if (*args) { + int l; + char *b = getkeystring(*args++, &l, 0, &nnl); + fwrite(b, l, 1, stdout); + } + continue; + case 'q': + if (*args) + stringval = bslashquote(unmetafy(*args++, NULL), NULL, 0); + else + stringval = &nullstr; + *c = 's'; + print_val(stringval); + break; + case 'd': + case 'i': + type=1; + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + type=2; + break; + case 'o': + case 'u': + case 'x': + case 'X': + type=3; + break; + default: + zerrnam(name, "%s: invalid directive", start, 0); + ret = 1; + } + + if (type > 0) { + if (*args && (**args == '\'' || **args == '"' )) { + if (type == 2) { + doubleval = (*args)[1]; + print_val(doubleval); + } else { + intval = (*args)[1]; + print_val(intval); + } + args++; + } else { + switch (type) { + case 1: + intval = (*args) ? strtol(*args, &endptr, 0) : 0; + print_val(intval); + break; + case 2: + doubleval = (*args) ? strtod(*args, &endptr) : 0; + print_val(doubleval); + break; + case 3: + uintval = (*args) ? strtoul(*args, &endptr, 0) : 0; + print_val(uintval); + } + if (*args) { + if (errno == ERANGE) { + zerrnam(name, "`%s' arithmetic overflow", *args, 0); + ret = 1; + } else if (**args && endptr == *args) { + zerrnam(name, "`%s' expected numeric value", endptr, 0); + ret = 1; + } else if (*endptr) { + zerrnam(name, "`%s' not completely converted", *args, 0); + ret = 1; + } + args++; + } + } + } + if (*c) c[1] = save; + } + + /* if there are remaining args, reuse format string */ + } while (*args && args != first); + + return ret; +} + /* shift builtin */ /**/ |