#include <emsg: see ... '~$ sh aaa.sh.c -h'   (other opt:no/-m/-w/)>	/*
C='^[/][/*]SH_'		;O=${0##*[/]};R=`dirname $0`/;R0=$R$O;Re=eval\ ;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:NGext"&&exit 1;R='sed -ne ';Cm=$R'"/[E]ND/!d;:l
n;p;b l"<$R0>$Rm;$Rp"$Rm"';Rw=$R'"/$C$R/!d;:l;n;/${C}ED/q;p;b l"<$R0';Cw="(R=LS
$Rw;$Rw>&3;R=HD;$Rw;R=SC;$Rw>&3)"'>$Rh 3>$Rs;$Rp"$Rh $Rs"';RB=$($R"s/${C}OP//p"\
<$R0|(F=mw;while read -r a b;do B=${a%:};F=`$Rp"$F"|$R"s#$B:*##;p"`${a%_};$Rp"
C$B=\$(cat<<'E'$O$b${O}E$O)";done;$Rp"R1=$F"));$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  GPLv3+*/

/*SH_LS*/

//AGPLv3+
/* Copyright 2023 Momi-g
   Copyright 2005 The Geany contributors

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

/*SH_doc
title=*SH_bn* section=3 repnl=\040

@name *SH_bn*
@_brief geany plugins, jump to B/EOF, 20clm left/right, jump dialog ext
@_syno -
@tl_dr
		@(code)@
	//build shared lib
	~$ cc *SH_bn*.c -o *SH_bn*.so -shared -fPIC $(pkg-config --cflags geany)
	~$ cp *SH_bn*.so ~/.config/geany/plugins/   #--mkdir if plugins/ isnt
	
	//plugin_load: run geany >> menubar >> tool >> plugins and set keybind
	//add if you want: ~/.config/geany/geany.conf, [plugins] bejmp_width=10
		@()@
@_desc
	-- BOF/EOF jump--
	geany lacks the 'jump to BOF/EOF' keybind settings, and 'ctrl+Home/End'
	uses Scintilla api directly. this plugin add the settings. --
	--
	-- fixed width move to left/right--
	move caret left/right by 20 columns.
	edit ~/.config/geany/geany.conf(or other place) to change width as below: --
	@(code)--@
	#geany.conf
	...
	[plugins]
	...
	*SH_bn*_width=25
	#add this line (change to 25 column)
	@()--@
	--
	-- linejump dialog extension--
	dfl linejump dialog cant move to EOF. '-1' means relative val, (current)-1.
	this plugin extends the syntax which supports EOF/EOL as follows:
		@(code)--@
	(same as dfl)
	12  : jump to abs line 12
	+12 : jump to relative line +12 (== 10+12==22, if caret is line 10)
	-12 : jump to relative line -12 (== 10-12== -2 >> 1, BOF)
	+0  : no change, current line
	-0  : no change
	
	(ext syntax, '0' prefix means END)
	0   : jump to EOF. prefix '0' means EOF
	0-3 : jump to EOF-3 == 97 (if EOF line is 100)
	0+3 : jump to EOF-3 == 97 (not typo. prefix '0' ignores next sign '-+')
	03  : jump to EOF-3 == 97 (accept no sign)
	
	12,30 : jump to abs line 12, column 30.
	12  30: same. grep '2nd number like' token as column
	,30   : l/c == current/30, blank val means current
	12,   : l/c == 12/current
	,+30  : l/c == - /current+30
	,-30  : l/c == - /current-30
	12,0  : l/c == 12 / EOL, column '0' prefix means END==EOL
	12,0-3: l/c == 12 / EOL-3
	12,0+3: l/c == 12 / EOL-3 ('+' >> '-', not typo. abs sub)
	12,03 : l/c == 12 / EOL-3
		@()--@
	geany BOF posinfo l/c is L1/C0. ext jmp dialog takes '1,1' to move BOF.
@_eg -
@return_value -
@notes	-
@conforming_to POSIX.1-2001+
@depend pkg-config, geanyplugin.h
@copyright
	Copyright 2023 momi-g, AGPLv3+ --
	Copyright 2005 The Geany contributors
@_ver 2023-01-11 v1.0.0 (2023-01-11 v1.0.0)
@_see
	https://github.com/geany/geany/issues/2994 --
	https://raw.githubusercontent.com/geany/geany/master/HACKING --
	https://www.geany.org/manual/reference/ --
	Geany 1.38 srccode (almost all is copied from here)
//SH_docE*/
/*SH_ED*/

/*SH_HD*/
#ifndef *SH_bn*_3841399a47a2
#define *SH_bn*_3841399a47a2

#include <stdio.h>
#include <string.h>
#if ( _POSIX_C_SOURCE +0 < 200112L )
	#include "stop cc: needs compiler posix-2001 or upper(c99+)"
#endif

#endif /* *SH_bn*_3841399a47a2 */
/*SH_ED*/

/*SH_SC*/
/* tool macros */
#ifndef ERRact
#include <stdio.h>
 #if (199901L <= __STDC_VERSION__ +0)	/* nealy 200112L, _POSIX_C_SOURCE	c99*/
	#include <sys/types.h>
	#include <unistd.h>
	#define ERRactag	__func__, getpid()
 #else
	#define ERRactag	"func:c99+", 0
 #endif
 #include <string.h>
 #include <errno.h>
 #define ERRact(xpr, msg, act)	if(xpr){ int en_=errno; fprintf(stderr, \
	"ERR: %s %d %s() pid:%d %s msg:%s sys:%s\n",__FILE__,__LINE__, ERRactag \
	, "hit(" #xpr ")", msg, strerror(en_) ); act; }
 #define STOP(xpr, msg)	ERRact(xpr, msg, fputs("STOP\n",stderr);exit(1) )
#endif

#define loop(a)		for(int lpcnt=1;lpcnt<=(a);lpcnt++)
/*tool end*/

#ifdef TEST
# include <assert.h>
# include "*SH_bn*.h"
# include "hcut.h"
# include "msgp.h"
# include "laptime.h"
# define qu(...)		Qsub(__VA_ARGS__)
# define Qsub(...)	#__VA_ARGS__
#endif

#include <geany/geanyplugin.h>	/*SH_co* `pkg-config --cflags geany --libs geany` */
#include "*SH_bn*.h"

//dbgtool
#define dpl(...) dpl_impl( __LINE__, "sh -c \"notify-send -t 5000 'L%d: "  __VA_ARGS__ )
#define dpl_impl(n, fmt, ...) dpl_impl_(fmt "'\"\n", n, __VA_ARGS__+0)
#define dpl_impl_(...) {char sbuf[100]={0};snprintf((void*)sbuf, 99, __VA_ARGS__); system(sbuf); }

//plugin connector
static gboolean mod_load(GeanyPlugin* pobj, gpointer udata);
static void mod_unload(GeanyPlugin* pobj, gpointer udata);
static void mod_help(GeanyPlugin* pobj, gpointer udata);
//static void mod_cfg(GeanyPlugin* pobj, GtkDialog* wobj, gpointer udata);
static gboolean f_bejmp(GeanyKeyBinding* key, guint keyid, gpointer user_data);

enum {
	W_
	, S_
	, A_
	, D_
	, G_
	, SUM_
};

static int f_wnum(void* dirname);
static gint wnum=1;
static GeanyPlugin* pobj_;

void geany_load_module(GeanyPlugin* pobj) {
	pobj->info->name = "*SH_bn*";
	pobj->info->description = "add keybind 'move to B/EOF, left/right by 20clm' ";
	pobj->info->version = "*SH_ver*"+1;
	pobj->info->author = "momi-g <dmy@dmy.dmy>";

	pobj->funcs->init = mod_load;	
	pobj->funcs->cleanup = mod_unload;
	
	pobj->funcs->configure = NULL;	//mod_cfg;
	pobj->funcs->help = mod_help;	
	pobj->funcs->callbacks = NULL;
	
	//--read geany.conf
	wnum = f_wnum(pobj->geany_data->app->configdir);
	pobj_=pobj;

	GEANY_PLUGIN_REGISTER(pobj, 226);
	return;
}

static const char* hstr_ =
/*SH_lit*/
/*SH_rf* 0 *SH_bn*.3.txt */
/*SH_litE*/
;

//copy: geany-master/src/libmain.c, get_windows_socket_port()
//--load val from .config/geany/geany.conf
static int f_wnum(void* d){
	gchar* p = g_build_filename(d, "geany.conf", NULL);	//malloc()ed pathstr
	GKeyFile* fp = g_key_file_new();	//gtk iofile
	g_key_file_load_from_file(fp, p, G_KEY_FILE_NONE, NULL);	//assign path>>fp
	
	wnum = 20;	//dfl_width
	wnum = utils_get_setting_integer(fp, "plugins", "*SH_bn*_width", wnum);
	// ag3:dfl, ret: newval if available, dfl if not found

	//--clear
	g_free(p);
	g_key_file_free(fp);
	return wnum;
}

static void mod_help(GeanyPlugin* pobj, gpointer udata){
    GtkWidget *tx, *w, *scr_v;
    GtkTextBuffer* tbuf;

    w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size(GTK_WINDOW(w), 600, 400);
    
    scr_v = gtk_scrolled_window_new(NULL, NULL); 
    tx = gtk_text_view_new();
    tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(tx));

	//--helptext
    gtk_text_buffer_set_text(tbuf, hstr_, -1);

    gtk_container_add(GTK_CONTAINER(scr_v), tx);
    gtk_container_add(GTK_CONTAINER(w), scr_v);
    gtk_widget_show_all(w);

//	dialogs_show_msgbox(GTK_MESSAGE_INFO, hmsg);	//dbgtest
}

static gboolean mod_load(GeanyPlugin* pobj, gpointer udata){
	GeanyKeyGroup *key_group;
	key_group = plugin_set_key_group(pobj, "*SH_bn*", SUM_, NULL);	/*obj, grpname, setsize, othercb */
	
	int id = W_;
	udata=(void*)W_;
	void* cs = (void*)"*SH_bn*_w";
	void* s = (void*)"(*SH_bn*)jump to BOF";
	keybindings_set_item_full(
		key_group		/* GeanyKeyGroup* */
		, id /* groupid, 0,1,2... above ag3 idx */
		, 0	/* dfl-key GDK_KEY_j etc.  but set 0 to avoid user keybind conflict */
		, 0	/* dfl-modkey GDK_CONTROL_MASK */
		, cs	/* namestr: APIname to use in config.ini "str" == myf() bindings */
		, s	/* label: desc in keybind cfg window */
		, NULL	/* additionally, GtkWidget* (btn etc) exec with this kb. set NULL if nouse */
		, f_bejmp	/* gboolean f(GeanyKeyBinding *key, guint keyid, gpointer user_data) */
		, udata	/* void* udata you want to pass the cb */
		, NULL	/* destructor, free() etc */ 
	);

	id = S_;
	udata=(void*)S_;
	cs = (void*)"*SH_bn*_s";
	s = (void*)"(*SH_bn*)jump to EOF";
	keybindings_set_item_full(key_group, id, 0, 0, cs, s, NULL, f_bejmp, udata, NULL);

	id = A_;
	udata=(void*)A_;
	cs = (void*)"*SH_bn*_a";
	s = (void*)"(*SH_bn*)caret left by XX clm";
	keybindings_set_item_full(key_group, id, 0, 0, cs, s, NULL, f_bejmp, udata, NULL);

	id = D_;
	udata=(void*)D_;
	cs = (void*)"*SH_bn*_d";
	s = (void*)"(*SH_bn*)caret right by XX clm";
	keybindings_set_item_full(key_group, id, 0, 0, cs, s, NULL, f_bejmp, udata, NULL);

	id = G_;
	udata=(void*)G_;
	cs = (void*)"*SH_bn*_g";
	s = (void*)"(*SH_bn*)gobox '5,10' == line/clm";
	keybindings_set_item_full(key_group, id, 0, 0, cs, s, NULL, f_bejmp, udata, NULL);

	return TRUE;
}
static void mod_unload(GeanyPlugin* obj, gpointer udata){ ; }


static int f_bejmp_impl(int udata);
static gboolean f_bejmp(GeanyKeyBinding* kobj, guint keyid, gpointer udata){
// dpl("%s\n", "123");
	int rc = f_bejmp_impl((int)udata);
	if(rc){return FALSE;}
// true/false == eat/fall-through keybind grab
// https://www.geany.org/manual/reference/keybindings_8h.html
	return TRUE;
}
//--plugin connector end


typedef struct pos_tag {
	int lnum, lnum_e, cpos, spos, epos, ccol, scol, ecol	;
} pos_t;

static pos_t cmnpos_[1];
static char jmptxt_[32];

static int f_cmnpos(int l){
	// input 'l' is dispinfo. l>=1.
	
	GeanyDocument *doc = document_get_current();
	ScintillaObject *sci = doc->editor->sci;
	
	//--inner line 0 == dispinfo line 1. fix.
	int lnum = sci_get_current_line(sci)+1;
	if(l>=0){lnum=l;}
	lnum--;	//--lnum is for inner line val
	
	int lnum_e = sci_get_line_count(sci);
	int cpos = sci_get_current_position(sci);	//pos == byte? charactor?
	int spos = sci_get_position_from_line(sci, lnum);
	int epos = sci_get_line_end_position(sci, lnum);

	int ccol = sci_get_col_from_position(sci, cpos);
	int scol = sci_get_col_from_position(sci, spos);
	int ecol = sci_get_col_from_position(sci, epos);

//dpl("%d %d %d, %d %d %d", cpos, spos, epos, ccol, scol, ecol);

	cmnpos_->lnum = lnum+1;		//--adjust to dispinfo lineval
	cmnpos_->lnum_e = lnum_e;	//--linecnt is +1 in dfl.
	cmnpos_->cpos = cpos;
	cmnpos_->spos = spos;
	cmnpos_->epos = epos;

	cmnpos_->ccol = ccol+1;	//base col0 >> head, bejmp col1 >> head
	cmnpos_->scol = scol+1;
	cmnpos_->ecol = ecol+1;
	return 0;
}

//ref: editor.c, sciwrappers.c/h
static int f_move(int lnum, int ncol){
	GeanyDocument *doc = document_get_current();
	ScintillaObject *sci = doc->editor->sci;

//dpl("%d %d\n", lnum, ncol);
	f_cmnpos(-1);
	if(lnum < 1){ lnum = 1; }
	if(cmnpos_->lnum_e < lnum ){ lnum = cmnpos_->lnum_e; }
	
	f_cmnpos(lnum);
	if(ncol < 1){ ncol = 1; }
	if(cmnpos_->ecol < ncol){ ncol = cmnpos_->ecol; }
//dpl("%d %d\n", lnum, ncol);
	lnum--;
	ncol--;

	int npos = scintilla_send_message(sci, SCI_FINDCOLUMN, lnum, ncol);
//dpl("%d %d %d\n", lnum, ncol, npos);
	
	sci_set_current_position(sci, npos, TRUE);		//-- ag3 set scrview to caret or not
	sci_scroll_caret(sci);
	return 0;
}

//--impl_code 
static int f_lrmove(ScintillaObject *sci, int rcol){
	f_cmnpos(-1);
	
	int lnum = cmnpos_->lnum;

	int ccol = cmnpos_->ccol;
	int ecol = cmnpos_->ecol;
	int ncol = ccol+rcol;
	
	f_move(lnum, ncol);
	return 0;
}

#include <sys/types.h>
#include <regex.h>
static void* w_;
static void f_cb_go(GtkWidget *obj, gpointer e){
	const gchar* p = gtk_entry_get_text(GTK_ENTRY(e) );
	snprintf(jmptxt_, 32, "%s", p);	//save

	//grep valstr
	regex_t re[1];
	regcomp(re, "[^0-9,+-]*([0]?[-+]?[0-9]+)?[ \t]*([^0-9+-]*)([0]?[-+]?[0-9]+)?", REG_EXTENDED);
	regmatch_t rbuf[4];
	int rc;
	int ldx, lsz;
	int cdx, csz;

	//grep
	rc = regexec(re, p, 4, rbuf, 0);	//flg, REG_NOTBOL/EOL	
//dpl("%s, %s, %d", lbuf, cbuf, rc); return;
	if(rc!=0){ dpl("fatal_err: regexe() failed"); goto lb_END; }

	ldx = rbuf[1].rm_so;
	lsz = rbuf[1].rm_eo - ldx;
	char lbuf[16]={0};
	snprintf(lbuf, 16, "%.*s", lsz, p+ldx);

	cdx = rbuf[3].rm_so;
	csz = rbuf[3].rm_eo - cdx;
	char cbuf[16]={0};
	snprintf(cbuf, 16, "%.*s", csz, p+cdx);
	if(lsz==0 && csz==0){ goto lb_END; }
	if(csz==0 && rbuf[2].rm_eo==rbuf[2].rm_so){ csz=-1; }

	//--parse
	int lnum, ncol;
	f_cmnpos(-1);
	
	p = lbuf;
	lnum = cmnpos_->lnum;	// blank ', 10'は自動的にcurline
	if(*p=='\0'){;}
	else if(p[0]=='0'){
		//--tailmode
		lnum = cmnpos_->lnum_e;
		p++;
		if( strpbrk(p, "-+") == p ){ p++; }	//ignore
		if( *p != '\0'){ lnum -= (int)strtol(p, NULL, 10); }
	}
	else{
		if( strpbrk(p, "-+") != p ){ lnum = 0; }
		if( *p != '\0'){ lnum += (int)strtol(p, NULL, 10); }
	}

	p = cbuf;
	ncol = cmnpos_->ccol;
	if(*p=='\0'){;}
	else if(p[0]=='0'){
		//--tailmode
		f_cmnpos(lnum);
		ncol = cmnpos_->ecol;
		p++;
		if( strpbrk(p, "-+") == p ){ p++; }	//ignore
		if( *p != '\0' ){ ncol -= (int)strtol(p, NULL, 10); }
	}
	else{
		if( strpbrk(p, "-+") != p ){ ncol = 0; }
		if( *p != '\0' ){ ncol += (int)strtol(p, NULL, 10); }
	}
	if(csz<0){ ncol=0; }
	f_move(lnum, ncol);

lb_END:;
	gtk_window_close(w_);
	return;
}

static gboolean f_cb_close(GtkWidget* obj, GdkEventKey *ev, gpointer udata){
	if (ev->keyval != GDK_KEY_Escape) { return FALSE;}
	gtk_window_close(GTK_WINDOW(obj) );
	return TRUE;
}

// toolbar.c / keybindings.c / callbacks.c 
// word: GotoEntry, GEANY_KEY_GROUP_GOTO, on_toolbutton_goto_entry_activate
// fn: on_toolbutton_goto_clicked(), on_go_to_line_activate(), on_toolbutton_goto_entry_activate()
static int f_gobox(){
	GtkWidget *w, *vbox, *hbox, *e, *b, *l;
	GtkTextBuffer* tbuf;
	
	w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	g_signal_connect(w, "key_press_event", G_CALLBACK(f_cb_close), NULL);
	w_=w;
	
//	gtk_window_set_default_size(GTK_WINDOW(w), 300, 50);
	gtk_window_set_title(GTK_WINDOW(w), "*SH_bn*_goto");
	gtk_container_set_border_width(GTK_CONTAINER(w), 5);
	
	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);	//5: flame width
	gtk_container_add(GTK_CONTAINER(w), vbox);

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
	gtk_container_add(GTK_CONTAINER(vbox), hbox);
	
	e = gtk_entry_new();
	g_signal_connect(G_OBJECT(e), "activate", G_CALLBACK(f_cb_go), e);
	gtk_box_pack_start(GTK_BOX(hbox), e, TRUE, TRUE, 0);	
	gtk_entry_set_text(GTK_ENTRY(e), jmptxt_);

	b = gtk_button_new_with_label("go");
	gtk_box_pack_start(GTK_BOX(hbox), b, TRUE, TRUE, 0);
	g_signal_connect(G_OBJECT(b), "clicked", G_CALLBACK(f_cb_go), e);

	//--info
 #define X_(s) hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); \
	gtk_container_add(GTK_CONTAINER(vbox), hbox); \
	l = gtk_label_new(s); \
	gtk_box_pack_start(GTK_BOX(hbox), l, FALSE, FALSE, 0);

	X_("12,5: L12,C5");
	X_("0,-4: EOF,current-4");
	X_(",04 : current,EOL-4");
 #undef X_

	gtk_widget_show_all(w);
	return 0;
}


