diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/expr | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/expr')
| -rw-r--r-- | usr/src/cmd/expr/Makefile | 93 | ||||
| -rw-r--r-- | usr/src/cmd/expr/compile.c | 495 | ||||
| -rw-r--r-- | usr/src/cmd/expr/expr.c | 712 | ||||
| -rw-r--r-- | usr/src/cmd/expr/expr.xcl | 45 |
4 files changed, 1345 insertions, 0 deletions
diff --git a/usr/src/cmd/expr/Makefile b/usr/src/cmd/expr/Makefile new file mode 100644 index 0000000000..9a2cf3b144 --- /dev/null +++ b/usr/src/cmd/expr/Makefile @@ -0,0 +1,93 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +PROG= expr +XPG4PROG= expr +XPG6PROG= expr + +EXPROBJ= exprobjs/expr.o exprobjs/compile.o +XPG4EXPROBJ= exprobjs.xpg4/expr.o exprobjs.xpg4/compile.o +XPG6EXPROBJ= exprobjs.xpg6/expr.o exprobjs.xpg6/compile.o + +OBJS= $(EXPROBJ) $(XPG4EXPROBJ) $(XPG6EXPROBJ) +SRCS= expr.c compile.c + +include ../Makefile.cmd + +CFLAGS += $(CCVERBOSE) +$(XPG4) := CFLAGS += -DXPG4 +$(XPG6) := CFLAGS += -DXPG6 + +# This flag is being added only for SCO (x86) compatibility +CFLAGS += $(iBCS2FLAG) + +XGETFLAGS += -a -x expr.xcl + +LDLIBS += -lgen + +.KEEP_STATE: + +all: $(PROG) $(XPG4) $(XPG6) + +install: all $(ROOTPROG) $(ROOTXPG4PROG) $(ROOTXPG6PROG) + +$(PROG): exprobjs $(EXPROBJ) + $(LINK.c) $(EXPROBJ) -o $@ $(LDLIBS) + $(POST_PROCESS) + +$(XPG4): exprobjs.xpg4 $(XPG4EXPROBJ) + $(LINK.c) $(XPG4EXPROBJ) -o $@ $(LDLIBS) + $(POST_PROCESS) + +$(XPG6): exprobjs.xpg6 $(XPG6EXPROBJ) + $(LINK.c) $(XPG6EXPROBJ) -o $@ $(LDLIBS) + $(POST_PROCESS) + +exprobjs/%.o: %.c + $(COMPILE.c) -o $@ $< + +exprobjs.xpg4/%.o: %.c + $(COMPILE.c) -o $@ $< + +exprobjs.xpg6/%.o: %.c + $(COMPILE.c) -o $@ $< + +exprobjs: + -@mkdir -p $@ + +exprobjs.xpg4: + -@mkdir -p $@ + +exprobjs.xpg6: + -@mkdir -p $@ + +clean: + $(RM) $(OBJS) + +lint: lint_PROG + +include ../Makefile.targ diff --git a/usr/src/cmd/expr/compile.c b/usr/src/cmd/expr/compile.c new file mode 100644 index 0000000000..cb619a5ff1 --- /dev/null +++ b/usr/src/cmd/expr/compile.c @@ -0,0 +1,495 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1995-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * xcompile, xstep, xadvance - simulate compile(3g), step(3g), advance(3g) + * using regcomp(3c), regexec(3c) interfaces. This is an XCU4 + * porting aid. switches out to libgen compile/step if collation + * table not present. + * + * Goal is to work with vi and sed/ed. + * Returns expbuf in dhl format (encoding of first two bytes). + * Note also that this is profoundly single threaded. You + * cannot call compile twice with two separate search strings + * because the second call will wipe out the earlier stored string. + * This must be fixed, plus a general cleanup should be performed + * if this is to be integrated into libc. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <widec.h> +#include <sys/types.h> +#include <regex.h> +#include <locale.h> +#include <stdlib.h> +#include <locale.h> +#include <string.h> +#include <unistd.h> +#include <regexpr.h> + +/* + * psuedo compile/step/advance global variables + */ +extern int nbra; +extern char *locs; /* for stopping execess recursion */ +extern char *loc1; /* 1st character which matched RE */ +extern char *loc2; /* char after lst char in matched RE */ +extern char *braslist[]; /* start of nbra subexp */ +extern char *braelist[]; /* end of nbra subexp */ +extern int regerrno; +extern int reglength; + +int regcomp_flags; /* interface to specify cflags for regcomp */ + +void regex_comp_free(void *a); +static int dhl_step(const char *str, const char *ep); +static int dhl_advance(const char *str, const char *ep); +static int map_errnos(int); /* Convert regcomp error */ +static int dhl_doit(const char *, const regex_t *, const int flags); +static char * dhl_compile(const char *instr, char *ep, char *endbuf); + +/* + * # of sub re's: NOTE: For now limit on bra list defined here + * but fix is to add maxbra define to to regex.h + * One problem is that a bigger number is a performance hit since + * regexec() has a slow initialization loop that goes around SEPSIZE times + */ +#define SEPSIZE 20 +static regmatch_t rm[SEPSIZE]; /* ptr to list of RE matches */ + +/* + * Structure to contain dl encoded first two bytes for vi, plus hold two + * regex structures, one for advance and one for step. + */ +static struct regex_comp { + char r_head[2]; /* Header for DL encoding for vi */ + regex_t r_stp; /* For use by step */ + regex_t r_adv; /* For use by advance */ +} reg_comp; + +/* + * global value for the size of a regex_comp structure: + */ +size_t regexc_size = sizeof (reg_comp); + + +char * +compile(const char *instr, char *expbuf, char *endbuf) +{ + return (dhl_compile(instr, expbuf, endbuf)); +} + +int +step(const char *instr, const char *expbuf) +{ + return (dhl_step(instr, expbuf)); +} + +int +advance(const char *instr, const char *expbuf) +{ + return (dhl_advance(instr, expbuf)); +} + + +/* + * the compile and step routines here simulate the old libgen routines of + * compile/step Re: regexpr(3G). in order to do this, we must assume + * that expbuf[] consists of the following format: + * 1) the first two bytes consist of a special encoding - see below. + * 2) the next part is a regex_t used by regexec()/regcomp() for step + * 3) the final part is a regex_t used by regexec()/regcomp() for advance + * + * the special encoding of the first two bytes is referenced throughout + * vi. apparently expbuf[0] is set to: + * = 0 upon initialization + * = 1 if the first char of the RE is a ^ + * = 0 if the first char of the RE isn't a ^ + * and expbuf[1-35+] = bitmap of the type of RE chars in the expression. + * this is apparently 0 if there's no RE. + * Here, we use expbuf[0] in a similar fashion; and expbuf[1] is non-zero + * if there's at least 1 RE in the string. + * I say "apparently" as the code to compile()/step() is poorly written. + */ +static char * +dhl_compile(instr, expbuf, endbuf) +const char *instr; /* the regular expression */ +char *expbuf; /* where the compiled RE gets placed */ +char *endbuf; /* ending addr of expbuf */ +{ + int rv; + int alloc = 0; + char adv_instr[4096]; /* PLENTY big temp buffer */ + char *instrp; /* PLENTY big temp buffer */ + + if (*instr == (char) NULL) { + regerrno = 41; + return (NULL); + } + + /* + * Check values of expbuf and endbuf + */ + if (expbuf == NULL) { + if ((expbuf = malloc(regexc_size)) == NULL) { + regerrno = 50; + return (NULL); + } + memset(®_comp, 0, regexc_size); + alloc = 1; + endbuf = expbuf + regexc_size; + } else { /* Check if enough memory was allocated */ + if (expbuf + regexc_size > endbuf) { + regerrno = 50; + return (NULL); + } + memcpy(®_comp, expbuf, regexc_size); + } + + /* + * Clear global flags + */ + nbra = 0; + regerrno = 0; + + /* + * Free any data being held for previous search strings + */ + regex_comp_free(®_comp); + + /* + * We call regcomp twice, once to get a regex_t for use by step() + * and then again with for use by advance() + */ + if ((rv = regcomp(®_comp.r_stp, instr, regcomp_flags)) != 0) { + regerrno = map_errnos(rv); /* Convert regcomp error */ + goto out; + } + /* + * To support advance, which assumes an implicit ^ to match at start + * of line we prepend a ^ to the pattern by copying to a temp buffer + */ + + if (instr[0] == '^') + instrp = (char *) instr; /* String already has leading ^ */ + else { + adv_instr[0] = '^'; + strncpy(&adv_instr[1], instr, 2048); + instrp = adv_instr; + } + + if ((rv = regcomp(®_comp.r_adv, instrp, regcomp_flags)) != 0) { + regerrno = map_errnos(rv); /* Convert regcomp error */ + goto out; + } + + /* + * update global variables + */ + nbra = (int) reg_comp.r_adv.re_nsub > 0 ? + (int) reg_comp.r_adv.re_nsub : 0; + regerrno = 0; + + /* + * Set the header flags for use by vi + */ + if (instr[0] == '^') /* if beginning of string, */ + reg_comp.r_head[0] = 1; /* set special flag */ + else + reg_comp.r_head[0] = 0; /* clear special flag */ + /* + * note that for a single BRE, nbra will be 0 here. + * we're guaranteed that, at this point, a RE has been found. + */ + reg_comp.r_head[1] = 1; /* set special flag */ + /* + * Copy our reg_comp structure to expbuf + */ + (void) memcpy(expbuf, (char *) ®_comp, regexc_size); + +out: + /* + * Return code from libgen regcomp with mods. Note weird return + * value - if space is malloc'd return pointer to start of space, + * if user provided his own space, return pointer to 1+last byte + * of his space. + */ + if (regerrno != 0) { + if (alloc) + free(expbuf); + return (NULL); + } + reglength = regexc_size; + + if (alloc) + return (expbuf); + else + return (expbuf + regexc_size); +} + + +/* + * dhl_step: step through a string until a RE match is found, or end of str + */ +static int +dhl_step(str, ep) +const char *str; /* characters to be checked for a match */ +const char *ep; /* compiled RE from dhl_compile() */ +{ + /* + * Check if we're passed a null ep + */ + if (ep == NULL) { + regerrno = 41; /* No remembered search string error */ + return (0); + } + /* + * Call common routine with r_stp (step) structure + */ + return (dhl_doit(str, &(((struct regex_comp *) ep)->r_stp), + ((locs != NULL) ? REG_NOTBOL : 0))); +} + +/* + * dhl_advance: implement advance + */ +static int +dhl_advance(str, ep) +const char *str; /* characters to be checked for a match */ +const char *ep; /* compiled RE from dhl_compile() */ +{ + int rv; + /* + * Check if we're passed a null ep + */ + if (ep == NULL) { + regerrno = 41; /* No remembered search string error */ + return (0); + } + /* + * Call common routine with r_adv (advance) structure + */ + rv = dhl_doit(str, &(((struct regex_comp *) ep)->r_adv), 0); + loc1 = NULL; /* Clear it per the compile man page */ + return (rv); +} + +/* + * dhl_doit - common code for step and advance + */ +static int +dhl_doit(str, rep, flags) +const char *str; /* characters to be checked for a match */ +const regex_t *rep; +const int flags; /* flags to be passed to regexec directly */ +{ + int rv; + int i; + regmatch_t *prm; /* ptr to current regmatch_t */ + + /* + * Check if we're passed a null regex_t + */ + if (rep == NULL) { + regerrno = 41; /* No remembered search string error */ + return (0); + } + + regerrno = 0; + prm = &rm[0]; + + if ((rv = regexec(rep, str, SEPSIZE, prm, flags)) != REG_OK) { + if (rv == REG_NOMATCH) + return (0); + regerrno = map_errnos(rv); + return (0); + } + + loc1 = (char *)str + prm->rm_so; + loc2 = (char *)str + prm->rm_eo; + + /* + * Now we need to fill up the bra lists with all of the sub re's + * Note we subtract nsub -1, and preincrement prm. + */ + for (i = 0; i <= rep->re_nsub; i++) { + prm++; /* XXX inc past first subexp */ + braslist[i] = (char *)str + prm->rm_so; + braelist[i] = (char *)str + prm->rm_eo; + if (i >= SEPSIZE) { + regerrno = 50; /* regex overflow */ + return (0); + } + } + + /* + * Inverse logic, a zero from regexec - success, is a 1 + * from advance/step. + */ + + return (rv == 0); +} + + +/* + * regerrno to compile/step error mapping: + * This is really a big compromise. Some errors don't map at all + * like regcomp error 15 is generated by both compile() error types + * 44 & 46. So which one should we map to? + * Note REG_ESUB Can't happen- 9 is no longer max num of subexpressions + * To do your errors right use xregerr() to get the regcomp error + * string and print that. + * + * | regcomp/regexec | Compile/step/advance | + * +---------------------------------+--------------------------------------+ + * 0 REG_OK Pattern matched 1 - Pattern matched + * 1 REG_NOMATCH No match 0 - Pattern didn't match + * 2 REG_ECOLLATE Bad collation elmnt. 67 - Returned by compile on mbtowc err + * 3 REG_EESCAPE trailing \ in patrn 45 - } expected after \. + * 4 REG_ENEWLINE \n before end pattrn 36 - Illegal or missing delimiter. + * 5 REG_ENSUB Over 9 \( \) pairs 43 - Too many \( + * 6 REG_ESUBREG Bad number in \[0-9] 25 - ``\digit'' out of range. + * 7 REG_EBRACK [ ] inbalance 49 - [ ] imbalance. + * 8 REG_EPAREN ( ) inbalance 42 - \(~\) imbalance. + * 9 REG_EBRACE \{ \} inbalance 45 - } expected after \. + * 10 REG_ERANGE bad range endpoint 11 - Range endpoint too large. + * 11 REG_ESPACE no memory for pattern 50 - Regular expression overflow. + * 12 REG_BADRPT invalid repetition 36 - Illegal or missing delimiter. + * 13 REG_ECTYPE invalid char-class 67 - illegal byte sequence + * 14 REG_BADPAT syntax error 50 - Regular expression overflow. + * 15 REG_BADBR \{ \} contents bad 46 - First number exceeds 2nd in \{~\} + * 16 REG_EFATAL internal error 50 - Regular expression overflow. + * 17 REG_ECHAR bad mulitbyte char 67 - illegal byte sequence + * 18 REG_STACK stack overflow 50 - Regular expression overflow. + * 19 REG_ENOSYS function not supported 50- Regular expression overflow. + * + * For reference here's the compile/step errno's. We don't generate + * 41 here - it's done earlier, nor 44 since we can't tell if from 46. + * + * 11 - Range endpoint too large. + * 16 - Bad number. + * 25 - ``\digit'' out of range. + * 36 - Illegal or missing delimiter. + * 41 - No remembered search string. + * 42 - \(~\) imbalance. + * 43 - Too many \(. + * 44 - More than 2 numbers given in "\{~\}" + * 45 - } expected after \. + * 46 - First number exceeds 2nd in "\{~\}" + * 49 - [ ] imbalance. + * 50 - Regular expression overflow. + */ + +static int +map_errnos(int Errno) +{ + switch (Errno) { + case REG_ECOLLATE: + regerrno = 67; + break; + case REG_EESCAPE: + regerrno = 45; + break; + case REG_ENEWLINE: + regerrno = 36; + break; + case REG_ENSUB: + regerrno = 43; + break; + case REG_ESUBREG: + regerrno = 25; + break; + case REG_EBRACK: + regerrno = 49; + break; + case REG_EPAREN: + regerrno = 42; + break; + case REG_EBRACE: + regerrno = 45; + break; + case REG_ERANGE: + regerrno = 11; + break; + case REG_ESPACE: + regerrno = 50; + break; + case REG_BADRPT: + regerrno = 36; + break; + case REG_ECTYPE: + regerrno = 67; + break; + case REG_BADPAT: + regerrno = 50; + break; + case REG_BADBR: + regerrno = 46; + break; + case REG_EFATAL: + regerrno = 50; + break; + case REG_ECHAR: + regerrno = 67; + break; + case REG_STACK: + regerrno = 50; + break; + case REG_ENOSYS: + regerrno = 50; + break; + default: + regerrno = 50; + break; + } + return (regerrno); +} + +/* + * This is a routine to clean up the subtle substructure of the struct + * regex_comp type for use by clients of this module. Since the struct + * type is private, we use a generic interface, and trust the + * application to be damn sure that this operation is valid for the + * named memory. + */ + +void +regex_comp_free(void * a) +{ + /* + * Free any data being held for previous search strings + */ + + if (((struct regex_comp *) a) == NULL) { + return; + } + + regfree(&((struct regex_comp *)a)->r_stp); + regfree(&((struct regex_comp *)a)->r_adv); +} diff --git a/usr/src/cmd/expr/expr.c b/usr/src/cmd/expr/expr.c new file mode 100644 index 0000000000..5daff8d3f8 --- /dev/null +++ b/usr/src/cmd/expr/expr.c @@ -0,0 +1,712 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + + +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <regexpr.h> +#include <locale.h> +#include <string.h> +#include <unistd.h> +#include <regex.h> +#include <limits.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> + +#define A_STRING 258 +#define NOARG 259 +#define OR 260 +#define AND 261 +#define EQ 262 +#define LT 263 +#define GT 264 +#define GEQ 265 +#define LEQ 266 +#define NEQ 267 +#define ADD 268 +#define SUBT 269 +#define MULT 270 +#define DIV 271 +#define REM 272 +#define MCH 273 +#define MATCH 274 +#ifdef _iBCS2 +#define SUBSTR 276 +#define LENGTH 277 +#define INDEX 278 +#endif /* _iBCS2 */ + +/* size of subexpression array */ +#define MSIZE LINE_MAX +#define error(c) errxx() +#define EQL(x, y) (strcmp(x, y) == 0) + +#define ERROR(c) errxx() +#define MAX_MATCH 20 +static int ematch(char *, char *); +static void yyerror(char *); +static void errxx(); +static void *exprmalloc(size_t size); + +long atol(); +char *strcpy(), *strncpy(); +void exit(); + +static char *ltoa(); +static char *lltoa(); +static char **Av; +static char *buf; +static int Ac; +static int Argi; +static int noarg; +static int paren; +#ifdef _iBCS2 +char *sysv3_set; +#endif /* _iBCS2 */ +/* + * Array used to store subexpressions in regular expressions + * Only one subexpression allowed per regular expression currently + */ +static char Mstring[1][MSIZE]; + + +static char *operator[] = { + "|", "&", "+", "-", "*", "/", "%", ":", + "=", "==", "<", "<=", ">", ">=", "!=", + "match", +#ifdef _iBCS2 + "substr", "length", "index", +#endif /* _iBCS2 */ + "\0" }; +static int op[] = { + OR, AND, ADD, SUBT, MULT, DIV, REM, MCH, + EQ, EQ, LT, LEQ, GT, GEQ, NEQ, + MATCH +#ifdef _iBCS2 +, SUBSTR, LENGTH, INDEX +#endif /* _iBCS2 */ + }; +static int pri[] = { + 1, 2, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5, 5, 6, 7 +#ifdef _iBCS2 +, 7, 7, 7 +#endif /* _iBCS2 */ + }; + + +/* + * clean_buf - XCU4 mod to remove leading zeros from negative signed + * numeric output, e.g., -00001 becomes -1 + */ +static void +clean_buf(buf) + char *buf; +{ + int i = 0; + int is_a_num = 1; + int len; + long long num; + + if (buf[0] == '\0') + return; + len = strlen(buf); + if (len <= 0) + return; + + if (buf[0] == '-') { + i++; /* Skip the leading '-' see while loop */ + if (len <= 1) /* Is it a '-' all by itself? */ + return; /* Yes, so return */ + + while (i < len) { + if (! isdigit(buf[i])) { + is_a_num = 0; + break; + } + i++; + } + if (is_a_num) { + (void) sscanf(buf, "%lld", &num); + (void) sprintf(buf, "%lld", num); + } + } +} + +/* + * End XCU4 mods. + */ + +static int +yylex() +{ + char *p; + int i; + + if (Argi >= Ac) + return (NOARG); + + p = Av[Argi]; + + if ((*p == '(' || *p == ')') && p[1] == '\0') + return ((int)*p); + for (i = 0; *operator[i]; ++i) + if (EQL(operator[i], p)) + return (op[i]); + + + return (A_STRING); +} + +static char +*rel(oper, r1, r2) register char *r1, *r2; +{ + long long i; + + if (ematch(r1, "-\\{0,1\\}[0-9]*$") && + ematch(r2, "-\\{0,1\\}[0-9]*$")) { + errno = 0; + i = strtoll(r1, (char **)NULL, 10) - + strtoll(r2, (char **)NULL, 10); + if (errno) { +#ifdef XPG6 + /* XPG6: stdout will always contain newline even on error */ + (void) write(1, "\n", 1); +#endif + if (errno == ERANGE) { + (void) fprintf(stderr, gettext( + "expr: Integer argument too large\n")); + exit(3); + } else { + perror("expr"); + exit(3); + } + } + } + else + i = strcoll(r1, r2); + switch (oper) { + case EQ: + i = i == 0; + break; + case GT: + i = i > 0; + break; + case GEQ: + i = i >= 0; + break; + case LT: + i = i < 0; + break; + case LEQ: + i = i <= 0; + break; + case NEQ: + i = i != 0; + break; + } + return (i ? "1": "0"); +} + +static char +*arith(oper, r1, r2) char *r1, *r2; +{ + long long i1, i2; + register char *rv; + + if (!(ematch(r1, "-\\{0,1\\}[0-9]*$") && + ematch(r2, "-\\{0,1\\}[0-9]*$"))) + yyerror("non-numeric argument"); + errno = 0; + i1 = strtoll(r1, (char **)NULL, 10); + i2 = strtoll(r2, (char **)NULL, 10); + if (errno) { +#ifdef XPG6 + /* XPG6: stdout will always contain newline even on error */ + (void) write(1, "\n", 1); +#endif + if (errno == ERANGE) { + (void) fprintf(stderr, gettext( + "expr: Integer argument too large\n")); + exit(3); + } else { + perror("expr"); + exit(3); + } + } + + switch (oper) { + case ADD: + i1 = i1 + i2; + break; + case SUBT: + i1 = i1 - i2; + break; + case MULT: + i1 = i1 * i2; + break; + case DIV: + if (i2 == 0) + yyerror("division by zero"); + i1 = i1 / i2; + break; + case REM: + if (i2 == 0) + yyerror("division by zero"); + i1 = i1 % i2; + break; + } + rv = exprmalloc(25); + (void) strcpy(rv, lltoa(i1)); + return (rv); +} + +static char +*conj(oper, r1, r2) + char *r1, *r2; +{ + register char *rv; + + switch (oper) { + + case OR: + if (EQL(r1, "0") || EQL(r1, "")) { + if (EQL(r2, "0") || EQL(r2, "")) + rv = "0"; + else + rv = r2; + } else + rv = r1; + break; + case AND: + if (EQL(r1, "0") || EQL(r1, "")) + rv = "0"; + else if (EQL(r2, "0") || EQL(r2, "")) + rv = "0"; + else + rv = r1; + break; + } + return (rv); +} + +#ifdef _iBCS2 +char * +substr(char *v, char *s, char *w) +{ + int si, wi; + char *res; + + si = atol(s); + wi = atol(w); + while (--si) + if (*v) ++v; + + res = v; + + while (wi--) + if (*v) ++v; + + *v = '\0'; + return (res); +} + +char * +index(char *s, char *t) +{ + long i, j; + char *rv; + + for (i = 0; s[i]; ++i) + for (j = 0; t[j]; ++j) + if (s[i] == t[j]) { + (void) strcpy(rv = exprmalloc(8), ltoa(++i)); + return (rv); + } + return ("0"); +} + +char * +length(char *s) +{ + long i = 0; + char *rv; + + while (*s++) ++i; + + rv = exprmalloc(8); + (void) strcpy(rv, ltoa(i)); + return (rv); +} +#endif /* _iBCS2 */ + +static char * +match(char *s, char *p) +{ + char *rv; + long val; /* XCU4 */ + + (void) strcpy(rv = exprmalloc(8), ltoa(val = (long)ematch(s, p))); + if (nbra /* && val != 0 */) { + rv = exprmalloc((unsigned)strlen(Mstring[0]) + 1); + (void) strcpy(rv, Mstring[0]); + } + return (rv); +} + + +/* + * ematch - XCU4 mods involve calling compile/advance which simulate + * the obsolete compile/advance functions using regcomp/regexec + */ +static int +ematch(char *s, char *p) +{ + static char *expbuf; + char *nexpbuf; + int num; +#ifdef XPG4 + int nmatch; /* number of matched bytes */ + char tempbuf[256]; + char *tmptr1 = 0; /* If tempbuf is not large enough */ + char *tmptr; + int nmbchars; /* number characters in multibyte string */ +#endif + + nexpbuf = compile(p, (char *)0, (char *)0); /* XCU4 regex mod */ + if (0 /* XXX nbra > 1*/) + yyerror("Too many '\\('s"); + if (regerrno) { + if (regerrno != 41 || expbuf == NULL) + errxx(); + } else { + if (expbuf) + free(expbuf); + expbuf = nexpbuf; + } + if (advance(s, expbuf)) { + if (nbra > 0) { + p = braslist[0]; + num = braelist[0] - p; + if ((num > MSIZE - 1) || (num < 0)) + yyerror("string too long"); + (void) strncpy(Mstring[0], p, num); + Mstring[0][num] = '\0'; + } +#ifdef XPG4 + /* + * Use mbstowcs to find the number of multibyte characters + * in the multibyte string beginning at s, and + * ending at loc2. Create a separate string + * of the substring, so it can be passed to mbstowcs. + */ + nmatch = loc2 - s; + if (nmatch > ((sizeof (tempbuf) / sizeof (char)) - 1)) { + tmptr1 = exprmalloc(nmatch + 1); + tmptr = tmptr1; + } else { + tmptr = tempbuf; + } + memcpy(tmptr, s, nmatch); + *(tmptr + nmatch) = '\0'; + if ((nmbchars = mbstowcs(NULL, tmptr, NULL)) == -1) { + yyerror("invalid multibyte character encountered"); + if (tmptr1 != NULL) + free(tmptr1); + return (0); + } + if (tmptr1 != NULL) + free(tmptr1); + return (nmbchars); +#else + return (loc2-s); +#endif + } + return (0); +} + +static void +errxx() +{ + yyerror("RE error"); +} + +static void +yyerror(char *s) +{ +#ifdef XPG6 + /* XPG6: stdout will always contain newline even on error */ + (void) write(1, "\n", 1); +#endif + (void) write(2, "expr: ", 6); + (void) write(2, gettext(s), (unsigned)strlen(gettext(s))); + (void) write(2, "\n", 1); + exit(2); + /* NOTREACHED */ +} + +static char * +ltoa(long l) +{ + static char str[20]; + char *sp = &str[18]; /* u370 */ + int i; + int neg = 0; + + if ((unsigned long)l == 0x80000000UL) + return ("-2147483648"); + if (l < 0) + ++neg, l = -l; + str[19] = '\0'; + do { + i = l % 10; + *sp-- = '0' + i; + l /= 10; + } while (l); + if (neg) + *sp-- = '-'; + return (++sp); +} + +static char * +lltoa(long long l) +{ + static char str[25]; + char *sp = &str[23]; + int i; + int neg = 0; + + if (l == 0x8000000000000000ULL) + return ("-9223372036854775808"); + if (l < 0) + ++neg, l = -l; + str[24] = '\0'; + do { + i = l % 10; + *sp-- = '0' + i; + l /= 10; + } while (l); + if (neg) + *sp-- = '-'; + return (++sp); +} + +static char * +expres(int prior, int par) +{ + int ylex, temp, op1; + char *r1, *ra, *rb, *rc; + ylex = yylex(); + if (ylex >= NOARG && ylex < MATCH) { + yyerror("syntax error"); + } + if (ylex == A_STRING) { + r1 = Av[Argi++]; + temp = Argi; + } else { + if (ylex == '(') { + paren++; + Argi++; + r1 = expres(0, Argi); + Argi--; + } + } +lop: + ylex = yylex(); + if (ylex > NOARG && ylex < MATCH) { + op1 = ylex; + Argi++; + if (pri[op1-OR] <= prior) + return (r1); + else { + switch (op1) { + case OR: + case AND: + r1 = conj(op1, r1, expres(pri[op1-OR], 0)); + break; + case EQ: + case LT: + case GT: + case LEQ: + case GEQ: + case NEQ: + r1 = rel(op1, r1, expres(pri[op1-OR], 0)); + break; + case ADD: + case SUBT: + case MULT: + case DIV: + case REM: + r1 = arith(op1, r1, expres(pri[op1-OR], 0)); + break; + case MCH: + r1 = match(r1, expres(pri[op1-OR], 0)); + break; + } + if (noarg == 1) { + return (r1); + } + Argi--; + goto lop; + } + } + ylex = yylex(); + if (ylex == ')') { + if (par == Argi) { + yyerror("syntax error"); + } + if (par != 0) { + paren--; + Argi++; + } + Argi++; + return (r1); + } + ylex = yylex(); +#ifdef _iBCS2 + if (ylex > MCH && ((sysv3_set && ylex <= INDEX) || ylex <= MATCH)) { +#else + if (ylex > MCH && ylex <= MATCH) { +#endif /* _iBCS2 */ + if (Argi == temp) { + return (r1); + } + op1 = ylex; + Argi++; + switch (op1) { + case MATCH: + rb = expres(pri[op1-OR], 0); + ra = expres(pri[op1-OR], 0); + break; +#ifdef _iBCS2 + case SUBSTR: + rc = expres(pri[op1-OR], 0); + rb = expres(pri[op1-OR], 0); + ra = expres(pri[op1-OR], 0); + break; + case LENGTH: + ra = expres(pri[op1-OR], 0); + break; + case INDEX: + rb = expres(pri[op1-OR], 0); + ra = expres(pri[op1-OR], 0); + break; +#endif /* _iBCS2 */ + } + switch (op1) { + case MATCH: + r1 = match(rb, ra); + break; +#ifdef _iBCS2 + case SUBSTR: + r1 = substr(rc, rb, ra); + break; + case LENGTH: + r1 = length(ra); + break; + case INDEX: + r1 = index(rb, ra); + break; +#endif /* _iBCS2 */ + } + if (noarg == 1) { + return (r1); + } + Argi--; + goto lop; + } + ylex = yylex(); + if (ylex == NOARG) { + noarg = 1; + } + return (r1); +} + +void * +exprmalloc(size_t size) +{ + void *rv; + + if ((rv = malloc(size)) == NULL) { + char *s = gettext("malloc error"); + + (void) write(2, "expr: ", 6); + (void) write(2, s, (unsigned)strlen(s)); + (void) write(2, "\n", 1); + exit(3); + } + return (rv); +} + +int +main(int argc, char **argv) +{ + /* + * XCU4 allow "--" as argument + */ + if (argc > 1 && strcmp(argv[1], "--") == 0) + argv++, argc--; + /* + * XCU4 - print usage message when invoked without args + */ + if (argc < 2) { +#ifdef XPG6 + /* XPG6: stdout will always contain newline even on error */ + (void) write(1, "\n", 1); +#endif + (void) fprintf(stderr, gettext("Usage: expr expression\n")); + exit(3); + } + Ac = argc; + Argi = 1; + noarg = 0; + paren = 0; + Av = argv; +#ifdef _iBCS2 + sysv3_set = getenv("SYSV3"); +#endif /* _iBCS2 */ + + (void) setlocale(LC_ALL, ""); +#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ +#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ +#endif + (void) textdomain(TEXT_DOMAIN); + buf = expres(0, 1); + if (Ac != Argi || paren != 0) { + yyerror("syntax error"); + } + /* + * XCU4 - strip leading zeros from numeric output + */ + clean_buf(buf); + (void) write(1, buf, (unsigned)strlen(buf)); + (void) write(1, "\n", 1); + return ((strcmp(buf, "0") == 0 || buf[0] == 0) ? 1 : 0); +} diff --git a/usr/src/cmd/expr/expr.xcl b/usr/src/cmd/expr/expr.xcl new file mode 100644 index 0000000000..fb2c70cd86 --- /dev/null +++ b/usr/src/cmd/expr/expr.xcl @@ -0,0 +1,45 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +msgid "" +msgid "!=" +msgid "%" +msgid "&" +msgid "*" +msgid "+" +msgid "-" +msgid "-2147483648" +msgid "-\\{0,1\\}[0-9]*$" +msgid "/" +msgid "0" +msgid "1" +msgid ":" +msgid "<" +msgid "<=" +msgid "=" +msgid "==" +msgid ">" +msgid ">=" +msgid "\0" +msgid "\n" +msgid "expr: " +msgid "match" +msgid "|" |
