ircproxy  The Ultimate Cyborg

match.h File Reference


Detailed Description

This file contains the declaration of match related functions.

Definition in file match.h.

#include <netinet/in.h>

Go to the source code of this file.

Classes

struct  in_mask
 An IP mask used for matchcompIP. More...

Functions

int mmatch (char const *old_mask, char const *new_mask)
 Mask match.
int match (char const *mask, char const *string)
 Wildcard match.
char * collapse (char *mask)
 Collapse mask.
int matchcomp (char *cmask, int *minlen, int *charset, char const *mask)
 Match compile function.
int matchexec (char const *string, size_t len, char const *cmask, int minlen)
 Match execute function.
int matchdecomp (char *mask, char const *cmask)
 Match decompile function.
int mmexec (char const *wcm, int wminlen, char const *rcm, int rminlen)
 Match execute function.
int matchcompIP (in_mask *imask, char const *mask)
 Match compile function.


Function Documentation

int mmatch ( char const *  old_mask,
char const *  new_mask 
)

Mask match.

Definition at line 30 of file match.cc.

00031 {
00032   register char const* m = old_mask;
00033   register char const* n = new_mask;
00034   char const* ma = m;
00035   char const* na = n;
00036   int wild = 0;
00037   int mq = 0, nq = 0;
00038 
00039   while (1)
00040   {
00041     if (*m == '*')
00042     {
00043       while (*m == '*')
00044         m++;
00045       wild = 1;
00046       ma = m;
00047       na = n;
00048     }
00049 
00050     if (!*m)
00051     {
00052       if (!*n)
00053         return 0;
00054       for (m--; (m > old_mask) && (*m == '?'); m--)
00055         ;
00056       if ((*m == '*') && (m > old_mask) && (m[-1] != '\\'))
00057         return 0;
00058       if (!wild)
00059         return 1;
00060       m = ma;
00061 
00062       /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
00063       if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
00064         ++na;
00065 
00066       n = ++na;
00067     }
00068     else if (!*n)
00069     {
00070       while (*m == '*')
00071         m++;
00072       return (*m != 0);
00073     }
00074     if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
00075     {
00076       m++;
00077       mq = 1;
00078     }
00079     else
00080       mq = 0;
00081 
00082     /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
00083     if ((*n == '\\') && ((n[1] == '*') || (n[1] == '?')))
00084     {
00085       n++;
00086       nq = 1;
00087     }
00088     else
00089       nq = 0;
00090 
00091 /*
00092  * This `if' has been changed compared to match() to do the following:
00093  * Match when:
00094  *   old (m)         new (n)         boolean expression
00095  *    *               any             (*m == '*' && !mq) ||
00096  *    ?               any except '*'  (*m == '?' && !mq && (*n != '*' || nq)) ||
00097  * any except * or ?  same as m       (!((*m == '*' || *m == '?') && !mq) &&
00098  *                                      ToLower(*m) == ToLower(*n) &&
00099  *                                        !((mq && !nq) || (!mq && nq)))
00100  *
00101  * Here `any' also includes \* and \? !
00102  *
00103  * After reworking the boolean expressions, we get:
00104  * (Optimized to use boolean shortcircuits, with most frequently occuring
00105  *  cases upfront (which took 2 hours!)).
00106  */
00107     if ((*m == '*' && !mq) ||
00108         ((!mq || nq) && ToLower(*m) == ToLower(*n)) ||
00109         (*m == '?' && !mq && (*n != '*' || nq)))
00110     {
00111       if (*m)
00112         m++;
00113       if (*n)
00114         n++;
00115     }
00116     else
00117     {
00118       if (!wild)
00119         return 1;
00120       m = ma;
00121 
00122       /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
00123       if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
00124         ++na;
00125 
00126       n = ++na;
00127     }
00128   }
00129 }

int match ( char const *  mask,
char const *  string 
)

Wildcard match.

Definition at line 149 of file match.cc.

