diff options
Diffstat (limited to 'sftp-int.c')
-rw-r--r-- | sftp-int.c | 217 |
1 files changed, 130 insertions, 87 deletions
diff --git a/sftp-int.c b/sftp-int.c index 6987de9a3..94299aa43 100644 --- a/sftp-int.c +++ b/sftp-int.c | |||
@@ -25,7 +25,7 @@ | |||
25 | /* XXX: recursive operations */ | 25 | /* XXX: recursive operations */ |
26 | 26 | ||
27 | #include "includes.h" | 27 | #include "includes.h" |
28 | RCSID("$OpenBSD: sftp-int.c,v 1.57 2003/03/05 22:33:43 markus Exp $"); | 28 | RCSID("$OpenBSD: sftp-int.c,v 1.62 2003/08/25 08:13:09 fgsch Exp $"); |
29 | 29 | ||
30 | #include "buffer.h" | 30 | #include "buffer.h" |
31 | #include "xmalloc.h" | 31 | #include "xmalloc.h" |
@@ -53,6 +53,10 @@ int showprogress = 1; | |||
53 | /* Seperators for interactive commands */ | 53 | /* Seperators for interactive commands */ |
54 | #define WHITESPACE " \t\r\n" | 54 | #define WHITESPACE " \t\r\n" |
55 | 55 | ||
56 | /* Define what type of ls view (0 - multi-column) */ | ||
57 | #define LONG_VIEW 1 /* Full view ala ls -l */ | ||
58 | #define SHORT_VIEW 2 /* Single row view ala ls -1 */ | ||
59 | |||
56 | /* Commands for interactive mode */ | 60 | /* Commands for interactive mode */ |
57 | #define I_CHDIR 1 | 61 | #define I_CHDIR 1 |
58 | #define I_CHGRP 2 | 62 | #define I_CHGRP 2 |
@@ -307,7 +311,10 @@ parse_ls_flags(const char **cpp, int *lflag) | |||
307 | for(; strchr(WHITESPACE, *cp) == NULL; cp++) { | 311 | for(; strchr(WHITESPACE, *cp) == NULL; cp++) { |
308 | switch (*cp) { | 312 | switch (*cp) { |
309 | case 'l': | 313 | case 'l': |
310 | *lflag = 1; | 314 | *lflag = LONG_VIEW; |
315 | break; | ||
316 | case '1': | ||
317 | *lflag = SHORT_VIEW; | ||
311 | break; | 318 | break; |
312 | default: | 319 | default: |
313 | error("Invalid flag -%c", *cp); | 320 | error("Invalid flag -%c", *cp); |
@@ -325,7 +332,7 @@ get_pathname(const char **cpp, char **path) | |||
325 | { | 332 | { |
326 | const char *cp = *cpp, *end; | 333 | const char *cp = *cpp, *end; |
327 | char quot; | 334 | char quot; |
328 | int i; | 335 | int i, j; |
329 | 336 | ||
330 | cp += strspn(cp, WHITESPACE); | 337 | cp += strspn(cp, WHITESPACE); |
331 | if (!*cp) { | 338 | if (!*cp) { |
@@ -334,37 +341,54 @@ get_pathname(const char **cpp, char **path) | |||
334 | return (0); | 341 | return (0); |
335 | } | 342 | } |
336 | 343 | ||
344 | *path = xmalloc(strlen(cp) + 1); | ||
345 | |||
337 | /* Check for quoted filenames */ | 346 | /* Check for quoted filenames */ |
338 | if (*cp == '\"' || *cp == '\'') { | 347 | if (*cp == '\"' || *cp == '\'') { |
339 | quot = *cp++; | 348 | quot = *cp++; |
340 | 349 | ||
341 | end = strchr(cp, quot); | 350 | /* Search for terminating quote, unescape some chars */ |
342 | if (end == NULL) { | 351 | for (i = j = 0; i <= strlen(cp); i++) { |
343 | error("Unterminated quote"); | 352 | if (cp[i] == quot) { /* Found quote */ |
344 | goto fail; | 353 | (*path)[j] = '\0'; |
354 | break; | ||
355 | } | ||
356 | if (cp[i] == '\0') { /* End of string */ | ||
357 | error("Unterminated quote"); | ||
358 | goto fail; | ||
359 | } | ||
360 | if (cp[i] == '\\') { /* Escaped characters */ | ||
361 | i++; | ||
362 | if (cp[i] != '\'' && cp[i] != '\"' && | ||
363 | cp[i] != '\\') { | ||
364 | error("Bad escaped character '\%c'", | ||
365 | cp[i]); | ||
366 | goto fail; | ||
367 | } | ||
368 | } | ||
369 | (*path)[j++] = cp[i]; | ||
345 | } | 370 | } |
346 | if (cp == end) { | 371 | |
372 | if (j == 0) { | ||
347 | error("Empty quotes"); | 373 | error("Empty quotes"); |
348 | goto fail; | 374 | goto fail; |
349 | } | 375 | } |
350 | *cpp = end + 1 + strspn(end + 1, WHITESPACE); | 376 | *cpp = cp + i + strspn(cp + i, WHITESPACE); |
351 | } else { | 377 | } else { |
352 | /* Read to end of filename */ | 378 | /* Read to end of filename */ |
353 | end = strpbrk(cp, WHITESPACE); | 379 | end = strpbrk(cp, WHITESPACE); |
354 | if (end == NULL) | 380 | if (end == NULL) |
355 | end = strchr(cp, '\0'); | 381 | end = strchr(cp, '\0'); |
356 | *cpp = end + strspn(end, WHITESPACE); | 382 | *cpp = end + strspn(end, WHITESPACE); |
357 | } | ||
358 | 383 | ||
359 | i = end - cp; | 384 | memcpy(*path, cp, end - cp); |
360 | 385 | (*path)[end - cp] = '\0'; | |
361 | *path = xmalloc(i + 1); | 386 | } |
362 | memcpy(*path, cp, i); | 387 | return (0); |
363 | (*path)[i] = '\0'; | ||
364 | return(0); | ||
365 | 388 | ||
366 | fail: | 389 | fail: |
367 | *path = NULL; | 390 | xfree(*path); |
391 | *path = NULL; | ||
368 | return (-1); | 392 | return (-1); |
369 | } | 393 | } |
370 | 394 | ||
@@ -425,29 +449,8 @@ process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) | |||
425 | goto out; | 449 | goto out; |
426 | } | 450 | } |
427 | 451 | ||
428 | /* Only one match, dst may be file, directory or unspecified */ | 452 | /* If multiple matches, dst must be a directory or unspecified */ |
429 | if (g.gl_pathv[0] && g.gl_matchc == 1) { | 453 | if (g.gl_matchc > 1 && dst && !is_dir(dst)) { |
430 | if (dst) { | ||
431 | /* If directory specified, append filename */ | ||
432 | if (is_dir(dst)) { | ||
433 | if (infer_path(g.gl_pathv[0], &tmp)) { | ||
434 | err = 1; | ||
435 | goto out; | ||
436 | } | ||
437 | abs_dst = path_append(dst, tmp); | ||
438 | xfree(tmp); | ||
439 | } else | ||
440 | abs_dst = xstrdup(dst); | ||
441 | } else if (infer_path(g.gl_pathv[0], &abs_dst)) { | ||
442 | err = -1; | ||
443 | goto out; | ||
444 | } | ||
445 | err = do_download(conn, g.gl_pathv[0], abs_dst, pflag); | ||
446 | goto out; | ||
447 | } | ||
448 | |||
449 | /* Multiple matches, dst may be directory or unspecified */ | ||
450 | if (dst && !is_dir(dst)) { | ||
451 | error("Multiple files match, but \"%s\" is not a directory", | 454 | error("Multiple files match, but \"%s\" is not a directory", |
452 | dst); | 455 | dst); |
453 | err = -1; | 456 | err = -1; |
@@ -459,7 +462,19 @@ process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) | |||
459 | err = -1; | 462 | err = -1; |
460 | goto out; | 463 | goto out; |
461 | } | 464 | } |
462 | if (dst) { | 465 | |
466 | if (g.gl_matchc == 1 && dst) { | ||
467 | /* If directory specified, append filename */ | ||
468 | if (is_dir(dst)) { | ||
469 | if (infer_path(g.gl_pathv[0], &tmp)) { | ||
470 | err = 1; | ||
471 | goto out; | ||
472 | } | ||
473 | abs_dst = path_append(dst, tmp); | ||
474 | xfree(tmp); | ||
475 | } else | ||
476 | abs_dst = xstrdup(dst); | ||
477 | } else if (dst) { | ||
463 | abs_dst = path_append(dst, tmp); | 478 | abs_dst = path_append(dst, tmp); |
464 | xfree(tmp); | 479 | xfree(tmp); |
465 | } else | 480 | } else |
@@ -503,38 +518,8 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) | |||
503 | goto out; | 518 | goto out; |
504 | } | 519 | } |
505 | 520 | ||
506 | /* Only one match, dst may be file, directory or unspecified */ | 521 | /* If multiple matches, dst may be directory or unspecified */ |
507 | if (g.gl_pathv[0] && g.gl_matchc == 1) { | 522 | if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) { |
508 | if (!is_reg(g.gl_pathv[0])) { | ||
509 | error("Can't upload %s: not a regular file", | ||
510 | g.gl_pathv[0]); | ||
511 | err = 1; | ||
512 | goto out; | ||
513 | } | ||
514 | if (tmp_dst) { | ||
515 | /* If directory specified, append filename */ | ||
516 | if (remote_is_dir(conn, tmp_dst)) { | ||
517 | if (infer_path(g.gl_pathv[0], &tmp)) { | ||
518 | err = 1; | ||
519 | goto out; | ||
520 | } | ||
521 | abs_dst = path_append(tmp_dst, tmp); | ||
522 | xfree(tmp); | ||
523 | } else | ||
524 | abs_dst = xstrdup(tmp_dst); | ||
525 | } else { | ||
526 | if (infer_path(g.gl_pathv[0], &abs_dst)) { | ||
527 | err = -1; | ||
528 | goto out; | ||
529 | } | ||
530 | abs_dst = make_absolute(abs_dst, pwd); | ||
531 | } | ||
532 | err = do_upload(conn, g.gl_pathv[0], abs_dst, pflag); | ||
533 | goto out; | ||
534 | } | ||
535 | |||
536 | /* Multiple matches, dst may be directory or unspecified */ | ||
537 | if (tmp_dst && !remote_is_dir(conn, tmp_dst)) { | ||
538 | error("Multiple files match, but \"%s\" is not a directory", | 523 | error("Multiple files match, but \"%s\" is not a directory", |
539 | tmp_dst); | 524 | tmp_dst); |
540 | err = -1; | 525 | err = -1; |
@@ -551,7 +536,20 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) | |||
551 | err = -1; | 536 | err = -1; |
552 | goto out; | 537 | goto out; |
553 | } | 538 | } |
554 | if (tmp_dst) { | 539 | |
540 | if (g.gl_matchc == 1 && tmp_dst) { | ||
541 | /* If directory specified, append filename */ | ||
542 | if (remote_is_dir(conn, tmp_dst)) { | ||
543 | if (infer_path(g.gl_pathv[0], &tmp)) { | ||
544 | err = 1; | ||
545 | goto out; | ||
546 | } | ||
547 | abs_dst = path_append(tmp_dst, tmp); | ||
548 | xfree(tmp); | ||
549 | } else | ||
550 | abs_dst = xstrdup(tmp_dst); | ||
551 | |||
552 | } else if (tmp_dst) { | ||
555 | abs_dst = path_append(tmp_dst, tmp); | 553 | abs_dst = path_append(tmp_dst, tmp); |
556 | xfree(tmp); | 554 | xfree(tmp); |
557 | } else | 555 | } else |
@@ -567,6 +565,7 @@ out: | |||
567 | xfree(abs_dst); | 565 | xfree(abs_dst); |
568 | if (tmp_dst) | 566 | if (tmp_dst) |
569 | xfree(tmp_dst); | 567 | xfree(tmp_dst); |
568 | globfree(&g); | ||
570 | return(err); | 569 | return(err); |
571 | } | 570 | } |
572 | 571 | ||
@@ -583,15 +582,27 @@ sdirent_comp(const void *aa, const void *bb) | |||
583 | static int | 582 | static int |
584 | do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) | 583 | do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) |
585 | { | 584 | { |
586 | int n; | 585 | int n, c = 1, colspace = 0, columns = 1; |
587 | SFTP_DIRENT **d; | 586 | SFTP_DIRENT **d; |
588 | 587 | ||
589 | if ((n = do_readdir(conn, path, &d)) != 0) | 588 | if ((n = do_readdir(conn, path, &d)) != 0) |
590 | return (n); | 589 | return (n); |
591 | 590 | ||
592 | /* Count entries for sort */ | 591 | if (!(lflag & SHORT_VIEW)) { |
593 | for (n = 0; d[n] != NULL; n++) | 592 | int m = 0, width = 80; |
594 | ; | 593 | struct winsize ws; |
594 | |||
595 | /* Count entries for sort and find longest filename */ | ||
596 | for (n = 0; d[n] != NULL; n++) | ||
597 | m = MAX(m, strlen(d[n]->filename)); | ||
598 | |||
599 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) | ||
600 | width = ws.ws_col; | ||
601 | |||
602 | columns = width / (m + 2); | ||
603 | columns = MAX(columns, 1); | ||
604 | colspace = width / columns; | ||
605 | } | ||
595 | 606 | ||
596 | qsort(d, n, sizeof(*d), sdirent_comp); | 607 | qsort(d, n, sizeof(*d), sdirent_comp); |
597 | 608 | ||
@@ -602,7 +613,7 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) | |||
602 | fname = path_strip(tmp, strip_path); | 613 | fname = path_strip(tmp, strip_path); |
603 | xfree(tmp); | 614 | xfree(tmp); |
604 | 615 | ||
605 | if (lflag) { | 616 | if (lflag & LONG_VIEW) { |
606 | char *lname; | 617 | char *lname; |
607 | struct stat sb; | 618 | struct stat sb; |
608 | 619 | ||
@@ -612,13 +623,20 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) | |||
612 | printf("%s\n", lname); | 623 | printf("%s\n", lname); |
613 | xfree(lname); | 624 | xfree(lname); |
614 | } else { | 625 | } else { |
615 | /* XXX - multicolumn display would be nice here */ | 626 | printf("%-*s", colspace, fname); |
616 | printf("%s\n", fname); | 627 | if (c >= columns) { |
628 | printf("\n"); | ||
629 | c = 1; | ||
630 | } else | ||
631 | c++; | ||
617 | } | 632 | } |
618 | 633 | ||
619 | xfree(fname); | 634 | xfree(fname); |
620 | } | 635 | } |
621 | 636 | ||
637 | if (!(lflag & LONG_VIEW) && (c != 1)) | ||
638 | printf("\n"); | ||
639 | |||
622 | free_sftp_dirents(d); | 640 | free_sftp_dirents(d); |
623 | return (0); | 641 | return (0); |
624 | } | 642 | } |
@@ -629,9 +647,8 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, | |||
629 | int lflag) | 647 | int lflag) |
630 | { | 648 | { |
631 | glob_t g; | 649 | glob_t g; |
632 | int i; | 650 | int i, c = 1, colspace = 0, columns = 1; |
633 | Attrib *a; | 651 | Attrib *a; |
634 | struct stat sb; | ||
635 | 652 | ||
636 | memset(&g, 0, sizeof(g)); | 653 | memset(&g, 0, sizeof(g)); |
637 | 654 | ||
@@ -658,12 +675,31 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, | |||
658 | } | 675 | } |
659 | } | 676 | } |
660 | 677 | ||
678 | if (!(lflag & SHORT_VIEW)) { | ||
679 | int m = 0, width = 80; | ||
680 | struct winsize ws; | ||
681 | |||
682 | /* Count entries for sort and find longest filename */ | ||
683 | for (i = 0; g.gl_pathv[i]; i++) | ||
684 | m = MAX(m, strlen(g.gl_pathv[i])); | ||
685 | |||
686 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) | ||
687 | width = ws.ws_col; | ||
688 | |||
689 | columns = width / (m + 2); | ||
690 | columns = MAX(columns, 1); | ||
691 | colspace = width / columns; | ||
692 | } | ||
693 | |||
661 | for (i = 0; g.gl_pathv[i]; i++) { | 694 | for (i = 0; g.gl_pathv[i]; i++) { |
662 | char *fname, *lname; | 695 | char *fname; |
663 | 696 | ||
664 | fname = path_strip(g.gl_pathv[i], strip_path); | 697 | fname = path_strip(g.gl_pathv[i], strip_path); |
665 | 698 | ||
666 | if (lflag) { | 699 | if (lflag & LONG_VIEW) { |
700 | char *lname; | ||
701 | struct stat sb; | ||
702 | |||
667 | /* | 703 | /* |
668 | * XXX: this is slow - 1 roundtrip per path | 704 | * XXX: this is slow - 1 roundtrip per path |
669 | * A solution to this is to fork glob() and | 705 | * A solution to this is to fork glob() and |
@@ -679,12 +715,19 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, | |||
679 | printf("%s\n", lname); | 715 | printf("%s\n", lname); |
680 | xfree(lname); | 716 | xfree(lname); |
681 | } else { | 717 | } else { |
682 | /* XXX - multicolumn display would be nice here */ | 718 | printf("%-*s", colspace, fname); |
683 | printf("%s\n", fname); | 719 | if (c >= columns) { |
720 | printf("\n"); | ||
721 | c = 1; | ||
722 | } else | ||
723 | c++; | ||
684 | } | 724 | } |
685 | xfree(fname); | 725 | xfree(fname); |
686 | } | 726 | } |
687 | 727 | ||
728 | if (!(lflag & LONG_VIEW) && (c != 1)) | ||
729 | printf("\n"); | ||
730 | |||
688 | if (g.gl_pathc) | 731 | if (g.gl_pathc) |
689 | globfree(&g); | 732 | globfree(&g); |
690 | 733 | ||