Some checks failed
CI / ubuntu-latest-html-tests (push) Has been cancelled
CI / alpine-mbedtls-3_6_0 (push) Has been cancelled
CI / ubuntu-latest-no-tls (push) Has been cancelled
CI / ubuntu-latest-mbedtls2 (push) Has been cancelled
CI / ubuntu-latest-openssl-3 (push) Has been cancelled
CI / ubuntu-latest-with-old-std (push) Has been cancelled
CI / ubuntu-20-04-openssl-1-1 (push) Has been cancelled
CI / macOS-13-openssl-1-1 (push) Has been cancelled
CI / macOS-13-openssl-3 (push) Has been cancelled
CI / freebsd-14-openssl-3 (push) Has been cancelled
CI / windows-mbedtls (push) Has been cancelled
This fixes at least one regression.
1040 lines
20 KiB
C++
1040 lines
20 KiB
C++
/*
|
|
* File: dlib.c
|
|
*
|
|
* Copyright (C) 2006-2007 Jorge Arellano Cid <jcid@dillo.org>
|
|
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
|
*
|
|
* 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
/* Memory allocation, Simple dynamic strings, Lists (simple and sorted),
|
|
* and a few utility functions
|
|
*/
|
|
|
|
/*
|
|
* TODO: vsnprintf() is in C99, maybe a simple replacement if necessary.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
|
|
#include <Alepha/AutoRAII.h>
|
|
|
|
#include "dlib.hh"
|
|
|
|
static bool dLib_show_msg = TRUE;
|
|
|
|
/* dlib msgs go to stderr to avoid problems with filter dpis */
|
|
#define DLIB_MSG(...) \
|
|
D_STMT_START { \
|
|
if (dLib_show_msg) \
|
|
fprintf(stderr, __VA_ARGS__); \
|
|
} D_STMT_END
|
|
|
|
/*
|
|
*- Memory --------------------------------------------------------------------
|
|
*/
|
|
|
|
void *dMalloc (size_t size)
|
|
{
|
|
void *value = malloc (size);
|
|
if (value == 0)
|
|
exit(1);
|
|
return value;
|
|
}
|
|
|
|
void *dRealloc (void *mem, size_t size)
|
|
{
|
|
void *value = realloc (mem, size);
|
|
if (value == 0)
|
|
exit(1);
|
|
return value;
|
|
}
|
|
|
|
void *dMalloc0 (size_t size)
|
|
{
|
|
void *value = dMalloc (size);
|
|
memset (value, 0, size);
|
|
return value;
|
|
}
|
|
|
|
void dFree (void *mem)
|
|
{
|
|
free(mem);
|
|
}
|
|
|
|
/*
|
|
*- strings (char *) ----------------------------------------------------------
|
|
*/
|
|
|
|
CharPtrNoStringConversion dStrdup(const char *s)
|
|
{
|
|
if (s) {
|
|
int len = strlen(s)+1;
|
|
char *ns = dNew(char, len);
|
|
memcpy(ns, s, len);
|
|
return { ns };
|
|
}
|
|
return {};
|
|
}
|
|
|
|
CharPtrNoStringConversion dStrndup(const char *s, size_t sz)
|
|
{
|
|
if (s) {
|
|
char *ns = dNew(char, sz+1);
|
|
memcpy(ns, s, sz);
|
|
ns[sz] = 0;
|
|
return { ns };
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/**
|
|
* Concatenate a NULL-terminated list of strings
|
|
*/
|
|
CharPtrNoStringConversion dStrconcat(const char *s1, ...)
|
|
{
|
|
va_list args;
|
|
char *s, *ns = NULL;
|
|
|
|
if (s1) {
|
|
Dstr *dstr = dStr_sized_new(64);
|
|
va_start(args, s1);
|
|
for (s = (char*)s1; s; s = va_arg(args, char*))
|
|
dStr_append(dstr, s);
|
|
va_end(args);
|
|
ns = dstr->str;
|
|
dStr_free(dstr, 0);
|
|
}
|
|
return { ns };
|
|
}
|
|
|
|
/**
|
|
* Remove leading and trailing whitespace
|
|
*/
|
|
char *dStrstrip(char *s)
|
|
{
|
|
char *p;
|
|
int len;
|
|
|
|
if (s && *s) {
|
|
for (p = s; dIsspace(*p); ++p);
|
|
for (len = strlen(p); len && dIsspace(p[len-1]); --len);
|
|
if (p > s)
|
|
memmove(s, p, len);
|
|
s[len] = 0;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Clear the contents of the string
|
|
*/
|
|
void dStrshred(char *s)
|
|
{
|
|
if (s)
|
|
memset(s, 0, strlen(s));
|
|
}
|
|
|
|
/**
|
|
* Return a new string of length 'len' filled with 'c' characters
|
|
*/
|
|
CharPtrNoStringConversion dStrnfill(size_t len, char c)
|
|
{
|
|
char *ret = dNew(char, len+1);
|
|
for (ret[len] = 0; len > 0; ret[--len] = c);
|
|
return { ret };
|
|
}
|
|
|
|
/**
|
|
* strsep() implementation
|
|
*/
|
|
char *dStrsep(char **orig, const char *delim)
|
|
{
|
|
char *str, *p;
|
|
|
|
if (!(str = *orig))
|
|
return NULL;
|
|
|
|
p = strpbrk(str, delim);
|
|
if (p) {
|
|
*p++ = 0;
|
|
*orig = p;
|
|
} else {
|
|
*orig = NULL;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
/*
|
|
* ASCII functions to avoid the case difficulties introduced by I/i in
|
|
* Turkic locales.
|
|
*/
|
|
|
|
/**
|
|
* Case insensitive strstr
|
|
*/
|
|
char *dStriAsciiStr(const char *haystack, const char *needle)
|
|
{
|
|
int i, j;
|
|
char *ret = NULL;
|
|
|
|
if (haystack && needle) {
|
|
for (i = 0, j = 0; haystack[i] && needle[j]; ++i)
|
|
if (D_ASCII_TOLOWER(haystack[i]) == D_ASCII_TOLOWER(needle[j])) {
|
|
++j;
|
|
} else if (j) {
|
|
i -= j;
|
|
j = 0;
|
|
}
|
|
if (!needle[j]) /* Got all */
|
|
ret = (char *)(haystack + i - j);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int dStrAsciiCasecmp(const char *s1, const char *s2)
|
|
{
|
|
int ret = 0;
|
|
|
|
while ((*s1 || *s2) &&
|
|
!(ret = D_ASCII_TOLOWER(*s1) - D_ASCII_TOLOWER(*s2))) {
|
|
s1++;
|
|
s2++;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int dStrnAsciiCasecmp(const char *s1, const char *s2, size_t n)
|
|
{
|
|
int ret = 0;
|
|
|
|
while (n-- && (*s1 || *s2) &&
|
|
!(ret = D_ASCII_TOLOWER(*s1) - D_ASCII_TOLOWER(*s2))) {
|
|
s1++;
|
|
s2++;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*- dStr ----------------------------------------------------------------------
|
|
*/
|
|
|
|
/**
|
|
* Private allocator
|
|
*/
|
|
static void dStr_resize(Dstr *ds, int n_sz, int keep)
|
|
{
|
|
if (n_sz >= 0) {
|
|
if (keep && n_sz > ds->len) {
|
|
ds->str = (Dstr_char_t*) dRealloc (ds->str, n_sz*sizeof(Dstr_char_t));
|
|
ds->sz = n_sz;
|
|
} else {
|
|
dFree(ds->str);
|
|
ds->str = dNew(Dstr_char_t, n_sz);
|
|
ds->sz = n_sz;
|
|
ds->len = 0;
|
|
ds->str[0] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new string with a given size.
|
|
* Initialized to ""
|
|
*/
|
|
Dstr *dStr_sized_new (int sz)
|
|
{
|
|
Dstr *ds;
|
|
if (sz < 2)
|
|
sz = 2;
|
|
|
|
ds = dNew(Dstr, 1);
|
|
ds->str = NULL;
|
|
dStr_resize(ds, sz + 1, 0); /* (sz + 1) for the extra '\0' */
|
|
return ds;
|
|
}
|
|
|
|
/**
|
|
* Return memory if there's too much allocated.
|
|
*/
|
|
void dStr_fit (Dstr *ds)
|
|
{
|
|
dStr_resize(ds, ds->len + 1, 1);
|
|
}
|
|
|
|
/**
|
|
* Insert a C string, at a given position, into a Dstr (providing length).
|
|
* Note: It also works with embedded nil characters.
|
|
*/
|
|
void dStr_insert_l (Dstr *ds, int pos_0, const char *s, int l)
|
|
{
|
|
int n_sz;
|
|
|
|
if (ds && s && l && pos_0 >= 0 && pos_0 <= ds->len) {
|
|
for (n_sz = ds->sz; ds->len + l >= n_sz; n_sz *= 2);
|
|
if (n_sz > ds->sz) {
|
|
dStr_resize(ds, n_sz, (ds->len > 0) ? 1 : 0);
|
|
}
|
|
if (pos_0 < ds->len)
|
|
memmove(ds->str+pos_0+l, ds->str+pos_0, ds->len-pos_0);
|
|
memcpy(ds->str+pos_0, s, l);
|
|
ds->len += l;
|
|
ds->str[ds->len] = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Insert a C string, at a given position, into a Dstr.
|
|
*/
|
|
void dStr_insert (Dstr *ds, int pos_0, const char *s)
|
|
{
|
|
if (s)
|
|
dStr_insert_l(ds, pos_0, s, strlen(s));
|
|
}
|
|
|
|
/**
|
|
* Append a C string to a Dstr (providing length).
|
|
* Note: It also works with embedded nil characters.
|
|
*/
|
|
void dStr_append_l (Dstr *ds, const char *s, int l)
|
|
{
|
|
dStr_insert_l(ds, ds->len, s, l);
|
|
}
|
|
|
|
/**
|
|
* Append a C string to a Dstr.
|
|
*/
|
|
void dStr_append (Dstr *ds, const char *s)
|
|
{
|
|
dStr_append_l(ds, s, strlen(s));
|
|
}
|
|
|
|
/**
|
|
* Create a new string.
|
|
* Initialized to 's' or empty if 's == NULL'
|
|
*/
|
|
Dstr *dStr_new (const char *s)
|
|
{
|
|
Dstr *ds = dStr_sized_new(0);
|
|
if (s && *s)
|
|
dStr_append(ds, s);
|
|
return ds;
|
|
}
|
|
|
|
/**
|
|
* Free a dillo string.
|
|
* if 'all' free everything, else free the structure only.
|
|
*/
|
|
void dStr_free (Dstr *ds, int all)
|
|
{
|
|
if (ds) {
|
|
if (all)
|
|
dFree(ds->str);
|
|
dFree(ds);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Append one character.
|
|
*/
|
|
void dStr_append_c (Dstr *ds, int c)
|
|
{
|
|
char cs[2];
|
|
|
|
if (ds) {
|
|
if (ds->sz > ds->len + 1) {
|
|
ds->str[ds->len++] = (Dstr_char_t)c;
|
|
ds->str[ds->len] = 0;
|
|
} else {
|
|
cs[0] = (Dstr_char_t)c;
|
|
cs[1] = 0;
|
|
dStr_append_l (ds, cs, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Truncate a Dstr to be 'len' bytes long.
|
|
*/
|
|
void dStr_truncate (Dstr *ds, int len)
|
|
{
|
|
if (ds && len < ds->len) {
|
|
ds->str[len] = 0;
|
|
ds->len = len;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear a Dstr.
|
|
*/
|
|
void dStr_shred (Dstr *ds)
|
|
{
|
|
if (ds && ds->sz > 0)
|
|
memset(ds->str, '\0', ds->sz);
|
|
}
|
|
|
|
/**
|
|
* Erase a substring.
|
|
*/
|
|
void dStr_erase (Dstr *ds, int pos_0, int len)
|
|
{
|
|
if (ds && pos_0 >= 0 && len > 0 && pos_0 + len <= ds->len) {
|
|
memmove(ds->str + pos_0, ds->str + pos_0 + len, ds->len - pos_0 - len);
|
|
ds->len -= len;
|
|
ds->str[ds->len] = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* vsprintf-like function that appends.
|
|
* Used by: dStr_vsprintf(), dStr_sprintf() and dStr_sprintfa().
|
|
*/
|
|
void dStr_vsprintfa (Dstr *ds, const char *format, va_list argp)
|
|
{
|
|
int n, n_sz;
|
|
|
|
if (ds && format) {
|
|
va_list argp2; /* Needed in case of looping on non-32bit arch */
|
|
while (1) {
|
|
va_copy(argp2, argp);
|
|
n = vsnprintf(ds->str + ds->len, ds->sz - ds->len, format, argp2);
|
|
va_end(argp2);
|
|
#if defined(__sgi)
|
|
/* IRIX does not conform to C99; if the entire argument did not fit
|
|
* into the buffer, n = buffer space used (minus 1 for terminator)
|
|
*/
|
|
if (n > -1 && n + 1 < ds->sz - ds->len) {
|
|
ds->len += n; /* Success! */
|
|
break;
|
|
} else {
|
|
n_sz = ds->sz * 2;
|
|
}
|
|
#else
|
|
if (n > -1 && n < ds->sz - ds->len) {
|
|
ds->len += n; /* Success! */
|
|
break;
|
|
} else if (n > -1) { /* glibc >= 2.1 */
|
|
n_sz = ds->len + n + 1;
|
|
} else { /* old glibc */
|
|
n_sz = ds->sz * 2;
|
|
}
|
|
#endif
|
|
dStr_resize(ds, n_sz, (ds->len > 0) ? 1 : 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* vsprintf-like function.
|
|
*/
|
|
void dStr_vsprintf (Dstr *ds, const char *format, va_list argp)
|
|
{
|
|
if (ds) {
|
|
dStr_truncate(ds, 0);
|
|
dStr_vsprintfa(ds, format, argp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Printf-like function
|
|
*/
|
|
void dStr_sprintf (Dstr *ds, const char *format, ...)
|
|
{
|
|
va_list argp;
|
|
|
|
if (ds && format) {
|
|
va_start(argp, format);
|
|
dStr_vsprintf(ds, format, argp);
|
|
va_end(argp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Printf-like function that appends.
|
|
*/
|
|
void dStr_sprintfa (Dstr *ds, const char *format, ...)
|
|
{
|
|
va_list argp;
|
|
|
|
if (ds && format) {
|
|
va_start(argp, format);
|
|
dStr_vsprintfa(ds, format, argp);
|
|
va_end(argp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compare two dStrs.
|
|
*/
|
|
int dStr_cmp(Dstr *ds1, Dstr *ds2)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (ds1 && ds2)
|
|
ret = memcmp(ds1->str, ds2->str, MIN(ds1->len+1, ds2->len+1));
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Return a pointer to the first occurrence of needle in haystack.
|
|
*/
|
|
char *dStr_memmem(Dstr *haystack, Dstr *needle)
|
|
{
|
|
int i;
|
|
|
|
if (needle && haystack) {
|
|
if (needle->len == 0)
|
|
return haystack->str;
|
|
|
|
for (i = 0; i <= (haystack->len - needle->len); i++) {
|
|
if (haystack->str[i] == needle->str[0] &&
|
|
!memcmp(haystack->str + i, needle->str, needle->len))
|
|
return haystack->str + i;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Return a printable representation of the provided Dstr, limited to a length
|
|
* of roughly maxlen.
|
|
*
|
|
* This is NOT threadsafe.
|
|
*/
|
|
const char *dStr_printable(Dstr *in, int maxlen)
|
|
{
|
|
int i;
|
|
static const char *const HEX = "0123456789ABCDEF";
|
|
static Dstr *out = NULL;
|
|
|
|
if (in == NULL)
|
|
return NULL;
|
|
|
|
if (out)
|
|
dStr_truncate(out, 0);
|
|
else
|
|
out = dStr_sized_new(in->len);
|
|
|
|
for (i = 0; (i < in->len) && (out->len < maxlen); ++i) {
|
|
if (isprint(in->str[i]) || (in->str[i] == '\n')) {
|
|
dStr_append_c(out, in->str[i]);
|
|
} else {
|
|
dStr_append_l(out, "\\x", 2);
|
|
dStr_append_c(out, HEX[(in->str[i] >> 4) & 15]);
|
|
dStr_append_c(out, HEX[in->str[i] & 15]);
|
|
}
|
|
}
|
|
if (out->len >= maxlen)
|
|
dStr_append(out, "...");
|
|
return out->str;
|
|
}
|
|
|
|
extern "C++"
|
|
{
|
|
#include <Alepha/AutoRAII.h>
|
|
}
|
|
|
|
std::string
|
|
dStr_printable_std(const std::string &in, const int maxlen)
|
|
{
|
|
const auto wrapped= Alepha::AutoRAII
|
|
{
|
|
[&]{ return dStr_new( in.c_str() ); },
|
|
[]( auto d ) { dStr_free( d, true ); }
|
|
};
|
|
|
|
return dStr_printable( wrapped, maxlen );
|
|
}
|
|
|
|
/*
|
|
*- dList ---------------------------------------------------------------------
|
|
*/
|
|
|
|
/**
|
|
* Create a new empty list
|
|
*/
|
|
Dlist *dList_new(int size)
|
|
{
|
|
Dlist *l;
|
|
if (size <= 0)
|
|
return NULL;
|
|
|
|
l = dNew(Dlist, 1);
|
|
l->len = 0;
|
|
l->sz = size;
|
|
l->list = dNew(void*, l->sz);
|
|
return l;
|
|
}
|
|
|
|
/**
|
|
* Free a list (not its elements)
|
|
*/
|
|
void dList_free (Dlist *lp)
|
|
{
|
|
if (!lp)
|
|
return;
|
|
|
|
dFree(lp->list);
|
|
dFree(lp);
|
|
}
|
|
|
|
/**
|
|
* Insert an element at a given position [0 based]
|
|
*/
|
|
void dList_insert_pos (Dlist *lp, void *data, int pos0)
|
|
{
|
|
int i;
|
|
|
|
if (!lp || pos0 < 0 || pos0 > lp->len)
|
|
return;
|
|
|
|
if (lp->sz == lp->len) {
|
|
lp->sz *= 2;
|
|
lp->list = (void**) dRealloc (lp->list, lp->sz*sizeof(void*));
|
|
}
|
|
++lp->len;
|
|
|
|
for (i = lp->len - 1; i > pos0; --i)
|
|
lp->list[i] = lp->list[i - 1];
|
|
lp->list[pos0] = data;
|
|
}
|
|
|
|
/**
|
|
* Append a data item to the list
|
|
*/
|
|
void dList_append (Dlist *lp, void *data)
|
|
{
|
|
dList_insert_pos(lp, data, lp->len);
|
|
}
|
|
|
|
/**
|
|
* Prepend a data item to the list
|
|
*/
|
|
void dList_prepend (Dlist *lp, void *data)
|
|
{
|
|
dList_insert_pos(lp, data, 0);
|
|
}
|
|
|
|
/**
|
|
* For completing the ADT.
|
|
*/
|
|
int dList_length (Dlist *lp)
|
|
{
|
|
if (!lp)
|
|
return 0;
|
|
return lp->len;
|
|
}
|
|
|
|
/**
|
|
* Remove a data item without preserving order.
|
|
*/
|
|
void dList_remove_fast (Dlist *lp, const void *data)
|
|
{
|
|
int i;
|
|
|
|
if (!lp)
|
|
return;
|
|
|
|
for (i = 0; i < lp->len; ++i) {
|
|
if (lp->list[i] == data) {
|
|
lp->list[i] = lp->list[--lp->len];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Remove a data item preserving order.
|
|
*/
|
|
void dList_remove (Dlist *lp, const void *data)
|
|
{
|
|
int i, j;
|
|
|
|
if (!lp)
|
|
return;
|
|
|
|
for (i = 0; i < lp->len; ++i) {
|
|
if (lp->list[i] == data) {
|
|
--lp->len;
|
|
for (j = i; j < lp->len; ++j)
|
|
lp->list[j] = lp->list[j + 1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the nth data item,
|
|
* NULL when not found or 'n0' is out of range
|
|
*/
|
|
void *dList_nth_data (Dlist *lp, int n0)
|
|
{
|
|
if (!lp || n0 < 0 || n0 >= lp->len)
|
|
return NULL;
|
|
return lp->list[n0];
|
|
}
|
|
|
|
/**
|
|
* Return the found data item, or NULL if not present.
|
|
*/
|
|
void *dList_find (Dlist *lp, const void *data)
|
|
{
|
|
int i = dList_find_idx(lp, data);
|
|
return (i >= 0) ? lp->list[i] : NULL;
|
|
}
|
|
|
|
/**
|
|
* Search a data item.
|
|
* Return value: its index if found, -1 if not present.
|
|
* (this is useful for a list of integers, for finding number zero).
|
|
*/
|
|
int dList_find_idx (Dlist *lp, const void *data)
|
|
{
|
|
int i, ret = -1;
|
|
|
|
if (!lp)
|
|
return ret;
|
|
|
|
for (i = 0; i < lp->len; ++i) {
|
|
if (lp->list[i] == data) {
|
|
ret = i;
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Search a data item using a custom function.
|
|
* func() is given the list item and the user data as parameters.
|
|
* Return: data item when found, NULL otherwise.
|
|
*/
|
|
void *dList_find_custom (Dlist *lp, const void *data, dCompareFunc func)
|
|
{
|
|
int i;
|
|
void *ret = NULL;
|
|
|
|
if (!lp)
|
|
return ret;
|
|
|
|
for (i = 0; i < lp->len; ++i) {
|
|
if (func(lp->list[i], data) == 0) {
|
|
ret = lp->list[i];
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* QuickSort implementation.
|
|
* This allows for a simple compare function for all the ADT.
|
|
*/
|
|
static void QuickSort(void **left, void **right, dCompareFunc compare)
|
|
{
|
|
void **p = left, **q = right, **t = left;
|
|
|
|
while (1) {
|
|
while (p != t && compare(*p, *t) < 0)
|
|
++p;
|
|
while (q != t && compare(*q, *t) > 0)
|
|
--q;
|
|
if (p > q)
|
|
break;
|
|
if (p < q) {
|
|
void *tmp = *p;
|
|
*p = *q;
|
|
*q = tmp;
|
|
if (t == p)
|
|
t = q;
|
|
else if (t == q)
|
|
t = p;
|
|
}
|
|
if (++p > --q)
|
|
break;
|
|
}
|
|
|
|
if (left < q)
|
|
QuickSort(left, q, compare);
|
|
if (p < right)
|
|
QuickSort(p, right, compare);
|
|
}
|
|
|
|
/**
|
|
* Sort the list using a custom function
|
|
*/
|
|
void dList_sort (Dlist *lp, dCompareFunc func)
|
|
{
|
|
if (lp && lp->len > 1) {
|
|
QuickSort(lp->list, lp->list + lp->len - 1, func);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Insert an element into a sorted list.
|
|
* The comparison function receives two list elements.
|
|
*/
|
|
void dList_insert_sorted (Dlist *lp, void *data, dCompareFunc func)
|
|
{
|
|
int i, st, min, max;
|
|
|
|
if (lp) {
|
|
min = st = i = 0;
|
|
max = lp->len - 1;
|
|
while (min <= max) {
|
|
i = (min + max) / 2;
|
|
st = func(lp->list[i], data);
|
|
if (st < 0) {
|
|
min = i + 1;
|
|
} else if (st > 0) {
|
|
max = i - 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
dList_insert_pos(lp, data, (st >= 0) ? i : i+1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Search a sorted list.
|
|
* Return the found data item, or NULL if not present.
|
|
* func() is given the list item and the user data as parameters.
|
|
*/
|
|
void *dList_find_sorted (Dlist *lp, const void *data, dCompareFunc func)
|
|
{
|
|
int i, st, min, max;
|
|
void *ret = NULL;
|
|
|
|
if (lp && lp->len) {
|
|
min = 0;
|
|
max = lp->len - 1;
|
|
while (min <= max) {
|
|
i = (min + max) / 2;
|
|
st = func(lp->list[i], data);
|
|
if (st < 0) {
|
|
min = i + 1;
|
|
} else if (st > 0) {
|
|
max = i - 1;
|
|
} else {
|
|
ret = lp->list[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*- Parse function ------------------------------------------------------------
|
|
*/
|
|
|
|
/**
|
|
* Take a dillo rc line and return 'name' and 'value' pointers to it.
|
|
* Notes:
|
|
* - line is modified!
|
|
* - it skips blank lines and lines starting with '#'
|
|
*
|
|
* Return value: 1 on blank line or comment, 0 on successful value/pair,
|
|
* -1 otherwise.
|
|
*/
|
|
int dParser_parse_rc_line(char **line, char **name, char **value)
|
|
{
|
|
char *eq, *p;
|
|
int len, ret = -1;
|
|
|
|
dReturn_val_if_fail(*line, ret);
|
|
|
|
*name = NULL;
|
|
*value = NULL;
|
|
dStrstrip(*line);
|
|
if (!*line[0] || *line[0] == '#') {
|
|
/* blank line or comment */
|
|
ret = 1;
|
|
} else if ((eq = strchr(*line, '='))) {
|
|
/* get name */
|
|
for (p = *line; *p && *p != '=' && !dIsspace(*p); ++p);
|
|
*p = 0;
|
|
*name = *line;
|
|
|
|
/* skip whitespace */
|
|
if (p < eq)
|
|
for (++p; dIsspace(*p); ++p);
|
|
|
|
/* get value */
|
|
if (p == eq) {
|
|
for (++p; dIsspace(*p); ++p);
|
|
len = strlen(p);
|
|
if (len >= 2 && *p == '"' && p[len-1] == '"') {
|
|
p[len-1] = 0;
|
|
++p;
|
|
}
|
|
*value = p;
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*- Dlib messages -------------------------------------------------------------
|
|
*/
|
|
void dLib_show_messages(bool show)
|
|
{
|
|
dLib_show_msg = show;
|
|
}
|
|
|
|
/*
|
|
*- Misc utility functions ----------------------------------------------------
|
|
*/
|
|
|
|
/**
|
|
* Return the current working directory in a new string
|
|
*/
|
|
std::optional< std::string >
|
|
dGetcwd_string()
|
|
{
|
|
auto owned= Alepha::AutoRAII< char * >{ dGetcwd, dFree };
|
|
const char *const ptr= owned;
|
|
|
|
if( not ptr ) return std::nullopt;
|
|
return ptr;
|
|
}
|
|
|
|
CharPtrNoStringConversion dGetcwd (void)
|
|
{
|
|
size_t size = 128;
|
|
|
|
while (1) {
|
|
char *buffer = dNew(char, size);
|
|
if (getcwd (buffer, size) == buffer)
|
|
return { buffer };
|
|
dFree (buffer);
|
|
if (errno != ERANGE)
|
|
return {};
|
|
size *= 2;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the home directory in a static string (don't free)
|
|
*/
|
|
std::optional< std::string >
|
|
dGethomedir_string()
|
|
{
|
|
const char *const ptr= dGethomedir();
|
|
|
|
if( not ptr ) return std::nullopt;
|
|
return ptr;
|
|
}
|
|
|
|
CharPtrNoStringConversion dGethomedir (void)
|
|
{
|
|
static char *homedir = NULL;
|
|
|
|
if (!homedir) {
|
|
if (getenv("HOME")) {
|
|
homedir = dStrdup(getenv("HOME"));
|
|
|
|
} else if (getenv("HOMEDRIVE") && getenv("HOMEPATH")) {
|
|
homedir = dStrconcat(getenv("HOMEDRIVE"), getenv("HOMEPATH"), NULL);
|
|
} else {
|
|
DLIB_MSG("dGethomedir: $HOME not set, using '/'.\n");
|
|
homedir = dStrdup("/");
|
|
}
|
|
}
|
|
return { homedir };
|
|
}
|
|
|
|
/**
|
|
* Get a line from a FILE stream.
|
|
* Return value: read line on success, NULL on EOF.
|
|
*/
|
|
char *dGetline_unsafe (FILE *stream)
|
|
{
|
|
int ch;
|
|
Dstr *dstr;
|
|
char *line;
|
|
|
|
dReturn_val_if_fail (stream, 0);
|
|
|
|
dstr = dStr_sized_new(64);
|
|
while ((ch = fgetc(stream)) != EOF) {
|
|
dStr_append_c(dstr, ch);
|
|
if (ch == '\n')
|
|
break;
|
|
}
|
|
|
|
line = (dstr->len) ? dstr->str : NULL;
|
|
dStr_free(dstr, (line) ? 0 : 1);
|
|
return line;
|
|
}
|
|
|
|
std::optional< std::string >
|
|
dGetline( FILE *const stream )
|
|
{
|
|
std::string rv;
|
|
int ch;
|
|
|
|
if( stream == nullptr ) return std::nullopt;
|
|
|
|
while( ( ch= fgetc( stream ) ) != EOF )
|
|
{
|
|
rv+= ch;
|
|
if (ch == '\n')
|
|
break;
|
|
}
|
|
|
|
std::optional< std::string > line;
|
|
if(rv.size()) line= std::move( rv );
|
|
|
|
return line;
|
|
}
|
|
|
|
/**
|
|
* Close a FD handling EINTR.
|
|
*/
|
|
int dClose(int fd)
|
|
{
|
|
int st;
|
|
|
|
do
|
|
st = close(fd);
|
|
while (st == -1 && errno == EINTR);
|
|
return st;
|
|
}
|
|
|
|
/**
|
|
* Portable usleep() function.
|
|
*
|
|
* The usleep() function is deprecated in POSIX.1-2001 and removed in
|
|
* POSIX.1-2008, see usleep(3).
|
|
*/
|
|
int dUsleep(unsigned long usec)
|
|
{
|
|
struct timespec ts;
|
|
int res;
|
|
|
|
ts.tv_sec = usec / 1000000UL;
|
|
ts.tv_nsec = (usec % 1000000UL) * 1000UL;
|
|
|
|
do {
|
|
res = nanosleep(&ts, &ts);
|
|
} while (res && errno == EINTR);
|
|
|
|
return res;
|
|
}
|