00150 {
00151   register char const* m = mask, *s = string;
00152   register char ch;
00153   char const* bm, *bs;          /* Will be reg anyway on a decent CPU/compiler */
00154 
00155   /* Process the "head" of the mask, if any */
00156   while ((ch = *m++) && (ch != '*'))
00157     switch (ch)
00158     {
00159       case '\\':
00160         if (*m == '?' || *m == '*')
00161           ch = *m++;
00162       default:
00163         if (ToLower(*s) != ToLower(ch))
00164           return 1;
00165       case '?':
00166         if (!*s++)
00167           return 1;
00168     };
00169   if (!ch)
00170     return *s;
00171 
00172   /* We got a star: quickly find if/where we match the next char */
00173 got_star:
00174   bm = m;                       /* Next try rollback here */
00175   while ((ch = *m++))
00176     switch (ch)
00177     {
00178       case '?':
00179         if (!*s++)
00180           return 1;
00181       case '*':
00182         bm = m;
00183         continue;               /* while */
00184       case '\\':
00185         if (*m == '?' || *m == '*')
00186           ch = *m++;
00187       default:
00188         goto break_while;       /* C is structured ? */
00189     };
00190 break_while:
00191   if (!ch)
00192     return 0;                   /* mask ends with '*', we got it */
00193   ch = ToLower(ch);
00194   while (ToLower(*s++) != ch)
00195     if (!*s)
00196       return 1;
00197   bs = s;                       /* Next try start from here */
00198 
00199   /* Check the rest of the "chunk" */
00200   while ((ch = *m++))
00201   {
00202     switch (ch)
00203     {
00204       case '*':
00205         goto got_star;
00206       case '\\':
00207         if (*m == '?' || *m == '*')
00208           ch = *m++;
00209       default:
00210         if (ToLower(*s) != ToLower(ch))
00211         {
00212           m = bm;
00213           s = bs;
00214           goto got_star;
00215         };
00216       case '?':
00217         if (!*s++)
00218           return 1;
00219     };
00220   };
00221   if (*s)
00222   {
00223     m = bm;
00224     s = bs;
00225     goto got_star;
00226   };
00227   return 0;
00228 }

char* collapse ( char *  mask  ) 

Collapse mask.

Definition at line 241 of file match.cc.

00242 {
00243   register int star = 0;
00244   register char* m = mask;
00245   register char* b;
00246 
00247   if (m)
00248   {
00249     do
00250     {
00251       if ((*m == '*') && ((m[1] == '*') || (m[1] == '?')))
00252       {
00253         b = m;
00254         do
00255         {
00256           if (*m == '*')
00257             star = 1;
00258           else
00259           {
00260             if (star && (*m != '?'))
00261             {
00262               *b++ = '*';
00263               star = 0;
00264             };
00265             *b++ = *m;
00266             if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
00267               *b++ = *++m;
00268           };
00269         }
00270         while (*m++);
00271         break;
00272       }
00273       else
00274       {
00275         if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
00276           m++;
00277       };
00278     }
00279     while (*m++);
00280   };
00281   return mask;
00282 }

int matchcomp ( char *  cmask,
int *  minlen,
int *  charset,
char const *  mask 
)

Match compile function.

Definition at line 364 of file match.cc.

00365 {
00366   char const* m = mask;
00367   char* b = cmask;
00368   char* fs = NULL;
00369   char* ls = NULL;
00370   char* x1, *x2;
00371   int l1, l2, lmin, loop, sign;
00372   int star = 0;
00373   int cnt = 0;
00374   char ch;
00375   int chset = ~0;
00376   int chset2 = (NTL_LOWER | NTL_UPPER);
00377 
00378   if (m)
00379     while ((ch = *m++))
00380       switch (ch)
00381       {
00382         case '*':
00383           star = 1;
00384           break;
00385         case '?':
00386           cnt++;
00387           *b++ = 'A';
00388           chset2 &= ~NTL_LOWER;
00389           break;
00390         case '\\':
00391           if ((*m == '?') || (*m == '*'))
00392             ch = *m++;
00393         default:
00394           if (star)
00395           {
00396             ls = b;
00397             fs = fs ? fs : b;
00398             *b++ = 'Z';
00399             chset2 &= ~NTL_LOWER;
00400             star = 0;
00401           };
00402           cnt++;
00403           chset &= IRCD_CharAttrTab[((*b++ = ToLower(ch))) - std::numeric_limits<char>::min()];
00404           chset2 &= ~NTL_UPPER;
00405       };
00406 
00407   if (charset)
00408     *charset = (chset | chset2);
00409 
00410   if (star)
00411   {
00412     ls = b;
00413     fs = (fs ? fs : b);
00414     *b++ = 'Z';
00415   };
00416 
00417   if (ls)
00418   {
00419     for (x1 = ls + 1, x2 = (b - 1); x1 < x2; x1++, x2--)
00420     {
00421       ch = *x1;
00422       *x1 = *x2;
00423       *x2 = ch;
00424     };
00425     l1 = (ls - fs);
00426     l2 = (b - ls);
00427     x1 = fs;
00428     while ((lmin = (l1 < l2) ? l1 : l2))
00429     {
00430       x2 = x1 + l1;
00431       for (loop = 0; loop < lmin; loop++)
00432       {
00433         ch = x1[loop];
00434         x1[loop] = x2[loop];
00435         x2[loop] = ch;
00436       };
00437       x1 += lmin;
00438       sign = l1 - l2;
00439       l1 -= (sign < 0) ? 0 : lmin;
00440       l2 -= (sign > 0) ? 0 : lmin;
00441     };
00442   };
00443 
00444   *b = '\000';
00445   *minlen = cnt;
00446   return (b - cmask);
00447 }

