diff options
Diffstat (limited to 'usr.sbin/bsdconfig/share/packages')
| -rw-r--r-- | usr.sbin/bsdconfig/share/packages/Makefile | 4 | ||||
| -rw-r--r-- | usr.sbin/bsdconfig/share/packages/Makefile.depend | 10 | ||||
| -rw-r--r-- | usr.sbin/bsdconfig/share/packages/categories.subr | 208 | ||||
| -rw-r--r-- | usr.sbin/bsdconfig/share/packages/index.awk | 98 | ||||
| -rw-r--r-- | usr.sbin/bsdconfig/share/packages/index.subr | 320 | ||||
| -rw-r--r-- | usr.sbin/bsdconfig/share/packages/musthavepkg.subr | 80 | ||||
| -rw-r--r-- | usr.sbin/bsdconfig/share/packages/packages.subr | 1201 | 
7 files changed, 1921 insertions, 0 deletions
| diff --git a/usr.sbin/bsdconfig/share/packages/Makefile b/usr.sbin/bsdconfig/share/packages/Makefile new file mode 100644 index 000000000000..939938e3f61a --- /dev/null +++ b/usr.sbin/bsdconfig/share/packages/Makefile @@ -0,0 +1,4 @@ +FILESDIR=	${SHAREDIR}/bsdconfig/packages +FILES=		categories.subr index.awk index.subr musthavepkg.subr packages.subr + +.include <bsd.prog.mk> diff --git a/usr.sbin/bsdconfig/share/packages/Makefile.depend b/usr.sbin/bsdconfig/share/packages/Makefile.depend new file mode 100644 index 000000000000..11aba52f82cf --- /dev/null +++ b/usr.sbin/bsdconfig/share/packages/Makefile.depend @@ -0,0 +1,10 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/bsdconfig/share/packages/categories.subr b/usr.sbin/bsdconfig/share/packages/categories.subr new file mode 100644 index 000000000000..96abaef7f16e --- /dev/null +++ b/usr.sbin/bsdconfig/share/packages/categories.subr @@ -0,0 +1,208 @@ +if [ ! "$_PACKAGES_CATEGORIES_SUBR" ]; then _PACKAGES_CATEGORIES_SUBR=1 +# +# Copyright (c) 2013 Devin Teske +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +#    notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +#    notice, this list of conditions and the following disclaimer in the +#    documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_dprintf "%s: loading includes..." packages/categories.subr +f_include $BSDCFG_SHARE/strings.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +f_include_lang $BSDCFG_LIBE/include/messages.subr + +############################################################ GLOBALS + +CATEGORIES= +NCATEGORIES=0 + +############################################################ FUNCTIONS + +# f_category_desc_get $category [$var_to_set] +# +# Fetch the description of a given category. Returns success if a match was +# found, otherwise failure. +# +# If $var_to_set is missing or NULL, the category description is printed to +# standard out for capturing in a sub-shell (which is less-recommended because +# of performance degredation; for example, when called in a loop). +# +f_category_desc_get() +{ +	local __category="$1" __var_to_set="$2" __cat __varcat + +	# Return failure if $category +	[ "$__category" ] || return $FAILURE + +	for __cat in $CATEGORIES; do +		[ "$__cat" = "$__category" ] || continue +		f_str2varname $__cat __varcat +		f_getvar _category_$__varcat $__var_to_set +		return $? +	done +	return $FAILURE +} + +# f_category_desc_set $category $desc +# +# Store a description in-association with a category. $category should be +# alphanumeric and can include the underscore [_] but should not contain +# whitespace. Returns success unless $category is NULL or no arguments. Use the +# f_category_desc_get() routine with the same $category to retrieve the stored +# description. +# +f_category_desc_set() +{ +	local category="$1" desc="$2" +	local cat varcat found= +	[ "$category" ] || return $FAILURE +	for cat in $CATEGORIES; do +		[ "$cat" = "$category" ] || continue +		f_str2varname $cat varcat +		f_isset _category_$varcat || continue +		found=1 && break +	done +	if [ ! "$found" ]; then +		CATEGORIES="$CATEGORIES $category" +	fi +	f_str2varname $category varcat +	setvar "_category_$varcat" "$desc" +	# Export the variable for awk(1) ENVIRON visibility +	export "_category_$varcat" +	return $SUCCESS +} + +############################################################ MAIN + +# +# Load descriptions for package categories. Note that we don't internationalize +# category names because this would be confusing for people used to browsing +# the FTP mirrors or are otherwise familiar with an interface that does not +# provide internationalized names. The descriptions can be used to provide i18n +# users a description of the non-i18n category name. +# +f_category() { f_category_desc_set "$1" "$2"; } +f_category All           "$msg_all_desc" +f_category accessibility "$msg_accessibility_desc" +f_category afterstep     "$msg_afterstep_desc" +f_category arabic        "$msg_arabic_desc" +f_category archivers     "$msg_archivers_desc" +f_category astro         "$msg_astro_desc" +f_category audio         "$msg_audio_desc" +f_category benchmarks    "$msg_benchmarks_desc" +f_category biology       "$msg_biology_desc" +f_category cad           "$msg_cad_desc" +f_category chinese       "$msg_chinese_desc" +f_category comms         "$msg_comms_desc" +f_category converters    "$msg_converters_desc" +f_category databases     "$msg_databases_desc" +f_category deskutils     "$msg_deskutils_desc" +f_category devel         "$msg_devel_desc" +f_category dns           "$msg_dns_desc" +f_category docs          "$msg_docs_desc" +f_category editors       "$msg_editors_desc" +f_category elisp         "$msg_elisp_desc" +f_category emulators     "$msg_emulators_desc" +f_category enlightenment "$msg_enlightenment_desc" +f_category finance       "$msg_finance_desc" +f_category french        "$msg_french_desc" +f_category ftp           "$msg_ftp_desc" +f_category games         "$msg_games_desc" +f_category geography     "$msg_geography_desc" +f_category german        "$msg_german_desc" +f_category gnome         "$msg_gnome_desc" +f_category gnustep       "$msg_gnustep_desc" +f_category graphics      "$msg_graphics_desc" +f_category hamradio      "$msg_hamradio_desc" +f_category haskell       "$msg_haskell_desc" +f_category hebrew        "$msg_hebrew_desc" +f_category hungarian     "$msg_hungarian_desc" +f_category ipv6          "$msg_ipv6_desc" +f_category irc           "$msg_irc_desc" +f_category japanese      "$msg_japanese_desc" +f_category java          "$msg_java_desc" +f_category kde           "$msg_kde_desc" +f_category kld           "$msg_kld_desc" +f_category korean        "$msg_korean_desc" +f_category lang          "$msg_lang_desc" +f_category linux         "$msg_linux_desc" +f_category lisp          "$msg_lisp_desc" +f_category mail          "$msg_mail_desc" +f_category math          "$msg_math_desc" +f_category mbone         "$msg_mbone_desc" +f_category misc          "$msg_misc_desc" +f_category multimedia    "$msg_multimedia_desc" +f_category net           "$msg_net_desc" +f_category net-im        "$msg_net_im_desc" +f_category net-mgmt      "$msg_net_mgmt_desc" +f_category net-p2p       "$msg_net_p2p_desc" +f_category news          "$msg_news_desc" +f_category palm          "$msg_palm_desc" +f_category parallel      "$msg_parallel_desc" +f_category pear          "$msg_pear_desc" +f_category perl5         "$msg_perl5_desc" +f_category plan9         "$msg_plan9_desc" +f_category polish        "$msg_polish_desc" +f_category ports-mgmt    "$msg_ports_mgmt_desc" +f_category portuguese    "$msg_portuguese_desc" +f_category print         "$msg_print_desc" +f_category python        "$msg_python_desc" +f_category ruby          "$msg_ruby_desc" +f_category rubygems      "$msg_rubygems_desc" +f_category russian       "$msg_russian_desc" +f_category scheme        "$msg_scheme_desc" +f_category science       "$msg_science_desc" +f_category security      "$msg_security_desc" +f_category shells        "$msg_shells_desc" +f_category spanish       "$msg_spanish_desc" +f_category sysutils      "$msg_sysutils_desc" +f_category tcl           "$msg_tcl_desc" +f_category textproc      "$msg_textproc_desc" +f_category tk            "$msg_tk_desc" +f_category ukrainian     "$msg_ukrainian_desc" +f_category vietnamese    "$msg_vietnamese_desc" +f_category windowmaker   "$msg_windowmaker_desc" +f_category www           "$msg_www_desc" +f_category x11           "$msg_x11_desc" +f_category x11-clocks    "$msg_x11_clocks_desc" +f_category x11-drivers   "$msg_x11_drivers_desc" +f_category x11-fm        "$msg_x11_fm_desc" +f_category x11-fonts     "$msg_x11_fonts_desc" +f_category x11-servers   "$msg_x11_servers_desc" +f_category x11-themes    "$msg_x11_themes_desc" +f_category x11-toolkits  "$msg_x11_toolkits_desc" +f_category x11-wm        "$msg_x11_wm_desc" +f_category xfce          "$msg_xfce_desc" +f_category zope          "$msg_zope_desc" + +f_count NCATEGORIES $CATEGORIES +f_dprintf "%s: Initialized %u package category descriptions." \ +          packages/categories.subr $NCATEGORIES + +f_dprintf "%s: Successfully loaded." packages/categories.subr + +fi # ! $_PACKAGES_CATEGORIES_SUBR diff --git a/usr.sbin/bsdconfig/share/packages/index.awk b/usr.sbin/bsdconfig/share/packages/index.awk new file mode 100644 index 000000000000..f22aa150bd63 --- /dev/null +++ b/usr.sbin/bsdconfig/share/packages/index.awk @@ -0,0 +1,98 @@ +function _asorti(src, dest) +{ +	k = nitems = 0 + +	# Copy src indices to dest and calculate array length +	for (i in src) dest[++nitems] = i + +	# Sort the array of indices (dest) using insertion sort method +	for (i = 1; i <= nitems; k = i++) +	{ +		idx = dest[i] +		while ((k > 0) && (dest[k] > idx)) +		{ +			dest[k+1] = dest[k] +			k-- +		} +		dest[k+1] = idx +	} + +	return nitems +} + +function print_category(category, npkgs, desc) +{ +	cat = category +	# Accent the category if the first page has been +	# cached (also acting as a visitation indicator) +	if ( ENVIRON["_index_page_" varcat "_1"] ) +		cat = cat "*" +	printf "'\''%s'\'' '\''%s " packages "'\'' '\''%s'\''\n", +	       cat, npkgs, desc >>tmpfile +} + +BEGIN{ +	cnt=0 +	div=int(npkg / 100) +	last=0 +	prefix = "" +} +{ +	cnt+=1 +	i = int(cnt / div) +	if (i > last) { +		last = i +		print "XXX" +		print i +		print msg +		print "XXX" +		fflush("/dev/stdout"); +	} +	varpkg = $1 +	gsub("[^" valid_chars "]", "_", varpkg) +	print "_categories_" varpkg "=\"" $7 "\"" >> tmpfile +	split($7, pkg_categories, /[[:space:]]+/) +	for (pkg_category in pkg_categories) +		categories[pkg_categories[pkg_category]]++ +	print "_rundeps_" varpkg "=\"" $9 "\"" >> tmpfile + +} +END { +	n = _asorti(categories, categories_sorted) +	# Produce package counts for each category +	for (i = 1; i <= n; i++) +	{ +		cat = varcat = categories_sorted[i] +		npkgs = categories[cat] +		gsub("[^" valid_chars "]", "_", varcat) +		print "_npkgs_" varcat "=\"" npkgs "\"" >>tmpfile +	} +	# +	# Create menu list and generate list of categories at same time +	print "CATEGORY_MENU_LIST=\"" >>tmpfile +	print_category(msg_all, npkg, msg_all_desc) +	category_list = "" +	for (i = 1; i <= n; i++) +	{ +		cat = varcat = categories_sorted[i] +		npkgs = categories[cat] +		cur_prefix = tolower(substr(cat, 1, 1)) +		if ( prefix != cur_prefix ) +			prefix = cur_prefix +		else +			cat = " " cat +		gsub("[^" valid_chars "]", "_", varcat) +		desc = ENVIRON["_category_" varcat] +		if ( ! desc ) desc = default_desc +		print_category(cat, npkgs, desc) +		category_list = category_list " " cat +	} +	print "\"" >>tmpfile + +	# Produce the list of categories (calculated in above block) +	sub(/^ /, "", category_list) +	print "PACKAGE_CATEGORIES=\"" category_list "\"" >> tmpfile +	print "_npkgs=\""npkg"\"" >>tmpfile + +	print "EOF" +} diff --git a/usr.sbin/bsdconfig/share/packages/index.subr b/usr.sbin/bsdconfig/share/packages/index.subr new file mode 100644 index 000000000000..04f28603426b --- /dev/null +++ b/usr.sbin/bsdconfig/share/packages/index.subr @@ -0,0 +1,320 @@ +if [ ! "$_PACKAGES_INDEX_SUBR" ]; then _PACKAGES_INDEX_SUBR=1 +# +# Copyright (c) 2013-2016 Devin Teske +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +#    notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +#    notice, this list of conditions and the following disclaimer in the +#    documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_dprintf "%s: loading includes..." packages/index.subr +f_include $BSDCFG_SHARE/device.subr +f_include $BSDCFG_SHARE/media/common.subr +f_include $BSDCFG_SHARE/packages/musthavepkg.subr +f_include $BSDCFG_SHARE/strings.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +f_include_lang $BSDCFG_LIBE/include/messages.subr + +############################################################ GLOBALS + +PACKAGE_INDEX= +_INDEX_INITTED= + +# +# Default path to pkg(8) repo-packagesite.sqlite database +# +SQLITE_REPO="/var/db/pkg/repo-FreeBSD.sqlite" + +# +# Default path to on-disk cache INDEX file +# +PACKAGES_INDEX_CACHEFILE="/var/run/bsdconfig/packages_INDEX.cache" + +############################################################ FUNCTIONS + +# f_index_initialize [$var_to_set] +# +# Read and initialize the global index. Returns success unless media cannot be +# initialized for any reason (e.g. user cancels media selection dialog or an +# error occurs). The index is sorted before being loaded into $var_to_set. +# +# NOTE: The index is processed with f_index_read() [below] after being loaded. +# +f_index_initialize() +{ +	local __funcname=f_index_initialize +	local __var_to_set="${1:-PACKAGE_INDEX}" + +	[ "$_INDEX_INITTED" ] && return $SUCCESS + +	# Got any media? +	f_media_verify || return $FAILURE + +	# Make sure we have a usable pkg(8) with $PKG_ABI +	f_musthavepkg_init + +	# Does it move when you kick it? +	f_device_init device_media || return $FAILURE + +	f_show_info "$msg_attempting_to_update_repository_catalogue" + +	# +	# Generate $PACKAGESITE variable for pkg(8) based on media type +	# +	local __type __data __site +	device_media get type __type +	device_media get private __data +	case "$__type" in +	$DEVICE_TYPE_DIRECTORY) +		__site="file://$__data/packages/$PKG_ABI" ;; +	$DEVICE_TYPE_HTTP) +		f_getvar $VAR_HTTP_PATH __site +		__site="$__site/$PKG_ABI/latest" ;; +	$DEVICE_TYPE_HTTP_PROXY) +		f_getvar $VAR_HTTP_PROXY_PATH __site +		__site="$__site/packages/$PKG_ABI" ;; +	$DEVICE_TYPE_CDROM) +		__site="file://$MOUNTPOINT/packages/$PKG_ABI" +		export REPOS_DIR="$MOUNTPOINT/packages/repos" ;; +	*) # UFS, DISK, CDROM, USB, DOS, NFS, etc. +		__site="file://$MOUNTPOINT/packages/$PKG_ABI" +	esac + +	f_dprintf "PACKAGESITE=[%s]" "$__site" +	if ! f_eval_catch $__funcname pkg \ +		'PACKAGESITE="%s" pkg update' "$__site" +	then +		f_show_err "$msg_unable_to_update_pkg_from_selected_media" +		f_device_shutdown device_media +		return $FAILURE +	fi + +	# +	# Try to get contents from validated on-disk cache +	# + +	# +	# Calculate digest used to determine if the on-disk persistent cache +	# INDEX (containing this digest on the first line) is valid and can be +	# used to quickly populate the environment. +	# +	local __sqlite_digest +	if ! __sqlite_digest=$( md5 < "$SQLITE_REPO" 2> /dev/null ); then +		f_show_err "$msg_no_pkg_database_found" +		f_device_shutdown device_media +		return $FAILURE +	fi + +	# +	# Check to see if the persistent cache INDEX file exists +	# +	if [ -f "$PACKAGES_INDEX_CACHEFILE" ]; then +		# +		# Attempt to populate the environment with the (soon to be) +		# validated on-disk cache. If validation fails, fall-back to +		# generating a fresh cache. +		# +		if eval $__var_to_set='$( +			(	# Get digest as the first word on first line +				read digest rest_ignored + +				# +				# If the stored digest matches the calculated- +				# one populate the environment from the on-disk +				# cache and provide success exit status. +				# +				if [ "$digest" = "#$__sqlite_digest" ]; then +					cat +					exit $SUCCESS +				else +					# Otherwise, return the current value +					eval echo \"\$__var_to_set\" +					exit $FAILURE +				fi +			) < "$PACKAGES_INDEX_CACHEFILE" 2> /dev/null +		)'; then +			if ! f_index_read "$__var_to_set"; then +				f_show_err \ +					"$msg_io_or_format_error_on_index_file" +				return $FAILURE +			fi +			_INDEX_INITTED=1 +			return $SUCCESS +		fi +		# Otherwise, fall-thru to create a fresh cache from scratch +	fi + +	# +	# If we reach this point, we need to generate the data from scratch +	# + +	# Create a new temporary file to write to +	local __tmpfile +	if f_eval_catch -dk __tmpfile $__funcname mktemp \ +		'mktemp -t "%s"' "$pgm"; then +		local _npkg +		# Write the temporary file contents +		echo "#$__sqlite_digest" > "$__tmpfile" +		f_eval_catch -k _npkg $__funcname pkg \ +			"pkg stat -r | awk '%s'" '/Packages available/ { print $3 }' +		pkg rquery -I | awk -v npkg=$_npkg \ +			-v msg="$msg_generating_index_from_pkg_database" \ +			-v tmpfile="$__tmpfile" \ +			-v valid_chars="$VALID_VARNAME_CHARS" \ +			-v default_desc="$msg_no_description_provided" \ +			-v packages="$msg_packages" \ +			-v msg_all="$msg_all" \ +			-v msg_all_desc="$msg_all_desc" \ +			-F "|" \ +			-f $BSDCFG_SHARE/packages/index.awk | \ +			$DIALOG --backtitle "$DIALOG_BACKTITLE" \ +			--gauge "$msg_generating_index_from_pkg_database" 0 0 + +		# Finally, move the temporary file into place +		case "$PACKAGES_INDEX_CACHEFILE" in +		*/*) f_eval_catch -d $__funcname mkdir \ +			'mkdir -p "%s"' "${PACKAGES_INDEX_CACHEFILE%/*}" +		esac +		f_eval_catch -d $__funcname mv 'mv -f "%s" "%s"' \ +			"$__tmpfile" "$PACKAGES_INDEX_CACHEFILE" +	else +		return $FAILURE +	fi + +	if ! f_index_read "$__var_to_set"; then +		f_show_err \ +			"$msg_io_or_format_error_on_index_file" +		return $FAILURE +	fi + +	_INDEX_INITTED=1 +	return $SUCCESS +} + +# f_index_read [$var_to_get] +# +# Process the INDEX file (contents contained in $var_to_get) and... +# +# 1. create a list ($CATEGORY_MENU_LIST) of categories with package counts +# 2. For convenience, create $_npkgs holding the total number of all packages +# 3. extract associative categories for each package into $_categories_$varpkg +# 4. extract runtime dependencies for each package into $_rundeps_$varpkg +# 5. extract a [sorted] list of categories into $PACKAGE_CATEGORIES +# 6. create $_npkgs_$varcat holding the total number of packages in category +# +# NOTE: $varpkg is the product of f_str2varname $package varpkg +# NOTE: $package is the name as it appears in the INDEX (no archive suffix) +# NOTE: We only show categories for which there are at least one package. +# NOTE: $varcat is the product of f_str2varname $category varcat +# +f_index_read() +{ +	local var_to_get="${1:-PACKAGE_INDEX}" + +	# Export variables required by awk(1) below +	export msg_no_description_provided +	export msg_all msg_all_desc +	export VALID_VARNAME_CHARS +	export msg_packages + +	. $PACKAGES_INDEX_CACHEFILE +} + +# f_index_extract_pages $var_to_get $var_basename $pagesize [$category] +# +# Extracts the package INDEX ($PACKAGE_INDEX by default if/when $var_to_get is +# NULL; but should not be missing) into a series of sequential variables +# corresponding to "pages" containing up to $pagesize packages. The package +# INDEX data must be contained in the variable $var_to_get. The extracted pages +# are stored in variables ${var_basename}_# -- where "#" is a the page number. +# If $category is set, only packages for that category are extracted. +# Otherwise, if $category is "All", missing, or NULL, all packages are +# extracted and no filtering is done. +# +f_index_extract_pages() +{ +	local var_to_get="${1:-PACKAGE_INDEX}" var_basename="$2" pagesize="$3" + +	eval "$( +		debug= f_getvar "$var_to_get" | awk -F'|' \ +			-v pagesize="$pagesize" \ +			-v var_basename="$var_basename" ' \ +		BEGIN { n = page = 0 } +		/'\''/{ gsub(/'\''/, "'\''\\'\'\''") } +		{ +			starting_new_page = (n++ == (pagesize * page)) +			if ( starting_new_page ) +				printf "%s%s", ( n > 1 ? "'\''\n" : "" ), +				       var_basename "_" ++page "='\''" +			printf "%s%s", ( starting_new_page ? "" : "\n" ), $0 +		} +		END { if ( n > 0 ) print "'\''" }' +	)" +} + +# f_index_search $var_to_get $name [$var_to_set] +# +# Search the package INDEX ($PACKAGE_INDEX by default if/when $var_to_get is +# NULL; but should not be missing) for $name, returning the first match. +# Matches are strict (not regular expressions) and must match the beginning +# portion of the package name to be considered a match. If $var_to_set is +# missing or NULL, output is sent to standard output. If a match is found, +# returns success; otherwise failure. +# +f_index_search() +{ +	local __var_to_get="${1:-PACKAGE_INDEX}" __pkg_basename="$2" +	local __var_to_set="$3" + +	f_dprintf "f_index_search: Searching package data (in %s) for %s" \ +	          "$__var_to_get" "$__pkg_basename" + +	local __pkg= +	__pkg=$( debug= f_getvar "$__var_to_get" | +			awk -F'|' -v basename="$__pkg_basename" ' +		BEGIN { n = length(basename) } +		substr($1, 0, n) == basename { print $1; exit } +	' ) +	if [ ! "$__pkg" ]; then +		f_dprintf "f_index_search: No packages matching %s found" \ +		          "$__pkg_basename" +		return $FAILURE +	fi + +	f_dprintf "f_index_search: Found package %s" "$__pkg" +	if [ "$__var_to_set" ]; then +		setvar "$__var_to_set" "$__pkg" +	else +		echo "$__pkg" +	fi +	return $SUCCESS +} + +############################################################ MAIN + +f_dprintf "%s: Successfully loaded." packages/index.subr + +fi # ! $_PACKAGES_INDEX_SUBR diff --git a/usr.sbin/bsdconfig/share/packages/musthavepkg.subr b/usr.sbin/bsdconfig/share/packages/musthavepkg.subr new file mode 100644 index 000000000000..0a1026afe86c --- /dev/null +++ b/usr.sbin/bsdconfig/share/packages/musthavepkg.subr @@ -0,0 +1,80 @@ +if [ ! "$_PACKAGES_MUSTHAVEPKG_SUBR" ]; then _PACKAGES_MUSTHAVEPKG_SUBR=1 +# +# Copyright (c) 2014-2016 Devin Teske +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +#    notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +#    notice, this list of conditions and the following disclaimer in the +#    documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_dprintf "%s: loading includes..." packages/musthavepkg.subr +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/mustberoot.subr + +############################################################ FUNCTIONS + +# f_musthavepkg_init +# +# Validate pkg(8) is installed and set $PKG_ABI global if not already set. +# Returns success unless pkg(8) is not installed and user refuses to install +# it (upon prompt when running interactively). +# +f_musthavepkg_init() +{ +	local funcname=f_musthavepkg_init + +	if f_quietly pkg -N; then +		if [ -n "$PKG_ABI" ]; then +			export PKG_ABI +			return $SUCCESS +		fi +		if f_eval_catch -k PKG_ABI $funcname pkg "pkg config abi"; then +			export PKG_ABI +			return $SUCCESS +		fi +	fi + +	# pkg(8) not yet bootstrapped; ask for permission unless nonInteractive +	f_dialog_yesno "$msg_pkg_not_yet_installed_install_now" || +		f_die 1 "$msg_must_have_pkg_to_execute" "$pgm" + +	f_mustberoot_init # Have to be root to install pkg(8) + +	# Bootstrap pkg(8) +	f_dialog_info "$msg_bootstrapping_pkg" +	f_eval_catch $funcname pkg \ +		"pkg bootstrap -y" || +		f_die 1 "$msg_must_have_pkg_to_execute" "$pgm" +	f_eval_catch -k PKG_ABI $funcname pkg "pkg config abi" +	f_dprintf "PKG_ABI=[%s]" "$PKG_ABI" +	export PKG_ABI +	return $SUCCESS +} + +############################################################ MAIN + +f_dprintf "%s: Successfully loaded." packages/musthavepkg.subr + +fi # ! $_PACKAGES_MUSTHAVEPKG_SUBR diff --git a/usr.sbin/bsdconfig/share/packages/packages.subr b/usr.sbin/bsdconfig/share/packages/packages.subr new file mode 100644 index 000000000000..38059d4748a6 --- /dev/null +++ b/usr.sbin/bsdconfig/share/packages/packages.subr @@ -0,0 +1,1201 @@ +if [ ! "$_PACKAGES_PACKAGES_SUBR" ]; then _PACKAGES_PACKAGES_SUBR=1 +# +# Copyright (c) 2013-2016 Devin Teske +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +#    notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +#    notice, this list of conditions and the following disclaimer in the +#    documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_dprintf "%s: loading includes..." "$0" +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/device.subr +f_include $BSDCFG_SHARE/media/common.subr +f_include $BSDCFG_SHARE/packages/categories.subr +f_include $BSDCFG_SHARE/packages/index.subr +f_include $BSDCFG_SHARE/packages/musthavepkg.subr +f_include $BSDCFG_SHARE/strings.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +f_include_lang $BSDCFG_LIBE/include/messages.subr + +############################################################ CONFIGURATION + +# +# How many packages to display (maximum) per dialog menubox. +# +: ${PACKAGE_MENU_PAGESIZE:=2000} + +############################################################ GLOBALS + +# +# Package extensions to try +# +PACKAGE_EXTENSIONS=".txz .tbz .tbz2 .tgz" + +# +# Variables used to track runtime states +# +PACKAGES_DETECTED=	# Boolean (NULL/non-NULL); detected installed packages? +PACKAGE_CATEGORIES=	# List of package categories parsed from INDEX +SELECTED_PACKAGES=	# Packages selected by user in [X]dialog(1) interface + +# +# Options +# +[ "${SHOW_DESC+set}" ] || SHOW_DESC=1 + +############################################################ FUNCTIONS + +# eval f_package_accent_category_menu $var_to_set $CATEGORY_MENU_LIST +# +# Accent the CATEGORY_MENU_LIST produced by f_index_read() (see +# packages/index.subr). Accented information includes adding an asterisk to the +# category name if its index has been cached, adding the number of installed +# packages for each category, and adding the number _selected_ packages for +# each category. +# +# NOTE: The reason `eval' is recommended/shown for the syntax above is because +# the $CATEGORY_MENU_LIST generated by f_index_read() is meant to be expanded +# prior to execution (it contains a series of pre-quoted strings which act as +# the interpolated command arguments). +# +f_package_accent_category_menu() +{ +	local var_to_set="$1" category cat desc help varcat menu_buf n +	shift 1 # var_to_set +	while [ $# -gt 0 ]; do +		category="${1%\*}" desc="${2%%; *}" help="$3" +		shift 3 # cat/desc/help + +		cat="${category# }" # Trim lead space inserted by sort-method +		f_str2varname "$cat" varcat + +		# Add number of installed packages for this category (if any) +		n=0 +		case "$cat" in +		"$msg_all") debug= f_getvar "_All_ninstalled" n ;; +		         *) debug= f_getvar "_${varcat}_ninstalled" n ;; +		esac && +		[ $n -ge 1 ] && desc="$desc; $n $msg_installed_lc" + +		# Add number of selected packages for this category (if any) +		n=0 +		case "$cat" in +		"$msg_all") debug= f_getvar "_All_nselected" n ;; +		         *) debug= f_getvar "_${varcat}_nselected" n ;; +		esac && +		[ $n -ge 1 ] && desc="$desc; $n $msg_selected" + +		# Re-Add asterisk to the category if its index has been cached +		f_isset _index_page_${varcat}_1 && category="$category*" + +		# Update buffer with modified elements +		menu_buf="$menu_buf +		'$category' '$desc' '$help'" # End-Quote +	done +	setvar "$var_to_set" "$menu_buf" # return our buffer +} + +# f_package_select $package ... +# +# Add $package to the list of tracked/selected packages. If $package is already +# being tracked (already apears in $SELECTED_PACKAGES), this function amounts +# to having no effect. +# +f_package_select() +{ +	local package pkgsel +	while [ $# -gt 0 ]; do +		package="$1" +		shift 1 # package +		for pkgsel in $SELECTED_PACKAGES; do +			[ "$package" = "$pkgsel" ] && return $SUCCESS +		done +		SELECTED_PACKAGES="$SELECTED_PACKAGES $package" +		f_dprintf "Added %s to selection list" "$package" +	done +	SELECTED_PACKAGES="${SELECTED_PACKAGES# }" # Trim leading space +} + +# f_package_deselect $package ... +# +# Remove $package from the list of tracked/selected packages. If $package is +# not being tracked (doesn't appear in $SELECTED_PACKAGES), this function +# amounts to having no effet. +# +f_package_deselect() +{ +	local package pkgsel +	while [ $# -gt 1 ]; do +		local new_list="" +		package="$1" +		shift 1 # package +		for pkgsel in $SELECTED_PACKAGES; do +			[ "$pkgsel" = "$package" ] && continue +			new_list="$new_list${new_list:+ }$pkgsel" +		done +		SELECTED_PACKAGES="$new_list" +		f_dprintf "Removed %s from selection list" "$package" +	done +} + +# f_package_detect_installed +# +# Detect installed packages. Currently this uses pkg-query(8) for querying +# entries and marks each entry as an installed/selected package. +# +f_package_detect_installed() +{ +	local package varpkg +	for package in $( pkg query "%n-%v" ); do +		f_str2varname $package varpkg +		export _mark_$varpkg=X # exported for awk(1) ENVIRON[] +		f_package_select $package +	done +} + +# f_package_calculate_totals +# +# Calculate number of installed/selected packages for each category listed in +# $PACKAGE_CATEGORIES (the number of installed packages for $category is stored +# as $_${varcat}_ninstalled -- where $varcat is the product of `f_str2varname +# $category varcat' -- and number selected packages as $_${varcat}_nselected). +# Also calculates the total number of installed/selected packages stored as +# $_All_ninstalled and $_All_nselected. +# +# Calculations are performed by checking "marks". A "mark" is stored as +# $_mark_$varpkg -- where $varpkg is the product of `f_str2varname $package +# varpkg'. A mark can be "X" for an installed package, `I' for a package that +# is marked for installation, "R" for a package that is marked for re-install, +# and "U" for a package that is marked for uninstallation. If a package mark is +# NULL or a single space (e.g., " "), the package is considered to be NOT +# selected (and therefore does not increment the counts calculated herein). +# +f_package_calculate_totals() +{ +	local pkg varpkg mark cat varcat pkgcat n tselected=0 tinstalled=0 +	for cat in $PACKAGE_CATEGORIES; do +		f_str2varname $cat varcat +		setvar _${varcat}_ninstalled=0 +		setvar _${varcat}_nselected=0 +	done +	for pkg in $SELECTED_PACKAGES; do +		f_str2varname $pkg varpkg +		mark= +		f_getvar _mark_$varpkg mark +		case "$mark" in +		""|" ") : ;; +		X) tinstalled=$(( $tinstalled + 1 )) ;; +		*) tselected=$(( $tselected + 1 )) +		esac +		f_getvar _categories_$varpkg pkgcat +		for cat in $pkgcat; do +			f_str2varname $cat varcat +			case "$mark" in +			""|" ") : ;; +			X) debug= f_getvar _${varcat}_ninstalled n +			   setvar _${varcat}_ninstalled $(( $n + 1 )) ;; +			*) debug= f_getvar _${varcat}_nselected n +			   setvar _${varcat}_nselected $(( $n + 1 )) +			esac +		done +	done +	_All_nselected=$tselected +	_All_ninstalled=$tinstalled +} + +# f_package_calculate_rundeps +# +# Update package dependencies by first unmarking all dependencies and then +# re-marking all dependencies of packages marked for either install ("I") or +# re-install ("R"). +# +f_package_calculate_rundeps() +{ +	local pkg varpkg mark rundeps dep vardep + +	# +	# First unmark all the existing run-dependencies +	# +	f_dprintf "Unselecting package run-dependencies..." +	for pkg in $SELECTED_PACKAGES; do +		f_str2varname $pkg varpkg +		mark= +		debug= f_getvar _mark_$varpkg mark +		# Only unmark if it's marked as a Dependency +		if [ "$mark" = "D" ]; then +			f_dprintf "%s unselected" $pkg +			unset _mark_$varpkg +			f_package_deselect $pkg +		fi +	done + +	# +	# Processes selected packages, adding dependencies +	# +	f_dprintf "Re-selecting package run-dependencies..." +	for pkg in $SELECTED_PACKAGES; do +		f_str2varname $pkg varpkg +		mark= +		debug= f_getvar _mark_$varpkg mark +		# Skip pkg unless marked for [Re-]Install +		[ "$mark" = "I" -o "$mark" = "R" ] || continue +		f_getvar _rundeps_$varpkg rundeps +		for dep in $rundeps; do +			f_str2varname $dep vardep +			mark= +			debug= f_getvar _mark_$vardep mark +			# Skip dep if already marked +			[ "${mark:- }" = " " ] || continue +			export _mark_$vardep="D" +			f_package_select $dep +		done +	done + +	f_dprintf "Finished recalculating dependencies." +} + +# f_package_menu_categories $var_to_set $defaultitem +# +# Dislay the menu of package categories, complete with package counts for each +# category, accents, and other miscellany. If $defaultitem is non-NULL and +# matches one of the existing menu-items, it will be pre-highlighted in the +# menu dialog (HINT: Use f_dialog_menutag_fetch() to populate a local variable +# that is passed as $defaultitem to highlight the user's last selection). +# +f_package_menu_categories() +{ +	local var_to_get="$1" defaultitem="$2" +	local prompt="$msg_please_select_a_category_to_display" +	local menu_list=" +		'> $msg_review' '$msg_review_desc' '$msg_review_help' +	" # End-Quote +	local hline= + +	f_package_calculate_rundeps +		# updates package mark variables and SELECTED_PACKAGES +	f_package_calculate_totals +		# creates _{varcat}_ninstalled and _{varcat}_nselected + +	local category_list +	debug= f_getvar "$var_to_get" category_list || return $DIALOG_CANCEL + +	# Accent the category menu list with ninstalled/nselected +	eval f_package_accent_category_menu category_list $category_list + +	# Add list of categories to menu list +	menu_list="$menu_list $category_list" + +	local height width rows +	eval f_dialog_menu_with_help_size height width rows \ +	                                  \"\$DIALOG_TITLE\"     \ +	                                  \"\$DIALOG_BACKTITLE\" \ +	                                  \"\$prompt\"           \ +	                                  \"\$hline\"            \ +	                                  $menu_list +	local menu_choice +	menu_choice=$( eval $DIALOG \ +		--title \"\$DIALOG_TITLE\"         \ +		--backtitle \"\$DIALOG_BACKTITLE\" \ +		--hline \"\$hline\"                \ +		--item-help                        \ +		--default-item \"\$defaultitem\"   \ +		--ok-label \"$msg_select\"         \ +		--cancel-label \"$msg_cancel\"     \ +		--menu \"\$prompt\"                \ +		$height $width $rows               \ +		$menu_list                         \ +		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD +	) +	local retval=$? +	f_dialog_menutag_store -s "$menu_choice" +	return $retval +} + +# f_package_index_get_page $category $page [$var_to_set [$var_to_get]] +# +# Obtain a [potentially cached] page of the INDEX file for a given $category. +# If $page is 1 and the cache has not yet been generated, the cache-generating +# function f_index_extract_pages() (above) is called to generate all pages +# (not just the requested page) in cache before returning the requested page. +# If $page is not 1 and there is no cached page, failure status is returned. +# +f_package_index_get_page() +{ +	local category="$1" page="$2" var_to_set="$3" var_to_get="$4" varcat +	f_str2varname "$category" varcat +	if ! debug= f_getvar "_index_page_${varcat}_$page" $var_to_set && +	   [ "$page" = "1" ] +	then +		f_show_info "$msg_building_package_menus" +		local pagesize="$PACKAGE_MENU_PAGESIZE" +		f_index_extract_pages "${var_to_get:-PACKAGE_INDEX}" \ +			_index_page_${varcat} "$pagesize" "$category" +		debug= f_getvar _index_page_${varcat}_$page $var_to_set + +		# Update category default-item because now we're cached +		[ $page -eq 1 ] && +		    category_defaultitem="${category_defaultitem%\*}*" +	else +		return $FAILURE +	fi +} + +# f_package_menu_select $category [$page [$defaultitem]] +# +# Display list of packages for $category, optionally $page N and with a default +# item selected. If $page is omitted, the first page is displayed (but this +# only matters if there are multiple pages; which is determined by the global +# maximum $PACKAGE_MENU_PAGESIZE). +# +# On success, if the user doesn't press ESC or choose Cancel, use +# f_dialog_menuitem_fetch() to populate a local variable with the item (not +# tag) corresponding to the user's selection. The tag portion of the user's +# selection is available through f_dialog_menutag_fetch(). +# +f_package_menu_select() +{ +	local __funcname=f_package_menu_deselect +	local category="$1" page="${2:-1}" +	local prompt= # Calculated below +	local menu_list # Calculated below +	local defaultitem="$3" +	local hline="$hline_arrows_tab_punc_enter" + +	f_isinteger "$page" || return $DIALOG_CANCEL + +	local varcat +	f_str2varname "$category" varcat + +	# Get number of packages for this category +	local npkgs=0 +	case "$category" in +	"$msg_all"|"") npkgs="${_npkgs:-0}" ;; +	*) f_getvar _npkgs_$varcat npkgs +	esac + +	# Calculate number of pages +	local npages=$(( ${npkgs:=0} / $PACKAGE_MENU_PAGESIZE )) + +	# Add a page to the pagecount if not evenly divisible +	[ $(( $npages * $PACKAGE_MENU_PAGESIZE )) -lt $npkgs ] && +		npages=$(( $npages + 1 )) + +	# Print some debugging information +	f_dprintf "f_package_menu_select: category=[%s] npkgs=%u npages=%u" \ +	          "$category" "$npkgs" "$npages" + +	local add_prev="" add_next="" +	local previous_page="$msg_previous_page" next_page="$msg_next_page" +	if [ $page -gt 1 ]; then +		add_prev=1 +		# Accent the `Previous Page' item with an asterisk +		# if the page-before-previous is loaded/cached +		f_isset _index_page_${varcat}_$(( $page - 1 )) && +			previous_page="$previous_page*" +	fi +	if [ $page -lt $npages ]; then +		add_next=1 +		# Accent the `Next Page' item with an asterisk +		# if the page-after-next is loaded/cached +		f_isset _index_page_${varcat}_$(( $page + 1 )) && +			next_page="$next_page*" +	fi + +	local index_page __index +	if [ "$category" = "$msg_all" ]; then +		f_eval_catch -k __index $__funcname pkg \ +			"pkg rquery -a '%s'"  "%n|%o||%c" +	else +		f_eval_catch -k __index $__funcname pkg \ +			"pkg rquery -g '%s' '%s/\*'"  "%n|%o||%c" "$category" +	fi +	f_package_index_get_page "$category" $page index_page __index + +	menu_list=" +		${add_prev:+'> $previous_page' '' ${SHOW_DESC:+''}} +		${add_next:+'> $next_page' '' ${SHOW_DESC:+''}} +	$( +		export SHOW_DESC +		export VALID_VARNAME_CHARS +		echo "$index_page" | awk -F'|' -v view="port" ' +		BEGIN { +			valid_chars = ENVIRON["VALID_VARNAME_CHARS"] +			prefix = "" +		} +		{ +			cur_prefix = tolower(substr($1, 1, 1)) +			printf "'\''" +			if ( prefix != cur_prefix ) +				prefix = cur_prefix +			else +				printf " " +			package = $1 +			if ( view == "port" ) +				desc = $2 +			varpkg = package +			gsub("[^" valid_chars "]", "_", varpkg) +			mark = ENVIRON["_mark_" varpkg] +			if ( ! mark ) mark = " " +			printf "%s'\'' '\''[%c] %s'\''", +			       package, mark, desc +			if ( ENVIRON["SHOW_DESC"] ) { +				help = $4 +				gsub(/'\''/, "'\''\\'\'\''", help) +				printf " '\''%s'\''", help +			} +			printf "\n" +		}' +	) +		${add_prev:+'> $previous_page' '' ${SHOW_DESC:+''}} +		${add_next:+'> $next_page' '' ${SHOW_DESC:+''}} +	" # End-Quote + +	# Accept/Translate i18n "All" but other category names must +	# match tree definitions from INDEX, ports, FTP, etc. +	case "$category" in +	"$msg_all"|"") f_category_desc_get "All" prompt ;; +	            *) f_category_desc_get "$category" prompt ;; +	esac +	f_sprintf prompt "%s $msg_page_of_npages" "$prompt" "$page" "$npages" + +	local mheight mwidth mrows +	eval f_dialog_menu${SHOW_DESC:+_with_help}_size mheight mwidth mrows \ +	        	\"\$DIALOG_TITLE\" \"\$DIALOG_BACKTITLE\" \ +	        	\"\$prompt\" \"\$hline\" $menu_list +	local iheight iwidth +	f_dialog_infobox_size iheight iwidth \ +	         	"$DIALOG_TITLE" "$DIALOG_BACKTITLE" \ +	         	"$msg_processing_selection" + +	local menu_choice +	menu_choice=$( eval $DIALOG \ +		--title \"\$DIALOG_TITLE\"         \ +		--backtitle \"\$DIALOG_BACKTITLE\" \ +		--hline \"\$hline\"                \ +		--keep-tite                        \ +		--ok-label \"$msg_select\"         \ +		--cancel-label \"$msg_back\"       \ +		${SHOW_DESC:+--item-help}          \ +		--default-item \"\$defaultitem\"   \ +		--menu \"\$prompt\"                \ +		$mheight $mwidth $mrows            \ +		$menu_list                         \ +		--and-widget                       \ +		${USE_XDIALOG:+--no-buttons}       \ +		--infobox \"\$msg_processing_selection\" \ +		$iheight $iwidth                   \ +		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD +	) +	local retval=$? +	f_dialog_data_sanitize menu_choice +	f_dialog_menutag_store "$menu_choice" + +	if [ $retval -eq $DIALOG_OK ]; then +		local item +		item=$( eval f_dialog_menutag2item${SHOW_DESC:+_with_help} \ +		             	\"\$menu_choice\" $menu_list ) +		f_dialog_menuitem_store "$item" +	fi + +	return $retval +} + +# f_package_menu_deselect $package +# +# Display a menu, asking the user what they would like to do with $package +# with regard to "deselecting" an already installed package. Choices include +# uninstall, re-install, or cancel (leave $package marked as installed). +# Returns success if the user does not press ESC or choose Cnacel. Use the +# f_dialog_menutag_fetch() function upon success to retrieve the user's choice. +# +f_package_menu_deselect() +{ +	local package="$1" +	local prompt # Calculated below +	local menu_list=" +		'X $msg_installed' '$msg_installed_desc' +		'R $msg_reinstall' '$msg_reinstall_desc' +		'U $msg_uninstall' '$msg_uninstall_desc' +	" # End-Quote +	local hline="$hline_alnum_arrows_punc_tab_enter" + +	f_sprintf prompt "$msg_what_would_you_like_to_do_with" "$package" + +	local height width rows +	eval f_dialog_menu_size height width rows \ +	                        \"\$DIALOG_TITLE\"     \ +	                        \"\$DIALOG_BACKTITLE\" \ +	                        \"\$prompt\"           \ +	                        \"\$hline\"            \ +	                        $menu_list +	local menu_choice +	menu_choice=$( eval $DIALOG \ +		--title \"\$DIALOG_TITLE\"         \ +		--backtitle \"\$DIALOG_BACKTITLE\" \ +		--hline \"\$hline\"                \ +		--ok-label \"$msg_select\"         \ +		--cancel-label \"$msg_cancel\"     \ +		--menu \"\$prompt\"                \ +		$height $width $rows               \ +		$menu_list                         \ +		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD +	) +	local retval=$? +	f_dialog_menutag_store -s "$menu_choice" +	return $retval +} + +# f_package_review +# +# Display a review screen, showing selected packages and what they are marked +# for, before proceeding (if the user does not press ESC or choose Cancel) to +# operate on each selection. Returns error if no packages have been selected, +# or the user has pressed ESC, or if they have chosen Cancel. +# +f_package_review() +{ +	local funcname=f_package_review +	local prompt # Calculated below +	local menu_list # Calculated below +	local hline="$hline_alnum_arrows_punc_tab_enter" + +	f_dprintf "$funcname: SELECTED_PACKAGES=[%s]" "$SELECTED_PACKAGES" + +	f_sprintf prompt "$msg_reviewing_selected_packages" "$_All_nselected" + +	local package varpkg mark +	for package in $SELECTED_PACKAGES; do +		mark= +		f_str2varname "$package" varpkg +		f_getvar _mark_$varpkg mark +		[ "$mark" -a ! "${mark#[IRUD]}" ] || continue +		menu_list="$menu_list +			'$mark' '$package' +		" # End-Quote +	done +	if [ ! "$menu_list" ]; then +		f_show_msg "$msg_no_packages_were_selected_for_extraction" +		return $DIALOG_CANCEL # Might have selected this by accident +	fi +	menu_list=$( echo "$menu_list" | sort ) + +	local height width rows +	eval f_dialog_menu_size height width rows \ +	                        \"\$DIALOG_TITLE\"     \ +	                        \"\$DIALOG_BACKTITLE\" \ +	                        \"\$prompt\"           \ +	                        \"\$hline\"            \ +	                        $menu_list + +	# Show the review menu (ignore menu choice) +	eval $DIALOG \ +		--title \"\$DIALOG_TITLE\"         \ +		--backtitle \"\$DIALOG_BACKTITLE\" \ +		--hline \"\$hline\"                \ +		--ok-label \"\$msg_proceed\"       \ +		--cancel-label \"\$msg_cancel\"    \ +		--menu \"\$prompt\"                \ +		$height $width $rows               \ +		$menu_list                         \ +		2> /dev/null || return $? +			# Return if the user pressed ESC or chose Cancel/No + +	# +	# Process each of the selected packages: +	# + First, process packages marked for Install. +	# + Second, process packages marked for Re-install. +	# + Finally, process packages marked for Uninstall. +	# +	for package in $SELECTED_PACKAGES; do +		mark= +		f_str2varname "$package" varpkg +		debug= f_getvar _mark_$varpkg mark +		[ "$mark" = "I" ] || continue +		f_dprintf "$funcname: Installing %s package" "$package" +		f_package_add "$package" +	done +	for package in $SELECTED_PACKAGES; do +		mark= +		f_str2varname "$package" varpkg +		debug= f_getvar _mark_$varpkg mark +		[ "$mark" = "R" ] || continue +		f_dprintf "$funcname: Reinstalling %s package" "$package" +		f_package_reinstall "$package" +	done +	for package in $SELECTED_PACKAGES; do +		mark= +		f_str2varname "$package" varpkg +		debug= f_getvar _mark_$varpkg mark +		[ "$mark" = "U" ] || continue +		f_dprintf "$funcname: Uninstalling %s package" "$package" +		f_package_delete "$package" || continue +		f_package_deselect "$package" +	done + +	return $DIALOG_OK +} + +# f_package_config +# +# Allow the user to configure packages and install them. Initially, a list of +# package categories is loaded/displayed. When the user selects a category, +# the menus for that category are built (unlike sysinstall which built all +# category menus up-front -- which also took forever, despite the fact that +# few people visit more than a couple of categories each time). +# +f_package_config() +{ +	# Did we get an INDEX? +	f_index_initialize || return $FAILURE +		# Creates following variables (indirectly via f_index_read()) +		#   CATEGORY_MENU_LIST _categories_{varpkg} _rundeps_{varpkg} +		#   PACKAGE_CATEGORIES _npkgs + +	f_show_info "$msg_building_package_main_menu" + +	# Detect installed packages (updates marks/SELECTED_PACKAGES) +	f_package_detect_installed +	export PACKAGES_DETECTED=1 # exported for awk(1) ENVIRON[] + +	local retval category varcat defaultitem category_defaultitem="" +	while :; do +		# Display the list of package categories +		f_package_menu_categories \ +			CATEGORY_MENU_LIST "$category_defaultitem" +		retval=$? +		f_dialog_menutag_fetch category +		f_dprintf "retval=%u mtag=[%s]" $retval "$category" +		category_defaultitem="$category" + +		[ $retval -eq $DIALOG_OK ] || break + +		# Maybe the user chose an action (like `Review') +		case "$category" in +		"> $msg_review") +			f_package_review && break +			continue ;; +		"> "*) +			continue +		esac + +		# Anything else is a package category + +		category=${category# } # Trim leading space if present +		category=${category%\*} # Trim trailing asterisk if present + +		f_str2varname "$category" varcat + +		local page package varpkg mark menu_choice +		while :; do +			# Display the list of packages for selected category +			page=1 defaultitem="" +			f_getvar _defaultitem_$varcat defaultitem +			f_getvar _defaultpage_$varcat page +			f_package_menu_select \ +				"$category" "${page:=1}" "$defaultitem" +			retval=$? +			f_dialog_menutag_fetch menu_choice +			f_dprintf "retval=%u mtag=[%s]" $retval "$menu_choice" + +			# NOTE: When --and-widget is used only ESC will cause +			# dialog(1) to return without going to the next widget. +			# This is alright in our case as we can still detect +			# the Cancel button because stdout will be NULL. +			# Alternatively, Xdialog(1) will terminate with 1 +			# if/when Cancel is chosen on any widget. +			if [ $retval -eq $DIALOG_ESC -o ! "$menu_choice" ] +			then +				break +			elif [ $retval -eq $DIALOG_CANCEL ]; then +				# Using X11, Xdialog(1) returned 1 for Cancel +				f_show_msg "%s" "$menu_choice" +				break +			elif [ $retval -ne $DIALOG_OK ]; then +				# X11-related error occurred using Xdialog(1) +				f_show_msg "%s" "$menu_choice" +				break +			fi + +			defaultitem="$menu_choice" + +			# NOTE: f_package_menu_select() does not show the +			# `Previous Page' or `Next Page' items unless needed +			case "$menu_choice" in +			"> $msg_previous_page"|"> $msg_previous_page*") +				page=$(( $page - 1 )) +				setvar _defaultpage_$varcat $page +				# Update default-item to match accent that will +				# be applied by f_package_menu_select(); if the +				# page-before-prev is cached, add an asterisk. +				if f_isset \ +					_index_page_${varcat}_$(( $page - 1 )) +				then +					defaultitem="${defaultitem%\*}*" +				else +					defaultitem="${defaultitem%\*}" +				fi +				setvar _defaultitem_$varcat "$defaultitem" +				continue ;; +			"> $msg_next_page"|"> $msg_next_page*") +				page=$(( $page + 1 )) +				setvar _defaultpage_$varcat $page +				# Update default-item to match accent that will +				# be applied by f_package_menu_select(); if the +				# page-after-next is cached, add an asterisk. +				if f_isset \ +					_index_page_${varcat}_$(( $page + 1 )) +				then +					defaultitem="${defaultitem%\*}*" +				else +					defaultitem="${defaultitem%\*}" +				fi +				setvar _defaultitem_$varcat "$defaultitem" +				continue ;; +			"> "*) # Unknown navigation/action item +				setvar _defaultpage_$varcat $page +				continue ;; # Do not treat as a package +			*) +				setvar _defaultitem_$varcat "$defaultitem" +			esac + +			# Treat any other selection as a package +			package="${menu_choice# }" # Trim leading space +			f_str2varname $package varpkg +			f_dialog_menuitem_fetch mark +			mark="${mark#?}" +			mark="${mark%%\] *}" +			case "$mark" in +			"I") +				mark=" " +				f_package_deselect $package +				;; +			" "|"D") +				mark="I" +				f_package_select $package +				;; +			"X"|"R"|"U") +				f_package_menu_deselect $package || continue +				f_dialog_menutag_fetch menu_choice +				case "$menu_choice" in +				"X $msg_installed") +					f_package_deselect "$package" +					mark="X" +					;; +				"R $msg_reinstall") +					f_package_select "$package" +					mark="R" +					;; +				"U $msg_uninstall") +					f_package_select "$package" +					mark="U" +					;; +				esac +				;; +			esac +			export _mark_$varpkg="$mark" +				# NOTE: exported for awk(1) ENVIRON[] +		done +	done +} + +# f_package_add $package_name [$depended] +# +# Like f_package_extract(), but assumes current media device and chases deps. +# Note that $package_name should not contain the archive suffix (e.g., `.tbz'). +# If $depended is present and non-NULL, the package is treated as a dependency +# (in this function, dependencies are not handled any differently, but the +# f_package_extract() function is passed this value and it displays a different +# message when installing a dependency versus non-dependency). +# +f_package_add() +{ +	local name="$1" depended="$2" status=$SUCCESS retval + +	local alert=f_show_msg no_confirm= +	f_getvar $VAR_NO_CONFIRM no_confirm +	[ "$no_confirm" ] && alert=f_show_info + +	if ! { [ "$name" ] || { f_getvar $VAR_PACKAGE name && [ "$name" ]; }; } +	then +		f_dprintf "packageAdd: %s" \ +		          "$msg_no_package_name_passed_in_package_variable" +		return $FAILURE +	fi + +	{ # Verify and initialize device media if-defined +		f_media_verify && +		f_device_init device_media && +		f_index_initialize +	} || return $FAILURE + +	# Now we have (indirectly via f_index_read()): +	#   CATEGORY_MENU_LIST _categories_{varpkg} _rundeps_{varpkg} +	#   PACKAGE_CATEGORIES _npkgs + +	local varpkg +	f_str2varname "$name" varpkg + +	# Just as-in the user-interface (opposed to scripted-use), only allow +	# packages with at least one category to be recognized. +	# +	local pkgcat= +	if ! f_getvar _categories_$varpkg pkgcat || [ ! "$pkgcat" ]; then +		# $pkg may be a partial name, search the index (this is slow) +		f_index_search PACKAGE_INDEX $name name +		if [ ! "$name" ]; then +			f_show_msg \ +			    "$msg_sorry_package_was_not_found_in_the_index" \ +			    "$name" +			return $FAILURE +		fi +		f_str2varname "$name" varpkg +	fi + +	# If invoked through the scripted interface, we likely have not yet +	# detected the installed packages -- something we should do only once. +	# +	if [ ! "$PACKAGES_DETECTED" ]; then +		f_dprintf "f_package_add: Detecting installed packages" +		f_package_detect_installed +		export PACKAGES_DETECTED=1 # exported for awk(1) ENVIRON[] +	fi +	# Now we have: _mark_{varpkg}=X for all installed packages + +	# +	# Since we're maintaining data structures for installed packages, +	# short-circuit the package dependency checks if the package is already +	# installed. This prevents wasted cycles, minor delays between package +	# extractions, and worst-case an infinite loop with a certain faulty +	# INDEX file. +	# +	local mark= +	f_getvar _mark_$varpkg mark && [ "$mark" = "X" ] && return $SUCCESS + +	local dep vardep rundeps= +	f_getvar _rundeps_$varpkg rundeps +	for dep in $rundeps; do +		f_str2varname "$dep" vardep + +		# Skip dependency if already installed +		mark= +		f_getvar _mark_$vardep mark && [ "$mark" = "X" ] && continue + +		# Just as-in the user-interface (opposed to scripted-use), only +		# allow packages with at least one category to be recognized. +		# +		local depcat= +		if ! f_getvar _categories_$vardep depcat || [ ! "$depcat" ] +		then +			$alert "$msg_required_package_not_found" "$dep" +			[ "$no_confirm" ] && sleep 2 +		fi + +		f_package_add "$dep" +		retval=$? +		if [ $retval -ne $SUCCESS ]; then +			status=$(( $status | $retval )) + +			# XXX package could be on a future disc volume +			# XXX (not supporting multiple disc volumes yet) + +			$alert "$msg_loading_of_dependent_package_failed" \ +			       "$dep" +			[ "$no_confirm" ] && sleep 2 +		fi +	done +	[ $status -eq $SUCCESS ] || return $status + +	# +	# Done with the deps? Try to load the real m'coy. +	# + +	f_package_extract device_media "$name" "$depended" +	retval=$? +	if [ $retval -ne $SUCCESS ]; then +		status=$(( $status | $retval )) +	else +		setvar _mark_$varpkg X +	fi + +	return $status +} + +# f_package_extract $device $name [$depended] +# +# Extract a package based on a namespec and media device. If $depended is +# present and non-NULL, the notification displayed while installing the package +# has "as a dependency" appended. +# +f_package_extract() +{ +	local funcname=f_package_extract +	local device="$1" name="$2" depended="$3" +	local devname= + +	f_musthavepkg_init # Make sure we have a usable pkg(8) with $PKG_ABI + +	$device get name devname +	f_dprintf "$funcname: device=[%s] name=[%s] depended=[%s]" \ +	          "$devname" "$name" "$depended" + +	# Check to make sure it's not already there +	local varpkg mark= +	f_str2varname "$name" varpkg +	f_getvar _mark_$varpkg mark +	[ "$mark" = "X" ] && return $SUCCESS + +	if ! f_device_init $device; then +		f_show_msg \ +		    "$msg_unable_to_initialize_media_type_for_package_extract" +		return $FAILURE +	fi + +	# If necessary, initialize the ldconfig hints +	[ -f "/var/run/ld-elf.so.hints" ] || +		f_quietly ldconfig /usr/lib /usr/lib/compat /usr/local/lib + +	# Make a couple paranoid locations for temp +	# files to live if user specified none +	local tmpdir +	f_getvar $VAR_PKG_TMPDIR:-/var/tmp tmpdir +	f_quietly mkdir -p -m 1777 "$tmpdir" + +	local path device_type +	$device get type device_type +	case "$name" in +	*/*) path="$name" ;; +	*) +		if [ "$device_type" = "$DEVICE_TYPE_HTTP" ]; then +			path="$PKG_ABI/latest/All/$name" +		else +			path="packages/$PKG_ABI/All/$name" +		fi +	esac + +	# We have a path, call the device strategy routine to check the file +	local pkg_ext found= +	for pkg_ext in "" $PACKAGE_EXTENSIONS; do +		if f_device_get $device "$path$pkg_ext" $PROBE_EXIST; then +			path="$path$pkg_ext" +			found=1 +			break +		elif [ "$device_type" = "$DEVICE_TYPE_HTTP" ] && +		     f_device_get $device \ +			"packages/$PKG_ABI/All/$name$pkg_ext" $PROBE_EXIST +		then +			# Mirroring physical media over HTTP +			path="packages/$PKG_ABI/All/$name$pkg_ext" +			found=1 +			break +		fi +	done +	[ "$found" ] && f_dprintf "$funcname: found path=[%s] dev=[%s]" \ +	                          "$path" "$devname" + +	local alert=f_show_msg no_confirm= +	f_getvar $VAR_NO_CONFIRM no_confirm +	[ "$no_confirm" ] && alert=f_show_info + +	if [ ! "$found" ]; then +		f_dprintf "$funcname: No such %s file on %s device" \ +	                  "$path" "$devname" +		$alert "$msg_unable_to_fetch_package_from_selected_media" \ +		       "$name" +		[ "$no_confirm" ] && sleep 2 +		return $FAILURE +	fi + +	if [ "$depended" ]; then +		f_show_info "$msg_adding_package_as_a_dependency_from_media" \ +		            "$name" "$devname" +	else +		f_show_info "$msg_adding_package_from_media" "$name" "$devname" +	fi + +	# Request the package be added via pkg-install(8) +	if f_debugging; then +		f_eval_catch $funcname pkg \ +			'pkg -d install -${depended:+A}y "%s"' "$name" +	else +		f_eval_catch $funcname pkg \ +			'pkg install -${depended:+A}y "%s"' "$name" +	fi +	if [ $? -ne $SUCCESS ]; then +		$alert "$msg_pkg_install_apparently_did_not_like_the_package" \ +		       "$name" +		[ "$no_confirm" ] && sleep 2 +	else +		f_show_info "$msg_package_was_added_successfully" "$name" +		sleep 1 +	fi + +	return $SUCCESS +} + +# f_package_delete $name +# +# Delete package by full $name (lacks archive suffix; e.g., `.tbz'). +# +f_package_delete() +{ +	local funcname=f_package_delete +	local name="$1" + +	if ! { [ "$name" ] || { f_getvar $VAR_PACKAGE name && [ "$name" ]; }; } +	then +		f_dprintf "packageDelete: %s" \ +		          "$msg_no_package_name_passed_in_package_variable" +		return $FAILURE +	fi + +	f_dprintf "$funcname: name=[%s]" "$name" + +	[ "$name" ] || return $FAILURE + +	{ # Verify and initialize device media if-defined +		f_media_verify && +		f_device_init device_media && +		f_index_initialize +	} || return $FAILURE + +	# Now we have (indirectly via f_index_read()): +	#   CATEGORY_MENU_LIST _categories_{varpkg} _rundeps_{varpkg} +	#   PACKAGE_CATEGORIES _npkgs + +	local varpkg +	f_str2varname "$name" varpkg + +	# Just as-in the user-interface (opposed to scripted-use), only allow +	# packages with at least one category to be recognized. +	# +	local pkgcat= +	if ! f_getvar _categories_$varpkg pkgcat || [ ! "$pkgcat" ]; then +		# $pkg may be a partial name, search the index (this is slow) +		f_index_search PACKAGE_INDEX "$name" name +		if [ ! "$name" ]; then +			f_show_msg \ +			    "$msg_sorry_package_was_not_found_in_the_index" \ +			    "$name" +			return $FAILURE +		fi +		f_str2varname "$name" varpkg +	fi + +	# If invoked through the scripted interface, we likely have not yet +	# detected the installed packages -- something we should do only once. +	# +	if [ ! "$PACKAGES_DETECTED" ]; then +		f_dprintf "$funcname: Detecting installed packages" +		f_package_detect_installed +		export PACKAGES_DETECTED=1 # exported for awk(1) ENVIRON[] +	fi +	# Now we have: _mark_{varpkg}=X for all installed packages + +	# +	# Return failure if the package is not already installed. +	# +	local pkgmark= +	f_getvar _mark_$varpkg pkgmark +	if ! [ "$pkgmark" -a ! "${pkgmark#[XUR]}" ]; then +		f_show_msg "$msg_package_not_installed_cannot_delete" "$name" +		return $FAILURE +	fi + +	# +	# Check for dependencies +	# +	local pkgsel depc=0 udeps= +	for pkgsel in $SELECTED_PACKAGES; do +		local mark= +		f_str2varname $pkgsel varpkg +		debug= f_getvar _mark_$varpkg mark +		[ "$mark" -a ! "${mark#[XUR]}" ] || continue +		local dep rundeps= +		debug= f_getvar _rundeps_$varpkg rundeps +		for dep in $rundeps; do +			if [ "$dep" = "$name" ]; then +				# Maybe this package is marked for deletion too +				if [ "$mark" = "U" ]; then +					udeps="$udeps $pkgsel" +				else +					depc=$(( $depc + 1 )) +				fi +				break +			fi +		done +	done +	if [ $depc -gt 0 ]; then +		local grammatical_s= +		[ $depc -gt 1 ] && grammatical_s=s +		f_show_msg \ +			"$msg_package_is_needed_by_other_installed_packages" \ +			"$name" "$depc" "$grammatical_s" +		return $FAILURE +	fi + +	# +	# Chase dependencies that are marked for uninstallation +	# +	for pkgsel in $udeps; do +		f_dprintf "$funcname: Uninstalling dependency %s (%s)" \ +		          "$pkgsel" "marked for delete" +		f_package_delete "$pkgsel" +	done + +	# +	# OK to perform the delete (no other packages depend on it)... +	# +	f_show_info "$msg_uninstalling_package_waiting_for_pkg_delete" "$name" +	if f_debugging; then +		f_eval_catch $funcname pkg 'pkg -d delete -y "%s"' "$name" +	else +		f_eval_catch $funcname pkg 'pkg delete -y "%s"' "$name" +	fi +	if [ $? -ne $SUCCESS ]; then +		f_show_msg "$msg_pkg_delete_failed" "$name" +		return $FAILURE +	else +		f_dprintf "$funcname: pkg-delete(8) of %s successful" "$name" +		f_str2varname "$name" varpkg +		setvar _mark_$varpkg "" +	fi +} + +# f_package_reinstall $name +# +# A simple wrapper to f_package_delete() + f_package_add() +# +f_package_reinstall() +{ +	f_package_delete "$1" && f_package_add "$1" +} + +############################################################ MAIN + +f_dprintf "%s: Successfully loaded." packages/packages.subr + +fi # ! $_PACKAGES_PACKAGES_SUBR | 
