Annotation of embedaddon/dnsmasq/src/util.c, revision 1.1.1.3

1.1.1.3 ! misho       1: /* dnsmasq is Copyright (c) 2000-2016 Simon Kelley
1.1       misho       2: 
                      3:    This program is free software; you can redistribute it and/or modify
                      4:    it under the terms of the GNU General Public License as published by
                      5:    the Free Software Foundation; version 2 dated June, 1991, or
                      6:    (at your option) version 3 dated 29 June, 2007.
                      7:  
                      8:    This program is distributed in the hope that it will be useful,
                      9:    but WITHOUT ANY WARRANTY; without even the implied warranty of
                     10:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     11:    GNU General Public License for more details.
                     12:       
                     13:    You should have received a copy of the GNU General Public License
                     14:    along with this program.  If not, see <http://www.gnu.org/licenses/>.
                     15: */
                     16: 
                     17: /* The SURF random number generator was taken from djbdns-1.05, by 
                     18:    Daniel J Bernstein, which is public domain. */
                     19: 
                     20: 
                     21: #include "dnsmasq.h"
                     22: 
                     23: #ifdef HAVE_BROKEN_RTC
                     24: #include <sys/times.h>
                     25: #endif
                     26: 
                     27: #if defined(LOCALEDIR) || defined(HAVE_IDN)
                     28: #include <idna.h>
                     29: #endif
                     30: 
                     31: /* SURF random number generator */
                     32: 
                     33: static u32 seed[32];
                     34: static u32 in[12];
                     35: static u32 out[8];
1.1.1.2   misho      36: static int outleft = 0;
1.1       misho      37: 
                     38: void rand_init()
                     39: {
                     40:   int fd = open(RANDFILE, O_RDONLY);
                     41:   
                     42:   if (fd == -1 ||
                     43:       !read_write(fd, (unsigned char *)&seed, sizeof(seed), 1) ||
                     44:       !read_write(fd, (unsigned char *)&in, sizeof(in), 1))
                     45:     die(_("failed to seed the random number generator: %s"), NULL, EC_MISC);
                     46:   
                     47:   close(fd);
                     48: }
                     49: 
                     50: #define ROTATE(x,b) (((x) << (b)) | ((x) >> (32 - (b))))
                     51: #define MUSH(i,b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x,b));
                     52: 
                     53: static void surf(void)
                     54: {
                     55:   u32 t[12]; u32 x; u32 sum = 0;
                     56:   int r; int i; int loop;
                     57: 
                     58:   for (i = 0;i < 12;++i) t[i] = in[i] ^ seed[12 + i];
                     59:   for (i = 0;i < 8;++i) out[i] = seed[24 + i];
                     60:   x = t[11];
                     61:   for (loop = 0;loop < 2;++loop) {
                     62:     for (r = 0;r < 16;++r) {
                     63:       sum += 0x9e3779b9;
                     64:       MUSH(0,5) MUSH(1,7) MUSH(2,9) MUSH(3,13)
                     65:       MUSH(4,5) MUSH(5,7) MUSH(6,9) MUSH(7,13)
                     66:       MUSH(8,5) MUSH(9,7) MUSH(10,9) MUSH(11,13)
                     67:     }
                     68:     for (i = 0;i < 8;++i) out[i] ^= t[i + 4];
                     69:   }
                     70: }
                     71: 
                     72: unsigned short rand16(void)
                     73: {
1.1.1.2   misho      74:   if (!outleft) 
                     75:     {
                     76:       if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
                     77:       surf();
                     78:       outleft = 8;
                     79:     }
                     80:   
                     81:   return (unsigned short) out[--outleft];
                     82: }
                     83: 
1.1.1.3 ! misho      84: u32 rand32(void)
        !            85: {
        !            86:  if (!outleft) 
        !            87:     {
        !            88:       if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
        !            89:       surf();
        !            90:       outleft = 8;
        !            91:     }
        !            92:   
        !            93:   return out[--outleft]; 
        !            94: }
        !            95: 
