1/*
2 * utils.c - miscellaneous utilities
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1992-1997 Paul Falstad
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Paul Falstad or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Paul Falstad and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Paul Falstad and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose.  The software
24 * provided hereunder is on an "as is" basis, and Paul Falstad and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30#include "zsh.mdh"
31#include "utils.pro"
32
33/* name of script being sourced */
34
35/**/
36mod_export char *scriptname;     /* is sometimes a function name */
37
38/* filename of script or other file containing code source e.g. autoload */
39
40/**/
41mod_export char *scriptfilename;
42
43/* != 0 if we are in a new style completion function */
44
45/**/
46mod_export int incompfunc;
47
48#ifdef MULTIBYTE_SUPPORT
49struct widechar_array {
50    wchar_t *chars;
51    size_t len;
52};
53typedef struct widechar_array *Widechar_array;
54
55/*
56 * The wordchars variable turned into a wide character array.
57 * This is much more convenient for testing.
58 */
59struct widechar_array wordchars_wide;
60
61/*
62 * The same for the separators (IFS) array.
63 */
64struct widechar_array ifs_wide;
65
66/* Function to set one of the above from the multibyte array */
67
68static void
69set_widearray(char *mb_array, Widechar_array wca)
70{
71    if (wca->chars) {
72	free(wca->chars);
73	wca->chars = NULL;
74    }
75    wca->len = 0;
76
77    if (!isset(MULTIBYTE))
78	return;
79
80    if (mb_array) {
81	VARARR(wchar_t, tmpwcs, strlen(mb_array));
82	wchar_t *wcptr = tmpwcs;
83	wint_t wci;
84
85	mb_metacharinit();
86	while (*mb_array) {
87	    int mblen = mb_metacharlenconv(mb_array, &wci);
88
89	    if (!mblen)
90		break;
91	    /* No good unless all characters are convertible */
92	    if (wci == WEOF)
93		return;
94	    *wcptr++ = (wchar_t)wci;
95#ifdef DEBUG
96	    /*
97	     * This generates a warning from the compiler (and is
98	     * indeed useless) if chars are unsigned.  It's
99	     * extreme paranoia anyway.
100	     */
101	    if (wcptr[-1] < 0)
102		fprintf(stderr, "BUG: Bad cast to wchar_t\n");
103#endif
104	    mb_array += mblen;
105	}
106
107	wca->len = wcptr - tmpwcs;
108	wca->chars = (wchar_t *)zalloc(wca->len * sizeof(wchar_t));
109	wmemcpy(wca->chars, tmpwcs, wca->len);
110    }
111}
112#endif
113
114
115/* Print an error */
116
117static void
118zwarning(const char *cmd, const char *fmt, va_list ap)
119{
120    if (isatty(2))
121	zleentry(ZLE_CMD_TRASH);
122
123    if (cmd) {
124	if (unset(SHINSTDIN) || locallevel) {
125	    nicezputs(scriptname ? scriptname : argzero, stderr);
126	    fputc((unsigned char)':', stderr);
127	}
128	nicezputs(cmd, stderr);
129	fputc((unsigned char)':', stderr);
130    } else {
131	/*
132	 * scriptname is set when sourcing scripts, so that we get the
133	 * correct name instead of the generic name of whatever
134	 * program/script is running.  It's also set in shell functions,
135	 * so test locallevel, too.
136	 */
137	nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" :
138		  scriptname ? scriptname : argzero, stderr);
139	fputc((unsigned char)':', stderr);
140    }
141
142    zerrmsg(stderr, fmt, ap);
143}
144
145
146/**/
147mod_export void
148zerr(VA_ALIST1(const char *fmt))
149VA_DCL
150{
151    va_list ap;
152    VA_DEF_ARG(const char *fmt);
153
154    if (errflag || noerrs) {
155	if (noerrs < 2)
156	    errflag = 1;
157	return;
158    }
159
160    VA_START(ap, fmt);
161    VA_GET_ARG(ap, fmt, const char *);
162    zwarning(NULL, fmt, ap);
163    va_end(ap);
164    errflag = 1;
165}
166
167/**/
168mod_export void
169zerrnam(VA_ALIST2(const char *cmd, const char *fmt))
170VA_DCL
171{
172    va_list ap;
173    VA_DEF_ARG(const char *cmd);
174    VA_DEF_ARG(const char *fmt);
175
176    if (errflag || noerrs)
177	return;
178
179    VA_START(ap, fmt);
180    VA_GET_ARG(ap, cmd, const char *);
181    VA_GET_ARG(ap, fmt, const char *);
182    zwarning(cmd, fmt, ap);
183    va_end(ap);
184    errflag = 1;
185}
186
187/**/
188mod_export void
189zwarn(VA_ALIST1(const char *fmt))
190VA_DCL
191{
192    va_list ap;
193    VA_DEF_ARG(const char *fmt);
194
195    if (errflag || noerrs)
196	return;
197
198    VA_START(ap, fmt);
199    VA_GET_ARG(ap, fmt, const char *);
200    zwarning(NULL, fmt, ap);
201    va_end(ap);
202}
203
204/**/
205mod_export void
206zwarnnam(VA_ALIST2(const char *cmd, const char *fmt))
207VA_DCL
208{
209    va_list ap;
210    VA_DEF_ARG(const char *cmd);
211    VA_DEF_ARG(const char *fmt);
212
213    if (errflag || noerrs)
214	return;
215
216    VA_START(ap, fmt);
217    VA_GET_ARG(ap, cmd, const char *);
218    VA_GET_ARG(ap, fmt, const char *);
219    zwarning(cmd, fmt, ap);
220    va_end(ap);
221}
222
223
224#ifdef DEBUG
225
226/**/
227mod_export void
228dputs(VA_ALIST1(const char *message))
229VA_DCL
230{
231    char *filename;
232    FILE *file;
233    va_list ap;
234    VA_DEF_ARG(const char *message);
235
236    VA_START(ap, message);
237    VA_GET_ARG(ap, message, const char *);
238    if ((filename = getsparam("ZSH_DEBUG_LOG")) != NULL &&
239	(file = fopen(filename, "a")) != NULL) {
240	zerrmsg(file, message, ap);
241	fclose(file);
242    } else
243	zerrmsg(stderr, message, ap);
244    va_end(ap);
245}
246
247#endif /* DEBUG */
248
249#ifdef __CYGWIN__
250/*
251 * This works around an occasional problem with dllwrap on Cygwin, seen
252 * on at least two installations.  It fails to find the last symbol
253 * exported in alphabetical order (in our case zwarnnam).  Until this is
254 * properly categorised and fixed we add a dummy symbol at the end.
255 */
256mod_export void
257zz_plural_z_alpha(void)
258{
259}
260#endif
261
262/**/
263void
264zerrmsg(FILE *file, const char *fmt, va_list ap)
265{
266    const char *str;
267    int num;
268#ifdef DEBUG
269    long lnum;
270#endif
271#ifdef HAVE_STRERROR_R
272#define ERRBUFSIZE (80)
273    int olderrno;
274    char errbuf[ERRBUFSIZE];
275#endif
276    char *errmsg;
277
278    if ((unset(SHINSTDIN) || locallevel) && lineno) {
279#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
280	fprintf(file, "%lld: ", lineno);
281#else
282	fprintf(file, "%ld: ", (long)lineno);
283#endif
284    } else
285	fputc((unsigned char)' ', file);
286
287    while (*fmt)
288	if (*fmt == '%') {
289	    fmt++;
290	    switch (*fmt++) {
291	    case 's':
292		str = va_arg(ap, const char *);
293		nicezputs(str, file);
294		break;
295	    case 'l': {
296		char *s;
297		str = va_arg(ap, const char *);
298		num = va_arg(ap, int);
299		num = metalen(str, num);
300		s = zhalloc(num + 1);
301		memcpy(s, str, num);
302		s[num] = '\0';
303		nicezputs(s, file);
304		break;
305	    }
306#ifdef DEBUG
307	    case 'L':
308		lnum = va_arg(ap, long);
309		fprintf(file, "%ld", lnum);
310		break;
311#endif
312	    case 'd':
313		num = va_arg(ap, int);
314		fprintf(file, "%d", num);
315		break;
316	    case '%':
317		putc('%', file);
318		break;
319	    case 'c':
320		num = va_arg(ap, int);
321#ifdef MULTIBYTE_SUPPORT
322		mb_metacharinit();
323		zputs(wcs_nicechar(num, NULL, NULL), file);
324#else
325		zputs(nicechar(num), file);
326#endif
327		break;
328	    case 'e':
329		/* print the corresponding message for this errno */
330		num = va_arg(ap, int);
331		if (num == EINTR) {
332		    fputs("interrupt\n", file);
333		    errflag = 1;
334		    return;
335		}
336		errmsg = strerror(num);
337		/* If the message is not about I/O problems, it looks better *
338		 * if we uncapitalize the first letter of the message        */
339		if (num == EIO)
340		    fputs(errmsg, file);
341		else {
342		    fputc(tulower(errmsg[0]), file);
343		    fputs(errmsg + 1, file);
344		}
345		break;
346	    }
347	} else {
348	    putc(*fmt == Meta ? *++fmt ^ 32 : *fmt, file);
349	    fmt++;
350	}
351    putc('\n', file);
352    fflush(file);
353}
354
355/* Output a single character, for the termcap routines.     *
356 * This is used instead of putchar since it can be a macro. */
357
358/**/
359mod_export int
360putraw(int c)
361{
362    putc(c, stdout);
363    return 0;
364}
365
366/* Output a single character, for the termcap routines. */
367
368/**/
369mod_export int
370putshout(int c)
371{
372    putc(c, shout);
373    return 0;
374}
375
376/*
377 * Turn a character into a visible representation thereof.  The visible
378 * string is put together in a static buffer, and this function returns
379 * a pointer to it.  Printable characters stand for themselves, DEL is
380 * represented as "^?", newline and tab are represented as "\n" and
381 * "\t", and normal control characters are represented in "^C" form.
382 * Characters with bit 7 set, if unprintable, are represented as "\M-"
383 * followed by the visible representation of the character with bit 7
384 * stripped off.  Tokens are interpreted, rather than being treated as
385 * literal characters.
386 *
387 * Note that the returned string is metafied, so that it must be
388 * treated like any other zsh internal string (and not, for example,
389 * output directly).
390 *
391 * This function is used even if MULTIBYTE_SUPPORT is defined: we
392 * use it as a fallback in case we couldn't identify a wide character
393 * in a multibyte string.
394 */
395
396/**/
397mod_export char *
398nicechar(int c)
399{
400    static char buf[6];
401    char *s = buf;
402    c &= 0xff;
403    if (isprint(c))
404	goto done;
405    if (c & 0x80) {
406	if (isset(PRINTEIGHTBIT))
407	    goto done;
408	*s++ = '\\';
409	*s++ = 'M';
410	*s++ = '-';
411	c &= 0x7f;
412	if(isprint(c))
413	    goto done;
414    }
415    if (c == 0x7f) {
416	*s++ = '^';
417	c = '?';
418    } else if (c == '\n') {
419	*s++ = '\\';
420	c = 'n';
421    } else if (c == '\t') {
422	*s++ = '\\';
423	c = 't';
424    } else if (c < 0x20) {
425	*s++ = '^';
426	c += 0x40;
427    }
428    done:
429    /*
430     * The resulting string is still metafied, so check if
431     * we are returning a character in the range that needs metafication.
432     * This can't happen if the character is printed "nicely", so
433     * this results in a maximum of two bytes total (plus the null).
434     */
435    if (imeta(c)) {
436	*s++ = Meta;
437	*s++ = c ^ 32;
438    } else
439	*s++ = c;
440    *s = 0;
441    return buf;
442}
443
444/**/
445#ifdef MULTIBYTE_SUPPORT
446static mbstate_t mb_shiftstate;
447
448/*
449 * Initialise multibyte state: called before a sequence of
450 * wcs_nicechar() or mb_metacharlenconv().
451 */
452
453/**/
454mod_export void
455mb_metacharinit(void)
456{
457    memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
458}
459
460/*
461 * The number of bytes we need to allocate for a "nice" representation
462 * of a multibyte character.
463 *
464 * We double MB_CUR_MAX to take account of the fact that
465 * we may need to metafy.  In fact the representation probably
466 * doesn't allow every character to be in the meta range, but
467 * we don't need to be too pedantic.
468 *
469 * The 12 is for the output of a UCS-4 code; we don't actually
470 * need this at the same time as MB_CUR_MAX, but again it's
471 * not worth calculating more exactly.
472 */
473#define NICECHAR_MAX (12 + 2*MB_CUR_MAX)
474/*
475 * Input a wide character.  Output a printable representation,
476 * which is a metafied multibyte string.   With widthp return
477 * the printing width.
478 *
479 * swide, if non-NULL, is used to help the completion code, which needs
480 * to know the printing width of the each part of the representation.
481 * *swide is set to the part of the returned string where the wide
482 * character starts.  Any string up to that point is ASCII characters,
483 * so the width of it is (*swide - <return_value>).  Anything left is
484 * a single wide character corresponding to the remaining width.
485 * Either the initial ASCII part or the wide character part may be empty
486 * (but not both).  (Note the complication that the wide character
487 * part may contain metafied characters.)
488 *
489 * The caller needs to call mb_metacharinit() before the first call, to
490 * set up the multibyte shift state for a range of characters.
491 */
492
493/**/
494mod_export char *
495wcs_nicechar(wchar_t c, size_t *widthp, char **swidep)
496{
497    static char *buf;
498    static int bufalloc = 0, newalloc;
499    char *s, *mbptr;
500    int ret = 0;
501    VARARR(char, mbstr, MB_CUR_MAX);
502
503    /*
504     * We want buf to persist beyond the return.  MB_CUR_MAX and hence
505     * NICECHAR_MAX may not be constant, so we have to allocate this at
506     * run time.  (We could probably get away with just allocating a
507     * large buffer, in practice.)  For efficiency, only reallocate if
508     * we really need to, since this function will be called frequently.
509     */
510    newalloc = NICECHAR_MAX;
511    if (bufalloc != newalloc)
512    {
513	bufalloc = newalloc;
514	buf = (char *)zrealloc(buf, bufalloc);
515    }
516
517    s = buf;
518    if (!iswprint(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) {
519	if (c == 0x7f) {
520	    *s++ = '^';
521	    c = '?';
522	} else if (c == L'\n') {
523	    *s++ = '\\';
524	    c = 'n';
525	} else if (c == L'\t') {
526	    *s++ = '\\';
527	    c = 't';
528	} else if (c < 0x20) {
529	    *s++ = '^';
530	    c += 0x40;
531	} else if (c >= 0x80) {
532	    ret = -1;
533	}
534    }
535
536    if (ret != -1)
537	ret = wcrtomb(mbstr, c, &mb_shiftstate);
538
539    if (ret == -1) {
540	memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
541	/*
542	 * Can't or don't want to convert character: use UCS-2 or
543	 * UCS-4 code in print escape format.
544	 *
545	 * This comparison fails and generates a compiler warning
546	 * if wchar_t is 16 bits, but the code is still correct.
547	 */
548	if (c >=  0x10000) {
549	    sprintf(buf, "\\U%.8x", (unsigned int)c);
550	    if (widthp)
551		*widthp = 10;
552	} else if (c >= 0x100) {
553	    sprintf(buf, "\\u%.4x", (unsigned int)c);
554	    if (widthp)
555		*widthp = 6;
556	} else {
557	    strcpy(buf, nicechar((int)c));
558	    /*
559	     * There may be metafied characters from nicechar(),
560	     * so compute width and end position independently.
561	     */
562	    if (widthp)
563		*widthp = ztrlen(buf);
564	    if (swidep)
565	      *swidep = buf + strlen(buf);
566	    return buf;
567	}
568	if (swidep)
569	    *swidep = buf + *widthp;
570	return buf;
571    }
572
573    if (widthp) {
574	int wcw = WCWIDTH(c);
575	*widthp = (s - buf);
576	if (wcw >= 0)
577	    *widthp += wcw;
578	else
579	    (*widthp)++;
580    }
581    if (swidep)
582	*swidep = s;
583    for (mbptr = mbstr; ret; s++, mbptr++, ret--) {
584	DPUTS(s >= buf + NICECHAR_MAX,
585	      "BUG: buffer too small in wcs_nicechar");
586	if (imeta(*mbptr)) {
587	    *s++ = Meta;
588	    DPUTS(s >= buf + NICECHAR_MAX,
589		  "BUG: buffer too small for metafied char in wcs_nicechar");
590	    *s = *mbptr ^ 32;
591	} else {
592	    *s = *mbptr;
593	}
594    }
595    *s = 0;
596    return buf;
597}
598
599/**/
600mod_export int
601zwcwidth(wint_t wc)
602{
603    int wcw;
604    /* assume a single-byte character if not valid */
605    if (wc == WEOF || unset(MULTIBYTE))
606	return 1;
607    wcw = WCWIDTH(wc);
608    /* if not printable, assume width 1 */
609    if (wcw < 0)
610	return 1;
611    return wcw;
612}
613
614/**/
615#endif /* MULTIBYTE_SUPPORT */
616
617/*
618 * Search the path for prog and return the file name.
619 * The returned value is unmetafied and in the unmeta storage
620 * area (N.B. should be duplicated if not used immediately and not
621 * equal to *namep).
622 *
623 * If namep is not NULL, *namep is set to the metafied programme
624 * name, which is in heap storage.
625 */
626/**/
627char *
628pathprog(char *prog, char **namep)
629{
630    char **pp, ppmaxlen = 0, *buf, *funmeta;
631    struct stat st;
632
633    for (pp = path; *pp; pp++)
634    {
635	int len = strlen(*pp);
636	if (len > ppmaxlen)
637	    ppmaxlen = len;
638    }
639    buf = zhalloc(ppmaxlen + strlen(prog) + 2);
640    for (pp = path; *pp; pp++) {
641	sprintf(buf, "%s/%s", *pp, prog);
642	funmeta = unmeta(buf);
643	if (access(funmeta, F_OK) == 0 &&
644	    stat(funmeta, &st) >= 0 &&
645	    !S_ISDIR(st.st_mode)) {
646	    if (namep)
647		*namep = buf;
648	    return funmeta;
649	}
650    }
651
652    return NULL;
653}
654
655/* get a symlink-free pathname for s relative to PWD */
656
657/**/
658char *
659findpwd(char *s)
660{
661    char *t;
662
663    if (*s == '/')
664	return xsymlink(s);
665    s = tricat((pwd[1]) ? pwd : "", "/", s);
666    t = xsymlink(s);
667    zsfree(s);
668    return t;
669}
670
671/* Check whether a string contains the *
672 * name of the present directory.      */
673
674/**/
675int
676ispwd(char *s)
677{
678    struct stat sbuf, tbuf;
679
680    if (stat(unmeta(s), &sbuf) == 0 && stat(".", &tbuf) == 0)
681	if (sbuf.st_dev == tbuf.st_dev && sbuf.st_ino == tbuf.st_ino)
682	    return 1;
683    return 0;
684}
685
686static char xbuf[PATH_MAX*2];
687
688/**/
689static char **
690slashsplit(char *s)
691{
692    char *t, **r, **q;
693    int t0;
694
695    if (!*s)
696	return (char **) zshcalloc(sizeof(char **));
697
698    for (t = s, t0 = 0; *t; t++)
699	if (*t == '/')
700	    t0++;
701    q = r = (char **) zalloc(sizeof(char **) * (t0 + 2));
702
703    while ((t = strchr(s, '/'))) {
704	*q++ = ztrduppfx(s, t - s);
705	while (*t == '/')
706	    t++;
707	if (!*t) {
708	    *q = NULL;
709	    return r;
710	}
711	s = t;
712    }
713    *q++ = ztrdup(s);
714    *q = NULL;
715    return r;
716}
717
718/* expands symlinks and .. or . expressions */
719/* if flag = 0, only expand .. and . expressions */
720
721/**/
722static int
723xsymlinks(char *s)
724{
725    char **pp, **opp;
726    char xbuf2[PATH_MAX*2], xbuf3[PATH_MAX*2];
727    int t0, ret = 0;
728
729    opp = pp = slashsplit(s);
730    for (; *pp; pp++) {
731	if (!strcmp(*pp, ".")) {
732	    zsfree(*pp);
733	    continue;
734	}
735	if (!strcmp(*pp, "..")) {
736	    char *p;
737
738	    zsfree(*pp);
739	    if (!strcmp(xbuf, "/"))
740		continue;
741	    if (!*xbuf)
742		continue;
743	    p = xbuf + strlen(xbuf);
744	    while (*--p != '/');
745	    *p = '\0';
746	    continue;
747	}
748	sprintf(xbuf2, "%s/%s", xbuf, *pp);
749	t0 = readlink(unmeta(xbuf2), xbuf3, PATH_MAX);
750	if (t0 == -1) {
751	    strcat(xbuf, "/");
752	    strcat(xbuf, *pp);
753	    zsfree(*pp);
754	} else {
755	    ret = 1;
756	    metafy(xbuf3, t0, META_NOALLOC);
757	    if (*xbuf3 == '/') {
758		strcpy(xbuf, "");
759		xsymlinks(xbuf3 + 1);
760	    } else
761		xsymlinks(xbuf3);
762	    zsfree(*pp);
763	}
764    }
765    free(opp);
766    return ret;
767}
768
769/*
770 * expand symlinks in s, and remove other weird things:
771 * note that this always expands symlinks.
772 */
773
774/**/
775char *
776xsymlink(char *s)
777{
778    if (*s != '/')
779	return NULL;
780    *xbuf = '\0';
781    xsymlinks(s + 1);
782    if (!*xbuf)
783	return ztrdup("/");
784    return ztrdup(xbuf);
785}
786
787/**/
788void
789print_if_link(char *s)
790{
791    if (*s == '/') {
792	*xbuf = '\0';
793	if (xsymlinks(s + 1))
794	    printf(" -> "), zputs(*xbuf ? xbuf : "/", stdout);
795    }
796}
797
798/* print a directory */
799
800/**/
801void
802fprintdir(char *s, FILE *f)
803{
804    Nameddir d = finddir(s);
805
806    if (!d)
807	fputs(unmeta(s), f);
808    else {
809	putc('~', f);
810	fputs(unmeta(d->node.nam), f);
811	fputs(unmeta(s + strlen(d->dir)), f);
812    }
813}
814
815/*
816 * Substitute a directory using a name.
817 * If there is none, return the original argument.
818 *
819 * At this level all strings involved are metafied.
820 */
821
822/**/
823char *
824substnamedir(char *s)
825{
826    Nameddir d = finddir(s);
827
828    if (!d)
829	return quotestring(s, NULL, QT_BACKSLASH);
830    return zhtricat("~", d->node.nam, quotestring(s + strlen(d->dir),
831						  NULL, QT_BACKSLASH));
832}
833
834
835/* Returns the current username.  It caches the username *
836 * and uid to try to avoid requerying the password files *
837 * or NIS/NIS+ database.                                 */
838
839/**/
840uid_t cached_uid;
841/**/
842char *cached_username;
843
844/**/
845char *
846get_username(void)
847{
848#ifdef HAVE_GETPWUID
849    struct passwd *pswd;
850    uid_t current_uid;
851
852    current_uid = getuid();
853    if (current_uid != cached_uid) {
854	cached_uid = current_uid;
855	zsfree(cached_username);
856	if ((pswd = getpwuid(current_uid)))
857	    cached_username = ztrdup(pswd->pw_name);
858	else
859	    cached_username = ztrdup("");
860    }
861#else /* !HAVE_GETPWUID */
862    cached_uid = getuid();
863#endif /* !HAVE_GETPWUID */
864    return cached_username;
865}
866
867/* static variables needed by finddir(). */
868
869static char *finddir_full;
870static Nameddir finddir_last;
871static int finddir_best;
872
873/* ScanFunc used by finddir(). */
874
875/**/
876static void
877finddir_scan(HashNode hn, UNUSED(int flags))
878{
879    Nameddir nd = (Nameddir) hn;
880
881    if(nd->diff > finddir_best && !dircmp(nd->dir, finddir_full)
882       && !(nd->node.flags & ND_NOABBREV)) {
883	finddir_last=nd;
884	finddir_best=nd->diff;
885    }
886}
887
888/*
889 * See if a path has a named directory as its prefix.
890 * If passed a NULL argument, it will invalidate any
891 * cached information.
892 *
893 * s here is metafied.
894 */
895
896/**/
897Nameddir
898finddir(char *s)
899{
900    static struct nameddir homenode = { {NULL, "", 0}, NULL, 0 };
901    static int ffsz;
902    char **ares;
903    int len;
904
905    /* Invalidate directory cache if argument is NULL.  This is called *
906     * whenever a node is added to or removed from the hash table, and *
907     * whenever the value of $HOME changes.  (On startup, too.)        */
908    if (!s) {
909	homenode.dir = home ? home : "";
910	homenode.diff = home ? strlen(home) : 0;
911	if(homenode.diff==1)
912	    homenode.diff = 0;
913	if(!finddir_full)
914	    finddir_full = zalloc(ffsz = PATH_MAX);
915	finddir_full[0] = 0;
916	return finddir_last = NULL;
917    }
918
919#if 0
920    /*
921     * It's not safe to use the cache while we have function
922     * transformations, and it's not clear it's worth the
923     * complexity of guessing here whether subst_string_by_hook
924     * is going to turn up the goods.
925     */
926    if (!strcmp(s, finddir_full) && *finddir_full)
927	return finddir_last;
928#endif
929
930    if ((int)strlen(s) >= ffsz) {
931	free(finddir_full);
932	finddir_full = zalloc(ffsz = strlen(s) * 2);
933    }
934    strcpy(finddir_full, s);
935    finddir_best=0;
936    finddir_last=NULL;
937    finddir_scan(&homenode.node, 0);
938    scanhashtable(nameddirtab, 0, 0, 0, finddir_scan, 0);
939
940    ares = subst_string_by_hook("zsh_directory_name", "d", finddir_full);
941    if (ares && arrlen(ares) >= 2 &&
942	(len = (int)zstrtol(ares[1], NULL, 10)) > finddir_best) {
943	/* better duplicate this string since it's come from REPLY */
944	finddir_last = (Nameddir)hcalloc(sizeof(struct nameddir));
945	finddir_last->node.nam = zhtricat("[", dupstring(ares[0]), "]");
946	finddir_last->dir = dupstrpfx(finddir_full, len);
947	finddir_last->diff = len - strlen(finddir_last->node.nam);
948	finddir_best = len;
949    }
950
951    return finddir_last;
952}
953
954/* add a named directory */
955
956/**/
957mod_export void
958adduserdir(char *s, char *t, int flags, int always)
959{
960    Nameddir nd;
961    char *eptr;
962
963    /* We don't maintain a hash table in non-interactive shells. */
964    if (!interact)
965	return;
966
967    /* The ND_USERNAME flag means that this possible hash table *
968     * entry is derived from a passwd entry.  Such entries are  *
969     * subordinate to explicitly generated entries.             */
970    if ((flags & ND_USERNAME) && nameddirtab->getnode2(nameddirtab, s))
971	return;
972
973    /* Normal parameter assignments generate calls to this function, *
974     * with always==0.  Unless the AUTO_NAME_DIRS option is set, we  *
975     * don't let such assignments actually create directory names.   *
976     * Instead, a reference to the parameter as a directory name can *
977     * cause the actual creation of the hash table entry.            */
978    if (!always && unset(AUTONAMEDIRS) &&
979	    !nameddirtab->getnode2(nameddirtab, s))
980	return;
981
982    if (!t || *t != '/' || strlen(t) >= PATH_MAX) {
983	/* We can't use this value as a directory, so simply remove *
984	 * the corresponding entry in the hash table, if any.       */
985	HashNode hn = nameddirtab->removenode(nameddirtab, s);
986
987	if(hn)
988	    nameddirtab->freenode(hn);
989	return;
990    }
991
992    /* add the name */
993    nd = (Nameddir) zshcalloc(sizeof *nd);
994    nd->node.flags = flags;
995    eptr = t + strlen(t);
996    while (eptr > t && eptr[-1] == '/')
997	eptr--;
998    if (eptr == t) {
999	/*
1000	 * Don't abbreviate multiple slashes at the start of a
1001	 * named directory, since these are sometimes used for
1002	 * special purposes.
1003	 */
1004	nd->dir = ztrdup(t);
1005    } else
1006	nd->dir = ztrduppfx(t, eptr - t);
1007    /* The variables PWD and OLDPWD are not to be displayed as ~PWD etc. */
1008    if (!strcmp(s, "PWD") || !strcmp(s, "OLDPWD"))
1009	nd->node.flags |= ND_NOABBREV;
1010    nameddirtab->addnode(nameddirtab, ztrdup(s), nd);
1011}
1012
1013/* Get a named directory: this function can cause a directory name *
1014 * to be added to the hash table, if it isn't there already.       */
1015
1016/**/
1017char *
1018getnameddir(char *name)
1019{
1020    Param pm;
1021    char *str;
1022    Nameddir nd;
1023
1024    /* Check if it is already in the named directory table */
1025    if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)))
1026	return dupstring(nd->dir);
1027
1028    /* Check if there is a scalar parameter with this name whose value *
1029     * begins with a `/'.  If there is, add it to the hash table and   *
1030     * return the new value.                                           */
1031    if ((pm = (Param) paramtab->getnode(paramtab, name)) &&
1032	    (PM_TYPE(pm->node.flags) == PM_SCALAR) &&
1033	    (str = getsparam(name)) && *str == '/') {
1034	pm->node.flags |= PM_NAMEDDIR;
1035	adduserdir(name, str, 0, 1);
1036	return str;
1037    }
1038
1039#ifdef HAVE_GETPWNAM
1040    {
1041	/* Retrieve an entry from the password table/database for this user. */
1042	struct passwd *pw;
1043	if ((pw = getpwnam(name))) {
1044	    char *dir = isset(CHASELINKS) ? xsymlink(pw->pw_dir)
1045		: ztrdup(pw->pw_dir);
1046	    adduserdir(name, dir, ND_USERNAME, 1);
1047	    str = dupstring(dir);
1048	    zsfree(dir);
1049	    return str;
1050	}
1051    }
1052#endif /* HAVE_GETPWNAM */
1053
1054    /* There are no more possible sources of directory names, so give up. */
1055    return NULL;
1056}
1057
1058/*
1059 * Compare directories.  Both are metafied.
1060 */
1061
1062/**/
1063static int
1064dircmp(char *s, char *t)
1065{
1066    if (s) {
1067	for (; *s == *t; s++, t++)
1068	    if (!*s)
1069		return 0;
1070	if (!*s && *t == '/')
1071	    return 0;
1072    }
1073    return 1;
1074}
1075
1076/*
1077 * Extra functions to call before displaying the prompt.
1078 * The data is a Prepromptfn.
1079 */
1080
1081static LinkList prepromptfns;
1082
1083/* Add a function to the list of pre-prompt functions. */
1084
1085/**/
1086mod_export void
1087addprepromptfn(voidvoidfnptr_t func)
1088{
1089    Prepromptfn ppdat = (Prepromptfn)zalloc(sizeof(struct prepromptfn));
1090    ppdat->func = func;
1091    if (!prepromptfns)
1092	prepromptfns = znewlinklist();
1093    zaddlinknode(prepromptfns, ppdat);
1094}
1095
1096/* Remove a function from the list of pre-prompt functions. */
1097
1098/**/
1099mod_export void
1100delprepromptfn(voidvoidfnptr_t func)
1101{
1102    LinkNode ln;
1103
1104    for (ln = firstnode(prepromptfns); ln; ln = nextnode(ln)) {
1105	Prepromptfn ppdat = (Prepromptfn)getdata(ln);
1106	if (ppdat->func == func) {
1107	    (void)remnode(prepromptfns, ln);
1108	    zfree(ppdat, sizeof(struct prepromptfn));
1109	    return;
1110	}
1111    }
1112#ifdef DEBUG
1113    dputs("BUG: failed to delete node from prepromptfns");
1114#endif
1115}
1116
1117/*
1118 * Functions to call at a particular time even if not at
1119 * the prompt.  This is handled by zle.  The data is a
1120 * Timedfn.  The functions must be in time order, but this
1121 * is enforced by addtimedfn().
1122 *
1123 * Note on debugging:  the code in sched.c currently assumes it's
1124 * the only user of timedfns for the purposes of checking whether
1125 * there's a function on the list.  If this becomes no longer the case,
1126 * the DPUTS() tests in sched.c need rewriting.
1127 */
1128
1129/**/
1130mod_export LinkList timedfns;
1131
1132/* Add a function to the list of timed functions. */
1133
1134/**/
1135mod_export void
1136addtimedfn(voidvoidfnptr_t func, time_t when)
1137{
1138    Timedfn tfdat = (Timedfn)zalloc(sizeof(struct timedfn));
1139    tfdat->func = func;
1140    tfdat->when = when;
1141
1142    if (!timedfns) {
1143	timedfns = znewlinklist();
1144	zaddlinknode(timedfns, tfdat);
1145    } else {
1146	LinkNode ln = firstnode(timedfns);
1147
1148	/*
1149	 * Insert the new element in the linked list.  We do
1150	 * rather too much work here since the standard
1151	 * functions insert after a given node, whereas we
1152	 * want to insert the new data before the first element
1153	 * with a greater time.
1154	 *
1155	 * In practice, the only use of timed functions is
1156	 * sched, which only adds the one function; so this
1157	 * whole branch isn't used beyond the following block.
1158	 */
1159	if (!ln) {
1160	    zaddlinknode(timedfns, tfdat);
1161	    return;
1162	}
1163	for (;;) {
1164	    Timedfn tfdat2;
1165	    LinkNode next = nextnode(ln);
1166	    if (!next) {
1167		zaddlinknode(timedfns, tfdat);
1168		return;
1169	    }
1170	    tfdat2 = (Timedfn)getdata(next);
1171	    if (when < tfdat2->when) {
1172		zinsertlinknode(timedfns, ln, tfdat);
1173		return;
1174	    }
1175	    ln = next;
1176	}
1177    }
1178}
1179
1180/*
1181 * Delete a function from the list of timed functions.
1182 * Note that if the function apperas multiple times only
1183 * the first occurrence will be removed.
1184 *
1185 * Note also that when zle calls the function it does *not*
1186 * automatically delete the entry from the list.  That must
1187 * be done by the function called.  This is recommended as otherwise
1188 * the function will keep being called immediately.  (It just so
1189 * happens this "feature" fits in well with the only current use
1190 * of timed functions.)
1191 */
1192
1193/**/
1194mod_export void
1195deltimedfn(voidvoidfnptr_t func)
1196{
1197    LinkNode ln;
1198
1199    for (ln = firstnode(timedfns); ln; ln = nextnode(ln)) {
1200	Timedfn ppdat = (Timedfn)getdata(ln);
1201	if (ppdat->func == func) {
1202	    (void)remnode(timedfns, ln);
1203	    zfree(ppdat, sizeof(struct timedfn));
1204	    return;
1205	}
1206    }
1207#ifdef DEBUG
1208    dputs("BUG: failed to delete node from timedfns");
1209#endif
1210}
1211
1212/* the last time we checked mail */
1213
1214/**/
1215time_t lastmailcheck;
1216
1217/* the last time we checked the people in the WATCH variable */
1218
1219/**/
1220time_t lastwatch;
1221
1222/*
1223 * Call a function given by "name" with optional arguments
1224 * "lnklist".  If these are present the first argument is the function name.
1225 *
1226 * If "arrayp" is not zero, we also look through
1227 * the array "name"_functions and execute functions found there.
1228 *
1229 * If "retval" is not NULL, the return value of the first hook function to
1230 * return non-zero is stored in *"retval".  The return value is not otherwise
1231 * available as the calling context is restored.
1232 */
1233
1234/**/
1235mod_export int
1236callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval)
1237{
1238    Shfunc shfunc;
1239	/*
1240	 * Save stopmsg, since user doesn't get a chance to respond
1241	 * to a list of jobs generated in a hook.
1242	 */
1243    int osc = sfcontext, osm = stopmsg, stat = 1, ret = 0;
1244    int old_incompfunc = incompfunc;
1245
1246    sfcontext = SFC_HOOK;
1247    incompfunc = 0;
1248
1249    if ((shfunc = getshfunc(name))) {
1250	ret = doshfunc(shfunc, lnklst, 1);
1251	stat = 0;
1252    }
1253
1254    if (arrayp) {
1255	char **arrptr;
1256	int namlen = strlen(name);
1257	VARARR(char, arrnam, namlen + HOOK_SUFFIX_LEN);
1258	memcpy(arrnam, name, namlen);
1259	memcpy(arrnam + namlen, HOOK_SUFFIX, HOOK_SUFFIX_LEN);
1260
1261	if ((arrptr = getaparam(arrnam))) {
1262	    arrptr = arrdup(arrptr);
1263	    for (; *arrptr; arrptr++) {
1264		if ((shfunc = getshfunc(*arrptr))) {
1265		    int newret = doshfunc(shfunc, lnklst, 1);
1266		    if (!ret)
1267			ret = newret;
1268		    stat = 0;
1269		}
1270	    }
1271	}
1272    }
1273
1274    sfcontext = osc;
1275    stopmsg = osm;
1276    incompfunc = old_incompfunc;
1277
1278    if (retval)
1279	*retval = ret;
1280    return stat;
1281}
1282
1283/* do pre-prompt stuff */
1284
1285/**/
1286void
1287preprompt(void)
1288{
1289    static time_t lastperiodic;
1290    time_t currentmailcheck;
1291    LinkNode ln;
1292    zlong period = getiparam("PERIOD");
1293    zlong mailcheck = getiparam("MAILCHECK");
1294
1295    /*
1296     * Handle any pending window size changes before we compute prompts,
1297     * then block them again to avoid interrupts during prompt display.
1298     */
1299    winch_unblock();
1300    winch_block();
1301
1302    if (isset(PROMPTSP) && isset(PROMPTCR) && !use_exit_printed && shout) {
1303	/* The PROMPT_SP heuristic will move the prompt down to a new line
1304	 * if there was any dangling output on the line (assuming the terminal
1305	 * has automatic margins, but we try even if hasam isn't set).
1306	 * Unfortunately it interacts badly with ZLE displaying message
1307	 * when ^D has been pressed. So just disable PROMPT_SP logic in
1308	 * this case */
1309	char *eolmark = getsparam("PROMPT_EOL_MARK");
1310	char *str;
1311	int percents = opts[PROMPTPERCENT], w = 0;
1312	if (!eolmark)
1313	    eolmark = "%B%S%#%s%b";
1314	opts[PROMPTPERCENT] = 1;
1315	str = promptexpand(eolmark, 1, NULL, NULL, NULL);
1316	countprompt(str, &w, 0, -1);
1317	opts[PROMPTPERCENT] = percents;
1318	zputs(str, shout);
1319	fprintf(shout, "%*s\r%*s\r", (int)zterm_columns - w - !hasxn,
1320		"", w, "");
1321	fflush(shout);
1322	free(str);
1323    }
1324
1325    /* If NOTIFY is not set, then check for completed *
1326     * jobs before we print the prompt.               */
1327    if (unset(NOTIFY))
1328	scanjobs();
1329    if (errflag)
1330	return;
1331
1332    /* If a shell function named "precmd" exists, *
1333     * then execute it.                           */
1334    callhookfunc("precmd", NULL, 1, NULL);
1335    if (errflag)
1336	return;
1337
1338    /* If 1) the parameter PERIOD exists, 2) a hook function for    *
1339     * "periodic" exists, 3) it's been greater than PERIOD since we *
1340     * executed any such hook, then execute it now.                 */
1341    if (period && ((zlong)time(NULL) > (zlong)lastperiodic + period) &&
1342	!callhookfunc("periodic", NULL, 1, NULL))
1343	lastperiodic = time(NULL);
1344    if (errflag)
1345	return;
1346
1347    /* If WATCH is set, then check for the *
1348     * specified login/logout events.      */
1349    if (watch) {
1350	if ((int) difftime(time(NULL), lastwatch) > getiparam("LOGCHECK")) {
1351	    dowatch();
1352	    lastwatch = time(NULL);
1353	}
1354    }
1355    if (errflag)
1356	return;
1357
1358    /* Check mail */
1359    currentmailcheck = time(NULL);
1360    if (mailcheck &&
1361	(zlong) difftime(currentmailcheck, lastmailcheck) > mailcheck) {
1362	char *mailfile;
1363
1364	if (mailpath && *mailpath && **mailpath)
1365	    checkmailpath(mailpath);
1366	else {
1367	    queue_signals();
1368	    if ((mailfile = getsparam("MAIL")) && *mailfile) {
1369		char *x[2];
1370
1371		x[0] = mailfile;
1372		x[1] = NULL;
1373		checkmailpath(x);
1374	    }
1375	    unqueue_signals();
1376	}
1377	lastmailcheck = currentmailcheck;
1378    }
1379
1380    if (prepromptfns) {
1381	for(ln = firstnode(prepromptfns); ln; ln = nextnode(ln)) {
1382	    Prepromptfn ppnode = (Prepromptfn)getdata(ln);
1383	    ppnode->func();
1384	}
1385    }
1386}
1387
1388/**/
1389static void
1390checkmailpath(char **s)
1391{
1392    struct stat st;
1393    char *v, *u, c;
1394
1395    while (*s) {
1396	for (v = *s; *v && *v != '?'; v++);
1397	c = *v;
1398	*v = '\0';
1399	if (c != '?')
1400	    u = NULL;
1401	else
1402	    u = v + 1;
1403	if (**s == 0) {
1404	    *v = c;
1405	    zerr("empty MAILPATH component: %s", *s);
1406	} else if (mailstat(unmeta(*s), &st) == -1) {
1407	    if (errno != ENOENT)
1408		zerr("%e: %s", errno, *s);
1409	} else if (S_ISDIR(st.st_mode)) {
1410	    LinkList l;
1411	    DIR *lock = opendir(unmeta(*s));
1412	    char buf[PATH_MAX * 2], **arr, **ap;
1413	    int ct = 1;
1414
1415	    if (lock) {
1416		char *fn;
1417
1418		pushheap();
1419		l = newlinklist();
1420		while ((fn = zreaddir(lock, 1)) && !errflag) {
1421		    if (u)
1422			sprintf(buf, "%s/%s?%s", *s, fn, u);
1423		    else
1424			sprintf(buf, "%s/%s", *s, fn);
1425		    addlinknode(l, dupstring(buf));
1426		    ct++;
1427		}
1428		closedir(lock);
1429		ap = arr = (char **) zhalloc(ct * sizeof(char *));
1430
1431		while ((*ap++ = (char *)ugetnode(l)));
1432		checkmailpath(arr);
1433		popheap();
1434	    }
1435	} else if (shout) {
1436	    if (st.st_size && st.st_atime <= st.st_mtime &&
1437		st.st_mtime >= lastmailcheck) {
1438		if (!u) {
1439		    fprintf(shout, "You have new mail.\n");
1440		    fflush(shout);
1441		} else {
1442		    char *usav;
1443		    int uusav = underscoreused;
1444
1445		    usav = zalloc(underscoreused);
1446
1447		    if (usav)
1448			memcpy(usav, zunderscore, underscoreused);
1449
1450		    setunderscore(*s);
1451
1452		    u = dupstring(u);
1453		    if (! parsestr(u)) {
1454			singsub(&u);
1455			zputs(u, shout);
1456			fputc('\n', shout);
1457			fflush(shout);
1458		    }
1459		    if (usav) {
1460			setunderscore(usav);
1461			zfree(usav, uusav);
1462		    }
1463		}
1464	    }
1465	    if (isset(MAILWARNING) && st.st_atime > st.st_mtime &&
1466		st.st_atime > lastmailcheck && st.st_size) {
1467		fprintf(shout, "The mail in %s has been read.\n", unmeta(*s));
1468		fflush(shout);
1469	    }
1470	}
1471	*v = c;
1472	s++;
1473    }
1474}
1475
1476/* This prints the XTRACE prompt. */
1477
1478/**/
1479FILE *xtrerr = 0;
1480
1481/**/
1482void
1483printprompt4(void)
1484{
1485    if (!xtrerr)
1486	xtrerr = stderr;
1487    if (prompt4) {
1488	int l, t = opts[XTRACE];
1489	char *s = dupstring(prompt4);
1490
1491	opts[XTRACE] = 0;
1492	unmetafy(s, &l);
1493	s = unmetafy(promptexpand(metafy(s, l, META_NOALLOC),
1494				  0, NULL, NULL, NULL), &l);
1495	opts[XTRACE] = t;
1496
1497	fprintf(xtrerr, "%s", s);
1498	free(s);
1499    }
1500}
1501
1502/**/
1503mod_export void
1504freestr(void *a)
1505{
1506    zsfree(a);
1507}
1508
1509/**/
1510mod_export void
1511gettyinfo(struct ttyinfo *ti)
1512{
1513    if (SHTTY != -1) {
1514#ifdef HAVE_TERMIOS_H
1515# ifdef HAVE_TCGETATTR
1516	if (tcgetattr(SHTTY, &ti->tio) == -1)
1517# else
1518	if (ioctl(SHTTY, TCGETS, &ti->tio) == -1)
1519# endif
1520	    zerr("bad tcgets: %e", errno);
1521#else
1522# ifdef HAVE_TERMIO_H
1523	ioctl(SHTTY, TCGETA, &ti->tio);
1524# else
1525	ioctl(SHTTY, TIOCGETP, &ti->sgttyb);
1526	ioctl(SHTTY, TIOCLGET, &ti->lmodes);
1527	ioctl(SHTTY, TIOCGETC, &ti->tchars);
1528	ioctl(SHTTY, TIOCGLTC, &ti->ltchars);
1529# endif
1530#endif
1531    }
1532}
1533
1534/**/
1535mod_export void
1536settyinfo(struct ttyinfo *ti)
1537{
1538    if (SHTTY != -1) {
1539#ifdef HAVE_TERMIOS_H
1540# ifdef HAVE_TCGETATTR
1541#  ifndef TCSADRAIN
1542#   define TCSADRAIN 1	/* XXX Princeton's include files are screwed up */
1543#  endif
1544	while (tcsetattr(SHTTY, TCSADRAIN, &ti->tio) == -1 && errno == EINTR)
1545	    ;
1546# else
1547	while (ioctl(SHTTY, TCSETS, &ti->tio) == -1 && errno == EINTR)
1548	    ;
1549# endif
1550	/*	zerr("settyinfo: %e",errno);*/
1551#else
1552# ifdef HAVE_TERMIO_H
1553	ioctl(SHTTY, TCSETA, &ti->tio);
1554# else
1555	ioctl(SHTTY, TIOCSETN, &ti->sgttyb);
1556	ioctl(SHTTY, TIOCLSET, &ti->lmodes);
1557	ioctl(SHTTY, TIOCSETC, &ti->tchars);
1558	ioctl(SHTTY, TIOCSLTC, &ti->ltchars);
1559# endif
1560#endif
1561    }
1562}
1563
1564/* the default tty state */
1565
1566/**/
1567mod_export struct ttyinfo shttyinfo;
1568
1569/* != 0 if we need to call resetvideo() */
1570
1571/**/
1572mod_export int resetneeded;
1573
1574#ifdef TIOCGWINSZ
1575/* window size changed */
1576
1577/**/
1578mod_export int winchanged;
1579#endif
1580
1581static int
1582adjustlines(int signalled)
1583{
1584    int oldlines = zterm_lines;
1585
1586#ifdef TIOCGWINSZ
1587    if (signalled || zterm_lines <= 0)
1588	zterm_lines = shttyinfo.winsize.ws_row;
1589    else
1590	shttyinfo.winsize.ws_row = zterm_lines;
1591#endif /* TIOCGWINSZ */
1592    if (zterm_lines <= 0) {
1593	DPUTS(signalled, "BUG: Impossible TIOCGWINSZ rows");
1594	zterm_lines = tclines > 0 ? tclines : 24;
1595    }
1596
1597    if (zterm_lines > 2)
1598	termflags &= ~TERM_SHORT;
1599    else
1600	termflags |= TERM_SHORT;
1601
1602    return (zterm_lines != oldlines);
1603}
1604
1605static int
1606adjustcolumns(int signalled)
1607{
1608    int oldcolumns = zterm_columns;
1609
1610#ifdef TIOCGWINSZ
1611    if (signalled || zterm_columns <= 0)
1612	zterm_columns = shttyinfo.winsize.ws_col;
1613    else
1614	shttyinfo.winsize.ws_col = zterm_columns;
1615#endif /* TIOCGWINSZ */
1616    if (zterm_columns <= 0) {
1617	DPUTS(signalled, "BUG: Impossible TIOCGWINSZ cols");
1618	zterm_columns = tccolumns > 0 ? tccolumns : 80;
1619    }
1620
1621    if (zterm_columns > 2)
1622	termflags &= ~TERM_NARROW;
1623    else
1624	termflags |= TERM_NARROW;
1625
1626    return (zterm_columns != oldcolumns);
1627}
1628
1629/* check the size of the window and adjust if necessary. *
1630 * The value of from:					 *
1631 *   0: called from update_job or setupvals		 *
1632 *   1: called from the SIGWINCH handler		 *
1633 *   2: called from the LINES parameter callback	 *
1634 *   3: called from the COLUMNS parameter callback	 */
1635
1636/**/
1637void
1638adjustwinsize(int from)
1639{
1640    static int getwinsz = 1;
1641#ifdef TIOCGWINSZ
1642    int ttyrows = shttyinfo.winsize.ws_row;
1643    int ttycols = shttyinfo.winsize.ws_col;
1644#endif
1645    int resetzle = 0;
1646
1647    if (getwinsz || from == 1) {
1648#ifdef TIOCGWINSZ
1649	if (SHTTY == -1)
1650	    return;
1651	if (ioctl(SHTTY, TIOCGWINSZ, (char *)&shttyinfo.winsize) == 0) {
1652	    resetzle = (ttyrows != shttyinfo.winsize.ws_row ||
1653			ttycols != shttyinfo.winsize.ws_col);
1654	    if (from == 0 && resetzle && ttyrows && ttycols)
1655		from = 1; /* Signal missed while a job owned the tty? */
1656	    ttyrows = shttyinfo.winsize.ws_row;
1657	    ttycols = shttyinfo.winsize.ws_col;
1658	} else {
1659	    /* Set to value from environment on failure */
1660	    shttyinfo.winsize.ws_row = zterm_lines;
1661	    shttyinfo.winsize.ws_col = zterm_columns;
1662	    resetzle = (from == 1);
1663	}
1664#else
1665	resetzle = from == 1;
1666#endif /* TIOCGWINSZ */
1667    } /* else
1668	 return; */
1669
1670    switch (from) {
1671    case 0:
1672    case 1:
1673	getwinsz = 0;
1674	/* Calling setiparam() here calls this function recursively, but  *
1675	 * because we've already called adjustlines() and adjustcolumns() *
1676	 * here, recursive calls are no-ops unless a signal intervenes.   *
1677	 * The commented "else return;" above might be a safe shortcut,   *
1678	 * but I'm concerned about what happens on race conditions; e.g., *
1679	 * suppose the user resizes his xterm during `eval $(resize)'?    */
1680	if (adjustlines(from) && zgetenv("LINES"))
1681	    setiparam("LINES", zterm_lines);
1682	if (adjustcolumns(from) && zgetenv("COLUMNS"))
1683	    setiparam("COLUMNS", zterm_columns);
1684	getwinsz = 1;
1685	break;
1686    case 2:
1687	resetzle = adjustlines(0);
1688	break;
1689    case 3:
1690	resetzle = adjustcolumns(0);
1691	break;
1692    }
1693
1694#ifdef TIOCGWINSZ
1695    if (interact && from >= 2 &&
1696	(shttyinfo.winsize.ws_row != ttyrows ||
1697	 shttyinfo.winsize.ws_col != ttycols)) {
1698	/* shttyinfo.winsize is already set up correctly */
1699	/* ioctl(SHTTY, TIOCSWINSZ, (char *)&shttyinfo.winsize); */
1700    }
1701#endif /* TIOCGWINSZ */
1702
1703    if (zleactive && resetzle) {
1704#ifdef TIOCGWINSZ
1705	winchanged =
1706#endif /* TIOCGWINSZ */
1707	    resetneeded = 1;
1708	zleentry(ZLE_CMD_RESET_PROMPT);
1709	zleentry(ZLE_CMD_REFRESH);
1710    }
1711}
1712
1713/*
1714 * Ensure the fdtable is large enough for fd, and that the
1715 * maximum fd is set appropriately.
1716 */
1717static void
1718check_fd_table(int fd)
1719{
1720    if (fd <= max_zsh_fd)
1721	return;
1722
1723    if (fd >= fdtable_size) {
1724	int old_size = fdtable_size;
1725	while (fd >= fdtable_size)
1726	    fdtable = zrealloc(fdtable,
1727			       (fdtable_size *= 2)*sizeof(*fdtable));
1728	memset(fdtable + old_size, 0,
1729	       (fdtable_size - old_size) * sizeof(*fdtable));
1730    }
1731    max_zsh_fd = fd;
1732}
1733
1734/* Move a fd to a place >= 10 and mark the new fd in fdtable.  If the fd *
1735 * is already >= 10, it is not moved.  If it is invalid, -1 is returned. */
1736
1737/**/
1738mod_export int
1739movefd(int fd)
1740{
1741    if(fd != -1 && fd < 10) {
1742#ifdef F_DUPFD
1743	int fe = fcntl(fd, F_DUPFD, 10);
1744#else
1745	int fe = movefd(dup(fd));
1746#endif
1747	/*
1748	 * To close or not to close if fe is -1?
1749	 * If it is -1, we haven't moved the fd, so if we close
1750	 * it we lose it; but we're probably not going to be able
1751	 * to use it in situ anyway.  So probably better to avoid a leak.
1752	 */
1753	zclose(fd);
1754	fd = fe;
1755    }
1756    if(fd != -1) {
1757	check_fd_table(fd);
1758	fdtable[fd] = FDT_INTERNAL;
1759    }
1760    return fd;
1761}
1762
1763/*
1764 * Move fd x to y.  If x == -1, fd y is closed.
1765 * Returns y for success, -1 for failure.
1766 */
1767
1768/**/
1769mod_export int
1770redup(int x, int y)
1771{
1772    int ret = y;
1773
1774    if(x < 0)
1775	zclose(y);
1776    else if (x != y) {
1777	if (dup2(x, y) == -1) {
1778	    ret = -1;
1779	} else {
1780	    check_fd_table(y);
1781	    fdtable[y] = fdtable[x];
1782	    if (fdtable[y] == FDT_FLOCK || fdtable[y] == FDT_FLOCK_EXEC)
1783		fdtable[y] = FDT_INTERNAL;
1784	}
1785	/*
1786	 * Closing any fd to the locked file releases the lock.
1787	 * This isn't expected to happen, it's here for completeness.
1788	 */
1789	if (fdtable[x] == FDT_FLOCK)
1790	    fdtable_flocks--;
1791	zclose(x);
1792    }
1793
1794    return ret;
1795}
1796
1797/*
1798 * Indicate that an fd has a file lock; if cloexec is 1 it will be closed
1799 * on exec.
1800 * The fd should already be known to fdtable (e.g. by movefd).
1801 * Note the fdtable code doesn't care what sort of lock
1802 * is used; this simply prevents the main shell exiting prematurely
1803 * when it holds a lock.
1804 */
1805
1806/**/
1807mod_export void
1808addlockfd(int fd, int cloexec)
1809{
1810    if (cloexec) {
1811	if (fdtable[fd] != FDT_FLOCK)
1812	    fdtable_flocks++;
1813	fdtable[fd] = FDT_FLOCK;
1814    } else {
1815	fdtable[fd] = FDT_FLOCK_EXEC;
1816    }
1817}
1818
1819/* Close the given fd, and clear it from fdtable. */
1820
1821/**/
1822mod_export int
1823zclose(int fd)
1824{
1825    if (fd >= 0) {
1826	/*
1827	 * Careful: we allow closing of arbitrary fd's, beyond
1828	 * max_zsh_fd.  In that case we don't try anything clever.
1829	 */
1830	if (fd <= max_zsh_fd) {
1831	    if (fdtable[fd] == FDT_FLOCK)
1832		fdtable_flocks--;
1833	    fdtable[fd] = FDT_UNUSED;
1834	    while (max_zsh_fd > 0 && fdtable[max_zsh_fd] == FDT_UNUSED)
1835		max_zsh_fd--;
1836	    if (fd == coprocin)
1837		coprocin = -1;
1838	    if (fd == coprocout)
1839		coprocout = -1;
1840	}
1841	return close(fd);
1842    }
1843    return -1;
1844}
1845
1846/*
1847 * Close an fd returning 0 if used for locking; return -1 if it isn't.
1848 */
1849
1850/**/
1851mod_export int
1852zcloselockfd(int fd)
1853{
1854    if (fd > max_zsh_fd)
1855	return -1;
1856    if (fdtable[fd] != FDT_FLOCK && fdtable[fd] != FDT_FLOCK_EXEC)
1857	return -1;
1858    zclose(fd);
1859    return 0;
1860}
1861
1862#ifdef HAVE__MKTEMP
1863extern char *_mktemp(char *);
1864#endif
1865
1866/* Get a unique filename for use as a temporary file.  If "prefix" is
1867 * NULL, the name is relative to $TMPPREFIX; If it is non-NULL, the
1868 * unique suffix includes a prefixed '.' for improved readability.  If
1869 * "use_heap" is true, we allocate the returned name on the heap. */
1870
1871/**/
1872mod_export char *
1873gettempname(const char *prefix, int use_heap)
1874{
1875    char *ret, *suffix = prefix ? ".XXXXXX" : "XXXXXX";
1876
1877    queue_signals();
1878    if (!prefix && !(prefix = getsparam("TMPPREFIX")))
1879	prefix = DEFAULT_TMPPREFIX;
1880    if (use_heap)
1881	ret = dyncat(unmeta(prefix), suffix);
1882    else
1883	ret = bicat(unmeta(prefix), suffix);
1884
1885#ifdef HAVE__MKTEMP
1886    /* Zsh uses mktemp() safely, so silence the warnings */
1887    ret = (char *) _mktemp(ret);
1888#else
1889    ret = (char *) mktemp(ret);
1890#endif
1891    unqueue_signals();
1892
1893    return ret;
1894}
1895
1896/**/
1897mod_export int
1898gettempfile(const char *prefix, int use_heap, char **tempname)
1899{
1900    char *fn;
1901    int fd;
1902#if HAVE_MKSTEMP
1903    char *suffix = prefix ? ".XXXXXX" : "XXXXXX";
1904
1905    if (!prefix && !(prefix = getsparam("TMPPREFIX")))
1906	prefix = DEFAULT_TMPPREFIX;
1907    if (use_heap)
1908	fn = dyncat(unmeta(prefix), suffix);
1909    else
1910	fn = bicat(unmeta(prefix), suffix);
1911
1912    fd = mkstemp(fn);
1913    if (fd < 0) {
1914	if (!use_heap)
1915	    free(fn);
1916	fn = NULL;
1917    }
1918#else
1919    int failures = 0;
1920
1921    do {
1922	if (!(fn = gettempname(prefix, use_heap))) {
1923	    fd = -1;
1924	    break;
1925	}
1926	if ((fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0600)) >= 0)
1927	    break;
1928	if (!use_heap)
1929	    free(fn);
1930	fn = NULL;
1931    } while (errno == EEXIST && ++failures < 16);
1932#endif
1933    *tempname = fn;
1934    return fd;
1935}
1936
1937/* Check if a string contains a token */
1938
1939/**/
1940mod_export int
1941has_token(const char *s)
1942{
1943    while(*s)
1944	if(itok(*s++))
1945	    return 1;
1946    return 0;
1947}
1948
1949/* Delete a character in a string */
1950
1951/**/
1952mod_export void
1953chuck(char *str)
1954{
1955    while ((str[0] = str[1]))
1956	str++;
1957}
1958
1959/**/
1960mod_export int
1961tulower(int c)
1962{
1963    c &= 0xff;
1964    return (isupper(c) ? tolower(c) : c);
1965}
1966
1967/**/
1968mod_export int
1969tuupper(int c)
1970{
1971    c &= 0xff;
1972    return (islower(c) ? toupper(c) : c);
1973}
1974
1975/* copy len chars from t into s, and null terminate */
1976
1977/**/
1978void
1979ztrncpy(char *s, char *t, int len)
1980{
1981    while (len--)
1982	*s++ = *t++;
1983    *s = '\0';
1984}
1985
1986/* copy t into *s and update s */
1987
1988/**/
1989mod_export void
1990strucpy(char **s, char *t)
1991{
1992    char *u = *s;
1993
1994    while ((*u++ = *t++));
1995    *s = u - 1;
1996}
1997
1998/**/
1999mod_export void
2000struncpy(char **s, char *t, int n)
2001{
2002    char *u = *s;
2003
2004    while (n--)
2005	*u++ = *t++;
2006    *s = u;
2007    *u = '\0';
2008}
2009
2010/* Return the number of elements in an array of pointers. *
2011 * It doesn't count the NULL pointer at the end.          */
2012
2013/**/
2014mod_export int
2015arrlen(char **s)
2016{
2017    int count;
2018
2019    for (count = 0; *s; s++, count++);
2020    return count;
2021}
2022
2023/* Skip over a balanced pair of parenthesis. */
2024
2025/**/
2026mod_export int
2027skipparens(char inpar, char outpar, char **s)
2028{
2029    int level;
2030
2031    if (**s != inpar)
2032	return -1;
2033
2034    for (level = 1; *++*s && level;)
2035	if (**s == inpar)
2036	   ++level;
2037	else if (**s == outpar)
2038	   --level;
2039
2040   return level;
2041}
2042
2043/**/
2044mod_export zlong
2045zstrtol(const char *s, char **t, int base)
2046{
2047    return zstrtol_underscore(s, t, base, 0);
2048}
2049
2050/* Convert string to zlong (see zsh.h).  This function (without the z) *
2051 * is contained in the ANSI standard C library, but a lot of them seem *
2052 * to be broken.                                                       */
2053
2054/**/
2055mod_export zlong
2056zstrtol_underscore(const char *s, char **t, int base, int underscore)
2057{
2058    const char *inp, *trunc = NULL;
2059    zulong calc = 0, newcalc = 0;
2060    int neg;
2061
2062    while (inblank(*s))
2063	s++;
2064
2065    if ((neg = (*s == '-')))
2066	s++;
2067    else if (*s == '+')
2068	s++;
2069
2070    if (!base) {
2071	if (*s != '0')
2072	    base = 10;
2073	else if (*++s == 'x' || *s == 'X')
2074	    base = 16, s++;
2075	else
2076	    base = 8;
2077    }
2078    inp = s;
2079    if (base < 2 || base > 36) {
2080	zerr("invalid base (must be 2 to 36 inclusive): %d", base);
2081	return (zlong)0;
2082    } else if (base <= 10) {
2083	for (; (*s >= '0' && *s < ('0' + base)) ||
2084		 (underscore && *s == '_'); s++) {
2085	    if (trunc || *s == '_')
2086		continue;
2087	    newcalc = calc * base + *s - '0';
2088	    if (newcalc < calc)
2089	    {
2090		trunc = s;
2091		continue;
2092	    }
2093	    calc = newcalc;
2094	}
2095    } else {
2096	for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10))
2097	     || (*s >= 'A' && *s < ('A' + base - 10))
2098	     || (underscore && *s == '_'); s++) {
2099	    if (trunc || *s == '_')
2100		continue;
2101	    newcalc = calc*base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9);
2102	    if (newcalc < calc)
2103	    {
2104		trunc = s;
2105		continue;
2106	    }
2107	    calc = newcalc;
2108	}
2109    }
2110
2111    /*
2112     * Special case: check for a number that was just too long for
2113     * signed notation.
2114     * Extra special case: the lowest negative number would trigger
2115     * the first test, but is actually representable correctly.
2116     * This is a 1 in the top bit, all others zero, so test for
2117     * that explicitly.
2118     */
2119    if (!trunc && (zlong)calc < 0 &&
2120	(!neg || calc & ~((zulong)1 << (8*sizeof(zulong)-1))))
2121    {
2122	trunc = s - 1;
2123	calc /= base;
2124    }
2125
2126    if (trunc)
2127	zwarn("number truncated after %d digits: %s", (int)(trunc - inp), inp);
2128
2129    if (t)
2130	*t = (char *)s;
2131    return neg ? -(zlong)calc : (zlong)calc;
2132}
2133
2134/**/
2135mod_export int
2136setblock_fd(int turnonblocking, int fd, long *modep)
2137{
2138#ifdef O_NDELAY
2139# ifdef O_NONBLOCK
2140#  define NONBLOCK (O_NDELAY|O_NONBLOCK)
2141# else /* !O_NONBLOCK */
2142#  define NONBLOCK O_NDELAY
2143# endif /* !O_NONBLOCK */
2144#else /* !O_NDELAY */
2145# ifdef O_NONBLOCK
2146#  define NONBLOCK O_NONBLOCK
2147# else /* !O_NONBLOCK */
2148#  define NONBLOCK 0
2149# endif /* !O_NONBLOCK */
2150#endif /* !O_NDELAY */
2151
2152#if NONBLOCK
2153    struct stat st;
2154
2155    if (!fstat(fd, &st) && !S_ISREG(st.st_mode)) {
2156	*modep = fcntl(fd, F_GETFL, 0);
2157	if (*modep != -1) {
2158	    if (!turnonblocking) {
2159		/* We want to know if blocking was off */
2160		if ((*modep & NONBLOCK) ||
2161		    !fcntl(fd, F_SETFL, *modep | NONBLOCK))
2162		    return 1;
2163	    } else if ((*modep & NONBLOCK) &&
2164		       !fcntl(fd, F_SETFL, *modep & ~NONBLOCK)) {
2165		/* Here we want to know if the state changed */
2166		return 1;
2167	    }
2168	}
2169    } else
2170#endif /* NONBLOCK */
2171	*modep = -1;
2172    return 0;
2173
2174#undef NONBLOCK
2175}
2176
2177/**/
2178int
2179setblock_stdin(void)
2180{
2181    long mode;
2182    return setblock_fd(1, 0, &mode);
2183}
2184
2185/*
2186 * Check for pending input on fd.  If polltty is set, we may need to
2187 * use termio to look for input.  As a final resort, go to non-blocking
2188 * input and try to read a character, which in this case will be
2189 * returned in *readchar.
2190 *
2191 * Note that apart from setting (and restoring) non-blocking input,
2192 * this function does not change the input mode.  The calling function
2193 * should have set cbreak mode if necessary.
2194 */
2195
2196/**/
2197mod_export int
2198read_poll(int fd, int *readchar, int polltty, zlong microseconds)
2199{
2200    int ret = -1;
2201    long mode = -1;
2202    char c;
2203#ifdef HAVE_SELECT
2204    fd_set foofd;
2205    struct timeval expire_tv;
2206#else
2207#ifdef FIONREAD
2208    int val;
2209#endif
2210#endif
2211#ifdef HAS_TIO
2212    struct ttyinfo ti;
2213#endif
2214
2215
2216#if defined(HAS_TIO) && !defined(__CYGWIN__)
2217    /*
2218     * Under Solaris, at least, reading from the terminal in non-canonical
2219     * mode requires that we use the VMIN mechanism to poll.  Any attempt
2220     * to check any other way, or to set the terminal to non-blocking mode
2221     * and poll that way, fails; it will just for canonical mode input.
2222     * We should probably use this mechanism if the user has set non-canonical
2223     * mode, in which case testing here for isatty() and ~ICANON would be
2224     * better than testing whether bin_read() set it, but for now we've got
2225     * enough problems.
2226     *
2227     * Under Cygwin, you won't be surprised to here, this mechanism,
2228     * although present, doesn't work, and we *have* to use ordinary
2229     * non-blocking reads to find out if there is a character present
2230     * in non-canonical mode.
2231     *
2232     * I am assuming Solaris is nearer the UNIX norm.  This is not necessarily
2233     * as plausible as it sounds, but it seems the right way to guess.
2234     *		pws 2000/06/26
2235     */
2236    if (polltty) {
2237	gettyinfo(&ti);
2238	if ((polltty = ti.tio.c_cc[VMIN])) {
2239	    ti.tio.c_cc[VMIN] = 0;
2240	    /* termios timeout is 10ths of a second */
2241	    ti.tio.c_cc[VTIME] = (int) (microseconds / (zlong)100000);
2242	    settyinfo(&ti);
2243	}
2244    }
2245#else
2246    polltty = 0;
2247#endif
2248#ifdef HAVE_SELECT
2249    expire_tv.tv_sec = (int) (microseconds / (zlong)1000000);
2250    expire_tv.tv_usec = microseconds % (zlong)1000000;
2251    FD_ZERO(&foofd);
2252    FD_SET(fd, &foofd);
2253    ret = select(fd+1, (SELECT_ARG_2_T) &foofd, NULL, NULL, &expire_tv);
2254#else
2255#ifdef FIONREAD
2256    if (ioctl(fd, FIONREAD, (char *) &val) == 0)
2257	ret = (val > 0);
2258#endif
2259#endif
2260
2261    if (ret < 0) {
2262	/*
2263	 * Final attempt: set non-blocking read and try to read a character.
2264	 * Praise Bill, this works under Cygwin (nothing else seems to).
2265	 */
2266	if ((polltty || setblock_fd(0, fd, &mode)) && read(fd, &c, 1) > 0) {
2267	    *readchar = c;
2268	    ret = 1;
2269	}
2270	if (mode != -1)
2271	    fcntl(fd, F_SETFL, mode);
2272    }
2273#ifdef HAS_TIO
2274    if (polltty) {
2275	ti.tio.c_cc[VMIN] = 1;
2276	ti.tio.c_cc[VTIME] = 0;
2277	settyinfo(&ti);
2278    }
2279#endif
2280    return (ret > 0);
2281}
2282
2283/**/
2284int
2285checkrmall(char *s)
2286{
2287    if (!shout)
2288	return 1;
2289    fprintf(shout, "zsh: sure you want to delete all the files in ");
2290    if (*s != '/') {
2291	nicezputs(pwd[1] ? unmeta(pwd) : "", shout);
2292	fputc('/', shout);
2293    }
2294    nicezputs(s, shout);
2295    if(isset(RMSTARWAIT)) {
2296	fputs("? (waiting ten seconds)", shout);
2297	fflush(shout);
2298	zbeep();
2299	sleep(10);
2300	fputc('\n', shout);
2301    }
2302    if (errflag)
2303      return 0;
2304    fputs(" [yn]? ", shout);
2305    fflush(shout);
2306    zbeep();
2307    return (getquery("ny", 1) == 'y');
2308}
2309
2310/**/
2311mod_export ssize_t
2312read_loop(int fd, char *buf, size_t len)
2313{
2314    ssize_t got = len;
2315
2316    while (1) {
2317	ssize_t ret = read(fd, buf, len);
2318	if (ret == len)
2319	    break;
2320	if (ret <= 0) {
2321	    if (ret < 0) {
2322		if (errno == EINTR)
2323		    continue;
2324		if (fd != SHTTY)
2325		    zwarn("read failed: %e", errno);
2326	    }
2327	    return ret;
2328	}
2329	buf += ret;
2330	len -= ret;
2331    }
2332
2333    return got;
2334}
2335
2336/**/
2337mod_export ssize_t
2338write_loop(int fd, const char *buf, size_t len)
2339{
2340    ssize_t wrote = len;
2341
2342    while (1) {
2343	ssize_t ret = write(fd, buf, len);
2344	if (ret == len)
2345	    break;
2346	if (ret < 0) {
2347	    if (errno == EINTR)
2348		continue;
2349	    if (fd != SHTTY)
2350		zwarn("write failed: %e", errno);
2351	    return -1;
2352	}
2353	buf += ret;
2354	len -= ret;
2355    }
2356
2357    return wrote;
2358}
2359
2360static int
2361read1char(int echo)
2362{
2363    char c;
2364
2365    while (read(SHTTY, &c, 1) != 1) {
2366	if (errno != EINTR || errflag || retflag || breaks || contflag)
2367	    return -1;
2368    }
2369    if (echo)
2370	write_loop(SHTTY, &c, 1);
2371    return STOUC(c);
2372}
2373
2374/**/
2375mod_export int
2376noquery(int purge)
2377{
2378    int val = 0;
2379
2380#ifdef FIONREAD
2381    char c;
2382
2383    ioctl(SHTTY, FIONREAD, (char *)&val);
2384    if (purge) {
2385	for (; val; val--) {
2386	    if (read(SHTTY, &c, 1) != 1) {
2387		/* Do nothing... */
2388	    }
2389	}
2390    }
2391#endif
2392
2393    return val;
2394}
2395
2396/**/
2397int
2398getquery(char *valid_chars, int purge)
2399{
2400    int c, d, nl = 0;
2401    int isem = !strcmp(term, "emacs");
2402    struct ttyinfo ti;
2403
2404    attachtty(mypgrp);
2405
2406    gettyinfo(&ti);
2407#ifdef HAS_TIO
2408    ti.tio.c_lflag &= ~ECHO;
2409    if (!isem) {
2410	ti.tio.c_lflag &= ~ICANON;
2411	ti.tio.c_cc[VMIN] = 1;
2412	ti.tio.c_cc[VTIME] = 0;
2413    }
2414#else
2415    ti.sgttyb.sg_flags &= ~ECHO;
2416    if (!isem)
2417	ti.sgttyb.sg_flags |= CBREAK;
2418#endif
2419    settyinfo(&ti);
2420
2421    if (noquery(purge)) {
2422	if (!isem)
2423	    settyinfo(&shttyinfo);
2424	write_loop(SHTTY, "n\n", 2);
2425	return 'n';
2426    }
2427
2428    while ((c = read1char(0)) >= 0) {
2429	if (c == 'Y')
2430	    c = 'y';
2431	else if (c == 'N')
2432	    c = 'n';
2433	if (!valid_chars)
2434	    break;
2435	if (c == '\n') {
2436	    c = *valid_chars;
2437	    nl = 1;
2438	    break;
2439	}
2440	if (strchr(valid_chars, c)) {
2441	    nl = 1;
2442	    break;
2443	}
2444	zbeep();
2445    }
2446    if (c >= 0) {
2447	char buf = (char)c;
2448	write_loop(SHTTY, &buf, 1);
2449    }
2450    if (nl)
2451	write_loop(SHTTY, "\n", 1);
2452
2453    if (isem) {
2454	if (c != '\n')
2455	    while ((d = read1char(1)) >= 0 && d != '\n');
2456    } else {
2457	if (c != '\n' && !valid_chars) {
2458#ifdef MULTIBYTE_SUPPORT
2459	    if (isset(MULTIBYTE) && c >= 0) {
2460		/*
2461		 * No waiting for a valid character, and no draining;
2462		 * we should ensure we haven't stopped in the middle
2463		 * of a multibyte character.
2464		 */
2465		mbstate_t mbs;
2466		char cc = (char)c;
2467		memset(&mbs, 0, sizeof(mbs));
2468		for (;;) {
2469		    size_t ret = mbrlen(&cc, 1, &mbs);
2470
2471		    if (ret != MB_INCOMPLETE)
2472			break;
2473		    c = read1char(1);
2474		    if (c < 0)
2475			break;
2476		    cc = (char)c;
2477		}
2478	    }
2479#endif
2480	    write_loop(SHTTY, "\n", 1);
2481	}
2482    }
2483    settyinfo(&shttyinfo);
2484    return c;
2485}
2486
2487static int d;
2488static char *guess, *best;
2489static Patprog spckpat;
2490
2491/**/
2492static void
2493spscan(HashNode hn, UNUSED(int scanflags))
2494{
2495    int nd;
2496
2497    if (spckpat && pattry(spckpat, hn->nam))
2498	return;
2499
2500    nd = spdist(hn->nam, guess, (int) strlen(guess) / 4 + 1);
2501    if (nd <= d) {
2502	best = hn->nam;
2503	d = nd;
2504    }
2505}
2506
2507/* spellcheck a word */
2508/* fix s ; if hist is nonzero, fix the history list too */
2509
2510/**/
2511mod_export void
2512spckword(char **s, int hist, int cmd, int ask)
2513{
2514    char *t, *correct_ignore;
2515    int x;
2516    char ic = '\0';
2517    int ne;
2518    int preflen = 0;
2519    int autocd = cmd && isset(AUTOCD) && strcmp(*s, ".") && strcmp(*s, "..");
2520
2521    if ((histdone & HISTFLAG_NOEXEC) || **s == '-' || **s == '%')
2522	return;
2523    if (!strcmp(*s, "in"))
2524	return;
2525    if (!(*s)[0] || !(*s)[1])
2526	return;
2527    if (cmd) {
2528	if (shfunctab->getnode(shfunctab, *s) ||
2529	    builtintab->getnode(builtintab, *s) ||
2530	    cmdnamtab->getnode(cmdnamtab, *s) ||
2531	    aliastab->getnode(aliastab, *s)  ||
2532	    reswdtab->getnode(reswdtab, *s))
2533	    return;
2534	else if (isset(HASHLISTALL)) {
2535	    cmdnamtab->filltable(cmdnamtab);
2536	    if (cmdnamtab->getnode(cmdnamtab, *s))
2537		return;
2538	}
2539    }
2540    t = *s;
2541    if (*t == Tilde || *t == Equals || *t == String)
2542	t++;
2543    for (; *t; t++)
2544	if (itok(*t))
2545	    return;
2546    best = NULL;
2547    for (t = *s; *t; t++)
2548	if (*t == '/')
2549	    break;
2550    if (**s == Tilde && !*t)
2551	return;
2552
2553    if ((correct_ignore = getsparam("CORRECT_IGNORE")) != NULL) {
2554	tokenize(correct_ignore = dupstring(correct_ignore));
2555	remnulargs(correct_ignore);
2556	spckpat = patcompile(correct_ignore, 0, NULL);
2557    } else
2558	spckpat = NULL;
2559
2560    if (**s == String && !*t) {
2561	guess = *s + 1;
2562	if (itype_end(guess, IIDENT, 1) == guess)
2563	    return;
2564	ic = String;
2565	d = 100;
2566	scanhashtable(paramtab, 1, 0, 0, spscan, 0);
2567    } else if (**s == Equals) {
2568	if (*t)
2569	    return;
2570	if (hashcmd(guess = *s + 1, pathchecked))
2571	    return;
2572	d = 100;
2573	ic = Equals;
2574	scanhashtable(aliastab, 1, 0, 0, spscan, 0);
2575	scanhashtable(cmdnamtab, 1, 0, 0, spscan, 0);
2576    } else {
2577	guess = *s;
2578	if (*guess == Tilde || *guess == String) {
2579	    ic = *guess;
2580	    if (!*++t)
2581		return;
2582	    guess = dupstring(guess);
2583	    ne = noerrs;
2584	    noerrs = 2;
2585	    singsub(&guess);
2586	    noerrs = ne;
2587	    if (!guess)
2588		return;
2589	    preflen = strlen(guess) - strlen(t);
2590	}
2591	if (access(unmeta(guess), F_OK) == 0)
2592	    return;
2593	best = spname(guess);
2594	if (!*t && cmd) {
2595	    if (hashcmd(guess, pathchecked))
2596		return;
2597	    d = 100;
2598	    scanhashtable(reswdtab, 1, 0, 0, spscan, 0);
2599	    scanhashtable(aliastab, 1, 0, 0, spscan, 0);
2600	    scanhashtable(shfunctab, 1, 0, 0, spscan, 0);
2601	    scanhashtable(builtintab, 1, 0, 0, spscan, 0);
2602	    scanhashtable(cmdnamtab, 1, 0, 0, spscan, 0);
2603	    if (autocd) {
2604		char **pp;
2605		for (pp = cdpath; *pp; pp++) {
2606		    char bestcd[PATH_MAX + 1];
2607		    int thisdist;
2608		    /* Less than d here, instead of less than or equal  *
2609		     * as used in spscan(), so that an autocd is chosen *
2610		     * only when it is better than anything so far, and *
2611		     * so we prefer directories earlier in the cdpath.  */
2612		    if ((thisdist = mindist(*pp, *s, bestcd)) < d) {
2613			best = dupstring(bestcd);
2614			d = thisdist;
2615		    }
2616		}
2617	    }
2618	}
2619    }
2620    if (errflag)
2621	return;
2622    if (best && (int)strlen(best) > 1 && strcmp(best, guess)) {
2623	if (ic) {
2624	    char *u;
2625	    if (preflen) {
2626		/* do not correct the result of an expansion */
2627		if (strncmp(guess, best, preflen))
2628		    return;
2629		/* replace the temporarily expanded prefix with the original */
2630		u = (char *) hcalloc(t - *s + strlen(best + preflen) + 1);
2631		strncpy(u, *s, t - *s);
2632		strcpy(u + (t - *s), best + preflen);
2633	    } else {
2634		u = (char *) hcalloc(strlen(best) + 2);
2635		strcpy(u + 1, best);
2636	    }
2637	    best = u;
2638	    guess = *s;
2639	    *guess = *best = ztokens[ic - Pound];
2640	}
2641	if (ask) {
2642	    if (noquery(0)) {
2643		x = 'n';
2644	    } else if (shout) {
2645		char *pptbuf;
2646		pptbuf = promptexpand(sprompt, 0, best, guess, NULL);
2647		zputs(pptbuf, shout);
2648		free(pptbuf);
2649		fflush(shout);
2650		zbeep();
2651		x = getquery("nyae \t", 0);
2652		if (cmd && x == 'n')
2653		    pathchecked = path;
2654	    } else
2655		x = 'n';
2656	} else
2657	    x = 'y';
2658	if (x == 'y' || x == ' ' || x == '\t') {
2659	    *s = dupstring(best);
2660	    if (hist)
2661		hwrep(best);
2662	} else if (x == 'a') {
2663	    histdone |= HISTFLAG_NOEXEC;
2664	} else if (x == 'e') {
2665	    histdone |= HISTFLAG_NOEXEC | HISTFLAG_RECALL;
2666	}
2667	if (ic)
2668	    **s = ic;
2669    }
2670}
2671
2672/*
2673 * Helper for ztrftime.  Called with a pointer to the length left
2674 * in the buffer, and a new string length to decrement from that.
2675 * Returns 0 if the new length fits, 1 otherwise.  We assume a terminating
2676 * NUL and return 1 if that doesn't fit.
2677 */
2678
2679static int
2680ztrftimebuf(int *bufsizeptr, int decr)
2681{
2682    if (*bufsizeptr <= decr)
2683	return 1;
2684    *bufsizeptr -= decr;
2685    return 0;
2686}
2687
2688/*
2689 * Like the system function, this returns the number of characters
2690 * copied, not including the terminating NUL.  This may be zero
2691 * if the string didn't fit.
2692 *
2693 * As an extension, try to detect an error in strftime --- typically
2694 * not enough memory --- and return -1.  Not guaranteed to be portable,
2695 * since the strftime() interface doesn't make any guarantees about
2696 * the state of the buffer if it returns zero.
2697 */
2698
2699/**/
2700mod_export int
2701ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm)
2702{
2703    int hr12, decr;
2704#ifndef HAVE_STRFTIME
2705    static char *astr[] =
2706    {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
2707    static char *estr[] =
2708    {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
2709     "Aug", "Sep", "Oct", "Nov", "Dec"};
2710#endif
2711    char *origbuf = buf;
2712    char tmp[4];
2713
2714
2715    while (*fmt)
2716	if (*fmt == '%') {
2717	    int strip;
2718
2719	    fmt++;
2720	    if (*fmt == '-') {
2721		strip = 1;
2722		fmt++;
2723	    } else
2724		strip = 0;
2725	    /*
2726	     * Assume this format will take up at least two
2727	     * characters.  Not always true, but if that matters
2728	     * we are so close to the edge it's not a big deal.
2729	     * Fix up some longer cases specially when we get to them.
2730	     */
2731	    if (ztrftimebuf(&bufsize, 2))
2732		return -1;
2733	    switch (*fmt++) {
2734	    case 'd':
2735		if (tm->tm_mday > 9 || !strip)
2736		    *buf++ = '0' + tm->tm_mday / 10;
2737		*buf++ = '0' + tm->tm_mday % 10;
2738		break;
2739	    case 'f':
2740		strip = 1;
2741		/* FALLTHROUGH */
2742	    case 'e':
2743		if (tm->tm_mday > 9)
2744		    *buf++ = '0' + tm->tm_mday / 10;
2745		else if (!strip)
2746		    *buf++ = ' ';
2747		*buf++ = '0' + tm->tm_mday % 10;
2748		break;
2749	    case 'K':
2750		strip = 1;
2751		/* FALLTHROUGH */
2752	    case 'H':
2753	    case 'k':
2754		if (tm->tm_hour > 9)
2755		    *buf++ = '0' + tm->tm_hour / 10;
2756		else if (!strip) {
2757		    if (fmt[-1] == 'H')
2758			*buf++ = '0';
2759		    else
2760			*buf++ = ' ';
2761		}
2762		*buf++ = '0' + tm->tm_hour % 10;
2763		break;
2764	    case 'L':
2765		strip = 1;
2766		/* FALLTHROUGH */
2767	    case 'l':
2768		hr12 = tm->tm_hour % 12;
2769		if (hr12 == 0)
2770		    hr12 = 12;
2771	        if (hr12 > 9)
2772		    *buf++ = '1';
2773		else if (!strip)
2774		    *buf++ = ' ';
2775
2776		*buf++ = '0' + (hr12 % 10);
2777		break;
2778	    case 'm':
2779		if (tm->tm_mon > 8 || !strip)
2780		    *buf++ = '0' + (tm->tm_mon + 1) / 10;
2781		*buf++ = '0' + (tm->tm_mon + 1) % 10;
2782		break;
2783	    case 'M':
2784		if (tm->tm_min > 9 || !strip)
2785		    *buf++ = '0' + tm->tm_min / 10;
2786		*buf++ = '0' + tm->tm_min % 10;
2787		break;
2788	    case 'S':
2789		if (tm->tm_sec > 9 || !strip)
2790		    *buf++ = '0' + tm->tm_sec / 10;
2791		*buf++ = '0' + tm->tm_sec % 10;
2792		break;
2793	    case 'y':
2794		if (tm->tm_year > 9 || !strip)
2795		    *buf++ = '0' + (tm->tm_year / 10) % 10;
2796		*buf++ = '0' + tm->tm_year % 10;
2797		break;
2798	    case '\0':
2799		/* Guard against premature end of string */
2800		*buf++ = '%';
2801		fmt--;
2802		break;
2803#ifndef HAVE_STRFTIME
2804	    case 'Y':
2805	    {
2806		/*
2807		 * Not worth handling this natively if
2808		 * strftime has it.
2809		 */
2810		int year, digits, testyear;
2811		year = tm->tm_year + 1900;
2812		digits = 1;
2813		testyear = year;
2814		while (testyear > 9) {
2815		    digits++;
2816		    testyear /= 10;
2817		}
2818		if (ztrftimebuf(&bufsize, digits))
2819		    return -1;
2820		sprintf(buf, "%d", year);
2821		buf += digits;
2822		break;
2823	    }
2824	    case 'a':
2825		if (ztrftimebuf(&bufsize, strlen(astr[tm->tm_wday]) - 2))
2826		    return -1;
2827		strucpy(&buf, astr[tm->tm_wday]);
2828		break;
2829	    case 'b':
2830		if (ztrftimebuf(&bufsize, strlen(estr[tm->tm_mon]) - 2))
2831		    return -1;
2832		strucpy(&buf, estr[tm->tm_mon]);
2833		break;
2834	    case 'p':
2835		*buf++ = (tm->tm_hour > 11) ? 'p' : 'a';
2836		*buf++ = 'm';
2837		break;
2838	    default:
2839		*buf++ = '%';
2840		if (fmt[-1] != '%')
2841		    *buf++ = fmt[-1];
2842#else
2843	    default:
2844		/*
2845		 * Remember we've already allowed for two characters
2846		 * in the accounting in bufsize (but nowhere else).
2847		 */
2848		*buf = '\1';
2849		sprintf(tmp, strip ? "%%-%c" : "%%%c", fmt[-1]);
2850		if (!strftime(buf, bufsize + 2, tmp, tm))
2851		{
2852		    if (*buf) {
2853			buf[0] = '\0';
2854			return -1;
2855		    }
2856		    return 0;
2857		}
2858		decr = strlen(buf);
2859		buf += decr;
2860		bufsize -= decr - 2;
2861#endif
2862		break;
2863	    }
2864	} else {
2865	    if (ztrftimebuf(&bufsize, 1))
2866		return -1;
2867	    *buf++ = *fmt++;
2868	}
2869    *buf = '\0';
2870    return buf - origbuf;
2871}
2872
2873/**/
2874mod_export char *
2875zjoin(char **arr, int delim, int heap)
2876{
2877    int len = 0;
2878    char **s, *ret, *ptr;
2879
2880    for (s = arr; *s; s++)
2881	len += strlen(*s) + 1 + (imeta(delim) ? 1 : 0);
2882    if (!len)
2883	return heap? "" : ztrdup("");
2884    ptr = ret = (heap ? (char *) hcalloc(len) : (char *) zshcalloc(len));
2885    for (s = arr; *s; s++) {
2886	strucpy(&ptr, *s);
2887	    if (imeta(delim)) {
2888		*ptr++ = Meta;
2889		*ptr++ = delim ^ 32;
2890	    }
2891	    else
2892		*ptr++ = delim;
2893    }
2894    ptr[-1 - (imeta(delim) ? 1 : 0)] = '\0';
2895    return ret;
2896}
2897
2898/* Split a string containing a colon separated list *
2899 * of items into an array of strings.               */
2900
2901/**/
2902mod_export char **
2903colonsplit(char *s, int uniq)
2904{
2905    int ct;
2906    char *t, **ret, **ptr, **p;
2907
2908    for (t = s, ct = 0; *t; t++) /* count number of colons */
2909	if (*t == ':')
2910	    ct++;
2911    ptr = ret = (char **) zalloc(sizeof(char **) * (ct + 2));
2912
2913    t = s;
2914    do {
2915	s = t;
2916        /* move t to point at next colon */
2917	for (; *t && *t != ':'; t++);
2918	if (uniq)
2919	    for (p = ret; p < ptr; p++)
2920		if ((int)strlen(*p) == t - s && ! strncmp(*p, s, t - s))
2921		    goto cont;
2922	*ptr = (char *) zalloc((t - s) + 1);
2923	ztrncpy(*ptr++, s, t - s);
2924      cont: ;
2925    }
2926    while (*t++);
2927    *ptr = NULL;
2928    return ret;
2929}
2930
2931/**/
2932static int
2933skipwsep(char **s)
2934{
2935    char *t = *s;
2936    int i = 0;
2937
2938    /*
2939     * Don't need to handle mutlibyte characters, they can't
2940     * be IWSEP.  Do need to check for metafication.
2941     */
2942    while (*t && iwsep(*t == Meta ? t[1] ^ 32 : *t)) {
2943	if (*t == Meta)
2944	    t++;
2945	t++;
2946	i++;
2947    }
2948    *s = t;
2949    return i;
2950}
2951
2952/*
2953 * haven't worked out what allownull does; it's passed down from
2954 *   sepsplit but all the cases it's used are either 0 or 1 without
2955 *   a comment.  it seems to be something to do with the `nulstring'
2956 *   which i think is some kind of a metafication thing, so probably
2957 *   allownull's value is associated with whether we are using
2958 *   metafied strings.
2959 * see findsep() below for handling of `quote' argument
2960 */
2961
2962/**/
2963mod_export char **
2964spacesplit(char *s, int allownull, int heap, int quote)
2965{
2966    char *t, **ret, **ptr;
2967    int l = sizeof(*ret) * (wordcount(s, NULL, -!allownull) + 1);
2968    char *(*dup)(const char *) = (heap ? dupstring : ztrdup);
2969
2970    ptr = ret = (heap ? (char **) hcalloc(l) : (char **) zshcalloc(l));
2971
2972    if (quote) {
2973	/*
2974	 * we will be stripping quoted separators by hacking string,
2975	 * so make sure it's hackable.
2976	 */
2977	s = dupstring(s);
2978    }
2979
2980    t = s;
2981    skipwsep(&s);
2982    MB_METACHARINIT();
2983    if (*s && itype_end(s, ISEP, 1) != s)
2984	*ptr++ = dup(allownull ? "" : nulstring);
2985    else if (!allownull && t != s)
2986	*ptr++ = dup("");
2987    while (*s) {
2988	char *iend = itype_end(s, ISEP, 1);
2989	if (iend != s) {
2990	    s = iend;
2991	    skipwsep(&s);
2992	}
2993	else if (quote && *s == '\\') {
2994	    s++;
2995	    skipwsep(&s);
2996	}
2997	t = s;
2998	(void)findsep(&s, NULL, quote);
2999	if (s > t || allownull) {
3000	    *ptr = (heap ? (char *) hcalloc((s - t) + 1) :
3001		    (char *) zshcalloc((s - t) + 1));
3002	    ztrncpy(*ptr++, t, s - t);
3003	} else
3004	    *ptr++ = dup(nulstring);
3005	t = s;
3006	skipwsep(&s);
3007    }
3008    if (!allownull && t != s)
3009	*ptr++ = dup("");
3010    *ptr = NULL;
3011    return ret;
3012}
3013
3014/*
3015 * Find a separator.  Return 0 if already at separator, 1 if separator
3016 * found later, else -1.  (Historical note: used to return length into
3017 * string but this is all that is necessary and is less ambiguous with
3018 * multibyte characters around.)
3019 *
3020 * *s is the string we are looking along, which will be updated
3021 * to the point we have got to.
3022 *
3023 * sep is a possibly multicharacter separator to look for.  If NULL,
3024 * use normal separator characters.  If *sep is NULL, split on individual
3025 * characters.
3026 *
3027 * quote is a flag that '\<sep>' should not be treated as a separator.
3028 * in this case we need to be able to strip the backslash directly
3029 * in the string, so the calling function must have sent us something
3030 * modifiable.  currently this only works for sep == NULL.  also in
3031 * in this case only, we need to turn \\ into \.
3032 */
3033
3034/**/
3035static int
3036findsep(char **s, char *sep, int quote)
3037{
3038    /*
3039     */
3040    int i, ilen;
3041    char *t, *tt;
3042    convchar_t c;
3043
3044    MB_METACHARINIT();
3045    if (!sep) {
3046	for (t = *s; *t; t += ilen) {
3047	    if (quote && *t == '\\') {
3048		if (t[1] == '\\') {
3049		    chuck(t);
3050		    ilen = 1;
3051		    continue;
3052		} else {
3053		    ilen = MB_METACHARLENCONV(t+1, &c);
3054		    if (WC_ZISTYPE(c, ISEP)) {
3055			chuck(t);
3056			/* then advance over new character, length ilen */
3057		    } else {
3058			/* treat *t (backslash) as normal byte */
3059			if (isep(*t))
3060			    break;
3061			ilen = 1;
3062		    }
3063		}
3064	    } else {
3065		ilen = MB_METACHARLENCONV(t, &c);
3066		if (WC_ZISTYPE(c, ISEP))
3067		    break;
3068	    }
3069	}
3070	i = (t > *s);
3071	*s = t;
3072	return i;
3073    }
3074    if (!sep[0]) {
3075	/*
3076	 * NULL separator just means advance past first character,
3077	 * if any.
3078	 */
3079	if (**s) {
3080	    *s += MB_METACHARLEN(*s);
3081	    return 1;
3082	}
3083	return -1;
3084    }
3085    for (i = 0; **s; i++) {
3086	/*
3087	 * The following works for multibyte characters by virtue of
3088	 * the fact that sep may be a string (and we don't care how
3089	 * it divides up, we need to match all of it).
3090	 */
3091	for (t = sep, tt = *s; *t && *tt && *t == *tt; t++, tt++);
3092	if (!*t)
3093	    return (i > 0);
3094	*s += MB_METACHARLEN(*s);
3095    }
3096    return -1;
3097}
3098
3099/**/
3100char *
3101findword(char **s, char *sep)
3102{
3103    char *r, *t;
3104    int sl;
3105
3106    if (!**s)
3107	return NULL;
3108
3109    if (sep) {
3110	sl = strlen(sep);
3111	r = *s;
3112	while (! findsep(s, sep, 0)) {
3113	    r = *s += sl;
3114	}
3115	return r;
3116    }
3117    MB_METACHARINIT();
3118    for (t = *s; *t; t += sl) {
3119	convchar_t c;
3120	sl = MB_METACHARLENCONV(t, &c);
3121	if (!WC_ZISTYPE(c, ISEP))
3122	    break;
3123    }
3124    *s = t;
3125    (void)findsep(s, sep, 0);
3126    return t;
3127}
3128
3129/**/
3130int
3131wordcount(char *s, char *sep, int mul)
3132{
3133    int r, sl, c;
3134
3135    if (sep) {
3136	r = 1;
3137	sl = strlen(sep);
3138	for (; (c = findsep(&s, sep, 0)) >= 0; s += sl)
3139	    if ((c || mul) && (sl || *(s + sl)))
3140		r++;
3141    } else {
3142	char *t = s;
3143
3144	r = 0;
3145	if (mul <= 0)
3146	    skipwsep(&s);
3147	if ((*s && itype_end(s, ISEP, 1) != s) ||
3148	    (mul < 0 && t != s))
3149	    r++;
3150	for (; *s; r++) {
3151	    char *ie = itype_end(s, ISEP, 1);
3152	    if (ie != s) {
3153		s = ie;
3154		if (mul <= 0)
3155		    skipwsep(&s);
3156	    }
3157	    (void)findsep(&s, NULL, 0);
3158	    t = s;
3159	    if (mul <= 0)
3160		skipwsep(&s);
3161	}
3162	if (mul < 0 && t != s)
3163	    r++;
3164    }
3165    return r;
3166}
3167
3168/**/
3169mod_export char *
3170sepjoin(char **s, char *sep, int heap)
3171{
3172    char *r, *p, **t;
3173    int l, sl;
3174    char sepbuf[2];
3175
3176    if (!*s)
3177	return heap ? "" : ztrdup("");
3178    if (!sep) {
3179	/* optimise common case that ifs[0] is space */
3180	if (ifs && *ifs != ' ') {
3181	    MB_METACHARINIT();
3182	    sep = dupstrpfx(ifs, MB_METACHARLEN(ifs));
3183	} else {
3184	    p = sep = sepbuf;
3185	    *p++ = ' ';
3186	    *p = '\0';
3187	}
3188    }
3189    sl = strlen(sep);
3190    for (t = s, l = 1 - sl; *t; l += strlen(*t) + sl, t++);
3191    r = p = (heap ? (char *) hcalloc(l) : (char *) zshcalloc(l));
3192    t = s;
3193    while (*t) {
3194	strucpy(&p, *t);
3195	if (*++t)
3196	    strucpy(&p, sep);
3197    }
3198    *p = '\0';
3199    return r;
3200}
3201
3202/**/
3203char **
3204sepsplit(char *s, char *sep, int allownull, int heap)
3205{
3206    int n, sl;
3207    char *t, *tt, **r, **p;
3208
3209    /* Null string?  Treat as empty string. */
3210    if (s[0] == Nularg && !s[1])
3211	s++;
3212
3213    if (!sep)
3214	return spacesplit(s, allownull, heap, 0);
3215
3216    sl = strlen(sep);
3217    n = wordcount(s, sep, 1);
3218    r = p = (heap ? (char **) hcalloc((n + 1) * sizeof(char *)) :
3219	     (char **) zshcalloc((n + 1) * sizeof(char *)));
3220
3221    for (t = s; n--;) {
3222	tt = t;
3223	(void)findsep(&t, sep, 0);
3224	*p = (heap ? (char *) hcalloc(t - tt + 1) :
3225	      (char *) zshcalloc(t - tt + 1));
3226	strncpy(*p, tt, t - tt);
3227	(*p)[t - tt] = '\0';
3228	p++;
3229	t += sl;
3230    }
3231    *p = NULL;
3232
3233    return r;
3234}
3235
3236/* Get the definition of a shell function */
3237
3238/**/
3239mod_export Shfunc
3240getshfunc(char *nam)
3241{
3242    return (Shfunc) shfunctab->getnode(shfunctab, nam);
3243}
3244
3245/*
3246 * Call the function func to substitute string orig by setting
3247 * the parameter reply.
3248 * Return the array from reply, or NULL if the function returned
3249 * non-zero status.
3250 * The returned value comes directly from the parameter and
3251 * so should be used before there is any chance of that
3252 * being changed or unset.
3253 * If arg1 is not NULL, it is used as an initial argument to
3254 * the function, with the original string as the second argument.
3255 */
3256
3257/**/
3258char **
3259subst_string_by_func(Shfunc func, char *arg1, char *orig)
3260{
3261    int osc = sfcontext, osm = stopmsg, old_incompfunc = incompfunc;
3262    LinkList l = newlinklist();
3263    char **ret;
3264
3265    addlinknode(l, func->node.nam);
3266    if (arg1)
3267	addlinknode(l, arg1);
3268    addlinknode(l, orig);
3269    sfcontext = SFC_SUBST;
3270    incompfunc = 0;
3271
3272    if (doshfunc(func, l, 1))
3273	ret = NULL;
3274    else
3275	ret = getaparam("reply");
3276
3277    sfcontext = osc;
3278    stopmsg = osm;
3279    incompfunc = old_incompfunc;
3280    return ret;
3281}
3282
3283/**
3284 * Front end to subst_string_by_func to use hook-like logic.
3285 * name can refer to a function, and name + "_hook" can refer
3286 * to an array containing a list of functions.  The functions
3287 * are tried in order until one returns success.
3288 */
3289/**/
3290char **
3291subst_string_by_hook(char *name, char *arg1, char *orig)
3292{
3293    Shfunc func;
3294    char **ret = NULL;
3295
3296    if ((func = getshfunc(name))) {
3297	ret = subst_string_by_func(func, arg1, orig);
3298    }
3299
3300    if (!ret) {
3301	char **arrptr;
3302	int namlen = strlen(name);
3303	VARARR(char, arrnam, namlen + HOOK_SUFFIX_LEN);
3304	memcpy(arrnam, name, namlen);
3305	memcpy(arrnam + namlen, HOOK_SUFFIX, HOOK_SUFFIX_LEN);
3306
3307	if ((arrptr = getaparam(arrnam))) {
3308	    /* Guard against internal modification of the array */
3309	    arrptr = arrdup(arrptr);
3310	    for (; *arrptr; arrptr++) {
3311		if ((func = getshfunc(*arrptr))) {
3312		    ret = subst_string_by_func(func, arg1, orig);
3313		    if (ret)
3314			break;
3315		}
3316	    }
3317	}
3318    }
3319
3320    return ret;
3321}
3322
3323/**/
3324mod_export char **
3325mkarray(char *s)
3326{
3327    char **t = (char **) zalloc((s) ? (2 * sizeof s) : (sizeof s));
3328
3329    if ((*t = s))
3330	t[1] = NULL;
3331    return t;
3332}
3333
3334/**/
3335mod_export void
3336zbeep(void)
3337{
3338    char *vb;
3339    queue_signals();
3340    if ((vb = getsparam("ZBEEP"))) {
3341	int len;
3342	vb = getkeystring(vb, &len, GETKEYS_BINDKEY, NULL);
3343	write_loop(SHTTY, vb, len);
3344    } else if (isset(BEEP))
3345	write_loop(SHTTY, "\07", 1);
3346    unqueue_signals();
3347}
3348
3349/**/
3350mod_export void
3351freearray(char **s)
3352{
3353    char **t = s;
3354
3355    DPUTS(!s, "freearray() with zero argument");
3356
3357    while (*s)
3358	zsfree(*s++);
3359    free(t);
3360}
3361
3362/**/
3363int
3364equalsplit(char *s, char **t)
3365{
3366    for (; *s && *s != '='; s++);
3367    if (*s == '=') {
3368	*s++ = '\0';
3369	*t = s;
3370	return 1;
3371    }
3372    return 0;
3373}
3374
3375static int specialcomma;
3376
3377/* the ztypes table */
3378
3379/**/
3380mod_export short int typtab[256];
3381
3382/* initialize the ztypes table */
3383
3384/**/
3385void
3386inittyptab(void)
3387{
3388    int t0;
3389    char *s;
3390
3391    for (t0 = 0; t0 != 256; t0++)
3392	typtab[t0] = 0;
3393    for (t0 = 0; t0 != 32; t0++)
3394	typtab[t0] = typtab[t0 + 128] = ICNTRL;
3395    typtab[127] = ICNTRL;
3396    for (t0 = '0'; t0 <= '9'; t0++)
3397	typtab[t0] = IDIGIT | IALNUM | IWORD | IIDENT | IUSER;
3398    for (t0 = 'a'; t0 <= 'z'; t0++)
3399	typtab[t0] = typtab[t0 - 'a' + 'A'] = IALPHA | IALNUM | IIDENT | IUSER | IWORD;
3400#ifndef MULTIBYTE_SUPPORT
3401    /*
3402     * This really doesn't seem to me the right thing to do when
3403     * we have multibyte character support...  it was a hack to assume
3404     * eight bit characters `worked' for some values of work before
3405     * we could test for them properly.  I'm not 100% convinced
3406     * having IIDENT here is a good idea at all, but this code
3407     * should disappear into history...
3408     */
3409    for (t0 = 0240; t0 != 0400; t0++)
3410	typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD;
3411#endif
3412    /* typtab['.'] |= IIDENT; */ /* Allow '.' in variable names - broken */
3413    typtab['_'] = IIDENT | IUSER;
3414    typtab['-'] = typtab['.'] = IUSER;
3415    typtab[' '] |= IBLANK | INBLANK;
3416    typtab['\t'] |= IBLANK | INBLANK;
3417    typtab['\n'] |= INBLANK;
3418    typtab['\0'] |= IMETA;
3419    typtab[STOUC(Meta)  ] |= IMETA;
3420    typtab[STOUC(Marker)] |= IMETA;
3421    for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(Comma); t0++)
3422	typtab[t0] |= ITOK | IMETA;
3423    for (t0 = (int)STOUC(Snull); t0 <= (int)STOUC(Nularg); t0++)
3424	typtab[t0] |= ITOK | IMETA | INULL;
3425    for (s = ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ?
3426	ztrdup(DEFAULT_IFS_SH) : ztrdup(DEFAULT_IFS); *s; s++) {
3427	int c = STOUC(*s == Meta ? *++s ^ 32 : *s);
3428#ifdef MULTIBYTE_SUPPORT
3429	if (!isascii(c)) {
3430	    /* see comment for wordchars below */
3431	    continue;
3432	}
3433#endif
3434	if (inblank(c)) {
3435	    if (s[1] == c)
3436		s++;
3437	    else
3438		typtab[c] |= IWSEP;
3439	}
3440	typtab[c] |= ISEP;
3441    }
3442    for (s = wordchars ? wordchars : DEFAULT_WORDCHARS; *s; s++) {
3443	int c = STOUC(*s == Meta ? *++s ^ 32 : *s);
3444#ifdef MULTIBYTE_SUPPORT
3445	if (!isascii(c)) {
3446	    /*
3447	     * If we have support for multibyte characters, we don't
3448	     * handle non-ASCII characters here; instead, we turn
3449	     * wordchars into a wide character array.
3450	     * (We may actually have a single-byte 8-bit character set,
3451	     * but it works the same way.)
3452	     */
3453	    continue;
3454	}
3455#endif
3456	typtab[c] |= IWORD;
3457    }
3458#ifdef MULTIBYTE_SUPPORT
3459    set_widearray(wordchars, &wordchars_wide);
3460    set_widearray(ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ?
3461	ztrdup(DEFAULT_IFS_SH) : ztrdup(DEFAULT_IFS), &ifs_wide);
3462#endif
3463    for (s = SPECCHARS; *s; s++)
3464	typtab[STOUC(*s)] |= ISPECIAL;
3465    if (specialcomma)
3466	typtab[STOUC(',')] |= ISPECIAL;
3467    if (isset(BANGHIST) && bangchar && interact && isset(SHINSTDIN))
3468	typtab[bangchar] |= ISPECIAL;
3469}
3470
3471/**/
3472mod_export void
3473makecommaspecial(int yesno)
3474{
3475    if ((specialcomma = yesno) != 0)
3476	typtab[STOUC(',')] |= ISPECIAL;
3477    else
3478	typtab[STOUC(',')] &= ~ISPECIAL;
3479}
3480
3481
3482/**/
3483#ifdef MULTIBYTE_SUPPORT
3484/* A wide-character version of the iblank() macro. */
3485/**/
3486mod_export int
3487wcsiblank(wint_t wc)
3488{
3489    if (iswspace(wc) && wc != L'\n')
3490	return 1;
3491    return 0;
3492}
3493
3494/*
3495 * zistype macro extended to support wide characters.
3496 * Works for IIDENT, IWORD, IALNUM, ISEP.
3497 * We don't need this for IWSEP because that only applies to
3498 * a fixed set of ASCII characters.
3499 * Note here that use of multibyte mode is not tested:
3500 * that's because for ZLE this is unconditional,
3501 * not dependent on the option.  The caller must decide.
3502 */
3503
3504/**/
3505mod_export int
3506wcsitype(wchar_t c, int itype)
3507{
3508    int len;
3509    mbstate_t mbs;
3510    VARARR(char, outstr, MB_CUR_MAX);
3511
3512    if (!isset(MULTIBYTE))
3513	return zistype(c, itype);
3514
3515    /*
3516     * Strategy:  the shell requires that the multibyte representation
3517     * be an extension of ASCII.  So see if converting the character
3518     * produces an ASCII character.  If it does, use zistype on that.
3519     * If it doesn't, use iswalnum on the original character.
3520     * If that fails, resort to the appropriate wide character array.
3521     */
3522    memset(&mbs, 0, sizeof(mbs));
3523    len = wcrtomb(outstr, c, &mbs);
3524
3525    if (len == 0) {
3526	/* NULL is special */
3527	return zistype(0, itype);
3528    } else if (len == 1 && isascii(outstr[0])) {
3529	return zistype(outstr[0], itype);
3530    } else {
3531	switch (itype) {
3532	case IIDENT:
3533	    if (!isset(POSIXIDENTIFIERS))
3534		return 0;
3535	    return iswalnum(c);
3536
3537	case IWORD:
3538	    if (iswalnum(c))
3539		return 1;
3540	    /*
3541	     * If we are handling combining characters, any punctuation
3542	     * characters with zero width needs to be considered part of
3543	     * a word.  If we are not handling combining characters then
3544	     * logically they are still part of the word, even if they
3545	     * don't get displayed properly, so always do this.
3546	     */
3547	    if (IS_COMBINING(c))
3548		return 1;
3549	    return !!wmemchr(wordchars_wide.chars, c, wordchars_wide.len);
3550
3551	case ISEP:
3552	    return !!wmemchr(ifs_wide.chars, c, ifs_wide.len);
3553
3554	default:
3555	    return iswalnum(c);
3556	}
3557    }
3558}
3559
3560/**/
3561#endif
3562
3563
3564/*
3565 * Find the end of a set of characters in the set specified by itype;
3566 * one of IALNUM, IIDENT, IWORD or IUSER.  For non-ASCII characters, we assume
3567 * alphanumerics are part of the set, with the exception that
3568 * identifiers are not treated that way if POSIXIDENTIFIERS is set.
3569 *
3570 * See notes above for identifiers.
3571 * Returns the same pointer as passed if not on an identifier character.
3572 * If "once" is set, just test the first character, i.e. (outptr !=
3573 * inptr) tests whether the first character is valid in an identifier.
3574 *
3575 * Currently this is only called with itype IIDENT, IUSER or ISEP.
3576 */
3577
3578/**/
3579mod_export char *
3580itype_end(const char *ptr, int itype, int once)
3581{
3582#ifdef MULTIBYTE_SUPPORT
3583    if (isset(MULTIBYTE) &&
3584	(itype != IIDENT || !isset(POSIXIDENTIFIERS))) {
3585	mb_metacharinit();
3586	while (*ptr) {
3587	    wint_t wc;
3588	    int len = mb_metacharlenconv(ptr, &wc);
3589
3590	    if (!len)
3591		break;
3592
3593	    if (wc == WEOF) {
3594		/* invalid, treat as single character */
3595		int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr);
3596		/* in this case non-ASCII characters can't match */
3597		if (chr > 127 || !zistype(chr,itype))
3598		    break;
3599	    } else if (len == 1 && isascii(*ptr)) {
3600		/* ASCII: can't be metafied, use standard test */
3601		if (!zistype(*ptr,itype))
3602		    break;
3603	    } else {
3604		/*
3605		 * Valid non-ASCII character.
3606		 */
3607		switch (itype) {
3608		case IWORD:
3609		    if (!iswalnum(wc) &&
3610			!wmemchr(wordchars_wide.chars, wc,
3611				 wordchars_wide.len))
3612			return (char *)ptr;
3613		    break;
3614
3615		case ISEP:
3616		    if (!wmemchr(ifs_wide.chars, wc, ifs_wide.len))
3617			return (char *)ptr;
3618		    break;
3619
3620		default:
3621		    if (!iswalnum(wc))
3622			return (char *)ptr;
3623		}
3624	    }
3625	    ptr += len;
3626
3627	    if (once)
3628		break;
3629	}
3630    } else
3631#endif
3632	for (;;) {
3633	    int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr);
3634	    if (!zistype(chr,itype))
3635		break;
3636	    ptr += (*ptr == Meta) ? 2 : 1;
3637
3638	    if (once)
3639		break;
3640	}
3641
3642    /*
3643     * Nasty.  The first argument is const char * because we
3644     * don't modify it here.  However, we really want to pass
3645     * back the same type as was passed down, to allow idioms like
3646     *   p = itype_end(p, IIDENT, 0);
3647     * So returning a const char * isn't really the right thing to do.
3648     * Without having two different functions the following seems
3649     * to be the best we can do.
3650     */
3651    return (char *)ptr;
3652}
3653
3654/**/
3655mod_export char **
3656arrdup(char **s)
3657{
3658    char **x, **y;
3659
3660    y = x = (char **) zhalloc(sizeof(char *) * (arrlen(s) + 1));
3661
3662    while ((*x++ = dupstring(*s++)));
3663
3664    return y;
3665}
3666
3667/**/
3668mod_export char **
3669zarrdup(char **s)
3670{
3671    char **x, **y;
3672
3673    y = x = (char **) zalloc(sizeof(char *) * (arrlen(s) + 1));
3674
3675    while ((*x++ = ztrdup(*s++)));
3676
3677    return y;
3678}
3679
3680/**/
3681#ifdef MULTIBYTE_SUPPORT
3682/**/
3683mod_export wchar_t **
3684wcs_zarrdup(wchar_t **s)
3685{
3686    wchar_t **x, **y;
3687
3688    y = x = (wchar_t **) zalloc(sizeof(wchar_t *) * (arrlen((char **)s) + 1));
3689
3690    while ((*x++ = wcs_ztrdup(*s++)));
3691
3692    return y;
3693}
3694/**/
3695#endif /* MULTIBYTE_SUPPORT */
3696
3697/**/
3698static char *
3699spname(char *oldname)
3700{
3701    char *p, spnameguess[PATH_MAX + 1], spnamebest[PATH_MAX + 1];
3702    static char newname[PATH_MAX + 1];
3703    char *new = newname, *old = oldname;
3704    int bestdist = 0, thisdist, thresh, maxthresh = 0;
3705
3706    /* This loop corrects each directory component of the path, stopping *
3707     * when any correction distance would exceed the distance threshold. *
3708     * NULL is returned only if the first component cannot be corrected; *
3709     * otherwise a copy of oldname with a corrected prefix is returned.  *
3710     * Rationale for this, if there ever was any, has been forgotten.    */
3711    for (;;) {
3712	while (*old == '/') {
3713	    if ((new - newname) >= (sizeof(newname)-1))
3714		return NULL;
3715	    *new++ = *old++;
3716	}
3717	*new = '\0';
3718	if (*old == '\0')
3719	    return newname;
3720	p = spnameguess;
3721	for (; *old != '/' && *old != '\0'; old++)
3722	    if (p < spnameguess + PATH_MAX)
3723		*p++ = *old;
3724	*p = '\0';
3725	/* Every component is allowed a single distance 2 correction or two *
3726	 * distance 1 corrections.  Longer ones get additional corrections. */
3727	thresh = (int)(p - spnameguess) / 4 + 1;
3728	if (thresh < 3)
3729	    thresh = 3;
3730	else if (thresh > 100)
3731	    thresh = 100;
3732	if ((thisdist = mindist(newname, spnameguess, spnamebest)) >= thresh) {
3733	    /* The next test is always true, except for the first path    *
3734	     * component.  We could initialize bestdist to some large     *
3735	     * constant instead, and then compare to that constant here,  *
3736	     * because an invariant is that we've never exceeded the      *
3737	     * threshold for any component so far; but I think that looks *
3738	     * odd to the human reader, and we may make use of the total  *
3739	     * distance for all corrections at some point in the future.  */
3740	    if (bestdist < maxthresh) {
3741		strcpy(new, spnameguess);
3742		strcat(new, old);
3743		return newname;
3744	    } else
3745	    	return NULL;
3746	} else {
3747	    maxthresh = bestdist + thresh;
3748	    bestdist += thisdist;
3749	}
3750	for (p = spnamebest; (*new = *p++);)
3751	    new++;
3752    }
3753}
3754
3755/**/
3756static int
3757mindist(char *dir, char *mindistguess, char *mindistbest)
3758{
3759    int mindistd, nd;
3760    DIR *dd;
3761    char *fn;
3762    char *buf;
3763
3764    if (dir[0] == '\0')
3765	dir = ".";
3766    mindistd = 100;
3767
3768    buf = zalloc(strlen(dir) + strlen(mindistguess) + 2);
3769    sprintf(buf, "%s/%s", dir, mindistguess);
3770
3771    if (access(unmeta(buf), F_OK) == 0) {
3772	strcpy(mindistbest, mindistguess);
3773	free(buf);
3774	return 0;
3775    }
3776    free(buf);
3777
3778    if (!(dd = opendir(unmeta(dir))))
3779	return mindistd;
3780    while ((fn = zreaddir(dd, 0))) {
3781	nd = spdist(fn, mindistguess,
3782		    (int)strlen(mindistguess) / 4 + 1);
3783	if (nd <= mindistd) {
3784	    strcpy(mindistbest, fn);
3785	    mindistd = nd;
3786	    if (mindistd == 0)
3787		break;
3788	}
3789    }
3790    closedir(dd);
3791    return mindistd;
3792}
3793
3794/**/
3795static int
3796spdist(char *s, char *t, int thresh)
3797{
3798    /* TODO: Correction for non-ASCII and multibyte-input keyboards. */
3799    char *p, *q;
3800    const char qwertykeymap[] =
3801    "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
3802\t1234567890-=\t\
3803\tqwertyuiop[]\t\
3804\tasdfghjkl;'\n\t\
3805\tzxcvbnm,./\t\t\t\
3806\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
3807\t!@#$%^&*()_+\t\
3808\tQWERTYUIOP{}\t\
3809\tASDFGHJKL:\"\n\t\
3810\tZXCVBNM<>?\n\n\t\
3811\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
3812    const char dvorakkeymap[] =
3813    "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
3814\t1234567890[]\t\
3815\t',.pyfgcrl/=\t\
3816\taoeuidhtns-\n\t\
3817\t;qjkxbmwvz\t\t\t\
3818\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
3819\t!@#$%^&*(){}\t\
3820\t\"<>PYFGCRL?+\t\
3821\tAOEUIDHTNS_\n\t\
3822\t:QJKXBMWVZ\n\n\t\
3823\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
3824    const char *keymap;
3825    if ( isset( DVORAK ) )
3826      keymap = dvorakkeymap;
3827    else
3828      keymap = qwertykeymap;
3829
3830    if (!strcmp(s, t))
3831	return 0;
3832    /* any number of upper/lower mistakes allowed (dist = 1) */
3833    for (p = s, q = t; *p && tulower(*p) == tulower(*q); p++, q++);
3834    if (!*p && !*q)
3835	return 1;
3836    if (!thresh)
3837	return 200;
3838    for (p = s, q = t; *p && *q; p++, q++)
3839	if (*p == *q)
3840	    continue;		/* don't consider "aa" transposed, ash */
3841	else if (p[1] == q[0] && q[1] == p[0])	/* transpositions */
3842	    return spdist(p + 2, q + 2, thresh - 1) + 1;
3843	else if (p[1] == q[0])	/* missing letter */
3844	    return spdist(p + 1, q + 0, thresh - 1) + 2;
3845	else if (p[0] == q[1])	/* missing letter */
3846	    return spdist(p + 0, q + 1, thresh - 1) + 2;
3847	else if (*p != *q)
3848	    break;
3849    if ((!*p && strlen(q) == 1) || (!*q && strlen(p) == 1))
3850	return 2;
3851    for (p = s, q = t; *p && *q; p++, q++)
3852	if (p[0] != q[0] && p[1] == q[1]) {
3853	    int t0;
3854	    char *z;
3855
3856	    /* mistyped letter */
3857
3858	    if (!(z = strchr(keymap, p[0])) || *z == '\n' || *z == '\t')
3859		return spdist(p + 1, q + 1, thresh - 1) + 1;
3860	    t0 = z - keymap;
3861	    if (*q == keymap[t0 - 15] || *q == keymap[t0 - 14] ||
3862		*q == keymap[t0 - 13] ||
3863		*q == keymap[t0 - 1] || *q == keymap[t0 + 1] ||
3864		*q == keymap[t0 + 13] || *q == keymap[t0 + 14] ||
3865		*q == keymap[t0 + 15])
3866		return spdist(p + 1, q + 1, thresh - 1) + 2;
3867	    return 200;
3868	} else if (*p != *q)
3869	    break;
3870    return 200;
3871}
3872
3873/* set cbreak mode, or the equivalent */
3874
3875/**/
3876void
3877setcbreak(void)
3878{
3879    struct ttyinfo ti;
3880
3881    ti = shttyinfo;
3882#ifdef HAS_TIO
3883    ti.tio.c_lflag &= ~ICANON;
3884    ti.tio.c_cc[VMIN] = 1;
3885    ti.tio.c_cc[VTIME] = 0;
3886#else
3887    ti.sgttyb.sg_flags |= CBREAK;
3888#endif
3889    settyinfo(&ti);
3890}
3891
3892/* give the tty to some process */
3893
3894/**/
3895mod_export void
3896attachtty(pid_t pgrp)
3897{
3898    static int ep = 0;
3899
3900    if (jobbing && interact) {
3901#ifdef HAVE_TCSETPGRP
3902	if (SHTTY != -1 && tcsetpgrp(SHTTY, pgrp) == -1 && !ep)
3903#else
3904# if ardent
3905	if (SHTTY != -1 && setpgrp() == -1 && !ep)
3906# else
3907	int arg = pgrp;
3908
3909	if (SHTTY != -1 && ioctl(SHTTY, TIOCSPGRP, &arg) == -1 && !ep)
3910# endif
3911#endif
3912	{
3913	    if (pgrp != mypgrp && kill(-pgrp, 0) == -1)
3914		attachtty(mypgrp);
3915	    else {
3916		if (errno != ENOTTY)
3917		{
3918		    zwarn("can't set tty pgrp: %e", errno);
3919		    fflush(stderr);
3920		}
3921		opts[MONITOR] = 0;
3922		ep = 1;
3923	    }
3924	}
3925    }
3926}
3927
3928/* get the process group associated with the tty */
3929
3930/**/
3931pid_t
3932gettygrp(void)
3933{
3934    pid_t arg;
3935
3936    if (SHTTY == -1)
3937	return -1;
3938
3939#ifdef HAVE_TCSETPGRP
3940    arg = tcgetpgrp(SHTTY);
3941#else
3942    ioctl(SHTTY, TIOCGPGRP, &arg);
3943#endif
3944
3945    return arg;
3946}
3947
3948
3949/* Escape tokens and null characters.  Buf is the string which should be     *
3950 * escaped.  len is the length of the string.  If len is -1, buf should be   *
3951 * null terminated.  If len is non-negative and the third parameter is not   *
3952 * META_DUP, buf should point to an at least len+1 long memory area.  The    *
3953 * return value points to the quoted string.  If the given string does not   *
3954 * contain any special character which should be quoted and the third        *
3955 * parameter is not META_(HEAP|)DUP, buf is returned unchanged (a            *
3956 * terminating null character is appended to buf if necessary).  Otherwise   *
3957 * the third `heap' argument determines the method used to allocate space    *
3958 * for the result.  It can have the following values:                        *
3959 *   META_REALLOC:  use zrealloc on buf                                      *
3960 *   META_HREALLOC: use hrealloc on buf                                      *
3961 *   META_USEHEAP:  get memory from the heap.  This leaves buf unchanged.    *
3962 *   META_NOALLOC:  buf points to a memory area which is long enough to hold *
3963 *                  the quoted form, just quote it and return buf.           *
3964 *   META_STATIC:   store the quoted string in a static area.  The original  *
3965 *                  string should be at most PATH_MAX long.                  *
3966 *   META_ALLOC:    allocate memory for the new string with zalloc().        *
3967 *   META_DUP:      leave buf unchanged and allocate space for the return    *
3968 *                  value even if buf does not contains special characters   *
3969 *   META_HEAPDUP:  same as META_DUP, but uses the heap                      */
3970
3971/**/
3972mod_export char *
3973metafy(char *buf, int len, int heap)
3974{
3975    int meta = 0;
3976    char *t, *p, *e;
3977    static char mbuf[PATH_MAX*2+1];
3978
3979    if (len == -1) {
3980	for (e = buf, len = 0; *e; len++)
3981	    if (imeta(*e++))
3982		meta++;
3983    } else
3984	for (e = buf; e < buf + len;)
3985	    if (imeta(*e++))
3986		meta++;
3987
3988    if (meta || heap == META_DUP || heap == META_HEAPDUP) {
3989	switch (heap) {
3990	case META_REALLOC:
3991	    buf = zrealloc(buf, len + meta + 1);
3992	    break;
3993	case META_HREALLOC:
3994	    buf = hrealloc(buf, len, len + meta + 1);
3995	    break;
3996	case META_ALLOC:
3997	case META_DUP:
3998	    buf = memcpy(zalloc(len + meta + 1), buf, len);
3999	    break;
4000	case META_USEHEAP:
4001	case META_HEAPDUP:
4002	    buf = memcpy(zhalloc(len + meta + 1), buf, len);
4003	    break;
4004	case META_STATIC:
4005#ifdef DEBUG
4006	    if (len > PATH_MAX) {
4007		fprintf(stderr, "BUG: len = %d > PATH_MAX in metafy\n", len);
4008		fflush(stderr);
4009	    }
4010#endif
4011	    buf = memcpy(mbuf, buf, len);
4012	    break;
4013#ifdef DEBUG
4014	case META_NOALLOC:
4015	    break;
4016	default:
4017	    fprintf(stderr, "BUG: metafy called with invalid heap value\n");
4018	    fflush(stderr);
4019	    break;
4020#endif
4021	}
4022	p = buf + len;
4023	e = t = buf + len + meta;
4024	while (meta) {
4025	    if (imeta(*--t = *--p)) {
4026		*t-- ^= 32;
4027		*t = Meta;
4028		meta--;
4029	    }
4030	}
4031    }
4032    *e = '\0';
4033    return buf;
4034}
4035
4036
4037/*
4038 * Duplicate a string, metafying it as we go.
4039 *
4040 * Typically, this is used only for strings imported from outside
4041 * zsh, as strings internally are either already metafied or passed
4042 * around with an associated length.
4043 */
4044/**/
4045mod_export char *
4046ztrdup_metafy(const char *s)
4047{
4048    /* To mimic ztrdup() behaviour */
4049    if (!s)
4050	return NULL;
4051    /*
4052     * metafy() does lots of different things, so the pointer
4053     * isn't const.  Using it with META_DUP should be safe.
4054     */
4055    return metafy((char *)s, -1, META_DUP);
4056}
4057
4058
4059/*
4060 * Take a null-terminated, metafied string in s into a literal
4061 * representation by converting in place.  The length is in *len
4062 * len is non-NULL; if len is NULL, you don't know the length of
4063 * the final string, but if it's to be supplied to some system
4064 * routine that always uses NULL termination, such as a filename
4065 * interpreter, that doesn't matter.  Note the NULL termination
4066 * is always copied for purposes of that kind.
4067 */
4068
4069/**/
4070mod_export char *
4071unmetafy(char *s, int *len)
4072{
4073    char *p, *t;
4074
4075    for (p = s; *p && *p != Meta; p++);
4076    for (t = p; (*t = *p++);)
4077	if (*t++ == Meta)
4078	    t[-1] = *p++ ^ 32;
4079    if (len)
4080	*len = t - s;
4081    return s;
4082}
4083
4084/* Return the character length of a metafied substring, given the      *
4085 * unmetafied substring length.                                        */
4086
4087/**/
4088mod_export int
4089metalen(const char *s, int len)
4090{
4091    int mlen = len;
4092
4093    while (len--) {
4094	if (*s++ == Meta) {
4095	    mlen++;
4096	    s++;
4097	}
4098    }
4099    return mlen;
4100}
4101
4102/*
4103 * This function converts a zsh internal string to a form which can be
4104 * passed to a system call as a filename.  The result is stored in a
4105 * single static area, sized to fit.  If there is no Meta character
4106 * the original string is returned.
4107 */
4108
4109/**/
4110mod_export char *
4111unmeta(const char *file_name)
4112{
4113    static char *fn;
4114    static int sz;
4115    char *p;
4116    const char *t;
4117    int newsz, meta;
4118
4119    meta = 0;
4120    for (t = file_name; *t; t++) {
4121	if (*t == Meta)
4122	    meta = 1;
4123    }
4124    if (!meta) {
4125	/*
4126	 * don't need allocation... free if it's long, see below
4127	 */
4128	if (sz > 4 * PATH_MAX) {
4129	    zfree(fn, sz);
4130	    fn = NULL;
4131	    sz = 0;
4132	}
4133	return (char *) file_name;
4134    }
4135
4136    newsz = (t - file_name) + 1;
4137    /*
4138     * Optimisation: don't resize if we don't have to.
4139     * We need a new allocation if
4140     * - nothing was allocated before
4141     * - the new string is larger than the old one
4142     * - the old string was larger than an arbitrary limit but the
4143     *   new string isn't so that we free up significant space by resizing.
4144     */
4145    if (!fn || newsz > sz || (sz > 4 * PATH_MAX && newsz <= 4 * PATH_MAX))
4146    {
4147	if (fn)
4148	    zfree(fn, sz);
4149	sz = newsz;
4150	fn = (char *)zalloc(sz);
4151	if (!fn) {
4152	    sz = 0;
4153	    /*
4154	     * will quite likely crash in the caller anyway...
4155	     */
4156	    return NULL;
4157	}
4158    }
4159
4160    for (t = file_name, p = fn; *t; p++)
4161	if ((*p = *t++) == Meta)
4162	    *p = *t++ ^ 32;
4163    *p = '\0';
4164    return fn;
4165}
4166
4167/*
4168 * Unmetafy and compare two strings, comparing unsigned character values.
4169 * "a\0" sorts after "a".
4170 *
4171 * Currently this is only used in hash table sorting, where the
4172 * keys are names of hash nodes and where we don't use strcoll();
4173 * it's not clear if that's right but it does guarantee the ordering
4174 * of shell structures on output.
4175 *
4176 * As we don't use strcoll(), it seems overkill to convert multibyte
4177 * characters to wide characters for comparison every time.  In the case
4178 * of UTF-8, Unicode ordering is preserved when sorted raw, and for
4179 * other character sets we rely on an extension of ASCII so the result,
4180 * while it may not be correct, is at least rational.
4181 */
4182
4183/**/
4184int
4185ztrcmp(char const *s1, char const *s2)
4186{
4187    int c1, c2;
4188
4189    while(*s1 && *s1 == *s2) {
4190	s1++;
4191	s2++;
4192    }
4193
4194    if(!(c1 = *s1))
4195	c1 = -1;
4196    else if(c1 == STOUC(Meta))
4197	c1 = *++s1 ^ 32;
4198    if(!(c2 = *s2))
4199	c2 = -1;
4200    else if(c2 == STOUC(Meta))
4201	c2 = *++s2 ^ 32;
4202
4203    if(c1 == c2)
4204	return 0;
4205    else if(c1 < c2)
4206	return -1;
4207    else
4208	return 1;
4209}
4210
4211/* Return the unmetafied length of a metafied string. */
4212
4213/**/
4214mod_export int
4215ztrlen(char const *s)
4216{
4217    int l;
4218
4219    for (l = 0; *s; l++) {
4220	if (*s++ == Meta) {
4221#ifdef DEBUG
4222	    if (! *s)
4223		fprintf(stderr, "BUG: unexpected end of string in ztrlen()\n");
4224	    else
4225#endif
4226	    s++;
4227	}
4228    }
4229    return l;
4230}
4231
4232/* Subtract two pointers in a metafied string. */
4233
4234/**/
4235mod_export int
4236ztrsub(char const *t, char const *s)
4237{
4238    int l = t - s;
4239
4240    while (s != t) {
4241	if (*s++ == Meta) {
4242#ifdef DEBUG
4243	    if (! *s || s == t)
4244		fprintf(stderr, "BUG: substring ends in the middle of a metachar in ztrsub()\n");
4245	    else
4246#endif
4247	    s++;
4248	    l--;
4249	}
4250    }
4251    return l;
4252}
4253
4254/**/
4255mod_export char *
4256zreaddir(DIR *dir, int ignoredots)
4257{
4258    struct dirent *de;
4259
4260    do {
4261	de = readdir(dir);
4262	if(!de)
4263	    return NULL;
4264    } while(ignoredots && de->d_name[0] == '.' &&
4265	(!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])));
4266
4267    return metafy(de->d_name, -1, META_STATIC);
4268}
4269
4270/* Unmetafy and output a string.  Tokens are skipped. */
4271
4272/**/
4273mod_export int
4274zputs(char const *s, FILE *stream)
4275{
4276    int c;
4277
4278    while (*s) {
4279	if (*s == Meta)
4280	    c = *++s ^ 32;
4281	else if(itok(*s)) {
4282	    s++;
4283	    continue;
4284	} else
4285	    c = *s;
4286	s++;
4287	if (fputc(c, stream) < 0)
4288	    return EOF;
4289    }
4290    return 0;
4291}
4292
4293#ifndef MULTIBYTE_SUPPORT
4294/* Create a visibly-represented duplicate of a string. */
4295
4296/**/
4297mod_export char *
4298nicedup(char const *s, int heap)
4299{
4300    int c, len = strlen(s) * 5 + 1;
4301    VARARR(char, buf, len);
4302    char *p = buf, *n;
4303
4304    while ((c = *s++)) {
4305	if (itok(c)) {
4306	    if (c <= Comma)
4307		c = ztokens[c - Pound];
4308	    else
4309		continue;
4310	}
4311	if (c == Meta)
4312	    c = *s++ ^ 32;
4313	/* The result here is metafied */
4314	n = nicechar(c);
4315	while(*n)
4316	    *p++ = *n++;
4317    }
4318    *p = '\0';
4319    return heap ? dupstring(buf) : ztrdup(buf);
4320}
4321#endif
4322
4323/**/
4324mod_export char *
4325nicedupstring(char const *s)
4326{
4327    return nicedup(s, 1);
4328}
4329
4330
4331#ifndef MULTIBYTE_SUPPORT
4332/* Unmetafy and output a string, displaying special characters readably. */
4333
4334/**/
4335mod_export int
4336nicezputs(char const *s, FILE *stream)
4337{
4338    int c;
4339
4340    while ((c = *s++)) {
4341	if (itok(c)) {
4342	    if (c <= Comma)
4343		c = ztokens[c - Pound];
4344	    else
4345		continue;
4346	}
4347	if (c == Meta)
4348	    c = *s++ ^ 32;
4349	if(zputs(nicechar(c), stream) < 0)
4350	    return EOF;
4351    }
4352    return 0;
4353}
4354
4355
4356/* Return the length of the visible representation of a metafied string. */
4357
4358/**/
4359mod_export size_t
4360niceztrlen(char const *s)
4361{
4362    size_t l = 0;
4363    int c;
4364
4365    while ((c = *s++)) {
4366	if (itok(c)) {
4367	    if (c <= Comma)
4368		c = ztokens[c - Pound];
4369	    else
4370		continue;
4371	}
4372	if (c == Meta)
4373	    c = *s++ ^ 32;
4374	l += strlen(nicechar(c));
4375    }
4376    return l;
4377}
4378#endif
4379
4380
4381/**/
4382#ifdef MULTIBYTE_SUPPORT
4383/*
4384 * Version of both nicezputs() and niceztrlen() for use with multibyte
4385 * characters.  Input is a metafied string; output is the screen width of
4386 * the string.
4387 *
4388 * If the FILE * is not NULL, output to that, too.
4389 *
4390 * If outstrp is not NULL, set *outstrp to a zalloc'd version of
4391 * the output (still metafied).
4392 *
4393 * If "heap" is non-zero, use the heap for *outstrp, else zalloc.
4394 */
4395
4396/**/
4397mod_export size_t
4398mb_niceformat(const char *s, FILE *stream, char **outstrp, int heap)
4399{
4400    size_t l = 0, newl;
4401    int umlen, outalloc, outleft, eol = 0;
4402    wchar_t c;
4403    char *ums, *ptr, *fmt, *outstr, *outptr;
4404    mbstate_t mbs;
4405
4406    if (outstrp) {
4407	outleft = outalloc = 5 * strlen(s);
4408	outptr = outstr = zalloc(outalloc);
4409    } else {
4410	outleft = outalloc = 0;
4411	outptr = outstr = NULL;
4412    }
4413
4414    ums = ztrdup(s);
4415    /*
4416     * is this necessary at this point? niceztrlen does this
4417     * but it's used in lots of places.  however, one day this may
4418     * be, too.
4419     */
4420    untokenize(ums);
4421    ptr = unmetafy(ums, &umlen);
4422
4423    memset(&mbs, 0, sizeof mbs);
4424    while (umlen > 0) {
4425	size_t cnt = eol ? MB_INVALID : mbrtowc(&c, ptr, umlen, &mbs);
4426
4427	switch (cnt) {
4428	case MB_INCOMPLETE:
4429	    eol = 1;
4430	    /* FALL THROUGH */
4431	case MB_INVALID:
4432	    /* The byte didn't convert, so output it as a \M-... sequence. */
4433	    fmt = nicechar(*ptr);
4434	    newl = strlen(fmt);
4435	    cnt = 1;
4436	    /* Get mbs out of its undefined state. */
4437	    memset(&mbs, 0, sizeof mbs);
4438	    break;
4439	case 0:
4440	    /* Careful:  converting '\0' returns 0, but a '\0' is a
4441	     * real character for us, so we should consume 1 byte. */
4442	    cnt = 1;
4443	    /* FALL THROUGH */
4444	default:
4445	    fmt = wcs_nicechar(c, &newl, NULL);
4446	    break;
4447	}
4448
4449	umlen -= cnt;
4450	ptr += cnt;
4451	l += newl;
4452
4453	if (stream)
4454	    zputs(fmt, stream);
4455	if (outstr) {
4456	    /* Append to output string */
4457	    int outlen = strlen(fmt);
4458	    if (outlen >= outleft) {
4459		/* Reallocate to twice the length */
4460		int outoffset = outptr - outstr;
4461
4462		outleft += outalloc;
4463		outalloc *= 2;
4464		outstr = zrealloc(outstr, outalloc);
4465		outptr = outstr + outoffset;
4466	    }
4467	    memcpy(outptr, fmt, outlen);
4468	    /* Update start position */
4469	    outptr += outlen;
4470	    /* Update available bytes */
4471	    outleft -= outlen;
4472	}
4473    }
4474
4475    free(ums);
4476    if (outstrp) {
4477	*outptr = '\0';
4478	/* Use more efficient storage for returned string */
4479	*outstrp = heap ? dupstring(outstr) : ztrdup(outstr);
4480	free(outstr);
4481    }
4482
4483    return l;
4484}
4485
4486/* ztrdup multibyte string with nice formatting */
4487
4488/**/
4489mod_export char *
4490nicedup(const char *s, int heap)
4491{
4492    char *retstr;
4493
4494    (void)mb_niceformat(s, NULL, &retstr, heap);
4495
4496    return retstr;
4497}
4498
4499
4500/*
4501 * The guts of mb_metacharlenconv().  This version assumes we are
4502 * processing a true multibyte character string without tokens, and
4503 * takes the shift state as an argument.
4504 */
4505
4506/**/
4507mod_export int
4508mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp)
4509{
4510    size_t ret = MB_INVALID;
4511    char inchar;
4512    const char *ptr;
4513    wchar_t wc;
4514
4515    for (ptr = s; *ptr; ) {
4516	if (*ptr == Meta) {
4517	    inchar = *++ptr ^ 32;
4518	    DPUTS(!*ptr,
4519		  "BUG: unexpected end of string in mb_metacharlen()\n");
4520	} else
4521	    inchar = *ptr;
4522	ptr++;
4523	ret = mbrtowc(&wc, &inchar, 1, mbsp);
4524
4525	if (ret == MB_INVALID)
4526	    break;
4527	if (ret == MB_INCOMPLETE)
4528	    continue;
4529	if (wcp)
4530	    *wcp = wc;
4531	return ptr - s;
4532    }
4533
4534    if (wcp)
4535	*wcp = WEOF;
4536    /* No valid multibyte sequence */
4537    memset(mbsp, 0, sizeof(*mbsp));
4538    if (ptr > s) {
4539	return 1 + (*s == Meta);	/* Treat as single byte character */
4540    } else
4541	return 0;		/* Probably shouldn't happen */
4542}
4543
4544/*
4545 * Length of metafied string s which contains the next multibyte
4546 * character; single (possibly metafied) character if string is not null
4547 * but character is not valid (e.g. possibly incomplete at end of string).
4548 * Returned value is guaranteed not to reach beyond the end of the
4549 * string (assuming correct metafication).
4550 *
4551 * If wcp is not NULL, the converted wide character is stored there.
4552 * If no conversion could be done WEOF is used.
4553 */
4554
4555/**/
4556mod_export int
4557mb_metacharlenconv(const char *s, wint_t *wcp)
4558{
4559    if (!isset(MULTIBYTE)) {
4560	/* treat as single byte, possibly metafied */
4561	if (wcp)
4562	    *wcp = (wint_t)(*s == Meta ? s[1] ^ 32 : *s);
4563	return 1 + (*s == Meta);
4564    }
4565    /*
4566     * We have to handle tokens here, since we may be looking
4567     * through a tokenized input.  Obviously this isn't
4568     * a valid multibyte character, so just return WEOF
4569     * and let the caller handle it as a single character.
4570     *
4571     * TODO: I've a sneaking suspicion we could do more here
4572     * to prevent the caller always needing to handle invalid
4573     * characters specially, but sometimes it may need to know.
4574     */
4575    if (itok(*s)) {
4576	if (wcp)
4577	    *wcp = WEOF;
4578	return 1;
4579    }
4580
4581    return mb_metacharlenconv_r(s, wcp, &mb_shiftstate);
4582}
4583
4584/*
4585 * Total number of multibyte characters in metafied string s.
4586 * Same answer as iterating mb_metacharlen() and counting calls
4587 * until end of string.
4588 *
4589 * If width is 1, return total character width rather than number.
4590 * If width is greater than 1, return 1 if character has non-zero width,
4591 * else 0.
4592 */
4593
4594/**/
4595mod_export int
4596mb_metastrlen(char *ptr, int width)
4597{
4598    char inchar, *laststart;
4599    size_t ret;
4600    wchar_t wc;
4601    int num, num_in_char;
4602
4603    if (!isset(MULTIBYTE))
4604	return ztrlen(ptr);
4605
4606    laststart = ptr;
4607    ret = MB_INVALID;
4608    num = num_in_char = 0;
4609
4610    memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
4611    while (*ptr) {
4612	if (*ptr == Meta)
4613	    inchar = *++ptr ^ 32;
4614	else
4615	    inchar = *ptr;
4616	ptr++;
4617	ret = mbrtowc(&wc, &inchar, 1, &mb_shiftstate);
4618
4619	if (ret == MB_INCOMPLETE) {
4620	    num_in_char++;
4621	} else {
4622	    if (ret == MB_INVALID) {
4623		/* Reset, treat as single character */
4624		memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
4625		ptr = laststart + (*laststart == Meta) + 1;
4626		num++;
4627	    } else if (width) {
4628		/*
4629		 * Returns -1 if not a printable character.  We
4630		 * turn this into 0.
4631		 */
4632		int wcw = WCWIDTH(wc);
4633		if (wcw > 0) {
4634		    if (width == 1)
4635			num += wcw;
4636		    else
4637			num++;
4638		}
4639	    } else
4640		num++;
4641	    laststart = ptr;
4642	    num_in_char = 0;
4643	}
4644    }
4645
4646    /* If incomplete, treat remainder as trailing single bytes */
4647    return num + num_in_char;
4648}
4649
4650/**/
4651#else
4652
4653/* Simple replacement for mb_metacharlenconv */
4654
4655/**/
4656mod_export int
4657metacharlenconv(const char *x, int *c)
4658{
4659    /*
4660     * Here we don't use STOUC() on the chars since they
4661     * may be compared against other chars and this will fail
4662     * if chars are signed and the high bit is set.
4663     */
4664    if (*x == Meta) {
4665	if (c)
4666	    *c = x[1] ^ 32;
4667	return 2;
4668    }
4669    if (c)
4670	*c = (char)*x;
4671    return 1;
4672}
4673
4674/**/
4675#endif /* MULTIBYTE_SUPPORT */
4676
4677/* check for special characters in the string */
4678
4679/**/
4680mod_export int
4681hasspecial(char const *s)
4682{
4683    for (; *s; s++) {
4684	if (ispecial(*s == Meta ? *++s ^ 32 : *s))
4685	    return 1;
4686    }
4687    return 0;
4688}
4689
4690
4691static char *
4692addunprintable(char *v, const char *u, const char *uend)
4693{
4694    for (; u < uend; u++) {
4695	/*
4696	 * Just do this byte by byte; there's no great
4697	 * advantage in being clever with multibyte
4698	 * characters if we don't think they're printable.
4699	 */
4700	int c;
4701	if (*u == Meta)
4702	    c = STOUC(*++u ^ 32);
4703	else
4704	    c = STOUC(*u);
4705	switch (c) {
4706	case '\0':
4707	    *v++ = '\\';
4708	    *v++ = '0';
4709	    if ('0' <= u[1] && u[1] <= '7') {
4710		*v++ = '0';
4711		*v++ = '0';
4712	    }
4713	    break;
4714
4715	case '\007': *v++ = '\\'; *v++ = 'a'; break;
4716	case '\b': *v++ = '\\'; *v++ = 'b'; break;
4717	case '\f': *v++ = '\\'; *v++ = 'f'; break;
4718	case '\n': *v++ = '\\'; *v++ = 'n'; break;
4719	case '\r': *v++ = '\\'; *v++ = 'r'; break;
4720	case '\t': *v++ = '\\'; *v++ = 't'; break;
4721	case '\v': *v++ = '\\'; *v++ = 'v'; break;
4722
4723	default:
4724	    *v++ = '\\';
4725	    *v++ = '0' + ((c >> 6) & 7);
4726	    *v++ = '0' + ((c >> 3) & 7);
4727	    *v++ = '0' + (c & 7);
4728	    break;
4729	}
4730    }
4731
4732    return v;
4733}
4734
4735/*
4736 * Quote the string s and return the result as a string from the heap.
4737 *
4738 * If e is non-zero, the
4739 * pointer it points to may point to a position in s and in e the position
4740 * of the corresponding character in the quoted string is returned.
4741 *
4742 * The last argument is a QT_ value defined in zsh.h other than QT_NONE.
4743 *
4744 * Most quote styles other than backslash assume the quotes are to
4745 * be added outside quotestring().  QT_SINGLE_OPTIONAL is different:
4746 * the single quotes are only added where necessary, so the
4747 * whole expression is handled here.
4748 *
4749 * The string may be metafied and contain tokens.
4750 */
4751
4752/**/
4753mod_export char *
4754quotestring(const char *s, char **e, int instring)
4755{
4756    const char *u;
4757    char *v;
4758    int alloclen;
4759    char *buf;
4760    int sf = 0, shownull = 0;
4761    /*
4762     * quotesub is used with QT_SINGLE_OPTIONAL.
4763     * quotesub = 0:  mechanism not active
4764     * quotesub = 1:  mechanism pending, no "'" yet;
4765     *                needs adding at quotestart.
4766     * quotesub = 2:  mechanism active, added opening "'"; need
4767     *                closing "'".
4768     */
4769    int quotesub = 0, slen;
4770    char *quotestart;
4771    convchar_t cc;
4772    const char *uend;
4773
4774    slen = strlen(s);
4775    switch (instring)
4776    {
4777    case QT_BACKSLASH_SHOWNULL:
4778	shownull = 1;
4779	instring = QT_BACKSLASH;
4780	/*FALLTHROUGH*/
4781    case QT_BACKSLASH:
4782	/*
4783	 * With QT_BACKSLASH we may need to use $'\300' stuff.
4784	 * Keep memory usage within limits by allocating temporary
4785	 * storage and using heap for correct size at end.
4786	 */
4787	alloclen = slen * 7 + 1;
4788	break;
4789
4790    case QT_SINGLE_OPTIONAL:
4791	/*
4792	 * Here, we may need to add single quotes.
4793	 * Always show empty strings.
4794	 */
4795	alloclen = slen * 4 + 3;
4796	quotesub = shownull = 1;
4797	break;
4798
4799    default:
4800	alloclen = slen * 4 + 1;
4801	break;
4802    }
4803    if (!*s && shownull)
4804	alloclen += 2;	/* for '' */
4805
4806    quotestart = v = buf = zshcalloc(alloclen);
4807
4808    DPUTS(instring < QT_BACKSLASH || instring == QT_BACKTICK ||
4809	  instring > QT_SINGLE_OPTIONAL,
4810	  "BUG: bad quote type in quotestring");
4811    u = s;
4812    if (instring == QT_DOLLARS) {
4813	/*
4814	 * As we test for printability here we need to be able
4815	 * to look for multibyte characters.
4816	 */
4817	MB_METACHARINIT();
4818	while (*u) {
4819	    uend = u + MB_METACHARLENCONV(u, &cc);
4820
4821	    if (e && !sf && *e <= u) {
4822		*e = v;
4823		sf = 1;
4824	    }
4825	    if (
4826#ifdef MULTIBYTE_SUPPORT
4827		cc != WEOF &&
4828#endif
4829		WC_ISPRINT(cc)) {
4830		switch (cc) {
4831		case ZWC('\\'):
4832		case ZWC('\''):
4833		    *v++ = '\\';
4834		    break;
4835
4836		default:
4837		    if (isset(BANGHIST) && cc == (wchar_t)bangchar)
4838			*v++ = '\\';
4839		    break;
4840		}
4841		while (u < uend)
4842		    *v++ = *u++;
4843	    } else {
4844		/* Not printable */
4845		v = addunprintable(v, u, uend);
4846		u = uend;
4847	    }
4848	}
4849    }
4850    else
4851    {
4852	if (shownull) {
4853	    /* We can't show an empty string with just backslash quoting. */
4854	    if (!*u) {
4855		*v++ = '\'';
4856		*v++ = '\'';
4857	    }
4858	}
4859	/*
4860	 * Here there are syntactic special characters, so
4861	 * we start by going through bytewise.
4862	 */
4863	while (*u) {
4864	    int dobackslash = 0;
4865	    if (e && *e == u)
4866		*e = v, sf = 1;
4867	    if (*u == Tick || *u == Qtick) {
4868		char c = *u++;
4869
4870		*v++ = c;
4871		while (*u && *u != c)
4872		    *v++ = *u++;
4873		*v++ = c;
4874		if (*u)
4875		    u++;
4876		continue;
4877	    } else if ((*u == Qstring || *u == '$') && u[1] == '\'' &&
4878		       instring == QT_DOUBLE) {
4879		/*
4880		 * We don't need to quote $'...' inside a double-quoted
4881		 * string.  This is largely cosmetic; it looks neater
4882		 * if we don't but it doesn't do any harm since the
4883		 * \ is stripped.
4884		 */
4885		*v++ = *u++;
4886	    } else if ((*u == String || *u == Qstring) &&
4887		       (u[1] == Inpar || u[1] == Inbrack || u[1] == Inbrace)) {
4888		char c = (u[1] == Inpar ? Outpar : (u[1] == Inbrace ?
4889						    Outbrace : Outbrack));
4890		char beg = *u;
4891		int level = 0;
4892
4893		*v++ = *u++;
4894		*v++ = *u++;
4895		while (*u && (*u != c || level)) {
4896		    if (*u == beg)
4897			level++;
4898		    else if (*u == c)
4899			level--;
4900		    *v++ = *u++;
4901		}
4902		if (*u)
4903		    *v++ = *u++;
4904		continue;
4905	    }
4906	    else if (ispecial(*u) &&
4907		     ((*u != '=' && *u != '~') ||
4908		      u == s ||
4909		      (isset(MAGICEQUALSUBST) &&
4910		       (u[-1] == '=' || u[-1] == ':')) ||
4911		      (*u == '~' && isset(EXTENDEDGLOB))) &&
4912		     (instring == QT_BACKSLASH ||
4913		      instring == QT_SINGLE_OPTIONAL ||
4914		      (isset(BANGHIST) && *u == (char)bangchar &&
4915		       instring != QT_SINGLE) ||
4916		      (instring == QT_DOUBLE &&
4917		       (*u == '$' || *u == '`' || *u == '\"' || *u == '\\')) ||
4918		      (instring == QT_SINGLE && *u == '\''))) {
4919		if (instring == QT_SINGLE_OPTIONAL) {
4920		    if (quotesub == 1) {
4921			/*
4922			 * We haven't yet had to quote at the start.
4923			 */
4924			if (*u == '\'') {
4925			    /*
4926			     * We don't need to.
4927			     */
4928			    *v++ = '\\';
4929			} else {
4930			    /*
4931			     * It's now time to add quotes.
4932			     */
4933			    if (v > quotestart)
4934			    {
4935				char *addq;
4936
4937				for (addq = v; addq > quotestart; addq--)
4938				    *addq = addq[-1];
4939			    }
4940			    *quotestart = '\'';
4941			    v++;
4942			    quotesub = 2;
4943			}
4944			*v++ = *u++;
4945			/*
4946			 * Next place to start quotes is here.
4947			 */
4948			quotestart = v;
4949		    } else if (*u == '\'') {
4950			if (unset(RCQUOTES)) {
4951			    *v++ = '\'';
4952			    *v++ = '\\';
4953			    *v++ = '\'';
4954			    /* Don't restart quotes unless we need them */
4955			    quotesub = 1;
4956			    quotestart = v;
4957			} else {
4958			    /* simplest just to use '' always */
4959			    *v++ = '\'';
4960			    *v++ = '\'';
4961			}
4962			/* dealt with */
4963			u++;
4964		    } else {
4965			/* else already quoting, just add */
4966			*v++ = *u++;
4967		    }
4968		    continue;
4969		} else if (*u == '\n' ||
4970			   (instring == QT_SINGLE && *u == '\'')) {
4971		    if (*u == '\n') {
4972			*v++ = '$';
4973			*v++ = '\'';
4974			*v++ = '\\';
4975			*v++ = 'n';
4976			*v++ = '\'';
4977		    } else if (unset(RCQUOTES)) {
4978			*v++ = '\'';
4979			if (*u == '\'')
4980			    *v++ = '\\';
4981			*v++ = *u;
4982			*v++ = '\'';
4983		    } else
4984			*v++ = '\'', *v++ = '\'';
4985		    u++;
4986		    continue;
4987		} else {
4988		    /*
4989		     * We'll need a backslash, but don't add it
4990		     * yet since if the character isn't printable
4991		     * we'll have to upgrade it to $'...'.
4992		     */
4993		    dobackslash = 1;
4994		}
4995	    }
4996
4997	    if (itok(*u) || instring != QT_BACKSLASH) {
4998		/* Needs to be passed straight through. */
4999		if (dobackslash)
5000		    *v++ = '\\';
5001		*v++ = *u++;
5002		continue;
5003	    }
5004
5005	    /*
5006	     * Now check if the output is unprintable in the
5007	     * current character set.
5008	     */
5009	    uend = u + MB_METACHARLENCONV(u, &cc);
5010	    if (
5011#ifdef MULTIBYTE_SUPPORT
5012		cc != WEOF &&
5013#endif
5014		WC_ISPRINT(cc)) {
5015		if (dobackslash)
5016		    *v++ = '\\';
5017		while (u < uend) {
5018		    if (*u == Meta)
5019			*v++ = *u++;
5020		    *v++ = *u++;
5021		}
5022	    } else {
5023		/* Not printable */
5024		*v++ = '$';
5025		*v++ = '\'';
5026		v = addunprintable(v, u, uend);
5027		*v++ = '\'';
5028		u = uend;
5029	    }
5030	}
5031    }
5032    if (quotesub == 2)
5033	*v++ = '\'';
5034    *v = '\0';
5035
5036    if (e && *e == u)
5037	*e = v, sf = 1;
5038    DPUTS(e && !sf, "BUG: Wild pointer *e in quotestring()");
5039
5040    v = dupstring(buf);
5041    zfree(buf, alloclen);
5042    return v;
5043}
5044
5045/* Unmetafy and output a string, quoted if it contains special characters. */
5046
5047/**/
5048mod_export int
5049quotedzputs(char const *s, FILE *stream)
5050{
5051    int inquote = 0, c;
5052
5053    /* check for empty string */
5054    if(!*s)
5055	return fputs("''", stream);
5056
5057    if (!hasspecial(s))
5058	return zputs(s, stream);
5059
5060    if (isset(RCQUOTES)) {
5061	/* use rc-style quotes-within-quotes for the whole string */
5062	if(fputc('\'', stream) < 0)
5063	    return EOF;
5064	while(*s) {
5065	    if (*s == Meta)
5066		c = *++s ^ 32;
5067	    else
5068		c = *s;
5069	    s++;
5070	    if (c == '\'') {
5071		if(fputc('\'', stream) < 0)
5072		    return EOF;
5073	    } else if(c == '\n' && isset(CSHJUNKIEQUOTES)) {
5074		if(fputc('\\', stream) < 0)
5075		    return EOF;
5076	    }
5077	    if(fputc(c, stream) < 0)
5078		return EOF;
5079	}
5080	if(fputc('\'', stream) < 0)
5081	    return EOF;
5082    } else {
5083	/* use Bourne-style quoting, avoiding empty quoted strings */
5084	while(*s) {
5085	    if (*s == Meta)
5086		c = *++s ^ 32;
5087	    else
5088		c = *s;
5089	    s++;
5090	    if (c == '\'') {
5091		if(inquote) {
5092		    if(fputc('\'', stream) < 0)
5093			return EOF;
5094		    inquote=0;
5095		}
5096		if(fputs("\\'", stream) < 0)
5097		    return EOF;
5098	    } else {
5099		if (!inquote) {
5100		    if(fputc('\'', stream) < 0)
5101			return EOF;
5102		    inquote=1;
5103		}
5104		if(c == '\n' && isset(CSHJUNKIEQUOTES)) {
5105		    if(fputc('\\', stream) < 0)
5106			return EOF;
5107		}
5108		if(fputc(c, stream) < 0)
5109		    return EOF;
5110	    }
5111	}
5112	if (inquote) {
5113	    if(fputc('\'', stream) < 0)
5114		return EOF;
5115	}
5116    }
5117    return 0;
5118}
5119
5120/* Double-quote a metafied string. */
5121
5122/**/
5123mod_export char *
5124dquotedztrdup(char const *s)
5125{
5126    int len = strlen(s) * 4 + 2;
5127    char *buf = zalloc(len);
5128    char *p = buf, *ret;
5129
5130    if(isset(CSHJUNKIEQUOTES)) {
5131	int inquote = 0;
5132
5133	while(*s) {
5134	    int c = *s++;
5135
5136	    if (c == Meta)
5137		c = *s++ ^ 32;
5138	    switch(c) {
5139		case '"':
5140		case '$':
5141		case '`':
5142		    if(inquote) {
5143			*p++ = '"';
5144			inquote = 0;
5145		    }
5146		    *p++ = '\\';
5147		    *p++ = c;
5148		    break;
5149		default:
5150		    if(!inquote) {
5151			*p++ = '"';
5152			inquote = 1;
5153		    }
5154		    if(c == '\n')
5155			*p++ = '\\';
5156		    *p++ = c;
5157		    break;
5158	    }
5159	}
5160	if (inquote)
5161	    *p++ = '"';
5162    } else {
5163	int pending = 0;
5164
5165	*p++ = '"';
5166	while(*s) {
5167	    int c = *s++;
5168
5169	    if (c == Meta)
5170		c = *s++ ^ 32;
5171	    switch(c) {
5172		case '\\':
5173		    if(pending)
5174			*p++ = '\\';
5175		    *p++ = '\\';
5176		    pending = 1;
5177		    break;
5178		case '"':
5179		case '$':
5180		case '`':
5181		    if(pending)
5182			*p++ = '\\';
5183		    *p++ = '\\';
5184		    /* FALL THROUGH */
5185		default:
5186		    *p++ = c;
5187		    pending = 0;
5188		    break;
5189	    }
5190	}
5191	if(pending)
5192	    *p++ = '\\';
5193	*p++ = '"';
5194    }
5195    ret = metafy(buf, p - buf, META_DUP);
5196    zfree(buf, len);
5197    return ret;
5198}
5199
5200/* Unmetafy and output a string, double quoting it in its entirety. */
5201
5202#if 0 /**/
5203int
5204dquotedzputs(char const *s, FILE *stream)
5205{
5206    char *d = dquotedztrdup(s);
5207    int ret = zputs(d, stream);
5208
5209    zsfree(d);
5210    return ret;
5211}
5212#endif
5213
5214# if defined(HAVE_NL_LANGINFO) && defined(CODESET) && !defined(__STDC_ISO_10646__)
5215/* Convert a character from UCS4 encoding to UTF-8 */
5216
5217static size_t
5218ucs4toutf8(char *dest, unsigned int wval)
5219{
5220    size_t len;
5221
5222    if (wval < 0x80)
5223      len = 1;
5224    else if (wval < 0x800)
5225      len = 2;
5226    else if (wval < 0x10000)
5227      len = 3;
5228    else if (wval < 0x200000)
5229      len = 4;
5230    else if (wval < 0x4000000)
5231      len = 5;
5232    else
5233      len = 6;
5234
5235    switch (len) { /* falls through except to the last case */
5236    case 6: dest[5] = (wval & 0x3f) | 0x80; wval >>= 6;
5237    case 5: dest[4] = (wval & 0x3f) | 0x80; wval >>= 6;
5238    case 4: dest[3] = (wval & 0x3f) | 0x80; wval >>= 6;
5239    case 3: dest[2] = (wval & 0x3f) | 0x80; wval >>= 6;
5240    case 2: dest[1] = (wval & 0x3f) | 0x80; wval >>= 6;
5241	*dest = wval | ((0xfc << (6 - len)) & 0xfc);
5242	break;
5243    case 1: *dest = wval;
5244    }
5245
5246    return len;
5247}
5248#endif
5249
5250
5251/*
5252 * The following only occurs once or twice in the code, but in different
5253 * places depending how character set conversion is implemented.
5254 */
5255#define CHARSET_FAILED()		      \
5256    if (how & GETKEY_DOLLAR_QUOTE) {	      \
5257	while ((*tdest++ = *++s)) {	      \
5258	    if (how & GETKEY_UPDATE_OFFSET) { \
5259		if (s - sstart > *misc)	      \
5260		    (*misc)++;		      \
5261	    }				      \
5262	    if (*s == Snull) {		      \
5263		*len = (s - sstart) + 1;      \
5264		*tdest = '\0';		      \
5265		return buf;		      \
5266	    }				      \
5267	}				      \
5268	*len = tdest - buf;		      \
5269	return buf;			      \
5270    }					      \
5271    *t = '\0';				      \
5272    *len = t - buf;			      \
5273    return buf
5274
5275/*
5276 * Decode a key string, turning it into the literal characters.
5277 * The value returned is a newly allocated string from the heap.
5278 *
5279 * The length is returned in *len.  This is usually the length of
5280 * the final unmetafied string.  The exception is the case of
5281 * a complete GETKEY_DOLLAR_QUOTE conversion where *len is the
5282 * length of the input string which has been used (up to and including
5283 * the terminating single quote); as the final string is metafied and
5284 * NULL-terminated its length is not required.  If both GETKEY_DOLLAR_QUOTE
5285 * and GETKEY_UPDATE_OFFSET are present in "how", the string is not
5286 * expected to be terminated (this is used in completion to parse
5287 * a partial $'...'-quoted string) and the length passed back is
5288 * that of the converted string.  Note in both cases that this is a length
5289 * in bytes (i.e. the same as given by a raw pointer difference), not
5290 * characters, which may occupy multiple bytes.
5291 *
5292 * how is a set of bits from the GETKEY_ values defined in zsh.h;
5293 * not all combinations of bits are useful.  Callers will typically
5294 * use one of the GETKEYS_ values which define sets of bits.
5295 * Note, for example that:
5296 * - GETKEY_SINGLE_CHAR must not be combined with GETKEY_DOLLAR_QUOTE.
5297 * - GETKEY_UPDATE_OFFSET is only allowed if GETKEY_DOLLAR_QUOTE is
5298 *   also present.
5299 *
5300 * *misc is used for various purposes:
5301 * - If GETKEY_BACKSLASH_MINUS is set, it indicates the presence
5302 *   of \- in the input.
5303 * - If GETKEY_BACKSLASH_C is set, it indicates the presence
5304 *   of \c in the input.
5305 * - If GETKEY_UPDATE_OFFSET is set, it is set on input to some
5306 *   mystical completion offset and is updated to a new offset based
5307 *   on the converted characters.  All Hail the Completion System
5308 *   [makes the mystic completion system runic sign in the air].
5309 *
5310 * The return value is unmetafied unless GETKEY_DOLLAR_QUOTE is
5311 * in use.
5312 */
5313
5314/**/
5315mod_export char *
5316getkeystring(char *s, int *len, int how, int *misc)
5317{
5318    char *buf, tmp[1];
5319    char *t, *tdest = NULL, *u = NULL, *sstart = s, *tbuf = NULL;
5320    char svchar = '\0';
5321    int meta = 0, control = 0, ignoring = 0;
5322    int i;
5323#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__)
5324    wint_t wval;
5325    int count;
5326#else
5327    unsigned int wval;
5328# if defined(HAVE_NL_LANGINFO) && defined(CODESET)
5329#  if defined(HAVE_ICONV)
5330    iconv_t cd;
5331    char inbuf[4];
5332    size_t inbytes, outbytes;
5333#  endif
5334    size_t count;
5335# endif
5336#endif
5337
5338    DPUTS((how & GETKEY_UPDATE_OFFSET) &&
5339	  (how & ~(GETKEYS_DOLLARS_QUOTE|GETKEY_UPDATE_OFFSET)),
5340	  "BUG: offset updating in getkeystring only supported with $'.");
5341    DPUTS((how & (GETKEY_DOLLAR_QUOTE|GETKEY_SINGLE_CHAR)) ==
5342	  (GETKEY_DOLLAR_QUOTE|GETKEY_SINGLE_CHAR),
5343	  "BUG: incompatible options in getkeystring");
5344
5345    if (how & GETKEY_SINGLE_CHAR)
5346	t = buf = tmp;
5347    else {
5348	/* Length including terminating NULL */
5349	int maxlen = 1;
5350	/*
5351	 * We're not necessarily guaranteed the output string will
5352	 * be no longer than the input with \u and \U when output
5353	 * characters need to be metafied.  As this is the only
5354	 * case where the string can get longer (?I think),
5355	 * include it in the allocation length here but don't
5356	 * bother taking account of other factors.
5357	 */
5358	for (t = s; *t; t++) {
5359	    if (*t == '\\') {
5360		if (!t[1]) {
5361		    maxlen++;
5362		    break;
5363		}
5364		if (t[1] == 'u' || t[1] == 'U')
5365		    maxlen += MB_CUR_MAX * 2;
5366		else
5367		    maxlen += 2;
5368		/* skip the backslash and the following character */
5369		t++;
5370	    } else
5371		maxlen++;
5372	}
5373	if (how & GETKEY_DOLLAR_QUOTE) {
5374	    /*
5375	     * We're going to unmetafy into a new string, but
5376	     * to get a proper metafied input we're going to metafy
5377	     * into an intermediate buffer.  This is necessary if we have
5378	     * \u and \U's with multiple metafied bytes.  We can't
5379	     * simply remetafy the entire string because there may
5380	     * be tokens (indeed, we know there are lexical nulls floating
5381	     * around), so we have to be aware character by character
5382	     * what we are converting.
5383	     *
5384	     * In this case, buf is the final buffer (as usual),
5385	     * but t points into a temporary buffer that just has
5386	     * to be long enough to hold the result of one escape
5387	     * code transformation.  We count this is a full multibyte
5388	     * character (MB_CUR_MAX) with every character metafied
5389	     * (*2) plus a little bit of fuzz (for e.g. the odd backslash).
5390	     */
5391	    buf = tdest = zhalloc(maxlen);
5392	    t = tbuf = zhalloc(MB_CUR_MAX * 3 + 1);
5393	} else {
5394	    t = buf = zhalloc(maxlen);
5395	}
5396    }
5397    for (; *s; s++) {
5398	if (*s == '\\' && s[1]) {
5399	    int miscadded;
5400	    if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) {
5401		(*misc)--;
5402		miscadded = 1;
5403	    } else
5404		miscadded = 0;
5405	    switch (*++s) {
5406	    case 'a':
5407#ifdef __STDC__
5408		*t++ = '\a';
5409#else
5410		*t++ = '\07';
5411#endif
5412		break;
5413	    case 'n':
5414		*t++ = '\n';
5415		break;
5416	    case 'b':
5417		*t++ = '\b';
5418		break;
5419	    case 't':
5420		*t++ = '\t';
5421		break;
5422	    case 'v':
5423		*t++ = '\v';
5424		break;
5425	    case 'f':
5426		*t++ = '\f';
5427		break;
5428	    case 'r':
5429		*t++ = '\r';
5430		break;
5431	    case 'E':
5432		if (!(how & GETKEY_EMACS)) {
5433		    *t++ = '\\', s--;
5434		    if (miscadded)
5435			(*misc)++;
5436		    continue;
5437		}
5438		/* FALL THROUGH */
5439	    case 'e':
5440		*t++ = '\033';
5441		break;
5442	    case 'M':
5443		/* HERE: GETKEY_UPDATE_OFFSET */
5444		if (how & GETKEY_EMACS) {
5445		    if (s[1] == '-')
5446			s++;
5447		    meta = 1 + control;	/* preserve the order of ^ and meta */
5448		} else {
5449		    if (miscadded)
5450			(*misc)++;
5451		    *t++ = '\\', s--;
5452		}
5453		continue;
5454	    case 'C':
5455		/* HERE: GETKEY_UPDATE_OFFSET */
5456		if (how & GETKEY_EMACS) {
5457		    if (s[1] == '-')
5458			s++;
5459		    control = 1;
5460		} else {
5461		    if (miscadded)
5462			(*misc)++;
5463		    *t++ = '\\', s--;
5464		}
5465		continue;
5466	    case Meta:
5467		if (miscadded)
5468		    (*misc)++;
5469		*t++ = '\\', s--;
5470		break;
5471	    case '-':
5472		if (how & GETKEY_BACKSLASH_MINUS) {
5473		    *misc  = 1;
5474		    break;
5475		}
5476		goto def;
5477	    case 'c':
5478		if (how & GETKEY_BACKSLASH_C) {
5479		    *misc = 1;
5480		    *t = '\0';
5481		    *len = t - buf;
5482		    return buf;
5483		}
5484		goto def;
5485	    case 'U':
5486		if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc)
5487		    (*misc) -= 4;
5488		/* FALLTHROUGH */
5489	    case 'u':
5490		if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) {
5491		    (*misc) -= 6; /* HERE don't really believe this */
5492		    /*
5493		     * We've now adjusted the offset for all the input
5494		     * characters, so we need to add for each
5495		     * byte of output below.
5496		     */
5497		}
5498	    	wval = 0;
5499		for (i=(*s == 'u' ? 4 : 8); i>0; i--) {
5500		    if (*++s && idigit(*s))
5501		        wval = wval * 16 + (*s - '0');
5502		    else if (*s && ((*s >= 'a' && *s <= 'f') ||
5503				    (*s >= 'A' && *s <= 'F')))
5504		        wval = wval * 16 + (*s & 0x1f) + 9;
5505		    else {
5506		    	s--;
5507		        break;
5508		    }
5509		}
5510    	    	if (how & GETKEY_SINGLE_CHAR) {
5511		    *misc = wval;
5512		    return s+1;
5513		}
5514#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__)
5515		count = wctomb(t, (wchar_t)wval);
5516		if (count == -1) {
5517		    zerr("character not in range");
5518		    CHARSET_FAILED();
5519		}
5520		if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc)
5521		    (*misc) += count;
5522		t += count;
5523# else
5524#  if defined(HAVE_NL_LANGINFO) && defined(CODESET)
5525		if (!strcmp(nl_langinfo(CODESET), "UTF-8")) {
5526		    count = ucs4toutf8(t, wval);
5527		    t += count;
5528		    if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc)
5529			(*misc) += count;
5530		} else {
5531#   ifdef HAVE_ICONV
5532		    ICONV_CONST char *inptr = inbuf;
5533		    const char *codesetstr = nl_langinfo(CODESET);
5534    	    	    inbytes = 4;
5535		    outbytes = 6;
5536		    /* store value in big endian form */
5537		    for (i=3;i>=0;i--) {
5538			inbuf[i] = wval & 0xff;
5539			wval >>= 8;
5540		    }
5541
5542		    /*
5543		     * If the code set isn't handled, we'd better
5544		     * assume it's US-ASCII rather than just failing
5545		     * hopelessly.  Solaris has a weird habit of
5546		     * returning 646.  This is handled by the
5547		     * native iconv(), but not by GNU iconv; what's
5548		     * more, some versions of the native iconv don't
5549		     * handle standard names like ASCII.
5550		     *
5551		     * This should only be a problem if there's a
5552		     * mismatch between the NLS and the iconv in use,
5553		     * which probably only means if libiconv is in use.
5554		     * We checked at configure time if our libraries
5555		     * pulled in _libiconv_version, which should be
5556		     * a good test.
5557		     *
5558		     * It shouldn't ever be NULL, but while we're
5559		     * being paranoid...
5560		     */
5561#ifdef ICONV_FROM_LIBICONV
5562		    if (!codesetstr || !*codesetstr)
5563			codesetstr = "US-ASCII";
5564#endif
5565    	    	    cd = iconv_open(codesetstr, "UCS-4BE");
5566#ifdef ICONV_FROM_LIBICONV
5567		    if (cd == (iconv_t)-1 &&  !strcmp(codesetstr, "646")) {
5568			codesetstr = "US-ASCII";
5569			cd = iconv_open(codesetstr, "UCS-4BE");
5570		    }
5571#endif
5572		    if (cd == (iconv_t)-1) {
5573			zerr("cannot do charset conversion (iconv failed)");
5574			CHARSET_FAILED();
5575		    }
5576                    count = iconv(cd, &inptr, &inbytes, &t, &outbytes);
5577		    iconv_close(cd);
5578		    if (count == (size_t)-1) {
5579                        zerr("character not in range");
5580			CHARSET_FAILED();
5581		    }
5582		    if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc)
5583			(*misc) += count;
5584#   else
5585                    zerr("cannot do charset conversion (iconv not available)");
5586		    CHARSET_FAILED();
5587#   endif
5588		}
5589#  else
5590                zerr("cannot do charset conversion (NLS not supported)");
5591		CHARSET_FAILED();
5592#  endif
5593# endif
5594		if (how & GETKEY_DOLLAR_QUOTE) {
5595		    char *t2;
5596		    for (t2 = tbuf; t2 < t; t2++) {
5597			if (imeta(*t2)) {
5598			    *tdest++ = Meta;
5599			    *tdest++ = *t2 ^ 32;
5600			} else
5601			    *tdest++ = *t2;
5602		    }
5603		    /* reset temporary buffer after handling */
5604		    t = tbuf;
5605		}
5606		continue;
5607	    case '\'':
5608	    case '\\':
5609		if (how & GETKEY_DOLLAR_QUOTE) {
5610		    /*
5611		     * Usually \' and \\ will have the initial
5612		     * \ turned into a Bnull, however that's not
5613		     * necessarily the case when called from
5614		     * completion.
5615		     */
5616		    *t++ = *s;
5617		    break;
5618		}
5619		/* FALLTHROUGH */
5620	    default:
5621	    def:
5622		/* HERE: GETKEY_UPDATE_OFFSET? */
5623		if ((idigit(*s) && *s < '8') || *s == 'x') {
5624		    if (!(how & GETKEY_OCTAL_ESC)) {
5625			if (*s == '0')
5626			    s++;
5627			else if (*s != 'x') {
5628			    *t++ = '\\', s--;
5629			    continue;
5630			}
5631		    }
5632		    if (s[1] && s[2] && s[3]) {
5633			svchar = s[3];
5634			s[3] = '\0';
5635			u = s;
5636		    }
5637		    *t++ = zstrtol(s + (*s == 'x'), &s,
5638				   (*s == 'x') ? 16 : 8);
5639		    if ((how & GETKEY_PRINTF_PERCENT) && t[-1] == '%')
5640		        *t++ = '%';
5641		    if (svchar) {
5642			u[3] = svchar;
5643			svchar = '\0';
5644		    }
5645		    s--;
5646		} else {
5647		    if (!(how & GETKEY_EMACS) && *s != '\\') {
5648			if (miscadded)
5649			    (*misc)++;
5650			*t++ = '\\';
5651		    }
5652		    *t++ = *s;
5653		}
5654		break;
5655	    }
5656	} else if ((how & GETKEY_DOLLAR_QUOTE) && *s == Snull) {
5657	    /* return length to following character */
5658	    *len = (s - sstart) + 1;
5659	    *tdest = '\0';
5660	    return buf;
5661	} else if (*s == '^' && !control && (how & GETKEY_CTRL) && s[1]) {
5662	    control = 1;
5663	    continue;
5664#ifdef MULTIBYTE_SUPPORT
5665	} else if ((how & GETKEY_SINGLE_CHAR) &&
5666		   isset(MULTIBYTE) && STOUC(*s) > 127) {
5667	    wint_t wc;
5668	    int len;
5669	    len = mb_metacharlenconv(s, &wc);
5670	    if (wc != WEOF) {
5671		*misc = (int)wc;
5672		return s + len;
5673	    }
5674#endif
5675
5676	} else if (*s == Meta)
5677	    *t++ = *++s ^ 32;
5678	else {
5679	    if (itok(*s)) {
5680		/*
5681		 * We need to be quite careful here.  We haven't
5682		 * necessarily got an input stream with all tokens
5683		 * removed, so the majority of tokens need passing
5684		 * through untouched and without Meta handling.
5685		 * However, me may need to handle tokenized
5686		 * backslashes.
5687		 */
5688		if (meta || control) {
5689		    /*
5690		     * Presumably we should be using meta or control
5691		     * on the character representing the token.
5692		     *
5693		     * Special case: $'\M-\\' where the token is a Bnull.
5694		     * This time we dump the Bnull since we're
5695		     * replacing the whole thing.  The lexer
5696		     * doesn't know about the meta or control modifiers.
5697		     */
5698		    if ((how & GETKEY_DOLLAR_QUOTE) && *s == Bnull)
5699			*t++ = *++s;
5700		    else
5701			*t++ = ztokens[*s - Pound];
5702		} else if (how & GETKEY_DOLLAR_QUOTE) {
5703		    /*
5704		     * We don't want to metafy this, it's a real
5705		     * token.
5706		     */
5707		    *tdest++ = *s;
5708		    if (*s == Bnull) {
5709			/*
5710			 * Bnull is a backslash which quotes a couple
5711			 * of special characters that always appear
5712			 * literally next.  See strquote handling
5713			 * in gettokstr() in lex.c.  We need
5714			 * to retain the Bnull (as above) so that quote
5715			 * handling in completion can tell where the
5716			 * backslash was.
5717			 */
5718			*tdest++ = *++s;
5719		    }
5720		    /* reset temporary buffer, now handled */
5721		    t = tbuf;
5722		    continue;
5723		} else
5724		    *t++ = *s;
5725	    } else
5726		*t++ = *s;
5727	}
5728	if (meta == 2) {
5729	    t[-1] |= 0x80;
5730	    meta = 0;
5731	}
5732	if (control) {
5733	    if (t[-1] == '?')
5734		t[-1] = 0x7f;
5735	    else
5736		t[-1] &= 0x9f;
5737	    control = 0;
5738	}
5739	if (meta) {
5740	    t[-1] |= 0x80;
5741	    meta = 0;
5742	}
5743	if (how & GETKEY_DOLLAR_QUOTE) {
5744	    char *t2;
5745	    for (t2 = tbuf; t2 < t; t2++) {
5746		/*
5747		 * In POSIX mode, an embedded NULL is discarded and
5748		 * terminates processing.  It just does, that's why.
5749		 */
5750		if (isset(POSIXSTRINGS)) {
5751		    if (*t2 == '\0')
5752			ignoring = 1;
5753		    if (ignoring)
5754			break;
5755		}
5756		if (imeta(*t2)) {
5757		    *tdest++ = Meta;
5758		    *tdest++ = *t2 ^ 32;
5759		} else {
5760		    *tdest++ = *t2;
5761		}
5762	    }
5763	    /*
5764	     * Reset use of temporary buffer.
5765	     */
5766	    t = tbuf;
5767	}
5768	if ((how & GETKEY_SINGLE_CHAR) && t != tmp) {
5769	    *misc = STOUC(tmp[0]);
5770	    return s + 1;
5771	}
5772    }
5773    /*
5774     * When called from completion, where we use GETKEY_UPDATE_OFFSET to
5775     * update the index into the metafied editor line, we don't necessarily
5776     * have the end of a $'...' quotation, else we should do.
5777     */
5778    DPUTS((how & (GETKEY_DOLLAR_QUOTE|GETKEY_UPDATE_OFFSET)) ==
5779	  GETKEY_DOLLAR_QUOTE, "BUG: unterminated $' substitution");
5780    *t = '\0';
5781    if (how & GETKEY_DOLLAR_QUOTE)
5782	*tdest = '\0';
5783    if (how & GETKEY_SINGLE_CHAR)
5784	*misc = 0;
5785    else
5786	*len = ((how & GETKEY_DOLLAR_QUOTE) ? tdest : t) - buf;
5787    return buf;
5788}
5789
5790/* Return non-zero if s is a prefix of t. */
5791
5792/**/
5793mod_export int
5794strpfx(const char *s, const char *t)
5795{
5796    while (*s && *s == *t)
5797	s++, t++;
5798    return !*s;
5799}
5800
5801/* Return non-zero if s is a suffix of t. */
5802
5803/**/
5804mod_export int
5805strsfx(char *s, char *t)
5806{
5807    int ls = strlen(s), lt = strlen(t);
5808
5809    if (ls <= lt)
5810	return !strcmp(t + lt - ls, s);
5811    return 0;
5812}
5813
5814/**/
5815static int
5816upchdir(int n)
5817{
5818    char buf[PATH_MAX];
5819    char *s;
5820    int err = -1;
5821
5822    while (n > 0) {
5823	for (s = buf; s < buf + PATH_MAX - 4 && n--; )
5824	    *s++ = '.', *s++ = '.', *s++ = '/';
5825	s[-1] = '\0';
5826	if (chdir(buf))
5827	    return err;
5828	err = -2;
5829    }
5830    return 0;
5831}
5832
5833/*
5834 * Initialize a "struct dirsav".
5835 * The structure will be set to the directory we want to save
5836 * the first time we change to a different directory.
5837 */
5838
5839/**/
5840mod_export void
5841init_dirsav(Dirsav d)
5842{
5843    d->ino = d->dev = 0;
5844    d->dirname = NULL;
5845    d->dirfd = d->level = -1;
5846}
5847
5848/* Change directory, without following symlinks.  Returns 0 on success, -1 *
5849 * on failure.  Sets errno to ENOTDIR if any symlinks are encountered.  If *
5850 * fchdir() fails, or the current directory is unreadable, we might end up *
5851 * in an unwanted directory in case of failure.                            */
5852
5853/**/
5854mod_export int
5855lchdir(char const *path, struct dirsav *d, int hard)
5856{
5857    char const *pptr;
5858    int level;
5859    struct stat st1;
5860    struct dirsav ds;
5861#ifdef HAVE_LSTAT
5862    char buf[PATH_MAX + 1], *ptr;
5863    int err;
5864    struct stat st2;
5865#endif
5866#ifdef HAVE_FCHDIR
5867    int close_dir = 0;
5868#endif
5869
5870    if (!d) {
5871	init_dirsav(&ds);
5872	d = &ds;
5873    }
5874#ifdef HAVE_LSTAT
5875    if ((*path == '/' || !hard) &&
5876	(d != &ds || hard)){
5877#else
5878    if (*path == '/') {
5879#endif
5880	level = -1;
5881#ifndef HAVE_FCHDIR
5882	if (!d->dirname)
5883	    zgetdir(d);
5884#endif
5885    } else {
5886	level = 0;
5887	if (!d->dev && !d->ino) {
5888	    stat(".", &st1);
5889	    d->dev = st1.st_dev;
5890	    d->ino = st1.st_ino;
5891	}
5892    }
5893
5894#ifdef HAVE_LSTAT
5895    if (!hard)
5896#endif
5897    {
5898	if (d != &ds) {
5899	    for (pptr = path; *pptr; level++) {
5900		while (*pptr && *pptr++ != '/');
5901		while (*pptr == '/')
5902		    pptr++;
5903	    }
5904	    d->level = level;
5905	}
5906	return zchdir((char *) path);
5907    }
5908
5909#ifdef HAVE_LSTAT
5910#ifdef HAVE_FCHDIR
5911    if (d->dirfd < 0) {
5912	close_dir = 1;
5913        if ((d->dirfd = open(".", O_RDONLY | O_NOCTTY)) < 0 &&
5914	    zgetdir(d) && *d->dirname != '/')
5915	    d->dirfd = open("..", O_RDONLY | O_NOCTTY);
5916    }
5917#endif
5918    if (*path == '/')
5919	if (chdir("/") < 0)
5920	    zwarn("failed to chdir(/): %e", errno);
5921    for(;;) {
5922	while(*path == '/')
5923	    path++;
5924	if(!*path) {
5925	    if (d == &ds)
5926		zsfree(ds.dirname);
5927	    else
5928		d->level = level;
5929#ifdef HAVE_FCHDIR
5930	    if (d->dirfd >=0 && close_dir) {
5931		close(d->dirfd);
5932		d->dirfd = -1;
5933	    }
5934#endif
5935	    return 0;
5936	}
5937	for(pptr = path; *++pptr && *pptr != '/'; ) ;
5938	if(pptr - path > PATH_MAX) {
5939	    err = ENAMETOOLONG;
5940	    break;
5941	}
5942	for(ptr = buf; path != pptr; )
5943	    *ptr++ = *path++;
5944	*ptr = 0;
5945	if(lstat(buf, &st1)) {
5946	    err = errno;
5947	    break;
5948	}
5949	if(!S_ISDIR(st1.st_mode)) {
5950	    err = ENOTDIR;
5951	    break;
5952	}
5953	if(chdir(buf)) {
5954	    err = errno;
5955	    break;
5956	}
5957	if (level >= 0)
5958	    level++;
5959	if(lstat(".", &st2)) {
5960	    err = errno;
5961	    break;
5962	}
5963	if(st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) {
5964	    err = ENOTDIR;
5965	    break;
5966	}
5967    }
5968    if (restoredir(d)) {
5969	int restoreerr = errno;
5970	int i;
5971	/*
5972	 * Failed to restore the directory.
5973	 * Just be definite, cd to root and report the result.
5974	 */
5975	for (i = 0; i < 2; i++) {
5976	    const char *cdest;
5977	    if (i)
5978		cdest = "/";
5979	    else {
5980		if (!home)
5981		    continue;
5982		cdest = home;
5983	    }
5984	    zsfree(pwd);
5985	    pwd = ztrdup(cdest);
5986	    if (chdir(pwd) == 0)
5987		break;
5988	}
5989	if (i == 2)
5990	    zerr("lost current directory, failed to cd to /: %e", errno);
5991	else
5992	    zerr("lost current directory: %e: changed to `%s'", restoreerr,
5993		pwd);
5994	if (d == &ds)
5995	    zsfree(ds.dirname);
5996#ifdef HAVE_FCHDIR
5997	if (d->dirfd >=0 && close_dir) {
5998	    close(d->dirfd);
5999	    d->dirfd = -1;
6000	}
6001#endif
6002	errno = err;
6003	return -2;
6004    }
6005    if (d == &ds)
6006	zsfree(ds.dirname);
6007#ifdef HAVE_FCHDIR
6008    if (d->dirfd >=0 && close_dir) {
6009	close(d->dirfd);
6010	d->dirfd = -1;
6011    }
6012#endif
6013    errno = err;
6014    return -1;
6015#endif /* HAVE_LSTAT */
6016}
6017
6018/**/
6019mod_export int
6020restoredir(struct dirsav *d)
6021{
6022    int err = 0;
6023    struct stat sbuf;
6024
6025    if (d->dirname && *d->dirname == '/')
6026	return chdir(d->dirname);
6027#ifdef HAVE_FCHDIR
6028    if (d->dirfd >= 0) {
6029	if (!fchdir(d->dirfd)) {
6030	    if (!d->dirname) {
6031		return 0;
6032	    } else if (chdir(d->dirname)) {
6033		close(d->dirfd);
6034		d->dirfd = -1;
6035		err = -2;
6036	    }
6037	} else {
6038	    close(d->dirfd);
6039	    d->dirfd = err = -1;
6040	}
6041    } else
6042#endif
6043    if (d->level > 0)
6044	err = upchdir(d->level);
6045    else if (d->level < 0)
6046	err = -1;
6047    if (d->dev || d->ino) {
6048	stat(".", &sbuf);
6049	if (sbuf.st_ino != d->ino || sbuf.st_dev != d->dev)
6050	    err = -2;
6051    }
6052    return err;
6053}
6054
6055
6056/* Check whether the shell is running with privileges in effect.  *
6057 * This is the case if EITHER the euid is zero, OR (if the system *
6058 * supports POSIX.1e (POSIX.6) capability sets) the process'      *
6059 * Effective or Inheritable capability sets are non-empty.        */
6060
6061/**/
6062int
6063privasserted(void)
6064{
6065    if(!geteuid())
6066	return 1;
6067#ifdef HAVE_CAP_GET_PROC
6068    {
6069	cap_t caps = cap_get_proc();
6070	if(caps) {
6071	    /* POSIX doesn't define a way to test whether a capability set *
6072	     * is empty or not.  Typical.  I hope this is conforming...    */
6073	    cap_flag_value_t val;
6074	    cap_value_t n;
6075	    for(n = 0; !cap_get_flag(caps, n, CAP_EFFECTIVE, &val); n++)
6076		if(val) {
6077		    cap_free(caps);
6078		    return 1;
6079		}
6080	}
6081	cap_free(caps);
6082    }
6083#endif /* HAVE_CAP_GET_PROC */
6084    return 0;
6085}
6086
6087/**/
6088mod_export int
6089mode_to_octal(mode_t mode)
6090{
6091    int m = 0;
6092
6093    if(mode & S_ISUID)
6094	m |= 04000;
6095    if(mode & S_ISGID)
6096	m |= 02000;
6097    if(mode & S_ISVTX)
6098	m |= 01000;
6099    if(mode & S_IRUSR)
6100	m |= 00400;
6101    if(mode & S_IWUSR)
6102	m |= 00200;
6103    if(mode & S_IXUSR)
6104	m |= 00100;
6105    if(mode & S_IRGRP)
6106	m |= 00040;
6107    if(mode & S_IWGRP)
6108	m |= 00020;
6109    if(mode & S_IXGRP)
6110	m |= 00010;
6111    if(mode & S_IROTH)
6112	m |= 00004;
6113    if(mode & S_IWOTH)
6114	m |= 00002;
6115    if(mode & S_IXOTH)
6116	m |= 00001;
6117    return m;
6118}
6119
6120#ifdef MAILDIR_SUPPORT
6121/*
6122 *     Stat a file. If it's a maildir, check all messages
6123 *     in the maildir and present the grand total as a file.
6124 *     The fields in the 'struct stat' are from the mail directory.
6125 *     The following fields are emulated:
6126 *
6127 *     st_nlink        always 1
6128 *     st_size         total number of bytes in all files
6129 *     st_blocks       total number of messages
6130 *     st_atime        access time of newest file in maildir
6131 *     st_mtime        modify time of newest file in maildir
6132 *     st_mode         S_IFDIR changed to S_IFREG
6133 *
6134 *     This is good enough for most mail-checking applications.
6135 */
6136
6137/**/
6138int
6139mailstat(char *path, struct stat *st)
6140{
6141       DIR                     *dd;
6142       struct                  dirent *fn;
6143       struct stat             st_ret, st_tmp;
6144       static struct stat      st_ret_last;
6145       char                    *dir, *file = 0;
6146       int                     i;
6147       time_t                  atime = 0, mtime = 0;
6148       size_t                  plen = strlen(path), dlen;
6149
6150       /* First see if it's a directory. */
6151       if ((i = stat(path, st)) != 0 || !S_ISDIR(st->st_mode))
6152               return i;
6153
6154       st_ret = *st;
6155       st_ret.st_nlink = 1;
6156       st_ret.st_size  = 0;
6157       st_ret.st_blocks  = 0;
6158       st_ret.st_mode  &= ~S_IFDIR;
6159       st_ret.st_mode  |= S_IFREG;
6160
6161       /* See if cur/ is present */
6162       dir = appstr(ztrdup(path), "/cur");
6163       if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0;
6164       st_ret.st_atime = st_tmp.st_atime;
6165
6166       /* See if tmp/ is present */
6167       dir[plen] = 0;
6168       dir = appstr(dir, "/tmp");
6169       if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0;
6170       st_ret.st_mtime = st_tmp.st_mtime;
6171
6172       /* And new/ */
6173       dir[plen] = 0;
6174       dir = appstr(dir, "/new");
6175       if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0;
6176       st_ret.st_mtime = st_tmp.st_mtime;
6177
6178#if THERE_IS_EXACTLY_ONE_MAILDIR_IN_MAILPATH
6179       {
6180       static struct stat      st_new_last;
6181       /* Optimization - if new/ didn't change, nothing else did. */
6182       if (st_tmp.st_dev == st_new_last.st_dev &&
6183           st_tmp.st_ino == st_new_last.st_ino &&
6184           st_tmp.st_atime == st_new_last.st_atime &&
6185           st_tmp.st_mtime == st_new_last.st_mtime) {
6186	   *st = st_ret_last;
6187	   return 0;
6188       }
6189       st_new_last = st_tmp;
6190       }
6191#endif
6192
6193       /* Loop over new/ and cur/ */
6194       for (i = 0; i < 2; i++) {
6195	   dir[plen] = 0;
6196	   dir = appstr(dir, i ? "/cur" : "/new");
6197	   if ((dd = opendir(dir)) == NULL) {
6198	       zsfree(file);
6199	       zsfree(dir);
6200	       return 0;
6201	   }
6202	   dlen = strlen(dir) + 1; /* include the "/" */
6203	   while ((fn = readdir(dd)) != NULL) {
6204	       if (fn->d_name[0] == '.')
6205		   continue;
6206	       if (file) {
6207		   file[dlen] = 0;
6208		   file = appstr(file, fn->d_name);
6209	       } else {
6210		   file = tricat(dir, "/", fn->d_name);
6211	       }
6212	       if (stat(file, &st_tmp) != 0)
6213		   continue;
6214	       st_ret.st_size += st_tmp.st_size;
6215	       st_ret.st_blocks++;
6216	       if (st_tmp.st_atime != st_tmp.st_mtime &&
6217		   st_tmp.st_atime > atime)
6218		   atime = st_tmp.st_atime;
6219	       if (st_tmp.st_mtime > mtime)
6220		   mtime = st_tmp.st_mtime;
6221	   }
6222	   closedir(dd);
6223       }
6224       zsfree(file);
6225       zsfree(dir);
6226
6227       if (atime) st_ret.st_atime = atime;
6228       if (mtime) st_ret.st_mtime = mtime;
6229
6230       *st = st_ret_last = st_ret;
6231       return 0;
6232}
6233#endif
6234