#include "config.h" /* must be first for 64-bit file-offset support */
#include "gskutils.h"
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include "gskerror.h"
/**
* gsk_mkdir_p:
* @dir: the directory to make.
* @permissions: file creation mode for the directory
* and its subdirectories.
* @error: where to put an error, if one occurs.
*
* Make a directory and any nonexistant parent directories.
*
* This parallels the unix command 'mkdir -p DIR'.
*
* returns: whether the directory now exists.
* Note that this function returns TRUE if the directory
* existed on startup.
*/
gboolean gsk_mkdir_p (const char *dir,
guint permissions,
GError **error)
{
guint dir_len = strlen (dir);
char *dir_buf = g_alloca (dir_len + 1);
guint cur_len = 0;
if (g_file_test (dir, G_FILE_TEST_IS_DIR))
return TRUE;
/* append to dir_buf any number of consecutive '/'
characters. */
#define SCAN_THROUGH_SLASHES() \
G_STMT_START{ \
while (cur_len < dir_len && dir[cur_len] == G_DIR_SEPARATOR) \
dir_buf[cur_len++] = G_DIR_SEPARATOR; \
}G_STMT_END
SCAN_THROUGH_SLASHES();
while (cur_len < dir_len)
{
const char *slash = strchr (dir + cur_len, G_DIR_SEPARATOR); /* not UTF8 */
guint new_cur_len;
if (slash == NULL)
new_cur_len = dir_len;
else
new_cur_len = slash - dir;
memcpy (dir_buf + cur_len, dir + cur_len, new_cur_len - cur_len);
dir_buf[new_cur_len] = 0;
cur_len = new_cur_len;
if (g_file_test (dir_buf, G_FILE_TEST_IS_DIR))
;
else
{
if (mkdir (dir_buf, permissions) < 0)
{
if (errno != EEXIST)
{
g_set_error (error, GSK_G_ERROR_DOMAIN,
gsk_error_code_from_errno (errno),
"error making directory %s: %s",
dir_buf, g_strerror (errno));
return FALSE;
}
}
}
SCAN_THROUGH_SLASHES();
}
return TRUE;
#undef SCAN_THROUGH_SLASHES
}
/* TODO: actually, this should be TRUE for all known versions
of linux... that would speed up rm_rf by eliminating an
extra lstat(2) call. */
#define UNLINK_DIR_RETURNS_EISDIR FALSE
static gboolean
safe_unlink (const char *dir_or_file,
const char **failed_op_out,
int *errno_out)
{
#if ! UNLINK_DIR_RETURNS_EISDIR
struct stat stat_buf;
if (lstat (dir_or_file, &stat_buf) < 0)
{
*errno_out = errno;
*failed_op_out = "lstat";
return FALSE;
}
if (S_ISDIR (stat_buf.st_mode))
{
*errno_out = EISDIR;
*failed_op_out = "unlink";
return FALSE;
}
#endif
if (unlink (dir_or_file) < 0)
{
*errno_out = errno;
*failed_op_out = "unlink";
return FALSE;
}
return TRUE;
}
/**
* gsk_rm_rf:
* @dir_or_file: the directory or file to delete.
* @error: optional error return location.
*
* Recursively remove a directory or file,
* similar to 'rm -rf DIR_OR_FILE' on the unix command-line.
*
* returns: whether the removal was successful.
* This routine fails if there is a permission or i/o problem.
* (It returns TRUE if the file does not exist.)
* If it fails, and error is non-NULL, *error will hold
* a #GError object.
*/
gboolean gsk_rm_rf (const char *dir_or_file,
GError **error)
{
int e;
const char *op;
if (!g_file_test (dir_or_file, G_FILE_TEST_EXISTS))
return TRUE;
if (!safe_unlink (dir_or_file, &op, &e))
{
if (e == EISDIR)
{
/* scan directory, removing contents recursively */
GDir *dir = g_dir_open (dir_or_file, 0, error);
const char *base;
if (dir == NULL)
return FALSE;
while ((base = g_dir_read_name (dir)) != NULL)
{
char *fname;
/* skip . and .. */
if (base[0] == '.'
&& (base[1] == 0 || (base[1] == '.' && base[2] == 0)))
continue;
/* recurse */
fname = g_strdup_printf ("%s/%s", dir_or_file, base);
if (!gsk_rm_rf (fname, error))
{
g_free (fname);
g_dir_close (dir);
return FALSE;
}
g_free (fname);
}
g_dir_close (dir);
if (rmdir (dir_or_file) < 0)
{
g_set_error (error, GSK_G_ERROR_DOMAIN,
gsk_error_code_from_errno (errno),
"error running rmdir(%s): %s", dir_or_file,
g_strerror (errno));
return FALSE;
}
return TRUE;
}
else
{
g_set_error (error, GSK_G_ERROR_DOMAIN,
gsk_error_code_from_errno (e),
"error %s %s: %s", op, dir_or_file, g_strerror (e));
return FALSE;
}
}
return TRUE;
}
/**
* gsk_escape_memory:
* @data: raw data to C-escape.
* @len: length of raw data in bytes
*
* Convert a bunch of memory to something
* suitable for addition into a C string.
*
* returns: a newly allocated string of escaped data.
*/
char *
gsk_escape_memory (gconstpointer data,
guint len)
{
GString *out = g_string_new ("");
guint i;
for (i = 0; i < len; i++)
{
guint8 c = ((guint8 *) data)[i];
if (!isprint (c) || c <= 27 || c == '"' || c == '\\')
{
switch (c)
{
case '\t':
g_string_append (out, "\\t");
break;
case '\r':
g_string_append (out, "\\r");
break;
case '\n':
g_string_append (out, "\\n");
break;
case '\\':
g_string_append (out, "\\\\");
break;
case '"':
g_string_append (out, "\\\"");
break;
default:
g_string_sprintfa (out, "\\%o", c);
break;
}
}
else
{
g_string_append_c (out, c);
}
}
return g_string_free (out, FALSE);
}
/**
* gsk_unescape_memory:
* @quoted: C-string to unquote.
* @has_quote_marks: whether to strip off double-quotes.
* @end: where to store the end of the quoted string (right
* past the last double-quote.
* @length_out: where to store the length of the
* unquoted memory.
* @error: optional error return location.
*
* Take a C double-quoted string and make it into a
* suitable for addition into a C string.
*
* returns: a newly allocated string of raw data,
* with an extra NUL postpended, so that it can
* be used as a string.
*/
gpointer
gsk_unescape_memory (const char *quoted,
gboolean has_quote_marks,
const char**end,
guint *length_out,
GError **error)
{
/* parse quoted string */
GString *s = g_string_new ("");
const char *str = quoted;
if (has_quote_marks)
{
if (*str != '"')
goto expected_double_quote;
str++;
}
while (*str != '"' && *str != '\0')
{
if (*str == '\\')
{
str++;
if (g_ascii_isalpha (*str))
{
static const char *pairs
= "r\r"
"n\n"
"t\t"
;
const char *p = pairs;
while (*p)
{
if (*p == *str)
break;
p += 2;
}
if (*p == 0)
goto bad_backslashed;
g_string_append_c (s, p[1]);
}
else if (g_ascii_isdigit (*str))
{
/* octal */
/* XXX: i don't think this really matches
how c parses \ooo expressions... */
guint c = strtoul (s, &end, 8);
g_string_append_c (s, c);
}
else
goto bad_backslashed;
}
else
{
g_string_append_c (s, *str);
str++;
}
}
if (has_quote_marks)
{
if (*str != '"')
goto expected_double_quote;
str++;
}
if (end)
*end = str;
if (length_out)
*length_out = s->len;
return g_string_free (s, FALSE);
bad_backslashed:
g_set_error (error, GSK_G_ERROR_DOMAIN, GSK_ERROR_BAD_FORMAT,
"unknown backslashed character \\%c", *str);
g_string_free (s, TRUE);
return NULL;
expected_double_quote:
if (*str == 0)
g_set_error (error, GSK_G_ERROR_DOMAIN, GSK_ERROR_BAD_FORMAT,
"end-of-string parsing double-quoted string");
else
g_set_error (error, GSK_G_ERROR_DOMAIN, GSK_ERROR_BAD_FORMAT,
"bad character %c instead of double-quote", *str);
g_string_free (s, TRUE);
return NULL;
}
/**
* gsk_escape_memory_hex:
* @data: raw data to dump as hex
* @len: length of raw data in bytes
*
* Convert a bunch of memory to its hex dump.
*
* returns: a newly allocated string of hex digits.
*/
char *gsk_escape_memory_hex (gconstpointer data,
guint len)
{
char *out = g_malloc (len * 2 + 1);
char *at = out;
const guint8 *in = data;
static char value_to_hex[16] = { "0123456789abcdef" };
while (len--)
{
guint8 i = *in++;
*at++ = value_to_hex[i >> 4];
*at++ = value_to_hex[i & 0xf];
}
*at = 0;
return out;
}
#define IS_HEX_CHAR(c) \
(('0' <= (c) && (c) <= '9') \
|| ('a' <= (c) && (c) <= 'f') \
|| ('A' <= (c) && (c) <= 'F'))
#define HEX_VALUE(c) \
(((c) <= '9') ? ((c) - '0') \
: ((c) <= 'F') ? ((c) - 'A' + 10) \
: ((c) - 'a' + 10))
/**
* gsk_unescape_memory_hex:
* @str: the memory dump as a string.
* @len: the maximum length of the string, or -1 to use NUL-termination.
* @length_out: length of the returned memory.
* @error: where to put an error, if one occurs.
*
* Converts an even-number of hex digits into a
* binary set of bytes, and returns the bytes.
* Even if the data is length 0, you must free the return value.
*
* If NULL is returned, it means an error occurred.
*
* returns: the binary data.
*/
guint8 *
gsk_unescape_memory_hex (const char *str,
gssize len,
gsize *length_out,
GError **error)
{
guint outlen;
guint8 *rv;
guint i;
if (len >= 0)
{
for (i = 0; i < (guint)len; i++)
if (str[i] == 0)
{
len = i;
break;
}
else if (IS_HEX_CHAR (str[i]))
;
else
{
g_set_error (error, GSK_G_ERROR_DOMAIN,
GSK_ERROR_BAD_FORMAT,
"invalid char %c in hex string", str[i]);
return NULL;
}
}
else
{
for (i = 0; str[i]; i++)
if (!IS_HEX_CHAR (str[i]))
{
g_set_error (error, GSK_G_ERROR_DOMAIN,
GSK_ERROR_BAD_FORMAT,
"invalid char %c in hex string", str[i]);
return NULL;
}
len = i;
}
if (len % 2 == 1)
{
g_set_error (error, GSK_G_ERROR_DOMAIN,
GSK_ERROR_BAD_FORMAT,
"hex escaped data should be an even number of nibbles");
return NULL;
}
outlen = len / 2;
*length_out = outlen;
rv = g_malloc (MAX (1,outlen));
for (i = 0; i < outlen; i++)
rv[i] = (HEX_VALUE (str[2*i+0]) << 4)
+ (HEX_VALUE (str[2*i+1]) << 0);
return rv;
}
/**
* gsk_fd_set_close_on_exec:
* @fd: the file-descriptor to affect.
* @close_on_exec: whether the close the file-descriptor on exec(2).
*
* This function sets the value of the close-on-exec flag
* for a file-descriprtor.
*
* Most files will be closed on the exec system call, but normally 0,1,2
* (standard input, output and error) are set not to close-on-exec,
* which is why they are always inherited.
*/
void gsk_fd_set_close_on_exec (int fd, gboolean close_on_exec)
{
int fdflags = fcntl (fd, F_GETFD);
if (close_on_exec)
fdflags |= FD_CLOEXEC;
else
fdflags &= ~FD_CLOEXEC;
fcntl (fd, F_SETFD, fdflags);
}
/**
* gsk_fatal_user_error:
* @format: printf-style format string.
* @...: printf-style arguments.
*
* Print the message the standard-error,
* plus a terminating newline.
* Then exit with status 1.
*/
void gsk_fatal_user_error (const char *format,
...)
{
va_list args;
va_start (args, format);
vfprintf (stderr, format, args);
va_end (args);
fputc ('\n', stderr);
exit (1);
}
/**
* gsk_readn:
* @fd: the file-descriptor to read from.
* @buf: the buffer to fill.
* @len: number of bytes to read from fd.
*
* Read data from fd, retrying the read
* if not enough data is found. This is only
* for blocking reads.
*
* returns: the number of bytes read or -1 if an error occurs.
*/
gssize
gsk_readn (guint fd, void *buf, gsize len)
{
gsize rv = 0;
while (rv < len)
{
gssize read_rv = read (fd, (char*)buf + rv, len - rv);
if (read_rv < 0)
return read_rv;
if (read_rv == 0)
break;
rv += (gsize) read_rv;
}
return rv;
}
/**
* gsk_writen:
* @fd: the file-descriptor to write to.
* @buf: the buffer to read from.
* @len: number of bytes to write to fd.
*
* Write data to fd, retrying the write
* if not enough data is sent. This is only
* for blocking writes.
*
* returns: the number of bytes written or -1 if an error occurs.
*/
gssize
gsk_writen (guint fd, const void *buf, gsize len)
{
gsize rv = 0;
while (rv < len)
{
gssize write_rv = write (fd, (const char*)buf + rv, len - rv);
if (write_rv < 0)
return write_rv;
if (write_rv == 0)
break;
rv += (gsize) write_rv;
}
return rv;
}
syntax highlighted by Code2HTML, v. 0.9.1