2000-09-11 16:16:13 +00:00
|
|
|
/* strutil.c
|
|
|
|
* String utility routines
|
|
|
|
*
|
2004-07-18 00:24:25 +00:00
|
|
|
* $Id$
|
2000-09-11 16:16:13 +00:00
|
|
|
*
|
|
|
|
* Ethereal - Network traffic analyzer
|
2002-12-31 21:51:10 +00:00
|
|
|
* By Gerald Combs <gerald@ethereal.com>
|
2000-09-11 16:16:13 +00:00
|
|
|
* Copyright 1998 Gerald Combs
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
2002-08-28 20:41:00 +00:00
|
|
|
*
|
2000-09-11 16:16:13 +00:00
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* 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.
|
2002-08-28 20:41:00 +00:00
|
|
|
*
|
2000-09-11 16:16:13 +00:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <glib.h>
|
2000-09-11 20:05:13 +00:00
|
|
|
#include "strutil.h"
|
2000-09-11 16:16:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given a pointer into a data buffer, and to the end of the buffer,
|
|
|
|
* find the end of the (putative) line at that position in the data
|
|
|
|
* buffer.
|
|
|
|
* Return a pointer to the EOL character(s) in "*eol".
|
|
|
|
*/
|
2002-08-02 21:29:45 +00:00
|
|
|
const guchar *
|
|
|
|
find_line_end(const guchar *data, const guchar *dataend, const guchar **eol)
|
2000-09-11 16:16:13 +00:00
|
|
|
{
|
2002-08-02 21:29:45 +00:00
|
|
|
const guchar *lineend;
|
2000-09-11 16:16:13 +00:00
|
|
|
|
|
|
|
lineend = memchr(data, '\n', dataend - data);
|
|
|
|
if (lineend == NULL) {
|
|
|
|
/*
|
|
|
|
* No LF - line is probably continued in next TCP segment.
|
|
|
|
*/
|
|
|
|
lineend = dataend;
|
|
|
|
*eol = dataend;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Is the LF at the beginning of the line?
|
|
|
|
*/
|
|
|
|
if (lineend > data) {
|
|
|
|
/*
|
|
|
|
* No - is it preceded by a carriage return?
|
|
|
|
* (Perhaps it's supposed to be, but that's not guaranteed....)
|
|
|
|
*/
|
|
|
|
if (*(lineend - 1) == '\r') {
|
|
|
|
/*
|
|
|
|
* Yes. The EOL starts with the CR.
|
|
|
|
*/
|
|
|
|
*eol = lineend - 1;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* No. The EOL starts with the LF.
|
|
|
|
*/
|
|
|
|
*eol = lineend;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* I seem to remember that we once saw lines ending with LF-CR
|
|
|
|
* in an HTTP request or response, so check if it's *followed*
|
|
|
|
* by a carriage return.
|
|
|
|
*/
|
|
|
|
if (lineend < (dataend - 1) && *(lineend + 1) == '\r') {
|
|
|
|
/*
|
|
|
|
* It's <non-LF><LF><CR>; say it ends with the CR.
|
|
|
|
*/
|
|
|
|
lineend++;
|
|
|
|
}
|
|
|
|
}
|
2000-09-30 05:44:48 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Yes - the EOL starts with the LF.
|
|
|
|
*/
|
|
|
|
*eol = lineend;
|
2000-09-11 16:16:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Point to the character after the last character.
|
|
|
|
*/
|
|
|
|
lineend++;
|
|
|
|
}
|
|
|
|
return lineend;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the length of the next token in a line, and the beginning of the
|
|
|
|
* next token after that (if any).
|
|
|
|
* Return 0 if there is no next token.
|
|
|
|
*/
|
|
|
|
int
|
2002-08-02 21:29:45 +00:00
|
|
|
get_token_len(const guchar *linep, const guchar *lineend,
|
|
|
|
const guchar **next_token)
|
2000-09-11 16:16:13 +00:00
|
|
|
{
|
2002-08-02 21:29:45 +00:00
|
|
|
const guchar *tokenp;
|
2000-09-11 16:16:13 +00:00
|
|
|
int token_len;
|
|
|
|
|
|
|
|
tokenp = linep;
|
2002-08-28 20:41:00 +00:00
|
|
|
|
2000-09-11 16:16:13 +00:00
|
|
|
/*
|
|
|
|
* Search for a blank, a CR or an LF, or the end of the buffer.
|
|
|
|
*/
|
|
|
|
while (linep < lineend && *linep != ' ' && *linep != '\r' && *linep != '\n')
|
|
|
|
linep++;
|
|
|
|
token_len = linep - tokenp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Skip trailing blanks.
|
|
|
|
*/
|
|
|
|
while (linep < lineend && *linep == ' ')
|
|
|
|
linep++;
|
|
|
|
|
|
|
|
*next_token = linep;
|
|
|
|
|
|
|
|
return token_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-09-29 19:02:37 +00:00
|
|
|
#define INITIAL_FMTBUF_SIZE 128
|
2000-09-11 16:16:13 +00:00
|
|
|
|
2004-02-05 09:42:26 +00:00
|
|
|
#if GLIB_MAJOR_VERSION >= 2
|
2003-08-01 01:39:01 +00:00
|
|
|
/*
|
|
|
|
* XXX - "isprint()" can return "true" for non-ASCII characters, but
|
2003-12-24 01:12:17 +00:00
|
|
|
* those don't work with GTK+ 1.3 or later, as they take UTF-8 strings
|
|
|
|
* as input. Until we fix up Ethereal to properly handle non-ASCII
|
|
|
|
* characters in all output (both GUI displays and text printouts)
|
|
|
|
* in those versions of GTK+, we work around the problem by escaping
|
|
|
|
* all characters that aren't printable ASCII.
|
2004-02-05 09:42:26 +00:00
|
|
|
*
|
|
|
|
* We don't know what version of GTK+ we're using, as epan doesn't
|
|
|
|
* use any GTK+ stuff; we use GLib as a proxy for that, with GLib 2.x
|
|
|
|
* implying GTK+ 1.3 or later (we don't support GLib 1.3[.x]).
|
2003-08-01 01:39:01 +00:00
|
|
|
*/
|
|
|
|
#undef isprint
|
|
|
|
#define isprint(c) (c >= 0x20 && c < 0x7f)
|
|
|
|
#endif
|
|
|
|
|
2000-09-11 16:16:13 +00:00
|
|
|
/*
|
|
|
|
* Given a string, generate a string from it that shows non-printable
|
|
|
|
* characters as C-style escapes, and return a pointer to it.
|
|
|
|
*/
|
|
|
|
gchar *
|
2002-08-02 21:29:45 +00:00
|
|
|
format_text(const guchar *string, int len)
|
2000-09-11 16:16:13 +00:00
|
|
|
{
|
2004-10-29 00:39:56 +00:00
|
|
|
static gchar *fmtbuf[3];
|
|
|
|
static int fmtbuf_len[3];
|
|
|
|
static int idx;
|
2000-09-11 16:16:13 +00:00
|
|
|
int column;
|
2002-08-02 21:29:45 +00:00
|
|
|
const guchar *stringend = string + len;
|
|
|
|
guchar c;
|
2000-09-11 16:16:13 +00:00
|
|
|
int i;
|
|
|
|
|
2004-10-29 00:39:56 +00:00
|
|
|
idx = (idx + 1) % 3;
|
|
|
|
|
2000-09-29 19:02:37 +00:00
|
|
|
/*
|
|
|
|
* Allocate the buffer if it's not already allocated.
|
|
|
|
*/
|
2004-10-29 00:39:56 +00:00
|
|
|
if (fmtbuf[idx] == NULL) {
|
|
|
|
fmtbuf[idx] = g_malloc(INITIAL_FMTBUF_SIZE);
|
|
|
|
fmtbuf_len[idx] = INITIAL_FMTBUF_SIZE;
|
2000-09-29 19:02:37 +00:00
|
|
|
}
|
2000-09-11 16:16:13 +00:00
|
|
|
column = 0;
|
|
|
|
while (string < stringend) {
|
2000-09-29 19:02:37 +00:00
|
|
|
/*
|
|
|
|
* Is there enough room for this character, if it expands to
|
|
|
|
* a backslash plus 3 octal digits (which is the most it can
|
|
|
|
* expand to), and also enough room for a terminating '\0'?
|
|
|
|
*/
|
2004-10-29 00:39:56 +00:00
|
|
|
if (column+3+1 >= fmtbuf_len[idx]) {
|
2000-09-11 16:16:13 +00:00
|
|
|
/*
|
2000-09-29 19:02:37 +00:00
|
|
|
* Double the buffer's size if it's not big enough.
|
|
|
|
* The size of the buffer starts at 128, so doubling its size
|
|
|
|
* adds at least another 128 bytes, which is more than enough
|
|
|
|
* for one more character plus a terminating '\0'.
|
2000-09-11 16:16:13 +00:00
|
|
|
*/
|
2004-10-29 00:39:56 +00:00
|
|
|
fmtbuf_len[idx] = fmtbuf_len[idx] * 2;
|
|
|
|
fmtbuf[idx] = g_realloc(fmtbuf[idx], fmtbuf_len[idx]);
|
2000-09-11 16:16:13 +00:00
|
|
|
}
|
|
|
|
c = *string++;
|
2002-12-31 21:51:10 +00:00
|
|
|
|
2000-09-11 16:16:13 +00:00
|
|
|
if (isprint(c)) {
|
2004-10-29 00:39:56 +00:00
|
|
|
fmtbuf[idx][column] = c;
|
2000-09-11 16:16:13 +00:00
|
|
|
column++;
|
|
|
|
} else {
|
2004-10-29 00:39:56 +00:00
|
|
|
fmtbuf[idx][column] = '\\';
|
2000-09-11 16:16:13 +00:00
|
|
|
column++;
|
|
|
|
switch (c) {
|
|
|
|
|
|
|
|
case '\\':
|
2004-10-29 00:39:56 +00:00
|
|
|
fmtbuf[idx][column] = '\\';
|
2000-09-11 16:16:13 +00:00
|
|
|
column++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\a':
|
2004-10-29 00:39:56 +00:00
|
|
|
fmtbuf[idx][column] = 'a';
|
2000-09-11 16:16:13 +00:00
|
|
|
column++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\b':
|
2004-10-29 00:39:56 +00:00
|
|
|
fmtbuf[idx][column] = 'b';
|
2000-09-11 16:16:13 +00:00
|
|
|
column++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\f':
|
2004-10-29 00:39:56 +00:00
|
|
|
fmtbuf[idx][column] = 'f';
|
2000-09-11 16:16:13 +00:00
|
|
|
column++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\n':
|
2004-10-29 00:39:56 +00:00
|
|
|
fmtbuf[idx][column] = 'n';
|
2000-09-11 16:16:13 +00:00
|
|
|
column++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\r':
|
2004-10-29 00:39:56 +00:00
|
|
|
fmtbuf[idx][column] = 'r';
|
2000-09-11 16:16:13 +00:00
|
|
|
column++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\t':
|
2004-10-29 00:39:56 +00:00
|
|
|
fmtbuf[idx][column] = 't';
|
2000-09-11 16:16:13 +00:00
|
|
|
column++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\v':
|
2004-10-29 00:39:56 +00:00
|
|
|
fmtbuf[idx][column] = 'v';
|
2000-09-11 16:16:13 +00:00
|
|
|
column++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
i = (c>>6)&03;
|
2004-10-29 00:39:56 +00:00
|
|
|
fmtbuf[idx][column] = i + '0';
|
2000-09-11 16:16:13 +00:00
|
|
|
column++;
|
|
|
|
i = (c>>3)&07;
|
2004-10-29 00:39:56 +00:00
|
|
|
fmtbuf[idx][column] = i + '0';
|
2000-09-11 16:16:13 +00:00
|
|
|
column++;
|
|
|
|
i = (c>>0)&07;
|
2004-10-29 00:39:56 +00:00
|
|
|
fmtbuf[idx][column] = i + '0';
|
2000-09-11 16:16:13 +00:00
|
|
|
column++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-10-29 00:39:56 +00:00
|
|
|
fmtbuf[idx][column] = '\0';
|
|
|
|
return fmtbuf[idx];
|
2000-09-11 16:16:13 +00:00
|
|
|
}
|
2000-11-13 07:19:37 +00:00
|
|
|
|
|
|
|
/* Max string length for displaying byte string. */
|
2004-11-28 19:35:21 +00:00
|
|
|
#define MAX_BYTE_STR_LEN 48
|
2000-11-13 07:19:37 +00:00
|
|
|
|
|
|
|
/* Turn an array of bytes into a string showing the bytes in hex. */
|
|
|
|
#define N_BYTES_TO_STR_STRINGS 6
|
|
|
|
gchar *
|
|
|
|
bytes_to_str(const guint8 *bd, int bd_len) {
|
2000-12-22 12:05:38 +00:00
|
|
|
return bytes_to_str_punct(bd,bd_len,'\0');
|
|
|
|
}
|
|
|
|
|
2002-08-28 20:41:00 +00:00
|
|
|
/* Turn an array of bytes into a string showing the bytes in hex with
|
2000-12-22 12:05:38 +00:00
|
|
|
* punct as a bytes separator.
|
|
|
|
*/
|
|
|
|
gchar *
|
|
|
|
bytes_to_str_punct(const guint8 *bd, int bd_len, gchar punct) {
|
2000-11-13 07:19:37 +00:00
|
|
|
static gchar str[N_BYTES_TO_STR_STRINGS][MAX_BYTE_STR_LEN+3+1];
|
|
|
|
static int cur_idx;
|
|
|
|
gchar *cur;
|
|
|
|
gchar *p;
|
|
|
|
int len;
|
|
|
|
static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
|
|
|
|
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
|
|
|
|
|
|
|
cur_idx++;
|
|
|
|
if (cur_idx >= N_BYTES_TO_STR_STRINGS)
|
|
|
|
cur_idx = 0;
|
|
|
|
cur = &str[cur_idx][0];
|
|
|
|
p = cur;
|
|
|
|
len = MAX_BYTE_STR_LEN;
|
|
|
|
while (bd_len > 0 && len > 0) {
|
|
|
|
*p++ = hex[(*bd) >> 4];
|
|
|
|
*p++ = hex[(*bd) & 0xF];
|
|
|
|
len -= 2;
|
|
|
|
bd++;
|
|
|
|
bd_len--;
|
2000-12-22 12:05:38 +00:00
|
|
|
if(punct && bd_len > 0){
|
|
|
|
*p++ = punct;
|
|
|
|
len--;
|
|
|
|
}
|
2000-11-13 07:19:37 +00:00
|
|
|
}
|
|
|
|
if (bd_len != 0) {
|
|
|
|
/* Note that we're not showing the full string. */
|
|
|
|
*p++ = '.';
|
|
|
|
*p++ = '.';
|
|
|
|
*p++ = '.';
|
|
|
|
}
|
|
|
|
*p = '\0';
|
|
|
|
return cur;
|
|
|
|
}
|
2003-08-27 15:23:11 +00:00
|
|
|
|
2003-12-29 04:07:06 +00:00
|
|
|
static gboolean
|
|
|
|
is_byte_sep(guint8 c)
|
|
|
|
{
|
|
|
|
return (c == '-' || c == ':' || c == '.');
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Turn a string of hex digits with optional separators (defined by
|
|
|
|
* is_byte_sep() into a byte array.
|
|
|
|
*/
|
|
|
|
gboolean
|
2004-06-06 14:29:07 +00:00
|
|
|
hex_str_to_bytes(const char *hex_str, GByteArray *bytes, gboolean force_separators) {
|
2003-12-29 04:07:06 +00:00
|
|
|
guint8 val;
|
2004-02-01 04:54:48 +00:00
|
|
|
const guchar *p, *q, *punct;
|
2003-12-29 04:07:06 +00:00
|
|
|
char two_digits[3];
|
|
|
|
char one_digit[2];
|
|
|
|
|
|
|
|
g_byte_array_set_size(bytes, 0);
|
2004-02-01 04:54:48 +00:00
|
|
|
p = (const guchar *)hex_str;
|
2003-12-29 04:07:06 +00:00
|
|
|
while (*p) {
|
|
|
|
q = p+1;
|
|
|
|
if (*q && isxdigit(*p) && isxdigit(*q)) {
|
|
|
|
two_digits[0] = *p;
|
|
|
|
two_digits[1] = *q;
|
|
|
|
two_digits[2] = '\0';
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Two or more hex digits in a row.
|
|
|
|
* "strtoul()" will succeed, as it'll see at
|
|
|
|
* least one hex digit.
|
|
|
|
*/
|
|
|
|
val = (guint8) strtoul(two_digits, NULL, 16);
|
|
|
|
g_byte_array_append(bytes, &val, 1);
|
|
|
|
punct = q + 1;
|
|
|
|
if (*punct) {
|
|
|
|
/*
|
|
|
|
* Make sure the character after
|
|
|
|
* the second hex digit is a byte
|
|
|
|
* separator, i.e. that we don't have
|
|
|
|
* more than two hex digits, or a
|
|
|
|
* bogus character.
|
|
|
|
*/
|
|
|
|
if (is_byte_sep(*punct)) {
|
|
|
|
p = punct + 1;
|
|
|
|
continue;
|
|
|
|
}
|
2004-06-06 14:29:07 +00:00
|
|
|
else if (force_separators) {
|
2003-12-29 04:07:06 +00:00
|
|
|
return FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2004-06-06 14:29:07 +00:00
|
|
|
p = punct;
|
|
|
|
continue;
|
2003-12-29 04:07:06 +00:00
|
|
|
}
|
|
|
|
else if (*q && isxdigit(*p) && is_byte_sep(*q)) {
|
|
|
|
one_digit[0] = *p;
|
|
|
|
one_digit[1] = '\0';
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only one hex digit.
|
|
|
|
* "strtoul()" will succeed, as it'll see that
|
|
|
|
* hex digit.
|
|
|
|
*/
|
|
|
|
val = (guint8) strtoul(one_digit, NULL, 16);
|
|
|
|
g_byte_array_append(bytes, &val, 1);
|
|
|
|
p = q + 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (!*q && isxdigit(*p)) {
|
|
|
|
one_digit[0] = *p;
|
|
|
|
one_digit[1] = '\0';
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only one hex digit.
|
|
|
|
* "strtoul()" will succeed, as it'll see that
|
|
|
|
* hex digit.
|
|
|
|
*/
|
|
|
|
val = (guint8) strtoul(one_digit, NULL, 16);
|
|
|
|
g_byte_array_append(bytes, &val, 1);
|
|
|
|
p = q;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-05-01 20:46:24 +00:00
|
|
|
/* Return a XML escaped representation of the unescaped string.
|
|
|
|
* The returned string must be freed when no longer in use. */
|
|
|
|
gchar *
|
|
|
|
xml_escape(const gchar *unescaped)
|
|
|
|
{
|
|
|
|
GString *buffer = g_string_sized_new(128);
|
|
|
|
const gchar *p;
|
|
|
|
gchar c;
|
2004-05-01 23:56:03 +00:00
|
|
|
#if GLIB_MAJOR_VERSION < 2
|
|
|
|
gchar *ret;
|
|
|
|
#endif
|
2004-05-01 20:46:24 +00:00
|
|
|
|
|
|
|
p = unescaped;
|
|
|
|
while ( (c = *p++) ) {
|
|
|
|
switch (c) {
|
|
|
|
case '<':
|
|
|
|
g_string_append(buffer, "<");
|
|
|
|
break;
|
|
|
|
case '>':
|
|
|
|
g_string_append(buffer, ">");
|
|
|
|
break;
|
|
|
|
case '&':
|
|
|
|
g_string_append(buffer, "&");
|
|
|
|
break;
|
|
|
|
case '\'':
|
|
|
|
g_string_append(buffer, "'");
|
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
g_string_append(buffer, """);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
g_string_append_c(buffer, c);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2004-05-01 23:56:03 +00:00
|
|
|
#if GLIB_MAJOR_VERSION >= 2
|
2004-05-01 20:46:24 +00:00
|
|
|
/* Return the string value contained within the GString
|
|
|
|
* after getting rid of the GString structure.
|
|
|
|
* This is the way to do this, see the GLib reference. */
|
|
|
|
return g_string_free(buffer, FALSE);
|
2004-05-01 23:56:03 +00:00
|
|
|
#else
|
|
|
|
/* But it's not the way to do it in GLib 1.2[.x], as
|
|
|
|
* 1.2[.x]'s "g_string_free()" doesn't return anything.
|
|
|
|
* This is the way to do this in GLib 1.2[.x]. */
|
|
|
|
ret = buffer->str;
|
|
|
|
g_string_free(buffer, FALSE);
|
|
|
|
return ret;
|
|
|
|
#endif
|
2004-05-01 20:46:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-08-27 15:23:11 +00:00
|
|
|
/* Return the first occurrence of needle in haystack.
|
|
|
|
* If not found, return NULL.
|
|
|
|
* If either haystack or needle has 0 length, return NULL.
|
|
|
|
* Algorithm copied from GNU's glibc 2.3.2 memcmp() */
|
|
|
|
const guint8 *
|
|
|
|
epan_memmem(const guint8 *haystack, guint haystack_len,
|
|
|
|
const guint8 *needle, guint needle_len)
|
|
|
|
{
|
|
|
|
const guint8 *begin;
|
|
|
|
const guint8 *const last_possible
|
|
|
|
= haystack + haystack_len - needle_len;
|
|
|
|
|
|
|
|
if (needle_len == 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (needle_len > haystack_len) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (begin = haystack ; begin <= last_possible; ++begin) {
|
|
|
|
if (begin[0] == needle[0] &&
|
|
|
|
!memcmp(&begin[1], needle + 1,
|
|
|
|
needle_len - 1)) {
|
|
|
|
return begin;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2004-08-13 02:39:49 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan the search string to make sure it's valid hex. Return the
|
|
|
|
* number of bytes in nbytes.
|
|
|
|
*/
|
|
|
|
guint8 *
|
|
|
|
convert_string_to_hex(const char *string, size_t *nbytes)
|
|
|
|
{
|
|
|
|
size_t n_bytes;
|
|
|
|
const char *p;
|
|
|
|
guchar c;
|
|
|
|
guint8 *bytes, *q, byte_val;
|
|
|
|
|
|
|
|
n_bytes = 0;
|
|
|
|
p = &string[0];
|
|
|
|
for (;;) {
|
|
|
|
c = *p++;
|
|
|
|
if (c == '\0')
|
|
|
|
break;
|
|
|
|
if (isspace(c))
|
|
|
|
continue; /* allow white space */
|
|
|
|
if (c==':' || c=='.' || c=='-')
|
|
|
|
continue; /* skip any ':', '.', or '-' between bytes */
|
|
|
|
if (!isxdigit(c)) {
|
|
|
|
/* Not a valid hex digit - fail */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We can only match bytes, not nibbles; we must have a valid
|
|
|
|
* hex digit immediately after that hex digit.
|
|
|
|
*/
|
|
|
|
c = *p++;
|
|
|
|
if (!isxdigit(c))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* 2 hex digits = 1 byte */
|
|
|
|
n_bytes++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Were we given any hex digits?
|
|
|
|
*/
|
|
|
|
if (n_bytes == 0) {
|
|
|
|
/* No. */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* OK, it's valid, and it generates "n_bytes" bytes; generate the
|
|
|
|
* raw byte array.
|
|
|
|
*/
|
|
|
|
bytes = g_malloc(n_bytes);
|
|
|
|
p = &string[0];
|
|
|
|
q = &bytes[0];
|
|
|
|
for (;;) {
|
|
|
|
c = *p++;
|
|
|
|
if (c == '\0')
|
|
|
|
break;
|
|
|
|
if (isspace(c))
|
|
|
|
continue; /* allow white space */
|
|
|
|
if (c==':' || c=='.' || c=='-')
|
|
|
|
continue; /* skip any ':', '.', or '-' between bytes */
|
|
|
|
/* From the loop above, we know this is a hex digit */
|
|
|
|
if (isdigit(c))
|
|
|
|
byte_val = c - '0';
|
|
|
|
else if (c >= 'a')
|
|
|
|
byte_val = (c - 'a') + 10;
|
|
|
|
else
|
|
|
|
byte_val = (c - 'A') + 10;
|
|
|
|
byte_val <<= 4;
|
|
|
|
|
|
|
|
/* We also know this is a hex digit */
|
|
|
|
c = *p++;
|
|
|
|
if (isdigit(c))
|
|
|
|
byte_val |= c - '0';
|
|
|
|
else if (c >= 'a')
|
|
|
|
byte_val |= (c - 'a') + 10;
|
|
|
|
else if (c >= 'A')
|
|
|
|
byte_val |= (c - 'A') + 10;
|
|
|
|
|
|
|
|
*q++ = byte_val;
|
|
|
|
}
|
|
|
|
*nbytes = n_bytes;
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy if if it's a case-sensitive search; uppercase it if it's
|
|
|
|
* a case-insensitive search.
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
convert_string_case(const char *string, gboolean case_insensitive)
|
|
|
|
{
|
|
|
|
char *out_string;
|
|
|
|
const char *p;
|
|
|
|
char c;
|
|
|
|
char *q;
|
|
|
|
|
|
|
|
if (case_insensitive) {
|
|
|
|
out_string = g_malloc(strlen(string) + 1);
|
|
|
|
for (p = &string[0], q = &out_string[0]; (c = *p) != '\0'; p++, q++)
|
|
|
|
*q = toupper((unsigned char)*p);
|
|
|
|
*q = '\0';
|
|
|
|
} else
|
|
|
|
out_string = g_strdup(string);
|
|
|
|
return out_string;
|
|
|
|
}
|
|
|
|
|
|
|
|
|