home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume3 / dtree4.2 < prev    next >
Text File  |  1986-11-30  |  25KB  |  954 lines

  1. Subject: dtree for 4.2
  2. From: Mike Meyer <ucbvax!ucbjade!ucbopal:mwm>
  3. Newsgroups: mod.sources
  4. Approved: jpn@panda.UUCP
  5.  
  6. Mod.sources:  Volume 3, Issue 20
  7. Submitted by: Mike Meyer <ucbvax!ucbjade!ucbopal:mwm>
  8.  
  9.  
  10. # This is a shell archive.  Remove anything before this line, then
  11. # unpack it by saving it in a file and typing "sh file".  (Files
  12. # unpacked will be owned by you and have default permissions.)
  13. #
  14. # This archive contains:
  15. # dtree.1 dtree.c
  16.  
  17. echo x - dtree.1
  18. sed 's/^    //' > "dtree.1" << '//E*O*F dtree.1//'
  19.     .TH DTREE l
  20.     .SH NAME
  21.     dtree \- print directory tree structures
  22.     .SH SYNOPSIS
  23.     .B dtree
  24.     [
  25.     .B \-adfglnpsvx
  26.     ]
  27.     [
  28.     .B \-b filenamesize
  29.     ]
  30.     [
  31.     .B \-c linelength
  32.     ] [ directory1 ...]
  33.     .SH DESCRIPTION
  34.     .I Dtree
  35.     is a program to print out the tree structure of directories
  36.     and their children. If no directories are specified,
  37.     .I dtree
  38.     takes the current working directory to be the top of the tree
  39.     structure.
  40.     If no flags are specified,
  41.     .I dtree
  42.     prints out just the directory structures.
  43.     Recognized options are as follows:
  44.     .TP
  45.     -a
  46.     Include files in printout (excluding entries beginning with '.')
  47.     .TP
  48.     -b
  49.     Take the next argument to be the maximum length of a directory name; default
  50.     is 14 characters, or the value associated with a -c argument, if any.  
  51.     Any directories with names longer than this length will not be searched, thus
  52.     any files and directories within them will not be included in the output.
  53.     This flag only has an effect if the -v flag is specified.
  54.     .TP
  55.     -c
  56.     Take the next argument to be the length of each
  57.     column of the printout. (By default, this is 14, the
  58.     maximum filename length.  Any lengths greater than
  59.     the column width are truncated accordingly, and the last character which
  60.     fits into the column is replaced by an asterisk.)
  61.     This flag only has an effect if the -v flag is specified.
  62.     .TP
  63.     -d
  64.     List directories first. For each directory, its subdirectories
  65.     will be listed first, and then all its other entries.
  66.     .TP
  67.     -f
  68.     List files first. Reverse of -d.
  69.     .TP
  70.     -l
  71.     Long listing. Useful information is printed to the right of
  72.     each entry. The name of the owner, its size in blocks, and its mode are
  73.     printed.
  74.     .TP
  75.     -g
  76.     Same as the -l flag, except that the group name is used instead of
  77.     the owner name. If both the -l and -g flags are used, both the
  78.     owner and group will be printed.
  79.     .TP
  80.     -n
  81.     No sort. Names are listed in the order they are read
  82.     from the directory.
  83.     .TP
  84.     -p
  85.     Include entries beginning with '.' (excluding "." and "..").
  86.     .TP
  87.     -s
  88.     Simplify the long listing. Prints uid, size in blocks, and octal mode of the
  89.     file. This flag implies the -l flag unless the -g flag is specified.
  90.     .TP
  91.     -v
  92.     Don't let columns be of variable length. Use the same
  93.     width for each column of output. The width defaults to 14 (the
  94.     normal Unix maximum file length), but can be set with either -c or -b.
  95.     .TP
  96.     -x
  97.     Do not cross file systems.  Dtree will not cross over to a
  98.     subdirectory if it is on a different file system.
  99.     .SH AUTHOR
  100.     Dave Borman, Digital Unix Engineering Group
  101.     .br
  102.     decvax!borman
  103.     .br
  104.     Originally written at St. Olaf College, Northfield, MN.
  105.     
  106. //E*O*F dtree.1//
  107.  
  108. echo x - dtree.c
  109. sed 's/^    //' > "dtree.c" << '//E*O*F dtree.c//'
  110.     /*
  111.      *    DTREE - Print the tree structure of a directory
  112.      *    4/7/83 name was changed from TREE to DTREE
  113.      *    9/7/83 mods for 4.1c and 4.2 dirctory structure added
  114.      *
  115.      *    Dave Borman, Digital Unix Engineering Group
  116.      *        decvax!borman
  117.      *    Originally written at St. Olaf College, Northfield MN.
  118.      *    Copyright (c) 1983 by Dave Borman
  119.      *    All rights reserved
  120.      *    This program may not be sold, but may be distributed
  121.      *    provided this header is included.
  122.      *
  123.      *    Compile: PDP V7 w/split i&d    cc -O dtree.c -i -n -o dtree
  124.      *         VAX 4.1bsd           cc -O dtree.c -n -o dtree
  125.      *         VAX 4.2bsd           cc -O -DNEWDIR -DLINK dtree.c -n -o dtree
  126.      *
  127.      *    Usage:    dtree -[adfglnpsvx] [-b filenamesize] [-c linelength] [top]
  128.      *    Flags:    -a) include non-directory entries in listing
  129.      *        -d) sort tree with directories at the top
  130.      *        -f) sort tree with files at the top
  131.      *        -g) same as l, but use group name instead of user name
  132.      *        -l) print stats with each listing
  133.      *            if both g & l flags are given, both owner and
  134.      *            group will be printed
  135.      *        -n) do not sort the tree
  136.      *        -p) include files starting with a '.' (except "." & "..")
  137.      *        -s) use shorter stats. Implies -l if -g isn't given.
  138.      *        -v) variable length columns off
  139.      *        -x) do not cross mounted file systems.
  140.      *              -b length) big directory names (define the max. name length)
  141.      *        -c length) set max column length to "length" (if col.
  142.      *                         length > file name length, this implies -b also)
  143.      */
  144.     
  145.      /*     Modified by      Ed Arnold      CSU-CS   (csu-cs!arnold)     3-5-84
  146.       *
  147.       *     Allows symbolic links to both directories and individual files.
  148.       *     With a '-l' or '-al' option, links are denoted with a 'l' in front of 
  149.       *     file or directory permissions. In all other instances both links to 
  150.       *     directories and files are represented just as files are. Contents of
  151.       *     linked directories are not printed due to the possibility of 
  152.       *     recursively linked directories.
  153.       *
  154.       *     Big directory name option added by:
  155.       *                      Mike Vevea      CSU-CS (csu-cs!vevea)      3-22-84
  156.       *    Toggle sense of -v (running 4.2), and eliminate some extraneous
  157.       *        print info    Mike Meyer Energy Analysts (mwm@ea)    4/17/84
  158.       *    Fix the exit status to correctly indicate what happened.
  159.       *                Mike Meyer Energy Analysts (mwm@ea)    4/23/84
  160.      */
  161.     
  162.     #define STATS    /* comment out to remove stats, giving more core space    */
  163.             /* and thus the ability to tree larger tree structures    */
  164.             /* on PDP 11/70s */
  165.     
  166.     /* #define    NEWDIR    /* directory structure ala Berkeley 4.1c or 4.2 */
  167.      
  168.     
  169.     /* #define LINK    /* allows links to be processed in Berkeley 4.2 version 
  170.                           NEWDIR must be defined as well as LINK */
  171.     static    char Sccsid[]="@(#)dtree.c    2.3    2/14/84";
  172.     
  173.     #ifdef LINK
  174.     static  char Rcsid[] ="$Header: /mnt/ntape/RCS/dtree.c,v 1.2 84/04/23 10:33:41 root Exp $";
  175.     #endif LINK
  176.     
  177.     #include    <stdio.h>
  178.     #include    <sys/types.h>
  179.     #include    <sys/stat.h>
  180.     #ifdef    STATS
  181.     # include    <pwd.h>
  182.     # include    <grp.h>
  183.     #endif    STATS
  184.     #ifdef    NEWDIR
  185.     # include    <sys/dir.h>
  186.     #else
  187.     # include    <sys/dir.h>
  188.     #endif    NEWDIR
  189.     #define    DEFDIRSIZ    255
  190.     #define DEFCOLWID       14
  191.     #ifndef MAXNAMLEN
  192.     # define MAXNAMLEN      DEFCOLWID
  193.     #endif
  194.     /*
  195.      * TWIDDLE is a fudge factor.  It should be declared so that
  196.      * sizeof(struct entry) is on a nice boundry.
  197.      */
  198.     #define    TWIDDLE    4
  199.     
  200.     #define    addr(b,o) ((struct entry *)\
  201.                 ((int)(b) + (o)*(sizeof(struct entry)+Length-TWIDDLE)))
  202.     #define    SIZEOFentry    (sizeof(struct entry) + Length - TWIDDLE)
  203.     
  204.     #define    DEPTH    10    /* maximum depth that dtree will go */
  205.     #define    MAX    100    /* initial # of elements for list of files */
  206.     #define    PWD    "/bin/pwd"    /* program to get name of current dir */
  207.     #define    DATE    "/bin/date"    /* program to print current date */
  208.     
  209.     #define    FFIRST    2    /* sort files first */
  210.     #define DFIRST    1    /* sort directories first */
  211.     #define    FAIL    -1    /* failure return status of sys calls */
  212.     #define    GREATER    1    /* return value of strcmp if arg1 > arg2 */
  213.     #define    LESSTHAN -1    /* return value of strcmp if arg1 < arg2 */
  214.     #define    SAME    0    /* return value of strcmp if arg1 == arg2 */
  215.     
  216.     int    Index = 0;        /* current element of list[]    */
  217.     int    Length = DEFDIRSIZ;    /* max length of a dir. name    */
  218.     int     CLength = DEFDIRSIZ;    /* max length of a column       */
  219.     int    All = 0;        /* all != 0; list non-directory entries */
  220.     int    File_dir = 0;        /* flag for how to sort */
  221.     int    Sort = 1;        /* flag to cause sorting of entries */
  222.     int    Point = 1;        /* skip point files if set    */
  223.     int    Maxes[DEPTH];        /* array keeps track of max length in columns */
  224.     int    Level = 0;        /* counter for how deep we are    */
  225.     int    Device;            /* device that we are starting tree on */
  226.     int    Xdev = 1;        /* set to allow crossing of devices */
  227.     int    Varspaces = 1;        /* set to allow compaction of column width */
  228.     #ifdef    STATS
  229.     int    Gflag = 0;        /* set for group stats instead of owner */
  230.     int    Longflg = 0;        /* set for long listing */
  231.     int    Compact = 0;        /* set for shortened long listing */
  232.     #endif    STATS
  233.     struct    stat Status;
  234.     #ifdef  LINK
  235.     struct  stat Lstat;   /* stat of link, if there is one */
  236.     #endif  LINK
  237.     
  238.     struct entry {
  239.         int next;            /* index to next element in list */
  240.                         /* could be a ptr, but realloc() */
  241.                         /* might screw us then */
  242.     #ifdef    STATS
  243.         off_t    e_size;            /* size in blocks */
  244.         unsigned short    e_mode;        /* file mode */
  245.         short    e_uid;            /* uid of owner */
  246.         short    e_gid;            /* gid of owner */
  247.     #endif    STATS
  248.         short unsigned dir : 1;        /* entry is a directory */
  249.         short unsigned last : 1;    /* last entry in the dir. */
  250.         short unsigned dev : 1;        /* set if same device as top */
  251.         short unsigned end : 13;    /* index of last subdir entry*/
  252.         char    e_name[TWIDDLE];    /* name from directory entry */
  253.                         /* it will actually be larger */
  254.     } *List, *SaveList;
  255.     
  256.     unsigned Size;                /* how big of space we've malloced */
  257.     
  258.     char    *Spaces;    /* used for output */
  259.     char    Buf1[BUFSIZ];    /* buffers for stdio stuff.  We don't want    */
  260.     char    Buf2[BUFSIZ];    /* anyone calling malloc, because then        */
  261.     char    Buf3[BUFSIZ];    /* realloc() will have to move the whole list    */
  262.     
  263.     main(argc, argv)
  264.     char    **argv;
  265.     int    argc;
  266.     {
  267.         register int i, j = 0;
  268.         int    flag = 0;    /* used to jump over 'c' argument */
  269.         char    top[128];    /* array for treetop name */
  270.         char    home[128];    /* starting dir for multiple trees */
  271.         char    *ptr;
  272.         char    *malloc();
  273.         char    *rindex();
  274.         FILE    *istr, *popen();
  275.     
  276.         setbuf(stdout, Buf1);
  277.     
  278.         for (j=1; j<argc; j++) {
  279.             if (flag) {    /* saw a 'c' or 'b', so jump over value */
  280.                 if (flag > 1) j += flag-1;
  281.                 flag = 0;
  282.                 continue;
  283.             }
  284.             if (argv[j][0] == '-') {
  285.                 for (i = 1; i < strlen(argv[j]); i++) {
  286.                     switch (argv[j][i]) {
  287.                     case 'a':
  288.                         All = 1;
  289.                         break;
  290.                     case 'b':
  291.                         Length = atoi(argv[j+1+flag]);
  292.                         if (Length > MAXNAMLEN)
  293.                             Length = MAXNAMLEN;
  294.                         else if (Length < 1)
  295.                             Length = DEFCOLWID;
  296.                         flag += 1;
  297.                         break;
  298.                     case 'c':
  299.                         CLength = atoi(argv[j+1+flag]);
  300.                         if (CLength > MAXNAMLEN)
  301.                             CLength = MAXNAMLEN;
  302.                         else if (CLength < 1)
  303.                             CLength = DEFCOLWID;
  304.                         if (Length < CLength) Length = CLength;
  305.                         flag += 1;
  306.                         break;
  307.                     case 'd':
  308.                         File_dir = DFIRST;
  309.                         break;
  310.                     case 'f':
  311.                         File_dir = FFIRST;
  312.                         break;
  313.                     case 'n':
  314.                         Sort = 0;
  315.                         break;
  316.                     case 'p':
  317.                         Point = 0;
  318.                         break;
  319.                     case 'v':
  320.                         Varspaces = 0;
  321.                         break;
  322.                     case 'x':
  323.                         Xdev = 0;
  324.                         break;
  325.     #ifdef    STATS
  326.                     case 'g':
  327.                         Gflag = 1;
  328.                         break;
  329.                     case 'l':
  330.                         Longflg = 1;
  331.                         break;
  332.                     case 's':
  333.                         Compact = 1;
  334.                         break;
  335.     #endif    STATS
  336.                     default:
  337.                         fprintf(stderr,
  338.                             "Bad flag: %c\n", argv[j][i]);
  339.                         fprintf(stderr,
  340.     #ifdef    STATS
  341.             "Usage: dtree -[adfglnpsvx] [-b filenamesize] [-c linelength] [directory ... ]\n");
  342.     #else    STATS
  343.             "Usage: dtree -[adfglnpsvx] [-b filenamesize] [-c linelength] [directory ... ]\n");
  344.     #endif    STATS
  345.                         exit(FAIL);
  346.                     }
  347.                 }
  348.             } else
  349.                 break;
  350.         }
  351.     #ifdef    STATS
  352.         if (Compact && !Gflag)
  353.             Longflg = 1;
  354.     #endif    STATS
  355.     
  356.         /* Establish where we are (our home base...) */
  357.         if ((istr = popen(PWD, "r")) == NULL) {
  358.             fprintf(stderr,"dtree: %s failed\n", PWD);
  359.             exit(1);
  360.         } else {
  361.             setbuf(istr, Buf2);
  362.             if (fgets(home, 128, istr) == NULL) {
  363.                 fprintf(stderr, "dtree: EOF from %s\n", PWD);
  364.                 exit(1);
  365.             }
  366.         }
  367.         pclose(istr);
  368.         if (home[strlen(home)-1] == '\n')
  369.             home[strlen(home)-1] = '\0';
  370.     
  371.         Spaces = malloc(Length+2);
  372.         for(i = 0; i < Length + 1; i++)
  373.             Spaces[i] = ' ';
  374.         Spaces[i] = '\0';
  375.     
  376.         /* Get initial Storage space */
  377.         Size = SIZEOFentry * MAX;
  378.         SaveList = (struct entry *)malloc(Size);
  379.     
  380.         /* adjust for no specified directory */
  381.         if (j == argc)
  382.             argv[--j] = ".\0";
  383.     
  384.     /*    system(DATE); Why in gods name did he do this? */    
  385.     /*    printf("\n");*/
  386.         /* walk down the rest of the args, treeing them one at at time */
  387.         for (; j < argc; j++) {
  388.             if (chdir(home) == -1) {
  389.                 fprintf(stderr, "Can't chdir back to %s\n", home);
  390.                 exit(1);
  391.             }
  392.             sprintf(top, "%s", argv[j]);
  393.     
  394.             if (chdir(top) == FAIL) {
  395.                 fprintf(stderr, "Can't chdir to %s\n", top);
  396.                 continue;
  397.             } else if ((istr = popen(PWD, "r")) == NULL) {
  398.                 fprintf(stderr,"dtree: %s failed\n", PWD);
  399.                 continue;
  400.             } else {
  401.                 setbuf(istr, Buf2);
  402.                 if (fgets(top, 128, istr) == NULL) {
  403.                     fprintf(stderr, "dtree: EOF from %s\n", PWD);
  404.                     pclose(istr);
  405.                     continue;
  406.                 }
  407.                 ptr = top;
  408.                 while (*ptr && (*ptr != '\n'))
  409.                     ptr++;
  410.                 *ptr = '\0';
  411.                 pclose(istr);
  412.             }
  413.     
  414.             List = SaveList; Index = 0;
  415.             ptr = rindex(top, '/');
  416.     
  417.             if (!ptr || *++ptr == '\0')
  418.                 sprintf(addr(List,Index)->e_name, "%.*s", Length, top);
  419.             else
  420.                 sprintf(addr(List,Index)->e_name, "%.*s", Length, ptr);
  421.     
  422.             if(stat(top, &Status) == FAIL) {
  423.                 fprintf(stderr, "Can't stat %s\n", top);
  424.                 continue;
  425.             }
  426.             Device = Status.st_dev;
  427.             addr(List,0)->dir = 1;
  428.             addr(List,0)->last = 1;
  429.             addr(List,0)->next = 1;
  430.     #ifdef    STATS
  431.             addr(List,0)->e_mode = Status.st_mode;
  432.             addr(List,0)->e_uid = Status.st_uid;
  433.             addr(List,0)->e_gid = Status.st_gid;
  434.             addr(List,0)->e_size = Status.st_size;
  435.     #endif    STATS
  436.             Index = 1;
  437.             for (i = 1; i < DEPTH; i++)
  438.                 Maxes[i] = 0;
  439.             Maxes[0] = stln(addr(List,0)->e_name);
  440.             Level = 1;
  441.     
  442.             /* search the tree */
  443.             addr(List,0)->end = t_search(top, addr(List,0));
  444.     
  445.             if (Index == 1)                /* empty tree */
  446.                 addr(List,0)->next = 0;
  447.     
  448.     /*        if (All)
  449.                 printf("\nDirectory structure and contents of %s\n", top);
  450.             else
  451.                 printf("\nDirectory structure of %s\n", top);
  452.             if (Point)
  453.                 printf("(excluding entries that begin with '.')\n");
  454.     */
  455.             pt_tree();                /* print the tree */
  456.         }
  457.         exit(0) ;
  458.     }
  459.     
  460.     
  461.     t_search(dir, addrs)
  462.     char *dir;
  463.     struct    entry *addrs;
  464.     {
  465.         int    bsort;            /* index to begin sort */
  466.         int    stmp;            /* save temporary index value */
  467.         struct entry *sstep;        /* saved step in list */
  468.         int    nitems;            /* # of items in this directory */
  469.     #ifdef    NEWDIR
  470.         DIR    *dirp;            /* pointer to directory */
  471.     #else
  472.         FILE    *dirp;
  473.     #endif
  474.         char    sub[MAXNAMLEN+1];    /* used for subdirectory names */
  475.         int    i;
  476.     #ifdef    NEWDIR
  477.         struct direct *dp;
  478.     #else
  479.         struct direct dirent;
  480.         struct direct *dp = &dirent;
  481.     #endif    NEWDIR
  482.         int    n_subs = 0;
  483.         int    tmp = 0;
  484.         extern    qsort();
  485.         int    compar();    /* comparison routine for qsort */
  486.         char    *realloc();
  487.     
  488.     
  489.     #ifdef    NEWDIR
  490.         dirp = opendir(".");
  491.     #else
  492.         dirp = fopen(".", "r");
  493.     #endif    NEWDIR
  494.         if (dirp == NULL) {
  495.             fprintf(stderr, "Cannot open %s\n", dir);
  496.             return(0);
  497.         }
  498.     #ifndef    NEWDIR
  499.         setbuf(dirp, Buf3);
  500.     #endif    NEWDIR
  501.     
  502.         bsort = Index;
  503.         sstep = addr(List,bsort); /* initialize sstep for for loop later on */
  504.         nitems = Index;
  505.         /* get the entries of the directory that we are interested in */
  506.     #ifndef    NEWDIR
  507.         while (fread((char *)(dp), sizeof(struct direct), 1, dirp) == 1) {
  508.     #else
  509.         while ((dp = readdir(dirp)) != NULL) {
  510.     #endif    NEWDIR
  511.     
  512.             if (!dp->d_ino
  513.                 || (strcmp(dp->d_name, ".") == SAME)
  514.                 || (strcmp(dp->d_name, "..") == SAME)
  515.                 || (Point && dp->d_name[0] == '.'))
  516.                     continue;
  517.     
  518.             sprintf(sub, "%s", dp->d_name);
  519.     #ifdef LINK
  520.             if (lstat (sub,&Lstat) == FAIL) {
  521.                 fprintf(stderr, "%s:lstat can't find\n", sub);
  522.                 continue;
  523.                     }
  524.     #endif LINK
  525.             if ((tmp = stat(sub, &Status)) == FAIL) {
  526.                 fprintf(stderr, "%s:stat can't find\n", sub);
  527.                 continue;
  528.             }
  529.     #ifdef LINK
  530.             if (((Lstat.st_mode & S_IFMT) == S_IFLNK)  &&
  531.                 ((Status.st_mode & S_IFMT) == S_IFDIR)) 
  532.                     addr(List,Index)->dir = 0;    
  533.             else if ((((Lstat.st_mode & S_IFMT) == S_IFLNK) &&
  534.                 ((Status.st_mode & S_IFMT) != S_IFDIR)) && (All)) 
  535.                             addr(List,Index)->dir = 0;
  536.     #endif LINK
  537.             else if ((Status.st_mode & S_IFMT) == S_IFDIR)
  538.                 addr(List,Index)->dir = 1;
  539.             else if (All)
  540.                 addr(List,Index)->dir = 0;
  541.             else
  542.                 continue;
  543.             sprintf(addr(List,Index)->e_name, "%.*s", Length, dp->d_name);
  544.             addr(List,Index)->last = 0;
  545.             addr(List,Index)->end = 0;
  546.     #ifdef LINK
  547.             if ((Lstat.st_mode & S_IFMT) == S_IFLNK) {
  548.                  addr(List,Index)->dev = (Device == Lstat.st_dev);
  549.                  addr(List,Index)->e_mode = Lstat.st_mode;
  550.                  addr(List,Index)->e_uid = Lstat.st_uid;
  551.                  addr(List,Index)->e_gid = Lstat.st_gid;
  552.                  addr(List,Index)->e_size = Lstat.st_size;
  553.                     }
  554.                     else {
  555.     #endif LINK
  556.                  addr(List,Index)->dev = (Device == Status.st_dev);
  557.     #ifdef STATS
  558.                  addr(List,Index)->e_mode = Status.st_mode;
  559.                  addr(List,Index)->e_uid = Status.st_uid;
  560.                  addr(List,Index)->e_gid = Status.st_gid;
  561.                  addr(List,Index)->e_size = Status.st_size;
  562.     #endif STATS
  563.     #ifdef LINK
  564.                     }
  565.     #endif LINK
  566.             if (stln(addr(List,Index)->e_name) > Maxes[Level])
  567.                 Maxes[Level] = stln(addr(List,Index)->e_name);
  568.             ++Index;
  569.             if (Index*SIZEOFentry >= Size) {
  570.                 Size += 20*SIZEOFentry;
  571.                 if (!(List =
  572.                     (struct entry *)realloc((char *)List, Size))) {
  573.                     fprintf(stderr, "Out of space\n");
  574.                     break;
  575.                 }
  576.             }
  577.         }
  578.     #ifdef    NEWDIR
  579.         closedir(dirp);
  580.     #else
  581.         fclose(dirp);
  582.     #endif    NEWDIR
  583.     
  584.         nitems = Index - nitems;    /* nitems now contains the # of */
  585.                         /* items in this dir, rather than */
  586.                         /* # total items before this dir */
  587.     
  588.         if (Sort)
  589.             qsort(addr(List,bsort), nitems, SIZEOFentry, compar);
  590.     
  591.         addr(List,Index-1)->last = 1;    /* mark last item for this dir */
  592.         n_subs = nitems;
  593.         stmp = Index;
  594.     
  595.         /* now walk through, and recurse on directory entries */
  596.         /* sstep was initialized above */
  597.     
  598.         for (i = 0; i < nitems; sstep = addr(List,stmp - nitems+(++i))) {
  599.             if (sstep->dir && (Xdev || sstep->dev)) {
  600.                 sstep->next = Index;
  601.                 sprintf(sub, "%.*s", Length, sstep->e_name);
  602.                 tmp = n_subs;
  603.                 Level++;
  604.                 if (chdir(sub) == FAIL)
  605.                     fprintf(stderr,
  606.                         "Can't chdir to %s/%s\n", dir, sub);
  607.                 else {
  608.                     n_subs += t_search(sub, sstep);
  609.                     if (chdir("..") == FAIL) {
  610.                         fprintf(stderr,
  611.                             "No '..' in %s/%s!\n",dir, sub);
  612.                         exit(1);
  613.                     }
  614.                 }
  615.                 --Level;
  616.                 if (n_subs - tmp <= 0)
  617.                     sstep->next = 0;
  618.                 else
  619.                     --n_subs;
  620.             }
  621.             else
  622.                 sstep->next = 0;
  623.         }
  624.         addrs->end = (unsigned)n_subs;
  625.         return(n_subs);
  626.     }
  627.     
  628.     /*
  629.      *    comparison routine for qsort
  630.      */
  631.     
  632.     compar(a, b)
  633.     struct entry *a, *b;
  634.     {
  635.         if (!File_dir)        /* straight alphabetical */
  636.             return(strncmp(a->e_name, b->e_name, Length));
  637.     
  638.         /* sort alphabetically if both dirs or both not dirs */
  639.     
  640.         if ((a->dir && b->dir) || (!a->dir && !b->dir))
  641.             return(strncmp(a->e_name, b->e_name, Length));
  642.     
  643.         if (File_dir == FFIRST) {    /* sort by files first */
  644.             if (a->dir)
  645.                 return(GREATER);
  646.             else
  647.                 return(LESSTHAN);
  648.         }
  649.     
  650.         if (a->dir)            /* sort by dir first */
  651.             return(LESSTHAN);
  652.         else
  653.             return(GREATER);
  654.     }
  655.     
  656.     
  657.     pt_tree()
  658.     {
  659.         register int    i,j;
  660.         struct entry *l;
  661.         struct entry *hdr[DEPTH];
  662.         int posit[DEPTH];        /* array of positions to print dirs */
  663.         int con[DEPTH];            /* flags for connecting up tree */
  664.         char    flag;            /* flag to leave blank line after dir */
  665.         struct    entry *stack[DEPTH];    /* save positions for changing levels */
  666.         int    top = 0;        /* index to top of stack */
  667.         int    count = 1;        /* count of line of output */
  668.     #ifdef    STATS
  669.         char    *getmode();
  670.         char    *guid(), *ggid();
  671.     #endif    STATS
  672.     
  673.         Level = 0;            /* initialize Level */
  674.     
  675.         /* this loop appends each entry with dashes or spaces, for */
  676.         /* directories or files respectively */
  677.     
  678.         for (i = 0; i < Index; i++) {
  679.             for (j = 0; j < Length; j++) {
  680.                 if (!addr(List,i)->e_name[j])
  681.                     break;
  682.             }
  683.             if (addr(List,i)->dir) {
  684.                 for (; j < Length; j++)
  685.                     addr(List,i)->e_name[j] = '-';
  686.             } else {
  687.                 for (; j < Length; j++)
  688.                     addr(List,i)->e_name[j] = ' ';
  689.             }
  690.         }
  691.     
  692.         /* adjust the Maxes array according to the flags */
  693.     
  694.         for (i = 0; i < DEPTH; i++) {
  695.             if (Varspaces) {
  696.                 if (Maxes[i] > CLength )
  697.                     Maxes[i] = CLength;
  698.             } else
  699.                 Maxes[i] = CLength;
  700.         }
  701.     
  702.         /* clear the connective and position flags */
  703.     
  704.         for (i = 0; i < DEPTH; i++)
  705.             con[i] = posit[i] = 0;
  706.     
  707.         /* this is the main loop to print the tree structure. */
  708.         l = addr(List,0);
  709.         j = 0;
  710.         for (;;) {
  711.             /* directory entry, save it for later printing */
  712.             if (l->dir != 0 && l->next != 0) {
  713.                 hdr[Level] = l;
  714.                 posit[Level] = count + (l->end + 1)/2 - 1;
  715.                 flag = 1;
  716.                 stack[top++] = l;
  717.                 l = addr(List,l->next);
  718.                 ++Level;
  719.                 continue;
  720.             }
  721.     
  722.     #ifdef    STATS
  723.         do_it_again:
  724.     #endif    STATS
  725.             /* print columns up to our entry */
  726.             for (j = 0; j < (flag ? Level-1 : Level); j++) {
  727.                 if (!flag && posit[j] && posit[j] <= count) {
  728.                     /* time to print it */
  729.                     if (hdr[j]->e_name[CLength-1] != '-')
  730.                         hdr[j]->e_name[CLength-1] = '*';
  731.                     printf("|-%.*s",Maxes[j],hdr[j]->e_name);
  732.                     posit[j] = 0;
  733.                     if (hdr[j]->last != 0)
  734.                         con[j] = 0;
  735.                     else
  736.                         con[j] = 1;
  737.     #ifdef    STATS
  738.                     if (Gflag || Longflg) {
  739.                         if ((i = j+1) <= Level)
  740.                         printf("| %.*s", Maxes[i], Spaces);
  741.                         for (i++; i <= Level; i++) {
  742.                         printf("%c %.*s",
  743.                             (con[i] ? '|' : ' '),
  744.                             Maxes[i], Spaces);
  745.                         }
  746.                         if (!Compact) {
  747.                         printf("%s ", getmode(hdr[j]->e_mode));
  748.                         if (Longflg)
  749.                            printf("%8.8s ",guid(hdr[j]->e_uid));
  750.                         if (Gflag)
  751.                            printf("%8.8s ",ggid(hdr[j]->e_gid));
  752.                         printf("%7ld\n",
  753.                             (hdr[j]->e_size+511L)/512L);
  754.                         } else {
  755.                         printf(" %04o ",hdr[j]->e_mode & 07777);
  756.                         if (Longflg)
  757.                             printf("%5u ", hdr[j]->e_uid);
  758.                         if (Gflag)
  759.                             printf("%5u ", hdr[j]->e_gid);
  760.                         printf("%7ld\n",
  761.                             (hdr[j]->e_size+511L)/512L);
  762.                         }
  763.                         goto do_it_again;
  764.                     }
  765.     #endif    STATS
  766.                 } else
  767.                     printf("%c %.*s", (con[j] ? '|' : ' '),
  768.                         Maxes[j], Spaces);
  769.             }
  770.             if (flag) {    /* start of directory, so leave a blank line */
  771.                 printf(con[j] ? "|\n" : "\n");
  772.                 flag = 0;
  773.                 continue;
  774.             } else {
  775.                     /* normal file name, print it out */
  776.                 if (l->e_name[CLength-1] != '-' &&
  777.                     l->e_name[CLength-1] != ' ')
  778.                     l->e_name[CLength-1] = '*';
  779.                 printf("|-%.*s",Maxes[Level],l->e_name);
  780.                 if (l->last) {
  781.                     con[j] = 0;
  782.                 } else {
  783.                     con[j] = 1;
  784.                 }
  785.     #ifdef    STATS
  786.                 if (Gflag || Longflg) {
  787.                     if (Compact) {
  788.                         printf(" %04o ", l->e_mode & 07777);
  789.                         if (Longflg)
  790.                             printf("%5u ", l->e_uid);
  791.                         if (Gflag)
  792.                             printf("%5u ", l->e_gid);
  793.                         printf("%7ld",
  794.                             (l->e_size+511L)/512L);
  795.                     } else {
  796.                         printf("%s ", getmode(l->e_mode));
  797.                         if (Longflg)
  798.                             printf("%8.8s ",guid(l->e_uid));
  799.                         if (Gflag)
  800.                             printf("%8.8s ",ggid(l->e_gid));
  801.                         printf("%7ld",
  802.                             (l->e_size+511L)/512L);
  803.                     }
  804.                 }
  805.     #endif    STATS
  806.             }
  807.             printf("\n");
  808.     
  809.             if (l->last) {
  810.                 /* walk back up */
  811.                 while (l->last) {
  812.                     --Level;
  813.                     if (--top <= 0)
  814.                         return;
  815.                     l = stack[top];
  816.                 }
  817.             }
  818.             l = addr(l,1);
  819.             ++count;
  820.         }
  821.     }
  822.     
  823.     #ifdef    STATS
  824.     
  825.     char *
  826.     guid(uid)
  827.     short uid;
  828.     {
  829.         static char tb[10];
  830.         extern struct passwd *getpwuid();
  831.         struct passwd *pswd;
  832.     
  833.         pswd = getpwuid(uid);
  834.         if (pswd == NULL)
  835.             sprintf(tb,"%u", uid);
  836.         else
  837.             sprintf(tb, "%8s", pswd->pw_name);
  838.         return(tb);
  839.     }
  840.     
  841.     char *
  842.     ggid(gid)
  843.     short gid;
  844.     {
  845.         static char tb[10];
  846.         extern struct group *getgrgid();
  847.         struct group *grp;
  848.     
  849.         grp = getgrgid(gid);
  850.         if (grp == NULL)
  851.             sprintf(tb,"%u", gid);
  852.         else
  853.             sprintf(tb, "%8s", grp->gr_name);
  854.         return(tb);
  855.     }
  856.     
  857.     /* take the mode and make it into a nice character string */
  858.     
  859.     char    *
  860.     getmode(p_mode)
  861.     unsigned short p_mode;
  862.     {
  863.         static char a_mode[16];
  864.         register int    i = 0, j = 0;
  865.     
  866.         a_mode[j++] = ' ';
  867.     
  868.         switch (p_mode & S_IFMT) {
  869.     #ifdef LINK
  870.         case S_IFLNK:
  871.             a_mode[j++] = 'l';
  872.             break;
  873.     #endif LINK
  874.         case S_IFDIR:
  875.             a_mode[j++] = 'd';
  876.             break;
  877.     #ifdef    S_IFMPC        /* defined in stats.h if you have MPX files */
  878.         case S_IFMPC:
  879.             a_mode[j-1] = 'm';
  880.             /* FALL THROUGH */
  881.     #endif    S_IFMPC
  882.         case S_IFCHR:
  883.             a_mode[j++] = 'c';
  884.             break;
  885.     #ifdef    S_IFMPB        /* defined in stats.h if you have MPX files */
  886.         case S_IFMPB:
  887.             a_mode[j-1] = 'm';
  888.             /* FALL THROUGH */
  889.     #endif    S_IFMPB
  890.         case S_IFBLK:
  891.             a_mode[j++] = 'b';
  892.             break;
  893.         case S_IFREG:
  894.         default:
  895.             a_mode[j++] = (p_mode & S_ISVTX) ? 't' : ' ';
  896.             break;
  897.         }
  898.         a_mode[j++] = ' ';
  899.         for( i = 0;i<3;i++ ) {
  900.             a_mode[j++] = (p_mode<<(3*i) & S_IREAD) ? 'r' : '-';
  901.             a_mode[j++] = (p_mode<<(3*i) & S_IWRITE) ? 'w' : '-';
  902.             a_mode[j++] = (i<2 && (p_mode<<i & S_ISUID)) ? 's' :
  903.                       ((p_mode<<(3*i) & S_IEXEC ) ? 'x' : '-');
  904.             a_mode[j++] = ' ';
  905.         }
  906.         a_mode[j] = '\0';
  907.         return(a_mode);
  908.     }
  909.     #endif
  910.     
  911.     /* stln - sortof like strlen, returns length up to Length-1 */
  912.     stln(st)
  913.     register char *st;
  914.     {
  915.         register int t;
  916.     
  917.         for (t=0; t<Length-1; ++t)
  918.             if (!st[t])
  919.                 return (++t);
  920.         return (++t);
  921.     }
  922.     
  923.     /*
  924.      * Return a pointer into str at which the character
  925.      * c last appears; NULL if not found.
  926.     */
  927.     
  928.     char *
  929.     rindex(str, c)
  930.     register char *str, c;
  931.     {
  932.         register char *ptr;
  933.     
  934.         ptr = NULL;
  935.         do {
  936.             if (*str == c)
  937.                 ptr = str;
  938.         } while (*str++);
  939.         return(ptr);
  940.     }
  941. //E*O*F dtree.c//
  942.  
  943. echo Possible errors detected by \'wc\' [hopefully none]:
  944. temp=/tmp/shar$$
  945. trap "rm -f $temp; exit" 0 1 2 3 15
  946. cat > $temp <<\!!!
  947.       87     455    2507 dtree.1
  948.      831    3183   21040 dtree.c
  949.      918    3638   23547 total
  950. !!!
  951. wc  dtree.1 dtree.c | sed 's=[^ ]*/==' | diff -b $temp -
  952. exit 0
  953.  
  954.