int matchexec ( char const *  string,
size_t  len,
char const *  cmask,
int  minlen 
)

Match execute function.

Definition at line 464 of file match.cc.

00465 {
00466   char const* bs = string - 1;
00467   char const* s = string + len;
00468   char const* b = cmask - 1;
00469   int trash;
00470   char const* bb;
00471   char ch;
00472 
00473 tryhead:
00474   while ((ToLower(*++bs) == *++b) && bs != s);
00475   if (bs == s)
00476     return ((*b != '\000') && ((*b++ != 'Z') || (*b != '\000')));
00477   if (*b != 'Z')
00478   {
00479     if (*b == 'A')
00480       goto tryhead;
00481     return 1;
00482   };
00483 
00484   if ((trash = (len - minlen)) < 0)
00485     return 2;
00486 
00487 trytail:
00488   while ((ToLower(*--s) == *++b) && *b && (ToLower(*--s) == *++b) && *b
00489       && (ToLower(*--s) == *++b) && *b && (ToLower(*--s) == *++b) && *b);
00490   if (*b != 'Z')
00491   {
00492     if (*b == 'A')
00493       goto trytail;
00494     return (*b != '\000');
00495   };
00496 
00497   s = --bs;
00498   bb = b;
00499 
00500   while ((ch = *++b))
00501   {
00502     while ((ToLower(*++s) != ch))
00503       if (--trash < 0)
00504         return 4;
00505     bs = s;
00506 
00507   trychunk:
00508     while ((ToLower(*++s) == *++b) && *b);
00509     if (!*b)
00510       return 0;
00511     if (*b == 'Z')
00512     {
00513       bs = --s;
00514       bb = b;
00515       continue;
00516     };
00517     if (*b == 'A')
00518       goto trychunk;
00519 
00520     b = bb;
00521     s = bs;
00522     if (--trash < 0)
00523       return 5;
00524   };
00525 
00526   return 0;
00527 }

int matchdecomp ( char *  mask,
char const *  cmask 
)

Match decompile function.

Definition at line 542 of file match.cc.

00543 {
00544   register char* rtb = mask;
00545   register char const* rcm = cmask;
00546   register char const* begtail, *endtail;
00547 
00548   if (rtb == NULL)
00549     return (-1);
00550 
00551   if (rcm == NULL)
00552     return (-2);
00553 
00554   for (; (*rcm != 'Z'); rcm++, rtb++)
00555   {
00556     if ((*rcm == '?') || (*rcm == '*'))
00557       *rtb++ = '\\';
00558     if (!((*rtb = ((*rcm == 'A') ? '?' : *rcm))))
00559       return (rtb - mask);
00560   };
00561 
00562   begtail = rcm++;
00563   *rtb++ = '*';
00564 
00565   while (*rcm && (*rcm != 'Z'))
00566     rcm++;
00567 
00568   endtail = rcm;
00569 
00570   if (*rcm)
00571   {
00572     while (*++rcm)
00573       switch (*rcm)
00574       {
00575         case 'A':
00576           *rtb++ = '?';
00577           break;
00578         case 'Z':
00579           *rtb++ = '*';
00580           break;
00581         case '*':
00582         case '?':
00583           *rtb++ = '\\';
00584         default:
00585           *rtb++ = *rcm;
00586       };
00587     *rtb++ = '*';
00588   };
00589 
00590   for (rcm = endtail; (--rcm) > begtail; *rtb++ = ((*rcm == 'A') ? '?' : *rcm))
00591     if ((*rcm == '?') || (*rcm == '*'))
00592       *rtb++ = '\\';
00593 
00594   *rtb = '\000';
00595   return (rtb - mask);
00596 }

