#!/bin/sh
# platform ... devuan dash
# GPL_3+
cat << 'EEE' > /dev/null
/* rdopt ....read option, simple option parse helper. bourne-shell script.
 * Copyright (C) 2017 Momi-g
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */
EEE

# usage--------
if [ "$#" -eq 0 ] || [ "$1" = '-h' ] ; then
cat << 'EEE'
HowTo (readopt, option parse helper. bourne-shell script.)
option: -h (this)
------
eg.) ~$ rdopt ":ab:cd:" zzz -b vvv "y' y" -a   # format first ':' is quiet err.
>> OPTIND=1 ; opt_a='1'
   opt_b='vvv'	(subarg type is defined only when selected. ck ${opt_d+aaa})
   opt_c=''	(switch type opt is always declared. 'set -u' etc.)
   set -- 'zzz' 'y'"'"' yy'	(literal safe)
eg.)
 #!/bin/sh			# run...  yoursh.sh zzz -b vvv -c yyy
 buf=`rdopt ":ab:cd:" "$@"` 		#use "$@" or $@, not work at $*
 eval "$buf"
 if [ $? -ne 0 ] ; then echo "opterr: $OPTARG" >/dev/stderr ; exit 1; fi
 if [ "${opt_d:+e}" != "e" ];then echo "needs -d opt";fi #(man:param expantion)
 echo "$opt_b $@ $#"		# >>> vvv, zzz yyy, 2	...opts removed

- check all args or until detect '--'.
	yoursh.sh -c zzz -5+4	>> NG. -5 is treated as option. rtns err.
	yoursh.sh -c zzz -- -5+4	>> ok. -5+4 parsed as args '$1'
- if failed to parse option, ' eval "$buf" ' returns $?=1
  wrong optchar is set to OPTARG (OPTARG='z' etc)
- if not quiet mode (:a:bc -> a:bc etc), disp errmsg & sleep loop.
- you can use parse function directly (copy & paste). see source.
EEE
exit 0
fi

# purse func. 
# maybe you can copy & paste to your script directly.
# time
# normal use: 10-20 ms
# copy&paste: 5-10 ms

########## copy start ###########
func_rdopt() {
	(	# subshell. local vars. 
rdopt_fmt="$1"	#	":ab:c"	etc. format.
shift

#ck quiet mode
rdopt_quiet=1
rdopt_buf=${rdopt_fmt%%[!:]*}
if [ "$rdopt_buf" != ':' ] ; then
	rdopt_quiet=0
	rdopt_fmt=':'"$rdopt_fmt"
fi

#--inicialize	make list opt_a='', ...

#ab:c -> 'a' \n 'c' \n ... omit 'b:' ... for check  null pointer or "blank"
rdopt_buf='s/.://g
s/://g
s/./&\
/g'
rdopt_zerolist=`echo "$rdopt_fmt" | sed -e "$rdopt_buf" | sed -e "
/^$/d
s/^/opt_/g
s/$/=''/g
" `

#--- local vars
# rdopt_fmt="$1"	#	":ab:c"		opt format
# rdopt_quiet=1
# rdopt_zerolist=`echo "$rdopt_fmt" | sed -e "$rdopt_buf" | sed -e "
# rdopt_buf='s/://g

rdopt_out=""
rdopt_opt=""
rdopt_skip=""
rdopt_err=""

rdopt_safe="
s#'#'\"'\"'#g
1 s/^/'/g
$ s/$/'/g"

#---getopts loop. break if all args read or detect '--'
while :
do
if [ 0 -eq "$#" ] || [ "$rdopt_err" = "1" ] ; then
	break
fi
if [ "$1" = "--" ] ; then
	shift ; break
fi

getopts "$rdopt_fmt" rdopt_opt "$@"	# ":a:bc:f:" etc...
# err detect@silent mode
# $?=1 ... detect optend or '--'. '--' is removed at previous line.
# ':' ... detect option, but dont have subargs (OPTARG="factor").
# '?' ... detect unsupported option char (OPTARG="factor") or args end (OPTARG="blank").
# OPTARG ... if err, "" is optend. "a/b/c..." is invalid option 
# OPTIND ... if err, OPTIND indicates next (new) arg pos. 

if [ "$?" = "1" ] ; then 		# normal end (detect normal args). save general args.
	shift $((OPTIND - 1))
	if [ "$#" -eq "0" ] ; then
		break
	fi
	# new set make
	rdopt_buf=`printf '%s\n' "$1" | sed -e "$rdopt_safe"`
	rdopt_skip="$rdopt_skip $rdopt_buf"
	shift
	OPTIND=1
	continue
fi

if [ "$rdopt_opt" = "?" ]&&[ ${#OPTARG} != 0 ] || [ "$rdopt_opt" = ":" ] ; then 	# detect invalid opt
	rdopt_err=1		#OPTARG has err option char.
	continue
fi

# though err filter. below is for normal. detect option.
#if [ "$OPTARG" = "" ] ; then	# no subargs >>> debag. fixed.
# 2とは限らない。 -f 123 -f123 が同じ。-fh で-f -hもありえるし-f""と -g -h
# OPTARGだけでは分からない。フォーマットを見ないと。f:を探すしかない。

# optchar has ':' or not
buf=${rdopt_fmt#*$rdopt_opt}	#fmt ... :a:bc:d ...
buf=${buf%%[!:]*}	
if [ "$buf" != ":" ] ; then	# no subargs
	rdopt_out="$rdopt_out
opt_$rdopt_opt"'=1'
else	# exist subargs. =2
	rdopt_buf=`printf "%s\n" "$OPTARG" | sed -e "$rdopt_safe" `
	rdopt_out="$rdopt_out
opt_$rdopt_opt"'='"$rdopt_buf"
fi
done
# exit parse

if [ "$rdopt_err" = "1" ] ; then
	if [ "$rdopt_quiet" = "0" ] ; then
		rdopt_out='OPTIND=1
echo "$0: invalid option ( '"$OPTARG"' ). sleep" >/dev/stderr
while : ; do sleep 1000 ; done
exit 1'
	else
		rdopt_out="OPTARG=$OPTARG
OPTIND=1
test 1 = 0"
	fi
else

#	if '--' end ...
	if [ "$#" != "0" ] ; then
		rdopt_buf=""
		for ii
		do
			rdopt_buf="$rdopt_buf "`printf '%s\n' "$ii" | sed -e "$rdopt_safe" `
		done 
		rdopt_skip="$rdopt_skip $rdopt_buf"
	fi

#	output
	rdopt_out="OPTIND=1
$rdopt_zerolist
$rdopt_out
set -- $rdopt_skip"
fi
echo "$rdopt_err"
printf '%s\n' "$rdopt_out"
test ${#rdopt_err} = 0 || return 1
)
}
########## copy end ###########

# direct use sample
# buf=`func_rdopt ':ab:c:d' "$@" `
# eval "$buf"
# if [ $? != 0 ] ; then echo "err. $OPTARG" >/dev/stderr ; exit 1; fi
# ... or ...
# buf=`func_rdopt 'ab:c:d' "$@" `		# stop mode.	

# ---main
func_rdopt "$@"
