/*** replace.c - string replacement functions * * Copyright 1988, Microsoft Corporation * * Repalces funnel through these routines as follows: * * zreplace mreplace qreplace * \ | / * \ | / * \______ | ______/ * \|/ * v * doreplace * | * (fScan) * | * fDoReplace * / \ * / \ * patRpl simpleRpl (if a change is made) * \ / * \ / * ReplaceEdit * * Revision History: * 26-Nov-1991 mz Strip off near/far *************************************************************************/ #define NOVM #include "z.h" static flagType fQrpl = FALSE; /* TRUE => prompt for replacement */ static struct patType *patBuf = NULL; /* compiled pattern */ static int srchlen; /* length of textual search */ static unsigned MaxREStack; /* Elements in RE stack */ static RE_OPCODE ** REStack; /* Stack for REMatch */ /*** mreplace - multiple file search and replace * * Perform a search and replace across multiple files. Acts like qreplace, in * that the first instance the user is always asked. he may then say "replace * all". * * Input: * Standard editting function. * * Output: * Returns TRUE on successfull replacement. * *************************************************************************/ flagType mreplace ( CMDDATA argData, ARG * pArg, flagType fMeta ) { return doreplace (TRUE, pArg, fMeta, TRUE); argData; } /*** zreplace & qreplace - perform search/replace * * Editting functions which implement search & replace. qreplace prompts, * zreplace does not. * * Input: * Standard editting function parameters. * * Output: * Returns * *************************************************************************/ flagType zreplace ( CMDDATA argData, ARG * pArg, flagType fMeta ) { return doreplace (FALSE, pArg, fMeta, FALSE); argData; } flagType qreplace ( CMDDATA argData, ARG * pArg, flagType fMeta ) { return doreplace (TRUE, pArg, fMeta, FALSE); argData; } /*** doreplace - perform search-replace * * Performs the actual search and replace argument verification, set up and * high level control. * * Input: * fQuery = TRUE if a query replace * pArg = pArg of parent function * fMeta = fMeta of parent function * fFiles = TRUE is multiple file search and replace. * * Output: * Returns ..... * * Exceptions: * * Notes: * *************************************************************************/ flagType doreplace ( flagType fQuery, ARG * pArg, flagType fMeta, flagType fFiles ) { buffer bufFn; /* filename buffer */ fl flStart; char *p; PCMD pCmd; PFILE pFileSave; /* file to save as top of heap */ p = "Query Search string: "; if (!fQuery) { p += 6; } fQrpl = fQuery; fSrchCasePrev = fMeta ? (flagType)!fSrchCaseSwit : fSrchCaseSwit; Display (); cRepl = 0; /* * If not menu-driven, ask the user for a search string. If none is entered, * we're done. */ if ((pCmd = getstring (srcbuf, sizeof(srcbuf), p, NULL, GS_NEWLINE | GS_INITIAL)) == NULL || (PVOID)pCmd->func == (PVOID)cancel) { return FALSE; } if (srcbuf[0] == '\0') { return FALSE; } /* * If RE search to take place, the compile the expression. */ if (pArg->arg.nullarg.cArg == 2) { if (patBuf != NULL) { FREE ((char *) patBuf); patBuf = NULL; } patBuf = RECompile (srcbuf, fSrchCaseSwit, (flagType)!fUnixRE); if (patBuf == NULL) { printerror ((RESize == -1) ? "Invalid pattern" : "Not enough memory for pattern"); return FALSE; } fRplRePrev = TRUE; } else { fRplRePrev = FALSE; } /* * If not menu driven, ask the user for a replacement string. Confirm the * entry of a null string. Error check the replacement if an RE search. */ if ((pCmd = getstring (rplbuf, sizeof(rplbuf), "Replace string: ", NULL, GS_NEWLINE | GS_INITIAL)) == NULL || (PVOID)pCmd->func == (PVOID)cancel) { return FALSE; } if (rplbuf[0] == 0) { if (!confirm ("Empty replacement string, confirm: ", NULL)) { return FALSE; } } if (fRplRePrev && !RETranslate (patBuf, rplbuf, scanreal)) { printerror ("Invalid replacement pattern"); return FALSE; } srchlen = strlen (srcbuf); switch (pArg->argType) { case NOARG: case NULLARG: setAllScan (TRUE); break; case LINEARG: rnScan.flFirst.col = 0; rnScan.flLast.col = sizeof(linebuf)-1; rnScan.flFirst.lin = pArg->arg.linearg.yStart; rnScan.flLast.lin = pArg->arg.linearg.yEnd; break; case BOXARG: rnScan.flFirst.col = pArg->arg.boxarg.xLeft; rnScan.flLast.col = pArg->arg.boxarg.xRight; rnScan.flFirst.lin = pArg->arg.boxarg.yTop; rnScan.flLast.lin = pArg->arg.boxarg.yBottom; break; case STREAMARG: if (pArg->arg.streamarg.yStart == pArg->arg.streamarg.yEnd) { rnScan.flFirst.col = pArg->arg.streamarg.xStart; rnScan.flLast.col = pArg->arg.streamarg.xEnd; rnScan.flFirst.lin = pArg->arg.streamarg.yStart; rnScan.flLast.lin = pArg->arg.streamarg.yEnd; } else { rnScan.flFirst.col = 0; /* Do all but last line first */ rnScan.flLast.col = sizeof(linebuf)-1; rnScan.flFirst.lin = pArg->arg.streamarg.yStart; rnScan.flLast.lin = pArg->arg.streamarg.yEnd - 1; flStart.col = pArg->arg.streamarg.xStart - 1; flStart.lin = rnScan.flFirst.lin; fScan (flStart, fDoReplace , TRUE, fSrchWrapSwit); rnScan.flLast.col = pArg->arg.streamarg.xEnd; rnScan.flFirst.lin = ++rnScan.flLast.lin; } } flStart.col = rnScan.flFirst.col-1; flStart.lin = rnScan.flFirst.lin; if (fRplRePrev) { MaxREStack = 512; REStack = (RE_OPCODE **)ZEROMALLOC (MaxREStack * sizeof(*REStack)); } if (fFiles) { /* * Get the list handle, and initialize to start at the head of the list. * Attempt to read each file. */ if (pCmd = GetListHandle ("mgreplist", TRUE)) { pFileSave = pFileHead; p = ScanList (pCmd, TRUE); while (p) { CanonFilename (p, bufFn); forfile (bufFn, A_ALL, mrepl1file, &p); p = ScanList (NULL, TRUE); if (fCtrlc) { return FALSE; } } pFileToTop (pFileSave); dispmsg (0); } } else { fScan (flStart, fDoReplace , TRUE, fSrchWrapSwit); } if (fRplRePrev) { FREE (REStack); } domessage ("%d occurrences replaced", cRepl); return (flagType)(cRepl != 0); } /*** mrepl1file - search/replace the contents of 1 file. * * Searches through one file for stuff. * * Input: * * Output: * Returns ..... * * Exceptions: * * Notes: * *************************************************************************/ void mrepl1file ( char *szGrepFile, struct findType *pfbuf, void *dummy ) { flagType fDiscard; /* discard the file read? */ fl flGrep; /* ptr to current grep loc */ int cReplBefore; /* number of matches before */ PFILE pFileGrep; /* file to be grepped */ assert (szGrepFile); assert (_pinschk(pInsCur)); if (fCtrlc) { return; } /* * If we can get a handle to the file, then it's alread in the list, and we * should not discard it when done. If it is not in the list, we read it in, * but we'll discard it, unless something is found there. */ if (!(pFileGrep = FileNameToHandle (szGrepFile, szGrepFile))) { pFileGrep = AddFile (szGrepFile); SETFLAG (FLAGS (pFileGrep), REFRESH); fDiscard = TRUE; } else { fDiscard = FALSE; } assert (_pinschk(pInsCur)); /* * If the file needs to be physically read, do so. */ if ((FLAGS (pFileGrep) & (REFRESH | REAL)) != REAL) { FileRead (pFileGrep->pName, pFileGrep, FALSE); RSETFLAG (FLAGS(pFileGrep), REFRESH); } dispmsg (MSG_SCANFILE, szGrepFile); pFileToTop (pFileGrep); /* * run through the file, searching and replacing as we go. */ cReplBefore = cRepl; setAllScan (FALSE); flGrep.col = rnScan.flFirst.col-1; flGrep.lin = rnScan.flFirst.lin; fScan (flGrep, fDoReplace, TRUE, FALSE); /* * If the search was not successfull, discard the file, if needed, and move * to the next. */ if (cReplBefore == cRepl) { if (fDiscard) { RemoveFile (pFileGrep); } } else { AutoSaveFile (pFileGrep); } assert (_pinschk(pInsCur)); pfbuf; dummy; } /*** fDoReplace - called by fScan as file is scanned. * * Purpose: * * Input: * * Output: * Returns ..... * * Exceptions: * * Notes: * *************************************************************************/ flagType fDoReplace ( void ) { int c; char *p = pLog (scanreal, flScan.col, TRUE); if (fRplRePrev) { int rem; flagType fAgain = TRUE; do { switch (rem = REMatch (patBuf, scanreal, p, REStack, MaxREStack, TRUE)) { case REM_NOMATCH: flScan.col = scanlen; return FALSE; case REM_STKOVR: MaxREStack += 128; REStack = (RE_OPCODE **)ZEROREALLOC ((char *)REStack, MaxREStack * sizeof(*REStack)); break; default: printerror ("Internal Error: RE error %d, line %ld", rem, flScan.lin); case REM_MATCH: fAgain = FALSE; break; } } while (fAgain); c = colPhys (scanreal, REStart (patBuf)); srchlen = RELength (patBuf, 0); if (c + srchlen - 1 > scanlen) { return FALSE; } flScan.col = c; } else { if ( (*(fSrchCasePrev ? strncmp : _strnicmp)) (srcbuf, p, srchlen)) { return FALSE; } if (flScan.col + srchlen - 1 > scanlen) { return FALSE; } } if (fQrpl) { ClearHiLite (pFileHead, TRUE); Display(); cursorfl (flScan); HighLight (flScan.col, flScan.lin, flScan.col+srchlen-1, flScan.lin); Display (); c = askuser ('n', 'a', "Replace this occurrence? (Yes/No/All/Quit): ", NULL); ClearHiLite (pFileHead, TRUE); redraw (pFileHead, flScan.lin, flScan.lin); RSETFLAG (fDisplay, RHIGH); switch (c) { case -1: case 'q': fCtrlc = TRUE; return TRUE; case 'n': return FALSE; case 'a': dispmsg(0); /* clear dialog line */ fQrpl = FALSE; break; } } if (fRplRePrev) { patRpl (); } else { simpleRpl (p); } return FALSE; } /*** simpleRpl & patRpl - perform textual replacement * * Purpose: * * Input: * * Output: * Returns ..... * * Exceptions: * * Notes: * *************************************************************************/ void simpleRpl ( char *p ) { ReplaceEdit (p, rplbuf); } void patRpl ( void ) { buffer txt; RETranslate (patBuf, rplbuf, txt); ReplaceEdit (REStart (patBuf), txt); } /*** ReplaceEdit - perform replacement in a line of text * * Purpose: * * Input: * p = pointer to beginning of match within scanreal * rpl = text of replacement * * Output: * Returns nothing * *************************************************************************/ void ReplaceEdit ( char *p, char *rpl ) { int c; /* length of replacement string */ /* if the len of line - len of search + len of replacement string < BUFLEN * then we can make the replacement. Otherwise we flag an error and * advance to the next line */ c = strlen (rpl); if (cbLog (scanreal) + c - srchlen < sizeof(linebuf)) { /* open up a space in the buffer at the spot where the string was * found. Move the characters starting at the END of the match to * the point after where the END of the replacement is. */ memmove ((char*) &p[c], (char *) &p[srchlen], sizeof(linebuf) - flScan.col - c); memmove ((char *) p, (char *) rpl, c); PutLine (flScan.lin, scanreal, pFileHead); /* if search length != 0 or replace length != 0, skip over replacement */ if (srchlen != 0 || c != 0) { flScan.col += c - 1; } // // Adjust scan len to account for the fact that the end of the region being // scanned may have moved as a result of the replacement. Adjust by the // replacement difference, and bound by 0 and the length of the line. // scanlen = max (0, min (scanlen + c - srchlen, cbLog(scanreal))); cRepl++; } else { printerror ("line %ld too long; replacement skipped", flScan.lin+1); flScan.col = scanlen; } }