1/* GNU test program (ksb and mjb) */ 2 3/* Modified to run with the GNU shell by bfox. */ 4 5/* Copyright (C) 1987-2005, 2007-2010 Free Software Foundation, Inc. 6 7 This program is free software: you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation, either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 19 20/* Define TEST_STANDALONE to get the /bin/test version. Otherwise, you get 21 the shell builtin version. */ 22 23#include <config.h> 24#include <stdio.h> 25#include <sys/types.h> 26 27#define TEST_STANDALONE 1 28 29#ifndef LBRACKET 30# define LBRACKET 0 31#endif 32 33/* The official name of this program (e.g., no `g' prefix). */ 34#if LBRACKET 35# define PROGRAM_NAME "[" 36#else 37# define PROGRAM_NAME "test" 38#endif 39 40#include "system.h" 41#include "quote.h" 42#include "stat-time.h" 43#include "strnumcmp.h" 44 45#if HAVE_SYS_PARAM_H 46# include <sys/param.h> 47#endif 48 49/* Exit status for syntax errors, etc. */ 50enum { TEST_TRUE, TEST_FALSE, TEST_FAILURE }; 51 52#if defined TEST_STANDALONE 53# define test_exit(val) exit (val) 54#else 55 static jmp_buf test_exit_buf; 56 static int test_error_return = 0; 57# define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1) 58#endif /* !TEST_STANDALONE */ 59 60static int pos; /* The offset of the current argument in ARGV. */ 61static int argc; /* The number of arguments present in ARGV. */ 62static char **argv; /* The argument list. */ 63 64static bool test_unop (char const *s); 65static bool unary_operator (void); 66static bool binary_operator (bool); 67static bool two_arguments (void); 68static bool three_arguments (void); 69static bool posixtest (int); 70 71static bool expr (void); 72static bool term (void); 73static bool and (void); 74static bool or (void); 75 76static void test_syntax_error (char const *format, char const *arg) 77 ATTRIBUTE_NORETURN; 78static void beyond (void) ATTRIBUTE_NORETURN; 79 80static void 81test_syntax_error (char const *format, char const *arg) 82{ 83 fprintf (stderr, "%s: ", argv[0]); 84 fprintf (stderr, format, arg); 85 fputc ('\n', stderr); 86 fflush (stderr); 87 test_exit (TEST_FAILURE); 88} 89 90/* Increment our position in the argument list. Check that we're not 91 past the end of the argument list. This check is supressed if the 92 argument is false. */ 93 94static void 95advance (bool f) 96{ 97 ++pos; 98 99 if (f && pos >= argc) 100 beyond (); 101} 102 103static void 104unary_advance (void) 105{ 106 advance (true); 107 ++pos; 108} 109 110/* 111 * beyond - call when we're beyond the end of the argument list (an 112 * error condition) 113 */ 114static void 115beyond (void) 116{ 117 test_syntax_error (_("missing argument after %s"), quote (argv[argc - 1])); 118} 119 120/* If the characters pointed to by STRING constitute a valid number, 121 return a pointer to the start of the number, skipping any blanks or 122 leading '+'. Otherwise, report an error and exit. */ 123static char const * 124find_int (char const *string) 125{ 126 char const *p; 127 char const *number_start; 128 129 for (p = string; isblank (to_uchar (*p)); p++) 130 continue; 131 132 if (*p == '+') 133 { 134 p++; 135 number_start = p; 136 } 137 else 138 { 139 number_start = p; 140 p += (*p == '-'); 141 } 142 143 if (ISDIGIT (*p++)) 144 { 145 while (ISDIGIT (*p)) 146 p++; 147 while (isblank (to_uchar (*p))) 148 p++; 149 if (!*p) 150 return number_start; 151 } 152 153 test_syntax_error (_("invalid integer %s"), quote (string)); 154} 155 156/* Find the modification time of FILE, and stuff it into *MTIME. 157 Return true if successful. */ 158static bool 159get_mtime (char const *filename, struct timespec *mtime) 160{ 161 struct stat finfo; 162 bool ok = (stat (filename, &finfo) == 0); 163#ifdef lint 164 static struct timespec const zero; 165 *mtime = zero; 166#endif 167 if (ok) 168 *mtime = get_stat_mtime (&finfo); 169 return ok; 170} 171 172/* Return true if S is one of the test command's binary operators. */ 173static bool 174binop (char const *s) 175{ 176 return ((STREQ (s, "=")) || (STREQ (s, "!=")) || (STREQ (s, "-nt")) || 177 (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) || 178 (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) || 179 (STREQ (s, "-gt")) || (STREQ (s, "-ge"))); 180} 181 182/* 183 * term - parse a term and return 1 or 0 depending on whether the term 184 * evaluates to true or false, respectively. 185 * 186 * term ::= 187 * '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename 188 * '-'('L'|'x') filename 189 * '-t' int 190 * '-'('z'|'n') string 191 * string 192 * string ('!='|'=') string 193 * <int> '-'(eq|ne|le|lt|ge|gt) <int> 194 * file '-'(nt|ot|ef) file 195 * '(' <expr> ')' 196 * int ::= 197 * '-l' string 198 * positive and negative integers 199 */ 200static bool 201term (void) 202{ 203 bool value; 204 bool negated = false; 205 206 /* Deal with leading `not's. */ 207 while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0') 208 { 209 advance (true); 210 negated = !negated; 211 } 212 213 if (pos >= argc) 214 beyond (); 215 216 /* A paren-bracketed argument. */ 217 if (argv[pos][0] == '(' && argv[pos][1] == '\0') 218 { 219 int nargs; 220 221 advance (true); 222 223 for (nargs = 1; 224 pos + nargs < argc && ! STREQ (argv[pos + nargs], ")"); 225 nargs++) 226 if (nargs == 4) 227 { 228 nargs = argc - pos; 229 break; 230 } 231 232 value = posixtest (nargs); 233 if (argv[pos] == 0) 234 test_syntax_error (_("')' expected"), NULL); 235 else 236 if (argv[pos][0] != ')' || argv[pos][1]) 237 test_syntax_error (_("')' expected, found %s"), argv[pos]); 238 advance (false); 239 } 240 241 /* Are there enough arguments left that this could be dyadic? */ 242 else if (4 <= argc - pos && STREQ (argv[pos], "-l") && binop (argv[pos + 2])) 243 value = binary_operator (true); 244 else if (3 <= argc - pos && binop (argv[pos + 1])) 245 value = binary_operator (false); 246 247 /* It might be a switch type argument. */ 248 else if (argv[pos][0] == '-' && argv[pos][1] && argv[pos][2] == '\0') 249 { 250 if (test_unop (argv[pos])) 251 value = unary_operator (); 252 else 253 test_syntax_error (_("%s: unary operator expected"), argv[pos]); 254 } 255 else 256 { 257 value = (argv[pos][0] != '\0'); 258 advance (false); 259 } 260 261 return negated ^ value; 262} 263 264static bool 265binary_operator (bool l_is_l) 266{ 267 int op; 268 struct stat stat_buf, stat_spare; 269 /* Is the right integer expression of the form '-l string'? */ 270 bool r_is_l; 271 272 if (l_is_l) 273 advance (false); 274 op = pos + 1; 275 276 if ((op < argc - 2) && STREQ (argv[op + 1], "-l")) 277 { 278 r_is_l = true; 279 advance (false); 280 } 281 else 282 r_is_l = false; 283 284 if (argv[op][0] == '-') 285 { 286 /* check for eq, nt, and stuff */ 287 if ((((argv[op][1] == 'l' || argv[op][1] == 'g') 288 && (argv[op][2] == 'e' || argv[op][2] == 't')) 289 || (argv[op][1] == 'e' && argv[op][2] == 'q') 290 || (argv[op][1] == 'n' && argv[op][2] == 'e')) 291 && !argv[op][3]) 292 { 293 char lbuf[INT_BUFSIZE_BOUND (uintmax_t)]; 294 char rbuf[INT_BUFSIZE_BOUND (uintmax_t)]; 295 char const *l = (l_is_l 296 ? umaxtostr (strlen (argv[op - 1]), lbuf) 297 : find_int (argv[op - 1])); 298 char const *r = (r_is_l 299 ? umaxtostr (strlen (argv[op + 2]), rbuf) 300 : find_int (argv[op + 1])); 301 int cmp = strintcmp (l, r); 302 bool xe_operator = (argv[op][2] == 'e'); 303 pos += 3; 304 return (argv[op][1] == 'l' ? cmp < xe_operator 305 : argv[op][1] == 'g' ? cmp > - xe_operator 306 : (cmp != 0) == xe_operator); 307 } 308 309 switch (argv[op][1]) 310 { 311 default: 312 break; 313 314 case 'n': 315 if (argv[op][2] == 't' && !argv[op][3]) 316 { 317 /* nt - newer than */ 318 struct timespec lt, rt; 319 bool le, re; 320 pos += 3; 321 if (l_is_l || r_is_l) 322 test_syntax_error (_("-nt does not accept -l"), NULL); 323 le = get_mtime (argv[op - 1], <); 324 re = get_mtime (argv[op + 1], &rt); 325 return le && (!re || timespec_cmp (lt, rt) > 0); 326 } 327 break; 328 329 case 'e': 330 if (argv[op][2] == 'f' && !argv[op][3]) 331 { 332 /* ef - hard link? */ 333 pos += 3; 334 if (l_is_l || r_is_l) 335 test_syntax_error (_("-ef does not accept -l"), NULL); 336 return (stat (argv[op - 1], &stat_buf) == 0 337 && stat (argv[op + 1], &stat_spare) == 0 338 && stat_buf.st_dev == stat_spare.st_dev 339 && stat_buf.st_ino == stat_spare.st_ino); 340 } 341 break; 342 343 case 'o': 344 if ('t' == argv[op][2] && '\000' == argv[op][3]) 345 { 346 /* ot - older than */ 347 struct timespec lt, rt; 348 bool le, re; 349 pos += 3; 350 if (l_is_l || r_is_l) 351 test_syntax_error (_("-ot does not accept -l"), NULL); 352 le = get_mtime (argv[op - 1], <); 353 re = get_mtime (argv[op + 1], &rt); 354 return re && (!le || timespec_cmp (lt, rt) < 0); 355 } 356 break; 357 } 358 359 /* FIXME: is this dead code? */ 360 test_syntax_error (_("unknown binary operator"), argv[op]); 361 } 362 363 if (argv[op][0] == '=' && !argv[op][1]) 364 { 365 bool value = STREQ (argv[pos], argv[pos + 2]); 366 pos += 3; 367 return value; 368 } 369 370 if (STREQ (argv[op], "!=")) 371 { 372 bool value = !STREQ (argv[pos], argv[pos + 2]); 373 pos += 3; 374 return value; 375 } 376 377 /* Not reached. */ 378 abort (); 379} 380 381static bool 382unary_operator (void) 383{ 384 struct stat stat_buf; 385 386 switch (argv[pos][1]) 387 { 388 default: 389 return false; 390 391 /* All of the following unary operators use unary_advance (), which 392 checks to make sure that there is an argument, and then advances 393 pos right past it. This means that pos - 1 is the location of the 394 argument. */ 395 396 case 'a': /* file exists in the file system? */ 397 case 'e': 398 unary_advance (); 399 return stat (argv[pos - 1], &stat_buf) == 0; 400 401 case 'r': /* file is readable? */ 402 unary_advance (); 403 return euidaccess (argv[pos - 1], R_OK) == 0; 404 405 case 'w': /* File is writable? */ 406 unary_advance (); 407 return euidaccess (argv[pos - 1], W_OK) == 0; 408 409 case 'x': /* File is executable? */ 410 unary_advance (); 411 return euidaccess (argv[pos - 1], X_OK) == 0; 412 413 case 'O': /* File is owned by you? */ 414 unary_advance (); 415 return (stat (argv[pos - 1], &stat_buf) == 0 416 && (geteuid () == stat_buf.st_uid)); 417 418 case 'G': /* File is owned by your group? */ 419 unary_advance (); 420 return (stat (argv[pos - 1], &stat_buf) == 0 421 && (getegid () == stat_buf.st_gid)); 422 423 case 'f': /* File is a file? */ 424 unary_advance (); 425 /* Under POSIX, -f is true if the given file exists 426 and is a regular file. */ 427 return (stat (argv[pos - 1], &stat_buf) == 0 428 && S_ISREG (stat_buf.st_mode)); 429 430 case 'd': /* File is a directory? */ 431 unary_advance (); 432 return (stat (argv[pos - 1], &stat_buf) == 0 433 && S_ISDIR (stat_buf.st_mode)); 434 435 case 's': /* File has something in it? */ 436 unary_advance (); 437 return (stat (argv[pos - 1], &stat_buf) == 0 438 && 0 < stat_buf.st_size); 439 440 case 'S': /* File is a socket? */ 441 unary_advance (); 442 return (stat (argv[pos - 1], &stat_buf) == 0 443 && S_ISSOCK (stat_buf.st_mode)); 444 445 case 'c': /* File is character special? */ 446 unary_advance (); 447 return (stat (argv[pos - 1], &stat_buf) == 0 448 && S_ISCHR (stat_buf.st_mode)); 449 450 case 'b': /* File is block special? */ 451 unary_advance (); 452 return (stat (argv[pos - 1], &stat_buf) == 0 453 && S_ISBLK (stat_buf.st_mode)); 454 455 case 'p': /* File is a named pipe? */ 456 unary_advance (); 457 return (stat (argv[pos - 1], &stat_buf) == 0 458 && S_ISFIFO (stat_buf.st_mode)); 459 460 case 'L': /* Same as -h */ 461 /*FALLTHROUGH*/ 462 463 case 'h': /* File is a symbolic link? */ 464 unary_advance (); 465 return (lstat (argv[pos - 1], &stat_buf) == 0 466 && S_ISLNK (stat_buf.st_mode)); 467 468 case 'u': /* File is setuid? */ 469 unary_advance (); 470 return (stat (argv[pos - 1], &stat_buf) == 0 471 && (stat_buf.st_mode & S_ISUID)); 472 473 case 'g': /* File is setgid? */ 474 unary_advance (); 475 return (stat (argv[pos - 1], &stat_buf) == 0 476 && (stat_buf.st_mode & S_ISGID)); 477 478 case 'k': /* File has sticky bit set? */ 479 unary_advance (); 480 return (stat (argv[pos - 1], &stat_buf) == 0 481 && (stat_buf.st_mode & S_ISVTX)); 482 483 case 't': /* File (fd) is a terminal? */ 484 { 485 long int fd; 486 char const *arg; 487 unary_advance (); 488 arg = find_int (argv[pos - 1]); 489 errno = 0; 490 fd = strtol (arg, NULL, 10); 491 return (errno != ERANGE && 0 <= fd && fd <= INT_MAX && isatty (fd)); 492 } 493 494 case 'n': /* True if arg has some length. */ 495 unary_advance (); 496 return argv[pos - 1][0] != 0; 497 498 case 'z': /* True if arg has no length. */ 499 unary_advance (); 500 return argv[pos - 1][0] == '\0'; 501 } 502} 503 504/* 505 * and: 506 * term 507 * term '-a' and 508 */ 509static bool 510and (void) 511{ 512 bool value = true; 513 514 for (;;) 515 { 516 value &= term (); 517 if (! (pos < argc && STREQ (argv[pos], "-a"))) 518 return value; 519 advance (false); 520 } 521} 522 523/* 524 * or: 525 * and 526 * and '-o' or 527 */ 528static bool 529or (void) 530{ 531 bool value = false; 532 533 for (;;) 534 { 535 value |= and (); 536 if (! (pos < argc && STREQ (argv[pos], "-o"))) 537 return value; 538 advance (false); 539 } 540} 541 542/* 543 * expr: 544 * or 545 */ 546static bool 547expr (void) 548{ 549 if (pos >= argc) 550 beyond (); 551 552 return or (); /* Same with this. */ 553} 554 555/* Return true if OP is one of the test command's unary operators. */ 556static bool 557test_unop (char const *op) 558{ 559 if (op[0] != '-') 560 return false; 561 562 switch (op[1]) 563 { 564 case 'a': case 'b': case 'c': case 'd': case 'e': 565 case 'f': case 'g': case 'h': case 'k': case 'n': 566 case 'o': case 'p': case 'r': case 's': case 't': 567 case 'u': case 'w': case 'x': case 'z': 568 case 'G': case 'L': case 'O': case 'S': case 'N': 569 return true; 570 default: 571 return false; 572 } 573} 574 575static bool 576one_argument (void) 577{ 578 return argv[pos++][0] != '\0'; 579} 580 581static bool 582two_arguments (void) 583{ 584 bool value; 585 586 if (STREQ (argv[pos], "!")) 587 { 588 advance (false); 589 value = ! one_argument (); 590 } 591 else if (argv[pos][0] == '-' 592 && argv[pos][1] != '\0' 593 && argv[pos][2] == '\0') 594 { 595 if (test_unop (argv[pos])) 596 value = unary_operator (); 597 else 598 test_syntax_error (_("%s: unary operator expected"), argv[pos]); 599 } 600 else 601 beyond (); 602 return (value); 603} 604 605static bool 606three_arguments (void) 607{ 608 bool value; 609 610 if (binop (argv[pos + 1])) 611 value = binary_operator (false); 612 else if (STREQ (argv[pos], "!")) 613 { 614 advance (true); 615 value = !two_arguments (); 616 } 617 else if (STREQ (argv[pos], "(") && STREQ (argv[pos + 2], ")")) 618 { 619 advance (false); 620 value = one_argument (); 621 advance (false); 622 } 623 else if (STREQ (argv[pos + 1], "-a") || STREQ (argv[pos + 1], "-o")) 624 value = expr (); 625 else 626 test_syntax_error (_("%s: binary operator expected"), argv[pos+1]); 627 return (value); 628} 629 630/* This is an implementation of a Posix.2 proposal by David Korn. */ 631static bool 632posixtest (int nargs) 633{ 634 bool value; 635 636 switch (nargs) 637 { 638 case 1: 639 value = one_argument (); 640 break; 641 642 case 2: 643 value = two_arguments (); 644 break; 645 646 case 3: 647 value = three_arguments (); 648 break; 649 650 case 4: 651 if (STREQ (argv[pos], "!")) 652 { 653 advance (true); 654 value = !three_arguments (); 655 break; 656 } 657 if (STREQ (argv[pos], "(") && STREQ (argv[pos + 3], ")")) 658 { 659 advance (false); 660 value = two_arguments (); 661 advance (false); 662 break; 663 } 664 /* FALLTHROUGH */ 665 case 5: 666 default: 667 if (nargs <= 0) 668 abort (); 669 value = expr (); 670 } 671 672 return (value); 673} 674 675#if defined TEST_STANDALONE 676 677void 678usage (int status) 679{ 680 if (status != EXIT_SUCCESS) 681 fprintf (stderr, _("Try `%s --help' for more information.\n"), 682 program_name); 683 else 684 { 685 fputs (_("\ 686Usage: test EXPRESSION\n\ 687 or: test\n\ 688 or: [ EXPRESSION ]\n\ 689 or: [ ]\n\ 690 or: [ OPTION\n\ 691"), stdout); 692 fputs (_("\ 693Exit with the status determined by EXPRESSION.\n\ 694\n\ 695"), stdout); 696 fputs (HELP_OPTION_DESCRIPTION, stdout); 697 fputs (VERSION_OPTION_DESCRIPTION, stdout); 698 fputs (_("\ 699\n\ 700An omitted EXPRESSION defaults to false. Otherwise,\n\ 701EXPRESSION is true or false and sets exit status. It is one of:\n\ 702"), stdout); 703 fputs (_("\ 704\n\ 705 ( EXPRESSION ) EXPRESSION is true\n\ 706 ! EXPRESSION EXPRESSION is false\n\ 707 EXPRESSION1 -a EXPRESSION2 both EXPRESSION1 and EXPRESSION2 are true\n\ 708 EXPRESSION1 -o EXPRESSION2 either EXPRESSION1 or EXPRESSION2 is true\n\ 709"), stdout); 710 fputs (_("\ 711\n\ 712 -n STRING the length of STRING is nonzero\n\ 713 STRING equivalent to -n STRING\n\ 714 -z STRING the length of STRING is zero\n\ 715 STRING1 = STRING2 the strings are equal\n\ 716 STRING1 != STRING2 the strings are not equal\n\ 717"), stdout); 718 fputs (_("\ 719\n\ 720 INTEGER1 -eq INTEGER2 INTEGER1 is equal to INTEGER2\n\ 721 INTEGER1 -ge INTEGER2 INTEGER1 is greater than or equal to INTEGER2\n\ 722 INTEGER1 -gt INTEGER2 INTEGER1 is greater than INTEGER2\n\ 723 INTEGER1 -le INTEGER2 INTEGER1 is less than or equal to INTEGER2\n\ 724 INTEGER1 -lt INTEGER2 INTEGER1 is less than INTEGER2\n\ 725 INTEGER1 -ne INTEGER2 INTEGER1 is not equal to INTEGER2\n\ 726"), stdout); 727 fputs (_("\ 728\n\ 729 FILE1 -ef FILE2 FILE1 and FILE2 have the same device and inode numbers\n\ 730 FILE1 -nt FILE2 FILE1 is newer (modification date) than FILE2\n\ 731 FILE1 -ot FILE2 FILE1 is older than FILE2\n\ 732"), stdout); 733 fputs (_("\ 734\n\ 735 -b FILE FILE exists and is block special\n\ 736 -c FILE FILE exists and is character special\n\ 737 -d FILE FILE exists and is a directory\n\ 738 -e FILE FILE exists\n\ 739"), stdout); 740 fputs (_("\ 741 -f FILE FILE exists and is a regular file\n\ 742 -g FILE FILE exists and is set-group-ID\n\ 743 -G FILE FILE exists and is owned by the effective group ID\n\ 744 -h FILE FILE exists and is a symbolic link (same as -L)\n\ 745 -k FILE FILE exists and has its sticky bit set\n\ 746"), stdout); 747 fputs (_("\ 748 -L FILE FILE exists and is a symbolic link (same as -h)\n\ 749 -O FILE FILE exists and is owned by the effective user ID\n\ 750 -p FILE FILE exists and is a named pipe\n\ 751 -r FILE FILE exists and read permission is granted\n\ 752 -s FILE FILE exists and has a size greater than zero\n\ 753"), stdout); 754 fputs (_("\ 755 -S FILE FILE exists and is a socket\n\ 756 -t FD file descriptor FD is opened on a terminal\n\ 757 -u FILE FILE exists and its set-user-ID bit is set\n\ 758 -w FILE FILE exists and write permission is granted\n\ 759 -x FILE FILE exists and execute (or search) permission is granted\n\ 760"), stdout); 761 fputs (_("\ 762\n\ 763Except for -h and -L, all FILE-related tests dereference symbolic links.\n\ 764Beware that parentheses need to be escaped (e.g., by backslashes) for shells.\n\ 765INTEGER may also be -l STRING, which evaluates to the length of STRING.\n\ 766"), stdout); 767 fputs (_("\ 768\n\ 769NOTE: [ honors the --help and --version options, but test does not.\n\ 770test treats each of those as it treats any other nonempty STRING.\n\ 771"), stdout); 772 printf (USAGE_BUILTIN_WARNING, _("test and/or [")); 773 emit_ancillary_info (); 774 } 775 exit (status); 776} 777#endif /* TEST_STANDALONE */ 778 779#if !defined TEST_STANDALONE 780# define main test_command 781#endif 782 783#define AUTHORS \ 784 proper_name ("Kevin Braunsdorf"), \ 785 proper_name ("Matthew Bradburn") 786 787/* 788 * [: 789 * '[' expr ']' 790 * test: 791 * test expr 792 */ 793int 794main (int margc, char **margv) 795{ 796 bool value; 797 798#if !defined TEST_STANDALONE 799 int code; 800 801 code = setjmp (test_exit_buf); 802 803 if (code) 804 return (test_error_return); 805#else /* TEST_STANDALONE */ 806 initialize_main (&margc, &margv); 807 set_program_name (margv[0]); 808 setlocale (LC_ALL, ""); 809 bindtextdomain (PACKAGE, LOCALEDIR); 810 textdomain (PACKAGE); 811 812 initialize_exit_failure (TEST_FAILURE); 813 atexit (close_stdout); 814#endif /* TEST_STANDALONE */ 815 816 argv = margv; 817 818 if (LBRACKET) 819 { 820 /* Recognize --help or --version, but only when invoked in the 821 "[" form, when the last argument is not "]". Use direct 822 parsing, rather than parse_long_options, to avoid accepting 823 abbreviations. POSIX allows "[ --help" and "[ --version" to 824 have the usual GNU behavior, but it requires "test --help" 825 and "test --version" to exit silently with status 0. */ 826 if (margc == 2) 827 { 828 if (STREQ (margv[1], "--help")) 829 usage (EXIT_SUCCESS); 830 831 if (STREQ (margv[1], "--version")) 832 { 833 version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS, 834 (char *) NULL); 835 test_exit (EXIT_SUCCESS); 836 } 837 } 838 if (margc < 2 || !STREQ (margv[margc - 1], "]")) 839 test_syntax_error (_("missing `]'"), NULL); 840 841 --margc; 842 } 843 844 argc = margc; 845 pos = 1; 846 847 if (pos >= argc) 848 test_exit (TEST_FALSE); 849 850 value = posixtest (argc - 1); 851 852 if (pos != argc) 853 test_syntax_error (_("extra argument %s"), quote (argv[pos])); 854 855 test_exit (value ? TEST_TRUE : TEST_FALSE); 856} 857