Rewrite WUtils string.c in Rust #3
@@ -15,7 +15,7 @@ wraster = $(top_builddir)/wrlib/libwraster.la
|
||||
|
||||
LDADD= libWUtil.la libWINGs.la $(wraster) $(wutilrs) @INTLIBS@
|
||||
libWINGs_la_LIBADD = libWUtil.la $(wraster) $(wutilrs) @XLIBS@ @XFT_LIBS@ @FCLIBS@ @LIBM@ @PANGO_LIBS@
|
||||
libWUtil_la_LIBADD = @LIBBSD@ $(wutilrs)
|
||||
libWUtil_la_LIBADD = $(wutilrs)
|
||||
|
||||
EXTRA_DIST = BUGS make-rgb Examples Extras Tests
|
||||
|
||||
@@ -76,7 +76,6 @@ libWUtil_la_SOURCES = \
|
||||
misc.c \
|
||||
notification.c \
|
||||
proplist.c \
|
||||
string.c \
|
||||
tree.c \
|
||||
userdefaults.c \
|
||||
userdefaults.h \
|
||||
|
||||
@@ -271,14 +271,9 @@ char* wstrconcat(const char *str1, const char *str2);
|
||||
* so always assign the returned address to avoid dangling pointers. */
|
||||
char* wstrappend(char *dst, const char *src);
|
||||
|
||||
size_t wstrlcpy(char *, const char *, size_t);
|
||||
size_t wstrlcat(char *, const char *, size_t);
|
||||
|
||||
|
||||
void wtokensplit(char *command, char ***argv, int *argc);
|
||||
|
||||
char* wtokennext(char *word, char **next);
|
||||
|
||||
char* wtokenjoin(char **list, int count);
|
||||
|
||||
void wtokenfree(char **tokens, int count);
|
||||
|
||||
@@ -498,7 +498,7 @@ static void registerDescriptionList(WMScreen * scr, WMView * view, WMArray * ope
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
text = WMGetDragOperationItemText(WMGetFromArray(operationArray, i));
|
||||
wstrlcpy(textListItem, text, size);
|
||||
strlcpy(textListItem, text, size);
|
||||
|
||||
/* to next text offset */
|
||||
textListItem = &(textListItem[strlen(textListItem) + 1]);
|
||||
|
||||
@@ -76,8 +76,8 @@ char *wfindfileinarray(WMPropList *array, const char *file)
|
||||
path = wmalloc(len + flen + 2);
|
||||
path = memcpy(path, p, len);
|
||||
path[len] = 0;
|
||||
if (wstrlcat(path, "/", len + flen + 2) >= len + flen + 2 ||
|
||||
wstrlcat(path, file, len + flen + 2) >= len + flen + 2) {
|
||||
if (strlcat(path, "/", len + flen + 2) >= len + flen + 2 ||
|
||||
strlcat(path, file, len + flen + 2) >= len + flen + 2) {
|
||||
wfree(path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -652,7 +652,7 @@ static void mpm_get_hostname(WParserMacro *this, WMenuParser parser)
|
||||
return;
|
||||
}
|
||||
}
|
||||
wstrlcpy((char *) this->value, h, sizeof(this->value) );
|
||||
strlcpy((char *) this->value, h, sizeof(this->value) );
|
||||
}
|
||||
|
||||
/* Name of the current user */
|
||||
@@ -677,7 +677,7 @@ static void mpm_get_user_name(WParserMacro *this, WMenuParser parser)
|
||||
user = pw_user->pw_name;
|
||||
if (user == NULL) goto error_no_username;
|
||||
}
|
||||
wstrlcpy((char *) this->value, user, sizeof(this->value) );
|
||||
strlcpy((char *) this->value, user, sizeof(this->value) );
|
||||
}
|
||||
|
||||
/* Number id of the user under which we are running */
|
||||
|
||||
425
WINGs/string.c
425
WINGs/string.c
@@ -1,425 +0,0 @@
|
||||
/*
|
||||
* Until FreeBSD gets their act together;
|
||||
* http://www.mail-archive.com/freebsd-hackers@freebsd.org/msg69469.html
|
||||
*/
|
||||
#if defined( FREEBSD )
|
||||
# undef _XOPEN_SOURCE
|
||||
#endif
|
||||
|
||||
#include "wconfig.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#ifdef HAVE_BSD_STRING_H
|
||||
#include <bsd/string.h>
|
||||
#endif
|
||||
|
||||
#include "WUtil.h"
|
||||
|
||||
#define PRC_ALPHA 0
|
||||
#define PRC_BLANK 1
|
||||
#define PRC_ESCAPE 2
|
||||
#define PRC_DQUOTE 3
|
||||
#define PRC_EOS 4
|
||||
#define PRC_SQUOTE 5
|
||||
|
||||
typedef struct {
|
||||
short nstate;
|
||||
short output;
|
||||
} DFA;
|
||||
|
||||
static DFA mtable[9][6] = {
|
||||
{{3, 1}, {0, 0}, {4, 0}, {1, 0}, {8, 0}, {6, 0}},
|
||||
{{1, 1}, {1, 1}, {2, 0}, {3, 0}, {5, 0}, {1, 1}},
|
||||
{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {5, 0}, {1, 1}},
|
||||
{{3, 1}, {5, 0}, {4, 0}, {1, 0}, {5, 0}, {6, 0}},
|
||||
{{3, 1}, {3, 1}, {3, 1}, {3, 1}, {5, 0}, {3, 1}},
|
||||
{{-1, -1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, /* final state */
|
||||
{{6, 1}, {6, 1}, {7, 0}, {6, 1}, {5, 0}, {3, 0}},
|
||||
{{6, 1}, {6, 1}, {6, 1}, {6, 1}, {5, 0}, {6, 1}},
|
||||
{{-1, -1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, /* final state */
|
||||
};
|
||||
|
||||
char *wtokennext(char *word, char **next)
|
||||
{
|
||||
char *ptr;
|
||||
char *ret, *t;
|
||||
int state, ctype;
|
||||
|
||||
t = ret = wmalloc(strlen(word) + 1);
|
||||
ptr = word;
|
||||
|
||||
state = 0;
|
||||
while (1) {
|
||||
if (*ptr == 0)
|
||||
ctype = PRC_EOS;
|
||||
else if (*ptr == '\\')
|
||||
ctype = PRC_ESCAPE;
|
||||
else if (*ptr == '"')
|
||||
ctype = PRC_DQUOTE;
|
||||
else if (*ptr == '\'')
|
||||
ctype = PRC_SQUOTE;
|
||||
else if (*ptr == ' ' || *ptr == '\t')
|
||||
ctype = PRC_BLANK;
|
||||
else
|
||||
ctype = PRC_ALPHA;
|
||||
|
||||
if (mtable[state][ctype].output) {
|
||||
*t = *ptr;
|
||||
t++;
|
||||
*t = 0;
|
||||
}
|
||||
state = mtable[state][ctype].nstate;
|
||||
ptr++;
|
||||
if (mtable[state][0].output < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*ret == 0) {
|
||||
wfree(ret);
|
||||
ret = NULL;
|
||||
}
|
||||
|
||||
if (ctype == PRC_EOS)
|
||||
*next = NULL;
|
||||
else
|
||||
*next = ptr;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* separate a string in tokens, taking " and ' into account */
|
||||
void wtokensplit(char *command, char ***argv, int *argc)
|
||||
{
|
||||
char *token, *line;
|
||||
int count;
|
||||
|
||||
count = 0;
|
||||
line = command;
|
||||
do {
|
||||
token = wtokennext(line, &line);
|
||||
if (token) {
|
||||
if (count == 0)
|
||||
*argv = wmalloc(sizeof(**argv));
|
||||
else
|
||||
*argv = wrealloc(*argv, (count + 1) * sizeof(**argv));
|
||||
(*argv)[count++] = token;
|
||||
}
|
||||
} while (token != NULL && line != NULL);
|
||||
|
||||
*argc = count;
|
||||
}
|
||||
|
||||
char *wtokenjoin(char **list, int count)
|
||||
{
|
||||
int i, j;
|
||||
char *flat_string, *wspace;
|
||||
|
||||
j = 0;
|
||||
for (i = 0; i < count; i++) {
|
||||
if (list[i] != NULL && list[i][0] != 0) {
|
||||
j += strlen(list[i]);
|
||||
if (strpbrk(list[i], " \t"))
|
||||
j += 2;
|
||||
}
|
||||
}
|
||||
|
||||
flat_string = wmalloc(j + count + 1);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (list[i] != NULL && list[i][0] != 0) {
|
||||
if (i > 0 &&
|
||||
wstrlcat(flat_string, " ", j + count + 1) >= j + count + 1)
|
||||
goto error;
|
||||
|
||||
wspace = strpbrk(list[i], " \t");
|
||||
|
||||
if (wspace &&
|
||||
wstrlcat(flat_string, "\"", j + count + 1) >= j + count + 1)
|
||||
goto error;
|
||||
|
||||
if (wstrlcat(flat_string, list[i], j + count + 1) >= j + count + 1)
|
||||
goto error;
|
||||
|
||||
if (wspace &&
|
||||
wstrlcat(flat_string, "\"", j + count + 1) >= j + count + 1)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return flat_string;
|
||||
|
||||
error:
|
||||
wfree(flat_string);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void wtokenfree(char **tokens, int count)
|
||||
{
|
||||
while (count--)
|
||||
wfree(tokens[count]);
|
||||
wfree(tokens);
|
||||
}
|
||||
|
||||
char *wtrimspace(const char *s)
|
||||
{
|
||||
const char *t;
|
||||
|
||||
if (s == NULL)
|
||||
return NULL;
|
||||
|
||||
while (isspace(*s) && *s)
|
||||
s++;
|
||||
t = s + strlen(s) - 1;
|
||||
while (t > s && isspace(*t))
|
||||
t--;
|
||||
|
||||
return wstrndup(s, t - s + 1);
|
||||
}
|
||||
|
||||
char *wstrdup(const char *str)
|
||||
{
|
||||
assert(str != NULL);
|
||||
|
||||
return strcpy(wmalloc(strlen(str) + 1), str);
|
||||
}
|
||||
|
||||
char *wstrndup(const char *str, size_t len)
|
||||
{
|
||||
char *copy;
|
||||
|
||||
assert(str != NULL);
|
||||
|
||||
len = WMIN(len, strlen(str));
|
||||
copy = strncpy(wmalloc(len + 1), str, len);
|
||||
copy[len] = 0;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
char *wstrconcat(const char *str1, const char *str2)
|
||||
{
|
||||
char *str;
|
||||
size_t slen, slen1;
|
||||
|
||||
if (!str1 && str2)
|
||||
return wstrdup(str2);
|
||||
else if (str1 && !str2)
|
||||
return wstrdup(str1);
|
||||
else if (!str1 && !str2)
|
||||
return NULL;
|
||||
|
||||
slen1 = strlen(str1);
|
||||
slen = slen1 + strlen(str2) + 1;
|
||||
str = wmalloc(slen);
|
||||
strcpy(str, str1);
|
||||
strcpy(str + slen1, str2);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
char *wstrappend(char *dst, const char *src)
|
||||
{
|
||||
size_t slen;
|
||||
|
||||
if (!src || *src == 0)
|
||||
return dst;
|
||||
else if (!dst)
|
||||
return wstrdup(src);
|
||||
|
||||
slen = strlen(dst) + strlen(src) + 1;
|
||||
dst = wrealloc(dst, slen);
|
||||
strcat(dst, src);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_STRLCAT
|
||||
size_t
|
||||
wstrlcat(char *dst, const char *src, size_t siz)
|
||||
{
|
||||
return strlcat(dst, src, siz);
|
||||
}
|
||||
#else
|
||||
/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Appends src to string dst of size siz (unlike strncat, siz is the
|
||||
* full size of dst, not space left). At most siz-1 characters
|
||||
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
|
||||
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
|
||||
* If retval >= siz, truncation occurred.
|
||||
*/
|
||||
size_t
|
||||
wstrlcat(char *dst, const char *src, size_t siz)
|
||||
{
|
||||
char *d = dst;
|
||||
const char *s = src;
|
||||
size_t n = siz;
|
||||
size_t dlen;
|
||||
|
||||
/* Find the end of dst and adjust bytes left but don't go past end */
|
||||
while (n-- != 0 && *d != '\0')
|
||||
d++;
|
||||
dlen = d - dst;
|
||||
n = siz - dlen;
|
||||
|
||||
if (n == 0)
|
||||
return(dlen + strlen(s));
|
||||
while (*s != '\0') {
|
||||
if (n != 1) {
|
||||
*d++ = *s;
|
||||
n--;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
*d = '\0';
|
||||
|
||||
return(dlen + (s - src)); /* count does not include NUL */
|
||||
}
|
||||
#endif /* HAVE_STRLCAT */
|
||||
|
||||
#ifdef HAVE_STRLCPY
|
||||
size_t
|
||||
wstrlcpy(char *dst, const char *src, size_t siz)
|
||||
{
|
||||
return strlcpy(dst, src, siz);
|
||||
}
|
||||
#else
|
||||
|
||||
/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copy src to string dst of size siz. At most siz-1 characters
|
||||
* will be copied. Always NUL terminates (unless siz == 0).
|
||||
* Returns strlen(src); if retval >= siz, truncation occurred.
|
||||
*/
|
||||
size_t
|
||||
wstrlcpy(char *dst, const char *src, size_t siz)
|
||||
{
|
||||
char *d = dst;
|
||||
const char *s = src;
|
||||
size_t n = siz;
|
||||
|
||||
/* Copy as many bytes as will fit */
|
||||
if (n != 0) {
|
||||
while (--n != 0) {
|
||||
if ((*d++ = *s++) == '\0')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not enough room in dst, add NUL and traverse rest of src */
|
||||
if (n == 0) {
|
||||
if (siz != 0)
|
||||
*d = '\0'; /* NUL-terminate dst */
|
||||
while (*s++)
|
||||
;
|
||||
}
|
||||
|
||||
return(s - src - 1); /* count does not include NUL */
|
||||
}
|
||||
#endif /* HAVE_STRLCPY */
|
||||
|
||||
/* transform `s' so that the result is safe to pass to the shell as an argument.
|
||||
* returns a newly allocated string.
|
||||
* with very heavy inspirations from NetBSD's shquote(3).
|
||||
*/
|
||||
char *wshellquote(const char *s)
|
||||
{
|
||||
char *p, *r, *last, *ret;
|
||||
size_t slen;
|
||||
int needs_quoting;
|
||||
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
needs_quoting = !*s; /* the empty string does need quoting */
|
||||
|
||||
/* do not quote if consists only of the following characters */
|
||||
for (p = (char *)s; *p && !needs_quoting; p++) {
|
||||
needs_quoting = !(isalnum(*p) || (*p == '+') || (*p == '/') ||
|
||||
(*p == '.') || (*p == ',') || (*p == '-'));
|
||||
}
|
||||
|
||||
if (!needs_quoting)
|
||||
return wstrdup(s);
|
||||
|
||||
for (slen = 0, p = (char *)s; *p; p++) /* count space needed (worst case) */
|
||||
slen += *p == '\'' ? 4 : 1; /* every single ' becomes ''\' */
|
||||
|
||||
slen += 2 /* leading + trailing "'" */ + 1 /* NULL */;
|
||||
|
||||
ret = r = wmalloc(slen);
|
||||
p = (char *)s;
|
||||
last = p;
|
||||
|
||||
if (*p != '\'') /* if string doesn't already begin with "'" */
|
||||
*r++ ='\''; /* start putting it in quotes */
|
||||
|
||||
while (*p) {
|
||||
last = p;
|
||||
if (*p == '\'') { /* turn each ' into ''\' */
|
||||
if (p != s) /* except if it's the first ', in which case */
|
||||
*r++ = '\''; /* only escape it */
|
||||
*r++ = '\\';
|
||||
*r++ = '\'';
|
||||
while (*++p && *p == '\'') { /* keep turning each consecutive 's into \' */
|
||||
*r++ = '\\';
|
||||
*r++ = '\'';
|
||||
}
|
||||
if (*p) /* if more input follows, terminate */
|
||||
*r++ = '\''; /* what we have so far */
|
||||
} else {
|
||||
*r++ = *p++;
|
||||
}
|
||||
}
|
||||
|
||||
if (*last != '\'') /* if the last one isn't already a ' */
|
||||
*r++ = '\''; /* terminate the whole shebang */
|
||||
|
||||
*r = '\0';
|
||||
|
||||
return ret; /* technically, we lose (but not leak) a couple of */
|
||||
/* bytes (twice the number of consecutive 's in the */
|
||||
/* input or so), but since these are relatively rare */
|
||||
/* and short-lived strings, not sure if a trip to */
|
||||
/* wstrdup+wfree worths the gain. */
|
||||
}
|
||||
@@ -101,21 +101,21 @@ static char *checkFile(const char *path, const char *folder, const char *ext, co
|
||||
slen = strlen(path) + strlen(resource) + 1 + extralen;
|
||||
ret = wmalloc(slen);
|
||||
|
||||
if (wstrlcpy(ret, path, slen) >= slen)
|
||||
if (strlcpy(ret, path, slen) >= slen)
|
||||
goto error;
|
||||
|
||||
if (folder &&
|
||||
(wstrlcat(ret, "/", slen) >= slen ||
|
||||
wstrlcat(ret, folder, slen) >= slen))
|
||||
(strlcat(ret, "/", slen) >= slen ||
|
||||
strlcat(ret, folder, slen) >= slen))
|
||||
goto error;
|
||||
|
||||
if (ext &&
|
||||
(wstrlcat(ret, "/", slen) >= slen ||
|
||||
wstrlcat(ret, ext, slen) >= slen))
|
||||
(strlcat(ret, "/", slen) >= slen ||
|
||||
strlcat(ret, ext, slen) >= slen))
|
||||
goto error;
|
||||
|
||||
if (wstrlcat(ret, "/", slen) >= slen ||
|
||||
wstrlcat(ret, resource, slen) >= slen)
|
||||
if (strlcat(ret, "/", slen) >= slen ||
|
||||
strlcat(ret, resource, slen) >= slen)
|
||||
goto error;
|
||||
|
||||
if (access(ret, F_OK) != 0)
|
||||
|
||||
@@ -720,14 +720,14 @@ char *WMGetBrowserPathToColumn(WMBrowser * bPtr, int column)
|
||||
path = wmalloc(slen);
|
||||
/* ignore first `/' */
|
||||
for (i = 0; i <= column; i++) {
|
||||
if (wstrlcat(path, bPtr->pathSeparator, slen) >= slen)
|
||||
if (strlcat(path, bPtr->pathSeparator, slen) >= slen)
|
||||
goto error;
|
||||
|
||||
item = WMGetListSelectedItem(bPtr->columns[i]);
|
||||
if (!item)
|
||||
break;
|
||||
|
||||
if (wstrlcat(path, item->text, slen) >= slen)
|
||||
if (strlcat(path, item->text, slen) >= slen)
|
||||
goto error;
|
||||
|
||||
}
|
||||
@@ -782,7 +782,7 @@ WMArray *WMGetBrowserPaths(WMBrowser * bPtr)
|
||||
path = wmalloc(slen);
|
||||
/* ignore first `/' */
|
||||
for (i = 0; i <= column; i++) {
|
||||
wstrlcat(path, bPtr->pathSeparator, slen);
|
||||
strlcat(path, bPtr->pathSeparator, slen);
|
||||
if (i == column) {
|
||||
item = lastItem;
|
||||
} else {
|
||||
@@ -790,7 +790,7 @@ WMArray *WMGetBrowserPaths(WMBrowser * bPtr)
|
||||
}
|
||||
if (!item)
|
||||
break;
|
||||
wstrlcat(path, item->text, slen);
|
||||
strlcat(path, item->text, slen);
|
||||
}
|
||||
WMAddToArray(paths, path);
|
||||
}
|
||||
@@ -1130,25 +1130,25 @@ static char *createTruncatedString(WMFont * font, const char *text, int *textLen
|
||||
if (width >= 3 * dLen) {
|
||||
int tmpTextLen = *textLen;
|
||||
|
||||
if (wstrlcpy(textBuf, text, slen) >= slen)
|
||||
if (strlcpy(textBuf, text, slen) >= slen)
|
||||
goto error;
|
||||
|
||||
while (tmpTextLen && (WMWidthOfString(font, textBuf, tmpTextLen) + 3 * dLen > width))
|
||||
tmpTextLen--;
|
||||
|
||||
if (wstrlcpy(textBuf + tmpTextLen, "...", slen) >= slen)
|
||||
if (strlcpy(textBuf + tmpTextLen, "...", slen) >= slen)
|
||||
goto error;
|
||||
|
||||
*textLen = tmpTextLen + 3;
|
||||
|
||||
} else if (width >= 2 * dLen) {
|
||||
if (wstrlcpy(textBuf, "..", slen) >= slen)
|
||||
if (strlcpy(textBuf, "..", slen) >= slen)
|
||||
goto error;
|
||||
|
||||
*textLen = 2;
|
||||
|
||||
} else if (width >= dLen) {
|
||||
if (wstrlcpy(textBuf, ".", slen) >= slen)
|
||||
if (strlcpy(textBuf, ".", slen) >= slen)
|
||||
goto error;
|
||||
|
||||
*textLen = 1;
|
||||
|
||||
@@ -513,12 +513,12 @@ static void listDirectoryOnColumn(WMFilePanel * panel, int column, const char *p
|
||||
if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
|
||||
continue;
|
||||
|
||||
if (wstrlcpy(pbuf, path, sizeof(pbuf)) >= sizeof(pbuf))
|
||||
if (strlcpy(pbuf, path, sizeof(pbuf)) >= sizeof(pbuf))
|
||||
goto out;
|
||||
if (strcmp(path, "/") != 0 &&
|
||||
wstrlcat(pbuf, "/", sizeof(pbuf)) >= sizeof(pbuf))
|
||||
strlcat(pbuf, "/", sizeof(pbuf)) >= sizeof(pbuf))
|
||||
goto out;
|
||||
if (wstrlcat(pbuf, dentry->d_name, sizeof(pbuf)) >= sizeof(pbuf))
|
||||
if (strlcat(pbuf, dentry->d_name, sizeof(pbuf)) >= sizeof(pbuf))
|
||||
goto out;
|
||||
|
||||
if (stat(pbuf, &stat_buf) != 0) {
|
||||
@@ -626,11 +626,11 @@ static void createDir(WMWidget *widget, void *p_panel)
|
||||
file = wmalloc(slen);
|
||||
|
||||
if (directory &&
|
||||
(wstrlcat(file, directory, slen) >= slen ||
|
||||
wstrlcat(file, "/", slen) >= slen))
|
||||
(strlcat(file, directory, slen) >= slen ||
|
||||
strlcat(file, "/", slen) >= slen))
|
||||
goto out;
|
||||
|
||||
if (wstrlcat(file, dirName, slen) >= slen)
|
||||
if (strlcat(file, dirName, slen) >= slen)
|
||||
goto out;
|
||||
|
||||
if (mkdir(file, 00777) != 0) {
|
||||
|
||||
@@ -558,7 +558,7 @@ static void listFamilies(WMScreen * scr, WMFontPanel * panel)
|
||||
WMListItem *item;
|
||||
|
||||
WM_ITERATE_ARRAY(array, fam, i) {
|
||||
wstrlcpy(buffer, fam->name, sizeof(buffer));
|
||||
strlcpy(buffer, fam->name, sizeof(buffer));
|
||||
item = WMAddListItem(panel->famLs, buffer);
|
||||
|
||||
item->clientData = fam;
|
||||
@@ -640,7 +640,7 @@ static void familyClick(WMWidget * w, void *data)
|
||||
int top = 0;
|
||||
WMListItem *fitem;
|
||||
|
||||
wstrlcpy(buffer, face->typeface, sizeof(buffer));
|
||||
strlcpy(buffer, face->typeface, sizeof(buffer));
|
||||
if (strcasecmp(face->typeface, "Roman") == 0)
|
||||
top = 1;
|
||||
if (strcasecmp(face->typeface, "Regular") == 0)
|
||||
@@ -773,7 +773,7 @@ static void setFontPanelFontName(FontPanel * panel, const char *family, const ch
|
||||
int top = 0;
|
||||
WMListItem *fitem;
|
||||
|
||||
wstrlcpy(buffer, face->typeface, sizeof(buffer));
|
||||
strlcpy(buffer, face->typeface, sizeof(buffer));
|
||||
if (strcasecmp(face->typeface, "Roman") == 0)
|
||||
top = 1;
|
||||
if (top)
|
||||
|
||||
@@ -404,7 +404,7 @@ void WMInsertTextFieldText(WMTextField * tPtr, const char *text, int position)
|
||||
|
||||
if (position < 0 || position >= tPtr->textLen) {
|
||||
/* append the text at the end */
|
||||
wstrlcat(tPtr->text, text, tPtr->bufferSize);
|
||||
strlcat(tPtr->text, text, tPtr->bufferSize);
|
||||
tPtr->textLen += len;
|
||||
tPtr->cursorPosition += len;
|
||||
incrToFit(tPtr);
|
||||
@@ -473,7 +473,7 @@ void WMSetTextFieldText(WMTextField * tPtr, const char *text)
|
||||
tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
|
||||
tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
|
||||
}
|
||||
wstrlcpy(tPtr->text, text, tPtr->bufferSize);
|
||||
strlcpy(tPtr->text, text, tPtr->bufferSize);
|
||||
}
|
||||
|
||||
tPtr->cursorPosition = tPtr->selection.position = tPtr->textLen;
|
||||
|
||||
@@ -365,7 +365,7 @@ char *capture_shortcut(Display *dpy, Bool *capturing, Bool convert_case)
|
||||
if ((numlock_mask != Mod5Mask) && (ev.xkey.state & Mod5Mask))
|
||||
strcat(buffer, "Mod5+");
|
||||
|
||||
wstrlcat(buffer, key, sizeof(buffer));
|
||||
strlcat(buffer, key, sizeof(buffer));
|
||||
|
||||
return wstrdup(buffer);
|
||||
}
|
||||
|
||||
33
configure.ac
33
configure.ac
@@ -391,39 +391,6 @@ dnl the flag 'O_NOFOLLOW' for 'open' is used in WINGs
|
||||
WM_FUNC_OPEN_NOFOLLOW
|
||||
|
||||
|
||||
dnl Check for strlcat/strlcpy
|
||||
dnl =========================
|
||||
m4_divert_push([INIT_PREPARE])dnl
|
||||
AC_ARG_WITH([libbsd],
|
||||
[AS_HELP_STRING([--without-libbsd], [do not use libbsd for strlcat and strlcpy [default=check]])],
|
||||
[AS_IF([test "x$with_libbsd" != "xno"],
|
||||
[with_libbsd=bsd],
|
||||
[with_libbsd=]
|
||||
)],
|
||||
[with_libbsd=bsd])
|
||||
m4_divert_pop([INIT_PREPARE])dnl
|
||||
|
||||
tmp_libs=$LIBS
|
||||
AC_SEARCH_LIBS([strlcat],[$with_libbsd],
|
||||
[AC_DEFINE(HAVE_STRLCAT, 1, [Define if strlcat is available])],
|
||||
[],
|
||||
[]
|
||||
)
|
||||
AC_SEARCH_LIBS([strlcpy],[$with_libbsd],
|
||||
[AC_DEFINE(HAVE_STRLCAT, 1, [Define if strlcpy is available])],
|
||||
[],
|
||||
[]
|
||||
)
|
||||
LIBS=$tmp_libs
|
||||
|
||||
LIBBSD=
|
||||
AS_IF([test "x$ac_cv_search_strlcat" = "x-lbsd" -o "x$ac_cv_search_strlcpy" = "x-lbsd"],
|
||||
[LIBBSD=-lbsd
|
||||
AC_CHECK_HEADERS([bsd/string.h])]
|
||||
)
|
||||
AC_SUBST(LIBBSD)
|
||||
|
||||
|
||||
dnl Check for OpenBSD kernel memory interface - kvm(3)
|
||||
dnl ==================================================
|
||||
AS_IF([test "x$WM_OSDEP" = "xbsd"],
|
||||
|
||||
@@ -94,7 +94,7 @@ static WMenu *parseMenuCommand(WScreen * scr, Window win, char **slist, int coun
|
||||
wwarning(_("appmenu: bad menu entry \"%s\" in window %lx"), slist[*index], win);
|
||||
return NULL;
|
||||
}
|
||||
if (wstrlcpy(title, &slist[*index][pos], sizeof(title)) >= sizeof(title)) {
|
||||
if (strlcpy(title, &slist[*index][pos], sizeof(title)) >= sizeof(title)) {
|
||||
wwarning(_("appmenu: menu command size exceeded in window %lx"), win);
|
||||
return NULL;
|
||||
}
|
||||
@@ -127,7 +127,7 @@ static WMenu *parseMenuCommand(WScreen * scr, Window win, char **slist, int coun
|
||||
slist[*index], win);
|
||||
return NULL;
|
||||
}
|
||||
wstrlcpy(title, &slist[*index][pos], sizeof(title));
|
||||
strlcpy(title, &slist[*index][pos], sizeof(title));
|
||||
rtext[0] = 0;
|
||||
} else {
|
||||
if (sscanf(slist[*index], "%i %i %i %i %s %n",
|
||||
@@ -137,7 +137,7 @@ static WMenu *parseMenuCommand(WScreen * scr, Window win, char **slist, int coun
|
||||
slist[*index], win);
|
||||
return NULL;
|
||||
}
|
||||
wstrlcpy(title, &slist[*index][pos], sizeof(title));
|
||||
strlcpy(title, &slist[*index][pos], sizeof(title));
|
||||
}
|
||||
data = wmalloc(sizeof(WAppMenuData));
|
||||
if (data == NULL) {
|
||||
@@ -174,7 +174,7 @@ static WMenu *parseMenuCommand(WScreen * scr, Window win, char **slist, int coun
|
||||
|
||||
return NULL;
|
||||
}
|
||||
wstrlcpy(title, &slist[*index][pos], sizeof(title));
|
||||
strlcpy(title, &slist[*index][pos], sizeof(title));
|
||||
*index += 1;
|
||||
|
||||
submenu = parseMenuCommand(scr, win, slist, count, index);
|
||||
|
||||
@@ -2205,7 +2205,7 @@ static int getKeybind(WScreen * scr, WDefaultEntry * entry, WMPropList * value,
|
||||
return True;
|
||||
}
|
||||
|
||||
wstrlcpy(buf, val, MAX_SHORTCUT_LENGTH);
|
||||
strlcpy(buf, val, MAX_SHORTCUT_LENGTH);
|
||||
|
||||
b = (char *)buf;
|
||||
|
||||
|
||||
@@ -605,9 +605,9 @@ static void listPixmaps(WScreen *scr, WMList *lPtr, const char *path)
|
||||
if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
|
||||
continue;
|
||||
|
||||
if (wstrlcpy(pbuf, apath, sizeof(pbuf)) >= sizeof(pbuf) ||
|
||||
wstrlcat(pbuf, "/", sizeof(pbuf)) >= sizeof(pbuf) ||
|
||||
wstrlcat(pbuf, dentry->d_name, sizeof(pbuf)) >= sizeof(pbuf)) {
|
||||
if (strlcpy(pbuf, apath, sizeof(pbuf)) >= sizeof(pbuf) ||
|
||||
strlcat(pbuf, "/", sizeof(pbuf)) >= sizeof(pbuf) ||
|
||||
strlcat(pbuf, dentry->d_name, sizeof(pbuf)) >= sizeof(pbuf)) {
|
||||
wwarning(_("full path for file \"%s\" in \"%s\" is longer than %d bytes, skipped"),
|
||||
dentry->d_name, path, (int) (sizeof(pbuf) - 1) );
|
||||
continue;
|
||||
|
||||
@@ -406,7 +406,7 @@ static Bool addShortcut(const char *file, const char *shortcutDefinition, WMenu
|
||||
|
||||
ptr = wmalloc(sizeof(Shortcut));
|
||||
|
||||
wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
|
||||
strlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
|
||||
b = (char *)buf;
|
||||
|
||||
/* get modifiers */
|
||||
|
||||
@@ -797,7 +797,7 @@ void wWorkspaceMenuUpdate(WScreen * scr, WMenu * menu)
|
||||
i = scr->workspace_count - (menu->entry_no - MC_WORKSPACE1);
|
||||
ws = menu->entry_no - MC_WORKSPACE1;
|
||||
while (i > 0) {
|
||||
wstrlcpy(title, scr->workspaces[ws]->name, MAX_WORKSPACENAME_WIDTH);
|
||||
strlcpy(title, scr->workspaces[ws]->name, MAX_WORKSPACENAME_WIDTH);
|
||||
|
||||
entry = wMenuAddCallback(menu, title, switchWSCommand, (void *)ws);
|
||||
entry->flags.indicator = 1;
|
||||
|
||||
@@ -9,7 +9,8 @@ RUST_SOURCES = \
|
||||
src/hash_table.rs \
|
||||
src/lib.rs \
|
||||
src/memory.rs \
|
||||
src/prop_list.rs
|
||||
src/prop_list.rs \
|
||||
src/string.rs
|
||||
|
||||
RUST_EXTRA = \
|
||||
Cargo.lock \
|
||||
|
||||
@@ -5,3 +5,4 @@ pub mod find_file;
|
||||
pub mod hash_table;
|
||||
pub mod memory;
|
||||
pub mod prop_list;
|
||||
pub mod string;
|
||||
|
||||
439
wutil-rs/src/string.rs
Normal file
439
wutil-rs/src/string.rs
Normal file
@@ -0,0 +1,439 @@
|
||||
//! String manipulation utilities.
|
||||
//!
|
||||
//! ## Rust rewrite notes
|
||||
//!
|
||||
//! These are more or less bug-for-bug reimplementations of the original WUtils
|
||||
//! functions. Avoid using these functions in new code.
|
||||
|
||||
use std::{
|
||||
ffi::{c_char, c_int, CStr},
|
||||
iter, mem, ptr, slice,
|
||||
};
|
||||
|
||||
use crate::memory::{alloc_bytes, alloc_string, ffi::wrealloc, free_bytes};
|
||||
|
||||
/// Returns a `wmalloc`-managed C-style string that duplicates `s`, which cannot
|
||||
/// be null.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn wstrdup(s: *const c_char) -> *mut c_char {
|
||||
assert!(!s.is_null());
|
||||
alloc_string(unsafe { CStr::from_ptr(s) })
|
||||
}
|
||||
|
||||
/// Returns a `wmalloc`-managed C-style string of the first `len` bytes of `s`,
|
||||
/// which cannot be null.
|
||||
///
|
||||
/// If `len` exceeds the length of `s`, uses the lesser value.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn wstrndup(s: *const c_char, len: usize) -> *mut c_char {
|
||||
assert!(!s.is_null());
|
||||
let len = unsafe {
|
||||
slice::from_raw_parts(s, len)
|
||||
|
|
||||
.into_iter()
|
||||
.position(|p| *p == 0)
|
||||
.unwrap_or(len)
|
||||
};
|
||||
let copy: *mut c_char = alloc_bytes(len + 1).cast(); // Implicitly zeroed.
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(s, copy, len);
|
||||
}
|
||||
copy.cast::<c_char>()
|
||||
}
|
||||
|
||||
/// Concatenates `s1` and `s2` into a `wmalloc`-managed C-style string.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn wstrconcat(s1: *const c_char, s2: *const c_char) -> *mut c_char {
|
||||
match (s1.is_null(), s2.is_null()) {
|
||||
(true, true) => ptr::null_mut(),
|
||||
(true, false) => unsafe { wstrdup(s1) },
|
||||
(false, true) => unsafe { wstrdup(s2) },
|
||||
(false, false) => unsafe {
|
||||
let s1 = CStr::from_ptr(s1);
|
||||
let l1 = s1.count_bytes();
|
||||
let s2 = CStr::from_ptr(s2);
|
||||
let l2 = s2.count_bytes();
|
||||
let s: *mut c_char = alloc_bytes(l1 + l2 + 1).cast(); // Implicitly zeroed.
|
||||
ptr::copy_nonoverlapping(s1.as_ptr(), s, l1);
|
||||
ptr::copy_nonoverlapping(s2.as_ptr(), s.offset(l1 as isize), l2);
|
||||
s
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Appends `src` to `dest`, destructively reallocating `dest` and returning a
|
||||
/// new `wmalloc`-managed pointer where the result is stored. `dst` must come
|
||||
/// from `wmalloc` or `wrealloc`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn wstrappend(dst: *mut c_char, src: *const c_char) -> *mut c_char {
|
||||
if src.is_null() {
|
||||
return dst;
|
||||
}
|
||||
let src = unsafe { CStr::from_ptr(src) };
|
||||
let src_len = src.count_bytes();
|
||||
if src_len == 0 {
|
||||
return dst;
|
||||
}
|
||||
|
||||
if dst.is_null() {
|
||||
return unsafe { wstrdup(src.as_ptr()) };
|
||||
}
|
||||
let dst_len = unsafe { CStr::from_ptr(dst).count_bytes() + 1 };
|
||||
|
||||
let len = dst_len + src_len + 1;
|
||||
let result: *mut c_char = unsafe { wrealloc(dst.cast(), len).cast() };
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(
|
||||
src.as_ptr(),
|
||||
result.offset(dst_len.try_into().unwrap()),
|
||||
src_len,
|
||||
);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Strips leading and trailing whitespace from `s`, returning a
|
||||
/// `wmalloc`-managed C-style string holding the result.
|
||||
///
|
||||
/// ## Rust rewite notes
|
||||
///
|
||||
/// This uses a slightly different notion of "space character" than the original
|
||||
/// C implementation did. `s` must be valid UTF-8.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn wtrimspace(s: *const c_char) -> *mut c_char {
|
||||
if s.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let Ok(s) = (unsafe { CStr::from_ptr(s).to_str() }) else {
|
||||
|
cross
commented
So this will allocate a So this will allocate a `Vec<u8>` to hold the trimmed bytes, and then invoke `alloc_string` to do another allocation. I don't know if there's a good way around that; perhaps use `strndup`? Something like,
```rust
let trimmmed = s.trim();
let ptr = trimmed.as_ptr();
let len = trimmed.len();
unsafe { wstrndup(ptr, len) }
```
trurl
commented
This is an improvement. Thanks! This is an improvement. Thanks!
|
||||
// TODO: complain.
|
||||
return ptr::null_mut();
|
||||
};
|
||||
let trimmed = s.trim();
|
||||
let ptr = trimmed.as_ptr();
|
||||
let len = trimmed.len();
|
||||
unsafe { wstrndup(ptr, len) }
|
||||
}
|
||||
|
||||
/// Splits `command` into tokens with approximately the same rules as used in
|
||||
/// the shell for splitting up a command into program arguments.
|
||||
fn tokensplit(command: &[u8]) -> Vec<Vec<u8>> {
|
||||
enum Mode {
|
||||
Start,
|
||||
Unquoted(Vec<u8>),
|
||||
Escape(Context),
|
||||
Quoted { token: Vec<u8>, delimiter: u8, },
|
||||
}
|
||||
enum Context {
|
||||
Unquoted(Vec<u8>),
|
||||
Quoted { token: Vec<u8>, delimiter: u8, },
|
||||
}
|
||||
let mut out = Vec::new();
|
||||
let mut mode = Mode::Start;
|
||||
for &b in command {
|
||||
mode = match (mode, b) {
|
||||
(Mode::Start, b'\\') => Mode::Escape(Context::Unquoted(vec![])),
|
||||
(Mode::Start, b'\'' | b'"') => Mode::Quoted { token: vec![], delimiter: b },
|
||||
(Mode::Start, b' ' | b'\t') => Mode::Start,
|
||||
(Mode::Start, _) => Mode::Unquoted(vec![b]),
|
||||
(Mode::Unquoted(token), b'\\') => Mode::Escape(Context::Unquoted(token)),
|
||||
(Mode::Unquoted(token), b'\'' | b'"') => Mode::Quoted { token, delimiter: b },
|
||||
(Mode::Unquoted(token), b' ' | b'\t') => {
|
||||
out.push(token);
|
||||
Mode::Start
|
||||
}
|
||||
(Mode::Unquoted(mut token), _) => {
|
||||
token.push(b);
|
||||
Mode::Unquoted(token)
|
||||
}
|
||||
(Mode::Escape(Context::Unquoted(mut token)), _) => {
|
||||
token.push(b);
|
||||
Mode::Unquoted(token)
|
||||
}
|
||||
(Mode::Escape(Context::Quoted { mut token, delimiter }), _) => {
|
||||
token.push(b);
|
||||
Mode::Quoted { token, delimiter }
|
||||
}
|
||||
(Mode::Quoted { token, delimiter }, _) if b == delimiter => {
|
||||
Mode::Unquoted(token)
|
||||
}
|
||||
(Mode::Quoted { token, delimiter }, _) if b == b'\\' => {
|
||||
Mode::Escape(Context::Quoted { token, delimiter })
|
||||
}
|
||||
(Mode::Quoted { mut token, delimiter }, _) => {
|
||||
token.push(b);
|
||||
Mode::Quoted { token, delimiter }
|
||||
}
|
||||
}
|
||||
}
|
||||
match mode {
|
||||
Mode::Start => (),
|
||||
Mode::Unquoted(token) => out.push(token),
|
||||
Mode::Quoted { token, .. } => out.push(token),
|
||||
Mode::Escape(Context::Unquoted(token)) => out.push(token),
|
||||
Mode::Escape(Context::Quoted { token, .. }) => out.push(token),
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Splits `command` into tokens, storing the number of tokens in `argc` and
|
||||
/// `wmalloc`-managed C-style strings for each token in `argv`. Call
|
||||
/// [`wtokenfree`] to free `argv`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn wtokensplit(
|
||||
command: *const c_char,
|
||||
argv: *mut *mut *mut c_char,
|
||||
argc: *mut c_int,
|
||||
) {
|
||||
if argv.is_null() || argc.is_null() {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
*argv = ptr::null_mut();
|
||||
*argc = 0;
|
||||
}
|
||||
if command.is_null() {
|
||||
return;
|
||||
}
|
||||
let command = unsafe { CStr::from_ptr(command) };
|
||||
let Ok(command) = command.to_str() else {
|
||||
return;
|
||||
};
|
||||
let tokens = tokensplit(command.as_bytes());
|
||||
if tokens.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let argv = unsafe {
|
||||
*argv = alloc_bytes(mem::size_of::<*mut c_char>() * tokens.len()).cast::<*mut c_char>();
|
||||
*argc = tokens.len() as c_int;
|
||||
slice::from_raw_parts_mut(*argv, tokens.len())
|
||||
};
|
||||
for (dest, mut token) in argv.iter_mut().zip(tokens.into_iter()) {
|
||||
token.push(b'\0');
|
||||
*dest = alloc_string(unsafe { CStr::from_bytes_with_nul_unchecked(&token) });
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees an `argv` populated by [`wtokensplit`].
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn wtokenfree(tokens: *mut *mut c_char, count: c_int) {
|
||||
if tokens.is_null() {
|
||||
return;
|
||||
}
|
||||
if count > 0 {
|
||||
let tokens = unsafe { slice::from_raw_parts_mut(tokens, count as usize) };
|
||||
for token in tokens {
|
||||
unsafe {
|
||||
free_bytes(*token);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
free_bytes(tokens.cast::<u8>());
|
||||
}
|
||||
}
|
||||
|
||||
/// Joins the tokens of the `count` elements of `list` into a single string,
|
||||
/// enclosing them in double quotes (`"`) if necessary. Returns a
|
||||
/// `wmalloc`-managed C-style string with the result, or null on failure.
|
||||
///
|
||||
/// ## Rust rewrite notes
|
||||
///
|
||||
/// `list` must consist of valid UTF-8 strings. This reimplementation does not
|
||||
/// attempt to improve on the original, so it has bad failure modes (like not
|
||||
/// escaping quotes in the input). Do not use this function in new code.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn wtokenjoin(list: *const *const char, count: c_int) -> *mut c_char {
|
||||
if list.is_null() || count <= 0 {
|
||||
return alloc_string(c"");
|
||||
}
|
||||
|
||||
let list = unsafe {
|
||||
slice::from_raw_parts(list.cast::<*const u8>(), count as usize)
|
||||
};
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
for term in list {
|
||||
if term.is_null() {
|
||||
continue;
|
||||
}
|
||||
let term = unsafe { CStr::from_ptr(*term) };
|
||||
if term.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if !buffer.is_empty() {
|
||||
buffer.push(b' ');
|
||||
}
|
||||
let term = term.to_bytes();
|
||||
if term.iter().find(|&&x| x == b' ' || x == b'\t').is_some() {
|
||||
buffer.push(b'"');
|
||||
buffer.extend_from_slice(term);
|
||||
buffer.push(b'"');
|
||||
} else {
|
||||
buffer.extend_from_slice(term);
|
||||
}
|
||||
}
|
||||
buffer.push(b'\0');
|
||||
|
||||
if let Ok(buffer) = CStr::from_bytes_until_nul(&buffer) {
|
||||
alloc_string(buffer)
|
||||
} else {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{wtokenfree, wtokensplit, wtokenjoin};
|
||||
|
||||
use std::{ffi::{c_char, c_int, CStr}, ptr, slice};
|
||||
|
||||
#[test]
|
||||
fn split_empty_whitespace() {
|
||||
let mut argv = ptr::null_mut();
|
||||
let mut argc = 0;
|
||||
|
||||
unsafe { wtokensplit(c"".as_ptr(), &mut argv, &mut argc); }
|
||||
assert_eq!(argc, 0);
|
||||
assert_eq!(argv, ptr::null_mut());
|
||||
unsafe { wtokenfree(argv, argc); }
|
||||
|
||||
unsafe { wtokensplit(c" ".as_ptr(), &mut argv, &mut argc); }
|
||||
assert_eq!(argc, 0);
|
||||
assert_eq!(argv, ptr::null_mut());
|
||||
unsafe { wtokenfree(argv, argc); }
|
||||
|
||||
unsafe { wtokensplit(c" \t ".as_ptr(), &mut argv, &mut argc); }
|
||||
assert_eq!(argc, 0);
|
||||
assert_eq!(argv, ptr::null_mut());
|
||||
unsafe { wtokenfree(argv, argc); }
|
||||
|
||||
unsafe { wtokensplit(c" \t ".as_ptr(), &mut argv, &mut argc); }
|
||||
assert_eq!(argc, 0);
|
||||
assert_eq!(argv, ptr::null_mut());
|
||||
unsafe { wtokenfree(argv, argc); }
|
||||
|
||||
unsafe { wtokensplit(c"\t\t".as_ptr(), &mut argv, &mut argc); }
|
||||
assert_eq!(argc, 0);
|
||||
assert_eq!(argv, ptr::null_mut());
|
||||
unsafe { wtokenfree(argv, argc); }
|
||||
}
|
||||
|
||||
fn args_of(argv: *mut *mut u8, argc: usize) -> Vec<String> {
|
||||
let mut v = Vec::with_capacity(argc);
|
||||
for s in unsafe { slice::from_raw_parts(argv, argc) } {
|
||||
if s.is_null() {
|
||||
return v;
|
||||
}
|
||||
v.push(String::from(unsafe { CStr::from_ptr(*s) }.to_str().unwrap()));
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_empty_quoted() {
|
||||
let mut argv = ptr::null_mut();
|
||||
let mut argc = 0;
|
||||
|
||||
unsafe { wtokensplit(c"\"\"".as_ptr(), &mut argv, &mut argc); }
|
||||
assert_eq!(argc, 1);
|
||||
assert_eq!(args_of(argv, argc as usize), vec![String::from("")]);
|
||||
unsafe { wtokenfree(argv, argc); }
|
||||
|
||||
argv = ptr::null_mut();
|
||||
argc = 0;
|
||||
|
||||
unsafe { wtokensplit(c"''".as_ptr(), &mut argv, &mut argc); }
|
||||
assert_eq!(argc, 1);
|
||||
assert_eq!(args_of(argv, argc as usize), vec![String::from("")]);
|
||||
unsafe { wtokenfree(argv, argc); }
|
||||
|
||||
argv = ptr::null_mut();
|
||||
argc = 0;
|
||||
|
||||
unsafe { wtokensplit(c" ''\t".as_ptr(), &mut argv, &mut argc); }
|
||||
assert_eq!(argc, 1);
|
||||
assert_eq!(args_of(argv, argc as usize), vec![String::from("")]);
|
||||
unsafe { wtokenfree(argv, argc); }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_one_unquoted() {
|
||||
let mut argv = ptr::null_mut();
|
||||
let mut argc = 0;
|
||||
|
||||
unsafe { wtokensplit(c"hello".as_ptr(), &mut argv, &mut argc); }
|
||||
assert_eq!(argc, 1);
|
||||
assert_eq!(args_of(argv, argc as usize), vec![String::from("hello")]);
|
||||
|
||||
unsafe { wtokensplit(c"hello\\\\".as_ptr(), &mut argv, &mut argc); }
|
||||
assert_eq!(argc, 1);
|
||||
assert_eq!(args_of(argv, argc as usize), vec![String::from("hello\\")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_one_quoted() {
|
||||
let mut argv = ptr::null_mut();
|
||||
let mut argc = 0;
|
||||
|
||||
unsafe { wtokensplit(c"\"hello\"".as_ptr(), &mut argv, &mut argc); }
|
||||
assert_eq!(argc, 1);
|
||||
assert_eq!(args_of(argv, argc as usize), vec![String::from("hello")]);
|
||||
|
||||
unsafe { wtokensplit(c"\"hello world\"".as_ptr(), &mut argv, &mut argc); }
|
||||
assert_eq!(argc, 1);
|
||||
assert_eq!(args_of(argv, argc as usize), vec![String::from("hello world")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_multi() {
|
||||
let mut argv = ptr::null_mut();
|
||||
let mut argc = 0;
|
||||
|
||||
unsafe {
|
||||
wtokensplit(
|
||||
c"\"hello world\" what\\'s happening' here it\\'s weird'".as_ptr(),
|
||||
&mut argv,
|
||||
&mut argc,
|
||||
);
|
||||
}
|
||||
assert_eq!(argc, 3);
|
||||
assert_eq!(args_of(argv, argc as usize), vec![String::from("hello world"),
|
||||
String::from("what's"),
|
||||
String::from("happening here it's weird")]);
|
||||
}
|
||||
|
||||
/// Calls `f(wtokenjoin(list))` transparently.
|
||||
fn with_list<R>(list: &[&CStr], f: impl FnOnce(Option<&CStr>) -> R) -> R {
|
||||
let list: Vec<_> = list.iter().map(|s| s.as_ptr().cast::<c_char>()).collect();
|
||||
let joined = unsafe { wtokenjoin(list.as_slice().as_ptr().cast(), list.len() as c_int) };
|
||||
let result = f(if joined.is_null() { None } else { unsafe { Some(CStr::from_ptr(joined)) } });
|
||||
unsafe { crate::memory::free_bytes(joined.cast()); }
|
||||
result
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn join_nothing() {
|
||||
with_list(&[], |joined| {
|
||||
assert_eq!(joined.unwrap(), c"");
|
||||
});
|
||||
|
||||
with_list(&[c"", c""], |joined| {
|
||||
assert_eq!(joined.unwrap(), c"");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn join_basic() {
|
||||
with_list(&[c"hello", c"world"], |joined| {
|
||||
assert_eq!(joined.unwrap(), c"hello world");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn join_quoted() {
|
||||
with_list(&[c"hello", c"there world"], |joined| {
|
||||
assert_eq!(joined.unwrap(), c"hello \"there world\"");
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user
I don't think this is what you want, necessarily. You want to find a zero byte, sure, but looking beyond the first
lenbytes is not useful and may not even be correct, if the source string is not e.g. 0 terminated. I would argue that's a bug, but I could be wrong.So I'd do something like this:
Thanks, this is an improvement. (The extant
wstrndupcalledstrlenons, so I didn't feel too badly about scanning ahead for a null byte ... but we can do better.)I'd like to migrate away from supporting C-style strings, so I'm putting the length computation inline.