1
/* test.c -- The test command.. */
3
* GRUB -- GRand Unified Bootloader
4
* Copyright (C) 2005,2007,2009 Free Software Foundation, Inc.
6
* GRUB is free software: you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation, either version 3 of the License, or
9
* (at your option) any later version.
11
* GRUB is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
21
#include <grub/misc.h>
25
#include <grub/device.h>
26
#include <grub/file.h>
27
#include <grub/command.h>
28
#include <grub/i18n.h>
30
/* A simple implementation for signed numbers. */
32
grub_strtosl (char *arg, char **end, int base)
35
return -grub_strtoul (arg + 1, end, base);
36
return grub_strtoul (arg, end, base);
39
/* Parse a test expression starting from *argn. */
41
test_parse (char **args, int *argn, int argc)
43
int ret = 0, discard = 0, invert = 0;
45
struct grub_dirhook_info file_info;
47
auto void update_val (int val);
48
auto void get_fileinfo (char *pathname);
50
/* Take care of discarding and inverting. */
51
void update_val (int val)
54
ret = invert ? ! val : val;
58
/* Check if file exists and fetch its information. */
59
void get_fileinfo (char *path)
61
char *filename, *pathname;
66
/* A hook for iterating directories. */
67
auto int find_file (const char *cur_filename,
68
const struct grub_dirhook_info *info);
69
int find_file (const char *cur_filename,
70
const struct grub_dirhook_info *info)
72
if ((info->case_insensitive ? grub_strcasecmp (cur_filename, filename)
73
: grub_strcmp (cur_filename, filename)) == 0)
83
device_name = grub_file_get_device_name (path);
84
dev = grub_device_open (device_name);
87
grub_free (device_name);
91
fs = grub_fs_probe (dev);
94
grub_free (device_name);
95
grub_device_close (dev);
99
pathname = grub_strchr (path, ')');
105
/* Remove trailing '/'. */
106
while (*pathname && pathname[grub_strlen (pathname) - 1] == '/')
107
pathname[grub_strlen (pathname) - 1] = 0;
109
/* Split into path and filename. */
110
filename = grub_strrchr (pathname, '/');
113
path = grub_strdup ("/");
119
path = grub_strdup (pathname);
120
path[filename - pathname] = 0;
123
/* It's the whole device. */
127
grub_memset (&file_info, 0, sizeof (file_info));
128
/* Root is always a directory. */
131
/* Fetch writing time. */
132
file_info.mtimeset = 0;
135
if (! fs->mtime (dev, &file_info.mtime))
136
file_info.mtimeset = 1;
137
grub_errno = GRUB_ERR_NONE;
141
(fs->dir) (dev, path, find_file);
143
grub_device_close (dev);
145
grub_free (device_name);
148
/* Here we have the real parsing. */
151
/* First try 3 argument tests. */
152
if (*argn + 2 < argc)
155
if (grub_strcmp (args[*argn + 1], "=") == 0
156
|| grub_strcmp (args[*argn + 1], "==") == 0)
158
update_val (grub_strcmp (args[*argn], args[*argn + 2]) == 0);
163
if (grub_strcmp (args[*argn + 1], "!=") == 0)
165
update_val (grub_strcmp (args[*argn], args[*argn + 2]) != 0);
170
/* GRUB extension: lexicographical sorting. */
171
if (grub_strcmp (args[*argn + 1], "<") == 0)
173
update_val (grub_strcmp (args[*argn], args[*argn + 2]) < 0);
178
if (grub_strcmp (args[*argn + 1], "<=") == 0)
180
update_val (grub_strcmp (args[*argn], args[*argn + 2]) <= 0);
185
if (grub_strcmp (args[*argn + 1], ">") == 0)
187
update_val (grub_strcmp (args[*argn], args[*argn + 2]) > 0);
192
if (grub_strcmp (args[*argn + 1], ">=") == 0)
194
update_val (grub_strcmp (args[*argn], args[*argn + 2]) >= 0);
200
if (grub_strcmp (args[*argn + 1], "-eq") == 0)
202
update_val (grub_strtosl (args[*argn], 0, 0)
203
== grub_strtosl (args[*argn + 2], 0, 0));
208
if (grub_strcmp (args[*argn + 1], "-ge") == 0)
210
update_val (grub_strtosl (args[*argn], 0, 0)
211
>= grub_strtosl (args[*argn + 2], 0, 0));
216
if (grub_strcmp (args[*argn + 1], "-gt") == 0)
218
update_val (grub_strtosl (args[*argn], 0, 0)
219
> grub_strtosl (args[*argn + 2], 0, 0));
224
if (grub_strcmp (args[*argn + 1], "-le") == 0)
226
update_val (grub_strtosl (args[*argn], 0, 0)
227
<= grub_strtosl (args[*argn + 2], 0, 0));
232
if (grub_strcmp (args[*argn + 1], "-lt") == 0)
234
update_val (grub_strtosl (args[*argn], 0, 0)
235
< grub_strtosl (args[*argn + 2], 0, 0));
240
if (grub_strcmp (args[*argn + 1], "-ne") == 0)
242
update_val (grub_strtosl (args[*argn], 0, 0)
243
!= grub_strtosl (args[*argn + 2], 0, 0));
248
/* GRUB extension: compare numbers skipping prefixes.
249
Useful for comparing versions. E.g. vmlinuz-2 -plt vmlinuz-11. */
250
if (grub_strcmp (args[*argn + 1], "-pgt") == 0
251
|| grub_strcmp (args[*argn + 1], "-plt") == 0)
254
/* Skip common prefix. */
255
for (i = 0; args[*argn][i] == args[*argn + 2][i]
256
&& args[*argn][i]; i++);
258
/* Go the digits back. */
260
while (grub_isdigit (args[*argn][i]) && i > 0)
264
if (grub_strcmp (args[*argn + 1], "-pgt") == 0)
265
update_val (grub_strtoul (args[*argn] + i, 0, 0)
266
> grub_strtoul (args[*argn + 2] + i, 0, 0));
268
update_val (grub_strtoul (args[*argn] + i, 0, 0)
269
< grub_strtoul (args[*argn + 2] + i, 0, 0));
274
/* -nt and -ot tests. GRUB extension: when doing -?t<bias> bias
275
will be added to the first mtime. */
276
if (grub_memcmp (args[*argn + 1], "-nt", 3) == 0
277
|| grub_memcmp (args[*argn + 1], "-ot", 3) == 0)
279
struct grub_dirhook_info file1;
283
/* Fetch fileinfo. */
284
get_fileinfo (args[*argn]);
286
file1exists = file_exists;
287
get_fileinfo (args[*argn + 2]);
289
if (args[*argn + 1][3])
290
bias = grub_strtosl (args[*argn + 1] + 3, 0, 0);
292
if (grub_memcmp (args[*argn + 1], "-nt", 3) == 0)
293
update_val ((file1exists && ! file_exists)
294
|| (file1.mtimeset && file_info.mtimeset
295
&& file1.mtime + bias > file_info.mtime));
297
update_val ((! file1exists && file_exists)
298
|| (file1.mtimeset && file_info.mtimeset
299
&& file1.mtime + bias < file_info.mtime));
305
/* Two-argument tests. */
306
if (*argn + 1 < argc)
309
if (grub_strcmp (args[*argn], "-d") == 0)
311
get_fileinfo (args[*argn + 1]);
312
update_val (file_exists && file_info.dir);
317
if (grub_strcmp (args[*argn], "-e") == 0)
319
get_fileinfo (args[*argn + 1]);
320
update_val (file_exists);
325
if (grub_strcmp (args[*argn], "-f") == 0)
327
get_fileinfo (args[*argn + 1]);
328
/* FIXME: check for other types. */
329
update_val (file_exists && ! file_info.dir);
334
if (grub_strcmp (args[*argn], "-s") == 0)
337
file = grub_file_open (args[*argn + 1]);
338
update_val (file && (grub_file_size (file) != 0));
340
grub_file_close (file);
341
grub_errno = GRUB_ERR_NONE;
347
if (grub_strcmp (args[*argn], "-n") == 0)
349
update_val (args[*argn + 1][0]);
354
if (grub_strcmp (args[*argn], "-z") == 0)
356
update_val (! args[*argn + 1][0]);
362
/* Special modifiers. */
364
/* End of expression. return to parent. */
365
if (grub_strcmp (args[*argn], ")") == 0)
370
/* Recursively invoke if parenthesis. */
371
if (grub_strcmp (args[*argn], "(") == 0)
374
update_val (test_parse (args, argn, argc));
378
if (grub_strcmp (args[*argn], "!") == 0)
384
if (grub_strcmp (args[*argn], "-a") == 0)
386
/* If current value is 0 second value is to be discarded. */
391
if (grub_strcmp (args[*argn], "-o") == 0)
393
/* If current value is 1 second value is to be discarded. */
399
/* No test found. Interpret if as just a string. */
400
update_val (args[*argn][0]);
407
grub_cmd_test (grub_command_t cmd __attribute__ ((unused)),
408
int argc, char **args)
412
if (argc >= 1 && grub_strcmp (args[argc - 1], "]") == 0)
415
return test_parse (args, &argn, argc) ? GRUB_ERR_NONE
416
: grub_error (GRUB_ERR_TEST_FAILURE, "false");
419
static grub_command_t cmd_1, cmd_2;
423
cmd_1 = grub_register_command ("[", grub_cmd_test,
424
N_("EXPRESSION ]"), N_("Evaluate an expression."));
425
cmd_2 = grub_register_command ("test", grub_cmd_test,
426
N_("EXPRESSION"), N_("Evaluate an expression."));
431
grub_unregister_command (cmd_1);
432
grub_unregister_command (cmd_2);