blob: 765ed79285a148d57fff57503471fdbf7b8911da [file] [log] [blame]
/*****************************************************************************\
* 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.
*
* UCRL-CODE-226842.
*
* This file is part of SLURM, a resource management program.
* For details, see <http://www.llnl.gov/linux/slurm/>.
*
* 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.
\*****************************************************************************/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef WITH_PTHREADS
# include <pthread.h>
#endif
#include <stdarg.h>
#include <ctype.h>
#include <slurm/slurm_errno.h>
#include "src/common/macros.h"
#include "src/common/strlcpy.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(_xstrcatchar, slurm_xstrcatchar);
strong_alias(_xslurm_strerrorcat, slurm_xslurm_strerrorcat);
strong_alias(_xstrftimecat, slurm_xstrftimecat);
strong_alias(_xstrfmtcat, slurm_xstrfmtcat);
strong_alias(_xmemcat, slurm_xmemcat);
strong_alias(xstrdup, slurm_xstrdup);
strong_alias(xstrndup, slurm_xstrndup);
strong_alias(xbasename, slurm_xbasename);
strong_alias(_xstrsubstitute, slurm_xstrsubstitute);
strong_alias(xshort_hostname, slurm_xshort_hostname);
strong_alias(xstring_is_whitespace, slurm_xstring_is_whitespace);
/*
* Ensure that a string has enough space to add 'needed' characters.
* If the string is uninitialized, it should be NULL.
*/
static void makespace(char **str, int needed)
{
int used;
if (*str == NULL)
*str = xmalloc(needed + 1);
else {
used = strlen(*str) + 1;
while (used + needed > xsize(*str)) {
int newsize = xsize(*str) + XFGETS_CHUNKSIZE;
int actualsize;
xrealloc(*str, newsize);
actualsize = xsize(*str);
xassert(actualsize == newsize);
}
}
}
/*
* 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, strlen(str2));
strcat(*str1, str2);
}
/*
* append one char to str and null terminate
*/
static void strcatchar(char *str, char c)
{
int len = strlen(str);
str[len++] = c;
str[len] = '\0';
}
/*
* Add a character to str, expanding str1 as needed.
* str1 (IN/OUT) target string (pointer to in case of expansion)
* size (IN/OUT) size of str1 (pointer to in case of expansion)
* c (IN) character to add
*/
void _xstrcatchar(char **str, char c)
{
makespace(str, 1);
strcatchar(*str, c);
}
/*
* concatenate slurm_strerror(errno) onto string in buf, expand buf as needed
*
*/
void _xslurm_strerrorcat(char **buf)
{
char *err = slurm_strerror(errno);
xstrcat(*buf, err);
}
/*
* 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";
#ifdef DISABLE_LOCALTIME
static int disabled=0;
if (!buf) disabled=1;
if (disabled) return;
#endif
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 formatted string with printf-style args to buf, expanding
* buf as needed
*/
int _xstrfmtcat(char **str, const char *fmt, ...)
{
/* Start out with a size of 100 bytes. */
int n, size = 100;
char *p = NULL;
va_list ap;
if((p = xmalloc(size)) == NULL)
return 0;
while(1) {
/* Try to print in the allocated space. */
va_start(ap, fmt);
n = vsnprintf(p, size, fmt, ap);
va_end (ap);
/* If that worked, return the string. */
if (n > -1 && n < size)
break;
/* 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 */
if ((p = xrealloc(p, size)) == NULL)
return 0;
}
xstrcat(*str, p);
xfree(p);
return n;
}
/*
* 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);
}
/*
* Duplicate a string.
* str (IN) string to duplicate
* RETURN copy of string
*/
char * xstrdup(const char *str)
{
size_t siz,
rsiz;
char *result;
if (str == NULL) {
return NULL;
}
siz = strlen(str) + 1;
result = (char *)xmalloc(siz);
rsiz = strlcpy(result, str, siz);
xassert(rsiz == siz-1);
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,
rsiz;
char *result;
if (str == NULL)
return NULL;
siz = strlen(str);
if (n < siz)
siz = n;
siz++;
result = (char *)xmalloc(siz);
rsiz = strlcpy(result, str, siz);
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 = xstrndup(str, n);
if(!new_str)
goto end_it;
number = strtol(new_str, endptr, base);
xfree(new_str);
end_it:
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
*/
void _xstrsubstitute(char **str, const char *pattern, const char *replacement)
{
int pat_len, rep_len;
char *ptr, *end_copy;
int pat_offset;
if (*str == NULL || pattern == NULL || pattern[0] == '\0')
return;
if ((ptr = strstr(*str, pattern)) == NULL)
return;
pat_offset = ptr - (*str);
pat_len = strlen(pattern);
if (replacement == NULL)
rep_len = 0;
else
rep_len = strlen(replacement);
end_copy = xstrdup(ptr + pat_len);
if (rep_len != 0) {
makespace(str, rep_len-pat_len);
strcpy((*str)+pat_offset, replacement);
}
strcpy((*str)+pat_offset+rep_len, end_copy);
xfree(end_copy);
}
/* 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 = path_name + strlen(path_name);
else
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, len;
len = strlen(str);
for (i = 0; i < len; i++) {
if (!isspace(str[i])) {
return false;
}
}
return true;
}