BSD 4_3 release
[unix-history] / usr / src / usr.bin / find / find.c
index 3138899..e948647 100644 (file)
@@ -1,19 +1,24 @@
-static char *sccsid = "@(#)find.c      4.6 (Berkeley) %G%";
-/*     find    COMPILE:        cc -o find -s -O -i find.c -lS  */
+#ifndef        lint
+static char *sccsid = "@(#)find.c      4.17 (Berkeley) 1/31/86";
+#endif
+
 #include <stdio.h>
 #include <sys/param.h>
 #include <sys/dir.h>
 #include <sys/stat.h>
 #include <stdio.h>
 #include <sys/param.h>
 #include <sys/dir.h>
 #include <sys/stat.h>
+
 #define A_DAY  86400L /* a day full of seconds */
 #define EQ(x, y)       (strcmp(x, y)==0)
 
 int    Randlast;
 #define A_DAY  86400L /* a day full of seconds */
 #define EQ(x, y)       (strcmp(x, y)==0)
 
 int    Randlast;
-char   Pathname[200];
+char   Pathname[MAXPATHLEN+1];
+
+#define MAXNODES       100
 
 struct anode {
        int (*F)();
        struct anode *L, *R;
 
 struct anode {
        int (*F)();
        struct anode *L, *R;
-} Node[100];
+} Node[MAXNODES];
 int Nn;  /* number of nodes */
 char   *Fname;
 long   Now;
 int Nn;  /* number of nodes */
 char   *Fname;
 long   Now;
@@ -29,6 +34,9 @@ int   Wct = 2560;
 
 long   Newer;
 
 
 long   Newer;
 
+int    Xdev = 1;       /* true if SHOULD cross devices (file systems) */
+struct stat Devstat;   /* stats of each argument path's file system */
+
 struct stat Statb;
 
 struct anode   *exp(),
 struct stat Statb;
 
 struct anode   *exp(),
@@ -37,22 +45,66 @@ struct      anode   *exp(),
                *e3(),
                *mk();
 char   *nxtarg();
                *e3(),
                *mk();
 char   *nxtarg();
-char   Home[128];
+char   Home[MAXPATHLEN + 1];
 long   Blocks;
 char *rindex();
 char *sbrk();
 long   Blocks;
 char *rindex();
 char *sbrk();
