xref: /original-bsd/usr.bin/tftp/main.c (revision f0fd5f8a)
1 /*	main.c	4.4	82/11/15	*/
2 
3 /*
4  * TFTP User Program -- Command Interface.
5  */
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 
9 #include <netinet/in.h>
10 
11 #include <signal.h>
12 #include <stdio.h>
13 #include <errno.h>
14 #include <setjmp.h>
15 #include <ctype.h>
16 #include <netdb.h>
17 
18 struct	sockaddr_in sin;
19 int	f;
20 int	trace;
21 int	verbose;
22 int	connected;
23 char	mode[32];
24 char	line[200];
25 int	margc;
26 char	*margv[20];
27 char	*prompt = "tftp";
28 jmp_buf	toplevel;
29 int	intr();
30 struct	servent *sp;
31 
32 int	quit(), help(), setverbose(), settrace(), status();
33 int	get(), put(), setpeer(), setmode();
34 
35 #define HELPINDENT (sizeof("connect"))
36 
37 struct cmd {
38 	char	*name;
39 	char	*help;
40 	int	(*handler)();
41 };
42 
43 char	vhelp[] = "toggle verbose mode";
44 char	thelp[] = "toggle packet tracing";
45 char	chelp[] = "connect to remote tftp";
46 char	qhelp[] = "exit tftp";
47 char	hhelp[] = "print help information";
48 char	shelp[] = "send file";
49 char	rhelp[] = "receive file";
50 char	mhelp[] = "set file transfer mode";
51 char	sthelp[] = "show current status";
52 
53 struct cmd cmdtab[] = {
54 	{ "connect",	chelp,		setpeer },
55 	{ "mode",	mhelp,		setmode },
56 	{ "put",	shelp,		put },
57 	{ "get",	rhelp,		get },
58 	{ "quit",	qhelp,		quit },
59 	{ "verbose",	vhelp,		setverbose },
60 	{ "trace",	thelp,		settrace },
61 	{ "status",	sthelp,		status },
62 	{ "?",		hhelp,		help },
63 	0
64 };
65 
66 struct	cmd *getcmd();
67 char	*tail();
68 char	*index();
69 char	*rindex();
70 
71 main(argc, argv)
72 	char *argv[];
73 {
74 	sp = getservbyname("tftp", "udp");
75 	if (sp == 0) {
76 		fprintf(stderr, "tftp: udp/tftp: unknown service\n");
77 		exit(1);
78 	}
79 	f = socket(AF_INET, SOCK_DGRAM, 0, 0);
80 	if (f < 0) {
81 		perror("socket");
82 		exit(3);
83 	}
84 	strcpy(mode, "netascii");
85 	if (argc > 1) {
86 		if (setjmp(toplevel) != 0)
87 			exit(0);
88 		setpeer(argc, argv);
89 	}
90 	setjmp(toplevel);
91 	for (;;)
92 		command(1);
93 }
94 
95 char	*hostname;
96 char	hnamebuf[32];
97 
98 setpeer(argc, argv)
99 	int argc;
100 	char *argv[];
101 {
102 	register int c;
103 	struct hostent *host;
104 
105 	if (argc < 2) {
106 		strcpy(line, "Connect ");
107 		printf("(to) ");
108 		gets(&line[strlen(line)]);
109 		makeargv();
110 		argc = margc;
111 		argv = margv;
112 	}
113 	if (argc > 3) {
114 		printf("usage: %s host-name [port]\n", argv[0]);
115 		return;
116 	}
117 	host = gethostbyname(argv[1]);
118 	if (host) {
119 		sin.sin_family = host->h_addrtype;
120 		bcopy(host->h_addr, &sin.sin_addr, host->h_length);
121 		hostname = host->h_name;
122 	} else {
123 		sin.sin_family = AF_INET;
124 		sin.sin_addr.s_addr = inet_addr(argv[1]);
125 		if (sin.sin_addr.s_addr == -1) {
126 			connected = 0;
127 			printf("%s: unknown host\n", argv[1]);
128 			return;
129 		}
130 		strcpy(hnamebuf, argv[1]);
131 		hostname = hnamebuf;
132 	}
133 	sin.sin_port = sp->s_port;
134 	if (argc == 3) {
135 		sin.sin_port = atoi(argv[2]);
136 		if (sin.sin_port < 0) {
137 			printf("%s: bad port number\n", argv[2]);
138 			connected = 0;
139 			return;
140 		}
141 	}
142 	sin.sin_port = htons((u_short)sin.sin_port);
143 	connected = 1;
144 }
145 
146 struct	modes {
147 	char *m_name;
148 	char *m_mode;
149 } modes[] = {
150 	{ "ascii",	"netascii" },
151 	{ "binary",	"octect" },
152 	{ "mail",	"mail" },
153 	{ 0,		0 }
154 };
155 
156 setmode(argc, argv)
157 	char *argv[];
158 {
159 	register struct modes *p;
160 
161 	if (argc > 2) {
162 		char *sep;
163 
164 		printf("usage: %s [", argv[0]);
165 		sep = " ";
166 		for (p = modes; p->m_name; p++) {
167 			printf("%s%s", sep, p->m_name);
168 			if (*sep == ' ')
169 				sep = " | ";
170 		}
171 		printf(" ]\n");
172 		return;
173 	}
174 	if (argc < 2) {
175 		printf("Using %s mode to transfer files.\n", mode);
176 		return;
177 	}
178 	for (p = modes; p->m_name; p++)
179 		if (strcmp(argv[1], p->m_name) == 0)
180 			break;
181 	if (p->m_name)
182 		strcpy(mode, p->m_mode);
183 	else
184 		printf("%s: unknown mode\n", argv[1]);
185 }
186 
187 /*
188  * Send file(s).
189  */
190 put(argc, argv)
191 	char *argv[];
192 {
193 	int fd;
194 	register int n, addr;
195 	register char *cp, *targ;
196 
197 	if (argc < 2) {
198 		strcpy(line, "send ");
199 		printf("(file) ");
200 		gets(&line[strlen(line)]);
201 		makeargv();
202 		argc = margc;
203 		argv = margv;
204 	}
205 	if (argc < 2) {
206 		putusage(argv[0]);
207 		return;
208 	}
209 	targ = argv[argc - 1];
210 	if (index(argv[argc - 1], ':')) {
211 		char *cp;
212 		struct hostent *hp;
213 
214 		for (n = 1; n < argc - 1; n++)
215 			if (index(argv[n], ':')) {
216 				putusage(argv[0]);
217 				return;
218 			}
219 		cp = argv[argc - 1];
220 		targ = index(cp, ':');
221 		*targ++ = 0;
222 		hp = gethostbyname(cp);
223 		if (hp == 0) {
224 			printf("%s: Unknown host.\n", cp);
225 			return;
226 		}
227 		bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length);
228 		sin.sin_family = hp->h_addrtype;
229 		connected = 1;
230 		hostname = hp->h_name;
231 	}
232 	if (!connected) {
233 		printf("No target machine specified.\n");
234 		return;
235 	}
236 	sigset(SIGINT, intr);
237 	if (argc < 4) {
238 		cp = argc == 2 ? tail(targ) : argv[1];
239 		fd = open(cp);
240 		if (fd < 0) {
241 			perror(cp);
242 			return;
243 		}
244 		sendfile(fd, targ);
245 		return;
246 	}
247 	cp = index(targ, '\0');
248 	*cp++ = '/';
249 	for (n = 1; n < argc - 1; n++) {
250 		strcpy(cp, tail(argv[n]));
251 		fd = open(argv[n], 0);
252 		if (fd < 0) {
253 			perror(argv[n]);
254 			continue;
255 		}
256 		sendfile(fd, targ);
257 	}
258 }
259 
260 putusage(s)
261 	char *s;
262 {
263 	printf("usage: %s file ... host:target, or\n", s);
264 	printf("       %s file ... target (when already connected)\n", s);
265 }
266 
267 /*
268  * Receive file(s).
269  */
270 get(argc, argv)
271 	char *argv[];
272 {
273 	int fd;
274 	register int n, addr;
275 	register char *cp;
276 	char *src;
277 
278 	if (argc < 2) {
279 		strcpy(line, "get ");
280 		printf("(files) ");
281 		gets(&line[strlen(line)]);
282 		makeargv();
283 		argc = margc;
284 		argv = margv;
285 	}
286 	if (argc < 2) {
287 		getusage(argv[0]);
288 		return;
289 	}
290 	if (!connected)
291 		for (n = 1; n < argc - 1; n++)
292 			if (index(argv[n], ':') == 0) {
293 				getusage(argv[0]);
294 				return;
295 			}
296 	sigset(SIGINT, intr);
297 	for (n = 1; argc == 2 || n < argc - 1; n++) {
298 		src = index(argv[n], ':');
299 		if (src == NULL)
300 			src = argv[n];
301 		else {
302 			struct hostent *hp;
303 
304 			*src++ = 0;
305 			hp = gethostbyname(argv[n]);
306 			if (hp == 0) {
307 				printf("%s: Unknown host.\n", argv[n]);
308 				continue;
309 			}
310 			bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length);
311 			sin.sin_family = hp->h_addrtype;
312 			connected = 1;
313 			hostname = hp->h_name;
314 		}
315 		if (argc < 4) {
316 			cp = argc == 3 ? argv[2] : tail(src);
317 			fd = creat(cp, 0644);
318 			if (fd < 0) {
319 				perror(cp);
320 				return;
321 			}
322 			recvfile(fd, src);
323 			break;
324 		}
325 		cp = index(argv[argc - 1], '\0');
326 		*cp++ = '/';
327 		strcpy(cp, tail(src));
328 		fd = creat(src, 0644);
329 		if (fd < 0) {
330 			perror(src);
331 			continue;
332 		}
333 		recvfile(fd, src);
334 	}
335 }
336 
337 getusage(s)
338 {
339 	printf("usage: %s host:file host:file ... file, or\n", s);
340 	printf("       %s file file ... file if connected\n", s);
341 }
342 
343 status(argc, argv)
344 	char *argv[];
345 {
346 	if (connected)
347 		printf("Connected to %s.\n", hostname);
348 	else
349 		printf("Not connected.\n");
350 	printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
351 		verbose ? "on" : "off", trace ? "on" : "off");
352 }
353 
354 intr()
355 {
356 	longjmp(toplevel, -1);
357 }
358 
359 char *
360 tail(filename)
361 	char *filename;
362 {
363 	register char *s;
364 
365 	while (*filename) {
366 		s = rindex(filename, '/');
367 		if (s == NULL)
368 			break;
369 		if (s[1])
370 			return (s + 1);
371 		*s = '\0';
372 	}
373 	return (filename);
374 }
375 
376 /*
377  * Command parser.
378  */
379 command(top)
380 	int top;
381 {
382 	register struct cmd *c;
383 
384 	if (!top)
385 		putchar('\n');
386 	else
387 		sigset(SIGINT, SIG_DFL);
388 	for (;;) {
389 		printf("%s> ", prompt);
390 		if (gets(line) == 0)
391 			break;
392 		if (line[0] == 0)
393 			break;
394 		makeargv();
395 		c = getcmd(margv[0]);
396 		if (c == (struct cmd *)-1) {
397 			printf("?Ambiguous command\n");
398 			continue;
399 		}
400 		if (c == 0) {
401 			printf("?Invalid command\n");
402 			continue;
403 		}
404 		(*c->handler)(margc, margv);
405 		if (c->handler != help)
406 			break;
407 	}
408 	longjmp(toplevel, 1);
409 }
410 
411 struct cmd *
412 getcmd(name)
413 	register char *name;
414 {
415 	register char *p, *q;
416 	register struct cmd *c, *found;
417 	register int nmatches, longest;
418 
419 	longest = 0;
420 	nmatches = 0;
421 	found = 0;
422 	for (c = cmdtab; p = c->name; c++) {
423 		for (q = name; *q == *p++; q++)
424 			if (*q == 0)		/* exact match? */
425 				return (c);
426 		if (!*q) {			/* the name was a prefix */
427 			if (q - name > longest) {
428 				longest = q - name;
429 				nmatches = 1;
430 				found = c;
431 			} else if (q - name == longest)
432 				nmatches++;
433 		}
434 	}
435 	if (nmatches > 1)
436 		return ((struct cmd *)-1);
437 	return (found);
438 }
439 
440 /*
441  * Slice a string up into argc/argv.
442  */
443 makeargv()
444 {
445 	register char *cp;
446 	register char **argp = margv;
447 
448 	margc = 0;
449 	for (cp = line; *cp;) {
450 		while (isspace(*cp))
451 			cp++;
452 		if (*cp == '\0')
453 			break;
454 		*argp++ = cp;
455 		margc += 1;
456 		while (*cp != '\0' && !isspace(*cp))
457 			cp++;
458 		if (*cp == '\0')
459 			break;
460 		*cp++ = '\0';
461 	}
462 	*argp++ = 0;
463 }
464 
465 /*VARARGS*/
466 quit()
467 {
468 	exit(0);
469 }
470 
471 /*
472  * Help command.
473  */
474 help(argc, argv)
475 	int argc;
476 	char *argv[];
477 {
478 	register struct cmd *c;
479 
480 	if (argc == 1) {
481 		printf("Commands may be abbreviated.  Commands are:\n\n");
482 		for (c = cmdtab; c->name; c++)
483 			printf("%-*s\t%s\n", HELPINDENT, c->name, c->help);
484 		return;
485 	}
486 	while (--argc > 0) {
487 		register char *arg;
488 		arg = *++argv;
489 		c = getcmd(arg);
490 		if (c == (struct cmd *)-1)
491 			printf("?Ambiguous help command %s\n", arg);
492 		else if (c == (struct cmd *)0)
493 			printf("?Invalid help command %s\n", arg);
494 		else
495 			printf("%s\n", c->help);
496 	}
497 }
498 
499 /*
500  * Call routine with argc, argv set from args (terminated by 0).
501  */
502 /* VARARGS2 */
503 call(routine, args)
504 	int (*routine)();
505 	int args;
506 {
507 	register int *argp;
508 	register int argc;
509 
510 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
511 		;
512 	(*routine)(argc, &args);
513 }
514 
515 /*VARARGS*/
516 settrace()
517 {
518 	trace = !trace;
519 	printf("Packet tracing %s.\n", trace ? "on" : "off");
520 }
521 
522 /*VARARGS*/
523 setverbose()
524 {
525 	verbose = !verbose;
526 	printf("Verbose mode %s.\n", verbose ? "on" : "off");
527 }
528