xref: /linux/tools/perf/util/string.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2a067558eSArnaldo Carvalho de Melo #include "string2.h"
3a067558eSArnaldo Carvalho de Melo #include <linux/kernel.h>
4a067558eSArnaldo Carvalho de Melo #include <linux/string.h>
5a067558eSArnaldo Carvalho de Melo #include <stdlib.h>
686470930SIngo Molnar 
73052ba56SArnaldo Carvalho de Melo #include <linux/ctype.h>
83d689ed6SArnaldo Carvalho de Melo 
96a9fa4e3SArnaldo Carvalho de Melo const char *graph_dotted_line =
106a9fa4e3SArnaldo Carvalho de Melo 	"---------------------------------------------------------------------"
116a9fa4e3SArnaldo Carvalho de Melo 	"---------------------------------------------------------------------"
126a9fa4e3SArnaldo Carvalho de Melo 	"---------------------------------------------------------------------";
136a9fa4e3SArnaldo Carvalho de Melo const char *dots =
146a9fa4e3SArnaldo Carvalho de Melo 	"....................................................................."
156a9fa4e3SArnaldo Carvalho de Melo 	"....................................................................."
166a9fa4e3SArnaldo Carvalho de Melo 	".....................................................................";
176a9fa4e3SArnaldo Carvalho de Melo 
18d2fb8b41SHitoshi Mitake /*
19d2fb8b41SHitoshi Mitake  * perf_atoll()
20d2fb8b41SHitoshi Mitake  * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB")
21d2fb8b41SHitoshi Mitake  * and return its numeric value
22d2fb8b41SHitoshi Mitake  */
perf_atoll(const char * str)23d2fb8b41SHitoshi Mitake s64 perf_atoll(const char *str)
24d2fb8b41SHitoshi Mitake {
258ba7f6c2SAl Viro 	s64 length;
268ba7f6c2SAl Viro 	char *p;
278ba7f6c2SAl Viro 	char c;
28d2fb8b41SHitoshi Mitake 
29d2fb8b41SHitoshi Mitake 	if (!isdigit(str[0]))
30d2fb8b41SHitoshi Mitake 		goto out_err;
31d2fb8b41SHitoshi Mitake 
328ba7f6c2SAl Viro 	length = strtoll(str, &p, 10);
338ba7f6c2SAl Viro 	switch (c = *p++) {
348ba7f6c2SAl Viro 		case 'b': case 'B':
358ba7f6c2SAl Viro 			if (*p)
36d2fb8b41SHitoshi Mitake 				goto out_err;
3794bdd5edSArnaldo Carvalho de Melo 
38f7a858bfSLiam Howlett 			fallthrough;
398ba7f6c2SAl Viro 		case '\0':
408ba7f6c2SAl Viro 			return length;
41d2fb8b41SHitoshi Mitake 		default:
42d2fb8b41SHitoshi Mitake 			goto out_err;
438ba7f6c2SAl Viro 		/* two-letter suffices */
448ba7f6c2SAl Viro 		case 'k': case 'K':
458ba7f6c2SAl Viro 			length <<= 10;
468ba7f6c2SAl Viro 			break;
478ba7f6c2SAl Viro 		case 'm': case 'M':
488ba7f6c2SAl Viro 			length <<= 20;
498ba7f6c2SAl Viro 			break;
508ba7f6c2SAl Viro 		case 'g': case 'G':
518ba7f6c2SAl Viro 			length <<= 30;
528ba7f6c2SAl Viro 			break;
538ba7f6c2SAl Viro 		case 't': case 'T':
548ba7f6c2SAl Viro 			length <<= 40;
55d2fb8b41SHitoshi Mitake 			break;
56d2fb8b41SHitoshi Mitake 	}
578ba7f6c2SAl Viro 	/* we want the cases to match */
588ba7f6c2SAl Viro 	if (islower(c)) {
598ba7f6c2SAl Viro 		if (strcmp(p, "b") != 0)
608ba7f6c2SAl Viro 			goto out_err;
618ba7f6c2SAl Viro 	} else {
628ba7f6c2SAl Viro 		if (strcmp(p, "B") != 0)
638ba7f6c2SAl Viro 			goto out_err;
64d2fb8b41SHitoshi Mitake 	}
658ba7f6c2SAl Viro 	return length;
66d2fb8b41SHitoshi Mitake 
67d2fb8b41SHitoshi Mitake out_err:
688ba7f6c2SAl Viro 	return -1;
69d2fb8b41SHitoshi Mitake }
70e1c01d61SMasami Hiramatsu 
716964cd2cSMasami Hiramatsu /* Character class matching */
__match_charclass(const char * pat,char c,const char ** npat)726964cd2cSMasami Hiramatsu static bool __match_charclass(const char *pat, char c, const char **npat)
736964cd2cSMasami Hiramatsu {
746964cd2cSMasami Hiramatsu 	bool complement = false, ret = true;
756964cd2cSMasami Hiramatsu 
766964cd2cSMasami Hiramatsu 	if (*pat == '!') {
776964cd2cSMasami Hiramatsu 		complement = true;
786964cd2cSMasami Hiramatsu 		pat++;
796964cd2cSMasami Hiramatsu 	}
806964cd2cSMasami Hiramatsu 	if (*pat++ == c)	/* First character is special */
816964cd2cSMasami Hiramatsu 		goto end;
826964cd2cSMasami Hiramatsu 
836964cd2cSMasami Hiramatsu 	while (*pat && *pat != ']') {	/* Matching */
846964cd2cSMasami Hiramatsu 		if (*pat == '-' && *(pat + 1) != ']') {	/* Range */
856964cd2cSMasami Hiramatsu 			if (*(pat - 1) <= c && c <= *(pat + 1))
866964cd2cSMasami Hiramatsu 				goto end;
876964cd2cSMasami Hiramatsu 			if (*(pat - 1) > *(pat + 1))
886964cd2cSMasami Hiramatsu 				goto error;
896964cd2cSMasami Hiramatsu 			pat += 2;
906964cd2cSMasami Hiramatsu 		} else if (*pat++ == c)
916964cd2cSMasami Hiramatsu 			goto end;
926964cd2cSMasami Hiramatsu 	}
936964cd2cSMasami Hiramatsu 	if (!*pat)
946964cd2cSMasami Hiramatsu 		goto error;
956964cd2cSMasami Hiramatsu 	ret = false;
966964cd2cSMasami Hiramatsu 
976964cd2cSMasami Hiramatsu end:
986964cd2cSMasami Hiramatsu 	while (*pat && *pat != ']')	/* Searching closing */
996964cd2cSMasami Hiramatsu 		pat++;
1006964cd2cSMasami Hiramatsu 	if (!*pat)
1016964cd2cSMasami Hiramatsu 		goto error;
1026964cd2cSMasami Hiramatsu 	*npat = pat + 1;
1036964cd2cSMasami Hiramatsu 	return complement ? !ret : ret;
1046964cd2cSMasami Hiramatsu 
1056964cd2cSMasami Hiramatsu error:
1066964cd2cSMasami Hiramatsu 	return false;
1076964cd2cSMasami Hiramatsu }
1086964cd2cSMasami Hiramatsu 
1092a9c8c36SMasami Hiramatsu /* Glob/lazy pattern matching */
__match_glob(const char * str,const char * pat,bool ignore_space,bool case_ins)11038d14f0cSAndi Kleen static bool __match_glob(const char *str, const char *pat, bool ignore_space,
11138d14f0cSAndi Kleen 			bool case_ins)
112bbbb521bSMasami Hiramatsu {
113bbbb521bSMasami Hiramatsu 	while (*str && *pat && *pat != '*') {
1142a9c8c36SMasami Hiramatsu 		if (ignore_space) {
1152a9c8c36SMasami Hiramatsu 			/* Ignore spaces for lazy matching */
1162a9c8c36SMasami Hiramatsu 			if (isspace(*str)) {
1172a9c8c36SMasami Hiramatsu 				str++;
1182a9c8c36SMasami Hiramatsu 				continue;
1192a9c8c36SMasami Hiramatsu 			}
1202a9c8c36SMasami Hiramatsu 			if (isspace(*pat)) {
1212a9c8c36SMasami Hiramatsu 				pat++;
1222a9c8c36SMasami Hiramatsu 				continue;
1232a9c8c36SMasami Hiramatsu 			}
1242a9c8c36SMasami Hiramatsu 		}
1256964cd2cSMasami Hiramatsu 		if (*pat == '?') {	/* Matches any single character */
126bbbb521bSMasami Hiramatsu 			str++;
127bbbb521bSMasami Hiramatsu 			pat++;
1286964cd2cSMasami Hiramatsu 			continue;
1296964cd2cSMasami Hiramatsu 		} else if (*pat == '[')	/* Character classes/Ranges */
1306964cd2cSMasami Hiramatsu 			if (__match_charclass(pat + 1, *str, &pat)) {
1316964cd2cSMasami Hiramatsu 				str++;
1326964cd2cSMasami Hiramatsu 				continue;
133bbbb521bSMasami Hiramatsu 			} else
1346964cd2cSMasami Hiramatsu 				return false;
1356964cd2cSMasami Hiramatsu 		else if (*pat == '\\') /* Escaped char match as normal char */
1366964cd2cSMasami Hiramatsu 			pat++;
13738d14f0cSAndi Kleen 		if (case_ins) {
13838d14f0cSAndi Kleen 			if (tolower(*str) != tolower(*pat))
139bbbb521bSMasami Hiramatsu 				return false;
14038d14f0cSAndi Kleen 		} else if (*str != *pat)
14138d14f0cSAndi Kleen 			return false;
14238d14f0cSAndi Kleen 		str++;
14338d14f0cSAndi Kleen 		pat++;
144bbbb521bSMasami Hiramatsu 	}
145bbbb521bSMasami Hiramatsu 	/* Check wild card */
146bbbb521bSMasami Hiramatsu 	if (*pat == '*') {
147bbbb521bSMasami Hiramatsu 		while (*pat == '*')
148bbbb521bSMasami Hiramatsu 			pat++;
149bbbb521bSMasami Hiramatsu 		if (!*pat)	/* Tail wild card matches all */
150bbbb521bSMasami Hiramatsu 			return true;
151bbbb521bSMasami Hiramatsu 		while (*str)
15238d14f0cSAndi Kleen 			if (__match_glob(str++, pat, ignore_space, case_ins))
153bbbb521bSMasami Hiramatsu 				return true;
154bbbb521bSMasami Hiramatsu 	}
155bbbb521bSMasami Hiramatsu 	return !*str && !*pat;
156bbbb521bSMasami Hiramatsu }
157bbbb521bSMasami Hiramatsu 
1582a9c8c36SMasami Hiramatsu /**
1592a9c8c36SMasami Hiramatsu  * strglobmatch - glob expression pattern matching
1602a9c8c36SMasami Hiramatsu  * @str: the target string to match
1612a9c8c36SMasami Hiramatsu  * @pat: the pattern string to match
1622a9c8c36SMasami Hiramatsu  *
1632a9c8c36SMasami Hiramatsu  * This returns true if the @str matches @pat. @pat can includes wildcards
1642a9c8c36SMasami Hiramatsu  * ('*','?') and character classes ([CHARS], complementation and ranges are
1652a9c8c36SMasami Hiramatsu  * also supported). Also, this supports escape character ('\') to use special
1662a9c8c36SMasami Hiramatsu  * characters as normal character.
1672a9c8c36SMasami Hiramatsu  *
1682a9c8c36SMasami Hiramatsu  * Note: if @pat syntax is broken, this always returns false.
1692a9c8c36SMasami Hiramatsu  */
strglobmatch(const char * str,const char * pat)1702a9c8c36SMasami Hiramatsu bool strglobmatch(const char *str, const char *pat)
1712a9c8c36SMasami Hiramatsu {
17238d14f0cSAndi Kleen 	return __match_glob(str, pat, false, false);
17338d14f0cSAndi Kleen }
17438d14f0cSAndi Kleen 
strglobmatch_nocase(const char * str,const char * pat)17538d14f0cSAndi Kleen bool strglobmatch_nocase(const char *str, const char *pat)
17638d14f0cSAndi Kleen {
17738d14f0cSAndi Kleen 	return __match_glob(str, pat, false, true);
1782a9c8c36SMasami Hiramatsu }
1792a9c8c36SMasami Hiramatsu 
1802a9c8c36SMasami Hiramatsu /**
1812a9c8c36SMasami Hiramatsu  * strlazymatch - matching pattern strings lazily with glob pattern
1822a9c8c36SMasami Hiramatsu  * @str: the target string to match
1832a9c8c36SMasami Hiramatsu  * @pat: the pattern string to match
1842a9c8c36SMasami Hiramatsu  *
1852a9c8c36SMasami Hiramatsu  * This is similar to strglobmatch, except this ignores spaces in
1862a9c8c36SMasami Hiramatsu  * the target string.
1872a9c8c36SMasami Hiramatsu  */
strlazymatch(const char * str,const char * pat)1882a9c8c36SMasami Hiramatsu bool strlazymatch(const char *str, const char *pat)
1892a9c8c36SMasami Hiramatsu {
19038d14f0cSAndi Kleen 	return __match_glob(str, pat, true, false);
1912a9c8c36SMasami Hiramatsu }
192bad03ae4SMasami Hiramatsu 
193bad03ae4SMasami Hiramatsu /**
194bad03ae4SMasami Hiramatsu  * strtailcmp - Compare the tail of two strings
195bad03ae4SMasami Hiramatsu  * @s1: 1st string to be compared
196bad03ae4SMasami Hiramatsu  * @s2: 2nd string to be compared
197bad03ae4SMasami Hiramatsu  *
198bad03ae4SMasami Hiramatsu  * Return 0 if whole of either string is same as another's tail part.
199bad03ae4SMasami Hiramatsu  */
strtailcmp(const char * s1,const char * s2)200bad03ae4SMasami Hiramatsu int strtailcmp(const char *s1, const char *s2)
201bad03ae4SMasami Hiramatsu {
202bad03ae4SMasami Hiramatsu 	int i1 = strlen(s1);
203bad03ae4SMasami Hiramatsu 	int i2 = strlen(s2);
204bad03ae4SMasami Hiramatsu 	while (--i1 >= 0 && --i2 >= 0) {
205bad03ae4SMasami Hiramatsu 		if (s1[i1] != s2[i2])
206bad03ae4SMasami Hiramatsu 			return s1[i1] - s2[i2];
207bad03ae4SMasami Hiramatsu 	}
208bad03ae4SMasami Hiramatsu 	return 0;
209bad03ae4SMasami Hiramatsu }
210bad03ae4SMasami Hiramatsu 
asprintf_expr_inout_ints(const char * var,bool in,size_t nints,int * ints)21193ec4ce7SArnaldo Carvalho de Melo char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints)
21293ec4ce7SArnaldo Carvalho de Melo {
21393ec4ce7SArnaldo Carvalho de Melo 	/*
21493ec4ce7SArnaldo Carvalho de Melo 	 * FIXME: replace this with an expression using log10() when we
21593ec4ce7SArnaldo Carvalho de Melo 	 * find a suitable implementation, maybe the one in the dvb drivers...
21693ec4ce7SArnaldo Carvalho de Melo 	 *
21793ec4ce7SArnaldo Carvalho de Melo 	 * "%s == %d || " = log10(MAXINT) * 2 + 8 chars for the operators
21893ec4ce7SArnaldo Carvalho de Melo 	 */
21993ec4ce7SArnaldo Carvalho de Melo 	size_t size = nints * 28 + 1; /* \0 */
22093ec4ce7SArnaldo Carvalho de Melo 	size_t i, printed = 0;
22193ec4ce7SArnaldo Carvalho de Melo 	char *expr = malloc(size);
22293ec4ce7SArnaldo Carvalho de Melo 
22393ec4ce7SArnaldo Carvalho de Melo 	if (expr) {
22493ec4ce7SArnaldo Carvalho de Melo 		const char *or_and = "||", *eq_neq = "==";
22593ec4ce7SArnaldo Carvalho de Melo 		char *e = expr;
22693ec4ce7SArnaldo Carvalho de Melo 
22793ec4ce7SArnaldo Carvalho de Melo 		if (!in) {
22893ec4ce7SArnaldo Carvalho de Melo 			or_and = "&&";
22993ec4ce7SArnaldo Carvalho de Melo 			eq_neq = "!=";
23093ec4ce7SArnaldo Carvalho de Melo 		}
23193ec4ce7SArnaldo Carvalho de Melo 
23293ec4ce7SArnaldo Carvalho de Melo 		for (i = 0; i < nints; ++i) {
23393ec4ce7SArnaldo Carvalho de Melo 			if (printed == size)
23493ec4ce7SArnaldo Carvalho de Melo 				goto out_err_overflow;
23593ec4ce7SArnaldo Carvalho de Melo 
23693ec4ce7SArnaldo Carvalho de Melo 			if (i > 0)
237a067558eSArnaldo Carvalho de Melo 				printed += scnprintf(e + printed, size - printed, " %s ", or_and);
23893ec4ce7SArnaldo Carvalho de Melo 			printed += scnprintf(e + printed, size - printed,
23993ec4ce7SArnaldo Carvalho de Melo 					     "%s %s %d", var, eq_neq, ints[i]);
24093ec4ce7SArnaldo Carvalho de Melo 		}
24193ec4ce7SArnaldo Carvalho de Melo 	}
24293ec4ce7SArnaldo Carvalho de Melo 
24393ec4ce7SArnaldo Carvalho de Melo 	return expr;
24493ec4ce7SArnaldo Carvalho de Melo 
24593ec4ce7SArnaldo Carvalho de Melo out_err_overflow:
24693ec4ce7SArnaldo Carvalho de Melo 	free(expr);
24793ec4ce7SArnaldo Carvalho de Melo 	return NULL;
24893ec4ce7SArnaldo Carvalho de Melo }
2491e9f9e8aSMasami Hiramatsu 
2501e9f9e8aSMasami Hiramatsu /* Like strpbrk(), but not break if it is right after a backslash (escaped) */
strpbrk_esc(char * str,const char * stopset)2511e9f9e8aSMasami Hiramatsu char *strpbrk_esc(char *str, const char *stopset)
2521e9f9e8aSMasami Hiramatsu {
2531e9f9e8aSMasami Hiramatsu 	char *ptr;
2541e9f9e8aSMasami Hiramatsu 
2551e9f9e8aSMasami Hiramatsu 	do {
2561e9f9e8aSMasami Hiramatsu 		ptr = strpbrk(str, stopset);
2571e9f9e8aSMasami Hiramatsu 		if (ptr == str ||
2581e9f9e8aSMasami Hiramatsu 		    (ptr == str + 1 && *(ptr - 1) != '\\'))
2591e9f9e8aSMasami Hiramatsu 			break;
2601e9f9e8aSMasami Hiramatsu 		str = ptr + 1;
2611e9f9e8aSMasami Hiramatsu 	} while (ptr && *(ptr - 1) == '\\' && *(ptr - 2) != '\\');
2621e9f9e8aSMasami Hiramatsu 
2631e9f9e8aSMasami Hiramatsu 	return ptr;
2641e9f9e8aSMasami Hiramatsu }
2651e9f9e8aSMasami Hiramatsu 
2661e9f9e8aSMasami Hiramatsu /* Like strdup, but do not copy a single backslash */
strdup_esc(const char * str)2671e9f9e8aSMasami Hiramatsu char *strdup_esc(const char *str)
2681e9f9e8aSMasami Hiramatsu {
2691e9f9e8aSMasami Hiramatsu 	char *s, *d, *p, *ret = strdup(str);
2701e9f9e8aSMasami Hiramatsu 
2711e9f9e8aSMasami Hiramatsu 	if (!ret)
2721e9f9e8aSMasami Hiramatsu 		return NULL;
2731e9f9e8aSMasami Hiramatsu 
2741e9f9e8aSMasami Hiramatsu 	d = strchr(ret, '\\');
2751e9f9e8aSMasami Hiramatsu 	if (!d)
2761e9f9e8aSMasami Hiramatsu 		return ret;
2771e9f9e8aSMasami Hiramatsu 
2781e9f9e8aSMasami Hiramatsu 	s = d + 1;
2791e9f9e8aSMasami Hiramatsu 	do {
2801e9f9e8aSMasami Hiramatsu 		if (*s == '\0') {
2811e9f9e8aSMasami Hiramatsu 			*d = '\0';
2821e9f9e8aSMasami Hiramatsu 			break;
2831e9f9e8aSMasami Hiramatsu 		}
2841e9f9e8aSMasami Hiramatsu 		p = strchr(s + 1, '\\');
2851e9f9e8aSMasami Hiramatsu 		if (p) {
2861e9f9e8aSMasami Hiramatsu 			memmove(d, s, p - s);
2871e9f9e8aSMasami Hiramatsu 			d += p - s;
2881e9f9e8aSMasami Hiramatsu 			s = p + 1;
2891e9f9e8aSMasami Hiramatsu 		} else
2901e9f9e8aSMasami Hiramatsu 			memmove(d, s, strlen(s) + 1);
2911e9f9e8aSMasami Hiramatsu 	} while (p);
2921e9f9e8aSMasami Hiramatsu 
2931e9f9e8aSMasami Hiramatsu 	return ret;
2941e9f9e8aSMasami Hiramatsu }
295cef7af25SFabian Hemmer 
hex(char c)296cef7af25SFabian Hemmer unsigned int hex(char c)
297cef7af25SFabian Hemmer {
298cef7af25SFabian Hemmer 	if (c >= '0' && c <= '9')
299cef7af25SFabian Hemmer 		return c - '0';
300cef7af25SFabian Hemmer 	if (c >= 'a' && c <= 'f')
301cef7af25SFabian Hemmer 		return c - 'a' + 10;
302cef7af25SFabian Hemmer 	return c - 'A' + 10;
303cef7af25SFabian Hemmer }
304*8a55c1e2SJames Clark 
305*8a55c1e2SJames Clark /*
306*8a55c1e2SJames Clark  * Replace all occurrences of character 'needle' in string 'haystack' with
307*8a55c1e2SJames Clark  * string 'replace'
308*8a55c1e2SJames Clark  *
309*8a55c1e2SJames Clark  * The new string could be longer so a new string is returned which must be
310*8a55c1e2SJames Clark  * freed.
311*8a55c1e2SJames Clark  */
strreplace_chars(char needle,const char * haystack,const char * replace)312*8a55c1e2SJames Clark char *strreplace_chars(char needle, const char *haystack, const char *replace)
313*8a55c1e2SJames Clark {
314*8a55c1e2SJames Clark 	int replace_len = strlen(replace);
315*8a55c1e2SJames Clark 	char *new_s, *to;
316*8a55c1e2SJames Clark 	const char *loc = strchr(haystack, needle);
317*8a55c1e2SJames Clark 	const char *from = haystack;
318*8a55c1e2SJames Clark 	int num = 0;
319*8a55c1e2SJames Clark 
320*8a55c1e2SJames Clark 	/* Count occurrences */
321*8a55c1e2SJames Clark 	while (loc) {
322*8a55c1e2SJames Clark 		loc = strchr(loc + 1, needle);
323*8a55c1e2SJames Clark 		num++;
324*8a55c1e2SJames Clark 	}
325*8a55c1e2SJames Clark 
326*8a55c1e2SJames Clark 	/* Allocate enough space for replacements and reset first location */
327*8a55c1e2SJames Clark 	new_s = malloc(strlen(haystack) + (num * (replace_len - 1) + 1));
328*8a55c1e2SJames Clark 	if (!new_s)
329*8a55c1e2SJames Clark 		return NULL;
330*8a55c1e2SJames Clark 	loc = strchr(haystack, needle);
331*8a55c1e2SJames Clark 	to = new_s;
332*8a55c1e2SJames Clark 
333*8a55c1e2SJames Clark 	while (loc) {
334*8a55c1e2SJames Clark 		/* Copy original string up to found char and update positions */
335*8a55c1e2SJames Clark 		memcpy(to, from, 1 + loc - from);
336*8a55c1e2SJames Clark 		to += loc - from;
337*8a55c1e2SJames Clark 		from = loc + 1;
338*8a55c1e2SJames Clark 
339*8a55c1e2SJames Clark 		/* Copy replacement string and update positions */
340*8a55c1e2SJames Clark 		memcpy(to, replace, replace_len);
341*8a55c1e2SJames Clark 		to += replace_len;
342*8a55c1e2SJames Clark 
343*8a55c1e2SJames Clark 		/* needle next occurrence or end of string */
344*8a55c1e2SJames Clark 		loc = strchr(from, needle);
345*8a55c1e2SJames Clark 	}
346*8a55c1e2SJames Clark 
347*8a55c1e2SJames Clark 	/* Copy any remaining chars + null */
348*8a55c1e2SJames Clark 	strcpy(to, from);
349*8a55c1e2SJames Clark 
350*8a55c1e2SJames Clark 	return new_s;
351*8a55c1e2SJames Clark }
352