[PATCH]<<3 suffix and productdir attributes, zip fixes, ui changes

Ludwig Nussel ludwig.nussel at gmx.de
Sun Oct 31 08:53:01 EST 2004


Hi,

If you want one patch per mail just say so :-)

loki_setup-noinlinescripts.diff
	- loki-setup doesn't actually have the inline scripts
	  feature

loki_setup-binaryinline.diff
	- this one adds it

loki_setup-uiupdatefunc.diff
	- add a typedef for the ui update function to make code more
	  readable

loki_setup-forceplugin.diff
	- add a suffix attribute to the files element to make
	  loki-setup assume the files are of the specified type.
	  Useful for example if you have a .exe sfx zip archive and
	  want it to be unpacked rather ran copied.

loki_setup-zipfix.diff
	- I found a zip file which has additional zeroes at the end
	  of the central directory, probably some padding. The
	  condition (pos + 22 + ui16) != len isn't fulfilled because
	  of that.
	- change debug message to tell the code line where the io
	  error happened
	- create directories with mode 0755 rather than 0700 which
	  doesn't make sense

loki_setup-productchdir.diff
	- add productdir attribute for meta installers to optionally make
	  loki-setup change the directory before executing the new
	  installer.

loki_setup-nogreybutton.diff
	- don't grey out the 'install' button as that confuses
	  users because they have to guess the reason why it's
	  greyed out. I.e. locate the small status message and draw
	  a conclusion from that. Instead show a popup window
	  explaining what's wrong and if possible also give a hint
	  how to solve it ("/usr/local/games is owned by root. You
	  may need to run this installer as user root or choose
	  another directory."). Requires new translations. I also
	  changed "Link path" to "Binary path" in the glade file.
	  All messages refer to "Binary path" but no such thing is
	  in the UI.

loki_setupdb-closeproduct.diff
	- save xml to a temporary file and then atomically rename it
	  to prevent corruption if loki-setup crashes while writing
	  the file. Yeah, that crash which corrupted my ut2004 xml
	  file was my own fault but still this patch makes things
	  more robust :-)

cu
Ludwig

-- 
(o_  Ludwig.Nussel at gmx.de
//\
V_/_ PGP Key ID: FF8135CE
-------------- next part --------------
Index: loki_setup/install.c
===================================================================
--- loki_setup.orig/install.c
+++ loki_setup/install.c
@@ -138,7 +138,6 @@ typedef struct
 /** features supported by this installer */
 static SetupFeature features[] =
 {
-	{ "inline-scripts", 1 },
 	{ NULL, 0 }
 };
 
-------------- next part --------------
Index: loki_setup/file.h
===================================================================
--- loki_setup.orig/file.h
+++ loki_setup/file.h
@@ -55,4 +55,12 @@ extern int dir_exists(const char *path);
 extern void file_create_hierarchy(install_info *info, const char *path);
 extern void dir_create_hierarchy(install_info *info, const char *path, int mode);
 extern int dir_is_accessible(const char *path);
+
+/** create a temporary directory and return it's name. Failure to create the
+ * directory will abort the program. It is only allowed to created inodes that
+ * can be removed with unlink() in that directory */
+extern const char* tmpdir();
+
+/** clean all files in the temporary directory and remove it */
+extern int cleantmpdir();
 #endif
Index: loki_setup/main.c
===================================================================
--- loki_setup.orig/main.c
+++ loki_setup/main.c
@@ -75,6 +75,7 @@ void exit_setup(int ret)
     FreePlugins();
 	free_corrupt_files();
     unmount_filesystems();
+	cleantmpdir();
 	log_exit();
     exit(ret);
 }
Index: loki_setup/file.c
===================================================================
--- loki_setup.orig/file.c
+++ loki_setup/file.c
@@ -11,6 +11,7 @@
 #include <errno.h>
 #include <unistd.h>
 #include <dirent.h>
+#include <time.h>
 
 #include <zlib.h>
 
@@ -784,3 +785,71 @@ static void dobz2init()
 }
 
 #endif
