#include <emsg: see ... '~$ sh aaa.sh.c -h'   (other opt:no/-m/-w/)>	/*
C='^[/][/*]SH_'     ;O=${0##*[/]};R=`dirname $0`;R=${R%/}/;R0=$R$O;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="
";[ "${R##*$R0*}" = '' ]&&$Rp"$0:NGsuffix"&&exit 1;R='sed -ne ';Cm=$R'"/[E]ND/!d
:l;n;p;bl"<$R0>$Rm;$Rp"$Rm"';RB=$($R"s/${C}OP//p"<$R0|(F=mw;while read -r a b;do
B=${a%:};F=`$Rp"$F"|$R"s#$B:*##1;p"`${a%_};$Rp"C$B=\$(cat<<'E'$O$b${O}E$O)";done
$Rp"R1=$F"));Rw=$R'"/$C$R/!d;:l;n;/${C}ED/q;p;bl"<$R0';Cw="(R=LS;$Rw;$Rw>&3;R=HD
$Rw;R=SC;$Rw>&3)"'>$Rh 3>$Rs;$Rp"$Rh $Rs"';Re=eval\ ;$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 GPL3+*/

/*SH_LS*/
/* Copyright (C) 2019 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/>.
 */

/* add license cmt from cutest-1.5 README.txt */
/*
 NOTE
 
 The license is based on the zlib/libpng license. For more details see
 http://www.opensource.org/licenses/zlib-license.html. The intent of the
 license is to:
 
 - keep the license as simple as possible
 - encourage the use of CuTest in both free and commercial applications
   and libraries
 - keep the source code together
 - give credit to the CuTest contributors for their work
 
 If you ship CuTest in source form with your source distribution, the
 following license document must be included with it in unaltered form.
 If you find CuTest useful we would like to hear about it.
 
 LICENSE
 
 Copyright (c) 2003 Asim Jalis
 
 This software is provided 'as-is', without any express or implied
 warranty. In no event will the authors be held liable for any damages
 arising from the use of this software.
 
 Permission is granted to anyone to use this software for any purpose,
 including commercial applications, and to alter it and redistribute it
 freely, subject to the following restrictions:
 
 1. The origin of this software must not be misrepresented; you must not
 claim that you wrote the original software. If you use this software in
 a product, an acknowledgment in the product documentation would be
 appreciated but is not required.
 
 2. Altered source versions must be plainly marked as such, and must not
 be misrepresented as being the original software.
 
 3. This notice may not be removed or altered from any source
 distribution.
*/
/*SH_ED*/

/*SH_HD*/
/*-*
@_name	HCUT_ADD, HCUT_RUN, eq, eq_s/i/d/p
@auther momi-g
@brief	C Unit Test, portable unittest implemented in 1 header file
@_syno
	void HCUT_ADD(fcname)
	int HCUT_RUN(const char* logfile, int msglv [, fcnames ...] )
	//assert macros, expand to 'do{...}while(0)'
	eq(int cmp)
	eq_s(void* s1, void* s2)
	eq_i(int i1, int i2)
	eq_d(double d1, double d2)
	eq_p(void* p1, void* p2)
	... all asserts can take lastarg as cmt, eq_i(1,2, "failtest_cmt") etc. 
@_eg
	#include <stdio.h>
	#include "hcut.h"		//header holds all implements 
	int hw() {
		puts("hw");
		return 0;
	}
	HCUT_ADD(anyname_hw) {
		int ck = 123;
		eq( hw()==10 );	//== do{errck_eq(); if...;...}while(0)
		eq_i( hw(), 100, "mycmt" );
		printf("bin:%s\n", argv[0]);
	}
	HCUT_RUN("./mytest.log", 1, anyname_hw );	//HCUT_RUN() has main() func
	// ~$ cc hw.c && ./a.out && echo $?	>>	drop ./mytest.log

@_eg
	-- hw.c
	#include <stdio.h>
	int hw() { puts("hw"); return 0; }
	-- test_hw.c
	#include "hcut.h"
	HCUT_ADD(t_hw1) { eq_i( hw(), 0, "mymsg" ); }
	HCUT_ADD(t_hw2) { printf("ag1:%s\n", argv[1]); eq( hw()==1 ); }
	HCUT_RUN("stderr", 2, t_hw1, t_hw2 );		// disp to stderr
	// ~$ cc hw.c test_hw.c && ./a.out xyz && echo $?

@param fcname	testfunc(test suite) name.  
@param logfile msgout fname. "stdout","stderr","NULL" are used as FILE* / noout.
@param lv	verbose lv. 0/1/2/3. quiet/normal/verbose/verbose+stop if err
@param fcname1,2-	test tgt. use as funcptr. test all suite you set here
@return		HCUT_RUN() returns failed assert cnt. rtn -1 if fatal err.
@details	HCUT_RUN() holds "int main(int argc, char** argv){...}" func.
 you can use cmdin data 'argc/argv' in HCUR_ADD() block.
@_note	other sample src is at srcend.
 https://sourceforge.net/projects/cutest/files/cutest/1.5/
 https://en.wikipedia.org/wiki/List_of_unit_testing_frameworks
@conforming_to c99+
@_ver 2021-11-30 v1.3.0
-*/

