Mon, 28 Jan 2013 17:37:18 +0100
Correct socket error reporting improvement with IPv6 portable code,
after helpful recommendation by Saúl Ibarra Corretgé on OSips devlist.
michael@428 | 1 | ## |
michael@428 | 2 | ## sea -- Shell Execution Archive |
michael@428 | 3 | ## Copyright (c) 2012 Ralf S. Engelschall <rse@engelschall.com> |
michael@428 | 4 | ## |
michael@428 | 5 | ## This program is free software; you can redistribute it and/or modify |
michael@428 | 6 | ## it under the terms of the GNU General Public License as published by |
michael@428 | 7 | ## the Free Software Foundation; either version 2 of the License, or |
michael@428 | 8 | ## (at your option) any later version. |
michael@428 | 9 | ## |
michael@428 | 10 | ## This program is distributed in the hope that it will be useful, |
michael@428 | 11 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of |
michael@428 | 12 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
michael@428 | 13 | ## General Public License for more details. |
michael@428 | 14 | ## |
michael@428 | 15 | ## You should have received a copy of the GNU General Public License |
michael@428 | 16 | ## along with this program; if not, write to the Free Software |
michael@428 | 17 | ## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
michael@428 | 18 | ## USA, or contact Ralf S. Engelschall <rse@engelschall.com>. |
michael@428 | 19 | ## |
michael@428 | 20 | ## NOTICE: Given that you include this file verbatim into your own |
michael@428 | 21 | ## source tree, you are justified in saying that it remains separate |
michael@428 | 22 | ## from your package, and that this way you are simply just using GNU |
michael@428 | 23 | ## shtool. So, in this situation, there is no requirement that your |
michael@428 | 24 | ## package itself is licensed under the GNU General Public License in |
michael@428 | 25 | ## order to take advantage of GNU shtool. |
michael@428 | 26 | ## |
michael@428 | 27 | |
michael@428 | 28 | ## |
michael@428 | 29 | ## COMMAND LINE PARSING |
michael@428 | 30 | ## |
michael@428 | 31 | |
michael@428 | 32 | str_usage="[-h|--help] [-o|--output <file>] <script> [<file-or-dir> ...]" |
michael@428 | 33 | arg_spec="1+" |
michael@428 | 34 | opt_spec="h.o:" |
michael@428 | 35 | opt_alias="h:help,o:output" |
michael@428 | 36 | opt_h=no |
michael@428 | 37 | opt_o="" |
michael@428 | 38 | |
michael@428 | 39 | # parse argument specification string |
michael@428 | 40 | eval `echo $arg_spec |\ |
michael@428 | 41 | sed -e 's/^\([0-9]*\)\([+=]\)/arg_NUMS=\1; arg_MODE=\2/'` |
michael@428 | 42 | |
michael@428 | 43 | # parse option specification string |
michael@428 | 44 | eval `echo $opt_spec |\ |
michael@428 | 45 | sed -e 's/\([a-zA-Z0-9]\)\([.:+]\)/opt_MODE_\1=\2;/g'` |
michael@428 | 46 | |
michael@428 | 47 | # parse option alias string |
michael@428 | 48 | eval `echo $opt_alias |\ |
michael@428 | 49 | sed -e 's/-/_/g' -e 's/\([a-zA-Z0-9]\):\([^,]*\),*/opt_ALIAS_\2=\1;/g'` |
michael@428 | 50 | |
michael@428 | 51 | # interate over argument line |
michael@428 | 52 | opt_PREV='' |
michael@428 | 53 | while [ $# -gt 0 ]; do |
michael@428 | 54 | # special option stops processing |
michael@428 | 55 | if [ ".$1" = ".--" ]; then |
michael@428 | 56 | shift |
michael@428 | 57 | break |
michael@428 | 58 | fi |
michael@428 | 59 | |
michael@428 | 60 | # determine option and argument |
michael@428 | 61 | opt_ARG_OK=no |
michael@428 | 62 | if [ ".$opt_PREV" != . ]; then |
michael@428 | 63 | # merge previous seen option with argument |
michael@428 | 64 | opt_OPT="$opt_PREV" |
michael@428 | 65 | opt_ARG="$1" |
michael@428 | 66 | opt_ARG_OK=yes |
michael@428 | 67 | opt_PREV='' |
michael@428 | 68 | else |
michael@428 | 69 | # split argument into option and argument |
michael@428 | 70 | case "$1" in |
michael@428 | 71 | --[a-zA-Z0-9]*=*) |
michael@428 | 72 | eval `echo "x$1" |\ |
michael@428 | 73 | sed -e 's/^x--\([a-zA-Z0-9-]*\)=\(.*\)$/opt_OPT="\1";opt_ARG="\2"/'` |
michael@428 | 74 | opt_STR=`echo $opt_OPT | sed -e 's/-/_/g'` |
michael@428 | 75 | eval "opt_OPT=\${opt_ALIAS_${opt_STR}-${opt_OPT}}" |
michael@428 | 76 | ;; |
michael@428 | 77 | --[a-zA-Z0-9]*) |
michael@428 | 78 | opt_OPT=`echo "x$1" | cut -c4-` |
michael@428 | 79 | opt_STR=`echo $opt_OPT | sed -e 's/-/_/g'` |
michael@428 | 80 | eval "opt_OPT=\${opt_ALIAS_${opt_STR}-${opt_OPT}}" |
michael@428 | 81 | opt_ARG='' |
michael@428 | 82 | ;; |
michael@428 | 83 | -[a-zA-Z0-9]*) |
michael@428 | 84 | eval `echo "x$1" |\ |
michael@428 | 85 | sed -e 's/^x-\([a-zA-Z0-9]\)/opt_OPT="\1";/' \ |
michael@428 | 86 | -e 's/";\(.*\)$/"; opt_ARG="\1"/'` |
michael@428 | 87 | ;; |
michael@428 | 88 | -[a-zA-Z0-9]) |
michael@428 | 89 | opt_OPT=`echo "x$1" | cut -c3-` |
michael@428 | 90 | opt_ARG='' |
michael@428 | 91 | ;; |
michael@428 | 92 | *) |
michael@428 | 93 | break |
michael@428 | 94 | ;; |
michael@428 | 95 | esac |
michael@428 | 96 | fi |
michael@428 | 97 | |
michael@428 | 98 | # eat up option |
michael@428 | 99 | shift |
michael@428 | 100 | |
michael@428 | 101 | # determine whether option needs an argument |
michael@428 | 102 | eval "opt_MODE=\$opt_MODE_${opt_OPT}" |
michael@428 | 103 | if [ ".$opt_ARG" = . ] && [ ".$opt_ARG_OK" != .yes ]; then |
michael@428 | 104 | if [ ".$opt_MODE" = ".:" ] || [ ".$opt_MODE" = ".+" ]; then |
michael@428 | 105 | opt_PREV="$opt_OPT" |
michael@428 | 106 | continue |
michael@428 | 107 | fi |
michael@428 | 108 | fi |
michael@428 | 109 | |
michael@428 | 110 | # process option |
michael@428 | 111 | case $opt_MODE in |
michael@428 | 112 | '.' ) |
michael@428 | 113 | # boolean option |
michael@428 | 114 | eval "opt_${opt_OPT}=yes" |
michael@428 | 115 | ;; |
michael@428 | 116 | ':' ) |
michael@428 | 117 | # option with argument (multiple occurances override) |
michael@428 | 118 | eval "opt_${opt_OPT}=\"\$opt_ARG\"" |
michael@428 | 119 | ;; |
michael@428 | 120 | '+' ) |
michael@428 | 121 | # option with argument (multiple occurances append) |
michael@428 | 122 | eval "opt_${opt_OPT}=\"\$opt_${opt_OPT} \$opt_ARG\"" |
michael@428 | 123 | ;; |
michael@428 | 124 | * ) |
michael@428 | 125 | echo "$0: ERROR: unknown option: \`$opt_OPT'" 1>&2 |
michael@428 | 126 | exit 1 |
michael@428 | 127 | ;; |
michael@428 | 128 | esac |
michael@428 | 129 | done |
michael@428 | 130 | if [ ".$opt_PREV" != . ]; then |
michael@428 | 131 | echo "$0: ERROR: missing argument to option \`$opt_PREV'" 1>&2 |
michael@428 | 132 | exit 1 |
michael@428 | 133 | fi |
michael@428 | 134 | |
michael@428 | 135 | # process help option |
michael@428 | 136 | if [ ".$opt_h" = .yes ]; then |
michael@428 | 137 | echo "Usage: $0 $str_usage" |
michael@428 | 138 | exit 0 |
michael@428 | 139 | fi |
michael@428 | 140 | |
michael@428 | 141 | # complain about incorrect number of arguments |
michael@428 | 142 | case $arg_MODE in |
michael@428 | 143 | '=' ) |
michael@428 | 144 | if [ $# -ne $arg_NUMS ]; then |
michael@428 | 145 | echo "$0: ERROR: invalid number of arguments (exactly $arg_NUMS expected)" 1>&2 |
michael@428 | 146 | exit 1 |
michael@428 | 147 | fi |
michael@428 | 148 | ;; |
michael@428 | 149 | '+' ) |
michael@428 | 150 | if [ $# -lt $arg_NUMS ]; then |
michael@428 | 151 | echo "$0: ERROR: invalid number of arguments (at least $arg_NUMS expected)" 1>&2 |
michael@428 | 152 | exit 1 |
michael@428 | 153 | fi |
michael@428 | 154 | ;; |
michael@428 | 155 | esac |
michael@428 | 156 | |
michael@428 | 157 | ## |
michael@428 | 158 | ## MAIN |
michael@428 | 159 | ## |
michael@428 | 160 | |
michael@428 | 161 | # we required at least the control script |
michael@428 | 162 | script="$1" |
michael@428 | 163 | shift |
michael@428 | 164 | |
michael@428 | 165 | # determine output file |
michael@428 | 166 | sea="$opt_o" |
michael@428 | 167 | if [ ".$sea" = . ]; then |
michael@428 | 168 | sea=`echo "$script" | sed -e 's;\.[^.][^.]*$;;' -e 's;$;.sea;'` |
michael@428 | 169 | fi |
michael@428 | 170 | |
michael@428 | 171 | # wrap into a self-extracting distribution file |
michael@428 | 172 | tmpdir="${TMPDIR-/tmp}" |
michael@428 | 173 | tmpfile="$tmpdir/sea.tmp.$$" |
michael@428 | 174 | rm -f $tmpfile |
michael@428 | 175 | set -o noclobber |
michael@428 | 176 | ( sed <$0 \ |
michael@428 | 177 | -e '1,/^____/d' \ |
michael@428 | 178 | -e "s;@script@;$script;g" |
michael@428 | 179 | echo . | awk '{ |
michael@428 | 180 | for (i = 0; i < 2048/8; i++) { |
michael@428 | 181 | printf(" "); |
michael@428 | 182 | } |
michael@428 | 183 | }' |
michael@428 | 184 | ) >$tmpfile |
michael@428 | 185 | dd bs=2048 count=1 if=$tmpfile of=$sea 2>/dev/null |
michael@428 | 186 | rm -f $tmpfile |
michael@428 | 187 | tar cf - "$script" "$@" 2>/dev/null >>$sea |
michael@428 | 188 | |
michael@428 | 189 | # graceful termination |
michael@428 | 190 | # (before the shell discovers that we carry the wrapper with us below) |
michael@428 | 191 | exit 0 |
michael@428 | 192 | |
michael@428 | 193 | ## |
michael@428 | 194 | ## WRAPPER SCRIPT |
michael@428 | 195 | ## |
michael@428 | 196 | |
michael@428 | 197 | ______________________________________________________________________________ |
michael@428 | 198 | #!/bin/sh |
michael@428 | 199 | #![OpenPKG] |
michael@428 | 200 | ## |
michael@446 | 201 | ## THIS IS AN AUTOGENERATED SHELL WRAPPER ARCHIVE |
michael@428 | 202 | ## |
michael@428 | 203 | |
michael@428 | 204 | # establish sane environment |
michael@428 | 205 | PATH="$PATH:/bin:/sbin:/usr/bin:/usr/sbin" |
michael@428 | 206 | LC_CTYPE=C |
michael@428 | 207 | export LC_CTYPE |
michael@428 | 208 | umask 022 |
michael@428 | 209 | |
michael@446 | 210 | # passthrough |
michael@428 | 211 | if [ $# -eq 1 -a ".$1" = ".--tar" ]; then |
michael@446 | 212 | # passthrough attached payload on stdout |
michael@428 | 213 | dd if="$0" bs=2048 skip=1 2>/dev/null |
michael@428 | 214 | rc=$? |
michael@428 | 215 | else |
michael@428 | 216 | # extract attached payload |
michael@428 | 217 | rm -rf "$0.d" >/dev/null 2>&1 |
michael@428 | 218 | mkdir "$0.d" || exit $? |
michael@428 | 219 | dd if="$0" bs=2048 skip=1 2>/dev/null | \ |
michael@428 | 220 | (cd "$0.d" && tar xf - 2>/dev/null) |
michael@428 | 221 | if [ ! -f "$0.d/@script@" ]; then |
michael@428 | 222 | echo "$0: ERROR: failed to unpack attached payload into directory \"$0.d\"" 1>&2 |
michael@428 | 223 | rm -rf "$0.d" >/dev/null 2>&1 || true |
michael@428 | 224 | exit 1 |
michael@428 | 225 | fi |
michael@428 | 226 | |
michael@446 | 227 | # passthrough execution to control script |
michael@428 | 228 | (cd "$0.d" && exec sh "./@script@" ${1+"$@"}) |
michael@428 | 229 | rc=$? |
michael@428 | 230 | |
michael@428 | 231 | # cleanup |
michael@428 | 232 | rm -rf "$0.d" >/dev/null 2>&1 || true |
michael@428 | 233 | fi |
michael@428 | 234 | |
michael@428 | 235 | # terminate gracefully |
michael@428 | 236 | # (before the shell discovers that we carry MBs of raw data with us below) |
michael@428 | 237 | exit $rc |
michael@428 | 238 | |
michael@428 | 239 | # the payload tarball is appended in raw format directly to the end |
michael@428 | 240 | # of this script, just leaded by padding whitespaces which make sure |
michael@446 | 241 | # that the tarball data starts at the predefined offset of 2KB. This |
michael@428 | 242 | # allows us to unpack the tarball by just skipping the leading 2KB. |
michael@428 | 243 |