cmd.c
changeset 0 1d0ce1ebbc72
equal deleted inserted replaced
-1:000000000000 0:1d0ce1ebbc72
       
     1 /*
       
     2  * Copyright (c) 2018 Sunil Nimmagadda <sunil@openbsd.org>
       
     3  *
       
     4  * Permission to use, copy, modify, and distribute this software for any
       
     5  * purpose with or without fee is hereby granted, provided that the above
       
     6  * copyright notice and this permission notice appear in all copies.
       
     7  *
       
     8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
       
     9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
       
    10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
       
    11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
       
    12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
       
    13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
       
    14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
       
    15  */
       
    16 
       
    17 #include <sys/socket.h>
       
    18 #include <sys/stat.h>
       
    19 
       
    20 #include <arpa/telnet.h>
       
    21 
       
    22 #include <err.h>
       
    23 #include <errno.h>
       
    24 #include <histedit.h>
       
    25 #include <libgen.h>
       
    26 #include <limits.h>
       
    27 #include <pwd.h>
       
    28 #include <signal.h>
       
    29 #include <stdio.h>
       
    30 #include <stdlib.h>
       
    31 #include <string.h>
       
    32 #include <unistd.h>
       
    33 
       
    34 #include "ftp.h"
       
    35 
       
    36 #define ARGVMAX	64
       
    37 
       
    38 static void	 cmd_interrupt(int);
       
    39 static int	 cmd_lookup(const char *);
       
    40 static FILE	*data_fopen(const char *);
       
    41 static void	 do_open(int, char **);
       
    42 static void	 do_help(int, char **);
       
    43 static void	 do_quit(int, char **);
       
    44 static void	 do_ls(int, char **);
       
    45 static void	 do_pwd(int, char **);
       
    46 static void	 do_cd(int, char **);
       
    47 static void	 do_get(int, char **);
       
    48 static void	 do_passive(int, char **);
       
    49 static void	 do_lcd(int, char **);
       
    50 static void	 do_lpwd(int, char **);
       
    51 static void	 do_put(int, char **);
       
    52 static void	 do_mget(int, char **);
       
    53 static void	 ftp_abort(void);
       
    54 static char	*prompt(void);
       
    55 
       
    56 static FILE	*ctrl_fp, *data_fp;
       
    57 
       
    58 static struct {
       
    59 	const char	 *name;
       
    60 	const char	 *info;
       
    61 	void		(*cmd)(int, char **);
       
    62 	int		  conn_required;
       
    63 } cmd_tbl[] = {
       
    64 	{ "open", "connect to remote ftp server", do_open, 0 },
       
    65 	{ "close", "terminate ftp session", do_quit, 1 },
       
    66 	{ "help", "print local help information", do_help, 0 },
       
    67 	{ "?", "print local help information", do_help, 0 },
       
    68 	{ "quit", "terminate ftp session and exit", do_quit, 0 },
       
    69 	{ "exit", "terminate ftp session and exit", do_quit, 0 },
       
    70 	{ "ls", "list contents of remote directory", do_ls, 1 },
       
    71 	{ "pwd", "print working directory on remote machine", do_pwd, 1 },
       
    72 	{ "cd", "change remote working directory", do_cd, 1 },
       
    73 	{ "nlist", "nlist contents of remote directory", do_ls, 1 },
       
    74 	{ "get", "receive file", do_get, 1 },
       
    75 	{ "passive", "toggle passive transfer mode", do_passive, 0 },
       
    76 	{ "lcd", "change local working directory", do_lcd, 0 },
       
    77 	{ "lpwd", "print local working directory", do_lpwd, 0 },
       
    78 	{ "put", "send one file", do_put, 1 },
       
    79 	{ "mget", "get multiple files", do_mget, 1 },
       
    80 	{ "mput", "send multiple files", do_mget, 1 },
       
    81 };
       
    82 
       
    83 static void
       
    84 cmd_interrupt(int signo)
       
    85 {
       
    86 	const char	msg[] = "\rwaiting for remote to finish abort\n";
       
    87 	int		save_errno = errno;
       
    88 
       
    89 	if (data_fp != NULL)
       
    90 		(void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
       
    91 
       
    92 	interrupted = 1;
       
    93 	errno = save_errno;
       
    94 }
       
    95 
       
    96 void
       
    97 cmd(const char *host, const char *port, const char *path)
       
    98 {
       
    99 	HistEvent	  hev;
       
   100 	EditLine	 *el;
       
   101 	History		 *hist;
       
   102 	const char	 *line;
       
   103 	char		**ap, *argv[ARGVMAX], *cp;
       
   104 	int		  count, i;
       
   105 
       
   106 	if ((el = el_init(getprogname(), stdin, stdout, stderr)) == NULL)
       
   107 		err(1, "couldn't initialise editline");
       
   108 
       
   109 	if ((hist = history_init()) == NULL)
       
   110 		err(1, "couldn't initialise editline history");
       
   111 
       
   112 	history(hist, &hev, H_SETSIZE, 100);
       
   113 	el_set(el, EL_HIST, history, hist);
       
   114 	el_set(el, EL_PROMPT, prompt);
       
   115 	el_set(el, EL_EDITOR, "emacs");
       
   116 	el_set(el, EL_TERMINAL, NULL);
       
   117 	el_set(el, EL_SIGNAL, 1);
       
   118 	el_source(el, NULL);
       
   119 
       
   120 	if (host != NULL) {
       
   121 		argv[0] = "open";
       
   122 		argv[1] = (char *)host;
       
   123 		argv[2] = port ? (char *)port : "21";
       
   124 		do_open(3, argv);
       
   125 		/* If we don't have a connection, exit */
       
   126 		if (ctrl_fp == NULL)
       
   127 			exit(1);
       
   128 
       
   129 		if (path != NULL) {
       
   130 			argv[0] = "cd";
       
   131 			argv[1] = (char *)path;
       
   132 			do_cd(2, argv);
       
   133 		}
       
   134 	}
       
   135 
       
   136 	for (;;) {
       
   137 		signal(SIGINT, SIG_IGN);
       
   138 		if ((line = el_gets(el, &count)) == NULL || count <= 0) {
       
   139 			if (verbose)
       
   140 				fprintf(stderr, "\n");
       
   141 			argv[0] = "quit";
       
   142 			do_quit(1, argv);
       
   143 			break;
       
   144 		}
       
   145 
       
   146 		if (count <= 1)
       
   147 			continue;
       
   148 
       
   149 		if ((cp = strrchr(line, '\n')) != NULL)
       
   150 			*cp = '\0';
       
   151 
       
   152 		history(hist, &hev, H_ENTER, line);
       
   153 		for (ap = argv; ap < &argv[ARGVMAX - 1] &&
       
   154 		    (*ap = strsep((char **)&line, " \t")) != NULL;) {
       
   155 			if (**ap != '\0')
       
   156 				ap++;
       
   157 		}
       
   158 		*ap = NULL;
       
   159 
       
   160 		if (argv[0] == NULL)
       
   161 			continue;
       
   162 
       
   163 		if ((i = cmd_lookup(argv[0])) == -1) {
       
   164 			fprintf(stderr, "Invalid command.\n");
       
   165 			continue;
       
   166 		}
       
   167 
       
   168 		if (cmd_tbl[i].conn_required && ctrl_fp == NULL) {
       
   169 			fprintf(stderr, "Not connected.\n");
       
   170 			continue;
       
   171 		}
       
   172 
       
   173 		interrupted = 0;
       
   174 		signal(SIGINT, cmd_interrupt);
       
   175 		cmd_tbl[i].cmd(ap - argv, argv);
       
   176 
       
   177 		if (strcmp(cmd_tbl[i].name, "quit") == 0 ||
       
   178 		    strcmp(cmd_tbl[i].name, "exit") == 0)
       
   179 			break;
       
   180 	}
       
   181 
       
   182 	el_end(el);
       
   183 }
       
   184 
       
   185 static int
       
   186 cmd_lookup(const char *cmd)
       
   187 {
       
   188 	size_t	i;
       
   189 
       
   190 	for (i = 0; i < nitems(cmd_tbl); i++)
       
   191 		if (strcmp(cmd, cmd_tbl[i].name) == 0)
       
   192 			return i;
       
   193 
       
   194 	return -1;
       
   195 }
       
   196 
       
   197 static char *
       
   198 prompt(void)
       
   199 {
       
   200 	return "ftp> ";
       
   201 }
       
   202 
       
   203 static FILE *
       
   204 data_fopen(const char *mode)
       
   205 {
       
   206 	int	 fd;
       
   207 
       
   208 	fd = activemode ? ftp_eprt(ctrl_fp) : ftp_epsv(ctrl_fp);
       
   209 	if (fd == -1) {
       
   210 		if (io_debug)
       
   211 			fprintf(stderr, "Failed to open data connection");
       
   212 
       
   213 		return NULL;
       
   214 	}
       
   215 
       
   216 	return fdopen(fd, mode);
       
   217 }
       
   218 
       
   219 static void
       
   220 ftp_abort(void)
       
   221 {
       
   222 	char	buf[BUFSIZ];
       
   223 
       
   224 	snprintf(buf, sizeof buf, "%c%c%c", IAC, IP, IAC);
       
   225 	if (send(fileno(ctrl_fp), buf, 3, MSG_OOB) != 3)
       
   226 		warn("abort");
       
   227 
       
   228 	ftp_command(ctrl_fp, "%cABOR", DM);
       
   229 }
       
   230 
       
   231 static void
       
   232 do_open(int argc, char **argv)
       
   233 {
       
   234 	const char	*host = NULL, *port = "21";
       
   235 	char		*buf = NULL;
       
   236 	size_t		 n = 0;
       
   237 	int		 sock;
       
   238 
       
   239 	if (ctrl_fp != NULL) {
       
   240 		fprintf(stderr, "already connected, use close first.\n");
       
   241 		return;
       
   242 	}
       
   243 
       
   244 	switch (argc) {
       
   245 	case 3:
       
   246 		port = argv[2];
       
   247 		/* FALLTHROUGH */
       
   248 	case 2:
       
   249 		host = argv[1];
       
   250 		break;
       
   251 	default:
       
   252 		fprintf(stderr, "usage: open host [port]\n");
       
   253 		return;
       
   254 	}
       
   255 
       
   256 	if ((sock = tcp_connect(host, port, 0)) == -1)
       
   257 		return;
       
   258 
       
   259 	fprintf(stderr, "Connected to %s.\n", host);
       
   260 	if ((ctrl_fp = fdopen(sock, "r+")) == NULL)
       
   261 		err(1, "%s: fdopen", __func__);
       
   262 
       
   263 	/* greeting */
       
   264 	ftp_getline(&buf, &n, 0, ctrl_fp);
       
   265 	free(buf);
       
   266 	if (ftp_auth(ctrl_fp, NULL, NULL) != P_OK) {
       
   267 		fclose(ctrl_fp);
       
   268 		ctrl_fp = NULL;
       
   269 	}
       
   270 }
       
   271 
       
   272 static void
       
   273 do_help(int argc, char **argv)
       
   274 {
       
   275 	size_t	i;
       
   276 	int	j;
       
   277 
       
   278 	if (argc == 1) {
       
   279 		for (i = 0; i < nitems(cmd_tbl); i++)
       
   280 			fprintf(stderr, "%s\n", cmd_tbl[i].name);
       
   281 
       
   282 		return;
       
   283 	}
       
   284 
       
   285 	for (i = 1; i < (size_t)argc; i++) {
       
   286 		if ((j = cmd_lookup(argv[i])) == -1)
       
   287 			fprintf(stderr, "invalid help command %s\n", argv[i]);
       
   288 		else
       
   289 			fprintf(stderr, "%s\t%s\n", argv[i], cmd_tbl[j].info);
       
   290 	}
       
   291 }
       
   292 
       
   293 static void
       
   294 do_quit(int argc, char **argv)
       
   295 {
       
   296 	if (ctrl_fp == NULL)
       
   297 		return;
       
   298 
       
   299 	ftp_command(ctrl_fp, "QUIT");
       
   300 	fclose(ctrl_fp);
       
   301 	ctrl_fp = NULL;
       
   302 }
       
   303 
       
   304 static void
       
   305 do_ls(int argc, char **argv)
       
   306 {
       
   307 	FILE		*dst_fp = stdout;
       
   308 	const char	*cmd, *local_fname = NULL, *remote_dir = NULL;
       
   309 	char		*buf = NULL;
       
   310 	size_t		 n = 0;
       
   311 	ssize_t		 len;
       
   312 	int		 r;
       
   313 
       
   314 	switch (argc) {
       
   315 	case 3:
       
   316 		if (strcmp(argv[2], "-") != 0)
       
   317 			local_fname = argv[2];
       
   318 		/* FALLTHROUGH */
       
   319 	case 2:
       
   320 		remote_dir = argv[1];
       
   321 		/* FALLTHROUGH */
       
   322 	case 1:
       
   323 		break;
       
   324 	default:
       
   325 		fprintf(stderr, "usage: ls [remote-directory [local-file]]\n");
       
   326 		return;
       
   327 	}
       
   328 
       
   329 	if ((data_fp = data_fopen("r")) == NULL)
       
   330 		return;
       
   331 
       
   332 	if (local_fname && (dst_fp = fopen(local_fname, "w")) == NULL) {
       
   333 		warn("fopen %s", local_fname);
       
   334 		fclose(data_fp);
       
   335 		data_fp = NULL;
       
   336 		return;
       
   337 	}
       
   338 
       
   339 	cmd = (strcmp(argv[0], "ls") == 0) ? "LIST" : "NLST";
       
   340 	if (remote_dir != NULL)
       
   341 		r = ftp_command(ctrl_fp, "%s %s", cmd, remote_dir);
       
   342 	else
       
   343 		r = ftp_command(ctrl_fp, "%s", cmd);
       
   344 
       
   345 	if (r != P_PRE) {
       
   346 		fclose(data_fp);
       
   347 		data_fp = NULL;
       
   348 		if (dst_fp != stdout)
       
   349 			fclose(dst_fp);
       
   350 
       
   351 		return;
       
   352 	}
       
   353 
       
   354 	while ((len = getline(&buf, &n, data_fp)) != -1 && !interrupted) {
       
   355 		buf[len - 1] = '\0';
       
   356 		if (len >= 2 && buf[len - 2] == '\r')
       
   357 			buf[len - 2] = '\0';
       
   358 
       
   359 		fprintf(dst_fp, "%s\n", buf);
       
   360 	}
       
   361 
       
   362 	if (interrupted)
       
   363 		ftp_abort();
       
   364 
       
   365 	fclose(data_fp);
       
   366 	data_fp = NULL;
       
   367 	ftp_getline(&buf, &n, 0, ctrl_fp);
       
   368 	free(buf);
       
   369 	if (dst_fp != stdout)
       
   370 		fclose(dst_fp);
       
   371 }
       
   372 
       
   373 static void
       
   374 do_get(int argc, char **argv)
       
   375 {
       
   376 	FILE		*dst_fp;
       
   377 	const char	*local_fname = NULL, *p, *remote_fname;
       
   378 	char		*buf = NULL;
       
   379 	size_t		 n = 0;
       
   380 	off_t		 file_sz, offset = 0;
       
   381 
       
   382 	switch (argc) {
       
   383 	case 3:
       
   384 		local_fname = argv[2];
       
   385 		/* FALLTHROUGH */
       
   386 	case 2:
       
   387 		remote_fname = argv[1];
       
   388 		break;
       
   389 	default:
       
   390 		fprintf(stderr, "usage: get remote-file [local-file]\n");
       
   391 		return;
       
   392 	}
       
   393 
       
   394 	if (local_fname == NULL)
       
   395 		local_fname = remote_fname;
       
   396 
       
   397 	if (ftp_command(ctrl_fp, "TYPE I") != P_OK)
       
   398 		return;
       
   399 
       
   400 	log_info("local: %s remote: %s\n", local_fname, remote_fname);
       
   401 	if (ftp_size(ctrl_fp, remote_fname, &file_sz, &buf) != P_OK) {
       
   402 		fprintf(stderr, "%s", buf);
       
   403 		return;
       
   404 	}
       
   405 
       
   406 	if ((data_fp = data_fopen("r")) == NULL)
       
   407 		return;
       
   408 
       
   409 	if ((dst_fp = fopen(local_fname, "w")) == NULL) {
       
   410 		warn("%s", local_fname);
       
   411 		fclose(data_fp);
       
   412 		data_fp = NULL;
       
   413 		return;
       
   414 	}
       
   415 
       
   416 	if (ftp_command(ctrl_fp, "RETR %s", remote_fname) != P_PRE) {
       
   417 		fclose(data_fp);
       
   418 		data_fp = NULL;
       
   419 		fclose(dst_fp);
       
   420 		return;
       
   421 	}
       
   422 
       
   423 	if (progressmeter) {
       
   424 		p = basename(remote_fname);
       
   425 		start_progress_meter(p, NULL, file_sz, &offset);
       
   426 	}
       
   427 
       
   428 	copy_file(dst_fp, data_fp, &offset);
       
   429 	if (progressmeter)
       
   430 		stop_progress_meter();
       
   431 
       
   432 	if (interrupted)
       
   433 		ftp_abort();
       
   434 
       
   435 	fclose(data_fp);
       
   436 	data_fp = NULL;
       
   437 	fclose(dst_fp);
       
   438 	ftp_getline(&buf, &n, 0, ctrl_fp);
       
   439 	free(buf);
       
   440 }
       
   441 
       
   442 static void
       
   443 do_pwd(int argc, char **argv)
       
   444 {
       
   445 	ftp_command(ctrl_fp, "PWD");
       
   446 }
       
   447 
       
   448 static void
       
   449 do_cd(int argc, char **argv)
       
   450 {
       
   451 	if (argc != 2) {
       
   452 		fprintf(stderr, "usage: cd remote-directory\n");
       
   453 		return;
       
   454 	}
       
   455 
       
   456 	ftp_command(ctrl_fp, "CWD %s", argv[1]);
       
   457 }
       
   458 
       
   459 static void
       
   460 do_passive(int argc, char **argv)
       
   461 {
       
   462 	switch (argc) {
       
   463 	case 1:
       
   464 		break;
       
   465 	case 2:
       
   466 		if (strcmp(argv[1], "on") == 0 || strcmp(argv[1], "off") == 0)
       
   467 			break;
       
   468 
       
   469 		/* FALLTHROUGH */
       
   470 	default:
       
   471 		fprintf(stderr, "usage: passive [on | off]\n");
       
   472 		return;
       
   473 	}
       
   474 
       
   475 	if (argv[1] != NULL) {
       
   476 		activemode = (strcmp(argv[1], "off") == 0) ? 1 : 0;
       
   477 		fprintf(stderr, "passive mode is %s\n", argv[1]);
       
   478 		return;
       
   479 	}
       
   480 
       
   481 	activemode = !activemode;
       
   482 	fprintf(stderr, "passive mode is %s\n", activemode ? "off" : "on");
       
   483 }
       
   484 
       
   485 static void
       
   486 do_lcd(int argc, char **argv)
       
   487 {
       
   488 	struct passwd	*pw = NULL;
       
   489 	const char	*dir, *login;
       
   490 	char		 cwd[PATH_MAX];
       
   491 
       
   492 	switch (argc) {
       
   493 	case 1:
       
   494 	case 2:
       
   495 		break;
       
   496 	default:
       
   497 		fprintf(stderr, "usage: lcd [local-directory]\n");
       
   498 		return;
       
   499 	}
       
   500 
       
   501 	if ((login = getlogin()) != NULL)
       
   502 		pw = getpwnam(login);
       
   503 
       
   504 	if (pw == NULL && (pw = getpwuid(getuid())) == NULL) {
       
   505 		fprintf(stderr, "Failed to get home directory\n");
       
   506 		return;
       
   507 	}
       
   508 
       
   509 	dir = argv[1] ? argv[1] : pw->pw_dir;
       
   510 	if (chdir(dir) != 0) {
       
   511 		warn("local: %s", dir);
       
   512 		return;
       
   513 	}
       
   514 
       
   515 	if (getcwd(cwd, sizeof cwd) == NULL) {
       
   516 		warn("getcwd");
       
   517 		return;
       
   518 	}
       
   519 
       
   520 	fprintf(stderr, "Local directory now %s\n", cwd);
       
   521 }
       
   522 
       
   523 static void
       
   524 do_lpwd(int argc, char **argv)
       
   525 {
       
   526 	char	cwd[PATH_MAX];
       
   527 
       
   528 	if (getcwd(cwd, sizeof cwd) == NULL) {
       
   529 		warn("getcwd");
       
   530 		return;
       
   531 	}
       
   532 
       
   533 	fprintf(stderr, "Local directory %s\n", cwd);
       
   534 }
       
   535 
       
   536 static void
       
   537 do_put(int argc, char **argv)
       
   538 {
       
   539 	struct stat	 sb;
       
   540 	FILE		*src_fp;
       
   541 	const char	*local_fname, *p, *remote_fname = NULL;
       
   542 	char		*buf = NULL;
       
   543 	size_t		 n = 0;
       
   544 	off_t		 file_sz, offset = 0;
       
   545 
       
   546 	switch (argc) {
       
   547 	case 3:
       
   548 		remote_fname = argv[2];
       
   549 		/* FALLTHROUGH */
       
   550 	case 2:
       
   551 		local_fname = argv[1];
       
   552 		break;
       
   553 	default:
       
   554 		fprintf(stderr, "usage: put local-file [remote-file]\n");
       
   555 		return;
       
   556 	}
       
   557 
       
   558 	if (remote_fname == NULL)
       
   559 		remote_fname = local_fname;
       
   560 
       
   561 	if (ftp_command(ctrl_fp, "TYPE I") != P_OK)
       
   562 		return;
       
   563 
       
   564 	log_info("local: %s remote: %s\n", local_fname, remote_fname);
       
   565 	if ((data_fp = data_fopen("w")) == NULL)
       
   566 		return;
       
   567 
       
   568 	if ((src_fp = fopen(local_fname, "r")) == NULL) {
       
   569 		warn("%s", local_fname);
       
   570 		fclose(data_fp);
       
   571 		data_fp = NULL;
       
   572 		return;
       
   573 	}
       
   574 
       
   575 	if (fstat(fileno(src_fp), &sb) != 0) {
       
   576 		warn("%s", local_fname);
       
   577 		fclose(data_fp);
       
   578 		data_fp = NULL;
       
   579 		fclose(src_fp);
       
   580 		return;
       
   581 	}
       
   582 	file_sz = sb.st_size;
       
   583 
       
   584 	if (ftp_command(ctrl_fp, "STOR %s", remote_fname) != P_PRE) {
       
   585 		fclose(data_fp);
       
   586 		data_fp = NULL;
       
   587 		fclose(src_fp);
       
   588 		return;
       
   589 	}
       
   590 
       
   591 	if (progressmeter) {
       
   592 		p = basename(remote_fname);
       
   593 		start_progress_meter(p, NULL, file_sz, &offset);
       
   594 	}
       
   595 
       
   596 	copy_file(data_fp, src_fp, &offset);
       
   597 	if (progressmeter)
       
   598 		stop_progress_meter();
       
   599 
       
   600 	if (interrupted)
       
   601 		ftp_abort();
       
   602 
       
   603 	fclose(data_fp);
       
   604 	data_fp = NULL;
       
   605 	fclose(src_fp);
       
   606 	ftp_getline(&buf, &n, 0, ctrl_fp);
       
   607 	free(buf);
       
   608 }
       
   609 
       
   610 static void
       
   611 do_mget(int argc, char **argv)
       
   612 {
       
   613 	void		(*fn)(int, char **);
       
   614 	const char	 *usage;
       
   615 	char		 *args[2];
       
   616 	int		  i;
       
   617 
       
   618 	if (strcmp(argv[0], "mget") == 0) {
       
   619 		fn = do_get;
       
   620 		args[0] = "get";
       
   621 		usage = "mget remote-files";
       
   622 	} else {
       
   623 		fn = do_put;
       
   624 		args[0] = "put";
       
   625 		usage = "mput local-files";
       
   626 	}
       
   627 
       
   628 	if (argc == 1) {
       
   629 		fprintf(stderr, "usage: %s\n", usage);
       
   630 		return;
       
   631 	}
       
   632 
       
   633 	for (i = 1; i < argc && !interrupted; i++) {
       
   634 		args[1] = argv[i];
       
   635 		fn(2, args);
       
   636 	}
       
   637 }