Fix NULL pointer constant issues in cmd
[unleashed.git] / usr / src / cmd / expr / expr.c
blob0f4df1bc13d04749c02a31daed365b9fa78b77b4
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
21 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
22 /* All Rights Reserved */
26 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
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>
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
61 /* size of subexpression array */
62 #define MSIZE LINE_MAX
63 #define error(c) errxx()
64 #define EQL(x, y) (strcmp(x, y) == 0)
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);
73 long atol();
74 char *strcpy(), *strncpy();
75 void exit();
77 static char *ltoa();
78 static char *lltoa();
79 static char **Av;
80 static char *buf;
81 static int Ac;
82 static int Argi;
83 static int noarg;
84 static int paren;
86 * Array used to store subexpressions in regular expressions
87 * Only one subexpression allowed per regular expression currently
89 static char Mstring[1][MSIZE];
92 static char *operator[] = {
93 "|", "&", "+", "-", "*", "/", "%", ":",
94 "=", "==", "<", "<=", ">", ">=", "!=",
95 "match",
96 "substr", "length", "index",
97 "\0" };
98 static int op[] = {
99 OR, AND, ADD, SUBT, MULT, DIV, REM, MCH,
100 EQ, EQ, LT, LEQ, GT, GEQ, NEQ,
101 MATCH,
102 SUBSTR, LENGTH, INDEX
104 static int pri[] = {
105 1, 2, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5, 5, 6, 7,
106 7, 7, 7
111 * clean_buf - XCU4 mod to remove leading zeros from negative signed
112 * numeric output, e.g., -00001 becomes -1
114 static void
115 clean_buf(buf)
116 char *buf;
118 int i = 0;
119 int is_a_num = 1;
120 int len;
121 long long num;
123 if (buf[0] == '\0')
124 return;
125 len = strlen(buf);
126 if (len <= 0)
127 return;
129 if (buf[0] == '-') {
130 i++; /* Skip the leading '-' see while loop */
131 if (len <= 1) /* Is it a '-' all by itself? */
132 return; /* Yes, so return */
134 while (i < len) {
135 if (! isdigit(buf[i])) {
136 is_a_num = 0;
137 break;
139 i++;
141 if (is_a_num) {
142 (void) sscanf(buf, "%lld", &num);
143 (void) sprintf(buf, "%lld", num);
149 * End XCU4 mods.
152 static int
153 yylex()
155 char *p;
156 int i;
158 if (Argi >= Ac)
159 return (NOARG);
161 p = Av[Argi];
163 if ((*p == '(' || *p == ')') && p[1] == '\0')
164 return ((int)*p);
165 for (i = 0; *operator[i]; ++i)
166 if (EQL(operator[i], p))
167 return (op[i]);
170 return (A_STRING);
173 static char
174 *rel(oper, r1, r2) register char *r1, *r2;
176 long long i, l1, l2;
178 if (ematch(r1, "-\\{0,1\\}[0-9]*$") &&
179 ematch(r2, "-\\{0,1\\}[0-9]*$")) {
180 errno = 0;
181 l1 = strtoll(r1, (char **)NULL, 10);
182 l2 = strtoll(r2, (char **)NULL, 10);
183 if (errno) {
184 #ifdef XPG6
185 /* XPG6: stdout will always contain newline even on error */
186 (void) write(1, "\n", 1);
187 #endif
188 if (errno == ERANGE) {
189 (void) fprintf(stderr, gettext(
190 "expr: Integer argument too large\n"));
191 exit(3);
192 } else {
193 perror("expr");
194 exit(3);
197 switch (oper) {
198 case EQ:
199 i = (l1 == l2);
200 break;
201 case GT:
202 i = (l1 > l2);
203 break;
204 case GEQ:
205 i = (l1 >= l2);
206 break;
207 case LT:
208 i = (l1 < l2);
209 break;
210 case LEQ:
211 i = (l1 <= l2);
212 break;
213 case NEQ:
214 i = (l1 != l2);
215 break;
218 else
220 i = strcoll(r1, r2);
221 switch (oper) {
222 case EQ:
223 i = i == 0;
224 break;
225 case GT:
226 i = i > 0;
227 break;
228 case GEQ:
229 i = i >= 0;
230 break;
231 case LT:
232 i = i < 0;
233 break;
234 case LEQ:
235 i = i <= 0;
236 break;
237 case NEQ:
238 i = i != 0;
239 break;
242 return (i ? "1": "0");
245 static char
246 *arith(oper, r1, r2) char *r1, *r2;
248 long long i1, i2;
249 register char *rv;
251 if (!(ematch(r1, "-\\{0,1\\}[0-9]*$") &&
252 ematch(r2, "-\\{0,1\\}[0-9]*$")))
253 yyerror("non-numeric argument");
254 errno = 0;
255 i1 = strtoll(r1, (char **)NULL, 10);
256 i2 = strtoll(r2, (char **)NULL, 10);
257 if (errno) {
258 #ifdef XPG6
259 /* XPG6: stdout will always contain newline even on error */
260 (void) write(1, "\n", 1);
261 #endif
262 if (errno == ERANGE) {
263 (void) fprintf(stderr, gettext(
264 "expr: Integer argument too large\n"));
265 exit(3);
266 } else {
267 perror("expr");
268 exit(3);
272 switch (oper) {
273 case ADD:
274 i1 = i1 + i2;
275 break;
276 case SUBT:
277 i1 = i1 - i2;
278 break;
279 case MULT:
280 i1 = i1 * i2;
281 break;
282 case DIV:
283 if (i2 == 0)
284 yyerror("division by zero");
285 i1 = i1 / i2;
286 break;
287 case REM:
288 if (i2 == 0)
289 yyerror("division by zero");
290 i1 = i1 % i2;
291 break;
293 rv = exprmalloc(25);
294 (void) strcpy(rv, lltoa(i1));
295 return (rv);
298 static char
299 *conj(oper, r1, r2)
300 char *r1, *r2;
302 register char *rv;
304 switch (oper) {
306 case OR:
307 if (EQL(r1, "0") || EQL(r1, "")) {
308 if (EQL(r2, "0") || EQL(r2, ""))
309 rv = "0";
310 else
311 rv = r2;
312 } else
313 rv = r1;
314 break;
315 case AND:
316 if (EQL(r1, "0") || EQL(r1, ""))
317 rv = "0";
318 else if (EQL(r2, "0") || EQL(r2, ""))
319 rv = "0";
320 else
321 rv = r1;
322 break;
324 return (rv);
327 char *
328 substr(char *v, char *s, char *w)
330 int si, wi;
331 char *res;
333 si = atol(s);
334 wi = atol(w);
335 while (--si)
336 if (*v) ++v;
338 res = v;
340 while (wi--)
341 if (*v) ++v;
343 *v = '\0';
344 return (res);
347 char *
348 index(char *s, char *t)
350 long i, j;
351 char *rv;
353 for (i = 0; s[i]; ++i)
354 for (j = 0; t[j]; ++j)
355 if (s[i] == t[j]) {
356 (void) strcpy(rv = exprmalloc(8), ltoa(++i));
357 return (rv);
359 return ("0");
362 char *
363 length(char *s)
365 long i = 0;
366 char *rv;
368 while (*s++) ++i;
370 rv = exprmalloc(8);
371 (void) strcpy(rv, ltoa(i));
372 return (rv);
375 static char *
376 match(char *s, char *p)
378 char *rv;
379 long val; /* XCU4 */
381 (void) strcpy(rv = exprmalloc(8), ltoa(val = (long)ematch(s, p)));
382 if (nbra /* && val != 0 */) {
383 rv = exprmalloc((unsigned)strlen(Mstring[0]) + 1);
384 (void) strcpy(rv, Mstring[0]);
386 return (rv);
391 * ematch - XCU4 mods involve calling compile/advance which simulate
392 * the obsolete compile/advance functions using regcomp/regexec
394 static int
395 ematch(char *s, char *p)
397 static char *expbuf;
398 char *nexpbuf;
399 int num;
400 #ifdef XPG4
401 int nmatch; /* number of matched bytes */
402 char tempbuf[256];
403 char *tmptr1 = 0; /* If tempbuf is not large enough */
404 char *tmptr;
405 int nmbchars; /* number characters in multibyte string */
406 #endif
408 nexpbuf = compile(p, (char *)0, (char *)0); /* XCU4 regex mod */
409 if (0 /* XXX nbra > 1*/)
410 yyerror("Too many '\\('s");
411 if (regerrno) {
412 if (regerrno != 41 || expbuf == NULL)
413 errxx();
414 } else {
415 free(expbuf);
416 expbuf = nexpbuf;
418 if (advance(s, expbuf)) {
419 if (nbra > 0) {
420 p = braslist[0];
421 num = braelist[0] - p;
422 if ((num > MSIZE - 1) || (num < 0))
423 yyerror("string too long");
424 (void) strncpy(Mstring[0], p, num);
425 Mstring[0][num] = '\0';
427 #ifdef XPG4
429 * Use mbstowcs to find the number of multibyte characters
430 * in the multibyte string beginning at s, and
431 * ending at loc2. Create a separate string
432 * of the substring, so it can be passed to mbstowcs.
434 nmatch = loc2 - s;
435 if (nmatch > ((sizeof (tempbuf) / sizeof (char)) - 1)) {
436 tmptr1 = exprmalloc(nmatch + 1);
437 tmptr = tmptr1;
438 } else {
439 tmptr = tempbuf;
441 memcpy(tmptr, s, nmatch);
442 *(tmptr + nmatch) = '\0';
443 if ((nmbchars = mbstowcs(NULL, tmptr, 0)) == -1) {
444 yyerror("invalid multibyte character encountered");
445 free(tmptr1);
446 return (0);
448 free(tmptr1);
449 return (nmbchars);
450 #else
451 return (loc2-s);
452 #endif
454 return (0);
457 static void
458 errxx()
460 yyerror("RE error");
463 static void
464 yyerror(char *s)
466 #ifdef XPG6
467 /* XPG6: stdout will always contain newline even on error */
468 (void) write(1, "\n", 1);
469 #endif
470 (void) write(2, "expr: ", 6);
471 (void) write(2, gettext(s), (unsigned)strlen(gettext(s)));
472 (void) write(2, "\n", 1);
473 exit(2);
474 /* NOTREACHED */
477 static char *
478 ltoa(long l)
480 static char str[20];
481 char *sp = &str[18]; /* u370 */
482 int i;
483 int neg = 0;
485 if ((unsigned long)l == 0x80000000UL)
486 return ("-2147483648");
487 if (l < 0)
488 ++neg, l = -l;
489 str[19] = '\0';
490 do {
491 i = l % 10;
492 *sp-- = '0' + i;
493 l /= 10;
494 } while (l);
495 if (neg)
496 *sp-- = '-';
497 return (++sp);
500 static char *
501 lltoa(long long l)
503 static char str[25];
504 char *sp = &str[23];
505 int i;
506 int neg = 0;
508 if (l == 0x8000000000000000ULL)
509 return ("-9223372036854775808");
510 if (l < 0)
511 ++neg, l = -l;
512 str[24] = '\0';
513 do {
514 i = l % 10;
515 *sp-- = '0' + i;
516 l /= 10;
517 } while (l);
518 if (neg)
519 *sp-- = '-';
520 return (++sp);
523 static char *
524 expres(int prior, int par)
526 int ylex, temp, op1;
527 char *r1, *ra, *rb, *rc;
528 ylex = yylex();
529 if (ylex >= NOARG && ylex < MATCH) {
530 yyerror("syntax error");
532 if (ylex == A_STRING) {
533 r1 = Av[Argi++];
534 temp = Argi;
535 } else {
536 if (ylex == '(') {
537 paren++;
538 Argi++;
539 r1 = expres(0, Argi);
540 Argi--;
543 lop:
544 ylex = yylex();
545 if (ylex > NOARG && ylex < MATCH) {
546 op1 = ylex;
547 Argi++;
548 if (pri[op1-OR] <= prior)
549 return (r1);
550 else {
551 switch (op1) {
552 case OR:
553 case AND:
554 r1 = conj(op1, r1, expres(pri[op1-OR], 0));
555 break;
556 case EQ:
557 case LT:
558 case GT:
559 case LEQ:
560 case GEQ:
561 case NEQ:
562 r1 = rel(op1, r1, expres(pri[op1-OR], 0));
563 break;
564 case ADD:
565 case SUBT:
566 case MULT:
567 case DIV:
568 case REM:
569 r1 = arith(op1, r1, expres(pri[op1-OR], 0));
570 break;
571 case MCH:
572 r1 = match(r1, expres(pri[op1-OR], 0));
573 break;
575 if (noarg == 1) {
576 return (r1);
578 Argi--;
579 goto lop;
582 ylex = yylex();
583 if (ylex == ')') {
584 if (par == Argi) {
585 yyerror("syntax error");
587 if (par != 0) {
588 paren--;
589 Argi++;
591 Argi++;
592 return (r1);
594 ylex = yylex();
595 if (ylex > MCH && ylex <= INDEX) {
596 if (Argi == temp) {
597 return (r1);
599 op1 = ylex;
600 Argi++;
601 switch (op1) {
602 case MATCH:
603 rb = expres(pri[op1-OR], 0);
604 ra = expres(pri[op1-OR], 0);
605 break;
606 case SUBSTR:
607 rc = expres(pri[op1-OR], 0);
608 rb = expres(pri[op1-OR], 0);
609 ra = expres(pri[op1-OR], 0);
610 break;
611 case LENGTH:
612 ra = expres(pri[op1-OR], 0);
613 break;
614 case INDEX:
615 rb = expres(pri[op1-OR], 0);
616 ra = expres(pri[op1-OR], 0);
617 break;
619 switch (op1) {
620 case MATCH:
621 r1 = match(rb, ra);
622 break;
623 case SUBSTR:
624 r1 = substr(rc, rb, ra);
625 break;
626 case LENGTH:
627 r1 = length(ra);
628 break;
629 case INDEX:
630 r1 = index(rb, ra);
631 break;
633 if (noarg == 1) {
634 return (r1);
636 Argi--;
637 goto lop;
639 ylex = yylex();
640 if (ylex == NOARG) {
641 noarg = 1;
643 return (r1);
646 void *
647 exprmalloc(size_t size)
649 void *rv;
651 if ((rv = malloc(size)) == NULL) {
652 char *s = gettext("malloc error");
654 (void) write(2, "expr: ", 6);
655 (void) write(2, s, (unsigned)strlen(s));
656 (void) write(2, "\n", 1);
657 exit(3);
659 return (rv);
663 main(int argc, char **argv)
666 * XCU4 allow "--" as argument
668 if (argc > 1 && strcmp(argv[1], "--") == 0)
669 argv++, argc--;
671 * XCU4 - print usage message when invoked without args
673 if (argc < 2) {
674 #ifdef XPG6
675 /* XPG6: stdout will always contain newline even on error */
676 (void) write(1, "\n", 1);
677 #endif
678 (void) fprintf(stderr, gettext("Usage: expr expression\n"));
679 exit(3);
681 Ac = argc;
682 Argi = 1;
683 noarg = 0;
684 paren = 0;
685 Av = argv;
687 (void) setlocale(LC_ALL, "");
688 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
689 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
690 #endif
691 (void) textdomain(TEXT_DOMAIN);
692 buf = expres(0, 1);
693 if (Ac != Argi || paren != 0) {
694 yyerror("syntax error");
697 * XCU4 - strip leading zeros from numeric output
699 clean_buf(buf);
700 (void) write(1, buf, (unsigned)strlen(buf));
701 (void) write(1, "\n", 1);
702 return ((strcmp(buf, "0") == 0 || buf[0] == 0) ? 1 : 0);