home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Shells / zsh-3.0.5-MIHS / src / Src / hist.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-06-03  |  30.9 KB  |  1,502 lines

  1. /*
  2.  * $Id: hist.c,v 2.27 1996/10/18 01:00:43 hzoli Exp $
  3.  *
  4.  * hist.c - history expansion
  5.  *
  6.  * This file is part of zsh, the Z shell.
  7.  *
  8.  * Copyright (c) 1992-1996 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.h"
  33.  
  34. /*
  35.  * Note on curhist: with history active, this points to the
  36.  * last line actually added to the history list.  With history inactive,
  37.  * the line does not get added to the list until hend(), if at all.
  38.  * However, curhist is incremented to reflect the current line anyway.
  39.  * Thus if the line is not added to the list, curhist must be
  40.  * decremented in hend().
  41.  */
  42.  
  43. /* Bits of histactive variable */
  44. #define HA_ACTIVE    (1<<0)    /* History mechanism is active */
  45. #define HA_NOSTORE    (1<<1)    /* Don't store the line when finished */
  46. #define HA_JUNKED    (1<<2)    /* Last history line was already junked */
  47. #define HA_NOINC    (1<<3)    /* Don't store, curhist not incremented */
  48.  
  49. extern int cs, ll;
  50.  
  51. /* Array of word beginnings and endings in current history line. */
  52. short *chwords;
  53. /* Max, actual position in chwords.
  54.  * nwords = chwordpos/2 because we record beginning and end of words.
  55.  */
  56. int chwordlen, chwordpos;
  57.  
  58. /* add a character to the current history word */
  59.  
  60. /**/
  61. void
  62. hwaddc(int c)
  63. {
  64.     /* Only if history line exists and lexing has not finished. */
  65.     if (chline && !(errflag || lexstop)) {
  66.     /* Quote un-expanded bangs in the history line. */
  67.     if (c == bangchar && stophist < 2 && qbang)
  68.         /* If qbang is not set, we do not escape this bangchar as it's *
  69.          * not mecessary (e.g. it's a bang in !=, or it is followed    *
  70.          * by a space). Roughly speaking, qbang is zero only if the    *
  71.          * history interpreter has already digested this bang and      *
  72.          * found that it is not necessary to escape it.                */
  73.         hwaddc('\\');
  74.     *hptr++ = c;
  75.  
  76.     /* Resize history line if necessary */
  77.     if (hptr - chline >= hlinesz) {
  78.         int oldsiz = hlinesz;
  79.  
  80.         chline = realloc(chline, hlinesz = oldsiz + 16);
  81.         hptr = chline + oldsiz;
  82.     }
  83.     }
  84. }
  85.  
  86. /* This function adds a character to the zle input line. It is used when *
  87.  * zsh expands history (see doexpandhist() in zle_tricky.c). It also     *
  88.  * calculates the new cursor position after the expansion. It is called  *
  89.  * from hgetc() and from gettok() in lex.c for characters in comments.   */
  90.  
  91. /**/
  92. void
  93. addtoline(int c)
  94. {
  95.     if (! expanding || lexstop)
  96.     return;
  97.     if (qbang && c == bangchar && stophist < 2) {
  98.     exlast--;
  99.     spaceinline(1);
  100.     line[cs++] = '\\';
  101.     }
  102.     if (excs > cs) {
  103.     excs += 1 + inbufct - exlast;
  104.     if (excs < cs)
  105.         /* this case could be handled better but it is    *
  106.          * so rare that it does not worth it              */
  107.         excs = cs;
  108.     }
  109.     exlast = inbufct;
  110.     spaceinline(1);
  111.     line[cs++] = itok(c) ? ztokens[c - Pound] : c;
  112. }
  113.  
  114. /**/
  115. int
  116. hgetc(void)
  117. {
  118.     int c = ingetc();
  119.  
  120.     qbang = 0;
  121.     if (!stophist && !(inbufflags & INP_ALIAS)) {
  122.     /* If necessary, expand history characters. */
  123.     c = histsubchar(c);
  124.     if (c < 0) {
  125.         /* bad expansion */
  126.         errflag = lexstop = 1;
  127.         return ' ';
  128.     }
  129.     }
  130.     if ((inbufflags & INP_HIST) && !stophist) {
  131.     /* the current character c came from a history expansion          *
  132.      * (inbufflags && INP_HIST) and history is not disabled           *
  133.      * (e.g. we are not inside single quotes). In that case, \!       *
  134.      * should be treated as ! (since this \! came from a previous     *
  135.      * history line where \ was used to escape the bang). So if       *
  136.      * c == '\\' we fetch one more character to see if it's a bang,   *
  137.      * and if it is not, we unget it and reset c back to '\\'         */
  138.     qbang = 0;
  139.     if (c == '\\' && !(qbang = (c = ingetc()) == bangchar))
  140.         safeinungetc(c), c = '\\';
  141.     } else if (stophist || (inbufflags & INP_ALIAS))
  142.     /* If the result is a bangchar which came from history or alias  *
  143.      * expansion, we treat it as an escaped bangchar, unless history *
  144.      * is disabled. If stophist == 1 it only means that history is   *
  145.      * temporarily disabled by a !" which won't appear in in the     *
  146.      * history, so we still have an escaped bang. stophist > 1 if    *
  147.      * history is disabled with NOBANGHIST or by someone else (e.g.  *
  148.      * when the lexer scans single quoted text).                     */
  149.     qbang = c == bangchar && (stophist < 2);
  150.     hwaddc(c);
  151.     addtoline(c);
  152.  
  153.     return c;
  154. }
  155.  
  156. /**/
  157. void
  158. safeinungetc(int c)
  159. {
  160.     if (lexstop)
  161.     lexstop = 0;
  162.     else
  163.     inungetc(c);
  164. }
  165.  
  166. /* extract :s/foo/bar/ delimiters and arguments */
  167.  
  168. /**/
  169. int
  170. getsubsargs(char *subline)
  171. {
  172.     int del;
  173.     char *ptr1, *ptr2;
  174.  
  175.     del = ingetc();
  176.     ptr1 = hdynread2(del);
  177.     if (!ptr1)
  178.     return 1;
  179.     ptr2 = hdynread2(del);
  180.     if (strlen(ptr1)) {
  181.     zsfree(hsubl);
  182.     hsubl = ptr1;
  183.     }
  184.     zsfree(hsubr);
  185.     hsubr = ptr2;
  186.     if (hsubl && !strstr(subline, hsubl)) {
  187.     zerr("substitution failed", NULL, 0);
  188.     inerrflush();
  189.     return 1;
  190.     }
  191.     return 0;
  192. }
  193.  
  194. /* Get the maximum no. of words for a history entry. */
  195.  
  196. /**/
  197. int
  198. getargc(Histent ehist)
  199. {
  200.     return ehist->nwords ? ehist->nwords-1 : 0;
  201. }
  202.  
  203. /* Perform history substitution, returning the next character afterwards. */
  204.  
  205. /**/
  206. int
  207. histsubchar(int c)
  208. {
  209.     int ev, farg, evset = -1, larg, argc, cflag = 0, bflag = 0;
  210.     static int mev = -1, marg = -1;
  211.     char buf[256], *ptr;
  212.     char *sline;
  213.     Histent ehist;
  214.  
  215.     /* look, no goto's */
  216.     if (isfirstch && c == hatchar) {
  217.     /* Line begins ^foo^bar */
  218.     isfirstch = 0;
  219.     inungetc(hatchar);
  220.     if (!(ehist = gethist(defev))
  221.         || !(sline = getargs(ehist, 0, getargc(ehist)))
  222.         || getsubsargs(sline) || !hsubl)
  223.         return -1;
  224.     subst(&sline, hsubl, hsubr, 0);
  225.     } else {
  226.     /* Line doesn't begin ^foo^bar */
  227.     if (c != ' ')
  228.         isfirstch = 0;
  229.     if (c == '\\') {
  230.         int g = ingetc();
  231.  
  232.         if (g != bangchar)
  233.         safeinungetc(g);
  234.         else {
  235.         qbang = 1;
  236.         return bangchar;
  237.         }
  238.     }
  239.     if (c != bangchar)
  240.         return c;
  241.     *hptr = '\0';
  242.     if ((c = ingetc()) == '{') {
  243.         bflag = cflag = 1;
  244.         c = ingetc();
  245.     }
  246.     if (c == '\"') {
  247.         stophist = 1;
  248.         return ingetc();
  249.     }
  250.     if ((!cflag && inblank(c)) || c == '=' || c == '(' || lexstop) {
  251.         safeinungetc(c);
  252.         return bangchar;
  253.     }
  254.     cflag = 0;
  255.     ptr = buf;
  256.  
  257.     /* get event number */
  258.  
  259.     if (c == '?') {
  260.         for (;;) {
  261.         c = ingetc();
  262.         if (c == '?' || c == '\n' || lexstop)
  263.             break;
  264.         else
  265.             *ptr++ = c;
  266.         }
  267.         if (c != '\n' && !lexstop)
  268.         c = ingetc();
  269.         *ptr = '\0';
  270.         mev = ev = hconsearch(hsubl = ztrdup(buf), &marg);
  271.         evset = 0;
  272.         if (ev == -1) {
  273.         inerrflush();
  274.         zerr("no such event: %s", buf, 0);
  275.         return -1;
  276.         }
  277.     } else {
  278.         int t0;
  279.  
  280.         for (;;) {
  281.         if (inblank(c) || c == ';' || c == ':' || c == '^' ||
  282.             c == '$' || c == '*' || c == '%' || c == '}' ||
  283.             c == '\'' || c == '"' || c == '`' || lexstop)
  284.             break;
  285.         if (ptr != buf) {
  286.             if (c == '-')
  287.             break;
  288.             if ((idigit(buf[0]) || buf[0] == '-') && !idigit(c))
  289.             break;
  290.         }
  291.         *ptr++ = c;
  292.         if (c == '#' || c == bangchar) {
  293.             c = ingetc();
  294.             break;
  295.         }
  296.         c = ingetc();
  297.         }
  298.         *ptr = 0;
  299.         if (!*buf)
  300.         if (c != '%') {
  301.             if (isset(CSHJUNKIEHISTORY))
  302.             ev = curhist - 1;
  303.             else
  304.             ev = defev;
  305.             if (c == ':' && evset == -1)
  306.             evset = 0;
  307.             else
  308.             evset = 1;
  309.         } else {
  310.             if (marg != -1)
  311.             ev = mev;
  312.             else
  313.             ev = defev;
  314.             evset = 0;
  315.         } else if ((t0 = atoi(buf))) {
  316.         ev = (t0 < 0) ? curhist + t0 : t0;
  317.         evset = 1;
  318.         } else if ((unsigned)*buf == bangchar) {
  319.         ev = curhist - 1;
  320.         evset = 1;
  321.         } else if (*buf == '#') {
  322.         ev = curhist;
  323.         evset = 1;
  324.         } else if ((ev = hcomsearch(buf)) == -1) {
  325.         zerr("event not found: %s", buf, 0);
  326.         while (c != '\n' && !lexstop)
  327.             c = ingetc();
  328.         return -1;
  329.         } else
  330.         evset = 1;
  331.     }
  332.  
  333.     /* get the event */
  334.  
  335.     if (!(ehist = gethist(defev = ev)))
  336.         return -1;
  337.  
  338.     /* extract the relevant arguments */
  339.  
  340.     argc = getargc(ehist);
  341.     if (c == ':') {
  342.         cflag = 1;
  343.         c = ingetc();
  344.         if (c == '%' && marg != -1) {
  345.         if (!evset) {
  346.             ehist = gethist(defev = mev);
  347.             argc = getargc(ehist);
  348.         } else {
  349.             zerr("Ambiguous history reference", NULL, 0);
  350.             while (c != '\n' && !lexstop)
  351.             c = ingetc();
  352.             return -1;
  353.         }
  354.  
  355.         }
  356.     }
  357.     if (c == '*') {
  358.         farg = 1;
  359.         larg = argc;
  360.         cflag = 0;
  361.     } else {
  362.         inungetc(c);
  363.         larg = farg = getargspec(argc, marg, evset);
  364.         if (larg == -2)
  365.         return -1;
  366.         if (farg != -1)
  367.         cflag = 0;
  368.         c = ingetc();
  369.         if (c == '*') {
  370.         cflag = 0;
  371.         larg = argc;
  372.         } else if (c == '-') {
  373.         cflag = 0;
  374.         larg = getargspec(argc, marg, evset);
  375.         if (larg == -2)
  376.             return -1;
  377.         if (larg == -1)
  378.             larg = argc - 1;
  379.         } else
  380.         inungetc(c);
  381.     }
  382.     if (farg == -1)
  383.         farg = 0;
  384.     if (larg == -1)
  385.         larg = argc;
  386.     if (!(sline = getargs(ehist, farg, larg)))
  387.         return -1;
  388.     }
  389.  
  390.     /* do the modifiers */
  391.  
  392.     for (;;) {
  393.     c = (cflag) ? ':' : ingetc();
  394.     cflag = 0;
  395.     if (c == ':') {
  396.         int gbal = 0;
  397.  
  398.         if ((c = ingetc()) == 'g') {
  399.         gbal = 1;
  400.         c = ingetc();
  401.         }
  402.         switch (c) {
  403.         case 'p':
  404.         histdone = HISTFLAG_DONE | HISTFLAG_NOEXEC;
  405.         break;
  406.         case 'h':
  407.         if (!remtpath(&sline)) {
  408.             inerrflush();
  409.             zerr("modifier failed: h", NULL, 0);
  410.             return -1;
  411.         }
  412.         break;
  413.         case 'e':
  414.         if (!rembutext(&sline)) {
  415.             inerrflush();
  416.             zerr("modifier failed: e", NULL, 0);
  417.             return -1;
  418.         }
  419.         break;
  420.         case 'r':
  421.         if (!remtext(&sline)) {
  422.             inerrflush();
  423.             zerr("modifier failed: r", NULL, 0);
  424.             return -1;
  425.         }
  426.         break;
  427.         case 't':
  428.         if (!remlpaths(&sline)) {
  429.             inerrflush();
  430.             zerr("modifier failed: t", NULL, 0);
  431.             return -1;
  432.         }
  433.         break;
  434.         case 's':
  435.         if (getsubsargs(sline))
  436.             return -1; /* fall through */
  437.         case '&':
  438.         if (hsubl && hsubr)
  439.             subst(&sline, hsubl, hsubr, gbal);
  440.         else {
  441.             inerrflush();
  442.             zerr("no previous substitution", NULL, 0);
  443.             return -1;
  444.         }
  445.         break;
  446.         case 'q':
  447.         quote(&sline);
  448.         break;
  449.         case 'x':
  450.         quotebreak(&sline);
  451.         break;
  452.         case 'l':
  453.         downcase(&sline);
  454.         break;
  455.         case 'u':
  456.         upcase(&sline);
  457.         break;
  458.         default:
  459.         inerrflush();
  460.         zerr("illegal modifier: %c", NULL, c);
  461.         return -1;
  462.         }
  463.     } else {
  464.         if (c != '}' || !bflag)
  465.         inungetc(c);
  466.         if (c != '}' && bflag) {
  467.         zerr("'}' expected", NULL, 0);
  468.         return -1;
  469.         }
  470.         break;
  471.     }
  472.     }
  473.  
  474.     /*
  475.      * Push the expanded value onto the input stack,
  476.      * marking this as a history word for purposes of the alias stack.
  477.      */
  478.  
  479.     lexstop = 0;
  480.     /* this function is called only called from hgetc and only if      *
  481.      * !(inbufflags & INP_ALIAS). History expansion should never be    *
  482.      * done with INP_ALIAS (to prevent recursive history expansion and *
  483.      * histoty expansion of aliases). Escapes are not removed here.    *
  484.      * This is now handled in hgetc.                                   */
  485.     inpush(sline, INP_HIST, NULL); /* sline from heap, don't free */
  486.     histdone |= HISTFLAG_DONE;
  487.     if (isset(HISTVERIFY))
  488.     histdone |= HISTFLAG_NOEXEC | HISTFLAG_RECALL;
  489.  
  490.     /* Don't try and re-expand line. */
  491.     return ingetc();
  492. }
  493.  
  494. /* unget a char and remove it from chline. It can only be used *
  495.  * to unget a character returned by hgetc.                     */
  496.  
  497. /**/
  498. void
  499. hungetc(int c)
  500. {
  501.     int doit = 1;
  502.  
  503.     while (!lexstop) {
  504.     if (hptr[-1] != (char) c && stophist < 4 &&
  505.         hptr > chline + 1 && hptr[-1] == '\n' && hptr[-2] == '\\')
  506.         hungetc('\n'), hungetc('\\');
  507.  
  508.     if (expanding) {
  509.         cs--;
  510.         ll--;
  511.         exlast++;
  512.     }
  513.     DPUTS(hptr <= chline, "BUG: hungetc attempted at buffer start");
  514.     hptr--;
  515.     DPUTS(*hptr != (char) c, "BUG: wrong character in hungetc() ");
  516.     qbang = (c == bangchar && stophist < 2 &&
  517.          hptr > chline && hptr[-1] == '\\');
  518.     if (doit)
  519.         inungetc(c);
  520.     if (!qbang)
  521.         return;
  522.     doit = !stophist && ((inbufflags & INP_HIST) ||
  523.                  !(inbufflags & INP_ALIAS));
  524.     c = '\\';
  525.     }
  526. }
  527.  
  528. /* begin reading a string */
  529.  
  530. /**/
  531. void
  532. strinbeg(void)
  533. {
  534.     strin++;
  535.     hbegin();
  536.     lexinit();
  537. }
  538.  
  539. /* done reading a string */
  540.  
  541. /**/
  542. void
  543. strinend(void)
  544. {
  545.     hend();
  546.     DPUTS(!strin, "BUG: strinend() called without strinbeg()");
  547.     strin--;
  548.     isfirstch = 1;
  549.     histdone = 0;
  550. }
  551.  
  552. /* initialize the history mechanism */
  553.  
  554. /**/
  555. void
  556. hbegin(void)
  557. {
  558.     Histent curhistent;
  559.  
  560.     isfirstln = isfirstch = 1;
  561.     errflag = histdone = spaceflag = 0;
  562.     stophist = (!interact || unset(BANGHIST) || unset(SHINSTDIN)) << 1;
  563.     chline = hptr = zcalloc(hlinesz = 16);
  564.     chwords = zalloc((chwordlen = 16)*sizeof(short));
  565.     chwordpos = 0;
  566.  
  567.     curhistent = gethistent(curhist);
  568.     if (!curhistent->ftim)
  569.     curhistent->ftim = time(NULL);
  570.     histactive = HA_ACTIVE;
  571.     if (interact && isset(SHINSTDIN) && !strin) {
  572.     attachtty(mypgrp);
  573.     defev = curhist;
  574.     curhist++;
  575.     } else
  576.     histactive |= HA_NOINC;
  577. }
  578.  
  579. /* say we're done using the history mechanism */
  580.  
  581. /**/
  582. int
  583. hend(void)
  584. {
  585.     int flag, save = 1;
  586.     Histent he;
  587.  
  588.     DPUTS(!chline, "BUG: chline is NULL in hend()");
  589.     if (histactive & (HA_NOSTORE|HA_NOINC)) {
  590.     zfree(chline, hlinesz);
  591.     zfree(chwords, chwordlen*sizeof(short));
  592.     chline = NULL;
  593.     if (!(histactive & HA_NOINC))
  594.         curhist--;
  595.     histactive = 0;
  596.     return 1;
  597.     }
  598.     flag = histdone;
  599.     histdone = 0;
  600.     if (hptr < chline + 1)
  601.     save = 0;
  602.     else {
  603.     *hptr = '\0';
  604.     if (hptr[-1] == '\n')
  605.         if (chline[1]) {
  606.         *--hptr = '\0';
  607.         } else
  608.         save = 0;
  609.     he = gethistent(curhist - 1);
  610.     if (!*chline || !strcmp(chline, "\n") ||
  611.         (isset(HISTIGNOREDUPS) && he->text && !strcmp(he->text, chline)) ||
  612.         (isset(HISTIGNORESPACE) && spaceflag))
  613.         save = 0;
  614.     }
  615.     if (flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) {
  616.     char *ptr;
  617.  
  618.     ptr = ztrdup(chline);
  619.     if ((flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) == HISTFLAG_DONE) {
  620.         zputs(ptr, stderr);
  621.         fputc('\n', stderr);
  622.         fflush(stderr);
  623.     }
  624.     if (flag & HISTFLAG_RECALL) {
  625.         PERMALLOC {
  626.         pushnode(bufstack, ptr);
  627.         } LASTALLOC;
  628.         save = 0;
  629.     } else
  630.         zsfree(ptr);
  631.     }
  632.     if (save) {
  633.     Histent curhistent = gethistent(curhist);
  634.     zsfree(curhistent->text);
  635.     if (curhistent->nwords)
  636.         zfree(curhistent->words, curhistent->nwords*2*sizeof(short));
  637.  
  638.     curhistent->text = ztrdup(chline);
  639.     curhistent->stim = time(NULL);
  640.     curhistent->ftim = 0L;
  641.     curhistent->flags = 0;
  642. #ifdef DEBUG
  643.     /* debugging only */
  644.     if (chwordpos%2) {
  645.         hwend();
  646.         DPUTS(1, "internal:  uncompleted line in history");
  647.     }
  648. #endif
  649.     /* get rid of pesky \n which we've already nulled out */
  650.     if (!chline[chwords[chwordpos-2]])
  651.         chwordpos -= 2;
  652.     if ((curhistent->nwords = chwordpos/2)) {
  653.         curhistent->words =
  654.         (short *)zalloc(curhistent->nwords*2*sizeof(short));
  655.         memcpy(curhistent->words, chwords,
  656.            curhistent->nwords*2*sizeof(short));
  657.     }
  658.     } else
  659.     curhist--;
  660.     zfree(chline, hlinesz);
  661.     zfree(chwords, chwordlen*sizeof(short));
  662.     chline = NULL;
  663.     histactive = 0;
  664.     return !(flag & HISTFLAG_NOEXEC || errflag);
  665. }
  666.  
  667. /* remove the current line from the history List */
  668.  
  669. /**/
  670. void
  671. remhist(void)
  672. {
  673.     if (!(histactive & HA_ACTIVE)) {
  674.     if (!(histactive & HA_JUNKED)) {
  675.         /* make sure this doesn't show up when we do firsthist() */
  676.         Histent he = gethistent(curhist);
  677.         zsfree(he->text);
  678.         he->text = NULL;
  679.         histactive |= HA_JUNKED;
  680.         curhist--;
  681.     }
  682.     } else
  683.     histactive |= HA_NOSTORE;
  684. }
  685.  
  686. /* Gives current expansion word if not last word before chwordpos. */
  687. int hwgetword = -1;
  688.  
  689. /* begin a word */
  690.  
  691. /**/
  692. void
  693. hwbegin(int offset)
  694. {
  695.     if (chwordpos%2)
  696.     chwordpos--;    /* make sure we're on a word start, not end */
  697.     /* If we're expanding an alias, we should overwrite the expansion
  698.      * in the history.
  699.      */
  700.     if ((inbufflags & INP_ALIAS) && !(inbufflags & INP_HIST))
  701.     hwgetword = chwordpos;
  702.     else
  703.     hwgetword = -1;
  704.     chwords[chwordpos++] = hptr - chline + offset;
  705. }
  706.  
  707. /* add a word to the history List */
  708.  
  709. /**/
  710. void
  711. hwend(void)
  712. {
  713.     if (chwordpos%2 && chline) {
  714.     /* end of word reached and we've already begun a word */
  715.     if (hptr > chline + chwords[chwordpos-1]) {
  716.         chwords[chwordpos++] = hptr - chline;
  717.         if (chwordpos >= chwordlen) {
  718.         chwords = (short *) realloc(chwords,
  719.                         (chwordlen += 16)*sizeof(short));
  720.         }
  721.         if (hwgetword > -1) {
  722.         /* We want to reuse the current word position */
  723.         chwordpos = hwgetword;
  724.         /* Start from where previous word ended, if possible */
  725.         hptr = chline + chwords[chwordpos ? chwordpos - 1 : 0];
  726.         }
  727.     } else {
  728.         /* scrub that last word, it doesn't exist */
  729.         chwordpos--;
  730.     }
  731.     }
  732. }
  733.  
  734. /* Go back to immediately after the last word, skipping space. */
  735.  
  736. /**/
  737. void
  738. histbackword(void)
  739. {
  740.     if (!(chwordpos%2) && chwordpos)
  741.     hptr = chline + chwords[chwordpos-1];
  742. }
  743.  
  744. /* Get the start and end point of the current history word */
  745.  
  746. /**/
  747. void
  748. hwget(char **startptr)
  749. {
  750.     int pos = hwgetword > -1 ? hwgetword : chwordpos - 2;
  751.  
  752. #ifdef DEBUG
  753.     /* debugging only */
  754.     if (hwgetword == -1 && !chwordpos) {
  755.     /* no words available */
  756.     DPUTS(1, "hwget called with no words.");
  757.     *startptr = "";
  758.     return;
  759.     } 
  760.     else if (hwgetword == -1 && chwordpos%2) {
  761.     DPUTS(1, "hwget called in middle of word.");
  762.     *startptr = "";
  763.     return;
  764.     }
  765. #endif
  766.  
  767.     *startptr = chline + chwords[pos];
  768.     chline[chwords[++pos]] = '\0';
  769. }
  770.  
  771. /* Replace the current history word with rep, if different */
  772.  
  773. /**/
  774. void
  775. hwrep(char *rep)
  776. {
  777.     char *start;
  778.     hwget(&start);
  779.  
  780.     if (!strcmp(rep, start))
  781.     return;
  782.     
  783.     hptr = start;
  784.     chwordpos = (hwgetword > -1) ? hwgetword : chwordpos - 2;
  785.     hwbegin(0);
  786.     qbang = 1;
  787.     while (*rep)
  788.     hwaddc(*rep++);
  789.     hwend();
  790. }
  791.  
  792. /* Get the entire current line, deleting it in the history. */
  793.  
  794. /**/
  795. char *
  796. hgetline(void)
  797. {
  798.     /* Currently only used by pushlineoredit().
  799.      * It's necessary to prevent that from getting too pally with
  800.      * the history code.
  801.      */
  802.     char *ret;
  803.  
  804.     if (!chline || hptr == chline)
  805.     return NULL;
  806.     *hptr = '\0';
  807.     ret = dupstring(chline);
  808.  
  809.     /* reset line */
  810.     hptr = chline;
  811.     chwordpos = 0;
  812.     hwgetword = -1;
  813.  
  814.     return ret;
  815. }
  816.  
  817. /* get an argument specification */
  818.  
  819. /**/
  820. int
  821. getargspec(int argc, int marg, int evset)
  822. {
  823.     int c, ret = -1;
  824.  
  825.     if ((c = ingetc()) == '0')
  826.     return 0;
  827.     if (idigit(c)) {
  828.     ret = 0;
  829.     while (idigit(c)) {
  830.         ret = ret * 10 + c - '0';
  831.         c = ingetc();
  832.     }
  833.     inungetc(c);
  834.     } else if (c == '^')
  835.     ret = 1;
  836.     else if (c == '$')
  837.     ret = argc;
  838.     else if (c == '%') {
  839.     if (evset) {
  840.         inerrflush();
  841.         zerr("Ambiguous history reference", NULL, 0);
  842.         return -2;
  843.     }
  844.     if (marg == -1) {
  845.         inerrflush();
  846.         zerr("%% with no previous word matched", NULL, 0);
  847.         return -2;
  848.     }
  849.     ret = marg;
  850.     } else
  851.     inungetc(c);
  852.     return ret;
  853. }
  854.  
  855. /* do ?foo? search */
  856.  
  857. /**/
  858. int
  859. hconsearch(char *str, int *marg)
  860. {
  861.     int t0, t1 = 0;
  862.     char *s;
  863.     Histent he;
  864.  
  865.     for (t0 = curhist - 1; (he = quietgethist(t0)); t0--)
  866.     if ((s = strstr(he->text, str))) {
  867.         int pos = s - he->text;
  868.         while (t1 < he->nwords && he->words[2*t1] <= pos)
  869.         t1++;
  870.         *marg = t1 - 1;
  871.         return t0;
  872.     }
  873.     return -1;
  874. }
  875.  
  876. /* do !foo search */
  877.  
  878. /**/
  879. int
  880. hcomsearch(char *str)
  881. {
  882.     int t0;
  883.     char *hs;
  884.  
  885.     for (t0 = curhist - 1; (hs = quietgetevent(t0)); t0--)
  886.     if (!strncmp(hs, str, strlen(str)))
  887.         return t0;
  888.     return -1;
  889. }
  890.  
  891. /* various utilities for : modifiers */
  892.  
  893. /**/
  894. int
  895. remtpath(char **junkptr)
  896. {
  897.     char *str = *junkptr, *remcut;
  898.  
  899.     if ((remcut = strrchr(str, '/'))) {
  900.     if (str != remcut)
  901.         *remcut = '\0';
  902.     else
  903.         str[1] = '\0';
  904.     return 1;
  905.     }
  906.     return 0;
  907. }
  908.  
  909. /**/
  910. int
  911. remtext(char **junkptr)
  912. {
  913.     char *str = *junkptr, *remcut;
  914.  
  915.     if ((remcut = strrchr(str, '.')) && remcut != str) {
  916.     *remcut = '\0';
  917.     return 1;
  918.     }
  919.     return 0;
  920. }
  921.  
  922. /**/
  923. int
  924. rembutext(char **junkptr)
  925. {
  926.     char *str = *junkptr, *remcut;
  927.  
  928.     if ((remcut = strrchr(str, '.')) && remcut != str) {
  929.     *junkptr = dupstring(remcut + 1);    /* .xx or xx? */
  930.     return 1;
  931.     }
  932.     return 0;
  933. }
  934.  
  935. /**/
  936. int
  937. remlpaths(char **junkptr)
  938. {
  939.     char *str = *junkptr, *remcut;
  940.  
  941.     if ((remcut = strrchr(str, '/'))) {
  942.     *remcut = '\0';
  943.     *junkptr = dupstring(remcut + 1);
  944.     return 1;
  945.     }
  946.     return 0;
  947. }
  948.  
  949. /**/
  950. int
  951. makeuppercase(char **junkptr)
  952. {
  953.     char *str = *junkptr;
  954.  
  955.     for (; *str; str++)
  956.     *str = tuupper(*str);
  957.     return 1;
  958. }
  959.  
  960. /**/
  961. int
  962. makelowercase(char **junkptr)
  963. {
  964.     char *str = *junkptr;
  965.  
  966.     for (; *str; str++)
  967.     *str = tulower(*str);
  968.     return 1;
  969. }
  970.  
  971. /**/
  972. int
  973. makecapitals(char **junkptr)
  974. {
  975.     char *str = *junkptr;
  976.  
  977.     for (; *str;) {
  978.     for (; *str && !ialnum(*str); str++);
  979.     if (*str)
  980.         *str = tuupper(*str), str++;
  981.     for (; *str && ialnum(*str); str++)
  982.         *str = tulower(*str);
  983.     }
  984.     return 1;
  985. }
  986.  
  987. /**/
  988. void
  989. subst(char **strptr, char *in, char *out, int gbal)
  990. {
  991.     char *str = *strptr, *instr = *strptr, *substcut, *sptr, *oldstr;
  992.     int off, inlen, outlen;
  993.  
  994.     if (!*in)
  995.     in = str, gbal = 0;
  996.     if (!(substcut = (char *)strstr(str, in)))
  997.     return;
  998.     inlen = strlen(in);
  999.     sptr = convamps(out, in, inlen);
  1000.     outlen = strlen(sptr);
  1001.  
  1002.     do {
  1003.     *substcut = '\0';
  1004.     off = substcut - *strptr + outlen;
  1005.     substcut += inlen;
  1006.     *strptr = tricat(oldstr = *strptr, sptr, substcut);
  1007.     if (oldstr != instr)
  1008.         zsfree(oldstr);
  1009.     str = (char *)*strptr + off;
  1010.     } while (gbal && (substcut = (char *)strstr(str, in)));
  1011. }
  1012.  
  1013. /**/
  1014. char *
  1015. convamps(char *out, char *in, int inlen)
  1016. {
  1017.     char *ptr, *ret, *pp;
  1018.     int slen, sdup = 0;
  1019.  
  1020.     for (ptr = out, slen = 0; *ptr; ptr++, slen++)
  1021.     if (*ptr == '\\')
  1022.         ptr++, sdup = 1;
  1023.     else if (*ptr == '&')
  1024.         slen += inlen - 1, sdup = 1;
  1025.     if (!sdup)
  1026.     return out;
  1027.     ret = pp = (char *)alloc(slen + 1);
  1028.     for (ptr = out; *ptr; ptr++)
  1029.     if (*ptr == '\\')
  1030.         *pp++ = *++ptr;
  1031.     else if (*ptr == '&') {
  1032.         strcpy(pp, in);
  1033.         pp += inlen;
  1034.     } else
  1035.         *pp++ = *ptr;
  1036.     *pp = '\0';
  1037.     return ret;
  1038. }
  1039.  
  1040. /**/
  1041. struct histent *
  1042. quietgethist(int ev)
  1043. {
  1044.     static struct histent storehist;
  1045.  
  1046.     if (ev < firsthist() || ev > curhist)
  1047.     return NULL;
  1048.     if (ev == curhist && (histactive & HA_ACTIVE)) {
  1049.     /* The current history line has not been stored.  Build it up
  1050.      * from other variables.
  1051.      */
  1052.     storehist.text = chline;
  1053.     storehist.nwords = chwordpos/2;
  1054.     storehist.words = chwords;
  1055.  
  1056.     return &storehist;
  1057.     } else
  1058.     return gethistent(ev);
  1059. }
  1060.  
  1061. /**/
  1062. char *
  1063. quietgetevent(int ev)
  1064. {
  1065.     Histent ent = quietgethist(ev);
  1066.  
  1067.     return ent ? ent->text : NULL;
  1068. }
  1069.  
  1070. /**/
  1071. Histent
  1072. gethist(int ev)
  1073. {
  1074.     Histent ret;
  1075.  
  1076.     ret = quietgethist(ev);
  1077.     if (!ret) {
  1078.     inerrflush();
  1079.     zerr("no such event: %d", NULL, ev);
  1080.     }
  1081.     return ret;
  1082. }
  1083.  
  1084. /**/
  1085. char *
  1086. getargs(Histent elist, int arg1, int arg2)
  1087. {
  1088.     short *words = elist->words;
  1089.     int pos1, nwords = elist->nwords;
  1090.  
  1091.     if (arg2 < arg1 || arg1 >= nwords || arg2 >= nwords) {
  1092.     /* remember, argN is indexed from 0, nwords is total no. of words */
  1093.     inerrflush();
  1094.     zerr("no such word in event", NULL, 0);
  1095.     return NULL;
  1096.     }
  1097.  
  1098.     pos1 = words[2*arg1];
  1099.     return dupstrpfx(elist->text + pos1, words[2*arg2+1] - pos1);
  1100. }
  1101.  
  1102. /**/
  1103. void
  1104. upcase(char **x)
  1105. {
  1106.     char *pp = *(char **)x;
  1107.  
  1108.     for (; *pp; pp++)
  1109.     *pp = tuupper(*pp);
  1110. }
  1111.  
  1112. /**/
  1113. void
  1114. downcase(char **x)
  1115. {
  1116.     char *pp = *(char **)x;
  1117.  
  1118.     for (; *pp; pp++)
  1119.     *pp = tulower(*pp);
  1120. }
  1121.  
  1122. /**/
  1123. int
  1124. quote(char **tr)
  1125. {
  1126.     char *ptr, *rptr, **str = (char **)tr;
  1127.     int len = 3;
  1128.     int inquotes = 0;
  1129.  
  1130.     for (ptr = *str; *ptr; ptr++, len++)
  1131.     if (*ptr == '\'') {
  1132.         len += 3;
  1133.         if (!inquotes)
  1134.         inquotes = 1;
  1135.         else
  1136.         inquotes = 0;
  1137.     } else if (inblank(*ptr) && !inquotes && ptr[-1] != '\\')
  1138.         len += 2;
  1139.     ptr = *str;
  1140.     *str = rptr = (char *)alloc(len);
  1141.     *rptr++ = '\'';
  1142.     for (; *ptr; ptr++)
  1143.     if (*ptr == '\'') {
  1144.         if (!inquotes)
  1145.         inquotes = 1;
  1146.         else
  1147.         inquotes = 0;
  1148.         *rptr++ = '\'';
  1149.         *rptr++ = '\\';
  1150.         *rptr++ = '\'';
  1151.         *rptr++ = '\'';
  1152.     } else if (inblank(*ptr) && !inquotes && ptr[-1] != '\\') {
  1153.         *rptr++ = '\'';
  1154.         *rptr++ = *ptr;
  1155.         *rptr++ = '\'';
  1156.     } else
  1157.         *rptr++ = *ptr;
  1158.     *rptr++ = '\'';
  1159.     *rptr++ = 0;
  1160.     str[1] = NULL;
  1161.     return 0;
  1162. }
  1163.  
  1164. /**/
  1165. int
  1166. quotebreak(char **tr)
  1167. {
  1168.     char *ptr, *rptr, **str = (char **)tr;
  1169.     int len = 3;
  1170.  
  1171.     for (ptr = *str; *ptr; ptr++, len++)
  1172.     if (*ptr == '\'')
  1173.         len += 3;
  1174.     else if (inblank(*ptr))
  1175.         len += 2;
  1176.     ptr = *str;
  1177.     *str = rptr = (char *)alloc(len);
  1178.     *rptr++ = '\'';
  1179.     for (; *ptr;)
  1180.     if (*ptr == '\'') {
  1181.         *rptr++ = '\'';
  1182.         *rptr++ = '\\';
  1183.         *rptr++ = '\'';
  1184.         *rptr++ = '\'';
  1185.         ptr++;
  1186.     } else if (inblank(*ptr)) {
  1187.         *rptr++ = '\'';
  1188.         *rptr++ = *ptr++;
  1189.         *rptr++ = '\'';
  1190.     } else
  1191.         *rptr++ = *ptr++;
  1192.     *rptr++ = '\'';
  1193.     *rptr++ = '\0';
  1194.     return 0;
  1195. }
  1196.  
  1197. /* read an arbitrary amount of data into a buffer until stop is found */
  1198.  
  1199. /**/
  1200. char *
  1201. hdynread(int stop)
  1202. {
  1203.     int bsiz = 256, ct = 0, c;
  1204.     char *buf = (char *)zalloc(bsiz), *ptr;
  1205.  
  1206.     ptr = buf;
  1207.     while ((c = ingetc()) != stop && c != '\n' && !lexstop) {
  1208.     if (c == '\\')
  1209.         c = ingetc();
  1210.     *ptr++ = c;
  1211.     if (++ct == bsiz) {
  1212.         buf = realloc(buf, bsiz *= 2);
  1213.         ptr = buf + ct;
  1214.     }
  1215.     }
  1216.     *ptr = 0;
  1217.     if (c == '\n') {
  1218.     inungetc('\n');
  1219.     zerr("delimiter expected", NULL, 0);
  1220.     zfree(buf, bsiz);
  1221.     return NULL;
  1222.     }
  1223.     return buf;
  1224. }
  1225.  
  1226. /**/
  1227. char *
  1228. hdynread2(int stop)
  1229. {
  1230.     int bsiz = 256, ct = 0, c;
  1231.     char *buf = (char *)zalloc(bsiz), *ptr;
  1232.  
  1233.     ptr = buf;
  1234.     while ((c = ingetc()) != stop && c != '\n' && !lexstop) {
  1235.     if (c == '\n') {
  1236.         inungetc(c);
  1237.         break;
  1238.     }
  1239.     if (c == '\\')
  1240.         c = ingetc();
  1241.     *ptr++ = c;
  1242.     if (++ct == bsiz) {
  1243.         buf = realloc(buf, bsiz *= 2);
  1244.         ptr = buf + ct;
  1245.     }
  1246.     }
  1247.     *ptr = 0;
  1248.     if (c == '\n')
  1249.     inungetc('\n');
  1250.     return buf;
  1251. }
  1252.  
  1253. /**/
  1254. void
  1255. inithist(void)
  1256. {
  1257.     histentct = histsiz;
  1258.     histentarr = (Histent) zcalloc(histentct * sizeof *histentarr);
  1259. }
  1260.  
  1261. /**/
  1262. void
  1263. resizehistents(void)
  1264. {
  1265.     int newentct, t0, t1, firstlex;
  1266.     Histent newarr;
  1267.  
  1268.     newentct = histsiz;
  1269.     newarr = (Histent) zcalloc(newentct * sizeof *newarr);
  1270.     firstlex = curhist - histsiz + 1;
  1271.     t0 = firsthist();
  1272.     if (t0 < curhist - newentct)
  1273.     t0 = curhist - newentct;
  1274.     t1 = t0 % newentct;
  1275.     for (; t0 <= curhist; t0++) {
  1276.     newarr[t1] = *gethistent(t0);
  1277.     if (t0 < firstlex) {
  1278.         zsfree(newarr[t1].text);
  1279.         newarr[t1].text = NULL;
  1280.     }
  1281.     t1++;
  1282.     if (t1 == newentct)
  1283.         t1 = 0;
  1284.     }
  1285.     free(histentarr);
  1286.     histentarr = newarr;
  1287.     histentct = newentct;
  1288. }
  1289.  
  1290. /**/
  1291. void
  1292. readhistfile(char *s, int err)
  1293. {
  1294.     char *buf;
  1295.     FILE *in;
  1296.     Histent ent;
  1297.     time_t tim = time(NULL);
  1298.     short *wordlist;
  1299.     int nwordpos, nwordlist, bufsiz;
  1300.  
  1301.     if (!s)
  1302.     return;
  1303.     if ((in = fopen(unmeta(s), "r"))) {
  1304.     nwordlist = 16;
  1305.     wordlist = (short *)zalloc(nwordlist*sizeof(short));
  1306.     bufsiz = 1024;
  1307.     buf = zalloc(bufsiz);
  1308.  
  1309.     while (fgets(buf, bufsiz, in)) {
  1310.         int l = strlen(buf);
  1311.         char *pt, *start;
  1312.  
  1313.         while (l) {
  1314.         while (buf[l - 1] != '\n') {
  1315.             buf = zrealloc(buf, 2 * bufsiz);
  1316.             bufsiz = 2 * bufsiz;
  1317.             if (!fgets(buf + l, bufsiz - l, in)) {
  1318.             l++;
  1319.             break;
  1320.             }
  1321.             l = strlen(buf);
  1322.         }
  1323.         buf[l - 1] = '\0';
  1324.         if (l > 1 && buf[l - 2] == '\\') {
  1325.             buf[l - 2] = '\n';
  1326.             fgets(buf + l - 1, bufsiz - (l - 1), in);
  1327.             l = strlen(buf);
  1328.         } else
  1329.             break;
  1330.         }
  1331.  
  1332.         ent = gethistent(++curhist);
  1333.         pt = buf;
  1334.         if (*pt == ':') {
  1335.         pt++;
  1336.         ent->stim = zstrtol(pt, NULL, 0);
  1337.         for (; *pt != ':' && *pt; pt++);
  1338.         if (*pt) {
  1339.             pt++;
  1340.             ent->ftim = zstrtol(pt, NULL, 0);
  1341.             for (; *pt != ';' && *pt; pt++);
  1342.             if (*pt)
  1343.             pt++;
  1344.         } else {
  1345.             ent->ftim = tim;
  1346.         }
  1347.         if (ent->stim == 0)
  1348.             ent->stim = tim;
  1349.         if (ent->ftim == 0)
  1350.             ent->ftim = tim;
  1351.         } else {
  1352.         ent->ftim = ent->stim = tim;
  1353.         }
  1354.  
  1355.         zsfree(ent->text);
  1356.         ent->text = ztrdup(pt);
  1357.         ent->flags = HIST_OLD;
  1358.         if (ent->nwords)
  1359.         zfree(ent->words, ent->nwords*2*sizeof(short));
  1360.  
  1361.         /* Divide up the words.  We don't know how it lexes,
  1362.            so just look for spaces.
  1363.            */
  1364.         nwordpos = 0;
  1365.         start = pt;
  1366.         do {
  1367.         while (*pt == ' ')
  1368.             pt++;
  1369.         if (*pt) {
  1370.             if (nwordpos >= nwordlist)
  1371.             wordlist = (short *) realloc(wordlist,
  1372.                     (nwordlist += 16)*sizeof(short));
  1373.             wordlist[nwordpos++] = pt - start;
  1374.             while (*pt && *pt != ' ')
  1375.             pt++;
  1376.             wordlist[nwordpos++] = pt - start;
  1377.         }
  1378.         } while (*pt);
  1379.  
  1380.         ent->nwords = nwordpos/2;
  1381.         if (ent->nwords) {
  1382.         ent->words = (short *)zalloc(nwordpos*sizeof(short));
  1383.         memcpy(ent->words, wordlist, nwordpos*sizeof(short));
  1384.         } else
  1385.         ent->words = (short *)NULL;
  1386.     }
  1387.     fclose(in);
  1388.  
  1389.     zfree(wordlist, nwordlist*sizeof(short));
  1390.     zfree(buf, bufsiz);
  1391.     } else if (err)
  1392.     zerr("can't read history file", s, 0);
  1393. }
  1394.  
  1395. /**/
  1396. void
  1397. savehistfile(char *s, int err, int app)
  1398. {
  1399.     char *t;
  1400.     FILE *out;
  1401.     int ev;
  1402.     Histent ent;
  1403.     int savehist = getiparam("SAVEHIST");
  1404.  
  1405.     if (!s || !interact || savehist <= 0)
  1406.     return;
  1407.     ev = curhist - savehist + 1;
  1408.     if (ev < firsthist())
  1409.     ev = firsthist();
  1410.     if (app & 1)
  1411.     out = fdopen(open(unmeta(s), O_CREAT | O_WRONLY | O_APPEND, 0600), "a");
  1412.     else
  1413.     out = fdopen(open(unmeta(s), O_CREAT | O_WRONLY | O_TRUNC, 0600), "w");
  1414.     if (out) {
  1415.     for (; ev <= curhist - !!histactive; ev++) {
  1416.         ent = gethistent(ev);
  1417.         if (app & 2) {
  1418.         if (ent->flags & HIST_OLD)
  1419.             continue;
  1420.         ent->flags |= HIST_OLD;
  1421.         }
  1422.         t = ent->text;
  1423.         if (isset(EXTENDEDHISTORY)) {
  1424.         fprintf(out, ": %ld:%ld;",
  1425.             (long)ent->stim,
  1426.             (long)ent->ftim);
  1427.         } else if (*t == ':')
  1428.         fputc('\\', out);
  1429.  
  1430.         for (; *t; t++) {
  1431.         if (*t == '\n')
  1432.             fputc('\\', out);
  1433.         fputc(*t, out);
  1434.         }
  1435.         fputc('\n', out);
  1436.     }
  1437.     fclose(out);
  1438.  
  1439.     if (app & 2 && (out = fopen(unmeta(s), "r"))) {
  1440.         char **store, buf[1024], **ptr;
  1441.         int i, l, histnum = 0;
  1442.  
  1443.         store = (char **)zcalloc((savehist + 1) * sizeof *store);
  1444.         while (fgets(buf, sizeof(buf), out)) {
  1445.         char *t;
  1446.  
  1447.         if (store[i = histnum % savehist])
  1448.             free(store[i]);
  1449.         store[i] = ztrdup(buf);
  1450.         l = strlen(buf);
  1451.         if (l > 1) {
  1452.             t = store[i] + l;
  1453.             while ((t[-1] != '\n' ||
  1454.                 (t[-1] == '\n' && t[-2] == '\\')) &&
  1455.                fgets(buf, sizeof(buf), out)) {
  1456.             l += strlen(buf);
  1457.             store[i] = zrealloc(store[i], l + 1);
  1458.             t = store[i] + l;
  1459.             strcat(store[i], buf);
  1460.             }
  1461.         }
  1462.         histnum++;
  1463.         }
  1464.         fclose(out);
  1465.         if ((out = fdopen(open(unmeta(s), O_WRONLY | O_TRUNC, 0600), "w"))) {
  1466.         if (histnum < savehist)
  1467.             for (i = 0; i < histnum; i++)
  1468.             fprintf(out, "%s", store[i]);
  1469.         else
  1470.             for (i = histnum; i < histnum + savehist; i++)
  1471.             fprintf(out, "%s", store[i % savehist]);
  1472.         fclose(out);
  1473.         }
  1474.         for (ptr = store; *ptr; ptr++)
  1475.         zsfree(*ptr);
  1476.         free(store);
  1477.     }
  1478.     } else if (err)
  1479.     zerr("can't write history file %s", s, 0);
  1480. }
  1481.  
  1482. /**/
  1483. int
  1484. firsthist(void)
  1485. {
  1486.     int ev;
  1487.     Histent ent;
  1488.  
  1489.     ev = curhist - histentct + 1;
  1490.     if (ev < 1)
  1491.     ev = 1;
  1492.     do {
  1493.     ent = gethistent(ev);
  1494.     if (ent->text)
  1495.         break;
  1496.     ev++;
  1497.     }
  1498.     while (ev < curhist);
  1499.     return ev;
  1500. }
  1501.  
  1502.