summaryrefslogtreecommitdiff
path: root/dotfiles/system/.zsh/modules/Src/utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'dotfiles/system/.zsh/modules/Src/utils.c')
-rw-r--r--dotfiles/system/.zsh/modules/Src/utils.c7520
1 files changed, 0 insertions, 7520 deletions
diff --git a/dotfiles/system/.zsh/modules/Src/utils.c b/dotfiles/system/.zsh/modules/Src/utils.c
deleted file mode 100644
index 075d272..0000000
--- a/dotfiles/system/.zsh/modules/Src/utils.c
+++ /dev/null
@@ -1,7520 +0,0 @@
-/*
- * utils.c - miscellaneous utilities
- *
- * This file is part of zsh, the Z shell.
- *
- * Copyright (c) 1992-1997 Paul Falstad
- * All rights reserved.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and to distribute modified versions of this software for any
- * purpose, provided that the above copyright notice and the following
- * two paragraphs appear in all copies of this software.
- *
- * In no event shall Paul Falstad or the Zsh Development Group be liable
- * to any party for direct, indirect, special, incidental, or consequential
- * damages arising out of the use of this software and its documentation,
- * even if Paul Falstad and the Zsh Development Group have been advised of
- * the possibility of such damage.
- *
- * Paul Falstad and the Zsh Development Group specifically disclaim any
- * warranties, including, but not limited to, the implied warranties of
- * merchantability and fitness for a particular purpose. The software
- * provided hereunder is on an "as is" basis, and Paul Falstad and the
- * Zsh Development Group have no obligation to provide maintenance,
- * support, updates, enhancements, or modifications.
- *
- */
-
-#include "zsh.mdh"
-#include "utils.pro"
-
-/* name of script being sourced */
-
-/**/
-mod_export char *scriptname; /* is sometimes a function name */
-
-/* filename of script or other file containing code source e.g. autoload */
-
-/**/
-mod_export char *scriptfilename;
-
-/* != 0 if we are in a new style completion function */
-
-/**/
-mod_export int incompfunc;
-
-#ifdef MULTIBYTE_SUPPORT
-struct widechar_array {
- wchar_t *chars;
- size_t len;
-};
-typedef struct widechar_array *Widechar_array;
-
-/*
- * The wordchars variable turned into a wide character array.
- * This is much more convenient for testing.
- */
-static struct widechar_array wordchars_wide;
-
-/*
- * The same for the separators (IFS) array.
- */
-static struct widechar_array ifs_wide;
-
-/* Function to set one of the above from the multibyte array */
-
-static void
-set_widearray(char *mb_array, Widechar_array wca)
-{
- if (wca->chars) {
- free(wca->chars);
- wca->chars = NULL;
- }
- wca->len = 0;
-
- if (!isset(MULTIBYTE))
- return;
-
- if (mb_array) {
- VARARR(wchar_t, tmpwcs, strlen(mb_array));
- wchar_t *wcptr = tmpwcs;
- wint_t wci;
-
- mb_charinit();
- while (*mb_array) {
- int mblen;
-
- if (STOUC(*mb_array) <= 0x7f) {
- mb_array++;
- *wcptr++ = (wchar_t)*mb_array;
- continue;
- }
-
- mblen = mb_metacharlenconv(mb_array, &wci);
-
- if (!mblen)
- break;
- /* No good unless all characters are convertible */
- if (wci == WEOF)
- return;
- *wcptr++ = (wchar_t)wci;
-#ifdef DEBUG
- /*
- * This generates a warning from the compiler (and is
- * indeed useless) if chars are unsigned. It's
- * extreme paranoia anyway.
- */
- if (wcptr[-1] < 0)
- fprintf(stderr, "BUG: Bad cast to wchar_t\n");
-#endif
- mb_array += mblen;
- }
-
- wca->len = wcptr - tmpwcs;
- wca->chars = (wchar_t *)zalloc(wca->len * sizeof(wchar_t));
- wmemcpy(wca->chars, tmpwcs, wca->len);
- }
-}
-#endif
-
-
-/* Print an error
-
- The following functions use the following printf-like format codes
- (implemented by zerrmsg()):
-
- Code Argument types Prints
- %s const char * C string (null terminated)
- %l const char *, int C string of given length (null not required)
- %L long decimal value
- %d int decimal value
- %% (none) literal '%'
- %c int character at that codepoint
- %e int strerror() message (argument is typically 'errno')
- */
-
-static void
-zwarning(const char *cmd, const char *fmt, va_list ap)
-{
- if (isatty(2))
- zleentry(ZLE_CMD_TRASH);
-
- char *prefix = scriptname ? scriptname : (argzero ? argzero : "");
-
- if (cmd) {
- if (unset(SHINSTDIN) || locallevel) {
- nicezputs(prefix, stderr);
- fputc((unsigned char)':', stderr);
- }
- nicezputs(cmd, stderr);
- fputc((unsigned char)':', stderr);
- } else {
- /*
- * scriptname is set when sourcing scripts, so that we get the
- * correct name instead of the generic name of whatever
- * program/script is running. It's also set in shell functions,
- * so test locallevel, too.
- */
- nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" : prefix, stderr);
- fputc((unsigned char)':', stderr);
- }
-
- zerrmsg(stderr, fmt, ap);
-}
-
-
-/**/
-mod_export void
-zerr(VA_ALIST1(const char *fmt))
-VA_DCL
-{
- va_list ap;
- VA_DEF_ARG(const char *fmt);
-
- if (errflag || noerrs) {
- if (noerrs < 2)
- errflag |= ERRFLAG_ERROR;
- return;
- }
- errflag |= ERRFLAG_ERROR;
-
- VA_START(ap, fmt);
- VA_GET_ARG(ap, fmt, const char *);
- zwarning(NULL, fmt, ap);
- va_end(ap);
-}
-
-/**/
-mod_export void
-zerrnam(VA_ALIST2(const char *cmd, const char *fmt))
-VA_DCL
-{
- va_list ap;
- VA_DEF_ARG(const char *cmd);
- VA_DEF_ARG(const char *fmt);
-
- if (errflag || noerrs)
- return;
- errflag |= ERRFLAG_ERROR;
-
- VA_START(ap, fmt);
- VA_GET_ARG(ap, cmd, const char *);
- VA_GET_ARG(ap, fmt, const char *);
- zwarning(cmd, fmt, ap);
- va_end(ap);
-}
-
-/**/
-mod_export void
-zwarn(VA_ALIST1(const char *fmt))
-VA_DCL
-{
- va_list ap;
- VA_DEF_ARG(const char *fmt);
-
- if (errflag || noerrs)
- return;
-
- VA_START(ap, fmt);
- VA_GET_ARG(ap, fmt, const char *);
- zwarning(NULL, fmt, ap);
- va_end(ap);
-}
-
-/**/
-mod_export void
-zwarnnam(VA_ALIST2(const char *cmd, const char *fmt))
-VA_DCL
-{
- va_list ap;
- VA_DEF_ARG(const char *cmd);
- VA_DEF_ARG(const char *fmt);
-
- if (errflag || noerrs)
- return;
-
- VA_START(ap, fmt);
- VA_GET_ARG(ap, cmd, const char *);
- VA_GET_ARG(ap, fmt, const char *);
- zwarning(cmd, fmt, ap);
- va_end(ap);
-}
-
-
-#ifdef DEBUG
-
-/**/
-mod_export void
-dputs(VA_ALIST1(const char *message))
-VA_DCL
-{
- char *filename;
- FILE *file;
- va_list ap;
- VA_DEF_ARG(const char *message);
-
- VA_START(ap, message);
- VA_GET_ARG(ap, message, const char *);
- if ((filename = getsparam_u("ZSH_DEBUG_LOG")) != NULL &&
- (file = fopen(filename, "a")) != NULL) {
- zerrmsg(file, message, ap);
- fclose(file);
- } else
- zerrmsg(stderr, message, ap);
- va_end(ap);
-}
-
-#endif /* DEBUG */
-
-#ifdef __CYGWIN__
-/*
- * This works around an occasional problem with dllwrap on Cygwin, seen
- * on at least two installations. It fails to find the last symbol
- * exported in alphabetical order (in our case zwarnnam). Until this is
- * properly categorised and fixed we add a dummy symbol at the end.
- */
-mod_export void
-zz_plural_z_alpha(void)
-{
-}
-#endif
-
-/**/
-void
-zerrmsg(FILE *file, const char *fmt, va_list ap)
-{
- const char *str;
- int num;
-#ifdef DEBUG
- long lnum;
-#endif
-#ifdef HAVE_STRERROR_R
-#define ERRBUFSIZE (80)
- int olderrno;
- char errbuf[ERRBUFSIZE];
-#endif
- char *errmsg;
-
- if ((unset(SHINSTDIN) || locallevel) && lineno) {
-#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
- fprintf(file, "%lld: ", lineno);
-#else
- fprintf(file, "%ld: ", (long)lineno);
-#endif
- } else
- fputc((unsigned char)' ', file);
-
- while (*fmt)
- if (*fmt == '%') {
- fmt++;
- switch (*fmt++) {
- case 's':
- str = va_arg(ap, const char *);
- nicezputs(str, file);
- break;
- case 'l': {
- char *s;
- str = va_arg(ap, const char *);
- num = va_arg(ap, int);
- num = metalen(str, num);
- s = zhalloc(num + 1);
- memcpy(s, str, num);
- s[num] = '\0';
- nicezputs(s, file);
- break;
- }
-#ifdef DEBUG
- case 'L':
- lnum = va_arg(ap, long);
- fprintf(file, "%ld", lnum);
- break;
-#endif
- case 'd':
- num = va_arg(ap, int);
- fprintf(file, "%d", num);
- break;
- case '%':
- putc('%', file);
- break;
- case 'c':
- num = va_arg(ap, int);
-#ifdef MULTIBYTE_SUPPORT
- mb_charinit();
- zputs(wcs_nicechar(num, NULL, NULL), file);
-#else
- zputs(nicechar(num), file);
-#endif
- break;
- case 'e':
- /* print the corresponding message for this errno */
- num = va_arg(ap, int);
- if (num == EINTR) {
- fputs("interrupt\n", file);
- errflag |= ERRFLAG_ERROR;
- return;
- }
- errmsg = strerror(num);
- /* If the message is not about I/O problems, it looks better *
- * if we uncapitalize the first letter of the message */
- if (num == EIO)
- fputs(errmsg, file);
- else {
- fputc(tulower(errmsg[0]), file);
- fputs(errmsg + 1, file);
- }
- break;
- /* When adding format codes, update the comment above zwarning(). */
- }
- } else {
- putc(*fmt == Meta ? *++fmt ^ 32 : *fmt, file);
- fmt++;
- }
- putc('\n', file);
- fflush(file);
-}
-
-/*
- * Wrapper for setupterm() and del_curterm().
- * These are called from terminfo.c and termcap.c.
- */
-static int term_count; /* reference count of cur_term */
-
-/**/
-mod_export void
-zsetupterm(void)
-{
-#ifdef HAVE_SETUPTERM
- int errret;
-
- DPUTS(term_count < 0 || (term_count > 0 && !cur_term),
- "inconsistent term_count and/or cur_term");
- /*
- * Just because we can't set up the terminal doesn't
- * mean the modules hasn't booted---TERM may change,
- * and it should be handled dynamically---so ignore errors here.
- */
- if (term_count++ == 0)
- (void)setupterm((char *)0, 1, &errret);
-#endif
-}
-
-/**/
-mod_export void
-zdeleteterm(void)
-{
-#ifdef HAVE_SETUPTERM
- DPUTS(term_count < 1 || !cur_term,
- "inconsistent term_count and/or cur_term");
- if (--term_count == 0)
- del_curterm(cur_term);
-#endif
-}
-
-/* Output a single character, for the termcap routines. *
- * This is used instead of putchar since it can be a macro. */
-
-/**/
-mod_export int
-putraw(int c)
-{
- putc(c, stdout);
- return 0;
-}
-
-/* Output a single character, for the termcap routines. */
-
-/**/
-mod_export int
-putshout(int c)
-{
- putc(c, shout);
- return 0;
-}
-
-#ifdef MULTIBYTE_SUPPORT
-/*
- * Turn a character into a visible representation thereof. The visible
- * string is put together in a static buffer, and this function returns
- * a pointer to it. Printable characters stand for themselves, DEL is
- * represented as "^?", newline and tab are represented as "\n" and
- * "\t", and normal control characters are represented in "^C" form.
- * Characters with bit 7 set, if unprintable, are represented as "\M-"
- * followed by the visible representation of the character with bit 7
- * stripped off. Tokens are interpreted, rather than being treated as
- * literal characters.
- *
- * Note that the returned string is metafied, so that it must be
- * treated like any other zsh internal string (and not, for example,
- * output directly).
- *
- * This function is used even if MULTIBYTE_SUPPORT is defined: we
- * use it as a fallback in case we couldn't identify a wide character
- * in a multibyte string.
- */
-
-/**/
-mod_export char *
-nicechar_sel(int c, int quotable)
-{
- static char buf[10];
- char *s = buf;
- c &= 0xff;
- if (ZISPRINT(c))
- goto done;
- if (c & 0x80) {
- if (isset(PRINTEIGHTBIT))
- goto done;
- *s++ = '\\';
- *s++ = 'M';
- *s++ = '-';
- c &= 0x7f;
- if(ZISPRINT(c))
- goto done;
- }
- if (c == 0x7f) {
- if (quotable) {
- *s++ = '\\';
- *s++ = 'C';
- *s++ = '-';
- } else
- *s++ = '^';
- c = '?';
- } else if (c == '\n') {
- *s++ = '\\';
- c = 'n';
- } else if (c == '\t') {
- *s++ = '\\';
- c = 't';
- } else if (c < 0x20) {
- if (quotable) {
- *s++ = '\\';
- *s++ = 'C';
- *s++ = '-';
- } else
- *s++ = '^';
- c += 0x40;
- }
- done:
- /*
- * The resulting string is still metafied, so check if
- * we are returning a character in the range that needs metafication.
- * This can't happen if the character is printed "nicely", so
- * this results in a maximum of two bytes total (plus the null).
- */
- if (imeta(c)) {
- *s++ = Meta;
- *s++ = c ^ 32;
- } else
- *s++ = c;
- *s = 0;
- return buf;
-}
-
-/**/
-mod_export char *
-nicechar(int c)
-{
- return nicechar_sel(c, 0);
-}
-
-#else /* MULTIBYTE_SUPPORT */
-
-/**/
-mod_export char *
-nicechar(int c)
-{
- static char buf[10];
- char *s = buf;
- c &= 0xff;
- if (ZISPRINT(c))
- goto done;
- if (c & 0x80) {
- if (isset(PRINTEIGHTBIT))
- goto done;
- *s++ = '\\';
- *s++ = 'M';
- *s++ = '-';
- c &= 0x7f;
- if(ZISPRINT(c))
- goto done;
- }
- if (c == 0x7f) {
- *s++ = '\\';
- *s++ = 'C';
- *s++ = '-';
- c = '?';
- } else if (c == '\n') {
- *s++ = '\\';
- c = 'n';
- } else if (c == '\t') {
- *s++ = '\\';
- c = 't';
- } else if (c < 0x20) {
- *s++ = '\\';
- *s++ = 'C';
- *s++ = '-';
- c += 0x40;
- }
- done:
- /*
- * The resulting string is still metafied, so check if
- * we are returning a character in the range that needs metafication.
- * This can't happen if the character is printed "nicely", so
- * this results in a maximum of two bytes total (plus the null).
- */
- if (imeta(c)) {
- *s++ = Meta;
- *s++ = c ^ 32;
- } else
- *s++ = c;
- *s = 0;
- return buf;
-}
-
-#endif /* MULTIBYTE_SUPPORT */
-
-/*
- * Return 1 if nicechar() would reformat this character.
- */
-
-/**/
-mod_export int
-is_nicechar(int c)
-{
- c &= 0xff;
- if (ZISPRINT(c))
- return 0;
- if (c & 0x80)
- return !isset(PRINTEIGHTBIT);
- return (c == 0x7f || c == '\n' || c == '\t' || c < 0x20);
-}
-
-/**/
-#ifdef MULTIBYTE_SUPPORT
-static mbstate_t mb_shiftstate;
-
-/*
- * Initialise multibyte state: called before a sequence of
- * wcs_nicechar(), mb_metacharlenconv(), or
- * mb_charlenconv().
- */
-
-/**/
-mod_export void
-mb_charinit(void)
-{
- memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
-}
-
-/*
- * The number of bytes we need to allocate for a "nice" representation
- * of a multibyte character.
- *
- * We double MB_CUR_MAX to take account of the fact that
- * we may need to metafy. In fact the representation probably
- * doesn't allow every character to be in the meta range, but
- * we don't need to be too pedantic.
- *
- * The 12 is for the output of a UCS-4 code; we don't actually
- * need this at the same time as MB_CUR_MAX, but again it's
- * not worth calculating more exactly.
- */
-#define NICECHAR_MAX (12 + 2*MB_CUR_MAX)
-/*
- * Input a wide character. Output a printable representation,
- * which is a metafied multibyte string. With widthp return
- * the printing width.
- *
- * swide, if non-NULL, is used to help the completion code, which needs
- * to know the printing width of the each part of the representation.
- * *swide is set to the part of the returned string where the wide
- * character starts. Any string up to that point is ASCII characters,
- * so the width of it is (*swide - <return_value>). Anything left is
- * a single wide character corresponding to the remaining width.
- * Either the initial ASCII part or the wide character part may be empty
- * (but not both). (Note the complication that the wide character
- * part may contain metafied characters.)
- *
- * The caller needs to call mb_charinit() before the first call, to
- * set up the multibyte shift state for a range of characters.
- */
-
-/**/
-mod_export char *
-wcs_nicechar_sel(wchar_t c, size_t *widthp, char **swidep, int quotable)
-{
- static char *buf;
- static int bufalloc = 0, newalloc;
- char *s, *mbptr;
- int ret = 0;
- VARARR(char, mbstr, MB_CUR_MAX);
-
- /*
- * We want buf to persist beyond the return. MB_CUR_MAX and hence
- * NICECHAR_MAX may not be constant, so we have to allocate this at
- * run time. (We could probably get away with just allocating a
- * large buffer, in practice.) For efficiency, only reallocate if
- * we really need to, since this function will be called frequently.
- */
- newalloc = NICECHAR_MAX;
- if (bufalloc != newalloc)
- {
- bufalloc = newalloc;
- buf = (char *)zrealloc(buf, bufalloc);
- }
-
- s = buf;
- if (!WC_ISPRINT(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) {
- if (c == 0x7f) {
- if (quotable) {
- *s++ = '\\';
- *s++ = 'C';
- *s++ = '-';
- } else
- *s++ = '^';
- c = '?';
- } else if (c == L'\n') {
- *s++ = '\\';
- c = 'n';
- } else if (c == L'\t') {
- *s++ = '\\';
- c = 't';
- } else if (c < 0x20) {
- if (quotable) {
- *s++ = '\\';
- *s++ = 'C';
- *s++ = '-';
- } else
- *s++ = '^';
- c += 0x40;
- } else if (c >= 0x80) {
- ret = -1;
- }
- }
-
- if (ret != -1)
- ret = wcrtomb(mbstr, c, &mb_shiftstate);
-
- if (ret == -1) {
- memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
- /*
- * Can't or don't want to convert character: use UCS-2 or
- * UCS-4 code in print escape format.
- *
- * This comparison fails and generates a compiler warning
- * if wchar_t is 16 bits, but the code is still correct.
- */
- if (c >= 0x10000) {
- sprintf(buf, "\\U%.8x", (unsigned int)c);
- if (widthp)
- *widthp = 10;
- } else if (c >= 0x100) {
- sprintf(buf, "\\u%.4x", (unsigned int)c);
- if (widthp)
- *widthp = 6;
- } else {
- strcpy(buf, nicechar((int)c));
- /*
- * There may be metafied characters from nicechar(),
- * so compute width and end position independently.
- */
- if (widthp)
- *widthp = ztrlen(buf);
- if (swidep)
- *swidep = buf + strlen(buf);
- return buf;
- }
- if (swidep)
- *swidep = widthp ? buf + *widthp : buf;
- return buf;
- }
-
- if (widthp) {
- int wcw = WCWIDTH(c);
- *widthp = (s - buf);
- if (wcw >= 0)
- *widthp += wcw;
- else
- (*widthp)++;
- }
- if (swidep)
- *swidep = s;
- for (mbptr = mbstr; ret; s++, mbptr++, ret--) {
- DPUTS(s >= buf + NICECHAR_MAX,
- "BUG: buffer too small in wcs_nicechar");
- if (imeta(*mbptr)) {
- *s++ = Meta;
- DPUTS(s >= buf + NICECHAR_MAX,
- "BUG: buffer too small for metafied char in wcs_nicechar");
- *s = *mbptr ^ 32;
- } else {
- *s = *mbptr;
- }
- }
- *s = 0;
- return buf;
-}
-
-/**/
-mod_export char *
-wcs_nicechar(wchar_t c, size_t *widthp, char **swidep)
-{
- return wcs_nicechar_sel(c, widthp, swidep, 0);
-}
-
-/*
- * Return 1 if wcs_nicechar() would reformat this character for display.
- */
-
-/**/
-mod_export int is_wcs_nicechar(wchar_t c)
-{
- if (!WC_ISPRINT(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) {
- if (c == 0x7f || c == L'\n' || c == L'\t' || c < 0x20)
- return 1;
- if (c >= 0x80) {
- return (c >= 0x100);
- }
- }
- return 0;
-}
-
-/**/
-mod_export int
-zwcwidth(wint_t wc)
-{
- int wcw;
- /* assume a single-byte character if not valid */
- if (wc == WEOF || unset(MULTIBYTE))
- return 1;
- wcw = WCWIDTH(wc);
- /* if not printable, assume width 1 */
- if (wcw < 0)
- return 1;
- return wcw;
-}
-
-/**/
-#endif /* MULTIBYTE_SUPPORT */
-
-/*
- * Search the path for prog and return the file name.
- * The returned value is unmetafied and in the unmeta storage
- * area (N.B. should be duplicated if not used immediately and not
- * equal to *namep).
- *
- * If namep is not NULL, *namep is set to the metafied programme
- * name, which is in heap storage.
- */
-/**/
-char *
-pathprog(char *prog, char **namep)
-{
- char **pp, ppmaxlen = 0, *buf, *funmeta;
- struct stat st;
-
- for (pp = path; *pp; pp++)
- {
- int len = strlen(*pp);
- if (len > ppmaxlen)
- ppmaxlen = len;
- }
- buf = zhalloc(ppmaxlen + strlen(prog) + 2);
- for (pp = path; *pp; pp++) {
- sprintf(buf, "%s/%s", *pp, prog);
- funmeta = unmeta(buf);
- if (access(funmeta, F_OK) == 0 &&
- stat(funmeta, &st) >= 0 &&
- !S_ISDIR(st.st_mode)) {
- if (namep)
- *namep = buf;
- return funmeta;
- }
- }
-
- return NULL;
-}
-
-/* get a symlink-free pathname for s relative to PWD */
-
-/**/
-char *
-findpwd(char *s)
-{
- char *t;
-
- if (*s == '/')
- return xsymlink(s, 0);
- s = tricat((pwd[1]) ? pwd : "", "/", s);
- t = xsymlink(s, 0);
- zsfree(s);
- return t;
-}
-
-/* Check whether a string contains the *
- * name of the present directory. */
-
-/**/
-int
-ispwd(char *s)
-{
- struct stat sbuf, tbuf;
-
- /* POSIX: environment PWD must be absolute */
- if (*s != '/')
- return 0;
-
- if (stat((s = unmeta(s)), &sbuf) == 0 && stat(".", &tbuf) == 0)
- if (sbuf.st_dev == tbuf.st_dev && sbuf.st_ino == tbuf.st_ino) {
- /* POSIX: No element of $PWD may be "." or ".." */
- while (*s) {
- if (s[0] == '.' &&
- (!s[1] || s[1] == '/' ||
- (s[1] == '.' && (!s[2] || s[2] == '/'))))
- break;
- while (*s++ != '/' && *s)
- continue;
- }
- return !*s;
- }
- return 0;
-}
-
-static char xbuf[PATH_MAX*2+1];
-
-/**/
-static char **
-slashsplit(char *s)
-{
- char *t, **r, **q;
- int t0;
-
- if (!*s)
- return (char **) zshcalloc(sizeof(char *));
-
- for (t = s, t0 = 0; *t; t++)
- if (*t == '/')
- t0++;
- q = r = (char **) zalloc(sizeof(char *) * (t0 + 2));
-
- while ((t = strchr(s, '/'))) {
- *q++ = ztrduppfx(s, t - s);
- while (*t == '/')
- t++;
- if (!*t) {
- *q = NULL;
- return r;
- }
- s = t;
- }
- *q++ = ztrdup(s);
- *q = NULL;
- return r;
-}
-
-/* expands symlinks and .. or . expressions */
-
-/**/
-static int
-xsymlinks(char *s, int full)
-{
- char **pp, **opp;
- char xbuf2[PATH_MAX*3+1], xbuf3[PATH_MAX*2+1];
- int t0, ret = 0;
- zulong xbuflen = strlen(xbuf), pplen;
-
- opp = pp = slashsplit(s);
- for (; xbuflen < sizeof(xbuf) && *pp && ret >= 0; pp++) {
- if (!strcmp(*pp, "."))
- continue;
- if (!strcmp(*pp, "..")) {
- char *p;
-
- if (!strcmp(xbuf, "/"))
- continue;
- if (!*xbuf)
- continue;
- p = xbuf + xbuflen;
- while (*--p != '/')
- xbuflen--;
- *p = '\0';
- /* The \0 isn't included in the length */
- xbuflen--;
- continue;
- }
- /* Includes null byte. */
- pplen = strlen(*pp) + 1;
- if (xbuflen + pplen + 1 > sizeof(xbuf2)) {
- *xbuf = 0;
- ret = -1;
- break;
- }
- memcpy(xbuf2, xbuf, xbuflen);
- xbuf2[xbuflen] = '/';
- memcpy(xbuf2 + xbuflen + 1, *pp, pplen);
- t0 = readlink(unmeta(xbuf2), xbuf3, PATH_MAX);
- if (t0 == -1) {
- if ((xbuflen += pplen) < sizeof(xbuf)) {
- strcat(xbuf, "/");
- strcat(xbuf, *pp);
- } else {
- *xbuf = 0;
- ret = -1;
- break;
- }
- } else {
- ret = 1;
- metafy(xbuf3, t0, META_NOALLOC);
- if (!full) {
- /*
- * If only one expansion requested, ensure the
- * full path is in xbuf.
- */
- zulong len = xbuflen;
- if (*xbuf3 == '/')
- strcpy(xbuf, xbuf3);
- else if ((len += strlen(xbuf3) + 1) < sizeof(xbuf)) {
- strcpy(xbuf + xbuflen, "/");
- strcpy(xbuf + xbuflen + 1, xbuf3);
- } else {
- *xbuf = 0;
- ret = -1;
- break;
- }
-
- while (*++pp) {
- zulong newlen = len + strlen(*pp) + 1;
- if (newlen < sizeof(xbuf)) {
- strcpy(xbuf + len, "/");
- strcpy(xbuf + len + 1, *pp);
- len = newlen;
- } else {
- *xbuf = 01;
- ret = -1;
- break;
- }
- }
- /*
- * No need to update xbuflen, we're finished
- * the expansion (for now).
- */
- break;
- }
- if (*xbuf3 == '/') {
- strcpy(xbuf, "");
- if (xsymlinks(xbuf3 + 1, 1) < 0)
- ret = -1;
- else
- xbuflen = strlen(xbuf);
- } else
- if (xsymlinks(xbuf3, 1) < 0)
- ret = -1;
- else
- xbuflen = strlen(xbuf);
- }
- }
- freearray(opp);
- return ret;
-}
-
-/*
- * expand symlinks in s, and remove other weird things:
- * note that this always expands symlinks.
- *
- * 'heap' indicates whether to malloc() or allocate on the heap.
- */
-
-/**/
-char *
-xsymlink(char *s, int heap)
-{
- if (*s != '/')
- return NULL;
- *xbuf = '\0';
- if (xsymlinks(s + 1, 1) < 0)
- zwarn("path expansion failed, using root directory");
- if (!*xbuf)
- return heap ? dupstring("/") : ztrdup("/");
- return heap ? dupstring(xbuf) : ztrdup(xbuf);
-}
-
-/**/
-void
-print_if_link(char *s, int all)
-{
- if (*s == '/') {
- *xbuf = '\0';
- if (all) {
- char *start = s + 1;
- char xbuflink[PATH_MAX+1];
- for (;;) {
- if (xsymlinks(start, 0) > 0) {
- printf(" -> ");
- zputs(*xbuf ? xbuf : "/", stdout);
- if (!*xbuf)
- break;
- strcpy(xbuflink, xbuf);
- start = xbuflink + 1;
- *xbuf = '\0';
- } else {
- break;
- }
- }
- } else {
- if (xsymlinks(s + 1, 1) > 0)
- printf(" -> "), zputs(*xbuf ? xbuf : "/", stdout);
- }
- }
-}
-
-/* print a directory */
-
-/**/
-void
-fprintdir(char *s, FILE *f)
-{
- Nameddir d = finddir(s);
-
- if (!d)
- fputs(unmeta(s), f);
- else {
- putc('~', f);
- fputs(unmeta(d->node.nam), f);
- fputs(unmeta(s + strlen(d->dir)), f);
- }
-}
-
-/*
- * Substitute a directory using a name.
- * If there is none, return the original argument.
- *
- * At this level all strings involved are metafied.
- */
-
-/**/
-char *
-substnamedir(char *s)
-{
- Nameddir d = finddir(s);
-
- if (!d)
- return quotestring(s, QT_BACKSLASH);
- return zhtricat("~", d->node.nam, quotestring(s + strlen(d->dir),
- QT_BACKSLASH));
-}
-
-
-/* Returns the current username. It caches the username *
- * and uid to try to avoid requerying the password files *
- * or NIS/NIS+ database. */
-
-/**/
-uid_t cached_uid;
-/**/
-char *cached_username;
-
-/**/
-char *
-get_username(void)
-{
-#ifdef HAVE_GETPWUID
- struct passwd *pswd;
- uid_t current_uid;
-
- current_uid = getuid();
- if (current_uid != cached_uid) {
- cached_uid = current_uid;
- zsfree(cached_username);
- if ((pswd = getpwuid(current_uid)))
- cached_username = ztrdup(pswd->pw_name);
- else
- cached_username = ztrdup("");
- }
-#else /* !HAVE_GETPWUID */
- cached_uid = getuid();
-#endif /* !HAVE_GETPWUID */
- return cached_username;
-}
-
-/* static variables needed by finddir(). */
-
-static char *finddir_full;
-static Nameddir finddir_last;
-static int finddir_best;
-
-/* ScanFunc used by finddir(). */
-
-/**/
-static void
-finddir_scan(HashNode hn, UNUSED(int flags))
-{
- Nameddir nd = (Nameddir) hn;
-
- if(nd->diff > finddir_best && !dircmp(nd->dir, finddir_full)
- && !(nd->node.flags & ND_NOABBREV)) {
- finddir_last=nd;
- finddir_best=nd->diff;
- }
-}
-
-/*
- * See if a path has a named directory as its prefix.
- * If passed a NULL argument, it will invalidate any
- * cached information.
- *
- * s here is metafied.
- */
-
-/**/
-Nameddir
-finddir(char *s)
-{
- static struct nameddir homenode = { {NULL, "", 0}, NULL, 0 };
- static int ffsz;
- char **ares;
- int len;
-
- /* Invalidate directory cache if argument is NULL. This is called *
- * whenever a node is added to or removed from the hash table, and *
- * whenever the value of $HOME changes. (On startup, too.) */
- if (!s) {
- homenode.dir = home ? home : "";
- homenode.diff = home ? strlen(home) : 0;
- if(homenode.diff==1)
- homenode.diff = 0;
- if(!finddir_full)
- finddir_full = zalloc(ffsz = PATH_MAX+1);
- finddir_full[0] = 0;
- return finddir_last = NULL;
- }
-
-#if 0
- /*
- * It's not safe to use the cache while we have function
- * transformations, and it's not clear it's worth the
- * complexity of guessing here whether subst_string_by_hook
- * is going to turn up the goods.
- */
- if (!strcmp(s, finddir_full) && *finddir_full)
- return finddir_last;
-#endif
-
- if ((int)strlen(s) >= ffsz) {
- free(finddir_full);
- finddir_full = zalloc(ffsz = strlen(s) * 2);
- }
- strcpy(finddir_full, s);
- finddir_best=0;
- finddir_last=NULL;
- finddir_scan(&homenode.node, 0);
- scanhashtable(nameddirtab, 0, 0, 0, finddir_scan, 0);
-
- ares = subst_string_by_hook("zsh_directory_name", "d", finddir_full);
- if (ares && arrlen_ge(ares, 2) &&
- (len = (int)zstrtol(ares[1], NULL, 10)) > finddir_best) {
- /* better duplicate this string since it's come from REPLY */
- finddir_last = (Nameddir)hcalloc(sizeof(struct nameddir));
- finddir_last->node.nam = zhtricat("[", dupstring(ares[0]), "]");
- finddir_last->dir = dupstrpfx(finddir_full, len);
- finddir_last->diff = len - strlen(finddir_last->node.nam);
- finddir_best = len;
- }
-
- return finddir_last;
-}
-
-/* add a named directory */
-
-/**/
-mod_export void
-adduserdir(char *s, char *t, int flags, int always)
-{
- Nameddir nd;
- char *eptr;
-
- /* We don't maintain a hash table in non-interactive shells. */
- if (!interact)
- return;
-
- /* The ND_USERNAME flag means that this possible hash table *
- * entry is derived from a passwd entry. Such entries are *
- * subordinate to explicitly generated entries. */
- if ((flags & ND_USERNAME) && nameddirtab->getnode2(nameddirtab, s))
- return;
-
- /* Normal parameter assignments generate calls to this function, *
- * with always==0. Unless the AUTO_NAME_DIRS option is set, we *
- * don't let such assignments actually create directory names. *
- * Instead, a reference to the parameter as a directory name can *
- * cause the actual creation of the hash table entry. */
- if (!always && unset(AUTONAMEDIRS) &&
- !nameddirtab->getnode2(nameddirtab, s))
- return;
-
- if (!t || *t != '/' || strlen(t) >= PATH_MAX) {
- /* We can't use this value as a directory, so simply remove *
- * the corresponding entry in the hash table, if any. */
- HashNode hn = nameddirtab->removenode(nameddirtab, s);
-
- if(hn)
- nameddirtab->freenode(hn);
- return;
- }
-
- /* add the name */
- nd = (Nameddir) zshcalloc(sizeof *nd);
- nd->node.flags = flags;
- eptr = t + strlen(t);
- while (eptr > t && eptr[-1] == '/')
- eptr--;
- if (eptr == t) {
- /*
- * Don't abbreviate multiple slashes at the start of a
- * named directory, since these are sometimes used for
- * special purposes.
- */
- nd->dir = metafy(t, -1, META_DUP);
- } else
- nd->dir = metafy(t, eptr - t, META_DUP);
- /* The variables PWD and OLDPWD are not to be displayed as ~PWD etc. */
- if (!strcmp(s, "PWD") || !strcmp(s, "OLDPWD"))
- nd->node.flags |= ND_NOABBREV;
- nameddirtab->addnode(nameddirtab, metafy(s, -1, META_DUP), nd);
-}
-
-/* Get a named directory: this function can cause a directory name *
- * to be added to the hash table, if it isn't there already. */
-
-/**/
-char *
-getnameddir(char *name)
-{
- Param pm;
- char *str;
- Nameddir nd;
-
- /* Check if it is already in the named directory table */
- if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)))
- return dupstring(nd->dir);
-
- /* Check if there is a scalar parameter with this name whose value *
- * begins with a `/'. If there is, add it to the hash table and *
- * return the new value. */
- if ((pm = (Param) paramtab->getnode(paramtab, name)) &&
- (PM_TYPE(pm->node.flags) == PM_SCALAR) &&
- (str = getsparam(name)) && *str == '/') {
- pm->node.flags |= PM_NAMEDDIR;
- adduserdir(name, str, 0, 1);
- return str;
- }
-
-#ifdef HAVE_GETPWNAM
- {
- /* Retrieve an entry from the password table/database for this user. */
- struct passwd *pw;
- if ((pw = getpwnam(name))) {
- char *dir = isset(CHASELINKS) ? xsymlink(pw->pw_dir, 0)
- : ztrdup(pw->pw_dir);
- if (dir) {
- adduserdir(name, dir, ND_USERNAME, 1);
- str = dupstring(dir);
- zsfree(dir);
- return str;
- } else
- return dupstring(pw->pw_dir);
- }
- }
-#endif /* HAVE_GETPWNAM */
-
- /* There are no more possible sources of directory names, so give up. */
- return NULL;
-}
-
-/*
- * Compare directories. Both are metafied.
- */
-
-/**/
-static int
-dircmp(char *s, char *t)
-{
- if (s) {
- for (; *s == *t; s++, t++)
- if (!*s)
- return 0;
- if (!*s && *t == '/')
- return 0;
- }
- return 1;
-}
-
-/*
- * Extra functions to call before displaying the prompt.
- * The data is a Prepromptfn.
- */
-
-static LinkList prepromptfns;
-
-/* Add a function to the list of pre-prompt functions. */
-
-/**/
-mod_export void
-addprepromptfn(voidvoidfnptr_t func)
-{
- Prepromptfn ppdat = (Prepromptfn)zalloc(sizeof(struct prepromptfn));
- ppdat->func = func;
- if (!prepromptfns)
- prepromptfns = znewlinklist();
- zaddlinknode(prepromptfns, ppdat);
-}
-
-/* Remove a function from the list of pre-prompt functions. */
-
-/**/
-mod_export void
-delprepromptfn(voidvoidfnptr_t func)
-{
- LinkNode ln;
-
- for (ln = firstnode(prepromptfns); ln; ln = nextnode(ln)) {
- Prepromptfn ppdat = (Prepromptfn)getdata(ln);
- if (ppdat->func == func) {
- (void)remnode(prepromptfns, ln);
- zfree(ppdat, sizeof(struct prepromptfn));
- return;
- }
- }
-#ifdef DEBUG
- dputs("BUG: failed to delete node from prepromptfns");
-#endif
-}
-
-/*
- * Functions to call at a particular time even if not at
- * the prompt. This is handled by zle. The data is a
- * Timedfn. The functions must be in time order, but this
- * is enforced by addtimedfn().
- *
- * Note on debugging: the code in sched.c currently assumes it's
- * the only user of timedfns for the purposes of checking whether
- * there's a function on the list. If this becomes no longer the case,
- * the DPUTS() tests in sched.c need rewriting.
- */
-
-/**/
-mod_export LinkList timedfns;
-
-/* Add a function to the list of timed functions. */
-
-/**/
-mod_export void
-addtimedfn(voidvoidfnptr_t func, time_t when)
-{
- Timedfn tfdat = (Timedfn)zalloc(sizeof(struct timedfn));
- tfdat->func = func;
- tfdat->when = when;
-
- if (!timedfns) {
- timedfns = znewlinklist();
- zaddlinknode(timedfns, tfdat);
- } else {
- LinkNode ln = firstnode(timedfns);
-
- /*
- * Insert the new element in the linked list. We do
- * rather too much work here since the standard
- * functions insert after a given node, whereas we
- * want to insert the new data before the first element
- * with a greater time.
- *
- * In practice, the only use of timed functions is
- * sched, which only adds the one function; so this
- * whole branch isn't used beyond the following block.
- */
- if (!ln) {
- zaddlinknode(timedfns, tfdat);
- return;
- }
- for (;;) {
- Timedfn tfdat2;
- LinkNode next = nextnode(ln);
- if (!next) {
- zaddlinknode(timedfns, tfdat);
- return;
- }
- tfdat2 = (Timedfn)getdata(next);
- if (when < tfdat2->when) {
- zinsertlinknode(timedfns, ln, tfdat);
- return;
- }
- ln = next;
- }
- }
-}
-
-/*
- * Delete a function from the list of timed functions.
- * Note that if the function apperas multiple times only
- * the first occurrence will be removed.
- *
- * Note also that when zle calls the function it does *not*
- * automatically delete the entry from the list. That must
- * be done by the function called. This is recommended as otherwise
- * the function will keep being called immediately. (It just so
- * happens this "feature" fits in well with the only current use
- * of timed functions.)
- */
-
-/**/
-mod_export void
-deltimedfn(voidvoidfnptr_t func)
-{
- LinkNode ln;
-
- for (ln = firstnode(timedfns); ln; ln = nextnode(ln)) {
- Timedfn ppdat = (Timedfn)getdata(ln);
- if (ppdat->func == func) {
- (void)remnode(timedfns, ln);
- zfree(ppdat, sizeof(struct timedfn));
- return;
- }
- }
-#ifdef DEBUG
- dputs("BUG: failed to delete node from timedfns");
-#endif
-}
-
-/* the last time we checked mail */
-
-/**/
-time_t lastmailcheck;
-
-/* the last time we checked the people in the WATCH variable */
-
-/**/
-time_t lastwatch;
-
-/*
- * Call a function given by "name" with optional arguments
- * "lnklist". If these are present the first argument is the function name.
- *
- * If "arrayp" is not zero, we also look through
- * the array "name"_functions and execute functions found there.
- *
- * If "retval" is not NULL, the return value of the first hook function to
- * return non-zero is stored in *"retval". The return value is not otherwise
- * available as the calling context is restored.
- *
- * Returns 0 if at least one function was called (regardless of that function's
- * exit status), and 1 otherwise.
- */
-
-/**/
-mod_export int
-callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval)
-{
- Shfunc shfunc;
- /*
- * Save stopmsg, since user doesn't get a chance to respond
- * to a list of jobs generated in a hook.
- */
- int osc = sfcontext, osm = stopmsg, stat = 1, ret = 0;
- int old_incompfunc = incompfunc;
-
- sfcontext = SFC_HOOK;
- incompfunc = 0;
-
- if ((shfunc = getshfunc(name))) {
- ret = doshfunc(shfunc, lnklst, 1);
- stat = 0;
- }
-
- if (arrayp) {
- char **arrptr;
- int namlen = strlen(name);
- VARARR(char, arrnam, namlen + HOOK_SUFFIX_LEN);
- memcpy(arrnam, name, namlen);
- memcpy(arrnam + namlen, HOOK_SUFFIX, HOOK_SUFFIX_LEN);
-
- if ((arrptr = getaparam(arrnam))) {
- arrptr = arrdup(arrptr);
- for (; *arrptr; arrptr++) {
- if ((shfunc = getshfunc(*arrptr))) {
- int newret = doshfunc(shfunc, lnklst, 1);
- if (!ret)
- ret = newret;
- stat = 0;
- }
- }
- }
- }
-
- sfcontext = osc;
- stopmsg = osm;
- incompfunc = old_incompfunc;
-
- if (retval)
- *retval = ret;
- return stat;
-}
-
-/* do pre-prompt stuff */
-
-/**/
-void
-preprompt(void)
-{
- static time_t lastperiodic;
- time_t currentmailcheck;
- LinkNode ln;
- zlong period = getiparam("PERIOD");
- zlong mailcheck = getiparam("MAILCHECK");
-
- /*
- * Handle any pending window size changes before we compute prompts,
- * then block them again to avoid interrupts during prompt display.
- */
- winch_unblock();
- winch_block();
-
- if (isset(PROMPTSP) && isset(PROMPTCR) && !use_exit_printed && shout) {
- /* The PROMPT_SP heuristic will move the prompt down to a new line
- * if there was any dangling output on the line (assuming the terminal
- * has automatic margins, but we try even if hasam isn't set).
- * Unfortunately it interacts badly with ZLE displaying message
- * when ^D has been pressed. So just disable PROMPT_SP logic in
- * this case */
- char *eolmark = getsparam("PROMPT_EOL_MARK");
- char *str;
- int percents = opts[PROMPTPERCENT], w = 0;
- if (!eolmark)
- eolmark = "%B%S%#%s%b";
- opts[PROMPTPERCENT] = 1;
- str = promptexpand(eolmark, 1, NULL, NULL, NULL);
- countprompt(str, &w, 0, -1);
- opts[PROMPTPERCENT] = percents;
- zputs(str, shout);
- fprintf(shout, "%*s\r%*s\r", (int)zterm_columns - w - !hasxn,
- "", w, "");
- fflush(shout);
- free(str);
- }
-
- /* If NOTIFY is not set, then check for completed *
- * jobs before we print the prompt. */
- if (unset(NOTIFY))
- scanjobs();
- if (errflag)
- return;
-
- /* If a shell function named "precmd" exists, *
- * then execute it. */
- callhookfunc("precmd", NULL, 1, NULL);
- if (errflag)
- return;
-
- /* If 1) the parameter PERIOD exists, 2) a hook function for *
- * "periodic" exists, 3) it's been greater than PERIOD since we *
- * executed any such hook, then execute it now. */
- if (period && ((zlong)time(NULL) > (zlong)lastperiodic + period) &&
- !callhookfunc("periodic", NULL, 1, NULL))
- lastperiodic = time(NULL);
- if (errflag)
- return;
-
- /* If WATCH is set, then check for the *
- * specified login/logout events. */
- if (watch) {
- if ((int) difftime(time(NULL), lastwatch) > getiparam("LOGCHECK")) {
- dowatch();
- lastwatch = time(NULL);
- }
- }
- if (errflag)
- return;
-
- /* Check mail */
- currentmailcheck = time(NULL);
- if (mailcheck &&
- (zlong) difftime(currentmailcheck, lastmailcheck) > mailcheck) {
- char *mailfile;
-
- if (mailpath && *mailpath && **mailpath)
- checkmailpath(mailpath);
- else {
- queue_signals();
- if ((mailfile = getsparam("MAIL")) && *mailfile) {
- char *x[2];
-
- x[0] = mailfile;
- x[1] = NULL;
- checkmailpath(x);
- }
- unqueue_signals();
- }
- lastmailcheck = currentmailcheck;
- }
-
- if (prepromptfns) {
- for(ln = firstnode(prepromptfns); ln; ln = nextnode(ln)) {
- Prepromptfn ppnode = (Prepromptfn)getdata(ln);
- ppnode->func();
- }
- }
-}
-
-/**/
-static void
-checkmailpath(char **s)
-{
- struct stat st;
- char *v, *u, c;
-
- while (*s) {
- for (v = *s; *v && *v != '?'; v++);
- c = *v;
- *v = '\0';
- if (c != '?')
- u = NULL;
- else
- u = v + 1;
- if (**s == 0) {
- *v = c;
- zerr("empty MAILPATH component: %s", *s);
- } else if (mailstat(unmeta(*s), &st) == -1) {
- if (errno != ENOENT)
- zerr("%e: %s", errno, *s);
- } else if (S_ISDIR(st.st_mode)) {
- LinkList l;
- DIR *lock = opendir(unmeta(*s));
- char buf[PATH_MAX * 2 + 1], **arr, **ap;
- int buflen, ct = 1;
-
- if (lock) {
- char *fn;
-
- pushheap();
- l = newlinklist();
- while ((fn = zreaddir(lock, 1)) && !errflag) {
- if (u)
- buflen = snprintf(buf, sizeof(buf), "%s/%s?%s", *s, fn, u);
- else
- buflen = snprintf(buf, sizeof(buf), "%s/%s", *s, fn);
- if (buflen < 0 || buflen >= (int)sizeof(buf))
- continue;
- addlinknode(l, dupstring(buf));
- ct++;
- }
- closedir(lock);
- ap = arr = (char **) zhalloc(ct * sizeof(char *));
-
- while ((*ap++ = (char *)ugetnode(l)));
- checkmailpath(arr);
- popheap();
- }
- } else if (shout) {
- if (st.st_size && st.st_atime <= st.st_mtime &&
- st.st_mtime >= lastmailcheck) {
- if (!u) {
- fprintf(shout, "You have new mail.\n");
- fflush(shout);
- } else {
- char *usav;
- int uusav = underscoreused;
-
- usav = zalloc(underscoreused);
-
- if (usav)
- memcpy(usav, zunderscore, underscoreused);
-
- setunderscore(*s);
-
- u = dupstring(u);
- if (!parsestr(&u)) {
- singsub(&u);
- zputs(u, shout);
- fputc('\n', shout);
- fflush(shout);
- }
- if (usav) {
- setunderscore(usav);
- zfree(usav, uusav);
- }
- }
- }
- if (isset(MAILWARNING) && st.st_atime > st.st_mtime &&
- st.st_atime > lastmailcheck && st.st_size) {
- fprintf(shout, "The mail in %s has been read.\n", unmeta(*s));
- fflush(shout);
- }
- }
- *v = c;
- s++;
- }
-}
-
-/* This prints the XTRACE prompt. */
-
-/**/
-FILE *xtrerr = 0;
-
-/**/
-void
-printprompt4(void)
-{
- if (!xtrerr)
- xtrerr = stderr;
- if (prompt4) {
- int l, t = opts[XTRACE];
- char *s = dupstring(prompt4);
-
- opts[XTRACE] = 0;
- unmetafy(s, &l);
- s = unmetafy(promptexpand(metafy(s, l, META_NOALLOC),
- 0, NULL, NULL, NULL), &l);
- opts[XTRACE] = t;
-
- fprintf(xtrerr, "%s", s);
- free(s);
- }
-}
-
-/**/
-mod_export void
-freestr(void *a)
-{
- zsfree(a);
-}
-
-/**/
-mod_export void
-gettyinfo(struct ttyinfo *ti)
-{
- if (SHTTY != -1) {
-#ifdef HAVE_TERMIOS_H
-# ifdef HAVE_TCGETATTR
- if (tcgetattr(SHTTY, &ti->tio) == -1)
-# else
- if (ioctl(SHTTY, TCGETS, &ti->tio) == -1)
-# endif
- zerr("bad tcgets: %e", errno);
-#else
-# ifdef HAVE_TERMIO_H
- ioctl(SHTTY, TCGETA, &ti->tio);
-# else
- ioctl(SHTTY, TIOCGETP, &ti->sgttyb);
- ioctl(SHTTY, TIOCLGET, &ti->lmodes);
- ioctl(SHTTY, TIOCGETC, &ti->tchars);
- ioctl(SHTTY, TIOCGLTC, &ti->ltchars);
-# endif
-#endif
- }
-}
-
-/**/
-mod_export void
-settyinfo(struct ttyinfo *ti)
-{
- if (SHTTY != -1) {
-#ifdef HAVE_TERMIOS_H
-# ifdef HAVE_TCGETATTR
-# ifndef TCSADRAIN
-# define TCSADRAIN 1 /* XXX Princeton's include files are screwed up */
-# endif
- while (tcsetattr(SHTTY, TCSADRAIN, &ti->tio) == -1 && errno == EINTR)
- ;
-# else
- while (ioctl(SHTTY, TCSETS, &ti->tio) == -1 && errno == EINTR)
- ;
-# endif
- /* zerr("settyinfo: %e",errno);*/
-#else
-# ifdef HAVE_TERMIO_H
- ioctl(SHTTY, TCSETA, &ti->tio);
-# else
- ioctl(SHTTY, TIOCSETN, &ti->sgttyb);
- ioctl(SHTTY, TIOCLSET, &ti->lmodes);
- ioctl(SHTTY, TIOCSETC, &ti->tchars);
- ioctl(SHTTY, TIOCSLTC, &ti->ltchars);
-# endif
-#endif
- }
-}
-
-/* the default tty state */
-
-/**/
-mod_export struct ttyinfo shttyinfo;
-
-/* != 0 if we need to call resetvideo() */
-
-/**/
-mod_export int resetneeded;
-
-#ifdef TIOCGWINSZ
-/* window size changed */
-
-/**/
-mod_export int winchanged;
-#endif
-
-static int
-adjustlines(int signalled)
-{
- int oldlines = zterm_lines;
-
-#ifdef TIOCGWINSZ
- if (signalled || zterm_lines <= 0)
- zterm_lines = shttyinfo.winsize.ws_row;
- else
- shttyinfo.winsize.ws_row = zterm_lines;
-#endif /* TIOCGWINSZ */
- if (zterm_lines <= 0) {
- DPUTS(signalled && zterm_lines < 0,
- "BUG: Impossible TIOCGWINSZ rows");
- zterm_lines = tclines > 0 ? tclines : 24;
- }
-
- if (zterm_lines > 2)
- termflags &= ~TERM_SHORT;
- else
- termflags |= TERM_SHORT;
-
- return (zterm_lines != oldlines);
-}
-
-static int
-adjustcolumns(int signalled)
-{
- int oldcolumns = zterm_columns;
-
-#ifdef TIOCGWINSZ
- if (signalled || zterm_columns <= 0)
- zterm_columns = shttyinfo.winsize.ws_col;
- else
- shttyinfo.winsize.ws_col = zterm_columns;
-#endif /* TIOCGWINSZ */
- if (zterm_columns <= 0) {
- DPUTS(signalled && zterm_columns < 0,
- "BUG: Impossible TIOCGWINSZ cols");
- zterm_columns = tccolumns > 0 ? tccolumns : 80;
- }
-
- if (zterm_columns > 2)
- termflags &= ~TERM_NARROW;
- else
- termflags |= TERM_NARROW;
-
- return (zterm_columns != oldcolumns);
-}
-
-/* check the size of the window and adjust if necessary. *
- * The value of from: *
- * 0: called from update_job or setupvals *
- * 1: called from the SIGWINCH handler *
- * 2: called from the LINES parameter callback *
- * 3: called from the COLUMNS parameter callback */
-
-/**/
-void
-adjustwinsize(int from)
-{
- static int getwinsz = 1;
-#ifdef TIOCGWINSZ
- int ttyrows = shttyinfo.winsize.ws_row;
- int ttycols = shttyinfo.winsize.ws_col;
-#endif
- int resetzle = 0;
-
- if (getwinsz || from == 1) {
-#ifdef TIOCGWINSZ
- if (SHTTY == -1)
- return;
- if (ioctl(SHTTY, TIOCGWINSZ, (char *)&shttyinfo.winsize) == 0) {
- resetzle = (ttyrows != shttyinfo.winsize.ws_row ||
- ttycols != shttyinfo.winsize.ws_col);
- if (from == 0 && resetzle && ttyrows && ttycols)
- from = 1; /* Signal missed while a job owned the tty? */
- ttyrows = shttyinfo.winsize.ws_row;
- ttycols = shttyinfo.winsize.ws_col;
- } else {
- /* Set to value from environment on failure */
- shttyinfo.winsize.ws_row = zterm_lines;
- shttyinfo.winsize.ws_col = zterm_columns;
- resetzle = (from == 1);
- }
-#else
- resetzle = from == 1;
-#endif /* TIOCGWINSZ */
- } /* else
- return; */
-
- switch (from) {
- case 0:
- case 1:
- getwinsz = 0;
- /* Calling setiparam() here calls this function recursively, but *
- * because we've already called adjustlines() and adjustcolumns() *
- * here, recursive calls are no-ops unless a signal intervenes. *
- * The commented "else return;" above might be a safe shortcut, *
- * but I'm concerned about what happens on race conditions; e.g., *
- * suppose the user resizes his xterm during `eval $(resize)'? */
- if (adjustlines(from) && zgetenv("LINES"))
- setiparam("LINES", zterm_lines);
- if (adjustcolumns(from) && zgetenv("COLUMNS"))
- setiparam("COLUMNS", zterm_columns);
- getwinsz = 1;
- break;
- case 2:
- resetzle = adjustlines(0);
- break;
- case 3:
- resetzle = adjustcolumns(0);
- break;
- }
-
-#ifdef TIOCGWINSZ
- if (interact && from >= 2 &&
- (shttyinfo.winsize.ws_row != ttyrows ||
- shttyinfo.winsize.ws_col != ttycols)) {
- /* shttyinfo.winsize is already set up correctly */
- /* ioctl(SHTTY, TIOCSWINSZ, (char *)&shttyinfo.winsize); */
- }
-#endif /* TIOCGWINSZ */
-
- if (zleactive && resetzle) {
-#ifdef TIOCGWINSZ
- winchanged =
-#endif /* TIOCGWINSZ */
- resetneeded = 1;
- zleentry(ZLE_CMD_RESET_PROMPT);
- zleentry(ZLE_CMD_REFRESH);
- }
-}
-
-/*
- * Ensure the fdtable is large enough for fd, and that the
- * maximum fd is set appropriately.
- */
-static void
-check_fd_table(int fd)
-{
- if (fd <= max_zsh_fd)
- return;
-
- if (fd >= fdtable_size) {
- int old_size = fdtable_size;
- while (fd >= fdtable_size)
- fdtable = zrealloc(fdtable,
- (fdtable_size *= 2)*sizeof(*fdtable));
- memset(fdtable + old_size, 0,
- (fdtable_size - old_size) * sizeof(*fdtable));
- }
- max_zsh_fd = fd;
-}
-
-/* Move a fd to a place >= 10 and mark the new fd in fdtable. If the fd *
- * is already >= 10, it is not moved. If it is invalid, -1 is returned. */
-
-/**/
-mod_export int
-movefd(int fd)
-{
- if(fd != -1 && fd < 10) {
-#ifdef F_DUPFD
- int fe = fcntl(fd, F_DUPFD, 10);
-#else
- int fe = movefd(dup(fd));
-#endif
- /*
- * To close or not to close if fe is -1?
- * If it is -1, we haven't moved the fd, so if we close
- * it we lose it; but we're probably not going to be able
- * to use it in situ anyway. So probably better to avoid a leak.
- */
- zclose(fd);
- fd = fe;
- }
- if(fd != -1) {
- check_fd_table(fd);
- fdtable[fd] = FDT_INTERNAL;
- }
- return fd;
-}
-
-/*
- * Move fd x to y. If x == -1, fd y is closed.
- * Returns y for success, -1 for failure.
- */
-
-/**/
-mod_export int
-redup(int x, int y)
-{
- int ret = y;
-
- if(x < 0)
- zclose(y);
- else if (x != y) {
- if (dup2(x, y) == -1) {
- ret = -1;
- } else {
- check_fd_table(y);
- fdtable[y] = fdtable[x];
- if (fdtable[y] == FDT_FLOCK || fdtable[y] == FDT_FLOCK_EXEC)
- fdtable[y] = FDT_INTERNAL;
- }
- /*
- * Closing any fd to the locked file releases the lock.
- * This isn't expected to happen, it's here for completeness.
- */
- if (fdtable[x] == FDT_FLOCK)
- fdtable_flocks--;
- zclose(x);
- }
-
- return ret;
-}
-
-/*
- * Add an fd opened ithin a module.
- *
- * fdt is the type of the fd; see the FDT_ definitions in zsh.h.
- * The most likely falures are:
- *
- * FDT_EXTERNAL: the fd can be used within the shell for normal I/O but
- * it will not be closed automatically or by normal shell syntax.
- *
- * FDT_MODULE: as FDT_EXTERNAL, but it can only be closed by the module
- * (which should included zclose() as part of the sequence), not by
- * the standard shell syntax for closing file descriptors.
- *
- * FDT_INTERNAL: fd is treated like others created by the shell for
- * internal use; it can be closed and will be closed by the shell if it
- * exec's or performs an exec with a fork optimised out.
- *
- * Safe if fd is -1 to indicate failure.
- */
-/**/
-mod_export void
-addmodulefd(int fd, int fdt)
-{
- if (fd >= 0) {
- check_fd_table(fd);
- fdtable[fd] = fdt;
- }
-}
-
-/**/
-
-/*
- * Indicate that an fd has a file lock; if cloexec is 1 it will be closed
- * on exec.
- * The fd should already be known to fdtable (e.g. by movefd).
- * Note the fdtable code doesn't care what sort of lock
- * is used; this simply prevents the main shell exiting prematurely
- * when it holds a lock.
- */
-
-/**/
-mod_export void
-addlockfd(int fd, int cloexec)
-{
- if (cloexec) {
- if (fdtable[fd] != FDT_FLOCK)
- fdtable_flocks++;
- fdtable[fd] = FDT_FLOCK;
- } else {
- fdtable[fd] = FDT_FLOCK_EXEC;
- }
-}
-
-/* Close the given fd, and clear it from fdtable. */
-
-/**/
-mod_export int
-zclose(int fd)
-{
- if (fd >= 0) {
- /*
- * Careful: we allow closing of arbitrary fd's, beyond
- * max_zsh_fd. In that case we don't try anything clever.
- */
- if (fd <= max_zsh_fd) {
- if (fdtable[fd] == FDT_FLOCK)
- fdtable_flocks--;
- fdtable[fd] = FDT_UNUSED;
- while (max_zsh_fd > 0 && fdtable[max_zsh_fd] == FDT_UNUSED)
- max_zsh_fd--;
- if (fd == coprocin)
- coprocin = -1;
- if (fd == coprocout)
- coprocout = -1;
- }
- return close(fd);
- }
- return -1;
-}
-
-/*
- * Close an fd returning 0 if used for locking; return -1 if it isn't.
- */
-
-/**/
-mod_export int
-zcloselockfd(int fd)
-{
- if (fd > max_zsh_fd)
- return -1;
- if (fdtable[fd] != FDT_FLOCK && fdtable[fd] != FDT_FLOCK_EXEC)
- return -1;
- zclose(fd);
- return 0;
-}
-
-#ifdef HAVE__MKTEMP
-extern char *_mktemp(char *);
-#endif
-
-/* Get a unique filename for use as a temporary file. If "prefix" is
- * NULL, the name is relative to $TMPPREFIX; If it is non-NULL, the
- * unique suffix includes a prefixed '.' for improved readability. If
- * "use_heap" is true, we allocate the returned name on the heap.
- * The string passed as "prefix" is expected to be metafied. */
-
-/**/
-mod_export char *
-gettempname(const char *prefix, int use_heap)
-{
- char *ret, *suffix = prefix ? ".XXXXXX" : "XXXXXX";
-
- queue_signals();
- if (!prefix && !(prefix = getsparam("TMPPREFIX")))
- prefix = DEFAULT_TMPPREFIX;
- if (use_heap)
- ret = dyncat(unmeta(prefix), suffix);
- else
- ret = bicat(unmeta(prefix), suffix);
-
-#ifdef HAVE__MKTEMP
- /* Zsh uses mktemp() safely, so silence the warnings */
- ret = (char *) _mktemp(ret);
-#else
- ret = (char *) mktemp(ret);
-#endif
- unqueue_signals();
-
- return ret;
-}
-
-/* The gettempfile() "prefix" is expected to be metafied, see hist.c
- * and gettempname(). */
-
-/**/
-mod_export int
-gettempfile(const char *prefix, int use_heap, char **tempname)
-{
- char *fn;
- int fd;
- mode_t old_umask;
-#if HAVE_MKSTEMP
- char *suffix = prefix ? ".XXXXXX" : "XXXXXX";
-
- queue_signals();
- old_umask = umask(0177);
- if (!prefix && !(prefix = getsparam("TMPPREFIX")))
- prefix = DEFAULT_TMPPREFIX;
- if (use_heap)
- fn = dyncat(unmeta(prefix), suffix);
- else
- fn = bicat(unmeta(prefix), suffix);
-
- fd = mkstemp(fn);
- if (fd < 0) {
- if (!use_heap)
- free(fn);
- fn = NULL;
- }
-#else
- int failures = 0;
-
- queue_signals();
- old_umask = umask(0177);
- do {
- if (!(fn = gettempname(prefix, use_heap))) {
- fd = -1;
- break;
- }
- if ((fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0600)) >= 0)
- break;
- if (!use_heap)
- free(fn);
- fn = NULL;
- } while (errno == EEXIST && ++failures < 16);
-#endif
- *tempname = fn;
-
- umask(old_umask);
- unqueue_signals();
- return fd;
-}
-
-/* Check if a string contains a token */
-
-/**/
-mod_export int
-has_token(const char *s)
-{
- while(*s)
- if(itok(*s++))
- return 1;
- return 0;
-}
-
-/* Delete a character in a string */
-
-/**/
-mod_export void
-chuck(char *str)
-{
- while ((str[0] = str[1]))
- str++;
-}
-
-/**/
-mod_export int
-tulower(int c)
-{
- c &= 0xff;
- return (isupper(c) ? tolower(c) : c);
-}
-
-/**/
-mod_export int
-tuupper(int c)
-{
- c &= 0xff;
- return (islower(c) ? toupper(c) : c);
-}
-
-/* copy len chars from t into s, and null terminate */
-
-/**/
-void
-ztrncpy(char *s, char *t, int len)
-{
- while (len--)
- *s++ = *t++;
- *s = '\0';
-}
-
-/* copy t into *s and update s */
-
-/**/
-mod_export void
-strucpy(char **s, char *t)
-{
- char *u = *s;
-
- while ((*u++ = *t++));
- *s = u - 1;
-}
-
-/**/
-mod_export void
-struncpy(char **s, char *t, int n)
-{
- char *u = *s;
-
- while (n-- && (*u = *t++))
- u++;
- *s = u;
- if (n > 0) /* just one null-byte will do, unlike strncpy(3) */
- *u = '\0';
-}
-
-/* Return the number of elements in an array of pointers. *
- * It doesn't count the NULL pointer at the end. */
-
-/**/
-mod_export int
-arrlen(char **s)
-{
- int count;
-
- for (count = 0; *s; s++, count++);
- return count;
-}
-
-/* Return TRUE iff arrlen(s) >= lower_bound, but more efficiently. */
-
-/**/
-mod_export char
-arrlen_ge(char **s, unsigned lower_bound)
-{
- while (lower_bound--)
- if (!*s++)
- return 0 /* FALSE */;
-
- return 1 /* TRUE */;
-}
-
-/* Return TRUE iff arrlen(s) > lower_bound, but more efficiently. */
-
-/**/
-mod_export char
-arrlen_gt(char **s, unsigned lower_bound)
-{
- return arrlen_ge(s, 1+lower_bound);
-}
-
-/* Return TRUE iff arrlen(s) <= upper_bound, but more efficiently. */
-
-/**/
-mod_export char
-arrlen_le(char **s, unsigned upper_bound)
-{
- return arrlen_lt(s, 1+upper_bound);
-}
-
-/* Return TRUE iff arrlen(s) < upper_bound, but more efficiently. */
-
-/**/
-mod_export char
-arrlen_lt(char **s, unsigned upper_bound)
-{
- return !arrlen_ge(s, upper_bound);
-}
-
-/* Skip over a balanced pair of parenthesis. */
-
-/**/
-mod_export int
-skipparens(char inpar, char outpar, char **s)
-{
- int level;
-
- if (**s != inpar)
- return -1;
-
- for (level = 1; *++*s && level;)
- if (**s == inpar)
- ++level;
- else if (**s == outpar)
- --level;
-
- return level;
-}
-
-/**/
-mod_export zlong
-zstrtol(const char *s, char **t, int base)
-{
- return zstrtol_underscore(s, t, base, 0);
-}
-
-/* Convert string to zlong (see zsh.h). This function (without the z) *
- * is contained in the ANSI standard C library, but a lot of them seem *
- * to be broken. */
-
-/**/
-mod_export zlong
-zstrtol_underscore(const char *s, char **t, int base, int underscore)
-{
- const char *inp, *trunc = NULL;
- zulong calc = 0, newcalc = 0;
- int neg;
-
- while (inblank(*s))
- s++;
-
- if ((neg = IS_DASH(*s)))
- s++;
- else if (*s == '+')
- s++;
-
- if (!base) {
- if (*s != '0')
- base = 10;
- else if (*++s == 'x' || *s == 'X')
- base = 16, s++;
- else if (*s == 'b' || *s == 'B')
- base = 2, s++;
- else
- base = 8;
- }
- inp = s;
- if (base < 2 || base > 36) {
- zerr("invalid base (must be 2 to 36 inclusive): %d", base);
- return (zlong)0;
- } else if (base <= 10) {
- for (; (*s >= '0' && *s < ('0' + base)) ||
- (underscore && *s == '_'); s++) {
- if (trunc || *s == '_')
- continue;
- newcalc = calc * base + *s - '0';
- if (newcalc < calc)
- {
- trunc = s;
- continue;
- }
- calc = newcalc;
- }
- } else {
- for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10))
- || (*s >= 'A' && *s < ('A' + base - 10))
- || (underscore && *s == '_'); s++) {
- if (trunc || *s == '_')
- continue;
- newcalc = calc*base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9);
- if (newcalc < calc)
- {
- trunc = s;
- continue;
- }
- calc = newcalc;
- }
- }
-
- /*
- * Special case: check for a number that was just too long for
- * signed notation.
- * Extra special case: the lowest negative number would trigger
- * the first test, but is actually representable correctly.
- * This is a 1 in the top bit, all others zero, so test for
- * that explicitly.
- */
- if (!trunc && (zlong)calc < 0 &&
- (!neg || calc & ~((zulong)1 << (8*sizeof(zulong)-1))))
- {
- trunc = s - 1;
- calc /= base;
- }
-
- if (trunc)
- zwarn("number truncated after %d digits: %s", (int)(trunc - inp), inp);
-
- if (t)
- *t = (char *)s;
- return neg ? -(zlong)calc : (zlong)calc;
-}
-
-/*
- * If s represents a complete unsigned integer (and nothing else)
- * return 1 and set retval to the value. Otherwise return 0.
- *
- * Underscores are always allowed.
- *
- * Sensitive to OCTAL_ZEROES.
- */
-
-/**/
-mod_export int
-zstrtoul_underscore(const char *s, zulong *retval)
-{
- zulong calc = 0, newcalc = 0, base;
-
- if (*s == '+')
- s++;
-
- if (*s != '0')
- base = 10;
- else if (*++s == 'x' || *s == 'X')
- base = 16, s++;
- else if (*s == 'b' || *s == 'B')
- base = 2, s++;
- else
- base = isset(OCTALZEROES) ? 8 : 10;
- if (base <= 10) {
- for (; (*s >= '0' && *s < ('0' + base)) ||
- *s == '_'; s++) {
- if (*s == '_')
- continue;
- newcalc = calc * base + *s - '0';
- if (newcalc < calc)
- {
- return 0;
- }
- calc = newcalc;
- }
- } else {
- for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10))
- || (*s >= 'A' && *s < ('A' + base - 10))
- || *s == '_'; s++) {
- if (*s == '_')
- continue;
- newcalc = calc*base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9);
- if (newcalc < calc)
- {
- return 0;
- }
- calc = newcalc;
- }
- }
-
- if (*s)
- return 0;
- *retval = calc;
- return 1;
-}
-
-/**/
-mod_export int
-setblock_fd(int turnonblocking, int fd, long *modep)
-{
-#ifdef O_NDELAY
-# ifdef O_NONBLOCK
-# define NONBLOCK (O_NDELAY|O_NONBLOCK)
-# else /* !O_NONBLOCK */
-# define NONBLOCK O_NDELAY
-# endif /* !O_NONBLOCK */
-#else /* !O_NDELAY */
-# ifdef O_NONBLOCK
-# define NONBLOCK O_NONBLOCK
-# else /* !O_NONBLOCK */
-# define NONBLOCK 0
-# endif /* !O_NONBLOCK */
-#endif /* !O_NDELAY */
-
-#if NONBLOCK
- struct stat st;
-
- if (!fstat(fd, &st) && !S_ISREG(st.st_mode)) {
- *modep = fcntl(fd, F_GETFL, 0);
- if (*modep != -1) {
- if (!turnonblocking) {
- /* We want to know if blocking was off */
- if ((*modep & NONBLOCK) ||
- !fcntl(fd, F_SETFL, *modep | NONBLOCK))
- return 1;
- } else if ((*modep & NONBLOCK) &&
- !fcntl(fd, F_SETFL, *modep & ~NONBLOCK)) {
- /* Here we want to know if the state changed */
- return 1;
- }
- }
- } else
-#endif /* NONBLOCK */
- *modep = -1;
- return 0;
-
-#undef NONBLOCK
-}
-
-/**/
-int
-setblock_stdin(void)
-{
- long mode;
- return setblock_fd(1, 0, &mode);
-}
-
-/*
- * Check for pending input on fd. If polltty is set, we may need to
- * use termio to look for input. As a final resort, go to non-blocking
- * input and try to read a character, which in this case will be
- * returned in *readchar.
- *
- * Note that apart from setting (and restoring) non-blocking input,
- * this function does not change the input mode. The calling function
- * should have set cbreak mode if necessary.
- *
- * fd may be -1 to sleep until the timeout in microseconds. This is a
- * fallback for old systems that don't have nanosleep(). Some very old
- * systems might not have select: get with it, daddy-o.
- */
-
-/**/
-mod_export int
-read_poll(int fd, int *readchar, int polltty, zlong microseconds)
-{
- int ret = -1;
- long mode = -1;
- char c;
-#ifdef HAVE_SELECT
- fd_set foofd;
- struct timeval expire_tv;
-#else
-#ifdef FIONREAD
- int val;
-#endif
-#endif
-#ifdef HAS_TIO
- struct ttyinfo ti;
-#endif
-
- if (fd < 0 || (polltty && !isatty(fd)))
- polltty = 0; /* no tty to poll */
-
-#if defined(HAS_TIO) && !defined(__CYGWIN__)
- /*
- * Under Solaris, at least, reading from the terminal in non-canonical
- * mode requires that we use the VMIN mechanism to poll. Any attempt
- * to check any other way, or to set the terminal to non-blocking mode
- * and poll that way, fails; it will just for canonical mode input.
- * We should probably use this mechanism if the user has set non-canonical
- * mode, in which case testing here for isatty() and ~ICANON would be
- * better than testing whether bin_read() set it, but for now we've got
- * enough problems.
- *
- * Under Cygwin, you won't be surprised to here, this mechanism,
- * although present, doesn't work, and we *have* to use ordinary
- * non-blocking reads to find out if there is a character present
- * in non-canonical mode.
- *
- * I am assuming Solaris is nearer the UNIX norm. This is not necessarily
- * as plausible as it sounds, but it seems the right way to guess.
- * pws 2000/06/26
- */
- if (polltty && fd >= 0) {
- gettyinfo(&ti);
- if ((polltty = ti.tio.c_cc[VMIN])) {
- ti.tio.c_cc[VMIN] = 0;
- /* termios timeout is 10ths of a second */
- ti.tio.c_cc[VTIME] = (int) (microseconds / (zlong)100000);
- settyinfo(&ti);
- }
- }
-#else
- polltty = 0;
-#endif
-#ifdef HAVE_SELECT
- expire_tv.tv_sec = (int) (microseconds / (zlong)1000000);
- expire_tv.tv_usec = microseconds % (zlong)1000000;
- FD_ZERO(&foofd);
- if (fd > -1) {
- FD_SET(fd, &foofd);
- ret = select(fd+1, (SELECT_ARG_2_T) &foofd, NULL, NULL, &expire_tv);
- } else
- ret = select(0, NULL, NULL, NULL, &expire_tv);
-#else
- if (fd < 0) {
- /* OK, can't do that. Just quietly sleep for a second. */
- sleep(1);
- return 1;
- }
-#ifdef FIONREAD
- if (ioctl(fd, FIONREAD, (char *) &val) == 0)
- ret = (val > 0);
-#endif
-#endif
-
- if (fd >= 0 && ret < 0 && !errflag) {
- /*
- * Final attempt: set non-blocking read and try to read a character.
- * Praise Bill, this works under Cygwin (nothing else seems to).
- */
- if ((polltty || setblock_fd(0, fd, &mode)) && read(fd, &c, 1) > 0) {
- *readchar = c;
- ret = 1;
- }
- if (mode != -1)
- fcntl(fd, F_SETFL, mode);
- }
-#ifdef HAS_TIO
- if (polltty) {
- ti.tio.c_cc[VMIN] = 1;
- ti.tio.c_cc[VTIME] = 0;
- settyinfo(&ti);
- }
-#endif
- return (ret > 0);
-}
-
-/*
- * Sleep for the given number of microseconds --- must be within
- * range of a long at the moment, but this is only used for
- * limited internal purposes.
- */
-
-/**/
-int
-zsleep(long us)
-{
-#ifdef HAVE_NANOSLEEP
- struct timespec sleeptime;
-
- sleeptime.tv_sec = (time_t)us / (time_t)1000000;
- sleeptime.tv_nsec = (us % 1000000L) * 1000L;
- for (;;) {
- struct timespec rem;
- int ret = nanosleep(&sleeptime, &rem);
-
- if (ret == 0)
- return 1;
- else if (errno != EINTR)
- return 0;
- sleeptime = rem;
- }
-#else
- int dummy;
- return read_poll(-1, &dummy, 0, us);
-#endif
-}
-
-/**
- * Sleep for time (fairly) randomly up to max_us microseconds.
- * Don't let the wallclock time extend beyond end_time.
- * Return 1 if that seemed to work, else 0.
- *
- * For best results max_us should be a multiple of 2**16 or large
- * enough that it doesn't matter.
- */
-
-/**/
-int
-zsleep_random(long max_us, time_t end_time)
-{
- long r;
- time_t now = time(NULL);
-
- /*
- * Randomish backoff. Doesn't need to be fundamentally
- * unpredictable, just probably unlike the value another
- * exiting shell is using. On some systems the bottom 16
- * bits aren't that random but the use here doesn't
- * really care.
- */
- r = (long)(rand() & 0xFFFF);
- /*
- * Turn this into a fraction of sleep_us. Again, this
- * doesn't need to be particularly accurate and the base time
- * is sufficient that we can do the division first and not
- * worry about the range.
- */
- r = (max_us >> 16) * r;
- /*
- * Don't sleep beyond timeout.
- * Not that important as timeout is ridiculously long, but
- * if there's an interface, interface to it...
- */
- while (r && now + (time_t)(r / 1000000) > end_time)
- r >>= 1;
- if (r) /* pedantry */
- return zsleep(r);
- return 0;
-}
-
-/**/
-int
-checkrmall(char *s)
-{
- DIR *rmd;
- int count = 0;
- if (!shout)
- return 1;
- if (*s != '/') {
- if (pwd[1])
- s = zhtricat(pwd, "/", s);
- else
- s = dyncat("/", s);
- }
- const int max_count = 100;
- if ((rmd = opendir(unmeta(s)))) {
- int ignoredots = !isset(GLOBDOTS);
- char *fname;
-
- while ((fname = zreaddir(rmd, 1))) {
- if (ignoredots && *fname == '.')
- continue;
- count++;
- if (count > max_count)
- break;
- }
- closedir(rmd);
- }
- if (count > max_count)
- fprintf(shout, "zsh: sure you want to delete more than %d files in ",
- max_count);
- else if (count == 1)
- fprintf(shout, "zsh: sure you want to delete the only file in ");
- else if (count > 0)
- fprintf(shout, "zsh: sure you want to delete all %d files in ",
- count);
- else {
- /* We don't know how many files the glob will expand to; see 41707. */
- fprintf(shout, "zsh: sure you want to delete all the files in ");
- }
- nicezputs(s, shout);
- if(isset(RMSTARWAIT)) {
- fputs("? (waiting ten seconds)", shout);
- fflush(shout);
- zbeep();
- sleep(10);
- fputc('\n', shout);
- }
- if (errflag)
- return 0;
- fputs(" [yn]? ", shout);
- fflush(shout);
- zbeep();
- return (getquery("ny", 1) == 'y');
-}
-
-/**/
-mod_export ssize_t
-read_loop(int fd, char *buf, size_t len)
-{
- ssize_t got = len;
-
- while (1) {
- ssize_t ret = read(fd, buf, len);
- if (ret == len)
- break;
- if (ret <= 0) {
- if (ret < 0) {
- if (errno == EINTR)
- continue;
- if (fd != SHTTY)
- zwarn("read failed: %e", errno);
- }
- return ret;
- }
- buf += ret;
- len -= ret;
- }
-
- return got;
-}
-
-/**/
-mod_export ssize_t
-write_loop(int fd, const char *buf, size_t len)
-{
- ssize_t wrote = len;
-
- while (1) {
- ssize_t ret = write(fd, buf, len);
- if (ret == len)
- break;
- if (ret < 0) {
- if (errno == EINTR)
- continue;
- if (fd != SHTTY)
- zwarn("write failed: %e", errno);
- return -1;
- }
- buf += ret;
- len -= ret;
- }
-
- return wrote;
-}
-
-static int
-read1char(int echo)
-{
- char c;
- int q = queue_signal_level();
-
- dont_queue_signals();
- while (read(SHTTY, &c, 1) != 1) {
- if (errno != EINTR || errflag || retflag || breaks || contflag) {
- restore_queue_signals(q);
- return -1;
- }
- }
- restore_queue_signals(q);
- if (echo)
- write_loop(SHTTY, &c, 1);
- return STOUC(c);
-}
-
-/**/
-mod_export int
-noquery(int purge)
-{
- int val = 0;
-
-#ifdef FIONREAD
- char c;
-
- ioctl(SHTTY, FIONREAD, (char *)&val);
- if (purge) {
- for (; val; val--) {
- if (read(SHTTY, &c, 1) != 1) {
- /* Do nothing... */
- }
- }
- }
-#endif
-
- return val;
-}
-
-/**/
-int
-getquery(char *valid_chars, int purge)
-{
- int c, d, nl = 0;
- int isem = !strcmp(term, "emacs");
- struct ttyinfo ti;
-
- attachtty(mypgrp);
-
- gettyinfo(&ti);
-#ifdef HAS_TIO
- ti.tio.c_lflag &= ~ECHO;
- if (!isem) {
- ti.tio.c_lflag &= ~ICANON;
- ti.tio.c_cc[VMIN] = 1;
- ti.tio.c_cc[VTIME] = 0;
- }
-#else
- ti.sgttyb.sg_flags &= ~ECHO;
- if (!isem)
- ti.sgttyb.sg_flags |= CBREAK;
-#endif
- settyinfo(&ti);
-
- if (noquery(purge)) {
- if (!isem)
- settyinfo(&shttyinfo);
- write_loop(SHTTY, "n\n", 2);
- return 'n';
- }
-
- while ((c = read1char(0)) >= 0) {
- if (c == 'Y')
- c = 'y';
- else if (c == 'N')
- c = 'n';
- if (!valid_chars)
- break;
- if (c == '\n') {
- c = *valid_chars;
- nl = 1;
- break;
- }
- if (strchr(valid_chars, c)) {
- nl = 1;
- break;
- }
- zbeep();
- }
- if (c >= 0) {
- char buf = (char)c;
- write_loop(SHTTY, &buf, 1);
- }
- if (nl)
- write_loop(SHTTY, "\n", 1);
-
- if (isem) {
- if (c != '\n')
- while ((d = read1char(1)) >= 0 && d != '\n');
- } else {
- if (c != '\n' && !valid_chars) {
-#ifdef MULTIBYTE_SUPPORT
- if (isset(MULTIBYTE) && c >= 0) {
- /*
- * No waiting for a valid character, and no draining;
- * we should ensure we haven't stopped in the middle
- * of a multibyte character.
- */
- mbstate_t mbs;
- char cc = (char)c;
- memset(&mbs, 0, sizeof(mbs));
- for (;;) {
- size_t ret = mbrlen(&cc, 1, &mbs);
-
- if (ret != MB_INCOMPLETE)
- break;
- c = read1char(1);
- if (c < 0)
- break;
- cc = (char)c;
- }
- }
-#endif
- write_loop(SHTTY, "\n", 1);
- }
- }
- settyinfo(&shttyinfo);
- return c;
-}
-
-static int d;
-static char *guess, *best;
-static Patprog spckpat, spnamepat;
-
-/**/
-static void
-spscan(HashNode hn, UNUSED(int scanflags))
-{
- int nd;
-
- if (spckpat && pattry(spckpat, hn->nam))
- return;
-
- nd = spdist(hn->nam, guess, (int) strlen(guess) / 4 + 1);
- if (nd <= d) {
- best = hn->nam;
- d = nd;
- }
-}
-
-/* spellcheck a word */
-/* fix s ; if hist is nonzero, fix the history list too */
-
-/**/
-mod_export void
-spckword(char **s, int hist, int cmd, int ask)
-{
- char *t, *correct_ignore;
- char ic = '\0';
- int preflen = 0;
- int autocd = cmd && isset(AUTOCD) && strcmp(*s, ".") && strcmp(*s, "..");
-
- if ((histdone & HISTFLAG_NOEXEC) || **s == '-' || **s == '%')
- return;
- if (!strcmp(*s, "in"))
- return;
- if (!(*s)[0] || !(*s)[1])
- return;
- if (cmd) {
- if (shfunctab->getnode(shfunctab, *s) ||
- builtintab->getnode(builtintab, *s) ||
- cmdnamtab->getnode(cmdnamtab, *s) ||
- aliastab->getnode(aliastab, *s) ||
- reswdtab->getnode(reswdtab, *s))
- return;
- else if (isset(HASHLISTALL)) {
- cmdnamtab->filltable(cmdnamtab);
- if (cmdnamtab->getnode(cmdnamtab, *s))
- return;
- }
- }
- t = *s;
- if (*t == Tilde || *t == Equals || *t == String)
- t++;
- for (; *t; t++)
- if (itok(*t))
- return;
- best = NULL;
- for (t = *s; *t; t++)
- if (*t == '/')
- break;
- if (**s == Tilde && !*t)
- return;
-
- if ((correct_ignore = getsparam("CORRECT_IGNORE")) != NULL) {
- tokenize(correct_ignore = dupstring(correct_ignore));
- remnulargs(correct_ignore);
- spckpat = patcompile(correct_ignore, 0, NULL);
- } else
- spckpat = NULL;
-
- if ((correct_ignore = getsparam("CORRECT_IGNORE_FILE")) != NULL) {
- tokenize(correct_ignore = dupstring(correct_ignore));
- remnulargs(correct_ignore);
- spnamepat = patcompile(correct_ignore, 0, NULL);
- } else
- spnamepat = NULL;
-
- if (**s == String && !*t) {
- guess = *s + 1;
- if (itype_end(guess, IIDENT, 1) == guess)
- return;
- ic = String;
- d = 100;
- scanhashtable(paramtab, 1, 0, 0, spscan, 0);
- } else if (**s == Equals) {
- if (*t)
- return;
- if (hashcmd(guess = *s + 1, pathchecked))
- return;
- d = 100;
- ic = Equals;
- scanhashtable(aliastab, 1, 0, 0, spscan, 0);
- scanhashtable(cmdnamtab, 1, 0, 0, spscan, 0);
- } else {
- guess = *s;
- if (*guess == Tilde || *guess == String) {
- int ne;
- ic = *guess;
- if (!*++t)
- return;
- guess = dupstring(guess);
- ne = noerrs;
- noerrs = 2;
- singsub(&guess);
- noerrs = ne;
- if (!guess)
- return;
- preflen = strlen(guess) - strlen(t);
- }
- if (access(unmeta(guess), F_OK) == 0)
- return;
- best = spname(guess);
- if (!*t && cmd) {
- if (hashcmd(guess, pathchecked))
- return;
- d = 100;
- scanhashtable(reswdtab, 1, 0, 0, spscan, 0);
- scanhashtable(aliastab, 1, 0, 0, spscan, 0);
- scanhashtable(shfunctab, 1, 0, 0, spscan, 0);
- scanhashtable(builtintab, 1, 0, 0, spscan, 0);
- scanhashtable(cmdnamtab, 1, 0, 0, spscan, 0);
- if (autocd) {
- char **pp;
- for (pp = cdpath; *pp; pp++) {
- char bestcd[PATH_MAX + 1];
- int thisdist;
- /* Less than d here, instead of less than or equal *
- * as used in spscan(), so that an autocd is chosen *
- * only when it is better than anything so far, and *
- * so we prefer directories earlier in the cdpath. */
- if ((thisdist = mindist(*pp, *s, bestcd, 1)) < d) {
- best = dupstring(bestcd);
- d = thisdist;
- }
- }
- }
- }
- }
- if (errflag)
- return;
- if (best && (int)strlen(best) > 1 && strcmp(best, guess)) {
- int x;
- if (ic) {
- char *u;
- if (preflen) {
- /* do not correct the result of an expansion */
- if (strncmp(guess, best, preflen))
- return;
- /* replace the temporarily expanded prefix with the original */
- u = (char *) zhalloc(t - *s + strlen(best + preflen) + 1);
- strncpy(u, *s, t - *s);
- strcpy(u + (t - *s), best + preflen);
- } else {
- u = (char *) zhalloc(strlen(best) + 2);
- *u = '\0';
- strcpy(u + 1, best);
- }
- best = u;
- guess = *s;
- *guess = *best = ztokens[ic - Pound];
- }
- if (ask) {
- if (noquery(0)) {
- x = 'n';
- } else if (shout) {
- char *pptbuf;
- pptbuf = promptexpand(sprompt, 0, best, guess, NULL);
- zputs(pptbuf, shout);
- free(pptbuf);
- fflush(shout);
- zbeep();
- x = getquery("nyae", 0);
- if (cmd && x == 'n')
- pathchecked = path;
- } else
- x = 'n';
- } else
- x = 'y';
- if (x == 'y') {
- *s = dupstring(best);
- if (hist)
- hwrep(best);
- } else if (x == 'a') {
- histdone |= HISTFLAG_NOEXEC;
- } else if (x == 'e') {
- histdone |= HISTFLAG_NOEXEC | HISTFLAG_RECALL;
- }
- if (ic)
- **s = ic;
- }
-}
-
-/*
- * Helper for ztrftime. Called with a pointer to the length left
- * in the buffer, and a new string length to decrement from that.
- * Returns 0 if the new length fits, 1 otherwise. We assume a terminating
- * NUL and return 1 if that doesn't fit.
- */
-
-static int
-ztrftimebuf(int *bufsizeptr, int decr)
-{
- if (*bufsizeptr <= decr)
- return 1;
- *bufsizeptr -= decr;
- return 0;
-}
-
-/*
- * Like the system function, this returns the number of characters
- * copied, not including the terminating NUL. This may be zero
- * if the string didn't fit.
- *
- * As an extension, try to detect an error in strftime --- typically
- * not enough memory --- and return -1. Not guaranteed to be portable,
- * since the strftime() interface doesn't make any guarantees about
- * the state of the buffer if it returns zero.
- *
- * fmt is metafied, but we need to unmetafy it on the fly to
- * pass into strftime / combine with the output from strftime.
- * The return value in buf is not metafied.
- */
-
-/**/
-mod_export int
-ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long nsec)
-{
- int hr12;
-#ifdef HAVE_STRFTIME
- int decr;
- char *fmtstart;
-#else
- static char *astr[] =
- {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
- static char *estr[] =
- {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
- "Aug", "Sep", "Oct", "Nov", "Dec"};
-#endif
- char *origbuf = buf;
-
-
- while (*fmt) {
- if (*fmt == Meta) {
- int chr = fmt[1] ^ 32;
- if (ztrftimebuf(&bufsize, 1))
- return -1;
- *buf++ = chr;
- fmt += 2;
- } else if (*fmt == '%') {
- int strip;
- int digs = 3;
-
-#ifdef HAVE_STRFTIME
- fmtstart =
-#endif
- fmt++;
-
- if (*fmt == '-') {
- strip = 1;
- fmt++;
- } else
- strip = 0;
- if (idigit(*fmt)) {
- /* Digit --- only useful with . */
- char *dstart = fmt;
- char *dend = fmt+1;
- while (idigit(*dend))
- dend++;
- if (*dend == '.') {
- fmt = dend;
- digs = atoi(dstart);
- }
- }
- /*
- * Assume this format will take up at least two
- * characters. Not always true, but if that matters
- * we are so close to the edge it's not a big deal.
- * Fix up some longer cases specially when we get to them.
- */
- if (ztrftimebuf(&bufsize, 2))
- return -1;
-#ifdef HAVE_STRFTIME
- /* Our internal handling doesn't handle padding and other gnu extensions,
- * so here we detect them and pass over to strftime(). We don't want
- * to do this unconditionally though, as we have some extensions that
- * strftime() doesn't have (%., %f, %L and %K) */
-morefmt:
- if (!((fmt - fmtstart == 1) || (fmt - fmtstart == 2 && strip) || *fmt == '.')) {
- while (*fmt && strchr("OE^#_-0123456789", *fmt))
- fmt++;
- if (*fmt) {
- fmt++;
- goto strftimehandling;
- }
- }
-#endif
- switch (*fmt++) {
- case '.':
- if (ztrftimebuf(&bufsize, digs))
- return -1;
- if (digs > 9)
- digs = 9;
- if (digs < 9) {
- int trunc;
- for (trunc = 8 - digs; trunc; trunc--)
- nsec /= 10;
- nsec = (nsec + 8) / 10;
- }
- sprintf(buf, "%0*ld", digs, nsec);
- buf += digs;
- break;
- case '\0':
- /* Guard against premature end of string */
- *buf++ = '%';
- fmt--;
- break;
- case 'f':
- strip = 1;
- /* FALLTHROUGH */
- case 'e':
- if (tm->tm_mday > 9)
- *buf++ = '0' + tm->tm_mday / 10;
- else if (!strip)
- *buf++ = ' ';
- *buf++ = '0' + tm->tm_mday % 10;
- break;
- case 'K':
- strip = 1;
- /* FALLTHROUGH */
- case 'H':
- case 'k':
- if (tm->tm_hour > 9)
- *buf++ = '0' + tm->tm_hour / 10;
- else if (!strip) {
- if (fmt[-1] == 'H')
- *buf++ = '0';
- else
- *buf++ = ' ';
- }
- *buf++ = '0' + tm->tm_hour % 10;
- break;
- case 'L':
- strip = 1;
- /* FALLTHROUGH */
- case 'l':
- hr12 = tm->tm_hour % 12;
- if (hr12 == 0)
- hr12 = 12;
- if (hr12 > 9)
- *buf++ = '1';
- else if (!strip)
- *buf++ = ' ';
-
- *buf++ = '0' + (hr12 % 10);
- break;
- case 'd':
- if (tm->tm_mday > 9 || !strip)
- *buf++ = '0' + tm->tm_mday / 10;
- *buf++ = '0' + tm->tm_mday % 10;
- break;
- case 'm':
- if (tm->tm_mon > 8 || !strip)
- *buf++ = '0' + (tm->tm_mon + 1) / 10;
- *buf++ = '0' + (tm->tm_mon + 1) % 10;
- break;
- case 'M':
- if (tm->tm_min > 9 || !strip)
- *buf++ = '0' + tm->tm_min / 10;
- *buf++ = '0' + tm->tm_min % 10;
- break;
- case 'N':
- if (ztrftimebuf(&bufsize, 9))
- return -1;
- sprintf(buf, "%09ld", nsec);
- buf += 9;
- break;
- case 'S':
- if (tm->tm_sec > 9 || !strip)
- *buf++ = '0' + tm->tm_sec / 10;
- *buf++ = '0' + tm->tm_sec % 10;
- break;
- case 'y':
- if (tm->tm_year > 9 || !strip)
- *buf++ = '0' + (tm->tm_year / 10) % 10;
- *buf++ = '0' + tm->tm_year % 10;
- break;
-#ifndef HAVE_STRFTIME
- case 'Y':
- {
- int year, digits, testyear;
- year = tm->tm_year + 1900;
- digits = 1;
- testyear = year;
- while (testyear > 9) {
- digits++;
- testyear /= 10;
- }
- if (ztrftimebuf(&bufsize, digits))
- return -1;
- sprintf(buf, "%d", year);
- buf += digits;
- break;
- }
- case 'a':
- if (ztrftimebuf(&bufsize, strlen(astr[tm->tm_wday]) - 2))
- return -1;
- strucpy(&buf, astr[tm->tm_wday]);
- break;
- case 'b':
- if (ztrftimebuf(&bufsize, strlen(estr[tm->tm_mon]) - 2))
- return -1;
- strucpy(&buf, estr[tm->tm_mon]);
- break;
- case 'p':
- *buf++ = (tm->tm_hour > 11) ? 'p' : 'a';
- *buf++ = 'm';
- break;
- default:
- *buf++ = '%';
- if (fmt[-1] != '%')
- *buf++ = fmt[-1];
-#else
- case 'E':
- case 'O':
- case '^':
- case '#':
- case '_':
- case '-':
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- goto morefmt;
-strftimehandling:
- default:
- /*
- * Remember we've already allowed for two characters
- * in the accounting in bufsize (but nowhere else).
- */
- {
- char origchar = fmt[-1];
- int size = fmt - fmtstart;
- char *tmp, *last;
- tmp = zhalloc(size + 1);
- strncpy(tmp, fmtstart, size);
- last = fmt-1;
- if (*last == Meta) {
- /*
- * This is for consistency in counting:
- * a metafiable character isn't actually
- * a valid strftime descriptor.
- *
- * Previous characters were explicitly checked,
- * so can't be metafied.
- */
- *last = *++fmt ^ 32;
- }
- tmp[size] = '\0';
- *buf = '\1';
- if (!strftime(buf, bufsize + 2, tmp, tm))
- {
- /*
- * Some locales don't have strings for
- * AM/PM, so empty output is valid.
- */
- if (*buf || (origchar != 'p' && origchar != 'P')) {
- if (*buf) {
- buf[0] = '\0';
- return -1;
- }
- return 0;
- }
- }
- decr = strlen(buf);
- buf += decr;
- bufsize -= decr - 2;
- }
-#endif
- break;
- }
- } else {
- if (ztrftimebuf(&bufsize, 1))
- return -1;
- *buf++ = *fmt++;
- }
- }
- *buf = '\0';
- return buf - origbuf;
-}
-
-/**/
-mod_export char *
-zjoin(char **arr, int delim, int heap)
-{
- int len = 0;
- char **s, *ret, *ptr;
-
- for (s = arr; *s; s++)
- len += strlen(*s) + 1 + (imeta(delim) ? 1 : 0);
- if (!len)
- return heap? "" : ztrdup("");
- ptr = ret = (char *) (heap ? zhalloc(len) : zalloc(len));
- for (s = arr; *s; s++) {
- strucpy(&ptr, *s);
- if (imeta(delim)) {
- *ptr++ = Meta;
- *ptr++ = delim ^ 32;
- }
- else
- *ptr++ = delim;
- }
- ptr[-1 - (imeta(delim) ? 1 : 0)] = '\0';
- return ret;
-}
-
-/* Split a string containing a colon separated list *
- * of items into an array of strings. */
-
-/**/
-mod_export char **
-colonsplit(char *s, int uniq)
-{
- int ct;
- char *t, **ret, **ptr, **p;
-
- for (t = s, ct = 0; *t; t++) /* count number of colons */
- if (*t == ':')
- ct++;
- ptr = ret = (char **) zalloc(sizeof(char *) * (ct + 2));
-
- t = s;
- do {
- s = t;
- /* move t to point at next colon */
- for (; *t && *t != ':'; t++);
- if (uniq)
- for (p = ret; p < ptr; p++)
- if ((int)strlen(*p) == t - s && ! strncmp(*p, s, t - s))
- goto cont;
- *ptr = (char *) zalloc((t - s) + 1);
- ztrncpy(*ptr++, s, t - s);
- cont: ;
- }
- while (*t++);
- *ptr = NULL;
- return ret;
-}
-
-/**/
-static int
-skipwsep(char **s)
-{
- char *t = *s;
- int i = 0;
-
- /*
- * Don't need to handle mutlibyte characters, they can't
- * be IWSEP. Do need to check for metafication.
- */
- while (*t && iwsep(*t == Meta ? t[1] ^ 32 : *t)) {
- if (*t == Meta)
- t++;
- t++;
- i++;
- }
- *s = t;
- return i;
-}
-
-/*
- * haven't worked out what allownull does; it's passed down from
- * sepsplit but all the cases it's used are either 0 or 1 without
- * a comment. it seems to be something to do with the `nulstring'
- * which i think is some kind of a metafication thing, so probably
- * allownull's value is associated with whether we are using
- * metafied strings.
- * see findsep() below for handling of `quote' argument
- */
-
-/**/
-mod_export char **
-spacesplit(char *s, int allownull, int heap, int quote)
-{
- char *t, **ret, **ptr;
- int l = sizeof(*ret) * (wordcount(s, NULL, -!allownull) + 1);
- char *(*dup)(const char *) = (heap ? dupstring : ztrdup);
-
- /* ### TODO: s/calloc/alloc/ */
- ptr = ret = (char **) (heap ? hcalloc(l) : zshcalloc(l));
-
- if (quote) {
- /*
- * we will be stripping quoted separators by hacking string,
- * so make sure it's hackable.
- */
- s = dupstring(s);
- }
-
- t = s;
- skipwsep(&s);
- MB_METACHARINIT();
- if (*s && itype_end(s, ISEP, 1) != s)
- *ptr++ = dup(allownull ? "" : nulstring);
- else if (!allownull && t != s)
- *ptr++ = dup("");
- while (*s) {
- char *iend = itype_end(s, ISEP, 1);
- if (iend != s) {
- s = iend;
- skipwsep(&s);
- }
- else if (quote && *s == '\\') {
- s++;
- skipwsep(&s);
- }
- t = s;
- (void)findsep(&s, NULL, quote);
- if (s > t || allownull) {
- *ptr = (char *) (heap ? zhalloc((s - t) + 1) :
- zalloc((s - t) + 1));
- ztrncpy(*ptr++, t, s - t);
- } else
- *ptr++ = dup(nulstring);
- t = s;
- skipwsep(&s);
- }
- if (!allownull && t != s)
- *ptr++ = dup("");
- *ptr = NULL;
- return ret;
-}
-
-/*
- * Find a separator. Return 0 if already at separator, 1 if separator
- * found later, else -1. (Historical note: used to return length into
- * string but this is all that is necessary and is less ambiguous with
- * multibyte characters around.)
- *
- * *s is the string we are looking along, which will be updated
- * to the point we have got to.
- *
- * sep is a possibly multicharacter separator to look for. If NULL,
- * use normal separator characters. If *sep is NULL, split on individual
- * characters.
- *
- * quote is a flag that '\<sep>' should not be treated as a separator.
- * in this case we need to be able to strip the backslash directly
- * in the string, so the calling function must have sent us something
- * modifiable. currently this only works for sep == NULL. also in
- * in this case only, we need to turn \\ into \.
- */
-
-/**/
-static int
-findsep(char **s, char *sep, int quote)
-{
- /*
- */
- int i, ilen;
- char *t, *tt;
- convchar_t c;
-
- MB_METACHARINIT();
- if (!sep) {
- for (t = *s; *t; t += ilen) {
- if (quote && *t == '\\') {
- if (t[1] == '\\') {
- chuck(t);
- ilen = 1;
- continue;
- } else {
- ilen = MB_METACHARLENCONV(t+1, &c);
- if (WC_ZISTYPE(c, ISEP)) {
- chuck(t);
- /* then advance over new character, length ilen */
- } else {
- /* treat *t (backslash) as normal byte */
- if (isep(*t))
- break;
- ilen = 1;
- }
- }
- } else {
- ilen = MB_METACHARLENCONV(t, &c);
- if (WC_ZISTYPE(c, ISEP))
- break;
- }
- }
- i = (t > *s);
- *s = t;
- return i;
- }
- if (!sep[0]) {
- /*
- * NULL separator just means advance past first character,
- * if any.
- */
- if (**s) {
- *s += MB_METACHARLEN(*s);
- return 1;
- }
- return -1;
- }
- for (i = 0; **s; i++) {
- /*
- * The following works for multibyte characters by virtue of
- * the fact that sep may be a string (and we don't care how
- * it divides up, we need to match all of it).
- */
- for (t = sep, tt = *s; *t && *tt && *t == *tt; t++, tt++);
- if (!*t)
- return (i > 0);
- *s += MB_METACHARLEN(*s);
- }
- return -1;
-}
-
-/**/
-char *
-findword(char **s, char *sep)
-{
- char *r, *t;
- int sl;
-
- if (!**s)
- return NULL;
-
- if (sep) {
- sl = strlen(sep);
- r = *s;
- while (! findsep(s, sep, 0)) {
- r = *s += sl;
- }
- return r;
- }
- MB_METACHARINIT();
- for (t = *s; *t; t += sl) {
- convchar_t c;
- sl = MB_METACHARLENCONV(t, &c);
- if (!WC_ZISTYPE(c, ISEP))
- break;
- }
- *s = t;
- (void)findsep(s, sep, 0);
- return t;
-}
-
-/**/
-int
-wordcount(char *s, char *sep, int mul)
-{
- int r, sl, c;
-
- if (sep) {
- r = 1;
- sl = strlen(sep);
- for (; (c = findsep(&s, sep, 0)) >= 0; s += sl)
- if ((c || mul) && (sl || *(s + sl)))
- r++;
- } else {
- char *t = s;
-
- r = 0;
- if (mul <= 0)
- skipwsep(&s);
- if ((*s && itype_end(s, ISEP, 1) != s) ||
- (mul < 0 && t != s))
- r++;
- for (; *s; r++) {
- char *ie = itype_end(s, ISEP, 1);
- if (ie != s) {
- s = ie;
- if (mul <= 0)
- skipwsep(&s);
- }
- (void)findsep(&s, NULL, 0);
- t = s;
- if (mul <= 0)
- skipwsep(&s);
- }
- if (mul < 0 && t != s)
- r++;
- }
- return r;
-}
-
-/**/
-mod_export char *
-sepjoin(char **s, char *sep, int heap)
-{
- char *r, *p, **t;
- int l, sl;
- char sepbuf[2];
-
- if (!*s)
- return heap ? "" : ztrdup("");
- if (!sep) {
- /* optimise common case that ifs[0] is space */
- if (ifs && *ifs != ' ') {
- MB_METACHARINIT();
- sep = dupstrpfx(ifs, MB_METACHARLEN(ifs));
- } else {
- p = sep = sepbuf;
- *p++ = ' ';
- *p = '\0';
- }
- }
- sl = strlen(sep);
- for (t = s, l = 1 - sl; *t; l += strlen(*t) + sl, t++);
- r = p = (char *) (heap ? zhalloc(l) : zalloc(l));
- t = s;
- while (*t) {
- strucpy(&p, *t);
- if (*++t)
- strucpy(&p, sep);
- }
- *p = '\0';
- return r;
-}
-
-/**/
-char **
-sepsplit(char *s, char *sep, int allownull, int heap)
-{
- int n, sl;
- char *t, *tt, **r, **p;
-
- /* Null string? Treat as empty string. */
- if (s[0] == Nularg && !s[1])
- s++;
-
- if (!sep)
- return spacesplit(s, allownull, heap, 0);
-
- sl = strlen(sep);
- n = wordcount(s, sep, 1);
- r = p = (char **) (heap ? zhalloc((n + 1) * sizeof(char *)) :
- zalloc((n + 1) * sizeof(char *)));
-
- for (t = s; n--;) {
- tt = t;
- (void)findsep(&t, sep, 0);
- *p = (char *) (heap ? zhalloc(t - tt + 1) :
- zalloc(t - tt + 1));
- strncpy(*p, tt, t - tt);
- (*p)[t - tt] = '\0';
- p++;
- t += sl;
- }
- *p = NULL;
-
- return r;
-}
-
-/* Get the definition of a shell function */
-
-/**/
-mod_export Shfunc
-getshfunc(char *nam)
-{
- return (Shfunc) shfunctab->getnode(shfunctab, nam);
-}
-
-/*
- * Call the function func to substitute string orig by setting
- * the parameter reply.
- * Return the array from reply, or NULL if the function returned
- * non-zero status.
- * The returned value comes directly from the parameter and
- * so should be used before there is any chance of that
- * being changed or unset.
- * If arg1 is not NULL, it is used as an initial argument to
- * the function, with the original string as the second argument.
- */
-
-/**/
-char **
-subst_string_by_func(Shfunc func, char *arg1, char *orig)
-{
- int osc = sfcontext, osm = stopmsg, old_incompfunc = incompfunc;
- LinkList l = newlinklist();
- char **ret;
-
- addlinknode(l, func->node.nam);
- if (arg1)
- addlinknode(l, arg1);
- addlinknode(l, orig);
- sfcontext = SFC_SUBST;
- incompfunc = 0;
-
- if (doshfunc(func, l, 1))
- ret = NULL;
- else
- ret = getaparam("reply");
-
- sfcontext = osc;
- stopmsg = osm;
- incompfunc = old_incompfunc;
- return ret;
-}
-
-/**
- * Front end to subst_string_by_func to use hook-like logic.
- * name can refer to a function, and name + "_hook" can refer
- * to an array containing a list of functions. The functions
- * are tried in order until one returns success.
- */
-/**/
-char **
-subst_string_by_hook(char *name, char *arg1, char *orig)
-{
- Shfunc func;
- char **ret = NULL;
-
- if ((func = getshfunc(name))) {
- ret = subst_string_by_func(func, arg1, orig);
- }
-
- if (!ret) {
- char **arrptr;
- int namlen = strlen(name);
- VARARR(char, arrnam, namlen + HOOK_SUFFIX_LEN);
- memcpy(arrnam, name, namlen);
- memcpy(arrnam + namlen, HOOK_SUFFIX, HOOK_SUFFIX_LEN);
-
- if ((arrptr = getaparam(arrnam))) {
- /* Guard against internal modification of the array */
- arrptr = arrdup(arrptr);
- for (; *arrptr; arrptr++) {
- if ((func = getshfunc(*arrptr))) {
- ret = subst_string_by_func(func, arg1, orig);
- if (ret)
- break;
- }
- }
- }
- }
-
- return ret;
-}
-
-/**/
-mod_export char **
-mkarray(char *s)
-{
- char **t = (char **) zalloc((s) ? (2 * sizeof s) : (sizeof s));
-
- if ((*t = s))
- t[1] = NULL;
- return t;
-}
-
-/**/
-mod_export char **
-hmkarray(char *s)
-{
- char **t = (char **) zhalloc((s) ? (2 * sizeof s) : (sizeof s));
-
- if ((*t = s))
- t[1] = NULL;
- return t;
-}
-
-/**/
-mod_export void
-zbeep(void)
-{
- char *vb;
- queue_signals();
- if ((vb = getsparam_u("ZBEEP"))) {
- int len;
- vb = getkeystring(vb, &len, GETKEYS_BINDKEY, NULL);
- write_loop(SHTTY, vb, len);
- } else if (isset(BEEP))
- write_loop(SHTTY, "\07", 1);
- unqueue_signals();
-}
-
-/**/
-mod_export void
-freearray(char **s)
-{
- char **t = s;
-
- DPUTS(!s, "freearray() with zero argument");
-
- while (*s)
- zsfree(*s++);
- free(t);
-}
-
-/**/
-int
-equalsplit(char *s, char **t)
-{
- for (; *s && *s != '='; s++);
- if (*s == '=') {
- *s++ = '\0';
- *t = s;
- return 1;
- }
- return 0;
-}
-
-
-/* the ztypes table */
-
-/**/
-mod_export short int typtab[256];
-static int typtab_flags = 0;
-
-/* initialize the ztypes table */
-
-/**/
-void
-inittyptab(void)
-{
- int t0;
- char *s;
-
- if (!(typtab_flags & ZTF_INIT)) {
- typtab_flags = ZTF_INIT;
- if (interact && isset(SHINSTDIN))
- typtab_flags |= ZTF_INTERACT;
- }
-
- queue_signals();
-
- memset(typtab, 0, sizeof(typtab));
- for (t0 = 0; t0 != 32; t0++)
- typtab[t0] = typtab[t0 + 128] = ICNTRL;
- typtab[127] = ICNTRL;
- for (t0 = '0'; t0 <= '9'; t0++)
- typtab[t0] = IDIGIT | IALNUM | IWORD | IIDENT | IUSER;
- for (t0 = 'a'; t0 <= 'z'; t0++)
- typtab[t0] = typtab[t0 - 'a' + 'A'] = IALPHA | IALNUM | IIDENT | IUSER | IWORD;
-#ifndef MULTIBYTE_SUPPORT
- /*
- * This really doesn't seem to me the right thing to do when
- * we have multibyte character support... it was a hack to assume
- * eight bit characters `worked' for some values of work before
- * we could test for them properly. I'm not 100% convinced
- * having IIDENT here is a good idea at all, but this code
- * should disappear into history...
- */
- for (t0 = 0240; t0 != 0400; t0++)
- typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD;
-#endif
- /* typtab['.'] |= IIDENT; */ /* Allow '.' in variable names - broken */
- typtab['_'] = IIDENT | IUSER;
- typtab['-'] = typtab['.'] = typtab[STOUC(Dash)] = IUSER;
- typtab[' '] |= IBLANK | INBLANK;
- typtab['\t'] |= IBLANK | INBLANK;
- typtab['\n'] |= INBLANK;
- typtab['\0'] |= IMETA;
- typtab[STOUC(Meta) ] |= IMETA;
- typtab[STOUC(Marker)] |= IMETA;
- for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(LAST_NORMAL_TOK); t0++)
- typtab[t0] |= ITOK | IMETA;
- for (t0 = (int)STOUC(Snull); t0 <= (int)STOUC(Nularg); t0++)
- typtab[t0] |= ITOK | IMETA | INULL;
- for (s = ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ?
- DEFAULT_IFS_SH : DEFAULT_IFS; *s; s++) {
- int c = STOUC(*s == Meta ? *++s ^ 32 : *s);
-#ifdef MULTIBYTE_SUPPORT
- if (!isascii(c)) {
- /* see comment for wordchars below */
- continue;
- }
-#endif
- if (inblank(c)) {
- if (s[1] == c)
- s++;
- else
- typtab[c] |= IWSEP;
- }
- typtab[c] |= ISEP;
- }
- for (s = wordchars ? wordchars : DEFAULT_WORDCHARS; *s; s++) {
- int c = STOUC(*s == Meta ? *++s ^ 32 : *s);
-#ifdef MULTIBYTE_SUPPORT
- if (!isascii(c)) {
- /*
- * If we have support for multibyte characters, we don't
- * handle non-ASCII characters here; instead, we turn
- * wordchars into a wide character array.
- * (We may actually have a single-byte 8-bit character set,
- * but it works the same way.)
- */
- continue;
- }
-#endif
- typtab[c] |= IWORD;
- }
-#ifdef MULTIBYTE_SUPPORT
- set_widearray(wordchars, &wordchars_wide);
- set_widearray(ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ?
- DEFAULT_IFS_SH : DEFAULT_IFS, &ifs_wide);
-#endif
- for (s = SPECCHARS; *s; s++)
- typtab[STOUC(*s)] |= ISPECIAL;
- if (typtab_flags & ZTF_SP_COMMA)
- typtab[STOUC(',')] |= ISPECIAL;
- if (isset(BANGHIST) && bangchar && (typtab_flags & ZTF_INTERACT)) {
- typtab_flags |= ZTF_BANGCHAR;
- typtab[bangchar] |= ISPECIAL;
- } else
- typtab_flags &= ~ZTF_BANGCHAR;
- for (s = PATCHARS; *s; s++)
- typtab[STOUC(*s)] |= IPATTERN;
-
- unqueue_signals();
-}
-
-/**/
-mod_export void
-makecommaspecial(int yesno)
-{
- if (yesno != 0) {
- typtab_flags |= ZTF_SP_COMMA;
- typtab[STOUC(',')] |= ISPECIAL;
- } else {
- typtab_flags &= ~ZTF_SP_COMMA;
- typtab[STOUC(',')] &= ~ISPECIAL;
- }
-}
-
-/**/
-mod_export void
-makebangspecial(int yesno)
-{
- /* Name and call signature for congruence with makecommaspecial(),
- * but in this case when yesno is nonzero we defer to the state
- * saved by inittyptab().
- */
- if (yesno == 0) {
- typtab[bangchar] &= ~ISPECIAL;
- } else if (typtab_flags & ZTF_BANGCHAR) {
- typtab[bangchar] |= ISPECIAL;
- }
-}
-
-
-/**/
-#ifdef MULTIBYTE_SUPPORT
-/* A wide-character version of the iblank() macro. */
-/**/
-mod_export int
-wcsiblank(wint_t wc)
-{
- if (iswspace(wc) && wc != L'\n')
- return 1;
- return 0;
-}
-
-/*
- * zistype macro extended to support wide characters.
- * Works for IIDENT, IWORD, IALNUM, ISEP.
- * We don't need this for IWSEP because that only applies to
- * a fixed set of ASCII characters.
- * Note here that use of multibyte mode is not tested:
- * that's because for ZLE this is unconditional,
- * not dependent on the option. The caller must decide.
- */
-
-/**/
-mod_export int
-wcsitype(wchar_t c, int itype)
-{
- int len;
- mbstate_t mbs;
- VARARR(char, outstr, MB_CUR_MAX);
-
- if (!isset(MULTIBYTE))
- return zistype(c, itype);
-
- /*
- * Strategy: the shell requires that the multibyte representation
- * be an extension of ASCII. So see if converting the character
- * produces an ASCII character. If it does, use zistype on that.
- * If it doesn't, use iswalnum on the original character.
- * If that fails, resort to the appropriate wide character array.
- */
- memset(&mbs, 0, sizeof(mbs));
- len = wcrtomb(outstr, c, &mbs);
-
- if (len == 0) {
- /* NULL is special */
- return zistype(0, itype);
- } else if (len == 1 && isascii(outstr[0])) {
- return zistype(outstr[0], itype);
- } else {
- switch (itype) {
- case IIDENT:
- if (!isset(POSIXIDENTIFIERS))
- return 0;
- return iswalnum(c);
-
- case IWORD:
- if (iswalnum(c))
- return 1;
- /*
- * If we are handling combining characters, any punctuation
- * characters with zero width needs to be considered part of
- * a word. If we are not handling combining characters then
- * logically they are still part of the word, even if they
- * don't get displayed properly, so always do this.
- */
- if (IS_COMBINING(c))
- return 1;
- return !!wmemchr(wordchars_wide.chars, c, wordchars_wide.len);
-
- case ISEP:
- return !!wmemchr(ifs_wide.chars, c, ifs_wide.len);
-
- default:
- return iswalnum(c);
- }
- }
-}
-
-/**/
-#endif
-
-
-/*
- * Find the end of a set of characters in the set specified by itype;
- * one of IALNUM, IIDENT, IWORD or IUSER. For non-ASCII characters, we assume
- * alphanumerics are part of the set, with the exception that
- * identifiers are not treated that way if POSIXIDENTIFIERS is set.
- *
- * See notes above for identifiers.
- * Returns the same pointer as passed if not on an identifier character.
- * If "once" is set, just test the first character, i.e. (outptr !=
- * inptr) tests whether the first character is valid in an identifier.
- *
- * Currently this is only called with itype IIDENT, IUSER or ISEP.
- */
-
-/**/
-mod_export char *
-itype_end(const char *ptr, int itype, int once)
-{
-#ifdef MULTIBYTE_SUPPORT
- if (isset(MULTIBYTE) &&
- (itype != IIDENT || !isset(POSIXIDENTIFIERS))) {
- mb_charinit();
- while (*ptr) {
- int len;
- if (itok(*ptr)) {
- /* Not untokenised yet --- can happen in raw command line */
- len = 1;
- if (!zistype(*ptr,itype))
- break;
- } else {
- wint_t wc;
- len = mb_metacharlenconv(ptr, &wc);
-
- if (!len)
- break;
-
- if (wc == WEOF) {
- /* invalid, treat as single character */
- int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr);
- /* in this case non-ASCII characters can't match */
- if (chr > 127 || !zistype(chr,itype))
- break;
- } else if (len == 1 && isascii(*ptr)) {
- /* ASCII: can't be metafied, use standard test */
- if (!zistype(*ptr,itype))
- break;
- } else {
- /*
- * Valid non-ASCII character.
- */
- switch (itype) {
- case IWORD:
- if (!iswalnum(wc) &&
- !wmemchr(wordchars_wide.chars, wc,
- wordchars_wide.len))
- return (char *)ptr;
- break;
-
- case ISEP:
- if (!wmemchr(ifs_wide.chars, wc, ifs_wide.len))
- return (char *)ptr;
- break;
-
- default:
- if (!iswalnum(wc))
- return (char *)ptr;
- }
- }
- }
- ptr += len;
-
- if (once)
- break;
- }
- } else
-#endif
- for (;;) {
- int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr);
- if (!zistype(chr,itype))
- break;
- ptr += (*ptr == Meta) ? 2 : 1;
-
- if (once)
- break;
- }
-
- /*
- * Nasty. The first argument is const char * because we
- * don't modify it here. However, we really want to pass
- * back the same type as was passed down, to allow idioms like
- * p = itype_end(p, IIDENT, 0);
- * So returning a const char * isn't really the right thing to do.
- * Without having two different functions the following seems
- * to be the best we can do.
- */
- return (char *)ptr;
-}
-
-/**/
-mod_export char **
-arrdup(char **s)
-{
- char **x, **y;
-
- y = x = (char **) zhalloc(sizeof(char *) * (arrlen(s) + 1));
-
- while ((*x++ = dupstring(*s++)));
-
- return y;
-}
-
-/* Duplicate at most max elements of the array s with heap memory */
-
-/**/
-mod_export char **
-arrdup_max(char **s, unsigned max)
-{
- char **x, **y, **send;
- int len = 0;
-
- if (max)
- len = arrlen(s);
-
- /* Limit has sense only if not equal to len */
- if (max > len)
- max = len;
-
- y = x = (char **) zhalloc(sizeof(char *) * (max + 1));
-
- send = s + max;
- while (s < send)
- *x++ = dupstring(*s++);
- *x = NULL;
-
- return y;
-}
-
-/**/
-mod_export char **
-zarrdup(char **s)
-{
- char **x, **y;
-
- y = x = (char **) zalloc(sizeof(char *) * (arrlen(s) + 1));
-
- while ((*x++ = ztrdup(*s++)));
-
- return y;
-}
-
-/**/
-#ifdef MULTIBYTE_SUPPORT
-/**/
-mod_export wchar_t **
-wcs_zarrdup(wchar_t **s)
-{
- wchar_t **x, **y;
-
- y = x = (wchar_t **) zalloc(sizeof(wchar_t *) * (arrlen((char **)s) + 1));
-
- while ((*x++ = wcs_ztrdup(*s++)));
-
- return y;
-}
-/**/
-#endif /* MULTIBYTE_SUPPORT */
-
-/**/
-static char *
-spname(char *oldname)
-{
- char *p, spnameguess[PATH_MAX + 1], spnamebest[PATH_MAX + 1];
- static char newname[PATH_MAX + 1];
- char *new = newname, *old = oldname;
- int bestdist = 0, thisdist, thresh, maxthresh = 0;
-
- /* This loop corrects each directory component of the path, stopping *
- * when any correction distance would exceed the distance threshold. *
- * NULL is returned only if the first component cannot be corrected; *
- * otherwise a copy of oldname with a corrected prefix is returned. *
- * Rationale for this, if there ever was any, has been forgotten. */
- for (;;) {
- while (*old == '/') {
- if (new >= newname + sizeof(newname) - 1)
- return NULL;
- *new++ = *old++;
- }
- *new = '\0';
- if (*old == '\0')
- return newname;
- p = spnameguess;
- for (; *old != '/' && *old != '\0'; old++)
- if (p < spnameguess + PATH_MAX)
- *p++ = *old;
- *p = '\0';
- /* Every component is allowed a single distance 2 correction or two *
- * distance 1 corrections. Longer ones get additional corrections. */
- thresh = (int)(p - spnameguess) / 4 + 1;
- if (thresh < 3)
- thresh = 3;
- else if (thresh > 100)
- thresh = 100;
- thisdist = mindist(newname, spnameguess, spnamebest, *old == '/');
- if (thisdist >= thresh) {
- /* The next test is always true, except for the first path *
- * component. We could initialize bestdist to some large *
- * constant instead, and then compare to that constant here, *
- * because an invariant is that we've never exceeded the *
- * threshold for any component so far; but I think that looks *
- * odd to the human reader, and we may make use of the total *
- * distance for all corrections at some point in the future. */
- if (bestdist < maxthresh) {
- struncpy(&new, spnameguess, sizeof(newname) - (new - newname));
- struncpy(&new, old, sizeof(newname) - (new - newname));
- return (new >= newname + sizeof(newname) -1) ? NULL : newname;
- } else
- return NULL;
- } else {
- maxthresh = bestdist + thresh;
- bestdist += thisdist;
- }
- for (p = spnamebest; (*new = *p++);) {
- if (new >= newname + sizeof(newname) - 1)
- return NULL;
- new++;
- }
- }
-}
-
-/**/
-static int
-mindist(char *dir, char *mindistguess, char *mindistbest, int wantdir)
-{
- int mindistd, nd;
- DIR *dd;
- char *fn;
- char *buf;
- struct stat st;
- size_t dirlen;
-
- if (dir[0] == '\0')
- dir = ".";
- mindistd = 100;
-
- if (!(buf = zalloc((dirlen = strlen(dir)) + strlen(mindistguess) + 2)))
- return 0;
- sprintf(buf, "%s/%s", dir, mindistguess);
-
- if (stat(unmeta(buf), &st) == 0 && (!wantdir || S_ISDIR(st.st_mode))) {
- strcpy(mindistbest, mindistguess);
- free(buf);
- return 0;
- }
-
- if ((dd = opendir(unmeta(dir)))) {
- while ((fn = zreaddir(dd, 0))) {
- if (spnamepat && pattry(spnamepat, fn))
- continue;
- nd = spdist(fn, mindistguess,
- (int)strlen(mindistguess) / 4 + 1);
- if (nd <= mindistd) {
- if (wantdir) {
- if (!(buf = zrealloc(buf, dirlen + strlen(fn) + 2)))
- continue;
- sprintf(buf, "%s/%s", dir, fn);
- if (stat(unmeta(buf), &st) != 0 || !S_ISDIR(st.st_mode))
- continue;
- }
- strcpy(mindistbest, fn);
- mindistd = nd;
- if (mindistd == 0)
- break;
- }
- }
- closedir(dd);
- }
- free(buf);
- return mindistd;
-}
-
-/**/
-static int
-spdist(char *s, char *t, int thresh)
-{
- /* TODO: Correction for non-ASCII and multibyte-input keyboards. */
- char *p, *q;
- const char qwertykeymap[] =
- "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
-\t1234567890-=\t\
-\tqwertyuiop[]\t\
-\tasdfghjkl;'\n\t\
-\tzxcvbnm,./\t\t\t\
-\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
-\t!@#$%^&*()_+\t\
-\tQWERTYUIOP{}\t\
-\tASDFGHJKL:\"\n\t\
-\tZXCVBNM<>?\n\n\t\
-\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
- const char dvorakkeymap[] =
- "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
-\t1234567890[]\t\
-\t',.pyfgcrl/=\t\
-\taoeuidhtns-\n\t\
-\t;qjkxbmwvz\t\t\t\
-\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
-\t!@#$%^&*(){}\t\
-\t\"<>PYFGCRL?+\t\
-\tAOEUIDHTNS_\n\t\
-\t:QJKXBMWVZ\n\n\t\
-\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
- const char *keymap;
- if ( isset( DVORAK ) )
- keymap = dvorakkeymap;
- else
- keymap = qwertykeymap;
-
- if (!strcmp(s, t))
- return 0;
- /* any number of upper/lower mistakes allowed (dist = 1) */
- for (p = s, q = t; *p && tulower(*p) == tulower(*q); p++, q++);
- if (!*p && !*q)
- return 1;
- if (!thresh)
- return 200;
- for (p = s, q = t; *p && *q; p++, q++)
- if (*p == *q)
- continue; /* don't consider "aa" transposed, ash */
- else if (p[1] == q[0] && q[1] == p[0]) /* transpositions */
- return spdist(p + 2, q + 2, thresh - 1) + 1;
- else if (p[1] == q[0]) /* missing letter */
- return spdist(p + 1, q + 0, thresh - 1) + 2;
- else if (p[0] == q[1]) /* missing letter */
- return spdist(p + 0, q + 1, thresh - 1) + 2;
- else if (*p != *q)
- break;
- if ((!*p && strlen(q) == 1) || (!*q && strlen(p) == 1))
- return 2;
- for (p = s, q = t; *p && *q; p++, q++)
- if (p[0] != q[0] && p[1] == q[1]) {
- int t0;
- char *z;
-
- /* mistyped letter */
-
- if (!(z = strchr(keymap, p[0])) || *z == '\n' || *z == '\t')
- return spdist(p + 1, q + 1, thresh - 1) + 1;
- t0 = z - keymap;
- if (*q == keymap[t0 - 15] || *q == keymap[t0 - 14] ||
- *q == keymap[t0 - 13] ||
- *q == keymap[t0 - 1] || *q == keymap[t0 + 1] ||
- *q == keymap[t0 + 13] || *q == keymap[t0 + 14] ||
- *q == keymap[t0 + 15])
- return spdist(p + 1, q + 1, thresh - 1) + 2;
- return 200;
- } else if (*p != *q)
- break;
- return 200;
-}
-
-/* set cbreak mode, or the equivalent */
-
-/**/
-void
-setcbreak(void)
-{
- struct ttyinfo ti;
-
- ti = shttyinfo;
-#ifdef HAS_TIO
- ti.tio.c_lflag &= ~ICANON;
- ti.tio.c_cc[VMIN] = 1;
- ti.tio.c_cc[VTIME] = 0;
-#else
- ti.sgttyb.sg_flags |= CBREAK;
-#endif
- settyinfo(&ti);
-}
-
-/* give the tty to some process */
-
-/**/
-mod_export void
-attachtty(pid_t pgrp)
-{
- static int ep = 0;
-
- if (jobbing && interact) {
-#ifdef HAVE_TCSETPGRP
- if (SHTTY != -1 && tcsetpgrp(SHTTY, pgrp) == -1 && !ep)
-#else
-# if ardent
- if (SHTTY != -1 && setpgrp() == -1 && !ep)
-# else
- int arg = pgrp;
-
- if (SHTTY != -1 && ioctl(SHTTY, TIOCSPGRP, &arg) == -1 && !ep)
-# endif
-#endif
- {
- if (pgrp != mypgrp && kill(-pgrp, 0) == -1)
- attachtty(mypgrp);
- else {
- if (errno != ENOTTY)
- {
- zwarn("can't set tty pgrp: %e", errno);
- fflush(stderr);
- }
- opts[MONITOR] = 0;
- ep = 1;
- }
- }
- }
-}
-
-/* get the process group associated with the tty */
-
-/**/
-pid_t
-gettygrp(void)
-{
- pid_t arg;
-
- if (SHTTY == -1)
- return -1;
-
-#ifdef HAVE_TCSETPGRP
- arg = tcgetpgrp(SHTTY);
-#else
- ioctl(SHTTY, TIOCGPGRP, &arg);
-#endif
-
- return arg;
-}
-
-
-/* Escape tokens and null characters. Buf is the string which should be *
- * escaped. len is the length of the string. If len is -1, buf should be *
- * null terminated. If len is non-negative and the third parameter is not *
- * META_DUP, buf should point to an at least len+1 long memory area. The *
- * return value points to the quoted string. If the given string does not *
- * contain any special character which should be quoted and the third *
- * parameter is not META_(HEAP|)DUP, buf is returned unchanged (a *
- * terminating null character is appended to buf if necessary). Otherwise *
- * the third `heap' argument determines the method used to allocate space *
- * for the result. It can have the following values: *
- * META_REALLOC: use zrealloc on buf *
- * META_HREALLOC: use hrealloc on buf *
- * META_USEHEAP: get memory from the heap. This leaves buf unchanged. *
- * META_NOALLOC: buf points to a memory area which is long enough to hold *
- * the quoted form, just quote it and return buf. *
- * META_STATIC: store the quoted string in a static area. The original *
- * string should be at most PATH_MAX long. *
- * META_ALLOC: allocate memory for the new string with zalloc(). *
- * META_DUP: leave buf unchanged and allocate space for the return *
- * value even if buf does not contains special characters *
- * META_HEAPDUP: same as META_DUP, but uses the heap */
-
-/**/
-mod_export char *
-metafy(char *buf, int len, int heap)
-{
- int meta = 0;
- char *t, *p, *e;
- static char mbuf[PATH_MAX*2+1];
-
- if (len == -1) {
- for (e = buf, len = 0; *e; len++)
- if (imeta(*e++))
- meta++;
- } else
- for (e = buf; e < buf + len;)
- if (imeta(*e++))
- meta++;
-
- if (meta || heap == META_DUP || heap == META_HEAPDUP) {
- switch (heap) {
- case META_REALLOC:
- buf = zrealloc(buf, len + meta + 1);
- break;
- case META_HREALLOC:
- buf = hrealloc(buf, len, len + meta + 1);
- break;
- case META_ALLOC:
- case META_DUP:
- buf = memcpy(zalloc(len + meta + 1), buf, len);
- break;
- case META_USEHEAP:
- case META_HEAPDUP:
- buf = memcpy(zhalloc(len + meta + 1), buf, len);
- break;
- case META_STATIC:
-#ifdef DEBUG
- if (len > PATH_MAX) {
- fprintf(stderr, "BUG: len = %d > PATH_MAX in metafy\n", len);
- fflush(stderr);
- }
-#endif
- buf = memcpy(mbuf, buf, len);
- break;
-#ifdef DEBUG
- case META_NOALLOC:
- break;
- default:
- fprintf(stderr, "BUG: metafy called with invalid heap value\n");
- fflush(stderr);
- break;
-#endif
- }
- p = buf + len;
- e = t = buf + len + meta;
- while (meta) {
- if (imeta(*--t = *--p)) {
- *t-- ^= 32;
- *t = Meta;
- meta--;
- }
- }
- }
- *e = '\0';
- return buf;
-}
-
-
-/*
- * Duplicate a string, metafying it as we go.
- *
- * Typically, this is used only for strings imported from outside
- * zsh, as strings internally are either already metafied or passed
- * around with an associated length.
- */
-/**/
-mod_export char *
-ztrdup_metafy(const char *s)
-{
- /* To mimic ztrdup() behaviour */
- if (!s)
- return NULL;
- /*
- * metafy() does lots of different things, so the pointer
- * isn't const. Using it with META_DUP should be safe.
- */
- return metafy((char *)s, -1, META_DUP);
-}
-
-
-/*
- * Take a null-terminated, metafied string in s into a literal
- * representation by converting in place. The length is in *len
- * len is non-NULL; if len is NULL, you don't know the length of
- * the final string, but if it's to be supplied to some system
- * routine that always uses NULL termination, such as a filename
- * interpreter, that doesn't matter. Note the NULL termination
- * is always copied for purposes of that kind.
- */
-
-/**/
-mod_export char *
-unmetafy(char *s, int *len)
-{
- char *p, *t;
-
- for (p = s; *p && *p != Meta; p++);
- for (t = p; (*t = *p++);)
- if (*t++ == Meta && *p)
- t[-1] = *p++ ^ 32;
- if (len)
- *len = t - s;
- return s;
-}
-
-/* Return the character length of a metafied substring, given the *
- * unmetafied substring length. */
-
-/**/
-mod_export int
-metalen(const char *s, int len)
-{
- int mlen = len;
-
- while (len--) {
- if (*s++ == Meta) {
- mlen++;
- s++;
- }
- }
- return mlen;
-}
-
-/*
- * This function converts a zsh internal string to a form which can be
- * passed to a system call as a filename. The result is stored in a
- * single static area, sized to fit. If there is no Meta character
- * the original string is returned.
- */
-
-/**/
-mod_export char *
-unmeta(const char *file_name)
-{
- static char *fn;
- static int sz;
- char *p;
- const char *t;
- int newsz, meta;
-
- if (!file_name)
- return NULL;
-
- meta = 0;
- for (t = file_name; *t; t++) {
- if (*t == Meta)
- meta = 1;
- }
- if (!meta) {
- /*
- * don't need allocation... free if it's long, see below
- */
- if (sz > 4 * PATH_MAX) {
- zfree(fn, sz);
- fn = NULL;
- sz = 0;
- }
- return (char *) file_name;
- }
-
- newsz = (t - file_name) + 1;
- /*
- * Optimisation: don't resize if we don't have to.
- * We need a new allocation if
- * - nothing was allocated before
- * - the new string is larger than the old one
- * - the old string was larger than an arbitrary limit but the
- * new string isn't so that we free up significant space by resizing.
- */
- if (!fn || newsz > sz || (sz > 4 * PATH_MAX && newsz <= 4 * PATH_MAX))
- {
- if (fn)
- zfree(fn, sz);
- sz = newsz;
- fn = (char *)zalloc(sz);
- if (!fn) {
- sz = 0;
- /*
- * will quite likely crash in the caller anyway...
- */
- return NULL;
- }
- }
-
- for (t = file_name, p = fn; *t; p++)
- if ((*p = *t++) == Meta && *t)
- *p = *t++ ^ 32;
- *p = '\0';
- return fn;
-}
-
-/*
- * Unmetafy just one character and store the number of bytes it occupied.
- */
-/**/
-mod_export convchar_t
-unmeta_one(const char *in, int *sz)
-{
- convchar_t wc;
- int newsz;
-#ifdef MULTIBYTE_SUPPORT
- mbstate_t wstate;
-#endif
-
- if (!sz)
- sz = &newsz;
- *sz = 0;
-
- if (!in || !*in)
- return 0;
-
-#ifdef MULTIBYTE_SUPPORT
- memset(&wstate, 0, sizeof(wstate));
- *sz = mb_metacharlenconv_r(in, &wc, &wstate);
-#else
- if (in[0] == Meta) {
- *sz = 2;
- wc = STOUC(in[1] ^ 32);
- } else {
- *sz = 1;
- wc = STOUC(in[0]);
- }
-#endif
- return wc;
-}
-
-/*
- * Unmetafy and compare two strings, comparing unsigned character values.
- * "a\0" sorts after "a".
- *
- * Currently this is only used in hash table sorting, where the
- * keys are names of hash nodes and where we don't use strcoll();
- * it's not clear if that's right but it does guarantee the ordering
- * of shell structures on output.
- *
- * As we don't use strcoll(), it seems overkill to convert multibyte
- * characters to wide characters for comparison every time. In the case
- * of UTF-8, Unicode ordering is preserved when sorted raw, and for
- * other character sets we rely on an extension of ASCII so the result,
- * while it may not be correct, is at least rational.
- */
-
-/**/
-int
-ztrcmp(char const *s1, char const *s2)
-{
- int c1, c2;
-
- while(*s1 && *s1 == *s2) {
- s1++;
- s2++;
- }
-
- if(!(c1 = *s1))
- c1 = -1;
- else if(c1 == STOUC(Meta))
- c1 = *++s1 ^ 32;
- if(!(c2 = *s2))
- c2 = -1;
- else if(c2 == STOUC(Meta))
- c2 = *++s2 ^ 32;
-
- if(c1 == c2)
- return 0;
- else if(c1 < c2)
- return -1;
- else
- return 1;
-}
-
-/* Return the unmetafied length of a metafied string. */
-
-/**/
-mod_export int
-ztrlen(char const *s)
-{
- int l;
-
- for (l = 0; *s; l++) {
- if (*s++ == Meta) {
-#ifdef DEBUG
- if (! *s) {
- fprintf(stderr, "BUG: unexpected end of string in ztrlen()\n");
- break;
- } else
-#endif
- s++;
- }
- }
- return l;
-}
-
-#ifndef MULTIBYTE_SUPPORT
-/*
- * ztrlen() but with explicit end point for non-null-terminated
- * segments. eptr may not be NULL.
- */
-
-/**/
-mod_export int
-ztrlenend(char const *s, char const *eptr)
-{
- int l;
-
- for (l = 0; s < eptr; l++) {
- if (*s++ == Meta) {
-#ifdef DEBUG
- if (! *s) {
- fprintf(stderr,
- "BUG: unexpected end of string in ztrlenend()\n");
- break;
- } else
-#endif
- s++;
- }
- }
- return l;
-}
-
-#endif /* MULTIBYTE_SUPPORT */
-
-/* Subtract two pointers in a metafied string. */
-
-/**/
-mod_export int
-ztrsub(char const *t, char const *s)
-{
- int l = t - s;
-
- while (s != t) {
- if (*s++ == Meta) {
-#ifdef DEBUG
- if (! *s || s == t)
- fprintf(stderr, "BUG: substring ends in the middle of a metachar in ztrsub()\n");
- else
-#endif
- s++;
- l--;
- }
- }
- return l;
-}
-
-/*
- * Wrapper for readdir().
- *
- * If ignoredots is true, skip the "." and ".." entries.
- *
- * When __APPLE__ is defined, recode dirent names from UTF-8-MAC to UTF-8.
- *
- * Return the dirent's name, metafied.
- */
-
-/**/
-mod_export char *
-zreaddir(DIR *dir, int ignoredots)
-{
- struct dirent *de;
-#if defined(HAVE_ICONV) && defined(__APPLE__)
- static iconv_t conv_ds = (iconv_t)0;
- static char *conv_name = 0;
- char *conv_name_ptr, *orig_name_ptr;
- size_t conv_name_len, orig_name_len;
-#endif
-
- do {
- de = readdir(dir);
- if(!de)
- return NULL;
- } while(ignoredots && de->d_name[0] == '.' &&
- (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])));
-
-#if defined(HAVE_ICONV) && defined(__APPLE__)
- if (!conv_ds)
- conv_ds = iconv_open("UTF-8", "UTF-8-MAC");
- if (conv_ds != (iconv_t)(-1)) {
- /* Force initial state in case re-using conv_ds */
- (void) iconv(conv_ds, 0, &orig_name_len, 0, &conv_name_len);
-
- orig_name_ptr = de->d_name;
- orig_name_len = strlen(de->d_name);
- conv_name = zrealloc(conv_name, orig_name_len+1);
- conv_name_ptr = conv_name;
- conv_name_len = orig_name_len;
- if (iconv(conv_ds,
- &orig_name_ptr, &orig_name_len,
- &conv_name_ptr, &conv_name_len) != (size_t)(-1) &&
- orig_name_len == 0) {
- /* Completely converted, metafy and return */
- *conv_name_ptr = '\0';
- return metafy(conv_name, -1, META_STATIC);
- }
- /* Error, or conversion incomplete, keep the original name */
- }
-#endif
-
- return metafy(de->d_name, -1, META_STATIC);
-}
-
-/* Unmetafy and output a string. Tokens are skipped. */
-
-/**/
-mod_export int
-zputs(char const *s, FILE *stream)
-{
- int c;
-
- while (*s) {
- if (*s == Meta)
- c = *++s ^ 32;
- else if(itok(*s)) {
- s++;
- continue;
- } else
- c = *s;
- s++;
- if (fputc(c, stream) < 0)
- return EOF;
- }
- return 0;
-}
-
-#ifndef MULTIBYTE_SUPPORT
-/* Create a visibly-represented duplicate of a string. */
-
-/**/
-mod_export char *
-nicedup(char const *s, int heap)
-{
- int c, len = strlen(s) * 5 + 1;
- VARARR(char, buf, len);
- char *p = buf, *n;
-
- while ((c = *s++)) {
- if (itok(c)) {
- if (c <= Comma)
- c = ztokens[c - Pound];
- else
- continue;
- }
- if (c == Meta)
- c = *s++ ^ 32;
- /* The result here is metafied */
- n = nicechar(c);
- while(*n)
- *p++ = *n++;
- }
- *p = '\0';
- return heap ? dupstring(buf) : ztrdup(buf);
-}
-#endif
-
-/**/
-mod_export char *
-nicedupstring(char const *s)
-{
- return nicedup(s, 1);
-}
-
-
-#ifndef MULTIBYTE_SUPPORT
-/* Unmetafy and output a string, displaying special characters readably. */
-
-/**/
-mod_export int
-nicezputs(char const *s, FILE *stream)
-{
- int c;
-
- while ((c = *s++)) {
- if (itok(c)) {
- if (c <= Comma)
- c = ztokens[c - Pound];
- else
- continue;
- }
- if (c == Meta)
- c = *s++ ^ 32;
- if(zputs(nicechar(c), stream) < 0)
- return EOF;
- }
- return 0;
-}
-
-
-/* Return the length of the visible representation of a metafied string. */
-
-/**/
-mod_export size_t
-niceztrlen(char const *s)
-{
- size_t l = 0;
- int c;
-
- while ((c = *s++)) {
- if (itok(c)) {
- if (c <= Comma)
- c = ztokens[c - Pound];
- else
- continue;
- }
- if (c == Meta)
- c = *s++ ^ 32;
- l += strlen(nicechar(c));
- }
- return l;
-}
-#endif
-
-
-/**/
-#ifdef MULTIBYTE_SUPPORT
-/*
- * Version of both nicezputs() and niceztrlen() for use with multibyte
- * characters. Input is a metafied string; output is the screen width of
- * the string.
- *
- * If the FILE * is not NULL, output to that, too.
- *
- * If outstrp is not NULL, set *outstrp to a zalloc'd version of
- * the output (still metafied).
- *
- * If flags contains NICEFLAG_HEAP, use the heap for *outstrp, else
- * zalloc.
- * If flags contsins NICEFLAG_QUOTE, the output is going to be within
- * $'...', so quote "'" and "\" with a backslash.
- */
-
-/**/
-mod_export size_t
-mb_niceformat(const char *s, FILE *stream, char **outstrp, int flags)
-{
- size_t l = 0, newl;
- int umlen, outalloc, outleft, eol = 0;
- wchar_t c;
- char *ums, *ptr, *fmt, *outstr, *outptr;
- mbstate_t mbs;
-
- if (outstrp) {
- outleft = outalloc = 5 * strlen(s);
- outptr = outstr = zalloc(outalloc);
- } else {
- outleft = outalloc = 0;
- outptr = outstr = NULL;
- }
-
- ums = ztrdup(s);
- /*
- * is this necessary at this point? niceztrlen does this
- * but it's used in lots of places. however, one day this may
- * be, too.
- */
- untokenize(ums);
- ptr = unmetafy(ums, &umlen);
-
- memset(&mbs, 0, sizeof mbs);
- while (umlen > 0) {
- size_t cnt = eol ? MB_INVALID : mbrtowc(&c, ptr, umlen, &mbs);
-
- switch (cnt) {
- case MB_INCOMPLETE:
- eol = 1;
- /* FALL THROUGH */
- case MB_INVALID:
- /* The byte didn't convert, so output it as a \M-... sequence. */
- fmt = nicechar_sel(*ptr, flags & NICEFLAG_QUOTE);
- newl = strlen(fmt);
- cnt = 1;
- /* Get mbs out of its undefined state. */
- memset(&mbs, 0, sizeof mbs);
- break;
- case 0:
- /* Careful: converting '\0' returns 0, but a '\0' is a
- * real character for us, so we should consume 1 byte. */
- cnt = 1;
- /* FALL THROUGH */
- default:
- if (c == L'\'' && (flags & NICEFLAG_QUOTE)) {
- fmt = "\\'";
- newl = 2;
- }
- else if (c == L'\\' && (flags & NICEFLAG_QUOTE)) {
- fmt = "\\\\";
- newl = 2;
- }
- else
- fmt = wcs_nicechar_sel(c, &newl, NULL, flags & NICEFLAG_QUOTE);
- break;
- }
-
- umlen -= cnt;
- ptr += cnt;
- l += newl;
-
- if (stream)
- zputs(fmt, stream);
- if (outstr) {
- /* Append to output string */
- int outlen = strlen(fmt);
- if (outlen >= outleft) {
- /* Reallocate to twice the length */
- int outoffset = outptr - outstr;
-
- outleft += outalloc;
- outalloc *= 2;
- outstr = zrealloc(outstr, outalloc);
- outptr = outstr + outoffset;
- }
- memcpy(outptr, fmt, outlen);
- /* Update start position */
- outptr += outlen;
- /* Update available bytes */
- outleft -= outlen;
- }
- }
-
- free(ums);
- if (outstrp) {
- *outptr = '\0';
- /* Use more efficient storage for returned string */
- if (flags & NICEFLAG_NODUP)
- *outstrp = outstr;
- else {
- *outstrp = (flags & NICEFLAG_HEAP) ? dupstring(outstr) :
- ztrdup(outstr);
- free(outstr);
- }
- }
-
- return l;
-}
-
-/*
- * Return 1 if mb_niceformat() would reformat this string, else 0.
- */
-
-/**/
-mod_export int
-is_mb_niceformat(const char *s)
-{
- int umlen, eol = 0, ret = 0;
- wchar_t c;
- char *ums, *ptr;
- mbstate_t mbs;
-
- ums = ztrdup(s);
- untokenize(ums);
- ptr = unmetafy(ums, &umlen);
-
- memset(&mbs, 0, sizeof mbs);
- while (umlen > 0) {
- size_t cnt = eol ? MB_INVALID : mbrtowc(&c, ptr, umlen, &mbs);
-
- switch (cnt) {
- case MB_INCOMPLETE:
- eol = 1;
- /* FALL THROUGH */
- case MB_INVALID:
- /* The byte didn't convert, so output it as a \M-... sequence. */
- if (is_nicechar(*ptr)) {
- ret = 1;
- break;
- }
- cnt = 1;
- /* Get mbs out of its undefined state. */
- memset(&mbs, 0, sizeof mbs);
- break;
- case 0:
- /* Careful: converting '\0' returns 0, but a '\0' is a
- * real character for us, so we should consume 1 byte. */
- cnt = 1;
- /* FALL THROUGH */
- default:
- if (is_wcs_nicechar(c))
- ret = 1;
- break;
- }
-
- if (ret)
- break;
-
- umlen -= cnt;
- ptr += cnt;
- }
-
- free(ums);
-
- return ret;
-}
-
-/* ztrdup multibyte string with nice formatting */
-
-/**/
-mod_export char *
-nicedup(const char *s, int heap)
-{
- char *retstr;
-
- (void)mb_niceformat(s, NULL, &retstr, heap ? NICEFLAG_HEAP : 0);
-
- return retstr;
-}
-
-
-/*
- * The guts of mb_metacharlenconv(). This version assumes we are
- * processing a true multibyte character string without tokens, and
- * takes the shift state as an argument.
- */
-
-/**/
-mod_export int
-mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp)
-{
- size_t ret = MB_INVALID;
- char inchar;
- const char *ptr;
- wchar_t wc;
-
- if (STOUC(*s) <= 0x7f) {
- if (wcp)
- *wcp = (wint_t)*s;
- return 1;
- }
-
- for (ptr = s; *ptr; ) {
- if (*ptr == Meta) {
- inchar = *++ptr ^ 32;
- DPUTS(!*ptr,
- "BUG: unexpected end of string in mb_metacharlen()\n");
- } else if (imeta(*ptr)) {
- /*
- * As this is metafied input, this is a token --- this
- * can't be a part of the string. It might be
- * something on the end of an unbracketed parameter
- * reference, for example.
- */
- break;
- } else
- inchar = *ptr;
- ptr++;
- ret = mbrtowc(&wc, &inchar, 1, mbsp);
-
- if (ret == MB_INVALID)
- break;
- if (ret == MB_INCOMPLETE)
- continue;
- if (wcp)
- *wcp = wc;
- return ptr - s;
- }
-
- if (wcp)
- *wcp = WEOF;
- /* No valid multibyte sequence */
- memset(mbsp, 0, sizeof(*mbsp));
- if (ptr > s) {
- return 1 + (*s == Meta); /* Treat as single byte character */
- } else
- return 0; /* Probably shouldn't happen */
-}
-
-/*
- * Length of metafied string s which contains the next multibyte
- * character; single (possibly metafied) character if string is not null
- * but character is not valid (e.g. possibly incomplete at end of string).
- * Returned value is guaranteed not to reach beyond the end of the
- * string (assuming correct metafication).
- *
- * If wcp is not NULL, the converted wide character is stored there.
- * If no conversion could be done WEOF is used.
- */
-
-/**/
-mod_export int
-mb_metacharlenconv(const char *s, wint_t *wcp)
-{
- if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) {
- /* treat as single byte, possibly metafied */
- if (wcp)
- *wcp = (wint_t)(*s == Meta ? s[1] ^ 32 : *s);
- return 1 + (*s == Meta);
- }
- /*
- * We have to handle tokens here, since we may be looking
- * through a tokenized input. Obviously this isn't
- * a valid multibyte character, so just return WEOF
- * and let the caller handle it as a single character.
- *
- * TODO: I've a sneaking suspicion we could do more here
- * to prevent the caller always needing to handle invalid
- * characters specially, but sometimes it may need to know.
- */
- if (itok(*s)) {
- if (wcp)
- *wcp = WEOF;
- return 1;
- }
-
- return mb_metacharlenconv_r(s, wcp, &mb_shiftstate);
-}
-
-/*
- * Total number of multibyte characters in metafied string s.
- * Same answer as iterating mb_metacharlen() and counting calls
- * until end of string.
- *
- * If width is 1, return total character width rather than number.
- * If width is greater than 1, return 1 if character has non-zero width,
- * else 0.
- *
- * Ends if either *ptr is '\0', the normal case (eptr may be NULL for
- * this), or ptr is eptr (i.e. *eptr is where the null would be if null
- * terminated) for strings not delimited by nulls --- note these are
- * still metafied.
- */
-
-/**/
-mod_export int
-mb_metastrlenend(char *ptr, int width, char *eptr)
-{
- char inchar, *laststart;
- size_t ret;
- wchar_t wc;
- int num, num_in_char, complete;
-
- if (!isset(MULTIBYTE) || MB_CUR_MAX == 1)
- return eptr ? (int)(eptr - ptr) : ztrlen(ptr);
-
- laststart = ptr;
- ret = MB_INVALID;
- num = num_in_char = 0;
- complete = 1;
-
- memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
- while (*ptr && !(eptr && ptr >= eptr)) {
- if (*ptr == Meta)
- inchar = *++ptr ^ 32;
- else
- inchar = *ptr;
- ptr++;
-
- if (complete && STOUC(inchar) <= STOUC(0x7f)) {
- /*
- * We rely on 7-bit US-ASCII as a subset, so skip
- * multibyte handling if we have such a character.
- */
- num++;
- laststart = ptr;
- num_in_char = 0;
- continue;
- }
-
- ret = mbrtowc(&wc, &inchar, 1, &mb_shiftstate);
-
- if (ret == MB_INCOMPLETE) {
- /*
- * "num_in_char" is only used for incomplete characters.
- * The assumption is that we will output all trailing octets
- * that form part of an incomplete character as a single
- * character (of single width) if we don't get a complete
- * character. This is purely pragmatic --- I'm not aware
- * of a standard way of dealing with incomplete characters.
- *
- * If we do get a complete character, num_in_char
- * becomes irrelevant and is set to zero
- *
- * This is in contrast to "num" which counts the characters
- * or widths in complete characters. The two are summed,
- * so we don't count characters twice.
- */
- num_in_char++;
- complete = 0;
- } else {
- if (ret == MB_INVALID) {
- /* Reset, treat as single character */
- memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
- ptr = laststart + (*laststart == Meta) + 1;
- num++;
- } else if (width) {
- /*
- * Returns -1 if not a printable character. We
- * turn this into 0.
- */
- int wcw = WCWIDTH(wc);
- if (wcw > 0) {
- if (width == 1)
- num += wcw;
- else
- num++;
- }
- } else
- num++;
- laststart = ptr;
- num_in_char = 0;
- complete = 1;
- }
- }
-
- /* If incomplete, treat remainder as trailing single character */
- return num + (num_in_char ? 1 : 0);
-}
-
-/*
- * The equivalent of mb_metacharlenconv_r() for
- * strings that aren't metafied and hence have
- * explicit lengths.
- */
-
-/**/
-mod_export int
-mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp)
-{
- size_t ret = MB_INVALID;
- char inchar;
- const char *ptr;
- wchar_t wc;
-
- if (slen && STOUC(*s) <= 0x7f) {
- if (wcp)
- *wcp = (wint_t)*s;
- return 1;
- }
-
- for (ptr = s; slen; ) {
- inchar = *ptr;
- ptr++;
- slen--;
- ret = mbrtowc(&wc, &inchar, 1, mbsp);
-
- if (ret == MB_INVALID)
- break;
- if (ret == MB_INCOMPLETE)
- continue;
- if (wcp)
- *wcp = wc;
- return ptr - s;
- }
-
- if (wcp)
- *wcp = WEOF;
- /* No valid multibyte sequence */
- memset(mbsp, 0, sizeof(*mbsp));
- if (ptr > s) {
- return 1; /* Treat as single byte character */
- } else
- return 0; /* Probably shouldn't happen */
-}
-
-/*
- * The equivalent of mb_metacharlenconv() for
- * strings that aren't metafied and hence have
- * explicit lengths;
- */
-
-/**/
-mod_export int
-mb_charlenconv(const char *s, int slen, wint_t *wcp)
-{
- if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) {
- if (wcp)
- *wcp = (wint_t)*s;
- return 1;
- }
-
- return mb_charlenconv_r(s, slen, wcp, &mb_shiftstate);
-}
-
-/**/
-#else
-
-/* Simple replacement for mb_metacharlenconv */
-
-/**/
-mod_export int
-metacharlenconv(const char *x, int *c)
-{
- /*
- * Here we don't use STOUC() on the chars since they
- * may be compared against other chars and this will fail
- * if chars are signed and the high bit is set.
- */
- if (*x == Meta) {
- if (c)
- *c = x[1] ^ 32;
- return 2;
- }
- if (c)
- *c = (char)*x;
- return 1;
-}
-
-/* Simple replacement for mb_charlenconv */
-
-/**/
-mod_export int
-charlenconv(const char *x, int len, int *c)
-{
- if (!len) {
- if (c)
- *c = '\0';
- return 0;
- }
-
- if (c)
- *c = (char)*x;
- return 1;
-}
-
-/**/
-#endif /* MULTIBYTE_SUPPORT */
-
-/*
- * Expand tabs to given width, with given starting position on line.
- * len is length of unmetafied string in bytes.
- * Output to fout.
- * Return the end position on the line, i.e. if this is 0 modulo width
- * the next character is aligned with a tab stop.
- *
- * If all is set, all tabs are expanded, else only leading tabs.
- */
-
-/**/
-mod_export int
-zexpandtabs(const char *s, int len, int width, int startpos, FILE *fout,
- int all)
-{
- int at_start = 1;
-
-#ifdef MULTIBYTE_SUPPORT
- mbstate_t mbs;
- size_t ret;
- wchar_t wc;
-
- memset(&mbs, 0, sizeof(mbs));
-#endif
-
- while (len) {
- if (*s == '\t') {
- if (all || at_start) {
- s++;
- len--;
- if (width <= 0 || !(startpos % width)) {
- /* always output at least one space */
- fputc(' ', fout);
- startpos++;
- }
- if (width <= 0)
- continue; /* paranoia */
- while (startpos % width) {
- fputc(' ', fout);
- startpos++;
- }
- } else {
- /*
- * Leave tab alone.
- * Guess width to apply... we might get this wrong.
- * This is only needed if there's a following string
- * that needs tabs expanding, which is unusual.
- */
- startpos += width - startpos % width;
- s++;
- len--;
- fputc('\t', fout);
- }
- continue;
- } else if (*s == '\n' || *s == '\r') {
- fputc(*s, fout);
- s++;
- len--;
- startpos = 0;
- at_start = 1;
- continue;
- }
-
- at_start = 0;
-#ifdef MULTIBYTE_SUPPORT
- if (isset(MULTIBYTE)) {
- const char *sstart = s;
- ret = mbrtowc(&wc, s, len, &mbs);
- if (ret == MB_INVALID) {
- /* Assume single character per character */
- memset(&mbs, 0, sizeof(mbs));
- s++;
- len--;
- } else if (ret == MB_INCOMPLETE) {
- /* incomplete at end --- assume likewise, best we've got */
- s++;
- len--;
- } else {
- s += ret;
- len -= (int)ret;
- }
- if (ret == MB_INVALID || ret == MB_INCOMPLETE) {
- startpos++;
- } else {
- int wcw = WCWIDTH(wc);
- if (wcw > 0) /* paranoia */
- startpos += wcw;
- }
- fwrite(sstart, s - sstart, 1, fout);
-
- continue;
- }
-#endif /* MULTIBYTE_SUPPORT */
- fputc(*s, fout);
- s++;
- len--;
- startpos++;
- }
-
- return startpos;
-}
-
-/* check for special characters in the string */
-
-/**/
-mod_export int
-hasspecial(char const *s)
-{
- for (; *s; s++) {
- if (ispecial(*s == Meta ? *++s ^ 32 : *s))
- return 1;
- }
- return 0;
-}
-
-
-static char *
-addunprintable(char *v, const char *u, const char *uend)
-{
- for (; u < uend; u++) {
- /*
- * Just do this byte by byte; there's no great
- * advantage in being clever with multibyte
- * characters if we don't think they're printable.
- */
- int c;
- if (*u == Meta)
- c = STOUC(*++u ^ 32);
- else
- c = STOUC(*u);
- switch (c) {
- case '\0':
- *v++ = '\\';
- *v++ = '0';
- if ('0' <= u[1] && u[1] <= '7') {
- *v++ = '0';
- *v++ = '0';
- }
- break;
-
- case '\007': *v++ = '\\'; *v++ = 'a'; break;
- case '\b': *v++ = '\\'; *v++ = 'b'; break;
- case '\f': *v++ = '\\'; *v++ = 'f'; break;
- case '\n': *v++ = '\\'; *v++ = 'n'; break;
- case '\r': *v++ = '\\'; *v++ = 'r'; break;
- case '\t': *v++ = '\\'; *v++ = 't'; break;
- case '\v': *v++ = '\\'; *v++ = 'v'; break;
-
- default:
- *v++ = '\\';
- *v++ = '0' + ((c >> 6) & 7);
- *v++ = '0' + ((c >> 3) & 7);
- *v++ = '0' + (c & 7);
- break;
- }
- }
-
- return v;
-}
-
-/*
- * Quote the string s and return the result as a string from the heap.
- *
- * The last argument is a QT_ value defined in zsh.h other than QT_NONE.
- *
- * Most quote styles other than backslash assume the quotes are to
- * be added outside quotestring(). QT_SINGLE_OPTIONAL is different:
- * the single quotes are only added where necessary, so the
- * whole expression is handled here.
- *
- * The string may be metafied and contain tokens.
- */
-
-/**/
-mod_export char *
-quotestring(const char *s, int instring)
-{
- const char *u;
- char *v;
- int alloclen;
- char *buf;
- int shownull = 0;
- /*
- * quotesub is used with QT_SINGLE_OPTIONAL.
- * quotesub = 0: mechanism not active
- * quotesub = 1: mechanism pending, no "'" yet;
- * needs adding at quotestart.
- * quotesub = 2: mechanism active, added opening "'"; need
- * closing "'".
- */
- int quotesub = 0, slen;
- char *quotestart;
- convchar_t cc;
- const char *uend;
-
- slen = strlen(s);
- switch (instring)
- {
- case QT_BACKSLASH_SHOWNULL:
- shownull = 1;
- instring = QT_BACKSLASH;
- /*FALLTHROUGH*/
- case QT_BACKSLASH:
- /*
- * With QT_BACKSLASH we may need to use $'\300' stuff.
- * Keep memory usage within limits by allocating temporary
- * storage and using heap for correct size at end.
- */
- alloclen = slen * 7 + 1;
- break;
-
- case QT_BACKSLASH_PATTERN:
- alloclen = slen * 2 + 1;
- break;
-
- case QT_SINGLE_OPTIONAL:
- /*
- * Here, we may need to add single quotes.
- * Always show empty strings.
- */
- alloclen = slen * 4 + 3;
- quotesub = shownull = 1;
- break;
-
- default:
- alloclen = slen * 4 + 1;
- break;
- }
- if (!*s && shownull)
- alloclen += 2; /* for '' */
-
- quotestart = v = buf = zshcalloc(alloclen);
-
- DPUTS(instring < QT_BACKSLASH || instring == QT_BACKTICK ||
- instring > QT_BACKSLASH_PATTERN,
- "BUG: bad quote type in quotestring");
- u = s;
- if (instring == QT_DOLLARS) {
- /*
- * The only way to get Nularg here is when
- * it is placeholding for the empty string?
- */
- if (inull(*u))
- u++;
- /*
- * As we test for printability here we need to be able
- * to look for multibyte characters.
- */
- MB_METACHARINIT();
- while (*u) {
- uend = u + MB_METACHARLENCONV(u, &cc);
-
- if (
-#ifdef MULTIBYTE_SUPPORT
- cc != WEOF &&
-#endif
- WC_ISPRINT(cc)) {
- switch (cc) {
- case ZWC('\\'):
- case ZWC('\''):
- *v++ = '\\';
- break;
-
- default:
- if (isset(BANGHIST) && cc == (wchar_t)bangchar)
- *v++ = '\\';
- break;
- }
- while (u < uend)
- *v++ = *u++;
- } else {
- /* Not printable */
- v = addunprintable(v, u, uend);
- u = uend;
- }
- }
- } else if (instring == QT_BACKSLASH_PATTERN) {
- while (*u) {
- if (ipattern(*u))
- *v++ = '\\';
- *v++ = *u++;
- }
- } else {
- if (shownull) {
- /* We can't show an empty string with just backslash quoting. */
- if (!*u) {
- *v++ = '\'';
- *v++ = '\'';
- }
- }
- /*
- * Here there are syntactic special characters, so
- * we start by going through bytewise.
- */
- while (*u) {
- int dobackslash = 0;
- if (*u == Tick || *u == Qtick) {
- char c = *u++;
-
- *v++ = c;
- while (*u && *u != c)
- *v++ = *u++;
- *v++ = c;
- if (*u)
- u++;
- continue;
- } else if ((*u == Qstring || *u == '$') && u[1] == '\'' &&
- instring == QT_DOUBLE) {
- /*
- * We don't need to quote $'...' inside a double-quoted
- * string. This is largely cosmetic; it looks neater
- * if we don't but it doesn't do any harm since the
- * \ is stripped.
- */
- *v++ = *u++;
- } else if ((*u == String || *u == Qstring) &&
- (u[1] == Inpar || u[1] == Inbrack || u[1] == Inbrace)) {
- char c = (u[1] == Inpar ? Outpar : (u[1] == Inbrace ?
- Outbrace : Outbrack));
- char beg = *u;
- int level = 0;
-
- *v++ = *u++;
- *v++ = *u++;
- while (*u && (*u != c || level)) {
- if (*u == beg)
- level++;
- else if (*u == c)
- level--;
- *v++ = *u++;
- }
- if (*u)
- *v++ = *u++;
- continue;
- }
- else if (ispecial(*u) &&
- ((*u != '=' && *u != '~') ||
- u == s ||
- (isset(MAGICEQUALSUBST) &&
- (u[-1] == '=' || u[-1] == ':')) ||
- (*u == '~' && isset(EXTENDEDGLOB))) &&
- (instring == QT_BACKSLASH ||
- instring == QT_SINGLE_OPTIONAL ||
- (isset(BANGHIST) && *u == (char)bangchar &&
- instring != QT_SINGLE) ||
- (instring == QT_DOUBLE &&
- (*u == '$' || *u == '`' || *u == '\"' || *u == '\\')) ||
- (instring == QT_SINGLE && *u == '\''))) {
- if (instring == QT_SINGLE_OPTIONAL) {
- if (quotesub == 1) {
- /*
- * We haven't yet had to quote at the start.
- */
- if (*u == '\'') {
- /*
- * We don't need to.
- */
- *v++ = '\\';
- } else {
- /*
- * It's now time to add quotes.
- */
- if (v > quotestart)
- {
- char *addq;
-
- for (addq = v; addq > quotestart; addq--)
- *addq = addq[-1];
- }
- *quotestart = '\'';
- v++;
- quotesub = 2;
- }
- *v++ = *u++;
- /*
- * Next place to start quotes is here.
- */
- quotestart = v;
- } else if (*u == '\'') {
- if (unset(RCQUOTES)) {
- *v++ = '\'';
- *v++ = '\\';
- *v++ = '\'';
- /* Don't restart quotes unless we need them */
- quotesub = 1;
- quotestart = v;
- } else {
- /* simplest just to use '' always */
- *v++ = '\'';
- *v++ = '\'';
- }
- /* dealt with */
- u++;
- } else {
- /* else already quoting, just add */
- *v++ = *u++;
- }
- continue;
- } else if (*u == '\n' ||
- (instring == QT_SINGLE && *u == '\'')) {
- if (*u == '\n') {
- *v++ = '$';
- *v++ = '\'';
- *v++ = '\\';
- *v++ = 'n';
- *v++ = '\'';
- } else if (unset(RCQUOTES)) {
- *v++ = '\'';
- if (*u == '\'')
- *v++ = '\\';
- *v++ = *u;
- *v++ = '\'';
- } else
- *v++ = '\'', *v++ = '\'';
- u++;
- continue;
- } else {
- /*
- * We'll need a backslash, but don't add it
- * yet since if the character isn't printable
- * we'll have to upgrade it to $'...'.
- */
- dobackslash = 1;
- }
- }
-
- if (itok(*u) || instring != QT_BACKSLASH) {
- /* Needs to be passed straight through. */
- if (dobackslash)
- *v++ = '\\';
- if (*u == Inparmath) {
- /*
- * Already syntactically quoted: don't
- * add more.
- */
- int inmath = 1;
- *v++ = *u++;
- for (;;) {
- char uc = *u;
- *v++ = *u++;
- if (uc == '\0')
- break;
- else if (uc == Outparmath && !--inmath)
- break;
- else if (uc == Inparmath)
- ++inmath;
- }
- } else
- *v++ = *u++;
- continue;
- }
-
- /*
- * Now check if the output is unprintable in the
- * current character set.
- */
- uend = u + MB_METACHARLENCONV(u, &cc);
- if (
-#ifdef MULTIBYTE_SUPPORT
- cc != WEOF &&
-#endif
- WC_ISPRINT(cc)) {
- if (dobackslash)
- *v++ = '\\';
- while (u < uend) {
- if (*u == Meta)
- *v++ = *u++;
- *v++ = *u++;
- }
- } else {
- /* Not printable */
- *v++ = '$';
- *v++ = '\'';
- v = addunprintable(v, u, uend);
- *v++ = '\'';
- u = uend;
- }
- }
- }
- if (quotesub == 2)
- *v++ = '\'';
- *v = '\0';
-
- v = dupstring(buf);
- zfree(buf, alloclen);
- return v;
-}
-
-/*
- * Unmetafy and output a string, quoted if it contains special
- * characters.
- *
- * If stream is NULL, return the same output with any allocation on the
- * heap.
- */
-
-/**/
-mod_export char *
-quotedzputs(char const *s, FILE *stream)
-{
- int inquote = 0, c;
- char *outstr, *ptr;
-
- /* check for empty string */
- if(!*s) {
- if (!stream)
- return dupstring("''");
- fputs("''", stream);
- return NULL;
- }
-
-#ifdef MULTIBYTE_SUPPORT
- if (is_mb_niceformat(s)) {
- if (stream) {
- fputs("$'", stream);
- mb_niceformat(s, stream, NULL, NICEFLAG_QUOTE);
- fputc('\'', stream);
- return NULL;
- } else {
- char *substr;
- mb_niceformat(s, NULL, &substr, NICEFLAG_QUOTE|NICEFLAG_NODUP);
- outstr = (char *)zhalloc(4 + strlen(substr));
- sprintf(outstr, "$'%s'", substr);
- free(substr);
- return outstr;
- }
- }
-#endif /* MULTIBYTE_SUPPORT */
-
- if (!hasspecial(s)) {
- if (stream) {
- zputs(s, stream);
- return NULL;
- } else {
- return dupstring(s);
- }
- }
-
- if (!stream) {
- const char *cptr;
- int l = strlen(s) + 2;
- for (cptr = s; *cptr; cptr++) {
- if (*cptr == Meta)
- cptr++;
- else if (*cptr == '\'')
- l += isset(RCQUOTES) ? 1 : 3;
- }
- ptr = outstr = zhalloc(l + 1);
- } else {
- ptr = outstr = NULL;
- }
- if (isset(RCQUOTES)) {
- /* use rc-style quotes-within-quotes for the whole string */
- if (stream) {
- if (fputc('\'', stream) < 0)
- return NULL;
- } else
- *ptr++ = '\'';
- while(*s) {
- if (*s == Dash)
- c = '-';
- else if (*s == Meta)
- c = *++s ^ 32;
- else
- c = *s;
- s++;
- if (c == '\'') {
- if (stream) {
- if (fputc('\'', stream) < 0)
- return NULL;
- } else
- *ptr++ = '\'';
- } else if (c == '\n' && isset(CSHJUNKIEQUOTES)) {
- if (stream) {
- if (fputc('\\', stream) < 0)
- return NULL;
- } else
- *ptr++ = '\\';
- }
- if (stream) {
- if (fputc(c, stream) < 0)
- return NULL;
- } else {
- if (imeta(c)) {
- *ptr++ = Meta;
- *ptr++ = c ^ 32;
- } else
- *ptr++ = c;
- }
- }
- if (stream) {
- if (fputc('\'', stream) < 0)
- return NULL;
- } else
- *ptr++ = '\'';
- } else {
- /* use Bourne-style quoting, avoiding empty quoted strings */
- while (*s) {
- if (*s == Dash)
- c = '-';
- else if (*s == Meta)
- c = *++s ^ 32;
- else
- c = *s;
- s++;
- if (c == '\'') {
- if (inquote) {
- if (stream) {
- if (putc('\'', stream) < 0)
- return NULL;
- } else
- *ptr++ = '\'';
- inquote=0;
- }
- if (stream) {
- if (fputs("\\'", stream) < 0)
- return NULL;
- } else {
- *ptr++ = '\\';
- *ptr++ = '\'';
- }
- } else {
- if (!inquote) {
- if (stream) {
- if (fputc('\'', stream) < 0)
- return NULL;
- } else
- *ptr++ = '\'';
- inquote=1;
- }
- if (c == '\n' && isset(CSHJUNKIEQUOTES)) {
- if (stream) {
- if (fputc('\\', stream) < 0)
- return NULL;
- } else
- *ptr++ = '\\';
- }
- if (stream) {
- if (fputc(c, stream) < 0)
- return NULL;
- } else {
- if (imeta(c)) {
- *ptr++ = Meta;
- *ptr++ = c ^ 32;
- } else
- *ptr++ = c;
- }
- }
- }
- if (inquote) {
- if (stream) {
- if (fputc('\'', stream) < 0)
- return NULL;
- } else
- *ptr++ = '\'';
- }
- }
- if (!stream)
- *ptr++ = '\0';
-
- return outstr;
-}
-
-/* Double-quote a metafied string. */
-
-/**/
-mod_export char *
-dquotedztrdup(char const *s)
-{
- int len = strlen(s) * 4 + 2;
- char *buf = zalloc(len);
- char *p = buf, *ret;
-
- if(isset(CSHJUNKIEQUOTES)) {
- int inquote = 0;
-
- while(*s) {
- int c = *s++;
-
- if (c == Meta)
- c = *s++ ^ 32;
- switch(c) {
- case '"':
- case '$':
- case '`':
- if(inquote) {
- *p++ = '"';
- inquote = 0;
- }
- *p++ = '\\';
- *p++ = c;
- break;
- default:
- if(!inquote) {
- *p++ = '"';
- inquote = 1;
- }
- if(c == '\n')
- *p++ = '\\';
- *p++ = c;
- break;
- }
- }
- if (inquote)
- *p++ = '"';
- } else {
- int pending = 0;
-
- *p++ = '"';
- while(*s) {
- int c = *s++;
-
- if (c == Meta)
- c = *s++ ^ 32;
- switch(c) {
- case '\\':
- if(pending)
- *p++ = '\\';
- *p++ = '\\';
- pending = 1;
- break;
- case '"':
- case '$':
- case '`':
- if(pending)
- *p++ = '\\';
- *p++ = '\\';
- /* FALL THROUGH */
- default:
- *p++ = c;
- pending = 0;
- break;
- }
- }
- if(pending)
- *p++ = '\\';
- *p++ = '"';
- }
- ret = metafy(buf, p - buf, META_DUP);
- zfree(buf, len);
- return ret;
-}
-
-/* Unmetafy and output a string, double quoting it in its entirety. */
-
-#if 0 /**/
-int
-dquotedzputs(char const *s, FILE *stream)
-{
- char *d = dquotedztrdup(s);
- int ret = zputs(d, stream);
-
- zsfree(d);
- return ret;
-}
-#endif
-
-# if defined(HAVE_NL_LANGINFO) && defined(CODESET) && !defined(__STDC_ISO_10646__)
-/* Convert a character from UCS4 encoding to UTF-8 */
-
-static size_t
-ucs4toutf8(char *dest, unsigned int wval)
-{
- size_t len;
-
- if (wval < 0x80)
- len = 1;
- else if (wval < 0x800)
- len = 2;
- else if (wval < 0x10000)
- len = 3;
- else if (wval < 0x200000)
- len = 4;
- else if (wval < 0x4000000)
- len = 5;
- else
- len = 6;
-
- switch (len) { /* falls through except to the last case */
- case 6: dest[5] = (wval & 0x3f) | 0x80; wval >>= 6;
- case 5: dest[4] = (wval & 0x3f) | 0x80; wval >>= 6;
- case 4: dest[3] = (wval & 0x3f) | 0x80; wval >>= 6;
- case 3: dest[2] = (wval & 0x3f) | 0x80; wval >>= 6;
- case 2: dest[1] = (wval & 0x3f) | 0x80; wval >>= 6;
- *dest = wval | ((0xfc << (6 - len)) & 0xfc);
- break;
- case 1: *dest = wval;
- }
-
- return len;
-}
-#endif
-
-
-/*
- * The following only occurs once or twice in the code, but in different
- * places depending how character set conversion is implemented.
- */
-#define CHARSET_FAILED() \
- if (how & GETKEY_DOLLAR_QUOTE) { \
- while ((*tdest++ = *++s)) { \
- if (how & GETKEY_UPDATE_OFFSET) { \
- if (s - sstart > *misc) \
- (*misc)++; \
- } \
- if (*s == Snull) { \
- *len = (s - sstart) + 1; \
- *tdest = '\0'; \
- return buf; \
- } \
- } \
- *len = tdest - buf; \
- return buf; \
- } \
- *t = '\0'; \
- *len = t - buf; \
- return buf
-
-/*
- * Decode a key string, turning it into the literal characters.
- * The value returned is a newly allocated string from the heap.
- *
- * The length is returned in *len. This is usually the length of
- * the final unmetafied string. The exception is the case of
- * a complete GETKEY_DOLLAR_QUOTE conversion where *len is the
- * length of the input string which has been used (up to and including
- * the terminating single quote); as the final string is metafied and
- * NULL-terminated its length is not required. If both GETKEY_DOLLAR_QUOTE
- * and GETKEY_UPDATE_OFFSET are present in "how", the string is not
- * expected to be terminated (this is used in completion to parse
- * a partial $'...'-quoted string) and the length passed back is
- * that of the converted string. Note in both cases that this is a length
- * in bytes (i.e. the same as given by a raw pointer difference), not
- * characters, which may occupy multiple bytes.
- *
- * how is a set of bits from the GETKEY_ values defined in zsh.h;
- * not all combinations of bits are useful. Callers will typically
- * use one of the GETKEYS_ values which define sets of bits.
- * Note, for example that:
- * - GETKEY_SINGLE_CHAR must not be combined with GETKEY_DOLLAR_QUOTE.
- * - GETKEY_UPDATE_OFFSET is only allowed if GETKEY_DOLLAR_QUOTE is
- * also present.
- *
- * *misc is used for various purposes:
- * - If GETKEY_BACKSLASH_MINUS is set, it indicates the presence
- * of \- in the input.
- * - If GETKEY_BACKSLASH_C is set, it indicates the presence
- * of \c in the input.
- * - If GETKEY_UPDATE_OFFSET is set, it is set on input to some
- * mystical completion offset and is updated to a new offset based
- * on the converted characters. All Hail the Completion System
- * [makes the mystic completion system runic sign in the air].
- *
- * The return value is unmetafied unless GETKEY_DOLLAR_QUOTE is
- * in use.
- */
-
-/**/
-mod_export char *
-getkeystring(char *s, int *len, int how, int *misc)
-{
- char *buf, tmp[1];
- char *t, *tdest = NULL, *u = NULL, *sstart = s, *tbuf = NULL;
- char svchar = '\0';
- int meta = 0, control = 0, ignoring = 0;
- int i;
-#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__)
- wint_t wval;
- int count;
-#else
- unsigned int wval;
-# if defined(HAVE_NL_LANGINFO) && defined(CODESET)
-# if defined(HAVE_ICONV)
- iconv_t cd;
- char inbuf[4];
- size_t inbytes, outbytes;
-# endif
- size_t count;
-# endif
-#endif
-
- DPUTS((how & GETKEY_UPDATE_OFFSET) &&
- (how & ~(GETKEYS_DOLLARS_QUOTE|GETKEY_UPDATE_OFFSET)),
- "BUG: offset updating in getkeystring only supported with $'.");
- DPUTS((how & (GETKEY_DOLLAR_QUOTE|GETKEY_SINGLE_CHAR)) ==
- (GETKEY_DOLLAR_QUOTE|GETKEY_SINGLE_CHAR),
- "BUG: incompatible options in getkeystring");
-
- if (how & GETKEY_SINGLE_CHAR)
- t = buf = tmp;
- else {
- /* Length including terminating NULL */
- int maxlen = 1;
- /*
- * We're not necessarily guaranteed the output string will
- * be no longer than the input with \u and \U when output
- * characters need to be metafied. As this is the only
- * case where the string can get longer (?I think),
- * include it in the allocation length here but don't
- * bother taking account of other factors.
- */
- for (t = s; *t; t++) {
- if (*t == '\\') {
- if (!t[1]) {
- maxlen++;
- break;
- }
- if (t[1] == 'u' || t[1] == 'U')
- maxlen += MB_CUR_MAX * 2;
- else
- maxlen += 2;
- /* skip the backslash and the following character */
- t++;
- } else
- maxlen++;
- }
- if (how & GETKEY_DOLLAR_QUOTE) {
- /*
- * We're going to unmetafy into a new string, but
- * to get a proper metafied input we're going to metafy
- * into an intermediate buffer. This is necessary if we have
- * \u and \U's with multiple metafied bytes. We can't
- * simply remetafy the entire string because there may
- * be tokens (indeed, we know there are lexical nulls floating
- * around), so we have to be aware character by character
- * what we are converting.
- *
- * In this case, buf is the final buffer (as usual),
- * but t points into a temporary buffer that just has
- * to be long enough to hold the result of one escape
- * code transformation. We count this is a full multibyte
- * character (MB_CUR_MAX) with every character metafied
- * (*2) plus a little bit of fuzz (for e.g. the odd backslash).
- */
- buf = tdest = zhalloc(maxlen);
- t = tbuf = zhalloc(MB_CUR_MAX * 3 + 1);
- } else {
- t = buf = zhalloc(maxlen);
- }
- }
- for (; *s; s++) {
- if (*s == '\\' && s[1]) {
- int miscadded;
- if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) {
- (*misc)--;
- miscadded = 1;
- } else
- miscadded = 0;
- switch (*++s) {
- case 'a':
-#ifdef __STDC__
- *t++ = '\a';
-#else
- *t++ = '\07';
-#endif
- break;
- case 'n':
- *t++ = '\n';
- break;
- case 'b':
- *t++ = '\b';
- break;
- case 't':
- *t++ = '\t';
- break;
- case 'v':
- *t++ = '\v';
- break;
- case 'f':
- *t++ = '\f';
- break;
- case 'r':
- *t++ = '\r';
- break;
- case 'E':
- if (!(how & GETKEY_EMACS)) {
- *t++ = '\\', s--;
- if (miscadded)
- (*misc)++;
- continue;
- }
- /* FALL THROUGH */
- case 'e':
- *t++ = '\033';
- break;
- case 'M':
- /* HERE: GETKEY_UPDATE_OFFSET */
- if (how & GETKEY_EMACS) {
- if (s[1] == '-')
- s++;
- meta = 1 + control; /* preserve the order of ^ and meta */
- } else {
- if (miscadded)
- (*misc)++;
- *t++ = '\\', s--;
- }
- continue;
- case 'C':
- /* HERE: GETKEY_UPDATE_OFFSET */
- if (how & GETKEY_EMACS) {
- if (s[1] == '-')
- s++;
- control = 1;
- } else {
- if (miscadded)
- (*misc)++;
- *t++ = '\\', s--;
- }
- continue;
- case Meta:
- if (miscadded)
- (*misc)++;
- *t++ = '\\', s--;
- break;
- case '-':
- if (how & GETKEY_BACKSLASH_MINUS) {
- *misc = 1;
- break;
- }
- goto def;
- case 'c':
- if (how & GETKEY_BACKSLASH_C) {
- *misc = 1;
- *t = '\0';
- *len = t - buf;
- return buf;
- }
- goto def;
- case 'U':
- if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc)
- (*misc) -= 4;
- /* FALLTHROUGH */
- case 'u':
- if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) {
- (*misc) -= 6; /* HERE don't really believe this */
- /*
- * We've now adjusted the offset for all the input
- * characters, so we need to add for each
- * byte of output below.
- */
- }
- wval = 0;
- for (i=(*s == 'u' ? 4 : 8); i>0; i--) {
- if (*++s && idigit(*s))
- wval = wval * 16 + (*s - '0');
- else if (*s && ((*s >= 'a' && *s <= 'f') ||
- (*s >= 'A' && *s <= 'F')))
- wval = wval * 16 + (*s & 0x1f) + 9;
- else {
- s--;
- break;
- }
- }
- if (how & GETKEY_SINGLE_CHAR) {
- *misc = wval;
- return s+1;
- }
-#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__)
- count = wctomb(t, (wchar_t)wval);
- if (count == -1) {
- zerr("character not in range");
- CHARSET_FAILED();
- }
- if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc)
- (*misc) += count;
- t += count;
-# else
-# if defined(HAVE_NL_LANGINFO) && defined(CODESET)
- if (!strcmp(nl_langinfo(CODESET), "UTF-8")) {
- count = ucs4toutf8(t, wval);
- t += count;
- if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc)
- (*misc) += count;
- } else {
-# ifdef HAVE_ICONV
- ICONV_CONST char *inptr = inbuf;
- const char *codesetstr = nl_langinfo(CODESET);
- inbytes = 4;
- outbytes = 6;
- /* store value in big endian form */
- for (i=3;i>=0;i--) {
- inbuf[i] = wval & 0xff;
- wval >>= 8;
- }
-
- /*
- * If the code set isn't handled, we'd better
- * assume it's US-ASCII rather than just failing
- * hopelessly. Solaris has a weird habit of
- * returning 646. This is handled by the
- * native iconv(), but not by GNU iconv; what's
- * more, some versions of the native iconv don't
- * handle standard names like ASCII.
- *
- * This should only be a problem if there's a
- * mismatch between the NLS and the iconv in use,
- * which probably only means if libiconv is in use.
- * We checked at configure time if our libraries
- * pulled in _libiconv_version, which should be
- * a good test.
- *
- * It shouldn't ever be NULL, but while we're
- * being paranoid...
- */
-#ifdef ICONV_FROM_LIBICONV
- if (!codesetstr || !*codesetstr)
- codesetstr = "US-ASCII";
-#endif
- cd = iconv_open(codesetstr, "UCS-4BE");
-#ifdef ICONV_FROM_LIBICONV
- if (cd == (iconv_t)-1 && !strcmp(codesetstr, "646")) {
- codesetstr = "US-ASCII";
- cd = iconv_open(codesetstr, "UCS-4BE");
- }
-#endif
- if (cd == (iconv_t)-1) {
- zerr("cannot do charset conversion (iconv failed)");
- CHARSET_FAILED();
- }
- count = iconv(cd, &inptr, &inbytes, &t, &outbytes);
- iconv_close(cd);
- if (count == (size_t)-1) {
- zerr("character not in range");
- CHARSET_FAILED();
- }
- if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc)
- (*misc) += count;
-# else
- zerr("cannot do charset conversion (iconv not available)");
- CHARSET_FAILED();
-# endif
- }
-# else
- zerr("cannot do charset conversion (NLS not supported)");
- CHARSET_FAILED();
-# endif
-# endif
- if (how & GETKEY_DOLLAR_QUOTE) {
- char *t2;
- for (t2 = tbuf; t2 < t; t2++) {
- if (imeta(*t2)) {
- *tdest++ = Meta;
- *tdest++ = *t2 ^ 32;
- } else
- *tdest++ = *t2;
- }
- /* reset temporary buffer after handling */
- t = tbuf;
- }
- continue;
- case '\'':
- case '\\':
- if (how & GETKEY_DOLLAR_QUOTE) {
- /*
- * Usually \' and \\ will have the initial
- * \ turned into a Bnull, however that's not
- * necessarily the case when called from
- * completion.
- */
- *t++ = *s;
- break;
- }
- /* FALLTHROUGH */
- default:
- def:
- /* HERE: GETKEY_UPDATE_OFFSET? */
- if ((idigit(*s) && *s < '8') || *s == 'x') {
- if (!(how & GETKEY_OCTAL_ESC)) {
- if (*s == '0')
- s++;
- else if (*s != 'x') {
- *t++ = '\\', s--;
- continue;
- }
- }
- if (s[1] && s[2] && s[3]) {
- svchar = s[3];
- s[3] = '\0';
- u = s;
- }
- *t++ = zstrtol(s + (*s == 'x'), &s,
- (*s == 'x') ? 16 : 8);
- if ((how & GETKEY_PRINTF_PERCENT) && t[-1] == '%')
- *t++ = '%';
- if (svchar) {
- u[3] = svchar;
- svchar = '\0';
- }
- s--;
- } else {
- if (!(how & GETKEY_EMACS) && *s != '\\') {
- if (miscadded)
- (*misc)++;
- *t++ = '\\';
- }
- *t++ = *s;
- }
- break;
- }
- } else if ((how & GETKEY_DOLLAR_QUOTE) && *s == Snull) {
- /* return length to following character */
- *len = (s - sstart) + 1;
- *tdest = '\0';
- return buf;
- } else if (*s == '^' && !control && (how & GETKEY_CTRL) && s[1]) {
- control = 1;
- continue;
-#ifdef MULTIBYTE_SUPPORT
- } else if ((how & GETKEY_SINGLE_CHAR) &&
- isset(MULTIBYTE) && STOUC(*s) > 127) {
- wint_t wc;
- int len;
- len = mb_metacharlenconv(s, &wc);
- if (wc != WEOF) {
- *misc = (int)wc;
- return s + len;
- }
-#endif
-
- } else if (*s == Meta)
- *t++ = *++s ^ 32;
- else {
- if (itok(*s)) {
- /*
- * We need to be quite careful here. We haven't
- * necessarily got an input stream with all tokens
- * removed, so the majority of tokens need passing
- * through untouched and without Meta handling.
- * However, me may need to handle tokenized
- * backslashes.
- */
- if (meta || control) {
- /*
- * Presumably we should be using meta or control
- * on the character representing the token.
- *
- * Special case: $'\M-\\' where the token is a Bnull.
- * This time we dump the Bnull since we're
- * replacing the whole thing. The lexer
- * doesn't know about the meta or control modifiers.
- */
- if ((how & GETKEY_DOLLAR_QUOTE) && *s == Bnull)
- *t++ = *++s;
- else
- *t++ = ztokens[*s - Pound];
- } else if (how & GETKEY_DOLLAR_QUOTE) {
- /*
- * We don't want to metafy this, it's a real
- * token.
- */
- *tdest++ = *s;
- if (*s == Bnull) {
- /*
- * Bnull is a backslash which quotes a couple
- * of special characters that always appear
- * literally next. See strquote handling
- * in gettokstr() in lex.c. We need
- * to retain the Bnull (as above) so that quote
- * handling in completion can tell where the
- * backslash was.
- */
- *tdest++ = *++s;
- }
- /* reset temporary buffer, now handled */
- t = tbuf;
- continue;
- } else
- *t++ = *s;
- } else
- *t++ = *s;
- }
- if (meta == 2) {
- t[-1] |= 0x80;
- meta = 0;
- }
- if (control) {
- if (t[-1] == '?')
- t[-1] = 0x7f;
- else
- t[-1] &= 0x9f;
- control = 0;
- }
- if (meta) {
- t[-1] |= 0x80;
- meta = 0;
- }
- if (how & GETKEY_DOLLAR_QUOTE) {
- char *t2;
- for (t2 = tbuf; t2 < t; t2++) {
- /*
- * In POSIX mode, an embedded NULL is discarded and
- * terminates processing. It just does, that's why.
- */
- if (isset(POSIXSTRINGS)) {
- if (*t2 == '\0')
- ignoring = 1;
- if (ignoring)
- break;
- }
- if (imeta(*t2)) {
- *tdest++ = Meta;
- *tdest++ = *t2 ^ 32;
- } else {
- *tdest++ = *t2;
- }
- }
- /*
- * Reset use of temporary buffer.
- */
- t = tbuf;
- }
- if ((how & GETKEY_SINGLE_CHAR) && t != tmp) {
- *misc = STOUC(tmp[0]);
- return s + 1;
- }
- }
- /*
- * When called from completion, where we use GETKEY_UPDATE_OFFSET to
- * update the index into the metafied editor line, we don't necessarily
- * have the end of a $'...' quotation, else we should do.
- */
- DPUTS((how & (GETKEY_DOLLAR_QUOTE|GETKEY_UPDATE_OFFSET)) ==
- GETKEY_DOLLAR_QUOTE, "BUG: unterminated $' substitution");
- *t = '\0';
- if (how & GETKEY_DOLLAR_QUOTE)
- *tdest = '\0';
- if (how & GETKEY_SINGLE_CHAR)
- *misc = 0;
- else
- *len = ((how & GETKEY_DOLLAR_QUOTE) ? tdest : t) - buf;
- return buf;
-}
-
-/* Return non-zero if s is a prefix of t. */
-
-/**/
-mod_export int
-strpfx(const char *s, const char *t)
-{
- while (*s && *s == *t)
- s++, t++;
- return !*s;
-}
-
-/* Return non-zero if s is a suffix of t. */
-
-/**/
-mod_export int
-strsfx(char *s, char *t)
-{
- int ls = strlen(s), lt = strlen(t);
-
- if (ls <= lt)
- return !strcmp(t + lt - ls, s);
- return 0;
-}
-
-/**/
-static int
-upchdir(int n)
-{
- char buf[PATH_MAX+1];
- char *s;
- int err = -1;
-
- while (n > 0) {
- for (s = buf; s < buf + PATH_MAX - 4 && n--; )
- *s++ = '.', *s++ = '.', *s++ = '/';
- s[-1] = '\0';
- if (chdir(buf))
- return err;
- err = -2;
- }
- return 0;
-}
-
-/*
- * Initialize a "struct dirsav".
- * The structure will be set to the directory we want to save
- * the first time we change to a different directory.
- */
-
-/**/
-mod_export void
-init_dirsav(Dirsav d)
-{
- d->ino = d->dev = 0;
- d->dirname = NULL;
- d->dirfd = d->level = -1;
-}
-
-/*
- * Change directory, without following symlinks. Returns 0 on success, -1
- * on failure. Sets errno to ENOTDIR if any symlinks are encountered. If
- * fchdir() fails, or the current directory is unreadable, we might end up
- * in an unwanted directory in case of failure.
- *
- * path is an unmetafied but null-terminated string, as needed by system
- * calls.
- */
-
-/**/
-mod_export int
-lchdir(char const *path, struct dirsav *d, int hard)
-{
- char const *pptr;
- int level;
- struct stat st1;
- struct dirsav ds;
-#ifdef HAVE_LSTAT
- char buf[PATH_MAX + 1], *ptr;
- int err;
- struct stat st2;
-#endif
-#ifdef HAVE_FCHDIR
- int close_dir = 0;
-#endif
-
- if (!d) {
- init_dirsav(&ds);
- d = &ds;
- }
-#ifdef HAVE_LSTAT
- if ((*path == '/' || !hard) &&
- (d != &ds || hard)){
-#else
- if (*path == '/') {
-#endif
- level = -1;
-#ifndef HAVE_FCHDIR
- if (!d->dirname)
- zgetdir(d);
-#endif
- } else {
- level = 0;
- if (!d->dev && !d->ino) {
- stat(".", &st1);
- d->dev = st1.st_dev;
- d->ino = st1.st_ino;
- }
- }
-
-#ifdef HAVE_LSTAT
- if (!hard)
-#endif
- {
- if (d != &ds) {
- for (pptr = path; *pptr; level++) {
- while (*pptr && *pptr++ != '/');
- while (*pptr == '/')
- pptr++;
- }
- d->level = level;
- }
- return zchdir((char *) path);
- }
-
-#ifdef HAVE_LSTAT
-#ifdef HAVE_FCHDIR
- if (d->dirfd < 0) {
- close_dir = 1;
- if ((d->dirfd = open(".", O_RDONLY | O_NOCTTY)) < 0 &&
- zgetdir(d) && *d->dirname != '/')
- d->dirfd = open("..", O_RDONLY | O_NOCTTY);
- }
-#endif
- if (*path == '/')
- if (chdir("/") < 0)
- zwarn("failed to chdir(/): %e", errno);
- for(;;) {
- while(*path == '/')
- path++;
- if(!*path) {
- if (d == &ds)
- zsfree(ds.dirname);
- else
- d->level = level;
-#ifdef HAVE_FCHDIR
- if (d->dirfd >=0 && close_dir) {
- close(d->dirfd);
- d->dirfd = -1;
- }
-#endif
- return 0;
- }
- for(pptr = path; *++pptr && *pptr != '/'; ) ;
- if(pptr - path > PATH_MAX) {
- err = ENAMETOOLONG;
- break;
- }
- for(ptr = buf; path != pptr; )
- *ptr++ = *path++;
- *ptr = 0;
- if(lstat(buf, &st1)) {
- err = errno;
- break;
- }
- if(!S_ISDIR(st1.st_mode)) {
- err = ENOTDIR;
- break;
- }
- if(chdir(buf)) {
- err = errno;
- break;
- }
- if (level >= 0)
- level++;
- if(lstat(".", &st2)) {
- err = errno;
- break;
- }
- if(st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) {
- err = ENOTDIR;
- break;
- }
- }
- if (restoredir(d)) {
- int restoreerr = errno;
- int i;
- /*
- * Failed to restore the directory.
- * Just be definite, cd to root and report the result.
- */
- for (i = 0; i < 2; i++) {
- const char *cdest;
- if (i)
- cdest = "/";
- else {
- if (!home)
- continue;
- cdest = home;
- }
- zsfree(pwd);
- pwd = ztrdup(cdest);
- if (chdir(pwd) == 0)
- break;
- }
- if (i == 2)
- zerr("lost current directory, failed to cd to /: %e", errno);
- else
- zerr("lost current directory: %e: changed to `%s'", restoreerr,
- pwd);
- if (d == &ds)
- zsfree(ds.dirname);
-#ifdef HAVE_FCHDIR
- if (d->dirfd >=0 && close_dir) {
- close(d->dirfd);
- d->dirfd = -1;
- }
-#endif
- errno = err;
- return -2;
- }
- if (d == &ds)
- zsfree(ds.dirname);
-#ifdef HAVE_FCHDIR
- if (d->dirfd >=0 && close_dir) {
- close(d->dirfd);
- d->dirfd = -1;
- }
-#endif
- errno = err;
- return -1;
-#endif /* HAVE_LSTAT */
-}
-
-/**/
-mod_export int
-restoredir(struct dirsav *d)
-{
- int err = 0;
- struct stat sbuf;
-
- if (d->dirname && *d->dirname == '/')
- return chdir(d->dirname);
-#ifdef HAVE_FCHDIR
- if (d->dirfd >= 0) {
- if (!fchdir(d->dirfd)) {
- if (!d->dirname) {
- return 0;
- } else if (chdir(d->dirname)) {
- close(d->dirfd);
- d->dirfd = -1;
- err = -2;
- }
- } else {
- close(d->dirfd);
- d->dirfd = err = -1;
- }
- } else
-#endif
- if (d->level > 0)
- err = upchdir(d->level);
- else if (d->level < 0)
- err = -1;
- if (d->dev || d->ino) {
- stat(".", &sbuf);
- if (sbuf.st_ino != d->ino || sbuf.st_dev != d->dev)
- err = -2;
- }
- return err;
-}
-
-
-/* Check whether the shell is running with privileges in effect. *
- * This is the case if EITHER the euid is zero, OR (if the system *
- * supports POSIX.1e (POSIX.6) capability sets) the process' *
- * Effective or Inheritable capability sets are non-empty. */
-
-/**/
-int
-privasserted(void)
-{
- if(!geteuid())
- return 1;
-#ifdef HAVE_CAP_GET_PROC
- {
- cap_t caps = cap_get_proc();
- if(caps) {
- /* POSIX doesn't define a way to test whether a capability set *
- * is empty or not. Typical. I hope this is conforming... */
- cap_flag_value_t val;
- cap_value_t n;
- for(n = 0; !cap_get_flag(caps, n, CAP_EFFECTIVE, &val); n++)
- if(val) {
- cap_free(caps);
- return 1;
- }
- }
- cap_free(caps);
- }
-#endif /* HAVE_CAP_GET_PROC */
- return 0;
-}
-
-/**/
-mod_export int
-mode_to_octal(mode_t mode)
-{
- int m = 0;
-
- if(mode & S_ISUID)
- m |= 04000;
- if(mode & S_ISGID)
- m |= 02000;
- if(mode & S_ISVTX)
- m |= 01000;
- if(mode & S_IRUSR)
- m |= 00400;
- if(mode & S_IWUSR)
- m |= 00200;
- if(mode & S_IXUSR)
- m |= 00100;
- if(mode & S_IRGRP)
- m |= 00040;
- if(mode & S_IWGRP)
- m |= 00020;
- if(mode & S_IXGRP)
- m |= 00010;
- if(mode & S_IROTH)
- m |= 00004;
- if(mode & S_IWOTH)
- m |= 00002;
- if(mode & S_IXOTH)
- m |= 00001;
- return m;
-}
-
-#ifdef MAILDIR_SUPPORT
-/*
- * Stat a file. If it's a maildir, check all messages
- * in the maildir and present the grand total as a file.
- * The fields in the 'struct stat' are from the mail directory.
- * The following fields are emulated:
- *
- * st_nlink always 1
- * st_size total number of bytes in all files
- * st_blocks total number of messages
- * st_atime access time of newest file in maildir
- * st_mtime modify time of newest file in maildir
- * st_mode S_IFDIR changed to S_IFREG
- *
- * This is good enough for most mail-checking applications.
- */
-
-/**/
-int
-mailstat(char *path, struct stat *st)
-{
- DIR *dd;
- struct dirent *fn;
- struct stat st_ret, st_tmp;
- static struct stat st_ret_last;
- char *dir, *file = 0;
- int i;
- time_t atime = 0, mtime = 0;
- size_t plen = strlen(path), dlen;
-
- /* First see if it's a directory. */
- if ((i = stat(path, st)) != 0 || !S_ISDIR(st->st_mode))
- return i;
-
- st_ret = *st;
- st_ret.st_nlink = 1;
- st_ret.st_size = 0;
- st_ret.st_blocks = 0;
- st_ret.st_mode &= ~S_IFDIR;
- st_ret.st_mode |= S_IFREG;
-
- /* See if cur/ is present */
- dir = appstr(ztrdup(path), "/cur");
- if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0;
- st_ret.st_atime = st_tmp.st_atime;
-
- /* See if tmp/ is present */
- dir[plen] = 0;
- dir = appstr(dir, "/tmp");
- if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0;
- st_ret.st_mtime = st_tmp.st_mtime;
-
- /* And new/ */
- dir[plen] = 0;
- dir = appstr(dir, "/new");
- if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0;
- st_ret.st_mtime = st_tmp.st_mtime;
-
-#if THERE_IS_EXACTLY_ONE_MAILDIR_IN_MAILPATH
- {
- static struct stat st_new_last;
- /* Optimization - if new/ didn't change, nothing else did. */
- if (st_tmp.st_dev == st_new_last.st_dev &&
- st_tmp.st_ino == st_new_last.st_ino &&
- st_tmp.st_atime == st_new_last.st_atime &&
- st_tmp.st_mtime == st_new_last.st_mtime) {
- *st = st_ret_last;
- return 0;
- }
- st_new_last = st_tmp;
- }
-#endif
-
- /* Loop over new/ and cur/ */
- for (i = 0; i < 2; i++) {
- dir[plen] = 0;
- dir = appstr(dir, i ? "/cur" : "/new");
- if ((dd = opendir(dir)) == NULL) {
- zsfree(file);
- zsfree(dir);
- return 0;
- }
- dlen = strlen(dir) + 1; /* include the "/" */
- while ((fn = readdir(dd)) != NULL) {
- if (fn->d_name[0] == '.')
- continue;
- if (file) {
- file[dlen] = 0;
- file = appstr(file, fn->d_name);
- } else {
- file = tricat(dir, "/", fn->d_name);
- }
- if (stat(file, &st_tmp) != 0)
- continue;
- st_ret.st_size += st_tmp.st_size;
- st_ret.st_blocks++;
- if (st_tmp.st_atime != st_tmp.st_mtime &&
- st_tmp.st_atime > atime)
- atime = st_tmp.st_atime;
- if (st_tmp.st_mtime > mtime)
- mtime = st_tmp.st_mtime;
- }
- closedir(dd);
- }
- zsfree(file);
- zsfree(dir);
-
- if (atime) st_ret.st_atime = atime;
- if (mtime) st_ret.st_mtime = mtime;
-
- *st = st_ret_last = st_ret;
- return 0;
-}
-#endif