int mmexec ( char const *  wcm,
int  wminlen,
char const *  rcm,
int  rminlen 
)

Match execute function.

Definition at line 613 of file match.cc.

00614 {
00615   register char const* w, *r, *br, *bw, *rx, *rz;
00616   register int eat, trash;
00617 
00618   /* First of all rm must have enough non-stars to 'contain' wm */
00619   if ((trash = rminlen - wminlen) < 0)
00620     return 1;
00621   w = wcm;
00622   r = rcm;
00623   eat = 0;
00624 
00625   /* Let's start the game, remember that '*' is mapped to 'Z', '?'
00626      is mapped to 'A' and that head?*??*?chunk*???*tail becomes
00627      headAAAAZliatAAAZchunk for compiled masks */
00628 
00629   /* Match the head of wm with the head of rm */
00630   for (; (*r) && (*r != 'Z') && ((*w == *r) || (*w == 'A')); r++, w++);
00631   if (*r == 'Z')
00632     while (*w == 'A')           /* Eat extra '?' before '*' in wm if got '*' in rm */
00633       w++, eat++;
00634   if (*w != 'Z')                /* head1<any>.. can't match head2<any>.. */
00635     return ((*w) || (*r)) ? 1 : 0;      /* and head<nul> matches only head<nul> */
00636   if (!*++w)
00637     return 0;                   /* headZ<nul> matches head<anything>    */
00638 
00639   /* Does rm have any stars in it ? let's check */
00640   for (rx = r; *r && (*r != 'Z'); r++);
00641   if (!*r)
00642   {
00643     /* rm has no stars and thus isn't a mask but it's just a flat
00644        string: special handling occurs here, note that eat must be 0 here */
00645 
00646     /* match the tail */
00647     if (*w != 'Z')
00648     {
00649       for (; r--, (*w) && ((*w == *r) || (*w == 'A')); w++);
00650       if (*w != 'Z')            /* headZliat1<any> fails on head<any>2tail  */
00651         return (*w) ? 1 : 0;    /* but headZliat<nul> matches head<any>tail */
00652     };
00653 
00654     /* match the chunks */
00655     while (1)
00656     {                           /* This loop can't break but only return   */
00657 
00658       for (bw = w++; (*w != *rx); rx++) /* Seek the 1st char of the chunk */
00659         if (--trash < 0)        /* See if we can trash one more char of rm */
00660           return 1;             /* If not we can only fail of course       */
00661       for (r = ++rx, w++; (*w) && ((*w == *r) || (*w == 'A')); r++, w++);
00662       if (!*w)                  /* Did last loop match the rest of chunk ? */
00663         return 0;               /* ... Yes, end of wm, matched !           */
00664       if (*w != 'Z')
00665       {                         /* ... No, hitted non-star                 */
00666         w = bw;                 /* Rollback at beginning of chunk          */
00667         if (--trash < 0)        /* Trashed the char where this try started */
00668           return 1;             /* if we can't trash more chars fail       */
00669       }
00670       else
00671       {
00672         rx = r;                 /* Successfully matched a chunk, move rx   */
00673       };                        /* and go on with the next one             */
00674     };
00675   };
00676 
00677   /* rm has at least one '*' and thus is a 'real' mask */
00678   rz = r++;                     /* rx = unused of head, rz = beg-tail */
00679 
00680   /* Match the tail of wm (if any) against the tail of rm */
00681   if (*w != 'Z')
00682   {
00683     for (; (*w) && (*r != 'Z') && ((*w == *r) || (*w == 'A')); w++, r++);
00684     if (*r == 'Z')              /* extra '?' before tail are fluff, just flush 'em */
00685       while (*w == 'A')
00686         w++;
00687     if (*w != 'Z')              /* We aren't matching a chunk, can't rollback      */
00688       return (*w) ? 1 : 0;
00689   };
00690 
00691   /* Match the chunks of wm against what remains of the head of rm */
00692   while (1)
00693   {
00694     bw = w;
00695     for (bw++; (rx < rz) && (*bw != *rx); rx++) /* Seek the first           */
00696       if (--trash < 0)          /* waste some trash reserve */
00697         return 1;
00698     if (!(rx < rz))             /* head finished            */
00699       break;
00700     for (bw++, (br = ++rx);
00701         (br < rz) && (*bw) && ((*bw == *br) || (*bw == 'A')); br++, bw++);
00702     if (!(br < rz))             /* Note that we didn't use any 'eat' char yet, if  */
00703       while (*bw == 'A')        /* there were eat-en chars the head would be over  */
00704         bw++, eat++;            /* Happens only at end of head, and eat is still 0 */
00705     if (!*bw)
00706       return 0;
00707     if (*bw != 'Z')
00708     {
00709       eat = 0;
00710       if (!(br < rz))
00711       {                         /* If we failed because we got the end of head */
00712         trash -= (br - rx);     /* it makes no sense to rollback, just trash   */
00713         if (--trash < 0)        /* all the rest of the head wich isn't long    */
00714           return 1;             /* enough for this chunk and go out of this    */
00715         break;                  /* loop, then we try with the chunks of rm     */
00716       };
00717       if (--trash < 0)
00718         return 1;
00719     }
00720     else
00721     {
00722       w = bw;
00723       rx = br;
00724     };
00725   };
00726 
00727   /* Match the unused chunks of wm against the chunks of rm */
00728   rx = r;
00729   for (; *r && (*r != 'Z'); r++);
00730   rz = r;
00731   if (*r++)
00732   {
00733     while (*r)
00734     {
00735       bw = w;
00736       while (eat && *r)         /* the '?' we had eated make us skip as many chars */
00737         if (*r++ != 'Z')        /* here, but can't skip stars or trailing zero     */
00738           eat--;
00739       for (bw++; (*r) && (*bw != *r); r++)
00740         if ((*r != 'Z') && (--trash < 0))
00741           return 1;
00742       if (!*r)
00743         break;
00744       for ((br = ++r), bw++;
00745           (*br) && (*br != 'Z') && ((*bw == *br) || (*bw == 'A')); br++, bw++);
00746       if (*br == 'Z')
00747         while (*bw == 'A')
00748           bw++, eat++;
00749       if (!*bw)
00750         return 0;
00751       if (*bw != 'Z')
00752       {
00753         eat = 0;
00754         if ((!*br) || (*r == 'Z'))
00755         {                       /* If we hit the end of rm or a star in it */
00756           trash -= (br - r);    /* makes no sense to rollback within this  */
00757           if (trash < 0)        /* same chunk of br, skip it all and then  */
00758             return 1;           /* either rollback or break this loop if   */
00759           if (!*br)             /* it was the end of rm                    */
00760             break;
00761           r = br;
00762         };
00763         if (--trash < 0)
00764           return 1;
00765       }
00766       else
00767       {
00768         r = br;
00769         w = bw;
00770       };
00771     };
00772   };
00773 
00774   /* match the remaining chunks of wm against what remains of the tail of rm */
00775   r = rz - eat - 1;             /* can't have <nul> or 'Z'within the tail, so just move r */
00776   while (r >= rx)
00777   {
00778     bw = w;
00779     for (bw++; (*bw != *r); r--)
00780       if (--trash < 0)
00781         return 1;
00782     if (!(r >= rx))
00783       return 1;
00784     for ((br = --r), bw++;
00785         (*bw) && (br >= rx) && ((*bw == *br) || (*bw == 'A')); br--, bw++);
00786     if (!*bw)
00787       return 0;
00788     if (!(br >= rx))
00789       return 1;
00790     if (*bw != 'Z')
00791     {
00792       if (--trash < 0)
00793         return 1;
00794     }
00795     else
00796     {
00797       r = br;
00798       w = bw;
00799     };
00800   };
00801   return 1;                     /* Auch... something left out ? Fail */
00802 }

