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