5 ######################################################################################
16 aururl="https://aur.archlinux.org/"
19 #-- options (bool) --#
30 makepkg_command="/usr/bin/makepkg"
32 makepkg_config="/etc/makepkg.conf"
38 pacman_command="/usr/bin/pacman"
40 pacman_config="/etc/pacman.conf"
46 git_command="/usr/bin/git"
52 gpg_command="/usr/bin/gpg"
58 sudo_command="/usr/bin/sudo"
63 ######################################################################################
67 # https://github.com/FascodeNet/alterlinux/blob/dev/tools/msg.sh の変数名にアンダーバーを追加し関数化
69 local OPTIND OPTARG arg
71 local _appname="msg.sh"
72 local _bash_debug=false
76 local _msg_type="info"
78 local _label_space="7"
80 local _customized_label=false
81 local _customized_label_color=false
83 local _noappname=false
85 local _output="stdout"
88 echo "usage ${0} [option] [type] [message]"
90 echo "Display a message with a colored app name and message type label"
93 echo " info General message"
94 echo " warn Warning message"
95 echo " error Error message"
96 echo " debug Debug message"
98 echo " General options:"
99 echo " -a [name] Specify the app name"
100 echo " -c [character] Specify the character to adjust the label"
101 echo " -l [label] Specify the label."
102 echo " -n | --nocolor No output colored output"
103 echo " -o [option] Specify echo options"
104 echo " -r [color] Specify the color of label"
105 echo " -s [number] Specifies the label space."
106 echo " -x | --bash-debug Enables output bash debugging"
107 echo " -h | --help This help message"
109 echo " --nolabel Do not output label"
110 echo " --noappname Do not output app name"
111 echo " --noadjust Do not adjust the width of the label"
114 while getopts "a:c:l:no:r:s:xh-:" arg; do
120 _adjust_chr="${OPTARG}"
123 _customized_label=true
124 _msg_label="${OPTARG}"
130 _echo_opts="${OPTARG}"
133 _customized_label_color=true
165 _label_space="${OPTARG}"
206 shift $((OPTIND - 1))
231 # You can specify multiple decorations with ;.
232 # 0 => All attributs off (ノーマル)
234 # 4 => Underscore (下線)
236 # 7 => Reverse video on (色反転)
243 [[ "${_customized_label_color}" = false ]] && _labelcolor="32"
244 [[ "${_customized_label}" = false ]] && _msg_label="Info"
250 [[ "${_customized_label_color}" = false ]] && _labelcolor="33"
251 [[ "${_customized_label}" = false ]] && _msg_label="Warning"
257 [[ "${_customized_label_color}" = false ]] && _labelcolor="35"
258 [[ "${_customized_label}" = false ]] && _msg_label="Debug"
264 [[ "${_customized_label_color}" = false ]] && _labelcolor="31"
265 [[ "${_customized_label}" = false ]] && _msg_label="Error"
269 echo "Please specify the message type" >&2
273 echo "Unknown message type" >&2
278 _word_count="${#_msg_label}"
283 if [[ "${_nolabel}" = false ]]; then
284 if [[ "${_noadjust}" = false ]]; then
285 for __time in $( seq 1 $(( ${_label_space} - ${_word_count})) ); do
286 echo -ne "${_adjust_chr}"
289 if [[ "${_nocolor}" = false ]]; then
290 echo -ne "\e[$([[ -v _backcolor ]] && echo -n "${_backcolor}"; [[ -v _labelcolor ]] && echo -n ";${_labelcolor}"; [[ -v _decotypes ]] && echo -n ";${_decotypes}")m${_msg_label}\e[m "
292 echo -ne "${_msg_label} "
298 if [[ "${_noappname}" = false ]]; then
299 if [[ "${_nocolor}" = false ]]; then
300 echo -ne "\e[36m[${_appname}]\e[m "
302 echo -ne "[${_appname}] "
307 for _count in $(seq "1" "$(echo -ne "${_message}\n" | wc -l)"); do
308 _echo_message=$(echo -ne "${_message}\n" |head -n "${_count}" | tail -n 1 )
309 _full_message="$(echo_appname)$(echo_type)${_echo_message}"
312 echo ${_echo_opts} "${_full_message}" >&1
315 echo ${_echo_opts} "${_full_message}" >&2
318 echo ${_echo_opts} "${_full_message}" > ${_output}
324 # Show an INFO message
327 local _msg_opts="-a ${wfa_name}"
328 if [[ "${1}" = "-n" ]]; then
329 _msg_opts="${_msg_opts} -o -n"
332 [[ "${msgdebug}" = true ]] && _msg_opts="${_msg_opts} -x"
333 [[ "${nocolor}" = true ]] && _msg_opts="${_msg_opts} -n"
334 msg ${_msg_opts} info "${1}"
337 # Show an Warning message
340 local _msg_opts="-a ${wfa_name}"
341 if [[ "${1}" = "-n" ]]; then
342 _msg_opts="${_msg_opts} -o -n"
345 [[ "${msgdebug}" = true ]] && _msg_opts="${_msg_opts} -x"
346 [[ "${nocolor}" = true ]] && _msg_opts="${_msg_opts} -n"
347 msg ${_msg_opts} warn "${1}"
350 # Show an debug message
353 if [[ "${debug}" = true ]]; then
354 local _msg_opts="-a ${wfa_name}"
355 if [[ "${1}" = "-n" ]]; then
356 _msg_opts="${_msg_opts} -o -n"
359 [[ "${msgdebug}" = true ]] && _msg_opts="${_msg_opts} -x"
360 [[ "${nocolor}" = true ]] && _msg_opts="${_msg_opts} -n"
361 msg ${_msg_opts} debug "${1}"
365 # Show an ERROR message then exit with status
367 # $2: exit code number (with 0 does not exit)
369 local _msg_opts="-a ${wfa_name}"
370 if [[ "${1}" = "-n" ]]; then
371 _msg_opts="${_msg_opts} -o -n"
374 [[ "${msgdebug}" = true ]] && _msg_opts="${_msg_opts} -x"
375 [[ "${nocolor}" = true ]] && _msg_opts="${_msg_opts} -n"
376 msg ${_msg_opts} error "${1}"
377 if [[ -n "${2:-}" ]]; then
383 # Delete the file if it exists.
384 # For directories, rm -rf is used.
385 # If the file does not exist, skip it.
386 # remove <file> <file> ...
388 local _list=($(echo "$@")) _file
389 for _file in "${_list[@]}"; do
390 msg_debug "Removing ${_file}"
391 if [[ -f "${_file}" ]]; then
393 elif [[ -d "${_file}" ]]; then
400 local _pacman_help=false
405 echo "${wfa_command}"
406 echo "${wfa_command} <operation> [...]"
409 echo " ${wfa_command} {-h --help}"
410 echo " ${wfa_command} {-V --version}"
411 #echo " ${wfa_command} {-D --database} <options> <package(s)>"
412 #echo " ${wfa_command} {-F --files} [options] [package(s)]"
413 echo " ${wfa_command} {-Q --query} [options] [package(s)]"
414 echo " ${wfa_command} {-R --remove} [options] <package(s)>"
415 echo " ${wfa_command} {-S --sync} [options] [package(s)]"
416 #echo " ${wfa_command} {-T --deptest} [options] [package(s)]"
417 #echo " ${wfa_command} {-U --upgrade} [options] <file(s)>"
419 #echo "New operations:"
420 #echo " ${wfa_command} {-P --show} [options]"
421 #echo " ${wfa_command} {-G --getpkgbuild} [package(s)]"
424 echo " --repo Assume targets are from the repositories"
425 echo " -a --aur Assume targets are from the AUR"
427 echo "Permanent configuration options:"
428 echo " --aururl <url> Set an alternative AUR URL"
429 echo " --makepkg <file> makepkg command to use"
430 echo " --mflags <flags> Pass arguments to makepkg"
431 echo " --pacman <file> pacman command to use"
432 echo " --git <file> git command to use"
433 echo " --gitflags <flags> Pass arguments to git"
434 echo " --gpg <file> gpg command to use"
435 echo " --gpgflags <flags> Pass arguments to gpg"
436 echo " --config <file> pacman.conf file to use"
437 echo " --makepkgconf <file> makepkg.conf file to use"
438 echo " --nomakepkgconf Use the default makepkg.conf"
442 local _wfa_usage_sync
444 echo "usage: ${wfa_command} {-S --sync} [options] [package(s)]"
446 echo " -b, --dbpath <path> set an alternate database location"
447 echo " --config <path> set an alternate configuration file"
448 echo " --noconfirm do not ask for any confirmation"
452 if [[ "${operation}" = "none" ]]; then
454 elif [[ "${_pacman_help}" = true ]]; then
455 "${pacman_command}" -h --${operation}
456 elif [[ "$(type -t "_wfa_usage_${operation}" )" = "function" ]]; then
457 _wfa_usage_${operation}
459 msg_error "Undefined operation." 1
464 if [[ "${operation}" = "none" ]]; then
466 add_args pacman "--${operation}"
468 msg_error "only one operation may be used at a time" 1
473 if (( ${UID} == 0 )); then
481 run_sudo "${pacman_command}" ${@}
485 # https://github.com/FascodeNet/aptpac/blob/master/aptpac のADD_OPTION関数を参考
486 # Usage: add_args [pacman/makepkg] <args1> <args2>...
494 _args_array=(${makepkg_args})
496 makepkg_args=${_args_array[@]}
497 msg_debug "makepkg ARGS: ${makepkg_args}"
501 _args_array=(${pacman_args})
503 pacman_args=${_args_array[@]}
504 msg_debug "pacman ARGS: ${pacman_args}"
507 _args_array=(${mpg_args})
509 git_args=${_args_array[@]}
510 msg_debug "git ARGS: ${git_args}"
513 _args_array=(${gpg_args})
515 gpg_args=${_args_array[@]}
516 msg_debug "gpg ARGS: ${gpg_args}"
519 _args_array=(${sudo_args})
521 sudo_args=${_args_array[@]}
522 msg_debug "sudo ARGS: ${sudo_args}"
525 msg_error "Failed to set the argument of ${_target}\nSetting that command is not currently supported." 1
530 # 引数で指定されたパッケージがAUR以外の場所に存在しない場合にのみ正常終了します(AURのパッケージの場合に正常終了)
531 check_aur_package() {
532 local _package="${1}"
533 # 参考: https://qiita.com/Hayao0819/items/a8740a17301fafa2fdab
534 if [[ -z "$(pacman -Ssq "${_package}" 2>/dev/null | grep -o ".*${_package}$")" ]]; then
543 # 引数で指定されたパッケージが既にインストールされている場合は正常終了します。
544 check_installed_package() {
545 local _package="${1}"
546 if "${pacman_command}" -Qq "${_package}" > /dev/null 2>&1; then
554 local _user_config_dir
555 if [[ -v XDG_CONFIG_HOME ]]; then
556 _user_config_dir="${XDG_CONFIG_HOME}"
558 _use_config_dir="${HOME}/.config"
560 if [[ -f "${_use_config_dir}/user-dirs.dirs" ]]; then
561 source "${_use_config_dir}/user-dirs.dirs"
563 if [[ -v XDG_CACHE_HOME ]]; then
564 echo -n "${XDG_CACHE_HOME}"
567 echo -n "${HOME}/.cache"
572 # Usage: get_srcinfo_data <path> <var>
573 # 参考: https://qiita.com/withelmo/items/b0e1ffba639dd3ae18c0
575 local _srcinfo="${1}" _ver="${2}"
576 local _srcinfo_json=$(python << EOF
577 from srcinfo.parse import parse_srcinfo; import json
581 parsed, errors = parse_srcinfo(text)
582 print(json.dumps(parsed))
585 echo "${_srcinfo_json}" | jq -rc "${2}" | tr '\n' ' '
589 # AURからパッケージをビルドしてインストールします
590 # 現在1つのパッケージしか指定できません
591 install_aur_package() {
592 local _package="${1}"
595 if [[ ! -v wfa_cache_dir ]]; then
596 wfa_cache_dir="$(get_cache_dir)/wfa"
598 mkdir -p "${wfa_cache_dir}/archive"
599 mkdir -p "${wfa_cache_dir}/build/${_package}"
602 local _aur_json=$(curl -sL "https://aur.archlinux.org/rpc/?v=5&type=search&by=name&arg=${_package}" | jq -r)
603 if (( "$(echo "${_aur_json}" | jq -r ".resultcount")" == 0 )); then
604 msg_error "Could not find all required packages:\n ${_package}" 1
607 local _found_packages="$(echo "${_aur_json}" | jq -r --tab '.results[].Name')"
608 #msg_debug "Found package: $(echo ${_found_packages} | tr '\n' ' ')"
610 if [[ -n "$(echo "${_found_packages}" | grep -x "${_package}" )" ]]; then
611 msg_debug "Select a package ${_package} with an exact name match"
613 msg_error "No package with an exact name match was found." 1
617 msg_info "Download PKGBUILD of ${_package}"
618 _aur_json=$(echo "${_aur_json}" | jq -r ".results[] | select(.Name == \"${_package}\")" )
619 local _aur_snapshot_url="${aururl%/}$(echo "${_aur_json}" | jq -r ".URLPath")"
620 local _aur_version="$(echo "${_aur_json}" | jq -r ".Version")"
621 msg_debug "Get PKGBUILD from ${_aur_snapshot_url}"
623 local _pkgbuild_archive_path="${wfa_cache_dir}/archive/${_package}-${_aur_version}"
624 local _download_pkgbuild=true
625 if [[ -f "${_pkgbuild_archive_path}" ]]; then
626 msg_warn "PKGBUILD has already been downloaded."
627 msg_warn -n "Do you want to overwrite and download? [n] :"
629 if [[ "${noconfirm}" = true ]]; then
635 case "${_yes_or_no}" in
636 "y" | "Y" | "yes" | "Yes" | "YES" ) _download_pkgbuild=true ;;
637 * ) _download_pkgbuild=false ;;
640 if [[ "${_download_pkgbuild}" = true ]]; then
641 remove "${_pkgbuild_archive_path}"
642 curl -L -C - -f -o "${_pkgbuild_archive_path}" "${_aur_snapshot_url}"
646 msg_info "Unpacking the tarball of PKGBUILD ..."
647 tar -xv -f "${_pkgbuild_archive_path}" -C "${wfa_cache_dir}/build/" > /dev/null 2>&1
650 local _build_dir="${wfa_cache_dir}/build/${_package}"
651 if [[ ! -f "${_build_dir}/.SRCINFO" ]]; then
652 msg_warn ".SRCINFO was not found.\nGenerate it using makepkg."
655 "${makepkg_command}" --printsrcinfo > "${_build_dir}/.SRCINFO"
659 local _makedepends="$(get_srcinfo_data "${_build_dir}/.SRCINFO" ".makedepends[]?")"
660 local _depends="$(get_srcinfo_data "${_build_dir}/.SRCINFO" ".depends[]?")"
661 local _conflicts="$(get_srcinfo_data "${_build_dir}/.SRCINFO" ".conflicts[]?")"
662 msg_debug "makedepends: ${_makedepends}"
663 msg_debug "depends: ${_depends}"
664 msg_debug "conflicts: ${_conflicts}"
668 local _pkg _conflicts_found=false
669 for _pkg in ${_conflicts[@]}; do
670 if "${pacman_command}" -Qq "${_pkg}" > /dev/null 2>&1 ; then
671 _conflicts_found=true
672 msg_error "${_package} is colliding with ${_pkg}"
675 if "${pacman_command}" -Qq "${_package}" > /dev/null 2>&1 && [[ ! "$("${pacman_command}" -Qq "${_package}" 2> /dev/null)" = "${_package}" ]]; then
676 msg_error "${_package} is colliding with $("${pacman_command}" -Qq "${_package}")"
677 _conflicts_found=true
679 if [[ "${_conflicts_found}" = true ]]; then
680 msg_error "A conflict was found." 1
685 if [[ "${nodeps}" = false ]]; then
686 msg_info "Install dependent packages..."
687 local _force_aur="${force_aur}"
689 install_package "${_depends}"
690 force_aur="${_force_aur}"
696 if [[ -d "${_build_dir}/src" ]]; then
697 msg_info "Found ${_build_dir}/src"
698 msg_info "Packages to cleanBuild? [n] :"
701 if [[ "${noconfirm}" = true ]]; then
707 case "${_yes_or_no}" in
708 "y" | "Y" | "yes" | "Yes" | "YES" ) add_args makepkg "--clean" ;;
714 add_args "makepkg" "-sf"
717 "${makepkg_command}" "${makepkg_args}"
724 "${makepkg_command}" --printsrcinfo > "${_build_dir}/.SRCINFO"
726 local _pkgnames=($(get_srcinfo_data "${_build_dir}/.SRCINFO" ".packages | keys[]" | sed 's/ //g'))
727 local _pkgver="$(get_srcinfo_data "${_build_dir}/.SRCINFO" ".pkgver" | sed 's/ //g')"
728 local _pkgrel="$(get_srcinfo_data "${_build_dir}/.SRCINFO" ".pkgrel" | sed 's/ //g')"
729 local _arch_array=($(get_srcinfo_data "${_build_dir}/.SRCINFO" ".arch[]"))
731 if [[ "${_arch_array[*]}" = "any" ]]; then
737 source "${makepkg_config}"
740 local _pkgfilelist=()
741 for _pkgname in ${_pkgnames[@]}; do
742 _pkgfilelist+=("${_build_dir}/${_pkgname}-${_pkgver}-${_pkgrel}-${_arch}${_PKGEXT}")
746 run_pacman -U --noconfirm ${_pkgfilelist[@]}
750 operation_version() {
751 # Pyalpmからlibalpmの値を取得
752 # 参考: https://pyalpm.readthedocs.io/en/latest/pyalpm/pyalpm.html
753 local _libalpm_version="$(python3 -c 'import pyalpm; print(pyalpm.alpmversion())')"
754 local _pacman_version="$("${pacman_command}" -Q pacman | cut -d ' ' -f 2)"
755 echo "wfa v${wfa_version} - pacman v${_pacman_version} - libalpm v${_libalpm_version}"
759 run_pacman ${pacman_args} "${specified_packages[@]}"
762 # Usage: install_package <package1> <package2>...
765 for _package in ${@}; do
766 if ! check_installed_package "${_package}"; then
767 if ! check_aur_package "${_package}"; then
768 # 公式パッケージなのでpacmanでそのままインストール
769 run_pacman ${pacman_args} "${_package}"
772 install_aur_package "${_package}"
773 #msg_error "Getting the AUR package has not been implemented yet." 1
781 for _package in ${specified_packages[@]}; do
782 if ! check_aur_package "${_package}" && [[ "${force_aur}" = false ]]; then
783 # 公式パッケージなのでpacmanでそのままインストール
784 run_pacman ${pacman_args} "${_package}"
787 install_aur_package "${_package}"
788 #msg_error "Getting the AUR package has not been implemented yet." 1
796 _opt_short="QRShVdb:ay"
797 _opt_long="query,remove,sync,help,version,debug,dbpath:,aururl,aur,noconfirm,config:,makepkg:,mflags:,pacman:,git:,gitflags:,gpg:,gpgflags:,makepkgconf:,nomakepkgconf,nodeps,refresh"
799 OPT=$(getopt -o ${_opt_short} -l ${_opt_long} -- ${ARGUMENT})
800 [[ ${?} != 0 ]] && exit 1
801 unset _opt_short _opt_long
804 msg_debug "Argument: ${OPT}"
809 set_operation "query"
813 set_operation "remove"
821 set_operation "version"
840 msg_debug "Assume targets are from the AUR"
845 add_args pacman "--debug"
850 add_args pacman "--nodeps"
854 add_args pacman "--dbpath '${2}'"
858 option_y_count=$(( option_y_count + 1 ))
866 add_args pacman "--noconfirm"
872 add_args pacman "--config \"${2}\""
876 makepkg_command="${2}"
884 pacman_command="${2}"
896 if [[ "${nomakepkgconf}" = false ]]; then
897 makepkg_config="${2}"
899 msg_warn "--nomakepkgconf is specified.\n--makepkgconf has been ignored."
904 makepkg_config="/etc/makepkg.conf"
923 specified_packages=(${@})
925 # Run database update
926 if (( "${option_y_count}" == 1 )); then
928 elif (( "${option_y_count}" >= 2 )); then
932 case "${operation}" in
946 msg_error "Undefined operation." 1