]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/commands/test.c
Update vtoytool
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / commands / test.c
1 /* test.c -- The test command.. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2007,2009 Free Software Foundation, Inc.
5 *
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.
10 *
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.
15 *
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/>.
18 */
19
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>
28 #include <grub/i18n.h>
29
30 GRUB_MOD_LICENSE ("GPLv3+");
31
32 static int g_test_case_insensitive = 0;
33
34 /* A simple implementation for signed numbers. */
35 static int
36 grub_strtosl (char *arg, char **end, int base)
37 {
38 if (arg[0] == '-')
39 return -grub_strtoul (arg + 1, end, base);
40 return grub_strtoul (arg, end, base);
41 }
42
43 /* Context for test_parse. */
44 struct test_parse_ctx
45 {
46 int invert;
47 int or, and;
48 int file_exists;
49 struct grub_dirhook_info file_info;
50 char *filename;
51 };
52
53 /* Take care of discarding and inverting. */
54 static void
55 update_val (int val, struct test_parse_ctx *ctx)
56 {
57 ctx->and = ctx->and && (ctx->invert ? ! val : val);
58 ctx->invert = 0;
59 }
60
61 /* A hook for iterating directories. */
62 static int
63 find_file (const char *cur_filename, const struct grub_dirhook_info *info,
64 void *data)
65 {
66 int case_insensitive = 0;
67 struct test_parse_ctx *ctx = data;
68
69 if (g_test_case_insensitive || info->case_insensitive)
70 case_insensitive = 1;
71
72 if ((case_insensitive ? grub_strcasecmp (cur_filename, ctx->filename)
73 : grub_strcmp (cur_filename, ctx->filename)) == 0)
74 {
75 ctx->file_info = *info;
76 ctx->file_exists = 1;
77 return 1;
78 }
79 return 0;
80 }
81
82 /* Check if file exists and fetch its information. */
83 static void
84 get_fileinfo (char *path, struct test_parse_ctx *ctx)
85 {
86 char *pathname;
87 char *device_name;
88 grub_fs_t fs;
89 grub_device_t dev;
90
91 ctx->file_exists = 0;
92 device_name = grub_file_get_device_name (path);
93 dev = grub_device_open (device_name);
94 if (! dev)
95 {
96 grub_free (device_name);
97 return;
98 }
99
100 fs = grub_fs_probe (dev);
101 if (! fs)
102 {
103 grub_free (device_name);
104 grub_device_close (dev);
105 return;
106 }
107
108 pathname = grub_strchr (path, ')');
109 if (! pathname)
110 pathname = path;
111 else
112 pathname++;
113
114 /* Remove trailing '/'. */
115 while (*pathname && pathname[grub_strlen (pathname) - 1] == '/')
116 pathname[grub_strlen (pathname) - 1] = 0;
117
118 /* Split into path and filename. */
119 ctx->filename = grub_strrchr (pathname, '/');
120 if (! ctx->filename)
121 {
122 path = grub_strdup ("/");
123 ctx->filename = pathname;
124 }
125 else
126 {
127 ctx->filename++;
128 path = grub_strdup (pathname);
129 path[ctx->filename - pathname] = 0;
130 }
131
132 /* It's the whole device. */
133 if (! *pathname)
134 {
135 ctx->file_exists = 1;
136 grub_memset (&ctx->file_info, 0, sizeof (ctx->file_info));
137 /* Root is always a directory. */
138 ctx->file_info.dir = 1;
139
140 /* Fetch writing time. */
141 ctx->file_info.mtimeset = 0;
142 if (fs->fs_mtime)
143 {
144 if (! fs->fs_mtime (dev, &ctx->file_info.mtime))
145 ctx->file_info.mtimeset = 1;
146 grub_errno = GRUB_ERR_NONE;
147 }
148 }
149 else
150 (fs->fs_dir) (dev, path, find_file, ctx);
151
152 grub_device_close (dev);
153 grub_free (path);
154 grub_free (device_name);
155 }
156
157 /* Parse a test expression starting from *argn. */
158 static int
159 test_parse (char **args, int *argn, int argc)
160 {
161 struct test_parse_ctx ctx = {
162 .and = 1,
163 .or = 0,
164 .invert = 0
165 };
166
167 /* Here we have the real parsing. */
168 while (*argn < argc)
169 {
170 /* First try 3 argument tests. */
171 if (*argn + 2 < argc)
172 {
173 /* String tests. */
174 if (grub_strcmp (args[*argn + 1], "=") == 0
175 || grub_strcmp (args[*argn + 1], "==") == 0)
176 {
177 update_val (grub_strcmp (args[*argn], args[*argn + 2]) == 0,
178 &ctx);
179 (*argn) += 3;
180 continue;
181 }
182
183 if (grub_strcmp (args[*argn + 1], "!=") == 0)
184 {
185 update_val (grub_strcmp (args[*argn], args[*argn + 2]) != 0,
186 &ctx);
187 (*argn) += 3;
188 continue;
189 }
190
191 /* GRUB extension: lexicographical sorting. */
192 if (grub_strcmp (args[*argn + 1], "<") == 0)
193 {
194 update_val (grub_strcmp (args[*argn], args[*argn + 2]) < 0,
195 &ctx);
196 (*argn) += 3;
197 continue;
198 }
199
200 if (grub_strcmp (args[*argn + 1], "<=") == 0)
201 {
202 update_val (grub_strcmp (args[*argn], args[*argn + 2]) <= 0,
203 &ctx);
204 (*argn) += 3;
205 continue;
206 }
207
208 if (grub_strcmp (args[*argn + 1], ">") == 0)
209 {
210 update_val (grub_strcmp (args[*argn], args[*argn + 2]) > 0,
211 &ctx);
212 (*argn) += 3;
213 continue;
214 }
215
216 if (grub_strcmp (args[*argn + 1], ">=") == 0)
217 {
218 update_val (grub_strcmp (args[*argn], args[*argn + 2]) >= 0,
219 &ctx);
220 (*argn) += 3;
221 continue;
222 }
223
224 /* Number tests. */
225 if (grub_strcmp (args[*argn + 1], "-eq") == 0)
226 {
227 update_val (grub_strtosl (args[*argn], 0, 0)
228 == grub_strtosl (args[*argn + 2], 0, 0), &ctx);
229 (*argn) += 3;
230 continue;
231 }
232 if (grub_strcmp (args[*argn + 1], "-EQ") == 0)
233 {
234 update_val (grub_strtoull (args[*argn], 0, 0)
235 == grub_strtoull (args[*argn + 2], 0, 0), &ctx);
236 (*argn) += 3;
237 continue;
238 }
239
240 if (grub_strcmp (args[*argn + 1], "-ge") == 0)
241 {
242 update_val (grub_strtosl (args[*argn], 0, 0)
243 >= grub_strtosl (args[*argn + 2], 0, 0), &ctx);
244 (*argn) += 3;
245 continue;
246 }
247 if (grub_strcmp (args[*argn + 1], "-GE") == 0)
248 {
249 update_val (grub_strtoull (args[*argn], 0, 0)
250 >= grub_strtoull (args[*argn + 2], 0, 0), &ctx);
251 (*argn) += 3;
252 continue;
253 }
254
255 if (grub_strcmp (args[*argn + 1], "-gt") == 0)
256 {
257 update_val (grub_strtosl (args[*argn], 0, 0)
258 > grub_strtosl (args[*argn + 2], 0, 0), &ctx);
259 (*argn) += 3;
260 continue;
261 }
262 if (grub_strcmp (args[*argn + 1], "-GT") == 0)
263 {
264 update_val (grub_strtoull (args[*argn], 0, 0)
265 > grub_strtoull (args[*argn + 2], 0, 0), &ctx);
266 (*argn) += 3;
267 continue;
268 }
269
270 if (grub_strcmp (args[*argn + 1], "-le") == 0)
271 {
272 update_val (grub_strtosl (args[*argn], 0, 0)
273 <= grub_strtosl (args[*argn + 2], 0, 0), &ctx);
274 (*argn) += 3;
275 continue;
276 }
277 if (grub_strcmp (args[*argn + 1], "-LE") == 0)
278 {
279 update_val (grub_strtoull (args[*argn], 0, 0)
280 <= grub_strtoull (args[*argn + 2], 0, 0), &ctx);
281 (*argn) += 3;
282 continue;
283 }
284
285 if (grub_strcmp (args[*argn + 1], "-lt") == 0)
286 {
287 update_val (grub_strtosl (args[*argn], 0, 0)
288 < grub_strtosl (args[*argn + 2], 0, 0), &ctx);
289 (*argn) += 3;
290 continue;
291 }
292 if (grub_strcmp (args[*argn + 1], "-LT") == 0)
293 {
294 update_val (grub_strtoull (args[*argn], 0, 0)
295 < grub_strtoull (args[*argn + 2], 0, 0), &ctx);
296 (*argn) += 3;
297 continue;
298 }
299
300 if (grub_strcmp (args[*argn + 1], "-ne") == 0)
301 {
302 update_val (grub_strtosl (args[*argn], 0, 0)
303 != grub_strtosl (args[*argn + 2], 0, 0), &ctx);
304 (*argn) += 3;
305 continue;
306 }
307 if (grub_strcmp (args[*argn + 1], "-NE") == 0)
308 {
309 update_val (grub_strtoull (args[*argn], 0, 0)
310 != grub_strtoull (args[*argn + 2], 0, 0), &ctx);
311 (*argn) += 3;
312 continue;
313 }
314
315 /* GRUB extension: compare numbers skipping prefixes.
316 Useful for comparing versions. E.g. vmlinuz-2 -plt vmlinuz-11. */
317 if (grub_strcmp (args[*argn + 1], "-pgt") == 0
318 || grub_strcmp (args[*argn + 1], "-plt") == 0)
319 {
320 int i;
321 /* Skip common prefix. */
322 for (i = 0; args[*argn][i] == args[*argn + 2][i]
323 && args[*argn][i]; i++);
324
325 /* Go the digits back. */
326 i--;
327 while (grub_isdigit (args[*argn][i]) && i > 0)
328 i--;
329 i++;
330
331 if (grub_strcmp (args[*argn + 1], "-pgt") == 0)
332 update_val (grub_strtoul (args[*argn] + i, 0, 0)
333 > grub_strtoul (args[*argn + 2] + i, 0, 0), &ctx);
334 else
335 update_val (grub_strtoul (args[*argn] + i, 0, 0)
336 < grub_strtoul (args[*argn + 2] + i, 0, 0), &ctx);
337 (*argn) += 3;
338 continue;
339 }
340
341 /* -nt and -ot tests. GRUB extension: when doing -?t<bias> bias
342 will be added to the first mtime. */
343 if (grub_memcmp (args[*argn + 1], "-nt", 3) == 0
344 || grub_memcmp (args[*argn + 1], "-ot", 3) == 0)
345 {
346 struct grub_dirhook_info file1;
347 int file1exists;
348 int bias = 0;
349
350 /* Fetch fileinfo. */
351 get_fileinfo (args[*argn], &ctx);
352 file1 = ctx.file_info;
353 file1exists = ctx.file_exists;
354 get_fileinfo (args[*argn + 2], &ctx);
355
356 if (args[*argn + 1][3])
357 bias = grub_strtosl (args[*argn + 1] + 3, 0, 0);
358
359 if (grub_memcmp (args[*argn + 1], "-nt", 3) == 0)
360 update_val ((file1exists && ! ctx.file_exists)
361 || (file1.mtimeset && ctx.file_info.mtimeset
362 && file1.mtime + bias > ctx.file_info.mtime),
363 &ctx);
364 else
365 update_val ((! file1exists && ctx.file_exists)
366 || (file1.mtimeset && ctx.file_info.mtimeset
367 && file1.mtime + bias < ctx.file_info.mtime),
368 &ctx);
369 (*argn) += 3;
370 continue;
371 }
372 }
373
374 /* Two-argument tests. */
375 if (*argn + 1 < argc)
376 {
377 /* File tests. */
378 if (grub_strcmp (args[*argn], "-d") == 0)
379 {
380 get_fileinfo (args[*argn + 1], &ctx);
381 update_val (ctx.file_exists && ctx.file_info.dir, &ctx);
382 (*argn) += 2;
383 continue;
384 }
385
386 if (grub_strcmp (args[*argn], "-D") == 0)
387 {
388 g_test_case_insensitive = 1;
389 get_fileinfo (args[*argn + 1], &ctx);
390 g_test_case_insensitive = 0;
391 update_val (ctx.file_exists && ctx.file_info.dir, &ctx);
392 (*argn) += 2;
393 continue;
394 }
395
396 if (grub_strcmp (args[*argn], "-e") == 0)
397 {
398 get_fileinfo (args[*argn + 1], &ctx);
399 update_val (ctx.file_exists, &ctx);
400 (*argn) += 2;
401 continue;
402 }
403
404 if (grub_strcmp (args[*argn], "-E") == 0)
405 {
406 g_test_case_insensitive = 1;
407 get_fileinfo (args[*argn + 1], &ctx);
408 g_test_case_insensitive = 0;
409 update_val (ctx.file_exists, &ctx);
410 (*argn) += 2;
411 continue;
412 }
413
414 if (grub_strcmp (args[*argn], "-f") == 0)
415 {
416 get_fileinfo (args[*argn + 1], &ctx);
417 /* FIXME: check for other types. */
418 update_val (ctx.file_exists && ! ctx.file_info.dir, &ctx);
419 (*argn) += 2;
420 continue;
421 }
422 if (grub_strcmp (args[*argn], "-F") == 0)
423 {
424 g_test_case_insensitive = 1;
425 get_fileinfo (args[*argn + 1], &ctx);
426 g_test_case_insensitive = 0;
427 /* FIXME: check for other types. */
428 update_val (ctx.file_exists && ! ctx.file_info.dir, &ctx);
429 (*argn) += 2;
430 continue;
431 }
432
433 if (grub_strcmp (args[*argn], "-s") == 0)
434 {
435 grub_file_t file;
436 file = grub_file_open (args[*argn + 1], GRUB_FILE_TYPE_GET_SIZE
437 | GRUB_FILE_TYPE_NO_DECOMPRESS);
438 update_val (file && (grub_file_size (file) != 0), &ctx);
439 if (file)
440 grub_file_close (file);
441 grub_errno = GRUB_ERR_NONE;
442 (*argn) += 2;
443 continue;
444 }
445
446 /* String tests. */
447 if (grub_strcmp (args[*argn], "-n") == 0)
448 {
449 update_val (args[*argn + 1][0], &ctx);
450
451 (*argn) += 2;
452 continue;
453 }
454 if (grub_strcmp (args[*argn], "-z") == 0)
455 {
456 update_val (! args[*argn + 1][0], &ctx);
457 (*argn) += 2;
458 continue;
459 }
460 }
461
462 /* Special modifiers. */
463
464 /* End of expression. return to parent. */
465 if (grub_strcmp (args[*argn], ")") == 0)
466 {
467 (*argn)++;
468 return ctx.or || ctx.and;
469 }
470 /* Recursively invoke if parenthesis. */
471 if (grub_strcmp (args[*argn], "(") == 0)
472 {
473 (*argn)++;
474 update_val (test_parse (args, argn, argc), &ctx);
475 continue;
476 }
477
478 if (grub_strcmp (args[*argn], "!") == 0)
479 {
480 ctx.invert = ! ctx.invert;
481 (*argn)++;
482 continue;
483 }
484 if (grub_strcmp (args[*argn], "-a") == 0)
485 {
486 (*argn)++;
487 continue;
488 }
489 if (grub_strcmp (args[*argn], "-o") == 0)
490 {
491 ctx.or = ctx.or || ctx.and;
492 ctx.and = 1;
493 (*argn)++;
494 continue;
495 }
496
497 /* No test found. Interpret if as just a string. */
498 update_val (args[*argn][0], &ctx);
499 (*argn)++;
500 }
501 return ctx.or || ctx.and;
502 }
503
504 static grub_err_t
505 grub_cmd_test (grub_command_t cmd __attribute__ ((unused)),
506 int argc, char **args)
507 {
508 int argn = 0;
509
510 if (argc >= 1 && grub_strcmp (args[argc - 1], "]") == 0)
511 argc--;
512
513 return test_parse (args, &argn, argc) ? GRUB_ERR_NONE
514 : grub_error (GRUB_ERR_TEST_FAILURE, N_("false"));
515 }
516
517 static grub_command_t cmd_1, cmd_2;
518 \f
519 GRUB_MOD_INIT(test)
520 {
521 cmd_1 = grub_register_command ("[", grub_cmd_test,
522 N_("EXPRESSION ]"), N_("Evaluate an expression."));
523 cmd_1->flags |= GRUB_COMMAND_FLAG_EXTRACTOR;
524 cmd_2 = grub_register_command ("test", grub_cmd_test,
525 N_("EXPRESSION"), N_("Evaluate an expression."));
526 cmd_2->flags |= GRUB_COMMAND_FLAG_EXTRACTOR;
527 }
528
529 GRUB_MOD_FINI(test)
530 {
531 grub_unregister_command (cmd_1);
532 grub_unregister_command (cmd_2);
533 }