|
|
1.1 root 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.