"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.

    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