/* gsar.c ***************************************** UPDATED: 940228.13:16 TT * * Description : General Search And Replace utility (GSAR !) * Author : Tormod Tjaberg & Hans Peter Verne * * Copyright (C) 1992-94 Tormod Tjaberg & Hans Peter Verne, * This is free software, distributed under the terms of the * GNU General Public License. For details see the file COPYING * * * Changes: * 940117 : 1.07 Bugfix see gsarbmg.c * 930328 : 1.06 GetArgs now uses GetOpt, context length can be specified on * the command line. Setvbuf error is no longer fatal. * 930127 : 1.05 filter implemented, setvbuf used to speed up disk I/O, * + bugfixes, see gsarbmg.c * 921122 : 1.04 implemented signal handling. * 920610 : 1.03 correct filename is now passed in OneSearchReplace * 920404 : 1.02 BMG routines use compile time generated table instead of * filling it in at run time * 920314 : simplified file buffer read --> 1 argument less, ta' HPV * 920310 : BMG search & replace now functioning speed increase by 300% * in search and replace and nearly 700% in searching!! * 920226 : temporary name is now really temporary * 920223 : now able to enter hexadecimal numbers on the command line, UD * DU parameters working. * 920203 : main program working all parsing seems to work * */ #include #include #include #include #include #include #include #include #include #include "comp_dep.h" #include "arg_func.h" #include "gsar.h" /* Need setmode to turn my streams into binary Under MS-DOS */ #ifdef MSDOS #include #include #endif /* Clever little option mimic wildcard expansion from * shell ONLY available with the Zortech compiler ver 3.0x and upwards */ #if defined(__ZTC__) #include WILDCARDS #endif /* function prototypes internal to this module */ void ShowLicence(void); void Abort(char *, ...); void CtrlBreak(int); char *DupStr(char *); char *ExtractPathFromFSpec(char *); char *TmpName(char *, char *); void DumpBuffer(char *, unsigned short, unsigned char); unsigned short GetPattern(char *, char *); int GetArgs(int , char *[]); void StreamSearchReplace(void); void FileSearch(void); void OneSearchReplace(void); void SearchReplace(void); int main(int , char *[]); /* Macro to determine if char is a path or drive delimiter */ #define IS_PATH_DELIM(c) ((c) == '\\' || (c) == ':' || (c) == '/') #define LF 0xA #define CR 0xD #define INP_BUF_SIZ 0x4000 /* Input buffer used by setvbuf */ unsigned short nItemsSearch = 0; unsigned short nItemsReplace = 0; /* if the flag below is set we are not allowed to interrupt ! */ static sig_atomic_t fCriticalPart = 0; OUTPUT_CTRL Ctrl; char **pFileList; /* list of files found on the command line */ char *pOutFile = NULL; /* current output file name make sure */ /* it's NULL so we won't delete garbage */ char SearchBuf[PAT_BUFSIZ] = ""; char ReplaceBuf[PAT_BUFSIZ] = ""; char fSearchReplace = 0; /* default to a search initially */ char fFolded = 0; /* fold pattern ie. ignore case */ char fOverWrite = 0; /* overwrite input file */ char fForce = 0; /* force overwrite of existing output file */ char fBuffers = 0; /* just display search & replace buffers */ char fFilter = 0; /* make GSAR act like a filter */ /* Usage text and GNU licence information */ char *Usage[] = { "gsar ver 1.07 -- Copyright (C) 1992-94 Tormod Tjaberg & Hans Peter Verne", "", "Usage: gsar [options] [infile(s)] [outfile]", "Options are:", "-s Search string ", "-r[string] Replace string. Use '-r' to delete the search string from the file", "-i Ignore case difference when comparing strings", "-B just display search & replace Buffers", "-f Force overwrite of an existing output file", "-o Overwrite the existing input file", "-c[n] show textual Context of match, 'n' is number of bytes in context", "-x[n] show context as a heX dump, 'n' is number of bytes in context", "-b display Byte offsets of matches in file", "-l only List filespec and number of matches (default)", "-h suppress display of filespec when displaying context or offsets", "-du convert a DOS ASCII file to UNIX (strips carriage return)", "-ud convert a UNIX ASCII file to DOS (adds carriage return)", "-F 'Filter' mode, input from stdin and eventual output to stdout", "-G display the GNU General Public Licence", "", "Ctrl characters can be entered by using a ':' in the string followed by the", "ASCII value of the character. The value is entered using ':' followed by three", "decimal digits or ':x' followed by two hex numbers. To enter ':' use '::'", NULL }; char *Licence[] = { "This program is free software; you can redistribute it and/or modify", "it under the terms of the GNU General Public License as published by", "the Free Software Foundation; either version 2 of the License, or", "(at your option) any later version.", "", "This program is distributed in the hope that it will be useful,", "but WITHOUT ANY WARRANTY; without even the implied warranty of", "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the", "GNU General Public License for more details.", "", "You should have received a copy of the GNU General Public License", "along with this program; if not, write to the Free Software", "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.", NULL }; /* ShowLicence () * * Display the GNU General Public License */ void ShowLicence(void) { int i = 0; while (Licence[i] != NULL) { fputs(Licence[i++], Ctrl.fpMsg); fputc('\n', Ctrl.fpMsg); } } /* Abort () * * Input : Message to be displayed with the format of standard printf! * Returns: Nothing, but instead it exits to operating system * * Terminate program, return to OS with a suitable return value * exit() will close all open files. All fatal errors are written * to stderr. */ void Abort(char *pMessage, ...) { va_list argp; fprintf(stderr, "gsar: "); va_start(argp, pMessage); vfprintf(stderr ,pMessage, argp); va_end(argp); fprintf(stderr, "\n"); exit(EXIT_FAILURE); /* exit & tell operating system that we've failed */ } /* CtrlBreak () * * Ctrl-Break handler. Returns to operating system */ void CtrlBreak(int Val) { if (fCriticalPart) return; signal(SIGINT,SIG_IGN); /* Before we die try to clean up as much as possible * make sure we don't delete stdout... */ if (fSearchReplace && pOutFile != NULL && !fFilter) remove(pOutFile); exit(EXIT_FAILURE); /* exit & tell operating system that we've failed */ } /* DupStr() * * Input : pActStr - pointer to actual string which is to be duplicated * Returns: pDupStr - pointer to malloc'd duplicate * NULL - Out of memory * * Duplicates a string by malloc'ing and then doing a strcpy */ char *DupStr(char *pActStr) { register char *pDupStr; pDupStr = (char *) malloc(strlen(pActStr) + 1); if (pDupStr) strcpy(pDupStr,pActStr); return pDupStr; } /* ExtractPathFromFSpec() * * Input : filespec - a filespec which contains a path and a filename * Returns: savfilespec - pointer to malloc'd string with path * * Extracts the path from a filespec */ char *ExtractPathFromFSpec(register char *filespec) { register char *p; register char *savfilespec; savfilespec = DupStr(filespec); /* avoid destroying the original */ /* Start at end of string and back up till we find the beginning */ /* of the filename or a path. */ for (p = savfilespec + strlen(savfilespec); p != savfilespec && !IS_PATH_DELIM(*(p - 1)); p--) ; *p = '\0'; return savfilespec; } /* TmpName * * Input : pPath - which has to be properly terminated * pPrefix - tmp filename prefix maximum 4 chars * Returns: pointer to static buffer * NULL - unable to generate tmp file name * * Generates a temporary filename by combining an optional path, * a prefix ,a number between 0 & SHRT_MAX and the suffix '.tmp'. */ char *TmpName(char *pPath, char *pPrefix) { static char FileSpec[FILENAME_MAX]; static char Seed = 0; static short TmpNum; char *pDigits; unsigned short i; struct stat buf; *FileSpec = '\0'; /* Null terminate the buffer so strcat will always work */ if (!Seed) /* if first time through */ { Seed++; TmpNum = -1; /* since we increment this will make the first number 0000 */ } if (pPath != NULL) strcpy(FileSpec,pPath); /* first the path */ if (pPrefix != NULL) strcat(FileSpec,pPrefix); /* then the prefix */ /* pointer to start of digits in filespec */ pDigits = FileSpec + strlen(FileSpec); for (i = 0; i <= SHRT_MAX; i++) /* try SHRT_MAX times */ { TmpNum++; /* TmpNum is static ! */ sprintf(pDigits ,"%04x",TmpNum); /* convert to string */ strcat(FileSpec,".tmp"); /* add the suffix */ if (stat(FileSpec, &buf) != 0) return FileSpec; /* file not found, success */ *(pDigits) = '\0'; /* pre_XXXX --> pre_ */ } /* unable to create a temporary file ! more than SHRT_MAX files !!*/ return NULL; } /* DumpBuffer() * * Input : pBuffer pointer to character buffer * nItem number of items in buffer * base 1 = hex , 0 = ASCII * Returns : nothing * * Prints the contents of a buffer in either ASCII or HEX. The * nItems variable is needed since we don't stop on a NUL */ void DumpBuffer(char *pBuffer, unsigned short nItem , unsigned char Base) { unsigned short i; /* loop counter */ if (!Base) { for (i = 0; i < nItem; i++) { #ifdef MSDOS /* MSDOS can display all characters except CTRL chars */ if (!iscntrl((int) *pBuffer)) #else /* its __UNIX__ */ if (isprint((int) *pBuffer)) #endif fputc(*pBuffer, Ctrl.fpMsg); else fputc('.', Ctrl.fpMsg); pBuffer++; } } else for (i = 0; i < nItem; i++) fprintf(Ctrl.fpMsg,"%02x ", (unsigned char) *pBuffer++); fputc('\n',Ctrl.fpMsg); } /* GetPattern() * * Input : pArgStr - pointer to command line search or replace string * pBuffer - pointer to buffer to store the parsed string * Returns : actual length of parsed string * * Takes a string & transforms it into the correct internal representation * ie :070OO -> FOO or :x46OO -> FOO */ unsigned short GetPattern(char *pArgStr, char *pBuffer) { char number[4]; /* array to store number to convert */ char *pEnd; /* pointer to the result of the string to long conversion */ char *pStart; /* pointer to the start of the buffer */ pStart = pBuffer; number[3] = '\0'; /* make sure number buffer is terminated */ while (*pArgStr != '\0') { if (*pArgStr != ':') *pBuffer++ = *pArgStr++; /* just copy the buffer */ else { if (*(pArgStr+1) == ':') /* check for multiple :'s */ { *pBuffer++ = *pArgStr; pArgStr = pArgStr + 2; /* position ourselves past the 2 :'s */ } else { /* have we actually got three chars to copy ? */ if (strlen(++pArgStr) > 2) { memcpy(number,pArgStr,3); /* negative numbers are silently ignored*/ pArgStr += 3; /* jump past this number in the argument string */ if ((char) tolower(number[0]) == 'x') { /* perform hex conversion */ number[0] = '0'; /* replace the x with a zero */ *pBuffer++ = (char) strtol(number, &pEnd, 16); if (pEnd != number+3) Abort("command error, not a valid hexadecimal number : %s\n",&number[1]); } else /* decimal conversion */ { *pBuffer++ = (char) strtol(number, &pEnd, 10); if (pEnd != number+3) Abort("command error, not a valid decimal number : %s\n",number); } } else /* strlen(pArgStr) < 3 so abort */ Abort("command error, a single colon must be followed by three decimal digits or an 'x' followed by 2 hexadecimal numbers\n"); } } if (pBuffer - pStart > PAT_BUFSIZ) Abort("command error, length of search or replace buffer must not exceed %d chars",PAT_BUFSIZ); } /* while */ return pBuffer - pStart; /* actual length of buffer */ } /* GetArgs() * * Input : argc - number of arguments on the command line * argv - pointer to the argument vevtor * Returns: number of actual filenames found * * Parses the command line & returns number of filenames found */ int GetArgs(int argc, char *argv[]) { int i = 0; int j = 0; /* counters */ int c; /* switch char */ char *pEnd; /* the result of the string to long conversion */ long longVal; pFileList = NULL; if (argc > 1) while ((c = GetOpt(argc, argv, "|s:r::iBfoc::x::blhd::u::FG")) != EOF) { switch (c) { case '|': /* create a vector of file pointers */ pFileList = (char **) realloc (pFileList, (j + 2) * sizeof(char *)); pFileList[j++] = pOptArg; pFileList[j] = NULL; /* The C-standard specifies that argv[argc] == NULL */ break; case MISSING_ARG: Abort("command error, the '%c' option requires an argument", (unsigned char) CurOpt); break; case BAD_CHAR: Abort("command error, unknown option '%c'. Type 'gsar' by itself help", (unsigned char) CurOpt); break; case MISSING_OPT: i = 0; while (Usage[i] != NULL) { fputs(Usage[i++], Ctrl.fpMsg); fputc('\n', Ctrl.fpMsg); } exit(EXIT_SUCCESS); break; case 's': nItemsSearch = GetPattern(pOptArg, SearchBuf); break; case 'r': if (pOptArg == NULL) nItemsReplace = 0; /* we are to remove occurrence of -s */ else nItemsReplace = GetPattern(pOptArg, ReplaceBuf); fSearchReplace = 1; /* only search & replace if -r option */ break; case 'u': /* replace LF with CR LF */ if (pOptArg == NULL) pOptArg = ""; if (!(*pOptArg == 'd' && *(pOptArg + 1) == '\0')) Abort("command error, unknown option 'u%s'. Type 'gsar' by itself for help", pOptArg); fSearchReplace = 1; nItemsSearch = 1; SearchBuf[0] = LF; nItemsReplace = 2; ReplaceBuf[0] = CR; ReplaceBuf[1] = LF; break; case 'd': /* replace CR LF with LF */ if (pOptArg == NULL) pOptArg = ""; if (!(*pOptArg == 'u' && *(pOptArg + 1) == '\0')) Abort("command error, unknown option 'd%s'. Type 'gsar' by itself for help", pOptArg); fSearchReplace = 1; nItemsSearch = 2; SearchBuf[0] = CR; SearchBuf[1] = LF; nItemsReplace = 1; ReplaceBuf[0] = LF; break; case 'i': fFolded = 1; break; case 'G': ShowLicence(); exit(EXIT_SUCCESS); break; case 'l': Ctrl.fTextual = 0; /* return to terse display */ Ctrl.fHex = 0; Ctrl.fByteOffset = 0; break; case 'o': fOverWrite = 1; break; case 'f': fForce = 1; break; case 'F': fFilter = 1; break; case 'B': fBuffers = 1; break; case 'b': Ctrl.fByteOffset = 1; /* display file offset */ break; case 'h': Ctrl.fFileSpec = 0; /* turn off filespec */ break; case 'x': Ctrl.fTextual = -1; /* signal fall through */ Ctrl.fHex = 1; /* display context in hex */ case 'c': if (Ctrl.fTextual == -1) /* entered through case 'x' */ Ctrl.fTextual = 0; /* reset sentinel value */ else { Ctrl.fHex = 0; /* entered through case 'c' */ Ctrl.fTextual = 1; /* display textual context */ } if (pOptArg == NULL) Ctrl.Context = (Ctrl.fHex == 1) ? HEX_CONTEXT : TXT_CONTEXT; else { longVal = strtol(pOptArg, &pEnd, 10); if (*pEnd != '\0') Abort("command error, invalid number : %s", pOptArg); if (longVal < 16 || longVal > BUFSIZ) Abort("command error, context size must be in the range 16 to %d", BUFSIZ); if (longVal > USHRT_MAX) Ctrl.Context = USHRT_MAX; else Ctrl.Context = (unsigned short) longVal; } break; default: Abort("internal error, option '%c' not handled in switch", (unsigned char) CurOpt); break; } } else { i = 0; while (Usage[i] != NULL) { fputs(Usage[i++], Ctrl.fpMsg); fputc('\n', Ctrl.fpMsg); } exit(EXIT_SUCCESS); } Ctrl.fVerbose = (Ctrl.fTextual || Ctrl.fHex || Ctrl.fByteOffset) ? 1 : 0; return j; } /* StreamSearchReplace() * * Perform search or replace using stdin and stdout ie as a 'filter' * When gsar operates as a filter all output ie context, byte filenames * etc are all sent to stderr. */ void StreamSearchReplace(void) { long nMatches; Ctrl.pInputFile = "stdin"; /* proper filename */ Ctrl.fpMsg = stderr; /* redirect messages */ #ifdef MSDOS /* when MSDOS operates on streams it translates characters and terminates * input and output when it encounters a CTRL Z. The code below makes * MSDOS treat the streams as binary. Stdin cannot be used since MSDOS * ignores the CTRL-Z. If you pipe a binary stream to MSDOS under some * compilers you might get a write error (BCC, Zortech) */ if (isatty(fileno(stdin))) Abort("error, input from tty is not supported under MSDOS"); setmode(fileno(stdin), O_BINARY); setmode(fileno(stdout), O_BINARY); #endif Ctrl.fpIn = stdin; /* input from stdin */ Ctrl.fpOut = stdout; /* output to stdout */ if (!fSearchReplace) { nMatches = BMGSearch(&Ctrl); if (nMatches > 0) fprintf(Ctrl.fpMsg,"%s : %ld match%s found\n" , Ctrl.pInputFile,nMatches, (nMatches == 1) ? "" : "es"); } else { nMatches = BMGSearchReplace(&Ctrl,ReplaceBuf,nItemsReplace); if (nMatches == -1) /* error in writing file */ Abort("error in writing file to stdout\n"); else if (nMatches > 1) { fflush(Ctrl.fpOut); fprintf(Ctrl.fpMsg,"%s : %ld occurrences changed\n", Ctrl.pInputFile,nMatches); } } } /* FileSearch() * * Performs a BMG search on one or multiple files. The files * to process are found in pFileList. Files that cannot be * opened are just ignored. */ void FileSearch(void) { long nMatches; while (*pFileList != NULL) { if ((Ctrl.fpIn = fopen(*pFileList, "rb")) == NULL) { fprintf(Ctrl.fpMsg, "gsar: unable to open input file '%s'\n", *pFileList); pFileList++; continue; } Ctrl.pInputFile = *pFileList; Ctrl.fpMsg = stdout; #if defined(MSDOS) && !defined(__ZTC__) /* Don't use setvbuf if we're compiling under Zortech */ if (setvbuf(Ctrl.fpIn, NULL, _IOFBF, INP_BUF_SIZ) != 0) fprintf(Ctrl.fpMsg, "warning, unable to set up buffering for input file\n"); #endif nMatches = BMGSearch(&Ctrl); fclose(Ctrl.fpIn); if (nMatches > 0) fprintf(Ctrl.fpMsg,"%s : %ld match%s found\n", *pFileList, nMatches, (nMatches == 1) ? "" : "es"); pFileList++; } } /* OneSearchReplace() * * Performs a search and replace on a specific outputfile */ void OneSearchReplace(void) { long nMatches = 0; struct stat StatBuf; /* struct so we can access file information */ Ctrl.fpMsg = stdout; /* redirect messages */ Ctrl.pInputFile = NULL; /* initially we haven't found a file */ /* find the input and output file */ while (*pFileList != NULL) { if (Ctrl.pInputFile == NULL) Ctrl.pInputFile = *pFileList; else if (pOutFile == NULL) pOutFile = *pFileList; pFileList++; } if ((Ctrl.fpIn = fopen(Ctrl.pInputFile ,"rb")) == NULL) Abort("error, unable to open input file '%s' ", Ctrl.pInputFile); if ((stat(pOutFile,&StatBuf)) == 0 && !fForce) /* stat got the info ie. file exists */ Abort("error, output file '%s' already exists. Use the 'f' option to force overwrite",pOutFile); if ((Ctrl.fpOut = fopen(pOutFile,"wb")) == NULL) Abort("error, unable to open output file '%s' ",pOutFile); #if defined(MSDOS) && !defined(__ZTC__) /* Don't use setvbuf if we're compiling under Zortech */ if (setvbuf(Ctrl.fpIn, NULL, _IOFBF, INP_BUF_SIZ) != 0) fprintf(Ctrl.fpMsg, "warning, unable to set up buffering for input file"); if (setvbuf(Ctrl.fpOut, NULL, _IOFBF, INP_BUF_SIZ) != 0) fprintf(Ctrl.fpMsg, "warning, unable to set up buffering for output file"); #endif nMatches = BMGSearchReplace(&Ctrl,ReplaceBuf,nItemsReplace); fclose(Ctrl.fpIn); fclose(Ctrl.fpOut); fCriticalPart = 1; /* ignore interrupts here */ if (nMatches == -1) /* error in writing file */ { fprintf(Ctrl.fpMsg,"gsar: error in writing file '%s' - cleaning up\n",pOutFile); if (remove(pOutFile) != 0) Abort("error, unable to remove output file '%s'",pOutFile); exit(EXIT_FAILURE); /* exit & tell operating system that we've failed */ } if (nMatches == 0) { if (remove(pOutFile) != 0) Abort("error, unable to remove output file '%s'",pOutFile); } else fprintf(Ctrl.fpMsg,"%s : %ld occurrences changed\n",Ctrl.pInputFile,nMatches); /* Make sure the Ctrl-C handler does not delete a completly * processed output file */ pOutFile = NULL; fCriticalPart = 0; /* enable interrupts */ } /* SearchReplace() * * Performs a BMG search and replace on one or multiple files. The files * to process are found in pFileList. Files that cannot be opened * are ignored. */ void SearchReplace(void) { long nMatches; #ifdef __UNIX__ struct stat stat_buf; /* - some compilers mess up this... extern int chown(char *, uid_t, gid_t); extern int chmod(char *, mode_t); */ #endif Ctrl.fpMsg = stdout; /* redirect messages */ while (*pFileList != NULL) { Ctrl.pInputFile = *pFileList++; if ((Ctrl.fpIn = fopen(Ctrl.pInputFile,"rb")) == NULL) { fprintf(Ctrl.fpMsg,"gsar: unable to open input file '%s'\n",Ctrl.pInputFile); continue; } /* generate a temporary file name with prefix 'gsr_' */ pOutFile = ExtractPathFromFSpec(Ctrl.pInputFile); if ((pOutFile = TmpName(pOutFile, "gsr_")) == NULL) Abort("error, unable to create a temporary file name"); if ((Ctrl.fpOut = fopen(pOutFile,"wb")) == NULL) Abort("error, unable to open output file '%s' ",pOutFile); #ifdef __UNIX__ /* In Unix, we try to preserve the mode and id's of the file : */ if (stat(Ctrl.pInputFile, &stat_buf) != -1) { chown(pOutFile, stat_buf.st_uid, stat_buf.st_gid); chmod(pOutFile, stat_buf.st_mode); } /* We just ignore errors here ... (hpv) */ #endif #if defined(MSDOS) && !defined(__ZTC__) /* Don't use setvbuf if we're compiling under Zortech */ if (setvbuf(Ctrl.fpIn, NULL, _IOFBF, INP_BUF_SIZ) != 0) fprintf(Ctrl.fpMsg, "warning, unable to set up buffering for input file\n"); if (setvbuf(Ctrl.fpOut, NULL, _IOFBF, INP_BUF_SIZ) != 0) fprintf(Ctrl.fpMsg, "warning, unable to set up buffering for output file\n"); #endif nMatches = BMGSearchReplace(&Ctrl,ReplaceBuf, nItemsReplace); fclose(Ctrl.fpIn); fclose(Ctrl.fpOut); fCriticalPart = 1; /* ignore interrupts here */ if (nMatches == -1) /* error in writing file */ { fprintf(Ctrl.fpMsg,"gsar: error in writing file '%s' - cleaning up\n",pOutFile); if (remove(pOutFile) != 0) Abort("error, unable to remove output file '%s'",pOutFile); exit(EXIT_FAILURE); } if (nMatches == 0) { if (remove(pOutFile) != 0) Abort("error, unable to remove output file '%s'",pOutFile); } else { /* if we can't remove the input file delete the tmp output file */ if (remove(Ctrl.pInputFile) != 0) { fprintf(Ctrl.fpMsg,"gsar: error, unable to remove input file '%s' before rename (read-only ?)",Ctrl.pInputFile); if (remove(pOutFile) != 0) Abort("error, unable to remove output file '%s'",pOutFile); exit(EXIT_FAILURE); } if (rename(pOutFile, Ctrl.pInputFile) != 0) Abort("error, unable to rename file '%s' to '%s'",pOutFile,Ctrl.pInputFile); fprintf(Ctrl.fpMsg,"%s : %ld occurrences changed\n",Ctrl.pInputFile,nMatches); } fCriticalPart = 0; } } int main(int argc, char *argv[]) { int i; /* number of files on command line */ Ctrl.fFileSpec = 1; Ctrl.fpMsg = stdout; if(signal(SIGINT,CtrlBreak) == SIG_ERR) Abort("internal error, unable to set SIGINT"); /* parse command line arguments & set variables * i is number of files found */ i = GetArgs(argc, argv); /* perform different health & sanity checks */ if (nItemsSearch == 0) Abort("command error, no search string specified"); /* display search and replace buffers if any */ if (fBuffers) { fputc('\n',Ctrl.fpMsg); if (nItemsSearch > 0) { fprintf(Ctrl.fpMsg,"Search buffer (ASCII) : "); DumpBuffer(SearchBuf, nItemsSearch, 0); fprintf(Ctrl.fpMsg," (Hex) : "); DumpBuffer(SearchBuf, nItemsSearch, 1); fprintf(Ctrl.fpMsg,"\n"); } if (nItemsReplace > 0) { fprintf(Ctrl.fpMsg,"Replace buffer (ASCII) : "); DumpBuffer(ReplaceBuf, nItemsReplace, 0); fprintf(Ctrl.fpMsg," (Hex) : "); DumpBuffer(ReplaceBuf, nItemsReplace, 1); } return EXIT_SUCCESS; } if (i == 0 && !fFilter) Abort("command error, no input file name specified"); if (i != 2 && fForce && !fOverWrite) Abort("command error, two file names are required to use the 'f' option"); /* set up the search pattern once and for all */ BMGSetup(SearchBuf, (int) nItemsSearch, fFolded); if (fFilter) { if (fOverWrite || fForce) Abort("command error, the 'o' or 'f' option is meaningless in 'filter' mode"); StreamSearchReplace(); return EXIT_SUCCESS; } /* just perform a file search */ if (!fSearchReplace) { if (fOverWrite || fForce) Abort("command error, the 'o' or 'f' option is meaningless in 'search' mode"); FileSearch(); return EXIT_SUCCESS; } /* Do a search and replace with specific output file */ if (i == 2 && !fOverWrite && fSearchReplace) { OneSearchReplace(); return EXIT_SUCCESS; } if (!fOverWrite && fSearchReplace) Abort("command error, multiple search & replace requires the 'o' option"); if (fOverWrite && fSearchReplace) { if (fForce) Abort("command error, the 'f' option is meaningless in multiple search and replace"); SearchReplace(); return EXIT_SUCCESS; } return EXIT_SUCCESS; }