unicode: Move gscripttable.h generation into main script
[platform/upstream/glib.git] / glib / gfileutils.c
index 5e7e286..c80c1ef 100644 (file)
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with GLib; see the file COPYING.LIB.  If not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- *   Boston, MA 02111-1307, USA.
+ * see <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
 #include "glibconfig.h"
 
 #include <sys/stat.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
@@ -35,6 +31,9 @@
 #include <fcntl.h>
 #include <stdlib.h>
 
+#ifdef G_OS_UNIX
+#include <unistd.h>
+#endif
 #ifdef G_OS_WIN32
 #include <windows.h>
 #include <io.h>
@@ -73,8 +72,8 @@
  *
  * The pathname argument should be in the GLib file name encoding.
  * On POSIX this is the actual on-disk encoding which might correspond
- * to the locale settings of the process (or the
- * <envar>G_FILENAME_ENCODING</envar> environment variable), or not.
+ * to the locale settings of the process (or the `G_FILENAME_ENCODING`
+ * environment variable), or not.
  *
  * On Windows the GLib file name encoding is UTF-8. Note that the
  * Microsoft C library does not use UTF-8, but has separate APIs for
  *     not correctly attached to the computer.
  * @G_FILE_ERROR_NODEV: The underlying file system of the specified file
  *     does not support memory mapping.
- * G_FILE_ERROR_ROFS: The directory containing the new link can't be
+ * @G_FILE_ERROR_ROFS: The directory containing the new link can't be
  *     modified because it's on a read-only file system.
  * @G_FILE_ERROR_TXTBSY: Text file busy.
  * @G_FILE_ERROR_FAULT: You passed in a pointer to bad memory.
  *     library function.
  * @G_FILE_ERROR_PIPE: Broken pipe; there is no process reading from the
  *     other end of a pipe. Every library function that returns this
- *     error code also generates a `SIGPIPE' signal; this signal
+ *     error code also generates a 'SIGPIPE' signal; this signal
  *     terminates the program if not handled or blocked. Thus, your
  *     program will never actually see this code unless it has handled
- *     or blocked `SIGPIPE'.
+ *     or blocked 'SIGPIPE'.
  * @G_FILE_ERROR_AGAIN: Resource temporarily unavailable; the call might
  *     work if you try again later.
  * @G_FILE_ERROR_INTR: Interrupted function call; an asynchronous signal
