#!/usr/bin/bash
shopt -s extglob

NB_VERSION="1.0"
NB_CONFIGDIR="${NB_CONFIGDIR:-"${XDG_CONFIG_HOME:-"${HOME}/.config"}/napi-bash"}"
NB_CACHEDIR="${NB_CACHEDIR:-"${XDG_CACHE_HOME:-"${HOME}/.cache"}/napi-bash/"}"
NB_CONFIG="${NB_CONFIG:-"$NB_CONFIGDIR/config"}"

declare -a files hashes
declare -A subfmt_ext notifications_fmt

subfmt_ext[fab]=txt
subfmt_ext[microdvd]=sub
subfmt_ext[mpl2]=txt
subfmt_ext[subviewer]=sub
subfmt_ext[subrip]=srt
subfmt_ext[tmplayer]=txt

auth_login=nie

subcp_conv=""
subfmt_conv=""
subeol_conv=""

subext="txt"
subext_autodetect=tak
subext_prefix=""
suboverwrite=nie
subskipifexist=nie
subretry_restore=nie
subretry_store=nie
notifications=tak
colors=tak
videoext_match=dir
videoext=(.{3gp,avi,asf,divx,m{4,k,o}v,mp{4,e,eg,g},ogg,ogv,rmvb,wmv,qt})
DLAGENT="curl -fLsS --retry 3 --retry-delay 3 -o %o %u"
msg_raport="Łącznie pobranych napisów: %b%d/%a%B"
msg_status[0]="%2%bPobrano%R dla %i%{file-uri:f}"
msg_status[1]="%1%bBrak napisów%R dla %i%f"
msg_status[2]="%1Brak uprawnień do zapisu w folderze z plikiem: %f"
msg_status[3]="%1Niewystarczająca ilość wolnego miejsca na urządzeniu z plikiem: %f"
msg_status[100]="%1Nie powiodła się detekcja formatu napisów dla: %f"
msg_status[101]="%1Nie powiodło się konwertowanie formatu napisów dla: %f"
msg_status[102]="%1Nie powiodło się konwertowanie strony kodowej napisów dla: %f"
msg_status[200]="%1Zdalny serwer zwrócił: Przerwa techniczna..."
msg_status[254]="%1DLAGENT nie zawiera tagu %o dla pliku wyjściowego.."
msg_status[255]="%1Pobieranie pliku z napisami nie powiodło się, problemy z połączeniem?"
msg_gui_disabled_array=(0 1)
msg_cli_disabled_array=()
raport_gui_enabled=tak
raport_cli_enabled=tak
gui_int_warn=tak

#notifications_tools=(notify-send growlnotify terminal-notifier)

enabled() {
  case "${1,,}" in
    0|-|f*|n*|wy*|of*|"") false
  esac
}

#-------------------------------------------------------------------------------
# Ładowanie konfiguracji (uwaga: do tego miejsca można nadpisać configiem)
#-------------------------------------------------------------------------------
load_config() {
  local opt config

  until [ -z "$1" ]; do
    opt="$1"; shift
    case "${opt}" in
      --config=*)
        if [ -f "${opt#*=}" ] && [ -r "${opt#*=}"]; then
          config="${opt#*=}"
        elif [ -f "$NB_CONFIGDIR/${opt#*=}" ] && [ -r "$NB_CONFIGDIR/${opt#*=}" ]; then
          config="$NB_CONFIGDIR/${opt#*=}"
        fi
        break
      ;;
      --)
        break
    esac
  done

  if [ -z "$config" ]; then
    config="$NB_CONFIGDIR/config"
    test -f "$config" && test -r "$config" || return 1
  fi

  source "$config"
}

alias exit=return
load_config "$@"
unalias exit

if [[ "$0" =~ / ]]; then
  read NB_basebin < <(realpath -- "$0")
else
  if hash "$0" &> /dev/null; then
    read NB_basebin < <(hash -t "$0")
  else
    if [ -f "./$0" ]; then
      read NB_basebin < <(realpath -- "./$0")
    fi
  fi
fi

if [ -z "$NB_basebin" ]; then
  NB_basebin=/usr/bin
else
  read NB_basebin < <(dirname -- "$NB_basebin")
fi

plugin() {
  # TODO $NB_plugin_${1//[^[:alnum]]/_}+=(name)
  if [ -d "$NB_basebin/../lib/napi-bash/$1" ]; then
    for i in "$NB_basebin/../lib/napi-bash/$1/"*.inc; do
      source "$i"
    done
  elif [ -f "$NB_basebin/../lib/napi-bash/$1" ]; then
    source "$NB_basebin/../lib/napi-bash/$1"
  else
    return 1
  fi
  return 0
}
fps_detect_tools_reg() {
  local i
  if [ "${#fps_detect_tools[@]}" != 0 ]; then
    for i in "${fps_detect_tools[@]}"; do
      test "$1" = "$i" && return
    done
  fi
  fps_detect_tools+=($1)
}
#-------------------------------------------------------------------------------
#  Podstawowe funkcje..
#-------------------------------------------------------------------------------
msg() {
  local print_msg_fmt_ret
  print_msg_fmt "0;32;1" "==>" "0;1" "$1"\
    && printfo STDOUT "$print_msg_fmt_ret"

}
msg2() {
  local print_msg_fmt_ret
  print_msg_fmt "0;34" "  -->" "0" "$1"\
    && printfo STDOUT "$print_msg_fmt_ret"
}

info() {
  local print_msg_fmt_ret
  print_msg_fmt "0;35;1" "Info:" "0" "$1" \
    && printfo STDOUT "$print_msg_fmt_ret"

  return 1
}
warn() {
  local print_msg_fmt_ret
  print_msg_fmt "0;33;1" "Uwaga:" "0" "$1" \
    && printfo STDERR "$print_msg_fmt_ret"
  enabled "$gui_int_warn" && gui_msg "warn" "$1" &> /dev/null &
  return 1
}
error() {
  local print_msg_fmt_ret
  print_msg_fmt "0;31;1" "Błąd:" "0" "$1" \
    && printfo STDERR "$print_msg_fmt_ret"
  gui_msg "error" "$1" &> /dev/null &
  return 1
}
print_msg_fmt() {
  if enabled "$colors"; then
    printf -v print_msg_fmt_ret -- '\e[%sm%s\e[%sm %s...\e[0m\n' "$@"
  else
    printf -v print_msg_fmt_ret -- '%s %s...\n' "$2" "$4"
  fi
}

