mirror of
https://github.com/vanhoefm/fragattacks.git
synced 2024-11-27 01:38:37 -05:00
5dff6dff63
The off-by-one error in printf_encode() bounds checking could have allowed buffer overflow with 0x00 being written to the memory position following the last octet of the target buffer. Since this output is used as \0-terminated string, the following operation would likely read past the buffer as well. Either of these operations can result in the process dying either due to buffer overflow protection or by a read from unallowed address. This has been seen to cause wpa_supplicant crash on OpenBSD when control interface client attaches (debug print shows the client socket address). Similarly, it may be possible to trigger the issue in RADIUS/EAP server implementation within hostapd with a suitable constructed user name. Signed-off-by: Stuart Henderson <sthen@openbsd.org>
830 lines
15 KiB
C
830 lines
15 KiB
C
/*
|
|
* wpa_supplicant/hostapd / common helper functions, etc.
|
|
* Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
|
|
*
|
|
* This software may be distributed under the terms of the BSD license.
|
|
* See README for more details.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
|
|
#include "common.h"
|
|
|
|
|
|
static int hex2num(char c)
|
|
{
|
|
if (c >= '0' && c <= '9')
|
|
return c - '0';
|
|
if (c >= 'a' && c <= 'f')
|
|
return c - 'a' + 10;
|
|
if (c >= 'A' && c <= 'F')
|
|
return c - 'A' + 10;
|
|
return -1;
|
|
}
|
|
|
|
|
|
int hex2byte(const char *hex)
|
|
{
|
|
int a, b;
|
|
a = hex2num(*hex++);
|
|
if (a < 0)
|
|
return -1;
|
|
b = hex2num(*hex++);
|
|
if (b < 0)
|
|
return -1;
|
|
return (a << 4) | b;
|
|
}
|
|
|
|
|
|
/**
|
|
* hwaddr_aton - Convert ASCII string to MAC address (colon-delimited format)
|
|
* @txt: MAC address as a string (e.g., "00:11:22:33:44:55")
|
|
* @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
|
|
* Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
|
|
*/
|
|
int hwaddr_aton(const char *txt, u8 *addr)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
int a, b;
|
|
|
|
a = hex2num(*txt++);
|
|
if (a < 0)
|
|
return -1;
|
|
b = hex2num(*txt++);
|
|
if (b < 0)
|
|
return -1;
|
|
*addr++ = (a << 4) | b;
|
|
if (i < 5 && *txt++ != ':')
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* hwaddr_compact_aton - Convert ASCII string to MAC address (no colon delimitors format)
|
|
* @txt: MAC address as a string (e.g., "001122334455")
|
|
* @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
|
|
* Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
|
|
*/
|
|
int hwaddr_compact_aton(const char *txt, u8 *addr)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
int a, b;
|
|
|
|
a = hex2num(*txt++);
|
|
if (a < 0)
|
|
return -1;
|
|
b = hex2num(*txt++);
|
|
if (b < 0)
|
|
return -1;
|
|
*addr++ = (a << 4) | b;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* hwaddr_aton2 - Convert ASCII string to MAC address (in any known format)
|
|
* @txt: MAC address as a string (e.g., 00:11:22:33:44:55 or 0011.2233.4455)
|
|
* @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
|
|
* Returns: Characters used (> 0) on success, -1 on failure
|
|
*/
|
|
int hwaddr_aton2(const char *txt, u8 *addr)
|
|
{
|
|
int i;
|
|
const char *pos = txt;
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
int a, b;
|
|
|
|
while (*pos == ':' || *pos == '.' || *pos == '-')
|
|
pos++;
|
|
|
|
a = hex2num(*pos++);
|
|
if (a < 0)
|
|
return -1;
|
|
b = hex2num(*pos++);
|
|
if (b < 0)
|
|
return -1;
|
|
*addr++ = (a << 4) | b;
|
|
}
|
|
|
|
return pos - txt;
|
|
}
|
|
|
|
|
|
/**
|
|
* hexstr2bin - Convert ASCII hex string into binary data
|
|
* @hex: ASCII hex string (e.g., "01ab")
|
|
* @buf: Buffer for the binary data
|
|
* @len: Length of the text to convert in bytes (of buf); hex will be double
|
|
* this size
|
|
* Returns: 0 on success, -1 on failure (invalid hex string)
|
|
*/
|
|
int hexstr2bin(const char *hex, u8 *buf, size_t len)
|
|
{
|
|
size_t i;
|
|
int a;
|
|
const char *ipos = hex;
|
|
u8 *opos = buf;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
a = hex2byte(ipos);
|
|
if (a < 0)
|
|
return -1;
|
|
*opos++ = a;
|
|
ipos += 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* inc_byte_array - Increment arbitrary length byte array by one
|
|
* @counter: Pointer to byte array
|
|
* @len: Length of the counter in bytes
|
|
*
|
|
* This function increments the last byte of the counter by one and continues
|
|
* rolling over to more significant bytes if the byte was incremented from
|
|
* 0xff to 0x00.
|
|
*/
|
|
void inc_byte_array(u8 *counter, size_t len)
|
|
{
|
|
int pos = len - 1;
|
|
while (pos >= 0) {
|
|
counter[pos]++;
|
|
if (counter[pos] != 0)
|
|
break;
|
|
pos--;
|
|
}
|
|
}
|
|
|
|
|
|
void wpa_get_ntp_timestamp(u8 *buf)
|
|
{
|
|
struct os_time now;
|
|
u32 sec, usec;
|
|
be32 tmp;
|
|
|
|
/* 64-bit NTP timestamp (time from 1900-01-01 00:00:00) */
|
|
os_get_time(&now);
|
|
sec = now.sec + 2208988800U; /* Epoch to 1900 */
|
|
/* Estimate 2^32/10^6 = 4295 - 1/32 - 1/512 */
|
|
usec = now.usec;
|
|
usec = 4295 * usec - (usec >> 5) - (usec >> 9);
|
|
tmp = host_to_be32(sec);
|
|
os_memcpy(buf, (u8 *) &tmp, 4);
|
|
tmp = host_to_be32(usec);
|
|
os_memcpy(buf + 4, (u8 *) &tmp, 4);
|
|
}
|
|
|
|
|
|
static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data,
|
|
size_t len, int uppercase)
|
|
{
|
|
size_t i;
|
|
char *pos = buf, *end = buf + buf_size;
|
|
int ret;
|
|
if (buf_size == 0)
|
|
return 0;
|
|
for (i = 0; i < len; i++) {
|
|
ret = os_snprintf(pos, end - pos, uppercase ? "%02X" : "%02x",
|
|
data[i]);
|
|
if (ret < 0 || ret >= end - pos) {
|
|
end[-1] = '\0';
|
|
return pos - buf;
|
|
}
|
|
pos += ret;
|
|
}
|
|
end[-1] = '\0';
|
|
return pos - buf;
|
|
}
|
|
|
|
/**
|
|
* wpa_snprintf_hex - Print data as a hex string into a buffer
|
|
* @buf: Memory area to use as the output buffer
|
|
* @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1)
|
|
* @data: Data to be printed
|
|
* @len: Length of data in bytes
|
|
* Returns: Number of bytes written
|
|
*/
|
|
int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len)
|
|
{
|
|
return _wpa_snprintf_hex(buf, buf_size, data, len, 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* wpa_snprintf_hex_uppercase - Print data as a upper case hex string into buf
|
|
* @buf: Memory area to use as the output buffer
|
|
* @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1)
|
|
* @data: Data to be printed
|
|
* @len: Length of data in bytes
|
|
* Returns: Number of bytes written
|
|
*/
|
|
int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
|
|
size_t len)
|
|
{
|
|
return _wpa_snprintf_hex(buf, buf_size, data, len, 1);
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_ANSI_C_EXTRA
|
|
|
|
#ifdef _WIN32_WCE
|
|
void perror(const char *s)
|
|
{
|
|
wpa_printf(MSG_ERROR, "%s: GetLastError: %d",
|
|
s, (int) GetLastError());
|
|
}
|
|
#endif /* _WIN32_WCE */
|
|
|
|
|
|
int optind = 1;
|
|
int optopt;
|
|
char *optarg;
|
|
|
|
int getopt(int argc, char *const argv[], const char *optstring)
|
|
{
|
|
static int optchr = 1;
|
|
char *cp;
|
|
|
|
if (optchr == 1) {
|
|
if (optind >= argc) {
|
|
/* all arguments processed */
|
|
return EOF;
|
|
}
|
|
|
|
if (argv[optind][0] != '-' || argv[optind][1] == '\0') {
|
|
/* no option characters */
|
|
return EOF;
|
|
}
|
|
}
|
|
|
|
if (os_strcmp(argv[optind], "--") == 0) {
|
|
/* no more options */
|
|
optind++;
|
|
return EOF;
|
|
}
|
|
|
|
optopt = argv[optind][optchr];
|
|
cp = os_strchr(optstring, optopt);
|
|
if (cp == NULL || optopt == ':') {
|
|
if (argv[optind][++optchr] == '\0') {
|
|
optchr = 1;
|
|
optind++;
|
|
}
|
|
return '?';
|
|
}
|
|
|
|
if (cp[1] == ':') {
|
|
/* Argument required */
|
|
optchr = 1;
|
|
if (argv[optind][optchr + 1]) {
|
|
/* No space between option and argument */
|
|
optarg = &argv[optind++][optchr + 1];
|
|
} else if (++optind >= argc) {
|
|
/* option requires an argument */
|
|
return '?';
|
|
} else {
|
|
/* Argument in the next argv */
|
|
optarg = argv[optind++];
|
|
}
|
|
} else {
|
|
/* No argument */
|
|
if (argv[optind][++optchr] == '\0') {
|
|
optchr = 1;
|
|
optind++;
|
|
}
|
|
optarg = NULL;
|
|
}
|
|
return *cp;
|
|
}
|
|
#endif /* CONFIG_ANSI_C_EXTRA */
|
|
|
|
|
|
#ifdef CONFIG_NATIVE_WINDOWS
|
|
/**
|
|
* wpa_unicode2ascii_inplace - Convert unicode string into ASCII
|
|
* @str: Pointer to string to convert
|
|
*
|
|
* This function converts a unicode string to ASCII using the same
|
|
* buffer for output. If UNICODE is not set, the buffer is not
|
|
* modified.
|
|
*/
|
|
void wpa_unicode2ascii_inplace(TCHAR *str)
|
|
{
|
|
#ifdef UNICODE
|
|
char *dst = (char *) str;
|
|
while (*str)
|
|
*dst++ = (char) *str++;
|
|
*dst = '\0';
|
|
#endif /* UNICODE */
|
|
}
|
|
|
|
|
|
TCHAR * wpa_strdup_tchar(const char *str)
|
|
{
|
|
#ifdef UNICODE
|
|
TCHAR *buf;
|
|
buf = os_malloc((strlen(str) + 1) * sizeof(TCHAR));
|
|
if (buf == NULL)
|
|
return NULL;
|
|
wsprintf(buf, L"%S", str);
|
|
return buf;
|
|
#else /* UNICODE */
|
|
return os_strdup(str);
|
|
#endif /* UNICODE */
|
|
}
|
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
|
|
|
|
|
void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len)
|
|
{
|
|
char *end = txt + maxlen;
|
|
size_t i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (txt + 4 >= end)
|
|
break;
|
|
|
|
switch (data[i]) {
|
|
case '\"':
|
|
*txt++ = '\\';
|
|
*txt++ = '\"';
|
|
break;
|
|
case '\\':
|
|
*txt++ = '\\';
|
|
*txt++ = '\\';
|
|
break;
|
|
case '\e':
|
|
*txt++ = '\\';
|
|
*txt++ = 'e';
|
|
break;
|
|
case '\n':
|
|
*txt++ = '\\';
|
|
*txt++ = 'n';
|
|
break;
|
|
case '\r':
|
|
*txt++ = '\\';
|
|
*txt++ = 'r';
|
|
break;
|
|
case '\t':
|
|
*txt++ = '\\';
|
|
*txt++ = 't';
|
|
break;
|
|
default:
|
|
if (data[i] >= 32 && data[i] <= 127) {
|
|
*txt++ = data[i];
|
|
} else {
|
|
txt += os_snprintf(txt, end - txt, "\\x%02x",
|
|
data[i]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
*txt = '\0';
|
|
}
|
|
|
|
|
|
size_t printf_decode(u8 *buf, size_t maxlen, const char *str)
|
|
{
|
|
const char *pos = str;
|
|
size_t len = 0;
|
|
int val;
|
|
|
|
while (*pos) {
|
|
if (len + 1 >= maxlen)
|
|
break;
|
|
switch (*pos) {
|
|
case '\\':
|
|
pos++;
|
|
switch (*pos) {
|
|
case '\\':
|
|
buf[len++] = '\\';
|
|
pos++;
|
|
break;
|
|
case '"':
|
|
buf[len++] = '"';
|
|
pos++;
|
|
break;
|
|
case 'n':
|
|
buf[len++] = '\n';
|
|
pos++;
|
|
break;
|
|
case 'r':
|
|
buf[len++] = '\r';
|
|
pos++;
|
|
break;
|
|
case 't':
|
|
buf[len++] = '\t';
|
|
pos++;
|
|
break;
|
|
case 'e':
|
|
buf[len++] = '\e';
|
|
pos++;
|
|
break;
|
|
case 'x':
|
|
pos++;
|
|
val = hex2byte(pos);
|
|
if (val < 0) {
|
|
val = hex2num(*pos);
|
|
if (val < 0)
|
|
break;
|
|
buf[len++] = val;
|
|
pos++;
|
|
} else {
|
|
buf[len++] = val;
|
|
pos += 2;
|
|
}
|
|
break;
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
val = *pos++ - '0';
|
|
if (*pos >= '0' && *pos <= '7')
|
|
val = val * 8 + (*pos++ - '0');
|
|
if (*pos >= '0' && *pos <= '7')
|
|
val = val * 8 + (*pos++ - '0');
|
|
buf[len++] = val;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
buf[len++] = *pos++;
|
|
break;
|
|
}
|
|
}
|
|
if (maxlen > len)
|
|
buf[len] = '\0';
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
/**
|
|
* wpa_ssid_txt - Convert SSID to a printable string
|
|
* @ssid: SSID (32-octet string)
|
|
* @ssid_len: Length of ssid in octets
|
|
* Returns: Pointer to a printable string
|
|
*
|
|
* This function can be used to convert SSIDs into printable form. In most
|
|
* cases, SSIDs do not use unprintable characters, but IEEE 802.11 standard
|
|
* does not limit the used character set, so anything could be used in an SSID.
|
|
*
|
|
* This function uses a static buffer, so only one call can be used at the
|
|
* time, i.e., this is not re-entrant and the returned buffer must be used
|
|
* before calling this again.
|
|
*/
|
|
const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len)
|
|
{
|
|
static char ssid_txt[32 * 4 + 1];
|
|
|
|
if (ssid == NULL) {
|
|
ssid_txt[0] = '\0';
|
|
return ssid_txt;
|
|
}
|
|
|
|
printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len);
|
|
return ssid_txt;
|
|
}
|
|
|
|
|
|
void * __hide_aliasing_typecast(void *foo)
|
|
{
|
|
return foo;
|
|
}
|
|
|
|
|
|
char * wpa_config_parse_string(const char *value, size_t *len)
|
|
{
|
|
if (*value == '"') {
|
|
const char *pos;
|
|
char *str;
|
|
value++;
|
|
pos = os_strrchr(value, '"');
|
|
if (pos == NULL || pos[1] != '\0')
|
|
return NULL;
|
|
*len = pos - value;
|
|
str = dup_binstr(value, *len);
|
|
if (str == NULL)
|
|
return NULL;
|
|
return str;
|
|
} else if (*value == 'P' && value[1] == '"') {
|
|
const char *pos;
|
|
char *tstr, *str;
|
|
size_t tlen;
|
|
value += 2;
|
|
pos = os_strrchr(value, '"');
|
|
if (pos == NULL || pos[1] != '\0')
|
|
return NULL;
|
|
tlen = pos - value;
|
|
tstr = dup_binstr(value, tlen);
|
|
if (tstr == NULL)
|
|
return NULL;
|
|
|
|
str = os_malloc(tlen + 1);
|
|
if (str == NULL) {
|
|
os_free(tstr);
|
|
return NULL;
|
|
}
|
|
|
|
*len = printf_decode((u8 *) str, tlen + 1, tstr);
|
|
os_free(tstr);
|
|
|
|
return str;
|
|
} else {
|
|
u8 *str;
|
|
size_t tlen, hlen = os_strlen(value);
|
|
if (hlen & 1)
|
|
return NULL;
|
|
tlen = hlen / 2;
|
|
str = os_malloc(tlen + 1);
|
|
if (str == NULL)
|
|
return NULL;
|
|
if (hexstr2bin(value, str, tlen)) {
|
|
os_free(str);
|
|
return NULL;
|
|
}
|
|
str[tlen] = '\0';
|
|
*len = tlen;
|
|
return (char *) str;
|
|
}
|
|
}
|
|
|
|
|
|
int is_hex(const u8 *data, size_t len)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (data[i] < 32 || data[i] >= 127)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int find_first_bit(u32 value)
|
|
{
|
|
int pos = 0;
|
|
|
|
while (value) {
|
|
if (value & 0x1)
|
|
return pos;
|
|
value >>= 1;
|
|
pos++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
size_t merge_byte_arrays(u8 *res, size_t res_len,
|
|
const u8 *src1, size_t src1_len,
|
|
const u8 *src2, size_t src2_len)
|
|
{
|
|
size_t len = 0;
|
|
|
|
os_memset(res, 0, res_len);
|
|
|
|
if (src1) {
|
|
if (src1_len >= res_len) {
|
|
os_memcpy(res, src1, res_len);
|
|
return res_len;
|
|
}
|
|
|
|
os_memcpy(res, src1, src1_len);
|
|
len += src1_len;
|
|
}
|
|
|
|
if (src2) {
|
|
if (len + src2_len >= res_len) {
|
|
os_memcpy(res + len, src2, res_len - len);
|
|
return res_len;
|
|
}
|
|
|
|
os_memcpy(res + len, src2, src2_len);
|
|
len += src2_len;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
char * dup_binstr(const void *src, size_t len)
|
|
{
|
|
char *res;
|
|
|
|
if (src == NULL)
|
|
return NULL;
|
|
res = os_malloc(len + 1);
|
|
if (res == NULL)
|
|
return NULL;
|
|
os_memcpy(res, src, len);
|
|
res[len] = '\0';
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value)
|
|
{
|
|
struct wpa_freq_range *freq = NULL, *n;
|
|
unsigned int count = 0;
|
|
const char *pos, *pos2, *pos3;
|
|
|
|
/*
|
|
* Comma separated list of frequency ranges.
|
|
* For example: 2412-2432,2462,5000-6000
|
|
*/
|
|
pos = value;
|
|
while (pos && pos[0]) {
|
|
n = os_realloc_array(freq, count + 1,
|
|
sizeof(struct wpa_freq_range));
|
|
if (n == NULL) {
|
|
os_free(freq);
|
|
return -1;
|
|
}
|
|
freq = n;
|
|
freq[count].min = atoi(pos);
|
|
pos2 = os_strchr(pos, '-');
|
|
pos3 = os_strchr(pos, ',');
|
|
if (pos2 && (!pos3 || pos2 < pos3)) {
|
|
pos2++;
|
|
freq[count].max = atoi(pos2);
|
|
} else
|
|
freq[count].max = freq[count].min;
|
|
pos = pos3;
|
|
if (pos)
|
|
pos++;
|
|
count++;
|
|
}
|
|
|
|
os_free(res->range);
|
|
res->range = freq;
|
|
res->num = count;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int freq_range_list_includes(const struct wpa_freq_range_list *list,
|
|
unsigned int freq)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (list == NULL)
|
|
return 0;
|
|
|
|
for (i = 0; i < list->num; i++) {
|
|
if (freq >= list->range[i].min && freq <= list->range[i].max)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
char * freq_range_list_str(const struct wpa_freq_range_list *list)
|
|
{
|
|
char *buf, *pos, *end;
|
|
size_t maxlen;
|
|
unsigned int i;
|
|
int res;
|
|
|
|
if (list->num == 0)
|
|
return NULL;
|
|
|
|
maxlen = list->num * 30;
|
|
buf = os_malloc(maxlen);
|
|
if (buf == NULL)
|
|
return NULL;
|
|
pos = buf;
|
|
end = buf + maxlen;
|
|
|
|
for (i = 0; i < list->num; i++) {
|
|
struct wpa_freq_range *range = &list->range[i];
|
|
|
|
if (range->min == range->max)
|
|
res = os_snprintf(pos, end - pos, "%s%u",
|
|
i == 0 ? "" : ",", range->min);
|
|
else
|
|
res = os_snprintf(pos, end - pos, "%s%u-%u",
|
|
i == 0 ? "" : ",",
|
|
range->min, range->max);
|
|
if (res < 0 || res > end - pos) {
|
|
os_free(buf);
|
|
return NULL;
|
|
}
|
|
pos += res;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
|
|
int int_array_len(const int *a)
|
|
{
|
|
int i;
|
|
for (i = 0; a && a[i]; i++)
|
|
;
|
|
return i;
|
|
}
|
|
|
|
|
|
void int_array_concat(int **res, const int *a)
|
|
{
|
|
int reslen, alen, i;
|
|
int *n;
|
|
|
|
reslen = int_array_len(*res);
|
|
alen = int_array_len(a);
|
|
|
|
n = os_realloc_array(*res, reslen + alen + 1, sizeof(int));
|
|
if (n == NULL) {
|
|
os_free(*res);
|
|
*res = NULL;
|
|
return;
|
|
}
|
|
for (i = 0; i <= alen; i++)
|
|
n[reslen + i] = a[i];
|
|
*res = n;
|
|
}
|
|
|
|
|
|
static int freq_cmp(const void *a, const void *b)
|
|
{
|
|
int _a = *(int *) a;
|
|
int _b = *(int *) b;
|
|
|
|
if (_a == 0)
|
|
return 1;
|
|
if (_b == 0)
|
|
return -1;
|
|
return _a - _b;
|
|
}
|
|
|
|
|
|
void int_array_sort_unique(int *a)
|
|
{
|
|
int alen;
|
|
int i, j;
|
|
|
|
if (a == NULL)
|
|
return;
|
|
|
|
alen = int_array_len(a);
|
|
qsort(a, alen, sizeof(int), freq_cmp);
|
|
|
|
i = 0;
|
|
j = 1;
|
|
while (a[i] && a[j]) {
|
|
if (a[i] == a[j]) {
|
|
j++;
|
|
continue;
|
|
}
|
|
a[++i] = a[j++];
|
|
}
|
|
if (a[i])
|
|
i++;
|
|
a[i] = 0;
|
|
}
|
|
|
|
|
|
void int_array_add_unique(int **res, int a)
|
|
{
|
|
int reslen;
|
|
int *n;
|
|
|
|
for (reslen = 0; *res && (*res)[reslen]; reslen++) {
|
|
if ((*res)[reslen] == a)
|
|
return; /* already in the list */
|
|
}
|
|
|
|
n = os_realloc_array(*res, reslen + 2, sizeof(int));
|
|
if (n == NULL) {
|
|
os_free(*res);
|
|
*res = NULL;
|
|
return;
|
|
}
|
|
|
|
n[reslen] = a;
|
|
n[reslen + 1] = 0;
|
|
|
|
*res = n;
|
|
}
|