summaryrefslogtreecommitdiff
path: root/xstrings.c
diff options
context:
space:
mode:
Diffstat (limited to 'xstrings.c')
-rw-r--r--xstrings.c473
1 files changed, 473 insertions, 0 deletions
diff --git a/xstrings.c b/xstrings.c
new file mode 100644
index 0000000..395def5
--- /dev/null
+++ b/xstrings.c
@@ -0,0 +1,473 @@
+/* $XTermId: xstrings.c,v 1.50 2012/03/30 10:54:12 tom Exp $ */
+
+/*
+ * Copyright 2000-2011,2012 by Thomas E. Dickey
+ *
+ * All Rights Reserved
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name(s) of the above copyright
+ * holders shall not be used in advertising or otherwise to promote the
+ * sale, use or other dealings in this Software without prior written
+ * authorization.
+ */
+
+#include <xterm.h>
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <xstrings.h>
+
+static void
+alloc_pw(struct passwd *target, struct passwd *source)
+{
+ *target = *source;
+ /* we care only about these strings */
+ target->pw_dir = x_strdup(source->pw_dir);
+ target->pw_name = x_strdup(source->pw_name);
+ target->pw_shell = x_strdup(source->pw_shell);
+}
+
+void
+x_appendargv(char **target, char **source)
+{
+ if (target && source) {
+ target += x_countargv(target);
+ while ((*target++ = *source++) != 0) ;
+ }
+}
+
+char *
+x_basename(char *name)
+{
+ char *cp;
+
+ cp = strrchr(name, '/');
+#ifdef __UNIXOS2__
+ if (cp == 0)
+ cp = strrchr(name, '\\');
+#endif
+ return (cp ? cp + 1 : name);
+}
+
+unsigned
+x_countargv(char **argv)
+{
+ unsigned result = 0;
+ if (argv) {
+ while (*argv++) {
+ ++result;
+ }
+ }
+ return result;
+}
+
+/*
+ * Decode a hexadecimal string, returning the decoded string.
+ * On return, 'next' points to the first character not part of the input.
+ * The caller must free the result.
+ */
+char *
+x_decode_hex(const char *source, const char **next)
+{
+ char *result = 0;
+ int pass;
+ size_t j, k;
+
+ for (pass = 0; pass < 2; ++pass) {
+ for (j = k = 0; isxdigit(CharOf(source[j])); ++j) {
+ if ((pass != 0) && (j & 1) != 0) {
+ result[k++] = (char) ((x_hex2int(source[j - 1]) << 4)
+ | x_hex2int(source[j]));
+ }
+ }
+ *next = (source + j);
+ if ((j & 1) == 0) {
+ if (pass) {
+ result[k] = '\0';
+ } else {
+ result = malloc(++j);
+ if (result == 0)
+ break; /* not enough memory */
+ }
+ } else {
+ break; /* must have an even number of digits */
+ }
+ }
+ return result;
+}
+
+/*
+ * Encode a string into hexadecimal, returning the encoded string.
+ * The caller must free the result.
+ */
+char *
+x_encode_hex(const char *source)
+{
+ size_t need = (strlen(source) * 2) + 1;
+ char *result = malloc(need);
+
+ if (result != 0) {
+ unsigned j, k;
+ for (j = k = 0; source[j] != '\0'; ++j) {
+ sprintf(result + k, "%02X", CharOf(source[j]));
+ k += 2;
+ }
+ }
+ return result;
+}
+
+char *
+x_getenv(const char *name)
+{
+ char *result;
+ result = x_strdup(x_nonempty(getenv(name)));
+ TRACE2(("getenv(%s) %s\n", name, result));
+ return result;
+}
+
+static char *
+login_alias(char *login_name, uid_t uid, struct passwd *in_out)
+{
+ /*
+ * If the logon-name differs from the value we get by looking in the
+ * password file, check if it does correspond to the same uid. If so,
+ * allow that as an alias for the uid.
+ */
+ if (!IsEmpty(login_name)
+ && strcmp(login_name, in_out->pw_name)) {
+ struct passwd pw2;
+
+ if (x_getpwnam(login_name, &pw2)) {
+ uid_t uid2 = pw2.pw_uid;
+ struct passwd pw3;
+
+ if (x_getpwuid(uid, &pw3)
+ && ((uid_t) pw3.pw_uid == uid2)) {
+ /* use the other passwd-data including shell */
+ alloc_pw(in_out, &pw2);
+ } else {
+ login_name = NULL;
+ }
+ }
+ }
+ return login_name;
+}
+
+/*
+ * Call this with in_out pointing to data filled in by x_getpwnam() or by
+ * x_getpwnam(). It finds the user's logon name, if possible. As a side
+ * effect, it updates in_out to fill in possibly more-relevant data, i.e.,
+ * in case there is more than one alias for the same uid.
+ */
+char *
+x_getlogin(uid_t uid, struct passwd *in_out)
+{
+ char *login_name = NULL;
+
+ login_name = login_alias(x_getenv("LOGNAME"), uid, in_out);
+ if (IsEmpty(login_name)) {
+ login_name = login_alias(x_getenv("USER"), uid, in_out);
+ }
+#ifdef HAVE_GETLOGIN
+ /*
+ * Of course getlogin() will fail if we're started from a window-manager,
+ * since there's no controlling terminal to fuss with. For that reason, we
+ * tried first to get something useful from the user's $LOGNAME or $USER
+ * environment variables.
+ */
+ if (IsEmpty(login_name)) {
+ TRACE2(("...try getlogin\n"));
+ login_name = login_alias(getlogin(), uid, in_out);
+ }
+#endif
+
+ if (IsEmpty(login_name))
+ login_name = in_out->pw_name;
+ if (!IsEmpty(login_name))
+ login_name = x_strdup(login_name);
+
+ TRACE2(("x_getloginid ->%s\n", NonNull(login_name)));
+ return login_name;
+}
+
+/*
+ * Simpler than getpwnam_r, retrieves the passwd result by name and stores the
+ * result via the given pointer. On failure, wipes the data to prevent use.
+ */
+Boolean
+x_getpwnam(const char *name, struct passwd * result)
+{
+ struct passwd *ptr = getpwnam(name);
+ Boolean code;
+
+ if (ptr != 0 && OkPasswd(ptr)) {
+ code = True;
+ alloc_pw(result, ptr);
+ } else {
+ code = False;
+ memset(result, 0, sizeof(*result));
+ }
+ return code;
+}
+
+/*
+ * Simpler than getpwuid_r, retrieves the passwd result by uid and stores the
+ * result via the given pointer. On failure, wipes the data to prevent use.
+ */
+Boolean
+x_getpwuid(uid_t uid, struct passwd * result)
+{
+ struct passwd *ptr = getpwuid((uid_t) uid);
+ Boolean code;
+
+ if (ptr != 0 && OkPasswd(ptr)) {
+ code = True;
+ alloc_pw(result, ptr);
+ } else {
+ code = False;
+ memset(result, 0, sizeof(*result));
+ }
+ TRACE2(("x_getpwuid(%d) %d\n", (int) uid, (int) code));
+ return code;
+}
+
+/*
+ * Decode a single hex "nibble", returning the nibble as 0-15, or -1 on error.
+ */
+int
+x_hex2int(int c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return -1;
+}
+
+/*
+ * Check if the given string is nonnull/nonempty. If so, return a pointer
+ * to the beginning of its content, otherwise return null.
+ */
+String
+x_nonempty(String s)
+{
+ if (s != 0) {
+ if (*s == '\0') {
+ s = 0;
+ } else {
+ s = x_skip_blanks(s);
+ if (*s == '\0')
+ s = 0;
+ }
+ }
+ return s;
+}
+
+String
+x_skip_blanks(String s)
+{
+ while (isspace(CharOf(*s)))
+ ++s;
+ return s;
+}
+
+String
+x_skip_nonblanks(String s)
+{
+ while (*s != '\0' && !isspace(CharOf(*s)))
+ ++s;
+ return s;
+}
+
+/*
+ * Split a command-string into an argv[]-style array.
+ */
+char **
+x_splitargs(const char *command)
+{
+ char **result = 0;
+
+ if (command != 0) {
+ char *blob = x_strdup(command);
+ size_t count;
+ size_t n;
+ int state;
+ int pass;
+
+ for (pass = 0; pass < 2; ++pass) {
+ for (n = count = 0, state = 0; command[n] != '\0'; ++n) {
+ switch (state) {
+ case 0:
+ if (!isspace(CharOf(command[n]))) {
+ state = 1;
+ if (pass)
+ result[count] = blob + n;
+ ++count;
+ } else {
+ blob[n] = '\0';
+ }
+ break;
+ case 1:
+ if (isspace(CharOf(command[n]))) {
+ blob[n] = '\0';
+ state = 0;
+ }
+ break;
+ }
+ }
+ if (!pass) {
+ result = TypeCallocN(char *, count + 1);
+ if (!result)
+ break;
+ }
+ }
+ } else {
+ result = TypeCalloc(char *);
+ }
+ return result;
+}
+
+int
+x_strcasecmp(const char *s1, const char *s2)
+{
+ size_t len = strlen(s1);
+
+ if (len != strlen(s2))
+ return 1;
+
+ return x_strncasecmp(s1, s2, (unsigned) len);
+}
+
+int
+x_strncasecmp(const char *s1, const char *s2, unsigned n)
+{
+ while (n-- != 0) {
+ char c1 = x_toupper(*s1);
+ char c2 = x_toupper(*s2);
+ if (c1 != c2)
+ return 1;
+ if (c1 == 0)
+ break;
+ s1++, s2++;
+ }
+
+ return 0;
+}
+
+/*
+ * Allocates a copy of a string
+ */
+char *
+x_strdup(const char *s)
+{
+ char *result = 0;
+
+ if (s != 0) {
+ char *t = CastMallocN(char, strlen(s) + 1);
+ if (t != 0) {
+ strcpy(t, s);
+ }
+ result = t;
+ }
+ return result;
+}
+
+/*
+ * Returns a pointer to the first occurrence of s2 in s1,
+ * or NULL if there are none.
+ */
+char *
+x_strindex(char *s1, const char *s2)
+{
+ char *s3;
+ size_t s2len = strlen(s2);
+
+ while ((s3 = strchr(s1, *s2)) != NULL) {
+ if (strncmp(s3, s2, s2len) == 0)
+ return (s3);
+ s1 = ++s3;
+ }
+ return (NULL);
+}
+
+/*
+ * Trims leading/trailing spaces from a copy of the string.
+ */
+char *
+x_strtrim(const char *source)
+{
+ char *result;
+ char *s;
+ char *d;
+
+ if (source != 0 && *source != '\0') {
+ char *t = x_strdup(source);
+ s = t;
+ d = s;
+ while (isspace(CharOf(*s)))
+ ++s;
+ while ((*d++ = *s++) != '\0') {
+ ;
+ }
+ if (*t != '\0') {
+ s = t + strlen(t);
+ while (s != t && isspace(CharOf(s[-1]))) {
+ *--s = '\0';
+ }
+ }
+ result = t;
+ } else {
+ result = x_strdup("");
+ }
+ return result;
+}
+
+/*
+ * Avoid using system locale for upper/lowercase conversion, since there are
+ * a few locales where toupper(tolower(c)) != c.
+ */
+char
+x_toupper(int ch)
+{
+ static char table[256];
+ char result = table[CharOf(ch)];
+
+ if (result == '\0') {
+ unsigned n;
+ const char *s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+ for (n = 0; n < sizeof(table); ++n) {
+ table[n] = (char) n;
+ }
+ for (n = 0; s[n] != '\0'; ++n) {
+ table[CharOf(s[n])] = s[n % 26];
+ }
+ result = table[CharOf(ch)];
+ }
+
+ return result;
+}