*************************************************************************
*** Applied ***
Date: Mon, 14 Feb 2000 12:24:32 -0700
From: "Troy A. Griffitts" <scribe@crosswire.org>
Subject: desktops, icons, etc.

I've made the desktop location code a little more intuitive for gnome
and kde.

There is a new destop: REDHAT where info is stored in /etc/Xll/applnk. 
Redhat intends for this to work in both the gnome and kde worlds, but
kde is not handled well as of 6.1, so KDE icons are still installed
regardless if a redhat desktop is found.

There are also new options on binary nodes to specify the 'name' on the
icon and to what sub-'menu' the icon will be intalled (created if
necessary).  This handles multiple binaries much better.
eg.

    <binary arch="any" libc="any" symlink="app1"
menu="Applications/MyProject" name="Application 1" icon="app1icon.xpm">
      app1
    </binary>
    <binary arch="any" libc="any" symlink="app2"
menu="Applications/MyProject" name="Application 2" icon="app2icon.xpm">
      app2
    </binary>

Everything should still default to 'Games' and such, if none of these
extra options are used.

Again, hope these are useful.  I'm not sure if I'm being more of a
burden then a help on this list.  Would you like me to continue posting
patches or should we just maintain our own branch and, if you are
interested, you may check our CVS repository?  Let me know.  I don't
mind either.

        -Troy.

Index: copy.c
===================================================================
RCS file: /usr/local/cvsroot/setup/copy.c,v
retrieving revision 1.3
diff -u -r1.3 copy.c
--- copy.c	2000/02/11 18:34:04	1.3
+++ copy.c	2000/02/14 19:07:38
@@ -615,6 +615,8 @@
             }
             add_bin_entry(info, final, symlink,
                                        xmlGetProp(node, "desc"),
+                                       xmlGetProp(node, "menu"),
+                                       xmlGetProp(node, "name"),
                                        xmlGetProp(node, "icon"));
         }
     }
Index: install.c
===================================================================
RCS file: /usr/local/cvsroot/setup/install.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 install.c
--- install.c	2000/02/06 01:04:01	1.1.1.1
+++ install.c	2000/02/14 19:07:41
@@ -7,12 +7,14 @@
 #include <pwd.h>
 #include <unistd.h>
 #include <signal.h>
+#include <stdio.h>
 
 #include "install.h"
 #include "install_log.h"
 #include "detect.h"
 #include "log.h"
 #include "copy.h"
+#include "file.h"
 #include "network.h"
 
 extern char *rpm_root;
