#!/bin/bash # ------- # File: skel-install # Description: Simple (but useful) tool to install files in the skel # directory from cpkg scripts # Author: Luis Garcia Gisbert # (written for LliureX in June 2007) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston MA 02110-1301 USA # -------- set -e PATH="/usr/sbin:/usr/bin:/sbin:/bin" usage() { CMD_NAME="$(basename "$0")" exit_message \ "Usage: $CMD_NAME [options] [SOURCE_FILE|-] (SKEL_RELATIVE)TARGET_FILE\n\ where options are: [-n] [-t|--template] [-p|--preserve] [-m |--mode=MODE] [-o |--owner=OWNER] [-g |--group=GROUP]\n\ Installs \"SOURCE_FILE\" in \"$SKEL_DIR/TARGET_FILE\" (or in \"$TEMPLATES_DIR/TARGET_FILE\" if -t option is used)\n\ If \"SOURCE_FILE\" is omitted, or equals to "-", standard input is used, otherwise, it must exist.\n\ All the directory components of \"$SKEL_DIR/TARGET_FILE\" are created if required.\n\ If 'real target' exists \"(/TARGET_FILE)\", its MODE,OWNER and GROUP are used as default\n\ instead of $DEFMODE, $DEFOWNER:$DEFGROUP. Use parameters to override this values and/or behaviour." } exit_message() { [ "$VERBOSE" ] && echo -e "$1" >&2 exit 1 } skel_md(){ TDIR="$(dirname "$1")" # check if there is nothing to do [ ! -d "$TDIR" ] || return 0 mkdir -p "$TDIR" &>/dev/null || return 1 # remove first "./", add a "/" at end and remove superfluous "/" TDIR="$(echo "${TDIR}/"|sed -e "s%/\+%/%g;s%^\./%%")" # separate first dir component of TDIR FDIR="${TDIR%%/*}" TDIR="${TDIR#*/}" PDIR="/" while [ "$TDIR" ] ; do SUBDIR="${TDIR%%/*}" TDIR="${TDIR#*/}" PDIR="${PDIR}/${SUBDIR}" [ -d "${PDIR}" ] || return 0 DMODE="$(find "${PDIR}" -maxdepth 0 -printf "%m")" DOW_GR="$(find "${PDIR}" -maxdepth 0 -printf "%u:%g")" chmod "$DMODE" "${FDIR}/${PDIR}" || return 1 chown "${DOW_GR}" "${FDIR}/${PDIR}" || return 1 done return 0 } # variables # --------- SKEL_DIR="./skel" TEMPLATES_DIR="./templates" DEFMODE="644" DEFOWNER="root" DEFGROUP="root" MODE="" OWNER="" GROUP="" SOURCE_FILE="" TARGET_FILE="" VERBOSE="Y" # to store non-opt arguments declare -a ARGV # Bash arrays are 0-based, but we like ARGC to be equal to the number of non-option arguments. # To solve this issue, we use a "C-like" ARGV vector by setting ARGV[0] with the name of script # ('basename' of script for arbitrary cosmetic reasons), so real arguments begin with index 1 # the array will be ARGC+1 elements ARGC=0 ARGV[0]="$(basename "$0")" # main # ---- TARGET_DIR="$SKEL_DIR" while [ $# -ge 1 ] ; do case "$1" in --group=*) GROUP="${1#--group=}" shift ;; -g) # -g GROUP as equivalent to --group=GROUP [ -z "$2" ] && usage GROUP="$2" shift 2 ;; --owner=*) OWNER="${1#--owner=}" shift ;; -o) # -o OWNER as equivalent to --owner=MODE [ -z "$2" ] && usage OWNER="$2" shift 2 ;; --mode=*) MODE="${1#--mode=}" shift ;; -m) # -m MODE as equivalent to --mode=MODE [ -z "$2" ] && usage MODE="$2" shift 2 ;; -n) # short option (no-verbose) VERBOSE="" shift ;; -t|--template) # short option (use templates dir instead of skel) TARGET_DIR="$TEMPLATES_DIR" shift ;; -p|--preserve) # short option (use permisisons/owner/group from SOURCE) PRESERVE="YES" shift ;; -?*) # an invalid option -* or --* but not a single - usage ;; *) ARGC=$(( $ARGC +1 )) ARGV[$ARGC]="$1" shift ;; esac done case $ARGC in 0) # no files usage ;; 1) # just one file ... asume stdin TARGET_FILE="${ARGV[1]}" SOURCE_FILE="-" ;; 2) # 2 files, first is source and last is target SOURCE_FILE="${ARGV[1]}" TARGET_FILE="${ARGV[2]}" ;; *) # this (simple) script only install one file, so exit usage ;; esac PROTO_FILE="/${TARGET_FILE}" # check for source if [ "$SOURCE_FILE" != "-" ] ; then [ -r "$SOURCE_FILE" ] || exit_message "Unable read source file \"$SOURCE_FILE\"" if [ "$PRESERVE" ] ; then PROTO_FILE="$SOURCE_FILE" fi fi # check for target in "real world" if [ -f "${PROTO_FILE}" ] ; then DEFMODE="$(find "${PROTO_FILE}" -printf "%m")" DEFOWNER="$(find "${PROTO_FILE}" -printf "%u")" DEFGROUP="$(find "${PROTO_FILE}" -printf "%g")" fi REAL_TARGET="${TARGET_DIR}/${TARGET_FILE}" skel_md "$REAL_TARGET" &>/dev/null || exit_message "Unable to create directory for \"$REAL_TARGET\"" [ -f "$REAL_TARGET" ] || touch "$REAL_TARGET" &>/dev/null || exit_message "Unable to create file \"$REAL_TARGET\"" # use default values for unset variables chmod "${MODE:="$DEFMODE"}" "$REAL_TARGET" &>/dev/null || exit_message "Error setting file permissions to \"${MODE:="$DEFMODE"}\"" chown "${OWNER:="$DEFOWNER"}:${GROUP:="$DEFGROUP"}" "$REAL_TARGET" &>/dev/null || exit_message "Error setting owner and group to \"${OWNER:="$DEFOWNER"}:${GROUP:="$DEFGROUP"}\"" # OK, 'cat' over the target cat "$SOURCE_FILE" > "$REAL_TARGET" 2>/dev/null || exit_message "Error writing file \"$REAL_TARGET\"" exit 0