LCOV - code coverage report
Current view: top level - openssh-6.6p1 - sftp.c (source / functions) Hit Total Coverage
Test: lcov_coverage_final.info Lines: 615 903 68.1 %
Date: 2014-08-01 Functions: 26 29 89.7 %
Branches: 386 678 56.9 %

           Branch data     Line data    Source code
       1                 :            : /* $OpenBSD: sftp.c,v 1.158 2013/11/20 20:54:10 deraadt Exp $ */
       2                 :            : /*
       3                 :            :  * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
       4                 :            :  *
       5                 :            :  * Permission to use, copy, modify, and distribute this software for any
       6                 :            :  * purpose with or without fee is hereby granted, provided that the above
       7                 :            :  * copyright notice and this permission notice appear in all copies.
       8                 :            :  *
       9                 :            :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      10                 :            :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      11                 :            :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      12                 :            :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      13                 :            :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      14                 :            :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      15                 :            :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      16                 :            :  */
      17                 :            : 
      18                 :            : #include "includes.h"
      19                 :            : 
      20                 :            : #include <sys/types.h>
      21                 :            : #include <sys/ioctl.h>
      22                 :            : #ifdef HAVE_SYS_STAT_H
      23                 :            : # include <sys/stat.h>
      24                 :            : #endif
      25                 :            : #include <sys/param.h>
      26                 :            : #include <sys/socket.h>
      27                 :            : #include <sys/wait.h>
      28                 :            : #ifdef HAVE_SYS_STATVFS_H
      29                 :            : #include <sys/statvfs.h>
      30                 :            : #endif
      31                 :            : 
      32                 :            : #include <ctype.h>
      33                 :            : #include <errno.h>
      34                 :            : 
      35                 :            : #ifdef HAVE_PATHS_H
      36                 :            : # include <paths.h>
      37                 :            : #endif
      38                 :            : #ifdef HAVE_LIBGEN_H
      39                 :            : #include <libgen.h>
      40                 :            : #endif
      41                 :            : #ifdef HAVE_LOCALE_H
      42                 :            : # include <locale.h>
      43                 :            : #endif
      44                 :            : #ifdef USE_LIBEDIT
      45                 :            : #include <histedit.h>
      46                 :            : #else
      47                 :            : typedef void EditLine;
      48                 :            : #endif
      49                 :            : #include <signal.h>
      50                 :            : #include <stdlib.h>
      51                 :            : #include <stdio.h>
      52                 :            : #include <string.h>
      53                 :            : #include <unistd.h>
      54                 :            : #include <stdarg.h>
      55                 :            : 
      56                 :            : #ifdef HAVE_UTIL_H
      57                 :            : # include <util.h>
      58                 :            : #endif
      59                 :            : 
      60                 :            : #include "xmalloc.h"
      61                 :            : #include "log.h"
      62                 :            : #include "pathnames.h"
      63                 :            : #include "misc.h"
      64                 :            : 
      65                 :            : #include "sftp.h"
      66                 :            : #include "buffer.h"
      67                 :            : #include "sftp-common.h"
      68                 :            : #include "sftp-client.h"
      69                 :            : 
      70                 :            : #define DEFAULT_COPY_BUFLEN     32768   /* Size of buffer for up/download */
      71                 :            : #define DEFAULT_NUM_REQUESTS    64      /* # concurrent outstanding requests */
      72                 :            : 
      73                 :            : /* File to read commands from */
      74                 :            : FILE* infile;
      75                 :            : 
      76                 :            : /* Are we in batchfile mode? */
      77                 :            : int batchmode = 0;
      78                 :            : 
      79                 :            : /* PID of ssh transport process */
      80                 :            : static pid_t sshpid = -1;
      81                 :            : 
      82                 :            : /* Suppress diagnositic messages */
      83                 :            : int quiet = 0;
      84                 :            : 
      85                 :            : /* This is set to 0 if the progressmeter is not desired. */
      86                 :            : int showprogress = 1;
      87                 :            : 
      88                 :            : /* When this option is set, we always recursively download/upload directories */
      89                 :            : int global_rflag = 0;
      90                 :            : 
      91                 :            : /* When this option is set, we resume download if possible */
      92                 :            : int global_aflag = 0;
      93                 :            : 
      94                 :            : /* When this option is set, the file transfers will always preserve times */
      95                 :            : int global_pflag = 0;
      96                 :            : 
      97                 :            : /* When this option is set, transfers will have fsync() called on each file */
      98                 :            : int global_fflag = 0;
      99                 :            : 
     100                 :            : /* SIGINT received during command processing */
     101                 :            : volatile sig_atomic_t interrupted = 0;
     102                 :            : 
     103                 :            : /* I wish qsort() took a separate ctx for the comparison function...*/
     104                 :            : int sort_flag;
     105                 :            : 
     106                 :            : /* Context used for commandline completion */
     107                 :            : struct complete_ctx {
     108                 :            :         struct sftp_conn *conn;
     109                 :            :         char **remote_pathp;
     110                 :            : };
     111                 :            : 
     112                 :            : int remote_glob(struct sftp_conn *, const char *, int,
     113                 :            :     int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
     114                 :            : 
     115                 :            : extern char *__progname;
     116                 :            : 
     117                 :            : /* Separators for interactive commands */
     118                 :            : #define WHITESPACE " \t\r\n"
     119                 :            : 
     120                 :            : /* ls flags */
     121                 :            : #define LS_LONG_VIEW    0x0001  /* Full view ala ls -l */
     122                 :            : #define LS_SHORT_VIEW   0x0002  /* Single row view ala ls -1 */
     123                 :            : #define LS_NUMERIC_VIEW 0x0004  /* Long view with numeric uid/gid */
     124                 :            : #define LS_NAME_SORT    0x0008  /* Sort by name (default) */
     125                 :            : #define LS_TIME_SORT    0x0010  /* Sort by mtime */
     126                 :            : #define LS_SIZE_SORT    0x0020  /* Sort by file size */
     127                 :            : #define LS_REVERSE_SORT 0x0040  /* Reverse sort order */
     128                 :            : #define LS_SHOW_ALL     0x0080  /* Don't skip filenames starting with '.' */
     129                 :            : #define LS_SI_UNITS     0x0100  /* Display sizes as K, M, G, etc. */
     130                 :            : 
     131                 :            : #define VIEW_FLAGS      (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
     132                 :            : #define SORT_FLAGS      (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
     133                 :            : 
     134                 :            : /* Commands for interactive mode */
     135                 :            : enum sftp_command {
     136                 :            :         I_CHDIR = 1,
     137                 :            :         I_CHGRP,
     138                 :            :         I_CHMOD,
     139                 :            :         I_CHOWN,
     140                 :            :         I_DF,
     141                 :            :         I_GET,
     142                 :            :         I_HELP,
     143                 :            :         I_LCHDIR,
     144                 :            :         I_LINK,
     145                 :            :         I_LLS,
     146                 :            :         I_LMKDIR,
     147                 :            :         I_LPWD,
     148                 :            :         I_LS,
     149                 :            :         I_LUMASK,
     150                 :            :         I_MKDIR,
     151                 :            :         I_PUT,
     152                 :            :         I_PWD,
     153                 :            :         I_QUIT,
     154                 :            :         I_RENAME,
     155                 :            :         I_RM,
     156                 :            :         I_RMDIR,
     157                 :            :         I_SHELL,
     158                 :            :         I_SYMLINK,
     159                 :            :         I_VERSION,
     160                 :            :         I_PROGRESS,
     161                 :            :         I_REGET,
     162                 :            : };
     163                 :            : 
     164                 :            : struct CMD {
     165                 :            :         const char *c;
     166                 :            :         const int n;
     167                 :            :         const int t;
     168                 :            : };
     169                 :            : 
     170                 :            : /* Type of completion */
     171                 :            : #define NOARGS  0
     172                 :            : #define REMOTE  1
     173                 :            : #define LOCAL   2
     174                 :            : 
     175                 :            : static const struct CMD cmds[] = {
     176                 :            :         { "bye",      I_QUIT,         NOARGS  },
     177                 :            :         { "cd",               I_CHDIR,        REMOTE  },
     178                 :            :         { "chdir",    I_CHDIR,        REMOTE  },
     179                 :            :         { "chgrp",    I_CHGRP,        REMOTE  },
     180                 :            :         { "chmod",    I_CHMOD,        REMOTE  },
     181                 :            :         { "chown",    I_CHOWN,        REMOTE  },
     182                 :            :         { "df",               I_DF,           REMOTE  },
     183                 :            :         { "dir",      I_LS,           REMOTE  },
     184                 :            :         { "exit",     I_QUIT,         NOARGS  },
     185                 :            :         { "get",      I_GET,          REMOTE  },
     186                 :            :         { "help",     I_HELP,         NOARGS  },
     187                 :            :         { "lcd",      I_LCHDIR,       LOCAL   },
     188                 :            :         { "lchdir",   I_LCHDIR,       LOCAL   },
     189                 :            :         { "lls",      I_LLS,          LOCAL   },
     190                 :            :         { "lmkdir",   I_LMKDIR,       LOCAL   },
     191                 :            :         { "ln",               I_LINK,         REMOTE  },
     192                 :            :         { "lpwd",     I_LPWD,         LOCAL   },
     193                 :            :         { "ls",               I_LS,           REMOTE  },
     194                 :            :         { "lumask",   I_LUMASK,       NOARGS  },
     195                 :            :         { "mkdir",    I_MKDIR,        REMOTE  },
     196                 :            :         { "mget",     I_GET,          REMOTE  },
     197                 :            :         { "mput",     I_PUT,          LOCAL   },
     198                 :            :         { "progress", I_PROGRESS,     NOARGS  },
     199                 :            :         { "put",      I_PUT,          LOCAL   },
     200                 :            :         { "pwd",      I_PWD,          REMOTE  },
     201                 :            :         { "quit",     I_QUIT,         NOARGS  },
     202                 :            :         { "reget",    I_REGET,        REMOTE  },
     203                 :            :         { "rename",   I_RENAME,       REMOTE  },
     204                 :            :         { "rm",               I_RM,           REMOTE  },
     205                 :            :         { "rmdir",    I_RMDIR,        REMOTE  },
     206                 :            :         { "symlink",  I_SYMLINK,      REMOTE  },
     207                 :            :         { "version",  I_VERSION,      NOARGS  },
     208                 :            :         { "!",                I_SHELL,        NOARGS  },
     209                 :            :         { "?",                I_HELP,         NOARGS  },
     210                 :            :         { NULL,         -1,             -1      }
     211                 :            : };
     212                 :            : 
     213                 :            : int interactive_loop(struct sftp_conn *, char *file1, char *file2);
     214                 :            : 
     215                 :            : /* ARGSUSED */
     216                 :            : static void
     217                 :          0 : killchild(int signo)
     218                 :            : {
     219         [ #  # ]:          0 :         if (sshpid > 1) {
     220                 :          0 :                 kill(sshpid, SIGTERM);
     221                 :          0 :                 waitpid(sshpid, NULL, 0);
     222                 :            :         }
     223                 :            : 
     224                 :          0 :         _exit(1);
     225                 :            : }
     226                 :            : 
     227                 :            : /* ARGSUSED */
     228                 :            : static void
     229                 :          0 : cmd_interrupt(int signo)
     230                 :            : {
     231                 :          0 :         const char msg[] = "\rInterrupt  \n";
     232                 :          0 :         int olderrno = errno;
     233                 :            : 
     234                 :          0 :         (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
     235                 :          0 :         interrupted = 1;
     236                 :          0 :         errno = olderrno;
     237                 :          0 : }
     238                 :            : 
     239                 :            : static void
     240                 :            : help(void)
     241                 :            : {
     242                 :            :         printf("Available commands:\n"
     243                 :            :             "bye                                Quit sftp\n"
     244                 :            :             "cd path                            Change remote directory to 'path'\n"
     245                 :            :             "chgrp grp path                     Change group of file 'path' to 'grp'\n"
     246                 :            :             "chmod mode path                    Change permissions of file 'path' to 'mode'\n"
     247                 :            :             "chown own path                     Change owner of file 'path' to 'own'\n"
     248                 :            :             "df [-hi] [path]                    Display statistics for current directory or\n"
     249                 :            :             "                                   filesystem containing 'path'\n"
     250                 :            :             "exit                               Quit sftp\n"
     251                 :            :             "get [-Ppr] remote [local]          Download file\n"
     252                 :            :             "reget remote [local]          Resume download file\n"
     253                 :            :             "help                               Display this help text\n"
     254                 :            :             "lcd path                           Change local directory to 'path'\n"
     255                 :            :             "lls [ls-options [path]]            Display local directory listing\n"
     256                 :            :             "lmkdir path                        Create local directory\n"
     257                 :            :             "ln [-s] oldpath newpath            Link remote file (-s for symlink)\n"
     258                 :            :             "lpwd                               Print local working directory\n"
     259                 :            :             "ls [-1afhlnrSt] [path]             Display remote directory listing\n"
     260                 :            :             "lumask umask                       Set local umask to 'umask'\n"
     261                 :            :             "mkdir path                         Create remote directory\n"
     262                 :            :             "progress                           Toggle display of progress meter\n"
     263                 :            :             "put [-Ppr] local [remote]          Upload file\n"
     264                 :            :             "pwd                                Display remote working directory\n"
     265                 :            :             "quit                               Quit sftp\n"
     266                 :            :             "rename oldpath newpath             Rename remote file\n"
     267                 :            :             "rm path                            Delete remote file\n"
     268                 :            :             "rmdir path                         Remove remote directory\n"
     269                 :            :             "symlink oldpath newpath            Symlink remote file\n"
     270                 :            :             "version                            Show SFTP version\n"
     271                 :            :             "!command                           Execute 'command' in local shell\n"
     272                 :            :             "!                                  Escape to local shell\n"
     273                 :            :             "?                                  Synonym for help\n");
     274                 :            : }
     275                 :            : 
     276                 :            : static void
     277                 :          3 : local_do_shell(const char *args)
     278                 :            : {
     279                 :            :         int status;
     280                 :            :         char *shell;
     281                 :            :         pid_t pid;
     282                 :            : 
     283         [ -  + ]:          3 :         if (!*args)
     284                 :          0 :                 args = NULL;
     285                 :            : 
     286 [ +  - ][ -  + ]:          3 :         if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
     287                 :          0 :                 shell = _PATH_BSHELL;
     288                 :            : 
     289         [ -  + ]:          3 :         if ((pid = fork()) == -1)
     290                 :          0 :                 fatal("Couldn't fork: %s", strerror(errno));
     291                 :            : 
     292         [ +  + ]:          6 :         if (pid == 0) {
     293                 :            :                 /* XXX: child has pipe fds to ssh subproc open - issue? */
     294         [ +  - ]:          3 :                 if (args) {
     295                 :          3 :                         debug3("Executing %s -c \"%s\"", shell, args);
     296                 :          0 :                         execl(shell, shell, "-c", args, (char *)NULL);
     297                 :            :                 } else {
     298                 :          0 :                         debug3("Executing %s", shell);
     299                 :          0 :                         execl(shell, shell, (char *)NULL);
     300                 :            :                 }
     301                 :          0 :                 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
     302                 :          0 :                     strerror(errno));
     303                 :          0 :                 _exit(1);
     304                 :            :         }
     305         [ -  + ]:          3 :         while (waitpid(pid, &status, 0) == -1)
     306         [ #  # ]:          0 :                 if (errno != EINTR)
     307                 :          3 :                         fatal("Couldn't wait for child: %s", strerror(errno));
     308         [ -  + ]:          3 :         if (!WIFEXITED(status))
     309                 :          0 :                 error("Shell exited abnormally");
     310         [ -  + ]:          3 :         else if (WEXITSTATUS(status))
     311                 :          0 :                 error("Shell exited with status %d", WEXITSTATUS(status));
     312                 :          3 : }
     313                 :            : 
     314                 :            : static void
     315                 :          2 : local_do_ls(const char *args)
     316                 :            : {
     317 [ +  - ][ +  + ]:          2 :         if (!args || !*args)
     318                 :          1 :                 local_do_shell(_PATH_LS);
     319                 :            :         else {
     320                 :          1 :                 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
     321                 :          1 :                 char *buf = xmalloc(len);
     322                 :            : 
     323                 :            :                 /* XXX: quoting - rip quoting code from ftp? */
     324                 :          1 :                 snprintf(buf, len, _PATH_LS " %s", args);
     325                 :          1 :                 local_do_shell(buf);
     326                 :          1 :                 free(buf);
     327                 :            :         }
     328                 :          2 : }
     329                 :            : 
     330                 :            : /* Strip one path (usually the pwd) from the start of another */
     331                 :            : static char *
     332                 :        755 : path_strip(char *path, char *strip)
     333                 :            : {
     334                 :            :         size_t len;
     335                 :            : 
     336         [ +  + ]:        755 :         if (strip == NULL)
     337                 :        631 :                 return (xstrdup(path));
     338                 :            : 
     339                 :        124 :         len = strlen(strip);
     340         [ +  - ]:        124 :         if (strncmp(path, strip, len) == 0) {
     341 [ +  - ][ +  + ]:        124 :                 if (strip[len - 1] != '/' && path[len] == '/')
     342                 :        123 :                         len++;
     343                 :        124 :                 return (xstrdup(path + len));
     344                 :            :         }
     345                 :            : 
     346                 :          0 :         return (xstrdup(path));
     347                 :            : }
     348                 :            : 
     349                 :            : static char *
     350                 :        183 : make_absolute(char *p, char *pwd)
     351                 :            : {
     352                 :            :         char *abs_str;
     353                 :            : 
     354                 :            :         /* Derelativise */
     355 [ +  - ][ +  + ]:        183 :         if (p && p[0] != '/') {
     356                 :          3 :                 abs_str = path_append(pwd, p);
     357                 :          3 :                 free(p);
     358                 :          3 :                 return(abs_str);
     359                 :            :         } else
     360                 :            :                 return(p);
     361                 :            : }
     362                 :            : 
     363                 :            : static int
     364                 :         70 : parse_getput_flags(const char *cmd, char **argv, int argc,
     365                 :            :     int *aflag, int *fflag, int *pflag, int *rflag)
     366                 :            : {
     367                 :            :         extern int opterr, optind, optopt, optreset;
     368                 :            :         int ch;
     369                 :            : 
     370                 :         70 :         optind = optreset = 1;
     371                 :         70 :         opterr = 0;
     372                 :            : 
     373                 :         70 :         *aflag = *fflag = *rflag = *pflag = 0;
     374         [ -  + ]:         70 :         while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
     375   [ #  #  #  #  :          0 :                 switch (ch) {
                      # ]
     376                 :            :                 case 'a':
     377                 :          0 :                         *aflag = 1;
     378                 :          0 :                         break;
     379                 :            :                 case 'f':
     380                 :          0 :                         *fflag = 1;
     381                 :          0 :                         break;
     382                 :            :                 case 'p':
     383                 :            :                 case 'P':
     384                 :          0 :                         *pflag = 1;
     385                 :          0 :                         break;
     386                 :            :                 case 'r':
     387                 :            :                 case 'R':
     388                 :          0 :                         *rflag = 1;
     389                 :          0 :                         break;
     390                 :            :                 default:
     391                 :          0 :                         error("%s: Invalid flag -%c", cmd, optopt);
     392                 :          0 :                         return -1;
     393                 :            :                 }
     394                 :            :         }
     395                 :            : 
     396                 :         70 :         return optind;
     397                 :            : }
     398                 :            : 
     399                 :            : static int
     400                 :         14 : parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
     401                 :            : {
     402                 :            :         extern int opterr, optind, optopt, optreset;
     403                 :            :         int ch;
     404                 :            : 
     405                 :         14 :         optind = optreset = 1;
     406                 :         14 :         opterr = 0;
     407                 :            : 
     408                 :         14 :         *sflag = 0;
     409         [ +  + ]:         21 :         while ((ch = getopt(argc, argv, "s")) != -1) {
     410         [ +  - ]:          7 :                 switch (ch) {
     411                 :            :                 case 's':
     412                 :          7 :                         *sflag = 1;
     413                 :          7 :                         break;
     414                 :            :                 default:
     415                 :          0 :                         error("%s: Invalid flag -%c", cmd, optopt);
     416                 :          0 :                         return -1;
     417                 :            :                 }
     418                 :            :         }
     419                 :            : 
     420                 :         14 :         return optind;
     421                 :            : }
     422                 :            : 
     423                 :            : static int
     424                 :         16 : parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
     425                 :            : {
     426                 :            :         extern int opterr, optind, optopt, optreset;
     427                 :            :         int ch;
     428                 :            : 
     429                 :         16 :         optind = optreset = 1;
     430                 :         16 :         opterr = 0;
     431                 :            : 
     432                 :         16 :         *lflag = 0;
     433         [ +  + ]:         22 :         while ((ch = getopt(argc, argv, "l")) != -1) {
     434         [ +  - ]:          6 :                 switch (ch) {
     435                 :            :                 case 'l':
     436                 :          6 :                         *lflag = 1;
     437                 :          6 :                         break;
     438                 :            :                 default:
     439                 :          0 :                         error("%s: Invalid flag -%c", cmd, optopt);
     440                 :          0 :                         return -1;
     441                 :            :                 }
     442                 :            :         }
     443                 :            : 
     444                 :         16 :         return optind;
     445                 :            : }
     446                 :            : 
     447                 :            : static int
     448                 :         25 : parse_ls_flags(char **argv, int argc, int *lflag)
     449                 :            : {
     450                 :            :         extern int opterr, optind, optopt, optreset;
     451                 :            :         int ch;
     452                 :            : 
     453                 :         25 :         optind = optreset = 1;
     454                 :         25 :         opterr = 0;
     455                 :            : 
     456                 :         25 :         *lflag = LS_NAME_SORT;
     457         [ +  + ]:         56 :         while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
     458   [ -  -  -  -  :         31 :                 switch (ch) {
          -  +  +  -  -  
                      - ]
     459                 :            :                 case '1':
     460                 :          0 :                         *lflag &= ~VIEW_FLAGS;
     461                 :          0 :                         *lflag |= LS_SHORT_VIEW;
     462                 :          0 :                         break;
     463                 :            :                 case 'S':
     464                 :          0 :                         *lflag &= ~SORT_FLAGS;
     465                 :          0 :                         *lflag |= LS_SIZE_SORT;
     466                 :          0 :                         break;
     467                 :            :                 case 'a':
     468                 :          0 :                         *lflag |= LS_SHOW_ALL;
     469                 :          0 :                         break;
     470                 :            :                 case 'f':
     471                 :          0 :                         *lflag &= ~SORT_FLAGS;
     472                 :          0 :                         break;
     473                 :            :                 case 'h':
     474                 :          0 :                         *lflag |= LS_SI_UNITS;
     475                 :          0 :                         break;
     476                 :            :                 case 'l':
     477                 :         23 :                         *lflag &= ~LS_SHORT_VIEW;
     478                 :         23 :                         *lflag |= LS_LONG_VIEW;
     479                 :         23 :                         break;
     480                 :            :                 case 'n':
     481                 :          8 :                         *lflag &= ~LS_SHORT_VIEW;
     482                 :          8 :                         *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
     483                 :          8 :                         break;
     484                 :            :                 case 'r':
     485                 :          0 :                         *lflag |= LS_REVERSE_SORT;
     486                 :          0 :                         break;
     487                 :            :                 case 't':
     488                 :          0 :                         *lflag &= ~SORT_FLAGS;
     489                 :          0 :                         *lflag |= LS_TIME_SORT;
     490                 :          0 :                         break;
     491                 :            :                 default:
     492                 :          0 :                         error("ls: Invalid flag -%c", optopt);
     493                 :         31 :                         return -1;
     494                 :            :                 }
     495                 :            :         }
     496                 :            : 
     497                 :         25 :         return optind;
     498                 :            : }
     499                 :            : 
     500                 :            : static int
     501                 :          4 : parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
     502                 :            : {
     503                 :            :         extern int opterr, optind, optopt, optreset;
     504                 :            :         int ch;
     505                 :            : 
     506                 :          4 :         optind = optreset = 1;
     507                 :          4 :         opterr = 0;
     508                 :            : 
     509                 :          4 :         *hflag = *iflag = 0;
     510         [ -  + ]:          4 :         while ((ch = getopt(argc, argv, "hi")) != -1) {
     511      [ #  #  # ]:          0 :                 switch (ch) {
     512                 :            :                 case 'h':
     513                 :          0 :                         *hflag = 1;
     514                 :          0 :                         break;
     515                 :            :                 case 'i':
     516                 :          0 :                         *iflag = 1;
     517                 :          0 :                         break;
     518                 :            :                 default:
     519                 :          0 :                         error("%s: Invalid flag -%c", cmd, optopt);
     520                 :          0 :                         return -1;
     521                 :            :                 }
     522                 :            :         }
     523                 :            : 
     524                 :          4 :         return optind;
     525                 :            : }
     526                 :            : 
     527                 :            : static int
     528                 :         52 : parse_no_flags(const char *cmd, char **argv, int argc)
     529                 :            : {
     530                 :            :         extern int opterr, optind, optopt, optreset;
     531                 :            :         int ch;
     532                 :            : 
     533                 :         52 :         optind = optreset = 1;
     534                 :         52 :         opterr = 0;
     535                 :            : 
     536         [ -  + ]:         52 :         while ((ch = getopt(argc, argv, "")) != -1) {
     537                 :            :                 switch (ch) {
     538                 :            :                 default:
     539                 :          0 :                         error("%s: Invalid flag -%c", cmd, optopt);
     540                 :          0 :                         return -1;
     541                 :            :                 }
     542                 :            :         }
     543                 :            : 
     544                 :         52 :         return optind;
     545                 :            : }
     546                 :            : 
     547                 :            : static int
     548                 :         33 : is_dir(char *path)
     549                 :            : {
     550                 :            :         struct stat sb;
     551                 :            : 
     552                 :            :         /* XXX: report errors? */
     553         [ +  + ]:         33 :         if (stat(path, &sb) == -1)
     554                 :            :                 return(0);
     555                 :            : 
     556                 :          3 :         return(S_ISDIR(sb.st_mode));
     557                 :            : }
     558                 :            : 
     559                 :            : static int
     560                 :         30 : remote_is_dir(struct sftp_conn *conn, char *path)
     561                 :            : {
     562                 :            :         Attrib *a;
     563                 :            : 
     564                 :            :         /* XXX: report errors? */
     565         [ +  + ]:         30 :         if ((a = do_stat(conn, path, 1)) == NULL)
     566                 :            :                 return(0);
     567         [ +  - ]:          3 :         if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
     568                 :            :                 return(0);
     569                 :          3 :         return(S_ISDIR(a->perm));
     570                 :            : }
     571                 :            : 
     572                 :            : /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
     573                 :            : static int
     574                 :         87 : pathname_is_dir(char *pathname)
     575                 :            : {
     576                 :         87 :         size_t l = strlen(pathname);
     577                 :            : 
     578 [ +  - ][ +  - ]:         87 :         return l > 0 && pathname[l - 1] == '/';
     579                 :            : }
     580                 :            : 
     581                 :            : static int
     582                 :         38 : process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
     583                 :            :     int pflag, int rflag, int resume, int fflag)
     584                 :            : {
     585                 :         38 :         char *abs_src = NULL;
     586                 :         38 :         char *abs_dst = NULL;
     587                 :            :         glob_t g;
     588                 :         38 :         char *filename, *tmp=NULL;
     589                 :         38 :         int i, err = 0;
     590                 :            : 
     591                 :         38 :         abs_src = xstrdup(src);
     592                 :         38 :         abs_src = make_absolute(abs_src, pwd);
     593                 :            :         memset(&g, 0, sizeof(g));
     594                 :            : 
     595                 :         38 :         debug3("Looking up %s", abs_src);
     596         [ +  + ]:         38 :         if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
     597                 :          3 :                 error("File \"%s\" not found.", abs_src);
     598                 :          3 :                 err = -1;
     599                 :          3 :                 goto out;
     600                 :            :         }
     601                 :            : 
     602                 :            :         /*
     603                 :            :          * If multiple matches then dst must be a directory or
     604                 :            :          * unspecified.
     605                 :            :          */
     606 [ +  + ][ +  + ]:         35 :         if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
                 [ +  + ]
     607                 :          1 :                 error("Multiple source paths, but destination "
     608                 :            :                     "\"%s\" is not a directory", dst);
     609                 :          1 :                 err = -1;
     610                 :          1 :                 goto out;
     611                 :            :         }
     612                 :            : 
     613 [ +  + ][ +  - ]:         92 :         for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
     614                 :         58 :                 tmp = xstrdup(g.gl_pathv[i]);
     615         [ -  + ]:         58 :                 if ((filename = basename(tmp)) == NULL) {
     616                 :          0 :                         error("basename %s: %s", tmp, strerror(errno));
     617                 :          0 :                         free(tmp);
     618                 :          0 :                         err = -1;
     619                 :          0 :                         goto out;
     620                 :            :                 }
     621                 :            : 
     622 [ +  + ][ +  + ]:         58 :                 if (g.gl_matchc == 1 && dst) {
     623         [ +  + ]:         31 :                         if (is_dir(dst)) {
     624                 :          1 :                                 abs_dst = path_append(dst, filename);
     625                 :            :                         } else {
     626                 :         30 :                                 abs_dst = xstrdup(dst);
     627                 :            :                         }
     628         [ +  + ]:         27 :                 } else if (dst) {
     629                 :         13 :                         abs_dst = path_append(dst, filename);
     630                 :            :                 } else {
     631                 :         14 :                         abs_dst = xstrdup(filename);
     632                 :            :                 }
     633                 :         58 :                 free(tmp);
     634                 :            : 
     635                 :         58 :                 resume |= global_aflag;
     636 [ +  + ][ -  + ]:         58 :                 if (!quiet && resume)
     637                 :          0 :                         printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
     638         [ +  + ]:         58 :                 else if (!quiet && !resume)
     639                 :         34 :                         printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
     640 [ -  + ][ #  # ]:         58 :                 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
                 [ #  # ]
     641 [ #  # ][ #  # ]:          0 :                         if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
         [ #  # ][ #  # ]
                 [ #  # ]
     642                 :          0 :                             pflag || global_pflag, 1, resume,
     643                 :          0 :                             fflag || global_fflag) == -1)
     644                 :          0 :                                 err = -1;
     645                 :            :                 } else {
     646 [ +  - ][ +  - ]:         58 :                         if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
         [ +  - ][ +  - ]
                 [ +  + ]
     647                 :         58 :                             pflag || global_pflag, resume,
     648                 :         58 :                             fflag || global_fflag) == -1)
     649                 :          4 :                                 err = -1;
     650                 :            :                 }
     651                 :         58 :                 free(abs_dst);
     652                 :         58 :                 abs_dst = NULL;
     653                 :            :         }
     654                 :            : 
     655                 :            : out:
     656                 :         38 :         free(abs_src);
     657                 :         38 :         globfree(&g);
     658                 :         38 :         return(err);
     659                 :            : }
     660                 :            : 
     661                 :            : static int
     662                 :         32 : process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
     663                 :            :     int pflag, int rflag, int fflag)
     664                 :            : {
     665                 :         32 :         char *tmp_dst = NULL;
     666                 :         32 :         char *abs_dst = NULL;
     667                 :         32 :         char *tmp = NULL, *filename = NULL;
     668                 :            :         glob_t g;
     669                 :         32 :         int err = 0;
     670                 :         32 :         int i, dst_is_dir = 1;
     671                 :            :         struct stat sb;
     672                 :            : 
     673         [ +  + ]:         32 :         if (dst) {
     674                 :         30 :                 tmp_dst = xstrdup(dst);
     675                 :         30 :                 tmp_dst = make_absolute(tmp_dst, pwd);
     676                 :            :         }
     677                 :            : 
     678                 :            :         memset(&g, 0, sizeof(g));
     679                 :         32 :         debug3("Looking up %s", src);
     680         [ -  + ]:         32 :         if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
     681                 :          0 :                 error("File \"%s\" not found.", src);
     682                 :          0 :                 err = -1;
     683                 :          0 :                 goto out;
     684                 :            :         }
     685                 :            : 
     686                 :            :         /* If we aren't fetching to pwd then stash this status for later */
     687         [ +  + ]:         32 :         if (tmp_dst != NULL)
     688                 :         30 :                 dst_is_dir = remote_is_dir(conn, tmp_dst);
     689                 :            : 
     690                 :            :         /* If multiple matches, dst may be directory or unspecified */
     691 [ +  + ][ +  + ]:         32 :         if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
     692                 :          2 :                 error("Multiple paths match, but destination "
     693                 :            :                     "\"%s\" is not a directory", tmp_dst);
     694                 :          2 :                 err = -1;
     695                 :          2 :                 goto out;
     696                 :            :         }
     697                 :            : 
     698 [ +  + ][ +  - ]:         62 :         for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
     699         [ +  + ]:         32 :                 if (stat(g.gl_pathv[i], &sb) == -1) {
     700                 :          3 :                         err = -1;
     701                 :          3 :                         error("stat %s: %s", g.gl_pathv[i], strerror(errno));
     702                 :          3 :                         continue;
     703                 :            :                 }
     704                 :            : 
     705                 :         29 :                 tmp = xstrdup(g.gl_pathv[i]);
     706         [ -  + ]:         29 :                 if ((filename = basename(tmp)) == NULL) {
     707                 :          0 :                         error("basename %s: %s", tmp, strerror(errno));
     708                 :          0 :                         free(tmp);
     709                 :          0 :                         err = -1;
     710                 :          0 :                         goto out;
     711                 :            :                 }
     712                 :            : 
     713 [ +  + ][ +  + ]:         29 :                 if (g.gl_matchc == 1 && tmp_dst) {
     714                 :            :                         /* If directory specified, append filename */
     715         [ +  + ]:         24 :                         if (dst_is_dir)
     716                 :          1 :                                 abs_dst = path_append(tmp_dst, filename);
     717                 :            :                         else
     718                 :         23 :                                 abs_dst = xstrdup(tmp_dst);
     719         [ +  + ]:          5 :                 } else if (tmp_dst) {
     720                 :          2 :                         abs_dst = path_append(tmp_dst, filename);
     721                 :            :                 } else {
     722                 :          3 :                         abs_dst = make_absolute(xstrdup(filename), pwd);
     723                 :            :                 }
     724                 :         29 :                 free(tmp);
     725                 :            : 
     726         [ +  + ]:         29 :                 if (!quiet)
     727                 :          9 :                         printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
     728 [ -  + ][ #  # ]:         29 :                 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
                 [ #  # ]
     729 [ #  # ][ #  # ]:          0 :                         if (upload_dir(conn, g.gl_pathv[i], abs_dst,
         [ #  # ][ #  # ]
                 [ #  # ]
     730                 :          0 :                             pflag || global_pflag, 1,
     731                 :          0 :                             fflag || global_fflag) == -1)
     732                 :          0 :                                 err = -1;
     733                 :            :                 } else {
     734 [ +  - ][ +  - ]:         29 :                         if (do_upload(conn, g.gl_pathv[i], abs_dst,
         [ +  - ][ +  - ]
                 [ +  + ]
     735                 :         29 :                             pflag || global_pflag,
     736                 :         29 :                             fflag || global_fflag) == -1)
     737                 :          3 :                                 err = -1;
     738                 :            :                 }
     739                 :            :         }
     740                 :            : 
     741                 :            : out:
     742                 :         32 :         free(abs_dst);
     743                 :         32 :         free(tmp_dst);
     744                 :         32 :         globfree(&g);
     745                 :         32 :         return(err);
     746                 :            : }
     747                 :            : 
     748                 :            : static int
     749                 :       4192 : sdirent_comp(const void *aa, const void *bb)
     750                 :            : {
     751                 :       4192 :         SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
     752                 :       4192 :         SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
     753         [ +  - ]:       4192 :         int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
     754                 :            : 
     755                 :            : #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
     756         [ +  - ]:       4192 :         if (sort_flag & LS_NAME_SORT)
     757                 :       4192 :                 return (rmul * strcmp(a->filename, b->filename));
     758         [ #  # ]:          0 :         else if (sort_flag & LS_TIME_SORT)
     759 [ #  # ][ #  # ]:          0 :                 return (rmul * NCMP(a->a.mtime, b->a.mtime));
     760         [ #  # ]:          0 :         else if (sort_flag & LS_SIZE_SORT)
     761 [ #  # ][ #  # ]:          0 :                 return (rmul * NCMP(a->a.size, b->a.size));
     762                 :            : 
     763                 :          0 :         fatal("Unknown ls sort type");
     764                 :            : }
     765                 :            : 
     766                 :            : /* sftp ls.1 replacement for directories */
     767                 :            : static int
     768                 :         11 : do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
     769                 :            : {
     770                 :            :         int n;
     771                 :         11 :         u_int c = 1, colspace = 0, columns = 1;
     772                 :            :         SFTP_DIRENT **d;
     773                 :            : 
     774         [ +  + ]:         11 :         if ((n = do_readdir(conn, path, &d)) != 0)
     775                 :            :                 return (n);
     776                 :            : 
     777         [ +  - ]:          7 :         if (!(lflag & LS_SHORT_VIEW)) {
     778                 :            :                 u_int m = 0, width = 80;
     779                 :            :                 struct winsize ws;
     780                 :            :                 char *tmp;
     781                 :            : 
     782                 :            :                 /* Count entries for sort and find longest filename */
     783         [ +  + ]:        755 :                 for (n = 0; d[n] != NULL; n++) {
     784 [ +  + ][ -  + ]:        748 :                         if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
     785         [ +  + ]:        734 :                                 m = MAX(m, strlen(d[n]->filename));
     786                 :            :                 }
     787                 :            : 
     788                 :            :                 /* Add any subpath that also needs to be counted */
     789                 :          7 :                 tmp = path_strip(path, strip_path);
     790                 :          7 :                 m += strlen(tmp);
     791                 :          7 :                 free(tmp);
     792                 :            : 
     793         [ +  + ]:          7 :                 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
     794                 :          1 :                         width = ws.ws_col;
     795                 :            : 
     796                 :          7 :                 columns = width / (m + 2);
     797                 :          7 :                 columns = MAX(columns, 1);
     798                 :          7 :                 colspace = width / columns;
     799                 :          7 :                 colspace = MIN(colspace, width);
     800                 :            :         }
     801                 :            : 
     802         [ +  - ]:          7 :         if (lflag & SORT_FLAGS) {
     803         [ +  + ]:        755 :                 for (n = 0; d[n] != NULL; n++)
     804                 :            :                         ;       /* count entries */
     805                 :          7 :                 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
     806                 :          7 :                 qsort(d, n, sizeof(*d), sdirent_comp);
     807                 :            :         }
     808                 :            : 
     809 [ +  + ][ +  - ]:        755 :         for (n = 0; d[n] != NULL && !interrupted; n++) {
     810                 :            :                 char *tmp, *fname;
     811                 :            : 
     812 [ +  + ][ +  - ]:        748 :                 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
     813                 :         14 :                         continue;
     814                 :            : 
     815                 :        734 :                 tmp = path_append(path, d[n]->filename);
     816                 :        734 :                 fname = path_strip(tmp, strip_path);
     817                 :        734 :                 free(tmp);
     818                 :            : 
     819         [ +  + ]:        734 :                 if (lflag & LS_LONG_VIEW) {
     820         [ +  + ]:        492 :                         if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
     821                 :            :                                 char *lname;
     822                 :            :                                 struct stat sb;
     823                 :            : 
     824                 :            :                                 memset(&sb, 0, sizeof(sb));
     825                 :        484 :                                 attrib_to_stat(&d[n]->a, &sb);
     826                 :        484 :                                 lname = ls_file(fname, &sb, 1,
     827                 :            :                                     (lflag & LS_SI_UNITS));
     828                 :            :                                 printf("%s\n", lname);
     829                 :        484 :                                 free(lname);
     830                 :            :                         } else
     831                 :          8 :                                 printf("%s\n", d[n]->longname);
     832                 :            :                 } else {
     833                 :            :                         printf("%-*s", colspace, fname);
     834         [ +  + ]:        242 :                         if (c >= columns) {
     835                 :            :                                 printf("\n");
     836                 :        136 :                                 c = 1;
     837                 :            :                         } else
     838                 :        106 :                                 c++;
     839                 :            :                 }
     840                 :            : 
     841                 :        734 :                 free(fname);
     842                 :            :         }
     843                 :            : 
     844 [ +  + ][ +  + ]:          7 :         if (!(lflag & LS_LONG_VIEW) && (c != 1))
     845                 :            :                 printf("\n");
     846                 :            : 
     847                 :          7 :         free_sftp_dirents(d);
     848                 :          7 :         return (0);
     849                 :            : }
     850                 :            : 
     851                 :            : /* sftp ls.1 replacement which handles path globs */
     852                 :            : static int
     853                 :         24 : do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
     854                 :            :     int lflag)
     855                 :            : {
     856                 :            :         char *fname, *lname;
     857                 :            :         glob_t g;
     858                 :            :         int err;
     859                 :            :         struct winsize ws;
     860                 :         24 :         u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
     861                 :            : 
     862                 :            :         memset(&g, 0, sizeof(g));
     863                 :            : 
     864         [ +  - ]:         24 :         if (remote_glob(conn, path,
     865                 :            :             GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
     866         [ +  - ]:         24 :             NULL, &g) ||
     867         [ -  + ]:         24 :             (g.gl_pathc && !g.gl_matchc)) {
     868         [ #  # ]:          0 :                 if (g.gl_pathc)
     869                 :          0 :                         globfree(&g);
     870                 :          0 :                 error("Can't ls: \"%s\" not found", path);
     871                 :          0 :                 return -1;
     872                 :            :         }
     873                 :            : 
     874         [ +  - ]:         24 :         if (interrupted)
     875                 :            :                 goto out;
     876                 :            : 
     877                 :            :         /*
     878                 :            :          * If the glob returns a single match and it is a directory,
     879                 :            :          * then just list its contents.
     880                 :            :          */
     881 [ +  - ][ +  - ]:         24 :         if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
                 [ +  + ]
     882                 :         24 :             S_ISDIR(g.gl_statv[0]->st_mode)) {
     883                 :         10 :                 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
     884                 :         10 :                 globfree(&g);
     885                 :         10 :                 return err;
     886                 :            :         }
     887                 :            : 
     888         [ -  + ]:         14 :         if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
     889                 :          0 :                 width = ws.ws_col;
     890                 :            : 
     891         [ +  - ]:         14 :         if (!(lflag & LS_SHORT_VIEW)) {
     892                 :            :                 /* Count entries for sort and find longest filename */
     893         [ +  + ]:         28 :                 for (i = 0; g.gl_pathv[i]; i++)
     894         [ +  - ]:         14 :                         m = MAX(m, strlen(g.gl_pathv[i]));
     895                 :            : 
     896                 :         14 :                 columns = width / (m + 2);
     897                 :         14 :                 columns = MAX(columns, 1);
     898                 :         14 :                 colspace = width / columns;
     899                 :            :         }
     900                 :            : 
     901 [ +  + ][ +  - ]:         28 :         for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
     902                 :         14 :                 fname = path_strip(g.gl_pathv[i], strip_path);
     903         [ +  - ]:         14 :                 if (lflag & LS_LONG_VIEW) {
     904         [ -  + ]:         14 :                         if (g.gl_statv[i] == NULL) {
     905                 :          0 :                                 error("no stat information for %s", fname);
     906                 :          0 :                                 continue;
     907                 :            :                         }
     908                 :         14 :                         lname = ls_file(fname, g.gl_statv[i], 1,
     909                 :            :                             (lflag & LS_SI_UNITS));
     910                 :            :                         printf("%s\n", lname);
     911                 :         14 :                         free(lname);
     912                 :            :                 } else {
     913                 :            :                         printf("%-*s", colspace, fname);
     914         [ #  # ]:          0 :                         if (c >= columns) {
     915                 :            :                                 printf("\n");
     916                 :          0 :                                 c = 1;
     917                 :            :                         } else
     918                 :          0 :                                 c++;
     919                 :            :                 }
     920                 :         14 :                 free(fname);
     921                 :            :         }
     922                 :            : 
     923 [ -  + ][ #  # ]:         14 :         if (!(lflag & LS_LONG_VIEW) && (c != 1))
     924                 :            :                 printf("\n");
     925                 :            : 
     926                 :            :  out:
     927         [ +  - ]:         14 :         if (g.gl_pathc)
     928                 :         14 :                 globfree(&g);
     929                 :            : 
     930                 :            :         return 0;
     931                 :            : }
     932                 :            : 
     933                 :            : static int
     934                 :          4 : do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
     935                 :            : {
     936                 :            :         struct sftp_statvfs st;
     937                 :            :         char s_used[FMT_SCALED_STRSIZE];
     938                 :            :         char s_avail[FMT_SCALED_STRSIZE];
     939                 :            :         char s_root[FMT_SCALED_STRSIZE];
     940                 :            :         char s_total[FMT_SCALED_STRSIZE];
     941                 :            :         unsigned long long ffree;
     942                 :            : 
     943         [ +  + ]:          4 :         if (do_statvfs(conn, path, &st, 1) == -1)
     944                 :            :                 return -1;
     945         [ -  + ]:          2 :         if (iflag) {
     946         [ #  # ]:          0 :                 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
     947                 :            :                 printf("     Inodes        Used       Avail      "
     948                 :            :                     "(root)    %%Capacity\n");
     949                 :            :                 printf("%11llu %11llu %11llu %11llu         %3llu%%\n",
     950                 :          0 :                     (unsigned long long)st.f_files,
     951                 :          0 :                     (unsigned long long)(st.f_files - st.f_ffree),
     952                 :          0 :                     (unsigned long long)st.f_favail,
     953                 :            :                     (unsigned long long)st.f_ffree, ffree);
     954         [ -  + ]:          2 :         } else if (hflag) {
     955                 :          0 :                 strlcpy(s_used, "error", sizeof(s_used));
     956                 :          0 :                 strlcpy(s_avail, "error", sizeof(s_avail));
     957                 :          0 :                 strlcpy(s_root, "error", sizeof(s_root));
     958                 :          0 :                 strlcpy(s_total, "error", sizeof(s_total));
     959                 :          0 :                 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
     960                 :          0 :                 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
     961                 :          0 :                 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
     962                 :          0 :                 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
     963                 :            :                 printf("    Size     Used    Avail   (root)    %%Capacity\n");
     964                 :            :                 printf("%7sB %7sB %7sB %7sB         %3llu%%\n",
     965                 :            :                     s_total, s_used, s_avail, s_root,
     966                 :          0 :                     (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
     967                 :            :                     st.f_blocks));
     968                 :            :         } else {
     969                 :            :                 printf("        Size         Used        Avail       "
     970                 :            :                     "(root)    %%Capacity\n");
     971                 :            :                 printf("%12llu %12llu %12llu %12llu         %3llu%%\n",
     972                 :          2 :                     (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
     973                 :          2 :                     (unsigned long long)(st.f_frsize *
     974                 :          2 :                     (st.f_blocks - st.f_bfree) / 1024),
     975                 :          2 :                     (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
     976                 :          2 :                     (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
     977                 :          2 :                     (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
     978                 :            :                     st.f_blocks));
     979                 :            :         }
     980                 :            :         return 0;
     981                 :            : }
     982                 :            : 
     983                 :            : /*
     984                 :            :  * Undo escaping of glob sequences in place. Used to undo extra escaping
     985                 :            :  * applied in makeargv() when the string is destined for a function that
     986                 :            :  * does not glob it.
     987                 :            :  */
     988                 :            : static void
     989                 :        152 : undo_glob_escape(char *s)
     990                 :            : {
     991                 :            :         size_t i, j;
     992                 :            : 
     993                 :       6252 :         for (i = j = 0;;) {
     994         [ +  + ]:       6252 :                 if (s[i] == '\0') {
     995                 :        152 :                         s[j] = '\0';
     996                 :        152 :                         return;
     997                 :            :                 }
     998         [ +  - ]:       6100 :                 if (s[i] != '\\') {
     999                 :       6100 :                         s[j++] = s[i++];
    1000                 :       6100 :                         continue;
    1001                 :            :                 }
    1002                 :            :                 /* s[i] == '\\' */
    1003                 :          0 :                 ++i;
    1004      [ #  #  # ]:          0 :                 switch (s[i]) {
    1005                 :            :                 case '?':
    1006                 :            :                 case '[':
    1007                 :            :                 case '*':
    1008                 :            :                 case '\\':
    1009                 :          0 :                         s[j++] = s[i++];
    1010                 :          0 :                         break;
    1011                 :            :                 case '\0':
    1012                 :          0 :                         s[j++] = '\\';
    1013                 :          0 :                         s[j] = '\0';
    1014                 :          0 :                         return;
    1015                 :            :                 default:
    1016                 :          0 :                         s[j++] = '\\';
    1017                 :          0 :                         s[j++] = s[i++];
    1018                 :          0 :                         break;
    1019                 :            :                 }
    1020                 :            :         }
    1021                 :            : }
    1022                 :            : 
    1023                 :            : /*
    1024                 :            :  * Split a string into an argument vector using sh(1)-style quoting,
    1025                 :            :  * comment and escaping rules, but with some tweaks to handle glob(3)
    1026                 :            :  * wildcards.
    1027                 :            :  * The "sloppy" flag allows for recovery from missing terminating quote, for
    1028                 :            :  * use in parsing incomplete commandlines during tab autocompletion.
    1029                 :            :  *
    1030                 :            :  * Returns NULL on error or a NULL-terminated array of arguments.
    1031                 :            :  *
    1032                 :            :  * If "lastquote" is not NULL, the quoting character used for the last
    1033                 :            :  * argument is placed in *lastquote ("\0", "'" or "\"").
    1034                 :            :  *
    1035                 :            :  * If "terminated" is not NULL, *terminated will be set to 1 when the
    1036                 :            :  * last argument's quote has been properly terminated or 0 otherwise.
    1037                 :            :  * This parameter is only of use if "sloppy" is set.
    1038                 :            :  */
    1039                 :            : #define MAXARGS         128
    1040                 :            : #define MAXARGLEN       8192
    1041                 :            : static char **
    1042                 :        185 : makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
    1043                 :            :     u_int *terminated)
    1044                 :            : {
    1045                 :            :         int argc, quot;
    1046                 :            :         size_t i, j;
    1047                 :            :         static char argvs[MAXARGLEN];
    1048                 :            :         static char *argv[MAXARGS + 1];
    1049                 :            :         enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
    1050                 :            : 
    1051                 :        185 :         *argcp = argc = 0;
    1052         [ -  + ]:        185 :         if (strlen(arg) > sizeof(argvs) - 1) {
    1053                 :            :  args_too_longs:
    1054                 :          0 :                 error("string too long");
    1055                 :          0 :                 return NULL;
    1056                 :            :         }
    1057         [ -  + ]:        185 :         if (terminated != NULL)
    1058                 :          0 :                 *terminated = 1;
    1059         [ -  + ]:        185 :         if (lastquote != NULL)
    1060                 :        185 :                 *lastquote = '\0';
    1061                 :            :         state = MA_START;
    1062                 :            :         i = j = 0;
    1063                 :            :         for (;;) {
    1064         [ -  + ]:      11633 :                 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
    1065                 :          0 :                         error("Too many arguments.");
    1066                 :          0 :                         return NULL;
    1067                 :            :                 }
    1068         [ +  + ]:      11633 :                 if (isspace((unsigned char)arg[i])) {
    1069         [ +  + ]:        307 :                         if (state == MA_UNQUOTED) {
    1070                 :            :                                 /* Terminate current argument */
    1071                 :        305 :                                 argvs[j++] = '\0';
    1072                 :        305 :                                 argc++;
    1073                 :        305 :                                 state = MA_START;
    1074         [ +  - ]:          2 :                         } else if (state != MA_START)
    1075                 :          2 :                                 argvs[j++] = arg[i];
    1076         [ +  + ]:      11326 :                 } else if (arg[i] == '"' || arg[i] == '\'') {
    1077         [ +  + ]:         24 :                         q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
    1078         [ +  + ]:         24 :                         if (state == MA_START) {
    1079                 :         10 :                                 argv[argc] = argvs + j;
    1080                 :         10 :                                 state = q;
    1081         [ -  + ]:         10 :                                 if (lastquote != NULL)
    1082                 :          0 :                                         *lastquote = arg[i];
    1083         [ +  + ]:         14 :                         } else if (state == MA_UNQUOTED)
    1084                 :            :                                 state = q;
    1085         [ +  + ]:         13 :                         else if (state == q)
    1086                 :            :                                 state = MA_UNQUOTED;
    1087                 :            :                         else
    1088                 :          2 :                                 argvs[j++] = arg[i];
    1089         [ +  + ]:      11302 :                 } else if (arg[i] == '\\') {
    1090         [ +  + ]:         21 :                         if (state == MA_SQUOTE || state == MA_DQUOTE) {
    1091         [ +  + ]:          8 :                                 quot = state == MA_SQUOTE ? '\'' : '"';
    1092                 :            :                                 /* Unescape quote we are in */
    1093                 :            :                                 /* XXX support \n and friends? */
    1094         [ +  + ]:          8 :                                 if (arg[i + 1] == quot) {
    1095                 :          5 :                                         i++;
    1096                 :          5 :                                         argvs[j++] = arg[i];
    1097         [ +  - ]:          3 :                                 } else if (arg[i + 1] == '?' ||
    1098         [ -  + ]:          3 :                                     arg[i + 1] == '[' || arg[i + 1] == '*') {
    1099                 :            :                                         /*
    1100                 :            :                                          * Special case for sftp: append
    1101                 :            :                                          * double-escaped glob sequence -
    1102                 :            :                                          * glob will undo one level of
    1103                 :            :                                          * escaping. NB. string can grow here.
    1104                 :            :                                          */
    1105         [ #  # ]:          0 :                                         if (j >= sizeof(argvs) - 5)
    1106                 :            :                                                 goto args_too_longs;
    1107                 :          0 :                                         argvs[j++] = '\\';
    1108                 :          0 :                                         argvs[j++] = arg[i++];
    1109                 :          0 :                                         argvs[j++] = '\\';
    1110                 :          0 :                                         argvs[j++] = arg[i];
    1111                 :            :                                 } else {
    1112                 :          3 :                                         argvs[j++] = arg[i++];
    1113                 :          3 :                                         argvs[j++] = arg[i];
    1114                 :            :                                 }
    1115                 :            :                         } else {
    1116         [ -  + ]:         13 :                                 if (state == MA_START) {
    1117                 :          0 :                                         argv[argc] = argvs + j;
    1118                 :          0 :                                         state = MA_UNQUOTED;
    1119         [ #  # ]:          0 :                                         if (lastquote != NULL)
    1120                 :          0 :                                                 *lastquote = '\0';
    1121                 :            :                                 }
    1122 [ +  - ][ +  + ]:         13 :                                 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
    1123         [ +  + ]:         12 :                                     arg[i + 1] == '*' || arg[i + 1] == '\\') {
    1124                 :            :                                         /*
    1125                 :            :                                          * Special case for sftp: append
    1126                 :            :                                          * escaped glob sequence -
    1127                 :            :                                          * glob will undo one level of
    1128                 :            :                                          * escaping.
    1129                 :            :                                          */
    1130                 :          4 :                                         argvs[j++] = arg[i++];
    1131                 :          4 :                                         argvs[j++] = arg[i];
    1132                 :            :                                 } else {
    1133                 :            :                                         /* Unescape everything */
    1134                 :            :                                         /* XXX support \n and friends? */
    1135                 :          9 :                                         i++;
    1136                 :          9 :                                         argvs[j++] = arg[i];
    1137                 :            :                                 }
    1138                 :            :                         }
    1139         [ -  + ]:      11281 :                 } else if (arg[i] == '#') {
    1140         [ #  # ]:          0 :                         if (state == MA_SQUOTE || state == MA_DQUOTE)
    1141                 :          0 :                                 argvs[j++] = arg[i];
    1142                 :            :                         else
    1143                 :            :                                 goto string_done;
    1144         [ +  + ]:      11281 :                 } else if (arg[i] == '\0') {
    1145         [ -  + ]:        185 :                         if (state == MA_SQUOTE || state == MA_DQUOTE) {
    1146         [ #  # ]:          0 :                                 if (sloppy) {
    1147                 :          0 :                                         state = MA_UNQUOTED;
    1148         [ #  # ]:          0 :                                         if (terminated != NULL)
    1149                 :          0 :                                                 *terminated = 0;
    1150                 :            :                                         goto string_done;
    1151                 :            :                                 }
    1152                 :          0 :                                 error("Unterminated quoted argument");
    1153                 :          0 :                                 return NULL;
    1154                 :            :                         }
    1155                 :            :  string_done:
    1156         [ +  - ]:        185 :                         if (state == MA_UNQUOTED) {
    1157                 :        185 :                                 argvs[j++] = '\0';
    1158                 :        185 :                                 argc++;
    1159                 :            :                         }
    1160                 :            :                         break;
    1161                 :            :                 } else {
    1162         [ +  + ]:      11096 :                         if (state == MA_START) {
    1163                 :        480 :                                 argv[argc] = argvs + j;
    1164                 :        480 :                                 state = MA_UNQUOTED;
    1165         [ -  + ]:        480 :                                 if (lastquote != NULL)
    1166                 :          0 :                                         *lastquote = '\0';
    1167                 :            :                         }
    1168 [ +  + ][ +  + ]:      11096 :                         if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
    1169         [ +  + ]:        494 :                             (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
    1170                 :            :                                 /*
    1171                 :            :                                  * Special case for sftp: escape quoted
    1172                 :            :                                  * glob(3) wildcards. NB. string can grow
    1173                 :            :                                  * here.
    1174                 :            :                                  */
    1175         [ -  + ]:          2 :                                 if (j >= sizeof(argvs) - 3)
    1176                 :            :                                         goto args_too_longs;
    1177                 :          2 :                                 argvs[j++] = '\\';
    1178                 :          2 :                                 argvs[j++] = arg[i];
    1179                 :            :                         } else
    1180                 :      11094 :                                 argvs[j++] = arg[i];
    1181                 :            :                 }
    1182                 :      11448 :                 i++;
    1183                 :      11448 :         }
    1184                 :        185 :         *argcp = argc;
    1185                 :        185 :         return argv;
    1186                 :            : }
    1187                 :            : 
    1188                 :            : static int
    1189                 :        190 : parse_args(const char **cpp, int *ignore_errors, int *aflag, int *fflag,
    1190                 :            :     int *hflag, int *iflag, int *lflag, int *pflag, int *rflag, int *sflag,
    1191                 :            :     unsigned long *n_arg, char **path1, char **path2)
    1192                 :            : {
    1193                 :        190 :         const char *cmd, *cp = *cpp;
    1194                 :            :         char *cp2, **argv;
    1195                 :        190 :         int base = 0;
    1196                 :            :         long l;
    1197                 :            :         int i, cmdnum, optidx, argc;
    1198                 :            : 
    1199                 :            :         /* Skip leading whitespace */
    1200                 :        190 :         cp = cp + strspn(cp, WHITESPACE);
    1201                 :            : 
    1202                 :            :         /* Check for leading '-' (disable error processing) */
    1203                 :        190 :         *ignore_errors = 0;
    1204         [ +  + ]:        190 :         if (*cp == '-') {
    1205                 :          1 :                 *ignore_errors = 1;
    1206                 :          1 :                 cp++;
    1207                 :          1 :                 cp = cp + strspn(cp, WHITESPACE);
    1208                 :            :         }
    1209                 :            : 
    1210                 :            :         /* Ignore blank lines and lines which begin with comment '#' char */
    1211         [ +  + ]:        190 :         if (*cp == '\0' || *cp == '#')
    1212                 :            :                 return (0);
    1213                 :            : 
    1214         [ +  - ]:        185 :         if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
    1215                 :            :                 return -1;
    1216                 :            : 
    1217                 :            :         /* Figure out which command we have */
    1218         [ +  + ]:       3542 :         for (i = 0; cmds[i].c != NULL; i++) {
    1219 [ +  - ][ +  + ]:       3540 :                 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
    1220                 :            :                         break;
    1221                 :            :         }
    1222                 :        185 :         cmdnum = cmds[i].n;
    1223                 :        185 :         cmd = cmds[i].c;
    1224                 :            : 
    1225                 :            :         /* Special case */
    1226         [ +  + ]:        185 :         if (*cp == '!') {
    1227                 :          1 :                 cp++;
    1228                 :          1 :                 cmdnum = I_SHELL;
    1229         [ +  + ]:        184 :         } else if (cmdnum == -1) {
    1230                 :          1 :                 error("Invalid command.");
    1231                 :          1 :                 return -1;
    1232                 :            :         }
    1233                 :            : 
    1234                 :            :         /* Get arguments and parse flags */
    1235                 :        184 :         *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
    1236                 :        184 :         *rflag = *sflag = 0;
    1237                 :        184 :         *path1 = *path2 = NULL;
    1238                 :        184 :         optidx = 1;
    1239   [ +  +  +  -  :        184 :         switch (cmdnum) {
          +  +  +  +  +  
             -  +  -  + ]
    1240                 :            :         case I_GET:
    1241                 :            :         case I_REGET:
    1242                 :            :         case I_PUT:
    1243         [ +  - ]:         70 :                 if ((optidx = parse_getput_flags(cmd, argv, argc,
    1244                 :            :                     aflag, fflag, pflag, rflag)) == -1)
    1245                 :            :                         return -1;
    1246                 :            :                 /* Get first pathname (mandatory) */
    1247         [ -  + ]:         70 :                 if (argc - optidx < 1) {
    1248                 :          0 :                         error("You must specify at least one path after a "
    1249                 :            :                             "%s command.", cmd);
    1250                 :          0 :                         return -1;
    1251                 :            :                 }
    1252                 :         70 :                 *path1 = xstrdup(argv[optidx]);
    1253                 :            :                 /* Get second pathname (optional) */
    1254         [ +  + ]:         70 :                 if (argc - optidx > 1) {
    1255                 :         66 :                         *path2 = xstrdup(argv[optidx + 1]);
    1256                 :            :                         /* Destination is not globbed */
    1257                 :         66 :                         undo_glob_escape(*path2);
    1258                 :            :                 }
    1259 [ -  + ][ #  # ]:         70 :                 if (*aflag && cmdnum == I_PUT) {
    1260                 :            :                         /* XXX implement resume for uploads */
    1261                 :          0 :                         error("Resume is not supported for uploads");
    1262                 :          0 :                         return -1;
    1263                 :            :                 }
    1264                 :            :                 break;
    1265                 :            :         case I_LINK:
    1266         [ +  - ]:         14 :                 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
    1267                 :            :                         return -1;
    1268                 :            :                 goto parse_two_paths;
    1269                 :            :         case I_RENAME:
    1270         [ +  - ]:         16 :                 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
    1271                 :            :                         return -1;
    1272                 :            :                 goto parse_two_paths;
    1273                 :            :         case I_SYMLINK:
    1274         [ #  # ]:          0 :                 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
    1275                 :            :                         return -1;
    1276                 :            :  parse_two_paths:
    1277         [ -  + ]:         30 :                 if (argc - optidx < 2) {
    1278                 :          0 :                         error("You must specify two paths after a %s "
    1279                 :            :                             "command.", cmd);
    1280                 :          0 :                         return -1;
    1281                 :            :                 }
    1282                 :         30 :                 *path1 = xstrdup(argv[optidx]);
    1283                 :         30 :                 *path2 = xstrdup(argv[optidx + 1]);
    1284                 :            :                 /* Paths are not globbed */
    1285                 :         30 :                 undo_glob_escape(*path1);
    1286                 :         30 :                 undo_glob_escape(*path2);
    1287                 :         30 :                 break;
    1288                 :            :         case I_RM:
    1289                 :            :         case I_MKDIR:
    1290                 :            :         case I_RMDIR:
    1291                 :            :         case I_CHDIR:
    1292                 :            :         case I_LCHDIR:
    1293                 :            :         case I_LMKDIR:
    1294         [ +  - ]:         30 :                 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
    1295                 :            :                         return -1;
    1296                 :            :                 /* Get pathname (mandatory) */
    1297         [ -  + ]:         30 :                 if (argc - optidx < 1) {
    1298                 :          0 :                         error("You must specify a path after a %s command.",
    1299                 :            :                             cmd);
    1300                 :          0 :                         return -1;
    1301                 :            :                 }
    1302                 :         30 :                 *path1 = xstrdup(argv[optidx]);
    1303                 :            :                 /* Only "rm" globs */
    1304         [ +  + ]:         30 :                 if (cmdnum != I_RM)
    1305                 :         22 :                         undo_glob_escape(*path1);
    1306                 :            :                 break;
    1307                 :            :         case I_DF:
    1308         [ +  - ]:          4 :                 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
    1309                 :            :                     iflag)) == -1)
    1310                 :            :                         return -1;
    1311                 :            :                 /* Default to current directory if no path specified */
    1312         [ -  + ]:          4 :                 if (argc - optidx < 1)
    1313                 :          0 :                         *path1 = NULL;
    1314                 :            :                 else {
    1315                 :          4 :                         *path1 = xstrdup(argv[optidx]);
    1316                 :          4 :                         undo_glob_escape(*path1);
    1317                 :            :                 }
    1318                 :            :                 break;
    1319                 :            :         case I_LS:
    1320         [ +  - ]:         25 :                 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
    1321                 :            :                         return(-1);
    1322                 :            :                 /* Path is optional */
    1323         [ +  + ]:         25 :                 if (argc - optidx > 0)
    1324                 :         24 :                         *path1 = xstrdup(argv[optidx]);
    1325                 :            :                 break;
    1326                 :            :         case I_LLS:
    1327                 :            :                 /* Skip ls command and following whitespace */
    1328                 :          2 :                 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
    1329                 :            :         case I_SHELL:
    1330                 :            :                 /* Uses the rest of the line */
    1331                 :            :                 break;
    1332                 :            :         case I_LUMASK:
    1333                 :            :         case I_CHMOD:
    1334                 :          6 :                 base = 8;
    1335                 :            :         case I_CHOWN:
    1336                 :            :         case I_CHGRP:
    1337         [ +  - ]:          6 :                 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
    1338                 :            :                         return -1;
    1339                 :            :                 /* Get numeric arg (mandatory) */
    1340         [ +  - ]:          6 :                 if (argc - optidx < 1)
    1341                 :            :                         goto need_num_arg;
    1342                 :          6 :                 errno = 0;
    1343                 :          6 :                 l = strtol(argv[optidx], &cp2, base);
    1344 [ +  - ][ +  - ]:          6 :                 if (cp2 == argv[optidx] || *cp2 != '\0' ||
                 [ -  + ]
    1345 [ #  # ][ -  + ]:          6 :                     ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
    1346                 :            :                     l < 0) {
    1347                 :            :  need_num_arg:
    1348                 :          0 :                         error("You must supply a numeric argument "
    1349                 :            :                             "to the %s command.", cmd);
    1350                 :          0 :                         return -1;
    1351                 :            :                 }
    1352                 :          6 :                 *n_arg = l;
    1353         [ +  - ]:          6 :                 if (cmdnum == I_LUMASK)
    1354                 :            :                         break;
    1355                 :            :                 /* Get pathname (mandatory) */
    1356         [ -  + ]:          6 :                 if (argc - optidx < 2) {
    1357                 :          0 :                         error("You must specify a path after a %s command.",
    1358                 :            :                             cmd);
    1359                 :          0 :                         return -1;
    1360                 :            :                 }
    1361                 :          6 :                 *path1 = xstrdup(argv[optidx + 1]);
    1362                 :          6 :                 break;
    1363                 :            :         case I_QUIT:
    1364                 :            :         case I_PWD:
    1365                 :            :         case I_LPWD:
    1366                 :            :         case I_HELP:
    1367                 :            :         case I_VERSION:
    1368                 :            :         case I_PROGRESS:
    1369         [ +  - ]:         16 :                 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
    1370                 :            :                         return -1;
    1371                 :            :                 break;
    1372                 :            :         default:
    1373                 :          0 :                 fatal("Command not implemented");
    1374                 :            :         }
    1375                 :            : 
    1376                 :        184 :         *cpp = cp;
    1377                 :        184 :         return(cmdnum);
    1378                 :            : }
    1379                 :            : 
    1380                 :            : static int
    1381                 :        190 : parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
    1382                 :            :     int err_abort)
    1383                 :            : {
    1384                 :            :         char *path1, *path2, *tmp;
    1385                 :        190 :         int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0, iflag = 0;
    1386                 :        190 :         int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
    1387                 :            :         int cmdnum, i;
    1388                 :        190 :         unsigned long n_arg = 0;
    1389                 :            :         Attrib a, *aa;
    1390                 :            :         char path_buf[MAXPATHLEN];
    1391                 :        190 :         int err = 0;
    1392                 :            :         glob_t g;
    1393                 :            : 
    1394                 :        190 :         path1 = path2 = NULL;
    1395                 :        190 :         cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
    1396                 :            :             &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
    1397         [ +  + ]:        190 :         if (ignore_errors != 0)
    1398                 :          1 :                 err_abort = 0;
    1399                 :            : 
    1400                 :            :         memset(&g, 0, sizeof(g));
    1401                 :            : 
    1402                 :            :         /* Perform command */
    1403   [ +  -  +  +  :        190 :         switch (cmdnum) {
          +  -  +  +  +  
          +  +  +  +  +  
          +  +  +  -  +  
          -  +  +  +  +  
                -  -  + ]
    1404                 :            :         case 0:
    1405                 :            :                 /* Blank line */
    1406                 :            :                 break;
    1407                 :            :         case -1:
    1408                 :            :                 /* Unrecognized command */
    1409                 :          1 :                 err = -1;
    1410                 :          1 :                 break;
    1411                 :            :         case I_REGET:
    1412                 :          0 :                 aflag = 1;
    1413                 :            :                 /* FALLTHROUGH */
    1414                 :            :         case I_GET:
    1415                 :         38 :                 err = process_get(conn, path1, path2, *pwd, pflag,
    1416                 :            :                     rflag, aflag, fflag);
    1417                 :         38 :                 break;
    1418                 :            :         case I_PUT:
    1419                 :         32 :                 err = process_put(conn, path1, path2, *pwd, pflag,
    1420                 :            :                     rflag, fflag);
    1421                 :         32 :                 break;
    1422                 :            :         case I_RENAME:
    1423                 :         16 :                 path1 = make_absolute(path1, *pwd);
    1424                 :         16 :                 path2 = make_absolute(path2, *pwd);
    1425                 :         16 :                 err = do_rename(conn, path1, path2, lflag);
    1426                 :         16 :                 break;
    1427                 :            :         case I_SYMLINK:
    1428                 :          0 :                 sflag = 1;
    1429                 :            :         case I_LINK:
    1430         [ +  + ]:         14 :                 if (!sflag)
    1431                 :          7 :                         path1 = make_absolute(path1, *pwd);
    1432                 :         14 :                 path2 = make_absolute(path2, *pwd);
    1433         [ +  + ]:         14 :                 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
    1434                 :         14 :                 break;
    1435                 :            :         case I_RM:
    1436                 :          8 :                 path1 = make_absolute(path1, *pwd);
    1437                 :          8 :                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
    1438 [ +  + ][ +  - ]:         14 :                 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
    1439         [ -  + ]:          9 :                         if (!quiet)
    1440                 :          0 :                                 printf("Removing %s\n", g.gl_pathv[i]);
    1441                 :          9 :                         err = do_rm(conn, g.gl_pathv[i]);
    1442         [ +  + ]:          9 :                         if (err != 0 && err_abort)
    1443                 :            :                                 break;
    1444                 :            :                 }
    1445                 :            :                 break;
    1446                 :            :         case I_MKDIR:
    1447                 :          7 :                 path1 = make_absolute(path1, *pwd);
    1448                 :          7 :                 attrib_clear(&a);
    1449                 :          7 :                 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
    1450                 :          7 :                 a.perm = 0777;
    1451                 :          7 :                 err = do_mkdir(conn, path1, &a, 1);
    1452                 :          7 :                 break;
    1453                 :            :         case I_RMDIR:
    1454                 :          7 :                 path1 = make_absolute(path1, *pwd);
    1455                 :          7 :                 err = do_rmdir(conn, path1);
    1456                 :          7 :                 break;
    1457                 :            :         case I_CHDIR:
    1458                 :          3 :                 path1 = make_absolute(path1, *pwd);
    1459         [ +  - ]:          3 :                 if ((tmp = do_realpath(conn, path1)) == NULL) {
    1460                 :            :                         err = 1;
    1461                 :            :                         break;
    1462                 :            :                 }
    1463         [ -  + ]:          3 :                 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
    1464                 :          0 :                         free(tmp);
    1465                 :          0 :                         err = 1;
    1466                 :          0 :                         break;
    1467                 :            :                 }
    1468         [ -  + ]:          3 :                 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
    1469                 :          0 :                         error("Can't change directory: Can't check target");
    1470                 :          0 :                         free(tmp);
    1471                 :          0 :                         err = 1;
    1472                 :          0 :                         break;
    1473                 :            :                 }
    1474         [ -  + ]:          3 :                 if (!S_ISDIR(aa->perm)) {
    1475                 :          0 :                         error("Can't change directory: \"%s\" is not "
    1476                 :            :                             "a directory", tmp);
    1477                 :          0 :                         free(tmp);
    1478                 :          0 :                         err = 1;
    1479                 :          0 :                         break;
    1480                 :            :                 }
    1481                 :          3 :                 free(*pwd);
    1482                 :          3 :                 *pwd = tmp;
    1483                 :          3 :                 break;
    1484                 :            :         case I_LS:
    1485         [ +  + ]:         25 :                 if (!path1) {
    1486                 :          1 :                         do_ls_dir(conn, *pwd, *pwd, lflag);
    1487                 :          1 :                         break;
    1488                 :            :                 }
    1489                 :            : 
    1490                 :            :                 /* Strip pwd off beginning of non-absolute paths */
    1491                 :         24 :                 tmp = NULL;
    1492         [ -  + ]:         24 :                 if (*path1 != '/')
    1493                 :          0 :                         tmp = *pwd;
    1494                 :            : 
    1495                 :         24 :                 path1 = make_absolute(path1, *pwd);
    1496                 :         24 :                 err = do_globbed_ls(conn, path1, tmp, lflag);
    1497                 :         24 :                 break;
    1498                 :            :         case I_DF:
    1499                 :            :                 /* Default to current directory if no path specified */
    1500         [ -  + ]:          4 :                 if (path1 == NULL)
    1501                 :          0 :                         path1 = xstrdup(*pwd);
    1502                 :          4 :                 path1 = make_absolute(path1, *pwd);
    1503                 :          4 :                 err = do_df(conn, path1, hflag, iflag);
    1504                 :          4 :                 break;
    1505                 :            :         case I_LCHDIR:
    1506         [ -  + ]:          4 :                 if (chdir(path1) == -1) {
    1507                 :          0 :                         error("Couldn't change local directory to "
    1508                 :          0 :                             "\"%s\": %s", path1, strerror(errno));
    1509                 :          0 :                         err = 1;
    1510                 :            :                 }
    1511                 :            :                 break;
    1512                 :            :         case I_LMKDIR:
    1513         [ -  + ]:          1 :                 if (mkdir(path1, 0777) == -1) {
    1514                 :          0 :                         error("Couldn't create local directory "
    1515                 :          0 :                             "\"%s\": %s", path1, strerror(errno));
    1516                 :          0 :                         err = 1;
    1517                 :            :                 }
    1518                 :            :                 break;
    1519                 :            :         case I_LLS:
    1520                 :          2 :                 local_do_ls(cmd);
    1521                 :          2 :                 break;
    1522                 :            :         case I_SHELL:
    1523                 :          1 :                 local_do_shell(cmd);
    1524                 :          1 :                 break;
    1525                 :            :         case I_LUMASK:
    1526                 :          0 :                 umask(n_arg);
    1527                 :          0 :                 printf("Local umask: %03lo\n", n_arg);
    1528                 :            :                 break;
    1529                 :            :         case I_CHMOD:
    1530                 :          6 :                 path1 = make_absolute(path1, *pwd);
    1531                 :          6 :                 attrib_clear(&a);
    1532                 :          6 :                 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
    1533                 :          6 :                 a.perm = n_arg;
    1534                 :          6 :                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
    1535 [ +  + ][ +  - ]:          9 :                 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
    1536         [ -  + ]:          6 :                         if (!quiet)
    1537                 :          0 :                                 printf("Changing mode on %s\n", g.gl_pathv[i]);
    1538                 :          6 :                         err = do_setstat(conn, g.gl_pathv[i], &a);
    1539         [ +  + ]:          6 :                         if (err != 0 && err_abort)
    1540                 :            :                                 break;
    1541                 :            :                 }
    1542                 :            :                 break;
    1543                 :            :         case I_CHOWN:
    1544                 :            :         case I_CHGRP:
    1545                 :          0 :                 path1 = make_absolute(path1, *pwd);
    1546                 :          0 :                 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
    1547 [ #  # ][ #  # ]:          0 :                 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
    1548         [ #  # ]:          0 :                         if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
    1549         [ #  # ]:          0 :                                 if (err_abort) {
    1550                 :            :                                         err = -1;
    1551                 :            :                                         break;
    1552                 :            :                                 } else
    1553                 :          0 :                                         continue;
    1554                 :            :                         }
    1555         [ #  # ]:          0 :                         if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
    1556                 :          0 :                                 error("Can't get current ownership of "
    1557                 :          0 :                                     "remote file \"%s\"", g.gl_pathv[i]);
    1558         [ #  # ]:          0 :                                 if (err_abort) {
    1559                 :            :                                         err = -1;
    1560                 :            :                                         break;
    1561                 :            :                                 } else
    1562                 :          0 :                                         continue;
    1563                 :            :                         }
    1564                 :          0 :                         aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
    1565         [ #  # ]:          0 :                         if (cmdnum == I_CHOWN) {
    1566         [ #  # ]:          0 :                                 if (!quiet)
    1567                 :          0 :                                         printf("Changing owner on %s\n",
    1568                 :          0 :                                             g.gl_pathv[i]);
    1569                 :          0 :                                 aa->uid = n_arg;
    1570                 :            :                         } else {
    1571         [ #  # ]:          0 :                                 if (!quiet)
    1572                 :          0 :                                         printf("Changing group on %s\n",
    1573                 :          0 :                                             g.gl_pathv[i]);
    1574                 :          0 :                                 aa->gid = n_arg;
    1575                 :            :                         }
    1576                 :          0 :                         err = do_setstat(conn, g.gl_pathv[i], aa);
    1577         [ #  # ]:          0 :                         if (err != 0 && err_abort)
    1578                 :            :                                 break;
    1579                 :            :                 }
    1580                 :            :                 break;
    1581                 :            :         case I_PWD:
    1582                 :          1 :                 printf("Remote working directory: %s\n", *pwd);
    1583                 :            :                 break;
    1584                 :            :         case I_LPWD:
    1585         [ -  + ]:          1 :                 if (!getcwd(path_buf, sizeof(path_buf))) {
    1586                 :          0 :                         error("Couldn't get local cwd: %s", strerror(errno));
    1587                 :          0 :                         err = -1;
    1588                 :          0 :                         break;
    1589                 :            :                 }
    1590                 :            :                 printf("Local working directory: %s\n", path_buf);
    1591                 :            :                 break;
    1592                 :            :         case I_QUIT:
    1593                 :            :                 /* Processed below */
    1594                 :            :                 break;
    1595                 :            :         case I_HELP:
    1596                 :            :                 help();
    1597                 :            :                 break;
    1598                 :            :         case I_VERSION:
    1599                 :         12 :                 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
    1600                 :            :                 break;
    1601                 :            :         case I_PROGRESS:
    1602                 :          0 :                 showprogress = !showprogress;
    1603         [ #  # ]:          0 :                 if (showprogress)
    1604                 :            :                         printf("Progress meter enabled\n");
    1605                 :            :                 else
    1606                 :            :                         printf("Progress meter disabled\n");
    1607                 :            :                 break;
    1608                 :            :         default:
    1609                 :          0 :                 fatal("%d is not implemented", cmdnum);
    1610                 :            :         }
    1611                 :            : 
    1612         [ +  + ]:        190 :         if (g.gl_pathc)
    1613                 :         14 :                 globfree(&g);
    1614                 :        190 :         free(path1);
    1615                 :        190 :         free(path2);
    1616                 :            : 
    1617                 :            :         /* If an unignored error occurs in batch mode we should abort. */
    1618         [ +  + ]:        190 :         if (err_abort && err != 0)
    1619                 :            :                 return (-1);
    1620         [ +  + ]:        149 :         else if (cmdnum == I_QUIT)
    1621                 :            :                 return (1);
    1622                 :            : 
    1623                 :        148 :         return (0);
    1624                 :            : }
    1625                 :            : 
    1626                 :            : #ifdef USE_LIBEDIT
    1627                 :            : static char *
    1628                 :            : prompt(EditLine *el)
    1629                 :            : {
    1630                 :            :         return ("sftp> ");
    1631                 :            : }
    1632                 :            : 
    1633                 :            : /* Display entries in 'list' after skipping the first 'len' chars */
    1634                 :            : static void
    1635                 :            : complete_display(char **list, u_int len)
    1636                 :            : {
    1637                 :            :         u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
    1638                 :            :         struct winsize ws;
    1639                 :            :         char *tmp;
    1640                 :            : 
    1641                 :            :         /* Count entries for sort and find longest */
    1642                 :            :         for (y = 0; list[y]; y++)
    1643                 :            :                 m = MAX(m, strlen(list[y]));
    1644                 :            : 
    1645                 :            :         if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
    1646                 :            :                 width = ws.ws_col;
    1647                 :            : 
    1648                 :            :         m = m > len ? m - len : 0;
    1649                 :            :         columns = width / (m + 2);
    1650                 :            :         columns = MAX(columns, 1);
    1651                 :            :         colspace = width / columns;
    1652                 :            :         colspace = MIN(colspace, width);
    1653                 :            : 
    1654                 :            :         printf("\n");
    1655                 :            :         m = 1;
    1656                 :            :         for (y = 0; list[y]; y++) {
    1657                 :            :                 llen = strlen(list[y]);
    1658                 :            :                 tmp = llen > len ? list[y] + len : "";
    1659                 :            :                 printf("%-*s", colspace, tmp);
    1660                 :            :                 if (m >= columns) {
    1661                 :            :                         printf("\n");
    1662                 :            :                         m = 1;
    1663                 :            :                 } else
    1664                 :            :                         m++;
    1665                 :            :         }
    1666                 :            :         printf("\n");
    1667                 :            : }
    1668                 :            : 
    1669                 :            : /*
    1670                 :            :  * Given a "list" of words that begin with a common prefix of "word",
    1671                 :            :  * attempt to find an autocompletion to extends "word" by the next
    1672                 :            :  * characters common to all entries in "list".
    1673                 :            :  */
    1674                 :            : static char *
    1675                 :            : complete_ambiguous(const char *word, char **list, size_t count)
    1676                 :            : {
    1677                 :            :         if (word == NULL)
    1678                 :            :                 return NULL;
    1679                 :            : 
    1680                 :            :         if (count > 0) {
    1681                 :            :                 u_int y, matchlen = strlen(list[0]);
    1682                 :            : 
    1683                 :            :                 /* Find length of common stem */
    1684                 :            :                 for (y = 1; list[y]; y++) {
    1685                 :            :                         u_int x;
    1686                 :            : 
    1687                 :            :                         for (x = 0; x < matchlen; x++)
    1688                 :            :                                 if (list[0][x] != list[y][x])
    1689                 :            :                                         break;
    1690                 :            : 
    1691                 :            :                         matchlen = x;
    1692                 :            :                 }
    1693                 :            : 
    1694                 :            :                 if (matchlen > strlen(word)) {
    1695                 :            :                         char *tmp = xstrdup(list[0]);
    1696                 :            : 
    1697                 :            :                         tmp[matchlen] = '\0';
    1698                 :            :                         return tmp;
    1699                 :            :                 }
    1700                 :            :         }
    1701                 :            : 
    1702                 :            :         return xstrdup(word);
    1703                 :            : }
    1704                 :            : 
    1705                 :            : /* Autocomplete a sftp command */
    1706                 :            : static int
    1707                 :            : complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
    1708                 :            :     int terminated)
    1709                 :            : {
    1710                 :            :         u_int y, count = 0, cmdlen, tmplen;
    1711                 :            :         char *tmp, **list, argterm[3];
    1712                 :            :         const LineInfo *lf;
    1713                 :            : 
    1714                 :            :         list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
    1715                 :            : 
    1716                 :            :         /* No command specified: display all available commands */
    1717                 :            :         if (cmd == NULL) {
    1718                 :            :                 for (y = 0; cmds[y].c; y++)
    1719                 :            :                         list[count++] = xstrdup(cmds[y].c);
    1720                 :            : 
    1721                 :            :                 list[count] = NULL;
    1722                 :            :                 complete_display(list, 0);
    1723                 :            : 
    1724                 :            :                 for (y = 0; list[y] != NULL; y++)
    1725                 :            :                         free(list[y]);
    1726                 :            :                 free(list);
    1727                 :            :                 return count;
    1728                 :            :         }
    1729                 :            : 
    1730                 :            :         /* Prepare subset of commands that start with "cmd" */
    1731                 :            :         cmdlen = strlen(cmd);
    1732                 :            :         for (y = 0; cmds[y].c; y++)  {
    1733                 :            :                 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
    1734                 :            :                         list[count++] = xstrdup(cmds[y].c);
    1735                 :            :         }
    1736                 :            :         list[count] = NULL;
    1737                 :            : 
    1738                 :            :         if (count == 0) {
    1739                 :            :                 free(list);
    1740                 :            :                 return 0;
    1741                 :            :         }
    1742                 :            : 
    1743                 :            :         /* Complete ambigious command */
    1744                 :            :         tmp = complete_ambiguous(cmd, list, count);
    1745                 :            :         if (count > 1)
    1746                 :            :                 complete_display(list, 0);
    1747                 :            : 
    1748                 :            :         for (y = 0; list[y]; y++)
    1749                 :            :                 free(list[y]);
    1750                 :            :         free(list);
    1751                 :            : 
    1752                 :            :         if (tmp != NULL) {
    1753                 :            :                 tmplen = strlen(tmp);
    1754                 :            :                 cmdlen = strlen(cmd);
    1755                 :            :                 /* If cmd may be extended then do so */
    1756                 :            :                 if (tmplen > cmdlen)
    1757                 :            :                         if (el_insertstr(el, tmp + cmdlen) == -1)
    1758                 :            :                                 fatal("el_insertstr failed.");
    1759                 :            :                 lf = el_line(el);
    1760                 :            :                 /* Terminate argument cleanly */
    1761                 :            :                 if (count == 1) {
    1762                 :            :                         y = 0;
    1763                 :            :                         if (!terminated)
    1764                 :            :                                 argterm[y++] = quote;
    1765                 :            :                         if (lastarg || *(lf->cursor) != ' ')
    1766                 :            :                                 argterm[y++] = ' ';
    1767                 :            :                         argterm[y] = '\0';
    1768                 :            :                         if (y > 0 && el_insertstr(el, argterm) == -1)
    1769                 :            :                                 fatal("el_insertstr failed.");
    1770                 :            :                 }
    1771                 :            :                 free(tmp);
    1772                 :            :         }
    1773                 :            : 
    1774                 :            :         return count;
    1775                 :            : }
    1776                 :            : 
    1777                 :            : /*
    1778                 :            :  * Determine whether a particular sftp command's arguments (if any)
    1779                 :            :  * represent local or remote files.
    1780                 :            :  */
    1781                 :            : static int
    1782                 :            : complete_is_remote(char *cmd) {
    1783                 :            :         int i;
    1784                 :            : 
    1785                 :            :         if (cmd == NULL)
    1786                 :            :                 return -1;
    1787                 :            : 
    1788                 :            :         for (i = 0; cmds[i].c; i++) {
    1789                 :            :                 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
    1790                 :            :                         return cmds[i].t;
    1791                 :            :         }
    1792                 :            : 
    1793                 :            :         return -1;
    1794                 :            : }
    1795                 :            : 
    1796                 :            : /* Autocomplete a filename "file" */
    1797                 :            : static int
    1798                 :            : complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
    1799                 :            :     char *file, int remote, int lastarg, char quote, int terminated)
    1800                 :            : {
    1801                 :            :         glob_t g;
    1802                 :            :         char *tmp, *tmp2, ins[8];
    1803                 :            :         u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
    1804                 :            :         int clen;
    1805                 :            :         const LineInfo *lf;
    1806                 :            : 
    1807                 :            :         /* Glob from "file" location */
    1808                 :            :         if (file == NULL)
    1809                 :            :                 tmp = xstrdup("*");
    1810                 :            :         else
    1811                 :            :                 xasprintf(&tmp, "%s*", file);
    1812                 :            : 
    1813                 :            :         /* Check if the path is absolute. */
    1814                 :            :         isabs = tmp[0] == '/';
    1815                 :            : 
    1816                 :            :         memset(&g, 0, sizeof(g));
    1817                 :            :         if (remote != LOCAL) {
    1818                 :            :                 tmp = make_absolute(tmp, remote_path);
    1819                 :            :                 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
    1820                 :            :         } else
    1821                 :            :                 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
    1822                 :            : 
    1823                 :            :         /* Determine length of pwd so we can trim completion display */
    1824                 :            :         for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
    1825                 :            :                 /* Terminate counting on first unescaped glob metacharacter */
    1826                 :            :                 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
    1827                 :            :                         if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
    1828                 :            :                                 hadglob = 1;
    1829                 :            :                         break;
    1830                 :            :                 }
    1831                 :            :                 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
    1832                 :            :                         tmplen++;
    1833                 :            :                 if (tmp[tmplen] == '/')
    1834                 :            :                         pwdlen = tmplen + 1;    /* track last seen '/' */
    1835                 :            :         }
    1836                 :            :         free(tmp);
    1837                 :            : 
    1838                 :            :         if (g.gl_matchc == 0)
    1839                 :            :                 goto out;
    1840                 :            : 
    1841                 :            :         if (g.gl_matchc > 1)
    1842                 :            :                 complete_display(g.gl_pathv, pwdlen);
    1843                 :            : 
    1844                 :            :         tmp = NULL;
    1845                 :            :         /* Don't try to extend globs */
    1846                 :            :         if (file == NULL || hadglob)
    1847                 :            :                 goto out;
    1848                 :            : 
    1849                 :            :         tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
    1850                 :            :         tmp = path_strip(tmp2, isabs ? NULL : remote_path);
    1851                 :            :         free(tmp2);
    1852                 :            : 
    1853                 :            :         if (tmp == NULL)
    1854                 :            :                 goto out;
    1855                 :            : 
    1856                 :            :         tmplen = strlen(tmp);
    1857                 :            :         filelen = strlen(file);
    1858                 :            : 
    1859                 :            :         /* Count the number of escaped characters in the input string. */
    1860                 :            :         cesc = isesc = 0;
    1861                 :            :         for (i = 0; i < filelen; i++) {
    1862                 :            :                 if (!isesc && file[i] == '\\' && i + 1 < filelen){
    1863                 :            :                         isesc = 1;
    1864                 :            :                         cesc++;
    1865                 :            :                 } else
    1866                 :            :                         isesc = 0;
    1867                 :            :         }
    1868                 :            : 
    1869                 :            :         if (tmplen > (filelen - cesc)) {
    1870                 :            :                 tmp2 = tmp + filelen - cesc;
    1871                 :            :                 len = strlen(tmp2);
    1872                 :            :                 /* quote argument on way out */
    1873                 :            :                 for (i = 0; i < len; i += clen) {
    1874                 :            :                         if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
    1875                 :            :                             (size_t)clen > sizeof(ins) - 2)
    1876                 :            :                                 fatal("invalid multibyte character");
    1877                 :            :                         ins[0] = '\\';
    1878                 :            :                         memcpy(ins + 1, tmp2 + i, clen);
    1879                 :            :                         ins[clen + 1] = '\0';
    1880                 :            :                         switch (tmp2[i]) {
    1881                 :            :                         case '\'':
    1882                 :            :                         case '"':
    1883                 :            :                         case '\\':
    1884                 :            :                         case '\t':
    1885                 :            :                         case '[':
    1886                 :            :                         case ' ':
    1887                 :            :                         case '#':
    1888                 :            :                         case '*':
    1889                 :            :                                 if (quote == '\0' || tmp2[i] == quote) {
    1890                 :            :                                         if (el_insertstr(el, ins) == -1)
    1891                 :            :                                                 fatal("el_insertstr "
    1892                 :            :                                                     "failed.");
    1893                 :            :                                         break;
    1894                 :            :                                 }
    1895                 :            :                                 /* FALLTHROUGH */
    1896                 :            :                         default:
    1897                 :            :                                 if (el_insertstr(el, ins + 1) == -1)
    1898                 :            :                                         fatal("el_insertstr failed.");
    1899                 :            :                                 break;
    1900                 :            :                         }
    1901                 :            :                 }
    1902                 :            :         }
    1903                 :            : 
    1904                 :            :         lf = el_line(el);
    1905                 :            :         if (g.gl_matchc == 1) {
    1906                 :            :                 i = 0;
    1907                 :            :                 if (!terminated)
    1908                 :            :                         ins[i++] = quote;
    1909                 :            :                 if (*(lf->cursor - 1) != '/' &&
    1910                 :            :                     (lastarg || *(lf->cursor) != ' '))
    1911                 :            :                         ins[i++] = ' ';
    1912                 :            :                 ins[i] = '\0';
    1913                 :            :                 if (i > 0 && el_insertstr(el, ins) == -1)
    1914                 :            :                         fatal("el_insertstr failed.");
    1915                 :            :         }
    1916                 :            :         free(tmp);
    1917                 :            : 
    1918                 :            :  out:
    1919                 :            :         globfree(&g);
    1920                 :            :         return g.gl_matchc;
    1921                 :            : }
    1922                 :            : 
    1923                 :            : /* tab-completion hook function, called via libedit */
    1924                 :            : static unsigned char
    1925                 :            : complete(EditLine *el, int ch)
    1926                 :            : {
    1927                 :            :         char **argv, *line, quote;
    1928                 :            :         int argc, carg;
    1929                 :            :         u_int cursor, len, terminated, ret = CC_ERROR;
    1930                 :            :         const LineInfo *lf;
    1931                 :            :         struct complete_ctx *complete_ctx;
    1932                 :            : 
    1933                 :            :         lf = el_line(el);
    1934                 :            :         if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
    1935                 :            :                 fatal("%s: el_get failed", __func__);
    1936                 :            : 
    1937                 :            :         /* Figure out which argument the cursor points to */
    1938                 :            :         cursor = lf->cursor - lf->buffer;
    1939                 :            :         line = (char *)xmalloc(cursor + 1);
    1940                 :            :         memcpy(line, lf->buffer, cursor);
    1941                 :            :         line[cursor] = '\0';
    1942                 :            :         argv = makeargv(line, &carg, 1, &quote, &terminated);
    1943                 :            :         free(line);
    1944                 :            : 
    1945                 :            :         /* Get all the arguments on the line */
    1946                 :            :         len = lf->lastchar - lf->buffer;
    1947                 :            :         line = (char *)xmalloc(len + 1);
    1948                 :            :         memcpy(line, lf->buffer, len);
    1949                 :            :         line[len] = '\0';
    1950                 :            :         argv = makeargv(line, &argc, 1, NULL, NULL);
    1951                 :            : 
    1952                 :            :         /* Ensure cursor is at EOL or a argument boundary */
    1953                 :            :         if (line[cursor] != ' ' && line[cursor] != '\0' &&
    1954                 :            :             line[cursor] != '\n') {
    1955                 :            :                 free(line);
    1956                 :            :                 return ret;
    1957                 :            :         }
    1958                 :            : 
    1959                 :            :         if (carg == 0) {
    1960                 :            :                 /* Show all available commands */
    1961                 :            :                 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
    1962                 :            :                 ret = CC_REDISPLAY;
    1963                 :            :         } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ')  {
    1964                 :            :                 /* Handle the command parsing */
    1965                 :            :                 if (complete_cmd_parse(el, argv[0], argc == carg,
    1966                 :            :                     quote, terminated) != 0)
    1967                 :            :                         ret = CC_REDISPLAY;
    1968                 :            :         } else if (carg >= 1) {
    1969                 :            :                 /* Handle file parsing */
    1970                 :            :                 int remote = complete_is_remote(argv[0]);
    1971                 :            :                 char *filematch = NULL;
    1972                 :            : 
    1973                 :            :                 if (carg > 1 && line[cursor-1] != ' ')
    1974                 :            :                         filematch = argv[carg - 1];
    1975                 :            : 
    1976                 :            :                 if (remote != 0 &&
    1977                 :            :                     complete_match(el, complete_ctx->conn,
    1978                 :            :                     *complete_ctx->remote_pathp, filematch,
    1979                 :            :                     remote, carg == argc, quote, terminated) != 0)
    1980                 :            :                         ret = CC_REDISPLAY;
    1981                 :            :         }
    1982                 :            : 
    1983                 :            :         free(line);
    1984                 :            :         return ret;
    1985                 :            : }
    1986                 :            : #endif /* USE_LIBEDIT */
    1987                 :            : 
    1988                 :            : int
    1989                 :        150 : interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
    1990                 :            : {
    1991                 :            :         char *remote_path;
    1992                 :        150 :         char *dir = NULL;
    1993                 :            :         char cmd[2048];
    1994                 :            :         int err, interactive;
    1995                 :        150 :         EditLine *el = NULL;
    1996                 :            : #ifdef USE_LIBEDIT
    1997                 :            :         History *hl = NULL;
    1998                 :            :         HistEvent hev;
    1999                 :            :         extern char *__progname;
    2000                 :            :         struct complete_ctx complete_ctx;
    2001                 :            : 
    2002                 :            :         if (!batchmode && isatty(STDIN_FILENO)) {
    2003                 :            :                 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
    2004                 :            :                         fatal("Couldn't initialise editline");
    2005                 :            :                 if ((hl = history_init()) == NULL)
    2006                 :            :                         fatal("Couldn't initialise editline history");
    2007                 :            :                 history(hl, &hev, H_SETSIZE, 100);
    2008                 :            :                 el_set(el, EL_HIST, history, hl);
    2009                 :            : 
    2010                 :            :                 el_set(el, EL_PROMPT, prompt);
    2011                 :            :                 el_set(el, EL_EDITOR, "emacs");
    2012                 :            :                 el_set(el, EL_TERMINAL, NULL);
    2013                 :            :                 el_set(el, EL_SIGNAL, 1);
    2014                 :            :                 el_source(el, NULL);
    2015                 :            : 
    2016                 :            :                 /* Tab Completion */
    2017                 :            :                 el_set(el, EL_ADDFN, "ftp-complete",
    2018                 :            :                     "Context sensitive argument completion", complete);
    2019                 :            :                 complete_ctx.conn = conn;
    2020                 :            :                 complete_ctx.remote_pathp = &remote_path;
    2021                 :            :                 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
    2022                 :            :                 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
    2023                 :            :                 /* enable ctrl-left-arrow and ctrl-right-arrow */
    2024                 :            :                 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
    2025                 :            :                 el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
    2026                 :            :                 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
    2027                 :            :                 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
    2028                 :            :                 /* make ^w match ksh behaviour */
    2029                 :            :                 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
    2030                 :            :         }
    2031                 :            : #endif /* USE_LIBEDIT */
    2032                 :            : 
    2033                 :        150 :         remote_path = do_realpath(conn, ".");
    2034         [ -  + ]:        150 :         if (remote_path == NULL)
    2035                 :          0 :                 fatal("Need cwd");
    2036                 :            : 
    2037         [ -  + ]:        150 :         if (file1 != NULL) {
    2038                 :          0 :                 dir = xstrdup(file1);
    2039                 :          0 :                 dir = make_absolute(dir, remote_path);
    2040                 :            : 
    2041 [ #  # ][ #  # ]:          0 :                 if (remote_is_dir(conn, dir) && file2 == NULL) {
    2042         [ #  # ]:          0 :                         if (!quiet)
    2043                 :            :                                 printf("Changing to: %s\n", dir);
    2044                 :            :                         snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
    2045         [ #  # ]:          0 :                         if (parse_dispatch_command(conn, cmd,
    2046                 :            :                             &remote_path, 1) != 0) {
    2047                 :          0 :                                 free(dir);
    2048                 :          0 :                                 free(remote_path);
    2049                 :          0 :                                 free(conn);
    2050                 :          0 :                                 return (-1);
    2051                 :            :                         }
    2052                 :            :                 } else {
    2053                 :            :                         /* XXX this is wrong wrt quoting */
    2054 [ #  # ][ #  # ]:          0 :                         snprintf(cmd, sizeof cmd, "get%s %s%s%s",
                 [ #  # ]
    2055                 :          0 :                             global_aflag ? " -a" : "", dir,
    2056                 :            :                             file2 == NULL ? "" : " ",
    2057                 :            :                             file2 == NULL ? "" : file2);
    2058                 :          0 :                         err = parse_dispatch_command(conn, cmd,
    2059                 :            :                             &remote_path, 1);
    2060                 :          0 :                         free(dir);
    2061                 :          0 :                         free(remote_path);
    2062                 :          0 :                         free(conn);
    2063                 :          0 :                         return (err);
    2064                 :            :                 }
    2065                 :          0 :                 free(dir);
    2066                 :            :         }
    2067                 :            : 
    2068                 :        150 :         setlinebuf(stdout);
    2069                 :        150 :         setlinebuf(infile);
    2070                 :            : 
    2071 [ +  + ][ +  - ]:        150 :         interactive = !batchmode && isatty(STDIN_FILENO);
    2072                 :        150 :         err = 0;
    2073                 :            :         for (;;) {
    2074                 :            :                 char *cp;
    2075                 :            : 
    2076                 :        298 :                 signal(SIGINT, SIG_IGN);
    2077                 :            : 
    2078                 :            :                 if (el == NULL) {
    2079         [ -  + ]:        298 :                         if (interactive)
    2080                 :            :                                 printf("sftp> ");
    2081         [ +  + ]:        298 :                         if (fgets(cmd, sizeof(cmd), infile) == NULL) {
    2082         [ -  + ]:        108 :                                 if (interactive)
    2083                 :            :                                         printf("\n");
    2084                 :            :                                 break;
    2085                 :            :                         }
    2086         [ +  - ]:        190 :                         if (!interactive) { /* Echo command */
    2087                 :            :                                 printf("sftp> %s", cmd);
    2088 [ +  - ][ +  + ]:        190 :                                 if (strlen(cmd) > 0 &&
    2089                 :        190 :                                     cmd[strlen(cmd) - 1] != '\n')
    2090                 :            :                                         printf("\n");
    2091                 :            :                         }
    2092                 :            :                 } else {
    2093                 :            : #ifdef USE_LIBEDIT
    2094                 :            :                         const char *line;
    2095                 :            :                         int count = 0;
    2096                 :            : 
    2097                 :            :                         if ((line = el_gets(el, &count)) == NULL ||
    2098                 :            :                             count <= 0) {
    2099                 :            :                                 printf("\n");
    2100                 :            :                                 break;
    2101                 :            :                         }
    2102                 :            :                         history(hl, &hev, H_ENTER, line);
    2103                 :            :                         if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
    2104                 :            :                                 fprintf(stderr, "Error: input line too long\n");
    2105                 :            :                                 continue;
    2106                 :            :                         }
    2107                 :            : #endif /* USE_LIBEDIT */
    2108                 :            :                 }
    2109                 :            : 
    2110                 :        190 :                 cp = strrchr(cmd, '\n');
    2111         [ +  + ]:        190 :                 if (cp)
    2112                 :        175 :                         *cp = '\0';
    2113                 :            : 
    2114                 :            :                 /* Handle user interrupts gracefully during commands */
    2115                 :        190 :                 interrupted = 0;
    2116                 :        190 :                 signal(SIGINT, cmd_interrupt);
    2117                 :            : 
    2118                 :        190 :                 err = parse_dispatch_command(conn, cmd, &remote_path,
    2119                 :            :                     batchmode);
    2120         [ +  + ]:        190 :                 if (err != 0)
    2121                 :            :                         break;
    2122                 :            :         }
    2123                 :        150 :         free(remote_path);
    2124                 :        150 :         free(conn);
    2125                 :            : 
    2126                 :            : #ifdef USE_LIBEDIT
    2127                 :            :         if (el != NULL)
    2128                 :            :                 el_end(el);
    2129                 :            : #endif /* USE_LIBEDIT */
    2130                 :            : 
    2131                 :            :         /* err == 1 signifies normal "quit" exit */
    2132         [ +  + ]:        150 :         return (err >= 0 ? 0 : -1);
    2133                 :            : }
    2134                 :            : 
    2135                 :            : static void
    2136                 :        150 : connect_to_server(char *path, char **args, int *in, int *out)
    2137                 :            : {
    2138                 :            :         int c_in, c_out;
    2139                 :            : 
    2140                 :            : #ifdef USE_PIPES
    2141                 :            :         int pin[2], pout[2];
    2142                 :            : 
    2143                 :            :         if ((pipe(pin) == -1) || (pipe(pout) == -1))
    2144                 :            :                 fatal("pipe: %s", strerror(errno));
    2145                 :            :         *in = pin[0];
    2146                 :            :         *out = pout[1];
    2147                 :            :         c_in = pout[0];
    2148                 :            :         c_out = pin[1];
    2149                 :            : #else /* USE_PIPES */
    2150                 :            :         int inout[2];
    2151                 :            : 
    2152         [ -  + ]:        150 :         if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
    2153                 :          0 :                 fatal("socketpair: %s", strerror(errno));
    2154                 :        150 :         *in = *out = inout[0];
    2155                 :        150 :         c_in = c_out = inout[1];
    2156                 :            : #endif /* USE_PIPES */
    2157                 :            : 
    2158         [ -  + ]:        150 :         if ((sshpid = fork()) == -1)
    2159                 :          0 :                 fatal("fork: %s", strerror(errno));
    2160         [ +  + ]:        300 :         else if (sshpid == 0) {
    2161   [ +  -  -  + ]:        300 :                 if ((dup2(c_in, STDIN_FILENO) == -1) ||
    2162                 :        150 :                     (dup2(c_out, STDOUT_FILENO) == -1)) {
    2163                 :          0 :                         fprintf(stderr, "dup2: %s\n", strerror(errno));
    2164                 :          0 :                         _exit(1);
    2165                 :            :                 }
    2166                 :        150 :                 close(*in);
    2167                 :        150 :                 close(*out);
    2168                 :        150 :                 close(c_in);
    2169                 :        150 :                 close(c_out);
    2170                 :            : 
    2171                 :            :                 /*
    2172                 :            :                  * The underlying ssh is in the same process group, so we must
    2173                 :            :                  * ignore SIGINT if we want to gracefully abort commands,
    2174                 :            :                  * otherwise the signal will make it to the ssh process and
    2175                 :            :                  * kill it too.  Contrawise, since sftp sends SIGTERMs to the
    2176                 :            :                  * underlying ssh, it must *not* ignore that signal.
    2177                 :            :                  */
    2178                 :        150 :                 signal(SIGINT, SIG_IGN);
    2179                 :        150 :                 signal(SIGTERM, SIG_DFL);
    2180                 :        150 :                 execvp(path, args);
    2181                 :        150 :                 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
    2182                 :          0 :                 _exit(1);
    2183                 :            :         }
    2184                 :            : 
    2185                 :        150 :         signal(SIGTERM, killchild);
    2186                 :        150 :         signal(SIGINT, killchild);
    2187                 :        150 :         signal(SIGHUP, killchild);
    2188                 :        150 :         close(c_in);
    2189                 :        150 :         close(c_out);
    2190                 :        150 : }
    2191                 :            : 
    2192                 :            : static void
    2193                 :          0 : usage(void)
    2194                 :            : {
    2195                 :            :         extern char *__progname;
    2196                 :            : 
    2197                 :          0 :         fprintf(stderr,
    2198                 :            :             "usage: %s [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
    2199                 :            :             "          [-D sftp_server_path] [-F ssh_config] "
    2200                 :            :             "[-i identity_file] [-l limit]\n"
    2201                 :            :             "          [-o ssh_option] [-P port] [-R num_requests] "
    2202                 :            :             "[-S program]\n"
    2203                 :            :             "          [-s subsystem | sftp_server] host\n"
    2204                 :            :             "       %s [user@]host[:file ...]\n"
    2205                 :            :             "       %s [user@]host[:dir[/]]\n"
    2206                 :            :             "       %s -b batchfile [user@]host\n",
    2207                 :            :             __progname, __progname, __progname, __progname);
    2208                 :          0 :         exit(1);
    2209                 :            : }
    2210                 :            : 
    2211                 :            : int
    2212                 :        150 : main(int argc, char **argv)
    2213                 :            : {
    2214                 :            :         int in, out, ch, err;
    2215                 :        150 :         char *host = NULL, *userhost, *cp, *file2 = NULL;
    2216                 :        150 :         int debug_level = 0, sshver = 2;
    2217                 :        150 :         char *file1 = NULL, *sftp_server = NULL;
    2218                 :        150 :         char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
    2219                 :            :         const char *errstr;
    2220                 :        150 :         LogLevel ll = SYSLOG_LEVEL_INFO;
    2221                 :            :         arglist args;
    2222                 :            :         extern int optind;
    2223                 :            :         extern char *optarg;
    2224                 :            :         struct sftp_conn *conn;
    2225                 :        150 :         size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
    2226                 :        150 :         size_t num_requests = DEFAULT_NUM_REQUESTS;
    2227                 :        150 :         long long limit_kbps = 0;
    2228                 :            : 
    2229                 :            :         /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
    2230                 :        150 :         sanitise_stdfd();
    2231                 :        150 :         setlocale(LC_CTYPE, "");
    2232                 :            : 
    2233                 :        150 :         __progname = ssh_get_progname(argv[0]);
    2234                 :            :         memset(&args, '\0', sizeof(args));
    2235                 :            :         args.list = NULL;
    2236                 :        150 :         addargs(&args, "%s", ssh_program);
    2237                 :        150 :         addargs(&args, "-oForwardX11 no");
    2238                 :        150 :         addargs(&args, "-oForwardAgent no");
    2239                 :        150 :         addargs(&args, "-oPermitLocalCommand no");
    2240                 :        150 :         addargs(&args, "-oClearAllForwardings yes");
    2241                 :            : 
    2242                 :        150 :         ll = SYSLOG_LEVEL_INFO;
    2243                 :        150 :         infile = stdin;
    2244                 :            : 
    2245         [ +  + ]:        669 :         while ((ch = getopt(argc, argv,
    2246                 :            :             "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
    2247   [ -  +  -  -  :        519 :                 switch (ch) {
          +  -  -  +  +  
          -  -  +  -  -  
             +  -  +  -  
                      - ]
    2248                 :            :                 /* Passed through to ssh(1) */
    2249                 :            :                 case '4':
    2250                 :            :                 case '6':
    2251                 :            :                 case 'C':
    2252                 :          0 :                         addargs(&args, "-%c", ch);
    2253                 :        519 :                         break;
    2254                 :            :                 /* Passed through to ssh(1) with argument */
    2255                 :            :                 case 'F':
    2256                 :            :                 case 'c':
    2257                 :            :                 case 'i':
    2258                 :            :                 case 'o':
    2259                 :          2 :                         addargs(&args, "-%c", ch);
    2260                 :          2 :                         addargs(&args, "%s", optarg);
    2261                 :          2 :                         break;
    2262                 :            :                 case 'q':
    2263                 :          0 :                         ll = SYSLOG_LEVEL_ERROR;
    2264                 :          0 :                         quiet = 1;
    2265                 :          0 :                         showprogress = 0;
    2266                 :          0 :                         addargs(&args, "-%c", ch);
    2267                 :          0 :                         break;
    2268                 :            :                 case 'P':
    2269                 :          0 :                         addargs(&args, "-oPort %s", optarg);
    2270                 :          0 :                         break;
    2271                 :            :                 case 'v':
    2272         [ +  - ]:        234 :                         if (debug_level < 3) {
    2273                 :        234 :                                 addargs(&args, "-v");
    2274                 :        234 :                                 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
    2275                 :            :                         }
    2276                 :        234 :                         debug_level++;
    2277                 :        234 :                         break;
    2278                 :            :                 case '1':
    2279                 :          0 :                         sshver = 1;
    2280         [ #  # ]:          0 :                         if (sftp_server == NULL)
    2281                 :          0 :                                 sftp_server = _PATH_SFTP_SERVER;
    2282                 :            :                         break;
    2283                 :            :                 case '2':
    2284                 :            :                         sshver = 2;
    2285                 :            :                         break;
    2286                 :            :                 case 'a':
    2287                 :          0 :                         global_aflag = 1;
    2288                 :          0 :                         break;
    2289                 :            :                 case 'B':
    2290                 :         12 :                         copy_buffer_len = strtol(optarg, &cp, 10);
    2291 [ +  - ][ -  + ]:         12 :                         if (copy_buffer_len == 0 || *cp != '\0')
    2292                 :          0 :                                 fatal("Invalid buffer size \"%s\"", optarg);
    2293                 :            :                         break;
    2294                 :            :                 case 'b':
    2295         [ -  + ]:        109 :                         if (batchmode)
    2296                 :          0 :                                 fatal("Batch file already specified.");
    2297                 :            : 
    2298                 :            :                         /* Allow "-" as stdin */
    2299         [ +  + ]:        125 :                         if (strcmp(optarg, "-") != 0 &&
           [ +  +  -  + ]
    2300                 :         16 :                             (infile = fopen(optarg, "r")) == NULL)
    2301                 :          0 :                                 fatal("%s (%s).", strerror(errno), optarg);
    2302                 :        109 :                         showprogress = 0;
    2303                 :        109 :                         quiet = batchmode = 1;
    2304                 :        109 :                         addargs(&args, "-obatchmode yes");
    2305                 :        109 :                         break;
    2306                 :            :                 case 'f':
    2307                 :          0 :                         global_fflag = 1;
    2308                 :          0 :                         break;
    2309                 :            :                 case 'p':
    2310                 :          0 :                         global_pflag = 1;
    2311                 :          0 :                         break;
    2312                 :            :                 case 'D':
    2313                 :        149 :                         sftp_direct = optarg;
    2314                 :        149 :                         break;
    2315                 :            :                 case 'l':
    2316                 :          0 :                         limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
    2317                 :            :                             &errstr);
    2318         [ #  # ]:          0 :                         if (errstr != NULL)
    2319                 :          0 :                                 usage();
    2320                 :          0 :                         limit_kbps *= 1024; /* kbps */
    2321                 :          0 :                         break;
    2322                 :            :                 case 'r':
    2323                 :          0 :                         global_rflag = 1;
    2324                 :          0 :                         break;
    2325                 :            :                 case 'R':
    2326                 :         12 :                         num_requests = strtol(optarg, &cp, 10);
    2327 [ +  - ][ -  + ]:         12 :                         if (num_requests == 0 || *cp != '\0')
    2328                 :          0 :                                 fatal("Invalid number of requests \"%s\"",
    2329                 :            :                                     optarg);
    2330                 :            :                         break;
    2331                 :            :                 case 's':
    2332                 :          0 :                         sftp_server = optarg;
    2333                 :          0 :                         break;
    2334                 :            :                 case 'S':
    2335                 :          1 :                         ssh_program = optarg;
    2336                 :          1 :                         replacearg(&args, 0, "%s", ssh_program);
    2337                 :          1 :                         break;
    2338                 :            :                 case 'h':
    2339                 :            :                 default:
    2340                 :          0 :                         usage();
    2341                 :            :                 }
    2342                 :            :         }
    2343                 :            : 
    2344         [ +  - ]:        150 :         if (!isatty(STDERR_FILENO))
    2345                 :        150 :                 showprogress = 0;
    2346                 :            : 
    2347                 :        150 :         log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
    2348                 :            : 
    2349         [ +  + ]:        150 :         if (sftp_direct == NULL) {
    2350 [ +  - ][ -  + ]:          1 :                 if (optind == argc || argc > (optind + 2))
    2351                 :          0 :                         usage();
    2352                 :            : 
    2353                 :          1 :                 userhost = xstrdup(argv[optind]);
    2354                 :          1 :                 file2 = argv[optind+1];
    2355                 :            : 
    2356         [ -  + ]:          1 :                 if ((host = strrchr(userhost, '@')) == NULL)
    2357                 :            :                         host = userhost;
    2358                 :            :                 else {
    2359                 :          0 :                         *host++ = '\0';
    2360         [ #  # ]:          0 :                         if (!userhost[0]) {
    2361                 :          0 :                                 fprintf(stderr, "Missing username\n");
    2362                 :          0 :                                 usage();
    2363                 :            :                         }
    2364                 :          0 :                         addargs(&args, "-l");
    2365                 :          0 :                         addargs(&args, "%s", userhost);
    2366                 :            :                 }
    2367                 :            : 
    2368         [ -  + ]:          1 :                 if ((cp = colon(host)) != NULL) {
    2369                 :          0 :                         *cp++ = '\0';
    2370                 :          0 :                         file1 = cp;
    2371                 :            :                 }
    2372                 :            : 
    2373                 :          1 :                 host = cleanhostname(host);
    2374         [ -  + ]:          1 :                 if (!*host) {
    2375                 :          0 :                         fprintf(stderr, "Missing hostname\n");
    2376                 :          0 :                         usage();
    2377                 :            :                 }
    2378                 :            : 
    2379                 :          1 :                 addargs(&args, "-oProtocol %d", sshver);
    2380                 :            : 
    2381                 :            :                 /* no subsystem if the server-spec contains a '/' */
    2382 [ -  + ][ #  # ]:          1 :                 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
    2383                 :          1 :                         addargs(&args, "-s");
    2384                 :            : 
    2385                 :          1 :                 addargs(&args, "--");
    2386                 :          1 :                 addargs(&args, "%s", host);
    2387         [ -  + ]:          1 :                 addargs(&args, "%s", (sftp_server != NULL ?
    2388                 :            :                     sftp_server : "sftp"));
    2389                 :            : 
    2390                 :          1 :                 connect_to_server(ssh_program, args.list, &in, &out);
    2391                 :            :         } else {
    2392                 :        149 :                 args.list = NULL;
    2393                 :        149 :                 addargs(&args, "sftp-server");
    2394                 :            : 
    2395                 :        149 :                 connect_to_server(sftp_direct, args.list, &in, &out);
    2396                 :            :         }
    2397                 :        150 :         freeargs(&args);
    2398                 :            : 
    2399                 :        150 :         conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
    2400         [ -  + ]:        150 :         if (conn == NULL)
    2401                 :          0 :                 fatal("Couldn't initialise connection to server");
    2402                 :            : 
    2403         [ +  + ]:        150 :         if (!quiet) {
    2404         [ +  + ]:         41 :                 if (sftp_direct == NULL)
    2405                 :          1 :                         fprintf(stderr, "Connected to %s.\n", host);
    2406                 :            :                 else
    2407                 :         40 :                         fprintf(stderr, "Attached to %s.\n", sftp_direct);
    2408                 :            :         }
    2409                 :            : 
    2410                 :        150 :         err = interactive_loop(conn, file1, file2);
    2411                 :            : 
    2412                 :            : #if !defined(USE_PIPES)
    2413                 :        150 :         shutdown(in, SHUT_RDWR);
    2414                 :        150 :         shutdown(out, SHUT_RDWR);
    2415                 :            : #endif
    2416                 :            : 
    2417                 :        150 :         close(in);
    2418                 :        150 :         close(out);
    2419         [ +  + ]:        150 :         if (batchmode)
    2420                 :        109 :                 fclose(infile);
    2421                 :            : 
    2422         [ -  + ]:        150 :         while (waitpid(sshpid, NULL, 0) == -1)
    2423         [ #  # ]:          0 :                 if (errno != EINTR)
    2424                 :        150 :                         fatal("Couldn't wait for ssh process: %s",
    2425                 :            :                             strerror(errno));
    2426                 :            : 
    2427                 :        150 :         exit(err == 0 ? 0 : 1);
    2428                 :            : }

Generated by: LCOV version 1.9