summaryrefslogtreecommitdiff
path: root/usr/src/cmd/expr/expr.c
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/expr.c
downloadillumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/expr/expr.c')
-rw-r--r--usr/src/cmd/expr/expr.c712
1 files changed, 712 insertions, 0 deletions
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);
+}