main.c revision 1.11 1 /*
2 * Copyright (c) 1985, 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 static char copyright[] =
36 "@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
37 The Regents of the University of California. All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94";
43 #else
44 static char rcsid[] = "$NetBSD: main.c,v 1.11 1996/05/07 00:16:55 pk Exp $";
45 #endif
46 #endif /* not lint */
47
48 /*
49 * FTP User Program -- Command Interface.
50 */
51 /*#include <sys/ioctl.h>*/
52 #include <sys/types.h>
53 #include <sys/socket.h>
54
55 #include <arpa/ftp.h>
56
57 #include <ctype.h>
58 #include <err.h>
59 #include <netdb.h>
60 #include <pwd.h>
61 #include <signal.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <unistd.h>
66
67 #include "ftp_var.h"
68
69 int
70 main(argc, argv)
71 int argc;
72 char *argv[];
73 {
74 int ch, top;
75 struct passwd *pw = NULL;
76 char *cp, homedir[MAXPATHLEN];
77
78 sp = getservbyname("ftp", "tcp");
79 if (sp == 0)
80 errx(1, "ftp/tcp: unknown service");
81 doglob = 1;
82 interactive = 1;
83 autologin = 1;
84
85 while ((ch = getopt(argc, argv, "dgintv")) != EOF) {
86 switch (ch) {
87 case 'd':
88 options |= SO_DEBUG;
89 debug++;
90 break;
91
92 case 'g':
93 doglob = 0;
94 break;
95
96 case 'i':
97 interactive = 0;
98 break;
99
100 case 'n':
101 autologin = 0;
102 break;
103
104 case 't':
105 trace++;
106 break;
107
108 case 'v':
109 verbose++;
110 break;
111
112 default:
113 (void)fprintf(stderr,
114 "usage: ftp [-dgintv] [host [port]]\n");
115 exit(1);
116 }
117 }
118 argc -= optind;
119 argv += optind;
120
121 fromatty = isatty(fileno(stdin));
122 if (fromatty)
123 verbose++;
124 cpend = 0; /* no pending replies */
125 proxy = 0; /* proxy not active */
126 passivemode = 0; /* passive mode not active */
127 crflag = 1; /* strip c.r. on ascii gets */
128 sendport = -1; /* not using ports */
129 /*
130 * Set up the home directory in case we're globbing.
131 */
132 cp = getlogin();
133 if (cp != NULL) {
134 pw = getpwnam(cp);
135 }
136 if (pw == NULL)
137 pw = getpwuid(getuid());
138 if (pw != NULL) {
139 home = homedir;
140 (void) strcpy(home, pw->pw_dir);
141 }
142 if (argc > 0) {
143 char *xargv[5];
144 extern char *__progname;
145
146 if (setjmp(toplevel))
147 exit(0);
148 (void) signal(SIGINT, intr);
149 (void) signal(SIGPIPE, lostpeer);
150 xargv[0] = __progname;
151 xargv[1] = argv[0];
152 xargv[2] = argv[1];
153 xargv[3] = argv[2];
154 xargv[4] = NULL;
155 setpeer(argc+1, xargv);
156 }
157 top = setjmp(toplevel) == 0;
158 if (top) {
159 (void) signal(SIGINT, intr);
160 (void) signal(SIGPIPE, lostpeer);
161 }
162 for (;;) {
163 cmdscanner(top);
164 top = 1;
165 }
166 }
167
168 void
169 intr()
170 {
171
172 longjmp(toplevel, 1);
173 }
174
175 void
176 lostpeer()
177 {
178
179 if (connected) {
180 if (cout != NULL) {
181 (void) shutdown(fileno(cout), 1+1);
182 (void) fclose(cout);
183 cout = NULL;
184 }
185 if (data >= 0) {
186 (void) shutdown(data, 1+1);
187 (void) close(data);
188 data = -1;
189 }
190 connected = 0;
191 }
192 pswitch(1);
193 if (connected) {
194 if (cout != NULL) {
195 (void) shutdown(fileno(cout), 1+1);
196 (void) fclose(cout);
197 cout = NULL;
198 }
199 connected = 0;
200 }
201 proxflag = 0;
202 pswitch(0);
203 }
204
205 /*
206 char *
207 tail(filename)
208 char *filename;
209 {
210 char *s;
211
212 while (*filename) {
213 s = strrchr(filename, '/');
214 if (s == NULL)
215 break;
216 if (s[1])
217 return (s + 1);
218 *s = '\0';
219 }
220 return (filename);
221 }
222 */
223
224 /*
225 * Command parser.
226 */
227 void
228 cmdscanner(top)
229 int top;
230 {
231 struct cmd *c;
232 int l;
233
234 if (!top)
235 (void) putchar('\n');
236 for (;;) {
237 if (fromatty) {
238 printf("ftp> ");
239 (void) fflush(stdout);
240 }
241 if (fgets(line, sizeof line, stdin) == NULL)
242 quit(0, 0);
243 l = strlen(line);
244 if (l == 0)
245 break;
246 if (line[--l] == '\n') {
247 if (l == 0)
248 break;
249 line[l] = '\0';
250 } else if (l == sizeof(line) - 2) {
251 printf("sorry, input line too long\n");
252 while ((l = getchar()) != '\n' && l != EOF)
253 /* void */;
254 break;
255 } /* else it was a line without a newline */
256 makeargv();
257 if (margc == 0) {
258 continue;
259 }
260 c = getcmd(margv[0]);
261 if (c == (struct cmd *)-1) {
262 printf("?Ambiguous command\n");
263 continue;
264 }
265 if (c == 0) {
266 printf("?Invalid command\n");
267 continue;
268 }
269 if (c->c_conn && !connected) {
270 printf("Not connected.\n");
271 continue;
272 }
273 (*c->c_handler)(margc, margv);
274 if (bell && c->c_bell)
275 (void) putchar('\007');
276 if (c->c_handler != help)
277 break;
278 }
279 (void) signal(SIGINT, intr);
280 (void) signal(SIGPIPE, lostpeer);
281 }
282
283 struct cmd *
284 getcmd(name)
285 char *name;
286 {
287 char *p, *q;
288 struct cmd *c, *found;
289 int nmatches, longest;
290
291 if (name == NULL)
292 return (0);
293
294 longest = 0;
295 nmatches = 0;
296 found = 0;
297 for (c = cmdtab; p = c->c_name; c++) {
298 for (q = name; *q == *p++; q++)
299 if (*q == 0) /* exact match? */
300 return (c);
301 if (!*q) { /* the name was a prefix */
302 if (q - name > longest) {
303 longest = q - name;
304 nmatches = 1;
305 found = c;
306 } else if (q - name == longest)
307 nmatches++;
308 }
309 }
310 if (nmatches > 1)
311 return ((struct cmd *)-1);
312 return (found);
313 }
314
315 /*
316 * Slice a string up into argc/argv.
317 */
318
319 int slrflag;
320
321 void
322 makeargv()
323 {
324 char **argp;
325
326 argp = margv;
327 stringbase = line; /* scan from first of buffer */
328 argbase = argbuf; /* store from first of buffer */
329 slrflag = 0;
330 for (margc = 0; ; margc++) {
331 /* Expand array if necessary */
332 if (margc == margvlen) {
333 margv = (margvlen == 0)
334 ? (char **)malloc(20 * sizeof(char *))
335 : (char **)realloc(margv,
336 (margvlen + 20)*sizeof(char *));
337 if (margv == NULL)
338 errx(1, "cannot realloc argv array");
339 margvlen += 20;
340 argp = margv + margc;
341 }
342
343 if ((*argp++ = slurpstring()) == NULL)
344 break;
345 }
346
347 }
348
349 /*
350 * Parse string into argbuf;
351 * implemented with FSM to
352 * handle quoting and strings
353 */
354 char *
355 slurpstring()
356 {
357 int got_one = 0;
358 char *sb = stringbase;
359 char *ap = argbase;
360 char *tmp = argbase; /* will return this if token found */
361
362 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
363 switch (slrflag) { /* and $ as token for macro invoke */
364 case 0:
365 slrflag++;
366 stringbase++;
367 return ((*sb == '!') ? "!" : "$");
368 /* NOTREACHED */
369 case 1:
370 slrflag++;
371 altarg = stringbase;
372 break;
373 default:
374 break;
375 }
376 }
377
378 S0:
379 switch (*sb) {
380
381 case '\0':
382 goto OUT;
383
384 case ' ':
385 case '\t':
386 sb++; goto S0;
387
388 default:
389 switch (slrflag) {
390 case 0:
391 slrflag++;
392 break;
393 case 1:
394 slrflag++;
395 altarg = sb;
396 break;
397 default:
398 break;
399 }
400 goto S1;
401 }
402
403 S1:
404 switch (*sb) {
405
406 case ' ':
407 case '\t':
408 case '\0':
409 goto OUT; /* end of token */
410
411 case '\\':
412 sb++; goto S2; /* slurp next character */
413
414 case '"':
415 sb++; goto S3; /* slurp quoted string */
416
417 default:
418 *ap++ = *sb++; /* add character to token */
419 got_one = 1;
420 goto S1;
421 }
422
423 S2:
424 switch (*sb) {
425
426 case '\0':
427 goto OUT;
428
429 default:
430 *ap++ = *sb++;
431 got_one = 1;
432 goto S1;
433 }
434
435 S3:
436 switch (*sb) {
437
438 case '\0':
439 goto OUT;
440
441 case '"':
442 sb++; goto S1;
443
444 default:
445 *ap++ = *sb++;
446 got_one = 1;
447 goto S3;
448 }
449
450 OUT:
451 if (got_one)
452 *ap++ = '\0';
453 argbase = ap; /* update storage pointer */
454 stringbase = sb; /* update scan pointer */
455 if (got_one) {
456 return (tmp);
457 }
458 switch (slrflag) {
459 case 0:
460 slrflag++;
461 break;
462 case 1:
463 slrflag++;
464 altarg = (char *) 0;
465 break;
466 default:
467 break;
468 }
469 return ((char *)0);
470 }
471
472 #define HELPINDENT ((int) sizeof ("directory"))
473
474 /*
475 * Help command.
476 * Call each command handler with argc == 0 and argv[0] == name.
477 */
478 void
479 help(argc, argv)
480 int argc;
481 char *argv[];
482 {
483 struct cmd *c;
484
485 if (argc == 1) {
486 int i, j, w, k;
487 int columns, width = 0, lines;
488
489 printf("Commands may be abbreviated. Commands are:\n\n");
490 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
491 int len = strlen(c->c_name);
492
493 if (len > width)
494 width = len;
495 }
496 width = (width + 8) &~ 7;
497 columns = 80 / width;
498 if (columns == 0)
499 columns = 1;
500 lines = (NCMDS + columns - 1) / columns;
501 for (i = 0; i < lines; i++) {
502 for (j = 0; j < columns; j++) {
503 c = cmdtab + j * lines + i;
504 if (c->c_name && (!proxy || c->c_proxy)) {
505 printf("%s", c->c_name);
506 }
507 else if (c->c_name) {
508 for (k=0; k < strlen(c->c_name); k++) {
509 (void) putchar(' ');
510 }
511 }
512 if (c + lines >= &cmdtab[NCMDS]) {
513 printf("\n");
514 break;
515 }
516 w = strlen(c->c_name);
517 while (w < width) {
518 w = (w + 8) &~ 7;
519 (void) putchar('\t');
520 }
521 }
522 }
523 return;
524 }
525 while (--argc > 0) {
526 char *arg;
527 arg = *++argv;
528 c = getcmd(arg);
529 if (c == (struct cmd *)-1)
530 printf("?Ambiguous help command %s\n", arg);
531 else if (c == (struct cmd *)0)
532 printf("?Invalid help command %s\n", arg);
533 else
534 printf("%-*s\t%s\n", HELPINDENT,
535 c->c_name, c->c_help);
536 }
537 }
538