1
0
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:
Oliver Kiddle 2001-10-08 10:47:46 +00:00
parent 0ca8bb74f0
commit 648d1c2756
7 changed files with 250 additions and 91 deletions

View File

@ -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

View File

@ -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]' \

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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
View 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%