2009-06-26 Pavel Roskin <proski@gnu.org>
[grub2/bean.git] / commands / test.c
blob26df8b5c5b73fe4be7264a6e72f1a59a47a47a23
1 /* test.c -- The test command.. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2007 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/>.
20 #include <grub/dl.h>
21 #include <grub/misc.h>
22 #include <grub/mm.h>
23 #include <grub/env.h>
24 #include <grub/fs.h>
25 #include <grub/device.h>
26 #include <grub/file.h>
27 #include <grub/command.h>
29 /* A simple implementation for signed numbers. */
30 static int
31 grub_strtosl (char *arg, char **end, int base)
33 if (arg[0] == '-')
34 return -grub_strtoul (arg + 1, end, base);
35 return grub_strtoul (arg, end, base);
38 /* Parse a test expression starting from *argn. */
39 static int
40 test_parse (char **args, int *argn, int argc)
42 int ret = 0, discard = 0, invert = 0;
43 int file_exists;
44 struct grub_dirhook_info file_info;
46 auto void update_val (int val);
47 auto void get_fileinfo (char *pathname);
49 /* Take care of discarding and inverting. */
50 void update_val (int val)
52 if (! discard)
53 ret = invert ? ! val : val;
54 invert = discard = 0;
57 /* Check if file exists and fetch its information. */
58 void get_fileinfo (char *path)
60 char *filename, *pathname;
61 char *device_name;
62 grub_fs_t fs;
63 grub_device_t dev;
65 /* A hook for iterating directories. */
66 auto int find_file (const char *cur_filename,
67 const struct grub_dirhook_info *info);
68 int find_file (const char *cur_filename,
69 const struct grub_dirhook_info *info)
71 if ((info->case_insensitive ? grub_strcasecmp (cur_filename, filename)
72 : grub_strcmp (cur_filename, filename)) == 0)
74 file_info = *info;
75 file_exists = 1;
76 return 1;
78 return 0;
81 file_exists = 0;
82 device_name = grub_file_get_device_name (path);
83 dev = grub_device_open (device_name);
84 if (! dev)
86 grub_free (device_name);
87 return;
90 fs = grub_fs_probe (dev);
91 pathname = grub_strchr (path, ')');
92 if (! pathname)
93 pathname = path;
94 else
95 pathname++;
97 /* Remove trailing '/'. */
98 while (*pathname && pathname[grub_strlen (pathname) - 1] == '/')
99 pathname[grub_strlen (pathname) - 1] = 0;
101 /* Split into path and filename. */
102 filename = grub_strrchr (pathname, '/');
103 if (! filename)
105 path = grub_strdup ("/");
106 filename = pathname;
108 else
110 filename++;
111 path = grub_strdup (pathname);
112 path[filename - pathname] = 0;
115 /* It's the whole device. */
116 if (! *pathname)
118 file_exists = 1;
119 grub_memset (&file_info, 0, sizeof (file_info));
120 /* Root is always a directory. */
121 file_info.dir = 1;
123 /* Fetch writing time. */
124 file_info.mtimeset = 0;
125 if (fs->mtime)
127 if (! fs->mtime (dev, &file_info.mtime))
128 file_info.mtimeset = 1;
129 grub_errno = GRUB_ERR_NONE;
132 else
133 (fs->dir) (dev, path, find_file);
135 grub_device_close (dev);
136 grub_free (path);
137 grub_free (device_name);
140 /* Here we have the real parsing. */
141 while (*argn < argc)
143 /* First try 3 argument tests. */
144 if (*argn + 2 < argc)
146 /* String tests. */
147 if (grub_strcmp (args[*argn + 1], "=") == 0
148 || grub_strcmp (args[*argn + 1], "==") == 0)
150 update_val (grub_strcmp (args[*argn], args[*argn + 2]) == 0);
151 (*argn) += 3;
152 continue;
155 if (grub_strcmp (args[*argn + 1], "!=") == 0)
157 update_val (grub_strcmp (args[*argn], args[*argn + 2]) != 0);
158 (*argn) += 3;
159 continue;
162 /* GRUB extension: lexicographical sorting. */
163 if (grub_strcmp (args[*argn + 1], "<") == 0)
165 update_val (grub_strcmp (args[*argn], args[*argn + 2]) < 0);
166 (*argn) += 3;
167 continue;
170 if (grub_strcmp (args[*argn + 1], "<=") == 0)
172 update_val (grub_strcmp (args[*argn], args[*argn + 2]) <= 0);
173 (*argn) += 3;
174 continue;
177 if (grub_strcmp (args[*argn + 1], ">") == 0)
179 update_val (grub_strcmp (args[*argn], args[*argn + 2]) > 0);
180 (*argn) += 3;
181 continue;
184 if (grub_strcmp (args[*argn + 1], ">=") == 0)
186 update_val (grub_strcmp (args[*argn], args[*argn + 2]) >= 0);
187 (*argn) += 3;
188 continue;
191 /* Number tests. */
192 if (grub_strcmp (args[*argn + 1], "-eq") == 0)
194 update_val (grub_strtosl (args[*argn], 0, 0)
195 == grub_strtosl (args[*argn + 2], 0, 0));
196 (*argn) += 3;
197 continue;
200 if (grub_strcmp (args[*argn + 1], "-ge") == 0)
202 update_val (grub_strtosl (args[*argn], 0, 0)
203 >= grub_strtosl (args[*argn + 2], 0, 0));
204 (*argn) += 3;
205 continue;
208 if (grub_strcmp (args[*argn + 1], "-gt") == 0)
210 update_val (grub_strtosl (args[*argn], 0, 0)
211 > grub_strtosl (args[*argn + 2], 0, 0));
212 (*argn) += 3;
213 continue;
216 if (grub_strcmp (args[*argn + 1], "-le") == 0)
218 update_val (grub_strtosl (args[*argn], 0, 0)
219 <= grub_strtosl (args[*argn + 2], 0, 0));
220 (*argn) += 3;
221 continue;
224 if (grub_strcmp (args[*argn + 1], "-lt") == 0)
226 update_val (grub_strtosl (args[*argn], 0, 0)
227 < grub_strtosl (args[*argn + 2], 0, 0));
228 (*argn) += 3;
229 continue;
232 if (grub_strcmp (args[*argn + 1], "-ne") == 0)
234 update_val (grub_strtosl (args[*argn], 0, 0)
235 != grub_strtosl (args[*argn + 2], 0, 0));
236 (*argn) += 3;
237 continue;
240 /* GRUB extension: compare numbers skipping prefixes.
241 Useful for comparing versions. E.g. vmlinuz-2 -plt vmlinuz-11. */
242 if (grub_strcmp (args[*argn + 1], "-pgt") == 0
243 || grub_strcmp (args[*argn + 1], "-plt") == 0)
245 int i;
246 /* Skip common prefix. */
247 for (i = 0; args[*argn][i] == args[*argn + 2][i]
248 && args[*argn][i]; i++);
250 /* Go the digits back. */
251 i--;
252 while (grub_isdigit (args[*argn][i]) && i > 0)
253 i--;
254 i++;
256 if (grub_strcmp (args[*argn + 1], "-pgt") == 0)
257 update_val (grub_strtoul (args[*argn] + i, 0, 0)
258 > grub_strtoul (args[*argn + 2] + i, 0, 0));
259 else
260 update_val (grub_strtoul (args[*argn] + i, 0, 0)
261 < grub_strtoul (args[*argn + 2] + i, 0, 0));
262 (*argn) += 3;
263 continue;
266 /* -nt and -ot tests. GRUB extension: when doing -?t<bias> bias
267 will be added to the first mtime. */
268 if (grub_memcmp (args[*argn + 1], "-nt", 3) == 0
269 || grub_memcmp (args[*argn + 1], "-ot", 3) == 0)
271 struct grub_dirhook_info file1;
272 int file1exists;
273 int bias = 0;
275 /* Fetch fileinfo. */
276 get_fileinfo (args[*argn]);
277 file1 = file_info;
278 file1exists = file_exists;
279 get_fileinfo (args[*argn + 2]);
281 if (args[*argn + 1][3])
282 bias = grub_strtosl (args[*argn + 1] + 3, 0, 0);
284 if (grub_memcmp (args[*argn + 1], "-nt", 3) == 0)
285 update_val ((file1exists && ! file_exists)
286 || (file1.mtimeset && file_info.mtimeset
287 && file1.mtime + bias > file_info.mtime));
288 else
289 update_val ((! file1exists && file_exists)
290 || (file1.mtimeset && file_info.mtimeset
291 && file1.mtime + bias < file_info.mtime));
292 (*argn) += 3;
293 continue;
297 /* Two-argument tests. */
298 if (*argn + 1 < argc)
300 /* File tests. */
301 if (grub_strcmp (args[*argn], "-d") == 0)
303 get_fileinfo (args[*argn + 1]);
304 update_val (file_exists && file_info.dir);
305 (*argn) += 2;
306 return ret;
309 if (grub_strcmp (args[*argn], "-e") == 0)
311 get_fileinfo (args[*argn + 1]);
312 update_val (file_exists);
313 (*argn) += 2;
314 return ret;
317 if (grub_strcmp (args[*argn], "-f") == 0)
319 get_fileinfo (args[*argn + 1]);
320 /* FIXME: check for other types. */
321 update_val (file_exists && ! file_info.dir);
322 (*argn) += 2;
323 return ret;
326 if (grub_strcmp (args[*argn], "-s") == 0)
328 grub_file_t file;
329 file = grub_file_open (args[*argn + 1]);
330 update_val (file && (grub_file_size (file) != 0));
331 if (file)
332 grub_file_close (file);
333 grub_errno = GRUB_ERR_NONE;
334 (*argn) += 2;
335 return ret;
338 /* String tests. */
339 if (grub_strcmp (args[*argn], "-n") == 0)
341 update_val (args[*argn + 1][0]);
343 (*argn) += 2;
344 continue;
346 if (grub_strcmp (args[*argn], "-z") == 0)
348 update_val (! args[*argn + 1][0]);
349 (*argn) += 2;
350 continue;
354 /* Special modifiers. */
356 /* End of expression. return to parent. */
357 if (grub_strcmp (args[*argn], ")") == 0)
359 (*argn)++;
360 return ret;
362 /* Recursively invoke if parenthesis. */
363 if (grub_strcmp (args[*argn], "(") == 0)
365 (*argn)++;
366 update_val (test_parse (args, argn, argc));
367 continue;
370 if (grub_strcmp (args[*argn], "!") == 0)
372 invert = ! invert;
373 (*argn)++;
374 continue;
376 if (grub_strcmp (args[*argn], "-a") == 0)
378 /* If current value is 0 second value is to be discarded. */
379 discard = ! ret;
380 (*argn)++;
381 continue;
383 if (grub_strcmp (args[*argn], "-o") == 0)
385 /* If current value is 1 second value is to be discarded. */
386 discard = ret;
387 (*argn)++;
388 continue;
391 /* No test found. Interpret if as just a string. */
392 update_val (args[*argn][0]);
393 (*argn)++;
395 return ret;
398 static grub_err_t
399 grub_cmd_test (grub_command_t cmd __attribute__ ((unused)),
400 int argc, char **args)
402 int argn = 0;
404 if (argc >= 1 && grub_strcmp (args[argc - 1], "]") == 0)
405 argc--;
407 return test_parse (args, &argn, argc) ? GRUB_ERR_NONE
408 : grub_error (GRUB_ERR_TEST_FAILURE, "false");
411 static grub_command_t cmd_1, cmd_2;
413 GRUB_MOD_INIT(test)
415 cmd_1 = grub_register_command ("[", grub_cmd_test,
416 "[ EXPRESSION ]", "Evaluate an expression");
417 cmd_2 = grub_register_command ("test", grub_cmd_test,
418 "test EXPRESSION", "Evaluate an expression");
421 GRUB_MOD_FINI(test)
423 grub_unregister_command (cmd_1);
424 grub_unregister_command (cmd_2);