//-- copied: src/keybindings.c, cb_func_goto_action(), check_snippet_completion()
static int f_bejmp_impl(int reqid){

	GeanyDocument *doc = document_get_current();
	//GtkWidget *focusw = gtk_window_get_focus(GTK_WINDOW(main_widgets.window));	// main_widgets==global, ui_utils.h 
	GtkWidget *focusw = gtk_window_get_focus(GTK_WINDOW(pobj_->geany_data->main_widgets->window));	// main_widgets==global, ui_utils.h 
	ScintillaObject *sci = doc->editor->sci;
	if (doc == NULL || focusw != GTK_WIDGET(sci) ){ return 1; }	//skip if sci_widget has no nofocus
	
	switch (reqid)
	{
		case W_:
			//sci_send_command(sci, editor_prefs.smart_home_key ? SCI_VCHOMEDISPLAY : SCI_HOMEDISPLAY);
			//sci_send_command(sci, pobj_->geany_data->editor_prefs->smart_home_key ? SCI_VCHOMEDISPLAY : SCI_HOMEDISPLAY);
			sci_send_command(sci, SCI_DOCUMENTSTART);
			break;
		case S_:
			sci_send_command(sci, SCI_DOCUMENTEND);
			break;
		case A_:
			f_lrmove(sci, -wnum);
			break;
		case D_:
			f_lrmove(sci, wnum);
			break;
		case G_:
			f_gobox();
			break;
	}
	return FALSE;	// grab key
//	return TRUE;	// fall key
}
//*/
#ifdef TEST
HCUT_ADD(t_0) {
	eq_i(1, 1, "msg");
}
#endif

