mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-09-20 10:53:29 +02:00
add print -f option, %n format specifier and tests for print/printf (15973)
This commit is contained in:
parent
0ca8bb74f0
commit
648d1c2756
@ -1,3 +1,10 @@
|
||||
2001-10-08 Oliver Kiddle <opk@zsh.org>
|
||||
|
||||
* 15973: Completion/Zsh/Command/_print, Doc/Zsh/builtins.yo,
|
||||
Src/builtin.c, Src/hashtable.h, Test/.distfiles, Test/B03print.ztst:
|
||||
merge bin_printf and bin_print to allow print -f option and add
|
||||
%n format specifier and tests
|
||||
|
||||
2001-10-08 Peter Stephenson <pws@csr.com>
|
||||
|
||||
* 15965: Src/exec.c, Src/glob.c, Src/subst.c: rename glob() to
|
||||
|
@ -10,18 +10,19 @@ eflag="${words[1,CURRENT-1][(r)-*R*]:+-e[enable escapes]}"
|
||||
pflag='(-s -u -z)-p[print arguments to input of coprocess]'
|
||||
|
||||
_arguments -C -s -A "-*" -S \
|
||||
'-r[ignore escape conventions of echo]' \
|
||||
'(-r -b -m -s -l -N -o -O -i -c -u -p -z -D -P)-R[emulate BSD echo (no escapes, -n & -e flags only)]' \
|
||||
'(-f)-r[ignore escape conventions of echo]' \
|
||||
'(-r -b -f -m -s -l -N -o -O -i -c -u -p -z -D -P)-R[emulate BSD echo (no escapes, -n & -e flags only)]' \
|
||||
'-b[recognise bindkey escape sequences]' \
|
||||
'-m[remove arguments matching specified pattern]' \
|
||||
'(-r -n -R -l -N -c)-f[print arguments as for the printf builtin]:format' \
|
||||
'(-u -p -z)-s[place results in the history list]' \
|
||||
'(-N -c)-n[do not add a newline to the result]' \
|
||||
'(-N -c)-l[print arguments separated by newlines]' \
|
||||
'(-n -l -c)-N[print arguments separated and terminated by nulls]' \
|
||||
'(-c -f)-n[do not add a newline to the result]' \
|
||||
'(-N -c -f)-l[print arguments separated by newlines]' \
|
||||
'(-n -l -c -f)-N[print arguments separated and terminated by nulls]' \
|
||||
'(-O)-o[sort arguments in ascending order]' \
|
||||
'(-o)-O[sort arguments in descending order]' \
|
||||
'-i[case-insensitive sorting]' \
|
||||
'(-n -l -N)-c[print arguments in columns]' \
|
||||
'(-n -l -N -f)-c[print arguments in columns]' \
|
||||
'(-s -p -z)-u+[specify file-descriptor to print arguments to]:file-descriptor:_file_descriptors' \
|
||||
'(-s -p -u)-z[push arguments onto editing buffer stack]' \
|
||||
'-D[substitute any arguments which are named directories using ~ notation]' \
|
||||
|
@ -638,7 +638,8 @@ If the tt(PUSHD_MINUS) option is set, the meanings of `tt(PLUS())' and
|
||||
`tt(-)' in this context are swapped.
|
||||
)
|
||||
findex(print)
|
||||
item(tt(print) [ tt(-bnrslzpNDPoOicm) ] [ tt(-u)var(n) ] [ tt(-R) [ tt(-en) ]] [ var(arg) ... ])(
|
||||
item(tt(print) [ tt(-bnrslzpNDPoOicm) ] [ tt(-u)var(n) ] [ tt(-f) var(format) ] [ tt(-R) [ tt(-en) ]] [ var(arg) ... ])(
|
||||
With the `tt(-f)' option the arguments are printed as described by tt(printf).
|
||||
With no flags or with flag `tt(-)', the arguments are printed on
|
||||
the standard output as described by tt(echo), with the following differences:
|
||||
the escape sequence `tt(\M-)var(x)' metafies the character
|
||||
@ -715,19 +716,24 @@ ifnzman(noderef(Prompt Expansion))\
|
||||
).
|
||||
)
|
||||
enditem()
|
||||
|
||||
If any of `tt(-m)', `tt(-o)' or `tt(-O)' are used in combination with
|
||||
`tt(-f)' and there are no arguments (after the removal process in the
|
||||
case of `tt(-m)') then nothing is printed.
|
||||
)
|
||||
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
|
||||
csdiouxXeEfgGn 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.
|
||||
print. With `tt(%n)', the corresponding argument is taken as an identifier
|
||||
which is created as an integer parameter.
|
||||
|
||||
If arguments remain unused after formatting, the format string is reused
|
||||
until all arguments have been consumed. If more arguments are required by
|
||||
|
171
Src/builtin.c
171
Src/builtin.c
@ -91,8 +91,8 @@ static struct builtin builtins[] =
|
||||
#endif
|
||||
|
||||
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("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "RDPbnrsflzNu0123456789pioOcm-", NULL),
|
||||
BUILTIN("printf", 0, bin_print, 1, -1, BIN_PRINTF, 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),
|
||||
@ -296,10 +296,19 @@ execbuiltin(LinkList args, Builtin bn)
|
||||
}
|
||||
arg = (char *) ugetnode(args);
|
||||
/* for the "print" builtin, the options after -R are treated as
|
||||
options to "echo" */
|
||||
if ((flags & BINF_PRINTOPTS) && ops['R']) {
|
||||
optstr = "ne";
|
||||
flags |= BINF_ECHOPTS;
|
||||
options to "echo" and -f takes an extra argument */
|
||||
if (flags & BINF_PRINTOPTS) {
|
||||
if (ops['R'] && !ops['f']) {
|
||||
optstr = "ne";
|
||||
flags |= BINF_ECHOPTS;
|
||||
} else if (execop == 'f') {
|
||||
if (!arg) {
|
||||
zwarnnam(name, "-f: format argument expected", NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
auxdata = arg;
|
||||
arg = (char *) ugetnode(args);
|
||||
}
|
||||
}
|
||||
/* the option -- indicates the end of the options */
|
||||
if (ops['-'])
|
||||
@ -2871,17 +2880,42 @@ bin_false(char *name, char **argv, char *ops, int func)
|
||||
/**/
|
||||
mod_export LinkList bufstack;
|
||||
|
||||
/* echo, print, pushln */
|
||||
/* echo, print, printf, pushln */
|
||||
|
||||
#define print_val(VAL) \
|
||||
if (width >= 0) { \
|
||||
if (prec >= 0) \
|
||||
count += fprintf(fout, start, width, prec, VAL); \
|
||||
else \
|
||||
count += fprintf(fout, start, width, VAL); \
|
||||
} else { \
|
||||
if (prec >= 0) \
|
||||
count += fprintf(fout, start, prec, VAL); \
|
||||
else \
|
||||
count += fprintf(fout, start, VAL); \
|
||||
}
|
||||
|
||||
/**/
|
||||
int
|
||||
bin_print(char *name, char **args, char *ops, int func)
|
||||
{
|
||||
int nnl = 0, fd, argc, n;
|
||||
int flen, width, prec, type, argc, n, nnl = 0, ret = 0;
|
||||
int *len;
|
||||
Histent ent;
|
||||
char *start, *endptr, *c, *fmt = NULL;
|
||||
char **first, nullstr = '\0', save = '\0';
|
||||
zlong count = 0;
|
||||
FILE *fout = stdout;
|
||||
|
||||
double doubleval;
|
||||
int intval;
|
||||
unsigned int uintval;
|
||||
char *stringval;
|
||||
|
||||
if (func == BIN_PRINTF) auxdata = *args++;
|
||||
if (auxdata)
|
||||
fmt = getkeystring(auxdata, &flen, ops['b'] ? 2 : 0, &nnl);
|
||||
first = args;
|
||||
|
||||
/* -m option -- treat the first argument as a pattern and remove
|
||||
* arguments not matching */
|
||||
if (ops['m']) {
|
||||
@ -2894,16 +2928,19 @@ bin_print(char *name, char **args, char *ops, int func)
|
||||
zwarnnam(name, "bad pattern : %s", *args, 0);
|
||||
return 1;
|
||||
}
|
||||
for (p = ++args; *p; p++)
|
||||
if (!pattry(pprog, *p))
|
||||
for (t = p--; (*t = t[1]); t++);
|
||||
for (t = p = ++args; *p; p++)
|
||||
if (pattry(pprog, *p))
|
||||
*t++ = *p;
|
||||
*t = NULL;
|
||||
first = args;
|
||||
if (fmt && !*args) return 0;
|
||||
}
|
||||
/* compute lengths, and interpret according to -P, -D, -e, etc. */
|
||||
argc = arrlen(args);
|
||||
len = (int *) hcalloc(argc * sizeof(int));
|
||||
for(n = 0; n < argc; n++) {
|
||||
/* first \ sequences */
|
||||
if (!ops['e'] && (ops['R'] || ops['r'] || ops['E']))
|
||||
if (fmt || !ops['e'] && (ops['R'] || ops['r'] || ops['E']))
|
||||
unmetafy(args[n], &len[n]);
|
||||
else
|
||||
args[n] = getkeystring(args[n], &len[n], ops['b'] ? 2 :
|
||||
@ -2948,6 +2985,7 @@ bin_print(char *name, char **args, char *ops, int func)
|
||||
if (ops['s']) {
|
||||
int nwords = 0, nlen, iwords;
|
||||
char **pargs = args;
|
||||
Histent ent;
|
||||
|
||||
queue_signals();
|
||||
ent = prepnexthistent();
|
||||
@ -2971,8 +3009,11 @@ bin_print(char *name, char **args, char *ops, int func)
|
||||
unqueue_signals();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -u and -p -- output to other than standard output */
|
||||
if (ops['u'] || ops['p']) {
|
||||
int fd;
|
||||
|
||||
if (ops['u']) {
|
||||
for (fd = 0; fd < 10; fd++)
|
||||
if (ops[fd + '0'])
|
||||
@ -2993,15 +3034,15 @@ bin_print(char *name, char **args, char *ops, int func)
|
||||
|
||||
/* -o and -O -- sort the arguments */
|
||||
if (ops['o']) {
|
||||
if (fmt && !*args) return 0;
|
||||
if (ops['i'])
|
||||
qsort(args, arrlen(args), sizeof(char *), cstrpcmp);
|
||||
|
||||
else
|
||||
qsort(args, arrlen(args), sizeof(char *), strpcmp);
|
||||
} else if (ops['O']) {
|
||||
if (fmt && !*args) return 0;
|
||||
if (ops['i'])
|
||||
qsort(args, arrlen(args), sizeof(char *), invcstrpcmp);
|
||||
|
||||
else
|
||||
qsort(args, arrlen(args), sizeof(char *), invstrpcmp);
|
||||
}
|
||||
@ -3041,62 +3082,37 @@ bin_print(char *name, char **args, char *ops, int func)
|
||||
fclose(fout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* normal output */
|
||||
for (; *args; args++, len++) {
|
||||
fwrite(*args, *len, 1, fout);
|
||||
if (args[1])
|
||||
fputc(ops['l'] ? '\n' : ops['N'] ? '\0' : ' ', fout);
|
||||
if (!fmt) {
|
||||
for (; *args; args++, len++) {
|
||||
fwrite(*args, *len, 1, fout);
|
||||
if (args[1])
|
||||
fputc(ops['l'] ? '\n' : ops['N'] ? '\0' : ' ', fout);
|
||||
}
|
||||
if (!(ops['n'] || nnl))
|
||||
fputc(ops['N'] ? '\0' : '\n', fout);
|
||||
if (fout != stdout)
|
||||
fclose(fout);
|
||||
return 0;
|
||||
}
|
||||
if (!(ops['n'] || nnl))
|
||||
fputc(ops['N'] ? '\0' : '\n', fout);
|
||||
if (fout != stdout)
|
||||
fclose(fout);
|
||||
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;
|
||||
|
||||
|
||||
/* printf style output */
|
||||
do {
|
||||
|
||||
for (c = fmt;c-fmt < len;c++) {
|
||||
type = prec = width = -1;
|
||||
|
||||
for (c = fmt;c-fmt < flen;c++) {
|
||||
if (*c != '%') {
|
||||
putchar(*c);
|
||||
putc(*c, fout);
|
||||
++count;
|
||||
continue;
|
||||
}
|
||||
|
||||
start = c++;
|
||||
if (*c == '%') {
|
||||
putchar('%');
|
||||
++count;
|
||||
continue;
|
||||
}
|
||||
type = prec = width = -1;
|
||||
|
||||
if (strchr("+- #", *c)) c++;
|
||||
|
||||
@ -3126,34 +3142,26 @@ bin_printf(char *name, char **args, char *ops, int func)
|
||||
switch (*c) {
|
||||
case 'c':
|
||||
if (*args) {
|
||||
if (**args == Meta)
|
||||
intval = (*args)[1] ^ 32;
|
||||
else
|
||||
intval = **args;
|
||||
intval = **args;
|
||||
args++;
|
||||
} else
|
||||
intval = 0;
|
||||
print_val(intval);
|
||||
break;
|
||||
case 's':
|
||||
if (*args)
|
||||
stringval = unmetafy(*args++, NULL);
|
||||
else
|
||||
stringval = &nullstr;
|
||||
stringval = *args ? *args++ : &nullstr;
|
||||
print_val(stringval);
|
||||
break;
|
||||
case 'b':
|
||||
if (*args) {
|
||||
int l;
|
||||
char *b = getkeystring(*args++, &l, 0, &nnl);
|
||||
fwrite(b, l, 1, stdout);
|
||||
char *b = getkeystring(*args++, &l, ops['b'] ? 2 : 0, &nnl);
|
||||
fwrite(b, l, 1, fout);
|
||||
count += l;
|
||||
}
|
||||
continue;
|
||||
break;
|
||||
case 'q':
|
||||
if (*args)
|
||||
stringval = bslashquote(unmetafy(*args++, NULL), NULL, 0);
|
||||
else
|
||||
stringval = &nullstr;
|
||||
stringval = *args ? bslashquote(*args++, NULL, 0) : &nullstr;
|
||||
*c = 's';
|
||||
print_val(stringval);
|
||||
break;
|
||||
@ -3174,6 +3182,9 @@ bin_printf(char *name, char **args, char *ops, int func)
|
||||
case 'X':
|
||||
type=3;
|
||||
break;
|
||||
case 'n':
|
||||
if (*args) setiparam(*args++, count);
|
||||
break;
|
||||
default:
|
||||
zerrnam(name, "%s: invalid directive", start, 0);
|
||||
ret = 1;
|
||||
@ -3185,12 +3196,12 @@ bin_printf(char *name, char **args, char *ops, int func)
|
||||
doubleval = (*args)[1];
|
||||
print_val(doubleval);
|
||||
} else {
|
||||
intval = (*args)[1];
|
||||
intval = (*args)[1];
|
||||
print_val(intval);
|
||||
}
|
||||
args++;
|
||||
} else {
|
||||
switch (type) {
|
||||
switch (type) {
|
||||
case 1:
|
||||
intval = (*args) ? strtol(*args, &endptr, 0) : 0;
|
||||
print_val(intval);
|
||||
@ -3224,6 +3235,8 @@ bin_printf(char *name, char **args, char *ops, int func)
|
||||
/* if there are remaining args, reuse format string */
|
||||
} while (*args && args != first);
|
||||
|
||||
if (fout != stdout)
|
||||
fclose(fout);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -3429,16 +3442,12 @@ bin_break(char *name, char **argv, char *ops, int func)
|
||||
}
|
||||
/*FALLTHROUGH*/
|
||||
case BIN_EXIT:
|
||||
if (locallevel > forklevel) {
|
||||
if (locallevel) {
|
||||
/*
|
||||
* We don't exit directly from functions to allow tidying
|
||||
* up, in particular EXIT traps. We still need to perform
|
||||
* the usual interactive tests to see if we can exit at
|
||||
* all, however.
|
||||
*
|
||||
* The forklevel test means we *do* exit from a subshell
|
||||
* inside a function when we reach the level of the
|
||||
* function itself.
|
||||
*/
|
||||
if (stopmsg || (zexit(0,2), !stopmsg)) {
|
||||
retflag = 1;
|
||||
|
@ -56,6 +56,7 @@
|
||||
#define BIN_ECHO 22
|
||||
#define BIN_DISABLE 23
|
||||
#define BIN_ENABLE 24
|
||||
#define BIN_PRINTF 25
|
||||
|
||||
/* These currently depend on being 0 and 1. */
|
||||
#define BIN_SETOPT 0
|
||||
|
@ -8,6 +8,6 @@ A03quoting.ztst C04funcdef.ztst Makefile.in ztst.zsh
|
||||
A04redirect.ztst D01prompt.ztst V02zregexparse.ztst
|
||||
A05execution.ztst D02glob.ztst Y01completion.ztst
|
||||
D06subscript.ztst V01zmodload.ztst E01options.ztst
|
||||
B02typeset.ztst
|
||||
B02typeset.ztst B03print.ztst
|
||||
README
|
||||
'
|
||||
|
135
Test/B03print.ztst
Normal file
135
Test/B03print.ztst
Normal file
@ -0,0 +1,135 @@
|
||||
# Tests for the echo, print, printf and pushln builtins
|
||||
|
||||
# Tested elsewhere:
|
||||
# Use of print -p to output to coprocess A01grammar
|
||||
# Prompt expansion with print -P D01prompt
|
||||
# -l, -r, -R and -n indirectly tested in various places
|
||||
|
||||
# Not yet tested:
|
||||
# echo and pushln
|
||||
# print's -b -c -s -z -N options
|
||||
|
||||
|
||||
%test
|
||||
|
||||
print -D "${HOME:-~}"
|
||||
0:replace directory name
|
||||
>~
|
||||
|
||||
print -u2 'error message'
|
||||
0:output to file-descriptor
|
||||
?error message
|
||||
|
||||
print -o foo bar Baz
|
||||
0:argument sorting
|
||||
>Baz bar foo
|
||||
|
||||
print -f
|
||||
1:print -f needs a format specified
|
||||
?(eval):print:1: -f: format argument expected
|
||||
|
||||
print -Of '%s\n' foo bar baz
|
||||
0:reverse argument sorting
|
||||
>foo
|
||||
>baz
|
||||
>bar
|
||||
|
||||
print -io a B c
|
||||
0:case-insensitive argument sorting
|
||||
>a B c
|
||||
|
||||
print -m '[0-9]' one 2 three 4 five 6
|
||||
0:removal of non-matching arguments
|
||||
>2 4 6
|
||||
|
||||
printf '%s\n' string
|
||||
0:test s format specifier
|
||||
>string
|
||||
|
||||
printf '%b' '\t\\\n'
|
||||
0:test b format specifier
|
||||
> \
|
||||
|
||||
# test %q here - it doesn't quite work yet
|
||||
|
||||
printf '%c\n' char
|
||||
0:test c format specifier
|
||||
>c
|
||||
|
||||
printf '%.10e%n\n' 1 count >/dev/null
|
||||
printf '%d\n' $count
|
||||
0:test n format specifier
|
||||
>16
|
||||
|
||||
printf '%d\n' 123
|
||||
0:test d format specifier
|
||||
>123
|
||||
|
||||
printf '%g\n' 123.45
|
||||
0:test g format specifier
|
||||
>123.45
|
||||
|
||||
# Is anyone not using ASCII
|
||||
printf '%d\n' \'A
|
||||
0:initial quote to get numeric value of character with int
|
||||
>65
|
||||
|
||||
printf '%.1E\n' \'B
|
||||
0:initial quote to get numeric value of character with double
|
||||
>6.6E+01
|
||||
|
||||
# code will probably be changed to print the literal `%s' in this case
|
||||
printf '\x25s\n' arg
|
||||
0:using \x25 to introduce a format specifier
|
||||
>arg
|
||||
|
||||
printf '%3c\n' c
|
||||
0:width specified in format specifier
|
||||
> c
|
||||
|
||||
printf '%.4s\n' chopped
|
||||
0:precision specified in format specifier
|
||||
>chop
|
||||
|
||||
printf '%*.*f\n' 6 2 10.2
|
||||
0:width/precision specified in arguments
|
||||
> 10.20
|
||||
|
||||
printf '%d\n' 3000000000
|
||||
1d:out of range numeric result
|
||||
?(eval):printf:1: `3000000000' arithmetic overflow
|
||||
|
||||
printf '%G\n' letters
|
||||
1:non numeric argument
|
||||
?(eval):printf:1: `letters' expected numeric value
|
||||
>0
|
||||
|
||||
print -f '%d\n' 2e4
|
||||
1:letters in numeric argument
|
||||
?(eval):print:1: `2e4' not completely converted
|
||||
>2
|
||||
|
||||
printf '%z'
|
||||
1:test invalid directive
|
||||
?(eval):printf:1: %z: invalid directive
|
||||
|
||||
print -m -f 'format - %s.\n' 'z' a b c
|
||||
0:format not printed if no arguments left after -m removal
|
||||
|
||||
print -f 'format - %s.\n'
|
||||
0:format printed despite lack of arguments
|
||||
>format - .
|
||||
|
||||
printf 'x%4sx\n'
|
||||
0:with no arguments empty string where string needed
|
||||
>x x
|
||||
|
||||
printf '%d\n'
|
||||
0:with no arguments, zero used where number needed
|
||||
>0
|
||||
|
||||
printf '%s\t%c:%#x%%\n' one a 1 two b 2 three c 3
|
||||
0:multiple arguments with format reused
|
||||
>one a:0x1%
|
||||
>two b:0x2%
|
||||
>three c:0x3%
|
Loading…
Reference in New Issue
Block a user