// change... sed -i 'HCUT_9611a28d' 'some_new_prefix_str'
#ifndef HCUT_9611a28d
#define HCUT_9611a28d

#include <stdio.h>
#if (__STDC_VERSION__ +0 < 199901L) 
	# include "needs compiler c99 or upper"
#endif
#if (199901L <= __STDC_VERSION__ +0)	/* nealy 200112L, _POSIX_C_SOURCE	c99*/
	#include <sys/types.h>
	#include <unistd.h>
	#define HCUT_Eactag	__func__, getpid()
#else
	#define HCUT_Eactag	"func:c99+", 0
#endif
#include <string.h>
#include <errno.h>
#define HCUT_Eact(xpr, msg, act)	if(xpr){ int en_=errno; fprintf(stderr, \
	"ERR: %s %d %s() pid:%d %s msg:%s sys:%s\n",__FILE__,__LINE__, HCUT_Eactag \
	, "hit(" #xpr ")", msg, strerror(en_) ); act; }

//api_macro...see this srcend sample
#define HCUT_ADD(a)		HCUT_9611a28d_ADD(a)
#define HCUT_RUN(...)	HCUT_9611a28d_RUN(__VA_ARGS__)
#define HCUT_9611a28d_BACK	if(HCUT_9611a28d_cmn.outlv==3&&HCUT_9611a28d_cmn.fail){return;}