@@ -271,11 +270,11 @@ g_mkdir_with_parents (const gchar *pathname,
  * @test: bitfield of #GFileTest flags
  * 
  * Returns %TRUE if any of the tests in the bitfield @test are
- * %TRUE. For example, <literal>(G_FILE_TEST_EXISTS | 
- * G_FILE_TEST_IS_DIR)</literal> will return %TRUE if the file exists; 
- * the check whether it's a directory doesn't matter since the existence 
- * test is %TRUE. With the current set of available tests, there's no point
- * passing in more than one test at a time.
+ * %TRUE. For example, `(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)`
+ * will return %TRUE if the file exists; the check whether it's a
+ * directory doesn't matter since the existence test is %TRUE. With
+ * the current set of available tests, there's no point passing in
+ * more than one test at a time.
  * 
  * Apart from %G_FILE_TEST_IS_SYMLINK all tests follow symbolic links,
  * so for a symbolic link to a regular file g_file_test() will return
@@ -290,12 +289,12 @@ g_mkdir_with_parents (const gchar *pathname,
  * For example, you might think you could use %G_FILE_TEST_IS_SYMLINK
  * to know whether it is safe to write to a file without being
  * tricked into writing into a different location. It doesn't work!
- * |[
- * /&ast; DON'T DO THIS &ast;/
+ * |[<!-- language="C" -->
+ *  // DON'T DO THIS
  *  if (!g_file_test (filename, G_FILE_TEST_IS_SYMLINK)) 
  *    {
  *      fd = g_open (filename, O_WRONLY);
- *      /&ast; write to fd &ast;/
+ *      // write to fd
  *    }
  * ]|
  *
@@ -310,9 +309,9 @@ g_mkdir_with_parents (const gchar *pathname,
  * %G_FILE_TEST_IS_SYMLINK will always return %FALSE. Testing for
  * %G_FILE_TEST_IS_EXECUTABLE will just check that the file exists and
  * its name indicates that it is executable, checking for well-known
- * extensions and those listed in the %PATHEXT environment variable.
+ * extensions and those listed in the `PATHEXT` environment variable.
  *
- * Return value: whether a test was %TRUE
+ * Returns: whether a test was %TRUE
  **/
 gboolean
 g_file_test (const gchar *filename,
@@ -459,26 +458,22 @@ g_file_test (const gchar *filename,
 #endif
 }
 
-GQuark
-g_file_error_quark (void)
-{
-  return g_quark_from_static_string ("g-file-error-quark");
-}
+G_DEFINE_QUARK (g-file-error-quark, g_file_error)
 
 /**
  * g_file_error_from_errno:
  * @err_no: an "errno" value
  * 
- * Gets a #GFileError constant based on the passed-in @errno.
- * For example, if you pass in %EEXIST this function returns
- * #G_FILE_ERROR_EXIST. Unlike @errno values, you can portably
+ * Gets a #GFileError constant based on the passed-in @err_no.
+ * For example, if you pass in `EEXIST` this function returns
+ * #G_FILE_ERROR_EXIST. Unlike `errno` values, you can portably
  * assume that all #GFileError values will exist.
  *
  * Normally a #GFileError value goes into a #GError returned
  * from a function that manipulates files. So you would use
  * g_file_error_from_errno() when constructing a #GError.
  * 
- * Return value: #GFileError corresponding to the given @errno
+ * Returns: #GFileError corresponding to the given @errno
  **/
 GFileError
 g_file_error_from_errno (gint err_no)
@@ -488,166 +483,184 @@ g_file_error_from_errno (gint err_no)
 #ifdef EEXIST
     case EEXIST:
       return G_FILE_ERROR_EXIST;
-      break;
 #endif
 
 #ifdef EISDIR
     case EISDIR:
       return G_FILE_ERROR_ISDIR;
-      break;
 #endif
 
 #ifdef EACCES
     case EACCES:
       return G_FILE_ERROR_ACCES;
-      break;
 #endif
 
 #ifdef ENAMETOOLONG
     case ENAMETOOLONG:
       return G_FILE_ERROR_NAMETOOLONG;
-      break;
 #endif
 
 #ifdef ENOENT
     case ENOENT:
       return G_FILE_ERROR_NOENT;
-      break;
 #endif
 
 #ifdef ENOTDIR
     case ENOTDIR:
       return G_FILE_ERROR_NOTDIR;
-      break;
 #endif
 
 #ifdef ENXIO
     case ENXIO:
       return G_FILE_ERROR_NXIO;
-      break;
 #endif
 
 #ifdef ENODEV
     case ENODEV:
       return G_FILE_ERROR_NODEV;
-      break;
 #endif
 
 #ifdef EROFS
     case EROFS:
       return G_FILE_ERROR_ROFS;
-      break;
 #endif
 
 #ifdef ETXTBSY
     case ETXTBSY:
       return G_FILE_ERROR_TXTBSY;
-      break;
 #endif
 
 #ifdef EFAULT
     case EFAULT:
       return G_FILE_ERROR_FAULT;
-      break;
 #endif
 
 #ifdef ELOOP
     case ELOOP:
       return G_FILE_ERROR_LOOP;
-      break;
 #endif
 
 #ifdef ENOSPC
     case ENOSPC:
       return G_FILE_ERROR_NOSPC;
-      break;
 #endif
 
 #ifdef ENOMEM
     case ENOMEM:
       return G_FILE_ERROR_NOMEM;
-      break;
 #endif
 
 #ifdef EMFILE
     case EMFILE:
       return G_FILE_ERROR_MFILE;
-      break;
 #endif
 
 #ifdef ENFILE
     case ENFILE:
       return G_FILE_ERROR_NFILE;
-      break;
 #endif
 
 #ifdef EBADF
     case EBADF:
       return G_FILE_ERROR_BADF;
-      break;
 #endif
 
 #ifdef EINVAL
     case EINVAL:
       return G_FILE_ERROR_INVAL;
-      break;
 #endif
 
 #ifdef EPIPE
     case EPIPE:
       return G_FILE_ERROR_PIPE;
-      break;
 #endif
 
 #ifdef EAGAIN
     case EAGAIN:
       return G_FILE_ERROR_AGAIN;
-      break;
 #endif
 
 #ifdef EINTR
     case EINTR:
       return G_FILE_ERROR_INTR;
-      break;
 #endif
 
 #ifdef EIO
     case EIO:
       return G_FILE_ERROR_IO;
-      break;
 #endif
 
 #ifdef EPERM
     case EPERM:
       return G_FILE_ERROR_PERM;
-      break;
 #endif
 
 #ifdef ENOSYS
     case ENOSYS:
       return G_FILE_ERROR_NOSYS;
-      break;
 #endif
 
     default:
       return G_FILE_ERROR_FAILED;
-      break;
     }
 }
 
+static char *
+format_error_message (const gchar  *filename,
+                      const gchar  *format_string) G_GNUC_FORMAT(2);
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+
+static char *
+format_error_message (const gchar  *filename,
+                      const gchar  *format_string)
+{
+  gint saved_errno = errno;
+  gchar *display_name;
+  gchar *msg;
+
+  display_name = g_filename_display_name (filename);
+  msg = g_strdup_printf (format_string, display_name, g_strerror (saved_errno));
+  g_free (display_name);
+
+  return msg;
+}
+
+#pragma GCC diagnostic pop
+
+/* format string must have two '%s':
+ *
+ *   - the place for the filename
+ *   - the place for the strerror
+ */
+static void
+set_file_error (GError      **error,
+                const gchar  *filename,
+                const gchar  *format_string)
+{
+  int saved_errno = errno;
+  char *msg = format_error_message (filename, format_string);
+
+  g_set_error_literal (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno),
+                       msg);
+  g_free (msg);
+}
+
 static gboolean
-get_contents_stdio (const gchar  *display_filename,
+get_contents_stdio (const gchar  *filename,
                     FILE         *f,
                     gchar       **contents,
                     gsize        *length,
                     GError      **error)
 {
   gchar buf[4096];
-  gsize bytes;
+  gsize bytes;  /* always <= sizeof(buf) */
   gchar *str = NULL;
   gsize total_bytes = 0;
   gsize total_allocated = 0;
   gchar *tmp;
+  gchar *display_filename;
 
   g_assert (f != NULL);
 
@@ -658,23 +671,35 @@ get_contents_stdio (const gchar  *display_filename,
       bytes = fread (buf, 1, sizeof (buf), f);
       save_errno = errno;
 
-      while ((total_bytes + bytes + 1) > total_allocated)
+      if (total_bytes > G_MAXSIZE - bytes)
+          goto file_too_large;
+
+      /* Possibility of overflow eliminated above. */
+      while (total_bytes + bytes >= total_allocated)
         {
           if (str)
-            total_allocated *= 2;
+            {
+              if (total_allocated > G_MAXSIZE / 2)
+                  goto file_too_large;
+              total_allocated *= 2;
+            }
           else
-            total_allocated = MIN (bytes + 1, sizeof (buf));
+            {
+              total_allocated = MIN (bytes + 1, sizeof (buf));
+            }
 
           tmp = g_try_realloc (str, total_allocated);
 
           if (tmp == NULL)
             {
+              display_filename = g_filename_display_name (filename);
               g_set_error (error,
                            G_FILE_ERROR,
                            G_FILE_ERROR_NOMEM,
-                           _("Could not allocate %lu bytes to read file \"%s\""),
+                           g_dngettext (GETTEXT_PACKAGE, "Could not allocate %lu byte to read file \"%s\"", "Could not allocate %lu bytes to read file \"%s\"", (gulong)total_allocated),
                            (gulong) total_allocated,
                           display_filename);
+              g_free (display_filename);
 
               goto error;
             }
@@ -684,29 +709,21 @@ get_contents_stdio (const gchar  *display_filename,
 
       if (ferror (f))
         {
+          display_filename = g_filename_display_name (filename);
           g_set_error (error,
                        G_FILE_ERROR,
                        g_file_error_from_errno (save_errno),
                        _("Error reading file '%s': %s"),
                        display_filename,
                       g_strerror (save_errno));
+          g_free (display_filename);
 
           goto error;
         }
 
+      g_assert (str != NULL);
       memcpy (str + total_bytes, buf, bytes);
 
-      if (total_bytes + bytes < total_bytes) 
-        {
-          g_set_error (error,
-                       G_FILE_ERROR,
-                       G_FILE_ERROR_FAILED,
-                       _("File \"%s\" is too large"),
-                       display_filename);
-
-          goto error;
-        }
-
       total_bytes += bytes;
     }
 
@@ -727,6 +744,15 @@ get_contents_stdio (const gchar  *display_filename,
 
   return TRUE;
 
+ file_too_large:
+  display_filename = g_filename_display_name (filename);
+  g_set_error (error,
+               G_FILE_ERROR,
+               G_FILE_ERROR_FAILED,
+               _("File \"%s\" is too large"),
+               display_filename);
+  g_free (display_filename);
+
  error:
 
   g_free (str);
@@ -738,7 +764,7 @@ get_contents_stdio (const gchar  *display_filename,
 #ifndef G_OS_WIN32
 
 static gboolean
-get_contents_regfile (const gchar  *display_filename,
+get_contents_regfile (const gchar  *filename,
                       struct stat  *stat_buf,
                       gint          fd,
                       gchar       **contents,
@@ -749,6 +775,7 @@ get_contents_regfile (const gchar  *display_filename,
   gsize bytes_read;
   gsize size;
   gsize alloc_size;
+  gchar *display_filename;
   
   size = stat_buf->st_size;
 
@@ -757,13 +784,14 @@ get_contents_regfile (const gchar  *display_filename,
 
   if (buf == NULL)
     {
+      display_filename = g_filename_display_name (filename);
       g_set_error (error,
                    G_FILE_ERROR,
                    G_FILE_ERROR_NOMEM,
-                   _("Could not allocate %lu bytes to read file \"%s\""),
+                           g_dngettext (GETTEXT_PACKAGE, "Could not allocate %lu byte to read file \"%s\"", "Could not allocate %lu bytes to read file \"%s\"", (gulong)alloc_size),
                    (gulong) alloc_size, 
                   display_filename);
-
+      g_free (display_filename);
       goto error;
     }
   
@@ -781,13 +809,14 @@ get_contents_regfile (const gchar  *display_filename,
              int save_errno = errno;
 
               g_free (buf);
+              display_filename = g_filename_display_name (filename);
               g_set_error (error,
                            G_FILE_ERROR,
                            g_file_error_from_errno (save_errno),
                            _("Failed to read from file '%s': %s"),
                            display_filename, 
                           g_strerror (save_errno));
-
+              g_free (display_filename);
              goto error;
             }
         }
@@ -823,22 +852,15 @@ get_contents_posix (const gchar  *filename,
 {
   struct stat stat_buf;
   gint fd;
-  gchar *display_filename = g_filename_display_name (filename);
 
   /* O_BINARY useful on Cygwin */
   fd = open (filename, O_RDONLY|O_BINARY);
 
   if (fd < 0)
     {
-      int save_errno = errno;
-
-      g_set_error (error,
-                   G_FILE_ERROR,
-                   g_file_error_from_errno (save_errno),
-                   _("Failed to open file '%s': %s"),
-                   display_filename, 
-                  g_strerror (save_errno));
-      g_free (display_filename);
+      set_file_error (error,
+                      filename,
+                      _("Failed to open file '%s': %s"));
 
       return FALSE;
     }
@@ -846,29 +868,22 @@ get_contents_posix (const gchar  *filename,
   /* I don't think this will ever fail, aside from ENOMEM, but. */
   if (fstat (fd, &stat_buf) < 0)
     {
-      int save_errno = errno;
-
+      set_file_error (error,
+                      filename,
+                      _("Failed to get attributes of file '%s': fstat() failed: %s"));
       close (fd);
-      g_set_error (error,
-                   G_FILE_ERROR,
-                   g_file_error_from_errno (save_errno),
-                   _("Failed to get attributes of file '%s': fstat() failed: %s"),
-                   display_filename, 
-                  g_strerror (save_errno));
-      g_free (display_filename);
 
       return FALSE;
     }
 
   if (stat_buf.st_size > 0 && S_ISREG (stat_buf.st_mode))
     {
-      gboolean retval = get_contents_regfile (display_filename,
+      gboolean retval = get_contents_regfile (filename,
                                              &stat_buf,
                                              fd,
                                              contents,
                                              length,
                                              error);
-      g_free (display_filename);
 
       return retval;
     }
@@ -881,21 +896,14 @@ get_contents_posix (const gchar  *filename,
       
       if (f == NULL)
         {
-         int save_errno = errno;
-
-          g_set_error (error,
-                       G_FILE_ERROR,
-                       g_file_error_from_errno (save_errno),
-                       _("Failed to open file '%s': fdopen() failed: %s"),
-                       display_filename, 
-                      g_strerror (save_errno));
-          g_free (display_filename);
+          set_file_error (error,
+                          filename,
+                          _("Failed to open file '%s': fdopen() failed: %s"));
 
           return FALSE;
         }
   
-      retval = get_contents_stdio (display_filename, f, contents, length, error);
-      g_free (display_filename);
+      retval = get_contents_stdio (filename, f, contents, length, error);
 
       return retval;
     }
@@ -911,27 +919,19 @@ get_contents_win32 (const gchar  *filename,
 {
   FILE *f;
   gboolean retval;
-  gchar *display_filename = g_filename_display_name (filename);
-  int save_errno;
   
   f = g_fopen (filename, "rb");
-  save_errno = errno;
 
   if (f == NULL)
     {
-      g_set_error (error,
-                   G_FILE_ERROR,
-                   g_file_error_from_errno (save_errno),
-                   _("Failed to open file '%s': %s"),
-                   display_filename,
-                  g_strerror (save_errno));
-      g_free (display_filename);
+      set_file_error (error,
+                      filename,
+                      _("Failed to open file '%s': %s"));
 
       return FALSE;
     }
   
-  retval = get_contents_stdio (display_filename, f, contents, length, error);
-  g_free (display_filename);
+  retval = get_contents_stdio (filename, f, contents, length, error);
 
   return retval;
 }
@@ -957,7 +957,7 @@ get_contents_win32 (const gchar  *filename,
  * codes are those in the #GFileError enumeration. In the error case,
  * @contents is set to %NULL and @length is set to zero.
  *
- * Return value: %TRUE on success, %FALSE if an error occurred
+ * Returns: %TRUE on success, %FALSE if an error occurred
  **/
 gboolean
 g_file_get_contents (const gchar  *filename,
@@ -1015,93 +1015,53 @@ write_to_temp_file (const gchar  *contents,
                    GError      **err)
 {
   gchar *tmp_name;
-  gchar *display_name;
   gchar *retval;
-  FILE *file;
   gint fd;
-  int save_errno;
 
   retval = NULL;
-  
+
   tmp_name = g_strdup_printf ("%s.XXXXXX", dest_file);
 
   errno = 0;
   fd = g_mkstemp_full (tmp_name, O_RDWR | O_BINARY, 0666);
-  save_errno = errno;
 
-  display_name = g_filename_display_name (tmp_name);
-      
   if (fd == -1)
     {
-      g_set_error (err,
-                  G_FILE_ERROR,
-                  g_file_error_from_errno (save_errno),
-                  _("Failed to create file '%s': %s"),
-                  display_name, g_strerror (save_errno));
-      
+      set_file_error (err, tmp_name, _("Failed to create file '%s': %s"));
       goto out;
     }
 
-  errno = 0;
-  file = fdopen (fd, "wb");
-  if (!file)
+#ifdef HAVE_FALLOCATE
+  if (length > 0)
     {
-      save_errno = errno;
-      g_set_error (err,
-                  G_FILE_ERROR,
-                  g_file_error_from_errno (save_errno),
-                  _("Failed to open file '%s' for writing: fdopen() failed: %s"),
-                  display_name,
-                  g_strerror (save_errno));
-
-      close (fd);
-      g_unlink (tmp_name);
-      
-      goto out;
+      /* We do this on a 'best effort' basis... It may not be supported
+       * on the underlying filesystem.
+       */
+      (void) fallocate (fd, 0, 0, length);
     }
-
-  if (length > 0)
+#endif
+  while (length > 0)
     {
-      gsize n_written;
-      
-      errno = 0;
+      gssize s;
 
-      n_written = fwrite (contents, 1, length, file);
+      s = write (fd, contents, length);
 
-      if (n_written < length)
-       {
-         save_errno = errno;
-      
-         g_set_error (err,
-                      G_FILE_ERROR,
-                      g_file_error_from_errno (save_errno),
-                      _("Failed to write file '%s': fwrite() failed: %s"),
-                      display_name,
-                      g_strerror (save_errno));
+      if (s < 0)
+        {
+          if (errno == EINTR)
+            continue;
 
-         fclose (file);
-         g_unlink (tmp_name);
-         
-         goto out;
-       }
-    }
+          set_file_error (err, tmp_name, _("Failed to write file '%s': write() failed: %s"));
+          close (fd);
+          g_unlink (tmp_name);
 
-  errno = 0;
-  if (fflush (file) != 0)
-    { 
-      save_errno = errno;
-      
-      g_set_error (err,
-                  G_FILE_ERROR,
-                  g_file_error_from_errno (save_errno),
-                  _("Failed to write file '%s': fflush() failed: %s"),
-                  display_name, 
-                  g_strerror (save_errno));
+          goto out;
+        }
 
-      fclose (file);
-      g_unlink (tmp_name);
-      
-      goto out;
+      g_assert (s <= length);
+
+      contents += s;
+      length -= s;
     }
 
 #ifdef BTRFS_SUPER_MAGIC
@@ -1117,7 +1077,7 @@ write_to_temp_file (const gchar  *contents,
       goto no_fsync;
   }
 #endif
-  
+
 #ifdef HAVE_FSYNC
   {
     struct stat statbuf;
@@ -1129,23 +1089,13 @@ write_to_temp_file (const gchar  *contents,
      * the new and the old file on some filesystems. (I.E. those that don't
      * guarantee the data is written to the disk before the metadata.)
      */
-    if (g_lstat (dest_file, &statbuf) == 0 &&
-       statbuf.st_size > 0 &&
-       fsync (fileno (file)) != 0)
+    if (g_lstat (dest_file, &statbuf) == 0 && statbuf.st_size > 0 && fsync (fd) != 0)
       {
-       save_errno = errno;
-
-       g_set_error (err,
-                    G_FILE_ERROR,
-                    g_file_error_from_errno (save_errno),
-                    _("Failed to write file '%s': fsync() failed: %s"),
-                    display_name,
-                    g_strerror (save_errno));
-
-        fclose (file);
-       g_unlink (tmp_name);
+        set_file_error (err, tmp_name, _("Failed to write file '%s': fsync() failed: %s"));
+        close (fd);
+        g_unlink (tmp_name);
 
-       goto out;
+        goto out;
       }
   }
 #endif
@@ -1153,31 +1103,20 @@ write_to_temp_file (const gchar  *contents,
 #ifdef BTRFS_SUPER_MAGIC
  no_fsync:
 #endif
-  
-  errno = 0;
-  if (fclose (file) == EOF)
-    { 
-      save_errno = errno;
-      
-      g_set_error (err,
-                  G_FILE_ERROR,
-                  g_file_error_from_errno (save_errno),
-                  _("Failed to close file '%s': fclose() failed: %s"),
-                  display_name, 
-                  g_strerror (save_errno));
 
-      fclose (file);
+  errno = 0;
+  if (!g_close (fd, err))
+    {
       g_unlink (tmp_name);
-      
+
       goto out;
     }
 
   retval = g_strdup (tmp_name);
-  
+
  out:
   g_free (tmp_name);
-  g_free (display_name);
-  
+
   return retval;
 }
 
@@ -1194,24 +1133,19 @@ write_to_temp_file (const gchar  *contents,
  *
  * This write is atomic in the sense that it is first written to a temporary
  * file which is then renamed to the final name. Notes:
- * <itemizedlist>
- * <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. 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
+ *
+ * - 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. If @filename is a symbolic link,
+ *   the link itself will be replaced, not the linked file.
+ *
+ * - On Windows renaming a file will not remove an existing file with the
  *   new name, so on Windows there is a race condition between the existing
  *   file being removed and the temporary file being renamed.
- * </listitem>
- * <listitem>
- *   On Windows there is no way to remove a file that is open to some
+ *
+ * - On Windows there is no way to remove a file that is open to some
  *   process, or mapped into memory. Thus, this function will fail if
  *   @filename already exists and is open.
- * </listitem>
- * </itemizedlist>
  *
  * If the call was successful, it returns %TRUE. If the call was not successful,
  * it returns %FALSE and sets @error. The error domain is #G_FILE_ERROR.
@@ -1220,10 +1154,10 @@ write_to_temp_file (const gchar  *contents,
  * Note that the name for the temporary file is constructed by appending up
  * to 7 characters to @filename.
  *
- * Return value: %TRUE on success, %FALSE if an error occurred
+ * Returns: %TRUE on success, %FALSE if an error occurred
  *
  * Since: 2.8
- **/
+ */
 gboolean
 g_file_set_contents (const gchar  *filename,
                     const gchar  *contents,
@@ -1277,18 +1211,9 @@ g_file_set_contents (const gchar  *filename,
       
       if (g_unlink (filename) == -1)
        {
-          gchar *display_filename = g_filename_display_name (filename);
-
-         int save_errno = errno;
-         
-         g_set_error (error,
-                      G_FILE_ERROR,
-                      g_file_error_from_errno (save_errno),
-                      _("Existing file '%s' could not be removed: g_unlink() failed: %s"),
-                      display_filename,
-                      g_strerror (save_errno));
-
-         g_free (display_filename);
+          set_file_error (error,
+                          filename,
+                         _("Existing file '%s' could not be removed: g_unlink() failed: %s"));
          g_unlink (tmp_filename);
          retval = FALSE;
          goto out;
@@ -1315,7 +1240,7 @@ g_file_set_contents (const gchar  *filename,
  * get_tmp_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.
  */
-typedef gint (*GTmpFileCallback) (gchar *, gint, gint);
+typedef gint (*GTmpFileCallback) (const gchar *, gint, gint);
 
 static gint
 get_tmp_file (gchar            *tmpl,
@@ -1380,13 +1305,27 @@ get_tmp_file (gchar            *tmpl,
   return -1;
 }
 
-gint
-wrap_mkdir (gchar *tmpl,
-            int    flags G_GNUC_UNUSED,
-            int    mode)
+/* Some GTmpFileCallback implementations.
+ *
+ * Note: we cannot use open() or g_open() directly because even though
+ * they appear compatible, they may be vararg functions and calling
+ * varargs functions through a non-varargs type is undefined.
+ */
+static gint
+wrap_g_mkdir (const gchar *filename,
+              int          flags G_GNUC_UNUSED,
+              int          mode)
 {
   /* tmpl is in UTF-8 on Windows, thus use g_mkdir() */
-  return g_mkdir (tmpl, mode);
+  return g_mkdir (filename, mode);
+}
+
+static gint
+wrap_g_open (const gchar *filename,
+                int          flags,
+                int          mode)
+{
+  return g_open (filename, flags, mode);
 }
 
 /**
@@ -1406,17 +1345,17 @@ wrap_mkdir (gchar *tmpl,
  * in the GLib file name encoding. Most importantly, on Windows it
  * should be in UTF-8.
  *
- * Return value: A pointer to @tmpl, which has been modified
+ * Returns: A pointer to @tmpl, which has been modified
  *     to hold the directory name. In case of errors, %NULL is
  *     returned, and %errno will be set.
  *
- * Since: 2.26
+ * Since: 2.30
  */
 gchar *
 g_mkdtemp_full (gchar *tmpl,
                 gint   mode)
 {
-  if (get_tmp_file (tmpl, wrap_mkdir, 0, mode) == -1)
+  if (get_tmp_file (tmpl, wrap_g_mkdir, 0, mode) == -1)
     return NULL;
   else
     return tmpl;
@@ -1438,11 +1377,11 @@ g_mkdtemp_full (gchar *tmpl,
  * The string should be in the GLib file name encoding. Most importantly,
  * on Windows it should be in UTF-8.
  *
- * Return value: A pointer to @tmpl, which has been modified
+ * Returns: A pointer to @tmpl, which has been modified
  *     to hold the directory name.  In case of errors, %NULL is
  *     returned and %errno will be set.
  *
- * Since: 2.26
+ * Since: 2.30
  */
 gchar *
 g_mkdtemp (gchar *tmpl)
@@ -1469,7 +1408,7 @@ g_mkdtemp (gchar *tmpl)
  * 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
+ * Returns: A file handle (as from open()) to the file
  *     opened for reading and writing. The file handle should be
  *     closed with close(). In case of errors, -1 is returned
  *     and %errno will be set.
@@ -1482,7 +1421,7 @@ g_mkstemp_full (gchar *tmpl,
                 gint   mode)
 {
   /* tmpl is in UTF-8 on Windows, thus use g_open() */
-  return get_tmp_file (tmpl, (GTmpFileCallback) g_open,
+  return get_tmp_file (tmpl, wrap_g_open,
                        flags | O_CREAT | O_EXCL, mode);
 }
 
@@ -1501,7 +1440,7 @@ g_mkstemp_full (gchar *tmpl,
  * 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
+ * Returns: 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
@@ -1575,15 +1514,9 @@ g_get_tmp_name (const gchar      *tmpl,
   retval = get_tmp_file (fulltemplate, f, flags, mode);
   if (retval == -1)
     {
-      int save_errno = errno;
-      gchar *display_fulltemplate = g_filename_display_name (fulltemplate);
-
-      g_set_error (error,
-                   G_FILE_ERROR,
-                   g_file_error_from_errno (save_errno),
-                   _("Failed to create file '%s': %s"),
-                   display_fulltemplate, g_strerror (save_errno));
-      g_free (display_fulltemplate);
+      set_file_error (error,
+                      fulltemplate,
+                      _("Failed to create file '%s': %s"));
       g_free (fulltemplate);
       return -1;
     }
@@ -1618,7 +1551,7 @@ g_get_tmp_name (const gchar      *tmpl,
  * when not needed any longer. The returned name is in the GLib file
  * name encoding.
  *
- * Return value: A file handle (as from open()) to the file opened for
+ * Returns: 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 and @error will be set.
@@ -1632,7 +1565,7 @@ g_file_open_tmp (const gchar  *tmpl,
   gint result;
 
   result = g_get_tmp_name (tmpl, &fulltemplate,
-                           (GTmpFileCallback) g_open,
+                           wrap_g_open,
                            O_CREAT | O_EXCL | O_RDWR | O_BINARY,
                            0600,
                            error);
@@ -1665,7 +1598,7 @@ g_file_open_tmp (const gchar  *tmpl,
  * Note that in contrast to g_mkdtemp() (and mkdtemp()) @tmpl is not
  * modified, and might thus be a read-only literal string.
  *
- * Return value: (type filename): The actual name used. This string
+ * Returns: (type filename): The actual name used. This string
  *     should be freed with g_free() when not needed any longer and is
  *     is in the GLib file name encoding. In case of errors, %NULL is
  *     returned and @error will be set.
@@ -1678,7 +1611,7 @@ g_dir_make_tmp (const gchar  *tmpl,
 {
   gchar *fulltemplate;
 
-  if (g_get_tmp_name (tmpl, &fulltemplate, wrap_mkdir, 0, 0700, error) == -1)
+  if (g_get_tmp_name (tmpl, &fulltemplate, wrap_g_mkdir, 0, 0700, error) == -1)
     return NULL;
   else
     return fulltemplate;
@@ -1796,7 +1729,7 @@ g_build_path_va (const gchar  *separator,
  * 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().
+ * Returns: a newly-allocated string that must be freed with g_free().
  *
  * Since: 2.8
  */
@@ -1833,8 +1766,7 @@ g_build_pathv (const gchar  *separator,
  * the same as the number of trailing copies of the separator on
  * the last non-empty element. (Determination of the number of
  * trailing copies is done without stripping leading copies, so
- * if the separator is <literal>ABA</literal>, <literal>ABABA</literal>
- * has 1 trailing copy.)
+ * if the separator is `ABA`, then `ABABA` has 1 trailing copy.)
  *
  * However, if there is only a single non-empty element, and there
  * are no characters in that element not part of the leading or
@@ -1845,7 +1777,7 @@ g_build_pathv (const gchar  *separator,
  * copies of the separator, elements consisting only of copies
  * of the separator are ignored.
  * 
- * Return value: a newly-allocated string that must be freed with g_free().
+ * Returns: a newly-allocated string that must be freed with g_free().
  **/
 gchar *
 g_build_path (const gchar *separator,
@@ -1988,7 +1920,7 @@ g_build_pathname_va (const gchar  *first_element,
  * 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().
+ * Returns: a newly-allocated string that must be freed with g_free().
  * 
  * Since: 2.8
  */
@@ -2014,21 +1946,20 @@ g_build_filenamev (gchar **args)
  * 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 Unix, this function behaves identically to `g_build_path
+ * (G_DIR_SEPARATOR_S, first_element, ....)`.
  *
  * 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.
+ * (`\` or slash (`/`) 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().
+ * Returns: a newly-allocated string that must be freed with g_free().
  **/
 gchar *
 g_build_filename (const gchar *first_element, 
@@ -2048,328 +1979,505 @@ g_build_filename (const gchar *first_element,
   return str;
 }
 
-#define KILOBYTE_FACTOR (G_GOFFSET_CONSTANT (1000))
-#define MEGABYTE_FACTOR (KILOBYTE_FACTOR * KILOBYTE_FACTOR)
-#define GIGABYTE_FACTOR (MEGABYTE_FACTOR * KILOBYTE_FACTOR)
-#define TERABYTE_FACTOR (GIGABYTE_FACTOR * KILOBYTE_FACTOR)
-#define PETABYTE_FACTOR (TERABYTE_FACTOR * KILOBYTE_FACTOR)
-#define EXABYTE_FACTOR  (PETABYTE_FACTOR * KILOBYTE_FACTOR)
-
-#define KIBIBYTE_FACTOR (G_GOFFSET_CONSTANT (1024))
-#define MEBIBYTE_FACTOR (KIBIBYTE_FACTOR * KIBIBYTE_FACTOR)
-#define GIBIBYTE_FACTOR (MEBIBYTE_FACTOR * KIBIBYTE_FACTOR)
-#define TEBIBYTE_FACTOR (GIBIBYTE_FACTOR * KIBIBYTE_FACTOR)
-#define PEBIBYTE_FACTOR (TEBIBYTE_FACTOR * KIBIBYTE_FACTOR)
-#define EXBIBYTE_FACTOR (PEBIBYTE_FACTOR * KIBIBYTE_FACTOR)
-
 /**
- * g_format_size:
- * @size: a size in bytes
- *
- * Formats a size (for example the size of a file) into a human readable
- * string.  Sizes are rounded to the nearest size prefix (kB, MB, GB)
- * and are displayed rounded to the nearest tenth. E.g. the file size
- * 3292528 bytes will be converted into the string "3.2 MB".
- *
- * The prefix units base is 1000 (i.e. 1 kB is 1000 bytes).
- *
- * This string should be freed with g_free() when not needed any longer.
+ * g_file_read_link:
+ * @filename: the symbolic link
+ * @error: return location for a #GError
  *
- * See g_format_size_full() for more options about how the size might be
- * formatted.
+ * Reads the contents of the symbolic link @filename like the POSIX
+ * readlink() function.  The returned string is in the encoding used
+ * for filenames. Use g_filename_to_utf8() to convert it to UTF-8.
  *
- * Returns: a newly-allocated formatted string containing a human readable
- *          file size.
+ * Returns: A newly-allocated string with the contents of the symbolic link, 
+ *          or %NULL if an error occurred.
  *
- * Since: 2.30
- **/
+ * Since: 2.4
+ */
 gchar *
-g_format_size (guint64 size)
+g_file_read_link (const gchar  *filename,
+                 GError      **error)
+{
+#ifdef HAVE_READLINK
+  gchar *buffer;
+  guint size;
+  gint read_size;    
+  
+  size = 256; 
+  buffer = g_malloc (size);
+  
+  while (TRUE) 
+    {
+      read_size = readlink (filename, buffer, size);
+      if (read_size < 0)
+        {
+          set_file_error (error,
+                          filename,
+                          _("Failed to read the symbolic link '%s': %s"));
+       
+          return NULL;
+        }
+    
+      if (read_size < size) 
+        {
+          buffer[read_size] = 0;
+          return buffer;
+        }
+      
+      size *= 2;
+      buffer = g_realloc (buffer, size);
+    }
+#else
+  g_set_error_literal (error,
+                       G_FILE_ERROR,
+                       G_FILE_ERROR_INVAL,
+                       _("Symbolic links not supported"));
+       
+  return NULL;
+#endif
+}
+
+/**
+ * g_path_is_absolute:
+ * @file_name: a file name
+ *
+ * Returns %TRUE if the given @file_name is an absolute file name.
+ * Note that this is a somewhat vague concept on Windows.
+ *
+ * On POSIX systems, an absolute file name is well-defined. It always
+ * starts from the single root directory. For example "/usr/local".
+ *
+ * On Windows, the concepts of current drive and drive-specific
+ * current directory introduce vagueness. This function interprets as
+ * an absolute file name one that either begins with a directory
+ * separator such as "\Users\tml" or begins with the root on a drive,
+ * for example "C:\Windows". The first case also includes UNC paths
+ * such as "\\myserver\docs\foo". In all cases, either slashes or
+ * backslashes are accepted.
+ *
+ * Note that a file name relative to the current drive root does not
+ * truly specify a file uniquely over time and across processes, as
+ * the current drive is a per-process value and can be changed.
+ *
+ * File names relative the current directory on some specific drive,
+ * such as "D:foo/bar", are not interpreted as absolute by this
+ * function, but they obviously are not relative to the normal current
+ * directory as returned by getcwd() or g_get_current_dir()
+ * either. Such paths should be avoided, or need to be handled using
+ * Windows-specific code.
+ *
+ * Returns: %TRUE if @file_name is absolute
+ */
+gboolean
+g_path_is_absolute (const gchar *file_name)
 {
-  return g_format_size_full (size, G_FORMAT_SIZE_DEFAULT);
+  g_return_val_if_fail (file_name != NULL, FALSE);
+
+  if (G_IS_DIR_SEPARATOR (file_name[0]))
+    return TRUE;
+
+#ifdef G_OS_WIN32
+  /* Recognize drive letter on native Windows */
+  if (g_ascii_isalpha (file_name[0]) &&
+      file_name[1] == ':' && G_IS_DIR_SEPARATOR (file_name[2]))
+    return TRUE;
+#endif
+
+  return FALSE;
 }
 
 /**
- * g_format_size_full:
- * @size: a size in bytes
- * @flags: #GFormatSizeFlags to modify the output
- *
- * Formats a size.
+ * g_path_skip_root:
+ * @file_name: a file name
  *
- * This function is similar to g_format_size() but allows for flags that
- * modify the output.  See #GFormatSizeFlags.
+ * Returns a pointer into @file_name after the root component,
+ * i.e. after the "/" in UNIX or "C:\" under Windows. If @file_name
+ * is not an absolute path it returns %NULL.
  *
- * Returns: a newly-allocated formatted string containing a human
- *          readable file size.
- *
- * Since: 2.30
- **/
-/**
- * GFormatSizeFlags:
- * @G_FORMAT_SIZE_DEFAULT: behave the same as g_format_size()
- * @G_FORMAT_SIZE_LONG_FORMAT: include the exact number of bytes as part
- *                             of the returned string.  For example,
- *                             "45.6 kB (45,612 bytes)".
- * @G_FORMAT_SIZE_IEC_UNITS: use IEC (base 1024) units with "KiB"-style
- *                           suffixes.  IEC units should only be used
- *                           for reporting things with a strong "power
- *                           of 2" basis, like RAM sizes or RAID stripe
- *                           sizes.  Network and storage sizes should
- *                           be reported in the normal SI units.
- *
- * Flags to modify the format of the string returned by
- * g_format_size_full().
- **/
-gchar *
-g_format_size_full (guint64          size,
-                    GFormatSizeFlags flags)
+ * Returns: a pointer into @file_name after the root component
+ */
+const gchar *
+g_path_skip_root (const gchar *file_name)
 {
-  GString *string;
+  g_return_val_if_fail (file_name != NULL, NULL);
+
+#ifdef G_PLATFORM_WIN32
+  /* Skip \\server\share or //server/share */
+  if (G_IS_DIR_SEPARATOR (file_name[0]) &&
+      G_IS_DIR_SEPARATOR (file_name[1]) &&
+      file_name[2] &&
+      !G_IS_DIR_SEPARATOR (file_name[2]))
+    {
+      gchar *p;
+      p = strchr (file_name + 2, G_DIR_SEPARATOR);
 
-  string = g_string_new (NULL);
+#ifdef G_OS_WIN32
+      {
+        gchar *q;
 
-  if (flags & G_FORMAT_SIZE_IEC_UNITS)
-    {
-      if (size < KIBIBYTE_FACTOR)
+        q = strchr (file_name + 2, '/');
+        if (p == NULL || (q != NULL && q < p))
+        p = q;
+      }
+#endif
+
+      if (p && p > file_name + 2 && p[1])
         {
-          g_string_printf (string,
-                           g_dngettext(GETTEXT_PACKAGE, "%u byte", "%u bytes", (guint) size),
-                           (guint) size);
-          flags &= ~G_FORMAT_SIZE_LONG_FORMAT;
+          file_name = p + 1;
+
+          while (file_name[0] && !G_IS_DIR_SEPARATOR (file_name[0]))
+            file_name++;
+
+          /* Possibly skip a backslash after the share name */
+          if (G_IS_DIR_SEPARATOR (file_name[0]))
+            file_name++;
+
+          return (gchar *)file_name;
         }
+    }
+#endif
 
-      else if (size < MEBIBYTE_FACTOR)
-        g_string_printf (string, _("%.1f KiB"), (gdouble) size / (gdouble) KIBIBYTE_FACTOR);
+  /* Skip initial slashes */
+  if (G_IS_DIR_SEPARATOR (file_name[0]))
+    {
+      while (G_IS_DIR_SEPARATOR (file_name[0]))
+        file_name++;
+      return (gchar *)file_name;
+    }
+
+#ifdef G_OS_WIN32
+  /* Skip X:\ */
+  if (g_ascii_isalpha (file_name[0]) &&
+      file_name[1] == ':' &&
+      G_IS_DIR_SEPARATOR (file_name[2]))
+    return (gchar *)file_name + 3;
+#endif
+
+  return NULL;
+}
 
-      else if (size < GIBIBYTE_FACTOR)
-        g_string_printf (string, _("%.1f MiB"), (gdouble) size / (gdouble) MEBIBYTE_FACTOR);
+/**
+ * g_basename:
+ * @file_name: the name of the file
+ *
+ * Gets the name of the file without any leading directory
+ * components. It returns a pointer into the given file name
+ * string.
+ *
+ * Returns: the name of the file without any leading
+ *     directory components
+ *
+ * Deprecated:2.2: Use g_path_get_basename() instead, but notice
+ *     that g_path_get_basename() allocates new memory for the
+ *     returned string, unlike this function which returns a pointer
+ *     into the argument.
+ */
+const gchar *
+g_basename (const gchar *file_name)
+{
+  gchar *base;
 
-      else if (size < TEBIBYTE_FACTOR)
-        g_string_printf (string, _("%.1f GiB"), (gdouble) size / (gdouble) GIBIBYTE_FACTOR);
+  g_return_val_if_fail (file_name != NULL, NULL);
 
-      else if (size < PEBIBYTE_FACTOR)
-        g_string_printf (string, _("%.1f TiB"), (gdouble) size / (gdouble) TEBIBYTE_FACTOR);
+  base = strrchr (file_name, G_DIR_SEPARATOR);
 
-      else if (size < EXBIBYTE_FACTOR)
-        g_string_printf (string, _("%.1f PiB"), (gdouble) size / (gdouble) PEBIBYTE_FACTOR);
+#ifdef G_OS_WIN32
+  {
+    gchar *q;
+    q = strrchr (file_name, '/');
+    if (base == NULL || (q != NULL && q > base))
+      base = q;
+  }
+#endif
 
-      else
-        g_string_printf (string, _("%.1f EiB"), (gdouble) size / (gdouble) EXBIBYTE_FACTOR);
-    }
-  else
-    {
-      if (size < KILOBYTE_FACTOR)
-        {
-          g_string_printf (string,
-                           g_dngettext(GETTEXT_PACKAGE, "%u byte", "%u bytes", (guint) size),
-                           (guint) size);
-          flags &= ~G_FORMAT_SIZE_LONG_FORMAT;
-        }
+  if (base)
+    return base + 1;
 
-      else if (size < MEGABYTE_FACTOR)
-        g_string_printf (string, _("%.1f kB"), (gdouble) size / (gdouble) KILOBYTE_FACTOR);
+#ifdef G_OS_WIN32
+  if (g_ascii_isalpha (file_name[0]) && file_name[1] == ':')
+    return (gchar*) file_name + 2;
+#endif
 
-      else if (size < GIGABYTE_FACTOR)
-        g_string_printf (string, _("%.1f MB"), (gdouble) size / (gdouble) MEGABYTE_FACTOR);
+  return (gchar*) file_name;
+}
 
-      else if (size < TERABYTE_FACTOR)
-        g_string_printf (string, _("%.1f GB"), (gdouble) size / (gdouble) GIGABYTE_FACTOR);
+/**
+ * g_path_get_basename:
+ * @file_name: the name of the file
+ *
+ * Gets the last component of the filename.
+ *
+ * If @file_name ends with a directory separator it gets the component
+ * before the last slash. If @file_name consists only of directory
+ * separators (and on Windows, possibly a drive letter), a single
+ * separator is returned. If @file_name is empty, it gets ".".
+ *
+ * Returns: a newly allocated string containing the last
+ *    component of the filename
+ */
+gchar *
+g_path_get_basename (const gchar *file_name)
+{
+  gssize base;
+  gssize last_nonslash;
+  gsize len;
+  gchar *retval;
 
-      else if (size < PETABYTE_FACTOR)
-        g_string_printf (string, _("%.1f TB"), (gdouble) size / (gdouble) TERABYTE_FACTOR);
+  g_return_val_if_fail (file_name != NULL, NULL);
 
-      else if (size < EXABYTE_FACTOR)
-        g_string_printf (string, _("%.1f PB"), (gdouble) size / (gdouble) PETABYTE_FACTOR);
+  if (file_name[0] == '\0')
+    return g_strdup (".");
 
-      else
-        g_string_printf (string, _("%.1f EB"), (gdouble) size / (gdouble) EXABYTE_FACTOR);
-    }
+  last_nonslash = strlen (file_name) - 1;
 
-  if (flags & G_FORMAT_SIZE_LONG_FORMAT)
-    {
-      /* First problem: we need to use the number of bytes to decide on
-       * the plural form that is used for display, but the number of
-       * bytes potentially exceeds the size of a guint (which is what
-       * ngettext() takes).
-       *
-       * From a pragmatic standpoint, it seems that all known languages
-       * base plural forms on one or both of the following:
-       *
-       *   - the lowest digits of the number
-       *
-       *   - if the number if greater than some small value
-       *
-       * Here's how we fake it:  Draw an arbitrary line at one thousand.
-       * If the number is below that, then fine.  If it is above it,
-       * then we take the modulus of the number by one thousand (in
-       * order to keep the lowest digits) and add one thousand to that
-       * (in order to ensure that 1001 is not treated the same as 1).
-       */
-      guint plural_form = size < 1000 ? size : size % 1000 + 1000;
-
-      /* Second problem: we need to translate the string "%u byte" and
-       * "%u bytes" for pluralisation, but the correct number format to
-       * use for a gsize is different depending on which architecture
-       * we're on.
-       *
-       * Solution: format the number separately and use "%s bytes" on
-       * all platforms.
-       */
-      const gchar *translated_format;
-      gchar *formatted_number;
+  while (last_nonslash >= 0 && G_IS_DIR_SEPARATOR (file_name [last_nonslash]))
+    last_nonslash--;
 
-      /* Translators: the %s in "%s bytes" will always be replaced by a number. */
-      translated_format = g_dngettext(GETTEXT_PACKAGE, "%s byte", "%s bytes", plural_form);
+  if (last_nonslash == -1)
+    /* string only containing slashes */
+    return g_strdup (G_DIR_SEPARATOR_S);
 
-      /* XXX: Windows doesn't support the "'" format modifier, so we
-       * must not use it there.  Instead, just display the number
-       * without separation.  Bug #655336 is open until a solution is
-       * found.
-       */
-#ifndef G_OS_WIN32
-      formatted_number = g_strdup_printf ("%'"G_GUINT64_FORMAT, size);
-#else
-      formatted_number = g_strdup_printf ("%"G_GUINT64_FORMAT, size);
+#ifdef G_OS_WIN32
+  if (last_nonslash == 1 &&
+      g_ascii_isalpha (file_name[0]) &&
+      file_name[1] == ':')
+    /* string only containing slashes and a drive */
+    return g_strdup (G_DIR_SEPARATOR_S);
 #endif
+  base = last_nonslash;
 
-      g_string_append (string, " (");
-      g_string_append_printf (string, translated_format, formatted_number);
-      g_free (formatted_number);
-      g_string_append (string, ")");
-    }
+  while (base >=0 && !G_IS_DIR_SEPARATOR (file_name [base]))
+    base--;
+
+#ifdef G_OS_WIN32
+  if (base == -1 &&
+      g_ascii_isalpha (file_name[0]) &&
+      file_name[1] == ':')
+    base = 1;
+#endif /* G_OS_WIN32 */
+
+  len = last_nonslash - base;
+  retval = g_malloc (len + 1);
+  memcpy (retval, file_name + base + 1, len);
+  retval [len] = '\0';
 
-  return g_string_free (string, FALSE);
+  return retval;
 }
 
 /**
- * g_format_size_for_display:
- * @size: a size in bytes.
- * 
- * Formats a size (for example the size of a file) into a human readable string.
- * Sizes are rounded to the nearest size prefix (KB, MB, GB) and are displayed 
- * rounded to the nearest  tenth. E.g. the file size 3292528 bytes will be
- * converted into the string "3.1 MB".
+ * g_dirname:
+ * @file_name: the name of the file
  *
- * The prefix units base is 1024 (i.e. 1 KB is 1024 bytes).
+ * Gets the directory components of a file name.
  *
- * This string should be freed with g_free() when not needed any longer.
+ * If the file name has no directory components "." is returned.
+ * The returned string should be freed when no longer needed.
  *
- * Returns: a newly-allocated formatted string containing a human readable
- *          file size.
+ * Returns: the directory components of the file
  *
- * Deprecated:2.30: This function is broken due to its use of SI
- *                  suffixes to denote IEC units.  Use g_format_size()
- *                  instead.
- * Since: 2.16
- **/
-char *
-g_format_size_for_display (goffset size)
+ * Deprecated: use g_path_get_dirname() instead
+ */
+
+/**
+ * g_path_get_dirname:
+ * @file_name: the name of the file
+ *
+ * Gets the directory components of a file name.
+ *
+ * If the file name has no directory components "." is returned.
+ * The returned string should be freed when no longer needed.
+ *
+ * Returns: the directory components of the file
+ */
+gchar *
+g_path_get_dirname (const gchar *file_name)
 {
-  if (size < (goffset) KIBIBYTE_FACTOR)
-    return g_strdup_printf (g_dngettext(GETTEXT_PACKAGE, "%u byte", "%u bytes",(guint) size), (guint) size);
-  else
+  gchar *base;
+  gsize len;
+
+  g_return_val_if_fail (file_name != NULL, NULL);
+
+  base = strrchr (file_name, G_DIR_SEPARATOR);
+
+#ifdef G_OS_WIN32
+  {
+    gchar *q;
+    q = strrchr (file_name, '/');
+    if (base == NULL || (q != NULL && q > base))
+      base = q;
+  }
+#endif
+
+  if (!base)
     {
-      gdouble displayed_size;
-      
-      if (size < (goffset) MEBIBYTE_FACTOR)
-       {
-         displayed_size = (gdouble) size / (gdouble) KIBIBYTE_FACTOR;
-         return g_strdup_printf (_("%.1f KB"), displayed_size);
-       }
-      else if (size < (goffset) GIBIBYTE_FACTOR)
-       {
-         displayed_size = (gdouble) size / (gdouble) MEBIBYTE_FACTOR;
-         return g_strdup_printf (_("%.1f MB"), displayed_size);
-       }
-      else if (size < (goffset) TEBIBYTE_FACTOR)
-       {
-         displayed_size = (gdouble) size / (gdouble) GIBIBYTE_FACTOR;
-         return g_strdup_printf (_("%.1f GB"), displayed_size);
-       }
-      else if (size < (goffset) PEBIBYTE_FACTOR)
-       {
-         displayed_size = (gdouble) size / (gdouble) TEBIBYTE_FACTOR;
-         return g_strdup_printf (_("%.1f TB"), displayed_size);
-       }
-      else if (size < (goffset) EXBIBYTE_FACTOR)
-       {
-         displayed_size = (gdouble) size / (gdouble) PEBIBYTE_FACTOR;
-         return g_strdup_printf (_("%.1f PB"), displayed_size);
-       }
-      else
+#ifdef G_OS_WIN32
+      if (g_ascii_isalpha (file_name[0]) && file_name[1] == ':')
+        {
+          gchar drive_colon_dot[4];
+
+          drive_colon_dot[0] = file_name[0];
+          drive_colon_dot[1] = ':';
+          drive_colon_dot[2] = '.';
+          drive_colon_dot[3] = '\0';
+
+          return g_strdup (drive_colon_dot);
+        }
+#endif
+    return g_strdup (".");
+    }
+
+  while (base > file_name && G_IS_DIR_SEPARATOR (*base))
+    base--;
+
+#ifdef G_OS_WIN32
+  /* base points to the char before the last slash.
+   *
+   * In case file_name is the root of a drive (X:\) or a child of the
+   * root of a drive (X:\foo), include the slash.
+   *
+   * In case file_name is the root share of an UNC path
+   * (\\server\share), add a slash, returning \\server\share\ .
+   *
+   * In case file_name is a direct child of a share in an UNC path
+   * (\\server\share\foo), include the slash after the share name,
+   * returning \\server\share\ .
+   */
+  if (base == file_name + 1 &&
+      g_ascii_isalpha (file_name[0]) &&
+      file_name[1] == ':')
+    base++;
+  else if (G_IS_DIR_SEPARATOR (file_name[0]) &&
+           G_IS_DIR_SEPARATOR (file_name[1]) &&
+           file_name[2] &&
+           !G_IS_DIR_SEPARATOR (file_name[2]) &&
+           base >= file_name + 2)
+    {
+      const gchar *p = file_name + 2;
+      while (*p && !G_IS_DIR_SEPARATOR (*p))
+        p++;
+      if (p == base + 1)
+        {
+          len = (guint) strlen (file_name) + 1;
+          base = g_new (gchar, len + 1);
+          strcpy (base, file_name);
+          base[len-1] = G_DIR_SEPARATOR;
+          base[len] = 0;
+          return base;
+        }
+      if (G_IS_DIR_SEPARATOR (*p))
         {
-         displayed_size = (gdouble) size / (gdouble) EXBIBYTE_FACTOR;
-         return g_strdup_printf (_("%.1f EB"), displayed_size);
+          p++;
+          while (*p && !G_IS_DIR_SEPARATOR (*p))
+            p++;
+          if (p == base + 1)
+            base++;
         }
     }
+#endif
+
+  len = (guint) 1 + base - file_name;
+  base = g_new (gchar, len + 1);
+  memmove (base, file_name, len);
+  base[len] = 0;
+
+  return base;
 }
 
+#if defined(MAXPATHLEN)
+#define G_PATH_LENGTH MAXPATHLEN
+#elif defined(PATH_MAX)
+#define G_PATH_LENGTH PATH_MAX
+#elif defined(_PC_PATH_MAX)
+#define G_PATH_LENGTH sysconf(_PC_PATH_MAX)
+#else
+#define G_PATH_LENGTH 2048
+#endif
 
 /**
- * g_file_read_link:
- * @filename: the symbolic link
- * @error: return location for a #GError
+ * g_get_current_dir:
  *
- * Reads the contents of the symbolic link @filename like the POSIX
- * readlink() function.  The returned string is in the encoding used
- * for filenames. Use g_filename_to_utf8() to convert it to UTF-8.
+ * Gets the current directory.
  *
- * Returns: A newly-allocated string with the contents of the symbolic link, 
- *          or %NULL if an error occurred.
+ * The returned string should be freed when no longer needed.
+ * The encoding of the returned string is system defined.
+ * On Windows, it is always UTF-8.
  *
- * Since: 2.4
+ * Since GLib 2.40, this function will return the value of the "PWD"
+ * environment variable if it is set and it happens to be the same as
+ * the current directory.  This can make a difference in the case that
+ * the current directory is the target of a symbolic link.
+ *
+ * Returns: the current directory
  */
 gchar *
-g_file_read_link (const gchar  *filename,
-                 GError      **error)
+g_get_current_dir (void)
 {
-#ifdef HAVE_READLINK
-  gchar *buffer;
-  guint size;
-  gint read_size;    
-  
-  size = 256; 
-  buffer = g_malloc (size);
-  
-  while (TRUE) 
+#ifdef G_OS_WIN32
+
+  gchar *dir = NULL;
+  wchar_t dummy[2], *wdir;
+  int len;
+
+  len = GetCurrentDirectoryW (2, dummy);
+  wdir = g_new (wchar_t, len);
+
+  if (GetCurrentDirectoryW (len, wdir) == len - 1)
+    dir = g_utf16_to_utf8 (wdir, -1, NULL, NULL, NULL);
+
+  g_free (wdir);
+
+  if (dir == NULL)
+    dir = g_strdup ("\\");
+
+  return dir;
+
+#else
+  const gchar *pwd;
+  gchar *buffer = NULL;
+  gchar *dir = NULL;
+  static gulong max_len = 0;
+  struct stat pwdbuf, dotbuf;
+
+  pwd = g_getenv ("PWD");
+  if (pwd != NULL &&
+      g_stat (".", &dotbuf) == 0 && g_stat (pwd, &pwdbuf) == 0 &&
+      dotbuf.st_dev == pwdbuf.st_dev && dotbuf.st_ino == pwdbuf.st_ino)
+    return g_strdup (pwd);
+
+  if (max_len == 0)
+    max_len = (G_PATH_LENGTH == -1) ? 2048 : G_PATH_LENGTH;
+
+  while (max_len < G_MAXULONG / 2)
     {
-      read_size = readlink (filename, buffer, size);
-      if (read_size < 0) {
-       int save_errno = errno;
-       gchar *display_filename = g_filename_display_name (filename);
-
-       g_free (buffer);
-       g_set_error (error,
-                    G_FILE_ERROR,
-                    g_file_error_from_errno (save_errno),
-                    _("Failed to read the symbolic link '%s': %s"),
-                    display_filename, 
-                    g_strerror (save_errno));
-       g_free (display_filename);
-       
-       return NULL;
-      }
-    
-      if (read_size < size) 
-       {
-         buffer[read_size] = 0;
-         return buffer;
-       }
-      
-      size *= 2;
-      buffer = g_realloc (buffer, size);
+      g_free (buffer);
+      buffer = g_new (gchar, max_len + 1);
+      *buffer = 0;
+      dir = getcwd (buffer, max_len);
+
+      if (dir || errno != ERANGE)
+        break;
+
+      max_len *= 2;
     }
-#else
-  g_set_error_literal (error,
-                       G_FILE_ERROR,
-                       G_FILE_ERROR_INVAL,
-                       _("Symbolic links not supported"));
-       
-  return NULL;
-#endif
+
+  if (!dir || !*buffer)
+    {
+      /* hm, should we g_error() out here?
+       * this can happen if e.g. "./" has mode \0000
+       */
+      buffer[0] = G_DIR_SEPARATOR;
+      buffer[1] = 0;
+    }
+
+  dir = g_strdup (buffer);
+  g_free (buffer);
+
+  return dir;
+
+#endif /* !G_OS_WIN32 */
 }
 
-/* NOTE : Keep this part last to ensure nothing in this file uses the
+
+/* NOTE : Keep this part last to ensure nothing in this file uses thn
  * below binary compatibility versions.
  */
 #if defined (G_OS_WIN32) && !defined (_WIN64)
@@ -2421,13 +2529,21 @@ g_file_get_contents (const gchar  *filename,
 
 #undef g_mkstemp
 
+static gint
+wrap_libc_open (const gchar *filename,
+                int          flags,
+                int          mode)
+{
+  return open (filename, flags, mode);
+}
+
 gint
 g_mkstemp (gchar *tmpl)
 {
   /* This is the backward compatibility system codepage version,
    * thus use normal open().
    */
-  return get_tmp_file (tmpl, (GTmpFileCallback) open,
+  return get_tmp_file (tmpl, wrap_libc_open,
                       O_RDWR | O_CREAT | O_EXCL, 0600);
 }
 
@@ -2458,4 +2574,16 @@ g_file_open_tmp (const gchar  *tmpl,
   return retval;
 }
 
+#undef g_get_current_dir
+
+gchar *
+g_get_current_dir (void)
+{
+  gchar *utf8_dir = g_get_current_dir_utf8 ();
+  gchar *dir = g_locale_from_utf8 (utf8_dir, -1, NULL, NULL, NULL);
+  g_free (utf8_dir);
+  return dir;
+}
+
 #endif
+