@@ -214,7 +216,8 @@
 
 /* Add a binary entry to the list of binaries installed */
 void add_bin_entry(install_info *info, const char *path,
-                   const char *symlink, const char *desc, const char *icon)
+                   const char *symlink, const char *desc, const char *menu,
+                   const char *name, const char *icon)
 {
     struct bin_elem *elem;
 
@@ -224,6 +227,8 @@
         if ( elem->path ) {
             elem->symlink = symlink;
             elem->desc = desc;
+            elem->menu = menu;
+            elem->name = name;
             elem->icon = icon;
             elem->next = info->bin_list;
             info->bin_list = elem;
@@ -376,8 +381,10 @@
     copy_tree(info, node, info->install_path, update);
     if(info->options.install_menuitems){
       int i;
-      for(i = 0; i<MAX_DESKTOPS; i++)
-        install_menuitems(info, i);
+      for(i = 0; i<MAX_DESKTOPS; i++) {
+        if (install_menuitems(info, i))
+          break;
+        }
     }
     generate_uninstall(info);
     return SETUP_COMPLETE;
@@ -570,64 +577,110 @@
     return SETUP_EXIT;
 }
 
+static const char* redhat_app_links[] =
+{
+    "/etc/X11/applnk/",
+    0
+};
+
+
 static const char* kde_app_links[] =
 {
-    "/usr/share/applnk/Games/",
-    "/opt/kde/share/applnk/Games/",
+    "/usr/share/applnk/",
+    "/opt/kde/share/applnk/",
     "~/.kde/share/applnk/",
     0
 };
 
+
 static const char* gnome_app_links[] =
 {
-    "/usr/share/gnome/apps/Games/",
-    "/opt/gnome/apps/Games/",
+    "/usr/share/gnome/apps/",
+    "/usr/local/share/gnome/apps/",
+    "/opt/gnome/share/gnome/apps/",
     "~/.gnome/apps/",
     0
 };
 
 /* Install the desktop menu items */
-void install_menuitems(install_info *info, desktop_type desktop)
+char install_menuitems(install_info *info, desktop_type desktop)
 {
     const char **app_links;
     char buf[PATH_MAX];
     struct bin_elem *elem;
+    char ret_val = 0;
+    const char *desk_base;
+    char icon_base[PATH_MAX];
+    const char *found_links[3];
+    FILE *fp;
 
     switch (desktop) {
+        case DESKTOP_REDHAT:
+            app_links = redhat_app_links;
+            break;
         case DESKTOP_KDE:
-            app_links = kde_app_links;
+            desk_base = getenv("KDEDIR");
+            if (desk_base) {
+                sprintf(icon_base, "%s/share/applnk/", desk_base); 
+                found_links[0] = icon_base;
+                found_links[1] = "~/.kde/share/applnk/";
+                found_links[2] = 0;
+                app_links = found_links;
+            }
+            else {
+                app_links = kde_app_links;
+            }
             break;
         case DESKTOP_GNOME:
-            app_links = gnome_app_links;
+            fp = popen("gnome-config --prefix", "r");
+            if (fp) {
+                fgets(icon_base, PATH_MAX, fp);
+                icon_base[strlen(icon_base)-1]=0;
+                strcat(icon_base, "/share/gnome/apps/");
+                found_links[0] = icon_base;
+                found_links[1] = "~/.gnome/apps/";
+                found_links[2] = 0;
+                app_links = found_links;
+            }
+            else {
+                app_links = gnome_app_links;
+            }
             break;
         default:
-            return;
+            return ret_val;
     }
+
     for( ; *app_links; app_links ++){
         expand_home(info, *app_links, buf);
+
         if ( access(buf, W_OK) < 0 )
             continue;
 
         for (elem = info->bin_list; elem; elem = elem->next ) {      
             FILE *fp;
+            char finalbuf[PATH_MAX];
+
+            sprintf(finalbuf, "%s%s/", buf, (elem->menu) ? elem->menu : "Games");
+            file_create_hierarchy(info, finalbuf);
 
             /* Presumably if there is no icon, no desktop entry */
             if ( (elem->icon == NULL) || (elem->symlink == NULL) ) {
                 continue;
             }
-            strncat(buf, elem->symlink, PATH_MAX);
+            strncat(finalbuf, elem->symlink, PATH_MAX);
             switch(desktop){
                 case DESKTOP_KDE:
-                    strncat(buf,".kdelnk", PATH_MAX);
+                    strncat(finalbuf,".kdelnk", PATH_MAX);
                     break;
+                case DESKTOP_REDHAT:
                 case DESKTOP_GNOME:
-                    strncat(buf,".desktop", PATH_MAX);
+                    strncat(finalbuf,".desktop", PATH_MAX);
                     break;
                 default:
                     break;
             }
 
-            fp = fopen(buf, "w");
+            fp = fopen(finalbuf, "w");
             if (fp) {
                 char exec[PATH_MAX], icon[PATH_MAX];
 
@@ -644,16 +697,22 @@
                              "Terminal=0\n"
                              "Type=Application\n",
                              (desktop==DESKTOP_KDE) ? "KDE " : "",
-                             info->name, info->desc,
+                             elem->name, info->desc,
                              exec, icon
                              );
                 fclose(fp);
-                add_file_entry(info, buf);
+                add_file_entry(info, finalbuf);
+
+                // successful REDHAT takes care of KDE/GNOME
+                // tell caller no need to continue others
+                ret_val = (desktop == DESKTOP_REDHAT);
+
             } else {
-                log_warning(info, "Unable to create desktop file '%s'", buf);
+                log_warning(info, "Unable to create desktop file '%s'", finalbuf);
             }
         }
     }
+    return ret_val;
 }
 
 /* Run some shell script commands */
Index: install.h
===================================================================
RCS file: /usr/local/cvsroot/setup/install.h,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 install.h
--- install.h	2000/02/06 01:04:03	1.1.1.1
+++ install.h	2000/02/14 19:07:42
@@ -33,7 +33,8 @@
 
 /* The types of desktop we support menu items for */
 typedef enum {
-    DESKTOP_KDE,
+    DESKTOP_KDE, // KDE first because RH6.1 does not yet handle KDE well.
+    DESKTOP_REDHAT,
     DESKTOP_GNOME,
 	MAX_DESKTOPS
 	/* More to come ? */
@@ -102,6 +103,8 @@
         char *path;
         const char *symlink;
         const char *desc;
+        const char *menu;
+        const char *name;
         const char *icon;
         struct bin_elem *next;
     } *bin_list;
