unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
support.c (39571B)
1 /* 2 * Unreal Internet Relay Chat Daemon, src/support.c 3 * Copyright (C) 1990, 1991 Armin Gruner 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 1, or (at your option) 8 * any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 20 /** @file 21 * @brief Functions that don't always exist on every OS are provided here. 22 * That was the original idea, anyway. Right now it also contains some 23 * functions that should probably be in src/misc.c instead. 24 * In any case, most functions here don't have any special meaning 25 * specific for IRC, they could just as well be used in non-IRC code. 26 */ 27 28 /* support.c 2.21 4/13/94 1990, 1991 Armin Gruner; 1992, 1993 Darren Reed */ 29 30 #include "unrealircd.h" 31 32 extern void outofmemory(); 33 34 #define is_enabled match 35 36 /** Convert integer to string */ 37 const char *my_itoa(int i) 38 { 39 static char buf[128]; 40 ircsnprintf(buf, sizeof(buf), "%d", i); 41 return buf; 42 } 43 44 /** Walk through a string of tokens, using a set of separators. 45 * @param save Pointer used for saving between calls 46 * @param str String to parse (will be altered!) 47 * @param fs Separator character(s) 48 * @returns substring (token) 49 * @note This function works similar to (but not identical?) to strtok_r(). 50 * @section Ex1 Example 51 * @code 52 * for (name = strtoken(&p, buf, ","); name; name = strtoken(&p, NULL, ",")) 53 * unreal_log(ULOG_INFO, "test", "TEST", "Got: $name", log_data_string(name)); 54 * @endcode 55 */ 56 char *strtoken(char **save, char *str, char *fs) 57 { 58 char *pos, *tmp; 59 60 if (str) 61 pos = str; /* new string scan */ 62 else 63 pos = *save; /* keep last position across calls */ 64 65 while (pos && *pos && strchr(fs, *pos) != NULL) 66 pos++; /* skip leading separators */ 67 68 if (!pos || !*pos) 69 return (pos = *save = NULL); /* string contains only sep's */ 70 71 tmp = pos; /* now, keep position of the token */ 72 73 while (*pos && strchr(fs, *pos) == NULL) 74 pos++; /* skip content of the token */ 75 76 if (*pos) 77 *pos++ = '\0'; /* remove first sep after the token */ 78 else 79 pos = NULL; /* end of string */ 80 81 *save = pos; 82 return (tmp); 83 } 84 85 /** Walk through a string of tokens, using a set of separators. 86 * This is the special version that won't skip/merge tokens, 87 * eg "a,,c" would return "a", then "" (empty), then "c". 88 * This in contrast to strtoken() which would return "a" and then "c". 89 * This strtoken_noskip() will also not skip tokens at the 90 * beginning, eg ",,c" would return "" (empty), "" (empty), "c". 91 * 92 * @param save Pointer used for saving between calls 93 * @param str String to parse (will be altered!) 94 * @param fs Separator character(s) 95 * @returns substring (token) 96 * @note This function works similar to (but not identical?) to strtok_r(). 97 */ 98 char *strtoken_noskip(char **save, char *str, char *fs) 99 { 100 char *pos, *tmp; 101 102 if (str) 103 { 104 pos = str; /* new string scan */ 105 } else { 106 if (*save == NULL) 107 { 108 /* We reached the end of the string */ 109 return NULL; 110 } 111 pos = *save; /* keep last position across calls */ 112 } 113 114 tmp = pos; /* start position, used for returning later */ 115 116 /* Hunt for next separator (fs in pos) */ 117 while (*pos && !strchr(fs, *pos)) 118 pos++; 119 120 if (!*pos) 121 { 122 /* Next call is end of string */ 123 *save = NULL; 124 *pos++ = '\0'; 125 } else { 126 *pos++ = '\0'; 127 *save = pos; 128 } 129 130 return tmp; 131 } 132 133 /** Convert binary address to an IP string - like inet_ntop but will always return the uncompressed IPv6 form. 134 * @param af Address family (AF_INET, AF_INET6) 135 * @param in Address (binary) 136 * @param out Buffer to use for storing the returned IP string 137 * @param size Size of the 'out' buffer 138 * @returns IP address as a string (IPv4 or IPv6, in case of the latter: 139 * always the uncompressed form without ::) 140 */ 141 const char *inetntop(int af, const void *in, char *out, size_t size) 142 { 143 char tmp[MYDUMMY_SIZE]; 144 145 inet_ntop(af, in, tmp, size); 146 if (!strstr(tmp, "::")) 147 { 148 /* IPv4 or IPv6 that is already uncompressed */ 149 strlcpy(out, tmp, size); 150 } else 151 { 152 char cnt = 0, *cp = tmp, *op = out; 153 154 /* It's an IPv6 compressed address that we need to expand */ 155 while (*cp) 156 { 157 if (*cp == ':') 158 cnt += 1; 159 if (*cp++ == '.') 160 { 161 cnt += 1; 162 break; 163 } 164 } 165 cp = tmp; 166 while (*cp) 167 { 168 *op++ = *cp++; 169 if (*(cp - 1) == ':' && *cp == ':') 170 { 171 if ((cp - 1) == tmp) 172 { 173 op--; 174 *op++ = '0'; 175 *op++ = ':'; 176 } 177 178 *op++ = '0'; 179 while (cnt++ < 7) 180 { 181 *op++ = ':'; 182 *op++ = '0'; 183 } 184 } 185 } 186 if (*(op - 1) == ':') 187 *op++ = '0'; 188 *op = '\0'; 189 } 190 return out; 191 } 192 193 /** Cut string off at the first occurance of CR or LF */ 194 void stripcrlf(char *c) 195 { 196 for (; *c; c++) 197 { 198 if ((*c == '\n') || (*c == '\r')) 199 { 200 *c = '\0'; 201 return; 202 } 203 } 204 } 205 206 #ifndef HAVE_STRNLEN 207 size_t strnlen(const char *s, size_t maxlen) 208 { 209 const char *end = memchr (s, 0, maxlen); 210 return end ? (size_t)(end - s) : maxlen; 211 } 212 #endif 213 214 #ifndef HAVE_STRLCPY 215 /** BSD'ish strlcpy(). 216 * The strlcpy() function copies up to size-1 characters from the 217 * NUL-terminated string src to dst, NUL-terminating the result. 218 * Return: total length of the string tried to create. 219 */ 220 size_t strlcpy(char *dst, const char *src, size_t size) 221 { 222 size_t len = strlen(src); 223 size_t ret = len; 224 225 if (size <= 0) 226 return 0; 227 if (len >= size) 228 len = size - 1; 229 memcpy(dst, src, len); 230 dst[len] = 0; 231 232 return ret; 233 } 234 #endif 235 236 #ifndef HAVE_STRLNCPY 237 /** BSD'ish strlncpy() - similar to strlcpy but never copies more then n characters. 238 */ 239 size_t strlncpy(char *dst, const char *src, size_t size, size_t n) 240 { 241 size_t len = strnlen(src, n); 242 size_t ret = len; 243 244 if (size <= 0) 245 return 0; 246 if (len >= size) 247 len = size - 1; 248 memcpy(dst, src, len); 249 dst[len] = 0; 250 251 return ret; 252 } 253 #endif 254 255 #ifndef HAVE_STRLCAT 256 /* BSD'ish strlcat(). 257 * The strlcat() function appends the NUL-terminated string src to the end of 258 * dst. It will append at most size - strlen(dst) - 1 bytes, NUL-terminating 259 * the result. 260 */ 261 size_t strlcat(char *dst, const char *src, size_t size) 262 { 263 size_t len1 = strlen(dst); 264 size_t len2 = strlen(src); 265 size_t ret = len1 + len2; 266 267 if (size <= len1) 268 return size; 269 if (len1 + len2 >= size) 270 len2 = size - (len1 + 1); 271 272 if (len2 > 0) { 273 memcpy(dst + len1, src, len2); 274 dst[len1 + len2] = 0; 275 } 276 277 return ret; 278 } 279 #endif 280 281 #ifndef HAVE_STRLNCAT 282 /** BSD'ish strlncat() - similar to strlcat but never cat more then n characters. 283 */ 284 size_t strlncat(char *dst, const char *src, size_t size, size_t n) 285 { 286 size_t len1 = strlen(dst); 287 size_t len2 = strnlen(src, n); 288 size_t ret = len1 + len2; 289 290 if (size <= len1) 291 return size; 292 293 if (len1 + len2 >= size) 294 len2 = size - (len1 + 1); 295 296 if (len2 > 0) { 297 memcpy(dst + len1, src, len2); 298 dst[len1 + len2] = 0; 299 } 300 301 return ret; 302 } 303 #endif 304 305 /** Like strlcpy but concat one letter */ 306 void strlcat_letter(char *buf, char c, size_t buflen) 307 { 308 int n = strlen(buf); 309 if (!buflen || (n >= buflen-1)) 310 return; 311 buf[n] = c; 312 buf[n+1] = '\0'; 313 } 314 315 /** Copies a string and ensure the new buffer is at most 'max' size, including NUL. 316 * The syntax is pretty much identical to strlcpy() except that 317 * the buffer is newly allocated. 318 * If you wonder why not use strndup() instead? 319 * I feel that mixing code with strlcpy() and strndup() would be 320 * rather confusing since strlcpy() assumes buffer size INCLUDING 321 * the nul byte and strndup() assumes WITHOUT the nul byte and 322 * will write one character extra. Hence this strldup(). -- Syzop 323 */ 324 char *strldup(const char *src, size_t max) 325 { 326 char *ptr; 327 int n; 328 329 if ((max == 0) || !src) 330 return NULL; 331 332 n = strlen(src); 333 if (n > max-1) 334 n = max-1; 335 336 ptr = safe_alloc(n+1); 337 memcpy(ptr, src, n); 338 ptr[n] = '\0'; 339 340 return ptr; 341 } 342 343 static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 344 static const char Pad64 = '='; 345 346 /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) 347 The following encoding technique is taken from RFC 1521 by Borenstein 348 and Freed. It is reproduced here in a slightly edited form for 349 convenience. 350 351 A 65-character subset of US-ASCII is used, enabling 6 bits to be 352 represented per printable character. (The extra 65th character, "=", 353 is used to signify a special processing function.) 354 355 The encoding process represents 24-bit groups of input bits as output 356 strings of 4 encoded characters. Proceeding from left to right, a 357 24-bit input group is formed by concatenating 3 8-bit input groups. 358 These 24 bits are then treated as 4 concatenated 6-bit groups, each 359 of which is translated into a single digit in the base64 alphabet. 360 361 Each 6-bit group is used as an index into an array of 64 printable 362 characters. The character referenced by the index is placed in the 363 output string. 364 365 Table 1: The Base64 Alphabet 366 367 Value Encoding Value Encoding Value Encoding Value Encoding 368 0 A 17 R 34 i 51 z 369 1 B 18 S 35 j 52 0 370 2 C 19 T 36 k 53 1 371 3 D 20 U 37 l 54 2 372 4 E 21 V 38 m 55 3 373 5 F 22 W 39 n 56 4 374 6 G 23 X 40 o 57 5 375 7 H 24 Y 41 p 58 6 376 8 I 25 Z 42 q 59 7 377 9 J 26 a 43 r 60 8 378 10 K 27 b 44 s 61 9 379 11 L 28 c 45 t 62 + 380 12 M 29 d 46 u 63 / 381 13 N 30 e 47 v 382 14 O 31 f 48 w (pad) = 383 15 P 32 g 49 x 384 16 Q 33 h 50 y 385 386 Special processing is performed if fewer than 24 bits are available 387 at the end of the data being encoded. A full encoding quantum is 388 always completed at the end of a quantity. When fewer than 24 input 389 bits are available in an input group, zero bits are added (on the 390 right) to form an integral number of 6-bit groups. Padding at the 391 end of the data is performed using the '=' character. 392 393 Since all base64 input is an integral number of octets, only the 394 ------------------------------------------------- 395 following cases can arise: 396 397 (1) the final quantum of encoding input is an integral 398 multiple of 24 bits; here, the final unit of encoded 399 output will be an integral multiple of 4 characters 400 with no "=" padding, 401 (2) the final quantum of encoding input is exactly 8 bits; 402 here, the final unit of encoded output will be two 403 characters followed by two "=" padding characters, or 404 (3) the final quantum of encoding input is exactly 16 bits; 405 here, the final unit of encoded output will be three 406 characters followed by one "=" padding character. 407 */ 408 409 /** Base64 encode data. 410 * @param src The data to encode (input) 411 * @param srclength The length of the data to encode (input length) 412 * @param target The output buffer to use (output) 413 * @param targetsize The length of the output buffer to use (maximum output length) 414 * @returns length of the targetsize, or -1 in case of error. 415 */ 416 int b64_encode(unsigned char const *src, size_t srclength, char *target, size_t targsize) 417 { 418 size_t datalength = 0; 419 u_char input[3]; 420 u_char output[4]; 421 size_t i; 422 423 while (2 < srclength) { 424 input[0] = *src++; 425 input[1] = *src++; 426 input[2] = *src++; 427 srclength -= 3; 428 429 output[0] = input[0] >> 2; 430 output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); 431 output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); 432 output[3] = input[2] & 0x3f; 433 434 if (datalength + 4 > targsize) 435 return (-1); 436 target[datalength++] = Base64[output[0]]; 437 target[datalength++] = Base64[output[1]]; 438 target[datalength++] = Base64[output[2]]; 439 target[datalength++] = Base64[output[3]]; 440 } 441 442 /* Now we worry about padding. */ 443 if (0 != srclength) { 444 /* Get what's left. */ 445 input[0] = input[1] = input[2] = '\0'; 446 for (i = 0; i < srclength; i++) 447 input[i] = *src++; 448 449 output[0] = input[0] >> 2; 450 output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); 451 output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); 452 453 if (datalength + 4 > targsize) 454 return (-1); 455 target[datalength++] = Base64[output[0]]; 456 target[datalength++] = Base64[output[1]]; 457 if (srclength == 1) 458 target[datalength++] = Pad64; 459 else 460 target[datalength++] = Base64[output[2]]; 461 target[datalength++] = Pad64; 462 } 463 if (datalength >= targsize) 464 return (-1); 465 target[datalength] = '\0'; /* Returned value doesn't count \0. */ 466 return (datalength); 467 } 468 469 /** Base64 decode a string. 470 * @param src The data to decode (input) 471 * @param srclength The length of the data to decode (input length) 472 * @param target The output buffer to use (output) 473 * @param targetsize The length of the output buffer to use (maximum output length) 474 * @returns length of the targetsize, or -1 in case of error. 475 * @note Skips whitespace, and hmm.. I think we don't require padding? Not sure. 476 */ 477 int b64_decode(char const *src, unsigned char *target, size_t targsize) 478 { 479 int tarindex, state, ch; 480 char *pos; 481 482 state = 0; 483 tarindex = 0; 484 485 while ((ch = *src++) != '\0') { 486 if (isspace(ch)) /* Skip whitespace anywhere. */ 487 continue; 488 489 if (ch == Pad64) 490 break; 491 492 pos = strchr(Base64, ch); 493 if (pos == 0) /* A non-base64 character. */ 494 return (-1); 495 496 switch (state) { 497 case 0: 498 if (target) { 499 if ((size_t)tarindex >= targsize) 500 return (-1); 501 target[tarindex] = (pos - Base64) << 2; 502 } 503 state = 1; 504 break; 505 case 1: 506 if (target) { 507 if ((size_t)tarindex + 1 >= targsize) 508 return (-1); 509 target[tarindex] |= (pos - Base64) >> 4; 510 target[tarindex+1] = ((pos - Base64) & 0x0f) 511 << 4 ; 512 } 513 tarindex++; 514 state = 2; 515 break; 516 case 2: 517 if (target) { 518 if ((size_t)tarindex + 1 >= targsize) 519 return (-1); 520 target[tarindex] |= (pos - Base64) >> 2; 521 target[tarindex+1] = ((pos - Base64) & 0x03) 522 << 6; 523 } 524 tarindex++; 525 state = 3; 526 break; 527 case 3: 528 if (target) { 529 if ((size_t)tarindex >= targsize) 530 return (-1); 531 target[tarindex] |= (pos - Base64); 532 } 533 tarindex++; 534 state = 0; 535 break; 536 default: 537 abort(); 538 } 539 } 540 541 /* 542 * We are done decoding Base-64 chars. Let's see if we ended 543 * on a byte boundary, and/or with erroneous trailing characters. 544 */ 545 546 if (ch == Pad64) { /* We got a pad char. */ 547 ch = *src++; /* Skip it, get next. */ 548 switch (state) { 549 case 0: /* Invalid = in first position */ 550 case 1: /* Invalid = in second position */ 551 return (-1); 552 553 case 2: /* Valid, means one byte of info */ 554 /* Skip any number of spaces. */ 555 for (; ch != '\0'; ch = *src++) 556 if (!isspace(ch)) 557 break; 558 /* Make sure there is another trailing = sign. */ 559 if (ch != Pad64) 560 return (-1); 561 ch = *src++; /* Skip the = */ 562 /* Fall through to "single trailing =" case. */ 563 /* FALLTHROUGH */ 564 565 case 3: /* Valid, means two bytes of info */ 566 /* 567 * We know this char is an =. Is there anything but 568 * whitespace after it? 569 */ 570 for (; ch != '\0'; ch = *src++) 571 if (!isspace(ch)) 572 return (-1); 573 574 /* 575 * Now make sure for cases 2 and 3 that the "extra" 576 * bits that slopped past the last full byte were 577 * zeros. If we don't check them, they become a 578 * subliminal channel. 579 */ 580 if (target && target[tarindex] != 0) 581 return (-1); 582 } 583 } else { 584 /* 585 * We ended by seeing the end of the string. Make sure we 586 * have no partial bytes lying around. 587 */ 588 if (state != 0) 589 return (-1); 590 } 591 592 return (tarindex); 593 } 594 595 /* Natural sort case comparison routines. The following copyright header applies: 596 strnatcmp.c -- Perform 'natural order' comparisons of strings in C. 597 Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net> 598 599 This software is provided 'as-is', without any express or implied 600 warranty. In no event will the authors be held liable for any damages 601 arising from the use of this software. 602 603 Permission is granted to anyone to use this software for any purpose, 604 including commercial applications, and to alter it and redistribute it 605 freely, subject to the following restrictions: 606 607 1. The origin of this software must not be misrepresented; you must not 608 claim that you wrote the original software. If you use this software 609 in a product, an acknowledgment in the product documentation would be 610 appreciated but is not required. 611 2. Altered source versions must be plainly marked as such, and must not be 612 misrepresented as being the original software. 613 3. This notice may not be removed or altered from any source distribution. 614 615 The code was modified / UnrealIRCderized by Bram Matthys ("Syzop"). 616 We always have unsigned char and this makes it possible to get rid 617 of various stuff.. also re-indent this monster. 618 */ 619 620 static int compare_right(char const *a, char const *b) 621 { 622 int bias = 0; 623 624 /* The longest run of digits wins. That aside, the greatest 625 * value wins, but we can't know that it will until we've scanned 626 * both numbers to know that they have the same magnitude, so we 627 * remember it in BIAS. 628 */ 629 for (;; a++, b++) 630 { 631 if (!isdigit(*a) && !isdigit(*b)) 632 return bias; 633 if (!isdigit(*a)) 634 return -1; 635 if (!isdigit(*b)) 636 return +1; 637 if (*a < *b) 638 { 639 if (!bias) 640 bias = -1; 641 } else 642 if (*a > *b) 643 { 644 if (!bias) 645 bias = +1; 646 } else 647 if (!*a && !*b) 648 return bias; 649 } 650 651 return 0; 652 } 653 654 655 static int compare_left(char const *a, char const *b) 656 { 657 /* Compare two left-aligned numbers: the first to have a 658 * different value wins. 659 */ 660 for (;; a++, b++) 661 { 662 if (!isdigit(*a) && !isdigit(*b)) 663 return 0; 664 if (!isdigit(*a)) 665 return -1; 666 if (!isdigit(*b)) 667 return +1; 668 if (*a < *b) 669 return -1; 670 if (*a > *b) 671 return +1; 672 } 673 674 return 0; 675 } 676 677 static int strnatcmp0(char const *a, char const *b, int fold_case) 678 { 679 int ai, bi; 680 char ca, cb; 681 int fractional, result; 682 683 ai = bi = 0; 684 while (1) 685 { 686 ca = a[ai]; cb = b[bi]; 687 688 /* skip over leading spaces or zeros */ 689 while (isspace(ca)) 690 ca = a[++ai]; 691 692 while (isspace(cb)) 693 cb = b[++bi]; 694 695 /* process run of digits */ 696 if (isdigit(ca) && isdigit(cb)) 697 { 698 fractional = (ca == '0' || cb == '0'); 699 if (fractional) 700 { 701 if ((result = compare_left(a+ai, b+bi)) != 0) 702 return result; 703 } else { 704 if ((result = compare_right(a+ai, b+bi)) != 0) 705 return result; 706 } 707 } 708 709 if (!ca && !cb) 710 { 711 /* The strings compare the same. Perhaps the caller 712 * will want to call strcmp to break the tie. 713 */ 714 return 0; 715 } 716 717 if (fold_case) 718 { 719 ca = toupper(ca); 720 cb = toupper(cb); 721 } 722 723 if (ca < cb) 724 return -1; 725 726 if (ca > cb) 727 return +1; 728 729 ai++; 730 bi++; 731 } 732 } 733 734 /** Like strcmp() but with "natural sort", so that for example 735 * the string "1.4.10" is seen as higher than "1.4.9" 736 * This is the case sensitive version. 737 */ 738 int strnatcmp(char const *a, char const *b) 739 { 740 return strnatcmp0(a, b, 0); 741 } 742 743 /** Like strcmp() but with "natural sort", so that for example 744 * the string "1.4.10" is seen as higher than "1.4.9" 745 * This is the case insensitive version. 746 */ 747 int strnatcasecmp(char const *a, char const *b) 748 { 749 return strnatcmp0(a, b, 1); 750 } 751 752 /* End of natural sort case comparison functions */ 753 754 /* Memory allocation routines */ 755 756 /** Allocate memory - should always be used instead of malloc/calloc. 757 * @param size How many bytes to allocate 758 * @returns A pointer to the newly allocated memory. 759 * @note If out of memory then the IRCd will exit. 760 */ 761 void *safe_alloc(size_t size) 762 { 763 void *p; 764 if (size == 0) 765 return NULL; 766 p = calloc(1, size); 767 if (!p) 768 outofmemory(size); 769 return p; 770 } 771 772 /** Safely duplicate a string */ 773 char *our_strdup(const char *str) 774 { 775 char *ret = strdup(str); 776 if (!ret) 777 outofmemory(strlen(str)); 778 return ret; 779 } 780 781 /** Safely duplicate a string with a maximum size */ 782 char *our_strldup(const char *str, size_t max) 783 { 784 char *ret = strldup(str, max); 785 if (!ret) 786 outofmemory(MAX(strlen(str), max)); 787 return ret; 788 } 789 790 /** Called when out of memory */ 791 void outofmemory(size_t bytes) 792 { 793 static int log_attempt = 1; 794 795 if (log_attempt) 796 { 797 /* This will probably fail, but we can try... */ 798 unreal_log(ULOG_ERROR, "main", "OUT_OF_MEMORY", NULL, 799 "Out of memory while trying to allocate $bytes bytes!", 800 log_data_integer("bytes", bytes)); 801 log_attempt = 0; 802 } 803 exit(7); 804 } 805 806 /** Allocate sensitive memory - this should only be used for HIGHLY sensitive data, since 807 * it wastes 8192+ bytes even if only asked to allocate for example 32 bytes (this is by design). 808 * @param size How many bytes to allocate 809 * @returns A pointer to the newly allocated memory. 810 * @note If out of memory then the IRCd will exit. 811 */ 812 void *safe_alloc_sensitive(size_t size) 813 { 814 void *p; 815 if (size == 0) 816 return NULL; 817 p = sodium_malloc(((size/32)*32)+32); 818 if (!p) 819 outofmemory(size); 820 memset(p, 0, size); 821 return p; 822 } 823 824 /** Safely duplicate a string */ 825 char *our_strdup_sensitive(const char *str) 826 { 827 char *ret = safe_alloc_sensitive(strlen(str)+1); 828 if (!ret) 829 outofmemory(strlen(str)); 830 strcpy(ret, str); /* safe, see above */ 831 return ret; 832 } 833 834 /** Returns a unique filename in the specified directory 835 * using the specified suffix. The returned value will 836 * be of the form <dir>/<random-hex>.<suffix> 837 */ 838 char *unreal_mktemp(const char *dir, const char *suffix) 839 { 840 FILE *fd; 841 unsigned int i; 842 static char tempbuf[PATH_MAX+1]; 843 844 for (i = 500; i > 0; i--) 845 { 846 snprintf(tempbuf, PATH_MAX, "%s/%X.%s", dir, getrandom32(), suffix); 847 fd = fopen(tempbuf, "r"); 848 if (!fd) 849 return tempbuf; 850 fclose(fd); 851 } 852 config_error("Unable to create temporary file in directory '%s': %s", 853 dir, strerror(errno)); /* eg: permission denied :p */ 854 return NULL; 855 } 856 857 /** Returns the path portion of the given path/file 858 * in the specified location (must be at least PATH_MAX bytes). 859 */ 860 char *unreal_getpathname(const char *filepath, char *path) 861 { 862 const char *end = filepath+strlen(filepath); 863 864 while (*end != '\\' && *end != '/' && end > filepath) 865 end--; 866 if (end == filepath) 867 path = NULL; 868 else 869 { 870 int size = end-filepath; 871 if (size >= PATH_MAX) 872 path = NULL; 873 else 874 { 875 memcpy(path, filepath, size); 876 path[size] = 0; 877 } 878 } 879 return path; 880 } 881 882 /** Returns the filename portion of the given path. 883 * The original string is not modified 884 */ 885 const char *unreal_getfilename(const char *path) 886 { 887 int len = strlen(path); 888 const char *end; 889 890 if (!len) 891 return NULL; 892 893 end = path+len-1; 894 if (*end == '\\' || *end == '/') 895 return NULL; 896 897 while (end > path) 898 { 899 if (*end == '\\' || *end == '/') 900 { 901 end++; 902 break; 903 } 904 end--; 905 } 906 907 return end; 908 } 909 910 /** Wrapper for mkdir() so you don't need ifdefs everywhere for Windows. 911 * @returns 0 on failure!! (like mkdir) 912 */ 913 int unreal_mkdir(const char *pathname, mode_t mode) 914 { 915 #ifdef _WIN32 916 return mkdir(pathname); 917 #else 918 return mkdir(pathname, mode); 919 #endif 920 } 921 922 /** Create the entire directory structure. 923 * @param dname The directory name, eg /home/irc/unrealircd/logs/2022/08/05 924 * @param mode The mode to create with, eg 0777. Ignored on Windows. 925 * @returns 1 on success, 0 on failure. 926 */ 927 int unreal_create_directory_structure(const char *dname, mode_t mode) 928 { 929 if (unreal_mkdir(dname, mode) == 0) 930 { 931 /* Ok, that failed as well, we have some work to do: 932 * for every path element run mkdir(). 933 */ 934 int lastresult; 935 char buf[512], *p; 936 strlcpy(buf, dname, sizeof(buf)); /* work on a copy */ 937 for (p=strchr(buf+1, '/'); p; p=strchr(p+1, '/')) 938 { 939 *p = '\0'; 940 unreal_mkdir(buf,mode); 941 *p = '/'; 942 } 943 /* Finally, try the complete path */ 944 if (unreal_mkdir(dname, mode)) 945 return 0; /* failed */ 946 /* fallthrough.... */ 947 } 948 return 1; /* success */ 949 } 950 951 /** Create entire directory structure for a path with a filename. 952 * @param fname The full path name, eg /home/irc/unrealircd/logs/2022/08/05/ircd.log 953 * @param mode The mode to create with, eg 0777. Ignored on Windows. 954 * @notes This is used as an easier way to call unreal_create_directory_structure() 955 * if you have a filename instead of the directory part. 956 * @returns 1 on success, 0 on failure. 957 */ 958 int unreal_create_directory_structure_for_file(const char *fname, mode_t mode) 959 { 960 char buf[PATH_MAX+1]; 961 const char *path = unreal_getpathname(fname, buf); 962 if (!path) 963 return 0; 964 return unreal_create_directory_structure(path, mode); 965 } 966 967 /** Returns the special module tmp name for a given path. 968 * The original string is not modified. 969 */ 970 const char *unreal_getmodfilename(const char *path) 971 { 972 static char ret[512]; 973 char buf[512]; 974 char *p; 975 char *name = NULL; 976 char *directory = NULL; 977 978 if (BadPtr(path)) 979 return path; 980 981 strlcpy(buf, path, sizeof(buf)); 982 983 /* Backtrack... */ 984 for (p = buf + strlen(buf); p >= buf; p--) 985 { 986 if ((*p == '/') || (*p == '\\')) 987 { 988 name = p+1; 989 *p = '\0'; 990 directory = buf; /* fallback */ 991 for (; p >= buf; p--) 992 { 993 if ((*p == '/') || (*p == '\\')) 994 { 995 directory = p + 1; 996 break; 997 } 998 } 999 break; 1000 } 1001 } 1002 1003 if (!name) 1004 name = buf; 1005 1006 if (!directory || !strcmp(directory, "modules")) 1007 snprintf(ret, sizeof(ret), "%s", name); 1008 else 1009 snprintf(ret, sizeof(ret), "%s.%s", directory, name); 1010 1011 return ret; 1012 } 1013 1014 /* Returns a consistent filename for the cache/ directory. 1015 * Returned value will be like: cache/<hash of url> 1016 */ 1017 const char *unreal_mkcache(const char *url) 1018 { 1019 static char tempbuf[PATH_MAX+1]; 1020 char tmp2[128]; 1021 1022 snprintf(tempbuf, PATH_MAX, "%s/%s", CACHEDIR, sha256hash(tmp2, url, strlen(url))); 1023 return tempbuf; 1024 } 1025 1026 /** Returns 1 if a cached version of the url exists, otherwise 0. */ 1027 int has_cached_version(const char *url) 1028 { 1029 return file_exists(unreal_mkcache(url)); 1030 } 1031 1032 /** Used to blow away result of bad copy or cancel file copy */ 1033 void cancel_copy(int srcfd, int destfd, const char *dest) 1034 { 1035 close(srcfd); 1036 close(destfd); 1037 unlink(dest); 1038 } 1039 1040 /** Copys the contents of the src file to the dest file. 1041 * The dest file will have permissions r-x------ 1042 */ 1043 int unreal_copyfile(const char *src, const char *dest) 1044 { 1045 char buf[2048]; 1046 time_t mtime; 1047 int srcfd, destfd, len; 1048 1049 mtime = unreal_getfilemodtime(src); 1050 1051 #ifndef _WIN32 1052 srcfd = open(src, O_RDONLY); 1053 #else 1054 srcfd = open(src, _O_RDONLY|_O_BINARY); 1055 #endif 1056 1057 if (srcfd < 0) 1058 { 1059 config_error("Unable to open file '%s': %s", src, strerror(errno)); 1060 return 0; 1061 } 1062 1063 #ifndef _WIN32 1064 #if defined(DEFAULT_PERMISSIONS) && (DEFAULT_PERMISSIONS != 0) 1065 destfd = open(dest, O_WRONLY|O_CREAT, DEFAULT_PERMISSIONS); 1066 #else 1067 destfd = open(dest, O_WRONLY|O_CREAT, S_IRUSR | S_IXUSR); 1068 #endif /* DEFAULT_PERMISSIONS */ 1069 #else 1070 destfd = open(dest, _O_BINARY|_O_WRONLY|_O_CREAT, _S_IWRITE); 1071 #endif /* _WIN32 */ 1072 if (destfd < 0) 1073 { 1074 config_error("Unable to create file '%s': %s", dest, strerror(errno)); 1075 close(srcfd); 1076 return 0; 1077 } 1078 1079 while ((len = read(srcfd, buf, 1023)) > 0) 1080 if (write(destfd, buf, len) != len) 1081 { 1082 config_error("Write error to file '%s': %s [not enough free hd space / quota? need several mb's!]", 1083 dest, strerror(ERRNO)); 1084 cancel_copy(srcfd,destfd,dest); 1085 return 0; 1086 } 1087 1088 if (len < 0) /* very unusual.. perhaps an I/O error */ 1089 { 1090 config_error("Read error from file '%s': %s", src, strerror(errno)); 1091 cancel_copy(srcfd,destfd,dest); 1092 return 0; 1093 } 1094 1095 close(srcfd); 1096 close(destfd); 1097 unreal_setfilemodtime(dest, mtime); 1098 return 1; 1099 } 1100 1101 /** Same as unreal_copyfile, but with an option to try hardlinking first */ 1102 int unreal_copyfileex(const char *src, const char *dest, int tryhardlink) 1103 { 1104 unlink(dest); 1105 #ifndef _WIN32 1106 /* Try a hardlink first... */ 1107 if (tryhardlink && !link(src, dest)) 1108 return 1; /* success */ 1109 #endif 1110 return unreal_copyfile(src, dest); 1111 } 1112 1113 /** Set the modification time on a file */ 1114 void unreal_setfilemodtime(const char *filename, time_t mtime) 1115 { 1116 #ifndef _WIN32 1117 struct utimbuf utb; 1118 utb.actime = utb.modtime = mtime; 1119 utime(filename, &utb); 1120 #else 1121 FILETIME mTime; 1122 LONGLONG llValue; 1123 HANDLE hFile = CreateFile(filename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 1124 FILE_ATTRIBUTE_NORMAL, NULL); 1125 if (hFile == INVALID_HANDLE_VALUE) 1126 return; 1127 llValue = Int32x32To64(mtime, 10000000) + 116444736000000000; 1128 mTime.dwLowDateTime = (long)llValue; 1129 mTime.dwHighDateTime = llValue >> 32; 1130 1131 SetFileTime(hFile, &mTime, &mTime, &mTime); 1132 CloseHandle(hFile); 1133 #endif 1134 } 1135 1136 /** Get the modification time ("last modified") of a file */ 1137 time_t unreal_getfilemodtime(const char *filename) 1138 { 1139 #ifndef _WIN32 1140 struct stat sb; 1141 if (stat(filename, &sb)) 1142 return 0; 1143 return sb.st_mtime; 1144 #else 1145 /* See how much more fun WinAPI programming is??? */ 1146 FILETIME cTime; 1147 SYSTEMTIME sTime, lTime; 1148 ULARGE_INTEGER fullTime; 1149 time_t result; 1150 HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 1151 FILE_ATTRIBUTE_NORMAL, NULL); 1152 if (hFile == INVALID_HANDLE_VALUE) 1153 return 0; 1154 if (!GetFileTime(hFile, NULL, NULL, &cTime)) 1155 return 0; 1156 1157 CloseHandle(hFile); 1158 1159 FileTimeToSystemTime(&cTime, &sTime); 1160 SystemTimeToTzSpecificLocalTime(NULL, &sTime, &lTime); 1161 SystemTimeToFileTime(&sTime, &cTime); 1162 1163 fullTime.LowPart = cTime.dwLowDateTime; 1164 fullTime.HighPart = cTime.dwHighDateTime; 1165 fullTime.QuadPart -= 116444736000000000; 1166 fullTime.QuadPart /= 10000000; 1167 1168 return fullTime.LowPart; 1169 #endif 1170 } 1171 1172 #ifndef AF_INET6 1173 #define AF_INET6 AF_MAX+1 /* just to let this compile */ 1174 #endif 1175 1176 /** Encode an IP string (eg: "1.2.3.4") to a BASE64 encoded value for S2S traffic */ 1177 const char *encode_ip(const char *ip) 1178 { 1179 static char retbuf[25]; /* returned string */ 1180 char addrbuf[16]; 1181 1182 if (!ip) 1183 return "*"; 1184 1185 if (strchr(ip, ':')) 1186 { 1187 /* IPv6 (likely) */ 1188 inet_pton(AF_INET6, ip, addrbuf); 1189 /* hack for IPv4-in-IPv6 (::ffff:1.2.3.4) */ 1190 if (addrbuf[0] == 0 && addrbuf[1] == 0 && addrbuf[2] == 0 && addrbuf[3] == 0 1191 && addrbuf[4] == 0 && addrbuf[5] == 0 && addrbuf[6] == 0 1192 && addrbuf[7] == 0 && addrbuf[8] == 0 && addrbuf[9] == 0 1193 && addrbuf[10] == 0xff && addrbuf[11] == 0xff) 1194 { 1195 b64_encode(&addrbuf[12], sizeof(struct in_addr), retbuf, sizeof(retbuf)); 1196 } else { 1197 b64_encode(addrbuf, 16, retbuf, sizeof(retbuf)); 1198 } 1199 } 1200 else 1201 { 1202 /* IPv4 */ 1203 inet_pton(AF_INET, ip, addrbuf); 1204 b64_encode((char *)&addrbuf, sizeof(struct in_addr), retbuf, sizeof(retbuf)); 1205 } 1206 return retbuf; 1207 } 1208 1209 /** Decode a BASE64 encoded string to an IP address string. Used for S2S traffic. */ 1210 const char *decode_ip(const char *buf) 1211 { 1212 int n; 1213 char targ[25]; 1214 static char result[64]; 1215 1216 n = b64_decode(buf, targ, sizeof(targ)); 1217 if (n == 4) /* should be IPv4 */ 1218 return inetntop(AF_INET, targ, result, sizeof(result)); 1219 else if (n == 16) /* should be IPv6 */ 1220 return inetntop(AF_INET6, targ, result, sizeof(result)); 1221 else /* Error! */ 1222 return NULL; 1223 } 1224 1225 /* IPv6 stuff */ 1226 1227 #ifndef IN6ADDRSZ 1228 #define IN6ADDRSZ 16 1229 #endif 1230 1231 #ifndef INT16SZ 1232 #define INT16SZ 2 1233 #endif 1234 1235 #ifndef INADDRSZ 1236 #define INADDRSZ 4 1237 #endif 1238 1239 #ifdef _WIN32 1240 /* Microsoft makes things nice and fun for us! */ 1241 struct u_WSA_errors { 1242 int error_code; 1243 char *error_string; 1244 }; 1245 1246 /* Must be sorted ascending by error code */ 1247 struct u_WSA_errors WSAErrors[] = { 1248 { WSAEINTR, "Interrupted system call" }, 1249 { WSAEBADF, "Bad file number" }, 1250 { WSAEACCES, "Permission denied" }, 1251 { WSAEFAULT, "Bad address" }, 1252 { WSAEINVAL, "Invalid argument" }, 1253 { WSAEMFILE, "Too many open sockets" }, 1254 { WSAEWOULDBLOCK, "Operation would block" }, 1255 { WSAEINPROGRESS, "Operation now in progress" }, 1256 { WSAEALREADY, "Operation already in progress" }, 1257 { WSAENOTSOCK, "Socket operation on non-socket" }, 1258 { WSAEDESTADDRREQ, "Destination address required" }, 1259 { WSAEMSGSIZE, "Message too long" }, 1260 { WSAEPROTOTYPE, "Protocol wrong type for socket" }, 1261 { WSAENOPROTOOPT, "Bad protocol option" }, 1262 { WSAEPROTONOSUPPORT, "Protocol not supported" }, 1263 { WSAESOCKTNOSUPPORT, "Socket type not supported" }, 1264 { WSAEOPNOTSUPP, "Operation not supported on socket" }, 1265 { WSAEPFNOSUPPORT, "Protocol family not supported" }, 1266 { WSAEAFNOSUPPORT, "Address family not supported" }, 1267 { WSAEADDRINUSE, "Address already in use" }, 1268 { WSAEADDRNOTAVAIL, "Can't assign requested address" }, 1269 { WSAENETDOWN, "Network is down" }, 1270 { WSAENETUNREACH, "Network is unreachable" }, 1271 { WSAENETRESET, "Net connection reset" }, 1272 { WSAECONNABORTED, "Software caused connection abort" }, 1273 { WSAECONNRESET, "Connection reset by peer" }, 1274 { WSAENOBUFS, "No buffer space available" }, 1275 { WSAEISCONN, "Socket is already connected" }, 1276 { WSAENOTCONN, "Socket is not connected" }, 1277 { WSAESHUTDOWN, "Can't send after socket shutdown" }, 1278 { WSAETOOMANYREFS, "Too many references, can't splice" }, 1279 { WSAETIMEDOUT, "Connection timed out" }, 1280 { WSAECONNREFUSED, "Connection refused" }, 1281 { WSAELOOP, "Too many levels of symbolic links" }, 1282 { WSAENAMETOOLONG, "File name too long" }, 1283 { WSAEHOSTDOWN, "Host is down" }, 1284 { WSAEHOSTUNREACH, "No route to host" }, 1285 { WSAENOTEMPTY, "Directory not empty" }, 1286 { WSAEPROCLIM, "Too many processes" }, 1287 { WSAEUSERS, "Too many users" }, 1288 { WSAEDQUOT, "Disc quota exceeded" }, 1289 { WSAESTALE, "Stale NFS file handle" }, 1290 { WSAEREMOTE, "Too many levels of remote in path" }, 1291 { WSASYSNOTREADY, "Network subsystem is unavailable" }, 1292 { WSAVERNOTSUPPORTED, "Winsock version not supported" }, 1293 { WSANOTINITIALISED, "Winsock not yet initialized" }, 1294 { WSAHOST_NOT_FOUND, "Host not found" }, 1295 { WSATRY_AGAIN, "Non-authoritative host not found" }, 1296 { WSANO_RECOVERY, "Non-recoverable errors" }, 1297 { WSANO_DATA, "Valid name, no data record of requested type" }, 1298 { WSAEDISCON, "Graceful disconnect in progress" }, 1299 { WSASYSCALLFAILURE, "System call failure" }, 1300 { 0,NULL} 1301 }; 1302 1303 /** Get socket error string */ 1304 const char *sock_strerror(int error) 1305 { 1306 static char unkerr[64]; 1307 int start = 0; 1308 int stop = sizeof(WSAErrors)/sizeof(WSAErrors[0])-1; 1309 int mid; 1310 1311 if (!error) 1312 return "No error"; 1313 1314 if (error < WSABASEERR) /* Just a regular error code */ 1315 return strerror(error); 1316 1317 /* Microsoft decided not to use sequential numbers for the error codes, 1318 * so we can't just use the array index for the code. But, at least 1319 * use a binary search to make it as fast as possible. 1320 */ 1321 while (start <= stop) 1322 { 1323 mid = (start+stop)/2; 1324 if (WSAErrors[mid].error_code > error) 1325 stop = mid-1; 1326 1327 else if (WSAErrors[mid].error_code < error) 1328 start = mid+1; 1329 else 1330 return WSAErrors[mid].error_string; 1331 } 1332 snprintf(unkerr, sizeof(unkerr), "Unknown Error: %d", error); 1333 return unkerr; 1334 } 1335 #endif 1336 1337 /** Build a string and replace $variables where needed. 1338 * See src/modules/blacklist.c for an example. 1339 * @param inbuf The input string 1340 * @param outbuf The output string 1341 * @param len The maximum size of the output string (including NUL) 1342 * @param name Array of variables names 1343 * @param value Array of variable values 1344 */ 1345 void buildvarstring(const char *inbuf, char *outbuf, size_t len, const char *name[], const char *value[]) 1346 { 1347 const char *i, *p; 1348 char *o; 1349 int left = len - 1; 1350 int cnt, found; 1351 1352 #ifdef DEBUGMODE 1353 if (len <= 0) 1354 abort(); 1355 #endif 1356 1357 for (i = inbuf, o = outbuf; *i; i++) 1358 { 1359 if (*i == '$') 1360 { 1361 i++; 1362 1363 /* $$ = literal $ */ 1364 if (*i == '$') 1365 goto literal; 1366 1367 if (!isalnum(*i)) 1368 { 1369 /* What do we do with things like '$/' ? -- treat literal */ 1370 i--; 1371 goto literal; 1372 } 1373 1374 /* find termination */ 1375 for (p=i; isalnum(*p); p++); 1376 1377 /* find variable name in list */ 1378 found = 0; 1379 for (cnt = 0; name[cnt]; cnt++) 1380 if (!strncasecmp(name[cnt], i, strlen(name[cnt]))) 1381 { 1382 /* Found */ 1383 found = 1; 1384 1385 if (!BadPtr(value[cnt])) 1386 { 1387 strlcpy(o, value[cnt], left); 1388 left -= strlen(value[cnt]); /* may become <0 */ 1389 if (left <= 0) 1390 return; /* return - don't write \0 to 'o'. ensured by strlcpy already */ 1391 o += strlen(value[cnt]); /* value entirely written */ 1392 } 1393 1394 break; /* done */ 1395 } 1396 1397 if (!found) 1398 { 1399 /* variable name does not exist -- treat literal */ 1400 i--; 1401 goto literal; 1402 } 1403 1404 /* value written. we're done. */ 1405 i = p - 1; 1406 continue; 1407 } 1408 literal: 1409 if (!left) 1410 break; 1411 *o++ = *i; 1412 left--; 1413 if (!left) 1414 break; 1415 } 1416 *o = '\0'; 1417 } 1418 1419 /** Return the PCRE2 library version in use */ 1420 const char *pcre2_version(void) 1421 { 1422 static char buf[256]; 1423 1424 strlcpy(buf, "PCRE2 ", sizeof(buf)); 1425 pcre2_config(PCRE2_CONFIG_VERSION, buf+6); 1426 return buf; 1427 } 1428 1429 1430 #ifdef _WIN32 1431 /** POSIX gettimeofday function for Windows (ignoring timezones) */ 1432 int gettimeofday(struct timeval *tp, void *tzp) 1433 { 1434 // This magic number is the number of 100 nanosecond intervals since January 1, 1601 (UTC) 1435 // until 00:00:00 January 1, 1970 1436 static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL); 1437 1438 SYSTEMTIME system_time; 1439 FILETIME file_time; 1440 uint64_t time; 1441 1442 GetSystemTime( &system_time ); 1443 SystemTimeToFileTime( &system_time, &file_time ); 1444 time = ((uint64_t)file_time.dwLowDateTime ) ; 1445 time += ((uint64_t)file_time.dwHighDateTime) << 32; 1446 1447 tp->tv_sec = (long) ((time - EPOCH) / 10000000L); 1448 tp->tv_usec = (long) (system_time.wMilliseconds * 1000); 1449 return 0; 1450 } 1451 #endif 1452 1453 /** Get the numer of characters per line that fit on the terminal (the width) */ 1454 int get_terminal_width(void) 1455 { 1456 #if defined(_WIN32) || !defined(TIOCGWINSZ) 1457 return 80; 1458 #else 1459 struct winsize w; 1460 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); 1461 return w.ws_col; 1462 #endif 1463 } 1464 1465 #if defined(__GNUC__) 1466 #pragma GCC diagnostic push 1467 #pragma GCC diagnostic ignored "-Wformat-nonliteral" 1468 #endif 1469 1470 /** Like strftime() but easier. */ 1471 char *unreal_strftime(const char *str) 1472 { 1473 time_t t; 1474 struct tm *tmp; 1475 static char buf[512]; 1476 1477 t = time(NULL); 1478 tmp = localtime(&t); 1479 if (!tmp || !strftime(buf, sizeof(buf), str, tmp)) 1480 { 1481 /* If anything fails bigtime, then return the format string */ 1482 strlcpy(buf, str, sizeof(buf)); 1483 return buf; 1484 } 1485 return buf; 1486 } 1487 1488 #if defined(__GNUC__) 1489 #pragma GCC diagnostic pop 1490 #endif 1491 1492 /** Convert a string to lowercase - with separate input/output buffer */ 1493 void strtolower_safe(char *dst, const char *src, int size) 1494 { 1495 if (!size) 1496 return; /* size of 0 is unworkable */ 1497 size--; /* for \0 */ 1498 1499 for (; *src && size; src++) 1500 { 1501 *dst++ = tolower(*src); 1502 size--; 1503 } 1504 *dst = '\0'; 1505 } 1506 1507 /** Convert a string to lowercase - modifying existing string */ 1508 void strtolower(char *str) 1509 { 1510 for (; *str; str++) 1511 *str = tolower(*str); 1512 } 1513 1514 /** Convert a string to uppercase - with separate input/output buffer */ 1515 void strtoupper_safe(char *dst, const char *src, int size) 1516 { 1517 if (!size) 1518 return; /* size of 0 is unworkable */ 1519 size--; /* for \0 */ 1520 1521 for (; *src && size; src++) 1522 { 1523 *dst++ = toupper(*src); 1524 size--; 1525 } 1526 *dst = '\0'; 1527 } 1528 1529 /** Convert a string to uppercase - modifying existing string */ 1530 void strtoupper(char *str) 1531 { 1532 for (; *str; str++) 1533 *str = toupper(*str); 1534 }