make getpeername() return the original socket address which before it was intercepted
[hband-tools.git] / user-tools / tabularize
blobb949d4b6777e433bd48144e945b050a55c951302
1 #!/bin/bash
3 true <<EOF
4 =pod
6 =head1 NAME
8 tabularize - Takes TAB-delimited lines of text and outputs formatted table.
10 =head1 SYNOPSIS
12 I<COMMAND> | tabularize [I<OPTIONS>]
14 =head1 OPTIONS
16 =over 4
18 =item -a, --ascii
20 7-bit ascii borders
22 =item -u, --unicode
24 borders with nice graphical chars
26 =item -H, --no-horizontal
28 no horizontal lines in the output
30 =item -M, --no-margins
32 no margins, ie. no right-most and left-most vertical borders
34 =item -v, --output-vertical-separator I<CHAR>
36 vertical separator character(s) in the output
38 =item -r, --align-right I<NUM>
40 align these columns (0-indexed) to the right,
41 others are auto-detected and if deemed to contain numeric data,
42 only then aligned to the right.
43 this option is repeatable.
45 =item -l, --align-left I<NUM>
47 similar to --align-right option
49 =back
51 =head1 ENVIRONMENT
53 =over 4
55 =item PAGER
57 If B<$PAGER> is set and standard output is a terminal
58 and the resulting table is wider than the terminal,
59 then pipe the table through B<$PAGER>.
61 =back
63 =head1 SEE ALSO
65 column(1), untabularize(1)
67 =cut
69 EOF
72 TAB=$'\t'
73 IFS=''
75 set_ascii()
77 verticalBar='|'
78 horizontalBar='-'
79 topleftCorner='+'
80 toprightCorner='+'
81 bottomleftCorner='+'
82 bottomrightCorner='+'
83 leftCross='+'
84 middleCross='+'
85 rightCross='+'
86 topCross='+'
87 bottomCross='+'
89 set_unicode()
91 verticalBar='│'
92 horizontalBar='─'
93 topleftCorner='┌'
94 toprightCorner='┐'
95 bottomleftCorner='└'
96 bottomrightCorner='┘'
97 leftCross='├'
98 middleCross='┼'
99 rightCross='┤'
100 topCross='┬'
101 bottomCross='┴'
105 set_unicode
107 declare -a columnAlignment=()
108 horizontalLines=yes
109 margins=yes
111 while [ $# -gt 0 ]
113 case "$1" in
114 -a|--ascii)
115 set_ascii
117 -u|--unicode)
118 set_unicode
120 -H|--no-horizontal)
121 horizontalLines=''
123 -M|--no-margins)
124 margins=''
126 -r|--align-right)
127 shift
128 columnAlignment[$1]=right
130 -l|--align-left)
131 shift
132 columnAlignment[$1]=left
134 -v|--output-vertical-separator)
135 shift
136 verticalBar=$1
137 topleftCorner=$1
138 toprightCorner=$1
139 bottomleftCorner=$1
140 bottomrightCorner=$1
141 leftCross=$1
142 middleCross=$1
143 rightCross=$1
144 topCross=$1
145 bottomCross=$1
147 -h|--help)
148 pod2text "$0"
149 exit
152 echo "$0: unknown option: $1" >&2
153 exit -1
155 esac
156 shift
157 done
160 explode()
162 local separator=$1
163 local string=$2
164 local new_string
165 declare -g -a EXPLODE_ARRAY=()
167 while [ -n "$string" ]
169 EXPLODE_ARRAY+=("${string%%$separator*}")
170 new_string=${string#*$separator}
171 if [ "$string" = "$new_string" ]
172 then
173 break
175 string=$new_string
176 done
178 # return $EXPLODE_ARRAY
181 is_numeric_check_sep()
183 local thousands_sep=$2
184 local fraction_sep=$3
185 [[ $1 =~ ^[+-]?[0-9]+($thousands_sep[0-9]+)*($fraction_sep[0-9]+($thousands_sep[0-9]+)*)?$ ]]
187 is_numeric()
189 is_numeric_check_sep "$1" ',' . && return 0
190 is_numeric_check_sep "$1" ' ' , && return 0
191 is_numeric_check_sep "$1" '.' , && return 0
192 return 1
196 # compute width of each column
198 column_width=()
199 num_line=0
200 text=''
201 numericals_by_col=()
202 non_numericals_by_col=()
203 while read -r line
205 text="$text${text:+$'\n'}$line"
206 explode $'\t' "$line"
208 column=0
209 for cell in "${EXPLODE_ARRAY[@]}"
211 width=${#cell}
212 if [ -z "${column_width[$column]}" ] || [ $width -gt "${column_width[$column]}" ]
213 then
214 column_width[$column]=$width
217 # guess if this cell has numerical content (skip 1st line as it's likely a header)
218 if [ $num_line -gt 0 -a -n "$cell" -a -z "${columnAlignment[$column]:-}" ]
219 then
220 if is_numeric "$cell"
221 then
222 let numericals_by_col[$column]++
223 else
224 let non_numericals_by_col[$column]++
227 let column++
228 done
230 let num_line++
231 done
233 if [ $num_line = 0 ]
234 then
235 # no input, no output
236 exit 0
240 set -u
242 # compose the format string
244 table_width=0
245 cellFmt=''
246 topFmt=''
247 innerFmt=''
248 bottomFmt=''
249 segments=()
250 sequentColumn=''
251 for column in "${!column_width[@]}"
253 if [ -z "${columnAlignment[$column]:-}" ]
254 then
255 if [ ${numericals_by_col[$column]:-0} -ge ${non_numericals_by_col[$column]:-0} ]
256 then
257 columnAlignment[$column]=right
258 else
259 columnAlignment[$column]=left
263 table_width=$[table_width + (column == 0 ? (0${margins:+1} ? ${#verticalBar} : 0) : ${#verticalBar}) + ${column_width[$column]}]
265 cellFmt="${cellFmt:-${margins:+$verticalBar}}${sequentColumn:+$verticalBar}%*s"
266 topFmt="${topFmt:-${margins:+$topleftCorner}}${sequentColumn:+$topCross}%${column_width[$column]}s"
267 innerFmt="${innerFmt:-${margins:+$leftCross}}${sequentColumn:+$middleCross}%${column_width[$column]}s"
268 bottomFmt="${bottomFmt:-${margins:+$bottomleftCorner}}${sequentColumn:+$bottomCross}%${column_width[$column]}s"
269 segments+=('')
271 sequentColumn=yes
272 done
273 table_width=$[table_width + (0${margins:+1} ? ${#verticalBar} : 0)]
274 cellFmt="$cellFmt${margins:+$verticalBar}"
275 topFmt="$topFmt${margins:+$toprightCorner}"
276 innerFmt="$innerFmt${margins:+$rightCross}"
277 bottomFmt="$bottomFmt${margins:+$bottomrightCorner}"
280 topGrid=''
281 innerGrid=''
282 bottomGrid=''
283 if [ $horizontalLines ]
284 then
285 topGrid=`printf -- "$topFmt" "${segments[@]}"`
286 topGrid=${topGrid// /$horizontalBar}$'\n'
287 innerGrid=`printf -- "$innerFmt" "${segments[@]}"`
288 innerGrid=${innerGrid// /$horizontalBar}$'\n'
289 bottomGrid=`printf -- "$bottomFmt" "${segments[@]}"`
290 bottomGrid=${bottomGrid// /$horizontalBar}$'\n'
294 print_table_g()
296 line_no=1
297 while read -r line
299 if [ $line_no = 1 ]
300 then
301 echo -n "$topGrid"
302 else
303 echo -n "$innerGrid"
306 explode $'\t' "$line"
307 printf_args=()
308 for column in "${!column_width[@]}"
310 [ $column -lt ${#EXPLODE_ARRAY[@]} ] && cell=${EXPLODE_ARRAY[$column]} || cell=''
311 chars=${#cell}
312 bytes=`LANG=C; echo "${#cell}"`
313 swidth=$[ ${column_width[$column]} + ( $bytes - $chars ) ]
314 [ ${columnAlignment[$column]} = left ] && swidth=-$swidth
315 printf_args+=($swidth "$cell")
316 done
317 printf -- "$cellFmt\n" "${printf_args[@]}"
319 let line_no++
320 done <<<"$text"
322 echo -n "$bottomGrid"
326 if [ -t 1 -a -n "$PAGER" ]
327 then
328 terminal_cols=`tput cols`
329 if [ "${terminal_cols:-0}" -le $table_width ]
330 then
331 print_table_g | exec "$PAGER"
332 exit ${PIPESTATUS[1]}
336 print_table_g