| /* |
| * test.c - sh like test |
| * |
| * Originally based on bareboxs do_test, but mostly reimplemented |
| * for smaller binary size |
| * |
| * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix |
| * |
| * See file CREDITS for list of people who contributed to this |
| * project. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 |
| * as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| #include <common.h> |
| #include <command.h> |
| #include <fs.h> |
| #include <linux/stat.h> |
| |
| typedef enum { |
| OPT_EQUAL, |
| OPT_NOT_EQUAL, |
| OPT_ARITH_EQUAL, |
| OPT_ARITH_NOT_EQUAL, |
| OPT_ARITH_GREATER_EQUAL, |
| OPT_ARITH_GREATER_THAN, |
| OPT_ARITH_LESS_EQUAL, |
| OPT_ARITH_LESS_THAN, |
| OPT_OR, |
| OPT_AND, |
| OPT_ZERO, |
| OPT_NONZERO, |
| OPT_DIRECTORY, |
| OPT_FILE, |
| OPT_EXISTS, |
| OPT_MAX, |
| } test_opts; |
| |
| static char *test_options[] = { |
| [OPT_EQUAL] = "=", |
| [OPT_NOT_EQUAL] = "!=", |
| [OPT_ARITH_EQUAL] = "-eq", |
| [OPT_ARITH_NOT_EQUAL] = "-ne", |
| [OPT_ARITH_GREATER_EQUAL] = "-ge", |
| [OPT_ARITH_GREATER_THAN] = "-gt", |
| [OPT_ARITH_LESS_EQUAL] = "-le", |
| [OPT_ARITH_LESS_THAN] = "-lt", |
| [OPT_OR] = "-o", |
| [OPT_AND] = "-a", |
| [OPT_ZERO] = "-z", |
| [OPT_NONZERO] = "-n", |
| [OPT_FILE] = "-f", |
| [OPT_DIRECTORY] = "-d", |
| [OPT_EXISTS] = "-e", |
| }; |
| |
| static int parse_opt(const char *opt) |
| { |
| char **opts = test_options; |
| int i; |
| |
| for (i = 0; i < OPT_MAX; i++) { |
| if (!strcmp(opts[i], opt)) |
| return i; |
| } |
| return -1; |
| } |
| |
| static int do_test(struct command *cmdtp, int argc, char *argv[]) |
| { |
| char **ap; |
| int left, adv, expr, last_expr, neg, last_cmp, opt, zero; |
| ulong a, b; |
| struct stat statbuf; |
| |
| if (*argv[0] == '[') { |
| if (*argv[argc - 1] != ']') { |
| printf("[: missing `]'\n"); |
| return 1; |
| } |
| argc--; |
| } |
| |
| /* args? */ |
| if (argc < 2) |
| return 1; |
| |
| last_expr = 0; |
| left = argc - 1; |
| ap = argv + 1; |
| |
| if (strcmp(ap[0], "!") == 0) { |
| neg = 1; |
| ap++; |
| left--; |
| } else |
| neg = 0; |
| |
| expr = -1; |
| last_cmp = -1; |
| last_expr = -1; |
| adv = 0; |
| while (left - adv > 0) { |
| ap += adv; left -= adv; |
| |
| adv = 1; |
| opt = parse_opt(ap[0]); |
| switch (opt) { |
| /* one argument options */ |
| case OPT_OR: |
| last_expr = expr; |
| last_cmp = 0; |
| continue; |
| case OPT_AND: |
| last_expr = expr; |
| last_cmp = 1; |
| continue; |
| |
| /* two argument options */ |
| case OPT_ZERO: |
| case OPT_NONZERO: |
| adv = 2; |
| zero = 1; |
| if (ap[1] && *ap[1] != ']' && strlen(ap[1])) |
| zero = 0; |
| |
| expr = (opt == OPT_ZERO) ? zero : !zero; |
| break; |
| |
| case OPT_FILE: |
| case OPT_DIRECTORY: |
| case OPT_EXISTS: |
| adv = 2; |
| if (ap[1] && *ap[1] != ']' && strlen(ap[1])) { |
| expr = stat(ap[1], &statbuf); |
| if (expr < 0) { |
| expr = 0; |
| break; |
| } |
| expr = 0; |
| if (opt == OPT_EXISTS) { |
| expr = 1; |
| break; |
| } |
| if (opt == OPT_FILE && S_ISREG(statbuf.st_mode)) { |
| expr = 1; |
| break; |
| } |
| if (opt == OPT_DIRECTORY && S_ISDIR(statbuf.st_mode)) { |
| expr = 1; |
| break; |
| } |
| } |
| break; |
| |
| /* three argument options */ |
| default: |
| adv = 3; |
| if (left < 3) { |
| expr = 1; |
| break; |
| } |
| |
| a = simple_strtol(ap[0], NULL, 0); |
| b = simple_strtol(ap[2], NULL, 0); |
| switch (parse_opt(ap[1])) { |
| case OPT_EQUAL: |
| expr = strcmp(ap[0], ap[2]) == 0; |
| break; |
| case OPT_NOT_EQUAL: |
| expr = strcmp(ap[0], ap[2]) != 0; |
| break; |
| case OPT_ARITH_EQUAL: |
| expr = a == b; |
| break; |
| case OPT_ARITH_NOT_EQUAL: |
| expr = a != b; |
| break; |
| case OPT_ARITH_LESS_THAN: |
| expr = a < b; |
| break; |
| case OPT_ARITH_LESS_EQUAL: |
| expr = a <= b; |
| break; |
| case OPT_ARITH_GREATER_THAN: |
| expr = a > b; |
| break; |
| case OPT_ARITH_GREATER_EQUAL: |
| expr = a >= b; |
| break; |
| default: |
| expr = 1; |
| goto out; |
| } |
| } |
| |
| if (last_cmp == 0) |
| expr = last_expr || expr; |
| else if (last_cmp == 1) |
| expr = last_expr && expr; |
| last_cmp = -1; |
| } |
| out: |
| if (neg) |
| expr = !expr; |
| |
| expr = !expr; |
| |
| |
| return expr; |
| } |
| |
| static const char *test_aliases[] = { "[", NULL}; |
| |
| static const __maybe_unused char cmd_test_help[] = |
| "Usage: test [OPTIONS]\n" |
| "options: !, =, !=, -eq, -ne, -ge, -gt, -le, -lt, -o, -a, -z, -n, -d, -e, -f\n" |
| "see 'man test' on your PC for more information.\n"; |
| |
| static const __maybe_unused char cmd_test_usage[] = "minimal test like /bin/sh"; |
| |
| BAREBOX_CMD_START(test) |
| .aliases = test_aliases, |
| .cmd = do_test, |
| .usage = cmd_test_usage, |
| BAREBOX_CMD_HELP(cmd_test_help) |
| BAREBOX_CMD_END |