OSDN Git Service

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