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