X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fgfileutils.c;h=0db07c541e973d470c9ef03f9c1771fe5b2e6aca;hb=1cbdbef77209fe82239bd10f062425491cf256ae;hp=4f5c669bd45a7c453b20ec2abae5b31eea431490;hpb=6cff88ba18b3bc0d118308f109840cb163dcea03;p=platform%2Fupstream%2Fglib.git diff --git a/glib/gfileutils.c b/glib/gfileutils.c index 4f5c669..0db07c5 100644 --- a/glib/gfileutils.c +++ b/glib/gfileutils.c @@ -14,18 +14,13 @@ * * 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 . */ #include "config.h" - -#include "glib.h" +#include "glibconfig.h" #include -#ifdef HAVE_UNISTD_H -#include -#endif #include #include #include @@ -36,6 +31,9 @@ #include #include +#ifdef G_OS_UNIX +#include +#endif #ifdef G_OS_WIN32 #include #include @@ -49,13 +47,150 @@ #define O_BINARY 0 #endif +#include "gfileutils.h" + #include "gstdio.h" #include "glibintl.h" -#include "galias.h" +#ifdef HAVE_LINUX_MAGIC_H /* for btrfs check */ +#include +#include +#endif + + +/** + * SECTION:fileutils + * @title: File Utilities + * @short_description: various file-related functions + * + * There is a group of functions which wrap the common POSIX functions + * dealing with filenames (g_open(), g_rename(), g_mkdir(), g_stat(), + * g_unlink(), g_remove(), g_fopen(), g_freopen()). The point of these + * wrappers is to make it possible to handle file names with any Unicode + * characters in them on Windows without having to use ifdefs and the + * wide character API in the application code. + * + * 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 `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 + * current system code page and wide characters (UTF-16). The GLib + * wrappers call the wide character API if present (on modern Windows + * systems), otherwise convert to/from the system code page. + * + * Another group of functions allows to open and read directories + * in the GLib file name encoding. These are g_dir_open(), + * g_dir_read_name(), g_dir_rewind(), g_dir_close(). + */ + +/** + * GFileError: + * @G_FILE_ERROR_EXIST: Operation not permitted; only the owner of + * the file (or other resource) or processes with special privileges + * can perform the operation. + * @G_FILE_ERROR_ISDIR: File is a directory; you cannot open a directory + * for writing, or create or remove hard links to it. + * @G_FILE_ERROR_ACCES: Permission denied; the file permissions do not + * allow the attempted operation. + * @G_FILE_ERROR_NAMETOOLONG: Filename too long. + * @G_FILE_ERROR_NOENT: No such file or directory. This is a "file + * doesn't exist" error for ordinary files that are referenced in + * contexts where they are expected to already exist. + * @G_FILE_ERROR_NOTDIR: A file that isn't a directory was specified when + * a directory is required. + * @G_FILE_ERROR_NXIO: No such device or address. The system tried to + * use the device represented by a file you specified, and it + * couldn't find the device. This can mean that the device file was + * installed incorrectly, or that the physical device is missing or + * 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 + * 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. + * (GLib won't reliably return this, don't pass in pointers to bad + * memory.) + * @G_FILE_ERROR_LOOP: Too many levels of symbolic links were encountered + * in looking up a file name. This often indicates a cycle of symbolic + * links. + * @G_FILE_ERROR_NOSPC: No space left on device; write operation on a + * file failed because the disk is full. + * @G_FILE_ERROR_NOMEM: No memory available. The system cannot allocate + * more virtual memory because its capacity is full. + * @G_FILE_ERROR_MFILE: The current process has too many files open and + * can't open any more. Duplicate descriptors do count toward this + * limit. + * @G_FILE_ERROR_NFILE: There are too many distinct file openings in the + * entire system. + * @G_FILE_ERROR_BADF: Bad file descriptor; for example, I/O on a + * descriptor that has been closed or reading from a descriptor open + * only for writing (or vice versa). + * @G_FILE_ERROR_INVAL: Invalid argument. This is used to indicate + * various kinds of problems with passing the wrong argument to a + * 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 + * terminates the program if not handled or blocked. Thus, your + * program will never actually see this code unless it has handled + * 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 + * occurred and prevented completion of the call. When this + * happens, you should try the call again. + * @G_FILE_ERROR_IO: Input/output error; usually used for physical read + * or write errors. i.e. the disk or other physical device hardware + * is returning errors. + * @G_FILE_ERROR_PERM: Operation not permitted; only the owner of the + * file (or other resource) or processes with special privileges can + * perform the operation. + * @G_FILE_ERROR_NOSYS: Function not implemented; this indicates that + * the system is missing some functionality. + * @G_FILE_ERROR_FAILED: Does not correspond to a UNIX error code; this + * is the standard "failed for unspecified reason" error code present + * in all #GError error code enumerations. Returned if no specific + * code applies. + * + * Values corresponding to @errno codes returned from file operations + * on UNIX. Unlike @errno codes, GFileError values are available on + * all systems, even Windows. The exact meaning of each code depends + * on what sort of file operation you were performing; the UNIX + * documentation gives more details. The following error code descriptions + * come from the GNU C Library manual, and are under the copyright + * of that manual. + * + * It's not very portable to make detailed assumptions about exactly + * which errors will be returned from a given operation. Some errors + * don't occur on some systems, etc., sometimes there are subtle + * differences in when a system will report a given error, etc. + */ + +/** + * G_FILE_ERROR: + * + * Error domain for file operations. Errors in this domain will + * be from the #GFileError enumeration. See #GError for information + * on error domains. + */ -static gint create_temp_file (gchar *tmpl, - int permissions); +/** + * GFileTest: + * @G_FILE_TEST_IS_REGULAR: %TRUE if the file is a regular file + * (not a directory). Note that this test will also return %TRUE + * if the tested file is a symlink to a regular file. + * @G_FILE_TEST_IS_SYMLINK: %TRUE if the file is a symlink. + * @G_FILE_TEST_IS_DIR: %TRUE if the file is a directory. + * @G_FILE_TEST_IS_EXECUTABLE: %TRUE if the file is executable. + * @G_FILE_TEST_EXISTS: %TRUE if the file exists. It may or may not + * be a regular file. + * + * A test to perform on a file using g_file_test(). + */ /** * g_mkdir_with_parents: @@ -101,7 +236,7 @@ g_mkdir_with_parents (const gchar *pathname, if (!g_file_test (fn, G_FILE_TEST_EXISTS)) { - if (g_mkdir (fn, mode) == -1) + if (g_mkdir (fn, mode) == -1 && errno != EEXIST) { int errno_save = errno; g_free (fn); @@ -135,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, (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. + * %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 @@ -154,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! - * |[ - * /* DON'T DO THIS */ + * |[ + * // DON'T DO THIS * if (!g_file_test (filename, G_FILE_TEST_IS_SYMLINK)) * { * fd = g_open (filename, O_WRONLY); - * /* write to fd */ + * // write to fd * } * ]| * @@ -174,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, @@ -207,19 +342,26 @@ g_file_test (const gchar *filename, return TRUE; if (test & G_FILE_TEST_IS_REGULAR) - return (attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0; + { + if ((attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0) + return TRUE; + } if (test & G_FILE_TEST_IS_DIR) - return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + { + if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) + return TRUE; + } - if (test & G_FILE_TEST_IS_EXECUTABLE) + /* "while" so that we can exit this "loop" with a simple "break" */ + while (test & G_FILE_TEST_IS_EXECUTABLE) { const gchar *lastdot = strrchr (filename, '.'); const gchar *pathext = NULL, *p; int extlen; if (lastdot == NULL) - return FALSE; + break; if (_stricmp (lastdot, ".exe") == 0 || _stricmp (lastdot, ".cmd") == 0 || @@ -231,7 +373,7 @@ g_file_test (const gchar *filename, pathext = g_getenv ("PATHEXT"); if (pathext == NULL) - return FALSE; + break; pathext = g_utf8_casefold (pathext, -1); @@ -259,7 +401,7 @@ g_file_test (const gchar *filename, g_free ((gchar *) pathext); g_free ((gchar *) lastdot); - return FALSE; + break; } return FALSE; @@ -316,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) @@ -345,166 +483,185 @@ 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, + int saved_errno) 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, + int saved_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) +{ + char *msg = format_error_message (filename, format_string, saved_errno); + + 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); @@ -515,23 +672,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; } @@ -541,29 +710,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; } @@ -584,6 +745,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); @@ -595,7 +765,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, @@ -606,6 +776,7 @@ get_contents_regfile (const gchar *display_filename, gsize bytes_read; gsize size; gsize alloc_size; + gchar *display_filename; size = stat_buf->st_size; @@ -614,13 +785,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; } @@ -638,13 +810,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; } } @@ -680,22 +853,17 @@ 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); + int saved_errno = errno; + set_file_error (error, + filename, + _("Failed to open file '%s': %s"), + saved_errno); return FALSE; } @@ -703,29 +871,24 @@ 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; - + int saved_errno = errno; + set_file_error (error, + filename, + _("Failed to get attributes of file '%s': fstat() failed: %s"), + saved_errno); 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; } @@ -738,21 +901,16 @@ 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); + int saved_errno = errno; + set_file_error (error, + filename, + _("Failed to open file '%s': fdopen() failed: %s"), + saved_errno); 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; } @@ -768,27 +926,21 @@ 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); + int saved_errno = errno; + set_file_error (error, + filename, + _("Failed to open file '%s': %s"), + saved_errno); 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; } @@ -797,10 +949,10 @@ get_contents_win32 (const gchar *filename, /** * g_file_get_contents: - * @filename: name of a file to read contents from, in the GLib file name encoding - * @contents: location to store an allocated string, use g_free() to free + * @filename: (type filename): name of a file to read contents from, in the GLib file name encoding + * @contents: (out) (array length=length) (element-type guint8): location to store an allocated string, use g_free() to free * the returned string - * @length: location to store length in bytes of the contents, or %NULL + * @length: (allow-none): location to store length in bytes of the contents, or %NULL * @error: return location for a #GError, or %NULL * * Reads an entire file into allocated memory, with good error @@ -814,7 +966,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, @@ -872,151 +1024,125 @@ 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 = create_temp_file (tmp_name, 0666); - save_errno = errno; + fd = g_mkstemp_full (tmp_name, O_RDWR | O_BINARY, 0666); - 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)); - + int saved_errno = errno; + set_file_error (err, + tmp_name, _("Failed to create file '%s': %s"), + saved_errno); 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) + { + int saved_errno = errno; + if (saved_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"), + saved_errno); + 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; + } - g_unlink (tmp_name); - - goto out; + g_assert (s <= length); + + contents += s; + length -= s; } - + +#ifdef BTRFS_SUPER_MAGIC + { + struct statfs buf; + + /* On Linux, on btrfs, skip the fsync since rename-over-existing is + * guaranteed to be atomic and this is the only case in which we + * would fsync() anyway. + */ + + if (fstatfs (fd, &buf) == 0 && buf.f_type == BTRFS_SUPER_MAGIC) + goto no_fsync; + } +#endif + #ifdef HAVE_FSYNC - errno = 0; - /* If the final destination exists, we want to sync the newly written - * file to ensure the data is on disk when we rename over the destination. - * otherwise if we get a system crash we can lose both 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_file_test (dest_file, G_FILE_TEST_EXISTS) && - fsync (fileno (file)) != 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)); + { + struct stat statbuf; + + errno = 0; + /* If the final destination exists and is > 0 bytes, we want to sync the + * newly written file to ensure the data is on disk when we rename over + * the destination. Otherwise if we get a system crash we can lose both + * 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 (fd) != 0) + { + int saved_errno = errno; + set_file_error (err, + tmp_name, _("Failed to write file '%s': fsync() failed: %s"), + saved_errno); + close (fd); + g_unlink (tmp_name); + + goto out; + } + } +#endif - g_unlink (tmp_name); - - goto out; - } +#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)); + 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; } /** * g_file_set_contents: - * @filename: name of a file to write @contents to, in the GLib file name + * @filename: (type filename): name of a file to write @contents to, in the GLib file name * encoding - * @contents: string to write to the file + * @contents: (array length=length) (element-type guint8): string to write to the file * @length: length of @contents, or -1 if @contents is a nul-terminated string * @error: return location for a #GError, or %NULL * @@ -1025,33 +1151,31 @@ 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: - * - * - * 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 + * + * - 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. - * - * - * 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. - * - * * - * If the call was sucessful, it returns %TRUE. If the call was not successful, + * 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. * Possible error codes are those in the #GFileError enumeration. * - * Return value: %TRUE on success, %FALSE if an error occurred + * Note that the name for the temporary file is constructed by appending up + * to 7 characters to @filename. + * + * Returns: %TRUE on success, %FALSE if an error occurred * * Since: 2.8 - **/ + */ gboolean g_file_set_contents (const gchar *filename, const gchar *contents, @@ -1105,18 +1229,11 @@ 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); + int saved_errno = errno; + set_file_error (error, + filename, + _("Existing file '%s' could not be removed: g_unlink() failed: %s"), + saved_errno); g_unlink (tmp_filename); retval = FALSE; goto out; @@ -1140,12 +1257,16 @@ g_file_set_contents (const gchar *filename, } /* - * create_temp_file based on the mkstemp implementation from the GNU C library. + * 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) (const gchar *, gint, gint); + static gint -create_temp_file (gchar *tmpl, - int permissions) +get_tmp_file (gchar *tmpl, + GTmpFileCallback f, + int flags, + int mode) { char *XXXXXX; int count, fd; @@ -1156,6 +1277,8 @@ create_temp_file (gchar *tmpl, GTimeVal tv; static int counter = 0; + g_return_val_if_fail (tmpl != NULL, -1); + /* find the last occurrence of "XXXXXX" */ XXXXXX = g_strrstr (tmpl, "XXXXXX"); @@ -1186,16 +1309,15 @@ create_temp_file (gchar *tmpl, v /= NLETTERS; 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, permissions); + fd = f (tmpl, flags, mode); if (fd >= 0) - return fd; + return fd; else if (errno != EEXIST) - /* Any other error will apply also to other names we might - * try, and there are 2^32 or so of them, so give up now. - */ - return -1; + /* Any other error will apply also to other names we might + * try, and there are 2^32 or so of them, so give up now. + */ + return -1; } /* We got out of the loop because we ran out of combinations to try. */ @@ -1203,69 +1325,163 @@ create_temp_file (gchar *tmpl, return -1; } +/* 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 (filename, mode); +} + +static gint +wrap_g_open (const gchar *filename, + int flags, + int mode) +{ + return g_open (filename, flags, mode); +} + /** - * g_mkstemp: - * @tmpl: template filename + * g_mkdtemp_full: + * @tmpl: (type filename): template directory name + * @mode: permissions to create the temporary directory with * - * Opens a temporary file. See the mkstemp() documentation - * on most UNIX-like systems. + * Creates a temporary directory. See the mkdtemp() documentation + * on most UNIX-like systems. * * The parameter is a string that should follow the rules for - * mkstemp() templates, i.e. contain the string "XXXXXX". - * g_mkstemp() is slightly more flexible than mkstemp() - * in that the sequence does not have to occur at the very end of the - * template. 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. + * mkdtemp() templates, i.e. contain the string "XXXXXX". + * g_mkdtemp() is slightly more flexible than mkdtemp() in that the + * sequence does not have to occur at the very end of the template + * and you can pass a @mode. The X string will be modified to form + * the name of a directory 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) + * 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.30 + */ +gchar * +g_mkdtemp_full (gchar *tmpl, + gint mode) { - return create_temp_file (tmpl, 0600); + if (get_tmp_file (tmpl, wrap_g_mkdir, 0, mode) == -1) + return NULL; + else + return tmpl; } /** - * g_file_open_tmp: - * @tmpl: Template for file name, as in g_mkstemp(), basename only, - * or %NULL, to a default template - * @name_used: location to store actual name used, or %NULL - * @error: return location for a #GError - * - * Opens a file for writing in the preferred directory for temporary - * files (as returned by g_get_tmp_dir()). + * g_mkdtemp: + * @tmpl: (type filename): template directory name * - * @tmpl should be a string in the GLib file name encoding containing - * a sequence of six 'X' characters, as the parameter to g_mkstemp(). - * However, unlike these functions, the template should only be a - * basename, no directory components are allowed. If template is - * %NULL, a default template is used. + * Creates a temporary directory. See the mkdtemp() documentation + * on most UNIX-like systems. * - * Note that in contrast to g_mkstemp() (and mkstemp()) - * @tmpl is not modified, and might thus be a read-only literal string. + * The parameter is a string that should follow the rules for + * mkdtemp() templates, i.e. contain the string "XXXXXX". + * g_mkdtemp() is slightly more flexible than mkdtemp() in that the + * sequence does not have to occur at the very end of the template + * and you can pass a @mode and additional @flags. The X string will + * be modified to form the name of a directory that didn't exist. + * The string should be in the GLib file name encoding. Most importantly, + * on Windows it should be in UTF-8. * - * The actual name used is returned in @name_used if non-%NULL. This - * string should be freed with g_free() when not needed any longer. - * The returned name is in the GLib file name encoding. + * 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. * - * 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 - * and @error will be set. - **/ -gint -g_file_open_tmp (const gchar *tmpl, - gchar **name_used, - GError **error) + * Since: 2.30 + */ +gchar * +g_mkdtemp (gchar *tmpl) { - int retval; - const char *tmpdir; + return g_mkdtemp_full (tmpl, 0700); +} + +/** + * g_mkstemp_full: + * @tmpl: (type filename): template filename + * @flags: flags to pass to an open() call in addition to O_EXCL + * and O_CREAT, which are passed automatically + * @mode: permissions to create the temporary file with + * + * Opens a temporary file. See the mkstemp() documentation + * on most UNIX-like systems. + * + * The parameter is a string that should follow the rules for + * mkstemp() templates, i.e. contain the string "XXXXXX". + * g_mkstemp_full() is slightly more flexible than mkstemp() + * in that the sequence does not have to occur at the very end of the + * template and you can pass a @mode and additional @flags. 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. + * + * 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. + * + * Since: 2.22 + */ +gint +g_mkstemp_full (gchar *tmpl, + gint flags, + gint mode) +{ + /* tmpl is in UTF-8 on Windows, thus use g_open() */ + return get_tmp_file (tmpl, wrap_g_open, + flags | O_CREAT | O_EXCL, mode); +} + +/** + * g_mkstemp: + * @tmpl: (type filename): template filename + * + * Opens a temporary file. See the mkstemp() documentation + * on most UNIX-like systems. + * + * The parameter is a string that should follow the rules for + * mkstemp() templates, i.e. contain the string "XXXXXX". + * g_mkstemp() is slightly more flexible than mkstemp() in that the + * sequence does not have to occur at the very end of the template. + * 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. + * + * 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 %errno will be set. + */ +gint +g_mkstemp (gchar *tmpl) +{ + return g_mkstemp_full (tmpl, O_RDWR | O_BINARY, 0600); +} + +static gint +g_get_tmp_name (const gchar *tmpl, + gchar **name_used, + GTmpFileCallback f, + gint flags, + gint mode, + GError **error) +{ + int retval; + const char *tmpdir; const char *sep; char *fulltemplate; const char *slash; @@ -1285,23 +1501,23 @@ g_file_open_tmp (const gchar *tmpl, c[1] = '\0'; g_set_error (error, - G_FILE_ERROR, - G_FILE_ERROR_FAILED, - _("Template '%s' invalid, should not contain a '%s'"), - display_tmpl, c); + G_FILE_ERROR, + G_FILE_ERROR_FAILED, + _("Template '%s' invalid, should not contain a '%s'"), + display_tmpl, c); g_free (display_tmpl); return -1; } - + if (strstr (tmpl, "XXXXXX") == NULL) { gchar *display_tmpl = g_filename_display_name (tmpl); g_set_error (error, - G_FILE_ERROR, - G_FILE_ERROR_FAILED, - _("Template '%s' doesn't contain XXXXXX"), - display_tmpl); + G_FILE_ERROR, + G_FILE_ERROR_FAILED, + _("Template '%s' doesn't contain XXXXXX"), + display_tmpl); g_free (display_tmpl); return -1; } @@ -1315,31 +1531,114 @@ g_file_open_tmp (const gchar *tmpl, fulltemplate = g_strconcat (tmpdir, sep, tmpl, NULL); - retval = g_mkstemp (fulltemplate); - + 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); + int saved_errno = errno; + set_file_error (error, + fulltemplate, + _("Failed to create file '%s': %s"), + saved_errno); g_free (fulltemplate); return -1; } - if (name_used) - *name_used = fulltemplate; - else - g_free (fulltemplate); + *name_used = fulltemplate; return retval; } +/** + * g_file_open_tmp: + * @tmpl: (type filename) (allow-none): Template for file name, as in + * g_mkstemp(), basename only, or %NULL for a default template + * @name_used: (out) (type filename): location to store actual name used, + * or %NULL + * @error: return location for a #GError + * + * Opens a file for writing in the preferred directory for temporary + * files (as returned by g_get_tmp_dir()). + * + * @tmpl should be a string in the GLib file name encoding containing + * a sequence of six 'X' characters, as the parameter to g_mkstemp(). + * However, unlike these functions, the template should only be a + * basename, no directory components are allowed. If template is + * %NULL, a default template is used. + * + * Note that in contrast to g_mkstemp() (and mkstemp()) @tmpl is not + * modified, and might thus be a read-only literal string. + * + * Upon success, and if @name_used is non-%NULL, the actual name used + * is returned in @name_used. This string should be freed with g_free() + * when not needed any longer. The returned name is in the GLib file + * name encoding. + * + * 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. + */ +gint +g_file_open_tmp (const gchar *tmpl, + gchar **name_used, + GError **error) +{ + gchar *fulltemplate; + gint result; + + result = g_get_tmp_name (tmpl, &fulltemplate, + wrap_g_open, + O_CREAT | O_EXCL | O_RDWR | O_BINARY, + 0600, + error); + if (result != -1) + { + if (name_used) + *name_used = fulltemplate; + else + g_free (fulltemplate); + } + + return result; +} + +/** + * g_dir_make_tmp: + * @tmpl: (type filename) (allow-none): Template for directory name, + * as in g_mkdtemp(), basename only, or %NULL for a default template + * @error: return location for a #GError + * + * Creates a subdirectory in the preferred directory for temporary + * files (as returned by g_get_tmp_dir()). + * + * @tmpl should be a string in the GLib file name encoding containing + * a sequence of six 'X' characters, as the parameter to g_mkstemp(). + * However, unlike these functions, the template should only be a + * basename, no directory components are allowed. If template is + * %NULL, a default template is used. + * + * Note that in contrast to g_mkdtemp() (and mkdtemp()) @tmpl is not + * modified, and might thus be a read-only literal 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. + * + * Since: 2.30 + */ +gchar * +g_dir_make_tmp (const gchar *tmpl, + GError **error) +{ + gchar *fulltemplate; + + if (g_get_tmp_name (tmpl, &fulltemplate, wrap_g_mkdir, 0, 0700, error) == -1) + return NULL; + else + return fulltemplate; +} + static gchar * g_build_path_va (const gchar *separator, const gchar *first_element, @@ -1387,8 +1686,7 @@ g_build_path_va (const gchar *separator, if (separator_len) { - while (start && - strncmp (start, separator, separator_len) == 0) + while (strncmp (start, separator, separator_len) == 0) start += separator_len; } @@ -1447,13 +1745,13 @@ g_build_path_va (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. + * @args: (array zero-terminated=1): %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(). + * Returns: a newly-allocated string that must be freed with g_free(). * * Since: 2.8 */ @@ -1472,7 +1770,7 @@ g_build_pathv (const gchar *separator, * g_build_path: * @separator: a string used to separator the elements of the path. * @first_element: the first element in the path - * @Varargs: remaining elements in path, terminated by %NULL + * @...: remaining elements in path, terminated by %NULL * * Creates a path from a series of elements using @separator as the * separator between elements. At the boundary between two elements, @@ -1490,8 +1788,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 ABA, ABABA - * 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 @@ -1502,7 +1799,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, @@ -1639,13 +1936,13 @@ g_build_pathname_va (const gchar *first_element, /** * g_build_filenamev: - * @args: %NULL-terminated array of strings containing the path elements. + * @args: (array zero-terminated=1): %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(). + * Returns: a newly-allocated string that must be freed with g_free(). * * Since: 2.8 */ @@ -1666,26 +1963,25 @@ g_build_filenamev (gchar **args) /** * g_build_filename: * @first_element: the first element in the path - * @Varargs: remaining elements in path, terminated by %NULL + * @...: 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 g_build_path - * (G_DIR_SEPARATOR_S, first_element, ....). + * 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 - * (\ 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. + * (`\` 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, @@ -1705,56 +2001,6 @@ g_build_filename (const gchar *first_element, return str; } -#define KILOBYTE_FACTOR 1024.0 -#define MEGABYTE_FACTOR (1024.0 * 1024.0) -#define GIGABYTE_FACTOR (1024.0 * 1024.0 * 1024.0) - -/** - * 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". - * - * The prefix units base is 1024 (i.e. 1 KB is 1024 bytes). - * - * This string should be freed with g_free() when not needed any longer. - * - * Returns: a newly-allocated formatted string containing a human readable - * file size. - * - * Since: 2.16 - **/ -char * -g_format_size_for_display (goffset size) -{ - if (size < (goffset) KILOBYTE_FACTOR) - return g_strdup_printf (g_dngettext(GETTEXT_PACKAGE, "%u byte", "%u bytes",(guint) size), (guint) size); - else - { - gdouble displayed_size; - - if (size < (goffset) MEGABYTE_FACTOR) - { - displayed_size = (gdouble) size / KILOBYTE_FACTOR; - return g_strdup_printf (_("%.1f KB"), displayed_size); - } - else if (size < (goffset) GIGABYTE_FACTOR) - { - displayed_size = (gdouble) size / MEGABYTE_FACTOR; - return g_strdup_printf (_("%.1f MB"), displayed_size); - } - else - { - displayed_size = (gdouble) size / GIGABYTE_FACTOR; - return g_strdup_printf (_("%.1f GB"), displayed_size); - } - } -} - - /** * g_file_read_link: * @filename: the symbolic link @@ -1784,27 +2030,22 @@ g_file_read_link (const gchar *filename, while (TRUE) { 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 < 0) + { + int saved_errno = errno; + set_file_error (error, + filename, + _("Failed to read the symbolic link '%s': %s"), + saved_errno); + g_free (buffer); + return NULL; + } if (read_size < size) - { - buffer[read_size] = 0; - return buffer; - } + { + buffer[read_size] = 0; + return buffer; + } size *= 2; buffer = g_realloc (buffer, size); @@ -1819,7 +2060,448 @@ g_file_read_link (const gchar *filename, #endif } -/* NOTE : Keep this part last to ensure nothing in this file uses the +/** + * 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) +{ + 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_path_skip_root: + * @file_name: a file name + * + * 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 pointer into @file_name after the root component + */ +const gchar * +g_path_skip_root (const gchar *file_name) +{ + 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); + +#ifdef G_OS_WIN32 + { + gchar *q; + + q = strchr (file_name + 2, '/'); + if (p == NULL || (q != NULL && q < p)) + p = q; + } +#endif + + if (p && p > file_name + 2 && p[1]) + { + 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 + + /* 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; +} + +/** + * 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; + + 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) + return base + 1; + +#ifdef G_OS_WIN32 + if (g_ascii_isalpha (file_name[0]) && file_name[1] == ':') + return (gchar*) file_name + 2; +#endif + + return (gchar*) file_name; +} + +/** + * 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; + + g_return_val_if_fail (file_name != NULL, NULL); + + if (file_name[0] == '\0') + return g_strdup ("."); + + last_nonslash = strlen (file_name) - 1; + + while (last_nonslash >= 0 && G_IS_DIR_SEPARATOR (file_name [last_nonslash])) + last_nonslash--; + + if (last_nonslash == -1) + /* string only containing slashes */ + return g_strdup (G_DIR_SEPARATOR_S); + +#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; + + 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 retval; +} + +/** + * g_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 + * + * 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) +{ + 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) + { +#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)) + { + 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_get_current_dir: + * + * Gets the current directory. + * + * 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 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_get_current_dir (void) +{ +#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) + { + 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; + } + + 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 thn * below binary compatibility versions. */ #if defined (G_OS_WIN32) && !defined (_WIN64) @@ -1871,65 +2553,22 @@ 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) { - char *XXXXXX; - int count, fd; - static const char letters[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - static const int NLETTERS = sizeof (letters) - 1; - glong value; - GTimeVal tv; - static int counter = 0; - - /* find the last occurrence of 'XXXXXX' */ - XXXXXX = g_strrstr (tmpl, "XXXXXX"); - - if (!XXXXXX) - { - errno = EINVAL; - return -1; - } - - /* Get some more or less random data. */ - g_get_current_time (&tv); - value = (tv.tv_usec ^ tv.tv_sec) + counter++; - - for (count = 0; count < 100; value += 7777, ++count) - { - glong v = value; - - /* Fill in the random bits. */ - XXXXXX[0] = letters[v % NLETTERS]; - v /= NLETTERS; - XXXXXX[1] = letters[v % NLETTERS]; - v /= NLETTERS; - XXXXXX[2] = letters[v % NLETTERS]; - v /= NLETTERS; - XXXXXX[3] = letters[v % NLETTERS]; - v /= NLETTERS; - XXXXXX[4] = letters[v % NLETTERS]; - v /= NLETTERS; - XXXXXX[5] = letters[v % NLETTERS]; - - /* This is the backward compatibility system codepage version, - * thus use normal open(). - */ - fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600); - - if (fd >= 0) - return fd; - else if (errno != EEXIST) - /* Any other error will apply also to other names we might - * try, and there are 2^32 or so of them, so give up now. - */ - return -1; - } - - /* We got out of the loop because we ran out of combinations to try. */ - errno = EEXIST; - return -1; + /* This is the backward compatibility system codepage version, + * thus use normal open(). + */ + return get_tmp_file (tmpl, wrap_libc_open, + O_RDWR | O_CREAT | O_EXCL, 0600); } #undef g_file_open_tmp @@ -1959,7 +2598,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 -#define __G_FILEUTILS_C__ -#include "galiasdef.c"