home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume9 / dtree / dtree.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-12-20  |  21.3 KB  |  902 lines

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