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

/*-*
@_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
