xref: /freebsd/contrib/sendmail/libsm/strl.c (revision ee7b0571c2c18bdec848ed2044223cc88db29bd8)
140266059SGregory Neil Shapiro /*
25dd76dd0SGregory Neil Shapiro  * Copyright (c) 1999-2002 Proofpoint, Inc. and its suppliers.
340266059SGregory Neil Shapiro  *	All rights reserved.
440266059SGregory Neil Shapiro  *
540266059SGregory Neil Shapiro  * By using this file, you agree to the terms and conditions set
640266059SGregory Neil Shapiro  * forth in the LICENSE file which can be found at the top level of
740266059SGregory Neil Shapiro  * the sendmail distribution.
840266059SGregory Neil Shapiro  *
940266059SGregory Neil Shapiro  */
1040266059SGregory Neil Shapiro 
1140266059SGregory Neil Shapiro #include <sm/gen.h>
12*4313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: strl.c,v 1.32 2013-11-22 20:51:43 ca Exp $")
1340266059SGregory Neil Shapiro #include <sm/config.h>
1440266059SGregory Neil Shapiro #include <sm/string.h>
1540266059SGregory Neil Shapiro 
1640266059SGregory Neil Shapiro /*
1740266059SGregory Neil Shapiro **  Notice: this file is used by libmilter. Please try to avoid
1840266059SGregory Neil Shapiro **	using libsm specific functions.
1940266059SGregory Neil Shapiro */
2040266059SGregory Neil Shapiro 
2140266059SGregory Neil Shapiro /*
2240266059SGregory Neil Shapiro **  XXX the type of the length parameter has been changed
2340266059SGregory Neil Shapiro **  from size_t to ssize_t to avoid theoretical problems with negative
2440266059SGregory Neil Shapiro **  numbers passed into these functions.
2540266059SGregory Neil Shapiro **  The real solution to this problem is to make sure that this doesn't
2640266059SGregory Neil Shapiro **  happen, but for now we'll use this workaround.
2740266059SGregory Neil Shapiro */
2840266059SGregory Neil Shapiro 
2940266059SGregory Neil Shapiro /*
3040266059SGregory Neil Shapiro **  SM_STRLCPY -- size bounded string copy
3140266059SGregory Neil Shapiro **
3240266059SGregory Neil Shapiro **	This is a bounds-checking variant of strcpy.
3340266059SGregory Neil Shapiro **	If size > 0, copy up to size-1 characters from the nul terminated
3440266059SGregory Neil Shapiro **	string src to dst, nul terminating the result.  If size == 0,
3540266059SGregory Neil Shapiro **	the dst buffer is not modified.
3640266059SGregory Neil Shapiro **	Additional note: this function has been "tuned" to run fast and tested
3740266059SGregory Neil Shapiro **	as such (versus versions in some OS's libc).
3840266059SGregory Neil Shapiro **
3940266059SGregory Neil Shapiro **	The result is strlen(src).  You can detect truncation (not all
4040266059SGregory Neil Shapiro **	of the characters in the source string were copied) using the
4140266059SGregory Neil Shapiro **	following idiom:
4240266059SGregory Neil Shapiro **
4340266059SGregory Neil Shapiro **		char *s, buf[BUFSIZ];
4440266059SGregory Neil Shapiro **		...
4540266059SGregory Neil Shapiro **		if (sm_strlcpy(buf, s, sizeof(buf)) >= sizeof(buf))
4640266059SGregory Neil Shapiro **			goto overflow;
4740266059SGregory Neil Shapiro **
4840266059SGregory Neil Shapiro **	Parameters:
4940266059SGregory Neil Shapiro **		dst -- destination buffer
5040266059SGregory Neil Shapiro **		src -- source string
5140266059SGregory Neil Shapiro **		size -- size of destination buffer
5240266059SGregory Neil Shapiro **
5340266059SGregory Neil Shapiro **	Returns:
5440266059SGregory Neil Shapiro **		strlen(src)
5540266059SGregory Neil Shapiro */
5640266059SGregory Neil Shapiro 
5740266059SGregory Neil Shapiro size_t
5840266059SGregory Neil Shapiro sm_strlcpy(dst, src, size)
5940266059SGregory Neil Shapiro 	register char *dst;
6040266059SGregory Neil Shapiro 	register const char *src;
6140266059SGregory Neil Shapiro 	ssize_t size;
6240266059SGregory Neil Shapiro {
6340266059SGregory Neil Shapiro 	register ssize_t i;
6440266059SGregory Neil Shapiro 
6540266059SGregory Neil Shapiro 	if (size-- <= 0)
6640266059SGregory Neil Shapiro 		return strlen(src);
6740266059SGregory Neil Shapiro 	for (i = 0; i < size && (dst[i] = src[i]) != 0; i++)
6840266059SGregory Neil Shapiro 		continue;
6940266059SGregory Neil Shapiro 	dst[i] = '\0';
7040266059SGregory Neil Shapiro 	if (src[i] == '\0')
7140266059SGregory Neil Shapiro 		return i;
7240266059SGregory Neil Shapiro 	else
7340266059SGregory Neil Shapiro 		return i + strlen(src + i);
7440266059SGregory Neil Shapiro }
7540266059SGregory Neil Shapiro 
7640266059SGregory Neil Shapiro /*
7740266059SGregory Neil Shapiro **  SM_STRLCAT -- size bounded string concatenation
7840266059SGregory Neil Shapiro **
7940266059SGregory Neil Shapiro **	This is a bounds-checking variant of strcat.
8040266059SGregory Neil Shapiro **	If strlen(dst) < size, then append at most size - strlen(dst) - 1
8140266059SGregory Neil Shapiro **	characters from the source string to the destination string,
8240266059SGregory Neil Shapiro **	nul terminating the result.  Otherwise, dst is not modified.
8340266059SGregory Neil Shapiro **
8440266059SGregory Neil Shapiro **	The result is the initial length of dst + the length of src.
8540266059SGregory Neil Shapiro **	You can detect overflow (not all of the characters in the
8640266059SGregory Neil Shapiro **	source string were copied) using the following idiom:
8740266059SGregory Neil Shapiro **
8840266059SGregory Neil Shapiro **		char *s, buf[BUFSIZ];
8940266059SGregory Neil Shapiro **		...
9040266059SGregory Neil Shapiro **		if (sm_strlcat(buf, s, sizeof(buf)) >= sizeof(buf))
9140266059SGregory Neil Shapiro **			goto overflow;
9240266059SGregory Neil Shapiro **
9340266059SGregory Neil Shapiro **	Parameters:
9440266059SGregory Neil Shapiro **		dst -- nul-terminated destination string buffer
9540266059SGregory Neil Shapiro **		src -- nul-terminated source string
9640266059SGregory Neil Shapiro **		size -- size of destination buffer
9740266059SGregory Neil Shapiro **
9840266059SGregory Neil Shapiro **	Returns:
9940266059SGregory Neil Shapiro **		total length of the string tried to create
10040266059SGregory Neil Shapiro **		(= initial length of dst + length of src)
10140266059SGregory Neil Shapiro */
10240266059SGregory Neil Shapiro 
10340266059SGregory Neil Shapiro size_t
sm_strlcat(dst,src,size)10440266059SGregory Neil Shapiro sm_strlcat(dst, src, size)
10540266059SGregory Neil Shapiro 	register char *dst;
10640266059SGregory Neil Shapiro 	register const char *src;
10740266059SGregory Neil Shapiro 	ssize_t size;
10840266059SGregory Neil Shapiro {
10940266059SGregory Neil Shapiro 	register ssize_t i, j, o;
11040266059SGregory Neil Shapiro 
11140266059SGregory Neil Shapiro 	o = strlen(dst);
11240266059SGregory Neil Shapiro 	if (size < o + 1)
11340266059SGregory Neil Shapiro 		return o + strlen(src);
11440266059SGregory Neil Shapiro 	size -= o + 1;
11540266059SGregory Neil Shapiro 	for (i = 0, j = o; i < size && (dst[j] = src[i]) != 0; i++, j++)
11640266059SGregory Neil Shapiro 		continue;
11740266059SGregory Neil Shapiro 	dst[j] = '\0';
11840266059SGregory Neil Shapiro 	if (src[i] == '\0')
11940266059SGregory Neil Shapiro 		return j;
12040266059SGregory Neil Shapiro 	else
12140266059SGregory Neil Shapiro 		return j + strlen(src + i);
12240266059SGregory Neil Shapiro }
12340266059SGregory Neil Shapiro /*
12440266059SGregory Neil Shapiro **  SM_STRLCAT2 -- append two strings to dst obeying length and
12540266059SGregory Neil Shapiro **		'\0' terminate it
12640266059SGregory Neil Shapiro **
12740266059SGregory Neil Shapiro **		strlcat2 will append at most len - strlen(dst) - 1 chars.
12840266059SGregory Neil Shapiro **		terminates with '\0' if len > 0
12940266059SGregory Neil Shapiro **		dst = dst "+" src1 "+" src2
13040266059SGregory Neil Shapiro **		use this instead of sm_strlcat(dst,src1); sm_strlcat(dst,src2);
13140266059SGregory Neil Shapiro **		for better speed.
13240266059SGregory Neil Shapiro **
13340266059SGregory Neil Shapiro **	Parameters:
13440266059SGregory Neil Shapiro **		dst -- "destination" string.
13540266059SGregory Neil Shapiro **		src1 -- "from" string 1.
13640266059SGregory Neil Shapiro **		src2 -- "from" string 2.
13740266059SGregory Neil Shapiro **		len -- max. length of "destination" string.
13840266059SGregory Neil Shapiro **
13940266059SGregory Neil Shapiro **	Returns:
14040266059SGregory Neil Shapiro **		total length of the string tried to create
14140266059SGregory Neil Shapiro **		(= initial length of dst + length of src)
14240266059SGregory Neil Shapiro **		if this is greater than len then an overflow would have
14340266059SGregory Neil Shapiro **		occurred.
14440266059SGregory Neil Shapiro **
14540266059SGregory Neil Shapiro */
14640266059SGregory Neil Shapiro 
14740266059SGregory Neil Shapiro size_t
sm_strlcat2(dst,src1,src2,len)14840266059SGregory Neil Shapiro sm_strlcat2(dst, src1, src2, len)
14940266059SGregory Neil Shapiro 	register char *dst;
15040266059SGregory Neil Shapiro 	register const char *src1;
15140266059SGregory Neil Shapiro 	register const char *src2;
15240266059SGregory Neil Shapiro 	ssize_t len;
15340266059SGregory Neil Shapiro {
15440266059SGregory Neil Shapiro 	register ssize_t i, j, o;
15540266059SGregory Neil Shapiro 
15640266059SGregory Neil Shapiro 	/* current size of dst */
15740266059SGregory Neil Shapiro 	o = strlen(dst);
15840266059SGregory Neil Shapiro 
15940266059SGregory Neil Shapiro 	/* max. size is less than current? */
16040266059SGregory Neil Shapiro 	if (len < o + 1)
16140266059SGregory Neil Shapiro 		return o + strlen(src1) + strlen(src2);
16240266059SGregory Neil Shapiro 
16340266059SGregory Neil Shapiro 	len -= o + 1;	/* space left in dst */
16440266059SGregory Neil Shapiro 
16540266059SGregory Neil Shapiro 	/* copy the first string; i: index in src1; j: index in dst */
16640266059SGregory Neil Shapiro 	for (i = 0, j = o; i < len && (dst[j] = src1[i]) != 0; i++, j++)
16740266059SGregory Neil Shapiro 		continue;
16840266059SGregory Neil Shapiro 
16940266059SGregory Neil Shapiro 	/* src1: end reached? */
17040266059SGregory Neil Shapiro 	if (src1[i] != '\0')
17140266059SGregory Neil Shapiro 	{
17240266059SGregory Neil Shapiro 		/* no: terminate dst; there is space since i < len */
17340266059SGregory Neil Shapiro 		dst[j] = '\0';
17440266059SGregory Neil Shapiro 		return j + strlen(src1 + i) + strlen(src2);
17540266059SGregory Neil Shapiro 	}
17640266059SGregory Neil Shapiro 
17740266059SGregory Neil Shapiro 	len -= i;	/* space left in dst */
17840266059SGregory Neil Shapiro 
17940266059SGregory Neil Shapiro 	/* copy the second string; i: index in src2; j: index in dst */
18040266059SGregory Neil Shapiro 	for (i = 0; i < len && (dst[j] = src2[i]) != 0; i++, j++)
18140266059SGregory Neil Shapiro 		continue;
18240266059SGregory Neil Shapiro 	dst[j] = '\0';	/* terminate dst; there is space since i < len */
18340266059SGregory Neil Shapiro 	if (src2[i] == '\0')
18440266059SGregory Neil Shapiro 		return j;
18540266059SGregory Neil Shapiro 	else
18640266059SGregory Neil Shapiro 		return j + strlen(src2 + i);
18740266059SGregory Neil Shapiro }
18840266059SGregory Neil Shapiro 
18940266059SGregory Neil Shapiro /*
19040266059SGregory Neil Shapiro **  SM_STRLCPYN -- concatenate n strings and assign the result to dst
19140266059SGregory Neil Shapiro **		while obeying length and '\0' terminate it
19240266059SGregory Neil Shapiro **
19340266059SGregory Neil Shapiro **		dst = src1 "+" src2 "+" ...
19440266059SGregory Neil Shapiro **		use this instead of sm_snprintf() for string values
19540266059SGregory Neil Shapiro **		and repeated sm_strlc*() calls for better speed.
19640266059SGregory Neil Shapiro **
19740266059SGregory Neil Shapiro **	Parameters:
19840266059SGregory Neil Shapiro **		dst -- "destination" string.
19940266059SGregory Neil Shapiro **		len -- max. length of "destination" string.
20040266059SGregory Neil Shapiro **		n -- number of strings
20140266059SGregory Neil Shapiro **		strings...
20240266059SGregory Neil Shapiro **
20340266059SGregory Neil Shapiro **	Returns:
20440266059SGregory Neil Shapiro **		total length of the string tried to create
20540266059SGregory Neil Shapiro **		(= initial length of dst + length of src)
20640266059SGregory Neil Shapiro **		if this is greater than len then an overflow would have
20740266059SGregory Neil Shapiro **		occurred.
20840266059SGregory Neil Shapiro */
20940266059SGregory Neil Shapiro 
21040266059SGregory Neil Shapiro size_t
21140266059SGregory Neil Shapiro #ifdef __STDC__
sm_strlcpyn(char * dst,ssize_t len,int n,...)21240266059SGregory Neil Shapiro sm_strlcpyn(char *dst, ssize_t len, int n, ...)
21340266059SGregory Neil Shapiro #else /* __STDC__ */
21440266059SGregory Neil Shapiro sm_strlcpyn(dst, len, n, va_alist)
21540266059SGregory Neil Shapiro 	register char *dst;
21640266059SGregory Neil Shapiro 	ssize_t len;
21740266059SGregory Neil Shapiro 	int n;
21840266059SGregory Neil Shapiro 	va_dcl
21940266059SGregory Neil Shapiro #endif /* __STDC__ */
22040266059SGregory Neil Shapiro {
22140266059SGregory Neil Shapiro 	register ssize_t i, j;
22240266059SGregory Neil Shapiro 	char *str;
22340266059SGregory Neil Shapiro 	SM_VA_LOCAL_DECL
22440266059SGregory Neil Shapiro 
22540266059SGregory Neil Shapiro 	SM_VA_START(ap, n);
22640266059SGregory Neil Shapiro 
22740266059SGregory Neil Shapiro 	if (len-- <= 0) /* This allows space for the terminating '\0' */
22840266059SGregory Neil Shapiro 	{
22940266059SGregory Neil Shapiro 		i = 0;
23040266059SGregory Neil Shapiro 		while (n-- > 0)
23140266059SGregory Neil Shapiro 			i += strlen(SM_VA_ARG(ap, char *));
232605302a5SGregory Neil Shapiro 		SM_VA_END(ap);
23340266059SGregory Neil Shapiro 		return i;
23440266059SGregory Neil Shapiro 	}
23540266059SGregory Neil Shapiro 
23640266059SGregory Neil Shapiro 	j = 0;	/* index in dst */
23740266059SGregory Neil Shapiro 
23840266059SGregory Neil Shapiro 	/* loop through all source strings */
23940266059SGregory Neil Shapiro 	while (n-- > 0)
24040266059SGregory Neil Shapiro 	{
24140266059SGregory Neil Shapiro 		str = SM_VA_ARG(ap, char *);
24240266059SGregory Neil Shapiro 
24340266059SGregory Neil Shapiro 		/* copy string; i: index in str; j: index in dst */
24440266059SGregory Neil Shapiro 		for (i = 0; j < len && (dst[j] = str[i]) != 0; i++, j++)
24540266059SGregory Neil Shapiro 			continue;
24640266059SGregory Neil Shapiro 
24740266059SGregory Neil Shapiro 		/* str: end reached? */
24840266059SGregory Neil Shapiro 		if (str[i] != '\0')
24940266059SGregory Neil Shapiro 		{
25040266059SGregory Neil Shapiro 			/* no: terminate dst; there is space since j < len */
25140266059SGregory Neil Shapiro 			dst[j] = '\0';
25240266059SGregory Neil Shapiro 			j += strlen(str + i);
25340266059SGregory Neil Shapiro 			while (n-- > 0)
25440266059SGregory Neil Shapiro 				j += strlen(SM_VA_ARG(ap, char *));
255605302a5SGregory Neil Shapiro 			SM_VA_END(ap);
25640266059SGregory Neil Shapiro 			return j;
25740266059SGregory Neil Shapiro 		}
25840266059SGregory Neil Shapiro 	}
259605302a5SGregory Neil Shapiro 	SM_VA_END(ap);
26040266059SGregory Neil Shapiro 
26140266059SGregory Neil Shapiro 	dst[j] = '\0';	/* terminate dst; there is space since j < len */
26240266059SGregory Neil Shapiro 	return j;
26340266059SGregory Neil Shapiro }
26440266059SGregory Neil Shapiro 
26540266059SGregory Neil Shapiro #if 0
26640266059SGregory Neil Shapiro /*
26740266059SGregory Neil Shapiro **  SM_STRLAPP -- append string if it fits into buffer.
26840266059SGregory Neil Shapiro **
26940266059SGregory Neil Shapiro **	If size > 0, copy up to size-1 characters from the nul terminated
27040266059SGregory Neil Shapiro **	string src to dst, nul terminating the result.  If size == 0,
27140266059SGregory Neil Shapiro **	the dst buffer is not modified.
27240266059SGregory Neil Shapiro **
27340266059SGregory Neil Shapiro **	This routine is useful for appending strings in a loop, e.g, instead of
27440266059SGregory Neil Shapiro **	s = buf;
27540266059SGregory Neil Shapiro **	for (ptr, ptr != NULL, ptr = next->ptr)
27640266059SGregory Neil Shapiro **	{
27740266059SGregory Neil Shapiro **		(void) sm_strlcpy(s, ptr->string, sizeof buf - (s - buf));
27840266059SGregory Neil Shapiro **		s += strlen(s);
27940266059SGregory Neil Shapiro **	}
28040266059SGregory Neil Shapiro **	replace the loop body with:
28140266059SGregory Neil Shapiro **		if (!sm_strlapp(*s, ptr->string, sizeof buf - (s - buf)))
28240266059SGregory Neil Shapiro **			break;
28340266059SGregory Neil Shapiro **	it's faster...
28440266059SGregory Neil Shapiro **
28540266059SGregory Neil Shapiro **	XXX interface isn't completely clear (yet), hence this code is
28640266059SGregory Neil Shapiro **	not available.
28740266059SGregory Neil Shapiro **
28840266059SGregory Neil Shapiro **
28940266059SGregory Neil Shapiro **	Parameters:
29040266059SGregory Neil Shapiro **		dst -- (pointer to) destination buffer
29140266059SGregory Neil Shapiro **		src -- source string
29240266059SGregory Neil Shapiro **		size -- size of destination buffer
29340266059SGregory Neil Shapiro **
29440266059SGregory Neil Shapiro **	Returns:
29540266059SGregory Neil Shapiro **		true if strlen(src) < size
29640266059SGregory Neil Shapiro **
29740266059SGregory Neil Shapiro **	Side Effects:
29840266059SGregory Neil Shapiro **		modifies dst if append succeeds (enough space).
29940266059SGregory Neil Shapiro */
30040266059SGregory Neil Shapiro 
30140266059SGregory Neil Shapiro bool
30240266059SGregory Neil Shapiro sm_strlapp(dst, src, size)
30340266059SGregory Neil Shapiro 	register char **dst;
30440266059SGregory Neil Shapiro 	register const char *src;
30540266059SGregory Neil Shapiro 	ssize_t size;
30640266059SGregory Neil Shapiro {
30740266059SGregory Neil Shapiro 	register size_t i;
30840266059SGregory Neil Shapiro 
30940266059SGregory Neil Shapiro 	if (size-- <= 0)
31040266059SGregory Neil Shapiro 		return false;
31140266059SGregory Neil Shapiro 	for (i = 0; i < size && ((*dst)[i] = src[i]) != '\0'; i++)
31240266059SGregory Neil Shapiro 		continue;
31340266059SGregory Neil Shapiro 	(*dst)[i] = '\0';
31440266059SGregory Neil Shapiro 	if (src[i] == '\0')
31540266059SGregory Neil Shapiro 	{
31640266059SGregory Neil Shapiro 		*dst += i;
31740266059SGregory Neil Shapiro 		return true;
31840266059SGregory Neil Shapiro 	}
31940266059SGregory Neil Shapiro 
32040266059SGregory Neil Shapiro 	/* undo */
32140266059SGregory Neil Shapiro 	(*dst)[0] = '\0';
32240266059SGregory Neil Shapiro 	return false;
32340266059SGregory Neil Shapiro }
32440266059SGregory Neil Shapiro #endif /* 0 */
325