first version.
[build-srcpkg.git] / bin / shlibinc
blobf57b1591739cae2580f97c178d63a14e40aac6a9
1 #!/bin/bash
2 ############################################################
3 # source: shlibinc
4 # author: CottonCandyOwner(CottonCandyOwner@126.com)
5 # date: 2021-10-03
6 ############################################################
7 # Copyright (C) 2022- Free Software Foundation, Inc.
8 # This configure script is free software; the Free Software
9 # Foundation gives unlimited permission to copy, distribute
10 # and modify it.
11 ############################################################
12 # note:
13 # this file define a cmd called 'include' which is used
14 # to include a shlib file.
15 # it define a shlib version variable SHLIB_VERSION, and
16 # ROOTFS to store root path in paticulor enviroument.
17 # sometimes, user need file include feature like shlib,
18 # so it define a cmd called 'define_include' to provide this
19 # feature.
20 # when i use 'source' in function, it can not load global
21 # variables in sourced file, so, this cmd is defined as an
22 # alias.
23 # when shlib code need to be updated in memory, reloadshlib()
24 # is used to do this work.
25 # shlib is included unique, if the shlib loaded in system/bash
26 # init script, and the global variable should be re-init in
27 # every program. so, cmd 'uniqlib' and 'endlib' is defined,
28 # try to load part of lib unique, and others executed every
29 # time. this feature have not been implemented.
30 ############################################################
33 # todolist:
34 # @ trim code & cmnt, put cmnt into design doc.
35 # @ combine include alias with define_include string.
37 # @ debug info output setting by global variable.
38 # @ testing for load in different process.
40 # @ diff list by listvar_diff(), trim code.
43 # features:
44 # @ include/load/unload/reload
45 # include/load, similar with it in C. include file in
46 # $PWD, or global include path.
47 # # reloading/re-including for same file in different path string.
48 # @ PROG_GVAR_INIT/PROG_INIT/uniqlib
49 # # uniq-include in nestly including.
50 # # compactly load in sub-script-proc.
51 # @ SHLIB_INC_LIST/SHLIB_LIST,
52 # nest_level/nest_level_shlib/shlib_load_state,
53 # inc_flag/last_inc_flag,
54 # for nestly including/loading.
55 # @
56 # @ if lib in different lib path with same name, they might be mixed.
57 # @ include ./scripts/uniq.shlib
58 # include uniq.shlib
59 # this will record different lib flag in SHLIB_INC_LIST.
60 # maybe use full-path-name will resolve this problem.
63 # @ [shlibinc] binary shlib check and load.
64 # for shlibinc, add a func/alias to check and load binary shlib.
65 # at the beginning of shlib src file, add a binary lib checking cmd. if the
66 # shlib implemented by a binary lib, load it first, and do not load script code
67 # below.
69 # interface:
71 # @ SHLIB_VERSION, shlib version infomation.
72 # @ include <shlib-file>, used to include a shlib in the process of current program.
73 # it will be uniq included in a process. once include, does not need to
74 # be 'source' again. but it still need to be included in a new sub-script
75 # program.
76 # @ load/unload <shlib-file>, used to load a shlib in system, or current session,
77 # or current process. the difference with include is that, it recode the
78 # global var & func & alias cmd in exported env var, and set them with
79 # export attribute. when the shlib would be load in a new script program,
80 # it still keep load content.
81 # @ reload <shlib-file>, used to load a shlib again if shlib is updated.
82 # @ PROG_GVAR_INIT <libname>, global variable init in shlib, when the lib file
83 # has been loaded.
84 # @ GVAR_INIT <libname>, global variable first init in shlib.
85 # @ uniqlib <libname>, skip loading the code after this cmd, if the shlib has
86 # been loaded.
87 # @ functions:
88 # # get_shlib_path, print shlib one path string by one line.
89 # # get_shlib_path_var <var-name>, get the shlib path in var array.
90 # # set_shlib_path <PATH>, set whole shlib path string.
91 # # add_shlib_path <PATH>, append path to shlib path.
92 # # del_shlib_path <PATH>, delete path by specified string.
93 # # define_include <> <> <> <>, define a include cmd for a kind of lib file.
97 # enable alias feature in script.
98 shopt -s expand_aliases
100 # init ROOTFS if it is not /
101 if [[ ! -d $ROOTFS ]]; then
102 TMP_DIR=`dirname $(which ls)`/..
103 TMP_DIR=`cd $TMP_DIR; pwd; cd - > /dev/null`
104 ROOTFS=${ROOTFS:-$TMP_DIR}
105 unset TMP_DIR
109 # save current process id every time when this file is sourced in a program.
111 PROG_ID=$$
114 # shlib module name stack.
116 if [[ ! -v SHLIB_IDX || -z $SHLIB_IDX ]]; then
117 declare -g SHLIB_IDX=0
118 declare -g SHLIB_MODULE=${BASH_SOURCE[1]##*\/}
119 SHLIB_MODULE=${SHLIB_MODULE^^}
123 ##############################
124 # section: public comment info
125 ##############################
128 #echo SHLIB_VERSION=$SHLIB_VERSION
130 ##############################
131 # section: variable define
132 ##############################
135 # todo: comment this code, exported function cannot be used in sub-process.
138 # unique executing judged by shlib version variable.
139 #if [[ -n $SHLIB_VERSION ]]; then
141 # invoke global var init if this script file is invoked in a shlib
143 # [[ -n $1 ]] && PROG_GVAR_INIT $1
145 # echo shlibinc has been loaded, skip to save cpu cost.
148 # shlibinc only could be used by source
149 # use return to exit from current script file.
151 # return
156 # todo:
157 # if defined as exported var, it can be used for feature of one time code loading.
158 # but this feature can not works.
160 #declare -g -x
161 SHLIB_VERSION="0.0.1"
165 # it stored shlib paths like enviroument variable 'PATH'.
167 SHLIB_PATH=${SHLIB_PATH:-`echo $ROOTFS/usr/shlib:$ROOTFS/usr/libexec/dvar/shlib`}
169 path_list=
171 # an array of SHLIB_PATH path.
172 if [[ -z $path_list ]]; then
173 OLD_IFS=$IFS
174 IFS=":"
175 path_list=( $SHLIB_PATH )
176 IFS=$OLD_IFS
180 # initialization of vch_progstdin should be putted here instead of stdio.shlib.
182 shlibinc__get_file_top_valid_id ()
184 local i
186 i=$(ulimit -n)
187 for (( i=$((i-1)); i>0; i-- )); do
188 [[ ! -e /proc/self/fd/$i ]] && echo $i && return
189 done
192 shlibinc_onetime_init ()
194 [[ "$shlibinc_onetime_init_flag" == 1 ]] && return
195 shlibinc_onetime_init_flag=1
197 # init null channel
198 [[ -z "$vch_null" ]] &&
199 vch_null=$(shlibinc__get_file_top_valid_id) &&
200 eval "exec $vch_null<>/dev/null"
202 [[ -z "$vch_progstdin" ]] &&
203 vch_progstdin=$(shlibinc__get_file_top_valid_id) &&
204 eval "exec $vch_progstdin<&0"
205 [[ -z "$vch_progstdout" ]] &&
206 vch_progstdout=$(shlibinc__get_file_top_valid_id) &&
207 eval "exec $vch_progstdout>&1"
208 [[ -z "$vch_progstderr" ]] &&
209 vch_progstderr=$(shlibinc__get_file_top_valid_id) &&
210 eval "exec $vch_progstderr>&2"
212 grep -e "^pipe:" << EOF
213 `readlink /proc/self/fd/$vch_progstdin`
215 [[ $? == 0 ]] && eval "exec $vch_progstdin>&$vch_null"
217 grep -e "^pipe:" << EOF
218 `readlink /proc/self/fd/$vch_progstdout`
220 [[ $? == 0 ]] && eval "exec $vch_progstdout>&$vch_null"
222 grep -e "^pipe:" << EOF
223 `readlink /proc/self/fd/$vch_progstderr`
225 [[ $? == 0 ]] && eval "exec $vch_progstderr>&$vch_null"
229 shlibinc_onetime_init
232 # @ include时,判断SHLIB_LIST,已include,skip。首次include中,调用_init()函数,设置SHLIB_LIST。
233 # @ load时,
234 # # 判断SHLIB_INC_LIST,已load,且判断SHLIB_LIST中未load,只调用_init()进行初始化+ 判断SHLIB_INC_LIST,已load,且判断SHLIB_LIST中已load,skip
235 # # 其它,首次load时,调用_init()函数,设置SHLIB_INC_LIST和当前进程的SHLIB_LIST。SHLIB_LIST在创建新进程时不export。
239 # fsyntax: decl_shlib_var <var-name>
240 # fdesc: check exist before var declare, and add SHLIB__ prefix.
242 decl_shlib_var ()
244 [[ ! -v SHLIB__${1^^} ]] && declare -g SHLIB__${1^^}
249 # LIST variable have not been used
251 if [[ ! -v SHLIB_INC_LIST ]]; then
252 declare -g SHLIB_INC_LIST=${SHLIB_INC_LIST=""}
254 if [[ ! -v SHLIB_LIST ]]; then
255 declare -g -x SHLIB_LIST=${SHLIB_LIST=""}
257 #SHLIB_LIST=${SHLIB_LIST=""}
260 [[ ! -v nest_level ]] && declare -g nest_level=0
262 [[ ! -v nest_level_shlib ]] && declare -g -a nest_level_shlib[0]=""
264 # load switchs
265 [[ ! -v shlib_load_state ]] && declare -g -a shlib_load_state[0]=0
267 # do not append -x attr, it should be resetted in sub-process.
268 [[ ! -v loading_flag ]] && declare -g -a loading_flag[0]=0
270 # TBD: this var should be used as a load type for shlib,
271 # code in shlib use it to set different variable type, -x or not.
272 [[ ! -v inc_flag ]] && declare -g -a inc_flag=""
273 [[ ! -v last_inc_flag ]] && declare -g last_inc_flag=""
277 # ROOTFS
278 # PROG_ID
279 # SHLIB_VERSION
280 # SHLIB_PATH
281 # path_list
283 # SHLIB_IDX
284 # SHLIB_MODULE
285 # SHLIB_MODULE_STK[]
287 # SHLIB_INC_LIST
288 # SHLIB_LIST
289 # nest_level
290 # nest_level_shlib[], similar with BASH_SOURCE, and used for nestly sourcing.
291 # shlib_load_state[], used for nestly sourcing.
292 # loading_flag[], load/include for current nest level.
293 # inc_flag
294 # last_inc_flag
297 ##############################
298 # section: private function
299 ##############################
301 ########################################################################
302 # debug info output
303 ########################################################################
306 # private dbgout function in this lib.
308 tmp_dbgout ()
310 echo -ne "$@\n" >&2
314 # private dbgout function in this lib.
316 misc_dbgout ()
318 if [[ ${SHLIB_DBGOUT_OPT} =~ ':shlibinc:' || ${SHLIB_DBGOUT_OPT} =~ ':shlibinc-misc:' ]]; then
319 echo -ne "$@\n" >&2
324 # private dbgout function in this lib.
326 lvlist_dbgout ()
328 if [[ ${SHLIB_DBGOUT_OPT} =~ ':shlibinc:' || ${SHLIB_DBGOUT_OPT} =~ ':shlibinc-lvlist:' ]]; then
329 local pfx=
330 printf -v pfx "%*s" $(( nest_level * 4 )) " "
331 echo -ne "${pfx}\033[33m$@\033[0m\n" >&2
336 # private dbgout function in this lib.
338 list_dbgout ()
340 if [[ ${SHLIB_DBGOUT_OPT} =~ ':shlibinc:' || ${SHLIB_DBGOUT_OPT} =~ ':shlibinc-list:' ]]; then
341 echo -ne "$@\n" >&2
346 # private dbgout function in this lib.
348 inc_dbgout ()
350 if [[ ${SHLIB_DBGOUT_OPT} =~ ':shlibinc:' || ${SHLIB_DBGOUT_OPT} =~ ':shlibinc-inc:' ]]; then
351 echo -ne "$@\n" >&2
355 dbgout_print_elemlist ()
358 local vname=$(get_vname_pfx "$1")
360 tmp_dbgout "\$1=$1\n"
361 tmp_dbgout "vname=$vname\n"
363 list2_name="varlist2_$vname[@]"
364 echo "${list2_name}=${!list2_name}"
365 declare -p varlist2_$vname
367 list2_name="funclist2_$vname[@]"
368 echo "${list2_name}=${!list2_name}"
369 declare -p funclist2_$vname
371 list2_name="aliaslist2_$vname[@]"
372 echo "${list2_name}=${!list2_name}"
373 declare -p aliaslist2_$vname
375 list2_name="ctx_aliaslist2_$vname[@]"
376 echo "${list2_name}=${!list2_name}"
377 declare -p ctx_aliaslist2_$vname
379 list_dbgout ""
384 ########################################################################
385 # debug info output
386 ########################################################################
389 # fsyntax: get_vname_pfx
390 # fdesc: get translate vname string by a function, so that shlib
391 # prefix uses the same syntax by a function.
393 get_vname_pfx ()
395 echo "$1" | sed -E "s|[^[:alnum:]_]|_|g" | tr '[[:lower:]]' '[[:upper:]]'
397 # local vname=
399 # vname=${1//[^[:alnum:]_]/_}
400 # vname=${vname^^}
401 # echo "$vname"
403 # nEKfP=${nEKfP%%\.*}
404 # nEKfP=${nEKfP##*/}
406 # vname="$(basename ${1})"
407 # vname="$(echo $1 | cksum | cut -d ' ' -f1)_${vname//[^_[:alnum:]]/_}"
411 # fsyntax: alias_redefine
412 # fdesc: it re-define an alias cmd for a shlib load in new process.
414 alias_redefine ()
416 local vname=$(get_vname_pfx "$1")
418 list_dbgout "######################################################"
419 list_dbgout "alias_redefine()"
420 dbgout_print_elemlist "$1"
422 list2_name="ctx_aliaslist2_$vname"
423 list_dbgout "${!list2_name}"
424 eval ${!list2_name}
427 ########################################################################
428 # list info for var/func/alias
429 ########################################################################
431 # get func list in current process.
432 get_func_list ()
434 local data=
436 while read data; do
437 data="${data##* }"
438 echo -ne "${data}\n"
439 done < <(declare -F)
442 # get var list in current process.
443 get_var_list ()
445 local data=
447 while read data; do
448 data="${data%%=*}"
449 data="${data##* }"
450 echo "${data}"
451 done < <(declare -p | grep -e "declare" | sed -e "s/^.*-f.*$//g")
454 # get alias list in current process.
455 get_alias_list ()
457 local data=
459 while read data; do
460 # XXX: do not put alias content in list?
461 # data="${data%%=*}"
462 # data="${data##*alias }"
463 echo "${data}"
464 done < <(alias | grep -e "^alias")
468 # fsyntax: list_diff <type> <name>
469 # outputvarname> = <listvarname1> - <listvarname2>
470 # fdesc: type is one of func/var/alias, name is shlib.
472 list_diff ()
474 local vname="$2"
475 local list_name=
476 local list2_name=
477 local data=
479 list_name="${1}list1_$vname"
480 list2_name="${1}list2_$vname"
481 # unset $list2_name
482 export $list2_name=""
483 # echo "$list2_name define:"
484 # declare -p $list2_name
486 # diff with the list saved before source operation.
487 while read data; do
488 # data="${data##* }"
489 data="${data#*< }"
490 if [[ "$1" == func ]]; then
491 # add -x option to function, so that sub-proc can use it without load again.
492 declare -f -x ${data}
493 elif [[ "$1" == var ]]; then
494 if [[ "$data" =~ 'funclist' || "$data" =~ 'varlist' || "$data" =~ 'aliaslist' ]]; then
495 continue
497 elif [[ "$1" == alias ]]; then
498 data="${data%%=*}"
499 data="${data##*alias }"
501 # save to array.
502 eval $list2_name+="\"${data} \""
503 done < <(echo "${!list_name}" | diff --from-file=<(get_$1_list) - | grep -e "<")
505 # ctx_xxx, just for alias recovery.
506 [[ "$1" == alias ]] && eval ctx_$list2_name="\$(alias \${$list2_name})"
508 unset $list_name
509 list_dbgout "\033[34m==================================\033[0m\n"
510 eval list_dbgout \"\${$list2_name[@]}\" | tr ' ' $'\n'
511 list_dbgout "\033[44m==================================\033[0m\n"
515 # fsyntax: get_elem_list null <switchs> <lib-name>
516 # fdesc: get var/func/alias list in current process. it is used for comparing
517 # with the list after shlib load.
519 get_elem_list ()
521 local vname=$(get_vname_pfx "$3")
523 # when it was a pure include invoking, do not
524 # record elem-list.
525 [[ -z $2 || "$2" != "load" ]] && return
527 declare -g funclist1_$vname=""
528 eval funclist1_$vname=\"\$\(get_func_list\)\"
530 declare -g varlist1_$vname=""
531 eval varlist1_$vname=\"\$\(get_var_list\)\"
533 declare -g aliaslist1_$vname=""
534 eval aliaslist1_$vname=\"\$\(get_alias_list\)\"
538 # fsyntax: save_lib_elem_list null <switch> <lib-name>
539 # fdesc: get current var/func/alias list again, and comparing with the one saved
540 # in get_curr_elem_list(), output tree array list of var/func/alias, which
541 # is appended by the loaded shlib.
543 save_lib_elem_list ()
545 local vname=$(get_vname_pfx "$3")
547 if [[ "$2" == "load" ]]; then
548 # for func list
549 list_diff func $vname
551 # for var list
552 list_diff var $vname
554 # for alias list
555 list_diff alias $vname
560 # xxx_curr_xxx(), it works in nestly sourcing.
561 # it compare list in source file, and update nestly source
562 # variable.
566 # fsyntax: get_curr_elem_list <lib-path> <switchs> <lib-name>
567 # fdesc: get var/func/alias list in current process. it is used for comparing
568 # with the list after shlib load.
569 # the difference with get_elem_list() is that it can work in nestly sourcing.
571 get_curr_elem_list ()
573 [[ ! -v nest_level ]] && declare -g nest_level=""
575 lvlist_dbgout "get_curr_elem_list ($(basename $1), $2, nest_level=$nest_level)"
577 # save current elem_list for last level
578 if [[ $nest_level != 0 && -n "${nest_level_shlib[$nest_level]}" ]]; then
579 # list_dbgout nest_level=$nest_level
580 lvlist_dbgout "save_lib_elem_list(null," \
581 "${shlib_load_state[$nest_level]}," \
582 "${nest_level_shlib[$nest_level]}," \
583 "nest_level=$nest_level)\n"
584 # in this fucn, it diff elem list, and append to previous one.
585 save_lib_elem_list null ${shlib_load_state[$nest_level]} "${nest_level_shlib[$nest_level]}"
588 # record nest level
589 # this code is running in a `` sub-proc.
590 # print ": $((nest_level++))" to update the value in parent process.
591 nest_level=$(( nest_level + 1 ))
592 nest_level_shlib[$nest_level]="$3"
593 shlib_load_state[$nest_level]="$2"
594 loading_flag[$nest_level]="$inc_flag"
596 # get current elem_list for current level
597 get_elem_list "$1" "$2" "$3"
598 lvlist_dbgout "exit get_elem_list($(basename $1), $2, nest_level=$nest_level)"
602 # fsyntax: save_curr_lib_elem_list <lib-path> <switch> <lib-name>
603 # fdesc: get current var/func/alias list again, and comparing with the one saved
604 # in get_curr_elem_list(), output tree array list of var/func/alias, which
605 # is appended by the loaded shlib.
607 save_curr_lib_elem_list ()
609 local pfx=
611 lvlist_dbgout "xxx save_curr_lib_elem_list($(basename $1), $2, nest_level=$nest_level)"
613 # release last level elem.
614 unset loading_flag[$nest_level]
615 unset nest_level_shlib[$nest_level]
616 unset shlib_load_state[$nest_level]
618 nest_level=$(( nest_level - 1 ))
619 #nest_level_shlib[$nest_level]="$1"
621 save_lib_elem_list "$@"
623 if [[ -n "${nest_level_shlib[$nest_level]}" ]]; then
624 get_elem_list "null" "${shlib_load_state[$nest_level]}" "${nest_level_shlib[$nest_level]}"
625 lvlist_dbgout "xxx get_elem_list($(basename ${nest_level_shlib[$nest_level]}), $2, nest_level=$nest_level)"
629 save_lib_elem_list_bak ()
631 local vname=$(get_vname_pfx "$3")
632 local list_name=
633 local data=
634 local list2_name=
636 if [[ "$2" == "load" ]]; then
637 # for func list
638 list_name="funclist1_$vname"
639 list2_name="funclist2_$vname"
640 # unset $list2_name
641 declare -g -x $list2_name=""
642 # echo "$list2_name define:"
643 # declare -p $list2_name
644 # diff with the funclist saved before source operation.
645 while read data; do
646 # add -x option to function, so that sub-proc can use it without load again.
647 declare -f -x ${data##* }
648 # save to array.
649 eval $list2_name+="\"${data##* } \""
650 done < <(echo "${!list_name}" | diff --from-file=<(get_func_list) - | grep -e "<")
651 unset $list_name
652 list_dbgout "\033[34m==================================\033[0m\n"
653 list2_name="${list2_name}[@]"
654 list_dbgout "${!list2_name}\n"
655 # return
657 # for var list
658 list_name="varlist1_$vname"
659 list2_name="varlist2_$vname"
660 # unset $list2_name
661 declare -g -x $list2_name=""
662 get_var_list > ~/b.txt
663 while read data; do
664 # save to array.
665 # data="${data%=*}"
666 if [[ "$data" =~ 'funclist' || "$data" =~ 'varlist' || "$data" =~ 'aliaslist' ]]; then
667 # echo $data
668 continue
670 data="${data#*< }"
671 # list_dbgout "data=$data\n"
672 # echo $data
673 eval $list2_name+="\"\${data} \""
674 done < <(echo "${!list_name}" | diff --from-file=<(get_var_list) - | grep -e "<")
675 unset $list_name
676 list_dbgout "\033[44m==================================\033[0m\n"
677 eval list_dbgout \"\${$list2_name[@]}\" | tr ' ' $'
679 # return
681 # for alias list
682 list_name="aliaslist1_$vname"
683 list2_name="aliaslist2_$vname"
684 # unset $list2_name
685 declare -g -x $list2_name=""
686 while read data; do
687 # save to array.
688 data="${data%%=*}"
689 data="${data##*alias }"
690 eval $list2_name+="\"${data} \""
691 done < <(echo "${!list_name}" | diff --from-file=<(get_alias_list) - | grep -e "< alias")
692 ( eval list_dbgout \"\${$list2_name}\" ) | tr ' ' $'
694 eval $list2_name="\$(alias \${$list2_name})"
695 unset $list_name
696 list_dbgout "\033[44m==================================\033[0m\n"
697 ( eval list_dbgout \"\$list2_name=\${$list2_name}\" ) | tr ' ' $'\n'
698 list_dbgout "\033[44m==================================\033[0m\n"
702 elemlist_var_clear ()
704 local vname=$(get_vname_pfx "$1")
706 unset varlist2_$vname
707 unset funclist2_$vname
708 unset aliaslist2_$vname
712 # fsyntax: lib_load_prev <lib-path> <inc-type> <lib-name>
713 # fdesc: source lib file, and compare var/func/alias list between before and after of source.
714 # the name of lib must be an absolutly path for shlib file. switch enable the comparing
715 # feature.
716 # this function is used for nest inclussion, when some of the variable need to be local
717 # attribute variable.
719 lib_load_prev ()
721 # local libfile=$1
722 # local inc_type=$2
723 # local lib_name=$3
725 local vname=
726 local pfx=
727 local pwd=
728 local tmp=
729 local ret=
730 local ret2=
732 # declare the variable, or it will be defined as a local variable.
733 if [[ $2 == load ]]; then
734 vname=SHLIB_LIST
735 else
736 vname=SHLIB_INC_LIST
738 declare -g $vname
739 # declare -p $vname
741 inc_dbgout "[$2]: XXXXXXXXXXX $1"
742 get_curr_elem_list $1 $2 $3
743 printf -v pfx "%*s" $(( nest_level * 4 )) " "
744 inc_dbgout "${pfx}[source]: $1\n"
746 # append variable like 'BASH_SOURCE'
748 tmp="${3##*\/}"
749 tmp="${tmp//\.shlib}"
751 [[ ! -v SHLIB_IDX || -z $SHLIB_IDX ]] && SHLIB_IDX=0
752 declare -g SHLIB_MODULE_STK[$SHLIB_IDX]=$SHLIB_MODULE
753 declare -g SHLIB_MODULE=$(get_vname_pfx ${tmp^^})
754 declare -g SHLIB_IDX=$(( SHLIB_IDX + 1 ))
756 pwd="$PWD"
757 cd $(dirname $1)
758 source $1
759 ret=$?
760 cd "$pwd"
762 # recovery
763 declare -g SHLIB_IDX=$(( ${SHLIB_IDX} - 1 ))
764 eval declare -g SHLIB_MODULE=${SHLIB_MODULE_STK[$SHLIB_IDX]}
765 unset SHLIB_MODULE_STK[$SHLIB_IDX]
767 if [[ $? == 0 && $ret == 0 ]]; then
768 eval "$vname+=\"
769 $1\""
770 # misc_dbgout "XXXXXXXXXXXXXXXX $3=${!3}=$1"
771 if [[ $2 == load ]]; then
772 # tmp="$(basename $1)"
773 # tmp="${tmp%\.*}"
775 # tmp=${tmp//[^[:alnum:]_]/_}
776 # GVAR_INIT ${tmp,,}
777 GVAR_INIT "$3"
779 else
780 echo -ne "err: source file($1) failed.\n" >&2
783 save_curr_lib_elem_list $1 $2 $3
787 # fsyntax: lib_unload_prev <lib-path> <inc-type> <lib-name>
788 # fdesc: source lib file, and compare var/func/alias list between before and after of source.
789 # the name of lib must be an absolutly path for shlib file. switch enable the comparing
790 # feature.
791 # this function is used for nest inclussion, when some of the variable need to be local
792 # attribute variable.
794 lib_unload_prev ()
796 local vname=$(get_vname_pfx "$3")
797 local listname=
798 local data=
799 local tmp=
801 # release var define.
802 listname="varlist2_$vname"
803 unset ${!listname}
805 # release function define.
806 listname="funclist2_$vname"
807 unset ${!listname}
809 # release function define.
810 listname="aliaslist2_$vname"
811 while read data; do
812 data=${data#*alias }
813 data=${data%%=*}
814 tmp+="$data "
815 done < <(echo "${!listname}" | grep -e "^alias ")
816 unalias ${tmp}
820 # fsyntax: lib_path_adapting <xdsource_lib>
822 lib_path_adapting ()
824 local xdsource_cnt=
825 local xdsource_i=
826 local xdsource_lib="$1"
828 if [[ "${xdsource_lib:0:1}" == "/" ]]; then
830 elif [[ "${xdsource_lib:0:2}" == "./" ]]; then
831 xdsource_lib=$PWD/${xdsource_lib}
832 else
833 xdsource_cnt=${#path_list[@]}
834 for (( xdsource_i=0; xdsource_i < xdsource_cnt; xdsource_i++ )); do
835 # use eval to change path with '~' char to absolutly path.
836 eval path_list[$xdsource_i]="${path_list[$xdsource_i]}"
837 if [[ ! -f "${path_list[$xdsource_i]}/$xdsource_lib" ]]; then
838 continue;
840 xdsource_lib="${path_list[$xdsource_i]}/$xdsource_lib";
841 break
842 done
844 if [[ $xdsource_i == $xdsource_cnt ]]; then
845 if [[ -f $PWD/$xdsource_lib ]]; then
846 xdsource_lib="$PWD/$xdsource_lib";
847 else
848 echo -ne "err: file $xdsource_lib can not be found in $SHLIB_PATH\n" >&2
849 kill $PROG_ID
850 # exit 1
855 echo "$xdsource_lib"
856 return 0
860 # fsyntax: uniq_inc_chk <xdsource_lib>
861 # fdesc: inc/load pre-proc for uniq_inc_chk
862 # @ inc_flag, last_inc_flag, tag flag with load/include,
863 # to avoid re-include in nestly sourcing.
864 # @ SHLIB_LIST, SHLIB_INC_LIST, check if current lib has been
865 # loaded or included.
867 uniq_inc_chk ()
869 local xdsource_lib="$1"
871 # set this var before lib_load_prev().
872 last_inc_flag=$inc_flag
873 if [[ $inc_flag == load ]]; then
874 inc_dbgout "include => load"
875 inc_flag="load"
876 else
877 inc_flag="include"
880 # uniq include judgement.
881 # SHLIB_LIST is LOADING tag list.
882 # SHLIB_INC_LIST is INCLUDing tag list.
883 if [[ "$SHLIB_INC_LIST" =~ "${xdsource_lib}" ]]; then
884 inc_dbgout "$xdsource_lib has been included before."
885 inc_dbgout "SHLIB_INC_LIST=$SHLIB_INC_LIST"
887 if [[ $inc_flag == load ]]; then
888 #conflect with included shlib
889 inc_dbgout "ignore this conflect in nest include shlib by 'load'."
892 # restore value.
893 inc_flag=$last_inc_flag
894 last_inc_flag=""
896 return 1
898 if [[ $inc_flag == load && "$SHLIB_LIST" =~ "${xdsource_lib}" ]]; then
899 inc_dbgout "$xdsource_lib has been loaded before."
900 inc_dbgout "SHLIB_LIST=$SHLIB_LIST"
902 # just invoke shlib_xxx_init() only in PROG_GVAR_INIT().
903 # xdsource_lib=${xdsource_lib//[^[:alnum:]_]/_}
904 PROG_GVAR_INIT ${xdsource_lib,,}
906 # restore value.
907 inc_flag=$last_inc_flag
908 last_inc_flag=""
910 return 1
913 return 0
917 ##############################
918 # section: public function
919 ##############################
921 #alias shlibopt="source shlibopt.sh"
923 shlibopt ()
925 source shlibopt.sh $@
930 # 3 type of XXX_PROG_ID_FLAG
931 # @ XXX_PROG_ID_FLAG undefined, it means first time lib loading, invoke init function
932 # in GVAR_INIT, and do nothing in PROG_GVAR_INIT.
933 # @ XXX_PROG_ID_FLAG equal to $$, it means lib has been loaded in current process. do
934 # nothing either in PROG_GVAR_INIT or in GVAR_INIT. return from souce cmd.
935 # @ XXX_PROG_ID_FLAG not equal to $$, it means lib is loaded by session/system process,
936 # and it only need to re-init lib var in one time, but didn't load other functions.
937 # invoke init function in PROG_GVAR_INIT, and return from shlib 'source'(cmd not code),
938 # GVAR_INIT can not be invoked.
940 # there is a example:
941 # stdio.shlib has been loaded by 'include' in sysyem init. program named prog invoke
942 # include stdio.shlib, and shlib_templete.shlib. shlib_templete.shlib invoke include
943 # stdio.shlib. here stdio.shlib is loaded 3 times.
944 # @ first time in system init, and invoke var_init() in GVAR_INIT, while XXX_PROG_ID_FLAG
945 # is not defined.
946 # @ second time in 'prog', XXX_PROG_ID_FLAG is pid of session process, and not equal to
947 # current pid. so invoke var_init() in PROG_GVAR_INIT, and retrun from source cmd.
948 # @ the third time in shlib_templete.shlib, XXX_PROG_ID_FLAG is current pid, and do nothing,
949 # return from source cmd directly.
952 # todo:
953 # there are two condition in using. re-include when source include, or invoke in sub-process.
957 # fsyntax: PROG_GVAR_INIT
958 # fdesc: global variable init in shlib, when the lib file has been loaded.
959 # it checks variable setted by GVAR_INIT.
961 alias PROG_GVAR_INIT='
963 nEKfP=
964 input_nEKfP=
965 PFX_nEKfP=
966 flag_aFmIG=
967 func_aFmIG=
969 read -t 1 nEKfP
971 input_nEKfP="$nEKfP"
972 PFX_nEKfP=$(get_vname_pfx "$nEKfP")
973 nEKfP=${nEKfP%%\.*}
974 nEKfP=${nEKfP##*/}
976 flag_aFmIG="${PFX_nEKfP}_PROG_ID_FLAG"
977 func_aFmIG="$(declare -F shlib_${nEKfP,,}_init)"
979 if [[ -n ${!flag_aFmIG} && -n "$func_aFmIG" ]]; then
980 if [[ ${!flag_aFmIG} != $$ ]]; then
981 declare -g -x "${flag_aFmIG}=$$"
983 # re-define alias cmd automatically before init.
984 alias_redefine ${input_nEKfP}
987 # it will display error while readonly var is defined
988 # and setted again. odirect stderr to null,
989 # or process by shlib code.
991 nEKfP=${nEKfP,,}
992 shlib_${nEKfP}_init
993 inc_dbgout "${nEKfP} re-init.\n"
995 unset nEKfP
996 unset flag_aFmIG
997 unset func_aFmIG
1001 unset nEKfP
1002 unset flag_aFmIG
1003 unset func_aFmIG
1004 } <<<'
1007 # fsyntax: GVAR_INIT
1008 # fdesc: set mutex include flag.
1010 alias GVAR_INIT='
1012 nEKfP=
1013 PFX_nEKfP=
1014 flag_aFmIG=
1016 read -t 1 nEKfP
1018 PFX_nEKfP=$(get_vname_pfx "$nEKfP")
1019 nEKfP=${nEKfP%%\.*}
1020 nEKfP=${nEKfP##*/}
1022 flag_aFmIG="${PFX_nEKfP}_PROG_ID_FLAG"
1024 if [[ -z ${!flag_aFmIG} ]]; then
1025 declare -g -x "${flag_aFmIG}=$$"
1027 # nEKfP=${nEKfP,,}
1028 # shlib_${nEKfP}_init
1031 unset nEKfP
1032 unset flag_aFmIG
1033 } <<<'
1035 alias PROG_GVAR_CLR='
1037 nEKfP=
1038 PFX_nEKfP=
1039 flag_aFmIG=
1040 func_aFmIG=
1042 read -t 1 nEKfP
1044 PFX_nEKfP=$(get_vname_pfx "$nEKfP")
1045 nEKfP=${nEKfP%%\.*}
1046 nEKfP=${nEKfP##*/}
1048 flag_aFmIG="${PFX_nEKfP}_PROG_ID_FLAG"
1050 unset ${!flag_aFmIG}
1052 unset nEKfP
1053 unset flag_aFmIG
1054 unset func_aFmIG
1055 } <<<'
1058 # fsyntax: uniqlib
1059 # fdesc: the code below let the shlib loading operation only
1060 # executing one time.
1062 alias uniqlib='
1064 nEKfP=
1065 PFX_nEKfP=
1066 flag_aFmIG=
1067 func_aFmIG=
1069 read -t 1 nEKfP
1071 PFX_nEKfP=$(get_vname_pfx "$nEKfP")
1072 nEKfP=${nEKfP%%\.*}
1073 nEKfP=${nEKfP##*/}
1075 flag_aFmIG="${PFX_nEKfP}_PROG_ID_FLAG"
1076 func_aFmIG="$(declare -F shlib_${nEKfP,,}_init)"
1078 flag_aFmIG="${PFX_nEKfP}_PROG_ID_FLAG"
1079 func_aFmIG="$(declare -F shlib_${nEKfP,,}_init)"
1080 if [[ -n ${!flag_aFmIG} && -n $func_aFmIG ]]; then
1081 inc_dbgout "uniqlib: return if it has been loaded."
1083 unset nEKfP
1084 unset flag_aFmIG
1085 unset func_aFmIG
1086 return 0
1089 declare -g -x "${flag_aFmIG}=$$"
1091 unset nEKfP
1092 unset flag_aFmIG
1093 unset func_aFmIG
1094 } <<<'
1098 # this cmd is an alias cmd. if i use source cmd to include
1099 # a shell script file, the global cann't be accessed out
1100 # of function. it seems that variable is defined as local
1101 # variable in function. at first, include cmd defined as
1102 # a script file, and use alias define a wrap cmd like
1103 # 'source dsource '. and then, define include cmd all
1104 # in alias cmd string.
1107 ###########################################################
1108 # TBD: add a switch for en/dis loading when it is included.
1109 ###########################################################
1112 # @ inc => load, load
1113 # @ inc => inc, skip
1114 # @ load => inc, set inc, skip
1115 # @ load => load, skip
1117 # different process:
1118 # @ inc => load, load
1119 # @ inc => inc, inc
1120 # @ load => inc, set inc, skip
1121 # @ load => load, skip
1125 # load operation record different var/func/alias, and set func with -x attr,
1126 # but include operation not.
1127 # include operation declared -x variables could be used in sub-proc. so,
1128 # do not use include in cmdline, it will lead that -x variables does not exist
1129 # in load save list.
1133 # fsyntax: include <lib-name>
1134 # fdesc: include a shlib file unique.
1136 # ps:
1137 # cannot use stdin as a tty device, the related cmd like stty
1138 # can not work in shlib file. because the stdin is redirected
1139 # by '<<<'.
1141 alias include=" code='
1142 xdsource_lib=
1143 xdsource_name=
1144 path_list=
1146 read -t 1 xdsource_lib
1147 [[ $? != 0 ]] && break
1148 # tmp_dbgout \"xdsource_lib=$xdsource_lib\n\"
1150 path_list=( \$(echo \$SHLIB_PATH | tr \":\" \" \") )
1152 xdsource_name=\$xdsource_lib
1154 uniq_inc_chk \"\$xdsource_lib\" || break
1157 # lib path adapting.
1159 xdsource_lib=\"\$(lib_path_adapting \"\$xdsource_lib\")\"
1161 lib_load_prev \$xdsource_lib \$inc_flag \$xdsource_name
1163 # restore value.
1164 inc_flag=\$last_inc_flag
1165 last_inc_flag=\"\"
1167 unset xdsource_lib
1168 unset xdsource_name
1171 while true; do
1172 eval \"\$code\";
1173 break;
1174 done;
1175 } <<< "
1178 alias include_bak=" code='
1179 xdsource_cnt=
1180 xdsource_i=
1181 xdsource_lib=
1182 xdsource_name=
1183 path_list=
1185 read -t 1 xdsource_lib
1186 # tmp_dbgout \"xdsource_lib=$xdsource_lib\n\"
1188 xdsource_name=\$xdsource_lib
1190 # set this var before lib_load_prev().
1191 last_inc_flag=\$inc_flag
1192 if [[ \$inc_flag == load ]]; then
1193 inc_dbgout \"include => load\"
1194 inc_flag=\"load\"
1195 else
1196 inc_flag=\"include\"
1199 path_list=( \$(echo \$SHLIB_PATH | tr \":\" \" \") )
1201 # uniq include judgement.
1202 # SHLIB_LIST is LOADING tag list.
1203 # SHLIB_INC_LIST is INCLUDing tag list.
1204 if [[ \"\$SHLIB_INC_LIST\" =~ \"\${xdsource_lib}\" ]]; then
1205 inc_dbgout \"\$xdsource_lib has been included before.\"
1206 inc_dbgout \"SHLIB_INC_LIST=\$SHLIB_INC_LIST\"
1208 if [[ \$inc_flag == load ]]; then
1209 #conflect with included shlib
1210 inc_dbgout \"ignore this conflect in nest include shlib by 'load'.\"
1213 # restore value.
1214 inc_flag=\$last_inc_flag
1215 last_inc_flag=\"\"
1217 break
1219 if [[ \$inc_flag == load && \"\$SHLIB_LIST\" =~ \"\${xdsource_lib}\" ]]; then
1220 inc_dbgout \"\$xdsource_lib has been loaded before.\"
1221 inc_dbgout \"SHLIB_LIST=\$SHLIB_LIST\"
1223 # just invoke shlib_xxx_init() only in PROG_GVAR_INIT().
1224 # xdsource_lib=\${xdsource_lib//[^[:alnum:]_]/_}
1225 PROG_GVAR_INIT \${xdsource_name,,}
1227 # restore value.
1228 inc_flag=\$last_inc_flag
1229 last_inc_flag=\"\"
1231 break
1235 # lib path adapting.
1237 if [[ \"\${xdsource_lib:0:1}\" == \"/\" ]]; then
1239 elif [[ \"\${xdsource_lib:0:2}\" == \"./\" ]]; then
1240 xdsource_lib=\$PWD/\${xdsource_lib}
1241 else
1242 xdsource_cnt=\${#path_list[@]}
1243 for (( xdsource_i=0; xdsource_i < xdsource_cnt; xdsource_i++ )); do
1244 # use eval to change path with '~' char to absolutly path.
1245 eval path_list[\$xdsource_i]=\"\${path_list[\$xdsource_i]}\"
1246 if [[ ! -f \"\${path_list[\$xdsource_i]}/\$xdsource_lib\" ]]; then
1247 continue;
1249 xdsource_lib=\"\${path_list[\$xdsource_i]}/\$xdsource_lib\";
1250 break
1251 done
1253 if [[ \$xdsource_i == \$xdsource_cnt ]]; then
1254 if [[ -f \$PWD/\$xdsource_lib ]]; then
1255 xdsource_lib=\"\$PWD/\$xdsource_lib\";
1256 else
1257 echo -ne \"err: file \$xdsource_lib can not be found in \$SHLIB_PATH\n\" >&2
1258 break;
1263 lib_load_prev \$xdsource_lib \$inc_flag \$xdsource_name
1265 # restore value.
1266 inc_flag=\$last_inc_flag
1267 last_inc_flag=\"\"
1269 unset xdsource_cnt
1270 unset xdsource_i
1271 unset xdsource_lib
1272 unset xdsource_name
1275 while true; do
1276 eval \"\$code\";
1277 break;
1278 done;
1279 } <<< "
1282 # fsyntax: load <lib-name>
1283 # fdesc: load a shlib file uniq in cmdline, it could not be sourced
1284 # by include cmd. when a shlib is very complex, it should be
1285 # load globally to avoid repeated including, to save cpu cost.
1287 # ATTENSION:
1288 # do not invoke 'load' in command line.
1290 alias load=" code='
1291 xdsource_cnt=
1292 xdsource_i=
1293 xdsource_lib=
1294 xdsource_name=
1295 path_list=
1297 read -t 1 xdsource_lib
1299 xdsource_name=\$xdsource_lib
1301 # set this var before lib_load_prev().
1302 last_inc_flag=\$inc_flag
1303 inc_flag=\"load\"
1305 path_list=( \$(echo \$SHLIB_PATH | tr \":\" \" \") )
1307 # SHLIB_LIST is LOADING tag list.
1308 # SHLIB_INC_LIST is INCLUDing tag list.
1309 # deny after include.
1310 if [[ \"\$SHLIB_INC_LIST\" =~ \"\${xdsource_lib}\" ]]; then
1311 inc_dbgout \"\$xdsource_lib has been included before.\"
1312 inc_dbgout \"it can not be loaded!\"
1314 # restore value.
1315 inc_flag=\$last_inc_flag
1316 last_inc_flag=\"\"
1318 break
1320 # uniq include judgement.
1321 if [[ \"\$SHLIB_LIST\" =~ \"\$xdsource_lib\" ]]; then
1322 inc_dbgout \"\$xdsource_lib has been loaded before.\"
1323 inc_dbgout \"execute compact load by invokeding shlib_xxx_init.\"
1324 inc_dbgout \"SHLIB_LIST=\$SHLIB_LIST\"
1326 # restore value.
1327 inc_flag=\$last_inc_flag
1328 last_inc_flag=\"\"
1330 # just invoke shlib_xxx_init() only in PROG_GVAR_INIT().
1331 # xdsource_lib=\${xdsource_lib//[^[:alnum:]_]/_}
1332 PROG_GVAR_INIT \${xdsource_name,,}
1333 break
1336 if [[ \"\${xdsource_lib:0:1}\" == \"/\" ]]; then
1338 elif [[ \"\${xdsource_lib:0:2}\" == \"./\" ]]; then
1339 xdsource_lib=\$PWD/\${xdsource_lib}
1340 else
1341 xdsource_cnt=\${#path_list[@]}
1342 for (( xdsource_i=0; xdsource_i < xdsource_cnt; xdsource_i++ )); do
1343 # use eval to change path with '~' char to absolutly path.
1344 eval path_list[\$xdsource_i]=\"\${path_list[\$xdsource_i]}\"
1345 if [[ ! -f \"\${path_list[\$xdsource_i]}/\$xdsource_lib\" ]]; then
1346 continue;
1348 xdsource_lib=\"\${path_list[\$xdsource_i]}/\$xdsource_lib\";
1349 break
1350 done
1352 if [[ \$xdsource_i == \$xdsource_cnt ]]; then
1353 if [[ -f \$PWD/\$xdsource_lib ]]; then
1354 xdsource_lib=\"\$PWD/\$xdsource_lib\";
1355 else
1356 echo -ne \"err: file \$xdsource_lib can not be found in \$SHLIB_PATH\n\" >&2
1357 break;
1362 lib_load_prev \$xdsource_lib \$inc_flag \$xdsource_name
1364 # restore value.
1365 inc_flag=\$last_inc_flag
1366 last_inc_flag=\"\"
1368 unset xdsource_cnt
1369 unset xdsource_i
1370 unset xdsource_lib
1371 unset xdsource_name
1374 while true; do
1375 eval \"\$code\";
1376 break;
1377 done;
1378 } <<< "
1380 alias unload=" code='
1381 xdsource_cnt=
1382 xdsource_i=
1383 xdsource_lib=
1384 xdsource_name=
1385 path_list=
1387 read -t 1 xdsource_lib
1389 xdsource_name=\$xdsource_lib
1391 inc_dbgout \"unload(\$xdsource_name)\n\"
1393 # set this var before lib_unload_prev().
1394 last_inc_flag=\$inc_flag
1395 inc_flag=\"unload\"
1397 path_list=( \$(echo \$SHLIB_PATH | tr \":\" \" \") )
1399 # SHLIB_LIST is LOADING tag list.
1400 # SHLIB_INC_LIST is INCLUDing tag list.
1401 # deny after include.
1402 if [[ \"\$SHLIB_INC_LIST\" =~ \"\${xdsource_lib}\" ]]; then
1403 inc_dbgout \"\$xdsource_lib has been included before.\"
1404 inc_dbgout \"it can not be unloaded!\"
1406 # restore value.
1407 inc_flag=\$last_inc_flag
1408 last_inc_flag=\"\"
1410 break
1412 # uniq include judgement.
1413 if [[ ! \"\$SHLIB_LIST\" =~ \"\$xdsource_lib\" ]]; then
1414 inc_dbgout \"\$xdsource_lib has not been loaded before.\"
1415 inc_dbgout \"SHLIB_LIST=\$SHLIB_LIST\"
1417 # restore value.
1418 inc_flag=\$last_inc_flag
1419 last_inc_flag=\"\"
1421 break
1424 inc_dbgout \"\$xdsource_lib has been loaded before.\"
1425 inc_dbgout \"execute compact load by invokeding shlib_xxx_init.\"
1427 # skip file existing judgement.
1429 lib_unload_prev \$xdsource_lib \$inc_flag \$xdsource_name
1430 PROG_GVAR_CLR \$xdsource_name
1431 SHLIB_LIST=\"\$(echo \"\${SHLIB_LIST}\" | grep -v \${xdsource_name})\"
1433 # restore value.
1434 inc_flag=\$last_inc_flag
1435 last_inc_flag=\"\"
1437 unset xdsource_cnt
1438 unset xdsource_i
1439 unset xdsource_lib
1440 unset xdsource_name
1443 while true; do
1444 eval \"\$code\";
1445 break;
1446 done;
1447 } <<< "
1450 # fsyntax: reload <lib-name>
1451 # fdesc: load a shlib file uniq in cmdline, it could not be sourced
1452 # by include cmd. when a shlib is very complex, it should be
1453 # load globally to avoid repeated including, to save cpu cost.
1455 # ATTENSION:
1456 # do not invoke 'load' in script code.
1458 alias reload=" code='
1459 xdsource_xxx=
1461 read -t 1 xdsource_xxx
1463 inc_dbgout \"reload(\$xdsource_xxx)\"
1465 unload \$xdsource_xxx
1466 load \$xdsource_xxx
1468 unset xdsource_xxx
1471 while true; do
1472 eval \"\$code\";
1473 break;
1474 done;
1475 } <<< "
1478 ####################################################################################
1481 # shlib info get and set.
1482 # user can use shlibopt to do this work in command line.
1486 # fsyntax: get_shlib_path
1487 # fdesc: get the path list of shlib.
1489 get_shlib_path ()
1491 echo $SHLIB_PATH | tr ':' '
1496 # fsyntax: get_shlib_path_var <var-name>
1497 # fdesc: get the path list of shlib in a variable.
1499 get_shlib_path_in_var ()
1501 local v=$1
1503 [[ -z $1 ]] && return
1505 OLD_IFS=$IFS
1506 IFS=":"
1508 eval "${1}=( $SHLIB_PATH )"
1510 IFS=$OLD_IFS
1514 # fsyntax: set_shlib_path <PATH>
1515 # fdesc: set the shlib path string.
1517 set_shlib_path ()
1519 export SHLIB_PATH="$1"
1521 OLD_IFS=$IFS
1522 IFS=":"
1523 path_list=( $path_list )
1524 IFS=$OLD_IFS
1528 # fsyntax: add_shlib_path <PATH>
1529 # fdesc: append a path string to shlib path.
1531 add_shlib_path ()
1533 local path=$1
1535 [[ $1 =~ ^\./ ]] && path=$PWD/$1
1537 export SHLIB_PATH=$(echo "$SHLIB_PATH:$path" | tr -s ':')
1539 OLD_IFS=$IFS
1540 IFS=":"
1541 path_list+=( $path )
1542 IFS=$OLD_IFS
1546 # fsyntax: del_shlib_path <PATH>
1547 # fdesc: delete the N th path string.
1549 del_shlib_path ()
1551 local tmp
1553 # delete path in SHLIB_PATH.
1554 SHLIB_PATH=$( echo ${SHLIB_PATH[@]//$1/} | tr -s $'\n')
1556 # add eval to expansion operator like '~' to corresponding path.
1557 # so that, $1 use ~ or not does not effect path setting.
1558 tmp=$(eval echo $1)
1559 SHLIB_PATH=$( echo ${SHLIB_PATH[@]//$tmp/} | tr -s $'\n')
1564 ####################################################################################
1567 # fsyntax: prev_include <shlibname>
1568 # fdesc: generate shlib file name with path. generate other paramters
1570 prev_include ()
1572 local cnt=
1573 local i=
1574 local lib=
1575 local name=
1576 local R_LIB_LOAD_LIST=
1577 local R_LIB_INC_LIST=
1578 path_list=
1580 #read -t 1 lib
1581 lib=$1
1582 name=$lib
1584 # set this var before lib_load_prev().
1585 last_inc_flag=$inc_flag
1586 if [[ $inc_flag == load ]]; then
1587 inc_dbgout "include => load"
1588 inc_flag="load"
1589 else
1590 inc_flag="include"
1593 path_list=( $(echo $SHLIB_PATH | tr ":" " ") )
1595 # uniq include judgement.
1596 # SHLIB_LIST is LOADING tag list.
1597 # SHLIB_INC_LIST is INCLUDing tag list.
1598 if [[ "$SHLIB_INC_LIST" =~ "${lib}" ]]; then
1599 inc_dbgout "$lib has been included before."
1600 inc_dbgout "SHLIB_INC_LIST=$SHLIB_INC_LIST"
1602 if [[ $inc_flag == load ]]; then
1603 #conflect with included shlib
1604 inc_dbgout "ignore this conflect in nest include shlib by 'load'."
1607 # restore value.
1608 inc_flag=$last_inc_flag
1609 last_inc_flag=""
1611 break
1613 if [[ $inc_flag == load && "$SHLIB_LIST" =~ "${lib}" ]]; then
1614 inc_dbgout "$lib has been loaded before."
1615 inc_dbgout "SHLIB_LIST=$SHLIB_LIST"
1617 # just invoke shlib_xxx_init() only in PROG_GVAR_INIT().
1618 # lib=${lib//[^[:alnum:]_]/_}
1619 PROG_GVAR_INIT ${name,,}
1621 # restore value.
1622 inc_flag=$last_inc_flag
1623 last_inc_flag=""
1625 break
1628 if [[ "${lib:0:1}" == "/" ]]; then
1630 elif [[ "${lib:0:2}" == "./" ]]; then
1631 lib=$PWD/${lib}
1632 else
1633 cnt=${#path_list[@]}
1634 for (( i=0; i < cnt; i++ )); do
1635 # use eval to change path with '~' char to absolutly path.
1636 eval path_list[$i]="${path_list[$i]}"
1637 if [[ ! -f "${path_list[$i]}/$lib" ]]; then
1638 continue;
1640 lib="${path_list[$i]}/$lib";
1641 break
1642 done
1644 if [[ $i == $cnt ]]; then
1645 if [[ -f $PWD/$lib ]]; then
1646 lib="$PWD/$lib";
1647 else
1648 echo -ne "err: file $lib can not be found in $SHLIB_PATH\n" >&2
1649 break;
1654 #lib_load_prev $lib $inc_flag $name
1655 echo "$lib $inc_flag $name"
1659 # fsyntax: post_include <param-list> <shlibname>
1660 # fdesc:
1662 post_include ()
1664 # restore value.
1665 inc_flag=$last_inc_flag
1666 last_inc_flag=""
1671 # this variable store a function name, which is used to implement features before
1672 # or after source operation.
1674 declare -g shlibwrap_prevcmd=:
1675 declare -g shlibwrap_postcmd=:
1677 alias cmdwrap="{
1678 while true; do
1679 param_BnDfh=""
1680 file_BnDfh=""
1682 # read paramter and invoke wrap cmd.
1683 read -t 1 param_BnDfh
1685 file_BnDfh=\"$(eval \$shlibwrap_prevcmd \$param_BnDfh)\"
1686 [[ -z $file_BnDfh ]] && break;
1688 # break invoking in shlib.
1689 while true; do
1690 source \${file_BnDfh%%\ *}
1691 break;
1692 done
1694 eval \$shlibwrap_postcmd \$file_BnDfh \$param_BnDfh
1696 unset param_BnDfh
1697 unset file_BnDfh
1699 break;
1700 done;
1701 } <<< "
1704 # fsyntax: <load-cmd-name> <lib-name>
1705 # fdesc: lib file including cmd string.
1707 # ps:
1708 # lib name which is used by include cmd, is passed throw stdin redirected by '<<<'.
1709 # but this causes stdin become a pipe, not a tty device. some code invoke 'stty size'
1710 # will return failed, it can not detect stdin as a console.
1712 inc_alias_str=" { \\\`
1713 xdsource_cnt=
1714 xdsource_i=
1715 read -t 1 xdsource_lib
1717 # [[ -z \\\$path_list ]] && \${INC_NAME}_PATH_LIST=( \\\$(echo \\\$\${INC_NAME}_INC_PATH | tr \\\":\\\" \\\" \\\") )
1719 # include file unique
1720 if [[ \\\"\\\$\${INC_NAME}_INC_LIST\\\" =~ \\\"\\\$xdsource_lib\\\" ]]; then
1721 return
1723 echo eval
1724 xdsource_cnt=\\\${#\${INC_NAME}_PATH_LIST[@]}
1725 for (( xdsource_i=0; xdsource_i < xdsource_cnt; xdsource_i++ )); do
1726 if [[ -f \\\${\${INC_NAME}_PATH_LIST[\\\$xdsource_i]}/\\\$xdsource_lib ]]; then
1727 echo \${INC_CMD} \\\\\\\${\${INC_NAME}_PATH_LIST[\\\\\\\$xdsource_i]}/\\\\\\\$xdsource_lib\;
1729 echo [[ \\\\\\\$? == 0 ]] \\&\\& \${INC_NAME}_INC_LIST+=\\\\\\\":\\\\\\\$xdsource_lib\\\\\\\"
1730 break
1732 # echo source \\\${\${INC_NAME}_PATH_LIST[\\\$xdsource_i]}/\\\$xdsource_lib # && return
1733 done
1735 [[ \\\$xdsource_i == \\\$xdsource_cnt ]] && echo -ne \\\"err: file \\\$xdsource_lib can not be found in \\\$\${INC_NAME}_INC_PATH\n\${INC_NAME}_INC_PATH=\\\\\\\"\\\$\${INC_NAME}_INC_PATH\\\\\\\"\n\\\" >&2
1737 unset xdsource_cnt
1738 unset xdsource_i
1739 \\\`; } <<< "
1742 # fsyntax: <unload-cmd-name> <lib-name>
1743 # fdesc: todo: unload is not implemented.
1745 unload_alias_str=" { \\\`
1746 unset xdsource_i
1747 \\\`; } <<< "
1751 # fsyntax: define_include <name> <inc-name> <loadcmd> <unloadcmd> <inc-path>
1752 # fdesc: include feature cmd defination.
1753 define_include ()
1755 local def_path=
1756 local name=${1^^}
1758 def_path=( "$5" )
1759 for (( i=$((${#def_path[@]}-1)); i>=0; i-- )); do
1760 mkdir -p "${def_path}"
1761 done
1763 eval ${name}_INC_PATH="${5}"
1764 eval ${name}_PATH_LIST=\( "$5" \)
1765 INC_NAME=$name
1766 INC_CMD=$3
1767 eval alias ${2}="\"$inc_alias_str\""
1768 unset INC_NAME
1772 # fsyntax: define_load <name> <inc-name> <loadcmd> <unloadcmd> <inc-path>
1773 # fdesc: load feature cmd defination.
1774 define_load ()
1776 local def_path=
1777 local name=${1^^}
1779 def_path=( "$5" )
1780 for (( i=$((${#def_path[@]}-1)); i>=0; i-- )); do
1781 mkdir -p "${def_path}"
1782 done
1784 eval ${name}_INC_PATH="${5}"
1785 eval ${name}_PATH_LIST=\( "$5" \)
1786 INC_NAME=$name
1787 INC_CMD=$3
1788 eval alias ${2}="\"$inc_alias_str\""
1789 unset INC_NAME
1793 ##############################
1794 # section: file tail
1795 ##############################