Index: head/lib/libc/gen/glob.c =================================================================== --- head/lib/libc/gen/glob.c (revision 315161) +++ head/lib/libc/gen/glob.c (revision 315162) @@ -1,1105 +1,1105 @@ /* * 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 __FBSDID("$FreeBSD$"); /* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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 avoid exponential behavior */ 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, newsize, len; + 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); } - newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs); - /* realloc(NULL, newsize) is equivalent to malloc(newsize). */ - pathv = realloc((void *)pglob->gl_pathv, newsize); + 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. Each occurrence of the * * pattern causes a recursion level. */ static int match(Char *name, Char *pat, Char *patend) { int ok, negate_range; Char c, k; struct xlocale_collate *table = (struct xlocale_collate*)__get_locale()->components[XLC_COLLATE]; while (pat < patend) { c = *pat++; switch (c & M_MASK) { case M_ALL: if (pat == patend) return (1); do if (match(name, pat, patend)) return (1); while (*name++ != EOS); return (0); case M_ONE: if (*name++ == EOS) return (0); break; case M_SET: ok = 0; if ((k = *name++) == EOS) return (0); if ((negate_range = ((*pat & M_MASK) == M_NOT)) != 0) ++pat; while (((c = *pat++) & M_MASK) != M_END) if ((*pat & M_MASK) == M_RNG) { 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) ok = 1; pat += 2; } else if (c == k) ok = 1; if (ok == negate_range) return (0); break; default: if (*name++ != c) return (0); break; } } return (*name == EOS); } /* 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 Index: head/lib/libc/gen/scandir.c =================================================================== --- head/lib/libc/gen/scandir.c (revision 315161) +++ head/lib/libc/gen/scandir.c (revision 315162) @@ -1,167 +1,167 @@ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * 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[] = "@(#)scandir.c 8.3 (Berkeley) 1/2/94"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); /* * Scan the directory dirname calling select to make a list of selected * directory entries then sort using qsort and compare routine dcomp. * Returns the number of entries and a pointer to a list of pointers to * struct dirent (through namelist). Returns -1 if there were any errors. */ #include "namespace.h" #include #include #include #include "un-namespace.h" #ifdef I_AM_SCANDIR_B #include "block_abi.h" #define SELECT(x) CALL_BLOCK(select, x) #ifndef __BLOCKS__ void qsort_b(void *, size_t, size_t, void*); #endif #else #define SELECT(x) select(x) #endif static int alphasort_thunk(void *thunk, const void *p1, const void *p2); /* * The DIRSIZ macro is the minimum record length which will hold the directory * entry. This requires the amount of space in struct dirent without the * d_name field, plus enough space for the name and a terminating nul byte * (dp->d_namlen + 1), rounded up to a 4 byte boundary. */ #undef DIRSIZ #define DIRSIZ(dp) \ ((sizeof(struct dirent) - sizeof(dp)->d_name) + \ (((dp)->d_namlen + 1 + 3) &~ 3)) int #ifdef I_AM_SCANDIR_B scandir_b(const char *dirname, struct dirent ***namelist, DECLARE_BLOCK(int, select, const struct dirent *), DECLARE_BLOCK(int, dcomp, const struct dirent **, const struct dirent **)) #else scandir(const char *dirname, struct dirent ***namelist, int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **, const struct dirent **)) #endif { struct dirent *d, *p, **names = NULL; size_t numitems; long arraysz; DIR *dirp; if ((dirp = opendir(dirname)) == NULL) return(-1); arraysz = 32; /* initial estimate of the array size */ names = (struct dirent **)malloc(arraysz * sizeof(struct dirent *)); if (names == NULL) goto fail; numitems = 0; while ((d = readdir(dirp)) != NULL) { if (select != NULL && !SELECT(d)) continue; /* just selected names */ /* * Make a minimum size copy of the data */ p = (struct dirent *)malloc(DIRSIZ(d)); if (p == NULL) goto fail; p->d_fileno = d->d_fileno; p->d_type = d->d_type; p->d_reclen = d->d_reclen; p->d_namlen = d->d_namlen; bcopy(d->d_name, p->d_name, p->d_namlen + 1); /* * Check to make sure the array has space left and * realloc the maximum size. */ if (numitems >= arraysz) { struct dirent **names2; - names2 = (struct dirent **)realloc((char *)names, - (arraysz * 2) * sizeof(struct dirent *)); + names2 = reallocarray(names, arraysz, + 2 * sizeof(struct dirent *)); if (names2 == NULL) { free(p); goto fail; } names = names2; arraysz *= 2; } names[numitems++] = p; } closedir(dirp); if (numitems && dcomp != NULL) #ifdef I_AM_SCANDIR_B qsort_b(names, numitems, sizeof(struct dirent *), (void*)dcomp); #else qsort_r(names, numitems, sizeof(struct dirent *), &dcomp, alphasort_thunk); #endif *namelist = names; return (numitems); fail: while (numitems > 0) free(names[--numitems]); free(names); closedir(dirp); return (-1); } /* * Alphabetic order comparison routine for those who want it. * POSIX 2008 requires that alphasort() uses strcoll(). */ int alphasort(const struct dirent **d1, const struct dirent **d2) { return (strcoll((*d1)->d_name, (*d2)->d_name)); } static int alphasort_thunk(void *thunk, const void *p1, const void *p2) { int (*dc)(const struct dirent **, const struct dirent **); dc = *(int (**)(const struct dirent **, const struct dirent **))thunk; return (dc((const struct dirent **)p1, (const struct dirent **)p2)); } Index: head/lib/libc/gen/setmode.c =================================================================== --- head/lib/libc/gen/setmode.c (revision 315161) +++ head/lib/libc/gen/setmode.c (revision 315162) @@ -1,485 +1,485 @@ /* * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Dave Borman at Cray Research, Inc. * * 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[] = "@(#)setmode.c 8.2 (Berkeley) 3/25/94"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #ifdef SETMODE_DEBUG #include #endif #include "un-namespace.h" #include "libc_private.h" #define SET_LEN 6 /* initial # of bitcmd struct to malloc */ #define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */ typedef struct bitcmd { char cmd; char cmd2; mode_t bits; } BITCMD; #define CMD2_CLR 0x01 #define CMD2_SET 0x02 #define CMD2_GBITS 0x04 #define CMD2_OBITS 0x08 #define CMD2_UBITS 0x10 static mode_t getumask(void); static BITCMD *addcmd(BITCMD *, mode_t, mode_t, mode_t, mode_t); static void compress_mode(BITCMD *); #ifdef SETMODE_DEBUG static void dumpmode(BITCMD *); #endif /* * Given the old mode and an array of bitcmd structures, apply the operations * described in the bitcmd structures to the old mode, and return the new mode. * Note that there is no '=' command; a strict assignment is just a '-' (clear * bits) followed by a '+' (set bits). */ mode_t getmode(const void *bbox, mode_t omode) { const BITCMD *set; mode_t clrval, newmode, value; set = (const BITCMD *)bbox; newmode = omode; for (value = 0;; set++) switch(set->cmd) { /* * When copying the user, group or other bits around, we "know" * where the bits are in the mode so that we can do shifts to * copy them around. If we don't use shifts, it gets real * grundgy with lots of single bit checks and bit sets. */ case 'u': value = (newmode & S_IRWXU) >> 6; goto common; case 'g': value = (newmode & S_IRWXG) >> 3; goto common; case 'o': value = newmode & S_IRWXO; common: if (set->cmd2 & CMD2_CLR) { clrval = (set->cmd2 & CMD2_SET) ? S_IRWXO : value; if (set->cmd2 & CMD2_UBITS) newmode &= ~((clrval<<6) & set->bits); if (set->cmd2 & CMD2_GBITS) newmode &= ~((clrval<<3) & set->bits); if (set->cmd2 & CMD2_OBITS) newmode &= ~(clrval & set->bits); } if (set->cmd2 & CMD2_SET) { if (set->cmd2 & CMD2_UBITS) newmode |= (value<<6) & set->bits; if (set->cmd2 & CMD2_GBITS) newmode |= (value<<3) & set->bits; if (set->cmd2 & CMD2_OBITS) newmode |= value & set->bits; } break; case '+': newmode |= set->bits; break; case '-': newmode &= ~set->bits; break; case 'X': if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH)) newmode |= set->bits; break; case '\0': default: #ifdef SETMODE_DEBUG (void)printf("getmode:%04o -> %04o\n", omode, newmode); #endif return (newmode); } } #define ADDCMD(a, b, c, d) \ if (set >= endset) { \ BITCMD *newset; \ setlen += SET_LEN_INCR; \ - newset = realloc(saveset, sizeof(BITCMD) * setlen); \ + newset = reallocarray(saveset, setlen, sizeof(BITCMD)); \ if (newset == NULL) \ goto out; \ set = newset + (set - saveset); \ saveset = newset; \ endset = newset + (setlen - 2); \ } \ set = addcmd(set, (mode_t)(a), (mode_t)(b), (mode_t)(c), (d)) #define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) void * setmode(const char *p) { int serrno; char op, *ep; BITCMD *set, *saveset, *endset; mode_t mask, perm, permXbits, who; long perml; int equalopdone; u_int setlen; if (!*p) { errno = EINVAL; return (NULL); } /* * Get a copy of the mask for the permissions that are mask relative. * Flip the bits, we want what's not set. */ mask = ~getumask(); setlen = SET_LEN + 2; if ((set = malloc(setlen * sizeof(BITCMD))) == NULL) return (NULL); saveset = set; endset = set + (setlen - 2); /* * If an absolute number, get it and return; disallow non-octal digits * or illegal bits. */ if (isdigit((unsigned char)*p)) { errno = 0; perml = strtol(p, &ep, 8); if (*ep) { errno = EINVAL; goto out; } if (errno == ERANGE && (perml == LONG_MAX || perml == LONG_MIN)) goto out; if (perml & ~(STANDARD_BITS|S_ISTXT)) { errno = EINVAL; goto out; } perm = (mode_t)perml; ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask); set->cmd = 0; return (saveset); } /* * Build list of structures to set/clear/copy bits as described by * each clause of the symbolic mode. */ equalopdone = 0; for (;;) { /* First, find out which bits might be modified. */ for (who = 0;; ++p) { switch (*p) { case 'a': who |= STANDARD_BITS; break; case 'u': who |= S_ISUID|S_IRWXU; break; case 'g': who |= S_ISGID|S_IRWXG; break; case 'o': who |= S_IRWXO; break; default: goto getop; } } getop: if ((op = *p++) != '+' && op != '-' && op != '=') { errno = EINVAL; goto out; } if (op == '=') equalopdone = 0; who &= ~S_ISTXT; for (perm = 0, permXbits = 0;; ++p) { switch (*p) { case 'r': perm |= S_IRUSR|S_IRGRP|S_IROTH; break; case 's': /* If only "other" bits ignore set-id. */ if (!who || who & ~S_IRWXO) perm |= S_ISUID|S_ISGID; break; case 't': /* If only "other" bits ignore sticky. */ if (!who || who & ~S_IRWXO) { who |= S_ISTXT; perm |= S_ISTXT; } break; case 'w': perm |= S_IWUSR|S_IWGRP|S_IWOTH; break; case 'X': permXbits = S_IXUSR|S_IXGRP|S_IXOTH; break; case 'x': perm |= S_IXUSR|S_IXGRP|S_IXOTH; break; case 'u': case 'g': case 'o': /* * When ever we hit 'u', 'g', or 'o', we have * to flush out any partial mode that we have, * and then do the copying of the mode bits. */ if (perm) { ADDCMD(op, who, perm, mask); perm = 0; } if (op == '=') equalopdone = 1; if (op == '+' && permXbits) { ADDCMD('X', who, permXbits, mask); permXbits = 0; } ADDCMD(*p, who, op, mask); break; default: /* * Add any permissions that we haven't already * done. */ if (perm || (op == '=' && !equalopdone)) { if (op == '=') equalopdone = 1; ADDCMD(op, who, perm, mask); perm = 0; } if (permXbits) { ADDCMD('X', who, permXbits, mask); permXbits = 0; } goto apply; } } apply: if (!*p) break; if (*p != ',') goto getop; ++p; } set->cmd = 0; #ifdef SETMODE_DEBUG (void)printf("Before compress_mode()\n"); dumpmode(saveset); #endif compress_mode(saveset); #ifdef SETMODE_DEBUG (void)printf("After compress_mode()\n"); dumpmode(saveset); #endif return (saveset); out: serrno = errno; free(saveset); errno = serrno; return NULL; } static mode_t getumask(void) { sigset_t sigset, sigoset; size_t len; mode_t mask; u_short smask; /* * First try requesting the umask without temporarily modifying it. * Note that this does not work if the sysctl * security.bsd.unprivileged_proc_debug is set to 0. */ len = sizeof(smask); if (sysctl((int[4]){ CTL_KERN, KERN_PROC, KERN_PROC_UMASK, getpid() }, 4, &smask, &len, NULL, 0) == 0) return (smask); /* * Since it's possible that the caller is opening files inside a signal * handler, protect them as best we can. */ sigfillset(&sigset); (void)__libc_sigprocmask(SIG_BLOCK, &sigset, &sigoset); (void)umask(mask = umask(0)); (void)__libc_sigprocmask(SIG_SETMASK, &sigoset, NULL); return (mask); } static BITCMD * addcmd(BITCMD *set, mode_t op, mode_t who, mode_t oparg, mode_t mask) { switch (op) { case '=': set->cmd = '-'; set->bits = who ? who : STANDARD_BITS; set++; op = '+'; /* FALLTHROUGH */ case '+': case '-': case 'X': set->cmd = op; set->bits = (who ? who : mask) & oparg; break; case 'u': case 'g': case 'o': set->cmd = op; if (who) { set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) | ((who & S_IRGRP) ? CMD2_GBITS : 0) | ((who & S_IROTH) ? CMD2_OBITS : 0); set->bits = (mode_t)~0; } else { set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS; set->bits = mask; } if (oparg == '+') set->cmd2 |= CMD2_SET; else if (oparg == '-') set->cmd2 |= CMD2_CLR; else if (oparg == '=') set->cmd2 |= CMD2_SET|CMD2_CLR; break; } return (set + 1); } #ifdef SETMODE_DEBUG static void dumpmode(BITCMD *set) { for (; set->cmd; ++set) (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n", set->cmd, set->bits, set->cmd2 ? " cmd2:" : "", set->cmd2 & CMD2_CLR ? " CLR" : "", set->cmd2 & CMD2_SET ? " SET" : "", set->cmd2 & CMD2_UBITS ? " UBITS" : "", set->cmd2 & CMD2_GBITS ? " GBITS" : "", set->cmd2 & CMD2_OBITS ? " OBITS" : ""); } #endif /* * Given an array of bitcmd structures, compress by compacting consecutive * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u', * 'g' and 'o' commands continue to be separate. They could probably be * compacted, but it's not worth the effort. */ static void compress_mode(BITCMD *set) { BITCMD *nset; int setbits, clrbits, Xbits, op; for (nset = set;;) { /* Copy over any 'u', 'g' and 'o' commands. */ while ((op = nset->cmd) != '+' && op != '-' && op != 'X') { *set++ = *nset++; if (!op) return; } for (setbits = clrbits = Xbits = 0;; nset++) { if ((op = nset->cmd) == '-') { clrbits |= nset->bits; setbits &= ~nset->bits; Xbits &= ~nset->bits; } else if (op == '+') { setbits |= nset->bits; clrbits &= ~nset->bits; Xbits &= ~nset->bits; } else if (op == 'X') Xbits |= nset->bits & ~setbits; else break; } if (clrbits) { set->cmd = '-'; set->cmd2 = 0; set->bits = clrbits; set++; } if (setbits) { set->cmd = '+'; set->cmd2 = 0; set->bits = setbits; set++; } if (Xbits) { set->cmd = 'X'; set->cmd2 = 0; set->bits = Xbits; set++; } } } Index: head/lib/libc/gen/wordexp.c =================================================================== --- head/lib/libc/gen/wordexp.c (revision 315161) +++ head/lib/libc/gen/wordexp.c (revision 315162) @@ -1,410 +1,410 @@ /*- * Copyright (c) 2002 Tim J. Robbins. * All rights reserved. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.h" __FBSDID("$FreeBSD$"); static int we_askshell(const char *, wordexp_t *, int); static int we_check(const char *); /* * wordexp -- * Perform shell word expansion on `words' and place the resulting list * of words in `we'. See wordexp(3). * * Specified by IEEE Std. 1003.1-2001. */ int wordexp(const char * __restrict words, wordexp_t * __restrict we, int flags) { int error; if (flags & WRDE_REUSE) wordfree(we); if ((flags & WRDE_APPEND) == 0) { we->we_wordc = 0; we->we_wordv = NULL; we->we_strings = NULL; we->we_nbytes = 0; } if ((error = we_check(words)) != 0) { wordfree(we); return (error); } if ((error = we_askshell(words, we, flags)) != 0) { wordfree(we); return (error); } return (0); } static size_t we_read_fully(int fd, char *buffer, size_t len) { size_t done; ssize_t nread; done = 0; do { nread = _read(fd, buffer + done, len - done); if (nread == -1 && errno == EINTR) continue; if (nread <= 0) break; done += nread; } while (done != len); return done; } static bool we_write_fully(int fd, const char *buffer, size_t len) { size_t done; ssize_t nwritten; done = 0; do { nwritten = _write(fd, buffer + done, len - done); if (nwritten == -1 && errno == EINTR) continue; if (nwritten <= 0) return (false); done += nwritten; } while (done != len); return (true); } /* * we_askshell -- * Use the `freebsd_wordexp' /bin/sh builtin function to do most of the * work in expanding the word string. This function is complicated by * memory management. */ static int we_askshell(const char *words, wordexp_t *we, int flags) { int pdesw[2]; /* Pipe for writing words */ int pdes[2]; /* Pipe for reading output */ char wfdstr[sizeof(int) * 3 + 1]; char buf[35]; /* Buffer for byte and word count */ long nwords, nbytes; /* Number of words, bytes from child */ long i; /* Handy integer */ size_t sofs; /* Offset into we->we_strings */ size_t vofs; /* Offset into we->we_wordv */ pid_t pid; /* Process ID of child */ pid_t wpid; /* waitpid return value */ int status; /* Child exit status */ int error; /* Our return value */ int serrno; /* errno to return */ char *np, *p; /* Handy pointers */ char *nstrings; /* Temporary for realloc() */ char **nwv; /* Temporary for realloc() */ sigset_t newsigblock, oldsigblock; const char *ifs; serrno = errno; ifs = getenv("IFS"); if (pipe2(pdesw, O_CLOEXEC) < 0) return (WRDE_NOSPACE); /* XXX */ snprintf(wfdstr, sizeof(wfdstr), "%d", pdesw[0]); if (pipe2(pdes, O_CLOEXEC) < 0) { _close(pdesw[0]); _close(pdesw[1]); return (WRDE_NOSPACE); /* XXX */ } (void)sigemptyset(&newsigblock); (void)sigaddset(&newsigblock, SIGCHLD); (void)__libc_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); if ((pid = fork()) < 0) { serrno = errno; _close(pdesw[0]); _close(pdesw[1]); _close(pdes[0]); _close(pdes[1]); (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); errno = serrno; return (WRDE_NOSPACE); /* XXX */ } else if (pid == 0) { /* * We are the child; make /bin/sh expand `words'. */ (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); if ((pdes[1] != STDOUT_FILENO ? _dup2(pdes[1], STDOUT_FILENO) : _fcntl(pdes[1], F_SETFD, 0)) < 0) _exit(1); if (_fcntl(pdesw[0], F_SETFD, 0) < 0) _exit(1); execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u", "-c", "IFS=$1;eval \"$2\";" "freebsd_wordexp -f \"$3\" ${4:+\"$4\"}", "", ifs != NULL ? ifs : " \t\n", flags & WRDE_SHOWERR ? "" : "exec 2>/dev/null", wfdstr, flags & WRDE_NOCMD ? "-p" : "", (char *)NULL); _exit(1); } /* * We are the parent; write the words. */ _close(pdes[1]); _close(pdesw[0]); if (!we_write_fully(pdesw[1], words, strlen(words))) { _close(pdesw[1]); error = WRDE_SYNTAX; goto cleanup; } _close(pdesw[1]); /* * Read the output of the shell wordexp function, * which is a byte indicating that the words were parsed successfully, * a 64-bit hexadecimal word count, a dummy byte, a 64-bit hexadecimal * byte count (not including terminating null bytes), followed by the * expanded words separated by nulls. */ switch (we_read_fully(pdes[0], buf, 34)) { case 1: error = buf[0] == 'C' ? WRDE_CMDSUB : WRDE_BADVAL; serrno = errno; goto cleanup; case 34: break; default: error = WRDE_SYNTAX; serrno = errno; goto cleanup; } buf[17] = '\0'; nwords = strtol(buf + 1, NULL, 16); buf[34] = '\0'; nbytes = strtol(buf + 18, NULL, 16) + nwords; /* * Allocate or reallocate (when flags & WRDE_APPEND) the word vector * and string storage buffers for the expanded words we're about to * read from the child. */ sofs = we->we_nbytes; vofs = we->we_wordc; if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND)) vofs += we->we_offs; we->we_wordc += nwords; we->we_nbytes += nbytes; - if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 + - (flags & WRDE_DOOFFS ? we->we_offs : 0)) * + if ((nwv = reallocarray(we->we_wordv, (we->we_wordc + 1 + + (flags & WRDE_DOOFFS ? we->we_offs : 0)), sizeof(char *))) == NULL) { error = WRDE_NOSPACE; goto cleanup; } we->we_wordv = nwv; if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) { error = WRDE_NOSPACE; goto cleanup; } for (i = 0; i < vofs; i++) if (we->we_wordv[i] != NULL) we->we_wordv[i] += nstrings - we->we_strings; we->we_strings = nstrings; if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { error = WRDE_NOSPACE; /* abort for unknown reason */ serrno = errno; goto cleanup; } error = 0; cleanup: _close(pdes[0]); do wpid = _waitpid(pid, &status, 0); while (wpid < 0 && errno == EINTR); (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); if (error != 0) { errno = serrno; return (error); } if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) return (WRDE_NOSPACE); /* abort for unknown reason */ /* * Break the null-terminated expanded word strings out into * the vector. */ if (vofs == 0 && flags & WRDE_DOOFFS) while (vofs < we->we_offs) we->we_wordv[vofs++] = NULL; p = we->we_strings + sofs; while (nwords-- != 0) { we->we_wordv[vofs++] = p; if ((np = memchr(p, '\0', nbytes)) == NULL) return (WRDE_NOSPACE); /* XXX */ nbytes -= np - p + 1; p = np + 1; } we->we_wordv[vofs] = NULL; return (0); } /* * we_check -- * Check that the string contains none of the following unquoted * special characters: |&;<>(){} * This mainly serves for {} which are normally legal in sh. * It deliberately does not attempt to model full sh syntax. */ static int we_check(const char *words) { char c; /* Saw \ or $, possibly not special: */ bool quote = false, dollar = false; /* Saw ', ", ${, ` or $(, possibly not special: */ bool have_sq = false, have_dq = false, have_par_begin = false; bool have_cmd = false; /* Definitely saw a ', ", ${, ` or $(, need a closing character: */ bool need_sq = false, need_dq = false, need_par_end = false; bool need_cmd_old = false, need_cmd_new = false; while ((c = *words++) != '\0') { switch (c) { case '\\': quote = !quote; continue; case '$': if (quote) quote = false; else dollar = !dollar; continue; case '\'': if (!quote && !have_sq && !have_dq) need_sq = true; else need_sq = false; have_sq = true; break; case '"': if (!quote && !have_sq && !have_dq) need_dq = true; else need_dq = false; have_dq = true; break; case '`': if (!quote && !have_sq && !have_cmd) need_cmd_old = true; else need_cmd_old = false; have_cmd = true; break; case '{': if (!quote && !dollar && !have_sq && !have_dq && !have_cmd) return (WRDE_BADCHAR); if (dollar) { if (!quote && !have_sq) need_par_end = true; have_par_begin = true; } break; case '}': if (!quote && !have_sq && !have_dq && !have_par_begin && !have_cmd) return (WRDE_BADCHAR); need_par_end = false; break; case '(': if (!quote && !dollar && !have_sq && !have_dq && !have_cmd) return (WRDE_BADCHAR); if (dollar) { if (!quote && !have_sq) need_cmd_new = true; have_cmd = true; } break; case ')': if (!quote && !have_sq && !have_dq && !have_cmd) return (WRDE_BADCHAR); need_cmd_new = false; break; case '|': case '&': case ';': case '<': case '>': case '\n': if (!quote && !have_sq && !have_dq && !have_cmd) return (WRDE_BADCHAR); break; default: break; } quote = dollar = false; } if (quote || dollar || need_sq || need_dq || need_par_end || need_cmd_old || need_cmd_new) return (WRDE_SYNTAX); return (0); } /* * wordfree -- * Free the result of wordexp(). See wordexp(3). * * Specified by IEEE Std. 1003.1-2001. */ void wordfree(wordexp_t *we) { if (we == NULL) return; free(we->we_wordv); free(we->we_strings); we->we_wordv = NULL; we->we_strings = NULL; we->we_nbytes = 0; we->we_wordc = 0; } Index: head/lib/libc/iconv/citrus_esdb.c =================================================================== --- head/lib/libc/iconv/citrus_esdb.c (revision 315161) +++ head/lib/libc/iconv/citrus_esdb.c (revision 315162) @@ -1,366 +1,366 @@ /* $FreeBSD$ */ /* $NetBSD: citrus_esdb.c,v 1.5 2008/02/09 14:56:20 junyoung Exp $ */ /*- * Copyright (c)2003 Citrus Project, * All rights reserved. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include #include #include #include #include #include #include #include #include #include #include "citrus_namespace.h" #include "citrus_types.h" #include "citrus_bcs.h" #include "citrus_region.h" #include "citrus_memstream.h" #include "citrus_mmap.h" #include "citrus_lookup.h" #include "citrus_db.h" #include "citrus_db_hash.h" #include "citrus_esdb.h" #include "citrus_esdb_file.h" #define ESDB_DIR "esdb.dir" #define ESDB_ALIAS "esdb.alias" /* * _citrus_esdb_alias: * resolve encoding scheme name aliases. */ const char * _citrus_esdb_alias(const char *esname, char *buf, size_t bufsize) { return (_lookup_alias(_PATH_ESDB "/" ESDB_ALIAS, esname, buf, bufsize, _LOOKUP_CASE_IGNORE)); } /* * conv_esdb: * external representation -> local structure. */ static int conv_esdb(struct _citrus_esdb *esdb, struct _region *fr) { struct _citrus_db *db; const char *str; char buf[100]; uint32_t csid, i, num_charsets, tmp, version; int ret; /* open db */ ret = _db_open(&db, fr, _CITRUS_ESDB_MAGIC, &_db_hash_std, NULL); if (ret) goto err0; /* check version */ ret = _db_lookup32_by_s(db, _CITRUS_ESDB_SYM_VERSION, &version, NULL); if (ret) goto err1; switch (version) { case 0x00000001: /* current version */ /* initial version */ break; default: ret = EFTYPE; goto err1; } /* get encoding/variable */ ret = _db_lookupstr_by_s(db, _CITRUS_ESDB_SYM_ENCODING, &str, NULL); if (ret) goto err1; esdb->db_encname = strdup(str); if (esdb->db_encname == NULL) { ret = errno; goto err1; } esdb->db_len_variable = 0; esdb->db_variable = NULL; ret = _db_lookupstr_by_s(db, _CITRUS_ESDB_SYM_VARIABLE, &str, NULL); if (ret == 0) { esdb->db_len_variable = strlen(str) + 1; esdb->db_variable = strdup(str); if (esdb->db_variable == NULL) { ret = errno; goto err2; } } else if (ret != ENOENT) goto err2; /* get number of charsets */ ret = _db_lookup32_by_s(db, _CITRUS_ESDB_SYM_NUM_CHARSETS, &num_charsets, NULL); if (ret) goto err3; esdb->db_num_charsets = num_charsets; /* get invalid character */ ret = _db_lookup32_by_s(db, _CITRUS_ESDB_SYM_INVALID, &tmp, NULL); if (ret == 0) { esdb->db_use_invalid = 1; esdb->db_invalid = tmp; } else if (ret == ENOENT) esdb->db_use_invalid = 0; else goto err3; /* get charsets */ esdb->db_charsets = malloc(num_charsets * sizeof(*esdb->db_charsets)); if (esdb->db_charsets == NULL) { ret = errno; goto err3; } for (i = 0; i < num_charsets; i++) { snprintf(buf, sizeof(buf), _CITRUS_ESDB_SYM_CSID_PREFIX "%d", i); ret = _db_lookup32_by_s(db, buf, &csid, NULL); if (ret) goto err4; esdb->db_charsets[i].ec_csid = csid; snprintf(buf, sizeof(buf), _CITRUS_ESDB_SYM_CSNAME_PREFIX "%d", i); ret = _db_lookupstr_by_s(db, buf, &str, NULL); if (ret) goto err4; esdb->db_charsets[i].ec_csname = strdup(str); if (esdb->db_charsets[i].ec_csname == NULL) { ret = errno; goto err4; } } _db_close(db); return (0); err4: for (; i > 0; i--) free(esdb->db_charsets[i - 1].ec_csname); free(esdb->db_charsets); err3: free(esdb->db_variable); err2: free(esdb->db_encname); err1: _db_close(db); if (ret == ENOENT) ret = EFTYPE; err0: return (ret); } /* * _citrus_esdb_open: * open an ESDB file. */ int _citrus_esdb_open(struct _citrus_esdb *db, const char *esname) { struct _region fr; const char *realname, *encfile; char buf1[PATH_MAX], buf2[PATH_MAX], path[PATH_MAX]; int ret; snprintf(path, sizeof(path), "%s/%s", _PATH_ESDB, ESDB_ALIAS); realname = _lookup_alias(path, esname, buf1, sizeof(buf1), _LOOKUP_CASE_IGNORE); snprintf(path, sizeof(path), "%s/%s", _PATH_ESDB, ESDB_DIR); encfile = _lookup_simple(path, realname, buf2, sizeof(buf2), _LOOKUP_CASE_IGNORE); if (encfile == NULL) return (ENOENT); /* open file */ snprintf(path, sizeof(path), "%s/%s", _PATH_ESDB, encfile); ret = _map_file(&fr, path); if (ret) return (ret); ret = conv_esdb(db, &fr); _unmap_file(&fr); return (ret); } /* * _citrus_esdb_close: * free an ESDB. */ void _citrus_esdb_close(struct _citrus_esdb *db) { for (int i = 0; i < db->db_num_charsets; i++) free(db->db_charsets[i].ec_csname); db->db_num_charsets = 0; free(db->db_charsets); db->db_charsets = NULL; free(db->db_encname); db->db_encname = NULL; db->db_len_variable = 0; free(db->db_variable); db->db_variable = NULL; } /* * _citrus_esdb_free_list: * free the list. */ void _citrus_esdb_free_list(char **list, size_t num) { for (size_t i = 0; i < num; i++) free(list[i]); free(list); } /* * _citrus_esdb_get_list: * get esdb entries. */ int _citrus_esdb_get_list(char ***rlist, size_t *rnum, bool sorted) { struct _citrus_lookup *cla, *cld; struct _region key, data; char **list, **q; char buf[PATH_MAX]; size_t num; int ret; ret = _lookup_seq_open(&cla, _PATH_ESDB "/" ESDB_ALIAS, _LOOKUP_CASE_IGNORE); if (ret) goto quit0; ret = _lookup_seq_open(&cld, _PATH_ESDB "/" ESDB_DIR, _LOOKUP_CASE_IGNORE); if (ret) goto quit1; /* count number of entries */ num = _lookup_get_num_entries(cla) + _lookup_get_num_entries(cld); _lookup_seq_rewind(cla); _lookup_seq_rewind(cld); /* allocate list pointer space */ list = malloc(num * sizeof(char *)); num = 0; if (list == NULL) { ret = errno; goto quit3; } /* get alias entries */ while ((ret = _lookup_seq_next(cla, &key, &data)) == 0) { /* XXX: sorted? */ snprintf(buf, sizeof(buf), "%.*s/%.*s", (int)_region_size(&data), (const char *)_region_head(&data), (int)_region_size(&key), (const char *)_region_head(&key)); _bcs_convert_to_upper(buf); list[num] = strdup(buf); if (list[num] == NULL) { ret = errno; goto quit3; } num++; } if (ret != ENOENT) goto quit3; /* get dir entries */ while ((ret = _lookup_seq_next(cld, &key, &data)) == 0) { if (!sorted) snprintf(buf, sizeof(buf), "%.*s", (int)_region_size(&key), (const char *)_region_head(&key)); else { /* check duplicated entry */ char *p; char buf1[PATH_MAX]; snprintf(buf1, sizeof(buf1), "%.*s", (int)_region_size(&data), (const char *)_region_head(&data)); if ((p = strchr(buf1, '/')) != NULL) memmove(buf1, p + 1, strlen(p) - 1); if ((p = strstr(buf1, ".esdb")) != NULL) *p = '\0'; snprintf(buf, sizeof(buf), "%s/%.*s", buf1, (int)_region_size(&key), (const char *)_region_head(&key)); } _bcs_convert_to_upper(buf); ret = _lookup_seq_lookup(cla, buf, NULL); if (ret) { if (ret != ENOENT) goto quit3; /* not duplicated */ list[num] = strdup(buf); if (list[num] == NULL) { ret = errno; goto quit3; } num++; } } if (ret != ENOENT) goto quit3; ret = 0; /* XXX: why reallocing the list space posteriorly? shouldn't be done earlier? */ - q = realloc(list, num * sizeof(char *)); + q = reallocarray(list, num, sizeof(char *)); if (!q) { ret = ENOMEM; goto quit3; } list = q; *rlist = list; *rnum = num; quit3: if (ret) _citrus_esdb_free_list(list, num); _lookup_seq_close(cld); quit1: _lookup_seq_close(cla); quit0: return (ret); } Index: head/lib/libc/net/nsdispatch.c =================================================================== --- head/lib/libc/net/nsdispatch.c (revision 315161) +++ head/lib/libc/net/nsdispatch.c (revision 315162) @@ -1,763 +1,763 @@ /* $NetBSD: nsdispatch.c,v 1.9 1999/01/25 00:16:17 lukem Exp $ */ /*- * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Luke Mewburn. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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) 2003 Networks Associates Technology, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * Jacques A. Vidrine, Safeport Network Services, and Network * Associates Laboratories, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #define _NS_PRIVATE #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "nss_tls.h" #include "libc_private.h" #ifdef NS_CACHING #include "nscache.h" #endif enum _nss_constants { /* Number of elements allocated when we grow a vector */ ELEMSPERCHUNK = 8 }; /* * Global NSS data structures are mostly read-only, but we update * them when we read or re-read the nsswitch.conf. */ static pthread_rwlock_t nss_lock = PTHREAD_RWLOCK_INITIALIZER; /* * Runtime determination of whether we are dynamically linked or not. */ extern int _DYNAMIC __attribute__ ((weak)); #define is_dynamic() (&_DYNAMIC != NULL) /* * default sourcelist: `files' */ const ns_src __nsdefaultsrc[] = { { NSSRC_FILES, NS_SUCCESS }, { 0 }, }; /* Database, source mappings. */ static unsigned int _nsmapsize; static ns_dbt *_nsmap = NULL; /* NSS modules. */ static unsigned int _nsmodsize; static ns_mod *_nsmod; /* Placeholder for builtin modules' dlopen `handle'. */ static int __nss_builtin_handle; static void *nss_builtin_handle = &__nss_builtin_handle; #ifdef NS_CACHING /* * Cache lookup cycle prevention function - if !NULL then no cache lookups * will be made */ static void *nss_cache_cycle_prevention_func = NULL; #endif /* * We keep track of nsdispatch() nesting depth in dispatch_depth. When a * fallback method is invoked from nsdispatch(), we temporarily set * fallback_depth to the current dispatch depth plus one. Subsequent * calls at that exact depth will run in fallback mode (restricted to the * same source as the call that was handled by the fallback method), while * calls below that depth will be handled normally, allowing fallback * methods to perform arbitrary lookups. */ struct fb_state { int dispatch_depth; int fallback_depth; }; static void fb_endstate(void *); NSS_TLS_HANDLING(fb); /* * Attempt to spew relatively uniform messages to syslog. */ #define nss_log(level, fmt, ...) \ syslog((level), "NSSWITCH(%s): " fmt, __func__, __VA_ARGS__) #define nss_log_simple(level, s) \ syslog((level), "NSSWITCH(%s): " s, __func__) /* * Dynamically growable arrays are used for lists of databases, sources, * and modules. The following `vector' interface is used to isolate the * common operations. */ typedef int (*vector_comparison)(const void *, const void *); typedef void (*vector_free_elem)(void *); static void vector_sort(void *, unsigned int, size_t, vector_comparison); static void vector_free(void *, unsigned int *, size_t, vector_free_elem); static void *vector_ref(unsigned int, void *, unsigned int, size_t); static void *vector_search(const void *, void *, unsigned int, size_t, vector_comparison); static void *vector_append(const void *, void *, unsigned int *, size_t); /* * Internal interfaces. */ static int string_compare(const void *, const void *); static int mtab_compare(const void *, const void *); static int nss_configure(void); static void ns_dbt_free(ns_dbt *); static void ns_mod_free(ns_mod *); static void ns_src_free(ns_src **, int); static void nss_load_builtin_modules(void); static void nss_load_module(const char *, nss_module_register_fn); static void nss_atexit(void); /* nsparser */ extern FILE *_nsyyin; /* * The vector operations */ static void vector_sort(void *vec, unsigned int count, size_t esize, vector_comparison comparison) { qsort(vec, count, esize, comparison); } static void * vector_search(const void *key, void *vec, unsigned int count, size_t esize, vector_comparison comparison) { return (bsearch(key, vec, count, esize, comparison)); } static void * vector_append(const void *elem, void *vec, unsigned int *count, size_t esize) { void *p; if ((*count % ELEMSPERCHUNK) == 0) { - p = realloc(vec, (*count + ELEMSPERCHUNK) * esize); + p = reallocarray(vec, *count + ELEMSPERCHUNK, esize); if (p == NULL) { nss_log_simple(LOG_ERR, "memory allocation failure"); return (vec); } vec = p; } memmove((void *)(((uintptr_t)vec) + (*count * esize)), elem, esize); (*count)++; return (vec); } static void * vector_ref(unsigned int i, void *vec, unsigned int count, size_t esize) { if (i < count) return (void *)((uintptr_t)vec + (i * esize)); else return (NULL); } #define VECTOR_FREE(v, c, s, f) \ do { vector_free(v, c, s, f); v = NULL; } while (0) static void vector_free(void *vec, unsigned int *count, size_t esize, vector_free_elem free_elem) { unsigned int i; void *elem; for (i = 0; i < *count; i++) { elem = vector_ref(i, vec, *count, esize); if (elem != NULL) free_elem(elem); } free(vec); *count = 0; } /* * Comparison functions for vector_search. */ static int string_compare(const void *a, const void *b) { return (strcasecmp(*(const char * const *)a, *(const char * const *)b)); } static int mtab_compare(const void *a, const void *b) { int cmp; cmp = strcmp(((const ns_mtab *)a)->name, ((const ns_mtab *)b)->name); if (cmp != 0) return (cmp); else return (strcmp(((const ns_mtab *)a)->database, ((const ns_mtab *)b)->database)); } /* * NSS nsmap management. */ void _nsdbtaddsrc(ns_dbt *dbt, const ns_src *src) { const ns_mod *modp; dbt->srclist = vector_append(src, dbt->srclist, &dbt->srclistsize, sizeof(*src)); modp = vector_search(&src->name, _nsmod, _nsmodsize, sizeof(*_nsmod), string_compare); if (modp == NULL) nss_load_module(src->name, NULL); } #ifdef _NSS_DEBUG void _nsdbtdump(const ns_dbt *dbt) { int i; printf("%s (%d source%s):", dbt->name, dbt->srclistsize, dbt->srclistsize == 1 ? "" : "s"); for (i = 0; i < (int)dbt->srclistsize; i++) { printf(" %s", dbt->srclist[i].name); if (!(dbt->srclist[i].flags & (NS_UNAVAIL|NS_NOTFOUND|NS_TRYAGAIN)) && (dbt->srclist[i].flags & NS_SUCCESS)) continue; printf(" ["); if (!(dbt->srclist[i].flags & NS_SUCCESS)) printf(" SUCCESS=continue"); if (dbt->srclist[i].flags & NS_UNAVAIL) printf(" UNAVAIL=return"); if (dbt->srclist[i].flags & NS_NOTFOUND) printf(" NOTFOUND=return"); if (dbt->srclist[i].flags & NS_TRYAGAIN) printf(" TRYAGAIN=return"); printf(" ]"); } printf("\n"); } #endif /* * The first time nsdispatch is called (during a process's lifetime, * or after nsswitch.conf has been updated), nss_configure will * prepare global data needed by NSS. */ static int nss_configure(void) { static time_t confmod; struct stat statbuf; int result, isthreaded; const char *path; #ifdef NS_CACHING void *handle; #endif result = 0; isthreaded = __isthreaded; #if defined(_NSS_DEBUG) && defined(_NSS_SHOOT_FOOT) /* NOTE WELL: THIS IS A SECURITY HOLE. This must only be built * for debugging purposes and MUST NEVER be used in production. */ path = getenv("NSSWITCH_CONF"); if (path == NULL) #endif path = _PATH_NS_CONF; if (stat(path, &statbuf) != 0) return (0); if (statbuf.st_mtime <= confmod) return (0); if (isthreaded) { (void)_pthread_rwlock_unlock(&nss_lock); result = _pthread_rwlock_wrlock(&nss_lock); if (result != 0) return (result); if (stat(path, &statbuf) != 0) goto fin; if (statbuf.st_mtime <= confmod) goto fin; } _nsyyin = fopen(path, "re"); if (_nsyyin == NULL) goto fin; VECTOR_FREE(_nsmap, &_nsmapsize, sizeof(*_nsmap), (vector_free_elem)ns_dbt_free); VECTOR_FREE(_nsmod, &_nsmodsize, sizeof(*_nsmod), (vector_free_elem)ns_mod_free); if (confmod == 0) (void)atexit(nss_atexit); nss_load_builtin_modules(); _nsyyparse(); (void)fclose(_nsyyin); vector_sort(_nsmap, _nsmapsize, sizeof(*_nsmap), string_compare); confmod = statbuf.st_mtime; #ifdef NS_CACHING handle = libc_dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL); if (handle != NULL) { nss_cache_cycle_prevention_func = dlsym(handle, "_nss_cache_cycle_prevention_function"); dlclose(handle); } #endif fin: if (isthreaded) { (void)_pthread_rwlock_unlock(&nss_lock); if (result == 0) result = _pthread_rwlock_rdlock(&nss_lock); } return (result); } void _nsdbtput(const ns_dbt *dbt) { unsigned int i; ns_dbt *p; for (i = 0; i < _nsmapsize; i++) { p = vector_ref(i, _nsmap, _nsmapsize, sizeof(*_nsmap)); if (string_compare(&dbt->name, &p->name) == 0) { /* overwrite existing entry */ if (p->srclist != NULL) ns_src_free(&p->srclist, p->srclistsize); memmove(p, dbt, sizeof(*dbt)); return; } } _nsmap = vector_append(dbt, _nsmap, &_nsmapsize, sizeof(*_nsmap)); } static void ns_dbt_free(ns_dbt *dbt) { ns_src_free(&dbt->srclist, dbt->srclistsize); if (dbt->name) free((void *)dbt->name); } static void ns_src_free(ns_src **src, int srclistsize) { int i; for (i = 0; i < srclistsize; i++) if ((*src)[i].name != NULL) /* This one was allocated by nslexer. You'll just * have to trust me. */ free((void *)((*src)[i].name)); free(*src); *src = NULL; } /* * NSS module management. */ /* The built-in NSS modules are all loaded at once. */ #define NSS_BACKEND(name, reg) \ ns_mtab *reg(unsigned int *, nss_module_unregister_fn *); #include "nss_backends.h" #undef NSS_BACKEND static void nss_load_builtin_modules(void) { #define NSS_BACKEND(name, reg) nss_load_module(#name, reg); #include "nss_backends.h" #undef NSS_BACKEND } /* Load a built-in or dynamically linked module. If the `reg_fn' * argument is non-NULL, assume a built-in module and use reg_fn to * register it. Otherwise, search for a dynamic NSS module. */ static void nss_load_module(const char *source, nss_module_register_fn reg_fn) { char buf[PATH_MAX]; ns_mod mod; nss_module_register_fn fn; memset(&mod, 0, sizeof(mod)); mod.name = strdup(source); if (mod.name == NULL) { nss_log_simple(LOG_ERR, "memory allocation failure"); return; } if (reg_fn != NULL) { /* The placeholder is required, as a NULL handle * represents an invalid module. */ mod.handle = nss_builtin_handle; fn = reg_fn; } else if (!is_dynamic()) goto fin; else { if (snprintf(buf, sizeof(buf), "nss_%s.so.%d", mod.name, NSS_MODULE_INTERFACE_VERSION) >= (int)sizeof(buf)) goto fin; mod.handle = libc_dlopen(buf, RTLD_LOCAL|RTLD_LAZY); if (mod.handle == NULL) { #ifdef _NSS_DEBUG /* This gets pretty annoying since the built-in * sources aren't modules yet. */ nss_log(LOG_DEBUG, "%s, %s", mod.name, dlerror()); #endif goto fin; } fn = (nss_module_register_fn)dlfunc(mod.handle, "nss_module_register"); if (fn == NULL) { (void)dlclose(mod.handle); mod.handle = NULL; nss_log(LOG_ERR, "%s, %s", mod.name, dlerror()); goto fin; } } mod.mtab = fn(mod.name, &mod.mtabsize, &mod.unregister); if (mod.mtab == NULL || mod.mtabsize == 0) { if (mod.handle != nss_builtin_handle) (void)dlclose(mod.handle); mod.handle = NULL; nss_log(LOG_ERR, "%s, registration failed", mod.name); goto fin; } if (mod.mtabsize > 1) qsort(mod.mtab, mod.mtabsize, sizeof(mod.mtab[0]), mtab_compare); fin: _nsmod = vector_append(&mod, _nsmod, &_nsmodsize, sizeof(*_nsmod)); vector_sort(_nsmod, _nsmodsize, sizeof(*_nsmod), string_compare); } static void ns_mod_free(ns_mod *mod) { free(mod->name); if (mod->handle == NULL) return; if (mod->unregister != NULL) mod->unregister(mod->mtab, mod->mtabsize); if (mod->handle != nss_builtin_handle) (void)dlclose(mod->handle); } /* * Cleanup */ static void nss_atexit(void) { int isthreaded; isthreaded = __isthreaded; if (isthreaded) (void)_pthread_rwlock_wrlock(&nss_lock); VECTOR_FREE(_nsmap, &_nsmapsize, sizeof(*_nsmap), (vector_free_elem)ns_dbt_free); VECTOR_FREE(_nsmod, &_nsmodsize, sizeof(*_nsmod), (vector_free_elem)ns_mod_free); if (isthreaded) (void)_pthread_rwlock_unlock(&nss_lock); } /* * Finally, the actual implementation. */ static nss_method nss_method_lookup(const char *source, const char *database, const char *method, const ns_dtab disp_tab[], void **mdata) { ns_mod *mod; ns_mtab *match, key; int i; if (disp_tab != NULL) for (i = 0; disp_tab[i].src != NULL; i++) if (strcasecmp(source, disp_tab[i].src) == 0) { *mdata = disp_tab[i].mdata; return (disp_tab[i].method); } mod = vector_search(&source, _nsmod, _nsmodsize, sizeof(*_nsmod), string_compare); if (mod != NULL && mod->handle != NULL) { key.database = database; key.name = method; match = bsearch(&key, mod->mtab, mod->mtabsize, sizeof(mod->mtab[0]), mtab_compare); if (match != NULL) { *mdata = match->mdata; return (match->method); } } *mdata = NULL; return (NULL); } static void fb_endstate(void *p) { free(p); } __weak_reference(_nsdispatch, nsdispatch); int _nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, const char *method_name, const ns_src defaults[], ...) { va_list ap; const ns_dbt *dbt; const ns_src *srclist; nss_method method, fb_method; void *mdata; int isthreaded, serrno, i, result, srclistsize; struct fb_state *st; int saved_depth; #ifdef NS_CACHING nss_cache_data cache_data; nss_cache_data *cache_data_p; int cache_flag; #endif dbt = NULL; fb_method = NULL; isthreaded = __isthreaded; serrno = errno; if (isthreaded) { result = _pthread_rwlock_rdlock(&nss_lock); if (result != 0) { result = NS_UNAVAIL; goto fin; } } result = fb_getstate(&st); if (result != 0) { result = NS_UNAVAIL; goto fin; } result = nss_configure(); if (result != 0) { result = NS_UNAVAIL; goto fin; } ++st->dispatch_depth; if (st->dispatch_depth > st->fallback_depth) { dbt = vector_search(&database, _nsmap, _nsmapsize, sizeof(*_nsmap), string_compare); fb_method = nss_method_lookup(NSSRC_FALLBACK, database, method_name, disp_tab, &mdata); } if (dbt != NULL) { srclist = dbt->srclist; srclistsize = dbt->srclistsize; } else { srclist = defaults; srclistsize = 0; while (srclist[srclistsize].name != NULL) srclistsize++; } #ifdef NS_CACHING cache_data_p = NULL; cache_flag = 0; #endif for (i = 0; i < srclistsize; i++) { result = NS_NOTFOUND; method = nss_method_lookup(srclist[i].name, database, method_name, disp_tab, &mdata); if (method != NULL) { #ifdef NS_CACHING if (strcmp(srclist[i].name, NSSRC_CACHE) == 0 && nss_cache_cycle_prevention_func == NULL) { #ifdef NS_STRICT_LIBC_EID_CHECKING if (issetugid() != 0) continue; #endif cache_flag = 1; memset(&cache_data, 0, sizeof(nss_cache_data)); cache_data.info = (nss_cache_info const *)mdata; cache_data_p = &cache_data; va_start(ap, defaults); if (cache_data.info->id_func != NULL) result = __nss_common_cache_read(retval, cache_data_p, ap); else if (cache_data.info->marshal_func != NULL) result = __nss_mp_cache_read(retval, cache_data_p, ap); else result = __nss_mp_cache_end(retval, cache_data_p, ap); va_end(ap); } else { cache_flag = 0; errno = 0; va_start(ap, defaults); result = method(retval, mdata, ap); va_end(ap); } #else /* NS_CACHING */ errno = 0; va_start(ap, defaults); result = method(retval, mdata, ap); va_end(ap); #endif /* NS_CACHING */ if (result & (srclist[i].flags)) break; } else { if (fb_method != NULL) { saved_depth = st->fallback_depth; st->fallback_depth = st->dispatch_depth + 1; va_start(ap, defaults); result = fb_method(retval, (void *)srclist[i].name, ap); va_end(ap); st->fallback_depth = saved_depth; } else nss_log(LOG_DEBUG, "%s, %s, %s, not found, " "and no fallback provided", srclist[i].name, database, method_name); } } #ifdef NS_CACHING if (cache_data_p != NULL && (result & (NS_NOTFOUND | NS_SUCCESS)) && cache_flag == 0) { va_start(ap, defaults); if (result == NS_SUCCESS) { if (cache_data.info->id_func != NULL) __nss_common_cache_write(retval, cache_data_p, ap); else if (cache_data.info->marshal_func != NULL) __nss_mp_cache_write(retval, cache_data_p, ap); } else if (result == NS_NOTFOUND) { if (cache_data.info->id_func == NULL) { if (cache_data.info->marshal_func != NULL) __nss_mp_cache_write_submit(retval, cache_data_p, ap); } else __nss_common_cache_write_negative(cache_data_p); } va_end(ap); } #endif /* NS_CACHING */ if (isthreaded) (void)_pthread_rwlock_unlock(&nss_lock); --st->dispatch_depth; fin: errno = serrno; return (result); } Index: head/lib/libc/regex/regcomp.c =================================================================== --- head/lib/libc/regex/regcomp.c (revision 315161) +++ head/lib/libc/regex/regcomp.c (revision 315162) @@ -1,1817 +1,1817 @@ /*- * Copyright (c) 1992, 1993, 1994 Henry Spencer. * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Copyright (c) 2011 The FreeBSD Foundation * All rights reserved. * Portions of this software were developed by David Chisnall * under sponsorship from the FreeBSD Foundation. * * This code is derived from software contributed to Berkeley by * Henry Spencer. * * 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. * * @(#)regcomp.c 8.5 (Berkeley) 3/20/94 */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)regcomp.c 8.5 (Berkeley) 3/20/94"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "collate.h" #include "utils.h" #include "regex2.h" #include "cname.h" /* * parse structure, passed up and down to avoid global variables and * other clumsinesses */ struct parse { char *next; /* next character in RE */ char *end; /* end of string (-> NUL normally) */ int error; /* has an error been seen? */ sop *strip; /* malloced strip */ sopno ssize; /* malloced strip size (allocated) */ sopno slen; /* malloced strip length (used) */ int ncsalloc; /* number of csets allocated */ struct re_guts *g; # define NPAREN 10 /* we need to remember () 1-9 for back refs */ sopno pbegin[NPAREN]; /* -> ( ([0] unused) */ sopno pend[NPAREN]; /* -> ) ([0] unused) */ }; /* ========= begin header generated by ./mkh ========= */ #ifdef __cplusplus extern "C" { #endif /* === regcomp.c === */ static void p_ere(struct parse *p, int stop); static void p_ere_exp(struct parse *p); static void p_str(struct parse *p); static void p_bre(struct parse *p, int end1, int end2); static int p_simp_re(struct parse *p, int starordinary); static int p_count(struct parse *p); static void p_bracket(struct parse *p); static void p_b_term(struct parse *p, cset *cs); static void p_b_cclass(struct parse *p, cset *cs); static void p_b_eclass(struct parse *p, cset *cs); static wint_t p_b_symbol(struct parse *p); static wint_t p_b_coll_elem(struct parse *p, wint_t endc); static wint_t othercase(wint_t ch); static void bothcases(struct parse *p, wint_t ch); static void ordinary(struct parse *p, wint_t ch); static void nonnewline(struct parse *p); static void repeat(struct parse *p, sopno start, int from, int to); static int seterr(struct parse *p, int e); static cset *allocset(struct parse *p); static void freeset(struct parse *p, cset *cs); static void CHadd(struct parse *p, cset *cs, wint_t ch); static void CHaddrange(struct parse *p, cset *cs, wint_t min, wint_t max); static void CHaddtype(struct parse *p, cset *cs, wctype_t wct); static wint_t singleton(cset *cs); static sopno dupl(struct parse *p, sopno start, sopno finish); static void doemit(struct parse *p, sop op, size_t opnd); static void doinsert(struct parse *p, sop op, size_t opnd, sopno pos); static void dofwd(struct parse *p, sopno pos, sop value); static int enlarge(struct parse *p, sopno size); static void stripsnug(struct parse *p, struct re_guts *g); static void findmust(struct parse *p, struct re_guts *g); static int altoffset(sop *scan, int offset); static void computejumps(struct parse *p, struct re_guts *g); static void computematchjumps(struct parse *p, struct re_guts *g); static sopno pluscount(struct parse *p, struct re_guts *g); static wint_t wgetnext(struct parse *p); #ifdef __cplusplus } #endif /* ========= end header generated by ./mkh ========= */ static char nuls[10]; /* place to point scanner in event of error */ /* * macros for use with parse structure * BEWARE: these know that the parse structure is named `p' !!! */ #define PEEK() (*p->next) #define PEEK2() (*(p->next+1)) #define MORE() (p->next < p->end) #define MORE2() (p->next+1 < p->end) #define SEE(c) (MORE() && PEEK() == (c)) #define SEETWO(a, b) (MORE() && MORE2() && PEEK() == (a) && PEEK2() == (b)) #define EAT(c) ((SEE(c)) ? (NEXT(), 1) : 0) #define EATTWO(a, b) ((SEETWO(a, b)) ? (NEXT2(), 1) : 0) #define NEXT() (p->next++) #define NEXT2() (p->next += 2) #define NEXTn(n) (p->next += (n)) #define GETNEXT() (*p->next++) #define WGETNEXT() wgetnext(p) #define SETERROR(e) seterr(p, (e)) #define REQUIRE(co, e) ((co) || SETERROR(e)) #define MUSTSEE(c, e) (REQUIRE(MORE() && PEEK() == (c), e)) #define MUSTEAT(c, e) (REQUIRE(MORE() && GETNEXT() == (c), e)) #define MUSTNOTSEE(c, e) (REQUIRE(!MORE() || PEEK() != (c), e)) #define EMIT(op, sopnd) doemit(p, (sop)(op), (size_t)(sopnd)) #define INSERT(op, pos) doinsert(p, (sop)(op), HERE()-(pos)+1, pos) #define AHEAD(pos) dofwd(p, pos, HERE()-(pos)) #define ASTERN(sop, pos) EMIT(sop, HERE()-pos) #define HERE() (p->slen) #define THERE() (p->slen - 1) #define THERETHERE() (p->slen - 2) #define DROP(n) (p->slen -= (n)) #ifndef NDEBUG static int never = 0; /* for use in asserts; shuts lint up */ #else #define never 0 /* some s have bugs too */ #endif /* Macro used by computejump()/computematchjump() */ #define MIN(a,b) ((a)<(b)?(a):(b)) /* - regcomp - interface for parser and compilation = extern int regcomp(regex_t *, const char *, int); = #define REG_BASIC 0000 = #define REG_EXTENDED 0001 = #define REG_ICASE 0002 = #define REG_NOSUB 0004 = #define REG_NEWLINE 0010 = #define REG_NOSPEC 0020 = #define REG_PEND 0040 = #define REG_DUMP 0200 */ int /* 0 success, otherwise REG_something */ regcomp(regex_t * __restrict preg, const char * __restrict pattern, int cflags) { struct parse pa; struct re_guts *g; struct parse *p = &pa; int i; size_t len; size_t maxlen; #ifdef REDEBUG # define GOODFLAGS(f) (f) #else # define GOODFLAGS(f) ((f)&~REG_DUMP) #endif cflags = GOODFLAGS(cflags); if ((cflags®_EXTENDED) && (cflags®_NOSPEC)) return(REG_INVARG); if (cflags®_PEND) { if (preg->re_endp < pattern) return(REG_INVARG); len = preg->re_endp - pattern; } else len = strlen((char *)pattern); /* do the mallocs early so failure handling is easy */ g = (struct re_guts *)malloc(sizeof(struct re_guts)); if (g == NULL) return(REG_ESPACE); /* * Limit the pattern space to avoid a 32-bit overflow on buffer * extension. Also avoid any signed overflow in case of conversion * so make the real limit based on a 31-bit overflow. * * Likely not applicable on 64-bit systems but handle the case * generically (who are we to stop people from using ~715MB+ * patterns?). */ maxlen = ((size_t)-1 >> 1) / sizeof(sop) * 2 / 3; if (len >= maxlen) { free((char *)g); return(REG_ESPACE); } p->ssize = len/(size_t)2*(size_t)3 + (size_t)1; /* ugh */ assert(p->ssize >= len); p->strip = (sop *)malloc(p->ssize * sizeof(sop)); p->slen = 0; if (p->strip == NULL) { free((char *)g); return(REG_ESPACE); } /* set things up */ p->g = g; p->next = (char *)pattern; /* convenience; we do not modify it */ p->end = p->next + len; p->error = 0; p->ncsalloc = 0; for (i = 0; i < NPAREN; i++) { p->pbegin[i] = 0; p->pend[i] = 0; } g->sets = NULL; g->ncsets = 0; g->cflags = cflags; g->iflags = 0; g->nbol = 0; g->neol = 0; g->must = NULL; g->moffset = -1; g->charjump = NULL; g->matchjump = NULL; g->mlen = 0; g->nsub = 0; g->backrefs = 0; /* do it */ EMIT(OEND, 0); g->firststate = THERE(); if (cflags®_EXTENDED) p_ere(p, OUT); else if (cflags®_NOSPEC) p_str(p); else p_bre(p, OUT, OUT); EMIT(OEND, 0); g->laststate = THERE(); /* tidy up loose ends and fill things in */ stripsnug(p, g); findmust(p, g); /* only use Boyer-Moore algorithm if the pattern is bigger * than three characters */ if(g->mlen > 3) { computejumps(p, g); computematchjumps(p, g); if(g->matchjump == NULL && g->charjump != NULL) { free(g->charjump); g->charjump = NULL; } } g->nplus = pluscount(p, g); g->magic = MAGIC2; preg->re_nsub = g->nsub; preg->re_g = g; preg->re_magic = MAGIC1; #ifndef REDEBUG /* not debugging, so can't rely on the assert() in regexec() */ if (g->iflags&BAD) SETERROR(REG_ASSERT); #endif /* win or lose, we're done */ if (p->error != 0) /* lose */ regfree(preg); return(p->error); } /* - p_ere - ERE parser top level, concatenation and alternation == static void p_ere(struct parse *p, int_t stop); */ static void p_ere(struct parse *p, int stop) /* character this ERE should end at */ { char c; sopno prevback; sopno prevfwd; sopno conc; int first = 1; /* is this the first alternative? */ for (;;) { /* do a bunch of concatenated expressions */ conc = HERE(); while (MORE() && (c = PEEK()) != '|' && c != stop) p_ere_exp(p); (void)REQUIRE(HERE() != conc, REG_EMPTY); /* require nonempty */ if (!EAT('|')) break; /* NOTE BREAK OUT */ if (first) { INSERT(OCH_, conc); /* offset is wrong */ prevfwd = conc; prevback = conc; first = 0; } ASTERN(OOR1, prevback); prevback = THERE(); AHEAD(prevfwd); /* fix previous offset */ prevfwd = HERE(); EMIT(OOR2, 0); /* offset is very wrong */ } if (!first) { /* tail-end fixups */ AHEAD(prevfwd); ASTERN(O_CH, prevback); } assert(!MORE() || SEE(stop)); } /* - p_ere_exp - parse one subERE, an atom possibly followed by a repetition op == static void p_ere_exp(struct parse *p); */ static void p_ere_exp(struct parse *p) { char c; wint_t wc; sopno pos; int count; int count2; sopno subno; int wascaret = 0; assert(MORE()); /* caller should have ensured this */ c = GETNEXT(); pos = HERE(); switch (c) { case '(': (void)REQUIRE(MORE(), REG_EPAREN); p->g->nsub++; subno = p->g->nsub; if (subno < NPAREN) p->pbegin[subno] = HERE(); EMIT(OLPAREN, subno); if (!SEE(')')) p_ere(p, ')'); if (subno < NPAREN) { p->pend[subno] = HERE(); assert(p->pend[subno] != 0); } EMIT(ORPAREN, subno); (void)MUSTEAT(')', REG_EPAREN); break; #ifndef POSIX_MISTAKE case ')': /* happens only if no current unmatched ( */ /* * You may ask, why the ifndef? Because I didn't notice * this until slightly too late for 1003.2, and none of the * other 1003.2 regular-expression reviewers noticed it at * all. So an unmatched ) is legal POSIX, at least until * we can get it fixed. */ SETERROR(REG_EPAREN); break; #endif case '^': EMIT(OBOL, 0); p->g->iflags |= USEBOL; p->g->nbol++; wascaret = 1; break; case '$': EMIT(OEOL, 0); p->g->iflags |= USEEOL; p->g->neol++; break; case '|': SETERROR(REG_EMPTY); break; case '*': case '+': case '?': SETERROR(REG_BADRPT); break; case '.': if (p->g->cflags®_NEWLINE) nonnewline(p); else EMIT(OANY, 0); break; case '[': p_bracket(p); break; case '\\': (void)REQUIRE(MORE(), REG_EESCAPE); wc = WGETNEXT(); switch (wc) { case '<': EMIT(OBOW, 0); break; case '>': EMIT(OEOW, 0); break; default: ordinary(p, wc); break; } break; case '{': /* okay as ordinary except if digit follows */ (void)REQUIRE(!MORE() || !isdigit((uch)PEEK()), REG_BADRPT); /* FALLTHROUGH */ default: p->next--; wc = WGETNEXT(); ordinary(p, wc); break; } if (!MORE()) return; c = PEEK(); /* we call { a repetition if followed by a digit */ if (!( c == '*' || c == '+' || c == '?' || (c == '{' && MORE2() && isdigit((uch)PEEK2())) )) return; /* no repetition, we're done */ NEXT(); (void)REQUIRE(!wascaret, REG_BADRPT); switch (c) { case '*': /* implemented as +? */ /* this case does not require the (y|) trick, noKLUDGE */ INSERT(OPLUS_, pos); ASTERN(O_PLUS, pos); INSERT(OQUEST_, pos); ASTERN(O_QUEST, pos); break; case '+': INSERT(OPLUS_, pos); ASTERN(O_PLUS, pos); break; case '?': /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ INSERT(OCH_, pos); /* offset slightly wrong */ ASTERN(OOR1, pos); /* this one's right */ AHEAD(pos); /* fix the OCH_ */ EMIT(OOR2, 0); /* offset very wrong... */ AHEAD(THERE()); /* ...so fix it */ ASTERN(O_CH, THERETHERE()); break; case '{': count = p_count(p); if (EAT(',')) { if (isdigit((uch)PEEK())) { count2 = p_count(p); (void)REQUIRE(count <= count2, REG_BADBR); } else /* single number with comma */ count2 = INFINITY; } else /* just a single number */ count2 = count; repeat(p, pos, count, count2); if (!EAT('}')) { /* error heuristics */ while (MORE() && PEEK() != '}') NEXT(); (void)REQUIRE(MORE(), REG_EBRACE); SETERROR(REG_BADBR); } break; } if (!MORE()) return; c = PEEK(); if (!( c == '*' || c == '+' || c == '?' || (c == '{' && MORE2() && isdigit((uch)PEEK2())) ) ) return; SETERROR(REG_BADRPT); } /* - p_str - string (no metacharacters) "parser" == static void p_str(struct parse *p); */ static void p_str(struct parse *p) { (void)REQUIRE(MORE(), REG_EMPTY); while (MORE()) ordinary(p, WGETNEXT()); } /* - p_bre - BRE parser top level, anchoring and concatenation == static void p_bre(struct parse *p, int end1, \ == int end2); * Giving end1 as OUT essentially eliminates the end1/end2 check. * * This implementation is a bit of a kludge, in that a trailing $ is first * taken as an ordinary character and then revised to be an anchor. * The amount of lookahead needed to avoid this kludge is excessive. */ static void p_bre(struct parse *p, int end1, /* first terminating character */ int end2) /* second terminating character */ { sopno start = HERE(); int first = 1; /* first subexpression? */ int wasdollar = 0; if (EAT('^')) { EMIT(OBOL, 0); p->g->iflags |= USEBOL; p->g->nbol++; } while (MORE() && !SEETWO(end1, end2)) { wasdollar = p_simp_re(p, first); first = 0; } if (wasdollar) { /* oops, that was a trailing anchor */ DROP(1); EMIT(OEOL, 0); p->g->iflags |= USEEOL; p->g->neol++; } (void)REQUIRE(HERE() != start, REG_EMPTY); /* require nonempty */ } /* - p_simp_re - parse a simple RE, an atom possibly followed by a repetition == static int p_simp_re(struct parse *p, int starordinary); */ static int /* was the simple RE an unbackslashed $? */ p_simp_re(struct parse *p, int starordinary) /* is a leading * an ordinary character? */ { int c; int count; int count2; sopno pos; int i; wint_t wc; sopno subno; # define BACKSL (1<g->cflags®_NEWLINE) nonnewline(p); else EMIT(OANY, 0); break; case '[': p_bracket(p); break; case BACKSL|'<': EMIT(OBOW, 0); break; case BACKSL|'>': EMIT(OEOW, 0); break; case BACKSL|'{': SETERROR(REG_BADRPT); break; case BACKSL|'(': p->g->nsub++; subno = p->g->nsub; if (subno < NPAREN) p->pbegin[subno] = HERE(); EMIT(OLPAREN, subno); /* the MORE here is an error heuristic */ if (MORE() && !SEETWO('\\', ')')) p_bre(p, '\\', ')'); if (subno < NPAREN) { p->pend[subno] = HERE(); assert(p->pend[subno] != 0); } EMIT(ORPAREN, subno); (void)REQUIRE(EATTWO('\\', ')'), REG_EPAREN); break; case BACKSL|')': /* should not get here -- must be user */ case BACKSL|'}': SETERROR(REG_EPAREN); break; case BACKSL|'1': case BACKSL|'2': case BACKSL|'3': case BACKSL|'4': case BACKSL|'5': case BACKSL|'6': case BACKSL|'7': case BACKSL|'8': case BACKSL|'9': i = (c&~BACKSL) - '0'; assert(i < NPAREN); if (p->pend[i] != 0) { assert(i <= p->g->nsub); EMIT(OBACK_, i); assert(p->pbegin[i] != 0); assert(OP(p->strip[p->pbegin[i]]) == OLPAREN); assert(OP(p->strip[p->pend[i]]) == ORPAREN); (void) dupl(p, p->pbegin[i]+1, p->pend[i]); EMIT(O_BACK, i); } else SETERROR(REG_ESUBREG); p->g->backrefs = 1; break; case '*': (void)REQUIRE(starordinary, REG_BADRPT); /* FALLTHROUGH */ default: p->next--; wc = WGETNEXT(); ordinary(p, wc); break; } if (EAT('*')) { /* implemented as +? */ /* this case does not require the (y|) trick, noKLUDGE */ INSERT(OPLUS_, pos); ASTERN(O_PLUS, pos); INSERT(OQUEST_, pos); ASTERN(O_QUEST, pos); } else if (EATTWO('\\', '{')) { count = p_count(p); if (EAT(',')) { if (MORE() && isdigit((uch)PEEK())) { count2 = p_count(p); (void)REQUIRE(count <= count2, REG_BADBR); } else /* single number with comma */ count2 = INFINITY; } else /* just a single number */ count2 = count; repeat(p, pos, count, count2); if (!EATTWO('\\', '}')) { /* error heuristics */ while (MORE() && !SEETWO('\\', '}')) NEXT(); (void)REQUIRE(MORE(), REG_EBRACE); SETERROR(REG_BADBR); } } else if (c == '$') /* $ (but not \$) ends it */ return(1); return(0); } /* - p_count - parse a repetition count == static int p_count(struct parse *p); */ static int /* the value */ p_count(struct parse *p) { int count = 0; int ndigits = 0; while (MORE() && isdigit((uch)PEEK()) && count <= DUPMAX) { count = count*10 + (GETNEXT() - '0'); ndigits++; } (void)REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR); return(count); } /* - p_bracket - parse a bracketed character list == static void p_bracket(struct parse *p); */ static void p_bracket(struct parse *p) { cset *cs; wint_t ch; /* Dept of Truly Sickening Special-Case Kludges */ if (p->next + 5 < p->end && strncmp(p->next, "[:<:]]", 6) == 0) { EMIT(OBOW, 0); NEXTn(6); return; } if (p->next + 5 < p->end && strncmp(p->next, "[:>:]]", 6) == 0) { EMIT(OEOW, 0); NEXTn(6); return; } if ((cs = allocset(p)) == NULL) return; if (p->g->cflags®_ICASE) cs->icase = 1; if (EAT('^')) cs->invert = 1; if (EAT(']')) CHadd(p, cs, ']'); else if (EAT('-')) CHadd(p, cs, '-'); while (MORE() && PEEK() != ']' && !SEETWO('-', ']')) p_b_term(p, cs); if (EAT('-')) CHadd(p, cs, '-'); (void)MUSTEAT(']', REG_EBRACK); if (p->error != 0) /* don't mess things up further */ return; if (cs->invert && p->g->cflags®_NEWLINE) cs->bmp['\n' >> 3] |= 1 << ('\n' & 7); if ((ch = singleton(cs)) != OUT) { /* optimize singleton sets */ ordinary(p, ch); freeset(p, cs); } else EMIT(OANYOF, (int)(cs - p->g->sets)); } /* - p_b_term - parse one term of a bracketed character list == static void p_b_term(struct parse *p, cset *cs); */ static void p_b_term(struct parse *p, cset *cs) { char c; wint_t start, finish; wint_t i; struct xlocale_collate *table = (struct xlocale_collate*)__get_locale()->components[XLC_COLLATE]; /* classify what we've got */ switch ((MORE()) ? PEEK() : '\0') { case '[': c = (MORE2()) ? PEEK2() : '\0'; break; case '-': SETERROR(REG_ERANGE); return; /* NOTE RETURN */ default: c = '\0'; break; } switch (c) { case ':': /* character class */ NEXT2(); (void)REQUIRE(MORE(), REG_EBRACK); c = PEEK(); (void)REQUIRE(c != '-' && c != ']', REG_ECTYPE); p_b_cclass(p, cs); (void)REQUIRE(MORE(), REG_EBRACK); (void)REQUIRE(EATTWO(':', ']'), REG_ECTYPE); break; case '=': /* equivalence class */ NEXT2(); (void)REQUIRE(MORE(), REG_EBRACK); c = PEEK(); (void)REQUIRE(c != '-' && c != ']', REG_ECOLLATE); p_b_eclass(p, cs); (void)REQUIRE(MORE(), REG_EBRACK); (void)REQUIRE(EATTWO('=', ']'), REG_ECOLLATE); break; default: /* symbol, ordinary character, or range */ start = p_b_symbol(p); if (SEE('-') && MORE2() && PEEK2() != ']') { /* range */ NEXT(); if (EAT('-')) finish = '-'; else finish = p_b_symbol(p); } else finish = start; if (start == finish) CHadd(p, cs, start); else { if (table->__collate_load_error || MB_CUR_MAX > 1) { (void)REQUIRE(start <= finish, REG_ERANGE); CHaddrange(p, cs, start, finish); } else { (void)REQUIRE(__wcollate_range_cmp(start, finish) <= 0, REG_ERANGE); for (i = 0; i <= UCHAR_MAX; i++) { if ( __wcollate_range_cmp(start, i) <= 0 && __wcollate_range_cmp(i, finish) <= 0 ) CHadd(p, cs, i); } } } break; } } /* - p_b_cclass - parse a character-class name and deal with it == static void p_b_cclass(struct parse *p, cset *cs); */ static void p_b_cclass(struct parse *p, cset *cs) { char *sp = p->next; size_t len; wctype_t wct; char clname[16]; while (MORE() && isalpha((uch)PEEK())) NEXT(); len = p->next - sp; if (len >= sizeof(clname) - 1) { SETERROR(REG_ECTYPE); return; } memcpy(clname, sp, len); clname[len] = '\0'; if ((wct = wctype(clname)) == 0) { SETERROR(REG_ECTYPE); return; } CHaddtype(p, cs, wct); } /* - p_b_eclass - parse an equivalence-class name and deal with it == static void p_b_eclass(struct parse *p, cset *cs); * * This implementation is incomplete. xxx */ static void p_b_eclass(struct parse *p, cset *cs) { wint_t c; c = p_b_coll_elem(p, '='); CHadd(p, cs, c); } /* - p_b_symbol - parse a character or [..]ed multicharacter collating symbol == static wint_t p_b_symbol(struct parse *p); */ static wint_t /* value of symbol */ p_b_symbol(struct parse *p) { wint_t value; (void)REQUIRE(MORE(), REG_EBRACK); if (!EATTWO('[', '.')) return(WGETNEXT()); /* collating symbol */ value = p_b_coll_elem(p, '.'); (void)REQUIRE(EATTWO('.', ']'), REG_ECOLLATE); return(value); } /* - p_b_coll_elem - parse a collating-element name and look it up == static wint_t p_b_coll_elem(struct parse *p, wint_t endc); */ static wint_t /* value of collating element */ p_b_coll_elem(struct parse *p, wint_t endc) /* name ended by endc,']' */ { char *sp = p->next; struct cname *cp; int len; mbstate_t mbs; wchar_t wc; size_t clen; while (MORE() && !SEETWO(endc, ']')) NEXT(); if (!MORE()) { SETERROR(REG_EBRACK); return(0); } len = p->next - sp; for (cp = cnames; cp->name != NULL; cp++) if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0') return(cp->code); /* known name */ memset(&mbs, 0, sizeof(mbs)); if ((clen = mbrtowc(&wc, sp, len, &mbs)) == len) return (wc); /* single character */ else if (clen == (size_t)-1 || clen == (size_t)-2) SETERROR(REG_ILLSEQ); else SETERROR(REG_ECOLLATE); /* neither */ return(0); } /* - othercase - return the case counterpart of an alphabetic == static wint_t othercase(wint_t ch); */ static wint_t /* if no counterpart, return ch */ othercase(wint_t ch) { assert(iswalpha(ch)); if (iswupper(ch)) return(towlower(ch)); else if (iswlower(ch)) return(towupper(ch)); else /* peculiar, but could happen */ return(ch); } /* - bothcases - emit a dualcase version of a two-case character == static void bothcases(struct parse *p, wint_t ch); * * Boy, is this implementation ever a kludge... */ static void bothcases(struct parse *p, wint_t ch) { char *oldnext = p->next; char *oldend = p->end; char bracket[3 + MB_LEN_MAX]; size_t n; mbstate_t mbs; assert(othercase(ch) != ch); /* p_bracket() would recurse */ p->next = bracket; memset(&mbs, 0, sizeof(mbs)); n = wcrtomb(bracket, ch, &mbs); assert(n != (size_t)-1); bracket[n] = ']'; bracket[n + 1] = '\0'; p->end = bracket+n+1; p_bracket(p); assert(p->next == p->end); p->next = oldnext; p->end = oldend; } /* - ordinary - emit an ordinary character == static void ordinary(struct parse *p, wint_t ch); */ static void ordinary(struct parse *p, wint_t ch) { cset *cs; if ((p->g->cflags®_ICASE) && iswalpha(ch) && othercase(ch) != ch) bothcases(p, ch); else if ((ch & OPDMASK) == ch) EMIT(OCHAR, ch); else { /* * Kludge: character is too big to fit into an OCHAR operand. * Emit a singleton set. */ if ((cs = allocset(p)) == NULL) return; CHadd(p, cs, ch); EMIT(OANYOF, (int)(cs - p->g->sets)); } } /* - nonnewline - emit REG_NEWLINE version of OANY == static void nonnewline(struct parse *p); * * Boy, is this implementation ever a kludge... */ static void nonnewline(struct parse *p) { char *oldnext = p->next; char *oldend = p->end; char bracket[4]; p->next = bracket; p->end = bracket+3; bracket[0] = '^'; bracket[1] = '\n'; bracket[2] = ']'; bracket[3] = '\0'; p_bracket(p); assert(p->next == bracket+3); p->next = oldnext; p->end = oldend; } /* - repeat - generate code for a bounded repetition, recursively if needed == static void repeat(struct parse *p, sopno start, int from, int to); */ static void repeat(struct parse *p, sopno start, /* operand from here to end of strip */ int from, /* repeated from this number */ int to) /* to this number of times (maybe INFINITY) */ { sopno finish = HERE(); # define N 2 # define INF 3 # define REP(f, t) ((f)*8 + (t)) # define MAP(n) (((n) <= 1) ? (n) : ((n) == INFINITY) ? INF : N) sopno copy; if (p->error != 0) /* head off possible runaway recursion */ return; assert(from <= to); switch (REP(MAP(from), MAP(to))) { case REP(0, 0): /* must be user doing this */ DROP(finish-start); /* drop the operand */ break; case REP(0, 1): /* as x{1,1}? */ case REP(0, N): /* as x{1,n}? */ case REP(0, INF): /* as x{1,}? */ /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ INSERT(OCH_, start); /* offset is wrong... */ repeat(p, start+1, 1, to); ASTERN(OOR1, start); AHEAD(start); /* ... fix it */ EMIT(OOR2, 0); AHEAD(THERE()); ASTERN(O_CH, THERETHERE()); break; case REP(1, 1): /* trivial case */ /* done */ break; case REP(1, N): /* as x?x{1,n-1} */ /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ INSERT(OCH_, start); ASTERN(OOR1, start); AHEAD(start); EMIT(OOR2, 0); /* offset very wrong... */ AHEAD(THERE()); /* ...so fix it */ ASTERN(O_CH, THERETHERE()); copy = dupl(p, start+1, finish+1); assert(copy == finish+4); repeat(p, copy, 1, to-1); break; case REP(1, INF): /* as x+ */ INSERT(OPLUS_, start); ASTERN(O_PLUS, start); break; case REP(N, N): /* as xx{m-1,n-1} */ copy = dupl(p, start, finish); repeat(p, copy, from-1, to-1); break; case REP(N, INF): /* as xx{n-1,INF} */ copy = dupl(p, start, finish); repeat(p, copy, from-1, to); break; default: /* "can't happen" */ SETERROR(REG_ASSERT); /* just in case */ break; } } /* - wgetnext - helper function for WGETNEXT() macro. Gets the next wide - character from the parse struct, signals a REG_ILLSEQ error if the - character can't be converted. Returns the number of bytes consumed. */ static wint_t wgetnext(struct parse *p) { mbstate_t mbs; wchar_t wc; size_t n; memset(&mbs, 0, sizeof(mbs)); n = mbrtowc(&wc, p->next, p->end - p->next, &mbs); if (n == (size_t)-1 || n == (size_t)-2) { SETERROR(REG_ILLSEQ); return (0); } if (n == 0) n = 1; p->next += n; return (wc); } /* - seterr - set an error condition == static int seterr(struct parse *p, int e); */ static int /* useless but makes type checking happy */ seterr(struct parse *p, int e) { if (p->error == 0) /* keep earliest error condition */ p->error = e; p->next = nuls; /* try to bring things to a halt */ p->end = nuls; return(0); /* make the return value well-defined */ } /* - allocset - allocate a set of characters for [] == static cset *allocset(struct parse *p); */ static cset * allocset(struct parse *p) { cset *cs, *ncs; - ncs = realloc(p->g->sets, (p->g->ncsets + 1) * sizeof(*ncs)); + ncs = reallocarray(p->g->sets, p->g->ncsets + 1, sizeof(*ncs)); if (ncs == NULL) { SETERROR(REG_ESPACE); return (NULL); } p->g->sets = ncs; cs = &p->g->sets[p->g->ncsets++]; memset(cs, 0, sizeof(*cs)); return(cs); } /* - freeset - free a now-unused set == static void freeset(struct parse *p, cset *cs); */ static void freeset(struct parse *p, cset *cs) { cset *top = &p->g->sets[p->g->ncsets]; free(cs->wides); free(cs->ranges); free(cs->types); memset(cs, 0, sizeof(*cs)); if (cs == top-1) /* recover only the easy case */ p->g->ncsets--; } /* - singleton - Determine whether a set contains only one character, - returning it if so, otherwise returning OUT. */ static wint_t singleton(cset *cs) { wint_t i, s, n; for (i = n = 0; i < NC; i++) if (CHIN(cs, i)) { n++; s = i; } if (n == 1) return (s); if (cs->nwides == 1 && cs->nranges == 0 && cs->ntypes == 0 && cs->icase == 0) return (cs->wides[0]); /* Don't bother handling the other cases. */ return (OUT); } /* - CHadd - add character to character set. */ static void CHadd(struct parse *p, cset *cs, wint_t ch) { wint_t nch, *newwides; assert(ch >= 0); if (ch < NC) cs->bmp[ch >> 3] |= 1 << (ch & 7); else { - newwides = realloc(cs->wides, (cs->nwides + 1) * + newwides = reallocarray(cs->wides, cs->nwides + 1, sizeof(*cs->wides)); if (newwides == NULL) { SETERROR(REG_ESPACE); return; } cs->wides = newwides; cs->wides[cs->nwides++] = ch; } if (cs->icase) { if ((nch = towlower(ch)) < NC) cs->bmp[nch >> 3] |= 1 << (nch & 7); if ((nch = towupper(ch)) < NC) cs->bmp[nch >> 3] |= 1 << (nch & 7); } } /* - CHaddrange - add all characters in the range [min,max] to a character set. */ static void CHaddrange(struct parse *p, cset *cs, wint_t min, wint_t max) { crange *newranges; for (; min < NC && min <= max; min++) CHadd(p, cs, min); if (min >= max) return; - newranges = realloc(cs->ranges, (cs->nranges + 1) * + newranges = reallocarray(cs->ranges, cs->nranges + 1, sizeof(*cs->ranges)); if (newranges == NULL) { SETERROR(REG_ESPACE); return; } cs->ranges = newranges; cs->ranges[cs->nranges].min = min; cs->ranges[cs->nranges].max = max; cs->nranges++; } /* - CHaddtype - add all characters of a certain type to a character set. */ static void CHaddtype(struct parse *p, cset *cs, wctype_t wct) { wint_t i; wctype_t *newtypes; for (i = 0; i < NC; i++) if (iswctype(i, wct)) CHadd(p, cs, i); - newtypes = realloc(cs->types, (cs->ntypes + 1) * + newtypes = reallocarray(cs->types, cs->ntypes + 1, sizeof(*cs->types)); if (newtypes == NULL) { SETERROR(REG_ESPACE); return; } cs->types = newtypes; cs->types[cs->ntypes++] = wct; } /* - dupl - emit a duplicate of a bunch of sops == static sopno dupl(struct parse *p, sopno start, sopno finish); */ static sopno /* start of duplicate */ dupl(struct parse *p, sopno start, /* from here */ sopno finish) /* to this less one */ { sopno ret = HERE(); sopno len = finish - start; assert(finish >= start); if (len == 0) return(ret); if (!enlarge(p, p->ssize + len)) /* this many unexpected additions */ return(ret); (void) memcpy((char *)(p->strip + p->slen), (char *)(p->strip + start), (size_t)len*sizeof(sop)); p->slen += len; return(ret); } /* - doemit - emit a strip operator == static void doemit(struct parse *p, sop op, size_t opnd); * * It might seem better to implement this as a macro with a function as * hard-case backup, but it's just too big and messy unless there are * some changes to the data structures. Maybe later. */ static void doemit(struct parse *p, sop op, size_t opnd) { /* avoid making error situations worse */ if (p->error != 0) return; /* deal with oversize operands ("can't happen", more or less) */ assert(opnd < 1<slen >= p->ssize) if (!enlarge(p, (p->ssize+1) / 2 * 3)) /* +50% */ return; /* finally, it's all reduced to the easy case */ p->strip[p->slen++] = SOP(op, opnd); } /* - doinsert - insert a sop into the strip == static void doinsert(struct parse *p, sop op, size_t opnd, sopno pos); */ static void doinsert(struct parse *p, sop op, size_t opnd, sopno pos) { sopno sn; sop s; int i; /* avoid making error situations worse */ if (p->error != 0) return; sn = HERE(); EMIT(op, opnd); /* do checks, ensure space */ assert(HERE() == sn+1); s = p->strip[sn]; /* adjust paren pointers */ assert(pos > 0); for (i = 1; i < NPAREN; i++) { if (p->pbegin[i] >= pos) { p->pbegin[i]++; } if (p->pend[i] >= pos) { p->pend[i]++; } } memmove((char *)&p->strip[pos+1], (char *)&p->strip[pos], (HERE()-pos-1)*sizeof(sop)); p->strip[pos] = s; } /* - dofwd - complete a forward reference == static void dofwd(struct parse *p, sopno pos, sop value); */ static void dofwd(struct parse *p, sopno pos, sop value) { /* avoid making error situations worse */ if (p->error != 0) return; assert(value < 1<strip[pos] = OP(p->strip[pos]) | value; } /* - enlarge - enlarge the strip == static int enlarge(struct parse *p, sopno size); */ static int enlarge(struct parse *p, sopno size) { sop *sp; if (p->ssize >= size) return 1; - sp = (sop *)realloc(p->strip, size*sizeof(sop)); + sp = reallocarray(p->strip, size, sizeof(sop)); if (sp == NULL) { SETERROR(REG_ESPACE); return 0; } p->strip = sp; p->ssize = size; return 1; } /* - stripsnug - compact the strip == static void stripsnug(struct parse *p, struct re_guts *g); */ static void stripsnug(struct parse *p, struct re_guts *g) { g->nstates = p->slen; - g->strip = (sop *)realloc((char *)p->strip, p->slen * sizeof(sop)); + g->strip = reallocarray((char *)p->strip, p->slen, sizeof(sop)); if (g->strip == NULL) { SETERROR(REG_ESPACE); g->strip = p->strip; } } /* - findmust - fill in must and mlen with longest mandatory literal string == static void findmust(struct parse *p, struct re_guts *g); * * This algorithm could do fancy things like analyzing the operands of | * for common subsequences. Someday. This code is simple and finds most * of the interesting cases. * * Note that must and mlen got initialized during setup. */ static void findmust(struct parse *p, struct re_guts *g) { sop *scan; sop *start = NULL; sop *newstart = NULL; sopno newlen; sop s; char *cp; int offset; char buf[MB_LEN_MAX]; size_t clen; mbstate_t mbs; /* avoid making error situations worse */ if (p->error != 0) return; /* * It's not generally safe to do a ``char'' substring search on * multibyte character strings, but it's safe for at least * UTF-8 (see RFC 3629). */ if (MB_CUR_MAX > 1 && strcmp(_CurrentRuneLocale->__encoding, "UTF-8") != 0) return; /* find the longest OCHAR sequence in strip */ newlen = 0; offset = 0; g->moffset = 0; scan = g->strip + 1; do { s = *scan++; switch (OP(s)) { case OCHAR: /* sequence member */ if (newlen == 0) { /* new sequence */ memset(&mbs, 0, sizeof(mbs)); newstart = scan - 1; } clen = wcrtomb(buf, OPND(s), &mbs); if (clen == (size_t)-1) goto toohard; newlen += clen; break; case OPLUS_: /* things that don't break one */ case OLPAREN: case ORPAREN: break; case OQUEST_: /* things that must be skipped */ case OCH_: offset = altoffset(scan, offset); scan--; do { scan += OPND(s); s = *scan; /* assert() interferes w debug printouts */ if (OP(s) != O_QUEST && OP(s) != O_CH && OP(s) != OOR2) { g->iflags |= BAD; return; } } while (OP(s) != O_QUEST && OP(s) != O_CH); /* FALLTHROUGH */ case OBOW: /* things that break a sequence */ case OEOW: case OBOL: case OEOL: case O_QUEST: case O_CH: case OEND: if (newlen > g->mlen) { /* ends one */ start = newstart; g->mlen = newlen; if (offset > -1) { g->moffset += offset; offset = newlen; } else g->moffset = offset; } else { if (offset > -1) offset += newlen; } newlen = 0; break; case OANY: if (newlen > g->mlen) { /* ends one */ start = newstart; g->mlen = newlen; if (offset > -1) { g->moffset += offset; offset = newlen; } else g->moffset = offset; } else { if (offset > -1) offset += newlen; } if (offset > -1) offset++; newlen = 0; break; case OANYOF: /* may or may not invalidate offset */ /* First, everything as OANY */ if (newlen > g->mlen) { /* ends one */ start = newstart; g->mlen = newlen; if (offset > -1) { g->moffset += offset; offset = newlen; } else g->moffset = offset; } else { if (offset > -1) offset += newlen; } if (offset > -1) offset++; newlen = 0; break; toohard: default: /* Anything here makes it impossible or too hard * to calculate the offset -- so we give up; * save the last known good offset, in case the * must sequence doesn't occur later. */ if (newlen > g->mlen) { /* ends one */ start = newstart; g->mlen = newlen; if (offset > -1) g->moffset += offset; else g->moffset = offset; } offset = -1; newlen = 0; break; } } while (OP(s) != OEND); if (g->mlen == 0) { /* there isn't one */ g->moffset = -1; return; } /* turn it into a character string */ g->must = malloc((size_t)g->mlen + 1); if (g->must == NULL) { /* argh; just forget it */ g->mlen = 0; g->moffset = -1; return; } cp = g->must; scan = start; memset(&mbs, 0, sizeof(mbs)); while (cp < g->must + g->mlen) { while (OP(s = *scan++) != OCHAR) continue; clen = wcrtomb(cp, OPND(s), &mbs); assert(clen != (size_t)-1); cp += clen; } assert(cp == g->must + g->mlen); *cp++ = '\0'; /* just on general principles */ } /* - altoffset - choose biggest offset among multiple choices == static int altoffset(sop *scan, int offset); * * Compute, recursively if necessary, the largest offset among multiple * re paths. */ static int altoffset(sop *scan, int offset) { int largest; int try; sop s; /* If we gave up already on offsets, return */ if (offset == -1) return -1; largest = 0; try = 0; s = *scan++; while (OP(s) != O_QUEST && OP(s) != O_CH) { switch (OP(s)) { case OOR1: if (try > largest) largest = try; try = 0; break; case OQUEST_: case OCH_: try = altoffset(scan, try); if (try == -1) return -1; scan--; do { scan += OPND(s); s = *scan; if (OP(s) != O_QUEST && OP(s) != O_CH && OP(s) != OOR2) return -1; } while (OP(s) != O_QUEST && OP(s) != O_CH); /* We must skip to the next position, or we'll * leave altoffset() too early. */ scan++; break; case OANYOF: case OCHAR: case OANY: try++; case OBOW: case OEOW: case OLPAREN: case ORPAREN: case OOR2: break; default: try = -1; break; } if (try == -1) return -1; s = *scan++; } if (try > largest) largest = try; return largest+offset; } /* - computejumps - compute char jumps for BM scan == static void computejumps(struct parse *p, struct re_guts *g); * * This algorithm assumes g->must exists and is has size greater than * zero. It's based on the algorithm found on Computer Algorithms by * Sara Baase. * * A char jump is the number of characters one needs to jump based on * the value of the character from the text that was mismatched. */ static void computejumps(struct parse *p, struct re_guts *g) { int ch; int mindex; /* Avoid making errors worse */ if (p->error != 0) return; g->charjump = (int*) malloc((NC + 1) * sizeof(int)); if (g->charjump == NULL) /* Not a fatal error */ return; /* Adjust for signed chars, if necessary */ g->charjump = &g->charjump[-(CHAR_MIN)]; /* If the character does not exist in the pattern, the jump * is equal to the number of characters in the pattern. */ for (ch = CHAR_MIN; ch < (CHAR_MAX + 1); ch++) g->charjump[ch] = g->mlen; /* If the character does exist, compute the jump that would * take us to the last character in the pattern equal to it * (notice that we match right to left, so that last character * is the first one that would be matched). */ for (mindex = 0; mindex < g->mlen; mindex++) g->charjump[(int)g->must[mindex]] = g->mlen - mindex - 1; } /* - computematchjumps - compute match jumps for BM scan == static void computematchjumps(struct parse *p, struct re_guts *g); * * This algorithm assumes g->must exists and is has size greater than * zero. It's based on the algorithm found on Computer Algorithms by * Sara Baase. * * A match jump is the number of characters one needs to advance based * on the already-matched suffix. * Notice that all values here are minus (g->mlen-1), because of the way * the search algorithm works. */ static void computematchjumps(struct parse *p, struct re_guts *g) { int mindex; /* General "must" iterator */ int suffix; /* Keeps track of matching suffix */ int ssuffix; /* Keeps track of suffixes' suffix */ int* pmatches; /* pmatches[k] points to the next i * such that i+1...mlen is a substring * of k+1...k+mlen-i-1 */ /* Avoid making errors worse */ if (p->error != 0) return; pmatches = (int*) malloc(g->mlen * sizeof(int)); if (pmatches == NULL) { g->matchjump = NULL; return; } g->matchjump = (int*) malloc(g->mlen * sizeof(int)); if (g->matchjump == NULL) { /* Not a fatal error */ free(pmatches); return; } /* Set maximum possible jump for each character in the pattern */ for (mindex = 0; mindex < g->mlen; mindex++) g->matchjump[mindex] = 2*g->mlen - mindex - 1; /* Compute pmatches[] */ for (mindex = g->mlen - 1, suffix = g->mlen; mindex >= 0; mindex--, suffix--) { pmatches[mindex] = suffix; /* If a mismatch is found, interrupting the substring, * compute the matchjump for that position. If no * mismatch is found, then a text substring mismatched * against the suffix will also mismatch against the * substring. */ while (suffix < g->mlen && g->must[mindex] != g->must[suffix]) { g->matchjump[suffix] = MIN(g->matchjump[suffix], g->mlen - mindex - 1); suffix = pmatches[suffix]; } } /* Compute the matchjump up to the last substring found to jump * to the beginning of the largest must pattern prefix matching * it's own suffix. */ for (mindex = 0; mindex <= suffix; mindex++) g->matchjump[mindex] = MIN(g->matchjump[mindex], g->mlen + suffix - mindex); ssuffix = pmatches[suffix]; while (suffix < g->mlen) { while (suffix <= ssuffix && suffix < g->mlen) { g->matchjump[suffix] = MIN(g->matchjump[suffix], g->mlen + ssuffix - suffix); suffix++; } if (suffix < g->mlen) ssuffix = pmatches[ssuffix]; } free(pmatches); } /* - pluscount - count + nesting == static sopno pluscount(struct parse *p, struct re_guts *g); */ static sopno /* nesting depth */ pluscount(struct parse *p, struct re_guts *g) { sop *scan; sop s; sopno plusnest = 0; sopno maxnest = 0; if (p->error != 0) return(0); /* there may not be an OEND */ scan = g->strip + 1; do { s = *scan++; switch (OP(s)) { case OPLUS_: plusnest++; break; case O_PLUS: if (plusnest > maxnest) maxnest = plusnest; plusnest--; break; } } while (OP(s) != OEND); if (plusnest != 0) g->iflags |= BAD; return(maxnest); } Index: head/lib/libc/rpc/getnetconfig.c =================================================================== --- head/lib/libc/rpc/getnetconfig.c (revision 315161) +++ head/lib/libc/rpc/getnetconfig.c (revision 315162) @@ -1,734 +1,734 @@ /* $NetBSD: getnetconfig.c,v 1.3 2000/07/06 03:10:34 christos Exp $ */ /*- * Copyright (c) 2009, Sun Microsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - 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. * - Neither the name of Sun Microsystems, Inc. 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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[] = "@(#)getnetconfig.c 1.12 91/12/19 SMI"; #endif #include __FBSDID("$FreeBSD$"); /* * Copyright (c) 1989 by Sun Microsystems, Inc. */ #include "namespace.h" #include "reentrant.h" #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "rpc_com.h" /* * The five library routines in this file provide application access to the * system network configuration database, /etc/netconfig. In addition to the * netconfig database and the routines for accessing it, the environment * variable NETPATH and its corresponding routines in getnetpath.c may also be * used to specify the network transport to be used. */ /* * netconfig errors */ #define NC_NONETCONFIG ENOENT #define NC_NOMEM ENOMEM #define NC_NOTINIT EINVAL /* setnetconfig was not called first */ #define NC_BADFILE EBADF /* format for netconfig file is bad */ #define NC_NOTFOUND ENOPROTOOPT /* specified netid was not found */ /* * semantics as strings (should be in netconfig.h) */ #define NC_TPI_CLTS_S "tpi_clts" #define NC_TPI_COTS_S "tpi_cots" #define NC_TPI_COTS_ORD_S "tpi_cots_ord" #define NC_TPI_RAW_S "tpi_raw" /* * flags as characters (also should be in netconfig.h) */ #define NC_NOFLAG_C '-' #define NC_VISIBLE_C 'v' #define NC_BROADCAST_C 'b' /* * Character used to indicate there is no name-to-address lookup library */ #define NC_NOLOOKUP "-" static const char * const _nc_errors[] = { "Netconfig database not found", "Not enough memory", "Not initialized", "Netconfig database has invalid format", "Netid not found in netconfig database" }; struct netconfig_info { int eof; /* all entries has been read */ int ref; /* # of times setnetconfig() has been called */ struct netconfig_list *head; /* head of the list */ struct netconfig_list *tail; /* last of the list */ }; struct netconfig_list { char *linep; /* hold line read from netconfig */ struct netconfig *ncp; struct netconfig_list *next; }; struct netconfig_vars { int valid; /* token that indicates a valid netconfig_vars */ int flag; /* first time flag */ struct netconfig_list *nc_configs; /* pointer to the current netconfig entry */ }; #define NC_VALID 0xfeed #define NC_STORAGE 0xf00d #define NC_INVALID 0 static int *__nc_error(void); static int parse_ncp(char *, struct netconfig *); static struct netconfig *dup_ncp(struct netconfig *); static FILE *nc_file; /* for netconfig db */ static mutex_t nc_file_lock = MUTEX_INITIALIZER; static struct netconfig_info ni = { 0, 0, NULL, NULL}; static mutex_t ni_lock = MUTEX_INITIALIZER; static thread_key_t nc_key; static once_t nc_once = ONCE_INITIALIZER; static int nc_key_error; static void nc_key_init(void) { nc_key_error = thr_keycreate(&nc_key, free); } #define MAXNETCONFIGLINE 1000 static int * __nc_error(void) { static int nc_error = 0; int *nc_addr; /* * Use the static `nc_error' if we are the main thread * (including non-threaded programs), or if an allocation * fails. */ if (thr_main()) return (&nc_error); if (thr_once(&nc_once, nc_key_init) != 0 || nc_key_error != 0) return (&nc_error); if ((nc_addr = (int *)thr_getspecific(nc_key)) == NULL) { nc_addr = (int *)malloc(sizeof (int)); if (thr_setspecific(nc_key, (void *) nc_addr) != 0) { free(nc_addr); return (&nc_error); } *nc_addr = 0; } return (nc_addr); } #define nc_error (*(__nc_error())) /* * A call to setnetconfig() establishes a /etc/netconfig "session". A session * "handle" is returned on a successful call. At the start of a session (after * a call to setnetconfig()) searches through the /etc/netconfig database will * proceed from the start of the file. The session handle must be passed to * getnetconfig() to parse the file. Each call to getnetconfig() using the * current handle will process one subsequent entry in /etc/netconfig. * setnetconfig() must be called before the first call to getnetconfig(). * (Handles are used to allow for nested calls to setnetpath()). * * A new session is established with each call to setnetconfig(), with a new * handle being returned on each call. Previously established sessions remain * active until endnetconfig() is called with that session's handle as an * argument. * * setnetconfig() need *not* be called before a call to getnetconfigent(). * setnetconfig() returns a NULL pointer on failure (for example, if * the netconfig database is not present). */ void * setnetconfig(void) { struct netconfig_vars *nc_vars; if ((nc_vars = (struct netconfig_vars *)malloc(sizeof (struct netconfig_vars))) == NULL) { return(NULL); } /* * For multiple calls, i.e. nc_file is not NULL, we just return the * handle without reopening the netconfig db. */ mutex_lock(&ni_lock); ni.ref++; mutex_unlock(&ni_lock); mutex_lock(&nc_file_lock); if ((nc_file != NULL) || (nc_file = fopen(NETCONFIG, "r")) != NULL) { nc_vars->valid = NC_VALID; nc_vars->flag = 0; nc_vars->nc_configs = ni.head; mutex_unlock(&nc_file_lock); return ((void *)nc_vars); } mutex_unlock(&nc_file_lock); mutex_lock(&ni_lock); ni.ref--; mutex_unlock(&ni_lock); nc_error = NC_NONETCONFIG; free(nc_vars); return (NULL); } /* * When first called, getnetconfig() returns a pointer to the first entry in * the netconfig database, formatted as a struct netconfig. On each subsequent * call, getnetconfig() returns a pointer to the next entry in the database. * getnetconfig() can thus be used to search the entire netconfig file. * getnetconfig() returns NULL at end of file. */ struct netconfig * getnetconfig(void *handlep) { struct netconfig_vars *ncp = (struct netconfig_vars *)handlep; char *stringp; /* tmp string pointer */ struct netconfig_list *list; struct netconfig *np; struct netconfig *result; /* * Verify that handle is valid */ mutex_lock(&nc_file_lock); if (ncp == NULL || nc_file == NULL) { nc_error = NC_NOTINIT; mutex_unlock(&nc_file_lock); return (NULL); } mutex_unlock(&nc_file_lock); switch (ncp->valid) { case NC_VALID: /* * If entry has already been read into the list, * we return the entry in the linked list. * If this is the first time call, check if there are any entries in * linked list. If no entries, we need to read the netconfig db. * If we have been here and the next entry is there, we just return * it. */ if (ncp->flag == 0) { /* first time */ ncp->flag = 1; mutex_lock(&ni_lock); ncp->nc_configs = ni.head; mutex_unlock(&ni_lock); if (ncp->nc_configs != NULL) /* entry already exist */ return(ncp->nc_configs->ncp); } else if (ncp->nc_configs != NULL && ncp->nc_configs->next != NULL) { ncp->nc_configs = ncp->nc_configs->next; return(ncp->nc_configs->ncp); } /* * If we cannot find the entry in the list and is end of file, * we give up. */ mutex_lock(&ni_lock); if (ni.eof == 1) { mutex_unlock(&ni_lock); return(NULL); } mutex_unlock(&ni_lock); break; default: nc_error = NC_NOTINIT; return (NULL); } stringp = (char *) malloc(MAXNETCONFIGLINE); if (stringp == NULL) return (NULL); #ifdef MEM_CHK if (malloc_verify() == 0) { fprintf(stderr, "memory heap corrupted in getnetconfig\n"); exit(1); } #endif /* * Read a line from netconfig file. */ mutex_lock(&nc_file_lock); do { if (fgets(stringp, MAXNETCONFIGLINE, nc_file) == NULL) { free(stringp); mutex_lock(&ni_lock); ni.eof = 1; mutex_unlock(&ni_lock); mutex_unlock(&nc_file_lock); return (NULL); } } while (*stringp == '#'); mutex_unlock(&nc_file_lock); list = (struct netconfig_list *) malloc(sizeof (struct netconfig_list)); if (list == NULL) { free(stringp); return(NULL); } np = (struct netconfig *) malloc(sizeof (struct netconfig)); if (np == NULL) { free(stringp); free(list); return(NULL); } list->ncp = np; list->next = NULL; list->ncp->nc_lookups = NULL; list->linep = stringp; if (parse_ncp(stringp, list->ncp) == -1) { free(stringp); free(np); free(list); return (NULL); } else { /* * If this is the first entry that's been read, it is the head of * the list. If not, put the entry at the end of the list. * Reposition the current pointer of the handle to the last entry * in the list. */ mutex_lock(&ni_lock); if (ni.head == NULL) { /* first entry */ ni.head = ni.tail = list; } else { ni.tail->next = list; ni.tail = ni.tail->next; } ncp->nc_configs = ni.tail; result = ni.tail->ncp; mutex_unlock(&ni_lock); return(result); } } /* * endnetconfig() may be called to "unbind" or "close" the netconfig database * when processing is complete, releasing resources for reuse. endnetconfig() * may not be called before setnetconfig(). endnetconfig() returns 0 on * success and -1 on failure (for example, if setnetconfig() was not called * previously). */ int endnetconfig(void *handlep) { struct netconfig_vars *nc_handlep = (struct netconfig_vars *)handlep; struct netconfig_list *q, *p; /* * Verify that handle is valid */ if (nc_handlep == NULL || (nc_handlep->valid != NC_VALID && nc_handlep->valid != NC_STORAGE)) { nc_error = NC_NOTINIT; return (-1); } /* * Return 0 if anyone still needs it. */ nc_handlep->valid = NC_INVALID; nc_handlep->flag = 0; nc_handlep->nc_configs = NULL; mutex_lock(&ni_lock); if (--ni.ref > 0) { mutex_unlock(&ni_lock); free(nc_handlep); return(0); } /* * No one needs these entries anymore, then frees them. * Make sure all info in netconfig_info structure has been reinitialized. */ q = ni.head; ni.eof = ni.ref = 0; ni.head = NULL; ni.tail = NULL; mutex_unlock(&ni_lock); while (q != NULL) { p = q->next; free(q->ncp->nc_lookups); free(q->ncp); free(q->linep); free(q); q = p; } free(nc_handlep); mutex_lock(&nc_file_lock); fclose(nc_file); nc_file = NULL; mutex_unlock(&nc_file_lock); return (0); } /* * getnetconfigent(netid) returns a pointer to the struct netconfig structure * corresponding to netid. It returns NULL if netid is invalid (that is, does * not name an entry in the netconfig database). It returns NULL and sets * errno in case of failure (for example, if the netconfig database cannot be * opened). */ struct netconfig * getnetconfigent(const char *netid) { FILE *file; /* NETCONFIG db's file pointer */ char *linep; /* holds current netconfig line */ char *stringp; /* temporary string pointer */ struct netconfig *ncp = NULL; /* returned value */ struct netconfig_list *list; /* pointer to cache list */ nc_error = NC_NOTFOUND; /* default error. */ if (netid == NULL || strlen(netid) == 0) { return (NULL); } /* * Look up table if the entries have already been read and parsed in * getnetconfig(), then copy this entry into a buffer and return it. * If we cannot find the entry in the current list and there are more * entries in the netconfig db that has not been read, we then read the * db and try find the match netid. * If all the netconfig db has been read and placed into the list and * there is no match for the netid, return NULL. */ mutex_lock(&ni_lock); if (ni.head != NULL) { for (list = ni.head; list; list = list->next) { if (strcmp(list->ncp->nc_netid, netid) == 0) { mutex_unlock(&ni_lock); return(dup_ncp(list->ncp)); } } if (ni.eof == 1) { /* that's all the entries */ mutex_unlock(&ni_lock); return(NULL); } } mutex_unlock(&ni_lock); if ((file = fopen(NETCONFIG, "r")) == NULL) { nc_error = NC_NONETCONFIG; return (NULL); } if ((linep = malloc(MAXNETCONFIGLINE)) == NULL) { fclose(file); nc_error = NC_NOMEM; return (NULL); } do { ptrdiff_t len; char *tmpp; /* tmp string pointer */ do { if ((stringp = fgets(linep, MAXNETCONFIGLINE, file)) == NULL) { break; } } while (*stringp == '#'); if (stringp == NULL) { /* eof */ break; } if ((tmpp = strpbrk(stringp, "\t ")) == NULL) { /* can't parse file */ nc_error = NC_BADFILE; break; } if (strlen(netid) == (size_t) (len = tmpp - stringp) && /* a match */ strncmp(stringp, netid, (size_t)len) == 0) { if ((ncp = (struct netconfig *) malloc(sizeof (struct netconfig))) == NULL) { break; } ncp->nc_lookups = NULL; if (parse_ncp(linep, ncp) == -1) { free(ncp); ncp = NULL; } break; } } while (stringp != NULL); if (ncp == NULL) { free(linep); } fclose(file); return(ncp); } /* * freenetconfigent(netconfigp) frees the netconfig structure pointed to by * netconfigp (previously returned by getnetconfigent()). */ void freenetconfigent(struct netconfig *netconfigp) { if (netconfigp != NULL) { free(netconfigp->nc_netid); /* holds all netconfigp's strings */ free(netconfigp->nc_lookups); free(netconfigp); } return; } /* * Parse line and stuff it in a struct netconfig * Typical line might look like: * udp tpi_cots vb inet udp /dev/udp /usr/lib/ip.so,/usr/local/ip.so * * We return -1 if any of the tokens don't parse, or malloc fails. * * Note that we modify stringp (putting NULLs after tokens) and * we set the ncp's string field pointers to point to these tokens within * stringp. * * stringp - string to parse * ncp - where to put results */ static int parse_ncp(char *stringp, struct netconfig *ncp) { char *tokenp; /* for processing tokens */ char *lasts; char **nc_lookups; nc_error = NC_BADFILE; /* nearly anything that breaks is for this reason */ stringp[strlen(stringp)-1] = '\0'; /* get rid of newline */ /* netid */ if ((ncp->nc_netid = strtok_r(stringp, "\t ", &lasts)) == NULL) { return (-1); } /* semantics */ if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL) { return (-1); } if (strcmp(tokenp, NC_TPI_COTS_ORD_S) == 0) ncp->nc_semantics = NC_TPI_COTS_ORD; else if (strcmp(tokenp, NC_TPI_COTS_S) == 0) ncp->nc_semantics = NC_TPI_COTS; else if (strcmp(tokenp, NC_TPI_CLTS_S) == 0) ncp->nc_semantics = NC_TPI_CLTS; else if (strcmp(tokenp, NC_TPI_RAW_S) == 0) ncp->nc_semantics = NC_TPI_RAW; else return (-1); /* flags */ if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL) { return (-1); } for (ncp->nc_flag = NC_NOFLAG; *tokenp != '\0'; tokenp++) { switch (*tokenp) { case NC_NOFLAG_C: break; case NC_VISIBLE_C: ncp->nc_flag |= NC_VISIBLE; break; case NC_BROADCAST_C: ncp->nc_flag |= NC_BROADCAST; break; default: return (-1); } } /* protocol family */ if ((ncp->nc_protofmly = strtok_r(NULL, "\t ", &lasts)) == NULL) { return (-1); } /* protocol name */ if ((ncp->nc_proto = strtok_r(NULL, "\t ", &lasts)) == NULL) { return (-1); } /* network device */ if ((ncp->nc_device = strtok_r(NULL, "\t ", &lasts)) == NULL) { return (-1); } if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL) { return (-1); } if (strcmp(tokenp, NC_NOLOOKUP) == 0) { ncp->nc_nlookups = 0; ncp->nc_lookups = NULL; } else { char *cp; /* tmp string */ free(ncp->nc_lookups); /* from last visit */ ncp->nc_lookups = NULL; ncp->nc_nlookups = 0; while ((cp = tokenp) != NULL) { - if ((nc_lookups = realloc(ncp->nc_lookups, - (ncp->nc_nlookups + 1) * sizeof *ncp->nc_lookups)) == NULL) { + if ((nc_lookups = reallocarray(ncp->nc_lookups, + ncp->nc_nlookups + 1, sizeof(*ncp->nc_lookups))) == NULL) { free(ncp->nc_lookups); ncp->nc_lookups = NULL; return (-1); } tokenp = _get_next_token(cp, ','); ncp->nc_lookups = nc_lookups; ncp->nc_lookups[ncp->nc_nlookups++] = cp; } } return (0); } /* * Returns a string describing the reason for failure. */ char * nc_sperror(void) { const char *message; switch(nc_error) { case NC_NONETCONFIG: message = _nc_errors[0]; break; case NC_NOMEM: message = _nc_errors[1]; break; case NC_NOTINIT: message = _nc_errors[2]; break; case NC_BADFILE: message = _nc_errors[3]; break; case NC_NOTFOUND: message = _nc_errors[4]; break; default: message = "Unknown network selection error"; } /* LINTED const castaway */ return ((char *)message); } /* * Prints a message onto standard error describing the reason for failure. */ void nc_perror(const char *s) { fprintf(stderr, "%s: %s\n", s, nc_sperror()); } /* * Duplicates the matched netconfig buffer. */ static struct netconfig * dup_ncp(struct netconfig *ncp) { struct netconfig *p; char *tmp, *tmp2; u_int i; if ((tmp=malloc(MAXNETCONFIGLINE)) == NULL) return(NULL); if ((p=(struct netconfig *)malloc(sizeof(struct netconfig))) == NULL) { free(tmp); return(NULL); } tmp2 = tmp; /* * First we dup all the data from matched netconfig buffer. Then we * adjust some of the member pointer to a pre-allocated buffer where * contains part of the data. * To follow the convention used in parse_ncp(), we store all the * necessary information in the pre-allocated buffer and let each * of the netconfig char pointer member point to the right address * in the buffer. */ *p = *ncp; p->nc_netid = (char *)strcpy(tmp,ncp->nc_netid); tmp = strchr(tmp, '\0') + 1; p->nc_protofmly = (char *)strcpy(tmp,ncp->nc_protofmly); tmp = strchr(tmp, '\0') + 1; p->nc_proto = (char *)strcpy(tmp,ncp->nc_proto); tmp = strchr(tmp, '\0') + 1; p->nc_device = (char *)strcpy(tmp,ncp->nc_device); p->nc_lookups = (char **)malloc((size_t)(p->nc_nlookups+1) * sizeof(char *)); if (p->nc_lookups == NULL) { free(p->nc_netid); free(p); free(tmp2); return(NULL); } for (i=0; i < p->nc_nlookups; i++) { tmp = strchr(tmp, '\0') + 1; p->nc_lookups[i] = (char *)strcpy(tmp,ncp->nc_lookups[i]); } return(p); } Index: head/lib/libc/stdio/open_wmemstream.c =================================================================== --- head/lib/libc/stdio/open_wmemstream.c (revision 315161) +++ head/lib/libc/stdio/open_wmemstream.c (revision 315162) @@ -1,274 +1,274 @@ /*- * Copyright (c) 2013 Hudson River Trading LLC * Written by: John H. Baldwin * All rights reserved. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #ifdef DEBUG #include #endif #include #include #include #include #include "un-namespace.h" /* XXX: There is no FPOS_MAX. This assumes fpos_t is an off_t. */ #define FPOS_MAX OFF_MAX struct wmemstream { wchar_t **bufp; size_t *sizep; ssize_t len; fpos_t offset; mbstate_t mbstate; }; static int wmemstream_grow(struct wmemstream *ms, fpos_t newoff) { wchar_t *buf; ssize_t newsize; if (newoff < 0 || newoff >= SSIZE_MAX / sizeof(wchar_t)) newsize = SSIZE_MAX / sizeof(wchar_t) - 1; else newsize = newoff; if (newsize > ms->len) { - buf = realloc(*ms->bufp, (newsize + 1) * sizeof(wchar_t)); + buf = reallocarray(*ms->bufp, newsize + 1, sizeof(wchar_t)); if (buf != NULL) { #ifdef DEBUG fprintf(stderr, "WMS: %p growing from %zd to %zd\n", ms, ms->len, newsize); #endif wmemset(buf + ms->len + 1, 0, newsize - ms->len); *ms->bufp = buf; ms->len = newsize; return (1); } return (0); } return (1); } static void wmemstream_update(struct wmemstream *ms) { assert(ms->len >= 0 && ms->offset >= 0); *ms->sizep = ms->len < ms->offset ? ms->len : ms->offset; } /* * Based on a starting multibyte state and an input buffer, determine * how many wchar_t's would be output. This doesn't use mbsnrtowcs() * so that it can handle embedded null characters. */ static size_t wbuflen(const mbstate_t *state, const char *buf, int len) { mbstate_t lenstate; size_t charlen, count; count = 0; lenstate = *state; while (len > 0) { charlen = mbrlen(buf, len, &lenstate); if (charlen == (size_t)-1) return (-1); if (charlen == (size_t)-2) break; if (charlen == 0) /* XXX: Not sure how else to handle this. */ charlen = 1; len -= charlen; buf += charlen; count++; } return (count); } static int wmemstream_write(void *cookie, const char *buf, int len) { struct wmemstream *ms; ssize_t consumed, wlen; size_t charlen; ms = cookie; wlen = wbuflen(&ms->mbstate, buf, len); if (wlen < 0) { errno = EILSEQ; return (-1); } if (!wmemstream_grow(ms, ms->offset + wlen)) return (-1); /* * This copies characters one at a time rather than using * mbsnrtowcs() so it can properly handle embedded null * characters. */ consumed = 0; while (len > 0 && ms->offset < ms->len) { charlen = mbrtowc(*ms->bufp + ms->offset, buf, len, &ms->mbstate); if (charlen == (size_t)-1) { if (consumed == 0) { errno = EILSEQ; return (-1); } /* Treat it as a successful short write. */ break; } if (charlen == 0) /* XXX: Not sure how else to handle this. */ charlen = 1; if (charlen == (size_t)-2) { consumed += len; len = 0; } else { consumed += charlen; buf += charlen; len -= charlen; ms->offset++; } } wmemstream_update(ms); #ifdef DEBUG fprintf(stderr, "WMS: write(%p, %d) = %zd\n", ms, len, consumed); #endif return (consumed); } static fpos_t wmemstream_seek(void *cookie, fpos_t pos, int whence) { struct wmemstream *ms; fpos_t old; ms = cookie; old = ms->offset; switch (whence) { case SEEK_SET: /* _fseeko() checks for negative offsets. */ assert(pos >= 0); ms->offset = pos; break; case SEEK_CUR: /* This is only called by _ftello(). */ assert(pos == 0); break; case SEEK_END: if (pos < 0) { if (pos + ms->len < 0) { #ifdef DEBUG fprintf(stderr, "WMS: bad SEEK_END: pos %jd, len %zd\n", (intmax_t)pos, ms->len); #endif errno = EINVAL; return (-1); } } else { if (FPOS_MAX - ms->len < pos) { #ifdef DEBUG fprintf(stderr, "WMS: bad SEEK_END: pos %jd, len %zd\n", (intmax_t)pos, ms->len); #endif errno = EOVERFLOW; return (-1); } } ms->offset = ms->len + pos; break; } /* Reset the multibyte state if a seek changes the position. */ if (ms->offset != old) memset(&ms->mbstate, 0, sizeof(ms->mbstate)); wmemstream_update(ms); #ifdef DEBUG fprintf(stderr, "WMS: seek(%p, %jd, %d) %jd -> %jd\n", ms, (intmax_t)pos, whence, (intmax_t)old, (intmax_t)ms->offset); #endif return (ms->offset); } static int wmemstream_close(void *cookie) { free(cookie); return (0); } FILE * open_wmemstream(wchar_t **bufp, size_t *sizep) { struct wmemstream *ms; int save_errno; FILE *fp; if (bufp == NULL || sizep == NULL) { errno = EINVAL; return (NULL); } *bufp = calloc(1, sizeof(wchar_t)); if (*bufp == NULL) return (NULL); ms = malloc(sizeof(*ms)); if (ms == NULL) { save_errno = errno; free(*bufp); *bufp = NULL; errno = save_errno; return (NULL); } ms->bufp = bufp; ms->sizep = sizep; ms->len = 0; ms->offset = 0; memset(&ms->mbstate, 0, sizeof(mbstate_t)); wmemstream_update(ms); fp = funopen(ms, NULL, wmemstream_write, wmemstream_seek, wmemstream_close); if (fp == NULL) { save_errno = errno; free(ms); free(*bufp); *bufp = NULL; errno = save_errno; return (NULL); } fwide(fp, 1); return (fp); } Index: head/lib/libc/stdio/printf-pos.c =================================================================== --- head/lib/libc/stdio/printf-pos.c (revision 315161) +++ head/lib/libc/stdio/printf-pos.c (revision 315162) @@ -1,774 +1,774 @@ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * 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[] = "@(#)vfprintf.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); /* * This is the code responsible for handling positional arguments * (%m$ and %m$.n$) for vfprintf() and vfwprintf(). */ #include "namespace.h" #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "printflocal.h" #ifdef NL_ARGMAX #define MAX_POSARG NL_ARGMAX #else #define MAX_POSARG 65536 #endif /* * Type ids for argument type table. */ enum typeid { T_UNUSED, TP_SHORT, T_INT, T_U_INT, TP_INT, T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG, T_PTRDIFFT, TP_PTRDIFFT, T_SSIZET, T_SIZET, TP_SSIZET, T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, TP_CHAR, TP_SCHAR, T_DOUBLE, T_LONG_DOUBLE, T_WINT, TP_WCHAR }; /* An expandable array of types. */ struct typetable { enum typeid *table; /* table of types */ enum typeid stattable[STATIC_ARG_TBL_SIZE]; u_int tablesize; /* current size of type table */ u_int tablemax; /* largest used index in table */ u_int nextarg; /* 1-based argument index */ }; static int __grow_type_table(struct typetable *); static void build_arg_table (struct typetable *, va_list, union arg **); /* * Initialize a struct typetable. */ static inline void inittypes(struct typetable *types) { u_int n; types->table = types->stattable; types->tablesize = STATIC_ARG_TBL_SIZE; types->tablemax = 0; types->nextarg = 1; for (n = 0; n < STATIC_ARG_TBL_SIZE; n++) types->table[n] = T_UNUSED; } /* * struct typetable destructor. */ static inline void freetypes(struct typetable *types) { if (types->table != types->stattable) free (types->table); } /* * Ensure that there is space to add a new argument type to the type table. * Expand the table if necessary. Returns 0 on success. */ static inline int _ensurespace(struct typetable *types) { if (types->nextarg >= types->tablesize) { if (__grow_type_table(types)) return (-1); } if (types->nextarg > types->tablemax) types->tablemax = types->nextarg; return (0); } /* * Add an argument type to the table, expanding if necessary. * Returns 0 on success. */ static inline int addtype(struct typetable *types, enum typeid type) { if (_ensurespace(types)) return (-1); types->table[types->nextarg++] = type; return (0); } static inline int addsarg(struct typetable *types, int flags) { if (_ensurespace(types)) return (-1); if (flags & INTMAXT) types->table[types->nextarg++] = T_INTMAXT; else if (flags & SIZET) types->table[types->nextarg++] = T_SSIZET; else if (flags & PTRDIFFT) types->table[types->nextarg++] = T_PTRDIFFT; else if (flags & LLONGINT) types->table[types->nextarg++] = T_LLONG; else if (flags & LONGINT) types->table[types->nextarg++] = T_LONG; else types->table[types->nextarg++] = T_INT; return (0); } static inline int adduarg(struct typetable *types, int flags) { if (_ensurespace(types)) return (-1); if (flags & INTMAXT) types->table[types->nextarg++] = T_UINTMAXT; else if (flags & SIZET) types->table[types->nextarg++] = T_SIZET; else if (flags & PTRDIFFT) types->table[types->nextarg++] = T_SIZET; else if (flags & LLONGINT) types->table[types->nextarg++] = T_U_LLONG; else if (flags & LONGINT) types->table[types->nextarg++] = T_U_LONG; else types->table[types->nextarg++] = T_U_INT; return (0); } /* * Add * arguments to the type array. */ static inline int addaster(struct typetable *types, char **fmtp) { char *cp; u_int n2; n2 = 0; cp = *fmtp; while (is_digit(*cp)) { n2 = 10 * n2 + to_digit(*cp); cp++; } if (*cp == '$') { u_int hold = types->nextarg; types->nextarg = n2; if (addtype(types, T_INT)) return (-1); types->nextarg = hold; *fmtp = ++cp; } else { if (addtype(types, T_INT)) return (-1); } return (0); } static inline int addwaster(struct typetable *types, wchar_t **fmtp) { wchar_t *cp; u_int n2; n2 = 0; cp = *fmtp; while (is_digit(*cp)) { n2 = 10 * n2 + to_digit(*cp); cp++; } if (*cp == '$') { u_int hold = types->nextarg; types->nextarg = n2; if (addtype(types, T_INT)) return (-1); types->nextarg = hold; *fmtp = ++cp; } else { if (addtype(types, T_INT)) return (-1); } return (0); } /* * Find all arguments when a positional parameter is encountered. Returns a * table, indexed by argument number, of pointers to each arguments. The * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. * It will be replaces with a malloc-ed one if it overflows. * Returns 0 on success. On failure, returns nonzero and sets errno. */ int __find_arguments (const char *fmt0, va_list ap, union arg **argtable) { char *fmt; /* format string */ int ch; /* character from fmt */ u_int n; /* handy integer (short term usage) */ int error; int flags; /* flags as above */ struct typetable types; /* table of types */ fmt = (char *)fmt0; inittypes(&types); error = 0; /* * Scan the format for conversions (`%' character). */ for (;;) { while ((ch = *fmt) != '\0' && ch != '%') fmt++; if (ch == '\0') goto done; fmt++; /* skip over '%' */ flags = 0; rflag: ch = *fmt++; reswitch: switch (ch) { case ' ': case '#': goto rflag; case '*': if ((error = addaster(&types, &fmt))) goto error; goto rflag; case '-': case '+': case '\'': goto rflag; case '.': if ((ch = *fmt++) == '*') { if ((error = addaster(&types, &fmt))) goto error; goto rflag; } while (is_digit(ch)) { ch = *fmt++; } goto reswitch; case '0': goto rflag; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; do { n = 10 * n + to_digit(ch); /* Detect overflow */ if (n > MAX_POSARG) { error = -1; goto error; } ch = *fmt++; } while (is_digit(ch)); if (ch == '$') { types.nextarg = n; goto rflag; } goto reswitch; #ifndef NO_FLOATING_POINT case 'L': flags |= LONGDBL; goto rflag; #endif case 'h': if (flags & SHORTINT) { flags &= ~SHORTINT; flags |= CHARINT; } else flags |= SHORTINT; goto rflag; case 'j': flags |= INTMAXT; goto rflag; case 'l': if (flags & LONGINT) { flags &= ~LONGINT; flags |= LLONGINT; } else flags |= LONGINT; goto rflag; case 'q': flags |= LLONGINT; /* not necessarily */ goto rflag; case 't': flags |= PTRDIFFT; goto rflag; case 'z': flags |= SIZET; goto rflag; case 'C': flags |= LONGINT; /*FALLTHROUGH*/ case 'c': error = addtype(&types, (flags & LONGINT) ? T_WINT : T_INT); if (error) goto error; break; case 'D': flags |= LONGINT; /*FALLTHROUGH*/ case 'd': case 'i': if ((error = addsarg(&types, flags))) goto error; break; #ifndef NO_FLOATING_POINT case 'a': case 'A': case 'e': case 'E': case 'f': case 'g': case 'G': error = addtype(&types, (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE); if (error) goto error; break; #endif /* !NO_FLOATING_POINT */ case 'n': if (flags & INTMAXT) error = addtype(&types, TP_INTMAXT); else if (flags & PTRDIFFT) error = addtype(&types, TP_PTRDIFFT); else if (flags & SIZET) error = addtype(&types, TP_SSIZET); else if (flags & LLONGINT) error = addtype(&types, TP_LLONG); else if (flags & LONGINT) error = addtype(&types, TP_LONG); else if (flags & SHORTINT) error = addtype(&types, TP_SHORT); else if (flags & CHARINT) error = addtype(&types, TP_SCHAR); else error = addtype(&types, TP_INT); if (error) goto error; continue; /* no output */ case 'O': flags |= LONGINT; /*FALLTHROUGH*/ case 'o': if ((error = adduarg(&types, flags))) goto error; break; case 'p': if ((error = addtype(&types, TP_VOID))) goto error; break; case 'S': flags |= LONGINT; /*FALLTHROUGH*/ case 's': error = addtype(&types, (flags & LONGINT) ? TP_WCHAR : TP_CHAR); if (error) goto error; break; case 'U': flags |= LONGINT; /*FALLTHROUGH*/ case 'u': case 'X': case 'x': if ((error = adduarg(&types, flags))) goto error; break; default: /* "%?" prints ?, unless ? is NUL */ if (ch == '\0') goto done; break; } } done: build_arg_table(&types, ap, argtable); error: freetypes(&types); return (error || *argtable == NULL); } /* wchar version of __find_arguments. */ int __find_warguments (const wchar_t *fmt0, va_list ap, union arg **argtable) { wchar_t *fmt; /* format string */ wchar_t ch; /* character from fmt */ u_int n; /* handy integer (short term usage) */ int error; int flags; /* flags as above */ struct typetable types; /* table of types */ fmt = (wchar_t *)fmt0; inittypes(&types); error = 0; /* * Scan the format for conversions (`%' character). */ for (;;) { while ((ch = *fmt) != '\0' && ch != '%') fmt++; if (ch == '\0') goto done; fmt++; /* skip over '%' */ flags = 0; rflag: ch = *fmt++; reswitch: switch (ch) { case ' ': case '#': goto rflag; case '*': if ((error = addwaster(&types, &fmt))) goto error; goto rflag; case '-': case '+': case '\'': goto rflag; case '.': if ((ch = *fmt++) == '*') { if ((error = addwaster(&types, &fmt))) goto error; goto rflag; } while (is_digit(ch)) { ch = *fmt++; } goto reswitch; case '0': goto rflag; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; do { n = 10 * n + to_digit(ch); /* Detect overflow */ if (n > MAX_POSARG) { error = -1; goto error; } ch = *fmt++; } while (is_digit(ch)); if (ch == '$') { types.nextarg = n; goto rflag; } goto reswitch; #ifndef NO_FLOATING_POINT case 'L': flags |= LONGDBL; goto rflag; #endif case 'h': if (flags & SHORTINT) { flags &= ~SHORTINT; flags |= CHARINT; } else flags |= SHORTINT; goto rflag; case 'j': flags |= INTMAXT; goto rflag; case 'l': if (flags & LONGINT) { flags &= ~LONGINT; flags |= LLONGINT; } else flags |= LONGINT; goto rflag; case 'q': flags |= LLONGINT; /* not necessarily */ goto rflag; case 't': flags |= PTRDIFFT; goto rflag; case 'z': flags |= SIZET; goto rflag; case 'C': flags |= LONGINT; /*FALLTHROUGH*/ case 'c': error = addtype(&types, (flags & LONGINT) ? T_WINT : T_INT); if (error) goto error; break; case 'D': flags |= LONGINT; /*FALLTHROUGH*/ case 'd': case 'i': if ((error = addsarg(&types, flags))) goto error; break; #ifndef NO_FLOATING_POINT case 'a': case 'A': case 'e': case 'E': case 'f': case 'g': case 'G': error = addtype(&types, (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE); if (error) goto error; break; #endif /* !NO_FLOATING_POINT */ case 'n': if (flags & INTMAXT) error = addtype(&types, TP_INTMAXT); else if (flags & PTRDIFFT) error = addtype(&types, TP_PTRDIFFT); else if (flags & SIZET) error = addtype(&types, TP_SSIZET); else if (flags & LLONGINT) error = addtype(&types, TP_LLONG); else if (flags & LONGINT) error = addtype(&types, TP_LONG); else if (flags & SHORTINT) error = addtype(&types, TP_SHORT); else if (flags & CHARINT) error = addtype(&types, TP_SCHAR); else error = addtype(&types, TP_INT); if (error) goto error; continue; /* no output */ case 'O': flags |= LONGINT; /*FALLTHROUGH*/ case 'o': if ((error = adduarg(&types, flags))) goto error; break; case 'p': if ((error = addtype(&types, TP_VOID))) goto error; break; case 'S': flags |= LONGINT; /*FALLTHROUGH*/ case 's': error = addtype(&types, (flags & LONGINT) ? TP_WCHAR : TP_CHAR); if (error) goto error; break; case 'U': flags |= LONGINT; /*FALLTHROUGH*/ case 'u': case 'X': case 'x': if ((error = adduarg(&types, flags))) goto error; break; default: /* "%?" prints ?, unless ? is NUL */ if (ch == '\0') goto done; break; } } done: build_arg_table(&types, ap, argtable); error: freetypes(&types); return (error || *argtable == NULL); } /* * Increase the size of the type table. Returns 0 on success. */ static int __grow_type_table(struct typetable *types) { enum typeid *const oldtable = types->table; const int oldsize = types->tablesize; enum typeid *newtable; u_int n, newsize; /* Detect overflow */ if (types->nextarg > NL_ARGMAX) return (-1); newsize = oldsize * 2; if (newsize < types->nextarg + 1) newsize = types->nextarg + 1; if (oldsize == STATIC_ARG_TBL_SIZE) { if ((newtable = malloc(newsize * sizeof(enum typeid))) == NULL) return (-1); bcopy(oldtable, newtable, oldsize * sizeof(enum typeid)); } else { - newtable = realloc(oldtable, newsize * sizeof(enum typeid)); + newtable = reallocarray(oldtable, newsize, sizeof(enum typeid)); if (newtable == NULL) return (-1); } for (n = oldsize; n < newsize; n++) newtable[n] = T_UNUSED; types->table = newtable; types->tablesize = newsize; return (0); } /* * Build the argument table from the completed type table. * On malloc failure, *argtable is set to NULL. */ static void build_arg_table(struct typetable *types, va_list ap, union arg **argtable) { u_int n; if (types->tablemax >= STATIC_ARG_TBL_SIZE) { *argtable = (union arg *) malloc (sizeof (union arg) * (types->tablemax + 1)); if (*argtable == NULL) return; } (*argtable) [0].intarg = 0; for (n = 1; n <= types->tablemax; n++) { switch (types->table[n]) { case T_UNUSED: /* whoops! */ (*argtable) [n].intarg = va_arg (ap, int); break; case TP_SCHAR: (*argtable) [n].pschararg = va_arg (ap, signed char *); break; case TP_SHORT: (*argtable) [n].pshortarg = va_arg (ap, short *); break; case T_INT: (*argtable) [n].intarg = va_arg (ap, int); break; case T_U_INT: (*argtable) [n].uintarg = va_arg (ap, unsigned int); break; case TP_INT: (*argtable) [n].pintarg = va_arg (ap, int *); break; case T_LONG: (*argtable) [n].longarg = va_arg (ap, long); break; case T_U_LONG: (*argtable) [n].ulongarg = va_arg (ap, unsigned long); break; case TP_LONG: (*argtable) [n].plongarg = va_arg (ap, long *); break; case T_LLONG: (*argtable) [n].longlongarg = va_arg (ap, long long); break; case T_U_LLONG: (*argtable) [n].ulonglongarg = va_arg (ap, unsigned long long); break; case TP_LLONG: (*argtable) [n].plonglongarg = va_arg (ap, long long *); break; case T_PTRDIFFT: (*argtable) [n].ptrdiffarg = va_arg (ap, ptrdiff_t); break; case TP_PTRDIFFT: (*argtable) [n].pptrdiffarg = va_arg (ap, ptrdiff_t *); break; case T_SIZET: (*argtable) [n].sizearg = va_arg (ap, size_t); break; case T_SSIZET: (*argtable) [n].sizearg = va_arg (ap, ssize_t); break; case TP_SSIZET: (*argtable) [n].pssizearg = va_arg (ap, ssize_t *); break; case T_INTMAXT: (*argtable) [n].intmaxarg = va_arg (ap, intmax_t); break; case T_UINTMAXT: (*argtable) [n].uintmaxarg = va_arg (ap, uintmax_t); break; case TP_INTMAXT: (*argtable) [n].pintmaxarg = va_arg (ap, intmax_t *); break; case T_DOUBLE: #ifndef NO_FLOATING_POINT (*argtable) [n].doublearg = va_arg (ap, double); #endif break; case T_LONG_DOUBLE: #ifndef NO_FLOATING_POINT (*argtable) [n].longdoublearg = va_arg (ap, long double); #endif break; case TP_CHAR: (*argtable) [n].pchararg = va_arg (ap, char *); break; case TP_VOID: (*argtable) [n].pvoidarg = va_arg (ap, void *); break; case T_WINT: (*argtable) [n].wintarg = va_arg (ap, wint_t); break; case TP_WCHAR: (*argtable) [n].pwchararg = va_arg (ap, wchar_t *); break; } } } Index: head/lib/libc/stdio/ungetc.c =================================================================== --- head/lib/libc/stdio/ungetc.c (revision 315161) +++ head/lib/libc/stdio/ungetc.c (revision 315162) @@ -1,168 +1,168 @@ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * 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[] = "@(#)ungetc.c 8.2 (Berkeley) 11/3/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include "un-namespace.h" #include "local.h" #include "libc_private.h" static int __submore(FILE *); /* * Expand the ungetc buffer `in place'. That is, adjust fp->_p when * the buffer moves, so that it points the same distance from the end, * and move the bytes in the buffer around as necessary so that they * are all at the end (stack-style). */ static int __submore(FILE *fp) { int i; unsigned char *p; if (fp->_ub._base == fp->_ubuf) { /* * Get a new buffer (rather than expanding the old one). */ if ((p = malloc((size_t)BUFSIZ)) == NULL) return (EOF); fp->_ub._base = p; fp->_ub._size = BUFSIZ; p += BUFSIZ - sizeof(fp->_ubuf); for (i = sizeof(fp->_ubuf); --i >= 0;) p[i] = fp->_ubuf[i]; fp->_p = p; return (0); } i = fp->_ub._size; - p = realloc(fp->_ub._base, (size_t)(i << 1)); + p = reallocarray(fp->_ub._base, i, 2); if (p == NULL) return (EOF); /* no overlap (hence can use memcpy) because we doubled the size */ (void)memcpy((void *)(p + i), (void *)p, (size_t)i); fp->_p = p + i; fp->_ub._base = p; - fp->_ub._size = i << 1; + fp->_ub._size = i * 2; return (0); } /* * MT-safe version */ int ungetc(int c, FILE *fp) { int ret; if (!__sdidinit) __sinit(); FLOCKFILE(fp); ORIENT(fp, -1); ret = __ungetc(c, fp); FUNLOCKFILE(fp); return (ret); } /* * Non-MT-safe version */ int __ungetc(int c, FILE *fp) { if (c == EOF) return (EOF); if ((fp->_flags & __SRD) == 0) { /* * Not already reading: no good unless reading-and-writing. * Otherwise, flush any current write stuff. */ if ((fp->_flags & __SRW) == 0) return (EOF); if (fp->_flags & __SWR) { if (__sflush(fp)) return (EOF); fp->_flags &= ~__SWR; fp->_w = 0; fp->_lbfsize = 0; } fp->_flags |= __SRD; } c = (unsigned char)c; /* * If we are in the middle of ungetc'ing, just continue. * This may require expanding the current ungetc buffer. */ if (HASUB(fp)) { if (fp->_r >= fp->_ub._size && __submore(fp)) return (EOF); *--fp->_p = c; fp->_r++; return (c); } fp->_flags &= ~__SEOF; /* * If we can handle this by simply backing up, do so, * but never replace the original character. * (This makes sscanf() work when scanning `const' data.) */ if (fp->_bf._base != NULL && fp->_p > fp->_bf._base && fp->_p[-1] == c) { fp->_p--; fp->_r++; return (c); } /* * Create an ungetc buffer. * Initially, we will use the `reserve' buffer. */ fp->_ur = fp->_r; fp->_up = fp->_p; fp->_ub._base = fp->_ubuf; fp->_ub._size = sizeof(fp->_ubuf); fp->_ubuf[sizeof(fp->_ubuf) - 1] = c; fp->_p = &fp->_ubuf[sizeof(fp->_ubuf) - 1]; fp->_r = 1; return (c); } Index: head/lib/libc/stdlib/getenv.c =================================================================== --- head/lib/libc/stdlib/getenv.c (revision 315161) +++ head/lib/libc/stdlib/getenv.c (revision 315162) @@ -1,691 +1,691 @@ /*- * Copyright (c) 2007-2009 Sean C. Farley * All rights reserved. * * 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, * without modification, immediately at the beginning of the file. * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include "un-namespace.h" static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find "; static const char CorruptEnvValueMsg[] = "environment corrupt; missing value for "; /* * Standard environ. environ variable is exposed to entire process. * * origEnviron: Upon cleanup on unloading of library or failure, this * allows environ to return to as it was before. * environSize: Number of variables environ can hold. Can only * increase. * intEnviron: Internally-built environ. Exposed via environ during * (re)builds of the environment. */ extern char **environ; static char **origEnviron; static char **intEnviron = NULL; static int environSize = 0; /* * Array of environment variables built from environ. Each element records: * name: Pointer to name=value string * name length: Length of name not counting '=' character * value: Pointer to value within same string as name * value size: Size (not length) of space for value not counting the * nul character * active state: true/false value to signify whether variable is active. * Useful since multiple variables with the same name can * co-exist. At most, one variable can be active at any * one time. * putenv: Created from putenv() call. This memory must not be * reused. */ static struct envVars { size_t nameLen; size_t valueSize; char *name; char *value; bool active; bool putenv; } *envVars = NULL; /* * Environment array information. * * envActive: Number of active variables in array. * envVarsSize: Size of array. * envVarsTotal: Number of total variables in array (active or not). */ static int envActive = 0; static int envVarsSize = 0; static int envVarsTotal = 0; /* Deinitialization of new environment. */ static void __attribute__ ((destructor)) __clean_env_destructor(void); /* * A simple version of warnx() to avoid the bloat of including stdio in static * binaries. */ static void __env_warnx(const char *msg, const char *name, size_t nameLen) { static const char nl[] = "\n"; static const char progSep[] = ": "; _write(STDERR_FILENO, _getprogname(), strlen(_getprogname())); _write(STDERR_FILENO, progSep, sizeof(progSep) - 1); _write(STDERR_FILENO, msg, strlen(msg)); _write(STDERR_FILENO, name, nameLen); _write(STDERR_FILENO, nl, sizeof(nl) - 1); return; } /* * Inline strlen() for performance. Also, perform check for an equals sign. * Cheaper here than peforming a strchr() later. */ static inline size_t __strleneq(const char *str) { const char *s; for (s = str; *s != '\0'; ++s) if (*s == '=') return (0); return (s - str); } /* * Comparison of an environment name=value to a name. */ static inline bool strncmpeq(const char *nameValue, const char *name, size_t nameLen) { if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=') return (true); return (false); } /* * Using environment, returns pointer to value associated with name, if any, * else NULL. If the onlyActive flag is set to true, only variables that are * active are returned else all are. */ static inline char * __findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive) { int ndx; /* * Find environment variable from end of array (more likely to be * active). A variable created by putenv is always active, or it is not * tracked in the array. */ for (ndx = *envNdx; ndx >= 0; ndx--) if (envVars[ndx].putenv) { if (strncmpeq(envVars[ndx].name, name, nameLen)) { *envNdx = ndx; return (envVars[ndx].name + nameLen + sizeof ("=") - 1); } } else if ((!onlyActive || envVars[ndx].active) && (envVars[ndx].nameLen == nameLen && strncmpeq(envVars[ndx].name, name, nameLen))) { *envNdx = ndx; return (envVars[ndx].value); } return (NULL); } /* * Using environ, returns pointer to value associated with name, if any, else * NULL. Used on the original environ passed into the program. */ static char * __findenv_environ(const char *name, size_t nameLen) { int envNdx; /* Find variable within environ. */ for (envNdx = 0; environ[envNdx] != NULL; envNdx++) if (strncmpeq(environ[envNdx], name, nameLen)) return (&(environ[envNdx][nameLen + sizeof("=") - 1])); return (NULL); } /* * Remove variable added by putenv() from variable tracking array. */ static void __remove_putenv(int envNdx) { envVarsTotal--; if (envVarsTotal > envNdx) memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]), (envVarsTotal - envNdx) * sizeof (*envVars)); memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars)); return; } /* * Deallocate the environment built from environ as well as environ then set * both to NULL. Eases debugging of memory leaks. */ static void __clean_env(bool freeVars) { int envNdx; /* Deallocate environment and environ if created by *env(). */ if (envVars != NULL) { for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) /* Free variables or deactivate them. */ if (envVars[envNdx].putenv) { if (!freeVars) __remove_putenv(envNdx); } else { if (freeVars) free(envVars[envNdx].name); else envVars[envNdx].active = false; } if (freeVars) { free(envVars); envVars = NULL; } else envActive = 0; /* Restore original environ if it has not updated by program. */ if (origEnviron != NULL) { if (environ == intEnviron) environ = origEnviron; free(intEnviron); intEnviron = NULL; environSize = 0; } } return; } /* * Using the environment, rebuild the environ array for use by other C library * calls that depend upon it. */ static int __rebuild_environ(int newEnvironSize) { char **tmpEnviron; int envNdx; int environNdx; int tmpEnvironSize; /* Resize environ. */ if (newEnvironSize > environSize) { tmpEnvironSize = newEnvironSize * 2; - tmpEnviron = realloc(intEnviron, sizeof (*intEnviron) * - (tmpEnvironSize + 1)); + tmpEnviron = reallocarray(intEnviron, tmpEnvironSize + 1, + sizeof(*intEnviron)); if (tmpEnviron == NULL) return (-1); environSize = tmpEnvironSize; intEnviron = tmpEnviron; } envActive = newEnvironSize; /* Assign active variables to environ. */ for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--) if (envVars[envNdx].active) intEnviron[environNdx++] = envVars[envNdx].name; intEnviron[environNdx] = NULL; /* Always set environ which may have been replaced by program. */ environ = intEnviron; return (0); } /* * Enlarge new environment. */ static inline bool __enlarge_env(void) { int newEnvVarsSize; struct envVars *tmpEnvVars; envVarsTotal++; if (envVarsTotal > envVarsSize) { newEnvVarsSize = envVarsTotal * 2; - tmpEnvVars = realloc(envVars, sizeof (*envVars) * - newEnvVarsSize); + tmpEnvVars = reallocarray(envVars, newEnvVarsSize, + sizeof(*envVars)); if (tmpEnvVars == NULL) { envVarsTotal--; return (false); } envVarsSize = newEnvVarsSize; envVars = tmpEnvVars; } return (true); } /* * Using environ, build an environment for use by standard C library calls. */ static int __build_env(void) { char **env; int activeNdx; int envNdx; int savedErrno; size_t nameLen; /* Check for non-existant environment. */ if (environ == NULL || environ[0] == NULL) return (0); /* Count environment variables. */ for (env = environ, envVarsTotal = 0; *env != NULL; env++) envVarsTotal++; envVarsSize = envVarsTotal * 2; /* Create new environment. */ envVars = calloc(1, sizeof (*envVars) * envVarsSize); if (envVars == NULL) goto Failure; /* Copy environ values and keep track of them. */ for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) { envVars[envNdx].putenv = false; envVars[envNdx].name = strdup(environ[envVarsTotal - envNdx - 1]); if (envVars[envNdx].name == NULL) goto Failure; envVars[envNdx].value = strchr(envVars[envNdx].name, '='); if (envVars[envNdx].value != NULL) { envVars[envNdx].value++; envVars[envNdx].valueSize = strlen(envVars[envNdx].value); } else { __env_warnx(CorruptEnvValueMsg, envVars[envNdx].name, strlen(envVars[envNdx].name)); errno = EFAULT; goto Failure; } /* * Find most current version of variable to make active. This * will prevent multiple active variables from being created * during this initialization phase. */ nameLen = envVars[envNdx].value - envVars[envNdx].name - 1; envVars[envNdx].nameLen = nameLen; activeNdx = envVarsTotal - 1; if (__findenv(envVars[envNdx].name, nameLen, &activeNdx, false) == NULL) { __env_warnx(CorruptEnvFindMsg, envVars[envNdx].name, nameLen); errno = EFAULT; goto Failure; } envVars[activeNdx].active = true; } /* Create a new environ. */ origEnviron = environ; environ = NULL; if (__rebuild_environ(envVarsTotal) == 0) return (0); Failure: savedErrno = errno; __clean_env(true); errno = savedErrno; return (-1); } /* * Destructor function with default argument to __clean_env(). */ static void __clean_env_destructor(void) { __clean_env(true); return; } /* * Returns the value of a variable or NULL if none are found. */ char * getenv(const char *name) { int envNdx; size_t nameLen; /* Check for malformed name. */ if (name == NULL || (nameLen = __strleneq(name)) == 0) { errno = EINVAL; return (NULL); } /* * Variable search order: * 1. Check for an empty environ. This allows an application to clear * the environment. * 2. Search the external environ array. * 3. Search the internal environment. * * Since malloc() depends upon getenv(), getenv() must never cause the * internal environment storage to be generated. */ if (environ == NULL || environ[0] == NULL) return (NULL); else if (envVars == NULL || environ != intEnviron) return (__findenv_environ(name, nameLen)); else { envNdx = envVarsTotal - 1; return (__findenv(name, nameLen, &envNdx, true)); } } /* * Set the value of a variable. Older settings are labeled as inactive. If an * older setting has enough room to store the new value, it will be reused. No * previous variables are ever freed here to avoid causing a segmentation fault * in a user's code. * * The variables nameLen and valueLen are passed into here to allow the caller * to calculate the length by means besides just strlen(). */ static int __setenv(const char *name, size_t nameLen, const char *value, int overwrite) { bool reuse; char *env; int envNdx; int newEnvActive; size_t valueLen; /* Find existing environment variable large enough to use. */ envNdx = envVarsTotal - 1; newEnvActive = envActive; valueLen = strlen(value); reuse = false; if (__findenv(name, nameLen, &envNdx, false) != NULL) { /* Deactivate entry if overwrite is allowed. */ if (envVars[envNdx].active) { if (overwrite == 0) return (0); envVars[envNdx].active = false; newEnvActive--; } /* putenv() created variable cannot be reused. */ if (envVars[envNdx].putenv) __remove_putenv(envNdx); /* Entry is large enough to reuse. */ else if (envVars[envNdx].valueSize >= valueLen) reuse = true; } /* Create new variable if none was found of sufficient size. */ if (! reuse) { /* Enlarge environment. */ envNdx = envVarsTotal; if (!__enlarge_env()) return (-1); /* Create environment entry. */ envVars[envNdx].name = malloc(nameLen + sizeof ("=") + valueLen); if (envVars[envNdx].name == NULL) { envVarsTotal--; return (-1); } envVars[envNdx].nameLen = nameLen; envVars[envNdx].valueSize = valueLen; /* Save name of name/value pair. */ env = stpncpy(envVars[envNdx].name, name, nameLen); *env++ = '='; } else env = envVars[envNdx].value; /* Save value of name/value pair. */ strcpy(env, value); envVars[envNdx].value = env; envVars[envNdx].active = true; newEnvActive++; /* No need to rebuild environ if an active variable was reused. */ if (reuse && newEnvActive == envActive) return (0); else return (__rebuild_environ(newEnvActive)); } /* * If the program attempts to replace the array of environment variables * (environ) environ or sets the first varible to NULL, then deactivate all * variables and merge in the new list from environ. */ static int __merge_environ(void) { char **env; char *equals; /* * Internally-built environ has been replaced or cleared (detected by * using the count of active variables against a NULL as the first value * in environ). Clean up everything. */ if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 && environ[0] == NULL))) { /* Deactivate all environment variables. */ if (envActive > 0) { origEnviron = NULL; __clean_env(false); } /* * Insert new environ into existing, yet deactivated, * environment array. */ origEnviron = environ; if (origEnviron != NULL) for (env = origEnviron; *env != NULL; env++) { if ((equals = strchr(*env, '=')) == NULL) { __env_warnx(CorruptEnvValueMsg, *env, strlen(*env)); errno = EFAULT; return (-1); } if (__setenv(*env, equals - *env, equals + 1, 1) == -1) return (-1); } } return (0); } /* * The exposed setenv() that peforms a few tests before calling the function * (__setenv()) that does the actual work of inserting a variable into the * environment. */ int setenv(const char *name, const char *value, int overwrite) { size_t nameLen; /* Check for malformed name. */ if (name == NULL || (nameLen = __strleneq(name)) == 0) { errno = EINVAL; return (-1); } /* Initialize environment. */ if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) return (-1); return (__setenv(name, nameLen, value, overwrite)); } /* * Insert a "name=value" string into the environment. Special settings must be * made to keep setenv() from reusing this memory block and unsetenv() from * allowing it to be tracked. */ int putenv(char *string) { char *equals; int envNdx; int newEnvActive; size_t nameLen; /* Check for malformed argument. */ if (string == NULL || (equals = strchr(string, '=')) == NULL || (nameLen = equals - string) == 0) { errno = EINVAL; return (-1); } /* Initialize environment. */ if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) return (-1); /* Deactivate previous environment variable. */ envNdx = envVarsTotal - 1; newEnvActive = envActive; if (__findenv(string, nameLen, &envNdx, true) != NULL) { /* Reuse previous putenv slot. */ if (envVars[envNdx].putenv) { envVars[envNdx].name = string; return (__rebuild_environ(envActive)); } else { newEnvActive--; envVars[envNdx].active = false; } } /* Enlarge environment. */ envNdx = envVarsTotal; if (!__enlarge_env()) return (-1); /* Create environment entry. */ envVars[envNdx].name = string; envVars[envNdx].nameLen = -1; envVars[envNdx].value = NULL; envVars[envNdx].valueSize = -1; envVars[envNdx].putenv = true; envVars[envNdx].active = true; newEnvActive++; return (__rebuild_environ(newEnvActive)); } /* * Unset variable with the same name by flagging it as inactive. No variable is * ever freed. */ int unsetenv(const char *name) { int envNdx; size_t nameLen; int newEnvActive; /* Check for malformed name. */ if (name == NULL || (nameLen = __strleneq(name)) == 0) { errno = EINVAL; return (-1); } /* Initialize environment. */ if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) return (-1); /* Deactivate specified variable. */ /* Remove all occurrences. */ envNdx = envVarsTotal - 1; newEnvActive = envActive; while (__findenv(name, nameLen, &envNdx, true) != NULL) { envVars[envNdx].active = false; if (envVars[envNdx].putenv) __remove_putenv(envNdx); envNdx--; newEnvActive--; } if (newEnvActive != envActive) __rebuild_environ(newEnvActive); return (0); }