1 #ifndef lint 2 static char sccsid[] = "@(#)cmds.c 4.9 (Berkeley) 07/26/83"; 3 #endif 4 5 /* 6 * FTP User Program -- Command Routines. 7 */ 8 #include <sys/param.h> 9 #include <sys/stat.h> 10 #include <sys/socket.h> 11 12 #include <arpa/ftp.h> 13 14 #include <signal.h> 15 #include <stdio.h> 16 #include <errno.h> 17 #include <netdb.h> 18 19 #include "ftp_var.h" 20 21 extern char *globerr; 22 extern char **glob(); 23 extern char *home; 24 extern short gflag; 25 extern char *remglob(); 26 extern char *getenv(); 27 extern char *index(); 28 extern char *rindex(); 29 30 /* 31 * Connect to peer server and 32 * auto-login, if possible. 33 */ 34 setpeer(argc, argv) 35 int argc; 36 char *argv[]; 37 { 38 struct hostent *host, *hookup(); 39 int port; 40 41 if (connected) { 42 printf("Already connected to %s, use disconnect first.\n", 43 hostname); 44 return; 45 } 46 if (argc < 2) { 47 strcat(line, " "); 48 printf("(to) "); 49 gets(&line[strlen(line)]); 50 makeargv(); 51 argc = margc; 52 argv = margv; 53 } 54 if (argc > 3) { 55 printf("usage: %s host-name [port]\n", argv[0]); 56 return; 57 } 58 port = sp->s_port; 59 if (argc > 2) { 60 port = atoi(argv[2]); 61 if (port <= 0) { 62 printf("%s: bad port number-- %s\n", argv[1], argv[2]); 63 printf ("usage: %s host-name [port]\n", argv[0]); 64 return; 65 } 66 port = htons(port); 67 } 68 host = hookup(argv[1], port); 69 if (host) { 70 connected = 1; 71 if (autologin) 72 login(host); 73 } 74 } 75 76 struct types { 77 char *t_name; 78 char *t_mode; 79 int t_type; 80 char *t_arg; 81 } types[] = { 82 { "ascii", "A", TYPE_A, 0 }, 83 { "binary", "I", TYPE_I, 0 }, 84 { "image", "I", TYPE_I, 0 }, 85 { "ebcdic", "E", TYPE_E, 0 }, 86 { "tenex", "L", TYPE_L, bytename }, 87 0 88 }; 89 90 /* 91 * Set transfer type. 92 */ 93 settype(argc, argv) 94 char *argv[]; 95 { 96 register struct types *p; 97 int comret; 98 99 if (argc > 2) { 100 char *sep; 101 102 printf("usage: %s [", argv[0]); 103 sep = " "; 104 for (p = types; p->t_name; p++) { 105 printf("%s%s", sep, p->t_name); 106 if (*sep == ' ') 107 sep = " | "; 108 } 109 printf(" ]\n"); 110 return; 111 } 112 if (argc < 2) { 113 printf("Using %s mode to transfer files.\n", typename); 114 return; 115 } 116 for (p = types; p->t_name; p++) 117 if (strcmp(argv[1], p->t_name) == 0) 118 break; 119 if (p->t_name == 0) { 120 printf("%s: unknown mode\n", argv[1]); 121 return; 122 } 123 if ((p->t_arg != NULL) && (*(p->t_arg) != '\0')) 124 comret = command ("TYPE %s %s", p->t_mode, p->t_arg); 125 else 126 comret = command("TYPE %s", p->t_mode); 127 if (comret == COMPLETE) { 128 strcpy(typename, p->t_name); 129 type = p->t_type; 130 } 131 } 132 133 /* 134 * Set binary transfer type. 135 */ 136 /*VARARGS*/ 137 setbinary() 138 { 139 140 call(settype, "type", "binary", 0); 141 } 142 143 /* 144 * Set ascii transfer type. 145 */ 146 /*VARARGS*/ 147 setascii() 148 { 149 150 call(settype, "type", "ascii", 0); 151 } 152 153 /* 154 * Set tenex transfer type. 155 */ 156 /*VARARGS*/ 157 settenex() 158 { 159 160 call(settype, "type", "tenex", 0); 161 } 162 163 /* 164 * Set ebcdic transfer type. 165 */ 166 /*VARARGS*/ 167 setebcdic() 168 { 169 170 call(settype, "type", "ebcdic", 0); 171 } 172 173 /* 174 * Set file transfer mode. 175 */ 176 setmode(argc, argv) 177 char *argv[]; 178 { 179 180 printf("We only support %s mode, sorry.\n", modename); 181 } 182 183 /* 184 * Set file transfer format. 185 */ 186 setform(argc, argv) 187 char *argv[]; 188 { 189 190 printf("We only support %s format, sorry.\n", formname); 191 } 192 193 /* 194 * Set file transfer structure. 195 */ 196 setstruct(argc, argv) 197 char *argv[]; 198 { 199 200 printf("We only support %s structure, sorry.\n", structname); 201 } 202 203 put(argc, argv) 204 int argc; 205 char *argv[]; 206 { 207 char *cmd; 208 209 if (argc == 2) 210 argc++, argv[2] = argv[1]; 211 if (argc < 2) { 212 strcat(line, " "); 213 printf("(local-file) "); 214 gets(&line[strlen(line)]); 215 makeargv(); 216 argc = margc; 217 argv = margv; 218 } 219 if (argc < 2) { 220 usage: 221 printf("%s local-file remote-file\n", argv[0]); 222 return; 223 } 224 if (argc < 3) { 225 strcat(line, " "); 226 printf("(remote-file) "); 227 gets(&line[strlen(line)]); 228 makeargv(); 229 argc = margc; 230 argv = margv; 231 } 232 if (argc < 3) 233 goto usage; 234 if (!globulize(&argv[1])) 235 return; 236 cmd = (argv[0][0] == 'a') ? "APPE" : "STOR"; 237 sendrequest(cmd, argv[1], argv[2]); 238 } 239 240 /* 241 * Send multiple files. 242 */ 243 mput(argc, argv) 244 char *argv[]; 245 { 246 register int i; 247 248 if (argc < 2) { 249 strcat(line, " "); 250 printf("(local-files) "); 251 gets(&line[strlen(line)]); 252 makeargv(); 253 argc = margc; 254 argv = margv; 255 } 256 if (argc < 2) { 257 printf("%s local-files\n", argv[0]); 258 return; 259 } 260 for (i = 1; i < argc; i++) { 261 register char **cpp, **gargs; 262 263 if (!doglob) { 264 if (confirm(argv[0], argv[i])) 265 sendrequest("STOR", argv[i], argv[i]); 266 continue; 267 } 268 gargs = glob(argv[i]); 269 if (globerr != NULL) { 270 printf("%s\n", globerr); 271 if (gargs) 272 blkfree(gargs); 273 continue; 274 } 275 for (cpp = gargs; cpp && *cpp != NULL; cpp++) 276 if (confirm(argv[0], *cpp)) 277 sendrequest("STOR", *cpp, *cpp); 278 if (gargs != NULL) 279 blkfree(gargs); 280 } 281 } 282 283 /* 284 * Receive one file. 285 */ 286 get(argc, argv) 287 char *argv[]; 288 { 289 290 if (argc == 2) 291 argc++, argv[2] = argv[1]; 292 if (argc < 2) { 293 strcat(line, " "); 294 printf("(remote-file) "); 295 gets(&line[strlen(line)]); 296 makeargv(); 297 argc = margc; 298 argv = margv; 299 } 300 if (argc < 2) { 301 usage: 302 printf("%s remote-file [ local-file ]\n", argv[0]); 303 return; 304 } 305 if (argc < 3) { 306 strcat(line, " "); 307 printf("(local-file) "); 308 gets(&line[strlen(line)]); 309 makeargv(); 310 argc = margc; 311 argv = margv; 312 } 313 if (argc < 3) 314 goto usage; 315 if (!globulize(&argv[2])) 316 return; 317 recvrequest("RETR", argv[2], argv[1], "w"); 318 } 319 320 /* 321 * Get multiple files. 322 */ 323 mget(argc, argv) 324 char *argv[]; 325 { 326 char *cp; 327 328 if (argc < 2) { 329 strcat(line, " "); 330 printf("(remote-files) "); 331 gets(&line[strlen(line)]); 332 makeargv(); 333 argc = margc; 334 argv = margv; 335 } 336 if (argc < 2) { 337 printf("%s remote-files\n", argv[0]); 338 return; 339 } 340 while ((cp = remglob(argc, argv)) != NULL) 341 if (confirm(argv[0], cp)) 342 recvrequest("RETR", cp, cp, "w"); 343 } 344 345 char * 346 remglob(argc, argv) 347 char *argv[]; 348 { 349 char temp[16]; 350 static char buf[MAXPATHLEN]; 351 static FILE *ftemp = NULL; 352 static char **args; 353 int oldverbose, oldhash; 354 char *cp, *mode; 355 356 if (!doglob) { 357 if (args == NULL) 358 args = argv; 359 if ((cp = *++args) == NULL) 360 args = NULL; 361 return (cp); 362 } 363 if (ftemp == NULL) { 364 strcpy(temp, "/tmp/ftpXXXXXX"); 365 mktemp(temp); 366 oldverbose = verbose, verbose = 0; 367 oldhash = hash, hash = 0; 368 for (mode = "w"; *++argv != NULL; mode = "a") 369 recvrequest ("NLST", temp, *argv, mode); 370 verbose = oldverbose; hash = oldhash; 371 ftemp = fopen(temp, "r"); 372 unlink(temp); 373 if (ftemp == NULL) { 374 printf("can't find list of remote files, oops\n"); 375 return (NULL); 376 } 377 } 378 if (fgets(buf, sizeof (buf), ftemp) == NULL) { 379 fclose(ftemp), ftemp = NULL; 380 return (NULL); 381 } 382 if ((cp = index(buf, '\n')) != NULL) 383 *cp = '\0'; 384 return (buf); 385 } 386 387 char * 388 onoff(bool) 389 int bool; 390 { 391 392 return (bool ? "on" : "off"); 393 } 394 395 /* 396 * Show status. 397 */ 398 status(argc, argv) 399 char *argv[]; 400 { 401 402 if (connected) 403 printf("Connected to %s.\n", hostname); 404 else 405 printf("Not connected.\n"); 406 printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n", 407 modename, typename, formname, structname); 408 printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n", 409 onoff(verbose), onoff(bell), onoff(interactive), 410 onoff(doglob)); 411 printf("Hash mark printing: %s; Use of PORT cmds: %s\n", 412 onoff(hash), onoff(sendport)); 413 } 414 415 /* 416 * Set beep on cmd completed mode. 417 */ 418 /*VARARGS*/ 419 setbell() 420 { 421 422 bell = !bell; 423 printf("Bell mode %s.\n", onoff(bell)); 424 } 425 426 /* 427 * Turn on packet tracing. 428 */ 429 /*VARARGS*/ 430 settrace() 431 { 432 433 trace = !trace; 434 printf("Packet tracing %s.\n", onoff(trace)); 435 } 436 437 /* 438 * Toggle hash mark printing during transfers. 439 */ 440 /*VARARGS*/ 441 sethash() 442 { 443 444 hash = !hash; 445 printf("Hash mark printing %s", onoff(hash)); 446 if (hash) 447 printf(" (%d bytes/hash mark)", BUFSIZ); 448 printf(".\n"); 449 } 450 451 /* 452 * Turn on printing of server echo's. 453 */ 454 /*VARARGS*/ 455 setverbose() 456 { 457 458 verbose = !verbose; 459 printf("Verbose mode %s.\n", onoff(verbose)); 460 } 461 462 /* 463 * Toggle PORT cmd use before each data connection. 464 */ 465 /*VARARGS*/ 466 setport() 467 { 468 469 sendport = !sendport; 470 printf("Use of PORT cmds %s.\n", onoff(sendport)); 471 } 472 473 /* 474 * Turn on interactive prompting 475 * during mget, mput, and mdelete. 476 */ 477 /*VARARGS*/ 478 setprompt() 479 { 480 481 interactive = !interactive; 482 printf("Interactive mode %s.\n", onoff(interactive)); 483 } 484 485 /* 486 * Toggle metacharacter interpretation 487 * on local file names. 488 */ 489 /*VARARGS*/ 490 setglob() 491 { 492 493 doglob = !doglob; 494 printf("Globbing %s.\n", onoff(doglob)); 495 } 496 497 /* 498 * Set debugging mode on/off and/or 499 * set level of debugging. 500 */ 501 /*VARARGS*/ 502 setdebug(argc, argv) 503 char *argv[]; 504 { 505 int val; 506 507 if (argc > 1) { 508 val = atoi(argv[1]); 509 if (val < 0) { 510 printf("%s: bad debugging value.\n", argv[1]); 511 return; 512 } 513 } else 514 val = !debug; 515 debug = val; 516 if (debug) 517 options |= SO_DEBUG; 518 else 519 options &= ~SO_DEBUG; 520 printf("Debugging %s (debug=%d).\n", onoff(debug), debug); 521 } 522 523 /* 524 * Set current working directory 525 * on remote machine. 526 */ 527 cd(argc, argv) 528 char *argv[]; 529 { 530 531 if (argc < 2) { 532 strcat(line, " "); 533 printf("(remote-directory) "); 534 gets(&line[strlen(line)]); 535 makeargv(); 536 argc = margc; 537 argv = margv; 538 } 539 if (argc < 2) { 540 printf("%s remote-directory\n", argv[0]); 541 return; 542 } 543 (void) command("CWD %s", argv[1]); 544 } 545 546 /* 547 * Set current working directory 548 * on local machine. 549 */ 550 lcd(argc, argv) 551 char *argv[]; 552 { 553 char buf[MAXPATHLEN]; 554 555 if (argc < 2) 556 argc++, argv[1] = home; 557 if (argc != 2) { 558 printf("%s local-directory\n", argv[0]); 559 return; 560 } 561 if (!globulize(&argv[1])) 562 return; 563 if (chdir(argv[1]) < 0) { 564 perror(argv[1]); 565 return; 566 } 567 printf("Local directory now %s\n", getwd(buf)); 568 } 569 570 /* 571 * Delete a single file. 572 */ 573 delete(argc, argv) 574 char *argv[]; 575 { 576 577 if (argc < 2) { 578 strcat(line, " "); 579 printf("(remote-file) "); 580 gets(&line[strlen(line)]); 581 makeargv(); 582 argc = margc; 583 argv = margv; 584 } 585 if (argc < 2) { 586 printf("%s remote-file\n", argv[0]); 587 return; 588 } 589 (void) command("DELE %s", argv[1]); 590 } 591 592 /* 593 * Delete multiple files. 594 */ 595 mdelete(argc, argv) 596 char *argv[]; 597 { 598 char *cp; 599 600 if (argc < 2) { 601 strcat(line, " "); 602 printf("(remote-files) "); 603 gets(&line[strlen(line)]); 604 makeargv(); 605 argc = margc; 606 argv = margv; 607 } 608 if (argc < 2) { 609 printf("%s remote-files\n", argv[0]); 610 return; 611 } 612 while ((cp = remglob(argc, argv)) != NULL) 613 if (confirm(argv[0], cp)) 614 (void) command("DELE %s", cp); 615 } 616 617 /* 618 * Rename a remote file. 619 */ 620 renamefile(argc, argv) 621 char *argv[]; 622 { 623 624 if (argc < 2) { 625 strcat(line, " "); 626 printf("(from-name) "); 627 gets(&line[strlen(line)]); 628 makeargv(); 629 argc = margc; 630 argv = margv; 631 } 632 if (argc < 2) { 633 usage: 634 printf("%s from-name to-name\n", argv[0]); 635 return; 636 } 637 if (argc < 3) { 638 strcat(line, " "); 639 printf("(to-name) "); 640 gets(&line[strlen(line)]); 641 makeargv(); 642 argc = margc; 643 argv = margv; 644 } 645 if (argc < 3) 646 goto usage; 647 if (command("RNFR %s", argv[1]) == CONTINUE) 648 (void) command("RNTO %s", argv[2]); 649 } 650 651 /* 652 * Get a directory listing 653 * of remote files. 654 */ 655 ls(argc, argv) 656 char *argv[]; 657 { 658 char *cmd; 659 660 if (argc < 2) 661 argc++, argv[1] = NULL; 662 if (argc < 3) 663 argc++, argv[2] = "-"; 664 if (argc > 3) { 665 printf("usage: %s remote-directory local-file\n", argv[0]); 666 return; 667 } 668 cmd = argv[0][0] == 'l' ? "NLST" : "LIST"; 669 if (strcmp(argv[2], "-") && !globulize(&argv[2])) 670 return; 671 recvrequest(cmd, argv[2], argv[1], "w"); 672 } 673 674 /* 675 * Get a directory listing 676 * of multiple remote files. 677 */ 678 mls(argc, argv) 679 char *argv[]; 680 { 681 char *cmd, *mode, *cp, *dest; 682 683 if (argc < 2) { 684 strcat(line, " "); 685 printf("(remote-files) "); 686 gets(&line[strlen(line)]); 687 makeargv(); 688 argc = margc; 689 argv = margv; 690 } 691 if (argc < 3) { 692 strcat(line, " "); 693 printf("(local-file) "); 694 gets(&line[strlen(line)]); 695 makeargv(); 696 argc = margc; 697 argv = margv; 698 } 699 if (argc < 3) { 700 printf("%s remote-files local-file\n", argv[0]); 701 return; 702 } 703 dest = argv[argc - 1]; 704 argv[argc - 1] = NULL; 705 if (strcmp(dest, "-")) 706 if (globulize(&dest) && confirm("local-file", dest)) 707 return; 708 cmd = argv[0][1] == 'l' ? "NLST" : "LIST"; 709 for (mode = "w"; cp = remglob(argc, argv); mode = "a") 710 if (confirm(argv[0], cp)) 711 recvrequest(cmd, dest, cp, mode); 712 } 713 714 /* 715 * Do a shell escape 716 */ 717 shell(argc, argv) 718 char *argv[]; 719 { 720 int pid, status, (*old1)(), (*old2)(); 721 char shellnam[40], *shell, *namep; 722 char **cpp, **gargs; 723 724 old1 = signal (SIGINT, SIG_IGN); 725 old2 = signal (SIGQUIT, SIG_IGN); 726 if ((pid = fork()) == 0) { 727 for (pid = 3; pid < 20; pid++) 728 close(pid); 729 signal(SIGINT, SIG_DFL); 730 signal(SIGQUIT, SIG_DFL); 731 if (argc <= 1) { 732 shell = getenv("SHELL"); 733 if (shell == NULL) 734 shell = "/bin/sh"; 735 namep = rindex(shell,'/'); 736 if (namep == NULL) 737 namep = shell; 738 strcpy(shellnam,"-"); 739 strcat(shellnam, ++namep); 740 if (strcmp(namep, "sh") != 0) 741 shellnam[0] = '+'; 742 if (debug) { 743 printf ("%s\n", shell); 744 fflush (stdout); 745 } 746 execl(shell, shellnam, 0); 747 perror(shell); 748 exit(1); 749 } 750 cpp = &argv[1]; 751 if (argc > 2) { 752 if ((gargs = glob(cpp)) != NULL) 753 cpp = gargs; 754 if (globerr != NULL) { 755 printf("%s\n", globerr); 756 exit(1); 757 } 758 } 759 if (debug) { 760 register char **zip = cpp; 761 762 printf("%s", *zip); 763 while (*++zip != NULL) 764 printf(" %s", *zip); 765 printf("\n"); 766 fflush(stdout); 767 } 768 execvp(argv[1], cpp); 769 perror(argv[1]); 770 exit(1); 771 } 772 if (pid > 0) 773 while (wait(&status) != pid) 774 ; 775 signal(SIGINT, old1); 776 signal(SIGQUIT, old2); 777 if (pid == -1) 778 perror("Try again later"); 779 return (0); 780 } 781 782 /* 783 * Send new user information (re-login) 784 */ 785 user(argc, argv) 786 int argc; 787 char **argv; 788 { 789 char acct[80], *getpass(); 790 int n; 791 792 if (argc < 2) { 793 strcat(line, " "); 794 printf("(username) "); 795 gets(&line[strlen(line)]); 796 makeargv(); 797 argc = margc; 798 argv = margv; 799 } 800 if (argc > 4) { 801 printf("usage: %s username [password] [account]\n", argv[0]); 802 return (0); 803 } 804 n = command("USER %s", argv[1]); 805 if (n == CONTINUE) { 806 if (argc < 3 ) 807 argv[2] = getpass("Password: "), argc++; 808 n = command("PASS %s", argv[2]); 809 } 810 if (n == CONTINUE) { 811 if (argc < 4) { 812 printf("Account: "); (void) fflush(stdout); 813 (void) fgets(acct, sizeof(acct) - 1, stdin); 814 acct[strlen(acct) - 1] = '\0'; 815 argv[3] = acct; argc++; 816 } 817 n = command("ACCT %s", acct); 818 } 819 if (n != COMPLETE) { 820 fprintf(stderr, "Login failed.\n"); 821 return (0); 822 } 823 return (1); 824 } 825 826 /* 827 * Print working directory. 828 */ 829 /*VARARGS*/ 830 pwd() 831 { 832 833 (void) command("XPWD"); 834 } 835 836 /* 837 * Make a directory. 838 */ 839 makedir(argc, argv) 840 char *argv[]; 841 { 842 843 if (argc < 2) { 844 strcat(line, " "); 845 printf("(directory-name) "); 846 gets(&line[strlen(line)]); 847 makeargv(); 848 argc = margc; 849 argv = margv; 850 } 851 if (argc < 2) { 852 printf("%s directory-name\n", argv[0]); 853 return; 854 } 855 (void) command("XMKD %s", argv[1]); 856 } 857 858 /* 859 * Remove a directory. 860 */ 861 removedir(argc, argv) 862 char *argv[]; 863 { 864 865 if (argc < 2) { 866 strcat(line, " "); 867 printf("(directory-name) "); 868 gets(&line[strlen(line)]); 869 makeargv(); 870 argc = margc; 871 argv = margv; 872 } 873 if (argc < 2) { 874 printf("%s directory-name\n", argv[0]); 875 return; 876 } 877 (void) command("XRMD %s", argv[1]); 878 } 879 880 /* 881 * Send a line, verbatim, to the remote machine. 882 */ 883 quote(argc, argv) 884 char *argv[]; 885 { 886 int i; 887 char buf[BUFSIZ]; 888 889 if (argc < 2) { 890 strcat(line, " "); 891 printf("(command line to send) "); 892 gets(&line[strlen(line)]); 893 makeargv(); 894 argc = margc; 895 argv = margv; 896 } 897 if (argc < 2) { 898 printf("usage: %s line-to-send\n", argv[0]); 899 return; 900 } 901 strcpy(buf, argv[1]); 902 for (i = 2; i < argc; i++) { 903 strcat(buf, " "); 904 strcat(buf, argv[i]); 905 } 906 (void) command(buf); 907 } 908 909 /* 910 * Ask the other side for help. 911 */ 912 rmthelp(argc, argv) 913 char *argv[]; 914 { 915 int oldverbose = verbose; 916 917 verbose = 1; 918 (void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]); 919 verbose = oldverbose; 920 } 921 922 /* 923 * Terminate session and exit. 924 */ 925 /*VARARGS*/ 926 quit() 927 { 928 929 disconnect(); 930 exit(0); 931 } 932 933 /* 934 * Terminate session, but don't exit. 935 */ 936 disconnect() 937 { 938 extern FILE *cout; 939 extern int data; 940 941 if (!connected) 942 return; 943 (void) command("QUIT"); 944 (void) fclose(cout); 945 cout = NULL; 946 connected = 0; 947 data = -1; 948 } 949 950 confirm(cmd, file) 951 char *cmd, *file; 952 { 953 char line[BUFSIZ]; 954 955 if (!interactive) 956 return (1); 957 printf("%s %s? ", cmd, file); 958 fflush(stdout); 959 gets(line); 960 return (*line != 'n' && *line != 'N'); 961 } 962 963 fatal(msg) 964 char *msg; 965 { 966 967 fprintf(stderr, "ftp: %s\n"); 968 exit(1); 969 } 970 971 /* 972 * Glob a local file name specification with 973 * the expectation of a single return value. 974 * Can't control multiple values being expanded 975 * from the expression, we return only the first. 976 */ 977 globulize(cpp) 978 char **cpp; 979 { 980 char **globbed; 981 982 if (!doglob) 983 return (1); 984 globbed = glob(*cpp); 985 if (globerr != NULL) { 986 printf("%s: %s\n", *cpp, globerr); 987 if (globbed) 988 blkfree(globbed); 989 return (0); 990 } 991 if (globbed) { 992 *cpp = *globbed++; 993 /* don't waste too much memory */ 994 if (*globbed) 995 blkfree(globbed); 996 } 997 return (1); 998 } 999