strl.c revision 266692
1/*
2 * Copyright (c) 1999-2002 Proofpoint, Inc. and its suppliers.
3 *	All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 *
9 */
10
11#include <sm/gen.h>
12SM_RCSID("@(#)$Id: strl.c,v 1.32 2013-11-22 20:51:43 ca Exp $")
13#include <sm/config.h>
14#include <sm/string.h>
15
16/*
17**  Notice: this file is used by libmilter. Please try to avoid
18**	using libsm specific functions.
19*/
20
21/*
22**  XXX the type of the length parameter has been changed
23**  from size_t to ssize_t to avoid theoretical problems with negative
24**  numbers passed into these functions.
25**  The real solution to this problem is to make sure that this doesn't
26**  happen, but for now we'll use this workaround.
27*/
28
29/*
30**  SM_STRLCPY -- size bounded string copy
31**
32**	This is a bounds-checking variant of strcpy.
33**	If size > 0, copy up to size-1 characters from the nul terminated
34**	string src to dst, nul terminating the result.  If size == 0,
35**	the dst buffer is not modified.
36**	Additional note: this function has been "tuned" to run fast and tested
37**	as such (versus versions in some OS's libc).
38**
39**	The result is strlen(src).  You can detect truncation (not all
40**	of the characters in the source string were copied) using the
41**	following idiom:
42**
43**		char *s, buf[BUFSIZ];
44**		...
45**		if (sm_strlcpy(buf, s, sizeof(buf)) >= sizeof(buf))
46**			goto overflow;
47**
48**	Parameters:
49**		dst -- destination buffer
50**		src -- source string
51**		size -- size of destination buffer
52**
53**	Returns:
54**		strlen(src)
55*/
56
57size_t
58sm_strlcpy(dst, src, size)
59	register char *dst;
60	register const char *src;
61	ssize_t size;
62{
63	register ssize_t i;
64
65	if (size-- <= 0)
66		return strlen(src);
67	for (i = 0; i < size && (dst[i] = src[i]) != 0; i++)
68		continue;
69	dst[i] = '\0';
70	if (src[i] == '\0')
71		return i;
72	else
73		return i + strlen(src + i);
74}
75
76/*
77**  SM_STRLCAT -- size bounded string concatenation
78**
79**	This is a bounds-checking variant of strcat.
80**	If strlen(dst) < size, then append at most size - strlen(dst) - 1
81**	characters from the source string to the destination string,
82**	nul terminating the result.  Otherwise, dst is not modified.
83**
84**	The result is the initial length of dst + the length of src.
85**	You can detect overflow (not all of the characters in the
86**	source string were copied) using the following idiom:
87**
88**		char *s, buf[BUFSIZ];
89**		...
90**		if (sm_strlcat(buf, s, sizeof(buf)) >= sizeof(buf))
91**			goto overflow;
92**
93**	Parameters:
94**		dst -- nul-terminated destination string buffer
95**		src -- nul-terminated source string
96**		size -- size of destination buffer
97**
98**	Returns:
99**		total length of the string tried to create
100**		(= initial length of dst + length of src)
101*/
102
103size_t
104sm_strlcat(dst, src, size)
105	register char *dst;
106	register const char *src;
107	ssize_t size;
108{
109	register ssize_t i, j, o;
110
111	o = strlen(dst);
112	if (size < o + 1)
113		return o + strlen(src);
114	size -= o + 1;
115	for (i = 0, j = o; i < size && (dst[j] = src[i]) != 0; i++, j++)
116		continue;
117	dst[j] = '\0';
118	if (src[i] == '\0')
119		return j;
120	else
121		return j + strlen(src + i);
122}
123/*
124**  SM_STRLCAT2 -- append two strings to dst obeying length and
125**		'\0' terminate it
126**
127**		strlcat2 will append at most len - strlen(dst) - 1 chars.
128**		terminates with '\0' if len > 0
129**		dst = dst "+" src1 "+" src2
130**		use this instead of sm_strlcat(dst,src1); sm_strlcat(dst,src2);
131**		for better speed.
132**
133**	Parameters:
134**		dst -- "destination" string.
135**		src1 -- "from" string 1.
136**		src2 -- "from" string 2.
137**		len -- max. length of "destination" string.
138**
139**	Returns:
140**		total length of the string tried to create
141**		(= initial length of dst + length of src)
142**		if this is greater than len then an overflow would have
143**		occurred.
144**
145*/
146
147size_t
148sm_strlcat2(dst, src1, src2, len)
149	register char *dst;
150	register const char *src1;
151	register const char *src2;
152	ssize_t len;
153{
154	register ssize_t i, j, o;
155
156	/* current size of dst */
157	o = strlen(dst);
158
159	/* max. size is less than current? */
160	if (len < o + 1)
161		return o + strlen(src1) + strlen(src2);
162
163	len -= o + 1;	/* space left in dst */
164
165	/* copy the first string; i: index in src1; j: index in dst */
166	for (i = 0, j = o; i < len && (dst[j] = src1[i]) != 0; i++, j++)
167		continue;
168
169	/* src1: end reached? */
170	if (src1[i] != '\0')
171	{
172		/* no: terminate dst; there is space since i < len */
173		dst[j] = '\0';
174		return j + strlen(src1 + i) + strlen(src2);
175	}
176
177	len -= i;	/* space left in dst */
178
179	/* copy the second string; i: index in src2; j: index in dst */
180	for (i = 0; i < len && (dst[j] = src2[i]) != 0; i++, j++)
181		continue;
182	dst[j] = '\0';	/* terminate dst; there is space since i < len */
183	if (src2[i] == '\0')
184		return j;
185	else
186		return j + strlen(src2 + i);
187}
188
189/*
190**  SM_STRLCPYN -- concatenate n strings and assign the result to dst
191**		while obeying length and '\0' terminate it
192**
193**		dst = src1 "+" src2 "+" ...
194**		use this instead of sm_snprintf() for string values
195**		and repeated sm_strlc*() calls for better speed.
196**
197**	Parameters:
198**		dst -- "destination" string.
199**		len -- max. length of "destination" string.
200**		n -- number of strings
201**		strings...
202**
203**	Returns:
204**		total length of the string tried to create
205**		(= initial length of dst + length of src)
206**		if this is greater than len then an overflow would have
207**		occurred.
208*/
209
210size_t
211#ifdef __STDC__
212sm_strlcpyn(char *dst, ssize_t len, int n, ...)
213#else /* __STDC__ */
214sm_strlcpyn(dst, len, n, va_alist)
215	register char *dst;
216	ssize_t len;
217	int n;
218	va_dcl
219#endif /* __STDC__ */
220{
221	register ssize_t i, j;
222	char *str;
223	SM_VA_LOCAL_DECL
224
225	SM_VA_START(ap, n);
226
227	if (len-- <= 0) /* This allows space for the terminating '\0' */
228	{
229		i = 0;
230		while (n-- > 0)
231			i += strlen(SM_VA_ARG(ap, char *));
232		SM_VA_END(ap);
233		return i;
234	}
235
236	j = 0;	/* index in dst */
237
238	/* loop through all source strings */
239	while (n-- > 0)
240	{
241		str = SM_VA_ARG(ap, char *);
242
243		/* copy string; i: index in str; j: index in dst */
244		for (i = 0; j < len && (dst[j] = str[i]) != 0; i++, j++)
245			continue;
246
247		/* str: end reached? */
248		if (str[i] != '\0')
249		{
250			/* no: terminate dst; there is space since j < len */
251			dst[j] = '\0';
252			j += strlen(str + i);
253			while (n-- > 0)
254				j += strlen(SM_VA_ARG(ap, char *));
255			SM_VA_END(ap);
256			return j;
257		}
258	}
259	SM_VA_END(ap);
260
261	dst[j] = '\0';	/* terminate dst; there is space since j < len */
262	return j;
263}
264
265#if 0
266/*
267**  SM_STRLAPP -- append string if it fits into buffer.
268**
269**	If size > 0, copy up to size-1 characters from the nul terminated
270**	string src to dst, nul terminating the result.  If size == 0,
271**	the dst buffer is not modified.
272**
273**	This routine is useful for appending strings in a loop, e.g, instead of
274**	s = buf;
275**	for (ptr, ptr != NULL, ptr = next->ptr)
276**	{
277**		(void) sm_strlcpy(s, ptr->string, sizeof buf - (s - buf));
278**		s += strlen(s);
279**	}
280**	replace the loop body with:
281**		if (!sm_strlapp(*s, ptr->string, sizeof buf - (s - buf)))
282**			break;
283**	it's faster...
284**
285**	XXX interface isn't completely clear (yet), hence this code is
286**	not available.
287**
288**
289**	Parameters:
290**		dst -- (pointer to) destination buffer
291**		src -- source string
292**		size -- size of destination buffer
293**
294**	Returns:
295**		true if strlen(src) < size
296**
297**	Side Effects:
298**		modifies dst if append succeeds (enough space).
299*/
300
301bool
302sm_strlapp(dst, src, size)
303	register char **dst;
304	register const char *src;
305	ssize_t size;
306{
307	register size_t i;
308
309	if (size-- <= 0)
310		return false;
311	for (i = 0; i < size && ((*dst)[i] = src[i]) != '\0'; i++)
312		continue;
313	(*dst)[i] = '\0';
314	if (src[i] == '\0')
315	{
316		*dst += i;
317		return true;
318	}
319
320	/* undo */
321	(*dst)[0] = '\0';
322	return false;
323}
324#endif /* 0 */
325