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