]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/bsdconfig/dot/dot
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / bsdconfig / dot / dot
1 #!/bin/sh
2 #-
3 # Copyright (c) 2012-2013 Devin Teske
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 # 1. Redistributions of source code must retain the above copyright
10 #    notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 #    notice, this list of conditions and the following disclaimer in the
13 #    documentation and/or other materials provided with the distribution.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 # ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 # SUCH DAMAGE.
26 #
27 # $FreeBSD$
28 #
29 ############################################################ INCLUDES
30
31 # Prevent common.subr from auto initializing debugging (this is not an inter-
32 # active utility that requires debugging; also `-d' has been repurposed).
33 #
34 DEBUG_SELF_INITIALIZE=NO
35
36 BSDCFG_SHARE="/usr/share/bsdconfig"
37 . $BSDCFG_SHARE/common.subr || exit 1
38 f_dprintf "%s: loading includes..." "$0"
39
40 BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="dot"
41 f_include_lang $BSDCFG_LIBE/include/messages.subr
42 f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr
43
44 ipgm=$( f_index_menusel_keyword $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" )
45 [ $? -eq $SUCCESS -a "$ipgm" ] && pgm="$ipgm"
46
47 ############################################################ CONFIGURATION
48
49 #
50 # Location of bsdconfig(8)
51 #
52 BSDCONFIG=/usr/sbin/bsdconfig
53
54 ############################################################ GLOBALS
55
56 #
57 # Options
58 #
59 SHOW_GRAPH_LABEL_DATE=1
60 SHOW_INCLUDES=1
61 SHOW_CMDLINE=1
62
63 ############################################################ FUNCTIONS
64
65 # begin_nodelist $shape $color $fillcolor $style
66 #
67 # Create a new multi-node list rendering nodes in a specific style described by
68 # the arguments passed.
69 #
70 begin_nodelist()
71 {
72         local shape="$1" color="$2" fillcolor="$3" style="$4"
73
74         printf "\tnode [\n"
75         [ "$shape" ] &&
76                 printf '\t\tshape = "%s",\n' "$shape"
77         [ "$color" ] &&
78                 printf '\t\tcolor = "%s",\n' "$color"
79         [ "$fillcolor" ] &&
80                 printf '\t\tfillcolor = "%s",\n' "$fillcolor"
81         [ "$style" ] &&
82                 printf '\t\tstyle = "%s",\n' "$style"
83         printf "\t] {\n"
84 }
85
86 # print_node $node [$attributes ...]
87 #
88 # Print a node within a multi-node list.
89 #
90 print_node()
91 {
92         local node="$1"
93
94         shift 1 # node
95
96         case "$node" in
97         edge) printf '\t\t%s' "$node" ;;
98            *) printf '\t\t"%s"' "$node" ;;
99         esac
100
101         if [ $# -gt 0 ]; then
102                 echo -n ' ['
103                 while [ $# -gt 0 ]; do
104                         printf " %s" "$1"
105                         shift 1
106                         [ $# -gt 0 ] && echo -n ","
107                 done
108                 echo -n " ]"
109         fi
110
111         echo ";"
112 }
113
114 # print_node2 $node $node [$attributes ...]
115 #
116 # Print a directed node-node connection within a multi-node list.
117 #
118 print_node2()
119 {
120         local node1="$1" node2="$2"
121
122         shift 2 # node1 node2
123
124         printf '\t\t"%s" -> "%s"' "$node1" "$node2"
125
126         if [ $# -gt 0 ]; then
127                 echo -n ' ['
128                 while [ $# -gt 0 ]; do
129                         printf " %s" "$1"
130                         shift 1
131                         [ $# -gt 0 ] && echo -n ","
132                 done
133                 echo -n " ]"
134         fi
135
136         echo ";"
137 }
138
139 # end_nodelist
140 #
141 # Close a multi-node list.
142 #
143 end_nodelist()
144 {
145         printf "\t};\n"
146 }
147
148 ############################################################ MAIN
149
150 # Incorporate rc-file if it exists
151 [ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc"
152
153 #
154 # Process command-line arguments
155 #
156 while getopts cdhi flag; do
157         case "$flag" in
158         i) SHOW_INCLUDES= ;;
159         d) SHOW_GRAPH_LABEL_DATE= ;;
160         c) SHOW_CMDLINE= ;;
161         h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm" ;;
162         esac
163 done
164 shift $(( $OPTIND - 1 ))
165
166 cd $BSDCFG_LIBE || f_die 1 "$msg_directory_not_found" "$BSDCFG_LIB"
167
168 #
169 # Get a list of menu programs
170 #
171 menu_program_list=
172 for file in [0-9][0-9][0-9].*/INDEX; do
173         menu_program_list="$menu_program_list $(
174                 tail -r "$file" | awk -v item="${file%%/*}" '
175                         /^[[:space:]]*menu_program="/ {
176                                 sub(/^.*="/, "")
177                                 sub(/"$/, "")
178                                 if ( ! $0 ) next
179                                 if ( $0 !~ "^/" ) sub(/^/, item "/")
180                                 print; exit
181                         }'
182         )"
183 done
184
185 #
186 # Get a list of submenu programs
187 #
188 submenu_program_list=
189 for menu_program in $menu_program_list; do
190         case "$menu_program" in
191         [0-9][0-9][0-9].*/*) : fall-through ;;
192         *) continue # No sub-menus we can process
193         esac
194
195         submenu_program_list="$submenu_program_list $(
196                 awk -v menu_program="$menu_program" \
197                     -v item="${menu_program%%/*}" \
198                 '
199                         /^menu_selection="/ {
200                                 sub(/.*\|/, "")
201                                 sub(/"$/, "")
202                                 if ( ! $0 ) next
203                                 if ( $0 !~ "^/" )
204                                         sub(/^/, item "/")
205                                 if ( $0 == menu_program ) next
206                                 print
207                         }
208                 ' "${menu_program%%/*}/INDEX"
209         )"
210 done
211
212 #
213 # Get a list of command-line programs
214 #
215 cmd_program_list=
216 for file in */INDEX; do
217         cmd_program_list="$cmd_program_list $(
218                 awk -v item="${file%%/*}" '
219                         /^menu_selection="/ {
220                                 sub(/.*\|/, "")
221                                 sub(/"$/, "")
222
223                                 if ( ! $0 ) next
224
225                                 if ( $0 !~ "^/" )
226                                         sub(/^/, item "/")
227
228                                 print
229                         }
230                 ' $file
231         )"
232 done
233
234 #
235 # [Optionally] Calculate list of include files
236 #
237 if [ "$SHOW_INCLUDES" ]; then
238         print_includes_awk='
239                 BEGIN { regex = "^f_include \\$BSDCFG_SHARE/" }
240                 ( $0 ~ regex ) { sub(regex, ""); print }
241         ' # END-QUOTE
242
243         #
244         # Build list of files in which to search for includes
245         #
246         file_list=$(
247                 for file in \
248                         $BSDCONFIG \
249                         $menu_program_list \
250                         $submenu_program_list \
251                         $cmd_program_list \
252                 ; do
253                         [ -e "$file" ] && echo $file
254                 done | sort -u
255         )
256
257         #
258         # Build list of includes used by the above files
259         #
260         include_file_list=
261         for file in $file_list; do
262                 include_file_list="$include_file_list $(
263                         awk "$print_includes_awk" $file
264                 )"
265         done
266
267         #
268         # Sort the list of includes and remove duplicate entries
269         #
270         include_file_list=$(
271                 for include_file in $include_file_list; do
272                         echo "$include_file"
273                 done | sort -u
274         )
275
276         #
277         # Search previously-discovered include files for further includes
278         #
279         before="$include_file_list"
280         while :; do
281                 for file in $include_file_list; do
282                         include_file_list="$include_file_list $(
283                                 awk "$print_includes_awk" $BSDCFG_SHARE/$file
284                         )"
285                 done
286
287                 #
288                 # Sort list of includes and remove duplicate entries [again]
289                 #
290                 include_file_list=$(
291                         for include_file in $include_file_list; do
292                                 echo "$include_file"
293                         done | sort -u
294                 )
295
296                 [ "$include_file_list" = "$before" ] && break
297                 before="$include_file_list"
298         done
299 fi
300
301 #
302 # Add script.subr to includes if it exists
303 #
304 [ -f $BSDCFG_SHARE/script.subr ] &&
305         include_file_list="$include_file_list script.subr"
306
307 #
308 # Start the directional-graph (digraph) output
309 #
310 printf 'strict digraph "" { // Empty name to prevent SVG Auto-Tooltip\n'
311 label_format="$msg_graph_label_with_command"
312 [ "$SHOW_GRAPH_LABEL_DATE" ] &&
313         label_format="$msg_graph_label_with_command_and_date"
314 lang="${LANG:-$LC_ALL}"
315 printf "\n\tlabel = \"$label_format\"\n" \
316        "${lang:+LANG=${lang%%[$IFS]*} }bsdconfig $pgm${ARGV:+ $ARGV}" \
317        "$( date +"%c %Z" )"
318
319 #
320 # Print graph-specific properties
321 #
322 printf '\n\t/*\n\t * Graph setup and orientation\n\t */\n'
323 printf '\tlabelloc = top;\t\t// display above label at top of graph\n'
324 printf '\trankdir = LR;\t\t// create ranks left-to-right\n'
325 printf '\torientation = portrait;\t// default\n'
326 printf '\tratio = fill;\t\t// approximate aspect ratio\n'
327 printf '\tcenter = 1;\t\t// center drawing on page\n'
328
329 #
330 # Perform edge-concentration when displaying a lot of information
331 #
332 # NOTE: This is disabled because dot(1) version 2.28.0 (current) and older have
333 #       a bug that causes a crash when rankdir = LR and concentrate = true
334 #
335 # NOTE: Do not re-enable until said bug is fixed in some future revision.
336 #
337 #[ "$SHOW_INCLUDES" -a "$SHOW_CMDLINE" ] &&
338 #       printf '\tconcentrate = true;\t// enable edge concentrators\n'
339
340 #
341 # Print font details for graph/cluster label(s)
342 #
343 printf '\n\t/*\n\t * Font details for graph/cluster label(s)\n\t */\n'
344 printf '\tfontname = "Times-Italic";\n'
345 printf '\tfontsize = 14;\n'
346
347 #
348 # Print default node attributes
349 #
350 printf '\n\t/*\n\t * Default node attributes\n\t */\n'
351 printf '\tnode [\n'
352 printf '\t\tfontname = "Times-Roman",\n'
353 printf '\t\tfontsize = 12,\n'
354 printf '\t\twidth = 2.5, // arbitrary minimum width for all nodes\n'
355 printf '\t\tfixedsize,   // turn minimum width into exact width\n'
356 printf '\t];\n'
357
358 #
359 # Print top-level item(s)
360 #
361 printf '\n\t/*\n\t * bsdconfig(8)\n\t */\n'
362 shape=circle color=black fillcolor=yellow style=filled
363 begin_nodelist "$shape" "$color" "$fillcolor" "$style"
364 print_node "bsdconfig" "fontname = \"Times-Bold\"" "fontsize = 16"
365 end_nodelist
366
367 #
368 # Print menus
369 #
370 printf '\n\t/*\n\t * Menu items\n\t */\n'
371 shape=box color=black fillcolor=lightblue style=filled
372 begin_nodelist "$shape" "$color" "$fillcolor" "$style"
373 for menu_program in $menu_program_list; do
374         print_node "$menu_program" "label = \"${menu_program#*/}\""
375 done
376 end_nodelist
377
378 #
379 # Print sub-menus
380 #
381 printf '\n\t/*\n\t * Sub-menu items\n\t */\n'
382 shape=box color=black fillcolor=lightblue style=filled
383 begin_nodelist "$shape" "$color" "$fillcolor" "$style"
384 for submenu_program in $submenu_program_list; do
385         print_node "$submenu_program" "label = \"${submenu_program#*/}\""
386 done
387 end_nodelist
388
389 #
390 # Print menu relationships
391 #
392 printf '\n\t/*\n\t * Menu item relationships\n\t */\n'
393 shape=box color=black fillcolor=lightblue style=filled edge_color=blue
394 begin_nodelist "$shape" "$color" "$fillcolor" "$style"
395 print_node edge "penwidth = 5.0" "style = bold" "color = $edge_color"
396 for menu_program in $menu_program_list; do
397         print_node2 "bsdconfig" "$menu_program"
398 done
399 end_nodelist
400
401 #
402 # Print sub-menu relationships
403 #
404 printf '\n\t/*\n\t * Sub-menu item relationships\n\t */\n'
405 shape=box color=black fillcolor=lightblue style=filled edge_color=blue
406 begin_nodelist "$shape" "$color" "$fillcolor" "$style"
407 # Lock sub-menu headport to the West (unless `-c' was passed)
408 [ "$SHOW_CMDLINE" -o ! "$SHOW_INCLUDES" ] && print_node edge "headport = w"
409 print_node edge "style = bold" "color = $edge_color"
410 for submenu_program in $submenu_program_list; do
411         for menu_program in $menu_program_list; do
412                 case "$menu_program" in
413                 [0-9][0-9][0-9].*/*) : fall-through ;;
414                 *) continue # Not a menu item
415                 esac
416
417                 # Continue if program directories do not match
418                 [ "${menu_program%%/*}" = "${submenu_program%%/*}" ] ||
419                         continue
420
421                 print_node2 "$menu_program" "$submenu_program"
422                 break
423         done
424 done
425 end_nodelist
426
427 #
428 # [Optionally] Print include files
429 #
430 if [ "$SHOW_INCLUDES" ]; then
431         printf '\n\t/*\n\t * Include files\n\t */\n'
432         shape=oval color=black fillcolor=white style=filled
433         begin_nodelist "$shape" "$color" "$fillcolor" "$style"
434         printf '\t\tconstraint = false;\n'
435         for include_file in $include_file_list; do
436                 print_node "$include_file" \
437                            "label = \"${include_file##*/}\""
438         done
439         end_nodelist
440 fi
441
442 #
443 # [Optionally] Print f_include() usage/relationships
444 #
445 if [ "$SHOW_INCLUDES" ]; then
446         printf '\n\t/*\n\t * Include usage\n\t */\n'
447         shape=oval color=black fillcolor=white style=filled edge_color=grey
448         begin_nodelist "$shape" "$color" "$fillcolor" "$style"
449         print_node edge "style = dashed" "color = $edge_color"
450         #print_node edge "label = \"\\T\"" "fontsize = 9"
451                 # NOTE: Edge labels are buggy on large graphs
452         file_list=$(
453                 for file in \
454                         $BSDCONFIG \
455                         $menu_program_list \
456                         $submenu_program_list \
457                         $cmd_program_list \
458                         $include_file_list \
459                 ; do
460                         [ -f "$BSDCFG_SHARE/$file" ] &&
461                                 echo $BSDCFG_SHARE/$file
462                         [ -e "$file" ] && echo $file
463                 done | sort -u
464         )
465         for file in $file_list; do
466                 # Skip binary files and text files that don't use f_include()
467                 grep -qlI f_include $file || continue
468
469                 awk \
470                         -v file="${file#$BSDCFG_SHARE/}" \
471                         -v bsdconfig="$BSDCONFIG" \
472                 '
473                         BEGIN { regex = "^f_include \\$BSDCFG_SHARE/" }
474                         ( $0 ~ regex ) {
475                                 sub(regex, "")
476                                 if ( file == bsdconfig ) sub(".*/", "", file)
477                                 printf "\t\t\"%s\" -> \"%s\";\n", $0, file
478                         }
479                 ' $file
480         done | sort
481         end_nodelist
482 fi
483
484 #
485 # Print command-line shortcuts
486 #
487 if [ "$SHOW_CMDLINE" ]; then
488         printf '\n\t/*\n\t * Command-line shortcuts\n\t */\n'
489         shape=parallelogram color=black fillcolor=lightseagreen style=filled
490         begin_nodelist "$shape" "$color" "$fillcolor" "$style"
491         for file in */INDEX; do
492                 awk -v item="${file%%/*}" '
493                         /^menu_selection="/ {
494                                 sub(/^.*="/, "")
495                                 sub(/\|.*/, "")
496                                 printf "\t\t\"bsdconfig %s\"", $0
497                                 printf " [ label = \"%s\" ];\n", $0
498                         }
499                 ' $file
500         done
501         end_nodelist
502 fi
503
504 #
505 # Print command-line shortcut relationships
506 #
507 if [ "$SHOW_CMDLINE" ]; then
508         printf '\n\t/*\n\t * Command-line shortcut relationships\n\t */\n'
509         shape=box color=black fillcolor=lightseagreen style=filled
510         begin_nodelist "$shape" "$color" "$fillcolor" "$style"
511         print_node edge "headport = w" "weight = 100.0"
512         print_node edge "style = bold" "color = $fillcolor"
513         for file in */INDEX; do
514                 awk -v item="${file%%/*}" \
515                     -v node_fillcolor="$node_fillcolor" \
516                     -v edge_color="$edge_color" \
517                 '
518                         /^menu_selection="/ {
519                                 sub(/^.*="/, "")
520                                 sub(/"$/, "")
521
522                                 if ( ! $0 ) next
523
524                                 split($0, menusel, "|")
525                                 if ( menusel[2] !~ "^/" )
526                                         sub(/^/, item "/", menusel[2])
527
528                                 printf "\t\t\"bsdconfig %s\" -> \"%s\";\n",
529                                        menusel[1], menusel[2]
530                         }
531                 ' $file
532         done
533         end_nodelist
534 fi
535
536 #
537 # Print clusters
538 #
539 bgcolor_bsdconfig="lightyellow"
540 bgcolor_includes="gray98"
541 bgcolor_menuitem="aliceblue"
542 bgcolor_shortcuts="honeydew"
543 printf '\n\t/*\n\t * Clusters\n\t */\n'
544 printf '\tsubgraph "cluster_bsdconfig" {\n'
545 printf '\t\tbgcolor = "%s";\n' "$bgcolor_bsdconfig"
546 printf '\t\tlabel = "bsdconfig(8)";\n'
547 printf '\t\ttooltip = "bsdconfig(8)";\n'
548 print_node "bsdconfig"
549 if [ "$SHOW_INCLUDES" ]; then
550         printf '\t\tsubgraph "cluster_includes" {\n'
551         printf '\t\t\tbgcolor = "%s";\n' "$bgcolor_includes"
552         printf '\t\t\tlabel = "%s";\n' "$msg_includes"
553
554         for include_file in $include_file_list; do
555                 echo $include_file
556         done | awk -v bgcolor="$bgcolor_bsdconfig" '
557         BEGIN { created = 0 }
558         function end_subgraph() { printf "\t\t\t};\n" }
559         ( $0 !~ "/" ) {
560                 if ( ! created )
561                 {
562                         printf "\t\t\tsubgraph \"%s\" {\n",
563                                "cluster_bsdconfig_includes"
564                         printf "\t\t\t\tbgcolor = \"%s\";\n", bgcolor
565                         printf "\t\t\t\tlabel = \"bsdconfig\";\n"
566                         created++
567                 }
568                 printf "\t\t\t\t\"%s\";\n", $1
569         }
570         END { created && end_subgraph() }'
571
572         for include_file in $include_file_list; do
573                 echo $include_file
574         done | awk '
575         BEGIN { created = 0 }
576         function end_subgraph() { printf "\t\t\t};\n" }
577         ( $0 ~ "/" ) {
578                 include_dir_tmp = $1
579                 sub("/[^/]*$", "", include_dir_tmp)
580                 gsub(/[^[:alnum:]_]/, "_", include_dir_tmp)
581
582                 if ( created && include_dir != include_dir_tmp )
583                 {
584                         end_subgraph()
585                         created = 0
586                 }
587
588                 if ( ! created )
589                 {
590                         include_dir = include_dir_tmp
591                         printf "\t\t\tsubgraph \"cluster_%s_includes\" {\n",
592                                include_dir
593                         printf "\t\t\t\tbgcolor = \"white\";\n"
594                         printf "\t\t\t\tlabel = \"%s\";\n", include_dir
595                         created++
596                 }
597
598                 printf "\t\t\t\t\"%s\";\n", $1
599         }
600         END { created && end_subgraph() }'
601
602         printf '\t\t};\n'
603 fi
604 end_nodelist
605 for INDEX in */INDEX; do
606         menu_title=
607         menu_help=
608         f_include_lang "$INDEX"
609
610         item="${INDEX%%/*}"
611         printf '\tsubgraph "cluster_%s" {\n' "$item"
612
613         case "$item" in
614         [0-9][0-9][0-9].*) bgcolor="$bgcolor_menuitem" ;;
615         *) bgcolor="$bgcolor_shortcuts"
616         esac
617         printf '\t\tbgcolor = "%s";\n' "$bgcolor"
618         if [ "$menu_title" ]; then
619                 printf '\t\tlabel = "%s\\n\\"%s\\"";\n' "$item" "$menu_title"
620         else
621                 printf '\t\tlabel = "%s";\n' "$item"
622         fi
623         printf '\t\ttooltip = "%s";\n' "${menu_help:-$item}"
624
625         program_list=$(
626                 for program in \
627                         $menu_program_list \
628                         $submenu_program_list \
629                         $cmd_program_list \
630                 ; do
631                         echo "$program"
632                 done | sort -u
633         )
634         for program in $program_list; do
635                 case "$program" in "$item"/*)
636                         print_node "$program" "label = \"${program#*/}\""
637                 esac
638         done
639
640         if [ "$SHOW_INCLUDES" ]; then
641                 item_include_list=
642                 [ -d "$item/include" ] &&
643                         item_include_list=$( find "$item/include" -type f )
644                 item_include_list=$(
645                         for item_include in $item_include_list; do
646                                 for include_file in $include_file_list; do
647                                         [ "$item_include" = "$include_file" ] ||
648                                                 continue
649                                         echo "$item_include"; break
650                                 done
651                         done
652                 )
653                 if [ "$item_include_list" ]; then
654                         printf '\t\tsubgraph "cluster_%s_includes" {\n' "$item"
655                         printf '\t\t\tbgcolor = "%s";\n' "$bgcolor_includes"
656                         printf '\t\t\tlabel = "%s";\n' "$msg_includes"
657                 fi
658                 for item_include in $item_include_list; do
659                         printf '\t\t\t"%s";\n' "$item_include"
660                 done
661                 [ "$item_include_list" ] && printf '\t\t};\n'
662         fi
663
664         if [ "$SHOW_CMDLINE" ]; then
665                 printf '\t\tsubgraph "cluster_%s_shortcuts" {\n' "$item"
666                 printf '\t\t\tbgcolor = "%s";\n' "$bgcolor_shortcuts"
667                 printf '\t\t\tlabel = "%s";\n' "$msg_shortcuts"
668                 awk '/^menu_selection="/ {
669                         sub(/^.*="/, "")
670                         sub(/\|.*/, "")
671                         printf "\t\t\t\"bsdconfig %s\";\n", $0
672                 }' "$INDEX"
673                 printf '\t\t};\n'
674         fi
675
676         end_nodelist
677 done
678
679 printf '\n};\n'
680
681 ################################################################################
682 # END
683 ################################################################################