Skip to content

Commit 6ecbfbd

Browse files
committed
WIP
1 parent f957afc commit 6ecbfbd

File tree

4 files changed

+300
-333
lines changed

4 files changed

+300
-333
lines changed

display-box.sh

Lines changed: 296 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,303 @@
11
#!/bin/bash
22
#
3-
# This script allows you to pipe in a multi-line input string
4-
# and it will parse out the top line as a header and call the
5-
# display-box function on the output.
3+
# DOCUMENTATION:
4+
# https://github.com/deanhouseholder/display-boxes
65
#
7-
# You can still use the flags to modify settings such as:
6+
# This script may be executed directly, sourced as a function, or
7+
# executed through a pipe.
8+
#
9+
# You may pass in $headers, $body, and flags. Or to pipe in
10+
# a multi-line input string and it will parse out the top line
11+
# as a header and call the display-box function on the output.
12+
#
13+
# USAGE:
14+
#
15+
# As a script:
16+
# /path/to-display-box.sh "$headers" "$body" -s 2 -p 2 -d ,
17+
#
18+
# As a function:
19+
# . /path/to/display-box.sh
20+
# display-box "$headers" "$body" -s 2 -p 2 -d ,
21+
#
22+
# Or via a pipe:
823
# echo "$multi_line_string" | /path/to/display-box.sh -s 2 -p 2 -d ,
924
#
25+
# To make it more convenient, you may want to create a symlink in the
26+
# global path, such as: /usr/local/bin/display-box
27+
# sudo ln -s /path/to/display-box.sh /usr/local/bin/display-box
28+
#
1029

11-
if [[ -f "${0%/*}/display-boxes.sh" ]]; then
12-
source "${0%/*}/display-boxes.sh"
13-
else
14-
printf "Error: Could not find display-boxes.sh in the same directory as this script.\n"
15-
exit 1
16-
fi
1730

