home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / kernex32.zip / mwdd_src.zip / 32bits / ext2-os2 / util / fnmatch.c < prev    next >
C/C++ Source or Header  |  1997-03-16  |  17KB  |  586 lines

  1. //
  2. // $Header: d:\\32bits\\ext2-os2\\util\\rcs\\fnmatch.c,v 1.2 1997/03/16 13:21:33 Willm Exp $
  3. //
  4.  
  5. // 32 bits OS/2 device driver and IFS support. Provides 32 bits kernel 
  6. // services (DevHelp) and utility functions to 32 bits OS/2 ring 0 code 
  7. // (device drivers and installable file system drivers).
  8. // Copyright (C) 1995, 1996, 1997  Matthieu WILLM (willm@ibm.net)
  9. //
  10. // This program is free software; you can redistribute it and/or modify
  11. // it under the terms of the GNU General Public License as published by
  12. // the Free Software Foundation; either version 2 of the License, or
  13. // (at your option) any later version.
  14. //
  15. // This program is distributed in the hope that it will be useful,
  16. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18. // GNU General Public License for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License
  21. // along with this program; if not, write to the Free Software
  22. // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  23.  
  24. #ifdef __IBMC__
  25. #pragma strings(readonly)
  26. #endif
  27.  
  28. /* From:                                                             */
  29. /* fnmatch.c (emx+gcc) -- Copyright (c) 1994-1995 by Eberhard Mattes */
  30.  
  31. #ifdef MWDD32
  32. #include <os2/types.h>
  33. #include <os2/ctype.h>
  34. #endif
  35. // #include <sys/emx.h>            /* _nls_init_flag */
  36. #include <string.h>
  37. // #include <ctype.h>
  38. #include <os2/fnmatch.h>
  39. // #include <sys/nls.h>
  40.  
  41. #ifdef MWDD32
  42. INLINE int _nls_tolower (int c)
  43. {
  44.     if (c >= 'A' && c <= 'Z')
  45.         return c + 'a' - 'A';
  46.     else
  47.         return c;
  48. }
  49. #endif
  50.  
  51. /* In OS/2 and DOS styles, both / and \ separate components of a path.
  52.    This macro returns true iff C is a separator. */
  53.  
  54. #define IS_OS2_COMP_SEP(C)  ((C) == '/' || (C) == '\\')
  55.  
  56.  
  57. /* This macro returns true if C is at the end of a component of a
  58.    path. */
  59.  
  60. #define IS_OS2_COMP_END(C)  ((C) == 0 || IS_OS2_COMP_SEP (C))
  61.  
  62.  
  63. /* Return a pointer to the next component of the path SRC, for OS/2
  64.    and DOS styles.  When the end of the string is reached, a pointer
  65.    to the terminating null character is returned. */
  66.  
  67. static const unsigned char *skip_comp_os2 (const unsigned char *src)
  68. {
  69.   /* Skip characters until hitting a separator or the end of the
  70.      string. */
  71.  
  72.   while (!IS_OS2_COMP_END (*src))
  73.     ++src;
  74.  
  75.   /* Skip the separator if we hit a separator. */
  76.  
  77.   if (*src != 0)
  78.     ++src;
  79.   return src;
  80. }
  81.  
  82.  
  83. /* Compare a single component (directory name or file name) of the
  84.    paths, for OS/2 and DOS styles.  MASK and NAME point into a
  85.    component of the wildcard and the name to be checked, respectively.
  86.    Comparing stops at the next separator.  The FLAGS argument is the
  87.    same as that of fnmatch().  HAS_DOT is true if a dot is in the
  88.    current component of NAME.  The number of dots is not restricted,
  89.    even in DOS style.  Return _FNM_MATCH iff MASK and NAME match.
  90.    Note that this function is recursive. */
  91.  
  92. static int match_comp_os2 (const unsigned char *mask,
  93.                            const unsigned char *name,
  94.                            unsigned flags, int has_dot)
  95. {
  96.   int rc;
  97.  
  98.   for (;;)
  99.     switch (*mask)
  100.       {
  101.       case 0:
  102.  
  103.         /* There must be no extra characters at the end of NAME when
  104.            reaching the end of MASK unless _FNM_PATHPREFIX is set: in
  105.            that case, NAME may point to a separator. */
  106.  
  107.         if (*name == 0)
  108.           return _FNM_MATCH;
  109.         if ((flags & _FNM_PATHPREFIX) && IS_OS2_COMP_SEP (*name))
  110.           return _FNM_MATCH;
  111.         return FNM_NOMATCH;
  112.  
  113.       case '/':
  114.       case '\\':
  115.  
  116.         /* Separators match separators. */
  117.  
  118.         if (IS_OS2_COMP_SEP (*name))
  119.           return _FNM_MATCH;
  120.  
  121.         /* If _FNM_PATHPREFIX is set, a trailing separator in MASK
  122.            is ignored at the end of NAME. */
  123.  
  124.         if ((flags & _FNM_PATHPREFIX) && mask[1] == 0 && *name == 0)
  125.           return _FNM_MATCH;
  126.  
  127.         /* Stop comparing at the separator. */
  128.  
  129.         return FNM_NOMATCH;
  130.  
  131.       case '?':
  132.  
  133.         /* A question mark matches one character.  It does not match a
  134.            dot.  At the end of the component (and before a dot), it
  135.            also matches zero characters. */
  136.  
  137.         if (*name != '.' && !IS_OS2_COMP_END (*name))
  138.           ++name;
  139.         ++mask;
  140.         break;
  141.  
  142.       case '*':
  143.  
  144.         /* An asterisk matches zero or more characters.  In DOS mode,
  145.            dots are not matched. */
  146.  
  147.         do
  148.           {
  149.             ++mask;
  150.           } while (*mask == '*');
  151.         for (;;)
  152.           {
  153.             rc = match_comp_os2 (mask, name, flags, has_dot);
  154.             if (rc != FNM_NOMATCH)
  155.               return rc;
  156.             if (IS_OS2_COMP_END (*name))
  157.               return FNM_NOMATCH;
  158.             if (*name == '.' && (flags & _FNM_STYLE_MASK) == _FNM_DOS)
  159.               return FNM_NOMATCH;
  160.             ++name;
  161.           }
  162.  
  163.       case '.':
  164.  
  165.         /* A dot matches a dot.  It also matches the implicit dot at
  166.            the end of a dot-less NAME. */
  167.  
  168.         ++mask;
  169.         if (*name == '.')
  170.           ++name;
  171.         else if (has_dot || !IS_OS2_COMP_END (*name))
  172.           return FNM_NOMATCH;
  173.         break;
  174.  
  175.       default:
  176.  
  177.         /* All other characters match themselves. */
  178.  
  179.         if (flags & _FNM_IGNORECASE)
  180.           {
  181.             if (_nls_tolower (*mask) != _nls_tolower (*name))
  182.               return FNM_NOMATCH;
  183.           }
  184.         else
  185.           {
  186.             if (*mask != *name)
  187.               return FNM_NOMATCH;
  188.           }
  189.         ++mask; ++name;
  190.         break;
  191.       }
  192. }
  193.  
  194.  
  195. /* Compare a single component (directory name or file name) of the
  196.    paths, for all styles which need component-by-component matching.
  197.    MASK and NAME point to the start of a component of the wildcard and
  198.    the name to be checked, respectively.  Comparing stops at the next
  199.    separator.  The FLAGS argument is the same as that of fnmatch().
  200.    Return _FNM_MATCH iff MASK and NAME match. */
  201.  
  202. static int match_comp (const unsigned char *mask, const unsigned char *name,
  203.                        unsigned flags)
  204. {
  205.   const unsigned char *s;
  206.  
  207.   switch (flags & _FNM_STYLE_MASK)
  208.     {
  209.     case _FNM_OS2:
  210.     case _FNM_DOS:
  211.  
  212.       /* For OS/2 and DOS styles, we add an implicit dot at the end of
  213.          the component if the component doesn't include a dot. */
  214.  
  215.       s = name;
  216.       while (!IS_OS2_COMP_END (*s) && *s != '.')
  217.         ++s;
  218.       return match_comp_os2 (mask, name, flags, *s == '.');
  219.  
  220.     default:
  221.       return _FNM_ERR;
  222.     }
  223. }
  224.  
  225.  
  226.  
  227. /* In Unix styles, / separates components of a path.  This macro
  228.    returns true iff C is a separator. */
  229.  
  230. #define IS_UNIX_COMP_SEP(C)  ((C) == '/')
  231.  
  232.  
  233. /* This macro returns true if C is at the end of a component of a
  234.    path. */
  235.  
  236. #define IS_UNIX_COMP_END(C)  ((C) == 0 || IS_UNIX_COMP_SEP (C))
  237.  
  238.  
  239. /* Match complete paths for Unix styles.  The FLAGS argument is the
  240.    same as that of fnmatch().  COMP points to the start of the current
  241.    component in NAME.  Return _FNM_MATCH iff MASK and NAME match.  The
  242.    backslash character is used for escaping ? and * unless
  243.    FNM_NOESCAPE is set. */
  244.  
  245. static int match_unix (const unsigned char *mask, const unsigned char *name,
  246.                        unsigned flags, const unsigned char *comp)
  247. {
  248.   unsigned char c1, c2;
  249.   char invert, matched;
  250.   const unsigned char *start;
  251.   int rc;
  252.  
  253.   for (;;)
  254.     switch (*mask)
  255.       {
  256.       case 0:
  257.  
  258.         /* There must be no extra characters at the end of NAME when
  259.            reaching the end of MASK unless _FNM_PATHPREFIX is set: in
  260.            that case, NAME may point to a separator. */
  261.  
  262.         if (*name == 0)
  263.           return _FNM_MATCH;
  264.         if ((flags & _FNM_PATHPREFIX) && IS_UNIX_COMP_SEP (*name))
  265.           return _FNM_MATCH;
  266.         return FNM_NOMATCH;
  267.  
  268.       case '?':
  269.  
  270.         /* A question mark matches one character.  It does not match
  271.            the component separator if FNM_PATHNAME is set.  It does
  272.            not match a dot at the start of a component if FNM_PERIOD
  273.            is set. */
  274.  
  275.         if (*name == 0)
  276.           return FNM_NOMATCH;
  277.         if ((flags & FNM_PATHNAME) && IS_UNIX_COMP_SEP (*name))
  278.           return FNM_NOMATCH;
  279.         if (*name == '.' && (flags & FNM_PERIOD) && name == comp)
  280.           return FNM_NOMATCH;
  281.         ++name; ++mask;
  282.         break;
  283.  
  284.       case '*':
  285.  
  286.         /* An asterisk matches zero or more characters.  It does not
  287.            match the component separator if FNM_PATHNAME is set.  It
  288.            does not match a dot at the start of a component if
  289.            FNM_PERIOD is set. */
  290.  
  291.         if (*name == '.' && (flags & FNM_PERIOD) && name == comp)
  292.           return FNM_NOMATCH;
  293.         do
  294.           {
  295.             ++mask;
  296.           } while (*mask == '*');
  297.         for (;;)
  298.           {
  299.             rc = match_unix (mask, name, flags, comp);
  300.             if (rc != FNM_NOMATCH)
  301.               return rc;
  302.             if (*name == 0)
  303.               return FNM_NOMATCH;
  304.             if ((flags & FNM_PATHNAME) && IS_UNIX_COMP_SEP (*name))
  305.               return FNM_NOMATCH;
  306.             ++name;
  307.           }
  308.  
  309.       case '/':
  310.  
  311.         /* Separators match only separators.  If _FNM_PATHPREFIX is set, a
  312.            trailing separator in MASK is ignored at the end of
  313.            NAME. */
  314.  
  315.         if (!(IS_UNIX_COMP_SEP (*name)
  316.               || ((flags & _FNM_PATHPREFIX) && *name == 0
  317.                   && (mask[1] == 0
  318.                       || (!(flags & FNM_NOESCAPE) && mask[1] == '\\'
  319.                           && mask[2] == 0)))))
  320.           return FNM_NOMATCH;
  321.  
  322.         ++mask;
  323.         if (*name != 0) ++name;
  324.  
  325.         /* This is the beginning of a new component if FNM_PATHNAME is
  326.            set. */
  327.  
  328.         if (flags & FNM_PATHNAME)
  329.           comp = name;
  330.         break;
  331.  
  332.       case '[':
  333.  
  334.         /* A set of characters.  Always case-sensitive. */
  335.  
  336.         if (*name == 0)
  337.           return FNM_NOMATCH;
  338.         if ((flags & FNM_PATHNAME) && IS_UNIX_COMP_SEP (*name))
  339.           return FNM_NOMATCH;
  340.         if (*name == '.' && (flags & FNM_PERIOD) && name == comp)
  341.           return FNM_NOMATCH;
  342.  
  343.         invert = 0; matched = 0;
  344.         ++mask;
  345.  
  346.         /* If the first character is a ! or ^, the set matches all
  347.            characters not listed in the set. */
  348.  
  349.         if (*mask == '!' || *mask == '^')
  350.           {
  351.             ++mask; invert = 1;
  352.           }
  353.  
  354.         /* Loop over all the characters of the set.  The loop ends if
  355.            the end of the string is reached or if a ] is encountered
  356.            unless it directly follows the initial [ or [-. */
  357.  
  358.         start = mask;
  359.         while (!(*mask == 0 || (*mask == ']' && mask != start)))
  360.           {
  361.             /* Get the next character which is optionally preceded by
  362.                a backslash. */
  363.  
  364.             c1 = *mask++;
  365.             if (!(flags & FNM_NOESCAPE) && c1 == '\\')
  366.               {
  367.                 if (*mask == 0)
  368.                   break;
  369.                 c1 = *mask++;
  370.               }
  371.  
  372.             /* Ranges of characters are written as a-z.  Don't forget
  373.                to check for the end of the string and to handle the
  374.                backslash.  If the character after - is a ], it isn't a
  375.                range. */
  376.  
  377.             if (*mask == '-' && mask[1] != ']')
  378.               {
  379.                 ++mask;         /* Skip the - character */
  380.                 if (!(flags & FNM_NOESCAPE) && *mask == '\\')
  381.                   ++mask;
  382.                 if (*mask == 0)
  383.                   break;
  384.                 c2 = *mask++;
  385.               }
  386.             else
  387.               c2 = c1;
  388.  
  389.             /* Now check whether this character or range matches NAME. */
  390.  
  391.             if (c1 <= *name && *name <= c2)
  392.               {
  393.                 if (!invert)
  394.                   matched = 1;
  395.               }
  396.             else
  397.               {
  398.                 if (invert)
  399.                   matched = 1;
  400.               }
  401.           }
  402.  
  403.         /* If the end of the string is reached before a ] is found,
  404.            back up to the [ and compare it to NAME. */
  405.  
  406.         if (*mask == 0)
  407.           {
  408.             if (*name != '[')
  409.               return FNM_NOMATCH;
  410.             ++name;
  411.             mask = start;
  412.             if (invert)
  413.               --mask;
  414.           }
  415.         else
  416.           {
  417.             if (!matched)
  418.               return FNM_NOMATCH;
  419.             ++mask;             /* Skip the ] character */
  420.             if (*name != 0) ++name;
  421.           }
  422.         break;
  423.  
  424.       case '\\':
  425.         ++mask;
  426.         if (flags & FNM_NOESCAPE)
  427.           {
  428.             if (*name != '\\')
  429.               return FNM_NOMATCH;
  430.             ++name;
  431.           }
  432.         else if (*mask == '*' || *mask == '?')
  433.           {
  434.             if (*mask != *name)
  435.               return FNM_NOMATCH;
  436.             ++mask; ++name;
  437.           }
  438.         break;
  439.  
  440.       default:
  441.  
  442.         /* All other characters match themselves. */
  443.  
  444.         if (flags & _FNM_IGNORECASE)
  445.           {
  446.             if (_nls_tolower (*mask) != _nls_tolower (*name))
  447.               return FNM_NOMATCH;
  448.           }
  449.         else
  450.           {
  451.             if (*mask != *name)
  452.               return FNM_NOMATCH;
  453.           }
  454.         ++mask; ++name;
  455.         break;
  456.       }
  457. }
  458.  
  459.  
  460. /* Check whether the path name NAME matches the wildcard MASK.  Return
  461.    0 (_FNM_MATCH) if it matches, _FNM_NOMATCH if it doesn't.  Return
  462.    _FNM_ERR on error. The operation of this function is controlled by
  463.    FLAGS.  This is an internal function, with unsigned arguments. */
  464.  
  465.  
  466. static int _fnmatch_unsigned (const unsigned char *mask,
  467.                               const unsigned char *name,
  468.                               unsigned flags)
  469. {
  470.   int m_drive, n_drive, rc;
  471.  
  472.   /* Call _nls_init() if not yet called. */
  473. #ifndef MWDD32
  474.   if (!_nls_init_flag)
  475.     _nls_init();
  476. #endif
  477.   /* Match and skip the drive name if present. */
  478.  
  479.   m_drive = ((isalpha (mask[0]) && mask[1] == ':') ? mask[0] : -1);
  480.   n_drive = ((isalpha (name[0]) && name[1] == ':') ? name[0] : -1);
  481.  
  482.   if (m_drive != n_drive)
  483.     {
  484.       if (m_drive == -1 || n_drive == -1)
  485.         return FNM_NOMATCH;
  486.       if (!(flags & _FNM_IGNORECASE))
  487.         return FNM_NOMATCH;
  488.       if (_nls_tolower (m_drive) != _nls_tolower (n_drive))
  489.         return FNM_NOMATCH;
  490.     }
  491.  
  492.   if (m_drive != -1) mask += 2;
  493.   if (n_drive != -1) name += 2;
  494.  
  495.   /* Colons are not allowed in path names, except for the drive name,
  496.      which was skipped above. */
  497.  
  498.   if (strchr (mask, ':') != NULL)
  499.     return _FNM_ERR;
  500.   if (strchr (name, ':') != NULL)
  501.     return FNM_NOMATCH;
  502.  
  503.   /* The name "\\server\path" should not be matched by mask
  504.      "\*\server\path".  Ditto for /. */
  505.  
  506.   switch (flags & _FNM_STYLE_MASK)
  507.     {
  508.     case _FNM_OS2:
  509.     case _FNM_DOS:
  510.  
  511.       if (IS_OS2_COMP_SEP (name[0]) && IS_OS2_COMP_SEP (name[1]))
  512.         {
  513.           if (!(IS_OS2_COMP_SEP (mask[0]) && IS_OS2_COMP_SEP (mask[1])))
  514.             return FNM_NOMATCH;
  515.           name += 2;
  516.           mask += 2;
  517.         }
  518.       break;
  519.  
  520.     case _FNM_POSIX:
  521.  
  522.       if (name[0] == '/' && name[1] == '/')
  523.         {
  524.           int i;
  525.  
  526.           name += 2;
  527.           for (i = 0; i < 2; ++i)
  528.             if (mask[0] == '/')
  529.               ++mask;
  530.             else if (mask[0] == '\\' && mask[1] == '/')
  531.               mask += 2;
  532.             else
  533.               return FNM_NOMATCH;
  534.         }
  535.  
  536.       /* In Unix styles, treating ? and * w.r.t. components is simple.
  537.          No need to do matching component by component. */
  538.  
  539.       return match_unix (mask, name, flags, name);
  540.     }
  541.  
  542.   /* Now compare all the components of the path name, one by one.
  543.      Note that the path separator must not be enclosed in brackets. */
  544.  
  545.   while (*mask != 0 || *name != 0)
  546.     {
  547.  
  548.       /* If _FNM_PATHPREFIX is set, the names match if the end of MASK
  549.          is reached even if there are components left in NAME. */
  550.  
  551.       if (*mask == 0 && (flags & _FNM_PATHPREFIX))
  552.         return _FNM_MATCH;
  553.  
  554.       /* Compare a single component of the path name. */
  555.  
  556.       rc = match_comp (mask, name, flags);
  557.       if (rc != _FNM_MATCH)
  558.         return rc;
  559.  
  560.       /* Skip to the next component or to the end of the path name. */
  561.  
  562.       mask = skip_comp_os2 (mask);
  563.       name = skip_comp_os2 (name);
  564.     }
  565.  
  566.   /* If we reached the ends of both strings, the names match. */
  567.  
  568.   if (*mask == 0 && *name == 0)
  569.     return _FNM_MATCH;
  570.  
  571.   /* The names do not match. */
  572.  
  573.   return FNM_NOMATCH;
  574. }
  575.  
  576.  
  577. /* This is the public entrypoint, with signed arguments. */
  578. #ifndef MWDD32
  579. int _fnmatch (const char *mask, const char *name, int flags)
  580. #else
  581. int DH32ENTRY __mwdd32_fnmatch (const char *mask, const char *name, int flags)
  582. #endif
  583. {
  584.   return _fnmatch_unsigned (mask, name, flags);
  585. }
  586.