openenvutils/commandshell/shell/src/utils.c
changeset 0 2e3d3ce01487
child 4 0fdb7f6b0309
equal deleted inserted replaced
-1:000000000000 0:2e3d3ce01487
       
     1 // utils.c - miscellaneous utilities
       
     2 //
       
     3 // © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved.
       
     4 //
       
     5 /*
       
     6  * This file is part of zsh, the Z shell.
       
     7  *
       
     8  * Copyright (c) 1992-1997 Paul Falstad
       
     9  * All rights reserved.
       
    10  *
       
    11  * Permission is hereby granted, without written agreement and without
       
    12  * license or royalty fees, to use, copy, modify, and distribute this
       
    13  * software and to distribute modified versions of this software for any
       
    14  * purpose, provided that the above copyright notice and the following
       
    15  * two paragraphs appear in all copies of this software.
       
    16  *
       
    17  * In no event shall Paul Falstad or the Zsh Development Group be liable
       
    18  * to any party for direct, indirect, special, incidental, or consequential
       
    19  * damages arising out of the use of this software and its documentation,
       
    20  * even if Paul Falstad and the Zsh Development Group have been advised of
       
    21  * the possibility of such damage.
       
    22  *
       
    23  * Paul Falstad and the Zsh Development Group specifically disclaim any
       
    24  * warranties, including, but not limited to, the implied warranties of
       
    25  * merchantability and fitness for a particular purpose.  The software
       
    26  * provided hereunder is on an "as is" basis, and Paul Falstad and the
       
    27  * Zsh Development Group have no obligation to provide maintenance,
       
    28  * support, updates, enhancements, or modifications.
       
    29  *
       
    30  */
       
    31 
       
    32 #include "zsh.mdh"
       
    33 #include "utils.pro"
       
    34 
       
    35 #ifdef __SYMBIAN32__
       
    36 #ifdef __WINSCW__
       
    37 #pragma warn_possunwant off
       
    38 #endif//__WINSCW__
       
    39 #endif//__SYMBIAN32__
       
    40 
       
    41 #ifdef __SYMBIAN32__
       
    42 #include "dummy.h"
       
    43 #endif //__SYMBIAN32__
       
    44 
       
    45 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined (__STDC_ISO_10646__)
       
    46 # include <wchar.h>
       
    47 #else
       
    48 # ifdef HAVE_LANGINFO_H
       
    49 #   include <langinfo.h>
       
    50 #   if defined(HAVE_ICONV_H) || defined(HAVE_ICONV) || defined(HAVE_LIBICONV)
       
    51 #     include <iconv.h>
       
    52 #   endif
       
    53 # endif
       
    54 #endif
       
    55 
       
    56 /* name of script being sourced */
       
    57 
       
    58 /**/
       
    59 char *scriptname;
       
    60 
       
    61 /* Print an error */
       
    62  
       
    63 /**/
       
    64 mod_export void
       
    65 zerr(const char *fmt, const char *str, int num)
       
    66 {
       
    67     if (errflag || noerrs) {
       
    68 	if (noerrs < 2)
       
    69 	    errflag = 1;
       
    70 	return;
       
    71     }
       
    72     zwarn(fmt, str, num);
       
    73     errflag = 1;
       
    74 }
       
    75 
       
    76 /**/
       
    77 mod_export void
       
    78 zerrnam(const char *cmd, const char *fmt, const char *str, int num)
       
    79 {
       
    80     if (errflag || noerrs)
       
    81 	return;
       
    82 
       
    83     zwarnnam(cmd, fmt, str, num);
       
    84     errflag = 1;
       
    85 }
       
    86 
       
    87 /**/
       
    88 mod_export void
       
    89 zwarn(const char *fmt, const char *str, int num)
       
    90 {
       
    91     if (errflag || noerrs)
       
    92 	return;
       
    93     if (isatty(2))
       
    94 	trashzle();
       
    95     /*
       
    96      * scriptname is set when sourcing scripts, so that we get the
       
    97      * correct name instead of the generic name of whatever
       
    98      * program/script is running.  It's also set in shell functions,
       
    99      * so test locallevel, too.
       
   100      */
       
   101 #ifdef __SYMBIAN32__     
       
   102     dup2(1, 2); //redirect stdout to stdin.
       
   103 #endif//  __SYMBIAN32__  
       
   104 
       
   105     nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" :
       
   106 	      scriptname ? scriptname : argzero, stderr);
       
   107     fputc((unsigned char)':', stderr);
       
   108     zerrmsg(fmt, str, num);
       
   109 }
       
   110 
       
   111 /**/
       
   112 mod_export void
       
   113 zwarnnam(const char *cmd, const char *fmt, const char *str, int num)
       
   114 {
       
   115 #ifdef __SYMBIAN32__     
       
   116     dup2(1, 2); //redirect stdout to stdout.
       
   117 #endif//  __SYMBIAN32__ 
       
   118     if (!cmd) {
       
   119 	zwarn(fmt, str, num);
       
   120 	return;
       
   121     }
       
   122     if (errflag || noerrs)
       
   123 	return;
       
   124     trashzle();
       
   125     if (unset(SHINSTDIN) || locallevel) {
       
   126 	nicezputs(scriptname ? scriptname : argzero, stderr);
       
   127 	fputc((unsigned char)':', stderr);
       
   128     }
       
   129     nicezputs(cmd, stderr);
       
   130     fputc((unsigned char)':', stderr);
       
   131     zerrmsg(fmt, str, num);
       
   132 }
       
   133 
       
   134 #ifdef __CYGWIN__
       
   135 /*
       
   136  * This works around an occasional problem with dllwrap on Cygwin, seen
       
   137  * on at least two installations.  It fails to find the last symbol
       
   138  * exported in alphabetical order (in our case zwarnnam).  Until this is
       
   139  * properly categorised and fixed we add a dummy symbol at the end.
       
   140  */
       
   141 mod_export void
       
   142 zz_plural_z_alpha(void)
       
   143 {
       
   144 }
       
   145 #endif
       
   146 
       
   147 /**/
       
   148 void
       
   149 zerrmsg(const char *fmt, const char *str, int num)
       
   150 {
       
   151 	dup2(1, 2);
       
   152     if ((unset(SHINSTDIN) || locallevel) && lineno)
       
   153 	fprintf(stderr, "%ld: ", (long)lineno);
       
   154     else
       
   155 	fputc((unsigned char)' ', stderr);
       
   156 
       
   157     while (*fmt)
       
   158 	if (*fmt == '%') {
       
   159 	    fmt++;
       
   160 	    switch (*fmt++) {
       
   161 	    case 's':
       
   162 		nicezputs(str, stderr);
       
   163 		break;
       
   164 	    case 'l': {
       
   165 		char *s;
       
   166 		num = metalen(str, num);
       
   167 		s = zhalloc(num + 1);
       
   168 		memcpy(s, str, num);
       
   169 		s[num] = '\0';
       
   170 		nicezputs(s, stderr);
       
   171 		break;
       
   172 	    }
       
   173 	    case 'd':
       
   174 		fprintf(stderr, "%d", num);
       
   175 		break;
       
   176 	    case '%':
       
   177 		putc('%', stderr);
       
   178 		break;
       
   179 	    case 'c':
       
   180 		fputs(nicechar(num), stderr);
       
   181 		break;
       
   182 	    case 'e':
       
   183 		/* print the corresponding message for this errno */
       
   184 		if (num == EINTR) {
       
   185 		    fputs("interrupt\n", stderr);
       
   186 		    errflag = 1;
       
   187 		    return;
       
   188 		}
       
   189 		/* If the message is not about I/O problems, it looks better *
       
   190 		 * if we uncapitalize the first letter of the message        */
       
   191 		if (num == EIO)
       
   192 		    fputs(strerror(num), stderr);
       
   193 		else {
       
   194 		    char *errmsg = strerror(num);
       
   195 		    fputc(tulower(errmsg[0]), stderr);
       
   196 		    fputs(errmsg + 1, stderr);
       
   197 		}
       
   198 		break;
       
   199 	    }
       
   200 	} else {
       
   201 	    putc(*fmt == Meta ? *++fmt ^ 32 : *fmt, stderr);
       
   202 	    fmt++;
       
   203 	}
       
   204     putc('\n', stderr);
       
   205     fflush(stderr);
       
   206 }
       
   207 
       
   208 /* Output a single character, for the termcap routines.     *
       
   209  * This is used instead of putchar since it can be a macro. */
       
   210 
       
   211 /**/
       
   212 mod_export int
       
   213 putraw(int c)
       
   214 {
       
   215     putc(c, stdout);
       
   216     return 0;
       
   217 }
       
   218 
       
   219 /* Output a single character, for the termcap routines. */
       
   220 
       
   221 /**/
       
   222 mod_export int
       
   223 putshout(int c)
       
   224 {
       
   225     putc(c, shout);
       
   226     return 0;
       
   227 }
       
   228 
       
   229 /* Turn a character into a visible representation thereof.  The visible *
       
   230  * string is put together in a static buffer, and this function returns *
       
   231  * a pointer to it.  Printable characters stand for themselves, DEL is  *
       
   232  * represented as "^?", newline and tab are represented as "\n" and     *
       
   233  * "\t", and normal control characters are represented in "^C" form.    *
       
   234  * Characters with bit 7 set, if unprintable, are represented as "\M-"  *
       
   235  * followed by the visible representation of the character with bit 7   *
       
   236  * stripped off.  Tokens are interpreted, rather than being treated as  *
       
   237  * literal characters.                                                  */
       
   238 
       
   239 /**/
       
   240 mod_export char *
       
   241 nicechar(int c)
       
   242 {
       
   243     static char buf[6];
       
   244     char *s = buf;
       
   245     c &= 0xff;
       
   246     if (isprint(c))
       
   247 	goto done;
       
   248     if (c & 0x80) {
       
   249 	if (isset(PRINTEIGHTBIT))
       
   250 	    goto done;
       
   251 	*s++ = '\\';
       
   252 	*s++ = 'M';
       
   253 	*s++ = '-';
       
   254 	c &= 0x7f;
       
   255 	if(isprint(c))
       
   256 	    goto done;
       
   257     }
       
   258     if (c == 0x7f) {
       
   259 	*s++ = '^';
       
   260 	c = '?';
       
   261     } else if (c == '\n') {
       
   262 	*s++ = '\\';
       
   263 	c = 'n';
       
   264     } else if (c == '\t') {
       
   265 	*s++ = '\\';
       
   266 	c = 't';
       
   267     } else if (c < 0x20) {
       
   268 	*s++ = '^';
       
   269 	c += 0x40;
       
   270     }
       
   271     done:
       
   272     *s++ = c;
       
   273     *s = 0;
       
   274     return buf;
       
   275 }
       
   276 
       
   277 /* Output a string's visible representation. */
       
   278 
       
   279 #if 0 /**/
       
   280 void
       
   281 nicefputs(char *s, FILE *f)
       
   282 {
       
   283     for (; *s; s++)
       
   284 	fputs(nicechar(STOUC(*s)), f);
       
   285 }
       
   286 #endif
       
   287 
       
   288 /* Return the length of the visible representation of a string. */
       
   289 
       
   290 /**/
       
   291 size_t
       
   292 nicestrlen(char *s)
       
   293 {
       
   294     size_t l = 0;
       
   295 
       
   296     for (; *s; s++)
       
   297 	l += strlen(nicechar(STOUC(*s)));
       
   298     return l;
       
   299 }
       
   300 
       
   301 /* get a symlink-free pathname for s relative to PWD */
       
   302 
       
   303 /**/
       
   304 #ifdef __SYMBIAN32__
       
   305 #define SYM_ROOT '\\'
       
   306 char drive;
       
   307 #endif
       
   308 
       
   309 char *
       
   310 findpwd(char *s)
       
   311 {
       
   312     char *t;
       
   313 	drive=*s;
       
   314 	
       
   315     if (*(s+2) == SYM_ROOT)
       
   316 	return xsymlink(s);
       
   317     s = tricat((pwd[1]) ? pwd : "", "/", s);
       
   318     t = xsymlink(s);
       
   319     zsfree(s);
       
   320     return t;
       
   321 }
       
   322 
       
   323 /* Check whether a string contains the *
       
   324  * name of the present directory.      */
       
   325 
       
   326 /**/
       
   327 int
       
   328 ispwd(char *s)
       
   329 {
       
   330     struct stat sbuf, tbuf;
       
   331 
       
   332     if (stat(unmeta(s), &sbuf) == 0 && stat(".", &tbuf) == 0)
       
   333 	if (sbuf.st_dev == tbuf.st_dev && sbuf.st_ino == tbuf.st_ino)
       
   334 	    return 1;
       
   335     return 0;
       
   336 }
       
   337 
       
   338 static char xbuf[PATH_MAX*2];
       
   339 
       
   340 /**/
       
   341 static char **
       
   342 slashsplit(char *s)
       
   343 {
       
   344     char *t, **r, **q;
       
   345     int t0;
       
   346 
       
   347     if (!*s)
       
   348 	return (char **) zshcalloc(sizeof(char **));
       
   349 
       
   350     for (t = s, t0 = 0; *t; t++)
       
   351 	if (*t == '\\')
       
   352 	    t0++;
       
   353     q = r = (char **) zalloc(sizeof(char **) * (t0 + 2));
       
   354 
       
   355     while ((t = strchr(s, '\\'))) {
       
   356 	*q++ = ztrduppfx(s, t - s);
       
   357 	while (*t == '\\')
       
   358 	    t++;
       
   359 	if (!*t) {
       
   360 	    *q = NULL;
       
   361 	    return r;
       
   362 	}
       
   363 	s = t;
       
   364     }
       
   365     *q++ = ztrdup(s);
       
   366     *q = NULL;
       
   367     return r;
       
   368 }
       
   369 
       
   370 /* expands symlinks and .. or . expressions */
       
   371 /* if flag = 0, only expand .. and . expressions */
       
   372 
       
   373 /**/
       
   374 static int
       
   375 xsymlinks(char *s)
       
   376 {
       
   377     char **pp, **opp;
       
   378     char xbuf2[PATH_MAX*2], xbuf3[PATH_MAX*2];
       
   379     int t0, ret = 0;
       
   380 	int init_buf=0;
       
   381 	
       
   382     opp = pp = slashsplit(s);
       
   383     for (; *pp; pp++) {    
       
   384 	if (!strcmp(*pp, ".")) {
       
   385 	    zsfree(*pp);
       
   386 	    continue;
       
   387 	}
       
   388 	if (!strcmp(*pp, "..")) {
       
   389 	    char *p;
       
   390 
       
   391 	    zsfree(*pp);
       
   392 	    if (!strcmp(xbuf, "\\"))
       
   393 		continue;
       
   394 	    p = xbuf + strlen(xbuf);
       
   395 	    while (*--p != '\\');
       
   396 	    *(p+1) = '\0';
       
   397 	    continue;
       
   398 	}
       
   399 	if(!init_buf)	
       
   400 		sprintf(xbuf2, "%s%c:\\%s",xbuf,  drive, *pp);
       
   401 	else
       
   402 		sprintf(xbuf2, "%s\\%s", xbuf, *pp);
       
   403 	
       
   404 	t0 = readlink(unmeta(xbuf2), xbuf3, PATH_MAX);
       
   405 	if (t0 == -1) {
       
   406 		if(!init_buf)
       
   407 			{
       
   408 			strcat(xbuf, &drive);
       
   409 			strcat(xbuf, ":\\");				
       
   410 			}
       
   411 		else
       
   412 		    strcat(xbuf, "\\");
       
   413 		
       
   414 	    strcat(xbuf, *pp);
       
   415 	    zsfree(*pp);
       
   416 	} else {
       
   417 	    ret = 1;
       
   418 	    metafy(xbuf3, t0, META_NOALLOC);
       
   419 	    if (*xbuf3 == '\\') {
       
   420 		strcpy(xbuf, "");
       
   421 		xsymlinks(xbuf3 + 1);
       
   422 	    } else
       
   423 		xsymlinks(xbuf3);
       
   424 	    zsfree(*pp);
       
   425 	}
       
   426 	init_buf++;
       
   427     }
       
   428     free(opp);
       
   429     return ret;
       
   430 }
       
   431 
       
   432 /*
       
   433  * expand symlinks in s, and remove other weird things:
       
   434  * note that this always expands symlinks.
       
   435  */
       
   436 
       
   437 /**/
       
   438 char *
       
   439 xsymlink(char *s)
       
   440 {
       
   441     if (*(s+2) != SYM_ROOT)
       
   442 	return NULL;
       
   443     *xbuf = '\0';
       
   444     xsymlinks(s + 3);
       
   445     if (!*xbuf)
       
   446 	return ztrdup(s);
       
   447     return ztrdup(xbuf);
       
   448 }
       
   449 
       
   450 /**/
       
   451 void
       
   452 print_if_link(char *s)
       
   453 {
       
   454     if (*s == '/') {
       
   455 	*xbuf = '\0';
       
   456 	if (xsymlinks(s + 1))
       
   457 	    printf(" -> "), zputs(*xbuf ? xbuf : "/", stdout);
       
   458     }
       
   459 }
       
   460 
       
   461 /* print a directory */
       
   462 
       
   463 /**/
       
   464 void
       
   465 fprintdir(char *s, FILE *f)
       
   466 {
       
   467     Nameddir d = finddir(s);
       
   468 
       
   469     if (!d)
       
   470 	fputs(unmeta(s), f);
       
   471     else {
       
   472 #ifndef __SYMBIAN32__    
       
   473 	putc('~', f);
       
   474 	fputs(unmeta(d->nam), f);
       
   475 #endif
       
   476 	fputs(unmeta(s),f);
       
   477     }
       
   478 }
       
   479 
       
   480 /* Returns the current username.  It caches the username *
       
   481  * and uid to try to avoid requerying the password files *
       
   482  * or NIS/NIS+ database.                                 */
       
   483 
       
   484 /**/
       
   485 uid_t cached_uid;
       
   486 /**/
       
   487 char *cached_username;
       
   488 
       
   489 /**/
       
   490 char *
       
   491 get_username(void)
       
   492 {
       
   493 #ifdef HAVE_GETPWUID
       
   494     struct passwd *pswd;
       
   495     uid_t current_uid;
       
   496  
       
   497     current_uid = getuid();
       
   498     if (current_uid != cached_uid) {
       
   499 	cached_uid = current_uid;
       
   500 	zsfree(cached_username);
       
   501 	if ((pswd = getpwuid(current_uid)))
       
   502 	    cached_username = ztrdup(pswd->pw_name);
       
   503 	else
       
   504 	    cached_username = ztrdup("");
       
   505     }
       
   506 #else /* !HAVE_GETPWUID */
       
   507     cached_uid = getuid();
       
   508 #endif /* !HAVE_GETPWUID */
       
   509     return cached_username;
       
   510 }
       
   511 
       
   512 /* static variables needed by finddir(). */
       
   513 
       
   514 static char *finddir_full;
       
   515 static Nameddir finddir_last;
       
   516 static int finddir_best;
       
   517 
       
   518 /* ScanFunc used by finddir(). */
       
   519 
       
   520 /**/
       
   521 static void
       
   522 finddir_scan(HashNode hn, UNUSED(int flags))
       
   523 {
       
   524     Nameddir nd = (Nameddir) hn;
       
   525 #ifdef __SYMBIAN32__
       
   526 	flags=flags;
       
   527 #endif
       
   528     if(nd->diff > finddir_best && !dircmp(nd->dir, finddir_full)
       
   529        && !(nd->flags & ND_NOABBREV)) {
       
   530 	finddir_last=nd;
       
   531 	finddir_best=nd->diff;
       
   532     }
       
   533 }
       
   534 
       
   535 /* See if a path has a named directory as its prefix. *
       
   536  * If passed a NULL argument, it will invalidate any  *
       
   537  * cached information.                                */
       
   538 
       
   539 /**/
       
   540 Nameddir
       
   541 finddir(char *s)
       
   542 {
       
   543     static struct nameddir homenode = { NULL, "", 0, NULL, 0 };
       
   544     static int ffsz;
       
   545 
       
   546     /* Invalidate directory cache if argument is NULL.  This is called *
       
   547      * whenever a node is added to or removed from the hash table, and *
       
   548      * whenever the value of $HOME changes.  (On startup, too.)        */
       
   549     if (!s) {
       
   550 	homenode.dir = home;
       
   551 	homenode.diff = strlen(home);
       
   552 	if(homenode.diff==1)
       
   553 	    homenode.diff = 0;
       
   554 	if(!finddir_full)
       
   555 	    finddir_full = zalloc(ffsz = PATH_MAX);
       
   556 	finddir_full[0] = 0;
       
   557 	return finddir_last = NULL;
       
   558     }
       
   559 
       
   560     if(!strcmp(s, finddir_full) && *finddir_full)
       
   561 	return finddir_last;
       
   562 
       
   563     if ((int)strlen(s) >= ffsz) {
       
   564 	free(finddir_full);
       
   565 	finddir_full = zalloc(ffsz = strlen(s) * 2);
       
   566     }
       
   567     strcpy(finddir_full, s);
       
   568     finddir_best=0;
       
   569     finddir_last=NULL;
       
   570     finddir_scan((HashNode)&homenode, 0);
       
   571     scanhashtable(nameddirtab, 0, 0, 0, finddir_scan, 0);
       
   572     return finddir_last;
       
   573 }
       
   574 
       
   575 /* add a named directory */
       
   576 
       
   577 /**/
       
   578 mod_export void
       
   579 adduserdir(char *s, char *t, int flags, int always)
       
   580 {
       
   581     Nameddir nd;
       
   582 
       
   583     /* We don't maintain a hash table in non-interactive shells. */
       
   584     if (!interact)
       
   585 	return;
       
   586 
       
   587     /* The ND_USERNAME flag means that this possible hash table *
       
   588      * entry is derived from a passwd entry.  Such entries are  *
       
   589      * subordinate to explicitly generated entries.             */
       
   590     if ((flags & ND_USERNAME) && nameddirtab->getnode2(nameddirtab, s))
       
   591 	return;
       
   592 
       
   593     /* Normal parameter assignments generate calls to this function, *
       
   594      * with always==0.  Unless the AUTO_NAME_DIRS option is set, we  *
       
   595      * don't let such assignments actually create directory names.   *
       
   596      * Instead, a reference to the parameter as a directory name can *
       
   597      * cause the actual creation of the hash table entry.            */
       
   598     if (!always && unset(AUTONAMEDIRS) &&
       
   599 	    !nameddirtab->getnode2(nameddirtab, s))
       
   600 	return;
       
   601 
       
   602     if (!t || *t != '/' || strlen(t) >= PATH_MAX) {
       
   603 	/* We can't use this value as a directory, so simply remove *
       
   604 	 * the corresponding entry in the hash table, if any.       */
       
   605 	HashNode hn = nameddirtab->removenode(nameddirtab, s);
       
   606 
       
   607 	if(hn)
       
   608 	    nameddirtab->freenode(hn);
       
   609 	return;
       
   610     }
       
   611 
       
   612     /* add the name */
       
   613     nd = (Nameddir) zshcalloc(sizeof *nd);
       
   614     nd->flags = flags;
       
   615     nd->dir = ztrdup(t);
       
   616     /* The variables PWD and OLDPWD are not to be displayed as ~PWD etc. */
       
   617     if (!strcmp(s, "PWD") || !strcmp(s, "OLDPWD"))
       
   618 	nd->flags |= ND_NOABBREV;
       
   619     nameddirtab->addnode(nameddirtab, ztrdup(s), nd);
       
   620 }
       
   621 
       
   622 /* Get a named directory: this function can cause a directory name *
       
   623  * to be added to the hash table, if it isn't there already.       */
       
   624 
       
   625 /**/
       
   626 char *
       
   627 getnameddir(char *name)
       
   628 {
       
   629     Param pm;
       
   630     char *str;
       
   631     Nameddir nd;
       
   632 
       
   633     /* Check if it is already in the named directory table */
       
   634     if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)))
       
   635 	return dupstring(nd->dir);
       
   636 
       
   637     /* Check if there is a scalar parameter with this name whose value *
       
   638      * begins with a `/'.  If there is, add it to the hash table and   *
       
   639      * return the new value.                                           */
       
   640     if ((pm = (Param) paramtab->getnode(paramtab, name)) &&
       
   641 	    (PM_TYPE(pm->flags) == PM_SCALAR) &&
       
   642 	    (str = getsparam(name)) && *str == '/') {
       
   643 	pm->flags |= PM_NAMEDDIR;
       
   644 	adduserdir(name, str, 0, 1);
       
   645 	return str;
       
   646     }
       
   647 
       
   648 #ifdef HAVE_GETPWNAM
       
   649     {
       
   650 	/* Retrieve an entry from the password table/database for this user. */
       
   651 	struct passwd *pw;
       
   652 	if ((pw = getpwnam(name))) {
       
   653 	    char *dir = isset(CHASELINKS) ? xsymlink(pw->pw_dir)
       
   654 		: ztrdup(pw->pw_dir);
       
   655 	    adduserdir(name, dir, ND_USERNAME, 1);
       
   656 	    str = dupstring(dir);
       
   657 	    zsfree(dir);
       
   658 	    return str;
       
   659 	}
       
   660     }
       
   661 #endif /* HAVE_GETPWNAM */
       
   662 
       
   663     /* There are no more possible sources of directory names, so give up. */
       
   664     return NULL;
       
   665 }
       
   666 
       
   667 /**/
       
   668 static int
       
   669 dircmp(char *s, char *t)
       
   670 {
       
   671     if (s) {
       
   672 	for (; *s == *t; s++, t++)
       
   673 	    if (!*s)
       
   674 		return 0;
       
   675 	if (!*s && (*t == '/' || *t == '\\'))
       
   676 	    return 0;
       
   677     }
       
   678     return 1;
       
   679 }
       
   680 
       
   681 /* extra functions to call before displaying the prompt */
       
   682 
       
   683 /**/
       
   684 mod_export LinkList prepromptfns;
       
   685 
       
   686 /* the last time we checked mail */
       
   687  
       
   688 /**/
       
   689 time_t lastmailcheck;
       
   690  
       
   691 /* the last time we checked the people in the WATCH variable */
       
   692  
       
   693 /**/
       
   694 time_t lastwatch;
       
   695 
       
   696 /**/
       
   697 mod_export int
       
   698 callhookfunc(char *name, LinkList lnklst)
       
   699 {
       
   700     Eprog prog;
       
   701 
       
   702     if ((prog = getshfunc(name)) != &dummy_eprog) {
       
   703 	/*
       
   704 	 * Save stopmsg, since user doesn't get a chance to respond
       
   705 	 * to a list of jobs generated in a hook.
       
   706 	 */
       
   707 	int osc = sfcontext, osm = stopmsg;
       
   708 
       
   709 	sfcontext = SFC_HOOK;
       
   710 	doshfunc(name, prog, lnklst, 0, 1);
       
   711 	sfcontext = osc;
       
   712 	stopmsg = osm;
       
   713 
       
   714 	return 0;
       
   715     }
       
   716 
       
   717     return 1;
       
   718 }
       
   719 
       
   720 /* do pre-prompt stuff */
       
   721 
       
   722 /**/
       
   723 void
       
   724 preprompt(void)
       
   725 {
       
   726     static time_t lastperiodic;
       
   727     LinkNode ln;
       
   728     int period = getiparam("PERIOD");
       
   729     int mailcheck = getiparam("MAILCHECK");
       
   730 
       
   731     /* If NOTIFY is not set, then check for completed *
       
   732      * jobs before we print the prompt.               */
       
   733     if (unset(NOTIFY))
       
   734 	scanjobs();
       
   735     if (errflag)
       
   736 	return;
       
   737 
       
   738     /* If a shell function named "precmd" exists, *
       
   739      * then execute it.                           */
       
   740     callhookfunc("precmd", NULL);
       
   741     if (errflag)
       
   742 	return;
       
   743 
       
   744     /* If 1) the parameter PERIOD exists, 2) the shell function     *
       
   745      * "periodic" exists, 3) it's been greater than PERIOD since we *
       
   746      * executed "periodic", then execute it now.                    */
       
   747     if (period && (time(NULL) > lastperiodic + period) &&
       
   748 	!callhookfunc("periodic", NULL))
       
   749 	lastperiodic = time(NULL);
       
   750     if (errflag)
       
   751 	return;
       
   752 
       
   753     /* If WATCH is set, then check for the *
       
   754      * specified login/logout events.      */
       
   755     if (watch) {
       
   756 	if ((int) difftime(time(NULL), lastwatch) > getiparam("LOGCHECK")) {
       
   757 #ifndef __SYMBIAN32__	
       
   758 	    dowatch();
       
   759 #endif	    
       
   760 	    lastwatch = time(NULL);
       
   761 	}
       
   762     }
       
   763     if (errflag)
       
   764 	return;
       
   765 
       
   766     /* Check mail */
       
   767     if (mailcheck && (int) difftime(time(NULL), lastmailcheck) > mailcheck) {
       
   768 	char *mailfile;
       
   769 
       
   770 	if (mailpath && *mailpath && **mailpath)
       
   771 	    checkmailpath(mailpath);
       
   772 	else {
       
   773 	    queue_signals();
       
   774 	    if ((mailfile = getsparam("MAIL")) && *mailfile) {
       
   775 		char *x[2];
       
   776 
       
   777 		x[0] = mailfile;
       
   778 		x[1] = NULL;
       
   779 		checkmailpath(x);
       
   780 	    }
       
   781 	    unqueue_signals();
       
   782 	}
       
   783 	lastmailcheck = time(NULL);
       
   784     }
       
   785 
       
   786     /* Some people have claimed that C performs type    *
       
   787      * checking, but they were later found to be lying. */
       
   788     for(ln = firstnode(prepromptfns); ln; ln = nextnode(ln))
       
   789 	(**(void (**) _((void)))getdata(ln))();
       
   790 }
       
   791 
       
   792 /**/
       
   793 static void
       
   794 checkmailpath(char **s)
       
   795 {
       
   796     struct stat st;
       
   797     char *v, *u, c;
       
   798 
       
   799     while (*s) {
       
   800 	for (v = *s; *v && *v != '?'; v++);
       
   801 	c = *v;
       
   802 	*v = '\0';
       
   803 	if (c != '?')
       
   804 	    u = NULL;
       
   805 	else
       
   806 	    u = v + 1;
       
   807 	if (**s == 0) {
       
   808 	    *v = c;
       
   809 	    zerr("empty MAILPATH component: %s", *s, 0);
       
   810 	} else if (mailstat(unmeta(*s), &st) == -1) {
       
   811 	    if (errno != ENOENT)
       
   812 		zerr("%e: %s", *s, errno);
       
   813 	} else if (S_ISDIR(st.st_mode)) {
       
   814 	    LinkList l;
       
   815 	    DIR *lock = opendir(unmeta(*s));
       
   816 	    char buf[PATH_MAX * 2], **arr, **ap;
       
   817 	    int ct = 1;
       
   818 
       
   819 	    if (lock) {
       
   820 		char *fn;
       
   821 
       
   822 		pushheap();
       
   823 		l = newlinklist();
       
   824 		while ((fn = zreaddir(lock, 1)) && !errflag) {
       
   825 		    if (u)
       
   826 			sprintf(buf, "%s/%s?%s", *s, fn, u);
       
   827 		    else
       
   828 			sprintf(buf, "%s/%s", *s, fn);
       
   829 		    addlinknode(l, dupstring(buf));
       
   830 		    ct++;
       
   831 		}
       
   832 		closedir(lock);
       
   833 		ap = arr = (char **) zhalloc(ct * sizeof(char *));
       
   834 
       
   835 		while ((*ap++ = (char *)ugetnode(l)));
       
   836 		checkmailpath(arr);
       
   837 		popheap();
       
   838 	    }
       
   839 	} else {
       
   840 	    if (st.st_size && st.st_atime <= st.st_mtime &&
       
   841 		st.st_mtime > lastmailcheck) {
       
   842 		if (!u) {
       
   843 		    fprintf(shout, "You have new mail.\n");
       
   844 		    fflush(shout);
       
   845 		} else {
       
   846 		    VARARR(char, usav, underscoreused);
       
   847 
       
   848 		    memcpy(usav, underscore, underscoreused);
       
   849 
       
   850 		    setunderscore(*s);
       
   851 
       
   852 		    u = dupstring(u);
       
   853 		    if (! parsestr(u)) {
       
   854 			singsub(&u);
       
   855 			zputs(u, shout);
       
   856 			fputc('\n', shout);
       
   857 			fflush(shout);
       
   858 		    }
       
   859 		    setunderscore(usav);
       
   860 		}
       
   861 	    }
       
   862 	    if (isset(MAILWARNING) && st.st_atime > st.st_mtime &&
       
   863 		st.st_atime > lastmailcheck && st.st_size) {
       
   864 		fprintf(shout, "The mail in %s has been read.\n", unmeta(*s));
       
   865 		fflush(shout);
       
   866 	    }
       
   867 	}
       
   868 	*v = c;
       
   869 	s++;
       
   870     }
       
   871 }
       
   872 
       
   873 /* This prints the XTRACE prompt. */
       
   874 
       
   875 /**/
       
   876 FILE *xtrerr = 0;
       
   877 
       
   878 /**/
       
   879 void
       
   880 printprompt4(void)
       
   881 {
       
   882     if (!xtrerr)
       
   883 	xtrerr = stderr;
       
   884     if (prompt4) {
       
   885 	int l, t = opts[XTRACE];
       
   886 	char *s = dupstring(prompt4);
       
   887 
       
   888 	opts[XTRACE] = 0;
       
   889 	unmetafy(s, &l);
       
   890 	s = unmetafy(promptexpand(metafy(s, l, META_NOALLOC), 0, NULL, NULL), &l);
       
   891 	opts[XTRACE] = t;
       
   892 
       
   893 	fprintf(xtrerr, "%s", s);
       
   894 	free(s);
       
   895     }
       
   896 }
       
   897 
       
   898 /**/
       
   899 mod_export void
       
   900 freestr(void *a)
       
   901 {
       
   902     zsfree(a);
       
   903 }
       
   904 
       
   905 /**/
       
   906 void
       
   907 gettyinfo(struct ttyinfo *ti)
       
   908 {
       
   909     if (SHTTY != -1) {
       
   910 #ifdef HAVE_TERMIOS_H
       
   911 # ifdef HAVE_TCGETATTR
       
   912 	if (tcgetattr(SHTTY, &ti->tio) == -1)
       
   913 # else
       
   914 	if (ioctl(SHTTY, TCGETS, &ti->tio) == -1)
       
   915 # endif
       
   916 	    zerr("bad tcgets: %e", NULL, errno);
       
   917 #else
       
   918 # ifdef HAVE_TERMIO_H
       
   919 	ioctl(SHTTY, TCGETA, &ti->tio);
       
   920 # else
       
   921 	ioctl(SHTTY, TIOCGETP, &ti->sgttyb);
       
   922 	ioctl(SHTTY, TIOCLGET, &ti->lmodes);
       
   923 	ioctl(SHTTY, TIOCGETC, &ti->tchars);
       
   924 	ioctl(SHTTY, TIOCGLTC, &ti->ltchars);
       
   925 # endif
       
   926 #endif
       
   927     }
       
   928 }
       
   929 
       
   930 /**/
       
   931 mod_export void
       
   932 settyinfo(struct ttyinfo *ti)
       
   933 {
       
   934     if (SHTTY != -1) {
       
   935 #ifdef HAVE_TERMIOS_H
       
   936 # ifdef HAVE_TCGETATTR
       
   937 #  ifndef TCSADRAIN
       
   938 #   define TCSADRAIN 1	/* XXX Princeton's include files are screwed up */
       
   939 #  endif
       
   940 	tcsetattr(SHTTY, TCSADRAIN, &ti->tio);
       
   941     /* if (tcsetattr(SHTTY, TCSADRAIN, &ti->tio) == -1) */
       
   942 # else
       
   943 	ioctl(SHTTY, TCSETS, &ti->tio);
       
   944     /* if (ioctl(SHTTY, TCSETS, &ti->tio) == -1) */
       
   945 # endif
       
   946 	/*	zerr("settyinfo: %e",NULL,errno)*/ ;
       
   947 #else
       
   948 # ifdef HAVE_TERMIO_H
       
   949 	ioctl(SHTTY, TCSETA, &ti->tio);
       
   950 # else
       
   951 	ioctl(SHTTY, TIOCSETN, &ti->sgttyb);
       
   952 	ioctl(SHTTY, TIOCLSET, &ti->lmodes);
       
   953 	ioctl(SHTTY, TIOCSETC, &ti->tchars);
       
   954 	ioctl(SHTTY, TIOCSLTC, &ti->ltchars);
       
   955 # endif
       
   956 #endif
       
   957     }
       
   958 }
       
   959 
       
   960 /* the default tty state */
       
   961  
       
   962 /**/
       
   963 mod_export struct ttyinfo shttyinfo;
       
   964 
       
   965 /* != 0 if we need to call resetvideo() */
       
   966 
       
   967 /**/
       
   968 mod_export int resetneeded;
       
   969 
       
   970 #ifdef TIOCGWINSZ
       
   971 /* window size changed */
       
   972 
       
   973 /**/
       
   974 mod_export int winchanged;
       
   975 #endif
       
   976 
       
   977 static int
       
   978 adjustlines(int signalled)
       
   979 {
       
   980     int oldlines = lines;
       
   981 
       
   982 #ifdef TIOCGWINSZ
       
   983     if (signalled || lines <= 0)
       
   984 	lines = shttyinfo.winsize.ws_row;
       
   985     else
       
   986 	shttyinfo.winsize.ws_row = lines;
       
   987 #endif /* TIOCGWINSZ */
       
   988     if (lines <= 0) {
       
   989 	DPUTS(signalled, "BUG: Impossible TIOCGWINSZ rows");
       
   990 	lines = tclines > 0 ? tclines : 24;
       
   991     }
       
   992 
       
   993     if (lines > 2)
       
   994 	termflags &= ~TERM_SHORT;
       
   995     else
       
   996 	termflags |= TERM_SHORT;
       
   997 
       
   998     return (lines != oldlines);
       
   999 }
       
  1000 
       
  1001 static int
       
  1002 adjustcolumns(int signalled)
       
  1003 {
       
  1004     int oldcolumns = columns;
       
  1005 
       
  1006 #ifdef TIOCGWINSZ
       
  1007     if (signalled || columns <= 0)
       
  1008 	columns = shttyinfo.winsize.ws_col;
       
  1009     else
       
  1010 	shttyinfo.winsize.ws_col = columns;
       
  1011 #endif /* TIOCGWINSZ */
       
  1012     if (columns <= 0) {
       
  1013 	DPUTS(signalled, "BUG: Impossible TIOCGWINSZ cols");
       
  1014 	columns = tccolumns > 0 ? tccolumns : 80;
       
  1015     }
       
  1016 
       
  1017     if (columns > 2)
       
  1018 	termflags &= ~TERM_NARROW;
       
  1019     else
       
  1020 	termflags |= TERM_NARROW;
       
  1021 
       
  1022     return (columns != oldcolumns);
       
  1023 }
       
  1024 
       
  1025 /* check the size of the window and adjust if necessary. *
       
  1026  * The value of from:					 *
       
  1027  *   0: called from update_job or setupvals		 *
       
  1028  *   1: called from the SIGWINCH handler		 *
       
  1029  *   2: called from the LINES parameter callback	 *
       
  1030  *   3: called from the COLUMNS parameter callback	 */
       
  1031 
       
  1032 /**/
       
  1033 void
       
  1034 adjustwinsize(int from)
       
  1035 {
       
  1036     static int getwinsz = 1;
       
  1037     int ttyrows = shttyinfo.winsize.ws_row;
       
  1038     int ttycols = shttyinfo.winsize.ws_col;
       
  1039     int resetzle = 0;
       
  1040 
       
  1041     if (getwinsz || from == 1) {
       
  1042 #ifdef TIOCGWINSZ
       
  1043 	if (SHTTY == -1)
       
  1044 	    return;
       
  1045 	if (ioctl(SHTTY, TIOCGWINSZ, (char *)&shttyinfo.winsize) == 0) {
       
  1046 	    resetzle = (ttyrows != shttyinfo.winsize.ws_row ||
       
  1047 			ttycols != shttyinfo.winsize.ws_col);
       
  1048 	    if (from == 0 && resetzle && ttyrows && ttycols)
       
  1049 		from = 1; /* Signal missed while a job owned the tty? */
       
  1050 	    ttyrows = shttyinfo.winsize.ws_row;
       
  1051 	    ttycols = shttyinfo.winsize.ws_col;
       
  1052 	} else {
       
  1053 	    /* Set to value from environment on failure */
       
  1054 	    shttyinfo.winsize.ws_row = lines;
       
  1055 	    shttyinfo.winsize.ws_col = columns;
       
  1056 	    resetzle = (from == 1);
       
  1057 	}
       
  1058 #else
       
  1059 	resetzle = from == 1;
       
  1060 #endif /* TIOCGWINSZ */
       
  1061     } /* else
       
  1062 	 return; */
       
  1063 
       
  1064     switch (from) {
       
  1065     case 0:
       
  1066     case 1:
       
  1067 	getwinsz = 0;
       
  1068 	/* Calling setiparam() here calls this function recursively, but  *
       
  1069 	 * because we've already called adjustlines() and adjustcolumns() *
       
  1070 	 * here, recursive calls are no-ops unless a signal intervenes.   *
       
  1071 	 * The commented "else return;" above might be a safe shortcut,   *
       
  1072 	 * but I'm concerned about what happens on race conditions; e.g., *
       
  1073 	 * suppose the user resizes his xterm during `eval $(resize)'?    */
       
  1074 	if (adjustlines(from) && zgetenv("LINES"))
       
  1075 	    setiparam("LINES", lines);
       
  1076 	if (adjustcolumns(from) && zgetenv("COLUMNS"))
       
  1077 	    setiparam("COLUMNS", columns);
       
  1078 	getwinsz = 1;
       
  1079 	break;
       
  1080     case 2:
       
  1081 	resetzle = adjustlines(0);
       
  1082 	break;
       
  1083     case 3:
       
  1084 	resetzle = adjustcolumns(0);
       
  1085 	break;
       
  1086     }
       
  1087 
       
  1088 #ifdef TIOCGWINSZ
       
  1089     if (interact && from >= 2 &&
       
  1090 	(shttyinfo.winsize.ws_row != ttyrows ||
       
  1091 	 shttyinfo.winsize.ws_col != ttycols)) {
       
  1092 	/* shttyinfo.winsize is already set up correctly */
       
  1093 	ioctl(SHTTY, TIOCSWINSZ, (char *)&shttyinfo.winsize);
       
  1094     }
       
  1095 #endif /* TIOCGWINSZ */
       
  1096 
       
  1097     if (zleactive && resetzle) {
       
  1098 #ifdef TIOCGWINSZ
       
  1099 	winchanged =
       
  1100 #endif /* TIOCGWINSZ */
       
  1101 	    resetneeded = 1;
       
  1102 	zrefresh();
       
  1103 	zle_resetprompt();
       
  1104     }
       
  1105 }
       
  1106 
       
  1107 /* Move a fd to a place >= 10 and mark the new fd in fdtable.  If the fd *
       
  1108  * is already >= 10, it is not moved.  If it is invalid, -1 is returned. */
       
  1109 
       
  1110 /**/
       
  1111 mod_export int
       
  1112 movefd(int fd)
       
  1113 {
       
  1114     if(fd != -1 && fd < 10) {
       
  1115 #ifdef F_DUPFD
       
  1116 	int fe = fcntl(fd, F_DUPFD, 10);
       
  1117 #else
       
  1118 	int fe = movefd(dup(fd));
       
  1119 #endif
       
  1120 	zclose(fd);
       
  1121 	fd = fe;
       
  1122     }
       
  1123     if(fd != -1) {
       
  1124 	if (fd > max_zsh_fd) {
       
  1125 	    while (fd >= fdtable_size)
       
  1126 		fdtable = zrealloc(fdtable, (fdtable_size *= 2));
       
  1127 	    max_zsh_fd = fd;
       
  1128 	}
       
  1129 	fdtable[fd] = 1;
       
  1130     }
       
  1131     return fd;
       
  1132 }
       
  1133 
       
  1134 /* Move fd x to y.  If x == -1, fd y is closed. */
       
  1135 
       
  1136 /**/
       
  1137 mod_export void
       
  1138 redup(int x, int y)
       
  1139 {
       
  1140     if(x < 0)
       
  1141 	zclose(y);
       
  1142     else if (x != y) {
       
  1143 	while (y >= fdtable_size)
       
  1144 	    fdtable = zrealloc(fdtable, (fdtable_size *= 2));
       
  1145 	dup2(x, y);
       
  1146 	if ((fdtable[y] = fdtable[x]) && y > max_zsh_fd)
       
  1147 	    max_zsh_fd = y;
       
  1148 	zclose(x);
       
  1149     }
       
  1150 }
       
  1151 
       
  1152 /* Close the given fd, and clear it from fdtable. */
       
  1153 
       
  1154 /**/
       
  1155 mod_export int
       
  1156 zclose(int fd)
       
  1157 {
       
  1158     if (fd >= 0) {
       
  1159 	fdtable[fd] = 0;
       
  1160 	while (max_zsh_fd > 0 && !fdtable[max_zsh_fd])
       
  1161 	    max_zsh_fd--;
       
  1162 	if (fd == coprocin)
       
  1163 	    coprocin = -1;
       
  1164 	if (fd == coprocout)
       
  1165 	    coprocout = -1;
       
  1166 	return close(fd);
       
  1167     }
       
  1168     return -1;
       
  1169 }
       
  1170 
       
  1171 #ifdef HAVE__MKTEMP
       
  1172 extern char *_mktemp(char *);
       
  1173 #endif
       
  1174 
       
  1175 /* Get a unique filename for use as a temporary file.  If "prefix" is
       
  1176  * NULL, the name is relative to $TMPPREFIX; If it is non-NULL, the
       
  1177  * unique suffix includes a prefixed '.' for improved readability.  If
       
  1178  * "use_heap" is true, we allocate the returned name on the heap. */
       
  1179  
       
  1180 /**/
       
  1181 mod_export char *
       
  1182 gettempname(const char *prefix, int use_heap)
       
  1183 {
       
  1184     char *ret, *suffix = prefix ? ".XXXXXX" : "XXXXXX";
       
  1185  
       
  1186     queue_signals();
       
  1187 #ifndef __SYMBIAN32__    
       
  1188     if (!prefix && !(prefix = getsparam("TMPPREFIX")))
       
  1189 #endif    
       
  1190 	prefix = DEFAULT_TMPPREFIX;
       
  1191     if (use_heap)
       
  1192 	ret = dyncat(unmeta(prefix), suffix);
       
  1193     else
       
  1194 	ret = bicat(unmeta(prefix), suffix);
       
  1195  
       
  1196 #ifdef HAVE__MKTEMP
       
  1197     /* Zsh uses mktemp() safely, so silence the warnings */
       
  1198     ret = (char *) _mktemp(ret);
       
  1199 #else
       
  1200     ret = (char *) mktemp(ret);
       
  1201 #endif
       
  1202     unqueue_signals();
       
  1203 
       
  1204     return ret;
       
  1205 }
       
  1206 
       
  1207 /**/
       
  1208 mod_export int
       
  1209 gettempfile(const char *prefix, int use_heap, char **tempname)
       
  1210 {
       
  1211     char *fn;
       
  1212     int fd;
       
  1213 #if HAVE_MKSTEMP
       
  1214     char *suffix = prefix ? ".XXXXXX" : "XXXXXX";
       
  1215 #ifndef __SYMBIAN32__ 
       
  1216     if (!prefix && !(prefix = getsparam("TMPPREFIX")))
       
  1217 #endif    
       
  1218 	prefix = DEFAULT_TMPPREFIX;
       
  1219     if (use_heap)
       
  1220 	fn = dyncat(unmeta(prefix), suffix);
       
  1221     else
       
  1222 	fn = bicat(unmeta(prefix), suffix);
       
  1223 
       
  1224     fd = mkstemp(fn);
       
  1225     if (fd < 0) {
       
  1226 	if (!use_heap)
       
  1227 	    free(fn);
       
  1228 	fn = NULL;
       
  1229     }
       
  1230 #else
       
  1231     int failures = 0;
       
  1232 
       
  1233     do {
       
  1234 	if (!(fn = gettempname(prefix, use_heap))) {
       
  1235 	    fd = -1;
       
  1236 	    break;
       
  1237 	}
       
  1238 	if ((fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0600)) >= 0)
       
  1239 	    break;
       
  1240 	if (!use_heap)
       
  1241 	    free(fn);
       
  1242 	fn = NULL;
       
  1243     } while (errno == EEXIST && ++failures < 16);
       
  1244 #endif
       
  1245     *tempname = fn;
       
  1246     return fd;
       
  1247 }
       
  1248  
       
  1249 /* Check if a string contains a token */
       
  1250 
       
  1251 /**/
       
  1252 mod_export int
       
  1253 has_token(const char *s)
       
  1254 {
       
  1255     while(*s)
       
  1256 	if(itok(*s++))
       
  1257 	    return 1;
       
  1258     return 0;
       
  1259 }
       
  1260 
       
  1261 /* Delete a character in a string */
       
  1262  
       
  1263 /**/
       
  1264 mod_export void
       
  1265 chuck(char *str)
       
  1266 {
       
  1267     while ((str[0] = str[1]))
       
  1268 	str++;
       
  1269 }
       
  1270 
       
  1271 /**/
       
  1272 mod_export int
       
  1273 tulower(int c)
       
  1274 {
       
  1275     c &= 0xff;
       
  1276     return (isupper(c) ? tolower(c) : c);
       
  1277 }
       
  1278 
       
  1279 /**/
       
  1280 mod_export int
       
  1281 tuupper(int c)
       
  1282 {
       
  1283     c &= 0xff;
       
  1284     return (islower(c) ? toupper(c) : c);
       
  1285 }
       
  1286 
       
  1287 /* copy len chars from t into s, and null terminate */
       
  1288 
       
  1289 /**/
       
  1290 void
       
  1291 ztrncpy(char *s, char *t, int len)
       
  1292 {
       
  1293     while (len--)
       
  1294 	*s++ = *t++;
       
  1295     *s = '\0';
       
  1296 }
       
  1297 
       
  1298 /* copy t into *s and update s */
       
  1299 
       
  1300 /**/
       
  1301 mod_export void
       
  1302 strucpy(char **s, char *t)
       
  1303 {
       
  1304     char *u = *s;
       
  1305 
       
  1306     while ((*u++ = *t++));
       
  1307     *s = u - 1;
       
  1308 }
       
  1309 
       
  1310 /**/
       
  1311 mod_export void
       
  1312 struncpy(char **s, char *t, int n)
       
  1313 {
       
  1314     char *u = *s;
       
  1315 
       
  1316     while (n--)
       
  1317 	*u++ = *t++;
       
  1318     *s = u;
       
  1319     *u = '\0';
       
  1320 }
       
  1321 
       
  1322 /* Return the number of elements in an array of pointers. *
       
  1323  * It doesn't count the NULL pointer at the end.          */
       
  1324 
       
  1325 /**/
       
  1326 mod_export int
       
  1327 arrlen(char **s)
       
  1328 {
       
  1329     int count = 0;
       
  1330 
       
  1331     if(NULL != s)
       
  1332     	{
       
  1333 	    for (; *s; s++, count++);
       
  1334     	}
       
  1335     
       
  1336     return count;
       
  1337 }
       
  1338 
       
  1339 /* Skip over a balanced pair of parenthesis. */
       
  1340 
       
  1341 /**/
       
  1342 mod_export int
       
  1343 skipparens(char inpar, char outpar, char **s)
       
  1344 {
       
  1345     int level;
       
  1346 
       
  1347     if (**s != inpar)
       
  1348 	return -1;
       
  1349 
       
  1350     for (level = 1; *++*s && level;)
       
  1351 	if (**s == inpar)
       
  1352 	   ++level;
       
  1353 	else if (**s == outpar)
       
  1354 	   --level;
       
  1355 
       
  1356    return level;
       
  1357 }
       
  1358 
       
  1359 /* Convert string to zlong (see zsh.h).  This function (without the z) *
       
  1360  * is contained in the ANSI standard C library, but a lot of them seem *
       
  1361  * to be broken.                                                       */
       
  1362 
       
  1363 /**/
       
  1364 mod_export zlong
       
  1365 zstrtol(const char *s, char **t, int base)
       
  1366 {
       
  1367     const char *inp, *trunc = NULL;
       
  1368     zulong calc = 0, newcalc = 0;
       
  1369     int neg;
       
  1370 
       
  1371     while (inblank(*s))
       
  1372 	s++;
       
  1373 
       
  1374     if ((neg = (*s == '-')))
       
  1375 	s++;
       
  1376     else if (*s == '+')
       
  1377 	s++;
       
  1378 
       
  1379     if (!base) {
       
  1380 	if (*s != '0')
       
  1381 	    base = 10;
       
  1382 	else if (*++s == 'x' || *s == 'X')
       
  1383 	    base = 16, s++;
       
  1384 	else
       
  1385 	    base = 8;
       
  1386     }
       
  1387     inp = s;
       
  1388     if (base <= 10)
       
  1389 	for (; *s >= '0' && *s < ('0' + base); s++) {
       
  1390 	    if (trunc)
       
  1391 		continue;
       
  1392 	    newcalc = calc * base + *s - '0';
       
  1393 	    if (newcalc < calc)
       
  1394 	    {
       
  1395 	      trunc = s;
       
  1396 	      continue;
       
  1397 	    }
       
  1398 	    calc = newcalc;
       
  1399 	}
       
  1400     else
       
  1401 	for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10))
       
  1402 	     || (*s >= 'A' && *s < ('A' + base - 10)); s++) {
       
  1403 	    if (trunc)
       
  1404 		continue;
       
  1405 	    newcalc = calc*base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9);
       
  1406 	    if (newcalc < calc)
       
  1407 	    {
       
  1408 		trunc = s;
       
  1409 		continue;
       
  1410 	    }
       
  1411 	    calc = newcalc;
       
  1412 	}
       
  1413 
       
  1414     /*
       
  1415      * Special case: check for a number that was just too long for
       
  1416      * signed notation.
       
  1417      * Extra special case: the lowest negative number would trigger
       
  1418      * the first test, but is actually representable correctly.
       
  1419      * This is a 1 in the top bit, all others zero, so test for
       
  1420      * that explicitly.
       
  1421      */
       
  1422     if (!trunc && (zlong)calc < 0 &&
       
  1423 	(!neg || calc & ~((zulong)1 << (8*sizeof(zulong)-1))))
       
  1424     {
       
  1425 	trunc = s - 1;
       
  1426 	calc /= base;
       
  1427     }
       
  1428 
       
  1429     if (trunc)
       
  1430 	zwarn("number truncated after %d digits: %s", inp, trunc - inp);
       
  1431 
       
  1432     if (t)
       
  1433 	*t = (char *)s;
       
  1434     return neg ? -(zlong)calc : (zlong)calc;
       
  1435 }
       
  1436 
       
  1437 /**/
       
  1438 int
       
  1439 setblock_fd(int turnonblocking, int fd, long *modep)
       
  1440 {
       
  1441 #ifdef O_NDELAY
       
  1442 # ifdef O_NONBLOCK
       
  1443 #  define NONBLOCK (O_NDELAY|O_NONBLOCK)
       
  1444 # else /* !O_NONBLOCK */
       
  1445 #  define NONBLOCK O_NDELAY
       
  1446 # endif /* !O_NONBLOCK */
       
  1447 #else /* !O_NDELAY */
       
  1448 # ifdef O_NONBLOCK
       
  1449 #  define NONBLOCK O_NONBLOCK
       
  1450 # else /* !O_NONBLOCK */
       
  1451 #  define NONBLOCK 0
       
  1452 # endif /* !O_NONBLOCK */
       
  1453 #endif /* !O_NDELAY */
       
  1454 
       
  1455 #if NONBLOCK
       
  1456     struct stat st;
       
  1457 
       
  1458     if (!fstat(fd, &st) && !S_ISREG(st.st_mode)) {
       
  1459 	*modep = fcntl(fd, F_GETFL, 0);
       
  1460 	if (*modep != -1) {
       
  1461 	    if (!turnonblocking) {
       
  1462 		/* We want to know if blocking was off */
       
  1463 		if ((*modep & NONBLOCK) ||
       
  1464 		    !fcntl(fd, F_SETFL, *modep | NONBLOCK))
       
  1465 		    return 1;
       
  1466 	    } else if ((*modep & NONBLOCK) &&
       
  1467 		       !fcntl(fd, F_SETFL, *modep & ~NONBLOCK)) {
       
  1468 		/* Here we want to know if the state changed */
       
  1469 		return 1;
       
  1470 	    }
       
  1471 	}
       
  1472     } else
       
  1473 #endif /* NONBLOCK */
       
  1474 	*modep = -1;
       
  1475     return 0;
       
  1476 
       
  1477 #undef NONBLOCK
       
  1478 }
       
  1479 
       
  1480 /**/
       
  1481 int
       
  1482 setblock_stdin(void)
       
  1483 {
       
  1484     long mode;
       
  1485     return setblock_fd(1, 0, &mode);
       
  1486 }
       
  1487 
       
  1488 /*
       
  1489  * Check for pending input on fd.  If polltty is set, we may need to
       
  1490  * use termio to look for input.  As a final resort, go to non-blocking
       
  1491  * input and try to read a character, which in this case will be
       
  1492  * returned in *readchar.
       
  1493  *
       
  1494  * Note that apart from setting (and restoring) non-blocking input,
       
  1495  * this function does not change the input mode.  The calling function
       
  1496  * should have set cbreak mode if necessary.
       
  1497  */
       
  1498 
       
  1499 /**/
       
  1500 mod_export int
       
  1501 read_poll(int fd, int *readchar, int polltty, zlong microseconds)
       
  1502 {
       
  1503     int ret = -1;
       
  1504     long mode = -1;
       
  1505     char c;
       
  1506 #ifdef HAVE_SELECT
       
  1507     fd_set foofd;
       
  1508     struct timeval expire_tv;
       
  1509 #else
       
  1510 #ifdef FIONREAD
       
  1511     int val;
       
  1512 #endif
       
  1513 #endif
       
  1514 #ifdef HAS_TIO
       
  1515     struct ttyinfo ti;
       
  1516 #endif
       
  1517 
       
  1518 
       
  1519 #if defined(HAS_TIO) && !defined(__CYGWIN__)
       
  1520     /*
       
  1521      * Under Solaris, at least, reading from the terminal in non-canonical
       
  1522      * mode requires that we use the VMIN mechanism to poll.  Any attempt
       
  1523      * to check any other way, or to set the terminal to non-blocking mode
       
  1524      * and poll that way, fails; it will just for canonical mode input.
       
  1525      * We should probably use this mechanism if the user has set non-canonical
       
  1526      * mode, in which case testing here for isatty() and ~ICANON would be
       
  1527      * better than testing whether bin_read() set it, but for now we've got
       
  1528      * enough problems.
       
  1529      *
       
  1530      * Under Cygwin, you won't be surprised to here, this mechanism,
       
  1531      * although present, doesn't work, and we *have* to use ordinary
       
  1532      * non-blocking reads to find out if there is a character present
       
  1533      * in non-canonical mode.
       
  1534      *
       
  1535      * I am assuming Solaris is nearer the UNIX norm.  This is not necessarily
       
  1536      * as plausible as it sounds, but it seems the right way to guess.
       
  1537      *		pws 2000/06/26
       
  1538      */
       
  1539     if (polltty) {
       
  1540 	gettyinfo(&ti);
       
  1541 	if ((polltty = ti.tio.c_cc[VMIN])) {
       
  1542 	    ti.tio.c_cc[VMIN] = 0;
       
  1543 	    /* termios timeout is 10ths of a second */
       
  1544 	    ti.tio.c_cc[VTIME] = (int) (microseconds / (zlong)100000);
       
  1545 	    settyinfo(&ti);
       
  1546 	}
       
  1547     }
       
  1548 #else
       
  1549     polltty = 0;
       
  1550 #endif
       
  1551 #ifdef HAVE_SELECT
       
  1552     expire_tv.tv_sec = (int) (microseconds / (zlong)1000000);
       
  1553     expire_tv.tv_usec = microseconds % (zlong)1000000;
       
  1554     FD_ZERO(&foofd);
       
  1555     FD_SET(fd, &foofd);
       
  1556     ret = select(fd+1, (SELECT_ARG_2_T) &foofd, NULL, NULL, &expire_tv);
       
  1557 #else
       
  1558 #ifdef FIONREAD
       
  1559     if (ioctl(fd, FIONREAD, (char *) &val) == 0)
       
  1560 	ret = (val > 0);
       
  1561 #endif
       
  1562 #endif
       
  1563 
       
  1564     if (ret < 0) {
       
  1565 	/*
       
  1566 	 * Final attempt: set non-blocking read and try to read a character.
       
  1567 	 * Praise Bill, this works under Cygwin (nothing else seems to).
       
  1568 	 */
       
  1569 	if ((polltty || setblock_fd(0, fd, &mode)) && read(fd, &c, 1) > 0) {
       
  1570 	    *readchar = STOUC(c);
       
  1571 	    ret = 1;
       
  1572 	}
       
  1573 	if (mode != -1)
       
  1574 	    fcntl(fd, F_SETFL, mode);
       
  1575     }
       
  1576 #ifdef HAS_TIO
       
  1577     if (polltty) {
       
  1578 	ti.tio.c_cc[VMIN] = 1;
       
  1579 	ti.tio.c_cc[VTIME] = 0;
       
  1580 	settyinfo(&ti);
       
  1581     }
       
  1582 #endif
       
  1583     return (ret > 0);
       
  1584 }
       
  1585 
       
  1586 /**/
       
  1587 int
       
  1588 checkrmall(char *s)
       
  1589 {
       
  1590     if (!shout)
       
  1591 	return 1;
       
  1592     fprintf(shout, "zsh: sure you want to delete all the files in ");
       
  1593     if (*s != '/') {
       
  1594 	nicezputs(pwd[1] ? unmeta(pwd) : "", shout);
       
  1595 	fputc('/', shout);
       
  1596     }
       
  1597     nicezputs(s, shout);
       
  1598     if(isset(RMSTARWAIT)) {
       
  1599 	fputs("? (waiting ten seconds)", shout);
       
  1600 	fflush(shout);
       
  1601 	zbeep();
       
  1602 	sleep(10);
       
  1603 	fputc('\n', shout);
       
  1604     }
       
  1605     fputs(" [yn]? ", shout);
       
  1606     fflush(shout);
       
  1607     zbeep();
       
  1608     return (getquery("ny", 1) == 'y');
       
  1609 }
       
  1610 
       
  1611 /**/
       
  1612 int
       
  1613 read1char(void)
       
  1614 {
       
  1615     char c;
       
  1616 
       
  1617     while (read(SHTTY, &c, 1) != 1) {
       
  1618 	if (errno != EINTR || errflag || retflag || breaks || contflag)
       
  1619 	    return -1;
       
  1620     }
       
  1621     return STOUC(c);
       
  1622 }
       
  1623 
       
  1624 /**/
       
  1625 mod_export int
       
  1626 noquery(int purge)
       
  1627 {
       
  1628     int val = 0;
       
  1629     char c;
       
  1630 
       
  1631 #ifdef FIONREAD
       
  1632     ioctl(SHTTY, FIONREAD, (char *)&val);
       
  1633     if (purge) {
       
  1634 	for (; val; val--)
       
  1635 	    read(SHTTY, &c, 1);
       
  1636     }
       
  1637 #endif
       
  1638 
       
  1639     return val;
       
  1640 }
       
  1641 
       
  1642 /**/
       
  1643 int
       
  1644 getquery(char *valid_chars, int purge)
       
  1645 {
       
  1646     int c, d;
       
  1647     int isem = !strcmp(term, "emacs");
       
  1648 
       
  1649     attachtty(mypgrp);
       
  1650     if (!isem)
       
  1651 	setcbreak();
       
  1652 
       
  1653     if (noquery(purge)) {
       
  1654 	if (!isem)
       
  1655 	    settyinfo(&shttyinfo);
       
  1656 	write(SHTTY, "n\n", 2);
       
  1657 	return 'n';
       
  1658     }
       
  1659 
       
  1660     while ((c = read1char()) >= 0) {
       
  1661 	if (c == 'Y')
       
  1662 	    c = 'y';
       
  1663 	else if (c == 'N')
       
  1664 	    c = 'n';
       
  1665 	if (!valid_chars)
       
  1666 	    break;
       
  1667 	if (c == '\n') {
       
  1668 	    c = *valid_chars;
       
  1669 	    break;
       
  1670 	}
       
  1671 	if (strchr(valid_chars, c)) {
       
  1672 	    write(SHTTY, "\n", 1);
       
  1673 	    break;
       
  1674 	}
       
  1675 	zbeep();
       
  1676 	if (icntrl(c))
       
  1677 	    write(SHTTY, "\b \b", 3);
       
  1678 	write(SHTTY, "\b \b", 3);
       
  1679     }
       
  1680     if (isem) {
       
  1681 	if (c != '\n')
       
  1682 	    while ((d = read1char()) >= 0 && d != '\n');
       
  1683     } else {
       
  1684 	settyinfo(&shttyinfo);
       
  1685 	if (c != '\n' && !valid_chars)
       
  1686 	    write(SHTTY, "\n", 1);
       
  1687     }
       
  1688     return c;
       
  1689 }
       
  1690 
       
  1691 static int d;
       
  1692 static char *guess, *best;
       
  1693 
       
  1694 /**/
       
  1695 static void
       
  1696 spscan(HashNode hn, UNUSED(int scanflags))
       
  1697 {
       
  1698     int nd;
       
  1699 #ifdef __SYMBIAN32__
       
  1700 	scanflags=scanflags;
       
  1701 #endif
       
  1702     nd = spdist(hn->nam, guess, (int) strlen(guess) / 4 + 1);
       
  1703     if (nd <= d) {
       
  1704 	best = hn->nam;
       
  1705 	d = nd;
       
  1706     }
       
  1707 }
       
  1708 
       
  1709 /* spellcheck a word */
       
  1710 /* fix s ; if hist is nonzero, fix the history list too */
       
  1711 
       
  1712 /**/
       
  1713 mod_export void
       
  1714 spckword(char **s, int hist, int cmd, int ask)
       
  1715 {
       
  1716     char *t, *u;
       
  1717     int x;
       
  1718     char ic = '\0';
       
  1719     int ne;
       
  1720     int preflen = 0;
       
  1721 
       
  1722     if ((histdone & HISTFLAG_NOEXEC) || **s == '-' || **s == '%')
       
  1723 	return;
       
  1724     if (!strcmp(*s, "in"))
       
  1725 	return;
       
  1726     if (!(*s)[0] || !(*s)[1])
       
  1727 	return;
       
  1728     if (shfunctab->getnode(shfunctab, *s) ||
       
  1729 	builtintab->getnode(builtintab, *s) ||
       
  1730 	cmdnamtab->getnode(cmdnamtab, *s) ||
       
  1731 	aliastab->getnode(aliastab, *s)  ||
       
  1732 	reswdtab->getnode(reswdtab, *s))
       
  1733 	return;
       
  1734     else if (isset(HASHLISTALL)) {
       
  1735 	cmdnamtab->filltable(cmdnamtab);
       
  1736 	if (cmdnamtab->getnode(cmdnamtab, *s))
       
  1737 	    return;
       
  1738     }
       
  1739     t = *s;
       
  1740     if (*t == Tilde || *t == Equals || *t == String)
       
  1741 	t++;
       
  1742     for (; *t; t++)
       
  1743 	if (itok(*t))
       
  1744 	    return;
       
  1745     best = NULL;
       
  1746     for (t = *s; *t; t++)
       
  1747 	if (*t == '/')
       
  1748 	    break;
       
  1749     if (**s == Tilde && !*t)
       
  1750 	return;
       
  1751     if (**s == String && !*t) {
       
  1752 	guess = *s + 1;
       
  1753 	if (*t || !ialpha(*guess))
       
  1754 	    return;
       
  1755 	ic = String;
       
  1756 	d = 100;
       
  1757 	scanhashtable(paramtab, 1, 0, 0, spscan, 0);
       
  1758     } else if (**s == Equals) {
       
  1759 	if (*t)
       
  1760 	    return;
       
  1761 	if (hashcmd(guess = *s + 1, pathchecked))
       
  1762 	    return;
       
  1763 	d = 100;
       
  1764 	ic = Equals;
       
  1765 	scanhashtable(aliastab, 1, 0, 0, spscan, 0);
       
  1766 	scanhashtable(cmdnamtab, 1, 0, 0, spscan, 0);
       
  1767     } else {
       
  1768 	guess = *s;
       
  1769 	if (*guess == Tilde || *guess == String) {
       
  1770 	    ic = *guess;
       
  1771 	    if (!*++t)
       
  1772 		return;
       
  1773 	    guess = dupstring(guess);
       
  1774 	    ne = noerrs;
       
  1775 	    noerrs = 2;
       
  1776 	    singsub(&guess);
       
  1777 	    noerrs = ne;
       
  1778 	    if (!guess)
       
  1779 		return;
       
  1780 	    preflen = strlen(guess) - strlen(t);
       
  1781 	}
       
  1782 	if (access(unmeta(guess), F_OK) == 0)
       
  1783 	    return;
       
  1784 	if ((u = spname(guess)) != guess)
       
  1785 	    best = u;
       
  1786 	if (!*t && cmd) {
       
  1787 	    if (hashcmd(guess, pathchecked))
       
  1788 		return;
       
  1789 	    d = 100;
       
  1790 	    scanhashtable(reswdtab, 1, 0, 0, spscan, 0);
       
  1791 	    scanhashtable(aliastab, 1, 0, 0, spscan, 0);
       
  1792 	    scanhashtable(shfunctab, 1, 0, 0, spscan, 0);
       
  1793 	    scanhashtable(builtintab, 1, 0, 0, spscan, 0);
       
  1794 	    scanhashtable(cmdnamtab, 1, 0, 0, spscan, 0);
       
  1795 	}
       
  1796     }
       
  1797     if (errflag)
       
  1798 	return;
       
  1799     if (best && (int)strlen(best) > 1 && strcmp(best, guess)) {
       
  1800 	if (ic) {
       
  1801 	    if (preflen) {
       
  1802 		/* do not correct the result of an expansion */
       
  1803 		if (strncmp(guess, best, preflen))
       
  1804 		    return;
       
  1805 		/* replace the temporarily expanded prefix with the original */
       
  1806 		u = (char *) hcalloc(t - *s + strlen(best + preflen) + 1);
       
  1807 		strncpy(u, *s, t - *s);
       
  1808 		strcpy(u + (t - *s), best + preflen);
       
  1809 	    } else {
       
  1810 		u = (char *) hcalloc(strlen(best) + 2);
       
  1811 		strcpy(u + 1, best);
       
  1812 	    }
       
  1813 	    best = u;
       
  1814 	    guess = *s;
       
  1815 	    *guess = *best = ztokens[ic - Pound];
       
  1816 	}
       
  1817 	if (ask) {
       
  1818 	    if (noquery(0)) {
       
  1819 		x = 'n';
       
  1820 	    } else {
       
  1821 		char *pptbuf;
       
  1822 		pptbuf = promptexpand(sprompt, 0, best, guess);
       
  1823 		zputs(pptbuf, shout);
       
  1824 		free(pptbuf);
       
  1825 		fflush(shout);
       
  1826 		zbeep();
       
  1827 		x = getquery("nyae \t", 0);
       
  1828 	    }
       
  1829 	} else
       
  1830 	    x = 'y';
       
  1831 	if (x == 'y' || x == ' ' || x == '\t') {
       
  1832 	    *s = dupstring(best);
       
  1833 	    if (hist)
       
  1834 		hwrep(best);
       
  1835 	} else if (x == 'a') {
       
  1836 	    histdone |= HISTFLAG_NOEXEC;
       
  1837 	} else if (x == 'e') {
       
  1838 	    histdone |= HISTFLAG_NOEXEC | HISTFLAG_RECALL;
       
  1839 	}
       
  1840 	if (ic)
       
  1841 	    **s = ic;
       
  1842     }
       
  1843 }
       
  1844 
       
  1845 /*
       
  1846  * Helper for ztrftime.  Called with a pointer to the length left
       
  1847  * in the buffer, and a new string length to decrement from that.
       
  1848  * Returns 0 if the new length fits, 1 otherwise.  We assume a terminating
       
  1849  * NUL and return 1 if that doesn't fit.
       
  1850  */
       
  1851 
       
  1852 /**/
       
  1853 static int
       
  1854 ztrftimebuf(int *bufsizeptr, int decr)
       
  1855 {
       
  1856     if (*bufsizeptr <= decr)
       
  1857 	return 1;
       
  1858     *bufsizeptr -= decr;
       
  1859     return 0;
       
  1860 }
       
  1861 
       
  1862 /*
       
  1863  * Like the system function, this returns the number of characters
       
  1864  * copied, not including the terminating NUL.  This may be zero
       
  1865  * if the string didn't fit.
       
  1866  *
       
  1867  * As an extension, try to detect an error in strftime --- typically
       
  1868  * not enough memory --- and return -1.  Not guaranteed to be portable,
       
  1869  * since the strftime() interface doesn't make any guarantees about
       
  1870  * the state of the buffer if it returns zero.
       
  1871  */
       
  1872 
       
  1873 /**/
       
  1874 mod_export int
       
  1875 ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm)
       
  1876 {
       
  1877     int hr12, decr;
       
  1878 #ifndef HAVE_STRFTIME
       
  1879     static char *astr[] =
       
  1880     {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
       
  1881     static char *estr[] =
       
  1882     {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
       
  1883      "Aug", "Sep", "Oct", "Nov", "Dec"};
       
  1884 #endif
       
  1885     char *origbuf = buf;
       
  1886     char tmp[3];
       
  1887 
       
  1888 
       
  1889     tmp[0] = '%';
       
  1890     tmp[2] = '\0';
       
  1891     while (*fmt)
       
  1892 	if (*fmt == '%') {
       
  1893 	    fmt++;
       
  1894 	    /*
       
  1895 	     * Assume this format will take up at least two
       
  1896 	     * characters.  Not always true, but if that matters
       
  1897 	     * we are so close to the edge it's not a big deal.
       
  1898 	     * Fix up some longer cases specially when we get to them.
       
  1899 	     */
       
  1900 	    if (ztrftimebuf(&bufsize, 2))
       
  1901 		return 0;
       
  1902 	    switch (*fmt++) {
       
  1903 	    case 'd':
       
  1904 		*buf++ = '0' + tm->tm_mday / 10;
       
  1905 		*buf++ = '0' + tm->tm_mday % 10;
       
  1906 		break;
       
  1907 	    case 'e':
       
  1908 	    case 'f':
       
  1909 		if (tm->tm_mday > 9)
       
  1910 		    *buf++ = '0' + tm->tm_mday / 10;
       
  1911 		else if (fmt[-1] == 'e')
       
  1912 		    *buf++ = ' ';
       
  1913 		*buf++ = '0' + tm->tm_mday % 10;
       
  1914 		break;
       
  1915 	    case 'k':
       
  1916 	    case 'K':
       
  1917 		if (tm->tm_hour > 9)
       
  1918 		    *buf++ = '0' + tm->tm_hour / 10;
       
  1919 		else if (fmt[-1] == 'k')
       
  1920 		    *buf++ = ' ';
       
  1921 		*buf++ = '0' + tm->tm_hour % 10;
       
  1922 		break;
       
  1923 	    case 'l':
       
  1924 	    case 'L':
       
  1925 		hr12 = tm->tm_hour % 12;
       
  1926 		if (hr12 == 0)
       
  1927 		    hr12 = 12;
       
  1928 	        if (hr12 > 9)
       
  1929 		    *buf++ = '1';
       
  1930 		else if (fmt[-1] == 'l')
       
  1931 		    *buf++ = ' ';
       
  1932 
       
  1933 		*buf++ = '0' + (hr12 % 10);
       
  1934 		break;
       
  1935 	    case 'm':
       
  1936 		*buf++ = '0' + (tm->tm_mon + 1) / 10;
       
  1937 		*buf++ = '0' + (tm->tm_mon + 1) % 10;
       
  1938 		break;
       
  1939 	    case 'M':
       
  1940 		*buf++ = '0' + tm->tm_min / 10;
       
  1941 		*buf++ = '0' + tm->tm_min % 10;
       
  1942 		break;
       
  1943 	    case 'S':
       
  1944 		*buf++ = '0' + tm->tm_sec / 10;
       
  1945 		*buf++ = '0' + tm->tm_sec % 10;
       
  1946 		break;
       
  1947 	    case 'y':
       
  1948 		*buf++ = '0' + (tm->tm_year / 10) % 10;
       
  1949 		*buf++ = '0' + tm->tm_year % 10;
       
  1950 		break;
       
  1951 	    case '\0':
       
  1952 		/* Guard against premature end of string */
       
  1953 		*buf++ = '%';
       
  1954 		fmt--;
       
  1955 		break;
       
  1956 #ifndef HAVE_STRFTIME
       
  1957 	    case 'a':
       
  1958 		if (ztrftimebuf(&bufsize, strlen(astr[tm->tm_wday]) - 2))
       
  1959 		    return 0;
       
  1960 		strucpy(&buf, astr[tm->tm_wday]);
       
  1961 		break;
       
  1962 	    case 'b':
       
  1963 		if (ztrftimebuf(&bufsize, strlen(estr[tm->tm_mon]) - 2))
       
  1964 		    return 0;
       
  1965 		strucpy(&buf, estr[tm->tm_mon]);
       
  1966 		break;
       
  1967 	    case 'p':
       
  1968 		*buf++ = (tm->tm_hour > 11) ? 'p' : 'a';
       
  1969 		*buf++ = 'm';
       
  1970 		break;
       
  1971 	    default:
       
  1972 		*buf++ = '%';
       
  1973 		if (fmt[-1] != '%')
       
  1974 		    *buf++ = fmt[-1];
       
  1975 #else
       
  1976 	    default:
       
  1977 		/*
       
  1978 		 * Remember we've already allowed for two characters
       
  1979 		 * in the accounting in bufsize (but nowhere else).
       
  1980 		 */
       
  1981 		*buf = '\1';
       
  1982 		tmp[1] = fmt[-1];
       
  1983 		if (!strftime(buf, bufsize + 2, tmp, tm))
       
  1984 		{
       
  1985 		    if (*buf) {
       
  1986 			buf[0] = '\0';
       
  1987 			return -1;
       
  1988 		    }
       
  1989 		    return 0;
       
  1990 		}
       
  1991 		decr = strlen(buf);
       
  1992 		buf += decr;
       
  1993 		bufsize -= decr - 2;
       
  1994 #endif
       
  1995 		break;
       
  1996 	    }
       
  1997 	} else {
       
  1998 	    if (ztrftimebuf(&bufsize, 1))
       
  1999 		return 0;
       
  2000 	    *buf++ = *fmt++;
       
  2001 	}
       
  2002     *buf = '\0';
       
  2003     return buf - origbuf;
       
  2004 }
       
  2005 
       
  2006 /**/
       
  2007 mod_export char *
       
  2008 zjoin(char **arr, int delim, int heap)
       
  2009 {
       
  2010     int len = 0;
       
  2011     char **s, *ret, *ptr;
       
  2012 
       
  2013     for (s = arr; *s; s++)
       
  2014 	len += strlen(*s) + 1 + (imeta(delim) ? 1 : 0);
       
  2015     if (!len)
       
  2016 	return heap? "" : ztrdup("");
       
  2017     ptr = ret = (heap ? (char *) hcalloc(len) : (char *) zshcalloc(len));
       
  2018     for (s = arr; *s; s++) {
       
  2019 	strucpy(&ptr, *s);
       
  2020 	    if (imeta(delim)) {
       
  2021 		*ptr++ = Meta;
       
  2022 		*ptr++ = delim ^ 32;
       
  2023 	    }
       
  2024 	    else
       
  2025 		*ptr++ = delim;
       
  2026     }
       
  2027     ptr[-1 - (imeta(delim) ? 1 : 0)] = '\0';
       
  2028     return ret;
       
  2029 }
       
  2030 
       
  2031 /* Split a string containing a colon separated list *
       
  2032  * of items into an array of strings.               */
       
  2033 
       
  2034 /**/
       
  2035 char **
       
  2036 colonsplit(char *s, int uniq)
       
  2037 {
       
  2038     int ct;
       
  2039     char *t, **ret, **ptr, **p;
       
  2040 
       
  2041     for (t = s, ct = 0; *t; t++) /* count number of colons */
       
  2042 	if (*t == ':')
       
  2043 	    ct++;
       
  2044     ptr = ret = (char **) zalloc(sizeof(char **) * (ct + 2));
       
  2045 
       
  2046     t = s;
       
  2047     do {
       
  2048 	s = t;
       
  2049         /* move t to point at next colon */
       
  2050 	for (; *t && *t != ':'; t++);
       
  2051 	if (uniq)
       
  2052 	    for (p = ret; p < ptr; p++)
       
  2053 		if ((int)strlen(*p) == t - s && ! strncmp(*p, s, t - s))
       
  2054 		    goto cont;
       
  2055 	*ptr = (char *) zalloc((t - s) + 1);
       
  2056 	ztrncpy(*ptr++, s, t - s);
       
  2057       cont: ;
       
  2058     }
       
  2059     while (*t++);
       
  2060     *ptr = NULL;
       
  2061     return ret;
       
  2062 }
       
  2063 
       
  2064 /**/
       
  2065 static int
       
  2066 skipwsep(char **s)
       
  2067 {
       
  2068     char *t = *s;
       
  2069     int i = 0;
       
  2070 
       
  2071     while (*t && iwsep(*t == Meta ? t[1] ^ 32 : *t)) {
       
  2072 	if (*t == Meta)
       
  2073 	    t++;
       
  2074 	t++;
       
  2075 	i++;
       
  2076     }
       
  2077     *s = t;
       
  2078     return i;
       
  2079 }
       
  2080 
       
  2081 /*
       
  2082  * haven't worked out what allownull does; it's passed down from
       
  2083  *   sepsplit but all the cases it's used are either 0 or 1 without
       
  2084  *   a comment.  it seems to be something to do with the `nulstring'
       
  2085  *   which i think is some kind of a metafication thing, so probably
       
  2086  *   allownull's value is associated with whether we are using
       
  2087  *   metafied strings.
       
  2088  * see findsep() below for handling of `quote' argument
       
  2089  */
       
  2090 
       
  2091 /**/
       
  2092 mod_export char **
       
  2093 spacesplit(char *s, int allownull, int heap, int quote)
       
  2094 {
       
  2095     char *t, **ret, **ptr;
       
  2096     int l = sizeof(*ret) * (wordcount(s, NULL, -!allownull) + 1);
       
  2097     char *(*dup)(const char *) = (heap ? dupstring : ztrdup);
       
  2098 
       
  2099     ptr = ret = (heap ? (char **) hcalloc(l) : (char **) zshcalloc(l));
       
  2100 
       
  2101     if (quote) {
       
  2102 	/*
       
  2103 	 * we will be stripping quoted separators by hacking string,
       
  2104 	 * so make sure it's hackable.
       
  2105 	 */
       
  2106 	s = dupstring(s);
       
  2107     }
       
  2108 
       
  2109     t = s;
       
  2110     skipwsep(&s);
       
  2111     if (*s && isep(*s == Meta ? s[1] ^ 32 : *s))
       
  2112 	*ptr++ = dup(allownull ? "" : nulstring);
       
  2113     else if (!allownull && t != s)
       
  2114 	*ptr++ = dup("");
       
  2115     while (*s) {
       
  2116 	if (isep(*s == Meta ? s[1] ^ 32 : *s) || (quote && *s == '\\')) {
       
  2117 	    if (*s == Meta)
       
  2118 		s++;
       
  2119 	    s++;
       
  2120 	    skipwsep(&s);
       
  2121 	}
       
  2122 	t = s;
       
  2123 	findsep(&s, NULL, quote);
       
  2124 	if (s > t || allownull) {
       
  2125 	    *ptr = (heap ? (char *) hcalloc((s - t) + 1) :
       
  2126 		    (char *) zshcalloc((s - t) + 1));
       
  2127 	    ztrncpy(*ptr++, t, s - t);
       
  2128 	} else
       
  2129 	    *ptr++ = dup(nulstring);
       
  2130 	t = s;
       
  2131 	skipwsep(&s);
       
  2132     }
       
  2133     if (!allownull && t != s)
       
  2134 	*ptr++ = dup("");
       
  2135     *ptr = NULL;
       
  2136     return ret;
       
  2137 }
       
  2138 
       
  2139 /**/
       
  2140 static int
       
  2141 findsep(char **s, char *sep, int quote)
       
  2142 {
       
  2143     /*
       
  2144      * *s is the string we are looking along, which will be updated
       
  2145      * to the point we have got to.
       
  2146      *
       
  2147      * sep is a possibly multicharacter separator to look for.  If NULL,
       
  2148      * use normal separator characters.
       
  2149      *
       
  2150      * quote is a flag that '\<sep>' should not be treated as a separator.
       
  2151      * in this case we need to be able to strip the backslash directly
       
  2152      * in the string, so the calling function must have sent us something
       
  2153      * modifiable.  currently this only works for sep == NULL.  also in
       
  2154      * in this case only, we need to turn \\ into \.
       
  2155      */
       
  2156     int i;
       
  2157     char *t, *tt;
       
  2158 
       
  2159     if (!sep) {
       
  2160 	for (t = *s; *t; t++) {
       
  2161 	    if (quote && *t == '\\' &&
       
  2162 		(isep(t[1] == Meta ? (t[2] ^ 32) : t[1]) || t[1] == '\\')) {
       
  2163 		chuck(t);
       
  2164 		if (*t == Meta)
       
  2165 		    t++;
       
  2166 		continue;
       
  2167 	    }
       
  2168 	    if (*t == Meta) {
       
  2169 		if (isep(t[1] ^ 32))
       
  2170 		    break;
       
  2171 		t++;
       
  2172 	    } else if (isep(*t))
       
  2173 		break;
       
  2174 	}
       
  2175 	i = t - *s;
       
  2176 	*s = t;
       
  2177 	return i;
       
  2178     }
       
  2179     if (!sep[0]) {
       
  2180 	if (**s) {
       
  2181 	    if (**s == Meta)
       
  2182 		*s += 2;
       
  2183 	    else
       
  2184 		++*s;
       
  2185 	    return 1;
       
  2186 	}
       
  2187 	return -1;
       
  2188     }
       
  2189     for (i = 0; **s; i++) {
       
  2190 	for (t = sep, tt = *s; *t && *tt && *t == *tt; t++, tt++);
       
  2191 	if (!*t)
       
  2192 	    return i;
       
  2193 	if (*(*s)++ == Meta) {
       
  2194 #ifdef DEBUG
       
  2195 	    if (! *(*s)++)
       
  2196 		fprintf(stderr, "BUG: unexpected end of string in findsep()\n");
       
  2197 #else
       
  2198 	    (*s)++;
       
  2199 #endif
       
  2200 	}
       
  2201     }
       
  2202     return -1;
       
  2203 }
       
  2204 
       
  2205 /**/
       
  2206 char *
       
  2207 findword(char **s, char *sep)
       
  2208 {
       
  2209     char *r, *t;
       
  2210     int sl;
       
  2211 
       
  2212     if (!**s)
       
  2213 	return NULL;
       
  2214 
       
  2215     if (sep) {
       
  2216 	sl = strlen(sep);
       
  2217 	r = *s;
       
  2218 	while (! findsep(s, sep, 0)) {
       
  2219 	    r = *s += sl;
       
  2220 	}
       
  2221 	return r;
       
  2222     }
       
  2223     for (t = *s; *t; t++) {
       
  2224 	if (*t == Meta) {
       
  2225 	    if (! isep(t[1] ^ 32))
       
  2226 		break;
       
  2227 	    t++;
       
  2228 	} else if (! isep(*t))
       
  2229 	    break;
       
  2230     }
       
  2231     *s = t;
       
  2232     findsep(s, sep, 0);
       
  2233     return t;
       
  2234 }
       
  2235 
       
  2236 /**/
       
  2237 int
       
  2238 wordcount(char *s, char *sep, int mul)
       
  2239 {
       
  2240     int r, sl, c;
       
  2241 
       
  2242     if (sep) {
       
  2243 	r = 1;
       
  2244 	sl = strlen(sep);
       
  2245 	for (; (c = findsep(&s, sep, 0)) >= 0; s += sl)
       
  2246 	    if ((c && *(s + sl)) || mul)
       
  2247 		r++;
       
  2248     } else {
       
  2249 	char *t = s;
       
  2250 
       
  2251 	r = 0;
       
  2252 	if (mul <= 0)
       
  2253 	    skipwsep(&s);
       
  2254 	if ((*s && isep(*s == Meta ? s[1] ^ 32 : *s)) ||
       
  2255 	    (mul < 0 && t != s))
       
  2256 	    r++;
       
  2257 	for (; *s; r++) {
       
  2258 	    if (isep(*s == Meta ? s[1] ^ 32 : *s)) {
       
  2259 		if (*s == Meta)
       
  2260 		    s++;
       
  2261 		s++;
       
  2262 		if (mul <= 0)
       
  2263 		    skipwsep(&s);
       
  2264 	    }
       
  2265 	    findsep(&s, NULL, 0);
       
  2266 	    t = s;
       
  2267 	    if (mul <= 0)
       
  2268 		skipwsep(&s);
       
  2269 	}
       
  2270 	if (mul < 0 && t != s)
       
  2271 	    r++;
       
  2272     }
       
  2273     return r;
       
  2274 }
       
  2275 
       
  2276 /**/
       
  2277 mod_export char *
       
  2278 sepjoin(char **s, char *sep, int heap)
       
  2279 {
       
  2280     char *r, *p, **t;
       
  2281     int l, sl;
       
  2282     char sepbuf[3];
       
  2283 
       
  2284     if (!*s)
       
  2285 	return heap ? "" : ztrdup("");
       
  2286     if (!sep) {
       
  2287 	p = sep = sepbuf;
       
  2288 	if (ifs) {
       
  2289 	    *p++ = *ifs;
       
  2290 	    *p++ = *ifs == Meta ? ifs[1] ^ 32 : '\0';
       
  2291 	} else {
       
  2292 	    *p++ = ' ';
       
  2293 	}
       
  2294 	*p = '\0';
       
  2295     }
       
  2296     sl = strlen(sep);
       
  2297     for (t = s, l = 1 - sl; *t; l += strlen(*t) + sl, t++);
       
  2298     r = p = (heap ? (char *) hcalloc(l) : (char *) zshcalloc(l));
       
  2299     t = s;
       
  2300     while (*t) {
       
  2301 	strucpy(&p, *t);
       
  2302 	if (*++t)
       
  2303 	    strucpy(&p, sep);
       
  2304     }
       
  2305     *p = '\0';
       
  2306     return r;
       
  2307 }
       
  2308 
       
  2309 /**/
       
  2310 char **
       
  2311 sepsplit(char *s, char *sep, int allownull, int heap)
       
  2312 {
       
  2313     int n, sl;
       
  2314     char *t, *tt, **r, **p;
       
  2315 
       
  2316     if (!sep)
       
  2317 	return spacesplit(s, allownull, heap, 0);
       
  2318 
       
  2319     sl = strlen(sep);
       
  2320     n = wordcount(s, sep, 1);
       
  2321     r = p = (heap ? (char **) hcalloc((n + 1) * sizeof(char *)) :
       
  2322 	     (char **) zshcalloc((n + 1) * sizeof(char *)));
       
  2323 
       
  2324     for (t = s; n--;) {
       
  2325 	tt = t;
       
  2326 	findsep(&t, sep, 0);
       
  2327 	*p = (heap ? (char *) hcalloc(t - tt + 1) :
       
  2328 	      (char *) zshcalloc(t - tt + 1));
       
  2329 	strncpy(*p, tt, t - tt);
       
  2330 	(*p)[t - tt] = '\0';
       
  2331 	p++;
       
  2332 	t += sl;
       
  2333     }
       
  2334     *p = NULL;
       
  2335 
       
  2336     return r;
       
  2337 }
       
  2338 
       
  2339 /* Get the definition of a shell function */
       
  2340 
       
  2341 /**/
       
  2342 mod_export Eprog
       
  2343 getshfunc(char *nam)
       
  2344 {
       
  2345     Shfunc shf;
       
  2346 
       
  2347     if (!(shf = (Shfunc) shfunctab->getnode(shfunctab, nam)))
       
  2348 	return &dummy_eprog;
       
  2349 
       
  2350     return shf->funcdef;
       
  2351 }
       
  2352 
       
  2353 /**/
       
  2354 char **
       
  2355 mkarray(char *s)
       
  2356 {
       
  2357     char **t = (char **) zalloc((s) ? (2 * sizeof s) : (sizeof s));
       
  2358 
       
  2359     if ((*t = s))
       
  2360 	t[1] = NULL;
       
  2361     return t;
       
  2362 }
       
  2363 
       
  2364 /**/
       
  2365 mod_export void
       
  2366 zbeep(void)
       
  2367 {
       
  2368     char *vb;
       
  2369     queue_signals();
       
  2370     if ((vb = getsparam("ZBEEP"))) {
       
  2371 	int len;
       
  2372 	vb = getkeystring(vb, &len, 2, NULL);
       
  2373 	write(SHTTY, vb, len);
       
  2374     } else if (isset(BEEP))
       
  2375 	write(SHTTY, "\07", 1);
       
  2376     unqueue_signals();
       
  2377 }
       
  2378 
       
  2379 /**/
       
  2380 mod_export void
       
  2381 freearray(char **s)
       
  2382 {
       
  2383     char **t = s;
       
  2384 
       
  2385     DPUTS(!s, "freearray() with zero argument");
       
  2386 
       
  2387     while (*s)
       
  2388 	zsfree(*s++);
       
  2389     free(t);
       
  2390 }
       
  2391 
       
  2392 /**/
       
  2393 int
       
  2394 equalsplit(char *s, char **t)
       
  2395 {
       
  2396     for (; *s && *s != '='; s++);
       
  2397     if (*s == '=') {
       
  2398 	*s++ = '\0';
       
  2399 	*t = s;
       
  2400 	return 1;
       
  2401     }
       
  2402     return 0;
       
  2403 }
       
  2404 
       
  2405 /* the ztypes table */
       
  2406 
       
  2407 /**/
       
  2408 mod_export short int typtab[256];
       
  2409 
       
  2410 /* initialize the ztypes table */
       
  2411 
       
  2412 /**/
       
  2413 void
       
  2414 inittyptab(void)
       
  2415 {
       
  2416     int t0;
       
  2417     char *s;
       
  2418 
       
  2419     for (t0 = 0; t0 != 256; t0++)
       
  2420 	typtab[t0] = 0;
       
  2421     for (t0 = 0; t0 != 32; t0++)
       
  2422 	typtab[t0] = typtab[t0 + 128] = ICNTRL;
       
  2423     typtab[127] = ICNTRL;
       
  2424     for (t0 = '0'; t0 <= '9'; t0++)
       
  2425 	typtab[t0] = IDIGIT | IALNUM | IWORD | IIDENT | IUSER;
       
  2426     for (t0 = 'a'; t0 <= 'z'; t0++)
       
  2427 	typtab[t0] = typtab[t0 - 'a' + 'A'] = IALPHA | IALNUM | IIDENT | IUSER | IWORD;
       
  2428     for (t0 = 0240; t0 != 0400; t0++)
       
  2429 	typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD;
       
  2430     typtab['_'] = IIDENT | IUSER;
       
  2431     typtab['-'] = IUSER;
       
  2432     typtab[' '] |= IBLANK | INBLANK;
       
  2433     typtab['\t'] |= IBLANK | INBLANK;
       
  2434     typtab['\n'] |= INBLANK;
       
  2435     typtab['\0'] |= IMETA;
       
  2436     typtab[STOUC(Meta)  ] |= IMETA;
       
  2437     typtab[STOUC(Marker)] |= IMETA;
       
  2438     for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(Nularg); t0++)
       
  2439 	typtab[t0] |= ITOK | IMETA;
       
  2440     for (s = ifs ? ifs : DEFAULT_IFS; *s; s++) {
       
  2441 	if (inblank(*s)) {
       
  2442 	    if (s[1] == *s)
       
  2443 		s++;
       
  2444 	    else
       
  2445 		typtab[STOUC(*s)] |= IWSEP;
       
  2446 	}
       
  2447 	typtab[STOUC(*s == Meta ? *++s ^ 32 : *s)] |= ISEP;
       
  2448     }
       
  2449     for (s = wordchars ? wordchars : DEFAULT_WORDCHARS; *s; s++)
       
  2450 	typtab[STOUC(*s == Meta ? *++s ^ 32 : *s)] |= IWORD;
       
  2451     for (s = SPECCHARS; *s; s++)
       
  2452 	typtab[STOUC(*s)] |= ISPECIAL;
       
  2453     if (isset(BANGHIST) && bangchar && interact && isset(SHINSTDIN))
       
  2454 	typtab[bangchar] |= ISPECIAL;
       
  2455 }
       
  2456 
       
  2457 /**/
       
  2458 mod_export char **
       
  2459 arrdup(char **s)
       
  2460 {
       
  2461     char **x, **y;
       
  2462 
       
  2463     y = x = (char **) zhalloc(sizeof(char *) * (arrlen(s) + 1));
       
  2464 
       
  2465     while ((*x++ = dupstring(*s++)));
       
  2466 
       
  2467     return y;
       
  2468 }
       
  2469 
       
  2470 /**/
       
  2471 mod_export char **
       
  2472 zarrdup(char **s)
       
  2473 {
       
  2474     char **x, **y;
       
  2475 
       
  2476     y = x = (char **) zalloc(sizeof(char *) * (arrlen(s) + 1));
       
  2477 
       
  2478     while ((*x++ = ztrdup(*s++)));
       
  2479 
       
  2480     return y;
       
  2481 }
       
  2482 
       
  2483 /**/
       
  2484 static char *
       
  2485 spname(char *oldname)
       
  2486 {
       
  2487     char *p, spnameguess[PATH_MAX + 1], spnamebest[PATH_MAX + 1];
       
  2488     static char newname[PATH_MAX + 1];
       
  2489     char *new = newname, *old;
       
  2490     int bestdist = 200, thisdist;
       
  2491 
       
  2492     old = oldname;
       
  2493     for (;;) {
       
  2494 	while (*old == '/')
       
  2495 	    *new++ = *old++;
       
  2496 	*new = '\0';
       
  2497 	if (*old == '\0')
       
  2498 	    return newname;
       
  2499 	p = spnameguess;
       
  2500 	for (; *old != '/' && *old != '\0'; old++)
       
  2501 	    if (p < spnameguess + PATH_MAX)
       
  2502 		*p++ = *old;
       
  2503 	*p = '\0';
       
  2504 	if ((thisdist = mindist(newname, spnameguess, spnamebest)) >= 3) {
       
  2505 	    if (bestdist < 3) {
       
  2506 		strcpy(new, spnameguess);
       
  2507 		strcat(new, old);
       
  2508 		return newname;
       
  2509 	    } else
       
  2510 	    	return NULL;
       
  2511 	} else
       
  2512 	    bestdist = thisdist;
       
  2513 	for (p = spnamebest; (*new = *p++);)
       
  2514 	    new++;
       
  2515     }
       
  2516 }
       
  2517 
       
  2518 /**/
       
  2519 static int
       
  2520 mindist(char *dir, char *mindistguess, char *mindistbest)
       
  2521 {
       
  2522     int mindistd, nd;
       
  2523     DIR *dd;
       
  2524     char *fn;
       
  2525     char buf[PATH_MAX];
       
  2526 
       
  2527     if (dir[0] == '\0')
       
  2528 	dir = ".";
       
  2529     mindistd = 100;
       
  2530     sprintf(buf, "%s/%s", dir, mindistguess);
       
  2531     if (access(unmeta(buf), F_OK) == 0) {
       
  2532 	strcpy(mindistbest, mindistguess);
       
  2533 	return 0;
       
  2534     }
       
  2535     if (!(dd = opendir(unmeta(dir))))
       
  2536 	return mindistd;
       
  2537     while ((fn = zreaddir(dd, 0))) {
       
  2538 	nd = spdist(fn, mindistguess,
       
  2539 		    (int)strlen(mindistguess) / 4 + 1);
       
  2540 	if (nd <= mindistd) {
       
  2541 	    strcpy(mindistbest, fn);
       
  2542 	    mindistd = nd;
       
  2543 	    if (mindistd == 0)
       
  2544 		break;
       
  2545 	}
       
  2546     }
       
  2547     closedir(dd);
       
  2548     return mindistd;
       
  2549 }
       
  2550 
       
  2551 /**/
       
  2552 static int
       
  2553 spdist(char *s, char *t, int thresh)
       
  2554 {
       
  2555     char *p, *q;
       
  2556     const char qwertykeymap[] =
       
  2557     "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
       
  2558 \t1234567890-=\t\
       
  2559 \tqwertyuiop[]\t\
       
  2560 \tasdfghjkl;'\n\t\
       
  2561 \tzxcvbnm,./\t\t\t\
       
  2562 \n\n\n\n\n\n\n\n\n\n\n\n\n\n\
       
  2563 \t!@#$%^&*()_+\t\
       
  2564 \tQWERTYUIOP{}\t\
       
  2565 \tASDFGHJKL:\"\n\t\
       
  2566 \tZXCVBNM<>?\n\n\t\
       
  2567 \n\n\n\n\n\n\n\n\n\n\n\n\n\n";
       
  2568     const char dvorakkeymap[] =
       
  2569     "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
       
  2570 \t1234567890[]\t\
       
  2571 \t',.pyfgcrl/=\t\
       
  2572 \taoeuidhtns-\n\t\
       
  2573 \t;qjkxbmwvz\t\t\t\
       
  2574 \n\n\n\n\n\n\n\n\n\n\n\n\n\n\
       
  2575 \t!@#$%^&*(){}\t\
       
  2576 \t\"<>PYFGCRL?+\t\
       
  2577 \tAOEUIDHTNS_\n\t\
       
  2578 \t:QJKXBMWVZ\n\n\t\
       
  2579 \n\n\n\n\n\n\n\n\n\n\n\n\n\n";
       
  2580     const char *keymap;
       
  2581     if ( isset( DVORAK ) )
       
  2582       keymap = dvorakkeymap;
       
  2583     else
       
  2584       keymap = qwertykeymap;
       
  2585 
       
  2586     if (!strcmp(s, t))
       
  2587 	return 0;
       
  2588 /* any number of upper/lower mistakes allowed (dist = 1) */
       
  2589     for (p = s, q = t; *p && tulower(*p) == tulower(*q); p++, q++);
       
  2590     if (!*p && !*q)
       
  2591 	return 1;
       
  2592     if (!thresh)
       
  2593 	return 200;
       
  2594     for (p = s, q = t; *p && *q; p++, q++)
       
  2595 	if (*p == *q)
       
  2596 	    continue;		/* don't consider "aa" transposed, ash */
       
  2597 	else if (p[1] == q[0] && q[1] == p[0])	/* transpositions */
       
  2598 	    return spdist(p + 2, q + 2, thresh - 1) + 1;
       
  2599 	else if (p[1] == q[0])	/* missing letter */
       
  2600 	    return spdist(p + 1, q + 0, thresh - 1) + 2;
       
  2601 	else if (p[0] == q[1])	/* missing letter */
       
  2602 	    return spdist(p + 0, q + 1, thresh - 1) + 2;
       
  2603 	else if (*p != *q)
       
  2604 	    break;
       
  2605     if ((!*p && strlen(q) == 1) || (!*q && strlen(p) == 1))
       
  2606 	return 2;
       
  2607     for (p = s, q = t; *p && *q; p++, q++)
       
  2608 	if (p[0] != q[0] && p[1] == q[1]) {
       
  2609 	    int t0;
       
  2610 	    char *z;
       
  2611 
       
  2612 	/* mistyped letter */
       
  2613 
       
  2614 	    if (!(z = strchr(keymap, p[0])) || *z == '\n' || *z == '\t')
       
  2615 		return spdist(p + 1, q + 1, thresh - 1) + 1;
       
  2616 	    t0 = z - keymap;
       
  2617 	    if (*q == keymap[t0 - 15] || *q == keymap[t0 - 14] ||
       
  2618 		*q == keymap[t0 - 13] ||
       
  2619 		*q == keymap[t0 - 1] || *q == keymap[t0 + 1] ||
       
  2620 		*q == keymap[t0 + 13] || *q == keymap[t0 + 14] ||
       
  2621 		*q == keymap[t0 + 15])
       
  2622 		return spdist(p + 1, q + 1, thresh - 1) + 2;
       
  2623 	    return 200;
       
  2624 	} else if (*p != *q)
       
  2625 	    break;
       
  2626     return 200;
       
  2627 }
       
  2628 
       
  2629 /* set cbreak mode, or the equivalent */
       
  2630 
       
  2631 /**/
       
  2632 void
       
  2633 setcbreak(void)
       
  2634 {
       
  2635     struct ttyinfo ti;
       
  2636 
       
  2637     ti = shttyinfo;
       
  2638 #ifdef HAS_TIO
       
  2639     ti.tio.c_lflag &= ~ICANON;
       
  2640     ti.tio.c_cc[VMIN] = 1;
       
  2641     ti.tio.c_cc[VTIME] = 0;
       
  2642 #else
       
  2643     ti.sgttyb.sg_flags |= CBREAK;
       
  2644 #endif
       
  2645     settyinfo(&ti);
       
  2646 }
       
  2647 
       
  2648 /* give the tty to some process */
       
  2649 
       
  2650 /**/
       
  2651 mod_export void
       
  2652 attachtty(pid_t pgrp)
       
  2653 {
       
  2654     static int ep = 0;
       
  2655 
       
  2656     if (jobbing) {
       
  2657 #ifdef HAVE_TCSETPGRP
       
  2658 	if (SHTTY != -1 && tcsetpgrp(SHTTY, pgrp) == -1 && !ep)
       
  2659 #else
       
  2660 # if ardent
       
  2661 	if (SHTTY != -1 && setpgrp() == -1 && !ep)
       
  2662 # else
       
  2663 	int arg = pgrp;
       
  2664 
       
  2665 	if (SHTTY != -1 && ioctl(SHTTY, TIOCSPGRP, &arg) == -1 && !ep)
       
  2666 # endif
       
  2667 #endif
       
  2668 	{
       
  2669 	    if (pgrp != mypgrp && kill(-pgrp, 0) == -1)
       
  2670 		attachtty(mypgrp);
       
  2671 	    else {
       
  2672 		if (errno != ENOTTY)
       
  2673 		{
       
  2674 		    zwarn("can't set tty pgrp: %e", NULL, errno);
       
  2675 		    fflush(stderr);
       
  2676 		}
       
  2677 		opts[MONITOR] = 0;
       
  2678 		ep = 1;
       
  2679 	    }
       
  2680 	}
       
  2681     }
       
  2682 }
       
  2683 
       
  2684 /* get the process group associated with the tty */
       
  2685 
       
  2686 /**/
       
  2687 pid_t
       
  2688 gettygrp(void)
       
  2689 {
       
  2690     pid_t arg;
       
  2691 
       
  2692     if (SHTTY == -1)
       
  2693 	return -1;
       
  2694 
       
  2695 #ifdef HAVE_TCSETPGRP
       
  2696     arg = tcgetpgrp(SHTTY);
       
  2697 #else
       
  2698     ioctl(SHTTY, TIOCGPGRP, &arg);
       
  2699 #endif
       
  2700 
       
  2701     return arg;
       
  2702 }
       
  2703 
       
  2704 /* Return the output baudrate */
       
  2705 
       
  2706 #ifdef HAVE_SELECT
       
  2707 /**/
       
  2708 long
       
  2709 getbaudrate(struct ttyinfo *shttyinfo)
       
  2710 {
       
  2711     long speedcode;
       
  2712 
       
  2713 #ifdef HAS_TIO
       
  2714 # if defined(HAVE_TCGETATTR) && defined(HAVE_TERMIOS_H)
       
  2715     speedcode = cfgetospeed(&shttyinfo->tio);
       
  2716 # else
       
  2717     speedcode = shttyinfo->tio.c_cflag & CBAUD;
       
  2718 # endif
       
  2719 #else
       
  2720     speedcode = shttyinfo->sgttyb.sg_ospeed;
       
  2721 #endif
       
  2722 
       
  2723     switch (speedcode) {
       
  2724     case B0:
       
  2725 	return (0L);
       
  2726     case B50:
       
  2727 	return (50L);
       
  2728     case B75:
       
  2729 	return (75L);
       
  2730     case B110:
       
  2731 	return (110L);
       
  2732     case B134:
       
  2733 	return (134L);
       
  2734     case B150:
       
  2735 	return (150L);
       
  2736     case B200:
       
  2737 	return (200L);
       
  2738     case B300:
       
  2739 	return (300L);
       
  2740     case B600:
       
  2741 	return (600L);
       
  2742 #ifdef _B900
       
  2743     case _B900:
       
  2744 	return (900L);
       
  2745 #endif
       
  2746     case B1200:
       
  2747 	return (1200L);
       
  2748     case B1800:
       
  2749 	return (1800L);
       
  2750     case B2400:
       
  2751 	return (2400L);
       
  2752 #ifdef _B3600
       
  2753     case _B3600:
       
  2754 	return (3600L);
       
  2755 #endif
       
  2756     case B4800:
       
  2757 	return (4800L);
       
  2758 #ifdef _B7200
       
  2759     case _B7200:
       
  2760 	return (7200L);
       
  2761 #endif
       
  2762     case B9600:
       
  2763 	return (9600L);
       
  2764 #ifdef B19200
       
  2765     case B19200:
       
  2766 	return (19200L);
       
  2767 #else
       
  2768 # ifdef EXTA
       
  2769     case EXTA:
       
  2770 	return (19200L);
       
  2771 # endif
       
  2772 #endif
       
  2773 #ifdef B38400
       
  2774     case B38400:
       
  2775 	return (38400L);
       
  2776 #else
       
  2777 # ifdef EXTB
       
  2778     case EXTB:
       
  2779 	return (38400L);
       
  2780 # endif
       
  2781 #endif
       
  2782 #ifdef B57600
       
  2783     case B57600:
       
  2784 	return (57600L);
       
  2785 #endif
       
  2786 #ifdef B115200
       
  2787     case B115200:
       
  2788 	return (115200L);
       
  2789 #endif
       
  2790 #ifdef B230400
       
  2791     case B230400:
       
  2792 	return (230400L);
       
  2793 #endif
       
  2794 #ifdef B460800
       
  2795     case B460800:
       
  2796 	return (460800L);
       
  2797 #endif
       
  2798     default:
       
  2799 	if (speedcode >= 100)
       
  2800 	    return speedcode;
       
  2801 	break;
       
  2802     }
       
  2803     return (0L);
       
  2804 }
       
  2805 #endif
       
  2806 
       
  2807 /* Escape tokens and null characters.  Buf is the string which should be     *
       
  2808  * escaped.  len is the length of the string.  If len is -1, buf should be   *
       
  2809  * null terminated.  If len is non-negative and the third parameter is not   *
       
  2810  * META_DUP, buf should point to an at least len+1 long memory area.  The    *
       
  2811  * return value points to the quoted string.  If the given string does not   *
       
  2812  * contain any special character which should be quoted and the third        *
       
  2813  * parameter is not META_(HEAP|)DUP, buf is returned unchanged (a            *
       
  2814  * terminating null character is appended to buf if necessary).  Otherwise   *
       
  2815  * the third `heap' argument determines the method used to allocate space    *
       
  2816  * for the result.  It can have the following values:                        *
       
  2817  *   META_REALLOC:  use zrealloc on buf                                      *
       
  2818  *   META_HREALLOC: use hrealloc on buf                                      *
       
  2819  *   META_USEHEAP:  get memory from the heap.  This leaves buf unchanged.    *
       
  2820  *   META_NOALLOC:  buf points to a memory area which is long enough to hold *
       
  2821  *                  the quoted form, just quote it and return buf.           *
       
  2822  *   META_STATIC:   store the quoted string in a static area.  The original  *
       
  2823  *                  string should be at most PATH_MAX long.                   *
       
  2824  *   META_ALLOC:    allocate memory for the new string with zalloc().        *
       
  2825  *   META_DUP:      leave buf unchanged and allocate space for the return    *
       
  2826  *                  value even if buf does not contains special characters   *
       
  2827  *   META_HEAPDUP:  same as META_DUP, but uses the heap                      */
       
  2828 
       
  2829 /**/
       
  2830 mod_export char *
       
  2831 metafy(char *buf, int len, int heap)
       
  2832 {
       
  2833     int meta = 0;
       
  2834     char *t, *p, *e;
       
  2835     static char mbuf[PATH_MAX*2+1];
       
  2836 
       
  2837     if (len == -1) {
       
  2838 	for (e = buf, len = 0; *e; len++)
       
  2839 	    if (imeta(*e++))
       
  2840 		meta++;
       
  2841     } else
       
  2842 	for (e = buf; e < buf + len;)
       
  2843 	    if (imeta(*e++))
       
  2844 		meta++;
       
  2845 
       
  2846     if (meta || heap == META_DUP || heap == META_HEAPDUP) {
       
  2847 	switch (heap) {
       
  2848 	case META_REALLOC:
       
  2849 	    buf = zrealloc(buf, len + meta + 1);
       
  2850 	    break;
       
  2851 	case META_HREALLOC:
       
  2852 	    buf = hrealloc(buf, len, len + meta + 1);
       
  2853 	    break;
       
  2854 	case META_ALLOC:
       
  2855 	case META_DUP:
       
  2856 	    buf = memcpy(zalloc(len + meta + 1), buf, len);
       
  2857 	    break;
       
  2858 	case META_USEHEAP:
       
  2859 	case META_HEAPDUP:
       
  2860 	    buf = memcpy(zhalloc(len + meta + 1), buf, len);
       
  2861 	    break;
       
  2862 	case META_STATIC:
       
  2863 #ifdef DEBUG
       
  2864 	    if (len > PATH_MAX) {
       
  2865 		fprintf(stderr, "BUG: len = %d > PATH_MAX in metafy\n", len);
       
  2866 		fflush(stderr);
       
  2867 	    }
       
  2868 #endif
       
  2869 	    buf = memcpy(mbuf, buf, len);
       
  2870 	    break;
       
  2871 #ifdef DEBUG
       
  2872 	case META_NOALLOC:
       
  2873 	    break;
       
  2874 	default:
       
  2875 	    fprintf(stderr, "BUG: metafy called with invalid heap value\n");
       
  2876 	    fflush(stderr);
       
  2877 	    break;
       
  2878 #endif
       
  2879 	}
       
  2880 	p = buf + len;
       
  2881 	e = t = buf + len + meta;
       
  2882 	while (meta) {
       
  2883 	    if (imeta(*--t = *--p)) {
       
  2884 		*t-- ^= 32;
       
  2885 		*t = Meta;
       
  2886 		meta--;
       
  2887 	    }
       
  2888 	}
       
  2889     }
       
  2890     *e = '\0';
       
  2891     return buf;
       
  2892 }
       
  2893 
       
  2894 /**/
       
  2895 mod_export char *
       
  2896 unmetafy(char *s, int *len)
       
  2897 {
       
  2898     char *p, *t;
       
  2899 
       
  2900     for (p = s; *p && *p != Meta; p++);
       
  2901     for (t = p; (*t = *p++);)
       
  2902 	if (*t++ == Meta)
       
  2903 	    t[-1] = *p++ ^ 32;
       
  2904     if (len)
       
  2905 	*len = t - s;
       
  2906     return s;
       
  2907 }
       
  2908 
       
  2909 /* Return the character length of a metafied substring, given the      *
       
  2910  * unmetafied substring length.                                        */
       
  2911 
       
  2912 /**/
       
  2913 mod_export int
       
  2914 metalen(const char *s, int len)
       
  2915 {
       
  2916     int mlen = len;
       
  2917 
       
  2918     while (len--) {
       
  2919 	if (*s++ == Meta) {
       
  2920 	    mlen++;
       
  2921 	    s++;
       
  2922 	}
       
  2923     }
       
  2924     return mlen;
       
  2925 }
       
  2926 
       
  2927 /* This function converts a zsh internal string to a form which can be *
       
  2928  * passed to a system call as a filename.  The result is stored in a   *
       
  2929  * single static area.  NULL returned if the result is longer than     *
       
  2930  * 4 * PATH_MAX.                                                       */
       
  2931 
       
  2932 /**/
       
  2933 mod_export char *
       
  2934 unmeta(const char *file_name)
       
  2935 {
       
  2936     static char fn[4 * PATH_MAX];
       
  2937     char *p;
       
  2938     const char *t;
       
  2939 
       
  2940     for (t = file_name, p = fn; *t && p < fn + 4 * PATH_MAX - 1; p++)
       
  2941 	if ((*p = *t++) == Meta)
       
  2942 	    *p = *t++ ^ 32;
       
  2943     if (*t)
       
  2944 	return NULL;
       
  2945     if (p - fn == t - file_name)
       
  2946 	return (char *) file_name;
       
  2947     *p = '\0';
       
  2948     return fn;
       
  2949 }
       
  2950 
       
  2951 /* Unmetafy and compare two strings, with unsigned characters. *
       
  2952  * "a\0" sorts after "a".                                      */
       
  2953 
       
  2954 /**/
       
  2955 int
       
  2956 ztrcmp(unsigned char const *s1, unsigned char const *s2)
       
  2957 {
       
  2958     int c1, c2;
       
  2959 
       
  2960     while(*s1 && *s1 == *s2) {
       
  2961 	s1++;
       
  2962 	s2++;
       
  2963     }
       
  2964 
       
  2965     if(!(c1 = *s1))
       
  2966 	c1 = -1;
       
  2967     else if(c1 == STOUC(Meta))
       
  2968 	c1 = *++s1 ^ 32;
       
  2969     if(!(c2 = *s2))
       
  2970 	c2 = -1;
       
  2971     else if(c2 == STOUC(Meta))
       
  2972 	c2 = *++s2 ^ 32;
       
  2973 
       
  2974     if(c1 == c2)
       
  2975 	return 0;
       
  2976     else if(c1 < c2)
       
  2977 	return -1;
       
  2978     else
       
  2979 	return 1;
       
  2980 }
       
  2981 
       
  2982 /* Return zero if the metafied string s and the non-metafied,  *
       
  2983  * len-long string r are the same.  Return -1 if r is a prefix *
       
  2984  * of s.  Return 1 if r is the lowercase version of s.  Return *
       
  2985  * 2 is r is the lowercase prefix of s and return 3 otherwise. */
       
  2986 
       
  2987 /**/
       
  2988 mod_export int
       
  2989 metadiffer(char const *s, char const *r, int len)
       
  2990 {
       
  2991     int l = len;
       
  2992 
       
  2993     while (l-- && *s && *r++ == (*s == Meta ? *++s ^ 32 : *s))
       
  2994 	s++;
       
  2995     if (*s && l < 0)
       
  2996 	return -1;
       
  2997     if (l < 0)
       
  2998 	return 0;
       
  2999     if (!*s)
       
  3000 	return 3;
       
  3001     s -= len - l - 1;
       
  3002     r -= len - l;
       
  3003     while (len-- && *s && *r++ == tulower(*s == Meta ? *++s ^ 32 : *s))
       
  3004 	s++;
       
  3005     if (*s && len < 0)
       
  3006 	return 2;
       
  3007     if (len < 0)
       
  3008 	return 1;
       
  3009     return 3;
       
  3010 }
       
  3011 
       
  3012 /* Return the unmetafied length of a metafied string. */
       
  3013 
       
  3014 /**/
       
  3015 mod_export int
       
  3016 ztrlen(char const *s)
       
  3017 {
       
  3018     int l;
       
  3019 
       
  3020     for (l = 0; *s; l++)
       
  3021 	if (*s++ == Meta) {
       
  3022 #ifdef DEBUG
       
  3023 	    if (! *s)
       
  3024 		fprintf(stderr, "BUG: unexpected end of string in ztrlen()\n");
       
  3025 	    else
       
  3026 #endif
       
  3027 	    s++;
       
  3028 	}
       
  3029     return l;
       
  3030 }
       
  3031 
       
  3032 /* Subtract two pointers in a metafied string. */
       
  3033 
       
  3034 /**/
       
  3035 mod_export int
       
  3036 ztrsub(char const *t, char const *s)
       
  3037 {
       
  3038     int l = t - s;
       
  3039 
       
  3040     while (s != t)
       
  3041 	if (*s++ == Meta) {
       
  3042 #ifdef DEBUG
       
  3043 	    if (! *s || s == t)
       
  3044 		fprintf(stderr, "BUG: substring ends in the middle of a metachar in ztrsub()\n");
       
  3045 	    else
       
  3046 #endif
       
  3047 	    s++;
       
  3048 	    l--;
       
  3049 	}
       
  3050     return l;
       
  3051 }
       
  3052 
       
  3053 /**/
       
  3054 mod_export char *
       
  3055 zreaddir(DIR *dir, int ignoredots)
       
  3056 {
       
  3057     struct dirent *de;
       
  3058 
       
  3059     do {
       
  3060 	de = readdir(dir);
       
  3061 	if(!de)
       
  3062 	    return NULL;
       
  3063     } while(ignoredots && de->d_name[0] == '.' &&
       
  3064 	(!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])));
       
  3065 
       
  3066     return metafy(de->d_name, -1, META_STATIC);
       
  3067 }
       
  3068 
       
  3069 /* Unmetafy and output a string.  Tokens are skipped. */
       
  3070 
       
  3071 /**/
       
  3072 mod_export int
       
  3073 zputs(char const *s, FILE *stream)
       
  3074 {
       
  3075     int c;
       
  3076 
       
  3077     while (*s) {
       
  3078 	if (*s == Meta)
       
  3079 	    c = *++s ^ 32;
       
  3080 	else if(itok(*s)) {
       
  3081 	    s++;
       
  3082 	    continue;
       
  3083 	} else
       
  3084 	    c = *s;
       
  3085 	s++;
       
  3086 	if (fputc(c, stream) < 0)
       
  3087 	    return EOF;
       
  3088     }
       
  3089     return 0;
       
  3090 }
       
  3091 
       
  3092 /* Create a visibly-represented duplicate of a string. */
       
  3093 
       
  3094 /**/
       
  3095 static char *
       
  3096 nicedup(char const *s, int heap)
       
  3097 {
       
  3098     int c, len = strlen(s) * 5;
       
  3099     VARARR(char, buf, len);
       
  3100     char *p = buf, *n;
       
  3101 
       
  3102     while ((c = *s++)) {
       
  3103 	if (itok(c)) {
       
  3104 	    if (c <= Comma)
       
  3105 		c = ztokens[c - Pound];
       
  3106 	    else 
       
  3107 		continue;
       
  3108 	}
       
  3109 	if (c == Meta)
       
  3110 	    c = *s++ ^ 32;
       
  3111 	n = nicechar(c);
       
  3112 	while(*n)
       
  3113 	    *p++ = *n++;
       
  3114     }
       
  3115     return metafy(buf, p - buf, (heap ? META_HEAPDUP : META_DUP));
       
  3116 }
       
  3117 
       
  3118 /**/
       
  3119 mod_export char *
       
  3120 niceztrdup(char const *s)
       
  3121 {
       
  3122     return nicedup(s, 0);
       
  3123 }
       
  3124 
       
  3125 /**/
       
  3126 mod_export char *
       
  3127 nicedupstring(char const *s)
       
  3128 {
       
  3129     return nicedup(s, 1);
       
  3130 }
       
  3131 
       
  3132 /* Unmetafy and output a string, displaying special characters readably. */
       
  3133 
       
  3134 /**/
       
  3135 mod_export int
       
  3136 nicezputs(char const *s, FILE *stream)
       
  3137 {
       
  3138     int c;
       
  3139 
       
  3140     while ((c = *s++)) {
       
  3141 	if (itok(c)) {
       
  3142 	    if (c <= Comma)
       
  3143 		c = ztokens[c - Pound];
       
  3144 	    else 
       
  3145 		continue;
       
  3146 	}
       
  3147 	if (c == Meta)
       
  3148 	    c = *s++ ^ 32;
       
  3149 	if(fputs(nicechar(c), stream) < 0)
       
  3150 	    return EOF;
       
  3151     }
       
  3152     return 0;
       
  3153 }
       
  3154 
       
  3155 /* Return the length of the visible representation of a metafied string. */
       
  3156 
       
  3157 /**/
       
  3158 mod_export size_t
       
  3159 niceztrlen(char const *s)
       
  3160 {
       
  3161     size_t l = 0;
       
  3162     int c;
       
  3163 
       
  3164     while ((c = *s++)) {
       
  3165 	if (itok(c)) {
       
  3166 	    if (c <= Comma)
       
  3167 		c = ztokens[c - Pound];
       
  3168 	    else 
       
  3169 		continue;
       
  3170 	}
       
  3171 	if (c == Meta)
       
  3172 	    c = *s++ ^ 32;
       
  3173 	l += strlen(nicechar(STOUC(c)));
       
  3174     }
       
  3175     return l;
       
  3176 }
       
  3177 
       
  3178 /* check for special characters in the string */
       
  3179 
       
  3180 /**/
       
  3181 mod_export int
       
  3182 hasspecial(char const *s)
       
  3183 {
       
  3184     for (; *s; s++)
       
  3185 	if (ispecial(*s == Meta ? *++s ^ 32 : *s))
       
  3186 	    return 1;
       
  3187     return 0;
       
  3188 }
       
  3189 
       
  3190 /* Quote the string s and return the result.  If e is non-zero, the         *
       
  3191  * pointer it points to may point to a position in s and in e the position  *
       
  3192  * of the corresponding character in the quoted string is returned.         *
       
  3193  * The last argument should be zero if this is to be used outside a string, *
       
  3194  * one if it is to be quoted for the inside of a single quoted string,      *
       
  3195  * two if it is for the inside of a double quoted string, and               *
       
  3196  * three if it is for the inside of a posix quoted string.                  *
       
  3197  * The string may be metafied and contain tokens.                           */
       
  3198 
       
  3199 /**/
       
  3200 mod_export char *
       
  3201 bslashquote(const char *s, char **e, int instring)
       
  3202 {
       
  3203     const char *u, *tt;
       
  3204     char *v;
       
  3205     char *buf = hcalloc(4 * strlen(s) + 1);
       
  3206     int sf = 0;
       
  3207 
       
  3208     tt = v = buf;
       
  3209     u = s;
       
  3210     for (; *u; u++) {
       
  3211 	if (e && *e == u)
       
  3212 	    *e = v, sf = 1;
       
  3213 	if (instring == 3) {
       
  3214 	  int c = *u;
       
  3215 	  if (c == Meta) {
       
  3216 	    c = *++u ^ 32;
       
  3217 	  }
       
  3218 	  c &= 0xff;
       
  3219 	  if(isprint(c)) {
       
  3220 	    switch (c) {
       
  3221 	    case '\\':
       
  3222 	    case '\'':
       
  3223 	      *v++ = '\\';
       
  3224 	      *v++ = c;
       
  3225 	      break;
       
  3226 
       
  3227 	    default:
       
  3228 	      if(imeta(c)) {
       
  3229 		*v++ = Meta;
       
  3230 		*v++ = c ^ 32;
       
  3231 	      }
       
  3232 	      else {
       
  3233 		if (isset(BANGHIST) && c == bangchar) {
       
  3234 		  *v++ = '\\';
       
  3235 		}
       
  3236 		*v++ = c;
       
  3237 	      }
       
  3238 	      break;
       
  3239 	    }
       
  3240 	  }
       
  3241 	  else {
       
  3242 	    switch (c) {
       
  3243 	    case '\0':
       
  3244 	      *v++ = '\\';
       
  3245 	      *v++ = '0';
       
  3246 	      if ('0' <= u[1] && u[1] <= '7') {
       
  3247 		*v++ = '0';
       
  3248 		*v++ = '0';
       
  3249 	      }
       
  3250 	      break;
       
  3251 
       
  3252 	    case '\007': *v++ = '\\'; *v++ = 'a'; break;
       
  3253 	    case '\b': *v++ = '\\'; *v++ = 'b'; break;
       
  3254 	    case '\f': *v++ = '\\'; *v++ = 'f'; break;
       
  3255 	    case '\n': *v++ = '\\'; *v++ = 'n'; break;
       
  3256 	    case '\r': *v++ = '\\'; *v++ = 'r'; break;
       
  3257 	    case '\t': *v++ = '\\'; *v++ = 't'; break;
       
  3258 	    case '\v': *v++ = '\\'; *v++ = 'v'; break;
       
  3259 
       
  3260 	    default:
       
  3261 	      *v++ = '\\';
       
  3262 	      *v++ = '0' + ((c >> 6) & 7);
       
  3263 	      *v++ = '0' + ((c >> 3) & 7);
       
  3264 	      *v++ = '0' + (c & 7);
       
  3265 	      break;
       
  3266 	    }
       
  3267 	  }
       
  3268 	  continue;
       
  3269 	}
       
  3270 	else if (*u == Tick || *u == Qtick) {
       
  3271 	    char c = *u++;
       
  3272 
       
  3273 	    *v++ = c;
       
  3274 	    while (*u && *u != c)
       
  3275 		*v++ = *u++;
       
  3276 	    *v++ = c;
       
  3277 	    if (!*u)
       
  3278 		u--;
       
  3279 	    continue;
       
  3280 	}
       
  3281 	else if ((*u == String || *u == Qstring) &&
       
  3282 		 (u[1] == Inpar || u[1] == Inbrack || u[1] == Inbrace)) {
       
  3283 	    char c = (u[1] == Inpar ? Outpar : (u[1] == Inbrace ?
       
  3284 						Outbrace : Outbrack));
       
  3285 	    char beg = *u;
       
  3286 	    int level = 0;
       
  3287 
       
  3288 	    *v++ = *u++;
       
  3289 	    *v++ = *u++;
       
  3290 	    while (*u && (*u != c || level)) {
       
  3291 		if (*u == beg)
       
  3292 		    level++;
       
  3293 		else if (*u == c)
       
  3294 		    level--;
       
  3295 		*v++ = *u++;
       
  3296 	    }
       
  3297 	    if (*u)
       
  3298 		*v++ = *u;
       
  3299 	    else
       
  3300 		u--;
       
  3301 	    continue;
       
  3302 	}
       
  3303 	else if (ispecial(*u) &&
       
  3304 		 ((*u != '=' && *u != '~') ||
       
  3305 		  u == s ||
       
  3306 		  (isset(MAGICEQUALSUBST) && (u[-1] == '=' || u[-1] == ':')) ||
       
  3307 		  (*u == '~' && isset(EXTENDEDGLOB))) &&
       
  3308 	    (!instring ||
       
  3309 	     (isset(BANGHIST) && *u == (char)bangchar && instring != 1) ||
       
  3310 	     (instring == 2 &&
       
  3311 	      (*u == '$' || *u == '`' || *u == '\"' || *u == '\\')) ||
       
  3312 	     (instring == 1 && *u == '\''))) {
       
  3313 	    if (*u == '\n' || (instring == 1 && *u == '\'')) {
       
  3314 		if (unset(RCQUOTES)) {
       
  3315 		    *v++ = '\'';
       
  3316 		    if (*u == '\'')
       
  3317 			*v++ = '\\';
       
  3318 		    *v++ = *u;
       
  3319 		    *v++ = '\'';
       
  3320 		} else if (*u == '\n')
       
  3321 		    *v++ = '"', *v++ = '\n', *v++ = '"';
       
  3322 		else
       
  3323 		    *v++ = '\'', *v++ = '\'';
       
  3324 		continue;
       
  3325 	    } else
       
  3326 		*v++ = '\\';
       
  3327 	}
       
  3328 	if(*u == Meta)
       
  3329 	    *v++ = *u++;
       
  3330 	*v++ = *u;
       
  3331     }
       
  3332     *v = '\0';
       
  3333 
       
  3334     if (e && *e == u)
       
  3335 	*e = v, sf = 1;
       
  3336     DPUTS(e && !sf, "BUG: Wild pointer *e in bslashquote()");
       
  3337 
       
  3338     return buf;
       
  3339 }
       
  3340 
       
  3341 /* Unmetafy and output a string, quoted if it contains special characters. */
       
  3342 
       
  3343 /**/
       
  3344 mod_export int
       
  3345 quotedzputs(char const *s, FILE *stream)
       
  3346 {
       
  3347     int inquote = 0, c;
       
  3348 
       
  3349     /* check for empty string */
       
  3350     if(!*s)
       
  3351 	return fputs("''", stream);
       
  3352 
       
  3353     if (!hasspecial(s))
       
  3354 	return zputs(s, stream);
       
  3355 
       
  3356     if (isset(RCQUOTES)) {
       
  3357 	/* use rc-style quotes-within-quotes for the whole string */
       
  3358 	if(fputc('\'', stream) < 0)
       
  3359 	    return EOF;
       
  3360 	while(*s) {
       
  3361 	    if (*s == Meta)
       
  3362 		c = *++s ^ 32;
       
  3363 	    else
       
  3364 		c = *s;
       
  3365 	    s++;
       
  3366 	    if (c == '\'') {
       
  3367 		if(fputc('\'', stream) < 0)
       
  3368 		    return EOF;
       
  3369 	    } else if(c == '\n' && isset(CSHJUNKIEQUOTES)) {
       
  3370 		if(fputc('\\', stream) < 0)
       
  3371 		    return EOF;
       
  3372 	    }
       
  3373 	    if(fputc(c, stream) < 0)
       
  3374 		return EOF;
       
  3375 	}
       
  3376 	if(fputc('\'', stream) < 0)
       
  3377 	    return EOF;
       
  3378     } else {
       
  3379 	/* use Bourne-style quoting, avoiding empty quoted strings */
       
  3380 	while(*s) {
       
  3381 	    if (*s == Meta)
       
  3382 		c = *++s ^ 32;
       
  3383 	    else
       
  3384 		c = *s;
       
  3385 	    s++;
       
  3386 	    if (c == '\'') {
       
  3387 		if(inquote) {
       
  3388 		    if(fputc('\'', stream) < 0)
       
  3389 			return EOF;
       
  3390 		    inquote=0;
       
  3391 		}
       
  3392 		if(fputs("\\'", stream) < 0)
       
  3393 		    return EOF;
       
  3394 	    } else {
       
  3395 		if (!inquote) {
       
  3396 		    if(fputc('\'', stream) < 0)
       
  3397 			return EOF;
       
  3398 		    inquote=1;
       
  3399 		}
       
  3400 		if(c == '\n' && isset(CSHJUNKIEQUOTES)) {
       
  3401 		    if(fputc('\\', stream) < 0)
       
  3402 			return EOF;
       
  3403 		}
       
  3404 		if(fputc(c, stream) < 0)
       
  3405 		    return EOF;
       
  3406 	    }
       
  3407 	}
       
  3408 	if (inquote) {
       
  3409 	    if(fputc('\'', stream) < 0)
       
  3410 		return EOF;
       
  3411 	}
       
  3412     }
       
  3413     return 0;
       
  3414 }
       
  3415 
       
  3416 /* Double-quote a metafied string. */
       
  3417 
       
  3418 /**/
       
  3419 mod_export char *
       
  3420 dquotedztrdup(char const *s)
       
  3421 {
       
  3422     int len = strlen(s) * 4 + 2;
       
  3423     char *buf = zalloc(len);
       
  3424     char *p = buf, *ret;
       
  3425 
       
  3426     if(isset(CSHJUNKIEQUOTES)) {
       
  3427 	int inquote = 0;
       
  3428 
       
  3429 	while(*s) {
       
  3430 	    int c = *s++;
       
  3431 
       
  3432 	    if (c == Meta)
       
  3433 		c = *s++ ^ 32;
       
  3434 	    switch(c) {
       
  3435 		case '"':
       
  3436 		case '$':
       
  3437 		case '`':
       
  3438 		    if(inquote) {
       
  3439 			*p++ = '"';
       
  3440 			inquote = 0;
       
  3441 		    }
       
  3442 		    *p++ = '\\';
       
  3443 		    *p++ = c;
       
  3444 		    break;
       
  3445 		default:
       
  3446 		    if(!inquote) {
       
  3447 			*p++ = '"';
       
  3448 			inquote = 1;
       
  3449 		    }
       
  3450 		    if(c == '\n')
       
  3451 			*p++ = '\\';
       
  3452 		    *p++ = c;
       
  3453 		    break;
       
  3454 	    }
       
  3455 	}
       
  3456 	if (inquote)
       
  3457 	    *p++ = '"';
       
  3458     } else {
       
  3459 	int pending = 0;
       
  3460 
       
  3461 	*p++ = '"';
       
  3462 	while(*s) {
       
  3463 	    int c = *s++;
       
  3464 
       
  3465 	    if (c == Meta)
       
  3466 		c = *s++ ^ 32;
       
  3467 	    switch(c) {
       
  3468 		case '\\':
       
  3469 		    if(pending)
       
  3470 			*p++ = '\\';
       
  3471 		    *p++ = '\\';
       
  3472 		    pending = 1;
       
  3473 		    break;
       
  3474 		case '"':
       
  3475 		case '$':
       
  3476 		case '`':
       
  3477 		    if(pending)
       
  3478 			*p++ = '\\';
       
  3479 		    *p++ = '\\';
       
  3480 		    /* fall through */
       
  3481 		default:
       
  3482 		    *p++ = c;
       
  3483 		    pending = 0;
       
  3484 		    break;
       
  3485 	    }
       
  3486 	}
       
  3487 	if(pending)
       
  3488 	    *p++ = '\\';
       
  3489 	*p++ = '"';
       
  3490     }
       
  3491     ret = metafy(buf, p - buf, META_DUP);
       
  3492     zfree(buf, len);
       
  3493     return ret;
       
  3494 }
       
  3495 
       
  3496 /* Unmetafy and output a string, double quoting it in its entirety. */
       
  3497 
       
  3498 #if 0 /**/
       
  3499 int
       
  3500 dquotedzputs(char const *s, FILE *stream)
       
  3501 {
       
  3502     char *d = dquotedztrdup(s);
       
  3503     int ret = zputs(d, stream);
       
  3504 
       
  3505     zsfree(d);
       
  3506     return ret;
       
  3507 }
       
  3508 #endif
       
  3509 
       
  3510 # if defined(HAVE_NL_LANGINFO) && defined(CODESET) && !defined(__STDC_ISO_10646__)
       
  3511 /* Convert a character from UCS4 encoding to UTF-8 */
       
  3512 
       
  3513 /**/
       
  3514 size_t
       
  3515 ucs4toutf8(char *dest, unsigned int wval)
       
  3516 {
       
  3517     size_t len;
       
  3518 
       
  3519     if (wval < 0x80)
       
  3520       len = 1;
       
  3521     else if (wval < 0x800)
       
  3522       len = 2;
       
  3523     else if (wval < 0x10000)
       
  3524       len = 3;
       
  3525     else if (wval < 0x200000)
       
  3526       len = 4;
       
  3527     else if (wval < 0x4000000)
       
  3528       len = 5;
       
  3529     else
       
  3530       len = 6;
       
  3531 
       
  3532     switch (len) { /* falls through except to the last case */
       
  3533     case 6: dest[5] = (wval & 0x3f) | 0x80; wval >>= 6;
       
  3534     case 5: dest[4] = (wval & 0x3f) | 0x80; wval >>= 6;
       
  3535     case 4: dest[3] = (wval & 0x3f) | 0x80; wval >>= 6;
       
  3536     case 3: dest[2] = (wval & 0x3f) | 0x80; wval >>= 6;
       
  3537     case 2: dest[1] = (wval & 0x3f) | 0x80; wval >>= 6;
       
  3538 	*dest = wval | ((0xfc << (6 - len)) & 0xfc);
       
  3539 	break;
       
  3540     case 1: *dest = wval;
       
  3541     }
       
  3542 
       
  3543     return len;
       
  3544 }
       
  3545 #endif
       
  3546 
       
  3547 /*
       
  3548  * Decode a key string, turning it into the literal characters.
       
  3549  * The length is returned in len.
       
  3550  * fromwhere determines how the processing works.
       
  3551  *   0:  Don't handle keystring, just print-like escapes.
       
  3552  *       Expects misc to be present.
       
  3553  *   1:  Handle Emacs-like \C-X arguments etc., but not ^X
       
  3554  *       Expects misc to be present.
       
  3555  *   2:  Handle ^X as well as emacs-like keys; don't handle \c
       
  3556  *       for no newlines.
       
  3557  *   3:  As 1, but don't handle \c.
       
  3558  *   4:  Do $'...' quoting.  Overwrites the existing string instead of
       
  3559  *       zhalloc'ing. If \uNNNN ever generates multi-byte chars longer
       
  3560  *       than 6 bytes, will need to adjust this to re-allocate memory.
       
  3561  *   5:  As 2, but \- is special.  Expects misc to be defined.
       
  3562  *   6:  As 2, but parses only one character and returns end-pointer
       
  3563  *       and parsed character in *misc
       
  3564  */
       
  3565 
       
  3566 /**/
       
  3567 mod_export char *
       
  3568 getkeystring(char *s, int *len, int fromwhere, int *misc)
       
  3569 {
       
  3570     char *buf, tmp[1];
       
  3571     char *t, *u = NULL;
       
  3572     char svchar = '\0';
       
  3573     int meta = 0, control = 0;
       
  3574     int i;
       
  3575 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__)
       
  3576     wint_t wval;
       
  3577     size_t count;
       
  3578 #else
       
  3579     unsigned int wval;
       
  3580 # if defined(HAVE_NL_LANGINFO) && defined(CODESET) && defined(HAVE_ICONV)
       
  3581     iconv_t cd;
       
  3582     char inbuf[4];
       
  3583     size_t inbytes, outbytes;
       
  3584     size_t count;
       
  3585 # endif
       
  3586 #endif
       
  3587 
       
  3588     if (fromwhere == 6)
       
  3589 	t = buf = tmp;
       
  3590     else if (fromwhere != 4)
       
  3591 	t = buf = zhalloc(strlen(s) + 1);
       
  3592     else {
       
  3593 	t = buf = s;
       
  3594 	s += 2;
       
  3595     }
       
  3596     for (; *s; s++) {
       
  3597 	if (*s == '\\' && s[1]) {
       
  3598 	    switch (*++s) {
       
  3599 	    case 'a':
       
  3600 #ifdef __STDC__
       
  3601 		*t++ = '\a';
       
  3602 #else
       
  3603 		*t++ = '\07';
       
  3604 #endif
       
  3605 		break;
       
  3606 	    case 'n':
       
  3607 		*t++ = '\n';
       
  3608 		break;
       
  3609 	    case 'b':
       
  3610 		*t++ = '\b';
       
  3611 		break;
       
  3612 	    case 't':
       
  3613 		*t++ = '\t';
       
  3614 		break;
       
  3615 	    case 'v':
       
  3616 		*t++ = '\v';
       
  3617 		break;
       
  3618 	    case 'f':
       
  3619 		*t++ = '\f';
       
  3620 		break;
       
  3621 	    case 'r':
       
  3622 		*t++ = '\r';
       
  3623 		break;
       
  3624 	    case 'E':
       
  3625 		if (!fromwhere) {
       
  3626 		    *t++ = '\\', s--;
       
  3627 		    continue;
       
  3628 		}
       
  3629 	    case 'e':
       
  3630 		*t++ = '\033';
       
  3631 		break;
       
  3632 	    case 'M':
       
  3633 		if (fromwhere) {
       
  3634 		    if (s[1] == '-')
       
  3635 			s++;
       
  3636 		    meta = 1 + control;	/* preserve the order of ^ and meta */
       
  3637 		} else
       
  3638 		    *t++ = '\\', s--;
       
  3639 		continue;
       
  3640 	    case 'C':
       
  3641 		if (fromwhere) {
       
  3642 		    if (s[1] == '-')
       
  3643 			s++;
       
  3644 		    control = 1;
       
  3645 		} else
       
  3646 		    *t++ = '\\', s--;
       
  3647 		continue;
       
  3648 	    case Meta:
       
  3649 		*t++ = '\\', s--;
       
  3650 		break;
       
  3651 	    case '-':
       
  3652 		if (fromwhere == 5) {
       
  3653 		    *misc  = 1;
       
  3654 		    break;
       
  3655 		}
       
  3656 		goto def;
       
  3657 	    case 'c':
       
  3658 		if (fromwhere < 2) {
       
  3659 		    *misc = 1;
       
  3660 		    break;
       
  3661 		}
       
  3662 	    case 'u':
       
  3663 	    case 'U':
       
  3664 	    	wval = 0;
       
  3665 		for (i=(*s == 'u' ? 4 : 8); i>0; i--) {
       
  3666 		    if (*++s && idigit(*s))
       
  3667 		        wval = wval * 16 + (*s - '0');
       
  3668 		    else if (*s && ((*s >= 'a' && *s <= 'f') ||
       
  3669 				    (*s >= 'A' && *s <= 'F')))
       
  3670 		        wval = wval * 16 + (*s & 0x1f) + 9;
       
  3671 		    else {
       
  3672 		    	s--;
       
  3673 		        break;
       
  3674 		    }
       
  3675 		}
       
  3676     	    	if (fromwhere == 6) {
       
  3677 		    *misc = wval;
       
  3678 		    return s+1;
       
  3679 		}
       
  3680 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__)
       
  3681 		count = wctomb(t, (wchar_t)wval);
       
  3682 		if (count == (size_t)-1) {
       
  3683 		    zerr("character not in range", NULL, 0);
       
  3684 		    if (fromwhere == 4) {
       
  3685 			for (u = t; (*u++ = *++s););
       
  3686 			return t;
       
  3687 		    }
       
  3688 		    *t = '\0';
       
  3689 		    *len = t - buf;
       
  3690 		    return buf;
       
  3691 		}
       
  3692 		t += count;  
       
  3693 		continue;
       
  3694 # else
       
  3695 #  if defined(HAVE_NL_LANGINFO) && defined(CODESET)
       
  3696 		if (!strcmp(nl_langinfo(CODESET), "UTF-8")) {
       
  3697 		    t += ucs4toutf8(t, wval);
       
  3698 		    continue;
       
  3699 		} else {
       
  3700 #   ifdef HAVE_ICONV
       
  3701 		    ICONV_CONST char *inptr = inbuf;
       
  3702     	    	    inbytes = 4;
       
  3703 		    outbytes = 6;
       
  3704 		    /* store value in big endian form */
       
  3705 		    for (i=3;i>=0;i--) {
       
  3706 			inbuf[i] = wval & 0xff;
       
  3707 			wval >>= 8;
       
  3708 		    }
       
  3709 
       
  3710     	    	    cd = iconv_open(nl_langinfo(CODESET), "UCS-4BE");
       
  3711 		    if (cd == (iconv_t)-1) {
       
  3712 			zerr("cannot do charset conversion", NULL, 0);
       
  3713 			if (fromwhere == 4) {
       
  3714 			    for (u = t; (*u++ = *++s););
       
  3715 			    return t;
       
  3716 			}
       
  3717 			*t = '\0';
       
  3718 			*len = t - buf;
       
  3719 			return buf;
       
  3720 		    }
       
  3721                     count = iconv(cd, (const char**)&inptr, &inbytes, &t, &outbytes);
       
  3722 		    iconv_close(cd);
       
  3723 		    if (count == (size_t)-1) {
       
  3724                         zerr("character not in range", NULL, 0);
       
  3725 		        *t = '\0';
       
  3726 			*len = t - buf;
       
  3727 			return buf;
       
  3728 		    }
       
  3729 		    continue;
       
  3730 #   else
       
  3731                     zerr("cannot do charset conversion", NULL, 0);
       
  3732 		    *t = '\0';
       
  3733 		    *len = t - buf;
       
  3734 		    return buf;
       
  3735 #   endif
       
  3736 		}
       
  3737 #  else
       
  3738                 zerr("cannot do charset conversion", NULL, 0);
       
  3739 		*t = '\0';
       
  3740 		*len = t - buf;
       
  3741 		return buf;
       
  3742 #  endif
       
  3743 # endif
       
  3744 	    default:
       
  3745 	    def:
       
  3746 		if ((idigit(*s) && *s < '8') || *s == 'x') {
       
  3747 		    if (!fromwhere) {
       
  3748 			if (*s == '0')
       
  3749 			    s++;
       
  3750 			else if (*s != 'x') {
       
  3751 			    *t++ = '\\', s--;
       
  3752 			    continue;
       
  3753 			}
       
  3754 		    }
       
  3755 		    if (s[1] && s[2] && s[3]) {
       
  3756 			svchar = s[3];
       
  3757 			s[3] = '\0';
       
  3758 			u = s;
       
  3759 		    }
       
  3760 		    *t++ = zstrtol(s + (*s == 'x'), &s,
       
  3761 				   (*s == 'x') ? 16 : 8);
       
  3762 		    if (svchar) {
       
  3763 			u[3] = svchar;
       
  3764 			svchar = '\0';
       
  3765 		    }
       
  3766 		    s--;
       
  3767 		} else {
       
  3768 		    if (!fromwhere && *s != '\\')
       
  3769 			*t++ = '\\';
       
  3770 		    *t++ = *s;
       
  3771 		}
       
  3772 		break;
       
  3773 	    }
       
  3774 	} else if (fromwhere == 4 && *s == Snull) {
       
  3775 	    for (u = t; (*u++ = *s++););
       
  3776 	    return t + 1;
       
  3777 	} else if (*s == '^' && !control &&
       
  3778 		   (fromwhere == 2 || fromwhere == 5 || fromwhere == 6)) {
       
  3779 	    control = 1;
       
  3780 	    continue;
       
  3781 	} else if (*s == Meta)
       
  3782 	    *t++ = *++s ^ 32;
       
  3783 	else
       
  3784 	    *t++ = *s;
       
  3785 	if (meta == 2) {
       
  3786 	    t[-1] |= 0x80;
       
  3787 	    meta = 0;
       
  3788 	}
       
  3789 	if (control) {
       
  3790 	    if (t[-1] == '?')
       
  3791 		t[-1] = 0x7f;
       
  3792 	    else
       
  3793 		t[-1] &= 0x9f;
       
  3794 	    control = 0;
       
  3795 	}
       
  3796 	if (meta) {
       
  3797 	    t[-1] |= 0x80;
       
  3798 	    meta = 0;
       
  3799 	}
       
  3800 	if (fromwhere == 4 && imeta(t[-1])) {
       
  3801 	    *t = t[-1] ^ 32;
       
  3802 	    t[-1] = Meta;
       
  3803 	    t++;
       
  3804 	}
       
  3805 	if (fromwhere == 6 && t != tmp) {
       
  3806 	    *misc = STOUC(tmp[0]);
       
  3807 	    return s + 1;
       
  3808 	}
       
  3809     }
       
  3810     DPUTS(fromwhere == 4, "BUG: unterminated $' substitution");
       
  3811     *t = '\0';
       
  3812     if (fromwhere == 6)
       
  3813       *misc = 0;
       
  3814     else
       
  3815       *len = t - buf;
       
  3816     return buf;
       
  3817 }
       
  3818 
       
  3819 /* Return non-zero if s is a prefix of t. */
       
  3820 
       
  3821 /**/
       
  3822 mod_export int
       
  3823 strpfx(char *s, char *t)
       
  3824 {
       
  3825     while (*s && *s == *t)
       
  3826 	s++, t++;
       
  3827     return !*s;
       
  3828 }
       
  3829 
       
  3830 /* Return non-zero if s is a suffix of t. */
       
  3831 
       
  3832 /**/
       
  3833 mod_export int
       
  3834 strsfx(char *s, char *t)
       
  3835 {
       
  3836     int ls = strlen(s), lt = strlen(t);
       
  3837 
       
  3838     if (ls <= lt)
       
  3839 	return !strcmp(t + lt - ls, s);
       
  3840     return 0;
       
  3841 }
       
  3842 
       
  3843 /**/
       
  3844 static int
       
  3845 upchdir(int n)
       
  3846 {
       
  3847     char buf[PATH_MAX];
       
  3848     char *s;
       
  3849     int err = -1;
       
  3850 
       
  3851     while (n > 0) {
       
  3852 	for (s = buf; s < buf + PATH_MAX - 4 && n--; )
       
  3853 	    *s++ = '.', *s++ = '.', *s++ = '\\';
       
  3854 	s[-1] = '\0';
       
  3855 	if (chdir(buf))
       
  3856 	    return err;
       
  3857 	err = -2;
       
  3858     }
       
  3859     return 0;
       
  3860 }
       
  3861 
       
  3862 /* Change directory, without following symlinks.  Returns 0 on success, -1 *
       
  3863  * on failure.  Sets errno to ENOTDIR if any symlinks are encountered.  If *
       
  3864  * fchdir() fails, or the current directory is unreadable, we might end up *
       
  3865  * in an unwanted directory in case of failure.                            */
       
  3866 
       
  3867 /**/
       
  3868 mod_export int
       
  3869 lchdir(char const *path, struct dirsav *d, int hard)
       
  3870 {
       
  3871     char const *pptr;
       
  3872     int level;
       
  3873     struct stat st1;
       
  3874     struct dirsav ds;
       
  3875 #ifdef HAVE_LSTAT
       
  3876     char buf[PATH_MAX + 1], *ptr;
       
  3877     int err;
       
  3878     struct stat st2;
       
  3879 #endif
       
  3880 
       
  3881     if (!d) {
       
  3882 	ds.ino = ds.dev = 0;
       
  3883 	ds.dirname = NULL;
       
  3884 	ds.dirfd = -1;
       
  3885 	d = &ds;
       
  3886     }
       
  3887 #ifdef HAVE_LSTAT
       
  3888     if ((*path == '\\' || !hard) &&
       
  3889 	(d != &ds || hard)){
       
  3890 #else
       
  3891     if (*path == '\\') {
       
  3892 #endif
       
  3893 	level = 0;
       
  3894 #ifdef HAVE_FCHDIR
       
  3895 	if (d->dirfd < 0 && (d->dirfd = open(".", O_RDONLY | O_NOCTTY)) < 0 &&
       
  3896 	    zgetdir(d) && *d->dirname != '\\')
       
  3897 	    d->dirfd = open("..", O_RDONLY | O_NOCTTY);
       
  3898 #else
       
  3899 	if (!d->dirname)
       
  3900 	    zgetdir(d);
       
  3901 #endif
       
  3902     } else {
       
  3903 	level = 0;
       
  3904 	if (!d->dev && !d->ino) {
       
  3905 	    stat(".", &st1);
       
  3906 	    d->dev = st1.st_dev;
       
  3907 	    d->ino = st1.st_ino;
       
  3908 	}
       
  3909     }
       
  3910 
       
  3911 #ifdef HAVE_LSTAT
       
  3912     if (!hard)
       
  3913 #endif
       
  3914     {
       
  3915 	if (d != &ds) {
       
  3916 	    for (pptr = path; *pptr; level++) {
       
  3917 		while (*pptr && *pptr++ != '\\');
       
  3918 		while (*pptr == '\\')
       
  3919 		    pptr++;
       
  3920 	    }
       
  3921 	    d->level = level;
       
  3922 	}
       
  3923 	return zchdir((char *) path);
       
  3924     }
       
  3925 #ifdef HAVE_LSTAT
       
  3926     if (*path == '\\')
       
  3927 	chdir("\\");
       
  3928     for(;;) {
       
  3929 	while(*path == '\\')
       
  3930 	    path++;
       
  3931 	if(!*path) {
       
  3932 	    if (d == &ds) {
       
  3933 		zsfree(ds.dirname);
       
  3934 		if (ds.dirfd >=0)
       
  3935 		    close(ds.dirfd);
       
  3936 	    } else
       
  3937 		d->level = level;
       
  3938 	    return 0;
       
  3939 	}
       
  3940 	for(pptr = path; *++pptr && *pptr != '\\'; ) ;
       
  3941 	if(pptr - path > PATH_MAX) {
       
  3942 	    err = ENAMETOOLONG;
       
  3943 	    break;
       
  3944 	}
       
  3945 	for(ptr = buf; path != pptr; )
       
  3946 	    *ptr++ = *path++;
       
  3947 	*ptr = 0;
       
  3948 	if(lstat(buf, &st1)) {
       
  3949 	    err = errno;
       
  3950 	    break;
       
  3951 	}
       
  3952 	if(!S_ISDIR(st1.st_mode)) {
       
  3953 	    err = ENOTDIR;
       
  3954 	    break;
       
  3955 	}
       
  3956 	if(chdir(buf)) {
       
  3957 	    err = errno;
       
  3958 	    break;
       
  3959 	}
       
  3960 	if (level >= 0)
       
  3961 	    level++;
       
  3962 	if(lstat(".", &st2)) {
       
  3963 	    err = errno;
       
  3964 	    break;
       
  3965 	}
       
  3966 	if(st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) {
       
  3967 	    err = ENOTDIR;
       
  3968 	    break;
       
  3969 	}
       
  3970     }
       
  3971     if (restoredir(d)) {
       
  3972 	if (d == &ds) {
       
  3973 	    zsfree(ds.dirname);
       
  3974 	    if (ds.dirfd >=0)
       
  3975 		close(ds.dirfd);
       
  3976 	}
       
  3977 	errno = err;
       
  3978 	return -2;
       
  3979     }
       
  3980     if (d == &ds) {
       
  3981 	zsfree(ds.dirname);
       
  3982 	if (ds.dirfd >=0)
       
  3983 	    close(ds.dirfd);
       
  3984     }
       
  3985     errno = err;
       
  3986     return -1;
       
  3987 #endif /* HAVE_LSTAT */
       
  3988 }
       
  3989 
       
  3990 /**/
       
  3991 mod_export int
       
  3992 restoredir(struct dirsav *d)
       
  3993 {
       
  3994     int err = 0;
       
  3995     struct stat sbuf;
       
  3996 
       
  3997     if (d->dirname && *d->dirname == '\\')
       
  3998 	return chdir(d->dirname);
       
  3999 #ifdef HAVE_FCHDIR
       
  4000     if (d->dirfd >= 0) {
       
  4001 	if (!fchdir(d->dirfd)) {
       
  4002 	    if (!d->dirname) {
       
  4003 		return 0;
       
  4004 	    } else if (chdir(d->dirname)) {
       
  4005 		close(d->dirfd);
       
  4006 		d->dirfd = -1;
       
  4007 		err = -2;
       
  4008 	    }
       
  4009 	} else {
       
  4010 	    close(d->dirfd);
       
  4011 	    d->dirfd = err = -1;
       
  4012 	}
       
  4013     } else
       
  4014 #endif
       
  4015     if (d->level > 0)
       
  4016 	err = upchdir(d->level);
       
  4017     else if (d->level < 0)
       
  4018 	err = -1;
       
  4019     if (d->dev || d->ino) {
       
  4020 	stat(".", &sbuf);
       
  4021 	if (sbuf.st_ino != d->ino || sbuf.st_dev != d->dev)
       
  4022 	    err = -2;
       
  4023     }
       
  4024     return err;
       
  4025 }
       
  4026 
       
  4027 
       
  4028 /* Check whether the shell is running with privileges in effect.  *
       
  4029  * This is the case if EITHER the euid is zero, OR (if the system *
       
  4030  * supports POSIX.1e (POSIX.6) capability sets) the process'      *
       
  4031  * Effective or Inheritable capability sets are non-empty.        */
       
  4032 
       
  4033 /**/
       
  4034 int
       
  4035 privasserted(void)
       
  4036 {
       
  4037     if(!geteuid())
       
  4038 	return 1;
       
  4039 #ifdef HAVE_CAP_GET_PROC
       
  4040     {
       
  4041 	cap_t caps = cap_get_proc();
       
  4042 	if(caps) {
       
  4043 	    /* POSIX doesn't define a way to test whether a capability set *
       
  4044 	     * is empty or not.  Typical.  I hope this is conforming...    */
       
  4045 	    cap_flag_value_t val;
       
  4046 	    cap_value_t n;
       
  4047 	    for(n = 0; !cap_get_flag(caps, n, CAP_EFFECTIVE, &val); n++)
       
  4048 		if(val) {
       
  4049 		    cap_free(caps);
       
  4050 		    return 1;
       
  4051 		}
       
  4052 	    cap_free(caps);
       
  4053 	}
       
  4054     }
       
  4055 #endif /* HAVE_CAP_GET_PROC */
       
  4056     return 0;
       
  4057 }
       
  4058 
       
  4059 #ifdef DEBUG
       
  4060 
       
  4061 /**/
       
  4062 mod_export void
       
  4063 dputs(char *message)
       
  4064 {
       
  4065     fprintf(stderr, "%s\n", message);
       
  4066     fflush(stderr);
       
  4067 }
       
  4068 
       
  4069 #endif /* DEBUG */
       
  4070 
       
  4071 /**/
       
  4072 mod_export int
       
  4073 mode_to_octal(mode_t mode)
       
  4074 {
       
  4075     int m = 0;
       
  4076 
       
  4077     if(mode & S_ISUID)
       
  4078 	m |= 04000;
       
  4079     if(mode & S_ISGID)
       
  4080 	m |= 02000;
       
  4081     if(mode & S_ISVTX)
       
  4082 	m |= 01000;
       
  4083     if(mode & S_IRUSR)
       
  4084 	m |= 00400;
       
  4085     if(mode & S_IWUSR)
       
  4086 	m |= 00200;
       
  4087     if(mode & S_IXUSR)
       
  4088 	m |= 00100;
       
  4089     if(mode & S_IRGRP)
       
  4090 	m |= 00040;
       
  4091     if(mode & S_IWGRP)
       
  4092 	m |= 00020;
       
  4093     if(mode & S_IXGRP)
       
  4094 	m |= 00010;
       
  4095     if(mode & S_IROTH)
       
  4096 	m |= 00004;
       
  4097     if(mode & S_IWOTH)
       
  4098 	m |= 00002;
       
  4099     if(mode & S_IXOTH)
       
  4100 	m |= 00001;
       
  4101     return m;
       
  4102 }
       
  4103 
       
  4104 #ifdef MAILDIR_SUPPORT
       
  4105 /*
       
  4106  *     Stat a file. If it's a maildir, check all messages
       
  4107  *     in the maildir and present the grand total as a file.
       
  4108  *     The fields in the 'struct stat' are from the mail directory.
       
  4109  *     The following fields are emulated:
       
  4110  *
       
  4111  *     st_nlink        always 1
       
  4112  *     st_size         total number of bytes in all files
       
  4113  *     st_blocks       total number of messages
       
  4114  *     st_atime        access time of newest file in maildir
       
  4115  *     st_mtime        modify time of newest file in maildir
       
  4116  *     st_mode         S_IFDIR changed to S_IFREG
       
  4117  *
       
  4118  *     This is good enough for most mail-checking applications.
       
  4119  */
       
  4120 
       
  4121 /**/
       
  4122 int
       
  4123 mailstat(char *path, struct stat *st)
       
  4124 {
       
  4125        DIR                     *dd;
       
  4126        struct                  dirent *fn;
       
  4127        struct stat             st_ret, st_tmp;
       
  4128        static struct stat      st_ret_last;
       
  4129        char                    *dir, *file = 0;
       
  4130        int                     i;
       
  4131        time_t                  atime = 0, mtime = 0;
       
  4132        size_t                  plen = strlen(path), dlen;
       
  4133 
       
  4134        /* First see if it's a directory. */
       
  4135        if ((i = stat(path, st)) != 0 || !S_ISDIR(st->st_mode))
       
  4136                return i;
       
  4137 
       
  4138        st_ret = *st;
       
  4139        st_ret.st_nlink = 1;
       
  4140        st_ret.st_size  = 0;
       
  4141        st_ret.st_blocks  = 0;
       
  4142        st_ret.st_mode  &= ~S_IFDIR;
       
  4143        st_ret.st_mode  |= S_IFREG;
       
  4144 
       
  4145        /* See if cur/ is present */
       
  4146        dir = appstr(ztrdup(path), "/cur");
       
  4147        if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0;
       
  4148        st_ret.st_atime = st_tmp.st_atime;
       
  4149 
       
  4150        /* See if tmp/ is present */
       
  4151        dir[plen] = 0;
       
  4152        dir = appstr(dir, "/tmp");
       
  4153        if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0;
       
  4154        st_ret.st_mtime = st_tmp.st_mtime;
       
  4155 
       
  4156        /* And new/ */
       
  4157        dir[plen] = 0;
       
  4158        dir = appstr(dir, "/new");
       
  4159        if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0;
       
  4160        st_ret.st_mtime = st_tmp.st_mtime;
       
  4161 
       
  4162 #if THERE_IS_EXACTLY_ONE_MAILDIR_IN_MAILPATH
       
  4163        {
       
  4164        static struct stat      st_new_last;
       
  4165        /* Optimization - if new/ didn't change, nothing else did. */
       
  4166        if (st_tmp.st_dev == st_new_last.st_dev &&
       
  4167            st_tmp.st_ino == st_new_last.st_ino &&
       
  4168            st_tmp.st_atime == st_new_last.st_atime &&
       
  4169            st_tmp.st_mtime == st_new_last.st_mtime) {
       
  4170 	   *st = st_ret_last;
       
  4171 	   return 0;
       
  4172        }
       
  4173        st_new_last = st_tmp;
       
  4174        }
       
  4175 #endif
       
  4176 
       
  4177        /* Loop over new/ and cur/ */
       
  4178        for (i = 0; i < 2; i++) {
       
  4179 	   dir[plen] = 0;
       
  4180 	   dir = appstr(dir, i ? "/cur" : "/new");
       
  4181 	   if ((dd = opendir(dir)) == NULL) {
       
  4182 	       zsfree(file);
       
  4183 	       zsfree(dir);
       
  4184 	       return 0;
       
  4185 	   }
       
  4186 	   dlen = strlen(dir) + 1; /* include the "/" */
       
  4187 	   while ((fn = readdir(dd)) != NULL) {
       
  4188 	       if (fn->d_name[0] == '.')
       
  4189 		   continue;
       
  4190 	       if (file) {
       
  4191 		   file[dlen] = 0;
       
  4192 		   file = appstr(file, fn->d_name);
       
  4193 	       } else {
       
  4194 		   file = tricat(dir, "/", fn->d_name);
       
  4195 	       }
       
  4196 	       if (stat(file, &st_tmp) != 0)
       
  4197 		   continue;
       
  4198 	       st_ret.st_size += st_tmp.st_size;
       
  4199 	       st_ret.st_blocks++;
       
  4200 	       if (st_tmp.st_atime != st_tmp.st_mtime &&
       
  4201 		   st_tmp.st_atime > atime)
       
  4202 		   atime = st_tmp.st_atime;
       
  4203 	       if (st_tmp.st_mtime > mtime)
       
  4204 		   mtime = st_tmp.st_mtime;
       
  4205 	   }
       
  4206 	   closedir(dd);
       
  4207        }
       
  4208        zsfree(file);
       
  4209        zsfree(dir);
       
  4210 
       
  4211        if (atime) st_ret.st_atime = atime;
       
  4212        if (mtime) st_ret.st_mtime = mtime;
       
  4213 
       
  4214        *st = st_ret_last = st_ret;
       
  4215        return 0;
       
  4216 }
       
  4217 #endif