#!/bin/bash # Version: 0.0.1 # Copyright (c) 2017: # Darren 'Tadgy' Austin # Licensed under the terms of the GNU General Public License version 3. # # Automatically or interractively merge tagfiles from two directories into a new set. # # The (space separated) list of package sets. PACKAGE_SETS="${PACKAGE_SETS:-a ap d e f k kde kdei l n t tcl x xap xfce y}" #........1.........2.........3.........4.........5.........6.........7.........8.........9.........0.........1.........2.........3.....:...4 display_help() { # |--------1---------2---------3---------4---------5---------6---------7---------8 echo "Usage: ${0##*/} [options] " echo "Automatically or interactively merge tagfiles from and" echo ", writing the resultant tagfile set to . Priorities " echo "from take presidence over those in ." echo echo "Options:" echo " -a, --default-add Do not interactively prompt whether to ADD or SKP any new" echo " packages in ; automatically use this default." echo " -s, --default-skp See above." echo " -v, --version Display version and copyright information." echo " -h, --help Display this help, obviously." echo " -- Force the end of option processing. Any options following" echo " this are treated as arguments." echo "Option processing ceases with the first non-option or --." echo echo "Arguments (all of which are mandatory):" echo " The top-level directory of the first set of tagfiles." echo " The top-level directory of the second set of tagfiles." echo " Path to the directory in which to write the new tagfiles." return 0 } display_version() { # |--------1---------2---------3---------4---------5---------6---------7---------8 echo "${0##*/} v0.0.1" echo "Copyright (c) 2017 Darren 'Tadgy' Austin " echo "Licensed under the terms of the GNU GPL v3 ." echo "This program is free software: you can modify or redistribute it in accordence" echo "with the GNU GPL. However, it comes with ABSOLUTELY NO WARRANTY; not even the" echo "implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." return 0 } check_directory() { # Params: $1 = Directory to check. # Returns: 0 = OK. # 1 = Doesn't exist. # 2 = Not a directory. # 3 = No read permission. # 4 = No write permission. if [[ ! -e "$1" ]]; then echo "${0##*/}: no such directory: $1" >&2 return 1 elif [[ ! -d "$1" ]]; then echo "${0##*/}: not a directory: $1" >&2 return 2 elif [[ ! -r "$1" ]]; then echo "${0##*/}: read permission denied: $1" >&2 return 3 elif [[ ! -w "$1" ]]; then echo "${0##*/}: write permission denied: $1" >&2 return 4 fi return 0 } write_line() { # Params: $1 = Package. # $2 = Priority. # $3 = Comment. # $4 = File to write to. if [[ ! -z "$3" ]]; then printf "%s:%s\t\t%s\n" "$1" "$2" "$3" >>$4 || exit 1 else printf "%s:%s\n" "$1" "$2" >>$4 || exit 1 fi } prompt_priority() { # Params: $1 = Package. # $2 = Priority. # $3 = Comment. # Variables: Modifies NEWPRI variable. echo echo echo "Package: $1" >&1 echo "Current priority: ${2:-(none)}" echo "Comment: ${3:-(none)}" echo local ANSWER while :; do read -r -n 1 -p "Select new tag for package: (A)DD or (S)KP: " ANSWER if [[ "${ANSWER^^}" = "A" ]]; then NEWPRI="ADD" break elif [[ "${ANSWER^^}" = "S" ]]; then NEWPRI="SKP" break else continue fi done } process_tagfile() { # Params: $1 = Input tagfile # $2 = Output tagfile # Clear the destination tagfile. : >$2 # Open the input tagfile for reading. # Note: We do this with an fd so we can use another 'read' of stdin for prompting. exec {FD}<"$1" # Process the tagfile line by line. local PACKAGE PRIORITY COMMENT NEWPRI while IFS=$'\t :' read -r -u "$FD" PACKAGE PRIORITY COMMENT; do if [[ "${PRIORITY^^}" = "ADD" || "${PRIORITY^^}" = "SKP" ]]; then # ADD or SKP. write_line "$PACKAGE" "${PRIORITY^^}" "$COMMENT" "$2" else # REC or OPT if [[ ! -z "$DEFAULT_PRIORITY" ]]; then # Use default priority. write_line "$PACKAGE" "$DEFAULT_PRIORITY" "$COMMENT" "$2" else # Prompt for new priority. prompt_priority "$PACKAGE" "${PRIORITY^^}" "$COMMENT" # Has modified the NEWPRI variable write_line "$PACKAGE" "$NEWPRI" "$COMMENT" "$2" fi fi done # Close tagfile file descriptor. exec {FD}<&- } merge_tagfiles() { # Params: $1 = Tagfile 1 # $2 = Tagfile 2 # $3 = Output tagfile # Open the first tagfile for reading. # Note: We do this with an fd so we can use another 'read' of stdin for prompting. exec {FD}<"$1" local PACKAGE1 PRIORITY1 COMMENT1 PACKAGE2_RHS NEWPRI while IFS=$'\t :' read -r -u "$FD" PACKAGE1 PRIORITY1 COMMENT1; do PRIORITY2="$(egrep "^${PACKAGE1//+/\\+}:" "$2" | cut -d: -f2- | sed -re 's/[[:blank:]]+.*$//')" COMMENT2="$(egrep "^${PACKAGE1//+/\\+}:" "$2" | cut -d: -f2- | sed -re 's/(ADD|SKP|REC|OPT)//')" if [[ -z "$PRIORITY2" ]]; then # Package wasn't in tagfile2. if [[ ! -z "$DEFAULT_PRIORITY" ]]; then # Use default priority. write_line "$PACKAGE1" "$DEFAULT_PRIORITY" "" "$3" else # Prompt for new priority. prompt_priority "$PACKAGE1" "${PRIORITY1^^}" "$COMMENT1" # Has modified the NEWPRI variable write_line "$PACKAGE1" "$NEWPRI" "$COMMENT1" "$3" fi elif ! [[ "${PRIORITY2^^}" =~ ^(ADD|SKP|REC|OPT) ]]; then # Package was listed in tagfile2, but priority is invalid. if [[ ! -z "$DEFAULT_PRIORITY" ]]; then # Use default priority. write_line "$PACKAGE1" "$DEFAULT_PRIORITY" "" "$3" else # Prompt for new priority. prompt_priority "$PACKAGE1" "${PRIORITY2^^} (invalid)" "$COMMENT2" # Has modified the NEWPRI variable write_line "$PACKAGE1" "$NEWPRI" "$COMMENT2" "$3" fi else # Package was listed in tagfile2 and priority was valid. write_line "$PACKAGE1" "${PRIORITY2^^}" "$COMMENT2" "$3" fi done } # Process command line options. while :; do case "$1" in '-a'|'-default-add'|'--default-add') if [[ "$DEFAULT_PRIORITY" = "SKP" ]]; then echo "${0##*/}: options '$1' and '-s|--default-skp' are incompatible" >&2 exit 1 else DEFAULT_PRIORITY="ADD" fi shift ;; '-s'|'-default-skp'|'--default-skp') if [[ "$DEFAULT_PRIORITY" = "ADD" ]]; then echo "${0##*/}: options '$1' and '-a|--default-add' are incompatible" >&2 exit 1 else DEFAULT_PRIORITY="SKP" fi shift ;; '-v'|'-version'|'--version') display_version exit 0 ;; '-h'|'-help'|'--help') display_help exit 0 ;; '--') shift break ;; -*) # This prevents the first non-option argument being an entry in the # current directory which begins with a '-'. Use the '--' option # or ./-foo where this becomes a problem. echo "${0##*/}: invalid option: $1" >&2 echo "Try: ${0##*/} --help" >&2 exit 1 ;; *) break ;; esac done # Process command line arguments. (( $# != 3 )) && { echo "${0##*/}: incorrect number of non-option arguments" >&2 echo "Try: ${0##*/} --help" >&2 exit 1 } for ARG in $*; do ERROR_TEXT="$(check_directory "$ARG" 2>&1)" ERROR_CODE=$? if [[ -z "$TAGFILES_DIR1" || -z "$TAGFILES_DIR2" ]]; then # and if (( $ERROR_CODE >= 1 && $ERROR_CODE <= 3 )); then # Ignore write permission failure. echo "$ERROR_TEXT" >&2 exit 1 else TAGFILES_DIR2="${TAGFILES_DIR1:+$ARG}" TAGFILES_DIR1="${TAGFILES_DIR1:-$ARG}" # Yes, these are correct and in the right order! fi else # if (( $ERROR_CODE >= 2 )); then # Non-existant (ERROR_CODE=1) is OK here. echo "$ERROR_TEXT" >&2 exit 1 else DEST_DIR="$ARG" fi fi done [[ -z "$TAGFILES_DIR1" || -z "$TAGFILES_DIR2" || -z "$DEST_DIR" ]] && { echo "Abort: argument processing failure" >&2 exit 1 } # Create destination directory. if [[ -e "$DEST_DIR" ]]; then echo "${0##*/}: directory exists: $DEST_DIR" >&2 exit 1 else mkdir -p -m 755 "$DEST_DIR" || exit 1 fi # Main loop. for SET in $PACKAGE_SETS; do mkdir "$DEST_DIR/$SET" || exit 1 if [[ ! -e "$TAGFILES_DIR1/$SET" ]]; then # No tagfile in dir1. if [[ -e "$TAGFILES_DIR2/$SET" ]]; then # Found tagfile in dir2 - process it and write to dest dir. process_tagfile "$TAGFILES_DIR2/$SET/tagfile" "$DEST_DIR/$SET/tagfile" || exit 1 else # No tagfile in either dir - create a stub file. touch "$DEST_DIR/$SET/tagfile" || exit 1 fi else # Found tagfile in dir1. if [[ ! -e "$TAGFILES_DIR2/$SET" ]]; then # Process tagfile in dir1 and write to dest dir. process_tagfile "$TAGFILES_DIR1/$SET/tagfile" "$DEST_DIR/$SET/tagfile" || exit 1 else # Found tagfiles in both dirs - merge the tagfiles into dest dir. merge_tagfiles "$TAGFILES_DIR1/$SET/tagfile" "$TAGFILES_DIR2/$SET/tagfile" "$DEST_DIR/$SET/tagfile" fi fi done echo