Add a note about casting the results of g_new() and g_new0().
[platform/upstream/glib.git] / glib / gfileutils.c
index 415d6fe..382564d 100644 (file)
@@ -20,7 +20,6 @@
 
 #include "config.h"
 
-#include "galias.h"
 #include "glib.h"
 
 #include <sys/stat.h>
 #include "gstdio.h"
 #include "glibintl.h"
 
+#include "galias.h"
+
+static gint create_temp_file (gchar *tmpl, 
+                             int    permissions);
+
+/**
+ * g_mkdir_with_parents:
+ * @pathname: a pathname in the GLib file name encoding
+ * @mode: permissions to use for newly created directories
+ *
+ * Create a directory if it doesn't already exist. Create intermediate
+ * parent directories as needed, too.
+ *
+ * Returns: 0 if the directory already exists, or was successfully
+ * created. Returns -1 if an error occurred, with errno set.
+ *
+ * Since: 2.8
+ */
+int
+g_mkdir_with_parents (const gchar *pathname,
+                     int          mode)
+{
+  gchar *fn, *p;
+
+  if (pathname == NULL || *pathname == '\0')
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  fn = g_strdup (pathname);
+
+  if (g_path_is_absolute (fn))
+    p = (gchar *) g_path_skip_root (fn);
+  else
+    p = fn;
+
+  do
+    {
+      while (*p && !G_IS_DIR_SEPARATOR (*p))
+       p++;
+      
+      if (!*p)
+       p = NULL;
+      else
+       *p = '\0';
+      
+      if (!g_file_test (fn, G_FILE_TEST_EXISTS))
+       {
+         if (g_mkdir (fn, mode) == -1)
+           {
+             int errno_save = errno;
+             g_free (fn);
+             errno = errno_save;
+             return -1;
+           }
+       }
+      else if (!g_file_test (fn, G_FILE_TEST_IS_DIR))
+       {
+         g_free (fn);
+         errno = ENOTDIR;
+         return -1;
+       }
+      if (p)
+       {
+         *p++ = G_DIR_SEPARATOR;
+         while (*p && G_IS_DIR_SEPARATOR (*p))
+           p++;
+       }
+    }
+  while (p);
+
+  g_free (fn);
+
+  return 0;
+}
+
 /**
  * g_file_test:
  * @filename: a filename to test in the GLib file name encoding
@@ -472,6 +548,7 @@ get_contents_stdio (const gchar *display_filename,
   gchar *str = NULL;
   size_t total_bytes = 0;
   size_t total_allocated = 0;
+  gchar *tmp;
 
   g_assert (f != NULL);
 
@@ -489,9 +566,9 @@ get_contents_stdio (const gchar *display_filename,
           else
             total_allocated = MIN (bytes + 1, sizeof (buf));
 
-          str = g_try_realloc (str, total_allocated);
+          tmp = g_try_realloc (str, total_allocated);
 
-          if (str == NULL)
+          if (tmp == NULL)
             {
               g_set_error (error,
                            G_FILE_ERROR,
@@ -502,6 +579,8 @@ get_contents_stdio (const gchar *display_filename,
 
               goto error;
             }
+
+         str = tmp;
         }
 
       if (ferror (f))
@@ -522,6 +601,9 @@ get_contents_stdio (const gchar *display_filename,
 
   fclose (f);
 
+  if (total_allocated == 0)
+    str = g_new (gchar, 1);
+
   str[total_bytes] = '\0';
 
   if (length)
@@ -809,7 +891,6 @@ g_file_get_contents (const gchar *filename,
 
 #endif
 
-
 static gboolean
 rename_file (const char *old_name,
             const char *new_name,
@@ -821,7 +902,7 @@ rename_file (const char *old_name,
       int save_errno = errno;
       gchar *display_old_name = g_filename_display_name (old_name);
       gchar *display_new_name = g_filename_display_name (new_name);
-      
+
       g_set_error (err,
                   G_FILE_ERROR,
                   g_file_error_from_errno (save_errno),
@@ -841,7 +922,7 @@ rename_file (const char *old_name,
 
 static gchar *
 write_to_temp_file (const gchar *contents,
-                   gsize length,
+                   gssize length,
                    const gchar *template,
                    GError **err)
 {
@@ -854,15 +935,15 @@ write_to_temp_file (const gchar *contents,
 
   retval = NULL;
   
-  tmp_name = g_strdup_printf (".%s.XXXXXX", template);
+  tmp_name = g_strdup_printf ("%s.XXXXXX", template);
 
   errno = 0;
-  fd = g_mkstemp (tmp_name);
-  save_errno = errno;
+  fd = create_temp_file (tmp_name, 0666);
   display_name = g_filename_display_name (tmp_name);
       
   if (fd == -1)
     {
+      save_errno = errno;
       g_set_error (err,
                   G_FILE_ERROR,
                   g_file_error_from_errno (save_errno),
@@ -876,12 +957,13 @@ write_to_temp_file (const gchar *contents,
   file = fdopen (fd, "wb");
   if (!file)
     {
+      save_errno = errno;
       g_set_error (err,
                   G_FILE_ERROR,
-                  g_file_error_from_errno (errno),
+                  g_file_error_from_errno (save_errno),
                   _("Failed to open file '%s' for writing: fdopen() failed: %s"),
                   display_name,
-                  g_strerror (errno));
+                  g_strerror (save_errno));
 
       close (fd);
       g_unlink (tmp_name);
@@ -896,15 +978,17 @@ write_to_temp_file (const gchar *contents,
       errno = 0;
 
       n_written = fwrite (contents, 1, length, file);
-      
+
       if (n_written < length)
        {
+         save_errno = errno;
+      
          g_set_error (err,
                       G_FILE_ERROR,
-                      g_file_error_from_errno (errno),
+                      g_file_error_from_errno (save_errno),
                       _("Failed to write file '%s': fwrite() failed: %s"),
                       display_name,
-                      g_strerror (errno));
+                      g_strerror (save_errno));
 
          fclose (file);
          g_unlink (tmp_name);
@@ -915,21 +999,23 @@ write_to_temp_file (const gchar *contents,
    
   errno = 0;
   if (fclose (file) == EOF)
-    {
+    { 
+      save_errno = 0;
+      
       g_set_error (err,
                   G_FILE_ERROR,
-                  g_file_error_from_errno (errno),
+                  g_file_error_from_errno (save_errno),
                   _("Failed to close file '%s': fclose() failed: %s"),
                   display_name, 
-                  g_strerror (errno));
+                  g_strerror (save_errno));
 
       g_unlink (tmp_name);
       
       goto out;
     }
-  
-  retval = g_strdup (tmp_name);
 
+  retval = g_strdup (tmp_name);
+  
  out:
   g_free (tmp_name);
   g_free (display_name);
@@ -938,7 +1024,7 @@ write_to_temp_file (const gchar *contents,
 }
 
 /**
- * g_file_replace:
+ * g_file_set_contents:
  * @filename: name of a file to write @contents to, in the GLib file name
  *   encoding
  * @contents: string to write to the file
@@ -954,7 +1040,8 @@ write_to_temp_file (const gchar *contents,
  * <listitem>
  *    On Unix, if @filename already exists hard links to @filename will break.
  *    Also since the file is recreated, existing permissions, access control
- *    lists, metadata etc. may be lost.
+ *    lists, metadata etc. may be lost. If @filename is a symbolic link,
+ *    the link itself will be replaced, not the linked file.
  * </listitem>
  * <listitem>
  *   On Windows renaming a file will not remove an existing file with the
@@ -977,10 +1064,10 @@ write_to_temp_file (const gchar *contents,
  * Since: 2.8
  **/
 gboolean
