1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
22 /* All Rights Reserved */
23
24
25 /*
26 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
27 */
28
29 #include <stdlib.h>
30 #include <regexpr.h>
31 #include <locale.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <regex.h>
35 #include <limits.h>
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <errno.h>
39
40 #define A_STRING 258
41 #define NOARG 259
42 #define OR 260
43 #define AND 261
44 #define EQ 262
45 #define LT 263
46 #define GT 264
47 #define GEQ 265
48 #define LEQ 266
49 #define NEQ 267
50 #define ADD 268
51 #define SUBT 269
52 #define MULT 270
53 #define DIV 271
54 #define REM 272
55 #define MCH 273
56 #define MATCH 274
57 #define SUBSTR 275
58 #define LENGTH 276
59 #define INDEX 277
60
61 /* size of subexpression array */
62 #define MSIZE LINE_MAX
63 #define error(c) errxx()
64 #define EQL(x, y) (strcmp(x, y) == 0)
65
66 #define ERROR(c) errxx()
67 #define MAX_MATCH 20
68 static int ematch(char *, char *);
69 static void yyerror(char *);
70 static void errxx();
71 static void *exprmalloc(size_t size);
72
73 static char *ltoa();
74 static char *lltoa();
75 static char **Av;
76 static char *buf;
77 static int Ac;
78 static int Argi;
79 static int noarg;
80 static int paren;
81 /*
82 * Array used to store subexpressions in regular expressions
83 * Only one subexpression allowed per regular expression currently
84 */
85 static char Mstring[1][MSIZE];
86
87
88 static char *operator[] = {
89 "|", "&", "+", "-", "*", "/", "%", ":",
90 "=", "==", "<", "<=", ">", ">=", "!=",
91 "match",
92 "substr", "length", "index",
93 "\0" };
94 static int op[] = {
95 OR, AND, ADD, SUBT, MULT, DIV, REM, MCH,
96 EQ, EQ, LT, LEQ, GT, GEQ, NEQ,
97 MATCH,
98 SUBSTR, LENGTH, INDEX
99 };
100 static int pri[] = {
101 1, 2, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5, 5, 6, 7,
102 7, 7, 7
103 };
104
105
106 /*
107 * clean_buf - XCU4 mod to remove leading zeros from negative signed
108 * numeric output, e.g., -00001 becomes -1
109 */
110 static void
clean_buf(char * buf)111 clean_buf(char *buf)
112 {
113 int i = 0;
114 int is_a_num = 1;
115 int len;
116 long long num;
117
118 if (buf[0] == '\0')
119 return;
120 len = strlen(buf);
121 if (len <= 0)
122 return;
123
124 if (buf[0] == '-') {
125 i++; /* Skip the leading '-' see while loop */
126 if (len <= 1) /* Is it a '-' all by itself? */
127 return; /* Yes, so return */
128
129 while (i < len) {
130 if (! isdigit(buf[i])) {
131 is_a_num = 0;
132 break;
133 }
134 i++;
135 }
136 if (is_a_num) {
137 (void) sscanf(buf, "%lld", &num);
138 (void) sprintf(buf, "%lld", num);
139 }
140 }
141 }
142
143 /*
144 * End XCU4 mods.
145 */
146
147 static int
yylex(void)148 yylex(void)
149 {
150 char *p;
151 int i;
152
153 if (Argi >= Ac)
154 return (NOARG);
155
156 p = Av[Argi];
157
158 if ((*p == '(' || *p == ')') && p[1] == '\0')
159 return ((int)*p);
160 for (i = 0; *operator[i]; ++i)
161 if (EQL(operator[i], p))
162 return (op[i]);
163
164
165 return (A_STRING);
166 }
167
168 static char *
rel(int oper,char * r1,char * r2)169 rel(int oper, char *r1, char *r2)
170 {
171 long long i, l1, l2;
172
173 if (ematch(r1, "-\\{0,1\\}[0-9]*$") &&
174 ematch(r2, "-\\{0,1\\}[0-9]*$")) {
175 errno = 0;
176 l1 = strtoll(r1, (char **)NULL, 10);
177 l2 = strtoll(r2, (char **)NULL, 10);
178 if (errno) {
179 #ifdef XPG6
180 /* XPG6: stdout will always contain newline even on error */
181 (void) write(1, "\n", 1);
182 #endif
183 if (errno == ERANGE) {
184 (void) fprintf(stderr, gettext(
185 "expr: Integer argument too large\n"));
186 exit(3);
187 } else {
188 perror("expr");
189 exit(3);
190 }
191 }
192 switch (oper) {
193 case EQ:
194 i = (l1 == l2);
195 break;
196 case GT:
197 i = (l1 > l2);
198 break;
199 case GEQ:
200 i = (l1 >= l2);
201 break;
202 case LT:
203 i = (l1 < l2);
204 break;
205 case LEQ:
206 i = (l1 <= l2);
207 break;
208 case NEQ:
209 i = (l1 != l2);
210 break;
211 }
212 }
213 else
214 {
215 i = strcoll(r1, r2);
216 switch (oper) {
217 case EQ:
218 i = i == 0;
219 break;
220 case GT:
221 i = i > 0;
222 break;
223 case GEQ:
224 i = i >= 0;
225 break;
226 case LT:
227 i = i < 0;
228 break;
229 case LEQ:
230 i = i <= 0;
231 break;
232 case NEQ:
233 i = i != 0;
234 break;
235 }
236 }
237 return (i ? "1": "0");
238 }
239
240 static char *
arith(int oper,char * r1,char * r2)241 arith(int oper, char *r1, char *r2)
242 {
243 long long i1, i2;
244 char *rv;
245
246 if (!(ematch(r1, "-\\{0,1\\}[0-9]*$") &&
247 ematch(r2, "-\\{0,1\\}[0-9]*$")))
248 yyerror("non-numeric argument");
249 errno = 0;
250 i1 = strtoll(r1, (char **)NULL, 10);
251 i2 = strtoll(r2, (char **)NULL, 10);
252 if (errno) {
253 #ifdef XPG6
254 /* XPG6: stdout will always contain newline even on error */
255 (void) write(1, "\n", 1);
256 #endif
257 if (errno == ERANGE) {
258 (void) fprintf(stderr, gettext(
259 "expr: Integer argument too large\n"));
260 exit(3);
261 } else {
262 perror("expr");
263 exit(3);
264 }
265 }
266
267 switch (oper) {
268 case ADD:
269 i1 = i1 + i2;
270 break;
271 case SUBT:
272 i1 = i1 - i2;
273 break;
274 case MULT:
275 i1 = i1 * i2;
276 break;
277 case DIV:
278 if (i2 == 0)
279 yyerror("division by zero");
280 i1 = i1 / i2;
281 break;
282 case REM:
283 if (i2 == 0)
284 yyerror("division by zero");
285 i1 = i1 % i2;
286 break;
287 }
288 rv = exprmalloc(25);
289 (void) strcpy(rv, lltoa(i1));
290 return (rv);
291 }
292
293 static char
conj(int oper,char * r1,char * r2)294 *conj(int oper, char *r1, char *r2)
295 {
296 char *rv;
297
298 switch (oper) {
299
300 case OR:
301 if (EQL(r1, "0") || EQL(r1, "")) {
302 if (EQL(r2, "0") || EQL(r2, ""))
303 rv = "0";
304 else
305 rv = r2;
306 } else
307 rv = r1;
308 break;
309 case AND:
310 if (EQL(r1, "0") || EQL(r1, ""))
311 rv = "0";
312 else if (EQL(r2, "0") || EQL(r2, ""))
313 rv = "0";
314 else
315 rv = r1;
316 break;
317 }
318 return (rv);
319 }
320
321 char *
substr(char * v,char * s,char * w)322 substr(char *v, char *s, char *w)
323 {
324 int si, wi;
325 char *res;
326
327 si = atol(s);
328 wi = atol(w);
329 while (--si)
330 if (*v) ++v;
331
332 res = v;
333
334 while (wi--)
335 if (*v) ++v;
336
337 *v = '\0';
338 return (res);
339 }
340
341 char *
index(char * s,char * t)342 index(char *s, char *t)
343 {
344 long i, j;
345 char *rv;
346
347 for (i = 0; s[i]; ++i)
348 for (j = 0; t[j]; ++j)
349 if (s[i] == t[j]) {
350 (void) strcpy(rv = exprmalloc(8), ltoa(++i));
351 return (rv);
352 }
353 return ("0");
354 }
355
356 char *
length(char * s)357 length(char *s)
358 {
359 long i = 0;
360 char *rv;
361
362 while (*s++) ++i;
363
364 rv = exprmalloc(8);
365 (void) strcpy(rv, ltoa(i));
366 return (rv);
367 }
368
369 static char *
match(char * s,char * p)370 match(char *s, char *p)
371 {
372 char *rv;
373 long val; /* XCU4 */
374
375 (void) strcpy(rv = exprmalloc(8), ltoa(val = (long)ematch(s, p)));
376 if (nbra /* && val != 0 */) {
377 rv = exprmalloc((unsigned)strlen(Mstring[0]) + 1);
378 (void) strcpy(rv, Mstring[0]);
379 }
380 return (rv);
381 }
382
383
384 /*
385 * ematch - XCU4 mods involve calling compile/advance which simulate
386 * the obsolete compile/advance functions using regcomp/regexec
387 */
388 static int
ematch(char * s,char * p)389 ematch(char *s, char *p)
390 {
391 static char *expbuf;
392 char *nexpbuf;
393 int num;
394 #ifdef XPG4
395 int nmatch; /* number of matched bytes */
396 char tempbuf[256];
397 char *tmptr1 = 0; /* If tempbuf is not large enough */
398 char *tmptr;
399 int nmbchars; /* number characters in multibyte string */
400 #endif
401
402 nexpbuf = compile(p, (char *)0, (char *)0); /* XCU4 regex mod */
403 if (0 /* XXX nbra > 1*/)
404 yyerror("Too many '\\('s");
405 if (regerrno) {
406 if (regerrno != 41 || expbuf == NULL)
407 errxx();
408 } else {
409 if (expbuf)
410 free(expbuf);
411 expbuf = nexpbuf;
412 }
413 if (advance(s, expbuf)) {
414 if (nbra > 0) {
415 p = braslist[0];
416 num = braelist[0] - p;
417 if ((num > MSIZE - 1) || (num < 0))
418 yyerror("string too long");
419 (void) strncpy(Mstring[0], p, num);
420 Mstring[0][num] = '\0';
421 }
422 #ifdef XPG4
423 /*
424 * Use mbstowcs to find the number of multibyte characters
425 * in the multibyte string beginning at s, and
426 * ending at loc2. Create a separate string
427 * of the substring, so it can be passed to mbstowcs.
428 */
429 nmatch = loc2 - s;
430 if (nmatch > ((sizeof (tempbuf) / sizeof (char)) - 1)) {
431 tmptr1 = exprmalloc(nmatch + 1);
432 tmptr = tmptr1;
433 } else {
434 tmptr = tempbuf;
435 }
436 memcpy(tmptr, s, nmatch);
437 *(tmptr + nmatch) = '\0';
438 if ((nmbchars = mbstowcs(NULL, tmptr, 0)) == -1) {
439 yyerror("invalid multibyte character encountered");
440 if (tmptr1 != NULL)
441 free(tmptr1);
442 return (0);
443 }
444 if (tmptr1 != NULL)
445 free(tmptr1);
446 return (nmbchars);
447 #else
448 return (loc2-s);
449 #endif
450 }
451 return (0);
452 }
453
454 static void
errxx()455 errxx()
456 {
457 yyerror("RE error");
458 }
459
460 static void
yyerror(char * s)461 yyerror(char *s)
462 {
463 #ifdef XPG6
464 /* XPG6: stdout will always contain newline even on error */
465 (void) write(1, "\n", 1);
466 #endif
467 (void) write(2, "expr: ", 6);
468 (void) write(2, gettext(s), (unsigned)strlen(gettext(s)));
469 (void) write(2, "\n", 1);
470 exit(2);
471 /* NOTREACHED */
472 }
473
474 static char *
ltoa(long l)475 ltoa(long l)
476 {
477 static char str[20];
478 char *sp = &str[18]; /* u370 */
479 int i;
480 int neg = 0;
481
482 if ((unsigned long)l == 0x80000000UL)
483 return ("-2147483648");
484 if (l < 0)
485 ++neg, l = -l;
486 str[19] = '\0';
487 do {
488 i = l % 10;
489 *sp-- = '0' + i;
490 l /= 10;
491 } while (l);
492 if (neg)
493 *sp-- = '-';
494 return (++sp);
495 }
496
497 static char *
lltoa(long long l)498 lltoa(long long l)
499 {
500 static char str[25];
501 char *sp = &str[23];
502 int i;
503 int neg = 0;
504
505 if (l == 0x8000000000000000ULL)
506 return ("-9223372036854775808");
507 if (l < 0)
508 ++neg, l = -l;
509 str[24] = '\0';
510 do {
511 i = l % 10;
512 *sp-- = '0' + i;
513 l /= 10;
514 } while (l);
515 if (neg)
516 *sp-- = '-';
517 return (++sp);
518 }
519
520 static char *
expres(int prior,int par)521 expres(int prior, int par)
522 {
523 int ylex, temp, op1;
524 char *r1, *ra, *rb, *rc;
525 ylex = yylex();
526 if (ylex >= NOARG && ylex < MATCH) {
527 yyerror("syntax error");
528 }
529 if (ylex == A_STRING) {
530 r1 = Av[Argi++];
531 temp = Argi;
532 } else {
533 if (ylex == '(') {
534 paren++;
535 Argi++;
536 r1 = expres(0, Argi);
537 Argi--;
538 }
539 }
540 lop:
541 ylex = yylex();
542 if (ylex > NOARG && ylex < MATCH) {
543 op1 = ylex;
544 Argi++;
545 if (pri[op1-OR] <= prior)
546 return (r1);
547 else {
548 switch (op1) {
549 case OR:
550 case AND:
551 r1 = conj(op1, r1, expres(pri[op1-OR], 0));
552 break;
553 case EQ:
554 case LT:
555 case GT:
556 case LEQ:
557 case GEQ:
558 case NEQ:
559 r1 = rel(op1, r1, expres(pri[op1-OR], 0));
560 break;
561 case ADD:
562 case SUBT:
563 case MULT:
564 case DIV:
565 case REM:
566 r1 = arith(op1, r1, expres(pri[op1-OR], 0));
567 break;
568 case MCH:
569 r1 = match(r1, expres(pri[op1-OR], 0));
570 break;
571 }
572 if (noarg == 1) {
573 return (r1);
574 }
575 Argi--;
576 goto lop;
577 }
578 }
579 ylex = yylex();
580 if (ylex == ')') {
581 if (par == Argi) {
582 yyerror("syntax error");
583 }
584 if (par != 0) {
585 paren--;
586 Argi++;
587 }
588 Argi++;
589 return (r1);
590 }
591 ylex = yylex();
592 if (ylex > MCH && ylex <= INDEX) {
593 if (Argi == temp) {
594 return (r1);
595 }
596 op1 = ylex;
597 Argi++;
598 switch (op1) {
599 case MATCH:
600 rb = expres(pri[op1-OR], 0);
601 ra = expres(pri[op1-OR], 0);
602 break;
603 case SUBSTR:
604 rc = expres(pri[op1-OR], 0);
605 rb = expres(pri[op1-OR], 0);
606 ra = expres(pri[op1-OR], 0);
607 break;
608 case LENGTH:
609 ra = expres(pri[op1-OR], 0);
610 break;
611 case INDEX:
612 rb = expres(pri[op1-OR], 0);
613 ra = expres(pri[op1-OR], 0);
614 break;
615 }
616 switch (op1) {
617 case MATCH:
618 r1 = match(rb, ra);
619 break;
620 case SUBSTR:
621 r1 = substr(rc, rb, ra);
622 break;
623 case LENGTH:
624 r1 = length(ra);
625 break;
626 case INDEX:
627 r1 = index(rb, ra);
628 break;
629 }
630 if (noarg == 1) {
631 return (r1);
632 }
633 Argi--;
634 goto lop;
635 }
636 ylex = yylex();
637 if (ylex == NOARG) {
638 noarg = 1;
639 }
640 return (r1);
641 }
642
643 void *
exprmalloc(size_t size)644 exprmalloc(size_t size)
645 {
646 void *rv;
647
648 if ((rv = malloc(size)) == NULL) {
649 char *s = gettext("malloc error");
650
651 (void) write(2, "expr: ", 6);
652 (void) write(2, s, (unsigned)strlen(s));
653 (void) write(2, "\n", 1);
654 exit(3);
655 }
656 return (rv);
657 }
658
659 int
main(int argc,char ** argv)660 main(int argc, char **argv)
661 {
662 /*
663 * XCU4 allow "--" as argument
664 */
665 if (argc > 1 && strcmp(argv[1], "--") == 0)
666 argv++, argc--;
667 /*
668 * XCU4 - print usage message when invoked without args
669 */
670 if (argc < 2) {
671 #ifdef XPG6
672 /* XPG6: stdout will always contain newline even on error */
673 (void) write(1, "\n", 1);
674 #endif
675 (void) fprintf(stderr, gettext("Usage: expr expression\n"));
676 exit(3);
677 }
678 Ac = argc;
679 Argi = 1;
680 noarg = 0;
681 paren = 0;
682 Av = argv;
683
684 (void) setlocale(LC_ALL, "");
685 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
686 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
687 #endif
688 (void) textdomain(TEXT_DOMAIN);
689 buf = expres(0, 1);
690 if (Ac != Argi || paren != 0) {
691 yyerror("syntax error");
692 }
693 /*
694 * XCU4 - strip leading zeros from numeric output
695 */
696 clean_buf(buf);
697 (void) write(1, buf, (unsigned)strlen(buf));
698 (void) write(1, "\n", 1);
699 return ((strcmp(buf, "0") == 0 || buf[0] == 0) ? 1 : 0);
700 }
701