summaryrefslogtreecommitdiff
path: root/usr/src/cmd/expr
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/expr
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/expr')
-rw-r--r--usr/src/cmd/expr/Makefile93
-rw-r--r--usr/src/cmd/expr/compile.c495
-rw-r--r--usr/src/cmd/expr/expr.c712
-rw-r--r--usr/src/cmd/expr/expr.xcl45
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(&reg_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(&reg_comp, expbuf, regexc_size);
+ }
+
+ /*
+ * Clear global flags
+ */
+ nbra = 0;
+ regerrno = 0;
+
+ /*
+ * Free any data being held for previous search strings
+ */
+ regex_comp_free(&reg_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(&reg_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(&reg_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 *) &reg_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 "|"