home *** CD-ROM | disk | FTP | other *** search
- /*
- * $Id: hist.c,v 2.27 1996/10/18 01:00:43 hzoli Exp $
- *
- * hist.c - history expansion
- *
- * This file is part of zsh, the Z shell.
- *
- * Copyright (c) 1992-1996 Paul Falstad
- * All rights reserved.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and to distribute modified versions of this software for any
- * purpose, provided that the above copyright notice and the following
- * two paragraphs appear in all copies of this software.
- *
- * In no event shall Paul Falstad or the Zsh Development Group be liable
- * to any party for direct, indirect, special, incidental, or consequential
- * damages arising out of the use of this software and its documentation,
- * even if Paul Falstad and the Zsh Development Group have been advised of
- * the possibility of such damage.
- *
- * Paul Falstad and the Zsh Development Group specifically disclaim any
- * warranties, including, but not limited to, the implied warranties of
- * merchantability and fitness for a particular purpose. The software
- * provided hereunder is on an "as is" basis, and Paul Falstad and the
- * Zsh Development Group have no obligation to provide maintenance,
- * support, updates, enhancements, or modifications.
- *
- */
-
- #include "zsh.h"
-
- /*
- * Note on curhist: with history active, this points to the
- * last line actually added to the history list. With history inactive,
- * the line does not get added to the list until hend(), if at all.
- * However, curhist is incremented to reflect the current line anyway.
- * Thus if the line is not added to the list, curhist must be
- * decremented in hend().
- */
-
- /* Bits of histactive variable */
- #define HA_ACTIVE (1<<0) /* History mechanism is active */
- #define HA_NOSTORE (1<<1) /* Don't store the line when finished */
- #define HA_JUNKED (1<<2) /* Last history line was already junked */
- #define HA_NOINC (1<<3) /* Don't store, curhist not incremented */
-
- extern int cs, ll;
-
- /* Array of word beginnings and endings in current history line. */
- short *chwords;
- /* Max, actual position in chwords.
- * nwords = chwordpos/2 because we record beginning and end of words.
- */
- int chwordlen, chwordpos;
-
- /* add a character to the current history word */
-
- /**/
- void
- hwaddc(int c)
- {
- /* Only if history line exists and lexing has not finished. */
- if (chline && !(errflag || lexstop)) {
- /* Quote un-expanded bangs in the history line. */
- if (c == bangchar && stophist < 2 && qbang)
- /* If qbang is not set, we do not escape this bangchar as it's *
- * not mecessary (e.g. it's a bang in !=, or it is followed *
- * by a space). Roughly speaking, qbang is zero only if the *
- * history interpreter has already digested this bang and *
- * found that it is not necessary to escape it. */
- hwaddc('\\');
- *hptr++ = c;
-
- /* Resize history line if necessary */
- if (hptr - chline >= hlinesz) {
- int oldsiz = hlinesz;
-
- chline = realloc(chline, hlinesz = oldsiz + 16);
- hptr = chline + oldsiz;
- }
- }
- }
-
- /* This function adds a character to the zle input line. It is used when *
- * zsh expands history (see doexpandhist() in zle_tricky.c). It also *
- * calculates the new cursor position after the expansion. It is called *
- * from hgetc() and from gettok() in lex.c for characters in comments. */
-
- /**/
- void
- addtoline(int c)
- {
- if (! expanding || lexstop)
- return;
- if (qbang && c == bangchar && stophist < 2) {
- exlast--;
- spaceinline(1);
- line[cs++] = '\\';
- }
- if (excs > cs) {
- excs += 1 + inbufct - exlast;
- if (excs < cs)
- /* this case could be handled better but it is *
- * so rare that it does not worth it */
- excs = cs;
- }
- exlast = inbufct;
- spaceinline(1);
- line[cs++] = itok(c) ? ztokens[c - Pound] : c;
- }
-
- /**/
- int
- hgetc(void)
- {
- int c = ingetc();
-
- qbang = 0;
- if (!stophist && !(inbufflags & INP_ALIAS)) {
- /* If necessary, expand history characters. */
- c = histsubchar(c);
- if (c < 0) {
- /* bad expansion */
- errflag = lexstop = 1;
- return ' ';
- }
- }
- if ((inbufflags & INP_HIST) && !stophist) {
- /* the current character c came from a history expansion *
- * (inbufflags && INP_HIST) and history is not disabled *
- * (e.g. we are not inside single quotes). In that case, \! *
- * should be treated as ! (since this \! came from a previous *
- * history line where \ was used to escape the bang). So if *
- * c == '\\' we fetch one more character to see if it's a bang, *
- * and if it is not, we unget it and reset c back to '\\' */
- qbang = 0;
- if (c == '\\' && !(qbang = (c = ingetc()) == bangchar))
- safeinungetc(c), c = '\\';
- } else if (stophist || (inbufflags & INP_ALIAS))
- /* If the result is a bangchar which came from history or alias *
- * expansion, we treat it as an escaped bangchar, unless history *
- * is disabled. If stophist == 1 it only means that history is *
- * temporarily disabled by a !" which won't appear in in the *
- * history, so we still have an escaped bang. stophist > 1 if *
- * history is disabled with NOBANGHIST or by someone else (e.g. *
- * when the lexer scans single quoted text). */
- qbang = c == bangchar && (stophist < 2);
- hwaddc(c);
- addtoline(c);
-
- return c;
- }
-
- /**/
- void
- safeinungetc(int c)
- {
- if (lexstop)
- lexstop = 0;
- else
- inungetc(c);
- }
-
- /* extract :s/foo/bar/ delimiters and arguments */
-
- /**/
- int
- getsubsargs(char *subline)
- {
- int del;
- char *ptr1, *ptr2;
-
- del = ingetc();
- ptr1 = hdynread2(del);
- if (!ptr1)
- return 1;
- ptr2 = hdynread2(del);
- if (strlen(ptr1)) {
- zsfree(hsubl);
- hsubl = ptr1;
- }
- zsfree(hsubr);
- hsubr = ptr2;
- if (hsubl && !strstr(subline, hsubl)) {
- zerr("substitution failed", NULL, 0);
- inerrflush();
- return 1;
- }
- return 0;
- }
-
- /* Get the maximum no. of words for a history entry. */
-
- /**/
- int
- getargc(Histent ehist)
- {
- return ehist->nwords ? ehist->nwords-1 : 0;
- }
-
- /* Perform history substitution, returning the next character afterwards. */
-
- /**/
- int
- histsubchar(int c)
- {
- int ev, farg, evset = -1, larg, argc, cflag = 0, bflag = 0;
- static int mev = -1, marg = -1;
- char buf[256], *ptr;
- char *sline;
- Histent ehist;
-
- /* look, no goto's */
- if (isfirstch && c == hatchar) {
- /* Line begins ^foo^bar */
- isfirstch = 0;
- inungetc(hatchar);
- if (!(ehist = gethist(defev))
- || !(sline = getargs(ehist, 0, getargc(ehist)))
- || getsubsargs(sline) || !hsubl)
- return -1;
- subst(&sline, hsubl, hsubr, 0);
- } else {
- /* Line doesn't begin ^foo^bar */
- if (c != ' ')
- isfirstch = 0;
- if (c == '\\') {
- int g = ingetc();
-
- if (g != bangchar)
- safeinungetc(g);
- else {
- qbang = 1;
- return bangchar;
- }
- }
- if (c != bangchar)
- return c;
- *hptr = '\0';
- if ((c = ingetc()) == '{') {
- bflag = cflag = 1;
- c = ingetc();
- }
- if (c == '\"') {
- stophist = 1;
- return ingetc();
- }
- if ((!cflag && inblank(c)) || c == '=' || c == '(' || lexstop) {
- safeinungetc(c);
- return bangchar;
- }
- cflag = 0;
- ptr = buf;
-
- /* get event number */
-
- if (c == '?') {
- for (;;) {
- c = ingetc();
- if (c == '?' || c == '\n' || lexstop)
- break;
- else
- *ptr++ = c;
- }
- if (c != '\n' && !lexstop)
- c = ingetc();
- *ptr = '\0';
- mev = ev = hconsearch(hsubl = ztrdup(buf), &marg);
- evset = 0;
- if (ev == -1) {
- inerrflush();
- zerr("no such event: %s", buf, 0);
- return -1;
- }
- } else {
- int t0;
-
- for (;;) {
- if (inblank(c) || c == ';' || c == ':' || c == '^' ||
- c == '$' || c == '*' || c == '%' || c == '}' ||
- c == '\'' || c == '"' || c == '`' || lexstop)
- break;
- if (ptr != buf) {
- if (c == '-')
- break;
- if ((idigit(buf[0]) || buf[0] == '-') && !idigit(c))
- break;
- }
- *ptr++ = c;
- if (c == '#' || c == bangchar) {
- c = ingetc();
- break;
- }
- c = ingetc();
- }
- *ptr = 0;
- if (!*buf)
- if (c != '%') {
- if (isset(CSHJUNKIEHISTORY))
- ev = curhist - 1;
- else
- ev = defev;
- if (c == ':' && evset == -1)
- evset = 0;
- else
- evset = 1;
- } else {
- if (marg != -1)
- ev = mev;
- else
- ev = defev;
- evset = 0;
- } else if ((t0 = atoi(buf))) {
- ev = (t0 < 0) ? curhist + t0 : t0;
- evset = 1;
- } else if ((unsigned)*buf == bangchar) {
- ev = curhist - 1;
- evset = 1;
- } else if (*buf == '#') {
- ev = curhist;
- evset = 1;
- } else if ((ev = hcomsearch(buf)) == -1) {
- zerr("event not found: %s", buf, 0);
- while (c != '\n' && !lexstop)
- c = ingetc();
- return -1;
- } else
- evset = 1;
- }
-
- /* get the event */
-
- if (!(ehist = gethist(defev = ev)))
- return -1;
-
- /* extract the relevant arguments */
-
- argc = getargc(ehist);
- if (c == ':') {
- cflag = 1;
- c = ingetc();
- if (c == '%' && marg != -1) {
- if (!evset) {
- ehist = gethist(defev = mev);
- argc = getargc(ehist);
- } else {
- zerr("Ambiguous history reference", NULL, 0);
- while (c != '\n' && !lexstop)
- c = ingetc();
- return -1;
- }
-
- }
- }
- if (c == '*') {
- farg = 1;
- larg = argc;
- cflag = 0;
- } else {
- inungetc(c);
- larg = farg = getargspec(argc, marg, evset);
- if (larg == -2)
- return -1;
- if (farg != -1)
- cflag = 0;
- c = ingetc();
- if (c == '*') {
- cflag = 0;
- larg = argc;
- } else if (c == '-') {
- cflag = 0;
- larg = getargspec(argc, marg, evset);
- if (larg == -2)
- return -1;
- if (larg == -1)
- larg = argc - 1;
- } else
- inungetc(c);
- }
- if (farg == -1)
- farg = 0;
- if (larg == -1)
- larg = argc;
- if (!(sline = getargs(ehist, farg, larg)))
- return -1;
- }
-
- /* do the modifiers */
-
- for (;;) {
- c = (cflag) ? ':' : ingetc();
- cflag = 0;
- if (c == ':') {
- int gbal = 0;
-
- if ((c = ingetc()) == 'g') {
- gbal = 1;
- c = ingetc();
- }
- switch (c) {
- case 'p':
- histdone = HISTFLAG_DONE | HISTFLAG_NOEXEC;
- break;
- case 'h':
- if (!remtpath(&sline)) {
- inerrflush();
- zerr("modifier failed: h", NULL, 0);
- return -1;
- }
- break;
- case 'e':
- if (!rembutext(&sline)) {
- inerrflush();
- zerr("modifier failed: e", NULL, 0);
- return -1;
- }
- break;
- case 'r':
- if (!remtext(&sline)) {
- inerrflush();
- zerr("modifier failed: r", NULL, 0);
- return -1;
- }
- break;
- case 't':
- if (!remlpaths(&sline)) {
- inerrflush();
- zerr("modifier failed: t", NULL, 0);
- return -1;
- }
- break;
- case 's':
- if (getsubsargs(sline))
- return -1; /* fall through */
- case '&':
- if (hsubl && hsubr)
- subst(&sline, hsubl, hsubr, gbal);
- else {
- inerrflush();
- zerr("no previous substitution", NULL, 0);
- return -1;
- }
- break;
- case 'q':
- quote(&sline);
- break;
- case 'x':
- quotebreak(&sline);
- break;
- case 'l':
- downcase(&sline);
- break;
- case 'u':
- upcase(&sline);
- break;
- default:
- inerrflush();
- zerr("illegal modifier: %c", NULL, c);
- return -1;
- }
- } else {
- if (c != '}' || !bflag)
- inungetc(c);
- if (c != '}' && bflag) {
- zerr("'}' expected", NULL, 0);
- return -1;
- }
- break;
- }
- }
-
- /*
- * Push the expanded value onto the input stack,
- * marking this as a history word for purposes of the alias stack.
- */
-
- lexstop = 0;
- /* this function is called only called from hgetc and only if *
- * !(inbufflags & INP_ALIAS). History expansion should never be *
- * done with INP_ALIAS (to prevent recursive history expansion and *
- * histoty expansion of aliases). Escapes are not removed here. *
- * This is now handled in hgetc. */
- inpush(sline, INP_HIST, NULL); /* sline from heap, don't free */
- histdone |= HISTFLAG_DONE;
- if (isset(HISTVERIFY))
- histdone |= HISTFLAG_NOEXEC | HISTFLAG_RECALL;
-
- /* Don't try and re-expand line. */
- return ingetc();
- }
-
- /* unget a char and remove it from chline. It can only be used *
- * to unget a character returned by hgetc. */
-
- /**/
- void
- hungetc(int c)
- {
- int doit = 1;
-
- while (!lexstop) {
- if (hptr[-1] != (char) c && stophist < 4 &&
- hptr > chline + 1 && hptr[-1] == '\n' && hptr[-2] == '\\')
- hungetc('\n'), hungetc('\\');
-
- if (expanding) {
- cs--;
- ll--;
- exlast++;
- }
- DPUTS(hptr <= chline, "BUG: hungetc attempted at buffer start");
- hptr--;
- DPUTS(*hptr != (char) c, "BUG: wrong character in hungetc() ");
- qbang = (c == bangchar && stophist < 2 &&
- hptr > chline && hptr[-1] == '\\');
- if (doit)
- inungetc(c);
- if (!qbang)
- return;
- doit = !stophist && ((inbufflags & INP_HIST) ||
- !(inbufflags & INP_ALIAS));
- c = '\\';
- }
- }
-
- /* begin reading a string */
-
- /**/
- void
- strinbeg(void)
- {
- strin++;
- hbegin();
- lexinit();
- }
-
- /* done reading a string */
-
- /**/
- void
- strinend(void)
- {
- hend();
- DPUTS(!strin, "BUG: strinend() called without strinbeg()");
- strin--;
- isfirstch = 1;
- histdone = 0;
- }
-
- /* initialize the history mechanism */
-
- /**/
- void
- hbegin(void)
- {
- Histent curhistent;
-
- isfirstln = isfirstch = 1;
- errflag = histdone = spaceflag = 0;
- stophist = (!interact || unset(BANGHIST) || unset(SHINSTDIN)) << 1;
- chline = hptr = zcalloc(hlinesz = 16);
- chwords = zalloc((chwordlen = 16)*sizeof(short));
- chwordpos = 0;
-
- curhistent = gethistent(curhist);
- if (!curhistent->ftim)
- curhistent->ftim = time(NULL);
- histactive = HA_ACTIVE;
- if (interact && isset(SHINSTDIN) && !strin) {
- attachtty(mypgrp);
- defev = curhist;
- curhist++;
- } else
- histactive |= HA_NOINC;
- }
-
- /* say we're done using the history mechanism */
-
- /**/
- int
- hend(void)
- {
- int flag, save = 1;
- Histent he;
-
- DPUTS(!chline, "BUG: chline is NULL in hend()");
- if (histactive & (HA_NOSTORE|HA_NOINC)) {
- zfree(chline, hlinesz);
- zfree(chwords, chwordlen*sizeof(short));
- chline = NULL;
- if (!(histactive & HA_NOINC))
- curhist--;
- histactive = 0;
- return 1;
- }
- flag = histdone;
- histdone = 0;
- if (hptr < chline + 1)
- save = 0;
- else {
- *hptr = '\0';
- if (hptr[-1] == '\n')
- if (chline[1]) {
- *--hptr = '\0';
- } else
- save = 0;
- he = gethistent(curhist - 1);
- if (!*chline || !strcmp(chline, "\n") ||
- (isset(HISTIGNOREDUPS) && he->text && !strcmp(he->text, chline)) ||
- (isset(HISTIGNORESPACE) && spaceflag))
- save = 0;
- }
- if (flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) {
- char *ptr;
-
- ptr = ztrdup(chline);
- if ((flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) == HISTFLAG_DONE) {
- zputs(ptr, stderr);
- fputc('\n', stderr);
- fflush(stderr);
- }
- if (flag & HISTFLAG_RECALL) {
- PERMALLOC {
- pushnode(bufstack, ptr);
- } LASTALLOC;
- save = 0;
- } else
- zsfree(ptr);
- }
- if (save) {
- Histent curhistent = gethistent(curhist);
- zsfree(curhistent->text);
- if (curhistent->nwords)
- zfree(curhistent->words, curhistent->nwords*2*sizeof(short));
-
- curhistent->text = ztrdup(chline);
- curhistent->stim = time(NULL);
- curhistent->ftim = 0L;
- curhistent->flags = 0;
- #ifdef DEBUG
- /* debugging only */
- if (chwordpos%2) {
- hwend();
- DPUTS(1, "internal: uncompleted line in history");
- }
- #endif
- /* get rid of pesky \n which we've already nulled out */
- if (!chline[chwords[chwordpos-2]])
- chwordpos -= 2;
- if ((curhistent->nwords = chwordpos/2)) {
- curhistent->words =
- (short *)zalloc(curhistent->nwords*2*sizeof(short));
- memcpy(curhistent->words, chwords,
- curhistent->nwords*2*sizeof(short));
- }
- } else
- curhist--;
- zfree(chline, hlinesz);
- zfree(chwords, chwordlen*sizeof(short));
- chline = NULL;
- histactive = 0;
- return !(flag & HISTFLAG_NOEXEC || errflag);
- }
-
- /* remove the current line from the history List */
-
- /**/
- void
- remhist(void)
- {
- if (!(histactive & HA_ACTIVE)) {
- if (!(histactive & HA_JUNKED)) {
- /* make sure this doesn't show up when we do firsthist() */
- Histent he = gethistent(curhist);
- zsfree(he->text);
- he->text = NULL;
- histactive |= HA_JUNKED;
- curhist--;
- }
- } else
- histactive |= HA_NOSTORE;
- }
-
- /* Gives current expansion word if not last word before chwordpos. */
- int hwgetword = -1;
-
- /* begin a word */
-
- /**/
- void
- hwbegin(int offset)
- {
- if (chwordpos%2)
- chwordpos--; /* make sure we're on a word start, not end */
- /* If we're expanding an alias, we should overwrite the expansion
- * in the history.
- */
- if ((inbufflags & INP_ALIAS) && !(inbufflags & INP_HIST))
- hwgetword = chwordpos;
- else
- hwgetword = -1;
- chwords[chwordpos++] = hptr - chline + offset;
- }
-
- /* add a word to the history List */
-
- /**/
- void
- hwend(void)
- {
- if (chwordpos%2 && chline) {
- /* end of word reached and we've already begun a word */
- if (hptr > chline + chwords[chwordpos-1]) {
- chwords[chwordpos++] = hptr - chline;
- if (chwordpos >= chwordlen) {
- chwords = (short *) realloc(chwords,
- (chwordlen += 16)*sizeof(short));
- }
- if (hwgetword > -1) {
- /* We want to reuse the current word position */
- chwordpos = hwgetword;
- /* Start from where previous word ended, if possible */
- hptr = chline + chwords[chwordpos ? chwordpos - 1 : 0];
- }
- } else {
- /* scrub that last word, it doesn't exist */
- chwordpos--;
- }
- }
- }
-
- /* Go back to immediately after the last word, skipping space. */
-
- /**/
- void
- histbackword(void)
- {
- if (!(chwordpos%2) && chwordpos)
- hptr = chline + chwords[chwordpos-1];
- }
-
- /* Get the start and end point of the current history word */
-
- /**/
- void
- hwget(char **startptr)
- {
- int pos = hwgetword > -1 ? hwgetword : chwordpos - 2;
-
- #ifdef DEBUG
- /* debugging only */
- if (hwgetword == -1 && !chwordpos) {
- /* no words available */
- DPUTS(1, "hwget called with no words.");
- *startptr = "";
- return;
- }
- else if (hwgetword == -1 && chwordpos%2) {
- DPUTS(1, "hwget called in middle of word.");
- *startptr = "";
- return;
- }
- #endif
-
- *startptr = chline + chwords[pos];
- chline[chwords[++pos]] = '\0';
- }
-
- /* Replace the current history word with rep, if different */
-
- /**/
- void
- hwrep(char *rep)
- {
- char *start;
- hwget(&start);
-
- if (!strcmp(rep, start))
- return;
-
- hptr = start;
- chwordpos = (hwgetword > -1) ? hwgetword : chwordpos - 2;
- hwbegin(0);
- qbang = 1;
- while (*rep)
- hwaddc(*rep++);
- hwend();
- }
-
- /* Get the entire current line, deleting it in the history. */
-
- /**/
- char *
- hgetline(void)
- {
- /* Currently only used by pushlineoredit().
- * It's necessary to prevent that from getting too pally with
- * the history code.
- */
- char *ret;
-
- if (!chline || hptr == chline)
- return NULL;
- *hptr = '\0';
- ret = dupstring(chline);
-
- /* reset line */
- hptr = chline;
- chwordpos = 0;
- hwgetword = -1;
-
- return ret;
- }
-
- /* get an argument specification */
-
- /**/
- int
- getargspec(int argc, int marg, int evset)
- {
- int c, ret = -1;
-
- if ((c = ingetc()) == '0')
- return 0;
- if (idigit(c)) {
- ret = 0;
- while (idigit(c)) {
- ret = ret * 10 + c - '0';
- c = ingetc();
- }
- inungetc(c);
- } else if (c == '^')
- ret = 1;
- else if (c == '$')
- ret = argc;
- else if (c == '%') {
- if (evset) {
- inerrflush();
- zerr("Ambiguous history reference", NULL, 0);
- return -2;
- }
- if (marg == -1) {
- inerrflush();
- zerr("%% with no previous word matched", NULL, 0);
- return -2;
- }
- ret = marg;
- } else
- inungetc(c);
- return ret;
- }
-
- /* do ?foo? search */
-
- /**/
- int
- hconsearch(char *str, int *marg)
- {
- int t0, t1 = 0;
- char *s;
- Histent he;
-
- for (t0 = curhist - 1; (he = quietgethist(t0)); t0--)
- if ((s = strstr(he->text, str))) {
- int pos = s - he->text;
- while (t1 < he->nwords && he->words[2*t1] <= pos)
- t1++;
- *marg = t1 - 1;
- return t0;
- }
- return -1;
- }
-
- /* do !foo search */
-
- /**/
- int
- hcomsearch(char *str)
- {
- int t0;
- char *hs;
-
- for (t0 = curhist - 1; (hs = quietgetevent(t0)); t0--)
- if (!strncmp(hs, str, strlen(str)))
- return t0;
- return -1;
- }
-
- /* various utilities for : modifiers */
-
- /**/
- int
- remtpath(char **junkptr)
- {
- char *str = *junkptr, *remcut;
-
- if ((remcut = strrchr(str, '/'))) {
- if (str != remcut)
- *remcut = '\0';
- else
- str[1] = '\0';
- return 1;
- }
- return 0;
- }
-
- /**/
- int
- remtext(char **junkptr)
- {
- char *str = *junkptr, *remcut;
-
- if ((remcut = strrchr(str, '.')) && remcut != str) {
- *remcut = '\0';
- return 1;
- }
- return 0;
- }
-
- /**/
- int
- rembutext(char **junkptr)
- {
- char *str = *junkptr, *remcut;
-
- if ((remcut = strrchr(str, '.')) && remcut != str) {
- *junkptr = dupstring(remcut + 1); /* .xx or xx? */
- return 1;
- }
- return 0;
- }
-
- /**/
- int
- remlpaths(char **junkptr)
- {
- char *str = *junkptr, *remcut;
-
- if ((remcut = strrchr(str, '/'))) {
- *remcut = '\0';
- *junkptr = dupstring(remcut + 1);
- return 1;
- }
- return 0;
- }
-
- /**/
- int
- makeuppercase(char **junkptr)
- {
- char *str = *junkptr;
-
- for (; *str; str++)
- *str = tuupper(*str);
- return 1;
- }
-
- /**/
- int
- makelowercase(char **junkptr)
- {
- char *str = *junkptr;
-
- for (; *str; str++)
- *str = tulower(*str);
- return 1;
- }
-
- /**/
- int
- makecapitals(char **junkptr)
- {
- char *str = *junkptr;
-
- for (; *str;) {
- for (; *str && !ialnum(*str); str++);
- if (*str)
- *str = tuupper(*str), str++;
- for (; *str && ialnum(*str); str++)
- *str = tulower(*str);
- }
- return 1;
- }
-
- /**/
- void
- subst(char **strptr, char *in, char *out, int gbal)
- {
- char *str = *strptr, *instr = *strptr, *substcut, *sptr, *oldstr;
- int off, inlen, outlen;
-
- if (!*in)
- in = str, gbal = 0;
- if (!(substcut = (char *)strstr(str, in)))
- return;
- inlen = strlen(in);
- sptr = convamps(out, in, inlen);
- outlen = strlen(sptr);
-
- do {
- *substcut = '\0';
- off = substcut - *strptr + outlen;
- substcut += inlen;
- *strptr = tricat(oldstr = *strptr, sptr, substcut);
- if (oldstr != instr)
- zsfree(oldstr);
- str = (char *)*strptr + off;
- } while (gbal && (substcut = (char *)strstr(str, in)));
- }
-
- /**/
- char *
- convamps(char *out, char *in, int inlen)
- {
- char *ptr, *ret, *pp;
- int slen, sdup = 0;
-
- for (ptr = out, slen = 0; *ptr; ptr++, slen++)
- if (*ptr == '\\')
- ptr++, sdup = 1;
- else if (*ptr == '&')
- slen += inlen - 1, sdup = 1;
- if (!sdup)
- return out;
- ret = pp = (char *)alloc(slen + 1);
- for (ptr = out; *ptr; ptr++)
- if (*ptr == '\\')
- *pp++ = *++ptr;
- else if (*ptr == '&') {
- strcpy(pp, in);
- pp += inlen;
- } else
- *pp++ = *ptr;
- *pp = '\0';
- return ret;
- }
-
- /**/
- struct histent *
- quietgethist(int ev)
- {
- static struct histent storehist;
-
- if (ev < firsthist() || ev > curhist)
- return NULL;
- if (ev == curhist && (histactive & HA_ACTIVE)) {
- /* The current history line has not been stored. Build it up
- * from other variables.
- */
- storehist.text = chline;
- storehist.nwords = chwordpos/2;
- storehist.words = chwords;
-
- return &storehist;
- } else
- return gethistent(ev);
- }
-
- /**/
- char *
- quietgetevent(int ev)
- {
- Histent ent = quietgethist(ev);
-
- return ent ? ent->text : NULL;
- }
-
- /**/
- Histent
- gethist(int ev)
- {
- Histent ret;
-
- ret = quietgethist(ev);
- if (!ret) {
- inerrflush();
- zerr("no such event: %d", NULL, ev);
- }
- return ret;
- }
-
- /**/
- char *
- getargs(Histent elist, int arg1, int arg2)
- {
- short *words = elist->words;
- int pos1, nwords = elist->nwords;
-
- if (arg2 < arg1 || arg1 >= nwords || arg2 >= nwords) {
- /* remember, argN is indexed from 0, nwords is total no. of words */
- inerrflush();
- zerr("no such word in event", NULL, 0);
- return NULL;
- }
-
- pos1 = words[2*arg1];
- return dupstrpfx(elist->text + pos1, words[2*arg2+1] - pos1);
- }
-
- /**/
- void
- upcase(char **x)
- {
- char *pp = *(char **)x;
-
- for (; *pp; pp++)
- *pp = tuupper(*pp);
- }
-
- /**/
- void
- downcase(char **x)
- {
- char *pp = *(char **)x;
-
- for (; *pp; pp++)
- *pp = tulower(*pp);
- }
-
- /**/
- int
- quote(char **tr)
- {
- char *ptr, *rptr, **str = (char **)tr;
- int len = 3;
- int inquotes = 0;
-
- for (ptr = *str; *ptr; ptr++, len++)
- if (*ptr == '\'') {
- len += 3;
- if (!inquotes)
- inquotes = 1;
- else
- inquotes = 0;
- } else if (inblank(*ptr) && !inquotes && ptr[-1] != '\\')
- len += 2;
- ptr = *str;
- *str = rptr = (char *)alloc(len);
- *rptr++ = '\'';
- for (; *ptr; ptr++)
- if (*ptr == '\'') {
- if (!inquotes)
- inquotes = 1;
- else
- inquotes = 0;
- *rptr++ = '\'';
- *rptr++ = '\\';
- *rptr++ = '\'';
- *rptr++ = '\'';
- } else if (inblank(*ptr) && !inquotes && ptr[-1] != '\\') {
- *rptr++ = '\'';
- *rptr++ = *ptr;
- *rptr++ = '\'';
- } else
- *rptr++ = *ptr;
- *rptr++ = '\'';
- *rptr++ = 0;
- str[1] = NULL;
- return 0;
- }
-
- /**/
- int
- quotebreak(char **tr)
- {
- char *ptr, *rptr, **str = (char **)tr;
- int len = 3;
-
- for (ptr = *str; *ptr; ptr++, len++)
- if (*ptr == '\'')
- len += 3;
- else if (inblank(*ptr))
- len += 2;
- ptr = *str;
- *str = rptr = (char *)alloc(len);
- *rptr++ = '\'';
- for (; *ptr;)
- if (*ptr == '\'') {
- *rptr++ = '\'';
- *rptr++ = '\\';
- *rptr++ = '\'';
- *rptr++ = '\'';
- ptr++;
- } else if (inblank(*ptr)) {
- *rptr++ = '\'';
- *rptr++ = *ptr++;
- *rptr++ = '\'';
- } else
- *rptr++ = *ptr++;
- *rptr++ = '\'';
- *rptr++ = '\0';
- return 0;
- }
-
- /* read an arbitrary amount of data into a buffer until stop is found */
-
- /**/
- char *
- hdynread(int stop)
- {
- int bsiz = 256, ct = 0, c;
- char *buf = (char *)zalloc(bsiz), *ptr;
-
- ptr = buf;
- while ((c = ingetc()) != stop && c != '\n' && !lexstop) {
- if (c == '\\')
- c = ingetc();
- *ptr++ = c;
- if (++ct == bsiz) {
- buf = realloc(buf, bsiz *= 2);
- ptr = buf + ct;
- }
- }
- *ptr = 0;
- if (c == '\n') {
- inungetc('\n');
- zerr("delimiter expected", NULL, 0);
- zfree(buf, bsiz);
- return NULL;
- }
- return buf;
- }
-
- /**/
- char *
- hdynread2(int stop)
- {
- int bsiz = 256, ct = 0, c;
- char *buf = (char *)zalloc(bsiz), *ptr;
-
- ptr = buf;
- while ((c = ingetc()) != stop && c != '\n' && !lexstop) {
- if (c == '\n') {
- inungetc(c);
- break;
- }
- if (c == '\\')
- c = ingetc();
- *ptr++ = c;
- if (++ct == bsiz) {
- buf = realloc(buf, bsiz *= 2);
- ptr = buf + ct;
- }
- }
- *ptr = 0;
- if (c == '\n')
- inungetc('\n');
- return buf;
- }
-
- /**/
- void
- inithist(void)
- {
- histentct = histsiz;
- histentarr = (Histent) zcalloc(histentct * sizeof *histentarr);
- }
-
- /**/
- void
- resizehistents(void)
- {
- int newentct, t0, t1, firstlex;
- Histent newarr;
-
- newentct = histsiz;
- newarr = (Histent) zcalloc(newentct * sizeof *newarr);
- firstlex = curhist - histsiz + 1;
- t0 = firsthist();
- if (t0 < curhist - newentct)
- t0 = curhist - newentct;
- t1 = t0 % newentct;
- for (; t0 <= curhist; t0++) {
- newarr[t1] = *gethistent(t0);
- if (t0 < firstlex) {
- zsfree(newarr[t1].text);
- newarr[t1].text = NULL;
- }
- t1++;
- if (t1 == newentct)
- t1 = 0;
- }
- free(histentarr);
- histentarr = newarr;
- histentct = newentct;
- }
-
- /**/
- void
- readhistfile(char *s, int err)
- {
- char *buf;
- FILE *in;
- Histent ent;
- time_t tim = time(NULL);
- short *wordlist;
- int nwordpos, nwordlist, bufsiz;
-
- if (!s)
- return;
- if ((in = fopen(unmeta(s), "r"))) {
- nwordlist = 16;
- wordlist = (short *)zalloc(nwordlist*sizeof(short));
- bufsiz = 1024;
- buf = zalloc(bufsiz);
-
- while (fgets(buf, bufsiz, in)) {
- int l = strlen(buf);
- char *pt, *start;
-
- while (l) {
- while (buf[l - 1] != '\n') {
- buf = zrealloc(buf, 2 * bufsiz);
- bufsiz = 2 * bufsiz;
- if (!fgets(buf + l, bufsiz - l, in)) {
- l++;
- break;
- }
- l = strlen(buf);
- }
- buf[l - 1] = '\0';
- if (l > 1 && buf[l - 2] == '\\') {
- buf[l - 2] = '\n';
- fgets(buf + l - 1, bufsiz - (l - 1), in);
- l = strlen(buf);
- } else
- break;
- }
-
- ent = gethistent(++curhist);
- pt = buf;
- if (*pt == ':') {
- pt++;
- ent->stim = zstrtol(pt, NULL, 0);
- for (; *pt != ':' && *pt; pt++);
- if (*pt) {
- pt++;
- ent->ftim = zstrtol(pt, NULL, 0);
- for (; *pt != ';' && *pt; pt++);
- if (*pt)
- pt++;
- } else {
- ent->ftim = tim;
- }
- if (ent->stim == 0)
- ent->stim = tim;
- if (ent->ftim == 0)
- ent->ftim = tim;
- } else {
- ent->ftim = ent->stim = tim;
- }
-
- zsfree(ent->text);
- ent->text = ztrdup(pt);
- ent->flags = HIST_OLD;
- if (ent->nwords)
- zfree(ent->words, ent->nwords*2*sizeof(short));
-
- /* Divide up the words. We don't know how it lexes,
- so just look for spaces.
- */
- nwordpos = 0;
- start = pt;
- do {
- while (*pt == ' ')
- pt++;
- if (*pt) {
- if (nwordpos >= nwordlist)
- wordlist = (short *) realloc(wordlist,
- (nwordlist += 16)*sizeof(short));
- wordlist[nwordpos++] = pt - start;
- while (*pt && *pt != ' ')
- pt++;
- wordlist[nwordpos++] = pt - start;
- }
- } while (*pt);
-
- ent->nwords = nwordpos/2;
- if (ent->nwords) {
- ent->words = (short *)zalloc(nwordpos*sizeof(short));
- memcpy(ent->words, wordlist, nwordpos*sizeof(short));
- } else
- ent->words = (short *)NULL;
- }
- fclose(in);
-
- zfree(wordlist, nwordlist*sizeof(short));
- zfree(buf, bufsiz);
- } else if (err)
- zerr("can't read history file", s, 0);
- }
-
- /**/
- void
- savehistfile(char *s, int err, int app)
- {
- char *t;
- FILE *out;
- int ev;
- Histent ent;
- int savehist = getiparam("SAVEHIST");
-
- if (!s || !interact || savehist <= 0)
- return;
- ev = curhist - savehist + 1;
- if (ev < firsthist())
- ev = firsthist();
- if (app & 1)
- out = fdopen(open(unmeta(s), O_CREAT | O_WRONLY | O_APPEND, 0600), "a");
- else
- out = fdopen(open(unmeta(s), O_CREAT | O_WRONLY | O_TRUNC, 0600), "w");
- if (out) {
- for (; ev <= curhist - !!histactive; ev++) {
- ent = gethistent(ev);
- if (app & 2) {
- if (ent->flags & HIST_OLD)
- continue;
- ent->flags |= HIST_OLD;
- }
- t = ent->text;
- if (isset(EXTENDEDHISTORY)) {
- fprintf(out, ": %ld:%ld;",
- (long)ent->stim,
- (long)ent->ftim);
- } else if (*t == ':')
- fputc('\\', out);
-
- for (; *t; t++) {
- if (*t == '\n')
- fputc('\\', out);
- fputc(*t, out);
- }
- fputc('\n', out);
- }
- fclose(out);
-
- if (app & 2 && (out = fopen(unmeta(s), "r"))) {
- char **store, buf[1024], **ptr;
- int i, l, histnum = 0;
-
- store = (char **)zcalloc((savehist + 1) * sizeof *store);
- while (fgets(buf, sizeof(buf), out)) {
- char *t;
-
- if (store[i = histnum % savehist])
- free(store[i]);
- store[i] = ztrdup(buf);
- l = strlen(buf);
- if (l > 1) {
- t = store[i] + l;
- while ((t[-1] != '\n' ||
- (t[-1] == '\n' && t[-2] == '\\')) &&
- fgets(buf, sizeof(buf), out)) {
- l += strlen(buf);
- store[i] = zrealloc(store[i], l + 1);
- t = store[i] + l;
- strcat(store[i], buf);
- }
- }
- histnum++;
- }
- fclose(out);
- if ((out = fdopen(open(unmeta(s), O_WRONLY | O_TRUNC, 0600), "w"))) {
- if (histnum < savehist)
- for (i = 0; i < histnum; i++)
- fprintf(out, "%s", store[i]);
- else
- for (i = histnum; i < histnum + savehist; i++)
- fprintf(out, "%s", store[i % savehist]);
- fclose(out);
- }
- for (ptr = store; *ptr; ptr++)
- zsfree(*ptr);
- free(store);
- }
- } else if (err)
- zerr("can't write history file %s", s, 0);
- }
-
- /**/
- int
- firsthist(void)
- {
- int ev;
- Histent ent;
-
- ev = curhist - histentct + 1;
- if (ev < 1)
- ev = 1;
- do {
- ent = gethistent(ev);
- if (ent->text)
- break;
- ev++;
- }
- while (ev < curhist);
- return ev;
- }
-
-