code repo connnect test
[shlib.git] / shlib / stdio.shlib
blobc3c60db47d71e1b37e93db0c6b6d1dbd54ed682c
1 #!/bin/bash
2 ############################################################
3 # source: stdio.shlib
4 # author: devenkong(18151155@qq.com)
5 # date: 2021-12-15
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 # stdio is a string output shlib. some times, we need
14 # to output on tty console, some times we need to output in
15 # log file, and somtimes we want to output string in pipe
16 # file. stdio provide different output channel.
17 # in linux kernel, there are functions outputing debug
18 # info by different level, or different name. stdio referenced
19 # from it, and provide info/warn/err/log output function.
20 # string output function in stdio is used to solve the
21 # problem, that output string in while loop with redirect or
22 # pipe connection. the stdout is changed to a non-named pipe,
23 # the defsult string output is redirected it the pipe. use
24 # function in stdio.shlib, it will output string on tty,
25 # or other place we invoked.
26 # there is another problem in redirected while loop,
27 # the loop code maybe running as an sub-process, and the cmd
28 # exit is exited from the sub-process, but not the program.
29 # we will find that the variavles cann't be uodated in
30 # loop code as a sub-process. use 'while do done < <()',
31 # instead of ',cat - | while do done'. this makes the loop
32 # code run in current process.
33 # system cmd echo output string without newline by -e
34 # option, but some version needn't this optiin. functions
35 # in stdio.shlib output without newline, and this can be
36 # setted. stdio.shlib also provide functiins to add prefix
37 # and surfix in a string output. we use err() invoking with
38 # defsult "[err]: " prefix.
39 # some usefull function is seperated in dbgout.shlib,
40 # so that stdio.shlib can be compact.
41 # it's the feature for string output, and some console
42 # operation like color char display, term setting, is in
43 # another shlib term.shlib.
44 # the string content can be orgnized as some format,
45 # as we usually use. and dbglogview is designed to dispatch
46 # the formated string. function event, invoke timestamp,
47 # a variable data output, can be dispatched from dbglog
48 # string, it will display dbglog string more beautifull.
49 ############################################################
52 # todo:
54 # - @ *adding channel_config, setting channel paramter.
55 # @ the default string output cmd echo, sometimes run with -e, and sometimes not.
56 # use an alias to making compatiable.
57 # @ add signal process function, and recieve cmd to setting paramter by other
58 # program. it's usefull for a deamon program, to set debug string output range.
59 # @
60 # !@ all the dispaly function will clear BASH_REMATCH when it is invoked. so, do not
61 # invoke string display function in =~ operation, or impove it.
62 # !@ when string output function invoke in set -x state, it will display many cmd in
63 # string output function. maybe disable it by set +x.
64 # !@ there is a bug for string output. echo -ne "-e" treat "-e" as a paramter.
65 # fileter first output char by "-", and output seperatly.
69 # this cmd enable alias feature in script. it is disabled in default.
70 shopt -s expand_aliases
72 # init ROOTFS used in current lib.
73 if [[ ! -d $ROOTFS ]]; then
74 TMP_DIR=`dirname $(which ls)`/..
75 TMP_DIR=`cd $TMP_DIR; pwd; cd - > /dev/null`
76 ROOTFS=${ROOTFS:-$TMP_DIR}
77 unset TMP_DIR
81 # global variable re-init if this shlib has been loaded.
82 # invoke this before 'uniqlib'
84 PROG_GVAR_INIT stdio
87 # the code below only load in one time.
89 uniqlib stdio
91 ##############################
92 # section: public comment info
93 ##############################
96 # %param-global-comment%
97 # dbgout-str: string used as debug output.
98 # func-name: such as dbgout, debout<N>, logout, info, warn, err, crash.
103 ##############################
104 # section: variable define
105 ##############################
109 # get free file handler id.
111 __get_file_top_valid_id ()
113 local i
115 i=$(ulimit -n)
116 for (( i=$((i-1)); i>0; i-- )); do
117 [[ ! -e /proc/self/fd/$i ]] && echo $i && return
118 done
122 # init channels when shlib is loaded in new pogram.
124 init_tty_n_prog_channel ()
126 # init null channel
127 vch_null=$(__get_file_top_valid_id)
128 eval "exec $vch_null<>/dev/null"
130 # init stdio channel of main process
131 vch_progstdin=$(__get_file_top_valid_id)
132 eval "exec $vch_progstdin<&0"
133 vch_progstdout=$(__get_file_top_valid_id)
134 eval "exec $vch_progstdout>&1"
135 vch_progstderr=$(__get_file_top_valid_id)
136 eval "exec $vch_progstderr>&2"
138 # get tty dev for current program
139 f_ttyout=$(ps -p $$ -o tty | tail -n 1 | cut -d ' ' -f1)
140 # echo -ne "f_ttyout=$f_ttyout\n"
141 f_ttyout="/dev/$f_ttyout"
142 # echo -ne "f_ttyout=$f_ttyout\n"
144 # get tty dev again
145 if [[ ! -e $f_ttyout ]]; then
146 f_ttyout=$(tty)
148 if [[ ! -e $f_ttyout ]]; then
149 :;#f_ttyout=$(tty)
153 # open tty dev, and bind to a fd.
155 vch_ttyout=$(__get_file_top_valid_id)
156 # echo $f_ttyout
157 eval "exec $vch_ttyout<>$f_ttyout"
158 [[ $? != 0 ]] && vch_ttyout=$vch_null
160 # echo $vch_ttyout
161 # ls -l /proc/self/fd/
165 # stdio.shlib global variable init.
166 # it should be invoked every time program launched. if stdio.shlib
167 # is loaded in cmd enviroument, use it to re-init in program init.
169 shlib_stdio_init ()
172 # the variable defined and initialized here would be effective in current process.
175 # save main process id of current program.
176 # it should be initialized in program init. and it is used for progexit, the function
177 # is exit from program, not sub-process in script code.
178 declare -g -x STDIO_PROG_ID=$$
181 # logical-channel define
182 # stdin is handler 0
183 # stdout is handler 1
184 # stderr is handler 2
185 # those three channel are not defined in variable.
187 # attention:
188 # those variables should not be defined with -x option.
190 declare -g vch_progstdin=0
191 declare -g vch_progstdout=1
192 declare -g vch_progstderr=2
193 declare -g vch_dbgout=2
194 declare -g vch_logout=2
195 declare -g vch_liberr=2
196 declare -g vch_ttyout=1
197 declare -g vch_null=2
200 # when the string is outputed by using cmd 'tee', it uses file name but not file handler.
201 # so, it defines log file name variable here.
203 #f_dbgout=
204 declare -g f_logout=
205 declare -g f_ttyout=/dev/null # the default ttyout is null dev, and it will be re-initialized in every program.
208 # directory define for ppe file and log file storage.
209 # if the file name paramter in channel init function contsin path info, it eill be resetted.
210 declare -g dbgout_file_path=
211 declare -g logout_file_path=
212 if [[ ! -v def_dbglogout_file_path ]]; then
213 declare -g -r readonly def_dbglogout_file_path=$ROOTFS/var/log/dbglogout/
216 # file, file name string with accessiable path.
217 # file_name, just only a file name.
218 # file_path, path of file.
221 # set initial file name string for pipe file and log file.
222 # the surfix of pipe file is .pipe, the surfix of log file is .1.log or .2.log.
225 declare -g dbgout_file_name=${dbgout_file_name-$(basename $0)}.pipe # dbgout
226 declare -g logout_file_name=${logout_file_name-$(basename $0)}.log
228 declare -g dbgout_sfx=".dbgout"
229 declare -g logout_sfx=".log"
232 # string prefix & surfix
233 # used for string hi-light display, color-full display.
234 # dbgouthex and other dbgout_xxx(), and logout, and ttyout, does not use sfx and pfx.
236 declare -g STROUT_PFX=""
237 declare -g LIBERR_PFX=""
238 declare -g DBGOUT_PFX=""
239 declare -g INFO_PFX=""
240 declare -g WARN_PFX="\033[1m[warn]: \\033[0m"
241 declare -g ERR_PFX="\033[31m[err]: \\033[0m"
242 declare -g CRASH_PFX="[crash]: "
243 declare -g NEWLINE_SFX="\n"
244 declare -g DBG_NEWLINE_SFX=""
247 # paramter of debug and log.
249 DBG_PARAM=""
251 # getting size of file is an io operstion, use string line count to limit log
252 # file size is more suitable.
253 log_row_cnt=0
254 max_log_lines=20000
256 logout_disable_list=
257 output_disable_list=
259 dbgout_default_level=3
260 dbgout_output_level=3
263 # program name
264 # todo: if a file is sourced, $0 means the file sourced, not the main program.
265 # maybe use BASH_ variable is better.
267 PROG_STR=`basename $0`
269 # program channel info init.
270 init_tty_n_prog_channel
272 DEF_INPUT=false
275 shlib_stdio_init
279 # global variable init function invoking.
280 # invoke GVAR_INIT if it is running at first time.
282 #GVAR_INIT stdio
285 ##############################
286 # section: private function
287 ##############################
290 # exit from main process of program.
292 __progexit ()
294 kill $STDIO_PROG_ID
297 # todo:hilight should be reviewed.
298 alias dbg_param_chk="
299 if [[ \$DBG_PARAM =~ \"d\" ]]; then
300 return
303 if [[ \$dbgout_output_level -lt \$dbgout_default_level ]]; then
304 return
307 if [[ \$DBG_PARAM =~ \"H\" ]]; then
308 tput rmso
309 DBG_PARAM=\${DBG_PARAM//H/h}
313 # limit log size.
314 log_size_limit ()
316 local name=
318 log_row_cnt=$(( log_row_cnt++ ))
319 if [[ $log_row_cnt -ge $max_log_lines ]]; then
320 log_row_cnt=0
321 if [[ $f_logout =~ ".2$logout_sfx" ]]; then
322 name=${f_logout%%.?.log}
323 rm $name.1$logout_sfx
324 mv $name.2$logout_sfx $name.1$logout_sfx
325 touch $name.2$logout_sfx
326 else
327 logout_file_name=${logout_file_name//\.1$logout_sfx/\.2$logout_sfx}
328 f_logout=${f_logout//\.1$logout_sfx/\.2$logout_sfx}
334 # set debug out port, and hi-light display mode
335 # dbgset <enable|disable> <value>
337 dbgset ()
339 local param
341 case "$2" in
342 "dbgout" )
343 if [[ "$1" == "enable" ]]; then
344 DBG_PARAM="${DBG_PARAM//d/}"
345 alias dbgout="dbgoutd"
346 else
347 DBG_PARAM="d${DBG_PARAM}"
348 alias dbgout=":"
352 "hilight" )
353 if [[ "$1" == "enable" ]]; then
354 tput rmso
355 DBG_PARAM="h${DBG_PARAM}"
356 else
357 tput rmso
358 DBG_PARAM="${DBG_PARAM//h/}"
362 "[0-9]|d" )
363 if [[ "$1" == "enable" ]]; then
364 DBG_PARAM="${DBG_PARAM//$2/}"
365 else
366 DBG_PARAM="$2${DBG_PARAM}"
369 esac
374 ##############################
375 # section: public function
376 ##############################
379 # fsyntax: libstrout <dbgout-str>
380 # fdesc: used for string output in shlib.
382 libstrout ()
384 [[ $output_disable_list =~ "libstrout" ]] && return
386 # todo: output string in vch_libout channel
388 if [[ $logout_disable_list =~ "libstrout" ]]; then
389 echo -ne "$LIBERR_PFX$@$NEWLINE_SFX" >&$vch_progstdout
390 else
391 echo -ne "$LIBERR_PFX$@$NEWLINE_SFX" | tee -a $f_logout >&$vch_progstdout
392 log_size_limit
397 # fsyntax: liberr <dbgout-str>
398 # fdesc: err info in shlib.
400 liberr ()
402 [[ $output_disable_list =~ "liberr" ]] && return
404 # todo: output string in vch_libout channel
406 if [[ $logout_disable_list =~ "liberr" ]]; then
407 echo -ne "$LIBERR_PFX$@$NEWLINE_SFX" >&vch_liberr
408 else
409 echo -ne "$LIBERR_PFX$@$NEWLINE_SFX" | tee -a $f_logout >&vch_liberr
410 log_size_limit
415 # fsyntax: info <dbgout-str>
416 # fdesc: normal application information string output.
418 info ()
420 [[ $output_disable_list =~ "info" ]] && return
422 if [[ $logout_disable_list =~ "info" ]]; then
423 echo -ne "$INFO_PFX$@$NEWLINE_SFX" >&$vch_progstdout
424 else
425 echo -ne "$INFO_PFX$@$NEWLINE_SFX" | tee -a $f_logout >&$vch_progstdout
426 log_size_limit
431 # fsyntax: warn <dbgout-str>
432 # fdesc: warn output, but use same channel with info.
434 warn ()
436 [[ $output_disable_list =~ "warn" ]] && return
438 if [[ $logout_disable_list =~ "warn" ]]; then
439 echo -ne "$WARN_PFX$@$NEWLINE_SFX" >&$vch_progstdout
440 else
441 echo -ne "$WARN_PFX$@$NEWLINE_SFX" | tee -a $f_logout >&$vch_progstdout
442 log_size_limit
447 # fsyntax: err <dbgout-str>
448 # fdesc: err string output to stderr.
450 err ()
452 [[ $output_disable_list =~ "err" ]] && return
454 if [[ $logout_disable_list =~ "err" ]]; then
455 echo -ne "$ERR_PFX$@$NEWLINE_SFX" >&$vch_progstderr
456 else
457 echo -ne "$ERR_PFX$@$NEWLINE_SFX" | tee -a $f_logout >&$vch_progstderr
458 log_size_limit
463 # fsyntax: errtrace
464 # fdesc: trace output in err channel.
466 errtrace ()
468 err "`echo ${FUNCNAME[*]} | rev | tr ' ' '\n' | rev | sed -E \"s/\n/()=>/g\"`"
472 # fsyntax: crash <dbgout-str>
473 # fdesc: output trace string, and exit from main process.
475 crash ()
477 [[ $output_disable_list =~ "crash" ]] && __progexit
479 if [[ $logout_disable_list =~ "crash" ]]; then
480 echo -ne "$CRASH_PFX`echo ${FUNCNAME[*]} | rev | tr ' ' '\n' | rev | sed -E \"s/\n/()=>/g\"`$NEWLINE_SFX" >&$vch_progstderr
481 echo -ne "$CRASH_PFX$@$NEWLINE_SFX" >&$vch_progstderr
482 else
483 echo -ne "$CRASH_PFX`echo ${FUNCNAME[*]} | rev | tr ' ' '\n' | rev | sed -E \"s/\n/()=>/g\"`$NEWLINE_SFX" |
484 tee -a $f_logout >&$vch_progstderr
485 echo -ne "$CRASH_PFX$@$NEWLINE_SFX" | tee -a $f_logout >&$vch_progstderr
486 log_size_limit
488 __progexit
492 # fsyntax: dbgout <dbgout-str>
493 # fdesc: debug string output. output level can be setted.
495 dbgout ()
497 [[ $output_disable_list =~ "dbgout" ]] && return
499 dbg_param_chk
501 # color setting
502 if [[ $dbg_color_set == 1 ]]; then
503 echo -ne "$dbg_fr_color$dbg_bg_color"
504 dbg_color_set=0
507 if [[ $logout_disable_list =~ "dbgout" ]]; then
508 echo -ne "$DBGOUT_PFX$@$DBG_NEWLINE_SFX" >&$vch_dbgout
509 else
510 echo -ne "$DBGOUT_PFX$@$DBG_NEWLINE_SFX" | tee -a $f_logout >&$vch_dbgout
511 log_size_limit
516 # fsyntax: logout <dbgout-str>
517 # fdesc: log string output with size limit.
519 logout ()
521 echo -ne "$@$NEWLINE_SFX" >> $f_logout
523 log_size_limit
528 # fsyntax: ttyout <dbgout-str>
529 # fdesc: force to output string in tty device in current session.
531 ttyout ()
533 printf "$@$NEWLINE_SFX" >&$vch_ttyout
537 # fsyntax: error <dbgout-str>
538 # fdesc: output err info to tty device, and can not be redirected. it is used for emergency
539 # info output.
541 error ()
543 [[ $output_disable_list =~ "err" ]] && return
545 if [[ $logout_disable_list =~ "err" ]]; then
546     printf "$ERR_PFX$@$NEWLINE_SFX" > $f_ttyout
547 log_size_limit
548 else
549 printf "$ERR_PFX$@$NEWLINE_SFX" | tee -a $f_logout > $f_ttyout
554 # fsyntax: dbgoutd <dbgout-str>
555 # fdesc: just only output string for debug without log.
556 # the purpose of this function is just for debug info.
557 # if the output string is usefull for program, change
558 # it to other output functions then.
559 # it's the temp code for program. delete thos function
560 # invoking when the program is published, or define a
561 # blank cmd like 'dbgoutd () {}' in your code after
562 # shlib including.
564 dbgoutd ()
566 echo -ne "$DBGOUT_PFX$@$DBG_NEWLINE_SFX" >&$vch_dbgout
570 # testing cmd for alias cmd parameter.
571 # echo input-string | { std0=123; exec 123<&0; { var=$(cat -); read $var <&$std0; echo "var=$var"; echo "$var=${!var}"; } <<< "aaa"; }; echo $aaa
575 # fsyntax: readln <var-name>
576 # fdesc: get line data from stdin to the specified var.
577 # the default cmd read does not output blanks
578 # at the beginning of lines.
580 alias readln="
581 stdio_std0=123;
582 ret_aS98s=
583 exec 123<&0;
585 OLD_IFS=\$IFS
586 IFS=\$'\n'
587 read -d \$'\n' -r \$(cat -) <&\$stdio_std0
588 ret_aS98s=\$?
589 IFS=\"\$OLD_IFS\"
590 exec 123>&-
591 unset stdio_std0
592 if [[ \$ret_aS98s == 0 ]]; then
593 unset ret_aS98s
594 true
595 else
596 unset ret_aS98s
597 false
599 } <<< "
603 # fsyntax: input <var-name>
604 # fdesc: input data from current stdin to specified var. var name is given
605 # though stdin, so, before redirect stdin, save it to another fd.
607 alias input='
608 stdio_std0=123;
609 exec 123<&0;
611 OLD_IFS=\$IFS
612 IFS=$"\x00"
613 read -d $'\x00' -r \$(cat -) <&$stdio_std0
614 IFS=\$OLD_IFS
615 exec 123>&-
616 unset stdio_std0
617 } <<< '
620 # fsyntax: var=$(pause <var-name> <promt-str> [<def-value>])
621 # fdesc: output with promt info, wait for string input by user. if DEF_INPUT is true,
622 # read operation will be skipped. it's like a single step feature.
624 pause ()
626 echo -ne "$2" >&vch_progstdout
628 if [[ -n $3 && $DEF_INPUT == "true" ]]; then
629 eval $1="\"\$3\""
630 else
631 OLD_IFS=$IFS
632 IFS=$'\n'
633 read -u $vch_progstdin -r $1
634 IFS=$OLD_IFS
635 [[ -n $3 ]] && [[ -z ${!1} || ${!1} == "$'\n'" ]] && $1=$3
640 # fsyntax: stdin <var>
641 # fdesc: get data in var from program stdin. this cmd is improved
642 # with IFS setting to read ' ' at the beginning of a string line.
643 # eg: read abc <<< " aaa", $abc is 'aaa' without blank prefix.
644 # others, it append -r arguments to read string include '\'.
646 # alias input="read -u $vch_progstdin -r"
647 alias stdin='
649 OLD_IFS=$IFS
650 IFS=$'\n'
651 read -d $'\n' -r $(cat -) <&$vch_progstdin
652 IFS=$OLD_IFS
653 } <<<'
656 # fsyntax: stdout <str>
657 # fdesc: output string data to stdout.
659 alias stdout="echo -ne"
662 # fsyntax: <cmd> | dataout
663 # fdesc: a compatiable interface.
664 # <cmd> | dataout
665 alias dataout="tee"
668 # fsyntax: var=$(datain) 或 datain | <cmd>
669 # fdesc: a compatiable interface. read command will filter
670 # chars. '\' char, ' ' at the beginning of a line, and other
671 # un-printable chars.
673 alias datain="cat - "
677 # fsyntax: set_output_prefix <func-name> [pfx-str]
678 # fparam:
679 # func-name: logout/dbgout/info/warn/err/crash
680 # pfx-str: orefix for different function output.
681 # fdesc: set different prefix string in different function.
683 set_output_prefix ()
685 if [[ -z $1 ]]; then
686 STROUT_PFX=""
687 LIBERR_PFX=""
688 DBGOUT_PFX=""
689 INFO_PFX=""
690 WARN_PFX=""
691 ERR_PFX=""
692 CRASH_PFX=""
693 return
696 if [[ "strout liberr dbgout info warn err crash" =~ $1 ]]; then
697 ${1^^}_PFX="$2"
698 else
699 warn "specify a valid output function name in \"strout liberr dbgout info warn err crash\" by param.\n"
704 # fsyntax: set_auto_newline [newline-sfx]
705 # fdesc: set surfix in every output string.
707 set_auto_newline ()
709 NEWLINE_SFX="$1"
713 # fsyntax: dbgout_set_auto_newline <newline-sfx>
714 # fdesc: output string surfix in dbgout.
716 dbgout_set_auto_newline ()
718 DBG_NEWLINE_SFX="$1"
722 # fsyntax: dbgout_set_default_level <level>
723 # fdesc: set default output level which is used in dbgout.
725 dbgout_set_default_level ()
727 dbgout_default_level=$1
731 # fsyntax: dbgout_set_output_level <level>
732 # fdesc: set debug string output level.
734 dbgout_set_output_level ()
736 dbgout_output_level=$1
740 # fsyntax: set_output_mask <func-name> [enable|disable]
741 # fdesc: set enable/disable for dbgout/info/warn/err/errtrace/crash, developer
742 # didn't need to comment string output function one by one.
744 set_output_mask ()
746 if [[ -z $1 ]]; then
747 output_disable_list=""
748 return
751 if [[ "strout liberr dbgout info warn err crash logout" =~ $1 ]]; then
752 if [[ -z $2 || $2 == "disable" ]]; then
753 output_disable_list="$output_disable_list$1 "
754 elif [[ $2 == "enable" ]]; then
755 output_disable_list=${output_disable_list//$1 /}
757 else
758 warn "specify a valid output function name in \"strout liberr dbgout info warn err crash\" by param.\n"
763 # fsyntax: set_logout_mask <func-name> [enable|disable]
764 # fparam:
765 # func_name: dbgout/info/warn/err/errtrace/crash
766 # fdesc: set log output in different string output function.
768 set_logout_mask ()
770 if [[ -z $1 ]]; then
771 logout_disable_list=""
772 return
775 if [[ "strout liberr dbgout logout info warn err crash" =~ $1 ]]; then
776 # set channel of logout for logout() disable.
777 if [[ $1 == "logout" ]]; then
778 if [[ -z $2 || $2 == "disable" ]]; then
779 init_channel logout "/dev/null"
780 elif [[ $2 == "enable" ]]; then
781 init_channel logout "$logout_file_name"
784 return
787 if [[ -z $2 || $2 == "disable" ]]; then
788 logout_disable_list="$logout_disable_list$1 "
789 elif [[ $2 == "enable" ]]; then
790 logout_disable_list=${logout_disable_list//$1 /}
792 else
793 warn "specify a valid output function name in \"strout liberr dbgout logout info warn err crash\" by param.\n"
798 # fsyntax: set_log_limit <max-log-lines>
799 # fdesc: set log output line limit count.
801 set_log_limit ()
803 max_log_lines=$1
804 log_size_limit
808 # fsyntax: dbgout_printinfo
809 # fdesc: print variable used in this shlib.
811 dbgout_printinfo ()
813 [[ $output_disable_list =~ "dbgout" ]] && return
815 dbg_param_chk
818 echo "vch_dbgout=$vch_dbgout"
819 echo "f_logout=$f_logout"
820 echo "logout_file_path=$logout_file_path"
821 echo "log_row_cnt=$log_row_cnt"
822 } | tee -a $f_logout >&$vch_dbgout
826 # fsyntax: duplicate_channel <dst-channel> <src-channel>
827 # fdesc: duplicate channel.
829 duplicate_channel ()
831 local src_ch_name_str
832 local dst_ch_name_str
834 [[ -z $2 ]] && echo -ne "[failed] init_channel() failed, path of \"$path\" is not existing.\n" >&2 && return 1
836 src_ch_name_str=vch_$1
837 dst_ch_name_str=vch_$2
838 [[ ! "dbgout logout progstdin progstdout progstderr ttyout null" =~ ${!src_ch_name_str} ]] && echo -ne "[failed] duplicate_channel() failed, name of \"${!src_ch_name_str}\" is not valid channel.\n" >&2 && return 2
839 [[ ! "dbgout logout progstdin progstdout progstderr" =~ ${!dst_ch_name_str} ]] && echo -ne "[failed] duplicate_channel() failed, path of \"${!dst_ch_name_str}\" is not valid channel.\n" >&2 && return 2
841 eval $dst_ch_name_str=${!src_ch_name_str}
843 if [[ $1 == "logout" ]]; then
844 f_logout=`readlink /proc/self/fd/${!dst_ch_name_str}`
847 if [[ $1 == "dbgout" ]]; then
848 f_dbgout=`readlink /proc/self/fd/${!dst_ch_name_str}`
852 __logfile_name_fix ()
854 local size=
855 local name=
857 # append number id if it is no number id.
858 if [[ ! $logout_file_name =~ ".1.$logout_sfx" && ! $logout_file_name =~ ".2.$logout_sfx" ]]; then
859 logout_file_name=${logout_file_name%%$logout_sfx}.1.log
860 if [[ -n ${logout_file_path} ]]; then
861 f_logout=$logout_file_path/${logout_file_name}
862 else
863 f_logout=${logout_file_name}
867 log_row_cnt=0
868 if [[ -f $f_logout ]]; then
869 size=$(stat $f_logout | grep -e "Size:")
870 size=${size#*Size: }
871 size=${size%% *}
873 if [[ ! $size =~ [^0-9] ]]; then
874 log_row_cnt=$((size / 30))
877 name=$f_logout
879 log_size_limit
881 # if .2.log is existing, get it's size to update log_row_cnt
882 if [[ -f $f_logout ]]; then
883 size=$(stat $f_logout | grep -e "Size:")
884 size=${size#*Size: }
885 size=${size%% *}
887 if [[ ! $size =~ [^0-9] ]]; then
888 log_row_cnt=$((size / 30))
891 name=$f_logout
893 log_size_limit
899 # fsyntax: init_channel <channel-name> <file-path>|<file-hd>
900 # fparam:
901 # channel-name: logout, dbgout, progstdout, progstdin, progstderr, stdout, stdin, stderr.
902 # set /dev/null if no param specified.
903 # file-path: file name string, maybe with path prefix. if ext-name is same as $dbgout_sfx,
904 # output is pipe file.
905 # setting for dbgout or logout:
906 # @ file name without ext-name, and the file is not a device name, append corresponding
907 # ext-name.
908 # @ if file name with path is valid, save path in var.
909 # @ if file name string without path, use the default path.
910 # @ if the file is stored in pwd, set the name like './file'.
911 # file-hd: an opened fd.
912 # foutput:
913 # fdesc: init and config logical-channel to actual output device. init channel with serial, net,
914 # and other device by device file name, or file handler. invoke physical device paramter setting
915 # before channel init. eg:serial or net.
917 init_channel ()
919 local tmp=
920 local name=$1
921 local name_str=
922 local path_str=
923 local path=${2:-'/dev/null'}
924 local fd=
926 # no channel specified, all channel would be setted with /dev/null
927 if [[ -z $name ]]; then
928 vch_dbgout=$vch_null
929 vch_logout=$vch_null
930 vch_progstdin=$vch_null
931 vch_progstdout=$vch_null
932 vch_progstderr=$vch_null
934 return 0
937 # no valid file name specified.
938 [[ -z ${path##*/} ]] && echo -ne "[failed] init_channel() failed, file of path \"$path\" does not contain file name.\n" >&2 && return 1
940 # todo:
941 # invoke call-back function for unknown device. or invoke device init before this function.
942 if [[ "dbgout logout liberr progstdin progstdout progstderr" =~ $name ]]; then
943 if [[ "$path" =~ [^0-9] ]]; then
945 # the specified file is opened as an idle handler.
948 # save file path and name, used to output log string.
949 if [[ "dbgout logout" =~ $name ]]; then
950 name_str="${name}_file_name"
951 path_str="${name}_file_path"
953 if [[ $(dirname $path) == "." && ! $path =~ "." ]]; then
954 # specify a file name with out path prefix.
955 # the file path is default value init by stdio.shlib.
956 if [[ ! -e ${def_dbglogout_file_path} ]]; then
957 mkdir -p ${def_dbglogout_file_path} 2>&1 >/dev/null
958 if [[ $? != 0 ]]; then
959 # warn "can not write in \"${def_dbglogout_file_path}\", use \$USDO try again.\n"
960 sudo mkdir -p ${def_dbglogout_file_path} 2>&1 >/dev/null
963 declare -g ${path_str}="$def_dbglogout_file_path/$(basename $0)"
964 declare -g ${name_str}=$path
965 elif [[ -e `dirname $path` ]]; then
966 # specify a file name with valid path prefix.
967 # save path prefix.
968 eval $path_str=`cd $(dirname $path); pwd; cd - 2>&1 >/dev/null`
969 eval $name_str=$(basename $path)
970 else
971 echo -ne "[failed] init_channel() failed, path of \"$path\" is not existing.\n" >&2
972 return 2
976 eval "mkdir -p ${!path_str} 2>/dev/null"
977 if [[ $? != 0 ]]; then
978 # warn "can not write in \"${!path_str}\", use \$USDO try again.\n"
979 eval sudo "mkdir -p ${!path_str} 2>/dev/null"
981 chown --reference ~ ${!path_str} 2>/dev/null
982 if [[ $? != 0 ]]; then
983 sudo chown --reference ~ ${!path_str}
985 path=${!path_str}/${!name_str}
987 # use $logout_sfx as default surfix for logout channel, when there is no surfix specified.
988 if [[ $name == "logout" ]]; then
989 if [[ ! ${!name_str##*/} =~ "." && ! -c $path && ! -b $path ]]; then
990 eval ${name_str}="${!name_str}$logout_sfx"
991 path="$path$logout_sfx"
993 logout_sfx=".${path##*.}"
994 # echo ${name_str}=${!name_str}
995 __logfile_name_fix
996 # echo ${name_str}=${!name_str}
997 path=${!path_str}/${!name_str}
1000 # use $dbgout_sfx as default surfix for dbgout channel.
1001 if [[ $name == "dbgout" && ! ${!name_str##*/} =~ "." && ! -c $path && ! -b $path ]]; then
1002 declare -g ${name_str}="${!name_str}$dbgout_sfx"
1003 path="$path$dbgout_sfx"
1006 # if the ext-name equal to $dbgout_sfx, it is opened as a pipe file.
1007 if [[ ".${path##*.}" == "$dbgout_sfx" ]]; then
1008 [[ -f $path && ! -p $path ]] && echo -ne "[failed] init_channel() failed, non pipe file \"$path\" has been existing.\n" >&2 && return 3
1009 [[ ! -e $path ]] && mknod -m 622 ${path} p 2>&1 > /dev/null
1012 # echo path=$path
1013 # others is normal file, run touch to create file.
1014 touch ${path} 2>/dev/null
1015 [[ $? != 0 ]] && echo -ne "[failed] init_channel() failed, cannot open file \"$path\". check if permission allowed.\n" >&2 && return 4
1017 # open it by a idle fd value.
1018 fd=$(__get_file_top_valid_id)
1019 eval "exec $fd<>$path"
1020 [[ $? != 0 ]] && echo -e "err: init_channel() failed, cannot open \"${!name_str}\" as a write file for channel $name." >&2 && return 5
1022 # save log file name when channel is valid.
1023 if [[ $name == "logout" ]]; then
1024 : #f_logout=$path
1025 else
1026 # echo name=$name
1027 eval vch_$name=$fd
1029 else
1030 if [[ $name == "logout" ]]; then
1031 warn "can not use pure digital number as a log filename. use the file handle instead.\n"
1032 f_logout=$(readlink /proc/self/fd/$path)
1033 return 0
1037 # use directly by file handler paramter instead of file name.
1039 fd=$path
1041 if [[ ! -e /proc/self/fd/$path ]]; then
1042 echo -e "err: init_channel() failed, file handle \"$path\" is not opened." >&2
1043 return 7
1045 if [[ ! -w /proc/self/fd/$path ]]; then
1046 echo -e "err: init_channel() failed, file handle \"$path\" cannot be write." >&2
1047 return 8
1050 declare -g vch_$name="$fd"
1051 else
1052 warn "specify a valid output channel name in \"dbgout logout progstdin progstdout progstderr\" by param.\n"
1057 # fsyntax: init_dbglogout <pipe_file> <log_file> <max_lines>
1058 # fdesc: init dbgout and logout channel, init log max line limit.
1059 # specify '-' paramter to use default value.
1061 init_dbglogout ()
1063 local pipe=$1
1064 local logfile=$2
1066 # init variable in stdio.shlib every program.
1067 # stdio_shlib_init
1070 # there are some different example of using:
1071 # @ cmd program, use stderr as dbgout channel, use global log path for logging.
1072 # @ deamon or app running continuously, use pipe as dbgoutchannel in global path.
1073 # and also log data in global path.
1074 # @ some times, we need to output data in current file, append ":" prefix before
1075 # specified name.
1076 # @ and sometimes, there are many process of program, and we need to output data
1077 # with different file name, paaend "+" prefix.
1080 if [[ -z $pipe || $pipe == '-' ]]; then
1081 pipe=2
1082 else
1083 case ${pipe:0:1} in
1084 ':' )
1085 pipe="$PWD/${pipe:1}"
1087 '+' )
1088 pipe="dbgout-$$-${pipe:1}"
1090 esac
1093 if [[ -z $logfile || $logfile == '-' ]]; then
1094 logfile=$logout_file_name
1095 else
1096 case ${logfile:0:1} in
1097 ':' )
1098 logfile="$PWD/${logfile:1}"
1100 '+' )
1101 logfile="log-$$-${logfile:1}"
1103 esac
1106 if [[ -n $3 || $3 != '-' ]]; then
1107 max_log_lines=$(($3))
1109 if [[ -z $max_log_lines ]]; then
1110 max_log_lines=20000
1113 init_channel dbgout $pipe # $dbgout_file_name
1114 init_channel logout $logfile # $logout_file_path/$logout_file_name
1116 # log_row_cnt=0
1117 # log_row_cnt=$(wc -l $f_logout | cut -d " " -f1)
1119 dbgset enable dbgout
1120 dbgset enable 0
1121 dbgset enable 1
1122 dbgset enable 2
1123 dbgset enable 3
1124 dbgset enable 4
1125 dbgset enable 5
1126 dbgset enable hilight
1128 return
1132 #libend
1135 ##############################
1136 # section: file tail
1137 ##############################