forked from Botspot/pi-apps
-
Notifications
You must be signed in to change notification settings - Fork 0
/
api
executable file
·2530 lines (2026 loc) · 109 KB
/
api
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
#!/bin/bash
#This script is the backbone of Pi-Apps. It hosts functions that all other scripts depend upon.
#By default, this file is hard to read. But there's an easy way to fix that. In your code editor, find the option to "Fold All".
#In Geany, this option can be found in the Document toolbar.
#Botspot has found it helpful to create a keyboard shortcut to do this quickly. You may find it helpful too.
#output functions below
error() { #red text and exit 1
echo -e "\e[91m$1\e[0m" 1>&2
exit 1
}
warning() { #yellow text
echo -e "\e[93m\e[5m◢◣\e[25m WARNING: $1\e[0m" 1>&2
}
status() { #cyan text to indicate what is happening
#detect if a flag was passed, and if so, pass it on to the echo command
if [[ "$1" == '-'* ]] && [ ! -z "$2" ];then
echo -e $1 "\e[96m$2\e[0m" 1>&2
else
echo -e "\e[96m$1\e[0m" 1>&2
fi
}
status_green() { #announce the success of a major action
echo -e "\e[92m$1\e[0m" 1>&2
}
generate_logo() { #display colorized Pi-Apps logo in terminal
#ANSI color codes: https://misc.flogisoft.com/bash/tip_colors_and_formatting
#Search for unicode characters: https://unicode-search.net - search for "block" and "quadrant"
# foreground colors
blue='\e[38;5;21m' #blue='\e[38;5;27m' #lighter
green='\e[38;5;46m'
darkgreen='\e[38;5;34m'
red='\e[38;5;197m'
white='\e[97m'
black='\e[30m'
default='\e[39m'
# background colors
bg_default='\e[49m'
bg_black='\e[40m'
bg_white='\e[107m'
#use simpler logo if OS is Buster or lower - to fix issue https://github.com/Botspot/pi-apps/issues/1441
local version_id="$(grep 'VERSION_ID=' /etc/os-release | tr -cd '0123456789.')"
if [[ "$version_id" != *\.* ]] && [ "$version_id" -ge 11 ];then
echo -e "${bg_default} ${green}🭊${darkgreen}▅▅▅▅${green}🬿${default} ${darkgreen} ${default}
${blue}🭇🬭🬭${green}█${blue}🬭🬭🬭🬭${green}█${blue}🬭🬭🬼${default} ${darkgreen} ${default}
${blue}🮉${bg_white} ${black}${bg_white}🬞🬭🬭🬭🬭🬏${blue} ${bg_default}▋${default} █▀▀🭍 ▄ ${darkgreen} ${black} ${darkgreen} ${black} ${darkgreen} ${default}
${blue}🮉${bg_white} ${black}${bg_white}▐${red}${bg_black}🬰🬠🬐🬰${black}${bg_white}▌${bg_white}${blue} ${bg_default}▋${default} █▄▄🭞 ▄ ${blue}▄▄${default} 🭂▀▀█ █▀▀🭍 █▀▀🭍 🭂🬰🬰🬰 ${darkgreen} ${black} ${darkgreen} ${black} ${darkgreen} ${default}
${blue}🮉${bg_white} ${black}${bg_white}▐${red}${bg_black}🬋🬇🬃🬋${black}${bg_white}▌${bg_white}${blue} ${bg_default}▋${default} █ █ 🭓▄▄█ █▄▄🭞 █▄▄🭞 ▄▄▄🭞 ${darkgreen} ${black} ${darkgreen} ${default}
${blue}🭦${bg_white}🭏🬭🬭🬭🬭🬭🬭🬭🬭🭄${bg_default}🭛${default} █ █ ${darkgreen} ${black} ${darkgreen} ${default}
\e[0m ${darkgreen} ${black} ${darkgreen} ${default}
${darkgreen} ${black} ${darkgreen} ${black} ${darkgreen} ${default}"
else
echo -e "${white}${bg_default} ${green}▅${darkgreen}▅▅▅▅${green}▅${default} ${darkgreen} ${default}
${blue}▂▂▂${green}█${blue}▂▂▂▂${green}█${blue}▂▂▂${white}${default} ${darkgreen} ${default}
${bg_white}${blue}▌ ${red}${bg_black}▗▖▗▖▗▖${bg_white}${blue} ▐${default}${bg_default} █▀▀▆ ▄ ${darkgreen} ${black} ${darkgreen} ${black} ${darkgreen} ${default}
${bg_white}${blue}▌ ${red}${bg_black}▗▖▗▖▗▖${bg_white}${blue} ▐${default}${bg_default} █▄▄\e[7m▂\e[27m ▄ ${blue}▄▄${default} ▆▀▀█ █▀▀▆ █▀▀▆ ▆\e[7m━━━\e[27m ${darkgreen} ${black} ${darkgreen} ${black} ${darkgreen} ${default}
${bg_white}${blue}▌ ${red}${bg_black}▗▖▗▖▗▖${bg_white}${blue} ▐${default}${bg_default} █ █ \e[7m▂\e[27m▄▄█ █▄▄\e[7m▂\e[27m █▄▄\e[7m▂\e[27m ▄▄▄\e[7m▂\e[27m ${darkgreen} ${black} ${darkgreen} ${default}
${blue}${bg_default}\e[7m▂\e[27m${bg_white}▃▃▃▃▃▃▃▃▃▃${bg_default}\e[7m▂\e[27m${default} █ █ ${darkgreen} ${black} ${darkgreen} ${default}
\e[0m ${darkgreen} ${black} ${darkgreen} ${default}
${darkgreen} ${black} ${darkgreen} ${black} ${darkgreen} ${default}"
fi
}
#end of output functions
add_english() { #add en_US locale for more accurate error
if [ "$(cat /usr/share/i18n/SUPPORTED | grep -o 'en_US.UTF-8' )" == "en_US.UTF-8" ]; then
locale=$(locale -a | grep -oF 'en_US.utf8')
if [ "$locale" != 'en_US.utf8' ]; then
status "Adding en_US locale for better logging... "
sudo sed -i '/en_US.UTF-8/s/^#[ ]//g' /etc/locale.gen
sudo locale-gen
fi
else
warning "en_US locale is not available on your system. This may cause bad logging experience."
fi
export LANG="en_US.UTF-8"
export LANGUAGE="en_US.UTF-8"
export LC_ALL="en_US.UTF-8"
}
#package functions
package_info() { #list everything dpkg knows about the $1 package. Note: the package has to be installed for this to show anything.
local package="$1"
[ -z "$package" ] && error "package_info(): no package specified!"
#list lines in /var/lib/dpkg/status between the package name and the next empty line (empty line is then removed)
sed -n -e '/^Package: '"$package"'$/,/^$/p' /var/lib/dpkg/status | head -n -1
true #this may exit with code 141 if the pipe was closed early (to be expected with grep -v)
}
package_installed() { #exit 0 if $1 package is installed, otherwise exit 1
local package="$1"
[ -z "$package" ] && error "package_installed(): no package specified!"
#find the package listed in /var/lib/dpkg/status
#package_info "$package"
#directly search /var/lib/dpkg/status
grep "^Package: $package$" /var/lib/dpkg/status -A 1 | tail -n 1 | grep -q 'Status: install ok installed'
}
package_available() { #determine if the specified package-name exists in a repository
local package="$1"
[ -z "$package" ] && error "package_available(): no package name specified!"
#using find and grep to do this is nearly instantaneous, rather than apt-cache which takes several seconds
find /var/lib/apt/lists -maxdepth 1 -name "*_Packages" | xargs grep -q "^Package: $package$"
}
package_dependencies() { #outputs the list of dependencies for the $1 package
local package="$1"
[ -z "$package" ] && error "package_dependencies(): no package specified!"
#find the package listed in /var/lib/dpkg/status
package_info "$package" | grep '^Depends: ' | sed 's/^Depends: //g'
}
package_latest_version() { #returns the latest available versions of the specified package-name. Doesn't matter if it's installed or not.
local package="$1"
[ -z "$package" ] && error "package_version(): no package specified!"
grep -rx "Package: $package" /var/lib/apt/lists --exclude="lock" --exclude-dir="partial" --after 4 | grep -o 'Version: .*' | awk '{print $2}' | sort -rV | head -n1
}
package_is_new_enough() { #check if the $1 package has an available version greater than $2
local package="$1"
[ -z "$package" ] && error "package_is_new_enough(): no package specified!"
local compare_version="$2"
[ -z "$package" ] && error "package_is_new_enough(): no comparison version number specified!"
#determine the latest available version for the specified package
local package_version="$(package_latest_version "$package")"
#if version value not found, return 1 now
if [ -z "$package_version" ];then
return 1
fi
#given both the package_version and compare_version, see if the greater of the two is the available package's version
if [ "$(echo "$package_version"$'\n'"$compare_version" | sort -rV | head -n1)" == "$package_version" ];then
#if so, indicate success
return 0
else
return 1
fi
}
anything_installed_from_repo() { #Given an apt repository URL, determine if any packages from it are currently installed
[ -z "$1" ] && error "anything_installed_from_repo: A repository URL must be specified."
#user input repo-url. Remove 'https://', and translate '/' to '_' to conform to apt file-naming standard
local url="$(echo "$1" | sed 's+.*://++g' | tr '/' '_')"
#find all package-lists pertaining to the url
local repofiles="$(ls /var/lib/apt/lists/*_Packages | grep "$url")"
#for every repo-file, check if any of them have an installed file
local found=0
local IFS=$'\n'
local repofile
for repofile in $repofiles ;do
#search the repo-file for installed packages
grep '^Package' "$repofile" | awk '{print $2}' | while read -r package ;do
if package_installed "$package" ;then
echo "Package installed: $package"
exit 1
fi
done #if exit code is 1, search was successful. If exit code is 0, no packages from the repo were installed.
found=$?
if [ $found == 1 ];then
break
fi
done
#return an exit code
if [ $found == 1 ];then
return 0
else
return 1
fi
}
remove_repofile_if_unused() { #Given a sources.list.d file, delete it if nothing from that repository is currently installed. Deletion skipped if $2 is 'test'
local file="$1"
local testmode="$2"
[ -z "$file" ] && error "remove_repo_if_unused: no sources.list.d file specified!"
#exit now if the list file does not exist
[ -f "$file" ] || exit 0
#determine what repo-urls are in the file
local urls="$(cat "$file" | grep -v '^#' | tr ' ' '\n' | grep '://')"
#there could be multiple urls in one file. Check each url and set the in_use variable to 1 if any packages are found
local IFS=$'\n'
local in_use=0
local url
for url in $urls ;do
if anything_installed_from_repo "$url" >/dev/null;then
in_use=1
break
fi
done
if [ "$in_use" == 0 ] && [ "$testmode" == test ];then
echo "The given repository is not in use and can be deleted:"$'\n'"$file" 1>&2
elif [ "$in_use" == 0 ];then
status "Removing the $(basename "$file" | sed 's/.list$//g') repo as it is not being used"
sudo rm -f "$file"
fi
}
#apt functions
apt_lock_wait() { #Wait until other apt processes are finished before proceeding
#make sure english locale is added first
add_english
#in a background subprocess, after 3 seconds, say "Waiting until APT locks are released... "
(sleep 3; echo -n "Waiting until APT locks are released... ") &
local pid=$!
#Try to install a non-existent package to see if apt fails due to a lock-file. Repeat until no errors mention 'Could not get lock'
while sudo -E apt install lkqecjhxwqekc 2>&1 | grep -q 'Could not get lock' ;do
sleep 1
done
while [ ! -z "$(sudo fuser /var/lib/dpkg/lock /var/lib/apt/lists/lock /var/cache/apt/archives/lock /var/log/unattended-upgrades/unattended-upgrades.log /var/lib/dpkg/lock-frontend 2>/dev/null)" ];do
sleep 1
done
#If the background process finished, then that means the "waitint until" message was displayed. This means the kill command will return 1, so echo Done
kill $pid &>/dev/null || echo "Done"
}
less_apt() { #remove unwanted lines from apt output
grep --line-buffered -v "apt does not have a stable CLI interface.\|Reading package lists...\|Building dependency tree\|Reading state information...\|Need to get\|After this operation,\|Selecting previously unselected package\|Preparing to unpack\|Setting up \|Processing triggers for \|^$"
}
apt_update() { #run an apt update with error-checking and minimal output
apt_lock_wait
status "Running \e[7msudo apt update\e[27m..."
output="$(sudo -E apt update --allow-releaseinfo-change "$@" 2>&1 | less_apt | tee /dev/stderr)"
exitcode=$?
status "apt update complete."
#inform user about autoremovable packages
if [ ! -z "$(echo "$output" | grep 'autoremove to remove them' )" ];then
echo -e "\e[33mSome packages are unnecessary.\e[39m Please consider running \e[4msudo a\e[0mp\e[4mt autoremove\e[0m."
fi
#inform user packages are upgradeable
if [ ! -z "$(echo "$output" | grep 'packages can be upgraded' )" ];then
echo -e "\e[33mSome packages can be upgraded.\e[39m Please consider running \e[4msudo a\e[0mp\e[4mt full-u\e[0mpg\e[4mrade\e[0m."
elif [ ! -z "$(echo "$output" | grep 'package can be upgraded' )" ];then
echo -e "\e[33mOne package can be upgraded.\e[39m Please consider running \e[4msudo a\e[0mp\e[4mt full-u\e[0mpg\e[4mrade\e[0m."
fi
#exit on apt error
errors="$(echo "$output" | grep '^[(E)|(Err]:')"
if [ $exitcode != 0 ] || [ ! -z "$errors" ];then
echo -e "\e[91mFailed to run \e[4msudo apt update\e[0m\e[39m!"
echo -e "APT reported these errors:\n\e[91m$errors\e[39m"
#run some apt error diagnosis
echo "$output"
exit 1
fi
return 0
}
repo_add() { #add local packages to the /tmp/pi-apps-local-packages repository
#given local deb file(s), make a local apt repository in /tmp/pi-apps-local-packages
#see: https://unix.stackexchange.com/questions/87130/how-to-quickly-create-a-local-apt-repository-for-random-packages-using-a-debian
#and: https://serverfault.com/questions/447457/use-apt-get-source-on-a-debian-repo-without-using-etc-apt-source-list
#and: https://askubuntu.com/questions/382664/use-custom-directory-for-apt-get
#use this flag for apt commands to use the local repository: -o Dir::Etc::SourceList=/tmp/pi-apps-local-packages/source.list
#ensure the repo-folder exists
mkdir -p /tmp/pi-apps-local-packages || error "repo_add(): failed to create folder /tmp/pi-apps-local-packages"
#move every mentioned deb file to it
for file in "$@"; do
mv -f "$file" /tmp/pi-apps-local-packages || error "repo_add(): failed to move '$file' to the repository: /tmp/pi-apps-local-packages"
done
}
repo_refresh() { #index the pi-apps local apt repository
[ -d /tmp/pi-apps-local-packages ] || error "repo_update(): cannot index the repository - it's missing! /tmp/pi-apps-local-packages"
#index the repository by creating a Packages file
(cd /tmp/pi-apps-local-packages && apt-ftparchive packages . > Packages) || error "repo_update(): apt-ftparchive failed to index the repository: /tmp/pi-apps-local-packages
The Pi-Apps developers have been receiving a few of these errors recently, but we can't figure out what the problem is without your help. Could you please reach out so we can solve this?"
#Make sure the Packages file actually exists
[ -f /tmp/pi-apps-local-packages/Packages ] || error "repo_update(): apt-ftparchive failed to index the repository: /tmp/pi-apps-local-packages
The Pi-Apps developers have been receiving a few of these errors recently, but we can't figure out what the problem is without your help. Could you please reach out so we can solve this?"
#by default, apt-ftparchive will generate lines like "Filename: ./package-name". This seemed to have caused one error-report, so we remove the "./" to hopefully solve the problem.
sed -i 's+^Filename: \./+Filename: +g' /tmp/pi-apps-local-packages/Packages
#hash the repository by creating a Release file
(cd /tmp/pi-apps-local-packages && apt-ftparchive release . > Release) || error "repo_update(): apt-ftparchive failed to hash the repository: /tmp/pi-apps-local-packages
The Pi-Apps developers have been receiving a few of these errors recently, but we can't figure out what the problem is without your help. Could you please reach out so we can solve this?"
#create a source.list for the repository - add pi-apps-local-packages to the top, then add lines from /etc/apt/sources.list
rm -f /tmp/pi-apps-local-packages/source.list
echo "deb [trusted=yes] file:/tmp/pi-apps-local-packages/ ./" > /tmp/pi-apps-local-packages/source.list
cat /etc/apt/sources.list >> /tmp/pi-apps-local-packages/source.list
}
repo_rm() { #remove the local apt repository
#wait for other operations to finish before continuing - hopefully this will solve cases when the pi-apps local repository was removed unexpectedly by a second process
apt_lock_wait
rm -rf /tmp/pi-apps-local-packages || sudo rm -rf /tmp/pi-apps-local-packages || error "repo_rm(): failed to remove the local repository: /tmp/pi-apps-local-packages"
}
app_to_pkgname() { #given an app-name, convert it to a unique, valid package-name that starts with 'pi-apps-'
local app="$1"
[ -z "$app" ] && error "app_to_pkgname(): no app-name specified"
echo "pi-apps-$(echo "$app" | md5sum | cut -c1-8 | awk '{print $1}')"
}
install_packages() { #Make some packages dependencies of the $app app. Package-names, regex, filenames, and urls are supported.
#convert input array to newline-separated string
local IFS=' '
for arg in "$@"; do
local packages+="$arg
"
done
packages="${packages::-1}" #remove final empty newline
#the $app variable must contain something
[ -z "$app" ] && error 'install_packages function can only be used by apps to install packages. (the $app variable was not set)'
status "Will install these packages: $(tr '\n' ' ' <<<"$packages")"
#array-variable to store custom apt options (for local repositories)
local apt_flags=()
#variable to remember if the pi-apps-local-packages repository is being used
local using_local_packages=0
repo_rm #remove the local repo, just in case the last operation left it in an unrecoverable state.
#handle regex, urls, local packages
IFS=$'\n'
local package
for package in $packages ;do
#handle local packages (package-name starts with /)
if [[ "$package" == /* ]];then
#status "Handling local package $package"
[ -f "$package" ] || error "install_packages(): Local package does not exist! ($package)"
#determine the package name from the filename
packagename="$(dpkg-deb -I "$package" | grep "^ Package:" | awk '{print $2}')"
[ -z "$packagename" ] && error "install_packages(): failed to determine a package-name for the file '$package'"
#add this local package to the pi-apps-local-packages repository
repo_add "$package" || return 1
using_local_packages=1 #remember that the pi-apps-local-packages repository is being used
#replace package filename with name of package
packages="$(echo "$packages" | sed "s|$package|$packagename|")"
#handle urls
elif [[ "$package" == *://* ]];then
#status "Handling url: $package"
local filename="$HOME/$(basename "$package")"
#add .deb extension if filename doesn't end with it.
if [ "${filename: -4}" != ".deb" ]; then
status "$filename is not ending with .deb, renaming it to '${filename}.deb'..."
local filename="${filename}.deb"
fi
wget -O "$filename" "$package" || return 1
#determine the package name from the filename
packagename="$(dpkg-deb -I "$filename" | grep "^ Package:" | awk '{print $2}')"
[ -z "$packagename" ] && error "install_packages(): failed to determine a package-name for the file '$filename'"
#add this local package to the pi-apps-local-packages repository
repo_add "$filename" || return 1
using_local_packages=1 #remember that the pi-apps-local-packages repository is being used
#replace package url with name of package
packages="$(echo "$packages" | sed "s|$package|$packagename|")"
#expand regex (package-name contains *)
elif echo "$package" | grep -q '*' ;then
status "Expanding regex in '${package}'..."
list="$(apt-cache search "$package" | awk '{print $1}' | grep "$(echo "$package" | tr -d '*')")"
#replace package with expanded list
packages="$(echo "$packages" | grep -vF "$package")"$'\n'"$list"
fi
done
#now package list shouldn't contain any '*' characters, urls, local filepaths
if echo "$packages" | grep -q '*';then
error "install_packages(): failed to remove all regex from the package list:\n$packages"
elif [[ "$packages" == *://* ]];then
error "install_packages(): failed to remove all urls from the package list:\n$packages"
elif [[ "$packages" == */* ]];then
error "install_packages(): failed to remove all filenames from the package list:\n$packages"
fi #package list contains no '*' characters, urls, local filepaths
#change the $packages list from newline-delimited to space-delimited
packages="$(tr '\n' ' ' <<<"$packages")"
if [ "$using_local_packages" == 1 ];then
#Initialize the pi-apps-local-packages repository
repo_refresh || return 1
#add this repository to flags to add to apt
apt_flags+=(-o 'Dir::Etc::SourceList=/tmp/pi-apps-local-packages/source.list')
fi
status "Creating an empty apt-package to install the necessary apt packages..."
#to avoid issues with symbols and spaces in app names, we shasum the app name for use in apt
local package_name="$(app_to_pkgname "$app")"
echo "It will be named: $package_name"
#If this app's dummy deb is already installed, add its dependencies to the list.
#this allows install_packages() to be used multiple times in an app's script
if package_installed "$package_name" ;then
local existing_deps="$(package_dependencies "$package_name")"
status "The $package_name package is already installed. Inheriting its dependencies: $(echo "$existing_deps")"
packages+=" $existing_deps"
fi
{ #create dummy apt package that depends on the packages this app requires
#this stores the comma-separated list of dependency packages. It removes duplicate entries.
local depends="$(echo "$packages" | tr ' ' ',' | sed 's/,|/ |/g' | sed 's/|,/| /g' | tr ',' '\n' | sort | uniq | tr '\n' ',' | sed 's/^,//g' | sed 's/,$//g' | sed 's/,/, /g' ; echo)"
rm -rf ~/$package_name ~/$package_name.deb
mkdir -p ~/$package_name/DEBIAN
echo "Maintainer: Pi-Apps team
Name: $app
Description: Dummy package created by pi-apps to install dependencies for the '$app' app
Version: 1.0
Architecture: all
Priority: optional
Section: custom
Depends: $depends
Package: $package_name" > ~/$package_name/DEBIAN/control
#fix error report "dpkg-deb: error: control directory has bad permissions 700 (must be >=0755 and <=0775)"
#The two zeros fix error report "dpkg-deb: error: control directory has bad permissions 2755 (must be >=0755 and <=0775)"
sudo chmod -R '00755' ~/$package_name
#display the finished "Depends: " line to the user
grep --color=never "^Depends: " ~/$package_name/DEBIAN/control
}
#Skip installing the dummy deb if it is already installed and has an identical control file
if package_installed "$package_name" && [ "$(package_info "$package_name" | sort | grep -v '^Status: ')" == "$(cat ~/$package_name/DEBIAN/control | sort)" ];then
echo "$package_name is already installed and no changes would be made. Skipping..."
else
#Before apt update, check if local repo still exists
if [ "$using_local_packages" == 1 ] && [ ! -f /tmp/pi-apps-local-packages/Packages ];then
error "User error: Uh-oh, the /tmp/pi-apps-local-packages folder went missing while installing packages. This usually happens if you try to install several apps at the same time in multiple terminals."
fi
#run an apt update
apt_update "${apt_flags[@]}" || exit 1
#After apt update, check again if local repo still exists
if [ "$using_local_packages" == 1 ] && [ ! -f /tmp/pi-apps-local-packages/Packages ];then
error "User error: Uh-oh, the /tmp/pi-apps-local-packages folder went missing while installing packages. This usually happens if you try to install several apps at the same time in multiple terminals."
fi
#Build .deb file for the dummy package
local output="$(dpkg-deb --build ~/$package_name 2>&1)"
if [ $? != 0 ] || [ ! -f ~/$package_name.deb ];then
echo ""
echo "$output"
error "install_packages(): failed to create dummy deb ${package_name}!"
fi
#install dummy deb
status "Installing the $package_name package..."
apt_lock_wait
local output="$(sudo -E apt install -fy --no-install-recommends --allow-downgrades "${apt_flags[@]}" ~/$package_name.deb 2>&1 | less_apt | tee /dev/stderr)"
status "Apt finished."
errors="$(echo "$output" | grep '^[(E)|(Err]:')"
if [ ! -z "$errors" ];then
echo -e "\e[91mFailed to install the packages!\e[39m"
echo -e "APT reported these errors:\n\e[91m$errors\e[39m"
echo "$output"
#some error reports seem to indicate that package URLs aren't being properly downloaded. This output aims to solve the mystery.
if [ "$using_local_packages" == 1 ] && [ ! -f /tmp/pi-apps-local-packages/Packages ];then
echo "User error: Uh-oh, the /tmp/pi-apps-local-packages folder went missing while installing packages. This usually happens if you try to install several apps at the same time in multiple terminals."
elif [ "$using_local_packages" == 1 ] && echo "$output" | grep 'but it is not installable' ;then
echo -e "\e[91mThe Pi-Apps Local Repository was being used, and a package seemed to not be available. Here's the Packages file:\e[39m"
cat /tmp/pi-apps-local-packages/Packages
fi
exit 1
fi
fi
rm -f ~/$package_name.deb
rm -rf ~/$package_name
#delete the local repository if it was used
if [ "$using_local_packages" == 1 ];then
repo_rm
fi
status_green "Package installation complete."
}
purge_packages() { #Allow dependencies of the $app app to be autoremoved.
#the $app variable must contain something
[ -z "$app" ] && error 'purge_packages function can only be used by apps to install packages. (the $app variable was not set)'
status "Allowing packages required by the $app app to be uninstalled"
#to avoid issues with symbols and spaces in app names, we shasum the app name for use in apt
local package_name="$(app_to_pkgname "$app")"
#if dummy deb found/installed
if package_installed "$package_name" ;then
echo "These packages were: $(package_dependencies "$package_name")"
status "Purging the $package_name package..."
apt_lock_wait
local output="$(sudo -E apt purge -y "$package_name" --autoremove 2>&1 | less_apt | tee /dev/stderr)"
status "Apt finished."
errors="$(echo "$output" | grep '^[(E)|(Err]:')"
if [ ! -z "$errors" ];then
echo -e "\e[91mFailed to uninstall the packages!\e[39m"
echo -e "APT reported these errors:\n\e[91m$errors\e[39m"
#run some apt error diagnosis
echo "$output"
exit 1
fi
elif [ -f "${DIRECTORY}/data/installed-packages/${app}" ];then
#legacy pkg-install implementation
warning "Using the old implementation - an installed-packages file instead of a dummy deb"
local packages="$(cat "${DIRECTORY}/data/installed-packages/${app}" | tr '\n' ' ' | sed 's/ / /g')"
#normal mode
local output="$(sudo -E apt purge -y $packages 2>&1)"
exitcode=$?
errors="$(echo "$output" | grep '^[(E)|(Err]:')"
if [ $exitcode != 0 ] || [ ! -z "$errors" ];then
echo -e "\e[91mFailed to uninstall the packages!\e[39m"
echo -e "APT reported these errors:\n\e[91m$errors\e[39m"
#run some apt error diagnosis
echo "$output"
exit 1
fi
else
status "The $package_name package is not installed so there's nothing to do."
fi
status_green "All packages have been purged successfully."
rm -f "${DIRECTORY}/data/installed-packages/${app}"
}
get_icon_from_package() { #given a package-name, find all png files that it installed and print the one with the largest file-size.
[ -z "$1" ] && error "get_icon_from_package(): requires an apt package name"
#Find dependencies of the listed packages and scan them too
local package=''
local extra_packages=''
for package in "$@" ;do
#for every package specified, look for dependencies to that package that begin with the same name as the original package
#Example: given the 'shotwell' package, this will find the 'shotwell-common' package, as well as others
extra_packages+=" $(package_dependencies "$package" | sed 's/, \||/\n/g' | awk '{print $1}' | grep "^$package" | sort | uniq | tr '\n' ' ')"
done
dpkg-query -L "$@" $extra_packages 2>/dev/null | grep '\.png$\|\.svg$' | grep '/icons/\|/pixmaps/' | xargs wc -c | grep -v ' total' | sort -nr | head -n1 | sed 's/ / /g' | sed 's/^ //g' | tr ' ' '\n' | tail -n +2
}
#end of apt functions
#flatpak functions
flatpak_install() { #install an app using flatpak
[ -z "$1" ] && error "flatpak_install(): This function is used to install a flatpak app, but nothing was specified."
#make sure flatpak is installed
if ! command -v flatpak >/dev/null ;then
error "flatpak_install(): Could not install $1 because flatpak is not installed!"
fi
status -n "Flatpak: Adding flathub remote... "
#Add the flathub remote, first as root, if that fails then try installing as user, while removing unwanted output
( sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo || flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo || error "Flatpak failed to add flathub remote!" ) | grep --line-buffered -v "Note that the directories
'/var/lib/flatpak/exports/share'
'$HOME/.local/share/flatpak/exports/share'
are not in the search path set by the XDG_DATA_DIRS environment variable, so
applications installed by Flatpak may not appear on your desktop until the
session is restarted."
#exit 1 if above code failed
if [ ${PIPESTATUS[0]} != 0 ];then
exit 1
fi
status_green Done
status -n "Flatpak: installing $1... "
#Install the specified app, first as root, if that fails then try installing as user, while removing unwanted output
( sudo flatpak install flathub "$1" -y || flatpak install flathub "$1" -y || error "Flatpak failed to install $1!" ) | grep --line-buffered -v "Note that the directories
'/var/lib/flatpak/exports/share'
'$HOME/.local/share/flatpak/exports/share'
are not in the search path set by the XDG_DATA_DIRS environment variable, so
applications installed by Flatpak may not appear on your desktop until the
session is restarted."
#exit 1 if above code failed
if [ ${PIPESTATUS[0]} != 0 ];then
exit 1
fi
status_green Done
#Pi-Apps tries to avoid unnecessary reboots at all cost. Flatpak places desktop launchers in /var/lib/flatpak/exports/share/applications, which is not searched by default.
#This path is added to $XDG_DATA_DIRS on the next reboot, but we don't want to wait for that!
#If there are files in /var/lib/flatpak/exports/share/applications, and XDG_DATA_DIRS is missing flatpak paths, then bind-mount to /usr/share/applications
if [[ "$XDG_DATA_DIRS" != */var/lib/flatpak/exports/share* ]] && [ ! -z "$(ls /var/lib/flatpak/exports/share/applications)" ] && [ -z "$(ls /usr/share/applications/flatpak-temporary)" ];then
sudo mkdir -p /usr/share/applications/flatpak-temporary
sudo mount --bind /var/lib/flatpak/exports/share/applications /usr/share/applications/flatpak-temporary
elif [[ "$XDG_DATA_DIRS" != */var/lib/flatpak/exports/share* ]] ;then
sudo rm -rf /usr/share/applications/flatpak-temporary
fi
#Additionally, PiOS Buster had a bug where XDG_DATA_DIRS was missing flatpak's entries due to PiOS mods. Pi-Apps fixes this with a runonce.
true
}
flatpak_uninstall() { #uninstall an app using flatpak
[ -z "$1" ] && error "flatpak_uninstall(): This function is used to uninstall a flatpak app, but nothing was specified."
#if flatpak is not installed, then skip everything with code 0.
if ! command -v flatpak >/dev/null ;then
return 0
fi
#Only try to remove flatpak app if it's installed.
if flatpak list | grep -qF "$1" ;then
sudo flatpak uninstall "$1" -y || flatpak uninstall "$1" -y || error "Flatpak failed to uninstall $1!"
fi
}
#end of flatpak functions
#app functions
list_apps() { # $1 can be: installed, uninstalled, corrupted, cpu_installable, hidden, visible, online, online_only, local, local_only
if [ -z "$1" ] || [ "$1" == local ];then
#list all apps
ls "${DIRECTORY}/apps"
elif [ "$1" == all ];then
#combined list of apps, both online and local. Removes duplicate apps from the list.
echo -e "$(list_apps local)\n$(list_apps online)" | sort | uniq
elif [ "$1" == installed ];then
#list apps | only show ( list of installed apps | remove match string | basename )
list_apps local | list_intersect "$(grep -rx 'installed' "${DIRECTORY}/data/status" | awk -F: '{print $1}' | sed 's!.*/!!')"
elif [ "$1" == corrupted ];then
#list apps |only show ( list of corrupted apps | remove match string | basename )
list_apps local | list_intersect "$(grep -rx 'corrupted' "${DIRECTORY}/data/status" | awk -F: '{print $1}' | sed 's!.*/!!')"
elif [ "$1" == disabled ];then
#list apps | only show ( list of disabled apps | remove match string | basename )
list_apps local | list_intersect "$(grep -rx 'disabled' "${DIRECTORY}/data/status" | awk -F: '{print $1}' | sed 's!.*/!!')"
elif [ "$1" == uninstalled ];then
#list apps that have a status file matching "uninstalled"
list_apps local | list_intersect "$(grep -rx 'uninstalled' "${DIRECTORY}/data/status" | awk -F: '{print $1}' | sed 's!.*/!!')"
#also list apps that don't have a status file
list_apps local | list_subtract "$(ls "${DIRECTORY}/data/status")"
elif [ "$1" == have_status ];then
#list apps that have a status file
list_apps local | list_intersect "$(ls "${DIRECTORY}/data/status")"
elif [ "$1" == missing_status ];then
#list apps that don't have a status file
list_apps local | list_subtract "$(ls "${DIRECTORY}/data/status")"
elif [ "$1" == cpu_installable ];then
#list apps that can be installed on the device's OS architecture (32-bit or 64-bit)
#find all apps that have install-XX script, install script, or a packages file
find "${DIRECTORY}/apps" -type f \( -name "install-$arch" -o -name "install" -o -name "packages" \) | sed "s+${DIRECTORY}/apps/++g" | sed 's+/.*++g' | sort | uniq
elif [ "$1" == package ];then
#list apps that have a "packages" file
find "${DIRECTORY}/apps" -type f -name "packages" | sed "s+/packages++g" | sed "s+${DIRECTORY}/apps/++g" | sort | uniq
elif [ "$1" == standard ];then
#list apps that have scripts
find "${DIRECTORY}/apps" -type f \( -name "install-32" -o -name "install-64" -o -name "install" -o -name "uninstall" \) | sed "s+${DIRECTORY}/apps/++g" | sed 's+/.*++g' | sort | uniq
elif [ "$1" == hidden ];then
#list apps that are hidden
read_category_files | grep '|hidden' | awk -F'|' '{print $1}'
elif [ "$1" == visible ];then
#list apps that are in any other category but 'hidden', and aren't disabled
read_category_files | grep -v '|hidden' | awk -F'|' '{print $1}' # | list_subtract "$(list_apps disabled)"
elif [ "$1" == online ];then
#list apps that exist on the online git repo
if [ -d "${DIRECTORY}/update/pi-apps/apps" ];then
#if update folder exists, just use that
ls "${DIRECTORY}/update/pi-apps/apps" | grep .
else
#if update folder doesn't exist, then parse github HTML to get a list of online apps. Horrible idea, but it works!
wget -qO- "${repo_url}/tree/master/apps" | grep 'title=".*" data-pjax=' -o | sed 's/title="//g' | sed 's/" data-pjax=//g'
fi
elif [ "$1" == online_only ];then
#list apps that exist only on the git repo, and not locally
list_apps online | list_subtract "$(list_apps local)"
elif [ "$1" == local_only ];then
#list apps that exist only locally, and not on the git repo
list_apps local | list_subtract "$(list_apps online)"
else
error "list_apps(): unrecognized filter '$1'!"
fi
}
list_intersect() { #Outputs only the apps that appear in both stdin and in $1
# for example, the following two inputs will be a match
# Audacity
# Audacity
# while these two will NOT be a match
# Multimedia/Audacity
# .*/Audacity
comm -12 - <(echo "$1" | sort)
}
list_intersect_partial() { #Outputs only the apps that appear in both stdin and in $1 even with a partial match
# for example, the following two inputs will be a match
# Multimedia/Audacity
# .*/Audacity
# change \n to \| | remove last "\|"
grep -x "$(echo "$1" | sed -z 's/\n/\\|/g' | sed -z 's/\\|$/\n/g')"
}
list_subtract() { #Outputs a list of apps from stdin, minus the ones that appear in $1
# for example, the following two inputs will be a match
# Audacity
# Audacity
# while these two will NOT be a match
# Multimedia/Audacity
# .*/Audacity
comm -23 - <(echo "$1" | sort)
}
list_subtract_partial() { #Outputs a list of apps from stdin, minus the ones that appear in $1 even with a partial match
# for example, the following two inputs will be a match
# Multimedia/Audacity
# .*/Audacity
# change \n to \| | remove last "\|"
grep -vx "$(echo "$1" | sed -z 's/\n/\\|/g' | sed -z 's/\\|$/\n/g')"
}
read_category_files() { #Generates a combined categories-list from several sources: category-overrides, global categories file, and unlisted apps. Format: "app|category"
#remove app category if app folder not found
local IFS=$'\n'
local app
for app in $(cat "${DIRECTORY}/data/category-overrides" 2>/dev/null); do
if ! [ -d "${DIRECTORY}/apps/$(echo "$app" | sed 's/|.*//')" ] &>/dev/null; then
sed -i "/$app/d" "${DIRECTORY}/data/category-overrides"
fi
done
# get device specific categories overrides (if they exist)
# obtain model and jetson_model
get_model &>/dev/null
version_id=$(grep 'VERSION_ID=' /etc/os-release | tr -cd '0123456789.')
unset device_override
if [[ ! -z "$jetson_model" ]] && [[ "$version_id" == "18.04" ]]; then
device_override="${DIRECTORY}/etc/category-overrides-jetson-18.04"
fi
#list the user-overrides file and the device specific override and the global categories file |-----and all apps------------| filter out duplicates no '\n\n'
(cat "${DIRECTORY}/data/category-overrides" "$device_override" "${DIRECTORY}/etc/categories" 2>/dev/null ; echo ; list_apps local | sed 's/$/|/g') | awk -F'|' '!seen[$1]++' | grep .
}
app_prefix_category() { #lists all apps in a category with format "category/app", or if $1 is left blank, then list the full structure of all categories
#Subtract a type of app if enabled
local show_apps_setting="$(cat "${DIRECTORY}/data/settings/Show apps")"
local filter=()
if [ "$show_apps_setting" == 'standard' ];then
#if only showing standard apps, hide package apps
filter=(list_subtract_partial "$(list_apps package | sed 's+^+.*/+g')")
elif [ "$show_apps_setting" == 'packages' ];then
#if only showing package apps, hide standard apps
filter=(list_subtract_partial "$(list_apps standard | sed 's+^+.*/+g')")
else
#is the setting is "all" or missing, don't filter.
filter=(cat)
fi
#show special "Installed" category - don't filter it
if [ "$1" == "Installed" ]; then
list_apps installed | sed 's+^+Installed/+g'
#show special "Packages" category
elif [ "$1" == "Packages" ]; then
list_apps package | list_subtract "$(list_apps hidden)" | sed 's+^+Packages/+g'
#show special "All Apps" category
elif [ "$1" == "All Apps" ]; then
list_apps cpu_installable | list_subtract "$(list_apps hidden)" | sed 's+^+All Apps/+g' | "${filter[@]}"
# show all categories
elif [ -z "$1" ]; then
#show normal categories
read_category_files | grep . | awk -F'|' '{print $2"/"$1}' | sed 's+^/++g' | "${filter[@]}"
#show special "Installed" category - don't filter it
list_apps installed | sed 's+^+Installed/+g'
#show special "Packages" category
if [ "$show_apps_setting" != standard ];then
list_apps package | list_subtract "$(list_apps hidden)" | sed 's+^+Packages/+g'
fi
#show special "All Apps" category
list_apps cpu_installable | list_subtract "$(list_apps hidden)" | sed 's+^+All Apps/+g' | "${filter[@]}"
#show normal categories
else
read_category_files | grep . | awk -F'|' '{print $2"/"$1}' | grep "^$1/" | sed 's+^/++g' | "${filter[@]}"
fi
}
# bitly_link() { #Runs whenever an app is installed/uninstalled to tally the number of users for each app
# #This cannot possibly be used to identify you, or any information about you.
# #It simply "clicks" a bitly link - a shortened URL - so that the total number of clicks can be tallied to determine how popular a certain app is.
# app="$1"
# trigger="$2"
# [ -z "$app" ] && error "bitly_link(): requires an app argument"
# [ -z "$trigger" ] && error "bitly_link(): requires a trigger argument"
# #if the 'Enable Analytics' setting is enabled
# if [ "$(cat "${DIRECTORY}/data/settings/Enable analytics")" == 'Yes' ];then
# #determine the name of the link to "click"
# bitlylink="https://bit.ly/pi-apps-$trigger-$(echo "$app" | tr -d ' ' | sed 's/[^a-zA-Z0-9]//g')"
# #click it
# curl -L --user-agent "Pi-Apps Raspberry Pi app store" "$bitlylink" &>/dev/null &
# fi
# }
bitly_link() { #compatibility function pointing to shlink_link (incase old manage script is running with new api)
shlink_link "$@"
}
shlink_link() { #Runs whenever an app is installed/uninstalled to tally the number of users for each app
#This cannot possibly be used to identify you, or any information about you.
#It simply "clicks" a shlink link - a shortened URL - so that the total number of clicks can be tallied to determine how popular a certain app is.
app="$1"
trigger="$2"
[ -z "$app" ] && error "shlink_link(): requires an app argument"
[ -z "$trigger" ] && error "shlink_link(): requires a trigger argument"
#if the 'Enable Analytics' setting is enabled
if [ "$(cat "${DIRECTORY}/data/settings/Enable analytics")" == 'Yes' ];then
#determine the name of the link to "click"
shlinklink="https://pi-apps-analytics.linkpc.net/pi-apps-$trigger-$(echo "$app" | tr -d ' ' | sed 's/[^a-zA-Z0-9]//g')/track"
#click it
curl -s -X 'GET' "$shlinklink" -H 'accept: image/gif' -A "Pi-Apps Raspberry Pi app store" >/dev/null &
fi
}
usercount() { #Return number of users for specified app. $1 is app name. If empty, all are shown.
#If clicklist file missing or over a day old, download it.
if [ ! -f "${DIRECTORY}/data/clicklist" ] || [ ! -z "$(find "${DIRECTORY}/data/clicklist" -mtime +1 -print)" ]; then
wget 'https://raw.githubusercontent.com/Botspot/pi-apps-analytics/main/clicklist' -qO "${DIRECTORY}/data/clicklist" >/dev/null || return 1
fi
clicklist="$(cat "${DIRECTORY}/data/clicklist")"
[ -z "$clicklist" ] && error "usercount(): clicklist empty. Likely no internet connection"
if [ -z "$1" ];then
echo "$clicklist"
else
# $1 is app
echo "$clicklist" | grep "[0-9] $1"'$' | awk '{print $1}' | head -n1
fi
}
script_name() { #returns name of install script(s) for the $1 app. outputs: '', 'install-32', 'install-64', 'install', 'install-32 install-64'
[ -z "$1" ] && error 'script_name(): requires an argument'
#ensure $1 is valid app name
[ ! -d "${DIRECTORY}/apps/$1" ] && error "script_name: '$1' is an invalid app name.\n${DIRECTORY}/apps/$1 does not exist."
if [ -f "${DIRECTORY}/apps/$1/install-32" ] && [ ! -f "${DIRECTORY}/apps/$1/install-64" ];then
echo 'install-32'
elif [ -f "${DIRECTORY}/apps/$1/install-64" ] && [ ! -f "${DIRECTORY}/apps/$1/install-32" ];then
echo 'install-64'
elif [ -f "${DIRECTORY}/apps/$1/install-64" ] && [ -f "${DIRECTORY}/apps/$1/install-32" ];then
echo 'install-32 install-64'
elif [ -f "${DIRECTORY}/apps/$1/install" ];then
echo 'install'
else
true
#error "No install script found for the $app app! Please report this to Botspot."
fi
}
script_name_cpu() { #get script name to run based on detected CPU arch
[ -z "$1" ] && error 'script_name_cpu(): requires an argument.'
#ensure $1 is valid app name
if ! list_apps all | grep -q "$1" ;then
error "script_name_cpu: '$1' is an invalid app name."
fi
#this is used by the updater so we need to check the update folder too
if [ -f "${DIRECTORY}/apps/$1/install-32" ] && [ $arch == 32 ];then
echo 'install-32'
elif [ -f "${DIRECTORY}/apps/$1/install-64" ] && [ $arch == 64 ];then
echo 'install-64'
elif [ -f "${DIRECTORY}/apps/$1/install" ];then
echo 'install'
elif [ -f "${DIRECTORY}/update/pi-apps/apps/$1/install-32" ] && [ $arch == 32 ];then
echo 'install-32'
elif [ -f "${DIRECTORY}/update/pi-apps/apps/$1/install-64" ] && [ $arch == 64 ];then
echo 'install-64'
elif [ -f "${DIRECTORY}/update/pi-apps/apps/$1/install" ];then
echo 'install'
else
true #app not compatible with current arch
fi
}
app_status() { #Gets the $1 app's current status. installed, uninstalled, corrupted, disabled
[ -z "$1" ] && error 'app_status(): requires an argument.'
#don't check if app exists, it may be a new app in an update
if [ -f "${DIRECTORY}/data/status/${1}" ];then
cat "${DIRECTORY}/data/status/${1}"
else
echo 'uninstalled' #if app status file doesn't exist, assume uninstalled
fi
}
app_type() { #there are 'standard' apps, and there are 'package' apps - an alias to install an apt package from the existing repositories.
#$1 is input app-name
local app="$1"
[ -z "$app" ] && error "app_type: no app specified!"
if [ -f "${DIRECTORY}/apps/${app}/packages" ];then
echo package
elif [ -f "${DIRECTORY}/apps/${app}/uninstall" ] || [ -f "${DIRECTORY}/apps/${app}/install" ] || [ -f "${DIRECTORY}/apps/${app}/install-32" ] || [ -f "${DIRECTORY}/apps/${app}/install-64" ];then
echo standard
else
return 1
fi
#if neither conditional above evaluated to true, no output will be returned and the function exits with code 1
}
will_reinstall() { #return 0 if $1 app will be reinstalled during an update, otherwise return 1.
[ -z "$1" ] && error 'will_reinstall(): requires an argument'
#detect which installation script exists and get the hash for that one
scriptname="$(script_name_cpu "$1")"
oldinstallhash="$(sha1sum "${DIRECTORY}/apps/${1}/${scriptname}" 2>/dev/null | awk '{print $1}')"
newinstallhash="$(sha1sum "${DIRECTORY}/update/pi-apps/apps/${1}/${scriptname}" 2>/dev/null | awk '{print $1}')"