1.1.1.2   misho      96: u64 rand64(void)
                     97: {
1.1       misho      98:   static int outleft = 0;
                     99: 
1.1.1.2   misho     100:   if (outleft < 2)
                    101:     {
                    102:       if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
                    103:       surf();
                    104:       outleft = 8;
                    105:     }
                    106:   
                    107:   outleft -= 2;
1.1       misho     108: 
1.1.1.2   misho     109:   return (u64)out[outleft+1] + (((u64)out[outleft]) << 32);
1.1       misho     110: }
                    111: 
                    112: static int check_name(char *in)
                    113: {
                    114:   /* remove trailing . 
                    115:      also fail empty string and label > 63 chars */
                    116:   size_t dotgap = 0, l = strlen(in);
                    117:   char c;
                    118:   int nowhite = 0;
                    119:   
                    120:   if (l == 0 || l > MAXDNAME) return 0;
                    121:   
                    122:   if (in[l-1] == '.')
                    123:     {
                    124:       in[l-1] = 0;
1.1.1.2   misho     125:       nowhite = 1;
1.1       misho     126:     }
1.1.1.2   misho     127: 
1.1       misho     128:   for (; (c = *in); in++)
                    129:     {
                    130:       if (c == '.')
                    131:        dotgap = 0;
                    132:       else if (++dotgap > MAXLABEL)
                    133:        return 0;
                    134:       else if (isascii((unsigned char)c) && iscntrl((unsigned char)c)) 
                    135:        /* iscntrl only gives expected results for ascii */
                    136:        return 0;
                    137: #if !defined(LOCALEDIR) && !defined(HAVE_IDN)
                    138:       else if (!isascii((unsigned char)c))
                    139:        return 0;
                    140: #endif
                    141:       else if (c != ' ')
                    142:        nowhite = 1;
                    143:     }
                    144: 
                    145:   if (!nowhite)
                    146:     return 0;
                    147: 
                    148:   return 1;
                    149: }
                    150: 
                    151: /* Hostnames have a more limited valid charset than domain names
                    152:    so check for legal char a-z A-Z 0-9 - _ 
                    153:    Note that this may receive a FQDN, so only check the first label 
                    154:    for the tighter criteria. */
                    155: int legal_hostname(char *name)
                    156: {
                    157:   char c;
1.1.1.2   misho     158:   int first;
1.1       misho     159: 
                    160:   if (!check_name(name))
                    161:     return 0;
                    162: 
1.1.1.2   misho     163:   for (first = 1; (c = *name); name++, first = 0)
1.1       misho     164:     /* check for legal char a-z A-Z 0-9 - _ . */
                    165:     {
                    166:       if ((c >= 'A' && c <= 'Z') ||
                    167:          (c >= 'a' && c <= 'z') ||
1.1.1.2   misho     168:          (c >= '0' && c <= '9'))
                    169:        continue;
                    170: 
                    171:       if (!first && (c == '-' || c == '_'))
1.1       misho     172:        continue;
                    173:       
                    174:       /* end of hostname part */
                    175:       if (c == '.')
                    176:        return 1;
                    177:       
                    178:       return 0;
                    179:     }
                    180:   
                    181:   return 1;
                    182: }
                    183:   
                    184: char *canonicalise(char *in, int *nomem)
                    185: {
                    186:   char *ret = NULL;
                    187: #if defined(LOCALEDIR) || defined(HAVE_IDN)
                    188:   int rc;
                    189: #endif
                    190: 
                    191:   if (nomem)
                    192:     *nomem = 0;
                    193:   
                    194:   if (!check_name(in))
                    195:     return NULL;
                    196:   
                    197: #if defined(LOCALEDIR) || defined(HAVE_IDN)
                    198:   if ((rc = idna_to_ascii_lz(in, &ret, 0)) != IDNA_SUCCESS)
                    199:     {
                    200:       if (ret)
                    201:        free(ret);
                    202: 
                    203:       if (nomem && (rc == IDNA_MALLOC_ERROR || rc == IDNA_DLOPEN_ERROR))
                    204:        {
                    205:          my_syslog(LOG_ERR, _("failed to allocate memory"));
                    206:          *nomem = 1;
                    207:        }
                    208:     
                    209:       return NULL;
                    210:     }
                    211: #else
                    212:   if ((ret = whine_malloc(strlen(in)+1)))
                    213:     strcpy(ret, in);
                    214:   else if (nomem)
                    215:     *nomem = 1;    
                    216: #endif
                    217: 
                    218:   return ret;
                    219: }
                    220: 
                    221: unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
                    222: {
                    223:   int j;
                    224:   
                    225:   while (sval && *sval)
                    226:     {
                    227:       unsigned char *cp = p++;
                    228:       for (j = 0; *sval && (*sval != '.'); sval++, j++)
1.1.1.3 ! misho     229:        {
        !           230: #ifdef HAVE_DNSSEC
        !           231:          if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE)
        !           232:            *p++ = (*(++sval))-1;
        !           233:          else
        !           234: #endif         
        !           235:            *p++ = *sval;
        !           236:        }
1.1       misho     237:       *cp  = j;
                    238:       if (*sval)
                    239:        sval++;
                    240:     }
                    241:   return p;
                    242: }
                    243: 
                    244: /* for use during startup */
                    245: void *safe_malloc(size_t size)
                    246: {
                    247:   void *ret = malloc(size);
                    248:   
                    249:   if (!ret)
                    250:     die(_("could not get memory"), NULL, EC_NOMEM);
                    251:      
                    252:   return ret;
                    253: }    
                    254: 
                    255: void safe_pipe(int *fd, int read_noblock)
                    256: {
                    257:   if (pipe(fd) == -1 || 
                    258:       !fix_fd(fd[1]) ||
                    259:       (read_noblock && !fix_fd(fd[0])))
                    260:     die(_("cannot create pipe: %s"), NULL, EC_MISC);
                    261: }
                    262: 
                    263: void *whine_malloc(size_t size)
                    264: {
                    265:   void *ret = malloc(size);
                    266: 
                    267:   if (!ret)
                    268:     my_syslog(LOG_ERR, _("failed to allocate %d bytes"), (int) size);
                    269: 
                    270:   return ret;
                    271: }
                    272: 
                    273: int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2)
                    274: {
                    275:   if (s1->sa.sa_family == s2->sa.sa_family)
                    276:     { 
                    277:       if (s1->sa.sa_family == AF_INET &&
                    278:          s1->in.sin_port == s2->in.sin_port &&
                    279:          s1->in.sin_addr.s_addr == s2->in.sin_addr.s_addr)
                    280:        return 1;
                    281: #ifdef HAVE_IPV6      
                    282:       if (s1->sa.sa_family == AF_INET6 &&
                    283:          s1->in6.sin6_port == s2->in6.sin6_port &&
1.1.1.3 ! misho     284:          s1->in6.sin6_scope_id == s2->in6.sin6_scope_id &&
1.1       misho     285:          IN6_ARE_ADDR_EQUAL(&s1->in6.sin6_addr, &s2->in6.sin6_addr))
                    286:        return 1;
                    287: #endif
                    288:     }
                    289:   return 0;
                    290: }
                    291: 
                    292: int sa_len(union mysockaddr *addr)
                    293: {
                    294: #ifdef HAVE_SOCKADDR_SA_LEN
                    295:   return addr->sa.sa_len;
                    296: #else
                    297: #ifdef HAVE_IPV6
                    298:   if (addr->sa.sa_family == AF_INET6)
                    299:     return sizeof(addr->in6);
                    300:   else
                    301: #endif
                    302:     return sizeof(addr->in); 
                    303: #endif
                    304: }
                    305: 
                    306: /* don't use strcasecmp and friends here - they may be messed up by LOCALE */
                    307: int hostname_isequal(const char *a, const char *b)
                    308: {
                    309:   unsigned int c1, c2;
                    310:   
                    311:   do {
                    312:     c1 = (unsigned char) *a++;
                    313:     c2 = (unsigned char) *b++;
                    314:     
                    315:     if (c1 >= 'A' && c1 <= 'Z')
                    316:       c1 += 'a' - 'A';
                    317:     if (c2 >= 'A' && c2 <= 'Z')
                    318:       c2 += 'a' - 'A';
                    319:     
                    320:     if (c1 != c2)
                    321:       return 0;
                    322:   } while (c1);
                    323:   
                    324:   return 1;
                    325: }
                    326:     
                    327: time_t dnsmasq_time(void)
                    328: {
                    329: #ifdef HAVE_BROKEN_RTC
                    330:   struct tms dummy;
                    331:   static long tps = 0;
                    332: 
                    333:   if (tps == 0)
                    334:     tps = sysconf(_SC_CLK_TCK);
                    335: 
                    336:   return (time_t)(times(&dummy)/tps);
                    337: #else
                    338:   return time(NULL);
                    339: #endif
                    340: }
                    341: 