printfo() {
  test "$#" = "0" && return
  local t=1

  if [ "$#" -ge "2" ]; then
    case "$1" in
      STDOUT) true;;
      STDERR) enabled "$DEBUG" || t=2;;
      *) false
    esac && shift
  fi

  printf -- "$@" >&$t
}

plugin gui_msg

gui_msg() {
  local type="$1" msg="$2"

  is_function "gui_msg__$NB_notify_tool" \
    && "gui_msg__$NB_notify_tool"
}

is_function() {
  declare -F "$1" &> /dev/null
}

copyfile() {
  case "$OSTYPE" in
    linux-gnu) cp \
         --remove-destination \
         --no-target-directory \
         --dereference \
         -- "$1" "$2" &> /dev/null
    ;;
    *) cp -f -- "$1" "$2" &> /dev/null
  esac
}

#-------------------------------------------------------------------------------
#  Obsługa argumentów, wyszkiwanie i badanie plików..
#-------------------------------------------------------------------------------
usage() {
  local nb_name="${0##*/}"

  printf -- 'napi-bash wersja %s | 3ED <krzysztof1987@gmail.com>\n' "$NB_VERSION"

  printf -- '
Użycie:
  %s [opcje] <plik/folder> [kolejne pliki/foldery]
  %s <-h|--help>
' "$nb_name" "$nb_name"

  printf -- '
Opcje:
  -o=<bool>, --suboverwrite=<bool>, --(no-)suboverwrite
      Nadpisuj pliki (ustawione: %s)

  -s=<bool>, --skipifexist=<bool>, --(no-)skipifexist
      Pomijaj pobrane (ustawione: %s)

  -c=<bool>, --colors=<bool>, --(no-)colors
      Używaj kolorów (ustawione: %s)

  -x=<tekst>, --subext=<tekst>
      Rozszerzenie pliku (ustawione: "%s").

  -X=<bool>, --subext-autodetect=<bool>, --(no-)subext-autodetect
      Przeprowadzić autodetekcji (ustawione: %s).

  -P=<tekst>, --subext-prefix=<tekst>
      Dodatkowy przedrostek przed rozszerzeniem pliku. Puste by
      wyłączyć. (Ustawione: "%s")

  -F=<tekst>, --subfmt-conv=<tekst>
      Format napisów. Puste by wyłączyć. (ustawione: "%s")

  -C=<tekst>, --subcp-conv=<tekst>
      Konwerowanie strony kodowej napisów. Puste by wyłączyć.
      (ustawione: "%s")

  -E=<tekst>, --subeol-conv=<tekst>
      Konwertowanie typu oznaczenia końca linii. Puste by
      wyłączyć. (ustawione: "%s")

  -d=<od-do>, --depth=<od-do>
      Głębia przeszukiwania rekursywnego..

  -g=<liczba>, --ge=<liczba>
      Tylko pliki większe bądź równe podanej ilości MiB.

  -l=<liczba>, --le=<liczba>
      Tylko pliki mniejsze bądź równe podanej ilości MiB.

  -n=<tekst>, --nick=<tekst>
      Twój login do napiprojektu. Puste dla konta anonimowego.
      (ustawione: "%s")

  -p=<tekst>, --pass=<tesk>
      Twoje hasło do napiprojektu. Puste dla konta anonimowego.
      (ustawione: "%s")

  -L=<bool>, --login=<bool>
      Loguj się podanym wcześniej nickiem i hasłem.
      (ustawione: %s)

  -N=<bool>, --notifications=<bool>
      Wł/wył notyfikacje pulpitowe. (ustawione: %s)

  --debug=<plik>
      Tryb debugowy

  --config=<plik>
      Nazwa konfiguracji lub pełna ścieżka. (Ustawione:
      %s)

  -h, --help, --usage
      Wyświetl pomoc

Aby dowiedzieć się więcej, zobacz: man 1 napi-bash

Napi-bash opublikowano na licencji GPL3:
  This program comes with  ABSOLUTELY NO WARRANTY;
  This program may be  freely redistributed  under
  the terms of the GNU GPL 3. For more information
  read:   http://www.gnu.org/licenses/gpl-3.0.html\n' \
    "$suboverwrite" "$subskipifexist" "$colors" \
    "$subext" "$subext_autodetect" "$subext_prefix" \
    "$subfmt_conv" "$subcp_conv" "$subeol_conv" \
    "$auth_nick" "${auth_pass:+"***"}" "$auth_login" \
    "$notifications" "${config:-"$NB_CONFIGDIR/config"}"

#  -r=<bool>, --subretry-restore=<bool> [szkic/koncepcja]
#      Wznawia pobierania wcześniej zakończone niepowodzeniem,
#      bez ponownego generowania sum kontrolnych. (ustawione: $subretry_restore)

	return 1
}

ARGV() {
  local -a paths

  ARGV__getopts "$@" || return 1

  # niezbędny:
  setup_bin_7z \
    && setup_bin_realpath \
    || return 1

  # opjonalne:
  setup_bin_iconv
  setup_bin_subotage
  setup_bin_dos2unix

  setup_notification

  setup_var_filesize

  # Sprawdzanie poprawności hasła o ile wł. logowanie
  validate_auth

  msg "Generowanie listy plików"

  if generate_files "${paths[@]}"; then
    msg "Wyszukiwanie i pobieranie napisów"
  else
    info "Brak plików lub odrzucone przez filtr"
    return 1
  fi
}

ARGV__getopts() {
  local opt bool_ret

  until [ -z "$1" ]; do
    opt="$1"; shift
    case "${opt}" in
      -h|--help|--usage) usage; return $? ;;
      -o=*|--suboverwrite=*|--suboverwrite|--no-suboverwrite)
        ARGV__bool "$opt"; suboverwrite="$bool_ret";;
      -s=*|--skipifexist=*|--skipifexist|--no-skipifexist)
        ARGV__bool "$opt"; subskipifexist="$bool_ret";;
      -c=*|--colors=*|--colors|--no-colors)
        ARGV__bool "$opt"; colors="$bool_ret";;
      -X=*|--subext-autodetect=*|--subext-autodetect|--no-subext-autodetect)
        ARGV__bool "$opt"; subext_autodetect="$bool_ret";;
      -N=*|--notifications=*|--notifications|--no-notifications)
        ARGV__bool "$opt"; notifications="$bool_ret";;
