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