/* Copyright (C) 2020 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_doc
@NAME	optbox - cmdline option parser consisting of a header file

@SYNOPSYS
<pre>
#include "optbox.h"

//macro
OPTBOX_SET(ptrname, mode
 , ( col1, col2, col3, col4, col5)	//setting(col1-4)+test(col5)
 , ( col1, col2, col3, col4)
 , .....
)	
//func
static int optbox_init(void* buff, int *argc_p, char** *argv_p);
static void* optbox_get(void* obj, const char* name);
static void* optbox_set(void* obj, const char* name, ...);	//va_arg
</pre>

@EXSAMPLE
<pre>
#include "optbox.h"

OPTBOX_SET( opts, "q"
 , ( help,  "-h", "0"     ,bool)	//basic
 , ( count, "-n", "1"     ,int	,return (0<=i && i<=3))	//with test
 , ( file,  "-f", "a.ini" ,str	, { if(strlen(s)>10){return 0;} } )
 , ( wait,  "-d", "0.3"   ,dbl	,if(0>d)	\
 	{ return 0;}; opts->wait += 123; return 1; )
)

int main(int argc, char** argv){
 int rc= optbox_init(opts, &argc, &argv);	//consume optargs
 if(rc){ printf("badopt: %c\n", rc);return rc;}
//
 printf("%d\n", opts->help);		// 0 or 1. bool >>> int.
 printf("%d\n", opts->count);		// 0<=num<=3 
 puts(opts->file);			// "a.ini" "cmdin_string"	etc
 puts(argv[1]);			//~$ ./a.out -h xy >>> ac=2, av[1]="xy"
 double d= *(double*)optbox_get(opts,"wait");	//type punning. d=123.03
 optbox_set(opts, "file", "hw");
 puts(opts->file);		// before:"a.ini" >>> after:"hw"
 return 0;
}
// ~$ gcc src.c 	#>> header file holds implements
// ~$ ./a.out -f abc.txt xyz
// ~$ ./a.out -n abc; echo $?	# emsg
</pre>

@DESCRIPTION
optbox saves cmdline options to static vars and doesnt use malloc().
options are parsed under the posix, getopts() rule. longopt is unsupported.

<pre>
- OPTBOX_SET(pname, mode, (cols), (cols),...): basic setting macro

	pname: ptrname to static buff created automatically like XX_pname 
	mode : flgchars for parse mode. dfl is "". allows "xq" chars
	 x: expand. dflmode works under the posix Utility Syntax Guidelines.
		'x' allows symbol opt, '-@' '-_' '-+' etc. '--' works as optend.
		'x' parses until '--' or args end.	eg) a.out abc -h >> hit -h
	
	 q: quiet. dflmode outputs emsg to stderr and exit if detect err in 
	    optbox_init(). 'q' mode rtns err charcode and skip emsg/exit.
		
	cols: option setting core. (col1, col2, col3, col4 [,col5])
	 col1: option name. no quote.
	 col2: cmdline short option char. needs prefix hyphen '-' and quote.
	 col3: dfl val. needs quote (or 'NULL' if datatype is str).
	 col4: datatype, 'bool(on-off)/int/dbl/str'. no quote. cmdin filter.
	 col5: optional. write raw testcode. no quote. expands as below.
 
		static int testfc_[col1](const char* s){
			if(!s){s="1";}
			int i=atoi(s); double d=atof(s);
			//--- expand [col5] --- 
			;return 1;
		}
		// ~$ ./a.out -p 32.1  >>>  s="32.1", i=32, d=32.1 

 ...you can use i/d/s vars. s is cmdin optarg. raise err if rtn 0.
 dfldata skips this test.

- int optbox_init(void* pname, int *ac_p, char** *av_p): execute parse

	pname: ptrname to buff. use the same of OPTBOX_SET() ag1
	ac_p : ptr to args count, &argc etc. optbox eats optargs and shift
	av_p : ptr to args array, &argv etc
	return: suc/fail == 0/not 0(mode q). rtn bad optchar, rc='h' etc

 you can get/set optvalues using pname. value type is defined in
 OPTBOX_SET(), bool/int/dbl/str. bool is treaded as int. optvalue
 holds 3 data, XXX, XXX_dfl, XXX_cmdin.

	pname->help	     : dfl/cmdin value. int/double/const char*
	pname->help_dfl	 : dfl setting *string* ptr
	pname->help_cmdin: cmdin *string* ptr. set NULL if no cmdin


- void* optbox_get(void* pname, const char* name): getter
 you can get optvalues from other srcfiles, out of filescope.

	pname: ptr address to buffer
	name : option name string
	return: ptr to optvalue. use type punning. exit(1) if err.

		val = *(int*)optbox_get(opts, "help")	//same as opts->help
		p = *(char**)optbox_get(opts, "help_dfl")	//opts->help_dfl
		p = *(char**)optbox_get(opts, "help_cmdin") //opts->help_cmdin


- void* optbox_set(void* pname, const char* name, ...): setter
 set optvalues from other srcfiles. 

	pname: see getter
	name : see getter
	va_arg:	new value. set the same typedata you used in OPTBOX_SET().
	return: see getter

		(width, "-w", "2", int) 
			>>>  obj->width=10 == optbox_set(obj,"width", 10)
		
		(sec, "-s", "0.1", dbl)
			>>> obj->sec=20 == optbox_set(obj,"sec",(double)20)
	
		(id, "-i", "alice",str)
			>>> obj->id="bob" == optbox_set(obj, "id", "bob" )
</pre>

@OPTIONS	-
@EXIT_STATUS	-
@RETURN_VALUE	-
@ERRORS almost of all err causes exit. optbox_init() + quiet mode
can avoid exit at invalid cmdline input, user input.
@NOTES
<pre>
- sloppy benchmark: gcc -O0
	real	65.208 ms	: msg:direct get: 10*1000*1000
	real	635.469 ms	: msg:getter()  : 10*1000*1000
	real	48.198 ms	: msg:direct set: 10*1000*1000
	real	814.323 ms	: msg:setter()  : 10*1000*1000

 FAST: direct get/set(1) >>> optbox_get(10) > optbox_set(15) :SLOW
	...getter/setter need cost to search optvalue ptr.

- call from other files
	//a.c
	#include "optbox.h"
	OPTBOX_SET(opt, "", (id, "-i", "alice", str)	)
	int main(int argc, char** argv){
		optbox_init(opt, &argc, &argv);
		myf(opt);
		return 0;
	}
	
	//b.c
	#include "optbox.h"
	void myf(void* obj){
		char* p = *(char**)optbox_get(obj, "id");
		puts(p);
		optbox_set(obj,"id", "bob");
		p = *(char**)optbox_get(obj, "id");
		puts(p);
	}
	// ~$ gcc a.c b.c
	// ~$ ./a.out -i chris


- optbox is created with the following concept
	- easy to set standard items such as '-h' and '-n 10'
	- support boring test such as int/str and range check
	- easy to access parse result
	- collect the setting information to reduce working line of srcedit
	- avoid to force the complex apis, complex orignal macros
</pre>
@CONFORMING_TO POSIX.1-2001+
@BUGS \-
@COPYRIGHT
<pre>
Copyright 2020 momi-g, GPLv3+

uses cc-by-sa 2.5/3.0 code:
https://stackoverflow.com/questions/2632300
https://stackoverflow.com/questions/44479282
</pre>
@VERSION 2021-06-21 v2.0.0
@SEE_ALSO	-
<pre>
</pre>
//SH_docE*/
//no code, header only

/*SH_SMP
#include "optbox.h"
OPTBOX_SET( obox, "x"
 ,( help,  "-h", "0"     ,bool )
 ,( count, "-n", "1"     ,int	,return (0<=i && i<=3) )
 ,( file,  "-f", "a.ini" ,str	,if(strlen(s)>10){return 0;} )
 ,( wait,  "-d", "0.3"   ,dbl	,if(0<d){return 1;}; return 0; )
)

int main(int ac, char** av) {
	int rc = optbox(obox, &ac, &av);
	void* p = optbox_get()
	printf("%f\n", obj->wait);
	puts(obj->file);
}
//SH_SMPE*/





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

	* optbox.c (optbox_initsub): add av[0] to lb_OPTERR msg

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

	* optbox.c (optbox_init): fix bool set s="" >> s="1"
	(OPTBOX_SET): same

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

	* optbox.c (optbox_all): rewrite all. v2.0.0

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

	* optbox.c (optbox_sub): fix testfunc err, exit(1) >> goto lb_TESTFUNC.

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

	* optbox.c (optbox_sub): debug sticky optarg '-n2' parsing, charpos++.

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

	* optbox.c (all): add getter/setter for access optdata from outer srcfile

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

	* optbox.c (all): v1.0.0 release

*/
