#!/bin/zsh # .files/install # (c) 2014-2021 MORI Taeyeon # Paths & Utils DOT="$(realpath "$(dirname "$0")")" cd "$DOT" setopt EXTENDED_GLOB source "$DOT/lib/libzsh-utils.zsh" # Parse commandline # ============================= OVERWRITE=false ASK=true INSTALL_ZSH=true INSTALL_DOT=true INSTALL_CNF=true INSTALL_GIT=true INSTALL_CHSH=true INSTALL_EXTRA=true function print_usage() { echo "Usage: $1 [--help] [--overwrite] [--dont-ask]" echo echo "Install Tae's dotfiles" echo echo "Parameters:" echo " -h, --help Display this help message" echo " --overwrite Just overwrite existing dotfiles" echo " --dont-ask Don't ask questions. Use default values" echo " --no-{zsh,dotfiles,config,git,chsh,extra}" echo " Don't set up the specific component" } for arg in "$@"; do [[ -z "$STATE" ]] && { case "$arg" in -h|--help) print_usage "$0"; exit 0;; --overwrite) OVERWRITE=true;; --dont-ask) ASK=false;; --no-zsh) INSTALL_ZSH=false;; --no-dotfiles) INSTALL_DOT=false;; --no-config) INSTALL_CNF=false;; --no-git) INSTALL_GIT=false;; --no-chsh) INSTALL_CHSH=false;; --no-extra) INSTALL_EXTRA=false;; *) print_usage "$0"; exit 1;; esac } || { case "$STATE" in # handle options with parameters esac STATE= } done [[ -n "$STATE" ]] && err "Option $STATE is missing it's parameter!" && exit 1 # Helper functions # ============================= function generate() { [[ -e "$1" ]] && ! $OVERWRITE && ! grep -q "Generated by .files/install" "$1" && \ err "Custom version of $1 detected. Please delete it before running .files/install" && exit 1 echo "${2-#} Generated by .files/install" >"$1" echo "${2-#} `date "+%m/%d/%Y %H:%M:%S"`" >>"$1" echo >>"$1" cat >>"$1" } function relink() { [[ ! -e "$1" ]] && err "No such file or directory: '$1'" && return 1 [[ -L "$2" ]] && rm -f "$2" [[ -e "$2" ]] && ! $OVERWRITE && err "File already exists and isn't a symbolic link: '$2'" && return 1 [[ ! -d "`dirname "$2"`" ]] && mkdir -p "`dirname "$2"`" color 31 ln -sr "$1" "$2" } # Prepare # ============================= # Update git modules msg "Updating git submodules..." git submodule update --init --recursive # Ask questions about user $ASK && msg "Asking questions..." || err "Not asking any questions:" # Try to load previous configuration [[ -e "$DOT/etc/user-info" ]] && source "$DOT/etc/user-info" [[ -e "$DOT/etc/hostname" ]] && HOSTNAME=`cat "$DOT/etc/hostname"` # Retrieve realname from /etc/passwd GECOS [[ -z "$REALNAME" ]] && REALNAME=`grep "^$USER:[^:]*:$UID:" /etc/passwd | cut -d: -f5 | cut -d, -f1` [[ -z "$HOSTNAME" ]] && HOSTNAME=`hostname` if $ASK; then ask " Real name" REALNAME ask " E-Mail address" EMAIL ask " Computer name" HOSTNAME else color 36 echo " default REALNAME=\"$REALNAME\"" color 36 echo " default EMAIL=\"$EMAIL\"" color 36 echo " default HOSTNAME=\"$HOSTNAME\"" fi # Write down answers color 32 echo " Writing etc/user-info" generate "$DOT/etc/user-info" <"$DOT/etc/hostname" # Handle local-only alternatives HOST_LC=${(L)HOSTNAME} function refset() { eval "$1=\$2" } function inspect_name() { # gimme namerefs... #local -n name=$1 prio=$2 #local filename=$3 # Check local-only files refset $1 ${3%.local} if [[ ${(P)1} != $3 ]]; then refset $2 3 return 0 fi # Check hostname-specific file refset $1 ${3%@*} if [[ ${(P)1} != $3 ]]; then refset $2 2 [[ ${3##*@} == $HOST_LC ]] return $? fi # Standard file refset $2 1 return 0 } function add_names() { local an_name an_prio #local -n names=$1 prios=$2 for an_file in ${(@)argv[3,$#]}; do if inspect_name an_name an_prio $an_file && [[ $an_prio -gt ${${(P)2}[$an_name]} ]]; then refset $1\[\$an_name\] $an_file refset $2\[\$an_name\] $an_prio fi done } # Write files to $HOME # ============================= # Setup dotfiles if $INSTALL_DOT; then msg "Linking dotfiles..." (){ local -A names prios pushd $DOT/dotfiles add_names names prios * for name src in ${(@kv)names}; do color 32 echo " Adding .$name" relink $src $HOME/.$name done popd } else msg "Skipped dotfiles." fi # Setup .config if $INSTALL_CNF; then : ${XDG_CONFIG_HOME:=~/.config} msg "Linking .config files... ($XDG_CONFIG_HOME)" (){ function relink_r() { local target=$1 dest=$2 name=${3:-$2} from [[ ${target:t} != ${dest:t} ]] && from=" (${target:t})" if [[ ! -d $target ]]; then color 32 echo " Adding $name$from" relink $target $dest else if [[ ! -d $dest ]]; then color 32 echo " Creating directory $name/$from" mkdir $dest elif [[ -n $from ]]; then color 32 echo " Adding directory $name/$from" fi # This is still slightly ugly local -A names prios add_names names prios $target/* for dst src in ${(@kv)names}; do relink_r $src $dest/${dst:t} $name/${dst:t} done fi } pushd $DOT/config local -A names prios add_names names prios * for dst src in ${(@kv)names}; do relink_r $src $XDG_CONFIG_HOME/$dst $dst done popd } else msg "Skipped .config" fi # Setup git # ============================= if $INSTALL_GIT; then msg "Setting up Git configuration..." GIT="$DOT/git" [[ -e "$HOME/.gitconfig" ]] && ! grep -q ".files/install" "$HOME/.gitconfig" && err "Custom ~/.gitconfig already exists! Please remove it before running .files/install" && exit 1 color 32 echo " Writing .gitconfig" generate "$HOME/.gitconfig" <>"$HOME/.gitconfig" <