#define eq(...)		do{HCUT_9611a28d_assert_eq_sub(__VA_ARGS__, 0);HCUT_9611a28d_BACK}while(0)
#define HCUT_9611a28d_assert_eq_sub(a, ...)	\
	HCUT_9611a28d_assert_eq(__FILE__, __LINE__, #a, !!(a), 1, __VA_ARGS__)

#define eq_s(...)	do{HCUT_9611a28d_assert_eqs_sub(__VA_ARGS__, 0);HCUT_9611a28d_BACK}while(0)
#define HCUT_9611a28d_assert_eqs_sub(a, b, ...)	\
	HCUT_9611a28d_assert_eqs(__FILE__, __LINE__, #a ", " #b, a, b, __VA_ARGS__)

#define eq_i(...)	do{HCUT_9611a28d_assert_eqi_sub(__VA_ARGS__, 0);HCUT_9611a28d_BACK}while(0)
#define HCUT_9611a28d_assert_eqi_sub(a, b, ...)	\
	HCUT_9611a28d_assert_eqi(__FILE__, __LINE__, #a ", " #b, (int)a, (int)b, __VA_ARGS__)

#define eq_d(...)	do{HCUT_9611a28d_assert_eqd_sub(__VA_ARGS__, 0);HCUT_9611a28d_BACK}while(0)
#define HCUT_9611a28d_assert_eqd_sub(a, b, ...)	\
	HCUT_9611a28d_assert_eqd(__FILE__, __LINE__, #a ", " #b, (double)a, (double)b, __VA_ARGS__)

#define eq_p(...)	do{HCUT_9611a28d_assert_eqp_sub(__VA_ARGS__, 0);HCUT_9611a28d_BACK}while(0)
#define HCUT_9611a28d_assert_eqp_sub(a, b, ...)	\
	HCUT_9611a28d_assert_eqp(__FILE__, __LINE__, #a ", " #b, (char*)a, (char*)b, __VA_ARGS__)


//--coresrc
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

typedef struct HCUT_9611a28d_cmn_tag{
	FILE* fp;
	int outlv;
	const char* fcname;
	int fail;
	int suc;
} HCUT_9611a28d_cmn_t;
static HCUT_9611a28d_cmn_t HCUT_9611a28d_cmn;

int HCUT_9611a28d_pf(int lv, const char* fmt, ...){
	if( HCUT_9611a28d_cmn.outlv < lv ){ return 0;}
	if( HCUT_9611a28d_cmn.fp == NULL ){ return 0;}
	va_list va;
	va_start(va, fmt);
	int rc = vfprintf(HCUT_9611a28d_cmn.fp, fmt, va);
	va_end(va);
	return rc;
}

#define HCUT_9611a28d_ADD(tfname)	void tfname ## _sub(int argc, char** argv);	\
	void tfname(int argc, char** argv){		\
		HCUT_9611a28d_cmn.fcname = #tfname; tfname ## _sub(argc, argv);} \
	void tfname ## _sub(int argc, char** argv)

// *p=NULL ... kill valgrind reachable mem warning
#define HCUT_9611a28d_RUN(out, lv, ...)		int main(int argc, char** argv){	\
	HCUT_9611a28d_tf_t tfarr[]={ __VA_ARGS__, NULL};	\
	int rc=HCUT_9611a28d_run(out, lv, tfarr, argc, argv);	\
	char** p = (char**)&tfarr;	\
	*p=NULL;	\
	return rc;	\
}

typedef void (*HCUT_9611a28d_tf_t)(int argc, char** argv);
int HCUT_9611a28d_run(const char* out, int lv, HCUT_9611a28d_tf_t* tfarr, int argc, char** argv) {
	FILE* fp=NULL;
	; HCUT_Eact(lv<0||3<lv, "verboseLv allows only 0,1,2,3" , exit(-1) );
	if(strcmp(out, "stdout")==0 ){ HCUT_9611a28d_cmn.fp = stdout;}
	else if(strcmp(out, "stderr")==0 ){ HCUT_9611a28d_cmn.fp = stderr;}
	else if(strcmp(out, "NULL")==0 ){ HCUT_9611a28d_cmn.fp = NULL;}
	else if(1){
		fp = fopen(out, "w+");
		; HCUT_Eact(fp==NULL, "fopen() failed: ", fputs(out, stderr);exit(-1); );
		HCUT_9611a28d_cmn.fp = fp;
	}
	HCUT_9611a28d_cmn.outlv = lv;
	int i;
	for(i=0; tfarr[i]!=NULL; i++){
		tfarr[i](argc, argv);
		if(lv==3&&HCUT_9611a28d_cmn.fail){
			HCUT_9611a28d_pf(1, "detect test err & stop mode\n");
			break;
		}
	}
	HCUT_9611a28d_pf(1, "fail/all: %d/%d\n", HCUT_9611a28d_cmn.fail
		, HCUT_9611a28d_cmn.fail + HCUT_9611a28d_cmn.suc);
	if(fp){fclose(fp);}
	return HCUT_9611a28d_cmn.fail;
}

#define HCUT_9611a28d_assert_eq_SAME(fmt, lnum, str, a1, a2)	\
	va_list va;	\
	va_start(va, a2);	\
	const char* msg = va_arg(va, char*);	\
	if(msg==NULL){msg="-";}	\
	if(rc==1){	\
		HCUT_9611a28d_cmn.suc++;	\
		HCUT_9611a28d_pf(2, fmt, "suc", flname, lnum, HCUT_9611a28d_cmn.fcname, ckstr, a1, a2, msg); \
	}	\
	else if(1){	\
		HCUT_9611a28d_cmn.fail++;	\
		HCUT_9611a28d_pf(1, fmt, "FAIL", flname, lnum, HCUT_9611a28d_cmn.fcname, ckstr, a1, a2, msg); \
	}	\
	va_end(va);	\
/* mc_end */

void HCUT_9611a28d_assert_eq(const char* flname, int lnum
	, const char* ckstr, int a1, int a2, ...){
	int rc = (a1==a2);
	const char* fmt = "%s: %s:%d:%s(): (%s): %d(suc:%d)	:%s\n";
	HCUT_9611a28d_assert_eq_SAME(fmt, lnum, str, a1, a2);
}

void HCUT_9611a28d_assert_eqs(const char* flname, int lnum
	, const char* ckstr, const char* a1, const char* a2, ...){
	int rc = (0==strcmp(a1, a2));
	const char* fmt = "%s: %s:%d:%s(): (%s): %s =s= %s	:%s\n";
	HCUT_9611a28d_assert_eq_SAME(fmt, lnum, str, a1, a2);
}

void HCUT_9611a28d_assert_eqi(const char* flname, int lnum
	, const char* ckstr, int a1, int a2, ...){
	int rc = (a1==a2);
	const char* fmt = "%s: %s:%d:%s(): (%s): %d =i= %d	:%s\n";
	HCUT_9611a28d_assert_eq_SAME(fmt, lnum, str, a1, a2);
}

void HCUT_9611a28d_assert_eqd(const char* flname, int lnum
	, const char* ckstr, double a1, double a2, ...){
	int rc = (a1==a2);
	const char* fmt = "%s: %s:%d:%s(): (%s): %f =d= %f	:%s\n";
	HCUT_9611a28d_assert_eq_SAME(fmt, lnum, str, a1, a2);
}

void HCUT_9611a28d_assert_eqp(const char* flname, int lnum
	, const char* ckstr, void* a1, void* a2, ...){
	int rc = (a1==a2);
	const char* fmt = "%s: %s:%d:%s(): (%s): %p =p= %p	:%s\n";
	HCUT_9611a28d_assert_eq_SAME(fmt, lnum, str, a1, a2);
}
#endif /* HCUT_9611a28d */
/*SH_ED*/

/*SH_SC*/
// no_mainsrc. header only code.

#ifdef TEST
#include "hcut.h"
HCUT_ADD(t_hcut) {
	eq_i(1,2,"errck");
	eq_i(1,1,"errck");
}
#endif

/*SH_SMP
#include <stdio.h>
#include "hcut.h"

int hw() {
	puts("hw");
	return 0;
}

// expand to "void t_hw(int argc, char** argv){...}"
HCUT_ADD(t_hw) {
	int ck = 123;
	eq(hw() == 0 );
	eq(hw() == ck, "cmt if last arg(void*) is exist" );
}

int gw() {
	puts("gw");
	return 0;
}
HCUT_ADD(test_gw12345) {
	//assert type: cmp, int, dbl, str, ptr 
	eq(10==2, "all asserts support lastarg cmt");
	eq_i( gw(), 3);
	eq_d(1.01, 666);
	eq_s("aaa", "bbb");
	eq_p("aaa", NULL, "my_cmt");
	
	puts("my_test_end?");
}

HCUT_RUN("stderr", 1, t_hw, test_gw12345 )

// HCUT_RUN() holds "int main(int argc, char** argv){..}" and returns fail cnt.
// arg1 : msgout filename. "stdout","stderr","NULL" are used as FILE* or noout. 
// arg2 : verbose lv. 0/1/2/3 ... quiet/normal/verbose/stop
// arg3-: test all suite you set here.  (use ~$ sed -ne '/^HCUT/{=;p}' test.c)
// rtn/exit status: cnt of failed assert. rtn -1 if fatal err.
//SH_SMPE*/

#ifdef TEST
HCUT_RUN("stderr", 3,	/* keep newline. use for SH sed edit, -t test.*/
  t_hcut);
#endif

/*
 change log
 --
2021-11-30	Momi-g	<dmy@dmy.dmy>
	
	* hcut.sh.c(HCUT_RUN): add lv==3, stop if fail. v1.3.0
	* (HCUT..._BACK): add macro for lv==3 stop
	* (eq asserts): change macro expr >> statement for lv==3

2021-07-10	Momi-g	<dmy@dmy.dmy>
	
	* hcut.sh.c(HCUT_RUN): add *p=NULL, stop valgrind mem warning

2021-07-06	Momi-g	<dmy@dmy.dmy>
	
	* hcut.sh.c(HCUT_RUN): return 1 >> -1 if apierr

2021-06-26	Momi-g	<dmy@dmy.dmy>
	
	* hcut.sh.c(eq_p): change cast (void*) >> (char*) for allow ptr+1

2021-06-14	Momi-g	<dmy@dmy.dmy>
	
	* hcut.sh.c(macros): change mc, ERRact >> HCUT_Eact

2021-06-09	Momi-g	<dmy@dmy.dmy>
	
	* hcut.sh.c(macros): fix c99 -pedantic, __VA_ARGS__, out of standard syntax

2021-02-18	Momi-g	<dmy@dmy.dmy>
	
	* hcut.sh.c(HCUT_RUN): debug, add fclose()
	
	* hcut.sh.c(eq): rewrite. use cmn macro _SAME logic
	
	* hcut.sh.c(*SH_bn*): change output .hpp >> .h

2021-02-18	Momi-g	<dmy@dmy.dmy>
	
	* hcut.sh.c(assert_eq_SAME): debug, add va_end() at macro end

2021-01-19	Momi-g	<dmy@dmy.dmy>
	
	* hcut.sh.c(eq_i,d,p): fix macro, (a,b)	>> ( (int)a,(int)b ) etc

2020-10-28	Momi-g	<dmy@dmy.dmy>
	
	* hcut.sh.c: v1.2.2 release
	* hcut.sh.c(*SH_bn*): rename srcfile name, header suffix .cc -> .hpp
	* (brp.sh): add Rh="$bn.hpp", change .cc to .hpp

2020-10-25	Momi-g	<dmy@dmy.dmy>

	* hcut.sh.cc(assert_eqs): debug rc strcmp() >> (strcmp==0)
	change eqs args type void* >> const char*

2020-10-19	Momi-g	<dmy@dmy.dmy>

	* hcut.sh.cc: support cmdline data (argc, argv) at HCUT_ADD() codeblock
	* (doc): fix doc.

2020-06-30	Momi-g	<dmy@dmy.dmy>

	* hcut.sh.cc: v1.1.0 rewrite.

2020-02-02	Momi-g	<dmy@dmy.dmy>

	* hcut.sh.cc: v1.0.2 releases.

	* hcut.sh.cc (#define ast): change API stirng, ast->eq.
	* hcut.sh.cc (@sh-opt): fix Doc.

2020-1-11	Momi-g	<dmy@dmy.dmy>

	* hcut.sh.cc (@sh-opt): fix Doc.
	
2019-11-3	Momi-g	<dmy@dmy.dmy>
	
	* hcut.sh.cc: v1.0.1 releases.
	
	* hcut.sh.cc: (@sh-opt): build opt -b add. fix Doc. 

2019-10-15	Momi-g	<dmy@dmy.dmy>

	* hcut.sh.cc: v1.0.0 releases. 

*/
/*SH_ED*/

/*SH_OP _ set -e;a=`sed -ne "/${C}DF/!d;:l;n;/${C}DE/q;p;bl"<$R0`;eval "$a";set +e	#*/
/*SH_OP	h $p"-tsbS:test/eg/.o/.so -LMP:leak,mem,prof -f:funcs -o:bldout		GPLv3+"	 #*/
/*SH_OP	f sed -ne "/${C}DF/q;/;/d;/^[a-zA-Z].*)/p"<$R0 #*/
/*SH_OP t $e"$CW";ftt "$@";$p'cc -O0 -static -Wall -pedantic -g -pg -ggdb3 $Rm `fOI $Rs $tf` `fg $Rs $tf` `fL`'|fv	#*/
/*SH_OP T $e"$CW";ftt "$@";$p'cc -O3 $Rm `fOI $Rs $tf ` `fg $Rs $tf ` `fL`'|fv	#*/
/*SH_OP s $e"$CB";fgr0 "${C}SMP" "${C}SMPE"<$Rs|fbn>eg.c;$p'cc eg.c `fg eg.c` `fOI eg.c`'|fv #*/

/*SH_OP L $p"valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose ./a.out 2>&1|sed -e '/SUMMA/!d;n;n;n;n'"|fv #*/
/*SH_OP M $p"fM ./a.out"|fv	 #*/
/*SH_OP P $p'valgrind --tool=callgrind --callgrind-out-file=log.out ./a.out;kcachegrind log.out'|fv	 #*/

/*SH_OP b $e"$CW";$p'cc -c $Rs -pedantic -O2 -Wall -g `fg $Rs` `fI $Rs`'|fv;$p"$bn.o"	#*/
/*SH_OP B $e"$Cb";$p"ar -r lib$bn.a $bn.o `fO $Rs`"|fv;$p"lib$bn.a"	#*/
/*SH_OP A $e"$CB";$p'fA lib$bn.a `fg $Rh $Rs|fu|grep '[.]a$'|fU`'|fv;$p"lib$bn.a" #*/
/*SH_OP S $e"$Cb";$p"cc -shared -fPIC -o lib$bn.so $bn.o `fOI $Rs` `fg $Rs`"|fv;$p"lib$bn.so" #*/
/*SH_OP W $e"$Cm$O$Cw">/dev/null;fborn;$p"$Rs $Rh $tf"	#*/

/*SH_OP o $e"$CW";$p'fman $Rh 3'|fv		#*/

/*SH_DF
#-- noob
fman()( $p"fgr0 '${C}doc' '${C}docE'<$1|amn >$bn.$2
 mandoc -Thtml <$bn.$2 >$bn.$2.html
 #fgr0 '${C}doc' '${C}docE'<$1|amn -Dzwsp=1 >$Rm
 #man -Tutf8 /dev/stdin<$Rm|sed -e 's/.`printf \"\\b\"`//g'>$bn.$2.txt
 "|fv
)

#-- local

#-- vars
bn=`basename ${Rs%.*}`; tf=${Rs%/*}/${bn}.ts.${Rs##*.}; e="eval "; p="$Rp"
#-- mod
fv()(while read -r a;do $e"cat<<E$O# $a${O}E"|sed -e 's@-L.*-L[^ ]*@-L(omit)@g'>/dev/stderr;$e"$a";done)

fbn()(sed -e "s@\*${C##*]}bn\*@$bn@g"|frf|frv|flit)
fsn()(tr -s ' \t' '\n')
fsl()(tr -s '\n' ' ')
fu()(fsn|sort -u)
fU()(fu|fsl;$p)

fgr()(sed -e "/$1/!d;:l;/$2/{p;d};n;bl")	#切出
fgr0()(sed -ne "/$1/!d;:l;n;/$2/d;p;bl")	#抜き切出
fgR()(sed -ne "/$1/bl;p;d;:l;n;/$2/d;bl")	#切すて
fg()(sed -ne "s/.*${C##*]}co\*\([^*]*\).*$/\1/p" "$@"|fsn|awk '!a[$0]{a[$0]=1;print}'|fsl)

# fO src.o from inc"src.abc" etc. kick self
fO()(set -- `fdp "$@"|awk '$0~/[.](h|hpp)$/{print}'|sed -e 's/[.][^.]*$/.o/'|fU`
	buf="";for i;do test -f $i&&buf="$buf $i";done;$p"$buf"
)
fI()(fdp "$@"|sed -e 's/[^/]*$//g'|fu|sed -e '/./s/^/ -I/g'|fu|grep -v '^\-I$'|fU)
fL()(find -L `dirname $R0` -type d|sed -e 's/^/-L/g'|fU)
# inc""系.h,hpp,oをパス付きで羅列 OIはfdpが重複するので高速化でまとめる 複数file_ok
fOI()(
set -- `fdp "$@"`
s="-I./ "`$p"$@"|sed -e 's/[^/]*$//g'|fu|sed -e 's/^/ -I/g'|fu|grep -v '^\-I$'|fU`
set -- `$p"$@"|awk '$0~/[.](h|hpp)$/{print}'|sed -e 's/[.][^.]*$/.o/'|fU`
buf="";for i;do test -f $i&&buf="$buf $i";done;
$p"$buf $s"
)

# 依存inc""を再帰的に取得./以下全て self系はkick
fdp()( l="$*"; paths="$@"; all=""; used=""
 while :;do
	all=`$p$all $paths|fU`	#差分を追加 repの始末 差分たちからaaa.hを取得 partial path
	buf=`(cat $paths|sed -ne 's@^[ \t]*#inc[^"]*.\([a-zA-Z0-9._]*\)".*@\1@p')|sort -u`
	ch=`$p$used $buf|tr -s ' ' '\n'|sort|uniq -u`	#使用済は外す
	used="$used $ch"	#リスト更新
	paths=`fsvy $ch|sort -u`	#ls検索 name系のみのはず
	buf=`$p"$all" "$paths"|fU`	#増えたらloop
	[ ${#all} = ${#buf} ]&&break
 done
# initを除く
 set -- $all
 for i;do a=${i##*[/]}; a=${a%%.*};[ "${l##*$a*}" = "$l" ]&&set -- "$@" $i;shift;done
 $p"$@"
)

# corecode:search + depthck + uniq
fsvy()(c="find -L ./ -false"
	for i; do c="$c -o -path '*'$i";done; l=`$e"$c"`
	for i; do $p"$l"|grep -F "$i"|awk '{sv=$0;print gsub("[/]","") " " sv}'|
	sort -k 1.1,1n -k 2.2,2|awk '{print $2;exit}'; done
)

# libをまとめる
fA()(n=0;dir=`dirname $0`/tmpdir;mkdir $dir;cd $dir;
 for i;do
 	n=$((n+1))
 	cp ../$i $i
 	ar -x $i
 	for ii in *.o;do mv "$ii" "p${n}_$ii";done
 	ar -r lib$bn.aa *.o
 	rm *.o
 done
 $p'mv lib$bn.aa ../lib$bn.a'|fv
 cd ..;rm -r $dir
)

#-- yacc
# /*SH_OP y $e"$CW";fy
# /*SH_OP Y $e"$Cy";fU $( ($p"lib$bn.a";fg $Rs $Rh)|$n|grep '[.]a$'|$U)
fy()(
cat<<'EEE'|fv
f0 "${C}YACC" "${C}YACCE"<$Rs>myyacc.y
f0 "${C}LEX" "${C}LEXE"<$Rs>mylex.l
lex mylex.l; yacc -p zz -dv myyacc.y
cat y.tab.c lex.yy.c > $Rs
gcc -c y.tab.c lex.yy.c -lfl `fA $Rs $Rh`
rm mylex.l myyacc.y lib$bn.a
ar r lib$bn.a `fo $Rs` y.tab.o lex.yy.o
$p"lib$bn.a"
EEE
)

#-- longcmd
frf()(
 # *sh_rf* 0 a.txt b.txt ...でcat纏めて出力 top0でsrcinfoは無し出力
 awk -v tg="${C##*]}rf" 'index($0,tg){
 s=substr($0, index($0,tg)+length(tg)+1);split(s, a)
 m="[ -f \"%s\" ]&&(echo \"/*--copyfrom %s*\"/;cat \"%s\";echo \"/*--copyend %s*\"/)"
 mm="[ -f \"%s\" ]&&(_=\"%s\"/;cat \"%s\";_=\"%s\")"
 for(i=1;i in a;i++){v=a[i];if(v==0){m=mm;continue};system(sprintf(m,v,v,v,v)) }
 next
 }
 {print}'
)
frv()(buf=`awk '$1=="@_ver" {print $3;exit}'<$R0`;sed -e "s@\*${C##*]}ver\*@$buf@g")
flit()(sed -ne "/${C}lit/bl;p;d;:l;n;/${C}litE/d;"'s/[\]/&&/g;s/"/\\"/g;s@.*@"&\\n"@g;p;bl')

fte(){
 cat > $Rm-
 a="`sed -ne 's@^HCUT_ADD(\([^)]*\).*@\1, @p' $Rm-|tr -d '\n'`NULL"
 if [ $# != 0 ];then	a=""; for i;do a="$a $i,";done; a="$a NULL"; fi
 sed -ne "p;/_RUN/bl;d;:l;/[)]/{c\\$O $a)$O p;d};n;bl" $Rm-
 rm $Rm-
}
ftt()(fte "$@"<$tf>$Rm;mv $Rm $tf; cat $Rs $tf>$Rm)
fborn(){
 fgr0 "^#ifdef TEST" "^#endif"<$R0|fbn>$tf
 fgR "^#ifdef TEST_" "^#endif"<$R0 |fgr0 "^#ifdef TEST" "^#endif"|fbn|fte>tests.code
 fgR "^#ifdef TEST" "^#endif"<$Rs|fbn>$Rm;mv $Rm $Rs;fbn<$Rh>$Rm;mv $Rm $Rh
}
fM()(
 valgrind -q --tool=massif --massif-out-file=./vmem.buf --stacks=yes --trace-children=yes $1>/dev/null
 ms_print ./vmem.buf|sed -ne '/[KMG]B/bl;d;:l;/snap/q;p;n;bl';rm ./vmem.buf)

/*SH_DE*/
