1 /* replace.c */
2 /* ########################### */
3 /* #           rpl           # */
4 /* #     by Joe Laffey       # */
5 /* # joe@laffeycomputer.com  # */
6 /* #                         # */
7 /* ########################### */
8 
9 /*
10  # Last Modified:  $Date: 2008/04/24 13:05:58 $
11  # by:             $Author: joe $
12 */
13 
14 
15 /*  rpl v1.4.1 by Joe Laffey, LAFFEY Computer Imaging. 							*/
16 /*  Visit http://www.laffeycomputer.com for updates 								*/
17 /*  This software is copyright 1998-2008 by Joe Laffey.   				*/
18 
19 /*  Permission is granted to any individual, institution, or company to use, 		*/
20 /*  copy, or redistribute rpl in source code or binary form so long as it is 		*/
21 /*  not modified in any way (beyond modifications required to compile or */
22 /*  "package"), and it is not sold by itself for a profit. 							*/
23 /*  Permission is also granted to bundle rpl in software distributions which 		*/
24 /*  are sold for a profit (e.g. CD-ROMs, etc.), as long as there are at least 		*/
25 /*  ten programs included in the distribution.  									*/
26 /*  In other words, please do NOT release updates or modified versions yourself.     */
27 /*  If you modify the source code and would like to see your changes incorporated 	*/
28 /*  please submit your source code to software@laffeycomputer.com 					*/
29 /*  Please report bugs to that address as well. 									*/
30 
31 /*  rpl IS PROVIDED AS IS AND COMES WITH NO WARRANTY OF ANY KIND, EITHER EXPRESSED 	*/
32 /*  OR IMPLIED. IN NO EVENT WILL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DAMAGES 	*/
33 /*  RESULTING FROM THE USE OF THIS SOFTWARE. 										*/
34 
35 
36 
37 
38 #include <sys/types.h>
39 #include <utime.h>
40 #include <sys/stat.h>
41 #include <sys/errno.h>
42 #include <sys/param.h>
43 #include <errno.h>
44 #include <sysexits.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <signal.h>
49 #include <sys/uio.h>
50 #include <unistd.h>
51 #include <dirent.h>
52 #include <string.h>
53 #include <ctype.h>
54 
55 #include "replace.h"
56 #include "suffix.h"
57 #include "estrcpy.h"
58 #include "usage.h"
59 
60 
61 
62 
63 
64 
main(int argc,char ** argv)65 int main(int argc, char** argv)
66 {
67 
68 	register 	int 	i;
69 	register 	int		currentChar;
70 	register 	int		prevChar;
71 	register 	int		currentState;
72 
73 	char		*suffixes[MAX_SUFFIXES];
74 	int			suffixLens[MAX_SUFFIXES];
75 	int			minSuffixLength =0;
76 	int			numSuffixes = 0;
77 
78 	int		verbose = 0;				/*							*/
79 	int		force = 0;					/*		These are the		*/
80 	int		useTmp = 0;					/*		modes from the		*/
81 	int		quiet = 0;					/*		command-line		*/
82 	int		whole_words = 0;			/*		switches.			*/
83 	int		recursive = 0;				/*							*/
84 	int		ignore_case = 0;			/*							*/
85 	int		keepDates = 0;				/*							*/
86 	int		useSuffixes = 0;			/*							*/
87 	int		escapeInput = 0; 	/*should we escape stuff like \n \r \t; etc? */
88 	int		doneFlag;
89 	int		stringChanged = 0;
90 	int		simulationMode = 0;
91 	int		prompt = 0;
92 	int		replaceStringLength;
93 	int		pathLength;
94 	int		ch;
95 	int		good;
96 	int		seen =0;
97 	int		skipIt = 0;
98 	int		outfile_fd;
99 	int		myLen;
100 	int		oldStrLen; 	/* length of old (search) string */
101 
102 
103 
104 	unsigned long 	fileMatches = 0;	/* These are counts for tallies */
105 	unsigned long	totalMatches = 0;
106 	unsigned long	filecount = 0;
107 	unsigned long	lineCount = 1;
108 
109 	unsigned	char	searchString[MAX_STRING_LENGTH];	/* The actual string we are looking for */
110 	unsigned	char	replaceString[MAX_STRING_LENGTH];	/* The string we will use to replace the search string */
111 
112 	unsigned	char	replaceBuffer[MAX_STRING_LENGTH];	/* holds the string that gets written to the modified file. */
113 															/* we use this so we can write out new lines inplace of spaces, etc. */
114 
115 	unsigned	char	*orgSearchString;	/* The original unescaped search string */
116 	unsigned	char	*orgReplaceString;/* The original unescaped replace string */
117 
118 	unsigned	char	buff[MAX_STRING_LENGTH];		/* Buffer we read data into */
119 	unsigned	char	line[MAX_INPUT_LINE];
120 
121 	char 	absPathIn[MAXPATHLEN];	/* absolute path of the original file */
122 	char	absPathOut[MAXPATHLEN];	/*absolute path of the temp file we are writing to */
123 
124 
125 
126 
127 	FILE	*INFILE;
128 	FILE	*OUTFILE;
129 
130 	struct stat		fileStat;
131 
132 	fileList 		*firstItem;
133 	fileList		*currentItem;
134 	fileList		*nextItem;
135 	fileList		*oldItem;
136 
137 	DIR		*dirp;
138 
139 	struct dirent	*dp;
140 
141 
142 
143 
144 
145 	while ((ch = getopt(argc, argv, "iwqvRLx:hpsftde")) != -1)		/* get our options */
146 		switch(ch) {
147 		case 'i':
148 				 ignore_case = 1;
149 				 break;
150 		case 'v':
151 				verbose = 1;
152 				break;
153 		case 'q':
154 				quiet = 1;
155 				break;
156 		case 'e':
157 				escapeInput = 1;
158 				break;
159 		case 'w':
160 				whole_words = 1;
161 				break;
162 		case 'R':
163 				recursive = 1;
164 				break;
165 		case 'L':
166 				License();
167 				break;
168 		case 'p':
169 				prompt = 1;
170 				break;
171 		case 's':
172 				simulationMode = 1;
173 				break;
174 		case 'f':
175 				force = 1;
176 				break;
177 		case 't':
178 				useTmp = 1;
179 				break;
180 		case 'd':
181 				keepDates = 1;
182 				break;
183 		case 'x':
184 				useSuffixes = 1;
185 				AddSuffix(optarg, suffixes, suffixLens, &minSuffixLength, &numSuffixes);
186 				break;
187 		case 'h':
188 		default:
189 				 usage();
190 	 }
191 	 argc -= optind;
192 	 argv += optind;
193 
194 	if( argc < 3 )						/* We Need at least three arguments */
195 		usage();
196 
197 	if(argv[0] == 0)					/*  old_string can't be null */
198 		usage();
199 
200 	for(i=2; i < argc ; i++)
201 	{
202 		if(stat(argv[i], &fileStat))	/* be sure files exist */
203 		{
204 			fprintf(stderr, "\n" PACKAGE ": File \"%s\" not found.\n", argv[i] );
205 			exit(EX_DATAERR);
206 		}
207 	}
208 
209 	oldStrLen = strlen(argv[0]);
210 	if( oldStrLen < MAX_STRING_LENGTH-1 )
211 	{
212 		if(escapeInput)
213 		{
214 			estrcpy((char*)searchString,argv[0]);
215 			MALLOC_AND_CHECK(orgSearchString, (size_t)oldStrLen+1);
216 
217 			/* put the original search string into orgSearchString */
218 			strcpy((char*)orgSearchString,argv[0]);
219 		}
220 		else
221 		{
222 			strcpy((char*)searchString,argv[0]);		/* old_string */
223 		}
224 
225 	}
226 	else
227 	{
228 		fprintf( stderr, "\n" PACKAGE ": old_string is too long!\nMaximum string length is %d.", MAX_STRING_LENGTH);
229 		exit(EX_DATAERR);
230 	}
231 
232 	argv++;
233 	argc--;
234 
235 	if(argv[0] != 0)	/* Ok for replacestring to be null */
236 	{
237 		myLen = strlen(argv[0]);
238 		if( myLen < MAX_STRING_LENGTH-1 )
239 		{
240 			if(escapeInput)
241 			{
242 				estrcpy((char*)replaceString,argv[0]);
243 				MALLOC_AND_CHECK(orgReplaceString, (size_t)myLen+1);
244 
245 				/* put original replace string into orgReplaceString */
246 				strcpy((char*)orgReplaceString,argv[0]);
247 			}
248 			else
249 				strcpy((char*)replaceString,argv[0]);	/*  but can't strcpy it if it is!! */
250 
251 
252 		}
253 		else
254 		{
255 			fprintf( stderr, "\n" PACKAGE ": new_string is too long!\nMaximum string length is %d.", MAX_STRING_LENGTH);
256 			exit(-1);
257 		}
258 
259 	}
260 	else
261 		replaceString[0] = '\0';			/* the solution... */
262 
263 	replaceStringLength = strlen((char*)replaceString);
264 
265 	strcpy((char*) replaceBuffer, (char*)replaceString); 	/*  replaceBuffer lets us change the text that gets */
266 															/* written in the case where a match is over a line break. */
267 															/* This lets us replace a space with a line break, etc. */
268 
269 	argv++; /*  Move down... */
270 	argc--;	/*  Another cup of tea... */
271 
272 
273 	/* Set up our linked list */
274 	MALLOC_AND_CHECK(firstItem, sizeof(fileList));
275 
276 
277 	currentItem = firstItem;
278 
279 	for(i=0; i<argc; )/* i++ below */
280 	{
281 
282 		pathLength = strlen(argv[i]);
283 
284 		if(argv[i][pathLength-1] == '/')	/*drop trailing slash */
285 			argv[i][pathLength-1] = '\0';
286 
287 
288 		MALLOC_AND_CHECK( currentItem->name, strlen(argv[i])+3); /* length + / null */
289 
290 
291 		strcpy( currentItem->name, argv[i]);
292 
293 		i++;
294 
295 		if( i < argc )
296 		{
297    			MALLOC_AND_CHECK( nextItem, sizeof(fileList) );
298 
299 			currentItem->next = nextItem;
300 
301 			currentItem = nextItem;
302 		}
303 		else
304 		{
305 			currentItem->next = 0;
306 			break;
307 		}
308 
309 	}
310 
311 
312 
313 	if(replaceString[0] == '\0')	/* Try to protect users from deleteing strings */
314 	{
315 		if( quiet == 0)
316 		{
317 			fprintf(stderr, "Really DELETE all occurences of %s ",(escapeInput)?orgSearchString:searchString);
318 			if(ignore_case == 1)
319 			{
320 				fprintf(stderr,"(ignoring case)? (Y/[N]) ");
321 
322 			}
323 			else
324 			{
325 				fprintf(stderr,"(case sensitive)? (Y/[N]) ");
326 			}
327 
328 			fgets((char*)line, 255, stdin);	/*read in response */
329 
330 
331 			if( line[0] != 'y' && line[0] != 'Y' )
332 			{
333 				fprintf(stderr,"\n" PACKAGE ":  User cancelled operation.\n");
334 				exit(EX_TEMPFAIL);
335 			}
336 		}
337 	}
338 
339 
340 	/*  Set up our Interrupt (control-C) handler... */
341 	signal(SIGINT, DoInterrupt);
342 
343 
344 	/* Print out what we are doing to stderr for the user to see */
345 	EchoModes(escapeInput, ignore_case, whole_words, simulationMode, searchString, replaceString, orgSearchString, orgReplaceString);
346 
347 
348 
349 	/* Start at the beginning */
350 	currentItem = firstItem;
351 
352 	 /* CONSTANTCONDITION */
353 	while( 1 )	/* loop forever... breaking is at the end of the loop if there are no */
354 	{			/* more items in our linked list... */
355 
356 		if(realpath(currentItem->name, absPathIn) == 0) 	/* get the absolute path to the file */
357 		{
358 			fprintf(stderr, "\n" PACKAGE ": Could not get realpath of %s.\n" PACKAGE ": Error: %s\n" PACKAGE ": SKIPPING %s\n\n", absPathIn, strerror(errno), absPathIn);
359 
360 			currentItem = GetNextItem(currentItem);
361 			if(currentItem == NULL)
362 				break;	/* break if there are no more files */
363 
364 
365 			continue;
366 		}
367 
368 
369 
370 		if(stat(absPathIn, &fileStat))		/* read the original permissions so our new file can match */
371 		{
372 			fprintf(stderr, "\n" PACKAGE ": Unable to read permissions of %s.\n" PACKAGE ": Error: %s\n" PACKAGE ": SKIPPING %s\n\n!",absPathIn, strerror(errno), absPathIn );
373 
374 			currentItem = GetNextItem(currentItem);
375 			if(currentItem == NULL)
376 				break;	/* break if there are no more files */
377 
378 
379 			continue;
380 
381 		}
382 
383 
384 
385 		/* Check for directory */
386 		if( (fileStat.st_mode) & S_IFDIR)
387 		{
388 
389 
390 			if(!recursive)	/* If we're not doing a recursive search then just  */
391 			{				/* ignore directories... */
392 
393 				if(verbose)
394 					fprintf(stderr, "Directory: %s skipped.\n",currentItem->name);
395 
396 				currentItem = GetNextItem(currentItem);
397 				if(currentItem == NULL)
398 					break;	/* break if there are no more files */
399 
400 				continue;
401 
402 			}
403 			else	/* we have a directory */
404 			{
405 				if(verbose)
406 					fprintf(stderr, "Scanning Directory: %s\n",currentItem->name);
407 
408 
409 
410 				dirp = opendir(absPathIn); /* open sesame! */
411 
412 
413 
414 
415 				if( ( readdir(dirp)) != NULL )		/*  These two reads skip the "." */
416 					if( ( readdir(dirp)) != NULL )	/*  and ".." entries... */
417 					{
418 
419 						if( (dp = readdir(dirp)) != NULL )	/* be sure something is in there */
420 						{
421 
422 							/* Set up our linked list */
423 							oldItem = currentItem;
424 
425 	   						MALLOC_AND_CHECK( firstItem, sizeof(fileList) );
426 
427 							currentItem = firstItem;
428 
429 							pathLength = strlen(oldItem->name) + 1;
430 							oldItem->name[pathLength - 1] = '/';
431 							oldItem->name[pathLength] = '\0';
432 
433 							 /* CONSTANTCONDITION */
434 							while (1)
435 							{
436 
437 								MALLOC_AND_CHECK( currentItem->name, (size_t)(strlen(dp->d_name) + pathLength+3));
438 
439 								strcpy( currentItem->name, oldItem->name);
440 
441 								strcat( currentItem->name, dp->d_name);
442 
443 
444 								if( (dp = readdir(dirp)) != NULL )
445 								{
446 									MALLOC_AND_CHECK( nextItem, sizeof(fileList) );
447 
448 									currentItem->next = nextItem;
449 
450 									currentItem = nextItem;
451 								}
452 								else
453 									break;
454 
455 							}
456 
457 
458 							currentItem->next = oldItem->next;  /*  fix up links in the */
459 							oldItem->next = firstItem;			/*  list */
460 
461 							currentItem = oldItem;		/* Get back in place */
462 
463 						}/* end the first two dps */
464 					}
465 
466 
467 
468 
469 				if(closedir(dirp))	/* cleanup */
470 				{
471 					fprintf( stderr, "\n" PACKAGE ": Unable to close directory!\nError: %s\n", strerror(errno));
472 					exit(EX_OSERR);
473 				}
474 
475 
476 				currentItem = GetNextItem(currentItem);
477 				if(currentItem == NULL)
478 					break;	/* break if there are no more files */
479 
480 
481 				continue;
482 			}
483 
484 		} /* end if this is a dir */
485 		else
486 		{
487 			/* We have a file. Be sure we should process this file by checking suffix list if needed. */
488 
489 			if(useSuffixes && SuffixIsBad(absPathIn, suffixes, suffixLens, minSuffixLength, numSuffixes))	/* If we are using suffix matching and this file has a bad suffix */
490 			{
491 				if(verbose)
492 					fprintf(stderr, "Skipping: %s. (suffix not in list)\n",currentItem->name);
493 
494 				currentItem = GetNextItem(currentItem);
495 				if(currentItem == NULL)
496 					break;	/* break if there are no more files */
497 
498 
499 				continue;
500 			}
501 		}
502 
503 		INFILE = fopen( absPathIn, "rb");  /* open up the input file */
504 
505 		if(INFILE == NULL)
506 		{
507 			fprintf(stderr, "\n" PACKAGE ": Unable to open %s for reading.\n" PACKAGE ": Error: %s\n" PACKAGE ": SKIPPING %s\n", absPathIn, strerror(errno), absPathIn);
508 
509 			currentItem = GetNextItem(currentItem);
510 			if(currentItem == NULL)
511 				break;	/* break if there are no more files */
512 
513 
514 			continue;
515 
516 		}
517 
518 
519 
520 
521 		if( useTmp )
522 		{
523 			/* try to get the $TMPDIR env var to set the temp dir to use for temp files */
524 			if(getenv("TMPDIR") != NULL )
525 			{
526 				strcpy(absPathOut,getenv("TMPDIR"));
527 				fprintf(stderr, "\n" PACKAGE ": Unable to read environment var $TMPDIR with -t option active.\n" PACKAGE ": Using source file directory instead.\n" );
528 
529 			}
530 			else
531 			{
532 				/* No-Go. So use the file's directory for the temp file */
533 				strcpy(absPathOut,absPathIn);
534 			}
535 
536 		}
537 		else
538 		{
539 			strcpy(absPathOut,absPathIn); /* use the file's directory for the temp file */
540 		}
541 
542 		strcat(absPathOut, ".tmp.XXXXXX");		/* set up our temp file name */
543 
544 
545 		outfile_fd = mkstemp(absPathOut); /* Make the tempfile */
546 
547 		if(outfile_fd == -1)
548 		{
549 			fprintf(stderr, "\n" PACKAGE ": Unable to create temp file %s.\n" PACKAGE ": Error: %s\n" PACKAGE ": (Type \"" PACKAGE " -h\" and consider \"-t\" to specify temp file location.)\n" PACKAGE ": SKIPPING %s.\n\n",absPathOut, strerror(errno), absPathIn);
550 
551 			fclose(INFILE);
552 
553 			currentItem = GetNextItem(currentItem);
554 			if(currentItem == NULL)
555 				break;	/* break if there are no more files */
556 
557 			continue;
558 		}
559 
560 		OUTFILE = fdopen( outfile_fd, "wb");  /* open up the tempfile to write to */
561 
562 		if(OUTFILE == NULL)
563 		{
564 			fprintf(stderr, "\n" PACKAGE ": Unable to open temp file %s for writing.\n" PACKAGE ": Error: %s\n" PACKAGE ": Aborting. Some files may not have been processed.\n\n", absPathOut, strerror(errno));
565 			exit(EX_IOERR);
566 		}
567 
568 		/*Set Permissions of newly created output file */
569 		SetPerms(fileStat, force, outfile_fd, absPathIn, &skipIt);
570 
571 
572 
573 		/* Echo feedback of processing to the user */
574 		EchoFeedback(verbose, simulationMode, quiet, currentItem->name);
575 
576 
577 
578 		/* Set our current state (What we are looking for) */
579 		currentState = FIND_FIRST_CHAR;
580 
581 		currentChar = 0; /* start at the first char of the string */
582 
583 		prevChar = ' '; /* set this up so match whole words will work at the begining of a file */
584 
585 		doneFlag = 0;
586 
587 
588 
589 		while( !doneFlag )
590 		{
591 
592 
593 			if( interrupted )
594 			{
595 				fprintf(stderr, "\n" PACKAGE ":  User cancelled.\n");
596 				if( simulationMode )
597 					fprintf(stderr, "      A Total of %lu matches found in %lu file%s searched.\n      None replaced (simulation mode).\n", totalMatches > 0 ? (totalMatches - fileMatches): fileMatches, filecount+1, (filecount>0)?"s":"" );
598 				else
599 				{
600 					fprintf(stderr, "      A Total of %lu matches replaced in %lu file%s searched.\n", totalMatches > 0 ? (totalMatches - fileMatches): fileMatches, filecount, (filecount>1)?"s":"" );
601 					fprintf(stderr, "      Changes to file \"%s\" not saved.\n",currentItem->name);
602 				}
603 
604 				if(unlink(absPathOut))
605 				{
606 					fprintf(stderr, "\n" PACKAGE ": An error occured deleting temp file %s.\n" PACKAGE ": Error: %s\n\n",absPathOut, strerror(errno));
607 					exit(EX_IOERR);
608 				}
609 				exit(EX_TEMPFAIL);
610 
611 			}
612 
613 			ch = getc(INFILE);
614 
615 			if(ch == EOF )
616 			{
617 				/* probably just EOF, but check for error */
618 				if(ferror(INFILE))
619 				{
620 					fprintf(stderr, "\n" PACKAGE ": An error occurred reading %s.\n" PACKAGE ": Error: %s\n\n",absPathIn, strerror(errno));
621 					exit(EX_OSERR);
622 				}
623 
624 				if( currentState != MATCH_STRING )
625 					break; /* we're done with this file */
626 				else
627 					currentState = FINISH_MATCH;
628 			}
629 			else if(ch == '\n')
630 			{
631 				lineCount++;
632 				seen = 0; 		/*mark line as not seen */
633 			}
634 
635 
636 
637 
638 			switch(currentState)
639 			{
640 				case FIND_FIRST_CHAR:
641 						/* if we're not looking for whole words then it doesn't matter what */
642 						/* prevChar is. But if we are, then we have to be sure it was whitespace */
643 
644 						if(whole_words == 0 || prevChar == ' ' || prevChar == '\n' ||
645 							prevChar == '\t' || prevChar == '\r')
646 						{
647 							if( searchString[0] == ch || (ignore_case &&  searchString[0] == toupper(ch)))	/* we found the first character of the searchstring */
648 							{
649 								currentState = MATCH_STRING; /* change state */
650 								currentChar = 0;
651 
652 								/* let's read that char again */
653 								ungetc(ch,INFILE);
654 								if( ch == '\n' )
655 									lineCount--;
656 								break; /* we're done this pass */
657 							}
658 						}
659 
660 						putc( ch, OUTFILE);  /* write out the char */
661 
662 						break;
663 
664 				case MATCH_STRING:
665 
666 						if(searchString[currentChar] == '\0')  /* we're at the end of searchString */
667 						{										/*  must be a match */
668 
669 							if(whole_words == 0 || ch == ' ' || ch == '\n' ||	/* be sure we have whitespace */
670 							ch == '\t' || ch == '\r' )							/*  next if looking for whole words */
671 							{
672 								fwrite( replaceBuffer, (size_t)replaceStringLength, (size_t)1, OUTFILE); /* write the replacement out */
673 
674 								if(stringChanged)
675 								{
676 									strcpy( (char*)replaceBuffer, (char*)replaceString);
677 									stringChanged = 0;
678 								}
679 
680 
681 
682 
683 								fileMatches++;
684 
685 								/* let's read that char again */
686 								ungetc(ch,INFILE);
687 								if( ch == '\n' )
688 									lineCount--;
689 							}
690 							else /* not a whole word */
691 							{
692 								buff[currentChar++] = (unsigned char)ch;  /* copy ch into buffer to write it later */
693 
694 								fwrite( buff, (size_t)currentChar, (size_t)1, OUTFILE); /* write the buff out */
695 							}
696 
697 							currentState = FIND_FIRST_CHAR;  /* change state */
698 
699 							if(verbose == 1)
700 							{
701 								if( !seen ) /* if we have not already listed this line */
702 								{
703 									/*curPos = ftell(INFILE);*/
704 									if( simulationMode )
705 										printf( "  Line: %ld String Found\n", lineCount);	/* print position found */
706 									else
707 										printf( "  Line: %ld String Found and Replaced\n", lineCount);	/* print position found */
708 
709 									seen = 1; /* Mark this line as seen*/
710 								}
711 							}
712 
713 							break;
714 						}
715 
716 						buff[currentChar] = (unsigned char)ch;  /* copy ch into buffer in case we need to write it later */
717 
718 
719 						if( (searchString[currentChar] == ' ') &&     /* if searchString has a space in it */
720 							(ch == '\n' || ch == '\r') )			/*  consider any newlines as a match and keep matching */
721 						{
722 
723 							for(i=0; i < replaceStringLength; i++ )
724 							{
725 								if(replaceBuffer[i] == ' ')
726 								{
727 									replaceBuffer[i] = (unsigned char)ch; /* replace spaces with the actual char (newline, etc.) */
728 
729 									stringChanged = 1;     /* mark string as changed */
730 
731 									break;
732 
733 								}
734 							}
735 
736 
737 							if(i >= replaceStringLength)
738 							{
739 								if(replaceStringLength+1 > MAX_STRING_LENGTH)
740 								{
741 									/* This is possible. Should rework to avoid this */
742 									fprintf( stderr, "\n" PACKAGE ": old_string has too many spaces at the end. Be sure the length of the new_string plus the number of spaces at the end of old_string is less than %d.\n", MAX_STRING_LENGTH);
743 										exit(EX_SOFTWARE);
744 								}
745 
746 								replaceBuffer[replaceStringLength] = (unsigned char)ch; 		/* This will add a newline after the replace string */
747 								replaceStringLength++;							/* if the search string has a space and the located */
748 																				/* string has a newline */
749 
750 							}
751 							currentChar++;
752 							break;
753 
754 						}
755 
756 						if( searchString[currentChar] != ch && !(ignore_case &&  searchString[currentChar] == toupper(ch)))/* we don't match the whole string		 */
757 						{
758 
759 							currentState = FIND_FIRST_CHAR;  /* change state */
760 
761 							fwrite( buff, (size_t)currentChar ,(size_t)1, OUTFILE); /* write the buff out */
762 
763 							/*read char again*/
764 							ungetc(ch, INFILE);
765 							if( ch == '\n' )
766 								lineCount--;
767 
768 							/* If wea altered our replace buffer due to substituting spaces with line breaks, etc. */
769 							/* we have to refresh the buffer */
770 							if(stringChanged)
771 							{
772 								strcpy( (char*)replaceBuffer, (char*)replaceString);  /* refresh the buffer */
773 								stringChanged = 0;
774 							}
775 						}
776 
777 
778 
779 						currentChar++;
780 
781 						break;
782 
783 				case FINISH_MATCH:
784 						if(searchString[currentChar] == '\0')  /* we're at the end of searchString */
785 						{										/*  must be a match */
786 
787 
788 							fwrite( replaceBuffer, (size_t)replaceStringLength, (size_t)1, OUTFILE); /* write the replacement out */
789 
790 							fileMatches++;
791 
792 
793 							doneFlag = 1; /* done with this file */
794 
795 
796 							if(verbose == 1)
797 							{
798 								if( !seen ) /* if we have not already listed this line */
799 								{
800 									/*curPos = ftell(INFILE);*/
801 									if( simulationMode )
802 										printf( "  Line: %ld String Found\n", lineCount);	/* print position found */
803 									else
804 										printf( "  Line: %ld String Found and Replaced\n", lineCount);	/* print position found */
805 
806 									seen = 1; /* Mark this line as seen*/
807 								}
808 							}
809 
810 							break;
811 						}
812 
813 
814 						doneFlag = 1; /* done with this file */
815 
816 						fwrite( buff, (size_t)currentChar, (size_t)1, OUTFILE); /* write the buff out */
817 
818 
819 
820 						break;
821 
822 				default:
823 						/* never get here... in theory ;-) */
824 						fprintf(stderr, "\n" PACKAGE "Ack!!! This should never happen... Bad case in switch... Tell somebody, quick!\n");
825 						exit(EX_SOFTWARE);
826 						break;
827 			 }/*  end switch(currentState) */
828 
829 
830 			prevChar = ch; /* copy this char for whole word searches. */
831 
832 
833 		}/* end while not eof */
834 
835 		fclose(INFILE);
836 		fclose(OUTFILE);
837 
838 		if( interrupted )
839 		{
840 			fprintf(stderr, "\n" PACKAGE ":  User cancelled.\n");
841 			if( simulationMode )
842 				fprintf(stderr, "      A Total of %lu matches found in %lu file%s searched.\n      None replaced (simulation mode).\n", totalMatches , filecount+1, (filecount>0)?"s":"");
843 			else
844 			{
845 				fprintf(stderr, "      A Total of %lu matches replaced in %lu file%s searched.\n", totalMatches > 0 ? (totalMatches - fileMatches): fileMatches, filecount, (filecount>1)?"s":"");
846 			}
847 
848 			if(unlink(absPathOut))
849 			{
850 				fprintf( stderr, "\n" PACKAGE ": An error occured deleting %s.\nError: %s\n",absPathOut, strerror(errno));
851 				exit(EX_IOERR);
852 			}
853 
854 			exit(EX_TEMPFAIL);
855 
856 		}
857 
858 
859 		if(fileMatches > 0)
860 		{
861 
862 			if( simulationMode )
863 			{
864 				fprintf( stderr, "  %s\n",absPathIn);	/*list the file*/
865 				/* delete tmp file */
866 				if(unlink(absPathOut))
867 					fprintf(stderr, "\n" PACKAGE ": An error occured deleting temp file %s.\n" PACKAGE ": Error: %s\n\n",absPathOut, strerror(errno));
868 				totalMatches += fileMatches;		/* handle our count */
869 			}
870 			else
871 			{
872 
873 
874 				if( prompt )	/* User must decide to process this file or not */
875 				{
876 					good = 0;
877 					while( !good )
878 					{
879 						fprintf(stderr, "\nSave '%s' ? ([Y]/N) ",absPathIn);
880 
881 						fgets((char*)line, 255, stdin);	/*read in response */
882 
883 
884 						if( line[0] == 'y' || line[0] == 'Y' || line[0] == '\n' )
885 						{
886 							good = 1; 		/*we got what we were looking for */
887 							fprintf(stderr, "Saved.\n");
888 
889 						}
890 						else if( line[0] == 'n' || line[0] == 'N' )
891 						{
892 							fprintf(stderr, "Not Saved.\n");		/*We need to skip this file */
893 							good = 1;
894 							skipIt = 1;  /*mark file to be skipped */
895 						}
896 
897 					}
898 
899 				}
900 
901 
902 
903 				if( !skipIt )
904 				{
905 					/* rename tmpfile to old file */
906 					if(rename(absPathOut, absPathIn))
907 					{
908 						fprintf( stderr, "\n" PACKAGE ": An error occured replacing %s with %s.\n" PACKAGE ": Error: %s\n\n",absPathIn,absPathOut, strerror(errno));
909 						if(unlink(absPathOut))
910 							fprintf(stderr, "\n" PACKAGE ": An error occured deleting temp file %s.\n" PACKAGE ": Error: %s\n\n",absPathOut, strerror(errno));
911 
912 					}
913 
914 					/* Set mod date to old mod date if we need to */
915 
916 					if(keepDates)
917 					{
918 						 struct utimbuf timeStruct;
919 
920 						 timeStruct.actime = fileStat.st_atime;
921 						 timeStruct.modtime = fileStat.st_mtime;
922 
923 						 if( utime(absPathIn, &timeStruct) )
924 						 	fprintf(stderr, "\n" PACKAGE ": An error occured setting the access time and mod time of the file %s.\n" PACKAGE ": Error: %s\n\n",absPathIn, strerror(errno));
925 
926 					}
927 
928 					totalMatches += fileMatches;		/* handle our count */
929 				}
930 				else
931 				{
932 					skipIt = 0;
933 					if(unlink(absPathOut))
934 							fprintf(stderr, "\n" PACKAGE ": An error occured deleting temp file %s.\n" PACKAGE ": Error: %s\n\n",absPathOut, strerror(errno));
935 
936 				}
937 			}
938 
939 			fileMatches = 0;
940 
941 
942 		}
943 		else
944 		{
945 			/* delete tmp file */
946 			if(unlink(absPathOut))
947 				fprintf(stderr, "\n" PACKAGE ": An error occured deleting temp file %s.\n" PACKAGE ":Error: %s\n\n",absPathOut, strerror(errno));
948 		}
949 
950 		lineCount = 1;
951 		filecount++;
952 
953 
954 		currentItem = GetNextItem(currentItem);
955 		if(currentItem == NULL)
956 			break;	/* break if there are no more files */
957 
958 
959 	}/*  end while there are more files */
960 
961 	if( simulationMode )
962 		fprintf(stderr, "\nA Total of %lu matches found in %lu file%s searched.\nNone replaced (simulation mode).\n", totalMatches, filecount, (filecount>1)?"s":"");
963 	else
964 		fprintf(stderr, "\nA Total of %lu matches replaced in %lu file%s searched.\n", totalMatches, filecount, (filecount>1)?"s":"");
965 
966 
967 	if(currentItem)
968 	{
969 		if(currentItem->name)
970 			free(currentItem->name);	/*   won't be needing */
971 
972 		free(currentItem);  		/*   these... */
973 	}
974 
975 	return(EX_OK); /*success*/
976 
977 } /* end main */
978 
979 
980 
981 
982 /**************************************/
983 /* EchoModes() prints out the         */
984 /* values of some vars to stderr      */
985 /* for the user to see...             */
986 /* (Take too many params!)            */
EchoModes(int escapeInput,int ignore_case,int whole_words,int simulationMode,unsigned char * searchString,unsigned char * replaceString,unsigned char * orgSearchString,unsigned char * orgReplaceString)987 static void EchoModes(int escapeInput, int ignore_case, int whole_words, int simulationMode, unsigned char* searchString, unsigned char* replaceString, unsigned char* orgSearchString, unsigned char* orgReplaceString)
988 {
989 	int i;
990 
991 	if(escapeInput)
992 	{
993 		if( simulationMode )
994 			fprintf(stderr, "Simulating replacement of \"%s\" with \"%s\" ",orgSearchString,orgReplaceString);
995 		else
996 			fprintf(stderr, "Replacing \"%s\" with \"%s\" ",orgSearchString,orgReplaceString);
997 	}
998 	else
999 	{
1000 		if( simulationMode )
1001 			fprintf(stderr, "Simulating replacement of \"%s\" with \"%s\" ",searchString,replaceString);
1002 		else
1003 			fprintf(stderr, "Replacing \"%s\" with \"%s\" ",searchString,replaceString);
1004 	}
1005 
1006 	if(ignore_case == 1)
1007 	{
1008 		fprintf(stderr, "(ignoring case) ");
1009 
1010 
1011 		for(i = 0; searchString[i] != '\0'; i++)						/* upcase the whole thing */
1012 			searchString[i] = (unsigned char)toupper(searchString[i]);
1013 	}
1014 	else
1015 	{
1016 		fprintf(stderr, "(case sensitive) ");
1017 	}
1018 
1019 	if(whole_words == 1)
1020 	{
1021 		fprintf(stderr, "(whole words only)\n");
1022 	}
1023 	else
1024 	{
1025 		fprintf(stderr, "(partial words matched)\n");
1026 	}
1027 
1028 
1029 
1030 	if( simulationMode )
1031 		fprintf(stderr, "The files listed below would be modified in a replace operation.\n");
1032 
1033 	return;
1034 
1035 }
1036 
1037 
1038 
1039 /**************************************/
1040 /* EchoFeedback() echoes filename     */
1041 /* info, or a "." to show the user    */
1042 /* that we are working.                */
1043 
EchoFeedback(int verbose,int simulationMode,int quiet,char * filename)1044 static void EchoFeedback(int verbose, int simulationMode, int quiet, char* filename)
1045 {
1046 
1047 	if(verbose == 1 && simulationMode == 0)
1048 	{
1049 		fprintf(stderr, "Processing:  %s\n", filename);	/* print the current filename */
1050 	}
1051 	else if( quiet != 1 )
1052 	{
1053 		if( !simulationMode )
1054 			fprintf(stderr,".");		/* print one dot per file for standard (non-quiet, non-verbose) mode */
1055 	}
1056 
1057 	return;
1058 }
1059 
1060 
1061 /********************************/
1062 /* SetPerms() sets the file     */
1063 /* Owner/group and permissions  */
1064 /* if possible. If force option */
1065 /* is set we ignore any errors. */
1066 /* Otherwise we skip the file   */
1067 /* and do not svae the changes  */
1068 /* later.                       */
SetPerms(struct stat fileStat,int force,int outfile_fd,char * absPathIn,int * skipIt)1069 static void SetPerms(struct stat fileStat, int force, int outfile_fd, char* absPathIn, int* skipIt )
1070 {
1071 
1072 	if(fchown(outfile_fd,fileStat.st_uid,fileStat.st_gid))	/* and its owner & group ids */
1073 	{
1074 		if( force )		/*we don't care that permissions don't match */
1075 		{
1076 			fprintf( stderr, "\n" PACKAGE ": Unable to set owner/group of %s.\nError: %s\n" PACKAGE ": WARNING: New owner/group may not match!\n\n",absPathIn, strerror(errno) ); /* only root cant change the owner id, though */
1077 		}
1078 		else
1079 		{
1080 			fprintf( stderr, "\n" PACKAGE ": Unable to set owner/group of %s.\n" PACKAGE ": Error: %s\n" PACKAGE ": SKIPPING %s!\n\n",absPathIn, strerror(errno), absPathIn); /* only root cant change the owner id, though */
1081 			*skipIt = 1; /* so we don't overwrite the file later */
1082 		}
1083 	}
1084 
1085 	if(fchmod(outfile_fd,fileStat.st_mode))		/* set up its permissions */
1086 	{
1087 		if( force )		/*we don't care that permissions don't match */
1088 		{
1089 			fprintf( stderr, "\n" PACKAGE ": Unable to set permissions of %s.\n" PACKAGE ": Error: %s\n" PACKAGE ": WARNING: New permissions may not match!\n",absPathIn, strerror(errno) );
1090 		}
1091 		else
1092 		{
1093 			fprintf( stderr, "\n" PACKAGE ": Unable to set permissions of %s.\n" PACKAGE ": Error: %s\n" PACKAGE ": SKIPPING %s!\n",absPathIn, strerror(errno), absPathIn );
1094 			*skipIt = 1; /* so we don't overwrite the file later */
1095 		}
1096 	}
1097 
1098 	return;
1099 }
1100 
1101 
1102 /********************************/
1103 /* GetNextItem (in linked list) */
1104 /* This ought to be put into    */
1105 /* a separate linked list module */
1106 
GetNextItem(fileList * currentItem)1107 static fileList*	GetNextItem( fileList* currentItem)
1108 {
1109 	fileList	*oldItem;
1110 
1111 	oldItem = currentItem;
1112 	if(currentItem->next)
1113 	{
1114 		currentItem = currentItem->next;
1115 
1116 		free(oldItem->name);	/*   won't be needing */
1117 		free(oldItem);  		/*   these... */
1118 	}
1119 	else
1120 		currentItem = NULL;
1121 
1122 	return currentItem;
1123 
1124 }
1125 
1126 /****************************/
1127 /*  Handle control-Cs       */
DoInterrupt(void)1128 static void DoInterrupt(void)
1129 {
1130 	interrupted++;						/* we got a SIG_INT */
1131 	write(STDERR_FILENO, "\n\n*Break*\n\n", 11);
1132 	if( interrupted > 3 )
1133 	{
1134 		fprintf(stderr, "\n\n*Terminating*\n\n");
1135 		exit(EX_TEMPFAIL);
1136 	}
1137 	return;
1138 }
1139 
1140 
1141 
1142 
1143