From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with archive (Exim 4.43) id 1LXwOk-000137-0v for mharc-grub-devel@gnu.org; Fri, 13 Feb 2009 06:39:58 -0500 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1LXwOi-00012n-1u for grub-devel@gnu.org; Fri, 13 Feb 2009 06:39:56 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1LXwOg-00012R-A0 for grub-devel@gnu.org; Fri, 13 Feb 2009 06:39:54 -0500 Received: from [199.232.76.173] (port=48505 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LXwOg-00012O-0A for grub-devel@gnu.org; Fri, 13 Feb 2009 06:39:54 -0500 Received: from mail-bw0-f208.google.com ([209.85.218.208]:40516) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1LXwOe-00077q-Uu for grub-devel@gnu.org; Fri, 13 Feb 2009 06:39:53 -0500 Received: by bwz4 with SMTP id 4so2262141bwz.18 for ; Fri, 13 Feb 2009 03:39:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:message-id:date:from :user-agent:mime-version:to:subject:references:in-reply-to :content-type; bh=ywd6Ebl6n67Wk3iumbeJ69K/u78mVKN3ln2JjzBif9g=; b=BLxyvXZ6IhKAJt81zNwAAfqxRpEc/X7KXSORxbrNNYZHkFfxVNLjpNaFUp7S+Lk4bn MPYoK0Z0Ww/N7KF7qw3zBhs7LHEkBV2Nnc7RxLoO8S95wwGpMsrD9wq3yLyZVcmxewKF OLkdcHIM4DN1y0jOSN3lL2ZU5tcQm7EAEjDNY= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=message-id:date:from:user-agent:mime-version:to:subject:references :in-reply-to:content-type; b=djukoduk82hvEolkEg0kg8y9gYsz1XUtOEbyX5eBx5Ycb1PGpQxtpLqB4mIe8hcGD7 0uPsEIumEm4lKaA6UXKn+DdJk5k1HEUVUF0Zr9e72JHKp1ky224C8pta7cJjOcBFBo4H 8k5qxFapsDxNkqgis1ghyWeGcJeSOmIKtsv3U= Received: by 10.86.95.20 with SMTP id s20mr200466fgb.43.1234525191074; Fri, 13 Feb 2009 03:39:51 -0800 (PST) Received: from ?192.168.1.25? (120-197.62-81.cust.bluewin.ch [81.62.197.120]) by mx.google.com with ESMTPS id l19sm1290914fgb.37.2009.02.13.03.39.49 (version=SSLv3 cipher=RC4-MD5); Fri, 13 Feb 2009 03:39:50 -0800 (PST) Message-ID: <49955C06.9060706@gmail.com> Date: Fri, 13 Feb 2009 12:39:50 +0100 From: phcoder User-Agent: Thunderbird 2.0.0.19 (X11/20090105) MIME-Version: 1.0 To: The development of GRUB 2 References: <49955BD7.2070206@gmail.com> In-Reply-To: <49955BD7.2070206@gmail.com> Content-Type: multipart/mixed; boundary="------------010304010903070705010005" X-detected-operating-system: by monty-python.gnu.org: GNU/Linux 2.6 (newer, 2) Subject: [PATCH] Test command X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: The development of GRUB 2 List-Id: The development of GRUB 2 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 13 Feb 2009 11:39:56 -0000 This is a multi-part message in MIME format. --------------010304010903070705010005 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Sorry forgot to attach the file phcoder wrote: > Hello. Here is an implementation of bash-like "test" command. Many file > tests are omitted because they are useless in grub (e.g. -c test). I > also added 3 extension: lexicographical comparing, prefixed -gt and -lt > (it skips common prefix. Useful for comparing versions. e.g. [ vmlinuz-3 > -plt vmlinuz-11 ] is true) and biased -nt/-ot which adds s specified > amount of seconds to mtime. > Regards > Vladimir 'phcoder' Serbinenko --------------010304010903070705010005 Content-Type: text/x-patch; name="test.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="test.diff" Index: commands/test.c =================================================================== --- commands/test.c (revision 1989) +++ commands/test.c (working copy) @@ -23,33 +23,385 @@ #include #include #include +#include +#include +#include +/* A simple implementation for signed numbers*/ +static int +grub_strtosl (char *arg, char **end, int base) +{ + if (arg[0] == '-') + return -grub_strtoul (arg + 1, end, base); + return grub_strtoul (arg, end, base); +} + +/* Parse a test expression startion from *argn*/ +static int +test_parse (char **args, int *argn, int argc) +{ + int ret = 0, discard = 0, invert = 0; + int file_exists; + struct grub_dirhook_info file_info; + + auto void update_val (int val); + auto void get_fileinfo (char *pathname); + + /*Take care of discarding and inverting*/ + void update_val (int val) + { + if (!discard) + ret = invert ? !val : val; + invert = discard = 0; + } + + /* Check if file exists and fetch its information */ + void get_fileinfo (char *pathname) + { + char *filename, *path; + char *device_name; + grub_fs_t fs; + grub_device_t dev; + + /* A hook for iterating directories */ + auto int find_file (const char *cur_filename, + struct grub_dirhook_info info); + int find_file (const char *cur_filename, struct grub_dirhook_info info) + { + if (info.case_insensitive ? !grub_strcasecmp (cur_filename, filename) + :!grub_strcmp (cur_filename, filename)) + { + file_info = info; + file_exists = 1; + return 1; + } + return 0; + } + + + file_exists = 0; + device_name = grub_file_get_device_name (pathname); + dev = grub_device_open (device_name); + if (! dev) + { + grub_free (device_name); + return; + } + + fs = grub_fs_probe (dev); + path = grub_strchr (pathname, ')'); + if (! path) + path = pathname; + else + path++; + + /* Remove trailing / */ + while (*pathname && pathname[grub_strlen (pathname) - 1] == '/') + pathname[grub_strlen (pathname) - 1] = 0; + + /* Split into path and filename*/ + filename = grub_strrchr (pathname, '/'); + if (!filename) + { + path = grub_strdup ("/"); + filename = pathname; + } + else + { + filename++; + path = grub_strdup (pathname); + path[filename - pathname] = 0; + } + + /* It's the whole device*/ + if (!*pathname) + { + file_exists = 1; + grub_memset (&file_info, 0, sizeof (file_info)); + /* Root is always a directory */ + file_info.dir = 1; + + /* Fetch writing time */ + file_info.mtimeset = 0; + if (fs->mtime) + { + if (! fs->mtime (dev, &file_info.mtime)) + file_info.mtimeset = 1; + grub_errno = GRUB_ERR_NONE; + } + } + else + (fs->dir) (dev, path, find_file); + + grub_device_close (dev); + grub_free (path); + grub_free (device_name); + } + + /* Here we have the real parsing */ + while (*argn < argc) + { + /* First try 3 argument tests */ + /* String tests */ + if (*argn + 2 < argc && (!grub_strcmp (args[*argn + 1], "=") + || !grub_strcmp (args[*argn + 1], "=="))) + { + update_val (grub_strcmp (args[*argn], args[*argn + 2]) == 0); + (*argn) += 3; + continue; + } + + if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "!=")) + { + update_val (grub_strcmp (args[*argn], args[*argn + 2]) != 0); + (*argn) += 3; + continue; + } + + /* GRUB extension: lexicographical sorting */ + if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "<")) + { + update_val (grub_strcmp (args[*argn], args[*argn + 2]) < 0); + (*argn) += 3; + continue; + } + + if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "<=")) + { + update_val (grub_strcmp (args[*argn], args[*argn + 2]) <= 0); + (*argn) += 3; + continue; + } + + if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], ">")) + { + update_val (grub_strcmp (args[*argn], args[*argn + 2]) > 0); + (*argn) += 3; + continue; + } + + if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], ">=")) + { + update_val (grub_strcmp (args[*argn], args[*argn + 2]) >= 0); + (*argn) += 3; + continue; + } + + /* Number tests */ + if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "-eq")) + { + update_val (grub_strtosl (args[*argn], 0, 0) + == grub_strtosl (args[*argn + 2], 0, 0)); + (*argn) += 3; + continue; + } + + if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "-ge")) + { + update_val (grub_strtosl (args[*argn], 0, 0) + >= grub_strtosl (args[*argn + 2], 0, 0)); + (*argn) += 3; + continue; + } + + if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "-gt")) + { + update_val (grub_strtosl (args[*argn], 0, 0) + > grub_strtosl (args[*argn + 2], 0, 0)); + (*argn) += 3; + continue; + } + + if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "-le")) + { + update_val (grub_strtosl (args[*argn], 0, 0) + <= grub_strtosl (args[*argn + 2], 0, 0)); + (*argn) += 3; + continue; + } + + if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "-lt")) + { + update_val (grub_strtosl (args[*argn], 0, 0) + < grub_strtosl (args[*argn + 2], 0, 0)); + (*argn) += 3; + continue; + } + + if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "-ne")) + { + update_val (grub_strtosl (args[*argn], 0, 0) + != grub_strtosl (args[*argn + 2], 0, 0)); + (*argn) += 3; + continue; + } + + /* GRUB extension: compare numbers skipping prefixes. + Useful for comparing versions. E.g. vmlinuz-2 -plt vmlinuz-11*/ + if (*argn + 2 < argc && (!grub_strcmp (args[*argn + 1], "-pgt") + || !grub_strcmp (args[*argn + 1], "-plt"))) + { + int i; + /* Skip common prefix */ + for (i = 0; args[*argn][i] == args[*argn + 2][i] && args[*argn][i]; + i++); + + /*Go the digits back*/ + i--; + while (grub_isdigit (args[*argn][i]) && i > 0) + i--; + i++; + + if (!grub_strcmp (args[*argn + 1], "-pgt")) + update_val (grub_strtoul (args[*argn] + i, 0, 0) + > grub_strtoul (args[*argn + 2] + i, 0, 0)); + else + update_val (grub_strtoul (args[*argn] + i, 0, 0) + < grub_strtoul (args[*argn + 2] + i, 0, 0)); + (*argn) += 3; + continue; + } + + /* -nt and -ot tests. GRUB extension: when doing -?t bias + will be added to the first mtime */ + if (*argn + 2 < argc && (!grub_memcmp (args[*argn + 1], "-nt", 3) + || !grub_memcmp (args[*argn + 1], "-ot", 3))) + { + struct grub_dirhook_info file1; + int file1exists; + int bias = 0; + + /* Fetch fileinfo */ + get_fileinfo (args[*argn]); + file1 = file_info; + file1exists = file_exists; + get_fileinfo (args[*argn + 2]); + + if (args[*argn + 1][3]) + bias = grub_strtosl (args[*argn + 1] + 3, 0, 0); + + if (!grub_memcmp (args[*argn + 1], "-nt", 3)) + update_val ((file1exists && ! file_exists) + || (file1.mtimeset && file_info.mtimeset + && file1.mtime + bias > file_info.mtime)); + else + update_val ((!file1exists && file_exists) + || (file1.mtimeset && file_info.mtimeset + && file1.mtime + bias < file_info.mtime)); + (*argn) += 3; + continue; + } + + /* Two-argument tests */ + /* file tests*/ + if (*argn + 1 < argc && !grub_strcmp (args[*argn], "-d")) + { + get_fileinfo (args[*argn + 1]); + update_val (file_exists && file_info.dir); + (*argn) += 2; + return ret; + } + + if (*argn + 1 < argc && !grub_strcmp (args[*argn], "-e")) + { + get_fileinfo (args[*argn + 1]); + update_val (file_exists); + (*argn) += 2; + return ret; + } + + if (*argn + 1 < argc && !grub_strcmp (args[*argn], "-f")) + { + get_fileinfo (args[*argn + 1]); + /* FIXME: check for other types */ + update_val (file_exists && !file_info.dir); + (*argn) += 2; + return ret; + } + + if (*argn + 1 < argc && !grub_strcmp (args[*argn], "-s")) + { + grub_file_t file; + file = grub_file_open (args[*argn + 1]); + update_val (file && grub_file_size (file)); + if (file) + grub_file_close (file); + grub_errno = GRUB_ERR_NONE; + (*argn) += 2; + return ret; + } + + /* string tests */ + if (!grub_strcmp (args[*argn], "-n") && *argn + 1 < argc) + { + update_val (args[*argn + 1][0]); + + (*argn)+=2; + continue; + } + if (!grub_strcmp (args[*argn], "-z") && *argn + 1 < argc) + { + update_val (!args[*argn + 1][0]); + (*argn)+=2; + continue; + } + + /* Special modifiers*/ + + /* End of expression. return to parent*/ + if (!grub_strcmp (args[*argn], ")")) + { + (*argn)++; + return ret; + } + /* Recursively invoke if parenthesis */ + if (!grub_strcmp (args[*argn], "(")) + { + (*argn)++; + update_val (test_parse (args, argn, argc)); + continue; + } + + if (!grub_strcmp (args[*argn], "!")) + { + invert = !invert; + (*argn)++; + continue; + } + if (!grub_strcmp (args[*argn], "-a")) + { + /* if current value is 0 second value is to be discarded */ + discard = !ret; + (*argn)++; + continue; + } + if (!grub_strcmp (args[*argn], "-o")) + { + /* if current value is 1 second value is to be discarded */ + discard = ret; + (*argn)++; + continue; + } + + /* To test found. Interpret if as just a string*/ + update_val (args[*argn][0]); + (*argn)++; + } + return ret; +} + static grub_err_t grub_cmd_test (struct grub_arg_list *state __attribute__ ((unused)), int argc, char **args) { - char *eq; - char *eqis; + int argn = 0; - /* XXX: No fancy expression evaluation yet. */ - - if (argc == 0) - return 0; - - eq = grub_strdup (args[0]); - eqis = grub_strchr (eq, '='); - if (! eqis) - return 0; + if (argc >= 1 && !grub_strcmp (args[argc-1], "]")) + argc--; - *eqis = '\0'; - eqis++; - /* Check an expression in the form `A=B'. */ - if (grub_strcmp (eq, eqis)) - grub_error (GRUB_ERR_TEST_FAILURE, "false"); - grub_free (eq); - - return grub_errno; + return test_parse (args, &argn, argc) ? GRUB_ERR_NONE + : grub_error (GRUB_ERR_TEST_FAILURE, "false"); } @@ -57,9 +409,11 @@ GRUB_MOD_INIT(test) { (void)mod; /* To stop warning. */ - grub_register_command ("[", grub_cmd_test, GRUB_COMMAND_FLAG_CMDLINE, + grub_register_command ("[", grub_cmd_test, GRUB_COMMAND_FLAG_CMDLINE + | GRUB_COMMAND_FLAG_NO_ARG_PARSE, "[ EXPRESSION ]", "Evaluate an expression", 0); - grub_register_command ("test", grub_cmd_test, GRUB_COMMAND_FLAG_CMDLINE, + grub_register_command ("test", grub_cmd_test, GRUB_COMMAND_FLAG_CMDLINE + | GRUB_COMMAND_FLAG_NO_ARG_PARSE, "test EXPRESSION", "Evaluate an expression", 0); } Index: include/grub/fs.h =================================================================== --- include/grub/fs.h (revision 1989) +++ include/grub/fs.h (working copy) @@ -27,13 +27,14 @@ /* Forward declaration is required, because of mutual reference. */ struct grub_file; struct grub_dirhook_info { int dir:1; int mtimeset:1; + int case_insensitive:1; grub_int32_t mtime; }; /* Filesystem descriptor. */ struct grub_fs { Index: fs/fat.c =================================================================== --- fs/fat.c (revision 1989) +++ fs/fat.c (working copy) @@ -594,9 +557,10 @@ struct grub_dirhook_info info; grub_memset (&info, 0, sizeof (info)); info.dir = (dir->attr & GRUB_FAT_ATTR_DIRECTORY); + info.case_insensitive = 1; if (dir->attr & GRUB_FAT_ATTR_VOLUME_ID) return 0; if (*dirname == '\0' && call_hook) return hook (filename, info); Index: fs/hfsplus.c =================================================================== --- fs/hfsplus.c (revision 1989) +++ fs/hfsplus.c (working copy) @@ -898,11 +899,12 @@ enum grub_fshelp_filetype filetype, grub_fshelp_node_t node) { struct grub_dirhook_info info; grub_memset (&info, 0, sizeof (info)); info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + info.case_insensitive = !! (filetype & GRUB_FSHELP_CASE_INSENSITIVE); grub_free (node); return hook (filename, info); } #ifndef GRUB_UTIL Index: ChangeLog =================================================================== --- ChangeLog (revision 1989) +++ ChangeLog (working copy) @@ -1,3 +1,13 @@ +2009-02-13 Vladimir Serbinenko + + Test command + + * commands/test.c: rewritten to use bash-like test + * include/grub/fs.h (struct grub_dirhook_info): new field + case_insensitive + * fs/hfsplus.c: declare FS as case-insensitive if necessary + * fs/fat.c: likewise + 2009-02-11 Robert Millan * util/grub.d/00_header.in: Update old reference to `font' command. --------------010304010903070705010005--