/* * The author of this software is Eric Grosse. Copyright (c) 1993 by AT&T. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ /* recursive ls. Does not follow symbolic links. Symbolic links are in the draft of the new POSIX.1; we assume that version. It is also possible (see 10.1.7 of Zlotnick's POSIX guide) that the new version will include a clean version of ftw. It's not clear how it handles symbolic links. But when it someday becomes universally implemented, might as well convert this program to use it. According to pixie, half the execution time is spent in printf; according to prof, 90 percent of the time is in lstat. Anyway, don't bother with trivial optimizations. 28 Dec 91 ehg first version 13 May 92 ehg port to Plan9 13 Sep 92 ehg keep going after bad opendir 20 Sep 92 ehg to avoid "feature" of Plan9 union directories, build full pathnames instead of using chdir, chdir .. 20 Sep 92 ehg -t option to print Unixtime 1 Jan 93 ehg follow crc format for -t (but still only stat file) 5 Apr 93 ehg sort. strip off leading ./ 5 Jan 94 ehg keep going if stat fails 29 Dec 94 ehg complain about nonprinting characters in filenames 24 Feb 96 ehg skip naughty files, quietly unless -v */ #define _POSIX_SOURCE #include #include #include #include #include #include #include #include #ifdef NOSYMLINK #define lstat(f,s) stat(f,s) #endif typedef unsigned char uchar; uchar *path, **component; int ncomp; int verbose = 0; int prunixtime = 0; void * Malloc(size_t n) { void *p; if (!(p = malloc((size_t)n))){ fprintf(stderr,"unable to allocate %d bytes\n",n); exit(1); } return p; } void * Realloc(void *ReallocP, int ReallocN) { if(!(ReallocP = realloc(ReallocP, ReallocN))){ fprintf(stderr,"unable to reallocate %d bytes\n",ReallocN); exit(1); } return(ReallocP); } char* Strdup(char* s) { int l; if (!s) return 0; l = strlen(s)+1; return strcpy((char *)Malloc((size_t)l), s); } int cmp(const void *a, const void *b) { return strcmp(*(char**)a,*(char**)b); } int unsafe(char *s) { char c, *file = s; /* check for shell specials or whitespace, which would confuse later tools */ for (c = (*s); c != '\0'; c = (*++s)){ if (!isgraph(c) || strchr("\"'`$#;&|^<>()\\",c)){ strcpy((char*)component[ncomp-1],file); if(verbose) fprintf(stderr,"%s: dangerous filename\n",path); return(1); } } return(0); } char ** sorted_names(DIR *dir) { int maxnames = 250; /* starting guess at size of directory */ int nnames = 0; char **names = (char **)Malloc(maxnames*sizeof(*names)); char *file, *s; struct dirent *entry; while( entry = readdir(dir) ){ file = entry->d_name; if(strcmp(file,".")!=0 && strcmp(file,"..")!=0 && !unsafe(file)){ names[nnames++] = Strdup(file); if(nnames>=maxnames){ maxnames += 200; names = (char **)Realloc(names,maxnames*sizeof(char*)); } } } qsort((void*)names,nnames,sizeof(*names),cmp); names[nnames] = 0; return(names); } void free_names(char **names) { char **name; for(name = names; *name; name++) free(*name); free(names); } void pr(void) { struct stat s; DIR *dir; char buf[BUFSIZ]; int cc; char **names, **name; if(lstat((char*)path,&s)){ if(verbose){ fprintf(stderr,"%s: ",path); perror("can't stat"); } return; } if(access((char*)path,R_OK)){ if(verbose) fprintf(stderr,"can't access %s\n",path); return; } #ifndef NOSYMLINK if(S_ISLNK(s.st_mode)){ if(verbose){ printf("%s -> ",path); cc = readlink((char*)path,buf,BUFSIZ); if (cc >= 0) { buf[cc] = 0; printf("%s\n",buf); }else{ perror("can't readlink\n"); exit(1); } } }else #endif if(S_ISREG(s.st_mode)){ uchar *p = path; if(p[0]=='.' && p[1]=='/') p += 2; /* chop off leading ./ */ if(prunixtime) printf("%s %lu %lu 0\n",p,(unsigned long)s.st_mtime, (unsigned long)s.st_size); else printf("%s\n",p); }else if(S_ISDIR(s.st_mode)){ if(dir = opendir((char*)path)){ cc = strlen((char*)component[ncomp-1]); component[ncomp] = cc+component[ncomp-1]+1; component[ncomp][-1] = '/'; ncomp++; names = sorted_names(dir); for(name = names; *name; name++){ strcpy((char*)component[ncomp-1],*name); pr(); } free_names(names); ncomp--; component[ncomp][-1] = '\0'; closedir(dir); }else{ fflush(stdout); fprintf(stderr,"can't opendir %s\n",(char*)path); } }else{ fprintf(stderr,"%s mode %o\n",(char*)path,s.st_mode); } } void main(int argc, char **argv) { if(argc>1 && argv[1][0]=='-' && (argv[1][1]=='s'||argv[1][1]=='v')){ verbose = 1; prunixtime = 0; argc--; argv++; } if(argc>1 && !strcmp(argv[1],"-t") ){ prunixtime = 1; verbose = 0; argc--; argv++; } component = (uchar**)Malloc(1000*sizeof(uchar*)); /* should check against depth */ path = (uchar *)Malloc(4096); /* should be _POSIX_PATH_MAX */ component[0] = path; ncomp = 1; if(argc>1){ while(--argc){ strcpy((char*)path,*++argv); pr(); } }else{ strcpy((char*)path,"."); pr(); } }