2 * IRC - Internet Relay Chat, ircd/ircd_string.c
3 * Copyright (C) 1999 Thomas Helvey
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)
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.
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.
20 * @brief Implementation of string operations.
25 #include "ircd_string.h"
26 #include "ircd_defs.h"
27 #include "ircd_chattr.h"
31 /* #include <assert.h> -- Now using assert in ircd_log.h */
33 #include <sys/types.h>
34 #include <netinet/in.h>
37 * include the character attribute tables here
39 #include "chattr.tab.c"
41 /** Check whether \a str contains wildcard characters.
42 * @param[in] str String that might contain wildcards.
43 * @return Non-zero if \a str contains naked (non-escaped) wildcards,
44 * zero if there are none or if they are all escaped.
46 int string_has_wildcards(const char* str)
49 for ( ; *str; ++str) {
54 else if ('*' == *str || '?' == *str)
60 /** Split a string on certain delimiters.
61 * This is a reentrant version of normal strtok(). The first call for
62 * a particular input string must use a non-NULL \a str; *save will be
63 * initialized based on that. Later calls must use a NULL \a str;
64 * *save will be updated.
65 * @param[in,out] save Pointer to a position indicator.
66 * @param[in] str Pointer to the input string, or NULL to continue.
67 * @param[in] fs String that lists token delimiters.
68 * @return Next token in input string, or NULL if no tokens remain.
70 char* ircd_strtok(char **save, char *str, char *fs)
72 char *pos = *save; /* keep last position across calls */
76 pos = str; /* new string scan */
78 while (pos && *pos && strchr(fs, *pos) != NULL)
79 pos++; /* skip leading separators */
82 return (pos = *save = NULL); /* string contains only sep's */
84 tmp = pos; /* now, keep position of the token */
86 while (*pos && strchr(fs, *pos) == NULL)
87 pos++; /* skip content of the token */
90 *pos++ = '\0'; /* remove first sep after the token */
92 pos = NULL; /* end of string */
98 /** Rewrite a comma-delimited list of items to remove duplicates.
99 * @param[in,out] buffer Comma-delimited list.
100 * @return The input buffer \a buffer.
102 char* canonize(char* buffer)
104 static char cbuf[BUFSIZE];
114 for (s = ircd_strtok(&p, buffer, ","); s; s = ircd_strtok(&p, NULL, ","))
119 for (t = ircd_strtok(&p2, cbuf, ","); t; t = ircd_strtok(&p2, NULL, ","))
120 if (0 == ircd_strcmp(s, t))
143 /** Copy one string to another, not to exceed a certain length.
144 * @param[in] s1 Output buffer.
145 * @param[in] s2 Source buffer.
146 * @param[in] n Maximum number of bytes to write, plus one.
147 * @return The original input buffer \a s1.
149 char* ircd_strncpy(char* s1, const char* s2, size_t n)
157 while (s < endp && (*s++ = *s2++))
164 NTL_HDR_strChattr { NTL_SRC_strChattr }
165 NTL_HDR_strCasediff { NTL_SRC_strCasediff }
166 #endif /* !FORCEINLINE */
169 * Other functions visible externally
172 /** Case insensitive string comparison.
173 * @param[in] a First string to compare.
174 * @param[in] b Second string to compare.
175 * @return Less than, equal to, or greater than zero if \a a is lexicographically less than, equal to, or greater than \a b.
177 int ircd_strcmp(const char *a, const char *b)
181 while (ToLower(*ra) == ToLower(*rb)) {
187 return (ToLower(*ra) - ToLower(*rb));
190 /** Case insensitive comparison of the starts of two strings.
191 * @param[in] a First string to compare.
192 * @param[in] b Second string to compare.
193 * @param[in] n Maximum number of characters to compare.
194 * @return Less than, equal to, or greater than zero if \a a is
195 * lexicographically less than, equal to, or greater than \a b.
197 int ircd_strncmp(const char *a, const char *b, size_t n)
204 while (ToLower(*ra) == ToLower(*rb)) {
205 if (!*ra++ || !left--)
210 return (ToLower(*ra) - ToLower(*rb));
213 /** Fill a vector of distinct names from a delimited input list.
214 * Empty tokens (when \a token occurs at the start or end of \a list,
215 * or when \a token occurs adjacent to itself) are ignored. When
216 * \a size tokens have been written to \a vector, the rest of the
218 * Unlike token_vector(), if a token repeats an earlier token, it is
220 * @param[in,out] names Input buffer.
221 * @param[in] token Delimiter used to split \a list.
222 * @param[out] vector Output vector.
223 * @param[in] size Maximum number of elements to put in \a vector.
224 * @return Number of elements written to \a vector.
226 int unique_name_vector(char* names, char token, char** vector, int size)
238 * ignore spurious tokens
240 while (token == *start)
243 for (end = strchr(start, token); end; end = strchr(start, token)) {
246 * ignore spurious tokens
248 while (token == *end)
250 for (i = 0; i < count; ++i) {
251 if (0 == ircd_strcmp(vector[i], start))
255 vector[count++] = start;
262 for (i = 0; i < count; ++i)
263 if (0 == ircd_strcmp(vector[i], start))
265 vector[count++] = start;
270 /** Fill a vector of tokens from a delimited input list.
271 * Empty tokens (when \a token occurs at the start or end of \a list,
272 * or when \a token occurs adjacent to itself) are ignored. When
273 * \a size tokens have been written to \a vector, the rest of the
275 * @param[in,out] names Input buffer.
276 * @param[in] token Delimiter used to split \a list.
277 * @param[out] vector Output vector.
278 * @param[in] size Maximum number of elements to put in \a vector.
279 * @return Number of elements written to \a vector.
281 int token_vector(char* names, char token, char** vector, int size)
291 vector[count++] = start;
292 for (end = strchr(start, token); end; end = strchr(start, token)) {
296 vector[count++] = start;
305 /** Copy all or part of the hostname in a string to another string.
306 * If \a userhost contains an '\@', the remaining portion is used;
307 * otherwise, the whole \a userhost is used.
308 * @param[out] buf Output buffer.
309 * @param[in] userhost user\@hostname or hostname string.
310 * @param[in] len Maximum number of bytes to write to \a host.
311 * @return The output buffer \a buf.
313 char* host_from_uh(char* buf, const char* userhost, size_t len)
318 assert(0 != userhost);
320 if ((s = strchr(userhost, '@')))
324 ircd_strncpy(buf, s, len);
330 * this new faster inet_ntoa was ripped from:
331 * From: Thomas Helvey <tomh@inxpress.net>
333 /** Array of text strings for dotted quads. */
334 static const char* IpQuadTab[] =
336 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
337 "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
338 "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
339 "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
340 "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
341 "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
342 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
343 "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
344 "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
345 "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
346 "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
347 "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
348 "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
349 "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
350 "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
351 "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
352 "160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
353 "170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
354 "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
355 "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
356 "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
357 "210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
358 "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
359 "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
360 "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
361 "250", "251", "252", "253", "254", "255"
364 /** Convert an IP address to printable ASCII form.
365 * This is generally deprecated in favor of ircd_ntoa_r().
366 * @param[in] in Address to convert.
367 * @return Pointer to a static buffer containing the readable form.
369 const char* ircd_ntoa(const struct irc_in_addr* in)
371 static char buf[SOCKIPLEN];
372 return ircd_ntoa_r(buf, in);
375 /** Convert an IP address to printable ASCII form.
376 * @param[out] buf Output buffer to write to.
377 * @param[in] in Address to format.
378 * @return Pointer to the output buffer \a buf.
380 const char* ircd_ntoa_r(char* buf, const struct irc_in_addr* in)
385 if (irc_in_addr_is_ipv4(in)) {
386 unsigned int pos, len;
389 pch = (unsigned char*)&in->in6_16[6];
390 len = strlen(IpQuadTab[*pch]);
391 memcpy(buf, IpQuadTab[*pch++], len);
394 len = strlen(IpQuadTab[*pch]);
395 memcpy(buf+pos, IpQuadTab[*pch++], len);
398 len = strlen(IpQuadTab[*pch]);
399 memcpy(buf+pos, IpQuadTab[*pch++], len);
402 len = strlen(IpQuadTab[*pch]);
403 memcpy(buf+pos, IpQuadTab[*pch++], len);
404 buf[pos + len] = '\0';
407 static const char hexdigits[] = "0123456789abcdef";
408 unsigned int pos, part, max_start, max_zeros, curr_zeros, ii;
410 /* Find longest run of zeros. */
411 for (max_start = ii = 1, max_zeros = curr_zeros = 0; ii < 8; ++ii) {
414 else if (curr_zeros > max_zeros) {
415 max_start = ii - curr_zeros;
416 max_zeros = curr_zeros;
420 if (curr_zeros > max_zeros) {
421 max_start = ii - curr_zeros;
422 max_zeros = curr_zeros;
425 /* Print out address. */
426 /** Append \a CH to the output buffer. */
427 #define APPEND(CH) do { buf[pos++] = (CH); } while (0)
428 for (pos = ii = 0; (ii < 8); ++ii) {
429 if ((max_zeros > 0) && (ii == max_start)) {
434 part = ntohs(in->in6_16[ii]);
436 APPEND(hexdigits[part >> 12]);
438 APPEND(hexdigits[(part >> 8) & 15]);
440 APPEND(hexdigits[(part >> 4) & 15]);
441 APPEND(hexdigits[part & 15]);
447 /* Nul terminate and return number of characters used. */
453 /** Attempt to parse an IPv4 address into a network-endian form.
454 * @param[in] input Input string.
455 * @param[out] output Network-endian representation of the address.
456 * @return Number of characters used from \a input, or 0 if the parse failed.
459 ircd_aton_ip4(const char *input, unsigned int *output)
461 unsigned int dots = 0, pos = 0, part = 0, ip = 0;
463 /* Intentionally no support for bizarre IPv4 formats (plain
464 * integers, octal or hex components) -- only vanilla dotted
470 if (IsDigit(input[pos])) {
471 part = part * 10 + input[pos++] - '0';
474 if ((dots == 3) && !IsDigit(input[pos])) {
475 *output = htonl(ip | part);
478 } else if (input[pos] == '.') {
479 if (input[++pos] == '.')
481 ip |= part << (24 - 8 * dots++);
488 /** Parse a numeric IPv4 or IPv6 address into an irc_in_addr.
489 * @param[out] ip Receives parsed IP address.
490 * @param[in] input Input buffer.
491 * @return Number of characters used from \a input, or 0 if the
492 * address was unparseable or malformed.
495 ircd_aton(struct irc_in_addr *ip, const char *input)
502 memset(ip, 0, sizeof(*ip));
503 colon = strchr(input, ':');
504 dot = strchr(input, '.');
506 if (colon && (!dot || (dot > colon))) {
507 unsigned int part = 0, pos = 0, ii = 0, colon = 8;
508 const char *part_start = NULL;
510 /* Parse IPv6, possibly like ::127.0.0.1.
511 * This is pretty straightforward; the only trick is borrowed
512 * from Paul Vixie (BIND): when it sees a "::" continue as if
513 * it were a single ":", but note where it happened, and fill
514 * with zeros afterward.
516 if (input[pos] == ':') {
517 if ((input[pos+1] != ':') || (input[pos+2] == ':'))
521 part_start = input + pos;
526 switch (input[pos]) {
527 case '0': case '1': case '2': case '3': case '4':
528 case '5': case '6': case '7': case '8': case '9':
529 chval = input[pos] - '0';
531 part = (part << 4) | chval;
536 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
537 chval = input[pos] - 'A' + 10;
539 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
540 chval = input[pos] - 'a' + 10;
543 part_start = input + ++pos;
544 if (input[pos] == '.')
546 ip->in6_16[ii++] = htons(part);
548 if (input[pos] == ':') {
558 len = ircd_aton_ip4(part_start, &ip4);
559 if (!len || (ii > 6))
561 ip->in6_16[ii++] = htons(ntohl(ip4) >> 16);
562 ip->in6_16[ii++] = htons(ntohl(ip4) & 65535);
565 /* Shift stuff after "::" up and fill middle with zeros. */
566 for (jj = 0; jj < ii - colon; jj++)
567 ip->in6_16[7 - jj] = ip->in6_16[ii - jj - 1];
568 for (jj = 0; jj < 8 - ii; jj++)
569 ip->in6_16[colon + jj] = 0;
571 return part_start - input + len;
574 ip->in6_16[ii++] = htons(part);
577 /* Shift stuff after "::" up and fill middle with zeros. */
578 for (jj = 0; jj < ii - colon; jj++)
579 ip->in6_16[7 - jj] = ip->in6_16[ii - jj - 1];
580 for (jj = 0; jj < 8 - ii; jj++)
581 ip->in6_16[colon + jj] = 0;
590 int len = ircd_aton_ip4(input, &addr);
592 ip->in6_16[5] = htons(65535);
593 ip->in6_16[6] = htons(ntohl(addr) >> 16);
594 ip->in6_16[7] = htons(ntohl(addr) & 65535);
598 return 0; /* parse failed */