From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 2243 invoked from network); 27 Sep 2004 11:05:31 -0000 Received: from news.dotsrc.org (HELO a.mx.sunsite.dk) (130.225.247.88) by ns1.primenet.com.au with SMTP; 27 Sep 2004 11:05:31 -0000 Received: (qmail 8670 invoked from network); 27 Sep 2004 11:05:25 -0000 Received: from sunsite.dk (130.225.247.90) by a.mx.sunsite.dk with SMTP; 27 Sep 2004 11:05:25 -0000 Received: (qmail 7020 invoked by alias); 27 Sep 2004 11:05:12 -0000 Mailing-List: contact zsh-workers-help@sunsite.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 20412 Received: (qmail 6997 invoked from network); 27 Sep 2004 11:05:10 -0000 Received: from unknown (HELO a.mx.sunsite.dk) (130.225.247.88) by sunsite.dk with SMTP; 27 Sep 2004 11:05:10 -0000 Received: (qmail 8149 invoked from network); 27 Sep 2004 11:04:35 -0000 Received: from lhuumrelay3.lnd.ops.eu.uu.net (62.189.58.19) by a.mx.sunsite.dk with SMTP; 27 Sep 2004 11:04:33 -0000 Received: from MAILSWEEPER01.csr.com (mailhost1.csr.com [62.189.183.235]) by lhuumrelay3.lnd.ops.eu.uu.net (8.11.0/8.11.0) with ESMTP id i8RB4Wv14059 for ; Mon, 27 Sep 2004 11:04:32 GMT Received: from EXCHANGE02.csr.com (unverified [192.168.137.45]) by MAILSWEEPER01.csr.com (Content Technologies SMTPRS 4.3.12) with ESMTP id for ; Mon, 27 Sep 2004 12:03:34 +0100 Received: from news01.csr.com ([192.168.143.38]) by EXCHANGE02.csr.com with Microsoft SMTPSVC(5.0.2195.6713); Mon, 27 Sep 2004 12:06:19 +0100 Received: from news01.csr.com (localhost.localdomain [127.0.0.1]) by news01.csr.com (8.12.11/8.12.11) with ESMTP id i8RB4Vvs009115 for ; Mon, 27 Sep 2004 12:04:31 +0100 Received: from csr.com (pws@localhost) by news01.csr.com (8.12.11/8.12.11/Submit) with ESMTP id i8RB4URU009112 for ; Mon, 27 Sep 2004 12:04:30 +0100 Message-Id: <200409271104.i8RB4URU009112@news01.csr.com> X-Authentication-Warning: news01.csr.com: pws owned process doing -bs To: zsh-workers@sunsite.dk Subject: Re: More POSIX developments In-reply-to: References: Date: Mon, 27 Sep 2004 12:04:30 +0100 From: Peter Stephenson X-OriginalArrivalTime: 27 Sep 2004 11:06:19.0441 (UTC) FILETIME=[04151210:01C4A482] X-Spam-Checker-Version: SpamAssassin 2.63 on a.mx.sunsite.dk X-Spam-Level: X-Spam-Status: No, hits=0.0 required=6.0 tests=none autolearn=no version=2.63 X-Spam-Hits: 0.0 Bart Schaefer wrote: > According to today's minutes of the yesterday's austin-group teleconf: > > ----- > It was agreed so far that > > test asdf -ge 0 > > is a syntax error and is expected to return something greater than 1 > ----- > > In zsh's builtin test, of course, "asdf -ge 0" is interpreted in math > context, and is equivalent to (( asdf >= 0 )) and thus to "$asdf -ge 0". > The (( )) form is still considered OK, but the test -ge form is (or soon > will be) required to fail. > > Yet another case where "emulate posix" might be useful. An idea that > occurs to me is that we could introduce hidden options, that is, having > no name that could be referenced via "setopt" but that are switched on > and off in groups, only via "emulate". Well, [[ ... ]] has always been the preferred zsh way of doing things, with [ and test for compatibility, so it's at least plausible that the latter should simply implement POSIX. The documentation clearly says test was "added for compatibility", as well as vaguely indicating it's "like" conditional expressions. So there is some wiggle room this time. Returning "something greater than 1" for an error is nastier to code, since the main function involved, evalcond(), returns statuses the other way round. Hence a lot of return statuses need flipping. The following patch, which I won't commit until we've decided which way to go, tries to cover the bases by making test work (more) like POSIX while leaving [[ ... ]] the way it is. The errors now return status 2 from evalcond, but for backward compatibility [[ ... ]] turns them into shell errors. Quite possibly I've missed something, but this is about the minimal damage we can inflict without an option. The existing tests still all pass, although the new code means new tests should be added. Also, the documentation needs clarifying whatever we decide to do. I needed to add an extra warning function to choose between the cases where we have a builtin name and where the code is called internally as a condition. Does anyone know if there's a reason zwarnnam handles the absence of a command name in a strange way? In other words, could I remove zwarnnamopt and make zwarnnam behave like that? The current behaviour of zwarnnam without a command name doesn't look very useful: it outputs a space and then the error message. However, I haven't checked all the zillion calls to it and maybe it's designed to be output after another message. Index: Src/builtin.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/builtin.c,v retrieving revision 1.126 diff -u -r1.126 builtin.c --- Src/builtin.c 9 Sep 2004 10:12:47 -0000 1.126 +++ Src/builtin.c 27 Sep 2004 10:56:12 -0000 @@ -4846,7 +4846,7 @@ state.strs = prog->strs; - return !evalcond(&state); + return evalcond(&state, name); } /* display a time, provided in units of 1/60s, as minutes and seconds */ Index: Src/cond.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/cond.c,v retrieving revision 1.5 diff -u -r1.5 cond.c --- Src/cond.c 1 Aug 2002 15:06:26 -0000 1.5 +++ Src/cond.c 27 Sep 2004 10:56:12 -0000 @@ -37,15 +37,26 @@ "-ne", "-lt", "-gt", "-le", "-ge" }; +/* + * Evaluate a conditional expression given the arguments. + * If fromtest is set, the caller is the test or [ builtin; + * with the pointer giving the name of the command. + * for POSIX conformance this supports a more limited range + * of functionality. + * + * Return status is the final shell status, i.e. 0 for true, + * 1 for false and 2 for error. + */ + /**/ int -evalcond(Estate state) +evalcond(Estate state, char *fromtest) { struct stat *st; char *left, *right; Wordcode pcode; wordcode code; - int ctype, htok = 0; + int ctype, htok = 0, ret; rec: @@ -58,24 +69,28 @@ case COND_NOT: if (tracingcond) fprintf(xtrerr, " %s", condstr[ctype]); - return !evalcond(state); + ret = evalcond(state, fromtest); + if (ret == 2) + return ret; + else + return !ret; case COND_AND: - if (evalcond(state)) { + if (!(ret = evalcond(state, fromtest))) { if (tracingcond) fprintf(xtrerr, " %s", condstr[ctype]); goto rec; } else { state->pc = pcode + (WC_COND_SKIP(code) + 1); - return 0; + return ret; } case COND_OR: - if (!evalcond(state)) { + if ((ret = evalcond(state, fromtest)) == 1) { if (tracingcond) fprintf(xtrerr, " %s", condstr[ctype]); goto rec; } else { state->pc = pcode + (WC_COND_SKIP(code) + 1); - return 1; + return ret; } case COND_MOD: case COND_MODI: @@ -99,12 +114,13 @@ if ((cd = getconddef((ctype == COND_MODI), name + 1, 1))) { if (ctype == COND_MOD && (l < cd->min || (cd->max >= 0 && l > cd->max))) { - zerr("unrecognized condition: `%s'", name, 0); - return 0; + zwarnnamopt(fromtest, "unrecognized condition: `%s'", + name, 0); + return 2; } if (tracingcond) tracemodcond(name, strs, ctype == COND_MODI); - return cd->handler(strs, cd->condid); + return !cd->handler(strs, cd->condid); } else { char *s = strs[0]; @@ -115,16 +131,20 @@ if (name && name[0] == '-' && (cd = getconddef(0, name + 1, 1))) { if (l < cd->min || (cd->max >= 0 && l > cd->max)) { - zerr("unrecognized condition: `%s'", name, 0); - return 0; + zwarnnamopt(fromtest, "unrecognized condition: `%s'", + name, 0); + return 2; } if (tracingcond) tracemodcond(name, strs, ctype == COND_MODI); - return cd->handler(strs, cd->condid); - } else - zerr("unrecognized condition: `%s'", name, 0); + return !cd->handler(strs, cd->condid); + } else { + zwarnnamopt(fromtest, + "unrecognized condition: `%s'", name, 0); + } } - return 0; + /* module not found, error */ + return 2; } } left = ecgetstr(state, EC_DUPTOK, &htok); @@ -159,8 +179,34 @@ if (ctype >= COND_EQ && ctype <= COND_GE) { mnumber mn1, mn2; - mn1 = matheval(left); - mn2 = matheval(right); + if (fromtest) { + /* + * For test and [, the expressions must be base 10 integers, + * not integer expressions. + */ + char *eptr, *err; + + mn1.u.l = zstrtol(left, &eptr, 10); + if (!*eptr) + { + mn2.u.l = zstrtol(right, &eptr, 10); + err = right; + } + else + err = left; + + if (*eptr) + { + zwarnnamopt(fromtest, "integer expression expected: %s", + err, 0); + return 2; + } + + mn1.type = mn2.type = MN_INTEGER; + } else { + mn1 = matheval(left); + mn2 = matheval(right); + } if (((mn1.type|mn2.type) & (MN_INTEGER|MN_FLOAT)) == (MN_INTEGER|MN_FLOAT)) { @@ -176,23 +222,23 @@ } switch(ctype) { case COND_EQ: - return (mn1.type & MN_FLOAT) ? (mn1.u.d == mn2.u.d) : - (mn1.u.l == mn2.u.l); + return !((mn1.type & MN_FLOAT) ? (mn1.u.d == mn2.u.d) : + (mn1.u.l == mn2.u.l)); case COND_NE: - return (mn1.type & MN_FLOAT) ? (mn1.u.d != mn2.u.d) : - (mn1.u.l != mn2.u.l); + return !((mn1.type & MN_FLOAT) ? (mn1.u.d != mn2.u.d) : + (mn1.u.l != mn2.u.l)); case COND_LT: - return (mn1.type & MN_FLOAT) ? (mn1.u.d < mn2.u.d) : - (mn1.u.l < mn2.u.l); + return !((mn1.type & MN_FLOAT) ? (mn1.u.d < mn2.u.d) : + (mn1.u.l < mn2.u.l)); case COND_GT: - return (mn1.type & MN_FLOAT) ? (mn1.u.d > mn2.u.d) : - (mn1.u.l > mn2.u.l); + return !((mn1.type & MN_FLOAT) ? (mn1.u.d > mn2.u.d) : + (mn1.u.l > mn2.u.l)); case COND_LE: - return (mn1.type & MN_FLOAT) ? (mn1.u.d <= mn2.u.d) : - (mn1.u.l <= mn2.u.l); + return !((mn1.type & MN_FLOAT) ? (mn1.u.d <= mn2.u.d) : + (mn1.u.l <= mn2.u.l)); case COND_GE: - return (mn1.type & MN_FLOAT) ? (mn1.u.d >= mn2.u.d) : - (mn1.u.l >= mn2.u.l); + return !((mn1.type & MN_FLOAT) ? (mn1.u.d >= mn2.u.d) : + (mn1.u.l >= mn2.u.l)); } } @@ -215,81 +261,83 @@ !strcmp(opat, right) && pprog != dummy_patprog2); if (!(pprog = patcompile(right, (save ? PAT_ZDUP : PAT_STATIC), - NULL))) - zerr("bad pattern: %s", right, 0); + NULL))) { + zwarnnamopt(fromtest, "bad pattern: %s", right, 0); + return 2; + } else if (save) state->prog->pats[npat] = pprog; } state->pc += 2; test = (pprog && pattry(pprog, left)); - return (ctype == COND_STREQ ? test : !test); + return !(ctype == COND_STREQ ? test : !test); } case COND_STRLT: - return strcmp(left, right) < 0; + return !(strcmp(left, right) < 0); case COND_STRGTR: - return strcmp(left, right) > 0; + return !(strcmp(left, right) > 0); case 'e': case 'a': - return (doaccess(left, F_OK)); + return (!doaccess(left, F_OK)); case 'b': - return (S_ISBLK(dostat(left))); + return (!S_ISBLK(dostat(left))); case 'c': - return (S_ISCHR(dostat(left))); + return (!S_ISCHR(dostat(left))); case 'd': - return (S_ISDIR(dostat(left))); + return (!S_ISDIR(dostat(left))); case 'f': - return (S_ISREG(dostat(left))); + return (!S_ISREG(dostat(left))); case 'g': - return (!!(dostat(left) & S_ISGID)); + return (!(dostat(left) & S_ISGID)); case 'k': - return (!!(dostat(left) & S_ISVTX)); + return (!(dostat(left) & S_ISVTX)); case 'n': - return (!!strlen(left)); + return (!strlen(left)); case 'o': - return (optison(left)); + return (optison(fromtest, left)); case 'p': - return (S_ISFIFO(dostat(left))); + return (!S_ISFIFO(dostat(left))); case 'r': - return (doaccess(left, R_OK)); + return (!doaccess(left, R_OK)); case 's': - return ((st = getstat(left)) && !!(st->st_size)); + return !((st = getstat(left)) && !!(st->st_size)); case 'S': - return (S_ISSOCK(dostat(left))); + return (!S_ISSOCK(dostat(left))); case 'u': - return (!!(dostat(left) & S_ISUID)); + return (!(dostat(left) & S_ISUID)); case 'w': - return (doaccess(left, W_OK)); + return (!doaccess(left, W_OK)); case 'x': if (privasserted()) { mode_t mode = dostat(left); - return (mode & S_IXUGO) || S_ISDIR(mode); + return !((mode & S_IXUGO) || S_ISDIR(mode)); } - return doaccess(left, X_OK); + return !doaccess(left, X_OK); case 'z': - return (!strlen(left)); + return !!(strlen(left)); case 'h': case 'L': - return (S_ISLNK(dolstat(left))); + return (!S_ISLNK(dolstat(left))); case 'O': - return ((st = getstat(left)) && st->st_uid == geteuid()); + return !((st = getstat(left)) && st->st_uid == geteuid()); case 'G': - return ((st = getstat(left)) && st->st_gid == getegid()); + return !((st = getstat(left)) && st->st_gid == getegid()); case 'N': - return ((st = getstat(left)) && st->st_atime <= st->st_mtime); + return !((st = getstat(left)) && st->st_atime <= st->st_mtime); case 't': - return isatty(mathevali(left)); + return !isatty(mathevali(left)); case COND_NT: case COND_OT: { time_t a; if (!(st = getstat(left))) - return 0; + return 1; a = st->st_mtime; if (!(st = getstat(right))) - return 0; - return (ctype == COND_NT) ? a > st->st_mtime : a < st->st_mtime; + return 2; + return !((ctype == COND_NT) ? a > st->st_mtime : a < st->st_mtime); } case COND_EF: { @@ -297,17 +345,18 @@ ino_t i; if (!(st = getstat(left))) - return 0; + return 1; d = st->st_dev; i = st->st_ino; if (!(st = getstat(right))) - return 0; - return d == st->st_dev && i == st->st_ino; + return 1; + return !(d == st->st_dev && i == st->st_ino); } default: - zerr("bad cond code", NULL, 0); + zwarnnamopt(fromtest, "bad cond code", NULL, 0); + return 2; } - return 0; + return 1; } @@ -371,9 +420,13 @@ } +/* + * optison returns evalcond-friendly statuses (true, false, error). + */ + /**/ static int -optison(char *s) +optison(char *name, char *s) { int i; @@ -382,12 +435,12 @@ else i = optlookup(s); if (!i) { - zerr("no such option: %s", s, 0); - return 0; + zwarnnamopt(name, "no such option: %s", s, 0); + return 2; } else if(i < 0) - return unset(-i); + return !unset(-i); else - return isset(i); + return !isset(i); } /**/ Index: Src/exec.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/exec.c,v retrieving revision 1.71 diff -u -r1.71 exec.c --- Src/exec.c 8 Sep 2004 08:24:41 -0000 1.71 +++ Src/exec.c 27 Sep 2004 10:56:13 -0000 @@ -3187,7 +3187,13 @@ tracingcond++; } cmdpush(CS_COND); - stat = !evalcond(state); + stat = evalcond(state, NULL); + /* + * 2 indicates a syntax error. For compatibility, turn this + * into a shell error. + */ + if (stat == 2) + errflag = 1; cmdpop(); if (isset(XTRACE)) { fprintf(xtrerr, " ]]\n"); Index: Src/utils.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/utils.c,v retrieving revision 1.65 diff -u -r1.65 utils.c --- Src/utils.c 17 Sep 2004 09:25:42 -0000 1.65 +++ Src/utils.c 27 Sep 2004 10:56:15 -0000 @@ -110,6 +110,22 @@ zerrmsg(fmt, str, num); } +/* + * zwarnnamopt is used in cases for code which is called both + * from a builtin (cmd non-NULL) and internally (cmd NULL). + * Is there a good reason zwarnnam doesn't do this? + */ + +/**/ +mod_export void +zwarnnamopt(const char *cmd, const char *fmt, const char *str, int num) +{ + if (cmd) + zwarnnam(cmd, fmt, str, num); + else + zwarn(fmt, str, num); +} + #ifdef __CYGWIN__ /* * This works around an occasional problem with dllwrap on Cygwin, seen -- Peter Stephenson Software Engineer CSR Ltd., Science Park, Milton Road, Cambridge, CB4 0WH, UK Tel: +44 (0)1223 692070 ********************************************************************** This email and any files transmitted with it are confidential and intended solely for the use of the individual or entity to whom they are addressed. If you have received this email in error please notify the system manager. This footnote also confirms that this email message has been swept by MIMEsweeper for the presence of computer viruses. www.mimesweeper.com **********************************************************************