1#!/bin/sh - 2#@ gen-uushar-wrapper creates a shar(1)-like shell archive (but with 3#@ uuencode(1)d content) of given files (read from STDIN or ARGV), that is 4#@ itself executable. 5#@ Execute it (the generated archive) to invoke any of the programs contained 6#@ therein. On the first run with arguments, the wrapper will create a hidden 7#@ directory in your $TMPDIR to unpack the archive therein; without arguments 8#@ it will always print creation time and members shipped. 9# 10# Copyright (c) 2012 - 2021 Steffen Nurpmeso <steffen@sdaoden.eu>. 11# 12# Permission to use, copy, modify, and/or distribute this software for any 13# purpose with or without fee is hereby granted, provided that the above 14# copyright notice and this permission notice appear in all copies. 15# 16# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 17# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 18# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 19# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 20# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 21# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 22# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 23 24## -- >8 -- 8< -- ## 25ARGS=${@} 26 27echo 'So let us create an uuencoded shell archive of the given utilities.' 28echo 'Please answer the upcoming questions for this to work.' 29echo 'You can at any time interrupt with CTRL-C.' 30 31printf '\nWhat should be the name of the wrapper: [] ' 32read SHAR 33if [ -z "${SHAR}" ]; then 34 echo >&2 'Cannot use the empty string for that, bailing out.' 35 exit 1 36fi 37SHAR="${SHAR}.sh" 38if [ -f "${SHAR}" ]; then 39 echo >&2 "\`${SHAR}' already exists, bailing out." 40 exit 1 41fi 42 43printf "Yay, it will be \`${SHAR}'.\n\n%s\n%s\n%s" \ 44 "The special command \`+' should map to a subcommand?" \ 45 "(As in: '\$ ${SHAR} + -h' -> '\$ SUBCOMMAND -h')?" \ 46 'Enter desired command or the empty string otherwise [] ' 47read SHAR_DEFEXEC 48if [ -n "${SHAR_DEFEXEC}" ]; then 49 SHAR_DEFEXEC="`basename \"${SHAR_DEFEXEC}\"`" 50 echo "..this is \`${SHAR_DEFEXEC}'" 51 NEED_DEFEXEC=0 52fi 53 54printf '\n%s\n%s' \ 55 'Members can be compressed via compress(1), gzip(1), bzip2(1) and xz(1).' \ 56 'Enter desired compressor or the empty string otherwise. [] ' 57read COMPRESS 58if [ -n "${COMPRESS}" ]; then 59 x_compress='compress -cf' 60 ux_compress='uncompress -cf' 61 x_gzip='gzip -cf' 62 ux_gzip='gzip -cdf' 63 x_bzip2='bzip2 -cf' 64 ux_bzip2='bzip2 -cdf' 65 x_xz='xz -cf' 66 ux_xz='xz -cdf' 67 68 eval cfun='${x_'"$COMPRESS"'}' 69 eval xfun='${ux_'"$COMPRESS"'}' 70 if [ -z "${cfun}" ]; then 71 echo >&2 "Unsupported compression method, bailing out: ${COMPRESS}" 72 exit 1 73 fi 74fi 75 76echo 77echo 'Enter member programs to include, empty value to terminate list.' 78MEMBERS= MEMBER_BASENAMES= 79[ -n "${ARGS}" ] && set -- ${ARGS} 80while :; do 81 if [ -n "${ARGS}" ]; then 82 [ ${#} -eq 0 ] && break 83 i=${1} 84 shift 85 printf '\t- ' 86 else 87 printf '\t - [] ' 88 read i 89 [ -z "${i}" ] && break 90 fi 91 92 if [ -x "${i}" ]; then 93 : 94 else 95 echo >&2 "${i} does not exist or is not executable, bailing out." 96 exit 1 97 fi 98 if { j=${i}; echo ${j}; } | grep -q '"'; then 99 echo >&2 "Double-quotes in filenames are unsupported, bailing out: ${i}" 100 exit 1 101 fi 102 103 j=`basename "${i}"` 104 if [ -z "${MEMBERS}" ]; then 105 MEMBERS="${i}" 106 MEMBER_BASENAMES="${j}" 107 else 108 MEMBERS="${MEMBERS}, ${i}" 109 MEMBER_BASENAMES="${MEMBER_BASENAMES}, ${j}" 110 fi 111 [ "${j}" = "${SHAR_DEFEXEC}" ] && unset NEED_DEFEXEC 112 echo "${i}: ok" 113done 114 115if [ -n "${SHAR_DEFEXEC}" ] && [ -n "${NEED_DEFEXEC}" ]; then 116 echo >&2 "The default executable is missing, bailing out: ${SHAR_DEFEXEC}" 117 exit 1 118fi 119 120# Everything get.set.go, so write the actual shell archive 121# (Not backward compatible with Bourne [-C], atomicity problem: set -C) 122echo '..creating archive..' 123CREATION_DATE=`date -u` 124 125# Header 126cat <<\! > "${SHAR}" 127#!/bin/sh - 128#@ This file has been created by gen-uushar-wrapper, which is 129# Copyright (c) 2012 - 2021 Steffen Nurpmeso <steffen@sdaoden.eu>. 130#@ by means of the ISC license. 131#@ It contains a shell-archive-execution-environment. 132#@ Run it, and it will tell you which executable files it contains. 133#@ Run it with the name of such an executable, and it will create a 134#@ hidden directory in your $TMPDIR to expand the shipped executables. 135#@ From then on, it will redirect its invocations to those programs. 136# 137#@ Archives produced using this implementation of gen-uushar-wrapper 138#@ may be easily examined with the command: 139#@ $ grep '^[^X#]' shell-archive 140# 141! 142 143# Its indeed our $SHAR 144trap "rm -rf '${SHAR}'" 0 145 146# A shell archive is not a transparent thing either! 147echo >> "${SHAR}" "SHAR='${SHAR}'" 148echo >> "${SHAR}" "CREATION_DATE='${CREATION_DATE}'" 149echo >> "${SHAR}" "MEMBERS='${MEMBER_BASENAMES}'" 150echo >> "${SHAR}" "# Default expansion (if argument \${1} equals \`+'):" 151echo >> "${SHAR}" "DEFEXEC='${SHAR_DEFEXEC}'" 152cat <<\! >> "${SHAR}" 153# 154[ ${#} -eq 0 ] && 155 { echo "Creation: ${CREATION_DATE}"; echo "Members: ${MEMBERS}"; exit 0; } 156# 157i="${TMPDIR}/.${SHAR}" 158if [ -d "${i}" ]; then 159 PATH="${i}:${PATH}" 160 export PATH 161 [ x"${1}" != 'x+' ] && DEFEXEC=${1} 162 shift 163 i="${i}/${DEFEXEC}" 164 if [ ! -f "${i}" ] || [ ! -x "${i}" ]; then 165 echo >&2 "Sorry, there is no program \`${DEFEXEC}' in this archive" 166 exit 54 167 fi 168 exec "${i}" ${@+"${@}"} 169 echo >&2 "Failed to execute <${DEFEXEC} ${@+\"${@}\"}>" 170 exit 71 # 71=EX_OSERR 171fi 172# 173printf "%s\n%s\n" "The directory \`${i}'" \ 174 "does not exist: creating it and expanding this archive first" 175MYSELF="`pwd`/${0}" 176[ ${?} -ne 0 ] && { echo >&2 "Cannot detect current directory"; exit 1; } 177mkdir -p "${i}" || { echo >&2 "Cannot create directory \`${i}'"; exit 1; } 178cd "${i}" || 179 { echo >&2 "Cannot cd into \`${i}', removing it"; rm -rf "${i}"; exit 1; } 180# 181! 182 183# uuencode(1)d and optionally compress the members to embed 184oifs=${IFS} 185IFS=', ' 186set -- ${MEMBERS} 187IFS=${oifs} 188for i 189do 190 bi=`basename "${i}"` 191 echo >> "${SHAR}" "echo 'X - ${bi}'" 192 if [ -z "${cfun}" ]; then 193 echo >> "${SHAR}" \ 194 "sed 's/^X//' << \! |\ 195 uudecode -o /dev/stdout > \"${bi}\"; chmod 0755 \"${bi}\"" 196 < "${i}" uuencode -m /dev/stdout | sed 's/^/X/' >> "${SHAR}" 197 else 198 echo >> "${SHAR}" \ 199 "sed 's/^X//' << \! | uudecode -o /dev/stdout | ${xfun} > \"${bi}\";\ 200 chmod 0755 \"${bi}\"" 201 < "${i}" ${cfun} | uuencode -m /dev/stdout | sed 's/^/X/' >> "${SHAR}" 202 fi 203 echo >> "${SHAR}" '!' 204 echo >> "${SHAR}" "# END of ${bi}" 205done 206 207echo >> "${SHAR}" "echo 'Archive is expanded, rerunning command...'" 208echo >> "${SHAR}" 'exec "${MYSELF}" ${@+"${@}"}' 209 210# The end 211chmod 0755 "${SHAR}" 212trap : 0 213exit 0 214# vim:set fenc=utf-8 syntax=sh:s-it-mode 215