"Fossies" - the Fresh Open Source Software Archive

Member "schily-2021-09-18/change/change.c" (20 Aug 2021, 12899 Bytes) of package /linux/privat/schily-2021-09-18.tar.bz2:


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. For more information about "change.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes reports: 2021-08-14_vs_2021-09-18 or 2021-07-29_vs_2021-09-18.

    1 /* @(#)change.c 1.47 21/08/20 Copyright 1985, 87-90, 95-99, 2000-2021 J. Schilling */
    2 #include <schily/mconfig.h>
    3 #ifndef lint
    4 static  UConst char sccsid[] =
    5     "@(#)change.c   1.47 21/08/20 Copyright 1985, 87-90, 95-99, 2000-2021 J. Schilling";
    6 #endif
    7 /*
    8  *  find pattern and substitute in files
    9  *
   10  *  Copyright (c) 1985, 87-90, 95-99, 2000-2021 J. Schilling
   11  */
   12 /*
   13  * The contents of this file are subject to the terms of the
   14  * Common Development and Distribution License, Version 1.0 only
   15  * (the "License").  You may not use this file except in compliance
   16  * with the License.
   17  *
   18  * See the file CDDL.Schily.txt in this distribution for details.
   19  * A copy of the CDDL is also available via the Internet at
   20  * http://www.opensource.org/licenses/cddl1.txt
   21  *
   22  * When distributing Covered Code, include this CDDL HEADER in each
   23  * file and include the License file CDDL.Schily.txt from this distribution.
   24  */
   25 
   26 #include <schily/stdio.h>
   27 #include <schily/varargs.h>
   28 #include <schily/stdlib.h>
   29 #include <schily/unistd.h>
   30 #include <schily/string.h>
   31 #include <schily/errno.h>
   32 #include <schily/stat.h>
   33 #   define  STATBUF     struct stat
   34 #   define  file_ino(sp)    ((sp)->st_ino)
   35 #   define  file_dev(sp)    ((sp)->st_dev)
   36 #include <schily/signal.h>
   37 #include <schily/standard.h>
   38 #include <schily/patmatch.h>
   39 #include <schily/utypes.h>
   40 #define GT_COMERR       /* #define comerr gtcomerr */
   41 #define GT_ERROR        /* #define error gterror   */
   42 #include <schily/schily.h>
   43 #include <schily/libport.h>
   44 #include <schily/nlsdefs.h>
   45 
   46 /*
   47  *  check for same file descriptor
   48  */
   49 #define samefile(sp, sp2)   (file_dev(sp1) == file_dev(sp2) && file_ino(sp1) == file_ino(sp2))
   50 
   51 #ifndef MAXLINE
   52 #define MAXLINE 8196
   53 #endif
   54 #define LINEINCR 1024
   55 #define MAXNAME 1024
   56 
   57 
   58 LOCAL   int Vflag = 0;
   59 LOCAL   int Cflag = 0;
   60 LOCAL   int Iflag = 0;
   61 LOCAL   int Nflag = 0;
   62 LOCAL   int CHcnt = MAXLINE;
   63 
   64 LOCAL   char    *oline;
   65 LOCAL   size_t  osize;
   66 LOCAL   char    *newline;
   67 LOCAL   size_t  newsize;
   68 LOCAL   char    tmpname[MAXNAME];
   69 LOCAL   int *aux;
   70 LOCAL   int *state;
   71 LOCAL   FILE    *tty;
   72 LOCAL   STATBUF ostat;  /* Old stat buf */
   73 LOCAL   STATBUF cstat;  /* Changed file stat buf */
   74 
   75 LOCAL   void    usage   __PR((int excode));
   76 LOCAL   RETSIGTYPE intr __PR((int signo));
   77 EXPORT  int main    __PR((int ac, char **av));
   78 LOCAL   int match   __PR((char *pat, int *auxp, char *linep, int off, int len, int alt));
   79 LOCAL   int change  __PR((char *name, FILE *ifile, FILE *ofile, char *pat, char *subst, int *auxp, int alt));
   80 LOCAL   int catsubst __PR((char *linep, char **newpp, int start, int end, char *subst, int idx, size_t *maxp));
   81 LOCAL   int appchar __PR((int c, char **bufp, int off, size_t *maxp));
   82 LOCAL   void    showchange __PR((char *name, char *linep, int start, int end, char *subst, int out, size_t max));
   83 LOCAL   BOOL    yes __PR((char *form, ...));
   84 LOCAL   void    mktmp   __PR((char *name));
   85 LOCAL   BOOL    mkbak   __PR((char *name));
   86 
   87 LOCAL void
   88 usage(excode)
   89     int excode;
   90 {
   91     error("Usage:   change [options] pattern substitution [file1...filen]\n");
   92     error("Options:\n");
   93     error(" -v  Display Text before change\n");
   94     error(" -c  Display Text after change\n");
   95     error(" -i  Interactive prompting for each change\n");
   96     error(" -#  Maximum number of changes per line\n");
   97     error(" -nobak  Do not create filename.bak\n");
   98     error(" -help   Print this help.\n");
   99     error(" -version Print version number.\n");
  100     error(" Standard input will be used if no files given.\n");
  101     exit(excode);
  102     /* NOTREACHED */
  103 }
  104 
  105 LOCAL RETSIGTYPE
  106 intr(signo)
  107     int signo;
  108 {
  109     if (tmpname[0] != '\0')
  110         unlink(tmpname);
  111     exit(signo);
  112     /* NOTREACHED */
  113 
  114     /*
  115      * Was ist wenn RETSIGTYPE == int ?
  116      */
  117 }
  118 
  119 EXPORT int
  120 main(ac, av)
  121     int ac;
  122     char    **av;
  123 {
  124     FILE    *file;
  125     FILE    *tfile;
  126     char    *pat;
  127     int alt;
  128     char    *subst;
  129     int len;
  130     int cnt;
  131     char    *opt = "help,version,v,c,i,#,nobak,n";
  132     BOOL    help = FALSE;
  133     BOOL    prversion = FALSE;
  134     BOOL    closeok;
  135     int cac;
  136     char    * const *cav;
  137 
  138     save_args(ac, av);
  139 
  140     (void) setlocale(LC_ALL, "");
  141 
  142 #ifdef  USE_NLS
  143 #if !defined(TEXT_DOMAIN)   /* Should be defined by cc -D */
  144 #define TEXT_DOMAIN "change"    /* Use this only if it weren't */
  145 #endif
  146     { char  *dir;
  147     dir = searchfileinpath("share/locale", F_OK,
  148                     SIP_ANY_FILE|SIP_NO_PATH, NULL);
  149     if (dir)
  150         (void) bindtextdomain(TEXT_DOMAIN, dir);
  151     else
  152 #if defined(PROTOTYPES) && defined(INS_BASE)
  153     (void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale");
  154 #else
  155     (void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale");
  156 #endif
  157     (void) textdomain(TEXT_DOMAIN);
  158     }
  159 #endif  /* USE_NLS */
  160 
  161 #ifdef  SIGHUP
  162     signal(SIGHUP, intr);
  163 #endif
  164 #ifdef  SIGINT
  165     signal(SIGINT, intr);
  166 #endif
  167 #ifdef  SIGTERM
  168     signal(SIGTERM, intr);
  169 #endif
  170 
  171     cac = --ac;
  172     cav = ++av;
  173 
  174     if (getallargs(&cac, &cav, opt,
  175                     &help, &prversion,
  176                     &Vflag, &Cflag, &Iflag,
  177                     &CHcnt,
  178                     &Nflag, &Nflag) < 0) {
  179         error("Bad flag: '%s'\n", cav[0]);
  180         usage(EX_BAD);
  181     }
  182     if (help) usage(0);
  183     if (prversion) {
  184         gtprintf("Change release %s %s (%s-%s-%s) Copyright (C) 1985, 87-90, 95-99, 2000-2021 %s\n",
  185                 "1.47", "2021/08/20",
  186                 HOST_CPU, HOST_VENDOR, HOST_OS,
  187                 _("Jörg Schilling"));
  188         exit(0);
  189     }
  190 
  191     if (Iflag) Cflag = 0;
  192 
  193     cac = ac;
  194     cav = av;
  195     if (getfiles(&cac, &cav, opt) <= 0) {
  196         error("No pattern or substitution given.\n");
  197         usage(EX_BAD);
  198     }
  199     pat = cav[0];
  200     cac--, cav++;
  201     len = strlen(pat);
  202 
  203     /*
  204      * Cygwin32 (and newer Linux versions too) make
  205      * stdin/stdout/stderr non constant expressions so we cannot do
  206      * loader initialization.
  207      *
  208      * XXX May this be a problem?
  209      */
  210     tty = stdin;
  211 
  212     aux = malloc(sizeof (int)*len);
  213     state = malloc(sizeof (int)*(len+1));
  214     if (aux == NULL || state == NULL)
  215         comerrno(EX_BAD, "No memory for pattern compiler");
  216 
  217     if ((alt = patcompile((unsigned char *)pat, len, aux)) == 0)
  218         comerrno(EX_BAD, "Bad pattern '%s'.\n", pat);
  219 
  220     if (getfiles(&cac, &cav, opt) <= 0) {
  221         error("No substitution given.\n");
  222         usage(EX_BAD);
  223     }
  224     subst = cav[0];
  225 
  226     cac--, cav++;
  227 
  228     if (getfiles(&cac, &cav, opt) <= 0) {
  229         /*
  230          * If no args, change is a filter from stdin to stdout.
  231          */
  232         if (Iflag) {
  233 #ifdef  HAVE__DEV_TTY
  234             if ((tty = fileopen("/dev/tty", "r")) == NULL)
  235                 comerr("Can't open '/dev/tty'\n");
  236 #else
  237             tty = stderr;
  238 #endif
  239         }
  240         (void) change("", stdin, stdout, pat, subst, aux, alt);
  241         flush();
  242     } else for (; getfiles(&cac, &cav, opt) > 0; cac--, cav++) {
  243         file = fileopen(cav[0], "r");
  244         if (file == NULL) {
  245             errmsg("Can't open '%s'.\n", cav[0]);
  246         } else {
  247             mktmp(cav[0]);
  248             if ((tfile = fileopen(tmpname, "wct")) == NULL) {
  249                 errmsg("Can't open '%s'.\n", tmpname);
  250             } else {
  251 #ifdef  HAVE_FSYNC
  252                 int err;
  253                 int scnt;
  254 #endif
  255                 stat(cav[0], &ostat);
  256                 stat(tmpname, &cstat);
  257                 chmod(tmpname, ostat.st_mode);
  258                 cnt = change(cav[0], file, tfile, pat, subst,
  259                             aux, alt);
  260                 fclose(file);
  261 
  262                 closeok = TRUE;
  263                 if (fflush(tfile) != 0)
  264                     closeok = FALSE;
  265 #ifdef  HAVE_FSYNC
  266                 err = 0;
  267                 scnt = 0;
  268                 do {
  269                     if (fsync(fdown(tfile)) != 0)
  270                         err = geterrno();
  271 
  272                     if (err == EINVAL)
  273                         err = 0;
  274                 } while (err == EINTR && ++scnt < 10);
  275                 if (err != 0)
  276                     closeok = FALSE;
  277 #endif
  278                 if (fclose(tfile) != 0)
  279                     closeok = FALSE;
  280 
  281                 if (!closeok && cnt > 0)
  282                     errmsg("Problems flushing outfile for '%s'.\n",
  283                             cav[0]);
  284 
  285                 if (closeok && cnt > 0 && mkbak(cav[0]))
  286                     chmod(cav[0], ostat.st_mode);
  287                 else
  288                     unlink(tmpname);
  289             }
  290         }
  291     }
  292     exit(0);
  293     /* NOTREACHED */
  294     return (0); /* Keep lint happy */
  295 }
  296 
  297 /*
  298  * Return index of first char after matching pattern (if any) in line.
  299  * If no match, return (-1).
  300  * Start from position off in linep.
  301  */
  302 LOCAL int
  303 match(pat, auxp, linep, off, len, alt)
  304     char    *pat;
  305     int *auxp;
  306     char    *linep;
  307     int off;
  308     int len;
  309     int alt;
  310 {
  311     char    *p;
  312 
  313     p = (char *)patmatch((unsigned char *)pat, auxp,
  314                     (unsigned char *)linep, off, len, alt, state);
  315     if (p == NULL)
  316         return (-1);
  317     else
  318         return (p-linep);
  319 }
  320 
  321 /*
  322  * Copy ifile to ofile, replace pattern with substitution.
  323  */
  324 LOCAL int
  325 change(name, ifile, ofile, pat, subst, auxp, alt)
  326     char    *name;
  327     FILE    *ifile;
  328     FILE    *ofile;
  329     char    *pat;
  330     char    *subst;
  331     int *auxp;
  332     int alt;
  333 {
  334     register char   *linep;
  335     int idx;
  336     int cnt = 0;
  337     int matend;
  338     int lastmend;
  339     int out;
  340     ssize_t llen;
  341     int changed;
  342     BOOL    hasnl;
  343 
  344     while ((llen = fgetaline(ifile, &oline, &osize)) > 0) {
  345         linep = oline;
  346         if (linep[llen-1] == '\n') {
  347             linep[--llen] = '\0';
  348             hasnl = TRUE;
  349         } else {
  350             hasnl = FALSE;
  351         }
  352 
  353         changed = 0;
  354         out = 0;
  355         lastmend = -1;
  356         for (idx = 0; linep[idx] != 0; ) {
  357             matend = match(pat, auxp, linep, idx, llen, alt);
  358             if (matend >= 0 && matend != lastmend) {
  359                 lastmend = matend;
  360                 if (changed >= CHcnt) {
  361                     matend = llen;
  362                     out = catsubst(linep, &newline, idx, matend, "&", out, &newsize);
  363                     break;
  364                 }
  365                 if (Vflag && !changed)
  366                     fprintf(stderr, "%s%s%s\n",
  367                         name, *name?": ":"", linep);
  368                 if (Iflag) {
  369                     showchange(name, linep, idx, matend, subst, out, llen);
  370                     if (!yes("OK? ")) {
  371                         out = catsubst(linep, &newline, idx, matend, "&", out, &newsize);
  372                         idx = matend;
  373                         continue;
  374                     }
  375                 }
  376                 out = catsubst(linep, &newline, idx, matend, subst, out, &newsize);
  377                 cnt++;
  378                 changed++;
  379             }
  380             if (matend == -1 || matend == idx)
  381                 out = appchar(linep[idx++], &newline, out, &newsize);
  382             else
  383                 idx = matend;
  384         }
  385         if (appchar('\0', &newline, out, &newsize) < 0) {
  386             errmsgno(EX_BAD, "Output line truncated: %s\n", newline);
  387             /*
  388              * Abort if not in filter mode.
  389              */
  390             if (ofile != stdout)
  391                 return (-1);
  392         }
  393         fprintf(ofile, "%s%s", newline, hasnl?"\n":"");
  394         if (Cflag && changed)
  395             fprintf(stderr, "%s%s%s\n", name, *name?": ":"", newline);
  396     }
  397     if (llen < 0 && !feof(ifile)) {
  398         errmsg("Input read error on '%s'.\n", *name?name:"stdin");
  399         return (-1);
  400     }
  401     return (cnt);
  402 }
  403 
  404 /*
  405  * Concatenate substitution to current version of new line.
  406  */
  407 LOCAL int
  408 catsubst(linep, newpp, start, end, subst, idx, maxp)
  409     char    *linep;
  410     char    **newpp;
  411     int start;
  412     int end;
  413     char    *subst;
  414     int idx;
  415     size_t  *maxp;
  416 {
  417     int i;
  418 
  419     while (*subst != '\0') {
  420         if (*subst == '&') {
  421             for (i = start; i < end; i++)
  422                 idx = appchar(linep[i], newpp, idx, maxp);
  423             subst++;
  424         } else {
  425             if (*subst == '\\')
  426                 subst++;
  427             idx = appchar(*subst++, newpp, idx, maxp);
  428         }
  429     }
  430     return (idx);
  431 }
  432 
  433 /*
  434  * Append a character to the buffer, use position off.
  435  */
  436 LOCAL int
  437 appchar(c, bufp, off, maxp)
  438     char    c;
  439     char    **bufp;
  440     int off;
  441     size_t  *maxp;
  442 {
  443     size_t  bufsize = *maxp;
  444 
  445     if (off < 0)
  446         return (-1);
  447     if (off >= bufsize) {
  448         char    *newbuf;
  449 
  450         while (bufsize <= off)
  451             bufsize += LINEINCR;
  452         newbuf = realloc(*bufp, bufsize);
  453         if (newbuf == NULL) {
  454             errmsg("No memory to append to current line.\n");
  455             return (-1);
  456         }
  457         *bufp = newbuf;
  458         *maxp = bufsize;
  459     }
  460     (*bufp)[off++] = c;
  461     return (off);
  462 }
  463 
  464 LOCAL void
  465 showchange(name, linep, start, end, subst, out, max)
  466     char    *name;
  467     char    *linep;
  468     int start;
  469     int end;
  470     char    *subst;
  471     int out;
  472     size_t  max;
  473 {
  474 static  char    *tmp = NULL;
  475 static  size_t  tmpsize = 0;
  476     char    *new;
  477 
  478     max++;
  479     while (tmpsize < max)
  480         tmpsize += LINEINCR;
  481     new = realloc(tmp, tmpsize);
  482     if (new == NULL) {
  483         errmsg("No memory for change preview.\n");
  484         return;
  485     }
  486 
  487     tmp = new;
  488     movebytes(newline, tmp, max);
  489     out = catsubst(linep, &tmp, start, end, subst, out, &tmpsize);
  490     if (appchar('\0', &tmp, out, &tmpsize) < 0)
  491         error("Line will be truncated!\n");
  492     fprintf(stderr, "%s%s%s%s\n", name, *name?": ":"", tmp, &linep[end]);
  493 }
  494 
  495 /* VARARGS1 */
  496 #ifdef  PROTOTYPES
  497 LOCAL BOOL
  498 yes(char *form, ...)
  499 #else
  500 LOCAL BOOL
  501 yes(form, va_alist)
  502     char    *form;
  503     va_dcl
  504 #endif
  505 {
  506     va_list args;
  507     char    okbuf[10];
  508 
  509 #ifdef  PROTOTYPES
  510     va_start(args, form);
  511 #else
  512     va_start(args);
  513 #endif
  514     fprintf(stderr, "%r", form, args);
  515     va_end(args);
  516     flush();
  517     fgetline(tty, okbuf, sizeof (okbuf));
  518     if (streql(okbuf, "y") || streql(okbuf, "yes"))
  519         return (TRUE);
  520     else
  521         return (FALSE);
  522 }
  523 
  524 LOCAL void
  525 mktmp(name)
  526     char    *name;
  527 {
  528     char    *p  = NULL;
  529     char    *p2 = name;
  530     char    c = '\0';
  531 
  532     /*
  533      * Extract dir from name
  534      */
  535     while (*p2) {
  536 #ifdef  tos
  537         if (*p2++ == '\\')
  538 #else
  539         if (*p2++ == '/')
  540 #endif
  541             p = p2;
  542     }
  543     if (p) {
  544         c = *p;
  545         *p = '\0';
  546     } else {
  547         name  = "";
  548     }
  549     js_snprintf(tmpname, sizeof (tmpname), "%sch%llo", name, (Llong)getpid());
  550     if (p)
  551         *p = c;
  552 }
  553 
  554 LOCAL BOOL
  555 mkbak(name)
  556     char    *name;
  557 {
  558     char    bakname[MAXNAME];
  559 
  560     if (!Nflag) {
  561         /*
  562          * Try to create a backup file.
  563          */
  564         if (strlen(name) > (MAXNAME-5) &&
  565             strchr(&name[MAXNAME-5], '/') != NULL) {
  566             errmsgno(EX_BAD, "Cannot backup '%s'; name too long\n", name);
  567             return (FALSE);
  568         }
  569 
  570         strncpy(bakname, name, MAXNAME-5);
  571         bakname[MAXNAME-5] = '\0';
  572         strcat(bakname, ".bak");
  573 
  574         if (rename(name, bakname) < 0) { /* make cur file .bak  */
  575 
  576             errmsg("Cannot backup '%s'\n", name);
  577             return (FALSE);
  578         }
  579     }
  580 
  581     if (rename(tmpname, name) < 0) {    /* rename new file  */
  582         errmsg("Cannot rename '%s' to '%s'\n", tmpname, name);
  583         if (!Nflag) {
  584             /*
  585              * Try to make .bak current again.
  586              */
  587             if (rename(bakname, name) < 0)
  588                 errmsg("Cannot rename backup '%s' back to '%s'\n",
  589                     bakname, name);
  590         }
  591         return (FALSE);
  592     }
  593     return (TRUE);
  594 }
  595