"Fossies" - the Fresh Open Source Software Archive

Member "replace-2.24/replace.c" (4 Sep 2004, 16760 Bytes) of package /linux/privat/old/replace-2.24-src-11.11.tar.gz:


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 }