#      -r=*|--subretry-restore=*|--subretry-restore|--no-subretry-restore)
#        ARGV__bool "$opt"; subretry_restore="$bool_ret";;
      -F=*|--subfmt-conv=*) subfmt_conv="${opt#*=}" ;;
      -E=*|--subeol-conv=*) subeol_conv="${opt#*=}";;
      -C=*|--subcp-conv=*) subcp_conv="${opt#*=}" ;;
      -x=*|--subext=*) subext="${opt#*=}" ;;
      -P=*|--subext-prefix=*) subext_prefix="${opt#*=}" ;;
      -d=*|--depth=*) ARGV__parse_depth "${opt#*=}";;
      -g=*|--ge=*) filesize_ge="${opt#*=}";;
      -l=*|--le=*) filesize_le="${opt#*=}";;
      -n=*|--nick=*) auth_nick="${opt#*=}";;
      -p=*|--pass=*) auth_pass="${opt#*=}";;
      -L=*|--login=*) auth_login="${opt#*=}";;
      --version) printf -- "%s\n" "$NB_VERSION"; return 1;;
      --debug=*|--config=*) true;;
      --) break ;;
      -*) ARGV__bad_arg l "$opt" || return 1;;
      *) paths+=("$opt")
    esac
  done
  until [ -z "$1" ]; do
    paths+=("$1")
    shift
  done

  #RETURNVAR: @paths
}
ARGV__bad_arg() {
  case "$1" in
    l) error "Niepoprawna opcja: '$2'";;
    r) error "Nieprawidłowy argument opcji: '$2'";;
    *) false
  esac
}

ARGV__bool() {
  local left="${1%%=*}" right="${1#*=}"
  bool_ret=nie

  if [ "$left" = "$right" ]; then
    ! [[ "$left" = "--no-"* ]] && bool_ret=tak
  else
    enabled "$right" && bool_ret=tak 
  fi
  #RETURNVAR: $bool_ret
}

ARGV__parse_depth() {
  [[ "$1" = +([0-9-]) ]] || return 0

  unset NB_FIND_DEPTH

  local left="${1%%-*}"
  local right="${1##*-}"

  test -n "$left" && NB_FIND_DEPTH+=("-mindepth" "$left")
  test -n "$right" && NB_FIND_DEPTH+=("-maxdepth" "$right")

  return 0
}

test_filesize() {
  local file="$1"

  # bash operuje na type int dla liczb (zaokrągla zawsze w dół)
  local -i filesize=0
  read filesize < <(stat -c "%s" -- "$file" 2> /dev/null)

  test "$filesize" != "0" \
    || return 1

  filesize="$[filesize / 1048576]"

  # większy bądź równy
  test "$filesize" -ge "$filesize_ge" \
    || return 1

  # mniejszy bądź równy
  test -z "$filesize_le" \
    || test "$filesize" -le "$filesize_le" \
    || return 1
}

test_videoext() {
  local file="${1,,}" i

  for i in "${videoext[@]}"; do
    case "${file}" in
      *"$i") return 0
    esac
  done

  return 1
}

generate_files() {
  local i path

  # Pliki/katalogi..
  for i in "${@}"; do
    test -n "$i" || continue

    if [ -n "$BIN_realpath" ]; then
      read path < <(realpath -- "$i")
    else
      path="$i"
    fi

    if [ -d "$path" ]; then
      generate_files__dir_recursively "$path"
    else
      generate_files__file "$path"
    fi
  done

  # Dodawanie zawartości katalogu subretry o posiada zawartość
  subretry_restore

  test "${#files[@]}" != "0"

  # RETURNVAR: @files
}

generate_files__dir_recursively() {
  local i

  while IFS= read -r -u4 -d $'\0' i; do
    NB_generate_files_FOR=dir \
      generate_files__file "$i"
  done 4< <(find "$1" "${NB_FIND_DEPTH[@]}" -type f -print0)
}

generate_files__file() {
  test -f "$1" && test -r "$1" || return 1

  local NB_generate_files_FOR="${NB_generate_files_FOR:-file}"

  case "${videoext_match,,}" in
    a*|${NB_generate_files_FOR:0:1}*) test_videoext "$1" || return 0;;
  esac

  test_filesize "$1" || return 1

  if enabled "$subskipifexist"; then
    local pref="${subext_prefix:+"."}$subext_prefix"
    if enabled "$subext_autodetect"; then
      # TODO "ułądnić"; Pomjać z opcją overwrite?
      for i in "${!subfmt_ext[@]}"; do
        test -f "${1%.*}$pref.${subfmt_ext[$i]}" && return 1
      done
    fi
    test -f "${1%.*}$pref.$subext" && return 1
  fi

  files+=("$1")
}

#-------------------------------------------------------------------------------
#  Specjalne funkcje wczesnego ładowania
#-------------------------------------------------------------------------------
early() {
  test -t 1 || colors=0

  early__check_bash_ver || return 1
  early__debug "$@"
}
early__check_bash_ver() {
 if [ "${BASH_VERSINFO[0]}" -lt "4" ] || [ "${BASH_VERSINFO[0]}" -eq "4" -a "${BASH_VERSINFO[1]}" -lt "1" ]; then
   error "Zaktualizuj swój bash do wersji 4.1 lub nowszej..."
   return 1
 fi
}
early__debug() {
  local opt

  until [ -z "$1" ]; do
    opt="$1"; shift
    case "${opt}" in
      --debug=*)
        DEBUG=1
        local debug_log="${opt#*=}"
        if [ -z "$debug_log" ] || [ -d "$debug_log" ]; then
          debug_log+="${debug_log:+"/"}napi-bash.nb_debug"
        else
          if [ -f "$debug_log" ]; then
            test "${debug_log##*.}" = "nb_debug" || debug_log+=".nb_debug"
          fi
        fi
        exec 2>&-;
        exec 2<>"$debug_log";
        set -x;
        PS4='+ [${LINENO}] ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
        break
      ;;
      --)
        break
    esac
  done
}

#-------------------------------------------------------------------------------
#  Obsłóga błędów i raporty
#-------------------------------------------------------------------------------
##### "enable" checker #####
napi_msg__enabled__gui() {
  test -n "$NB_notify_tool" || return 1

  if [ "$1" = "raport" ]; then
    enabled "$raport_gui_enabled" && return 0 || return 1
  fi

  local var
  for var in "${msg_gui_disabled_array[$@]}"; do
    test "$var" = "$1" -o "$var" = "@" && return 1
  done
  return 0
}
napi_msg__enabled__cli() {
  if [ "$1" = "raport" ]; then
    enabled "$raport_cli_enabled" && return 0 || return 1
  fi

  local var
  for var in "${msg_cli_disabled_array[$@]}"; do
    test "$var" = "$1" -o "$var" = "@" && return 1
  done
  return 0
}

