/* 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/>.
*/
#include "msgp.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];


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


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


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

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

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

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

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

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

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

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

*/
