190792Sgshapiro/*
2261194Sgshapiro * Copyright (c) 1999-2002 Proofpoint, Inc. and its suppliers.
390792Sgshapiro *	All rights reserved.
490792Sgshapiro *
590792Sgshapiro * By using this file, you agree to the terms and conditions set
690792Sgshapiro * forth in the LICENSE file which can be found at the top level of
790792Sgshapiro * the sendmail distribution.
890792Sgshapiro *
990792Sgshapiro */
1090792Sgshapiro
1190792Sgshapiro#include <sm/gen.h>
12266527SgshapiroSM_RCSID("@(#)$Id: strl.c,v 1.32 2013-11-22 20:51:43 ca Exp $")
1390792Sgshapiro#include <sm/config.h>
1490792Sgshapiro#include <sm/string.h>
1590792Sgshapiro
1690792Sgshapiro/*
1790792Sgshapiro**  Notice: this file is used by libmilter. Please try to avoid
1890792Sgshapiro**	using libsm specific functions.
1990792Sgshapiro*/
2090792Sgshapiro
2190792Sgshapiro/*
2290792Sgshapiro**  XXX the type of the length parameter has been changed
2390792Sgshapiro**  from size_t to ssize_t to avoid theoretical problems with negative
2490792Sgshapiro**  numbers passed into these functions.
2590792Sgshapiro**  The real solution to this problem is to make sure that this doesn't
2690792Sgshapiro**  happen, but for now we'll use this workaround.
2790792Sgshapiro*/
2890792Sgshapiro
2990792Sgshapiro/*
3090792Sgshapiro**  SM_STRLCPY -- size bounded string copy
3190792Sgshapiro**
3290792Sgshapiro**	This is a bounds-checking variant of strcpy.
3390792Sgshapiro**	If size > 0, copy up to size-1 characters from the nul terminated
3490792Sgshapiro**	string src to dst, nul terminating the result.  If size == 0,
3590792Sgshapiro**	the dst buffer is not modified.
3690792Sgshapiro**	Additional note: this function has been "tuned" to run fast and tested
3790792Sgshapiro**	as such (versus versions in some OS's libc).
3890792Sgshapiro**
3990792Sgshapiro**	The result is strlen(src).  You can detect truncation (not all
4090792Sgshapiro**	of the characters in the source string were copied) using the
4190792Sgshapiro**	following idiom:
4290792Sgshapiro**
4390792Sgshapiro**		char *s, buf[BUFSIZ];
4490792Sgshapiro**		...
4590792Sgshapiro**		if (sm_strlcpy(buf, s, sizeof(buf)) >= sizeof(buf))
4690792Sgshapiro**			goto overflow;
4790792Sgshapiro**
4890792Sgshapiro**	Parameters:
4990792Sgshapiro**		dst -- destination buffer
5090792Sgshapiro**		src -- source string
5190792Sgshapiro**		size -- size of destination buffer
5290792Sgshapiro**
5390792Sgshapiro**	Returns:
5490792Sgshapiro**		strlen(src)
5590792Sgshapiro*/
5690792Sgshapiro
5790792Sgshapirosize_t
5890792Sgshapirosm_strlcpy(dst, src, size)
5990792Sgshapiro	register char *dst;
6090792Sgshapiro	register const char *src;
6190792Sgshapiro	ssize_t size;
6290792Sgshapiro{
6390792Sgshapiro	register ssize_t i;
6490792Sgshapiro
6590792Sgshapiro	if (size-- <= 0)
6690792Sgshapiro		return strlen(src);
6790792Sgshapiro	for (i = 0; i < size && (dst[i] = src[i]) != 0; i++)
6890792Sgshapiro		continue;
6990792Sgshapiro	dst[i] = '\0';
7090792Sgshapiro	if (src[i] == '\0')
7190792Sgshapiro		return i;
7290792Sgshapiro	else
7390792Sgshapiro		return i + strlen(src + i);
7490792Sgshapiro}
7590792Sgshapiro
7690792Sgshapiro/*
7790792Sgshapiro**  SM_STRLCAT -- size bounded string concatenation
7890792Sgshapiro**
7990792Sgshapiro**	This is a bounds-checking variant of strcat.
8090792Sgshapiro**	If strlen(dst) < size, then append at most size - strlen(dst) - 1
8190792Sgshapiro**	characters from the source string to the destination string,
8290792Sgshapiro**	nul terminating the result.  Otherwise, dst is not modified.
8390792Sgshapiro**
8490792Sgshapiro**	The result is the initial length of dst + the length of src.
8590792Sgshapiro**	You can detect overflow (not all of the characters in the
8690792Sgshapiro**	source string were copied) using the following idiom:
8790792Sgshapiro**
8890792Sgshapiro**		char *s, buf[BUFSIZ];
8990792Sgshapiro**		...
9090792Sgshapiro**		if (sm_strlcat(buf, s, sizeof(buf)) >= sizeof(buf))
9190792Sgshapiro**			goto overflow;
9290792Sgshapiro**
9390792Sgshapiro**	Parameters:
9490792Sgshapiro**		dst -- nul-terminated destination string buffer
9590792Sgshapiro**		src -- nul-terminated source string
9690792Sgshapiro**		size -- size of destination buffer
9790792Sgshapiro**
9890792Sgshapiro**	Returns:
9990792Sgshapiro**		total length of the string tried to create
10090792Sgshapiro**		(= initial length of dst + length of src)
10190792Sgshapiro*/
10290792Sgshapiro
10390792Sgshapirosize_t
10490792Sgshapirosm_strlcat(dst, src, size)
10590792Sgshapiro	register char *dst;
10690792Sgshapiro	register const char *src;
10790792Sgshapiro	ssize_t size;
10890792Sgshapiro{
10990792Sgshapiro	register ssize_t i, j, o;
11090792Sgshapiro
11190792Sgshapiro	o = strlen(dst);
11290792Sgshapiro	if (size < o + 1)
11390792Sgshapiro		return o + strlen(src);
11490792Sgshapiro	size -= o + 1;
11590792Sgshapiro	for (i = 0, j = o; i < size && (dst[j] = src[i]) != 0; i++, j++)
11690792Sgshapiro		continue;
11790792Sgshapiro	dst[j] = '\0';
11890792Sgshapiro	if (src[i] == '\0')
11990792Sgshapiro		return j;
12090792Sgshapiro	else
12190792Sgshapiro		return j + strlen(src + i);
12290792Sgshapiro}
12390792Sgshapiro/*
12490792Sgshapiro**  SM_STRLCAT2 -- append two strings to dst obeying length and
12590792Sgshapiro**		'\0' terminate it
12690792Sgshapiro**
12790792Sgshapiro**		strlcat2 will append at most len - strlen(dst) - 1 chars.
12890792Sgshapiro**		terminates with '\0' if len > 0
12990792Sgshapiro**		dst = dst "+" src1 "+" src2
13090792Sgshapiro**		use this instead of sm_strlcat(dst,src1); sm_strlcat(dst,src2);
13190792Sgshapiro**		for better speed.
13290792Sgshapiro**
13390792Sgshapiro**	Parameters:
13490792Sgshapiro**		dst -- "destination" string.
13590792Sgshapiro**		src1 -- "from" string 1.
13690792Sgshapiro**		src2 -- "from" string 2.
13790792Sgshapiro**		len -- max. length of "destination" string.
13890792Sgshapiro**
13990792Sgshapiro**	Returns:
14090792Sgshapiro**		total length of the string tried to create
14190792Sgshapiro**		(= initial length of dst + length of src)
14290792Sgshapiro**		if this is greater than len then an overflow would have
14390792Sgshapiro**		occurred.
14490792Sgshapiro**
14590792Sgshapiro*/
14690792Sgshapiro
14790792Sgshapirosize_t
14890792Sgshapirosm_strlcat2(dst, src1, src2, len)
14990792Sgshapiro	register char *dst;
15090792Sgshapiro	register const char *src1;
15190792Sgshapiro	register const char *src2;
15290792Sgshapiro	ssize_t len;
15390792Sgshapiro{
15490792Sgshapiro	register ssize_t i, j, o;
15590792Sgshapiro
15690792Sgshapiro	/* current size of dst */
15790792Sgshapiro	o = strlen(dst);
15890792Sgshapiro
15990792Sgshapiro	/* max. size is less than current? */
16090792Sgshapiro	if (len < o + 1)
16190792Sgshapiro		return o + strlen(src1) + strlen(src2);
16290792Sgshapiro
16390792Sgshapiro	len -= o + 1;	/* space left in dst */
16490792Sgshapiro
16590792Sgshapiro	/* copy the first string; i: index in src1; j: index in dst */
16690792Sgshapiro	for (i = 0, j = o; i < len && (dst[j] = src1[i]) != 0; i++, j++)
16790792Sgshapiro		continue;
16890792Sgshapiro
16990792Sgshapiro	/* src1: end reached? */
17090792Sgshapiro	if (src1[i] != '\0')
17190792Sgshapiro	{
17290792Sgshapiro		/* no: terminate dst; there is space since i < len */
17390792Sgshapiro		dst[j] = '\0';
17490792Sgshapiro		return j + strlen(src1 + i) + strlen(src2);
17590792Sgshapiro	}
17690792Sgshapiro
17790792Sgshapiro	len -= i;	/* space left in dst */
17890792Sgshapiro
17990792Sgshapiro	/* copy the second string; i: index in src2; j: index in dst */
18090792Sgshapiro	for (i = 0; i < len && (dst[j] = src2[i]) != 0; i++, j++)
18190792Sgshapiro		continue;
18290792Sgshapiro	dst[j] = '\0';	/* terminate dst; there is space since i < len */
18390792Sgshapiro	if (src2[i] == '\0')
18490792Sgshapiro		return j;
18590792Sgshapiro	else
18690792Sgshapiro		return j + strlen(src2 + i);
18790792Sgshapiro}
18890792Sgshapiro
18990792Sgshapiro/*
19090792Sgshapiro**  SM_STRLCPYN -- concatenate n strings and assign the result to dst
19190792Sgshapiro**		while obeying length and '\0' terminate it
19290792Sgshapiro**
19390792Sgshapiro**		dst = src1 "+" src2 "+" ...
19490792Sgshapiro**		use this instead of sm_snprintf() for string values
19590792Sgshapiro**		and repeated sm_strlc*() calls for better speed.
19690792Sgshapiro**
19790792Sgshapiro**	Parameters:
19890792Sgshapiro**		dst -- "destination" string.
19990792Sgshapiro**		len -- max. length of "destination" string.
20090792Sgshapiro**		n -- number of strings
20190792Sgshapiro**		strings...
20290792Sgshapiro**
20390792Sgshapiro**	Returns:
20490792Sgshapiro**		total length of the string tried to create
20590792Sgshapiro**		(= initial length of dst + length of src)
20690792Sgshapiro**		if this is greater than len then an overflow would have
20790792Sgshapiro**		occurred.
20890792Sgshapiro*/
20990792Sgshapiro
21090792Sgshapirosize_t
21190792Sgshapiro#ifdef __STDC__
21290792Sgshapirosm_strlcpyn(char *dst, ssize_t len, int n, ...)
21390792Sgshapiro#else /* __STDC__ */
21490792Sgshapirosm_strlcpyn(dst, len, n, va_alist)
21590792Sgshapiro	register char *dst;
21690792Sgshapiro	ssize_t len;
21790792Sgshapiro	int n;
21890792Sgshapiro	va_dcl
21990792Sgshapiro#endif /* __STDC__ */
22090792Sgshapiro{
22190792Sgshapiro	register ssize_t i, j;
22290792Sgshapiro	char *str;
22390792Sgshapiro	SM_VA_LOCAL_DECL
22490792Sgshapiro
22590792Sgshapiro	SM_VA_START(ap, n);
22690792Sgshapiro
22790792Sgshapiro	if (len-- <= 0) /* This allows space for the terminating '\0' */
22890792Sgshapiro	{
22990792Sgshapiro		i = 0;
23090792Sgshapiro		while (n-- > 0)
23190792Sgshapiro			i += strlen(SM_VA_ARG(ap, char *));
23294334Sgshapiro		SM_VA_END(ap);
23390792Sgshapiro		return i;
23490792Sgshapiro	}
23590792Sgshapiro
23690792Sgshapiro	j = 0;	/* index in dst */
23790792Sgshapiro
23890792Sgshapiro	/* loop through all source strings */
23990792Sgshapiro	while (n-- > 0)
24090792Sgshapiro	{
24190792Sgshapiro		str = SM_VA_ARG(ap, char *);
24290792Sgshapiro
24390792Sgshapiro		/* copy string; i: index in str; j: index in dst */
24490792Sgshapiro		for (i = 0; j < len && (dst[j] = str[i]) != 0; i++, j++)
24590792Sgshapiro			continue;
24690792Sgshapiro
24790792Sgshapiro		/* str: end reached? */
24890792Sgshapiro		if (str[i] != '\0')
24990792Sgshapiro		{
25090792Sgshapiro			/* no: terminate dst; there is space since j < len */
25190792Sgshapiro			dst[j] = '\0';
25290792Sgshapiro			j += strlen(str + i);
25390792Sgshapiro			while (n-- > 0)
25490792Sgshapiro				j += strlen(SM_VA_ARG(ap, char *));
25594334Sgshapiro			SM_VA_END(ap);
25690792Sgshapiro			return j;
25790792Sgshapiro		}
25890792Sgshapiro	}
25994334Sgshapiro	SM_VA_END(ap);
26090792Sgshapiro
26190792Sgshapiro	dst[j] = '\0';	/* terminate dst; there is space since j < len */
26290792Sgshapiro	return j;
26390792Sgshapiro}
26490792Sgshapiro
26590792Sgshapiro#if 0
26690792Sgshapiro/*
26790792Sgshapiro**  SM_STRLAPP -- append string if it fits into buffer.
26890792Sgshapiro**
26990792Sgshapiro**	If size > 0, copy up to size-1 characters from the nul terminated
27090792Sgshapiro**	string src to dst, nul terminating the result.  If size == 0,
27190792Sgshapiro**	the dst buffer is not modified.
27290792Sgshapiro**
27390792Sgshapiro**	This routine is useful for appending strings in a loop, e.g, instead of
27490792Sgshapiro**	s = buf;
27590792Sgshapiro**	for (ptr, ptr != NULL, ptr = next->ptr)
27690792Sgshapiro**	{
27790792Sgshapiro**		(void) sm_strlcpy(s, ptr->string, sizeof buf - (s - buf));
27890792Sgshapiro**		s += strlen(s);
27990792Sgshapiro**	}
28090792Sgshapiro**	replace the loop body with:
28190792Sgshapiro**		if (!sm_strlapp(*s, ptr->string, sizeof buf - (s - buf)))
28290792Sgshapiro**			break;
28390792Sgshapiro**	it's faster...
28490792Sgshapiro**
28590792Sgshapiro**	XXX interface isn't completely clear (yet), hence this code is
28690792Sgshapiro**	not available.
28790792Sgshapiro**
28890792Sgshapiro**
28990792Sgshapiro**	Parameters:
29090792Sgshapiro**		dst -- (pointer to) destination buffer
29190792Sgshapiro**		src -- source string
29290792Sgshapiro**		size -- size of destination buffer
29390792Sgshapiro**
29490792Sgshapiro**	Returns:
29590792Sgshapiro**		true if strlen(src) < size
29690792Sgshapiro**
29790792Sgshapiro**	Side Effects:
29890792Sgshapiro**		modifies dst if append succeeds (enough space).
29990792Sgshapiro*/
30090792Sgshapiro
30190792Sgshapirobool
30290792Sgshapirosm_strlapp(dst, src, size)
30390792Sgshapiro	register char **dst;
30490792Sgshapiro	register const char *src;
30590792Sgshapiro	ssize_t size;
30690792Sgshapiro{
30790792Sgshapiro	register size_t i;
30890792Sgshapiro
30990792Sgshapiro	if (size-- <= 0)
31090792Sgshapiro		return false;
31190792Sgshapiro	for (i = 0; i < size && ((*dst)[i] = src[i]) != '\0'; i++)
31290792Sgshapiro		continue;
31390792Sgshapiro	(*dst)[i] = '\0';
31490792Sgshapiro	if (src[i] == '\0')
31590792Sgshapiro	{
31690792Sgshapiro		*dst += i;
31790792Sgshapiro		return true;
31890792Sgshapiro	}
31990792Sgshapiro
32090792Sgshapiro	/* undo */
32190792Sgshapiro	(*dst)[0] = '\0';
32290792Sgshapiro	return false;
32390792Sgshapiro}
32490792Sgshapiro#endif /* 0 */
325