1 /* 2 * Copyright (c) 1985 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that this notice is preserved and that due credit is given 7 * to the University of California at Berkeley. The name of the University 8 * may not be used to endorse or promote products derived from this 9 * software without specific prior written permission. This software 10 * is provided ``as is'' without express or implied warranty. 11 */ 12 13 #ifndef lint 14 char copyright[] = 15 "@(#) Copyright (c) 1985 Regents of the University of California.\n\ 16 All rights reserved.\n"; 17 #endif /* not lint */ 18 19 #ifndef lint 20 static char sccsid[] = "@(#)main.c 5.10 (Berkeley) 03/14/88"; 21 #endif /* not lint */ 22 23 /* 24 * FTP User Program -- Command Interface. 25 */ 26 #include "ftp_var.h" 27 #include <sys/socket.h> 28 #include <sys/ioctl.h> 29 #include <sys/types.h> 30 31 #include <arpa/ftp.h> 32 33 #include <signal.h> 34 #include <stdio.h> 35 #include <errno.h> 36 #include <ctype.h> 37 #include <netdb.h> 38 #include <pwd.h> 39 40 41 uid_t getuid(); 42 int intr(); 43 int lostpeer(); 44 extern char *home; 45 char *getlogin(); 46 47 main(argc, argv) 48 char *argv[]; 49 { 50 register char *cp; 51 int top; 52 struct passwd *pw = NULL; 53 char homedir[MAXPATHLEN]; 54 55 sp = getservbyname("ftp", "tcp"); 56 if (sp == 0) { 57 fprintf(stderr, "ftp: ftp/tcp: unknown service\n"); 58 exit(1); 59 } 60 doglob = 1; 61 interactive = 1; 62 autologin = 1; 63 argc--, argv++; 64 while (argc > 0 && **argv == '-') { 65 for (cp = *argv + 1; *cp; cp++) 66 switch (*cp) { 67 68 case 'd': 69 options |= SO_DEBUG; 70 debug++; 71 break; 72 73 case 'v': 74 verbose++; 75 break; 76 77 case 't': 78 trace++; 79 break; 80 81 case 'i': 82 interactive = 0; 83 break; 84 85 case 'n': 86 autologin = 0; 87 break; 88 89 case 'g': 90 doglob = 0; 91 break; 92 93 default: 94 fprintf(stdout, 95 "ftp: %c: unknown option\n", *cp); 96 exit(1); 97 } 98 argc--, argv++; 99 } 100 fromatty = isatty(fileno(stdin)); 101 /* 102 * Set up defaults for FTP. 103 */ 104 (void) strcpy(typename, "ascii"), type = TYPE_A; 105 (void) strcpy(formname, "non-print"), form = FORM_N; 106 (void) strcpy(modename, "stream"), mode = MODE_S; 107 (void) strcpy(structname, "file"), stru = STRU_F; 108 (void) strcpy(bytename, "8"), bytesize = 8; 109 if (fromatty) 110 verbose++; 111 cpend = 0; /* no pending replies */ 112 proxy = 0; /* proxy not active */ 113 crflag = 1; /* strip c.r. on ascii gets */ 114 /* 115 * Set up the home directory in case we're globbing. 116 */ 117 cp = getlogin(); 118 if (cp != NULL) { 119 pw = getpwnam(cp); 120 } 121 if (pw == NULL) 122 pw = getpwuid(getuid()); 123 if (pw != NULL) { 124 home = homedir; 125 (void) strcpy(home, pw->pw_dir); 126 } 127 if (argc > 0) { 128 if (setjmp(toplevel)) 129 exit(0); 130 (void) signal(SIGINT, intr); 131 (void) signal(SIGPIPE, lostpeer); 132 setpeer(argc + 1, argv - 1); 133 } 134 top = setjmp(toplevel) == 0; 135 if (top) { 136 (void) signal(SIGINT, intr); 137 (void) signal(SIGPIPE, lostpeer); 138 } 139 for (;;) { 140 cmdscanner(top); 141 top = 1; 142 } 143 } 144 145 intr() 146 { 147 148 longjmp(toplevel, 1); 149 } 150 151 lostpeer() 152 { 153 extern FILE *cout; 154 extern int data; 155 156 if (connected) { 157 if (cout != NULL) { 158 (void) shutdown(fileno(cout), 1+1); 159 (void) fclose(cout); 160 cout = NULL; 161 } 162 if (data >= 0) { 163 (void) shutdown(data, 1+1); 164 (void) close(data); 165 data = -1; 166 } 167 connected = 0; 168 } 169 pswitch(1); 170 if (connected) { 171 if (cout != NULL) { 172 (void) shutdown(fileno(cout), 1+1); 173 (void) fclose(cout); 174 cout = NULL; 175 } 176 connected = 0; 177 } 178 proxflag = 0; 179 pswitch(0); 180 } 181 182 /*char * 183 tail(filename) 184 char *filename; 185 { 186 register char *s; 187 188 while (*filename) { 189 s = rindex(filename, '/'); 190 if (s == NULL) 191 break; 192 if (s[1]) 193 return (s + 1); 194 *s = '\0'; 195 } 196 return (filename); 197 } 198 */ 199 /* 200 * Command parser. 201 */ 202 cmdscanner(top) 203 int top; 204 { 205 register struct cmd *c; 206 struct cmd *getcmd(); 207 extern int help(); 208 209 if (!top) 210 (void) putchar('\n'); 211 for (;;) { 212 if (fromatty) { 213 printf("ftp> "); 214 (void) fflush(stdout); 215 } 216 if (gets(line) == 0) { 217 if (feof(stdin) || ferror(stdin)) 218 quit(); 219 break; 220 } 221 if (line[0] == 0) 222 break; 223 makeargv(); 224 if (margc == 0) { 225 continue; 226 } 227 c = getcmd(margv[0]); 228 if (c == (struct cmd *)-1) { 229 printf("?Ambiguous command\n"); 230 continue; 231 } 232 if (c == 0) { 233 printf("?Invalid command\n"); 234 continue; 235 } 236 if (c->c_conn && !connected) { 237 printf ("Not connected.\n"); 238 continue; 239 } 240 (*c->c_handler)(margc, margv); 241 if (bell && c->c_bell) 242 (void) putchar(CTRL('g')); 243 if (c->c_handler != help) 244 break; 245 } 246 (void) signal(SIGINT, intr); 247 (void) signal(SIGPIPE, lostpeer); 248 } 249 250 struct cmd * 251 getcmd(name) 252 register char *name; 253 { 254 extern struct cmd cmdtab[]; 255 register char *p, *q; 256 register struct cmd *c, *found; 257 register int nmatches, longest; 258 259 longest = 0; 260 nmatches = 0; 261 found = 0; 262 for (c = cmdtab; p = c->c_name; c++) { 263 for (q = name; *q == *p++; q++) 264 if (*q == 0) /* exact match? */ 265 return (c); 266 if (!*q) { /* the name was a prefix */ 267 if (q - name > longest) { 268 longest = q - name; 269 nmatches = 1; 270 found = c; 271 } else if (q - name == longest) 272 nmatches++; 273 } 274 } 275 if (nmatches > 1) 276 return ((struct cmd *)-1); 277 return (found); 278 } 279 280 /* 281 * Slice a string up into argc/argv. 282 */ 283 284 int slrflag; 285 286 makeargv() 287 { 288 char **argp; 289 char *slurpstring(); 290 291 margc = 0; 292 argp = margv; 293 stringbase = line; /* scan from first of buffer */ 294 argbase = argbuf; /* store from first of buffer */ 295 slrflag = 0; 296 while (*argp++ = slurpstring()) 297 margc++; 298 } 299 300 /* 301 * Parse string into argbuf; 302 * implemented with FSM to 303 * handle quoting and strings 304 */ 305 char * 306 slurpstring() 307 { 308 int got_one = 0; 309 register char *sb = stringbase; 310 register char *ap = argbase; 311 char *tmp = argbase; /* will return this if token found */ 312 313 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 314 switch (slrflag) { /* and $ as token for macro invoke */ 315 case 0: 316 slrflag++; 317 stringbase++; 318 return ((*sb == '!') ? "!" : "$"); 319 break; 320 case 1: 321 slrflag++; 322 altarg = stringbase; 323 break; 324 default: 325 break; 326 } 327 } 328 329 S0: 330 switch (*sb) { 331 332 case '\0': 333 goto OUT; 334 335 case ' ': 336 case '\t': 337 sb++; goto S0; 338 339 default: 340 switch (slrflag) { 341 case 0: 342 slrflag++; 343 break; 344 case 1: 345 slrflag++; 346 altarg = sb; 347 break; 348 default: 349 break; 350 } 351 goto S1; 352 } 353 354 S1: 355 switch (*sb) { 356 357 case ' ': 358 case '\t': 359 case '\0': 360 goto OUT; /* end of token */ 361 362 case '\\': 363 sb++; goto S2; /* slurp next character */ 364 365 case '"': 366 sb++; goto S3; /* slurp quoted string */ 367 368 default: 369 *ap++ = *sb++; /* add character to token */ 370 got_one = 1; 371 goto S1; 372 } 373 374 S2: 375 switch (*sb) { 376 377 case '\0': 378 goto OUT; 379 380 default: 381 *ap++ = *sb++; 382 got_one = 1; 383 goto S1; 384 } 385 386 S3: 387 switch (*sb) { 388 389 case '\0': 390 goto OUT; 391 392 case '"': 393 sb++; goto S1; 394 395 default: 396 *ap++ = *sb++; 397 got_one = 1; 398 goto S3; 399 } 400 401 OUT: 402 if (got_one) 403 *ap++ = '\0'; 404 argbase = ap; /* update storage pointer */ 405 stringbase = sb; /* update scan pointer */ 406 if (got_one) { 407 return(tmp); 408 } 409 switch (slrflag) { 410 case 0: 411 slrflag++; 412 break; 413 case 1: 414 slrflag++; 415 altarg = (char *) 0; 416 break; 417 default: 418 break; 419 } 420 return((char *)0); 421 } 422 423 #define HELPINDENT (sizeof ("directory")) 424 425 /* 426 * Help command. 427 * Call each command handler with argc == 0 and argv[0] == name. 428 */ 429 help(argc, argv) 430 int argc; 431 char *argv[]; 432 { 433 extern struct cmd cmdtab[]; 434 register struct cmd *c; 435 436 if (argc == 1) { 437 register int i, j, w, k; 438 int columns, width = 0, lines; 439 extern int NCMDS; 440 441 printf("Commands may be abbreviated. Commands are:\n\n"); 442 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 443 int len = strlen(c->c_name); 444 445 if (len > width) 446 width = len; 447 } 448 width = (width + 8) &~ 7; 449 columns = 80 / width; 450 if (columns == 0) 451 columns = 1; 452 lines = (NCMDS + columns - 1) / columns; 453 for (i = 0; i < lines; i++) { 454 for (j = 0; j < columns; j++) { 455 c = cmdtab + j * lines + i; 456 if (c->c_name && (!proxy || c->c_proxy)) { 457 printf("%s", c->c_name); 458 } 459 else if (c->c_name) { 460 for (k=0; k < strlen(c->c_name); k++) { 461 (void) putchar(' '); 462 } 463 } 464 if (c + lines >= &cmdtab[NCMDS]) { 465 printf("\n"); 466 break; 467 } 468 w = strlen(c->c_name); 469 while (w < width) { 470 w = (w + 8) &~ 7; 471 (void) putchar('\t'); 472 } 473 } 474 } 475 return; 476 } 477 while (--argc > 0) { 478 register char *arg; 479 arg = *++argv; 480 c = getcmd(arg); 481 if (c == (struct cmd *)-1) 482 printf("?Ambiguous help command %s\n", arg); 483 else if (c == (struct cmd *)0) 484 printf("?Invalid help command %s\n", arg); 485 else 486 printf("%-*s\t%s\n", HELPINDENT, 487 c->c_name, c->c_help); 488 } 489 } 490 491 /* 492 * Call routine with argc, argv set from args (terminated by 0). 493 */ 494 /*VARARGS1*/ 495 call(routine, args) 496 int (*routine)(); 497 int args; 498 { 499 register int *argp; 500 register int argc; 501 502 for (argc = 0, argp = &args; *argp++ != 0; argc++) 503 ; 504 (*routine)(argc, &args); 505 } 506