OSDN Git Service

[fix] : Fixed package string when building multiple packages
[alterlinux/wfa.git] / wfa
1 #!/usr/bin/env bash
2 #
3 # 1. Author info
4 #
5 # Yamada Hayao
6 # Twitter: @Hayao0819
7 # Email  : hayao@fascode.net
8 #
9 # (c) 2019-2020 Fascode Network.
10 #
11 # 2. Overview
12
13 # Wfa is a multilingual AUR helper written in bash that is being developed to replace yaourt
14 #
15 # 3. License
16
17 #        DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 
18 #                    Version 2, December 2004 
19 #
20 # Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> 
21 #
22 # Everyone is permitted to copy and distribute verbatim or modified 
23 # copies of this license document, and changing it is allowed as long 
24 # as the name is changed. 
25 #
26 #            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 
27 #   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 
28 #
29 #  0. You just DO WHAT THE FUCK YOU WANT TO.
30 #
31 #
32
33 set -eu
34
35
36 ######################################################################################
37 # ここから翻訳データ
38
39 declare -A ja_JP=(
40     ["Undefined operation"]="未定義のオペレーションです"
41     ["only one operation may be used at a time"]="一度に使用できるオペレーションは1つだけです"
42     ["Failed to set the argument of %s"]="%sの引数の設定に失敗しました"
43     ["Setting that command is not currently supported"]="そのコマンドの設定は現在サポートされていません"
44     ["Searching in AUR ..."]="AUR内を検索しています..."
45     ["No package with an exact name match was found"]="完全に一致する名前のパッケージが見つかりませんでした"
46     ["Select a package %s with an exact name match"]="名前が完全に一致するパッケージ %s を選択します"
47     ["Download PKGBUILD of %s"]="%s のPKGBUILDをダウンロード"
48     ["Get PKGBUILD from %s"]="%sからPKGBUILDを取得します"
49     ["PKGBUILD has already been downloaded"]="PKGBUILDは既にダウンロードされています"
50     ["Do you want to overwrite and download? [n] :"]="上書きダウンロードしますか? [n] :"
51     ["Unpacking the tarball of PKGBUILD ..."]="PKGBUILDを展開しています ..."
52     [".SRCINFO was not found.\nGenerating it using makepkg"]=".SCRINFOが見つかりませんでした。makepkgを使用して生成しています"
53     ["Conflict(s) was found"]="衝突が見つかりました"
54     ["Install dependent packages..."]="依存パッケージをインストールします..."
55     ["Found %s"]="%s を見つけました"
56     ["Packages to cleanBuild? [n] :"]="パッケージをクリーンビルドしますか? [n] :"
57     ["Could not find all required packages:\n   %s"]="必要なすべてのパッケージが見つかりませんでした     %s"
58     ["This is a feature that has not been implemented yet"]="まだ実装されていない機能です"
59
60     ["(Orphaned)"]="(メンテナ不在)"
61     ["[Installed]"]="[インストール済み]"
62     ["This is a feature that has not been implemented yet"]="まだ実装されていない機能です"
63 )
64 ######################################################################################
65 # ここからデフォルト設定の定義
66
67 #-- wfa configs --#
68 wfa_version="0.1"
69 wfa_name="WFA"
70 wfa_command="wfa"
71
72 #-- options (int) --#
73 option_y_count=0
74 sync_clean_count=0
75
76 #-- options (str) --#
77 aururl="https://aur.archlinux.org/"
78 operation="none"
79
80 #-- options (bool) --#
81 bash_debug=false
82 debug=false
83 force_aur=false
84 msgdebug=false
85 nocolor=false
86 noconfirm=false
87 nodeps=false
88 nomakepkgconf=false
89 sync_search=false
90 sync_upgrade=false
91 quiet=false
92
93 #-- makepkg --#
94 # 実行ファイル
95 makepkg_command="/usr/bin/makepkg" 
96 # 設定ファイル
97 makepkg_config="/etc/makepkg.conf"
98 # 引数
99 makepkg_args=""
100
101 #-- pacman --#
102 # 実行ファイル
103 pacman_command="/usr/bin/pacman"
104 # 設定ファイル
105 pacman_config="/etc/pacman.conf"
106 # 引数
107 pacman_args=""
108
109 #-- git --#
110 # 実行ファイル
111 git_command="/usr/bin/git"
112 # 引数
113 git_args=""
114
115 #-- gpg --#
116 # 実行ファイル
117 gpg_command="/usr/bin/gpg"
118 # 引数
119 gpg_args=""
120
121 #-- sudo --#
122 # 実行ファイル
123 sudo_command="/usr/bin/sudo"
124 # 引数
125 sudo_args=""
126
127
128
129 ######################################################################################
130 # ここからメッセージ関連の関数定義
131
132
133 # メッセージ出力の制御
134 # https://github.com/FascodeNet/alterlinux/blob/dev/tools/msg.sh の変数名にアンダーバーを追加し関数化
135 msg() {
136     local OPTIND OPTARG arg
137
138     local _appname="msg.sh"
139     local _bash_debug=false
140     local _nocolor=false
141     local _echo_opts=""
142     local _message=""
143     local _msg_type="info"
144     local _msg_label=""
145     local _label_space="7"
146     local _adjust_chr=" "
147     local _customized_label=false
148     local _customized_label_color=false
149     local _nolabel=false
150     local _noappname=false
151     local _noadjust=false
152     local _output="stdout"
153
154     _help() {
155         echo "usage msg [option] [type] [message]"
156         echo
157         echo "Display a message with a colored app name and message type label"
158         echo
159         echo " General type:"
160         echo "    info                      General message"
161         echo "    warn                      Warning message"
162         echo "    error                     Error message"
163         echo "    debug                     Debug message"
164         echo
165         echo " General options:"
166         echo "    -a [name]                 Specify the app name"
167         echo "    -c [character]            Specify the character to adjust the label"
168         echo "    -l [label]                Specify the label."
169         echo "    -n | --nocolor            No output colored output"
170         echo "    -o [option]               Specify echo options"
171         echo "    -r [color]                Specify the color of label"
172         echo "    -s [number]               Specifies the label space."
173         echo "    -x | --bash-debug         Enables output bash debugging"
174         echo "    -h | --help               This help message"
175         echo
176         echo "         --nolabel            Do not output label"
177         echo "         --noappname          Do not output app name"
178         echo "         --noadjust           Do not adjust the width of the label"
179     }
180
181     while getopts "a:c:l:no:r:s:xh-:" arg; do
182         case ${arg} in
183                 a) 
184                     _appname="${OPTARG}"
185                     ;;
186                 c) 
187                     _adjust_chr="${OPTARG}"
188                     ;;
189                 l) 
190                     _customized_label=true
191                     _msg_label="${OPTARG}"
192                     ;;
193                 n)
194                     _nocolor=true
195                     ;;
196                 o)
197                     _echo_opts="${OPTARG}"
198                     ;;
199                 r)
200                     _customized_label_color=true
201                     case ${OPTARG} in
202                         "black")
203                             _labelcolor="30"
204                             ;;
205                         "red")
206                             _labelcolor="31"
207                             ;;
208                         "green")
209                             _labelcolor="32"
210                             ;;
211                         "yellow")
212                             _labelcolor="33"
213                             ;;
214                         "blue")
215                             _labelcolor="34"
216                             ;;
217                         "magenta")
218                             _labelcolor="35"
219                             ;;
220                         "cyan")
221                             _labelcolor="36"
222                             ;;
223                         "white")
224                             _labelcolor="37"
225                             ;;
226                         *)
227                             return 1
228                             ;;
229                     esac
230                     ;;
231                 s)
232                     _label_space="${OPTARG}"
233                     ;;
234                 x)
235                     _bash_debug=true
236                     set -xv
237                     ;;
238                 h)
239                     _help
240                     shift 1
241                     exit 0
242                     ;;
243                 -)
244                     case "${OPTARG}" in
245                         "nocolor")
246                             _nocolor=true
247                             ;;
248                         "bash-debug")
249                             _bash_debug=true
250                             set -xv
251                             ;;
252                         "help") 
253                             _help
254                             exit 0
255                             ;;
256                         "nolabel")
257                             _nolabel=true
258                             ;;
259                         "noappname")
260                             _noappname=true
261                             ;;
262                         "noadjust")
263                             _noadjust=true 
264                             ;;
265                         *)
266                             _help
267                             exit 1
268                             ;;
269                     esac
270         esac
271     done
272
273     shift $((OPTIND - 1))
274
275     # Color echo
276     #
277     # Text Color
278     # 30 => Black
279     # 31 => Red
280     # 32 => Green
281     # 33 => Yellow
282     # 34 => Blue
283     # 35 => Magenta
284     # 36 => Cyan
285     # 37 => White
286     #
287     # Background color
288     # 40 => Black
289     # 41 => Red
290     # 42 => Green
291     # 43 => Yellow
292     # 44 => Blue
293     # 45 => Magenta
294     # 46 => Cyan
295     # 47 => White
296     #
297     # Text decoration
298     # You can specify multiple decorations with ;.
299     # 0 => All attributs off (ノーマル)
300     # 1 => Bold on (太字)
301     # 4 => Underscore (下線)
302     # 5 => Blink on (点滅)
303     # 7 => Reverse video on (色反転)
304     # 8 => Concealed on
305
306     case ${1} in
307         "info")
308             _msg_type="type"
309             _output="stdout"
310             [[ "${_customized_label_color}" = false ]] && _labelcolor="32"
311             [[ "${_customized_label}"       = false ]] && _msg_label="Info"
312             shift 1
313             ;;
314         "warn")
315             _msg_type="warn"
316             _output="stdout"
317             [[ "${_customized_label_color}" = false ]] && _labelcolor="33"
318             [[ "${_customized_label}"       = false ]] && _msg_label="Warning"
319             shift 1
320             ;;
321         "debug")
322             _msg_type="debug"
323             _output="stdout"
324             [[ "${_customized_label_color}" = false ]] && _labelcolor="35"
325             [[ "${_customized_label}"       = false ]] && _msg_label="Debug"
326             shift 1
327             ;;
328         "error")
329             _msg_type="error"
330             _output="stderr"
331             [[ "${_customized_label_color}" = false ]] && _labelcolor="31"
332             [[ "${_customized_label}"       = false ]] && _msg_label="Error"
333             shift 1
334             ;;
335         "")
336             echo "Please specify the message type" >&2
337             exit 1
338             ;;
339         *)
340             echo "Unknown message type" >&2
341             exit 1
342             ;;
343     esac
344
345     _word_count="${#_msg_label}"
346     _message="${@}"
347
348     echo_type() {
349         local __time
350         if [[ "${_nolabel}" = false ]]; then
351             if [[ "${_noadjust}" = false ]]; then
352                 for __time in $( seq 1 $(( ${_label_space} - ${_word_count})) ); do
353                     echo -ne "${_adjust_chr}"
354                 done
355             fi
356             if [[ "${_nocolor}" = false ]]; then
357                 echo -ne "\e[$([[ -v _backcolor ]] && echo -n "${_backcolor}"; [[ -v _labelcolor ]] && echo -n ";${_labelcolor}"; [[ -v _decotypes ]] && echo -n ";${_decotypes}")m${_msg_label}\e[m "
358             else
359                 echo -ne "${_msg_label} "
360             fi
361         fi
362     }
363
364     echo_appname() {
365         if [[ "${_noappname}" = false ]]; then
366             if [[ "${_nocolor}" = false ]]; then
367                 echo -ne "\e[36m[${_appname}]\e[m "
368             else
369                 echo -ne "[${_appname}] "
370             fi
371         fi
372     }
373
374     for _count in $(seq "1" "$(echo -ne "${_message}\n" | wc -l)"); do
375         _echo_message=$(echo -ne "${_message}\n" |head -n "${_count}" | tail -n 1 )
376         _full_message="$(echo_appname)$(echo_type)${_echo_message}"
377         case "${_output}" in
378             "stdout")
379                 echo ${_echo_opts} "${_full_message}" >&1
380                 ;;
381             "stderr")
382                 echo ${_echo_opts} "${_full_message}" >&2
383                 ;;
384             *)
385                 echo ${_echo_opts} "${_full_message}" > ${_output}
386                 ;;
387         esac
388     done
389 }
390
391 # テキストの翻訳
392 #set -xv
393 translate() {
394     local _msg_translate
395     _msg_translate() {
396         local _get_text
397         local _locale="$(echo "${LANG}" | cut -d "." -f 1)"
398
399         _get_text() {
400             set +eu
401             local _translated_text="$(eval echo '$'{${_locale}[\"${*}\"]})"
402             set -eu
403             if [[ -z "${_translated_text}" ]]; then
404                 echo "${*}"
405             else
406                 echo "${_translated_text}"
407             fi
408         }
409
410         local _text _fulltext=() _main
411
412         if declare -p "${_locale}" 2> /dev/null 1>/dev/null; then
413             _main="$(_get_text ${1})"
414         else
415             _main="${1}"
416         fi
417         shift 1
418         echo "$(printf "${_main}" "${@}")"
419     }
420     _msg_translate "${@}"
421 }
422
423
424
425 # Show an INFO message
426 # $1: message string
427 msg_info() {
428     if [[ "${msgdebug}" = false ]]; then
429         set +xv
430     fi
431     local _msg_opts="-a ${wfa_name}"
432     if [[ "${1}" = "-n" ]]; then
433         _msg_opts="${_msg_opts} -o -n"
434         shift 1
435     fi
436     [[ "${msgdebug}" = true ]] && _msg_opts="${_msg_opts} -x"
437     [[ "${nocolor}"  = true ]] && _msg_opts="${_msg_opts} -n"
438     msg ${_msg_opts} info "$(translate "${@}")"
439     if [[ "${bash_debug}" = true ]]; then
440         set -xv
441     fi
442 }
443
444 # Show an Warning message
445 # $1: message string
446 msg_warn() {
447     if [[ "${msgdebug}" = false ]]; then
448         set +xv
449     fi
450     local _msg_opts="-a ${wfa_name}"
451     if [[ "${1}" = "-n" ]]; then
452         _msg_opts="${_msg_opts} -o -n"
453         shift 1
454     fi
455     [[ "${msgdebug}" = true ]] && _msg_opts="${_msg_opts} -x"
456     [[ "${nocolor}"  = true ]] && _msg_opts="${_msg_opts} -n"
457     msg ${_msg_opts} warn "$(translate "${@}")"
458     if [[ "${bash_debug}" = true ]]; then
459         set -xv
460     fi
461 }
462
463 # Show an debug message
464 # $1: message string
465 msg_debug() {
466     if [[ "${msgdebug}" = false ]]; then
467         set +xv
468     fi
469     if [[ "${debug}" = true ]]; then
470         local _msg_opts="-a ${wfa_name}"
471         if [[ "${1}" = "-n" ]]; then
472             _msg_opts="${_msg_opts} -o -n"
473             shift 1
474         fi
475         [[ "${msgdebug}" = true ]] && _msg_opts="${_msg_opts} -x"
476         [[ "${nocolor}"  = true ]] && _msg_opts="${_msg_opts} -n"
477         msg ${_msg_opts} debug "$(translate "${@}")"
478     fi
479     if [[ "${bash_debug}" = true ]]; then
480         set -xv
481     fi
482 }
483
484 # Show an ERROR message then exit with status
485 # $1: message string
486 # $2: exit code number (with 0 does not exit)
487 msg_error() {
488     if [[ "${msgdebug}" = false ]]; then
489         set +xv
490     fi
491     local _msg_opts="-a ${wfa_name}"
492     if [[ "${1}" = "-n" ]]; then
493         _msg_opts="${_msg_opts} -o -n"
494         shift 1
495     fi
496     [[ "${msgdebug}" = true ]] && _msg_opts="${_msg_opts} -x"
497     [[ "${nocolor}"  = true ]] && _msg_opts="${_msg_opts} -n"
498     msg ${_msg_opts} error "$(translate "${@}")"
499     if [[ "${bash_debug}" = true ]]; then
500         set -xv
501     fi
502 }
503
504 text() {
505     local OPTIND OPTARG arg _textcolor _decotypes=() _message
506     while getopts "c:b" arg; do
507         case ${arg} in
508             c)
509                 case "${OPTARG}" in
510                     "black")
511                         _textcolor="30"
512                         ;;
513                     "red")
514                         _textcolor="31"
515                         ;;
516                     "green")
517                         _textcolor="32"
518                         ;;
519                     "yellow")
520                         _textcolor="33"
521                         ;;
522                     "blue")
523                         _textcolor="34"
524                         ;;
525                     "magenta")
526                         _textcolor="35"
527                         ;;
528                     "cyan")
529                         _textcolor="36"
530                         ;;
531                     "white")
532                         _textcolor="37"
533                         ;;
534                     *)
535                         return 1
536                         ;;
537                 esac
538                 ;;
539             b)
540                 _decotypes+=(1)
541                 ;;
542         esac
543     done
544     shift $((OPTIND - 1))
545
546     _message="${@}"
547     _message="$(translate "${@}")"
548     if [[ "${nocolor}" = true ]]; then
549         echo -ne "${@}"
550     else
551         echo -ne "\e[$([[ -v _textcolor ]] && echo -n ";${_textcolor}"; [[ -v _decotypes ]] && echo -n ";${_decotypes}")m${_message}\e[m"
552     fi
553 }
554
555 ######################################################################################
556 # ここから実際の処理開始
557 # ここから下のメッセージは翻訳可能です
558
559 # rm helper
560 # Delete the file if it exists.
561 # For directories, rm -rf is used.
562 # If the file does not exist, skip it.
563 # remove <file> <file> ...
564 remove() {
565     local _list=($(echo "$@")) _file
566     for _file in "${_list[@]}"; do
567         msg_debug "Removing ${_file}"
568         if [[ -f "${_file}" ]]; then    
569             rm -f "${_file}"
570         elif [[ -d "${_file}" ]]; then
571             rm -rf "${_file}"
572         fi
573     done
574 }
575
576 usage (){
577     local _pacman_help=false
578
579     local _wfa_usage
580     _wfa_usage() {
581         echo "Usage:"
582         echo "${wfa_command}"
583         echo "${wfa_command} <operation> [...]"
584         echo
585         echo "operations:"
586         echo "    ${wfa_command} {-h --help}"
587         echo "    ${wfa_command} {-V --version}"
588        #echo "    ${wfa_command} {-D --database}    <options> <package(s)>"
589        #echo "    ${wfa_command} {-F --files}       [options] [package(s)]"
590         echo "    ${wfa_command} {-Q --query}       [options] [package(s)]"
591         echo "    ${wfa_command} {-R --remove}      [options] <package(s)>"
592         echo "    ${wfa_command} {-S --sync}        [options] [package(s)]"
593        #echo "    ${wfa_command} {-T --deptest}     [options] [package(s)]"
594        #echo "    ${wfa_command} {-U --upgrade}     [options] <file(s)>"
595        #echo
596        #echo "New operations:"
597        #echo "    ${wfa_command} {-P --show}        [options]"
598        #echo "    ${wfa_command} {-G --getpkgbuild} [package(s)]"
599         echo
600         echo "New options:"
601         echo "       --repo             Assume targets are from the repositories"
602         echo "    -a --aur              Assume targets are from the AUR"
603         echo
604         echo "Permanent configuration options:"
605         echo "    --aururl      <url>   Set an alternative AUR URL"
606         echo "    --makepkg     <file>  makepkg command to use"
607         echo "    --mflags      <flags> Pass arguments to makepkg"
608         echo "    --pacman      <file>  pacman command to use"
609         echo "    --git         <file>  git command to use"
610         echo "    --gitflags    <flags> Pass arguments to git"
611         echo "    --gpg         <file>  gpg command to use"
612         echo "    --gpgflags    <flags> Pass arguments to gpg"
613         echo "    --config      <file>  pacman.conf file to use"
614         echo "    --makepkgconf <file>  makepkg.conf file to use"
615         echo "    --nomakepkgconf       Use the default makepkg.conf"
616         echo
617     }
618
619     local _wfa_usage_sync
620     _wfa_usage_sync() {
621         echo "usage:  ${wfa_command} {-S --sync} [options] [package(s)]"
622         echo "options:"
623         echo "  -b, --dbpath <path>  set an alternate database location"
624         echo "  -c, --clean          remove old packages from cache directory (-cc for all)"
625         echo "  -d, --nodeps         skip dependency version checks (-dd to skip all checks)"
626         echo "  -s, --search <regex> search remote repositories for matching strings"
627         echo "  -u, --sysupgrade     upgrade installed packages (-uu enables downgrades)"
628         echo "  -y, --refresh        download fresh package databases from the server"
629         echo "                       (-yy to force a refresh even if up to date)"
630         echo
631         echo "      --color <when>   colorize the output"
632         echo "      --config <path>  set an alternate configuration file"
633         echo "      --noconfirm      do not ask for any confirmation"
634         echo "      --debug          display debug messages"
635         echo "      --noconfirm      do not ask for any confirmation"
636
637     }
638
639     if [[ "${operation}" = "none" ]]; then
640         _wfa_usage
641     elif [[ "${_pacman_help}" = true ]]; then
642         "${pacman_command}" -h --${operation}
643     elif [[ "$(type -t "_wfa_usage_${operation}" )" = "function" ]]; then
644         _wfa_usage_${operation}
645     else
646         msg_error "Undefined operation"
647         exit 1
648     fi
649 }
650
651 set_operation() {
652     if [[ "${operation}" = "none" ]]; then
653         operation="${1}"
654         add_args pacman "--${operation}"
655     else
656         msg_error "only one operation may be used at a time"
657         exit 1
658     fi
659 }
660
661 run_sudo() {
662     if (( ${UID} == 0 )); then
663         ${@}
664     else
665         sudo ${@}
666     fi
667 }
668
669 run_pacman() {
670     run_sudo "${pacman_command}" ${@}
671 }
672
673 # pacmanの引数を追加する
674 # https://github.com/FascodeNet/aptpac/blob/master/aptpac のADD_OPTION関数を参考
675 # Usage: add_args [pacman/makepkg] <args1> <args2>...
676 add_args() {
677     local _target="${1}"
678     local _args_array
679     shift 1
680
681     case "${_target}" in
682         "makepkg")
683             _args_array=(${makepkg_args})
684             _args_array+=(${@})
685             makepkg_args=${_args_array[@]}
686             msg_debug "makepkg ARGS: ${makepkg_args}"
687             ;;
688
689         "pacman")
690             _args_array=(${pacman_args})
691             _args_array+=(${@})
692             pacman_args=${_args_array[@]}
693             msg_debug "pacman ARGS: ${pacman_args}"
694             ;;
695         "git")
696             _args_array=(${mpg_args})
697             _args_array+=(${@})
698             git_args=${_args_array[@]}
699             msg_debug "git ARGS: ${git_args}"
700             ;;
701         "gpg")
702             _args_array=(${gpg_args})
703             _args_array+=(${@})
704             gpg_args=${_args_array[@]}
705             msg_debug "gpg ARGS: ${gpg_args}"
706             ;;
707         "sudo")
708             _args_array=(${sudo_args})
709             _args_array+=(${@})
710             sudo_args=${_args_array[@]}
711             msg_debug "sudo ARGS: ${sudo_args}"
712             ;;
713         *)
714             msg_error "Failed to set the argument of %s" "${_target}"
715             msg_error "Setting that command is not currently supported"
716             exit 1
717             ;;
718     esac
719 }
720
721 # 引数で指定されたパッケージがAUR以外の場所に存在しない場合にのみ正常終了します(AURのパッケージの場合に正常終了)
722 check_aur_package() {
723     local _package="${1}"
724     # 参考: https://qiita.com/Hayao0819/items/a8740a17301fafa2fdab
725     if [[ -z "$(pacman -Ssq "${_package}" 2>/dev/null | grep -o ".*${_package}$")" ]]; then
726         #AUR以外のリポジトリに存在しない
727         return 0
728     else
729         return 1
730     fi
731 }
732
733
734 # 引数で指定されたパッケージが既にインストールされている場合は正常終了します。
735 check_installed_package() {
736     local _package="${1}"
737     if "${pacman_command}" -Qq "${_package}" > /dev/null 2>&1; then
738         return 0
739     else
740         return 1
741     fi
742 }
743
744 # ~/.cacheに相当するディレクトリを返します
745 get_cache_dir() {
746     local _user_config_dir _cache_dir
747     if [[ -v XDG_CONFIG_HOME ]]; then
748         _user_config_dir="${XDG_CONFIG_HOME}"
749     else
750         _use_config_dir="${HOME}/.config"
751     fi
752     if [[ -f "${_use_config_dir}/user-dirs.dirs" ]]; then
753         source "${_use_config_dir}/user-dirs.dirs"
754     fi
755     if [[ -v XDG_CACHE_HOME ]]; then
756         _cache_dir="${XDG_CACHE_HOME}"
757     else
758         _cache_dir="${HOME}/.cache"
759     fi
760     echo -n "${_cache_dir}"
761 }
762
763 # Usage: get_srcinfo_data <path> <var>
764 # 参考: https://qiita.com/withelmo/items/b0e1ffba639dd3ae18c0
765 get_srcinfo_data() {
766     local _srcinfo="${1}" _ver="${2}"
767     local _srcinfo_json=$(python << EOF
768 from srcinfo.parse import parse_srcinfo; import json
769 text = """
770 $(cat ${1})
771 """
772 parsed, errors = parse_srcinfo(text)
773 print(json.dumps(parsed))
774 EOF
775 )
776     echo "${_srcinfo_json}" | jq -rc "${2}" | tr '\n' ' '
777 }
778
779
780 # AURからパッケージをビルドしてインストールします
781 # 現在1つのパッケージしか指定できません
782 install_aur_package() {
783     local _package="${1}"
784
785     # Create cache dir
786     if [[ ! -v wfa_cache_dir ]]; then
787         wfa_cache_dir="$(get_cache_dir)/wfa"
788     fi
789     mkdir -p "${wfa_cache_dir}/archive"
790     mkdir -p "${wfa_cache_dir}/build/${_package}"
791
792     # AurJsonから値を取得
793     msg_info "Searching in AUR ..."
794     local _aur_json=$(curl -sL "https://aur.archlinux.org/rpc/?v=5&type=search&by=name&arg=${_package}" | jq -r)
795     if (( "$(echo "${_aur_json}" | jq -r ".resultcount")" == 0 )); then
796         msg_error "Could not find all required packages:\n      ${_package}"
797         exit 1
798     fi
799
800     local _found_packages="$(echo "${_aur_json}" | jq -r --tab '.results[].Name')"
801     #msg_debug "Found package: $(echo ${_found_packages} | tr '\n' ' ')"
802
803     if [[ -n "$(echo "${_found_packages}" | grep -x "${_package}" )" ]]; then
804         msg_debug "Select a package %s with an exact name match" "${_package}"
805     else
806         msg_error "No package with an exact name match was found"
807         exit 1
808     fi
809
810     # PKGBUILDをダウンロード
811     msg_info "Download PKGBUILD of %s" "${_package}"
812     _aur_json=$(echo "${_aur_json}" | jq -r ".results[] | select(.Name == \"${_package}\")" )
813     local _aur_snapshot_url="${aururl%/}$(echo "${_aur_json}" | jq -r ".URLPath")"
814     local _aur_version="$(echo "${_aur_json}" | jq -r ".Version")"
815     msg_debug "Get PKGBUILD from %s" "${_aur_snapshot_url}"
816
817     local _pkgbuild_archive_path="${wfa_cache_dir}/archive/${_package}-${_aur_version}"
818     local _download_pkgbuild=true
819     if [[ -f "${_pkgbuild_archive_path}" ]]; then
820         msg_warn "PKGBUILD has already been downloaded"
821         msg_warn -n "Do you want to overwrite and download? [n] :"
822         local _yes_or_no
823         if [[ "${noconfirm}" = true ]]; then
824             echo
825             _yes_or_no="No"
826         else
827             read _yes_or_no
828         fi
829         case "${_yes_or_no}" in
830             "y" | "Y" | "yes" | "Yes" | "YES" ) _download_pkgbuild=true  ;;
831             *                                 ) _download_pkgbuild=false ;;
832         esac
833     fi
834     if [[ "${_download_pkgbuild}" = true ]]; then
835         remove "${_pkgbuild_archive_path}"
836         curl -L -C - -f -o "${_pkgbuild_archive_path}" "${_aur_snapshot_url}"
837     fi
838
839     # PKGBUILDを展開
840     msg_info "Unpacking the tarball of PKGBUILD ..."
841     tar -xv -f "${_pkgbuild_archive_path}" -C "${wfa_cache_dir}/build/" > /dev/null 2>&1
842
843     # .SRCINFOを解析
844     local _build_dir="${wfa_cache_dir}/build/${_package}"
845     if [[ ! -f "${_build_dir}/.SRCINFO" ]]; then
846         msg_warn ".SRCINFO was not found.\nGenerating it using makepkg"
847         (
848             cd "${_build_dir}"
849             "${makepkg_command}" --printsrcinfo > "${_build_dir}/.SRCINFO"
850         )
851     fi
852
853     local _makedepends="$(get_srcinfo_data "${_build_dir}/.SRCINFO" ".makedepends[]?")"
854     local _depends="$(get_srcinfo_data "${_build_dir}/.SRCINFO" ".depends[]?")"
855     local _conflicts="$(get_srcinfo_data "${_build_dir}/.SRCINFO" ".conflicts[]?")"
856     msg_debug "makedepends: %s" "${_makedepends}"
857     msg_debug "depends: %s" "${_depends}"
858     msg_debug "conflicts: %s " "${_conflicts}"
859
860
861     # 衝突を確認
862     local _pkg _conflicts_found=false
863     for _pkg in ${_conflicts[@]}; do
864         if "${pacman_command}" -Qq "${_pkg}" > /dev/null 2>&1 ; then
865             _conflicts_found=true
866             msg_error "${_package} is colliding with ${_pkg}"
867         fi
868     done
869     if "${pacman_command}" -Qq "${_package}" > /dev/null 2>&1 &&  [[ ! "$("${pacman_command}" -Qq "${_package}" 2> /dev/null)" = "${_package}" ]]; then
870         msg_error "${_package} is colliding with $("${pacman_command}" -Qq "${_package}")"
871         _conflicts_found=true
872     fi
873     if [[ "${_conflicts_found}" = true ]]; then
874         msg_error "Conflict(s) was found"
875         exit 1
876     fi
877
878
879     # 依存パッケージをインストール
880     if [[ "${nodeps}" = false ]]; then
881         msg_info "Install dependent packages..."
882         local _force_aur="${force_aur}"
883         force_aur=false
884         install_package "${_depends}"
885         force_aur="${_force_aur}"
886         unset _force_aur
887     fi
888
889     # ビルド準備
890     # srcdirの確認
891     if [[ -d "${_build_dir}/src" ]]; then
892         msg_info "Found %s" "${_build_dir}/src"
893         msg_info -n "Packages to cleanBuild? [n] :"
894         local _yes_or_no
895         unset _yes_or_no
896         if [[ "${noconfirm}" = true ]]; then
897             echo
898             _yes_or_no="No"
899         else
900             read _yes_or_no
901         fi
902         case "${_yes_or_no}" in
903             "y" | "Y" | "yes" | "Yes" | "YES" ) add_args makepkg "--clean" ;;
904         esac
905     fi
906
907
908     # ビルド
909     add_args "makepkg" "-sf"
910     (
911         cd "${_build_dir}"
912         "${makepkg_command}" "${makepkg_args}"
913     )
914
915
916     # ビルド後のパッケージ一覧を生成
917     (
918         cd "${_build_dir}"
919         "${makepkg_command}" --printsrcinfo > "${_build_dir}/.SRCINFO"
920     )
921     local _pkgnames=($(get_srcinfo_data "${_build_dir}/.SRCINFO" ".packages | keys[]"))
922     local _pkgver="$(get_srcinfo_data "${_build_dir}/.SRCINFO" ".pkgver" | sed 's/ //g')"
923     local _pkgrel="$(get_srcinfo_data "${_build_dir}/.SRCINFO" ".pkgrel" | sed 's/ //g')"
924     local _arch_array=($(get_srcinfo_data "${_build_dir}/.SRCINFO" ".arch[]"))
925     local _arch _pkgname
926     if [[ "${_arch_array[*]}" = "any" ]]; then
927         _arch="any"
928     else
929         _arch="$(uname -m)"
930     fi
931     local _PKGEXT=$(
932         source "${makepkg_config}"
933         echo "${PKGEXT}"
934     )
935     local _pkgfilelist=()
936     for _pkgname in ${_pkgnames[@]}; do
937         _pkgfilelist+=("${_build_dir}/${_pkgname}-${_pkgver}-${_pkgrel}-${_arch}${_PKGEXT}")
938     done
939
940     # インストール
941     run_pacman -U --noconfirm ${_pkgfilelist[@]}
942 }
943
944 # AURのパッケージを検索
945 search_aur_package() {
946     local _package="${1}"
947     #msg_info "Searching in AUR ..."
948     local _aur_json=$(curl -sL "https://aur.archlinux.org/rpc/?v=5&type=search&by=name-desc&arg=${_package}" | jq -r )
949     local _found_result_count="$(echo "${_aur_json}" | jq -r ".resultcount")"
950     if (( "${_found_result_count}" == 0 )); then
951         msg_error "Could not find all required packages:\n      %s" "${_package}"
952         exit 1
953     fi
954     _aur_json=$(echo "${_aur_json}" | jq -r ".results[]")
955
956     local _found_pkgname=($(echo "${_aur_json}" | jq -r ".Name" ))
957
958     if [[ "${quiet}" = true ]]; then
959         local _IFS="${IFS}"
960         IFS=$'\n'
961         echo "${_found_pkgname[*]}"
962         IFS="${_IFS}"
963     else
964         local  _found_package __pkgver __popularity __vote __pkgdesc __orphaned __installed __output_text=()
965         for _found_package in ${_found_pkgname[@]}; do
966             __orphaned=false
967             __installed=false
968             _found_json="$(echo ${_aur_json} | jq "select(.Name == \"${_found_package}\")")"
969
970             #echo "${_found_json}"
971             __pkgver="$(echo "${_found_json}" | jq -r ".Version" )"
972             __popularity="$(echo "${_found_json}" | jq -r ".Popularity" )"
973             __vote="$(echo "${_found_json}" | jq -r ".NumVotes" )"
974             __pkgdesc="$(echo "${_found_json}" | jq -r ".Description" )"
975
976             # 人気度の少数2位以下を四捨五入
977             # 参考: http://www.rivhiro-weather.com/knowledge/?p=536
978             msg_debug "Row popularity: %s" "${__popularity}"
979             __popularity="$(printf "%g\n" "${__popularity}" | awk '{printf("%4.2f", $1)}')"
980
981             # 孤児判定
982             #参考: https://www.366service.com/jp/qa/7c95f46e5236039134ff5b862ae2cd13
983             if ! echo "${_found_json}" | jq -r --exit-status ".Maintainer" 1> /dev/null 2> /dev/null ; then
984                 __orphaned=true
985             fi
986
987             # インストール済み判定
988             if check_installed_package "${_found_package}"; then
989                 __installed=true
990             fi
991
992             __output_text+=(
993                 "$(text -c blue -b "aur")/$(text -b "${_found_package}") $(text -c cyan "${__pkgver}") (+$(text -b "${__vote} ${__popularity}"))"
994             )
995             
996             if [[ "${__orphaned}" = true ]]; then
997                 __output_text+=("$(text -bc red "(Orphaned)")")
998             fi
999
1000             if [[ "${__installed}" = true ]]; then
1001                 __output_text+=("$(text -bc cyan "[Installed]")")
1002             fi
1003             echo "${__output_text[*]}"
1004             echo "    ${__pkgdesc}"
1005             unset __pkgver __popularity __vote __pkgdesc _found_json __orphaned __installed __output_text
1006         done
1007     fi
1008 }
1009
1010 # バージョンを表示して終了
1011 operation_version() {
1012     # Pyalpmからlibalpmの値を取得
1013     # 参考: https://pyalpm.readthedocs.io/en/latest/pyalpm/pyalpm.html
1014     local _libalpm_version="$(python3 -c 'import pyalpm; print(pyalpm.alpmversion())')"
1015     local _pacman_version="$("${pacman_command}" -Q pacman | cut -d ' ' -f 2)"
1016     echo "wfa v${wfa_version} - pacman v${_pacman_version} - libalpm v${_libalpm_version}"
1017 }
1018
1019 operation_remove() {
1020     run_pacman ${pacman_args} "${specified_packages[@]}"
1021 }
1022
1023 # Usage: install_package <package1> <package2>...
1024 install_package() {
1025     local _package
1026     for _package in ${@}; do
1027         if ! check_installed_package "${_package}"; then
1028             if ! check_aur_package "${_package}"; then
1029                 # 公式パッケージなのでpacmanでそのままインストール
1030                 run_pacman ${pacman_args} "${_package}"
1031             else
1032                 # AUR上のパッケージの場合の処理
1033                 install_aur_package "${_package}"
1034                 #msg_error "Getting the AUR package has not been implemented yet.
1035                 #exit 1
1036             fi
1037         fi
1038     done
1039 }
1040
1041 upgrade_aur_package() {
1042     #ここまで翻訳
1043     msg_error "This is a feature that has not been implemented yet"
1044 }
1045
1046 operation_sync(){
1047     local _package
1048     if (( "${sync_clean_count}" >= 1 )); then
1049         remove "$(get_cache_dir)/wfa"
1050         run_pacman ${pacman_args} $(
1051             local _count
1052             for _count in $(seq 1 ${sync_clean_count}); do
1053                 echo -n "-c "
1054             done
1055         )
1056     fi
1057
1058
1059     if [[ "${sync_search}" = true ]]; then
1060         for _package in ${specified_packages[@]}; do
1061             search_aur_package "${_package}"
1062         done
1063         "${pacman_command}" ${pacman_args} ${specified_packages[@]} || :
1064     else
1065         if [[ "${sync_upgrade}" = true ]]; then
1066             upgrade_aur_package
1067             run_pacman ${pacman_args} --sysupgrade
1068         fi
1069         for _package in ${specified_packages[@]}; do
1070             if ! check_aur_package "${_package}" && [[ "${force_aur}" = false ]]; then
1071                 # 公式パッケージなのでpacmanでそのままインストール
1072                 run_pacman ${pacman_args} "${_package}"
1073             else
1074                 # AUR上のパッケージの場合の処理
1075                 install_aur_package "${_package}"
1076                 #msg_error "Getting the AUR package has not been implemented yet."
1077                 #exit 1
1078             fi
1079         done
1080     fi
1081 }
1082
1083
1084 # Parse options
1085 ARGUMENT="${@}"
1086 _opt_short="QRShVdb:aysuc"q
1087 _opt_long="query,remove,sync,help,version,debug,dbpath:,aururl,aur,noconfirm,config:,makepkg:,mflags:,pacman:,git:,gitflags:,gpg:,gpgflags:,makepkgconf:,nomakepkgconf,nodeps,refresh,bash-debug,msg-debug,sysupgrade,color:,nocolor,clean,quiet"
1088
1089 OPT=$(getopt -o ${_opt_short} -l ${_opt_long} -- ${ARGUMENT})
1090 [[ ${?} != 0 ]] && exit 1
1091 unset _opt_short _opt_long
1092
1093 eval set -- "${OPT}"
1094 msg_debug "Argument: ${OPT}"
1095
1096 while :; do
1097     case ${1} in
1098         -Q | --query)
1099             set_operation "query"
1100             shift 1
1101             ;;
1102         -R | --remove)
1103             set_operation "remove"
1104             shift 1
1105             ;;
1106         -S | --sync)
1107             set_operation "sync"
1108             shift 1
1109             ;;
1110         -V | --version)
1111             set_operation "version"
1112             shift 1
1113             ;;
1114         --)
1115             shift
1116             break
1117             ;;
1118         *)
1119             shift 1
1120             ;;
1121     esac
1122 done
1123
1124 eval set -- "${OPT}"
1125
1126 while :; do
1127     case ${1} in
1128         -a | --aur)
1129             force_aur=true
1130             msg_debug "Assume targets are from the AUR"
1131             shift 1
1132             ;;
1133         --debug)
1134             debug=true
1135             add_args pacman "--debug"
1136             shift 1
1137             ;;
1138         -d | --nodeps)
1139             nodeps=true
1140             add_args pacman "--nodeps"
1141             shift 1
1142             ;;
1143         -b | --dbpath)
1144             add_args pacman "--dbpath '${2}'"
1145             shift 2
1146             ;;
1147         -y | --refresh)
1148             option_y_count=$(( option_y_count + 1 ))
1149             shift 1
1150             ;;
1151         -s | --search)
1152             add_args pacman "--search"
1153             sync_search=true
1154             shift 1
1155             ;;
1156         -u | --sysupgrade)
1157             sync_upgrade=true
1158             shift 1
1159             ;;
1160         -c | --clean)
1161             sync_clean_count=$(( sync_clean_count + 1 ))
1162             shift 1
1163             ;;
1164         -q | --quiet)
1165             quiet=true
1166             add_args pacman "--quiet"
1167             shift 1
1168             ;;
1169         --aururl)
1170             aururl="${2}"
1171             shift 2
1172             ;;
1173         --noconfirm)
1174             add_args pacman "--noconfirm"
1175             noconfirm=true
1176             shift 1
1177             ;;
1178         --config)
1179             pacman_config="${2}"
1180             add_args pacman "--config ${2}"
1181             shift 2
1182             ;;
1183         --makepkg)
1184             makepkg_command="${2}"
1185             shift 2
1186             ;;
1187         --mflags)
1188             makepkg_args="${2}"
1189             shift 2
1190             ;;
1191         --pacman)
1192             pacman_command="${2}"
1193             shift 2
1194             ;;
1195         --git)
1196             git_command="${2}"
1197             shift 2
1198             ;;
1199         --gitflags)
1200             git_args="${2}"
1201             shift 2
1202             ;;
1203         --makepkgconfig)
1204             if [[ "${nomakepkgconf}" = false ]]; then
1205                 makepkg_config="${2}"
1206             else
1207                 msg_warn "--nomakepkgconf is specified.\n--makepkgconf has been ignored."
1208             fi
1209             shift 2
1210             ;;
1211         --nomakepkgconf)
1212             makepkg_config="/etc/makepkg.conf"
1213             nomakepkgconf=true
1214             shift 1
1215         ;;
1216         --bash-debug)
1217             bash_debug=true
1218             set -xv
1219             shift 1
1220             ;;
1221         --msg-debug)
1222             msgdebug=true
1223             shift 1
1224             ;;
1225         --color)
1226             case "${2}" in
1227                 "never")
1228                     nocolor=true
1229                     add_args pacman "--color never"
1230                     ;;
1231                 "always")
1232                     nocolor=false
1233                     add_args pacman "--color always"
1234                     ;;
1235                 "auto")
1236                     msg_error "auto is not currently supported."
1237                     add_args pacman "--color auto"
1238                     ;;
1239             esac
1240             ;;
1241         --nocolor)
1242             nocolor=true
1243             add_args pacman "--color never"
1244             shift 1
1245             ;;
1246         -h | --help)
1247             usage
1248             shift 1
1249             exit 0
1250             ;;
1251         --)
1252             shift
1253             break
1254             ;;
1255         *)
1256             shift 1
1257             ;;
1258     esac
1259 done
1260
1261 specified_packages=(${@})
1262
1263 # Run database update
1264 if (( "${option_y_count}" == 1 )); then
1265     run_pacman -Sy
1266 elif (( "${option_y_count}" >= 2 )); then
1267     run_pacman -Syy
1268 fi
1269
1270 case "${operation}" in
1271     "version")
1272         operation_version
1273         ;;
1274     "sync")
1275         operation_sync
1276         ;;
1277     "remove")
1278         operation_remove
1279         ;;
1280     "none")
1281         exit 0
1282         ;;
1283     *)
1284         msg_error "Undefined operation"
1285         exit 1
1286         ;;
1287 esac