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 GRUB_MOD_LICENSE ("GPLv3+");
32 /* A simple implementation for signed numbers. */
34 grub_strtosl (char *arg
, char **end
, int base
)
37 return -grub_strtoul (arg
+ 1, end
, base
);
38 return grub_strtoul (arg
, end
, base
);
41 /* Parse a test expression starting from *argn. */
43 test_parse (char **args
, int *argn
, int argc
)
45 int ret
= 0, discard
= 0, invert
= 0;
47 struct grub_dirhook_info file_info
;
49 auto void update_val (int val
);
50 auto void get_fileinfo (char *pathname
);
52 /* Take care of discarding and inverting. */
53 void update_val (int val
)
56 ret
= invert
? ! val
: val
;
60 /* Check if file exists and fetch its information. */
61 void get_fileinfo (char *path
)
63 char *filename
, *pathname
;
68 /* A hook for iterating directories. */
69 auto int find_file (const char *cur_filename
,
70 const struct grub_dirhook_info
*info
);
71 int find_file (const char *cur_filename
,
72 const struct grub_dirhook_info
*info
)
74 if ((info
->case_insensitive
? grub_strcasecmp (cur_filename
, filename
)
75 : grub_strcmp (cur_filename
, filename
)) == 0)
85 device_name
= grub_file_get_device_name (path
);
86 dev
= grub_device_open (device_name
);
89 grub_free (device_name
);
93 fs
= grub_fs_probe (dev
);
96 grub_free (device_name
);
97 grub_device_close (dev
);
101 pathname
= grub_strchr (path
, ')');
107 /* Remove trailing '/'. */
108 while (*pathname
&& pathname
[grub_strlen (pathname
) - 1] == '/')
109 pathname
[grub_strlen (pathname
) - 1] = 0;
111 /* Split into path and filename. */
112 filename
= grub_strrchr (pathname
, '/');
115 path
= grub_strdup ("/");
121 path
= grub_strdup (pathname
);
122 path
[filename
- pathname
] = 0;
125 /* It's the whole device. */
129 grub_memset (&file_info
, 0, sizeof (file_info
));
130 /* Root is always a directory. */
133 /* Fetch writing time. */
134 file_info
.mtimeset
= 0;
137 if (! fs
->mtime (dev
, &file_info
.mtime
))
138 file_info
.mtimeset
= 1;
139 grub_errno
= GRUB_ERR_NONE
;
143 (fs
->dir
) (dev
, path
, find_file
);
145 grub_device_close (dev
);
147 grub_free (device_name
);
150 /* Here we have the real parsing. */
153 /* First try 3 argument tests. */
154 if (*argn
+ 2 < argc
)
157 if (grub_strcmp (args
[*argn
+ 1], "=") == 0
158 || grub_strcmp (args
[*argn
+ 1], "==") == 0)
160 update_val (grub_strcmp (args
[*argn
], args
[*argn
+ 2]) == 0);
165 if (grub_strcmp (args
[*argn
+ 1], "!=") == 0)
167 update_val (grub_strcmp (args
[*argn
], args
[*argn
+ 2]) != 0);
172 /* GRUB extension: lexicographical sorting. */
173 if (grub_strcmp (args
[*argn
+ 1], "<") == 0)
175 update_val (grub_strcmp (args
[*argn
], args
[*argn
+ 2]) < 0);
180 if (grub_strcmp (args
[*argn
+ 1], "<=") == 0)
182 update_val (grub_strcmp (args
[*argn
], args
[*argn
+ 2]) <= 0);
187 if (grub_strcmp (args
[*argn
+ 1], ">") == 0)
189 update_val (grub_strcmp (args
[*argn
], args
[*argn
+ 2]) > 0);
194 if (grub_strcmp (args
[*argn
+ 1], ">=") == 0)
196 update_val (grub_strcmp (args
[*argn
], args
[*argn
+ 2]) >= 0);
202 if (grub_strcmp (args
[*argn
+ 1], "-eq") == 0)
204 update_val (grub_strtosl (args
[*argn
], 0, 0)
205 == grub_strtosl (args
[*argn
+ 2], 0, 0));
210 if (grub_strcmp (args
[*argn
+ 1], "-ge") == 0)
212 update_val (grub_strtosl (args
[*argn
], 0, 0)
213 >= grub_strtosl (args
[*argn
+ 2], 0, 0));
218 if (grub_strcmp (args
[*argn
+ 1], "-gt") == 0)
220 update_val (grub_strtosl (args
[*argn
], 0, 0)
221 > grub_strtosl (args
[*argn
+ 2], 0, 0));
226 if (grub_strcmp (args
[*argn
+ 1], "-le") == 0)
228 update_val (grub_strtosl (args
[*argn
], 0, 0)
229 <= grub_strtosl (args
[*argn
+ 2], 0, 0));
234 if (grub_strcmp (args
[*argn
+ 1], "-lt") == 0)
236 update_val (grub_strtosl (args
[*argn
], 0, 0)
237 < grub_strtosl (args
[*argn
+ 2], 0, 0));
242 if (grub_strcmp (args
[*argn
+ 1], "-ne") == 0)
244 update_val (grub_strtosl (args
[*argn
], 0, 0)
245 != grub_strtosl (args
[*argn
+ 2], 0, 0));
250 /* GRUB extension: compare numbers skipping prefixes.
251 Useful for comparing versions. E.g. vmlinuz-2 -plt vmlinuz-11. */
252 if (grub_strcmp (args
[*argn
+ 1], "-pgt") == 0
253 || grub_strcmp (args
[*argn
+ 1], "-plt") == 0)
256 /* Skip common prefix. */
257 for (i
= 0; args
[*argn
][i
] == args
[*argn
+ 2][i
]
258 && args
[*argn
][i
]; i
++);
260 /* Go the digits back. */
262 while (grub_isdigit (args
[*argn
][i
]) && i
> 0)
266 if (grub_strcmp (args
[*argn
+ 1], "-pgt") == 0)
267 update_val (grub_strtoul (args
[*argn
] + i
, 0, 0)
268 > grub_strtoul (args
[*argn
+ 2] + i
, 0, 0));
270 update_val (grub_strtoul (args
[*argn
] + i
, 0, 0)
271 < grub_strtoul (args
[*argn
+ 2] + i
, 0, 0));
276 /* -nt and -ot tests. GRUB extension: when doing -?t<bias> bias
277 will be added to the first mtime. */
278 if (grub_memcmp (args
[*argn
+ 1], "-nt", 3) == 0
279 || grub_memcmp (args
[*argn
+ 1], "-ot", 3) == 0)
281 struct grub_dirhook_info file1
;
285 /* Fetch fileinfo. */
286 get_fileinfo (args
[*argn
]);
288 file1exists
= file_exists
;
289 get_fileinfo (args
[*argn
+ 2]);
291 if (args
[*argn
+ 1][3])
292 bias
= grub_strtosl (args
[*argn
+ 1] + 3, 0, 0);
294 if (grub_memcmp (args
[*argn
+ 1], "-nt", 3) == 0)
295 update_val ((file1exists
&& ! file_exists
)
296 || (file1
.mtimeset
&& file_info
.mtimeset
297 && file1
.mtime
+ bias
> file_info
.mtime
));
299 update_val ((! file1exists
&& file_exists
)
300 || (file1
.mtimeset
&& file_info
.mtimeset
301 && file1
.mtime
+ bias
< file_info
.mtime
));
307 /* Two-argument tests. */
308 if (*argn
+ 1 < argc
)
311 if (grub_strcmp (args
[*argn
], "-d") == 0)
313 get_fileinfo (args
[*argn
+ 1]);
314 update_val (file_exists
&& file_info
.dir
);
319 if (grub_strcmp (args
[*argn
], "-e") == 0)
321 get_fileinfo (args
[*argn
+ 1]);
322 update_val (file_exists
);
327 if (grub_strcmp (args
[*argn
], "-f") == 0)
329 get_fileinfo (args
[*argn
+ 1]);
330 /* FIXME: check for other types. */
331 update_val (file_exists
&& ! file_info
.dir
);
336 if (grub_strcmp (args
[*argn
], "-s") == 0)
339 grub_file_filter_disable_compression ();
340 file
= grub_file_open (args
[*argn
+ 1]);
341 update_val (file
&& (grub_file_size (file
) != 0));
343 grub_file_close (file
);
344 grub_errno
= GRUB_ERR_NONE
;
350 if (grub_strcmp (args
[*argn
], "-n") == 0)
352 update_val (args
[*argn
+ 1][0]);
357 if (grub_strcmp (args
[*argn
], "-z") == 0)
359 update_val (! args
[*argn
+ 1][0]);
365 /* Special modifiers. */
367 /* End of expression. return to parent. */
368 if (grub_strcmp (args
[*argn
], ")") == 0)
373 /* Recursively invoke if parenthesis. */
374 if (grub_strcmp (args
[*argn
], "(") == 0)
377 update_val (test_parse (args
, argn
, argc
));
381 if (grub_strcmp (args
[*argn
], "!") == 0)
387 if (grub_strcmp (args
[*argn
], "-a") == 0)
389 /* If current value is 0 second value is to be discarded. */
394 if (grub_strcmp (args
[*argn
], "-o") == 0)
396 /* If current value is 1 second value is to be discarded. */
402 /* No test found. Interpret if as just a string. */
403 update_val (args
[*argn
][0]);
410 grub_cmd_test (grub_command_t cmd
__attribute__ ((unused
)),
411 int argc
, char **args
)
415 if (argc
>= 1 && grub_strcmp (args
[argc
- 1], "]") == 0)
418 return test_parse (args
, &argn
, argc
) ? GRUB_ERR_NONE
419 : grub_error (GRUB_ERR_TEST_FAILURE
, N_("false"));
422 static grub_command_t cmd_1
, cmd_2
;
426 cmd_1
= grub_register_command ("[", grub_cmd_test
,
427 N_("EXPRESSION ]"), N_("Evaluate an expression."));
428 cmd_1
->flags
|= GRUB_COMMAND_FLAG_EXTRACTOR
;
429 cmd_2
= grub_register_command ("test", grub_cmd_test
,
430 N_("EXPRESSION"), N_("Evaluate an expression."));
431 cmd_2
->flags
|= GRUB_COMMAND_FLAG_EXTRACTOR
;
436 grub_unregister_command (cmd_1
);
437 grub_unregister_command (cmd_2
);