##### main fields filler (tag replace) #####
fill_fields() {
  local NB__type="${1//[^[:alnum]]/_}"
  local NB__fmt="${2//[^[:alnum]]/_}"
  local NB__msg="$3"
  local file_id="$4"
  local error_id="$5"
  shift 5

  if [ "$NB__fmt" = "gui" ]; then
    NB__fmt="$NB_notify_fmt"
  fi

  fill_fields_ret=

  local -a local_array
  local -i NB__begin=0 NB__end=0
  local NB__index return_str

  until [ "$NB__end" -ge "${#NB__msg}" ]; do
    NB__begin=$NB__end
    NB__index="${NB__msg:NB__begin}"

    case "${NB__index:0:2}" in
      '%{')
        NB__begin+=2
        NB__index="${NB__msg:NB__begin}"
        NB__index="${NB__index%%'}'*}"
        NB__end=${#NB__index}

        fill_fields__tag "${NB__msg:NB__begin:NB__end}" \
          && fill_fields_ret+="$return_str"

        NB__end+=$NB__begin
        NB__end+=1
        ;;
      '%'*)
        NB__begin+=1
        NB__end+=2
        fill_fields__tag "${NB__msg:NB__begin:1}" \
          && fill_fields_ret+="$return_str"
        ;;
      *)
        NB__index="${NB__index%%'%'*}"
        NB__end=${#NB__index}

        fill_fields__esc "${NB__msg:NB__begin:NB__end}" \
          && fill_fields_ret+="$return_str" \
          || fill_fields_ret+="${NB__msg:NB__begin:NB__end}"

        NB__end+=$NB__begin
    esac
  done

  test -n "$fill_fields_ret" \
    || return 1

  fill_fields__tag "R" \
    && fill_fields_ret+="$return_str"

  return 0

  #RETURNVAR: $fill_fields_ret
}

fill_fields__tag() {
  return_str=

  local -a NB__tag_list
  IFS=":" read -a NB__tag_list <<< "$1"

  local -i NB__tag_id=0
  for ((NB__tag_id=0; $NB__tag_id < ${#NB__tag_list[@]}; NB__tag_id++)); do
    is_function fill_fields__tag__$NB__type \
      && fill_fields__tag__$NB__type "${NB__tag_list[$NB__tag_id]}" >&2 \
      && return 0
  done

  return 1
}

fill_fields__esc() {
  return_str=

  # TODO według notyfierów - tablica assocjacyjna
  if is_function fill_fields__esc__$NB__fmt; then
    fill_fields__esc__$NB__fmt "$1" >&2 \
      || return 1
  else
    return_str="$1"
  fi

  test -n "$return_str"
}

fill_fields__fmt() {
  return_str=

  # TODO według notyfierów - tablica assocjacyjna
  is_function fill_fields__fmt__$NB__fmt \
    && fill_fields__fmt__$NB__fmt "$1" >&2 \
    || return 1

  test -n "$return_str"
}

# tag fields (put the information)
fill_fields__tag__msg() {
  case "$1" in
    f) fill_fields__esc "${files[$file_id]##*/}";;
    path) fill_fields__esc "${files[$file_id]}";;
    dir) fill_fields__esc "${files[$file_id]%/*}";;
    d) fill_fields__esc "$file_id";;
    e) fill_fields__esc "$error_id";;
    *) fill_fields__fmt "$1"
  esac || return 1
}
fill_fields__tag__raport() {
  local -i dl_done=0 dl_fail=0 dl_all=0

  for ((dl_all=0; $dl_all < ${#files_stat[@]}; dl_all++)); do
    test "${files_stat[$dl_all]}" = "0" && dl_done+=1 || dl_fail+=1
  done

  case "$1" in
    d) fill_fields__esc "$dl_done";;
    n) fill_fields__esc "$dl_fail";;
    a) fill_fields__esc "$dl_all";;
    *) fill_fields__fmt "$1"
  esac || return 1
}

plugin fill_fields

##### main message functions #####
napi_msg() {
  [ -z "$1" -o -z "$2" ] && return 1
  [ "$NB_USERBREAK" = "1" ] && return 0

  # $1 = id pliku
  # $2 = id błędu

  local msg
  local fill_fields_ret

  files_stat[$1]=$2

  test -n "${msg_status[$2]}" \
    && msg="${msg_status[$2]}" \
    || msg="Status #%e dla: %f"

  napi_msg_cli "$msg" "$@"
  napi_msg_gui "$msg" "$@"

  if [ "$2" -ge "100" ]; then
    test "$2" -ge "200" && exit 1
    files_warn[$1]+="${files_warn[$1]:+","}$2"
  fi

  return $2
}
napi_msg_gui() {
  napi_msg__enabled__gui "$3" || return 0
  local fill_fields_ret

  if fill_fields "msg" "gui" "$@"; then
    gui_msg "$3" "$fill_fields_ret" &
  else
    return 1
  fi
}
napi_msg_cli() {
  napi_msg__enabled__cli "$3" || return 0
  local fill_fields_ret

  if fill_fields "msg" "cli" "$@"; then
    if [ "$3" = "0" ]; then
      printfo STDOUT "${fill_fields_ret//%/%%}\n" || return 1
    else
      printfo STDERR "${fill_fields_ret//%/%%}\n" || return 1
    fi
  else
    return 1
  fi
}
napi_raport() {
  local msg="${msg_raport:-"Łącznie pobrano %i/%a napisów..."}"

  napi_raport_cli "$msg"
  napi_raport_gui "$msg"
}
napi_raport_gui() {
  napi_msg__enabled__gui "raport" || return 0
  local fill_fields_ret

  if fill_fields "raport" "gui" "$@"; then
    gui_msg "raport" "$fill_fields_ret" &
  else
    return 1
  fi

}
napi_raport_cli() {
  napi_msg__enabled__cli "raport" || return 0
  local fill_fields_ret

  msg "Raport"
  if fill_fields "raport" "cli" "$@"; then
    printfo STDOUT "${fill_fields_ret//%/%%}\n"
  else
    return 1
  fi
}

#-------------------------------------------------------------------------------
#  Generowanie sum kontrolnych
#-------------------------------------------------------------------------------
get_hash__md5sum() {
  test -n "$1" && test -f "$1" && test -r "$1" || return 1

  read md5sum < <(md5sum --binary < <(dd bs=10485760 count=1 if="$1" 2> /dev/null))
  md5sum="${md5sum:0:32}"

  test "${md5sum//[^[:xdigit:]]/}" = "$md5sum" \
    && test "${#md5sum}" = "32"

  # RETURN: $md5sum
}

