"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 /* replace.c - The ideal program for sed-haters (i.e. everyone).
2 (C) Richard K. Lloyd 2001-2004
3
4 Provides a simple way of replacing strings in lines without resorting to
5 the ludicrous gibberish of sed, whose syntax was obviously dreamt up by
6 someone on LSD !
7
8 Syntax:
9 replace [qualifiers] <oldstr> <newstr> [-a <oldstr> <newstr> [-a...]]
10 [filename...]
11
12 What "replace" does by default:
13
14 It performs a case-insensitive search for the "word" <oldstr> in each text
15 line and replaces it with <newstr>, which is case-adjusted to match the
16 case of the particular occurrence of <oldstr> in the original line. A "word"
17 is defined to be a string that is preceded AND followed by a non-
18 alphanumeric character (start and end of lines count as such a character)
19 in the original text line. Multiple strings can be replaced in order using
20 the "-a" qualifier.
21
22 The qualifiers modify this behaviour appropriately (try "replace -?"). */
23
24 #define Extern extern
25 #include "replace.h"
26
27 /* Routines only called from this source file and nowhere else */
28 extern int binary_io(P2(FILE *,FILE *)),text_io(P2(FILE *,FILE *)),
29 is_binary(P(FILE *));
30 extern void init_vars(P(void));
31 extern void get_options(P2(int,char **));
32
33 /* Need to track original pwd if following soft links */
34 static char curpwd[BUFSIZ];
35
36 #ifdef __STDC__
37 int ask_user(int pval,char *askname)
38 #else
39 int ask_user(pval,askname)
40 int pval;
41 char *askname;
42 #endif
43 {
44 /* Ask the user if they want to replace a string (or strings) */
45 int invalid=(prompt==pval);
46 while (invalid)
47 {
48 FILE *thand;
49 char tbuff[BUFSIZ]; tbuff[0]='\0';
50 if ((thand=fopen(TERMINAL_DEV,"r"))==(FILE *)NULL)
51 leave("Can't open " TERMINAL_DEV " - aborted");
52 (void)fprintf(stderr,"Replace %s (y/n/a/q) ? [y] ",askname);
53 if (fgets(tbuff,(int)BUFSIZ,thand)==(char *)NULL)
54 leave("Can't read terminal input - aborted");
55 (void)fclose(thand);
56 switch (tbuff[0])
57 {
58 case 'Y':
59 case 'y':
60 case '\n':
61 (void)fprintf(stderr,"OK, will replace %s\n",askname);
62 invalid=0;
63 break;
64 case 'N':
65 case 'n':
66 (void)fprintf(stderr,"Skipped replace of %s\n",askname);
67 return(0);
68 case 'A':
69 case 'a':
70 (void)fprintf(stderr,"OK, prompting has been turned off.\n");
71 (void)fprintf(stderr,"All appropriate strings will be replaced in all specified files\n");
72 prompt=0;
73 return(1);
74 case 'Q':
75 case 'q':
76 leave("Program quit at user's request");
77 break;
78 default:
79 (void)fprintf(stderr,"Invalid response - please try again\n");
80 break;
81 }
82
83 }
84 return(1);
85 }
86
87 #ifdef __STDC__
88 void plural(char *str,LONG_LONG value)
89 #else
90 void plural(str,value)
91 char *str;
92 LONG_LONG value;
93 #endif
94 {
95 /* Nicely format a numeric message. Note the kludgy check for <10 here -
96 this is so that the switch value can be cast down to a long safely.
97 Yep, dumb HP-UX 11.00 HP ANSI C compiler refuses to switch on a
98 long long value <rolls eyes upwards> */
99 if (value<10)
100 switch ((long)value)
101 {
102 case 0: (void)fprintf(stderr,"no %ss",str); break;
103 case 1: (void)fprintf(stderr,"one %s",str); break;
104 case 2: (void)fprintf(stderr,"two %ss",str); break;
105 case 3: (void)fprintf(stderr,"three %ss",str); break;
106 case 4: (void)fprintf(stderr,"four %ss",str); break;
107 case 5: (void)fprintf(stderr,"five %ss",str); break;
108 case 6: (void)fprintf(stderr,"six %ss",str); break;
109 case 7: (void)fprintf(stderr,"seven %ss",str); break;
110 case 8: (void)fprintf(stderr,"eight %ss",str); break;
111 default: (void)fprintf(stderr,"nine %ss",str); break;
112 }
113 else (void)fprintf(stderr,LONG_LONG_FORMAT " %ss",value,str);
114 }
115
116 #ifdef __STDC__
117 static int read_lines(FILE *fd,FILE *fdout,LONG_LONG filesize,char *filename)
118 #else
119 static int read_lines(fd,fdout,filesize,filename)
120 FILE *fd,*fdout;
121 LONG_LONG filesize;
122 char *filename;
123 #endif
124 {
125 /* Handle a whole file. Returns 0 = success, != 0 for failure */
126 int gotfail,this_binary=is_binary(fd);
127 if (verbose==2)
128 {
129 (void)fprintf(stderr,"Scanning %s %s",filetype,filename);
130 if (filesize>=0)
131 {
132 (void)fprintf(stderr," (");
133 plural("byte",filesize);
134 (void)fprintf(stderr,")");
135 }
136 (void)fprintf(stderr,"...\n");
137 }
138 linenum=0; repcount=0; numfiles++;
139 if (this_binary) gotfail=binary_io(fd,fdout);
140 else gotfail=text_io(fd,fdout);
141 if (verbose && !gotfail)
142 {
143 if (verbose==2 || repcount)
144 {
145 (void)fprintf(stderr,"The %s %s ",filetype,intitle);
146 if (!this_binary)
147 {
148 (void)fprintf(stderr,"contained ");
149 plural("line",(LONG_LONG)linenum);
150 (void)fprintf(stderr," and\n");
151 }
152 if (fake) (void)fprintf(stderr,"would have ");
153 (void)fprintf(stderr,"had ");
154 if (hex) plural("binary hex string",(LONG_LONG)repcount);
155 else plural("string",(LONG_LONG)repcount);
156 (void)fprintf(stderr," replaced");
157 if (!this_binary)
158 {
159 (void)fprintf(stderr," in ");
160 plural("line",(LONG_LONG)linecount);
161 }
162 (void)fprintf(stderr,"\n");
163 }
164 }
165 return(gotfail);
166 }
167
168 #ifdef __STDC__
169 static int rename_file(char *source,char *dest)
170 #else
171 static int rename_file(source,dest)
172 char *source,*dest;
173 #endif
174 {
175 /* Rename file source to file dest.
176 Returns 0 for failure, 1 for success */
177 if (rename(source,dest))
178 {
179 /* Note that the rename function CANNOT rename across filing systems,
180 but /bin/mv CAN, so might as well try /bin/mv if rename fails.
181 This is a weakness of "rpl -t" - if you set TMPDIR=/tmp, then
182 try to replace strings in a file not on the same disk as /tmp
183 using "rpl -t", you can't do it ! */
184 char command[BUFSIZ];
185 (void)snprintf(command,BUFSIZ,"%s \"%s\" \"%s\" 2>/dev/null",RENAME_COMMAND,source,dest);
186 if (system(command)) return(0);
187 }
188 return(1);
189 }
190
191 #ifdef __STDC__
192 static int update_file(char *oldname,char *newname,char *orgname)
193 #else
194 static int update_file(oldname,newname,orgname)
195 char *oldname,*newname,*orgname;
196 #endif
197 {
198 /* File needs to be updated and possibly a backup version kept */
199 struct stat finfo;
200 (void)stat(orgname,&finfo);
201 if (rename_file(oldname,newname))
202 {
203 updated=1;
204 if (verbose)
205 {
206 struct stat fst;
207 LONG_LONG new_file_size;
208 (void)stat(newname,&fst);
209 new_file_size=(LONG_LONG)fst.st_size;
210 (void)fprintf(stderr,"Updated %s %s (",filetype,newname);
211 plural("byte",new_file_size);
212 if (force) (void)fprintf(stderr,")\n");
213 else (void)fprintf(stderr," - backup with %s extension)\n",backupsuff);
214 }
215 if (chmod(newname,finfo.st_mode))
216 (void)fprintf(stderr,"WARNING: Couldn't set permissions of %s %s\n",filetype,newname);
217 if (chown(newname,finfo.st_uid,finfo.st_gid))
218 (void)fprintf(stderr,"WARNING: Couldn't set ownership of %s %s\n",filetype,newname);
219 if (retainstamp)
220 {
221 struct utimbuf oldtime;
222 oldtime.actime=finfo.st_atime;
223 oldtime.modtime=finfo.st_mtime;
224 if (utime(newname,&oldtime))
225 (void)fprintf(stderr,"WARNING: Couldn't retain timestamp of %s %s\n",filetype,newname);
226 }
227 return(1);
228 }
229 else return(0);
230 }
231
232 #ifdef __STDC__
233 static void replace_a_file(const char *thename)
234 #else
235 static void replace_a_file(thename)
236 char *thename;
237 #endif
238 {
239 /* Replace strings in a file, writing the new version to a temporary
240 file which is either then deleted (no replacements) or renamed onto
241 the original (some replacements) */
242 FILE *filedes,*fileout=(FILE *)NULL;
243 char repmess[BUFSIZ];
244
245 (void)strcpy(intitle,thename);
246 (void)snprintf(repmess,BUFSIZ,"strings in %s",intitle);
247 if (!ask_user(1,repmess)) return;
248 if ((filedes=fopen(intitle,"r"))==(FILE *)NULL)
249 (void)fprintf(stderr,"WARNING: Skipped %s - could not open\n",
250 intitle);
251 else
252 {
253 char tmpname[BUFSIZ];
254 int tmphandle=0;
255 (void)snprintf(tmpname,BUFSIZ,"%s/replace_tmp_XXXXXX",tmpdir);
256 if (!fake && (tmphandle=mkstemp(tmpname))==-1)
257 {
258 (void)fprintf(stderr,"WARNING: Skipped %s - couldn't create temp file %s\n",intitle,tmpname);
259 (void)fclose(filedes);
260 }
261 else
262 {
263 if (!fake) set_temp_file(tmpname);
264 updated=0;
265 if (!fake && (fileout=fdopen(tmphandle,"r+"))==(FILE *)NULL)
266 {
267 (void)fprintf(stderr,
268 "WARNING: Skipped %s - couldn't associate stream with temp file %s\n",
269 intitle,tmpname);
270 (void)fclose(filedes);
271 (void)close(tmphandle);
272 }
273 else
274 {
275 int gotfail;
276 struct stat fst;
277 LONG_LONG cur_file_size;
278
279 (void)stat(intitle,&fst);
280 cur_file_size=(LONG_LONG)fst.st_size;
281 gotfail=read_lines(filedes,fileout,cur_file_size,intitle);
282 if (!fake) gotfail|=fclose(fileout);
283 gotfail|=fclose(filedes);
284 if (gotfail)
285 (void)fprintf(stderr,"WARNING: Read/write/close failure when accessing %s - skipped\n",intitle);
286 else
287 if (repcount)
288 {
289 int goodone=1;
290 if (!fake)
291 {
292 if (force) goodone=update_file(tmpname,intitle,intitle);
293 else
294 {
295 char outtitle[BUFSIZ];
296 (void)strcpy(outtitle,intitle);
297 (void)strcat(outtitle,backupsuff);
298 if ((goodone=rename_file(intitle,outtitle)))
299 goodone=update_file(tmpname,intitle,outtitle);
300 }
301 }
302 if (goodone) { numfilereps++; numreps+=repcount; }
303 else (void)fprintf(stderr,"WARNING: Failed to update %s %s\n",filetype,intitle);
304 }
305 }
306 if (!fake && !updated) (void)unlink(tmpname);
307 if (!fake) set_temp_file("");
308 }
309 }
310 }
311
312 #ifdef __STDC__
313 static int do_branch(const char *fname,const struct stat *flstat,int fint)
314 #else
315 static int do_branch(fname,flstat,fint)
316 char *fname;
317 struct stat *flstat;
318 int fint;
319 #endif
320 {
321 /* Walk down a directory tree, replacing strings in appropriately
322 matching files */
323
324 #ifdef __STDC__
325 const
326 #endif
327 char *tmpfname=fname;
328 int suffmatch=0;
329 size_t flen,sloop;
330 /* The next line is just to make the filename neater */
331 if (!strncmp(tmpfname,"./",2)) { tmpfname+=2; }
332 flen=strlen(tmpfname);
333 switch (fint)
334 {
335 case FTW_F:
336 if (flen<sufflen ||
337 strncmp(&tmpfname[flen-sufflen],backupsuff,sufflen))
338 {
339 if (!suffixes) replace_a_file(tmpfname);
340 else
341 if (minsuff<=flen)
342 for (sloop=1;sloop<=suffixes && !suffmatch;sloop++)
343 {
344 char *sptr=sufflist[sloop];
345 size_t sxlen=strlen(sptr);
346 if (!strncasecmp(&tmpfname[flen-sxlen],sptr,sxlen))
347 replace_a_file(tmpfname);
348 }
349 }
350 break;
351 }
352 return(0);
353 }
354
355 #ifdef __STDC__
356 static void recurse_down(char *recdir)
357 #else
358 static void recurse_down(recdir)
359 char *recdir;
360 #endif
361 {
362 /* Recursive replacements requested */
363 if (verbose) (void)fprintf(stderr,"Recursing down %s dir tree\n",recdir);
364 if (ftw(recdir,do_branch,MAX_DIR_LEVELS))
365 (void)fprintf(stderr,"WARNING: Failed to recurse down %s dir tree\n",recdir);
366 }
367
368 static void finish_it(P(void))
369 {
370 /* All replacements/files done, so display a summary if in verbose mode 2 */
371 if (verbose==2 && numfiles>1)
372 {
373 (void)fprintf(stderr,"Number of files scanned: %d\n",numfiles);
374 (void)fprintf(stderr,"Number of files that ");
375 if (fake) (void)fprintf(stderr,"would have ");
376 (void)fprintf(stderr,"had strings replaced: %d\n",numfilereps);
377 (void)fprintf(stderr,"Number of ");
378 if (fake) (void)fprintf(stderr,"potential ");
379 (void)fprintf(stderr,"string replacements in total: %d\n",numreps);
380 }
381 tidy_up();
382 }
383
384 #ifdef __STDC__
385 static char *dirname(char *pname)
386 #else
387 static char *dirname(pname)
388 char *pname;
389 #endif
390 {
391 /* Get the directory name of the passed filename path */
392 static char dname[BUFSIZ],*dptr;
393 (void)strcpy(dname,pname);
394 if (!strcmp(pname,"/")) return("/");
395 if ((dptr=strrchr(dname,'/'))!=(char *)NULL)
396 {
397 *dptr='\0';
398 return(dname);
399 } else return(".");
400 }
401
402 #ifdef __STDC__
403 char *basename_path(char *pname)
404 #else
405 char *basename_path(pname)
406 char *pname;
407 #endif
408 {
409 /* Get the leafname of the passed filename path */
410 char *dptr;
411 if (!strcmp(pname,"/")) return("/");
412 if ((dptr=strrchr(pname,'/'))!=(char *)NULL)
413 return(&dptr[1]); else return(pname);
414 }
415
416 #ifdef __STDC__
417 static void do_the_file(char *fname,int sofar)
418 #else
419 static void do_the_file(fname,sofar)
420 char *fname;
421 int sofar;
422 #endif
423 {
424 /* Run appropriate function for the filename passed. Note that this
425 routine can be recursive (if followsoftlinks is on) */
426 struct stat finfo;
427 if (!lstat(fname,&finfo))
428 {
429 if (S_ISDIR(finfo.st_mode))
430 {
431 if (recursive) recurse_down(fname);
432 else (void)fprintf(stderr,"WARNING: Skipped %s directory - use -r for recursion\n",fname);
433 }
434 else
435 if (S_ISLNK(finfo.st_mode))
436 {
437 if (followsoftlinks)
438 {
439 char linkbuf[BUFSIZ];
440 if (readlink(fname,linkbuf,BUFSIZ)<0)
441 (void)fprintf(stderr,"WARNING: Can't read %s soft-link - skipped\n",fname);
442 else
443 if (sofar==MAX_SOFT_LINKS)
444 (void)fprintf(stderr,"WARNING: Soft-link loop (%s) detected - skipped\n",fname);
445 else
446 if (linkbuf[0]=='/')
447 do_the_file(linkbuf,sofar+1);
448 else
449 {
450 char linkdest[BUFSIZ],fdir[BUFSIZ];
451 (void)strcpy(fdir,dirname(fname));
452 (void)snprintf(linkdest,BUFSIZ,"%s/%s",fdir,dirname(linkbuf));
453 if (chdir(linkdest) || getcwd(linkdest,BUFSIZ)==(char *)NULL)
454 {
455 (void)fprintf(stderr,"WARNING: Soft-link (%s) to non-existent dir - skipped\n",fname);
456 (void)chdir(curpwd);
457 }
458 else
459 {
460 (void)chdir(curpwd);
461 (void)strcat(linkdest,"/");
462 (void)strcat(linkdest,basename_path(linkbuf));
463 do_the_file(linkdest,sofar+1);
464 }
465 }
466 }
467 else (void)fprintf(stderr,"WARNING: Soft-link (%s) detected - skipped\n",fname);
468 }
469 else
470 if (S_ISREG(finfo.st_mode))
471 {
472 if (finfo.st_size) replace_a_file(fname);
473 else
474 (void)fprintf(stderr,"WARNING: %s is zero-length - skipped\n",fname);
475 }
476 else (void)fprintf(stderr,"WARNING: %s is not a file/dir - skipped\n",fname);
477 }
478 else (void)fprintf(stderr,"WARNING: No such file/dir (%s) - skipped\n",fname);
479 }
480
481 void check_tmp_dir_exists(P(void))
482 {
483 /* Make sure tmpdir is actually a directory - it's fatal if it isn't,
484 because we can't do any replacements in files without it */
485 struct stat tmpdirinfo;
486 if (stat(tmpdir,&tmpdirinfo) || !S_ISDIR(tmpdirinfo.st_mode))
487 leave("Temporary directory not found");
488 }
489
490 #ifdef __STDC__
491 int main(int argc,char **argv)
492 #else
493 int main(argc,argv)
494 int argc;
495 char **argv;
496 #endif
497 {
498 /* Parse command line options and then either read from stdin/send to
499 stdout, recurse down or do replacements on specified files */
500 init_vars();
501 get_options(argc,argv);
502 if (optind==argc || !strcmp(argv[optind],"-"))
503 {
504 if (recursive)
505 {
506 check_tmp_dir_exists();
507 recurse_down(".");
508 }
509 else
510 {
511 if (ask_user(1,"strings in standard input"))
512 {
513 (void)strcpy(intitle,"<stdin>");
514 if (read_lines(stdin,stdout,(LONG_LONG)-1,intitle))
515 (void)fprintf(stderr,"WARNING: Read/write error during stdin/stdout operations - aborted\n");
516 }
517 }
518 }
519 else
520 {
521 if (followsoftlinks)
522 {
523 if (getcwd(curpwd,BUFSIZ)==(char *)NULL)
524 leave("Can't determine current working directory");
525 }
526 check_tmp_dir_exists();
527 for (;optind<argc;optind++) do_the_file(argv[optind],0);
528 }
529 finish_it();
530 return(0);
531 }