weechat/src/core/wee-string.c

1696 lines
43 KiB
C
Raw Normal View History

2007-10-31 12:11:00 -04:00
/*
2011-01-01 12:06:29 -05:00
* Copyright (C) 2003-2011 Sebastien Helleu <flashcode@flashtux.org>
2007-10-31 12:11:00 -04:00
*
* This file is part of WeeChat, the extensible chat client.
*
* WeeChat is free software; you can redistribute it and/or modify
2007-10-31 12:11:00 -04:00
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* WeeChat is distributed in the hope that it will be useful,
2007-10-31 12:11:00 -04:00
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WeeChat. If not, see <http://www.gnu.org/licenses/>.
2007-10-31 12:11:00 -04:00
*/
/*
* wee-string.c: string functions for WeeChat
*/
2007-10-31 12:11:00 -04:00
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <wctype.h>
#include <regex.h>
#if defined(__OpenBSD__)
#include <utf8/wchar.h>
#else
#include <wchar.h>
#endif
2007-10-31 12:11:00 -04:00
#ifdef HAVE_ICONV
#include <iconv.h>
#endif
#ifndef ICONV_CONST
#ifdef ICONV_2ARG_IS_CONST
#define ICONV_CONST const
#else
#define ICONV_CONST
#endif
#endif
#include "weechat.h"
#include "wee-string.h"
#include "wee-config.h"
#include "wee-hashtable.h"
2007-10-31 12:11:00 -04:00
#include "wee-utf8.h"
#include "../gui/gui-color.h"
#include "../plugins/plugin.h"
2007-10-31 12:11:00 -04:00
/*
* string_strndup: define strndup function for systems where this function does
* not exist (FreeBSD and maybe other)
2007-10-31 12:11:00 -04:00
*/
char *
string_strndup (const char *string, int length)
2007-10-31 12:11:00 -04:00
{
char *result;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
if ((int)strlen (string) < length)
return strdup (string);
2011-10-26 14:37:03 -04:00
result = malloc (length + 1);
2007-10-31 12:11:00 -04:00
if (!result)
return NULL;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
memcpy (result, string, length);
result[length] = '\0';
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
return result;
}
/*
* string_tolower: locale independant string conversion to lower case
*/
void
string_tolower (char *string)
{
while (string && string[0])
{
if ((string[0] >= 'A') && (string[0] <= 'Z'))
string[0] += ('a' - 'A');
string = utf8_next_char (string);
2007-10-31 12:11:00 -04:00
}
}
/*
* string_toupper: locale independant string conversion to upper case
*/
void
string_toupper (char *string)
{
while (string && string[0])
{
if ((string[0] >= 'a') && (string[0] <= 'z'))
string[0] -= ('a' - 'A');
string = utf8_next_char (string);
2007-10-31 12:11:00 -04:00
}
}
/*
* string_strcasecmp: locale and case independent string comparison
*/
int
string_strcasecmp (const char *string1, const char *string2)
2007-10-31 12:11:00 -04:00
{
int diff;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
if (!string1 || !string2)
return (string1) ? 1 : ((string2) ? -1 : 0);
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
while (string1[0] && string2[0])
{
diff = utf8_charcasecmp (string1, string2);
if (diff != 0)
return diff;
string1 = utf8_next_char (string1);
string2 = utf8_next_char (string2);
2007-10-31 12:11:00 -04:00
}
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
return (string1[0]) ? 1 : ((string2[0]) ? -1 : 0);
}
/*
* string_strncasecmp: locale and case independent string comparison
* with max length
*/
int
string_strncasecmp (const char *string1, const char *string2, int max)
2007-10-31 12:11:00 -04:00
{
int count, diff;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
if (!string1 || !string2)
return (string1) ? 1 : ((string2) ? -1 : 0);
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
count = 0;
while ((count < max) && string1[0] && string2[0])
{
diff = utf8_charcasecmp (string1, string2);
if (diff != 0)
return diff;
2011-10-26 14:37:03 -04:00
string1 = utf8_next_char (string1);
string2 = utf8_next_char (string2);
2007-10-31 12:11:00 -04:00
count++;
}
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
if (count >= max)
return 0;
else
return (string1[0]) ? 1 : ((string2[0]) ? -1 : 0);
}
/*
* string_strcmp_ignore_chars: compare 2 strings, ignoring ignore some chars
*/
int
string_strcmp_ignore_chars (const char *string1, const char *string2,
const char *chars_ignored, int case_sensitive)
{
int diff;
2011-10-26 14:37:03 -04:00
if (!string1 && !string2)
return 0;
if (!string1 && string2)
return -1;
if (string1 && !string2)
return 1;
2011-10-26 14:37:03 -04:00
while (string1 && string1[0] && string2 && string2[0])
{
/* skip ignored chars */
while (string1 && string1[0] && strchr (chars_ignored, string1[0]))
{
string1 = utf8_next_char (string1);
}
while (string2 && string2[0] && strchr (chars_ignored, string2[0]))
{
string2 = utf8_next_char (string2);
}
/* end of one (or both) string(s) ? */
if ((!string1 || !string1[0]) && (!string2 || !string2[0]))
return 0;
if ((!string1 || !string1[0]) && string2 && string2[0])
return -1;
if (string1 && string1[0] && (!string2 || !string2[0]))
return 1;
2011-10-26 14:37:03 -04:00
/* look at diff */
diff = (case_sensitive) ?
(int)string1[0] - (int)string2[0] : utf8_charcasecmp (string1, string2);
if (diff != 0)
return diff;
2011-10-26 14:37:03 -04:00
string1 = utf8_next_char (string1);
string2 = utf8_next_char (string2);
2011-10-26 14:37:03 -04:00
/* skip ignored chars */
while (string1 && string1[0] && strchr (chars_ignored, string1[0]))
{
string1 = utf8_next_char (string1);
}
while (string2 && string2[0] && strchr (chars_ignored, string2[0]))
{
string2 = utf8_next_char (string2);
}
}
if ((!string1 || !string1[0]) && string2 && string2[0])
return -1;
if (string1 && string1[0] && (!string2 || !string2[0]))
return 1;
return 0;
}
2007-10-31 12:11:00 -04:00
/*
* string_strcasestr: locale and case independent string search
*/
char *
string_strcasestr (const char *string, const char *search)
2007-10-31 12:11:00 -04:00
{
int length_search;
2011-10-26 14:37:03 -04:00
length_search = utf8_strlen (search);
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
if (!string || !search || (length_search == 0))
return NULL;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
while (string[0])
{
if (string_strncasecmp (string, search, length_search) == 0)
return (char *)string;
2011-10-26 14:37:03 -04:00
string = utf8_next_char (string);
2007-10-31 12:11:00 -04:00
}
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
return NULL;
}
/*
* string_match: return 1 if string matches a mask
* mask can begin or end with "*", no other "*" are allowed
* inside mask
*/
int
string_match (const char *string, const char *mask, int case_sensitive)
{
char last, *mask2;
int len_string, len_mask, rc;
2011-10-26 14:37:03 -04:00
if (!mask || !mask[0])
return 0;
2011-10-26 14:37:03 -04:00
/* if mask is "*", then any string matches */
if (strcmp (mask, "*") == 0)
return 1;
2011-10-26 14:37:03 -04:00
len_string = strlen (string);
len_mask = strlen (mask);
2011-10-26 14:37:03 -04:00
last = mask[len_mask - 1];
2011-10-26 14:37:03 -04:00
/* mask begins with "*" */
if ((mask[0] == '*') && (last != '*'))
{
/* not enough chars in string to match */
if (len_string < len_mask - 1)
return 0;
/* check if end of string matches */
if ((case_sensitive && (strcmp (string + len_string - (len_mask - 1),
mask + 1) == 0))
|| (!case_sensitive && (string_strcasecmp (string + len_string - (len_mask - 1),
mask + 1) == 0)))
return 1;
/* no match */
return 0;
}
2011-10-26 14:37:03 -04:00
/* mask ends with "*" */
if ((mask[0] != '*') && (last == '*'))
{
/* not enough chars in string to match */
if (len_string < len_mask - 1)
return 0;
/* check if beginning of string matches */
if ((case_sensitive && (strncmp (string, mask, len_mask - 1) == 0))
|| (!case_sensitive && (string_strncasecmp (string,
mask,
len_mask - 1) == 0)))
return 1;
/* no match */
return 0;
}
2011-10-26 14:37:03 -04:00
/* mask begins and ends with "*" */
if ((mask[0] == '*') && (last == '*'))
{
/* not enough chars in string to match */
if (len_string < len_mask - 1)
return 0;
/* keep only relevant chars in mask for searching string */
mask2 = string_strndup (mask + 1, len_mask - 2);
if (!mask2)
return 0;
/* search string */
rc = ((case_sensitive && strstr (string, mask2))
|| (!case_sensitive && string_strcasestr (string, mask2))) ?
1 : 0;
/* free and return */
free (mask2);
return rc;
}
2011-10-26 14:37:03 -04:00
/* no "*" at all, compare strings */
if ((case_sensitive && (strcmp (string, mask) == 0))
|| (!case_sensitive && (string_strcasecmp (string, mask) == 0)))
return 1;
2011-10-26 14:37:03 -04:00
/* no match */
return 0;
}
2007-10-31 12:11:00 -04:00
/*
* string_replace: replace a string by new one in a string
* note: returned value has to be free() after use
2007-10-31 12:11:00 -04:00
*/
char *
string_replace (const char *string, const char *search, const char *replace)
2007-10-31 12:11:00 -04:00
{
const char *pos;
char *new_string;
2007-10-31 12:11:00 -04:00
int length1, length2, length_new, count;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
if (!string || !search || !replace)
return NULL;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
length1 = strlen (search);
length2 = strlen (replace);
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
/* count number of strings to replace */
count = 0;
pos = string;
while (pos && pos[0] && (pos = strstr (pos, search)))
{
count++;
pos += length1;
}
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
/* easy: no string to replace! */
if (count == 0)
return strdup (string);
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
/* compute needed memory for new string */
length_new = strlen (string) - (count * length1) + (count * length2) + 1;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
/* allocate new string */
new_string = malloc (length_new);
2007-10-31 12:11:00 -04:00
if (!new_string)
return strdup (string);
2011-10-26 14:37:03 -04:00
/* replace all occurrences */
2007-10-31 12:11:00 -04:00
new_string[0] = '\0';
while (string && string[0])
{
pos = strstr (string, search);
if (pos)
{
strncat (new_string, string, pos - string);
strcat (new_string, replace);
pos += length1;
}
else
strcat (new_string, string);
string = pos;
}
return new_string;
}
/*
* string_expand_home: expand home in a PATH
* (for example: "~/file.txt" => "/home/xxx/file.txt")
* note: returned value has to be free() after use
*/
char *
string_expand_home (const char *path)
{
char *ptr_home, *str;
int length;
2011-10-26 14:37:03 -04:00
if (!path)
return NULL;
2011-10-26 14:37:03 -04:00
if (!path[0] || (path[0] != '~') || (path[1] != DIR_SEPARATOR_CHAR))
return strdup (path);
2011-10-26 14:37:03 -04:00
ptr_home = getenv ("HOME");
2011-10-26 14:37:03 -04:00
length = strlen (ptr_home) + strlen (path + 1) + 1;
str = malloc (length);
if (!str)
return strdup (path);
2011-10-26 14:37:03 -04:00
snprintf (str, length, "%s%s", ptr_home, path + 1);
2011-10-26 14:37:03 -04:00
return str;
}
/*
* string_remove_quotes: remove quotes at beginning/end of string
* (ignore spaces if there are before first quote or
* after last quote)
* note: returned value has to be free() after use
*/
char *
string_remove_quotes (const char *string, const char *quotes)
{
int length;
const char *pos_start, *pos_end;
2011-10-26 14:37:03 -04:00
if (!string || !quotes)
return NULL;
if (!string[0])
return strdup (string);
pos_start = string;
while (pos_start[0] == ' ')
{
pos_start++;
}
length = strlen (string);
pos_end = string + length - 1;
while ((pos_end[0] == ' ') && (pos_end > pos_start))
{
pos_end--;
}
if (!pos_start[0] || !pos_end[0] || (pos_end <= pos_start))
return strdup (string);
2011-10-26 14:37:03 -04:00
if (strchr (quotes, pos_start[0]) && (pos_end[0] == pos_start[0]))
{
if (pos_end == (pos_start + 1))
return strdup ("");
return string_strndup (pos_start + 1, pos_end - pos_start - 1);
}
2011-10-26 14:37:03 -04:00
return strdup (string);
}
/*
* string_strip: strip chars at beginning and/or end of string
* note: returned value has to be free() after use
*/
char *
string_strip (const char *string, int left, int right, const char *chars)
{
const char *ptr_start, *ptr_end;
2011-10-26 14:37:03 -04:00
if (!string)
return NULL;
2011-10-26 14:37:03 -04:00
if (!string[0])
return strdup (string);
2011-10-26 14:37:03 -04:00
ptr_start = string;
ptr_end = string + strlen (string) - 1;
2011-10-26 14:37:03 -04:00
if (left)
{
while (ptr_start[0] && strchr (chars, ptr_start[0]))
{
ptr_start++;
}
if (!ptr_start[0])
return strdup (ptr_start);
}
2011-10-26 14:37:03 -04:00
if (right)
{
while ((ptr_end >= ptr_start) && strchr (chars, ptr_end[0]))
{
ptr_end--;
}
if (ptr_end < ptr_start)
return strdup ("");
}
2011-10-26 14:37:03 -04:00
return string_strndup (ptr_start, ptr_end - ptr_start + 1);
}
2007-10-31 12:11:00 -04:00
/*
* string_convert_hex_chars: convert hex chars (\x??) to value
* note: returned value has to be free() after use
2007-10-31 12:11:00 -04:00
*/
char *
string_convert_hex_chars (const char *string)
2007-10-31 12:11:00 -04:00
{
char *output, hex_str[8], *error;
int pos_output;
long number;
output = malloc (strlen (string) + 1);
2007-10-31 12:11:00 -04:00
if (output)
{
pos_output = 0;
while (string && string[0])
{
if (string[0] == '\\')
{
string++;
switch (string[0])
{
case '\\':
output[pos_output++] = '\\';
string++;
break;
case 'x':
case 'X':
if (isxdigit ((unsigned char)string[1])
&& isxdigit ((unsigned char)string[2]))
2007-10-31 12:11:00 -04:00
{
snprintf (hex_str, sizeof (hex_str),
"0x%c%c", string[1], string[2]);
number = strtol (hex_str, &error, 16);
if (error && !error[0])
2007-10-31 12:11:00 -04:00
{
output[pos_output++] = number;
string += 3;
}
else
{
output[pos_output++] = '\\';
output[pos_output++] = string[0];
string++;
}
}
else
{
output[pos_output++] = string[0];
string++;
}
break;
default:
output[pos_output++] = '\\';
output[pos_output++] = string[0];
string++;
break;
}
}
else
{
output[pos_output++] = string[0];
string++;
}
}
output[pos_output] = '\0';
}
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
return output;
}
/*
* string_is_word_char: return 1 if given character is a "word character"
*/
int
string_is_word_char (const char *string)
{
wint_t c = utf8_wide_char (string);
2011-10-26 14:37:03 -04:00
if (c == WEOF)
return 0;
2011-10-26 14:37:03 -04:00
if (iswalnum (c))
return 1;
2011-10-26 14:37:03 -04:00
switch (c)
{
case '-':
case '_':
case '|':
return 1;
}
2011-10-26 14:37:03 -04:00
/* not a 'word char' */
return 0;
}
/*
* string_has_highlight: return 1 if string contains a highlight (using list of
* words to highlight)
* return 0 if no highlight is found in string
*/
int
string_has_highlight (const char *string, const char *highlight_words)
{
char *msg, *highlight, *match, *match_pre, *match_post, *msg_pos, *pos, *pos_end;
int end, length, startswith, endswith, wildcard_start, wildcard_end;
2011-10-26 14:37:03 -04:00
if (!string || !string[0] || !highlight_words || !highlight_words[0])
return 0;
2011-10-26 14:37:03 -04:00
/* convert both strings to lower case */
msg = strdup (string);
if (!msg)
return 0;
string_tolower (msg);
highlight = strdup (highlight_words);
if (!highlight)
{
free (msg);
return 0;
}
string_tolower (highlight);
2011-10-26 14:37:03 -04:00
pos = highlight;
end = 0;
while (!end)
{
pos_end = strchr (pos, ',');
if (!pos_end)
{
pos_end = strchr (pos, '\0');
end = 1;
}
/* error parsing string! */
if (!pos_end)
{
free (msg);
free (highlight);
return 0;
}
2011-10-26 14:37:03 -04:00
length = pos_end - pos;
pos_end[0] = '\0';
if (length > 0)
{
if ((wildcard_start = (pos[0] == '*')))
{
pos++;
length--;
}
if ((wildcard_end = (*(pos_end - 1) == '*')))
{
*(pos_end - 1) = '\0';
length--;
}
}
2011-10-26 14:37:03 -04:00
if (length > 0)
{
msg_pos = msg;
/* highlight found! */
while ((match = strstr (msg_pos, pos)) != NULL)
{
match_pre = utf8_prev_char (msg, match);
if (!match_pre)
match_pre = match - 1;
match_post = match + length;
startswith = ((match == msg) || (!string_is_word_char (match_pre)));
endswith = ((!match_post[0]) || (!string_is_word_char (match_post)));
if ((wildcard_start && wildcard_end) ||
2010-03-19 18:33:14 -04:00
(!wildcard_start && !wildcard_end &&
startswith && endswith) ||
(wildcard_start && endswith) ||
(wildcard_end && startswith))
{
free (msg);
free (highlight);
return 1;
}
msg_pos = match_post;
}
}
2011-10-26 14:37:03 -04:00
if (!end)
pos = pos_end + 1;
}
2011-10-26 14:37:03 -04:00
free (msg);
free (highlight);
2011-10-26 14:37:03 -04:00
/* no highlight found */
return 0;
}
/*
* string_has_highlight_regex_compiled: return 1 if string contains a highlight
* using a regular expression (any match
* in string must be surrounded by word
* chars)
*/
int
string_has_highlight_regex_compiled (const char *string, regex_t *regex)
{
int rc, startswith, endswith;
regmatch_t regex_match;
const char *match_pre;
2011-10-26 14:37:03 -04:00
if (!string || !regex)
return 0;
2011-10-26 14:37:03 -04:00
while (string && string[0])
{
rc = regexec (regex, string, 1, &regex_match, 0);
if ((rc != 0) || (regex_match.rm_so < 0) || (regex_match.rm_eo < 0))
break;
2011-10-26 14:37:03 -04:00
startswith = (regex_match.rm_so == 0);
if (!startswith)
{
match_pre = utf8_prev_char (string, string + regex_match.rm_so);
startswith = !string_is_word_char (match_pre);
}
endswith = 0;
if (startswith)
{
endswith = ((regex_match.rm_eo == (int)strlen (string))
|| !string_is_word_char (string + regex_match.rm_eo));
}
if (startswith && endswith)
return 1;
2011-10-26 14:37:03 -04:00
string += regex_match.rm_eo;
}
2011-10-26 14:37:03 -04:00
/* no highlight found */
return 0;
}
/*
* string_has_highlight_regex: return 1 if string contains a highlight
* using a regular expression (any match in string
* must be surrounded by word chars)
*/
int
string_has_highlight_regex (const char *string, const char *regex)
{
regex_t reg;
int rc;
2011-10-26 14:37:03 -04:00
if (!string || !regex || !regex[0])
return 0;
2011-10-26 14:37:03 -04:00
if (regcomp (&reg, regex, REG_EXTENDED) != 0)
return 0;
2011-10-26 14:37:03 -04:00
rc = string_has_highlight_regex_compiled (string, &reg);
2011-10-26 14:37:03 -04:00
regfree (&reg);
2011-10-26 14:37:03 -04:00
return rc;
}
/*
2010-04-18 17:52:32 -04:00
* string_mask_to_regex: convert a mask (string with only "*" as wildcard) to a
* regex, paying attention to special chars in a regex
*/
char *
string_mask_to_regex (const char *mask)
{
char *result;
const char *ptr_mask;
int index_result;
char *regex_special_char = ".[]{}()?+";
2011-10-26 14:37:03 -04:00
if (!mask)
return NULL;
2011-10-26 14:37:03 -04:00
result = malloc ((strlen (mask) * 2) + 1);
if (!result)
return NULL;
2011-10-26 14:37:03 -04:00
result[0] = '\0';
index_result = 0;
ptr_mask = mask;
while (ptr_mask[0])
{
/* '*' in string ? then replace by '.*' */
if (ptr_mask[0] == '*')
{
result[index_result++] = '.';
result[index_result++] = '*';
}
/* special regex char in string ? escape it with '\' */
else if (strchr (regex_special_char, ptr_mask[0]))
{
result[index_result++] = '\\';
result[index_result++] = ptr_mask[0];
}
/* standard char, just copy it */
else
result[index_result++] = ptr_mask[0];
2011-10-26 14:37:03 -04:00
ptr_mask++;
}
2011-10-26 14:37:03 -04:00
/* add final '\0' */
result[index_result] = '\0';
2011-10-26 14:37:03 -04:00
return result;
}
2007-10-31 12:11:00 -04:00
/*
* string_split: split a string according to separators
* examples:
* string_split ("abc de fghi", " ", 0, 0, NULL)
* ==> array[0] = "abc"
* array[1] = "de"
* array[2] = "fghi"
* array[3] = NULL
* string_split ("abc de fghi", " ", 1, 0, NULL)
* ==> array[0] = "abc de fghi"
* array[1] = "de fghi"
* array[2] = "fghi"
* array[3] = NULL
2007-10-31 12:11:00 -04:00
*/
char **
string_split (const char *string, const char *separators, int keep_eol,
int num_items_max, int *num_items)
2007-10-31 12:11:00 -04:00
{
2009-02-23 07:45:30 -05:00
int i, j, n_items;
char *string2, **array;
2007-10-31 12:11:00 -04:00
char *ptr, *ptr1, *ptr2;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
if (num_items != NULL)
*num_items = 0;
2011-10-26 14:37:03 -04:00
if (!string || !string[0] || !separators || !separators[0])
2007-10-31 12:11:00 -04:00
return NULL;
2011-10-26 14:37:03 -04:00
string2 = string_strip (string, 1, (keep_eol == 2) ? 0 : 1, separators);
if (!string2 || !string2[0])
return NULL;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
/* calculate number of items */
ptr = string2;
2007-10-31 12:11:00 -04:00
i = 1;
while ((ptr = strpbrk (ptr, separators)))
{
while (ptr[0] && (strchr (separators, ptr[0]) != NULL))
{
2007-10-31 12:11:00 -04:00
ptr++;
}
i++;
2007-10-31 12:11:00 -04:00
}
n_items = i;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
if ((num_items_max != 0) && (n_items > num_items_max))
n_items = num_items_max;
2011-10-26 14:37:03 -04:00
array = malloc ((n_items + 1) * sizeof (array[0]));
2009-02-23 07:45:30 -05:00
if (!array)
return NULL;
2011-10-26 14:37:03 -04:00
ptr1 = string2;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
for (i = 0; i < n_items; i++)
{
while (ptr1[0] && (strchr (separators, ptr1[0]) != NULL))
{
2007-10-31 12:11:00 -04:00
ptr1++;
}
if (i == (n_items - 1))
{
ptr2 = strpbrk (ptr1, separators);
if (!ptr2)
ptr2 = strchr (ptr1, '\0');
}
else
{
if ((ptr2 = strpbrk (ptr1, separators)) == NULL)
{
if ((ptr2 = strchr (ptr1, '\r')) == NULL)
{
if ((ptr2 = strchr (ptr1, '\n')) == NULL)
{
ptr2 = strchr (ptr1, '\0');
}
}
}
}
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
if ((ptr1 == NULL) || (ptr2 == NULL))
{
array[i] = NULL;
}
else
{
if (ptr2 - ptr1 > 0)
{
if (keep_eol)
{
array[i] = strdup (ptr1);
2009-02-23 07:45:30 -05:00
if (!array[i])
{
for (j = 0; j < n_items; j++)
{
if (array[j])
free (array[j]);
}
free (array);
free (string2);
return NULL;
}
}
else
{
array[i] = malloc (ptr2 - ptr1 + 1);
2009-02-23 07:45:30 -05:00
if (!array[i])
{
for (j = 0; j < n_items; j++)
{
if (array[j])
free (array[j]);
}
free (array);
free (string2);
return NULL;
}
strncpy (array[i], ptr1, ptr2 - ptr1);
array[i][ptr2 - ptr1] = '\0';
}
2007-10-31 12:11:00 -04:00
ptr1 = ++ptr2;
}
else
{
array[i] = NULL;
}
}
}
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
array[i] = NULL;
if (num_items != NULL)
*num_items = i;
2011-10-26 14:37:03 -04:00
free (string2);
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
return array;
}
/*
* string_free_split: free a split string
2007-10-31 12:11:00 -04:00
*/
void
string_free_split (char **split_string)
2007-10-31 12:11:00 -04:00
{
int i;
2011-10-26 14:37:03 -04:00
if (split_string)
2007-10-31 12:11:00 -04:00
{
for (i = 0; split_string[i]; i++)
free (split_string[i]);
free (split_string);
2007-10-31 12:11:00 -04:00
}
}
/*
* string_build_with_split_string: build a string with a split string
* note: returned value has to be free() after
* use
*/
char *
string_build_with_split_string (const char **split_string,
const char *separator)
{
int i, length, length_separator;
char *result;
2011-10-26 14:37:03 -04:00
if (!split_string)
return NULL;
2011-10-26 14:37:03 -04:00
length = 0;
length_separator = (separator) ? strlen (separator) : 0;
2011-10-26 14:37:03 -04:00
for (i = 0; split_string[i]; i++)
{
length += strlen (split_string[i]) + length_separator;
}
2011-10-26 14:37:03 -04:00
result = malloc (length + 1);
if (result)
{
result[0] = '\0';
2011-10-26 14:37:03 -04:00
for (i = 0; split_string[i]; i++)
{
strcat (result, split_string[i]);
if (separator && split_string[i + 1])
strcat (result, separator);
}
}
2011-10-26 14:37:03 -04:00
return result;
}
2007-10-31 12:11:00 -04:00
/*
2008-09-05 12:52:59 -04:00
* string_split_command: split a list of commands separated by 'separator'
* and ecscaped with '\'
* - empty commands are removed
* - spaces on the left of each commands are stripped
* Result must be freed with free_multi_command
2007-10-31 12:11:00 -04:00
*/
char **
string_split_command (const char *command, char separator)
2007-10-31 12:11:00 -04:00
{
int nb_substr, arr_idx, str_idx, type;
2011-08-28 09:25:30 -04:00
char **array, **array2;
char *buffer, *p;
const char *ptr;
2007-10-31 12:11:00 -04:00
if (!command || !command[0])
2010-01-24 04:47:53 -05:00
return NULL;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
nb_substr = 1;
ptr = command;
while ( (p = strchr(ptr, separator)) != NULL)
2007-10-31 12:11:00 -04:00
{
2010-01-24 04:47:53 -05:00
nb_substr++;
ptr = ++p;
2007-10-31 12:11:00 -04:00
}
array = malloc ((nb_substr + 1) * sizeof (array[0]));
2007-10-31 12:11:00 -04:00
if (!array)
2010-01-24 04:47:53 -05:00
return NULL;
2011-10-26 14:37:03 -04:00
buffer = malloc (strlen(command) + 1);
2007-10-31 12:11:00 -04:00
if (!buffer)
{
2010-01-24 04:47:53 -05:00
free (array);
return NULL;
2007-10-31 12:11:00 -04:00
}
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
ptr = command;
str_idx = 0;
arr_idx = 0;
2010-01-24 04:47:53 -05:00
while(*ptr != '\0')
{
type = 0;
if (*ptr == ';')
{
if (ptr == command)
type = 1;
else if ( *(ptr-1) != '\\')
type = 1;
else if ( *(ptr-1) == '\\')
type = 2;
}
if (type == 1)
{
buffer[str_idx] = '\0';
str_idx = -1;
p = buffer;
/* strip white spaces a the begining of the line */
while (*p == ' ') p++;
if (p && p[0])
array[arr_idx++] = strdup (p);
}
else if (type == 2)
buffer[--str_idx] = *ptr;
else
buffer[str_idx] = *ptr;
str_idx++;
ptr++;
2007-10-31 12:11:00 -04:00
}
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
buffer[str_idx] = '\0';
p = buffer;
while (*p == ' ') p++;
if (p && p[0])
2010-01-24 04:47:53 -05:00
array[arr_idx++] = strdup (p);
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
array[arr_idx] = NULL;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
free (buffer);
2011-10-26 14:37:03 -04:00
2011-08-28 09:25:30 -04:00
array2 = realloc (array, (arr_idx + 1) * sizeof(array[0]));
if (!array2)
{
if (array)
free (array);
return NULL;
}
2011-10-26 14:37:03 -04:00
2011-08-28 09:25:30 -04:00
return array2;
2007-10-31 12:11:00 -04:00
}
/*
2009-04-24 11:24:56 -04:00
* string_free_split_command : free a list of commands split
* with string_split_command
2007-10-31 12:11:00 -04:00
*/
void
2009-04-24 11:24:56 -04:00
string_free_split_command (char **split_command)
2007-10-31 12:11:00 -04:00
{
int i;
2009-04-24 11:24:56 -04:00
if (split_command)
2007-10-31 12:11:00 -04:00
{
2009-04-24 11:24:56 -04:00
for (i = 0; split_command[i]; i++)
free (split_command[i]);
free (split_command);
2007-10-31 12:11:00 -04:00
}
}
/*
* string_iconv: convert string to another charset
* note: returned value has to be free() after use
2007-10-31 12:11:00 -04:00
*/
char *
string_iconv (int from_utf8, const char *from_code, const char *to_code,
const char *string)
2007-10-31 12:11:00 -04:00
{
char *outbuf;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
#ifdef HAVE_ICONV
iconv_t cd;
char *inbuf, *ptr_inbuf, *ptr_outbuf, *next_char;
char *ptr_inbuf_shift;
int done;
size_t err, inbytesleft, outbytesleft;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
if (from_code && from_code[0] && to_code && to_code[0]
&& (string_strcasecmp(from_code, to_code) != 0))
{
cd = iconv_open (to_code, from_code);
if (cd == (iconv_t)(-1))
outbuf = strdup (string);
else
{
inbuf = strdup (string);
2009-02-23 07:45:30 -05:00
if (!inbuf)
return NULL;
2007-10-31 12:11:00 -04:00
ptr_inbuf = inbuf;
inbytesleft = strlen (inbuf);
outbytesleft = inbytesleft * 4;
outbuf = malloc (outbytesleft + 2);
2009-02-23 07:45:30 -05:00
if (!outbuf)
return inbuf;
2007-10-31 12:11:00 -04:00
ptr_outbuf = outbuf;
ptr_inbuf_shift = NULL;
done = 0;
while (!done)
{
err = iconv (cd, (ICONV_CONST char **)(&ptr_inbuf), &inbytesleft,
&ptr_outbuf, &outbytesleft);
if (err == (size_t)(-1))
{
switch (errno)
{
case EINVAL:
done = 1;
break;
case E2BIG:
done = 1;
break;
case EILSEQ:
if (from_utf8)
{
next_char = utf8_next_char (ptr_inbuf);
if (next_char)
{
inbytesleft -= next_char - ptr_inbuf;
ptr_inbuf = next_char;
}
else
{
inbytesleft--;
ptr_inbuf++;
}
}
else
{
ptr_inbuf++;
inbytesleft--;
}
ptr_outbuf[0] = '?';
ptr_outbuf++;
outbytesleft--;
break;
}
}
else
{
if (!ptr_inbuf_shift)
{
ptr_inbuf_shift = ptr_inbuf;
ptr_inbuf = NULL;
inbytesleft = 0;
}
else
done = 1;
}
}
if (ptr_inbuf_shift)
ptr_inbuf = ptr_inbuf_shift;
ptr_outbuf[0] = '\0';
free (inbuf);
iconv_close (cd);
}
}
else
outbuf = strdup (string);
#else
/* make C compiler happy */
(void) from_utf8;
(void) from_code;
(void) to_code;
outbuf = strdup (string);
#endif /* HAVE_ICONV */
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
return outbuf;
}
/*
* string_iconv_to_internal: convert user string (input, script, ..) to
* WeeChat internal storage charset
* note: returned value has to be free() after use
2007-10-31 12:11:00 -04:00
*/
char *
string_iconv_to_internal (const char *charset, const char *string)
2007-10-31 12:11:00 -04:00
{
char *input, *output;
2011-10-26 14:37:03 -04:00
if (!string)
return NULL;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
input = strdup (string);
2009-02-23 07:45:30 -05:00
if (!input)
return NULL;
2011-10-26 14:37:03 -04:00
2010-03-19 18:33:14 -04:00
/*
* optimized for UTF-8: if charset is NULL => we use term charset => if
* this charset is already UTF-8, then no iconv is needed
*/
2007-10-31 12:11:00 -04:00
if (local_utf8 && (!charset || !charset[0]))
return input;
2011-10-26 14:37:03 -04:00
2009-02-23 07:45:30 -05:00
if (utf8_has_8bits (input) && utf8_is_valid (input, NULL))
return input;
2011-10-26 14:37:03 -04:00
2009-02-23 07:45:30 -05:00
output = string_iconv (0,
(charset && charset[0]) ?
charset : weechat_local_charset,
WEECHAT_INTERNAL_CHARSET,
input);
if (!output)
return input;
utf8_normalize (output, '?');
free (input);
return output;
2007-10-31 12:11:00 -04:00
}
/*
* string_iconv_from_internal: convert internal string to terminal charset,
* for display
* note: returned value has to be free() after use
2007-10-31 12:11:00 -04:00
*/
char *
string_iconv_from_internal (const char *charset, const char *string)
2007-10-31 12:11:00 -04:00
{
char *input, *output;
2011-10-26 14:37:03 -04:00
if (!string)
return NULL;
2011-10-26 14:37:03 -04:00
2007-10-31 12:11:00 -04:00
input = strdup (string);
2009-02-23 07:45:30 -05:00
if (!input)
return NULL;
2011-10-26 14:37:03 -04:00
2010-03-19 18:33:14 -04:00
/*
* optimized for UTF-8: if charset is NULL => we use term charset => if
* this charset is already UTF-8, then no iconv needed
*/
2007-10-31 12:11:00 -04:00
if (local_utf8 && (!charset || !charset[0]))
return input;
2011-10-26 14:37:03 -04:00
2009-02-23 07:45:30 -05:00
utf8_normalize (input, '?');
output = string_iconv (1,
WEECHAT_INTERNAL_CHARSET,
(charset && charset[0]) ?
charset : weechat_local_charset,
input);
if (!output)
return input;
free (input);
return output;
2007-10-31 12:11:00 -04:00
}
/*
* string_iconv_fprintf: encode to terminal charset, then call fprintf on a file
* return 1 if ok, 0 if error
2007-10-31 12:11:00 -04:00
*/
int
string_iconv_fprintf (FILE *file, const char *data, ...)
2007-10-31 12:11:00 -04:00
{
char *buf2;
int rc, num_written;
2011-10-26 14:37:03 -04:00
rc = 0;
weechat_va_format (data);
if (vbuffer)
{
buf2 = string_iconv_from_internal (NULL, vbuffer);
num_written = fprintf (file, "%s", (buf2) ? buf2 : vbuffer);
rc = (num_written == (int)strlen ((buf2) ? buf2 : vbuffer)) ? 1 : 0;
if (buf2)
free (buf2);
free (vbuffer);
}
2011-10-26 14:37:03 -04:00
return rc;
2007-10-31 12:11:00 -04:00
}
/*
* string_format_size: format a string with size and unit name (bytes, KB, MB, GB)
* note: returned value has to be free() after use
*/
char *
string_format_size (unsigned long long size)
{
char *unit_name[] = { N_("bytes"), N_("KB"), N_("MB"), N_("GB") };
char *unit_format[] = { "%.0f", "%.1f", "%.02f", "%.02f" };
float unit_divide[] = { 1, 1024, 1024*1024, 1024*1024*1024 };
char format_size[128], str_size[128];
int num_unit;
2011-10-26 14:37:03 -04:00
str_size[0] = '\0';
2011-10-26 14:37:03 -04:00
if (size < 1024*10)
num_unit = 0;
else if (size < 1024*1024)
num_unit = 1;
else if (size < 1024*1024*1024)
num_unit = 2;
else
num_unit = 3;
2011-10-26 14:37:03 -04:00
snprintf (format_size, sizeof (format_size),
"%s %%s",
unit_format[num_unit]);
snprintf (str_size, sizeof (str_size),
format_size,
((float)size) / ((float)(unit_divide[num_unit])),
(size <= 1) ? _("byte") : _(unit_name[num_unit]));
2011-10-26 14:37:03 -04:00
return strdup (str_size);
}
/*
* string_convbase64_8x3_to_6x4 : convert 3 bytes of 8 bits in 4 bytes of 6 bits
*/
void
string_convbase64_8x3_to_6x4 (const char *from, char *to)
{
unsigned char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789+/";
2011-10-26 14:37:03 -04:00
to[0] = base64_table [ (from[0] & 0xfc) >> 2 ];
to[1] = base64_table [ ((from[0] & 0x03) << 4) + ((from[1] & 0xf0) >> 4) ];
to[2] = base64_table [ ((from[1] & 0x0f) << 2) + ((from[2] & 0xc0) >> 6) ];
to[3] = base64_table [ from[2] & 0x3f ];
}
/*
* string_encode_base64: encode a string in base64
* length is number of bytes in "from" to convert
* (commonly strlen(from))
*/
void
string_encode_base64 (const char *from, int length, char *to)
{
const char *ptr_from;
char *ptr_to;
2011-10-26 14:37:03 -04:00
ptr_from = from;
ptr_to = to;
2011-10-26 14:37:03 -04:00
while (length >= 3)
{
string_convbase64_8x3_to_6x4 (ptr_from, ptr_to);
ptr_from += 3 * sizeof (*ptr_from);
ptr_to += 4 * sizeof (*ptr_to);
length -= 3;
}
2011-10-26 14:37:03 -04:00
if (length > 0)
{
char rest[3] = { 0, 0, 0 };
switch (length)
{
case 1 :
rest[0] = ptr_from[0];
string_convbase64_8x3_to_6x4 (rest, ptr_to);
ptr_to[2] = ptr_to[3] = '=';
break;
case 2 :
rest[0] = ptr_from[0];
rest[1] = ptr_from[1];
string_convbase64_8x3_to_6x4 (rest, ptr_to);
ptr_to[3] = '=';
break;
}
ptr_to[4] = 0;
}
else
ptr_to[0] = '\0';
}
/*
* string_convbase64_6x4_to_8x3 : convert 4 bytes of 6 bits to 3 bytes of 8 bits
*/
void
string_convbase64_6x4_to_8x3 (const unsigned char *from, unsigned char *to)
{
to[0] = from[0] << 2 | from[1] >> 4;
to[1] = from[1] << 4 | from[2] >> 2;
to[2] = ((from[2] << 6) & 0xc0) | from[3];
}
/*
* string_decode_base64: decode a base64 string
* return length of string in *to
* (does not count final \0)
*/
int
string_decode_base64 (const char *from, char *to)
{
const char *ptr_from;
int length, to_length, i;
char *ptr_to;
unsigned char c, in[4], out[3];
unsigned char base64_table[]="|$$$}rstuvwxyz{$$$$$$$>?"
"@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
2011-10-26 14:37:03 -04:00
ptr_from = from;
ptr_to = to;
2011-10-26 14:37:03 -04:00
ptr_to[0] = '\0';
to_length = 0;
2011-10-26 14:37:03 -04:00
while (ptr_from && ptr_from[0])
{
length = 0;
for (i = 0; i < 4; i++)
{
c = 0;
while (ptr_from[0] && (c == 0))
{
c = (unsigned char) ptr_from[0];
ptr_from++;
c = ((c < 43) || (c > 122)) ? 0 : base64_table[c - 43];
if (c)
c = (c == '$') ? 0 : c - 61;
}
if (ptr_from[0])
{
length++;
if (c)
in[i] = c - 1;
}
else
{
in[i] = '\0';
break;
}
}
if (length)
{
string_convbase64_6x4_to_8x3 (in, out);
for (i = 0; i < length - 1; i++)
{
ptr_to[0] = out[i];
ptr_to++;
to_length++;
}
}
}
2011-10-26 14:37:03 -04:00
ptr_to[0] = '\0';
2011-10-26 14:37:03 -04:00
return to_length;
}
/*
* string_is_command_char: return 1 if first char of string is a command char,
* otherwise 0
*/
int
string_is_command_char (const char *string)
{
const char *ptr_command_chars;
2011-10-26 14:37:03 -04:00
if (!string)
return 0;
2011-10-26 14:37:03 -04:00
if (string[0] == '/')
return 1;
2011-10-26 14:37:03 -04:00
ptr_command_chars = CONFIG_STRING(config_look_command_chars);
if (!ptr_command_chars || !ptr_command_chars[0])
return 0;
2011-10-26 14:37:03 -04:00
while (ptr_command_chars && ptr_command_chars[0])
{
if (utf8_charcmp (ptr_command_chars, string) == 0)
return 1;
ptr_command_chars = utf8_next_char (ptr_command_chars);
}
2011-10-26 14:37:03 -04:00
return 0;
}
/*
* string_input_for_buffer: return pointer to input text for buffer (pointer
* inside "string" argument)
* or return NULL if it's a command
* (by default, a command starts with a single '/')
*/
const char *
string_input_for_buffer (const char *string)
{
char *pos_slash, *pos_space, *next_char;
2011-10-26 14:37:03 -04:00
/* special case for C comments pasted in input line */
if (strncmp (string, "/*", 2) == 0)
return string;
2011-10-26 14:37:03 -04:00
2010-03-19 18:33:14 -04:00
/*
* special case if string starts with '/': to allow to paste a path line
* "/path/to/file.txt", we check if next '/' is after a space or not
*/
if (string[0] == '/')
{
pos_slash = strchr (string + 1, '/');
pos_space = strchr (string + 1, ' ');
2011-10-26 14:37:03 -04:00
2010-03-19 18:33:14 -04:00
/*
* if there's no other '/' of if '/' is after first space,
* then it is a command, and return NULL
*/
if (!pos_slash || (pos_space && pos_slash > pos_space))
return NULL;
2011-10-26 14:37:03 -04:00
return (string[1] == '/') ? string + 1 : string;
}
2011-10-26 14:37:03 -04:00
/* if string does not start with a command char, then it's not command */
if (!string_is_command_char (string))
return string;
2011-10-26 14:37:03 -04:00
/* check if first char is doubled: if yes, then it's not a command */
next_char = utf8_next_char (string);
if (!next_char || !next_char[0])
return string;
if (utf8_charcmp (string, next_char) == 0)
return next_char;
2011-10-26 14:37:03 -04:00
/* string is a command */
return NULL;
}
/*
* string_replace_with_hashtable: replace ${codes} with value from hashtable
* "errors" is set with number of keys not found
* in hashtable
*/
char *
string_replace_with_hashtable (const char *string,
struct t_hashtable *hashtable,
int *errors)
{
int length, length_value, index_string, index_result;
2011-08-28 09:25:30 -04:00
char *result, *result2, *key;
const char *pos_end_name, *ptr_value;
2011-10-26 14:37:03 -04:00
*errors = 0;
2011-10-26 14:37:03 -04:00
if (!string)
return NULL;
2011-10-26 14:37:03 -04:00
if (!hashtable)
return strdup (string);
2011-10-26 14:37:03 -04:00
length = strlen (string) + 1;
result = malloc (length);
if (result)
{
index_string = 0;
index_result = 0;
while (string[index_string])
{
if ((string[index_string] == '\\')
&& (string[index_string + 1] == '$'))
{
index_string++;
result[index_result++] = string[index_string++];
}
else if ((string[index_string] == '$')
&& (string[index_string + 1] == '{'))
{
pos_end_name = strchr (string + index_string + 2, '}');
if (pos_end_name)
{
key = string_strndup (string + index_string + 2,
pos_end_name - (string + index_string + 2));
if (key)
{
ptr_value = (const char *)hashtable_get (hashtable, key);
if (ptr_value)
{
length_value = strlen (ptr_value);
length += length_value;
2011-08-28 09:25:30 -04:00
result2 = realloc (result, length);
if (!result2)
{
2011-08-28 09:25:30 -04:00
if (result)
free (result);
free (key);
return NULL;
}
2011-08-28 09:25:30 -04:00
result = result2;
strcpy (result + index_result, ptr_value);
index_result += length_value;
index_string += pos_end_name - string -
index_string + 1;
}
else
{
result[index_result++] = string[index_string++];
(*errors)++;
}
2011-10-26 14:37:03 -04:00
free (key);
}
else
result[index_result++] = string[index_string++];
}
else
result[index_result++] = string[index_string++];
}
else
result[index_result++] = string[index_string++];
}
result[index_result] = '\0';
}
2011-10-26 14:37:03 -04:00
return result;
}