|  | /* | 
|  | * (C) Copyright 2003 | 
|  | * Tait Electronics Limited, Christchurch, New Zealand | 
|  | * | 
|  | * SPDX-License-Identifier:	GPL-2.0+ | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * This file provides a shell like 'test' function to return | 
|  | * true/false from an integer or string compare of two memory | 
|  | * locations or a location and a scalar/literal. | 
|  | * A few parts were lifted from bash 'test' command | 
|  | */ | 
|  |  | 
|  | #include <common.h> | 
|  | #include <config.h> | 
|  | #include <command.h> | 
|  |  | 
|  | #define EQ	0 | 
|  | #define NE	1 | 
|  | #define LT	2 | 
|  | #define GT	3 | 
|  | #define LE	4 | 
|  | #define GE	5 | 
|  |  | 
|  | struct op_tbl_s { | 
|  | char	*op;		/* operator string */ | 
|  | int	opcode;		/* internal representation of opcode */ | 
|  | }; | 
|  |  | 
|  | typedef struct op_tbl_s op_tbl_t; | 
|  |  | 
|  | static const op_tbl_t op_table [] = { | 
|  | { "-lt", LT }, | 
|  | { "<"  , LT }, | 
|  | { "-gt", GT }, | 
|  | { ">"  , GT }, | 
|  | { "-eq", EQ }, | 
|  | { "==" , EQ }, | 
|  | { "-ne", NE }, | 
|  | { "!=" , NE }, | 
|  | { "<>" , NE }, | 
|  | { "-ge", GE }, | 
|  | { ">=" , GE }, | 
|  | { "-le", LE }, | 
|  | { "<=" , LE }, | 
|  | }; | 
|  |  | 
|  | static long evalexp(char *s, int w) | 
|  | { | 
|  | long l = 0; | 
|  | long *p; | 
|  |  | 
|  | /* if the parameter starts with a * then assume is a pointer to the value we want */ | 
|  | if (s[0] == '*') { | 
|  | p = (long *)simple_strtoul(&s[1], NULL, 16); | 
|  | switch (w) { | 
|  | case 1: return((long)(*(unsigned char *)p)); | 
|  | case 2: return((long)(*(unsigned short *)p)); | 
|  | case 4: return(*p); | 
|  | } | 
|  | } else { | 
|  | l = simple_strtoul(s, NULL, 16); | 
|  | } | 
|  |  | 
|  | return (l & ((1 << (w * 8)) - 1)); | 
|  | } | 
|  |  | 
|  | static char * evalstr(char *s) | 
|  | { | 
|  | /* if the parameter starts with a * then assume a string pointer else its a literal */ | 
|  | if (s[0] == '*') { | 
|  | return (char *)simple_strtoul(&s[1], NULL, 16); | 
|  | } else if (s[0] == '$') { | 
|  | int i = 2; | 
|  |  | 
|  | if (s[1] != '{') | 
|  | return NULL; | 
|  |  | 
|  | while (s[i] != '}') { | 
|  | if (s[i] == 0) | 
|  | return NULL; | 
|  | i++; | 
|  | } | 
|  | s[i] = 0; | 
|  | return  getenv((const char *)&s[2]); | 
|  | } else { | 
|  | return s; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int stringcomp(char *s, char *t, int op) | 
|  | { | 
|  | int p; | 
|  | char *l, *r; | 
|  |  | 
|  | l = evalstr(s); | 
|  | r = evalstr(t); | 
|  |  | 
|  | p = strcmp(l, r); | 
|  | switch (op) { | 
|  | case EQ: return (p == 0); | 
|  | case NE: return (p != 0); | 
|  | case LT: return (p < 0); | 
|  | case GT: return (p > 0); | 
|  | case LE: return (p <= 0); | 
|  | case GE: return (p >= 0); | 
|  | } | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | static int arithcomp (char *s, char *t, int op, int w) | 
|  | { | 
|  | long l, r; | 
|  |  | 
|  | l = evalexp (s, w); | 
|  | r = evalexp (t, w); | 
|  |  | 
|  | switch (op) { | 
|  | case EQ: return (l == r); | 
|  | case NE: return (l != r); | 
|  | case LT: return (l < r); | 
|  | case GT: return (l > r); | 
|  | case LE: return (l <= r); | 
|  | case GE: return (l >= r); | 
|  | } | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | static int binary_test(char *op, char *arg1, char *arg2, int w) | 
|  | { | 
|  | int len, i; | 
|  | const op_tbl_t *optp; | 
|  |  | 
|  | len = strlen(op); | 
|  |  | 
|  | for (optp = (op_tbl_t *)&op_table, i = 0; | 
|  | i < ARRAY_SIZE(op_table); | 
|  | optp++, i++) { | 
|  |  | 
|  | if ((strncmp (op, optp->op, len) == 0) && (len == strlen (optp->op))) { | 
|  | if (w == 0) { | 
|  | return (stringcomp(arg1, arg2, optp->opcode)); | 
|  | } else { | 
|  | return (arithcomp (arg1, arg2, optp->opcode, w)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | printf("Unknown operator '%s'\n", op); | 
|  | return 0;	/* op code not found */ | 
|  | } | 
|  |  | 
|  | /* command line interface to the shell test */ | 
|  | static int do_itest(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | 
|  | { | 
|  | int	value, w; | 
|  |  | 
|  | /* Validate arguments */ | 
|  | if ((argc != 4)) | 
|  | return CMD_RET_USAGE; | 
|  |  | 
|  | /* Check for a data width specification. | 
|  | * Defaults to long (4) if no specification. | 
|  | * Uses -2 as 'width' for .s (string) so as not to upset existing code | 
|  | */ | 
|  | switch (w = cmd_get_data_size(argv[0], 4)) { | 
|  | case 1: | 
|  | case 2: | 
|  | case 4: | 
|  | value = binary_test (argv[2], argv[1], argv[3], w); | 
|  | break; | 
|  | case -2: | 
|  | value = binary_test (argv[2], argv[1], argv[3], 0); | 
|  | break; | 
|  | case -1: | 
|  | default: | 
|  | puts("Invalid data width specifier\n"); | 
|  | value = 0; | 
|  | break; | 
|  | } | 
|  |  | 
|  | return !value; | 
|  | } | 
|  |  | 
|  | U_BOOT_CMD( | 
|  | itest, 4, 0, do_itest, | 
|  | "return true/false on integer compare", | 
|  | "[.b, .w, .l, .s] [*]value1 <op> [*]value2" | 
|  | ); |