|
Packit |
fabffb |
/* NetworkManager -- Network link manager
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* This library is free software; you can redistribute it and/or
|
|
Packit |
fabffb |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
fabffb |
* License as published by the Free Software Foundation; either
|
|
Packit |
fabffb |
* version 2 of the License, or (at your option) any later version.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* This library is distributed in the hope that it will be useful,
|
|
Packit |
fabffb |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
fabffb |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
fabffb |
* Lesser General Public License for more details.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
fabffb |
* License along with this library; if not, write to the
|
|
Packit |
fabffb |
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Packit |
fabffb |
* Boston, MA 02110-1301 USA.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* (C) Copyright 2016 Red Hat, Inc.
|
|
Packit |
fabffb |
*/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
#include "nm-default.h"
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
#include "nm-shared-utils.h"
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
#include <errno.h>
|
|
Packit |
fabffb |
#include <arpa/inet.h>
|
|
Packit |
fabffb |
#include <poll.h>
|
|
Packit |
fabffb |
#include <fcntl.h>
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/*****************************************************************************/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
const void *const _NM_PTRARRAY_EMPTY[1] = { NULL };
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/*****************************************************************************/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
const NMIPAddr nm_ip_addr_zero = { 0 };
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/*****************************************************************************/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
void
|
|
Packit |
fabffb |
nm_utils_strbuf_append_c (char **buf, gsize *len, char c)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
switch (*len) {
|
|
Packit |
fabffb |
case 0:
|
|
Packit |
fabffb |
return;
|
|
Packit |
fabffb |
case 1:
|
|
Packit |
fabffb |
(*buf)[0] = '\0';
|
|
Packit |
fabffb |
*len = 0;
|
|
Packit |
fabffb |
(*buf)++;
|
|
Packit |
fabffb |
return;
|
|
Packit |
fabffb |
default:
|
|
Packit |
fabffb |
(*buf)[0] = c;
|
|
Packit |
fabffb |
(*buf)[1] = '\0';
|
|
Packit |
fabffb |
(*len)--;
|
|
Packit |
fabffb |
(*buf)++;
|
|
Packit |
fabffb |
return;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
void
|
|
Packit |
fabffb |
nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
gsize src_len;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
switch (*len) {
|
|
Packit |
fabffb |
case 0:
|
|
Packit |
fabffb |
return;
|
|
Packit |
fabffb |
case 1:
|
|
Packit |
fabffb |
if (!str || !*str) {
|
|
Packit |
fabffb |
(*buf)[0] = '\0';
|
|
Packit |
fabffb |
return;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
(*buf)[0] = '\0';
|
|
Packit |
fabffb |
*len = 0;
|
|
Packit |
fabffb |
(*buf)++;
|
|
Packit |
fabffb |
return;
|
|
Packit |
fabffb |
default:
|
|
Packit |
fabffb |
if (!str || !*str) {
|
|
Packit |
fabffb |
(*buf)[0] = '\0';
|
|
Packit |
fabffb |
return;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
src_len = g_strlcpy (*buf, str, *len);
|
|
Packit |
fabffb |
if (src_len >= *len) {
|
|
Packit |
fabffb |
*buf = &(*buf)[*len];
|
|
Packit |
fabffb |
*len = 0;
|
|
Packit |
fabffb |
} else {
|
|
Packit |
fabffb |
*buf = &(*buf)[src_len];
|
|
Packit |
fabffb |
*len -= src_len;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
return;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
void
|
|
Packit |
fabffb |
nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
char *p = *buf;
|
|
Packit |
fabffb |
va_list args;
|
|
Packit |
fabffb |
gint retval;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (*len == 0)
|
|
Packit |
fabffb |
return;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
va_start (args, format);
|
|
Packit |
fabffb |
retval = g_vsnprintf (p, *len, format, args);
|
|
Packit |
fabffb |
va_end (args);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (retval >= *len) {
|
|
Packit |
fabffb |
*buf = &p[*len];
|
|
Packit |
fabffb |
*len = 0;
|
|
Packit |
fabffb |
} else {
|
|
Packit |
fabffb |
*buf = &p[retval];
|
|
Packit |
fabffb |
*len -= retval;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/*****************************************************************************/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/**
|
|
Packit |
fabffb |
* nm_strquote:
|
|
Packit |
fabffb |
* @buf: the output buffer of where to write the quoted @str argument.
|
|
Packit |
fabffb |
* @buf_len: the size of @buf.
|
|
Packit |
fabffb |
* @str: (allow-none): the string to quote.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* Writes @str to @buf with quoting. The resulting buffer
|
|
Packit |
fabffb |
* is always NUL terminated, unless @buf_len is zero.
|
|
Packit |
fabffb |
* If @str is %NULL, it writes "(null)".
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* If @str needs to be truncated, the closing quote is '^' instead
|
|
Packit |
fabffb |
* of '"'.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* This is similar to nm_strquote_a(), which however uses alloca()
|
|
Packit |
fabffb |
* to allocate a new buffer. Also, here @buf_len is the size of @buf,
|
|
Packit |
fabffb |
* while nm_strquote_a() has the number of characters to print. The latter
|
|
Packit |
fabffb |
* doesn't include the quoting.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* Returns: the input buffer with the quoted string.
|
|
Packit |
fabffb |
*/
|
|
Packit |
fabffb |
const char *
|
|
Packit |
fabffb |
nm_strquote (char *buf, gsize buf_len, const char *str)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
const char *const buf0 = buf;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (!str) {
|
|
Packit |
fabffb |
nm_utils_strbuf_append_str (&buf, &buf_len, "(null)");
|
|
Packit |
fabffb |
goto out;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (G_UNLIKELY (buf_len <= 2)) {
|
|
Packit |
fabffb |
switch (buf_len) {
|
|
Packit |
fabffb |
case 2:
|
|
Packit |
fabffb |
*(buf++) = '^';
|
|
Packit |
fabffb |
/* fall-through */
|
|
Packit |
fabffb |
case 1:
|
|
Packit |
fabffb |
*(buf++) = '\0';
|
|
Packit |
fabffb |
break;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
goto out;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
*(buf++) = '"';
|
|
Packit |
fabffb |
buf_len--;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
nm_utils_strbuf_append_str (&buf, &buf_len, str);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/* if the string was too long we indicate truncation with a
|
|
Packit |
fabffb |
* '^' instead of a closing quote. */
|
|
Packit |
fabffb |
if (G_UNLIKELY (buf_len <= 1)) {
|
|
Packit |
fabffb |
switch (buf_len) {
|
|
Packit |
fabffb |
case 1:
|
|
Packit |
fabffb |
buf[-1] = '^';
|
|
Packit |
fabffb |
break;
|
|
Packit |
fabffb |
case 0:
|
|
Packit |
fabffb |
buf[-2] = '^';
|
|
Packit |
fabffb |
break;
|
|
Packit |
fabffb |
default:
|
|
Packit |
fabffb |
nm_assert_not_reached ();
|
|
Packit |
fabffb |
break;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
} else {
|
|
Packit |
fabffb |
nm_assert (buf_len >= 2);
|
|
Packit |
fabffb |
*(buf++) = '"';
|
|
Packit |
fabffb |
*(buf++) = '\0';
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
out:
|
|
Packit |
fabffb |
return buf0;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/*****************************************************************************/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
char _nm_utils_to_string_buffer[];
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
void
|
|
Packit |
fabffb |
nm_utils_to_string_buffer_init (char **buf, gsize *len)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
if (!*buf) {
|
|
Packit |
fabffb |
*buf = _nm_utils_to_string_buffer;
|
|
Packit |
fabffb |
*len = sizeof (_nm_utils_to_string_buffer);
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
gboolean
|
|
Packit |
fabffb |
nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
nm_utils_to_string_buffer_init (buf, len);
|
|
Packit |
fabffb |
if (!obj) {
|
|
Packit |
fabffb |
g_strlcpy (*buf, "(null)", *len);
|
|
Packit |
fabffb |
return FALSE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
return TRUE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/*****************************************************************************/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
const char *
|
|
Packit |
fabffb |
nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs,
|
|
Packit |
fabffb |
gsize n_descs,
|
|
Packit |
fabffb |
unsigned flags,
|
|
Packit |
fabffb |
char *buf,
|
|
Packit |
fabffb |
gsize len)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
gsize i;
|
|
Packit |
fabffb |
char *p;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
#if NM_MORE_ASSERTS > 10
|
|
Packit |
fabffb |
nm_assert (descs);
|
|
Packit |
fabffb |
nm_assert (n_descs > 0);
|
|
Packit |
fabffb |
for (i = 0; i < n_descs; i++) {
|
|
Packit |
fabffb |
gsize j;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
nm_assert (descs[i].name && descs[i].name[0]);
|
|
Packit |
fabffb |
for (j = 0; j < i; j++)
|
|
Packit |
fabffb |
nm_assert (descs[j].flag != descs[i].flag);
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
#endif
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
nm_utils_to_string_buffer_init (&buf, &len;;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (!len)
|
|
Packit |
fabffb |
return buf;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
buf[0] = '\0';
|
|
Packit |
fabffb |
p = buf;
|
|
Packit |
fabffb |
if (!flags) {
|
|
Packit |
fabffb |
for (i = 0; i < n_descs; i++) {
|
|
Packit |
fabffb |
if (!descs[i].flag) {
|
|
Packit |
fabffb |
nm_utils_strbuf_append_str (&p, &len, descs[i].name);
|
|
Packit |
fabffb |
break;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
return buf;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
for (i = 0; flags && i < n_descs; i++) {
|
|
Packit |
fabffb |
if ( descs[i].flag
|
|
Packit |
fabffb |
&& NM_FLAGS_ALL (flags, descs[i].flag)) {
|
|
Packit |
fabffb |
flags &= ~descs[i].flag;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (buf[0] != '\0')
|
|
Packit |
fabffb |
nm_utils_strbuf_append_c (&p, &len, ',');
|
|
Packit |
fabffb |
nm_utils_strbuf_append_str (&p, &len, descs[i].name);
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
if (flags) {
|
|
Packit |
fabffb |
if (buf[0] != '\0')
|
|
Packit |
fabffb |
nm_utils_strbuf_append_c (&p, &len, ',');
|
|
Packit |
fabffb |
nm_utils_strbuf_append (&p, &len, "0x%x", flags);
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
return buf;
|
|
Packit |
fabffb |
};
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/*****************************************************************************/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/**
|
|
Packit |
fabffb |
* _nm_utils_ip4_prefix_to_netmask:
|
|
Packit |
fabffb |
* @prefix: a CIDR prefix
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* Returns: the netmask represented by the prefix, in network byte order
|
|
Packit |
fabffb |
**/
|
|
Packit |
fabffb |
guint32
|
|
Packit |
fabffb |
_nm_utils_ip4_prefix_to_netmask (guint32 prefix)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
return prefix < 32 ? ~htonl(0xFFFFFFFF >> prefix) : 0xFFFFFFFF;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/**
|
|
Packit |
fabffb |
* _nm_utils_ip4_get_default_prefix:
|
|
Packit |
fabffb |
* @ip: an IPv4 address (in network byte order)
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* When the Internet was originally set up, various ranges of IP addresses were
|
|
Packit |
fabffb |
* segmented into three network classes: A, B, and C. This function will return
|
|
Packit |
fabffb |
* a prefix that is associated with the IP address specified defining where it
|
|
Packit |
fabffb |
* falls in the predefined classes.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* Returns: the default class prefix for the given IP
|
|
Packit |
fabffb |
**/
|
|
Packit |
fabffb |
/* The function is originally from ipcalc.c of Red Hat's initscripts. */
|
|
Packit |
fabffb |
guint32
|
|
Packit |
fabffb |
_nm_utils_ip4_get_default_prefix (guint32 ip)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
if (((ntohl (ip) & 0xFF000000) >> 24) <= 127)
|
|
Packit |
fabffb |
return 8; /* Class A - 255.0.0.0 */
|
|
Packit |
fabffb |
else if (((ntohl (ip) & 0xFF000000) >> 24) <= 191)
|
|
Packit |
fabffb |
return 16; /* Class B - 255.255.0.0 */
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
return 24; /* Class C - 255.255.255.0 */
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
gboolean
|
|
Packit |
fabffb |
nm_utils_ip_is_site_local (int addr_family,
|
|
Packit |
fabffb |
const void *address)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
in_addr_t addr4;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
switch (addr_family) {
|
|
Packit |
fabffb |
case AF_INET:
|
|
Packit |
fabffb |
/* RFC1918 private addresses
|
|
Packit |
fabffb |
* 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 */
|
|
Packit |
fabffb |
addr4 = ntohl (*((const in_addr_t *) address));
|
|
Packit |
fabffb |
return (addr4 & 0xff000000) == 0x0a000000
|
|
Packit |
fabffb |
|| (addr4 & 0xfff00000) == 0xac100000
|
|
Packit |
fabffb |
|| (addr4 & 0xffff0000) == 0xc0a80000;
|
|
Packit |
fabffb |
case AF_INET6:
|
|
Packit |
fabffb |
return IN6_IS_ADDR_SITELOCAL (address);
|
|
Packit |
fabffb |
default:
|
|
Packit |
fabffb |
g_return_val_if_reached (FALSE);
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/*****************************************************************************/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
gboolean
|
|
Packit |
fabffb |
nm_utils_parse_inaddr_bin (int addr_family,
|
|
Packit |
fabffb |
const char *text,
|
|
Packit |
fabffb |
gpointer out_addr)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
NMIPAddr addrbin;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
g_return_val_if_fail (text, FALSE);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (addr_family == AF_UNSPEC)
|
|
Packit |
fabffb |
addr_family = strchr (text, ':') ? AF_INET6 : AF_INET;
|
|
Packit |
fabffb |
else
|
|
Packit |
fabffb |
g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/* use a temporary variable @addrbin, to guarantee that @out_addr
|
|
Packit |
fabffb |
* is only modified on success. */
|
|
Packit |
fabffb |
if (inet_pton (addr_family, text, &addrbin) != 1)
|
|
Packit |
fabffb |
return FALSE;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (out_addr) {
|
|
Packit |
fabffb |
switch (addr_family) {
|
|
Packit |
fabffb |
case AF_INET:
|
|
Packit |
fabffb |
*((in_addr_t *) out_addr) = addrbin.addr4;
|
|
Packit |
fabffb |
break;
|
|
Packit |
fabffb |
case AF_INET6:
|
|
Packit |
fabffb |
*((struct in6_addr *) out_addr) = addrbin.addr6;
|
|
Packit |
fabffb |
break;
|
|
Packit |
fabffb |
default:
|
|
Packit |
fabffb |
nm_assert_not_reached ();
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
return TRUE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
gboolean
|
|
Packit |
fabffb |
nm_utils_parse_inaddr (int addr_family,
|
|
Packit |
fabffb |
const char *text,
|
|
Packit |
fabffb |
char **out_addr)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
NMIPAddr addrbin;
|
|
Packit |
fabffb |
char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
nm_assert (!out_addr || !*out_addr);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (!nm_utils_parse_inaddr_bin (addr_family, text, &addrbin))
|
|
Packit |
fabffb |
return FALSE;
|
|
Packit |
fabffb |
NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof (addrstr_buf))));
|
|
Packit |
fabffb |
return TRUE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
gboolean
|
|
Packit |
fabffb |
nm_utils_parse_inaddr_prefix_bin (int addr_family,
|
|
Packit |
fabffb |
const char *text,
|
|
Packit |
fabffb |
gpointer out_addr,
|
|
Packit |
fabffb |
int *out_prefix)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
gs_free char *addrstr_free = NULL;
|
|
Packit |
fabffb |
int prefix = -1;
|
|
Packit |
fabffb |
const char *slash;
|
|
Packit |
fabffb |
const char *addrstr;
|
|
Packit |
fabffb |
NMIPAddr addrbin;
|
|
Packit |
fabffb |
int addr_len;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
g_return_val_if_fail (text, FALSE);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (addr_family == AF_UNSPEC)
|
|
Packit |
fabffb |
addr_family = strchr (text, ':') ? AF_INET6 : AF_INET;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (addr_family == AF_INET)
|
|
Packit |
fabffb |
addr_len = sizeof (in_addr_t);
|
|
Packit |
fabffb |
else if (addr_family == AF_INET6)
|
|
Packit |
fabffb |
addr_len = sizeof (struct in6_addr);
|
|
Packit |
fabffb |
else
|
|
Packit |
fabffb |
g_return_val_if_reached (FALSE);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
slash = strchr (text, '/');
|
|
Packit |
fabffb |
if (slash)
|
|
Packit |
fabffb |
addrstr = addrstr_free = g_strndup (text, slash - text);
|
|
Packit |
fabffb |
else
|
|
Packit |
fabffb |
addrstr = text;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (inet_pton (addr_family, addrstr, &addrbin) != 1)
|
|
Packit |
fabffb |
return FALSE;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (slash) {
|
|
Packit |
fabffb |
prefix = _nm_utils_ascii_str_to_int64 (slash + 1, 10,
|
|
Packit |
fabffb |
0,
|
|
Packit |
fabffb |
addr_family == AF_INET ? 32 : 128,
|
|
Packit |
fabffb |
-1);
|
|
Packit |
fabffb |
if (prefix == -1)
|
|
Packit |
fabffb |
return FALSE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (out_addr)
|
|
Packit |
fabffb |
memcpy (out_addr, &addrbin, addr_len);
|
|
Packit |
fabffb |
NM_SET_OUT (out_prefix, prefix);
|
|
Packit |
fabffb |
return TRUE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
gboolean
|
|
Packit |
fabffb |
nm_utils_parse_inaddr_prefix (int addr_family,
|
|
Packit |
fabffb |
const char *text,
|
|
Packit |
fabffb |
char **out_addr,
|
|
Packit |
fabffb |
int *out_prefix)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
NMIPAddr addrbin;
|
|
Packit |
fabffb |
char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (!nm_utils_parse_inaddr_prefix_bin (addr_family, text, &addrbin, out_prefix))
|
|
Packit |
fabffb |
return FALSE;
|
|
Packit |
fabffb |
NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof (addrstr_buf))));
|
|
Packit |
fabffb |
return TRUE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/*****************************************************************************/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/* _nm_utils_ascii_str_to_int64:
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* A wrapper for g_ascii_strtoll, that checks whether the whole string
|
|
Packit |
fabffb |
* can be successfully converted to a number and is within a given
|
|
Packit |
fabffb |
* range. On any error, @fallback will be returned and %errno will be set
|
|
Packit |
fabffb |
* to a non-zero value. On success, %errno will be set to zero, check %errno
|
|
Packit |
fabffb |
* for errors. Any trailing or leading (ascii) white space is ignored and the
|
|
Packit |
fabffb |
* functions is locale independent.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* The function is guaranteed to return a value between @min and @max
|
|
Packit |
fabffb |
* (inclusive) or @fallback. Also, the parsing is rather strict, it does
|
|
Packit |
fabffb |
* not allow for any unrecognized characters, except leading and trailing
|
|
Packit |
fabffb |
* white space.
|
|
Packit |
fabffb |
**/
|
|
Packit |
fabffb |
gint64
|
|
Packit |
fabffb |
_nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
gint64 v;
|
|
Packit |
fabffb |
const char *s = NULL;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (str) {
|
|
Packit |
fabffb |
while (g_ascii_isspace (str[0]))
|
|
Packit |
fabffb |
str++;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
if (!str || !str[0]) {
|
|
Packit |
fabffb |
errno = EINVAL;
|
|
Packit |
fabffb |
return fallback;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
errno = 0;
|
|
Packit |
fabffb |
v = g_ascii_strtoll (str, (char **) &s, base);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (errno != 0)
|
|
Packit |
fabffb |
return fallback;
|
|
Packit |
fabffb |
if (s[0] != '\0') {
|
|
Packit |
fabffb |
while (g_ascii_isspace (s[0]))
|
|
Packit |
fabffb |
s++;
|
|
Packit |
fabffb |
if (s[0] != '\0') {
|
|
Packit |
fabffb |
errno = EINVAL;
|
|
Packit |
fabffb |
return fallback;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
if (v > max || v < min) {
|
|
Packit |
fabffb |
errno = ERANGE;
|
|
Packit |
fabffb |
return fallback;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
return v;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/*****************************************************************************/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/**
|
|
Packit |
fabffb |
* nm_utils_strsplit_set:
|
|
Packit |
fabffb |
* @str: the string to split.
|
|
Packit |
fabffb |
* @delimiters: the set of delimiters. If %NULL, defaults to " \t\n",
|
|
Packit |
fabffb |
* like bash's $IFS.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* This is a replacement for g_strsplit_set() which avoids copying
|
|
Packit |
fabffb |
* each word once (the entire strv array), but instead copies it once
|
|
Packit |
fabffb |
* and all words point into that internal copy.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* Another difference from g_strsplit_set() is that this never returns
|
|
Packit |
fabffb |
* empty words. Multiple delimiters are combined and treated as one.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* Returns: %NULL if @str is %NULL or contains only delimiters.
|
|
Packit |
fabffb |
* Otherwise, a %NULL terminated strv array containing non-empty
|
|
Packit |
fabffb |
* words, split at the delimiter characters (delimiter characters
|
|
Packit |
fabffb |
* are removed).
|
|
Packit |
fabffb |
* The strings to which the result strv array points to are allocated
|
|
Packit |
fabffb |
* after the returned result itself. Don't free the strings themself,
|
|
Packit |
fabffb |
* but free everything with g_free().
|
|
Packit |
fabffb |
*/
|
|
Packit |
fabffb |
const char **
|
|
Packit |
fabffb |
nm_utils_strsplit_set (const char *str, const char *delimiters)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
const char **ptr, **ptr0;
|
|
Packit |
fabffb |
gsize alloc_size, plen, i;
|
|
Packit |
fabffb |
gsize str_len;
|
|
Packit |
fabffb |
char *s0;
|
|
Packit |
fabffb |
char *s;
|
|
Packit |
fabffb |
guint8 delimiters_table[256];
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (!str)
|
|
Packit |
fabffb |
return NULL;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/* initialize lookup table for delimiter */
|
|
Packit |
fabffb |
if (!delimiters)
|
|
Packit |
fabffb |
delimiters = " \t\n";
|
|
Packit |
fabffb |
memset (delimiters_table, 0, sizeof (delimiters_table));
|
|
Packit |
fabffb |
for (i = 0; delimiters[i]; i++)
|
|
Packit |
fabffb |
delimiters_table[(guint8) delimiters[i]] = 1;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
#define _is_delimiter(ch, delimiters_table) \
|
|
Packit |
fabffb |
((delimiters_table)[(guint8) (ch)] != 0)
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/* skip initial delimiters, and return of the remaining string is
|
|
Packit |
fabffb |
* empty. */
|
|
Packit |
fabffb |
while (_is_delimiter (str[0], delimiters_table))
|
|
Packit |
fabffb |
str++;
|
|
Packit |
fabffb |
if (!str[0])
|
|
Packit |
fabffb |
return NULL;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
str_len = strlen (str) + 1;
|
|
Packit |
fabffb |
alloc_size = 8;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/* we allocate the buffer larger, so to copy @str at the
|
|
Packit |
fabffb |
* end of it as @s0. */
|
|
Packit |
fabffb |
ptr0 = g_malloc ((sizeof (const char *) * (alloc_size + 1)) + str_len);
|
|
Packit |
fabffb |
s0 = (char *) &ptr0[alloc_size + 1];
|
|
Packit |
fabffb |
memcpy (s0, str, str_len);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
plen = 0;
|
|
Packit |
fabffb |
s = s0;
|
|
Packit |
fabffb |
ptr = ptr0;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
while (TRUE) {
|
|
Packit |
fabffb |
if (plen >= alloc_size) {
|
|
Packit |
fabffb |
const char **ptr_old = ptr;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/* reallocate the buffer. Note that for now the string
|
|
Packit |
fabffb |
* continues to be in ptr0/s0. We fix that at the end. */
|
|
Packit |
fabffb |
alloc_size *= 2;
|
|
Packit |
fabffb |
ptr = g_malloc ((sizeof (const char *) * (alloc_size + 1)) + str_len);
|
|
Packit |
fabffb |
memcpy (ptr, ptr_old, sizeof (const char *) * plen);
|
|
Packit |
fabffb |
if (ptr_old != ptr0)
|
|
Packit |
fabffb |
g_free (ptr_old);
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
ptr[plen++] = s;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
nm_assert (s[0] && !_is_delimiter (s[0], delimiters_table));
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
while (TRUE) {
|
|
Packit |
fabffb |
s++;
|
|
Packit |
fabffb |
if (_is_delimiter (s[0], delimiters_table))
|
|
Packit |
fabffb |
break;
|
|
Packit |
fabffb |
if (s[0] == '\0')
|
|
Packit |
fabffb |
goto done;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
s[0] = '\0';
|
|
Packit |
fabffb |
s++;
|
|
Packit |
fabffb |
while (_is_delimiter (s[0], delimiters_table))
|
|
Packit |
fabffb |
s++;
|
|
Packit |
fabffb |
if (s[0] == '\0')
|
|
Packit |
fabffb |
break;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
done:
|
|
Packit |
fabffb |
ptr[plen] = NULL;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (ptr != ptr0) {
|
|
Packit |
fabffb |
/* we reallocated the buffer. We must copy over the
|
|
Packit |
fabffb |
* string @s0 and adjust the pointers. */
|
|
Packit |
fabffb |
s = (char *) &ptr[alloc_size + 1];
|
|
Packit |
fabffb |
memcpy (s, s0, str_len);
|
|
Packit |
fabffb |
for (i = 0; i < plen; i++)
|
|
Packit |
fabffb |
ptr[i] = &s[ptr[i] - s0];
|
|
Packit |
fabffb |
g_free (ptr0);
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
return ptr;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/**
|
|
Packit |
fabffb |
* nm_utils_strv_find_first:
|
|
Packit |
fabffb |
* @list: the strv list to search
|
|
Packit |
fabffb |
* @len: the length of the list, or a negative value if @list is %NULL terminated.
|
|
Packit |
fabffb |
* @needle: the value to search for. The search is done using strcmp().
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* Searches @list for @needle and returns the index of the first match (based
|
|
Packit |
fabffb |
* on strcmp()).
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* For convenience, @list has type 'char**' instead of 'const char **'.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* Returns: index of first occurrence or -1 if @needle is not found in @list.
|
|
Packit |
fabffb |
*/
|
|
Packit |
fabffb |
gssize
|
|
Packit |
fabffb |
nm_utils_strv_find_first (char **list, gssize len, const char *needle)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
gssize i;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (len > 0) {
|
|
Packit |
fabffb |
g_return_val_if_fail (list, -1);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (!needle) {
|
|
Packit |
fabffb |
/* if we search a list with known length, %NULL is a valid @needle. */
|
|
Packit |
fabffb |
for (i = 0; i < len; i++) {
|
|
Packit |
fabffb |
if (!list[i])
|
|
Packit |
fabffb |
return i;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
} else {
|
|
Packit |
fabffb |
for (i = 0; i < len; i++) {
|
|
Packit |
fabffb |
if (list[i] && !strcmp (needle, list[i]))
|
|
Packit |
fabffb |
return i;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
} else if (len < 0) {
|
|
Packit |
fabffb |
g_return_val_if_fail (needle, -1);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (list) {
|
|
Packit |
fabffb |
for (i = 0; list[i]; i++) {
|
|
Packit |
fabffb |
if (strcmp (needle, list[i]) == 0)
|
|
Packit |
fabffb |
return i;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
return -1;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
char **
|
|
Packit |
fabffb |
_nm_utils_strv_cleanup (char **strv,
|
|
Packit |
fabffb |
gboolean strip_whitespace,
|
|
Packit |
fabffb |
gboolean skip_empty,
|
|
Packit |
fabffb |
gboolean skip_repeated)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
guint i, j;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (!strv || !*strv)
|
|
Packit |
fabffb |
return strv;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (strip_whitespace) {
|
|
Packit |
fabffb |
for (i = 0; strv[i]; i++)
|
|
Packit |
fabffb |
g_strstrip (strv[i]);
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
if (!skip_empty && !skip_repeated)
|
|
Packit |
fabffb |
return strv;
|
|
Packit |
fabffb |
j = 0;
|
|
Packit |
fabffb |
for (i = 0; strv[i]; i++) {
|
|
Packit |
fabffb |
if ( (skip_empty && !*strv[i])
|
|
Packit |
fabffb |
|| (skip_repeated && nm_utils_strv_find_first (strv, j, strv[i]) >= 0))
|
|
Packit |
fabffb |
g_free (strv[i]);
|
|
Packit |
fabffb |
else
|
|
Packit |
fabffb |
strv[j++] = strv[i];
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
strv[j] = NULL;
|
|
Packit |
fabffb |
return strv;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/*****************************************************************************/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
gint
|
|
Packit |
fabffb |
_nm_utils_ascii_str_to_bool (const char *str,
|
|
Packit |
fabffb |
gint default_value)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
gsize len;
|
|
Packit |
fabffb |
char *s = NULL;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (!str)
|
|
Packit |
fabffb |
return default_value;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
while (str[0] && g_ascii_isspace (str[0]))
|
|
Packit |
fabffb |
str++;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (!str[0])
|
|
Packit |
fabffb |
return default_value;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
len = strlen (str);
|
|
Packit |
fabffb |
if (g_ascii_isspace (str[len - 1])) {
|
|
Packit |
fabffb |
s = g_strdup (str);
|
|
Packit |
fabffb |
g_strchomp (s);
|
|
Packit |
fabffb |
str = s;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1"))
|
|
Packit |
fabffb |
default_value = TRUE;
|
|
Packit |
fabffb |
else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0"))
|
|
Packit |
fabffb |
default_value = FALSE;
|
|
Packit |
fabffb |
if (s)
|
|
Packit |
fabffb |
g_free (s);
|
|
Packit |
fabffb |
return default_value;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/*****************************************************************************/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
NM_CACHED_QUARK_FCN ("nm-utils-error-quark", nm_utils_error_quark)
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
void
|
|
Packit |
fabffb |
nm_utils_error_set_cancelled (GError **error,
|
|
Packit |
fabffb |
gboolean is_disposing,
|
|
Packit |
fabffb |
const char *instance_name)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
if (is_disposing) {
|
|
Packit |
fabffb |
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_CANCELLED_DISPOSING,
|
|
Packit |
fabffb |
"Disposing %s instance",
|
|
Packit |
fabffb |
instance_name && *instance_name ? instance_name : "source");
|
|
Packit |
fabffb |
} else {
|
|
Packit |
fabffb |
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED,
|
|
Packit |
fabffb |
"Request cancelled");
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
gboolean
|
|
Packit |
fabffb |
nm_utils_error_is_cancelled (GError *error,
|
|
Packit |
fabffb |
gboolean consider_is_disposing)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
if (error) {
|
|
Packit |
fabffb |
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
Packit |
fabffb |
return TRUE;
|
|
Packit |
fabffb |
if ( consider_is_disposing
|
|
Packit |
fabffb |
&& g_error_matches (error, NM_UTILS_ERROR, NM_UTILS_ERROR_CANCELLED_DISPOSING))
|
|
Packit |
fabffb |
return TRUE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
return FALSE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/*****************************************************************************/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/**
|
|
Packit |
fabffb |
* nm_g_object_set_property:
|
|
Packit |
fabffb |
* @object: the target object
|
|
Packit |
fabffb |
* @property_name: the property name
|
|
Packit |
fabffb |
* @value: the #GValue to set
|
|
Packit |
fabffb |
* @error: (allow-none): optional error argument
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* A reimplementation of g_object_set_property(), but instead
|
|
Packit |
fabffb |
* returning an error instead of logging a warning. All g_object_set*()
|
|
Packit |
fabffb |
* versions in glib require you to not pass invalid types or they will
|
|
Packit |
fabffb |
* log a g_warning() -- without reporting an error. We don't want that,
|
|
Packit |
fabffb |
* so we need to hack error checking around it.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* Returns: whether the value was successfully set.
|
|
Packit |
fabffb |
*/
|
|
Packit |
fabffb |
gboolean
|
|
Packit |
fabffb |
nm_g_object_set_property (GObject *object,
|
|
Packit |
fabffb |
const gchar *property_name,
|
|
Packit |
fabffb |
const GValue *value,
|
|
Packit |
fabffb |
GError **error)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
GParamSpec *pspec;
|
|
Packit |
fabffb |
nm_auto_unset_gvalue GValue tmp_value = G_VALUE_INIT;
|
|
Packit |
fabffb |
GObjectClass *klass;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
|
Packit |
fabffb |
g_return_val_if_fail (property_name != NULL, FALSE);
|
|
Packit |
fabffb |
g_return_val_if_fail (G_IS_VALUE (value), FALSE);
|
|
Packit |
fabffb |
g_return_val_if_fail (!error || !*error, FALSE);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/* g_object_class_find_property() does g_param_spec_get_redirect_target(),
|
|
Packit |
fabffb |
* where we differ from a plain g_object_set_property(). */
|
|
Packit |
fabffb |
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), property_name);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (!pspec) {
|
|
Packit |
fabffb |
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
|
Packit |
fabffb |
_("object class '%s' has no property named '%s'"),
|
|
Packit |
fabffb |
G_OBJECT_TYPE_NAME (object),
|
|
Packit |
fabffb |
property_name);
|
|
Packit |
fabffb |
return FALSE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
if (!(pspec->flags & G_PARAM_WRITABLE)) {
|
|
Packit |
fabffb |
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
|
Packit |
fabffb |
_("property '%s' of object class '%s' is not writable"),
|
|
Packit |
fabffb |
pspec->name,
|
|
Packit |
fabffb |
G_OBJECT_TYPE_NAME (object));
|
|
Packit |
fabffb |
return FALSE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
if ((pspec->flags & G_PARAM_CONSTRUCT_ONLY)) {
|
|
Packit |
fabffb |
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
|
Packit |
fabffb |
_("construct property \"%s\" for object '%s' can't be set after construction"),
|
|
Packit |
fabffb |
pspec->name, G_OBJECT_TYPE_NAME (object));
|
|
Packit |
fabffb |
return FALSE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
klass = g_type_class_peek (pspec->owner_type);
|
|
Packit |
fabffb |
if (klass == NULL) {
|
|
Packit |
fabffb |
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
|
Packit |
fabffb |
_("'%s::%s' is not a valid property name; '%s' is not a GObject subtype"),
|
|
Packit |
fabffb |
g_type_name (pspec->owner_type), pspec->name, g_type_name (pspec->owner_type));
|
|
Packit |
fabffb |
return FALSE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/* provide a copy to work from, convert (if necessary) and validate */
|
|
Packit |
fabffb |
g_value_init (&tmp_value, pspec->value_type);
|
|
Packit |
fabffb |
if (!g_value_transform (value, &tmp_value)) {
|
|
Packit |
fabffb |
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
|
Packit |
fabffb |
_("unable to set property '%s' of type '%s' from value of type '%s'"),
|
|
Packit |
fabffb |
pspec->name,
|
|
Packit |
fabffb |
g_type_name (pspec->value_type),
|
|
Packit |
fabffb |
G_VALUE_TYPE_NAME (value));
|
|
Packit |
fabffb |
return FALSE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
if ( g_param_value_validate (pspec, &tmp_value)
|
|
Packit |
fabffb |
&& !(pspec->flags & G_PARAM_LAX_VALIDATION)) {
|
|
Packit |
fabffb |
gs_free char *contents = g_strdup_value_contents (value);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
|
Packit |
fabffb |
_("value \"%s\" of type '%s' is invalid or out of range for property '%s' of type '%s'"),
|
|
Packit |
fabffb |
contents,
|
|
Packit |
fabffb |
G_VALUE_TYPE_NAME (value),
|
|
Packit |
fabffb |
pspec->name,
|
|
Packit |
fabffb |
g_type_name (pspec->value_type));
|
|
Packit |
fabffb |
return FALSE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
g_object_set_property (object, property_name, &tmp_value);
|
|
Packit |
fabffb |
return TRUE;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
gboolean
|
|
Packit |
fabffb |
nm_g_object_set_property_boolean (GObject *object,
|
|
Packit |
fabffb |
const gchar *property_name,
|
|
Packit |
fabffb |
gboolean value,
|
|
Packit |
fabffb |
GError **error)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
nm_auto_unset_gvalue GValue gvalue = { 0 };
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
g_value_init (&gvalue, G_TYPE_BOOLEAN);
|
|
Packit |
fabffb |
g_value_set_boolean (&gvalue, !!value);
|
|
Packit |
fabffb |
return nm_g_object_set_property (object, property_name, &gvalue, error);
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
gboolean
|
|
Packit |
fabffb |
nm_g_object_set_property_uint (GObject *object,
|
|
Packit |
fabffb |
const gchar *property_name,
|
|
Packit |
fabffb |
guint value,
|
|
Packit |
fabffb |
GError **error)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
nm_auto_unset_gvalue GValue gvalue = { 0 };
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
g_value_init (&gvalue, G_TYPE_UINT);
|
|
Packit |
fabffb |
g_value_set_uint (&gvalue, value);
|
|
Packit |
fabffb |
return nm_g_object_set_property (object, property_name, &gvalue, error);
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
GParamSpec *
|
|
Packit |
fabffb |
nm_g_object_class_find_property_from_gtype (GType gtype,
|
|
Packit |
fabffb |
const char *property_name)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
nm_auto_unref_gtypeclass GObjectClass *gclass = NULL;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
gclass = g_type_class_ref (gtype);
|
|
Packit |
fabffb |
return g_object_class_find_property (gclass, property_name);
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/*****************************************************************************/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
static void
|
|
Packit |
fabffb |
_str_append_escape (GString *s, char ch)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
g_string_append_c (s, '\\');
|
|
Packit |
fabffb |
g_string_append_c (s, '0' + ((((guchar) ch) >> 6) & 07));
|
|
Packit |
fabffb |
g_string_append_c (s, '0' + ((((guchar) ch) >> 3) & 07));
|
|
Packit |
fabffb |
g_string_append_c (s, '0' + ( ((guchar) ch) & 07));
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/**
|
|
Packit |
fabffb |
* nm_utils_str_utf8safe_escape:
|
|
Packit |
fabffb |
* @str: NUL terminated input string, possibly in utf-8 encoding
|
|
Packit |
fabffb |
* @flags: #NMUtilsStrUtf8SafeFlags flags
|
|
Packit |
fabffb |
* @to_free: (out): return the pointer location of the string
|
|
Packit |
fabffb |
* if a copying was necessary.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* Returns the possible non-UTF-8 NUL terminated string @str
|
|
Packit |
fabffb |
* and uses backslash escaping (C escaping, like g_strescape())
|
|
Packit |
fabffb |
* to sanitize non UTF-8 characters. The result is valid
|
|
Packit |
fabffb |
* UTF-8.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* The operation can be reverted with g_strcompress() or
|
|
Packit |
fabffb |
* nm_utils_str_utf8safe_unescape().
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* Depending on @flags, valid UTF-8 characters are not escaped at all
|
|
Packit |
fabffb |
* (except the escape character '\\'). This is the difference to g_strescape(),
|
|
Packit |
fabffb |
* which escapes all non-ASCII characters. This allows to pass on
|
|
Packit |
fabffb |
* valid UTF-8 characters as-is and can be directly shown to the user
|
|
Packit |
fabffb |
* as UTF-8 -- with exception of the backslash escape character,
|
|
Packit |
fabffb |
* invalid UTF-8 sequences, and other (depending on @flags).
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* Returns: the escaped input string, as valid UTF-8. If no escaping
|
|
Packit |
fabffb |
* is necessary, it returns the input @str. Otherwise, an allocated
|
|
Packit |
fabffb |
* string @to_free is returned which must be freed by the caller
|
|
Packit |
fabffb |
* with g_free. The escaping can be reverted by g_strcompress().
|
|
Packit |
fabffb |
**/
|
|
Packit |
fabffb |
const char *
|
|
Packit |
fabffb |
nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
const char *p = NULL;
|
|
Packit |
fabffb |
GString *s;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
g_return_val_if_fail (to_free, NULL);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
*to_free = NULL;
|
|
Packit |
fabffb |
if (!str || !str[0])
|
|
Packit |
fabffb |
return str;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if ( g_utf8_validate (str, -1, &p)
|
|
Packit |
fabffb |
&& !NM_STRCHAR_ANY (str, ch,
|
|
Packit |
fabffb |
( ch == '\\' \
|
|
Packit |
fabffb |
|| ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) \
|
|
Packit |
fabffb |
&& ch < ' ') \
|
|
Packit |
fabffb |
|| ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) \
|
|
Packit |
fabffb |
&& ((guchar) ch) >= 127))))
|
|
Packit |
fabffb |
return str;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
s = g_string_sized_new ((p - str) + strlen (p) + 5);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
do {
|
|
Packit |
fabffb |
for (; str < p; str++) {
|
|
Packit |
fabffb |
char ch = str[0];
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (ch == '\\')
|
|
Packit |
fabffb |
g_string_append (s, "\\\\");
|
|
Packit |
fabffb |
else if ( ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) \
|
|
Packit |
fabffb |
&& ch < ' ') \
|
|
Packit |
fabffb |
|| ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) \
|
|
Packit |
fabffb |
&& ((guchar) ch) >= 127))
|
|
Packit |
fabffb |
_str_append_escape (s, ch);
|
|
Packit |
fabffb |
else
|
|
Packit |
fabffb |
g_string_append_c (s, ch);
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (p[0] == '\0')
|
|
Packit |
fabffb |
break;
|
|
Packit |
fabffb |
_str_append_escape (s, p[0]);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
str = &p[1];
|
|
Packit |
fabffb |
g_utf8_validate (str, -1, &p);
|
|
Packit |
fabffb |
} while (TRUE);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
*to_free = g_string_free (s, FALSE);
|
|
Packit |
fabffb |
return *to_free;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
const char *
|
|
Packit |
fabffb |
nm_utils_str_utf8safe_unescape (const char *str, char **to_free)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
g_return_val_if_fail (to_free, NULL);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (!str || !strchr (str, '\\')) {
|
|
Packit |
fabffb |
*to_free = NULL;
|
|
Packit |
fabffb |
return str;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
return (*to_free = g_strcompress (str));
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/**
|
|
Packit |
fabffb |
* nm_utils_str_utf8safe_escape_cp:
|
|
Packit |
fabffb |
* @str: NUL terminated input string, possibly in utf-8 encoding
|
|
Packit |
fabffb |
* @flags: #NMUtilsStrUtf8SafeFlags flags
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* Like nm_utils_str_utf8safe_escape(), except the returned value
|
|
Packit |
fabffb |
* is always a copy of the input and must be freed by the caller.
|
|
Packit |
fabffb |
*
|
|
Packit |
fabffb |
* Returns: the escaped input string in UTF-8 encoding. The returned
|
|
Packit |
fabffb |
* value should be freed with g_free().
|
|
Packit |
fabffb |
* The escaping can be reverted by g_strcompress().
|
|
Packit |
fabffb |
**/
|
|
Packit |
fabffb |
char *
|
|
Packit |
fabffb |
nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
char *s;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
nm_utils_str_utf8safe_escape (str, flags, &s);
|
|
Packit |
fabffb |
return s ?: g_strdup (str);
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
char *
|
|
Packit |
fabffb |
nm_utils_str_utf8safe_unescape_cp (const char *str)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
return str ? g_strcompress (str) : NULL;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
char *
|
|
Packit |
fabffb |
nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
char *str_to_free;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
nm_utils_str_utf8safe_escape (str, flags, &str_to_free);
|
|
Packit |
fabffb |
if (str_to_free) {
|
|
Packit |
fabffb |
g_free (str);
|
|
Packit |
fabffb |
return str_to_free;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
return str;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/*****************************************************************************/
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/* taken from systemd's fd_wait_for_event(). Note that the timeout
|
|
Packit |
fabffb |
* is here in nano-seconds, not micro-seconds. */
|
|
Packit |
fabffb |
int
|
|
Packit |
fabffb |
nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
struct pollfd pollfd = {
|
|
Packit |
fabffb |
.fd = fd,
|
|
Packit |
fabffb |
.events = event,
|
|
Packit |
fabffb |
};
|
|
Packit |
fabffb |
struct timespec ts, *pts;
|
|
Packit |
fabffb |
int r;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (timeout_ns < 0)
|
|
Packit |
fabffb |
pts = NULL;
|
|
Packit |
fabffb |
else {
|
|
Packit |
fabffb |
ts.tv_sec = (time_t) (timeout_ns / NM_UTILS_NS_PER_SECOND);
|
|
Packit |
fabffb |
ts.tv_nsec = (long int) (timeout_ns % NM_UTILS_NS_PER_SECOND);
|
|
Packit |
fabffb |
pts = &ts;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
r = ppoll (&pollfd, 1, pts, NULL);
|
|
Packit |
fabffb |
if (r < 0)
|
|
Packit |
fabffb |
return -errno;
|
|
Packit |
fabffb |
if (r == 0)
|
|
Packit |
fabffb |
return 0;
|
|
Packit |
fabffb |
return pollfd.revents;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/* taken from systemd's loop_read() */
|
|
Packit |
fabffb |
ssize_t
|
|
Packit |
fabffb |
nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
uint8_t *p = buf;
|
|
Packit |
fabffb |
ssize_t n = 0;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
g_return_val_if_fail (fd >= 0, -EINVAL);
|
|
Packit |
fabffb |
g_return_val_if_fail (buf, -EINVAL);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/* If called with nbytes == 0, let's call read() at least
|
|
Packit |
fabffb |
* once, to validate the operation */
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (nbytes > (size_t) SSIZE_MAX)
|
|
Packit |
fabffb |
return -EINVAL;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
do {
|
|
Packit |
fabffb |
ssize_t k;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
k = read (fd, p, nbytes);
|
|
Packit |
fabffb |
if (k < 0) {
|
|
Packit |
fabffb |
if (errno == EINTR)
|
|
Packit |
fabffb |
continue;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (errno == EAGAIN && do_poll) {
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/* We knowingly ignore any return value here,
|
|
Packit |
fabffb |
* and expect that any error/EOF is reported
|
|
Packit |
fabffb |
* via read() */
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
(void) nm_utils_fd_wait_for_event (fd, POLLIN, -1);
|
|
Packit |
fabffb |
continue;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
return n > 0 ? n : -errno;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (k == 0)
|
|
Packit |
fabffb |
return n;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
g_assert ((size_t) k <= nbytes);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
p += k;
|
|
Packit |
fabffb |
nbytes -= k;
|
|
Packit |
fabffb |
n += k;
|
|
Packit |
fabffb |
} while (nbytes > 0);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
return n;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/* taken from systemd's loop_read_exact() */
|
|
Packit |
fabffb |
int
|
|
Packit |
fabffb |
nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
ssize_t n;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
n = nm_utils_fd_read_loop (fd, buf, nbytes, do_poll);
|
|
Packit |
fabffb |
if (n < 0)
|
|
Packit |
fabffb |
return (int) n;
|
|
Packit |
fabffb |
if ((size_t) n != nbytes)
|
|
Packit |
fabffb |
return -EIO;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
return 0;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
NMUtilsNamedValue *
|
|
Packit |
fabffb |
nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
GHashTableIter iter;
|
|
Packit |
fabffb |
NMUtilsNamedValue *values;
|
|
Packit |
fabffb |
guint i, len;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if ( !hash
|
|
Packit |
fabffb |
|| !(len = g_hash_table_size (hash))) {
|
|
Packit |
fabffb |
NM_SET_OUT (out_len, 0);
|
|
Packit |
fabffb |
return NULL;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
i = 0;
|
|
Packit |
fabffb |
values = g_new (NMUtilsNamedValue, len + 1);
|
|
Packit |
fabffb |
g_hash_table_iter_init (&iter, hash);
|
|
Packit |
fabffb |
while (g_hash_table_iter_next (&iter,
|
|
Packit |
fabffb |
(gpointer *) &values[i].name,
|
|
Packit |
fabffb |
(gpointer *) &values[i].value_ptr))
|
|
Packit |
fabffb |
i++;
|
|
Packit |
fabffb |
nm_assert (i == len);
|
|
Packit |
fabffb |
values[i].name = NULL;
|
|
Packit |
fabffb |
values[i].value_ptr = NULL;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (len > 1) {
|
|
Packit |
fabffb |
g_qsort_with_data (values, len, sizeof (values[0]),
|
|
Packit |
fabffb |
nm_utils_named_entry_cmp_with_data, NULL);
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
NM_SET_OUT (out_len, len);
|
|
Packit |
fabffb |
return values;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
const char **
|
|
Packit |
fabffb |
nm_utils_strdict_get_keys (const GHashTable *hash,
|
|
Packit |
fabffb |
gboolean sorted,
|
|
Packit |
fabffb |
guint *out_length)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
const char **names;
|
|
Packit |
fabffb |
guint length;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if ( !hash
|
|
Packit |
fabffb |
|| !g_hash_table_size ((GHashTable *) hash)) {
|
|
Packit |
fabffb |
NM_SET_OUT (out_length, 0);
|
|
Packit |
fabffb |
return NULL;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
names = (const char **) g_hash_table_get_keys_as_array ((GHashTable *) hash, &length);
|
|
Packit |
fabffb |
if ( sorted
|
|
Packit |
fabffb |
&& length > 1) {
|
|
Packit |
fabffb |
g_qsort_with_data (names,
|
|
Packit |
fabffb |
length,
|
|
Packit |
fabffb |
sizeof (char *),
|
|
Packit |
fabffb |
nm_strcmp_p_with_data,
|
|
Packit |
fabffb |
NULL);
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
NM_SET_OUT (out_length, length);
|
|
Packit |
fabffb |
return names;
|
|
Packit |
fabffb |
}
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
char **
|
|
Packit |
fabffb |
nm_utils_strv_make_deep_copied (const char **strv)
|
|
Packit |
fabffb |
{
|
|
Packit |
fabffb |
gsize i;
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
/* it takes a strv dictionary, and copies each
|
|
Packit |
fabffb |
* strings. Note that this updates @strv *in-place*
|
|
Packit |
fabffb |
* and returns it. */
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
if (!strv)
|
|
Packit |
fabffb |
return NULL;
|
|
Packit |
fabffb |
for (i = 0; strv[i]; i++)
|
|
Packit |
fabffb |
strv[i] = g_strdup (strv[i]);
|
|
Packit |
fabffb |
|
|
Packit |
fabffb |
return (char **) strv;
|
|
Packit |
fabffb |
}
|