diff options
author | Damien Miller <djm@mindrot.org> | 2007-10-26 14:27:45 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2007-10-26 14:27:45 +1000 |
commit | 1cbc292bc0b43a03071ef4924d14186dd0cf054e (patch) | |
tree | 972ac460d199727262aa83a738b22aac0076b2f1 | |
parent | 5a4456c6a592143a0a25ad925279eaf9ff620cc4 (diff) |
- djm@cvs.openbsd.org 2007/10/24 03:30:02
[sftp.c]
rework argument splitting and parsing to cope correctly with common
shell escapes and make handling of escaped characters consistent
with sh(1) and between sftp commands (especially between ones that
glob their arguments and ones that don't).
parse command flags using getopt(3) rather than hand-rolled parsers.
ok dtucker@
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | sftp.c | 449 |
2 files changed, 286 insertions, 173 deletions
@@ -41,6 +41,14 @@ | |||
41 | [readconf.c] | 41 | [readconf.c] |
42 | make sure that both the local and remote port are correct when | 42 | make sure that both the local and remote port are correct when |
43 | parsing -L; Jan Pechanec (bz #1378) | 43 | parsing -L; Jan Pechanec (bz #1378) |
44 | - djm@cvs.openbsd.org 2007/10/24 03:30:02 | ||
45 | [sftp.c] | ||
46 | rework argument splitting and parsing to cope correctly with common | ||
47 | shell escapes and make handling of escaped characters consistent | ||
48 | with sh(1) and between sftp commands (especially between ones that | ||
49 | glob their arguments and ones that don't). | ||
50 | parse command flags using getopt(3) rather than hand-rolled parsers. | ||
51 | ok dtucker@ | ||
44 | 52 | ||
45 | 20070927 | 53 | 20070927 |
46 | - (dtucker) [configure.ac atomicio.c] Fall back to including <sys/poll.h> if | 54 | - (dtucker) [configure.ac atomicio.c] Fall back to including <sys/poll.h> if |
@@ -3312,4 +3320,4 @@ | |||
3312 | OpenServer 6 and add osr5bigcrypt support so when someone migrates | 3320 | OpenServer 6 and add osr5bigcrypt support so when someone migrates |
3313 | passwords between UnixWare and OpenServer they will still work. OK dtucker@ | 3321 | passwords between UnixWare and OpenServer they will still work. OK dtucker@ |
3314 | 3322 | ||
3315 | $Id: ChangeLog,v 1.4769 2007/10/26 04:27:22 djm Exp $ | 3323 | $Id: ChangeLog,v 1.4770 2007/10/26 04:27:45 djm Exp $ |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sftp.c,v 1.96 2007/01/03 04:09:15 stevesk Exp $ */ | 1 | /* $OpenBSD: sftp.c,v 1.97 2007/10/24 03:30:02 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> | 3 | * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> |
4 | * | 4 | * |
@@ -26,6 +26,7 @@ | |||
26 | #include <sys/socket.h> | 26 | #include <sys/socket.h> |
27 | #include <sys/wait.h> | 27 | #include <sys/wait.h> |
28 | 28 | ||
29 | #include <ctype.h> | ||
29 | #include <errno.h> | 30 | #include <errno.h> |
30 | 31 | ||
31 | #ifdef HAVE_PATHS_H | 32 | #ifdef HAVE_PATHS_H |
@@ -346,144 +347,78 @@ infer_path(const char *p, char **ifp) | |||
346 | } | 347 | } |
347 | 348 | ||
348 | static int | 349 | static int |
349 | parse_getput_flags(const char **cpp, int *pflag) | 350 | parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag) |
350 | { | 351 | { |
351 | const char *cp = *cpp; | 352 | extern int optind, optreset, opterr; |
353 | int ch; | ||
352 | 354 | ||
353 | /* Check for flags */ | 355 | optind = optreset = 1; |
354 | if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) { | 356 | opterr = 0; |
355 | switch (cp[1]) { | 357 | |
358 | *pflag = 0; | ||
359 | while ((ch = getopt(argc, argv, "Pp")) != -1) { | ||
360 | switch (ch) { | ||
356 | case 'p': | 361 | case 'p': |
357 | case 'P': | 362 | case 'P': |
358 | *pflag = 1; | 363 | *pflag = 1; |
359 | break; | 364 | break; |
360 | default: | 365 | default: |
361 | error("Invalid flag -%c", cp[1]); | 366 | error("%s: Invalid flag -%c", cmd, ch); |
362 | return(-1); | 367 | return -1; |
363 | } | ||
364 | cp += 2; | ||
365 | *cpp = cp + strspn(cp, WHITESPACE); | ||
366 | } | ||
367 | |||
368 | return(0); | ||
369 | } | ||
370 | |||
371 | static int | ||
372 | parse_ls_flags(const char **cpp, int *lflag) | ||
373 | { | ||
374 | const char *cp = *cpp; | ||
375 | |||
376 | /* Defaults */ | ||
377 | *lflag = LS_NAME_SORT; | ||
378 | |||
379 | /* Check for flags */ | ||
380 | if (cp++[0] == '-') { | ||
381 | for (; strchr(WHITESPACE, *cp) == NULL; cp++) { | ||
382 | switch (*cp) { | ||
383 | case 'l': | ||
384 | *lflag &= ~VIEW_FLAGS; | ||
385 | *lflag |= LS_LONG_VIEW; | ||
386 | break; | ||
387 | case '1': | ||
388 | *lflag &= ~VIEW_FLAGS; | ||
389 | *lflag |= LS_SHORT_VIEW; | ||
390 | break; | ||
391 | case 'n': | ||
392 | *lflag &= ~VIEW_FLAGS; | ||
393 | *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; | ||
394 | break; | ||
395 | case 'S': | ||
396 | *lflag &= ~SORT_FLAGS; | ||
397 | *lflag |= LS_SIZE_SORT; | ||
398 | break; | ||
399 | case 't': | ||
400 | *lflag &= ~SORT_FLAGS; | ||
401 | *lflag |= LS_TIME_SORT; | ||
402 | break; | ||
403 | case 'r': | ||
404 | *lflag |= LS_REVERSE_SORT; | ||
405 | break; | ||
406 | case 'f': | ||
407 | *lflag &= ~SORT_FLAGS; | ||
408 | break; | ||
409 | case 'a': | ||
410 | *lflag |= LS_SHOW_ALL; | ||
411 | break; | ||
412 | default: | ||
413 | error("Invalid flag -%c", *cp); | ||
414 | return(-1); | ||
415 | } | ||
416 | } | 368 | } |
417 | *cpp = cp + strspn(cp, WHITESPACE); | ||
418 | } | 369 | } |
419 | 370 | ||
420 | return(0); | 371 | return optind; |
421 | } | 372 | } |
422 | 373 | ||
423 | static int | 374 | static int |
424 | get_pathname(const char **cpp, char **path) | 375 | parse_ls_flags(char **argv, int argc, int *lflag) |
425 | { | 376 | { |
426 | const char *cp = *cpp, *end; | 377 | extern int optind, optreset, opterr; |
427 | char quot; | 378 | int ch; |
428 | u_int i, j; | ||
429 | |||
430 | cp += strspn(cp, WHITESPACE); | ||
431 | if (!*cp) { | ||
432 | *cpp = cp; | ||
433 | *path = NULL; | ||
434 | return (0); | ||
435 | } | ||
436 | |||
437 | *path = xmalloc(strlen(cp) + 1); | ||
438 | 379 | ||
439 | /* Check for quoted filenames */ | 380 | optind = optreset = 1; |
440 | if (*cp == '\"' || *cp == '\'') { | 381 | opterr = 0; |
441 | quot = *cp++; | ||
442 | 382 | ||
443 | /* Search for terminating quote, unescape some chars */ | 383 | *lflag = LS_NAME_SORT; |
444 | for (i = j = 0; i <= strlen(cp); i++) { | 384 | while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) { |
445 | if (cp[i] == quot) { /* Found quote */ | 385 | switch (ch) { |
446 | i++; | 386 | case '1': |
447 | (*path)[j] = '\0'; | 387 | *lflag &= ~VIEW_FLAGS; |
448 | break; | 388 | *lflag |= LS_SHORT_VIEW; |
449 | } | 389 | break; |
450 | if (cp[i] == '\0') { /* End of string */ | 390 | case 'S': |
451 | error("Unterminated quote"); | 391 | *lflag &= ~SORT_FLAGS; |
452 | goto fail; | 392 | *lflag |= LS_SIZE_SORT; |
453 | } | 393 | break; |
454 | if (cp[i] == '\\') { /* Escaped characters */ | 394 | case 'a': |
455 | i++; | 395 | *lflag |= LS_SHOW_ALL; |
456 | if (cp[i] != '\'' && cp[i] != '\"' && | 396 | break; |
457 | cp[i] != '\\') { | 397 | case 'f': |
458 | error("Bad escaped character '\\%c'", | 398 | *lflag &= ~SORT_FLAGS; |
459 | cp[i]); | 399 | break; |
460 | goto fail; | 400 | case 'l': |
461 | } | 401 | *lflag &= ~VIEW_FLAGS; |
462 | } | 402 | *lflag |= LS_LONG_VIEW; |
463 | (*path)[j++] = cp[i]; | 403 | break; |
464 | } | 404 | case 'n': |
465 | 405 | *lflag &= ~VIEW_FLAGS; | |
466 | if (j == 0) { | 406 | *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; |
467 | error("Empty quotes"); | 407 | break; |
468 | goto fail; | 408 | case 'r': |
409 | *lflag |= LS_REVERSE_SORT; | ||
410 | break; | ||
411 | case 't': | ||
412 | *lflag &= ~SORT_FLAGS; | ||
413 | *lflag |= LS_TIME_SORT; | ||
414 | break; | ||
415 | default: | ||
416 | error("ls: Invalid flag -%c", ch); | ||
417 | return -1; | ||
469 | } | 418 | } |
470 | *cpp = cp + i + strspn(cp + i, WHITESPACE); | ||
471 | } else { | ||
472 | /* Read to end of filename */ | ||
473 | end = strpbrk(cp, WHITESPACE); | ||
474 | if (end == NULL) | ||
475 | end = strchr(cp, '\0'); | ||
476 | *cpp = end + strspn(end, WHITESPACE); | ||
477 | |||
478 | memcpy(*path, cp, end - cp); | ||
479 | (*path)[end - cp] = '\0'; | ||
480 | } | 419 | } |
481 | return (0); | ||
482 | 420 | ||
483 | fail: | 421 | return optind; |
484 | xfree(*path); | ||
485 | *path = NULL; | ||
486 | return (-1); | ||
487 | } | 422 | } |
488 | 423 | ||
489 | static int | 424 | static int |
@@ -866,15 +801,189 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, | |||
866 | return (0); | 801 | return (0); |
867 | } | 802 | } |
868 | 803 | ||
804 | /* | ||
805 | * Undo escaping of glob sequences in place. Used to undo extra escaping | ||
806 | * applied in makeargv() when the string is destined for a function that | ||
807 | * does not glob it. | ||
808 | */ | ||
809 | static void | ||
810 | undo_glob_escape(char *s) | ||
811 | { | ||
812 | size_t i, j; | ||
813 | |||
814 | for (i = j = 0;;) { | ||
815 | if (s[i] == '\0') { | ||
816 | s[j] = '\0'; | ||
817 | return; | ||
818 | } | ||
819 | if (s[i] != '\\') { | ||
820 | s[j++] = s[i++]; | ||
821 | continue; | ||
822 | } | ||
823 | /* s[i] == '\\' */ | ||
824 | ++i; | ||
825 | switch (s[i]) { | ||
826 | case '?': | ||
827 | case '[': | ||
828 | case '*': | ||
829 | case '\\': | ||
830 | s[j++] = s[i++]; | ||
831 | break; | ||
832 | case '\0': | ||
833 | s[j++] = '\\'; | ||
834 | s[j] = '\0'; | ||
835 | return; | ||
836 | default: | ||
837 | s[j++] = '\\'; | ||
838 | s[j++] = s[i++]; | ||
839 | break; | ||
840 | } | ||
841 | } | ||
842 | } | ||
843 | |||
844 | /* | ||
845 | * Split a string into an argument vector using sh(1)-style quoting, | ||
846 | * comment and escaping rules, but with some tweaks to handle glob(3) | ||
847 | * wildcards. | ||
848 | * Returns NULL on error or a NULL-terminated array of arguments. | ||
849 | */ | ||
850 | #define MAXARGS 128 | ||
851 | #define MAXARGLEN 8192 | ||
852 | static char ** | ||
853 | makeargv(const char *arg, int *argcp) | ||
854 | { | ||
855 | int argc, quot; | ||
856 | size_t i, j; | ||
857 | static char argvs[MAXARGLEN]; | ||
858 | static char *argv[MAXARGS + 1]; | ||
859 | enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q; | ||
860 | |||
861 | *argcp = argc = 0; | ||
862 | if (strlen(arg) > sizeof(argvs) - 1) { | ||
863 | args_too_longs: | ||
864 | error("string too long"); | ||
865 | return NULL; | ||
866 | } | ||
867 | state = MA_START; | ||
868 | i = j = 0; | ||
869 | for (;;) { | ||
870 | if (isspace(arg[i])) { | ||
871 | if (state == MA_UNQUOTED) { | ||
872 | /* Terminate current argument */ | ||
873 | argvs[j++] = '\0'; | ||
874 | argc++; | ||
875 | state = MA_START; | ||
876 | } else if (state != MA_START) | ||
877 | argvs[j++] = arg[i]; | ||
878 | } else if (arg[i] == '"' || arg[i] == '\'') { | ||
879 | q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE; | ||
880 | if (state == MA_START) { | ||
881 | argv[argc] = argvs + j; | ||
882 | state = q; | ||
883 | } else if (state == MA_UNQUOTED) | ||
884 | state = q; | ||
885 | else if (state == q) | ||
886 | state = MA_UNQUOTED; | ||
887 | else | ||
888 | argvs[j++] = arg[i]; | ||
889 | } else if (arg[i] == '\\') { | ||
890 | if (state == MA_SQUOTE || state == MA_DQUOTE) { | ||
891 | quot = state == MA_SQUOTE ? '\'' : '"'; | ||
892 | /* Unescape quote we are in */ | ||
893 | /* XXX support \n and friends? */ | ||
894 | if (arg[i + 1] == quot) { | ||
895 | i++; | ||
896 | argvs[j++] = arg[i]; | ||
897 | } else if (arg[i + 1] == '?' || | ||
898 | arg[i + 1] == '[' || arg[i + 1] == '*') { | ||
899 | /* | ||
900 | * Special case for sftp: append | ||
901 | * double-escaped glob sequence - | ||
902 | * glob will undo one level of | ||
903 | * escaping. NB. string can grow here. | ||
904 | */ | ||
905 | if (j >= sizeof(argvs) - 5) | ||
906 | goto args_too_longs; | ||
907 | argvs[j++] = '\\'; | ||
908 | argvs[j++] = arg[i++]; | ||
909 | argvs[j++] = '\\'; | ||
910 | argvs[j++] = arg[i]; | ||
911 | } else { | ||
912 | argvs[j++] = arg[i++]; | ||
913 | argvs[j++] = arg[i]; | ||
914 | } | ||
915 | } else { | ||
916 | if (state == MA_START) { | ||
917 | argv[argc] = argvs + j; | ||
918 | state = MA_UNQUOTED; | ||
919 | } | ||
920 | if (arg[i + 1] == '?' || arg[i + 1] == '[' || | ||
921 | arg[i + 1] == '*' || arg[i + 1] == '\\') { | ||
922 | /* | ||
923 | * Special case for sftp: append | ||
924 | * escaped glob sequence - | ||
925 | * glob will undo one level of | ||
926 | * escaping. | ||
927 | */ | ||
928 | argvs[j++] = arg[i++]; | ||
929 | argvs[j++] = arg[i]; | ||
930 | } else { | ||
931 | /* Unescape everything */ | ||
932 | /* XXX support \n and friends? */ | ||
933 | i++; | ||
934 | argvs[j++] = arg[i]; | ||
935 | } | ||
936 | } | ||
937 | } else if (arg[i] == '#') { | ||
938 | if (state == MA_SQUOTE || state == MA_DQUOTE) | ||
939 | argvs[j++] = arg[i]; | ||
940 | else | ||
941 | goto string_done; | ||
942 | } else if (arg[i] == '\0') { | ||
943 | if (state == MA_SQUOTE || state == MA_DQUOTE) { | ||
944 | error("Unterminated quoted argument"); | ||
945 | return NULL; | ||
946 | } | ||
947 | string_done: | ||
948 | if (state == MA_UNQUOTED) { | ||
949 | argvs[j++] = '\0'; | ||
950 | argc++; | ||
951 | } | ||
952 | break; | ||
953 | } else { | ||
954 | if (state == MA_START) { | ||
955 | argv[argc] = argvs + j; | ||
956 | state = MA_UNQUOTED; | ||
957 | } | ||
958 | if ((state == MA_SQUOTE || state == MA_DQUOTE) && | ||
959 | (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { | ||
960 | /* | ||
961 | * Special case for sftp: escape quoted | ||
962 | * glob(3) wildcards. NB. string can grow | ||
963 | * here. | ||
964 | */ | ||
965 | if (j >= sizeof(argvs) - 3) | ||
966 | goto args_too_longs; | ||
967 | argvs[j++] = '\\'; | ||
968 | argvs[j++] = arg[i]; | ||
969 | } else | ||
970 | argvs[j++] = arg[i]; | ||
971 | } | ||
972 | i++; | ||
973 | } | ||
974 | *argcp = argc; | ||
975 | return argv; | ||
976 | } | ||
977 | |||
869 | static int | 978 | static int |
870 | parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, | 979 | parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, |
871 | unsigned long *n_arg, char **path1, char **path2) | 980 | unsigned long *n_arg, char **path1, char **path2) |
872 | { | 981 | { |
873 | const char *cmd, *cp = *cpp; | 982 | const char *cmd, *cp = *cpp; |
874 | char *cp2; | 983 | char *cp2, **argv; |
875 | int base = 0; | 984 | int base = 0; |
876 | long l; | 985 | long l; |
877 | int i, cmdnum; | 986 | int i, cmdnum, optidx, argc; |
878 | 987 | ||
879 | /* Skip leading whitespace */ | 988 | /* Skip leading whitespace */ |
880 | cp = cp + strspn(cp, WHITESPACE); | 989 | cp = cp + strspn(cp, WHITESPACE); |
@@ -890,17 +999,13 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, | |||
890 | cp++; | 999 | cp++; |
891 | } | 1000 | } |
892 | 1001 | ||
1002 | if ((argv = makeargv(cp, &argc)) == NULL) | ||
1003 | return -1; | ||
1004 | |||
893 | /* Figure out which command we have */ | 1005 | /* Figure out which command we have */ |
894 | for (i = 0; cmds[i].c; i++) { | 1006 | for (i = 0; cmds[i].c != NULL; i++) { |
895 | int cmdlen = strlen(cmds[i].c); | 1007 | if (strcasecmp(cmds[i].c, argv[0]) == 0) |
896 | |||
897 | /* Check for command followed by whitespace */ | ||
898 | if (!strncasecmp(cp, cmds[i].c, cmdlen) && | ||
899 | strchr(WHITESPACE, cp[cmdlen])) { | ||
900 | cp += cmdlen; | ||
901 | cp = cp + strspn(cp, WHITESPACE); | ||
902 | break; | 1008 | break; |
903 | } | ||
904 | } | 1009 | } |
905 | cmdnum = cmds[i].n; | 1010 | cmdnum = cmds[i].n; |
906 | cmd = cmds[i].c; | 1011 | cmd = cmds[i].c; |
@@ -911,40 +1016,44 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, | |||
911 | cmdnum = I_SHELL; | 1016 | cmdnum = I_SHELL; |
912 | } else if (cmdnum == -1) { | 1017 | } else if (cmdnum == -1) { |
913 | error("Invalid command."); | 1018 | error("Invalid command."); |
914 | return (-1); | 1019 | return -1; |
915 | } | 1020 | } |
916 | 1021 | ||
917 | /* Get arguments and parse flags */ | 1022 | /* Get arguments and parse flags */ |
918 | *lflag = *pflag = *n_arg = 0; | 1023 | *lflag = *pflag = *n_arg = 0; |
919 | *path1 = *path2 = NULL; | 1024 | *path1 = *path2 = NULL; |
1025 | optidx = 1; | ||
920 | switch (cmdnum) { | 1026 | switch (cmdnum) { |
921 | case I_GET: | 1027 | case I_GET: |
922 | case I_PUT: | 1028 | case I_PUT: |
923 | if (parse_getput_flags(&cp, pflag)) | 1029 | if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1) |
924 | return(-1); | 1030 | return -1; |
925 | /* Get first pathname (mandatory) */ | 1031 | /* Get first pathname (mandatory) */ |
926 | if (get_pathname(&cp, path1)) | 1032 | if (argc - optidx < 1) { |
927 | return(-1); | ||
928 | if (*path1 == NULL) { | ||
929 | error("You must specify at least one path after a " | 1033 | error("You must specify at least one path after a " |
930 | "%s command.", cmd); | 1034 | "%s command.", cmd); |
931 | return(-1); | 1035 | return -1; |
1036 | } | ||
1037 | *path1 = xstrdup(argv[optidx]); | ||
1038 | /* Get second pathname (optional) */ | ||
1039 | if (argc - optidx > 1) { | ||
1040 | *path2 = xstrdup(argv[optidx + 1]); | ||
1041 | /* Destination is not globbed */ | ||
1042 | undo_glob_escape(*path2); | ||
932 | } | 1043 | } |
933 | /* Try to get second pathname (optional) */ | ||
934 | if (get_pathname(&cp, path2)) | ||
935 | return(-1); | ||
936 | break; | 1044 | break; |
937 | case I_RENAME: | 1045 | case I_RENAME: |
938 | case I_SYMLINK: | 1046 | case I_SYMLINK: |
939 | if (get_pathname(&cp, path1)) | 1047 | if (argc - optidx < 2) { |
940 | return(-1); | ||
941 | if (get_pathname(&cp, path2)) | ||
942 | return(-1); | ||
943 | if (!*path1 || !*path2) { | ||
944 | error("You must specify two paths after a %s " | 1048 | error("You must specify two paths after a %s " |
945 | "command.", cmd); | 1049 | "command.", cmd); |
946 | return(-1); | 1050 | return -1; |
947 | } | 1051 | } |
1052 | *path1 = xstrdup(argv[optidx]); | ||
1053 | *path2 = xstrdup(argv[optidx + 1]); | ||
1054 | /* Paths are not globbed */ | ||
1055 | undo_glob_escape(*path1); | ||
1056 | undo_glob_escape(*path2); | ||
948 | break; | 1057 | break; |
949 | case I_RM: | 1058 | case I_RM: |
950 | case I_MKDIR: | 1059 | case I_MKDIR: |
@@ -953,59 +1062,55 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, | |||
953 | case I_LCHDIR: | 1062 | case I_LCHDIR: |
954 | case I_LMKDIR: | 1063 | case I_LMKDIR: |
955 | /* Get pathname (mandatory) */ | 1064 | /* Get pathname (mandatory) */ |
956 | if (get_pathname(&cp, path1)) | 1065 | if (argc - optidx < 1) { |
957 | return(-1); | ||
958 | if (*path1 == NULL) { | ||
959 | error("You must specify a path after a %s command.", | 1066 | error("You must specify a path after a %s command.", |
960 | cmd); | 1067 | cmd); |
961 | return(-1); | 1068 | return -1; |
962 | } | 1069 | } |
1070 | *path1 = xstrdup(argv[optidx]); | ||
1071 | /* Only "rm" globs */ | ||
1072 | if (cmdnum != I_RM) | ||
1073 | undo_glob_escape(*path1); | ||
963 | break; | 1074 | break; |
964 | case I_LS: | 1075 | case I_LS: |
965 | if (parse_ls_flags(&cp, lflag)) | 1076 | if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1) |
966 | return(-1); | 1077 | return(-1); |
967 | /* Path is optional */ | 1078 | /* Path is optional */ |
968 | if (get_pathname(&cp, path1)) | 1079 | if (argc - optidx > 0) |
969 | return(-1); | 1080 | *path1 = xstrdup(argv[optidx]); |
970 | break; | 1081 | break; |
971 | case I_LLS: | 1082 | case I_LLS: |
972 | case I_SHELL: | 1083 | case I_SHELL: |
973 | /* Uses the rest of the line */ | 1084 | /* Uses the rest of the line */ |
974 | break; | 1085 | break; |
975 | case I_LUMASK: | 1086 | case I_LUMASK: |
976 | base = 8; | ||
977 | case I_CHMOD: | 1087 | case I_CHMOD: |
978 | base = 8; | 1088 | base = 8; |
979 | case I_CHOWN: | 1089 | case I_CHOWN: |
980 | case I_CHGRP: | 1090 | case I_CHGRP: |
981 | /* Get numeric arg (mandatory) */ | 1091 | /* Get numeric arg (mandatory) */ |
1092 | if (argc - optidx < 1) | ||
1093 | goto need_num_arg; | ||
982 | errno = 0; | 1094 | errno = 0; |
983 | l = strtol(cp, &cp2, base); | 1095 | l = strtol(argv[optidx], &cp2, base); |
984 | if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) && | 1096 | if (cp2 == argv[optidx] || *cp2 != '\0' || |
985 | errno == ERANGE) || l < 0) { | 1097 | ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) || |
1098 | l < 0) { | ||
1099 | need_num_arg: | ||
986 | error("You must supply a numeric argument " | 1100 | error("You must supply a numeric argument " |
987 | "to the %s command.", cmd); | 1101 | "to the %s command.", cmd); |
988 | return(-1); | 1102 | return -1; |
989 | } | 1103 | } |
990 | cp = cp2; | ||
991 | *n_arg = l; | 1104 | *n_arg = l; |
992 | if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp)) | 1105 | if (cmdnum == I_LUMASK) |
993 | break; | 1106 | break; |
994 | if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) { | ||
995 | error("You must supply a numeric argument " | ||
996 | "to the %s command.", cmd); | ||
997 | return(-1); | ||
998 | } | ||
999 | cp += strspn(cp, WHITESPACE); | ||
1000 | |||
1001 | /* Get pathname (mandatory) */ | 1107 | /* Get pathname (mandatory) */ |
1002 | if (get_pathname(&cp, path1)) | 1108 | if (argc - optidx < 2) { |
1003 | return(-1); | ||
1004 | if (*path1 == NULL) { | ||
1005 | error("You must specify a path after a %s command.", | 1109 | error("You must specify a path after a %s command.", |
1006 | cmd); | 1110 | cmd); |
1007 | return(-1); | 1111 | return -1; |
1008 | } | 1112 | } |
1113 | *path1 = xstrdup(argv[optidx + 1]); | ||
1009 | break; | 1114 | break; |
1010 | case I_QUIT: | 1115 | case I_QUIT: |
1011 | case I_PWD: | 1116 | case I_PWD: |