diff options
-rw-r--r-- | libc/Android.bp | 3 | ||||
-rw-r--r-- | libc/NOTICE | 69 | ||||
-rw-r--r-- | libc/include/glob.h | 98 | ||||
-rw-r--r-- | libc/libc.arm.map | 2 | ||||
-rw-r--r-- | libc/libc.arm64.map | 2 | ||||
-rw-r--r-- | libc/libc.map.txt | 2 | ||||
-rw-r--r-- | libc/libc.mips.map | 2 | ||||
-rw-r--r-- | libc/libc.mips64.map | 2 | ||||
-rw-r--r-- | libc/libc.x86.map | 2 | ||||
-rw-r--r-- | libc/libc.x86_64.map | 2 | ||||
-rw-r--r-- | libc/upstream-freebsd/android/include/collate.h | 0 | ||||
-rw-r--r-- | libc/upstream-freebsd/android/include/freebsd-compat.h | 12 | ||||
-rw-r--r-- | libc/upstream-freebsd/lib/libc/gen/glob.c | 1125 | ||||
-rw-r--r-- | libm/libm.arm.map | 2 | ||||
-rw-r--r-- | libm/libm.arm64.map | 2 | ||||
-rw-r--r-- | libm/libm.mips.map | 2 | ||||
-rw-r--r-- | libm/libm.mips64.map | 2 | ||||
-rw-r--r-- | libm/libm.x86.map | 2 | ||||
-rw-r--r-- | libm/libm.x86_64.map | 2 | ||||
-rw-r--r-- | tests/Android.bp | 1 | ||||
-rw-r--r-- | tests/glob_test.cpp | 249 |
21 files changed, 1576 insertions, 7 deletions
diff --git a/libc/Android.bp b/libc/Android.bp index d126f2f44..3391a6c3d 100644 --- a/libc/Android.bp +++ b/libc/Android.bp @@ -299,13 +299,14 @@ cc_library_static { cc_library_static { defaults: ["libc_defaults"], srcs: [ + "upstream-freebsd/lib/libc/gen/glob.c", "upstream-freebsd/lib/libc/stdlib/realpath.c", ], cflags: [ "-Wno-sign-compare", "-include freebsd-compat.h", - "-Wframe-larger-than=15000", + "-Wframe-larger-than=66000", ], local_include_dirs: [ diff --git a/libc/NOTICE b/libc/NOTICE index feea29d59..32980b7e2 100644 --- a/libc/NOTICE +++ b/libc/NOTICE @@ -1914,6 +1914,75 @@ Copyright (c) 1989, 1993 The Regents of the University of California. All rights reserved. This code is derived from software contributed to Berkeley by +Guido van Rossum. + +Copyright (c) 2011 The FreeBSD Foundation +All rights reserved. +Portions of this software were developed by David Chisnall +under sponsorship from the FreeBSD Foundation. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +------------------------------------------------------------------- + +Copyright (c) 1989, 1993 + The Regents of the University of California. All rights reserved. + +This code is derived from software contributed to Berkeley by +Guido van Rossum. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +------------------------------------------------------------------- + +Copyright (c) 1989, 1993 + The Regents of the University of California. All rights reserved. + +This code is derived from software contributed to Berkeley by Roger L. Snyder. Redistribution and use in source and binary forms, with or without diff --git a/libc/include/glob.h b/libc/include/glob.h new file mode 100644 index 000000000..6ae857340 --- /dev/null +++ b/libc/include/glob.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)glob.h 8.1 (Berkeley) 6/2/93 + * $FreeBSD$ + */ + +#ifndef _GLOB_H_ +#define _GLOB_H_ + +#include <sys/cdefs.h> +#include <sys/types.h> + +struct dirent; +struct stat; + +typedef struct { + size_t gl_pathc; /* Count of total paths so far. */ + size_t gl_matchc; /* Count of paths matching pattern. */ + size_t gl_offs; /* Reserved at beginning of gl_pathv. */ + int gl_flags; /* Copy of flags parameter to glob. */ + char** gl_pathv; /* List of paths matching pattern. */ + + /* Copy of `__error_callback` parameter to glob. */ + int (*gl_errfunc)(const char* __failure_path, int __failure_errno); + + /* + * Alternate filesystem access methods for glob; replacement + * versions of closedir(3), readdir(3), opendir(3), stat(2) + * and lstat(2). + */ + void (*gl_closedir)(void*); + struct dirent* (*gl_readdir)(void*); + void* (*gl_opendir)(const char*); + int (*gl_lstat)(const char*, struct stat*); + int (*gl_stat)(const char*, struct stat*); +} glob_t; + +/* Believed to have been introduced in 1003.2-1992 */ +#define GLOB_APPEND 0x0001 /* Append to output from previous call. */ +#define GLOB_DOOFFS 0x0002 /* Prepend `gl_offs` null pointers (leaving space for exec, say). */ +#define GLOB_ERR 0x0004 /* Return on error. */ +#define GLOB_MARK 0x0008 /* Append "/" to the names of returned directories. */ +#define GLOB_NOCHECK 0x0010 /* Return pattern itself if nothing matches. */ +#define GLOB_NOSORT 0x0020 /* Don't sort. */ +#define GLOB_NOESCAPE 0x2000 /* Disable backslash escaping. */ + +/* Error values returned by glob(3) */ +#define GLOB_NOSPACE (-1) /* Malloc call failed. */ +#define GLOB_ABORTED (-2) /* Unignored error. */ +#define GLOB_NOMATCH (-3) /* No match and GLOB_NOCHECK was not set. */ + +#if __USE_BSD +#define GLOB_ALTDIRFUNC 0x0040 /* Use alternately specified directory funcs. */ +#define GLOB_BRACE 0x0080 /* Expand braces like csh. */ +#define GLOB_MAGCHAR 0x0100 /* Set in `gl_flags` if the pattern had globbing characters. */ +#define GLOB_NOMAGIC 0x0200 /* GLOB_NOCHECK without magic chars (csh). */ +#define GLOB_QUOTE 0x0400 /* Quote special chars with \. */ +#define GLOB_TILDE 0x0800 /* Expand tilde names from the passwd file. */ +#define GLOB_LIMIT 0x1000 /* limit number of returned paths */ +#endif + +__BEGIN_DECLS + +int glob(const char* __pattern, int __flags, int (*__error_callback)(const char* __failure_path, int __failure_errno), glob_t* __result_ptr) __INTRODUCED_IN_FUTURE; +void globfree(glob_t* __result_ptr) __INTRODUCED_IN_FUTURE; + +__END_DECLS + +#endif diff --git a/libc/libc.arm.map b/libc/libc.arm.map index 501895173..50e0e7b8b 100644 --- a/libc/libc.arm.map +++ b/libc/libc.arm.map @@ -1323,6 +1323,8 @@ LIBC_P { __freading; # future __fwriting; # future getlogin_r; # future + glob; # future + globfree; # future hcreate; # future hcreate_r; # future hdestroy; # future diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map index 4ea589663..84b3d813d 100644 --- a/libc/libc.arm64.map +++ b/libc/libc.arm64.map @@ -1243,6 +1243,8 @@ LIBC_P { __freading; # future __fwriting; # future getlogin_r; # future + glob; # future + globfree; # future hcreate; # future hcreate_r; # future hdestroy; # future diff --git a/libc/libc.map.txt b/libc/libc.map.txt index a0ac50aca..aea5f9d7b 100644 --- a/libc/libc.map.txt +++ b/libc/libc.map.txt @@ -1348,6 +1348,8 @@ LIBC_P { __freading; # future __fwriting; # future getlogin_r; # future + glob; # future + globfree; # future hcreate; # future hcreate_r; # future hdestroy; # future diff --git a/libc/libc.mips.map b/libc/libc.mips.map index 897054933..9bb63abe1 100644 --- a/libc/libc.mips.map +++ b/libc/libc.mips.map @@ -1307,6 +1307,8 @@ LIBC_P { __freading; # future __fwriting; # future getlogin_r; # future + glob; # future + globfree; # future hcreate; # future hcreate_r; # future hdestroy; # future diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map index 4ea589663..84b3d813d 100644 --- a/libc/libc.mips64.map +++ b/libc/libc.mips64.map @@ -1243,6 +1243,8 @@ LIBC_P { __freading; # future __fwriting; # future getlogin_r; # future + glob; # future + globfree; # future hcreate; # future hcreate_r; # future hdestroy; # future diff --git a/libc/libc.x86.map b/libc/libc.x86.map index 543985258..4e4aa893d 100644 --- a/libc/libc.x86.map +++ b/libc/libc.x86.map @@ -1305,6 +1305,8 @@ LIBC_P { __freading; # future __fwriting; # future getlogin_r; # future + glob; # future + globfree; # future hcreate; # future hcreate_r; # future hdestroy; # future diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map index 4ea589663..84b3d813d 100644 --- a/libc/libc.x86_64.map +++ b/libc/libc.x86_64.map @@ -1243,6 +1243,8 @@ LIBC_P { __freading; # future __fwriting; # future getlogin_r; # future + glob; # future + globfree; # future hcreate; # future hcreate_r; # future hdestroy; # future diff --git a/libc/upstream-freebsd/android/include/collate.h b/libc/upstream-freebsd/android/include/collate.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/libc/upstream-freebsd/android/include/collate.h diff --git a/libc/upstream-freebsd/android/include/freebsd-compat.h b/libc/upstream-freebsd/android/include/freebsd-compat.h index 7acdf7cb1..8f0a30742 100644 --- a/libc/upstream-freebsd/android/include/freebsd-compat.h +++ b/libc/upstream-freebsd/android/include/freebsd-compat.h @@ -18,6 +18,10 @@ #define _BIONIC_FREEBSD_COMPAT_H_included #define _BSD_SOURCE + +#include <sys/cdefs.h> +#include <stddef.h> // For size_t. + #define REPLACE_GETOPT /* @@ -40,4 +44,12 @@ /* Redirect internal C library calls to the public function. */ #define _nanosleep nanosleep +/* FreeBSD has this as API, but we just use it internally. */ +void* reallocarray(void*, size_t, size_t); + +/* FreeBSD has this, but we can't really implement it correctly on Linux. */ +#define issetugid() 0 + +#define ARG_MAX sysconf(_SC_ARG_MAX) + #endif diff --git a/libc/upstream-freebsd/lib/libc/gen/glob.c b/libc/upstream-freebsd/lib/libc/gen/glob.c new file mode 100644 index 000000000..026f68e7a --- /dev/null +++ b/libc/upstream-freebsd/lib/libc/gen/glob.c @@ -0,0 +1,1125 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Copyright (c) 2011 The FreeBSD Foundation + * All rights reserved. + * Portions of this software were developed by David Chisnall + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)glob.c 8.3 (Berkeley) 10/13/93"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/lib/libc/gen/glob.c 317913 2017-05-07 19:52:56Z jilles $"); + +/* + * glob(3) -- a superset of the one defined in POSIX 1003.2. + * + * The [!...] convention to negate a range is supported (SysV, Posix, ksh). + * + * Optional extra services, controlled by flags not defined by POSIX: + * + * GLOB_QUOTE: + * Escaping convention: \ inhibits any special meaning the following + * character might have (except \ at end of string is retained). + * GLOB_MAGCHAR: + * Set in gl_flags if pattern contained a globbing character. + * GLOB_NOMAGIC: + * Same as GLOB_NOCHECK, but it will only append pattern if it did + * not contain any magic characters. [Used in csh style globbing] + * GLOB_ALTDIRFUNC: + * Use alternately specified directory access functions. + * GLOB_TILDE: + * expand ~user/foo to the /home/dir/of/user/foo + * GLOB_BRACE: + * expand {1,2}{a,b} to 1a 1b 2a 2b + * gl_matchc: + * Number of matches in the current invocation of glob. + */ + +/* + * Some notes on multibyte character support: + * 1. Patterns with illegal byte sequences match nothing - even if + * GLOB_NOCHECK is specified. + * 2. Illegal byte sequences in filenames are handled by treating them as + * single-byte characters with a values of such bytes of the sequence + * cast to wchar_t. + * 3. State-dependent encodings are not currently supported. + */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <glob.h> +#include <limits.h> +#include <pwd.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wchar.h> + +#include "collate.h" + +/* + * glob(3) expansion limits. Stop the expansion if any of these limits + * is reached. This caps the runtime in the face of DoS attacks. See + * also CVE-2010-2632 + */ +#define GLOB_LIMIT_BRACE 128 /* number of brace calls */ +#define GLOB_LIMIT_PATH 65536 /* number of path elements */ +#define GLOB_LIMIT_READDIR 16384 /* number of readdirs */ +#define GLOB_LIMIT_STAT 1024 /* number of stat system calls */ +#define GLOB_LIMIT_STRING ARG_MAX /* maximum total size for paths */ + +struct glob_limit { + size_t l_brace_cnt; + size_t l_path_lim; + size_t l_readdir_cnt; + size_t l_stat_cnt; + size_t l_string_cnt; +}; + +#define DOT L'.' +#define EOS L'\0' +#define LBRACKET L'[' +#define NOT L'!' +#define QUESTION L'?' +#define QUOTE L'\\' +#define RANGE L'-' +#define RBRACKET L']' +#define SEP L'/' +#define STAR L'*' +#define TILDE L'~' +#define LBRACE L'{' +#define RBRACE L'}' +#define COMMA L',' + +#define M_QUOTE 0x8000000000ULL +#define M_PROTECT 0x4000000000ULL +#define M_MASK 0xffffffffffULL +#define M_CHAR 0x00ffffffffULL + +typedef uint_fast64_t Char; + +#define CHAR(c) ((Char)((c)&M_CHAR)) +#define META(c) ((Char)((c)|M_QUOTE)) +#define UNPROT(c) ((c) & ~M_PROTECT) +#define M_ALL META(L'*') +#define M_END META(L']') +#define M_NOT META(L'!') +#define M_ONE META(L'?') +#define M_RNG META(L'-') +#define M_SET META(L'[') +#define ismeta(c) (((c)&M_QUOTE) != 0) +#ifdef DEBUG +#define isprot(c) (((c)&M_PROTECT) != 0) +#endif + +static int compare(const void *, const void *); +static int g_Ctoc(const Char *, char *, size_t); +static int g_lstat(Char *, struct stat *, glob_t *); +static DIR *g_opendir(Char *, glob_t *); +static const Char *g_strchr(const Char *, wchar_t); +#ifdef notdef +static Char *g_strcat(Char *, const Char *); +#endif +static int g_stat(Char *, struct stat *, glob_t *); +static int glob0(const Char *, glob_t *, struct glob_limit *, + const char *); +static int glob1(Char *, glob_t *, struct glob_limit *); +static int glob2(Char *, Char *, Char *, Char *, glob_t *, + struct glob_limit *); +static int glob3(Char *, Char *, Char *, Char *, Char *, glob_t *, + struct glob_limit *); +static int globextend(const Char *, glob_t *, struct glob_limit *, + const char *); +static const Char * + globtilde(const Char *, Char *, size_t, glob_t *); +static int globexp0(const Char *, glob_t *, struct glob_limit *, + const char *); +static int globexp1(const Char *, glob_t *, struct glob_limit *); +static int globexp2(const Char *, const Char *, glob_t *, + struct glob_limit *); +static int globfinal(glob_t *, struct glob_limit *, size_t, + const char *); +static int match(Char *, Char *, Char *); +static int err_nomatch(glob_t *, struct glob_limit *, const char *); +static int err_aborted(glob_t *, int, char *); +#ifdef DEBUG +static void qprintf(const char *, Char *); +#endif + +int +glob(const char * __restrict pattern, int flags, + int (*errfunc)(const char *, int), glob_t * __restrict pglob) +{ + struct glob_limit limit = { 0, 0, 0, 0, 0 }; + const char *patnext; + Char *bufnext, *bufend, patbuf[MAXPATHLEN], prot; + mbstate_t mbs; + wchar_t wc; + size_t clen; + int too_long; + + patnext = pattern; + if (!(flags & GLOB_APPEND)) { + pglob->gl_pathc = 0; + pglob->gl_pathv = NULL; + if (!(flags & GLOB_DOOFFS)) + pglob->gl_offs = 0; + } + if (flags & GLOB_LIMIT) { + limit.l_path_lim = pglob->gl_matchc; + if (limit.l_path_lim == 0) + limit.l_path_lim = GLOB_LIMIT_PATH; + } + pglob->gl_flags = flags & ~GLOB_MAGCHAR; + pglob->gl_errfunc = errfunc; + pglob->gl_matchc = 0; + + bufnext = patbuf; + bufend = bufnext + MAXPATHLEN - 1; + too_long = 1; + if (flags & GLOB_NOESCAPE) { + memset(&mbs, 0, sizeof(mbs)); + while (bufnext <= bufend) { + clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs); + if (clen == (size_t)-1 || clen == (size_t)-2) + return (err_nomatch(pglob, &limit, pattern)); + else if (clen == 0) { + too_long = 0; + break; + } + *bufnext++ = wc; + patnext += clen; + } + } else { + /* Protect the quoted characters. */ + memset(&mbs, 0, sizeof(mbs)); + while (bufnext <= bufend) { + if (*patnext == '\\') { + if (*++patnext == '\0') { + *bufnext++ = QUOTE; + continue; + } + prot = M_PROTECT; + } else + prot = 0; + clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs); + if (clen == (size_t)-1 || clen == (size_t)-2) + return (err_nomatch(pglob, &limit, pattern)); + else if (clen == 0) { + too_long = 0; + break; + } + *bufnext++ = wc | prot; + patnext += clen; + } + } + if (too_long) + return (err_nomatch(pglob, &limit, pattern)); + *bufnext = EOS; + + if (flags & GLOB_BRACE) + return (globexp0(patbuf, pglob, &limit, pattern)); + else + return (glob0(patbuf, pglob, &limit, pattern)); +} + +static int +globexp0(const Char *pattern, glob_t *pglob, struct glob_limit *limit, + const char *origpat) { + int rv; + size_t oldpathc; + + /* Protect a single {}, for find(1), like csh */ + if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) { + if ((pglob->gl_flags & GLOB_LIMIT) && + limit->l_brace_cnt++ >= GLOB_LIMIT_BRACE) { + errno = E2BIG; + return (GLOB_NOSPACE); + } + return (glob0(pattern, pglob, limit, origpat)); + } + + oldpathc = pglob->gl_pathc; + + if ((rv = globexp1(pattern, pglob, limit)) != 0) + return rv; + + return (globfinal(pglob, limit, oldpathc, origpat)); +} + +/* + * Expand recursively a glob {} pattern. When there is no more expansion + * invoke the standard globbing routine to glob the rest of the magic + * characters + */ +static int +globexp1(const Char *pattern, glob_t *pglob, struct glob_limit *limit) +{ + const Char* ptr; + + if ((ptr = g_strchr(pattern, LBRACE)) != NULL) { + if ((pglob->gl_flags & GLOB_LIMIT) && + limit->l_brace_cnt++ >= GLOB_LIMIT_BRACE) { + errno = E2BIG; + return (GLOB_NOSPACE); + } + return (globexp2(ptr, pattern, pglob, limit)); + } + + return (glob0(pattern, pglob, limit, NULL)); +} + + +/* + * Recursive brace globbing helper. Tries to expand a single brace. + * If it succeeds then it invokes globexp1 with the new pattern. + * If it fails then it tries to glob the rest of the pattern and returns. + */ +static int +globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, + struct glob_limit *limit) +{ + int i, rv; + Char *lm, *ls; + const Char *pe, *pm, *pm1, *pl; + Char patbuf[MAXPATHLEN]; + + /* copy part up to the brace */ + for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++) + continue; + *lm = EOS; + ls = lm; + + /* Find the balanced brace */ + for (i = 0, pe = ++ptr; *pe != EOS; pe++) + if (*pe == LBRACKET) { + /* Ignore everything between [] */ + for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++) + continue; + if (*pe == EOS) { + /* + * We could not find a matching RBRACKET. + * Ignore and just look for RBRACE + */ + pe = pm; + } + } + else if (*pe == LBRACE) + i++; + else if (*pe == RBRACE) { + if (i == 0) + break; + i--; + } + + /* Non matching braces; just glob the pattern */ + if (i != 0 || *pe == EOS) + return (glob0(pattern, pglob, limit, NULL)); + + for (i = 0, pl = pm = ptr; pm <= pe; pm++) + switch (*pm) { + case LBRACKET: + /* Ignore everything between [] */ + for (pm1 = pm++; *pm != RBRACKET && *pm != EOS; pm++) + continue; + if (*pm == EOS) { + /* + * We could not find a matching RBRACKET. + * Ignore and just look for RBRACE + */ + pm = pm1; + } + break; + + case LBRACE: + i++; + break; + + case RBRACE: + if (i) { + i--; + break; + } + /* FALLTHROUGH */ + case COMMA: + if (i && *pm == COMMA) + break; + else { + /* Append the current string */ + for (lm = ls; (pl < pm); *lm++ = *pl++) + continue; + /* + * Append the rest of the pattern after the + * closing brace + */ + for (pl = pe + 1; (*lm++ = *pl++) != EOS;) + continue; + + /* Expand the current pattern */ +#ifdef DEBUG + qprintf("globexp2:", patbuf); +#endif + rv = globexp1(patbuf, pglob, limit); + if (rv) + return (rv); + + /* move after the comma, to the next string */ + pl = pm + 1; + } + break; + + default: + break; + } + return (0); +} + + + +/* + * expand tilde from the passwd file. + */ +static const Char * +globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob) +{ + struct passwd *pwd; + char *h, *sc; + const Char *p; + Char *b, *eb; + wchar_t wc; + wchar_t wbuf[MAXPATHLEN]; + wchar_t *wbufend, *dc; + size_t clen; + mbstate_t mbs; + int too_long; + + if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE)) + return (pattern); + + /* + * Copy up to the end of the string or / + */ + eb = &patbuf[patbuf_len - 1]; + for (p = pattern + 1, b = patbuf; + b < eb && *p != EOS && UNPROT(*p) != SEP; *b++ = *p++) + continue; + + if (*p != EOS && UNPROT(*p) != SEP) + return (NULL); + + *b = EOS; + h = NULL; + + if (patbuf[0] == EOS) { + /* + * handle a plain ~ or ~/ by expanding $HOME first (iff + * we're not running setuid or setgid) and then trying + * the password file + */ + if (issetugid() != 0 || + (h = getenv("HOME")) == NULL) { + if (((h = getlogin()) != NULL && + (pwd = getpwnam(h)) != NULL) || + (pwd = getpwuid(getuid())) != NULL) + h = pwd->pw_dir; + else + return (pattern); + } + } + else { + /* + * Expand a ~user + */ + if (g_Ctoc(patbuf, (char *)wbuf, sizeof(wbuf))) + return (NULL); + if ((pwd = getpwnam((char *)wbuf)) == NULL) + return (pattern); + else + h = pwd->pw_dir; + } + + /* Copy the home directory */ + dc = wbuf; + sc = h; + wbufend = wbuf + MAXPATHLEN - 1; + too_long = 1; + memset(&mbs, 0, sizeof(mbs)); + while (dc <= wbufend) { + clen = mbrtowc(&wc, sc, MB_LEN_MAX, &mbs); + if (clen == (size_t)-1 || clen == (size_t)-2) { + /* XXX See initial comment #2. */ + wc = (unsigned char)*sc; + clen = 1; + memset(&mbs, 0, sizeof(mbs)); + } + if ((*dc++ = wc) == EOS) { + too_long = 0; + break; + } + sc += clen; + } + if (too_long) + return (NULL); + + dc = wbuf; + for (b = patbuf; b < eb && *dc != EOS; *b++ = *dc++ | M_PROTECT) + continue; + if (*dc != EOS) + return (NULL); + + /* Append the rest of the pattern */ + if (*p != EOS) { + too_long = 1; + while (b <= eb) { + if ((*b++ = *p++) == EOS) { + too_long = 0; + break; + } + } + if (too_long) + return (NULL); + } else + *b = EOS; + + return (patbuf); +} + + +/* + * The main glob() routine: compiles the pattern (optionally processing + * quotes), calls glob1() to do the real pattern matching, and finally + * sorts the list (unless unsorted operation is requested). Returns 0 + * if things went well, nonzero if errors occurred. + */ +static int +glob0(const Char *pattern, glob_t *pglob, struct glob_limit *limit, + const char *origpat) { + const Char *qpatnext; + int err; + size_t oldpathc; + Char *bufnext, c, patbuf[MAXPATHLEN]; + + qpatnext = globtilde(pattern, patbuf, MAXPATHLEN, pglob); + if (qpatnext == NULL) { + errno = E2BIG; + return (GLOB_NOSPACE); + } + oldpathc = pglob->gl_pathc; + bufnext = patbuf; + + /* We don't need to check for buffer overflow any more. */ + while ((c = *qpatnext++) != EOS) { + switch (c) { + case LBRACKET: + c = *qpatnext; + if (c == NOT) + ++qpatnext; + if (*qpatnext == EOS || + g_strchr(qpatnext+1, RBRACKET) == NULL) { + *bufnext++ = LBRACKET; + if (c == NOT) + --qpatnext; + break; + } + *bufnext++ = M_SET; + if (c == NOT) + *bufnext++ = M_NOT; + c = *qpatnext++; + do { + *bufnext++ = CHAR(c); + if (*qpatnext == RANGE && + (c = qpatnext[1]) != RBRACKET) { + *bufnext++ = M_RNG; + *bufnext++ = CHAR(c); + qpatnext += 2; + } + } while ((c = *qpatnext++) != RBRACKET); + pglob->gl_flags |= GLOB_MAGCHAR; + *bufnext++ = M_END; + break; + case QUESTION: + pglob->gl_flags |= GLOB_MAGCHAR; + *bufnext++ = M_ONE; + break; + case STAR: + pglob->gl_flags |= GLOB_MAGCHAR; + /* collapse adjacent stars to one, + * to ensure "**" at the end continues to match the + * empty string + */ + if (bufnext == patbuf || bufnext[-1] != M_ALL) + *bufnext++ = M_ALL; + break; + default: + *bufnext++ = CHAR(c); + break; + } + } + *bufnext = EOS; +#ifdef DEBUG + qprintf("glob0:", patbuf); +#endif + + if ((err = glob1(patbuf, pglob, limit)) != 0) + return(err); + + if (origpat != NULL) + return (globfinal(pglob, limit, oldpathc, origpat)); + + return (0); +} + +static int +globfinal(glob_t *pglob, struct glob_limit *limit, size_t oldpathc, + const char *origpat) { + if (pglob->gl_pathc == oldpathc) + return (err_nomatch(pglob, limit, origpat)); + + if (!(pglob->gl_flags & GLOB_NOSORT)) + qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc, + pglob->gl_pathc - oldpathc, sizeof(char *), compare); + + return (0); +} + +static int +compare(const void *p, const void *q) +{ + return (strcoll(*(char **)p, *(char **)q)); +} + +static int +glob1(Char *pattern, glob_t *pglob, struct glob_limit *limit) +{ + Char pathbuf[MAXPATHLEN]; + + /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */ + if (*pattern == EOS) + return (0); + return (glob2(pathbuf, pathbuf, pathbuf + MAXPATHLEN - 1, + pattern, pglob, limit)); +} + +/* + * The functions glob2 and glob3 are mutually recursive; there is one level + * of recursion for each segment in the pattern that contains one or more + * meta characters. + */ +static int +glob2(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern, + glob_t *pglob, struct glob_limit *limit) +{ + struct stat sb; + Char *p, *q; + int anymeta; + + /* + * Loop over pattern segments until end of pattern or until + * segment with meta character found. + */ + for (anymeta = 0;;) { + if (*pattern == EOS) { /* End of pattern? */ + *pathend = EOS; + if (g_lstat(pathbuf, &sb, pglob)) + return (0); + + if ((pglob->gl_flags & GLOB_LIMIT) && + limit->l_stat_cnt++ >= GLOB_LIMIT_STAT) { + errno = E2BIG; + return (GLOB_NOSPACE); + } + if ((pglob->gl_flags & GLOB_MARK) && + UNPROT(pathend[-1]) != SEP && + (S_ISDIR(sb.st_mode) || + (S_ISLNK(sb.st_mode) && + g_stat(pathbuf, &sb, pglob) == 0 && + S_ISDIR(sb.st_mode)))) { + if (pathend + 1 > pathend_last) { + errno = E2BIG; + return (GLOB_NOSPACE); + } + *pathend++ = SEP; + *pathend = EOS; + } + ++pglob->gl_matchc; + return (globextend(pathbuf, pglob, limit, NULL)); + } + + /* Find end of next segment, copy tentatively to pathend. */ + q = pathend; + p = pattern; + while (*p != EOS && UNPROT(*p) != SEP) { + if (ismeta(*p)) + anymeta = 1; + if (q + 1 > pathend_last) { + errno = E2BIG; + return (GLOB_NOSPACE); + } + *q++ = *p++; + } + + if (!anymeta) { /* No expansion, do next segment. */ + pathend = q; + pattern = p; + while (UNPROT(*pattern) == SEP) { + if (pathend + 1 > pathend_last) { + errno = E2BIG; + return (GLOB_NOSPACE); + } + *pathend++ = *pattern++; + } + } else /* Need expansion, recurse. */ + return (glob3(pathbuf, pathend, pathend_last, pattern, + p, pglob, limit)); + } + /* NOTREACHED */ +} + +static int +glob3(Char *pathbuf, Char *pathend, Char *pathend_last, + Char *pattern, Char *restpattern, + glob_t *pglob, struct glob_limit *limit) +{ + struct dirent *dp; + DIR *dirp; + int err, too_long, saverrno, saverrno2; + char buf[MAXPATHLEN + MB_LEN_MAX - 1]; + + struct dirent *(*readdirfunc)(DIR *); + + if (pathend > pathend_last) { + errno = E2BIG; + return (GLOB_NOSPACE); + } + *pathend = EOS; + if (pglob->gl_errfunc != NULL && + g_Ctoc(pathbuf, buf, sizeof(buf))) { + errno = E2BIG; + return (GLOB_NOSPACE); + } + + saverrno = errno; + errno = 0; + if ((dirp = g_opendir(pathbuf, pglob)) == NULL) { + if (errno == ENOENT || errno == ENOTDIR) + return (0); + err = err_aborted(pglob, errno, buf); + if (errno == 0) + errno = saverrno; + return (err); + } + + err = 0; + + /* pglob->gl_readdir takes a void *, fix this manually */ + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + readdirfunc = (struct dirent *(*)(DIR *))pglob->gl_readdir; + else + readdirfunc = readdir; + + errno = 0; + /* Search directory for matching names. */ + while ((dp = (*readdirfunc)(dirp)) != NULL) { + char *sc; + Char *dc; + wchar_t wc; + size_t clen; + mbstate_t mbs; + + if ((pglob->gl_flags & GLOB_LIMIT) && + limit->l_readdir_cnt++ >= GLOB_LIMIT_READDIR) { + errno = E2BIG; + err = GLOB_NOSPACE; + break; + } + + /* Initial DOT must be matched literally. */ + if (dp->d_name[0] == '.' && UNPROT(*pattern) != DOT) { + errno = 0; + continue; + } + memset(&mbs, 0, sizeof(mbs)); + dc = pathend; + sc = dp->d_name; + too_long = 1; + while (dc <= pathend_last) { + clen = mbrtowc(&wc, sc, MB_LEN_MAX, &mbs); + if (clen == (size_t)-1 || clen == (size_t)-2) { + /* XXX See initial comment #2. */ + wc = (unsigned char)*sc; + clen = 1; + memset(&mbs, 0, sizeof(mbs)); + } + if ((*dc++ = wc) == EOS) { + too_long = 0; + break; + } + sc += clen; + } + if (too_long && (err = err_aborted(pglob, ENAMETOOLONG, + buf))) { + errno = ENAMETOOLONG; + break; + } + if (too_long || !match(pathend, pattern, restpattern)) { + *pathend = EOS; + errno = 0; + continue; + } + if (errno == 0) + errno = saverrno; + err = glob2(pathbuf, --dc, pathend_last, restpattern, + pglob, limit); + if (err) + break; + errno = 0; + } + + saverrno2 = errno; + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + (*pglob->gl_closedir)(dirp); + else + closedir(dirp); + errno = saverrno2; + + if (err) + return (err); + + if (dp == NULL && errno != 0 && + (err = err_aborted(pglob, errno, buf))) + return (err); + + if (errno == 0) + errno = saverrno; + return (0); +} + + +/* + * Extend the gl_pathv member of a glob_t structure to accommodate a new item, + * add the new item, and update gl_pathc. + * + * This assumes the BSD realloc, which only copies the block when its size + * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic + * behavior. + * + * Return 0 if new item added, error code if memory couldn't be allocated. + * + * Invariant of the glob_t structure: + * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and + * gl_pathv points to (gl_offs + gl_pathc + 1) items. + */ +static int +globextend(const Char *path, glob_t *pglob, struct glob_limit *limit, + const char *origpat) +{ + char **pathv; + size_t i, newn, len; + char *copy; + const Char *p; + + if ((pglob->gl_flags & GLOB_LIMIT) && + pglob->gl_matchc > limit->l_path_lim) { + errno = E2BIG; + return (GLOB_NOSPACE); + } + + newn = 2 + pglob->gl_pathc + pglob->gl_offs; + /* reallocarray(NULL, newn, size) is equivalent to malloc(newn*size). */ + pathv = reallocarray(pglob->gl_pathv, newn, sizeof(*pathv)); + if (pathv == NULL) + return (GLOB_NOSPACE); + + if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) { + /* first time around -- clear initial gl_offs items */ + pathv += pglob->gl_offs; + for (i = pglob->gl_offs + 1; --i > 0; ) + *--pathv = NULL; + } + pglob->gl_pathv = pathv; + + if (origpat != NULL) + copy = strdup(origpat); + else { + for (p = path; *p++ != EOS;) + continue; + len = MB_CUR_MAX * (size_t)(p - path); /* XXX overallocation */ + if ((copy = malloc(len)) != NULL) { + if (g_Ctoc(path, copy, len)) { + free(copy); + errno = E2BIG; + return (GLOB_NOSPACE); + } + } + } + if (copy != NULL) { + limit->l_string_cnt += strlen(copy) + 1; + if ((pglob->gl_flags & GLOB_LIMIT) && + limit->l_string_cnt >= GLOB_LIMIT_STRING) { + free(copy); + errno = E2BIG; + return (GLOB_NOSPACE); + } + pathv[pglob->gl_offs + pglob->gl_pathc++] = copy; + } + pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; + return (copy == NULL ? GLOB_NOSPACE : 0); +} + +/* + * pattern matching function for filenames. + */ +static int +match(Char *name, Char *pat, Char *patend) +{ + int ok, negate_range; + Char c, k, *nextp, *nextn; +#if !defined(__BIONIC__) + struct xlocale_collate *table = + (struct xlocale_collate*)__get_locale()->components[XLC_COLLATE]; +#endif + + nextn = NULL; + nextp = NULL; + + while (1) { + while (pat < patend) { + c = *pat++; + switch (c & M_MASK) { + case M_ALL: + if (pat == patend) + return (1); + if (*name == EOS) + return (0); + nextn = name + 1; + nextp = pat - 1; + break; + case M_ONE: + if (*name++ == EOS) + goto fail; + break; + case M_SET: + ok = 0; + if ((k = *name++) == EOS) + goto fail; + negate_range = ((*pat & M_MASK) == M_NOT); + if (negate_range != 0) + ++pat; + while (((c = *pat++) & M_MASK) != M_END) + if ((*pat & M_MASK) == M_RNG) { +#if defined(__BIONIC__) + if (c <= k && k <= pat[1]) +#else + if (table->__collate_load_error ? + CHAR(c) <= CHAR(k) && + CHAR(k) <= CHAR(pat[1]) : + __wcollate_range_cmp(CHAR(c), + CHAR(k)) <= 0 && + __wcollate_range_cmp(CHAR(k), + CHAR(pat[1])) <= 0) +#endif + ok = 1; + pat += 2; + } else if (c == k) + ok = 1; + if (ok == negate_range) + goto fail; + break; + default: + if (*name++ != c) + goto fail; + break; + } + } + if (*name == EOS) + return (1); + + fail: + if (nextn == NULL) + break; + pat = nextp; + name = nextn; + } + return (0); +} + +/* Free allocated data belonging to a glob_t structure. */ +void +globfree(glob_t *pglob) +{ + size_t i; + char **pp; + + if (pglob->gl_pathv != NULL) { + pp = pglob->gl_pathv + pglob->gl_offs; + for (i = pglob->gl_pathc; i--; ++pp) + if (*pp) + free(*pp); + free(pglob->gl_pathv); + pglob->gl_pathv = NULL; + } +} + +static DIR * +g_opendir(Char *str, glob_t *pglob) +{ + char buf[MAXPATHLEN + MB_LEN_MAX - 1]; + + if (*str == EOS) + strcpy(buf, "."); + else { + if (g_Ctoc(str, buf, sizeof(buf))) { + errno = ENAMETOOLONG; + return (NULL); + } + } + + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + return ((*pglob->gl_opendir)(buf)); + + return (opendir(buf)); +} + +static int +g_lstat(Char *fn, struct stat *sb, glob_t *pglob) +{ + char buf[MAXPATHLEN + MB_LEN_MAX - 1]; + + if (g_Ctoc(fn, buf, sizeof(buf))) { + errno = ENAMETOOLONG; + return (-1); + } + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + return((*pglob->gl_lstat)(buf, sb)); + return (lstat(buf, sb)); +} + +static int +g_stat(Char *fn, struct stat *sb, glob_t *pglob) +{ + char buf[MAXPATHLEN + MB_LEN_MAX - 1]; + + if (g_Ctoc(fn, buf, sizeof(buf))) { + errno = ENAMETOOLONG; + return (-1); + } + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + return ((*pglob->gl_stat)(buf, sb)); + return (stat(buf, sb)); +} + +static const Char * +g_strchr(const Char *str, wchar_t ch) +{ + + do { + if (*str == ch) + return (str); + } while (*str++); + return (NULL); +} + +static int +g_Ctoc(const Char *str, char *buf, size_t len) +{ + mbstate_t mbs; + size_t clen; + + memset(&mbs, 0, sizeof(mbs)); + while (len >= MB_CUR_MAX) { + clen = wcrtomb(buf, CHAR(*str), &mbs); + if (clen == (size_t)-1) { + /* XXX See initial comment #2. */ + *buf = (char)CHAR(*str); + clen = 1; + memset(&mbs, 0, sizeof(mbs)); + } + if (CHAR(*str) == EOS) + return (0); + str++; + buf += clen; + len -= clen; + } + return (1); +} + +static int +err_nomatch(glob_t *pglob, struct glob_limit *limit, const char *origpat) { + /* + * If there was no match we are going to append the origpat + * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified + * and the origpat did not contain any magic characters + * GLOB_NOMAGIC is there just for compatibility with csh. + */ + if ((pglob->gl_flags & GLOB_NOCHECK) || + ((pglob->gl_flags & GLOB_NOMAGIC) && + !(pglob->gl_flags & GLOB_MAGCHAR))) + return (globextend(NULL, pglob, limit, origpat)); + return (GLOB_NOMATCH); +} + +static int +err_aborted(glob_t *pglob, int err, char *buf) { + if ((pglob->gl_errfunc != NULL && pglob->gl_errfunc(buf, err)) || + (pglob->gl_flags & GLOB_ERR)) + return (GLOB_ABORTED); + return (0); +} + +#ifdef DEBUG +static void +qprintf(const char *str, Char *s) +{ + Char *p; + + (void)printf("%s\n", str); + if (s != NULL) { + for (p = s; *p != EOS; p++) + (void)printf("%c", (char)CHAR(*p)); + (void)printf("\n"); + for (p = s; *p != EOS; p++) + (void)printf("%c", (isprot(*p) ? '\\' : ' ')); + (void)printf("\n"); + for (p = s; *p != EOS; p++) + (void)printf("%c", (ismeta(*p) ? '_' : ' ')); + (void)printf("\n"); + } +} +#endif diff --git a/libm/libm.arm.map b/libm/libm.arm.map index 7e3175ddb..bee08d412 100644 --- a/libm/libm.arm.map +++ b/libm/libm.arm.map @@ -272,7 +272,7 @@ LIBC { *; }; -LIBC_O { +LIBC_O { # introduced=O global: cacoshl; cacosl; diff --git a/libm/libm.arm64.map b/libm/libm.arm64.map index 3e259dd3f..550c39bba 100644 --- a/libm/libm.arm64.map +++ b/libm/libm.arm64.map @@ -272,7 +272,7 @@ LIBC { *; }; -LIBC_O { +LIBC_O { # introduced=O global: cacoshl; cacosl; diff --git a/libm/libm.mips.map b/libm/libm.mips.map index b368d416c..0b6dc0231 100644 --- a/libm/libm.mips.map +++ b/libm/libm.mips.map @@ -272,7 +272,7 @@ LIBC { *; }; -LIBC_O { +LIBC_O { # introduced=O global: cacoshl; cacosl; diff --git a/libm/libm.mips64.map b/libm/libm.mips64.map index 3e259dd3f..550c39bba 100644 --- a/libm/libm.mips64.map +++ b/libm/libm.mips64.map @@ -272,7 +272,7 @@ LIBC { *; }; -LIBC_O { +LIBC_O { # introduced=O global: cacoshl; cacosl; diff --git a/libm/libm.x86.map b/libm/libm.x86.map index 3e259dd3f..550c39bba 100644 --- a/libm/libm.x86.map +++ b/libm/libm.x86.map @@ -272,7 +272,7 @@ LIBC { *; }; -LIBC_O { +LIBC_O { # introduced=O global: cacoshl; cacosl; diff --git a/libm/libm.x86_64.map b/libm/libm.x86_64.map index 3e259dd3f..550c39bba 100644 --- a/libm/libm.x86_64.map +++ b/libm/libm.x86_64.map @@ -272,7 +272,7 @@ LIBC { *; }; -LIBC_O { +LIBC_O { # introduced=O global: cacoshl; cacosl; diff --git a/tests/Android.bp b/tests/Android.bp index 37311563a..693e3cf70 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -71,6 +71,7 @@ cc_test_library { "ftw_test.cpp", "getauxval_test.cpp", "getcwd_test.cpp", + "glob_test.cpp", "grp_pwd_test.cpp", "iconv_test.cpp", "ifaddrs_test.cpp", diff --git a/tests/glob_test.cpp b/tests/glob_test.cpp new file mode 100644 index 000000000..623a2a31e --- /dev/null +++ b/tests/glob_test.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <glob.h> + +#include <dirent.h> +#include <gtest/gtest.h> + +#include <string> +#include <vector> + +#include "TemporaryFile.h" + +#if defined(__BIONIC__) +#define ASSERT_MATCH_COUNT(n_,g_) ASSERT_EQ(n_, g_.gl_matchc) +#else +#define ASSERT_MATCH_COUNT(n_,g_) +#endif + +// +// Helper for use with GLOB_ALTDIRFUNC to iterate over the elements of `fake_dir`. +// + +static std::vector<std::string> fake_dir; +static size_t fake_dir_offset; +static void fake_closedir(void*) { +} +static dirent* fake_readdir(void*) { + static dirent d; + if (fake_dir_offset >= fake_dir.size()) return nullptr; + strcpy(d.d_name, fake_dir[fake_dir_offset++].c_str()); + return &d; +} +static void* fake_opendir(const char* path) { + fake_dir_offset = 0; + if (strcmp(path, "/opendir-fail/") == 0) { + errno = EINVAL; + return nullptr; + } + return &fake_dir; +} +static int fake_lstat(const char*, struct stat*) { + return 0; +} +static int fake_stat(const char*, struct stat*) { + return 0; +} +static void InstallFake(glob_t* g) { + g->gl_closedir = fake_closedir; + g->gl_readdir = fake_readdir; + g->gl_opendir = fake_opendir; + g->gl_lstat = fake_lstat; + g->gl_stat = fake_stat; +} + +TEST(glob, glob_result_GLOB_NOMATCH) { + glob_t g = {}; + ASSERT_EQ(GLOB_NOMATCH, glob("/will/match/nothing", 0, nullptr, &g)); + ASSERT_EQ(0U, g.gl_pathc); + ASSERT_MATCH_COUNT(0U, g); +} + +TEST(glob, glob_GLOB_APPEND) { + glob_t g = {}; + ASSERT_EQ(0, glob("/proc/version", 0, nullptr, &g)); + ASSERT_EQ(1U, g.gl_pathc); + ASSERT_MATCH_COUNT(1U, g); + ASSERT_STREQ("/proc/version", g.gl_pathv[0]); + ASSERT_EQ(nullptr, g.gl_pathv[1]); + ASSERT_EQ(0, glob("/proc/version", GLOB_APPEND, nullptr, &g)); + ASSERT_EQ(2U, g.gl_pathc); + ASSERT_MATCH_COUNT(1U, g); + ASSERT_STREQ("/proc/version", g.gl_pathv[0]); + ASSERT_STREQ("/proc/version", g.gl_pathv[1]); + ASSERT_EQ(nullptr, g.gl_pathv[2]); + globfree(&g); +} + +TEST(glob, glob_GLOB_DOOFFS) { + glob_t g = {}; + g.gl_offs = 2; + ASSERT_EQ(0, glob("/proc/version", GLOB_DOOFFS, nullptr, &g)); + ASSERT_EQ(1U, g.gl_pathc); + ASSERT_MATCH_COUNT(1U, g); + ASSERT_EQ(nullptr, g.gl_pathv[0]); + ASSERT_EQ(nullptr, g.gl_pathv[1]); + ASSERT_STREQ("/proc/version", g.gl_pathv[2]); + ASSERT_EQ(nullptr, g.gl_pathv[3]); + globfree(&g); +} + +static std::string g_failure_path; +static int g_failure_errno; +static int test_error_callback_result; +static int test_error_callback(const char* failure_path, int failure_errno) { + g_failure_path = failure_path; + g_failure_errno = failure_errno; + return test_error_callback_result; +} + +TEST(glob, glob_gl_errfunc) { + glob_t g = {}; + InstallFake(&g); + + test_error_callback_result = 0; + g_failure_errno = 0; + ASSERT_EQ(GLOB_NOMATCH, glob("/opendir-fail/x*", GLOB_ALTDIRFUNC, test_error_callback, &g)); + ASSERT_EQ("/opendir-fail/", g_failure_path); + ASSERT_EQ(EINVAL, g_failure_errno); + + test_error_callback_result = 1; + g_failure_errno = 0; + ASSERT_EQ(GLOB_ABORTED, glob("/opendir-fail/x*", GLOB_ALTDIRFUNC, test_error_callback, &g)); + ASSERT_EQ("/opendir-fail/", g_failure_path); + ASSERT_EQ(EINVAL, g_failure_errno); +} + +TEST(glob, glob_GLOB_ERR) { + glob_t g = {}; + InstallFake(&g); + + ASSERT_EQ(GLOB_NOMATCH, glob("/opendir-fail/x*", GLOB_ALTDIRFUNC, nullptr, &g)); + + ASSERT_EQ(GLOB_ABORTED, glob("/opendir-fail/x*", GLOB_ALTDIRFUNC | GLOB_ERR, nullptr, &g)); +} + +TEST(glob, glob_GLOB_MARK) { + TemporaryDir td; + // The pattern we're about to pass doesn't have a trailing '/'... + ASSERT_NE('/', std::string(td.dirname).back()); + + glob_t g = {}; + // Using GLOB_MARK gets you a trailing '/' on a directory... + ASSERT_EQ(0, glob(td.dirname, GLOB_MARK, nullptr, &g)); + ASSERT_EQ(1U, g.gl_pathc); + ASSERT_MATCH_COUNT(1U, g); + ASSERT_EQ(std::string(td.dirname) + "/", g.gl_pathv[0]); + ASSERT_EQ(nullptr, g.gl_pathv[1]); + + TemporaryFile tf; + // But not on a file... + ASSERT_EQ(0, glob(tf.filename, GLOB_MARK, nullptr, &g)); + ASSERT_EQ(1U, g.gl_pathc); + ASSERT_MATCH_COUNT(1U, g); + ASSERT_STREQ(tf.filename, g.gl_pathv[0]); + ASSERT_EQ(nullptr, g.gl_pathv[1]); + + globfree(&g); +} + +TEST(glob, glob_GLOB_NOCHECK) { + glob_t g = {}; + ASSERT_EQ(0, glob("/will/match/nothing", GLOB_NOCHECK, nullptr, &g)); + ASSERT_EQ(1U, g.gl_pathc); + ASSERT_MATCH_COUNT(0U, g); + ASSERT_STREQ("/will/match/nothing", g.gl_pathv[0]); + ASSERT_EQ(nullptr, g.gl_pathv[1]); + globfree(&g); +} + +TEST(glob, glob_GLOB_NOSORT) { + fake_dir = { "c", "a", "d", "b" }; + + glob_t g = {}; + InstallFake(&g); + + ASSERT_EQ(0, glob("*", GLOB_ALTDIRFUNC, nullptr, &g)); + ASSERT_EQ(4U, g.gl_pathc); + ASSERT_MATCH_COUNT(4U, g); + ASSERT_STREQ("a", g.gl_pathv[0]); + ASSERT_STREQ("b", g.gl_pathv[1]); + ASSERT_STREQ("c", g.gl_pathv[2]); + ASSERT_STREQ("d", g.gl_pathv[3]); + ASSERT_EQ(nullptr, g.gl_pathv[4]); + + ASSERT_EQ(0, glob("*", GLOB_ALTDIRFUNC | GLOB_NOSORT, nullptr, &g)); + ASSERT_EQ(4U, g.gl_pathc); + ASSERT_MATCH_COUNT(4U, g); + ASSERT_STREQ("c", g.gl_pathv[0]); + ASSERT_STREQ("a", g.gl_pathv[1]); + ASSERT_STREQ("d", g.gl_pathv[2]); + ASSERT_STREQ("b", g.gl_pathv[3]); + ASSERT_EQ(nullptr, g.gl_pathv[4]); +} + +TEST(glob, glob_GLOB_MAGCHAR) { + glob_t g = {}; + ASSERT_EQ(GLOB_NOMATCH, glob("/does-not-exist", 0, nullptr, &g)); + ASSERT_TRUE((g.gl_flags & GLOB_MAGCHAR) == 0); + ASSERT_EQ(GLOB_NOMATCH, glob("/does-not-exist*", 0, nullptr, &g)); + ASSERT_TRUE((g.gl_flags & GLOB_MAGCHAR) != 0); + + // We can lie, but glob(3) will turn that into truth... + ASSERT_EQ(GLOB_NOMATCH, glob("/does-not-exist", GLOB_MAGCHAR, nullptr, &g)); + ASSERT_TRUE((g.gl_flags & GLOB_MAGCHAR) == 0); +} + +static void CheckGlob(const char* pattern, const std::vector<std::string>& expected_matches) { + glob_t g = {}; + InstallFake(&g); + + int expected_result = expected_matches.empty() ? GLOB_NOMATCH : 0; + ASSERT_EQ(expected_result, glob(pattern, GLOB_ALTDIRFUNC, nullptr, &g)) << pattern; + ASSERT_EQ(expected_matches.size(), g.gl_pathc); + ASSERT_MATCH_COUNT(expected_matches.size(), g); + for (size_t i = 0; i < expected_matches.size(); ++i) { + ASSERT_EQ(expected_matches[i], g.gl_pathv[i]); + } + if (!expected_matches.empty()) { + ASSERT_EQ(nullptr, g.gl_pathv[expected_matches.size()]); + } + globfree(&g); +} + +TEST(glob, glob_globbing) { + fake_dir = { "f1", "f2", "f30", "f40" }; + + CheckGlob("f?", { "f1", "f2" }); + CheckGlob("f??", { "f30", "f40" }); + CheckGlob("f*", { "f1", "f2", "f30", "f40" }); +} + +TEST(glob, glob_globbing_rsc) { + // https://research.swtch.com/glob + fake_dir = { "axbxcxdxe" }; + CheckGlob("a*b*c*d*e*", { "axbxcxdxe" }); + fake_dir = { "axbxcxdxexxx" }; + CheckGlob("a*b*c*d*e*", { "axbxcxdxexxx" }); + fake_dir = { "abxbbxdbxebxczzx" }; + CheckGlob("a*b?c*x", { "abxbbxdbxebxczzx" }); + fake_dir = { "abxbbxdbxebxczzy" }; + CheckGlob("a*b?c*x", {}); + + fake_dir = { std::string(100, 'a') }; + CheckGlob("a*a*a*a*b", {}); +} |