diff --git a/WINGs/Makefile.am b/WINGs/Makefile.am index 82ea4514..0423a8be 100644 --- a/WINGs/Makefile.am +++ b/WINGs/Makefile.am @@ -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 \ diff --git a/WINGs/WINGs/WUtil.h b/WINGs/WINGs/WUtil.h index 18c8b52b..0caa7882 100644 --- a/WINGs/WINGs/WUtil.h +++ b/WINGs/WINGs/WUtil.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); diff --git a/WINGs/dragsource.c b/WINGs/dragsource.c index d70d7993..c6bb9466 100644 --- a/WINGs/dragsource.c +++ b/WINGs/dragsource.c @@ -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]); diff --git a/WINGs/findfile.c b/WINGs/findfile.c index 10c26b3b..c4f23093 100644 --- a/WINGs/findfile.c +++ b/WINGs/findfile.c @@ -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; } diff --git a/WINGs/menuparser_macros.c b/WINGs/menuparser_macros.c index 8c12f2de..56930c06 100644 --- a/WINGs/menuparser_macros.c +++ b/WINGs/menuparser_macros.c @@ -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 */ diff --git a/WINGs/string.c b/WINGs/string.c deleted file mode 100644 index 393f2887..00000000 --- a/WINGs/string.c +++ /dev/null @@ -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 -#include -#include -#include -#ifdef HAVE_BSD_STRING_H -#include -#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 - * - * 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 - * - * 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. */ -} diff --git a/WINGs/wapplication.c b/WINGs/wapplication.c index 07164ec0..45070a22 100644 --- a/WINGs/wapplication.c +++ b/WINGs/wapplication.c @@ -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) diff --git a/WINGs/wbrowser.c b/WINGs/wbrowser.c index 4465b73c..b25bc8c1 100644 --- a/WINGs/wbrowser.c +++ b/WINGs/wbrowser.c @@ -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; diff --git a/WINGs/wfilepanel.c b/WINGs/wfilepanel.c index 1da05355..cb01ca1a 100644 --- a/WINGs/wfilepanel.c +++ b/WINGs/wfilepanel.c @@ -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) { diff --git a/WINGs/wfontpanel.c b/WINGs/wfontpanel.c index c57678ab..884f6ce6 100644 --- a/WINGs/wfontpanel.c +++ b/WINGs/wfontpanel.c @@ -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) diff --git a/WINGs/wtextfield.c b/WINGs/wtextfield.c index feb1d189..f8cf253e 100644 --- a/WINGs/wtextfield.c +++ b/WINGs/wtextfield.c @@ -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; diff --git a/WPrefs.app/KeyboardShortcuts.c b/WPrefs.app/KeyboardShortcuts.c index 8df69a9e..49d94ab7 100644 --- a/WPrefs.app/KeyboardShortcuts.c +++ b/WPrefs.app/KeyboardShortcuts.c @@ -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); } diff --git a/configure.ac b/configure.ac index 78313976..72222d7d 100644 --- a/configure.ac +++ b/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"], diff --git a/src/appmenu.c b/src/appmenu.c index 11191641..fcc53ac9 100644 --- a/src/appmenu.c +++ b/src/appmenu.c @@ -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); diff --git a/src/defaults.c b/src/defaults.c index 1532692a..e4ef4a21 100644 --- a/src/defaults.c +++ b/src/defaults.c @@ -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; diff --git a/src/dialog.c b/src/dialog.c index e1aad51a..7b0298f5 100644 --- a/src/dialog.c +++ b/src/dialog.c @@ -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; diff --git a/src/rootmenu.c b/src/rootmenu.c index c7931580..bac0ce10 100644 --- a/src/rootmenu.c +++ b/src/rootmenu.c @@ -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 */ diff --git a/src/workspace.c b/src/workspace.c index 6686c1a1..2e4020ac 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -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; diff --git a/wutil-rs/Makefile.am b/wutil-rs/Makefile.am index 00067adb..afbf427a 100644 --- a/wutil-rs/Makefile.am +++ b/wutil-rs/Makefile.am @@ -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 \ diff --git a/wutil-rs/src/lib.rs b/wutil-rs/src/lib.rs index 68a17ca2..55adc6f7 100644 --- a/wutil-rs/src/lib.rs +++ b/wutil-rs/src/lib.rs @@ -5,3 +5,4 @@ pub mod find_file; pub mod hash_table; pub mod memory; pub mod prop_list; +pub mod string; diff --git a/wutil-rs/src/string.rs b/wutil-rs/src/string.rs new file mode 100644 index 00000000..98aec8e9 --- /dev/null +++ b/wutil-rs/src/string.rs @@ -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::() +} + +/// 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 { + // 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> { + enum Mode { + Start, + Unquoted(Vec), + Escape(Context), + Quoted { token: Vec, delimiter: u8, }, + } + enum Context { + Unquoted(Vec), + Quoted { token: Vec, 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::()); + } +} + +/// 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 { + 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(list: &[&CStr], f: impl FnOnce(Option<&CStr>) -> R) -> R { + let list: Vec<_> = list.iter().map(|s| s.as_ptr().cast::()).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\""); + }); + } +}