-main(argc, argv) char *argv[];
+
+/*
+ * SEE ALSO:   updatedb, bigram.c, code.c
+ *             Usenix ;login:, February/March, 1983, p. 8.
+ *
+ * REVISIONS:  James A. Woods, Informatics General Corporation,
+ *             NASA Ames Research Center, 6/81.
+ *
+ *             The second form searches a pre-computed filelist
+ *             (constructed nightly by /usr/lib/crontab) which is
+ *             compressed by updatedb (v.i.z.)  The effect of
+ *                     find <name>
+ *             is similar to
+ *                     find / +0 -name "*<name>*" -print
+ *             but much faster.
+ *
+ *             8/82 faster yet + incorporation of bigram coding -- jaw
+ *
+ *             1/83 incorporate glob-style matching -- jaw
+ */
+#define        AMES    1
+
+main(argc, argv)
+       int argc;
+       char *argv[];
 {
        struct anode *exlist;
        int paths;
        register char *cp, *sp = 0;
 {
        struct anode *exlist;
        int paths;
        register char *cp, *sp = 0;
+#ifdef SUID_PWD
        FILE *pwd, *popen();
        FILE *pwd, *popen();
+#endif
 
 
+#ifdef  AMES
+       if (argc < 2) {
+               fprintf(stderr,
+                       "Usage: find name, or find path-list predicate-list\n");
+               exit(1);
+       }
+       if (argc == 2) {
+               fastfind(argv[1]);
+               exit(0);
+       }
+#endif
        time(&Now);
        time(&Now);
+#ifdef SUID_PWD
        pwd = popen("pwd", "r");
        pwd = popen("pwd", "r");
-       fgets(Home, 128, pwd);
+       fgets(Home, sizeof Home, pwd);
        pclose(pwd);
        Home[strlen(Home) - 1] = '\0';
        pclose(pwd);
        Home[strlen(Home) - 1] = '\0';
+#else
+       if (getwd(Home) == NULL) {
+               fprintf(stderr, "find: Can't get current working directory\n");
+               exit(1);
+       }
+#endif
        Argc = argc; Argv = argv;
        if(argc<3) {
 usage:         fprintf(stderr, "Usage: find path-list predicate-list\n");
        Argc = argc; Argv = argv;
        if(argc<3) {
 usage:         fprintf(stderr, "Usage: find path-list predicate-list\n");
@@ -85,6 +137,8 @@ usage:               fprintf(stderr, "Usage: find path-list predicate-list\n");
                        *cp = '/';
                }
                Fname = sp? sp: Pathname;
                        *cp = '/';
                }
                Fname = sp? sp: Pathname;
+               if (!Xdev)
+                       stat(Pathname, &Devstat);
                descend(Pathname, Fname, exlist); /* to find files that match  */
        }
        if(Cpio) {
                descend(Pathname, Fname, exlist); /* to find files that match  */
        }
        if(Cpio) {
@@ -143,10 +197,12 @@ struct anode *e2() { /* parse NOT (!) */
 struct anode *e3() { /* parse parens and predicates */
        int exeq(), ok(), glob(),  mtime(), atime(), user(),
                group(), size(), perm(), links(), print(),
 struct anode *e3() { /* parse parens and predicates */
        int exeq(), ok(), glob(),  mtime(), atime(), user(),
                group(), size(), perm(), links(), print(),
-               type(), ino(), cpio(), newer();
+               type(), ino(), cpio(), newer(),
+               nouser(), nogroup(), ls(), dummy();
        struct anode *p1;
        int i;
        struct anode *p1;
        int i;
-       register char *a, *b, s;
+       register char *a, *b;
+       register int s;
 
        a = nxtarg();
        if(EQ(a, "(")) {
 
        a = nxtarg();
        if(EQ(a, "(")) {
@@ -159,6 +215,19 @@ struct anode *e3() { /* parse parens and predicates */
        else if(EQ(a, "-print")) {
                return(mk(print, (struct anode *)0, (struct anode *)0));
        }
        else if(EQ(a, "-print")) {
                return(mk(print, (struct anode *)0, (struct anode *)0));
        }
+       else if (EQ(a, "-nouser")) {
+               return (mk(nouser, (struct anode *)0, (struct anode *)0));
+       }
+       else if (EQ(a, "-nogroup")) {
+               return (mk(nogroup, (struct anode *)0, (struct anode *)0));
+       }
+       else if (EQ(a, "-ls")) {
+               return (mk(ls, (struct anode *)0, (struct anode *)0));
+       }
+       else if (EQ(a, "-xdev")) {
+               Xdev = 0;
+               return (mk(dummy, (struct anode *)0, (struct anode *)0));
+       }
        b = nxtarg();
        s = *b;
        if(s=='+') b++;
        b = nxtarg();
        s = *b;
        if(s=='+') b++;
@@ -169,7 +238,7 @@ struct anode *e3() { /* parse parens and predicates */
        else if(EQ(a, "-atime"))
                return(mk(atime, (struct anode *)atoi(b), (struct anode *)s));
        else if(EQ(a, "-user")) {
        else if(EQ(a, "-atime"))
                return(mk(atime, (struct anode *)atoi(b), (struct anode *)s));
        else if(EQ(a, "-user")) {
-               if((i=getunum("/etc/passwd", b)) == -1) {
+               if((i=getuid(b)) == -1) {
                        if(gmatch(b, "[0-9]*"))
                                return mk(user, (struct anode *)atoi(b), (struct anode *)s);
                        fprintf(stderr, "find: cannot find -user name\n");
                        if(gmatch(b, "[0-9]*"))
                                return mk(user, (struct anode *)atoi(b), (struct anode *)s);
                        fprintf(stderr, "find: cannot find -user name\n");
@@ -180,7 +249,7 @@ struct anode *e3() { /* parse parens and predicates */
        else if(EQ(a, "-inum"))
                return(mk(ino, (struct anode *)atoi(b), (struct anode *)s));
        else if(EQ(a, "-group")) {
        else if(EQ(a, "-inum"))
                return(mk(ino, (struct anode *)atoi(b), (struct anode *)s));
        else if(EQ(a, "-group")) {
-               if((i=getunum("/etc/group", b)) == -1) {
+               if((i=getgid(b)) == -1) {
                        if(gmatch(b, "[0-9]*"))
                                return mk(group, (struct anode *)atoi(b), (struct anode *)s);
                        fprintf(stderr, "find: cannot find -group name\n");
                        if(gmatch(b, "[0-9]*"))
                                return mk(group, (struct anode *)atoi(b), (struct anode *)s);
                        fprintf(stderr, "find: cannot find -group name\n");
@@ -203,7 +272,9 @@ struct anode *e3() { /* parse parens and predicates */
                i = s=='d' ? S_IFDIR :
                    s=='b' ? S_IFBLK :
                    s=='c' ? S_IFCHR :
                i = s=='d' ? S_IFDIR :
                    s=='b' ? S_IFBLK :
                    s=='c' ? S_IFCHR :
-                   s=='f' ? 0100000 :
+                   s=='f' ? S_IFREG :
+                   s=='l' ? S_IFLNK :
+                   s=='s' ? S_IFSOCK :
                    0;
                return(mk(type, (struct anode *)i, (struct anode *)0));
        }
                    0;
                return(mk(type, (struct anode *)i, (struct anode *)0));
        }
@@ -241,6 +312,11 @@ struct anode *mk(f, l, r)
 int (*f)();
 struct anode *l, *r;
 {
 int (*f)();
 struct anode *l, *r;
 {
+       if (Nn >= MAXNODES) {
+               fprintf(stderr, "find: Too many options\n");
+               exit(1);
+       }
+
        Node[Nn].F = f;
        Node[Nn].L = l;
        Node[Nn].R = r;
        Node[Nn].F = f;
        Node[Nn].L = l;
        Node[Nn].R = r;
@@ -283,7 +359,8 @@ register struct { int f; char *pat; } *p;
 {
        return(gmatch(Fname, p->pat));
 }
 {
        return(gmatch(Fname, p->pat));
 }
-print()
+print(p)
+struct anode *p;
 {
        puts(Pathname);
        return(1);
 {
        puts(Pathname);
        return(1);
@@ -303,6 +380,13 @@ register struct { int f, u, s; } *p;
 {
        return(scomp(Statb.st_uid, p->u, p->s));
 }
 {
        return(scomp(Statb.st_uid, p->u, p->s));
 }
+nouser(p)
+struct anode *p;
+{
+       char *getname();
+
+       return (getname(Statb.st_uid) == NULL);
+}
 ino(p)
 register struct { int f, u, s; } *p;
 {
 ino(p)
 register struct { int f, u, s; } *p;
 {
@@ -313,6 +397,13 @@ register struct { int f, u; } *p;
 {
        return(p->u == Statb.st_gid);
 }
 {
        return(p->u == Statb.st_gid);
 }
+nogroup(p)
+struct anode *p;
+{
+       char *getgroup();
+
+       return (getgroup(Statb.st_gid) == NULL);
+}
 links(p)
 register struct { int f, link, s; } *p; 
 {
 links(p)
 register struct { int f, link, s; } *p; 
 {
@@ -367,7 +458,8 @@ short v[];
                U.s[0] = v[0], U.s[1] = v[1];
        return U.l;
 }
                U.s[0] = v[0], U.s[1] = v[1];
        return U.l;
 }
-cpio()
+cpio(p)
+struct anode *p;
 {
 #define MAGIC 070707
        struct header {
 {
 #define MAGIC 070707
        struct header {
@@ -424,10 +516,23 @@ cerror:
        close(ifile);
        return;
 }
        close(ifile);
        return;
 }
-newer()
+newer(p)
+struct anode *p;
 {
        return Statb.st_mtime > Newer;
 }
 {
        return Statb.st_mtime > Newer;
 }
+ls(p)
+struct anode *p;
+{
+       list(Pathname, &Statb);
+       return (1);
+}
+dummy(p)
+struct anode *p;
+{
+       /* dummy */
+       return (1);
+}
 
 /* support functions */
 scomp(a, b, s) /* funny signed compare */
 
 /* support functions */
 scomp(a, b, s) /* funny signed compare */
@@ -447,28 +552,44 @@ doex(com)
        register char *na;
        static char *nargv[50];
        static ccode;
        register char *na;
        static char *nargv[50];
        static ccode;
+       register int w, pid, omask;
 
        ccode = np = 0;
        while (na=Argv[com++]) {
 
        ccode = np = 0;
        while (na=Argv[com++]) {
+               if(np >= sizeof nargv / sizeof *nargv - 1) break;
                if(strcmp(na, ";")==0) break;
                if(strcmp(na, "{}")==0) nargv[np++] = Pathname;
                else nargv[np++] = na;
        }
        nargv[np] = 0;
        if (np==0) return(9);
                if(strcmp(na, ";")==0) break;
                if(strcmp(na, "{}")==0) nargv[np++] = Pathname;
                else nargv[np++] = na;
        }
        nargv[np] = 0;
        if (np==0) return(9);
-       if(fork()) /*parent*/ {
-#include <signal.h>
-               int (*old)() = signal(SIGINT, SIG_IGN);
-               int (*oldq)() = signal(SIGQUIT, SIG_IGN);
-               wait(&ccode);
-               signal(SIGINT, old);
-               signal(SIGQUIT, oldq);
-       } else { /*child*/
+       switch (pid = vfork()) {
+       case -1:
+               perror("find: Can't fork");
+               exit(1);
+               break;
+
+       case 0:
                chdir(Home);
                execvp(nargv[0], nargv, np);
                chdir(Home);
                execvp(nargv[0], nargv, np);
-               exit(1);
+               write(2, "find: Can't execute ", 20);
+               perror(nargv[0]);
+               /*
+                * Kill ourselves; our exit status will be a suicide
+                * note indicating we couldn't do the "exec".
+                */
+               kill(getpid(), SIGUSR1);
+               break;
+
+       default:
+               omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT));
+               while ((w = wait(&ccode)) != pid && w != -1)
+                       ;
+               (void) sigsetmask(omask);
+               if ((ccode & 0177) == SIGUSR1)
+                       exit(1);
+               return (ccode != 0 ? 0 : 1);
        }
        }
-       return(ccode ? 0:1);
 }
 
 getunum(f, s) char *f, *s; { /* find user/group name and return number */
 }
 
 getunum(f, s) char *f, *s; { /* find user/group name and return number */
@@ -518,7 +639,8 @@ descend(name, fname, exlist)
                return(0);
        }
        (*exlist->F)(exlist);
                return(0);
        }
        (*exlist->F)(exlist);
-       if((Statb.st_mode&S_IFMT)!=S_IFDIR)
+       if((Statb.st_mode&S_IFMT)!=S_IFDIR ||
+          !Xdev && Devstat.st_dev != Statb.st_dev)
                return(1);
 
        for (c1 = name; *c1; ++c1);
                return(1);
 
        for (c1 = name; *c1; ++c1);
@@ -592,7 +714,8 @@ register char *s, *p;
                                        return(0);
 
                        case '-':
                                        return(0);
 
                        case '-':
-                               k |= lc <= scc & scc <= (cc=p[1]);
+                               cc = p[1];
+                               k |= lc <= scc && scc <= cc;
                        }
                        if (scc==(lc=cc)) k++;
                }
                        }
                        if (scc==(lc=cc)) k++;
                }
@@ -672,3 +795,420 @@ again:
        }
        return f;
 }
        }
        return f;
 }
+
+#ifdef AMES
+/*
+ * 'fastfind' scans a file list for the full pathname of a file
+ * given only a piece of the name.  The list has been processed with
+ * with "front-compression" and bigram coding.  Front compression reduces
+ * space by a factor of 4-5, bigram coding by a further 20-25%.
+ * The codes are:
+ *
+ *     0-28    likeliest differential counts + offset to make nonnegative 
+ *     30      escape code for out-of-range count to follow in next word
+ *     128-255 bigram codes, (128 most common, as determined by 'updatedb')
+ *     32-127  single character (printable) ascii residue
+ *
+ * A novel two-tiered string search technique is employed: 
+ *
+ * First, a metacharacter-free subpattern and partial pathname is
+ * matched BACKWARDS to avoid full expansion of the pathname list.
+ * The time savings is 40-50% over forward matching, which cannot efficiently
+ * handle overlapped search patterns and compressed path residue.
+ *
+ * Then, the actual shell glob-style regular expression (if in this form)
+ * is matched against the candidate pathnames using the slower routines
+ * provided in the standard 'find'.
+ */
+
+#define        FCODES  "/usr/lib/find/find.codes"
+#define        YES     1
+#define        NO      0
+#define        OFFSET  14
+#define        ESCCODE 30
+
+fastfind ( pathpart )  
+       char pathpart[];
+{
+       register char *p, *s;
+       register int c; 
+       char *q, *index(), *patprep();
+       int i, count = 0, globflag;
+       FILE *fp, *fopen();
+       char *patend, *cutoff;
+       char path[1024];
+       char bigram1[128], bigram2[128];
+       int found = NO;
+
+       if ( (fp = fopen ( FCODES, "r" )) == NULL ) {
+               fprintf ( stderr, "find: can't open %s\n", FCODES );
+               exit ( 1 );
+       }
+       for ( i = 0; i < 128; i++ ) 
+               bigram1[i] = getc ( fp ),  bigram2[i] = getc ( fp );
+       
+       if ( index ( pathpart, '*' ) || index ( pathpart, '?' ) || index ( pathpart, '[' ) )
+               globflag = YES;
+       patend = patprep ( pathpart );
+
+       c = getc ( fp );
+       for ( ; ; ) {
+
+               count += ( (c == ESCCODE) ? getw ( fp ) : c ) - OFFSET;
+
+               for ( p = path + count; (c = getc ( fp )) > ESCCODE; )  /* overlay old path */
+                       if ( c < 0200 ) 
+                               *p++ = c;
+                       else            /* bigrams are parity-marked */
+                               *p++ = bigram1[c & 0177],  *p++ = bigram2[c & 0177];
+               if ( c == EOF )
+                       break;
+               *p-- = NULL;
+               cutoff = ( found ? path : path + count);
+
+               for ( found = NO, s = p; s >= cutoff; s-- ) 
+                       if ( *s == *patend ) {          /* fast first char check */
+                               for ( p = patend - 1, q = s - 1; *p != NULL; p--, q-- )
+                                       if ( *q != *p )
+                                               break;
+                               if ( *p == NULL ) {     /* success on fast match */
+                                       found = YES;
+                                       if ( globflag == NO || amatch ( path, pathpart ) )
+                                               puts ( path );
+                                       break;
+                               }
+                       }
+       }
+}
+
+/*
+    extract last glob-free subpattern in name for fast pre-match;
+    prepend '\0' for backwards match; return end of new pattern
+*/
+static char globfree[100];
+
+char *
+patprep ( name )
+       char *name;
+{
+       register char *p, *endmark;
+       register char *subp = globfree;
+
+       *subp++ = '\0';
+       p = name + strlen ( name ) - 1;
+       /*
+          skip trailing metacharacters (and [] ranges)
+       */
+       for ( ; p >= name; p-- )
+               if ( index ( "*?", *p ) == 0 )
+                       break;
+       if ( p < name )
+               p = name;
+       if ( *p == ']' )
+               for ( p--; p >= name; p-- )
+                       if ( *p == '[' ) {
+                               p--;
+                               break;
+                       }
+       if ( p < name )
+               p = name;
+       /*
+          if pattern has only metacharacters,
+          check every path (force '/' search)
+       */
+       if ( (p == name) && index ( "?*[]", *p ) != 0 )
+               *subp++ = '/';                                  
+       else {                          
+               for ( endmark = p; p >= name; p-- )
+                       if ( index ( "]*?", *p ) != 0 )
+                               break;
+               for ( ++p; (p <= endmark) && subp < (globfree + sizeof ( globfree )); )
+                       *subp++ = *p++;
+       }
+       *subp = '\0';
+       return ( --subp );
+}
+#endif
+
+/* rest should be done with nameserver or database */
+
+#include <pwd.h>
+#include <grp.h>
+#include <utmp.h>
+
+struct utmp utmp;
+#define        NMAX    (sizeof (utmp.ut_name))
+#define SCPYN(a, b)    strncpy(a, b, NMAX)
+
+#define NUID   64
+#define NGID   300
+
+struct ncache {
+       int     uid;
+       char    name[NMAX+1];
+} nc[NUID];
+char   outrangename[NMAX+1];
+int    outrangeuid = -1;
+char   groups[NGID][NMAX+1];
+char   outrangegroup[NMAX+1];
+int    outrangegid = -1;
+
+/*
+ * This function assumes that the password file is hashed
+ * (or some such) to allow fast access based on a name key.
+ * If this isn't true, duplicate the code for getgroup().
+ */
+char *
+getname(uid)
+{
+       register struct passwd *pw;
+       struct passwd *getpwent();
+       register int cp;
+       extern int _pw_stayopen;
+
+       _pw_stayopen = 1;
+
+#if    (((NUID) & ((NUID) - 1)) != 0)
+       cp = uid % (NUID);
+#else
+       cp = uid & ((NUID) - 1);
+#endif
+       if (uid >= 0 && nc[cp].uid == uid && nc[cp].name[0])
+               return (nc[cp].name);
+       pw = getpwuid(uid);
+       if (!pw)
+               return (0);
+       nc[cp].uid = uid;
+       SCPYN(nc[cp].name, pw->pw_name);
+       return (nc[cp].name);
+}
+
+char *
+getgroup(gid)
+{
+       register struct group *gr;
+       static init;
+       struct group *getgrent();
+
+       if (gid >= 0 && gid < NGID && groups[gid][0])
+               return (&groups[gid][0]);
+       if (gid >= 0 && gid == outrangegid)
+               return (outrangegroup);
+rescan:
+       if (init == 2) {
+               if (gid < NGID)
+                       return (0);
+               setgrent();
+               while (gr = getgrent()) {
+                       if (gr->gr_gid != gid)
+                               continue;
+                       outrangegid = gr->gr_gid;
+                       SCPYN(outrangegroup, gr->gr_name);
+                       endgrent();
+                       return (outrangegroup);
+               }
+               endgrent();
+               return (0);
+       }
+       if (init == 0)
+               setgrent(), init = 1;
+       while (gr = getgrent()) {
+               if (gr->gr_gid < 0 || gr->gr_gid >= NGID) {
+                       if (gr->gr_gid == gid) {
+                               outrangegid = gr->gr_gid;
+                               SCPYN(outrangegroup, gr->gr_name);
+                               return (outrangegroup);
+                       }
+                       continue;
+               }
+               if (groups[gr->gr_gid][0])
+                       continue;
+               SCPYN(groups[gr->gr_gid], gr->gr_name);
+               if (gr->gr_gid == gid)
+                       return (&groups[gid][0]);
+       }
+       init = 2;
+       goto rescan;
+}
+
+int
+getuid(username)
+       char *username;
+{
+       register struct passwd *pw;
+       struct passwd *getpwnam();
+#ifndef        NO_PW_STAYOPEN
+       extern int _pw_stayopen;
+
+       _pw_stayopen = 1;
+#endif
+
+       pw = getpwnam(username);
+       if (pw != NULL)
+               return (pw->pw_uid);
+       else
+               return (-1);
+}
+
+int
+getgid(groupname)
+       char *groupname;
+{
+       register struct group *gr;
+       struct group *getgrnam();
+
+       gr = getgrnam(groupname);
+       if (gr != NULL)
+               return (gr->gr_gid);
+       else
+               return (-1);
+}
+
+#define permoffset(who)                ((who) * 3)
+#define permission(who, type)  ((type) >> permoffset(who))
+#define kbytes(bytes)          (((bytes) + 1023) / 1024)
+
+list(file, stp)
+       char *file;
+       register struct stat *stp;
+{
+       char pmode[32], uname[32], gname[32], fsize[32], ftime[32];
+       char *getname(), *getgroup(), *ctime();
+       static long special[] = { S_ISUID, 's', S_ISGID, 's', S_ISVTX, 't' };
+       static time_t sixmonthsago = -1;
+#ifdef S_IFLNK
+       char flink[MAXPATHLEN + 1];
+#endif
+       register int who;
+       register char *cp;
+       time_t now;
+
+       if (file == NULL || stp == NULL)
+               return (-1);
+
+       time(&now);
+       if (sixmonthsago == -1)
+               sixmonthsago = now - 6L*30L*24L*60L*60L;
+
+       switch (stp->st_mode & S_IFMT) {
+#ifdef S_IFDIR
+       case S_IFDIR:   /* directory */
+               pmode[0] = 'd';
+               break;
+#endif
+#ifdef S_IFCHR
+       case S_IFCHR:   /* character special */
+               pmode[0] = 'c';
+               break;
+#endif
+#ifdef S_IFBLK
+       case S_IFBLK:   /* block special */
+               pmode[0] = 'b';
+               break;
+#endif
+#ifdef S_IFLNK
+       case S_IFLNK:   /* symbolic link */
+               pmode[0] = 'l';
+               break;
+#endif
+#ifdef S_IFSOCK
+       case S_IFSOCK:  /* socket */
+               pmode[0] = 's';
+               break;
+#endif
+#ifdef S_IFREG
+       case S_IFREG:   /* regular */
+#endif
+       default:
+               pmode[0] = '-';
+               break;
+       }
+
+       for (who = 0; who < 3; who++) {
+               if (stp->st_mode & permission(who, S_IREAD))
+                       pmode[permoffset(who) + 1] = 'r';
+               else
+                       pmode[permoffset(who) + 1] = '-';
+
+               if (stp->st_mode & permission(who, S_IWRITE))
+                       pmode[permoffset(who) + 2] = 'w';
+               else
+                       pmode[permoffset(who) + 2] = '-';
+
+               if (stp->st_mode & special[who * 2])
+                       pmode[permoffset(who) + 3] = special[who * 2 + 1];
+               else if (stp->st_mode & permission(who, S_IEXEC))
+                       pmode[permoffset(who) + 3] = 'x';
+               else
+                       pmode[permoffset(who) + 3] = '-';
+       }
+       pmode[permoffset(who) + 1] = '\0';
+
+       cp = getname(stp->st_uid);
+       if (cp != NULL)
+               sprintf(uname, "%-9.9s", cp);
+       else
+               sprintf(uname, "%-9d", stp->st_uid);
+
+       cp = getgroup(stp->st_gid);
+       if (cp != NULL)
+               sprintf(gname, "%-9.9s", cp);
+       else
+               sprintf(gname, "%-9d", stp->st_gid);
+
+       if (pmode[0] == 'b' || pmode[0] == 'c')
+               sprintf(fsize, "%3d,%4d",
+                       major(stp->st_rdev), minor(stp->st_rdev));
+       else {
+               sprintf(fsize, "%8ld", stp->st_size);
+#ifdef S_IFLNK
+               if (pmode[0] == 'l') {
+                       /*
+                        * Need to get the tail of the file name, since we have
+                        * already chdir()ed into the directory of the file
+                        */
+                       cp = rindex(file, '/');
+                       if (cp == NULL)
+                               cp = file;
+                       else
+                               cp++;
+                       who = readlink(cp, flink, sizeof flink - 1);
+                       if (who >= 0)
+                               flink[who] = '\0';
+                       else
+                               flink[0] = '\0';
+               }
+#endif
+       }
+
+       cp = ctime(&stp->st_mtime);
+       if (stp->st_mtime < sixmonthsago || stp->st_mtime > now)
+               sprintf(ftime, "%-7.7s %-4.4s", cp + 4, cp + 20);
+       else
+               sprintf(ftime, "%-12.12s", cp + 4);
+
+       printf("%5lu %4ld %s %2d %s%s%s %s %s%s%s\n",
+               stp->st_ino,                            /* inode #      */
+#ifdef S_IFSOCK
+               (long) kbytes(dbtob(stp->st_blocks)),   /* kbytes       */
+#else
+               (long) kbytes(stp->st_size),            /* kbytes       */
+#endif
+               pmode,                                  /* protection   */
+               stp->st_nlink,                          /* # of links   */
+               uname,                                  /* owner        */
+               gname,                                  /* group        */
+               fsize,                                  /* # of bytes   */
+               ftime,                                  /* modify time  */
+               file,                                   /* name         */
+#ifdef S_IFLNK
+               (pmode[0] == 'l') ? " -> " : "",
+               (pmode[0] == 'l') ? flink  : ""         /* symlink      */
+#else
+               "",
+               ""
+#endif
+       );
+
+       return (0);
+}