1#!/usr/bin/ksh93
2
3#
4# CDDL HEADER START
5#
6# The contents of this file are subject to the terms of the
7# Common Development and Distribution License (the "License").
8# You may not use this file except in compliance with the License.
9#
10# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11# or http://www.opensolaris.org/os/licensing.
12# See the License for the specific language governing permissions
13# and limitations under the License.
14#
15# When distributing Covered Code, include this CDDL HEADER in each
16# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17# If applicable, add the following below this CDDL HEADER, with the
18# fields enclosed by brackets "[]" replaced with your own identifying
19# information: Portions Copyright [yyyy] [name of copyright owner]
20#
21# CDDL HEADER END
22#
23
24#
25# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
26#
27
28# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant
29export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin
30
31# Make sure all math stuff runs in the "C" locale to avoid problems
32# with alternative # radix point representations (e.g. ',' instead of
33# '.' in de_DE.*-locales). This needs to be set _before_ any
34# floating-point constants are defined in this script).
35if [[ "${LC_ALL}" != "" ]] ; then
36    export \
37        LC_MONETARY="${LC_ALL}" \
38        LC_MESSAGES="${LC_ALL}" \
39        LC_COLLATE="${LC_ALL}" \
40        LC_CTYPE="${LC_ALL}"
41        unset LC_ALL
42fi
43export LC_NUMERIC=C
44
45function fatal_error
46{
47	print -u2 "${progname}: $*"
48	exit 1
49}
50
51
52function do_directory
53{
54	nameref tree=$1
55	typeset basedir="$2"
56	
57	typeset basename
58	typeset dirname
59	typeset i
60	typeset dummy
61	
62	typeset -C -A tree.files
63	typeset -C -A tree.dirs
64
65	find "${basedir}"/* -prune 2>/dev/null | while read i ; do
66		dirname="$(dirname "$i")"
67		basename="$(basename "$i")"
68		
69		# define "node"
70		if [[ -d "$i" ]] ; then
71			typeset -C tree.dirs["${basename}"]
72			nameref node=tree.dirs["${basename}"]
73			typeset -C node.flags
74			node.flags.dir="true"
75			node.flags.file="false"
76		else
77			typeset -C tree.files["${basename}"]
78			nameref node=tree.files["${basename}"]
79			typeset -C node.flags
80			
81			node.flags.dir="false"
82			node.flags.file="true"
83		fi
84
85		# basic attributes
86		typeset -C node.paths=(
87			dirname="${dirname}"
88			basename="${basename}"
89			path="${i}"
90		)
91		
92		nameref nflags=node.flags
93		[[ -r "$i" ]] && nflags.readable="true"   || nflags.readable="false"
94		[[ -w "$i" ]] && nflags.writeable="true"  || nflags.writeable="false"
95		[[ -x "$i" ]] && nflags.executable="true" || nflags.executable="false"
96
97		[[ -b "$i" ]] && nflags.blockdevice="true"     || nflags.blockdevice="false"
98		[[ -c "$i" ]] && nflags.characterdevice="true" || nflags.characterdevice="false"
99		[[ -S "$i" ]] && nflags.socket="true"          || nflags.socket="false"
100
101		[[ -L "$i" ]] && nflags.symlink="true" || nflags.symlink="false"
102
103		integer node.size
104		integer node.links
105		typeset -C node.owner
106		( [[ -x /usr/bin/runat ]] && ls -@ade "$i" || ls -lade "$i" ) |
107		IFS=' ' read \
108			node.mask \
109			node.links \
110			node.owner.uid \
111			node.owner.gid \
112			node.size \
113			dummy
114		
115		typeset -C node.extended_attributes
116		if [[ ${node.mask} == ~(Er)@ ]] ; then
117			node.extended_attributes.hasattrs="true"
118			typeset -a attrlist=(
119				$( runat "$i" "ls -1" )
120			)
121		else
122			node.extended_attributes.hasattrs="false"
123		fi
124		
125		if ${nflags.readable} ; then
126			# note that /usr/xpg4/bin/file does not use $'\t' as seperator - we
127			# have to use ':' instead.
128			file -h "$i" | IFS=' ' read dummy node.filetype
129		fi
130
131		if ${nflags.dir} ; then
132			do_directory "${!node}" "$i"
133		fi
134	done
135	
136	# remove empty lists
137	(( ${#tree.files[@]} == 0 )) && unset tree.files
138	(( ${#tree.dirs[@]} == 0 ))  && unset tree.dirs
139
140	return 0
141}
142
143
144function pathtovartree
145{
146	nameref tree=$1
147	typeset basedir="$2"
148	
149	do_directory tree "${basedir}"
150
151	return 0
152}
153
154function usage
155{
156	OPTIND=0
157	getopts -a "${progname}" "${filetree1_usage}" OPT '-?'
158	exit 2
159}
160
161# program start
162builtin basename
163builtin cat
164builtin dirname
165builtin date
166builtin uname
167
168typeset progname="${ basename "${0}" ; }"
169
170typeset -r filetree1_usage=$'+
171[-?\n@(#)\$Id: filetree1 (Roland Mainz) 2009-05-06 \$\n]
172[-author?Roland Mainz <roland.mainz@sun.com>]
173[-author?Roland Mainz <roland.mainz@nrubsig.org>]
174[+NAME?filetree1 - file tree demo]
175[+DESCRIPTION?\bfiletree1\b is a small ksh93 compound variable demo
176	which accepts a directory name as input, and then builds tree
177	nodes for all files+directories and stores all file attributes
178	in these notes and then outputs the tree in the format
179	specified by viewmode (either "list", "namelist", "tree" or "compacttree")..]
180
181viewmode dirs
182
183[+SEE ALSO?\bksh93\b(1), \bfile\b(1)]
184'
185
186while getopts -a "${progname}" "${filetree1_usage}" OPT ; do 
187#	printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|"
188	case ${OPT} in
189		*)	usage ;;
190	esac
191done
192shift $((OPTIND-1))
193
194typeset viewmode="$1"
195shift
196
197if [[ "${viewmode}" != ~(Elr)(list|namelist|tree|compacttree) ]] ; then
198	fatal_error $"Invalid view mode \"${viewmode}\"."
199fi
200
201typeset -C myfiletree
202
203while (( $# > 0 )) ; do
204	print -u2 -f "# Scanning %s ...\n" "${1}"
205	pathtovartree myfiletree "${1}"
206	shift
207done
208print -u2 $"#parsing completed."
209
210case "${viewmode}" in
211	list)
212		set | egrep "^myfiletree\[" | fgrep -v ']=$'
213		;;
214	namelist)
215		typeset + | egrep "^myfiletree\["
216		;;
217	tree)
218		print -v myfiletree
219		;;
220	compacttree)
221		print -C myfiletree
222		;;
223	*)
224		fatal_error $"Invalid view mode \"${viewmode}\"."
225		;;
226esac
227
228print -u2 $"#done."
229
230exit 0
231# EOF.
232