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