+
+static char tmpdirname[PATH_MAX] = "\0";
+
+const char* tmpdir()
+{
+	unsigned count;
+
+	if(*tmpdirname)
+		return tmpdirname;
+
+	srand(time(NULL));
+	for(count = 0; count < 1000; ++count)
+	{
+		snprintf(tmpdirname, sizeof(tmpdirname), "/tmp/setuptmp.%06X", rand());
+		if(mkdir(tmpdirname, 0700) == 0)
+		{
+			log_debug("temporary directory %s created", tmpdirname);
+			return tmpdirname;
+		}
+	}
+	log_fatal(_("Unable to create temporary directory: %s"), strerror(errno));
+	
+	return NULL;
+}
+
+int cleantmpdir()
+{
+	DIR* dir;
+	struct dirent *entry;
+	char cwd[PATH_MAX];
+
+	if(!*tmpdirname)
+		return 0;
+
+	if(!getcwd(cwd, sizeof(cwd)))
+		cwd[0] = '\0';
+
+	if(chdir(tmpdirname) == -1)
+	{
+		log_warning(_("Can not chdir to temporary directory: %s"), strerror(errno));
+		return -1;
+	}
+
+	dir = opendir(tmpdirname);
+	if(!dir)
+	{
+		log_warning(_("Can not open temporary directory: %s"), strerror(errno));
+		return -1;
+	}
+
+	while((entry = readdir(dir)) != NULL)
+	{
+		if(!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
+			continue;
+
+		log_debug("unlink temporary file %s", entry->d_name);
+		if(unlink(entry->d_name) == -1)
+			log_warning(_("Can not remove %s/%s: %s"), tmpdirname, entry->d_name, strerror(errno));
+	}
+
+	closedir(dir);
+
+	if(*cwd)
+		chdir(cwd);
+
+	return rmdir(tmpdirname);
+}
+
Index: loki_setup/copy.c
===================================================================
--- loki_setup.orig/copy.c
+++ loki_setup/copy.c
@@ -108,6 +108,8 @@ typedef struct _corrupt_list {
 
 static corrupt_list *corrupts = NULL;
 
+static void copy_binary_finish(install_info* info, xmlNodePtr node, const char* fn, struct file_elem *file);
+
 void add_corrupt_file(const product_t *prod, const char *path, const char *option)
 {
 	corrupt_list *item;
@@ -585,6 +587,50 @@ ssize_t copy_manpage(install_info *info,
 	return copied;
 }
 
+/** write memory 'data' with length 'len' to a temporary file called 'name'.
+ * The full path to the created file is returned in name_out which must hold at
+ * least PATH_MAX characters.
+ * @return pointer to name_out for convenience. On error NULL is returned.
+ * content of name_out is undefined in this case.
+ */
+static char* write_temp_script(const char* name, char* name_out,
+		const char* data,
+		size_t len)
+{
+	int fd;
+	ssize_t ret;
+
+	if(!name_out)
+		return NULL;
+
+	if(strrchr(name, '/'))
+	{
+		log_warning("'name' must not contain slashes");
+		return NULL;
+	}
+
+	name_out[PATH_MAX-1] = '\0';
+	name_out[PATH_MAX-2] = '\0';
+	strncpy(name_out, tmpdir(), PATH_MAX-2);
+	name_out[strlen(name_out)] = '/';
+	strncpy(name_out+strlen(name_out), name, PATH_MAX-strlen(name_out)-1);
+
+	if((fd = open(name_out, O_WRONLY|O_CREAT, 0755)) == -1)
+	{
+		log_warning(_("Could not create temporary script: %s"), strerror(errno));
+		return NULL;
+	}
+
+	ret = write(fd, data, len);
+	if(ret < 0 || (unsigned)ret != len)
+	{
+		log_warning(_("Could not create temporary script: %s"), strerror(errno));
+		name_out = NULL;
+	}
+	close(fd);
+	return name_out;
+}
+
 ssize_t copy_binary(install_info *info, xmlNodePtr node, const char *filedesc, const char *dest, 
 		    const char *from_cdrom,
 		    int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
@@ -617,6 +663,36 @@ ssize_t copy_binary(install_info *info, 
     copied = 0;
     size = 0;
 
+	if(xmlNodePropIsTrue(node, "inline"))
+	{
+		if(!binpath)
+		{
+			log_warning(_("'binpath' attribute is mandatory for inline 'binary' tag"));
+			goto copy_binary_exit; 
+		}
+		else if(strrchr(binpath, '/'))
+		{
+			log_warning(_("'binpath' attribute must not contain slashes for inline 'binary' tag"));
+			goto copy_binary_exit; 
+		}
+
+		strncpy(fdest, dest, sizeof(fdest));
+		fdest[sizeof(fdest)-1] = '\0';
+
+		if(!write_temp_script(binpath, bin, filedesc, strlen(filedesc)))
+			goto copy_binary_exit;
+
+		copied = copy_file(info, NULL, bin, fdest, final, 1, 1, node, update, &file);
+		if(copied > 0)
+		{
+			size += copied;
+
+			copy_binary_finish(info, node, final, file);
+		}
+		unlink(bin);
+		goto copy_binary_exit;
+	}
+
     while ( filedesc && parse_line(&filedesc, final, (sizeof final)) ) {
 		if (! in_rpm) {
 			const char *cdpath = NULL;
@@ -716,23 +792,9 @@ ssize_t copy_binary(install_info *info, 
 			log_warning(_("Unable to copy file '%s'"), fpat);
 			ui_fatal_error(_("Unable to copy file '%s'"), fpat);
         } else if ( copied > 0 || in_rpm ) {
-            char *symlink = xmlGetProp(node, "symlink");
-            char sym_to[PATH_MAX];
-
             size += copied;
-            /* Create the symlink */
-            if ( *info->symlinks_path && symlink ) {
-                snprintf(sym_to, sizeof(sym_to), "%s/%s", info->symlinks_path, symlink);
-                file_symlink(info, final, sym_to);
-            }
-            add_bin_entry(info, current_option, file, symlink,
-						  xmlGetProp(node, "desc"),
-						  xmlGetProp(node, "menu"),
-						  xmlGetProp(node, "name"),
-						  xmlGetProp(node, "icon"),
-						  xmlGetProp(node, "args"),
-						  xmlGetProp(node, "play")
-						  );
+
+			copy_binary_finish(info, node, final, file);
         }
     }
  copy_binary_exit:
@@ -740,6 +802,29 @@ ssize_t copy_binary(install_info *info, 
     return size;
 }
 
+/** to be called when binary was successfully installed. creates optional
+ * symlink and registers the file in the setup database.
+ */
+static void copy_binary_finish(install_info* info, xmlNodePtr node, const char* fn, struct file_elem *file)
+{
+	char *symlink = xmlGetProp(node, "symlink");
+	char sym_to[PATH_MAX];
+
+	/* Create the symlink */
+	if ( *info->symlinks_path && symlink ) {
+		snprintf(sym_to, sizeof(sym_to), "%s/%s", info->symlinks_path, symlink);
+		file_symlink(info, fn, sym_to);
+	}
+	add_bin_entry(info, current_option, file, symlink,
+			xmlGetProp(node, "desc"),
+			xmlGetProp(node, "menu"),
+			xmlGetProp(node, "name"),
+			xmlGetProp(node, "icon"),
+			xmlGetProp(node, "args"),
+			xmlGetProp(node, "play")
+			);
+}
+
 static int copy_script(install_info *info, xmlNodePtr node, const char *script, const char *dest, size_t size,
 				int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current), const char *from_cdrom, const char *msg)
 {
@@ -1197,7 +1282,8 @@ unsigned long long size_node(install_inf
 					size += size_list(info, from_cdrom, srcpath,
 									  xmlNodeListGetString(info->config, node->childs, 1));
 				} else if ( strcmp(node->name, "binary") == 0 ) {
-					size += size_binary(info, from_cdrom,
+					if(!xmlNodePropIsTrue(node, "inline"))
+						size += size_binary(info, from_cdrom,
 									xmlNodeListGetString(info->config, node->childs, 1));
 				} else if ( strcmp(node->name, "manpage") == 0 ) {
 					size += size_manpage(info, node, from_cdrom);
@@ -1258,3 +1344,4 @@ int has_binaries(install_info *info, xml
 }
 
 
+
Index: loki_setup/README.xml
===================================================================
--- loki_setup.orig/README.xml
+++ loki_setup/README.xml
@@ -661,6 +661,10 @@ There are several optional attributes of
 
  args       Specify optional arguments to be added on the command line when running the binary.
 
+ inline     When set to 'yes' the text content of the element is used as script
+	    content instead of a path. The value of 'binarypath' is used as
+	    script name in this case
+
 The FILES element:
 
 The files element contains a list of files and directories, one per line,
Index: loki_setup/install.c
===================================================================
--- loki_setup.orig/install.c
+++ loki_setup/install.c
@@ -138,6 +138,7 @@ typedef struct
 /** features supported by this installer */
 static SetupFeature features[] =
 {
+	{ "inline-scripts", 1 },
 	{ NULL, 0 }
 };
 
-------------- next part --------------
Index: loki_setup/copy.h
===================================================================
--- loki_setup.orig/copy.h
+++ loki_setup/copy.h
@@ -13,11 +13,11 @@
 extern ssize_t copy_path(install_info *info, const char *path, 
 						 const char *dest, const char *cdrom, int strip_dirs,
 						 xmlNodePtr node,
-						 int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current));
+						 UIUpdateFunc update);
 
 /* Copy an option tree to the destination directory */
 extern ssize_t copy_tree(install_info *info, xmlNodePtr node, const char *dest,
-						int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current));
+						UIUpdateFunc update);
 
 /* Get the install size of an option node, in bytes */
 extern unsigned long long size_node(install_info *info, xmlNodePtr node);
Index: loki_setup/install_ui.h
===================================================================
--- loki_setup.orig/install_ui.h
+++ loki_setup/install_ui.h
@@ -22,7 +22,7 @@ typedef struct {
     install_state (*license)(install_info *info);
     install_state (*readme)(install_info *info);
     install_state (*setup)(install_info *info);
-    int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current);
+    UIUpdateFunc update;
     void (*abort)(install_info *info);
 	void (*idle)(install_info *info);
 	yesno_answer (*prompt)(const char *txt, yesno_answer suggest);
Index: loki_setup/copy.c
===================================================================
--- loki_setup.orig/copy.c
+++ loki_setup/copy.c
@@ -247,7 +247,7 @@ int parse_line(const char **srcpp, char 
 
 ssize_t copy_file(install_info *info, const char *cdrom, const char *path, const char *dest, char *final, 
 				  int binary, int strip_dirs, xmlNodePtr node,
-				  int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current),
+				  UIUpdateFunc update,
 				  struct file_elem **elem)
 {
     ssize_t size = 0;
@@ -394,7 +394,7 @@ ssize_t copy_file(install_info *info, co
 
 size_t copy_directory(install_info *info, const char *path, const char *dest, 
 					  const char *cdrom, xmlNodePtr node,
-					  int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
+					  UIUpdateFunc update)
 {
     char fpat[PATH_MAX];
     int i, err;
@@ -430,7 +430,7 @@ size_t copy_directory(install_info *info
 
 ssize_t copy_path(install_info *info, const char *path, const char *dest, 
 		  const char *cdrom, int strip_dirs, xmlNodePtr node,
-		  int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
+		  UIUpdateFunc update)
 {
     char final[PATH_MAX];
     struct stat sb;
@@ -467,7 +467,7 @@ ssize_t copy_path(install_info *info, co
 
 ssize_t copy_list(install_info *info, const char *filedesc, const char *dest, 
 		  const char *from_cdrom, const char *srcpath, int strip_dirs, xmlNodePtr node,
-		  int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
+		  UIUpdateFunc update)
 {
     char fpat[BUFSIZ];
     int i;
@@ -552,7 +552,7 @@ static void check_dynamic(const char *fp
 
 ssize_t copy_manpage(install_info *info, xmlNodePtr node, const char *dest,
 		    const char *from_cdrom,
-		    int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
+		    UIUpdateFunc update)
 {
     ssize_t copied = 0;
 	char fpat[PATH_MAX], final[PATH_MAX];
@@ -633,7 +633,7 @@ static char* write_temp_script(const cha
 
 ssize_t copy_binary(install_info *info, xmlNodePtr node, const char *filedesc, const char *dest, 
 		    const char *from_cdrom,
-		    int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
+		    UIUpdateFunc update)
 {
     struct stat sb;
     char fpat[PATH_MAX], bin[PATH_MAX], final[PATH_MAX], fdest[PATH_MAX];
@@ -826,7 +826,7 @@ static void copy_binary_finish(install_i
 }
 
 static int copy_script(install_info *info, xmlNodePtr node, const char *script, const char *dest, size_t size,
-				int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current), const char *from_cdrom, const char *msg)
+				UIUpdateFunc update, const char *from_cdrom, const char *msg)
 {
     struct cdrom_elem *cdrom;
     struct cdrom_elem *cdrom_start;
@@ -859,7 +859,7 @@ static int copy_script(install_info *inf
 }
 
 ssize_t copy_node(install_info *info, xmlNodePtr node, const char *dest,
-                int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
+                UIUpdateFunc update)
 {
     ssize_t size, copied;
     char tmppath[PATH_MAX], *tmp;
@@ -964,7 +964,7 @@ ssize_t copy_node(install_info *info, xm
 }
 
 ssize_t copy_tree(install_info *info, xmlNodePtr node, const char *dest,
-				  int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
+				  UIUpdateFunc update)
 {
     ssize_t size, copied;
     char tmppath[PATH_MAX];
Index: loki_setup/plugins/rpm.c
===================================================================
--- loki_setup.orig/plugins/rpm.c
+++ loki_setup/plugins/rpm.c
@@ -86,7 +86,7 @@ static size_t RPMSize(install_info *info
 /* Extract the file */
 static size_t RPMCopy(install_info *info, const char *path, const char *dest, const char *current_option_name, 
 		      xmlNodePtr node,
-		      int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
+		      UIUpdateFunc update)
 {
     FD_t fdi;
     Header hd;
Index: loki_setup/install.c
===================================================================
--- loki_setup.orig/install.c
+++ loki_setup/install.c
@@ -1589,9 +1589,7 @@ void delete_install(install_info *info)
 char gCDKeyString[128];
 
 /* Actually install the selected filesets */
-install_state install(install_info *info,
-					  int (*update)(install_info *info, const char *path, 
-									size_t progress, size_t size, const char *current))
+install_state install(install_info *info, UIUpdateFunc update)
 {
     xmlNodePtr node;
     install_state state;
Index: loki_setup/plugins/zip.c
===================================================================
--- loki_setup.orig/plugins/zip.c
+++ loki_setup/plugins/zip.c
@@ -663,7 +663,7 @@ zip_zipsize_end:
 /* Extract the file */
 static size_t ZIPCopy(install_info *info, const char *path, const char *dest, const char *current_option,
 					  xmlNodePtr node,
-					  int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
+					  UIUpdateFunc update)
 {
     char final[BUFSIZ];
     z_stream zstr;
Index: loki_setup/plugins/uz2.c
===================================================================
--- loki_setup.orig/plugins/uz2.c
+++ loki_setup/plugins/uz2.c
@@ -95,7 +95,7 @@ static size_t UZ2Size(install_info *info
 /* Extract the file */
 static size_t UZ2Copy(install_info *info, const char *path, const char *dest, const char *current_option,
 		      xmlNodePtr node,
-		      int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
+		      UIUpdateFunc update)
 {
     static uint8 cbuf[MAXCOMPSIZE];
     static uint8 ubuf[MAXUNCOMPSIZE];
Index: loki_setup/plugins.h
===================================================================
--- loki_setup.orig/plugins.h
+++ loki_setup/plugins.h
@@ -30,7 +30,7 @@ typedef struct {
 	/* Extract the file */
 	size_t (*Copy)(install_info *info, const char *path, const char *dest, const char *current_option,
 				   xmlNodePtr node,
-				   int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current));
+				   UIUpdateFunc update);
 
 } SetupPlugin;
 
Index: loki_setup/install.h
===================================================================
--- loki_setup.orig/install.h
+++ loki_setup/install.h
@@ -225,6 +225,9 @@ typedef struct _install_info {
 
 } install_info;
 
+/** callback for progress bar updates in the UI */
+typedef int (*UIUpdateFunc)(install_info *info, const char *path, size_t progress, size_t size, const char *current);
+
 /* Functions to retrieve attribution information from the XML tree */
 extern const char *GetProductName(install_info *info);
 extern const char *GetProductDesc(install_info *info);
@@ -353,8 +356,7 @@ extern void delete_install(install_info 
 extern void delete_cdrom_install(install_info *info);
 
 /* Actually install the selected filesets */
-extern install_state install(install_info *info,
-							 int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current));
+extern install_state install(install_info *info, UIUpdateFunc update);
 
 /* Abort a running installation (to be called from the update function) */
 extern void abort_install(void);
Index: loki_setup/plugins/cpio.c
===================================================================
--- loki_setup.orig/plugins/cpio.c
+++ loki_setup/plugins/cpio.c
@@ -55,7 +55,7 @@ static size_t CPIOSize(install_info *inf
 /* Exported so that the RPM plugin can access it */
 size_t copy_cpio_stream(install_info *info, stream *input, const char *dest, const char *current_option,
 			xmlNodePtr node,
-                        int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
+                        UIUpdateFunc update)
 {
     stream *output;
     char magic[6];
@@ -201,7 +201,7 @@ size_t copy_cpio_stream(install_info *in
 /* Extract the file */
 static size_t CPIOCopy(install_info *info, const char *path, const char *dest, const char *current_option, 
 					   xmlNodePtr node,
-					   int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
+					   UIUpdateFunc update)
 {
 	stream *input = file_open(info, path, "rb");
 	if(input)
Index: loki_setup/plugins/sample.c
===================================================================
--- loki_setup.orig/plugins/sample.c
+++ loki_setup/plugins/sample.c
@@ -34,7 +34,7 @@ static size_t Size(install_info *info, c
 /* Extract the file */
 static size_t Copy(install_info *info, const char *path, const char *dest, const char *current_option, 
 				   xmlNodePtr node,
-				   int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
+				   UIUpdateFunc update)
 {
 	/* TODO: Extract the files, calling the update function as often as possible. 
 	   Also check for optional MD5 sum, mode and mutable flag */
Index: loki_setup/plugins/cpio.h
===================================================================
--- loki_setup.orig/plugins/cpio.h
+++ loki_setup/plugins/cpio.h
@@ -128,6 +128,6 @@ struct new_cpio_header
 /* Exported so that the RPM plugin can access it */
 size_t copy_cpio_stream(install_info *info, stream *input, const char *dest, const char *current_option,
 			xmlNodePtr node,
-                        int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current));
+                        UIUpdateFunc update);
 
 #endif /* cpio.h */
Index: loki_setup/plugins/tar.c
===================================================================
--- loki_setup.orig/plugins/tar.c
+++ loki_setup/plugins/tar.c
@@ -40,7 +40,7 @@ static size_t TarSize(install_info *info
 /* Extract the file */
 static size_t TarCopy(install_info *info, const char *path, const char *dest, const char *current_option, 
 		      xmlNodePtr node,
-		      int (*update)(install_info *info, const char *path, size_t progress, size_t size, const char *current))
+		      UIUpdateFunc update)
 {
     static tar_record zeroes;
     tar_record record;
-------------- next part --------------
Index: loki_setup/install.c
===================================================================
--- loki_setup.orig/install.c
+++ loki_setup/install.c
@@ -1650,14 +1650,14 @@ install_state install(install_info *info
 	if ( f && ! GetProductIsMeta(info) ) {
 		if ( strstr(f, info->setup_path) == f )
 			f += strlen(info->setup_path)+1;
-		copy_path(info, f, info->install_path, NULL, !keepdirs, NULL, update);
+		copy_path(info, f, info->install_path, NULL, !keepdirs, NULL, NULL, update);
 	}
 	keepdirs = 0;
 	f = GetProductEULA(info, &keepdirs);
 	if ( f && ! GetProductIsMeta(info) ) {
 		if ( strstr(f, info->setup_path) == f )
 			f += strlen(info->setup_path)+1;
-		copy_path(info, f, info->install_path, NULL, !keepdirs, NULL, update);
+		copy_path(info, f, info->install_path, NULL, !keepdirs, NULL, NULL, update);
 	}
     if(info->options.install_menuitems){
 		int i;
Index: loki_setup/copy.h
===================================================================
--- loki_setup.orig/copy.h
+++ loki_setup/copy.h
@@ -12,6 +12,7 @@
 /* Copy a path to the destination directory */
 extern ssize_t copy_path(install_info *info, const char *path, 
 						 const char *dest, const char *cdrom, int strip_dirs,
+						 const char* suffix,
 						 xmlNodePtr node,
 						 UIUpdateFunc update);
 
Index: loki_setup/plugins.h
===================================================================
--- loki_setup.orig/plugins.h
+++ loki_setup/plugins.h
@@ -42,8 +42,12 @@ typedef struct {
 typedef SetupPlugin *(*GetSetupPluginPtr)(void);
 
 
-/* Return the plugin structure associated with a file (looks through the list of registered extensions */
-const SetupPlugin *FindPluginForFile(const char *path);
+/** Find the plugin structure associated with a file (looks through the list of registered extensions
+ * @param path file name
+ * @param suffix if non-NULL assume the file has this suffix to force a certain plugin
+ * @returns pointer to plugin or NULL if none matches
+ */
+const SetupPlugin *FindPluginForFile(const char *path, const char* suffix);
 
 /* Register a plugin */
 int RegisterPlugin(const SetupPlugin *plugin, void *handle);
Index: loki_setup/check.c
===================================================================
--- loki_setup.orig/check.c
+++ loki_setup/check.c
@@ -300,11 +300,11 @@ on_media_ok_clicked (GtkButton       *bu
 		*/
 		f = GetProductREADME(install, NULL);
 		if ( f && ! GetProductIsMeta(install) ) {
-			copy_path(install, f, install->install_path, NULL, 1, NULL, NULL);
+			copy_path(install, f, install->install_path, NULL, 1, NULL, NULL, NULL);
 		}
 		f = GetProductEULA(install, NULL);
 		if ( f && ! GetProductIsMeta(install) ) {
-			copy_path(install, f, install->install_path, NULL, 1, NULL, NULL);
+			copy_path(install, f, install->install_path, NULL, 1, NULL, NULL, NULL);
 		}
 
 	}
Index: loki_setup/copy.c
===================================================================
--- loki_setup.orig/copy.c
+++ loki_setup/copy.c
@@ -392,8 +392,8 @@ ssize_t copy_file(install_info *info, co
     return size;
 }
 
-size_t copy_directory(install_info *info, const char *path, const char *dest, 
-					  const char *cdrom, xmlNodePtr node,
+static size_t copy_directory(install_info *info, const char *path, const char *dest, 
+					  const char *cdrom, const char* suffix, xmlNodePtr node,
 					  UIUpdateFunc update)
 {
     char fpat[PATH_MAX];
@@ -415,7 +415,7 @@ size_t copy_directory(install_info *info
 		} else {
 			for ( i=0; i<globbed.gl_pathc; ++i ) {
 				copied = copy_path(info, globbed.gl_pathv[i], dest, cdrom, 0,
-								   node, update);
+								   suffix, node, update);
 				if ( copied > 0 ) {
 					size += copied;
 				}
@@ -429,7 +429,7 @@ size_t copy_directory(install_info *info
 }
 
 ssize_t copy_path(install_info *info, const char *path, const char *dest, 
-		  const char *cdrom, int strip_dirs, xmlNodePtr node,
+		  const char *cdrom, int strip_dirs, const char* suffix, xmlNodePtr node,
 		  UIUpdateFunc update)
 {
     char final[PATH_MAX];
@@ -442,9 +442,9 @@ ssize_t copy_path(install_info *info, co
 
     if ( ! stat(path, &sb) ) {
         if ( S_ISDIR(sb.st_mode) ) {
-            copied = copy_directory(info, path, dest, cdrom, node, update);
+            copied = copy_directory(info, path, dest, cdrom, suffix, node, update);
         } else {
-			const SetupPlugin *plug = FindPluginForFile(path);
+			const SetupPlugin *plug = FindPluginForFile(path, suffix);
 			if (plug) {
 				copied = plug->Copy(info, path, dest, current_option_txt, node, update);
 			} else {
@@ -465,8 +465,10 @@ ssize_t copy_path(install_info *info, co
     return size;
 }
 
-ssize_t copy_list(install_info *info, const char *filedesc, const char *dest, 
-		  const char *from_cdrom, const char *srcpath, int strip_dirs, xmlNodePtr node,
+static ssize_t copy_list(install_info *info, const char *filedesc, const char *dest, 
+		  const char *from_cdrom, const char *srcpath, int strip_dirs,
+		  const char* suffix,
+		  xmlNodePtr node,
 		  UIUpdateFunc update)
 {
     char fpat[BUFSIZ];
@@ -494,7 +496,7 @@ ssize_t copy_list(install_info *info, co
             if ( glob(fpat, GLOB_ERR, NULL, &globbed) == 0 ) {
                 for ( i=0; i<globbed.gl_pathc; ++i ) {
                     copied = copy_path(info, globbed.gl_pathv[i], dest, 
-                                       full_cdpath, strip_dirs, node, update);
+                                       full_cdpath, strip_dirs, suffix, node, update);
                     if ( copied > 0 ) {
                         size += copied;
                     }
@@ -509,7 +511,7 @@ ssize_t copy_list(install_info *info, co
             if ( glob(fpat, GLOB_ERR, NULL, &globbed) == 0 ) {
                 for ( i=0; i<globbed.gl_pathc; ++i ) {
                     copied = copy_path(info, globbed.gl_pathv[i], dest, NULL,
-                                       strip_dirs, node, update);
+                                       strip_dirs, suffix, node, update);
                     if ( copied > 0 ) {
                         size += copied;
                     }
@@ -917,16 +919,18 @@ ssize_t copy_node(install_info *info, xm
 
 
 			if ( strcmp(node->name, "files") == 0 ) {
+				char* suffix = xmlGetProp(node, "suffix");
 				const char *str = xmlNodeListGetString(info->config, (node->parent)->childs, 1);
             
 				parse_line(&str, current_option_txt, sizeof(current_option_txt));
 				copied = copy_list(info,
 						   xmlNodeListGetString(info->config, node->childs, 1),
-						   path, from_cdrom, srcpath, strip_dirs, node,
-						   update);
+						   path, from_cdrom, srcpath, strip_dirs, suffix,
+						   node, update);
 				if ( copied > 0 ) {
 					size += copied;
 				}
+				xmlFree(suffix);
 			} else if ( strcmp(node->name, "binary") == 0 ) {
 				copied = copy_binary(info, node,
 						     xmlNodeListGetString(info->config, node->childs, 1),
@@ -1154,7 +1158,8 @@ ssize_t size_binary(install_info *info, 
 }
 
 /* Returns the install size of a list of files, in bytes */
-ssize_t size_list(install_info *info, const char *from_cdrom, const char *srcpath, const char *filedesc)
+static ssize_t size_list(install_info *info, const char *from_cdrom, const char *srcpath,
+		const char *filedesc, const char* suffix)
 {
     char fpat[PATH_MAX];
     char fullpath[PATH_MAX];
@@ -1173,7 +1178,7 @@ ssize_t size_list(install_info *info, co
             snprintf(fullpath, sizeof(fullpath), "%s/%s/%s", cdpath, srcpath, fpat);
             if ( glob(fullpath, GLOB_ERR, NULL, &globbed) == 0 ) {
                 for ( i=0; i<globbed.gl_pathc; ++i ) {
-                    const SetupPlugin *plug = FindPluginForFile(globbed.gl_pathv[i]);
+                    const SetupPlugin *plug = FindPluginForFile(globbed.gl_pathv[i], suffix);
                     if (plug) {
                         count = plug->Size(info, globbed.gl_pathv[i]);
                     } else {
@@ -1193,7 +1198,7 @@ ssize_t size_list(install_info *info, co
             snprintf(fullpath, sizeof(fullpath), "%s/%s", srcpath, fpat);
             if ( glob(fullpath, GLOB_ERR, NULL, &globbed) == 0 ) {
                 for ( i=0; i<globbed.gl_pathc; ++i ) {
-					const SetupPlugin *plug = FindPluginForFile(globbed.gl_pathv[i]);
+					const SetupPlugin *plug = FindPluginForFile(globbed.gl_pathv[i], suffix);
 					if (plug) {
 						count = plug->Size(info, globbed.gl_pathv[i]);
 					} else {
@@ -1218,7 +1223,7 @@ ssize_t size_readme(install_info *info, 
 
     lang_prop = xmlGetProp(node, "lang");
 	if (lang_prop && match_locale(lang_prop) ) {
-		ret = size_list(info, 0, ".", xmlNodeListGetString(info->config, node->childs, 1));
+		ret = size_list(info, 0, ".", xmlNodeListGetString(info->config, node->childs, 1), NULL);
 	}
 	xmlFree(lang_prop);
 	return ret;
@@ -1279,8 +1284,10 @@ unsigned long long size_node(install_inf
 				 match_libc(info, xmlGetProp(node, "libc")) &&
 				 match_distro(info, xmlGetProp(node, "distro"))) {
 				if ( strcmp(node->name, "files") == 0 ) {
+					char* suffix = xmlGetProp(node, "suffix");
 					size += size_list(info, from_cdrom, srcpath,
-									  xmlNodeListGetString(info->config, node->childs, 1));
+									  xmlNodeListGetString(info->config, node->childs, 1), suffix);
+					xmlFree(suffix);
 				} else if ( strcmp(node->name, "binary") == 0 ) {
 					if(!xmlNodePropIsTrue(node, "inline"))
 						size += size_binary(info, from_cdrom,
Index: loki_setup/plugins.c
===================================================================
--- loki_setup.orig/plugins.c
+++ loki_setup/plugins.c
@@ -19,33 +19,32 @@ struct plugin_list {
 /* The list of registered plugins */
 static struct plugin_list *plugins = NULL;
 
-/* Return the plugin structure associated with a file (looks through the list of registered extensions */
-const SetupPlugin *FindPluginForFile(const char *path)
+/** Find the plugin structure associated with a file (looks through the list of registered extensions
+ * @param path file name
+ * @param suffix if non-NULL assume the file has this suffix to force a certain plugin
+ * @returns pointer to plugin or NULL if none matches
+ */
+const SetupPlugin *FindPluginForFile(const char *path, const char* suffix)
 {
-	const char *ext;
+	const char* p = suffix?suffix:path;
+	unsigned plen = strlen(p);
+	unsigned i;
 	struct plugin_list *plug = plugins;
 
-	/* Isolate the extension */
-	ext = strrchr(path, '/');
-	if ( ! ext ) {
-		ext = path;
-	}
-	ext = strchr(ext, '.');
-	if ( ! ext ) { /* File doesn't have an extension */
-		return NULL;
-	}
-
-	while ( plug ) {
-		int i;
+	for(plug = plugins; plug; plug = plug->next)
+	{
 		for ( i = 0; i < plug->plugin->num_extensions; ++i ) {
-			const char *str = strstr(path, plug->plugin->extensions[i]);
-			/* The lenght of the strings should match if the extension is at the end */
-			if( str && strlen(str) == strlen(plug->plugin->extensions[i])) {
+			const char* ext = plug->plugin->extensions[i];
+			unsigned len = strlen(ext);
+			
+			if(plen < len)
+				continue;
+
+			if(!strcmp(p+plen-len, ext))
 				return plug->plugin;
-			}
 		}
-		plug = plug->next;
 	}
+
 	return NULL;
 }
 
Index: loki_setup/check_carbon.c
===================================================================
--- loki_setup.orig/check_carbon.c
+++ loki_setup/check_carbon.c
@@ -246,11 +246,11 @@ void DoMediaCheck(const char *Dir)
 		*/
 		f = GetProductREADME(install, NULL);
 		if ( f && ! GetProductIsMeta(install) ) {
-			copy_path(install, f, install->install_path, NULL, 1, NULL, NULL);
+			copy_path(install, f, install->install_path, NULL, 1, NULL, NULL, NULL);
 		}
 		f = GetProductEULA(install, NULL);
 		if ( f && ! GetProductIsMeta(install) ) {
-			copy_path(install, f, install->install_path, NULL, 1, NULL, NULL);
+			copy_path(install, f, install->install_path, NULL, 1, NULL, NULL, NULL);
 		}
 
 	}
Index: loki_setup/README.xml
===================================================================
--- loki_setup.orig/README.xml
+++ loki_setup/README.xml
@@ -739,6 +739,10 @@ There are several optional attributes of
             NOTE: This doesn't apply to directories, which are currently always created with
             mode 755.
 
+ suffix     An optional file extension that is forced on all files. Can be used
+	    to make loki-setup recognize e.g. SFX ZIP archives as such even if
+	    they end in .exe.
+
 The MANPAGE element:
 
 If your product comes with man pages (destined to be installed system-wide), they have to be
-------------- next part --------------
Index: loki_setup/plugins/zip.c
===================================================================
--- loki_setup.orig/plugins/zip.c
+++ loki_setup/plugins/zip.c
@@ -39,7 +39,7 @@
 
 /* legacy defines from PhysicsFS... */
 #define BAIL_MACRO(err, val) { log_debug("ZIP: %s", (err == NULL) ? "i/o error" : err); return(val); }
-#define BAIL_IF_MACRO(cond, err, val) { if (cond) { log_debug("ZIP: %s", (err == NULL) ? "i/o error" : err); return(val); } }
+#define BAIL_IF_MACRO(cond, err, val) { if (cond) { log_debug("ZIP (line %d): %s", __LINE__, (err == NULL) ? "i/o error" : err); return(val); } }
 #define ERR_CORRUPTED           "Corrupted archive"
 #define ERR_NOT_AN_ARCHIVE      "Not a ZIP archive"
 #define ERR_OUT_OF_MEMORY       "Out of memory"
@@ -521,7 +521,7 @@ static int zip_parse_end_of_central_dir(
      *  If it doesn't, we're either in the wrong part of the file, or the
      *  file is corrupted, but we give up either way.
      */
-    BAIL_IF_MACRO((pos + 22 + ui16) != len, ERR_UNSUPPORTED_ARCHIVE, 0);
+    BAIL_IF_MACRO((pos + 22 + ui16) > len, ERR_UNSUPPORTED_ARCHIVE, 0);
 
     return(1);  /* made it. */
 } /* zip_parse_end_of_central_dir */
@@ -712,7 +712,7 @@ static size_t ZIPCopy(install_info *info
         if (entry->name[strlen(entry->name) - 1] == '/')
         {
             final[strlen(final) - 1] = '\0';  /* lose '/' at end. */
-            dir_create_hierarchy(info, final, S_IRWXU);
+            dir_create_hierarchy(info, final, 0755);
             continue;
         } /* if */
 
@@ -729,8 +729,11 @@ static size_t ZIPCopy(install_info *info
         if (entry->compression_method != COMPMETH_NONE)
         {
             memset(&zstr, '\0', sizeof (z_stream));
-            if (zlib_err(inflateInit2(&zstr, -MAX_WBITS)) != Z_OK)
+            if ((rc = inflateInit2(&zstr, -MAX_WBITS)) != Z_OK)
+            {
+                zlib_err(rc);
                 continue;
+            }
             zstr.next_out = zip_buf_out;
             zstr.avail_out = ZIP_WRITEBUFSIZE;
         } /* if */
@@ -801,9 +804,12 @@ static size_t ZIPCopy(install_info *info
                     } /* else */
                 } /* if */
 
-                rc = zlib_err(inflate(&zstr, Z_SYNC_FLUSH));
+                rc = inflate(&zstr, Z_SYNC_FLUSH);
                 if ((rc != Z_OK) && (rc != Z_STREAM_END))
+                {
+                    zlib_err(rc);
                     break;
+                }
 
                 /* if output buffer has data, dump it to disk. */
                 if (zstr.avail_out < ZIP_WRITEBUFSIZE)
-------------- next part --------------
Index: loki_setup/copy.c
===================================================================
--- loki_setup.orig/copy.c
+++ loki_setup/copy.c
@@ -975,41 +975,46 @@ ssize_t copy_tree(install_info *info, xm
 
     size = 0;
     while ( node ) {
-        const char *wanted;
-
 		if ( ! strcmp(node->name, "option") ) {
-			wanted = xmlGetProp(node, "install");
-			if ( wanted  && (strcmp(wanted, "true") == 0) ) {
-				const char *product = xmlGetProp(node, "product");
+			if ( xmlNodePropIsTrue(node, "install") ) {
+				char *product = xmlGetProp(node, "product");
 				if ( product ) {
 					if ( GetProductIsMeta(info) ) {
+						char *dir = xmlGetProp(node, "productdir");
+						// XXX sucks
 						extern const char *argv0; // Set in main.c
-                        if (UI.shutdown) UI.shutdown(info);
-						// We spawn a new setup for this product
+						if (UI.shutdown) UI.shutdown(info);
+						// We spawn a new setup for this product (#1868)
 #if defined(darwin)
-                        if (fork()) {
-                            if (UI.is_gui) {
-                                exit(0);
-                            } else {
-                                int status;
-                                wait(&status);
-                                exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1);
-                            }
-			}
+						if (fork()) {
+							if (UI.is_gui) {
+								exit(0);
+							} else {
+								int status;
+								wait(&status);
+								exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1);
+							}
+						}
 #endif
+						log_warning("directory: %s", dir);
+						if(dir && chdir(dir) == -1)
+							log_fatal("Could not change directory to %s: %s", dir, strerror(errno));
 						execlp(argv0, argv0, "-f", product, NULL);
-						perror("execlp");
+						log_fatal("Could not run new installer for selected product: %s", strerror(errno));
 					} else {
 						log_fatal("'product' attributes can only be used in files with the 'meta' attribute.");
 					}
 				} else {
-					const char *deviant_path = xmlGetProp(node, "path");
-					if (!deviant_path) {
+					const char* deviant_path = NULL;
+					char *prop = xmlGetProp(node, "path");
+					if (!prop) {
 						deviant_path = info->install_path;
 					} else {
+						deviant_path = prop;
 						parse_line(&deviant_path, tmppath, PATH_MAX);
 						deviant_path = tmppath;
 					}
+					xmlFree(prop);
 					copied = copy_node(info, node, deviant_path, update);
 					if ( copied > 0 ) {
 						size += copied;
Index: loki_setup/README.xml
===================================================================
--- loki_setup.orig/README.xml
+++ loki_setup/README.xml
@@ -486,6 +486,10 @@ There are several optional attributes of
             is ignored if this is not a meta-installation, and required if it is.
             See the section 'About Meta-Installations' below for more details.
 
+ productdir Valid only together with the 'product' attribute. Specifies the
+            directory to change to before executing the installer for the new
+            product.
+
  tag        If specified, this string will be included in the SETUP_OPTIONTAGS
             environment variable that is set before calling any scripts. This allows
             scripts to know what user selections are active.
-------------- next part --------------
Index: loki_setup/install.c
===================================================================
--- loki_setup.orig/install.c
+++ loki_setup/install.c
@@ -352,16 +352,16 @@ int GetProductPromptOverwrite(install_in
     return ret;
 }
 
-/* returns true if any deviant paths are not writable */
-char check_deviant_paths(xmlNodePtr node, install_info *info)
+/* returns true if any deviant paths are not writable. If path_ret is non-NULL
+ * it must point to a buffer of at least PATH_MAX characters. The first not
+ * writeable path is stored there if return value is true
+ */
+static char check_deviant_paths(xmlNodePtr node, install_info *info, char* path_ret)
 {
-    char path_up[PATH_MAX];
-
     while ( node ) {
         char *wanted;
         char *orig_dpath;
 		const char *dpath;
-        char deviant_path[PATH_MAX];
 
         wanted = xmlGetProp(node, "install");
         if ( wanted  && (strcmp(wanted, "true") == 0) ) {
@@ -369,22 +369,29 @@ char check_deviant_paths(xmlNodePtr node
             while ( elements ) {
                 dpath = orig_dpath = xmlGetProp(elements, "path");
                 if ( dpath ) {
+					char path_up[PATH_MAX];
+					char deviant_path[PATH_MAX];
 					parse_line(&dpath, deviant_path, PATH_MAX);
                     topmost_valid_path(path_up, deviant_path);
 					if ( path_up[0] != '/' ) { /* Not an absolute path */
 						char buf[PATH_MAX];
 						snprintf(buf, PATH_MAX, "%s/%s", info->install_path, path_up);
 						xmlFree(orig_dpath);
-						return ! dir_is_accessible(buf);
+						if (!dir_is_accessible(buf))
+						{
+							if(path_ret) strcpy(path_ret, buf);
+							return 1;
+						}
 					} else if ( ! dir_is_accessible(path_up) ) {
 						xmlFree(orig_dpath);
-                        return 1;
+						if(path_ret) strcpy(path_ret, path_up);
+						return 1;
 					}
 					xmlFree(orig_dpath);
                 }
                 elements = elements->next;
             }
-            if (check_deviant_paths(node->childs, info))
+            if (check_deviant_paths(node->childs, info, path_ret))
                 return 1;
         }
 		xmlFree(wanted);
@@ -393,7 +400,31 @@ char check_deviant_paths(xmlNodePtr node
     return 0;
 }
 
-const char *IsReadyToInstall(install_info *info)
+/** check path and return a string that explains who owns the directory. string
+ * must be freed manually */
+static char* explain_nowritepermission(const char* path)
+{
+    struct stat st;
+	char* buf = NULL;
+	// translator: directory, user name, user name
+	const char* msg = _("%s is owned by %s."
+	"You may need to run this installer as user %s or choose another directory.");
+
+	if(stat(path, &st) == 0) {
+		struct passwd* pw = getpwuid(st.st_uid);
+		if(pw) {
+			size_t buflen = strlen(msg)+strlen(path)+strlen(pw->pw_name)*2+1;
+			buf = malloc(buflen);
+			snprintf(buf, buflen, msg, path, pw->pw_name, pw->pw_name);
+		}
+	}
+
+	return buf;
+}
+
+/** explanation must be freed manually */
+static const char *_IsReadyToInstall(install_info *info, char** explanation)
+
 {
 	const char *message = NULL;
 	char path_up[PATH_MAX];
@@ -410,23 +441,47 @@ const char *IsReadyToInstall(install_inf
 			message = _("Please select at least one option");
 		}
     } else if ( BYTES2MB(info->install_size) > detect_diskspace(info->install_path) ) {
-        message = _("Not enough free space for the selected options");
+        message = _("Not enough free space for the selected options.");
     } else if ( (stat(path_up, &st) == 0) && !S_ISDIR(st.st_mode) ) {
-        message = _("Install path is not a directory");
+        message = _("Install path is not a directory.");
+		if(explanation) *explanation = strdup(_("Please choose an existing directory."));
     } else if ( access(path_up, W_OK) < 0 ) {
-        message = _("No write permissions on the install directory");
+        message = _("No write permissions on the install directory.");
+		if(explanation) *explanation = explain_nowritepermission(path_up);
 	} else if (strcmp(info->symlinks_path, info->install_path) == 0) {
-		message = _("Binary path and install path must be different");
-	} else if ( check_deviant_paths(info->config->root->childs, info) ) {
-        message = _("No write permissions to install a selected package");
-    } else if ( info->symlinks_path[0] &&
-				(access(info->symlinks_path, W_OK) < 0) ) {
-        message = _("No write permissions on the binary directory");
+		message = _("Binary path and install path must be different.");
+	} else if ( check_deviant_paths(info->config->root->childs, info, path_up) ) {
+        message = _("No write permissions to install a selected package.");
+		if(explanation) *explanation = explain_nowritepermission(path_up);
+    } else if ( info->symlinks_path[0]) {
+		if (stat(info->symlinks_path, &st) == 0) {
+			if(!S_ISDIR(st.st_mode)) {
+				message = _("Binary path is not a directory.");
+			}
+			else if (access(info->symlinks_path, W_OK) < 0) {
+				message = _("No write permissions on the binary directory.");
+				if(explanation) *explanation = explain_nowritepermission(info->symlinks_path);
+			}
+		}
+		else {
+			message = _("Binary directory does not exist.");
+			if(explanation) *explanation = strdup(_("Please choose an existing directory."));
+		}
     }
 
 	return message;
 }
 
+const char *IsReadyToInstall(install_info *info)
+{
+	return _IsReadyToInstall(info, NULL);
+}
+
+const char *IsReadyToInstall_explain(install_info *info, char** explanation)
+{
+	return _IsReadyToInstall(info, explanation);
+}
+
 const char *GetProductCDROMFile(install_info *info)
 {
     return xmlGetProp(info->config->root, "cdromfile");
Index: loki_setup/install.h
===================================================================
--- loki_setup.orig/install.h
+++ loki_setup/install.h
@@ -270,7 +270,16 @@ extern const char *GetProductPostInstall
 /** whether the user should be prompted when files already exist */
 extern int GetProductPromptOverwrite(install_info *info);
 
+/** check if product can be installed. Returns NULL if product can be
+ * installed, otherwise reason why not. */
 extern const char *IsReadyToInstall(install_info *info);
+
+/** same as IsReadyToInstall but may additionally return a more verbose
+ * explanation of what went wrong in the variable explanation. This string must
+ * be freed manually if set.
+ */
+extern const char *IsReadyToInstall_explain(install_info *info, char** explanation);
+
 extern int         CheckRequirements(install_info *info);
 
 /* Create the initial installation information */
@@ -396,9 +405,6 @@ extern int install_menuitems(install_inf
  */
 extern int run_script(install_info *info, const char *script, int arg, int include_tags);
 
-/* returns true if any deviant paths are not writable */
-char check_deviant_paths(xmlNodePtr node, install_info *info);
-
 /* Convenience functions to quickly change back and forth between current directories */
 
 extern void push_curdir(const char *path);
Index: loki_setup/gtk_ui.c
===================================================================
--- loki_setup.orig/gtk_ui.c
+++ loki_setup/gtk_ui.c
@@ -155,7 +155,7 @@ static const char* glade_file = SETUP_GL
 
 /******** Local prototypes **********/
 
-static const char *check_for_installation(install_info *info);
+static const char *check_for_installation(install_info *info, char** explanation);
 static void check_install_button(void);
 static void update_space(void);
 static void update_size(void);
@@ -313,7 +313,7 @@ void on_class_continue_clicked( GtkWidge
 	express_setup = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
 
 	if ( express_setup ) {
-		const char *msg = check_for_installation(cur_info);
+		const char *msg = check_for_installation(cur_info, NULL);
 		if ( msg ) {
 			char buf[BUFSIZ];
 			snprintf(buf, sizeof(buf),
@@ -543,9 +543,27 @@ void setup_button_cdkey_continue_slot( G
 
 void setup_button_install_slot( GtkWidget* widget, gpointer func_data )
 {
+	const char* message;
+	char* explanation = NULL;
     GtkWidget *notebook;
     notebook = glade_xml_get_widget(setup_glade, "setup_notebook");
 
+	message = check_for_installation(cur_info, &explanation);
+
+	if(message)
+	{
+		if(explanation)
+		{
+			char* tmp = g_strconcat(message, "\n\n", explanation, NULL);
+			g_free(explanation);
+			explanation = tmp;
+		}
+
+		gtkui_prompt(explanation?explanation:message, RESPONSE_OK);
+		g_free(explanation);
+		return;
+	}
+
     /* If CDKEY attribute was specified, show the CDKEY screen */
     if(GetProductCDKey(cur_info))
     {
@@ -575,12 +593,12 @@ void setup_button_browser_slot( GtkWidge
 }
 
 /* Returns NULL if installation can be performed */
-static const char *check_for_installation(install_info *info)
+static const char *check_for_installation(install_info *info, char** explanation)
 {
     if ( ! license_okay ) {
         return _("Please respond to the license dialog");
     }
-	return IsReadyToInstall(info);
+	return IsReadyToInstall_explain(info, explanation);
 }
 
 /* Checks if we can enable the "Begin install" button */
@@ -588,18 +606,14 @@ static void check_install_button(void)
 {
     const char *message;
     GtkWidget *options_status;
-    GtkWidget *button_install;
 
-	message = check_for_installation(cur_info);
+	message = check_for_installation(cur_info, NULL);
 
     /* Get the appropriate widgets and set the new state */
     options_status = glade_xml_get_widget(setup_glade, "options_status");
-    button_install = glade_xml_get_widget(setup_glade, "button_install");
-    if ( message ) {
-        gtk_widget_set_sensitive(button_install, 0);
-    } else {
+
+    if ( !message ) {
         message = _("Ready to install!");
-        gtk_widget_set_sensitive(button_install, 1);
     }
     gtk_label_set_text(GTK_LABEL(options_status), message);
 }
@@ -863,6 +877,7 @@ static yesno_answer gtkui_prompt(const c
     dialog = gtk_dialog_new();
     label = gtk_label_new (txt);
 	gtk_misc_set_padding(GTK_MISC(label), 8, 8);
+	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
     ok_button = gtk_button_new_with_label(_("OK"));
 
     prompt_response = RESPONSE_INVALID;
@@ -1608,7 +1623,7 @@ static install_state gtkui_init(install_
     license_okay = 1; /* Needed so that Expert is detected properly at this point */
     
 	/* Check if we should check "Expert" installation by default */
-	if ( check_for_installation(info) ) {
+	if ( check_for_installation(info, NULL) ) {
 		widget = glade_xml_get_widget(setup_glade, "expert_but");
 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
 	}
Index: loki_setup/image/setup.data/setup.glade
===================================================================
--- loki_setup.orig/image/setup.data/setup.glade
+++ loki_setup/image/setup.data/setup.glade
@@ -387,7 +387,7 @@
 	      <widget>
 		<class>GtkLabel</class>
 		<name>binary_label</name>
-		<label>Link path:</label>
+		<label>Binary path:</label>
 		<justify>GTK_JUSTIFY_RIGHT</justify>
 		<wrap>False</wrap>
 		<xalign>1</xalign>
-------------- next part --------------
Index: loki_setup/loki_setupdb/setupdb.c
===================================================================
--- loki_setup.orig/loki_setupdb/setupdb.c
+++ loki_setup/loki_setupdb/setupdb.c
@@ -12,6 +12,7 @@
 #include <sys/utsname.h>
 #include <stdio.h>
 #include <string.h>
+#include <errno.h>
 #ifdef HAVE_STRINGS_H
 #include <strings.h>
 #endif
@@ -680,12 +681,24 @@ void loki_setupdateurl_product(product_t
 
 int loki_closeproduct(product_t *product)
 {
+    int ret = 0;
     product_component_t *comp, *next;
 	product_envvar_t *var, *nextvar;
 
     if ( product->changed ) {
+        char tmp[PATH_MAX];
+        /* This isn't harmful as long as it's not a world writeable directory */
+        snprintf(tmp, sizeof(tmp), "%s.%05d", product->info.registry_path, getpid());
         /* Write XML file to disk if it has changed */
-        xmlSaveFile(product->info.registry_path, product->doc);
+        xmlSaveFile(tmp, product->doc);
+
+        if(rename(tmp, product->info.registry_path) != 0)
+        {
+            /* too bad but we can't do much about it */
+            fprintf(stderr, "Unable to overwrite %s: %s.\nRegistry saved as %s.\n",
+                    product->info.registry_path, strerror(errno), tmp);
+            ret = -1;
+        }
     }
 
     /* Go through all the allocated structs */
@@ -751,7 +764,7 @@ int loki_closeproduct(product_t *product
 	}
 
     free(product);
-    return 0;
+    return ret;
 }
 
 /* Clean up a product from the registry, i.e. removes all support files and directories */
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
URL: <http://icculus.org/pipermail/lokisetup/attachments/20041031/01a02e91/attachment.pgp>


More information about the Lokisetup mailing list