1 /* $XTermId: xstrings.c,v 1.78 2020/10/12 18:50:28 tom Exp $ */
2 
3 /*
4  * Copyright 2000-2019,2020 by Thomas E. Dickey
5  *
6  *                         All Rights Reserved
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the
10  * "Software"), to deal in the Software without restriction, including
11  * without limitation the rights to use, copy, modify, merge, publish,
12  * distribute, sublicense, and/or sell copies of the Software, and to
13  * permit persons to whom the Software is furnished to do so, subject to
14  * the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included
17  * in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  *
27  * Except as contained in this notice, the name(s) of the above copyright
28  * holders shall not be used in advertising or otherwise to promote the
29  * sale, use or other dealings in this Software without prior written
30  * authorization.
31  */
32 
33 #include <xterm.h>
34 
35 #include <sys/types.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <ctype.h>
39 
40 #include <xstrings.h>
41 
42 static void
alloc_pw(struct passwd * target,struct passwd * source)43 alloc_pw(struct passwd *target, struct passwd *source)
44 {
45     *target = *source;
46     /* we care only about these strings */
47     target->pw_dir = x_strdup(source->pw_dir);
48     target->pw_name = x_strdup(source->pw_name);
49     target->pw_shell = x_strdup(source->pw_shell);
50 }
51 
52 static void
free_pw(struct passwd * source)53 free_pw(struct passwd *source)
54 {
55     free(source->pw_dir);
56     free(source->pw_name);
57     free(source->pw_shell);
58 }
59 
60 void
x_appendargv(char ** target,char ** source)61 x_appendargv(char **target, char **source)
62 {
63     if (target && source) {
64 	target += x_countargv(target);
65 	while ((*target++ = *source++) != 0) ;
66     }
67 }
68 
69 char *
x_basename(char * name)70 x_basename(char *name)
71 {
72     char *cp;
73 
74     cp = strrchr(name, '/');
75     return (cp ? cp + 1 : name);
76 }
77 
78 unsigned
x_countargv(char ** argv)79 x_countargv(char **argv)
80 {
81     unsigned result = 0;
82     if (argv) {
83 	while (*argv++) {
84 	    ++result;
85 	}
86     }
87     return result;
88 }
89 
90 /*
91  * Decode a hexadecimal string, returning the decoded string.
92  * On return, 'next' points to the first character not part of the input.
93  * The caller must free the result.
94  */
95 char *
x_decode_hex(const char * source,const char ** next)96 x_decode_hex(const char *source, const char **next)
97 {
98     char *result = 0;
99     int pass;
100     size_t j, k;
101 
102     for (pass = 0; pass < 2; ++pass) {
103 	for (j = k = 0; isxdigit(CharOf(source[j])); ++j) {
104 	    if ((pass != 0) && (j & 1) != 0) {
105 		result[k++] = (char) ((CharOf(x_hex2int(source[j - 1])) << 4)
106 				      | CharOf(x_hex2int(source[j])));
107 	    }
108 	}
109 	*next = (source + j);
110 	if ((j & 1) == 0) {
111 	    if (pass) {
112 		result[k] = '\0';
113 	    } else {
114 		result = malloc(++j);
115 		if (result == 0)
116 		    break;	/* not enough memory */
117 	    }
118 	} else {
119 	    break;		/* must have an even number of digits */
120 	}
121     }
122     return result;
123 }
124 
125 /*
126  * Encode a string into hexadecimal, returning the encoded string.
127  * The caller must free the result.
128  */
129 char *
x_encode_hex(const char * source)130 x_encode_hex(const char *source)
131 {
132     size_t need = (strlen(source) * 2) + 1;
133     char *result = malloc(need);
134 
135     if (result != 0) {
136 	unsigned j, k;
137 	result[0] = '\0';
138 	for (j = k = 0; source[j] != '\0'; ++j) {
139 	    sprintf(result + k, "%02X", CharOf(source[j]));
140 	    k += 2;
141 	}
142     }
143     return result;
144 }
145 
146 char *
x_getenv(const char * name)147 x_getenv(const char *name)
148 {
149     char *result;
150     result = x_strdup(x_nonempty(getenv(name)));
151     TRACE2(("getenv(%s) %s\n", name, result));
152     return result;
153 }
154 
155 static char *
login_alias(char * login_name,uid_t uid,struct passwd * in_out)156 login_alias(char *login_name, uid_t uid, struct passwd *in_out)
157 {
158     /*
159      * If the logon-name differs from the value we get by looking in the
160      * password file, check if it does correspond to the same uid.  If so,
161      * allow that as an alias for the uid.
162      */
163     if (!IsEmpty(login_name)
164 	&& strcmp(login_name, in_out->pw_name)) {
165 	struct passwd pw2;
166 	Boolean ok2;
167 
168 	if ((ok2 = x_getpwnam(login_name, &pw2))) {
169 	    uid_t uid2 = pw2.pw_uid;
170 	    struct passwd pw3;
171 	    Boolean ok3;
172 
173 	    if ((ok3 = x_getpwuid(uid, &pw3))
174 		&& ((uid_t) pw3.pw_uid == uid2)) {
175 		/* use the other passwd-data including shell */
176 		alloc_pw(in_out, &pw2);
177 	    } else {
178 		FreeAndNull(login_name);
179 	    }
180 	    if (ok2)
181 		free_pw(&pw2);
182 	    if (ok3)
183 		free_pw(&pw3);
184 	}
185     }
186     return login_name;
187 }
188 
189 /*
190  * Call this with in_out pointing to data filled in by x_getpwnam() or by
191  * x_getpwnam().  It finds the user's logon name, if possible.  As a side
192  * effect, it updates in_out to fill in possibly more-relevant data, i.e.,
193  * in case there is more than one alias for the same uid.
194  */
195 char *
x_getlogin(uid_t uid,struct passwd * in_out)196 x_getlogin(uid_t uid, struct passwd *in_out)
197 {
198     char *login_name;
199 
200     login_name = login_alias(x_getenv("LOGNAME"), uid, in_out);
201     if (IsEmpty(login_name)) {
202 	free(login_name);
203 	login_name = login_alias(x_getenv("USER"), uid, in_out);
204     }
205 #ifdef HAVE_GETLOGIN
206     /*
207      * Of course getlogin() will fail if we're started from a window-manager,
208      * since there's no controlling terminal to fuss with.  For that reason, we
209      * tried first to get something useful from the user's $LOGNAME or $USER
210      * environment variables.
211      */
212     if (IsEmpty(login_name)) {
213 	TRACE2(("...try getlogin\n"));
214 	free(login_name);
215 	login_name = login_alias(x_strdup(getlogin()), uid, in_out);
216     }
217 #endif
218 
219     if (IsEmpty(login_name)) {
220 	free(login_name);
221 	login_name = x_strdup(in_out->pw_name);
222     }
223 
224     TRACE2(("x_getloginid ->%s\n", NonNull(login_name)));
225     return login_name;
226 }
227 
228 /*
229  * Simpler than getpwnam_r, retrieves the passwd result by name and stores the
230  * result via the given pointer.  On failure, wipes the data to prevent use.
231  */
232 Boolean
x_getpwnam(const char * name,struct passwd * result)233 x_getpwnam(const char *name, struct passwd *result)
234 {
235     struct passwd *ptr = getpwnam(name);
236     Boolean code;
237 
238     if (ptr != 0 && OkPasswd(ptr)) {
239 	code = True;
240 	alloc_pw(result, ptr);
241     } else {
242 	code = False;
243 	memset(result, 0, sizeof(*result));
244     }
245     return code;
246 }
247 
248 /*
249  * Simpler than getpwuid_r, retrieves the passwd result by uid and stores the
250  * result via the given pointer.  On failure, wipes the data to prevent use.
251  */
252 Boolean
x_getpwuid(uid_t uid,struct passwd * result)253 x_getpwuid(uid_t uid, struct passwd *result)
254 {
255     struct passwd *ptr = getpwuid((uid_t) uid);
256     Boolean code;
257 
258     if (ptr != 0 && OkPasswd(ptr)) {
259 	code = True;
260 	alloc_pw(result, ptr);
261     } else {
262 	code = False;
263 	memset(result, 0, sizeof(*result));
264     }
265     TRACE2(("x_getpwuid(%d) %d\n", (int) uid, (int) code));
266     return code;
267 }
268 
269 /*
270  * Decode a single hex "nibble", returning the nibble as 0-15, or -1 on error.
271  */
272 int
x_hex2int(int c)273 x_hex2int(int c)
274 {
275     if (c >= '0' && c <= '9')
276 	return c - '0';
277     if (c >= 'a' && c <= 'f')
278 	return c - 'a' + 10;
279     if (c >= 'A' && c <= 'F')
280 	return c - 'A' + 10;
281     return -1;
282 }
283 
284 /*
285  * Check if the given string is nonnull/nonempty.  If so, return a pointer
286  * to the beginning of its content, otherwise return null.
287  */
288 String
x_nonempty(String s)289 x_nonempty(String s)
290 {
291     if (s != 0) {
292 	if (*s == '\0') {
293 	    s = 0;
294 	} else {
295 	    s = x_skip_blanks(s);
296 	    if (*s == '\0')
297 		s = 0;
298 	}
299     }
300     return s;
301 }
302 
303 String
x_skip_blanks(String s)304 x_skip_blanks(String s)
305 {
306     while (IsSpace(CharOf(*s)))
307 	++s;
308     return s;
309 }
310 
311 String
x_skip_nonblanks(String s)312 x_skip_nonblanks(String s)
313 {
314     while (*s != '\0' && !IsSpace(CharOf(*s)))
315 	++s;
316     return s;
317 }
318 
319 static const char *
skip_blanks(const char * s)320 skip_blanks(const char *s)
321 {
322     while (IsSpace(CharOf(*s)))
323 	++s;
324     return s;
325 }
326 
327 /*
328  * Split a command-string into an argv[]-style array.
329  */
330 char **
x_splitargs(const char * command)331 x_splitargs(const char *command)
332 {
333     char **result = 0;
334 
335     if (command != 0) {
336 	const char *first = skip_blanks(command);
337 	char *blob = x_strdup(first);
338 
339 	if (blob != 0) {
340 	    int pass;
341 
342 	    for (pass = 0; pass < 2; ++pass) {
343 		int state;
344 		size_t count;
345 		size_t n;
346 
347 		for (n = count = 0, state = 0; first[n] != '\0'; ++n) {
348 
349 		    switch (state) {
350 		    case 0:
351 			if (!IsSpace(CharOf(first[n]))) {
352 			    state = 1;
353 			    if (pass)
354 				result[count] = blob + n;
355 			    ++count;
356 			} else {
357 			    blob[n] = '\0';
358 			}
359 			break;
360 		    case 1:
361 			if (IsSpace(CharOf(first[n]))) {
362 			    blob[n] = '\0';
363 			    state = 0;
364 			}
365 			break;
366 		    }
367 		}
368 		if (!pass) {
369 		    result = TypeCallocN(char *, count + 1);
370 		    if (!result) {
371 			free(blob);
372 			break;
373 		    }
374 		}
375 	    }
376 	}
377     } else {
378 	result = TypeCalloc(char *);
379     }
380     return result;
381 }
382 
383 /*
384  * Free storage allocated by x_splitargs().
385  */
386 void
x_freeargs(char ** argv)387 x_freeargs(char **argv)
388 {
389     if (argv != 0) {
390 	free(*argv);
391 	free(argv);
392     }
393 }
394 
395 int
x_strcasecmp(const char * s1,const char * s2)396 x_strcasecmp(const char *s1, const char *s2)
397 {
398     size_t len = strlen(s1);
399 
400     if (len != strlen(s2))
401 	return 1;
402 
403     return x_strncasecmp(s1, s2, (unsigned) len);
404 }
405 
406 int
x_strncasecmp(const char * s1,const char * s2,unsigned n)407 x_strncasecmp(const char *s1, const char *s2, unsigned n)
408 {
409     while (n-- != 0) {
410 	char c1 = x_toupper(*s1);
411 	char c2 = x_toupper(*s2);
412 	if (c1 != c2)
413 	    return 1;
414 	if (c1 == 0)
415 	    break;
416 	s1++;
417 	s2++;
418     }
419 
420     return 0;
421 }
422 
423 /*
424  * Allocates a copy of a string
425  */
426 char *
x_strdup(const char * s)427 x_strdup(const char *s)
428 {
429     char *result = 0;
430 
431     if (s != 0) {
432 	char *t = malloc(strlen(s) + 5);
433 	if (t != 0) {
434 	    strcpy(t, s);
435 	}
436 	result = t;
437     }
438     return result;
439 }
440 
441 /*
442  * Returns a pointer to the first occurrence of s2 in s1,
443  * or NULL if there are none.
444  */
445 char *
x_strindex(char * s1,const char * s2)446 x_strindex(char *s1, const char *s2)
447 {
448     char *s3;
449     size_t s2len = strlen(s2);
450 
451     while ((s3 = (strchr) (s1, *s2)) != NULL) {
452 	if (strncmp(s3, s2, s2len) == 0)
453 	    return (s3);
454 	s1 = ++s3;
455     }
456     return (NULL);
457 }
458 
459 /*
460  * Trims leading/trailing spaces from a copy of the string.
461  */
462 char *
x_strtrim(const char * source)463 x_strtrim(const char *source)
464 {
465     char *result;
466 
467     if (IsEmpty(source)) {
468 	result = x_strdup("");
469     } else {
470 	char *t = x_strdup(source);
471 	if (t != 0) {
472 	    char *s = t;
473 	    char *d = s;
474 	    while (IsSpace(CharOf(*s)))
475 		++s;
476 	    while ((*d++ = *s++) != '\0') {
477 		;
478 	    }
479 	    if (*t != '\0') {
480 		s = t + strlen(t);
481 		while (s != t && IsSpace(CharOf(s[-1]))) {
482 		    *--s = '\0';
483 		}
484 	    }
485 	}
486 	result = t;
487     }
488     return result;
489 }
490 
491 /*
492  * Trims trailing whitespace from a copy of the string.
493  */
494 char *
x_strrtrim(const char * source)495 x_strrtrim(const char *source)
496 {
497     char *result;
498 
499     if (IsEmpty(source)) {
500 	result = x_strdup("");
501     } else {
502 	char *t = x_strdup(source);
503 	if (t != 0) {
504 	    if (*t != '\0') {
505 		char *s = t + strlen(t);
506 		while (s != t && IsSpace(CharOf(s[-1]))) {
507 		    *--s = '\0';
508 		}
509 	    }
510 	}
511 	result = t;
512     }
513     return result;
514 }
515 
516 /*
517  * Avoid using system locale for upper/lowercase conversion, since there are
518  * a few locales where toupper(tolower(c)) != c.
519  */
520 char
x_toupper(int ch)521 x_toupper(int ch)
522 {
523     static char table[256];
524     char result = table[CharOf(ch)];
525 
526     if (result == '\0') {
527 	unsigned n;
528 	static const char s[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
529 
530 	for (n = 0; n < sizeof(table); ++n) {
531 	    table[n] = (char) n;
532 	}
533 	for (n = 0; s[n] != '\0'; ++n) {
534 	    table[CharOf(s[n])] = s[n % 26];
535 	}
536 	result = table[CharOf(ch)];
537     }
538 
539     return result;
540 }
541 
542 /*
543  * Match strings ignoring case and allowing glob-like '*' and '?'
544  */
545 int
x_wildstrcmp(const char * pattern,const char * actual)546 x_wildstrcmp(const char *pattern, const char *actual)
547 {
548     int result = 0;
549 
550     while (*pattern && *actual) {
551 	char c1 = x_toupper(*pattern);
552 	char c2 = x_toupper(*actual);
553 
554 	if (c1 == '*') {
555 	    Boolean found = False;
556 	    pattern++;
557 	    while (*actual != '\0') {
558 		if (!x_wildstrcmp(pattern, actual++)) {
559 		    found = True;
560 		    break;
561 		}
562 	    }
563 	    if (!found) {
564 		result = 1;
565 		break;
566 	    }
567 	} else if (c1 == '?') {
568 	    ++pattern;
569 	    ++actual;
570 	} else if ((result = (c1 != c2)) == 0) {
571 	    ++pattern;
572 	    ++actual;
573 	} else {
574 	    break;
575 	}
576     }
577     return result;
578 }
579