openenvutils/commandshell/shell/src/hist.c
changeset 0 2e3d3ce01487
child 4 0fdb7f6b0309
equal deleted inserted replaced
-1:000000000000 0:2e3d3ce01487
       
     1 //hist.c - history expansion
       
     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 "hist.pro"
       
    34 
       
    35 #ifdef __SYMBIAN32__
       
    36 #ifdef __WINSCW__
       
    37 #pragma warn_unusedarg off
       
    38 #pragma warn_possunwant off
       
    39 #endif//__WINSCW__
       
    40 #endif//__SYMBIAN32__
       
    41 
       
    42 
       
    43 /* Functions to call for getting/ungetting a character and for history
       
    44  * word control. */
       
    45 
       
    46 /**/
       
    47 mod_export int (*hgetc) _((void));
       
    48 
       
    49 /**/
       
    50 void (*hungetc) _((int));
       
    51 
       
    52 /**/
       
    53 void (*hwaddc) _((int));
       
    54 
       
    55 /**/
       
    56 void (*hwbegin) _((int));
       
    57 
       
    58 /**/
       
    59 void (*hwend) _((void));
       
    60 
       
    61 /**/
       
    62 void (*addtoline) _((int));
       
    63 
       
    64 /* != 0 means history substitution is turned off */
       
    65  
       
    66 /**/
       
    67 mod_export int stophist;
       
    68 
       
    69 /* if != 0, we are expanding the current line */
       
    70 
       
    71 /**/
       
    72 mod_export int expanding;
       
    73 
       
    74 /* these are used to modify the cursor position during expansion */
       
    75 
       
    76 /**/
       
    77 mod_export int excs, exlast;
       
    78 
       
    79 /*
       
    80  * Current history event number
       
    81  *
       
    82  * Note on curhist: with history inactive, this points to the
       
    83  * last line actually added to the history list.  With history active,
       
    84  * the line does not get added to the list until hend(), if at all.
       
    85  * However, curhist is incremented to reflect the current line anyway
       
    86  * and a temporary history entry is inserted while the user is editing.
       
    87  * If the resulting line was not added to the list, a flag is set so
       
    88  * that curhist will be decremented in hbegin().
       
    89  */
       
    90  
       
    91 /**/
       
    92 mod_export zlong curhist;
       
    93 
       
    94 /**/
       
    95 struct histent curline;
       
    96 
       
    97 /* current line count of allocated history entries */
       
    98 
       
    99 /**/
       
   100 zlong histlinect;
       
   101 
       
   102 /* The history lines are kept in a hash, and also doubly-linked in a ring */
       
   103 
       
   104 /**/
       
   105 HashTable histtab;
       
   106 /**/
       
   107 mod_export Histent hist_ring;
       
   108  
       
   109 /* capacity of history lists */
       
   110  
       
   111 /**/
       
   112 zlong histsiz;
       
   113  
       
   114 /* desired history-file size (in lines) */
       
   115  
       
   116 /**/
       
   117 zlong savehistsiz;
       
   118  
       
   119 /* if = 1, we have performed history substitution on the current line *
       
   120  * if = 2, we have used the 'p' modifier                              */
       
   121  
       
   122 /**/
       
   123 int histdone;
       
   124  
       
   125 /* state of the history mechanism */
       
   126  
       
   127 /**/
       
   128 int histactive;
       
   129 
       
   130 /* Current setting of the associated option, but sometimes also includes
       
   131  * the setting of the HIST_SAVE_NO_DUPS option. */
       
   132 
       
   133 /**/
       
   134 int hist_ignore_all_dups;
       
   135 
       
   136 /* What flags (if any) we should skip when moving through the history */
       
   137 
       
   138 /**/
       
   139 mod_export int hist_skip_flags;
       
   140 
       
   141 /* Bits of histactive variable */
       
   142 #define HA_ACTIVE	(1<<0)	/* History mechanism is active */
       
   143 #define HA_NOSTORE	(1<<1)	/* Don't store the line when finished */
       
   144 #define HA_NOINC	(1<<2)	/* Don't store, curhist not incremented */
       
   145 
       
   146 /* Array of word beginnings and endings in current history line. */
       
   147 
       
   148 /**/
       
   149 short *chwords;
       
   150 
       
   151 /* Max, actual position in chwords.
       
   152  * nwords = chwordpos/2 because we record beginning and end of words.
       
   153  */
       
   154 
       
   155 /**/
       
   156 int chwordlen, chwordpos;
       
   157 
       
   158 /* the last l for s/l/r/ history substitution */
       
   159  
       
   160 /**/
       
   161 char *hsubl;
       
   162 
       
   163 /* the last r for s/l/r/ history substitution */
       
   164  
       
   165 /**/
       
   166 char *hsubr;
       
   167  
       
   168 /* pointer into the history line */
       
   169  
       
   170 /**/
       
   171 mod_export char *hptr;
       
   172  
       
   173 /* the current history line */
       
   174  
       
   175 /**/
       
   176 mod_export char *chline;
       
   177 
       
   178 /* true if the last character returned by hgetc was an escaped bangchar *
       
   179  * if it is set and NOBANGHIST is unset hwaddc escapes bangchars        */
       
   180 
       
   181 /**/
       
   182 int qbang;
       
   183  
       
   184 /* max size of histline */
       
   185  
       
   186 /**/
       
   187 int hlinesz;
       
   188  
       
   189 /* default event (usually curhist-1, that is, "!!") */
       
   190  
       
   191 static zlong defev;
       
   192 
       
   193 /* add a character to the current history word */
       
   194 
       
   195 static void
       
   196 ihwaddc(int c)
       
   197 {
       
   198     /* Only if history line exists and lexing has not finished. */
       
   199     if (chline && !(errflag || lexstop)) {
       
   200 	/* Quote un-expanded bangs in the history line. */
       
   201 	if (c == bangchar && stophist < 2 && qbang)
       
   202 	    /* If qbang is not set, we do not escape this bangchar as it's *
       
   203 	     * not mecessary (e.g. it's a bang in !=, or it is followed    *
       
   204 	     * by a space). Roughly speaking, qbang is zero only if the    *
       
   205 	     * history interpreter has already digested this bang and      *
       
   206 	     * found that it is not necessary to escape it.                */
       
   207 	    hwaddc('\\');
       
   208 	*hptr++ = c;
       
   209 
       
   210 	/* Resize history line if necessary */
       
   211 	if (hptr - chline >= hlinesz) {
       
   212 	    int oldsiz = hlinesz;
       
   213 
       
   214 	    chline = realloc(chline, hlinesz = oldsiz + 64);
       
   215 	    hptr = chline + oldsiz;
       
   216 	}
       
   217     }
       
   218 }
       
   219 
       
   220 /* This function adds a character to the zle input line. It is used when *
       
   221  * zsh expands history (see doexpandhist() in zle_tricky.c). It also     *
       
   222  * calculates the new cursor position after the expansion. It is called  *
       
   223  * from hgetc() and from gettok() in lex.c for characters in comments.   */
       
   224 
       
   225 /**/
       
   226 void
       
   227 iaddtoline(int c)
       
   228 {
       
   229     if (!expanding || lexstop)
       
   230 	return;
       
   231     if (qbang && c == bangchar && stophist < 2) {
       
   232 	exlast--;
       
   233 	spaceinline(1);
       
   234 	line[cs++] = '\\';
       
   235     }
       
   236     if (excs > cs) {
       
   237 	excs += 1 + inbufct - exlast;
       
   238 	if (excs < cs)
       
   239 	    /* this case could be handled better but it is    *
       
   240 	     * so rare that it does not worth it              */
       
   241 	    excs = cs;
       
   242     }
       
   243     exlast = inbufct;
       
   244     spaceinline(1);
       
   245     line[cs++] = itok(c) ? ztokens[c - Pound] : c;
       
   246 }
       
   247 
       
   248 static int
       
   249 ihgetc(void)
       
   250 {
       
   251     int c = ingetc();
       
   252 
       
   253     qbang = 0;
       
   254     if (!stophist && !(inbufflags & INP_ALIAS)) {
       
   255 	/* If necessary, expand history characters. */
       
   256 	c = histsubchar(c);
       
   257 	if (c < 0) {
       
   258 	    /* bad expansion */
       
   259 	    errflag = lexstop = 1;
       
   260 	    return ' ';
       
   261 	}
       
   262     }
       
   263     if ((inbufflags & INP_HIST) && !stophist) {
       
   264 	/* the current character c came from a history expansion          *
       
   265 	 * (inbufflags & INP_HIST) and history is not disabled            *
       
   266 	 * (e.g. we are not inside single quotes). In that case, \!       *
       
   267 	 * should be treated as ! (since this \! came from a previous     *
       
   268 	 * history line where \ was used to escape the bang). So if       *
       
   269 	 * c == '\\' we fetch one more character to see if it's a bang,   *
       
   270 	 * and if it is not, we unget it and reset c back to '\\'         */
       
   271 	qbang = 0;
       
   272 	if (c == '\\' && !(qbang = (c = ingetc()) == bangchar))
       
   273 	    safeinungetc(c), c = '\\';
       
   274     } else if (stophist || (inbufflags & INP_ALIAS))
       
   275 	/* If the result is a bangchar which came from history or alias  *
       
   276 	 * expansion, we treat it as an escaped bangchar, unless history *
       
   277 	 * is disabled. If stophist == 1 it only means that history is   *
       
   278 	 * temporarily disabled by a !" which won't appear in in the     *
       
   279 	 * history, so we still have an escaped bang. stophist > 1 if    *
       
   280 	 * history is disabled with NOBANGHIST or by someone else (e.g.  *
       
   281 	 * when the lexer scans single quoted text).                     */
       
   282 	qbang = c == bangchar && (stophist < 2);
       
   283     hwaddc(c);
       
   284     addtoline(c);
       
   285 
       
   286     return c;
       
   287 }
       
   288 
       
   289 /**/
       
   290 static void
       
   291 safeinungetc(int c)
       
   292 {
       
   293     if (lexstop)
       
   294 	lexstop = 0;
       
   295     else
       
   296 	inungetc(c);
       
   297 }
       
   298 
       
   299 /**/
       
   300 void
       
   301 herrflush(void)
       
   302 {
       
   303     inpopalias();
       
   304 
       
   305     while (!lexstop && inbufct && !strin)
       
   306 	hwaddc(ingetc());
       
   307 }
       
   308 
       
   309 /* extract :s/foo/bar/ delimiters and arguments */
       
   310 
       
   311 /**/
       
   312 static int
       
   313 getsubsargs(char *subline)
       
   314 {
       
   315     int del;
       
   316     char *ptr1, *ptr2;
       
   317 
       
   318     del = ingetc();
       
   319     ptr1 = hdynread2(del);
       
   320     if (!ptr1)
       
   321 	return 1;
       
   322     ptr2 = hdynread2(del);
       
   323     if (strlen(ptr1)) {
       
   324 	zsfree(hsubl);
       
   325 	hsubl = ptr1;
       
   326     }
       
   327     zsfree(hsubr);
       
   328     hsubr = ptr2;
       
   329     if (hsubl && !strstr(subline, hsubl)) {
       
   330 	herrflush();
       
   331 	zerr("substitution failed", NULL, 0);
       
   332 	return 1;
       
   333     }
       
   334     return 0;
       
   335 }
       
   336 
       
   337 /* Get the maximum no. of words for a history entry. */
       
   338 
       
   339 /**/
       
   340 static int
       
   341 getargc(Histent ehist)
       
   342 {
       
   343     return ehist->nwords ? ehist->nwords-1 : 0;
       
   344 }
       
   345 
       
   346 /* Perform history substitution, returning the next character afterwards. */
       
   347 
       
   348 /**/
       
   349 static int
       
   350 histsubchar(int c)
       
   351 {
       
   352     int farg, evset = -1, larg, argc, cflag = 0, bflag = 0;
       
   353     zlong ev;
       
   354     static int marg = -1;
       
   355     static zlong mev = -1;
       
   356     char buf[256], *ptr;
       
   357     char *sline;
       
   358     Histent ehist;
       
   359 
       
   360     /* look, no goto's */
       
   361     if (isfirstch && c == hatchar) {
       
   362 	/* Line begins ^foo^bar */
       
   363 	isfirstch = 0;
       
   364 	inungetc(hatchar);
       
   365 	if (!(ehist = gethist(defev))
       
   366 	    || !(sline = getargs(ehist, 0, getargc(ehist)))
       
   367 	    || getsubsargs(sline) || !hsubl)
       
   368 	    return -1;
       
   369 	subst(&sline, hsubl, hsubr, 0);
       
   370     } else {
       
   371 	/* Line doesn't begin ^foo^bar */
       
   372 	if (c != ' ')
       
   373 	    isfirstch = 0;
       
   374 #ifndef __SYMBIAN32__	
       
   375 	if (c == '\\') {
       
   376 	    int g = ingetc();
       
   377 
       
   378 	    if (g != bangchar)
       
   379 		safeinungetc(g);
       
   380 	    else {
       
   381 		qbang = 1;
       
   382 		return bangchar;
       
   383 	    }
       
   384 	}
       
   385 #endif //__SYMBIAN32__	
       
   386 	if (c != bangchar)
       
   387 	    return c;
       
   388 	*hptr = '\0';
       
   389 	if ((c = ingetc()) == '{') {
       
   390 	    bflag = cflag = 1;
       
   391 	    c = ingetc();
       
   392 	}
       
   393 	if (c == '\"') {
       
   394 	    stophist = 1;
       
   395 	    return ingetc();
       
   396 	}
       
   397 	if ((!cflag && inblank(c)) || c == '=' || c == '(' || lexstop) {
       
   398 	    safeinungetc(c);
       
   399 	    return bangchar;
       
   400 	}
       
   401 	cflag = 0;
       
   402 	ptr = buf;
       
   403 
       
   404 	/* get event number */
       
   405 
       
   406 	queue_signals();
       
   407 	if (c == '?') {
       
   408 	    for (;;) {
       
   409 		c = ingetc();
       
   410 		if (c == '?' || c == '\n' || lexstop)
       
   411 		    break;
       
   412 		else
       
   413 		    *ptr++ = c;
       
   414 	    }
       
   415 	    if (c != '\n' && !lexstop)
       
   416 		c = ingetc();
       
   417 	    *ptr = '\0';
       
   418 	    mev = ev = hconsearch(hsubl = ztrdup(buf), &marg);
       
   419 	    evset = 0;
       
   420 	    if (ev == -1) {
       
   421 		herrflush();
       
   422 		unqueue_signals();
       
   423 		zerr("no such event: %s", buf, 0);
       
   424 		return -1;
       
   425 	    }
       
   426 	} else {
       
   427 	    zlong t0;
       
   428 
       
   429 	    for (;;) {
       
   430 		if (inblank(c) || c == ';' || c == ':' || c == '^' ||
       
   431 		    c == '$' || c == '*' || c == '%' || c == '}' ||
       
   432 		    c == '\'' || c == '"' || c == '`' || lexstop)
       
   433 		    break;
       
   434 		if (ptr != buf) {
       
   435 		    if (c == '-')
       
   436 			break;
       
   437 		    if ((idigit(buf[0]) || buf[0] == '-') && !idigit(c))
       
   438 			break;
       
   439 		}
       
   440 		*ptr++ = c;
       
   441 		if (c == '#' || c == bangchar) {
       
   442 		    c = ingetc();
       
   443 		    break;
       
   444 		}
       
   445 		c = ingetc();
       
   446 	    }
       
   447 	    *ptr = 0;
       
   448 	    if (!*buf) {
       
   449 		if (c != '%') {
       
   450 		    if (isset(CSHJUNKIEHISTORY))
       
   451 			ev = addhistnum(curhist,-1,HIST_FOREIGN);
       
   452 		    else
       
   453 			ev = defev;
       
   454 		    if (c == ':' && evset == -1)
       
   455 			evset = 0;
       
   456 		    else
       
   457 			evset = 1;
       
   458 		} else {
       
   459 		    if (marg != -1)
       
   460 			ev = mev;
       
   461 		    else
       
   462 			ev = defev;
       
   463 		    evset = 0;
       
   464 		}
       
   465 	    } else if ((t0 = zstrtol(buf, NULL, 10))) {
       
   466 		ev = (t0 < 0) ? addhistnum(curhist,t0,HIST_FOREIGN) : t0;
       
   467 		evset = 1;
       
   468 	    } else if ((unsigned)*buf == bangchar) {
       
   469 		ev = addhistnum(curhist,-1,HIST_FOREIGN);
       
   470 		evset = 1;
       
   471 	    } else if (*buf == '#') {
       
   472 		ev = curhist;
       
   473 		evset = 1;
       
   474 	    } else if ((ev = hcomsearch(buf)) == -1) {
       
   475 		herrflush();
       
   476 		unqueue_signals();
       
   477 		zerr("event not found: %s", buf, 0);
       
   478 		return -1;
       
   479 	    } else
       
   480 		evset = 1;
       
   481 	}
       
   482 
       
   483 	/* get the event */
       
   484 
       
   485 	if (!(ehist = gethist(defev = ev))) {
       
   486 	    unqueue_signals();
       
   487 	    return -1;
       
   488 	}
       
   489 	/* extract the relevant arguments */
       
   490 
       
   491 	argc = getargc(ehist);
       
   492 	if (c == ':') {
       
   493 	    cflag = 1;
       
   494 	    c = ingetc();
       
   495 	    if (c == '%' && marg != -1) {
       
   496 		if (!evset) {
       
   497 		    ehist = gethist(defev = mev);
       
   498 		    argc = getargc(ehist);
       
   499 		} else {
       
   500 		    herrflush();
       
   501 		    unqueue_signals();
       
   502 		    zerr("Ambiguous history reference", NULL, 0);
       
   503 		    return -1;
       
   504 		}
       
   505 
       
   506 	    }
       
   507 	}
       
   508 	if (c == '*') {
       
   509 	    farg = 1;
       
   510 	    larg = argc;
       
   511 	    cflag = 0;
       
   512 	} else {
       
   513 	    inungetc(c);
       
   514 	    larg = farg = getargspec(argc, marg, evset);
       
   515 	    if (larg == -2) {
       
   516 		unqueue_signals();
       
   517 		return -1;
       
   518 	    }
       
   519 	    if (farg != -1)
       
   520 		cflag = 0;
       
   521 	    c = ingetc();
       
   522 	    if (c == '*') {
       
   523 		cflag = 0;
       
   524 		larg = argc;
       
   525 	    } else if (c == '-') {
       
   526 		cflag = 0;
       
   527 		larg = getargspec(argc, marg, evset);
       
   528 		if (larg == -2) {
       
   529 		    unqueue_signals();
       
   530 		    return -1;
       
   531 		}
       
   532 		if (larg == -1)
       
   533 		    larg = argc - 1;
       
   534 	    } else
       
   535 		inungetc(c);
       
   536 	}
       
   537 	if (farg == -1)
       
   538 	    farg = 0;
       
   539 	if (larg == -1)
       
   540 	    larg = argc;
       
   541 	if (!(sline = getargs(ehist, farg, larg))) {
       
   542 	    unqueue_signals();
       
   543 	    return -1;
       
   544 	}
       
   545 	unqueue_signals();
       
   546     }
       
   547 
       
   548     /* do the modifiers */
       
   549 
       
   550     for (;;) {
       
   551 	c = (cflag) ? ':' : ingetc();
       
   552 	cflag = 0;
       
   553 	if (c == ':') {
       
   554 	    int gbal = 0;
       
   555 
       
   556 	    if ((c = ingetc()) == 'g') {
       
   557 		gbal = 1;
       
   558 		c = ingetc();
       
   559 	    }
       
   560 	    switch (c) {
       
   561 	    case 'p':
       
   562 		histdone = HISTFLAG_DONE | HISTFLAG_NOEXEC;
       
   563 		break;
       
   564 	    case 'h':
       
   565 		if (!remtpath(&sline)) {
       
   566 		    herrflush();
       
   567 		    zerr("modifier failed: h", NULL, 0);
       
   568 		    return -1;
       
   569 		}
       
   570 		break;
       
   571 	    case 'e':
       
   572 		if (!rembutext(&sline)) {
       
   573 		    herrflush();
       
   574 		    zerr("modifier failed: e", NULL, 0);
       
   575 		    return -1;
       
   576 		}
       
   577 		break;
       
   578 	    case 'r':
       
   579 		if (!remtext(&sline)) {
       
   580 		    herrflush();
       
   581 		    zerr("modifier failed: r", NULL, 0);
       
   582 		    return -1;
       
   583 		}
       
   584 		break;
       
   585 	    case 't':
       
   586 		if (!remlpaths(&sline)) {
       
   587 		    herrflush();
       
   588 		    zerr("modifier failed: t", NULL, 0);
       
   589 		    return -1;
       
   590 		}
       
   591 		break;
       
   592 	    case 's':
       
   593 		if (getsubsargs(sline))
       
   594 		    return -1; /* fall through */
       
   595 	    case '&':
       
   596 		if (hsubl && hsubr)
       
   597 		    subst(&sline, hsubl, hsubr, gbal);
       
   598 		else {
       
   599 		    herrflush();
       
   600 		    zerr("no previous substitution", NULL, 0);
       
   601 		    return -1;
       
   602 		}
       
   603 		break;
       
   604 	    case 'q':
       
   605 		quote(&sline);
       
   606 		break;
       
   607 	    case 'Q':
       
   608 		{
       
   609 		    int one = noerrs, oef = errflag;
       
   610 
       
   611 		    noerrs = 1;
       
   612 		    parse_subst_string(sline);
       
   613 		    noerrs = one;
       
   614 		    errflag = oef;
       
   615 		    remnulargs(sline);
       
   616 		    untokenize(sline);
       
   617 		}
       
   618 		break;
       
   619 	    case 'x':
       
   620 		quotebreak(&sline);
       
   621 		break;
       
   622 	    case 'l':
       
   623 		downcase(&sline);
       
   624 		break;
       
   625 	    case 'u':
       
   626 		upcase(&sline);
       
   627 		break;
       
   628 	    default:
       
   629 		herrflush();
       
   630 		zerr("illegal modifier: %c", NULL, c);
       
   631 		return -1;
       
   632 	    }
       
   633 	} else {
       
   634 	    if (c != '}' || !bflag)
       
   635 		inungetc(c);
       
   636 	    if (c != '}' && bflag) {
       
   637 		zerr("'}' expected", NULL, 0);
       
   638 		return -1;
       
   639 	    }
       
   640 	    break;
       
   641 	}
       
   642     }
       
   643 
       
   644     /*
       
   645      * Push the expanded value onto the input stack,
       
   646      * marking this as a history word for purposes of the alias stack.
       
   647      */
       
   648 
       
   649     lexstop = 0;
       
   650     /* this function is called only called from hgetc and only if      *
       
   651      * !(inbufflags & INP_ALIAS). History expansion should never be    *
       
   652      * done with INP_ALIAS (to prevent recursive history expansion and *
       
   653      * histoty expansion of aliases). Escapes are not removed here.    *
       
   654      * This is now handled in hgetc.                                   */
       
   655     inpush(sline, INP_HIST, NULL); /* sline from heap, don't free */
       
   656     histdone |= HISTFLAG_DONE;
       
   657     if (isset(HISTVERIFY))
       
   658 	histdone |= HISTFLAG_NOEXEC | HISTFLAG_RECALL;
       
   659 
       
   660     /* Don't try and re-expand line. */
       
   661     return ingetc();
       
   662 }
       
   663 
       
   664 /* unget a char and remove it from chline. It can only be used *
       
   665  * to unget a character returned by hgetc.                     */
       
   666 
       
   667 static void
       
   668 ihungetc(int c)
       
   669 {
       
   670     int doit = 1;
       
   671 
       
   672     while (!lexstop && !errflag) {
       
   673 	if (hptr[-1] != (char) c && stophist < 4 &&
       
   674 	    hptr > chline + 1 && hptr[-1] == '\n' && hptr[-2] == '\\')
       
   675 	    hungetc('\n'), hungetc('\\');
       
   676 
       
   677 	if (expanding) {
       
   678 	    cs--;
       
   679 	    ll--;
       
   680 	    exlast++;
       
   681 	}
       
   682 	DPUTS(hptr <= chline, "BUG: hungetc attempted at buffer start");
       
   683 	hptr--;
       
   684 	DPUTS(*hptr != (char) c, "BUG: wrong character in hungetc() ");
       
   685 	qbang = (c == bangchar && stophist < 2 &&
       
   686 		 hptr > chline && hptr[-1] == '\\');
       
   687 	if (doit)
       
   688 	    inungetc(c);
       
   689 	if (!qbang)
       
   690 	    return;
       
   691 	doit = !stophist && ((inbufflags & INP_HIST) ||
       
   692 				 !(inbufflags & INP_ALIAS));
       
   693 	c = '\\';
       
   694     }
       
   695 }
       
   696 
       
   697 /* begin reading a string */
       
   698 
       
   699 /**/
       
   700 mod_export void
       
   701 strinbeg(int dohist)
       
   702 {
       
   703     strin++;
       
   704     hbegin(dohist);
       
   705     lexinit();
       
   706 }
       
   707 
       
   708 /* done reading a string */
       
   709 
       
   710 /**/
       
   711 mod_export void
       
   712 strinend(void)
       
   713 {
       
   714     hend(NULL);
       
   715     DPUTS(!strin, "BUG: strinend() called without strinbeg()");
       
   716     strin--;
       
   717     isfirstch = 1;
       
   718     histdone = 0;
       
   719 }
       
   720 
       
   721 /* dummy functions to use instead of hwaddc(), hwbegin(), and hwend() when
       
   722  * they aren't needed */
       
   723 
       
   724 static void
       
   725 nohw(UNUSED(int c))
       
   726 {
       
   727 }
       
   728 
       
   729 static void
       
   730 nohwe(void)
       
   731 {
       
   732 }
       
   733 
       
   734 /* these functions handle adding/removing curline to/from the hist_ring */
       
   735 
       
   736 static void
       
   737 linkcurline(void)
       
   738 {
       
   739     if (!hist_ring)
       
   740 	hist_ring = curline.up = curline.down = &curline;
       
   741     else {
       
   742 	curline.up = hist_ring;
       
   743 	curline.down = hist_ring->down;
       
   744 	hist_ring->down = hist_ring->down->up = &curline;
       
   745 	hist_ring = &curline;
       
   746     }
       
   747     curline.histnum = ++curhist;
       
   748 }
       
   749 
       
   750 static void
       
   751 unlinkcurline(void)
       
   752 {
       
   753     curline.up->down = curline.down;
       
   754     curline.down->up = curline.up;
       
   755     if (hist_ring == &curline) {
       
   756 	if (!histlinect)
       
   757 	    hist_ring = NULL;
       
   758 	else
       
   759 	    hist_ring = curline.up;
       
   760     }
       
   761     curhist--;
       
   762 }
       
   763 
       
   764 /* initialize the history mechanism */
       
   765 
       
   766 /**/
       
   767 mod_export void
       
   768 hbegin(int dohist)
       
   769 {
       
   770     isfirstln = isfirstch = 1;
       
   771     errflag = histdone = 0;
       
   772     if (!dohist)
       
   773 	stophist = 2;
       
   774     else if (dohist != 2)
       
   775 	stophist = (!interact || unset(SHINSTDIN)) ? 2 : 0;
       
   776     else
       
   777 	stophist = 0;
       
   778     if (stophist == 2 || (inbufflags & INP_ALIAS)) {
       
   779 	chline = hptr = NULL;
       
   780 	hlinesz = 0;
       
   781 	chwords = NULL;
       
   782 	chwordlen = 0;
       
   783 	hgetc = ingetc;
       
   784 	hungetc = inungetc;
       
   785 	hwaddc = nohw;
       
   786 	hwbegin = nohw;
       
   787 	hwend = nohwe;
       
   788 	addtoline = nohw;
       
   789     } else {
       
   790 	chline = hptr = zshcalloc(hlinesz = 64);
       
   791 	chwords = zalloc((chwordlen = 64) * sizeof(short));
       
   792 	hgetc = ihgetc;
       
   793 	hungetc = ihungetc;
       
   794 	hwaddc = ihwaddc;
       
   795 	hwbegin = ihwbegin;
       
   796 	hwend = ihwend;
       
   797 	addtoline = iaddtoline;
       
   798 	if (!isset(BANGHIST))
       
   799 	    stophist = 4;
       
   800     }
       
   801     chwordpos = 0;
       
   802 
       
   803     if (hist_ring && !hist_ring->ftim)
       
   804 	hist_ring->ftim = time(NULL);
       
   805     if ((dohist == 2 || (interact && isset(SHINSTDIN))) && !strin) {
       
   806 	histactive = HA_ACTIVE;
       
   807 	attachtty(mypgrp);
       
   808 	linkcurline();
       
   809 	defev = addhistnum(curhist, -1, HIST_FOREIGN);
       
   810     } else
       
   811 	histactive = HA_ACTIVE | HA_NOINC;
       
   812 }
       
   813 
       
   814 /**/
       
   815 void
       
   816 histreduceblanks(void)
       
   817 {
       
   818     int i, len, pos, needblank, spacecount = 0;
       
   819 
       
   820     if (isset(HISTIGNORESPACE))
       
   821 	while (chline[spacecount] == ' ') spacecount++;
       
   822 
       
   823     for (i = 0, len = spacecount; i < chwordpos; i += 2) {
       
   824 	len += chwords[i+1] - chwords[i]
       
   825 	     + (i > 0 && chwords[i] > chwords[i-1]);
       
   826     }
       
   827     if (chline[len] == '\0')
       
   828 	return;
       
   829 
       
   830     for (i = 0, pos = spacecount; i < chwordpos; i += 2) {
       
   831 	len = chwords[i+1] - chwords[i];
       
   832 	needblank = (i < chwordpos-2 && chwords[i+2] > chwords[i+1]);
       
   833 	if (pos != chwords[i]) {
       
   834 	    memcpy(chline + pos, chline + chwords[i], len + needblank);
       
   835 	    chwords[i] = pos;
       
   836 	    chwords[i+1] = chwords[i] + len;
       
   837 	}
       
   838 	pos += len + needblank;
       
   839     }
       
   840     chline[pos] = '\0';
       
   841 }
       
   842 
       
   843 /**/
       
   844 void
       
   845 histremovedups(void)
       
   846 {
       
   847     Histent he, next;
       
   848     for (he = hist_ring; he; he = next) {
       
   849 	next = up_histent(he);
       
   850 	if (he->flags & HIST_DUP)
       
   851 	    freehistnode((HashNode)he);
       
   852     }
       
   853 }
       
   854 
       
   855 /**/
       
   856 mod_export zlong
       
   857 addhistnum(zlong hl, int n, int xflags)
       
   858 {
       
   859     int dir = n < 0? -1 : n > 0? 1 : 0;
       
   860     Histent he = gethistent(hl, dir);
       
   861 			     
       
   862     if (!he)
       
   863 	return 0;
       
   864     if (he->histnum != hl)
       
   865 	n -= dir;
       
   866     if (n)
       
   867 	he = movehistent(he, n, xflags);
       
   868     if (!he)
       
   869 	return dir < 0? firsthist() - 1 : curhist + 1;
       
   870     return he->histnum;
       
   871 }
       
   872 
       
   873 /**/
       
   874 mod_export Histent
       
   875 movehistent(Histent he, int n, int xflags)
       
   876 {
       
   877     while (n < 0) {
       
   878 	if (!(he = up_histent(he)))
       
   879 	    return NULL;
       
   880 	if (!(he->flags & xflags))
       
   881 	    n++;
       
   882     }
       
   883     while (n > 0) {
       
   884 	if (!(he = down_histent(he)))
       
   885 	    return NULL;
       
   886 	if (!(he->flags & xflags))
       
   887 	    n--;
       
   888     }
       
   889     checkcurline(he);
       
   890     return he;
       
   891 }
       
   892 
       
   893 /**/
       
   894 mod_export Histent
       
   895 up_histent(Histent he)
       
   896 {
       
   897     return he->up == hist_ring? NULL : he->up;
       
   898 }
       
   899 
       
   900 /**/
       
   901 mod_export Histent
       
   902 down_histent(Histent he)
       
   903 {
       
   904     return he == hist_ring? NULL : he->down;
       
   905 }
       
   906 
       
   907 /**/
       
   908 mod_export Histent
       
   909 gethistent(zlong ev, int nearmatch)
       
   910 {
       
   911     Histent he;
       
   912 
       
   913     if (!hist_ring)
       
   914 	return NULL;
       
   915 
       
   916     if (ev - hist_ring->down->histnum < hist_ring->histnum - ev) {
       
   917 	for (he = hist_ring->down; he->histnum < ev; he = he->down) ;
       
   918 	if (he->histnum != ev) {
       
   919 	    if (nearmatch == 0
       
   920 	     || (nearmatch < 0 && (he = up_histent(he)) == NULL))
       
   921 		return NULL;
       
   922 	}
       
   923     }
       
   924     else {
       
   925 	for (he = hist_ring; he->histnum > ev; he = he->up) ;
       
   926 	if (he->histnum != ev) {
       
   927 	    if (nearmatch == 0
       
   928 	     || (nearmatch > 0 && (he = down_histent(he)) == NULL))
       
   929 		return NULL;
       
   930 	}
       
   931     }
       
   932 
       
   933     checkcurline(he);
       
   934     return he;
       
   935 }
       
   936 
       
   937 static void
       
   938 putoldhistentryontop(short keep_going)
       
   939 {
       
   940     static Histent next = NULL;
       
   941     Histent he = keep_going? next : hist_ring->down;
       
   942     next = he->down;
       
   943     if (isset(HISTEXPIREDUPSFIRST) && !(he->flags & HIST_DUP)) {
       
   944 	static zlong max_unique_ct = 0;
       
   945 	if (!keep_going)
       
   946 	    max_unique_ct = savehistsiz;
       
   947 	do {
       
   948 	    if (max_unique_ct-- <= 0 || he == hist_ring) {
       
   949 		max_unique_ct = 0;
       
   950 		he = hist_ring->down;
       
   951 		next = hist_ring;
       
   952 		break;
       
   953 	    }
       
   954 	    he = next;
       
   955 	    next = he->down;
       
   956 	} while (!(he->flags & HIST_DUP));
       
   957     }
       
   958     if (he != hist_ring->down) {
       
   959 	he->up->down = he->down;
       
   960 	he->down->up = he->up;
       
   961 	he->up = hist_ring;
       
   962 	he->down = hist_ring->down;
       
   963 	hist_ring->down = he->down->up = he;
       
   964     }
       
   965     hist_ring = he;
       
   966 }
       
   967 
       
   968 /**/
       
   969 Histent
       
   970 prepnexthistent(void)
       
   971 {
       
   972     Histent he; 
       
   973     int curline_in_ring = hist_ring == &curline;
       
   974 
       
   975     if (curline_in_ring)
       
   976 	unlinkcurline();
       
   977     if (hist_ring && hist_ring->flags & HIST_TMPSTORE) {
       
   978 	curhist--;
       
   979 	freehistnode((HashNode)hist_ring);
       
   980     }
       
   981 
       
   982     if (histlinect < histsiz) {
       
   983 	he = (Histent)zshcalloc(sizeof *he);
       
   984 	if (!hist_ring)
       
   985 	    hist_ring = he->up = he->down = he;
       
   986 	else {
       
   987 	    he->up = hist_ring;
       
   988 	    he->down = hist_ring->down;
       
   989 	    hist_ring->down = he->down->up = he;
       
   990 	    hist_ring = he;
       
   991 	}
       
   992 	histlinect++;
       
   993     }
       
   994     else {
       
   995 	putoldhistentryontop(0);
       
   996 	freehistdata(hist_ring, 0);
       
   997 	he = hist_ring;
       
   998     }
       
   999     he->histnum = ++curhist;
       
  1000     if (curline_in_ring)
       
  1001 	linkcurline();
       
  1002     return he;
       
  1003 }
       
  1004 
       
  1005 /* A helper function for hend() */
       
  1006 
       
  1007 static int
       
  1008 should_ignore_line(Eprog prog)
       
  1009 {
       
  1010     if (isset(HISTIGNORESPACE)) {
       
  1011 	if (*chline == ' ' || aliasspaceflag)
       
  1012 	    return 1;
       
  1013     }
       
  1014 
       
  1015     if (!prog)
       
  1016 	return 0;
       
  1017 
       
  1018     if (isset(HISTNOFUNCTIONS)) {
       
  1019 	Wordcode pc = prog->prog;
       
  1020 	wordcode code = *pc;
       
  1021 	if (wc_code(code) == WC_LIST && WC_LIST_TYPE(code) & Z_SIMPLE
       
  1022 	 && wc_code(pc[2]) == WC_FUNCDEF)
       
  1023 	    return 1;
       
  1024     }
       
  1025 
       
  1026     if (isset(HISTNOSTORE)) {
       
  1027 	char *b = getjobtext(prog, NULL);
       
  1028 	int saw_builtin;
       
  1029 	if (*b == 'b' && strncmp(b,"builtin ",8) == 0) {
       
  1030 	    b += 8;
       
  1031 	    saw_builtin = 1;
       
  1032 	} else
       
  1033 	    saw_builtin = 0;
       
  1034 	if (*b == 'h' && strncmp(b,"history",7) == 0 && (!b[7] || b[7] == ' ')
       
  1035 	 && (saw_builtin || !shfunctab->getnode(shfunctab,"history")))
       
  1036 	    return 1;
       
  1037 	if (*b == 'r' && (!b[1] || b[1] == ' ')
       
  1038 	 && (saw_builtin || !shfunctab->getnode(shfunctab,"r")))
       
  1039 	    return 1;
       
  1040 	if (*b == 'f' && b[1] == 'c' && b[2] == ' ' && b[3] == '-'
       
  1041 	 && (saw_builtin || !shfunctab->getnode(shfunctab,"fc"))) {
       
  1042 	    b += 3;
       
  1043 	    do {
       
  1044 		if (*++b == 'l')
       
  1045 		    return 1;
       
  1046 	    } while (ialpha(*b));
       
  1047 	}
       
  1048     }
       
  1049 
       
  1050     return 0;
       
  1051 }
       
  1052 
       
  1053 /* say we're done using the history mechanism */
       
  1054 
       
  1055 /**/
       
  1056 mod_export int
       
  1057 hend(Eprog prog)
       
  1058 {
       
  1059     int flag, save = 1;
       
  1060     char *hf;
       
  1061 
       
  1062     DPUTS(stophist != 2 && !(inbufflags & INP_ALIAS) && !chline,
       
  1063 	  "BUG: chline is NULL in hend()");
       
  1064     queue_signals();
       
  1065     if (histdone & HISTFLAG_SETTY)
       
  1066 	settyinfo(&shttyinfo);
       
  1067     if (!(histactive & HA_NOINC))
       
  1068 	unlinkcurline();
       
  1069     if (histactive & (HA_NOSTORE|HA_NOINC)) {
       
  1070 	zfree(chline, hlinesz);
       
  1071 	zfree(chwords, chwordlen*sizeof(short));
       
  1072 	chline = NULL;
       
  1073 	histactive = 0;
       
  1074 	unqueue_signals();
       
  1075 	return 1;
       
  1076     }
       
  1077     if (hist_ignore_all_dups != isset(HISTIGNOREALLDUPS)
       
  1078      && (hist_ignore_all_dups = isset(HISTIGNOREALLDUPS)) != 0)
       
  1079 	histremovedups();
       
  1080     /* For history sharing, lock history file once for both read and write */
       
  1081     hf = getsparam("HISTFILE");
       
  1082     if (isset(SHAREHISTORY) && lockhistfile(hf, 0)) {
       
  1083 	readhistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST);
       
  1084 	curline.histnum = curhist+1;
       
  1085     }
       
  1086     flag = histdone;
       
  1087     histdone = 0;
       
  1088     if (hptr < chline + 1)
       
  1089 	save = 0;
       
  1090     else {
       
  1091 	*hptr = '\0';
       
  1092 	if (hptr[-1] == '\n') {
       
  1093 	    if (chline[1]) {
       
  1094 		*--hptr = '\0';
       
  1095 	    } else
       
  1096 		save = 0;
       
  1097 	}
       
  1098 	if (chwordpos <= 2)
       
  1099 	    save = 0;
       
  1100 	else if (should_ignore_line(prog))
       
  1101 	    save = -1;
       
  1102     }
       
  1103     if (flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) {
       
  1104 	char *ptr;
       
  1105 
       
  1106 	ptr = ztrdup(chline);
       
  1107 	if ((flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) == HISTFLAG_DONE) {
       
  1108 	    zputs(ptr, shout);
       
  1109 	    fputc('\n', shout);
       
  1110 	    fflush(shout);
       
  1111 	}
       
  1112 	if (flag & HISTFLAG_RECALL) {
       
  1113 	    zpushnode(bufstack, ptr);
       
  1114 	    save = 0;
       
  1115 	} else
       
  1116 	    zsfree(ptr);
       
  1117     }
       
  1118     if (save || *chline == ' ') {
       
  1119 	Histent he;
       
  1120 	for (he = hist_ring; he && he->flags & HIST_FOREIGN;
       
  1121 	     he = up_histent(he)) ;
       
  1122 	if (he && he->flags & HIST_TMPSTORE) {
       
  1123 	    if (he == hist_ring)
       
  1124 		curline.histnum = curhist--;
       
  1125 	    freehistnode((HashNode)he);
       
  1126 	}
       
  1127     }
       
  1128     if (save) {
       
  1129 	Histent he;
       
  1130 	int newflags;
       
  1131 
       
  1132 #ifdef DEBUG
       
  1133 	/* debugging only */
       
  1134 	if (chwordpos%2) {
       
  1135 	    hwend();
       
  1136 	    DPUTS(1, "BUG: uncompleted line in history");
       
  1137 	}
       
  1138 #endif
       
  1139 	/* get rid of pesky \n which we've already nulled out */
       
  1140 	if (chwordpos > 1 && !chline[chwords[chwordpos-2]]) {
       
  1141 	    chwordpos -= 2;
       
  1142 	    /* strip superfluous blanks, if desired */
       
  1143 	    if (isset(HISTREDUCEBLANKS))
       
  1144 		histreduceblanks();
       
  1145 	}
       
  1146 	newflags = save > 0? 0 : HIST_TMPSTORE;
       
  1147 	if ((isset(HISTIGNOREDUPS) || isset(HISTIGNOREALLDUPS)) && save > 0
       
  1148 	 && hist_ring && histstrcmp(chline, hist_ring->text) == 0) {
       
  1149 	    /* This history entry compares the same as the previous.
       
  1150 	     * In case minor changes were made, we overwrite the
       
  1151 	     * previous one with the current one.  This also gets the
       
  1152 	     * timestamp right.  Perhaps, preserve the HIST_OLD flag.
       
  1153 	     */
       
  1154 	    he = hist_ring;
       
  1155 	    newflags |= he->flags & HIST_OLD; /* Avoid re-saving */
       
  1156 	    freehistdata(he, 0);
       
  1157 	    curline.histnum = curhist;
       
  1158 	} else
       
  1159 	    he = prepnexthistent();
       
  1160 
       
  1161 	he->text = ztrdup(chline);
       
  1162 	he->stim = time(NULL);
       
  1163 	he->ftim = 0L;
       
  1164 	he->flags = newflags;
       
  1165 
       
  1166 	if ((he->nwords = chwordpos/2)) {
       
  1167 	    he->words = (short *)zalloc(chwordpos * sizeof(short));
       
  1168 	    memcpy(he->words, chwords, chwordpos * sizeof(short));
       
  1169 	}
       
  1170 	if (!(newflags & HIST_TMPSTORE))
       
  1171 	    addhistnode(histtab, he->text, he);
       
  1172     }
       
  1173     zfree(chline, hlinesz);
       
  1174     zfree(chwords, chwordlen*sizeof(short));
       
  1175     chline = NULL;
       
  1176     histactive = 0;
       
  1177     if (isset(SHAREHISTORY)? histfileIsLocked() : isset(INCAPPENDHISTORY))
       
  1178 	savehistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST);
       
  1179     unlockhistfile(hf); /* It's OK to call this even if we aren't locked */
       
  1180     unqueue_signals();
       
  1181     return !(flag & HISTFLAG_NOEXEC || errflag);
       
  1182 }
       
  1183 
       
  1184 /* Gives current expansion word if not last word before chwordpos. */
       
  1185 
       
  1186 /**/
       
  1187 int hwgetword = -1;
       
  1188 
       
  1189 /* begin a word */
       
  1190 
       
  1191 /**/
       
  1192 void
       
  1193 ihwbegin(int offset)
       
  1194 {
       
  1195     if (stophist == 2)
       
  1196 	return;
       
  1197     if (chwordpos%2)
       
  1198 	chwordpos--;	/* make sure we're on a word start, not end */
       
  1199     /* If we're expanding an alias, we should overwrite the expansion
       
  1200      * in the history.
       
  1201      */
       
  1202     if ((inbufflags & INP_ALIAS) && !(inbufflags & INP_HIST))
       
  1203 	hwgetword = chwordpos;
       
  1204     else
       
  1205 	hwgetword = -1;
       
  1206     chwords[chwordpos++] = hptr - chline + offset;
       
  1207 }
       
  1208 
       
  1209 /* add a word to the history List */
       
  1210 
       
  1211 /**/
       
  1212 void
       
  1213 ihwend(void)
       
  1214 {
       
  1215     if (stophist == 2)
       
  1216 	return;
       
  1217     if (chwordpos%2 && chline) {
       
  1218 	/* end of word reached and we've already begun a word */
       
  1219 	if (hptr > chline + chwords[chwordpos-1]) {
       
  1220 	    chwords[chwordpos++] = hptr - chline;
       
  1221 	    if (chwordpos >= chwordlen) {
       
  1222 		chwords = (short *) realloc(chwords,
       
  1223 					    (chwordlen += 32) * 
       
  1224 					    sizeof(short));
       
  1225 	    }
       
  1226 	    if (hwgetword > -1) {
       
  1227 		/* We want to reuse the current word position */
       
  1228 		chwordpos = hwgetword;
       
  1229 		/* Start from where previous word ended, if possible */
       
  1230 		hptr = chline + chwords[chwordpos ? chwordpos - 1 : 0];
       
  1231 	    }
       
  1232 	} else {
       
  1233 	    /* scrub that last word, it doesn't exist */
       
  1234 	    chwordpos--;
       
  1235 	}
       
  1236     }
       
  1237 }
       
  1238 
       
  1239 /* Go back to immediately after the last word, skipping space. */
       
  1240 
       
  1241 /**/
       
  1242 void
       
  1243 histbackword(void)
       
  1244 {
       
  1245     if (!(chwordpos%2) && chwordpos)
       
  1246 	hptr = chline + chwords[chwordpos-1];
       
  1247 }
       
  1248 
       
  1249 /* Get the start and end point of the current history word */
       
  1250 
       
  1251 /**/
       
  1252 static void
       
  1253 hwget(char **startptr)
       
  1254 {
       
  1255     int pos = hwgetword > -1 ? hwgetword : chwordpos - 2;
       
  1256 
       
  1257 #ifdef DEBUG
       
  1258     /* debugging only */
       
  1259     if (hwgetword == -1 && !chwordpos) {
       
  1260 	/* no words available */
       
  1261 	DPUTS(1, "BUG: hwget() called with no words");
       
  1262 	*startptr = "";
       
  1263 	return;
       
  1264     } 
       
  1265     else if (hwgetword == -1 && chwordpos%2) {
       
  1266 	DPUTS(1, "BUG: hwget() called in middle of word");
       
  1267 	*startptr = "";
       
  1268 	return;
       
  1269     }
       
  1270 #endif
       
  1271 
       
  1272     *startptr = chline + chwords[pos];
       
  1273     chline[chwords[++pos]] = '\0';
       
  1274 }
       
  1275 
       
  1276 /* Replace the current history word with rep, if different */
       
  1277 
       
  1278 /**/
       
  1279 void
       
  1280 hwrep(char *rep)
       
  1281 {
       
  1282     char *start;
       
  1283     hwget(&start);
       
  1284 
       
  1285     if (!strcmp(rep, start))
       
  1286 	return;
       
  1287     
       
  1288     hptr = start;
       
  1289     chwordpos = (hwgetword > -1) ? hwgetword : chwordpos - 2;
       
  1290     hwbegin(0);
       
  1291     qbang = 1;
       
  1292     while (*rep)
       
  1293 	hwaddc(*rep++);
       
  1294     hwend();
       
  1295 }
       
  1296 
       
  1297 /* Get the entire current line, deleting it in the history. */
       
  1298 
       
  1299 /**/
       
  1300 mod_export char *
       
  1301 hgetline(void)
       
  1302 {
       
  1303     /* Currently only used by pushlineoredit().
       
  1304      * It's necessary to prevent that from getting too pally with
       
  1305      * the history code.
       
  1306      */
       
  1307     char *ret;
       
  1308 
       
  1309     if (!chline || hptr == chline)
       
  1310 	return NULL;
       
  1311     *hptr = '\0';
       
  1312     ret = dupstring(chline);
       
  1313 
       
  1314     /* reset line */
       
  1315     hptr = chline;
       
  1316     chwordpos = 0;
       
  1317     hwgetword = -1;
       
  1318 
       
  1319     return ret;
       
  1320 }
       
  1321 
       
  1322 /* get an argument specification */
       
  1323 
       
  1324 /**/
       
  1325 static int
       
  1326 getargspec(int argc, int marg, int evset)
       
  1327 {
       
  1328     int c, ret = -1;
       
  1329 
       
  1330     if ((c = ingetc()) == '0')
       
  1331 	return 0;
       
  1332     if (idigit(c)) {
       
  1333 	ret = 0;
       
  1334 	while (idigit(c)) {
       
  1335 	    ret = ret * 10 + c - '0';
       
  1336 	    c = ingetc();
       
  1337 	}
       
  1338 	inungetc(c);
       
  1339     } else if (c == '^')
       
  1340 	ret = 1;
       
  1341     else if (c == '$')
       
  1342 	ret = argc;
       
  1343     else if (c == '%') {
       
  1344 	if (evset) {
       
  1345 	    herrflush();
       
  1346 	    zerr("Ambiguous history reference", NULL, 0);
       
  1347 	    return -2;
       
  1348 	}
       
  1349 	if (marg == -1) {
       
  1350 	    herrflush();
       
  1351 	    zerr("%% with no previous word matched", NULL, 0);
       
  1352 	    return -2;
       
  1353 	}
       
  1354 	ret = marg;
       
  1355     } else
       
  1356 	inungetc(c);
       
  1357     return ret;
       
  1358 }
       
  1359 
       
  1360 /* do ?foo? search */
       
  1361 
       
  1362 /**/
       
  1363 static zlong
       
  1364 hconsearch(char *str, int *marg)
       
  1365 {
       
  1366     int t1 = 0;
       
  1367     char *s;
       
  1368     Histent he;
       
  1369 
       
  1370     for (he = up_histent(hist_ring); he; he = up_histent(he)) {
       
  1371 	if (he->flags & HIST_FOREIGN)
       
  1372 	    continue;
       
  1373 	if ((s = strstr(he->text, str))) {
       
  1374 	    int pos = s - he->text;
       
  1375 	    while (t1 < he->nwords && he->words[2*t1] <= pos)
       
  1376 		t1++;
       
  1377 	    *marg = t1 - 1;
       
  1378 	    return he->histnum;
       
  1379 	}
       
  1380     }
       
  1381     return -1;
       
  1382 }
       
  1383 
       
  1384 /* do !foo search */
       
  1385 
       
  1386 /**/
       
  1387 zlong
       
  1388 hcomsearch(char *str)
       
  1389 {
       
  1390     Histent he;
       
  1391     int len = strlen(str);
       
  1392 
       
  1393     for (he = up_histent(hist_ring); he; he = up_histent(he)) {
       
  1394 	if (he->flags & HIST_FOREIGN)
       
  1395 	    continue;
       
  1396 	if (strncmp(he->text, str, len) == 0)
       
  1397 	    return he->histnum;
       
  1398     }
       
  1399     return -1;
       
  1400 }
       
  1401 
       
  1402 /* various utilities for : modifiers */
       
  1403 
       
  1404 /**/
       
  1405 int
       
  1406 remtpath(char **junkptr)
       
  1407 {
       
  1408     char *str = strend(*junkptr);
       
  1409 
       
  1410     /* ignore trailing slashes */
       
  1411     while (str >= *junkptr && IS_DIRSEP(*str))
       
  1412 	--str;
       
  1413     /* skip filename */
       
  1414     while (str >= *junkptr && !IS_DIRSEP(*str))
       
  1415 	--str;
       
  1416     if (str < *junkptr) {
       
  1417 	if (IS_DIRSEP(**junkptr))
       
  1418 	    *junkptr = dupstring ("/");
       
  1419 	else
       
  1420 	    *junkptr = dupstring (".");
       
  1421 
       
  1422 	return 0;
       
  1423     }
       
  1424     /* repeated slashes are considered like a single slash */
       
  1425     while (str > *junkptr && IS_DIRSEP(str[-1]))
       
  1426 	--str;
       
  1427     /* never erase the root slash */
       
  1428     if (str == *junkptr) {
       
  1429 	++str;
       
  1430 	/* Leading doubled slashes (`//') have a special meaning on cygwin
       
  1431 	   and some old flavor of UNIX, so we do not assimilate them to
       
  1432 	   a single slash.  However a greater number is ok to squeeze. */
       
  1433 	if (IS_DIRSEP(*str) && !IS_DIRSEP(str[1]))
       
  1434 	    ++str;
       
  1435     }
       
  1436     *str = '\0';
       
  1437     return 1;
       
  1438 }
       
  1439 
       
  1440 /**/
       
  1441 int
       
  1442 remtext(char **junkptr)
       
  1443 {
       
  1444     char *str;
       
  1445 
       
  1446     for (str = strend(*junkptr); str >= *junkptr && !IS_DIRSEP(*str); --str)
       
  1447 	if (*str == '.') {
       
  1448 	    *str = '\0';
       
  1449 	    return 1;
       
  1450 	}
       
  1451     return 0;
       
  1452 }
       
  1453 
       
  1454 /**/
       
  1455 int
       
  1456 rembutext(char **junkptr)
       
  1457 {
       
  1458     char *str;
       
  1459 
       
  1460     for (str = strend(*junkptr); str >= *junkptr && !IS_DIRSEP(*str); --str)
       
  1461 	if (*str == '.') {
       
  1462 	    *junkptr = dupstring(str + 1); /* .xx or xx? */
       
  1463 	    return 1;
       
  1464 	}
       
  1465     /* no extension */
       
  1466     *junkptr = dupstring ("");
       
  1467     return 0;
       
  1468 }
       
  1469 
       
  1470 /**/
       
  1471 mod_export int
       
  1472 remlpaths(char **junkptr)
       
  1473 {
       
  1474     char *str = strend(*junkptr);
       
  1475 
       
  1476     if (IS_DIRSEP(*str)) {
       
  1477 	/* remove trailing slashes */
       
  1478 	while (str >= *junkptr && IS_DIRSEP(*str))
       
  1479 	    --str;
       
  1480 	str[1] = '\0';
       
  1481     }
       
  1482     for (; str >= *junkptr; --str)
       
  1483 	if (IS_DIRSEP(*str)) {
       
  1484 	    *str = '\0';
       
  1485 	    *junkptr = dupstring(str + 1);
       
  1486 	    return 1;
       
  1487 	}
       
  1488     return 0;
       
  1489 }
       
  1490 
       
  1491 /**/
       
  1492 int
       
  1493 makeuppercase(char **junkptr)
       
  1494 {
       
  1495     char *str = *junkptr;
       
  1496 
       
  1497     for (; *str; str++)
       
  1498 	*str = tuupper(*str);
       
  1499     return 1;
       
  1500 }
       
  1501 
       
  1502 /**/
       
  1503 int
       
  1504 makelowercase(char **junkptr)
       
  1505 {
       
  1506     char *str = *junkptr;
       
  1507 
       
  1508     for (; *str; str++)
       
  1509 	*str = tulower(*str);
       
  1510     return 1;
       
  1511 }
       
  1512 
       
  1513 /**/
       
  1514 int
       
  1515 makecapitals(char **junkptr)
       
  1516 {
       
  1517     char *str = *junkptr;
       
  1518 
       
  1519     for (; *str;) {
       
  1520 	for (; *str && !ialnum(*str); str++);
       
  1521 	if (*str)
       
  1522 	    *str = tuupper(*str), str++;
       
  1523 	for (; *str && ialnum(*str); str++)
       
  1524 	    *str = tulower(*str);
       
  1525     }
       
  1526     return 1;
       
  1527 }
       
  1528 
       
  1529 /**/
       
  1530 void
       
  1531 subst(char **strptr, char *in, char *out, int gbal)
       
  1532 {
       
  1533     char *str = *strptr, *instr = *strptr, *substcut, *sptr, *oldstr;
       
  1534     int off, inlen, outlen;
       
  1535 
       
  1536     if (!*in)
       
  1537 	in = str, gbal = 0;
       
  1538     if (!(substcut = (char *)strstr(str, in)))
       
  1539 	return;
       
  1540     inlen = strlen(in);
       
  1541     sptr = convamps(out, in, inlen);
       
  1542     outlen = strlen(sptr);
       
  1543 
       
  1544     do {
       
  1545 	*substcut = '\0';
       
  1546 	off = substcut - *strptr + outlen;
       
  1547 	substcut += inlen;
       
  1548 	*strptr = tricat(oldstr = *strptr, sptr, substcut);
       
  1549 	if (oldstr != instr)
       
  1550 	    zsfree(oldstr);
       
  1551 	str = (char *)*strptr + off;
       
  1552     } while (gbal && (substcut = (char *)strstr(str, in)));
       
  1553 }
       
  1554 
       
  1555 /**/
       
  1556 static char *
       
  1557 convamps(char *out, char *in, int inlen)
       
  1558 {
       
  1559     char *ptr, *ret, *pp;
       
  1560     int slen, sdup = 0;
       
  1561 
       
  1562     for (ptr = out, slen = 0; *ptr; ptr++, slen++)
       
  1563 	if (*ptr == '\\')
       
  1564 	    ptr++, sdup = 1;
       
  1565 	else if (*ptr == '&')
       
  1566 	    slen += inlen - 1, sdup = 1;
       
  1567     if (!sdup)
       
  1568 	return out;
       
  1569     ret = pp = (char *) zhalloc(slen + 1);
       
  1570     for (ptr = out; *ptr; ptr++)
       
  1571 	if (*ptr == '\\')
       
  1572 	    *pp++ = *++ptr;
       
  1573 	else if (*ptr == '&') {
       
  1574 	    strcpy(pp, in);
       
  1575 	    pp += inlen;
       
  1576 	} else
       
  1577 	    *pp++ = *ptr;
       
  1578     *pp = '\0';
       
  1579     return ret;
       
  1580 }
       
  1581 
       
  1582 /**/
       
  1583 mod_export void
       
  1584 checkcurline(Histent he)
       
  1585 {
       
  1586     if (he->histnum == curhist && (histactive & HA_ACTIVE)) {
       
  1587 	curline.text = chline;
       
  1588 	curline.nwords = chwordpos/2;
       
  1589 	curline.words = chwords;
       
  1590     }
       
  1591 }
       
  1592 
       
  1593 /**/
       
  1594 mod_export Histent
       
  1595 quietgethist(int ev)
       
  1596 {
       
  1597     return gethistent(ev, GETHIST_EXACT);
       
  1598 }
       
  1599 
       
  1600 /**/
       
  1601 static Histent
       
  1602 gethist(int ev)
       
  1603 {
       
  1604     Histent ret;
       
  1605 
       
  1606     ret = quietgethist(ev);
       
  1607     if (!ret) {
       
  1608 	herrflush();
       
  1609 	zerr("no such event: %d", NULL, ev);
       
  1610     }
       
  1611     return ret;
       
  1612 }
       
  1613 
       
  1614 /**/
       
  1615 static char *
       
  1616 getargs(Histent elist, int arg1, int arg2)
       
  1617 {
       
  1618     short *words = elist->words;
       
  1619     int pos1, nwords = elist->nwords;
       
  1620 
       
  1621     if (arg2 < arg1 || arg1 >= nwords || arg2 >= nwords) {
       
  1622 	/* remember, argN is indexed from 0, nwords is total no. of words */
       
  1623 	herrflush();
       
  1624 	zerr("no such word in event", NULL, 0);
       
  1625 	return NULL;
       
  1626     }
       
  1627 
       
  1628     pos1 = words[2*arg1];
       
  1629     return dupstrpfx(elist->text + pos1, words[2*arg2+1] - pos1);
       
  1630 }
       
  1631 
       
  1632 /**/
       
  1633 void
       
  1634 upcase(char **x)
       
  1635 {
       
  1636     char *pp = *(char **)x;
       
  1637 
       
  1638     for (; *pp; pp++)
       
  1639 	*pp = tuupper(*pp);
       
  1640 }
       
  1641 
       
  1642 /**/
       
  1643 void
       
  1644 downcase(char **x)
       
  1645 {
       
  1646     char *pp = *(char **)x;
       
  1647 
       
  1648     for (; *pp; pp++)
       
  1649 	*pp = tulower(*pp);
       
  1650 }
       
  1651 
       
  1652 /**/
       
  1653 int
       
  1654 quote(char **tr)
       
  1655 {
       
  1656     char *ptr, *rptr, **str = (char **)tr;
       
  1657     int len = 3;
       
  1658     int inquotes = 0;
       
  1659 
       
  1660     for (ptr = *str; *ptr; ptr++, len++)
       
  1661 	if (*ptr == '\'') {
       
  1662 	    len += 3;
       
  1663 	    if (!inquotes)
       
  1664 		inquotes = 1;
       
  1665 	    else
       
  1666 		inquotes = 0;
       
  1667 	} else if (inblank(*ptr) && !inquotes && ptr[-1] != '\\')
       
  1668 	    len += 2;
       
  1669     ptr = *str;
       
  1670     *str = rptr = (char *) zhalloc(len);
       
  1671     *rptr++ = '\'';
       
  1672     for (; *ptr; ptr++)
       
  1673 	if (*ptr == '\'') {
       
  1674 	    if (!inquotes)
       
  1675 		inquotes = 1;
       
  1676 	    else
       
  1677 		inquotes = 0;
       
  1678 	    *rptr++ = '\'';
       
  1679 	    *rptr++ = '\\';
       
  1680 	    *rptr++ = '\'';
       
  1681 	    *rptr++ = '\'';
       
  1682 	} else if (inblank(*ptr) && !inquotes && ptr[-1] != '\\') {
       
  1683 	    *rptr++ = '\'';
       
  1684 	    *rptr++ = *ptr;
       
  1685 	    *rptr++ = '\'';
       
  1686 	} else
       
  1687 	    *rptr++ = *ptr;
       
  1688     *rptr++ = '\'';
       
  1689     *rptr++ = 0;
       
  1690     str[1] = NULL;
       
  1691     return 0;
       
  1692 }
       
  1693 
       
  1694 /**/
       
  1695 static int
       
  1696 quotebreak(char **tr)
       
  1697 {
       
  1698     char *ptr, *rptr, **str = (char **)tr;
       
  1699     int len = 3;
       
  1700 
       
  1701     for (ptr = *str; *ptr; ptr++, len++)
       
  1702 	if (*ptr == '\'')
       
  1703 	    len += 3;
       
  1704 	else if (inblank(*ptr))
       
  1705 	    len += 2;
       
  1706     ptr = *str;
       
  1707     *str = rptr = (char *) zhalloc(len);
       
  1708     *rptr++ = '\'';
       
  1709     for (; *ptr;)
       
  1710 	if (*ptr == '\'') {
       
  1711 	    *rptr++ = '\'';
       
  1712 	    *rptr++ = '\\';
       
  1713 	    *rptr++ = '\'';
       
  1714 	    *rptr++ = '\'';
       
  1715 	    ptr++;
       
  1716 	} else if (inblank(*ptr)) {
       
  1717 	    *rptr++ = '\'';
       
  1718 	    *rptr++ = *ptr++;
       
  1719 	    *rptr++ = '\'';
       
  1720 	} else
       
  1721 	    *rptr++ = *ptr++;
       
  1722     *rptr++ = '\'';
       
  1723     *rptr++ = '\0';
       
  1724     return 0;
       
  1725 }
       
  1726 
       
  1727 /* read an arbitrary amount of data into a buffer until stop is found */
       
  1728 
       
  1729 #if 0 /**/
       
  1730 char *
       
  1731 hdynread(int stop)
       
  1732 {
       
  1733     int bsiz = 256, ct = 0, c;
       
  1734     char *buf = (char *)zalloc(bsiz), *ptr;
       
  1735 
       
  1736     ptr = buf;
       
  1737     while ((c = ingetc()) != stop && c != '\n' && !lexstop) {
       
  1738 	if (c == '\\')
       
  1739 	    c = ingetc();
       
  1740 	*ptr++ = c;
       
  1741 	if (++ct == bsiz) {
       
  1742 	    buf = realloc(buf, bsiz *= 2);
       
  1743 	    ptr = buf + ct;
       
  1744 	}
       
  1745     }
       
  1746     *ptr = 0;
       
  1747     if (c == '\n') {
       
  1748 	inungetc('\n');
       
  1749 	zerr("delimiter expected", NULL, 0);
       
  1750 	zfree(buf, bsiz);
       
  1751 	return NULL;
       
  1752     }
       
  1753     return buf;
       
  1754 }
       
  1755 #endif
       
  1756 
       
  1757 /**/
       
  1758 static char *
       
  1759 hdynread2(int stop)
       
  1760 {
       
  1761     int bsiz = 256, ct = 0, c;
       
  1762     char *buf = (char *)zalloc(bsiz), *ptr;
       
  1763 
       
  1764     ptr = buf;
       
  1765     while ((c = ingetc()) != stop && c != '\n' && !lexstop) {
       
  1766 	if (c == '\n') {
       
  1767 	    inungetc(c);
       
  1768 	    break;
       
  1769 	}
       
  1770 	if (c == '\\')
       
  1771 	    c = ingetc();
       
  1772 	*ptr++ = c;
       
  1773 	if (++ct == bsiz) {
       
  1774 	    buf = realloc(buf, bsiz *= 2);
       
  1775 	    ptr = buf + ct;
       
  1776 	}
       
  1777     }
       
  1778     *ptr = 0;
       
  1779     if (c == '\n')
       
  1780 	inungetc('\n');
       
  1781     return buf;
       
  1782 }
       
  1783 
       
  1784 /**/
       
  1785 void
       
  1786 inithist(void)
       
  1787 {
       
  1788     createhisttable();
       
  1789 }
       
  1790 
       
  1791 /**/
       
  1792 void
       
  1793 resizehistents(void)
       
  1794 {
       
  1795     if (histlinect > histsiz) {
       
  1796 	/* The reason we don't just call freehistnode(hist_ring->down) is
       
  1797 	 * so that we can honor the HISTEXPIREDUPSFIRST setting. */
       
  1798 	putoldhistentryontop(0);
       
  1799 	freehistnode((HashNode)hist_ring);
       
  1800 	while (histlinect > histsiz) {
       
  1801 	    putoldhistentryontop(1);
       
  1802 	    freehistnode((HashNode)hist_ring);
       
  1803 	}
       
  1804     }
       
  1805 }
       
  1806 
       
  1807 /* Remember the last line in the history file so we can find it again. */
       
  1808 static struct histfile_stats {
       
  1809     char *text;
       
  1810     time_t stim, mtim;
       
  1811     off_t fpos, fsiz;
       
  1812     zlong next_write_ev;
       
  1813 } lasthist;
       
  1814 
       
  1815 static struct histsave {
       
  1816     struct histfile_stats lasthist;
       
  1817     char *histfile;
       
  1818     HashTable histtab;
       
  1819     Histent hist_ring;
       
  1820     zlong curhist;
       
  1821     zlong histlinect;
       
  1822     zlong histsiz;
       
  1823     zlong savehistsiz;
       
  1824     int locallevel;
       
  1825 } *histsave_stack;
       
  1826 static int histsave_stack_size = 0;
       
  1827 static int histsave_stack_pos = 0;
       
  1828 
       
  1829 static zlong histfile_linect;
       
  1830 
       
  1831 static int
       
  1832 readhistline(int start, char **bufp, int *bufsiz, FILE *in)
       
  1833 {
       
  1834     char *buf = *bufp;
       
  1835     if (fgets(buf + start, *bufsiz - start, in)) {
       
  1836 	int len = start + strlen(buf + start);
       
  1837 	if (len == start)
       
  1838 	    return -1;
       
  1839 	if (buf[len - 1] != '\n') {
       
  1840 	    if (!feof(in)) {
       
  1841 		if (len < (*bufsiz) - 1)
       
  1842 		    return -1;
       
  1843 		*bufp = zrealloc(buf, 2 * (*bufsiz));
       
  1844 		*bufsiz = 2 * (*bufsiz);
       
  1845 		return readhistline(len, bufp, bufsiz, in);
       
  1846 	    }
       
  1847 	}
       
  1848 	else {
       
  1849 	    buf[len - 1] = '\0';
       
  1850 	    if (len > 1 && buf[len - 2] == '\\') {
       
  1851 		buf[--len - 1] = '\n';
       
  1852 		if (!feof(in))
       
  1853 		    return readhistline(len, bufp, bufsiz, in);
       
  1854 	    }
       
  1855 	}
       
  1856 	return len;
       
  1857     }
       
  1858     return 0;
       
  1859 }
       
  1860 
       
  1861 /**/
       
  1862 void
       
  1863 readhistfile(char *fn, int err, int readflags)
       
  1864 {
       
  1865     char *buf, *start = NULL;
       
  1866     FILE *in;
       
  1867     Histent he;
       
  1868     time_t stim, ftim, tim = time(NULL);
       
  1869     off_t fpos;
       
  1870     short *wordlist;
       
  1871     struct stat sb;
       
  1872     int nwordpos, nwordlist, bufsiz;
       
  1873     int searching, newflags, l;
       
  1874 
       
  1875     if (!fn && !(fn = getsparam("HISTFILE")))
       
  1876 	return;
       
  1877     if (readflags & HFILE_FAST) {
       
  1878 	if (stat(unmeta(fn), &sb) < 0
       
  1879 	 || (lasthist.fsiz == sb.st_size && lasthist.mtim == sb.st_mtime)
       
  1880 	 || !lockhistfile(fn, 0))
       
  1881 	    return;
       
  1882 	lasthist.fsiz = sb.st_size;
       
  1883 	lasthist.mtim = sb.st_mtime;
       
  1884     }
       
  1885     else if (!lockhistfile(fn, 1))
       
  1886 	return;
       
  1887     if ((in = fopen(unmeta(fn), "r"))) {
       
  1888 	nwordlist = 64;
       
  1889 	wordlist = (short *)zalloc(nwordlist*sizeof(short));
       
  1890 	bufsiz = 1024;
       
  1891 	buf = zalloc(bufsiz);
       
  1892 
       
  1893 	if (readflags & HFILE_FAST && lasthist.text) {
       
  1894 	    if (lasthist.fpos < lasthist.fsiz) {
       
  1895 		fseek(in, lasthist.fpos, 0);
       
  1896 		searching = 1;
       
  1897 	    }
       
  1898 	    else {
       
  1899 		histfile_linect = 0;
       
  1900 		searching = -1;
       
  1901 	    }
       
  1902 	} else
       
  1903 	    searching = 0;
       
  1904 
       
  1905 	newflags = HIST_OLD | HIST_READ;
       
  1906 	if (readflags & HFILE_FAST)
       
  1907 	    newflags |= HIST_FOREIGN;
       
  1908 	if (readflags & HFILE_SKIPOLD
       
  1909 	 || (hist_ignore_all_dups && newflags & hist_skip_flags))
       
  1910 	    newflags |= HIST_MAKEUNIQUE;
       
  1911 	while (fpos = ftell(in), (l = readhistline(0, &buf, &bufsiz, in))) {
       
  1912 	    char *pt = buf;
       
  1913 
       
  1914 	    if (l < 0) {
       
  1915 		zerr("corrupt history file %s", fn, 0);
       
  1916 		break;
       
  1917 	    }
       
  1918 	    if (*pt == ':') {
       
  1919 		pt++;
       
  1920 		stim = zstrtol(pt, NULL, 0);
       
  1921 		for (; *pt != ':' && *pt; pt++);
       
  1922 		if (*pt) {
       
  1923 		    pt++;
       
  1924 		    ftim = zstrtol(pt, NULL, 0);
       
  1925 		    for (; *pt != ';' && *pt; pt++);
       
  1926 		    if (*pt)
       
  1927 			pt++;
       
  1928 		} else
       
  1929 		    ftim = stim;
       
  1930 	    } else {
       
  1931 		if (*pt == '\\' && pt[1] == ':')
       
  1932 		    pt++;
       
  1933 		stim = ftim = 0;
       
  1934 	    }
       
  1935 
       
  1936 	    if (searching) {
       
  1937 		if (searching > 0) {
       
  1938 		    if (stim == lasthist.stim
       
  1939 		     && histstrcmp(pt, lasthist.text) == 0)
       
  1940 			searching = 0;
       
  1941 		    else {
       
  1942 			fseek(in, 0, 0);
       
  1943 			histfile_linect = 0;
       
  1944 			searching = -1;
       
  1945 		    }
       
  1946 		    continue;
       
  1947 		}
       
  1948 		else if (stim < lasthist.stim) {
       
  1949 		    histfile_linect++;
       
  1950 		    continue;
       
  1951 		}
       
  1952 		searching = 0;
       
  1953 	    }
       
  1954 
       
  1955 	    if (readflags & HFILE_USE_OPTIONS) {
       
  1956 		histfile_linect++;
       
  1957 		lasthist.fpos = fpos;
       
  1958 		lasthist.stim = stim;
       
  1959 	    }
       
  1960 
       
  1961 	    he = prepnexthistent();
       
  1962 	    he->text = ztrdup(pt);
       
  1963 	    he->flags = newflags;
       
  1964 	    if ((he->stim = stim) == 0)
       
  1965 		he->stim = he->ftim = tim;
       
  1966 	    else if (ftim < stim)
       
  1967 		he->ftim = stim + ftim;
       
  1968 	    else
       
  1969 		he->ftim = ftim;
       
  1970 
       
  1971 	    /* Divide up the words.  We don't know how it lexes,
       
  1972 	       so just look for white-space.
       
  1973 	       */
       
  1974 	    nwordpos = 0;
       
  1975 	    start = pt;
       
  1976 	    do {
       
  1977 		while (inblank(*pt))
       
  1978 		    pt++;
       
  1979 		if (*pt) {
       
  1980 		    if (nwordpos >= nwordlist)
       
  1981 			wordlist = (short *) realloc(wordlist,
       
  1982 					(nwordlist += 64)*sizeof(short));
       
  1983 		    wordlist[nwordpos++] = pt - start;
       
  1984 		    while (*pt && !inblank(*pt))
       
  1985 			pt++;
       
  1986 		    wordlist[nwordpos++] = pt - start;
       
  1987 		}
       
  1988 	    } while (*pt);
       
  1989 
       
  1990 	    he->nwords = nwordpos/2;
       
  1991 	    if (he->nwords) {
       
  1992 		he->words = (short *)zalloc(nwordpos*sizeof(short));
       
  1993 		memcpy(he->words, wordlist, nwordpos*sizeof(short));
       
  1994 	    } else
       
  1995 		he->words = (short *)NULL;
       
  1996 	    addhistnode(histtab, he->text, he);
       
  1997 	    if (he->flags & HIST_DUP) {
       
  1998 		freehistnode((HashNode)he);
       
  1999 		curhist--;
       
  2000 	    }
       
  2001 	}
       
  2002 	if (start && readflags & HFILE_USE_OPTIONS) {
       
  2003 	    zsfree(lasthist.text);
       
  2004 	    lasthist.text = ztrdup(start);
       
  2005 	}
       
  2006 	zfree(wordlist, nwordlist*sizeof(short));
       
  2007 	zfree(buf, bufsiz);
       
  2008 
       
  2009 	fclose(in);
       
  2010     } else if (err)
       
  2011 	zerr("can't read history file %s", fn, 0);
       
  2012 
       
  2013     unlockhistfile(fn);
       
  2014 }
       
  2015 
       
  2016 /**/
       
  2017 void
       
  2018 savehistfile(char *fn, int err, int writeflags)
       
  2019 {
       
  2020     char *t, *start = NULL;
       
  2021     FILE *out;
       
  2022     Histent he;
       
  2023     zlong xcurhist = curhist - !!(histactive & HA_ACTIVE);
       
  2024     int extended_history = isset(EXTENDEDHISTORY);
       
  2025 
       
  2026     if (!interact || savehistsiz <= 0 || !hist_ring
       
  2027      || (!fn && !(fn = getsparam("HISTFILE"))))
       
  2028 	return;
       
  2029     if (writeflags & HFILE_FAST) {
       
  2030 	he = gethistent(lasthist.next_write_ev, GETHIST_DOWNWARD);
       
  2031 	while (he && he->flags & HIST_OLD) {
       
  2032 	    lasthist.next_write_ev = he->histnum + 1;
       
  2033 	    he = down_histent(he);
       
  2034 	}
       
  2035 	if (!he || !lockhistfile(fn, 0))
       
  2036 	    return;
       
  2037 	if (histfile_linect > savehistsiz + savehistsiz / 5)
       
  2038 	    writeflags &= ~HFILE_FAST;
       
  2039     }
       
  2040     else {
       
  2041 	if (!lockhistfile(fn, 1))
       
  2042 	    return;
       
  2043 	he = hist_ring->down;
       
  2044     }
       
  2045     if (writeflags & HFILE_USE_OPTIONS) {
       
  2046 	if (isset(APPENDHISTORY) || isset(INCAPPENDHISTORY)
       
  2047 	 || isset(SHAREHISTORY))
       
  2048 	    writeflags |= HFILE_APPEND | HFILE_SKIPOLD;
       
  2049 	else
       
  2050 	    histfile_linect = 0;
       
  2051 	if (isset(HISTSAVENODUPS))
       
  2052 	    writeflags |= HFILE_SKIPDUPS;
       
  2053 	if (isset(SHAREHISTORY))
       
  2054 	    extended_history = 1;
       
  2055     }
       
  2056     if (writeflags & HFILE_APPEND) {
       
  2057 	out = fdopen(open(unmeta(fn),
       
  2058 			O_CREAT | O_WRONLY | O_APPEND | O_NOCTTY, 0600), "a");
       
  2059     }
       
  2060     else {
       
  2061 	out = fdopen(open(unmeta(fn),
       
  2062 			 O_CREAT | O_WRONLY | O_TRUNC | O_NOCTTY, 0600), "w");
       
  2063     }
       
  2064     if (out) {
       
  2065 	for (; he && he->histnum <= xcurhist; he = down_histent(he)) {
       
  2066 	    if ((writeflags & HFILE_SKIPDUPS && he->flags & HIST_DUP)
       
  2067 	     || (writeflags & HFILE_SKIPFOREIGN && he->flags & HIST_FOREIGN)
       
  2068 	     || he->flags & HIST_TMPSTORE)
       
  2069 		continue;
       
  2070 	    if (writeflags & HFILE_SKIPOLD) {
       
  2071 		if (he->flags & HIST_OLD)
       
  2072 		    continue;
       
  2073 		he->flags |= HIST_OLD;
       
  2074 		if (writeflags & HFILE_USE_OPTIONS)
       
  2075 		    lasthist.next_write_ev = he->histnum + 1;
       
  2076 	    }
       
  2077 	    if (writeflags & HFILE_USE_OPTIONS) {
       
  2078 		lasthist.fpos = ftell(out);
       
  2079 		lasthist.stim = he->stim;
       
  2080 		histfile_linect++;
       
  2081 	    }
       
  2082 	    t = start = he->text;
       
  2083 	    if (extended_history) {
       
  2084 		fprintf(out, ": %ld:%ld;", (long)he->stim,
       
  2085 			he->ftim? (long)(he->ftim - he->stim) : 0L);
       
  2086 	    } else if (*t == ':')
       
  2087 		fputc('\\', out);
       
  2088 
       
  2089 	    for (; *t; t++) {
       
  2090 		if (*t == '\n')
       
  2091 		    fputc('\\', out);
       
  2092 		fputc(*t, out);
       
  2093 	    }
       
  2094 	    fputc('\n', out);
       
  2095 	}
       
  2096 	if (start && writeflags & HFILE_USE_OPTIONS) {
       
  2097 	    struct stat sb;
       
  2098 	    fflush(out);
       
  2099 	    if (fstat(fileno(out), &sb) == 0) {
       
  2100 		lasthist.fsiz = sb.st_size;
       
  2101 		lasthist.mtim = sb.st_mtime;
       
  2102 	    }
       
  2103 	    zsfree(lasthist.text);
       
  2104 	    lasthist.text = ztrdup(start);
       
  2105 	}
       
  2106 	fclose(out);
       
  2107 
       
  2108 	if (writeflags & HFILE_SKIPOLD
       
  2109 	 && !(writeflags & (HFILE_FAST | HFILE_NO_REWRITE))) {
       
  2110 	    int remember_histactive = histactive;
       
  2111 
       
  2112 	    /* Zeroing histactive avoids unnecessary munging of curline. */
       
  2113 	    histactive = 0;
       
  2114 	    /* The NULL leaves HISTFILE alone, preserving fn's value. */
       
  2115 	    pushhiststack(NULL, savehistsiz, savehistsiz, -1);
       
  2116 
       
  2117 	    hist_ignore_all_dups |= isset(HISTSAVENODUPS);
       
  2118 	    readhistfile(fn, err, 0);
       
  2119 	    hist_ignore_all_dups = isset(HISTIGNOREALLDUPS);
       
  2120 	    if (histlinect)
       
  2121 		savehistfile(fn, err, 0);
       
  2122 
       
  2123 	    pophiststack();
       
  2124 	    histactive = remember_histactive;
       
  2125 	}
       
  2126     } else if (err)
       
  2127 	zerr("can't write history file %s", fn, 0);
       
  2128 
       
  2129     unlockhistfile(fn);
       
  2130 }
       
  2131 
       
  2132 static int lockhistct;
       
  2133 
       
  2134 /**/
       
  2135 int
       
  2136 lockhistfile(char *fn, int keep_trying)
       
  2137 {
       
  2138     int ct = lockhistct;
       
  2139 
       
  2140     if (!fn && !(fn = getsparam("HISTFILE")))
       
  2141 	return 0;
       
  2142     if (!lockhistct++) {
       
  2143 	struct stat sb;
       
  2144 	int fd;
       
  2145 	char *lockfile;
       
  2146 #ifdef HAVE_LINK
       
  2147 	char *tmpfile;
       
  2148 #endif
       
  2149 
       
  2150 	lockfile = bicat(unmeta(fn), ".LOCK");
       
  2151 #ifdef HAVE_LINK
       
  2152 	if ((fd = gettempfile(fn, 0, &tmpfile)) >= 0) {
       
  2153 	    FILE *out = fdopen(fd, "w");
       
  2154 	    if (out) {
       
  2155 		fprintf(out, "%ld %s\n", (long)getpid(), getsparam("HOST"));
       
  2156 		fclose(out);
       
  2157 	    } else
       
  2158 		close(fd);
       
  2159 	    while (link(tmpfile, lockfile) < 0) {
       
  2160 		if (errno != EEXIST || !keep_trying)
       
  2161 		    ;
       
  2162 		else if (stat(lockfile, &sb) < 0) {
       
  2163 		    if (errno == ENOENT)
       
  2164 			continue;
       
  2165 		}
       
  2166 		else {
       
  2167 		    if (time(NULL) - sb.st_mtime < 10)
       
  2168 			sleep(1);
       
  2169 		    else
       
  2170 			unlink(lockfile);
       
  2171 		    continue;
       
  2172 		}
       
  2173 		lockhistct--;
       
  2174 		break;
       
  2175 	    }
       
  2176 	    unlink(tmpfile);
       
  2177 	    free(tmpfile);
       
  2178 	}
       
  2179 #else /* not HAVE_LINK */
       
  2180 	while ((fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0) {
       
  2181 	    if (errno != EEXIST || !keep_trying)
       
  2182 		break;
       
  2183 	    if (stat(lockfile, &sb) < 0) {
       
  2184 		if (errno == ENOENT)
       
  2185 		    continue;
       
  2186 		break;
       
  2187 	    }
       
  2188 	    if (time(NULL) - sb.st_mtime < 10)
       
  2189 		sleep(1);
       
  2190 	    else
       
  2191 		unlink(lockfile);
       
  2192 	}
       
  2193 	if (fd < 0)
       
  2194 	    lockhistct--;
       
  2195 	else {
       
  2196 	    FILE *out = fdopen(fd, "w");
       
  2197 	    if (out) {
       
  2198 		fprintf(out, "%ld %s\n", (long)mypid, getsparam("HOST"));
       
  2199 		fclose(out);
       
  2200 	    } else
       
  2201 		close(fd);
       
  2202 	}
       
  2203 #endif /* not HAVE_LINK */
       
  2204 	free(lockfile);
       
  2205     }
       
  2206     return ct != lockhistct;
       
  2207 }
       
  2208 
       
  2209 /* Unlock the history file if this corresponds to the last nested lock
       
  2210  * request.  If we don't have the file locked, just return.
       
  2211  */
       
  2212 
       
  2213 /**/
       
  2214 void
       
  2215 unlockhistfile(char *fn)
       
  2216 {
       
  2217     if (!fn && !(fn = getsparam("HISTFILE")))
       
  2218 	return;
       
  2219     if (--lockhistct) {
       
  2220 	if (lockhistct < 0)
       
  2221 	    lockhistct = 0;
       
  2222     }
       
  2223     else {
       
  2224 	char *lockfile;
       
  2225 	fn = unmeta(fn);
       
  2226 	lockfile = zalloc(strlen(fn) + 5 + 1);
       
  2227 	sprintf(lockfile, "%s.LOCK", fn);
       
  2228 	unlink(lockfile);
       
  2229 	free(lockfile);
       
  2230     }
       
  2231 }
       
  2232 
       
  2233 /**/
       
  2234 int
       
  2235 histfileIsLocked(void)
       
  2236 {
       
  2237     return lockhistct > 0;
       
  2238 }
       
  2239 
       
  2240 /* Get the words in the current buffer. Using the lexer. */
       
  2241 
       
  2242 /**/
       
  2243 mod_export LinkList
       
  2244 bufferwords(LinkList list, char *buf, int *index)
       
  2245 {
       
  2246     int num = 0, cur = -1, got = 0, ne = noerrs, ocs = cs, oll = ll;
       
  2247     int owb = wb, owe = we, oadx = addedx, ozp = zleparse, onc = nocomments;
       
  2248     int ona = noaliases;
       
  2249     char *p;
       
  2250 
       
  2251     if (!list)
       
  2252 	list = newlinklist();
       
  2253 
       
  2254     zleparse = 1;
       
  2255     addedx = 0;
       
  2256     noerrs = 1;
       
  2257     lexsave();
       
  2258     if (buf) {
       
  2259 	int l = strlen(buf);
       
  2260 
       
  2261 	p = (char *) zhalloc(l + 2);
       
  2262 	memcpy(p, buf, l);
       
  2263 	p[l] = ' ';
       
  2264 	p[l + 1] = '\0';
       
  2265 	inpush(p, 0, NULL);
       
  2266 	cs = strlen(p) + 1;
       
  2267 	nocomments = 1;
       
  2268     } else if (!isfirstln && chline) {
       
  2269 	p = (char *) zhalloc(hptr - chline + ll + 2);
       
  2270 	memcpy(p, chline, hptr - chline);
       
  2271 	memcpy(p + (hptr - chline), line, ll);
       
  2272 	p[(hptr - chline) + ll] = ' ';
       
  2273 	p[(hptr - chline) + ll + 1] = '\0';
       
  2274 	inpush(p, 0, NULL);
       
  2275 	cs += hptr - chline;
       
  2276     } else {
       
  2277 	p = (char *) zhalloc(ll + 2);
       
  2278 	memcpy(p, line, ll);
       
  2279 	p[ll] = ' ';
       
  2280 	p[ll + 1] = '\0';
       
  2281 	inpush(p, 0, NULL);
       
  2282     }
       
  2283     ll = strlen(p);
       
  2284     if (cs)
       
  2285 	cs--;
       
  2286     strinbeg(0);
       
  2287     noaliases = 1;
       
  2288     do {
       
  2289 	if (incond)
       
  2290 	    incond = 1 + (tok != DINBRACK && tok != INPAR &&
       
  2291 			  tok != DBAR && tok != DAMPER &&
       
  2292 			  tok != BANG);
       
  2293 	ctxtlex();
       
  2294 	if (tok == ENDINPUT || tok == LEXERR)
       
  2295 	    break;
       
  2296 	if (tokstr && *tokstr) {
       
  2297 	    untokenize((p = dupstring(tokstr)));
       
  2298 	    addlinknode(list, p);
       
  2299 	    num++;
       
  2300 	} else if (buf) {
       
  2301 	    if (IS_REDIROP(tok) && tokfd >= 0) {
       
  2302 		char b[20];
       
  2303 
       
  2304 		sprintf(b, "%d%s", tokfd, tokstrings[tok]);
       
  2305 		addlinknode(list, dupstring(b));
       
  2306 		num++;
       
  2307 	    } else if (tok != NEWLIN) {
       
  2308 		addlinknode(list, dupstring(tokstrings[tok]));
       
  2309 		num++;
       
  2310 	    }
       
  2311 	}
       
  2312 	if (!got && !zleparse) {
       
  2313 	    got = 1;
       
  2314 	    cur = num - 1;
       
  2315 	}
       
  2316     } while (tok != ENDINPUT && tok != LEXERR);
       
  2317     if (buf && tok == LEXERR && tokstr && *tokstr) {
       
  2318 	int plen;
       
  2319 	untokenize((p = dupstring(tokstr)));
       
  2320 	plen = strlen(p);
       
  2321 	/*
       
  2322 	 * Strip the space we added for lexing but which won't have
       
  2323 	 * been swallowed by the lexer because we aborted early.
       
  2324 	 * The test is paranoia.
       
  2325 	 */
       
  2326 	if (plen && p[plen-1] == ' ' && (plen == 1 || p[plen-2] != Meta))
       
  2327 	    p[plen - 1] = '\0';
       
  2328 	addlinknode(list, p);
       
  2329 	num++;
       
  2330     }
       
  2331     if (cur < 0 && num)
       
  2332 	cur = num - 1;
       
  2333     noaliases = ona;
       
  2334     strinend();
       
  2335     inpop();
       
  2336     errflag = 0;
       
  2337     zleparse = ozp;
       
  2338     nocomments = onc;
       
  2339     noerrs = ne;
       
  2340     lexrestore();
       
  2341     cs = ocs;
       
  2342     ll = oll;
       
  2343     wb = owb;
       
  2344     we = owe;
       
  2345     addedx = oadx;
       
  2346 
       
  2347     if (index)
       
  2348 	*index = cur;
       
  2349 
       
  2350     return list;
       
  2351 }
       
  2352 
       
  2353 /* Move the current history list out of the way and prepare a fresh history
       
  2354  * list using hf for HISTFILE, hs for HISTSIZE, and shs for SAVEHIST.  If
       
  2355  * the hf value is an empty string, HISTFILE will be unset from the new
       
  2356  * environment; if it is NULL, HISTFILE will not be changed, not even by the
       
  2357  * pop function (this functionality is used internally to rewrite the current
       
  2358  * history file without affecting pointers into the environment).
       
  2359  */
       
  2360 
       
  2361 /**/
       
  2362 int
       
  2363 pushhiststack(char *hf, zlong hs, zlong shs, int level)
       
  2364 {
       
  2365     struct histsave *h;
       
  2366     int curline_in_ring = (histactive & HA_ACTIVE) && hist_ring == &curline;
       
  2367 
       
  2368     if (histsave_stack_pos == histsave_stack_size) {
       
  2369 	histsave_stack_size += 5;
       
  2370 	histsave_stack = zrealloc(histsave_stack,
       
  2371 			    histsave_stack_size * sizeof (struct histsave));
       
  2372     }
       
  2373 
       
  2374     if (curline_in_ring)
       
  2375 	unlinkcurline();
       
  2376 
       
  2377     h = &histsave_stack[histsave_stack_pos++];
       
  2378 
       
  2379     h->lasthist = lasthist;
       
  2380     if (hf) {
       
  2381 	if ((h->histfile = getsparam("HISTFILE")) != NULL && *h->histfile)
       
  2382 	    h->histfile = ztrdup(h->histfile);
       
  2383 	else
       
  2384 	    h->histfile = "";
       
  2385     } else
       
  2386 	h->histfile = NULL;
       
  2387     h->histtab = histtab;
       
  2388     h->hist_ring = hist_ring;
       
  2389     h->curhist = curhist;
       
  2390     h->histlinect = histlinect;
       
  2391     h->histsiz = histsiz;
       
  2392     h->savehistsiz = savehistsiz;
       
  2393     h->locallevel = level;
       
  2394 
       
  2395     memset(&lasthist, 0, sizeof lasthist);
       
  2396     if (hf) {
       
  2397 	if (*hf)
       
  2398 	    setsparam("HISTFILE", ztrdup(hf));
       
  2399 	else
       
  2400 	    unsetparam("HISTFILE");
       
  2401     }
       
  2402     hist_ring = NULL;
       
  2403     curhist = histlinect = 0;
       
  2404     histsiz = hs;
       
  2405     savehistsiz = shs;
       
  2406     inithist(); /* sets histtab */
       
  2407 
       
  2408     if (curline_in_ring)
       
  2409 	linkcurline();
       
  2410 
       
  2411     return histsave_stack_pos;
       
  2412 }
       
  2413 
       
  2414 
       
  2415 /**/
       
  2416 int
       
  2417 pophiststack(void)
       
  2418 {
       
  2419     struct histsave *h;
       
  2420     int curline_in_ring = (histactive & HA_ACTIVE) && hist_ring == &curline;
       
  2421 
       
  2422     if (histsave_stack_pos == 0)
       
  2423 	return 0;
       
  2424 
       
  2425     if (curline_in_ring)
       
  2426 	unlinkcurline();
       
  2427 
       
  2428     deletehashtable(histtab);
       
  2429     zsfree(lasthist.text);
       
  2430 
       
  2431     h = &histsave_stack[--histsave_stack_pos];
       
  2432 
       
  2433     lasthist = h->lasthist;
       
  2434     if (h->histfile) {
       
  2435 	if (*h->histfile)
       
  2436 	    setsparam("HISTFILE", h->histfile);
       
  2437 	else
       
  2438 	    unsetparam("HISTFILE");
       
  2439     }
       
  2440     histtab = h->histtab;
       
  2441     hist_ring = h->hist_ring;
       
  2442     curhist = h->curhist;
       
  2443     histlinect = h->histlinect;
       
  2444     histsiz = h->histsiz;
       
  2445     savehistsiz = h->savehistsiz;
       
  2446 
       
  2447     if (curline_in_ring)
       
  2448 	linkcurline();
       
  2449 
       
  2450     return histsave_stack_pos + 1;
       
  2451 }
       
  2452 
       
  2453 /* If pop_through > 0, pop all array items >= the 1-relative index value.
       
  2454  * If pop_through <= 0, pop (-1)*pop_through levels off the stack.
       
  2455  * If the (new) top of stack is from a higher locallevel, auto-pop until
       
  2456  * it is not.
       
  2457  */
       
  2458 
       
  2459 /**/
       
  2460 int
       
  2461 saveandpophiststack(int pop_through, int writeflags)
       
  2462 {
       
  2463     if (pop_through <= 0) {
       
  2464 	pop_through += histsave_stack_pos + 1;
       
  2465 	if (pop_through <= 0)
       
  2466 	    pop_through = 1;
       
  2467     }
       
  2468     while (pop_through > 1
       
  2469      && histsave_stack[pop_through-2].locallevel > locallevel)
       
  2470 	pop_through--;
       
  2471     if (histsave_stack_pos < pop_through)
       
  2472 	return 0;
       
  2473     do {
       
  2474 	if (!nohistsave)
       
  2475 	    savehistfile(NULL, 1, writeflags);
       
  2476 	pophiststack();
       
  2477     } while (histsave_stack_pos >= pop_through);
       
  2478     return 1;
       
  2479 }