home *** CD-ROM | disk | FTP | other *** search
- Path: uunet!rs
- From: rs@uunet.UU.NET (Rich Salz)
- Newsgroups: comp.sources.unix
- Subject: v11i007: A query-replace program
- Message-ID: <897@uunet.UU.NET>
- Date: 13 Aug 87 22:37:41 GMT
- Organization: UUNET Communications Services, Arlington, VA
- Lines: 733
- Approved: rs@uunet.UU.NET
-
- Submitted-by: der Mouse <mouse@larry.ee.mcgill>
- Posting-number: Volume 11, Issue 7
- Archive-name: qsubst
-
- [ This is a program to do large amounts of query-replaces on a bunch
- of files. I spliced the Makefile into the shar. --r$ ]
-
- A useful little program. Designed one evening when someone wanted to
- rename a few routines which were used in umpteen zillion places all
- over a bunch of files. This code is known to work on mtXinu 4.3+NFS
- and on Sunix 3.0; I would expect it to work on BSD systems in general.
- I have no idea how easy porting to a USG (SysIII, SysV) environment
- would be; we don't have USG systems around here.
-
- #! /bin/sh
- #
- # Shar: Shell Archiver
- #
- # Run this through sh to create:
- # Makefile
- # qsubst.1
- # qsubst.c
- echo x - Makefile \(some number of characters\)
- sed 's/^X//' > Makefile << \SHAR_EOF
- all: qsubst
- install: qsubst qsubst.1
- @echo copy qsubst and qsubst.1 to appropriate directories.
- qsubst: qsubst.c
- $(CC) $(CFLAGS) -o qsubst qsubst.c
- SHAR_EOF
- echo No error check because the moderator is lazy...
- echo x - qsubst.1 \(4752 characters\)
- sed 's/^X//' > qsubst.1 << \SHAR_EOF
- X.TH QSUBST 1 "31 March 1985"
- X.UC 4
- X.SH NAME
- Xqsubst - query/substitute strings in files
- X.SH SYNOPSIS
- X.nf
- X.ft B
- Xqsubst string1 string2 [ options / files ]
- X.PP
- X.fi
- X.SH DESCRIPTION
- X\fIQsubst\fP is designed for substituting strings in (large) files. It
- Xaccepts a list of file names and two strings. For each of the files,
- X\fIqsubst\fP modifies it in-place to replace \fIstring1\fP with \fIstring2\fP
- Xwherever the user approves the change.
- X.PP
- XEach time \fIqsubst\fP finds the \fIstring1\fP in a file, it prints a few
- Xlines of context (see the -c option below) and expects the user to type a
- Xcharacter to indicate whether or not the replacement should be done. The
- Xuser can type:
- X.TP 8
- X space
- XReplace this instance and go on to the next one.
- X.TP
- X .
- XReplace this instance but don't do any more in this file (ie, go on to the
- Xnext file).
- X.TP
- X n
- XDon't change this instance, just go on to the next one.
- X.TP
- X ^G
- XDon't change this instance or any more in this file, just go on to the next
- Xfile.
- X.TP
- X !
- XChange this instance and all further ones in this file without asking, then
- Xgo on to the next file (for which \fIqsubst\fP will begin asking again).
- X.PP
- XThe arguments preceding the strings are interpreted as filenames (which
- X\fIqsubst\fP then performs replacements in) if they do not begin with \-
- Xsigns. If they do, they are interpreted as follows:
- X.TP 8
- X\-!
- X.br
- X.ns
- X.TP
- X\-go
- X.br
- X.ns
- X.TP
- X\-noask
- XDon't bother asking, just replace. This is like hitting ! as soon as the
- Xfirst instance appears for each file except that nothing is printed. This is
- Xin effect for all files encountered until a \-nogo or \-ask option is given.
- XNote that if you use csh, you will probably have to backslash the !, which is
- Xwhy the alternative forms exist.
- X.TP
- X\-nogo
- X.br
- X.ns
- X.TP
- X\-ask
- XNegate a \-go or \-noask option. \fIQsubst\fP will once more prompt the user
- Xas usual.
- X.TP
- X\-CA\fIN\fP
- XGive \fIN\fP lines of context above the line with the match when prompting
- Xthe user.
- X.TP
- X\-CB\fIN\fP
- XGive \fIN\fP lines of context below the line with the match when prompting
- Xthe user.
- X.TP
- X\-c\fIN\fP
- XGive \fIN\fP lines of context above and as many below the line with the
- Xmatch when prompting the user (equivalent to \-CA\fIN\fP \-CB\fIN\fP).
- X.TP
- X.SM
- X\-f \fIfilename\fP
- XSame as a simple \fIfilename\fP argument, but is useful if the file name
- Xbegins with a \-.
- X.TP
- X.SM
- X\-F \fIfilename\fP
- X\fIQsubst\fP reads file names from the file, one per line, and performs
- Xsubstitutions on the files whose names it reads thus.
- X.SH ERRORS
- X\fIQsubst\fP can generate the following error messages:
- X.PP
- XUsage: qsubst str1 str2 [ \-! \-noask \-go \-f file \-F file ]
- X.PP
- XThis is a usage message printed if fewer than two arguments are given.
- X.PP
- Xqsubst: argument order has changed, it's now: str1 str2 files...
- X.PP
- XQsubst used to take arguments differently; the two strings were the *last*
- Xtwo arguments rather than the first two. This message is printed if the
- Xfirst argument is a valid file name but the last two are not - qsubst assumes
- Xthat you didn't know of the change yet. However, qsubst continues as though
- Xwhat you gave were actually what you meant.
- X.PP
- Xqsubst: cannot create temp file /tmp/qsubst.012345
- X.PP
- X(The number may vary). This message is generated if \fIqsubst\fP cannot open
- Xits temporary file (the name appears in the message). Something is seriously
- Xwrong with either \fIqsubst\fP or /tmp.
- X.PP
- Xqsubst: search string too long (max 512 chars)
- X.PP
- XThis is surely self-explanatory.
- X.PP
- Xqsubst: \-C must be \-CA or \-CB
- X.PP
- XYou gave \-C, but it was neither \-CA nor \-CB.
- X.PP
- Xqsubst: \-f what?
- X.br
- Xqsubst: \-F what?
- X.PP
- XYou gave \-f (or -\F) without a file name.
- X.PP
- Xqsubst: cannot read \fIfilename\fP
- X.PP
- X\fIQsubst\fP couldn't open the named file for read-only.
- X.PP
- Xqsubst: cannot rewrite \fIfilename\fP
- X.PP
- X\fIQsubst\fP couldn't open the named file read/write for substitution.
- X.SH NOTES
- X\fIQsubst\fP reads its argument list in order and processes files as it comes
- Xto them. Hence (for instance), a \-go option will affect only files named
- Xafter the \-go option.
- X.PP
- XThe most context you can get is \-c10.
- X.PP
- XThe search string (\fIstring1\fP) is limited to 512 characters. There is no
- Xsimilar limit on the length of the replacement string (\fIstring2\fP).
- X.PP
- XNulls in the file may cause \fIqsubst\fP to make various mistakes.
- X.PP
- XIf any other program modifies the file while \fIqsubst\fP is running, all
- Xbets are off.
- X.PP
- X\fIQsubst\fP expect the files it is performing substitutions in to be real
- Xfiles, that is, objects on which \fIlseek(2)\fP works sensibly. Not, for
- Xexample, terminals or pipes.
- X.PP
- X\fIQsubst\fP is careful to modify files only when necessary, so it interacts
- Xnicely with \fImake(1)\fP.
- X.SH AUTHOR
- Xder Mouse (Mike Parker, mcgill-vision!mouse), March, 1985.
- SHAR_EOF
- if test 4752 -ne "`wc -c qsubst.1`"
- then
- echo shar: error transmitting qsubst.1 \(should have been 4752 characters\)
- fi
- echo x - qsubst.c \(10813 characters\)
- sed 's/^X//' > qsubst.c << \SHAR_EOF
- X/*
- X * Qsubst -- designed for renaming routines existing in a whole bunch of
- X * files. Must be compiled with -ltermcap.
- X *
- X * Usage:
- X *
- X * qsubst str1 str2 [ options ]
- X *
- X * Qsubst reads its options (see below) to get a list of files. For each
- X * file on this list, it then replaces str1 with str2 wherever possible in
- X * that file, depending on user input (see below). The result is written
- X * back onto the original file.
- X *
- X * For each possible substitution, the user is prompted with a few lines
- X * before and after the line containing the string to be substituted. The
- X * string itself is underlined, if the terminal supports this. Then one
- X * character is read from the terminal. This is then interpreted as
- X * follows (this is designed to be like Emacs' query-replace-string):
- X *
- X * space replace this occurrence and go on to the next one
- X * . replace this occurrence and don't change any more in this
- X * file (ie, go on to the next file).
- X * , tentatively replace this occurrence. The lines as they would
- X * look if the substitution were made are printed out. Then
- X * another character is read and it is used to decide the
- X * result (possibly undoing the tentative replacement).
- X * n don't change this one, but go on to the next one
- X * ^G don't change this one or any others in this file, but instead
- X * go on to the next file.
- X * ! change the rest in this file without asking, then go on to
- X * the next file (at which point qsubst will start asking
- X * again).
- X * ? print out the current filename and ask again.
- X *
- X * The first two arguments to qsubst are always the string to replace and the
- X * string to replace it with. The options are as follows:
- X *
- X * -! Enter ! mode automatically at the beginning of each file
- X * -go Same as -!
- X * -noask Same as -!
- X * -nogo Negate -go
- X * -ask Negate -noask (same as -nogo)
- X * -cN (N is a number) Give N lines of context above and below the
- X * line with the match when prompting the user.
- X * -CAN (N is a number) Give N lines of context above the line with
- X * the match when prompting the user.
- X * -CBN (N is a number) Give N lines of context below the line with
- X * the match when prompting the user.
- X * -f filename
- X * The filename following the -f argument is one of the files
- X * qsubst should perform substitutions in.
- X * -F filename
- X * Qsubst should read the named file to get the names of files
- X * to perform substitutions in. The names should appear one to
- X * a line.
- X *
- X * The default amount of context is -c2, that is, two lines above and two
- X * lines below the line with the match.
- X *
- X * Arguments not beginning with a - sign in the options field are implicitly
- X * preceded by -f. Thus, -f is really needed only when the file name begins
- X * with a - sign.
- X *
- X * Qsubst reads its options in order and processes files as it gets them.
- X * This means, for example, that a -go will affect only files from -f or -F
- X * options appearing after the -go option.
- X *
- X * The most context you can get is ten lines each, above and below
- X * (corresponding to -c10).
- X *
- X * Str1 is limited to 512 characters; there is no limit on the size of str2.
- X * Neither one may contain a null.
- X *
- X * Nulls in the file may cause qsubst to make various mistakes.
- X *
- X * If any other program modifies the file while qsubst is running, all bets
- X * are off.
- X */
- X#include <stdio.h>
- X#include <sgtty.h>
- X#include <signal.h>
- X#include <sys/file.h>
- X
- X#ifndef sigmask
- X#define sigmask(sig) (1<<((sig)-1))
- X#endif
- X
- X#define MAX_C_A 10
- X#define MAX_C_B 10
- X#define BUF_SIZ 1024
- X
- Xchar **argvec;
- X
- Xchar *getenv();
- Xchar *tgetstr();
- Xchar *malloc();
- Xchar *sindex();
- Xchar *mktemp();
- X
- XFILE *tempf;
- Xlong int tbeg;
- XFILE *workf;
- Xchar *str1;
- Xchar *str2;
- Xint s1l;
- Xint s2l;
- Xlong int nls[MAX_C_A+1];
- Xchar buf[BUF_SIZ*2];
- Xchar *buf1 = buf;
- Xchar *buf2 = buf + BUF_SIZ;
- Xint cabove;
- Xint cbelow;
- Xint flying;
- Xint flystate;
- Xint allfly;
- Xchar *nullstr = "";
- Xint ul_;
- Xchar *current_file;
- Xchar *beginul;
- Xchar *endul;
- Xchar tcp_buf[1024];
- Xchar cap_buf[1024];
- Xstruct sgttyb orig_sg;
- X
- Xtstp_self()
- X{
- X int (*old_tstp)();
- X int mask;
- X
- X mask = sigblock(0);
- X old_tstp = signal(SIGTSTP,SIG_DFL);
- X sigsetmask(mask&~sigmask(SIGTSTP));
- X kill(getpid(),SIGTSTP);
- X signal(SIGTSTP,old_tstp);
- X}
- X
- Xsigtstp()
- X{
- X struct sgttyb sg;
- X
- X if (ioctl(fileno(stdin),TIOCGETP,&sg) < 0)
- X { tstp_self();
- X return;
- X }
- X ioctl(fileno(stdin),TIOCSETN,&orig_sg);
- X tstp_self();
- X ioctl(fileno(stdin),TIOCSETN,&sg);
- X}
- X
- Xmain(ac,av)
- Xint ac;
- Xchar **av;
- X{
- X int skip;
- X char *cp;
- X
- X argvec = av;
- X if (ac < 3)
- X { fprintf(stderr,"Usage: %s str1 str2 [ -! -noask -go -f file -F file ]\n",
- X argvec[0]);
- X exit(1);
- X }
- X cp = getenv("TERM");
- X if (cp == 0)
- X { beginul = nullstr;
- X endul = nullstr;
- X }
- X else
- X { if (tgetent(tcp_buf,cp) != 1)
- X { beginul = nullstr;
- X endul = nullstr;
- X }
- X else
- X { cp = cap_buf;
- X if (tgetflag("os") || tgetflag("ul"))
- X { ul_ = 1;
- X }
- X else
- X { ul_ = 0;
- X beginul = tgetstr("us",&cp);
- X if (beginul == 0)
- X { beginul = tgetstr("so",&cp);
- X if (beginul == 0)
- X { beginul = nullstr;
- X endul = nullstr;
- X }
- X else
- X { endul = tgetstr("se",&cp);
- X }
- X }
- X else
- X { endul = tgetstr("ue",&cp);
- X }
- X }
- X }
- X }
- X cp = mktemp("/tmp/qsubst.XXXXXX");
- X tempf = fopen(cp,"w+");
- X if (tempf == NULL)
- X { fprintf(stderr,"%s: cannot create temp file %s\n",argvec[0],cp);
- X exit(1);
- X }
- X unlink(cp);
- X if ( (access(av[1],R_OK|W_OK) == 0) &&
- X (access(av[ac-1],R_OK|W_OK) < 0) &&
- X (access(av[ac-2],R_OK|W_OK) < 0) )
- X { fprintf(stderr,"\
- X%s: argument order has changed, it's now: str1 str2 files...\n",argvec[0]);
- X }
- X str1 = av[1];
- X str2 = av[2];
- X av += 2;
- X ac -= 2;
- X s1l = strlen(str1);
- X s2l = strlen(str2);
- X if (s1l > BUF_SIZ/2)
- X { fprintf(stderr,"%s: search string too long (max %d chars)\n",
- X argvec[0],BUF_SIZ/2);
- X exit(1);
- X }
- X ioctl(fileno(stdin),TIOCGETP,&orig_sg);
- X signal(SIGTSTP,sigtstp);
- X allfly = 0;
- X cabove = 2;
- X cbelow = 2;
- X skip = 0;
- X for (ac--,av++;ac;ac--,av++)
- X { if (skip > 0)
- X { skip --;
- X continue;
- X }
- X if (**av == '-')
- X { ++*av;
- X if ( (strcmp(*av,"!") == 0) ||
- X (strcmp(*av,"go") == 0) ||
- X (strcmp(*av,"noask") == 0) )
- X { allfly = 1;
- X }
- X else if ( (strcmp(*av,"nogo") == 0) ||
- X (strcmp(*av,"ask") == 0) )
- X { allfly = 0;
- X }
- X else if (**av == 'c')
- X { cabove = atoi(++*av);
- X cbelow = cabove;
- X limit_above_below();
- X }
- X else if (**av == 'C')
- X { ++*av;
- X if (**av == 'A')
- X { cabove = atoi(++*av);
- X limit_above_below();
- X }
- X else if (**av == 'B')
- X { cbelow = atoi(++*av);
- X limit_above_below();
- X }
- X else
- X { fprintf(stderr,"%s: -C must be -CA or -CB\n",argvec[0]);
- X }
- X }
- X else if ( (strcmp(*av,"f") == 0) ||
- X (strcmp(*av,"F") == 0) )
- X { if (++skip >= ac)
- X { fprintf(stderr,"%s: -%s what?\n",argvec[0],*av);
- X }
- X else
- X { if (**av == 'f')
- X { process_file(av[skip]);
- X }
- X else
- X { process_indir_file(av[skip]);
- X }
- X }
- X }
- X }
- X else
- X { process_file(*av);
- X }
- X }
- X exit(0);
- X}
- X
- Xlimit_above_below()
- X{
- X if (cabove > MAX_C_A)
- X { cabove = MAX_C_A;
- X }
- X if (cbelow > MAX_C_B)
- X { cbelow = MAX_C_B;
- X }
- X}
- X
- Xprocess_indir_file(fn)
- Xchar *fn;
- X{
- X char newfn[1024];
- X FILE *f;
- X
- X f = fopen(fn,"r");
- X if (f == NULL)
- X { fprintf(stderr,"%s: cannot read %s\n",argvec[0],fn);
- X return;
- X }
- X while (fgets(newfn,sizeof(newfn),f) == newfn)
- X { newfn[strlen(newfn)-1] = '\0';
- X process_file(newfn);
- X }
- X fclose(f);
- X}
- X
- Xprocess_file(fn)
- Xchar *fn;
- X{
- X int i;
- X long int n;
- X char *cp;
- X
- X workf = fopen(fn,"r+");
- X if (workf == NULL)
- X { fprintf(stderr,"%s: cannot read %s\n",argvec[0],fn);
- X return;
- X }
- X printf("(file: %s)\n",fn);
- X current_file = fn;
- X for (i=0;i<=MAX_C_A;i++)
- X { nls[i] = -1;
- X }
- X nls[MAX_C_A] = 0;
- X tbeg = -1;
- X n = 0;
- X flying = allfly;
- X flystate = 1;
- X while (1)
- X { while (n < s1l)
- X { buf[n] = getc(workf);
- X if (buf[n] == '\n')
- X { add_shift(nls,ftell(workf),MAX_C_A+1);
- X }
- X n ++;
- X if (feof(workf))
- X { if (tbeg >= 0)
- X { n --;
- X fwrite(buf,1,n,tempf);
- X fseek(workf,tbeg,0);
- X n = ftell(tempf);
- X fseek(tempf,0L,0);
- X for (;n;n--)
- X { putc(getc(tempf),workf);
- X }
- X fflush(workf);
- X ftruncate(fileno(workf),ftell(workf));
- X }
- X fclose(workf);
- X return;
- X }
- X }
- X if ((bcmp(buf,str1,s1l) == 0) && doit())
- X { if (tbeg < 0)
- X { tbeg = ftell(workf) - s1l;
- X fseek(tempf,0L,0);
- X }
- X fwrite(str2,1,s2l,tempf);
- X n = 0;
- X }
- X else
- X { if (tbeg >= 0)
- X { putc(buf[0],tempf);
- X }
- X n --;
- X bcopy(buf+1,buf,n);
- X }
- X }
- X}
- X
- Xadd_shift(a,e,n)
- Xregister long int *a;
- Xregister long int e;
- Xregister int n;
- X{
- X register int i;
- X
- X n --;
- X for (i=0;i<n;i++)
- X { a[i] = a[i+1];
- X }
- X a[n] = e;
- X}
- X
- Xchar getc_cbreak()
- X{
- X struct sgttyb sg;
- X struct sgttyb osg;
- X char c;
- X
- X if (ioctl(fileno(stdin),TIOCGETP,&sg) < 0)
- X { return(getchar());
- X }
- X osg = sg;
- X sg.sg_flags |= CBREAK;
- X sg.sg_flags &= ~ECHO;
- X ioctl(fileno(stdin),TIOCSETN,&sg);
- X c = getchar();
- X ioctl(fileno(stdin),TIOCSETN,&osg);
- X return(c);
- X}
- X
- Xint doit()
- X{
- X long int save;
- X int i;
- X char c;
- X int use_replacement;
- X
- X if (flying)
- X { return(flystate);
- X }
- X use_replacement = 0;
- X save = ftell(workf);
- X i = -1;
- X while (i < 0)
- X { for (i=MAX_C_A-cabove;nls[i]<0;i++) ;
- X fseek(workf,nls[i],0);
- X for (i=save-nls[i]-s1l;i;i--)
- X { putchar(getc(workf));
- X }
- X put_ul(use_replacement?str2:str1);
- X fseek(workf,save,0);
- X i = cbelow + 1;
- X while (i > 0)
- X { char c;
- X c = getc(workf);
- X if (feof(workf))
- X { clearerr(workf);
- X break;
- X }
- X putchar(c);
- X if (c == '\n')
- X { i --;
- X }
- X }
- X fseek(workf,save,0);
- X i = -1;
- X while (i == -1)
- X { switch (getc_cbreak())
- X { case ' ':
- X i = 1;
- X break;
- X case '.':
- X i = 1;
- X flying = 1;
- X flystate = 0;
- X break;
- X case 'n':
- X i = 0;
- X break;
- X case '\7':
- X i = 0;
- X flying = 1;
- X flystate = 0;
- X break;
- X case '!':
- X i = 1;
- X flying = 1;
- X flystate = 1;
- X break;
- X case ',':
- X use_replacement = ! use_replacement;
- X i = -2;
- X printf("(using %s string gives)\n",use_replacement?"new":"old");
- X break;
- X case '?':
- X printf("File is `%s'\n",current_file);
- X break;
- X default:
- X putchar('\7');
- X break;
- X }
- X }
- X }
- X if (i)
- X { printf("(replacing");
- X }
- X else
- X { printf("(leaving");
- X }
- X if (flying)
- X { if (flystate == i)
- X { printf(" this and all the rest");
- X }
- X else if (flystate)
- X { printf(" this, replacing all the rest");
- X }
- X else
- X { printf(" this, leaving all the rest");
- X }
- X }
- X printf(")\n");
- X return(i);
- X}
- X
- Xputcharf(c)
- Xchar c;
- X{
- X putchar(c);
- X}
- X
- Xput_ul(s)
- Xchar *s;
- X{
- X if (ul_)
- X { for (;*s;s++)
- X { printf("_\b%c",*s);
- X }
- X }
- X else
- X { tputs(beginul,1,putcharf);
- X fputs(s,stdout);
- X tputs(endul,1,putcharf);
- X }
- X}
- SHAR_EOF
- if test 10813 -ne "`wc -c qsubst.c`"
- then
- echo shar: error transmitting qsubst.c \(should have been 10813 characters\)
- fi
- exit 0
- # end of shell archive
-
- der Mouse
-
- Smart mailers: mouse@mcgill-vision.uucp
- USA: {ihnp4,decvax,akgua,utzoo,etc}!utcsri!musocs!mcgill-vision!mouse
- think!mosart!mcgill-vision!mouse
- ARPAnet: think!mosart!mcgill-vision!mouse@harvard.harvard.edu
-
- --
-
- Rich $alz
- Cronus Project, BBN Labs rsalz@bbn.com
- Moderator, comp.sources.unix sources@uunet.uu.net
-