-> Clean up the use of autoconf macros a bit.
[tftp-hpa.git] / tftp / main.c
blob7f383fa6992d0caa7be8f465c7ea3800ea2e65a8
1 /* $OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert Exp $ */
2 /* $NetBSD: main.c,v 1.6 1995/05/21 16:54:10 mycroft Exp $ */
4 /*
5 * Copyright (c) 1983, 1993
6 * The Regents of the University of California. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
37 #include "tftpsubs.h"
39 #ifndef lint
40 static const char *copyright UNUSED =
41 "@(#) Copyright (c) 1983, 1993\n\
42 The Regents of the University of California. All rights reserved.\n";
43 /* static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; */
44 /* static char rcsid[] = "$OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert Exp $"; */
45 static const char *rcsid UNUSED =
46 "tftp-hpa $Id$";
47 #endif /* not lint */
49 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
52 * TFTP User Program -- Command Interface.
54 #include <sys/file.h>
55 #include <ctype.h>
56 #include <netdb.h>
57 #ifdef WITH_READLINE
58 #include <readline/readline.h>
59 #ifdef HAVE_READLINE_HISTORY_H
60 #include <readline/history.h>
61 #endif
62 #endif
64 #include "extern.h"
66 #define TIMEOUT 5 /* secs between rexmt's */
67 #define LBUFLEN 200 /* size of input buffer */
69 struct modes {
70 const char *m_name;
71 const char *m_mode;
72 int m_openflags;
75 static const struct modes modes[] = {
76 { "netascii", "netascii", O_TEXT },
77 { "ascii", "netascii", O_TEXT },
78 { "octet", "octet", O_BINARY },
79 { "binary", "octet", O_BINARY },
80 { "image", "octet", O_BINARY },
81 { 0, 0, 0 }
83 #define MODE_OCTET (&modes[2])
84 #define MODE_NETASCII (&modes[0])
85 #define MODE_DEFAULT MODE_NETASCII
87 struct sockaddr_in peeraddr;
88 int f;
89 u_short port;
90 int trace;
91 int verbose;
92 int connected;
93 const struct modes *mode;
94 #ifdef WITH_READLINE
95 char *line = NULL;
96 #else
97 char line[LBUFLEN];
98 #endif
99 int margc;
100 char *margv[20];
101 const char *prompt = "tftp> ";
102 sigjmp_buf toplevel;
103 void intr(int);
104 struct servent *sp;
106 void get (int, char **);
107 void help (int, char **);
108 void modecmd (int, char **);
109 void put (int, char **);
110 void quit (int, char **);
111 void setascii (int, char **);
112 void setbinary (int, char **);
113 void setpeer (int, char **);
114 void setrexmt (int, char **);
115 void settimeout (int, char **);
116 void settrace (int, char **);
117 void setverbose (int, char **);
118 void status (int, char **);
120 static void command (void);
122 static void getusage (char *);
123 static void makeargv (void);
124 static void putusage (char *);
125 static void settftpmode (const struct modes *);
127 #define HELPINDENT (sizeof("connect"))
129 struct cmd {
130 const char *name;
131 const char *help;
132 void (*handler) (int, char **);
135 struct cmd cmdtab[] = {
136 { "connect",
137 "connect to remote tftp",
138 setpeer },
139 { "mode",
140 "set file transfer mode",
141 modecmd },
142 { "put",
143 "send file",
144 put },
145 { "get",
146 "receive file",
147 get },
148 { "quit",
149 "exit tftp",
150 quit },
151 { "verbose",
152 "toggle verbose mode",
153 setverbose },
154 { "trace",
155 "toggle packet tracing",
156 settrace },
157 { "status",
158 "show current status",
159 status },
160 { "binary",
161 "set mode to octet",
162 setbinary },
163 { "ascii",
164 "set mode to netascii",
165 setascii },
166 { "rexmt",
167 "set per-packet transmission timeout",
168 setrexmt },
169 { "timeout",
170 "set total retransmission timeout",
171 settimeout },
172 { "?",
173 "print help information",
174 help },
175 { "help",
176 "print help information",
177 help },
178 { 0, 0, 0 }
181 struct cmd *getcmd(char *);
182 char *tail(char *);
184 char *xstrdup(const char *);
187 main(int argc, char *argv[])
189 struct sockaddr_in s_in;
190 int c;
191 int pargc;
192 char **pargv;
194 while ((c = getopt(argc, argv, "Vv")) != -1) {
195 switch (c) {
196 case 'v':
197 verbose = 1;
198 break;
199 case 'V':
200 /* Print version and configuration to stdout and exit */
201 printf("%s\n", TFTP_CONFIG_STR);
202 exit(0);
203 default:
204 fprintf(stderr, "Usage: %s [-v] [host]\n", argv[0]);
205 exit(EX_USAGE);
209 pargc = argc - (optind-1);
210 pargv = argv + (optind-1);
212 sp = getservbyname("tftp", "udp");
213 if (sp == 0) {
214 /* Use canned values */
215 fprintf(stderr, "tftp: tftp/udp: unknown service, faking it...\n");
216 sp = xmalloc(sizeof(struct servent));
217 sp->s_name = (char *)"tftp";
218 sp->s_aliases = NULL;
219 sp->s_port = htons(IPPORT_TFTP);
220 sp->s_proto = (char *)"udp";
221 exit(1);
223 f = socket(AF_INET, SOCK_DGRAM, 0);
224 if (f < 0) {
225 perror("tftp: socket");
226 exit(3);
228 bzero((char *)&s_in, sizeof (s_in));
229 s_in.sin_family = AF_INET;
230 if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) {
231 perror("tftp: bind");
232 exit(1);
234 mode = MODE_DEFAULT;
235 bsd_signal(SIGINT, intr);
236 if (pargc > 1) {
237 if (sigsetjmp(toplevel,1) != 0)
238 exit(0);
239 setpeer(pargc, pargv);
241 if (sigsetjmp(toplevel,1) != 0)
242 (void)putchar('\n');
244 #ifdef WITH_READLINE
245 #ifdef HAVE_READLINE_HISTORY_H
246 using_history();
247 #endif
248 #endif
250 command();
252 return 0; /* Never reached */
255 char *hostname;
257 /* Called when a command is incomplete; modifies
258 the global variable "line" */
259 static void
260 getmoreargs(const char *partial, const char *mprompt)
262 #ifdef WITH_READLINE
263 char *eline;
264 int len, elen;
266 len = strlen(partial);
267 eline = readline(mprompt);
268 if (!eline)
269 exit(0); /* EOF */
271 elen = strlen(eline);
273 if (line)
274 free(line);
275 line = xmalloc(len+elen+1);
276 strcpy(line, partial);
277 strcpy(line+len, eline);
278 free(eline);
280 #ifdef HAVE_READLINE_HISTORY_H
281 add_history(line);
282 #endif
283 #else
284 int len = strlen(partial);
286 strcpy(line, partial);
287 fputs(mprompt, stdout);
288 if ( fgets(line+len, LBUFLEN-len, stdin) == 0 )
289 if ( feof(stdin) )
290 exit(0); /* EOF */
291 #endif
294 void
295 setpeer(int argc, char *argv[])
297 struct hostent *host;
299 if (argc < 2) {
300 getmoreargs("connect ", "(to) ");
301 makeargv();
302 argc = margc;
303 argv = margv;
305 if ((argc < 2) || (argc > 3)) {
306 printf("usage: %s host-name [port]\n", argv[0]);
307 return;
310 host = gethostbyname(argv[1]);
311 if (host == 0) {
312 connected = 0;
313 printf("%s: unknown host\n", argv[1]);
314 return;
316 peeraddr.sin_family = host->h_addrtype;
317 bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length);
318 hostname = xstrdup(host->h_name);
320 port = sp->s_port;
321 if (argc == 3) {
322 struct servent *usp;
323 usp = getservbyname(argv[2], "udp");
324 if ( usp ) {
325 port = usp->s_port;
326 } else {
327 unsigned long myport;
328 char *ep;
329 myport = strtoul(argv[2], &ep, 10);
330 if (*ep || myport > 65535UL) {
331 printf("%s: bad port number\n", argv[2]);
332 connected = 0;
333 return;
335 port = htons((u_short)myport);
339 if (verbose) {
340 printf("Connected to %s (%s), port %u\n",
341 hostname, inet_ntoa(peeraddr.sin_addr),
342 (unsigned int)ntohs(port));
344 connected = 1;
347 void
348 modecmd(int argc, char *argv[])
350 const struct modes *p;
351 const char *sep;
353 if (argc < 2) {
354 printf("Using %s mode to transfer files.\n", mode->m_mode);
355 return;
357 if (argc == 2) {
358 for (p = modes; p->m_name; p++)
359 if (strcmp(argv[1], p->m_name) == 0)
360 break;
361 if (p->m_name) {
362 settftpmode(p);
363 return;
365 printf("%s: unknown mode\n", argv[1]);
366 /* drop through and print usage message */
369 printf("usage: %s [", argv[0]);
370 sep = " ";
371 for (p = modes; p->m_name; p++) {
372 printf("%s%s", sep, p->m_name);
373 if (*sep == ' ')
374 sep = " | ";
376 printf(" ]\n");
377 return;
380 void
381 setbinary(int argc, char *argv[])
383 (void)argc; (void)argv; /* Quiet unused warning */
384 settftpmode(MODE_OCTET);
387 void
388 setascii(int argc, char *argv[])
390 (void)argc; (void)argv; /* Quiet unused warning */
391 settftpmode(MODE_NETASCII);
394 static void
395 settftpmode(const struct modes *newmode)
397 mode = newmode;
398 if (verbose)
399 printf("mode set to %s\n", mode->m_mode);
404 * Send file(s).
406 void
407 put(int argc, char *argv[])
409 int fd;
410 int n;
411 char *cp, *targ;
413 if (argc < 2) {
414 getmoreargs("send ", "(file) ");
415 makeargv();
416 argc = margc;
417 argv = margv;
419 if (argc < 2) {
420 putusage(argv[0]);
421 return;
423 targ = argv[argc - 1];
424 if (strchr(argv[argc - 1], ':')) {
425 struct hostent *hp;
427 for (n = 1; n < argc - 1; n++)
428 if (strchr(argv[n], ':')) {
429 putusage(argv[0]);
430 return;
432 cp = argv[argc - 1];
433 targ = strchr(cp, ':');
434 *targ++ = 0;
435 hp = gethostbyname(cp);
436 if (hp == NULL) {
437 fprintf(stderr, "tftp: %s: ", cp);
438 herror((char *)NULL);
439 return;
441 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length);
442 peeraddr.sin_family = hp->h_addrtype;
443 connected = 1;
444 hostname = xstrdup(hp->h_name);
446 if (!connected) {
447 printf("No target machine specified.\n");
448 return;
450 if (argc < 4) {
451 cp = argc == 2 ? tail(targ) : argv[1];
452 fd = open(cp, O_RDONLY|mode->m_openflags);
453 if (fd < 0) {
454 fprintf(stderr, "tftp: "); perror(cp);
455 return;
457 if (verbose)
458 printf("putting %s to %s:%s [%s]\n",
459 cp, hostname, targ, mode->m_mode);
460 peeraddr.sin_port = port;
461 tftp_sendfile(fd, targ, mode->m_mode);
462 return;
464 /* this assumes the target is a directory */
465 /* on a remote unix system. hmmmm. */
466 cp = strchr(targ, '\0');
467 *cp++ = '/';
468 for (n = 1; n < argc - 1; n++) {
469 strcpy(cp, tail(argv[n]));
470 fd = open(argv[n], O_RDONLY|mode->m_openflags);
471 if (fd < 0) {
472 fprintf(stderr, "tftp: "); perror(argv[n]);
473 continue;
475 if (verbose)
476 printf("putting %s to %s:%s [%s]\n",
477 argv[n], hostname, targ, mode->m_mode);
478 peeraddr.sin_port = port;
479 tftp_sendfile(fd, targ, mode->m_mode);
483 static void
484 putusage(char *s)
486 printf("usage: %s file ... host:target, or\n", s);
487 printf(" %s file ... target (when already connected)\n", s);
491 * Receive file(s).
493 void
494 get(int argc, char *argv[])
496 int fd;
497 int n;
498 char *cp;
499 char *src;
501 if (argc < 2) {
502 getmoreargs("get ", "(files) ");
503 makeargv();
504 argc = margc;
505 argv = margv;
507 if (argc < 2) {
508 getusage(argv[0]);
509 return;
511 if (!connected) {
512 for (n = 1; n < argc ; n++)
513 if (strchr(argv[n], ':') == 0) {
514 getusage(argv[0]);
515 return;
518 for (n = 1; n < argc ; n++) {
519 src = strchr(argv[n], ':');
520 if (src == NULL)
521 src = argv[n];
522 else {
523 struct hostent *hp;
525 *src++ = 0;
526 hp = gethostbyname(argv[n]);
527 if (hp == NULL) {
528 fprintf(stderr, "tftp: %s: ", argv[n]);
529 herror((char *)NULL);
530 continue;
532 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr,
533 hp->h_length);
534 peeraddr.sin_family = hp->h_addrtype;
535 connected = 1;
536 hostname = xstrdup(hp->h_name);
538 if (argc < 4) {
539 cp = argc == 3 ? argv[2] : tail(src);
540 fd = open(cp, O_WRONLY|O_CREAT|O_TRUNC|mode->m_openflags, 0666);
541 if (fd < 0) {
542 fprintf(stderr, "tftp: "); perror(cp);
543 return;
545 if (verbose)
546 printf("getting from %s:%s to %s [%s]\n",
547 hostname, src, cp, mode->m_mode);
548 peeraddr.sin_port = port;
549 tftp_recvfile(fd, src, mode->m_mode);
550 break;
552 cp = tail(src); /* new .. jdg */
553 fd = open(cp, O_WRONLY|O_CREAT|O_TRUNC|mode->m_openflags, 0666);
554 if (fd < 0) {
555 fprintf(stderr, "tftp: "); perror(cp);
556 continue;
558 if (verbose)
559 printf("getting from %s:%s to %s [%s]\n",
560 hostname, src, cp, mode->m_mode);
561 peeraddr.sin_port = port;
562 tftp_recvfile(fd, src, mode->m_mode);
566 static void
567 getusage(char *s)
569 printf("usage: %s host:file host:file ... file, or\n", s);
570 printf(" %s file file ... file if connected\n", s);
573 int rexmtval = TIMEOUT;
575 void
576 setrexmt(int argc, char *argv[])
578 int t;
580 if (argc < 2) {
581 getmoreargs("rexmt-timeout ", "(value) ");
582 makeargv();
583 argc = margc;
584 argv = margv;
586 if (argc != 2) {
587 printf("usage: %s value\n", argv[0]);
588 return;
590 t = atoi(argv[1]);
591 if (t < 0)
592 printf("%s: bad value\n", argv[1]);
593 else
594 rexmtval = t;
597 int maxtimeout = 5 * TIMEOUT;
599 void
600 settimeout(int argc, char *argv[])
602 int t;
604 if (argc < 2) {
605 getmoreargs("maximum-timeout ", "(value) ");
606 makeargv();
607 argc = margc;
608 argv = margv;
610 if (argc != 2) {
611 printf("usage: %s value\n", argv[0]);
612 return;
614 t = atoi(argv[1]);
615 if (t < 0)
616 printf("%s: bad value\n", argv[1]);
617 else
618 maxtimeout = t;
621 void
622 status(int argc, char *argv[])
624 (void)argc; (void)argv; /* Quiet unused warning */
625 if (connected)
626 printf("Connected to %s.\n", hostname);
627 else
628 printf("Not connected.\n");
629 printf("Mode: %s Verbose: %s Tracing: %s\n", mode->m_mode,
630 verbose ? "on" : "off", trace ? "on" : "off");
631 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
632 rexmtval, maxtimeout);
635 void
636 intr(int sig)
638 (void)sig; /* Quiet unused warning */
640 bsd_signal(SIGALRM, SIG_IGN);
641 alarm(0);
642 siglongjmp(toplevel, -1);
645 char *
646 tail(char *filename)
648 char *s;
650 while (*filename) {
651 s = strrchr(filename, '/');
652 if (s == NULL)
653 break;
654 if (s[1])
655 return (s + 1);
656 *s = '\0';
658 return (filename);
662 * Command parser.
664 static void
665 command(void)
667 struct cmd *c;
669 for (;;) {
670 #ifdef WITH_READLINE
671 if ( line )
672 free(line);
673 line = readline(prompt);
674 if ( !line )
675 exit(0); /* EOF */
676 #else
677 fputs(prompt, stdout);
678 if (fgets(line, LBUFLEN, stdin) == 0) {
679 if (feof(stdin)) {
680 exit(0);
681 } else {
682 continue;
685 #endif
686 if ((line[0] == 0) || (line[0] == '\n'))
687 continue;
688 #ifdef WITH_READLINE
689 #ifdef HAVE_READLINE_HISTORY_H
690 add_history(line);
691 #endif
692 #endif
693 makeargv();
694 if (margc == 0)
695 continue;
697 c = getcmd(margv[0]);
698 if (c == (struct cmd *)-1) {
699 printf("?Ambiguous command\n");
700 continue;
702 if (c == 0) {
703 printf("?Invalid command\n");
704 continue;
706 (*c->handler)(margc, margv);
710 struct cmd *
711 getcmd(char *name)
713 const char *p;
714 char *q;
715 struct cmd *c, *found;
716 int nmatches, longest;
718 longest = 0;
719 nmatches = 0;
720 found = 0;
721 for (c = cmdtab; (p = c->name) != NULL; c++) {
722 for (q = name; *q == *p++; q++)
723 if (*q == 0) /* exact match? */
724 return (c);
725 if (!*q) { /* the name was a prefix */
726 if (q - name > longest) {
727 longest = q - name;
728 nmatches = 1;
729 found = c;
730 } else if (q - name == longest)
731 nmatches++;
734 if (nmatches > 1)
735 return ((struct cmd *)-1);
736 return (found);
740 * Slice a string up into argc/argv.
742 static void
743 makeargv(void)
745 char *cp;
746 char **argp = margv;
748 margc = 0;
749 for (cp = line; *cp;) {
750 while (isspace(*cp))
751 cp++;
752 if (*cp == '\0')
753 break;
754 *argp++ = cp;
755 margc += 1;
756 while (*cp != '\0' && !isspace(*cp))
757 cp++;
758 if (*cp == '\0')
759 break;
760 *cp++ = '\0';
762 *argp++ = 0;
765 void
766 quit(int argc, char *argv[])
768 (void)argc; (void)argv; /* Quiet unused warning */
769 exit(0);
773 * Help command.
775 void
776 help(int argc, char *argv[])
778 struct cmd *c;
780 printf("%s\n", VERSION);
782 if (argc == 1) {
783 printf("Commands may be abbreviated. Commands are:\n\n");
784 for (c = cmdtab; c->name; c++)
785 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
786 return;
788 while (--argc > 0) {
789 char *arg;
790 arg = *++argv;
791 c = getcmd(arg);
792 if (c == (struct cmd *)-1)
793 printf("?Ambiguous help command %s\n", arg);
794 else if (c == (struct cmd *)0)
795 printf("?Invalid help command %s\n", arg);
796 else
797 printf("%s\n", c->help);
801 void
802 settrace(int argc, char *argv[])
804 (void)argc; (void)argv; /* Quiet unused warning */
806 trace = !trace;
807 printf("Packet tracing %s.\n", trace ? "on" : "off");
810 void
811 setverbose(int argc, char *argv[])
813 (void)argc; (void)argv; /* Quiet unused warning */
815 verbose = !verbose;
816 printf("Verbose mode %s.\n", verbose ? "on" : "off");