/*SH_SMP
#include "*SH_bn*.h"	//SH_co* -static *
#include <stdio.h>

int main(int argc, char** argv) {
	puts("hw");
	return 0;
}
//~$ gcc src.c
//SH_SMPE*/

#ifdef TEST
HCUT_RUN("stderr", 1,
		 t_0)
#endif

/*
 change log
 --
2023-01-11	Momi-g	<dmy@dmy.dmy>

	* *SH_bn*.sh.c: init

*/

/*SH_ED*/

/*SH_OP _ set -e;a=`sed -ne "/${C}DF/!d;:l;n;/${C}DE/q;p;bl"<$R0`;eval "$a";set +e	#*/
/*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-zA-Z].*)/p"<$R0 #*/
/*SH_OP t $e"$CW";ftt "$@";$p'cc -O0 $dm -Wall -pedantic -Wfatal-errors -g -pg -ggdb3 $Rm `fOI $Rs $tf` `fg $Rh $Rs $tf` `fL`'|fv	#*/
/*SH_OP T $e"$CW";ftt "$@";$p'cc -O3 $dm $Rm `fOI $Rs $tf ` `fg $Rs $tf ` `fL`'|fv	#*/
/*SH_OP s fgr0 "${C}SMP" "${C}SMPE"<$R0|fbn>eg.c;$p'cc eg.c `fg eg.c` `fOI eg.c`'|fv #*/

