Initial hack at a DB operations struct
[pacman-ng.git] / contrib / pactree.in
blob29c6af6c2a96351fc18b32309919eae410d140ee
1 #!@BASH@
2 # pactree : a simple dependency tree viewer
4 # Copyright (C) 2008 Carlo "carlocci" Bersani <carlocci@gmail.com>
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 # Original http://carlocci.ngi.it/arch/pactree
20 # Credit to scj for the graphviz idea
22 # set the colors
23 branch1_color="\033[0;33m"    #Brown
24 branch2_color="\033[0;37m"    #Gray
25 leaf_color="\033[1;32m"       #Light green
26 leaf2_color="\033[0;32m"      #Green
28 # set the separators
29 separator="   "
30 branch_tip1="|--"
31 branch_tip2="+--"
32 provides="provides "
34 # set the graphviz options
35 # http://www.graphviz.org/doc/info/output.html for available output formats
36 # http://www.graphviz.org/doc/info/colors.html for available colors
37 gformat="png"                 #output format
38 start_color="red"             #START color
39 nodes_color="green"           #color of the nodes
40 arrow1_color="chocolate4"     #color of the normal arrow
41 arrow2_color="grey"           #color of the "provided by" headless arrow
43 readonly prog_name="pactree"
44 readonly prog_ver="0.3"
46 _usage(){
47         echo "This program generates the dependency tree of an installed package"
48         echo "Usage:   $prog_name [OPTIONS] <installed packages>"
49         echo
50         echo " OPTIONS:"
51         echo "  -c, --color                Enable color output"
52         echo "  -d, --depth INT            Limit the shown dependencies depth"
53         echo "  -g, --graph                Use graphviz to make an image of the tree"
54         echo "  -l, --linear               Enable linear output"
55         echo "  -r, --reversed             Show reversed dependancies"
56         echo "  -s, --silent               Shh, let me hear those errors!"
57         echo "  -u, --unique               Print the dependency list with no duplicates"
58         echo
59         echo "  -h, --help                 Print this help message"
60         echo "  -v, --version              Print the program name and version"
61         echo
62         echo "Example: $prog_name -c -d2 readline"
65 _version(){
66         echo "$prog_name version $prog_ver"
67         echo "Copyright (C) 2008 Carlo \"carlocci\" Bersani <carlocci@gmail.com>"
69 # end of the friendliness
72 # grab a field from the database: $1=path/to/file, $2=field to grab
73 _grabfield(){
74         for line in $(cat "$1" 2>/dev/null ); do
75                 if [ -z "$line" ]; then
76                         continue;
77                 fi;
78                 if [[ "$line" =~ %[A-Z]*% ]]; then
79                         current="$line"
80                         continue;
81                 fi;
82                 if [ "$current" = "$2" ]; then
83                         echo "$line"
84                 fi;
85         done
89 # find a dep in the db: $1=dep, $2=field, $3=dbfile, ret=file list
90 _finddep(){
91         for line in $(awk 'BEGIN{RS=""}
92                            {
93                            if ($1=="'"$2"'"){
94                              for (i=2 ; i<=NF ; ++i){
95                                if ($i ~ /^'"$1"'([<>=]+.*|)$/ ){
96                                  print FILENAME}
97                                }
98                              }
99                            }' $(find $pac_db -name $3)); do
100                 echo "${line%/*}"
101         done
105 # Recursive function: does all of the work, pays all of the taxes     #
106 _tree(){
107         pkg_name="$1"
108         pkg_dirs="$(echo $pac_db/$pkg_name-[0-9]*)"
110         # Is $pkg_name real or provided?
111         [ ! -d "$pkg_dirs" ] && pkg_dirs="$(_finddep $pkg_name %PROVIDES% depends)"
113         for pkg_dir in $pkg_dirs ; do
114                 spaces="$2"
115                 unset provided
116                 branch_tip="$branch_tip1"
117                 branch_color="$branch1_color"
118                 pkg_name="$(_grabfield "$pkg_dir/desc" %NAME%)"
119                 if [ ! "$pkg_name" = "$1" ]; then
120                         provided="$leaf2_color $provides$leaf_color$1"
121                         branch_tip="$branch_tip2"
122                         branch_color="$branch2_color"
123                         if [ $graphviz -eq 1 ] && [[ ! "${dep_list[@]}" =~ _$1_ ]] && [ $spaces -ne $((max_depth+1)) ]; then
124                                 echo "\"$1\" -> \"$pkg_name\" [arrowhead=none, color=$arrow2_color];"
125                                 dep_list=( "${dep_list[@]}" "_$1_" )
126                                 _tree "$pkg_name" $((spaces+1))
127                                 continue
128                         fi
129                 fi
131                 # Generate the spacer
132                 spacer=""
133                 for ((count = 0; count < spaces; count++)); do
134                         spacer="$spacer$separator"
135                 done
136                 unset count
137                 spacer="$spacer$branch_tip"
139                 [ $silent -ne 1 ] &&    echo -e "$branch_color$spacer$leaf_color$pkg_name$provided"
141                 [ ! -d "$pkg_dir" ] && echo "No $pkg_name in the database (inconsistent database?)" >&2
143                 if [[ ! " ${dep_list[@]} " =~ " $pkg_name " ]] && [ $spaces -ne $max_depth ]; then
144                         dep_list=( "${dep_list[@]}" "$pkg_name" )
145                         if [ $reversed_dep -eq 0 ]; then
146                                 deps_pkg="$(_grabfield "$pkg_dir/depends" %DEPENDS%)"
147                         else
148                                 reqs_pkg_dir="$(_finddep "$pkg_name" %DEPENDS% depends)"
149                                 unset deps_pkg
150                                 for req_pkg_dir in $reqs_pkg_dir; do
151                                         deps_pkg=$(echo "$deps_pkg" "$(_grabfield "$req_pkg_dir/desc" %NAME%)")
152                                 done
153                         fi
154                         for dep_pkg in $deps_pkg; do
155                                 spaces=$2               #Bash scoping ;_;
156                                 if [ $graphviz -eq 1 ]; then
157                                         echo "\"$1\" -> \"${dep_pkg%%[<>=]*}\" [color=$arrow1_color];"
158                                 fi
159                                 _tree "${dep_pkg%%[<>=]*}" $((spaces+1))
160                         done
161                 fi
162         done
166 # Main program: gets all of the money, pays none of the taxes
168 # Command line parameters parser
169 if [ $# -eq 0 ]; then
170         _usage
171         exit 1
174 options=( "$@" )
175 len_options=${#options[@]}
176 for (( n=0 ; n < $len_options ; n++ )); do
177         if [ "${options[$n]}" = "--" ]; then
178                 unset options[$n]
179                 break
180         fi
181         if [ "${options[$n]}" = "-h" -o "${options[$n]}" = "--help" ]; then
182                 _usage
183                 exit 0
184         fi
186         if [ "${options[$n]}" = "-v" -o "${options[$n]}" = "--version" ]; then
187                 _version
188                 exit 0
189         fi
191         if [ "${options[$n]}" = "-l" -o "${options[$n]}" = "--linear" ]; then
192                 unset options[$n]
193                 linear=1
194                 continue
195         fi
197         if [ "${options[$n]}" = "-s" -o "${options[$n]}" = "--silent" ]; then
198                 unset options[$n]
199                 silent=1
200                 continue
201         fi
203         if [ "${options[$n]}" = "-u" -o "${options[$n]}" = "--unique" ]; then
204                 unset options[$n]
205                 silent=1
206                 nodup=1
207                 continue
208         fi
210         if [ "${options[$n]}" = "-g" -o "${options[$n]}" = "--graph" ]; then
211                 unset options[$n]
212                 graphviz=1
213                 continue
214         fi
216         if [ "${options[$n]}" = "-c" -o "${options[$n]}" = "--color" ]; then
217                 unset options[$n]
218                 colored=1
219                 continue
220         fi
222         if [ "${options[$n]}" = "-r" -o "${options[$n]}" = "--reversed" ]; then
223                 unset options[$n]
224                 reversed_dep=1
225                 continue
226         fi
228         if [[ "${options[$n]}" =~ -d[[:digit:]]+ || "${options[$n]}" == "--depth" ]]; then
229                 if [[ "${options[$n]#-d}" =~ [[:digit:]]+ ]]; then
230                         max_depth="${options[$n]#-d}"
231                 elif [[ ${options[$((n+1))]} =~ [[:digit:]]+ ]]; then
232                         max_depth="${options[$((n+1))]}"
233                         unset options[$((n+1))]
234                         ((++n))
235                 fi
236                 unset options[$n]
237                 continue
238         fi
239 done
240 # End of the dumb command line parser
242 # Env
243 colored=${colored:-0}
244 max_depth=${max_depth:--10}
245 linear=${linear:-0}
246 silent=${silent:-0}
247 nodup=${nodup:-0}
248 graphviz=${graphviz:-0}
249 reversed_dep=${reversed_dep:-0}
251 if [ $colored -ne 1 ]; then
252         unset branch1_color
253         unset leaf_color
254         unset leaf2_color
255         unset branch2_color
258 if [ $linear -eq 1 ]; then
259         unset separator
260         unset branch_tip1
261         unset branch_tip2
262         unset provides
265 if [ $graphviz -eq 1 ]; then
266         silent=1
267         nodup=0
268         if [ ! -f /usr/bin/dot ]; then
269                 echo "ERROR: package graphviz is not installed"
270                 echo "       Run pacman -S graphviz to install it"
271                 exit 1
272         fi
275 if [ ! -r @sysconfdir@/pacman.conf ]; then
276         echo "ERROR: unable to read @sysconfdir@/pacman.conf"
277         exit 1
278 else
279         eval $(awk '/DBPath/ {print $1$2$3}' @sysconfdir@/pacman.conf)
282 pac_db="${DBPath:-@localstatedir@/lib/pacman}/local"
284 if [ ! -d "$pac_db" ] ; then
285         echo "ERROR: pacman database directory ${pac_db} not found"
286         exit 1
288 # Env End
291 # Program starts
292 _main(){
293         for pkg_name in ${options[@]} ; do
294                 [ $graphviz -eq 1 ] && echo -e "\"START\" -> \"$pkg_name\" ;"
295                 _tree "$pkg_name" 0
296                 if [ $nodup -eq 1 ]; then
297                         for pkg_tree in ${dep_list[@]} ; do
298                                 echo "$pkg_tree"
299                         done
300                 fi
301         done
302         if [ $silent -eq 0 ]; then
303                 echo -ne '\033[0m' # return colors to normal?
304                 echo -ne '\033[?25h' #return cursor to normal?
305         fi
309 if [ $graphviz -eq 1 ]; then
310         root_pkgs="${options[@]}"
311         # Uncomment for the "generated by pactree" node in graphviz
312         #advert="xyz [height=0.07, fontsize=8.0, label=\"GENERATED WITH PACTREE\",shape=box,color="black",style=filled,fontcolor="white"];\n"
313         if [ $reversed_dep -eq 0 ]; then
314                 file_extension="deps.$gformat"
315         else
316                 file_extension="reqs.$gformat"
317         fi
318         echo -e "digraph G { START [color=$start_color, style=filled];\n node [style=filled, color=$nodes_color];\n$(_main)\n$advert}" | dot -T$gformat -o "${root_pkgs// /_}.$file_extension"
319 else _main
322 # vim: set ts=2 sw=2 noet: