diff options
-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: |