7 typeset
-a MENU_STACK
# A stack of MENUS to store the previously visited menus
9 # Array of menu characteristics for caching purpose
10 # '''''''''''''''''''''''''''''''''''''''''''''''''
13 typeset
-A CENTERING_ARRAY
14 typeset
-A TITLE_ARRAY
15 typeset
-A ERASE_ARRAY
18 SMENU
= # The smenu path will be stored here
20 # ============================ #
21 # Usage function, always fails #
22 # ============================ #
25 echo "Usage: $PROG menu_file[.mnu] user_program," >&2
26 echo " read the README for an example" >&2
30 # ==================== #
31 # Fatal error function #
32 # ==================== #
39 # The script expects exactly one argument (the filename of the root menu).
40 # """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
41 (( $# != 2 )) && usage
45 # ======================================================================= #
46 # Parse a level in the menu hierarchy and call process with the selection #
48 # ======================================================================= #
51 TITLE
=$
'\n'"[ENTER: select, q: abort]" # Untitled by default
52 CENTERING
="-M" # Is the menu centered in the screen ?, defaults to yes
53 ERASE
="-d" # Destroy the selection window after use. Defaults to yes
56 MENU
= # Make sure the working area is empty
58 # If the menu has already been seen, read its characteristics from the cache
59 # else construct them.
60 # """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
61 if [[ -z ${MENU_ARRAY[$MENU_FILE]} ]]; then
64 # Parse the directives embedded in the menu file
65 # """"""""""""""""""""""""""""""""""""""""""""""
66 MENU_DIRECIVES
=$
(grep '^\.' $MENU_FILE)
68 while read DIRECTIVE VALUE
; do
70 .columns
) # Number of columns of the menu
72 (( COL
< 1 || COL
> 15 )) && error
"$DIRECTIVE, bad value"
75 .centered
) # Is the menu centered?
76 [[ $VALUE == no
]] && CENTERING
=""
79 .eraseafter
) # Will the space used by the menu be reclaimed?
80 [[ $VALUE == no
]] && ERASE
=""
83 .title
) # The menu title
84 TITLE
="$VALUE"$
'\n'$TITLE
88 error
"bad directive $DIRECTIVE"
91 done <<< "$MENU_DIRECIVES"
93 # Build the menu entries in the working area
94 # """"""""""""""""""""""""""""""""""""""""""
95 MENU_LINES
=$
(grep -v -e '^\.' -e '^#' -e '^[ \t]*$' $MENU_FILE)
97 # The special tag "---" creates an empty entry (a hole in a column)
98 # The special tag "===" create an empty line
99 # The special tag "EXIT" permit to exit the menu
100 # '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
102 while read TAG VALUE
; do
103 ITEMS_TO_ADD
=1 # By default, only one iteration of the tag is taken
107 && error
"Menu tag \"${TAG}\" too long (max 30 characters)."
109 [[ $TAG == --- ]] && VALUE
=@@@
110 [[ $TAG == === ]] && VALUE
=@@@
&& ITEMS_TO_ADD
=COL
112 [[ -z $VALUE ]] && error
"Empty menu entry for $TAG"
113 [[ $TAG == EXIT
]] && TAG
="@EXIT@xxxxxxxxxxxxxxxxxxxxxxxx00"
114 [[ $TAG == "<"* ]] && TAG
="<xxxxxxxxxxxxxxxxxxxxxxxxxxxxx00"
116 # Protect quotes in VALUE
117 # """""""""""""""""""""""
118 FINAL_VALUE
=$
(echo "$VALUE" |
sed -e 's/"/\\"/' -e "s/'/\\\\'/")
120 while (( ITEMS_TO_ADD--
> 0 )); do
121 MENU
+="'$TAG $FINAL_VALUE'"$
'\n'
123 done <<< "$MENU_LINES"
127 MENU_ARRAY
[$MENU_FILE]="$MENU"
128 COL_ARRAY
[$MENU_FILE]=$COL
129 CENTERING_ARRAY
[$MENU_FILE]=$CENTERING
130 TITLE_ARRAY
[$MENU_FILE]=$TITLE
131 ERASE_ARRAY
[$MENU_FILE]=$ERASE
133 # Read from the cache
134 # """""""""""""""""""
135 MENU
="${MENU_ARRAY[$MENU_FILE]}"
136 COL
=${COL_ARRAY[$MENU_FILE]}
137 CENTERING
=${CENTERING_ARRAY[$MENU_FILE]}
138 TITLE
=${TITLE_ARRAY[$MENU_FILE]}
139 ERASE
=${ERASE_ARRAY[$MENU_FILE]}
142 # Display the menu and get the selection
143 # """"""""""""""""""""""""""""""""""""""
144 SEL
=$
(echo "$MENU" |
$SMENU \
159 # Check for the presence of smenu
160 # """""""""""""""""""""""""""""""
161 SMENU
=$
(which smenu
2>/dev
/null
)
162 [[ -x "$SMENU" ]] || error
"smenu is not in the PATH or not executable."
164 # Initialize the menu stack with the argument
165 # """""""""""""""""""""""""""""""""""""""""""
166 MENU_STACK
+=(${1%.mnu}.mnu
)
168 # And process the main menu
169 # """""""""""""""""""""""""
170 process_menu
${1%.mnu}.mnu
172 # According to the selection, navigate in the submenus or return
173 # the tag associated with the selected menu entry.
174 # """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
176 if [[ $SEL == "<"* ]]; then
177 # Back to the previous menu
178 # '''''''''''''''''''''''''
179 if (( ${#MENU_STACK[*]} == 1 )); then
180 process_menu
${MENU_STACK[${#MENU_STACK[*]}-1]}
182 # Unstack the newly found submenu
183 # '''''''''''''''''''''''''''''''
184 unset MENU_STACK
[${#MENU_STACK[*]}-1]
186 # And generate the previous menu
187 # ''''''''''''''''''''''''''''''
188 process_menu
${MENU_STACK[${#MENU_STACK[*]}-1]}
191 elif [[ $SEL == ">"* ]]; then
192 # Enter the selected submenu
193 # ''''''''''''''''''''''''''
195 SMENU_FILE
=${SMENU_FILE%.mnu}.mnu
196 [[ -f $SMENU_FILE ]] || error
"The file $SMENU_FILE was not found/readable."
198 # Stack the newly found submenu
199 # '''''''''''''''''''''''''''''
200 MENU_STACK
+=($SMENU_FILE)
202 # And generate the submenu
203 # ''''''''''''''''''''''''
204 process_menu
$SMENU_FILE
206 # An empty selection means than q or ^C has been hit
207 # ''''''''''''''''''''''''''''''''''''''''''''''''''
208 [[ -z $SEL ]] && exit 0
210 # Output the selected menu tag or exit the menu without outputting
212 # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
213 [[ $SEL == @EXIT@
* ]] && exit 0
215 # Launch the user action which has the responsibility to act according
216 # to the tag passed as argument.
217 # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
220 # And re-generate the current menu
221 # ''''''''''''''''''''''''''''''''
222 process_menu
${MENU_STACK[-1]}