X-Git-Url: http://git.pk910.de/?p=ircu2.10.12-pk.git;a=blobdiff_plain;f=ircd%2Fmatch.c;fp=ircd%2Fmatch.c;h=b585053c795348900039553fae8c29f037fea525;hp=0000000000000000000000000000000000000000;hb=0400a5a6479398d82526785c18c0df8bc8b92dce;hpb=d17e10da972ce5776c60b4c317267c6abe0e1ead diff --git a/ircd/match.c b/ircd/match.c new file mode 100644 index 0000000..b585053 --- /dev/null +++ b/ircd/match.c @@ -0,0 +1,864 @@ +/* + * IRC - Internet Relay Chat, common/match.c + * Copyright (C) 1990 Jarkko Oikarinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/** @file + * @brief Functions to match strings against IRC mask strings. + * @version $Id$ + */ +#include "config.h" + +#include "match.h" +#include "ircd_chattr.h" +#include "ircd_string.h" +#include "ircd_snprintf.h" + +/* + * mmatch() + * + * Written by Run (carlo@runaway.xs4all.nl), 25-10-96 + * + * + * From: Carlo Wood + * Message-Id: <199609021026.MAA02393@runaway.xs4all.nl> + * Subject: [C-Com] Analysis for `mmatch' (was: gline4 problem) + * To: coder-com@mail.undernet.org (coder committee) + * Date: Mon, 2 Sep 1996 12:26:01 +0200 (MET DST) + * + * We need a new function `mmatch(const char *old_mask, const char *new_mask)' + * which returns `true' likewise the current `match' (start with copying it), + * but which treats '*' and '?' in `new_mask' differently (not "\*" and "\?" !) + * as follows: a '*' in `new_mask' does not match a '?' in `old_mask' and + * a '?' in `new_mask' does not match a '\?' in `old_mask'. + * And ofcourse... a '*' in `new_mask' does not match a '\*' in `old_mask'... + * And last but not least, '\?' and '\*' in `new_mask' now become one character. + */ + +/** Compares one mask against another. + * One wildcard mask may be said to be a superset of another if the + * set of strings matched by the first is a proper superset of the set + * of strings matched by the second. In practical terms, this means + * that the second is made redundant by the first. + * + * The logic for this test is similar to that in match(), but a + * backslash in old_mask only matches a backslash in new_mask (and + * requires the next character to match exactly), and -- after + * contiguous runs of wildcards are logically collapsed -- a '?' in + * old_mask does not match a '*' in new_mask. + * + * @param[in] old_mask One wildcard mask. + * @param[in] new_mask Another wildcard mask. + * @return Zero if \a old_mask is a superset of \a new_mask, non-zero otherwise. + */ +int mmatch(const char *old_mask, const char *new_mask) +{ + const char *m = old_mask; + const char *n = new_mask; + const char *ma = m; + const char *na = n; + int wild = 0; + int mq = 0, nq = 0; + + while (1) + { + if (*m == '*') + { + while (*m == '*') + m++; + wild = 1; + ma = m; + na = n; + } + + if (!*m) + { + if (!*n) + return 0; + for (m--; (m > old_mask) && (*m == '?'); m--) + ; + if ((*m == '*') && (m > old_mask) && (m[-1] != '\\')) + return 0; + if (!wild) + return 1; + m = ma; + + /* Added to `mmatch' : Because '\?' and '\*' now is one character: */ + if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?'))) + ++na; + + n = ++na; + } + else if (!*n) + { + while (*m == '*') + m++; + return (*m != 0); + } + if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?'))) + { + m++; + mq = 1; + } + else + mq = 0; + + /* Added to `mmatch' : Because '\?' and '\*' now is one character: */ + if ((*n == '\\') && ((n[1] == '*') || (n[1] == '?'))) + { + n++; + nq = 1; + } + else + nq = 0; + +/* + * This `if' has been changed compared to match() to do the following: + * Match when: + * old (m) new (n) boolean expression + * * any (*m == '*' && !mq) || + * ? any except '*' (*m == '?' && !mq && (*n != '*' || nq)) || + * any except * or ? same as m (!((*m == '*' || *m == '?') && !mq) && + * ToLower(*m) == ToLower(*n) && + * !((mq && !nq) || (!mq && nq))) + * + * Here `any' also includes \* and \? ! + * + * After reworking the boolean expressions, we get: + * (Optimized to use boolean short-circuits, with most frequently occurring + * cases upfront (which took 2 hours!)). + */ + if ((*m == '*' && !mq) || + ((!mq || nq) && ToLower(*m) == ToLower(*n)) || + (*m == '?' && !mq && (*n != '*' || nq))) + { + if (*m) + m++; + if (*n) + n++; + } + else + { + if (!wild) + return 1; + m = ma; + + /* Added to `mmatch' : Because '\?' and '\*' now is one character: */ + if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?'))) + ++na; + + n = ++na; + } + } +} + +/* + * Compare if a given string (name) matches the given + * mask (which can contain wild cards: '*' - match any + * number of chars, '?' - match any single character. + * + * return 0, if match + * 1, if no match + * + * Originally by Douglas A Lewis (dalewis@acsu.buffalo.edu) + * Rewritten by Timothy Vogelsang (netski), net@astrolink.org + */ + +/** Check a string against a mask. + * This test checks using traditional IRC wildcards only: '*' means + * match zero or more characters of any type; '?' means match exactly + * one character of any type. A backslash escapes the next character + * so that a wildcard may be matched exactly. + * @param[in] mask Wildcard-containing mask. + * @param[in] name String to check against \a mask. + * @return Zero if \a mask matches \a name, non-zero if no match. + */ +int match(const char *mask, const char *name) +{ + const char *m = mask, *n = name; + const char *m_tmp = mask, *n_tmp = name; + int star_p; + + for (;;) switch (*m) { + case '\0': + if (!*n) + return 0; + backtrack: + if (m_tmp == mask) + return 1; + m = m_tmp; + n = ++n_tmp; + if (*n == '\0') + return 1; + break; + case '\\': + m++; + /* allow escaping to force capitalization */ + if (*m++ != *n++) + goto backtrack; + break; + case '*': case '?': + for (star_p = 0; ; m++) { + if (*m == '*') + star_p = 1; + else if (*m == '?') { + if (!*n++) + goto backtrack; + } else break; + } + if (star_p) { + if (!*m) + return 0; + else if (*m == '\\') { + m_tmp = ++m; + if (!*m) + return 1; + for (n_tmp = n; *n && *n != *m; n++) ; + } else { + m_tmp = m; + for (n_tmp = n; *n && ToLower(*n) != ToLower(*m); n++) ; + } + } + /* and fall through */ + default: + if (!*n) + return *m != '\0'; + if (ToLower(*m) != ToLower(*n)) + goto backtrack; + m++; + n++; + break; + } +} + +/* + * collapse() + * Collapse a pattern string into minimal components. + * This particular version is "in place", so that it changes the pattern + * which is to be reduced to a "minimal" size. + * + * (C) Carlo Wood - 6 Oct 1998 + * Speedup rewrite by Andrea Cocito, December 1998. + * Note that this new optimized algorithm can *only* work in place. + */ + +/** Collapse a mask string to remove redundancies. + * Specifically, it replaces a sequence of '*' followed by additional + * '*' or '?' with the same number of '?'s as the input, followed by + * one '*'. This minimizes useless backtracking when matching later. + * @param[in,out] mask Mask string to collapse. + * @return Pointer to the start of the string. + */ +char *collapse(char *mask) +{ + int star = 0; + char *m = mask; + char *b; + + if (m) + { + do + { + if ((*m == '*') && ((m[1] == '*') || (m[1] == '?'))) + { + b = m; + do + { + if (*m == '*') + star = 1; + else + { + if (star && (*m != '?')) + { + *b++ = '*'; + star = 0; + }; + *b++ = *m; + if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?'))) + *b++ = *++m; + }; + } + while (*m++); + break; + } + else + { + if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?'))) + m++; + }; + } + while (*m++); + }; + return mask; +} + +/* + ***************** Nemesi's matchcomp() / matchexec() ************** + */ + +/** @page compiledmasks Compiled Masks + * These functions allow the use of "compiled" masks, you compile a mask + * by means of matchcomp() that gets the plain text mask as input and writes + * its result in the memory locations addressed by the 3 parameters: + * - *cmask will contain the text of the compiled mask + * - *minlen will contain the length of the shortest string that can match + * the mask + * - *charset will contain the minimal set of chars needed to match the mask + * You can pass NULL as *charset and it will be simply not returned, but you + * MUST pass valid pointers for *minlen and *cmask (which must be big enough + * to contain the compiled mask text that is in the worst case as long as the + * text of the mask itself in plaintext format) and the return value of + * matchcomp() will be the number of chars actually written there (excluded + * the trailing zero). cmask can be == mask, matchcomp() can work in place. + * The {cmask, minlen} couple of values make the real compiled mask and + * need to be passed to the functions that use the compiled mask, if you pass + * the wrong minlen or something wrong in cmask to one of these expect a + * coredump. This means that when you record a compiled mask you must store + * *both* these values. + * Once compiled the mask can be used to match a string by means of + * matchexec(), it can be printed back to human-readable format by means + * of sprintmatch() or it can be compared to another compiled mask by means + * of mmexec() that will tell if it completely overrides that mask (a lot like + * what mmatch() does for plain text masks). + * You can gain a lot of speed in many situations avoiding to matchexec() when: + * - The maximum length of the field you are about to match() the mask to is + * shorter than minlen, in example when matching abc*def*ghil with a nick: + * It just cannot match since a nick is at most 9 chars long and the mask + * needs at least 10 chars (10 will be the value returned in minlen). + * - The charset allowed for the field you are about to match to doesn't + * "contain" the charset returned by matchcomp(), in example when you + * have *.* as mask it makes no sense to try to match it against a nick + * because, again, a nick can't contain a '.', you can check this with + * a simple (charset & NTL_IRCNK) in this case. + * - As a special case, since compiled masks are forced to lowercase, + * it would make no sense to use the NTL_LOWER and NTL_UPPER on a compiled + * mask, thus they are reused as follows: if the NTL_LOWER bit of charset + * is set it means that the mask contains only non-wilds chars (i.e. you can + * use strCasecmp() to match it or a direct hash lookup), if the NTL_UPPER + * bit is set it means that it contains only wild chars (and you can + * match it with strlen(field)>=minlen). + * Do these optimizations ONLY when the data you are about to pass to + * matchexec() are *known* to be invalid in advance, using strChattr() + * or strlen() on the text would be slower than calling matchexec() directly + * and let it fail. + * Internally a compiled mask contain in the *cmask area the text of + * the plain text form of the mask itself with applied the following hacks: + * - All characters are forced to lowercase (so that uppercase letters and + * specifically the symbols 'A' and 'Z' are reserved for special use) + * - All non-escaped stars '*' are replaced by the letter 'Z' + * - All non-escaped question marks '?' are replaced by the letter 'A' + * - All escape characters are removed, the wilds escaped by them are + * then passed by without the escape since they don't collide anymore + * with the real wilds (encoded as A/Z) + * - Finally the part of the mask that follows the last asterisk is + * reversed (byte order mirroring) and moved right after the first + * asterisk. + * After all this a mask like: Head*CHUNK1*chu\*nK2*ch??k3*TaIl + * .... becomes: headZliatZchunk1Zchu*nk2ZchAAk3 + * This can still be printed on a console, more or less understood by an + * human and handled with the usual str*() library functions. + * When you store somewhere the compiled mask you can avoid storing the + * textform of it since it can be "decompiled" by means of sprintmatch(), + * but at that time the following things are changed in the mask: + * - All chars have been forced to lowercase. + * - The mask is collapsed. + * The balance point of using compiled masks in terms of CPU is when you expect + * to use matchexec() instead of match() at least 20 times on the same mask + * or when you expect to use mmexec() instead of mmatch() 3 times. + */ + +/** Compile a mask for faster matching. + * See also @ref compiledmasks. + * @param[out] cmask Output buffer for compiled mask. + * @param[out] minlen Minimum length of matching strings. + * @param[out] charset Character attributes used in compiled mask. + * @param[out] mask Input mask. + * @return Length of compiled mask, not including NUL terminator. + */ +int matchcomp(char *cmask, int *minlen, int *charset, const char *mask) +{ + const char *m = mask; + char *b = cmask; + char *fs = 0; + char *ls = 0; + char *x1, *x2; + int l1, l2, lmin, loop, sign; + int star = 0; + int cnt = 0; + char ch; + int chset = ~0; + int chset2 = (NTL_LOWER | NTL_UPPER); + + if (m) + while ((ch = *m++)) + switch (ch) + { + case '*': + star = 1; + break; + case '?': + cnt++; + *b++ = 'A'; + chset2 &= ~NTL_LOWER; + break; + case '\\': + if ((*m == '?') || (*m == '*')) + ch = *m++; + default: + if (star) + { + ls = b; + fs = fs ? fs : b; + *b++ = 'Z'; + chset2 &= ~NTL_LOWER; + star = 0; + }; + cnt++; + *b = ToLower(ch); + chset &= IRCD_CharAttrTab[*b++ - CHAR_MIN]; + chset2 &= ~NTL_UPPER; + }; + + if (charset) + *charset = (chset | chset2); + + if (star) + { + ls = b; + fs = (fs ? fs : b); + *b++ = 'Z'; + }; + + if (ls) + { + for (x1 = ls + 1, x2 = (b - 1); x1 < x2; x1++, x2--) + { + ch = *x1; + *x1 = *x2; + *x2 = ch; + }; + l1 = (ls - fs); + l2 = (b - ls); + x1 = fs; + while ((lmin = (l1 < l2) ? l1 : l2)) + { + x2 = x1 + l1; + for (loop = 0; loop < lmin; loop++) + { + ch = x1[loop]; + x1[loop] = x2[loop]; + x2[loop] = ch; + }; + x1 += lmin; + sign = l1 - l2; + l1 -= (sign < 0) ? 0 : lmin; + l2 -= (sign > 0) ? 0 : lmin; + }; + }; + + *b = '\0'; + *minlen = cnt; + return (b - cmask); + +} + +/** Compare a string to a compiled mask. + * If \a cmask is not from matchcomp(), or if \a minlen is not the value + * passed out of matchcomp(), this may core. + * See also @ref compiledmasks. + * @param[in] string String to test. + * @param[in] cmask Compiled mask string. + * @param[in] minlen Minimum length of strings that match \a cmask. + * @return Zero if the string matches, non-zero otherwise. + */ +int matchexec(const char *string, const char *cmask, int minlen) +{ + const char *s = string - 1; + const char *b = cmask - 1; + int trash; + const char *bb, *bs; + char ch; + +tryhead: + while ((ToLower(*++s) == *++b) && *s); + if (!*s) + return ((*b != '\0') && ((*b++ != 'Z') || (*b != '\0'))); + if (*b != 'Z') + { + if (*b == 'A') + goto tryhead; + return 1; + }; + + bs = s; + while (*++s); + + if ((trash = (s - string - minlen)) < 0) + return 2; + +trytail: + while ((ToLower(*--s) == *++b) && *b && (ToLower(*--s) == *++b) && *b + && (ToLower(*--s) == *++b) && *b && (ToLower(*--s) == *++b) && *b); + if (*b != 'Z') + { + if (*b == 'A') + goto trytail; + return (*b != '\0'); + }; + + s = --bs; + bb = b; + + while ((ch = *++b)) + { + while ((ToLower(*++s) != ch)) + if (--trash < 0) + return 4; + bs = s; + +trychunk: + while ((ToLower(*++s) == *++b) && *b); + if (!*b) + return 0; + if (*b == 'Z') + { + bs = --s; + bb = b; + continue; + }; + if (*b == 'A') + goto trychunk; + + b = bb; + s = bs; + if (--trash < 0) + return 5; + }; + + return 0; +} + +/* + * matchdecomp() + * Prints the human readable version of *cmask into *mask, (decompiles + * cmask). + * The area pointed by *mask MUST be big enough (the mask might be up to + * twice the size of its compiled form if it's made all of \? or \*, and + * this function can NOT work in place since it might inflate the mask) + * The printed mask is not identical to the one that was compiled to cmask, + * in fact it is 1) forced to all lowercase, 2) collapsed, both things + * are supposed to NOT change it's meaning. + * It returns the number of chars actually written to *mask; + */ + +/** Decompile a compiled mask into printable form. + * See also @ref compiledmasks. + * @param[out] mask Output mask buffer. + * @param[in] cmask Compiled mask. + * @return Number of characters written to \a mask. + */ +int matchdecomp(char *mask, const char *cmask) +{ + char *rtb = mask; + const char *rcm = cmask; + const char *begtail, *endtail; + + if (rtb ==0) + return (-1); + + if (rcm == 0) + return (-2); + + for (; (*rcm != 'Z'); rcm++, rtb++) + { + if ((*rcm == '?') || (*rcm == '*')) + *rtb++ = '\\'; + if (!((*rtb = ((*rcm == 'A') ? '?' : *rcm)))) + return (rtb - mask); + }; + + begtail = rcm++; + *rtb++ = '*'; + + while (*rcm && (*rcm != 'Z')) + rcm++; + + endtail = rcm; + + if (*rcm) + { + while (*++rcm) + switch (*rcm) + { + case 'A': + *rtb++ = '?'; + break; + case 'Z': + *rtb++ = '*'; + break; + case '*': + case '?': + *rtb++ = '\\'; + default: + *rtb++ = *rcm; + }; + *rtb++ = '*'; + }; + + for (rcm = endtail; (--rcm) > begtail; *rtb++ = ((*rcm == 'A') ? '?' : *rcm)) + if ((*rcm == '?') || (*rcm == '*')) + *rtb++ = '\\'; + + *rtb = '\0'; + return (rtb - mask); +} + +/* + * mmexec() + * Checks if a wider compiled mask (wcm/wminlen) completely overrides + * a more restrict one (rcm/rminlen), basically what mmatch() does for + * non-compiled masks, returns 0 if the override is true (like mmatch()). + * "the wider overrides the restrict" means that any string that matches + * the restrict one _will_ also match the wider one, always. + * In this we behave differently from mmatch() because in example we return + * true for " a?*cd overrides a*bcd " for which the override happens for how + * we literally defined it, here mmatch() would have returned false. + * The original concepts and the base algorithm are copied from mmatch() + * written by Run (Carlo Wood), this function is written by + * Nemesi (Andrea Cocito) + */ +/** Tests for a superset relationship between compiled masks. This + * function does for compiled masks what mmatch() is does for normal + * masks. + * See also @ref compiledmasks. + * @param[in] wcm Compiled mask believed to be wider. + * @param[in] wminlen Minimum match length for \a wcm. + * @param[in] rcm Compiled mask believed to be restricted. + * @param[in] rminlen Minimum match length for \a rcm. + * @return Zero if \a wcm is a superset of \a rcm, non-zero if not. + */ +int mmexec(const char *wcm, int wminlen, const char *rcm, int rminlen) +{ + const char *w, *r, *br, *bw, *rx, *rz; + int eat, trash; + + /* First of all rm must have enough non-stars to 'contain' wm */ + if ((trash = rminlen - wminlen) < 0) + return 1; + w = wcm; + r = rcm; + eat = 0; + + /* Let's start the game, remember that '*' is mapped to 'Z', '?' + is mapped to 'A' and that head?*??*?chunk*???*tail becomes + headAAAAZliatAAAZchunk for compiled masks */ + + /* Match the head of wm with the head of rm */ + for (; (*r) && (*r != 'Z') && ((*w == *r) || (*w == 'A')); r++, w++); + if (*r == 'Z') + while (*w == 'A') /* Eat extra '?' before '*' in wm if got '*' in rm */ + w++, eat++; + if (*w != 'Z') /* head1.. can't match head2.. */ + return ((*w) || (*r)) ? 1 : 0; /* and head matches only head */ + if (!*++w) + return 0; /* headZ matches head */ + + /* Does rm have any stars in it ? let's check */ + for (rx = r; *r && (*r != 'Z'); r++); + if (!*r) + { + /* rm has no stars and thus isn't a mask but it's just a flat + string: special handling occurs here, note that eat must be 0 here */ + + /* match the tail */ + if (*w != 'Z') + { + for (; r--, (*w) && ((*w == *r) || (*w == 'A')); w++); + if (*w != 'Z') /* headZliat1 fails on head2tail */ + return (*w) ? 1 : 0; /* but headZliat matches headtail */ + } + + /* match the chunks */ + while (1) + { /* This loop can't break but only return */ + + for (bw = w++; (*w != *rx); rx++) /* Seek the 1st char of the chunk */ + if (--trash < 0) /* See if we can trash one more char of rm */ + return 1; /* If not we can only fail of course */ + for (r = ++rx, w++; (*w) && ((*w == *r) || (*w == 'A')); r++, w++); + if (!*w) /* Did last loop match the rest of chunk ? */ + return 0; /* ... Yes, end of wm, matched ! */ + if (*w != 'Z') + { /* ... No, hit non-star */ + w = bw; /* Rollback at beginning of chunk */ + if (--trash < 0) /* Trashed the char where this try started */ + return 1; /* if we can't trash more chars fail */ + } + else + { + rx = r; /* Successfully matched a chunk, move rx */ + } /* and go on with the next one */ + } + } + + /* rm has at least one '*' and thus is a 'real' mask */ + rz = r++; /* rx = unused of head, rz = beg-tail */ + + /* Match the tail of wm (if any) against the tail of rm */ + if (*w != 'Z') + { + for (; (*w) && (*r != 'Z') && ((*w == *r) || (*w == 'A')); w++, r++); + if (*r == 'Z') /* extra '?' before tail are fluff, just flush 'em */ + while (*w == 'A') + w++; + if (*w != 'Z') /* We aren't matching a chunk, can't rollback */ + return (*w) ? 1 : 0; + } + + /* Match the chunks of wm against what remains of the head of rm */ + while (1) + { + bw = w; + for (bw++; (rx < rz) && (*bw != *rx); rx++) /* Seek the first */ + if (--trash < 0) /* waste some trash reserve */ + return 1; + if (!(rx < rz)) /* head finished */ + break; + for (bw++, (br = ++rx); + (br < rz) && (*bw) && ((*bw == *br) || (*bw == 'A')); br++, bw++); + if (!(br < rz)) /* Note that we didn't use any 'eat' char yet, if */ + while (*bw == 'A') /* there were eat-en chars the head would be over */ + bw++, eat++; /* Happens only at end of head, and eat is still 0 */ + if (!*bw) + return 0; + if (*bw != 'Z') + { + eat = 0; + if (!(br < rz)) + { /* If we failed because we got the end of head */ + trash -= (br - rx); /* it makes no sense to rollback, just trash */ + if (--trash < 0) /* all the rest of the head which isn't long */ + return 1; /* enough for this chunk and go out of this */ + break; /* loop, then we try with the chunks of rm */ + }; + if (--trash < 0) + return 1; + } + else + { + w = bw; + rx = br; + } + } + + /* Match the unused chunks of wm against the chunks of rm */ + rx = r; + for (; *r && (*r != 'Z'); r++); + rz = r; + if (*r++) + { + while (*r) + { + bw = w; + while (eat && *r) /* the '?' we ate makes us skip as many chars */ + if (*r++ != 'Z') /* here, but can't skip stars or trailing zero */ + eat--; + for (bw++; (*r) && (*bw != *r); r++) + if ((*r != 'Z') && (--trash < 0)) + return 1; + if (!*r) + break; + for ((br = ++r), bw++; + (*br) && (*br != 'Z') && ((*bw == *br) || (*bw == 'A')); br++, bw++); + if (*br == 'Z') + while (*bw == 'A') + bw++, eat++; + if (!*bw) + return 0; + if (*bw != 'Z') + { + eat = 0; + if ((!*br) || (*r == 'Z')) + { /* If we hit the end of rm or a star in it */ + trash -= (br - r); /* makes no sense to rollback within this */ + if (trash < 0) /* same chunk of br, skip it all and then */ + return 1; /* either rollback or break this loop if */ + if (!*br) /* it was the end of rm */ + break; + r = br; + } + if (--trash < 0) + return 1; + } + else + { + r = br; + w = bw; + } + } + } + + /* match the remaining chunks of wm against what remains of the tail of rm */ + r = rz - eat - 1; /* can't have or 'Z' within the tail, so just move r */ + while (r >= rx) + { + bw = w; + for (bw++; (*bw != *r); r--) + if (--trash < 0) + return 1; + if (!(r >= rx)) + return 1; + for ((br = --r), bw++; + (*bw) && (br >= rx) && ((*bw == *br) || (*bw == 'A')); br--, bw++); + if (!*bw) + return 0; + if (!(br >= rx)) + return 1; + if (*bw != 'Z') + { + if (--trash < 0) + return 1; + } + else + { + r = br; + w = bw; + } + } + return 1; /* Auch... something left out ? Fail */ +} + +/** Test whether an address matches the most significant bits of a mask. + * @param[in] addr Address to test. + * @param[in] mask Address to test against. + * @param[in] bits Number of bits to test. + * @return 0 on mismatch, 1 if bits < 128 and all bits match; -1 if + * bits == 128 and all bits match. + */ +int ipmask_check(const struct irc_in_addr *addr, const struct irc_in_addr *mask, unsigned char bits) +{ + int k; + + for (k = 0; k < 8; k++) { + if (bits < 16) + return !(htons(addr->in6_16[k] ^ mask->in6_16[k]) >> (16-bits)); + if (addr->in6_16[k] != mask->in6_16[k]) + return 0; + if (!(bits -= 16)) + return 1; + } + return -1; +}