@@ -164,7 +167,8 @@
 
 /* Add a binary entry to the list of binaries installed */
 extern void add_bin_entry(install_info *info, const char *path,
-                const char *symlink, const char *desc, const char *icon);
+                   const char *symlink, const char *desc, const char *menu,
+                   const char *name, const char *icon);
 
 /* Expand a path with home directories into the provided buffer */
 extern void expand_home(install_info *info, const char *path, char *buffer);
@@ -209,7 +213,7 @@
 extern int launch_browser(install_info *info, int (*browser)(const char *url));
 
 /* Install the desktop menu items */
-extern void install_menuitems(install_info *info, desktop_type d);
+extern char install_menuitems(install_info *info, desktop_type d);
 
 /* Run shell script commands from a string
    If 'arg' is >= 0, it is passed to the script as a numeric argument,

*************************************************************************
*** Applied ***
Date: Fri, 11 Feb 2000 12:07:39 -0700
From: "Troy A. Griffitts" <scribe@crosswire.org>
Subject: setup patches

Sam,

    I've added another feature that we needed that I feel will be useful
to others.  We're keeping a copy on our CVS server if you would like to
grab a patched copy.  I'm also attaching the patch.  It expects the
previous patch that I sent to already be applied.

The new feature allows syntax like the following

...
        <files path=${KDEDIR|/usr/local}/share>
                kdesharebase.tar.gz
        </files>
...

This parses any ${ENVVAR} token and replaces it with the environment
variable value.  You may also specify a default value if the environment
variable is not set: ${ENVVAR|default_value}

Hope this is useful.

our anonymous cvs info:

$ export CVSROOT=:pserver:anonymous@cvs.crosswire.org:/usr/local/cvsroot
$ cvs login
Password: anonymous
$ cvs checkout setup

Thanks again for a great tool!

        -Troy A. Griffitts
        http://www.crosswire.org

Index: copy.c
===================================================================
RCS file: /usr/local/cvsroot/setup/copy.c,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -r1.1 -r1.3
--- copy.c	2000/02/06 01:03:56	1.1
+++ copy.c	2000/02/11 18:34:04	1.3
@@ -34,11 +34,29 @@
 static char current_option[200];
 extern char *rpm_root;
 
+void getToken(const char *src, const char **end) {
+    *end = 0;
+    while (*++src) {
+        if (*src == '}') {
+            *end = src;
+            break;
+        }
+    }
+}
+
 int parse_line(const char **srcpp, char *buf, int maxlen)
 {
     const char *srcp;
     char *dstp;
+    const char *subst = 0;
+    char *tokenval = 0;
+    char *token = 0;
+    const char *end;
 
+    if (!*srcpp) { // assert
+        *buf = 0;
+        return 0;
+    }
     /* Skip leading whitespace */
     srcp = *srcpp;
     while ( *srcp && isspace(*srcp) ) {
@@ -47,12 +65,33 @@
 
     /* Copy the line */
     dstp = buf;
-    while ( *srcp && (*srcp != '\r') && (*srcp != '\n') ) {
+    while ( (*srcp || subst) && (*srcp != '\r') && (*srcp != '\n') ) {
         if ( (dstp-buf) >= maxlen ) {
             break;
         }
+        if (!*srcp && subst) { // if we're substituting and done
+            srcp = subst;
+            subst = 0;
+        }
+        if ((!subst) && (*srcp == '$') && (*(srcp+1) == '{')) {
+            getToken(srcp+2, &end);
+            if (end) {	// we've got a good token
+                if (token) free(token);
+                token = calloc((end-(srcp+2))+1, 1);
+                memcpy(token, srcp+2, (end-(srcp+2)));
+                strtok(token, "|"); // in case a default val is specified
+                tokenval = getenv(token);
+                if (!tokenval) // if no env set, check for default
+                    tokenval = strtok(0, "|");
+                if (tokenval) {
+                    subst = end+1;  // where to continue after tokenval
+                    srcp = tokenval;
+                }
+            }
+        }
         *dstp++ = *srcp++;
     }
+    if (token) free(token);
 
     /* Trim whitespace */
     while ( (dstp > buf) && isspace(*(dstp-1)) ) {
@@ -591,6 +630,7 @@
                 void (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
 {
     size_t size, copied;
+    char tmppath[PATH_MAX];
 
     size = 0;
     node = node->childs;
@@ -598,6 +638,10 @@
         const char *path = xmlGetProp(node, "path");
         if (!path)
             path = dest;
+        else {
+            parse_line(&path, tmppath, PATH_MAX);
+            path = tmppath;
+        }
 /* printf("Checking node element '%s'\n", node->name); */
         if ( strcmp(node->name, "files") == 0 ) {
             const char *str = xmlNodeListGetString(info->config, (node->parent)->childs, 1);
@@ -643,6 +687,7 @@
                 void (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
 {
     size_t size, copied;
+    char tmppath[PATH_MAX];
 
     size = 0;
     while ( node ) {
@@ -651,8 +696,13 @@
         wanted = xmlGetProp(node, "install");
         if ( wanted  && (strcmp(wanted, "true") == 0) ) {
             const char *deviant_path = xmlGetProp(node, "path");
-            copied = copy_node(info, node,
-                (deviant_path) ? deviant_path : info->install_path, update);
+            if (!deviant_path)
+                deviant_path = info->install_path;
+            else {
+                parse_line(&deviant_path, tmppath, PATH_MAX);
+                deviant_path = tmppath;
+            }
+            copied = copy_node(info, node, deviant_path, update);
             if ( copied > 0 ) {
                 size += copied;
             }
Index: gtk_ui.c
===================================================================
RCS file: /usr/local/cvsroot/setup/gtk_ui.c,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -r1.1 -r1.3
--- gtk_ui.c	2000/02/06 01:03:59	1.1
+++ gtk_ui.c	2000/02/11 18:34:04	1.3
@@ -253,16 +253,19 @@
 char check_deviant_paths(xmlNodePtr node)
 {
     char path_up[PATH_MAX];
+
     while ( node ) {
         const char *wanted;
-        const char *deviant_path;
+        const char *dpath;
+        char deviant_path[PATH_MAX];
 
         wanted = xmlGetProp(node, "install");
         if ( wanted  && (strcmp(wanted, "true") == 0) ) {
             xmlNodePtr elements = node->childs;
             while ( elements ) {
-                deviant_path = xmlGetProp(elements, "path");
-                if ( deviant_path ) {
+                dpath = xmlGetProp(elements, "path");
+                if ( dpath ) {
+                parse_line(&dpath, deviant_path, PATH_MAX);
                     topmost_valid_path(path_up, deviant_path);
                     if (access(path_up, W_OK) < 0 )
                         return 1;
*************************************************************************
*** Applied ***
Date: Sat, 05 Feb 2000 14:41:09 -0700
From: "Troy A. Griffitts" <scribe@crosswire.org>
Subject: setup patches

Sam and Stephane,
        Here are some more patches to the setup util.  I haven't had much
time to work on it, but these were quick and should provide some useful
features.

        I've added a path="/deviant/path/" option to option components. 
This allows an install developer to place components like:
        <files path="/usr/lib">
                libbase.tar.gz
        </files>
        <files path="/usr/share">
                sharebase.tar.gz
        </files>

When all other components still install into the user's selected
directory, these 'deviant' entries will install to the set locations.

The gtk_ui checks write permission for these paths if the option is
selected before enabling install.

diff -Pru setup-1.1.orig/copy.c setup-1.1/copy.c
--- setup-1.1.orig/copy.c	Wed Dec  1 16:15:26 1999
+++ setup-1.1/copy.c	Fri Feb  4 01:38:17 2000
@@ -595,21 +595,36 @@
     size = 0;
     node = node->childs;
     while ( node ) {
+        const char *path = xmlGetProp(node, "path");
+        if (!path)
+            path = dest;
 /* printf("Checking node element '%s'\n", node->name); */
         if ( strcmp(node->name, "files") == 0 ) {
             const char *str = xmlNodeListGetString(info->config, (node->parent)->childs, 1);
             parse_line(&str, current_option, sizeof(current_option));
             copied = copy_list(info,
                                xmlNodeListGetString(info->config, node->childs, 1),
-                               dest, update);
+                               path, update);
             if ( copied > 0 ) {
                 size += copied;
             }
         }
+        if ( strcmp(node->name, "xfonts") == 0 ) {
+            if (info->copy_xfonts) {
+                const char *str = xmlNodeListGetString(info->config, (node->parent)->childs, 1);
+                parse_line(&str, current_option, sizeof(current_option));
+                copied = (*info->copy_xfonts)(info,
+                            xmlNodeListGetString(info->config, node->childs, 1),
+                            update);
+                if ( copied > 0 ) {
+                    size += copied;
+                }
+            }
+        }
         if ( strcmp(node->name, "binary") == 0 ) {
             copied = copy_binary(info, node,
                                xmlNodeListGetString(info->config, node->childs, 1),
-                               dest, update);
+                               path, update);
             if ( copied > 0 ) {
                 size += copied;
             }
@@ -617,7 +632,7 @@
         if ( strcmp(node->name, "script") == 0 ) {
             copy_script(info, node,
                         xmlNodeListGetString(info->config, node->childs, 1),
-                        dest);
+                        path);
         }
         node = node->next;
     }
@@ -635,7 +650,9 @@
 
         wanted = xmlGetProp(node, "install");
         if ( wanted  && (strcmp(wanted, "true") == 0) ) {
-            copied = copy_node(info, node, info->install_path, update);
+            const char *deviant_path = xmlGetProp(node, "path");
+            copied = copy_node(info, node,
+                (deviant_path) ? deviant_path : info->install_path, update);
             if ( copied > 0 ) {
                 size += copied;
             }
diff -Pru setup-1.1.orig/gtk_ui.c setup-1.1/gtk_ui.c
--- setup-1.1.orig/gtk_ui.c	Wed Dec  1 16:15:26 1999
+++ setup-1.1/gtk_ui.c	Fri Feb  4 01:38:17 2000
@@ -230,26 +231,64 @@
     cur_state = SETUP_INSTALL;
 }
 
+
+void topmost_valid_path(char *target, const char *src) {
+    char *cp;
+  
+    /* Get the topmost valid path */
+    strcpy(target, src);
+    if ( target[0] == '/' ) {
+        cp = target+strlen(target);
+        while ( access(target, F_OK) < 0 ) {
+            while ( (cp > (target+1)) && (*cp != '/') ) {
+                --cp;
+            }
+            *cp = '\0';
+        }
+    }
+}
+
+
+/* returns true if any deviant paths are not writable */
+char check_deviant_paths(xmlNodePtr node)
+{
+    char path_up[PATH_MAX];
+    while ( node ) {
+        const char *wanted;
+        const char *deviant_path;
+
+        wanted = xmlGetProp(node, "install");
+        if ( wanted  && (strcmp(wanted, "true") == 0) ) {
+            xmlNodePtr elements = node->childs;
+            while ( elements ) {
+                deviant_path = xmlGetProp(elements, "path");
+                if ( deviant_path ) {
+                    topmost_valid_path(path_up, deviant_path);
+                    if (access(path_up, W_OK) < 0 )
+                        return 1;
+                }
+                elements = elements->next;
+            }
+            if (check_deviant_paths(node->childs))
+                return 1;
+        }
+        node = node->next;
+    }
+    return 0;
+}
+
+
 /* Checks if we can enable the "Begin install" button */
 static void check_install_button(void)
 {
     const char *message;
     GtkWidget *options_status;
     GtkWidget *button_install;
-    char path_up[PATH_MAX], *cp;
+    char path_up[PATH_MAX];
     struct stat st;
   
     /* Get the topmost valid path */
-    strcpy(path_up, cur_info->install_path);
-    if ( path_up[0] == '/' ) {
-        cp = path_up+strlen(path_up);
-        while ( access(path_up, F_OK) < 0 ) {
-            while ( (cp > (path_up+1)) && (*cp != '/') ) {
-                --cp;
-            }
-            *cp = '\0';
-        }
-    }
+    topmost_valid_path(path_up, cur_info->install_path);
  
     /* See if we can install yet */
     message = "";
@@ -265,6 +304,8 @@
         message = "Install path is not a directory";
     } else if ( access(path_up, W_OK) < 0 ) {
         message = "No write permissions on the install directory";
+    } else if ( check_deviant_paths(cur_info->config->root->childs) ) {
+        message = "No write permissions to install a selected package";
     } else if ( cur_info->symlinks_path[0] &&
                (access(cur_info->symlinks_path, W_OK) < 0) ) {
         message = "No write permissions on the binary directory";
@@ -743,7 +787,7 @@
     gtk_label_set_text(GTK_LABEL(widget), info->install_path);
     widget = glade_xml_get_widget(setup_glade, "play_game_label");
     if ( info->installed_symlink ) {
-        sprintf(text, "Type '%s' to play the game", info->installed_symlink);
+        sprintf(text, "Type '%s' to start the program", info->installed_symlink);
     } else {
         strcpy(text, "");
     }
