From 7b60aae9f314a2674d8512b3d85f83020148da73 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 19 Jun 2003 19:43:00 +0000 Subject: 18616: Add ternary expression handling to zformat --- ChangeLog | 5 ++ Doc/Zsh/mod_zutil.yo | 25 ++++++ Src/Modules/zutil.c | 221 ++++++++++++++++++++++++++++++++++----------------- 3 files changed, 176 insertions(+), 75 deletions(-) diff --git a/ChangeLog b/ChangeLog index 94de17762..64c6e5329 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2003-06-19 Peter Stephenson + + * 18616: Src/Modules/zutil.c, Doc/Zsh/mod_zutil.yo: Add + ternary expression handling to zformat. + 2003-06-18 Peter Stephenson * unposted: README, Etc/NEWS, Config/version.mk: version 4.1.1, diff --git a/Doc/Zsh/mod_zutil.yo b/Doc/Zsh/mod_zutil.yo index e22197ea2..3dc4b25a6 100644 --- a/Doc/Zsh/mod_zutil.yo +++ b/Doc/Zsh/mod_zutil.yo @@ -124,6 +124,31 @@ many characters. After all `tt(%)' sequences for the given var(specs) have been processed, the resulting string is stored in the parameter var(param). +The tt(%)-escapes also understand ternary expressions in the form used by +prompts. The tt(%) is followed by a `tt(LPAR())' and then an ordinary +format specifier character as described above. There may be a set of +digits either before or after the `tt(LPAR())'; these specify a test +number, which defaults to zero. Negative numbers are also allowed. An +arbitrary delimiter character follows the format specifier, which is +followed by a piece of `true' text, the delimiter character again, a piece +of `false' text, and a closing parenthesis. The complete expression +(without the digits) thus looks like +`tt(%LPAR())var(X)tt(.)var(text1)tt(.)var(text2)tt(RPAR())', except that +the `tt(.)' character is arbitrary. The value given for the format +specifier in the var(char)tt(:)var(string) expressions is evaluated as a +mathematical expression, and compared with the test number. If they are +the same, var(text1) is output, else var(text2) is output. A parenthesis +may be escaped in var(text2) as tt(%RPAR()). Either of var(text1) or +var(text2) may contain nested tt(%)-escapes. + +For example: + +example(zformat -f REPLY "The answer is '%3(c.yes.no)'." c:3) + +outputs "The answer is 'yes'." to tt(REPLY) since the value for the format +specifier tt(c) is 3, agreeing with the digit argument to the ternary +expression. + The second form, using the tt(-a) option, can be used for aligning strings. Here, the var(specs) are of the form `var(left)tt(:)var(right)' where `var(left)' and `var(right)' are diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 4ef237d90..57a368aab 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -549,6 +549,148 @@ bin_zstyle(char *nam, char **args, Options ops, int func) /* Format stuff. */ +/* + * One chunk of text, to allow recursive handling of ternary + * expressions in zformat -f output. + * instr The input string. + * specs The format specifiers, specs[c] is the string from c:string + * outp *outp is the start of the output string + * ousedp (*outp)[*ousedp] is where to write next + * olenp *olenp is the size allocated for *outp + * endchar Terminator character in addition to `\0' (may be '\0') + * skip If 1, don't output, just parse. + */ +static char *zformat_substring(char* instr, char **specs, char **outp, + int *ousedp, int *olenp, int endchar, int skip) +{ + char *s; + + for (s = instr; *s && *s != endchar; s++) { + if (*s == '%') { + int right, min = -1, max = -1, outl, testit; + char *spec, *start = s; + + if ((right = (*++s == '-'))) + s++; + + if (*s >= '0' && *s <= '9') { + for (min = 0; *s >= '0' && *s <= '9'; s++) + min = (min * 10) + (int) STOUC(*s) - '0'; + } + + /* Ternary expressions */ + testit = (STOUC(*s) == '('); + if (testit && s[1] == '-') + { + /* Allow %(-1... etc. */ + right = 1; + s++; + } + if ((*s == '.' || testit) && s[1] >= '0' && s[1] <= '9') { + for (max = 0, s++; *s >= '0' && *s <= '9'; s++) + max = (max * 10) + (int) STOUC(*s) - '0'; + } + else if (testit) + s++; + + if (testit && STOUC(*s)) { + int actval, testval, endcharl; + + /* + * One one number is useful for ternary expressions. + * Remember to put the sign back. + */ + testval = (min >= 0) ? min : (max >= 0) ? max : 0; + if (right) + testval *= -1; + + if (specs[STOUC(*s)]) + actval = (int)mathevali(specs[STOUC(*s)]); + else + actval = 0; + /* zero means values are equal, i.e. true */ + actval -= testval; + + /* careful about premature end of string */ + if (!(endcharl = *++s)) + return NULL; + + /* + * Either skip true text and output false text, or + * vice versa... unless we are already skipping. + */ + if (!(s = zformat_substring(s+1, specs, outp, ousedp, + olenp, endcharl, skip || actval))) + return NULL; + if (!(s = zformat_substring(s+1, specs, outp, ousedp, + olenp, ')', skip || !actval))) + return NULL; + } else if (skip) { + continue; + } else if ((spec = specs[STOUC(*s)])) { + int len; + + if ((len = strlen(spec)) > max && max >= 0) + len = max; + outl = (min >= 0 ? (min > len ? min : len) : len); + + if (*ousedp + outl >= *olenp) { + int nlen = *olenp + outl + 128; + char *tmp = (char *) zhalloc(nlen); + + memcpy(tmp, *outp, *olenp); + *olenp = nlen; + *outp = tmp; + } + if (len >= outl) { + memcpy(*outp + *ousedp, spec, outl); + *ousedp += outl; + } else { + int diff = outl - len; + + if (right) { + while (diff--) + (*outp)[(*ousedp)++] = ' '; + memcpy(*outp + *ousedp, spec, len); + *ousedp += len; + } else { + memcpy(*outp + *ousedp, spec, len); + *ousedp += len; + while (diff--) + (*outp)[(*ousedp)++] = ' '; + } + } + } else { + int len = s - start + 1; + + if (*ousedp + len >= *olenp) { + int nlen = *olenp + len + 128; + char *tmp = (char *) zhalloc(nlen); + + memcpy(tmp, *outp, *olenp); + *olenp = nlen; + *outp = tmp; + } + memcpy(*outp + *ousedp, start, len); + *ousedp += len; + } + } else { + if (skip) + continue; + if (*ousedp + 1 >= *olenp) { + char *tmp = (char *) zhalloc((*olenp) << 1); + + memcpy(tmp, *outp, *olenp); + *olenp <<= 1; + *outp = tmp; + } + (*outp)[(*ousedp)++] = *s; + } + } + + return s; +} + static int bin_zformat(char *nam, char **args, Options ops, int func) { @@ -563,11 +705,13 @@ bin_zformat(char *nam, char **args, Options ops, int func) switch (opt) { case 'f': { - char **ap, *specs[256], *out, *s; + char **ap, *specs[256], *out; int olen, oused = 0; memset(specs, 0, 256 * sizeof(char *)); + specs['%'] = "%"; + specs[')'] = ")"; for (ap = args + 2; *ap; ap++) { if (!ap[0][0] || ap[0][0] == '-' || ap[0][0] == '.' || (ap[0][0] >= '0' && ap[0][0] <= '9') || @@ -579,80 +723,7 @@ bin_zformat(char *nam, char **args, Options ops, int func) } out = (char *) zhalloc(olen = 128); - for (s = args[1]; *s; s++) { - if (*s == '%') { - int right, min = -1, max = -1, outl; - char *spec, *start = s; - - if ((right = (*++s == '-'))) - s++; - - if (*s >= '0' && *s <= '9') { - for (min = 0; *s >= '0' && *s <= '9'; s++) - min = (min * 10) + (int) STOUC(*s) - '0'; - } - if (*s == '.' && s[1] >= '0' && s[1] <= '9') { - for (max = 0, s++; *s >= '0' && *s <= '9'; s++) - max = (max * 10) + (int) STOUC(*s) - '0'; - } - if ((spec = specs[STOUC(*s)])) { - int len; - - if ((len = strlen(spec)) > max && max >= 0) - len = max; - outl = (min >= 0 ? (min > len ? min : len) : len); - - if (oused + outl >= olen) { - int nlen = olen + outl + 128; - char *tmp = (char *) zhalloc(nlen); - - memcpy(tmp, out, olen); - olen = nlen; - out = tmp; - } - if (len >= outl) { - memcpy(out + oused, spec, outl); - oused += outl; - } else { - int diff = outl - len; - - if (right) { - while (diff--) - out[oused++] = ' '; - memcpy(out + oused, spec, len); - oused += len; - } else { - memcpy(out + oused, spec, len); - oused += len; - while (diff--) - out[oused++] = ' '; - } - } - } else { - int len = s - start + 1; - - if (oused + len >= olen) { - int nlen = olen + len + 128; - char *tmp = (char *) zhalloc(nlen); - - memcpy(tmp, out, olen); - olen = nlen; - out = tmp; - } - memcpy(out + oused, start, len); - oused += len; - } - } else { - if (oused + 1 >= olen) { - char *tmp = (char *) zhalloc(olen << 1); - - memcpy(tmp, out, olen); - olen <<= 1; - out = tmp; - } - out[oused++] = *s; - } - } + zformat_substring(args[1], specs, &out, &oused, &olen, '\0', 0); out[oused] = '\0'; setsparam(args[0], ztrdup(out)); -- cgit 1.4.1