hexpand.c revision 3e14f97f673e8a630f076077de35afdd43dc1587
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie/***********************************************************************
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie* This software is part of the ast package *
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie* Copyright (c) 1982-2010 AT&T Intellectual Property *
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie* and is licensed under the *
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie* Common Public License, Version 1.0 *
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie* by AT&T Intellectual Property *
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie* A copy of the License is available at *
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie* Information and Software Systems Research *
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie* AT&T Research *
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie* Florham Park NJ *
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie* David Korn <dgk@research.att.com> *
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie***********************************************************************/
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie * bash style history expansion
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie * Karsten Fleischer
a574db851cdc636fc3939b68e80d79fe7fbd57f2raf * Omnium Software Engineering
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie * An der Luisenburg 7
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie * D-51379 Leverkusen
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie * <K.Fleischer@omnium.de>
10a4fa49f51ed9ae1c857a626de6ce9ebf41661ariestatic int mod_flags[] = { 0, 0, 0, 0, HIST_PRINT, HIST_QUOTE, HIST_QUOTE|HIST_QUOTE_BR, 0, 0 };
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie#define DONE() {flag |= HIST_ERROR; cp = 0; stakseek(0); goto done;}
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie * parse an /old/new/ string, delimiter expected as first char.
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie * if "old" not specified, keep sb->str[0]
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie * if "new" not specified, set sb->str[1] to empty string
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie * read up to third delimeter char, \n or \0, whichever comes first.
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie * return adress is one past the last valid char in s:
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie * - the address containing \n or \0 or
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie * - one char beyond the third delimiter
10a4fa49f51ed9ae1c857a626de6ce9ebf41661ariestatic char *parse_subst(const char *s, struct subst *sb)
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie int off,n = 0;
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* build the strings on the stack, mainly for '&' substition in "new" */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* init "new" with empty string */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* get delimiter */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie while(n < 2)
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* delimiter or EOL */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* dupe string on stack and rewind stack */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* if not delimiter, we've reached EOL. Get outta here. */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie { /* quote '&' only in "new" */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* substitute '&' with "old" in "new" */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* rewind stack */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie * history expansion main routine
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie q, /* quotation flags */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie p, /* flag */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie c, /* current char */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie i, /* counter */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie *evp, /* event/word designator string, for error msgs */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie Sfio_t *ref=0, /* line referenced by event designator */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie static struct subst sb = {0,0}; /* substition strings */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie static Sfio_t *wm=0; /* word match from !?string? event designator */
a574db851cdc636fc3939b68e80d79fe7fbd57f2raf if((np = nv_open("histchars",sh.var_tree,0)) && (cp = nv_getval(np)))
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* save shell stack */
8cd45542f2a452ca0dab13d8b2d5cfa876ccbebcraf /* read until event/quick substitution/comment designator */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie if(hc[2] && *cp == hc[2]) /* history comment designator, skip rest of line */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie flag &= HIST_EVENT; /* save event flag for returning later */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie if(*cp == hc[0] && *(cp+1) == hc[0]) /* refer to line -1 */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie switch(c = *++cp) {
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie case '\t':
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie case '\n':
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie case '\0':
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie n = 0; /* skip history file referencing */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie if(c == '-')
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* read until end of string or word designator/modifier */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie || ((flag&HIST_QUESTION) && (*cp == '?' || *cp == '\n')))
8cd45542f2a452ca0dab13d8b2d5cfa876ccbebcraf /* search history for string */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie n = 0; /* not found */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie if(n < 0) /* determine index for backref */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* search and use history file if found */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* string not found or command # out of range */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie errormsg(SH_DICT, ERROR_ERROR, "%s: event not found", evp);
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* colon introduces either word designators or modifiers */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie w[0] = 0; /* -1 means last word, -2 means match from !?string? */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie w[1] = -1; /* -1 means last word, -2 means suppress last word */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie while(n < 2)
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie switch(c = *cp++) {
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie if(n == 0)
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie w[n] = -1;
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie if(n == 0)
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie w[0] = -2;
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie if(n == 0)
8cd45542f2a452ca0dab13d8b2d5cfa876ccbebcraf w[n] = c - '0';
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie if(n == 0)
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie w[1] = w[0];
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp);
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* no valid word designator after colon, rewind */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* open temp buffer, let sfio do the (re)allocation */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* push selected words into buffer, squash
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie whitespace into single blank or a newline */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie n = i = q = 0;
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie if(n >= w[0] && ((w[0] != -2) ? (w[1] < 0 || n <= w[1]) : 1))
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie if(w[0] < 0)
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp);
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* remove trailing newline */
10a4fa49f51ed9ae1c857a626de6ce9ebf41661arie /* close !# temp file */
494a4c5197688884a8a9a926ffc8d6627ba3bc1braf /* selected line/words are now in buffer, now go for the modifiers */
a574db851cdc636fc3939b68e80d79fe7fbd57f2raf errormsg(SH_DICT, ERROR_ERROR, "%c: unrecognized history modifier", c);
7257d1b4d25bfac0c802847390e98a464fd787acraf { /* remember position of / or . */
a574db851cdc636fc3939b68e80d79fe7fbd57f2raf if((c == '/' && *cp == 'h') || (c == '.' && *cp == 'r'))
a574db851cdc636fc3939b68e80d79fe7fbd57f2raf { /* rewind to last / or . */
7257d1b4d25bfac0c802847390e98a464fd787acraf /* end string there */
a574db851cdc636fc3939b68e80d79fe7fbd57f2raf { /* remember position of / or . */
a574db851cdc636fc3939b68e80d79fe7fbd57f2raf if((c == '/' && *cp == 't') || (c == '.' && *cp == 'e'))
a574db851cdc636fc3939b68e80d79fe7fbd57f2raf /* rewind to last / or . */
a574db851cdc636fc3939b68e80d79fe7fbd57f2raf /* copy from there on */
a574db851cdc636fc3939b68e80d79fe7fbd57f2raf if(c == 's')
a574db851cdc636fc3939b68e80d79fe7fbd57f2raf /* preset old with match from !?string? */
a574db851cdc636fc3939b68e80d79fe7fbd57f2raf "%s%s: no previous substitution",
a574db851cdc636fc3939b68e80d79fe7fbd57f2raf /* need pointer for strstr() */
a574db851cdc636fc3939b68e80d79fe7fbd57f2raf /* find string */
a574db851cdc636fc3939b68e80d79fe7fbd57f2raf { /* replace it */
*cc = c;
c = *cp;
evp);
*cp = c;
DONE();
if(*cp)
cp--;
tmp2 = 0;
cc = 0;
if(*cp)
cp++;
if(tmp)
if(isspace(c))
stakputc(c);
stakputc(c);
stakputc(c);
stakputc(c);
done:
cc = 0;
if(off)
stakseek(0);
if(tmp2)