/*
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2003 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2005 Hiroyuki Yamamoto & The Sylpheed-Claws Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include "defs.h"
#include <glib.h>
+#include <glib/gi18n.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/wait.h>
#include <dirent.h>
#include <time.h>
+#include <regex.h>
+#include <sys/utsname.h>
-#include "intl.h"
#include "utils.h"
#include "socket.h"
+#include "../codeconv.h"
#define BUFFSIZE 8192
g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
}
+gint str_case_equal(gconstpointer v, gconstpointer v2)
+{
+ return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
+}
+
+guint str_case_hash(gconstpointer key)
+{
+ const gchar *p = key;
+ guint h = *p;
+
+ if (h) {
+ h = tolower(h);
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + tolower(*p);
+ }
+
+ return h;
+}
+
void ptr_array_free_strings(GPtrArray *array)
{
gint i;
}
}
+gboolean str_find(const gchar *haystack, const gchar *needle)
+{
+ return strstr(haystack, needle) != NULL ? TRUE : FALSE;
+}
+
+gboolean str_case_find(const gchar *haystack, const gchar *needle)
+{
+ return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
+}
+
+gboolean str_find_equal(const gchar *haystack, const gchar *needle)
+{
+ return strcmp(haystack, needle) == 0;
+}
+
+gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
+{
+ return strcasecmp(haystack, needle) == 0;
+}
+
gint to_number(const gchar *nstr)
{
- register const gchar *p;
+ register const guchar *p;
if (*nstr == '\0') return -1;
return NULL;
while (haystack_len >= needle_len) {
- if (!strncasecmp(haystack, needle, needle_len))
+ if (!g_ascii_strncasecmp(haystack, needle, needle_len))
return (gchar *)haystack;
else {
haystack++;
return NULL;
}
+gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
+ gconstpointer needle, size_t needlelen)
+{
+ const gchar *haystack_ = (const gchar *)haystack;
+ const gchar *needle_ = (const gchar *)needle;
+ const gchar *haystack_cur = (const gchar *)haystack;
+
+ if (needlelen == 1)
+ return memchr(haystack_, *needle_, haystacklen);
+
+ while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
+ != NULL) {
+ if (haystacklen - (haystack_cur - haystack_) < needlelen)
+ break;
+ if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
+ return (gpointer)haystack_cur;
+ else
+ haystack_cur++;
+ }
+
+ return NULL;
+}
+
/* Copy no more than N characters of SRC to DEST, with NULL terminating. */
gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
{
- register gchar c;
- gchar *s = dest;
+ register const gchar *s = src;
+ register gchar *d = dest;
- do {
- if (--n == 0) {
- *dest = '\0';
- return s;
- }
- c = *src++;
- *dest++ = c;
- } while (c != '\0');
+ while (--n && *s)
+ *d++ = *s++;
+ *d = '\0';
- /* don't do zero fill */
- return s;
+ return dest;
}
#if !HAVE_ISWALNUM
return NULL;
}
+gint get_mbs_len(const gchar *s)
+{
+ const gchar *p = s;
+ gint mb_len;
+ gint len = 0;
+
+ if (!p)
+ return -1;
+
+ while (*p != '\0') {
+ mb_len = g_utf8_skip[*(guchar *)p];
+ if (mb_len == 0)
+ break;
+ else
+ len++;
+
+ p += mb_len;
+ }
+
+ return len;
+}
+
/* Examine if next block is non-ASCII string */
gboolean is_next_nonascii(const guchar *s)
{
return FALSE;
}
-gint get_next_word_len(const gchar *s)
+gint get_next_word_len(const guchar *s)
{
gint len = 0;
trim_subject_for_sort(str1);
trim_subject_for_sort(str2);
- return strcasecmp(str1, str2);
+ return g_utf8_collate(str1, str2);
}
void trim_subject_for_compare(gchar *str)
eliminate_parenthesis(str, '(', ')');
g_strstrip(str);
- while (!strncasecmp(str, "Re:", 3)) {
- srcp = str + 3;
- while (isspace(*srcp)) srcp++;
+ srcp = str + subject_get_prefix_length(str);
+ if (srcp != str)
memmove(str, srcp, strlen(srcp) + 1);
- }
}
void trim_subject_for_sort(gchar *str)
g_strstrip(str);
- while (!strncasecmp(str, "Re:", 3)) {
- srcp = str + 3;
- while (isspace(*srcp)) srcp++;
+ srcp = str + subject_get_prefix_length(str);
+ if (srcp != str)
memmove(str, srcp, strlen(srcp) + 1);
- }
}
void trim_subject(gchar *str)
{
- register gchar *srcp, *destp;
+ register guchar *srcp;
gchar op, cl;
gint in_brace;
+
+ g_strstrip(str);
- destp = str;
- while (!strncasecmp(destp, "Re:", 3)) {
- destp += 3;
- while (isspace(*destp)) destp++;
- }
+ srcp = str + subject_get_prefix_length(str);
- if (*destp == '[') {
+ if (*srcp == '[') {
op = '[';
cl = ']';
- } else if (*destp == '(') {
+ } else if (*srcp == '(') {
op = '(';
cl = ')';
} else
- return;
+ op = 0;
- srcp = destp + 1;
- in_brace = 1;
- while (*srcp) {
- if (*srcp == op)
- in_brace++;
- else if (*srcp == cl)
- in_brace--;
- srcp++;
- if (in_brace == 0)
- break;
+ if (op) {
+ ++srcp;
+ in_brace = 1;
+ while (*srcp) {
+ if (*srcp == op)
+ in_brace++;
+ else if (*srcp == cl)
+ in_brace--;
+ srcp++;
+ if (in_brace == 0)
+ break;
+ }
}
while (isspace(*srcp)) srcp++;
- memmove(destp, srcp, strlen(srcp) + 1);
+ memmove(str, srcp, strlen(srcp) + 1);
}
void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
{
- register gchar *srcp, *destp;
+ register guchar *srcp, *destp;
gint in_brace;
srcp = destp = str;
*destp = '\0';
}
-void extract_one_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
- gchar op, gchar cl)
-{
- register gchar *srcp, *destp;
- gint in_brace;
- gboolean in_quote = FALSE;
-
- srcp = destp = str;
-
- if ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
- memmove(destp, srcp + 1, strlen(srcp));
- in_brace = 1;
- while(*destp) {
- if (*destp == op && !in_quote)
- in_brace++;
- else if (*destp == cl && !in_quote)
- in_brace--;
- else if (*destp == quote_chr)
- in_quote ^= TRUE;
-
- if (in_brace == 0)
- break;
-
- destp++;
- }
- }
- *destp = '\0';
-}
-
void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
gchar op, gchar cl)
{
void eliminate_quote(gchar *str, gchar quote_chr)
{
- register gchar *srcp, *destp;
+ register guchar *srcp, *destp;
srcp = destp = str;
void eliminate_address_comment(gchar *str)
{
- register gchar *srcp, *destp;
+ register guchar *srcp, *destp;
gint in_brace;
srcp = destp = str;
g_strstrip(str);
}
+void extract_list_id_str(gchar *str)
+{
+ if (strchr_with_skip_quote(str, '"', '<'))
+ extract_parenthesis_with_skip_quote(str, '"', '<', '>');
+ g_strstrip(str);
+}
+
static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
{
gchar *work;
return address_list_append_real(addr_list, str, FALSE);
}
-GSList *references_list_append(GSList *msgid_list, const gchar *str)
+GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
{
const gchar *strp;
msgid = g_strndup(start + 1, end - start - 1);
g_strstrip(msgid);
if (*msgid)
- msgid_list = g_slist_append(msgid_list, msgid);
+ msgid_list = g_slist_prepend(msgid_list, msgid);
else
g_free(msgid);
return msgid_list;
}
+GSList *references_list_append(GSList *msgid_list, const gchar *str)
+{
+ GSList *list;
+
+ list = references_list_prepend(NULL, str);
+ list = g_slist_reverse(list);
+ msgid_list = g_slist_concat(msgid_list, list);
+
+ return msgid_list;
+}
+
GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
{
gchar *work;
void remove_space(gchar *str)
{
- register gchar *p = str;
+ register guchar *p = str;
register gint spc;
while (*p) {
void unfold_line(gchar *str)
{
- register gchar *p = str;
+ register guchar *p = str;
register gint spc;
while (*p) {
void subst_for_filename(gchar *str)
{
- subst_chars(str, " \t\r\n\"/\\", '_');
+ if (!str)
+ return;
+ subst_chars(str, "\t\r\n\\/*", '_');
+}
+
+void subst_for_shellsafe_filename(gchar *str)
+{
+ if (!str)
+ return;
+ subst_for_filename(str);
+ subst_chars(str, " \"'|&;()<>'!{}[]",'_');
}
gboolean is_header_line(const gchar *str)
gboolean is_ascii_str(const guchar *str)
{
+ g_return_val_if_fail(str, FALSE);
+
while (*str != '\0') {
if (*str != '\t' && *str != ' ' &&
*str != '\r' && *str != '\n' &&
gint get_quote_level(const gchar *str, const gchar *quote_chars)
{
- const gchar *first_pos;
- const gchar *last_pos;
- const gchar *p = str;
+ const guchar *first_pos;
+ const guchar *last_pos;
+ const guchar *p = str;
gint quote_level = -1;
/* speed up line processing by only searching to the last '>' */
if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
/* skip a line if it contains a '<' before the initial '>' */
- if (memchr(str, '<', first_pos - str) != NULL)
+ if (memchr(str, '<', first_pos - (const guchar *)str) != NULL)
return -1;
last_pos = line_has_quote_char_last(first_pos, quote_chars);
} else
n++;
str = s_cl + 1;
- while (*str && isspace(*str)) str++;
+ while (*str && isspace(*(guchar *)str)) str++;
if (*str != op) {
string_list = g_slist_prepend(string_list,
g_strdup(""));
if (!str) return NULL;
if (strlen(str) <= len)
return g_strdup(str);
+ if (g_utf8_validate(str, -1, NULL) == FALSE)
+ return g_strdup(str);
while (*p != '\0') {
- mb_len = mblen(p, MB_LEN_MAX);
+ mb_len = g_utf8_skip[*(guchar *)p];
if (mb_len == 0)
break;
- else if (mb_len < 0)
- return g_strdup(str);
else if (new_len + mb_len > len)
break;
- else
- new_len += mb_len;
+
+ new_len += mb_len;
p += mb_len;
}
{
GList *result = NULL;
const gchar *p, *q;
- gchar *file;
+ gchar *escaped_utf8uri;
p = uri_list;
if (*p != '#') {
while (isspace(*p)) p++;
if (!strncmp(p, "file:", 5)) {
- p += 5;
q = p;
+ q += 5;
while (*q && *q != '\n' && *q != '\r') q++;
if (q > p) {
+ gchar *file, *locale_file = NULL;
q--;
while (q > p && isspace(*q)) q--;
- file = g_malloc(q - p + 2);
- strncpy(file, p, q - p + 1);
- file[q - p + 1] = '\0';
- result = g_list_append(result,file);
+ Xalloca(escaped_utf8uri, q - p + 2,
+ return result);
+ Xalloca(file, q - p + 2,
+ return result);
+ *file = '\0';
+ strncpy(escaped_utf8uri, p, q - p + 1);
+ escaped_utf8uri[q - p + 1] = '\0';
+ decode_uri(file, escaped_utf8uri);
+ /*
+ * g_filename_from_uri() rejects escaped/locale encoded uri
+ * string which come from Nautilus.
+ */
+ if (g_utf8_validate(file, -1, NULL))
+ locale_file
+ = conv_codeset_strdup(
+ file + 5,
+ CS_UTF_8,
+ conv_get_locale_charset_str());
+ if (!locale_file)
+ locale_file = g_strdup(file + 5);
+ result = g_list_append(result, locale_file);
}
}
}
return result;
}
-#define HEX_TO_INT(val, hex) \
-{ \
- gchar c = hex; \
- \
- if ('0' <= c && c <= '9') { \
- val = c - '0'; \
- } else if ('a' <= c && c <= 'f') { \
- val = c - 'a' + 10; \
- } else if ('A' <= c && c <= 'F') { \
- val = c - 'A' + 10; \
- } else { \
- val = 0; \
- } \
+/* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
+ * characters
+ */
+static gint axtoi(const gchar *hexstr)
+{
+ gint hi, lo, result;
+
+ hi = hexstr[0];
+ if ('0' <= hi && hi <= '9') {
+ hi -= '0';
+ } else
+ if ('a' <= hi && hi <= 'f') {
+ hi -= ('a' - 10);
+ } else
+ if ('A' <= hi && hi <= 'F') {
+ hi -= ('A' - 10);
+ }
+
+ lo = hexstr[1];
+ if ('0' <= lo && lo <= '9') {
+ lo -= '0';
+ } else
+ if ('a' <= lo && lo <= 'f') {
+ lo -= ('a'-10);
+ } else
+ if ('A' <= lo && lo <= 'F') {
+ lo -= ('A' - 10);
+ }
+ result = lo + (16 * hi);
+ return result;
+}
+
+gboolean is_uri_string(const gchar *str)
+{
+ return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
+ g_ascii_strncasecmp(str, "https://", 8) == 0 ||
+ g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
+ g_ascii_strncasecmp(str, "www.", 4) == 0);
+}
+
+gchar *get_uri_path(const gchar *uri)
+{
+ if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
+ return (gchar *)(uri + 7);
+ else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
+ return (gchar *)(uri + 8);
+ else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
+ return (gchar *)(uri + 6);
+ else
+ return (gchar *)uri;
+}
+
+gint get_uri_len(const gchar *str)
+{
+ const gchar *p;
+
+ if (is_uri_string(str)) {
+ for (p = str; *p != '\0'; p++) {
+ if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
+ break;
+ }
+ return p - str;
+ }
+
+ return 0;
+}
+
+/* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
+ * plusses, and escape characters are used)
+ */
+void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
+{
+ gchar *dec = decoded_uri;
+ const gchar *enc = encoded_uri;
+
+ while (*enc) {
+ if (*enc == '%') {
+ enc++;
+ if (isxdigit((guchar)enc[0]) &&
+ isxdigit((guchar)enc[1])) {
+ *dec = axtoi(enc);
+ dec++;
+ enc += 2;
+ }
+ } else {
+ if (*enc == '+')
+ *dec = ' ';
+ else
+ *dec = *enc;
+ dec++;
+ enc++;
+ }
+ }
+
+ *dec = '\0';
}
gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
if (*value == '\0') continue;
- if (cc && !*cc && !g_strcasecmp(field, "cc")) {
+ if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) {
*cc = g_strdup(value);
- } else if (bcc && !*bcc && !g_strcasecmp(field, "bcc")) {
+ } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) {
*bcc = g_strdup(value);
} else if (subject && !*subject &&
- !g_strcasecmp(field, "subject")) {
+ !g_ascii_strcasecmp(field, "subject")) {
*subject = g_malloc(strlen(value) + 1);
decode_uri(*subject, value);
- } else if (body && !*body && !g_strcasecmp(field, "body")) {
+ } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
*body = g_malloc(strlen(value) + 1);
decode_uri(*body, value);
}
* but as long as we are not able to do our own extensions to glibc, we do
* it here.
*/
-gchar *get_home_dir(void)
+const gchar *get_home_dir(void)
{
#if HAVE_DOSISH_SYSTEM
static gchar *home_dir;
#endif
}
-gchar *get_rc_dir(void)
+const gchar *get_rc_dir(void)
{
static gchar *rc_dir = NULL;
return rc_dir;
}
-gchar *get_news_cache_dir(void)
+const gchar *get_news_cache_dir(void)
{
static gchar *news_cache_dir = NULL;
return news_cache_dir;
}
-gchar *get_imap_cache_dir(void)
+const gchar *get_imap_cache_dir(void)
{
static gchar *imap_cache_dir = NULL;
return imap_cache_dir;
}
-gchar *get_mbox_cache_dir(void)
+const gchar *get_mbox_cache_dir(void)
{
static gchar *mbox_cache_dir = NULL;
return mbox_cache_dir;
}
-gchar *get_mime_tmp_dir(void)
+const gchar *get_mime_tmp_dir(void)
{
static gchar *mime_tmp_dir = NULL;
return mime_tmp_dir;
}
-gchar *get_template_dir(void)
+const gchar *get_template_dir(void)
{
static gchar *template_dir = NULL;
return template_dir;
}
-gchar *get_header_cache_dir(void)
+const gchar *get_header_cache_dir(void)
{
static gchar *header_dir = NULL;
return header_dir;
}
-gchar *get_tmp_dir(void)
+const gchar *get_tmp_dir(void)
{
static gchar *tmp_dir = NULL;
return tmp_file;
}
-gchar *get_domain_name(void)
+const gchar *get_domain_name(void)
{
static gchar *domain_name = NULL;
if (!domain_name) {
- gchar buf[128] = "";
struct hostent *hp;
+ struct utsname uts;
- if (gethostname(buf, sizeof(buf)) < 0) {
+ if (uname(&uts) < 0) {
perror("gethostname");
domain_name = "unknown";
} else {
- buf[sizeof(buf) - 1] = '\0';
- if ((hp = my_gethostbyname(buf)) == NULL) {
+ if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
perror("gethostbyname");
- domain_name = g_strdup(buf);
+ domain_name = g_strdup(uts.nodename);
} else {
domain_name = g_strdup(hp->h_name);
}
return TRUE;
}
+gboolean dirent_is_regular_file(struct dirent *d)
+{
+ struct stat s;
+
+#ifdef HAVE_DIRENT_D_TYPE
+ if (d->d_type == DT_REG)
+ return TRUE;
+ else if (d->d_type != DT_UNKNOWN)
+ return FALSE;
+#endif
+
+ return (stat(d->d_name, &s) == 0 && S_ISREG(s.st_mode));
+}
+
+gboolean dirent_is_directory(struct dirent *d)
+{
+ struct stat s;
+
+#ifdef HAVE_DIRENT_D_TYPE
+ if (d->d_type == DT_DIR)
+ return TRUE;
+ else if (d->d_type != DT_UNKNOWN)
+ return FALSE;
+#endif
+
+ return (stat(d->d_name, &s) == 0 && S_ISDIR(s.st_mode));
+}
+
gint change_dir(const gchar *dir)
{
gchar *prevdir = NULL;
DIR *dp;
struct dirent *d;
gchar *prev_dir;
- gint fileno;
+ gint file_no;
prev_dir = g_get_current_dir();
}
while ((d = readdir(dp)) != NULL) {
- fileno = to_number(d->d_name);
- if (fileno >= 0 && first <= fileno && fileno <= last) {
+ file_no = to_number(d->d_name);
+ if (file_no > 0 && first <= file_no && file_no <= last) {
if (is_dir_exist(d->d_name))
continue;
if (unlink(d->d_name) < 0)
DIR *dp;
struct dirent *d;
gchar *prev_dir;
- gint fileno;
+ gint file_no;
prev_dir = g_get_current_dir();
}
while ((d = readdir(dp)) != NULL) {
- fileno = to_number(d->d_name);
- if (fileno >= 0 && (g_slist_find(numberlist, GINT_TO_POINTER(fileno)) == NULL)) {
- debug_print("removing unwanted file %d from %s\n", fileno, dir);
+ file_no = to_number(d->d_name);
+ if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
+ debug_print("removing unwanted file %d from %s\n", file_no, dir);
if (is_dir_exist(d->d_name))
continue;
if (unlink(d->d_name) < 0)
struct dirent *d;
struct stat s;
gchar *prev_dir;
- gint fileno;
+ gint file_no;
time_t mtime, now, expire_time;
prev_dir = g_get_current_dir();
expire_time = hours * 60 * 60;
while ((d = readdir(dp)) != NULL) {
- fileno = to_number(d->d_name);
- if (fileno >= 0) {
+ file_no = to_number(d->d_name);
+ if (file_no > 0) {
if (stat(d->d_name, &s) < 0) {
FILE_OP_ERROR(d->d_name, "stat");
continue;
!strcmp(d->d_name, ".."))
continue;
- if (stat(d->d_name, &s) < 0) {
- FILE_OP_ERROR(d->d_name, "stat");
- continue;
- }
-
/* g_print("removing %s\n", d->d_name); */
- if (S_ISDIR(s.st_mode)) {
+ if (dirent_is_directory(d)) {
if (remove_dir_recursive(d->d_name) < 0) {
g_warning("can't remove directory\n");
return -1;
while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
if (n_read < sizeof(buf) && ferror(src_fp))
break;
- if (fwrite(buf, n_read, 1, dest_fp) < 1) {
+ if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
g_warning("writing to %s failed.\n", dest);
fclose(dest_fp);
fclose(src_fp);
while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
if (n_read < sizeof(buf) && ferror(src_fp))
break;
- if (fwrite(buf, n_read, 1, dest_fp) < 1) {
+ if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
g_warning("writing to %s failed.\n", dest);
fclose(dest_fp);
fclose(src_fp);
return 0;
}
-gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
+gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
{
- FILE *dest_fp;
gint n_read;
gint bytes_left, to_read;
gchar buf[BUFSIZ];
- gboolean err = FALSE;
if (fseek(fp, offset, SEEK_SET) < 0) {
perror("fseek");
return -1;
}
- if ((dest_fp = fopen(dest, "wb")) == NULL) {
- FILE_OP_ERROR(dest, "fopen");
- return -1;
- }
-
- if (change_file_mode_rw(dest_fp, dest) < 0) {
- FILE_OP_ERROR(dest, "chmod");
- g_warning("can't change file mode\n");
- }
-
bytes_left = length;
to_read = MIN(bytes_left, sizeof(buf));
while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
if (n_read < to_read && ferror(fp))
break;
- if (fwrite(buf, n_read, 1, dest_fp) < 1) {
- g_warning("writing to %s failed.\n", dest);
- fclose(dest_fp);
- unlink(dest);
+ if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
return -1;
}
bytes_left -= n_read;
if (ferror(fp)) {
perror("fread");
- err = TRUE;
+ return -1;
}
- if (fclose(dest_fp) == EOF) {
+
+ return 0;
+}
+
+gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
+{
+ FILE *dest_fp;
+ gboolean err = FALSE;
+
+ if ((dest_fp = fopen(dest, "wb")) == NULL) {
+ FILE_OP_ERROR(dest, "fopen");
+ return -1;
+ }
+
+ if (change_file_mode_rw(dest_fp, dest) < 0) {
+ FILE_OP_ERROR(dest, "chmod");
+ g_warning("can't change file mode\n");
+ }
+
+ if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
+ err = TRUE;
+
+ if (!err && fclose(dest_fp) == EOF) {
FILE_OP_ERROR(dest, "fclose");
err = TRUE;
}
if (err) {
+ g_warning("writing to %s failed.\n", dest);
unlink(dest);
return -1;
}
r = fputs(buf, dest_fp);
} else {
if (len > 1) {
- r = fwrite(buf, len - 1, 1, dest_fp);
- if (r != 1)
+ r = fwrite(buf, 1, len - 1, dest_fp);
+ if (r != (len -1))
r = EOF;
}
if (r != EOF)
return 0;
}
-gchar *get_outgoing_rfc2822_str(FILE *fp)
+gchar *normalize_newlines(const gchar *str)
{
- gchar buf[BUFFSIZE];
- GString *str;
- gchar *ret;
+ const gchar *p = str;
+ gchar *out, *outp;
+
+ out = outp = g_malloc(strlen(str) + 1);
+ for (p = str; *p != '\0'; ++p) {
+ if (*p == '\r') {
+ if (*(p + 1) != '\n')
+ *outp++ = '\n';
+ } else
+ *outp++ = *p;
+ }
+
+ *outp = '\0';
+
+ return out;
+}
+
+gchar *get_outgoing_rfc2822_str(FILE *fp)
+{
+ gchar buf[BUFFSIZE];
+ GString *str;
+ gchar *ret;
str = g_string_new(NULL);
/* output header part */
while (fgets(buf, sizeof(buf), fp) != NULL) {
strretchomp(buf);
- if (!g_strncasecmp(buf, "Bcc:", 4)) {
+ if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
gint next;
for (;;) {
return ret;
}
+/*
+ * Create a new boundary in a way that it is very unlikely that this
+ * will occur in the following text. It would be easy to ensure
+ * uniqueness if everything is either quoted-printable or base64
+ * encoded (note that conversion is allowed), but because MIME bodies
+ * may be nested, it may happen that the same boundary has already
+ * been used. We avoid scanning the message for conflicts and hope the
+ * best.
+ *
+ * boundary := 0*69<bchars> bcharsnospace
+ * bchars := bcharsnospace / " "
+ * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
+ * "+" / "_" / "," / "-" / "." /
+ * "/" / ":" / "=" / "?"
+ *
+ * some special characters removed because of buggy MTAs
+ */
+
+gchar *generate_mime_boundary(const gchar *prefix)
+{
+ static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "1234567890+_./=";
+ gchar buf_uniq[17];
+ gchar buf_date[64];
+ gint i;
+ gint pid;
+
+ pid = getpid();
+
+ /* We make the boundary depend on the pid, so that all running
+ * processes generate different values even when they have been
+ * started within the same second and srandom(time(NULL)) has been
+ * used. I can't see whether this is really an advantage but it
+ * doesn't do any harm.
+ */
+ for (i = 0; i < sizeof(buf_uniq) - 1; i++)
+ buf_uniq[i] = tbl[(rand() ^ pid) % (sizeof(tbl) - 1)];
+ buf_uniq[i] = '\0';
+
+ get_rfc822_date(buf_date, sizeof(buf_date));
+ subst_char(buf_date, ' ', '_');
+ subst_char(buf_date, ',', '_');
+ subst_char(buf_date, ':', '_');
+
+ return g_strdup_printf("%s_%s_%s", prefix ? prefix : "Multipart",
+ buf_date, buf_uniq);
+}
+
gint change_file_mode_rw(FILE *fp, const gchar *file)
{
#if HAVE_FCHMOD
tmpdir = get_tmp_dir();
tmplen = strlen(tmpdir);
progname = g_get_prgname();
+ if (progname == NULL)
+ progname = "sylpheed-claws";
proglen = strlen(progname);
Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
return tmpfile());
return tmpfile();
}
+FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
+{
+ int fd;
+
+ *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
+ fd = mkstemp(*filename);
+
+ return fdopen(fd, "w+");
+}
+
FILE *str_open_as_stream(const gchar *str)
{
FILE *fp;
len = strlen(str);
if (len == 0) return fp;
- if (fwrite(str, len, 1, fp) != 1) {
+ if (fwrite(str, 1, len, fp) != len) {
FILE_OP_ERROR("str_open_as_stream", "fwrite");
fclose(fp);
return NULL;
return 0;
}
- if (fwrite(str, len, 1, fp) != 1) {
+ if (fwrite(str, 1, len, fp) != len) {
FILE_OP_ERROR(file, "fwrite");
fclose(fp);
unlink(file);
str = (gchar *)array->data;
g_byte_array_free(array, FALSE);
+ if (!g_utf8_validate(str, -1, NULL)) {
+ const gchar *src_codeset, *dest_codeset;
+ gchar *tmp = NULL;
+ src_codeset = conv_get_locale_charset_str();
+ dest_codeset = CS_UTF_8;
+ tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
+ g_free(str);
+ str = tmp;
+ }
+
return str;
}
gint execute_async(gchar *const argv[])
{
pid_t pid;
+ gint status;
if ((pid = fork()) < 0) {
perror("fork");
_exit(0);
}
- waitpid(pid, NULL, 0);
+ waitpid(pid, &status, 0);
- return 0;
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ else
+ return -1;
}
gint execute_sync(gchar *const argv[])
{
pid_t pid;
+ gint status;
if ((pid = fork()) < 0) {
perror("fork");
_exit(1);
}
- waitpid(pid, NULL, 0);
+ waitpid(pid, &status, 0);
- return 0;
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ else
+ return -1;
}
gint execute_command_line(const gchar *cmdline, gboolean async)
gchar **argv;
gint ret;
+ debug_print("executing: %s\n", cmdline);
+
argv = strsplit_with_quote(cmdline, " ", 0);
if (async)
ret = execute_async(argv);
else
ret = execute_sync(argv);
+
g_strfreev(argv);
return ret;
}
+gchar *get_command_output(const gchar *cmdline)
+{
+ gchar *child_stdout;
+ gint status;
+
+ g_return_val_if_fail(cmdline != NULL, NULL);
+
+ if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
+ NULL) == FALSE) {
+ g_warning("Can't execute command: %s\n", cmdline);
+ return NULL;
+ }
+
+ return child_stdout;
+}
+
static gint is_unchanged_uri_char(char c)
{
switch (c) {
encoded_uri[k] = 0;
}
-/* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
- * characters
- */
-static gint axtoi(const gchar *hexstr)
-{
- gint hi, lo, result;
-
- hi = hexstr[0];
- if ('0' <= hi && hi <= '9') {
- hi -= '0';
- } else
- if ('a' <= hi && hi <= 'f') {
- hi -= ('a' - 10);
- } else
- if ('A' <= hi && hi <= 'F') {
- hi -= ('A' - 10);
- }
-
- lo = hexstr[1];
- if ('0' <= lo && lo <= '9') {
- lo -= '0';
- } else
- if ('a' <= lo && lo <= 'f') {
- lo -= ('a'-10);
- } else
- if ('A' <= lo && lo <= 'F') {
- lo -= ('A' - 10);
- }
- result = lo + (16 * hi);
- return result;
-}
-
-
-/* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
- * plusses, and escape characters are used)
- */
-
-void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
-{
- const gchar *encoded;
- gchar *decoded;
-
- encoded = encoded_uri;
- decoded = decoded_uri;
-
- while (*encoded) {
- if (*encoded == '%') {
- encoded++;
- if (isxdigit(encoded[0])
- && isxdigit(encoded[1])) {
- *decoded = (gchar) axtoi(encoded);
- decoded++;
- encoded += 2;
- }
- }
- else if (*encoded == '+') {
- *decoded = ' ';
- decoded++;
- encoded++;
- }
- else {
- *decoded = *encoded;
- decoded++;
- encoded++;
- }
- }
-
- *decoded = '\0';
-}
-
-
gint open_uri(const gchar *uri, const gchar *cmdline)
{
gchar buf[BUFFSIZE];
g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
else {
if (cmdline)
- g_warning("Open URI command line is invalid: `%s'",
+ g_warning("Open URI command line is invalid "
+ "(there must be only one '%%s'): %s",
cmdline);
g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
}
-
+
execute_command_line(buf, TRUE);
return 0;
} else if (!strncmp(zone, "UT" , 2) ||
!strncmp(zone, "GMT", 2)) {
remoteoffset = 0;
- } else if (strlen(zone3) == 3 &&
- (p = strstr(ustzstr, zone3)) != NULL &&
- (p - ustzstr) % 3 == 0) {
- iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
- remoteoffset = iustz * 3600;
+ } else if (strlen(zone3) == 3) {
+ for (p = ustzstr; *p != '\0'; p += 3) {
+ if (!g_ascii_strncasecmp(p, zone3, 3)) {
+ iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
+ remoteoffset = iustz * 3600;
+ break;
+ }
+ }
+ if (*p == '\0')
+ return -1;
} else if (strlen(zone3) == 1) {
switch (zone[0]) {
case 'Z': remoteoffset = 0; break;
default: remoteoffset = 0; break;
}
remoteoffset = remoteoffset * 3600;
- }
+ } else
+ return -1;
return remoteoffset;
}
debug_mode = mode;
}
-gboolean debug_get_mode()
+gboolean debug_get_mode(void)
{
return debug_mode;
}
g_vsnprintf(buf, sizeof(buf), format, args);
va_end(args);
- fputs(buf, stdout);
+ g_print("%s", buf);
}
void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
{
if (subject == NULL)
subject = "";
-
- if (g_strncasecmp(subject, "Re: ", 4) == 0)
- return g_hash_table_lookup(subject_table, subject + 4);
else
- return g_hash_table_lookup(subject_table, subject);
+ subject += subject_get_prefix_length(subject);
+
+ return g_hash_table_lookup(subject_table, subject);
}
void subject_table_insert(GHashTable *subject_table, gchar * subject,
void * data)
{
- if (subject == NULL)
+ if (subject == NULL || *subject == 0)
return;
- if (* subject == 0)
- return;
- if (g_strcasecmp(subject, "Re:") == 0)
- return;
- if (g_strcasecmp(subject, "Re: ") == 0)
- return;
-
- if (g_strncasecmp(subject, "Re: ", 4) == 0)
- g_hash_table_insert(subject_table, subject + 4, data);
- else
- g_hash_table_insert(subject_table, subject, data);
+ subject += subject_get_prefix_length(subject);
+ g_hash_table_insert(subject_table, subject, data);
}
void subject_table_remove(GHashTable *subject_table, gchar * subject)
if (subject == NULL)
return;
- if (g_strncasecmp(subject, "Re: ", 4) == 0)
- g_hash_table_remove(subject_table, subject + 4);
+ subject += subject_get_prefix_length(subject);
+ g_hash_table_remove(subject_table, subject);
+}
+
+/*!
+ *\brief Check if a string is prefixed with known (combinations)
+ * of prefixes. The function assumes that each prefix
+ * is terminated by zero or exactly _one_ space.
+ *
+ *\param str String to check for a prefixes
+ *
+ *\return int Number of chars in the prefix that should be skipped
+ * for a "clean" subject line. If no prefix was found, 0
+ * is returned.
+ */
+int subject_get_prefix_length(const gchar *subject)
+{
+ /*!< Array with allowable reply prefixes regexps. */
+ static const gchar * const prefixes[] = {
+ "Re\\:", /* "Re:" */
+ "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
+ "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
+ "Aw\\:", /* "Aw:" (German) */
+ "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
+ "Res\\:", /* "Res:" (Brazilian Outlook) */
+ "Fw\\:", /* "Fw:" Forward */
+ "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
+ "Odp\\:", /* "Odp:" Re (Polish Outlook) */
+ "Rif\\:" /* "Rif:" (Italian Outlook) */
+ /* add more */
+ };
+ const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
+ int n;
+ regmatch_t pos;
+ static regex_t regex;
+ static gboolean init_;
+
+ if (!subject) return 0;
+ if (!*subject) return 0;
+
+ if (!init_) {
+ GString *s = g_string_new("");
+
+ for (n = 0; n < PREFIXES; n++)
+ /* Terminate each prefix regexpression by a
+ * "\ ?" (zero or ONE space), and OR them */
+ g_string_append_printf(s, "(%s\\ ?)%s",
+ prefixes[n],
+ n < PREFIXES - 1 ?
+ "|" : "");
+
+ g_string_prepend(s, "(");
+ g_string_append(s, ")+"); /* match at least once */
+ g_string_prepend(s, "^\\ *"); /* from beginning of line */
+
+
+ /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
+ * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
+ if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
+ debug_print("Error compiling regexp %s\n", s->str);
+ g_string_free(s, TRUE);
+ return 0;
+ } else {
+ init_ = TRUE;
+ g_string_free(s, TRUE);
+ }
+ }
+
+ if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
+ return pos.rm_eo;
else
- g_hash_table_remove(subject_table, subject);
+ return 0;
+}
+
+guint g_stricase_hash(gconstpointer gptr)
+{
+ guint hash_result = 0;
+ const char *str;
+
+ for (str = gptr; str && *str; str++) {
+ if (isupper((guchar)*str)) hash_result += (*str + ' ');
+ else hash_result += *str;
+ }
+
+ return hash_result;
}
-gboolean subject_is_reply(const gchar *subject)
+gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
{
- /* XXX: just simply here so someone can handle really
- * advanced Re: detection like "Re[4]", "ANTW:" or
- * Re: Re: Re: Re: Re: Re: Re: Re:" stuff. */
- if (subject == NULL) return FALSE;
- else return 0 == g_strncasecmp(subject, "Re: ", 4);
+ const char *str1 = gptr1;
+ const char *str2 = gptr2;
+
+ return !g_utf8_collate(str1, str2);
}
-FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
+gint g_int_compare(gconstpointer a, gconstpointer b)
{
- int fd;
-
- *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
- fd = mkstemp(*filename);
+ return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
+}
- return fdopen(fd, "w+");
+gchar *generate_msgid(gchar *buf, gint len)
+{
+ struct tm *lt;
+ time_t t;
+ gchar *addr;
+
+ t = time(NULL);
+ lt = localtime(&t);
+
+ addr = g_strconcat("@", get_domain_name(), NULL);
+
+ g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
+ lt->tm_year + 1900, lt->tm_mon + 1,
+ lt->tm_mday, lt->tm_hour,
+ lt->tm_min, lt->tm_sec,
+ (guint) rand(), addr);
+
+ g_free(addr);
+ return buf;
}
-/* allow Mutt-like patterns in quick search */
-gchar *expand_search_string(const gchar *search_string)
-{
- int i = 0;
- gchar term_char, save_char;
- gchar *cmd_start, *cmd_end;
- GString *matcherstr;
- gchar *returnstr = NULL;
- gchar *copy_str;
- gboolean casesens, dontmatch;
- /* list of allowed pattern abbreviations */
- struct {
- gchar *abbreviated; /* abbreviation */
- gchar *command; /* actual matcher command */
- gint numparams; /* number of params for cmd */
- gboolean qualifier; /* do we append regexpcase */
- gboolean quotes; /* do we need quotes */
- }
- cmds[] = {
- { "a", "all", 0, FALSE, FALSE },
- { "ag", "age_greater", 1, FALSE, FALSE },
- { "al", "age_lower", 1, FALSE, FALSE },
- { "b", "body_part", 1, TRUE, TRUE },
- { "B", "message", 1, TRUE, TRUE },
- { "c", "cc", 1, TRUE, TRUE },
- { "C", "to_or_cc", 1, TRUE, TRUE },
- { "D", "deleted", 0, FALSE, FALSE },
- { "e", "header \"Sender\"", 1, TRUE, TRUE },
- { "E", "execute", 1, FALSE, TRUE },
- { "f", "from", 1, TRUE, TRUE },
- { "F", "forwarded", 0, FALSE, FALSE },
- { "h", "headers_part", 1, TRUE, TRUE },
- { "i", "header \"Message-Id\"", 1, TRUE, TRUE },
- { "I", "inreplyto", 1, TRUE, TRUE },
- { "L", "locked", 0, FALSE, FALSE },
- { "n", "newsgroups", 1, TRUE, TRUE },
- { "N", "new", 0, FALSE, FALSE },
- { "O", "~new", 0, FALSE, FALSE },
- { "r", "replied", 0, FALSE, FALSE },
- { "R", "~unread", 0, FALSE, FALSE },
- { "s", "subject", 1, TRUE, TRUE },
- { "se", "score_equal", 1, FALSE, FALSE },
- { "sg", "score_greater", 1, FALSE, FALSE },
- { "sl", "score_lower", 1, FALSE, FALSE },
- { "Se", "size_equal", 1, FALSE, FALSE },
- { "Sg", "size_greater", 1, FALSE, FALSE },
- { "Ss", "size_smaller", 1, FALSE, FALSE },
- { "t", "to", 1, TRUE, TRUE },
- { "T", "marked", 0, FALSE, FALSE },
- { "U", "unread", 0, FALSE, FALSE },
- { "x", "header \"References\"", 1, TRUE, TRUE },
- { "y", "header \"X-Label\"", 1, TRUE, TRUE },
- { "&", "&", 0, FALSE, FALSE },
- { "|", "|", 0, FALSE, FALSE },
- { NULL, NULL, 0, FALSE, FALSE }
- };
+/*
+ quote_cmd_argument()
+
+ return a quoted string safely usable in argument of a command.
+
+ code is extracted and adapted from etPan! project -- DINH V. HoĆ .
+*/
+
+gint quote_cmd_argument(gchar * result, guint size,
+ const gchar * path)
+{
+ const gchar * p;
+ gchar * result_p;
+ guint remaining;
+
+ result_p = result;
+ remaining = size;
+
+ for(p = path ; * p != '\0' ; p ++) {
+
+ if (isalnum((guchar)*p) || (* p == '/')) {
+ if (remaining > 0) {
+ * result_p = * p;
+ result_p ++;
+ remaining --;
+ }
+ else {
+ result[size - 1] = '\0';
+ return -1;
+ }
+ }
+ else {
+ if (remaining >= 2) {
+ * result_p = '\\';
+ result_p ++;
+ * result_p = * p;
+ result_p ++;
+ remaining -= 2;
+ }
+ else {
+ result[size - 1] = '\0';
+ return -1;
+ }
+ }
+ }
+ if (remaining > 0) {
+ * result_p = '\0';
+ }
+ else {
+ result[size - 1] = '\0';
+ return -1;
+ }
+
+ return 0;
+}
- if (search_string == NULL)
- return NULL;
+typedef struct
+{
+ GNode *parent;
+ GNodeMapFunc func;
+ gpointer data;
+} GNodeMapData;
- copy_str = g_strdup(search_string);
-
- /* if it's a full command don't process it so users
- can still do something like from regexpcase "foo" */
- for (i = 0; cmds[i].command; i++) {
- const gchar *tmp_search_string = search_string;
- cmd_start = cmds[i].command;
- /* allow logical NOT */
- if (*tmp_search_string == '~')
- tmp_search_string++;
- if (!strncmp(tmp_search_string, cmd_start, strlen(cmd_start)))
- break;
+static void g_node_map_recursive(GNode *node, gpointer data)
+{
+ GNodeMapData *mapdata = (GNodeMapData *) data;
+ GNode *newnode;
+ GNodeMapData newmapdata;
+ gpointer newdata;
+
+ newdata = mapdata->func(node->data, mapdata->data);
+ if (newdata != NULL) {
+ newnode = g_node_new(newdata);
+ g_node_append(mapdata->parent, newnode);
+
+ newmapdata.parent = newnode;
+ newmapdata.func = mapdata->func;
+ newmapdata.data = mapdata->data;
+
+ g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
}
- if (cmds[i].command)
- return copy_str;
+}
- matcherstr = g_string_sized_new(16);
- cmd_start = cmd_end = copy_str;
- while (cmd_end && *cmd_end) {
- /* skip all white spaces */
- while (*cmd_end && isspace(*cmd_end))
- cmd_end++;
+GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
+{
+ GNode *root;
+ GNodeMapData mapdata;
- /* extract a command */
- while (*cmd_end && !isspace(*cmd_end))
- cmd_end++;
+ g_return_val_if_fail(node != NULL, NULL);
+ g_return_val_if_fail(func != NULL, NULL);
- /* save character */
- save_char = *cmd_end;
- *cmd_end = '\0';
+ root = g_node_new(func(node->data, data));
- dontmatch = FALSE;
- casesens = FALSE;
+ mapdata.parent = root;
+ mapdata.func = func;
+ mapdata.data = data;
- /* ~ and ! mean logical NOT */
- if (*cmd_start == '~' || *cmd_start == '!')
- {
- dontmatch = TRUE;
- cmd_start++;
- }
- /* % means case sensitive match */
- if (*cmd_start == '%')
- {
- casesens = TRUE;
- cmd_start++;
- }
+ g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
- /* find matching abbreviation */
- for (i = 0; cmds[i].command; i++) {
- if (!strcmp(cmd_start, cmds[i].abbreviated)) {
- /* restore character */
- *cmd_end = save_char;
+ return root;
+}
- /* copy command */
- if (matcherstr->len > 0) {
- g_string_append(matcherstr, " ");
- }
- if (dontmatch)
- g_string_append(matcherstr, "~");
- g_string_append(matcherstr, cmds[i].command);
- g_string_append(matcherstr, " ");
+#define HEX_TO_INT(val, hex) \
+{ \
+ gchar c = hex; \
+ \
+ if ('0' <= c && c <= '9') { \
+ val = c - '0'; \
+ } else if ('a' <= c && c <= 'f') { \
+ val = c - 'a' + 10; \
+ } else if ('A' <= c && c <= 'F') { \
+ val = c - 'A' + 10; \
+ } else { \
+ val = -1; \
+ } \
+}
- /* stop if no params required */
- if (cmds[i].numparams == 0)
- break;
+gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
+{
+ gint hi, lo;
- /* extract a parameter, allow quotes */
- cmd_end++;
- cmd_start = cmd_end;
- if (*cmd_start == '"') {
- term_char = '"';
- cmd_end++;
- }
- else
- term_char = ' ';
+ HEX_TO_INT(hi, c1);
+ HEX_TO_INT(lo, c2);
- /* extract actual parameter */
- while ((*cmd_end) && (*cmd_end != term_char))
- cmd_end++;
+ if (hi == -1 || lo == -1)
+ return FALSE;
- if (*cmd_end && (*cmd_end != term_char))
- break;
+ *out = (hi << 4) + lo;
+ return TRUE;
+}
- if (*cmd_end == '"')
- cmd_end++;
+#define INT_TO_HEX(hex, val) \
+{ \
+ if ((val) < 10) \
+ hex = '0' + (val); \
+ else \
+ hex = 'A' + (val) - 10; \
+}
- save_char = *cmd_end;
- *cmd_end = '\0';
+void get_hex_str(gchar *out, guchar ch)
+{
+ gchar hex;
- if (cmds[i].qualifier) {
- if (casesens)
- g_string_append(matcherstr, "regexp ");
- else
- g_string_append(matcherstr, "regexpcase ");
- }
+ INT_TO_HEX(hex, ch >> 4);
+ *out++ = hex;
+ INT_TO_HEX(hex, ch & 0x0f);
+ *out++ = hex;
+}
- /* do we need to add quotes ? */
- if (cmds[i].quotes && term_char != '"')
- g_string_append(matcherstr, "\"");
+#undef REF_DEBUG
+#ifndef REF_DEBUG
+#define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
+#else
+#define G_PRINT_REF g_print
+#endif
- /* copy actual parameter */
- g_string_append(matcherstr, cmd_start);
+/*!
+ *\brief Register ref counted pointer. It is based on GBoxed, so should
+ * work with anything that uses the GType system. The semantics
+ * are similar to a C++ auto pointer, with the exception that
+ * C doesn't have automatic closure (calling destructors) when
+ * exiting a block scope.
+ * Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
+ * function directly.
+ *
+ *\return GType A GType type.
+ */
+GType g_auto_pointer_register(void)
+{
+ static GType auto_pointer_type;
+ if (!auto_pointer_type)
+ auto_pointer_type =
+ g_boxed_type_register_static
+ ("G_TYPE_AUTO_POINTER",
+ (GBoxedCopyFunc) g_auto_pointer_copy,
+ (GBoxedFreeFunc) g_auto_pointer_free);
+ return auto_pointer_type;
+}
+
+/*!
+ *\brief Structure with g_new() allocated pointer guarded by the
+ * auto pointer
+ */
+typedef struct AutoPointerRef {
+ void (*free) (gpointer);
+ gpointer pointer;
+ glong cnt;
+} AutoPointerRef;
+
+/*!
+ *\brief The auto pointer opaque structure that references the
+ * pointer guard block.
+ */
+typedef struct AutoPointer {
+ AutoPointerRef *ref;
+ gpointer ptr; /*!< access to protected pointer */
+} AutoPointer;
- /* do we need to add quotes ? */
- if (cmds[i].quotes && term_char != '"')
- g_string_append(matcherstr, "\"");
+/*!
+ *\brief Creates an auto pointer for a g_new()ed pointer. Example:
+ *
+ *\code
+ *
+ * ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
+ * ... when assigning, copying and freeing storage elements
+ *
+ * gtk_list_store_new(N_S_COLUMNS,
+ * G_TYPE_AUTO_POINTER,
+ * -1);
+ *
+ *
+ * Template *precious_data = g_new0(Template, 1);
+ * g_pointer protect = g_auto_pointer_new(precious_data);
+ *
+ * gtk_list_store_set(container, &iter,
+ * S_DATA, protect,
+ * -1);
+ *
+ * ... the gtk_list_store has copied the pointer and
+ * ... incremented its reference count, we should free
+ * ... the auto pointer (in C++ a destructor would do
+ * ... this for us when leaving block scope)
+ *
+ * g_auto_pointer_free(protect);
+ *
+ * ... gtk_list_store_set() now manages the data. When
+ * ... *explicitly* requesting a pointer from the list
+ * ... store, don't forget you get a copy that should be
+ * ... freed with g_auto_pointer_free() eventually.
+ *
+ *\endcode
+ *
+ *\param pointer Pointer to be guarded.
+ *
+ *\return GAuto * Pointer that should be used in containers with
+ * GType support.
+ */
+GAuto *g_auto_pointer_new(gpointer p)
+{
+ AutoPointerRef *ref;
+ AutoPointer *ptr;
+
+ if (p == NULL)
+ return NULL;
- /* restore original character */
- *cmd_end = save_char;
+ ref = g_new0(AutoPointerRef, 1);
+ ptr = g_new0(AutoPointer, 1);
- break;
- }
- }
+ ref->pointer = p;
+ ref->free = g_free;
+ ref->cnt = 1;
- if (*cmd_end) {
- cmd_end++;
- cmd_start = cmd_end;
- }
- }
+ ptr->ref = ref;
+ ptr->ptr = p;
- g_free(copy_str);
- returnstr = matcherstr->str;
- g_string_free(matcherstr, FALSE);
- return returnstr;
+#ifdef REF_DEBUG
+ G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
+#endif
+ return ptr;
}
-guint g_stricase_hash(gconstpointer gptr)
+/*!
+ *\brief Allocate an autopointer using the passed \a free function to
+ * free the guarded pointer
+ */
+GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
{
- guint hash_result = 0;
- const char *str;
+ AutoPointer *aptr;
+
+ if (p == NULL)
+ return NULL;
- for (str = gptr; str && *str; str++) {
- if (isupper(*str)) hash_result += (*str + ' ');
- else hash_result += *str;
- }
+ aptr = g_auto_pointer_new(p);
+ aptr->ref->free = free_;
+ return aptr;
+}
- return hash_result;
+gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
+{
+ if (auto_ptr == NULL)
+ return NULL;
+ return ((AutoPointer *) auto_ptr)->ptr;
}
-gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
+/*!
+ *\brief Copies an auto pointer by. It's mostly not necessary
+ * to call this function directly, unless you copy/assign
+ * the guarded pointer.
+ *
+ *\param auto_ptr Auto pointer returned by previous call to
+ * g_auto_pointer_new_XXX()
+ *
+ *\return gpointer An auto pointer
+ */
+GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
{
- const char *str1 = gptr1;
- const char *str2 = gptr2;
+ AutoPointer *ptr;
+ AutoPointerRef *ref;
+ AutoPointer *newp;
- return !strcasecmp(str1, str2);
+ if (auto_ptr == NULL)
+ return NULL;
+
+ ptr = auto_ptr;
+ ref = ptr->ref;
+ newp = g_new0(AutoPointer, 1);
+
+ newp->ref = ref;
+ newp->ptr = ref->pointer;
+ ++(ref->cnt);
+
+#ifdef REF_DEBUG
+ G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
+#endif
+ return newp;
}
-gint g_int_compare(gconstpointer a, gconstpointer b)
+/*!
+ *\brief Free an auto pointer
+ */
+void g_auto_pointer_free(GAuto *auto_ptr)
{
- return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
+ AutoPointer *ptr;
+ AutoPointerRef *ref;
+
+ if (auto_ptr == NULL)
+ return;
+
+ ptr = auto_ptr;
+ ref = ptr->ref;
+
+ if (--(ref->cnt) == 0) {
+#ifdef REF_DEBUG
+ G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
+#endif
+ ref->free(ref->pointer);
+ g_free(ref);
+ }
+#ifdef REF_DEBUG
+ else
+ G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
+#endif
+ g_free(ptr);
+}
+
+void replace_returns(gchar *str)
+{
+ if (!str)
+ return;
+
+ while (strstr(str, "\n")) {
+ *strstr(str, "\n") = ' ';
+ }
+ while (strstr(str, "\r")) {
+ *strstr(str, "\r") = ' ';
+ }
}