get_hash__napisum() {
  test -n "$1" || return 1

  # Komuś chyba się nudziło...
  local md5sum idx mul add n a p i s y

  md5sum="$1"
  napisum=

  idx=("14" "3"  "6"  "8"  "2")
  mul=("2"  "2"  "5"  "4"  "3")
  add=("0"  "13" "16" "11" "5")

  for p in ${!idx[*]}; do
    n="${add[p]}"
    a="${mul[p]}"
    p="${idx[p]}"

    printf -v i -- "%d" "0x${md5sum:p:1}"; i="$[n + i]"
    printf -v s -- "%d" "0x${md5sum:i:2}"
    printf -v y -- "%x" "$[s*a]"

    napisum+="${y:${#y}-1:1}"
  done

  # RETURN: $napisum
}

get_hash() {
  get_hash__md5sum "$1" \
    && get_hash__napisum "$md5sum"
  #RETURNVAR: $md5sum $napisum
}

generate_hashes() {
  local c fid md5sum napisum

  test "$#" = "0" && set -- "${files[@]}"

  for ((c=1; $c <= $#; c++)); do
    fid="$[c-1]" md5sum=0 napisum=0
    test -z "${hashes[$fid]}" || continue
    get_hash "${!c}" \
      && hashes[$fid]="$md5sum $napisum"
  done

  #RETURNVAR: @hashes
}

#-------------------------------------------------------------------------------
# Poll i forkowanie
#-------------------------------------------------------------------------------
napi_poll__fork_hash() {
  local c md5sum napisum

  for ((c=0; $c < ${#files[@]}; c++)); do
    md5sum=0 napisum=0

    if [ -n "${hashes[$fid]}" ]; then
      md5sum="${hashes[$fid]%% *}"
      napisum="${hashes[$fid]##* }"
    fi

    test "$md5sum" != "$napisum" \
      || get_hash "${files[$c]}"

    echo "$? $c $md5sum $napisum"
  done

  echo bye
}

napi_poll__fork_download() {
  local ret=0
  local -a childARG

  while read -a childARG; do
    test "${childARG[0]}" = "bye" && break

    sub_download "${childARG[@]}"
    childARG[0]=$?

    echo "${childARG[@]}"
  done

  echo bye
}

napi_poll() {
  local CACHE
  read CACHE < <(mktemp -u -d -p "$NB_CACHEDIR" $$.XXXX)
  mkdir -p "$CACHE" || return 1
  NB_USERBREAK=0

# Tworzenie fifo (kolejka: "pierwszy wchodzi, pierwszy wychodzi")
# do komunikacji pomiędzy forkiem a rodzicem
  # tworzenie fifo
  mkfifo "$CACHE/hash.fifo" "$CACHE/download.fifo"

  local NB_queue_hash NB_queue_download
  # otwieranie fifo fd
  exec {NB_queue_hash}<>"$CACHE/hash.fifo" \
       {NB_queue_download}<>"$CACHE/download.fifo"

  # Obsługa sygnałów / czyszczenie
  trap 'kill ${NB_childPID[@]} &> /dev/null; rm -rf -- "$CACHE"' EXIT
  trap 'NB_USERBREAK=1; kill ${NB_childPID[@]} &> /dev/null; echo bye >&$NB_queue_download; echo -ne "\n*** Przerwane przez użytkownika ***\n"' SIGINT

  # subprocesy
  napi_poll__fork_hash 1>&$NB_queue_hash     & NB_childPID+=($!)
  napi_poll__fork_download \
    0<&$NB_queue_hash 1>&$NB_queue_download  & NB_childPID+=($!)

  local ext
  local -a childARG
  local -i ret=0

  while read -u $NB_queue_download -a childARG; do
    test "${childARG[0]}" = "bye" && break

    if [ "${childARG[0]}" = "0" ]; then
      # Konwertowanie strony kodowej
      subcp_convert "${childARG[@]}" || napi_msg "${childARG[1]}" "$?"

      # Konwertowanie formatu napisów i/lub pobieranie rozszerzenia o ile subext ma prefix
      subfmt_convert "${childARG[@]}" || napi_msg "${childARG[1]}" "$?"

      # konwertowanie znaku końca linii
      subeol_convert "${childARG[@]}"

      # Instalowanie napisów
      subext="$ext" sub_install "${childARG[@]}"
      childARG[0]=$?
    elif [ "${childARG[0]}" -lt "100" ]; then
      subretry_store "${childARG[@]}"
      ret=1
    fi

    napi_msg "${childARG[1]}" "${childARG[0]}"
  done

  # Zamykanie fifo fd
  exec {NB_queue_hash}>&- {NB_queue_download}>&-

  trap - SIGINT

  return $ret
}


#-------------------------------------------------------------------------------
# Wznawianie pobierania  TODO
#-------------------------------------------------------------------------------
subretry_store() {
  enabled "$subretry_store" || return 0
  local file="${files[$2]}" md5sum="$3" napisum="$4"
  test -d "$CACHEDIR/subretry/" || mkdir -p "$CACHEDIR/subretry/"
  echo "$file" > "$CACHEDIR/subretry/${md5sum}_${napisum}"
}

subretry_restore() {
  enabled "$subretry_restore" || return 0
  test -d "$CACHEDIR/subretry/" || return 1

  local file sums i
  local -i c="${#files[@]}"

  for i in "$CACHEDIR/subretry/"*; do
    read file < "$i"
    if ! [ -f "$file" ]; then
      unlink -- "$i"
      continue
    fi
    sums="${i##*/}"
    sums="${sums//_/ }"
    if [ -n "$sums" ]; then
      files[$c]="$file"
      hashes[$c]="$sums"
      c+=1
    fi
  done
  #RETURNVAR: @files[c,..] @hashes[c,..]
}

#-------------------------------------------------------------------------------
#  Pobieranie napisów
#-------------------------------------------------------------------------------
downloader() {
  local dlcmd="$DLAGENT" url="$1" dlfile="$2"

  if [[ "$dlcmd" = *'%o'* ]]; then
    dlcmd="${dlcmd//\%o/\"$dlfile\"}"
  else
    return 254
  fi

  if [[ "$dlcmd" = *'%u'* ]]; then
    dlcmd="${dlcmd//\%u/\"$url\"}"
  else
    dlcmd="$dlcmd \"$url\""
  fi

  local ret=0
  eval "$dlcmd || ret=\$?"
  if [ "$ret" != "0" ]; then
    test -f "$dlfile" && unlink -- "$dlfile"
    return 255
  fi

  test -s "$dlfile" || return 255
}

sub_download() {
  test "$1" = "0" || return $1
  local fid="$2" md5="$3" napi="$4"

  local dlfile="$CACHE/_${md5}_${napi}.7z"
  local subfile="$CACHE/_${md5}_${napi}.sub"

  local url="http://napiprojekt.pl/unit_napisy/dl.php?l=${napi_lang:-"PL"}&f=${md5}&t=${napi}&v=other&kolejka=false&nick=${napi_nick}&pass=${napi_pass}&napios=posix"

  # POBIERANIE ARCHIWUM 7Z Z NAPISAMI
  downloader "$url" "$dlfile" || return $?

  # SPRAWDZANIE ZAWARTOŚCI POBRANEGO PLIKU
  ### czasami jest tam tekst typu "Przerwa techniczna"
  local -i dlfile_size=0
  read dlfile_size < <(stat -c "%s" -- "$dlfile" 2> /dev/null)

  if [ "$dlfile_size" -le "200" ]; then
    local dlfile_mime
    read dlfile_mime < <(file \
      --brief \
      --exclude apptype \
      --exclude encoding \
      --exclude tokens \
      --exclude cdf \
      --exclude compress \
      --exclude elf \
      --exclude tar \
      --exclude soft \
      --mime-type \
      -- "$dlfile")

    if [ "$dlfile_mime" = "text/plain" ]; then
      local dlfile_source
      read dlfile_source < "$dlfile"
      unlink -- "$dlfile"
      case "${dlfile_source,,}" in
        *"przerwa techniczna"*) return 200;;
        npc0) return 1;;
        *) return 201;;
      esac
    fi
  fi


  # ROZPAKOWYWANIE NAPISÓW
  "$BIN_7z" x -y -so -piBlm8NTigvru0Jr0 "$dlfile" > "$subfile" 2>/dev/null
  test -f "$dlfile" && unlink -- "$dlfile"

  # PLIK JEST PUST (A NIE POWINIEN)
  if ! [ -s "$subfile" ]; then
    unlink -- "$subfile"
    return 202
  fi

  return 0
}

plugin subfmt_convert/fps

subfmt_convert__fps() {
  fps_ret=
  test "${#BIN_subotage__fps_f[@]}" = "0" && return 1

  local fid="$1" LANG=C
  local i bin func fps_sub file="${files[$fid]}"

  for ((i=0; i <= ${#BIN_subotage__fps_f[@]}; i++)); do
    bin="${BIN_subotage__fps_b[i]}"
    func="${BIN_subotage__fps_f[i]}"
    # subfmt_convert__fps__* ustawiają zmianną fps
    subfmt_convert__fps__$func \
      && [ -n "$fps_sub" ] \
      && printf -v fps_ret -- "%.3f" "$fps_sub" \
      && return 0
  done

  return 1
  #RETURNVAR: $fps_ret
}

subfmt_convert__fmt_detect() {
  local file="$1" line
  local -i c=0 max_lines=40

  fmt_detect_ret=

  local NB_FD_FILE
  exec {NB_FD_FILE}<"$file"

  # szybsze od subotage.sh o ok 10 do 300 razy... Wykrywa te same formaty.
  while read -u $NB_FD_FILE line; do
    c+=1; test "$c" -gt "$max_lines" && break

    case "${line:0:1}" in
      "{"|'[')
        case "$line" in
          '{'+([0-9])'}{'*([0-9])'}'*)
            fmt_detect_ret='microdvd'
            break
            ;;
          '['+([0-9])']['*([0-9])']'*)
            fmt_detect_ret='mpl2'
            break
        esac
        ;;
      0)
        case "$line" in
          00:[0-5][0-9]:[0-5][0-9],[0-9][0-9][0-9]' --> '00:[0-5][0-9]:[0-5][0-9],[0-9][0-9][0-9]*)
            fmt_detect_ret='subrip'
            break
            ;;
          00:[0-5][0-9]:[0-5][0-9]:[0-9][0-9],00:[0-5][0-9]:[0-5][0-9]:[0-9][0-9]*)
            fmt_detect_ret='subview'
            break
            ;;
          0?(0):[0-5][0-9]:[0-5][0-9]:*)
            fmt_detect_ret='tmplayer'
            break
            ;;
          +([0-9])' : 00:'[0-5][0-9]:[0-5][0-9]:[0-9][0-9]+( )00:[0-5][0-9]:[0-5][0-9]:[0-9][0-9]*)
            fmt_detect_ret='fab'
            break
        esac
    esac
  done

  exec {NB_FD_FILE}>&-

  # fallback - tak na wszelki wypadek, nigdy nic nie wiadomo...
  if [ -z "$fmt_detect_ret" ] && [ -n "$BIN_subotage" ]; then
    enabled "$DEBUG" && echo "!! [ fmt_detect ]: fallback do subotage..."

    local -a ifmt
    IFS=" " read -a ifmt < <(LANG=C "$BIN_subotage" -gi "$file" 2>&1)

    case "${ifmt[0]}" in
      microdvd|mpl2|subrip|tmplayer|subviewer|fab) fmt_detect_ret="${ifmt[0]}";;
    esac
  fi

  test -n "$fmt_detect_ret"
  #RETURNVAR: $fmt_detect_ret
}

