#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) 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);
 >>>
 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 2021-07-10 1.2.3
-*/

#ifndef b7b7ab95edd0
#define b7b7ab95edd0

#include <features.h>
#if ( _POSIX_C_SOURCE +0 < 200112L )
	#include	"needs compiler posix-2001 or upper(c99+)"
#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);

extern int pfset_b7b7_ENDPTR;
#define PF_NOOUT	NULL
#define PF_NOCHANGE	&pfset_b7b7_ENDPTR

// dbg() dmydata for b/o read
typedef union dbg_b7b7_argtag{
	int i;
	char* p;
	double d;
} dbg_b7b7_arg_t;

// from license: cc-by-sa 2.5/3.0 (code is changed from the orig)
// 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,(dbg_b7b7_arg_t){0} )
#define ITERMCR_2(a,...)	dbg_b7b7_middle( 2, #a,a,(dbg_b7b7_arg_t){0} ); ITERMCR_1(__VA_ARGS__)
#define ITERMCR_3(a,...)	dbg_b7b7_middle( 3, #a,a,(dbg_b7b7_arg_t){0} ); ITERMCR_2(__VA_ARGS__)
#define ITERMCR_4(a,...) 	dbg_b7b7_middle( 4, #a,a,(dbg_b7b7_arg_t){0} ); ITERMCR_3(__VA_ARGS__)
#define ITERMCR_5(a,...) 	dbg_b7b7_middle( 5, #a,a,(dbg_b7b7_arg_t){0} ); ITERMCR_4(__VA_ARGS__)
#define ITERMCR_6(a,...) 	dbg_b7b7_middle( 6, #a,a,(dbg_b7b7_arg_t){0} ); ITERMCR_5(__VA_ARGS__)
#define ITERMCR_7(a,...) 	dbg_b7b7_middle( 7, #a,a,(dbg_b7b7_arg_t){0} ); ITERMCR_6(__VA_ARGS__)
#define ITERMCR_8(a,...) 	dbg_b7b7_middle( 8, #a,a,(dbg_b7b7_arg_t){0} ); ITERMCR_7(__VA_ARGS__)
#define ITERMCR_9(a,...) 	dbg_b7b7_middle( 9, #a,a,(dbg_b7b7_arg_t){0} ); ITERMCR_8(__VA_ARGS__)
#define ITERMCR_10(a,...) 	dbg_b7b7_middle(10, #a,a,(dbg_b7b7_arg_t){0} ); ITERMCR_9(__VA_ARGS__)
#define ITERMCR_11(a,...) 	dbg_b7b7_middle(11, #a,a,(dbg_b7b7_arg_t){0} ); ITERMCR_10(__VA_ARGS__)
#define ITERMCR_12(a,...) 	dbg_b7b7_middle(12, #a,a,(dbg_b7b7_arg_t){0} ); ITERMCR_11(__VA_ARGS__)
#define ITERMCR_13(a,...) 	dbg_b7b7_middle(13, #a,a,(dbg_b7b7_arg_t){0} ); ITERMCR_12(__VA_ARGS__)
#define ITERMCR_14(a,...) 	dbg_b7b7_middle(14, #a,a,(dbg_b7b7_arg_t){0} ); ITERMCR_13(__VA_ARGS__)
#define ITERMCR_15(a,...) 	dbg_b7b7_middle(15, #a,a,(dbg_b7b7_arg_t){0} ); ITERMCR_14(__VA_ARGS__)
#define ITERMCR_16(a,...) 	dbg_b7b7_middle(16, #a,a,(dbg_b7b7_arg_t){0} ); ITERMCR_15(__VA_ARGS__)
// end license:	cc-by-sa 2.5/3.0

//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_sub( __VA_ARGS__+0)
#define	idbg_sub(...)	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]={0};
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.h"
#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
int pfset_b7b7_ENDPTR=0;
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;}	//尻尾に+0がついてるのでキリトリセン
	
	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:%f / ", 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);
	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("--");
}
//~$ gcc src.c msgp.c
//SH_SMPE*/

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

/*
 change log
 --
2021-07-10  Momi-g	<dmy@dmy.dmy>

	* *SH_bn*.h(dbg_middle): add (dbg_b7b7_arg_t){0}, fix b/o read.
 
2021-05-21  Momi-g	<dmy@dmy.dmy>

	* *SH_bn*.h(PF_NOCHANGE): funcptr>>glvar ptr. func <> void* is gray code.

	* *SH_bn*.h(-pedantic): debug code for -Wpedantic.

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

	* *SH_bn*.h(doc): fix cmtdoc, allow dbg(void)

2021-02-22  Momi-g	<dmy@dmy.dmy>

	* *SH_bn*.h(idbg): unofficial api, add+improve. dispname cutoff bug

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 _ set -e;a=`sed -ne "/${C}DF/!d;:l;n;/${C}DE/q;p;bl"<$R0`;eval "$a"	#*/
/*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-z].*)/p"<$R0 #*/
/*SH_OP t $e"$CW";ftt "$@";$p'cc -O0 -Wall -pedantic -g -pg -ggdb3 $tf $Rs `fOI $Rs $tf` `fg $Rs $tf` `fL`'|fv	#*/
/*SH_OP T $e"$CW";ftt "$@";$p'cc -O2 $tf $Rs `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`'|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;$i0;$i1;$p"$Rs $Rh $tf"	#*/
/*SH_OP o $e"$CW";$p"rm $tf"|fv;fman $R0 3	#*/

/*SH_DF
#-- noob
fman()(fgr0 "${C}doc" "${C}docE"<$1>$Rm
cat $Rm|sed -e's/^@\([_a-zA-Z][_[:alnum:]]*\)/\n\n# \1\n\n/g
/^\\/{s/^\\/<br>/g}'>$bn.md
cat $bn.md|pandoc -f markdown -thtml> $Rm
cat $Rm|pandoc -s -f html -t man -M title="$bn" -M section="$2" \
-M date=`date '+%Y-%m-%d'` > $bn.$2
sed -e 's/^.nf/.RE\n.nf/'<$bn.$2>$Rm
mv $Rm $bn.$2
fhtml
)
fhtml()( md2h $bn.md $bn )

#-- 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)
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()(
 awk -v r="${C##*]}rf" 'match($0,r){
 s=substr($0, RSTART+RLENGTH+1)
 gsub(/.[^*]*$/, "", s);split(s, a)
 m="[ -f %s ]&&echo \"/*--copyfrom %s*\"/&&cat %s&&echo \"/*--copyend %s*\"/"
 for(i=1;v=a[i];i++){ system( sprintf(m, v,v,v,v))}
 next
 }
 {print}'
)

ftt()(a="`sed -ne 's@^HCUT_ADD(\([^)]*\).*@\1, @p' $tf|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"<$tf>$Rm;mv $Rm $tf)
i0=$e'fgr0 "^#ifdef TEST" "^#endif"<$R0|fbn>$tf'
i1=$e'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*/
