#include <errmsg: see ... '~$ sh aaa.sh.c -h'   (other opt:no/-m/-w/)>	/*
C='^[/][/*]SH_';O=${0##*[/]};R=`dirname $0`;R=${R%/}/;R0=$R$O;Rb=$R${O%%.*};O="
";R=${0##*.};Rs=$Rb.$R;Rm=$Rb.tmp.$R;Rh=$Rb.h;R=$Rs$Rh$Rm;Rp='printf %s\n ';Rc=:
[ "${R##*$0*}" = '' ]&&$Rp"$0:NG suffix"&&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	*/

//SH_LS
/* 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/>.
*/
//SH_ED

//SH_HD

/*-*
@_name	msgp.c/h
@auther momi-g
@brief	printf() wrapper functions
@_synopsys
	void pf1-3(const char* fmt, ...)
	void npf1-3(const char* fmt, ...)
	void epf(const char* fmt, ...)
	void nepf(condt char* fmt, ...)
	void dbg(...)
	
	pf_t pfset(FILE* p1, FILE* p2, FILE* p3, FILE* dg)
	pf_t pfget(void)
	pf_t pfpush(pf_t obj)
@_eg
 #include "msgp.h"
 int main() {
	pf1("aa");	// automatically add '\n'
	npf2("bb");		// header 'n' no newline
	pf3("cc");
	dbg("dd");	// >> disp: aa, bb, cc, dd
	
	pfset(NULL,stderr,NULL,PF_NOCHANGE);		//>> pf1,pf2,pf3,dbg setting
	npf1("aa");
	pf2("bb");
	pf3("cc");
	dbg("dbg is", 123, "valiable");		// >> disp: bb, dbg

	pf_t old = pfset(stdout, stdout, stdout, stderr);	//>> dfl setting
	pf1("info: %s", old.tostring);
	epf("epf/nepf is always disp to stderr.");
 }
 // ~$ gcc src.c msgp.c

@param fmt	printf fmt. 
@param p1-dg	output dst ptr + NULL(noout), PF_NOCHANGE(use before fp) 
@param obj	output setting rtns before setting obj. use it to restore.
@return 	pfset()/pfpush() rtns before setting. pfget() rtns current setting.
@details
 dbg() is printf debug helper. not need args type(int, char* etc).
	eg)	dbg(12,num,s,"comment",1.11);		// noarg, dbg() is err.
 >>>
 DBG: src.c 7: main(): cnt:1 pid:17477 arg:(12,num,s,"comment",1.11)
	12 = i:12 / d:-1.72631 / p:0xc / c:\014 '\f' / s:(mem_err)
	num = i:678 / d:-1.72631 / p:0x2a6 / c:\246 '?' / s:(mem_err)
	s = i:4636870 / d:-1.72631 / p:0x46c0c6 / c:\306 '?' / s:abc
	"comment" = i:4636911 / d:-1.72631 / p:0x46c0ef / c:\357 '?' / s:comment
	1.11 = i:1546188227 / d:1.11 / p:0x5c28f5c3 / c:\303 '?' / s:(mem_err)
 
 the type of argument is not specified. guess type and convert.
	i/d/p/c/s == int/dbl/ptr/char/str

 dbg() are stricted output size. max 1024 byte(1024 char) at once.
 dbg() supports variable args. allows at least 0-9 args.
 dbg() uses SIGSEGV/longjmp. it may cause some troubles if your pg uses signals.
	
@_note -
@_conforming posix-2001+
@version 1.2.0, 2021-01-09
-*/

#ifndef b7b7ab95edd0
#define b7b7ab95edd0

#include <features.h>
#if ( _POSIX_C_SOURCE +0 < 200112L )
	#error	"needs compiler posix-2001 or upper(c99+)"
	#include "//dmy inc, preprocess stoper"
#endif

/*replace printf()*/
#define pf1(...)	mpfl_b7b7(1, 1, __VA_ARGS__)
#define pf2(...)	mpfl_b7b7(2, 1, __VA_ARGS__)
#define pf3(...)	mpfl_b7b7(3, 1, __VA_ARGS__)
#define epf(...)	mpfl_b7b7(0, 1, __VA_ARGS__)

#define npf1(...)	mpfl_b7b7(1, 0, __VA_ARGS__)
#define npf2(...)	mpfl_b7b7(2, 0, __VA_ARGS__)
#define npf3(...)	mpfl_b7b7(3, 0, __VA_ARGS__)
#define nepf(...)	mpfl_b7b7(0, 0, __VA_ARGS__)

/* setting*/
#define pfset(...)  pfset_b7b7(#__VA_ARGS__ , __VA_ARGS__)
#define pfget(...)	pfget_b7f7(__VA_ARGS__)
#define pfpush(...)	pfpush_b7f7(__VA_ARGS__)

#include <stdint.h>	//uint
#include <stdio.h>	//FILE* type

#define  PF_MAXINFOLEN 255
struct pfsetdata {
	char tostring[PF_MAXINFOLEN + 1];	// for tostring, "stderr, stdout ..."
	FILE* arrfp[5];	// epf + pf1,pf2,pf3,dbg
} typedef pf_t;

void mpfl_b7b7(int tgt, int nflg, const char* fmt, ...);
pf_t pfset_b7b7(const char* s, void* pf1, void* pf2, void* pf3, void* dbg);
pf_t pfget_b7f7(void);
pf_t pfpush_b7f7(pf_t pulldata);

#define PF_NOOUT	NULL
#define PF_NOCHANGE	&pfset_b7b7

//dbg print
// from licence: cc-by-sa
// https://stackoverflow.com/questions/2632300	Q: Ed Marty  ->  A: Matthew Slattery
#define ARGCNT(...) ARGCNT_2(__VA_ARGS__,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1)
#define ARGCNT_2(args,n16,n15,n14,n13,n12,n11,n10,n9,n8,n7,n6,n5,n4,n3,n2,num,...)	ARGCNT_3(num)
#define ARGCNT_3(a) ITERMCR_ ## a

// https://stackoverflow.com/questions/44479282 Q: user1150609 -> A: H Walters
#define ITERMCR_1(a) 		dbg_b7b7_middle( 1, #a,a)
#define ITERMCR_2(a,...)	dbg_b7b7_middle( 2, #a,a); ITERMCR_1(__VA_ARGS__)
#define ITERMCR_3(a,...)	dbg_b7b7_middle( 3, #a,a); ITERMCR_2(__VA_ARGS__)
#define ITERMCR_4(a,...) 	dbg_b7b7_middle( 4, #a,a); ITERMCR_3(__VA_ARGS__)
#define ITERMCR_5(a,...) 	dbg_b7b7_middle( 5, #a,a); ITERMCR_4(__VA_ARGS__)
#define ITERMCR_6(a,...) 	dbg_b7b7_middle( 6, #a,a); ITERMCR_5(__VA_ARGS__)
#define ITERMCR_7(a,...) 	dbg_b7b7_middle( 7, #a,a); ITERMCR_6(__VA_ARGS__)
#define ITERMCR_8(a,...) 	dbg_b7b7_middle( 8, #a,a); ITERMCR_7(__VA_ARGS__)
#define ITERMCR_9(a,...) 	dbg_b7b7_middle( 9, #a,a); ITERMCR_8(__VA_ARGS__)
#define ITERMCR_10(a,...) 	dbg_b7b7_middle(10, #a,a); ITERMCR_9(__VA_ARGS__)
#define ITERMCR_11(a,...) 	dbg_b7b7_middle(11, #a,a); ITERMCR_10(__VA_ARGS__)
#define ITERMCR_12(a,...) 	dbg_b7b7_middle(12, #a,a); ITERMCR_11(__VA_ARGS__)
#define ITERMCR_13(a,...) 	dbg_b7b7_middle(13, #a,a); ITERMCR_12(__VA_ARGS__)
#define ITERMCR_14(a,...) 	dbg_b7b7_middle(14, #a,a); ITERMCR_13(__VA_ARGS__)
#define ITERMCR_15(a,...) 	dbg_b7b7_middle(15, #a,a); ITERMCR_14(__VA_ARGS__)
#define ITERMCR_16(a,...) 	dbg_b7b7_middle(16, #a,a); ITERMCR_15(__VA_ARGS__)
// end licence:	cc-by-sa

//ref: http://idlysphere.blog66.fc2.com/blog-entry-181.html 可愛い。
//ref: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf >>A1.7 Punctuators（区切り子）
//ref: https://bytes.com/topic/c/answers/218725-does-not-give-valid-preprocessing-token-why
//ref: https://ja.stackoverflow.com/questions/34093
//ref: http://www.c-lang.org/detail/macro_detail.html	#__line__ TYPE

#define DBG_B7B7_MLEN	100
#define DBG_B7B7_BUFFSIZE	1024

#define	dbg(...)	dbg_sub( __VA_ARGS__+0)
#define	dbg_sub(...)	dbg_b7b7_top(4, __FILE__, __LINE__, __func__, #__VA_ARGS__)\
		; ARGCNT(__VA_ARGS__)(__VA_ARGS__) ;dbg_b7b7_end(4)

void dbg_b7b7_top(int plv, const char* fname, int nline, \
            const char* fcname, const char* rawstr);
void dbg_b7b7_middle(int agnum, char* rawlit, ...);
void dbg_b7b7_end(int plv);
// dbg() ... err

//add
void idbg_b7b7_top(void);
void idbg_b7b7_end(void);

#define	idbg(...)	idbg_b7b7_top();dbg_b7b7_top(4, __FILE__, __LINE__, __func__, #__VA_ARGS__)\
		; ARGCNT(__VA_ARGS__)(__VA_ARGS__) ;dbg_b7b7_end(4);idbg_b7b7_end();
#endif
//SH_ED

//SH_SC
#include "*SH_bn*.h"

#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>		//exit()
#include <errno.h>
#include <unistd.h>	//getpid()	fd, STDERR_FILENO.  FILE* ... stdout stream.
#include <stdint.h>	//intptr_t type
#include <signal.h> //siginfo_t

static int	GLdbgcounter = 0;
char GLdbgbuffer[DBG_B7B7_BUFFSIZE+1]={};
static pf_t	pfdata; //pfdata={NULL,{NULL, x 4} }; //glは自動でnil init
static int	GLdbgmode = 0;	//add. idbg
static char sbuf[PF_MAXINFOLEN+1];

#ifdef TEST
#include "hcut.hpp"
#include "msgp.h"
#endif

//------
static void pfdata_initck(void) {
	if(pfdata.tostring[0] == '\0') {
		strncpy(pfdata.tostring, "stdout, stdout, stdout, stderr", PF_MAXINFOLEN);
		pfdata.arrfp[0] = stderr;	//epf(). fixed. static. nochange.
		pfdata.arrfp[1] = stdout;
		pfdata.arrfp[2] = stdout;
		pfdata.arrfp[3] = stdout;
		pfdata.arrfp[4] = stderr;
	}
}
// stdout系はマクロなのでまともに初期化子として扱えない。ヘッダで初期化出来ない。
// 全ての関連関数で監視して初利用でトラップ初期化しかない。

//printf() parser
void mpfl_b7b7(int plv, int nlflg, const char* fmt, ...) {
	pfdata_initck();
	FILE* fp = pfdata.arrfp[plv];
	va_list vl;			// vl = ...   vl means pointer or lists of 3dot(many args)
	va_start(vl, fmt);
	if(fp == NULL) {
		goto MPFL_END;
	}
	vfprintf(fp, fmt, vl);
	if(nlflg==1) {
		fprintf(fp, "\n");
	}
	fflush(fp);
MPFL_END:
	va_end(vl);		//macros end
	return;
}

//setting funcs
pf_t pfset_b7b7(const char* s, void* p1, void* p2, void* p3, void* dg) {
	// constのカンマで数えられる。//pfset(0x1111,0x1032)	etc.
	pfdata_initck();
	pf_t rbuf=pfdata;
	char* bbuf[5];
	//元をスプリットしとく
	bbuf[1]=strtok(pfdata.tostring, ", \t\n");
	for(int i=2; i<5; i++) {
		bbuf[i]=strtok(NULL, ", \t\n");
	}
	//更新もスプリットする
	char* inbuf=(char*) calloc(PF_MAXINFOLEN +1, sizeof(char));
	strncpy(inbuf, s, PF_MAXINFOLEN);
	char* abuf[5];
	abuf[1]=strtok(inbuf, ", \t\n");
	for(int i=2; i<5; i++) {
		abuf[i]=strtok(NULL, ", \t\n");
	}

	if(p1 == (void*)PF_NOCHANGE) {	abuf[1] = bbuf[1]; p1=(void*)pfdata.arrfp[1] ;}
	if(p2 == (void*)PF_NOCHANGE) {	abuf[2] = bbuf[2]; p2=(void*)pfdata.arrfp[2] ;}
	if(p3 == (void*)PF_NOCHANGE) {	abuf[3] = bbuf[3]; p3=(void*)pfdata.arrfp[3] ;}
	if(dg == (void*)PF_NOCHANGE) {	abuf[4] = bbuf[4]; dg=(void*)pfdata.arrfp[4] ;}

	sbuf[0] ='\0';
	snprintf(sbuf, PF_MAXINFOLEN, "%s, %s, %s, %s", abuf[1], abuf[2], abuf[3], abuf[4] );
	strcpy(pfdata.tostring, sbuf);
	pfdata.arrfp[1] = (FILE*)p1;
	pfdata.arrfp[2] = (FILE*)p2;
	pfdata.arrfp[3] = (FILE*)p3;
	pfdata.arrfp[4] = (FILE*)dg;
	free(inbuf);
	return rbuf;
}

pf_t pfget_b7f7(void) {
	pfdata_initck();
	return pfdata;
}
pf_t pfpush_b7f7(pf_t obj) {
	pfdata_initck();
	pf_t rbuf=pfdata;
	pfdata = obj;
	return rbuf;
}

//dbg funcs
//add numonly
void idbg_b7b7_top(void){ GLdbgmode= -1;};
void idbg_b7b7_end(void){ GLdbgmode= 0;};

#include <setjmp.h>
static sigjmp_buf marktry;	// try-catch sigsegv. sigjmp ... posix2001 upper
static void dbg_siggrep(int flg, int signum);
void dbg_b7b7_top(int plv, const char* fname, int nline
 , const char* fcname, const char* rawstr) {
	pfdata_initck();
	FILE* fp = pfdata.arrfp[plv];
	GLdbgcounter++;		//global counter for debag info
	if(fp == NULL|| GLdbgbuffer[0]!='\0') { return; }
	int orest=  DBG_B7B7_BUFFSIZE - 1;
	GLdbgbuffer[0]='\0';
	snprintf( GLdbgbuffer, orest,
         "DBG: %s %d: %s(): cnt:%d pid:%d arg:(%.*s)\n",
         fname, nline, fcname, GLdbgcounter, getpid(), strlen(rawstr)-2, rawstr);
	return;
}

void dbg_b7b7_middle(int agnum, char* rawlit, ...) {
	if( GLdbgbuffer[0]=='\0') { return; }
	if(agnum==1 && strlen(rawlit)==2){return;}	//dbg()系。要素があれば+0で3以上になる。
	
	int cutsz=0;
	if(agnum==1){cutsz=2;}
	
	char sbuf[DBG_B7B7_MLEN+1]={0};
	int orest = DBG_B7B7_MLEN;
	snprintf( sbuf, orest, "\t%.*s = ", strlen(rawlit)-cutsz, rawlit);
	orest=DBG_B7B7_MLEN - strlen(sbuf);

	va_list vl, vls;
	va_start(vl, rawlit);
	va_copy(vls, vl);

	int ri;
	double rd;
	char* rp;
	//int系. double系が先だとそっちに行くから。アクセス違反はsegvで逃げる。
	ri = va_arg(vl, int);	//local変数 int未満は全部intに格上げされる。
	snprintf( &(sbuf[strlen(sbuf)]), orest, "i:%d / ", ri);
	orest=DBG_B7B7_MLEN - strlen(sbuf);

	//double系.
	va_copy(vl, vls);
	rd = va_arg(vl, double);
	snprintf( &(sbuf[strlen(sbuf)]), orest, "d:%g / ", rd);
	orest=DBG_B7B7_MLEN - strlen(sbuf);
	
	//ptr
	va_copy(vl, vls);
	rp = va_arg(vl, char*);	// vl_add -> argpos_add -> argraw(int/char*)
	snprintf( &(sbuf[strlen(sbuf)]), orest, "p:%p / ", rp);
	orest=DBG_B7B7_MLEN - 1 - strlen(sbuf);
	
	//char
	// %hh + c ... int args. not char args. (promote args problem. see man printf)
	// edit escape chars \n>>\\n etc
//	va_start(vl, rawlit);
//	ri = va_arg(vl, int);	
	char cbuf[2]= {(char)ri, 0};
	char* msg;
	if(cbuf[0] == '\0') { msg = (char*)"\\0";}
	else if(cbuf[0] == '\a') { msg = (char*)"\\a";}
	else if(cbuf[0] == '\b') { msg = (char*)"\\b";}
	else if(cbuf[0] == '\t') { msg = (char*)"\\t"; }
	else if(cbuf[0] == '\n') { msg = (char*)"\\n";}
	else if(cbuf[0] == '\v') { msg = (char*)"\\v";}
	else if(cbuf[0] == '\f') { msg = (char*)"\\f";}
	else if(cbuf[0] == '\r') { msg = (char*)"\\r";}
	else if(1){ msg = cbuf; }
	
	// hh ... size select
	snprintf( &(sbuf[strlen(sbuf)]), orest, "c:\134%03hho '%s' / s:", ri, msg );
	orest=DBG_B7B7_MLEN - strlen(sbuf);

	//string
	if(GLdbgmode != -1 ){
		//add str skip mode. avoid SEGV trap err
		// http://www.nurs.or.jp/~sug/soft/super/longjmp.htm
		// try - catch signal stop
		if(sigsetjmp(marktry, SIGSEGV) == 0) {
			dbg_siggrep(1, SIGSEGV);	//set trap
			// #include <setjmp.h>
			strncpy( &(sbuf[strlen(sbuf)] ), rp, orest);
		} else {
			strncpy( &(sbuf[strlen(sbuf)]), "(mem_err)", orest );
		}
		dbg_siggrep(0, SIGSEGV);	//reset trap
	}else{
		strncpy( &(sbuf[strlen(sbuf)]), "(skip)", orest );
	}

	va_end(vl);
	va_end(vls);

	//add + \n
	if(strlen(sbuf) >= DBG_B7B7_MLEN-1 ){ sbuf[DBG_B7B7_MLEN-1]='*'; }
	int len = strlen(GLdbgbuffer);
	strncpy( &(GLdbgbuffer[len]), sbuf, DBG_B7B7_BUFFSIZE-len-1);
	len = strlen(GLdbgbuffer);
	if( len+1 == DBG_B7B7_BUFFSIZE ){GLdbgbuffer[len-1]='\n';}
	else{GLdbgbuffer[len]='\n';}
	return;
}

void dbg_b7b7_end(int plv) {
	FILE* fp = pfdata.arrfp[plv];
	if(fp==NULL) {goto lbl_RTN;}
	fprintf(fp, "%s\n", GLdbgbuffer);
	fflush(fp);		//	buff is an evil
lbl_RTN:;	
	GLdbgbuffer[0]='\0';
	return;
}

static void dbg_sigfunc(int signum, siginfo_t* info, void* ctx) {
	//sighandle, sigcatch?, siginterrrupt?,sig"exception code(java)"?
	//in code, very,very, verrrry stricted.
	//1. use only "signal safe function"
	//2. use global(outer function) var, "volatile sig_atomic_t"
	//3. you can use auto var (stack, local memory?)
	//http://d.hatena.ne.jp/yupo5656/20040712/p2
	siglongjmp(marktry, 1);
	//	char msg[] = "sig_catch. err?\n";
	//	write(STDERR_FILENO, msg, strlen(msg) );	// #include <unistd.h>
}

// (1/0, SIGSEGV) on, off. if get sig, rtn 1(dbg_sigfunc), else rtn 0.
static void dbg_siggrep(int flg, int signum) {
	static struct sigaction sct_ss;	//snapshot for save state.
	struct sigaction sct = {0};
	sigemptyset(&(sct.sa_mask));	//clear. 0xFFFF -> block recall SIG when this method running.
	sigaddset(&sct.sa_mask, signum);	// target signal
	if(flg==0) {		// reset trap
		sigaction(signum, &sct_ss, NULL);
		//sct.sa_handler = SIG_DFL;	//SIG_DFL or SIG_IGN
	} else {	//set trap
		//signal select action 1.def 2.ign 3.func
		//3.func ...SA_SIGINFO+sa_sigaction or sct.sa_handler = *func.
		//sct.sa_handler = SIG_IGN;	//set proc sig work enable(def) or disable(catch) .
		sct.sa_flags = SA_SIGINFO;	//send sig state to myfunc();
		sct.sa_flags |= SA_RESETHAND;	//add one shot setting.
		//		zzz.sa_flags = zzz.sa_flags | SA_RESTART;	//default sigtrap drop blocking method.
		//sct.sa_flags |= SA_NOCLDWAIT;	//if SIG_DEF.  SIG_IGN ignore sa_flags.
		sct.sa_sigaction = dbg_sigfunc;	// restricted type, (int, siginfo_t *, void *)
		sigaction(signum, &sct, &sct_ss);		//run(set) trap  NULL...oldsetting buf.
	}
}

#ifdef TEST
HCUT_ADD(t_dbg) {
	dbg(1,10);
	int a=100;
	char* s = "unkokko";
	dbg(3,10.2, 10.3, s, 'z', a);
	dbg();
}
#endif

/*SH_SMP	sample
#include "msgp.h"

int main() {
	int a= 123 ;
	int b = 223;
	double c = 12.3;
	char* d = NULL;
	dbg(a,c,b,d);
//	return 0;
	
	epf("epf");
	pf1("pf1");
	npf1("npf1\n");
	pf2("pf2");
	pf3("pf3");
	dbg("dbg", printf("abc%d\n",1) );
	puts("--");
//
	FILE* fp = stdout;
	pf_t old=pfset(NULL,PF_NOCHANGE,fp,NULL);	// pf1,pf2,pf3,dbg. NULL=noout
	pf_t now=pfget();
	puts(old.tostring);	// >> stdout,sout,sout,serr
	puts(now.tostring);	// >> NULL,stdout(...PF_NOCHANGE),fp,NULL
	epf("epf");
	pf1("pf1");
	pf2("pf2");
	pf3("pf3");
	dbg("dbg");
	puts("--");
//
	old = pfset(stdout,NULL,NULL, stdout);
	now = pfget();
	puts(old.tostring);	// >> NULL,stdout,fp,NULL
	puts(now.tostring);	// >> stdout,NULL,NULL, stdout
	epf("epf");
	pf1("pf1");
	pf2("pf2");
	pf3("pf3");
	dbg("dbg");
	puts("--");
	//
	old = pfpush(old);
	now = pfget();
	puts(old.tostring);	// >> stdout,NULL,NULL, stdout	... save&load.
	puts(now.tostring);	// >> NULL,stdout,fp,NULL
	epf("epf");
	pf1("pf1");
	pf2("pf2");
	pf3("pf3");
	dbg("dbg");
	puts("--");
}
/*SH_SMPE	sampleend*/

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

/*
 change log
 --
2021-01-18  Momi-g	<dmy@dmy.dmy>

	* msgp.sh.c ( dbg() macro ): improve __VA_ARGS__+0, allow dbg(void) api
	* (dbg_b7b7_top): fix msg to skip "+0"
	* (dbg_b7b7_middle): fix msg, add dbg(void) ck code 
	* (t_dbg test): add test 
	
2021-01-09  Momi-g	<dmy@dmy.dmy>

	* msgp.sh.c (pfset): fix pfset strcpy src/dst overlap bug
	
	* msgp.sh.c (hcut.hpp): adapt new unittest macro
	
	* msgp.sh.c (sh code): adapt new build script

2020-05-15  Momi-g	<dmy@dmy.dmy>

	* msgp.sh.c (dbg): rewrite code, fix va_arg double bug

*/
/*SH_ED*/


/*SH_OP _ a=`sed -ne "/${C}DF/!d;:l;n;/${C}DE/q;p;bl"<$R0`;eval "$a"	#*/

/*SH_OP	h $p"-t/s/b:test/smpl/bld /T:t+mem /f:functop"	 #*/
/*SH_OP	f sed -ne 's@^HCUT_ADD(\([^)]*\).*@\1,@p'<$R0;sed -e '/^[a-z].*)/!d;/;/d'<$R0 #*/

/*SH_OP t $e"$CW";[ "$1" != "" ]&&$i4;cat $Rs $tf>$Rm;$p'cc -g $Rm `fA $Rm`'|fv #*/
/*SH_OP T $e"$Ct";$p"valgrind --leak-check=full ./a.out 2>&1|sed -e '/SUMMA/!d;n;n;n;n'"|fv #*/
/*SH_OP M $p"$i5"|fv	 #*/
/*SH_OP s $e"$CB";f0 "${C}SMP" "${C}SMPE"<$Rs>eg.c;$p'cc eg.c $Rs `fA eg.c`'|fv #*/

/*SH_OP b $e"$CW";$p'cc -c $Rs -O2 -Wall `fd`'|fv;$p"$bn.o"		#*/
/*SH_OP B $e"$Cb";$p"rm lib$bn.a;ar r lib$bn.a `fo $Rs` $bn.o"|fv;$p"lib$bn.a"	#*/
/*SH_OP U $e"$CB";fU $( ($p"lib$bn.a";fg "$Rh" "$Rs")|$n|grep '[.]a$'|$u)	#*/
/*SH_OP S $e"$CW";$p'cc $Rs `fA $Rs $Rh` -g -pg -Wall -shared -fPIC -o lib'$bn'.so'|fv;$p"lib$bn.so" #*/
/*SH_OP W $e"$Cm$O$Cw";$i0;$i1;$cn<$Rh>$Rm;mv $Rm $Rh;$p"$Rs $tf"	#*/

/*SH_DF
#--name
bn=`basename ${Rs%.*}`
tf=${bn}_ts.${Rs##*.}

#--cmd
e="eval "
p="$Rp" 
cn=$e'sed -e "s@\*${C##*]}bn\*@$bn@g"'
n=$e"tr ' \t' '\n'|grep -v ^$"
N=$e'(tr "\n" " ";$p)'
u=$e'$n|sort -u|$N'

f0()(sed -ne "/$1/!d;:l;n;/$2/d;p;bl"|$cn)
f1()(sed -e "/$1/!d;:l;/$2/{p;d};n;bl"|$cn)

fo()(a=$#;set `cat $@|(sed -ne 's/^#in.*"\(.*\.h\)".*$/\1/p';$p"$@")|$u`
if [ $a = $# ];then $p"$@"|$n|sed -ne 's/\.h$/.o/p'|(grep -vF "$bn.o";ls)|
sort|uniq -d|$u;else fo "$@";fi)
fg()(sed -ne 's/.*\*SH_co\*\([^*]*\).*$/\1/p' "$@"|$u)

fd()(find `dirname $0` -type d|sed -e 's/\(.*\)/ -I\1 -L\1 /g'|$N)
fA()( (fo "$@";fg "$@";fd "$@")|$N)
fU()(n=0;dir=`dirname $0`/tmpdir;mkdir $dir||exit 1;cd $dir||exit 1;
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
mv lib$bn.aa ../lib$bn.a
cd ..;rm -r $dir
)

fv()(while read -r a;do $e"cat<<E$O# $a${O}E">/dev/stderr;$e"$a";done)

i0=$e'f0 "^#ifdef TEST" "^#endif"<$Rm|$cn>$tf'
i1=$e'sed -ne "/^#ifdef TEST/bl;p;d;:l;n;/^#endif/d;bl"<$Rs|$cn>$Rm;mv $Rm $Rs'
i4=$e'sed -ne "p;/_RUN/bl;d;:l;/);/{c\\$O $1);$O p;d};n;bl"<$tf>$Rm;mv $Rm $tf'
i5="valgrind -q --tool=massif --massif-out-file=./vmem.buf \
--stacks=yes --trace-children=yes ./a.out >/dev/null
ms_print ./vmem.buf|sed -ne '/[KMG]B/bl;d;:l;/snap/q;p;n;bl';rm ./vmem.buf"

/*SH_DE*/
