1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2002 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 /* 43 * TFTP User Program -- Command Interface. 44 */ 45 #include <sys/types.h> 46 #include <sys/socket.h> 47 48 #include <arpa/inet.h> 49 50 #include <signal.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <errno.h> 54 #include <ctype.h> 55 #include <netdb.h> 56 #include <fcntl.h> 57 #include <string.h> 58 #include <limits.h> 59 60 #include "tftpcommon.h" 61 #include "tftpprivate.h" 62 63 #define NELEM(a) (sizeof (a) / sizeof ((a)[0])) 64 65 #define TIMEOUT 5 /* secs between rexmt's */ 66 67 struct sockaddr_in6 sin6; 68 int f; 69 int maxtimeout = 5 * TIMEOUT; 70 int verbose; 71 int trace; 72 int srexmtval; 73 int blksize; 74 int rexmtval = TIMEOUT; 75 int tsize_opt; 76 jmp_buf toplevel; 77 78 static int default_port, port; 79 static int connected; 80 static char mode[32]; 81 static char line[200]; 82 static char *prompt = "tftp"; 83 static char hostname[MAXHOSTNAMELEN]; 84 85 static void intr(int); 86 static void quit(int, char **); 87 static void help(int, char **); 88 static void setverbose(int, char **); 89 static void settrace(int, char **); 90 static void status(int, char **); 91 static void get(int, char **); 92 static void put(int, char **); 93 static void setpeer(int, char **); 94 static void modecmd(int, char **); 95 static void setrexmt(int, char **); 96 static void settimeout(int, char **); 97 static void setbinary(int, char **); 98 static void setascii(int, char **); 99 static void setblksize(int, char **); 100 static void setsrexmt(int, char **); 101 static void settsize(int, char **); 102 static void setmode(char *); 103 static void putusage(char *); 104 static void getusage(char *); 105 static char *finddelimiter(char *); 106 static char *removebrackets(char *); 107 static int prompt_for_arg(char *, int, char *); 108 static struct cmd *getcmd(char *); 109 static char *tail(char *); 110 static void command(int); 111 static void makeargv(int *, char ***); 112 113 #define HELPINDENT (sizeof ("connect")) 114 115 struct cmd { 116 char *name; 117 char *help; 118 void (*handler)(int, char **); 119 }; 120 121 static char vhelp[] = "toggle verbose mode"; 122 static char thelp[] = "toggle packet tracing"; 123 static char chelp[] = "connect to remote tftp"; 124 static char qhelp[] = "exit tftp"; 125 static char hhelp[] = "print help information"; 126 static char shelp[] = "send file"; 127 static char rhelp[] = "receive file"; 128 static char mhelp[] = "set file transfer mode"; 129 static char sthelp[] = "show current status"; 130 static char xhelp[] = "set per-packet retransmission timeout"; 131 static char ihelp[] = "set total retransmission timeout"; 132 static char ashelp[] = "set mode to netascii"; 133 static char bnhelp[] = "set mode to octet"; 134 static char bshelp[] = "set transfer blocksize to negotiate with the " 135 "server"; 136 static char srhelp[] = "set preferred per-packet retransmission " 137 "timeout for server"; 138 static char tshelp[] = "toggle sending the transfer size option to " 139 "the server"; 140 141 static struct cmd cmdtab[] = { 142 { "connect", chelp, setpeer }, 143 { "mode", mhelp, modecmd }, 144 { "put", shelp, put }, 145 { "get", rhelp, get }, 146 { "quit", qhelp, quit }, 147 { "verbose", vhelp, setverbose }, 148 { "trace", thelp, settrace }, 149 { "status", sthelp, status }, 150 { "binary", bnhelp, setbinary }, 151 { "ascii", ashelp, setascii }, 152 { "rexmt", xhelp, setrexmt }, 153 { "timeout", ihelp, settimeout }, 154 { "blksize", bshelp, setblksize }, 155 { "srexmt", srhelp, setsrexmt }, 156 { "tsize", tshelp, settsize }, 157 { "?", hhelp, help }, 158 { NULL } 159 }; 160 161 #define AMBIGCMD (&cmdtab[NELEM(cmdtab)]) 162 163 int 164 main(int argc, char **argv) 165 { 166 struct servent *sp; 167 struct sockaddr_in6 sin6; 168 int top; 169 170 sp = getservbyname("tftp", "udp"); 171 default_port = (sp != NULL) ? sp->s_port : htons(IPPORT_TFTP); 172 port = default_port; 173 174 f = socket(AF_INET6, SOCK_DGRAM, 0); 175 if (f < 0) { 176 perror("tftp: socket"); 177 exit(3); 178 } 179 180 (void) memset(&sin6, 0, sizeof (sin6)); 181 sin6.sin6_family = AF_INET6; 182 if (bind(f, (struct sockaddr *)&sin6, sizeof (sin6)) < 0) { 183 perror("tftp: bind"); 184 exit(1); 185 } 186 187 (void) strlcpy(mode, "netascii", sizeof (mode)); 188 (void) signal(SIGINT, intr); 189 if (argc > 1) { 190 if (setjmp(toplevel) != 0) 191 exit(0); 192 setpeer(argc, argv); 193 } 194 195 top = (setjmp(toplevel) == 0); 196 for (;;) 197 command(top); 198 199 /*NOTREACHED*/ 200 return (0); 201 } 202 203 /* Prompt for command argument, add to buffer with space separator */ 204 static int 205 prompt_for_arg(char *buffer, int buffer_size, char *prompt) 206 { 207 int ch; 208 209 if (strlcat(buffer, " ", buffer_size) >= buffer_size) { 210 (void) fputs("?Line too long\n", stderr); 211 return (-1); 212 } 213 (void) printf("(%s) ", prompt); 214 if (fgets(buffer + strlen(buffer), buffer_size - strlen(buffer), 215 stdin) == NULL) { 216 return (-1); 217 } 218 /* Flush what didn't fit in the buffer */ 219 if (buffer[strlen(buffer)-1] != '\n') { 220 while (((ch = getchar()) != EOF) && (ch != '\n')) 221 ; 222 (void) fputs("?Line too long\n", stderr); 223 return (-1); 224 } else { 225 buffer[strlen(buffer)-1] = '\0'; 226 } 227 return (0); 228 } 229 230 static void 231 unknown_host(int error, char *hostname) 232 { 233 if (error == TRY_AGAIN) 234 (void) fprintf(stderr, "%s: Unknown host (try again later).\n", 235 hostname); 236 else 237 (void) fprintf(stderr, "%s: Unknown host.\n", hostname); 238 } 239 240 static void 241 setpeer(int argc, char **argv) 242 { 243 struct hostent *host; 244 int error_num; 245 struct in6_addr ipv6addr; 246 struct in_addr ipv4addr; 247 char *hostnameinput; 248 249 if (argc < 2) { 250 if (prompt_for_arg(line, sizeof (line), "to") == -1) 251 return; 252 makeargv(&argc, &argv); 253 } 254 if (argc > 3 || argc < 2) { 255 (void) fprintf(stderr, "usage: %s host-name [port]\n", 256 argv[0]); 257 return; 258 } 259 hostnameinput = removebrackets(argv[1]); 260 261 (void) memset(&sin6, 0, sizeof (sin6)); 262 sin6.sin6_family = AF_INET6; 263 if (host = getipnodebyname(hostnameinput, AF_INET6, 264 AI_ALL | AI_ADDRCONFIG | AI_V4MAPPED, &error_num)) { 265 (void) memcpy(&sin6.sin6_addr, host->h_addr_list[0], 266 host->h_length); 267 /* 268 * If host->h_name is a IPv4-mapped IPv6 literal, we'll convert 269 * it to IPv4 literal address. 270 */ 271 if ((inet_pton(AF_INET6, host->h_name, &ipv6addr) > 0) && 272 IN6_IS_ADDR_V4MAPPED(&ipv6addr)) { 273 IN6_V4MAPPED_TO_INADDR(&ipv6addr, &ipv4addr); 274 (void) inet_ntop(AF_INET, &ipv4addr, hostname, 275 sizeof (hostname)); 276 } else { 277 (void) strlcpy(hostname, host->h_name, 278 sizeof (hostname)); 279 } 280 freehostent(host); 281 } else { 282 /* Keeping with previous semantics */ 283 connected = 0; 284 unknown_host(error_num, hostnameinput); 285 return; 286 } 287 288 port = default_port; 289 if (argc == 3) { 290 port = atoi(argv[2]); 291 if ((port < 1) || (port > 65535)) { 292 (void) fprintf(stderr, "%s: bad port number\n", 293 argv[2]); 294 connected = 0; 295 return; 296 } 297 port = htons(port); 298 } 299 connected = 1; 300 } 301 302 static struct modes { 303 char *m_name; 304 char *m_mode; 305 } modes[] = { 306 { "ascii", "netascii" }, 307 { "netascii", "netascii" }, 308 { "binary", "octet" }, 309 { "image", "octet" }, 310 { "octet", "octet" }, 311 /* { "mail", "mail" }, */ 312 { 0, 0 } 313 }; 314 315 static void 316 modecmd(int argc, char **argv) 317 { 318 struct modes *p; 319 320 if (argc < 2) { 321 (void) fprintf(stderr, "Using %s mode to transfer files.\n", 322 mode); 323 return; 324 } 325 if (argc == 2) { 326 for (p = modes; p->m_name != NULL; p++) 327 if (strcmp(argv[1], p->m_name) == 0) { 328 setmode(p->m_mode); 329 return; 330 } 331 (void) fprintf(stderr, "%s: unknown mode\n", argv[1]); 332 /* drop through and print usage message */ 333 } 334 335 p = modes; 336 (void) fprintf(stderr, "usage: %s [ %s", argv[0], p->m_name); 337 for (p++; p->m_name != NULL; p++) 338 (void) fprintf(stderr, " | %s", p->m_name); 339 (void) puts(" ]"); 340 } 341 342 /*ARGSUSED*/ 343 static void 344 setbinary(int argc, char **argv) 345 { 346 setmode("octet"); 347 } 348 349 /*ARGSUSED*/ 350 static void 351 setascii(int argc, char **argv) 352 { 353 setmode("netascii"); 354 } 355 356 static void 357 setmode(char *newmode) 358 { 359 (void) strlcpy(mode, newmode, sizeof (mode)); 360 if (verbose) 361 (void) printf("mode set to %s\n", mode); 362 } 363 364 /* 365 * Send file(s). 366 */ 367 static void 368 put(int argc, char **argv) 369 { 370 int fd; 371 int n; 372 char *cp, *targ; 373 struct in6_addr ipv6addr; 374 struct in_addr ipv4addr; 375 char buf[PATH_MAX + 1], *argtail; 376 377 if (argc < 2) { 378 if (prompt_for_arg(line, sizeof (line), "file") == -1) 379 return; 380 makeargv(&argc, &argv); 381 } 382 if (argc < 2) { 383 putusage(argv[0]); 384 return; 385 } 386 targ = argv[argc - 1]; 387 if (finddelimiter(argv[argc - 1])) { 388 char *cp; 389 struct hostent *hp; 390 int error_num; 391 392 for (n = 1; n < argc - 1; n++) 393 if (finddelimiter(argv[n])) { 394 putusage(argv[0]); 395 return; 396 } 397 cp = argv[argc - 1]; 398 targ = finddelimiter(cp); 399 *targ++ = 0; 400 cp = removebrackets(cp); 401 402 if ((hp = getipnodebyname(cp, 403 AF_INET6, AI_ALL | AI_ADDRCONFIG | AI_V4MAPPED, 404 &error_num)) == NULL) { 405 unknown_host(error_num, cp); 406 return; 407 } 408 (void) memcpy(&sin6.sin6_addr, hp->h_addr_list[0], 409 hp->h_length); 410 411 sin6.sin6_family = AF_INET6; 412 connected = 1; 413 /* 414 * If hp->h_name is a IPv4-mapped IPv6 literal, we'll convert 415 * it to IPv4 literal address. 416 */ 417 if ((inet_pton(AF_INET6, hp->h_name, &ipv6addr) > 0) && 418 IN6_IS_ADDR_V4MAPPED(&ipv6addr)) { 419 IN6_V4MAPPED_TO_INADDR(&ipv6addr, &ipv4addr); 420 (void) inet_ntop(AF_INET, &ipv4addr, hostname, 421 sizeof (hostname)); 422 } else { 423 (void) strlcpy(hostname, hp->h_name, 424 sizeof (hostname)); 425 } 426 } 427 if (!connected) { 428 (void) fputs("No target machine specified.\n", stderr); 429 return; 430 } 431 if (argc < 4) { 432 cp = argc == 2 ? tail(targ) : argv[1]; 433 fd = open(cp, O_RDONLY); 434 if (fd < 0) { 435 (void) fprintf(stderr, "tftp: %s: %s\n", cp, 436 strerror(errno)); 437 return; 438 } 439 if (verbose) 440 (void) printf("putting %s to %s:%s [%s]\n", 441 cp, hostname, targ, mode); 442 sin6.sin6_port = port; 443 tftp_sendfile(fd, targ, mode); 444 return; 445 } 446 /* this assumes the target is a directory */ 447 /* on a remote unix system. hmmmm. */ 448 if (strlen(targ) + 1 >= sizeof (buf)) { 449 (void) fprintf(stderr, "tftp: filename too long: %s\n", targ); 450 return; 451 } 452 for (n = 1; n < argc - 1; n++) { 453 argtail = tail(argv[n]); 454 if (snprintf(buf, sizeof (buf), "%s/%s", targ, argtail) >= 455 sizeof (buf)) { 456 (void) fprintf(stderr, 457 "tftp: filename too long: %s/%s\n", targ, argtail); 458 continue; 459 } 460 fd = open(argv[n], O_RDONLY); 461 if (fd < 0) { 462 (void) fprintf(stderr, "tftp: %s: %s\n", argv[n], 463 strerror(errno)); 464 continue; 465 } 466 if (verbose) 467 (void) printf("putting %s to %s:%s [%s]\n", 468 argv[n], hostname, buf, mode); 469 sin6.sin6_port = port; 470 tftp_sendfile(fd, buf, mode); 471 } 472 } 473 474 static void 475 putusage(char *s) 476 { 477 (void) fprintf(stderr, "usage: %s file ... host:target, or\n" 478 " %s file ... target (when already connected)\n", s, s); 479 } 480 481 /* 482 * Receive file(s). 483 */ 484 static void 485 get(int argc, char **argv) 486 { 487 int fd; 488 int n; 489 char *cp; 490 char *src; 491 struct in6_addr ipv6addr; 492 struct in_addr ipv4addr; 493 int error_num; 494 495 if (argc < 2) { 496 if (prompt_for_arg(line, sizeof (line), "files") == -1) 497 return; 498 makeargv(&argc, &argv); 499 } 500 if (argc < 2) { 501 getusage(argv[0]); 502 return; 503 } 504 if (!connected) { 505 for (n = 1; n < argc; n++) 506 if (finddelimiter(argv[n]) == 0) { 507 getusage(argv[0]); 508 return; 509 } 510 } 511 for (n = 1; n < argc; n++) { 512 src = finddelimiter(argv[n]); 513 if (src == NULL) 514 src = argv[n]; 515 else { 516 struct hostent *hp; 517 char *hostnameinput; 518 519 *src++ = 0; 520 hostnameinput = removebrackets(argv[n]); 521 522 if ((hp = getipnodebyname(hostnameinput, AF_INET6, 523 AI_ALL | AI_ADDRCONFIG | AI_V4MAPPED, 524 &error_num)) == NULL) { 525 unknown_host(error_num, hostnameinput); 526 continue; 527 } 528 (void) memcpy((caddr_t)&sin6.sin6_addr, 529 hp->h_addr_list[0], hp->h_length); 530 531 sin6.sin6_family = AF_INET6; 532 connected = 1; 533 /* 534 * If hp->h_name is a IPv4-mapped IPv6 literal, we'll 535 * convert it to IPv4 literal address. 536 */ 537 if ((inet_pton(AF_INET6, hp->h_name, &ipv6addr) > 0) && 538 IN6_IS_ADDR_V4MAPPED(&ipv6addr)) { 539 IN6_V4MAPPED_TO_INADDR(&ipv6addr, &ipv4addr); 540 (void) inet_ntop(AF_INET, &ipv4addr, hostname, 541 sizeof (hostname)); 542 } else { 543 (void) strlcpy(hostname, hp->h_name, 544 sizeof (hostname)); 545 } 546 } 547 if (argc < 4) { 548 cp = argc == 3 ? argv[2] : tail(src); 549 fd = creat(cp, 0644); 550 if (fd < 0) { 551 (void) fprintf(stderr, "tftp: %s: %s\n", cp, 552 strerror(errno)); 553 return; 554 } 555 if (verbose) 556 (void) printf("getting from %s:%s to %s [%s]\n", 557 hostname, src, cp, mode); 558 sin6.sin6_port = port; 559 tftp_recvfile(fd, src, mode); 560 break; 561 } 562 cp = tail(src); /* new .. jdg */ 563 fd = creat(cp, 0644); 564 if (fd < 0) { 565 (void) fprintf(stderr, "tftp: %s: %s\n", cp, 566 strerror(errno)); 567 continue; 568 } 569 if (verbose) 570 (void) printf("getting from %s:%s to %s [%s]\n", 571 hostname, src, cp, mode); 572 sin6.sin6_port = port; 573 tftp_recvfile(fd, src, mode); 574 } 575 } 576 577 static void 578 getusage(char *s) 579 { 580 (void) fprintf(stderr, "usage: %s host:file host:file ... file, or\n" 581 " %s file file ... file if connected\n", s, s); 582 } 583 584 static void 585 setrexmt(int argc, char **argv) 586 { 587 int t; 588 589 if (argc < 2) { 590 if (prompt_for_arg(line, sizeof (line), "value") == -1) 591 return; 592 makeargv(&argc, &argv); 593 } 594 if (argc != 2) { 595 (void) fprintf(stderr, "usage: %s value\n", argv[0]); 596 return; 597 } 598 t = atoi(argv[1]); 599 if (t < 0) 600 (void) fprintf(stderr, "%s: bad value\n", argv[1]); 601 else 602 rexmtval = t; 603 } 604 605 static void 606 settimeout(int argc, char **argv) 607 { 608 int t; 609 610 if (argc < 2) { 611 if (prompt_for_arg(line, sizeof (line), "value") == -1) 612 return; 613 makeargv(&argc, &argv); 614 } 615 if (argc != 2) { 616 (void) fprintf(stderr, "usage: %s value\n", argv[0]); 617 return; 618 } 619 t = atoi(argv[1]); 620 if (t < 0) 621 (void) fprintf(stderr, "%s: bad value\n", argv[1]); 622 else 623 maxtimeout = t; 624 } 625 626 /*ARGSUSED*/ 627 static void 628 status(int argc, char **argv) 629 { 630 if (connected) 631 (void) printf("Connected to %s.\n", hostname); 632 else 633 (void) puts("Not connected."); 634 (void) printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 635 verbose ? "on" : "off", trace ? "on" : "off"); 636 (void) printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 637 rexmtval, maxtimeout); 638 (void) printf("Transfer blocksize option: "); 639 if (blksize == 0) 640 (void) puts("off"); 641 else 642 (void) printf("%d bytes\n", blksize); 643 (void) printf("Server rexmt-interval option: "); 644 if (srexmtval == 0) 645 (void) puts("off"); 646 else 647 (void) printf("%d seconds\n", srexmtval); 648 (void) printf("Transfer size option: %s\n", tsize_opt ? "on" : "off"); 649 } 650 651 /*ARGSUSED*/ 652 static void 653 intr(int signum) 654 { 655 (void) cancel_alarm(); 656 longjmp(toplevel, -1); 657 } 658 659 static char * 660 tail(char *filename) 661 { 662 char *s; 663 664 while (*filename != '\0') { 665 s = strrchr(filename, '/'); 666 if (s == NULL) 667 break; 668 if (s[1] != '\0') 669 return (&s[1]); 670 *s = '\0'; 671 } 672 return (filename); 673 } 674 675 /* 676 * Command parser. 677 */ 678 static void 679 command(int top) 680 { 681 struct cmd *c; 682 int ch; 683 684 if (!top) 685 (void) putchar('\n'); 686 for (;;) { 687 (void) printf("%s> ", prompt); 688 if (fgets(line, sizeof (line), stdin) == NULL) { 689 if (feof(stdin)) 690 quit(0, NULL); 691 else 692 continue; 693 } 694 695 /* Flush what didn't fit in the buffer */ 696 if (line[strlen(line)-1] != '\n') { 697 while (((ch = getchar()) != EOF) && (ch != '\n')) 698 ; 699 (void) fputs("?Line too long\n", stderr); 700 } else { 701 line[strlen(line)-1] = '\0'; 702 if (line[0] != '\0') { 703 int argc; 704 char **argv; 705 706 makeargv(&argc, &argv); 707 c = getcmd(argv[0]); 708 if (c == AMBIGCMD) 709 (void) fputs("?Ambiguous command\n", 710 stderr); 711 else if (c == NULL) 712 (void) fputs("?Invalid command\n", 713 stderr); 714 else 715 (*c->handler)(argc, argv); 716 } 717 } 718 } 719 } 720 721 static struct cmd * 722 getcmd(char *name) 723 { 724 char *p, *q; 725 struct cmd *c, *found; 726 727 if (name == NULL) 728 return (NULL); 729 730 found = NULL; 731 for (c = cmdtab; (p = c->name) != NULL; c++) { 732 for (q = name; *q == *p++; q++) 733 if (*q == '\0') /* exact match? */ 734 return (c); 735 if (*q == '\0') /* the name was a prefix */ 736 found = (found == NULL) ? c : AMBIGCMD; 737 } 738 return (found); 739 } 740 741 /* 742 * Given a string, this function returns the pointer to the delimiting ':'. 743 * The string can contain an IPv6 literal address, which should be inside a 744 * pair of brackets, e.g. [1::2]. Any colons inside a pair of brackets are not 745 * accepted as delimiters. Returns NULL if delimiting ':' is not found. 746 */ 747 static char * 748 finddelimiter(char *str) 749 { 750 boolean_t is_bracket_open = B_FALSE; 751 char *cp; 752 753 for (cp = str; *cp != '\0'; cp++) { 754 if (*cp == '[') 755 is_bracket_open = B_TRUE; 756 else if (*cp == ']') 757 is_bracket_open = B_FALSE; 758 else if (*cp == ':' && !is_bracket_open) 759 return (cp); 760 } 761 return (NULL); 762 } 763 764 /* 765 * Given a string which is possibly surrounded by brackets, e.g. [1::2], this 766 * function returns a string after removing those brackets. If the brackets 767 * don't match, it does nothing. 768 */ 769 static char * 770 removebrackets(char *str) 771 { 772 char *newstr = str; 773 774 if ((str[0] == '[') && (str[strlen(str) - 1] == ']')) { 775 newstr = str + 1; 776 str[strlen(str) - 1] = '\0'; 777 } 778 return (newstr); 779 } 780 781 #define MARGV_INC 20 782 783 /* 784 * Slice a string up into argc/argv. 785 */ 786 static void 787 makeargv(int *argcp, char ***argvp) 788 { 789 char *cp; 790 char **argp; 791 int argc; 792 static char **argv; 793 static int argv_size; 794 795 if (argv == NULL) { 796 argv_size = MARGV_INC; 797 if ((argv = malloc(argv_size * sizeof (char *))) == NULL) { 798 perror("tftp: malloc"); 799 exit(1); 800 } 801 } 802 argc = 0; 803 argp = argv; 804 for (cp = line; *cp != '\0'; ) { 805 while (isspace(*cp)) 806 cp++; 807 if (*cp == '\0') 808 break; 809 *argp++ = cp; 810 argc++; 811 if (argc == argv_size) { 812 argv_size += MARGV_INC; 813 if ((argv = realloc(argv, 814 argv_size * sizeof (char *))) == NULL) { 815 perror("tftp: realloc"); 816 exit(1); 817 } 818 argp = argv + argc; 819 } 820 while (*cp != '\0' && !isspace(*cp)) 821 cp++; 822 if (*cp == '\0') 823 break; 824 *cp++ = '\0'; 825 } 826 *argp = NULL; 827 828 *argcp = argc; 829 *argvp = argv; 830 } 831 832 /*ARGSUSED*/ 833 static void 834 quit(int argc, char **argv) 835 { 836 exit(0); 837 } 838 839 /* 840 * Help command. 841 */ 842 static void 843 help(int argc, char **argv) 844 { 845 struct cmd *c; 846 847 if (argc == 1) { 848 (void) puts("Commands may be abbreviated. Commands are:\n"); 849 for (c = cmdtab; c->name != NULL; c++) 850 (void) printf("%-*s\t%s\n", HELPINDENT, c->name, 851 c->help); 852 return; 853 } 854 while (--argc > 0) { 855 char *arg; 856 arg = *++argv; 857 c = getcmd(arg); 858 if (c == AMBIGCMD) 859 (void) fprintf(stderr, "?Ambiguous help command %s\n", 860 arg); 861 else if (c == NULL) 862 (void) fprintf(stderr, "?Invalid help command %s\n", 863 arg); 864 else 865 (void) fprintf(stderr, "%s\n", c->help); 866 } 867 } 868 869 /*ARGSUSED*/ 870 static void 871 settrace(int argc, char **argv) 872 { 873 trace = !trace; 874 (void) printf("Packet tracing %s.\n", trace ? "on" : "off"); 875 } 876 877 /*ARGSUSED*/ 878 static void 879 setverbose(int argc, char **argv) 880 { 881 verbose = !verbose; 882 (void) printf("Verbose mode %s.\n", verbose ? "on" : "off"); 883 } 884 885 static void 886 setblksize(int argc, char **argv) 887 { 888 int b; 889 890 if (argc < 2) { 891 if (prompt_for_arg(line, sizeof (line), "value") == -1) 892 return; 893 makeargv(&argc, &argv); 894 } 895 if (argc != 2) { 896 (void) fprintf(stderr, "usage: %s value\n", argv[0]); 897 return; 898 } 899 b = atoi(argv[1]); 900 901 /* RFC 2348 specifies valid blksize range, allow 0 to turn option off */ 902 if ((b < MIN_BLKSIZE || b > MAX_BLKSIZE) && b != 0) 903 (void) fprintf(stderr, "%s: bad value\n", argv[1]); 904 else 905 blksize = b; 906 } 907 908 static void 909 setsrexmt(int argc, char **argv) 910 { 911 int t; 912 913 if (argc < 2) { 914 if (prompt_for_arg(line, sizeof (line), "value") == -1) 915 return; 916 makeargv(&argc, &argv); 917 } 918 if (argc != 2) { 919 (void) fprintf(stderr, "usage: %s value\n", argv[0]); 920 return; 921 } 922 t = atoi(argv[1]); 923 924 /* RFC 2349 specifies valid timeout range, allow 0 to turn option off */ 925 if ((t < MIN_TIMEOUT || t > MAX_TIMEOUT) && t != 0) 926 (void) fprintf(stderr, "%s: bad value\n", argv[1]); 927 else 928 srexmtval = t; 929 } 930 931 static void 932 settsize(int argc, char **argv) 933 { 934 if (argc != 1) { 935 (void) fprintf(stderr, "usage: %s\n", argv[0]); 936 return; 937 } 938 tsize_opt = !tsize_opt; 939 (void) printf("Transfer size option %s.\n", tsize_opt ? "on" : "off"); 940 } 941