18-
# Capture stdin and separate header from body
19-
input="$(cat)"
20-
headers="${input%%$'\n'*}"
21-
body="${input#*$'\n'}"
22-
23-
# Process flag options. Ignore anything else
24-
while [[ $# != 0 ]]; do
25-
case "$1" in
26-
-s) [[ ! -z "$2" ]] && { style="-s $2"; shift 2; } || shift;;
27-
-p) [[ ! -z "$2" ]] && { padding="-p $2"; shift 2; } || shift;;
28-
-d) [[ ! -z "$2" ]] && { delimiter="-d $2"; shift 2; } || shift;;
29-
*) shift;;
30-
esac
31-
done
32-
33-
display-box "$headers" "$body" $style $padding $delimiter
31+
# Main function to display box
32+
display-box() {
33+
headers=""
34+
# Process options ($headers is always defined before $body)
35+
while [[ $# != 0 ]]; do
36+
case "$1" in
37+
-s) [[ ! -z "$2" ]] && { style="$2"; shift 2; } || shift;;
38+
-p) [[ ! -z "$2" ]] && { padding="$2"; shift 2; } || shift;;
39+
-d) [[ ! -z "$2" ]] && { delimiter="$2"; shift 2; } || shift;;
40+
*)
41+
if [[ -z "$headers" ]]; then
42+
headers="$1"
43+
elif [[ -z "$body" ]]; then
44+
body="$1"
45+
elif [[ -z "$style" ]] && [[ $1 =~ ^[1-6]$ ]]; then
46+
style="$1"
47+
elif [[ -z "$padding" ]] && [[ $1 =~ ^[1-6]$ ]]; then
48+
padding="$1"
49+
elif [[ -z "$delimiter" ]] && [[ $1 =~ ^.\ *$ ]]; then
50+
delimiter="$1"
51+
fi
52+
shift
53+
;;
54+
esac
55+
done
56+
57+
# Set default values
58+
[[ -z "$style" ]] && style=1
59+
[[ -z "$padding" ]] && padding=1
60+
[[ -z "$delimiter" ]] && delimiter=$'\t'
61+
62+
# Catch missing inputs
63+
error=0
64+
if [[ -z "$headers" ]]; then
65+
echo "Missing required \$headers argument"
66+
error=1
67+
fi
68+
if [[ -z "$body" ]]; then
69+
echo "Missing required \$body argument"
70+
error=1
71+
fi
72+
if [[ ! -z "$style" ]] && [[ ! "$style" =~ ^[1-6]$ ]]; then # Make sure it's a positive integer with a valid style number
73+
echo "Invalid style number. Valid options: 1-6"
74+
error=1
75+
fi
76+
if [[ ! -z "$padding" ]] && [[ ! "$padding" =~ ^[1-6]$ ]]; then # Make sure it's a positive integer with a valid padding number
77+
echo "Invalid padding number. Valid options: 1-6"
78+
error=1
79+
fi
80+
if [[ ! -z "$delimiter" ]] && [[ ! "$delimiter" =~ ^.\ *$ ]]; then
81+
echo "Invalid delimiter. delimiter must be a single character or 2+ spaces."
82+
error=1
83+
fi
84+
if [[ "$error" -eq 1 ]]; then
85+
return
86+
fi
87+
88+
# Repeat a string n times
89+
repeat_string() {
90+
if [[ -z "$1" ]] || [[ -z "$2" ]] || [[ "$2" -eq 0 ]]; then
91+
return
92+
fi
93+
printf "%0.s$1" $(seq $2)
94+
}
95+
96+
# Clean up input strings
97+
clean_string() {
98+
r=$(($required_spaces - 1))
99+
test "$r" -lt 1 && r=1
100+
echo "$1" | sed -E \
101+
-e '/^\s*$/d' `# Delete blank lines` \
102+
-e 's/([ ]{'$r'}[ ]+)/\t/g' `# Detect n spaces and replace with tab` \
103+
-e 's/^\s*//g' `# Trim leading spaces` \
104+
-e 's/\s*$//g' `# Trim trailing spaces`
105+
}
106+
107+
# Set some default vars
108+
declare -a column_length
109+
required_spaces=2 # Spaces required as column separator
110+
headers="$(clean_string "$headers")"
111+
body="$(clean_string "$body")"
112+
[[ "$delimiter" == ' ' ]] && delimiter=$'\t'
113+
output=''
114+
show_top=1
115+
show_middle=1
116+
show_bottom=1
117+
shorten_border=0
118+
ps="$(repeat_string ' ' $padding)" # Padding spaces
119+
tl='' # Top-left
120+
tm='' # Top-Middle
121+
tr='' # Top-Right
122+
ml='' # Middle-Left
123+
mm='' # Middle-Middle
124+
mr='' # Middle-Right
125+
bl='' # Bottom-Left
126+
bm='' # Bottom-Middle
127+
br='' # Bottom-Right
128+
oh='' # Outer Horizontal Bar
129+
ih='' # Inner Horizontal Bar
130+
ov='' # Outer Vertical Bar
131+
iv='' # Inner Vertical Bar
132+
133+
# Set style type
134+
if [[ -z "$style" ]] || [[ "$style" -eq 1 ]]; then
135+
# ┌────┬────┐
136+
# │ ab │ cd │
137+
# ├────┼────┤
138+
# │ ef │ gh │
139+
# └────┴────┘
140+
tl='' tm='' tr='' ml='' mm='' mr='' bl='' bm='' br='' oh='' ih='' ov='' iv=''
141+
elif [[ "$style" -eq 2 ]]; then
142+
# ╔════╦════╗
143+
# ║ ab ║ cd ║
144+
# ╠════╬════╣
145+
# ║ ef ║ gh ║
146+
# ╚════╩════╝
147+
tl='' tm='' tr='' ml='' mm='' mr='' bl='' bm='' br='' oh='' ih='' ov='' iv=''
148+
elif [[ "$style" -eq 3 ]]; then
149+
# ╔════╤════╗
150+
# ║ ab │ cd ║
151+
# ╟────┼────╢
152+
# ║ ef │ gh ║
153+
# ╚════╧════╝
154+
tl='' tm='' tr='' ml='' mm='' mr='' bl='' bm='' br='' oh='' ih='' ov='' iv=''
155+
elif [[ "$style" -eq 4 ]]; then
156+
# ════ ════
157+
# ab cd
158+
# ════ ════
159+
# ef gh
160+
# ════ ════
161+
tm="$ps" mm="$ps" bm="$ps" oh='' ih='' iv="$ps"
162+
elif [[ "$style" -eq 5 ]]; then
163+
# ════ ════
164+
# ab cd
165+
# ──── ────
166+
# ef gh
167+
# ════ ════
168+
tm="$ps" mm="$ps" bm="$ps" oh='' ih='' iv="$ps"
169+
elif [[ "$style" -eq 6 ]]; then
170+
# ab cd
171+
# ──── ────
172+
# ef gh
173+
show_top=0 show_bottom=0 shorten_border=1 ih=''
174+
fi
175+
176+
# Determine the maximum length of each column based on the data
177+
get_max_depth() {
178+
while IFS="$delimiter" read -a line; do
179+
column=0
180+
for line in "${line[@]}"; do
181+
if [[ column_length[$column] -lt ${#line} ]]; then
182+
column_length[$column]=${#line}
183+
fi
184+
let column++
185+
done
186+
done <<< "$1"
187+
}
188+
189+
# Add padding to the maximum length of each column
190+
pad_each_column() {
191+
for col_key in ${!column_length[@]}; do
192+
col_len=${column_length[$col_key]}
193+
col_len=$(($col_len + ($padding * 2)))
194+
column_length[$col_key]=$col_len
195+
done
196+
}
197+
198+
# Display the top line above the header
199+
header_top_line() {
200+
for column in ${!column_length[@]}; do
201+
if [[ $column -eq 0 ]]; then # if the column is the first one
202+
output+="$tl$(repeat_string "$oh" ${column_length[$column]})"
203+
elif [[ $column -le ${#column_length[@]} ]]; then # if the column is the not last one
204+
output+="$tm$(repeat_string "$oh" ${column_length[$column]})"
205+
fi
206+
done
207+
output+="$tr\n" # print top-right corner
208+
}
209+
210+
# Display a line of data (header or body)
211+
process_data() {
212+
# Loop through each line of input creating an array of each column called $data
213+
while IFS="$delimiter" read -a data; do
214+
for column in ${!column_length[@]}; do
215+
# Determine remaining padding: column length - (data length + padding (on both sides))
216+
trailing_padding=$((${column_length[$column]} - (${#data[$column]} + ($padding * 2))))
217+
if [[ $column -eq 0 ]]; then
218+
output+="$ov"
219+
else
220+
output+="$iv"
221+
fi
222+
output+="$ps${data[$column]}$ps"
223+
output+="$(repeat_string " " $trailing_padding)"
224+
done
225+
output+="$ov\n"
226+
done <<< "$1"
227+
}
228+
229+
# Display the line between the header and the body
230+
header_bottom_line() {
231+
for column in ${!column_length[@]}; do
232+
if [[ $column -eq 0 ]]; then # if the column is the first one
233+
output+="$ml"
234+
elif [[ $column -le ${#column_length[@]} ]]; then # if the column is the not last one
235+
output+="$mm"
236+
fi
237+
if [[ "$shorten_border" -eq 1 ]]; then
238+
output+="$ps$(repeat_string "$ih" $((${column_length[$column]} - ($padding * 2))))$ps"
239+
else
240+
output+=$(repeat_string "$ih" ${column_length[$column]})
241+
fi
242+
done
243+
output+="$mr\n"
244+
}
245+
246+
# Display the bottom line after the body
247+
footer() {
248+
for column in ${!column_length[@]}; do
249+
if [[ $column -eq 0 ]]; then # if the column is the first one
250+
output+="$bl$(repeat_string "$oh" ${column_length[$column]})"
251+
elif [[ $column -le ${#column_length[@]} ]]; then # if the column is the not last one
252+
output+="$bm$(repeat_string "$oh" ${column_length[$column]})"
253+
fi
254+
done
255+
output+="$br\n" # print top-right corner
256+
}
257+
258+
# Define max depths for each column by processing input
259+
get_max_depth "$headers"
260+
get_max_depth "$body"
261+
262+
# Add padding to the max depths
263+
pad_each_column
264+
265+
# Generate the Header
266+
[[ "$show_top" -eq 1 ]] && header_top_line
267+
process_data "$headers"
268+
[[ "$show_middle" -eq 1 ]] && header_bottom_line
269+
270+
# Generate the Body
271+
process_data "$body"
272+
[[ "$show_bottom" -eq 1 ]] && footer
273+
274+
# Display the final output
275+
printf "$output"
276+
}
277+
278+
279+
# If the script is executed, parse the input and call the function.
280+
# Otherwise, if sourced, the function is added to the shell environment.
281+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
282+
# Get headers/body/flags
283+
if [[ -z "$1" ]] || [[ "$1" =~ ^\-.*$ ]]; then
284+
# Capture stdin and separate header from body
285+
input="$(timeout 1 cat)"
286+
headers="${input%%$'\n'*}"
287+
body="${input#*$'\n'}"
288+
289+
# Process flag options. Ignore anything else
290+
while [[ $# != 0 ]]; do
291+
case "$1" in
292+
-s) [[ ! -z "$2" ]] && { style="-s $2"; shift 2; } || shift;;
293+
-p) [[ ! -z "$2" ]] && { padding="-p $2"; shift 2; } || shift;;
294+
-d) [[ ! -z "$2" ]] && { delimiter="-d $2"; shift 2; } || shift;;
295+
*) shift;;
296+
esac
297+
done
298+
display-box "$headers" "$body" $style $padding $delimiter
299+
else
300+
# Try just running it with the same input
301+
display-box "$@"
302+
fi
303+
fi

0 commit comments

Comments
 (0)