]>
jfr.im git - irc/quakenet/snircd.git/blob - ircd/ircd_string.c
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.
21 * @version $Id: ircd_string.c,v 1.24.2.1 2007/01/15 03:08:23 entrope Exp $
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
++))
166 NTL_HDR_strChattr
{ NTL_SRC_strChattr
}
167 NTL_HDR_strCasediff
{ NTL_SRC_strCasediff
}
168 #endif /* !FORCEINLINE */
171 * Other functions visible externally
174 /** Case insensitive string comparison.
175 * @param[in] a First string to compare.
176 * @param[in] b Second string to compare.
177 * @return Less than, equal to, or greater than zero if \a a is lexicographically less than, equal to, or greater than \a b.
179 int ircd_strcmp(const char *a
, const char *b
)
183 while (ToLower(*ra
) == ToLower(*rb
)) {
189 return (ToLower(*ra
) - ToLower(*rb
));
192 /** Case insensitive comparison of the starts of two strings.
193 * @param[in] a First string to compare.
194 * @param[in] b Second string to compare.
195 * @param[in] n Maximum number of characters to compare.
196 * @return Less than, equal to, or greater than zero if \a a is
197 * lexicographically less than, equal to, or greater than \a b.
199 int ircd_strncmp(const char *a
, const char *b
, size_t n
)
206 while (ToLower(*ra
) == ToLower(*rb
)) {
207 if (!*ra
++ || !left
--)
212 return (ToLower(*ra
) - ToLower(*rb
));
215 /** Fill a vector of distinct names from a delimited input list.
216 * Empty tokens (when \a token occurs at the start or end of \a list,
217 * or when \a token occurs adjacent to itself) are ignored. When
218 * \a size tokens have been written to \a vector, the rest of the
220 * Unlike token_vector(), if a token repeats an earlier token, it is
222 * @param[in,out] names Input buffer.
223 * @param[in] token Delimiter used to split \a list.
224 * @param[out] vector Output vector.
225 * @param[in] size Maximum number of elements to put in \a vector.
226 * @return Number of elements written to \a vector.
228 int unique_name_vector(char* names
, char token
, char** vector
, int size
)
240 * ignore spurious tokens
242 while (token
== *start
)
245 for (end
= strchr(start
, token
); end
; end
= strchr(start
, token
)) {
248 * ignore spurious tokens
250 while (token
== *end
)
252 for (i
= 0; i
< count
; ++i
) {
253 if (0 == ircd_strcmp(vector
[i
], start
))
257 vector
[count
++] = start
;
264 for (i
= 0; i
< count
; ++i
)
265 if (0 == ircd_strcmp(vector
[i
], start
))
267 vector
[count
++] = start
;
272 /** Fill a vector of tokens from a delimited input list.
273 * Empty tokens (when \a token occurs at the start or end of \a list,
274 * or when \a token occurs adjacent to itself) are ignored. When
275 * \a size tokens have been written to \a vector, the rest of the
277 * @param[in,out] names Input buffer.
278 * @param[in] token Delimiter used to split \a list.
279 * @param[out] vector Output vector.
280 * @param[in] size Maximum number of elements to put in \a vector.
281 * @return Number of elements written to \a vector.
283 int token_vector(char* names
, char token
, char** vector
, int size
)
293 vector
[count
++] = start
;
294 for (end
= strchr(start
, token
); end
; end
= strchr(start
, token
)) {
298 vector
[count
++] = start
;
307 /** Copy all or part of the hostname in a string to another string.
308 * If \a userhost contains an '\@', the remaining portion is used;
309 * otherwise, the whole \a userhost is used.
310 * @param[out] buf Output buffer.
311 * @param[in] userhost user\@hostname or hostname string.
312 * @param[in] len Maximum number of bytes to write to \a host.
313 * @return The output buffer \a buf.
315 char* host_from_uh(char* buf
, const char* userhost
, size_t len
)
320 assert(0 != userhost
);
322 if ((s
= strchr(userhost
, '@')))
326 ircd_strncpy(buf
, s
, len
);
332 * this new faster inet_ntoa was ripped from:
333 * From: Thomas Helvey <tomh@inxpress.net>
335 /** Array of text strings for dotted quads. */
336 static const char* IpQuadTab
[] =
338 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
339 "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
340 "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
341 "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
342 "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
343 "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
344 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
345 "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
346 "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
347 "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
348 "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
349 "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
350 "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
351 "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
352 "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
353 "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
354 "160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
355 "170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
356 "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
357 "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
358 "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
359 "210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
360 "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
361 "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
362 "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
363 "250", "251", "252", "253", "254", "255"
366 /** Convert an IP address to printable ASCII form.
367 * This is generally deprecated in favor of ircd_ntoa_r().
368 * @param[in] in Address to convert.
369 * @return Pointer to a static buffer containing the readable form.
371 const char* ircd_ntoa(const struct irc_in_addr
* in
)
373 static char buf
[SOCKIPLEN
];
374 return ircd_ntoa_r(buf
, in
);
377 /** Convert an IP address to printable ASCII form.
378 * @param[out] buf Output buffer to write to.
379 * @param[in] in Address to format.
380 * @return Pointer to the output buffer \a buf.
382 const char* ircd_ntoa_r(char* buf
, const struct irc_in_addr
* in
)
387 if (irc_in_addr_is_ipv4(in
)) {
388 unsigned int pos
, len
;
391 pch
= (unsigned char*)&in
->in6_16
[6];
392 len
= strlen(IpQuadTab
[*pch
]);
393 memcpy(buf
, IpQuadTab
[*pch
++], len
);
396 len
= strlen(IpQuadTab
[*pch
]);
397 memcpy(buf
+pos
, IpQuadTab
[*pch
++], len
);
400 len
= strlen(IpQuadTab
[*pch
]);
401 memcpy(buf
+pos
, IpQuadTab
[*pch
++], len
);
404 len
= strlen(IpQuadTab
[*pch
]);
405 memcpy(buf
+pos
, IpQuadTab
[*pch
++], len
);
406 buf
[pos
+ len
] = '\0';
409 static const char hexdigits
[] = "0123456789abcdef";
410 unsigned int pos
, part
, max_start
, max_zeros
, curr_zeros
, ii
;
412 /* Find longest run of zeros. */
413 for (max_start
= ii
= 1, max_zeros
= curr_zeros
= 0; ii
< 8; ++ii
) {
416 else if (curr_zeros
> max_zeros
) {
417 max_start
= ii
- curr_zeros
;
418 max_zeros
= curr_zeros
;
422 if (curr_zeros
> max_zeros
) {
423 max_start
= ii
- curr_zeros
;
424 max_zeros
= curr_zeros
;
427 /* Print out address. */
428 /** Append \a CH to the output buffer. */
429 #define APPEND(CH) do { buf[pos++] = (CH); } while (0)
430 for (pos
= ii
= 0; (ii
< 8); ++ii
) {
431 if ((max_zeros
> 0) && (ii
== max_start
)) {
436 part
= ntohs(in
->in6_16
[ii
]);
438 APPEND(hexdigits
[part
>> 12]);
440 APPEND(hexdigits
[(part
>> 8) & 15]);
442 APPEND(hexdigits
[(part
>> 4) & 15]);
443 APPEND(hexdigits
[part
& 15]);
449 /* Nul terminate and return number of characters used. */
455 /** Attempt to parse an IPv4 address into a network-endian form.
456 * @param[in] input Input string.
457 * @param[out] output Network-endian representation of the address.
458 * @param[out] pbits Number of bits found in pbits.
459 * @return Number of characters used from \a input, or 0 if the parse failed.
462 ircd_aton_ip4(const char *input
, unsigned int *output
, unsigned char *pbits
)
464 unsigned int dots
= 0, pos
= 0, part
= 0, ip
= 0, bits
;
466 /* Intentionally no support for bizarre IPv4 formats (plain
467 * integers, octal or hex components) -- only vanilla dotted
473 while (1) switch (input
[pos
]) {
478 ip
|= part
<< (24 - 8 * dots
);
484 if (input
[++pos
] == '.')
486 ip
|= part
<< (24 - 8 * dots
++);
488 if (input
[pos
] == '*') {
489 while (input
[++pos
] == '*') ;
490 if (input
[pos
] != '\0')
499 if (!pbits
|| !IsDigit(input
[pos
+ 1]))
501 for (bits
= 0; IsDigit(input
[++pos
]); )
502 bits
= bits
* 10 + input
[pos
] - '0';
506 case '0': case '1': case '2': case '3': case '4':
507 case '5': case '6': case '7': case '8': case '9':
508 part
= part
* 10 + input
[pos
++] - '0';
517 /** Parse a numeric IPv4 or IPv6 address into an irc_in_addr.
518 * @param[in] input Input buffer.
519 * @param[out] ip Receives parsed IP address.
520 * @param[out] pbits If non-NULL, receives number of bits specified in address mask.
521 * @return Number of characters used from \a input, or 0 if the
522 * address was unparseable or malformed.
525 ipmask_parse(const char *input
, struct irc_in_addr
*ip
, unsigned char *pbits
)
532 memset(ip
, 0, sizeof(*ip
));
533 colon
= strchr(input
, ':');
534 dot
= strchr(input
, '.');
536 if (colon
&& (!dot
|| (dot
> colon
))) {
537 unsigned int part
= 0, pos
= 0, ii
= 0, colon
= 8;
538 const char *part_start
= NULL
;
540 /* Parse IPv6, possibly like ::127.0.0.1.
541 * This is pretty straightforward; the only trick is borrowed
542 * from Paul Vixie (BIND): when it sees a "::" continue as if
543 * it were a single ":", but note where it happened, and fill
544 * with zeros afterward.
546 if (input
[pos
] == ':') {
547 if ((input
[pos
+1] != ':') || (input
[pos
+2] == ':'))
551 part_start
= input
+ pos
;
553 while (ii
< 8) switch (input
[pos
]) {
555 case '0': case '1': case '2': case '3': case '4':
556 case '5': case '6': case '7': case '8': case '9':
557 chval
= input
[pos
] - '0';
559 part
= (part
<< 4) | chval
;
564 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
565 chval
= input
[pos
] - 'A' + 10;
567 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
568 chval
= input
[pos
] - 'a' + 10;
571 part_start
= input
+ ++pos
;
572 if (input
[pos
] == '.')
574 ip
->in6_16
[ii
++] = htons(part
);
576 if (input
[pos
] == ':') {
586 len
= ircd_aton_ip4(part_start
, &ip4
, pbits
);
587 if (!len
|| (ii
> 6))
589 ip
->in6_16
[ii
++] = htons(ntohl(ip4
) >> 16);
590 ip
->in6_16
[ii
++] = htons(ntohl(ip4
) & 65535);
593 pos
= part_start
+ len
- input
;
597 if (!pbits
|| !IsDigit(input
[pos
+ 1]))
599 ip
->in6_16
[ii
++] = htons(part
);
600 for (part
= 0; IsDigit(input
[++pos
]); )
601 part
= part
* 10 + input
[pos
] - '0';
607 while (input
[++pos
] == '*') ;
608 if (input
[pos
] != '\0' || colon
< 8)
614 ip
->in6_16
[ii
++] = htons(part
);
615 if (colon
== 8 && ii
< 8)
626 /* Shift stuff after "::" up and fill middle with zeros. */
627 for (jj
= 0; jj
< ii
- colon
; jj
++)
628 ip
->in6_16
[7 - jj
] = ip
->in6_16
[ii
- jj
- 1];
629 for (jj
= 0; jj
< 8 - ii
; jj
++)
630 ip
->in6_16
[colon
+ jj
] = 0;
633 } else if (dot
|| strchr(input
, '/')) {
635 int len
= ircd_aton_ip4(input
, &addr
, pbits
);
637 ip
->in6_16
[5] = htons(65535);
638 ip
->in6_16
[6] = htons(ntohl(addr
) >> 16);
639 ip
->in6_16
[7] = htons(ntohl(addr
) & 65535);
644 } else if (input
[0] == '*') {
645 unsigned int pos
= 0;
646 while (input
[++pos
] == '*') ;
647 if (input
[pos
] != '\0')
652 } else return 0; /* parse failed */