1if [ ! "$_PACKAGES_PACKAGES_SUBR" ]; then _PACKAGES_PACKAGES_SUBR=1 2# 3# Copyright (c) 2013 Devin Teske 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE 17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25# SUCH DAMAGE. 26# 27# $FreeBSD$ 28# 29############################################################ INCLUDES 30 31BSDCFG_SHARE="/usr/share/bsdconfig" 32. $BSDCFG_SHARE/common.subr || exit 1 33f_dprintf "%s: loading includes..." "$0" 34f_include $BSDCFG_SHARE/dialog.subr 35f_include $BSDCFG_SHARE/device.subr 36f_include $BSDCFG_SHARE/media/common.subr 37f_include $BSDCFG_SHARE/packages/categories.subr 38f_include $BSDCFG_SHARE/packages/index.subr 39f_include $BSDCFG_SHARE/strings.subr 40 41BSDCFG_LIBE="/usr/libexec/bsdconfig" 42f_include_lang $BSDCFG_LIBE/include/messages.subr 43 44############################################################ CONFIGURATION 45 46# 47# How many packages to display (maximum) per dialog menubox. 48# 49: ${PACKAGE_MENU_PAGESIZE:=2000} 50 51############################################################ GLOBALS 52 53# 54# Package extensions to try 55# 56PACKAGE_EXTENSIONS=".tbz .tbz2 .tgz" 57 58# 59# Variables used to track runtime states 60# 61PACKAGES_DETECTED= # Boolean (NULL/non-NULL); detected installed packages? 62PACKAGE_CATEGORIES= # List of package categories parsed from INDEX 63SELECTED_PACKAGES= # Packages selected by user in [X]dialog(1) interface 64 65# 66# Options 67# 68[ "${SHOW_DESC+set}" ] || SHOW_DESC=1 69 70############################################################ FUNCTIONS 71 72# eval f_package_accent_category_menu $var_to_set $CATEGORY_MENU_LIST 73# 74# Accent the CATEGORY_MENU_LIST produced by f_index_read() (see 75# packages/index.subr). Accented information includes adding an asterisk to the 76# category name if its index has been cached, adding the number of installed 77# packages for each category, and adding the number _selected_ packages for 78# each category. 79# 80# NOTE: The reason `eval' is recommended/shown for the syntax above is because 81# the $CATEGORY_MENU_LIST generated by f_index_read() is meant to be expanded 82# prior to execution (it contains a series of pre-quoted strings which act as 83# the interpolated command arguments). 84# 85f_package_accent_category_menu() 86{ 87 local var_to_set="$1" category cat desc help varcat menu_buf n 88 shift 1 # var_to_set 89 while [ $# -gt 0 ]; do 90 category="${1%\*}" desc="${2%%; *}" help="$3" 91 shift 3 # cat/desc/help 92 93 cat="${category# }" # Trim lead space inserted by sort-method 94 f_str2varname "$cat" varcat 95 96 # Add number of installed packages for this category (if any) 97 n=0 98 case "$cat" in 99 "$msg_all") debug= f_getvar "_All_ninstalled" n ;; 100 *) debug= f_getvar "_${varcat}_ninstalled" n ;; 101 esac && 102 [ $n -ge 1 ] && desc="$desc; $n $msg_installed_lc" 103 104 # Add number of selected packages for this category (if any) 105 n=0 106 case "$cat" in 107 "$msg_all") debug= f_getvar "_All_nselected" n ;; 108 *) debug= f_getvar "_${varcat}_nselected" n ;; 109 esac && 110 [ $n -ge 1 ] && desc="$desc; $n $msg_selected" 111 112 # Re-Add asterisk to the category if its index has been cached 113 f_isset _index_page_${varcat}_1 && category="$category*" 114 115 # Update buffer with modified elements 116 menu_buf="$menu_buf 117 '$category' '$desc' '$help'" # End-Quote 118 done 119 setvar "$var_to_set" "$menu_buf" # return our buffer 120} 121 122# f_package_select $package ... 123# 124# Add $package to the list of tracked/selected packages. If $package is already 125# being tracked (already apears in $SELECTED_PACKAGES), this function amounts 126# to having no effect. 127# 128f_package_select() 129{ 130 local package pkgsel 131 while [ $# -gt 0 ]; do 132 package="$1" 133 shift 1 # package 134 for pkgsel in $SELECTED_PACKAGES; do 135 [ "$package" = "$pkgsel" ] && return 136 done 137 SELECTED_PACKAGES="$SELECTED_PACKAGES $package" 138 f_dprintf "Added %s to selection list" "$package" 139 done 140 SELECTED_PACKAGES="${SELECTED_PACKAGES# }" # Trim leading space 141} 142 143# f_package_deselect $package ... 144# 145# Remove $package from teh list of tracked/selected packages. If $package is 146# not being tracked (doesn't appear in $SELECTED_PACKAGES), this function 147# amounts to having no effet. 148# 149f_package_deselect() 150{ 151 local package pkgsel 152 while [ $# -gt 1 ]; do 153 local new_list="" 154 package="$1" 155 shift 1 # package 156 for pkgsel in $SELECTED_PACKAGES; do 157 [ "$pkgsel" = "$package" ] && continue 158 new_list="$new_list${new_list:+ }$pkgsel" 159 done 160 SELECTED_PACKAGES="$new_list" 161 f_dprintf "Removed %s from selection list" "$package" 162 done 163} 164 165# f_package_detect_installed 166# 167# Detect installed packages. Currently this searches /var/db/pkg for directory 168# entries and marks each entry as an installed/selected package. 169# 170f_package_detect_installed() 171{ 172 local installed package varpkg 173 # 174 # XXX KLUDGE ALERT! This makes evil assumptions about how XXX 175 # packages register themselves and should *really* be done with 176 # `pkg_info -e <name>' except that this is too slow for an 177 # item check routine.. :-( 178 # 179 # NOTE: When transitioning to pkgng, make a single fork to `pkg' to 180 # produce a list of all installed packages and parse _that_ 181 # 182 installed=$( find -s /var/db/pkg -mindepth 1 -maxdepth 1 -type d | 183 sed -e 's:/var/db/pkg/::' ) 184 for package in $installed; do 185 f_str2varname $package varpkg 186 export _mark_$varpkg=X # exported for awk(1) ENVIRON[] 187 f_package_select $package 188 done 189} 190 191# f_package_calculate_totals 192# 193# Calculate number of installed/selected packages for each category listed in 194# $PACKAGE_CATEGORIES (the number of installed packages for $category is stored 195# as $_${varcat}_ninstalled -- where $varcat is the product of `f_str2varname 196# $category varcat' -- and number selected packages as $_${varcat}_nselected). 197# Also calculates the total number of installed/selected packages stored as 198# $_All_ninstalled and $_All_nselected. 199# 200# Calculations are peformed by checking "marks". A "mark" is stored as 201# $_mark_$varpkg -- where $varpkg is the product of `f_str2varname $package 202# varpkg'. A mark can be "X" for an installed package, `I' for a package that 203# is marked for installation, "R" for a package that is marked for re-install, 204# and "U" for a package that is marked for uninstallation. If a package mark is 205# NULL or a single space (e.g., " "), the package is considered to be NOT 206# selected (and therefore does not increment the counts calculated herein). 207# 208f_package_calculate_totals() 209{ 210 local pkg varpkg mark cat varcat pkgcat n tselected=0 tinstalled=0 211 for cat in $PACKAGE_CATEGORIES; do 212 f_str2varname $cat varcat 213 setvar _${varcat}_ninstalled=0 214 setvar _${varcat}_nselected=0 215 done 216 for pkg in $SELECTED_PACKAGES; do 217 f_str2varname $pkg varpkg 218 mark= 219 f_getvar _mark_$varpkg mark 220 case "$mark" in 221 ""|" ") : ;; 222 X) tinstalled=$(( $tinstalled + 1 )) ;; 223 *) tselected=$(( $tselected + 1 )) 224 esac 225 f_getvar _categories_$varpkg pkgcat 226 for cat in $pkgcat; do 227 f_str2varname $cat varcat 228 case "$mark" in 229 ""|" ") : ;; 230 X) debug= f_getvar _${varcat}_ninstalled n 231 setvar _${varcat}_ninstalled $(( $n + 1 )) ;; 232 *) debug= f_getvar _${varcat}_nselected n 233 setvar _${varcat}_nselected $(( $n + 1 )) 234 esac 235 done 236 done 237 _All_nselected=$tselected 238 _All_ninstalled=$tinstalled 239} 240 241# f_package_calculate_rundeps 242# 243# Update package dependencies by first unmarking all dependencies and then 244# re-marking all dependencies of packages marked for either install ("I") or 245# re-install ("R"). 246# 247f_package_calculate_rundeps() 248{ 249 local pkg varpkg mark rundeps dep vardep 250 251 # 252 # First unmark all the existing run-dependencies 253 # 254 f_dprintf "Unselecting package run-dependencies..." 255 for pkg in $SELECTED_PACKAGES; do 256 f_str2varname $pkg varpkg 257 mark= 258 debug= f_getvar _mark_$varpkg mark 259 # Only unmark if it's marked as a Dependency 260 if [ "$mark" = "D" ]; then 261 f_dprintf "%s unselected" $pkg 262 unset _mark_$varpkg 263 f_package_deselect $pkg 264 fi 265 done 266 267 # 268 # Processes selected packages, adding dependencies 269 # 270 f_dprintf "Re-selecting package run-dependencies..." 271 for pkg in $SELECTED_PACKAGES; do 272 f_str2varname $pkg varpkg 273 mark= 274 debug= f_getvar _mark_$varpkg mark 275 # Skip pkg unless marked for [Re-]Install 276 [ "$mark" = "I" -o "$mark" = "R" ] || continue 277 f_getvar _rundeps_$varpkg rundeps 278 for dep in $rundeps; do 279 f_str2varname $dep vardep 280 mark= 281 debug= f_getvar _mark_$vardep mark 282 # Skip dep if already marked 283 [ "${mark:- }" = " " ] || continue 284 export _mark_$vardep="D" 285 f_package_select $dep 286 done 287 done 288 289 f_dprintf "Finished recalculating dependencies." 290} 291 292# f_package_menu_categories $var_to_set $defaultitem 293# 294# Dislay the menu of package categories, complete with package counts for each 295# category, accents, and other miscellany. If $defaultitem is non-NULL and 296# matches one of the existing menu-items, it will be pre-highlighted in the 297# menu dialog (HINT: Use f_dialog_menutag_fetch() to populate a local variable 298# that is passed as $defaultitem to highlight the user's last selection). 299# 300f_package_menu_categories() 301{ 302 local var_to_get="$1" defaultitem="$2" 303 local prompt="$msg_please_select_a_category_to_display" 304 local menu_list=" 305 '> $msg_review' '$msg_review_desc' '$msg_review_help' 306 " # End-Quote 307 local hline= 308 309 f_package_calculate_rundeps 310 # updates package mark variables and SELECTED_PACKAGES 311 f_package_calculate_totals 312 # creates _{varcat}_ninstalled and _{varcat}_nselected 313 314 local category_list 315 debug= f_getvar "$var_to_get" category_list || return $FAILURE 316 317 # Accent the category menu list with ninstalled/nselected 318 eval f_package_accent_category_menu category_list $category_list 319 320 # Add list of categories to menu list 321 menu_list="$menu_list $category_list" 322 323 local height width rows 324 eval f_dialog_menu_with_help_size height width rows \ 325 \"\$DIALOG_TITLE\" \ 326 \"\$DIALOG_BACKTITLE\" \ 327 \"\$prompt\" \ 328 \"\$hline\" \ 329 $menu_list 330 local menu_choice 331 menu_choice=$( eval $DIALOG \ 332 --title \"\$DIALOG_TITLE\" \ 333 --backtitle \"\$DIALOG_BACKTITLE\" \ 334 --hline \"\$hline\" \ 335 --item-help \ 336 --default-item \"\$defaultitem\" \ 337 --ok-label \"$msg_select\" \ 338 --cancel-label \"$msg_cancel\" \ 339 --menu \"\$prompt\" \ 340 $height $width $rows \ 341 $menu_list \ 342 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 343 ) 344 local retval=$? 345 f_dialog_menutag_store -s "$menu_choice" 346 return $retval 347} 348 349# f_package_index_get_page $category $page [$var_to_set [$var_to_get]] 350# 351# Obtain a [potentially cached] page of the INDEX file for a given $category. 352# If $page is 1 and the cache has not yet been generated, the cache-generating 353# function f_index_extract_pages() (above) is called to generate all pages 354# (not just the requested page) in cache before returning the requested page. 355# If $page is not 1 and there is no cached page, failure status is returned. 356# 357f_package_index_get_page() 358{ 359 local category="$1" page="$2" var_to_set="$3" var_to_get="$4" varcat 360 f_str2varname "$category" varcat 361 if ! debug= f_getvar "_index_page_${varcat}_$page" $var_to_set && 362 [ "$page" = "1" ] 363 then 364 f_show_info "$msg_building_package_menus" 365 local pagesize="$PACKAGE_MENU_PAGESIZE" 366 f_index_extract_pages "${var_to_get:-PACKAGE_INDEX}" \ 367 _index_page_${varcat} "$pagesize" "$category" 368 debug= f_getvar _index_page_${varcat}_$page $var_to_set 369 370 # Update category default-item because now we're cached 371 [ $page -eq 1 ] && 372 category_defaultitem="${category_defaultitem%\*}*" 373 else 374 return $FAILURE 375 fi 376} 377 378# f_package_menu_select $category [$page [$defaultitem]] 379# 380# Display list of packages for $category, optionally $page N and with a default 381# item selected. If $page is omitted, the first page is displayed (but this 382# only matters if there are multiple pages; which is determined by the global 383# maximum $PACKAGE_MENU_PAGESIZE). 384# 385# On success, if the user doesn't press ESC or choose Cancel, use 386# f_dialog_menuitem_fetch() to populate a local variable with the item (not 387# tag) corresponding to the user's selection. The tag portion of the user's 388# selection is available through f_dialog_menutag_fetch(). 389# 390f_package_menu_select() 391{ 392 local category="$1" page="${2:-1}" 393 local prompt= # Calculated below 394 local menu_list # Calculated below 395 local defaultitem="$3" 396 local hline="$hline_arrows_tab_punc_enter" 397 398 f_isinteger "$page" || return $FAILURE 399 400 local varcat 401 f_str2varname "$category" varcat 402 403 # Get number of packages for this category 404 local npkgs=0 405 case "$category" in 406 "$msg_all"|"") npkgs="${_npkgs:-0}" ;; 407 *) f_getvar _npkgs_$varcat npkgs 408 esac 409 410 # Calculate number of pages 411 local npages=$(( ${npkgs:=0} / $PACKAGE_MENU_PAGESIZE )) 412 413 # Add a page to the pagecount if not evenly divisible 414 [ $(( $npages * $PACKAGE_MENU_PAGESIZE )) -lt $npkgs ] && 415 npages=$(( $npages + 1 )) 416 417 # Print some debugging information 418 f_dprintf "f_package_menu_select: category=[%s] npkgs=%u npages=%u" \ 419 "$category" "$npkgs" "$npages" 420 421 local add_prev="" add_next="" 422 local previous_page="$msg_previous_page" next_page="$msg_next_page" 423 if [ $page -gt 1 ]; then 424 add_prev=1 425 # Accent the `Previous Page' item with an asterisk 426 # if the page-before-previous is loaded/cached 427 f_isset _index_page_${varcat}_$(( $page - 1 )) && 428 previous_page="$previous_page*" 429 fi 430 if [ $page -lt $npages ]; then 431 add_next=1 432 # Accent the `Next Page' item with an asterisk 433 # if the page-after-next is loaded/cached 434 f_isset _index_page_${varcat}_$(( $page + 1 )) && 435 next_page="$next_page*" 436 fi 437 438 local index_page 439 f_package_index_get_page "$category" $page index_page 440 441 menu_list=" 442 ${add_prev:+'> $previous_page' '' ${SHOW_DESC:+''}} 443 ${add_next:+'> $next_page' '' ${SHOW_DESC:+''}} 444 $( 445 export SHOW_DESC 446 export VALID_VARNAME_CHARS 447 echo "$index_page" | awk -F'|' -v view="port" ' 448 BEGIN { 449 valid_chars = ENVIRON["VALID_VARNAME_CHARS"] 450 prefix = "" 451 } 452 { 453 cur_prefix = tolower(substr($1, 1, 1)) 454 printf "'\''" 455 if ( prefix != cur_prefix ) 456 prefix = cur_prefix 457 else 458 printf " " 459 package = $1 460 if ( view == "port" ) 461 desc = $2 462 varpkg = package 463 gsub("[^" valid_chars "]", "_", varpkg) 464 mark = ENVIRON["_mark_" varpkg] 465 if ( ! mark ) mark = " " 466 printf "%s'\'' '\''[%c] %s'\''", 467 package, mark, desc 468 if ( ENVIRON["SHOW_DESC"] ) { 469 help = $4 470 gsub(/'\''/, "'\''\\'\'\''", help) 471 printf " '\''%s'\''", help 472 } 473 printf "\n" 474 }' 475 ) 476 ${add_prev:+'> $previous_page' '' ${SHOW_DESC:+''}} 477 ${add_next:+'> $next_page' '' ${SHOW_DESC:+''}} 478 " # End-Quote 479 480 # Accept/Translate i18n "All" but other category names must 481 # match tree definitions from INDEX, ports, FTP, etc. 482 case "$category" in 483 "$msg_all"|"") f_category_desc_get "All" prompt ;; 484 *) f_category_desc_get "$category" prompt ;; 485 esac 486 prompt="$prompt $( printf "$msg_page_of_npages" \ 487 "$page" "$npages" )" 488 489 local mheight mwidth mrows 490 eval f_dialog_menu${SHOW_DESC:+_with_help}_size mheight mwidth mrows \ 491 \"\$DIALOG_TITLE\" \"\$DIALOG_BACKTITLE\" \ 492 \"\$prompt\" \"\$hline\" $menu_list 493 local iheight iwidth 494 f_dialog_infobox_size iheight iwidth \ 495 "$DIALOG_TITLE" "$DIALOG_BACKTITLE" \ 496 "$msg_processing_selection" 497 498 local menu_choice 499 menu_choice=$( eval $DIALOG \ 500 --title \"\$DIALOG_TITLE\" \ 501 --backtitle \"\$DIALOG_BACKTITLE\" \ 502 --hline \"\$hline\" \ 503 --keep-tite \ 504 --ok-label \"$msg_select\" \ 505 --cancel-label \"$msg_back\" \ 506 ${SHOW_DESC:+--item-help} \ 507 --default-item \"\$defaultitem\" \ 508 --menu \"\$prompt\" \ 509 $mheight $mwidth $mrows \ 510 $menu_list \ 511 --and-widget \ 512 ${USE_XDIALOG:+--no-buttons} \ 513 --infobox \"\$msg_processing_selection\" \ 514 $iheight $iwidth \ 515 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 516 ) 517 local retval=$? 518 f_dialog_data_sanitize menu_choice 519 f_dialog_menutag_store "$menu_choice" 520 521 if [ $retval -eq $SUCCESS ]; then 522 local item 523 item=$( eval f_dialog_menutag2item${SHOW_DESC:+_with_help} \ 524 \"\$menu_choice\" $menu_list ) 525 f_dialog_menuitem_store "$item" 526 fi 527 528 return $retval 529} 530 531# f_package_menu_deselect $package 532# 533# Display a menu, asking the user what they would like to do with $package 534# with regard to "deselecting" an already installed package. Choices include 535# uninstall, re-install, or cancel (leave $package marked as installed). 536# Returns success if the user does not press ESC or choose Cnacel. Use the 537# f_dialog_menutag_fetch() function upon success to retrieve the user's choice. 538# 539f_package_menu_deselect() 540{ 541 local package="$1" 542 local prompt # Calculated below 543 local menu_list=" 544 'X $msg_installed' '$msg_installed_desc' 545 'R $msg_reinstall' '$msg_reinstall_desc' 546 'U $msg_uninstall' '$msg_uninstall_desc' 547 " # End-Quote 548 local hline="$hline_alnum_arrows_punc_tab_enter" 549 550 prompt=$( printf "$msg_what_would_you_like_to_do_with" "$package" ) 551 552 local height width rows 553 eval f_dialog_menu_size height width rows \ 554 \"\$DIALOG_TITLE\" \ 555 \"\$DIALOG_BACKTITLE\" \ 556 \"\$prompt\" \ 557 \"\$hline\" \ 558 $menu_list 559 local menu_choice 560 menu_choice=$( eval $DIALOG \ 561 --title \"\$DIALOG_TITLE\" \ 562 --backtitle \"\$DIALOG_BACKTITLE\" \ 563 --hline \"\$hline\" \ 564 --ok-label \"$msg_select\" \ 565 --cancel-label \"$msg_cancel\" \ 566 --menu \"\$prompt\" \ 567 $height $width $rows \ 568 $menu_list \ 569 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 570 ) 571 local retval=$? 572 f_dialog_menutag_store -s "$menu_choice" 573 return $retval 574} 575 576# f_package_review 577# 578# Display a review screen, showing selected packages and what they are marked 579# for, before proceeding (if the user does not press ESC or choose Cancel) to 580# operate on each selection. Returns error if no packages have been selected, 581# or the user has pressed ESC, or if they have chosen Cancel. 582# 583f_package_review() 584{ 585 local prompt # Calculated below 586 local menu_list # Calculated below 587 local hline="$hline_alnum_arrows_punc_tab_enter" 588 589 local fname=f_package_review 590 f_dprintf "%s: SELECTED_PACKAGES=[%s]" $fname "$SELECTED_PACKAGES" 591 592 prompt=$( printf "$msg_reviewing_selected_packages" "$_All_nselected" ) 593 594 local package varpkg mark 595 for package in $SELECTED_PACKAGES; do 596 mark= 597 f_str2varname "$package" varpkg 598 f_getvar _mark_$varpkg mark 599 [ "$mark" -a ! "${mark#[IRUD]}" ] || continue 600 menu_list="$menu_list 601 '$mark' '$package' 602 " # End-Quote 603 done 604 if [ ! "$menu_list" ]; then 605 f_show_msg "$msg_no_packages_were_selected_for_extraction" 606 return $FAILURE # They might have selected this by accident 607 fi 608 menu_list=$( echo "$menu_list" | sort ) 609 610 local height width rows 611 eval f_dialog_menu_size height width rows \ 612 \"\$DIALOG_TITLE\" \ 613 \"\$DIALOG_BACKTITLE\" \ 614 \"\$prompt\" \ 615 \"\$hline\" \ 616 $menu_list 617 618 # Show the review menu (ignore menu choice) 619 eval $DIALOG \ 620 --title \"\$DIALOG_TITLE\" \ 621 --backtitle \"\$DIALOG_BACKTITLE\" \ 622 --hline \"\$hline\" \ 623 --ok-label \"\$msg_proceed\" \ 624 --cancel-label \"\$msg_cancel\" \ 625 --menu \"\$prompt\" \ 626 $height $width $rows \ 627 $menu_list \ 628 2> /dev/null || return $? 629 # Return if the user pressed ESC or chose Cancel/No 630 631 # 632 # Process each of the selected packages: 633 # + First, process packages marked for Install. 634 # + Second, process packages marked for Re-install. 635 # + Finally, process packages marked for Uninstall. 636 # 637 for package in $SELECTED_PACKAGES; do 638 mark= 639 f_str2varname "$package" varpkg 640 debug= f_getvar _mark_$varpkg mark 641 [ "$mark" = "I" ] || continue 642 f_dprintf "%s: Installing %s package" $fname "$package" 643 f_package_add "$package" 644 done 645 for package in $SELECTED_PACKAGES; do 646 mark= 647 f_str2varname "$package" varpkg 648 debug= f_getvar _mark_$varpkg mark 649 [ "$mark" = "R" ] || continue 650 f_dprintf "%s: Reinstalling %s package" $fname "$package" 651 f_package_reinstall "$package" 652 done 653 for package in $SELECTED_PACKAGES; do 654 mark= 655 f_str2varname "$package" varpkg 656 debug= f_getvar _mark_$varpkg mark 657 [ "$mark" = "U" ] || continue 658 f_dprintf "%s: Uninstalling %s package" $fname "$package" 659 f_package_delete "$package" || continue 660 f_package_deselect "$package" 661 done 662 663 return $SUCCESS 664} 665 666# f_package_config 667# 668# Allow the user to configure packages and install them. Initially, a list of 669# package categories is loaded/displayed. When the user selects a category, 670# the menus for that category are built (unlike sysinstall which built all 671# category menus up-front -- which also took forever, despite the fact that 672# few people visit more than a couple of categories each time). 673# 674f_package_config() 675{ 676 # Did we get an INDEX? 677 f_index_initialize packages/INDEX || return $FAILURE 678 # Creates following variables (indirectly via f_index_read()) 679 # CATEGORY_MENU_LIST _categories_{varpkg} _rundeps_{varpkg} 680 # PACKAGE_CATEGORIES _npkgs 681 682 # Detect installed packages (updates marks/SELECTED_PACKAGES) 683 f_package_detect_installed 684 export PACKAGES_DETECTED=1 # exported for awk(1) ENVIRON[] 685 686 local retval category varcat defaultitem category_defaultitem="" 687 while :; do 688 # Display the list of package categories 689 f_package_menu_categories \ 690 CATEGORY_MENU_LIST "$category_defaultitem" 691 retval=$? 692 f_dialog_menutag_fetch category 693 f_dprintf "retval=%u mtag=[%s]" $retval "$category" 694 category_defaultitem="$category" 695 696 [ $retval -eq $SUCCESS ] || break 697 698 # Maybe the user chose an action (like `Review') 699 case "$category" in 700 "> $msg_review") 701 f_package_review && break 702 continue ;; 703 "> "*) 704 continue 705 esac 706 707 # Anything else is a package category 708 709 category=${category# } # Trim leading space if present 710 category=${category%\*} # Trim trailing asterisk if present 711 712 f_str2varname "$category" varcat 713 714 local page package varpkg mark menu_choice 715 while :; do 716 # Display the list of packages for selected category 717 page=1 defaultitem="" 718 f_getvar _defaultitem_$varcat defaultitem 719 f_getvar _defaultpage_$varcat page 720 f_package_menu_select \ 721 "$category" "${page:=1}" "$defaultitem" 722 retval=$? 723 f_dialog_menutag_fetch menu_choice 724 f_dprintf "retval=%u mtag=[%s]" $retval "$menu_choice" 725 726 # NOTE: When --and-widget is used only ESC will cause 727 # dialog(1) to return without going to the next widget. 728 # This is alright in our case as we can still detect 729 # the Cancel button because stdout will be NULL. 730 # Alternatively, Xdialog(1) will terminate with 1 731 # if/when Cancel is chosen on any widget. 732 if [ $retval -eq 255 -o ! "$menu_choice" ]; then 733 # User pressed ESC or chose Cancel 734 break 735 elif [ $retval -eq 1 ]; then 736 # Using X11, Xdialog(1) returned 1 for Cancel 737 f_show_msg "%s" "$menu_choice" 738 break 739 elif [ $retval -ne $SUCCESS ]; then 740 # X11-related error occurred using Xdialog(1) 741 f_show_msg "%s" "$menu_choice" 742 break 743 fi 744 745 defaultitem="$menu_choice" 746 747 # NOTE: f_package_menu_select() does not show the 748 # `Previous Page' or `Next Page' items unless needed 749 case "$menu_choice" in 750 "> $msg_previous_page"|"> $msg_previous_page*") 751 page=$(( $page - 1 )) 752 setvar _defaultpage_$varcat $page 753 # Update default-item to match accent that will 754 # be applied by f_package_menu_select(); if the 755 # page-before-prev is cached, add an asterisk. 756 if f_isset \ 757 _index_page_${varcat}_$(( $page - 1 )) 758 then 759 defaultitem="${defaultitem%\*}*" 760 else 761 defaultitem="${defaultitem%\*}" 762 fi 763 setvar _defaultitem_$varcat "$defaultitem" 764 continue ;; 765 "> $msg_next_page"|"> $msg_next_page*") 766 page=$(( $page + 1 )) 767 setvar _defaultpage_$varcat $page 768 # Update default-item to match accent that will 769 # be applied by f_package_menu_select(); if the 770 # page-after-next is cached, add an asterisk. 771 if f_isset \ 772 _index_page_${varcat}_$(( $page + 1 )) 773 then 774 defaultitem="${defaultitem%\*}*" 775 else 776 defaultitem="${defaultitem%\*}" 777 fi 778 setvar _defaultitem_$varcat "$defaultitem" 779 continue ;; 780 "> "*) # Unknown navigation/action item 781 setvar _defaultpage_$varcat $page 782 continue ;; # Do not treat as a package 783 *) 784 setvar _defaultitem_$varcat "$defaultitem" 785 esac 786 787 # Treat any other selection as a package 788 package="${menu_choice# }" # Trim leading space 789 f_str2varname $package varpkg 790 f_dialog_menuitem_fetch mark 791 mark="${mark#?}" 792 mark="${mark%%\] *}" 793 case "$mark" in 794 "I") 795 mark=" " 796 f_package_deselect $package 797 ;; 798 " "|"D") 799 mark="I" 800 f_package_select $package 801 ;; 802 "X"|"R"|"U") 803 f_package_menu_deselect $package || continue 804 f_dialog_menutag_fetch menu_choice 805 case "$menu_choice" in 806 "X $msg_installed") 807 f_package_deselect "$package" 808 mark="X" 809 ;; 810 "R $msg_reinstall") 811 f_package_select "$package" 812 mark="R" 813 ;; 814 "U $msg_uninstall") 815 f_package_select "$package" 816 mark="U" 817 ;; 818 esac 819 ;; 820 esac 821 export _mark_$varpkg="$mark" 822 # NOTE: exported for awk(1) ENVIRON[] 823 done 824 done 825} 826 827# f_package_add $package_name [$depended] 828# 829# Like f_package_extract(), but assumes current media device and chases deps. 830# Note that $package_name should not contain the archive suffix (e.g., `.tbz'). 831# If $depended is present and non-NULL, the package is treated as a dependency 832# (in this function, dependencies are not handled any differently, but the 833# f_package_extract() function is passed this value and it displays a different 834# message when installing a dependency versus non-dependency). 835# 836f_package_add() 837{ 838 local name="$1" depended="$2" status=$SUCCESS retval 839 840 local alert=f_show_msg no_confirm= 841 f_getvar $VAR_NO_CONFIRM no_confirm 842 [ "$no_confirm" ] && alert=f_show_info 843 844 if ! { [ "$name" ] || { f_getvar $VAR_PACKAGE name && [ "$name" ]; }; } 845 then 846 f_dprintf "packageAdd: %s" \ 847 "$msg_no_package_name_passed_in_package_variable" 848 return $FAILURE 849 fi 850 851 { # Verify and initialize device media if-defined 852 f_media_verify && 853 f_device_init media && 854 f_index_initialize packages/INDEX 855 } || return $FAILURE 856 857 # Now we have (indirectly via f_index_read()): 858 # CATEGORY_MENU_LIST _categories_{varpkg} _rundeps_{varpkg} 859 # PACKAGE_CATEGORIES _npkgs 860 861 local varpkg 862 f_str2varname "$name" varpkg 863 864 # Just as-in the user-interface (opposed to scripted-use), only allow 865 # packages with at least one category to be recognized. 866 # 867 local pkgcat= 868 if ! f_getvar _categories_$varpkg pkgcat || [ ! "$pkgcat" ]; then 869 # $pkg may be a partial name, search the index (this is slow) 870 f_index_search PACKAGE_INDEX $name name 871 if [ ! "$name" ]; then 872 f_show_msg \ 873 "$msg_sorry_package_was_not_found_in_the_index" \ 874 "$name" 875 return $FAILURE 876 fi 877 f_str2varname "$name" varpkg 878 fi 879 880 # If invoked through the scripted interface, we likely have not yet 881 # detected the installed packages -- something we should do only once. 882 # 883 if [ ! "$PACKAGES_DETECTED" ]; then 884 f_dprintf "f_package_add: Detecting installed packages" 885 f_package_detect_installed 886 export PACKAGES_DETECTED=1 # exported for awk(1) ENVIRON[] 887 fi 888 # Now we have: _mark_{varpkg}=X for all installed packages 889 890 # 891 # Since we're maintaining data structures for installed packages, 892 # short-circuit the package dependency checks if the package is already 893 # installed. This prevents wasted cycles, minor delays between package 894 # extractions, and worst-case an infinite loop with a certain faulty 895 # INDEX file. 896 # 897 local mark= 898 f_getvar _mark_$varpkg mark && [ "$mark" = "X" ] && return $SUCCESS 899 900 local dep vardep rundeps= 901 f_getvar _rundeps_$varpkg rundeps 902 for dep in $rundeps; do 903 f_str2varname "$dep" vardep 904 905 # Skip dependency if already installed 906 mark= 907 f_getvar _mark_$vardep mark && [ "$mark" = "X" ] && continue 908 909 # Just as-in the user-interface (opposed to scripted-use), only 910 # allow packages with at least one category to be recognized. 911 # 912 local depcat= 913 if ! f_getvar _categories_$vardep depcat || [ ! "$depcat" ] 914 then 915 $alert "$msg_required_package_not_found" "$dep" 916 [ "$no_confirm" ] && sleep 2 917 fi 918 919 f_package_add "$dep" 920 retval=$? 921 if [ $retval -ne $SUCCESS ]; then 922 status=$(( $status | $retval )) 923 924 # XXX package could be on a future disc volume 925 # XXX (not supporting multiple disc volumes yet) 926 927 $alert "$msg_loading_of_dependent_package_failed" \ 928 "$dep" 929 [ "$no_confirm" ] && sleep 2 930 fi 931 done 932 [ $status -eq $SUCCESS ] || return $status 933 934 # 935 # Done with the deps? Try to load the real m'coy. 936 # 937 938 f_package_extract media "$name" "$depended" 939 retval=$? 940 if [ $retval -ne $SUCCESS ]; then 941 status=$(( $status | $retval )) 942 else 943 setvar _mark_$varpkg X 944 fi 945 946 return $status 947} 948 949# f_package_extract $device $name [$depended] 950# 951# Extract a package based on a namespec and media device. If $depended is 952# present and non-NULL, the notification displayed while installing the package 953# has "as a dependency" appended. 954# 955f_package_extract() 956{ 957 local device="$1" name="$2" depended="$3" 958 959 local fname=f_package_extract 960 f_dprintf "%s: device=[%s] name=[%s] depended=[%s]" \ 961 $fname "$device" "$name" "$depended" 962 963 # Check to make sure it's not already there 964 local varpkg mark= 965 f_str2varname "$name" varpkg 966 f_getvar _mark_$varpkg mark 967 [ "$mark" = "X" ] && return $SUCCESS 968 969 if ! f_device_init $device; then 970 f_show_msg \ 971 "$msg_unable_to_initialize_media_type_for_package_extract" 972 return $FAILURE 973 fi 974 975 # If necessary, initialize the ldconfig hints 976 [ -f "/var/run/ld-elf.so.hints" ] || 977 f_quietly ldconfig /usr/lib /usr/lib/compat /usr/local/lib 978 979 # Make a couple paranoid locations for temp 980 # files to live if user specified none 981 local tmpdir 982 f_getvar $VAR_PKG_TMPDIR:-/var/tmp tmpdir 983 f_quietly mkdir -p -m 1777 "$tmpdir" 984 985 local path 986 case "$name" in 987 */*) path="$name" ;; 988 *) 989 case "$name" in 990 *-*|*_*) path="packages/All/$name" ;; 991 *) path="packages/Latest/$name" 992 esac 993 esac 994 995 # We have a path, call the device strategy routine to get the file 996 local pkg_ext probe_only=1 found= 997 for pkg_ext in "" $PACKAGE_EXTENSIONS; do 998 if f_device_get $device "$path$pkg_ext" $probe_only; then 999 path="$path$pkg_ext" 1000 f_dprintf "%s: found path=[%s] dev=[%s]" \ 1001 $fname "$path" "$device" 1002 found=1 1003 break 1004 fi 1005 done 1006 1007 local alert=f_show_msg no_confirm= 1008 f_getvar $VAR_NO_CONFIRM no_confirm 1009 [ "$no_confirm" ] && alert=f_show_info 1010 1011 if [ ! "$found" ]; then 1012 f_dprintf "%s: No such %s file on %s device" \ 1013 $fname "$path" "$device" 1014 $alert "$msg_unable_to_fetch_package_from_selected_media" \ 1015 "$name" 1016 [ "$no_confirm" ] && sleep 2 1017 return $FAILURE 1018 fi 1019 1020 local devname= 1021 f_struct device_$device get name devname 1022 if [ "$depended" ]; then 1023 f_show_info "$msg_adding_package_as_a_dependency_from_media" \ 1024 "$name" "$devname" 1025 else 1026 f_show_info "$msg_adding_package_from_media" "$name" "$devname" 1027 fi 1028 1029 # Get package data and pipe into pkg_add(1) while providing feedback 1030 { 1031 if ! f_device_get $device "$path"; then 1032 $alert "$msg_io_error_while_reading_in_the_package" \ 1033 "$name" \ 1034 >&$DIALOG_TERMINAL_PASSTHRU_FD 2> /dev/null 1035 [ "$no_confirm" ] && sleep 2 1036 else 1037 f_show_info \ 1038 "$msg_package_read_successfully_waiting_for_pkg_add" \ 1039 "$name" >&$DIALOG_TERMINAL_PASSTHRU_FD 2> /dev/null 1040 fi 1041 } | { 1042 if f_debugging; then 1043 /usr/sbin/pkg_add -v - 1044 else 1045 f_quietly /usr/sbin/pkg_add - 1046 fi 1047 } 1048 if [ $? -ne $SUCCESS ]; then 1049 $alert "$msg_pkg_add_apparently_did_not_like_the_package" \ 1050 "$name" 1051 [ "$no_confirm" ] && sleep 2 1052 else 1053 f_show_info "$msg_package_was_added_successfully" "$name" 1054 sleep 1 1055 fi 1056 1057 return $SUCCESS 1058} 1059 1060# f_package_delete $name 1061# 1062# Delete package by full $name (lacks archive suffix; e.g., `.tbz'). 1063# 1064f_package_delete() 1065{ 1066 local name="$1" 1067 local fname=f_package_delete 1068 1069 if ! { [ "$name" ] || { f_getvar $VAR_PACKAGE name && [ "$name" ]; }; } 1070 then 1071 f_dprintf "packageDelete: %s" \ 1072 "$msg_no_package_name_passed_in_package_variable" 1073 return $FAILURE 1074 fi 1075 1076 f_dprintf "%s: name=[%s]" $fname "$name" 1077 1078 [ "$name" ] || return $FAILURE 1079 1080 { # Verify and initialize device media if-defined 1081 f_media_verify && 1082 f_device_init media && 1083 f_index_initialize packages/INDEX 1084 } || return $FAILURE 1085 1086 # Now we have (indirectly via f_index_read()): 1087 # CATEGORY_MENU_LIST _categories_{varpkg} _rundeps_{varpkg} 1088 # PACKAGE_CATEGORIES _npkgs 1089 1090 local varpkg 1091 f_str2varname "$name" varpkg 1092 1093 # Just as-in the user-interface (opposed to scripted-use), only allow 1094 # packages with at least one category to be recognized. 1095 # 1096 local pkgcat= 1097 if ! f_getvar _categories_$varpkg pkgcat || [ ! "$pkgcat" ]; then 1098 # $pkg may be a partial name, search the index (this is slow) 1099 f_index_search PACKAGE_INDEX "$name" name 1100 if [ ! "$name" ]; then 1101 f_show_msg \ 1102 "$msg_sorry_package_was_not_found_in_the_index" \ 1103 "$name" 1104 return $FAILURE 1105 fi 1106 f_str2varname "$name" varpkg 1107 fi 1108 1109 # If invoked through the scripted interface, we likely have not yet 1110 # detected the installed packages -- something we should do only once. 1111 # 1112 if [ ! "$PACKAGES_DETECTED" ]; then 1113 f_dprintf "%s: Detecting installed packages" $fname 1114 f_package_detect_installed 1115 export PACKAGES_DETECTED=1 # exported for awk(1) ENVIRON[] 1116 fi 1117 # Now we have: _mark_{varpkg}=X for all installed packages 1118 1119 # 1120 # Return failure if the package is not already installed. 1121 # 1122 local pkgmark= 1123 f_getvar _mark_$varpkg pkgmark 1124 if ! [ "$pkgmark" -a ! "${pkgmark#[XUR]}" ]; then 1125 f_show_msg "$msg_package_not_installed_cannot_delete" "$name" 1126 return $FAILURE 1127 fi 1128 1129 # 1130 # Check for dependencies 1131 # 1132 local pkgsel depc=0 udeps= 1133 for pkgsel in $SELECTED_PACKAGES; do 1134 local mark= 1135 f_str2varname $pkgsel varpkg 1136 debug= f_getvar _mark_$varpkg mark 1137 [ "$mark" -a ! "${mark#[XUR]}" ] || continue 1138 local dep rundeps= 1139 debug= f_getvar _rundeps_$varpkg rundeps 1140 for dep in $rundeps; do 1141 if [ "$dep" = "$name" ]; then 1142 # Maybe this package is marked for deletion too 1143 if [ "$mark" = "U" ]; then 1144 udeps="$udeps $pkgsel" 1145 else 1146 depc=$(( $depc + 1 )) 1147 fi 1148 break 1149 fi 1150 done 1151 done 1152 if [ $depc -gt 0 ]; then 1153 local grammatical_s= 1154 [ $depc -gt 1 ] && grammatical_s=s 1155 f_show_msg \ 1156 "$msg_package_is_needed_by_other_installed_packages" \ 1157 "$name" "$depc" "$grammatical_s" 1158 return $FAILURE 1159 fi 1160 1161 # 1162 # Chase dependencies that are marked for uninstallation 1163 # 1164 for pkgsel in $udeps; do 1165 f_dprintf "%s: Uninstalling dependecy %s (marked for delete)" \ 1166 $fname "$pkgsel" 1167 f_package_delete "$pkgsel" 1168 done 1169 1170 # 1171 # OK to perform the delete (no other packages depend on it)... 1172 # 1173 f_show_info "$msg_uninstalling_package_waiting_for_pkg_delete" "$name" 1174 if f_debugging; then 1175 pkg_delete -v "$name" 1176 else 1177 f_quietly pkg_delete "$name" 1178 fi 1179 if [ $? -ne $SUCCESS ]; then 1180 f_show_msg "$msg_pkg_delete_failed" "$name" 1181 return $FAILURE 1182 else 1183 f_dprintf "%s: pkg_delete(1) of %s successful" $fname "$name" 1184 f_str2varname "$name" varpkg 1185 setvar _mark_$varpkg "" 1186 fi 1187} 1188 1189# f_package_reinstall $name 1190# 1191# A simple wrapper to f_package_delete() + f_package_add() 1192# 1193f_package_reinstall() 1194{ 1195 f_package_delete "$1" && f_package_add "$1" 1196} 1197 1198############################################################ MAIN 1199 1200f_dprintf "%s: Successfully loaded." packages/packages.subr 1201 1202fi # ! $_PACKAGES_PACKAGES_SUBR 1203