14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*8462SApril.Chin@Sun.COM * Copyright (c) 1982-2008 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 7*8462SApril.Chin@Sun.COM * by AT&T Intellectual Property * 84887Schin * * 94887Schin * A copy of the License is available at * 104887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 124887Schin * * 134887Schin * Information and Software Systems Research * 144887Schin * AT&T Research * 154887Schin * Florham Park NJ * 164887Schin * * 174887Schin * David Korn <dgk@research.att.com> * 184887Schin * * 194887Schin ***********************************************************************/ 204887Schin #pragma prototyped 214887Schin /* 224887Schin * bash style history expansion 234887Schin * 244887Schin * Author: 254887Schin * Karsten Fleischer 264887Schin * Omnium Software Engineering 274887Schin * An der Luisenburg 7 284887Schin * D-51379 Leverkusen 294887Schin * Germany 304887Schin * 314887Schin * <K.Fleischer@omnium.de> 324887Schin */ 334887Schin 344887Schin 354887Schin #include "defs.h" 364887Schin #include "edit.h" 374887Schin 384887Schin #if ! SHOPT_HISTEXPAND 394887Schin 404887Schin NoN(hexpand) 414887Schin 424887Schin #else 434887Schin 444887Schin #include <ctype.h> 454887Schin 464887Schin static char *modifiers = "htrepqxs&"; 474887Schin static int mod_flags[] = { 0, 0, 0, 0, HIST_PRINT, HIST_QUOTE, HIST_QUOTE|HIST_QUOTE_BR, 0, 0 }; 484887Schin 494887Schin #define DONE() {flag |= HIST_ERROR; cp = 0; stakseek(0); goto done;} 504887Schin 514887Schin struct subst 524887Schin { 534887Schin char *str[2]; /* [0] is "old", [1] is "new" string */ 544887Schin }; 554887Schin 564887Schin 574887Schin /* 584887Schin * parse an /old/new/ string, delimiter expected as first char. 594887Schin * if "old" not specified, keep sb->str[0] 604887Schin * if "new" not specified, set sb->str[1] to empty string 614887Schin * read up to third delimeter char, \n or \0, whichever comes first. 624887Schin * return adress is one past the last valid char in s: 634887Schin * - the address containing \n or \0 or 644887Schin * - one char beyond the third delimiter 654887Schin */ 664887Schin 674887Schin static char *parse_subst(const char *s, struct subst *sb) 684887Schin { 694887Schin char *cp,del; 704887Schin int off,n = 0; 714887Schin 724887Schin /* build the strings on the stack, mainly for '&' substition in "new" */ 734887Schin off = staktell(); 744887Schin 754887Schin /* init "new" with empty string */ 764887Schin if(sb->str[1]) 774887Schin free(sb->str[1]); 784887Schin sb->str[1] = strdup(""); 794887Schin 804887Schin /* get delimiter */ 814887Schin del = *s; 824887Schin 834887Schin cp = (char*) s + 1; 844887Schin 854887Schin while(n < 2) 864887Schin { 874887Schin if(*cp == del || *cp == '\n' || *cp == '\0') 884887Schin { 894887Schin /* delimiter or EOL */ 904887Schin if(staktell() != off) 914887Schin { 924887Schin /* dupe string on stack and rewind stack */ 934887Schin stakputc('\0'); 944887Schin if(sb->str[n]) 954887Schin free(sb->str[n]); 964887Schin sb->str[n] = strdup(stakptr(off)); 974887Schin stakseek(off); 984887Schin } 994887Schin n++; 1004887Schin 1014887Schin /* if not delimiter, we've reached EOL. Get outta here. */ 1024887Schin if(*cp != del) 1034887Schin break; 1044887Schin } 1054887Schin else if(*cp == '\\') 1064887Schin { 1074887Schin if(*(cp+1) == del) /* quote delimiter */ 1084887Schin { 1094887Schin stakputc(del); 1104887Schin cp++; 1114887Schin } 1124887Schin else if(*(cp+1) == '&' && n == 1) 1134887Schin { /* quote '&' only in "new" */ 1144887Schin stakputc('&'); 1154887Schin cp++; 1164887Schin } 1174887Schin else 1184887Schin stakputc('\\'); 1194887Schin } 1204887Schin else if(*cp == '&' && n == 1 && sb->str[0]) 1214887Schin /* substitute '&' with "old" in "new" */ 1224887Schin stakputs(sb->str[0]); 1234887Schin else 1244887Schin stakputc(*cp); 1254887Schin cp++; 1264887Schin } 1274887Schin 1284887Schin /* rewind stack */ 1294887Schin stakseek(off); 1304887Schin 1314887Schin return cp; 1324887Schin } 1334887Schin 1344887Schin /* 1354887Schin * history expansion main routine 1364887Schin */ 1374887Schin 1384887Schin int hist_expand(const char *ln, char **xp) 1394887Schin { 1404887Schin int off, /* stack offset */ 1414887Schin q, /* quotation flags */ 1424887Schin p, /* flag */ 1434887Schin c, /* current char */ 1444887Schin flag=0; /* HIST_* flags */ 1454887Schin Sfoff_t n, /* history line number, counter, etc. */ 1464887Schin i, /* counter */ 1474887Schin w[2]; /* word range */ 1484887Schin char *sp, /* stack pointer */ 1494887Schin *cp, /* current char in ln */ 1504887Schin *str, /* search string */ 1514887Schin *evp, /* event/word designator string, for error msgs */ 1524887Schin *cc=0, /* copy of current line up to cp; temp ptr */ 1534887Schin hc[3], /* default histchars */ 1544887Schin *qc="\'\"`"; /* quote characters */ 1554887Schin Sfio_t *ref=0, /* line referenced by event designator */ 1564887Schin *tmp=0, /* temporary line buffer */ 1574887Schin *tmp2=0;/* temporary line buffer */ 1584887Schin Histloc_t hl; /* history location */ 1594887Schin static Namval_t *np = 0; /* histchars variable */ 1604887Schin static struct subst sb = {0,0}; /* substition strings */ 1614887Schin static Sfio_t *wm=0; /* word match from !?string? event designator */ 1624887Schin 1634887Schin if(!wm) 1644887Schin wm = sfopen(NULL, NULL, "swr"); 1654887Schin 1664887Schin hc[0] = '!'; 1674887Schin hc[1] = '^'; 1684887Schin hc[2] = 0; 1694887Schin if((np = nv_open("histchars",sh.var_tree,0)) && (cp = nv_getval(np))) 1704887Schin { 1714887Schin if(cp[0]) 1724887Schin { 1734887Schin hc[0] = cp[0]; 1744887Schin if(cp[1]) 1754887Schin { 1764887Schin hc[1] = cp[1]; 1774887Schin if(cp[2]) 1784887Schin hc[2] = cp[2]; 1794887Schin } 1804887Schin } 1814887Schin } 1824887Schin 1834887Schin /* save shell stack */ 1844887Schin if(off = staktell()) 1854887Schin sp = stakfreeze(0); 1864887Schin 1874887Schin cp = (char*)ln; 1884887Schin 1894887Schin while(cp && *cp) 1904887Schin { 1914887Schin /* read until event/quick substitution/comment designator */ 1924887Schin if((*cp != hc[0] && *cp != hc[1] && *cp != hc[2]) 1934887Schin || (*cp == hc[1] && cp != ln)) 1944887Schin { 1954887Schin if(*cp == '\\') /* skip escaped designators */ 1964887Schin stakputc(*cp++); 1974887Schin else if(*cp == '\'') /* skip quoted designators */ 1984887Schin { 1994887Schin do 2004887Schin stakputc(*cp); 2014887Schin while(*++cp && *cp != '\''); 2024887Schin } 2034887Schin stakputc(*cp++); 2044887Schin continue; 2054887Schin } 2064887Schin 2074887Schin if(hc[2] && *cp == hc[2]) /* history comment designator, skip rest of line */ 2084887Schin { 2094887Schin stakputc(*cp++); 2104887Schin stakputs(cp); 2114887Schin DONE(); 2124887Schin } 2134887Schin 2144887Schin n = -1; 2154887Schin str = 0; 2164887Schin flag &= HIST_EVENT; /* save event flag for returning later */ 2174887Schin evp = cp; 2184887Schin ref = 0; 2194887Schin 2204887Schin if(*cp == hc[1]) /* shortcut substitution */ 2214887Schin { 2224887Schin flag |= HIST_QUICKSUBST; 2234887Schin goto getline; 2244887Schin } 2254887Schin 2264887Schin if(*cp == hc[0] && *(cp+1) == hc[0]) /* refer to line -1 */ 2274887Schin { 2284887Schin cp += 2; 2294887Schin goto getline; 2304887Schin } 2314887Schin 2324887Schin switch(c = *++cp) { 2334887Schin case ' ': 2344887Schin case '\t': 2354887Schin case '\n': 2364887Schin case '\0': 2374887Schin case '=': 2384887Schin case '(': 2394887Schin stakputc(hc[0]); 2404887Schin continue; 2414887Schin case '#': /* the line up to current position */ 2424887Schin flag |= HIST_HASH; 2434887Schin cp++; 2444887Schin n = staktell(); /* terminate string and dup */ 2454887Schin stakputc('\0'); 2464887Schin cc = strdup(stakptr(0)); 2474887Schin stakseek(n); /* remove null byte again */ 2484887Schin ref = sfopen(ref, cc, "s"); /* open as file */ 2494887Schin n = 0; /* skip history file referencing */ 2504887Schin break; 2514887Schin case '-': /* back reference by number */ 2524887Schin if(!isdigit(*(cp+1))) 2534887Schin goto string_event; 2544887Schin cp++; 2554887Schin case '0': /* reference by number */ 2564887Schin case '1': 2574887Schin case '2': 2584887Schin case '3': 2594887Schin case '4': 2604887Schin case '5': 2614887Schin case '6': 2624887Schin case '7': 2634887Schin case '8': 2644887Schin case '9': 2654887Schin n = 0; 2664887Schin while(isdigit(*cp)) 2674887Schin n = n * 10 + (*cp++) - '0'; 2684887Schin if(c == '-') 2694887Schin n = -n; 2704887Schin break; 2714887Schin case '$': 2724887Schin n = -1; 2734887Schin case ':': 2744887Schin break; 2754887Schin case '?': 2764887Schin cp++; 2774887Schin flag |= HIST_QUESTION; 2784887Schin string_event: 2794887Schin default: 2804887Schin /* read until end of string or word designator/modifier */ 2814887Schin str = cp; 2824887Schin while(*cp) 2834887Schin { 2844887Schin cp++; 2854887Schin if((!(flag&HIST_QUESTION) && 2864887Schin (*cp == ':' || isspace(*cp) 2874887Schin || *cp == '^' || *cp == '$' 2884887Schin || *cp == '*' || *cp == '-' 2894887Schin || *cp == '%') 2904887Schin ) 2914887Schin || ((flag&HIST_QUESTION) && (*cp == '?' || *cp == '\n'))) 2924887Schin { 2934887Schin c = *cp; 2944887Schin *cp = '\0'; 2954887Schin } 2964887Schin } 2974887Schin break; 2984887Schin } 2994887Schin 3004887Schin getline: 3014887Schin flag |= HIST_EVENT; 3024887Schin if(str) /* !string or !?string? event designator */ 3034887Schin { 3044887Schin 3054887Schin /* search history for string */ 3064887Schin hl = hist_find(sh.hist_ptr, str, 3074887Schin sh.hist_ptr->histind, 3084887Schin flag&HIST_QUESTION, -1); 3094887Schin if((n = hl.hist_command) == -1) 3104887Schin n = 0; /* not found */ 3114887Schin } 3124887Schin if(n) 3134887Schin { 3144887Schin if(n < 0) /* determine index for backref */ 3154887Schin n = sh.hist_ptr->histind + n; 3164887Schin /* search and use history file if found */ 3174887Schin if(n > 0 && hist_seek(sh.hist_ptr, n) != -1) 3184887Schin ref = sh.hist_ptr->histfp; 3194887Schin 3204887Schin } 3214887Schin if(!ref) 3224887Schin { 3234887Schin /* string not found or command # out of range */ 3244887Schin c = *cp; 3254887Schin *cp = '\0'; 3264887Schin errormsg(SH_DICT, ERROR_ERROR, "%s: event not found", evp); 3274887Schin *cp = c; 3284887Schin DONE(); 3294887Schin } 3304887Schin 3314887Schin if(str) /* string search: restore orig. line */ 3324887Schin { 3334887Schin if(flag&HIST_QUESTION) 3344887Schin *cp++ = c; /* skip second question mark */ 3354887Schin else 3364887Schin *cp = c; 3374887Schin } 3384887Schin 3394887Schin /* colon introduces either word designators or modifiers */ 3404887Schin if(*(evp = cp) == ':') 3414887Schin cp++; 3424887Schin 3434887Schin w[0] = 0; /* -1 means last word, -2 means match from !?string? */ 3444887Schin w[1] = -1; /* -1 means last word, -2 means suppress last word */ 3454887Schin 3464887Schin if(flag & HIST_QUICKSUBST) /* shortcut substitution */ 3474887Schin goto getsel; 3484887Schin 3494887Schin n = 0; 3504887Schin while(n < 2) 3514887Schin { 3524887Schin switch(c = *cp++) { 3534887Schin case '^': /* first word */ 3544887Schin if(n == 0) 3554887Schin { 3564887Schin w[0] = w[1] = 1; 3574887Schin goto skip; 3584887Schin } 3594887Schin else 3604887Schin goto skip2; 3614887Schin case '$': /* last word */ 3624887Schin w[n] = -1; 3634887Schin goto skip; 3644887Schin case '%': /* match from !?string? event designator */ 3654887Schin if(n == 0) 3664887Schin { 3674887Schin if(!str) 3684887Schin { 3694887Schin w[0] = 0; 3704887Schin w[1] = -1; 3714887Schin ref = wm; 3724887Schin } 3734887Schin else 3744887Schin { 3754887Schin w[0] = -2; 3764887Schin w[1] = sftell(ref) + hl.hist_char; 3774887Schin } 3784887Schin sfseek(wm, 0, SEEK_SET); 3794887Schin goto skip; 3804887Schin } 3814887Schin default: 3824887Schin skip2: 3834887Schin cp--; 3844887Schin n = 2; 3854887Schin break; 3864887Schin case '*': /* until last word */ 3874887Schin if(n == 0) 3884887Schin w[0] = 1; 3894887Schin w[1] = -1; 3904887Schin skip: 3914887Schin flag |= HIST_WORDDSGN; 3924887Schin n = 2; 3934887Schin break; 3944887Schin case '-': /* until last word or specified index */ 3954887Schin w[1] = -2; 3964887Schin flag |= HIST_WORDDSGN; 3974887Schin n = 1; 3984887Schin break; 3994887Schin case '0': 4004887Schin case '1': 4014887Schin case '2': 4024887Schin case '3': 4034887Schin case '4': 4044887Schin case '5': 4054887Schin case '6': 4064887Schin case '7': 4074887Schin case '8': 4084887Schin case '9': /* specify index */ 4094887Schin if((*evp == ':') || w[1] == -2) 4104887Schin { 4114887Schin w[n] = c - '0'; 4124887Schin while(isdigit(c=*cp++)) 4134887Schin w[n] = w[n] * 10 + c - '0'; 4144887Schin flag |= HIST_WORDDSGN; 4154887Schin if(n == 0) 4164887Schin w[1] = w[0]; 4174887Schin n++; 4184887Schin } 4194887Schin else 4204887Schin n = 2; 4214887Schin cp--; 4224887Schin break; 4234887Schin } 4244887Schin } 4254887Schin 4264887Schin if(w[0] != -2 && w[1] > 0 && w[0] > w[1]) 4274887Schin { 4284887Schin c = *cp; 4294887Schin *cp = '\0'; 4304887Schin errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp); 4314887Schin *cp = c; 4324887Schin DONE(); 4334887Schin } 4344887Schin 4354887Schin /* no valid word designator after colon, rewind */ 4364887Schin if(!(flag & HIST_WORDDSGN) && (*evp == ':')) 4374887Schin cp = evp; 4384887Schin 4394887Schin getsel: 4404887Schin /* open temp buffer, let sfio do the (re)allocation */ 4414887Schin tmp = sfopen(NULL, NULL, "swr"); 4424887Schin 4434887Schin /* push selected words into buffer, squash 4444887Schin whitespace into single blank or a newline */ 4454887Schin n = i = q = 0; 4464887Schin 4474887Schin while((c = sfgetc(ref)) > 0) 4484887Schin { 4494887Schin if(isspace(c)) 4504887Schin { 4514887Schin flag |= (c == '\n' ? HIST_NEWLINE : 0); 4524887Schin continue; 4534887Schin } 4544887Schin 4554887Schin if(n >= w[0] && ((w[0] != -2) ? (w[1] < 0 || n <= w[1]) : 1)) 4564887Schin { 4574887Schin if(w[0] < 0) 4584887Schin sfseek(tmp, 0, SEEK_SET); 4594887Schin else 4604887Schin i = sftell(tmp); 4614887Schin 4624887Schin if(i > 0) 4634887Schin sfputc(tmp, flag & HIST_NEWLINE ? '\n' : ' '); 4644887Schin 4654887Schin flag &= ~HIST_NEWLINE; 4664887Schin p = 1; 4674887Schin } 4684887Schin else 4694887Schin p = 0; 4704887Schin 4714887Schin do 4724887Schin { 4734887Schin cc = strchr(qc, c); 4744887Schin q ^= cc ? 1<<(int)(cc - qc) : 0; 4754887Schin if(p) 4764887Schin sfputc(tmp, c); 4774887Schin } 4784887Schin while((c = sfgetc(ref)) > 0 && (!isspace(c) || q)); 4794887Schin 4804887Schin if(w[0] == -2 && sftell(ref) > w[1]) 4814887Schin break; 4824887Schin 4834887Schin flag |= (c == '\n' ? HIST_NEWLINE : 0); 4844887Schin n++; 4854887Schin } 4864887Schin if(w[0] != -2 && w[1] >= 0 && w[1] >= n) 4874887Schin { 4884887Schin c = *cp; 4894887Schin *cp = '\0'; 4904887Schin errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp); 4914887Schin *cp = c; 4924887Schin DONE(); 4934887Schin } 4944887Schin else if(w[1] == -2) /* skip last word */ 4954887Schin sfseek(tmp, i, SEEK_SET); 4964887Schin 4974887Schin /* remove trailing newline */ 4984887Schin if(sftell(tmp)) 4994887Schin { 5004887Schin sfseek(tmp, -1, SEEK_CUR); 5014887Schin if(sfgetc(tmp) == '\n') 5024887Schin sfungetc(tmp, '\n'); 5034887Schin } 5044887Schin 5054887Schin sfputc(tmp, '\0'); 5064887Schin 5074887Schin if(str) 5084887Schin { 5094887Schin if(wm) 5104887Schin sfclose(wm); 5114887Schin wm = tmp; 5124887Schin } 5134887Schin 5144887Schin if(cc && (flag&HIST_HASH)) 5154887Schin { 5164887Schin /* close !# temp file */ 5174887Schin sfclose(ref); 5184887Schin flag &= ~HIST_HASH; 5194887Schin free(cc); 5204887Schin cc = 0; 5214887Schin } 5224887Schin 5234887Schin evp = cp; 5244887Schin 5254887Schin /* selected line/words are now in buffer, now go for the modifiers */ 5264887Schin while(*cp == ':' || (flag & HIST_QUICKSUBST)) 5274887Schin { 5284887Schin if(flag & HIST_QUICKSUBST) 5294887Schin { 5304887Schin flag &= ~HIST_QUICKSUBST; 5314887Schin c = 's'; 5324887Schin cp--; 5334887Schin } 5344887Schin else 5354887Schin c = *++cp; 5364887Schin 5374887Schin sfseek(tmp, 0, SEEK_SET); 5384887Schin tmp2 = sfopen(tmp2, NULL, "swr"); 5394887Schin 5404887Schin if(c == 'g') /* global substitution */ 5414887Schin { 5424887Schin flag |= HIST_GLOBALSUBST; 5434887Schin c = *++cp; 5444887Schin } 5454887Schin 5464887Schin if(cc = strchr(modifiers, c)) 5474887Schin flag |= mod_flags[cc - modifiers]; 5484887Schin else 5494887Schin { 5504887Schin errormsg(SH_DICT, ERROR_ERROR, "%c: unrecognized history modifier", c); 5514887Schin DONE(); 5524887Schin } 5534887Schin 5544887Schin if(c == 'h' || c == 'r') /* head or base */ 5554887Schin { 5564887Schin n = -1; 5574887Schin while((c = sfgetc(tmp)) > 0) 5584887Schin { /* remember position of / or . */ 5594887Schin if((c == '/' && *cp == 'h') || (c == '.' && *cp == 'r')) 5604887Schin n = sftell(tmp2); 5614887Schin sfputc(tmp2, c); 5624887Schin } 5634887Schin if(n > 0) 5644887Schin { /* rewind to last / or . */ 5654887Schin sfseek(tmp2, n, SEEK_SET); 5664887Schin /* end string there */ 5674887Schin sfputc(tmp2, '\0'); 5684887Schin } 5694887Schin } 5704887Schin else if(c == 't' || c == 'e') /* tail or suffix */ 5714887Schin { 5724887Schin n = 0; 5734887Schin while((c = sfgetc(tmp)) > 0) 5744887Schin { /* remember position of / or . */ 5754887Schin if((c == '/' && *cp == 't') || (c == '.' && *cp == 'e')) 5764887Schin n = sftell(tmp); 5774887Schin } 5784887Schin /* rewind to last / or . */ 5794887Schin sfseek(tmp, n, SEEK_SET); 5804887Schin /* copy from there on */ 5814887Schin while((c = sfgetc(tmp)) > 0) 5824887Schin sfputc(tmp2, c); 5834887Schin } 5844887Schin else if(c == 's' || c == '&') 5854887Schin { 5864887Schin cp++; 5874887Schin 5884887Schin if(c == 's') 5894887Schin { 5904887Schin /* preset old with match from !?string? */ 5914887Schin if(!sb.str[0] && wm) 5924887Schin sb.str[0] = strdup(sfsetbuf(wm, (Void_t*)1, 0)); 5934887Schin cp = parse_subst(cp, &sb); 5944887Schin } 5954887Schin 5964887Schin if(!sb.str[0] || !sb.str[1]) 5974887Schin { 5984887Schin c = *cp; 5994887Schin *cp = '\0'; 6004887Schin errormsg(SH_DICT, ERROR_ERROR, 6014887Schin "%s%s: no previous substitution", 6024887Schin (flag & HIST_QUICKSUBST) ? ":s" : "", 6034887Schin evp); 6044887Schin *cp = c; 6054887Schin DONE(); 6064887Schin } 6074887Schin 6084887Schin /* need pointer for strstr() */ 6094887Schin str = sfsetbuf(tmp, (Void_t*)1, 0); 6104887Schin 6114887Schin flag |= HIST_SUBSTITUTE; 6124887Schin while(flag & HIST_SUBSTITUTE) 6134887Schin { 6144887Schin /* find string */ 6154887Schin if(cc = strstr(str, sb.str[0])) 6164887Schin { /* replace it */ 6174887Schin c = *cc; 6184887Schin *cc = '\0'; 6194887Schin sfputr(tmp2, str, -1); 6204887Schin sfputr(tmp2, sb.str[1], -1); 6214887Schin *cc = c; 6224887Schin str = cc + strlen(sb.str[0]); 6234887Schin } 6244887Schin else if(!sftell(tmp2)) 6254887Schin { /* not successfull */ 6264887Schin c = *cp; 6274887Schin *cp = '\0'; 6284887Schin errormsg(SH_DICT, ERROR_ERROR, 6294887Schin "%s%s: substitution failed", 6304887Schin (flag & HIST_QUICKSUBST) ? ":s" : "", 6314887Schin evp); 6324887Schin *cp = c; 6334887Schin DONE(); 6344887Schin } 6354887Schin /* loop if g modifier specified */ 6364887Schin if(!cc || !(flag & HIST_GLOBALSUBST)) 6374887Schin flag &= ~HIST_SUBSTITUTE; 6384887Schin } 6394887Schin /* output rest of line */ 6404887Schin sfputr(tmp2, str, -1); 6414887Schin if(*cp) 6424887Schin cp--; 6434887Schin } 6444887Schin 6454887Schin if(sftell(tmp2)) 6464887Schin { /* if any substitions done, swap buffers */ 6474887Schin if(wm != tmp) 6484887Schin sfclose(tmp); 6494887Schin tmp = tmp2; 6504887Schin tmp2 = 0; 6514887Schin } 6524887Schin cc = 0; 6534887Schin if(*cp) 6544887Schin cp++; 6554887Schin } 6564887Schin 6574887Schin /* flush temporary buffer to stack */ 6584887Schin if(tmp) 6594887Schin { 6604887Schin sfseek(tmp, 0, SEEK_SET); 6614887Schin 6624887Schin if(flag & HIST_QUOTE) 6634887Schin stakputc('\''); 6644887Schin 6654887Schin while((c = sfgetc(tmp)) > 0) 6664887Schin { 6674887Schin if(isspace(c)) 6684887Schin { 6694887Schin flag = flag & ~HIST_NEWLINE; 6704887Schin 6714887Schin /* squash white space to either a 6724887Schin blank or a newline */ 6734887Schin do 6744887Schin flag |= (c == '\n' ? HIST_NEWLINE : 0); 6754887Schin while((c = sfgetc(tmp)) > 0 && isspace(c)); 6764887Schin 6774887Schin sfungetc(tmp, c); 6784887Schin 6794887Schin c = (flag & HIST_NEWLINE) ? '\n' : ' '; 6804887Schin 6814887Schin if(flag & HIST_QUOTE_BR) 6824887Schin { 6834887Schin stakputc('\''); 6844887Schin stakputc(c); 6854887Schin stakputc('\''); 6864887Schin } 6874887Schin else 6884887Schin stakputc(c); 6894887Schin } 6904887Schin else if((c == '\'') && (flag & HIST_QUOTE)) 6914887Schin { 6924887Schin stakputc('\''); 6934887Schin stakputc('\\'); 6944887Schin stakputc(c); 6954887Schin stakputc('\''); 6964887Schin } 6974887Schin else 6984887Schin stakputc(c); 6994887Schin } 7004887Schin if(flag & HIST_QUOTE) 7014887Schin stakputc('\''); 7024887Schin } 7034887Schin } 7044887Schin 7054887Schin stakputc('\0'); 7064887Schin 7074887Schin done: 7084887Schin if(cc && (flag&HIST_HASH)) 7094887Schin { 7104887Schin /* close !# temp file */ 7114887Schin sfclose(ref); 7124887Schin free(cc); 7134887Schin cc = 0; 7144887Schin } 7154887Schin 7164887Schin /* error? */ 7174887Schin if(staktell() && !(flag & HIST_ERROR)) 7184887Schin *xp = strdup(stakfreeze(1)); 7194887Schin 7204887Schin /* restore shell stack */ 7214887Schin if(off) 7224887Schin stakset(sp,off); 7234887Schin else 7244887Schin stakseek(0); 7254887Schin 7264887Schin /* drop temporary files */ 7274887Schin 7284887Schin if(tmp && tmp != wm) 7294887Schin sfclose(tmp); 7304887Schin if(tmp2) 7314887Schin sfclose(tmp2); 7324887Schin 7334887Schin return (flag & HIST_ERROR ? HIST_ERROR : flag & HIST_FLAG_RETURN_MASK); 7344887Schin } 7354887Schin 7364887Schin #endif 737