subfmt_convert() {
  ext="${subext:-"txt"}"

  # Wyjdź jeśli nie ustawiono: subext_autodetect i subfmt_conv
  enabled "$subext_autodetect" || test -n "$subfmt_conv" || return 0

  # Zmienne lokalne
  local fid="$1" md5="$3" napi="$4"
  local subfile="$CACHE/_${md5}_${napi}.sub"
  local subftmp="$CACHE/_${md5}_${napi}.subfmt.tmp"
  local output fmt_detect_ret i fps_ret fps_opts

  # Detekcja (potrzebne dla obu: subext_autodetect i subfmt_conv)
  subfmt_convert__fmt_detect "$subfile" >&2 || return 100
  enabled "$subext_autodetect" && ext="${subfmt_ext[$fmt_detect_ret]}"

  # Zatrzymaj tutaj jeśli nie ustawiono subfmt_conv
  test -z "$subfmt_conv" && return 0

  # Konwersja nie jest potrzebna, plik już jest w tym formacie
  test "$subfmt_conv" = "$fmt_detect_ret" && return 0

  # Jedyny obsługiwany format, który wymaga FPS pliku filmowego to microdvd
  test "$subfmt_conv" = "microdvd" && fps_opts="-fo"
  test "$fmt_detect_ret"  = "microdvd" && fps_opts="-fi"

  # Wczytywanie FPS dla konwersji z/do microdvd
  if [ -n "$fps_opts" ]; then
    if ! subfmt_convert__fps "$fid" >&2; then
      return 101
    fi
  fi

  # Czas wygasania napisu
  local lasting_time_opts
  [[ "$subfmt_lasting_time" = +([0-9]) ]] \
    && lasting_time_opts="--lasting-time $subfmt_lasting_time"

  # Przeprowadzanie konwersji i zapis stanu w subostat
  local subostat='Done' line

  while read line; do
    if [ "$subostat" = "Done" ]; then
      case "$line" in
        "An error occured"*) subostat='Error';;
      esac
    else
      subostat="$line"
      break
    fi
  done < <(LANG=C "$BIN_subotage" $fps_opts $fps_ret $lasting_time_opts \
    -if "$fmt_detect_ret" \
    -i "$subfile" \
    -o "$subftmp" \
    -of "$subfmt_conv" 2>&1)

  # Przeprowadzanie post-operacji według subostat
  case "$subostat" in
    'Done')
      test -f "$subftmp" \
        && copyfile "$subftmp" "$subfile" &> /dev/null \
        && unlink -- "$subftmp" \
        || return 101
      ;;
    "No convertion is needed"*)
      true
      ;;
    *)
      enabled "$DEBUG" && echo "!! [ subotage ]: $subostat"
      return 101
  esac

  # Rozszerzenie zgodne z formatem lub subext
  test -n "${subfmt_ext[$subfmt_conv]}" \
    && ext="${subfmt_ext[$subfmt_conv]}" \
    || ext="${subext:-"txt"}"

  return 0

  #RETURNVAR: $ext
}

