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