3 # Copyright (c) 2012-2013 Devin Teske
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
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.
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
29 ############################################################ INCLUDES
31 # Prevent common.subr from auto initializing debugging (this is not an inter-
32 # active utility so does not require debugging; also `-d' has been repurposed).
34 DEBUG_SELF_INITIALIZE=NO
36 BSDCFG_SHARE="/usr/share/bsdconfig"
37 . $BSDCFG_SHARE/common.subr || exit 1
38 f_dprintf "%s: loading includes..." "$0"
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
44 f_index_menusel_keyword $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ipgm &&
47 ############################################################ CONFIGURATION
50 # Location of bsdconfig(8)
52 BSDCONFIG=/usr/sbin/bsdconfig
54 ############################################################ GLOBALS
59 SHOW_GRAPH_LABEL_DATE=1
63 ############################################################ FUNCTIONS
65 # begin_nodelist $shape $color $fillcolor $style
67 # Create a new multi-node list rendering nodes in a specific style described by
68 # the arguments passed.
72 local shape="$1" color="$2" fillcolor="$3" style="$4"
76 printf '\t\tshape = "%s",\n' "$shape"
78 printf '\t\tcolor = "%s",\n' "$color"
80 printf '\t\tfillcolor = "%s",\n' "$fillcolor"
82 printf '\t\tstyle = "%s",\n' "$style"
86 # print_node $node [$attributes ...]
88 # Print a node within a multi-node list.
97 edge) printf '\t\t%s' "$node" ;;
98 *) printf '\t\t"%s"' "$node" ;;
101 if [ $# -gt 0 ]; then
103 while [ $# -gt 0 ]; do
106 [ $# -gt 0 ] && echo -n ","
114 # print_node2 $node $node [$attributes ...]
116 # Print a directed node-node connection within a multi-node list.
120 local node1="$1" node2="$2"
122 shift 2 # node1 node2
124 printf '\t\t"%s" -> "%s"' "$node1" "$node2"
126 if [ $# -gt 0 ]; then
128 while [ $# -gt 0 ]; do
131 [ $# -gt 0 ] && echo -n ","
141 # Close a multi-node list.
148 ############################################################ MAIN
150 # Incorporate rc-file if it exists
151 [ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc"
154 # Process command-line arguments
156 while getopts cdhi flag; do
159 d) SHOW_GRAPH_LABEL_DATE= ;;
161 h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm" ;;
164 shift $(( $OPTIND - 1 ))
166 cd $BSDCFG_LIBE || f_die # Pedantic
169 # Get a list of menu programs
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="/ {
179 if ( $0 !~ "^/" ) sub(/^/, item "/")
186 # Get a list of submenu programs
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
195 submenu_program_list="$submenu_program_list $(
196 awk -v menu_program="$menu_program" \
197 -v item="${menu_program%%/*}" \
199 /^menu_selection="/ {
205 if ( $0 == menu_program ) next
208 ' "${menu_program%%/*}/INDEX"
213 # Get a list of command-line programs
216 for file in */INDEX; do
217 cmd_program_list="$cmd_program_list $(
218 awk -v item="${file%%/*}" '
219 /^menu_selection="/ {
235 # [Optionally] Calculate list of include files
237 if [ "$SHOW_INCLUDES" ]; then
239 BEGIN { regex = "^f_include \\$BSDCFG_SHARE/" }
240 ( $0 ~ regex ) { sub(regex, ""); print }
244 # Build list of files in which to search for includes
250 $submenu_program_list \
252 $BSDCFG_SHARE/script.subr \
254 [ -e "$file" ] && echo $file
259 # Build list of includes used by the above files
262 for file in $file_list; do
263 include_file_list="$include_file_list $(
264 awk "$print_includes_awk" $file
269 # Sort the list of includes and remove duplicate entries
272 for include_file in $include_file_list; do
278 # Search previously-discovered include files for further includes
280 before="$include_file_list"
282 for file in $include_file_list; do
283 include_file_list="$include_file_list $(
284 awk "$print_includes_awk" $BSDCFG_SHARE/$file
289 # Sort list of includes and remove duplicate entries [again]
292 for include_file in $include_file_list; do
297 [ "$include_file_list" = "$before" ] && break
298 before="$include_file_list"
303 # Start the directional-graph (digraph) output
305 printf 'strict digraph "" { // Empty name to prevent SVG Auto-Tooltip\n'
306 label_format="$msg_graph_label_with_command"
307 [ "$SHOW_GRAPH_LABEL_DATE" ] &&
308 label_format="$msg_graph_label_with_command_and_date"
309 lang="${LANG:-$LC_ALL}"
310 printf "\n\tlabel = \"$label_format\"\n" \
311 "${lang:+LANG=${lang%%[$IFS]*} }bsdconfig $pgm${ARGV:+ $ARGV}" \
315 # Print graph-specific properties
317 printf '\n\t/*\n\t * Graph setup and orientation\n\t */\n'
318 printf '\tlabelloc = top;\t\t// display above label at top of graph\n'
319 printf '\trankdir = LR;\t\t// create ranks left-to-right\n'
320 printf '\torientation = portrait;\t// default\n'
321 printf '\tratio = fill;\t\t// approximate aspect ratio\n'
322 printf '\tcenter = 1;\t\t// center drawing on page\n'
325 # Perform edge-concentration when displaying a lot of information
327 # NOTE: This is disabled because dot(1) version 2.28.0 (current) and older have
328 # a bug that causes a crash when rankdir = LR and concentrate = true
330 # NOTE: Do not re-enable until said bug is fixed in some future revision.
332 #[ "$SHOW_INCLUDES" -a "$SHOW_CMDLINE" ] &&
333 # printf '\tconcentrate = true;\t// enable edge concentrators\n'
336 # Print font details for graph/cluster label(s)
338 printf '\n\t/*\n\t * Font details for graph/cluster label(s)\n\t */\n'
339 printf '\tfontname = "Times-Italic";\n'
340 printf '\tfontsize = 14;\n'
343 # Print default node attributes
345 printf '\n\t/*\n\t * Default node attributes\n\t */\n'
347 printf '\t\tfontname = "Times-Roman",\n'
348 printf '\t\tfontsize = 12,\n'
349 printf '\t\twidth = 2.5, // arbitrary minimum width for all nodes\n'
350 printf '\t\tfixedsize = true, // turn minimum width into exact width\n'
354 # Print top-level item(s)
356 printf '\n\t/*\n\t * bsdconfig(8)\n\t */\n'
357 shape=circle color=black fillcolor=yellow style=filled
358 begin_nodelist "$shape" "$color" "$fillcolor" "$style"
359 print_node "bsdconfig" "fontname = \"Times-Bold\"" "fontsize = 16"
365 printf '\n\t/*\n\t * Menu items\n\t */\n'
366 shape=box color=black fillcolor=lightblue style=filled
367 begin_nodelist "$shape" "$color" "$fillcolor" "$style"
368 for menu_program in $menu_program_list; do
369 print_node "$menu_program" "label = \"${menu_program#*/}\""
376 printf '\n\t/*\n\t * Sub-menu items\n\t */\n'
377 shape=box color=black fillcolor=lightblue style=filled
378 begin_nodelist "$shape" "$color" "$fillcolor" "$style"
379 for submenu_program in $submenu_program_list; do
380 print_node "$submenu_program" "label = \"${submenu_program#*/}\""
385 # Print menu relationships
387 printf '\n\t/*\n\t * Menu item relationships\n\t */\n'
388 shape=box color=black fillcolor=lightblue style=filled edge_color=blue
389 begin_nodelist "$shape" "$color" "$fillcolor" "$style"
390 print_node edge "penwidth = 5.0" "style = bold" "color = $edge_color"
391 for menu_program in $menu_program_list; do
392 print_node2 "bsdconfig" "$menu_program"
397 # Print sub-menu relationships
399 printf '\n\t/*\n\t * Sub-menu item relationships\n\t */\n'
400 shape=box color=black fillcolor=lightblue style=filled edge_color=blue
401 begin_nodelist "$shape" "$color" "$fillcolor" "$style"
402 # Lock sub-menu headport to the West (unless `-c' was passed)
403 [ "$SHOW_CMDLINE" -o ! "$SHOW_INCLUDES" ] && print_node edge "headport = w"
404 print_node edge "style = bold" "color = $edge_color"
405 for submenu_program in $submenu_program_list; do
406 for menu_program in $menu_program_list; do
407 case "$menu_program" in
408 [0-9][0-9][0-9].*/*) : fall-through ;;
409 *) continue # Not a menu item
412 # Continue if program directories do not match
413 [ "${menu_program%%/*}" = "${submenu_program%%/*}" ] ||
416 print_node2 "$menu_program" "$submenu_program"
423 # [Optionally] Print include files
425 if [ "$SHOW_INCLUDES" ]; then
426 printf '\n\t/*\n\t * Include files\n\t */\n'
427 shape=oval color=black fillcolor=white style=filled
428 begin_nodelist "$shape" "$color" "$fillcolor" "$style"
429 printf '\t\tconstraint = false;\n'
430 for include_file in $include_file_list; do
431 print_node "$include_file" \
432 "label = \"${include_file##*/}\""
438 # [Optionally] Print f_include() usage/relationships
440 if [ "$SHOW_INCLUDES" ]; then
441 printf '\n\t/*\n\t * Include usage\n\t */\n'
442 shape=oval color=black fillcolor=white style=filled edge_color=grey
443 begin_nodelist "$shape" "$color" "$fillcolor" "$style"
444 print_node edge "style = dashed" "color = $edge_color"
445 #print_node edge "label = \"\\T\"" "fontsize = 9"
446 # NOTE: Edge labels are buggy on large graphs
451 $submenu_program_list \
455 [ -f "$BSDCFG_SHARE/$file" ] &&
456 echo $BSDCFG_SHARE/$file
457 [ -e "$file" ] && echo $file
460 for file in $file_list; do
461 # Skip binary files and text files that don't use f_include()
462 grep -qlI f_include $file || continue
465 -v file="${file#$BSDCFG_SHARE/}" \
466 -v bsdconfig="$BSDCONFIG" \
468 BEGIN { regex = "^f_include \\$BSDCFG_SHARE/" }
471 if ( file == bsdconfig ) sub(".*/", "", file)
472 printf "\t\t\"%s\" -> \"%s\";\n", $0, file
480 # Print command-line shortcuts
482 if [ "$SHOW_CMDLINE" ]; then
483 printf '\n\t/*\n\t * Command-line shortcuts\n\t */\n'
484 shape=parallelogram color=black fillcolor=lightseagreen style=filled
485 begin_nodelist "$shape" "$color" "$fillcolor" "$style"
486 for file in */INDEX; do
487 awk -v item="${file%%/*}" '
488 /^menu_selection="/ {
491 printf "\t\t\"bsdconfig %s\"", $0
492 printf " [ label = \"%s\" ];\n", $0
500 # Print command-line shortcut relationships
502 if [ "$SHOW_CMDLINE" ]; then
503 printf '\n\t/*\n\t * Command-line shortcut relationships\n\t */\n'
504 shape=box color=black fillcolor=lightseagreen style=filled
505 begin_nodelist "$shape" "$color" "$fillcolor" "$style"
506 print_node edge "headport = w" "weight = 100.0"
507 print_node edge "style = bold" "color = $fillcolor"
508 for file in */INDEX; do
509 awk -v item="${file%%/*}" \
510 -v node_fillcolor="$node_fillcolor" \
511 -v edge_color="$edge_color" \
513 /^menu_selection="/ {
519 split($0, menusel, "|")
520 if ( menusel[2] !~ "^/" )
521 sub(/^/, item "/", menusel[2])
523 printf "\t\t\"bsdconfig %s\" -> \"%s\";\n",
524 menusel[1], menusel[2]
534 bgcolor_bsdconfig="lightyellow"
535 bgcolor_includes="gray98"
536 bgcolor_menuitem="aliceblue"
537 bgcolor_shortcuts="honeydew"
538 printf '\n\t/*\n\t * Clusters\n\t */\n'
539 printf '\tsubgraph "cluster_bsdconfig" {\n'
540 printf '\t\tbgcolor = "%s";\n' "$bgcolor_bsdconfig"
541 printf '\t\tlabel = "bsdconfig(8)";\n'
542 printf '\t\ttooltip = "bsdconfig(8)";\n'
543 print_node "bsdconfig"
545 if [ "$SHOW_INCLUDES" ]; then
546 for include_file in $include_file_list; do
549 -v bgcolor="$bgcolor_bsdconfig" \
550 -v msg_subroutines="$msg_subroutines" \
552 BEGIN { created = 0 }
553 function end_subgraph() { printf "\t};\n" }
557 printf "\tsubgraph \"%s\" {\n",
558 "cluster_bsdconfig_includes"
559 printf "\t\tbgcolor = \"%s\";\n", bgcolor
560 printf "\t\tlabel = \"bsdconfig %s\";\n",
564 printf "\t\t\"%s\";\n", $1
566 END { created && end_subgraph() }
569 for include_file in $include_file_list; do
571 done | awk -v msg_subroutines="$msg_subroutines" '
572 BEGIN { created = 0 }
573 function end_subgraph() { printf "\t};\n" }
576 sub("/[^/]*$", "", include_dir_tmp)
577 gsub(/[^[:alnum:]_]/, "_", include_dir_tmp)
579 if ( created && include_dir != include_dir_tmp )
587 include_dir = include_dir_tmp
588 printf "\tsubgraph \"cluster_%s_includes\" {\n",
590 printf "\t\tbgcolor = \"thistle\";\n"
591 printf "\t\tlabel = \"%s %s\";\n", include_dir,
596 printf "\t\t\"%s\";\n", $1
598 END { created && end_subgraph() }'
600 for INDEX in */INDEX; do
603 f_include_lang "$INDEX"
606 printf '\tsubgraph "cluster_%s" {\n' "$item"
609 [0-9][0-9][0-9].*) bgcolor="$bgcolor_menuitem" ;;
610 *) bgcolor="$bgcolor_shortcuts"
612 printf '\t\tbgcolor = "%s";\n' "$bgcolor"
613 if [ "$menu_title" ]; then
614 printf '\t\tlabel = "%s\\n\\"%s\\"";\n' "$item" "$menu_title"
616 printf '\t\tlabel = "%s";\n' "$item"
618 printf '\t\ttooltip = "%s";\n' "${menu_help:-$item}"
623 $submenu_program_list \
629 for program in $program_list; do
630 case "$program" in "$item"/*)
631 print_node "$program" "label = \"${program#*/}\""
635 if [ "$SHOW_INCLUDES" ]; then
637 [ -d "$item/include" ] &&
638 item_include_list=$( find "$item/include" -type f )
640 for item_include in $item_include_list; do
641 for include_file in $include_file_list; do
642 [ "$item_include" = "$include_file" ] ||
644 echo "$item_include"; break
648 if [ "$item_include_list" ]; then
649 printf '\t\tsubgraph "cluster_%s_includes" {\n' "$item"
650 printf '\t\t\tbgcolor = "%s";\n' "$bgcolor_includes"
651 printf '\t\t\tlabel = "%s";\n' "$msg_includes"
653 for item_include in $item_include_list; do
654 printf '\t\t\t"%s";\n' "$item_include"
656 [ "$item_include_list" ] && printf '\t\t};\n'
659 if [ "$SHOW_CMDLINE" ]; then
660 printf '\t\tsubgraph "cluster_%s_shortcuts" {\n' "$item"
661 printf '\t\t\tbgcolor = "%s";\n' "$bgcolor_shortcuts"
662 printf '\t\t\tlabel = "%s";\n' "$msg_shortcuts"
663 awk '/^menu_selection="/ {
666 printf "\t\t\t\"bsdconfig %s\";\n", $0
676 ################################################################################
678 ################################################################################