1.1.1.3 ! misho     342: int netmask_length(struct in_addr mask)
        !           343: {
        !           344:   int zero_count = 0;
        !           345: 
        !           346:   while (0x0 == (mask.s_addr & 0x1) && zero_count < 32) 
        !           347:     {
        !           348:       mask.s_addr >>= 1;
        !           349:       zero_count++;
        !           350:     }
        !           351:   
        !           352:   return 32 - zero_count;
        !           353: }
        !           354: 
1.1       misho     355: int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
                    356: {
                    357:   return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
                    358: } 
                    359: 
                    360: #ifdef HAVE_IPV6
                    361: int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
                    362: {
                    363:   int pfbytes = prefixlen >> 3;
                    364:   int pfbits = prefixlen & 7;
                    365: 
                    366:   if (memcmp(&a->s6_addr, &b->s6_addr, pfbytes) != 0)
                    367:     return 0;
                    368: 
                    369:   if (pfbits == 0 ||
                    370:       (a->s6_addr[pfbytes] >> (8 - pfbits) == b->s6_addr[pfbytes] >> (8 - pfbits)))
                    371:     return 1;
                    372: 
                    373:   return 0;
                    374: }
                    375: 
                    376: /* return least signigicant 64 bits if IPv6 address */
                    377: u64 addr6part(struct in6_addr *addr)
                    378: {
                    379:   int i;
                    380:   u64 ret = 0;
                    381: 
                    382:   for (i = 8; i < 16; i++)
                    383:     ret = (ret << 8) + addr->s6_addr[i];
                    384: 
                    385:   return ret;
                    386: }
                    387: 
                    388: void setaddr6part(struct in6_addr *addr, u64 host)
                    389: {
                    390:   int i;
                    391: 
                    392:   for (i = 15; i >= 8; i--)
                    393:     {
                    394:       addr->s6_addr[i] = host;
                    395:       host = host >> 8;
                    396:     }
                    397: }
                    398: 
                    399: #endif
                    400:  
                    401: 
                    402: /* returns port number from address */
                    403: int prettyprint_addr(union mysockaddr *addr, char *buf)
                    404: {
                    405:   int port = 0;
                    406:   
                    407: #ifdef HAVE_IPV6
                    408:   if (addr->sa.sa_family == AF_INET)
                    409:     {
                    410:       inet_ntop(AF_INET, &addr->in.sin_addr, buf, ADDRSTRLEN);
                    411:       port = ntohs(addr->in.sin_port);
                    412:     }
                    413:   else if (addr->sa.sa_family == AF_INET6)
                    414:     {
                    415:       char name[IF_NAMESIZE];
                    416:       inet_ntop(AF_INET6, &addr->in6.sin6_addr, buf, ADDRSTRLEN);
                    417:       if (addr->in6.sin6_scope_id != 0 &&
                    418:          if_indextoname(addr->in6.sin6_scope_id, name) &&
                    419:          strlen(buf) + strlen(name) + 2 <= ADDRSTRLEN)
                    420:        {
                    421:          strcat(buf, "%");
                    422:          strcat(buf, name);
                    423:        }
                    424:       port = ntohs(addr->in6.sin6_port);
                    425:     }
                    426: #else
                    427:   strcpy(buf, inet_ntoa(addr->in.sin_addr));
                    428:   port = ntohs(addr->in.sin_port); 
                    429: #endif
                    430:   
                    431:   return port;
                    432: }
                    433: 
                    434: void prettyprint_time(char *buf, unsigned int t)
                    435: {
                    436:   if (t == 0xffffffff)
                    437:     sprintf(buf, _("infinite"));
                    438:   else
                    439:     {
                    440:       unsigned int x, p = 0;
                    441:        if ((x = t/86400))
                    442:        p += sprintf(&buf[p], "%dd", x);
                    443:        if ((x = (t/3600)%24))
                    444:        p += sprintf(&buf[p], "%dh", x);
                    445:       if ((x = (t/60)%60))
                    446:        p += sprintf(&buf[p], "%dm", x);
                    447:       if ((x = t%60))
                    448:        p += sprintf(&buf[p], "%ds", x);
                    449:     }
                    450: }
                    451: 
                    452: 
                    453: /* in may equal out, when maxlen may be -1 (No max len). 
                    454:    Return -1 for extraneous no-hex chars found. */
                    455: int parse_hex(char *in, unsigned char *out, int maxlen, 
                    456:              unsigned int *wildcard_mask, int *mac_type)
                    457: {
                    458:   int mask = 0, i = 0;
                    459:   char *r;
                    460:     
                    461:   if (mac_type)
                    462:     *mac_type = 0;
                    463:   
                    464:   while (maxlen == -1 || i < maxlen)
                    465:     {
                    466:       for (r = in; *r != 0 && *r != ':' && *r != '-' && *r != ' '; r++)
                    467:        if (*r != '*' && !isxdigit((unsigned char)*r))
                    468:          return -1;
                    469:       
                    470:       if (*r == 0)
                    471:        maxlen = i;
                    472:       
                    473:       if (r != in )
                    474:        {
                    475:          if (*r == '-' && i == 0 && mac_type)
                    476:           {
                    477:              *r = 0;
                    478:              *mac_type = strtol(in, NULL, 16);
                    479:              mac_type = NULL;
                    480:           }
                    481:          else
                    482:            {
                    483:              *r = 0;
                    484:              if (strcmp(in, "*") == 0)
                    485:                {
                    486:                  mask = (mask << 1) | 1;
                    487:                  i++;
                    488:                }
                    489:              else
                    490:                {
                    491:                  int j, bytes = (1 + (r - in))/2;
                    492:                  for (j = 0; j < bytes; j++)
                    493:                    { 
1.1.1.2   misho     494:                      char sav = sav;
1.1       misho     495:                      if (j < bytes - 1)
                    496:                        {
                    497:                          sav = in[(j+1)*2];
                    498:                          in[(j+1)*2] = 0;
                    499:                        }
                    500:                      out[i] = strtol(&in[j*2], NULL, 16);
                    501:                      mask = mask << 1;
                    502:                      i++;
                    503:                      if (j < bytes - 1)
                    504:                        in[(j+1)*2] = sav;
                    505:                    }
                    506:                }
                    507:            }
                    508:        }
                    509:       in = r+1;
                    510:     }
                    511:   
                    512:   if (wildcard_mask)
                    513:     *wildcard_mask = mask;
                    514: 
                    515:   return i;
                    516: }
                    517: 
                    518: /* return 0 for no match, or (no matched octets) + 1 */
                    519: int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask)
                    520: {
                    521:   int i, count;
                    522:   for (count = 1, i = len - 1; i >= 0; i--, mask = mask >> 1)
                    523:     if (!(mask & 1))
                    524:       {
                    525:        if (a[i] == b[i])
                    526:          count++;
                    527:        else
                    528:          return 0;
                    529:       }
                    530:   return count;
                    531: }
                    532: 
                    533: /* _note_ may copy buffer */
                    534: int expand_buf(struct iovec *iov, size_t size)
                    535: {
                    536:   void *new;
                    537: 
                    538:   if (size <= (size_t)iov->iov_len)
                    539:     return 1;
                    540: 
                    541:   if (!(new = whine_malloc(size)))
                    542:     {
                    543:       errno = ENOMEM;
                    544:       return 0;
                    545:     }
                    546: 
                    547:   if (iov->iov_base)
                    548:     {
                    549:       memcpy(new, iov->iov_base, iov->iov_len);
                    550:       free(iov->iov_base);
                    551:     }
                    552: 
                    553:   iov->iov_base = new;
                    554:   iov->iov_len = size;
                    555: 
                    556:   return 1;
                    557: }
                    558: 
                    559: char *print_mac(char *buff, unsigned char *mac, int len)
                    560: {
                    561:   char *p = buff;
                    562:   int i;
                    563:    
                    564:   if (len == 0)
                    565:     sprintf(p, "<null>");
                    566:   else
                    567:     for (i = 0; i < len; i++)
                    568:       p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
                    569:   
                    570:   return buff;
                    571: }
                    572: 
