#!/bin/sh
:<< 'E==='
/* Copyright (C) 2020 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/>.
 */
E===

: << 'E==='
#SH_doc
title=brp section=1 repnl=\040
@name brp
@_brief line oriented universal preprocesser using posix-shell
@_syno
	brp [-hHVQsS]
@tl_dr
		@(code)@
	~$ brp -s | tee src.sh.c
	~$ sh src.sh.c -a	#>>output 'hw'
	~$ sh src.sh.c -a -b -a		#>>hw, exec ls, hw
		@()
@_opt
		@(list_o)
	-hHV: usage, version
	-Q: output corecode
	-sS: output sample
		@()
@_desc
	brp is portable preprocesser. code is separated with 2 parts,
	main code(#inc...#END) and option code (//SH_OP ...). workflow is:
		@(code)--@
	  1: exec src as shell script. eg) ~$ sh src.sh.c -a
	  2: header code searches regex suitable line (dfl: //SH_OP, /*SH_OP etc)
	  3: read hitline and save as cmd string ($Ca, $Cb, $C... etc)
	  4: exec option code if you use -a >> eval "$Ca", -z >> eval "$Cz"
		@()--@

	@(raw).SS main code@()
	--
	brp exec the head 10 line code as shell script and exit. --
	linetop '#include...' is assert() for c/cpp (~$ cc src.sh.c #>>stop)
	so you can delete if you want.
		@(code)--
	#inc...
	C='^[/][/*]SH_'    ; ...
		...#END
		@()--
	main code holds regex expression var `C` to get option code line. this var is
	used as:
		@(code)--
	~$ cat src.sh.c | sed -ne "/$C/p"		#BRE-reg. 
		@()--
	and gather //SH_OP or //SH_??? etc. 	--
	main code holds predefined option code, -w, -m. see below for details.
	--
	@(raw).SS option code@()
	--
	you can expand the brp working with option code.
	@(code)
	//SH_OP  b: z=100;echo "good-bye $2 $Ob $1 $z $R0"
	  1  2 3 4                5
	@()
	@(list)
	1: dfl opthead is "//SH_" or "/*SH_". see below '$C'.
	2: option word suffix `OP` is fixed. 
	3: separate with blank char(spaces or tabs)
	4: optchar is [a-zA-Z0-9] or `_`(see below). add colon if use optarg
	5: write raw sh-script with 'ONE LINE'. code is used as the below.
		@(code)@
		Cb=$(cat<<'E'
		z=100;echo "good-bye $2 $Ob $1 $z $R0"
		E
		)
		eval "$Cb"		#if exec: ~$ src.sh.c -b
		@()
	-: if you set special optchar `_`, it works only once at the script
		beginning. --
		@(code)@
		//SH_OP _ echo "preset AA";AA=1
		//SH_OP a echo "$AA"	#>>~$ sh src.sh.c -a ... disp "1"
		@()@
	
	-: adopt new one if overlap the option setting.
		@(code)@
		//SH_OP a echo "hw"	#>> ignored. sh src.sh.c -a  -> disp "gw"
		//SH_OP a echo "gw"
		@()@
	@()

	@(raw).SS about reserved vars or other@()--
	1-2char var names R?, C?, O? are reserved (R,C,O,Ra,R7,Cg,O5 ...).
	optarg is set to $O? ( a: >> $Oa ,  v: >> $Ov etc) --
	eg) //SH_OP a: echo "$Oa" #>> ~$ sh src.sh.c -a1 -a 5 #>> disp 1, 5
	
	@(list)
	$R0,Rm,Rs,Rh: orig/tMp/src/header fname. ($Rm == src.tmp.c etc)
		fname uses topname + last suffix. eg) src.xx.yy.py3 -> ./src.py3 --
		avoid fname unnecessary conflict. --
		(sh src.c -w >> src.h / src.c ...orig destroyed)
		
	$C?: $Ca/$Cb etc. code buffer.
		@(code)@
		//SH_OP a echo "$Rm"	# ~$ sh brp.sh.c -a >> disp brp.tmp.c
		//SH_OP b printf "$Ca"	# ~$ sh brp.sh.c -b >> disp echo "$Rm"
		//SH_OP c eval "$Ca"	#.. -c works as equals to -a
		@()@

	$Cw: predefined -w option. uses for c-lang. you will make sense of
		the work by exec.
		@(code)--@
		~$ brp -S > src.sh.c	#sample
		~$ sh src.sh.c -w
		
		1. write SH_LS - SH_ED(LS_block) to both src.h($Rh)/src.c($Rs)
		2. add HD_block to src.h, SC_block to src.c
		3. disp filename to stdout.  (src.h src.c)
		-. suffixes(LS,HD,SC,ED) are fixed
		@()@

	$Cm: predefined -m option. remove brp maincode(#inc...#END) and output
		to $Rm (XX.tmp.XX). you may uses for general perpose. 

	$C: comment regex. dfl is  C=`^[/][/*]SH_`, //SH_xx or /*SH_yy etc. --
		brp uses linecmt as directive. if you want to use other
		pg-lang(python etc), edit srctop `C=...` directly. use BRE-reg.
		@(code)--@
		shell : C='^#ANYSTR_';	>>  #ANYSTR_LS, #ANYSTR_ED etc
		python: C='^["]["]["]MARKER_';	>> """MARKER_OP   etc
		basic : C="^[']SH_";	>> 'SH_OP etc
	    @()--@
		$C is used as follows. escape slash '/' plz. --
		``  sed -e "/${C}ED/"  ..  (bad)C='^/[/*]SH_' (good)C='^\/[/*]SH_' ``
	
	$O?: ``optargs. //SH_OP a: echo "$Oa"  .. ~$ sh src.sh.c -a 11 ..11``
	$O: newline(\n).  eg) //SH_OP a echo "a${O}b" >> disp a(\n)b
	$0,1,2: normal args. this pg uses getopts. checked opts are removed. --
	eg) //SH_OP a echo "$1"	 #~$ sh src.sh.c -a -c 11 >> output 11
	  
	@()
@_eg
	@(raw).SS sample for cxx@()--
	@(code)--@
	--- copy & paste main script ---
	#include <iostream>
	int main(void){
	  std::cout << "hw" << std::endl;
	}
	//SH_OP b eval "$Cm";g++ "$Rm"; ./a.out
	@()--@
	...save as `src.brp.cpp` and run ~$ sh src.brp.c -b  >>> hw.
	--
	@(raw).SS one liner script@()
	--
	this app frequency uses one liner. introduce some tips.
	@(code)--@
	//SH_OP m sed -ne "/[E]ND/{n;b l};d;:l;p;n;b l"<"$R0">"$Rm";echo "$Rm"
	>>
	sed -ne '*see below*' < "$R0" > "$Rm"
	echo "$Rm"
	>>
	cat 'foo.sh.c' | sed -ne '...' > foo.tmp.c
	echo "foo.tmp.c"
	@()--@

	sed command pseudocode is the follows:
	@(code)--@
	------
	sed -ne '/[E]ND/{n;b l};d;: l;p;n;b l'
	>>
	sed -n(o print. print only when requested) -e(xpression as script)
	
	if (line==/END/){  .../[E]ND/
	  n(ext read) ...n (if not -n opt, print nowline & readnext)
	  goto label l ...b l (b=jump/goto. b abc -> goto abc, needs space@posix)
	}
	del line (& read nextline & *goto top*) ...d (d cmd is hard worker)
	label l:		... : l (label. ':' + '1sp' + 'lbl name') 
	p(rint line)		... p
	n(extline read)		... n
	goto label l		... b l
	
	...del lines until 1st hit 'END'. print all lines until EOF.
	-------
	@()--@
	sed cmd is difficult but very powerful. Most requests can be solved 
	by referring to the above.
	--
	@(raw).SS edit string with sed/shell@()
	--
	sed cant use shortest match, but posix-sh is possible(shotest+longest).
		@(code)--
	str="aa_bb_cc"
	echo "${str#*_}" 	#>> bb_cc (match aa_ and del)
	echo "${str##*_}" 	#>> cc (longet)
	echo "${str%_*}" 	#>> aa_bb (from tail)
	echo "${str%%_*}" 	#>> aa
		@()--
	shell pattern(glob pattern) is very similar to sed-regex:
	@(code)
	aa_bb_cc -> a_bb_cc
	reg: s/^.//g
	sh : ${str#?}	... any one char. reg:'.'    sh:'?'
	
	aa_bb_cc -> (del)
	reg: ^[.]*
	sh : ${str#*}	... all. sh can uses wild card.
	
	aa_bb_cc -> aa_
	reg: [^a_]*
	sh : ${str%%[!a_]*}	... not.  reg:'^'    sh:'!'
	...[], bracket works as same, 'one char'
	
	escape
	sh: ${str%123"*"*}  ... "*" uses as literal. 0123*567 -> 0
		@()--
	see https://en.wikipedia.org/wiki/Glob_%28programming%29  ..or.. --
	~$ man sh + input `/` + input `pattern` + enter + `n` + `shift_n`

@exit_status	-
@notes
	@(code)@
	--- concept
	I wondered why to write dependencies or compile options to makefiles.
	The source code should contain all the necessary information.
	Because the programmer's will is written in it. I dont like writing
	in separate files and increasing the workflow.
	
	- avoid info fragmentation (script/src/header/gcc opt/ini/config etc)
	- small. avoid disturbing the main code.
	- (consider readability)
	- portable. avoid vender lockin, bashism etc.
	- low learning cost. good usage help, dont need installation etc
	- others ... see unix philosophy.
		@()
@conforming_to	posix-shell
@copyright Copyright (C) 2020 Momi-g, GPLv3+
@_ver 2023-07-11 v1.0.4 (2020-01-04  v1.0.0)
@_see
https://en.wikipedia.org/wiki/Glob_%28programming%29	--
http://catb.org/%7Eesr/writings/taoup/html/ch10s05.html
#SH_docE
E===
f_usage(){
cat << 'E==='
HowTo (brp, line oriented universal preprocessor using posix-sh)
opt: -hH(elp), -V(ersion), -Q(corecode), -sS(ample)
------
 this script works by just copying and pasting into your file top.
	~$ brp 	#>> output maincode
	~$ brp -s	#>> output sample
 
 sample usage is:
 	~$ brp -S | tee src.sh.c
	~$ sh src.sh.c -a	#>> output 'hw'
	~$ sh src.sh.c -c	#>> compile, link then exec a.out
	~$ ls	#>> check output files

 brp holds predefined option, -m/-w
  ~$ sh src.sh.c -m #>> make tMp file (src.tmp.c)
  ~$ sh src.sh.c -w #>> make src.h + src.c. if src.sh.java, src.h + src.java

 your extension: add '//SH_OP z echo "12345"' to this file end and run.
	~$ sh src.sh.c -z -w -z  #>> 12345 x2 ..see also  ~$ brp -H|less
E===
exit 0
}

f_usageH(){
	cat << 'E==='	#|sed -e'1d;$d'
BRP(1)                      General Commands Manual                     BRP(1)



NAME
       brp - line oriented universal preprocesser using posix-shell

SYNOPSIS
       brp [-hHVQsS]

TL_DR
       ~$ brp -s | tee src.sh.c
       ~$ sh src.sh.c -a   #>>output 'hw'
       ~$ sh src.sh.c -a -b -a       #>>hw, exec ls, hw


OPTIONS
       -hHV   usage, version

       -Q     output corecode

       -sS    output sample

DESCRIPTION
       brp  is  portable  preprocesser.  code  is separated with 2 parts, main
       code(#inc...#END) and option code (//SH_OP ...). workflow is:

         1: exec src as shell script. eg) ~$ sh src.sh.c -a
         2: header code searches regex suitable line (dfl: //SH_OP, /*SH_OP etc)
         3: read hitline and save as cmd string ($Ca, $Cb, $C... etc)
         4: exec option code if you use -a >> eval "$Ca", -z >> eval "$Cz"


   main code
       brp exec the head 10 line code as shell script and exit.
       linetop '#include...' is assert() for c/cpp (~$ cc src.sh.c #>>stop) so
       you can delete if you want.

            #inc...
            C='^[/][/*]SH_'    ; ...
                 ...#END

       main  code  holds  regex expression var C to get option code line. this
       var is used as:

            ~$ cat src.sh.c | sed -ne "/$C/p"       #BRE-reg.

       and gather //SH_OP or //SH_??? etc.
       main code holds predefined option code, -w, -m. see below for details.

   option code
       you can expand the brp working with option code.
            //SH_OP  b: z=100;echo "good-bye $2 $Ob $1 $z $R0"
              1  2 3 4                5

       1      dfl opthead is "//SH_" or "/*SH_". see below '$C'.

       2      option word suffix OP is fixed.

       3      separate with blank char(spaces or tabs)

       4      optchar is [a-zA-Z0-9] or _(see below). add colon if use optarg

       5      write raw sh-script with 'ONE LINE'. code is used as the below.
              Cb=$(cat<<'E'
              z=100;echo "good-bye $2 $Ob $1 $z $R0"
              E
              )
              eval "$Cb"          #if exec: ~$ src.sh.c -b

       -      if you set special optchar _, it works only once at  the  script
              beginning.
              //SH_OP _ echo "preset AA";AA=1
              //SH_OP a echo "$AA"     #>>~$ sh src.sh.c -a ... disp "1"

       -      adopt new one if overlap the option setting.
              //SH_OP a echo "hw" #>> ignored. sh src.sh.c -a  -> disp "gw"
              //SH_OP a echo "gw"

   about reserved vars or other
       1-2char  var names R?, C?, O? are reserved (R,C,O,Ra,R7,Cg,O5 ...). op‐
       targ is set to $O? ( a: >> $Oa , v: >> $Ov etc)
       eg) //SH_OP a: echo "$Oa" #>> ~$ sh src.sh.c -a1 -a 5 #>> disp 1, 5

       $R0,Rm,Rs,Rh
              orig/tMp/src/header fname. ($Rm ==  src.tmp.c  etc)  fname  uses
              topname + last suffix. eg) src.xx.yy.py3 -> ./src.py3
              avoid fname unnecessary conflict.
              (sh src.c -w >> src.h / src.c ...orig destroyed)

       $C?    $Ca/$Cb etc. code buffer.
              //SH_OP a echo "$Rm"     # ~$ sh brp.sh.c -a >> disp brp.tmp.c
              //SH_OP b printf "$Ca"   # ~$ sh brp.sh.c -b >> disp echo "$Rm"
              //SH_OP c eval "$Ca"     #.. -c works as equals to -a

       $Cw    predefined  -w  option.  uses for c-lang. you will make sense of
              the work by exec.

              ~$ brp -S > src.sh.c     #sample
              ~$ sh src.sh.c -w

              1. write SH_LS - SH_ED(LS_block) to both src.h($Rh)/src.c($Rs)
              2. add HD_block to src.h, SC_block to src.c
              3. disp filename to stdout.  (src.h src.c)
              -. suffixes(LS,HD,SC,ED) are fixed

       $Cm    predefined -m option. remove brp maincode(#inc...#END) and  out‐
              put to $Rm (XX.tmp.XX). you may uses for general perpose.

       $C     comment regex. dfl is C=^[/][/*]SH_, //SH_xx or /*SH_yy etc.
              brp  uses  linecmt  as  directive.  if  you  want  to  use other
              pg-lang(python etc), edit srctop C=... directly. use BRE-reg.

              shell : C='^#ANYSTR_';   >>  #ANYSTR_LS, #ANYSTR_ED etc
              python: C='^["]["]["]MARKER_';     >> """MARKER_OP   etc
              basic : C="^[']SH_";     >> 'SH_OP etc

              $C is used as follows. escape slash '/' plz.
                sed -e "/${C}ED/"  ..  (bad)C='^/[/*]SH_' (good)C='^\/[/*]SH_'

       $O?    optargs. //SH_OP a: echo "$Oa"  .. ~$ sh src.sh.c -a 11 ..11

       $O     newline(\n). eg) //SH_OP a echo "a${O}b" >> disp a(\n)b

       $0,1,2 normal args. this pg uses getopts. checked opts are removed.
              eg) //SH_OP a echo "$1" #~$ sh src.sh.c -a -c 11 >> output 11

