#include <string.h>
#include <time.h>
#include <stdio.h>
+#include <glib/gstrfuncs.h>
#include "bundle-util.h" /* self */
#include "bundle.h"
return TRUE;
}
-gboolean bundle_util_file_write(const gchar *pathname,const gchar *basename,mode_t pathname_mode,gboolean pathname_backup)
+struct dir_stack {
+ struct dir_stack *next;
+ gchar *dirname; /* in fact 'pathname' */
+ };
+
+/* Always creates up to 'dirname(pathname)', never 'pathname' itself! */
+static struct dir_stack *pathname_mkdirs(const gchar *pathname,mode_t mode)
+{
+gchar *dirname;
+struct dir_stack *r;
+
+ g_return_val_if_fail(pathname!=NULL,NULL);
+
+ dirname=g_path_get_dirname(pathname);
+ if (!strcmp(dirname,pathname)) {
+ g_free(dirname);
+ dirname=NULL;
+ }
+ if (!dirname)
+ return NULL;
+ r=pathname_mkdirs(dirname,mode);
+ if (mkdir(dirname,mode))
+ g_free(dirname);
+ else {
+struct dir_stack *dir_stack;
+
+ udpgate_new(dir_stack);
+ dir_stack->dirname=dirname;
+ dir_stack->next=r;
+ r=dir_stack;
+ }
+ return r;
+}
+
+static struct dir_stack *bundle_util_file_write_atexit_dir_stack_head;
+struct file_stack {
+ struct file_stack *next;
+ gchar *pathname;
+ gchar *basename;
+ };
+static struct file_stack *bundle_util_file_write_atexit_file_stack_head;
+
+static void bundle_util_file_write_atexit(void)
+{
+struct file_stack *file_stack;
+struct dir_stack *dir_stack;
+
+ /* Always remove files first before their directories! */
+ while ((file_stack=bundle_util_file_write_atexit_file_stack_head)) {
+ /* Errors already reported: */
+ bundle_util_file_remove(file_stack->pathname,file_stack->basename);
+ g_free(file_stack->pathname);
+ g_free(file_stack->basename);
+ bundle_util_file_write_atexit_file_stack_head=file_stack->next;
+ g_free(file_stack);
+ }
+
+ while ((dir_stack=bundle_util_file_write_atexit_dir_stack_head)) {
+ if (rmdir(dir_stack->dirname))
+ g_warning(_("Error cleaning up created temporary directory: %s"),dir_stack->dirname);
+ g_free(dir_stack->dirname);
+ bundle_util_file_write_atexit_dir_stack_head=dir_stack->next;
+ g_free(dir_stack);
+ }
+}
+
+gboolean bundle_util_file_write(const gchar *pathname,const gchar *basename,mode_t pathname_mode,
+ enum bundle_util_flags flags)
{
const guint8 *data;
guint32 data_length;
int fd;
+struct dir_stack *mkdirs=NULL;
+static gboolean atexited=FALSE;
g_return_val_if_fail(pathname!=NULL,FALSE);
g_return_val_if_fail(basename!=NULL,FALSE);
+ g_return_val_if_fail(flags&(BUNDLE_UTIL_BACKUP_MASK|BUNDLE_UTIL_MKDIRS_MASK|BUNDLE_UTIL_TEMPORARY_MASK),FALSE);
+ /* Currently just unsupported: */
+ g_return_val_if_fail((flags&(BUNDLE_UTIL_MKDIRS_MASK|BUNDLE_UTIL_TEMPORARY_MASK))!=BUNDLE_UTIL_MKDIRS_MASK,FALSE);
if (!(data=bundle_util_file_retrieve(&data_length,basename)))
return FALSE;
- if (pathname_backup && !bundle_util_file_backup_conditional(pathname,basename))
+ if ((flags&BUNDLE_UTIL_BACKUP_MASK) && !bundle_util_file_backup_conditional(pathname,basename))
return FALSE;
+
+ if (flags&BUNDLE_UTIL_MKDIRS_MASK) {
+mode_t dir_mode=pathname_mode;
+
+ dir_mode|=(pathname_mode&0444)>>2; /* mode|=('r'->'x') */
+ mkdirs=pathname_mkdirs(pathname,dir_mode);
+ }
+
+ if (!atexited) {
+ atexited=TRUE;
+ g_atexit(bundle_util_file_write_atexit);
+ }
+
+ if (flags&BUNDLE_UTIL_TEMPORARY_MASK) {
+struct dir_stack **mkdirs_tail_pointer;
+struct file_stack *file_stack;
+
+ /* Stack current 'mkdirs' in front of the current: bundle_util_file_write_atexit_dir_stack_head
+ * to remove them in the reverse order than created.
+ * Removal ordering of the current 'mkdirs' stack is preserved
+ * as it is already reversed.
+ */
+ for (mkdirs_tail_pointer=&mkdirs;*mkdirs_tail_pointer;mkdirs_tail_pointer=&(*mkdirs_tail_pointer)->next);
+ *mkdirs_tail_pointer=bundle_util_file_write_atexit_dir_stack_head;
+ bundle_util_file_write_atexit_dir_stack_head=mkdirs;
+ mkdirs=NULL;
+
+ /* Register also the file itself. */
+ udpgate_new(file_stack);
+ file_stack->pathname=g_strdup(pathname);
+ file_stack->basename=g_strdup(basename);
+ file_stack->next=bundle_util_file_write_atexit_file_stack_head;
+ bundle_util_file_write_atexit_file_stack_head=file_stack;
+ }
+ /* Currently just unsupported: */
+ g_assert(!mkdirs);
if (-1==(fd=open(pathname,O_WRONLY|O_CREAT|O_TRUNC,pathname_mode))) {
- g_warning(_("Error opening the file \"%s\" for rewrite: %m"),pathname);
+ if (errno!=EPERM)
+ g_warning(_("Error opening the file \"%s\" for rewrite: %m"),pathname);
return FALSE;
}
if ((int)data_length!=write(fd,data,data_length)) {