/*
* Copyright (c) 1999-2002 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sm/gen.h>
SM_RCSID("@(#)$Id: strl.c,v 1.31 2002/01/20 01:41:25 gshapiro Exp $")
#include <sm/config.h>
#include <sm/string.h>
/*
** Notice: this file is used by libmilter. Please try to avoid
** using libsm specific functions.
*/
/*
** XXX the type of the length parameter has been changed
** from size_t to ssize_t to avoid theoretical problems with negative
** numbers passed into these functions.
** The real solution to this problem is to make sure that this doesn't
** happen, but for now we'll use this workaround.
*/
/*
** SM_STRLCPY -- size bounded string copy
**
** This is a bounds-checking variant of strcpy.
** If size > 0, copy up to size-1 characters from the nul terminated
** string src to dst, nul terminating the result. If size == 0,
** the dst buffer is not modified.
** Additional note: this function has been "tuned" to run fast and tested
** as such (versus versions in some OS's libc).
**
** The result is strlen(src). You can detect truncation (not all
** of the characters in the source string were copied) using the
** following idiom:
**
** char *s, buf[BUFSIZ];
** ...
** if (sm_strlcpy(buf, s, sizeof(buf)) >= sizeof(buf))
** goto overflow;
**
** Parameters:
** dst -- destination buffer
** src -- source string
** size -- size of destination buffer
**
** Returns:
** strlen(src)
*/
size_t
sm_strlcpy(dst, src, size)
register char *dst;
register const char *src;
ssize_t size;
{
register ssize_t i;
if (size-- <= 0)
return strlen(src);
for (i = 0; i < size && (dst[i] = src[i]) != 0; i++)
continue;
dst[i] = '\0';
if (src[i] == '\0')
return i;
else
return i + strlen(src + i);
}
/*
** SM_STRLCAT -- size bounded string concatenation
**
** This is a bounds-checking variant of strcat.
** If strlen(dst) < size, then append at most size - strlen(dst) - 1
** characters from the source string to the destination string,
** nul terminating the result. Otherwise, dst is not modified.
**
** The result is the initial length of dst + the length of src.
** You can detect overflow (not all of the characters in the
** source string were copied) using the following idiom:
**
** char *s, buf[BUFSIZ];
** ...
** if (sm_strlcat(buf, s, sizeof(buf)) >= sizeof(buf))
** goto overflow;
**
** Parameters:
** dst -- nul-terminated destination string buffer
** src -- nul-terminated source string
** size -- size of destination buffer
**
** Returns:
** total length of the string tried to create
** (= initial length of dst + length of src)
*/
size_t
sm_strlcat(dst, src, size)
register char *dst;
register const char *src;
ssize_t size;
{
register ssize_t i, j, o;
o = strlen(dst);
if (size < o + 1)
return o + strlen(src);
size -= o + 1;
for (i = 0, j = o; i < size && (dst[j] = src[i]) != 0; i++, j++)
continue;
dst[j] = '\0';
if (src[i] == '\0')
return j;
else
return j + strlen(src + i);
}
/*
** SM_STRLCAT2 -- append two strings to dst obeying length and
** '\0' terminate it
**
** strlcat2 will append at most len - strlen(dst) - 1 chars.
** terminates with '\0' if len > 0
** dst = dst "+" src1 "+" src2
** use this instead of sm_strlcat(dst,src1); sm_strlcat(dst,src2);
** for better speed.
**
** Parameters:
** dst -- "destination" string.
** src1 -- "from" string 1.
** src2 -- "from" string 2.
** len -- max. length of "destination" string.
**
** Returns:
** total length of the string tried to create
** (= initial length of dst + length of src)
** if this is greater than len then an overflow would have
** occurred.
**
*/
size_t
sm_strlcat2(dst, src1, src2, len)
register char *dst;
register const char *src1;
register const char *src2;
ssize_t len;
{
register ssize_t i, j, o;
/* current size of dst */
o = strlen(dst);
/* max. size is less than current? */
if (len < o + 1)
return o + strlen(src1) + strlen(src2);
len -= o + 1; /* space left in dst */
/* copy the first string; i: index in src1; j: index in dst */
for (i = 0, j = o; i < len && (dst[j] = src1[i]) != 0; i++, j++)
continue;
/* src1: end reached? */
if (src1[i] != '\0')
{
/* no: terminate dst; there is space since i < len */
dst[j] = '\0';
return j + strlen(src1 + i) + strlen(src2);
}
len -= i; /* space left in dst */
/* copy the second string; i: index in src2; j: index in dst */
for (i = 0; i < len && (dst[j] = src2[i]) != 0; i++, j++)
continue;
dst[j] = '\0'; /* terminate dst; there is space since i < len */
if (src2[i] == '\0')
return j;
else
return j + strlen(src2 + i);
}
/*
** SM_STRLCPYN -- concatenate n strings and assign the result to dst
** while obeying length and '\0' terminate it
**
** dst = src1 "+" src2 "+" ...
** use this instead of sm_snprintf() for string values
** and repeated sm_strlc*() calls for better speed.
**
** Parameters:
** dst -- "destination" string.
** len -- max. length of "destination" string.
** n -- number of strings
** strings...
**
** Returns:
** total length of the string tried to create
** (= initial length of dst + length of src)
** if this is greater than len then an overflow would have
** occurred.
*/
size_t
#ifdef __STDC__
sm_strlcpyn(char *dst, ssize_t len, int n, ...)
#else /* __STDC__ */
sm_strlcpyn(dst, len, n, va_alist)
register char *dst;
ssize_t len;
int n;
va_dcl
#endif /* __STDC__ */
{
register ssize_t i, j;
char *str;
SM_VA_LOCAL_DECL
SM_VA_START(ap, n);
if (len-- <= 0) /* This allows space for the terminating '\0' */
{
i = 0;
while (n-- > 0)
i += strlen(SM_VA_ARG(ap, char *));
SM_VA_END(ap);
return i;
}
j = 0; /* index in dst */
/* loop through all source strings */
while (n-- > 0)
{
str = SM_VA_ARG(ap, char *);
/* copy string; i: index in str; j: index in dst */
for (i = 0; j < len && (dst[j] = str[i]) != 0; i++, j++)
continue;
/* str: end reached? */
if (str[i] != '\0')
{
/* no: terminate dst; there is space since j < len */
dst[j] = '\0';
j += strlen(str + i);
while (n-- > 0)
j += strlen(SM_VA_ARG(ap, char *));
SM_VA_END(ap);
return j;
}
}
SM_VA_END(ap);
dst[j] = '\0'; /* terminate dst; there is space since j < len */
return j;
}
#if 0
/*
** SM_STRLAPP -- append string if it fits into buffer.
**
** If size > 0, copy up to size-1 characters from the nul terminated
** string src to dst, nul terminating the result. If size == 0,
** the dst buffer is not modified.
**
** This routine is useful for appending strings in a loop, e.g, instead of
** s = buf;
** for (ptr, ptr != NULL, ptr = next->ptr)
** {
** (void) sm_strlcpy(s, ptr->string, sizeof buf - (s - buf));
** s += strlen(s);
** }
** replace the loop body with:
** if (!sm_strlapp(*s, ptr->string, sizeof buf - (s - buf)))
** break;
** it's faster...
**
** XXX interface isn't completely clear (yet), hence this code is
** not available.
**
**
** Parameters:
** dst -- (pointer to) destination buffer
** src -- source string
** size -- size of destination buffer
**
** Returns:
** true if strlen(src) < size
**
** Side Effects:
** modifies dst if append succeeds (enough space).
*/
bool
sm_strlapp(dst, src, size)
register char **dst;
register const char *src;
ssize_t size;
{
register size_t i;
if (size-- <= 0)
return false;
for (i = 0; i < size && ((*dst)[i] = src[i]) != '\0'; i++)
continue;
(*dst)[i] = '\0';
if (src[i] == '\0')
{
*dst += i;
return true;
}
/* undo */
(*dst)[0] = '\0';
return false;
}
#endif /* 0 */