-g_file_replace (const gchar *filename,
-               const gchar *contents,
-               gssize       length,
-               GError     **error)
+g_file_set_contents (const gchar *filename,
+                    const gchar *contents,
+                    gssize          length,
+                    GError        **error)
 {
   gchar *tmp_filename;
   gboolean retval;
@@ -989,7 +1076,8 @@ g_file_replace (const gchar *filename,
   g_return_val_if_fail (filename != NULL, FALSE);
   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
   g_return_val_if_fail (contents != NULL || length == 0, FALSE);
+  g_return_val_if_fail (length >= -1, FALSE);
+  
   if (length == -1)
     length = strlen (contents);
 
@@ -1030,12 +1118,14 @@ g_file_replace (const gchar *filename,
        {
           gchar *display_filename = g_filename_display_name (filename);
 
+         int save_errno = errno;
+         
          g_set_error (error,
                       G_FILE_ERROR,
-                      g_file_error_from_errno (errno),
+                      g_file_error_from_errno (save_errno),
                       _("Existing file '%s' could not be removed: g_unlink() failed: %s"),
                       display_filename,
-                      g_strerror (errno));
+                      g_strerror (save_errno));
 
          g_free (display_filename);
          g_unlink (tmp_filename);
@@ -1061,35 +1151,13 @@ g_file_replace (const gchar *filename,
 }
 
 /*
- * mkstemp() implementation is from the GNU C library.
+ * create_temp_file based on the mkstemp implementation from the GNU C library.
  * Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc.
  */
-/**
- * g_mkstemp:
- * @tmpl: template filename
- *
- * Opens a temporary file. See the mkstemp() documentation
- * on most UNIX-like systems. This is a portability wrapper, which simply calls 
- * mkstemp() on systems that have it, and implements 
- * it in GLib otherwise.
- *
- * The parameter is a string that should match the rules for
- * mkstemp(), i.e. end in "XXXXXX". The X string will 
- * be modified to form the name of a file that didn't exist.
- * The string should be in the GLib file name encoding. Most importantly, 
- * on Windows it should be in UTF-8.
- *
- * Return value: A file handle (as from open()) to the file
- * opened for reading and writing. The file is opened in binary mode
- * on platforms where there is a difference. The file handle should be
- * closed with close(). In case of errors, -1 is returned.
- */
-gint
-g_mkstemp (gchar *tmpl)
+static gint
+create_temp_file (gchar *tmpl, 
+                 int    permissions)
 {
-#ifdef HAVE_MKSTEMP
-  return mkstemp (tmpl);
-#else
   int len;
   char *XXXXXX;
   int count, fd;
@@ -1132,7 +1200,7 @@ g_mkstemp (gchar *tmpl)
       XXXXXX[5] = letters[v % NLETTERS];
 
       /* tmpl is in UTF-8 on Windows, thus use g_open() */
-      fd = g_open (tmpl, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
+      fd = g_open (tmpl, O_RDWR | O_CREAT | O_EXCL | O_BINARY, permissions);
 
       if (fd >= 0)
        return fd;
@@ -1146,6 +1214,35 @@ g_mkstemp (gchar *tmpl)
   /* We got out of the loop because we ran out of combinations to try.  */
   errno = EEXIST;
   return -1;
+}
+
+/**
+ * g_mkstemp:
+ * @tmpl: template filename
+ *
+ * Opens a temporary file. See the mkstemp() documentation
+ * on most UNIX-like systems. This is a portability wrapper, which simply calls 
+ * mkstemp() on systems that have it, and implements 
+ * it in GLib otherwise.
+ *
+ * The parameter is a string that should match the rules for
+ * mkstemp(), i.e. end in "XXXXXX". The X string will 
+ * be modified to form the name of a file that didn't exist.
+ * The string should be in the GLib file name encoding. Most importantly, 
+ * on Windows it should be in UTF-8.
+ *
+ * Return value: A file handle (as from open()) to the file
+ * opened for reading and writing. The file is opened in binary mode
+ * on platforms where there is a difference. The file handle should be
+ * closed with close(). In case of errors, -1 is returned.
+ */
+gint
+g_mkstemp (gchar *tmpl)
+{
+#ifdef HAVE_MKSTEMP
+  return mkstemp (tmpl);
+#else
+  return create_temp_file (tmpl, 0600);
 #endif
 }
 
@@ -1364,9 +1461,10 @@ g_file_open_tmp (const gchar *tmpl,
 #endif
 
 static gchar *
-g_build_pathv (const gchar *separator,
-              const gchar *first_element,
-              va_list      args)
+g_build_path_va (const gchar  *separator,
+                const gchar  *first_element,
+                va_list      *args,
+                gchar       **str_array)
 {
   GString *result;
   gint separator_len = strlen (separator);
@@ -1375,10 +1473,14 @@ g_build_pathv (const gchar *separator,
   const gchar *single_element = NULL;
   const gchar *next_element;
   const gchar *last_trailing = NULL;
+  gint i = 0;
 
   result = g_string_new (NULL);
 
-  next_element = first_element;
+  if (str_array)
+    next_element = str_array[i++];
+  else
+    next_element = first_element;
 
   while (TRUE)
     {
@@ -1389,7 +1491,10 @@ g_build_pathv (const gchar *separator,
       if (next_element)
        {
          element = next_element;
-         next_element = va_arg (args, gchar *);
+         if (str_array)
+           next_element = str_array[i++];
+         else
+           next_element = va_arg (*args, gchar *);
        }
       else
        break;
@@ -1460,6 +1565,30 @@ g_build_pathv (const gchar *separator,
 }
 
 /**
+ * g_build_pathv:
+ * @separator: a string used to separator the elements of the path.
+ * @args: %NULL-terminated array of strings containing the path elements.
+ * 
+ * Behaves exactly like g_build_path(), but takes the path elements 
+ * as a string array, instead of varargs. This function is mainly
+ * meant for language bindings.
+ *
+ * Return value: a newly-allocated string that must be freed with g_free().
+ *
+ * Since: 2.8
+ */
+gchar *
+g_build_pathv (const gchar  *separator,
+              gchar       **args)
+{
+  if (!args)
+    return NULL;
+
+  return g_build_path_va (separator, NULL, NULL, args);
+}
+
+
+/**
  * g_build_path:
  * @separator: a string used to separator the elements of the path.
  * @first_element: the first element in the path
@@ -1506,54 +1635,22 @@ g_build_path (const gchar *separator,
   g_return_val_if_fail (separator != NULL, NULL);
 
   va_start (args, first_element);
-  str = g_build_pathv (separator, first_element, args);
+  str = g_build_path_va (separator, first_element, &args, NULL);
   va_end (args);
 
   return str;
 }
 
-/**
- * g_build_filename:
- * @first_element: the first element in the path
- * @Varargs: remaining elements in path, terminated by %NULL
- * 
- * Creates a filename from a series of elements using the correct
- * separator for filenames.
- *
- * On Unix, this function behaves identically to <literal>g_build_path
- * (G_DIR_SEPARATOR_S, first_element, ....)</literal>.
- *
- * On Windows, it takes into account that either the backslash
- * (<literal>\</literal> or slash (<literal>/</literal>) can be used
- * as separator in filenames, but otherwise behaves as on Unix. When
- * file pathname separators need to be inserted, the one that last
- * previously occurred in the parameters (reading from left to right)
- * is used.
- *
- * No attempt is made to force the resulting filename to be an absolute
- * path. If the first element is a relative path, the result will
- * be a relative path. 
- * 
- * Return value: a newly-allocated string that must be freed with g_free().
- **/
-gchar *
-g_build_filename (const gchar *first_element, 
-                 ...)
-{
-#ifndef G_OS_WIN32
-  gchar *str;
-  va_list args;
-
-  va_start (args, first_element);
-  str = g_build_pathv (G_DIR_SEPARATOR_S, first_element, args);
-  va_end (args);
+#ifdef G_OS_WIN32
 
-  return str;
-#else
-  /* Code copied from g_build_pathv(), and modifed to use two
+static gchar *
+g_build_pathname_va (const gchar  *first_element,
+                    va_list      *args,
+                    gchar       **str_array)
+{
+  /* Code copied from g_build_pathv(), and modified to use two
    * alternative single-character separators.
    */
-  va_list args;
   GString *result;
   gboolean is_first = TRUE;
   gboolean have_leading = FALSE;
@@ -1561,13 +1658,15 @@ g_build_filename (const gchar *first_element,
   const gchar *next_element;
   const gchar *last_trailing = NULL;
   gchar current_separator = '\\';
-
-  va_start (args, first_element);
+  gint i = 0;
 
   result = g_string_new (NULL);
 
-  next_element = first_element;
-
+  if (str_array)
+    next_element = str_array[i++];
+  else
+    next_element = first_element;
+  
   while (TRUE)
     {
       const gchar *element;
@@ -1577,7 +1676,10 @@ g_build_filename (const gchar *first_element,
       if (next_element)
        {
          element = next_element;
-         next_element = va_arg (args, gchar *);
+         if (str_array)
+           next_element = str_array[i++];
+         else
+           next_element = va_arg (*args, gchar *);
        }
       else
        break;
@@ -1639,8 +1741,6 @@ g_build_filename (const gchar *first_element,
       is_first = FALSE;
     }
 
-  va_end (args);
-
   if (single_element)
     {
       g_string_free (result, TRUE);
@@ -1653,7 +1753,76 @@ g_build_filename (const gchar *first_element,
   
       return g_string_free (result, FALSE);
     }
+}
+
+#endif
+
+/**
+ * g_build_filenamev:
+ * @args: %NULL-terminated array of strings containing the path elements.
+ * 
+ * Behaves exactly like g_build_filename(), but takes the path elements 
+ * as a string array, instead of varargs. This function is mainly
+ * meant for language bindings.
+ *
+ * Return value: a newly-allocated string that must be freed with g_free().
+ * 
+ * Since: 2.8
+ */
+gchar *
+g_build_filenamev (gchar **args)
+{
+  gchar *str;
+
+#ifndef G_OS_WIN32
+  str = g_build_path_va (G_DIR_SEPARATOR_S, NULL, NULL, args);
+#else
+  str = g_build_pathname_va (NULL, NULL, args);
+#endif
+
+  return str;
+}
+
+/**
+ * g_build_filename:
+ * @first_element: the first element in the path
+ * @Varargs: remaining elements in path, terminated by %NULL
+ * 
+ * Creates a filename from a series of elements using the correct
+ * separator for filenames.
+ *
+ * On Unix, this function behaves identically to <literal>g_build_path
+ * (G_DIR_SEPARATOR_S, first_element, ....)</literal>.
+ *
+ * On Windows, it takes into account that either the backslash
+ * (<literal>\</literal> or slash (<literal>/</literal>) can be used
+ * as separator in filenames, but otherwise behaves as on Unix. When
+ * file pathname separators need to be inserted, the one that last
+ * previously occurred in the parameters (reading from left to right)
+ * is used.
+ *
+ * No attempt is made to force the resulting filename to be an absolute
+ * path. If the first element is a relative path, the result will
+ * be a relative path. 
+ * 
+ * Return value: a newly-allocated string that must be freed with g_free().
+ **/
+gchar *
+g_build_filename (const gchar *first_element, 
+                 ...)
+{
+  gchar *str;
+  va_list args;
+
+  va_start (args, first_element);
+#ifndef G_OS_WIN32
+  str = g_build_path_va (G_DIR_SEPARATOR_S, first_element, &args, NULL);
+#else
+  str = g_build_pathname_va (first_element, &args, NULL);
 #endif
+  va_end (args);
+
+  return str;
 }
 
 /**
@@ -1719,3 +1888,6 @@ g_file_read_link (const gchar *filename,
   return NULL;
 #endif
 }
+
+#define __G_FILEUTILS_C__
+#include "galiasdef.c"