1: char *ckzv = "Unix file support, 4C(032) 25 Jul 85"; 2: 3: /* C K U F I O -- Kermit file system support for Unix systems */ 4: 5: /* 6: Author: Frank da Cruz (SY.FDC@CU20B), 7: Columbia University Center for Computing Activities, January 1985. 8: Copyright (C) 1985, Trustees of Columbia University in the City of New York. 9: Permission is granted to any individual or institution to use, copy, or 10: redistribute this software so long as it is not sold for profit, provided this 11: copyright notice is retained. 12: */ 13: /* Includes */ 14: 15: #include "ckcker.h" /* Kermit definitions */ 16: #include "ckcdeb.h" /* Typedefs, debug formats, etc */ 17: #include <ctype.h> /* Character types */ 18: #include <stdio.h> /* Standard i/o */ 19: #include <sys/types.h> /* Data types */ 20: #include <sys/dir.h> /* Directory structure */ 21: #include <sys/stat.h> /* File status */ 22: #include <pwd.h> /* Password file for shell name */ 23: 24: /* Berkeley Unix Version 4.x */ 25: /* 4.1bsd support added by Charles E Brooks, EDN-VAX */ 26: 27: #ifdef BSD4 28: #ifdef MAXNAMLEN 29: #define BSD42 30: char *ckzsys = " 4.2 BSD"; 31: #else 32: #ifdef FT17 33: #define BSD41 34: char *ckzsys = " For:Pro Fortune 1.7"; 35: #else 36: #define BSD41 37: char *ckzsys = " 4.1 BSD"; 38: #endif 39: #endif 40: #endif 41: 42: /* 2.9bsd support contributed by Bradley Smith, UCLA */ 43: #ifdef BSD29 44: char *ckzsys = " 2.9 BSD"; 45: #endif 46: 47: /* Version 7 Unix */ 48: #ifdef V7 49: char *ckzsys = " Version 7 Unix"; 50: #endif 51: 52: /* DEC Professional-300 series with Venturcom Venix v1 */ 53: #ifdef PROVX1 54: char *ckzsys = " DEC Pro-3xx/Venix v1"; 55: #endif 56: 57: /* NCR Tower support contributed by John Bray, Auburn, AL. */ 58: /* Tower OS is like Sys III but with BSD features -- mostly follows BSD. */ 59: #ifdef TOWER1 60: char *ckzsys = " NCR Tower 1632, OS 1.02"; 61: #endif 62: 63: /* Sys III/V, Xenix, PC/IX,... support by Herm Fischer, Litton Data Systems */ 64: #ifdef UXIII 65: #ifdef XENIX 66: char *ckzsys = " Xenix/286"; 67: #else 68: #ifdef PCIX 69: char *ckzsys = " PC/IX"; 70: #else 71: #ifdef ISIII 72: char *ckzsys = " Interactive Systems Corp, System III"; 73: #else 74: char *ckzsys = " AT&T System III/System V"; 75: #endif 76: #endif 77: #endif 78: #endif 79: 80: /* Definitions of some Unix system commands */ 81: 82: char *DIRCMD = "ls -l "; /* For directory listing */ 83: char *DELCMD = "rm -f "; /* For file deletion */ 84: char *TYPCMD = "cat "; /* For typing a file */ 85: char *PWDCMD = "pwd "; /* For saying where I am */ 86: 87: #ifdef BSD4 88: char *SPACMD = "pwd ; quota ; df ."; /* Space/quota of current directory */ 89: #else 90: char *SPACMD = "df "; 91: #endif 92: 93: char *SPACM2 = "df "; /* For space in specified directory */ 94: 95: #ifdef BSD4 96: char *WHOCMD = "finger "; /* For seeing who's logged in */ 97: #else 98: char *WHOCMD = "who "; /* For seeing who's logged in */ 99: #endif 100: 101: /* 102: Functions (n is one of the predefined file numbers from ckermi.h): 103: 104: zopeni(n,name) -- Opens an existing file for input. 105: zopeno(n,name) -- Opens a new file for output. 106: zclose(n) -- Closes a file. 107: zchin(n,&c) -- Gets the next character from an input file. 108: zsout(n,s) -- Write a null-terminated string to output file, buffered. 109: zsoutl(n,s) -- Like zsout, but appends a line terminator. 110: zsoutx(n,s,x) -- Write x characters to output file, unbuffered. 111: zchout(n,c) -- Add a character to an output file, unbuffered. 112: zchki(name) -- Check if named file exists and is readable, return size. 113: zchko(name) -- Check if named file can be created. 114: znewn(name,s) -- Make a new unique file name based on the given name. 115: zdelet(name) -- Delete the named file. 116: zxpand(string) -- Expands the given wildcard string into a list of files. 117: znext(string) -- Returns the next file from the list in "string". 118: zxcmd(cmd) -- Execute the command in a lower fork. 119: zclosf() -- Close input file associated with zxcmd()'s lower fork. 120: zrtol(n1,n2) -- Convert remote filename into local form. 121: zltor(n1,n2) -- Convert local filename into remote form. 122: zchdir(dirnam) -- Change working directory. 123: zhome() -- Return pointer to home directory name string. 124: zkself() -- Kill self, log out own job. 125: */ 126: 127: 128: #ifdef FT17 129: #define PROVX1 130: #endif 131: #ifndef PROVX1 132: #include <sys/file.h> /* File access */ 133: #endif 134: #ifdef FT17 135: #undef PROVX1 136: #endif 137: 138: /* Some systems define these in include files, others don't... */ 139: #ifndef R_OK 140: #define R_OK 4 /* For access */ 141: #endif 142: 143: #ifndef W_OK 144: #define W_OK 2 145: #endif 146: 147: #ifdef PROVX1 148: #define MAXNAMLEN DIRSIZ /* Max file name length */ 149: #endif 150: 151: #ifdef UXIII 152: #include <fcntl.h> 153: #define MAXNAMLEN DIRSIZ 154: #endif 155: 156: #ifndef O_RDONLY 157: #define O_RDONLY 000 158: #endif 159: 160: #ifndef MAXNAMLEN 161: #define MAXNAMLEN 14 /* If still not defined... */ 162: #endif 163: 164: #ifdef PROVX1 165: #define MAXWLD 50 /* Maximum wildcard filenames */ 166: #else 167: #define MAXWLD 500 168: #endif 169: 170: /* Declarations */ 171: 172: FILE *fp[ZNFILS] = { /* File pointers */ 173: NULL, NULL, NULL, NULL, NULL, NULL, NULL }; 174: 175: static int pid; /* pid of child fork */ 176: static int fcount; /* Number of files in wild group */ 177: static char nambuf[MAXNAMLEN+1]; /* Buffer for a filename */ 178: char *malloc(), *getenv(), *strcpy(); /* System functions */ 179: extern errno; /* System error code */ 180: 181: static char *mtchs[MAXWLD], /* Matches found for filename */ 182: **mtchptr; /* Pointer to current match */ 183: 184: /* Z K S E L F -- Kill Self: log out own job, if possible. */ 185: 186: zkself() { /* For "bye", but no guarantee! */ 187: #ifdef PROVX1 188: return(kill(0,9)); 189: #else 190: #ifdef V7 191: return(kill(0,9)); 192: #else 193: #ifdef TOWER1 194: return(kill(0,9)); 195: #else 196: #ifdef FT17 197: return(kill(0,9)); 198: #else 199: return(kill(getppid(),1)); 200: #endif 201: #endif 202: #endif 203: #endif 204: } 205: 206: /* Z O P E N I -- Open an existing file for input. */ 207: 208: zopeni(n,name) int n; char *name; { 209: debug(F111," zopeni",name,n); 210: debug(F101," fp","",(int) fp[n]); 211: if (chkfn(n) != 0) return(0); 212: if (n == ZSYSFN) { /* Input from a system function? */ 213: debug(F110," invoking zxcmd",name,0); 214: return(zxcmd(name)); /* Try to fork the command */ 215: } 216: if (n == ZSTDIO) { /* Standard input? */ 217: if (isatty(0)) { 218: ermsg("Terminal input not allowed"); 219: debug(F110,"zopeni: attempts input from unredirected stdin","",0); 220: return(0); 221: } 222: fp[ZIFILE] = stdin; 223: return(1); 224: } 225: fp[n] = fopen(name,"r"); /* Real file. */ 226: debug(F111," zopeni", name, (int) fp[n]); 227: if (fp[n] == NULL) perror("zopeni"); 228: return((fp[n] != NULL) ? 1 : 0); 229: } 230: 231: /* Z O P E N O -- Open a new file for output. */ 232: 233: zopeno(n,name) int n; char *name; { 234: debug(F111," zopeno",name,n); 235: if (chkfn(n) != 0) return(0); 236: if ((n == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */ 237: fp[ZOFILE] = stdout; 238: debug(F101," fp[]=stdout", "", (int) fp[n]); 239: return(1); 240: } 241: fp[n] = fopen(name,"w"); /* A real file, try to open */ 242: if (fp[n] == NULL) { 243: perror("zopeno can't open"); 244: } else { 245: chown(name, getuid(), getgid()); /* In case set[gu]id */ 246: if (n == ZDFILE) setbuf(fp[n],NULL); /* Debugging file unbuffered */ 247: } 248: debug(F101, " fp[n]", "", (int) fp[n]); 249: return((fp[n] != NULL) ? 1 : 0); 250: } 251: 252: /* Z C L O S E -- Close the given file. */ 253: 254: /* Returns 0 if arg out of range, 1 if successful, -1 if close failed. */ 255: 256: zclose(n) int n; { 257: int x; 258: if (chkfn(n) < 1) return(0); /* Check range of n */ 259: if ((n == ZIFILE) && fp[ZSYSFN]) { /* If system function */ 260: x = zclosf(); /* do it specially */ 261: } else { 262: if ((fp[n] != stdout) && (fp[n] != stdin)) x = fclose(fp[n]); 263: fp[n] = NULL; 264: } 265: return((x == EOF) ? -1 : 1); 266: } 267: 268: /* Z C H I N -- Get a character from the input file. */ 269: 270: /* Returns -1 if EOF, 0 otherwise with character returned in argument */ 271: 272: zchin(n,c) int n; char *c; { 273: int a; 274: if (chkfn(n) < 1) return(-1); 275: a = getc(fp[n]); 276: if (a == EOF) return(-1); 277: *c = a & 0377; 278: return(0); 279: } 280: 281: /* Z S O U T -- Write a string to the given file, buffered. */ 282: 283: zsout(n,s) int n; char *s; { 284: if (chkfn(n) < 1) return(-1); 285: fputs(s,fp[n]); 286: return(0); 287: } 288: 289: /* Z S O U T L -- Write string to file, with line terminator, buffered */ 290: 291: zsoutl(n,s) int n; char *s; { 292: if (chkfn(n) < 1) return(-1); 293: fputs(s,fp[n]); 294: fputs("\n",fp[n]); 295: return(0); 296: } 297: 298: /* Z S O U T X -- Write x characters to file, unbuffered. */ 299: 300: zsoutx(n,s,x) int n, x; char *s; { 301: if (chkfn(n) < 1) return(-1); 302: return(write(fp[n]->_file,s,x)); 303: } 304: 305: 306: /* Z C H O U T -- Add a character to the given file. */ 307: 308: /* Should return 0 or greater on success, -1 on failure (e.g. disk full) */ 309: 310: zchout(n,c) int n; char c; { 311: if (chkfn(n) < 1) return(-1); 312: if (n == ZSFILE) 313: return(write(fp[n]->_file,&c,1)); /* Use unbuffered for session log */ 314: else { /* Buffered for everything else */ 315: if (putc(c,fp[n]) == EOF) /* If true, maybe there was an error */ 316: return(ferror(fp[n])); /* Check to make sure */ 317: else /* Otherwise... */ 318: return(0); /* There was no error. */ 319: } 320: } 321: 322: /* C H K F N -- Internal function to verify file number is ok */ 323: 324: /* 325: Returns: 326: -1: File number n is out of range 327: 0: n is in range, but file is not open 328: 1: n in range and file is open 329: */ 330: chkfn(n) int n; { 331: switch (n) { 332: case ZCTERM: 333: case ZSTDIO: 334: case ZIFILE: 335: case ZOFILE: 336: case ZDFILE: 337: case ZTFILE: 338: case ZPFILE: 339: case ZSFILE: 340: case ZSYSFN: break; 341: default: 342: debug(F101,"chkfn: file number out of range","",n); 343: fprintf(stderr,"?File number out of range - %d\n",n); 344: return(-1); 345: } 346: return( (fp[n] == NULL) ? 0 : 1 ); 347: } 348: 349: /* Z C H K I -- Check if input file exists and is readable */ 350: 351: /* 352: Returns: 353: >= 0 if the file can be read (returns the size). 354: -1 if file doesn't exist or can't be accessed, 355: -2 if file exists but is not readable (e.g. a directory file). 356: -3 if file exists but protected against read access. 357: */ 358: /* 359: For Berkeley Unix, a file must be of type "regular" to be readable. 360: Directory files, special files, and symbolic links are not readable. 361: */ 362: long 363: zchki(name) char *name; { 364: struct stat buf; 365: int x; long y; 366: 367: x = stat(name,&buf); 368: if (x < 0) { 369: debug(F111,"zchki stat fails",name,errno); 370: return(-1); 371: } 372: x = buf.st_mode & S_IFMT; /* Isolate file format field */ 373: if ((x != 0) && (x != S_IFREG)) { 374: debug(F111,"zchki skipping:",name,x); 375: return(-2); 376: } 377: debug(F111,"zchki stat ok:",name,x); 378: 379: if ((x = access(name,R_OK)) < 0) { /* Is the file accessible? */ 380: debug(F111," access failed:",name,x); /* No */ 381: return(-3); 382: } else { 383: y = buf.st_size; 384: debug(F111," access ok:",name,(int) y); /* Yes */ 385: return( (y > -1) ? y : 0 ); 386: } 387: } 388: 389: /* Z C H K O -- Check if output file can be created */ 390: 391: /* 392: Returns -1 if write permission for the file would be denied, 0 otherwise. 393: */ 394: zchko(name) char *name; { 395: int i, x; 396: char s[50], *sp; 397: 398: sp = s; /* Make a copy, get length */ 399: x = 0; 400: while ((*sp++ = *name++) != '\0') 401: x++; 402: if (x == 0) return(-1); /* If no filename, fail. */ 403: 404: debug(F101," length","",x); 405: for (i = x; i > 0; i--) /* Strip filename. */ 406: if (s[i-1] == '/') break; 407: 408: debug(F101," i","",i); 409: if (i == 0) /* If no path, use current directory */ 410: strcpy(s,"./"); 411: else /* Otherwise, use given one. */ 412: s[i] = '\0'; 413: 414: x = access(s,W_OK); /* Check access of path. */ 415: if (x < 0) { 416: debug(F111,"zchko access failed:",s,errno); 417: return(-1); 418: } else { 419: debug(F111,"zchko access ok:",s,x); 420: return(0); 421: } 422: } 423: 424: /* Z D E L E T -- Delete the named file. */ 425: 426: zdelet(name) char *name; { 427: unlink(name); 428: } 429: 430: 431: /* Z R T O L -- Convert remote filename into local form */ 432: 433: /* For UNIX, this means changing uppercase letters to lowercase. */ 434: 435: zrtol(name,name2) char *name, *name2; { 436: for ( ; *name != '\0'; name++) { 437: *name2++ = isupper(*name) ? tolower(*name) : *name; 438: } 439: *name2 = '\0'; 440: debug(F110,"zrtol:",name2,0); 441: } 442: 443: 444: /* Z L T O R -- Local TO Remote */ 445: 446: /* Convert filename from local format to common (remote) form. */ 447: 448: zltor(name,name2) char *name, *name2; { 449: char work[100], *cp, *pp; 450: int dc = 0; 451: 452: debug(F110,"zltor",name,0); 453: pp = work; 454: for (cp = name; *cp != '\0'; cp++) { /* strip path name */ 455: if (*cp == '/') { 456: dc = 0; 457: pp = work; 458: } 459: else if (islower(*cp)) *pp++ = toupper(*cp); /* Uppercase letters */ 460: else if (*cp == '~') *pp++ = 'X'; /* Change tilde to 'X' */ 461: else if (*cp == '#') *pp++ = 'X'; /* Change number sign to 'X' */ 462: else if ((*cp == '.') && (++dc > 1)) *pp++ = 'X'; /* & extra dots */ 463: else *pp++ = *cp; 464: } 465: *pp = '\0'; /* Tie it off. */ 466: cp = name2; /* If nothing before dot, */ 467: if (*work == '.') *cp++ = 'X'; /* insert 'X' */ 468: strcpy(cp,work); 469: debug(F110," name2",name2,0); 470: } 471: 472: 473: /* Z C H D I R -- Change directory */ 474: 475: zchdir(dirnam) char *dirnam; { 476: char *hd; 477: if (*dirnam == '\0') hd = getenv("HOME"); 478: else hd = dirnam; 479: return((chdir(hd) == 0) ? 1 : 0); 480: } 481: 482: 483: /* Z H O M E -- Return pointer to user's home directory */ 484: 485: char * 486: zhome() { 487: return(getenv("HOME")); 488: } 489: 490: /* Z X C M D -- Run a system command so its output can be read like a file */ 491: 492: zxcmd(comand) char *comand; { 493: int pipes[2]; 494: if (pipe(pipes) != 0) return(0); /* can't make pipe, fail */ 495: if ((pid = fork()) == 0) { /* child */ 496: 497: /*#if BSD4*/ /* Code from Dave Tweten@AMES-NAS */ 498: /* readapted to use getpwuid to find login shell */ 499: /* -- H. Fischer */ 500: char *shpath, *shname, *shptr; /* to find desired shell */ 501: struct passwd *p; 502: extern struct passwd * getpwuid(); 503: extern int getuid(); 504: char *defShel = "/bin/sh"; /* default shell */ 505: /*#endif*/ 506: 507: close(pipes[0]); /* close input side of pipe */ 508: close(0); /* close stdin */ 509: if (open("/dev/null",0) < 0) return(0); /* replace input by null */ 510: 511: #ifndef UXIII 512: dup2(pipes[1],1); /* replace stdout & stderr */ 513: dup2(pipes[1],2); /* by the pipe */ 514: #else 515: close(1); /* simulate dup2 */ 516: if (dup(pipes[1]) != 1 ) 517: conol("trouble duping stdout in routine zxcmd\n"); 518: close(2); /* simulate dup2 */ 519: if (dup(pipes[1]) != 2 ) 520: conol("trouble duping stderr in routine zxcmd\n"); 521: #endif 522: 523: close(pipes[1]); /* get rid of this copy of the pipe */ 524: 525: /**** shptr = shname = shpath = getenv("SHELL"); /* What shell? */ 526: p = getpwuid( getuid() ); /* get login data */ 527: if ( p == (struct passwd *) NULL || !*(p->pw_shell) ) shpath = defShel; 528: else shpath = p->pw_shell; 529: shptr = shname = shpath; 530: while (*shptr != '\0') if (*shptr++ == '/') shname = shptr; 531: debug(F100,"zxcmd...","",0); 532: debug(F110,shpath,shname,0); 533: execl(shpath,shname,"-c",comand,0); /* Execute the command */ 534: 535: /**** execl("/bin/sh","sh","-c",comand,0); /* Execute the command */ 536: 537: exit(0); /* just punt if it didnt work */ 538: } 539: close(pipes[1]); /* don't need the output side */ 540: fp[ZIFILE] = fdopen(pipes[0],"r"); /* open a stream for it */ 541: fp[ZSYSFN] = fp[ZIFILE]; /* Remember. */ 542: return(1); 543: } 544: 545: /* Z C L O S F - wait for the child fork to terminate and close the pipe. */ 546: 547: zclosf() { 548: int wstat; 549: fclose(fp[ZIFILE]); 550: fp[ZIFILE] = fp[ZSYSFN] = NULL; 551: while ((wstat = wait(0)) != pid && wstat != -1) ; 552: return(1); 553: } 554: 555: /* Z X P A N D -- Expand a wildcard string into an array of strings */ 556: /* 557: Returns the number of files that match fn1, with data structures set up 558: so that first file (if any) will be returned by the next znext() call. 559: */ 560: zxpand(fn) char *fn; { 561: fcount = fgen(fn,mtchs,MAXWLD); /* Look up the file. */ 562: if (fcount > 0) { 563: mtchptr = mtchs; /* Save pointer for next. */ 564: } 565: debug(F111,"zxpand",mtchs[0],fcount); 566: return(fcount); 567: } 568: 569: 570: /* Z N E X T -- Get name of next file from list created by zxpand(). */ 571: /* 572: Returns >0 if there's another file, with its name copied into the arg string, 573: or 0 if no more files in list. 574: */ 575: znext(fn) char *fn; { 576: if (fcount-- > 0) strcpy(fn,*mtchptr++); 577: else *fn = '\0'; 578: debug(F111,"znext",fn,fcount+1); 579: return(fcount+1); 580: } 581: 582: 583: /* Z N E W N -- Make a new name for the given file */ 584: 585: znewn(fn,s) char *fn, **s; { 586: static char buf[100]; 587: char *bp, *xp; 588: int len = 0, n = 0, d = 0, t; 589: #ifdef MAXNAMLEN 590: int max = MAXNAMLEN; 591: #else 592: int max = 14; 593: #endif 594: 595: bp = buf; 596: while (*fn) { /* Copy name into buf */ 597: *bp++ = *fn++; 598: len++; 599: } 600: if (len > max-3) bp -= 3; /* Don't let it get too long */ 601: 602: *bp++ = '*'; /* Put a star on the end */ 603: *bp-- = '\0'; 604: 605: n = zxpand(buf); /* Expand the resulting wild name */ 606: 607: while (n-- > 0) { /* Find any existing name~d files */ 608: xp = *mtchptr++; 609: xp += len; 610: if (*xp == '~') { 611: t = atoi(xp+1); 612: if (t > d) d = t; /* Get maximum d */ 613: } 614: } 615: sprintf(bp,"~%d",d+1); /* Make name~(d+1) */ 616: *s = buf; 617: } 618: 619: /* Directory Functions for Unix, written by Jeff Damens, CUCCA, 1984. */ 620: 621: /* 622: * The path structure is used to represent the name to match. 623: * Each slash-separated segment of the name is kept in one 624: * such structure, and they are linked together, to make 625: * traversing the name easier. 626: */ 627: 628: struct path { 629: char npart[MAXNAMLEN]; /* name part of path segment */ 630: struct path *fwd; /* forward ptr */ 631: }; 632: 633: #ifdef PROVX1 634: #define SSPACE 500 635: #else 636: #define SSPACE 2000 /* size of string-generating buffer */ 637: #endif 638: static char sspace[SSPACE]; /* buffer to generate names in */ 639: static char *freeptr,**resptr; /* copies of caller's arguments */ 640: static int remlen; /* remaining length in caller's array*/ 641: static int numfnd; /* number of matches found */ 642: 643: /* 644: * splitpath: 645: * takes a string and splits the slash-separated portions into 646: * a list of path structures. Returns the head of the list. The 647: * structures are allocated by malloc, so they must be freed. 648: * Splitpath is used internally by the filename generator. 649: * 650: * Input: A string. 651: * Returns: A linked list of the slash-separated segments of the input. 652: */ 653: 654: struct path * 655: splitpath(p) 656: char *p; 657: { 658: struct path *head,*cur,*prv; 659: int i; 660: head = prv = NULL; 661: if (*p == '/') p++; /* skip leading slash */ 662: while (*p != '\0') 663: { 664: cur = (struct path *) malloc(sizeof (struct path)); 665: debug(F101,"splitpath malloc","",cur); 666: if (cur == NULL) fatal("malloc fails in splitpath()"); 667: cur -> fwd = NULL; 668: if (head == NULL) head = cur; 669: else prv -> fwd = cur; /* link into chain */ 670: prv = cur; 671: for (i=0; i < MAXNAMLEN && *p != '/' && *p != '\0'; i++) 672: cur -> npart[i] = *p++; 673: cur -> npart[i] = '\0'; /* end this segment */ 674: if (i >= MAXNAMLEN) while (*p != '/' && *p != '\0') p++; 675: if (*p == '/') p++; 676: } 677: return(head); 678: } 679: 680: /* 681: * fgen: 682: * This is the actual name generator. It is passed a string, 683: * possibly containing wildcards, and an array of character pointers. 684: * It finds all the matching filenames and stores them into the array. 685: * The returned strings are allocated from a static buffer local to 686: * this module (so the caller doesn't have to worry about deallocating 687: * them); this means that successive calls to fgen will wipe out 688: * the results of previous calls. This isn't a problem here 689: * because we process one wildcard string at a time. 690: * 691: * Input: a wildcard string, an array to write names to, the 692: * length of the array. 693: * Returns: the number of matches. The array is filled with filenames 694: * that matched the pattern. If there wasn't enough room in the 695: * array, -1 is returned. 696: * By: Jeff Damens, CUCCA, 1984. 697: */ 698: 699: fgen(pat,resarry,len) 700: char *pat,*resarry[]; 701: int len; 702: { 703: struct path *head; 704: char scratch[100],*sptr; 705: head = splitpath(pat); 706: if (*pat == '/') 707: { 708: scratch[0] = '/'; 709: sptr = scratch+1; 710: } 711: else 712: { 713: strcpy(scratch,"./"); 714: sptr = scratch+2; 715: } /* init buffer correctly */ 716: numfnd = 0; /* none found yet */ 717: freeptr = sspace; /* this is where matches are copied */ 718: resptr = resarry; /* static copies of these so*/ 719: remlen = len; /* recursive calls can alter them */ 720: traverse(head,scratch,sptr); /* go walk the directory tree */ 721: for (; head != NULL; head = head -> fwd) 722: free(head); /* return the path segments */ 723: return(numfnd); /* and return the number of matches */ 724: } 725: 726: /* traverse: 727: * Walks the directory tree looking for matches to its arguments. 728: * The algorithm is, briefly: 729: * If the current pattern segment contains no wildcards, that 730: * segment is added to what we already have. If the name so far 731: * exists, we call ourselves recursively with the next segment 732: * in the pattern string; otherwise, we just return. 733: * 734: * If the current pattern segment contains wildcards, we open the name 735: * we've accumulated so far (assuming it is really a directory), then read 736: * each filename in it, and, if it matches the wildcard pattern segment, add 737: * that filename to what we have so far and call ourselves recursively on the 738: * next segment. 739: * 740: * Finally, when no more pattern segments remain, we add what's accumulated 741: * so far to the result array and increment the number of matches. 742: * 743: * Input: a pattern path list (as generated by splitpath), a string 744: * pointer that points to what we've traversed so far (this 745: * can be initialized to "/" to start the search at the root 746: * directory, or to "./" to start the search at the current 747: * directory), and a string pointer to the end of the string 748: * in the previous argument. 749: * Returns: nothing. 750: */ 751: traverse(pl,sofar,endcur) 752: struct path *pl; 753: char *sofar,*endcur; 754: { 755: #ifdef BSD42 756: DIR *fd, *opendir(); 757: struct direct *dirbuf; 758: #else 759: int fd; 760: struct direct dir_entry; 761: struct direct *dirbuf = &dir_entry; 762: #endif 763: struct stat statbuf; 764: if (pl == NULL) 765: { 766: *--endcur = '\0'; /* end string, overwrite trailing / */ 767: addresult(sofar); 768: return; 769: } 770: if (!iswild(pl -> npart)) 771: { 772: strcpy(endcur,pl -> npart); 773: endcur += strlen(pl -> npart); 774: *endcur = '\0'; /* end current string */ 775: if (stat(sofar,&statbuf) == 0) /* if current piece exists */ 776: { 777: *endcur++ = '/'; /* add slash to end */ 778: *endcur = '\0'; /* and end the string */ 779: traverse(pl -> fwd,sofar,endcur); 780: } 781: return; 782: } 783: /* cont'd... */ 784: 785: /*...traverse, cont'd */ 786: 787: /* segment contains wildcards, have to search directory */ 788: *endcur = '\0'; /* end current string */ 789: if (stat(sofar,&statbuf) == -1) return; /* doesn't exist, forget it */ 790: if ((statbuf.st_mode & S_IFDIR) == 0) return; /* not a directory, skip */ 791: #ifdef BSD42 /* ==BSD4 */ 792: if ((fd = opendir(sofar)) == NULL) return; /* can't open, forget it */ 793: while (dirbuf = readdir(fd)) 794: #else 795: if ((fd = open(sofar,O_RDONLY)) < 0) return; /* can't open, forget it */ 796: while ( read(fd,dirbuf,sizeof dir_entry) ) 797: #endif 798: { 799: strncpy(nambuf,dirbuf->d_name,MAXNAMLEN); /* Get a null terminated copy!!! */ 800: nambuf[MAXNAMLEN] = '\0'; 801: if (dirbuf->d_ino != 0 && match(pl -> npart,nambuf)) { 802: char *eos; 803: strcpy(endcur,nambuf); 804: eos = endcur + strlen(nambuf); 805: *eos = '/'; /* end this segment */ 806: traverse(pl -> fwd,sofar,eos+1); 807: } 808: } 809: #ifdef BSD42 /* ==BSD4 */ 810: closedir(fd); 811: #else 812: close(fd); 813: #endif 814: } 815: 816: /* 817: * addresult: 818: * Adds a result string to the result array. Increments the number 819: * of matches found, copies the found string into our string 820: * buffer, and puts a pointer to the buffer into the caller's result 821: * array. Our free buffer pointer is updated. If there is no 822: * more room in the caller's array, the number of matches is set to -1. 823: * Input: a result string. 824: * Returns: nothing. 825: */ 826: 827: addresult(str) 828: char *str; 829: { 830: int l; 831: if (strncmp(str,"./",2) == 0) str += 2; 832: if (--remlen < 0) { 833: numfnd = -1; 834: return; 835: } 836: l = strlen(str) + 1; /* size this will take up */ 837: if ((freeptr + l) > &sspace[SSPACE]) { 838: numfnd = -1; /* do not record if not enough space */ 839: return; 840: } 841: strcpy(freeptr,str); 842: *resptr++ = freeptr; 843: freeptr += l; 844: numfnd++; 845: } 846: 847: iswild(str) 848: char *str; 849: { 850: char c; 851: while ((c = *str++) != '\0') 852: if (c == '*' || c == '?') return(1); 853: return(0); 854: } 855: 856: /* 857: * match: 858: * pattern matcher. Takes a string and a pattern possibly containing 859: * the wildcard characters '*' and '?'. Returns true if the pattern 860: * matches the string, false otherwise. 861: * by: Jeff Damens, CUCCA 862: * 863: * Input: a string and a wildcard pattern. 864: * Returns: 1 if match, 0 if no match. 865: */ 866: 867: match(pattern,string) char *pattern,*string; { 868: char *psave,*ssave; /* back up pointers for failure */ 869: psave = ssave = NULL; 870: while (1) { 871: for (; *pattern == *string; pattern++,string++) /* skip first */ 872: if (*string == '\0') return(1); /* end of strings, succeed */ 873: if (*string != '\0' && *pattern == '?') { 874: pattern++; /* '?', let it match */ 875: string++; 876: } else if (*pattern == '*') { /* '*' ... */ 877: psave = ++pattern; /* remember where we saw it */ 878: ssave = string; /* let it match 0 chars */ 879: } else if (ssave != NULL && *ssave != '\0') { /* if not at end */ 880: /* ...have seen a star */ 881: string = ++ssave; /* skip 1 char from string */ 882: pattern = psave; /* and back up pattern */ 883: } else return(0); /* otherwise just fail */ 884: } 885: }