1*4887Schin /*********************************************************************** 2*4887Schin * * 3*4887Schin * This software is part of the ast package * 4*4887Schin * Copyright (c) 1982-2007 AT&T Knowledge Ventures * 5*4887Schin * and is licensed under the * 6*4887Schin * Common Public License, Version 1.0 * 7*4887Schin * by AT&T Knowledge Ventures * 8*4887Schin * * 9*4887Schin * A copy of the License is available at * 10*4887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 11*4887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12*4887Schin * * 13*4887Schin * Information and Software Systems Research * 14*4887Schin * AT&T Research * 15*4887Schin * Florham Park NJ * 16*4887Schin * * 17*4887Schin * David Korn <dgk@research.att.com> * 18*4887Schin * * 19*4887Schin ***********************************************************************/ 20*4887Schin #pragma prototyped 21*4887Schin /* 22*4887Schin * bash style history expansion 23*4887Schin * 24*4887Schin * Author: 25*4887Schin * Karsten Fleischer 26*4887Schin * Omnium Software Engineering 27*4887Schin * An der Luisenburg 7 28*4887Schin * D-51379 Leverkusen 29*4887Schin * Germany 30*4887Schin * 31*4887Schin * <K.Fleischer@omnium.de> 32*4887Schin */ 33*4887Schin 34*4887Schin 35*4887Schin #include "defs.h" 36*4887Schin #include "edit.h" 37*4887Schin 38*4887Schin #if ! SHOPT_HISTEXPAND 39*4887Schin 40*4887Schin NoN(hexpand) 41*4887Schin 42*4887Schin #else 43*4887Schin 44*4887Schin #include <ctype.h> 45*4887Schin 46*4887Schin static char *modifiers = "htrepqxs&"; 47*4887Schin static int mod_flags[] = { 0, 0, 0, 0, HIST_PRINT, HIST_QUOTE, HIST_QUOTE|HIST_QUOTE_BR, 0, 0 }; 48*4887Schin 49*4887Schin #define DONE() {flag |= HIST_ERROR; cp = 0; stakseek(0); goto done;} 50*4887Schin 51*4887Schin struct subst 52*4887Schin { 53*4887Schin char *str[2]; /* [0] is "old", [1] is "new" string */ 54*4887Schin }; 55*4887Schin 56*4887Schin 57*4887Schin /* 58*4887Schin * parse an /old/new/ string, delimiter expected as first char. 59*4887Schin * if "old" not specified, keep sb->str[0] 60*4887Schin * if "new" not specified, set sb->str[1] to empty string 61*4887Schin * read up to third delimeter char, \n or \0, whichever comes first. 62*4887Schin * return adress is one past the last valid char in s: 63*4887Schin * - the address containing \n or \0 or 64*4887Schin * - one char beyond the third delimiter 65*4887Schin */ 66*4887Schin 67*4887Schin static char *parse_subst(const char *s, struct subst *sb) 68*4887Schin { 69*4887Schin char *cp,del; 70*4887Schin int off,n = 0; 71*4887Schin 72*4887Schin /* build the strings on the stack, mainly for '&' substition in "new" */ 73*4887Schin off = staktell(); 74*4887Schin 75*4887Schin /* init "new" with empty string */ 76*4887Schin if(sb->str[1]) 77*4887Schin free(sb->str[1]); 78*4887Schin sb->str[1] = strdup(""); 79*4887Schin 80*4887Schin /* get delimiter */ 81*4887Schin del = *s; 82*4887Schin 83*4887Schin cp = (char*) s + 1; 84*4887Schin 85*4887Schin while(n < 2) 86*4887Schin { 87*4887Schin if(*cp == del || *cp == '\n' || *cp == '\0') 88*4887Schin { 89*4887Schin /* delimiter or EOL */ 90*4887Schin if(staktell() != off) 91*4887Schin { 92*4887Schin /* dupe string on stack and rewind stack */ 93*4887Schin stakputc('\0'); 94*4887Schin if(sb->str[n]) 95*4887Schin free(sb->str[n]); 96*4887Schin sb->str[n] = strdup(stakptr(off)); 97*4887Schin stakseek(off); 98*4887Schin } 99*4887Schin n++; 100*4887Schin 101*4887Schin /* if not delimiter, we've reached EOL. Get outta here. */ 102*4887Schin if(*cp != del) 103*4887Schin break; 104*4887Schin } 105*4887Schin else if(*cp == '\\') 106*4887Schin { 107*4887Schin if(*(cp+1) == del) /* quote delimiter */ 108*4887Schin { 109*4887Schin stakputc(del); 110*4887Schin cp++; 111*4887Schin } 112*4887Schin else if(*(cp+1) == '&' && n == 1) 113*4887Schin { /* quote '&' only in "new" */ 114*4887Schin stakputc('&'); 115*4887Schin cp++; 116*4887Schin } 117*4887Schin else 118*4887Schin stakputc('\\'); 119*4887Schin } 120*4887Schin else if(*cp == '&' && n == 1 && sb->str[0]) 121*4887Schin /* substitute '&' with "old" in "new" */ 122*4887Schin stakputs(sb->str[0]); 123*4887Schin else 124*4887Schin stakputc(*cp); 125*4887Schin cp++; 126*4887Schin } 127*4887Schin 128*4887Schin /* rewind stack */ 129*4887Schin stakseek(off); 130*4887Schin 131*4887Schin return cp; 132*4887Schin } 133*4887Schin 134*4887Schin /* 135*4887Schin * history expansion main routine 136*4887Schin */ 137*4887Schin 138*4887Schin int hist_expand(const char *ln, char **xp) 139*4887Schin { 140*4887Schin int off, /* stack offset */ 141*4887Schin q, /* quotation flags */ 142*4887Schin p, /* flag */ 143*4887Schin c, /* current char */ 144*4887Schin flag=0; /* HIST_* flags */ 145*4887Schin Sfoff_t n, /* history line number, counter, etc. */ 146*4887Schin i, /* counter */ 147*4887Schin w[2]; /* word range */ 148*4887Schin char *sp, /* stack pointer */ 149*4887Schin *cp, /* current char in ln */ 150*4887Schin *str, /* search string */ 151*4887Schin *evp, /* event/word designator string, for error msgs */ 152*4887Schin *cc=0, /* copy of current line up to cp; temp ptr */ 153*4887Schin hc[3], /* default histchars */ 154*4887Schin *qc="\'\"`"; /* quote characters */ 155*4887Schin Sfio_t *ref=0, /* line referenced by event designator */ 156*4887Schin *tmp=0, /* temporary line buffer */ 157*4887Schin *tmp2=0;/* temporary line buffer */ 158*4887Schin Histloc_t hl; /* history location */ 159*4887Schin static Namval_t *np = 0; /* histchars variable */ 160*4887Schin static struct subst sb = {0,0}; /* substition strings */ 161*4887Schin static Sfio_t *wm=0; /* word match from !?string? event designator */ 162*4887Schin 163*4887Schin if(!wm) 164*4887Schin wm = sfopen(NULL, NULL, "swr"); 165*4887Schin 166*4887Schin hc[0] = '!'; 167*4887Schin hc[1] = '^'; 168*4887Schin hc[2] = 0; 169*4887Schin if((np = nv_open("histchars",sh.var_tree,0)) && (cp = nv_getval(np))) 170*4887Schin { 171*4887Schin if(cp[0]) 172*4887Schin { 173*4887Schin hc[0] = cp[0]; 174*4887Schin if(cp[1]) 175*4887Schin { 176*4887Schin hc[1] = cp[1]; 177*4887Schin if(cp[2]) 178*4887Schin hc[2] = cp[2]; 179*4887Schin } 180*4887Schin } 181*4887Schin } 182*4887Schin 183*4887Schin /* save shell stack */ 184*4887Schin if(off = staktell()) 185*4887Schin sp = stakfreeze(0); 186*4887Schin 187*4887Schin cp = (char*)ln; 188*4887Schin 189*4887Schin while(cp && *cp) 190*4887Schin { 191*4887Schin /* read until event/quick substitution/comment designator */ 192*4887Schin if((*cp != hc[0] && *cp != hc[1] && *cp != hc[2]) 193*4887Schin || (*cp == hc[1] && cp != ln)) 194*4887Schin { 195*4887Schin if(*cp == '\\') /* skip escaped designators */ 196*4887Schin stakputc(*cp++); 197*4887Schin else if(*cp == '\'') /* skip quoted designators */ 198*4887Schin { 199*4887Schin do 200*4887Schin stakputc(*cp); 201*4887Schin while(*++cp && *cp != '\''); 202*4887Schin } 203*4887Schin stakputc(*cp++); 204*4887Schin continue; 205*4887Schin } 206*4887Schin 207*4887Schin if(hc[2] && *cp == hc[2]) /* history comment designator, skip rest of line */ 208*4887Schin { 209*4887Schin stakputc(*cp++); 210*4887Schin stakputs(cp); 211*4887Schin DONE(); 212*4887Schin } 213*4887Schin 214*4887Schin n = -1; 215*4887Schin str = 0; 216*4887Schin flag &= HIST_EVENT; /* save event flag for returning later */ 217*4887Schin evp = cp; 218*4887Schin ref = 0; 219*4887Schin 220*4887Schin if(*cp == hc[1]) /* shortcut substitution */ 221*4887Schin { 222*4887Schin flag |= HIST_QUICKSUBST; 223*4887Schin goto getline; 224*4887Schin } 225*4887Schin 226*4887Schin if(*cp == hc[0] && *(cp+1) == hc[0]) /* refer to line -1 */ 227*4887Schin { 228*4887Schin cp += 2; 229*4887Schin goto getline; 230*4887Schin } 231*4887Schin 232*4887Schin switch(c = *++cp) { 233*4887Schin case ' ': 234*4887Schin case '\t': 235*4887Schin case '\n': 236*4887Schin case '\0': 237*4887Schin case '=': 238*4887Schin case '(': 239*4887Schin stakputc(hc[0]); 240*4887Schin continue; 241*4887Schin case '#': /* the line up to current position */ 242*4887Schin flag |= HIST_HASH; 243*4887Schin cp++; 244*4887Schin n = staktell(); /* terminate string and dup */ 245*4887Schin stakputc('\0'); 246*4887Schin cc = strdup(stakptr(0)); 247*4887Schin stakseek(n); /* remove null byte again */ 248*4887Schin ref = sfopen(ref, cc, "s"); /* open as file */ 249*4887Schin n = 0; /* skip history file referencing */ 250*4887Schin break; 251*4887Schin case '-': /* back reference by number */ 252*4887Schin if(!isdigit(*(cp+1))) 253*4887Schin goto string_event; 254*4887Schin cp++; 255*4887Schin case '0': /* reference by number */ 256*4887Schin case '1': 257*4887Schin case '2': 258*4887Schin case '3': 259*4887Schin case '4': 260*4887Schin case '5': 261*4887Schin case '6': 262*4887Schin case '7': 263*4887Schin case '8': 264*4887Schin case '9': 265*4887Schin n = 0; 266*4887Schin while(isdigit(*cp)) 267*4887Schin n = n * 10 + (*cp++) - '0'; 268*4887Schin if(c == '-') 269*4887Schin n = -n; 270*4887Schin break; 271*4887Schin case '$': 272*4887Schin n = -1; 273*4887Schin case ':': 274*4887Schin break; 275*4887Schin case '?': 276*4887Schin cp++; 277*4887Schin flag |= HIST_QUESTION; 278*4887Schin string_event: 279*4887Schin default: 280*4887Schin /* read until end of string or word designator/modifier */ 281*4887Schin str = cp; 282*4887Schin while(*cp) 283*4887Schin { 284*4887Schin cp++; 285*4887Schin if((!(flag&HIST_QUESTION) && 286*4887Schin (*cp == ':' || isspace(*cp) 287*4887Schin || *cp == '^' || *cp == '$' 288*4887Schin || *cp == '*' || *cp == '-' 289*4887Schin || *cp == '%') 290*4887Schin ) 291*4887Schin || ((flag&HIST_QUESTION) && (*cp == '?' || *cp == '\n'))) 292*4887Schin { 293*4887Schin c = *cp; 294*4887Schin *cp = '\0'; 295*4887Schin } 296*4887Schin } 297*4887Schin break; 298*4887Schin } 299*4887Schin 300*4887Schin getline: 301*4887Schin flag |= HIST_EVENT; 302*4887Schin if(str) /* !string or !?string? event designator */ 303*4887Schin { 304*4887Schin 305*4887Schin /* search history for string */ 306*4887Schin hl = hist_find(sh.hist_ptr, str, 307*4887Schin sh.hist_ptr->histind, 308*4887Schin flag&HIST_QUESTION, -1); 309*4887Schin if((n = hl.hist_command) == -1) 310*4887Schin n = 0; /* not found */ 311*4887Schin } 312*4887Schin if(n) 313*4887Schin { 314*4887Schin if(n < 0) /* determine index for backref */ 315*4887Schin n = sh.hist_ptr->histind + n; 316*4887Schin /* search and use history file if found */ 317*4887Schin if(n > 0 && hist_seek(sh.hist_ptr, n) != -1) 318*4887Schin ref = sh.hist_ptr->histfp; 319*4887Schin 320*4887Schin } 321*4887Schin if(!ref) 322*4887Schin { 323*4887Schin /* string not found or command # out of range */ 324*4887Schin c = *cp; 325*4887Schin *cp = '\0'; 326*4887Schin errormsg(SH_DICT, ERROR_ERROR, "%s: event not found", evp); 327*4887Schin *cp = c; 328*4887Schin DONE(); 329*4887Schin } 330*4887Schin 331*4887Schin if(str) /* string search: restore orig. line */ 332*4887Schin { 333*4887Schin if(flag&HIST_QUESTION) 334*4887Schin *cp++ = c; /* skip second question mark */ 335*4887Schin else 336*4887Schin *cp = c; 337*4887Schin } 338*4887Schin 339*4887Schin /* colon introduces either word designators or modifiers */ 340*4887Schin if(*(evp = cp) == ':') 341*4887Schin cp++; 342*4887Schin 343*4887Schin w[0] = 0; /* -1 means last word, -2 means match from !?string? */ 344*4887Schin w[1] = -1; /* -1 means last word, -2 means suppress last word */ 345*4887Schin 346*4887Schin if(flag & HIST_QUICKSUBST) /* shortcut substitution */ 347*4887Schin goto getsel; 348*4887Schin 349*4887Schin n = 0; 350*4887Schin while(n < 2) 351*4887Schin { 352*4887Schin switch(c = *cp++) { 353*4887Schin case '^': /* first word */ 354*4887Schin if(n == 0) 355*4887Schin { 356*4887Schin w[0] = w[1] = 1; 357*4887Schin goto skip; 358*4887Schin } 359*4887Schin else 360*4887Schin goto skip2; 361*4887Schin case '$': /* last word */ 362*4887Schin w[n] = -1; 363*4887Schin goto skip; 364*4887Schin case '%': /* match from !?string? event designator */ 365*4887Schin if(n == 0) 366*4887Schin { 367*4887Schin if(!str) 368*4887Schin { 369*4887Schin w[0] = 0; 370*4887Schin w[1] = -1; 371*4887Schin ref = wm; 372*4887Schin } 373*4887Schin else 374*4887Schin { 375*4887Schin w[0] = -2; 376*4887Schin w[1] = sftell(ref) + hl.hist_char; 377*4887Schin } 378*4887Schin sfseek(wm, 0, SEEK_SET); 379*4887Schin goto skip; 380*4887Schin } 381*4887Schin default: 382*4887Schin skip2: 383*4887Schin cp--; 384*4887Schin n = 2; 385*4887Schin break; 386*4887Schin case '*': /* until last word */ 387*4887Schin if(n == 0) 388*4887Schin w[0] = 1; 389*4887Schin w[1] = -1; 390*4887Schin skip: 391*4887Schin flag |= HIST_WORDDSGN; 392*4887Schin n = 2; 393*4887Schin break; 394*4887Schin case '-': /* until last word or specified index */ 395*4887Schin w[1] = -2; 396*4887Schin flag |= HIST_WORDDSGN; 397*4887Schin n = 1; 398*4887Schin break; 399*4887Schin case '0': 400*4887Schin case '1': 401*4887Schin case '2': 402*4887Schin case '3': 403*4887Schin case '4': 404*4887Schin case '5': 405*4887Schin case '6': 406*4887Schin case '7': 407*4887Schin case '8': 408*4887Schin case '9': /* specify index */ 409*4887Schin if((*evp == ':') || w[1] == -2) 410*4887Schin { 411*4887Schin w[n] = c - '0'; 412*4887Schin while(isdigit(c=*cp++)) 413*4887Schin w[n] = w[n] * 10 + c - '0'; 414*4887Schin flag |= HIST_WORDDSGN; 415*4887Schin if(n == 0) 416*4887Schin w[1] = w[0]; 417*4887Schin n++; 418*4887Schin } 419*4887Schin else 420*4887Schin n = 2; 421*4887Schin cp--; 422*4887Schin break; 423*4887Schin } 424*4887Schin } 425*4887Schin 426*4887Schin if(w[0] != -2 && w[1] > 0 && w[0] > w[1]) 427*4887Schin { 428*4887Schin c = *cp; 429*4887Schin *cp = '\0'; 430*4887Schin errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp); 431*4887Schin *cp = c; 432*4887Schin DONE(); 433*4887Schin } 434*4887Schin 435*4887Schin /* no valid word designator after colon, rewind */ 436*4887Schin if(!(flag & HIST_WORDDSGN) && (*evp == ':')) 437*4887Schin cp = evp; 438*4887Schin 439*4887Schin getsel: 440*4887Schin /* open temp buffer, let sfio do the (re)allocation */ 441*4887Schin tmp = sfopen(NULL, NULL, "swr"); 442*4887Schin 443*4887Schin /* push selected words into buffer, squash 444*4887Schin whitespace into single blank or a newline */ 445*4887Schin n = i = q = 0; 446*4887Schin 447*4887Schin while((c = sfgetc(ref)) > 0) 448*4887Schin { 449*4887Schin if(isspace(c)) 450*4887Schin { 451*4887Schin flag |= (c == '\n' ? HIST_NEWLINE : 0); 452*4887Schin continue; 453*4887Schin } 454*4887Schin 455*4887Schin if(n >= w[0] && ((w[0] != -2) ? (w[1] < 0 || n <= w[1]) : 1)) 456*4887Schin { 457*4887Schin if(w[0] < 0) 458*4887Schin sfseek(tmp, 0, SEEK_SET); 459*4887Schin else 460*4887Schin i = sftell(tmp); 461*4887Schin 462*4887Schin if(i > 0) 463*4887Schin sfputc(tmp, flag & HIST_NEWLINE ? '\n' : ' '); 464*4887Schin 465*4887Schin flag &= ~HIST_NEWLINE; 466*4887Schin p = 1; 467*4887Schin } 468*4887Schin else 469*4887Schin p = 0; 470*4887Schin 471*4887Schin do 472*4887Schin { 473*4887Schin cc = strchr(qc, c); 474*4887Schin q ^= cc ? 1<<(int)(cc - qc) : 0; 475*4887Schin if(p) 476*4887Schin sfputc(tmp, c); 477*4887Schin } 478*4887Schin while((c = sfgetc(ref)) > 0 && (!isspace(c) || q)); 479*4887Schin 480*4887Schin if(w[0] == -2 && sftell(ref) > w[1]) 481*4887Schin break; 482*4887Schin 483*4887Schin flag |= (c == '\n' ? HIST_NEWLINE : 0); 484*4887Schin n++; 485*4887Schin } 486*4887Schin if(w[0] != -2 && w[1] >= 0 && w[1] >= n) 487*4887Schin { 488*4887Schin c = *cp; 489*4887Schin *cp = '\0'; 490*4887Schin errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp); 491*4887Schin *cp = c; 492*4887Schin DONE(); 493*4887Schin } 494*4887Schin else if(w[1] == -2) /* skip last word */ 495*4887Schin sfseek(tmp, i, SEEK_SET); 496*4887Schin 497*4887Schin /* remove trailing newline */ 498*4887Schin if(sftell(tmp)) 499*4887Schin { 500*4887Schin sfseek(tmp, -1, SEEK_CUR); 501*4887Schin if(sfgetc(tmp) == '\n') 502*4887Schin sfungetc(tmp, '\n'); 503*4887Schin } 504*4887Schin 505*4887Schin sfputc(tmp, '\0'); 506*4887Schin 507*4887Schin if(str) 508*4887Schin { 509*4887Schin if(wm) 510*4887Schin sfclose(wm); 511*4887Schin wm = tmp; 512*4887Schin } 513*4887Schin 514*4887Schin if(cc && (flag&HIST_HASH)) 515*4887Schin { 516*4887Schin /* close !# temp file */ 517*4887Schin sfclose(ref); 518*4887Schin flag &= ~HIST_HASH; 519*4887Schin free(cc); 520*4887Schin cc = 0; 521*4887Schin } 522*4887Schin 523*4887Schin evp = cp; 524*4887Schin 525*4887Schin /* selected line/words are now in buffer, now go for the modifiers */ 526*4887Schin while(*cp == ':' || (flag & HIST_QUICKSUBST)) 527*4887Schin { 528*4887Schin if(flag & HIST_QUICKSUBST) 529*4887Schin { 530*4887Schin flag &= ~HIST_QUICKSUBST; 531*4887Schin c = 's'; 532*4887Schin cp--; 533*4887Schin } 534*4887Schin else 535*4887Schin c = *++cp; 536*4887Schin 537*4887Schin sfseek(tmp, 0, SEEK_SET); 538*4887Schin tmp2 = sfopen(tmp2, NULL, "swr"); 539*4887Schin 540*4887Schin if(c == 'g') /* global substitution */ 541*4887Schin { 542*4887Schin flag |= HIST_GLOBALSUBST; 543*4887Schin c = *++cp; 544*4887Schin } 545*4887Schin 546*4887Schin if(cc = strchr(modifiers, c)) 547*4887Schin flag |= mod_flags[cc - modifiers]; 548*4887Schin else 549*4887Schin { 550*4887Schin errormsg(SH_DICT, ERROR_ERROR, "%c: unrecognized history modifier", c); 551*4887Schin DONE(); 552*4887Schin } 553*4887Schin 554*4887Schin if(c == 'h' || c == 'r') /* head or base */ 555*4887Schin { 556*4887Schin n = -1; 557*4887Schin while((c = sfgetc(tmp)) > 0) 558*4887Schin { /* remember position of / or . */ 559*4887Schin if((c == '/' && *cp == 'h') || (c == '.' && *cp == 'r')) 560*4887Schin n = sftell(tmp2); 561*4887Schin sfputc(tmp2, c); 562*4887Schin } 563*4887Schin if(n > 0) 564*4887Schin { /* rewind to last / or . */ 565*4887Schin sfseek(tmp2, n, SEEK_SET); 566*4887Schin /* end string there */ 567*4887Schin sfputc(tmp2, '\0'); 568*4887Schin } 569*4887Schin } 570*4887Schin else if(c == 't' || c == 'e') /* tail or suffix */ 571*4887Schin { 572*4887Schin n = 0; 573*4887Schin while((c = sfgetc(tmp)) > 0) 574*4887Schin { /* remember position of / or . */ 575*4887Schin if((c == '/' && *cp == 't') || (c == '.' && *cp == 'e')) 576*4887Schin n = sftell(tmp); 577*4887Schin } 578*4887Schin /* rewind to last / or . */ 579*4887Schin sfseek(tmp, n, SEEK_SET); 580*4887Schin /* copy from there on */ 581*4887Schin while((c = sfgetc(tmp)) > 0) 582*4887Schin sfputc(tmp2, c); 583*4887Schin } 584*4887Schin else if(c == 's' || c == '&') 585*4887Schin { 586*4887Schin cp++; 587*4887Schin 588*4887Schin if(c == 's') 589*4887Schin { 590*4887Schin /* preset old with match from !?string? */ 591*4887Schin if(!sb.str[0] && wm) 592*4887Schin sb.str[0] = strdup(sfsetbuf(wm, (Void_t*)1, 0)); 593*4887Schin cp = parse_subst(cp, &sb); 594*4887Schin } 595*4887Schin 596*4887Schin if(!sb.str[0] || !sb.str[1]) 597*4887Schin { 598*4887Schin c = *cp; 599*4887Schin *cp = '\0'; 600*4887Schin errormsg(SH_DICT, ERROR_ERROR, 601*4887Schin "%s%s: no previous substitution", 602*4887Schin (flag & HIST_QUICKSUBST) ? ":s" : "", 603*4887Schin evp); 604*4887Schin *cp = c; 605*4887Schin DONE(); 606*4887Schin } 607*4887Schin 608*4887Schin /* need pointer for strstr() */ 609*4887Schin str = sfsetbuf(tmp, (Void_t*)1, 0); 610*4887Schin 611*4887Schin flag |= HIST_SUBSTITUTE; 612*4887Schin while(flag & HIST_SUBSTITUTE) 613*4887Schin { 614*4887Schin /* find string */ 615*4887Schin if(cc = strstr(str, sb.str[0])) 616*4887Schin { /* replace it */ 617*4887Schin c = *cc; 618*4887Schin *cc = '\0'; 619*4887Schin sfputr(tmp2, str, -1); 620*4887Schin sfputr(tmp2, sb.str[1], -1); 621*4887Schin *cc = c; 622*4887Schin str = cc + strlen(sb.str[0]); 623*4887Schin } 624*4887Schin else if(!sftell(tmp2)) 625*4887Schin { /* not successfull */ 626*4887Schin c = *cp; 627*4887Schin *cp = '\0'; 628*4887Schin errormsg(SH_DICT, ERROR_ERROR, 629*4887Schin "%s%s: substitution failed", 630*4887Schin (flag & HIST_QUICKSUBST) ? ":s" : "", 631*4887Schin evp); 632*4887Schin *cp = c; 633*4887Schin DONE(); 634*4887Schin } 635*4887Schin /* loop if g modifier specified */ 636*4887Schin if(!cc || !(flag & HIST_GLOBALSUBST)) 637*4887Schin flag &= ~HIST_SUBSTITUTE; 638*4887Schin } 639*4887Schin /* output rest of line */ 640*4887Schin sfputr(tmp2, str, -1); 641*4887Schin if(*cp) 642*4887Schin cp--; 643*4887Schin } 644*4887Schin 645*4887Schin if(sftell(tmp2)) 646*4887Schin { /* if any substitions done, swap buffers */ 647*4887Schin if(wm != tmp) 648*4887Schin sfclose(tmp); 649*4887Schin tmp = tmp2; 650*4887Schin tmp2 = 0; 651*4887Schin } 652*4887Schin cc = 0; 653*4887Schin if(*cp) 654*4887Schin cp++; 655*4887Schin } 656*4887Schin 657*4887Schin /* flush temporary buffer to stack */ 658*4887Schin if(tmp) 659*4887Schin { 660*4887Schin sfseek(tmp, 0, SEEK_SET); 661*4887Schin 662*4887Schin if(flag & HIST_QUOTE) 663*4887Schin stakputc('\''); 664*4887Schin 665*4887Schin while((c = sfgetc(tmp)) > 0) 666*4887Schin { 667*4887Schin if(isspace(c)) 668*4887Schin { 669*4887Schin flag = flag & ~HIST_NEWLINE; 670*4887Schin 671*4887Schin /* squash white space to either a 672*4887Schin blank or a newline */ 673*4887Schin do 674*4887Schin flag |= (c == '\n' ? HIST_NEWLINE : 0); 675*4887Schin while((c = sfgetc(tmp)) > 0 && isspace(c)); 676*4887Schin 677*4887Schin sfungetc(tmp, c); 678*4887Schin 679*4887Schin c = (flag & HIST_NEWLINE) ? '\n' : ' '; 680*4887Schin 681*4887Schin if(flag & HIST_QUOTE_BR) 682*4887Schin { 683*4887Schin stakputc('\''); 684*4887Schin stakputc(c); 685*4887Schin stakputc('\''); 686*4887Schin } 687*4887Schin else 688*4887Schin stakputc(c); 689*4887Schin } 690*4887Schin else if((c == '\'') && (flag & HIST_QUOTE)) 691*4887Schin { 692*4887Schin stakputc('\''); 693*4887Schin stakputc('\\'); 694*4887Schin stakputc(c); 695*4887Schin stakputc('\''); 696*4887Schin } 697*4887Schin else 698*4887Schin stakputc(c); 699*4887Schin } 700*4887Schin if(flag & HIST_QUOTE) 701*4887Schin stakputc('\''); 702*4887Schin } 703*4887Schin } 704*4887Schin 705*4887Schin stakputc('\0'); 706*4887Schin 707*4887Schin done: 708*4887Schin if(cc && (flag&HIST_HASH)) 709*4887Schin { 710*4887Schin /* close !# temp file */ 711*4887Schin sfclose(ref); 712*4887Schin free(cc); 713*4887Schin cc = 0; 714*4887Schin } 715*4887Schin 716*4887Schin /* error? */ 717*4887Schin if(staktell() && !(flag & HIST_ERROR)) 718*4887Schin *xp = strdup(stakfreeze(1)); 719*4887Schin 720*4887Schin /* restore shell stack */ 721*4887Schin if(off) 722*4887Schin stakset(sp,off); 723*4887Schin else 724*4887Schin stakseek(0); 725*4887Schin 726*4887Schin /* drop temporary files */ 727*4887Schin 728*4887Schin if(tmp && tmp != wm) 729*4887Schin sfclose(tmp); 730*4887Schin if(tmp2) 731*4887Schin sfclose(tmp2); 732*4887Schin 733*4887Schin return (flag & HIST_ERROR ? HIST_ERROR : flag & HIST_FLAG_RETURN_MASK); 734*4887Schin } 735*4887Schin 736*4887Schin #endif 737