PageRenderTime 25ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/bin/expr/expr.y

https://gitlab.com/storedmirrors/minix
Happy | 457 lines | 406 code | 51 blank | 0 comment | 0 complexity | 82fb45f8fc2df6050edc360961ebf91b MD5 | raw file
  1. /* $NetBSD: expr.y,v 1.39 2016/09/05 01:00:07 sevan Exp $ */
  2. /*_
  3. * Copyright (c) 2000 The NetBSD Foundation, Inc.
  4. * All rights reserved.
  5. *
  6. * This code is derived from software contributed to The NetBSD Foundation
  7. * by Jaromir Dolecek <jdolecek@NetBSD.org> and J.T. Conklin <jtc@NetBSD.org>.
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions
  11. * are met:
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in the
  16. * documentation and/or other materials provided with the distribution.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  19. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  20. * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  21. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
  22. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  23. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  24. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  25. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  26. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. * POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. %{
  31. #include <sys/cdefs.h>
  32. #ifndef lint
  33. __RCSID("$NetBSD: expr.y,v 1.39 2016/09/05 01:00:07 sevan Exp $");
  34. #endif /* not lint */
  35. #include <sys/types.h>
  36. #include <err.h>
  37. #include <errno.h>
  38. #include <limits.h>
  39. #include <locale.h>
  40. #include <regex.h>
  41. #include <stdarg.h>
  42. #include <stdio.h>
  43. #include <stdlib.h>
  44. #include <string.h>
  45. static const char * const *av;
  46. static void yyerror(const char *, ...) __dead;
  47. static int yylex(void);
  48. static int is_zero_or_null(const char *);
  49. static int is_integer(const char *);
  50. static int64_t perform_arith_op(const char *, const char *, const char *);
  51. #define YYSTYPE const char *
  52. %}
  53. %token STRING
  54. %left SPEC_OR
  55. %left SPEC_AND
  56. %left COMPARE
  57. %left ADD_SUB_OPERATOR
  58. %left MUL_DIV_MOD_OPERATOR
  59. %left SPEC_REG
  60. %left LENGTH
  61. %left LEFT_PARENT RIGHT_PARENT
  62. %%
  63. exp: expr = {
  64. (void) printf("%s\n", $1);
  65. return (is_zero_or_null($1));
  66. }
  67. ;
  68. expr: item { $$ = $1; }
  69. | expr SPEC_OR expr = {
  70. /*
  71. * Return evaluation of first expression if it is neither
  72. * an empty string nor zero; otherwise, returns the evaluation
  73. * of second expression.
  74. */
  75. if (!is_zero_or_null($1))
  76. $$ = $1;
  77. else
  78. $$ = $3;
  79. }
  80. | expr SPEC_AND expr = {
  81. /*
  82. * Returns the evaluation of first expr if neither expression
  83. * evaluates to an empty string or zero; otherwise, returns
  84. * zero.
  85. */
  86. if (!is_zero_or_null($1) && !is_zero_or_null($3))
  87. $$ = $1;
  88. else
  89. $$ = "0";
  90. }
  91. | expr SPEC_REG expr = {
  92. /*
  93. * The ``:'' operator matches first expr against the second,
  94. * which must be a regular expression.
  95. */
  96. regex_t rp;
  97. regmatch_t rm[2];
  98. int eval;
  99. /* compile regular expression */
  100. if ((eval = regcomp(&rp, $3, REG_BASIC)) != 0) {
  101. char errbuf[256];
  102. (void)regerror(eval, &rp, errbuf, sizeof(errbuf));
  103. yyerror("%s", errbuf);
  104. /* NOT REACHED */
  105. }
  106. /* compare string against pattern -- remember that patterns
  107. are anchored to the beginning of the line */
  108. if (regexec(&rp, $1, 2, rm, 0) == 0 && rm[0].rm_so == 0) {
  109. char *val;
  110. if (rm[1].rm_so >= 0) {
  111. (void) asprintf(&val, "%.*s",
  112. (int) (rm[1].rm_eo - rm[1].rm_so),
  113. $1 + rm[1].rm_so);
  114. } else {
  115. (void) asprintf(&val, "%d",
  116. (int)(rm[0].rm_eo - rm[0].rm_so));
  117. }
  118. if (val == NULL)
  119. err(1, NULL);
  120. $$ = val;
  121. } else {
  122. if (rp.re_nsub == 0) {
  123. $$ = "0";
  124. } else {
  125. $$ = "";
  126. }
  127. }
  128. }
  129. | expr ADD_SUB_OPERATOR expr = {
  130. /* Returns the results of addition, subtraction */
  131. char *val;
  132. int64_t res;
  133. res = perform_arith_op($1, $2, $3);
  134. (void) asprintf(&val, "%lld", (long long int) res);
  135. if (val == NULL)
  136. err(1, NULL);
  137. $$ = val;
  138. }
  139. | expr MUL_DIV_MOD_OPERATOR expr = {
  140. /*
  141. * Returns the results of multiply, divide or remainder of
  142. * numeric-valued arguments.
  143. */
  144. char *val;
  145. int64_t res;
  146. res = perform_arith_op($1, $2, $3);
  147. (void) asprintf(&val, "%lld", (long long int) res);
  148. if (val == NULL)
  149. err(1, NULL);
  150. $$ = val;
  151. }
  152. | expr COMPARE expr = {
  153. /*
  154. * Returns the results of integer comparison if both arguments
  155. * are integers; otherwise, returns the results of string
  156. * comparison using the locale-specific collation sequence.
  157. * The result of each comparison is 1 if the specified relation
  158. * is true, or 0 if the relation is false.
  159. */
  160. int64_t l, r;
  161. int res;
  162. res = 0;
  163. /*
  164. * Slight hack to avoid differences in the compare code
  165. * between string and numeric compare.
  166. */
  167. if (is_integer($1) && is_integer($3)) {
  168. /* numeric comparison */
  169. l = strtoll($1, NULL, 10);
  170. r = strtoll($3, NULL, 10);
  171. } else {
  172. /* string comparison */
  173. l = strcoll($1, $3);
  174. r = 0;
  175. }
  176. switch($2[0]) {
  177. case '=': /* equal */
  178. res = (l == r);
  179. break;
  180. case '>': /* greater or greater-equal */
  181. if ($2[1] == '=')
  182. res = (l >= r);
  183. else
  184. res = (l > r);
  185. break;
  186. case '<': /* lower or lower-equal */
  187. if ($2[1] == '=')
  188. res = (l <= r);
  189. else
  190. res = (l < r);
  191. break;
  192. case '!': /* not equal */
  193. /* the check if this is != was done in yylex() */
  194. res = (l != r);
  195. }
  196. $$ = (res) ? "1" : "0";
  197. }
  198. | LEFT_PARENT expr RIGHT_PARENT { $$ = $2; }
  199. | LENGTH expr {
  200. /*
  201. * Return length of 'expr' in bytes.
  202. */
  203. char *ln;
  204. asprintf(&ln, "%ld", (long) strlen($2));
  205. if (ln == NULL)
  206. err(1, NULL);
  207. $$ = ln;
  208. }
  209. ;
  210. item: STRING
  211. | ADD_SUB_OPERATOR
  212. | MUL_DIV_MOD_OPERATOR
  213. | COMPARE
  214. | SPEC_OR
  215. | SPEC_AND
  216. | SPEC_REG
  217. | LENGTH
  218. ;
  219. %%
  220. /*
  221. * Returns 1 if the string is empty or contains only numeric zero.
  222. */
  223. static int
  224. is_zero_or_null(const char *str)
  225. {
  226. char *endptr;
  227. return str[0] == '\0'
  228. || ( strtoll(str, &endptr, 10) == 0LL
  229. && endptr[0] == '\0');
  230. }
  231. /*
  232. * Returns 1 if the string is an integer.
  233. */
  234. static int
  235. is_integer(const char *str)
  236. {
  237. char *endptr;
  238. (void) strtoll(str, &endptr, 10);
  239. /* note we treat empty string as valid number */
  240. return (endptr[0] == '\0');
  241. }
  242. static int64_t
  243. perform_arith_op(const char *left, const char *op, const char *right)
  244. {
  245. int64_t res, sign, l, r;
  246. u_int64_t temp;
  247. res = 0;
  248. if (!is_integer(left)) {
  249. yyerror("non-integer argument '%s'", left);
  250. /* NOTREACHED */
  251. }
  252. if (!is_integer(right)) {
  253. yyerror("non-integer argument '%s'", right);
  254. /* NOTREACHED */
  255. }
  256. errno = 0;
  257. l = strtoll(left, NULL, 10);
  258. if (errno == ERANGE) {
  259. yyerror("value '%s' is %s is %lld", left,
  260. (l > 0) ? "too big, maximum" : "too small, minimum",
  261. (l > 0) ? LLONG_MAX : LLONG_MIN);
  262. /* NOTREACHED */
  263. }
  264. errno = 0;
  265. r = strtoll(right, NULL, 10);
  266. if (errno == ERANGE) {
  267. yyerror("value '%s' is %s is %lld", right,
  268. (l > 0) ? "too big, maximum" : "too small, minimum",
  269. (l > 0) ? LLONG_MAX : LLONG_MIN);
  270. /* NOTREACHED */
  271. }
  272. switch(op[0]) {
  273. case '+':
  274. /*
  275. * Do the op into an unsigned to avoid overflow and then cast
  276. * back to check the resulting signage.
  277. */
  278. temp = l + r;
  279. res = (int64_t) temp;
  280. /* very simplistic check for over-& underflow */
  281. if ((res < 0 && l > 0 && r > 0)
  282. || (res > 0 && l < 0 && r < 0))
  283. yyerror("integer overflow or underflow occurred for "
  284. "operation '%s %s %s'", left, op, right);
  285. break;
  286. case '-':
  287. /*
  288. * Do the op into an unsigned to avoid overflow and then cast
  289. * back to check the resulting signage.
  290. */
  291. temp = l - r;
  292. res = (int64_t) temp;
  293. /* very simplistic check for over-& underflow */
  294. if ((res < 0 && l > 0 && l > r)
  295. || (res > 0 && l < 0 && l < r) )
  296. yyerror("integer overflow or underflow occurred for "
  297. "operation '%s %s %s'", left, op, right);
  298. break;
  299. case '/':
  300. if (r == 0)
  301. yyerror("second argument to '%s' must not be zero", op);
  302. res = l / r;
  303. break;
  304. case '%':
  305. if (r == 0)
  306. yyerror("second argument to '%s' must not be zero", op);
  307. res = l % r;
  308. break;
  309. case '*':
  310. /* shortcut */
  311. if ((l == 0) || (r == 0)) {
  312. res = 0;
  313. break;
  314. }
  315. sign = 1;
  316. if (l < 0)
  317. sign *= -1;
  318. if (r < 0)
  319. sign *= -1;
  320. res = l * r;
  321. /*
  322. * XXX: not the most portable but works on anything with 2's
  323. * complement arithmetic. If the signs don't match or the
  324. * result was 0 on 2's complement this overflowed.
  325. */
  326. if ((res < 0 && sign > 0) || (res > 0 && sign < 0) ||
  327. (res == 0))
  328. yyerror("integer overflow or underflow occurred for "
  329. "operation '%s %s %s'", left, op, right);
  330. /* NOTREACHED */
  331. break;
  332. }
  333. return res;
  334. }
  335. static const char *x = "|&=<>+-*/%:()";
  336. static const int x_token[] = {
  337. SPEC_OR, SPEC_AND, COMPARE, COMPARE, COMPARE, ADD_SUB_OPERATOR,
  338. ADD_SUB_OPERATOR, MUL_DIV_MOD_OPERATOR, MUL_DIV_MOD_OPERATOR,
  339. MUL_DIV_MOD_OPERATOR, SPEC_REG, LEFT_PARENT, RIGHT_PARENT
  340. };
  341. static int handle_ddash = 1;
  342. int
  343. yylex(void)
  344. {
  345. const char *p = *av++;
  346. int retval;
  347. if (!p)
  348. retval = 0;
  349. else if (p[1] == '\0') {
  350. const char *w = strchr(x, p[0]);
  351. if (w) {
  352. retval = x_token[w-x];
  353. } else {
  354. retval = STRING;
  355. }
  356. } else if (p[1] == '=' && p[2] == '\0'
  357. && (p[0] == '>' || p[0] == '<' || p[0] == '!'))
  358. retval = COMPARE;
  359. else if (handle_ddash && p[0] == '-' && p[1] == '-' && p[2] == '\0') {
  360. /* ignore "--" if passed as first argument and isn't followed
  361. * by another STRING */
  362. retval = yylex();
  363. if (retval != STRING && retval != LEFT_PARENT
  364. && retval != RIGHT_PARENT) {
  365. /* is not followed by string or parenthesis, use as
  366. * STRING */
  367. retval = STRING;
  368. av--; /* was increased in call to yylex() above */
  369. p = "--";
  370. } else {
  371. /* "--" is to be ignored */
  372. p = yylval;
  373. }
  374. } else if (strcmp(p, "length") == 0)
  375. retval = LENGTH;
  376. else
  377. retval = STRING;
  378. handle_ddash = 0;
  379. yylval = p;
  380. return retval;
  381. }
  382. /*
  383. * Print error message and exit with error 2 (syntax error).
  384. */
  385. static __printflike(1, 2) void
  386. yyerror(const char *fmt, ...)
  387. {
  388. va_list arg;
  389. va_start(arg, fmt);
  390. verrx(2, fmt, arg);
  391. va_end(arg);
  392. }
  393. int
  394. main(int argc, const char * const *argv)
  395. {
  396. setprogname(argv[0]);
  397. (void)setlocale(LC_ALL, "");
  398. if (argc == 1) {
  399. (void)fprintf(stderr, "usage: %s expression\n",
  400. getprogname());
  401. exit(2);
  402. }
  403. av = argv + 1;
  404. exit(yyparse());
  405. /* NOTREACHED */
  406. }