/*SH_OP L $p"fml $@"|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 -fPIC -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"$CW";$p"cc -shared -fPIC -o lib$bn.so $bn.c `fOI $Rs` `fg $Rs`"|fv;$p"lib$bn.so" #*/
/*SH_OP W fpp;$e"$Cm$O$Cw">/dev/null;fborn;$p"$Rs $Rh $tf"	#*/

/*SH_OP o $e"$CW";$p'fman $Rh 3'|fv		#*/

/*SH_OP y cp $Rs $bn.cbrt;$p"cbrt $bn.cbrt --c $Rs --h cbft.h"|fv	#*/
/*SH_OP Y $e"$Cm$O$Cw">/dev/null;$e"$Cy";fborn;$p"$Rs $Rh $tf"		#*/

/*SH_DF
#-- noob
fman()( $p"cat $1|sed -e '/${C}docE/q'|fgr0 '${C}doc' '${C}docE'|amn >$bn.$2
 mandoc -Thtml <$bn.$2 >$bn.$2.html
 man -Tutf8 /dev/stdin<$bn.$2|sed -e 's/.`printf \"\\b\"`//g'>$bn.$2.txt
 "|fv
)

#-- 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|frv|flit)
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()($e\$p`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/[.][^.]*$/.c/'|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/[.][^.]*$/.c/'|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
fy(){
set -e
cat<<'EEE'|fv
fgr0 "${C}YACC" "${C}YACCE"<$Rs|fbn>myyacc.y
fgr0 "${C}LEX" "${C}LEXE"<$Rs|fbn>mylex.l
fgR "${C}YACC" "${C}YACCE"<$Rs>$Rm
fgR "${C}LEX" "${C}LEXE"<$Rm>$Rs

lex mylex.l
#bison -Wnone -Wall -Wno-empty-rule -p zz -dv myyacc.y 
# bisonはy.tab.c固定名ではなくsrc.tab.c/hを吐く 標準に戻す
bison -Wnone -Wall -Wno-empty-rule -dv myyacc.y --defines=y.tab.h -o y.tab.c

cat $Rh y.tab.c lex.yy.c > $Rs
$p"$Rs"
EEE
}

#-- longcmd
frf()(
 # *sh_rf* 0 a.txt b.txt ...でcat纏めて出力 top0でsrcinfoは無し出力
 awk -v tg="${C##*]}rf" 'index($0,tg){
 s=substr($0, index($0,tg)+length(tg)+1);split(s, a)
 m="[ -f \"%s\" ]&&(echo \"/*--copyfrom %s*\"/;cat \"%s\";echo \"/*--copyend %s*\"/)"
 mm="[ -f \"%s\" ]&&(_=\"%s\"/;cat \"%s\";_=\"%s\")"
 for(i=1;i in a;i++){v=a[i];if(v==0){m=mm;continue};system(sprintf(m,v,v,v,v)) }
 next
 }
 {print}'
)
frv()(buf=`awk '$1=="@_ver" {print $3;exit}'<$R0`;sed -e "s@\*${C##*]}ver\*@$buf@g")
flit()(sed -ne "/${C##*]}lit/b l;p;d;:l;n;/${C##*]}litE/d;"'s/[\]/&&/g;s/"/\\"/g;s@.*@"&\\n"@g;p;b l')

fml()(
 # --show-leak-kinds=definite,indirect(inner ref),possible,reachable(doesnt p=NULL)
 #cmd="valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose ./a.out 2>&1|sed -e '/SUMMA/!d;n;n;n;n'"
 cmd="valgrind --leak-check=full --show-leak-kinds=definite,possible --track-origins=no --verbose ./a.out 2>&1|sed -e '/SUMMA/!d;n;n;n;n'"
 test $# = 0 && $p"$cmd"|fv && return
 set -- " " "$@"
 $e"$Ct"
 test $? = 0 && $p"$cmd"|fv
)

ftm(){
 awk -v w="$1" '
 /^HCUT_ADD/ && index($0,"("w")")==0 {
	while(match($0,"^#endif$") ==0 ){getline}
	print $0; next
 }
 {print $0}'
}

fte()(
 cat > $Rm-
 cmd="cat"
 [ $# != 0 ] && cmd='ftm $b'
 a="`sed -ne 's@^HCUT_ADD(\([^)]*\).*@\1, @p' $Rm-|tr -d '\n'`NULL"
 if [ $# != 0 ];then	a=""; for i;do a="$a $i,";done; a="$a NULL"; fi
 b=`$p"$a"|tr ',' ' '`
 sed -ne "p;/_RUN/b l;d;:l;/[)]/{c \\$O $a)$O p;d};n;b l" $Rm-|$e"$cmd"|
 fgr0 "^#ifdef TEST" '^#endif$'
 rm $Rm-
)

ftt(){
 [ $# != 0 ] && [ "${1#[0123]}" = "" ]&&dm="-DVLV=$1"&&shift
 (cat $Rs;fte "$@"<$tf)>$Rm
}
fborn(){
 fgr "^#ifdef TEST" "^#endif$"<$R0|fbn>$tf
 fgR "^#ifdef TEST_" "^#endif$"<$tf |fte>tests.code
 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)

fpp(){ awk "{gsub(/[*]${C##*]}ln[*]/,NR);print}"<$R0>${R0}_; R0="$R0"_; $p"${R0}"; }

/*SH_DE*/
