remap: *actually* build, and fix masked logic errors
[tftp-hpa.git] / tftp / main.c
blobac063305156d3e71de072c8b3c5fb4b00ffa93fb
1 /*
2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include "common/tftpsubs.h"
36 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
39 * TFTP User Program -- Command Interface.
41 #include <sys/file.h>
42 #include <ctype.h>
43 #ifdef WITH_READLINE
44 #include <readline/readline.h>
45 #ifdef HAVE_READLINE_HISTORY_H
46 #include <readline/history.h>
47 #endif
48 #endif
50 #include "extern.h"
52 #define TIMEOUT 5 /* secs between rexmt's */
53 #define LBUFLEN 200 /* size of input buffer */
55 struct modes {
56 const char *m_name;
57 const char *m_mode;
58 int m_openflags;
61 static const struct modes modes[] = {
62 {"netascii", "netascii", O_TEXT},
63 {"ascii", "netascii", O_TEXT},
64 {"octet", "octet", O_BINARY},
65 {"binary", "octet", O_BINARY},
66 {"image", "octet", O_BINARY},
67 {0, 0, 0}
70 #define MODE_OCTET (&modes[2])
71 #define MODE_NETASCII (&modes[0])
72 #define MODE_DEFAULT MODE_NETASCII
74 #ifdef HAVE_IPV6
75 int ai_fam = AF_UNSPEC;
76 int ai_fam_sock = AF_UNSPEC;
77 #else
78 int ai_fam = AF_INET;
79 int ai_fam_sock = AF_INET;
80 #endif
82 union sock_addr peeraddr;
83 int f = -1;
84 u_short port;
85 int trace;
86 int verbose;
87 int literal;
88 int connected;
89 const struct modes *mode;
90 #ifdef WITH_READLINE
91 char *line = NULL;
92 #else
93 char line[LBUFLEN];
94 #endif
95 int margc;
96 char *margv[20];
97 const char *prompt = "tftp> ";
98 sigjmp_buf toplevel;
99 void intr(int);
100 struct servent *sp;
101 int portrange = 0;
102 unsigned int portrange_from = 0;
103 unsigned int portrange_to = 0;
105 void get(int, char **);
106 void help(int, char **);
107 void modecmd(int, char **);
108 void put(int, char **);
109 void quit(int, char **);
110 void setascii(int, char **);
111 void setbinary(int, char **);
112 void setpeer(int, char **);
113 void setrexmt(int, char **);
114 void settimeout(int, char **);
115 void settrace(int, char **);
116 void setverbose(int, char **);
117 void status(int, char **);
118 void setliteral(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 {"literal",
158 "toggle literal mode, ignore ':' in file name",
159 setliteral},
160 {"status",
161 "show current status",
162 status},
163 {"binary",
164 "set mode to octet",
165 setbinary},
166 {"ascii",
167 "set mode to netascii",
168 setascii},
169 {"rexmt",
170 "set per-packet transmission timeout",
171 setrexmt},
172 {"timeout",
173 "set total retransmission timeout",
174 settimeout},
175 {"?",
176 "print help information",
177 help},
178 {"help",
179 "print help information",
180 help},
181 {0, 0, 0}
184 struct cmd *getcmd(char *);
185 char *tail(char *);
187 char *xstrdup(const char *);
189 const char *program;
191 static void usage(int errcode)
193 fprintf(stderr,
194 #ifdef HAVE_IPV6
195 "Usage: %s [-4][-6][-v][-l][-m mode] [host [port]] [-c command]\n",
196 #else
197 "Usage: %s [-v][-l][-m mode] [host [port]] [-c command]\n",
198 #endif
199 program);
200 exit(errcode);
203 int main(int argc, char *argv[])
205 union sock_addr sa;
206 int arg;
207 static int pargc, peerargc;
208 static int iscmd = 0;
209 char **pargv;
210 const char *optx;
211 char *peerargv[3];
213 program = argv[0];
215 mode = MODE_DEFAULT;
217 peerargv[0] = argv[0];
218 peerargc = 1;
220 for (arg = 1; !iscmd && arg < argc; arg++) {
221 if (argv[arg][0] == '-') {
222 for (optx = &argv[arg][1]; *optx; optx++) {
223 switch (*optx) {
224 case '4':
225 ai_fam = AF_INET;
226 break;
227 #ifdef HAVE_IPV6
228 case '6':
229 ai_fam = AF_INET6;
230 break;
231 #endif
232 case 'v':
233 verbose = 1;
234 break;
235 case 'V':
236 /* Print version and configuration to stdout and exit */
237 printf("%s\n", TFTP_CONFIG_STR);
238 exit(0);
239 case 'l':
240 literal = 1;
241 break;
242 case 'm':
243 if (++arg >= argc)
244 usage(EX_USAGE);
246 const struct modes *p;
248 for (p = modes; p->m_name; p++) {
249 if (!strcmp(argv[arg], p->m_name))
250 break;
252 if (p->m_name) {
253 settftpmode(p);
254 } else {
255 fprintf(stderr, "%s: invalid mode: %s\n",
256 argv[0], argv[arg]);
257 exit(EX_USAGE);
260 break;
261 case 'c':
262 iscmd = 1;
263 break;
264 case 'R':
265 if (++arg >= argc)
266 usage(EX_USAGE);
267 if (sscanf
268 (argv[arg], "%u:%u", &portrange_from,
269 &portrange_to) != 2
270 || portrange_from > portrange_to
271 || portrange_to > 65535) {
272 fprintf(stderr, "Bad port range: %s\n", argv[arg]);
273 exit(EX_USAGE);
275 portrange = 1;
276 break;
277 case 'h':
278 default:
279 usage(*optx == 'h' ? 0 : EX_USAGE);
282 } else {
283 if (peerargc >= 3)
284 usage(EX_USAGE);
286 peerargv[peerargc++] = argv[arg];
290 ai_fam_sock = ai_fam;
292 pargv = argv + arg;
293 pargc = argc - arg;
295 sp = getservbyname("tftp", "udp");
296 if (sp == 0) {
297 /* Use canned values */
298 if (verbose)
299 fprintf(stderr,
300 "tftp: tftp/udp: unknown service, faking it...\n");
301 sp = xmalloc(sizeof(struct servent));
302 sp->s_name = (char *)"tftp";
303 sp->s_aliases = NULL;
304 sp->s_port = htons(IPPORT_TFTP);
305 sp->s_proto = (char *)"udp";
308 tftp_signal(SIGINT, intr, 0);
310 if (peerargc) {
311 /* Set peer */
312 if (sigsetjmp(toplevel, 1) != 0)
313 exit(EX_NOHOST);
314 setpeer(peerargc, peerargv);
317 if (ai_fam_sock == AF_UNSPEC)
318 ai_fam_sock = AF_INET;
320 f = socket(ai_fam_sock, SOCK_DGRAM, 0);
321 if (f < 0) {
322 perror("tftp: socket");
323 exit(EX_OSERR);
325 bzero(&sa, sizeof(sa));
326 sa.sa.sa_family = ai_fam_sock;
327 if (pick_port_bind(f, &sa, portrange_from, portrange_to)) {
328 perror("tftp: bind");
329 exit(EX_OSERR);
332 if (iscmd && pargc) {
333 /* -c specified; execute command and exit */
334 struct cmd *c;
336 if (sigsetjmp(toplevel, 1) != 0)
337 exit(EX_UNAVAILABLE);
339 c = getcmd(pargv[0]);
340 if (c == (struct cmd *)-1 || c == (struct cmd *)0) {
341 fprintf(stderr, "%s: invalid command: %s\n", argv[0],
342 pargv[1]);
343 exit(EX_USAGE);
345 (*c->handler) (pargc, pargv);
346 exit(0);
348 #ifdef WITH_READLINE
349 #ifdef HAVE_READLINE_HISTORY_H
350 using_history();
351 #endif
352 #endif
354 if (sigsetjmp(toplevel, 1) != 0)
355 (void)putchar('\n');
356 command();
358 return 0; /* Never reached */
361 char *hostname;
363 /* Called when a command is incomplete; modifies
364 the global variable "line" */
365 static void getmoreargs(const char *partial, const char *mprompt)
367 #ifdef WITH_READLINE
368 char *eline;
369 int len, elen;
371 len = strlen(partial);
372 eline = readline(mprompt);
373 if (!eline)
374 exit(0); /* EOF */
376 elen = strlen(eline);
378 if (line) {
379 free(line);
380 line = NULL;
382 line = xmalloc(len + elen + 1);
383 strcpy(line, partial);
384 strcpy(line + len, eline);
385 free(eline);
387 #ifdef HAVE_READLINE_HISTORY_H
388 add_history(line);
389 #endif
390 #else
391 int len = strlen(partial);
393 strcpy(line, partial);
394 fputs(mprompt, stdout);
395 if (fgets(line + len, LBUFLEN - len, stdin) == 0)
396 if (feof(stdin))
397 exit(0); /* EOF */
398 #endif
401 void setpeer(int argc, char *argv[])
403 int err;
405 if (argc < 2) {
406 getmoreargs("connect ", "(to) ");
407 makeargv();
408 argc = margc;
409 argv = margv;
411 if ((argc < 2) || (argc > 3)) {
412 printf("usage: %s host-name [port]\n", argv[0]);
413 return;
416 peeraddr.sa.sa_family = ai_fam;
417 err = set_sock_addr(argv[1], &peeraddr, &hostname);
418 if (err) {
419 printf("Error: %s\n", gai_strerror(err));
420 printf("%s: unknown host\n", argv[1]);
421 connected = 0;
422 return;
424 ai_fam = peeraddr.sa.sa_family;
425 if (f == -1) { /* socket not open */
426 ai_fam_sock = ai_fam;
427 } else { /* socket was already open */
428 if (ai_fam_sock != ai_fam) { /* need reopen socken for new family */
429 union sock_addr sa;
431 close(f);
432 ai_fam_sock = ai_fam;
433 f = socket(ai_fam_sock, SOCK_DGRAM, 0);
434 if (f < 0) {
435 perror("tftp: socket");
436 exit(EX_OSERR);
438 bzero((char *)&sa, sizeof (sa));
439 sa.sa.sa_family = ai_fam_sock;
440 if (pick_port_bind(f, &sa, portrange_from, portrange_to)) {
441 perror("tftp: bind");
442 exit(EX_OSERR);
446 port = sp->s_port;
447 if (argc == 3) {
448 struct servent *usp;
449 usp = getservbyname(argv[2], "udp");
450 if (usp) {
451 port = usp->s_port;
452 } else {
453 unsigned long myport;
454 char *ep;
455 myport = strtoul(argv[2], &ep, 10);
456 if (*ep || myport > 65535UL) {
457 printf("%s: bad port number\n", argv[2]);
458 connected = 0;
459 return;
461 port = htons((u_short) myport);
465 if (verbose) {
466 char tmp[INET6_ADDRSTRLEN], *tp;
467 tp = (char *)inet_ntop(peeraddr.sa.sa_family, SOCKADDR_P(&peeraddr),
468 tmp, INET6_ADDRSTRLEN);
469 if (!tp)
470 tp = (char *)"???";
471 printf("Connected to %s (%s), port %u\n",
472 hostname, tp, (unsigned int)ntohs(port));
474 connected = 1;
477 void modecmd(int argc, char *argv[])
479 const struct modes *p;
480 const char *sep;
482 if (argc < 2) {
483 printf("Using %s mode to transfer files.\n", mode->m_mode);
484 return;
486 if (argc == 2) {
487 for (p = modes; p->m_name; p++)
488 if (strcmp(argv[1], p->m_name) == 0)
489 break;
490 if (p->m_name) {
491 settftpmode(p);
492 return;
494 printf("%s: unknown mode\n", argv[1]);
495 /* drop through and print usage message */
498 printf("usage: %s [", argv[0]);
499 sep = " ";
500 for (p = modes; p->m_name; p++) {
501 printf("%s%s", sep, p->m_name);
502 if (*sep == ' ')
503 sep = " | ";
505 printf(" ]\n");
506 return;
509 void setbinary(int argc, char *argv[])
511 (void)argc;
512 (void)argv; /* Quiet unused warning */
513 settftpmode(MODE_OCTET);
516 void setascii(int argc, char *argv[])
518 (void)argc;
519 (void)argv; /* Quiet unused warning */
520 settftpmode(MODE_NETASCII);
523 static void settftpmode(const struct modes *newmode)
525 mode = newmode;
526 if (verbose)
527 printf("mode set to %s\n", mode->m_mode);
531 * Send file(s).
533 void put(int argc, char *argv[])
535 int fd;
536 int n, err;
537 char *cp, *targ;
539 if (argc < 2) {
540 getmoreargs("send ", "(file) ");
541 makeargv();
542 argc = margc;
543 argv = margv;
545 if (argc < 2) {
546 putusage(argv[0]);
547 return;
549 targ = argv[argc - 1];
550 if (!literal && strchr(argv[argc - 1], ':')) {
551 for (n = 1; n < argc - 1; n++)
552 if (strchr(argv[n], ':')) {
553 putusage(argv[0]);
554 return;
556 cp = argv[argc - 1];
557 targ = strchr(cp, ':');
558 *targ++ = 0;
559 peeraddr.sa.sa_family = ai_fam;
560 err = set_sock_addr(cp, &peeraddr,&hostname);
561 if (err) {
562 printf("Error: %s\n", gai_strerror(err));
563 printf("%s: unknown host\n", argv[1]);
564 connected = 0;
565 return;
567 ai_fam = peeraddr.sa.sa_family;
568 connected = 1;
570 if (!connected) {
571 printf("No target machine specified.\n");
572 return;
574 if (argc < 4) {
575 cp = argc == 2 ? tail(targ) : argv[1];
576 fd = open(cp, O_RDONLY | mode->m_openflags);
577 if (fd < 0) {
578 fprintf(stderr, "tftp: ");
579 perror(cp);
580 return;
582 if (verbose)
583 printf("putting %s to %s:%s [%s]\n",
584 cp, hostname, targ, mode->m_mode);
585 sa_set_port(&peeraddr, port);
586 tftp_sendfile(fd, targ, mode->m_mode);
587 return;
589 /* this assumes the target is a directory */
590 /* on a remote unix system. hmmmm. */
591 cp = strchr(targ, '\0');
592 *cp++ = '/';
593 for (n = 1; n < argc - 1; n++) {
594 strcpy(cp, tail(argv[n]));
595 fd = open(argv[n], O_RDONLY | mode->m_openflags);
596 if (fd < 0) {
597 fprintf(stderr, "tftp: ");
598 perror(argv[n]);
599 continue;
601 if (verbose)
602 printf("putting %s to %s:%s [%s]\n",
603 argv[n], hostname, targ, mode->m_mode);
604 sa_set_port(&peeraddr, port);
605 tftp_sendfile(fd, targ, mode->m_mode);
609 static void putusage(char *s)
611 printf("usage: %s file ... host:target, or\n", s);
612 printf(" %s file ... target (when already connected)\n", s);
616 * Receive file(s).
618 void get(int argc, char *argv[])
620 int fd;
621 int n;
622 char *cp;
623 char *src;
625 if (argc < 2) {
626 getmoreargs("get ", "(files) ");
627 makeargv();
628 argc = margc;
629 argv = margv;
631 if (argc < 2) {
632 getusage(argv[0]);
633 return;
635 if (!connected) {
636 for (n = 1; n < argc; n++)
637 if (literal || strchr(argv[n], ':') == 0) {
638 getusage(argv[0]);
639 return;
642 for (n = 1; n < argc; n++) {
643 src = strchr(argv[n], ':');
644 if (literal || src == NULL)
645 src = argv[n];
646 else {
647 int err;
649 *src++ = 0;
650 peeraddr.sa.sa_family = ai_fam;
651 err = set_sock_addr(argv[n], &peeraddr, &hostname);
652 if (err) {
653 printf("Warning: %s\n", gai_strerror(err));
654 printf("%s: unknown host\n", argv[1]);
655 continue;
657 ai_fam = peeraddr.sa.sa_family;
658 connected = 1;
660 if (argc < 4) {
661 cp = argc == 3 ? argv[2] : tail(src);
662 fd = open(cp, O_WRONLY | O_CREAT | O_TRUNC | mode->m_openflags,
663 0666);
664 if (fd < 0) {
665 fprintf(stderr, "tftp: ");
666 perror(cp);
667 return;
669 if (verbose)
670 printf("getting from %s:%s to %s [%s]\n",
671 hostname, src, cp, mode->m_mode);
672 sa_set_port(&peeraddr, port);
673 tftp_recvfile(fd, src, mode->m_mode);
674 break;
676 cp = tail(src); /* new .. jdg */
677 fd = open(cp, O_WRONLY | O_CREAT | O_TRUNC | mode->m_openflags,
678 0666);
679 if (fd < 0) {
680 fprintf(stderr, "tftp: ");
681 perror(cp);
682 continue;
684 if (verbose)
685 printf("getting from %s:%s to %s [%s]\n",
686 hostname, src, cp, mode->m_mode);
687 sa_set_port(&peeraddr, port);
688 tftp_recvfile(fd, src, mode->m_mode);
692 static void getusage(char *s)
694 printf("usage: %s host:file host:file ... file, or\n", s);
695 printf(" %s file file ... file if connected\n", s);
698 int rexmtval = TIMEOUT;
700 void setrexmt(int argc, char *argv[])
702 int t;
704 if (argc < 2) {
705 getmoreargs("rexmt-timeout ", "(value) ");
706 makeargv();
707 argc = margc;
708 argv = margv;
710 if (argc != 2) {
711 printf("usage: %s value\n", argv[0]);
712 return;
714 t = atoi(argv[1]);
715 if (t < 0)
716 printf("%s: bad value\n", argv[1]);
717 else
718 rexmtval = t;
721 int maxtimeout = 5 * TIMEOUT;
723 void settimeout(int argc, char *argv[])
725 int t;
727 if (argc < 2) {
728 getmoreargs("maximum-timeout ", "(value) ");
729 makeargv();
730 argc = margc;
731 argv = margv;
733 if (argc != 2) {
734 printf("usage: %s value\n", argv[0]);
735 return;
737 t = atoi(argv[1]);
738 if (t < 0)
739 printf("%s: bad value\n", argv[1]);
740 else
741 maxtimeout = t;
744 void setliteral(int argc, char *argv[])
746 (void)argc;
747 (void)argv; /* Quiet unused warning */
748 literal = !literal;
749 printf("Literal mode %s.\n", literal ? "on" : "off");
752 void status(int argc, char *argv[])
754 (void)argc;
755 (void)argv; /* Quiet unused warning */
756 if (connected)
757 printf("Connected to %s.\n", hostname);
758 else
759 printf("Not connected.\n");
760 printf("Mode: %s Verbose: %s Tracing: %s Literal: %s\n", mode->m_mode,
761 verbose ? "on" : "off", trace ? "on" : "off",
762 literal ? "on" : "off");
763 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
764 rexmtval, maxtimeout);
767 void intr(int sig)
769 (void)sig; /* Quiet unused warning */
771 alarm(0);
772 tftp_signal(SIGALRM, SIG_DFL, 0);
773 siglongjmp(toplevel, -1);
776 char *tail(char *filename)
778 char *s;
780 while (*filename) {
781 s = strrchr(filename, '/');
782 if (s == NULL)
783 break;
784 if (s[1])
785 return (s + 1);
786 *s = '\0';
788 return (filename);
792 * Command parser.
794 static void command(void)
796 struct cmd *c;
798 for (;;) {
799 #ifdef WITH_READLINE
800 if (line) {
801 free(line);
802 line = NULL;
804 line = readline(prompt);
805 if (!line)
806 exit(0); /* EOF */
807 #else
808 fputs(prompt, stdout);
809 if (fgets(line, LBUFLEN, stdin) == 0) {
810 if (feof(stdin)) {
811 exit(0);
812 } else {
813 continue;
816 #endif
817 if ((line[0] == 0) || (line[0] == '\n'))
818 continue;
819 #ifdef WITH_READLINE
820 #ifdef HAVE_READLINE_HISTORY_H
821 add_history(line);
822 #endif
823 #endif
824 makeargv();
825 if (margc == 0)
826 continue;
828 c = getcmd(margv[0]);
829 if (c == (struct cmd *)-1) {
830 printf("?Ambiguous command\n");
831 continue;
833 if (c == 0) {
834 printf("?Invalid command\n");
835 continue;
837 (*c->handler) (margc, margv);
841 struct cmd *getcmd(char *name)
843 const char *p;
844 char *q;
845 struct cmd *c, *found;
846 int nmatches, longest;
848 longest = 0;
849 nmatches = 0;
850 found = 0;
851 for (c = cmdtab; (p = c->name) != NULL; c++) {
852 for (q = name; *q == *p++; q++)
853 if (*q == 0) /* exact match? */
854 return (c);
855 if (!*q) { /* the name was a prefix */
856 if (q - name > longest) {
857 longest = q - name;
858 nmatches = 1;
859 found = c;
860 } else if (q - name == longest)
861 nmatches++;
864 if (nmatches > 1)
865 return ((struct cmd *)-1);
866 return (found);
870 * Slice a string up into argc/argv.
872 static void makeargv(void)
874 char *cp;
875 char **argp = margv;
877 margc = 0;
878 for (cp = line; *cp;) {
879 while (isspace(*cp))
880 cp++;
881 if (*cp == '\0')
882 break;
883 *argp++ = cp;
884 margc += 1;
885 while (*cp != '\0' && !isspace(*cp))
886 cp++;
887 if (*cp == '\0')
888 break;
889 *cp++ = '\0';
891 *argp++ = 0;
894 void quit(int argc, char *argv[])
896 (void)argc;
897 (void)argv; /* Quiet unused warning */
898 exit(0);
902 * Help command.
904 void help(int argc, char *argv[])
906 struct cmd *c;
908 printf("%s\n", VERSION);
910 if (argc == 1) {
911 printf("Commands may be abbreviated. Commands are:\n\n");
912 for (c = cmdtab; c->name; c++)
913 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
914 return;
916 while (--argc > 0) {
917 char *arg;
918 arg = *++argv;
919 c = getcmd(arg);
920 if (c == (struct cmd *)-1)
921 printf("?Ambiguous help command %s\n", arg);
922 else if (c == (struct cmd *)0)
923 printf("?Invalid help command %s\n", arg);
924 else
925 printf("%s\n", c->help);
929 void settrace(int argc, char *argv[])
931 (void)argc;
932 (void)argv; /* Quiet unused warning */
934 trace = !trace;
935 printf("Packet tracing %s.\n", trace ? "on" : "off");
938 void setverbose(int argc, char *argv[])
940 (void)argc;
941 (void)argv; /* Quiet unused warning */
943 verbose = !verbose;
944 printf("Verbose mode %s.\n", verbose ? "on" : "off");