2007-10-31 12:11:00 -04:00
|
|
|
/*
|
2012-12-15 06:27:57 -05:00
|
|
|
* wee-string.c - string functions
|
|
|
|
*
|
2013-12-31 18:14:12 -05:00
|
|
|
* Copyright (C) 2003-2014 Sébastien Helleu <flashcode@flashtux.org>
|
2007-10-31 12:11:00 -04:00
|
|
|
*
|
2010-06-22 13:46:28 -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.
|
|
|
|
*
|
2010-06-22 13:46:28 -04:00
|
|
|
* 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
|
2010-06-22 13:46:28 -04:00
|
|
|
* along with WeeChat. If not, see <http://www.gnu.org/licenses/>.
|
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>
|
2008-06-01 16:09:12 -04:00
|
|
|
#include <wctype.h>
|
2010-11-25 15:28:14 -05:00
|
|
|
#include <regex.h>
|
2008-06-01 16:09:12 -04:00
|
|
|
#include <wchar.h>
|
2013-08-10 11:25:14 -04:00
|
|
|
#include <stdint.h>
|
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"
|
2010-03-02 11:34:49 -05:00
|
|
|
#include "wee-config.h"
|
2013-08-10 11:25:14 -04:00
|
|
|
#include "wee-hashtable.h"
|
2007-10-31 12:11:00 -04:00
|
|
|
#include "wee-utf8.h"
|
2009-02-08 13:52:16 -05:00
|
|
|
#include "../gui/gui-color.h"
|
2011-08-26 03:55:55 -04:00
|
|
|
#include "../plugins/plugin.h"
|
2007-10-31 12:11:00 -04:00
|
|
|
|
|
|
|
|
2014-01-19 17:29:00 -05:00
|
|
|
#define IS_OCTAL_DIGIT(c) ((c >= '0') && (c <= '7'))
|
|
|
|
#define HEX2DEC(c) (((c >= 'a') && (c <= 'f')) ? c - 'a' + 10 : \
|
|
|
|
((c >= 'A') && (c <= 'F')) ? c - 'A' + 10 : \
|
|
|
|
c - '0')
|
|
|
|
|
2013-08-10 11:25:14 -04:00
|
|
|
typedef uint32_t string_shared_count_t;
|
|
|
|
|
|
|
|
struct t_hashtable *string_hashtable_shared = NULL;
|
|
|
|
|
|
|
|
|
2007-10-31 12:11:00 -04:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Defines a "strndup" function for systems where this function does not exist
|
|
|
|
* (FreeBSD and maybe others).
|
2007-10-31 12:11:00 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
2008-06-03 04:56:51 -04:00
|
|
|
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
|
|
|
|
2008-03-23 18:00:04 -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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Converts string to lower case (locale independent).
|
2007-10-31 12:11:00 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
string_tolower (char *string)
|
|
|
|
{
|
|
|
|
while (string && string[0])
|
|
|
|
{
|
|
|
|
if ((string[0] >= 'A') && (string[0] <= 'Z'))
|
|
|
|
string[0] += ('a' - 'A');
|
2007-11-11 07:28:24 -05:00
|
|
|
string = utf8_next_char (string);
|
2007-10-31 12:11:00 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Converts string to upper case (locale independent).
|
2007-10-31 12:11:00 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
string_toupper (char *string)
|
|
|
|
{
|
|
|
|
while (string && string[0])
|
|
|
|
{
|
|
|
|
if ((string[0] >= 'a') && (string[0] <= 'z'))
|
|
|
|
string[0] -= ('a' - 'A');
|
2007-11-11 07:28:24 -05:00
|
|
|
string = utf8_next_char (string);
|
2007-10-31 12:11:00 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Compares two strings (locale and case independent).
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* < 0: string1 < string2
|
|
|
|
* 0: string1 == string2
|
|
|
|
* > 0: string1 > string2
|
2007-10-31 12:11:00 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
2008-06-03 04:56:51 -04:00
|
|
|
string_strcasecmp (const char *string1, const char *string2)
|
2007-10-31 12:11:00 -04:00
|
|
|
{
|
2007-11-11 07:28:24 -05: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])
|
|
|
|
{
|
2007-11-11 07:28:24 -05:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2011-11-12 04:37:44 -05:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Compares two strings (locale and case independent) using a range.
|
|
|
|
*
|
|
|
|
* The range is the number of chars which can be converted from upper to lower
|
|
|
|
* case. For example 26 = all letters of alphabet, 29 = all letters + 3 chars.
|
|
|
|
*
|
|
|
|
* Examples:
|
|
|
|
* - range = 26: A-Z ==> a-z
|
|
|
|
* - range = 29: A-Z [ \ ] ==> a-z { | }
|
|
|
|
* - range = 30: A-Z [ \ ] ^ ==> a-z { | } ~
|
|
|
|
* (ranges 29 and 30 are used by some protocols like IRC)
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* < 0: string1 < string2
|
|
|
|
* 0: string1 == string2
|
|
|
|
* > 0: string1 > string2
|
2011-11-12 04:37:44 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
string_strcasecmp_range (const char *string1, const char *string2, int range)
|
|
|
|
{
|
|
|
|
int diff;
|
|
|
|
|
|
|
|
if (!string1 || !string2)
|
|
|
|
return (string1) ? 1 : ((string2) ? -1 : 0);
|
|
|
|
|
|
|
|
while (string1[0] && string2[0])
|
|
|
|
{
|
|
|
|
diff = utf8_charcasecmp_range (string1, string2, range);
|
|
|
|
if (diff != 0)
|
|
|
|
return diff;
|
|
|
|
|
|
|
|
string1 = utf8_next_char (string1);
|
|
|
|
string2 = utf8_next_char (string2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (string1[0]) ? 1 : ((string2[0]) ? -1 : 0);
|
|
|
|
}
|
|
|
|
|
2007-10-31 12:11:00 -04:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Compares two strings with max length (locale and case independent).
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* < 0: string1 < string2
|
|
|
|
* 0: string1 == string2
|
|
|
|
* > 0: string1 > string2
|
2007-10-31 12:11:00 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
2008-06-03 04:56:51 -04:00
|
|
|
string_strncasecmp (const char *string1, const char *string2, int max)
|
2007-10-31 12:11:00 -04:00
|
|
|
{
|
2007-11-11 07:28:24 -05: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])
|
|
|
|
{
|
2007-11-11 07:28:24 -05:00
|
|
|
diff = utf8_charcasecmp (string1, string2);
|
|
|
|
if (diff != 0)
|
|
|
|
return diff;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2007-11-11 07:28:24 -05: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);
|
|
|
|
}
|
|
|
|
|
2011-11-12 04:37:44 -05:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Compares two strings with max length (locale and case independent) using a
|
|
|
|
* range.
|
|
|
|
*
|
|
|
|
* The range is the number of chars which can be converted from upper to lower
|
|
|
|
* case. For example 26 = all letters of alphabet, 29 = all letters + 3 chars.
|
|
|
|
*
|
|
|
|
* Examples:
|
|
|
|
* - range = 26: A-Z ==> a-z
|
|
|
|
* - range = 29: A-Z [ \ ] ==> a-z { | }
|
|
|
|
* - range = 30: A-Z [ \ ] ^ ==> a-z { | } ~
|
|
|
|
* (ranges 29 and 30 are used by some protocols like IRC)
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* < 0: string1 < string2
|
|
|
|
* 0: string1 == string2
|
|
|
|
* > 0: string1 > string2
|
2011-11-12 04:37:44 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
string_strncasecmp_range (const char *string1, const char *string2, int max,
|
|
|
|
int range)
|
|
|
|
{
|
|
|
|
int count, diff;
|
|
|
|
|
|
|
|
if (!string1 || !string2)
|
|
|
|
return (string1) ? 1 : ((string2) ? -1 : 0);
|
|
|
|
|
|
|
|
count = 0;
|
|
|
|
while ((count < max) && string1[0] && string2[0])
|
|
|
|
{
|
|
|
|
diff = utf8_charcasecmp_range (string1, string2, range);
|
|
|
|
if (diff != 0)
|
|
|
|
return diff;
|
|
|
|
|
|
|
|
string1 = utf8_next_char (string1);
|
|
|
|
string2 = utf8_next_char (string2);
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count >= max)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return (string1[0]) ? 1 : ((string2[0]) ? -1 : 0);
|
|
|
|
}
|
|
|
|
|
2008-01-11 09:08:36 -05:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Compares two strings, ignoring some chars.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* < 0: string1 < string2
|
|
|
|
* 0: string1 == string2
|
|
|
|
* > 0: string1 > string2
|
2008-01-11 09:08:36 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
2008-06-03 04:56:51 -04:00
|
|
|
string_strcmp_ignore_chars (const char *string1, const char *string2,
|
|
|
|
const char *chars_ignored, int case_sensitive)
|
2008-01-11 09:08:36 -05:00
|
|
|
{
|
|
|
|
int diff;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-01-11 09:08:36 -05:00
|
|
|
if (!string1 && !string2)
|
|
|
|
return 0;
|
|
|
|
if (!string1 && string2)
|
|
|
|
return -1;
|
|
|
|
if (string1 && !string2)
|
|
|
|
return 1;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-01-11 09:08:36 -05:00
|
|
|
while (string1 && string1[0] && string2 && string2[0])
|
|
|
|
{
|
2009-05-09 07:03:44 -04:00
|
|
|
/* skip ignored chars */
|
2008-01-11 09:08:36 -05:00
|
|
|
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
|
|
|
|
2008-01-11 09:08:36 -05: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
|
|
|
|
2008-01-11 09:08:36 -05:00
|
|
|
string1 = utf8_next_char (string1);
|
|
|
|
string2 = utf8_next_char (string2);
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2009-05-09 07:03:44 -04:00
|
|
|
/* skip ignored chars */
|
2008-01-11 09:08:36 -05:00
|
|
|
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
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Searches for a string in another string (locale and case independent).
|
2007-10-31 12:11:00 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
2008-06-03 04:56:51 -04:00
|
|
|
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
|
|
|
|
2009-04-14 08:12:28 -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)
|
2008-06-03 04:56:51 -04:00
|
|
|
return (char *)string;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2009-04-14 08:12:28 -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;
|
|
|
|
}
|
|
|
|
|
2008-03-22 18:36:12 -04:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Checks if a string matches a mask.
|
|
|
|
*
|
|
|
|
* Mask can begin or end with "*", no other "*" are allowed inside mask.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* 1: string matches mask
|
|
|
|
* 0: string does not match mask
|
2008-03-22 18:36:12 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
2008-06-03 04:56:51 -04:00
|
|
|
string_match (const char *string, const char *mask, int case_sensitive)
|
2008-03-22 18:36:12 -04:00
|
|
|
{
|
|
|
|
char last, *mask2;
|
|
|
|
int len_string, len_mask, rc;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-03-22 18:36:12 -04:00
|
|
|
if (!mask || !mask[0])
|
|
|
|
return 0;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-03-22 18:36:12 -04:00
|
|
|
/* if mask is "*", then any string matches */
|
|
|
|
if (strcmp (mask, "*") == 0)
|
|
|
|
return 1;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-03-22 18:36:12 -04:00
|
|
|
len_string = strlen (string);
|
|
|
|
len_mask = strlen (mask);
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-03-22 18:36:12 -04:00
|
|
|
last = mask[len_mask - 1];
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-03-22 18:36:12 -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
|
|
|
|
2008-03-22 18:36:12 -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
|
|
|
|
2008-03-22 18:36:12 -04:00
|
|
|
/* mask begins and ends with "*" */
|
|
|
|
if ((mask[0] == '*') && (last == '*'))
|
|
|
|
{
|
|
|
|
/* not enough chars in string to match */
|
2013-03-11 13:09:33 -04:00
|
|
|
if (len_string < len_mask - 2)
|
2008-03-22 18:36:12 -04:00
|
|
|
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
|
|
|
|
2008-03-22 18:36:12 -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
|
|
|
|
2008-03-22 18:36:12 -04:00
|
|
|
/* no match */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-10-31 12:11:00 -04:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Replaces a string by new one in a string.
|
|
|
|
*
|
|
|
|
* Note: result must be freed after use.
|
2007-10-31 12:11:00 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
2008-06-03 04:56:51 -04:00
|
|
|
string_replace (const char *string, const char *search, const char *replace)
|
2007-10-31 12:11:00 -04:00
|
|
|
{
|
2008-06-03 04:56:51 -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 */
|
2008-03-23 18:00:04 -04:00
|
|
|
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
|
|
|
|
2010-08-27 09:59:06 -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;
|
|
|
|
}
|
|
|
|
|
2010-05-02 12:21:58 -04:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Expands home in a path.
|
|
|
|
*
|
|
|
|
* Example: "~/file.txt" => "/home/xxx/file.txt"
|
|
|
|
*
|
|
|
|
* Note: result must be freed after use.
|
2010-05-02 12:21:58 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
|
|
|
string_expand_home (const char *path)
|
|
|
|
{
|
|
|
|
char *ptr_home, *str;
|
|
|
|
int length;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-05-02 12:21:58 -04:00
|
|
|
if (!path)
|
|
|
|
return NULL;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2012-02-25 02:42:00 -05:00
|
|
|
if (!path[0] || (path[0] != '~')
|
|
|
|
|| ((path[1] && path[1] != DIR_SEPARATOR_CHAR)))
|
|
|
|
{
|
2010-05-02 12:21:58 -04:00
|
|
|
return strdup (path);
|
2012-02-25 02:42:00 -05:00
|
|
|
}
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-05-02 12:21:58 -04:00
|
|
|
ptr_home = getenv ("HOME");
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-05-02 12:21:58 -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
|
|
|
|
2010-05-02 12:21:58 -04:00
|
|
|
snprintf (str, length, "%s%s", ptr_home, path + 1);
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-05-02 12:21:58 -04:00
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2007-11-05 12:51:53 -05:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Removes quotes at beginning/end of string (ignores spaces if there are before
|
|
|
|
* first quote or after last quote).
|
|
|
|
*
|
|
|
|
* Note: result must be freed after use.
|
2007-11-05 12:51:53 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
2008-06-03 04:56:51 -04:00
|
|
|
string_remove_quotes (const char *string, const char *quotes)
|
2007-11-05 12:51:53 -05:00
|
|
|
{
|
|
|
|
int length;
|
2008-06-03 04:56:51 -04:00
|
|
|
const char *pos_start, *pos_end;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2007-11-05 12:51:53 -05: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
|
|
|
|
2007-11-05 12:51:53 -05:00
|
|
|
if (strchr (quotes, pos_start[0]) && (pos_end[0] == pos_start[0]))
|
|
|
|
{
|
|
|
|
if (pos_end == (pos_start + 1))
|
|
|
|
return strdup ("");
|
2008-01-24 10:50:20 -05:00
|
|
|
return string_strndup (pos_start + 1, pos_end - pos_start - 1);
|
2007-11-05 12:51:53 -05:00
|
|
|
}
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2007-11-05 12:51:53 -05:00
|
|
|
return strdup (string);
|
|
|
|
}
|
|
|
|
|
2008-03-15 04:47:27 -04:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Strips chars at beginning/end of string.
|
|
|
|
*
|
|
|
|
* Note: result must be freed after use.
|
2008-03-15 04:47:27 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
2008-06-03 04:56:51 -04:00
|
|
|
string_strip (const char *string, int left, int right, const char *chars)
|
2008-03-15 04:47:27 -04:00
|
|
|
{
|
2008-06-03 04:56:51 -04:00
|
|
|
const char *ptr_start, *ptr_end;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-03-15 04:47:27 -04:00
|
|
|
if (!string)
|
|
|
|
return NULL;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-03-15 04:47:27 -04:00
|
|
|
if (!string[0])
|
|
|
|
return strdup (string);
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-03-15 04:47:27 -04:00
|
|
|
ptr_start = string;
|
|
|
|
ptr_end = string + strlen (string) - 1;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-03-15 04:47:27 -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
|
|
|
|
2008-03-15 04:47:27 -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
|
|
|
|
2008-03-15 04:47:27 -04:00
|
|
|
return string_strndup (ptr_start, ptr_end - ptr_start + 1);
|
|
|
|
}
|
|
|
|
|
2007-10-31 12:11:00 -04:00
|
|
|
/*
|
2014-01-19 17:29:00 -05:00
|
|
|
* Converts escaped chars to their value.
|
|
|
|
*
|
|
|
|
* Following escaped chars are supported:
|
|
|
|
* \" double quote
|
|
|
|
* \\ backslash
|
|
|
|
* \a alert (BEL)
|
|
|
|
* \b backspace
|
|
|
|
* \e escape
|
|
|
|
* \f form feed
|
|
|
|
* \n new line
|
|
|
|
* \r carriage return
|
|
|
|
* \t horizontal tab
|
|
|
|
* \v vertical tab
|
|
|
|
* \0ooo octal value (ooo is 0 to 3 digits)
|
|
|
|
* \xhh hexadecimal value (hh is 1 to 2 digits)
|
2012-12-13 12:51:44 -05:00
|
|
|
*
|
|
|
|
* Note: result must be freed after use.
|
2007-10-31 12:11:00 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
2014-01-19 17:29:00 -05:00
|
|
|
string_convert_escaped_chars (const char *string)
|
2007-10-31 12:11:00 -04:00
|
|
|
{
|
2014-01-19 17:29:00 -05:00
|
|
|
const unsigned char *ptr_string;
|
|
|
|
char *output;
|
|
|
|
int pos_output, i, value;
|
2007-10-31 12:11:00 -04:00
|
|
|
|
2014-01-19 17:29:00 -05:00
|
|
|
/* the output length is always <= to string length */
|
2008-03-23 18:00:04 -04:00
|
|
|
output = malloc (strlen (string) + 1);
|
2014-01-19 17:29:00 -05:00
|
|
|
if (!output)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pos_output = 0;
|
|
|
|
ptr_string = (const unsigned char *)string;
|
|
|
|
while (ptr_string && ptr_string[0])
|
2007-10-31 12:11:00 -04:00
|
|
|
{
|
2014-01-19 17:29:00 -05:00
|
|
|
if (ptr_string[0] == '\\')
|
2007-10-31 12:11:00 -04:00
|
|
|
{
|
2014-01-19 17:29:00 -05:00
|
|
|
ptr_string++;
|
|
|
|
switch (ptr_string[0])
|
2007-10-31 12:11:00 -04:00
|
|
|
{
|
2014-01-19 17:29:00 -05:00
|
|
|
case '"':
|
|
|
|
output[pos_output++] = '"';
|
|
|
|
ptr_string++;
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
output[pos_output++] = '\\';
|
|
|
|
ptr_string++;
|
|
|
|
break;
|
|
|
|
case 'a': /* alert */
|
|
|
|
output[pos_output++] = 7;
|
|
|
|
ptr_string++;
|
|
|
|
break;
|
|
|
|
case 'b': /* backspace */
|
|
|
|
output[pos_output++] = 8;
|
|
|
|
ptr_string++;
|
|
|
|
break;
|
|
|
|
case 'e': /* escape */
|
|
|
|
output[pos_output++] = 27;
|
|
|
|
ptr_string++;
|
|
|
|
break;
|
|
|
|
case 'f': /* form feed */
|
|
|
|
output[pos_output++] = 12;
|
|
|
|
ptr_string++;
|
|
|
|
break;
|
|
|
|
case 'n': /* new line */
|
|
|
|
output[pos_output++] = 10;
|
|
|
|
ptr_string++;
|
|
|
|
break;
|
|
|
|
case 'r': /* carriage return */
|
|
|
|
output[pos_output++] = 13;
|
|
|
|
ptr_string++;
|
|
|
|
break;
|
|
|
|
case 't': /* horizontal tab */
|
|
|
|
output[pos_output++] = 9;
|
|
|
|
ptr_string++;
|
|
|
|
break;
|
|
|
|
case 'v': /* vertical tab */
|
|
|
|
output[pos_output++] = 11;
|
|
|
|
ptr_string++;
|
|
|
|
break;
|
|
|
|
case '0': /* octal value (0 to 3 digits expected) */
|
|
|
|
value = 0;
|
|
|
|
for (i = 0; (i < 3) && IS_OCTAL_DIGIT(ptr_string[i + 1]); i++)
|
|
|
|
{
|
|
|
|
value = (value * 8) + (ptr_string[i + 1] - '0');
|
|
|
|
}
|
|
|
|
output[pos_output++] = value;
|
|
|
|
ptr_string += 1 + i;
|
|
|
|
break;
|
|
|
|
case 'x': /* hexadecimal value (1 to 2 digits expected) */
|
|
|
|
case 'X':
|
|
|
|
if (isxdigit (ptr_string[1]))
|
|
|
|
{
|
|
|
|
value = 0;
|
|
|
|
for (i = 0; (i < 2) && isxdigit (ptr_string[i + 1]); i++)
|
2007-10-31 12:11:00 -04:00
|
|
|
{
|
2014-01-19 17:29:00 -05:00
|
|
|
value = (value * 16) + HEX2DEC(ptr_string[i + 1]);
|
2007-10-31 12:11:00 -04:00
|
|
|
}
|
2014-01-19 17:29:00 -05:00
|
|
|
output[pos_output++] = value;
|
|
|
|
ptr_string += 1 + i;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
output[pos_output++] = ptr_string[0];
|
|
|
|
ptr_string++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
output[pos_output++] = '\\';
|
|
|
|
output[pos_output++] = ptr_string[0];
|
|
|
|
ptr_string++;
|
|
|
|
break;
|
2007-10-31 12:11:00 -04:00
|
|
|
}
|
|
|
|
}
|
2014-01-19 17:29:00 -05:00
|
|
|
else
|
|
|
|
{
|
|
|
|
output[pos_output++] = ptr_string[0];
|
|
|
|
ptr_string++;
|
|
|
|
}
|
2007-10-31 12:11:00 -04:00
|
|
|
}
|
2014-01-19 17:29:00 -05:00
|
|
|
output[pos_output] = '\0';
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2007-10-31 12:11:00 -04:00
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2008-06-01 16:09:12 -04:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Checks if first char of string is a "word char".
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* 1: first char is a word char
|
|
|
|
* 0: first char is not a word char
|
2008-06-01 16:09:12 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
2008-06-03 04:56:51 -04:00
|
|
|
string_is_word_char (const char *string)
|
2008-06-01 16:09:12 -04:00
|
|
|
{
|
2009-04-15 05:33:45 -04:00
|
|
|
wint_t c = utf8_wide_char (string);
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-06-01 16:09:12 -04:00
|
|
|
if (c == WEOF)
|
|
|
|
return 0;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-06-01 16:09:12 -04:00
|
|
|
if (iswalnum (c))
|
|
|
|
return 1;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-06-01 16:09:12 -04:00
|
|
|
switch (c)
|
|
|
|
{
|
|
|
|
case '-':
|
|
|
|
case '_':
|
|
|
|
case '|':
|
|
|
|
return 1;
|
|
|
|
}
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-06-01 16:09:12 -04:00
|
|
|
/* not a 'word char' */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-01-22 07:48:42 -05:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Converts a mask (string with only "*" as wildcard) to a regex, paying
|
|
|
|
* attention to special chars in a regex.
|
2012-01-22 07:48:42 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
|
|
|
string_mask_to_regex (const char *mask)
|
|
|
|
{
|
|
|
|
char *result;
|
|
|
|
const char *ptr_mask;
|
|
|
|
int index_result;
|
2013-02-26 15:12:25 -05:00
|
|
|
char *regex_special_char = ".[]{}()?+|^$\\";
|
2012-01-22 07:48:42 -05:00
|
|
|
|
|
|
|
if (!mask)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
result = malloc ((strlen (mask) * 2) + 1);
|
|
|
|
if (!result)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
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];
|
|
|
|
|
|
|
|
ptr_mask++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add final '\0' */
|
|
|
|
result[index_result] = '\0';
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Extracts flags and regex from a string.
|
|
|
|
*
|
|
|
|
* Format of flags is: (?eins-eins)string
|
|
|
|
* Flags are:
|
|
|
|
* e: POSIX extended regex (REG_EXTENDED)
|
|
|
|
* i: case insensitive (REG_ICASE)
|
|
|
|
* n: match-any-character operators don't match a newline (REG_NEWLINE)
|
|
|
|
* s: support for substring addressing of matches is not required (REG_NOSUB)
|
|
|
|
*
|
|
|
|
* Examples (with default_flags = REG_EXTENDED):
|
|
|
|
* "(?i)toto" : regex "toto", flags = REG_EXTENDED | REG_ICASE
|
|
|
|
* "(?i)toto" : regex "toto", flags = REG_EXTENDED | REG_ICASE
|
|
|
|
* "(?i-e)toto": regex "toto", flags = REG_ICASE
|
2012-01-22 07:48:42 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
const char *
|
|
|
|
string_regex_flags (const char *regex, int default_flags, int *flags)
|
|
|
|
{
|
|
|
|
const char *ptr_regex, *ptr_flags;
|
|
|
|
int set_flag, flag;
|
|
|
|
char *pos;
|
|
|
|
|
|
|
|
ptr_regex = regex;
|
|
|
|
if (flags)
|
|
|
|
*flags = default_flags;
|
|
|
|
|
|
|
|
while (strncmp (ptr_regex, "(?", 2) == 0)
|
|
|
|
{
|
|
|
|
pos = strchr (ptr_regex, ')');
|
|
|
|
if (!pos)
|
|
|
|
break;
|
2012-02-05 05:25:38 -05:00
|
|
|
if (!isalpha ((unsigned char)ptr_regex[2]) && (ptr_regex[2] != '-'))
|
2012-01-22 07:48:42 -05:00
|
|
|
break;
|
|
|
|
if (flags)
|
|
|
|
{
|
|
|
|
set_flag = 1;
|
|
|
|
for (ptr_flags = ptr_regex + 2; ptr_flags < pos; ptr_flags++)
|
|
|
|
{
|
|
|
|
flag = 0;
|
|
|
|
switch (ptr_flags[0])
|
|
|
|
{
|
|
|
|
case '-':
|
|
|
|
set_flag = 0;
|
|
|
|
break;
|
|
|
|
case 'e':
|
|
|
|
flag = REG_EXTENDED;
|
|
|
|
break;
|
|
|
|
case 'i':
|
|
|
|
flag = REG_ICASE;
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
flag = REG_NEWLINE;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
flag = REG_NOSUB;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (flag > 0)
|
|
|
|
{
|
|
|
|
if (set_flag)
|
|
|
|
*flags |= flag;
|
|
|
|
else
|
|
|
|
*flags &= ~flag;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ptr_regex = pos + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ptr_regex;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Compiles a regex using optional flags at beginning of string (for format of
|
|
|
|
* flags in regex, see string_regex_flags()).
|
2013-08-12 04:51:49 -04:00
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* 0: successful compilation
|
|
|
|
* other value: compilation failed
|
2012-01-22 07:48:42 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
2012-01-26 12:18:20 -05:00
|
|
|
string_regcomp (void *preg, const char *regex, int default_flags)
|
2012-01-22 07:48:42 -05:00
|
|
|
{
|
|
|
|
const char *ptr_regex;
|
|
|
|
int flags;
|
|
|
|
|
|
|
|
ptr_regex = string_regex_flags (regex, default_flags, &flags);
|
2012-01-26 12:18:20 -05:00
|
|
|
return regcomp ((regex_t *)preg, ptr_regex, flags);
|
2012-01-22 07:48:42 -05:00
|
|
|
}
|
|
|
|
|
2008-06-01 16:09:12 -04:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Checks if a string has a highlight (using list of words to highlight).
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* 1: string has a highlight
|
|
|
|
* 0: string has no highlight
|
2008-06-01 16:09:12 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
2008-06-03 04:56:51 -04:00
|
|
|
string_has_highlight (const char *string, const char *highlight_words)
|
2008-06-01 16:09:12 -04:00
|
|
|
{
|
2012-01-22 07:48:42 -05:00
|
|
|
char *msg, *highlight, *match, *match_pre, *match_post, *msg_pos;
|
|
|
|
char *pos, *pos_end, *ptr_str, *ptr_string_ref;
|
|
|
|
int end, length, startswith, endswith, wildcard_start, wildcard_end, flags;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-06-01 16:09:12 -04:00
|
|
|
if (!string || !string[0] || !highlight_words || !highlight_words[0])
|
|
|
|
return 0;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-06-01 16:09:12 -04:00
|
|
|
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
|
|
|
|
2008-06-01 16:09:12 -04:00
|
|
|
pos = highlight;
|
|
|
|
end = 0;
|
|
|
|
while (!end)
|
|
|
|
{
|
2012-01-22 07:48:42 -05:00
|
|
|
ptr_string_ref = (char *)string;
|
|
|
|
flags = 0;
|
|
|
|
pos = (char *)string_regex_flags (pos, REG_ICASE, &flags);
|
|
|
|
|
2008-06-01 16:09:12 -04:00
|
|
|
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
|
|
|
|
2012-01-22 07:48:42 -05:00
|
|
|
if (flags & REG_ICASE)
|
|
|
|
{
|
|
|
|
for (ptr_str = pos; ptr_str < pos_end; ptr_str++)
|
|
|
|
{
|
|
|
|
if ((ptr_str[0] >= 'A') && (ptr_str[0] <= 'Z'))
|
|
|
|
ptr_str[0] += ('a' - 'A');
|
|
|
|
}
|
|
|
|
ptr_string_ref = msg;
|
|
|
|
}
|
|
|
|
|
2008-06-01 16:09:12 -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
|
|
|
|
2008-06-01 16:09:12 -04:00
|
|
|
if (length > 0)
|
|
|
|
{
|
2012-01-22 07:48:42 -05:00
|
|
|
msg_pos = ptr_string_ref;
|
2008-06-01 16:09:12 -04:00
|
|
|
/* highlight found! */
|
|
|
|
while ((match = strstr (msg_pos, pos)) != NULL)
|
|
|
|
{
|
2012-01-22 07:48:42 -05:00
|
|
|
match_pre = utf8_prev_char (ptr_string_ref, match);
|
2008-06-01 16:09:12 -04:00
|
|
|
if (!match_pre)
|
|
|
|
match_pre = match - 1;
|
|
|
|
match_post = match + length;
|
2012-01-22 07:48:42 -05:00
|
|
|
startswith = ((match == ptr_string_ref) || (!string_is_word_char (match_pre)));
|
2008-06-01 16:09:12 -04:00
|
|
|
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 &&
|
2008-06-01 16:09:12 -04:00
|
|
|
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
|
|
|
|
2008-06-01 16:09:12 -04:00
|
|
|
if (!end)
|
|
|
|
pos = pos_end + 1;
|
|
|
|
}
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-06-01 16:09:12 -04:00
|
|
|
free (msg);
|
|
|
|
free (highlight);
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-06-01 16:09:12 -04:00
|
|
|
/* no highlight found */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-11-25 15:28:14 -05:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Checks if a string has a highlight using a compiled regular expression (any
|
2014-01-13 10:09:01 -05:00
|
|
|
* match in string must be surrounded by delimiters).
|
2010-11-25 15:28:14 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
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
|
|
|
|
2010-11-25 15:28:14 -05:00
|
|
|
if (!string || !regex)
|
|
|
|
return 0;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-11-25 15:28:14 -05:00
|
|
|
while (string && string[0])
|
|
|
|
{
|
|
|
|
rc = regexec (regex, string, 1, ®ex_match, 0);
|
2013-01-18 03:03:57 -05:00
|
|
|
|
|
|
|
/*
|
|
|
|
* no match found: exit the loop (if rm_eo == 0, it is an empty match
|
|
|
|
* at beginning of string: we consider there is no match, to prevent an
|
|
|
|
* infinite loop)
|
|
|
|
*/
|
|
|
|
if ((rc != 0) || (regex_match.rm_so < 0) || (regex_match.rm_eo <= 0))
|
2010-11-25 15:28:14 -05:00
|
|
|
break;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-11-25 15:28:14 -05: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
|
|
|
|
2010-11-25 15:28:14 -05:00
|
|
|
string += regex_match.rm_eo;
|
|
|
|
}
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-11-25 15:28:14 -05:00
|
|
|
/* no highlight found */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Checks if a string has a highlight using a regular expression (any match in
|
2014-01-13 10:09:01 -05:00
|
|
|
* string must be surrounded by delimiters).
|
2010-11-25 15:28:14 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
string_has_highlight_regex (const char *string, const char *regex)
|
|
|
|
{
|
|
|
|
regex_t reg;
|
|
|
|
int rc;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-12-09 17:09:49 -05:00
|
|
|
if (!string || !regex || !regex[0])
|
2010-11-25 15:28:14 -05:00
|
|
|
return 0;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2012-01-22 07:48:42 -05:00
|
|
|
if (string_regcomp (®, regex, REG_EXTENDED | REG_ICASE) != 0)
|
2010-11-25 15:28:14 -05:00
|
|
|
return 0;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-11-25 15:28:14 -05:00
|
|
|
rc = string_has_highlight_regex_compiled (string, ®);
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-11-25 15:28:14 -05:00
|
|
|
regfree (®);
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-11-25 15:28:14 -05:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2007-10-31 12:11:00 -04:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Splits a string according to separators.
|
|
|
|
*
|
2013-08-10 11:31:48 -04:00
|
|
|
* This function must not be called directly (call string_split or
|
|
|
|
* string_split_shared instead).
|
|
|
|
*
|
2012-12-13 12:51:44 -05:00
|
|
|
* 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 **
|
2013-08-10 11:31:48 -04:00
|
|
|
string_split_internal (const char *string, const char *separators, int keep_eol,
|
|
|
|
int num_items_max, int *num_items, int shared)
|
2007-10-31 12:11:00 -04:00
|
|
|
{
|
2009-02-23 07:45:30 -05:00
|
|
|
int i, j, n_items;
|
2008-03-15 04:47:27 -04:00
|
|
|
char *string2, **array;
|
2007-10-31 12:11:00 -04:00
|
|
|
char *ptr, *ptr1, *ptr2;
|
2013-08-10 11:31:48 -04:00
|
|
|
const char *str_shared;
|
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
|
|
|
|
2007-12-17 11:07:08 -05: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
|
|
|
|
2011-10-16 14:12:55 -04:00
|
|
|
string2 = string_strip (string, 1, (keep_eol == 2) ? 0 : 1, separators);
|
2008-04-30 14:09:43 -04:00
|
|
|
if (!string2 || !string2[0])
|
2008-03-15 04:47:27 -04:00
|
|
|
return NULL;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2007-10-31 12:11:00 -04:00
|
|
|
/* calculate number of items */
|
2008-03-15 04:47:27 -04:00
|
|
|
ptr = string2;
|
2007-10-31 12:11:00 -04:00
|
|
|
i = 1;
|
|
|
|
while ((ptr = strpbrk (ptr, separators)))
|
|
|
|
{
|
2008-01-01 12:22:26 -05:00
|
|
|
while (ptr[0] && (strchr (separators, ptr[0]) != NULL))
|
2008-03-13 17:50:01 -04:00
|
|
|
{
|
2007-10-31 12:11:00 -04:00
|
|
|
ptr++;
|
2008-03-13 17:50:01 -04:00
|
|
|
}
|
2008-03-13 19:14:27 -04:00
|
|
|
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
|
|
|
|
2008-03-23 18:00:04 -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
|
|
|
|
2008-03-15 04:47:27 -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++)
|
|
|
|
{
|
2008-01-01 12:22:26 -05:00
|
|
|
while (ptr1[0] && (strchr (separators, ptr1[0]) != NULL))
|
2008-03-13 17:50:01 -04:00
|
|
|
{
|
2007-10-31 12:11:00 -04:00
|
|
|
ptr1++;
|
2008-03-13 17:50:01 -04:00
|
|
|
}
|
2008-03-15 04:47:27 -04:00
|
|
|
if (i == (n_items - 1))
|
|
|
|
{
|
|
|
|
ptr2 = strpbrk (ptr1, separators);
|
|
|
|
if (!ptr2)
|
|
|
|
ptr2 = strchr (ptr1, '\0');
|
|
|
|
}
|
|
|
|
else
|
2008-03-13 17:50:01 -04:00
|
|
|
{
|
2008-03-15 04:47:27 -04:00
|
|
|
if ((ptr2 = strpbrk (ptr1, separators)) == NULL)
|
2008-03-13 17:50:01 -04:00
|
|
|
{
|
2008-03-15 04:47:27 -04:00
|
|
|
if ((ptr2 = strchr (ptr1, '\r')) == NULL)
|
2008-03-13 17:50:01 -04:00
|
|
|
{
|
2008-03-15 04:47:27 -04:00
|
|
|
if ((ptr2 = strchr (ptr1, '\n')) == NULL)
|
|
|
|
{
|
|
|
|
ptr2 = strchr (ptr1, '\0');
|
|
|
|
}
|
2008-03-13 17:50:01 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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)
|
|
|
|
{
|
2007-11-05 07:29:54 -05:00
|
|
|
if (keep_eol)
|
2008-03-13 17:50:01 -04:00
|
|
|
{
|
2013-08-10 11:31:48 -04:00
|
|
|
array[i] = (shared) ? (char *)string_shared_get (ptr1) : strdup (ptr1);
|
2009-02-23 07:45:30 -05:00
|
|
|
if (!array[i])
|
|
|
|
{
|
|
|
|
for (j = 0; j < n_items; j++)
|
|
|
|
{
|
|
|
|
if (array[j])
|
2013-08-10 11:31:48 -04:00
|
|
|
{
|
|
|
|
if (shared)
|
|
|
|
string_shared_free (array[j]);
|
|
|
|
else
|
|
|
|
free (array[j]);
|
|
|
|
}
|
2009-02-23 07:45:30 -05:00
|
|
|
}
|
|
|
|
free (array);
|
|
|
|
free (string2);
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-03-13 17:50:01 -04:00
|
|
|
}
|
2007-11-05 07:29:54 -05:00
|
|
|
else
|
|
|
|
{
|
2008-03-23 18:00:04 -04:00
|
|
|
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])
|
2013-08-10 11:31:48 -04:00
|
|
|
{
|
|
|
|
if (shared)
|
|
|
|
string_shared_free (array[j]);
|
|
|
|
else
|
|
|
|
free (array[j]);
|
|
|
|
}
|
2009-02-23 07:45:30 -05:00
|
|
|
}
|
|
|
|
free (array);
|
|
|
|
free (string2);
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-03-22 18:36:12 -04:00
|
|
|
strncpy (array[i], ptr1, ptr2 - ptr1);
|
2007-11-05 07:29:54 -05:00
|
|
|
array[i][ptr2 - ptr1] = '\0';
|
2013-08-10 11:31:48 -04:00
|
|
|
if (shared)
|
|
|
|
{
|
|
|
|
str_shared = string_shared_get (array[i]);
|
|
|
|
if (!str_shared)
|
|
|
|
{
|
|
|
|
for (j = 0; j < n_items; j++)
|
|
|
|
{
|
|
|
|
if (array[j])
|
|
|
|
string_shared_free (array[j]);
|
|
|
|
}
|
|
|
|
free (array);
|
|
|
|
free (string2);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
free (array[i]);
|
|
|
|
array[i] = (char *)str_shared;
|
|
|
|
}
|
2007-11-05 07:29:54 -05:00
|
|
|
}
|
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
|
|
|
|
2008-03-22 18:36:12 -04:00
|
|
|
free (string2);
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2007-10-31 12:11:00 -04:00
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
2013-08-10 11:31:48 -04:00
|
|
|
/*
|
|
|
|
* Splits a string according to separators.
|
|
|
|
*
|
|
|
|
* For full description, see function string_split_internal.
|
|
|
|
*/
|
|
|
|
|
|
|
|
char **
|
|
|
|
string_split (const char *string, const char *separators, int keep_eol,
|
|
|
|
int num_items_max, int *num_items)
|
|
|
|
{
|
|
|
|
return string_split_internal (string, separators, keep_eol,
|
|
|
|
num_items_max, num_items, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Splits a string according to separators, and use shared strings for the
|
|
|
|
* strings in the array returned.
|
|
|
|
*
|
|
|
|
* For full description, see function string_split_internal.
|
|
|
|
*/
|
|
|
|
|
|
|
|
char **
|
|
|
|
string_split_shared (const char *string, const char *separators, int keep_eol,
|
|
|
|
int num_items_max, int *num_items)
|
|
|
|
{
|
|
|
|
return string_split_internal (string, separators, keep_eol,
|
|
|
|
num_items_max, num_items, 1);
|
|
|
|
}
|
|
|
|
|
2012-11-18 04:38:30 -05:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Splits a string like the shell does for a command with arguments.
|
|
|
|
*
|
|
|
|
* This function is a C conversion of python class "shlex"
|
|
|
|
* (file: Lib/shlex.py in python repository)
|
|
|
|
* Doc: http://docs.python.org/3/library/shlex.html
|
|
|
|
*
|
|
|
|
* Copyrights in shlex.py:
|
|
|
|
* Module and documentation by Eric S. Raymond, 21 Dec 1998
|
|
|
|
* Input stacking and error message cleanup added by ESR, March 2000
|
|
|
|
* push_source() and pop_source() made explicit by ESR, January 2001.
|
|
|
|
* Posix compliance, split(), string arguments, and
|
|
|
|
* iterator interface by Gustavo Niemeyer, April 2003.
|
|
|
|
*
|
|
|
|
* Note: result must be freed with string_free_split.
|
2012-11-18 04:38:30 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
char **
|
|
|
|
string_split_shell (const char *string)
|
|
|
|
{
|
|
|
|
int temp_len, num_args, add_char_to_temp, add_temp_to_args, quoted;
|
|
|
|
char *string2, *temp, **args, **args2, state, escapedstate;
|
|
|
|
char *ptr_string, *ptr_next, saved_char;
|
|
|
|
|
|
|
|
if (!string)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
string2 = strdup (string);
|
|
|
|
if (!string2)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* prepare "args" with one pointer to NULL, the "args" will be reallocated
|
|
|
|
* later, each time a new argument is added
|
|
|
|
*/
|
|
|
|
num_args = 0;
|
|
|
|
args = malloc ((num_args + 1) * sizeof (args[0]));
|
|
|
|
if (!args)
|
|
|
|
{
|
|
|
|
free (string2);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
args[0] = NULL;
|
|
|
|
|
|
|
|
/* prepare a temp string for working (adding chars one by one) */
|
|
|
|
temp = malloc ((2 * strlen (string)) + 1);
|
|
|
|
if (!temp)
|
|
|
|
{
|
|
|
|
free (string2);
|
|
|
|
free (args);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
temp[0] = '\0';
|
|
|
|
temp_len = 0;
|
|
|
|
|
|
|
|
state = ' ';
|
|
|
|
escapedstate = ' ';
|
|
|
|
quoted = 0;
|
|
|
|
ptr_string = string2;
|
|
|
|
while (ptr_string[0])
|
|
|
|
{
|
|
|
|
add_char_to_temp = 0;
|
|
|
|
add_temp_to_args = 0;
|
|
|
|
ptr_next = utf8_next_char (ptr_string);
|
|
|
|
saved_char = ptr_next[0];
|
|
|
|
ptr_next[0] = '\0';
|
|
|
|
if (state == ' ')
|
|
|
|
{
|
|
|
|
if ((ptr_string[0] == ' ') || (ptr_string[0] == '\t')
|
|
|
|
|| (ptr_string[0] == '\r') || (ptr_string[0] == '\n'))
|
|
|
|
{
|
|
|
|
if (temp[0] || quoted)
|
|
|
|
add_temp_to_args = 1;
|
|
|
|
}
|
|
|
|
else if (ptr_string[0] == '\\')
|
|
|
|
{
|
|
|
|
escapedstate = 'a';
|
|
|
|
state = ptr_string[0];
|
|
|
|
}
|
|
|
|
else if ((ptr_string[0] == '\'') || (ptr_string[0] == '"'))
|
|
|
|
{
|
|
|
|
state = ptr_string[0];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
add_char_to_temp = 1;
|
|
|
|
state = 'a';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((state == '\'') || (state == '"'))
|
|
|
|
{
|
|
|
|
quoted = 1;
|
|
|
|
if (ptr_string[0] == state)
|
|
|
|
{
|
|
|
|
state = 'a';
|
|
|
|
}
|
|
|
|
else if ((state == '"') && (ptr_string[0] == '\\'))
|
|
|
|
{
|
|
|
|
escapedstate = state;
|
|
|
|
state = ptr_string[0];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
add_char_to_temp = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (state == '\\')
|
|
|
|
{
|
|
|
|
if (((escapedstate == '\'') || (escapedstate == '"'))
|
|
|
|
&& (ptr_string[0] != state) && (ptr_string[0] != escapedstate))
|
|
|
|
{
|
|
|
|
temp[temp_len] = state;
|
|
|
|
temp_len++;
|
|
|
|
temp[temp_len] = '\0';
|
|
|
|
}
|
|
|
|
add_char_to_temp = 1;
|
|
|
|
state = escapedstate;
|
|
|
|
}
|
|
|
|
else if (state == 'a')
|
|
|
|
{
|
|
|
|
if ((ptr_string[0] == ' ') || (ptr_string[0] == '\t')
|
|
|
|
|| (ptr_string[0] == '\r') || (ptr_string[0] == '\n'))
|
|
|
|
{
|
|
|
|
state = ' ';
|
|
|
|
if (temp[0] || quoted)
|
|
|
|
add_temp_to_args = 1;
|
|
|
|
}
|
|
|
|
else if (ptr_string[0] == '\\')
|
|
|
|
{
|
|
|
|
escapedstate = 'a';
|
|
|
|
state = ptr_string[0];
|
|
|
|
}
|
|
|
|
else if ((ptr_string[0] == '\'') || (ptr_string[0] == '"'))
|
|
|
|
{
|
|
|
|
state = ptr_string[0];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
add_char_to_temp = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (add_char_to_temp)
|
|
|
|
{
|
|
|
|
memcpy (temp + temp_len, ptr_string, ptr_next - ptr_string);
|
|
|
|
temp_len += (ptr_next - ptr_string);
|
|
|
|
temp[temp_len] = '\0';
|
|
|
|
}
|
|
|
|
if (add_temp_to_args)
|
|
|
|
{
|
|
|
|
num_args++;
|
|
|
|
args2 = realloc (args, (num_args + 1) * sizeof (args[0]));
|
|
|
|
if (!args2)
|
|
|
|
{
|
|
|
|
free (string2);
|
|
|
|
free (temp);
|
|
|
|
return args;
|
|
|
|
}
|
|
|
|
args = args2;
|
|
|
|
args[num_args - 1] = strdup (temp);
|
|
|
|
args[num_args] = NULL;
|
|
|
|
temp[0] = '\0';
|
|
|
|
temp_len = 0;
|
|
|
|
escapedstate = ' ';
|
|
|
|
quoted = 0;
|
|
|
|
}
|
|
|
|
ptr_next[0] = saved_char;
|
|
|
|
ptr_string = ptr_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (temp[0] || (state != ' '))
|
|
|
|
{
|
|
|
|
num_args++;
|
|
|
|
args2 = realloc (args, (num_args + 1) * sizeof (args[0]));
|
|
|
|
if (!args2)
|
|
|
|
{
|
|
|
|
free (string2);
|
|
|
|
free (temp);
|
|
|
|
return args;
|
|
|
|
}
|
|
|
|
args = args2;
|
|
|
|
args[num_args - 1] = strdup (temp);
|
|
|
|
args[num_args] = NULL;
|
|
|
|
temp[0] = '\0';
|
|
|
|
temp_len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
free (string2);
|
|
|
|
free (temp);
|
|
|
|
|
|
|
|
return args;
|
|
|
|
}
|
|
|
|
|
2007-10-31 12:11:00 -04:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Frees a split string.
|
2007-10-31 12:11:00 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2009-06-28 13:49:32 -04:00
|
|
|
string_free_split (char **split_string)
|
2007-10-31 12:11:00 -04:00
|
|
|
{
|
|
|
|
int i;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2009-06-28 13:49:32 -04:00
|
|
|
if (split_string)
|
2007-10-31 12:11:00 -04:00
|
|
|
{
|
2009-06-28 13:49:32 -04:00
|
|
|
for (i = 0; split_string[i]; i++)
|
|
|
|
free (split_string[i]);
|
|
|
|
free (split_string);
|
2007-10-31 12:11:00 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-10 11:31:48 -04:00
|
|
|
/*
|
|
|
|
* Frees a split string (using shared strings).
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
string_free_split_shared (char **split_string)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (split_string)
|
|
|
|
{
|
|
|
|
for (i = 0; split_string[i]; i++)
|
|
|
|
string_shared_free (split_string[i]);
|
|
|
|
free (split_string);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-22 18:36:12 -04:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Builds a string with a split string.
|
|
|
|
*
|
|
|
|
* Note: result must be free after use.
|
2008-03-22 18:36:12 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
2009-06-28 13:49:32 -04:00
|
|
|
string_build_with_split_string (const char **split_string,
|
|
|
|
const char *separator)
|
2008-03-22 18:36:12 -04:00
|
|
|
{
|
|
|
|
int i, length, length_separator;
|
|
|
|
char *result;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2009-06-28 13:49:32 -04:00
|
|
|
if (!split_string)
|
2008-03-22 18:36:12 -04:00
|
|
|
return NULL;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-03-22 18:36:12 -04:00
|
|
|
length = 0;
|
2008-08-24 02:57:59 -04:00
|
|
|
length_separator = (separator) ? strlen (separator) : 0;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2009-06-28 13:49:32 -04:00
|
|
|
for (i = 0; split_string[i]; i++)
|
2008-03-22 18:36:12 -04:00
|
|
|
{
|
2009-06-28 13:49:32 -04:00
|
|
|
length += strlen (split_string[i]) + length_separator;
|
2008-03-22 18:36:12 -04:00
|
|
|
}
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-03-23 18:00:04 -04:00
|
|
|
result = malloc (length + 1);
|
2008-08-24 02:57:59 -04:00
|
|
|
if (result)
|
2008-03-22 18:36:12 -04:00
|
|
|
{
|
2008-08-24 02:57:59 -04:00
|
|
|
result[0] = '\0';
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2009-06-28 13:49:32 -04:00
|
|
|
for (i = 0; split_string[i]; i++)
|
2008-08-24 02:57:59 -04:00
|
|
|
{
|
2009-06-28 13:49:32 -04:00
|
|
|
strcat (result, split_string[i]);
|
|
|
|
if (separator && split_string[i + 1])
|
2008-08-24 02:57:59 -04:00
|
|
|
strcat (result, separator);
|
|
|
|
}
|
2008-03-22 18:36:12 -04:00
|
|
|
}
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-03-22 18:36:12 -04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2007-10-31 12:11:00 -04:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Splits a list of commands separated by 'separator' and escaped with '\'.
|
|
|
|
* Empty commands are removed, spaces on the left of each commands are stripped.
|
|
|
|
*
|
|
|
|
* Note: result must be freed with free_multi_command.
|
2007-10-31 12:11:00 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
char **
|
2008-06-03 04:56:51 -04:00
|
|
|
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;
|
2008-06-03 04:56:51 -04:00
|
|
|
char *buffer, *p;
|
|
|
|
const char *ptr;
|
2007-10-31 12:11:00 -04:00
|
|
|
|
2007-12-17 11:07:08 -05: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;
|
2007-12-17 11:07:08 -05:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2008-03-23 18:00:04 -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
|
|
|
|
2008-03-23 18:00:04 -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;
|
2013-03-17 07:55:20 -04:00
|
|
|
/* strip white spaces a the beginning of the line */
|
2010-01-24 04:47:53 -05:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Frees a command split.
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Converts a string to another charset.
|
|
|
|
*
|
|
|
|
* Note: result must be freed after use.
|
2007-10-31 12:11:00 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
2008-06-03 04:56:51 -04:00
|
|
|
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;
|
2008-03-23 18:00:04 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Converts a string to WeeChat internal storage charset (UTF-8).
|
|
|
|
*
|
2013-08-04 02:56:56 -04:00
|
|
|
* Note: result must be freed after use.
|
2007-10-31 12:11:00 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
2008-06-03 04:56:51 -04:00
|
|
|
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
|
|
|
|
2007-12-17 11:07:08 -05: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
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Converts internal string to terminal charset, for display.
|
|
|
|
*
|
2013-08-04 02:56:56 -04:00
|
|
|
* Note: result must be freed after use.
|
2007-10-31 12:11:00 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
2008-06-03 04:56:51 -04:00
|
|
|
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
|
|
|
|
2007-12-17 11:07:08 -05: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
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Encodes a string to terminal charset and calls fprintf.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* 1: OK
|
|
|
|
* 0: error
|
2007-10-31 12:11:00 -04:00
|
|
|
*/
|
|
|
|
|
2010-03-26 14:01:25 -04:00
|
|
|
int
|
2008-06-03 04:56:51 -04:00
|
|
|
string_iconv_fprintf (FILE *file, const char *data, ...)
|
2007-10-31 12:11:00 -04:00
|
|
|
{
|
2011-08-26 03:55:55 -04:00
|
|
|
char *buf2;
|
2010-03-26 14:01:25 -04:00
|
|
|
int rc, num_written;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2011-08-26 03:55:55 -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
|
|
|
|
2010-03-26 14:01:25 -04:00
|
|
|
return rc;
|
2007-10-31 12:11:00 -04:00
|
|
|
}
|
2008-12-10 11:08:56 -05:00
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Formats a string with size and unit name (bytes, KB, MB, GB).
|
|
|
|
*
|
2013-08-04 02:56:56 -04:00
|
|
|
* Note: result must be freed after use.
|
2008-12-10 11:08:56 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
2010-11-10 10:34:56 -05:00
|
|
|
string_format_size (unsigned long long size)
|
2008-12-10 11:08:56 -05:00
|
|
|
{
|
|
|
|
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
|
|
|
|
2008-12-10 11:08:56 -05:00
|
|
|
str_size[0] = '\0';
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2008-12-10 11:08:56 -05: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
|
|
|
|
2008-12-10 11:08:56 -05: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
|
|
|
|
2008-12-10 11:08:56 -05:00
|
|
|
return strdup (str_size);
|
|
|
|
}
|
2010-02-15 05:51:44 -05:00
|
|
|
|
2013-07-27 06:57:08 -04:00
|
|
|
/*
|
|
|
|
* Encodes a string in base16 (hexadecimal).
|
|
|
|
*
|
|
|
|
* Argument "length" is number of bytes in "from" to convert (commonly
|
|
|
|
* strlen(from)).
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
string_encode_base16 (const char *from, int length, char *to)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
const char *hexa = "0123456789ABCDEF";
|
|
|
|
char *ptr_to;
|
|
|
|
|
|
|
|
ptr_to = to;
|
|
|
|
ptr_to[0] = '\0';
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
{
|
|
|
|
ptr_to[0] = hexa[((unsigned char)from[i]) / 16];
|
|
|
|
ptr_to[1] = hexa[((unsigned char)from[i]) % 16];
|
|
|
|
ptr_to += 2;
|
|
|
|
}
|
|
|
|
ptr_to[0] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decodes a base16 string (hexadecimal).
|
|
|
|
*
|
|
|
|
* Returns length of string in "*to" (it does not count final \0).
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
string_decode_base16 (const char *from, char *to)
|
|
|
|
{
|
|
|
|
int length, to_length, i, pos;
|
|
|
|
unsigned char *ptr_to, value;
|
|
|
|
|
|
|
|
length = strlen (from) / 2;
|
|
|
|
|
|
|
|
ptr_to = (unsigned char *)to;
|
|
|
|
ptr_to[0] = '\0';
|
|
|
|
to_length = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
{
|
|
|
|
pos = i * 2;
|
|
|
|
value = 0;
|
|
|
|
/* 4 bits on the left */
|
|
|
|
if ((from[pos] >= '0') && (from[pos] <= '9'))
|
|
|
|
value |= (from[pos] - '0') << 4;
|
|
|
|
else if ((from[pos] >= 'a') && (from[pos] <= 'f'))
|
|
|
|
value |= (from[pos] - 'a' + 10) << 4;
|
|
|
|
else if ((from[pos] >= 'A') && (from[pos] <= 'F'))
|
|
|
|
value |= (from[pos] - 'A' + 10) << 4;
|
|
|
|
/* 4 bits on the right */
|
|
|
|
pos++;
|
|
|
|
if ((from[pos] >= '0') && (from[pos] <= '9'))
|
|
|
|
value |= from[pos] - '0';
|
|
|
|
else if ((from[pos] >= 'a') && (from[pos] <= 'f'))
|
|
|
|
value |= from[pos] - 'a' + 10;
|
|
|
|
else if ((from[pos] >= 'A') && (from[pos] <= 'F'))
|
|
|
|
value |= from[pos] - 'A' + 10;
|
|
|
|
|
|
|
|
ptr_to[0] = value;
|
|
|
|
ptr_to++;
|
|
|
|
to_length++;
|
|
|
|
}
|
|
|
|
ptr_to[0] = '\0';
|
|
|
|
|
|
|
|
return to_length;
|
|
|
|
}
|
|
|
|
|
2010-02-15 05:51:44 -05:00
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Converts 3 bytes of 8 bits in 4 bytes of 6 bits.
|
2010-02-15 05:51:44 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
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
|
|
|
|
2010-02-15 05:51:44 -05: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 ];
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Encodes a string in base64.
|
|
|
|
*
|
|
|
|
* Argument "length" is number of bytes in "from" to convert (commonly
|
|
|
|
* strlen(from)).
|
2010-02-15 05:51:44 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
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
|
|
|
|
2010-02-15 05:51:44 -05:00
|
|
|
ptr_from = from;
|
|
|
|
ptr_to = to;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-02-15 05:51:44 -05: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
|
|
|
|
2010-02-15 05:51:44 -05: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';
|
|
|
|
}
|
2010-02-16 10:57:22 -05:00
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Converts 4 bytes of 6 bits to 3 bytes of 8 bits.
|
2010-02-16 10:57:22 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Decodes a base64 string.
|
|
|
|
*
|
|
|
|
* Returns length of string in "*to" (it does not count final \0).
|
2010-02-16 10:57:22 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
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
|
|
|
|
2010-02-16 10:57:22 -05:00
|
|
|
ptr_from = from;
|
|
|
|
ptr_to = to;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-02-16 10:57:22 -05:00
|
|
|
ptr_to[0] = '\0';
|
|
|
|
to_length = 0;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-02-16 10:57:22 -05: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
|
|
|
|
2010-02-16 10:57:22 -05:00
|
|
|
ptr_to[0] = '\0';
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-02-16 10:57:22 -05:00
|
|
|
return to_length;
|
|
|
|
}
|
2010-03-02 11:34:49 -05:00
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Checks if a string is a command.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* 1: first char of string is a command char
|
|
|
|
* 0: string is not a command
|
2010-03-02 11:34:49 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
string_is_command_char (const char *string)
|
|
|
|
{
|
|
|
|
const char *ptr_command_chars;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-03-02 11:34:49 -05:00
|
|
|
if (!string)
|
|
|
|
return 0;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-03-02 11:34:49 -05:00
|
|
|
if (string[0] == '/')
|
|
|
|
return 1;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-03-02 11:34:49 -05: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
|
|
|
|
2010-03-02 11:34:49 -05: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
|
|
|
|
2010-03-02 11:34:49 -05:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Gets pointer to input text for buffer.
|
|
|
|
*
|
|
|
|
* Returns pointer inside "string" argument or NULL if it's a command (by
|
|
|
|
* default a command starts with a single '/').
|
2010-03-02 11:34:49 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
const char *
|
|
|
|
string_input_for_buffer (const char *string)
|
|
|
|
{
|
|
|
|
char *pos_slash, *pos_space, *next_char;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-03-02 11:34:49 -05: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
|
|
|
|
*/
|
2010-03-02 11:34:49 -05:00
|
|
|
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
|
|
|
/*
|
2012-12-25 04:54:51 -05:00
|
|
|
* if there's no other '/' or if '/' is after first space,
|
2010-03-19 18:33:14 -04:00
|
|
|
* then it is a command, and return NULL
|
|
|
|
*/
|
2010-03-02 11:34:49 -05:00
|
|
|
if (!pos_slash || (pos_space && pos_slash > pos_space))
|
|
|
|
return NULL;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-03-02 11:34:49 -05:00
|
|
|
return (string[1] == '/') ? string + 1 : string;
|
|
|
|
}
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-03-02 11:34:49 -05: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
|
|
|
|
2010-03-02 11:34:49 -05:00
|
|
|
next_char = utf8_next_char (string);
|
2012-12-25 04:54:51 -05:00
|
|
|
|
|
|
|
/* there's no next char, then it's a command */
|
2010-03-02 11:34:49 -05:00
|
|
|
if (!next_char || !next_char[0])
|
2012-12-25 04:54:51 -05:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* check if first char is doubled: if yes, then it's not a command */
|
2010-03-02 11:34:49 -05:00
|
|
|
if (utf8_charcmp (string, next_char) == 0)
|
|
|
|
return next_char;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2010-03-02 11:34:49 -05:00
|
|
|
/* string is a command */
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-07-26 12:50:29 -04:00
|
|
|
|
|
|
|
/*
|
2012-12-13 12:51:44 -05:00
|
|
|
* Replaces ${codes} using a callback that returns replacement value (this value
|
|
|
|
* must be newly allocated because it will be freed in this function).
|
|
|
|
*
|
|
|
|
* Argument "errors" is set with number of keys not found by callback.
|
2013-08-04 02:56:56 -04:00
|
|
|
*
|
|
|
|
* Note: result must be freed after use.
|
2011-07-26 12:50:29 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
2012-11-02 04:37:15 -04:00
|
|
|
string_replace_with_callback (const char *string,
|
2013-07-27 06:57:08 -04:00
|
|
|
const char *prefix,
|
|
|
|
const char *suffix,
|
2012-11-02 04:37:15 -04:00
|
|
|
char *(*callback)(void *data, const char *text),
|
|
|
|
void *callback_data,
|
|
|
|
int *errors)
|
2011-07-26 12:50:29 -04:00
|
|
|
{
|
2013-07-27 06:57:08 -04:00
|
|
|
int length_prefix, length_suffix, length, length_value, index_string;
|
|
|
|
int index_result;
|
2012-11-02 04:37:15 -04:00
|
|
|
char *result, *result2, *key, *value;
|
|
|
|
const char *pos_end_name;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2011-07-26 12:50:29 -04:00
|
|
|
*errors = 0;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2013-07-27 06:57:08 -04:00
|
|
|
if (!string || !prefix || !prefix[0] || !suffix || !suffix[0])
|
2011-07-26 12:50:29 -04:00
|
|
|
return NULL;
|
2011-10-26 14:37:03 -04:00
|
|
|
|
2013-07-27 06:57:08 -04:00
|
|
|
length_prefix = strlen (prefix);
|
|
|
|
length_suffix = strlen (suffix);
|
|
|
|
|
2011-07-26 12:50:29 -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] == '\\')
|
2013-07-27 06:57:08 -04:00
|
|
|
&& (string[index_string + 1] == prefix[0]))
|
2011-07-26 12:50:29 -04:00
|
|
|
{
|
|
|
|
index_string++;
|
|
|
|
result[index_result++] = string[index_string++];
|
|
|
|
}
|
2013-07-27 06:57:08 -04:00
|
|
|
else if (strncmp (string + index_string, prefix, length_prefix) == 0)
|
2011-07-26 12:50:29 -04:00
|
|
|
{
|
2013-07-27 06:57:08 -04:00
|
|
|
pos_end_name = strstr (string + index_string + length_prefix, suffix);
|
2011-07-26 12:50:29 -04:00
|
|
|
if (pos_end_name)
|
|
|
|
{
|
2013-07-27 06:57:08 -04:00
|
|
|
key = string_strndup (string + index_string + length_prefix,
|
|
|
|
pos_end_name - (string + index_string + length_prefix));
|
2011-07-26 12:50:29 -04:00
|
|
|
if (key)
|
|
|
|
{
|
2012-11-02 04:37:15 -04:00
|
|
|
value = (*callback) (callback_data, key);
|
|
|
|
if (value)
|
2011-07-26 12:50:29 -04:00
|
|
|
{
|
2012-11-02 04:37:15 -04:00
|
|
|
length_value = strlen (value);
|
2011-07-26 12:50:29 -04:00
|
|
|
length += length_value;
|
2011-08-28 09:25:30 -04:00
|
|
|
result2 = realloc (result, length);
|
|
|
|
if (!result2)
|
2011-07-26 12:50:29 -04:00
|
|
|
{
|
2011-08-28 09:25:30 -04:00
|
|
|
if (result)
|
|
|
|
free (result);
|
2011-07-26 12:50:29 -04:00
|
|
|
free (key);
|
2012-11-02 04:37:15 -04:00
|
|
|
free (value);
|
2011-07-26 12:50:29 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
2011-08-28 09:25:30 -04:00
|
|
|
result = result2;
|
2012-11-02 04:37:15 -04:00
|
|
|
strcpy (result + index_result, value);
|
2011-07-26 12:50:29 -04:00
|
|
|
index_result += length_value;
|
|
|
|
index_string += pos_end_name - string -
|
2013-07-27 06:57:08 -04:00
|
|
|
index_string + length_suffix;
|
2012-11-02 04:37:15 -04:00
|
|
|
free (value);
|
2011-07-26 12:50:29 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result[index_result++] = string[index_string++];
|
|
|
|
(*errors)++;
|
|
|
|
}
|
|
|
|
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
|
|
|
|
2011-07-26 12:50:29 -04:00
|
|
|
return result;
|
|
|
|
}
|
2013-08-10 11:25:14 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Hashes a shared string.
|
|
|
|
* The string starts after the reference count, which is skipped.
|
|
|
|
*
|
|
|
|
* Returns the hash of the shared string (variant of djb2).
|
|
|
|
*/
|
|
|
|
|
|
|
|
unsigned long
|
|
|
|
string_shared_hash_key (struct t_hashtable *hashtable,
|
|
|
|
const void *key)
|
|
|
|
{
|
|
|
|
/* make C compiler happy */
|
|
|
|
(void) hashtable;
|
|
|
|
|
|
|
|
return hashtable_hash_key_djb2 (((const char *)key) + sizeof (string_shared_count_t));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compares two shared strings.
|
|
|
|
* Each string starts after the reference count, which is skipped.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* < 0: key1 < key2
|
|
|
|
* 0: key1 == key2
|
|
|
|
* > 0: key1 > key2
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
string_shared_keycmp (struct t_hashtable *hashtable,
|
|
|
|
const void *key1, const void *key2)
|
|
|
|
{
|
|
|
|
/* make C compiler happy */
|
|
|
|
(void) hashtable;
|
|
|
|
|
|
|
|
return strcmp (((const char *)key1) + sizeof (string_shared_count_t),
|
|
|
|
((const char *)key2) + sizeof (string_shared_count_t));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Frees a shared string.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
string_shared_free_key (struct t_hashtable *hashtable,
|
|
|
|
void *key, const void *value)
|
|
|
|
{
|
|
|
|
/* make C compiler happy */
|
|
|
|
(void) hashtable;
|
|
|
|
(void) value;
|
|
|
|
|
|
|
|
free (key);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Gets a pointer to a shared string.
|
|
|
|
*
|
|
|
|
* A shared string is an entry in the hashtable "string_hashtable_shared", with:
|
|
|
|
* - key: reference count (unsigned integer on 32 bits) + string
|
|
|
|
* - value: NULL pointer (not used)
|
|
|
|
*
|
|
|
|
* The initial reference count is set to 1 and is incremented each time this
|
|
|
|
* function is called for a same string (string content, not the pointer).
|
|
|
|
*
|
|
|
|
* Returns the pointer to the shared string (start of string in key, after the
|
|
|
|
* reference count), NULL if error.
|
|
|
|
* The string returned has exactly same content as string received in argument,
|
|
|
|
* but the pointer to the string is different.
|
2013-08-17 11:44:10 -04:00
|
|
|
*
|
|
|
|
* IMPORTANT: the returned string must NEVER be changed in any way, because it
|
|
|
|
* is used itself as the key of the hashtable.
|
2013-08-10 11:25:14 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
const char *
|
|
|
|
string_shared_get (const char *string)
|
|
|
|
{
|
|
|
|
struct t_hashtable_item *ptr_item;
|
|
|
|
char *key;
|
|
|
|
int length;
|
|
|
|
|
|
|
|
if (!string_hashtable_shared)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* use large htable inside hashtable to prevent too many collisions,
|
|
|
|
* which would slow down search of a string in the hashtable
|
|
|
|
*/
|
|
|
|
string_hashtable_shared = hashtable_new (1024,
|
|
|
|
WEECHAT_HASHTABLE_POINTER,
|
|
|
|
WEECHAT_HASHTABLE_POINTER,
|
|
|
|
&string_shared_hash_key,
|
|
|
|
&string_shared_keycmp);
|
|
|
|
if (!string_hashtable_shared)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
string_hashtable_shared->callback_free_key = &string_shared_free_key;
|
|
|
|
}
|
|
|
|
|
|
|
|
length = sizeof (string_shared_count_t) + strlen (string) + 1;
|
|
|
|
key = malloc (length);
|
|
|
|
if (!key)
|
|
|
|
return NULL;
|
|
|
|
*((string_shared_count_t *)key) = 1;
|
|
|
|
strcpy (key + sizeof (string_shared_count_t), string);
|
|
|
|
|
|
|
|
ptr_item = hashtable_get_item (string_hashtable_shared, key, NULL);
|
|
|
|
if (ptr_item)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* the string already exists in the hashtable, then just increase the
|
|
|
|
* reference count on the string
|
|
|
|
*/
|
|
|
|
(*((string_shared_count_t *)(ptr_item->key)))++;
|
|
|
|
free (key);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* add the shared string in the hashtable */
|
|
|
|
ptr_item = hashtable_set (string_hashtable_shared, key, NULL);
|
|
|
|
if (!ptr_item)
|
|
|
|
free (key);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ptr_item) ?
|
|
|
|
((const char *)ptr_item->key) + sizeof (string_shared_count_t) : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Frees a shared string.
|
|
|
|
*
|
|
|
|
* The reference count of the string is decremented. If it becomes 0, then the
|
|
|
|
* shared string is removed from the hashtable (and then the string is really
|
|
|
|
* destroyed).
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
string_shared_free (const char *string)
|
|
|
|
{
|
|
|
|
string_shared_count_t *ptr_count;
|
|
|
|
|
|
|
|
ptr_count = (string_shared_count_t *)(string - sizeof (string_shared_count_t));
|
|
|
|
|
|
|
|
(*ptr_count)--;
|
|
|
|
|
|
|
|
if (*ptr_count == 0)
|
|
|
|
hashtable_remove (string_hashtable_shared, ptr_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Frees all allocated data.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
string_end ()
|
|
|
|
{
|
|
|
|
if (string_hashtable_shared)
|
|
|
|
{
|
|
|
|
hashtable_free (string_hashtable_shared);
|
|
|
|
string_hashtable_shared = NULL;
|
|
|
|
}
|
|
|
|
}
|