| /*****************************************************************************\ |
| * xstring.c - heap-oriented string manipulation functions with "safe" |
| * string expansion as needed. |
| ****************************************************************************** |
| * Copyright (C) 2002 The Regents of the University of California. |
| * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). |
| * Written by Jim Garlick <garlick@llnl.gov> |
| * Mark Grondona <grondona@llnl.gov>, et al. |
| * |
| * CODE-OCEC-09-009. All rights reserved. |
| * |
| * This file is part of Slurm, a resource management program. |
| * For details, see <https://slurm.schedmd.com/>. |
| * Please also read the included file: DISCLAIMER. |
| * |
| * Slurm is free software; you can redistribute it and/or modify it under |
| * the terms of the GNU General Public License as published by the Free |
| * Software Foundation; either version 2 of the License, or (at your option) |
| * any later version. |
| * |
| * In addition, as a special exception, the copyright holders give permission |
| * to link the code of portions of this program with the OpenSSL library under |
| * certain conditions as described in each individual source file, and |
| * distribute linked combinations including the two. You must obey the GNU |
| * General Public License in all respects for all of the code used other than |
| * OpenSSL. If you modify file(s) with this exception, you may extend this |
| * exception to your version of the file(s), but you are not obligated to do |
| * so. If you do not wish to do so, delete this exception statement from your |
| * version. If you delete this exception statement from all source files in |
| * the program, then also delete it here. |
| * |
| * Slurm is distributed in the hope that it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| * details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with Slurm; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| \*****************************************************************************/ |
| |
| #include "config.h" |
| |
| #include <ctype.h> |
| #include <errno.h> |
| #include <libgen.h> |
| #include <pthread.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <time.h> |
| #include <sys/time.h> |
| #include <unistd.h> |
| |
| #include "slurm/slurm_errno.h" |
| |
| #include "src/common/macros.h" |
| #include "src/common/slurm_time.h" |
| #include "src/common/xassert.h" |
| #include "src/common/xmalloc.h" |
| #include "src/common/xstring.h" |
| |
| #define XFGETS_CHUNKSIZE 64 |
| |
| /* |
| * Define slurm-specific aliases for use by plugins, see slurm_xlator.h |
| * for details. |
| */ |
| strong_alias(_xstrcat, slurm_xstrcat); |
| strong_alias(_xstrncat, slurm_xstrncat); |
| strong_alias(_xstrncatat, slurm_xstrncatat); |
| strong_alias(_xstrcatchar, slurm_xstrcatchar); |
| strong_alias(_xstrftimecat, slurm_xstrftimecat); |
| strong_alias(_xiso8601timecat, slurm_xiso8601timecat); |
| strong_alias(_xrfc5424timecat, slurm_xrfc5424timecat); |
| strong_alias(_xstrfmtcat, slurm_xstrfmtcat); |
| strong_alias(_xstrfmtcatat, slurm_xstrfmtcatat); |
| strong_alias(_xmemcat, slurm_xmemcat); |
| strong_alias(xstrdup, slurm_xstrdup); |
| strong_alias(xstrdup_printf, slurm_xstrdup_printf); |
| strong_alias(_xstrdup_vprintf, slurm_xstrdup_vprintf); |
| strong_alias(xstrndup, slurm_xstrndup); |
| strong_alias(xbasename, slurm_xbasename); |
| strong_alias(xdirname, slurm_xdirname); |
| strong_alias(_xstrsubstitute, slurm_xstrsubstitute); |
| strong_alias(xshort_hostname, slurm_xshort_hostname); |
| strong_alias(xstring_is_whitespace, slurm_xstring_is_whitespace); |
| strong_alias(xstrtolower, slurm_xstrtolower); |
| strong_alias(xstrchr, slurm_xstrchr); |
| strong_alias(xstrrchr, slurm_xstrrchr); |
| strong_alias(xstrcmp, slurm_xstrcmp); |
| strong_alias(xstrncmp, slurm_xstrncmp); |
| strong_alias(xstrcasecmp, slurm_xstrcasecmp); |
| strong_alias(xstrncasecmp, slurm_xstrncasecmp); |
| strong_alias(xstrstr, slurm_xstrstr); |
| strong_alias(xstrcasestr, slurm_xstrcasestr); |
| strong_alias(xbase64_from_base64url, slurm_xbase64_from_base64url); |
| |
| /* |
| * Ensure that a string has enough space to add 'needed' characters. |
| * If the string is uninitialized, it should be NULL. |
| * str (IN/OUT) str |
| * str_len(IN) current string length, if known. -1 otherwise |
| * needed (IN) additional space needed |
| */ |
| static void _makespace(char **str, int str_len, int needed) |
| { |
| if (*str == NULL) |
| *str = xmalloc(needed + 1); |
| else { |
| int actual_size; |
| int used = (str_len < 0) ? strlen(*str) + 1 : str_len + 1; |
| int min_new_size = used + needed; |
| int cur_size = xsize(*str); |
| if (min_new_size > cur_size) { |
| int new_size = min_new_size; |
| if (new_size < (cur_size + XFGETS_CHUNKSIZE)) |
| new_size = cur_size + XFGETS_CHUNKSIZE; |
| if (new_size < (cur_size * 2)) |
| new_size = cur_size * 2; |
| |
| xrealloc(*str, new_size); |
| actual_size = xsize(*str); |
| if (actual_size) |
| xassert(actual_size == new_size); |
| } |
| } |
| } |
| |
| /* |
| * Concatenate str2 onto str1, expanding str1 as needed. |
| * str1 (IN/OUT) target string (pointer to in case of expansion) |
| * str2 (IN) source string |
| */ |
| void _xstrcat(char **str1, const char *str2) |
| { |
| if (str2 == NULL) |
| str2 = "(null)"; |
| |
| _makespace(str1, -1, strlen(str2)); |
| strcat(*str1, str2); |
| } |
| |
| /* |
| * Append str2 onto str at pos, * expanding buf as needed. pos is updated to the |
| * end of the appended string. |
| * |
| * Meant to be used in loops constructing longer strings that are performance |
| * sensitive, as xstrcat() needs to re-seek to the end of str making the string |
| * construction worse by another O(log(strlen)) factor. |
| */ |
| void _xstrncatat(char **str, char **pos, const char *str2, ssize_t append_len) |
| { |
| size_t orig_len; |
| |
| if (!str2) |
| return; |
| |
| if (append_len < 0) |
| append_len = strlen(str2); |
| |
| /* No string yet to append to, so return a copy of str2. */ |
| if (!*str) { |
| *str = xstrndup(str2, append_len); |
| *pos = *str + append_len; |
| return; |
| } |
| |
| if (!*pos) { |
| orig_len = strlen(*str); |
| } else { |
| xassert(*pos >= *str); |
| orig_len = *pos - *str; |
| } |
| |
| _makespace(str, orig_len, append_len); |
| |
| memcpy(*str + orig_len, str2, append_len); |
| |
| /* |
| * Update *pos. Cannot happen earlier as _makespace() may have |
| * changed *str to a different address. |
| */ |
| *pos = *str + orig_len + append_len; |
| } |
| |
| /* |
| * Concatenate len of str2 onto str1, expanding str1 as needed. |
| * str1 (IN/OUT) target string (pointer to in case of expansion) |
| * str2 (IN) source string |
| * len (IN) len of str2 to concat |
| */ |
| void _xstrncat(char **str1, const char *str2, size_t len) |
| { |
| if (str2 == NULL) |
| str2 = "(null)"; |
| |
| _makespace(str1, -1, len); |
| strncat(*str1, str2, len); |
| } |
| |
| /* |
| * Add a character to str, expanding str1 as needed. |
| * str1 (IN/OUT) target string (pointer to in case of expansion) |
| * c (IN) character to add |
| */ |
| void _xstrcatchar(char **str, char c) |
| { |
| int len = (*str) ? strlen(*str) : 0; |
| |
| _makespace(str, len, 1); |
| (*str)[len++] = c; |
| (*str)[len] = '\0'; |
| } |
| |
| /* |
| * append strftime of fmt to buffer buf, expand buf as needed |
| * |
| */ |
| void _xstrftimecat(char **buf, const char *fmt) |
| { |
| char p[256]; /* output truncated to 256 chars */ |
| time_t t; |
| struct tm tm; |
| |
| const char default_fmt[] = "%m/%d/%Y %H:%M:%S %Z"; |
| |
| if (fmt == NULL) |
| fmt = default_fmt; |
| |
| if (time(&t) == (time_t) -1) |
| fprintf(stderr, "time() failed\n"); |
| |
| if (!localtime_r(&t, &tm)) |
| fprintf(stderr, "localtime_r() failed\n"); |
| |
| strftime(p, sizeof(p), fmt, &tm); |
| |
| |
| _xstrcat(buf, p); |
| } |
| |
| /* |
| * Append a ISO 8601 formatted timestamp to buffer buf, expand as needed |
| */ |
| void _xiso8601timecat(char **buf, bool msec) |
| { |
| char p[64] = ""; |
| struct timeval tv; |
| struct tm tm; |
| |
| if (gettimeofday(&tv, NULL) == -1) |
| fprintf(stderr, "gettimeofday() failed\n"); |
| |
| if (!localtime_r(&tv.tv_sec, &tm)) |
| fprintf(stderr, "localtime_r() failed\n"); |
| |
| if (strftime(p, sizeof(p), "%Y-%m-%dT%T", &tm) == 0) |
| fprintf(stderr, "strftime() returned 0\n"); |
| |
| if (msec) |
| _xstrfmtcat(buf, "%s.%3.3d", p, (int)(tv.tv_usec / 1000)); |
| else |
| _xstrfmtcat(buf, "%s", p); |
| } |
| |
| /* |
| * Append a RFC 5424 formatted timestamp to buffer buf, expand as needed |
| * |
| */ |
| void _xrfc5424timecat(char **buf, bool msec) |
| { |
| char p[64] = ""; |
| char z[12] = ""; |
| struct timeval tv; |
| struct tm tm; |
| |
| if (gettimeofday(&tv, NULL) == -1) |
| fprintf(stderr, "gettimeofday() failed\n"); |
| |
| if (!localtime_r(&tv.tv_sec, &tm)) |
| fprintf(stderr, "localtime_r() failed\n"); |
| |
| if (strftime(p, sizeof(p), "%Y-%m-%dT%T", &tm) == 0) |
| fprintf(stderr, "strftime() returned 0\n"); |
| |
| /* The strftime %z format creates timezone offsets of the form |
| * (+/-)hhmm, whereas the RFC 5424 format is (+/-)hh:mm. So |
| * shift the minutes one step back and insert the semicolon. |
| */ |
| if (strftime(z, sizeof(z), "%z", &tm) == 0) |
| fprintf(stderr, "strftime() returned 0\n"); |
| z[5] = z[4]; |
| z[4] = z[3]; |
| z[3] = ':'; |
| |
| if (msec) |
| _xstrfmtcat(buf, "%s.%3.3d%s", p, (int)(tv.tv_usec / 1000), z); |
| else |
| _xstrfmtcat(buf, "%s%s", p, z); |
| } |
| |
| extern void _xrfc3339timecat(char **buf) |
| { |
| char p[64] = ""; |
| char z[12] = ""; |
| struct timeval tv; |
| struct tm tm; |
| |
| if (gettimeofday(&tv, NULL) == -1) |
| fprintf(stderr, "gettimeofday() failed\n"); |
| |
| if (!localtime_r(&tv.tv_sec, &tm)) |
| fprintf(stderr, "localtime_r() failed\n"); |
| |
| if (strftime(p, sizeof(p), "%FT%T", &tm) == 0) |
| fprintf(stderr, "strftime() returned 0\n"); |
| |
| /* The strftime %z format creates timezone offsets of the form |
| * (+/-)hhmm, whereas the RFC 3339 format is (+/-)hh:mm. So |
| * shift the minutes one step back and insert the semicolon. |
| */ |
| if (strftime(z, sizeof(z), "%z", &tm) == 0) |
| fprintf(stderr, "strftime() returned 0\n"); |
| z[5] = z[4]; |
| z[4] = z[3]; |
| z[3] = ':'; |
| |
| _xstrfmtcat(buf, "%s%s", p, z); |
| } |
| |
| /* |
| * append formatted string with printf-style args to buf, expanding |
| * buf as needed |
| */ |
| void _xstrfmtcat(char **str, const char *fmt, ...) |
| { |
| char *p = NULL; |
| va_list ap; |
| |
| va_start(ap, fmt); |
| _xstrdup_vprintf(&p, fmt, ap); |
| va_end(ap); |
| |
| if (!p) |
| return; |
| |
| /* If str does not exist yet, just give it back p directly */ |
| if (!*str) { |
| *str = p; |
| return; |
| } |
| |
| xstrcat(*str, p); |
| xfree(p); |
| } |
| |
| /* |
| * Append formatted string with printf-style args to str at pos, |
| * expanding buf as needed. pos is updated to the end of the appended |
| * string. |
| * |
| * Meant to be used in loops constructing longer strings that are performance |
| * sensitive, as xstrfmtcat() needs to re-seek to the end of str making the |
| * string construction worse by another O(log(strlen)) factor. |
| */ |
| void _xstrfmtcatat(char **str, char **pos, const char *fmt, ...) |
| { |
| size_t orig_len, append_len; |
| char *p = NULL; |
| va_list ap; |
| |
| va_start(ap, fmt); |
| append_len = _xstrdup_vprintf(&p, fmt, ap); |
| va_end(ap); |
| |
| if (!p) |
| return; |
| |
| /* No string yet to append to, so just return p. */ |
| if (!*str) { |
| *str = p; |
| *pos = p + append_len; |
| return; |
| } |
| |
| if (!*pos) { |
| orig_len = strlen(*str); |
| } else { |
| xassert(*pos >= *str); |
| orig_len = *pos - *str; |
| } |
| |
| _makespace(str, orig_len, append_len); |
| |
| memcpy(*str + orig_len, p, append_len); |
| xfree(p); |
| |
| /* |
| * Update *pos. Cannot happen earlier as _makespace() may have |
| * changed *str to a different address. |
| */ |
| *pos = *str + orig_len + append_len; |
| } |
| |
| /* |
| * append a range of memory from start to end to the string str, |
| * expanding str as needed |
| */ |
| void _xmemcat(char **str, char *start, char *end) |
| { |
| char buf[4096]; |
| size_t len; |
| |
| xassert(end >= start); |
| |
| len = (size_t) end - (size_t) start; |
| |
| if (len == 0) |
| return; |
| |
| if (len > 4095) |
| len = 4095; |
| |
| memcpy(buf, start, len); |
| buf[len] = '\0'; |
| xstrcat(*str, buf); |
| } |
| |
| /* |
| * Replacement for libc basename |
| * path (IN) path possibly containing '/' characters |
| * RETURN last component of path |
| */ |
| char * xbasename(char *path) |
| { |
| char *p; |
| |
| p = strrchr(path , '/'); |
| return (p ? (p + 1) : path); |
| } |
| |
| /* |
| * Slurm's safe implementation of dirname. |
| * |
| * This function returns a malloc'ed string which lets the caller to safely |
| * manipulate it, unlike in dirname() which in some cases returns pointers |
| * to the passed string, and in other cases a new malloc'ed string. dirname() |
| * can also modify the original string so it is preferred to pas a copy of the |
| * input string. |
| * |
| * Besides that, the result is exactly like in dirname(). |
| * |
| * IN path - path to get the dirname from. |
| * RETURN res - string containing the path without the basename. |
| */ |
| char *xdirname(const char *path) |
| { |
| char *res = NULL; |
| char *fname = xstrdup(path); |
| |
| res = xstrdup(dirname(fname)); |
| xfree(fname); |
| |
| return res; |
| } |
| |
| /* |
| * Duplicate a string. |
| * str (IN) string to duplicate |
| * RETURN copy of string |
| */ |
| char *xstrdup(const char *str) |
| { |
| size_t siz; |
| char *result; |
| |
| if (!str) |
| return NULL; |
| |
| siz = strlen(str) + 1; |
| result = xmalloc(siz); |
| |
| /* includes terminating NUL from source string */ |
| (void) memcpy(result, str, siz); |
| |
| return result; |
| } |
| |
| /* |
| * Give me a copy of the string as if it were printf. |
| * fmt (IN) format of string and args if any |
| * RETURN copy of formatted string |
| */ |
| char *xstrdup_printf(const char *fmt, ...) |
| { |
| char *result; |
| va_list ap; |
| |
| va_start(ap, fmt); |
| _xstrdup_vprintf(&result, fmt, ap); |
| va_end(ap); |
| |
| return result; |
| } |
| |
| /* |
| * Duplicate at most "n" characters of a string. |
| * str (IN) string to duplicate |
| * n (IN) |
| * RETURN copy of string |
| */ |
| char * xstrndup(const char *str, size_t n) |
| { |
| size_t siz; |
| char *result; |
| |
| if (str == NULL) |
| return NULL; |
| |
| siz = strnlen(str, n); |
| result = xmalloc(siz + 1); |
| |
| (void) memcpy(result, str, siz); |
| result[siz] = '\0'; |
| |
| return result; |
| } |
| |
| /* |
| ** strtol which only reads 'n' number of chars in the str to get the number |
| */ |
| long int xstrntol(const char *str, char **endptr, size_t n, int base) |
| { |
| long int number = 0; |
| char new_str[n+1], *new_endptr = NULL; |
| |
| memcpy(new_str, str, n); |
| new_str[n] = '\0'; |
| |
| number = strtol(new_str, &new_endptr, base); |
| if (endptr) |
| *endptr = ((char *)str) + (new_endptr - new_str); |
| |
| return number; |
| } |
| |
| /* |
| * Find the first instance of a sub-string "pattern" in the string "str", |
| * and replace it with the string "replacement". |
| * str (IN/OUT) target string (pointer to in case of expansion) |
| * pattern (IN) substring to look for in str |
| * replacement (IN) string with which to replace the "pattern" string |
| * all (IN) replace all or not |
| */ |
| void _xstrsubstitute(char **str, const char *pattern, const char *replacement, |
| const bool all) |
| { |
| int pat_len, rep_len; |
| char *ptr, *end_copy, *pos = NULL; |
| int append_len, pos_offset; |
| |
| if (*str == NULL || pattern == NULL || pattern[0] == '\0') |
| return; |
| |
| pat_len = strlen(pattern); |
| if (replacement == NULL) |
| rep_len = 0; |
| else |
| rep_len = strlen(replacement); |
| |
| append_len = rep_len - pat_len; |
| |
| pos_offset = 0; |
| again: |
| pos = *str + pos_offset; |
| |
| if ((ptr = strstr(pos, pattern)) == NULL) |
| return; |
| |
| /* Get the end after the pattern */ |
| end_copy = xstrdup(ptr + pat_len); |
| |
| pos_offset += ptr - pos; |
| |
| if (rep_len != 0) { |
| /* |
| * If we are making this bigger, make sure we get enough space. |
| */ |
| if (append_len > 0) |
| _makespace(str, -1, append_len); |
| memcpy((*str) + pos_offset, replacement, rep_len); |
| pos_offset += rep_len; |
| } |
| |
| /* add the end of new string */ |
| if (end_copy) { |
| int end_len = strlen(end_copy); |
| memcpy((*str) + pos_offset, end_copy, end_len); |
| |
| /* terminate the string if we are shrinking it */ |
| if (append_len < 0) |
| (*str)[pos_offset + end_len] = '\0'; |
| xfree(end_copy); |
| } |
| |
| if (all) |
| goto again; |
| |
| return; |
| } |
| |
| /* xshort_hostname |
| * Returns an xmalloc'd string containing the hostname |
| * of the local machine. The hostname contains only |
| * the short version of the hostname (e.g. "linux123.foo.bar" |
| * becomes "linux123") |
| * |
| * Returns NULL on error. |
| */ |
| char *xshort_hostname(void) |
| { |
| int error_code; |
| char *dot_ptr, path_name[1024]; |
| |
| error_code = gethostname (path_name, sizeof(path_name)); |
| if (error_code) |
| return NULL; |
| |
| dot_ptr = strchr (path_name, '.'); |
| if (dot_ptr != NULL) |
| dot_ptr[0] = '\0'; |
| |
| return xstrdup(path_name); |
| } |
| |
| /* Returns true if all characters in a string are whitespace characters, |
| * otherwise returns false; |
| */ |
| bool xstring_is_whitespace(const char *str) |
| { |
| int i = 0; |
| |
| while (str[i] != '\0') { |
| if (!isspace((int)str[i])) |
| return false; |
| i++; |
| } |
| |
| return true; |
| } |
| |
| extern bool xstrtolower(char *str) |
| { |
| bool modified = false; |
| |
| if (!str) |
| return modified; |
| |
| for (int j = 0; str[j]; j++) { |
| int c = tolower((int) str[j]); |
| |
| if (c != str[j]) |
| modified = true; |
| str[j] = c; |
| } |
| return modified; |
| } |
| |
| /* safe strchr */ |
| char *xstrchr(const char *s1, int c) |
| { |
| return s1 ? strchr(s1, c) : NULL; |
| } |
| |
| /* safe strrchr */ |
| char *xstrrchr(const char *s1, int c) |
| { |
| return s1 ? strrchr(s1, c) : NULL; |
| } |
| |
| /* safe strcmp */ |
| int xstrcmp(const char *s1, const char *s2) |
| { |
| if (!s1 && !s2) |
| return 0; |
| else if (!s1) |
| return -1; |
| else if (!s2) |
| return 1; |
| else |
| return strcmp(s1, s2); |
| } |
| |
| |
| /* safe strncmp */ |
| int xstrncmp(const char *s1, const char *s2, size_t n) |
| { |
| if (!s1 && !s2) |
| return 0; |
| else if (!s1) |
| return -1; |
| else if (!s2) |
| return 1; |
| else |
| return strncmp(s1, s2, n); |
| } |
| |
| /* safe strcasecmp */ |
| int xstrcasecmp(const char *s1, const char *s2) |
| { |
| if (!s1 && !s2) |
| return 0; |
| else if (!s1) |
| return -1; |
| else if (!s2) |
| return 1; |
| else |
| return strcasecmp(s1, s2); |
| } |
| |
| /* safe strncasecmp */ |
| int xstrncasecmp(const char *s1, const char *s2, size_t n) |
| { |
| if (!s1 && !s2) |
| return 0; |
| else if (!s1) |
| return -1; |
| else if (!s2) |
| return 1; |
| else |
| return strncasecmp(s1, s2, n); |
| } |
| |
| /* safe xstrstr */ |
| char *xstrstr(const char *haystack, const char *needle) |
| { |
| if (!haystack || !needle) |
| return NULL; |
| |
| return strstr(haystack, needle); |
| } |
| |
| char *xstrcasestr(const char *haystack, const char *needle) |
| { |
| int hay_inx, hay_size, need_inx, need_size; |
| char *hay_ptr = (char *) haystack; |
| |
| if (haystack == NULL || needle == NULL) |
| return NULL; |
| |
| hay_size = strlen(haystack); |
| need_size = strlen(needle); |
| |
| for (hay_inx=0; hay_inx<hay_size; hay_inx++) { |
| for (need_inx=0; need_inx<need_size; need_inx++) { |
| if (tolower((int) hay_ptr[need_inx]) != |
| tolower((int) needle [need_inx])) |
| break; /* mismatch */ |
| } |
| |
| if (need_inx == need_size) /* it matched */ |
| return hay_ptr; |
| else /* keep looking */ |
| hay_ptr++; |
| } |
| |
| return NULL; /* no match anywhere in string */ |
| } |
| |
| /* |
| * Give me a copy of the string as if it were printf. |
| * This is stdarg-compatible routine, so vararg-compatible |
| * functions can do va_start() and invoke this function. |
| * |
| * fmt (IN) format of string and args if any |
| * RETURN copy of formatted string |
| */ |
| size_t _xstrdup_vprintf(char **str, const char *fmt, va_list ap) |
| { |
| /* Start out with a size of 100 bytes. */ |
| int n, size = 100; |
| va_list our_ap; |
| char *p = xmalloc(size); |
| |
| while (1) { |
| /* Try to print in the allocated space. */ |
| va_copy(our_ap, ap); |
| n = vsnprintf(p, size, fmt, our_ap); |
| va_end(our_ap); |
| /* If that worked, return the string. */ |
| if (n > -1 && n < size) { |
| *str = p; |
| return n; |
| } |
| /* Else try again with more space. */ |
| if (n > -1) /* glibc 2.1 */ |
| size = n + 1; /* precisely what is needed */ |
| else /* glibc 2.0 */ |
| size *= 2; /* twice the old size */ |
| p = xrealloc(p, size); |
| } |
| /* NOTREACHED */ |
| } |
| |
| extern void xstrtrim(char *string) |
| { |
| char *start, *end, *ptr = string; |
| |
| if (!string || !(*string)) |
| return; |
| |
| /* walk start to the right until we find NULL or a real character */ |
| while (*ptr && isspace(*ptr)) |
| ++ptr; |
| |
| if (!(*ptr)) { |
| /* string is all whitespace */ |
| string[0] = '\0'; |
| return; |
| } |
| |
| /* save start of non-space string */ |
| start = ptr; |
| |
| /* find the end of the string */ |
| while (*ptr) |
| ++ptr; |
| |
| /* save real end of string as it will be overwritten later */ |
| end = ptr; |
| |
| /* walk to the left to find start of ending whitespace */ |
| do { |
| char *sptr = ptr - 1; |
| /* we should never walk past start */ |
| xassert(*sptr); |
| |
| if (!*sptr || sptr <= start || !isspace(*sptr)) |
| break; |
| |
| ptr = sptr; |
| *ptr = '\0'; |
| } while (true); |
| |
| xassert(*ptr == '\0'); |
| xassert(end >= ptr); |
| xassert(start >= string); |
| xassert(start <= end); |
| |
| /* shift it over if necessary */ |
| if (end != start) |
| memmove(string, start, (ptr - start + 1)); |
| } |
| |
| extern char *xstring_bytes2hex(const unsigned char *string, int len, |
| const char *delimiter) |
| { |
| char *hex = NULL, *pos = NULL; |
| |
| if (len <= 0) |
| return NULL; |
| |
| for (int i = 0; i < len; i++) { |
| if (hex && delimiter) |
| xstrfmtcatat(hex, &pos, "%s", delimiter); |
| |
| /* convert each char into equiv hex */ |
| xstrfmtcatat(hex, &pos, "%02x", string[i]); |
| } |
| |
| return hex; |
| } |
| |
| extern char *xstring_bytes2printable(const unsigned char *string, int len, |
| const char replace) |
| { |
| char *str = NULL, *pos = NULL; |
| |
| if (len <= 0) |
| return NULL; |
| |
| for (int i = 0; i < len; i++) { |
| if (isalnum(string[i]) || ispunct(string[i]) || |
| (string[i] == ' ')) |
| xstrfmtcatat(str, &pos, "%c", string[i]); |
| else |
| xstrfmtcatat(str, &pos, "%c", replace); |
| } |
| |
| return str; |
| } |
| |
| extern char *xbase64_from_base64url(const char *in) |
| { |
| char *out; |
| int i; |
| size_t length = strlen(in); |
| |
| /* extra padding in case the padding was stripped off */ |
| out = xmalloc(length + 3); |
| |
| for (i = 0; i < length; i++) { |
| switch (in[i]) { |
| case '-': |
| out[i] = '+'; |
| break; |
| case '_': |
| out[i] = '/'; |
| break; |
| default: |
| out[i] = in[i]; |
| } |
| } |
| |
| /* |
| * Fix padding in case it was trimmed. |
| * String length must be a multiple of 4. |
| */ |
| for (int padding = 4 - (i % 4); padding < 4 && padding; padding--) |
| out[i++] = '='; |
| |
| return out; |
| } |