1#! /bin/bash
2#
3# original from:
4# vtree: visual directory tree
5# @(#) vtree.sh 1.1 91/07/01
6# 90/04 john h. dubois iii (john@armory.com)
7# 91/07/01 fixed bug that caused problems when dir given on command line,
8#          added some info to help, changed to 4-space indenting
9#
10# conversion to bash v2 syntax done by Chet Ramey
11#
12help=\
13"Syntax: vtree [startdir] [namelen=#] [linelen=#]
14If startdir is not specified, tree will start at current dir.
15
16namelen specifies the minimum number of characters of a directory name that
17are guaranteed to be printed.
18This is a tradeoff between the number of tree levels that can fit on a
19screen line and the number of chars of each dir name that can be printed.
20In most cases it will be possible to print more than namelen characters of
21the name (a name up to namelen+1 chars will always be printed in full),
22but in some cases truncation down to namelen chars will occur.
23If truncation occurs, a '>' is printed at the end of the name.
24namelen=8 (the default) typically causes about 5 dirs/1000 to be truncated.
25namelen=7 typically causes about 10 dirs/1000 to be truncated.
26namelen=8 will allow 6 full length dirs to be printed in 79 columns.
27namelen=7 will allow 7 full length dirs to be printed in 79 columns;
28
29linelen specifies the maximum number of characters to print on one screen
30line.  All characters beyond this are truncated.  The default is 1024.
31To avoid line wrap on an 80 column terminal with autowrap, use linelen=79.
32"
33
34for i in "$@"; do
35    case $i in
36    -h) echo "$help"; exit;;
37    *=*)
38        vars="$vars $i"
39        ;;
40    *)
41        if [ ! -x $i ] || [ ! -d $i ]; then     # arg must be a dir and executable
42            echo "$i: directory not accessible."
43            exit
44        fi
45        cd $i
46        ;;
47    esac
48    shift
49done
50
51pwd     # print path of root of tree
52
53# find all directories depth first; ignore permission errors
54find . -type d -print 2> /dev/null | \
55gawk -F/ '
56
57# Do this block for NR == 1 instead of BEGIN because command line var
58# assignments are not done until after BEGIN block is executed.
59NR == 1 {
60    if (namelen)
61        MaxLen = namelen;
62    else
63        MaxLen = 8;
64    if (!linelen)
65        linelen = 1024
66    HSpace = substr("              ",1,MaxLen);     # used to indent tree
67    n = 0;          # number of dirs found on one major branch
68}
69
70$0 != "." {     # do for every line produced by find except tree root dir
71    if (NF == 2 && n > 0)   # print major branch whenever a new one starts
72        list();
73    Depth[n] = NF - 1;      # record depth and name of dir
74     Name[n++] = $NF;
75}
76
77END {
78    list()  # print last major branch
79}
80
81function list() {
82    Line = Name[0];     # initialize first line of branch to be branch base
83    for (i = 1; i < n; i++) {   # for each name in major branch
84        if (Depth[i] == Depth[i-1] + 1)
85            AddHLink(); # if moving deeper into branch, use same line
86        else {
87            print substr(Line,1,linelen);   # last line is done; print it
88            Line = "";  # start new line
89            # print indentation, vert links, and vert/horiz links
90            for (d = 1; d < Depth[i] - 1; d++)  # for each level of indentation
91                # if a vert. link has been established for this level
92                if (VLink[d])
93                    Line = Line HSpace " |  ";
94                else        # print empty indentation
95                    Line = Line HSpace "    ";
96            # Print last part of vert. link
97            if (VLink[d] == i) {
98                VLink[d] = 0;   # mark level for no vert link
99                Line = Line HSpace " \\--";
100            }
101            else
102                Line = Line HSpace " |--";
103        }
104        Line = Line Name[i];    # Add dir name to line
105    }
106    print substr(Line,1,linelen);   # print last line of major branch
107    n = 0;      # reset name counter
108}
109
110function AddHLink() {
111    NDepth = Depth[i];  # Depth of this name
112    VLink[NDepth - 1] = 0;
113    # search until a name found at a level less than this one
114    for (j = i + 1; j < n && Depth[j] >= NDepth; j++)
115        # keep track of last name that VLink should connect to
116        if (Depth[j] == NDepth)
117            VLink[NDepth - 1] = j;
118    if (VLink[NDepth - 1]) {
119        NLine = substr(Line,1,(NDepth - 2) * (MaxLen + 4) + MaxLen + 1);
120        if (length(NLine) < length(Line))
121            Line = substr(NLine,1,length(NLine) - 1) ">"
122        else
123            Line = NLine;
124        Line = Line substr("--------------+--",
125            18 - ((NDepth - 1) * (MaxLen + 4) - length(Line)));
126    }
127    else {
128        NLine = substr(Line,1,(NDepth - 2) * (MaxLen + 4) + MaxLen + 3);
129        if (length(NLine) < length(Line))
130            Line = substr(NLine,1,length(NLine) - 1) ">"
131        else
132            Line = NLine;
133        Line = Line substr("-----------------",
134            1,(NDepth - 1) * (MaxLen + 4) - length(Line));
135    }
136}
137' $vars
138