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.1 (Berkeley) 05/31/85"; 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 39 main(argc, argv) 40 char *argv[]; 41 { 42 register char *cp; 43 int top; 44 struct passwd *pw; 45 char homedir[MAXPATHLEN]; 46 47 sp = getservbyname("ftp", "tcp"); 48 if (sp == 0) { 49 fprintf(stderr, "ftp: ftp/tcp: unknown service\n"); 50 exit(1); 51 } 52 doglob = 1; 53 interactive = 1; 54 autologin = 1; 55 argc--, argv++; 56 while (argc > 0 && **argv == '-') { 57 for (cp = *argv + 1; *cp; cp++) 58 switch (*cp) { 59 60 case 'd': 61 options |= SO_DEBUG; 62 debug++; 63 break; 64 65 case 'v': 66 verbose++; 67 break; 68 69 case 't': 70 trace++; 71 break; 72 73 case 'i': 74 interactive = 0; 75 break; 76 77 case 'n': 78 autologin = 0; 79 break; 80 81 case 'g': 82 doglob = 0; 83 break; 84 85 default: 86 fprintf(stderr, 87 "ftp: %c: unknown option\n", *cp); 88 exit(1); 89 } 90 argc--, argv++; 91 } 92 fromatty = isatty(fileno(stdin)); 93 /* 94 * Set up defaults for FTP. 95 */ 96 strcpy(typename, "ascii"), type = TYPE_A; 97 strcpy(formname, "non-print"), form = FORM_N; 98 strcpy(modename, "stream"), mode = MODE_S; 99 strcpy(structname, "file"), stru = STRU_F; 100 strcpy(bytename, "8"), bytesize = 8; 101 if (fromatty) 102 verbose++; 103 /* 104 * Set up the home directory in case we're globbing. 105 */ 106 pw = getpwnam(getlogin()); 107 if (pw == NULL) 108 pw = getpwuid(getuid()); 109 if (pw != NULL) { 110 home = homedir; 111 strcpy(home, pw->pw_dir); 112 } 113 if (argc > 0) { 114 if (setjmp(toplevel)) 115 exit(0); 116 signal(SIGINT, intr); 117 signal(SIGPIPE, lostpeer); 118 setpeer(argc + 1, argv - 1); 119 } 120 top = setjmp(toplevel) == 0; 121 if (top) { 122 signal(SIGINT, intr); 123 signal(SIGPIPE, lostpeer); 124 } 125 for (;;) { 126 cmdscanner(top); 127 top = 1; 128 } 129 } 130 131 intr() 132 { 133 134 longjmp(toplevel, 1); 135 } 136 137 lostpeer() 138 { 139 extern FILE *cout; 140 extern int data; 141 142 if (connected) { 143 if (cout != NULL) { 144 shutdown(fileno(cout), 1+1); 145 fclose(cout); 146 cout = NULL; 147 } 148 if (data >= 0) { 149 shutdown(data, 1+1); 150 (void) close(data); 151 data = -1; 152 } 153 connected = 0; 154 } 155 longjmp(toplevel, 1); 156 } 157 158 char * 159 tail(filename) 160 char *filename; 161 { 162 register char *s; 163 164 while (*filename) { 165 s = rindex(filename, '/'); 166 if (s == NULL) 167 break; 168 if (s[1]) 169 return (s + 1); 170 *s = '\0'; 171 } 172 return (filename); 173 } 174 175 /* 176 * Command parser. 177 */ 178 cmdscanner(top) 179 int top; 180 { 181 register struct cmd *c; 182 struct cmd *getcmd(); 183 extern struct cmd cmdtab[]; 184 extern int help(); 185 186 if (!top) 187 putchar('\n'); 188 for (;;) { 189 if (fromatty) { 190 printf("ftp> "); 191 fflush(stdout); 192 } 193 if (gets(line) == 0) { 194 if (feof(stdin)) { 195 if (!fromatty) 196 quit(); 197 clearerr(stdin); 198 putchar('\n'); 199 } 200 break; 201 } 202 if (line[0] == 0) 203 break; 204 makeargv(); 205 c = getcmd(margv[0]); 206 if (c == (struct cmd *)-1) { 207 printf("?Ambiguous command\n"); 208 continue; 209 } 210 if (c == 0) { 211 printf("?Invalid command\n"); 212 continue; 213 } 214 if (c->c_conn && !connected) { 215 printf ("Not connected.\n"); 216 continue; 217 } 218 (*c->c_handler)(margc, margv); 219 if (bell && c->c_bell) 220 putchar(CTRL(g)); 221 if (c->c_handler != help) 222 break; 223 } 224 longjmp(toplevel, 0); 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 (*argp++ = slurpstring()) 269 margc++; 270 } 271 272 /* 273 * Parse string into argbuf; 274 * implemented with FSM to 275 * handle quoting and strings 276 */ 277 char * 278 slurpstring() 279 { 280 int got_one = 0; 281 register char *sb = stringbase; 282 register char *ap = argbase; 283 char *tmp = argbase; /* will return this if token found */ 284 285 if (*sb == '!') { /* recognize ! as a token for shell */ 286 stringbase++; 287 return ("!"); 288 } 289 S0: 290 switch (*sb) { 291 292 case '\0': 293 goto OUT; 294 295 case ' ': 296 case '\t': 297 sb++; goto S0; 298 299 default: 300 goto S1; 301 } 302 303 S1: 304 switch (*sb) { 305 306 case ' ': 307 case '\t': 308 case '\0': 309 goto OUT; /* end of token */ 310 311 case '\\': 312 sb++; goto S2; /* slurp next character */ 313 314 case '"': 315 sb++; goto S3; /* slurp quoted string */ 316 317 default: 318 *ap++ = *sb++; /* add character to token */ 319 got_one = 1; 320 goto S1; 321 } 322 323 S2: 324 switch (*sb) { 325 326 case '\0': 327 goto OUT; 328 329 default: 330 *ap++ = *sb++; 331 got_one = 1; 332 goto S1; 333 } 334 335 S3: 336 switch (*sb) { 337 338 case '\0': 339 goto OUT; 340 341 case '"': 342 sb++; goto S1; 343 344 default: 345 *ap++ = *sb++; 346 got_one = 1; 347 goto S3; 348 } 349 350 OUT: 351 if (got_one) 352 *ap++ = '\0'; 353 argbase = ap; /* update storage pointer */ 354 stringbase = sb; /* update scan pointer */ 355 if (got_one) 356 return(tmp); 357 return((char *)0); 358 } 359 360 #define HELPINDENT (sizeof ("directory")) 361 362 /* 363 * Help command. 364 * Call each command handler with argc == 0 and argv[0] == name. 365 */ 366 help(argc, argv) 367 int argc; 368 char *argv[]; 369 { 370 register struct cmd *c; 371 372 if (argc == 1) { 373 register int i, j, w; 374 int columns, width = 0, lines; 375 extern int NCMDS; 376 377 printf("Commands may be abbreviated. Commands are:\n\n"); 378 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 379 int len = strlen(c->c_name); 380 381 if (len > width) 382 width = len; 383 } 384 width = (width + 8) &~ 7; 385 columns = 80 / width; 386 if (columns == 0) 387 columns = 1; 388 lines = (NCMDS + columns - 1) / columns; 389 for (i = 0; i < lines; i++) { 390 for (j = 0; j < columns; j++) { 391 c = cmdtab + j * lines + i; 392 printf("%s", c->c_name); 393 if (c + lines >= &cmdtab[NCMDS]) { 394 printf("\n"); 395 break; 396 } 397 w = strlen(c->c_name); 398 while (w < width) { 399 w = (w + 8) &~ 7; 400 putchar('\t'); 401 } 402 } 403 } 404 return; 405 } 406 while (--argc > 0) { 407 register char *arg; 408 arg = *++argv; 409 c = getcmd(arg); 410 if (c == (struct cmd *)-1) 411 printf("?Ambiguous help command %s\n", arg); 412 else if (c == (struct cmd *)0) 413 printf("?Invalid help command %s\n", arg); 414 else 415 printf("%-*s\t%s\n", HELPINDENT, 416 c->c_name, c->c_help); 417 } 418 } 419 420 /* 421 * Call routine with argc, argv set from args (terminated by 0). 422 */ 423 /* VARARGS2 */ 424 call(routine, args) 425 int (*routine)(); 426 int args; 427 { 428 register int *argp; 429 register int argc; 430 431 for (argc = 0, argp = &args; *argp++ != 0; argc++) 432 ; 433 (*routine)(argc, &args); 434 } 435