X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fgfileutils.c;h=88a647d59b10ec68894fc7c3fef6f922ce6005c9;hb=7f5b2901cf5bea290c11133dad16850176178dad;hp=b3c1565a2fdd964bbdbc04a370f8af9c270b2343;hpb=da536e7e42eadff302b4da013d256bc89e2dfebe;p=platform%2Fupstream%2Fglib.git diff --git a/glib/gfileutils.c b/glib/gfileutils.c index b3c1565..88a647d 100644 --- a/glib/gfileutils.c +++ b/glib/gfileutils.c @@ -19,8 +19,7 @@ */ #include "config.h" - -#include "glib.h" +#include "glibconfig.h" #include #ifdef HAVE_UNISTD_H @@ -33,7 +32,6 @@ #include #include #include -#include #include #include @@ -50,10 +48,222 @@ #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. + */ + +/** + * 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: + * @pathname: a pathname in the GLib file name encoding + * @mode: permissions to use for newly created directories + * + * Create a directory if it doesn't already exist. Create intermediate + * parent directories as needed, too. + * + * Returns: 0 if the directory already exists, or was successfully + * created. Returns -1 if an error occurred, with errno set. + * + * Since: 2.8 + */ +int +g_mkdir_with_parents (const gchar *pathname, + int mode) +{ + gchar *fn, *p; + + if (pathname == NULL || *pathname == '\0') + { + errno = EINVAL; + return -1; + } + + fn = g_strdup (pathname); + + if (g_path_is_absolute (fn)) + p = (gchar *) g_path_skip_root (fn); + else + p = fn; + + do + { + while (*p && !G_IS_DIR_SEPARATOR (*p)) + p++; + + if (!*p) + p = NULL; + else + *p = '\0'; + + if (!g_file_test (fn, G_FILE_TEST_EXISTS)) + { + if (g_mkdir (fn, mode) == -1 && errno != EEXIST) + { + int errno_save = errno; + g_free (fn); + errno = errno_save; + return -1; + } + } + else if (!g_file_test (fn, G_FILE_TEST_IS_DIR)) + { + g_free (fn); + errno = ENOTDIR; + return -1; + } + if (p) + { + *p++ = G_DIR_SEPARATOR; + while (*p && G_IS_DIR_SEPARATOR (*p)) + p++; + } + } + while (p); + + g_free (fn); + + return 0; +} /** * g_file_test: @@ -78,16 +288,16 @@ * to perform an operation, because there is always the possibility * of the condition changing before you actually perform the operation. * For example, you might think you could use %G_FILE_TEST_IS_SYMLINK - * to know whether it is is safe to write to a file without being + * 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 */ - * if (!g_file_test (filename, G_FILE_TEST_IS_SYMLINK)) { - * fd = g_open (filename, O_WRONLY); - * /* write to fd */ - * } - * + * if (!g_file_test (filename, G_FILE_TEST_IS_SYMLINK)) + * { + * fd = g_open (filename, O_WRONLY); + * /* write to fd */ + * } + * ]| * * Another thing to note is that %G_FILE_TEST_EXISTS and * %G_FILE_TEST_IS_EXECUTABLE are implemented using the access() @@ -100,7 +310,7 @@ * %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 **/ @@ -117,29 +327,14 @@ g_file_test (const gchar *filename, # define FILE_ATTRIBUTE_DEVICE 64 # endif int attributes; + wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL); - if (G_WIN32_HAVE_WIDECHAR_API ()) - { - wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL); - - if (wfilename == NULL) - return FALSE; - - attributes = GetFileAttributesW (wfilename); + if (wfilename == NULL) + return FALSE; - g_free (wfilename); - } - else - { - gchar *cpfilename = g_locale_from_utf8 (filename, -1, NULL, NULL, NULL); + attributes = GetFileAttributesW (wfilename); - if (cpfilename == NULL) - return FALSE; - - attributes = GetFileAttributesA (cpfilename); - - g_free (cpfilename); - } + g_free (wfilename); if (attributes == INVALID_FILE_ATTRIBUTES) return FALSE; @@ -148,31 +343,38 @@ 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 || - stricmp (lastdot, ".bat") == 0 || - stricmp (lastdot, ".com") == 0) + if (_stricmp (lastdot, ".exe") == 0 || + _stricmp (lastdot, ".cmd") == 0 || + _stricmp (lastdot, ".bat") == 0 || + _stricmp (lastdot, ".com") == 0) return TRUE; /* Check if it is one of the types listed in %PATHEXT% */ pathext = g_getenv ("PATHEXT"); if (pathext == NULL) - return FALSE; + break; pathext = g_utf8_casefold (pathext, -1); @@ -200,7 +402,7 @@ g_file_test (const gchar *filename, g_free ((gchar *) pathext); g_free ((gchar *) lastdot); - return FALSE; + break; } return FALSE; @@ -257,48 +459,15 @@ g_file_test (const gchar *filename, #endif } -#ifdef G_OS_WIN32 - -#undef g_file_test - -/* Binary compatibility version. Not for newly compiled code. */ - -gboolean -g_file_test (const gchar *filename, - GFileTest test) -{ - gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, NULL); - gboolean retval; - - if (utf8_filename == NULL) - return FALSE; - - retval = g_file_test_utf8 (utf8_filename, test); - - g_free (utf8_filename); - - return retval; -} - -#endif - -GQuark -g_file_error_quark (void) -{ - static GQuark q = 0; - if (q == 0) - q = g_quark_from_static_string ("g-file-error-quark"); - - return q; -} +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 @@ -315,165 +484,140 @@ 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 gboolean -get_contents_stdio (const gchar *display_filename, - FILE *f, - gchar **contents, - gsize *length, - GError **error) +get_contents_stdio (const gchar *display_filename, + FILE *f, + gchar **contents, + gsize *length, + GError **error) { gchar buf[4096]; - size_t bytes; + gsize bytes; gchar *str = NULL; - size_t total_bytes = 0; - size_t total_allocated = 0; + gsize total_bytes = 0; + gsize total_allocated = 0; gchar *tmp; g_assert (f != NULL); @@ -499,7 +643,7 @@ get_contents_stdio (const gchar *display_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); @@ -522,13 +666,28 @@ get_contents_stdio (const gchar *display_filename, } 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; } fclose (f); if (total_allocated == 0) - str = g_new (gchar, 1); + { + str = g_new (gchar, 1); + total_bytes = 0; + } str[total_bytes] = '\0'; @@ -550,17 +709,17 @@ get_contents_stdio (const gchar *display_filename, #ifndef G_OS_WIN32 static gboolean -get_contents_regfile (const gchar *display_filename, - struct stat *stat_buf, - gint fd, - gchar **contents, - gsize *length, - GError **error) +get_contents_regfile (const gchar *display_filename, + struct stat *stat_buf, + gint fd, + gchar **contents, + gsize *length, + GError **error) { gchar *buf; - size_t bytes_read; - size_t size; - size_t alloc_size; + gsize bytes_read; + gsize size; + gsize alloc_size; size = stat_buf->st_size; @@ -572,7 +731,7 @@ get_contents_regfile (const gchar *display_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); @@ -628,10 +787,10 @@ get_contents_regfile (const gchar *display_filename, } static gboolean -get_contents_posix (const gchar *filename, - gchar **contents, - gsize *length, - GError **error) +get_contents_posix (const gchar *filename, + gchar **contents, + gsize *length, + GError **error) { struct stat stat_buf; gint fd; @@ -716,10 +875,10 @@ get_contents_posix (const gchar *filename, #else /* G_OS_WIN32 */ static gboolean -get_contents_win32 (const gchar *filename, - gchar **contents, - gsize *length, - GError **error) +get_contents_win32 (const gchar *filename, + gchar **contents, + gsize *length, + GError **error) { FILE *f; gboolean retval; @@ -752,29 +911,30 @@ 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 - * @length: location to store length in bytes of the contents, or %NULL + * @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: (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 - * checking. - * - * If the call was successful, it returns %TRUE and sets @contents to the file - * contents and @length to the length of the file contents in bytes. The string - * stored in @contents will be nul-terminated, so for text files you can pass - * %NULL for the @length argument. 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. In the error case, + * checking. + * + * If the call was successful, it returns %TRUE and sets @contents to the file + * contents and @length to the length of the file contents in bytes. The string + * stored in @contents will be nul-terminated, so for text files you can pass + * %NULL for the @length argument. 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. 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 **/ gboolean -g_file_get_contents (const gchar *filename, - gchar **contents, - gsize *length, - GError **error) +g_file_get_contents (const gchar *filename, + gchar **contents, + gsize *length, + GError **error) { g_return_val_if_fail (filename != NULL, FALSE); g_return_val_if_fail (contents != NULL, FALSE); @@ -790,37 +950,10 @@ g_file_get_contents (const gchar *filename, #endif } -#ifdef G_OS_WIN32 - -#undef g_file_get_contents - -/* Binary compatibility version. Not for newly compiled code. */ - -gboolean -g_file_get_contents (const gchar *filename, - gchar **contents, - gsize *length, - GError **error) -{ - gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, error); - gboolean retval; - - if (utf8_filename == NULL) - return FALSE; - - retval = g_file_get_contents_utf8 (utf8_filename, contents, length, error); - - g_free (utf8_filename); - - return retval; -} - -#endif - static gboolean -rename_file (const char *old_name, - const char *new_name, - GError **err) +rename_file (const char *old_name, + const char *new_name, + GError **err) { errno = 0; if (g_rename (old_name, new_name) == -1) @@ -846,230 +979,166 @@ rename_file (const char *old_name, return TRUE; } -static gboolean -set_umask_permissions (int fd, - GError **err) -{ -#ifdef G_OS_WIN32 - - return TRUE; - -#else - /* All of this function is just to work around the fact that - * there is no way to get the umask without changing it. - * - * We can't just change-and-reset the umask because that would - * lead to a race condition if another thread tried to change - * the umask in between the getting and the setting of the umask. - * So we have to do the whole thing in a child process. - */ - - int save_errno; - pid_t pid; - - pid = fork (); - - if (pid == -1) - { - save_errno = errno; - g_set_error (err, - G_FILE_ERROR, - g_file_error_from_errno (save_errno), - _("Could not change file mode: fork() failed: %s"), - g_strerror (save_errno)); - - return FALSE; - } - else if (pid == 0) - { - /* child */ - mode_t mask = umask (0666); +static char * +format_error_message (const gchar *filename, + const gchar *format_string) G_GNUC_FORMAT(2); - errno = 0; - if (fchmod (fd, 0666 & ~mask) == -1) - _exit (errno); - else - _exit (0); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" - return TRUE; /* To quiet gcc */ - } - else - { - /* parent */ - int status; +static char * +format_error_message (const gchar *filename, + const gchar *format_string) +{ + gint saved_errno = errno; + gchar *display_name; + gchar *msg; - errno = 0; - if (waitpid (pid, &status, 0) == -1) - { - save_errno = errno; + display_name = g_filename_display_name (filename); + msg = g_strdup_printf (format_string, display_name, g_strerror (saved_errno)); + g_free (display_name); - g_set_error (err, - G_FILE_ERROR, - g_file_error_from_errno (save_errno), - _("Could not change file mode: waitpid() failed: %s"), - g_strerror (save_errno)); + return msg; +} - return FALSE; - } +#pragma GCC diagnostic pop - if (WIFEXITED (status)) - { - save_errno = WEXITSTATUS (status); +/* 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); - if (save_errno == 0) - { - return TRUE; - } - else - { - g_set_error (err, - G_FILE_ERROR, - g_file_error_from_errno (save_errno), - _("Could not change file mode: chmod() failed: %s"), - g_strerror (save_errno)); - - return FALSE; - } - } - else if (WIFSIGNALED (status)) - { - g_set_error (err, - G_FILE_ERROR, - G_FILE_ERROR_FAILED, - _("Could not change file mode: Child terminated by signal: %s"), - g_strsignal (WTERMSIG (status))); - - return FALSE; - } - else - { - /* This shouldn't happen */ - g_set_error (err, - G_FILE_ERROR, - G_FILE_ERROR_FAILED, - _("Could not change file mode: Child terminated abnormally")); - return FALSE; - } - } -#endif + g_set_error_literal (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno), + msg); + g_free (msg); } static gchar * -write_to_temp_file (const gchar *contents, - gssize length, - const gchar *template, - GError **err) +write_to_temp_file (const gchar *contents, + gssize length, + const gchar *dest_file, + 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", template); + + tmp_name = g_strdup_printf ("%s.XXXXXX", dest_file); errno = 0; - fd = g_mkstemp (tmp_name); - display_name = g_filename_display_name (tmp_name); - + fd = g_mkstemp_full (tmp_name, O_RDWR | O_BINARY, 0666); + if (fd == -1) { - save_errno = errno; - 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; } - if (!set_umask_permissions (fd, err)) +#ifdef HAVE_FALLOCATE + if (length > 0) { - 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); } - - errno = 0; - file = fdopen (fd, "wb"); - if (!file) +#endif + while (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)); + gssize s; - close (fd); - g_unlink (tmp_name); - - goto out; - } + s = write (fd, contents, length); - if (length > 0) - { - size_t n_written; - - errno = 0; + if (s < 0) + { + if (errno == EINTR) + continue; - n_written = fwrite (contents, 1, length, file); + set_file_error (err, tmp_name, _("Failed to write file '%s': write() failed: %s")); + close (fd); + g_unlink (tmp_name); - 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)); + goto out; + } - fclose (file); - g_unlink (tmp_name); - - goto out; - } + g_assert (s <= length); + + contents += s; + length -= s; } - - errno = 0; - if (fclose (file) == EOF) - { - save_errno = 0; - - 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)); +#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 + { + 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) + { + set_file_error (err, tmp_name, _("Failed to write file '%s': fsync() failed: %s")); + close (fd); + g_unlink (tmp_name); + + goto out; + } + } +#endif + +#ifdef BTRFS_SUPER_MAGIC + no_fsync: +#endif + + 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_replace: - * @filename: name of a file to write @contents to, in the GLib file name + * g_file_set_contents: + * @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 * @@ -1097,19 +1166,22 @@ write_to_temp_file (const gchar *contents, * * * - * 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. * + * 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 * * Since: 2.8 **/ gboolean -g_file_replace (const gchar *filename, - const gchar *contents, - gssize length, - GError **error) +g_file_set_contents (const gchar *filename, + const gchar *contents, + gssize length, + GError **error) { gchar *tmp_filename; gboolean retval; @@ -1160,7 +1232,7 @@ g_file_replace (const gchar *filename, { gchar *display_filename = g_filename_display_name (filename); - save_errno = errno; + int save_errno = errno; g_set_error (error, G_FILE_ERROR, @@ -1193,36 +1265,17 @@ g_file_replace (const gchar *filename, } /* - * mkstemp() implementation is 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. */ -/** - * g_mkstemp: - * @tmpl: template filename - * - * Opens a temporary file. See the mkstemp() documentation - * on most UNIX-like systems. This is a portability wrapper, which simply calls - * mkstemp() on systems that have it, and implements - * it in GLib otherwise. - * - * The parameter is a string that should match the rules for - * mkstemp(), i.e. end in "XXXXXX". The X string will - * be modified to form the name of a file that didn't exist. - * The string should be in the GLib file name encoding. Most importantly, - * on Windows it should be in UTF-8. - * - * Return value: A file handle (as from open()) to the file - * opened for reading and writing. The file is opened in binary mode - * on platforms where there is a difference. The file handle should be - * closed with close(). In case of errors, -1 is returned. - */ -gint -g_mkstemp (gchar *tmpl) +typedef gint (*GTmpFileCallback) (const gchar *, gint, gint); + +static gint +get_tmp_file (gchar *tmpl, + GTmpFileCallback f, + int flags, + int mode) { -#ifdef HAVE_MKSTEMP - return mkstemp (tmpl); -#else - int len; char *XXXXXX; int count, fd; static const char letters[] = @@ -1232,16 +1285,17 @@ g_mkstemp (gchar *tmpl) GTimeVal tv; static int counter = 0; - len = strlen (tmpl); - if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) + g_return_val_if_fail (tmpl != NULL, -1); + + /* find the last occurrence of "XXXXXX" */ + XXXXXX = g_strrstr (tmpl, "XXXXXX"); + + if (!XXXXXX || strncmp (XXXXXX, "XXXXXX", 6)) { errno = EINVAL; return -1; } - /* This is where the Xs start. */ - XXXXXX = &tmpl[len - 6]; - /* Get some more or less random data. */ g_get_current_time (&tv); value = (tv.tv_usec ^ tv.tv_sec) + counter++; @@ -1263,131 +1317,180 @@ g_mkstemp (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, 0600); + 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. */ errno = EEXIST; return -1; -#endif } -#ifdef G_OS_WIN32 - -#undef g_mkstemp - -/* Binary compatibility version. Not for newly compiled code. */ - -gint -g_mkstemp (gchar *tmpl) +/* 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) { - int len; - 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; - - len = strlen (tmpl); - if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) - { - errno = EINVAL; - return -1; - } - - /* This is where the Xs start. */ - XXXXXX = &tmpl[len - 6]; - - /* 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); + /* tmpl is in UTF-8 on Windows, thus use g_mkdir() */ + return g_mkdir (filename, mode); +} - 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; - } +static gint +wrap_g_open (const gchar *filename, + int flags, + int mode) +{ + return g_open (filename, flags, mode); +} - /* We got out of the loop because we ran out of combinations to try. */ - errno = EEXIST; - return -1; +/** + * g_mkdtemp_full: + * @tmpl: (type filename): template directory name + * @mode: permissions to create the temporary directory with + * + * Creates a temporary directory. See the mkdtemp() documentation + * on most UNIX-like systems. + * + * 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. 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 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) +{ + if (get_tmp_file (tmpl, wrap_g_mkdir, 0, mode) == -1) + return NULL; + else + return tmpl; } -#endif +/** + * g_mkdtemp: + * @tmpl: (type filename): template directory name + * + * Creates a temporary directory. See the mkdtemp() documentation + * on most UNIX-like systems. + * + * 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. + * + * Return value: 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 (gchar *tmpl) +{ + return g_mkdtemp_full (tmpl, 0700); +} /** - * g_file_open_tmp: - * @tmpl: Template for file name, as in g_mkstemp(), basename only - * @name_used: location to store actual name used - * @error: return location for a #GError + * 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 file for writing in the preferred directory for temporary - * files (as returned by g_get_tmp_dir()). + * Opens a temporary file. See the mkstemp() documentation + * on most UNIX-like systems. * - * @tmpl should be a string in the GLib file name encoding ending with - * six 'X' characters, as the parameter to g_mkstemp() (or 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. + * 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. + * + * Return value: 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 * - * Note that in contrast to g_mkstemp() (and mkstemp()) - * @tmpl is not modified, and might thus be a read-only literal string. + * Opens a temporary file. See the mkstemp() documentation + * on most UNIX-like systems. * - * 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. + * 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. * - * 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. - **/ + * 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 %errno will be set. + */ gint -g_file_open_tmp (const gchar *tmpl, - gchar **name_used, - GError **error) +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; - char *sep; + const char *sep; char *fulltemplate; const char *slash; @@ -1406,24 +1509,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 (strlen (tmpl) < 6 || - strcmp (tmpl + strlen (tmpl) - 6, "XXXXXX") != 0) + + 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 end with XXXXXX"), - display_tmpl); + G_FILE_ERROR, + G_FILE_ERROR_FAILED, + _("Template '%s' doesn't contain XXXXXX"), + display_tmpl); g_free (display_tmpl); return -1; } @@ -1437,68 +1539,123 @@ 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_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); g_free (fulltemplate); return -1; } - if (name_used) - *name_used = fulltemplate; - else - g_free (fulltemplate); + *name_used = fulltemplate; return retval; } -#ifdef G_OS_WIN32 - -#undef g_file_open_tmp - -/* Binary compatibility version. Not for newly compiled code. */ - +/** + * 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. + * + * 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) +g_file_open_tmp (const gchar *tmpl, + gchar **name_used, + GError **error) { - gchar *utf8_tmpl = g_locale_to_utf8 (tmpl, -1, NULL, NULL, error); - gchar *utf8_name_used; - gint retval; - - if (utf8_tmpl == NULL) - return -1; - - retval = g_file_open_tmp_utf8 (utf8_tmpl, &utf8_name_used, error); - - if (retval == -1) - return -1; + 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); + } - if (name_used) - *name_used = g_locale_from_utf8 (utf8_name_used, -1, NULL, NULL, NULL); + return result; +} - g_free (utf8_name_used); +/** + * 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. + * + * Return value: (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; - return retval; + if (g_get_tmp_name (tmpl, &fulltemplate, wrap_g_mkdir, 0, 0700, error) == -1) + return NULL; + else + return fulltemplate; } -#endif - static gchar * -g_build_pathv (const gchar *separator, - const gchar *first_element, - va_list args) +g_build_path_va (const gchar *separator, + const gchar *first_element, + va_list *args, + gchar **str_array) { GString *result; gint separator_len = strlen (separator); @@ -1507,10 +1664,14 @@ g_build_pathv (const gchar *separator, const gchar *single_element = NULL; const gchar *next_element; const gchar *last_trailing = NULL; + gint i = 0; result = g_string_new (NULL); - next_element = first_element; + if (str_array) + next_element = str_array[i++]; + else + next_element = first_element; while (TRUE) { @@ -1521,7 +1682,10 @@ g_build_pathv (const gchar *separator, if (next_element) { element = next_element; - next_element = va_arg (args, gchar *); + if (str_array) + next_element = str_array[i++]; + else + next_element = va_arg (*args, gchar *); } else break; @@ -1534,8 +1698,7 @@ g_build_pathv (const gchar *separator, if (separator_len) { - while (start && - strncmp (start, separator, separator_len) == 0) + while (strncmp (start, separator, separator_len) == 0) start += separator_len; } @@ -1592,10 +1755,34 @@ g_build_pathv (const gchar *separator, } /** + * g_build_pathv: + * @separator: a string used to separator the elements of the path. + * @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(). + * + * Since: 2.8 + */ +gchar * +g_build_pathv (const gchar *separator, + gchar **args) +{ + if (!args) + return NULL; + + return g_build_path_va (separator, NULL, NULL, args); +} + + +/** * g_build_path: * @separator: a string used to separator the elements of the path. * @first_element: the first element in the path - * @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, @@ -1638,54 +1825,22 @@ g_build_path (const gchar *separator, g_return_val_if_fail (separator != NULL, NULL); va_start (args, first_element); - str = g_build_pathv (separator, first_element, args); + str = g_build_path_va (separator, first_element, &args, NULL); va_end (args); return str; } -/** - * g_build_filename: - * @first_element: the first element in the path - * @Varargs: remaining elements in path, terminated by %NULL - * - * Creates a filename from a series of elements using the correct - * separator for filenames. - * - * On Unix, this function behaves identically to 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. - * - * No attempt is made to force the resulting filename to be an absolute - * path. If the first element is a relative path, the result will - * be a relative path. - * - * Return value: a newly-allocated string that must be freed with g_free(). - **/ -gchar * -g_build_filename (const gchar *first_element, - ...) -{ -#ifndef G_OS_WIN32 - gchar *str; - va_list args; - - va_start (args, first_element); - str = g_build_pathv (G_DIR_SEPARATOR_S, first_element, args); - va_end (args); +#ifdef G_OS_WIN32 - return str; -#else - /* Code copied from g_build_pathv(), and modifed to use two +static gchar * +g_build_pathname_va (const gchar *first_element, + va_list *args, + gchar **str_array) +{ + /* Code copied from g_build_pathv(), and modified to use two * alternative single-character separators. */ - va_list args; GString *result; gboolean is_first = TRUE; gboolean have_leading = FALSE; @@ -1693,13 +1848,15 @@ g_build_filename (const gchar *first_element, const gchar *next_element; const gchar *last_trailing = NULL; gchar current_separator = '\\'; - - va_start (args, first_element); + gint i = 0; result = g_string_new (NULL); - next_element = first_element; - + if (str_array) + next_element = str_array[i++]; + else + next_element = first_element; + while (TRUE) { const gchar *element; @@ -1709,7 +1866,10 @@ g_build_filename (const gchar *first_element, if (next_element) { element = next_element; - next_element = va_arg (args, gchar *); + if (str_array) + next_element = str_array[i++]; + else + next_element = va_arg (*args, gchar *); } else break; @@ -1771,8 +1931,6 @@ g_build_filename (const gchar *first_element, is_first = FALSE; } - va_end (args); - if (single_element) { g_string_free (result, TRUE); @@ -1785,7 +1943,76 @@ g_build_filename (const gchar *first_element, return g_string_free (result, FALSE); } +} + #endif + +/** + * g_build_filenamev: + * @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(). + * + * Since: 2.8 + */ +gchar * +g_build_filenamev (gchar **args) +{ + gchar *str; + +#ifndef G_OS_WIN32 + str = g_build_path_va (G_DIR_SEPARATOR_S, NULL, NULL, args); +#else + str = g_build_pathname_va (NULL, NULL, args); +#endif + + return str; +} + +/** + * g_build_filename: + * @first_element: the first element in the path + * @...: 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 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. + * + * No attempt is made to force the resulting filename to be an absolute + * path. If the first element is a relative path, the result will + * be a relative path. + * + * Return value: a newly-allocated string that must be freed with g_free(). + **/ +gchar * +g_build_filename (const gchar *first_element, + ...) +{ + gchar *str; + va_list args; + + va_start (args, first_element); +#ifndef G_OS_WIN32 + str = g_build_path_va (G_DIR_SEPARATOR_S, first_element, &args, NULL); +#else + str = g_build_pathname_va (first_element, &args, NULL); +#endif + va_end (args); + + return str; } /** @@ -1797,14 +2024,14 @@ g_build_filename (const gchar *first_element, * 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 string with the contents of the symbolic link, + * Returns: A newly-allocated string with the contents of the symbolic link, * or %NULL if an error occurred. * * Since: 2.4 */ gchar * -g_file_read_link (const gchar *filename, - GError **error) +g_file_read_link (const gchar *filename, + GError **error) { #ifdef HAVE_READLINK gchar *buffer; @@ -1843,14 +2070,557 @@ g_file_read_link (const gchar *filename, buffer = g_realloc (buffer, size); } #else - g_set_error (error, - G_FILE_ERROR, - G_FILE_ERROR_INVAL, - _("Symbolic links not supported")); + g_set_error_literal (error, + G_FILE_ERROR, + G_FILE_ERROR_INVAL, + _("Symbolic links not supported")); return NULL; #endif } -#define __G_FILEUTILS_C__ -#include "galiasdef.c" +/** + * 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. + * + * Return value: 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 ".". + * + * Return value: 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); + g_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. + * + * 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 + + gchar *buffer = NULL; + gchar *dir = NULL; + static gulong max_len = 0; + + if (max_len == 0) + max_len = (G_PATH_LENGTH == -1) ? 2048 : G_PATH_LENGTH; + +#if !defined(HAVE_GETCWD) + buffer = g_new (gchar, max_len + 1); + *buffer = 0; + dir = getwd (buffer); +#else + 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; + } +#endif /* !sun || !HAVE_GETCWD */ + + 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) + +/* Binary compatibility versions. Will be called by code compiled + * against quite old (pre-2.8, I think) headers only, not from more + * recently compiled code. + */ + +#undef g_file_test + +gboolean +g_file_test (const gchar *filename, + GFileTest test) +{ + gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, NULL); + gboolean retval; + + if (utf8_filename == NULL) + return FALSE; + + retval = g_file_test_utf8 (utf8_filename, test); + + g_free (utf8_filename); + + return retval; +} + +#undef g_file_get_contents + +gboolean +g_file_get_contents (const gchar *filename, + gchar **contents, + gsize *length, + GError **error) +{ + gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, error); + gboolean retval; + + if (utf8_filename == NULL) + return FALSE; + + retval = g_file_get_contents_utf8 (utf8_filename, contents, length, error); + + g_free (utf8_filename); + + return retval; +} + +#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, wrap_libc_open, + O_RDWR | O_CREAT | O_EXCL, 0600); +} + +#undef g_file_open_tmp + +gint +g_file_open_tmp (const gchar *tmpl, + gchar **name_used, + GError **error) +{ + gchar *utf8_tmpl = g_locale_to_utf8 (tmpl, -1, NULL, NULL, error); + gchar *utf8_name_used; + gint retval; + + if (utf8_tmpl == NULL) + return -1; + + retval = g_file_open_tmp_utf8 (utf8_tmpl, &utf8_name_used, error); + + if (retval == -1) + return -1; + + if (name_used) + *name_used = g_locale_from_utf8 (utf8_name_used, -1, NULL, NULL, NULL); + + g_free (utf8_name_used); + + 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 +