diff --git a/etc/genapplist-yad.c b/etc/genapplist-yad.c new file mode 100644 index 0000000..7e3eeaf --- /dev/null +++ b/etc/genapplist-yad.c @@ -0,0 +1,197 @@ +/* ========================================================= * + * Pi-Apps app preloader for YAD GUI app list style. * + * Written by Itai-Nelken 09/09/2021 (MM/DD/YY). * + * --------------------------------------------------------- * + * gcc pi-apps_app-prel.c -o prel -Wall -Wextra -Wpedantic * + * ========================================================= */ + + +#include +#include +#include +#include +#include + +// check if a file exists +int fileExists(const char *path) { + FILE *file=fopen(path, "rb"); + if (!file) { + return 1; + } + fclose(file); + return 0; +} + +// fill a string with nul characters ('\0') +void str_zero(char *str) { + char *s=str; + while(*s++) { + *s='\0'; + } +} + +// get a single line from a file +void getLine(const char *filename, char *out, size_t size) { + FILE *f=fopen(filename, "r"); + if(!f) { + perror("getLine(): fopen()"); + } else { + fgets(out, size, f); + fclose(f); + } +} + +// get the status of a app in pi-apps +void get_app_status(const char *app, char *status, size_t size) { + char path[250]={0}, st[4096]={0}, *directory=getenv("DIRECTORY"); + snprintf(path, 250, "%s/data/status/%s", directory, app); + if (!fileExists(path)) { //if 'path' exists + getLine(path, st, 4096); + st[strlen(st)-1]='\0'; + assert(strlen(st)<=size); + snprintf(status, size, "%s", st); + } else { + snprintf(path, 250, "%s/apps/%s/uninstall", directory, app); + if(!fileExists(path)) { + assert(strlen("uninstalled")<=size); + snprintf(status, size, "uninstalled"); + } else { + fprintf(stderr, "ERROR: get_app_status(): No such app: \"%s\"!\n", app); + } + } +} + +// get the status of a app in pi-apps but print 'none' if the app has no status file +// but it exists (it has a uninstall script) +void get_app_status_with_none(const char *app, char *status, size_t size) { + char path[250]={0}, st[4096]={0}, *directory=getenv("DIRECTORY"); + snprintf(path, 250, "%s/data/status/%s", directory, app); + if (!fileExists(path)) { //if 'path' exists + getLine(path, st, 4096); + st[strlen(st)-1]='\0'; + assert(strlen(st)<=size); + snprintf(status, size, "%s", st); + } else { + snprintf(path, 250, "%s/apps/%s/uninstall", directory, app); + if(!fileExists(path)) { + assert(strlen("none")<=size); + snprintf(status, size, "none"); + } else { + fprintf(stderr, "ERROR: get_app_status(): No such app: \"%s\"!\n", app); + } + } +} + +// get the path to the status icon for a app based on the it's status +void get_status_icon(const char *app, char *icon, size_t size) { + char path[300]={0}, *directory=getenv("DIRECTORY"), status[30]={0}; + get_app_status_with_none(app, status, 30); + snprintf(path, 300, "%s/icons/%s.png", directory, status); + if(!fileExists(path)) { + assert(strlen(path)<=size); + strncpy(icon, path, size); + } else { + //fprintf(stderr, "ERROR: get_icon_name(): icon \"%s\" doesn't exist!\n", path); + assert(strlen(directory)+15<=size); + snprintf(icon, strlen(directory)+15, "%s/icons/none.png", directory); + } +} + +// get the path to a app's icon +void get_app_icon(const char *app, char *icon, size_t size) { + char path[4096]={0}, *directory=getenv("DIRECTORY"); + snprintf(path, 4096, "%s/apps/%s/icon-24.png", directory, app); + if(!fileExists(path)) { + assert(strlen(path)<=size); + strncpy(icon, path, size); + } else { + fprintf(stderr, "ERROR: get_app_icon(): app \"%s\" doesn't have a icon-24!\n", app); + } +} + +// get the status and first line of the description of an app in a format like this: +// "() " +void get_app_status_and_desc(const char *app, char *out, size_t out_size) { + char path[4096]={0}, buffer[out_size], status[30]={0}, *directory=getenv("DIRECTORY"); + str_zero(buffer); + snprintf(path, 4096, "%s/apps/%s/description", directory, app); + if(!fileExists(path)) { + getLine(path, buffer, out_size); + buffer[strlen(buffer)-1]='\0'; // remove newline from end + get_app_status(app, status, 30); + snprintf(out, out_size+33, "(%s) %s", status, buffer); + } else { + fprintf(stderr, "ERROR: get_app_status_and_desc(): app \"%s\" doesn't have a description!\n", app); + } +} + +// collect and print all the data +void print_all(const char *app) { + char st_icon[300]={0}, app_icon[4096]={0}, desc[4096]={0}; + get_status_icon(app, st_icon, 300); + get_app_icon(app, app_icon, 4096); + get_app_status_and_desc(app, desc, 4096); + printf("%s\n%s\n%s\n%s\n%s\n", st_icon, app_icon, app, app, desc); +} + +int main(void) { + if(!getenv("APPS")) { + fprintf(stderr, "ERROR: \"APPS\" environment variable isn't set!\n"); + return 1; + } + if(!getenv("DIRECTORY")) { + fprintf(stderr, "ERROR: \"DIRECTORY\" environment variable isn't set!\n"); + return 1; + } + + char *separator="\n"; + char *parsed; + + parsed=strtok(getenv("APPS"), separator); + while(parsed!=NULL) { + print_all(parsed); + + parsed=strtok(NULL, separator); + } + + return 0; +} + +/*** REQUIRED OUTPUT *** + * 1) status icon + * 2) app icon + * 3) app name + * 4) app folder name (in ~/pi-apps/apps) + * 5) status and short description +***********************/ + +/* LOOP OVER EVERY APP IN $HOME/PI-APPS/APPS * +char *home=getHomePath(); + char path[strlen(home)+20]; + str_zero(path); + snprintf(path, strlen(home)+20, "%s/pi-apps/apps", home); + DIR *dir; + struct dirent *d; + dir=opendir(path); + if(!dir) { + fprintf(stderr, "ERROR: failed to open directory \"%s\"!\n", path); + } else { + while((d=readdir(dir))!=NULL) { + if(strcmp(d->d_name, ".")&&strcmp(d->d_name, "..")&&strcmp(d->d_name, "template")) { + print_all(d->d_name); + } + } + closedir(dir); + } +**************************************************/ + +/********************************************* +void remove_all_chars(char* str, char c) { + char *pr = str, *pw = str; + while (*pr) { + *pw = *pr++; + pw += (*pw != c); + } + *pw = '\0'; +} +***********************************************/ diff --git a/preload b/preload index ff564fd..a0ffa33 100755 --- a/preload +++ b/preload @@ -31,22 +31,43 @@ listfile="${DIRECTORY}/data/preload/LIST-$(echo "$prefix" | tr -d '/')" mkdir -p "${DIRECTORY}/data/preload" +{ #compile the genapplist-yad.c program +if [ ! -f "${DIRECTORY}/etc/genapplist-yad" ] && [ -f "${DIRECTORY}/etc/genapplist-yad.c" ];then + echo "Compiling genapplist-yad program..." 1>&2 + + command -v gcc >/dev/null || sudo apt install -y gcc 1>&2 + gcc "${DIRECTORY}/etc/genapplist-yad.c" -o "${DIRECTORY}/etc/genapplist-yad" 1>&2 || failed=1 + + #Test the program and make sure it outputs 5 lines + if [ "$(APPS=Arduino DIRECTORY="$DIRECTORY" "${DIRECTORY}/etc/genapplist-yad" | wc -l)" != 5 ];then + failed=1 + fi + + if [ "$failed" == 1 ];then + rm -f "${DIRECTORY}/etc/genapplist-yad" + echo "The genapplist-yad program failed to compile." 1>&2 + else + echo "Success! The genapplist-yad program has been compiled." 1>&2 + fi +fi +} + mktimestamps() { #these directories are checked for changes checkdirs="${DIRECTORY}/apps ${DIRECTORY}/data/settings ${DIRECTORY}/data/status +${DIRECTORY}/data/update-status ${DIRECTORY}/etc" timestamps='' - PREIFS="$IFS" - IFS=$'\n' + local IFS=$'\n' for dir in $checkdirs do timestamps="$timestamps dir $dir $(stat -c %Y "${dir}/$(ls -t "$dir" | head -n1)")" done - IFS="$PREIFS" + #remove first empty newline and check a few other things for changes too timestamps="prefix: $prefix format: $format @@ -89,21 +110,13 @@ if [ $reloadlist == 1 ];then #for app_categories() and app_status() functions source "${DIRECTORY}/api" - vfiles="$(app_categories)" #generate paps within categories as folders - - #generate a virtual file system with apps in folders represented as subdirectories - - - vfiles="$(echo "$vfiles" | grep . | sort | uniq)" - #echo "$vfiles" 1>&2 - + vfiles="$(app_categories | grep . | sort | uniq)" #generate a virtual file system with apps in folders represented as subdirectories if [ ! -z "$prefix" ];then echo "Showing apps within $prefix/" 1>&2 vfiles="$(echo "$vfiles" | grep "^$prefix/" | sed "s+$prefix/++g")" fi - #echo "$vfiles" 1>&2 #remove apps within categories - show this layer of stuff only. vfiles="$(echo "$vfiles" | sed 's+/.*+/+g' | sort | uniq)" @@ -114,6 +127,18 @@ if [ $reloadlist == 1 ];then #get list of folders - excluding apps - and hide the hidden folder. DIRS="$(echo "$vfiles" | grep '/' | tr -d '/' | grep -vx "hidden")" + #If updates available, show special Updates category + if "${DIRECTORY}/updater" get-status &>/dev/null;then + DIRS="Updates +$DIRS" + fi + + #shuffle the list if enabled + if [ "$(cat "${DIRECTORY}/data/settings/Shuffle App list")" == 'Yes' ];then + APPS="$(echo "$APPS" | shuf)" + DIRS="$(echo "$DIRS" | shuf)" + fi + #remove apps that are not compatible with OS architecture PREIFS="$IFS" IFS=$'\n' @@ -128,12 +153,6 @@ if [ $reloadlist == 1 ];then done IFS="$PREIFS" - #shuffle the list if enabled - if [ "$(cat "${DIRECTORY}/data/settings/Shuffle App list")" == 'Yes' ];then - APPS="$(echo "$APPS" | shuf)" - DIRS="$(echo "$DIRS" | shuf)" - fi - if [ "$format" == yad ];then PREIFS="$IFS" IFS=$'\n' @@ -152,17 +171,27 @@ $i $i/ App folder " - done + done #finished preloading categories - for i in $APPS - do - LIST="$LIST$(echo "${DIRECTORY}/icons/$(cat "${DIRECTORY}/data/status/${i}" 2>/dev/null || echo "none").png") + #set to false to disable genapplist-yad program - use the original bash version + if true && [ -f "${DIRECTORY}/etc/genapplist-yad" ];then + LIST="$LIST$(DIRECTORY="$DIRECTORY" APPS="$APPS" "${DIRECTORY}/etc/genapplist-yad") +" + else + for i in $APPS + do + status1="$(cat "${DIRECTORY}/data/status/${i}" 2>/dev/null || echo "none")" + status2="$(echo "$status1" | sed 's/none/uninstalled/g')" + + LIST="$LIST$(echo "${DIRECTORY}/icons/$status1.png") ${DIRECTORY}/apps/${i}/icon-24.png $i $i -"\("$(app_status "$i")"\)" $(echo "$(cat "${DIRECTORY}/apps/${i}/description" || echo "Description unavailable")" | head -n1) +"\("$status2"\)" $(head -n1 "${DIRECTORY}/apps/${i}/description" || echo "Description unavailable") " - done + done + fi #finished preloading apps + IFS="$PREIFS" elif [ "$format" == xlunch ];then @@ -186,7 +215,7 @@ ${i};$diricon;${i}/" for i in $APPS do LIST="$LIST -${i} "\("$(app_status "${i}")"\)";${DIRECTORY}/apps/${i}/icon-64.png;${i}" +${i} ($(app_status "${i}"));${DIRECTORY}/apps/${i}/icon-64.png;${i}" done IFS="$PREIFS" fi @@ -215,5 +244,5 @@ IFS="$PREIFS" ) & echo "$LIST" -#re-preload all categories in background +#preload all categories in background "${DIRECTORY}/etc/preload-daemon" "$format" &>/dev/null &