1##
2##  path -- Deal with program paths
3##  Copyright (c) 1998-2008 Ralf S. Engelschall <rse@engelschall.com>
4##
5##  This file is part of shtool and free software; you can redistribute
6##  it and/or modify it under the terms of the GNU General Public
7##  License as published by the Free Software Foundation; either version
8##  2 of the License, or (at your option) any later version.
9##
10##  This file is distributed in the hope that it will be useful,
11##  but WITHOUT ANY WARRANTY; without even the implied warranty of
12##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13##  General Public License for more details.
14##
15##  You should have received a copy of the GNU General Public License
16##  along with this program; if not, write to the Free Software
17##  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
18##  USA, or contact Ralf S. Engelschall <rse@engelschall.com>.
19##
20
21str_tool="path"
22str_usage="[-s|--suppress] [-r|--reverse] [-d|--dirname] [-b|--basename] [-m|--magic] [-p|--path <path>] <str> [<str> ...]"
23gen_tmpfile=yes
24arg_spec="1+"
25opt_spec="s.r.d.b.m.p:"
26opt_alias="s:suppress,r:reverse,d:dirname,b:basename,m:magic,p:path"
27opt_s=no
28opt_r=no
29opt_d=no
30opt_b=no
31opt_m=no
32opt_p="$PATH"
33
34. ./sh.common
35
36namelist="$*"
37
38#   check whether the test command supports the -x option
39if [ -x /bin/sh ] 2>/dev/null; then
40    minusx="-x"
41else
42    minusx="-r"
43fi
44
45#   split path string
46paths="`echo $opt_p |\
47        sed -e 's/^:/.:/' \
48            -e 's/::/:.:/g' \
49            -e 's/:$/:./' \
50            -e 's/:/ /g'`"
51
52#   SPECIAL REQUEST
53#   translate forward to reverse path
54if [ ".$opt_r" = .yes ]; then
55    if [ "x$namelist" = "x." ]; then
56        rp='.'
57    else
58        rp=''
59        for pe in `IFS="$IFS/"; echo $namelist`; do
60            rp="../$rp"
61        done
62    fi
63    echo $rp | sed -e 's:/$::'
64    shtool_exit 0
65fi
66
67#   SPECIAL REQUEST
68#   strip out directory or base name
69if [ ".$opt_d" = .yes ]; then
70    echo "$namelist" |\
71    sed -e 's;[^/]*$;;' -e 's;\(.\)/$;\1;'
72    shtool_exit 0
73fi
74if [ ".$opt_b" = .yes ]; then
75    echo "$namelist" |\
76    sed -e 's;.*/\([^/]*\)$;\1;'
77    shtool_exit 0
78fi
79
80#   MAGIC SITUATION
81#   Perl Interpreter (perl)
82if [ ".$opt_m" = .yes ] && [ ".$namelist" = .perl ]; then
83    rm -f $tmpfile >/dev/null 2>&1
84    touch $tmpfile
85    found=0
86    pc=99
87    for dir in $paths; do
88        dir=`echo $dir | sed -e 's;/*$;;'`
89        nc=99
90        for name in perl perl5 miniperl; do
91             if [ $minusx "$dir/$name" ] && [ ! -d "$dir/$name" ]; then
92                 perl="$dir/$name"
93                 pv=`$perl -e 'printf("%.3f", $]);'`
94                 echo "$pv:$pc:$nc:$perl" >>$tmpfile
95                 found=1
96             fi
97             nc=`expr $nc - 1`
98        done
99        pc=`expr $pc - 1`
100    done
101    if [ $found = 1 ]; then
102        perl="`cat $tmpfile | sort -r -u | sed -e 'q' | cut -d: -f4`"
103        rm -f $tmpfile >/dev/null 2>&1
104        echo "$perl"
105        shtool_exit 0
106    fi
107    rm -f $tmpfile >/dev/null 2>&1
108    shtool_exit 1
109fi
110
111#   MAGIC SITUATION
112#   C pre-processor (cpp)
113if [ ".$opt_m" = .yes ] && [ ".$namelist" = .cpp ]; then
114    echo >$tmpfile.c "#include <assert.h>"
115    echo >>$tmpfile.c "Syntax Error"
116    #   1. try the standard cc -E approach
117    cpp="${CC-cc} -E"
118    (eval "$cpp $tmpfile.c >/dev/null") 2>$tmpfile.out
119    my_error=`grep -v '^ *+' $tmpfile.out`
120    if [ ".$my_error" != . ]; then
121        #   2. try the cc -E approach and GCC's -traditional-ccp option
122        cpp="${CC-cc} -E -traditional-cpp"
123        (eval "$cpp $tmpfile.c >/dev/null") 2>$tmpfile.out
124        my_error=`grep -v '^ *+' $tmpfile.out`
125        if [ ".$my_error" != . ]; then
126            #   3. try a standalone cpp command in path and lib dirs
127            for path in $paths /lib /usr/lib /usr/local/lib; do
128                path=`echo $path | sed -e 's;/*$;;'`
129                if [ $minusx "$path/cpp" ] && [ ! -d "$path/cpp" ]; then
130                    cpp="$path/cpp"
131                    break
132                fi
133            done
134            if [ ".$cpp" != . ]; then
135                (eval "$cpp $tmpfile.c >/dev/null") 2>$tmpfile.out
136                my_error=`grep -v '^ *+' $tmpfile.out`
137                if [ ".$my_error" != . ]; then
138                    #   ok, we gave up...
139                    cpp=''
140                fi
141            fi
142        fi
143    fi
144    rm -f $tmpfile >/dev/null 2>&1
145    rm -f $tmpfile.c $tmpfile.out >/dev/null 2>&1
146    if [ ".$cpp" != . ]; then
147        echo "$cpp"
148        shtool_exit 0
149    fi
150    shtool_exit 1
151fi
152
153#   STANDARD SITUATION
154#   iterate over names
155for name in $namelist; do
156    #   iterate over paths
157    for path in $paths; do
158        path=`echo $path | sed -e 's;/*$;;'`
159        if [ $minusx "$path/$name" ] && [ ! -d "$path/$name" ]; then
160            if [ ".$opt_s" != .yes ]; then
161                echo "$path/$name"
162            fi
163            shtool_exit 0
164        fi
165    done
166done
167
168shtool_exit 1
169
170##
171##  manual page
172##
173
174=pod
175
176=head1 NAME
177
178B<shtool path> - B<GNU shtool> command dealing with shell path variables
179
180=head1 SYNOPSIS
181
182B<shtool path>
183[B<-s>|B<--suppress>]
184[B<-r>|B<--reverse>]
185[B<-d>|B<--dirname>]
186[B<-b>|B<--basename>]
187[B<-m>|B<--magic>]
188[B<-p>|B<--path> I<path>]
189I<str> [I<str> ...]
190
191=head1 DESCRIPTION
192
193This command deals with shell C<$PATH> variables. It can find a program
194through one or more filenames given by one or more I<str> arguments.
195It prints the absolute filesystem path to the program displayed on
196C<stdout> plus an exit code of 0 if it was really found.
197
198=head1 OPTIONS
199
200The following command line options are available.
201
202=over 4
203
204=item B<-s>, B<--suppress>
205
206Supress output. Useful to only test whether a program exists with the
207help of the return code.
208
209=item B<-r>, B<--reverse>
210
211Transform a forward path to a subdirectory into a reverse path.
212
213=item B<-d>, B<--dirname>
214
215Output the directory name of I<str>.
216
217=item B<-b>, B<--basename>
218
219Output the base name of I<str>.
220
221=item B<-m>, B<--magic>
222
223Enable advanced magic search for "C<perl>" and "C<cpp>".
224
225=item B<-p>, B<--path> I<path>
226
227Search in I<path>. Default is to search in $PATH.
228
229=back
230
231=head1 EXAMPLE
232
233 #   shell script
234 awk=`shtool path -p "${PATH}:." gawk nawk awk`
235 perl=`shtool path -m perl`
236 cpp=`shtool path -m cpp`
237 revpath=`shtool path -r path/to/subdir`
238
239=head1 HISTORY
240
241The B<GNU shtool> B<path> command was originally written by Ralf S.
242Engelschall E<lt>rse@engelschall.comE<gt> in 1998 for B<Apache>. It was
243later taken over into B<GNU shtool>.
244
245=head1 SEE ALSO
246
247shtool(1), which(1).
248
249=cut
250
251