1.1.1.3 ! misho     573: /* rc is return from sendto and friends.
        !           574:    Return 1 if we should retry.
        !           575:    Set errno to zero if we succeeded. */
        !           576: int retry_send(ssize_t rc)
1.1       misho     577: {
1.1.1.3 ! misho     578:   static int retries = 0;
        !           579:   struct timespec waiter;
        !           580:   
        !           581:   if (rc != -1)
        !           582:     {
        !           583:       retries = 0;
        !           584:       errno = 0;
        !           585:       return 0;
        !           586:     }
        !           587:   
        !           588:   /* Linux kernels can return EAGAIN in perpetuity when calling
        !           589:      sendmsg() and the relevant interface has gone. Here we loop
        !           590:      retrying in EAGAIN for 1 second max, to avoid this hanging 
        !           591:      dnsmasq. */
1.1       misho     592: 
1.1.1.3 ! misho     593:   if (errno == EAGAIN || errno == EWOULDBLOCK)
1.1       misho     594:      {
                    595:        waiter.tv_sec = 0;
                    596:        waiter.tv_nsec = 10000;
                    597:        nanosleep(&waiter, NULL);
1.1.1.3 ! misho     598:        if (retries++ < 1000)
        !           599:         return 1;
1.1       misho     600:      }
1.1.1.3 ! misho     601:   
        !           602:   retries = 0;
        !           603:   
        !           604:   if (errno == EINTR)
        !           605:     return 1;
        !           606:   
        !           607:   return 0;
1.1       misho     608: }
                    609: 
                    610: int read_write(int fd, unsigned char *packet, int size, int rw)
                    611: {
                    612:   ssize_t n, done;
                    613:   
                    614:   for (done = 0; done < size; done += n)
                    615:     {
1.1.1.3 ! misho     616:       do { 
        !           617:        if (rw)
        !           618:          n = read(fd, &packet[done], (size_t)(size - done));
        !           619:        else
        !           620:          n = write(fd, &packet[done], (size_t)(size - done));
        !           621:        
        !           622:        if (n == 0)
        !           623:          return 0;
        !           624:        
        !           625:       } while (retry_send(n) || errno == ENOMEM || errno == ENOBUFS);
        !           626: 
        !           627:       if (errno != 0)
        !           628:        return 0;
1.1       misho     629:     }
1.1.1.3 ! misho     630:      
1.1       misho     631:   return 1;
                    632: }
                    633: 
                    634: /* Basically match a string value against a wildcard pattern.  */
                    635: int wildcard_match(const char* wildcard, const char* match)
                    636: {
                    637:   while (*wildcard && *match)
                    638:     {
                    639:       if (*wildcard == '*')
                    640:         return 1;
                    641: 
                    642:       if (*wildcard != *match)
                    643:         return 0; 
                    644: 
                    645:       ++wildcard;
                    646:       ++match;
                    647:     }
                    648: 
                    649:   return *wildcard == *match;
1.1.1.3 ! misho     650: }
        !           651: 
        !           652: /* The same but comparing a maximum of NUM characters, like strncmp.  */
        !           653: int wildcard_matchn(const char* wildcard, const char* match, int num)
        !           654: {
        !           655:   while (*wildcard && *match && num)
        !           656:     {
        !           657:       if (*wildcard == '*')
        !           658:         return 1;
        !           659: 
        !           660:       if (*wildcard != *match)
        !           661:         return 0; 
        !           662: 
        !           663:       ++wildcard;
        !           664:       ++match;
        !           665:       --num;
        !           666:     }
        !           667: 
        !           668:   return (!num) || (*wildcard == *match);
1.1       misho     669: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>