#!/bin/bash
[ "$UID" != 0 ] && { exec sudo bash "$0" && exit 0; }
export FONT="UniCyr_8x16" && if ! setfont "$FONT"; then exit 1; fi
#-----------------------------------------------------------------------------------------------;
declare -A COLOR=([BW]="30;47" [R]=31 [BR]="30;41" [G]=32 [Y]=33 [BY]="30;43" [M]=35 [N]=0)
function print_color { printf "\e[${COLOR[$1]}m%s\e[${COLOR[N]}m\n" "$2"; } && TARGET="/mnt"
#-----------------------------------------------------------------------------------------------;
title="Установка базовой системы Arch Linux EFI и вспомогательных пакетов." && version="2025-11-23"
printf '%s %s\n' "$(print_color M "$title")" "$(print_color BR "$version")" # (c) Геннадий Головин
#-----------------------------------------------------------------------------------------------;
print_color G "Для установки требуется смонтировать как минимум два раздела в каталог '$TARGET'."
print_color G "* Корневой раздел (/) минимальный размер 4 ГБ, рекомендуемый 20 ГБ или больше."
print_color G "* Раздел EFI (/boot/efi) 100 МБ достаточно для нескольких операционных систем."
print_color G "= Раздел подкачки (swap) на усмотрение пользователя, примерно 4 ГБ или больше."
#-----------------------------------------------------------------------------------------------;
print_color Y "Список блочных устройств и разделов на них."
lsblk -ao NAME,LABEL,FSTYPE,FSUSED,SIZE,UUID,MOUNTPOINTS
print_color Y "Монтирование разделов блочных устройств в каталог '$TARGET'." && printf '\n\n\n\n\e[4A'
#-----------------------------------------------------------------------------------------------;
function dropdown_menu {
  local dm_prompt="$1" p_len="$2" C="$3" vars=("${@:4}") && ((v_len = ${#vars[@]} + 1))
  readarray -t vars < <(printf ' %s ;\n' "НЕТ" "${vars[@]}" | column -t -s ';' -o '')
  printf '\r%s%s\e[K' "$dm_prompt" "<выбор>" && read -rs -n3 input
  tput civis # скрыть курсор
  while true; do
    ((pos < 0)) && ((pos = 0))
    ((pos >= v_len)) && ((pos = v_len - 1))
    printf '\r%s%s\e[K' "$dm_prompt" "[ ... ]"
    for ((i = 0; i < "$v_len"; i++)); do
      ((i == pos)) && color="${COLOR[$C]}" || color="${COLOR[BW]}"
      option=${vars[i]} && ((${#option} < 7)) && option=" $option "
      printf '\n\e[%sC\e[%sm%s\e[0m' "${p_len}" "${color}" "$option"
    done
    printf '\e[%sA' "${v_len}" && read -rs -n3 input
    case $(printf '%q\n' "$input" | tr -d "$'") in
      "\E[A") ((--pos)) ;; "\E[B") ((++pos)) ;; "") break ;;
    esac
  done
  tput cnorm # показать курсор
  dm_value="$(echo "${vars[pos]}" | tr -d ' ')"
}
#-----------------------------------------------------------------------------------------------;
while true; do
  if ((${#p_actions[@]} > 0)); then next="ещё один "; fi && text="Монтировать ${next}раздел? [Д/н]"
  prompt="$(print_color Y "$text") " && read -erp "$prompt" add && printf '\e[1A\e[K'
  case "${add,,}" in n | no | н | нет) printf '%s%s\n' "$prompt" "НЕТ" && break ;; esac
  t_prompt="Раздел:" && p_prompt="$(print_color Y "$t_prompt") "
  dm_p_len="$(printf '%s ' "$t_prompt" | wc -m)"
  readarray -t dm_list < <(lsblk -po NAME --filter 'TYPE=="part"' --noheadings)
  dropdown_menu "$p_prompt" "$dm_p_len" BY "${dm_list[@]}" && partition="$dm_value"
  [ "$partition" == "НЕТ" ] && { printf '\r\e[J' && continue; }
  printf '\r%s%s  \e[J' "$p_prompt" "$partition"
  grep -Fq "$partition" /proc/swaps && { print_color R "ОШИБКА: устройство уже смонтировано." && exit 1; }
  grep -Fq "$partition" /proc/mounts && { print_color R "ОШИБКА: устройство уже смонтировано." && exit 1; }
  t_system="файловая система:" && p_system="$(print_color Y "$t_system") "
  dm_prompt="$(printf '%s%s  %s' "$p_prompt" "$partition" "$p_system")"
  dm_p_len="$(printf '%s %s  %s ' "$t_prompt" "$partition" "$t_system" | wc -m)"
  dm_list=(ext4 fat32 swap)
  printf '\r%s\e[J' "$dm_prompt" && dropdown_menu "$dm_prompt" "$dm_p_len" BY "${dm_list[@]}" && system="$dm_value"
  [ "$system" == "НЕТ" ] && { printf '\r\e[J' && continue; }
  t_format="форматировать:" && p_format="$(print_color R "$t_format") "
  dm_prompt="$(printf '%s%s  %s%s  %s' "$p_prompt" "$partition" "$p_system" "$system" "$p_format")"
  dm_p_len="$(printf '%s %s  %s %s  %s ' "$t_prompt" "$partition" "$t_system" "$system" "$t_format" | wc -m)"
  printf '\r%s\e[J' "$dm_prompt" && dropdown_menu "$dm_prompt" "$dm_p_len" BR "ДА" && format="$dm_value"
  printf '\r%s%s  %s%s  %s%s  \e[J' "$p_prompt" "$partition" "$p_system" "$system" "$p_format" "$format"
  m_prompt="$(print_color Y "точка монтирования:") "
  if [ "$system" == "swap" ]; then
    [[ "$format" != "НЕТ" ]] && p_actions+=("mkswap --verbose $partition")
    printf '%s%s\n' "$m_prompt" "[SWAP]" && p_actions+=("swapon -v $partition") && continue
  fi
  read -erp "$m_prompt" -i "$TARGET/mnt" point && [ -z "$point" ] && { printf '\e[1A\e[K' && continue; }
  if [ "$format" != "НЕТ" ]; then
    case "$system" in
      "ext4") p_actions+=("mkfs.ext4 -v $partition") ;;
      "fat32") p_actions+=("mkfs.fat -v -F32 $partition") ;;
    esac
  fi
  p_actions+=("mount -v -m $partition $point")
done
#-----------------------------------------------------------------------------------------------;
print_color G "Название компьютера, имя пользователя и пароли для учётных записей не могут быть"
print_color G "пустыми, и не могут состоять только из одних пробелов. Название компьютера и имя"
print_color G "пользователя должны состоять как минимум из одной латинской буквы. Пароли должны"
print_color G "состоять как минимум из одной латинской буквы или другого непробельного символа."
#-----------------------------------------------------------------------------------------------;
function enter_the_name {
  prompt="$(print_color Y "$1:") " && placeholder="$2"
  while true; do
    read -erp "$prompt" -i "$placeholder" the_name && printf '\e[1A\e[K'
    error_message="$(print_color R "Введено некорректное значение!")"
    echo "$the_name" | grep -q '^[A-Za-z]' && break
    read -erp "$error_message" -s && printf '\e[1A\e[K'
  done && printf '%s%s\n' "$prompt" "$the_name"
}
#-----------------------------------------------------------------------------------------------;
enter_the_name "Название компьютера" "arch" && hostname="$the_name"
enter_the_name "Новый пользователь" "arch" && username="$the_name"
#-----------------------------------------------------------------------------------------------;
function enter_password_2times {
  while true; do
    prompt="$(print_color Y "$1:") " && unset password1 password2
    while [ -z "$password1" ]; do
      read -erp "$prompt" -s password1 && printf '\e[1A\e[K'
    done && prompt+="ВВЕДЁН  $(print_color Y "ещё раз:") "
    while [ -z "$password2" ]; do
      read -erp "$prompt" -s password2 && printf '\e[1A\e[K'
    done && printf '%s%s\n' "$prompt" "ВВЕДЁН"
    [ "$password1" == "$password2" ] && break
    error_message="$(print_color R "Пароли не совпадают!")"
    printf '\e[1A\e[K' && read -erp "$error_message" -s && printf '\e[1A'
  done
}
#-----------------------------------------------------------------------------------------------;
enter_password_2times "Пароль нового пользователя" && user_pass="$password1"
enter_password_2times "Пароль суперпользователя" && root_pass="$password1"
#-----------------------------------------------------------------------------------------------;
p="$(print_color M "Ввод необходимых данных завершён. Приступить к установке? [д/Н]") " && read -erp "$p" line
printf '\e[1A%s\e[K' "$p" && case "${line,,}" in y | yes | д | да) echo "ДА" ;; *) echo "НЕТ" && exit 0 ;; esac
#-----------------------------------------------------------------------------------------------;
print_color Y "Форматирование разделов блочных устройств и монтирование в каталог '$TARGET'."
# сначала форматирование всех разделов, потом монтирование swap, root и далее по порядку
printf '%s\n' "${p_actions[@]}" | sort -k5 | while read -r p_action; do
  print_color BW "$p_action" && { echo y | $p_action || exit 1; }
done
#-----------------------------------------------------------------------------------------------;
print_color Y "Зеркало для скачивания 'yandex'." && mirror="Server = https://mirror.yandex.ru"
sed -i -e 's|^Server|#Server|g' -e "s|#$mirror|$mirror|" /etc/pacman.d/mirrorlist
#-----------------------------------------------------------------------------------------------;
print_color R "Установка базовой системы и вспомогательных пакетов в каталог '$TARGET'."
pacstrap -K "$TARGET" base linux linux-firmware grub efibootmgr sudo networkmanager
#-----------------------------------------------------------------------------------------------;
print_color Y "Создание таблицы fstab из точек монтирования внутри каталога '$TARGET'."
new_fstab="$TARGET/etc/fstab" && cat >"$new_fstab" <<FSTAB
# Static information about the filesystems. See fstab(5) for details.
#   <file system>   <dir>   <type>   <options>   <dump>   <pass>
FSTAB
# сформировать таблицу, исключить пустые строки и лишние пробельные символы
genfstab -U -p "$TARGET" | grep '\S' | sed -E 's|\s+|  |g' >>"$new_fstab"
# вывести таблицу в удобочитаемой форме, где все строки одинаковой ширины, для наглядности
readarray -t pretty_print < <(awk '{print $0";"}' "$new_fstab" | column -t -s ';' -o '')
for pretty_line in "${pretty_print[@]}"; do print_color BW "$pretty_line"; done
#-----------------------------------------------------------------------------------------------;
echo "Часовой пояс 'Europe/Moscow'."
ln -rsf "$TARGET/usr/share/zoneinfo/Europe/Moscow" "$TARGET/etc/localtime"
echo "Установка табельных часов." && arch-chroot "$TARGET" hwclock --systohc
#-----------------------------------------------------------------------------------------------;
echo "Локализация 'ru_RU' и 'en_US'." && export LANG="ru_RU.UTF-8"
sed -i -E 's/#((ru_RU|en_US).UTF-8)/\1/g' "$TARGET/etc/locale.gen"
echo "Локализация системы '$LANG'."
echo "LANG=$LANG" >"$TARGET/etc/locale.conf"
echo "Генерация файлов локализации." && arch-chroot "$TARGET" locale-gen
#-----------------------------------------------------------------------------------------------;
echo "Шрифт и раскладка клавиатуры в консоли. Переключение по ALT+SHIFT."
printf '%s\n' "FONT=$FONT" "KEYMAP=ruwin_alt_sh-UTF-8" >"$TARGET/etc/vconsole.conf"
#-----------------------------------------------------------------------------------------------;
echo "Настройка 'sudo', допуск для группы 'wheel' со вводом пароля."
sed -i -E 's/#\s*(%wheel\s*ALL=\(ALL:ALL\)\s*ALL)\s*$/\1/' "$TARGET/etc/sudoers"
#-----------------------------------------------------------------------------------------------;
echo "Собственное название компьютера и отображение в сети."
echo "$hostname" >"$TARGET/etc/hostname"
echo "127.0.1.1  $hostname.home  $hostname" >>"$TARGET/etc/hosts"
echo "Включение службы Network Manager."
arch-chroot "$TARGET" systemctl enable NetworkManager
#-----------------------------------------------------------------------------------------------;
echo "Установка пароля для суперпользователя."
arch-chroot "$TARGET" bash -c "echo '$root_pass' | passwd -s"
#-----------------------------------------------------------------------------------------------;
echo "Создание нового пользователя и добавление в группу 'wheel'."
arch-chroot "$TARGET" useradd -m "$username"
arch-chroot "$TARGET" usermod -aG wheel,audio,video,storage "$username"
#-----------------------------------------------------------------------------------------------;
echo "Установка пароля для нового пользователя."
arch-chroot "$TARGET" bash -c "echo '$user_pass' | passwd -s '$username'"
#-----------------------------------------------------------------------------------------------;
echo "Добавление версий собранных файлов 'vmlinuz' и 'initramfs' для наглядности."
kernel="linux" && version="$(file -b "$TARGET/boot/vmlinuz-$kernel" | cut -d' ' -f8)"
test -d "$TARGET/usr/lib/modules/$version" || version="rolling"
echo "Установлено ядро операционной системы 'Linux $version'."
mv -f "$TARGET/boot/vmlinuz-$kernel" "$TARGET/boot/vmlinuz-$version"
mv -f "$TARGET/boot/initramfs-$kernel.img" "$TARGET/boot/initramfs-$version.img"
#-----------------------------------------------------------------------------------------------;
print_color R "Установка приложения GRUB EFI x86_64 и его модулей в каталог '$TARGET/boot'."
grub_options=(--target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB --removable)
arch-chroot "$TARGET" grub-install "${grub_options[@]}"
print_color R "Создание конфигурации загрузочного меню GRUB."
arch-chroot "$TARGET" grub-mkconfig -o /boot/grub/grub.cfg
#-----------------------------------------------------------------------------------------------;
print_color M "Установка базовой системы и вспомогательных пакетов завершена."
print_color M "Можно извлечь установочные носители и перезагрузить компьютер."
