medit/moo/mooutils/mooutils-fs.cpp

1254 lines
30 KiB
C++
Raw Normal View History

/*
* mooutils-fs.c
*
2010-12-21 20:15:45 -08:00
* Copyright (C) 2004-2010 by Yevgen Muntyan <emuntyan@users.sourceforge.net>
*
* This file is part of medit. medit is free software; you can
* redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public
* License along with medit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "mooutils/mooutils-fs.h"
2007-08-20 20:15:58 -07:00
#include "mooutils/mooutils-debug.h"
2008-01-19 02:17:31 -08:00
#include "mooutils/mooutils-mem.h"
2008-02-20 16:45:20 -08:00
#include "mooutils/mootype-macros.h"
2008-09-07 08:56:37 -07:00
#include "mooutils/mooi18n.h"
2010-09-23 01:20:26 -07:00
#include <mooutils/mooutils-tests.h>
#include <mooglib/moo-stat.h>
#include <mooglib/moo-glib.h>
#include <stdio.h>
2008-09-07 08:56:37 -07:00
#include <string.h>
#include <errno.h>
2015-12-25 21:30:53 -08:00
#include <sys/types.h>
#ifdef __WIN32__
#include <windows.h>
2006-04-20 12:13:42 -07:00
#include <io.h>
2006-04-30 11:36:54 -07:00
#include <direct.h>
2009-11-01 12:44:46 -08:00
#include <shellapi.h>
#include <gdk/gdkwin32.h>
#endif
2008-01-14 22:43:03 -08:00
#ifndef S_IRWXU
#define S_IRWXU 0
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
2006-04-16 10:14:12 -07:00
#define BROKEN_NAME "<" "????" ">"
2008-09-06 22:15:07 -07:00
MOO_DEFINE_QUARK (moo-file-error-quark, _moo_file_error_quark)
/* XXX fix this */
gboolean
_moo_save_file_utf8 (const char *name,
const char *text,
gssize len,
GError **error)
{
GIOChannel *file;
GIOStatus status;
gsize bytes_written;
gsize real_len;
file = g_io_channel_new_file (name, "w", error);
if (!file)
return FALSE;
real_len = len < 0 ? strlen (text) : (gsize) len;
status = g_io_channel_write_chars (file, text,
len, &bytes_written,
error);
if (status != G_IO_STATUS_NORMAL || bytes_written != real_len)
{
/* glib #320668 */
g_io_channel_flush (file, NULL);
g_io_channel_shutdown (file, FALSE, NULL);
g_io_channel_unref (file);
return FALSE;
}
/* glib #320668 */
g_io_channel_flush (file, NULL);
g_io_channel_shutdown (file, FALSE, NULL);
g_io_channel_unref (file);
return TRUE;
}
2009-11-01 12:44:46 -08:00
#ifdef __WIN32__
static wchar_t *
filename_to_double_null_terminated_string (const char *filename)
{
long len;
gunichar2 *ret;
ret = g_utf8_to_utf16 (filename, -1, NULL, &len, NULL);
if (!ret)
return NULL;
ret = g_renew (gunichar2, ret, len + 2);
ret[len + 1] = 0;
return (wchar_t*) ret;
}
static wchar_t *
filename_list_to_double_null_terminated_string (GList *filenames)
{
GArray *ret;
gunichar2 wzero = 0;
ret = g_array_new (FALSE, FALSE, sizeof (gunichar2));
while (filenames)
{
guint old_len;
long len;
gunichar2 *wstr;
2016-10-23 12:43:28 -07:00
wstr = g_utf8_to_utf16 ((const char*) filenames->data, -1, NULL, &len, NULL);
2009-11-01 12:44:46 -08:00
filenames = filenames->next;
if (!wstr)
{
g_array_free (ret, TRUE);
return NULL;
}
old_len = ret->len;
g_array_set_size (ret, old_len + len + 1);
memcpy (ret->data + old_len * sizeof (gunichar2), wstr, (len + 1) * sizeof (gunichar2));
g_free (wstr);
}
g_array_append_val (ret, wzero);
return (wchar_t*) g_array_free (ret, FALSE);
}
static gboolean
move_or_copy_files_ui (GList *filenames,
const char *destdir,
gboolean copy,
GtkWidget *parent)
{
SHFILEOPSTRUCT shop = {0};
gboolean ret;
wchar_t *from, *to;
from = filename_list_to_double_null_terminated_string (filenames);
to = filename_to_double_null_terminated_string (destdir);
if (!from || !to)
{
g_free (from);
g_free (to);
return FALSE;
}
2016-10-23 12:43:28 -07:00
shop.hwnd = parent && parent->window ? (HWND) GDK_WINDOW_HWND (parent->window) : NULL;
2009-11-01 12:44:46 -08:00
shop.wFunc = copy ? FO_COPY : FO_MOVE;
shop.pFrom = from;
shop.pTo = to;
shop.fFlags = FOF_ALLOWUNDO;
ret = SHFileOperation(&shop) == 0 && !shop.fAnyOperationsAborted;
g_free (from);
g_free (to);
return ret;
}
gboolean
_moo_copy_files_ui (GList *filenames,
const char *destdir,
GtkWidget *parent)
{
return move_or_copy_files_ui (filenames, destdir, TRUE, parent);
}
gboolean
_moo_move_files_ui (GList *filenames,
const char *destdir,
GtkWidget *parent)
{
return move_or_copy_files_ui (filenames, destdir, FALSE, parent);
}
#endif /* __WIN32__ */
#ifndef __WIN32__
static gboolean
2006-04-16 10:14:12 -07:00
rm_fr (const char *path,
GError **error)
{
2006-04-16 10:14:12 -07:00
GError *error_here = NULL;
char **argv;
char *child_err;
int status;
argv = g_new0 (char*, 5);
argv[0] = g_strdup ("/bin/rm");
argv[1] = g_strdup ("-fr");
argv[2] = g_strdup ("--");
argv[3] = g_strdup (path);
if (!g_spawn_sync (NULL, argv, NULL, G_SPAWN_STDOUT_TO_DEV_NULL,
2006-04-16 10:14:12 -07:00
NULL, NULL, NULL, &child_err, &status, &error_here))
{
2006-04-16 10:14:12 -07:00
g_set_error (error, MOO_FILE_ERROR, MOO_FILE_ERROR_FAILED,
/* This is error message in file selector when rm
fails for some strange reason */
2008-09-07 08:56:37 -07:00
_("Could not run 'rm' command: %s"),
2006-04-16 10:14:12 -07:00
error_here->message);
g_error_free (error_here);
g_strfreev (argv);
return FALSE;
}
g_strfreev (argv);
if (!WIFEXITED (status) || WEXITSTATUS (status))
{
2008-09-07 08:56:37 -07:00
if (child_err && strlen (child_err) > 5000)
strcpy (child_err + 4997, "...");
2006-04-16 10:14:12 -07:00
g_set_error (error, MOO_FILE_ERROR,
MOO_FILE_ERROR_FAILED,
/* This is error message in file selector when rm
fails to delete a file or folder */
2008-09-07 08:56:37 -07:00
_("'rm' command failed: %s"),
2011-01-14 01:31:03 -08:00
MOO_NZS (child_err));
2008-09-07 08:56:37 -07:00
g_free (child_err);
return FALSE;
}
else
{
g_free (child_err);
return TRUE;
}
}
#endif /* __WIN32__ */
#ifdef __WIN32__
static gboolean
2006-04-16 10:14:12 -07:00
rm_r (const char *path,
GError **error)
{
GDir *dir;
const char *file;
gboolean success = TRUE;
g_return_val_if_fail (path != NULL, FALSE);
2006-05-09 01:00:12 -07:00
dir = g_dir_open (path, 0, error);
if (!dir)
return FALSE;
2006-05-09 01:00:12 -07:00
while (success && (file = g_dir_read_name (dir)))
{
char *file_path = g_build_filename (path, file, NULL);
2008-02-10 10:55:20 -08:00
if (g_file_test (file_path, G_FILE_TEST_IS_DIR))
{
2008-02-10 10:55:20 -08:00
if (!rm_r (file_path, error))
success = FALSE;
}
else
{
2015-12-25 21:30:53 -08:00
mgw_errno_t err;
2015-12-25 21:30:53 -08:00
if (mgw_remove (file_path, &err) != 0)
{
2008-02-10 10:55:20 -08:00
success = FALSE;
g_set_error (error, MOO_FILE_ERROR,
_moo_file_error_from_errno (err),
2008-09-07 08:56:37 -07:00
_("Could not remove %s: %s"), file_path,
2015-12-25 21:30:53 -08:00
mgw_strerror (err));
}
}
g_free (file_path);
}
g_dir_close (dir);
2006-08-01 18:18:35 -07:00
if (success)
{
2015-12-25 21:30:53 -08:00
mgw_errno_t err;
2006-08-01 18:18:35 -07:00
2015-12-25 21:30:53 -08:00
if (mgw_remove (path, &err) != 0)
2006-08-01 18:18:35 -07:00
{
success = FALSE;
g_set_error (error, MOO_FILE_ERROR,
_moo_file_error_from_errno (err),
2008-09-07 08:56:37 -07:00
_("Could not remove %s: %s"), path,
2015-12-25 21:30:53 -08:00
mgw_strerror (err));
2006-08-01 18:18:35 -07:00
}
}
return success;
}
#endif
gboolean
2006-11-03 21:57:57 -08:00
_moo_remove_dir (const char *path,
gboolean recursive,
GError **error)
{
g_return_val_if_fail (path != NULL, FALSE);
if (!recursive)
2006-04-16 10:14:12 -07:00
{
2015-12-25 21:30:53 -08:00
mgw_errno_t err;
2006-08-01 18:18:35 -07:00
2015-12-25 21:30:53 -08:00
if (mgw_remove (path, &err) != 0)
2006-04-16 10:14:12 -07:00
{
2006-08-23 01:09:17 -07:00
char *path_utf8 = g_filename_display_name (path);
2006-04-16 10:14:12 -07:00
g_set_error (error, MOO_FILE_ERROR,
_moo_file_error_from_errno (err),
2008-09-07 08:56:37 -07:00
_("Could not remove %s: %s"),
2015-12-25 21:30:53 -08:00
path_utf8, mgw_strerror (err));
2006-04-16 10:14:12 -07:00
g_free (path_utf8);
return FALSE;
}
else
{
return TRUE;
}
}
#ifndef __WIN32__
2006-04-16 10:14:12 -07:00
return rm_fr (path, error);
#else
2006-04-16 10:14:12 -07:00
return rm_r (path, error);
#endif
}
int
2015-12-25 21:30:53 -08:00
_moo_mkdir_with_parents (const char *path, mgw_errno_t* err)
{
2015-12-25 21:30:53 -08:00
return mgw_mkdir_with_parents (path, S_IRWXU, err);
}
2006-04-09 02:01:14 -07:00
gboolean
2006-11-03 21:57:57 -08:00
_moo_create_dir (const char *path,
GError **error)
2006-04-09 02:01:14 -07:00
{
MgwStatBuf buf;
2006-04-09 02:01:14 -07:00
char *utf8_path;
2015-12-25 21:30:53 -08:00
mgw_errno_t err;
2006-04-09 02:01:14 -07:00
g_return_val_if_fail (path != NULL, FALSE);
2015-12-25 21:30:53 -08:00
if (mgw_stat (path, &buf, &err) != 0 && err.value != MGW_ENOENT)
2006-04-09 02:01:14 -07:00
{
2006-08-23 01:09:17 -07:00
utf8_path = g_filename_display_name (path);
2006-04-09 02:01:14 -07:00
g_set_error (error,
2006-04-16 10:14:12 -07:00
MOO_FILE_ERROR,
2015-12-25 21:30:53 -08:00
_moo_file_error_from_errno (err),
2008-09-07 08:56:37 -07:00
_("Could not create folder %s: %s"),
2015-12-25 21:30:53 -08:00
utf8_path, mgw_strerror (err));
2006-04-09 02:01:14 -07:00
g_free (utf8_path);
return FALSE;
}
2015-12-25 21:30:53 -08:00
if (mgw_errno_is_set (err))
2006-04-09 02:01:14 -07:00
{
2015-12-25 21:30:53 -08:00
if (_moo_mkdir (path, &err) == -1)
2006-04-09 02:01:14 -07:00
{
2006-08-23 01:09:17 -07:00
utf8_path = g_filename_display_name (path);
2006-04-09 02:01:14 -07:00
g_set_error (error,
2006-04-16 10:14:12 -07:00
MOO_FILE_ERROR,
2015-12-25 21:30:53 -08:00
_moo_file_error_from_errno (err),
2008-09-07 08:56:37 -07:00
_("Could not create folder %s: %s"),
2015-12-25 21:30:53 -08:00
utf8_path, mgw_strerror (err));
2006-04-09 02:01:14 -07:00
g_free (utf8_path);
return FALSE;
}
return TRUE;
}
if (buf.isdir)
2006-04-09 02:01:14 -07:00
return TRUE;
2006-08-23 01:09:17 -07:00
utf8_path = g_filename_display_name (path);
2006-04-16 10:14:12 -07:00
g_set_error (error, MOO_FILE_ERROR,
MOO_FILE_ERROR_ALREADY_EXISTS,
2008-09-07 08:56:37 -07:00
_("Could not create folder %s: %s"),
2015-12-25 21:30:53 -08:00
utf8_path, mgw_strerror (MGW_E_EXIST));
2006-04-09 02:01:14 -07:00
g_free (utf8_path);
2006-04-16 10:14:12 -07:00
2006-04-09 02:01:14 -07:00
return FALSE;
}
2006-08-23 01:09:17 -07:00
gboolean
2006-11-03 21:57:57 -08:00
_moo_rename_file (const char *path,
const char *new_path,
GError **error)
2006-08-23 01:09:17 -07:00
{
// Do not break this for directories!
2015-12-25 21:30:53 -08:00
mgw_errno_t err;
2006-08-23 01:09:17 -07:00
g_return_val_if_fail (path != NULL, FALSE);
g_return_val_if_fail (new_path != NULL, FALSE);
2015-12-25 23:09:59 -08:00
if (mgw_rename (path, new_path, &err) != 0)
2006-08-23 01:09:17 -07:00
{
char *utf8_path = g_filename_display_name (path);
char *utf8_new_path = g_filename_display_name (new_path);
g_set_error (error,
MOO_FILE_ERROR,
2015-12-25 21:30:53 -08:00
_moo_file_error_from_errno (err),
2008-09-07 08:56:37 -07:00
_("Could not rename file %s to %s: %s"),
2015-12-25 21:30:53 -08:00
utf8_path, utf8_new_path, mgw_strerror (err));
2006-08-23 01:09:17 -07:00
g_free (utf8_path);
g_free (utf8_new_path);
return FALSE;
}
return TRUE;
}
2006-04-16 10:14:12 -07:00
MooFileError
2015-12-25 21:30:53 -08:00
_moo_file_error_from_errno (mgw_errno_t code)
2006-04-16 10:14:12 -07:00
{
2015-12-25 21:30:53 -08:00
switch (code.value)
2006-04-16 10:14:12 -07:00
{
2017-10-23 15:23:48 -07:00
#ifdef __WIN32__
2015-12-25 21:30:53 -08:00
case MGW_EACCES:
2017-10-23 15:23:48 -07:00
#endif
2015-12-25 21:30:53 -08:00
case MGW_EPERM:
2006-04-16 10:14:12 -07:00
return MOO_FILE_ERROR_ACCESS_DENIED;
2015-12-25 21:30:53 -08:00
case MGW_EEXIST:
2006-04-16 10:14:12 -07:00
return MOO_FILE_ERROR_ALREADY_EXISTS;
#ifndef __WIN32__
2015-12-25 21:30:53 -08:00
case MGW_ELOOP:
2006-04-16 10:14:12 -07:00
#endif
2015-12-25 21:30:53 -08:00
case MGW_ENAMETOOLONG:
2006-04-16 10:14:12 -07:00
return MOO_FILE_ERROR_BAD_FILENAME;
2015-12-25 21:30:53 -08:00
case MGW_ENOENT:
2006-04-16 10:14:12 -07:00
return MOO_FILE_ERROR_NONEXISTENT;
2015-12-25 21:30:53 -08:00
case MGW_ENOTDIR:
2006-04-16 10:14:12 -07:00
return MOO_FILE_ERROR_NOT_FOLDER;
2015-12-25 21:30:53 -08:00
case MGW_EROFS:
2006-04-16 10:14:12 -07:00
return MOO_FILE_ERROR_READONLY;
2015-12-25 21:30:53 -08:00
case MGW_EXDEV:
2006-04-16 10:14:12 -07:00
return MOO_FILE_ERROR_DIFFERENT_FS;
2015-12-25 21:30:53 -08:00
default:
return MOO_FILE_ERROR_FAILED;
}
2006-04-16 10:14:12 -07:00
}
2016-10-02 21:14:12 -07:00
char **
moo_filenames_from_locale (char **files)
2006-03-16 12:01:15 -08:00
{
2016-10-02 21:14:12 -07:00
guint i;
char **conv;
2006-03-16 12:01:15 -08:00
2016-10-02 21:14:12 -07:00
if (!files)
return NULL;
conv = g_new0 (char*, g_strv_length (files) + 1);
2006-03-16 12:01:15 -08:00
2016-10-02 21:14:12 -07:00
for (i = 0; files && *files; ++files)
2006-03-16 12:01:15 -08:00
{
2016-10-02 21:14:12 -07:00
conv[i] = moo_filename_from_locale (*files);
2006-03-16 12:01:15 -08:00
2016-10-02 21:14:12 -07:00
if (!conv[i])
g_warning ("could not convert '%s' to UTF8", *files);
else
++i;
2006-03-16 12:01:15 -08:00
}
2016-10-02 21:14:12 -07:00
return conv;
}
char *
moo_filename_from_locale (const char *file)
{
g_return_val_if_fail (file != NULL, NULL);
#ifdef __WIN32__
return g_locale_to_utf8 (file, -1, NULL, NULL, NULL);
#else
return g_strdup (file);
#endif
2006-07-05 12:16:47 -07:00
}
2016-10-02 21:14:12 -07:00
char *
_moo_filename_to_uri (const char *file,
GError **error)
2008-05-21 13:37:44 -07:00
{
2016-10-02 21:14:12 -07:00
char *uri;
char *freeme = NULL;
2008-05-21 13:37:44 -07:00
g_return_val_if_fail (file != NULL, NULL);
if (!_moo_path_is_absolute (file))
{
2016-10-02 21:14:12 -07:00
char *cd = g_get_current_dir ();
2017-10-23 16:13:26 -07:00
file = freeme = g_build_filename (cd, file, nullptr);
2016-10-02 21:14:12 -07:00
g_free (cd);
2008-05-21 13:37:44 -07:00
}
2016-10-02 21:14:12 -07:00
uri = g_filename_to_uri (file, NULL, error);
g_free (freeme);
return uri;
2008-05-21 13:37:44 -07:00
}
2006-03-16 12:01:15 -08:00
2008-01-19 02:17:31 -08:00
#ifndef __WIN32__
2016-10-02 21:14:12 -07:00
static char *
normalize_path_string (const char *path)
{
GPtrArray *comps;
gboolean first_slash;
char **pieces, **p;
char *normpath;
g_return_val_if_fail (path != NULL, NULL);
first_slash = (path[0] == G_DIR_SEPARATOR);
pieces = g_strsplit (path, G_DIR_SEPARATOR_S, 0);
g_return_val_if_fail (pieces != NULL, NULL);
comps = g_ptr_array_new ();
for (p = pieces; *p != NULL; ++p)
2006-08-01 18:18:35 -07:00
{
char *s = *p;
gboolean push = TRUE;
gboolean pop = FALSE;
if (!strcmp (s, "") || !strcmp (s, "."))
{
push = FALSE;
}
else if (!strcmp (s, ".."))
{
if (!comps->len && first_slash)
{
push = FALSE;
}
else if (comps->len)
{
push = FALSE;
pop = TRUE;
}
}
if (pop)
{
g_free (comps->pdata[comps->len - 1]);
g_ptr_array_remove_index (comps, comps->len - 1);
}
if (push)
g_ptr_array_add (comps, g_strdup (s));
2006-08-01 18:18:35 -07:00
}
g_ptr_array_add (comps, NULL);
if (comps->len == 1)
{
if (first_slash)
normpath = g_strdup (G_DIR_SEPARATOR_S);
else
normpath = g_strdup (".");
}
else
{
char *tmp = g_strjoinv (G_DIR_SEPARATOR_S, (char**) comps->pdata);
if (first_slash)
{
guint len = strlen (tmp);
2008-01-18 19:45:53 -08:00
normpath = g_renew (char, tmp, len + 2);
memmove (normpath + 1, normpath, len + 1);
normpath[0] = G_DIR_SEPARATOR;
}
else
{
normpath = tmp;
}
}
g_strfreev (pieces);
g_strfreev ((char**) comps->pdata);
g_ptr_array_free (comps, FALSE);
2006-08-01 18:18:35 -07:00
2016-10-02 21:14:12 -07:00
return normpath;
}
2016-10-02 21:14:12 -07:00
static char *
2008-01-19 02:17:31 -08:00
normalize_full_path_unix (const char *path)
{
2016-10-02 21:14:12 -07:00
guint len;
char *normpath;
g_return_val_if_fail (path != NULL, NULL);
2006-08-01 18:18:35 -07:00
2016-10-02 21:14:12 -07:00
normpath = normalize_path_string (path);
g_return_val_if_fail (normpath != NULL, NULL);
2016-10-02 21:14:12 -07:00
len = strlen (normpath);
g_return_val_if_fail (len > 0, normpath);
2016-10-02 21:14:12 -07:00
if (len > 1 && normpath[len-1] == G_DIR_SEPARATOR)
normpath[len-1] = 0;
2006-08-01 18:18:35 -07:00
return normpath;
}
2008-01-19 02:17:31 -08:00
#endif /* !__WIN32__ */
2016-10-02 21:14:12 -07:00
static char *
normalize_full_path_win32 (const char *fullpath)
{
2008-01-19 02:17:31 -08:00
char *prefix;
char *path;
guint slashes;
2016-10-02 21:14:12 -07:00
g_return_val_if_fail (fullpath && fullpath[0], g_strdup (fullpath));
2008-01-19 02:17:31 -08:00
2016-10-02 21:14:12 -07:00
if (fullpath[0] && fullpath[1] == ':')
{
2008-01-19 02:17:31 -08:00
prefix = g_strndup (fullpath, 3);
prefix[2] = '\\';
if (fullpath[2] == '\\')
2016-10-02 21:14:12 -07:00
path = g_strdup (fullpath + 3);
2008-01-19 02:17:31 -08:00
else
2016-10-02 21:14:12 -07:00
path = g_strdup (fullpath + 2);
}
else
{
2008-01-19 02:17:31 -08:00
prefix = NULL;
path = g_strdup (fullpath);
}
g_strdelimit (path, "/", '\\');
for (slashes = 0; path[slashes] == '\\'; ++slashes) ;
2008-01-19 02:17:31 -08:00
if (!prefix && slashes)
prefix = g_strndup (path, slashes);
2008-01-19 02:17:31 -08:00
if (slashes)
memmove (path, path + slashes, strlen (path) + 1 - slashes);
2008-01-19 02:17:31 -08:00
g_assert (prefix || *path);
g_assert (*path != '\\');
2008-01-19 02:17:31 -08:00
if (*path)
{
2008-01-19 02:17:31 -08:00
char **comps;
guint n_comps, i;
comps = g_strsplit (path, "\\", 0);
n_comps = g_strv_length (comps);
for (i = 0; i < n_comps; )
{
if (strcmp (comps[i], "") == 0 || strcmp (comps[i], ".") == 0 ||
(i == 0 && strcmp (comps[i], "..") == 0))
{
g_free (comps[i]);
MOO_ELMMOVE (comps + i, comps + i + 1, n_comps - i);
n_comps -= 1;
}
else if (strcmp (comps[i], "..") == 0)
{
g_free (comps[i-1]);
g_free (comps[i]);
MOO_ELMMOVE (comps + i - 1, comps + i + 1, n_comps - i);
n_comps -= 2;
i -= 1;
}
else
{
i += 1;
}
}
2008-01-19 02:17:31 -08:00
if (n_comps > 0)
{
char *tmp = g_strjoinv ("\\", comps);
g_free (path);
path = tmp;
}
else
2008-01-19 02:17:31 -08:00
{
path[0] = 0;
}
2008-01-19 02:17:31 -08:00
g_strfreev (comps);
}
2008-01-19 02:17:31 -08:00
if (*path)
{
2008-01-19 02:17:31 -08:00
if (prefix)
{
2017-10-23 16:13:26 -07:00
char *tmp = g_strconcat (prefix, path, nullptr);
2008-01-19 02:17:31 -08:00
g_free (path);
path = tmp;
}
}
2008-01-19 02:17:31 -08:00
else if (prefix)
{
2008-01-19 02:17:31 -08:00
g_free (path);
path = prefix;
prefix = NULL;
}
else
{
g_free (path);
path = g_strdup (".");
2006-08-01 18:18:35 -07:00
}
2008-01-19 02:17:31 -08:00
g_free (prefix);
2016-10-02 21:14:12 -07:00
return path;
2016-05-10 23:26:06 -07:00
}
2016-10-02 21:14:12 -07:00
static char *
normalize_path (const char *filename)
{
2016-10-02 21:14:12 -07:00
char *freeme = NULL;
char *norm_filename;
2016-10-02 21:14:12 -07:00
g_assert (filename && filename[0]);
2008-01-26 22:07:18 -08:00
if (!_moo_path_is_absolute (filename))
{
2016-10-02 21:14:12 -07:00
char *working_dir = g_get_current_dir ();
g_return_val_if_fail (working_dir != NULL, g_strdup (filename));
2017-10-23 16:13:26 -07:00
freeme = g_build_filename (working_dir, filename, nullptr);
2016-10-02 21:14:12 -07:00
filename = freeme;
g_free (working_dir);
}
2008-01-19 02:17:31 -08:00
#ifdef __WIN32__
2016-10-02 21:14:12 -07:00
norm_filename = normalize_full_path_win32 (filename);
2008-01-19 02:17:31 -08:00
#else
2016-10-02 21:14:12 -07:00
norm_filename = normalize_full_path_unix (filename);
2008-01-19 02:17:31 -08:00
#endif
2016-10-02 21:14:12 -07:00
g_free (freeme);
return norm_filename;
2006-08-01 18:18:35 -07:00
}
2008-01-02 17:29:34 -08:00
char *
_moo_normalize_file_path (const char *filename)
{
2016-10-02 21:14:12 -07:00
g_return_val_if_fail (filename != NULL, NULL);
/* empty filename is an error, but we don't want to crash here */
g_return_val_if_fail (filename[0] != 0, g_strdup (""));
return normalize_path (filename);
2008-01-02 17:29:34 -08:00
}
2008-01-26 22:07:18 -08:00
gboolean
_moo_path_is_absolute (const char *path)
2008-01-02 17:29:34 -08:00
{
2008-01-26 22:07:18 -08:00
g_return_val_if_fail (path != NULL, FALSE);
return g_path_is_absolute (path)
#ifdef __WIN32__
/* 'C:' is an absolute path even if glib doesn't like it */
/* This will match nonsense like 1:23:23 too, but that's not
* a valid path, and it's better to have "1:23:23" in the error
* message than "c:\some\silly\current\dir\1:23:23" */
2008-01-28 22:51:50 -08:00
|| path[1] == ':'
2008-01-18 23:34:36 -08:00
#endif
2008-01-26 22:07:18 -08:00
;
}
2008-01-18 23:34:36 -08:00
static void
test_normalize_path_one (const char *path,
2008-01-19 02:17:31 -08:00
const char *expected,
2008-01-19 02:58:44 -08:00
char *(*func) (const char*),
const char *func_name)
2008-01-18 23:34:36 -08:00
{
char *result;
2008-01-19 23:15:56 -08:00
TEST_EXPECT_WARNING (!(expected && expected[0]),
"%s(%s)", func_name, TEST_FMT_STR (path));
2008-01-19 02:17:31 -08:00
result = func (path);
2008-01-19 23:15:56 -08:00
TEST_CHECK_WARNING ();
2008-01-18 23:34:36 -08:00
2008-01-19 22:36:06 -08:00
TEST_ASSERT_STR_EQ_MSG (result, expected, "%s(%s)",
func_name, TEST_FMT_STR (path));
2008-01-18 23:34:36 -08:00
g_free (result);
}
static GPtrArray *
2008-01-19 02:17:31 -08:00
make_cases (gboolean unix_paths)
2008-01-18 23:34:36 -08:00
{
guint i;
char *current_dir;
GPtrArray *paths = g_ptr_array_new ();
2008-01-19 02:17:31 -08:00
const char *abs_files_common[] = {
2008-01-18 23:34:36 -08:00
NULL, NULL,
"", "",
2008-01-19 02:17:31 -08:00
};
const char *abs_files_unix[] = {
2008-01-18 23:34:36 -08:00
"/usr", "/usr",
"/usr/", "/usr",
2008-01-26 22:07:18 -08:00
"/usr///", "/usr",
2008-01-18 23:34:36 -08:00
"///usr////", "/usr",
"/", "/",
"//", "/",
"/usr/../bin/../usr", "/usr",
"/usr/bin/../bin/.", "/usr/bin",
"/..", "/",
"/../../../", "/",
"/../whatever/..", "/",
"/../whatever/.././././", "/",
2008-01-19 02:17:31 -08:00
};
const char *abs_files_win32[] = {
2008-01-18 23:34:36 -08:00
"C:", "C:\\",
"C:\\", "C:\\",
2008-01-26 22:07:18 -08:00
"C:\\\\", "C:\\",
2008-01-18 23:34:36 -08:00
"C:\\foobar", "C:\\foobar",
"C:\\foobar\\", "C:\\foobar",
"C:\\foobar\\\\\\", "C:\\foobar",
"C:/", "C:\\",
"C:/foobar", "C:\\foobar",
"C:/foobar//", "C:\\foobar",
"C:/foobar///", "C:\\foobar",
"\\\\.host\\dir\\root", "\\\\.host\\dir\\root",
"\\\\.host\\dir\\root\\", "\\\\.host\\dir\\root",
"\\\\.host\\dir\\root\\..\\..\\", "\\\\.host",
};
2008-01-19 02:17:31 -08:00
const char *rel_files_common[] = {
2008-01-18 23:34:36 -08:00
".", NULL,
"././././/", NULL,
"foobar", "foobar",
"foobar/", "foobar",
2008-01-26 22:07:18 -08:00
"foobar//", "foobar",
2008-01-18 23:34:36 -08:00
"foobar/..", NULL,
"foobar/./..", NULL,
"foobar/../", NULL,
"foobar/./../", NULL,
2008-01-19 02:17:31 -08:00
};
2008-01-19 02:58:44 -08:00
#ifndef __WIN32__
2008-01-19 02:17:31 -08:00
const char *rel_files_unix[] = {
2008-01-18 23:34:36 -08:00
"foobar/com", "foobar/com",
2008-01-19 02:17:31 -08:00
};
2008-01-19 02:58:44 -08:00
#endif
2008-01-19 02:17:31 -08:00
#ifdef __WIN32__
const char *rel_files_win32[] = {
2008-01-18 23:34:36 -08:00
"foobar/com", "foobar\\com",
".\\.\\.\\.\\\\", NULL,
"foobar\\", "foobar",
2008-01-26 22:07:18 -08:00
"foobar\\\\", "foobar",
2008-01-18 23:34:36 -08:00
"foobar\\..", NULL,
"foobar\\.\\..", NULL,
"foobar\\..\\", NULL,
"foobar\\.\\..\\", NULL,
};
2008-01-19 02:17:31 -08:00
#endif
2008-01-18 23:34:36 -08:00
2008-01-19 02:17:31 -08:00
for (i = 0; i < G_N_ELEMENTS (abs_files_common); i += 2)
2008-01-18 23:34:36 -08:00
{
2008-01-19 02:17:31 -08:00
g_ptr_array_add (paths, g_strdup (abs_files_common[i]));
g_ptr_array_add (paths, g_strdup (abs_files_common[i+1]));
2008-01-18 23:34:36 -08:00
}
2008-01-19 02:17:31 -08:00
if (unix_paths)
for (i = 0; i < G_N_ELEMENTS (abs_files_unix); i += 2)
{
g_ptr_array_add (paths, g_strdup (abs_files_unix[i]));
g_ptr_array_add (paths, g_strdup (abs_files_unix[i+1]));
}
else
for (i = 0; i < G_N_ELEMENTS (abs_files_win32); i += 2)
{
g_ptr_array_add (paths, g_strdup (abs_files_win32[i]));
g_ptr_array_add (paths, g_strdup (abs_files_win32[i+1]));
}
2008-01-18 23:34:36 -08:00
current_dir = g_get_current_dir ();
2008-01-19 02:17:31 -08:00
#ifndef __WIN32__
if (unix_paths)
#endif
for (i = 0; i < G_N_ELEMENTS (rel_files_common); i += 2)
{
g_ptr_array_add (paths, g_strdup (rel_files_common[i]));
if (rel_files_common[i+1])
2017-10-23 16:13:26 -07:00
g_ptr_array_add (paths, g_build_filename (current_dir, rel_files_common[i+1], nullptr));
2008-01-19 02:17:31 -08:00
else
g_ptr_array_add (paths, g_strdup (current_dir));
}
#ifndef __WIN32__
if (unix_paths)
for (i = 0; i < G_N_ELEMENTS (rel_files_unix); i += 2)
{
g_ptr_array_add (paths, g_strdup (rel_files_unix[i]));
if (rel_files_unix[i+1])
2017-10-23 16:13:26 -07:00
g_ptr_array_add (paths, g_build_filename (current_dir, rel_files_unix[i+1], nullptr));
2008-01-19 02:17:31 -08:00
else
g_ptr_array_add (paths, g_strdup (current_dir));
}
#endif
#ifdef __WIN32__
for (i = 0; i < G_N_ELEMENTS (rel_files_win32); i += 2)
2008-01-18 23:34:36 -08:00
{
2008-01-19 02:17:31 -08:00
g_ptr_array_add (paths, g_strdup (rel_files_win32[i]));
if (rel_files_win32[i+1])
g_ptr_array_add (paths, g_build_filename (current_dir, rel_files_win32[i+1], NULL));
2008-01-18 23:34:36 -08:00
else
g_ptr_array_add (paths, g_strdup (current_dir));
}
2008-01-19 02:17:31 -08:00
#endif
2008-01-18 23:34:36 -08:00
2008-01-19 02:17:31 -08:00
#ifndef __WIN32__
if (unix_paths)
#endif
2008-01-18 23:34:36 -08:00
{
char *parent_dir = g_path_get_dirname (current_dir);
g_ptr_array_add (paths, g_strdup (".."));
g_ptr_array_add (paths, g_strdup (parent_dir));
g_ptr_array_add (paths, g_strdup ("../"));
g_ptr_array_add (paths, g_strdup (parent_dir));
g_ptr_array_add (paths, g_strdup ("../."));
g_ptr_array_add (paths, g_strdup (parent_dir));
g_ptr_array_add (paths, g_strdup (".././"));
g_ptr_array_add (paths, g_strdup (parent_dir));
2008-01-26 22:07:18 -08:00
g_ptr_array_add (paths, g_strdup ("..//"));
g_ptr_array_add (paths, g_strdup (parent_dir));
2008-01-18 23:34:36 -08:00
#ifdef __WIN32__
g_ptr_array_add (paths, g_strdup ("..\\"));
g_ptr_array_add (paths, g_strdup (parent_dir));
g_ptr_array_add (paths, g_strdup ("..\\."));
g_ptr_array_add (paths, g_strdup (parent_dir));
g_ptr_array_add (paths, g_strdup ("..\\.\\"));
g_ptr_array_add (paths, g_strdup (parent_dir));
2008-01-26 22:07:18 -08:00
g_ptr_array_add (paths, g_strdup ("..\\\\"));
g_ptr_array_add (paths, g_strdup (parent_dir));
2008-01-18 23:34:36 -08:00
#endif
g_free (parent_dir);
}
g_free (current_dir);
return paths;
}
static void
test_normalize_file_path (void)
{
GPtrArray *paths;
guint i;
2008-01-19 02:17:31 -08:00
#ifndef __WIN32__
paths = make_cases (TRUE);
#else
paths = make_cases (FALSE);
#endif
for (i = 0; i < paths->len; i += 2)
{
2016-10-23 12:43:28 -07:00
test_normalize_path_one ((const char*) paths->pdata[i], (const char*) paths->pdata[i+1],
2008-01-19 02:58:44 -08:00
_moo_normalize_file_path,
"_moo_normalize_file_path");
2008-01-19 02:17:31 -08:00
g_free (paths->pdata[i]);
g_free (paths->pdata[i+1]);
}
g_ptr_array_free (paths, TRUE);
}
#ifndef __WIN32__
static void
test_normalize_file_path_win32 (void)
{
GPtrArray *paths;
guint i;
paths = make_cases (FALSE);
2008-01-18 23:34:36 -08:00
for (i = 0; i < paths->len; i += 2)
{
2017-10-23 15:23:48 -07:00
test_normalize_path_one ((const char*) paths->pdata[i],
(const char*) paths->pdata[i+1],
2016-10-02 21:14:12 -07:00
normalize_full_path_win32,
2008-01-19 02:58:44 -08:00
"normalize_full_path_win32");
2008-01-18 23:34:36 -08:00
g_free (paths->pdata[i]);
g_free (paths->pdata[i+1]);
}
g_ptr_array_free (paths, TRUE);
}
2008-01-19 02:17:31 -08:00
#endif
2008-01-18 23:34:36 -08:00
void
moo_test_mooutils_fs (void)
{
2016-10-23 12:43:28 -07:00
MooTestSuite& suite = moo_test_suite_new ("mooutils-fs", "mooutils/mooutils-fs.c", NULL, NULL, NULL);
2008-01-18 23:34:36 -08:00
moo_test_suite_add_test (suite, "_moo_normalize_file_path", "test of _moo_normalize_file_path()",
2008-01-30 23:16:57 -08:00
(MooTestFunc) test_normalize_file_path, NULL);
2008-01-19 02:17:31 -08:00
#ifndef __WIN32__
moo_test_suite_add_test (suite, "normalize_full_path_win32", "test of normalize_full_path_win32()",
2008-01-30 23:16:57 -08:00
(MooTestFunc) test_normalize_file_path_win32, NULL);
2008-01-19 02:17:31 -08:00
#endif
2008-01-18 23:34:36 -08:00
}
2006-08-01 18:18:35 -07:00
int
2015-12-25 21:30:53 -08:00
_moo_mkdir (const char *path, mgw_errno_t *err)
{
2015-12-25 23:09:59 -08:00
#ifndef __WIN32__
return mgw_mkdir (path, S_IRWXU, err);
#else
2015-12-25 23:09:59 -08:00
return mgw_mkdir (path, 0, err);
#endif
}
2006-11-12 11:12:46 -08:00
/***********************************************************************/
/* Glob matching
*/
#define MOO_GLOB_REGEX
#include <mooglib/moo-glib.h>
2006-11-12 11:12:46 -08:00
typedef struct _MooGlob {
2006-11-12 11:12:46 -08:00
#ifdef MOO_GLOB_REGEX
GRegex *re;
2006-11-12 11:12:46 -08:00
#else
char *pattern;
#endif
} MooGlob;
2006-11-12 11:12:46 -08:00
#ifdef MOO_GLOB_REGEX
static char *
glob_to_re (const char *pattern)
{
GString *string;
const char *p, *piece, *bracket;
char *escaped;
g_return_val_if_fail (pattern != NULL, NULL);
p = piece = pattern;
string = g_string_new (NULL);
while (*p)
{
switch (*p)
{
case '*':
if (p != piece)
g_string_append_len (string, piece, p - piece);
g_string_append (string, ".*");
piece = ++p;
break;
case '?':
if (p != piece)
g_string_append_len (string, piece, p - piece);
g_string_append_c (string, '.');
piece = ++p;
break;
case '[':
if (!(bracket = strchr (p + 1, ']')))
{
g_warning ("in %s: unmatched '['", pattern);
goto error;
}
if (p != piece)
g_string_append_len (string, piece, p - piece);
g_string_append_c (string, '[');
if (p[1] == '^')
{
g_string_append_c (string, '^');
2015-07-11 14:32:02 -07:00
escaped = g_regex_escape_string (p + 2, (int) (bracket - p - 2));
2006-11-12 11:12:46 -08:00
}
else
{
2015-07-11 14:32:02 -07:00
escaped = g_regex_escape_string (p + 1, (int) (bracket - p - 1));
2006-11-12 11:12:46 -08:00
}
g_string_append (string, escaped);
g_free (escaped);
g_string_append_c (string, ']');
piece = p = bracket + 1;
break;
case '\\':
case '|':
case '(':
case ')':
case ']':
case '{':
case '}':
case '^':
case '$':
case '+':
case '.':
if (p != piece)
g_string_append_len (string, piece, p - piece);
g_string_append_c (string, '\\');
g_string_append_c (string, *p);
piece = ++p;
break;
default:
p = g_utf8_next_char (p);
}
}
if (*piece)
g_string_append (string, piece);
g_string_append_c (string, '$');
if (0)
_moo_message ("converted '%s' to '%s'\n", pattern, string->str);
return g_string_free (string, FALSE);
error:
g_string_free (string, TRUE);
return NULL;
}
static MooGlob *
2006-11-12 11:12:46 -08:00
_moo_glob_new (const char *pattern)
{
MooGlob *gl;
GRegex *re;
2006-11-12 11:12:46 -08:00
char *re_pattern;
2016-10-23 12:43:28 -07:00
GRegexCompileFlags flags = (GRegexCompileFlags) 0;
2006-11-12 11:12:46 -08:00
GError *error = NULL;
g_return_val_if_fail (pattern != NULL, NULL);
#ifdef __WIN32__
flags = G_REGEX_CASELESS;
2006-11-12 11:12:46 -08:00
#endif
if (!(re_pattern = glob_to_re (pattern)))
return NULL;
2016-10-23 12:43:28 -07:00
re = g_regex_new (re_pattern, flags, (GRegexMatchFlags) 0, &error);
2006-11-12 11:12:46 -08:00
g_free (re_pattern);
if (!re)
2006-11-12 11:12:46 -08:00
{
g_warning ("%s", moo_error_message (error));
2006-11-12 11:12:46 -08:00
g_error_free (error);
return NULL;
}
2006-11-12 11:12:46 -08:00
gl = g_new0 (MooGlob, 1);
gl->re = re;
return gl;
}
static gboolean
2006-11-12 11:12:46 -08:00
_moo_glob_match (MooGlob *glob,
const char *filename_utf8)
{
g_return_val_if_fail (glob != NULL, FALSE);
g_return_val_if_fail (filename_utf8 != NULL, FALSE);
g_return_val_if_fail (g_utf8_validate (filename_utf8, -1, NULL), FALSE);
2016-10-23 12:43:28 -07:00
return g_regex_match (glob->re, filename_utf8, (GRegexMatchFlags) 0, NULL);
2006-11-12 11:12:46 -08:00
}
#endif
static void
2006-11-12 11:12:46 -08:00
_moo_glob_free (MooGlob *glob)
{
if (glob)
{
#ifdef MOO_GLOB_REGEX
g_regex_unref (glob->re);
2006-11-12 11:12:46 -08:00
#else
g_free (glob->pattern);
#endif
g_free (glob);
}
}
gboolean
_moo_glob_match_simple (const char *pattern,
const char *filename)
{
MooGlob *gl;
gboolean result = FALSE;
g_return_val_if_fail (pattern != NULL, FALSE);
g_return_val_if_fail (filename != NULL, FALSE);
if ((gl = _moo_glob_new (pattern)))
result = _moo_glob_match (gl, filename);
2006-12-12 20:45:23 -08:00
if (result && 0)
2006-11-12 11:12:46 -08:00
_moo_message ("'%s' matched '%s'", filename, pattern);
_moo_glob_free (gl);
return result;
}