EXSAMPLE
   sample for cxx
       --- copy & paste main script ---
       #include <iostream>
       int main(void){
         std::cout << "hw" << std::endl;
       }
       //SH_OP b eval "$Cm";g++ "$Rm"; ./a.out

       ...save as src.brp.cpp and run ~$ sh src.brp.c -b >>> hw.

   one liner script
       this app frequency uses one liner. introduce some tips.

       //SH_OP m sed -ne "/[E]ND/{n;b l};d;:l;p;n;b l"<"$R0">"$Rm";echo "$Rm"
       >>
       sed -ne '*see below*' < "$R0" > "$Rm"
       echo "$Rm"
       >>
       cat 'foo.sh.c' | sed -ne '...' > foo.tmp.c
       echo "foo.tmp.c"

       sed command pseudocode is the follows:

       ------
       sed -ne '/[E]ND/{n;b l};d;: l;p;n;b l'
       >>
       sed -n(o print. print only when requested) -e(xpression as script)

       if (line==/END/){  .../[E]ND/
         n(ext read) ...n (if not -n opt, print nowline & readnext)
         goto label l ...b l (b=jump/goto. b abc -> goto abc, needs space@posix)
       }
       del line (& read nextline & *goto top*) ...d (d cmd is hard worker)
       label l:       ... : l (label. ':' + '1sp' + 'lbl name')
       p(rint line)        ... p
       n(extline read)          ... n
       goto label l        ... b l

       ...del lines until 1st hit 'END'. print all lines until EOF.
       -------

       sed cmd is difficult but very powerful. Most requests can be solved  by
       referring to the above.

   edit string with sed/shell
       sed cant use shortest match, but posix-sh is possible(shotest+longest).

            str="aa_bb_cc"
            echo "${str#*_}"    #>> bb_cc (match aa_ and del)
            echo "${str##*_}"   #>> cc (longet)
            echo "${str%_*}"    #>> aa_bb (from tail)
            echo "${str%%_*}"   #>> aa

       shell pattern(glob pattern) is very similar to sed-regex:
            aa_bb_cc -> a_bb_cc
            reg: s/^.//g
            sh : ${str#?}  ... any one char. reg:'.'    sh:'?'

            aa_bb_cc -> (del)
            reg: ^[.]*
            sh : ${str#*}  ... all. sh can uses wild card.

            aa_bb_cc -> aa_
            reg: [^a_]*
            sh : ${str%%[!a_]*} ... not.  reg:'^'    sh:'!'
            ...[], bracket works as same, 'one char'

            escape
            sh: ${str%123"*"*}  ... "*" uses as literal. 0123*567 -> 0

       see https://en.wikipedia.org/wiki/Glob_%28programming%29 ..or..
       ~$ man sh + input / + input pattern + enter + n + shift_n

EXIT_STATUS
       -

NOTES
       --- concept
       I wondered why to write dependencies or compile options to makefiles.
       The source code should contain all the necessary information.
       Because the programmer's will is written in it. I dont like writing
       in separate files and increasing the workflow.

       - avoid info fragmentation (script/src/header/gcc opt/ini/config etc)
       - small. avoid disturbing the main code.
       - (consider readability)
       - portable. avoid vender lockin, bashism etc.
       - low learning cost. good usage help, dont need installation etc
       - others ... see unix philosophy.


CONFORMING_TO
       posix-shell

COPYRIGHT
       Copyright (C) 2020 Momi-g, GPLv3+

VERSION
       2023-07-11 v1.0.4 (2020-01-04 v1.0.0)

SEE_ALSO
       https://en.wikipedia.org/wiki/Glob_%28programming%29
       http://catb.org/%7Eesr/writings/taoup/html/ch10s05.html



                                                                        BRP(1)
E===
exit 0
}

f_verinfo(){
	cat <<- 'E==='
brp 2023-07-11 v1.0.4 (2020-01-04 v1.0.0)
Copyright (C) 2020 Momi-g, GPLv3+
	E===
exit 0
}

f_eg1(){
cat<<-E===
$code

	//SH_OP a echo "hw"
	//SH_OP b ls -al
E===
exit 0
}

f_eg2(){
cat<<-HDOC; cat<<-'HDOC_'
$code

HDOC
//SH_LS
	//license is XXX
	//SH_ED

//SH_HD
#include <stdio.h>
int myf(void);
	//SH_ED

/*SH_SC*/
int myf(void){puts("cc_hw");return 0;}
int main(int argc, char** argv){return myf();}
	//SH_ED

	//SH_OP a echo "hw"
	//SH_OP c eval "$Cw";cat "$Rh" "$Rs">"$Rm"; cc "$Rm"; ./a.out
HDOC_
exit 0
}

code=$(cat<<'E===' | sed -e 's@\[#]@[/][/*]@'
#include <emsg: see ... '~$ sh aaa.sh.c -h'   (other opt:no/-m/-w/)>	/*
C='^[#]SH_'			;O=${0##*[/]};R=`dirname $0`/;R0=$R$O;Re=eval\ ;R=$R${O%%.*}
O=${0##*.};Rs=$R.$O;Rm=$R.tmp.$O;Rh=$R.h;R=$Rs$Rh$Rm;Rp='printf %s\n ';Rc=:;O="
";[ -z "${R##*$R0*}" ]&&$Rp"$0:NGext"&&exit 1;R='sed -ne ';Cm=$R'"/[E]ND/!d;: l
n;p;b l"<$R0>$Rm;$Rp"$Rm"';Rw=$R'"/$C$R/!d;: l;n;/${C}ED/q;p;b l"<$R0';Cw="(R=LS
$Rw;$Rw>&3;R=HD;$Rw;R=SC;$Rw>&3)"'>$Rh 3>$Rs;$Rp"$Rh $Rs"';RB=$($R"s/${C}OP//p"\
<$R0|(F=mw;while read -r a b;do B=${a%:};F=`$Rp"$F"|$R"s#$B:*##;p"`${a%_};$Rp"
C$B=\$(cat<<'E'$O$b${O}E$O)";done;$Rp"R1=$F"));$Re"$RB";while getopts $R1 R;do
case $R in \?)exit 1;;*)$Re"O$R=\$OPTARG";Rc=$Rc$O`$Re'$Rp"$C'$R\"`;;esac;done
[ "$Rc" = : ]&&Rc=$Cm;shift $((OPTIND-1));$Re"$C_$O$Rc";exit #END  GPLv3+*/
E===
)

f_err(){ echo "brp: err. $*">/dev/stderr; exit 1; }
brp()(
	OPTIND=1
		while getopts ":hHVQsS" c && xx=1; do
		while [ "$xx" = 1 ]&&xx=0; do case "$c" in	#-- xx: fall-through magic		
	(h) f_usage	;;
	(H) f_usageH	;;
	(V) f_verinfo	;;
	(Q) printf '%s\n' "$code"; exit 0	;;
	(s) f_eg1	;;
	(S) f_eg2	;;
	([?:]) f_err "bad opt: $OPTARG: $*" ;;	
		esac; done; done; shift $(($OPTIND-1))
	
	#direct use
	printf '%s\n' "$code"
)
brp_main(){ brp "$@"; }
brp_main "$@" || exit	#SH_MAIN




cat<<'EEE'>/dev/null
 change log
 --
2023-07-11	Momi-g	<dmy@dmy.dmy>

	* brp(all): fix :l >> : l, improve doc etc. v1.0.4

2022-03-26  Momi-g	<dmy@dmy.dmy>

	* brp: fix sed, bl >> b l, posix sed jumpcmd needs space. v1.0.3

2021-09-14  Momi-g	<dmy@dmy.dmy>

	* brp: new pkg. add doc etc. v1.0.2 

2021-05-11  Momi-g	<dmy@dmy.dmy>

	* brp.sh.c : v1.0.1 add mandoc, improve brp script 

2020-01-04  Momi-g	<dmy@dmy.dmy>

	* brp.sh.c : v1.0.0 releases. 
EEE
