forked from aristocratos/bashtop
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bashtop
executable file
·3508 lines (2913 loc) · 137 KB
/
bashtop
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env bash
# indent type=tab
# tab size=4
# shellcheck disable=SC2034 #Unused variables
# shellcheck disable=SC2068 #Double quote array warning
# shellcheck disable=SC2086 # Double quote warning
## shellcheck disable=SC2120
# shellcheck disable=SC2162 #Read without -r
# shellcheck disable=SC2206 #Word split warning
# shellcheck disable=SC2178 #Array to string warning
# shellcheck disable=SC2102 #Ranges only match single
# shellcheck disable=SC2004 #arithmetic brackets warning
# shellcheck disable=SC2017 #arithmetic precision warning
# shellcheck disable=SC2207 #split array warning
# Copyright 2020 Aristocratos
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
declare -x LC_MESSAGES="C" LC_NUMERIC="C" LC_ALL=""
shopt -qu failglob nullglob
shopt -qs extglob globasciiranges globstar
declare -a banner banner_colors
banner=(
"██████╗ █████╗ ███████╗██╗ ██╗████████╗ ██████╗ ██████╗ "
"██╔══██╗██╔══██╗██╔════╝██║ ██║╚══██╔══╝██╔═══██╗██╔══██╗"
"██████╔╝███████║███████╗███████║ ██║ ██║ ██║██████╔╝"
"██╔══██╗██╔══██║╚════██║██╔══██║ ██║ ██║ ██║██╔═══╝ "
"██████╔╝██║ ██║███████║██║ ██║ ██║ ╚██████╔╝██║ "
"╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ")
declare version="0.8.14"
declare banner_width=${#banner[0]}
banner_colors=("#E62525" "#CD2121" "#B31D1D" "#9A1919" "#801414")
#? Start default variables------------------------------------------------------------------------------>
#? These values are used to create "$HOME/.config/bashtop/bashtop.cfg"
#? Any changes made here will be ignored if config file exists
aaa_config() { : ; } #! Do not remove this line!
#* Color theme, looks for a .theme file in "$HOME/.config/bashtop/themes", "Default" for builtin default theme
color_theme="Default"
#* Update time in milliseconds, increases automatically if set below internal loops processing time, recommended 2000 ms or above for better sample times for graphs
update_ms="2500"
#* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu responsive"
#* "cpu lazy" updates sorting over time, "cpu responsive" updates sorting directly at a cpu usage cost
proc_sorting="cpu lazy"
#* Reverse sorting order, "true" or "false"
proc_reversed="false"
#* Check cpu temperature, only works if "sensors" command is available and have values for "Package" and "Core"
check_temp="true"
#* Draw a clock at top of screen, formatting according to strftime, empty string to disable
draw_clock="%X"
#* Update main ui when menus are showing, set this to false if the menus is flickering too much for comfort
background_update="true"
#* Custom cpu model name, empty string to disable
custom_cpu_name=""
#* Enable error logging to "$HOME/.config/bashtop/error.log", "true" or "false"
error_logging="true"
aaz_config() { : ; } #! Do not remove this line!
#? End default variables-------------------------------------------------------------------------------->
declare -a menu_options menu_help menu_quit
menu_options=(
"┌─┐┌─┐┌┬┐┬┌─┐┌┐┌┌─┐"
"│ │├─┘ │ ││ ││││└─┐"
"└─┘┴ ┴ ┴└─┘┘└┘└─┘")
menu_help=(
"┬ ┬┌─┐┬ ┌─┐"
"├─┤├┤ │ ├─┘"
"┴ ┴└─┘┴─┘┴ ")
menu_quit=(
"┌─┐ ┬ ┬ ┬┌┬┐"
"│─┼┐│ │ │ │ "
"└─┘└└─┘ ┴ ┴ ")
menu_options_selected=(
"╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗"
"║ ║╠═╝ ║ ║║ ║║║║╚═╗"
"╚═╝╩ ╩ ╩╚═╝╝╚╝╚═╝")
menu_help_selected=(
"╦ ╦╔═╗╦ ╔═╗"
"╠═╣║╣ ║ ╠═╝"
"╩ ╩╚═╝╩═╝╩ ")
menu_quit_selected=(
"╔═╗ ╦ ╦ ╦╔╦╗ "
"║═╬╗║ ║ ║ ║ "
"╚═╝╚╚═╝ ╩ ╩ ")
declare -A cpu mem swap proc net box theme
declare -a cpu_usage cpu_graph_a cpu_graph_b color_meter color_temp_graph color_cpu color_cpu_graph cpu_history color_mem_graph color_swap_graph
declare -a mem_history swap_history net_history_download net_history_upload mem_graph swap_graph proc_array download_graph upload_graph trace_array
declare resized=1 size_error clock tty_width tty_height hex="16#" cpu_p_box swap_on=1 draw_out esc_character boxes_out last_screen clock_out update_string
declare -a options_array=("color_theme" "update_ms" "proc_sorting" "check_temp" "draw_clock" "background_update" "error_logging" "custom_cpu_name")
declare -a save_array=("${options_array[@]}" "proc_reversed")
declare -a sorting=( "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu responsive" )
declare -a pid_history detail_graph detail_history detail_mem_history
declare time_left timestamp_start timestamp_end timestamp_input_start timestamp_input_end time_string mem_out proc_misc prev_screen pause_screen filter input_to_filter
declare no_epoch proc_det proc_misc2 sleeping=0 detail_mem_graph proc_det2 proc_out curled git_version
declare esc_character tab backspace sleepy late_update skip_process_draw winches quitting theme_int
declare -a disks_free disks_total disks_name disks_free_percent saved_key themes
printf -v esc_character "\u1b"
printf -v tab "\u09"
printf -v backspace "\u7F"
printf -v enter_key "\uA"
read tty_height tty_width < <(stty size)
#* Symbols for graphs
declare -a graph_symbol
graph_symbol=(" " "⡀" "⣀" "⣄" "⣤" "⣦" "⣴" "⣶" "⣷" "⣾" "⣿")
graph_symbol+=( " " "⣿" "⢿" "⡿" "⠿" "⠻" "⠟" "⠛" "⠙" "⠉" "⠈")
box[boxes]="cpu mem net processes"
cpu[threads]=0
#* Symbols for subscript function
subscript=("₀" "₁" "₂" "₃" "₄" "₅" "₆" "₇" "₈" "₉")
#* Symbols for create_box function
box[single_hor_line]="─"
box[single_vert_line]="│"
box[single_left_corner_up]="┌"
box[single_right_corner_up]="┐"
box[single_left_corner_down]="└"
box[single_right_corner_down]="┘"
box[single_title_left]="├"
box[single_title_right]="┤"
box[double_hor_line]="═"
box[double_vert_line]="║"
box[double_left_corner_up]="╔"
box[double_right_corner_up]="╗"
box[double_left_corner_down]="╚"
box[double_right_corner_down]="╝"
box[double_title_left]="╟"
box[double_title_right]="╢"
#* If using bash version 5, set timestamps with EPOCHREALTIME variable
if [[ -n $EPOCHREALTIME ]]; then
get_ms() { #? Set given variable to current epoch millisecond with EPOCHREALTIME varialble
local -n ms_out=$1
ms_out=$((${EPOCHREALTIME/[.,]/}/1000))
}
#* If not, use date command
else
get_ms() { #? Set given variable to current epoch millisecond with date command
local -n ms_out=$1
read ms_out < <(date +%s%3N)
}
fi
init_() { #? Collect needed information and set options before startig main loop
local i
#* Set terminal options, save and clear screen
tput smcup
stty -echo
tput civis
#* Check if "sensors" command is available, if not, disable temperature collection
if [[ $check_temp != false ]] && command -v sensors >/dev/null 2>&1; then check_temp="true"; else check_temp="false"; fi
#* Check if "curl" command is available, if not, disable update check and theme downloads
if command -v curl >/dev/null 2>&1; then curled=1; else unset curled; fi
#* Get number of cores and cpu threads
get_cpu_info
#* Get processor BCLK
local param_var
if [[ -e /usr/include/asm-generic/param.h ]]; then
param_var="$(</usr/include/asm-generic/param.h)"
get_value -v cpu[hz] -sv "param_var" -k "define HZ" -i
else
cpu[hz]="100"
fi
#* Get max pid value and length
proc[pid_max]="$(</proc/sys/kernel/pid_max)"
proc[pid_len]=${#proc[pid_max]}
if [[ ${proc[pid_len]} -lt 5 ]]; then proc[pid_len]=5; fi
#* Call init for cpu data collection
collect_cpu init
#* Call init for memory data collection and check if swap is available
mem[counter]=10
collect_mem init
#* Get default network device from "ip route" command and call init for net collection
get_value -v net[device] -ss "$(ip route get 1.1.1.1)" -k "dev" -mk 1
collect_net init
#* Check if newer version of bashtop is available from https://github.com/aristocratos/bashtop
if [[ -n $curled ]]; then
if ! get_value -v git_version -ss "$(curl -m 2 --raw -r 0-3500 https://raw.githubusercontent.com/aristocratos/bashtop/master/bashtop 2>/dev/null)" -k "version=" -r "[^0-9.]"; then unset git_version; fi
fi
#* Draw banner to banner array
local letter b_color banner_line y=0
local -a banner_out
#print -v banner_out[0] -t "\e[0m"
for banner_line in "${banner[@]}"; do
#* Read banner array letter by letter to set correct color for filled vs outline characters
while read -rN1 letter; do
if [[ $letter == "█" ]]; then b_color="${banner_colors[$y]}"
else b_color=$((80-y*6)); fi
if [[ $letter == " " ]]; then
print -v banner_out[y] -r 1
else
print -v banner_out[y] -fg ${b_color} "${letter}"
fi
done <<<"$banner_line"
((++y))
done
print -v banner_out[y] -rs -fg cc -b "← esc"
if [[ -n $git_version && $git_version != "$version" ]]; then print -v banner_out[y] -rs -fg "#80cc80" -r 15 "[${git_version} available!]" -r $((9-${#git_version}))
else print -v banner_out[y] -r 37; fi
print -v banner_out[y] -fg cc -i -b "Version: ${version}" -rs
unset 'banner[@]'
banner=("${banner_out[@]}")
#* Get theme and set colors
color_init_
#* Set up internals for quick processes sorting switching
for((i=0;i<${#sorting[@]};i++)); do
if [[ ${sorting[i]} == "${proc_sorting}" ]]; then
proc[sorting_int]=$i
break
fi
done
if [[ -z ${proc[sorting_int]} ]]; then
proc[sorting_int]=0
proc_sorting="${sorting[0]}"
fi
if [[ ${proc_reversed} == true ]]; then
proc[reverse]="+"
else
unset 'proc[reverse]'
fi
#* Wait for resize if terminal size is smaller then 80x25
if (($tty_width<80 | $tty_height<25)); then resized; fi
#* Calculate sizes of boxes
calc_sizes
#* Call init for processes data collection
proc[selected]=0
proc[page]=1
collect_processes init
}
color_init_() { #? Check for theme file and set colors
local main_bg="" main_fg="#cc" title="#ee" hi_fg="#90" inactive_fg="#40" cpu_box="#3d7b46" mem_box="#8a882e" net_box="#423ba5" proc_box="#923535" proc_misc="#0de756" selected_bg="#7e2626" selected_fg="#ee"
local temp_start="#4897d4" temp_mid="#5474e8" temp_end="#ff40b6" cpu_start="#50f095" cpu_mid="#f2e266" cpu_end="#fa1e1e" div_line="#30"
local free_start="#223014" free_mid="#b5e685" free_end="#dcff85" cached_start="#0b1a29" cached_mid="#74e6fc" cached_end="#26c5ff" available_start="#292107" available_mid="#ffd77a" available_end="#ffb814"
local used_start="#3b1f1c" used_mid="#d9626d" used_end="#ff4769" download_start="#231a63" download_mid="#4f43a3" download_end="#b0a9de" upload_start="#510554" upload_mid="#7d4180" upload_end="#dcafde"
local hex2rgb color_name array_name this_color main_fg_dec sourced theme_unset
local -i i y
local -A rgb
local -a dec_test
local -a convert_color=("main_bg" "temp_start" "temp_mid" "temp_end" "cpu_start" "cpu_mid" "cpu_end" "upload_start" "upload_mid" "upload_end" "download_start" "download_mid" "download_end" "used_start" "used_mid" "used_end" "available_start" "available_mid" "available_end" "cached_start" "cached_mid" "cached_end" "free_start" "free_mid" "free_end" "proc_misc" "main_fg_dec")
for theme_unset in ${!theme[@]}; do
unset 'theme[${theme_unset}]'
done
#* Check if theme set in config exists and source it if it does
if [[ -n ${color_theme} && ${color_theme} != "Default" && -e "${theme_dir}/${color_theme%.theme}.theme" ]]; then
# shellcheck source=/dev/null
source "${theme_dir}/${color_theme%.theme}.theme"
sourced=1
else
color_theme="Default"
fi
main_fg_dec="${theme[main_fg]:-$main_fg}"
theme[main_fg_dec]="${main_fg_dec}"
#* Convert colors for graphs and meters from rgb hexadecimal to rgb decimal if needed
for color_name in ${convert_color[@]}; do
if [[ -n $sourced ]]; then hex2rgb="${theme[${color_name}]}"
else hex2rgb="${!color_name}"; fi
hex2rgb=${hex2rgb//#/}
if [[ ${#hex2rgb} == 6 ]] && is_hex "$hex2rgb"; then hex2rgb="$((${hex}${hex2rgb:0:2})) $((${hex}${hex2rgb:2:2})) $((${hex}${hex2rgb:4:2}))"
elif [[ ${#hex2rgb} == 2 ]] && is_hex "$hex2rgb"; then hex2rgb="$((${hex}${hex2rgb:0:2})) $((${hex}${hex2rgb:0:2})) $((${hex}${hex2rgb:0:2}))"
else
dec_test=(${hex2rgb})
if [[ ${#dec_test[@]} -eq 3 ]] && is_int "${dec_test[@]}"; then hex2rgb="${dec_test[*]}"
else unset hex2rgb; fi
fi
theme[${color_name}]="${hex2rgb}"
done
#* Set background color if set, otherwise use terminal default
if [[ -n ${theme[main_bg]} ]]; then theme[main_bg_dec]="${theme[main_bg]}"; theme[main_bg]=";48;2;${theme[main_bg]// /;}"; fi
#* Set colors from theme file if found, otherwise use default values
theme[main_fg]="${theme[main_fg]:-$main_fg}"
theme[title]="${theme[title]:-$title}"
theme[hi_fg]="${theme[hi_fg]:-$hi_fg}"
theme[div_line]="${theme[div_line]:-$div_line}"
theme[inactive_fg]="${theme[inactive_fg]:-$inactive_fg}"
theme[selected_fg]="${theme[selected_fg]:-$selected_fg}"
theme[selected_bg]="${theme[selected_bg]:-$selected_bg}"
box[cpu_color]="${theme[cpu_box]:-$cpu_box}"
box[mem_color]="${theme[mem_box]:-$mem_box}"
box[net_color]="${theme[net_box]:-$net_box}"
box[processes_color]="${theme[proc_box]:-$proc_box}"
#* Create color arrays from one, two or three color gradient, 100 values in each
for array_name in "temp" "cpu" "upload" "download" "used" "available" "cached" "free"; do
local -n color_array="color_${array_name}_graph"
local -a rgb_start=(${theme[${array_name}_start]}) rgb_mid=(${theme[${array_name}_mid]}) rgb_end=(${theme[${array_name}_end]})
local pf_calc middle=1
rgb[red]=${rgb_start[0]}; rgb[green]=${rgb_start[1]}; rgb[blue]=${rgb_start[2]}
if [[ -z ${rgb_mid[*]} ]] && ((rgb_end[0]+rgb_end[1]+rgb_end[2]>rgb_start[0]+rgb_start[1]+rgb_start[2])); then
rgb_mid=( $((rgb_end[0]/2)) $((rgb_end[1]/2)) $((rgb_end[2]/2)) )
elif [[ -z ${rgb_mid[*]} ]]; then
rgb_mid=( $((rgb_start[0]/2)) $((rgb_start[1]/2)) $((rgb_start[2]/2)) )
fi
for((i=0;i<=100;i++,y=0)); do
if [[ -n ${rgb_end[*]} ]]; then
for this_color in "red" "green" "blue"; do
if ((i==50)); then rgb_start[y]=${rgb[$this_color]}; fi
if ((middle==1 & rgb[$this_color]<rgb_mid[y])); then
printf -v pf_calc "%.0f" "$(( i*( (rgb_mid[y]-rgb_start[y])*100/50*100) ))e-4"
elif ((middle==1 & rgb[$this_color]>rgb_mid[y])); then
printf -v pf_calc "%.0f" "-$(( i*( (rgb_start[y]-rgb_mid[y])*100/50*100) ))e-4"
elif ((middle==0 & rgb[$this_color]<rgb_end[y])); then
printf -v pf_calc "%.0f" "$(( (i-50)*( (rgb_end[y]-rgb_start[y])*100/50*100) ))e-4"
elif ((middle==0 & rgb[$this_color]>rgb_end[y])); then
printf -v pf_calc "%.0f" "-$(( (i-50)*( (rgb_start[y]-rgb_end[y])*100/50*100) ))e-4"
else
pf_calc=0
fi
rgb[$this_color]=$((rgb_start[y]+pf_calc))
if ((rgb[$this_color]<0)); then rgb[$this_color]=0
elif ((rgb[$this_color]>255)); then rgb[$this_color]=255; fi
y+=1
if ((i==49 & y==3 & middle==1)); then middle=0; fi
done
fi
color_array[i]="${rgb[red]} ${rgb[green]} ${rgb[blue]}"
done
done
}
quit_() { #? Clean exit
#* Restore terminal options and screen
tput rmcup
stty echo
tput cnorm
#* Save any changed values to config file
if [[ $config_file != "/dev/null" ]]; then
save_config "${save_array[@]}"
fi
exit 0
}
sleep_() { #? Restore terminal options, stop and send to background if caught SIGTSTP (ctrl+z)
tput rmcup
stty echo
tput cnorm
kill -s SIGSTOP $$
}
resume_() { #? Set terminal options and resume if caught SIGCONT ('fg' from terminal)
sleepy=0
tput smcup
stty -echo
tput civis
if [[ -n $pause_screen ]]; then
echo -en "$pause_screen"
else
echo -en "${boxes_out}${proc_det}${last_screen}${mem_out}${proc_misc}${proc_misc2}${update_string}${clock_out}"
fi
}
traperr() { #? Function for reporting error line numbers
local match len trap_muted err="${BASH_LINENO[0]}"
len=$((${#trace_array[@]}))
if ((len-->=1)); then
while ((len>=${#trace_array[@]}-2)); do
if [[ $err == "${trace_array[$((len--))]}" ]]; then ((++match)) ; fi
done
if ((match==2 & len != -2)); then return
elif ((match>=1)); then trap_muted="(MUTED!)"
fi
fi
if ((len>100)); then unset 'trace_array[@]'; fi
trace_array+=("$err")
echo "$(printf "%(%X)T") ERROR: On line $err $trap_muted" >> "${config_dir}/error.log"
}
resized() { #? Get new terminal size if terminal is resized
resized=1
unset winches
while ((++winches<5)); do
read tty_height tty_width < <(stty size)
if (($tty_width<80 | $tty_height<25)); then
size_error_msg
winches=0
else
create_box -w 30 -h 3 -c 1 -l 1 -lc "#EE2020" -title "resizing"
print -jc 28 -fg ${theme[title]} "New size: ${tty_width}x${tty_height}"
sleep 0.2
if [[ $(stty size) != "$tty_height $tty_width" ]]; then winches=0; fi
fi
done
}
size_error_msg() { #? Shows error message if terminal size is below 80x25
local width=$tty_width
local height=$tty_height
tput clear
create_box -full -lc "#EE2020" -title "resize window"
print -rs -m $((tty_height/2-1)) 2 -fg ${theme[title]} -c -l 11 "Current size: " -bg "#00" -fg dd2020 -d 1 -c "${tty_width}x${tty_height}" -rs
print -d 1 -fg ${theme[title]} -c -l 15 "Need to be atleast:" -bg "#00" -fg 30dd50 -d 1 -c "80x25" -rs
while [[ $(stty size) == "$tty_height $tty_width" ]]; do sleep 0.2; done
}
draw_banner() { #? Draw banner, usage: draw_banner <line> [output variable]
local y letter b_color x_color xpos ypos=$1 banner_out
if [[ -n $2 ]]; then local -n banner_out=$2; fi
xpos=$(( (tty_width/2)-(banner_width/2) ))
for banner_line in "${banner[@]}"; do
print -v banner_out -rs -move $((ypos+++y)) $xpos -t "${banner_line}"
done
if [[ -z $2 ]]; then echo -en "${banner_out}"; fi
}
create_config() { #? Creates a new config file with default values from above
local c_line c_read this_file
this_file="$(realpath "$0")"
echo "#? Config file for bashtop v. ${version}" > "$config_file"
while IFS= read -r c_line; do
if [[ $c_line =~ aaz_config() ]]; then break
elif [[ $c_read == "1" ]]; then echo "$c_line" >> "$config_file"
elif [[ $c_line =~ aaa_config() ]]; then c_read=1; fi
done < "$this_file"
}
save_config() { #? Saves variables to config file if not same, usage: save_config "var1" ["var2"] ["var3"]...
if [[ -z $1 || $config_file == "/dev/null" ]]; then return; fi
local var tmp_conf tmp_value quote original new
tmp_conf="$(<"$config_file")"
for var in "$@"; do
if [[ ${tmp_conf} =~ ${var} ]]; then
get_value -v "tmp_value" -sv "tmp_conf" -k "${var}="
if [[ ${tmp_value//\"/} != "${!var}" ]]; then
original="${var}=${tmp_value}"
new="${var}=\"${!var}\""
sed -i "s/${original}/${new}/" "${config_file}"
fi
else
echo "${var}=\"${!var}\"" >> "$config_file"
fi
done
}
set_font() { #? Take a string and generate a string of unicode characters of given font, usage; set_font "font-name [bold] [italic]" "string"
local i letter letter_hex new_hex add_hex start font="$1" string_in="$2" string_out hex="16#"
if [[ -z $font || -z $string_in ]]; then return; fi
case "$font" in
"sans-serif") lower_start="1D5BA"; upper_start="1D5A0"; digit_start="1D7E2";;
"sans-serif bold") lower_start="1D5EE"; upper_start="1D5D4"; digit_start="1D7EC";;
"sans-serif italic") lower_start="1D622"; upper_start="1D608"; digit_start="1D7E2";;
#"sans-serif bold italic") start="1D656"; upper_start="1D63C"; digit_start="1D7EC";;
"script") lower_start="1D4B6"; upper_start="1D49C"; digit_start="1D7E2";;
"script bold") lower_start="1D4EA"; upper_start="1D4D0"; digit_start="1D7EC";;
"fraktur") lower_start="1D51E"; upper_start="1D504"; digit_start="1D7E2";;
"fraktur bold") lower_start="1D586"; upper_start="1D56C"; digit_start="1D7EC";;
"monospace") lower_start="1D68A"; upper_start="1D670"; digit_start="1D7F6";;
"double-struck") lower_start="1D552"; upper_start="1D538"; digit_start="1D7D8";;
*) echo -n "${string_in}"; return;;
esac
for((i=0;i<${#string_in};i++)); do
letter=${string_in:i:1}
if [[ $letter =~ [a-z] ]]; then #61
printf -v letter_hex '%X\n' "'$letter"
printf -v add_hex '%X' "$((${hex}${letter_hex}-${hex}61))"
printf -v new_hex '%X' "$((${hex}${lower_start}+${hex}${add_hex}))"
string_out="${string_out}\U${new_hex}"
#if [[ $font =~ sans-serif && $letter =~ m|w ]]; then string_out="${string_out} "; fi
#\U205F
elif [[ $letter =~ [A-Z] ]]; then #41
printf -v letter_hex '%X\n' "'$letter"
printf -v add_hex '%X' "$((${hex}${letter_hex}-${hex}41))"
printf -v new_hex '%X' "$((${hex}${upper_start}+${hex}${add_hex}))"
string_out="${string_out}\U${new_hex}"
#if [[ $font =~ sans-serif && $letter =~ M|W ]]; then string_out="${string_out} "; fi
elif [[ $letter =~ [0-9] ]]; then #30
printf -v letter_hex '%X\n' "'$letter"
printf -v add_hex '%X' "$((${hex}${letter_hex}-${hex}30))"
printf -v new_hex '%X' "$((${hex}${digit_start}+${hex}${add_hex}))"
string_out="${string_out}\U${new_hex}"
else
string_out="${string_out} \e[1D${letter}"
fi
done
echo -en "${string_out}"
}
sort_array_int() { #? Copy and sort an array of integers from largest to smallest value, usage: sort_array_int "input array" "output array"
#* Return if given array has no values
if [[ -z ${!1} ]]; then return; fi
local start_n search_n tmp_array
#* Create pointers to arrays
local -n in_arr="$1"
local -n out_arr="$2"
#* Create local copy of array
local array=("${in_arr[@]}")
#* Start sorting
for ((start_n=0;start_n<=${#array[@]}-1;++start_n)); do
for ((search_n=start_n+1;search_n<=${#array[@]}-1;++search_n)); do
if ((array[start_n]<array[search_n])); then
tmp_array=${array[start_n]}
array[start_n]=${array[search_n]}
array[search_n]=$tmp_array
fi
done
done
#* Write the sorted array to output array
out_arr=("${array[@]}")
}
subscript() { #? Convert an integer to a string of subscript numbers
local i out int=$1
for((i=0;i<${#int};i++)); do
out="${out}${subscript[${int:$i:1}]}"
done
echo -n "${out}"
}
spaces() { #? Prints back spaces, usage: spaces "number of spaces"
printf "%${1}s" ""
}
is_int() { #? Check if value(s) is integer
local param
for param; do
if [[ ! $param =~ ^[\-]?[0-9]+$ ]]; then return 1; fi
done
}
is_float() { #? Check if value(s) is floating point
local param
for param; do
if [[ ! $param =~ ^[\-]?[0-9]*[,.][0-9]+$ ]]; then return 1; fi
done
}
is_hex() { #? Check if value(s) is hexadecimal
local param
for param; do
if [[ ! ${param//#/} =~ ^[0-9a-fA-F]*$ ]]; then return 1; fi
done
}
floating_humanizer() { #? Convert integer to floating point and scale up in steps of 1024 to highest positive unit
#? Usage: floating_humanizer <-b,-bit|-B,-Byte> [-ps,-per-second] [-s,-start "1024 multiplier start"] [-v,-variable-output] <input>
local value selector per_second unit_mult decimals out_var ext_var
local -a unit
until (($#==0)); do
case "$1" in
-b|-bit) unit=(bit Kib Mib Gib Tib Pib); unit_mult=8;;
-B|-Byte) unit=(Byte KiB MiB GiB TiB PiB); unit_mult=1;;
-ps|-per-second) per_second=1;;
-s|-start) selector="$2"; shift;;
-v|-variable-output) local -n out_var="$2"; ext_var=1; shift;;
*) if is_int "$1"; then value=$1; break; fi;;
esac
shift
done
if [[ -z $value || $value -lt 0 || -z $unit_mult ]]; then return; fi
if ((per_second==1 & unit_mult==1)); then per_second="/s"
elif ((per_second==1)); then per_second="ps"; fi
if ((value>0)); then
value=$((value*100*unit_mult))
until ((${#value}<6)); do
value=$((value>>10))
((++selector))
done
if ((${#value}<5 & ${#value}>=2 & selector>0)); then
decimals=$((5-${#value}))
value="${value::-2}.${value:(-${decimals})}"
elif ((${#value}>=2)); then
value="${value::-2}"
fi
fi
out_var="${value} ${unit[$selector]}${per_second}"
if [[ -z $ext_var ]]; then echo -n "${out_var}"; fi
}
get_cpu_info() {
local lscpu_var param_var
lscpu_var="$(lscpu)"
if [[ -z ${cpu[threads]} || -z ${cpu[cores]} ]]; then
get_value -v cpu[threads] -sv "lscpu_var" -k "CPU(s):" -i
get_value -v cpu[cores] -sv "lscpu_var" -k "Core(s)" -i
fi
if [[ -z $custom_cpu_name ]]; then
if ! get_value -v cpu[model] -sv "lscpu_var" -k "Model name:" -a -b -k "CPU" -mk -1; then
get_value -v cpu[model] -sv "lscpu_var" -k "Model name:" -r " "
fi
else
cpu[model]="${custom_cpu_name}"
fi
}
get_value() { #? Get a value from a file, variable or array by searching for a non spaced "key name" on the same line
local match line_pos=1 int reg key all tmp_array input found input_line line_array line_val ext_var line_nr current_line match_key math removing ext_arr
local -a remove
until (($#==0)); do
until (($#==0)); do
case "$1" in
-k|-key) key="$2"; shift;; #? Key "string" on the same line as target value
-m|-match) match="$2"; shift;; #? If multiple matches on a line, match occurrence "x"
-mk|-match-key) match_key=$2; line_pos=0; shift;; #? Match in relation to key position, -1 for previous value, 1 for next value
-b|-break) shift; break;; #? Break up arguments for multiple searches
-a|-all) all=1;; #? Prints back found line including key
-l|-line) line_nr="$2"; shift;; #? Set target line if no key is available
-ss|-source-string) input="$2"; shift;; #? Argument string as source
-sf|-source-file) input="$(<"$2")"; shift;; #? File as source
-sv|-source-var) input="${!2}"; shift;; #? Variable as source
-sa|-source-array) local -n tmp_array=$2; input="${tmp_array[*]}"; shift;; #? Array as source
-fp|-floating-point) reg="[\-]?[0-9]*[.,][0-9]+"; match=1;; #? Match floating point value
-math) math="$2"; shift;; #? Perform math on a integer value, "x" represents value, only works if "integer" argument is given
-i|-integer) reg="[\-]?[0-9]+[.,]?[0-9]*"; int=1; match=1;; #? Match integer value or float and convert to int
-r|-remove) remove+=("$2"); shift;; #? Format output by removing entered regex, can be used multiple times
-v|-variable-out) local -n found="$2"; ext_var=1; shift;; #? Output to variable
-map|-map-array) local -n array_out="$2"; ext_var=1; ext_arr=1; shift;; #? Map output to array
esac
shift
done
if [[ -z $input ]]; then return 1; fi
if [[ -z $line_nr && -z $key ]]; then line_nr=1; fi
while IFS='' read -r input_line; do
((++current_line))
if [[ -n $line_nr && $current_line -eq $line_nr || -z $line_nr && -n $key && ${input_line/${key}/} != "$input_line" ]]; then
if [[ -n $all ]]; then
found="${input_line}"
break
elif [[ -z $match && -z $match_key && -z $reg ]]; then
found="${input_line/${key}/}"
break
else
line_array=(${input_line/${key}/${key// /}})
fi
for line_val in "${line_array[@]}"; do
if [[ -n $match_key && $line_val == "${key// /}" ]]; then
if ((match_key<0 & line_pos+match_key>=0)) || ((match_key>=0 & line_pos+match_key<${#line_array[@]})); then
found="${line_array[$((line_pos+match_key))]}"
break 2
else
return 1
fi
elif [[ -n $match_key ]]; then
((++line_pos))
elif [[ -n $reg && $line_val =~ ^${reg}$ || -z $reg && -n $match ]]; then
if ((line_pos==match)); then
found=${line_val}
break 2
fi
((++line_pos))
fi
done
fi
done <<<"${input}"
if [[ -z $found ]]; then return 1; fi
if [[ -n ${remove[*]} ]]; then
for removing in "${remove[@]}"; do
found="${found//${removing}/}"
done
fi
if [[ -n $int && $found =~ [.,] ]]; then
found="${found/,/.}"
printf -v found "%.0f" "${found}"
fi
if [[ -n $math && -n $int ]]; then
math="${math//x/$found}"
found=$((${math}))
fi
if (($#>0)); then
input="${found}"
unset key match match_key all reg found int 'remove[@]' current_line
line_pos=1
fi
done
if [[ -z $ext_var ]]; then echo "${found}"; fi
if [[ -n $ext_arr ]]; then array_out=(${found}); fi
}
get_themes() {
local file
theme_int=0
themes=("Default")
for file in "${theme_dir}"/*.theme; do
file="${file##*/}"
if [[ ${file} != "*.theme" ]]; then themes+=("${file%.theme}"); fi
if [[ ${themes[-1]} == "${color_theme}" ]]; then theme_int=$i; fi
((i++))
done
}
cur_pos() { #? Get cursor postion, argument "line" prints current line, argument "col" prints current column, no argument prints both in format "line column"
local line col
IFS=';' read -sdR -p $'\E[6n' line col
if [[ -z $1 || $1 == "line" ]]; then echo -n "${line#*[}${1:-" "}"; fi
if [[ -z $1 || $1 == "col" ]]; then echo -n "$col"; fi
}
create_box() { #? Draw a box with an optional title at given location
local width height col line title ltype hpos vpos i hlines vlines color line_color c_rev=0 box_out ext_var fill
until (($#==0)); do
case $1 in
-f|-full) col=1; line=1; width=$((tty_width)); height=$((tty_height));; #? Use full terminal size for box
-c|-col) if is_int "$2"; then col=$2; shift; fi;; #? Column position to start box
-l|-line) if is_int "$2"; then line=$2; shift; fi;; #? Line position to start box
-w|-width) if is_int "$2"; then width=$2; shift; fi;; #? Width of box
-h|-height) if is_int "$2"; then height=$2; shift; fi;; #? Height of box
-t|-title) if [[ -n $2 ]]; then title="$2"; shift; fi;; #? Draw title without titlebar
-s|-single) ltype="single";; #? Use single lines
-d|-double) ltype="double";; #? Use double lines
-lc|-line-color) line_color="$2"; shift;; #? Color of the lines
-fill) fill=1;; #? Fill background of box
-v|-variable) local -n box_out=$2; ext_var=1; shift;; #? Output box to a variable
esac
shift
done
if [[ -z $col || -z $line || -z $width || -z $height ]]; then return; fi
ltype=${ltype:-"single"}
vlines+=("$col" "$((col+width-1))")
hlines+=("$line" "$((line+height-1))")
print -v box_out -rs
#* Fill box if enabled
if [[ -n $fill ]]; then
for((i=line+1;i<line+height-1;i++)); do
print -v box_out -m $i $((col+1)) -rp $((width-2)) -t " "
done
fi
#* Draw all horizontal lines
print -v box_out -fg ${line_color:-${theme[div_line]}}
for hpos in "${hlines[@]}"; do
print -v box_out -m $hpos $col -rp $((width-1)) -t "${box[${ltype}_hor_line]}"
done
#* Draw all vertical lines
for vpos in "${vlines[@]}"; do
print -v box_out -m $line $vpos
for((hpos=line;hpos<=line+height-1;hpos++)); do
print -v box_out -m $hpos $vpos -t "${box[${ltype}_vert_line]}"
done
done
#* Draw corners
print -v box_out -m $line $col -t "${box[${ltype}_left_corner_up]}"
print -v box_out -m $line $((col+width-1)) -t "${box[${ltype}_right_corner_up]}"
print -v box_out -m $((line+height-1)) $col -t "${box[${ltype}_left_corner_down]}"
print -v box_out -m $((line+height-1)) $((col+width-1)) -t "${box[${ltype}_right_corner_down]}"
#* Draw small title without titlebar
if [[ -n $title ]]; then
print -v box_out -m $line $((col+2)) -t "┤" -fg ${theme[title]} -b -t "$title" -rs -fg ${line_color:-${theme[div_line]}} -t "├"
fi
print -v box_out -rs -m $((line+1)) $((col+1))
if [[ -z $ext_var ]]; then echo -en "${box_out}"; fi
}
create_meter() { #? Create a horizontal percentage meter, usage; create_meter <value 0-100>
#? Optional arguments: [-p, -place <line> <col>] [-w, -width <columns>] [-f, -fill-empty]
#? [-c, -color "array-name"] [-i, -invert-color] [-v, -variable "variable-name"]
if [[ -z $1 ]]; then return; fi
local val width colors color block="■" i fill_empty col line var ext_var out meter_var print_var invert bg_color=${theme[inactive_fg]}
#* Argument parsing
until (($#==0)); do
case $1 in
-p|-place) if is_int "${@:2:2}"; then line=$2; col=$3; shift 2; fi;; #? Placement for meter
-w|-width) width=$2; shift;; #? Width of meter in columns
-c|-color) local -n colors=$2; shift;; #? Name of an array containing colors from index 0-100
-i|-invert) invert=1;; #? Invert meter
-f|-fill-empty) fill_empty=1;; #? Fill unused space with dark blocks
-v|-variable) local -n meter_var=$2; ext_var=1; shift;; #? Output meter to a variable
*) if is_int "$1"; then val=$1; fi;;
esac
shift
done
if [[ -z $val ]]; then return; fi
#* Set default width if not given
width=${width:-10}
#* If no color array was given, create a simple greyscale array
if [[ -z $colors ]]; then
for ((i=0,ic=50;i<=100;i++,ic=ic+2)); do
colors[i]="${ic} ${ic} ${ic}"
done
fi
#* Create the meter
meter_var=""
if [[ -n $line && -n $col ]]; then print -v meter_var -rs -m $line $col
else print -v meter_var -rs; fi
if [[ -n $invert ]]; then print -v meter_var -r $((width+1)); fi
for((i=1;i<=width;i++)); do
if [[ -n $invert ]]; then print -v meter_var -l 2; fi
if ((val>=i*100/width)); then
print -v meter_var -fg ${colors[$((i*100/width))]} -t "${block}"
elif ((fill_empty==1)); then
if [[ -n $invert ]]; then print -v meter_var -l $((width-i)); fi
print -v meter_var -fg $bg_color -rp $((1+width-i)) -t "${block}"; break
else
if [[ -n $invert ]]; then break; print -v meter_var -l $((1+width-i))
else print -v meter_var -r $((1+width-i)); break; fi
fi
done
if [[ -z $ext_var ]]; then echo -en "${meter_var}"; fi
}
create_graph() { #? Create a graph from an array of percentage values, usage; create_graph <options> <value-array>
#? Create a graph from an array of non percentage values: create_graph <options> <-max "max value"> <value-array>
#? Add a value to existing graph; create_graph [-i, -invert] [-max "max value"] -add-value "graph_array" <value>
#? Add last value from an array to existing graph; create_graph [-i, -invert] [-max "max value"] -add-last "graph_array" "value-array"
#? Options: < -d, -dimensions <line> <col> <height> <width> > [-i, -invert] [-n, -no-guide] [-c, -color "array-name"] [-o, -output-array "variable-name"]
if [[ -z $1 ]]; then return; fi
local val col s_col line s_line height s_height width s_width colors color i var ext_var out side_num side_nums=1 add add_array invert no_guide max
local -a graph_array input_array
#* Argument parsing
until (($#==0)); do
case $1 in
-d|-dimensions) if is_int "${@:2:4}"; then line=$2; col=$3; height=$4; width=$5; shift 4; fi;; #? Graph dimensions
-c|-color) local -n colors=$2; shift;; #? Name of an array containing colors from index 0-100
-o|-output-array) local -n output_array=$2; ext_var=1; shift;; #? Output meter to an array
-add-value) if is_int "$3"; then local -n output_array=$2; add=$3; break; else return; fi;; #? Add a value to existing graph
-add-last) local -n output_array=$2; local -n add_array=$3; add=${add_array[-1]}; break;; #? Add last value from array to existing graph
-i|-invert) invert=1;; #? Invert graph, drawing from top to bottom
-n|-no-guide) no_guide=1;; #? Don't print side and bottom guide lines
-max) if is_int "$2"; then max=$2; shift; fi;; #? Needed max value for non percentage arrays
*) local -n tmp_in_array=$1; input_array=("${tmp_in_array[@]}");;
esac
shift
done
if [[ -z $no_guide ]]; then
((--height))
else
if [[ -n $invert ]]; then ((line--)); fi
fi
if ((width<3)); then width=3; fi
if ((height<1)); then height=1; fi
#* If argument "add" was passed check for existing graph and make room for new value(s)
local add_start add_end
if [[ -n $add ]]; then
local cut_left search
if [[ -n ${input_array[0]} ]]; then return; fi
if [[ -n $output_array ]]; then
graph_array=("${output_array[@]}")
if [[ -z ${graph_array[0]} ]]; then return; fi
else
return
fi
height=$((${#graph_array[@]}-1))
input_array[0]=${add}
#* Remove last value in current graph
for ((i=0;i<height;i++)); do
cut_left="${graph_array[i]%m*}"
search=$((${#cut_left}+1))
graph_array[i]="${graph_array[i]::$search}${graph_array[i]:$((search+1))}"
done
fi
#* Initialize graph if no "add" argument was given
if [[ -z $add ]]; then
#* Scale down graph one line if height is even
local inv_offset h_inv normal_vals=1
local -a side_num=(100 0) g_char=(" ⡇" " ⠓" "⠒") g_index
if [[ -n $invert ]]; then
for((i=height;i>=0;i--)); do
g_index+=($i)
done
else
for((i=0;i<=height;i++)); do
g_index+=($i)
done
fi
if [[ -n $no_guide ]]; then unset normal_vals
elif [[ -n $invert ]]; then g_char=(" ⡇" " ⡤" "⠤")
fi
#* Set up graph array print side numbers and lines
print -v graph_array[0] -rs
print -v graph_array[0] -m $((line+g_index[0])) ${col} ${normal_vals:+-jr 3 -fg ee -b -t "${side_num[0]}" -rs -fg ${theme[main_fg]} -t "${g_char[0]}"} -fg ${colors[100]}
for((i=1;i<height;i++)); do
print -v graph_array[i] -m $((line+g_index[i])) ${col} ${normal_vals:+-r 3 -fg ${theme[main_fg]} -t "${g_char[0]}"} -fg ${colors[$((100-i*100/height))]}
done