subcp_convert() {
  test -n "$subcp_conv" || return 0
  local md5="$3" napi="$4"

  # LOKALIZACJE PLIKU
  local subfile="$CACHE/_${md5}_${napi}.sub"

  # DETEKCJA KONWERTOWANIA
  local from to="$subcp_conv" subcp_conv_to="$subcp_conv" enctype

  read enctype < <(file \
    --brief \
    --mime-encoding \
    --exclude apptype \
    --exclude tokens \
    --exclude cdf \
    --exclude compress \
    --exclude elf \
    --exclude soft \
    --exclude tar \
    -- "${subfile}")

  if [ "$?" = "0" ] && [ -n "$enctype" ]; then
    case "${enctype,,}" in
      *utf*32*) from="UTF32";;
      *utf*16*) from="UTF16";;
      *utf*) from="UTF8";;
      *iso*) from="ISO-8859-2";;
      *ascii*) to= ;;
      *) from="WINDOWS-1250";;
    esac
  else
    to= from=
  fi

  # PORÓWNYWANIE STRON KODOWYCH I ZAPIS
  if [ "$from" != "$to" ]; then
    # Tworzenie kopii zapasowa oryginału
    local subftmp="$CACHE/_${md5}_${napi}.subcp.tmp"
    if ! copyfile "$subfile" "$subftmp" &> /dev/null; then
      test -f "$subftmp" && unlink -- "$subftmp"
      return 102
    fi

    # Manipulacja w celu zapobieżenia:            (v2)
    # "iconv: błędna sekwencja wejściowa na pozycji 0"
    # https://github.com/3ed/napi-bash/issues/6
    local translit_append=""
    if [ "${from:0:3}" = "UTF" ]; then
		translit_append="//TRANSLIT"
	fi

    # Konwertowanie
    "$BIN_iconv" \
      -f "$from" \
      -t "$to$translit_append" > "$subftmp" 2> /dev/null < "$subfile" \
    && copyfile "$subftmp" "$subfile" &> /dev/null

    # SPrzątanie
    local ret=$?
    unlink -- "$subftmp"
    test "$ret" = "0" || return 102
  fi

  return 0
}

subeol_convert() {
  test -n "$subeol_conv" && test -n "$BIN_dos2unix" || return 0

  local md5="$3" napi="$4"
  local subfile="$CACHE/_${md5}_${napi}.sub"

  "$BIN_dos2unix" --quiet "$subfile" &> /dev/null
}

sub_install() {
  local fid="$2" md5="$3" napi="$4"

  local path="${files[$fid]%.*}"
  local pref="${subext_prefix:+"."}$subext_prefix"

  local from="$CACHE/_${md5}_${napi}.sub"
  local to="$path$pref.$subext"

  # ILITERACJA W NAZWIE W PRZYPADKU BRAKU PRZYWOLENIA NA NADPIS
  if [ -e "$to" ] && [ "$suboverwrite" = "0" ]; then
    local -i c
    for ((c=0; $c < 100; c++)); do
      test -f "$path.$c$pref.$subext" || break
    done
    to="$path.$c$pref.$subext"
  fi

  # INSTALACJA PLIKU (I GDY PLIK JEST PUSTY)
  if ! copyfile "$from" "$to" &> /dev/null; then
    if [ -f "$to" ]; then
      unlink -- "$to"
      return 3
    else
      return 2
    fi
  fi

  return 0
}


