OSDN Git Service

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