Initial import from FreeBSD RELENG_4:
[games.git] / crypto / heimdal / appl / ftp / ftp / main.c
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 /*
35  * FTP User Program -- Command Interface.
36  */
37
38 #include "ftp_locl.h"
39 #include <getarg.h>
40
41 RCSID("$Id: main.c,v 1.32 2002/08/23 19:11:03 assar Exp $");
42
43 static int help_flag;
44 static int version_flag;
45 static int debug_flag;
46
47 struct getargs getargs[] = {
48     { NULL,     'd', arg_flag, &debug_flag,
49       "debug", NULL },
50     { NULL,     'g', arg_negative_flag, &doglob,
51       "disables globbing", NULL},
52     { NULL,     'i', arg_negative_flag, &interactive,
53       "Turn off interactive prompting", NULL},
54     { NULL,     'l', arg_negative_flag, &lineedit,
55       "Turn off line editing", NULL},
56     { NULL,     'p', arg_flag, &passivemode,
57       "passive mode", NULL},
58     { NULL,     't', arg_counter, &trace,
59       "Packet tracing", NULL},
60     { NULL,     'v', arg_counter, &verbose,
61       "verbosity", NULL},
62     { NULL,     'K', arg_negative_flag, &use_kerberos,
63       "Disable kerberos authentication", NULL},
64     { "version", 0,  arg_flag, &version_flag },
65     { "help",   'h', arg_flag, &help_flag },
66 };
67
68 static int num_args = sizeof(getargs) / sizeof(getargs[0]);
69
70 static void
71 usage(int ecode)
72 {
73     arg_printusage(getargs, num_args, NULL, "[host [port]]");
74     exit(ecode);
75 }
76
77 int
78 main(int argc, char **argv)
79 {
80         int top;
81         struct passwd *pw = NULL;
82         char homedir[MaxPathLen];
83         struct servent *sp;
84         int optind = 0;
85
86         setprogname(argv[0]);
87
88         sp = getservbyname("ftp", "tcp");
89         if (sp == 0)
90                 errx(1, "ftp/tcp: unknown service");
91         doglob = 1;
92         interactive = 1;
93         autologin = 1;
94         lineedit = 1;
95         passivemode = 0; /* passive mode not active */
96         use_kerberos = 1;
97
98         if(getarg(getargs, num_args, argc, argv, &optind))
99                 usage(1);
100         if(help_flag)
101                 usage(0);
102         if(version_flag) {
103                 print_version(NULL);
104                 exit(0);
105         }
106
107         if (debug_flag) {
108                 options |= SO_DEBUG;
109                 debug++;
110         }
111
112         argc -= optind;
113         argv += optind;
114
115         fromatty = isatty(fileno(stdin));
116         if (fromatty)
117                 verbose++;
118         cpend = 0;      /* no pending replies */
119         proxy = 0;      /* proxy not active */
120         crflag = 1;     /* strip c.r. on ascii gets */
121         sendport = -1;  /* not using ports */
122         /*
123          * Set up the home directory in case we're globbing.
124          */
125         pw = k_getpwuid(getuid());
126         if (pw != NULL) {
127                 strlcpy(homedir, pw->pw_dir, sizeof(homedir));
128                 home = homedir;
129         }
130         if (argc > 0) {
131             char *xargv[5];
132             
133             if (setjmp(toplevel))
134                 exit(0);
135             signal(SIGINT, intr);
136             signal(SIGPIPE, lostpeer);
137             xargv[0] = (char*)getprogname();
138             xargv[1] = argv[0];
139             xargv[2] = argv[1];
140             xargv[3] = argv[2];
141             xargv[4] = NULL;
142             setpeer(argc+1, xargv);
143         }
144         if(setjmp(toplevel) == 0)
145             top = 1;
146         else
147             top = 0;
148         if (top) {
149             signal(SIGINT, intr);
150             signal(SIGPIPE, lostpeer);
151         }
152         for (;;) {
153             cmdscanner(top);
154             top = 1;
155         }
156 }
157
158 void
159 intr(int sig)
160 {
161
162         longjmp(toplevel, 1);
163 }
164
165 #ifndef SHUT_RDWR
166 #define SHUT_RDWR 2
167 #endif
168
169 RETSIGTYPE
170 lostpeer(int sig)
171 {
172
173     if (connected) {
174         if (cout != NULL) {
175             shutdown(fileno(cout), SHUT_RDWR);
176             fclose(cout);
177             cout = NULL;
178         }
179         if (data >= 0) {
180             shutdown(data, SHUT_RDWR);
181             close(data);
182             data = -1;
183         }
184         connected = 0;
185     }
186     pswitch(1);
187     if (connected) {
188         if (cout != NULL) {
189             shutdown(fileno(cout), SHUT_RDWR);
190             fclose(cout);
191             cout = NULL;
192         }
193         connected = 0;
194     }
195     proxflag = 0;
196     pswitch(0);
197     sec_end();
198     SIGRETURN(0);
199 }
200
201 /*
202 char *
203 tail(filename)
204         char *filename;
205 {
206         char *s;
207         
208         while (*filename) {
209                 s = strrchr(filename, '/');
210                 if (s == NULL)
211                         break;
212                 if (s[1])
213                         return (s + 1);
214                 *s = '\0';
215         }
216         return (filename);
217 }
218 */
219
220 static char *
221 simple_readline(char *prompt)
222 {
223     char buf[BUFSIZ];
224     printf ("%s", prompt);
225     fflush (stdout);
226     if(fgets(buf, sizeof(buf), stdin) == NULL)
227         return NULL;
228     if (buf[strlen(buf) - 1] == '\n')
229         buf[strlen(buf) - 1] = '\0';
230     return strdup(buf);
231 }
232
233 #ifndef HAVE_READLINE
234
235 static char *
236 readline(char *prompt)
237 {
238     return simple_readline (prompt);
239 }
240
241 static void
242 add_history(char *p)
243 {
244 }
245
246 #else
247
248 /* These should not really be here */
249
250 char *readline(char *);
251 void add_history(char *);
252
253 #endif
254
255 /*
256  * Command parser.
257  */
258 void
259 cmdscanner(int top)
260 {
261     struct cmd *c;
262     int l;
263
264     if (!top)
265         putchar('\n');
266     for (;;) {
267         if (fromatty) {
268             char *p;
269             if (lineedit)
270                 p = readline("ftp> ");
271             else
272                 p = simple_readline("ftp> ");
273             if(p == NULL) {
274                 printf("\n");
275                 quit(0, 0);
276             }
277             strlcpy(line, p, sizeof(line));
278             if (lineedit)
279                 add_history(p);
280             free(p);
281         } else{
282             if (fgets(line, sizeof line, stdin) == NULL)
283                 quit(0, 0);
284         }
285         /* XXX will break on long lines */
286         l = strlen(line);
287         if (l == 0)
288             break;
289         if (line[--l] == '\n') {
290             if (l == 0)
291                 break;
292             line[l] = '\0';
293         } else if (l == sizeof(line) - 2) {
294             printf("sorry, input line too long\n");
295             while ((l = getchar()) != '\n' && l != EOF)
296                 /* void */;
297             break;
298         } /* else it was a line without a newline */
299         makeargv();
300         if (margc == 0) {
301             continue;
302         }
303         c = getcmd(margv[0]);
304         if (c == (struct cmd *)-1) {
305             printf("?Ambiguous command\n");
306             continue;
307         }
308         if (c == 0) {
309             printf("?Invalid command\n");
310             continue;
311         }
312         if (c->c_conn && !connected) {
313             printf("Not connected.\n");
314             continue;
315         }
316         (*c->c_handler)(margc, margv);
317         if (bell && c->c_bell)
318             putchar('\007');
319         if (c->c_handler != help)
320             break;
321     }
322     signal(SIGINT, intr);
323     signal(SIGPIPE, lostpeer);
324 }
325
326 struct cmd *
327 getcmd(char *name)
328 {
329         char *p, *q;
330         struct cmd *c, *found;
331         int nmatches, longest;
332
333         longest = 0;
334         nmatches = 0;
335         found = 0;
336         for (c = cmdtab; (p = c->c_name); c++) {
337                 for (q = name; *q == *p++; q++)
338                         if (*q == 0)            /* exact match? */
339                                 return (c);
340                 if (!*q) {                      /* the name was a prefix */
341                         if (q - name > longest) {
342                                 longest = q - name;
343                                 nmatches = 1;
344                                 found = c;
345                         } else if (q - name == longest)
346                                 nmatches++;
347                 }
348         }
349         if (nmatches > 1)
350                 return ((struct cmd *)-1);
351         return (found);
352 }
353
354 /*
355  * Slice a string up into argc/argv.
356  */
357
358 int slrflag;
359
360 void
361 makeargv(void)
362 {
363         char **argp;
364
365         argp = margv;
366         stringbase = line;              /* scan from first of buffer */
367         argbase = argbuf;               /* store from first of buffer */
368         slrflag = 0;
369         for (margc = 0; ; margc++) {
370                 /* Expand array if necessary */
371                 if (margc == margvlen) {
372                         int i;
373
374                         margv = (margvlen == 0)
375                                 ? (char **)malloc(20 * sizeof(char *))
376                                 : (char **)realloc(margv,
377                                         (margvlen + 20)*sizeof(char *));
378                         if (margv == NULL)
379                                 errx(1, "cannot realloc argv array");
380                         for(i = margvlen; i < margvlen + 20; ++i)
381                                 margv[i] = NULL;
382                         margvlen += 20;
383                         argp = margv + margc;
384                 }
385
386                 if ((*argp++ = slurpstring()) == NULL)
387                         break;
388         }
389
390 }
391
392 /*
393  * Parse string into argbuf;
394  * implemented with FSM to
395  * handle quoting and strings
396  */
397 char *
398 slurpstring(void)
399 {
400         int got_one = 0;
401         char *sb = stringbase;
402         char *ap = argbase;
403         char *tmp = argbase;            /* will return this if token found */
404
405         if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
406                 switch (slrflag) {      /* and $ as token for macro invoke */
407                         case 0:
408                                 slrflag++;
409                                 stringbase++;
410                                 return ((*sb == '!') ? "!" : "$");
411                                 /* NOTREACHED */
412                         case 1:
413                                 slrflag++;
414                                 altarg = stringbase;
415                                 break;
416                         default:
417                                 break;
418                 }
419         }
420
421 S0:
422         switch (*sb) {
423
424         case '\0':
425                 goto OUT;
426
427         case ' ':
428         case '\t':
429                 sb++; goto S0;
430
431         default:
432                 switch (slrflag) {
433                         case 0:
434                                 slrflag++;
435                                 break;
436                         case 1:
437                                 slrflag++;
438                                 altarg = sb;
439                                 break;
440                         default:
441                                 break;
442                 }
443                 goto S1;
444         }
445
446 S1:
447         switch (*sb) {
448
449         case ' ':
450         case '\t':
451         case '\0':
452                 goto OUT;       /* end of token */
453
454         case '\\':
455                 sb++; goto S2;  /* slurp next character */
456
457         case '"':
458                 sb++; goto S3;  /* slurp quoted string */
459
460         default:
461                 *ap++ = *sb++;  /* add character to token */
462                 got_one = 1;
463                 goto S1;
464         }
465
466 S2:
467         switch (*sb) {
468
469         case '\0':
470                 goto OUT;
471
472         default:
473                 *ap++ = *sb++;
474                 got_one = 1;
475                 goto S1;
476         }
477
478 S3:
479         switch (*sb) {
480
481         case '\0':
482                 goto OUT;
483
484         case '"':
485                 sb++; goto S1;
486
487         default:
488                 *ap++ = *sb++;
489                 got_one = 1;
490                 goto S3;
491         }
492
493 OUT:
494         if (got_one)
495                 *ap++ = '\0';
496         argbase = ap;                   /* update storage pointer */
497         stringbase = sb;                /* update scan pointer */
498         if (got_one) {
499                 return (tmp);
500         }
501         switch (slrflag) {
502                 case 0:
503                         slrflag++;
504                         break;
505                 case 1:
506                         slrflag++;
507                         altarg = (char *) 0;
508                         break;
509                 default:
510                         break;
511         }
512         return NULL;
513 }
514
515 #define HELPINDENT ((int) sizeof ("directory"))
516
517 /*
518  * Help command.
519  * Call each command handler with argc == 0 and argv[0] == name.
520  */
521 void
522 help(int argc, char **argv)
523 {
524         struct cmd *c;
525
526         if (argc == 1) {
527                 int i, j, w, k;
528                 int columns, width = 0, lines;
529
530                 printf("Commands may be abbreviated.  Commands are:\n\n");
531                 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
532                         int len = strlen(c->c_name);
533
534                         if (len > width)
535                                 width = len;
536                 }
537                 width = (width + 8) &~ 7;
538                 columns = 80 / width;
539                 if (columns == 0)
540                         columns = 1;
541                 lines = (NCMDS + columns - 1) / columns;
542                 for (i = 0; i < lines; i++) {
543                         for (j = 0; j < columns; j++) {
544                                 c = cmdtab + j * lines + i;
545                                 if (c->c_name && (!proxy || c->c_proxy)) {
546                                         printf("%s", c->c_name);
547                                 }
548                                 else if (c->c_name) {
549                                         for (k=0; k < strlen(c->c_name); k++) {
550                                                 putchar(' ');
551                                         }
552                                 }
553                                 if (c + lines >= &cmdtab[NCMDS]) {
554                                         printf("\n");
555                                         break;
556                                 }
557                                 w = strlen(c->c_name);
558                                 while (w < width) {
559                                         w = (w + 8) &~ 7;
560                                         putchar('\t');
561                                 }
562                         }
563                 }
564                 return;
565         }
566         while (--argc > 0) {
567                 char *arg;
568                 arg = *++argv;
569                 c = getcmd(arg);
570                 if (c == (struct cmd *)-1)
571                         printf("?Ambiguous help command %s\n", arg);
572                 else if (c == (struct cmd *)0)
573                         printf("?Invalid help command %s\n", arg);
574                 else
575                         printf("%-*s\t%s\n", HELPINDENT,
576                                 c->c_name, c->c_help);
577         }
578 }