code repo connnect test
[shlib.git] / shlib / args.shlib
blob08232585a2ca283d1f8062bcc5ca6a38afe6f53d
1 #!/bin/bash
2 ############################################################
3 # source: args.shlib
4 # author: devenkong(18151155@qq.com)
5 # date: 2021-09-16
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 # it is a shlib for processing of program arguments.
14 # there are several part of args.shlib for args processing.
15 # @ several args desc-str-line. it describe a cmdline optoin
16 # by string syntax.
17 # @ desc-str dispatching result vars. it's the env var generated
18 # in desc-str-line processing. and it's used for actual args
19 # dispatching.
20 # @ action function. it is included in desc-str-line for the
21 # option. when this option apeard in args, this function will
22 # be invoked to implement corresponding feature. if a option
23 # bring with a paramter arg, it would be the first paramter
24 # of action function. if the action function is not used by
25 # programer, use the env var defined in desc-str. when the
26 # value is 'enable', it means this option is used in cmdline.
27 # it's usefull for multi paramter processing.
28 # @ paramter helper. it's a part of usage string. invoking
29 # helper_gen to output the string.
30 # it's a usefull feature for developer on args processing.
31 # and the developer get benifit by using this shlib is that:
32 # @ just care about option name, and the output resualt of
33 # vars and action functions and desc-info. those info is
34 # associate by this shlib automatically.
35 # @ in normal developping, programer is writing code for feature
36 # implement, and append helper info at last. it may be cause
37 # a problem that the option implement is not syncronized to
38 # code implement. in desc-str-line, the paramter and implement
39 # action function can be seen directly by developer.
40 ############################################################
43 include stdio.shlib
44 include dbgout.shlib
48 # todo:
49 # @ insert dispathed var into src file.
50 # @ use imi file instead of var define in src file.
51 # @ cmdline hint append.
52 # @ subcmd
53 # @ option mutex.
57 # @ the processing of description string bundary implemented by fold.
58 # but it works only in english. make some patch for it, and let it
59 # working in utf-8 charset.
60 # @
64 # @ the right side limit will truncate the words in desc string.
65 # impove with fold. english words would not be truncated.
66 # @ append blank line display desc-str-line "|blank" in desc-str.
67 # @ add string after 'blank', and displayed for option category.
68 # @ multiple desc-str.
69 # @ dispatch debug info output by --debug option, or DBGOUTD_OUTPUT=1.
70 # @ the old version only dispaly strings without blank. now it can display
71 # english string with blanks.
74 # this cmd enable alias feature in script. it is disabled in default.
75 shopt -s expand_aliases
78 ##############################
79 # section: public comment info
80 ##############################
84 ##############################
85 # section: variable define
86 ##############################
89 # example:
90 # usage_desc_str="
91 # prog $0 '<prog_desc>'
92 # param -<pchar> --<long-opt> ---<cmd-opt> =<value> %<pname> !<pproc> '<pdesc-str>'
93 # "
96 # member variable of option paramter var.
97 p_prog=
98 p_progdesc=
100 # vars for getopt.
101 p_shortparam=
102 p_longparam=
103 p_cmdparam=
106 p_pcnt=
108 # variable of dispatched paramter describe string
110 # those variable is used as an array.
111 # if it is a digital indexed array, it must be the variable used to store dispatched paramter.
112 # if it is a name string indexed array, it must be the variable updated by runtime arguments.
114 p_char=
115 p_longopt=
116 p_cmd=
117 p_value=
118 p_name=
119 p_proc=
120 p_desc=
122 # array of shell script can not use string indexed array mixed with array.
123 # before declare an array, unset it first.
124 # TBD: this code just running at once, when multiple desc-str is used.
125 unset p_char2idx
126 unset p_longopt2idx
127 unset p_cmd2idx
128 # not used currently.
129 unset p_name2idx
131 declare -g -A -x p_char2idx
132 declare -g -A -x p_longopt2idx
133 declare -g -A -x p_cmd2idx
134 declare -g -A -x p_name2idx
136 export p_char2idx
137 export p_longopt2idx
138 export p_cmd2idx
139 export p_name2idx
141 declare -g -a actionlist
145 # option desciption framework for helper.
146 # -f, --file=<file-path> name paramter string. use this
147 # paramter as the operating file.
148 # |<<-1->|--------- 2 -------->|<-3->|<----- 4 ---—->|
149 # |<---------------------------- 5 ----------------------------->|
151 # 1:pname_blanks. blanks befor paramter name string.
152 pname_blanks=${pname_blanks:-1}
153 # 2:pname_width. paramter string width, and blank prefix width.
154 pname_width=30
155 # 3:desc_blanks. blanks before paramter describe string.
156 desc_blanks=1
160 # others.
162 helper=
165 ##############################
166 # section: private function
167 ##############################
169 __get_term_width ()
171 local f_ttyout=$(ps -p $$ -o tty | tail -n 1 | cut -d ' ' -f1 2>/dev/null)
173 [[ -n $f_ttyout ]] && export term_width=$(stty -F /dev/$f_ttyout size 2>&1 | cut -d ' ' -f2 | tr -d "'")
175 echo $term_width
177 unset f_ttyout
181 # term display width
182 # get term width, to generate usage string adepted to screen size.
183 # when stdout is not a tty device, set a default value.
185 # this paramter is defined in other shlib
187 if [[ -z $term_width ]]; then
188 term_width=$(__get_term_width)
189 if [[ ! "$term_width" =~ [^0-9] ]]; then
190 args_ttydev=$(ps -p $$ -o tty | tail -n 1 | cut -d ' ' -f1)
192 # args_ttydev=$(ps -ajx | cut -d ' ' -f2 | grep -i "`ps -ajx | grep -i "$$" | cut -d ' ' -f4 | head -n 1`" | cut -d ' ' -f5)
194 [[ -z $args_ttydev ]] && args_ttydev=pts/0
195 args_ttydev="/dev/$args_ttydev"
196 export term_width=$(stty -F $args_ttydev size 2>&1 | cut -d ' ' -f2)
197 unset args_ttydev
199 if [[ $term_width =~ "[a-zA-Z]" ]]; then
200 export term_width=100
202 #else
203 # dbgoutd "defined term_width=$term_width\n"
207 # copy dbgout code to here, this shlib can be used indepently.
210 if [[ ! "$(type dbgout 2>&1)" =~ "is a function" && ! "$(type dbgout 2>&1)" =~ "is aliased" ]]; then
212 #dbgout_file=
213 #log_file=
215 dbgout_file=${dbgout_file:-&2}
216 log_file=${log_file:-/dev/null}
219 # 输出调试信息到管道文件和日志文件中
220 # dbgout <param-list>
221 dbgout ()
223 echo
224 echo -ne "$@" | tee -a $log_file >&2 #>$dbgout_file
229 # re-define here to disable dbgoutd.
230 # declare -g -x DBGOUTD_OUTPUT=1
231 # set DBGOUTD_OUTPUT to 1 as an exported global var, the dbgout info will be outputted.
232 # it's usefull for new option implement, especially in test code..
234 dbgoutdd ()
236 if [[ -n $DBGOUTD_OUTPUT ]]; then
237 dbgoutd "$@"
242 ###########################################
243 # helper string output by dispatched vars.
246 # paramter descript string display is not a complex thing.
247 # there is something should be pay attension on, that the lenth
248 # limit on the right side. so we sould get the string lenth/width
249 # to limit it.
250 # there are three type of char, and the corresponding lenth
251 # and display width.
252 # @ ascii char, it takes one byte, and use one unit of display char.
253 # @ gbk char, it takes two byte, and use two unit of display char.
254 # @ utf8 char, it may takes three byte, and use two unit of display
255 # char.
256 # the problem is, if the char type is mixed with those three type,
257 # how to get the display char width?
258 # ascii & gbk char is equal to the byte size of char. utf8 should
259 # be translated to gbk encode. then, those three type of char display
260 # width is equal to byte lenth of char.
261 # invoke "iconv -f utf-8 -t GBK", translate utf8 to gbk, calculate
262 # byte lenth, then convert back to utf8 encode. the byte size that we
263 # getted is the display char width.
267 # fsyntax: __oneline_desc_output <param-str-len> <width> <idx>
268 # fdesc: output single line option description string.
270 __oneline_desc_output_bak ()
272 local len=
273 local cnt=
274 local tmp=
275 local size=
277 offset=${offset:-0}
279 [[ $offset -ge ${#p_desc[$3]} ]] && return 0
281 # it's the length of string byte, but string length is the string char count.
282 len=$(( term_width - pname_width - desc_blanks ))
284 printf "%*s" $(( pname_width - $1 + desc_blanks )) " "
287 # todo: this cmd coast more cpu resource.
289 tmp=`echo -ne "${p_desc[$3]:$offset:$len}" | iconv -f utf-8 -t GBK 2> /dev/null | cut -c 1-$len | iconv -f GBK -t utf-8 2> /dev/null`
290 # tmp=`echo -ne "${p_desc[$3]:$offset:$len}" | cut -c 1-$len`
291 [[ -z $tmp ]] && return 0
293 cnt=`echo -ne "$tmp" | wc -c`
294 size=${#tmp}
295 tmp=`echo -ne "$tmp" | fold -b -s -w $cnt - 2> /dev/null`
297 echo -ne "$tmp\n"
298 offset=$(( offset + size ))
300 return 1
303 # improve for words bundary truncating.
304 __oneline_desc_output ()
306 local len=
307 local cnt=
308 local tmp=
309 local size=
311 offset=${offset:-0}
313 [[ $offset -ge ${#p_desc[$3]} ]] && return 0
315 # it's the length of string byte, but string length is the string char count.
316 len=$(( term_width - pname_width - desc_blanks ))
318 printf "%*s" $(( pname_width - $1 + desc_blanks )) " "
321 # todo: this cmd cost more cpu resource.
323 # $len is the dispaly width of description string area.
324 # first, traslate it to gbk, let the byte size equal to dispaly char width, and cut $len char that equal
325 # to the width of dispaly area, and get the byte $cnt. in utf-8 envronment, it maybe cut a double or triple
326 # byte char, and it will leading error dispaly. so $len is not the truncate length it should be. use echo
327 # cmd to output string again, it will filer the uncorrect char that may be truncated by 'cut', and count
328 # the bytes of string, it's the actual byte cnt we should use for 'fold' to cut the string.
329 tmp="`echo -ne "${p_desc[$3]:$offset}" | iconv -f utf-8 -t GBK 2> /dev/null | cut -c 1-$len`"
330 cnt=`echo -ne "$tmp" | wc -c`
332 # then, do tralate operation again, and intert with a fold cmd to cut it by $cnt size, get the first line
333 # of the string, that's the string should be dispalyed on screen.
334 # this method compactive with utf-8 char dispaly, and english words bundary truncate. but it cost a bit more
335 # cpu rate.
336 tmp="`echo -ne "${p_desc[$3]:$offset}" | iconv -f utf-8 -t GBK 2> /dev/null | fold -b -s -w $cnt - 2> /dev/null | iconv -f GBK -t utf-8 2> /dev/null`"
337 tmp="${tmp%%
340 [[ -z $tmp ]] && return 0
342 # get the char cnt of tmp that to be dispalyed
343 size=${#tmp}
345 echo -ne "$tmp\n"
346 offset=$(( offset + size ))
348 return 1
352 # fsyntax: __rest_desc_output <width> <idx>
353 # fdesc: output multi-line string. if the first line is outputed, it
354 # display the rest string.
356 __rest_desc_output ()
358 local i=
360 # the string length of env var is the char number of string.
361 # the string size of env is the buffer byte count of string.
362 # if it is a gbk char string, 2 byte size equal to 2 char width.
363 # calculate string display width is count the byte of string transformed from
364 # utf-8 to gbk.
365 # len=$(( $term_width - $pname_blanks - $pname_width - $desc_blanks ))
367 for (( i=0; i<200; i++ )); do
368 __oneline_desc_output 0 $1 $2
369 [[ $? == 0 ]] && break
370 done
372 [[ $i == 200 ]] && err "err: $FUNCNAME(line$LINENO) loop exception.\n" && exit
375 ############################################################
376 # argument describe string and dispatching
377 # using regex to match string, instead of others.
378 # TBD: it can not be executed under non-bash shell program.
381 str_dispatch ()
383 local fmt
385 # [[ "${abc}" =~ \|([^|]+)\ \|-([^|]*)\ \|--([^|]*)\ \|---([^|]*)\ \|=[\<\|\[]?([^|]*)[\>\|\]]? ]] && echo ${BASH_REMATCH[@]}
386 # |param |- |--logfile |--- |= | param logfile
388 # \|([^|]+)\ \|-([^|]*)\ \|--([^|]*)\ \|---([^|]*)\ \|=[\<|\[]?([^\|\>]*)[\>|\]?\ \|%[\<|\[]?([^\|\>]*)[\>|\]?\ \|=[\<|\[]?([^\|\>]*)[\>|\]?[\>|\]]?\ \|[\']?([^\|\>]*)[\']?
390 # it's a fixed syntax for desc-str-line.
391 fmt="\|([^|]+)\ \|-([^|\ ]*)?\ \|--([^|\ ]*)?\ \|---([^|\ ]*)?\ \|=([\<|\[]?[^\|\>\ ]*[\>|\]?)?\ [\|]%[\<|\[]?([^\|\>\ ]*)?[\>|\]?\ \|&[\<|\[]?([^\|\>\ ]*)?[\>|\]?\ \|[']?([^\']*)?"
393 if [[ "${1}" =~ $fmt ]]; then
394 unset param
395 declare -g -a param
396 for ((x=0; $x < ${#BASH_REMATCH[@]}; x++)); do
397 param[$x]="${BASH_REMATCH[$x]}"
398 done
399 elif [[ "${1}" =~ ^[:blank:]*[\|](prog) ]]; then
400 unset param
401 declare -g -a param
402 param[0]="${BASH_REMATCH[0]}"
403 param[1]="${BASH_REMATCH[1]}"
404 elif [[ "${1}" =~ ^[:blank:]*[\|](blank)[\ ]*[\']?([^\']*)?[\']? ]]; then
405 unset param
406 declare -g -a param
407 param[0]="${BASH_REMATCH[0]}"
408 param[1]="${BASH_REMATCH[1]}"
409 param[2]="${BASH_REMATCH[2]}"
410 else
411 unset param
412 declare -g -a param=""
419 blank_param_setting ()
421 p_char[$idx]=""; p_char2idx["${param[2]:0:1}"]=""
422 p_longopt[$idx]=""; p_longopt2idx["${param[3]}"]=""
423 p_cmd[$idx]=""; p_cmd2idx["${param[4]}"]=""
425 p_char[$idx]=""
426 p_char[$idx]=""
427 p_char[$idx]=""
430 desc_param_setting ()
432 [[ ! -v param ]] && return
434 [[ -n ${param[2]} ]] && p_char[$idx]=${param[2]:0:2} && p_char2idx["${param[2]:0:1}"]=$idx
435 [[ -n ${param[3]} ]] && p_longopt[$idx]=${param[3]} && p_longopt2idx["${param[3]}"]=$idx
436 [[ -n ${param[4]} ]] && p_cmd[$idx]=${param[4]} && p_cmd2idx["${param[4]}"]=$idx
438 # paramter value
439 if [[ "${param[5]:0:1}" == "<" ]]; then
440 p_char[$idx]+=":"
441 elif [[ "${param[5]:0:1}" == "[" ]]; then
442 p_char[$idx]+="::"
443 else
444 param[5]=""
446 param[5]=${param[5]:1:-1}
447 [[ -n ${param[5]} ]] && p_value[$idx]="${param[5]}"
449 # option name, proc function
450 [[ -n ${param[6]} ]] && p_name[$idx]=${param[6]} && p_name2idx["${param[6]}"]=$idx
451 [[ -n ${param[7]} ]] && p_proc[$idx]=${param[7]}
453 # desc string
454 [[ -n ${param[8]} ]] && p_desc[$idx]="${param[8]}"
456 dbgoutdd "============================================\n"
457 tmp=${p_char[$idx]}
458 dbgoutdd "p_char[$idx]='$tmp'\n"
459 tmp=${p_char[$idx]:0:1}
460 [[ -n "$tmp" ]] && dbgoutdd "p_char2idx[$tmp]='${p_char2idx[$tmp]}'\n"
461 tmp=${p_longopt[$idx]}
462 dbgoutdd "p_longopt[$idx]='${tmp}'\n"
463 [[ -n "$tmp" ]] && dbgoutdd "p_longopt2idx[$tmp]='${p_longopt2idx[$tmp]}'\n"
464 tmp=${p_cmd[$idx]}
465 dbgoutdd "p_cmd[$idx]='${tmp}'\n"
466 [[ -n "$tmp" ]] && dbgoutdd "p_cmd2idx[$tmp]='${p_cmd2idx[$tmp]}'\n"
467 dbgoutdd "p_value[$idx]='${p_value[$idx]}'\n"
468 tmp=${p_name[$idx]}
469 dbgoutdd "p_name[$idx]='${tmp}'\n"
470 [[ -n "$tmp" ]] && dbgoutdd "p_name2idx[$tmp]='${p_name2idx[$tmp]}'\n"
471 dbgoutdd "p_proc[$idx]='${p_proc[$idx]}'\n"
472 dbgoutdd "p_desc[$idx]='${p_desc[$idx]}'\n"
474 declare -l OPT_PFX=" "
476 len=0
477 if [[ -n "${p_char[$idx]}" ]]; then
478 content+="-${p_char[$idx]:0:1}"
479 p_shortparam+="|${p_char[$idx]}"
480 len=$(( $len + 2 ))
481 OPT_PFX=", "
482 else
483 OPT_PFX=" "
486 [[ $(( $len + ${#p_cmd[$idx]} + 2 )) -gt $pname_width ]] && content+="$(printf '\n%*s'  $pname_blanks ' ')" && len=0
487 if [[ -n "${p_cmd[$idx]}" ]]; then
488 content+="${OPT_PFX}${p_cmd[$idx]}"
489 p_cmdparam+="|${p_cmd[$idx]}"
490 len=$(( $len + ${#p_cmd[$idx]} + 2 ))
491 OPT_PFX=", "
494 [[ $(( $len + ${#p_longopt[$idx]} + 4 )) -gt $pname_width ]] && content+="$(printf '\n%*s'  $pname_blanks ' ')" && len=0
495 [[ -n "${p_longopt[$idx]}" ]] && content+="${OPT_PFX}--${p_longopt[$idx]}" && p_longparam+="|${p_longopt[$idx]}${p_char[$idx]:1}" && len=$(( $len + ${#p_longopt[$idx]} + 4 ))
497 [[ $(( $len + ${#p_value[$idx]} + 3 )) -gt $pname_width ]] && content+="$(printf '\n%*s'  $pname_blanks ' ')" && len=0
498 [[ -n "${p_value[$idx]}" ]] && content+="=<${p_value[$idx]}>" && len=$(( $len + ${#p_value[$idx]} + 3 ))
503 ##############################
504 # section: public function
505 ##############################
508 # fsyntax: OptDescParamPrint
509 # fdesc: output environment variable of dispatched option describe string.
511 OptDescParamPrint ()
513 dbgout "##########################\n"
514 dbgout "p_prog=$p_prog\n"
515 dbgout "p_progdesc=$p_progdesc\n"
517 dbgout "p_shortparam=$p_shortparam\n"
518 dbgout "p_longparam=$p_longparam\n"
519 dbgout "p_cmdparam=$p_cmdparam\n"
521 dbgout "p_pcnt=$p_pcnt\n"
523 for (( i=0; i<$p_pcnt; i++ )); do
524 dbgout "# [$i]\n"
525 dbgout " p_char[$i]=${p_char[$i]}\n"
526 dbgout " p_longopt[$i]=${p_longopt[$i]}\n"
527 dbgout " p_cmd[$i]=${p_cmd[$i]}\n"
528 dbgout " p_value[$i]=${p_value[$i]}\n"
529 dbgout " p_name[$i]=${p_name[$i]}\n"
530 dbgout " p_proc[$i]=${p_proc[$i]}\n"
531 dbgout " p_desc[$i]=\"${p_desc[$i]}\"\n"
532 done
534 dbgout "pname_width=$pname_width\n"
535 dbgout "pname_blanks=$pname_blanks\n"
536 dbgout "desc_blanks=$desc_blanks\n"
538 dbgout "term_width=$term_width\n"
539 opt_helper
540 dbgout "##########################\n"
543 ########################################
544 # main feature of args.shlib:
545 # @ dispatching desc-str to env vars which has a pfx 'p_'.
546 # @ resovle run time program args by 'p_*' env vars, set corresponding var value,
547 # and invoke action proc function.
548 # @ generate helper string by 'p_*' env vars
552 # fsyntax: helper_gen
553 # fdesc: generate helper string by paramter, and output it to the variable which
554 # specified in paramter. invoke this function before opt_helper() dispaly helper.
556 helper_gen ()
558 local i
559 local acnt
560 local data
561 local param
562 local len
563 local maxlen
564 local output
566 acnt=$idx #${#p_char[@]}
567 offset=0
568 for (( i=0; $i <= $acnt; i++ )); do
569 len=0
570 offset=0
572 # no description is seems as a blank line tag.
573 [[ -z "${p_desc[$i]}" ]] && echo && continue
575 # this is a category description string.
576 [[ -z "${p_char[$i]}" && -z "${p_longopt[$i]}" && -z "${p_cmd[$i]}" ]] && echo -ne "${p_desc[$i]}\n" && continue
578 printf "%*s" $pname_blanks " "
579 len=$pname_blanks
581 # if [[ "$OPT_PFX" == " " ]]; then
582 declare -l OPT_PFX=" "
585 # echo "-n"
586 # echo "-e"
587 # this two cmd display nothing on screen.
588 # this string is filtered automatically, it is treated as the paramter for echo.
589 # echo "-ea"
590 # but this is ok.
591 # so output this string by two step. one is '-', then is 'n' or 'e'
592 if [[ -n "${p_char[$i]:0:1}" ]]; then
593 echo -ne "-"
594 echo -ne "${p_char[$i]:0:1}"
595 OPT_PFX=", "
596 else
597 echo -ne " "
599 len=$(( $len + 2 ))
601 if [[ -n ${p_cmd[$i]} && $(( $len + ${#p_cmd[$i]} + 2 )) -gt $pname_width ]]; then
602 __oneline_desc_output $len $pname_width $i
603 printf "%*s" $pname_blanks " "
604 len=$pname_blanks
606 if [[ -n ${p_cmd[$i]} ]]; then
607 echo -ne "${OPT_PFX}${p_cmd[$i]}"
608 len=$(( $len + ${#p_cmd[$i]} + 2 ))
609 OPT_PFX=", "
612 if [[ $(( $len + ${#p_longopt[$i]} + 4 )) -gt $pname_width ]]; then
613 __oneline_desc_output $len $pname_width $i
614 printf "%*s" $pname_blanks " "
615 len=$pname_blanks
617 [[ -n ${p_longopt[$i]} ]] && echo -ne "${OPT_PFX}--${p_longopt[$i]}" && len=$(( $len + ${#p_longopt[$i]} + 4 ))
619 if [[ $(( $len + ${#p_value[$i]} + 3 )) -gt $pname_width ]]; then
620 __oneline_desc_output $len $pname_width $i
621 printf "%*s " $pname_blanks " "
622 len=$(( pname_blanks + 4 ))
624 if [[ ${p_char[$i]:1} == "::" ]]; then
625 [[ -n ${p_value[$i]} ]] && echo -ne "=[${p_value[$i]}]" && len=$(( $len + ${#p_value[$i]} + 3 ))
626 else
627 [[ -n ${p_value[$i]} ]] && echo -ne "=<${p_value[$i]}>" && len=$(( $len + ${#p_value[$i]} + 3 ))
630 [[ $offset -ge ${#p_desc[$i]} ]] && echo && continue
632 __oneline_desc_output $len $pname_width $i
634 # output rest lines.
635 __rest_desc_output $pname_width $i
636 done
640 # fsyntax: opt_helper <output-var>
641 # fsyntax: generate helper string by the dispatched var.
642 # if <output-var> is '-', output to stdout instead of var.
644 opt_helper ()
646 local tmp=$(__get_term_width)
648 if [[ -n $tmp && $term_width -gt "$tmp" || -z $helper ]]; then
649 term_width=$tmp
650 helper="`helper_gen`"
653 # TBD: iconv here at a time to save time coast.
655 [[ -n "$helper" ]] && echo "$helper" # | iconv -f GBK -t utf-8 -c -s 2>/dev/null
656 else
657 [[ -n "$helper" ]] && echo "$helper" # | iconv -f GBK -t utf-8 -c -s 2>/dev/null
661 # TBD: init the value to 0 if first init.
662 # it is defined as a global var, used for multiple desc-str dispathing.
663 declare -g idx=0
666 # fsyntax: opt_desc_str_dispatch <desc_str_var_name>
667 # fdesc: description string dispatch to p_* variables.
669 opt_desc_str_dispatch ()
671 local i
672 local ARGS="\$$1"
673 local tmp
674 local content
675 local desc=
676 local cnt
677 local opt
678 local longopt
679 local subcmd
680 local width
681 local tmpfile=
683 # resolve command line descript string and save to p_ variables.
684 IFS_OLD="$IFS"
685 IFS="@"
686 ARGS=( $(eval echo -ne "$ARGS" | tr '\n' '@' | tr -s ' ' ) )
687 IFS="$IFS_OLD"
688 # dbgout "ARGS=${ARGS[@]}\n"
690 width=$pname_width
691 cnt="${#ARGS[@]}"
692 tmp=0
693 [[ $pname_width == 0 ]] && pname_width=$(( ${term_width:-80} / 2 ))
695 #IFS_OLD="$IFS"
697 # TBD: there is a setting of IFS with ' ' here, check if it is used in some statement.
698 #IFS=" "
699 for (( i=1; i<$cnt ; i++ )) do
700 [[ "${ARGS[$i]:0:1}" == '#' ]] && continue
702 # str_dispatch_method2 "${ARGS[$i]}"
703 str_dispatch "${ARGS[$i]}"
705 if [[ ${param[1]} == "prog" ]]; then
706 p_prog=${param[2]}
707 p_progdesc=${param[3]//\'/} #'
708 elif [[ ${param[1]} =~ "blank" ]]; then
709 content+="\n"
710 [[ -n "${param[2]}" ]] && p_desc[$idx]="${param[2]}"
711 idx=$(( idx + 1 ))
712 elif [[ ${param[1]} =~ "param" ]]; then
714 # use string match to implement this code.
716 desc_param_setting
717 # desc_param_setting_method2
719 content+="\n"
720 idx=$(( idx + 1 ))
721 else
722 tmp="${ARGS[$i]//[ |\t]/}"
723 if [[ -z "${tmp}" ]]; then
725 elif [[ "${ARGS[$i]}" =~ [^\ ] ]]; then
726 # if there is no header with 'param' or 'prog'
727 # and it's not a blank line, it's maybe a descript string line
728 # continued from last config data.
729 tmp=${ARGS[$i]}
730 tmp=${tmp%%\'*} #'
732 if [[ -z $tmp || ! $tmp =~ "-" && ! $tmp =~ "=" && ! $tmp =~ "%" && ! $tmp =~ "!" ]]; then
733 tmp="${ARGS[$i]#*\'}"
734 tmp="${tmp%\'*}"
735 p_desc[$(( $idx - 1 ))]+="$tmp"
736 unset p_desc[$idx]
740 i=$((i++))
741 done
743 p_pcnt=$idx
745 # re-generate if pname_width equal to 0.
746 if [[ $width == 0 ]]; then
747 IFS_OLD="$IFS"
748 IFS=$'\n'
749 content=( $content )
750 IFS="$IFS_OLD"
751 tmp=0
752 for (( i=$(( ${#content[@]} - 1 )); i>=0; i-- )); do
753 cnt=${#content[$i]}
754 [[ $tmp -lt "$cnt" ]] && tmp="$cnt"
755 done
757 # update value of pname_width
758 pname_width=$(( $tmp + 1 ))
763 # foo () { ARGS="$(getopt -o lS:y::n:LFraAt:b:e:x:RBscuvqfj:M:d:O:pmgVh -l list,save:,sync::,num:,failed-begin,failed,ignor-err,all,test:,begin:,end:,exclude:,rollup,rollback,set,clean,update,verbose,quiet,force,multi-task:,matching:,dir:,output-dir:,print-vars,mono,logfile,version,help,debug,insert-helper,inc-path,param-desc-str-alian -- "$@")"; echo "ARGS=\"$ARGS\""; eval set -- "$ARGS"; echo param="$@"; echo "\$1='$1'"; echo "\$2='$2'"; for ((i=1; i<=$#; i++)); do eval echo "\\\$$i=\"\${$i}\""; done; }
766 # foo ()
768 # ARGS="$(getopt -o lS:y::n:LFraAt:b:e:x:RBscuvqfj:M:d:O:pmgVh -l list,save:,sync::,num:,failed-begin,failed,ignor-err,all,test:,begin:,end:,exclude:,rollup,rollback,set,clean,update,verbose,quiet,force,multi-task:,matching:,dir:,output-dir:,print-vars,mono,logfile,version,help,debug,insert-helper,inc-path,param-desc-str-alian -- "$@")";
769 # echo "ARGS=\"$ARGS\"";
770 # eval set -- "$ARGS";
771 # echo param="$@";
772 # echo "\$1='$1'";
773 # echo "\$2='$2'";
774 # for ((i=1; i<=$#; i++)); do
775 # eval echo "\\\$$i=\"\${$i}\"";
776 # done;
778 # $ foo -M '<861874> [citem] aqstk "stack and queue data structure with array feature"'
779 # ARGS=" -M '<861874> [citem] aqstk "stack and queue data structure with array feature"' --"
780 # param=-M <861874> [citem] aqstk "stack and queue data structure with array feature" --
781 # $1='-M'
782 # $2='<861874> [citem] aqstk "stack and queue data structure with array feature"'
783 # $1=-M
784 # $2=<861874> [citem] aqstk "stack and queue data structure with array feature"
785 # $3=--
789 # fsyntax: prog_opt_proc <arg-list>
790 # fdesc: process options in cmd arg, save the coressponding flag variable to 'enable', and
791 # append process function to actionlist.
792 # save paramters in cmd arg to actionlist with process function.
793 # at last, invoke functions in actionlist.
795 prog_opt_proc ()
797 local ARGS
798 local short_opt=
799 local long_opt=
800 local cmd_opt=
802 local index=
803 local param=
804 local name=
805 local value=
806 local proc=
808 local i
809 local cnt
811 local general_param=
813 dbgoutdd "=================================================\n"
815 # short option
816 short_opt=${p_shortparam:1}
817 short_opt=${short_opt//|/}
818 dbgoutdd "short_opt = $short_opt\n"
820 # long option
821 long_opt=${p_longparam:1}
822 long_opt=${long_opt//|/,}
823 dbgoutdd "long_opt = $long_opt\n"
825 # for ((i=1; i<=$#; i++)); do eval echo "\$i=\${$i}"; done
828 # paramter format translating.
829 # --long=a => --long a
830 # paramter maybe is a string with blanks in quote,
831 # if $@ directly, quoted string will be treated as several words.
832 # put "$@" to cmd string,
833 # ARGS="`eval $ARGS "$@"`"
834 # do not use eval.
836 ARGS="getopt -o $short_opt -l ${long_opt},insert-helper,inc-path,param-desc-str-alian --"
837 dbgoutdd "getopt cmd: ARGS = $ARGS $@\n"
838 ARGS="`$ARGS "$@"`"
841 # exit if failed
843 if [[ $? != 0 ]]; then
844 err "Fail to get args.\n"
845 exit 1
847 dbgoutdd "get opt resualt: ARGS = $ARGS\n"
849 # trimp arguments
850 eval set -- "${ARGS}"
852 dbgoutdd "ARGS = $@\n"
853 # generate actionlist[] by argument
854 for ((i=0; i < 1000 ; i++)) do
855 value=""
856 index=""
857 dbgoutdd "\$1 = $1\n"
858 case "$1" in
859 -- )
860 dbgoutdd "param --\n"
861 general_param="$@"
862 break
866 # inner func param
868 --insert-helper )
869 dbgout "param_insert_helper()\n"
870 param_insert_helper
871 exit
874 --inc-path )
875 dbgout "param_inc_path()\n"
876 param_inc_path
877 exit
880 --param-desc-str-alian )
881 dbgout "param_param_desc_str_alian()\n"
882 param_param_desc_str_alian
883 exit
886 --* )
887 #param=$1
888 param=${1:2}
889 index=${p_longopt2idx[$param]}
890 [[ -z $index ]] && break
893 -* )
894 # dbgout "$1,$2,$3\n"
895 #param=$1
896 param=${1:1}
897 index=${p_char2idx[$param]}
899 dbgoutdd "=================================================\n"
900 dbgoutdd "option -$param processing ...\n"
901 dbgoutdd "\${!p_char2idx[@]} = ${!p_char2idx[@]}\n"
902 dbgoutdd "\${p_char2idx[@]} = ${p_char2idx[@]}\n"
903 dbgoutdd "\${p_char2idx[\"$param\"]} = ${p_char2idx[$param]}\n"
904 dbgoutdd "param = $param\n"
905 dbgoutdd "index = $index\n"
907 [[ -z $index ]] && break
911 p_param="$@"
912 shift
913 continue
915 esac
917 [[ -z $index ]] && dbgout "err: param ($param) exist, but it's index value not found.\n" && continue
919 # paramter output for debugging
920 dbgoutdd "\$1 = $1\n"
921 dbgoutdd "\$2 = $2\n"
922 dbgoutdd "index = $index\n"
923 dbgoutdd "\${p_value[$index]} = ${p_value[$index]}\n"
925 if [[ -n ${p_value[$index]} ]]; then
926 declare -g ${p_value[$index]}="$2"
927 value=$(eval echo \$${p_value[$index]})
928 else
929 declare -g ${p_name[$index]}="enable"
930 value=""
932 proc=${p_proc[$index]}
934 if [[ ! -z $value ]]; then
935 shift 2
936 else
937 shift
938 value="enable"
941 # option action function list generating.
942 dbgoutdd "value = $value\n"
943 dbgoutdd "proc = $proc()\n"
944 [[ -n $proc ]] && actionlist="$actionlist
945 $proc $value "
946 done
947 # execute paramter proc function.
948 IFS_OLD="$IFS"
949 IFS="
951 actionlist=( $actionlist )
952 IFS="$IFS_OLD"
955 action_list_exec ()
957 local cnt=
959 cnt=${#actionlist[@]}
960 for (( i=0; i < cnt; i++ )); do
961 dbgoutdd "==============================================\n"
962 dbgoutdd "invoke: ${actionlist[$i]}\n"
963 ${actionlist[$i]} $general_param
964 done
965 [[ $i == ${#actionlist[@]} ]] && dbgoutdd "==============================================\n"
969 # TBD:
971 # the code below is reserved, because it's used for optimizing cpu cost in some system
972 # environment.
975 #tmp_usage_desc=
976 # tmp_usage_desc=`echo -ne "${usage_desc_str}" | iconv -f utf-8 -t GBK 2> /dev/null | cut -c 1-$len | iconv -f GBK -t utf-8 2> /dev/null`
977 # [[ -z $tmp_usage_desc ]] && return
980 # fsyntax: prog_opt_dispatch <prog-arg-list>
981 # fdesc: dispatch usage_desc_str to vars, and resolve program args by those
982 # vars.
983 # normally, it is not used when developer want to let the desc-str to be a re-usable
984 # desc-str in another program, which 'source' the program, to use the desc-str.
986 prog_opt_dispatch ()
989 # TBD: translate utf8 to gbk at a time, to avoid iconv invoking every line data.
991 # tmp_usage_desc=`echo -ne "${usage_desc_str}" | iconv -f utf-8 -t GBK 2> /dev/null`
992 # [[ -z $tmp_usage_desc ]] && return
994 opt_desc_str_dispatch usage_desc_str;
996 # do not generate helper string if it is not -h paramter.
997 # it takes more cpu resource.
998 # helper="`helper_gen`"
1000 prog_opt_proc "$@"
1005 ##############################
1006 # section: file tail
1007 ##############################
1014 main_args ()
1016 dbgout "usage_desc_str = $usage_desc_str\n"
1018 # prog_opt_testing
1020 dbgout "#######################################\n"
1022 # 这里的-i参数为可选,但参数是以-ixxx的形式将参数xxx传递到程序的。pp0
1023 # -i xxx的方式不会识别参数。像是gcc中-m参数的使用。
1024 # 也可以-i'xxx'的形式使用。
1025 prog_opt_testing -a --test params aaa -iabc -x 123
1027 dbgout "#######################################\n"
1029 opt_helper
1031 exit
1033 prog_opt_testing -a
1035 dbgout "#######################################\n"
1037 prog_opt_testing --help
1039 opt_helper
1043 #main "@"
1048 # wc方式以字节长度计数,但运行费时,使用字符长度,在英文字母为字符串的参数信息中,等同于字节数。
1049 # if [[ $(( $len + "`echo \"${p_cmd[$i]}\" | wc -L`" + 2 )) -gt $pname_width ]]; then
1058 # parmater list. used for enum paramter var.
1059 param_list=