#-------------------------------------------------------------------------------
#  Sprawdzanie
#-------------------------------------------------------------------------------
uri_encode() {
  # http://stackoverflow.com/questions/296536/urlencode-from-a-bash-script/2027690
  local string="$1" o LANG=C
  local -i i=0

  uri_encode_ret=

  for (( i=0 ; i<${#string} ; i++ )); do
    c="${string:$i:1}"
    case "$c" in
      [\&\(\)\?\!\;@#$-+=_,.:~/a-zA-Z0-9]) o="${c}" ;;
      *)
        printf -v o -- '%%%02x' "'$c"
    esac
    uri_encode_ret+="${o}"
  done

  #RETURNVAR: $uri_encode_ret
}

validate_auth() {
  if enabled "$auth_login"; then
    # obsługa starego sposóbu (bez uri_encode) lub
    # funkcja została już wcześniej wykonana...
    test -n "$napi_nick" && test -n "$napi_pass" && return 0
  else
    # logowanie jest wyłączone
    unset napi_nick napi_pass
    return 0
  fi

  local auth_status="Zły login lub hasło"
  local uri_encode_ret auth_status_file

  if [ -n "$auth_nick" ] && [ -n "$auth_pass" ] \
    && uri_encode "$auth_nick" \
    && napi_nick="$uri_encode_ret" \
    && uri_encode "$auth_pass" \
    && napi_pass="$uri_encode_ret"
  then
    local url="http://www.napiprojekt.pl/users_check.php?nick=$napi_nick&pswd=$napi_pass"

    read auth_status_file < <(mktemp -u -p "$NB_CACHEDIR" auth_stat.$$.XXXX)

    if downloader "$url" "$auth_status_file"; then
      read auth_status < "$auth_status_file"
      unlink -- "$auth_status_file"
      test "$auth_status" = "ok" && return 0
    fi
  fi

  warn "Autoryzacja nie powiodła się: $auth_status"
  unset napi_nick napi_pass

  return 1
}

setup_notification() {
  if enabled "$notifications"; then
    test -n "$NB_notify_tool" && return 0
  else
    unset NB_notify_tool NB_notify_fmt
  fi

  local findbin_ret

  case "$OSTYPE" in
    linux*|bsd*|solaris*) test -n "$DISPLAY" || return 0
  esac

  if [ "${#notifications_tools[@]}" = "0" ]; then
    notifications_tools=(${!notifications_fmt[@]})
  fi

  for i in "${notifications_tools[@]}"; do
    findbin "$i" \
      && NB_notify_tool="${i//[^[:alnum:]]/_}" \
      && NB_notify_fmt="${notifications_fmt[$i]:-"plain"}" \
      && return 0
  done
  return 1
}

setup_var_sublang() { #TODO
  test -n "$sublang" || return 0
  local uri_encode_ret
  uri_encode "$sublang" \
    && napi_lang="$uri_encode_ret" # TODO: sprawdzanie czy język jest obsługiwany
  unset sublang
  return 0
}

setup_var_filesize() {
  # równo lub więcej (równo 10MiB to min rozmiar pliku dla hasha)
  [[ "$filesize_ge" = +([0-9]) ]] \
    && [ "$filesize_ge" -ge "10" ] \
    || filesize_ge=10

  # równo lub mniej (puste = bez limitu)
  [[ "$filesize_le" = +([0-9]) ]] \
    && [ "$filesize_le" -lt "10" ] \
    || unset filesize_le
}

setup_bin_iconv() {
  test -n "$subcp_conv" || return 0

  case "${subcp_conv,,}" in
    u*32*) subcp_conv="UTF32";;
    u*16*) subcp_conv="UTF16";;
    u*) subcp_conv="UTF8";;
    i*) subcp_conv="ISO-8859-2";;
    w*) subcp_conv="WINDOWS-1250";;
    *)
      subcp_conv=""
      warn "Nieobsługiwana strona kodowa dla subcp_conv, pomijam..."
      return 0
  esac

  local findbin_ret
  if findbin iconv; then
    BIN_iconv="$findbin_ret"
  else
    subcp_conv=""
    warn "Aby móc korzystać z opcji subcp_conv, zainstaluj iconv..."
  fi
}

setup_bin_subotage() {
  test -n "$subfmt_conv" || return 0

  case "${subfmt_conv,,}" in
    f*)    subfmt_conv=fab;;
    mi*)   subfmt_conv=microdvd;;
    mp*)   subfmt_conv=mpl2;;
    subv*) subfmt_conv=subviewer;;
    subr*) subfmt_conv=subrip;;
    t*)    subfmt_conv=tmplayer;;
    *)
      subfmt_conv=""
      warn "Nieobsługiwana format napisów dla subfmt_conv, pomijam..."
      return 1
  esac

  local findbin_ret
  if findbin subotage.sh subotage; then
    BIN_subotage="$findbin_ret"
    setup_bin_subotage__fps
  else
    subfmt_conv=""
    warn "Aby móc korzystać z opcji subfmt_conv, zainstaluj subotage..."
  fi
}

setup_bin_subotage__fps() {
  local i f bin findbin_ret

  for i in "${fps_detect_tools[@]}"; do
    if findbin "$i"; then
      f="${i//[^[:alnum:]]/_}"
      if is_function "subfmt_convert__fps__$f"; then
        BIN_subotage__fps_b+=("$findbin_ret")
        BIN_subotage__fps_f+=("$f")
      fi
    fi
  done

  if [ "${#BIN_subotage__fps_f[@]}" = "0" ]; then
    local tools
    for i in "${fps_detect_tools[@]}"; do
      tools+="${tools:+", "}$i"
    done
    warn "Konwersja formatów wymaga detekcji ilości klatek na sekundę. Zainstaluj: $tools"
  fi
}

setup_bin_dos2unix() {
  test -n "$subeol_conv"  || return 0
  test -n "$BIN_dos2unix" && return 0

  local findbin_ret

  case "${subeol_conv,,}" in
    u*|l*) findbin dos2unix;;
    d*|c*) findbin unix2dos;;
    *)
      unset subeol_conv
      warn "Nieprawidłowa wartość dla opcji subeol_conv"
      return 1
  esac

  BIN_dos2unix="$findbin_ret"

  if [ -z "$BIN_dos2unix" ]; then
    unset BIN_dos2unix subeol_conv
    warn "Aby móc konwertować znak końca linii, zainstaluj: dos2unix"
    return 1
  fi
}

setup_bin_perl() {
  test -n "$BIN_perl" && return 0

  local findbin_ret
  if findbin perl; then
    BIN_perl="${BIN_perl:-"$findbin_ret"}"
    return 0
  fi

  return 1
}

setup_bin_realpath() {
  test -z "$BIN_realpath" && enabled "$subretry_store" || return 0

  local findbin_ret
  if findbin realpath; then
    BIN_realpath="$findbin_ret"
  elif setup_bin_perl; then
    BIN_realpath=realpath
    realpath() { perl -MCwd -e 'print Cwd::abs_path shift' "$@"; }
  else
    #warn ", zainstaluj: realpath lub perl"
    subretry_store=nie
    subretry_restore=nie
    return 1
  fi
  return 0
}

setup_bin_7z() {
  local findbin_ret
  if findbin 7za 7z; then
    BIN_7z="$findbin_ret"
  else
    error "Aby móc rozpakować archiwum z napisami, zainstaluj: p7zip..."
  fi
  #RETURNVAR: BIN_7z
}

findbin() {
  findbin_ret=

  while [ -n "$1" ]; do
    hash "$1" &> /dev/null && findbin_ret="$1" && return 0
    shift
  done

  return 1

  #RETURNVAR: $findbin_ret
}

#-------------------------------------------------------------------------------
#  Główny kod / main()
#-------------------------------------------------------------------------------
main() {
  local -i ret=0
  early "$@" || return 1
  ARGV "$@" || return 1
  napi_poll || ret=2
  napi_raport
  exit $ret
}

# Nie wykonuj funkcji main, gdy użyty przez source
[ "${BASH_SOURCE[0]}" != "${0}" ] || main "$@"
