Changeset 127 for trunk/src/helpers/stringh.c
- Timestamp:
- Jan 5, 2002, 8:11:10 PM (24 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/helpers/stringh.c
r123 r127 152 152 *ppszTarget = NULL; 153 153 } 154 else 155 return (ERROR_INVALID_PARAMETER); 154 156 155 157 if (pulLength) … … 1659 1661 /* ****************************************************************** 1660 1662 * 1661 * Wildcard matching1662 *1663 ********************************************************************/1664 1665 /*1666 * The following code has been taken from "fnmatch.zip".1667 *1668 * (c) 1994-1996 by Eberhard Mattes.1669 */1670 1671 /* In OS/2 and DOS styles, both / and \ separate components of a path.1672 * This macro returns true iff C is a separator. */1673 1674 #define IS_OS2_COMP_SEP(C) ((C) == '/' || (C) == '\\')1675 1676 1677 /* This macro returns true if C is at the end of a component of a1678 * path. */1679 1680 #define IS_OS2_COMP_END(C) ((C) == 0 || IS_OS2_COMP_SEP (C))1681 1682 /*1683 * skip_comp_os2:1684 * Return a pointer to the next component of the path SRC, for OS/21685 * and DOS styles. When the end of the string is reached, a pointer1686 * to the terminating null character is returned.1687 *1688 * (c) 1994-1996 by Eberhard Mattes.1689 */1690 1691 static const unsigned char* skip_comp_os2(const unsigned char *src)1692 {1693 /* Skip characters until hitting a separator or the end of the1694 * string. */1695 1696 while (!IS_OS2_COMP_END(*src))1697 ++src;1698 1699 /* Skip the separator if we hit a separator. */1700 1701 if (*src != 0)1702 ++src;1703 return src;1704 }1705 1706 /*1707 * has_colon:1708 * returns true iff the path P contains a colon.1709 *1710 * (c) 1994-1996 by Eberhard Mattes.1711 */1712 1713 static int has_colon(const unsigned char *p)1714 {1715 while (*p != 0)1716 if (*p == ':')1717 return 1;1718 else1719 ++p;1720 return 0;1721 }1722 1723 /*1724 * match_comp_os2:1725 * compares a single component (directory name or file name)1726 * of the paths, for OS/2 and DOS styles. MASK and NAME point1727 * into a component of the wildcard and the name to be checked,1728 * respectively. Comparing stops at the next separator.1729 * The FLAGS argument is the same as that of fnmatch().1730 *1731 * HAS_DOT is true if a dot is in the current component of NAME.1732 * The number of dots is not restricted, even in DOS style.1733 *1734 * Returns FNM_MATCH iff MASK and NAME match.1735 *1736 * Note that this function is recursive.1737 *1738 * (c) 1994-1996 by Eberhard Mattes.1739 */1740 1741 static int match_comp_os2(const unsigned char *mask,1742 const unsigned char *name,1743 unsigned flags,1744 int has_dot)1745 {1746 int rc;1747 1748 for (;;)1749 switch (*mask)1750 {1751 case 0:1752 1753 /* There must be no extra characters at the end of NAME when1754 * reaching the end of MASK unless _FNM_PATHPREFIX is set:1755 * in that case, NAME may point to a separator. */1756 1757 if (*name == 0)1758 return FNM_MATCH;1759 if ((flags & FNM_PATHPREFIX) && IS_OS2_COMP_SEP(*name))1760 return FNM_MATCH;1761 return FNM_NOMATCH;1762 1763 case '/':1764 case '\\':1765 1766 /* Separators match separators. */1767 1768 if (IS_OS2_COMP_SEP(*name))1769 return FNM_MATCH;1770 1771 /* If _FNM_PATHPREFIX is set, a trailing separator in MASK1772 * is ignored at the end of NAME. */1773 1774 if ((flags & FNM_PATHPREFIX) && mask[1] == 0 && *name == 0)1775 return FNM_MATCH;1776 1777 /* Stop comparing at the separator. */1778 1779 return FNM_NOMATCH;1780 1781 case '?':1782 1783 /* A question mark matches one character. It does not match1784 * a dot. At the end of the component (and before a dot),1785 * it also matches zero characters. */1786 1787 if (*name != '.' && !IS_OS2_COMP_END(*name))1788 ++name;1789 ++mask;1790 break;1791 1792 case '*':1793 1794 /* An asterisk matches zero or more characters. In DOS1795 * mode, dots are not matched. */1796 1797 do1798 {1799 ++mask;1800 }1801 while (*mask == '*');1802 for (;;)1803 {1804 rc = match_comp_os2(mask, name, flags, has_dot);1805 if (rc != FNM_NOMATCH)1806 return rc;1807 if (IS_OS2_COMP_END(*name))1808 return FNM_NOMATCH;1809 if (*name == '.' && (flags & FNM_STYLE_MASK) == FNM_DOS)1810 return FNM_NOMATCH;1811 ++name;1812 }1813 1814 case '.':1815 1816 /* A dot matches a dot. It also matches the implicit dot at1817 * the end of a dot-less NAME. */1818 1819 ++mask;1820 if (*name == '.')1821 ++name;1822 else if (has_dot || !IS_OS2_COMP_END(*name))1823 return FNM_NOMATCH;1824 break;1825 1826 default:1827 1828 /* All other characters match themselves. */1829 1830 if (flags & FNM_IGNORECASE)1831 {1832 if (tolower(*mask) != tolower(*name))1833 return FNM_NOMATCH;1834 }1835 else1836 {1837 if (*mask != *name)1838 return FNM_NOMATCH;1839 }1840 ++mask;1841 ++name;1842 break;1843 }1844 }1845 1846 /*1847 * match_comp:1848 * compares a single component (directory name or file1849 * name) of the paths, for all styles which need1850 * component-by-component matching. MASK and NAME point1851 * to the start of a component of the wildcard and the1852 * name to be checked, respectively. Comparing stops at1853 * the next separator. The FLAGS argument is the same as1854 * that of fnmatch().1855 *1856 * Return FNM_MATCH iff MASK and NAME match.1857 *1858 * (c) 1994-1996 by Eberhard Mattes.1859 */1860 1861 static int match_comp(const unsigned char *mask,1862 const unsigned char *name,1863 unsigned flags)1864 {1865 const unsigned char *s;1866 1867 switch (flags & FNM_STYLE_MASK)1868 {1869 case FNM_OS2:1870 case FNM_DOS:1871 1872 /* For OS/2 and DOS styles, we add an implicit dot at the end of1873 * the component if the component doesn't include a dot. */1874 1875 s = name;1876 while (!IS_OS2_COMP_END(*s) && *s != '.')1877 ++s;1878 return match_comp_os2(mask, name, flags, *s == '.');1879 1880 default:1881 return FNM_ERR;1882 }1883 }1884 1885 /* In Unix styles, / separates components of a path. This macro1886 * returns true iff C is a separator. */1887 1888 #define IS_UNIX_COMP_SEP(C) ((C) == '/')1889 1890 1891 /* This macro returns true if C is at the end of a component of a1892 * path. */1893 1894 #define IS_UNIX_COMP_END(C) ((C) == 0 || IS_UNIX_COMP_SEP (C))1895 1896 /*1897 * match_unix:1898 * matches complete paths for Unix styles.1899 *1900 * The FLAGS argument is the same as that of fnmatch().1901 * COMP points to the start of the current component in1902 * NAME. Return FNM_MATCH iff MASK and NAME match. The1903 * backslash character is used for escaping ? and * unless1904 * FNM_NOESCAPE is set.1905 *1906 * (c) 1994-1996 by Eberhard Mattes.1907 */1908 1909 static int match_unix(const unsigned char *mask,1910 const unsigned char *name,1911 unsigned flags,1912 const unsigned char *comp)1913 {1914 unsigned char c1, c2;1915 char invert, matched;1916 const unsigned char *start;1917 int rc;1918 1919 for (;;)1920 switch (*mask)1921 {1922 case 0:1923 1924 /* There must be no extra characters at the end of NAME when1925 * reaching the end of MASK unless _FNM_PATHPREFIX is set:1926 * in that case, NAME may point to a separator. */1927 1928 if (*name == 0)1929 return FNM_MATCH;1930 if ((flags & FNM_PATHPREFIX) && IS_UNIX_COMP_SEP(*name))1931 return FNM_MATCH;1932 return FNM_NOMATCH;1933 1934 case '?':1935 1936 /* A question mark matches one character. It does not match1937 * the component separator if FNM_PATHNAME is set. It does1938 * not match a dot at the start of a component if FNM_PERIOD1939 * is set. */1940 1941 if (*name == 0)1942 return FNM_NOMATCH;1943 if ((flags & FNM_PATHNAME) && IS_UNIX_COMP_SEP(*name))1944 return FNM_NOMATCH;1945 if (*name == '.' && (flags & FNM_PERIOD) && name == comp)1946 return FNM_NOMATCH;1947 ++mask;1948 ++name;1949 break;1950 1951 case '*':1952 1953 /* An asterisk matches zero or more characters. It does not1954 * match the component separator if FNM_PATHNAME is set. It1955 * does not match a dot at the start of a component if1956 * FNM_PERIOD is set. */1957 1958 if (*name == '.' && (flags & FNM_PERIOD) && name == comp)1959 return FNM_NOMATCH;1960 do1961 {1962 ++mask;1963 }1964 while (*mask == '*');1965 for (;;)1966 {1967 rc = match_unix(mask, name, flags, comp);1968 if (rc != FNM_NOMATCH)1969 return rc;1970 if (*name == 0)1971 return FNM_NOMATCH;1972 if ((flags & FNM_PATHNAME) && IS_UNIX_COMP_SEP(*name))1973 return FNM_NOMATCH;1974 ++name;1975 }1976 1977 case '/':1978 1979 /* Separators match only separators. If _FNM_PATHPREFIX is1980 * set, a trailing separator in MASK is ignored at the end1981 * of NAME. */1982 1983 if (!(IS_UNIX_COMP_SEP(*name)1984 || ((flags & FNM_PATHPREFIX) && *name == 01985 && (mask[1] == 01986 || (!(flags & FNM_NOESCAPE) && mask[1] == '\\'1987 && mask[2] == 0)))))1988 return FNM_NOMATCH;1989 1990 ++mask;1991 if (*name != 0)1992 ++name;1993 1994 /* This is the beginning of a new component if FNM_PATHNAME1995 * is set. */1996 1997 if (flags & FNM_PATHNAME)1998 comp = name;1999 break;2000 2001 case '[':2002 2003 /* A set of characters. Always case-sensitive. */2004 2005 if (*name == 0)2006 return FNM_NOMATCH;2007 if ((flags & FNM_PATHNAME) && IS_UNIX_COMP_SEP(*name))2008 return FNM_NOMATCH;2009 if (*name == '.' && (flags & FNM_PERIOD) && name == comp)2010 return FNM_NOMATCH;2011 2012 invert = 0;2013 matched = 0;2014 ++mask;2015 2016 /* If the first character is a ! or ^, the set matches all2017 * characters not listed in the set. */2018 2019 if (*mask == '!' || *mask == '^')2020 {2021 ++mask;2022 invert = 1;2023 }2024 2025 /* Loop over all the characters of the set. The loop ends2026 * if the end of the string is reached or if a ] is2027 * encountered unless it directly follows the initial [ or2028 * [-. */2029 2030 start = mask;2031 while (!(*mask == 0 || (*mask == ']' && mask != start)))2032 {2033 /* Get the next character which is optionally preceded2034 * by a backslash. */2035 2036 c1 = *mask++;2037 if (!(flags & FNM_NOESCAPE) && c1 == '\\')2038 {2039 if (*mask == 0)2040 break;2041 c1 = *mask++;2042 }2043 2044 /* Ranges of characters are written as a-z. Don't2045 * forget to check for the end of the string and to2046 * handle the backslash. If the character after - is a2047 * ], it isn't a range. */2048 2049 if (*mask == '-' && mask[1] != ']')2050 {2051 ++mask; /* Skip the - character */2052 if (!(flags & FNM_NOESCAPE) && *mask == '\\')2053 ++mask;2054 if (*mask == 0)2055 break;2056 c2 = *mask++;2057 }2058 else2059 c2 = c1;2060 2061 /* Now check whether this character or range matches NAME. */2062 2063 if (c1 <= *name && *name <= c2)2064 matched = 1;2065 }2066 2067 /* If the end of the string is reached before a ] is found,2068 * back up to the [ and compare it to NAME. */2069 2070 if (*mask == 0)2071 {2072 if (*name != '[')2073 return FNM_NOMATCH;2074 ++name;2075 mask = start;2076 if (invert)2077 --mask;2078 }2079 else2080 {2081 if (invert)2082 matched = !matched;2083 if (!matched)2084 return FNM_NOMATCH;2085 ++mask; /* Skip the ] character */2086 if (*name != 0)2087 ++name;2088 }2089 break;2090 2091 case '\\':2092 ++mask;2093 if (flags & FNM_NOESCAPE)2094 {2095 if (*name != '\\')2096 return FNM_NOMATCH;2097 ++name;2098 }2099 else if (*mask == '*' || *mask == '?')2100 {2101 if (*mask != *name)2102 return FNM_NOMATCH;2103 ++mask;2104 ++name;2105 }2106 break;2107 2108 default:2109 2110 /* All other characters match themselves. */2111 2112 if (flags & FNM_IGNORECASE)2113 {2114 if (tolower(*mask) != tolower(*name))2115 return FNM_NOMATCH;2116 }2117 else2118 {2119 if (*mask != *name)2120 return FNM_NOMATCH;2121 }2122 ++mask;2123 ++name;2124 break;2125 }2126 }2127 2128 /*2129 * _fnmatch_unsigned:2130 * Check whether the path name NAME matches the wildcard MASK.2131 *2132 * Return:2133 * -- 0 (FNM_MATCH) if it matches,2134 * -- _FNM_NOMATCH if it doesn't,2135 * -- FNM_ERR on error.2136 *2137 * The operation of this function is controlled by FLAGS.2138 * This is an internal function, with unsigned arguments.2139 *2140 * (c) 1994-1996 by Eberhard Mattes.2141 */2142 2143 static int _fnmatch_unsigned(const unsigned char *mask,2144 const unsigned char *name,2145 unsigned flags)2146 {2147 int m_drive,2148 n_drive,2149 rc;2150 2151 /* Match and skip the drive name if present. */2152 2153 m_drive = ((isalpha(mask[0]) && mask[1] == ':') ? mask[0] : -1);2154 n_drive = ((isalpha(name[0]) && name[1] == ':') ? name[0] : -1);2155 2156 if (m_drive != n_drive)2157 {2158 if (m_drive == -1 || n_drive == -1)2159 return FNM_NOMATCH;2160 if (!(flags & FNM_IGNORECASE))2161 return FNM_NOMATCH;2162 if (tolower(m_drive) != tolower(n_drive))2163 return FNM_NOMATCH;2164 }2165 2166 if (m_drive != -1)2167 mask += 2;2168 if (n_drive != -1)2169 name += 2;2170 2171 /* Colons are not allowed in path names, except for the drive name,2172 * which was skipped above. */2173 2174 if (has_colon(mask) || has_colon(name))2175 return FNM_ERR;2176 2177 /* The name "\\server\path" should not be matched by mask2178 * "\*\server\path". Ditto for /. */2179 2180 switch (flags & FNM_STYLE_MASK)2181 {2182 case FNM_OS2:2183 case FNM_DOS:2184 2185 if (IS_OS2_COMP_SEP(name[0]) && IS_OS2_COMP_SEP(name[1]))2186 {2187 if (!(IS_OS2_COMP_SEP(mask[0]) && IS_OS2_COMP_SEP(mask[1])))2188 return FNM_NOMATCH;2189 name += 2;2190 mask += 2;2191 }2192 break;2193 2194 case FNM_POSIX:2195 2196 if (name[0] == '/' && name[1] == '/')2197 {2198 int i;2199 2200 name += 2;2201 for (i = 0; i < 2; ++i)2202 if (mask[0] == '/')2203 ++mask;2204 else if (mask[0] == '\\' && mask[1] == '/')2205 mask += 2;2206 else2207 return FNM_NOMATCH;2208 }2209 2210 /* In Unix styles, treating ? and * w.r.t. components is simple.2211 * No need to do matching component by component. */2212 2213 return match_unix(mask, name, flags, name);2214 }2215 2216 /* Now compare all the components of the path name, one by one.2217 * Note that the path separator must not be enclosed in brackets. */2218 2219 while (*mask != 0 || *name != 0)2220 {2221 2222 /* If _FNM_PATHPREFIX is set, the names match if the end of MASK2223 * is reached even if there are components left in NAME. */2224 2225 if (*mask == 0 && (flags & FNM_PATHPREFIX))2226 return FNM_MATCH;2227 2228 /* Compare a single component of the path name. */2229 2230 rc = match_comp(mask, name, flags);2231 if (rc != FNM_MATCH)2232 return rc;2233 2234 /* Skip to the next component or to the end of the path name. */2235 2236 mask = skip_comp_os2(mask);2237 name = skip_comp_os2(name);2238 }2239 2240 /* If we reached the ends of both strings, the names match. */2241 2242 if (*mask == 0 && *name == 0)2243 return FNM_MATCH;2244 2245 /* The names do not match. */2246 2247 return FNM_NOMATCH;2248 }2249 2250 /*2251 *@@ strhMatchOS2:2252 * this matches wildcards, similar to what DosEditName does.2253 * However, this does not require a file to be present, but2254 * works on strings only.2255 */2256 2257 BOOL strhMatchOS2(const char *pcszMask, // in: mask (e.g. "*.txt")2258 const char *pcszName) // in: string to check (e.g. "test.txt")2259 {2260 return ((BOOL)(_fnmatch_unsigned((const unsigned char *)pcszMask,2261 (const unsigned char *)pcszName,2262 FNM_OS2 | FNM_IGNORECASE)2263 == FNM_MATCH)2264 );2265 }2266 2267 /*2268 *@@ strhMatchExt:2269 * like strhMatchOS2, but this takes all the flags2270 * for input.2271 *2272 *@@added V0.9.15 (2001-09-14) [umoeller]2273 */2274 2275 BOOL strhMatchExt(const char *pcszMask, // in: mask (e.g. "*.txt")2276 const char *pcszName, // in: string to check (e.g. "test.txt")2277 unsigned flags) // in: FNM_* flags2278 {2279 return ((BOOL)(_fnmatch_unsigned((const unsigned char *)pcszMask,2280 (const unsigned char *)pcszName,2281 flags)2282 == FNM_MATCH)2283 );2284 }2285 2286 /* ******************************************************************2287 *2288 1663 * Fast string searches 2289 1664 *
Note:
See TracChangeset
for help on using the changeset viewer.