int matchcompIP ( in_mask imask,
char const *  mask 
)

Match compile function.

Definition at line 851 of file match.cc.

References in_mask::bits, in_mask::fall, and in_mask::mask.

00852 {
00853   register char const* m = mask;
00854   register unsigned int bits = 0;
00855   register unsigned int filt = 0;
00856   register int unco = 0;
00857   register int digits = 0;
00858   register int shift = 24;
00859   register int tmp = 0;
00860 
00861   do
00862   {
00863     switch (*m)
00864     {
00865       case '\\':
00866         if ((m[1] == '\\') || (m[1] == '*') || (m[1] == '?')
00867             || (m[1] == '\000'))
00868           break;
00869         continue;
00870       case '0':
00871       case '1':
00872       case '2':
00873       case '3':
00874       case '4':
00875       case '5':
00876       case '6':
00877       case '7':
00878       case '8':
00879       case '9':
00880         if (digits && !tmp)     /* Leading zeros */
00881           break;
00882         digits++;
00883         tmp *= 10;
00884         tmp += (*m - '0');      /* Can't overflow, INT_MAX > 2559 */
00885         if (tmp > 255)
00886           break;
00887         continue;
00888       case '\000':
00889         filt = 0xFFFFFFFF;
00890         /* Intentional fallthrough */
00891       case '.':
00892         if ((!shift) != (!*m))
00893           break;
00894         /* Intentional fallthrough */
00895       case '/':
00896         bits |= (tmp << shift);
00897         shift -= 8;
00898         digits = 0;
00899         tmp = 0;
00900         if (*m != '/')
00901           continue;
00902         shift = 24;
00903         do
00904         {
00905           m++;
00906           if (IsDigit(*m))
00907           {
00908             if (digits && !tmp) /* Leading zeros */
00909               break;
00910             digits++;
00911             tmp *= 10;
00912             tmp += (*m - '0');  /* Can't overflow, INT_MAX > 2559 */
00913             if (tmp > 255)
00914               break;
00915           }
00916           else
00917           {
00918             switch (*m)
00919             {
00920               case '.':
00921               case '\000':
00922                 if ((!shift) && (*m))
00923                   break;
00924                 filt |= (tmp << shift);
00925                 shift -= 8;
00926                 tmp = 0;
00927                 digits = 0;
00928                 continue;
00929               default:
00930                 break;
00931             }
00932             break;
00933           }
00934         }
00935         while (*m);
00936         if (*m)
00937           break;
00938         if (filt && (!(shift < 16)) && (!(filt & 0xE0FFFFFF)))
00939           filt = 0xFFFFFFFF << (32 - ((filt >> 24)));
00940         bits &= filt;
00941         continue;
00942       case '?':
00943         unco = 1;
00944         /* Intentional fallthrough */
00945       case '*':
00946         if (digits)
00947           unco = 1;
00948         filt = (0xFFFFFFFF << (shift)) << 8;
00949         while (*++m)
00950         {
00951           if (IsDigit(*m))
00952             unco = 1;
00953           else
00954           {
00955             switch (*m)
00956             {
00957               case '.':
00958                 if (m[1] != '*')
00959                   unco = 1;
00960                 if (!shift)
00961                   break;
00962                 shift -= 8;
00963                 continue;
00964               case '?':
00965                 unco = 1;
00966               case '*':
00967                 continue;
00968               default:
00969                 break;
00970             }
00971             break;
00972           }
00973         }
00974         if (*m)
00975           break;
00976         continue;
00977       default:
00978         break;
00979     }
00980 
00981     /* If we get here there is some error and this can't ever match */
00982     filt = 0;
00983     bits = ~0;
00984     unco = 0;
00985     break;                      /* This time break the loop :) */
00986 
00987   }
00988   while (*m++);
00989 
00990   imask->bits.s_addr = htonl(bits);
00991   imask->mask.s_addr = htonl(filt);
00992   imask->fall = unco;
00993   return ((bits & ~filt) ? -1 : 0);
00994 }


Copyright © 2005-2007 Carlo Wood.  All rights reserved.