From 7f36b7bf087251733bd30c159e873d5bd427ed53 Mon Sep 17 00:00:00 2001 From: dteske Date: Thu, 21 Nov 2013 03:38:47 +0000 Subject: MFC r257755-257756,257780-257785,257787-257793, and 257795,257817,257819,257937-257938,258029,258263-258267: 257755: SRV records 257756: fix spurious error message 257780: Whitespace 257781: Comments and funny syntax 257782: Debug file truncation is optional 257783: f_show_err for debugging 257784: f_eval_catch for debugging 257785: fix size calculations bug 257787: fix broken HTTP "any" media type 257788: more debugging 257789: Comments 257790: fix printf usage bug 257791: f_[v]sprintf added 257792: Comments 257793: fix off-by-one error in size calcs 257795: Replace pkg-tools with pkgng 257817: fix cosmetic typos 257819: Use `pkg -vv' to get ABI 257937: Adjustment to last 257938: Adjustment to last 258029: Comments 258263: Shuffle code around 258264: Remove unused code 258265: Debugging. Use f_eval_catch with pkg 258266: Shutdown media on exit from packages 258267: Fix pkg install from DVD Reviewed by: many Discussed on: -current Approved by: re (hrs) --- usr.sbin/bsdconfig/bsdconfig | 2 +- usr.sbin/bsdconfig/include/messages.subr | 19 ++- usr.sbin/bsdconfig/packages/packages | 2 + usr.sbin/bsdconfig/share/common.subr | 152 +++++++++++++++-- usr.sbin/bsdconfig/share/dialog.subr | 13 +- usr.sbin/bsdconfig/share/media/cdrom.subr | 5 +- usr.sbin/bsdconfig/share/media/http.subr | 55 +++++-- usr.sbin/bsdconfig/share/media/tcpip.subr | 41 ++++- usr.sbin/bsdconfig/share/packages/index.subr | 209 ++++++++++++++++++++++-- usr.sbin/bsdconfig/share/packages/packages.subr | 108 ++++++------ usr.sbin/bsdconfig/share/strings.subr | 35 +++- usr.sbin/bsdconfig/share/sysrc.subr | 4 +- usr.sbin/bsdconfig/startup/share/rcconf.subr | 4 +- 13 files changed, 520 insertions(+), 129 deletions(-) (limited to 'usr.sbin/bsdconfig') diff --git a/usr.sbin/bsdconfig/bsdconfig b/usr.sbin/bsdconfig/bsdconfig index 2c0d513..c158bfa 100755 --- a/usr.sbin/bsdconfig/bsdconfig +++ b/usr.sbin/bsdconfig/bsdconfig @@ -342,7 +342,7 @@ while getopts f:h$GETOPTS_STDARGS flag; do h|\?) usage ;; esac done -shift $(( $OPTIND -1 )) +shift $(( $OPTIND - 1 )) # If we've loaded any scripts, do not continue any further [ $scripts_loaded -gt 0 ] && exit diff --git a/usr.sbin/bsdconfig/include/messages.subr b/usr.sbin/bsdconfig/include/messages.subr index 70b5c3d..eb13201 100644 --- a/usr.sbin/bsdconfig/include/messages.subr +++ b/usr.sbin/bsdconfig/include/messages.subr @@ -40,6 +40,7 @@ msg_afterstep_desc="Ports to support the AfterStep window manager." msg_all="All" msg_all_desc="All available packages in all categories." msg_always_try_sudo_when_run_as="Always try sudo(8) when run as %s" +msg_an_unknown_error_occurred="An unknown error occurred" msg_arabic_desc="Ported software for Arab countries." msg_archivers_desc="Utilities for archiving and unarchiving data." msg_armenia="Armenia" @@ -48,7 +49,7 @@ msg_assume_yes_to_all_non_critical_dialogs="Assume \"Yes\" answers to all non-cr msg_astro_desc="Applications related to astronomy." msg_attempt_automatic_dhcp_configuration="Attempt automatic DHCP configuration of interfaces" msg_attempt_ipv6_configuration_of_interfaces="Attempt IPv6 configuration of interfaces" -msg_attempting_to_fetch_file_from_selected_media="Attempting to fetch %s file from selected media." +msg_attempting_to_update_repository_catalogue="Attempting to update repository catalogue from selected media." msg_audio_desc="Audio utilities - most require a supported sound card." msg_australia="Australia" msg_austria="Austria" @@ -137,9 +138,12 @@ msg_ftp_desc="FTP client and server utilities." msg_ftp_passive="FTP Passive" msg_ftp_username="FTP username" msg_games_desc="Various games and sundry amusements." +msg_generating_index_from_pkg_database="Generating INDEX from pkg(8) database\n(this can take a while)..." msg_geography_desc="Geography-related software." msg_german_desc="Ported software for Germanic countries." msg_germany="Germany" +msg_getting_package_categories_via_pkg_rquery="Getting package categories via pkg-rquery(8)..." +msg_getting_package_dependencies_via_pkg_rquery="Getting package dependencies via pkg-rquery(8)\n(this can take a while)..." msg_gnome_desc="Components of the Gnome Desktop environment." msg_gnustep_desc="Software for GNUstep desktop environment." msg_graphics_desc="Graphics libraries and utilities." @@ -175,7 +179,7 @@ msg_invalid_name_server_ip_address_specified="Invalid name server IP address spe msg_invalid_netmask_value="Invalid netmask value" msg_invalid_nfs_path_specification="Invalid NFS path specification. Must be of the form:\nhost:/full/pathname/to/FreeBSD/distdir" msg_io_error_while_reading_in_the_package="I/O error while reading in the %s package." -msg_io_or_format_error_on_index_file="I/O or format error on %s file.\nPlease verify media (or path to media) and try again." +msg_io_or_format_error_on_index_file="I/O or format error on INDEX file.\nPlease verify media (or path to media) and try again." msg_ipv4_address="IPv4 Address" msg_ipv4_gateway="IPv4 Gateway" msg_ipv6="IPv6" @@ -245,6 +249,7 @@ msg_no_gateway_has_been_set="No gateway has been set. You will be unable to acce msg_no_network_devices="No network devices available!" msg_no_package_name_passed_in_package_variable="No package name passed in package variable" msg_no_packages_were_selected_for_extraction="No packages were selected for extraction." +msg_no_pkg_database_found="No pkg(8) database found!" msg_no_such_file_or_directory="%s: %s: No such file or directory" msg_no_usb_devices_found="No USB devices found (try Options/Re-scan Devices)" msg_no_username="No username provided!" @@ -256,10 +261,9 @@ msg_ok="OK" msg_options="Options" msg_options_editor="Options Editor" msg_other="other" -msg_pkg_delete_failed="Warning: pkg_delete of %s failed.\n Run with debugging for details." +msg_pkg_delete_failed="Warning: pkg-delete(8) of %s failed.\n Run with debugging for details." msg_package_is_needed_by_other_installed_packages="Warning: Package %s is needed by\n %d other installed package%s." msg_package_not_installed_cannot_delete="Warning: package %s not installed\n No package can be deleted." -msg_package_read_successfully_waiting_for_pkg_add="Package %s read successfully - waiting for pkg_add(1)" msg_package_temp="Package Temp" msg_package_was_added_successfully="Package %s was added successfully" msg_packages="packages" @@ -269,7 +273,7 @@ msg_parallel_desc="Applications dealing with parallelism in computing." msg_pear_desc="Software related to the Pear PHP framework." msg_perl5_desc="Utilities/modules for the PERL5 language." msg_permission_denied="%s: %s: Permission denied" -msg_pkg_add_apparently_did_not_like_the_package="pkg_add(1) apparently did not like the %s package." +msg_pkg_install_apparently_did_not_like_the_package="pkg-install(8) apparently did not like the %s package." msg_plan9_desc="Software from the Plan9 operating system." msg_please_check_the_url_and_try_again="No such directory: %s\nplease check the URL and try again.\n" msg_please_enter_password="Please enter your password for sudo(8):" @@ -371,10 +375,13 @@ msg_unable_to_get_file_from_selected_media="Unable to get %s file from selected msg_unable_to_get_proper_ftp_path="Unable to get proper FTP path. FTP media not initialized." msg_unable_to_initialize_media_type_for_package_extract="Unable to initialize media type for package extract." msg_unable_to_make_directory_mountpoint="Unable to make %s directory mountpoint for %s!" +msg_unable_to_pkg_rquery_package_dependencies="Unable to pkg-rquery(8) package dependencies!" +msg_unable_to_pkg_rquery_package_categories="Unable to pkg-rquery(8) package categories!" msg_unable_to_open="Unable to open %s" +msg_unable_to_update_pkg_from_selected_media="Unable to update pkg(8) from selected media." msg_uninstall="Uninstall" msg_uninstall_desc="Mark this package for deletion" -msg_uninstalling_package_waiting_for_pkg_delete="Uninstalling %s package - waiting for pkg_delete(1)" +msg_uninstalling_package_waiting_for_pkg_delete="Uninstalling %s package - waiting for pkg-delete(8)" msg_unknown="unknown" msg_unknown_user="Unknown user: %s" msg_url_was_not_found="%s was not found, maybe directory or release-version are wrong?" diff --git a/usr.sbin/bsdconfig/packages/packages b/usr.sbin/bsdconfig/packages/packages index 5b01c3e..586079f 100755 --- a/usr.sbin/bsdconfig/packages/packages +++ b/usr.sbin/bsdconfig/packages/packages @@ -38,6 +38,7 @@ 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/media/common.subr f_include $BSDCFG_SHARE/mustberoot.subr f_include $BSDCFG_SHARE/packages/packages.subr @@ -73,6 +74,7 @@ f_device_get_all # # Display the package configuration menu and exit # +trap 'f_media_close' EXIT f_package_config ################################################################################ diff --git a/usr.sbin/bsdconfig/share/common.subr b/usr.sbin/bsdconfig/share/common.subr index d087cca..31da26e 100644 --- a/usr.sbin/bsdconfig/share/common.subr +++ b/usr.sbin/bsdconfig/share/common.subr @@ -60,7 +60,14 @@ FAILURE=1 # export UNAME_S="$(uname -s)" # Operating System (i.e. FreeBSD) export UNAME_P="$(uname -p)" # Processor Architecture (i.e. i386) +export UNAME_M="$(uname -m)" # Machine platform (i.e. i386) export UNAME_R="$(uname -r)" # Release Level (i.e. X.Y-RELEASE) +if [ ! "${PKG_ABI+set}" ]; then + export PKG_ABI="$( + ASSUME_ALWAYS_YES=1 pkg -vv | + awk '$1=="ABI:"{print $2;exit}' 2> /dev/null + )" +fi # # Default behavior is to call f_debug_init() automatically when loaded. @@ -68,6 +75,14 @@ export UNAME_R="$(uname -r)" # Release Level (i.e. X.Y-RELEASE) : ${DEBUG_SELF_INITIALIZE=1} # +# Default behavior of f_debug_init() is to truncate $debugFile (set to NULL to +# disable truncating the debug file when initializing). To get child processes +# to append to the same log file, export this variarable (with a NULL value) +# and also export debugFile with the desired value. +# +: ${DEBUG_INITIALIZE_FILE=1} + +# # Define standard optstring arguments that should be supported by all programs # using this include (unless DEBUG_SELF_INITIALIZE is set to NULL to prevent # f_debug_init() from autamatically processing "$@" for the below arguments): @@ -106,7 +121,7 @@ GETOPTS_ALLFLAGS="${GETOPTS_ALLFLAGS}0123456789" ############################################################ FUNCTIONS -# f_dprintf $fmt [ $opts ... ] +# f_dprintf $format [$arguments ...] # # Sensible debug function. Override in ~/.bsdconfigrc if desired. # See /usr/share/examples/bsdconfig/bsdconfigrc for example. @@ -164,18 +179,17 @@ f_debug_init() [ "$debugFile" ] && export debugFile # - # Truncate the debug file upon. Note that we will trim a leading plus - # (`+') from the value of debugFile to support persistant meaning that - # f_dprintf() should print both to standard output and $debugFile - # (minus the leading plus, of course). + # Truncate debug file unless requested otherwise. Note that we will + # trim a leading plus (`+') from the value of debugFile to support + # persistant meaning that f_dprintf() should print both to standard + # output and $debugFile (minus the leading plus, of course). # local _debug_file="${debugFile#+}" - if [ "$_debug_file" ]; then + if [ "$_debug_file" -a "$DEBUG_INITIALIZE_FILE" ]; then if ( umask 022 && :> "$_debug_file" ); then f_dprintf "Successfully initialized debugFile \`%s'" \ "$_debug_file" - [ "${debug+set}" ] || - debug=1 # turn debugging on if not set + f_isset debug || debug=1 # turn debugging on if not set else unset debugFile f_dprintf "Unable to initialize debugFile \`%s'" \ @@ -184,7 +198,7 @@ f_debug_init() fi } -# f_err $fmt [ $opts ... ] +# f_err $format [$arguments ...] # # Print a message to stderr (fd=2). # @@ -193,7 +207,7 @@ f_err() printf "$@" >&${TERMINAL_STDERR_PASSTHRU:-2} } -# f_quietly $command [ $arguments ... ] +# f_quietly $command [$arguments ...] # # Run a command quietly (quell any output to stdout or stderr) # @@ -275,7 +289,7 @@ f_isset() eval [ \"\${${1%%[$IFS]*}+set}\" ] } -# f_die [ $status [ $fmt [ $opts ... ]]] +# f_die [$status [$format [$arguments ...]]] # # Abruptly terminate due to an error optionally displaying a message in a # dialog box using printf(1) syntax. @@ -309,7 +323,7 @@ f_interrupt() f_die } -# f_show_info $fmt [ $opts ... ] +# f_show_info $format [$arguments ...] # # Display a message in a dialog infobox using printf(1) syntax. # @@ -330,7 +344,7 @@ f_show_info() fi } -# f_show_msg $fmt [ $opts ... ] +# f_show_msg $format [$arguments ...] # # Display a message in a dialog box using printf(1) syntax. # @@ -351,8 +365,32 @@ f_show_msg() fi } +# f_show_err $format [$arguments ...] +# +# Display a message in a dialog box with ``Error'' i18n title (overridden by +# setting msg_error) using printf(1) syntax. If running non-interactively, +# the process will terminate (using [above] f_die()). +# +f_show_err() +{ + [ "$nonInteractive" ] && f_die + + local msg + msg=$( printf "$@" ) -# f_yesno $fmt [ $opts ... ] + : ${msg:=${msg_an_unknown_error_occurred:-An unknown error occurred}} + + if [ "$_DIALOG_SUBR" ]; then + f_dialog_title "${msg_error:-Error}" + f_dialog_msgbox "$msg" + f_dialog_title_restore + else + dialog --title "${msg_error:-Error}" --msgbox "$msg" 0 0 + fi + return $SUCCESS +} + +# f_yesno $format [$arguments ...] # # Display a message in a dialog yes/no box using printf(1) syntax. # @@ -373,7 +411,7 @@ f_yesno() fi } -# f_noyes $fmt [ $opts ... ] +# f_noyes $format [$arguments ...] # # Display a message in a dialog yes/no box using printf(1) syntax. # NOTE: THis is just like the f_yesno function except "No" is default. @@ -466,7 +504,7 @@ f_include_lang() fi } -# f_usage $file [ $key1 $value1 ... ] +# f_usage $file [$key1 $value1 ...] # # Display USAGE file with optional pre-processor macro definitions. The first # argument is the template file containing the usage text to be displayed. If @@ -746,6 +784,88 @@ f_mounted() mount | grep -Eq " on $dir \([^)]+\)$" } +# f_eval_catch [-d] $funcname $utility $format [$arguments ...] +# +# Silently evaluate a command in a sub-shell and test for error. If debugging +# is enabled a copy of the command and its output is sent to debug (either +# stdout or file depending on environment). If an error occurs, output of the +# command is displayed in a dialog(1) msgbox using the [above] f_show_err() +# function (unless optional `-d' flag is the first argument, then no dialog). +# The $funcname argument is sent to debugging while the $utility argument is +# used in the title of the dialog box. The command that is sent to debugging +# along with $funcname is the product of the printf(1) syntax produced by +# $format with optional $arguments. +# +# Example 1: +# +# debug=1 +# f_eval_catch myfunc cat 'contents=$( cat "%s" )' /some/file +# # Error displayed ``cat: /some/file: No such file or directory'' +# +# Produces the following debug output: +# +# DEBUG: myfunc: cat "/some/file" +# DEBUG: myfunc: retval=1 +# cat: /some/file: No such file or directory +# +# Example 2: +# +# debug=1 +# f_eval_catch myfunc echo 'echo "%s"' "Hello, World!" +# # No error displayed +# +# Produces the following debug output: +# +# DEBUG: myfunc: echo "Hello, World!" +# DEBUG: myfunc: retval=0 +# Hello, World! +# +# Example 3: +# +# debug=1 +# echo 123 | f_eval_catch myfunc rev rev +# # No error displayed +# +# Produces the following debug output: +# +# DEBUG: myfunc: rev +# DEBUG: myfunc: retval=0 +# 321 +# +# Example 4: +# +# debug=1 +# f_eval_catch myfunc true true +# # No error displayed +# +# Produces the following debug output: +# +# DEBUG: myfunc: true +# DEBUG: myfunc: retval=0 +# +f_eval_catch() +{ + local no_dialog= + [ "$1" = "-d" ] && no_dialog=1 && shift 1 + local funcname="$1" utility="$2"; shift 2 + local cmd output retval + cmd=$( printf -- "$@" ) + f_dprintf "%s: %s" "$funcname" "$cmd" # Log command *before* eval + output=$( exec 2>&1; eval "$cmd" ) + retval=$? + if [ "$output" ]; then + f_dprintf "%s: retval=%i \n%s" "$funcname" \ + $retval "$output" + else + f_dprintf "%s: retval=%i " "$funcname" $retval + fi + ! [ "$no_dialog" -o "$nonInteractive" -o $retval -eq $SUCCESS ] && + msg_error="${msg_error:-Error}${utility:+: $utility}" \ + f_show_err "%s" "$output" + # NB: f_show_err will handle NULL output appropriately + return $retval +} + ############################################################ MAIN # diff --git a/usr.sbin/bsdconfig/share/dialog.subr b/usr.sbin/bsdconfig/share/dialog.subr index 01e928d..1297711 100644 --- a/usr.sbin/bsdconfig/share/dialog.subr +++ b/usr.sbin/bsdconfig/share/dialog.subr @@ -301,7 +301,16 @@ f_dialog_max_size() if [ "$USE_XDIALOG" ]; then __max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION else - __max_size=$( stty size 2> /dev/null ) # usually "24 80" + if __max_size=$( $DIALOG --print-maxsize \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) + then + # usually "MaxSize: 24, 80" + __max_size="${__max_size#*: }" + f_replaceall "$__max_size" "," "" __max_size + else + __max_size=$( stty size 2> /dev/null ) + # usually "24 80" + fi : ${__max_size:=$DEFAULT_TERMINAL_SIZE} fi if [ "$__var_height" ]; then @@ -518,7 +527,7 @@ f_dialog_menu_constrain() : ${__min_rows:=0} fi - local __max_rows=$(( $__max_height_menu_constrain - 8 )) + local __max_rows=$(( $__max_height_menu_constrain - 7 )) # If prompt_len is zero (no prompt), bump the max-rows by 1 # Default assumption is (if no argument) that there's no prompt [ ${__prompt_len:-0} -gt 0 ] || diff --git a/usr.sbin/bsdconfig/share/media/cdrom.subr b/usr.sbin/bsdconfig/share/media/cdrom.subr index c59f88a..7f8b523 100644 --- a/usr.sbin/bsdconfig/share/media/cdrom.subr +++ b/usr.sbin/bsdconfig/share/media/cdrom.subr @@ -163,8 +163,7 @@ f_media_get_cdrom() # f_media_shutdown_cdrom $device # -# Shuts down the CDROM device and ejects the media using f_media_eject_cdrom(), -# below. Return status should be ignored. +# Shuts down the CDROM device. Return status should be ignored. # f_media_shutdown_cdrom() { @@ -184,8 +183,6 @@ f_media_shutdown_cdrom() else CDROM_MOUNTED= fi - - f_media_eject_cdrom "$dev" } # f_media_eject_cdrom $device diff --git a/usr.sbin/bsdconfig/share/media/http.subr b/usr.sbin/bsdconfig/share/media/http.subr index 97c4ed0..23c146c 100644 --- a/usr.sbin/bsdconfig/share/media/http.subr +++ b/usr.sbin/bsdconfig/share/media/http.subr @@ -77,7 +77,8 @@ f_dialog_menu_media_http() f_dialog_title_restore local prompt="$msg_please_select_the_site_closest_to_you_or_other" local menu_list=" - '$msg_main_site' 'ftp.freebsd.org' + 'dist $msg_main_site' 'ftp.freebsd.org' + 'pkg $msg_main_site' 'pkg.freebsd.org' 'URL' '$msg_specify_some_other_http_site' " # END-QUOTE local hline="$msg_select_a_site_thats_close" @@ -452,6 +453,11 @@ f_media_init_http() local dev="$1" f_dprintf "Init routine called for HTTP device. dev=[%s]" "$dev" + if [ "$HTTP_INITIALIZED" ]; then + f_dprintf "HTTP device already initialized." + return $SUCCESS + fi + # # First verify access # @@ -481,7 +487,7 @@ f_media_init_http() case "$rel" in __RELEASE|any) - setvar $VAR_HTTP_PATH "$VAR_HTTP_DIR" + f_getvar $VAR_HTTP_DIR $VAR_HTTP_PATH f_http_check_access http_found=$? ;; @@ -493,22 +499,31 @@ f_media_init_http() # local fdir hp f_getvar $VAR_HTTP_PATH%/ hp - for fdir in $HTTP_DIRS; do - setvar $VAR_HTTP_PATH "$hp/$fdir/$rel" - if f_http_check_access; then - http_found=$SUCCESS - break - fi - done + setvar $VAR_HTTP_PATH "$hp/$PKG_ABI/latest" + if [ "$PKG_ABI" ] && f_http_check_access; then + http_found=$SUCCESS + setvar $VAR_HTTP_PATH "$hp" + else + for fdir in $HTTP_DIRS; do + setvar $VAR_HTTP_PATH "$hp/$fdir/$rel" + if f_http_check_access; then + http_found=$SUCCESS + break + fi + done + fi esac - [ $http_found -eq $SUCCESS ] && break + if [ $http_found -eq $SUCCESS ]; then + HTTP_INITIALIZED=YES + break + fi f_getvar $VAR_HTTP_PATH http_path f_show_msg "$msg_please_check_the_url_and_try_again" \ "$http_path" - unset $VAR_HTTP_PATH + unset HTTP_INITIALIZED $VAR_HTTP_PATH f_media_set_http || break done @@ -558,6 +573,11 @@ f_media_get_http() f_getvar $VAR_HTTP_HOST http_host f_getvar $VAR_HTTP_PORT http_port + if [ ! "$HTTP_INITIALIZED" ]; then + f_dprintf "No HTTP connection open, can't get file %s" "$file" + return $FAILURE + fi + if ! { f_validate_ipaddr "$http_host" || f_validate_ipaddr6 "$http_host" || @@ -595,6 +615,7 @@ f_media_get_http() local url="$http_path/$file" rx f_dprintf "sending http request for: %s" "$url" + f_dprintf "using nc to connect to: %s:%s" "$host" "$http_port" printf "GET %s HTTP/1.0\r\n\r\n" "$url" | nc -n "$host" "$http_port" | ( # @@ -648,6 +669,18 @@ f_media_get_http() return $FAILURE } +# f_media_shutdown_http $device +# +# Shuts down the HTTP device. Return status should be ignored. Note that since +# we don't maintain an open connection to the HTTP server, nothing to do. +# +f_media_shutdown_http() +{ + [ "$HTTP_INITIALIZED" ] || return $SUCCESS + + unset HTTP_INITIALIZED +} + ############################################################ MAIN f_dprintf "%s: Successfully loaded." media/http.subr diff --git a/usr.sbin/bsdconfig/share/media/tcpip.subr b/usr.sbin/bsdconfig/share/media/tcpip.subr index 5957b90..aaf237b 100644 --- a/usr.sbin/bsdconfig/share/media/tcpip.subr +++ b/usr.sbin/bsdconfig/share/media/tcpip.subr @@ -1020,10 +1020,41 @@ f_host_lookup() # Fall back to host(1) -- which is further governed by nsswitch.conf(5) # - local __output __ip6 __addrs="" __wait="" - f_getvar $VAR_MEDIA_TIMEOUT __wait - [ "$__wait" ] && __wait="-W $(( $__wait / 2 ))" + local __output __ip6 __addrs= f_getvar $VAR_IPV6_ENABLE __ip6 + + # If we have a TCP media type configured, check for an SRV record + local __srvtypes= + { f_quietly f_getvar $VAR_HTTP_PATH || + f_quietly f_getvar $VAR_HTTP_PROXY_PATH + } && __srvtypes="$__srvtypes _http._tcp" + f_quietly f_getvar $VAR_FTP_PATH && __srvtypes="$__srvtypes _ftp._tcp" + f_quietly f_getvar $VAR_NFS_PATH && + __srvtypes="$__srvtypes _nfs._tcp _nfs._udp" + + # Calculate wait time as dividend of total time and host(1) invocations + local __host_runs __wait + if [ "$__ip6" = "YES" ]; then + __host_runs=$(( 2 + $( set -- $__srvtypes; echo $# ) )) + else + __host_runs=$(( 1 + $( set -- $__srvtypes; echo $# ) )) + fi + f_getvar $VAR_MEDIA_TIMEOUT __wait + [ "$__wait" ] && __wait="-W $(( $__wait / $__host_runs ))" + + # Query SRV types first (1st host response taken as new host to query) + for __type in $__srvtypes; do + if __output=$( + host -t SRV $__wait -- "$__type.$__host" \ + 2> /dev/null + ); then + __host=$( echo "$__output" | + awk '/ SRV /{print $NF;exit}' ) + break + fi + done + + # Try IPv6 first (if enabled) if [ "$__ip6" = "YES" ]; then if ! __output=$( host -t AAAA $__wait -- "$__host" 2>&1 ); then # An error occurred, display in-full and return error @@ -1031,13 +1062,17 @@ f_host_lookup() setvar "$__var_to_set" "$__output" return $FAILURE fi + # Add the IPv6 addresses and fall-through to collect IPv4 too __addrs=$( echo "$__output" | awk '/ address /{print $NF}' ) fi + + # Good ol' IPv4 if ! __output=$( host -t A $__wait -- "$__host" 2>&1 ); then # An error occurred, display it in-full and return error [ "$__var_to_set" ] && setvar "$__var_to_set" "$__output" return $FAILURE fi + __addrs="$__addrs${__addrs:+ }$( echo "$__output" | awk '/ address /{print $NF}' )" if [ "$__var_to_set" ]; then diff --git a/usr.sbin/bsdconfig/share/packages/index.subr b/usr.sbin/bsdconfig/share/packages/index.subr index 863249c..6e14e4c 100755 --- a/usr.sbin/bsdconfig/share/packages/index.subr +++ b/usr.sbin/bsdconfig/share/packages/index.subr @@ -43,24 +43,53 @@ f_include_lang $BSDCFG_LIBE/include/messages.subr PACKAGE_INDEX= _INDEX_INITTED= +# +# Default path to pkg(8) repo-packagesite.sqlite database +# +SQLITE_REPO="/var/db/pkg/repo-packagesite.sqlite" + +# +# Default path to on-disk cache INDEX file +# +PACKAGES_INDEX_CACHEFILE="/var/run/bsdconfig/packages_INDEX.cache" + +# +# INDEX format for FreeBSD-6.0 or higher: +# +# package|port-origin|install-prefix|comment|port-desc-file|maintainer| +# categories|build-deps|run-deps|www-site|reserve|reserve|reserve|disc +# +INDEX_FORMAT="%n-%v" # package +INDEX_FORMAT="$INDEX_FORMAT|/usr/ports/%o" # port-origin +INDEX_FORMAT="$INDEX_FORMAT|%p" # install-prefix +INDEX_FORMAT="$INDEX_FORMAT|%c" # comment +INDEX_FORMAT="$INDEX_FORMAT|/usr/ports/%o/pkg-descr" # port-desc-file +INDEX_FORMAT="$INDEX_FORMAT|%m" # maintainer +INDEX_FORMAT="$INDEX_FORMAT|@CATEGORIES@" # place-holder +INDEX_FORMAT="$INDEX_FORMAT|" # build-deps +INDEX_FORMAT="$INDEX_FORMAT|@RUNDEPS@" # place-holder +INDEX_FORMAT="$INDEX_FORMAT|%w" # www-site +INDEX_FORMAT="$INDEX_FORMAT|" # reserved +INDEX_FORMAT="$INDEX_FORMAT|" # reserved +INDEX_FORMAT="$INDEX_FORMAT|" # reserved +INDEX_FORMAT="$INDEX_FORMAT|" # disc + ############################################################ FUNCTIONS -# f_index_initialize $path [$var_to_set] +# f_index_initialize [$var_to_set] # -# Read and initialize the global index. $path is to be relative to the chosen -# media (not necessarily the filesystem; e.g. FTP) -- this is usually going to -# be `packages/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. +# 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 __path="$1" __var_to_set="${2:-PACKAGE_INDEX}" + local __funcname=f_index_initialize + local __var_to_set="${2:-PACKAGE_INDEX}" [ "$_INDEX_INITTED" ] && return $SUCCESS - [ "$__path" ] || return $FAILURE # Got any media? f_media_verify || return $FAILURE @@ -68,20 +97,170 @@ f_index_initialize() # Does it move when you kick it? f_device_init media || return $FAILURE - f_show_info "$msg_attempting_to_fetch_file_from_selected_media" \ - "$__path" - eval "$__var_to_set"='$( f_device_get media "$__path" )' - if [ $? -ne $SUCCESS ]; then - f_show_msg "$msg_unable_to_get_file_from_selected_media" \ - "$__path" + 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_FLOPPY) + __site="file://${__data:-$MOUNTPOINT}/packages/$PKG_ABI" ;; + $DEVICE_TYPE_FTP) + f_getvar $VAR_FTP_PATH __site + __site="$__site/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" ;; + *) # UFS, DISK, CDROM, USB, DOS, NFS, etc. + __site="file://$MOUNTPOINT/packages/$PKG_ABI" + esac + + export PACKAGESITE="$__site" + f_dprintf "PACKAGESITE=[%s]" "$PACKAGESITE" + if ! f_eval_catch $__funcname pkg "pkg update"; then + f_show_err "$msg_unable_to_update_pkg_from_selected_media" + f_device_shutdown media + return $FAILURE + fi + + # + # Try to get contents from validated on-disk cache + # + + # + # Calculate digest used to determine if the on-disk persistant 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 media + return $FAILURE + fi + + # + # Check to see if the persistant 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 + f_show_info \ + "$msg_located_index_now_reading_package_data_from_it" + 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 + # + + f_show_info "$msg_getting_package_categories_via_pkg_rquery" + if ! eval "$( pkg rquery "%n-%v %C" | awk ' + { categories[$1] = categories[$1] " " $2 } + END { + for (package in categories) + { + cats = categories[package] + sub(/^ /, "", cats) + gsub(/[^[:alnum:]_]/, "_", package) + printf "local _%s_categories=\"%s\";\n", package, cats + } + }' )"; then + f_show_err "$msg_unable_to_pkg_rquery_package_dependencies" + f_device_shutdown media + return $FAILURE + fi + + f_show_info "$msg_getting_package_dependencies_via_pkg_rquery" + if ! eval "$( pkg rquery "%n-%v %dn-%dv" | awk ' + { rundeps[$1] = rundeps[$1] " " $2 } + END { + for (package in rundeps) + { + deps = rundeps[package] + sub(/^ /, "", deps) + gsub(/[^[:alnum:]_]/, "_", package) + printf "local _%s_rundeps=\"%s\";\n", package, deps + } + }' )"; then + f_show_err "$msg_unable_to_pkg_rquery_package_dependencies" f_device_shutdown media return $FAILURE fi + + f_show_info "$msg_generating_index_from_pkg_database" + eval "$__var_to_set"='$( pkg rquery "$INDEX_FORMAT" | + while read LINE; do + package="${LINE%%|*}"; + f_str2varname "$package" varpkg; + eval f_replaceall \"\$LINE\" \"\|@CATEGORIES@\|\" \ + \"\|\$_${varpkg}_categories\|\" LINE + eval f_replaceall \"\$LINE\" \"\|@RUNDEPS@\|\" \ + \"\|\$_${varpkg}_rundeps\|\" LINE + echo "$LINE" + done + )' # always returns true (status of last item in pipe-chain) eval "$__var_to_set"='$( debug= f_getvar "$__var_to_set" | sort )' + # + # Attempt to create the persistant on-disk cache + # + + # Create a new temporary file to write to + local __tmpfile="$( mktemp -t "$pgm" )" + if [ "$__tmpfile" ]; then + # Write the temporary file contents + echo "$__sqlite_digest" > "$__tmpfile" + debug= f_getvar "$__var_to_set" >> "$__tmpfile" + + # Finally, move the temporary file into place + case "$PACKAGES_INDEX_CACHEFILE" in + */*) f_quietly mkdir -p "${PACKAGES_INDEX_CACHEFILE%/*}" + esac + f_quietly mv -f "$__tmpfile" "$PACKAGES_INDEX_CACHEFILE" + fi + f_show_info "$msg_located_index_now_reading_package_data_from_it" if ! f_index_read "$__var_to_set"; then - f_show_msg "$msg_io_or_format_error_on_index_file" "$__path" + f_show_err "$msg_io_or_format_error_on_index_file" return $FAILURE fi diff --git a/usr.sbin/bsdconfig/share/packages/packages.subr b/usr.sbin/bsdconfig/share/packages/packages.subr index 85aebc8..a4ecdb9 100755 --- a/usr.sbin/bsdconfig/share/packages/packages.subr +++ b/usr.sbin/bsdconfig/share/packages/packages.subr @@ -53,7 +53,7 @@ f_include_lang $BSDCFG_LIBE/include/messages.subr # # Package extensions to try # -PACKAGE_EXTENSIONS=".tbz .tbz2 .tgz" +PACKAGE_EXTENSIONS=".txz .tbz .tbz2 .tgz" # # Variables used to track runtime states @@ -164,24 +164,13 @@ f_package_deselect() # f_package_detect_installed # -# Detect installed packages. Currently this searches /var/db/pkg for directory +# 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 installed package varpkg - # - # XXX KLUDGE ALERT! This makes evil assumptions about how XXX - # packages register themselves and should *really* be done with - # `pkg_info -e ' except that this is too slow for an - # item check routine.. :-( - # - # NOTE: When transitioning to pkgng, make a single fork to `pkg' to - # produce a list of all installed packages and parse _that_ - # - installed=$( find -s /var/db/pkg -mindepth 1 -maxdepth 1 -type d | - sed -e 's:/var/db/pkg/::' ) - for package in $installed; do + 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 @@ -582,12 +571,12 @@ f_package_menu_deselect() # 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" - local fname=f_package_review - f_dprintf "%s: SELECTED_PACKAGES=[%s]" $fname "$SELECTED_PACKAGES" + f_dprintf "$funcname: SELECTED_PACKAGES=[%s]" "$SELECTED_PACKAGES" prompt=$( printf "$msg_reviewing_selected_packages" "$_All_nselected" ) @@ -639,7 +628,7 @@ f_package_review() f_str2varname "$package" varpkg debug= f_getvar _mark_$varpkg mark [ "$mark" = "I" ] || continue - f_dprintf "%s: Installing %s package" $fname "$package" + f_dprintf "$funcname: Installing %s package" "$package" f_package_add "$package" done for package in $SELECTED_PACKAGES; do @@ -647,7 +636,7 @@ f_package_review() f_str2varname "$package" varpkg debug= f_getvar _mark_$varpkg mark [ "$mark" = "R" ] || continue - f_dprintf "%s: Reinstalling %s package" $fname "$package" + f_dprintf "$funcname: Reinstalling %s package" "$package" f_package_reinstall "$package" done for package in $SELECTED_PACKAGES; do @@ -655,7 +644,7 @@ f_package_review() f_str2varname "$package" varpkg debug= f_getvar _mark_$varpkg mark [ "$mark" = "U" ] || continue - f_dprintf "%s: Uninstalling %s package" $fname "$package" + f_dprintf "$funcname: Uninstalling %s package" "$package" f_package_delete "$package" || continue f_package_deselect "$package" done @@ -954,11 +943,11 @@ f_package_add() # f_package_extract() { + local funcname=f_package_extract local device="$1" name="$2" depended="$3" - local fname=f_package_extract - f_dprintf "%s: device=[%s] name=[%s] depended=[%s]" \ - $fname "$device" "$name" "$depended" + f_dprintf "$funcname: device=[%s] name=[%s] depended=[%s]" \ + "$device" "$name" "$depended" # Check to make sure it's not already there local varpkg mark= @@ -982,35 +971,45 @@ f_package_extract() f_getvar $VAR_PKG_TMPDIR:-/var/tmp tmpdir f_quietly mkdir -p -m 1777 "$tmpdir" - local path + local path device_type + device_$device get type device_type case "$name" in */*) path="$name" ;; *) - case "$name" in - *-*|*_*) path="packages/All/$name" ;; - *) path="packages/Latest/$name" - esac + 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 get the file + # 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" - f_dprintf "%s: found path=[%s] dev=[%s]" \ - $fname "$path" "$device" + 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" "$device" 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 "%s: No such %s file on %s device" \ - $fname "$path" "$device" + f_dprintf "$funcname: No such %s file on %s device" \ + "$path" "$device" $alert "$msg_unable_to_fetch_package_from_selected_media" \ "$name" [ "$no_confirm" ] && sleep 2 @@ -1026,27 +1025,14 @@ f_package_extract() f_show_info "$msg_adding_package_from_media" "$name" "$devname" fi - # Get package data and pipe into pkg_add(1) while providing feedback - { - if ! f_device_get $device "$path"; then - $alert "$msg_io_error_while_reading_in_the_package" \ - "$name" \ - >&$DIALOG_TERMINAL_PASSTHRU_FD 2> /dev/null - [ "$no_confirm" ] && sleep 2 - else - f_show_info \ - "$msg_package_read_successfully_waiting_for_pkg_add" \ - "$name" >&$DIALOG_TERMINAL_PASSTHRU_FD 2> /dev/null - fi - } | { - if f_debugging; then - /usr/sbin/pkg_add -v - - else - f_quietly /usr/sbin/pkg_add - - fi - } + # Request the package be added via pkg-install(8) + if f_debugging; then + f_eval_catch $funcname pkg 'pkg -d install -y "%s"' "$name" + else + f_eval_catch $funcname pkg 'pkg install -y "%s"' "$name" + fi if [ $? -ne $SUCCESS ]; then - $alert "$msg_pkg_add_apparently_did_not_like_the_package" \ + $alert "$msg_pkg_install_apparently_did_not_like_the_package" \ "$name" [ "$no_confirm" ] && sleep 2 else @@ -1063,8 +1049,8 @@ f_package_extract() # f_package_delete() { + local funcname=f_package_delete local name="$1" - local fname=f_package_delete if ! { [ "$name" ] || { f_getvar $VAR_PACKAGE name && [ "$name" ]; }; } then @@ -1073,7 +1059,7 @@ f_package_delete() return $FAILURE fi - f_dprintf "%s: name=[%s]" $fname "$name" + f_dprintf "$funcname: name=[%s]" "$name" [ "$name" ] || return $FAILURE @@ -1110,7 +1096,7 @@ f_package_delete() # detected the installed packages -- something we should do only once. # if [ ! "$PACKAGES_DETECTED" ]; then - f_dprintf "%s: Detecting installed packages" $fname + f_dprintf "$funcname: Detecting installed packages" f_package_detect_installed export PACKAGES_DETECTED=1 # exported for awk(1) ENVIRON[] fi @@ -1162,8 +1148,8 @@ f_package_delete() # Chase dependencies that are marked for uninstallation # for pkgsel in $udeps; do - f_dprintf "%s: Uninstalling dependecy %s (marked for delete)" \ - $fname "$pkgsel" + f_dprintf "$funcname: Uninstalling dependency %s (%s)" \ + "$pkgsel" "marked for delete" f_package_delete "$pkgsel" done @@ -1172,15 +1158,15 @@ f_package_delete() # f_show_info "$msg_uninstalling_package_waiting_for_pkg_delete" "$name" if f_debugging; then - pkg_delete -v "$name" + f_eval_catch $funcname pkg 'pkg -d delete -y "%s"' "$name" else - f_quietly pkg_delete "$name" + 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 "%s: pkg_delete(1) of %s successful" $fname "$name" + f_dprintf "$funcname: pkg-delete(8) of %s successful" "$name" f_str2varname "$name" varpkg setvar _mark_$varpkg "" fi diff --git a/usr.sbin/bsdconfig/share/strings.subr b/usr.sbin/bsdconfig/share/strings.subr index f78948b..25cceee 100644 --- a/usr.sbin/bsdconfig/share/strings.subr +++ b/usr.sbin/bsdconfig/share/strings.subr @@ -46,7 +46,7 @@ VALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_" ############################################################ FUNCTIONS -# f_substr "$string" $start [ $length ] +# f_substr "$string" $start [$length] # # Simple wrapper to awk(1)'s `substr' function. # @@ -56,17 +56,18 @@ f_substr() echo "$string" | awk "{ print substr(\$0, $start, $len) }" } -# f_snprintf $var_to_set $size $format ... +# f_snprintf $var_to_set $size $format [$arguments ...] # # Similar to snprintf(3), write at most $size number of bytes into $var_to_set -# using printf(1) syntax (`$format ...'). The value of $var_to_set is NULL -# unless at-least one byte is stored from the output. +# using printf(1) syntax (`$format [$arguments ...]'). The value of $var_to_set +# is NULL unless at-least one byte is stored from the output. # f_snprintf() { local __var_to_set="$1" __size="$2" - shift 2 # var_to_set/size - eval "$__var_to_set"=\$\( printf \"\$@\" \| awk -v max=\"\$__size\" \'' + shift 2 # var_to_set size + eval "$__var_to_set"=\$\( printf -- \"\$@\" \| \ + awk -v max=\"\$__size\" \'' { len = length($0) max -= len @@ -76,6 +77,18 @@ f_snprintf() }'\' \) } +# f_sprintf $var_to_set $format [$arguments ...] +# +# Similar to sprintf(3), write a string into $var_to_set using printf(1) syntax +# (`$format [$arguments ...]'). +# +f_sprintf() +{ + local __var_to_set="$1" + shift 1 # var_to_set + eval "$__var_to_set"=\$\( printf -- \"\$@\" \) +} + # f_vsnprintf $var_to_set $size $format $format_args # # Similar to vsnprintf(3), write at most $size number of bytes into $var_to_set @@ -111,6 +124,16 @@ f_vsnprintf() eval f_snprintf \"\$1\" \"\$2\" \"\$3\" $4 } +# f_vsprintf $var_to_set $format $format_args +# +# Similar to vsprintf(3), write a string into $var_to_set using printf(1) +# syntax (`$format $format_args'). +# +f_vsprintf() +{ + eval f_sprintf \"\$1\" \"\$2\" $3 +} + # f_longest_line_length # # Simple wrapper to an awk(1) script to print the length of the longest line of diff --git a/usr.sbin/bsdconfig/share/sysrc.subr b/usr.sbin/bsdconfig/share/sysrc.subr index 66a4cf3..c1bd0e2 100644 --- a/usr.sbin/bsdconfig/share/sysrc.subr +++ b/usr.sbin/bsdconfig/share/sysrc.subr @@ -118,8 +118,8 @@ f_clean_env() # f_sysrc_get $varname # # Get a system configuration setting from the collection of system- -# configuration files (in order: /etc/defaults/rc.conf /etc/rc.conf -# and /etc/rc.conf). +# configuration files (in order: /etc/defaults/rc.conf /etc/rc.conf and +# /etc/rc.conf.local) # # NOTE: Additional shell parameter-expansion formats are supported. For # example, passing an argument of "hostname%%.*" (properly quoted) will diff --git a/usr.sbin/bsdconfig/startup/share/rcconf.subr b/usr.sbin/bsdconfig/startup/share/rcconf.subr index f8f9a66..1ff57fd 100644 --- a/usr.sbin/bsdconfig/startup/share/rcconf.subr +++ b/usr.sbin/bsdconfig/startup/share/rcconf.subr @@ -119,7 +119,7 @@ f_startup_rcconf_map() fi # - # create the in-memory cache (potentially from validated on-disk cache) + # Create the in-memory cache (potentially from validated on-disk cache) # # @@ -140,7 +140,7 @@ f_startup_rcconf_map() # the current value and provide error exit status. # STARTUP_RCCONF_MAP=$( - ( # Get digest as the first word on the first line + ( # Get digest as the first word on first line read digest rest_ignored # -- cgit v1.1