* 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 "sys.h"
-#include "h.h"
-#include "struct.h"
-#include "common.h"
#include "match.h"
-#include "ircd.h"
-
-RCSTAG_CC("$Id$");
+#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 <carlo@runaway.xs4all.nl>
* Message-Id: <199609021026.MAA02393@runaway.xs4all.nl>
* Subject: [C-Com] Analysis for `mmatch' (was: gline4 problem)
* 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)
{
- register const char *m = old_mask;
- register const char *n = new_mask;
+ const char *m = old_mask;
+ const char *n = new_mask;
const char *ma = m;
const char *na = n;
int wild = 0;
if (*m == '*')
{
while (*m == '*')
- m++;
+ m++;
wild = 1;
ma = m;
na = n;
if (!*m)
{
if (!*n)
- return 0;
+ return 0;
for (m--; (m > old_mask) && (*m == '?'); m--)
- ;
+ ;
if ((*m == '*') && (m > old_mask) && (m[-1] != '\\'))
- return 0;
+ return 0;
if (!wild)
- return 1;
+ return 1;
m = ma;
/* Added to `mmatch' : Because '\?' and '\*' now is one character: */
if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
- ++na;
+ ++na;
n = ++na;
}
else if (!*n)
{
while (*m == '*')
- m++;
+ m++;
return (*m != 0);
}
if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
* * any (*m == '*' && !mq) ||
* ? any except '*' (*m == '?' && !mq && (*n != '*' || nq)) ||
* any except * or ? same as m (!((*m == '*' || *m == '?') && !mq) &&
- * toLower(*m) == toLower(*n) &&
+ * ToLower(*m) == ToLower(*n) &&
* !((mq && !nq) || (!mq && nq)))
*
* Here `any' also includes \* and \? !
*
* After reworking the boolean expressions, we get:
- * (Optimized to use boolean shortcircuits, with most frequently occuring
+ * (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)))
+ ((!mq || nq) && ToLower(*m) == ToLower(*n)) ||
+ (*m == '?' && !mq && (*n != '*' || nq)))
{
if (*m)
- m++;
+ m++;
if (*n)
- n++;
+ n++;
}
else
{
if (!wild)
- return 1;
+ return 1;
m = ma;
/* Added to `mmatch' : Because '\?' and '\*' now is one character: */
if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
- ++na;
+ ++na;
n = ++na;
}
*
* return 0, if match
* 1, if no match
- */
-
-/*
- * match
- *
- * Rewritten by Andrea Cocito (Nemesi), November 1998.
*
+ * Originally by Douglas A Lewis (dalewis@acsu.buffalo.edu)
+ * Rewritten by Timothy Vogelsang (netski), net@astrolink.org
*/
-/****************** Nemesi's match() ***************/
-
-int match(const char *mask, const char *string)
+/** 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)
{
- register const char *m = mask, *s = string;
- register char ch;
- const char *bm, *bs; /* Will be reg anyway on a decent CPU/compiler */
+ const char *m = mask, *n = name;
+ const char *m_tmp = mask, *n_tmp = name;
+ int star_p;
- /* Process the "head" of the mask, if any */
- while ((ch = *m++) && (ch != '*'))
- switch (ch)
- {
- case '\\':
- if (*m == '?' || *m == '*')
- ch = *m++;
- default:
- if (toLower(*s) != toLower(ch))
- return 1;
- case '?':
- if (!*s++)
- return 1;
- };
- if (!ch)
- return *s;
-
- /* We got a star: quickly find if/where we match the next char */
-got_star:
- bm = m; /* Next try rollback here */
- while ((ch = *m++))
- switch (ch)
- {
- case '?':
- if (!*s++)
- return 1;
- case '*':
- bm = m;
- continue; /* while */
- case '\\':
- if (*m == '?' || *m == '*')
- ch = *m++;
- default:
- goto break_while; /* C is structured ? */
- };
-break_while:
- if (!ch)
- return 0; /* mask ends with '*', we got it */
- ch = toLower(ch);
- while (toLower(*s++) != ch)
- if (!*s)
+ for (;;) switch (*m) {
+ case '\0':
+ if (!*n)
+ return 0;
+ backtrack:
+ if (m_tmp == mask)
return 1;
- bs = s; /* Next try start from here */
-
- /* Check the rest of the "chunk" */
- while ((ch = *m++))
- {
- switch (ch)
- {
- case '*':
- goto got_star;
- case '\\':
- if (*m == '?' || *m == '*')
- ch = *m++;
- default:
- if (toLower(*s) != toLower(ch))
- {
- m = bm;
- s = bs;
- goto got_star;
- };
- case '?':
- if (!*s++)
- return 1;
- };
- };
- if (*s)
- {
- m = bm;
- s = bs;
- goto got_star;
- };
- return 0;
+ m = m_tmp;
+ n = ++n_tmp;
+ 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;
+ }
}
/*
*
* (C) Carlo Wood - 6 Oct 1998
* Speedup rewrite by Andrea Cocito, December 1998.
- * Note that this new optimized alghoritm can *only* work in place.
+ * 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)
{
- register int star = 0;
- register char *m = mask;
- register char *b;
+ int star = 0;
+ char *m = mask;
+ char *b;
if (m)
{
{
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;
+ 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++;
+ if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
+ m++;
};
}
while (*m++);
***************** Nemesi's matchcomp() / matchexec() **************
*/
-/* These functions allow the use of "compiled" masks, you compile a mask
+/** @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 lenght of the shortest string that can match
+ * - *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 (wich must be big enough
+ * 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
* 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 lenght of the field you are about to match() the mask to is
+ * - 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).
* or when you expect to use mmexec() instead of mmatch() 3 times.
*/
- /*
- * matchcomp()
- *
- * Compiles a mask into a form suitable for using in matchexec().
- */
-
+/** 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 = NULL;
- char *ls = NULL;
+ char *fs = 0;
+ char *ls = 0;
char *x1, *x2;
int l1, l2, lmin, loop, sign;
int star = 0;
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++;
- chset &= NTL_char_attrib[((*b++ = toLower(ch))) - CHAR_MIN];
- chset2 &= ~NTL_UPPER;
+ 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)
x2 = x1 + l1;
for (loop = 0; loop < lmin; loop++)
{
- ch = x1[loop];
- x1[loop] = x2[loop];
- x2[loop] = ch;
+ ch = x1[loop];
+ x1[loop] = x2[loop];
+ x2[loop] = ch;
};
x1 += lmin;
sign = l1 - l2;
}
-/*
- * matchexec()
- *
- * Executes a match with a mask previosuly compiled with matchcomp()
- * Note 1: If the mask isn't correctly produced by matchcomp() I will core
- * Note 2: 'min' MUST be the value returned by matchcomp on that mask,
- * or.... I will core even faster :-)
- * Note 3: This piece of code is not intended to be nice but efficient.
+/** 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)
{
- register const char *s = string - 1;
- register const char *b = cmask - 1;
- register int trash;
- register const char *bb, *bs;
- register char ch;
+ const char *s = string - 1;
+ const char *b = cmask - 1;
+ int trash;
+ const char *bb, *bs;
+ char ch;
tryhead:
- while ((toLower(*++s) == *++b) && *s);
+ while ((ToLower(*++s) == *++b) && *s);
if (!*s)
- return ((*b != '\000') && ((*b++ != 'Z') || (*b != '\000')));
+ return ((*b != '\0') && ((*b++ != 'Z') || (*b != '\0')));
if (*b != 'Z')
{
if (*b == 'A')
return 2;
trytail:
- while ((toLower(*--s) == *++b) && *b && (toLower(*--s) == *++b) && *b
- && (toLower(*--s) == *++b) && *b && (toLower(*--s) == *++b) && *b);
+ 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 != '\000');
+ return (*b != '\0');
};
s = --bs;
while ((ch = *++b))
{
- while ((toLower(*++s) != ch))
+ while ((ToLower(*++s) != ch))
if (--trash < 0)
- return 4;
+ return 4;
bs = s;
- trychunk:
- while ((toLower(*++s) == *++b) && *b);
+trychunk:
+ while ((ToLower(*++s) == *++b) && *b);
if (!*b)
return 0;
if (*b == 'Z')
* 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 enflate the mask)
+ * 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,
- * infact it is 1) forced to all lowercase, 2) collapsed, both things
+ * 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)
{
- register char *rtb = mask;
- register const char *rcm = cmask;
- register const char *begtail, *endtail;
+ char *rtb = mask;
+ const char *rcm = cmask;
+ const char *begtail, *endtail;
- if (rtb == NULL)
+ if (rtb ==0)
return (-1);
- if (rcm == NULL)
+ if (rcm == 0)
return (-2);
for (; (*rcm != 'Z'); rcm++, rtb++)
while (*++rcm)
switch (*rcm)
{
- case 'A':
- *rtb++ = '?';
- break;
- case 'Z':
- *rtb++ = '*';
- break;
- case '*':
- case '?':
- *rtb++ = '\\';
- default:
- *rtb++ = *rcm;
+ case 'A':
+ *rtb++ = '?';
+ break;
+ case 'Z':
+ *rtb++ = '*';
+ break;
+ case '*':
+ case '?':
+ *rtb++ = '\\';
+ default:
+ *rtb++ = *rcm;
};
*rtb++ = '*';
};
if ((*rcm == '?') || (*rcm == '*'))
*rtb++ = '\\';
- *rtb = '\000';
+ *rtb = '\0';
return (rtb - mask);
}
* "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 wich the override happens for how
+ * 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 alghoritm are copied from mmatch()
+ * 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)
{
- register const char *w, *r, *br, *bw, *rx, *rz;
- register int eat, trash;
+ 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)
/* 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 */
+ while (*w == 'A') /* Eat extra '?' before '*' in wm if got '*' in rm */
w++, eat++;
- if (*w != 'Z') /* head1<any>.. can't match head2<any>.. */
- return ((*w) || (*r)) ? 1 : 0; /* and head<nul> matches only head<nul> */
+ if (*w != 'Z') /* head1<any>.. can't match head2<any>.. */
+ return ((*w) || (*r)) ? 1 : 0; /* and head<nul> matches only head<nul> */
if (!*++w)
- return 0; /* headZ<nul> matches head<anything> */
+ return 0; /* headZ<nul> matches head<anything> */
/* Does rm have any stars in it ? let's check */
for (rx = r; *r && (*r != 'Z'); r++);
if (*w != 'Z')
{
for (; r--, (*w) && ((*w == *r) || (*w == 'A')); w++);
- if (*w != 'Z') /* headZliat1<any> fails on head<any>2tail */
- return (*w) ? 1 : 0; /* but headZliat<nul> matches head<any>tail */
- };
+ if (*w != 'Z') /* headZliat1<any> fails on head<any>2tail */
+ return (*w) ? 1 : 0; /* but headZliat<nul> matches head<any>tail */
+ }
/* match the chunks */
while (1)
- { /* This loop can't break but only return */
+ { /* 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 (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) /* Did last loop match the rest of chunk ? */
+ return 0; /* ... Yes, end of wm, matched ! */
if (*w != 'Z')
- { /* ... No, hitted 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 */
+ { /* ... 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 */
- };
- };
+ 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 */
+ 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 */
+ 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 */
+ 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 */
+ 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 */
+ (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 wich 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 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;
+ return 1;
}
else
{
w = bw;
rx = br;
- };
- };
+ }
+ }
/* Match the unused chunks of wm against the chunks of rm */
rx = r;
while (*r)
{
bw = w;
- while (eat && *r) /* the '?' we had eated make us skip as many chars */
- if (*r++ != 'Z') /* here, but can't skip stars or trailing zero */
- eat--;
+ 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 != 'Z') && (--trash < 0))
+ return 1;
if (!*r)
- break;
+ break;
for ((br = ++r), bw++;
- (*br) && (*br != 'Z') && ((*bw == *br) || (*bw == 'A')); br++, bw++);
+ (*br) && (*br != 'Z') && ((*bw == *br) || (*bw == 'A')); br++, bw++);
if (*br == 'Z')
- while (*bw == 'A')
- bw++, eat++;
+ while (*bw == 'A')
+ bw++, eat++;
if (!*bw)
- return 0;
+ 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;
+ 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;
- };
- };
- };
+ 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 <nul> or 'Z'within the tail, so just move r */
+ r = rz - eat - 1; /* can't have <nul> or 'Z' within the tail, so just move r */
while (r >= rx)
{
bw = w;
for (bw++; (*bw != *r); r--)
if (--trash < 0)
- return 1;
+ return 1;
if (!(r >= rx))
return 1;
for ((br = --r), bw++;
- (*bw) && (br >= rx) && ((*bw == *br) || (*bw == 'A')); br--, bw++);
+ (*bw) && (br >= rx) && ((*bw == *br) || (*bw == 'A')); br--, bw++);
if (!*bw)
return 0;
if (!(br >= rx))
if (*bw != 'Z')
{
if (--trash < 0)
- return 1;
+ return 1;
}
else
{
r = br;
w = bw;
- };
- };
- return 1; /* Auch... something left out ? Fail */
+ }
+ }
+ return 1; /* Auch... something left out ? Fail */
}
-/*
- * matchcompIP()
- * Compiles an IP mask into an in_mask structure
- * The given <mask> can either be:
- * - An usual irc type mask, containing * and or ?
- * - An ip number plus a /bitnumber part, that will only consider
- * the first "bitnumber" bits of the IP (bitnumber must be in 0-31 range)
- * - An ip numer plus a /ip.bit.mask.values that will consider
- * only the bits marked as 1 in the ip.bit.mask.values
- * In the last two cases both the ip number and the bitmask can specify
- * less than 4 bytes, the missing bytes then default to zero, note that
- * this is *different* from the way inet_aton() does and that this does
- * NOT happen for normal IPmasks (not containing '/')
- * If the returned value is zero the produced in_mask might match some IP,
- * if it's nonzero it will never match anything (and the imask struct is
- * set so that always fails).
- *
- * The returned structure contains 3 fields whose meaning is the following:
- * im.mask = The bits considered significative in the IP
- * im.bits = What these bits should look like to have a match
- * im.fall = If zero means that the above information used as
- * ((IP & im.mask) == im.bits) is enough to tell if the compiled
- * mask matches the given IP, nonzero means that it is needed,
- * in case they did match, to call also the usual text match
- * functions, because the mask wasn't "completely compiled"
- *
- * They should be used like:
- * matchcompIP(&im, mask);
- * if ( ((IP & im.mask)!=im.bits)) || (im.fall&&match(mask,inet_ntoa(IP))) )
- * { handle_non_match } else { handle_match };
- * instead of:
- * if ( match(mask, inet_ntoa(IP)) )
- * { handle_non_match } else { handle_match };
- *
- * Note: This function could be smarter when dealing with complex masks,
- * this implementation is quite lazy and understands only very simple
- * cases, whatever contains a ? anywhere or contains a '*' that isn't
- * part of a trailing '.*' will fallback to text-match, this could be
- * avoided for masks like 12?3.5.6 12.*.3.4 1.*.*.2 72?72?72?72 and
- * so on that "could" be completely compiled to IP masks.
- * If you try to improve this be aware of the fact that ? and *
- * could match both dots and digits and we _must_ always reject
- * what doesn't match in textform (like leading zeros and so on),
- * so it's a LOT more tricky than it might seem. By now most common
- * cases are optimized.
+/** 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 matchcompIP(struct in_mask *imask, const char *mask)
+int ipmask_check(const struct irc_in_addr *addr, const struct irc_in_addr *mask, unsigned char bits)
{
- register const char *m = mask;
- register unsigned int bits = 0;
- register unsigned int filt = 0;
- register int unco = 0;
- register int digits = 0;
- register int shift = 24;
- register int tmp = 0;
-
- do
- {
- switch (*m)
- {
- case '\\':
- if ((m[1] == '\\') || (m[1] == '*') || (m[1] == '?')
- || (m[1] == '\000'))
- break;
- continue;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (digits && !tmp) /* Leading zeros */
- break;
- digits++;
- tmp *= 10;
- tmp += (*m - '0'); /* Can't overflow, INT_MAX > 2559 */
- if (tmp > 255)
- break;
- continue;
- case '\000':
- filt = 0xFFFFFFFF;
- /* Intentional fallthrough */
- case '.':
- if ((!shift) != (!*m))
- break;
- /* Intentional fallthrough */
- case '/':
- bits |= (tmp << shift);
- shift -= 8;
- digits = 0;
- tmp = 0;
- if (*m != '/')
- continue;
- shift = 24;
- do
- {
- m++;
- if (isDigit(*m))
- {
- if (digits && !tmp) /* Leading zeros */
- break;
- digits++;
- tmp *= 10;
- tmp += (*m - '0'); /* Can't overflow, INT_MAX > 2559 */
- if (tmp > 255)
- break;
- }
- else
- {
- switch (*m)
- {
- case '.':
- case '\000':
- if ((!shift) && (*m))
- break;
- filt |= (tmp << shift);
- shift -= 8;
- tmp = 0;
- digits = 0;
- continue;
- default:
- break;
- }
- break;
- }
- }
- while (*m);
- if (*m)
- break;
- if (filt && (!(shift < 16)) && (!(filt & 0xE0FFFFFF)))
- filt = 0xFFFFFFFF << (32 - ((filt >> 24)));
- bits &= filt;
- continue;
- case '?':
- unco = 1;
- /* Intentional fallthrough */
- case '*':
- if (digits)
- unco = 1;
- filt = (0xFFFFFFFF << (shift)) << 8;
- while (*++m)
- {
- if (isDigit(*m))
- unco = 1;
- else
- {
- switch (*m)
- {
- case '.':
- if (m[1] != '*')
- unco = 1;
- if (!shift)
- break;
- shift -= 8;
- continue;
- case '?':
- unco = 1;
- case '*':
- continue;
- default:
- break;
- }
- break;
- }
- }
- if (*m)
- break;
- continue;
- default:
- break;
- }
-
- /* If we get here there is some error and this can't ever match */
- filt = 0;
- bits = ~0;
- unco = 0;
- break; /* This time break the loop :) */
+ 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;
}
- while (*m++);
-
- imask->bits.s_addr = htonl(bits);
- imask->mask.s_addr = htonl(filt);
- imask->fall = unco;
- return ((bits & ~filt) ? -1 : 0);
-
+ return -1;
}