home *** CD-ROM | disk | FTP | other *** search
- /* gsar.c ***************************************** updated: 960808.09:28 TT
- *
- * Description : General Search And Replace utility (gsar)
- * Author : Tormod Tjaberg & Hans Peter Verne
- *
- * Copyright (C) 1992-96 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
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <stdarg.h>
- #include <limits.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include "comp_dep.h"
- #include "arg_func.h"
- #include "gsar.h"
-
- #define PROGRAM_NAME "gsar"
- #define PROGRAM_VER "1.10"
-
- /* Need setmode to turn my streams into binary Under MS-DOS
- */
- #ifdef MSDOS
- #include <fcntl.h>
- #include <io.h>
- #endif
-
- #ifdef __UNIX__
- #include <unistd.h>
- #endif
-
- /* Clever little option mimic wildcard expansion from
- * shell ONLY available with the Zortech compiler ver 3.0x and upwards
- * And in the Symantec compiler though it's been renamed
- */
- #if defined(__ZTC__)
- #include <dos.h>
- #ifdef WILDCARDS
- WILDCARDS
- #endif
- #ifdef EXPAND_WILDCARDS
- EXPAND_WILDCARDS
- #endif
- #endif
-
- /* 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 SETVBUF_SIZ 0x4000
-
- unsigned short nItemsSearch = 0;
- unsigned short nItemsReplace = 0;
-
- /* if the flag below is set we are not allowed to interrupt!
- */
- #if defined(__UNIX__)
- /* SunOS does not have this definition, so play it safe */
- static int fCriticalPart = 0;
- #else
- static sig_atomic_t fCriticalPart = 0;
- #endif
-
- 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 */
- 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[] =
- {
- PROGRAM_NAME ", ver " PROGRAM_VER " -- Copyright (C) 1992-96 Tormod Tjaberg & Hans Peter Verne",
- "",
- "Usage: gsar [options] [infile(s)] [outfile]",
- "Options are:",
- "-s<string> 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 may 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
- };
-
-
- /* 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);
- }
- }
-
-
- /* 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 */
- }
-
-
- /* 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 */
- }
-
-
- /* 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;
- }
-
-
- /* 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;
- }
-
-
- /* 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;
- }
-
-
- /* 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);
- }
-
-
- /* 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 */
- }
-
-
- /* 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 */
- #if defined(__UNIX__)
- /* SunOS doesn't allow realloc to be called with NULL :-(
- * Insert extra test on unix systems...
- */
- if (pFileList == NULL)
- pFileList = (char **) malloc((j + 2) * sizeof(char *));
- else
- #endif
- 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':
- if (pOptArg == NULL)
- Abort("command error, the '%c' option requires an argument", (unsigned char) CurOpt);
-
- 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, 0);
- 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;
- }
-
-
-
- /* 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 = BMG_Search(&Ctrl);
-
- if (nMatches > 0)
- fprintf(Ctrl.fpMsg, "%s: %ld match%s found\n",
- Ctrl.pInputFile, nMatches, (nMatches == 1) ? "" : "es");
- }
- else
- {
- nMatches = BMG_SearchReplace(&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 occurrence%s changed\n",
- Ctrl.pInputFile, nMatches, (nMatches > 1) ? "s" : "");
- }
- }
- }
-
- /* Input : filename
- * Returns: 1 - file is OK, i.e we get a stat on it and it's a regular file
- * 0 - file is NOK
- *
- * Eventual errors are displayed here
- */
- char fCheckFile(char *pFileName)
- {
- struct stat buf;
-
- if (stat(pFileName, &buf) != 0)
- {
- fprintf(Ctrl.fpMsg, "gsar: unable to open input file '%s'\n", pFileName);
- return 0;
- }
-
- if (!(S_ISREG(buf.st_mode)))
- {
- fprintf(Ctrl.fpMsg, "gsar: warning, not a regular file '%s'\n", pFileName);
- return 0;
- }
-
- return 1;
- }
-
- /* 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)
- {
- Ctrl.pInputFile = *pFileList++;
-
- if (!fCheckFile(Ctrl.pInputFile))
- continue;
-
- if ((Ctrl.fpIn = fopen(Ctrl.pInputFile, "rb")) == NULL)
- {
- fprintf(Ctrl.fpMsg, "gsar: unable to open input file '%s'\n", Ctrl.pInputFile);
- continue;
- }
-
- Ctrl.fpMsg = stdout;
-
- #if defined(MSDOS) && (!defined(__ZTC__)) || (defined(__ZTC__) && !defined(__SMALL__))
- /* Don't use setvbuf if we're compiling under Zortech small model
- */
- if (setvbuf(Ctrl.fpIn, NULL, _IOFBF, SETVBUF_SIZ) != 0)
- fprintf(Ctrl.fpMsg, "warning, unable to set up buffering for input file\n");
- #endif
-
- nMatches = BMG_Search(&Ctrl);
- fclose(Ctrl.fpIn);
-
- if (nMatches > 0)
- fprintf(Ctrl.fpMsg, "%s: %ld match%s found\n",
- Ctrl.pInputFile, nMatches, (nMatches == 1) ? "" : "es");
- }
- }
-
- /* 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 (!fCheckFile(Ctrl.pInputFile))
- {
- /* Message has already been given */
- exit(EXIT_FAILURE);
- }
- else
- Ctrl.fpIn = fopen(Ctrl.pInputFile, "rb");
-
- if (Ctrl.fpIn == 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__)) || (defined(__ZTC__) && !defined(__SMALL__))
- /* Don't use setvbuf if we're compiling under Zortech small model
- */
- if (setvbuf(Ctrl.fpIn, NULL, _IOFBF, SETVBUF_SIZ) != 0)
- fprintf(Ctrl.fpMsg, "warning, unable to set up buffering for input file\n");
- if (setvbuf(Ctrl.fpOut, NULL, _IOFBF, SETVBUF_SIZ) != 0)
- fprintf(Ctrl.fpMsg, "warning, unable to set up buffering for output file\n");
- #endif
-
- nMatches = BMG_SearchReplace(&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 occurrence%s changed\n",
- Ctrl.pInputFile, nMatches, (nMatches > 1) ? "s" : "");
-
- /* Make sure the Ctrl-C handler does not delete a completly
- * processed output file
- */
- pOutFile = NULL;
-
- fCriticalPart = 0; /* enable interrupts */
- }
-
-
- /* 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 this up...
- 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 (!fCheckFile(Ctrl.pInputFile))
- continue;
-
- 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__)) || (defined(__ZTC__) && !defined(__SMALL__))
- /* Don't use setvbuf if we're compiling under Zortech small model
- */
- if (setvbuf(Ctrl.fpIn, NULL, _IOFBF, SETVBUF_SIZ) != 0)
- fprintf(Ctrl.fpMsg, "warning, unable to set up buffering for input file\n");
- if (setvbuf(Ctrl.fpOut, NULL, _IOFBF, SETVBUF_SIZ) != 0)
- fprintf(Ctrl.fpMsg, "warning, unable to set up buffering for output file\n");
- #endif
-
- nMatches = BMG_SearchReplace(&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 occurrence%s changed\n",
- Ctrl.pInputFile, nMatches, (nMatches > 1) ? "s" : "");
- }
- fCriticalPart = 0;
- }
- }
-
- int main(int argc, char *argv[])
- {
- int i; /* number of files on command line */
-
- #if defined(__ZTC__)
- /* Automatic response file expansion */
- response_expand(&argc, &argv);
- #endif
-
- 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);
-
- fprintf(Ctrl.fpMsg, "Search buffer (ASCII) : ");
- DumpBuffer(SearchBuf, nItemsSearch, 0);
- fprintf(Ctrl.fpMsg, "Length = %3d (Hex) : ", nItemsSearch);
- DumpBuffer(SearchBuf, nItemsSearch, 1);
- fprintf(Ctrl.fpMsg, "\n");
-
- if (fSearchReplace)
- {
- if (nItemsReplace > 0)
- {
- fprintf(Ctrl.fpMsg, "Replace buffer (ASCII) : ");
- DumpBuffer(ReplaceBuf, nItemsReplace, 0);
- fprintf(Ctrl.fpMsg, "Length = %3d (Hex) : ", nItemsReplace);
- DumpBuffer(ReplaceBuf, nItemsReplace, 1);
- }
- else
- fprintf(Ctrl.fpMsg, "Note: Replace buffer is empty, search string will be removed from file!\n");
- }
-
- 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
- */
- BMG_Setup(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;
- }
-
-