140266059SGregory Neil Shapiro /* 2605302a5SGregory Neil Shapiro * Copyright (c) 1999-2002 Sendmail, 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> 12605302a5SGregory Neil Shapiro SM_RCSID("@(#)$Id: strl.c,v 1.31 2002/01/20 01:41:25 gshapiro 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 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 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__ 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