alexis@reed.UUCP (Alexis Dimitriadis) (05/31/85)
Why another spelling fixer? Briefly, the following program handles the error correction and context selection itself. (without calling sed, ed, ex or grep). The only program exec'ed is `spell', to generate the error list. The particular speller program invoked is user-settable. `Fix', the core of this program, was written here at Reed by Pat Locke many years ago, and has enjoyed great success (and heavy use) ever since. Fix2 combines a quick pass over the error words, at which time correct words may be discarded and "global" changes specified, with a second, "context" pass, when entire lines containing an error are shown and specific changes are entered, while deletions and global changes are still available. Another important feature is the possibility of backing up during the execution of the program. (And of course, shell escapes, help menus, and other bells and whistles). Please mail questions, comments (such as suggestions for further features), and (ulp) bug reports to me, alexis @ reed. This program was developed on a VAX 11/780 running ULTRIX, a 4.2BSD clone. I would especially appreciate hearing from anyone porting it to other environments. Sincerely, Alexis Dimitriadis alexis @ reed _______________________________________________ As soon as I get a full time job, the opinions expressed above will attach themselves to my employer, who will never be rid of them again. alexis @ reed ...teneron! \ ...seismo!ihnp4! - tektronix! - reed.UUCP ...decvax! / _______________________________________________ ---- Cut Here and Feed to the Hungry Bourne Shell ---------- #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # fix2.1 # makefile # fix.h # main.c # dofix.c # readerr.c # edit.c # words.c # seek.c # misc.c sed 's/^X//' << 'SHAR_EOF' > fix2.1 X.ad l X.TH FIX2 1 local local X.SH NAME XFix2: Correct spelling errors interactively. X.SH SYNOPSIS X.nf Xfix2 [\-cu] [\-s speller] [\-h histfile] [-f typofile] textfile [ ... ] X.fi X.br Xfix2 [\-h histfile] \-e typofile X.SH DESCRIPTION X.PP X.I Fix2 Xfacilitates the correction of misspellings in a text file. It operates Xin two stages. If typofile is not given, X.I fix2 Xwill run a spelling program to generate a list of errors. Wrong words Xare presented one at a time, for quick previewing. At this time they Xmay be deleted from the error list, or a "global" substitution pattern Xmay be specified. A question mark causes a list of the available Xcommands to be printed. X.PP XIn the second stage, Xlines containing the remaining words are presented, with the suspected Xword enclosed in square braces. ([ ]). XThe user may enter replacement text, type a newline, (leaving the word Xunchanged), or type a colon followed by a Xspecial command. XAll words matching an erroneous word will be corrected as specified. X.PP X.I Fix2 Xaccepts the following options: X.IP \-c XContext pass only. Do not present word list. XGo directly to presenting entire lines. X.IP \-e Xpreview errorfile only. The file argument is a wordlist, Xand it is reviewed as usually, except that global Xsubstitutions are not meaningful. On exit, the Xerrorfile is changed to contain the remaining words. X.IP \-u XUpdate errofile. Deleted words will be removed from the Xerrorfile. Unless \-e or \-u is specified, the errorfile Xis not altered. X.IP \-f typofile XRead the error list from the file typofile, instead of running spell. XIf more than one textfile is being X.I fixed, X the typofile may be prepared Xin advance to avoid specifying the same global changes and deletions Xover and over and over. X.IP \-s "speller [options]" XUse `speller' instead of default X/usr/bin/spell. Useful for specifying personal word Xlists, etc. The environment variable SHELL may also used Xto specify a speller. XThe \-s option Xoverrides SPELL. In both cases, the `speller' specified may Xcontain options. X.IP \-h historyfile XThe file `historyfile' is opened and all words Xdeleted with 'D' are appended to it. The environment variable XSPELL_HIST may also be used to specify a history file. XThe \-h option overrides SPELL_HIST. X.PP XIn the first, (preview stage), X.I fix2 Xpresents the words in typofile one by one, and waits for a Xreply. The reply may be: X.IP <space> Xleave word in error list. XThe word will be presented during the second phase. X.IP d Xdelete word from list. XThe word is correct and need not be changed. X.IP D Xdelete, and save in history file. X`D' can be used if one uses their own word list with X.I spell. XIf a history file has been specified, Xthe deleted word will be appended to it. Otherwise, X.I "`D' Xworks like X.I "`d'. X.IP g Xenter replacement for global substitution. XThe replacement text will replace every occurence of the word in the Xfile. X.IP \- Xgo back to previous word. XThis can be used when a wrong word was deleted, or replacement text was Xmistyped, etc. XAfter backing up, you may go back to where you were by Xhitting spaces. Deleted words will need to be deleted again, but global Xreplacements are not lost. XTo X.I cancel Xa global change, type g and then <return>. X.IP ! Xenter a shell command. X.IP q Xdo not show remaining words. XThe remaining words are quickly read in, then X.I fix2 Xmoves on to the X`context' stage. X.IP ? Xprint command list. X.PP XIt is not necessary to hit return after giving a reply during Xthis stage. After receiving each command, X.I fix2 Xplaces a letter that Xrepresents it at the left of the word. If a word is successfully saved Xin the history file, the letters dh are used. X.PP XIn the context stage, X.I fix2 Xscans the Xtextfile for instances of words in the error list. XWords for which global replacement text Xwas given are corrected. Otherwise, X.I fix2 Xprints out the line on which a wrong word was found, Xwith the matched word enclosed in square braces, and accepts a Xcommand from the user. If the response is a blank line, X.I fix2 Xsimply proceeds to the next match. If the response Xstarts with a colon, X.I fix2 Xinterprets it as a special command. XOtherwise, it is taken as text to substitute for Xthe flagged word. Commands are as in the previous stage, with Xthe following differences: X.TP X<return> Xleaves word as it is. (it is not wrong). X.TP X:q XNo more words are flagged, but global changes are continued for Xthe rest of the file. X.TP X:x X.I Fix2 Xexits immediately. The textfile is left as it was before Xrunning it. X.PP XThe remaining commands work as above. XNote that it is necessary to hit <return> at this stage, and that a Xcolon is required before any special commands. X.SH BUGS X.PP XThere is a set limit to the number of errors that X.I fix2 Xcan remember. X.PP XWords containing embedded nroff directives will not be recognized. X.SH AUTHORS XFix2 was originally written by Pat Locke at Reed College, Portland, XOregon. XThis version was written by Alexis Dimitriadis, also at XReed, and added the preview stage, backing up, binary word search, Xand lots of other bells all over the place. X.SH COMING SOON X.PP XA simple line-editing facility, for errors not confined to one Xword; Adding arbitrary words to the error list; A hashed error list, Xfor additional speed. X.SH SEE ALSO Xspell (1), look (1), thimk (1). SHAR_EOF sed 's/^X//' << 'SHAR_EOF' > makefile X# X# Makefile for FIX2, an interactive error correction program. X# An extended version of `fix', written by Pat Locke at Reed College. X# Fix2 by Alexis Dimitriadis. X# X# This program is public domain. It may be copied freely, but it may X# not be sold for profit, and all copies must contain this restriction. X# Please report bugs to alexis@reed. X# X# VERSION 3.2. (first net release). X# This program was developed on a VAX 11/780 X# running ULTRIX, a 4.2BSD clone. X XOBJS = main.o dofix.o readerr.o edit.o words.o misc.o seek.o XSRCS = main.c dofix2.c readerr.c edit.c words.c misc.c seek.c X XINSTALLDIR = /usr/local X# MANDIR = /usr/man X# MANSECT = l XCFLAGS = -O -s X Xall: fix2 fix2.nr X Xfix2: $(OBJS) X cc $(CFLAGS) -o $@ $(OBJS) X X$(OBJS): fix.h X Xfix2.nr: fix2.1 X nroff -man $? > $@ X X Xinstall: all X cp fix2 $(INSTALLDIR)/fix2 X cp fix2.1 $(MANDIR)/man$(MANSECT)/fix2.$(MANSECT) X cp fix2.nr $(MANDIR)/cat$(MANSECT)/fix2.$(MANSECT) SHAR_EOF sed 's/^X//' << 'SHAR_EOF' > fix.h X X#define MAXWORD 400 X#define MAXCMD 120 X#define MAXLINE 256 X X#define WIDTH 75 X X#define BS '\b' X#define EOL ((char *) 0) X#define NOMATCH ((struct err *) 0) X X#define INWORD(A) (isalpha(A) || (A) == '\'') X X#define TOOMANY (-1) X#define QUIT (-2) X Xstruct err { X char *word; X char *global; X}; SHAR_EOF sed 's/^X//' << 'SHAR_EOF' > main.c Xstatic char * id = "@(#) fix2.c 3.2 Reed 5/20/85"; X X/**************************************************************\ X* * X* fix2 -- interactively match and correct errors * X* produced by spell etc. * X* * X* Based on `fix' by Pat Locke * X* * X* Fix2 by Alexis Dimitriadis * X* (alexis @ reed) * X* Reed College * X* Version 3.2, 85/5/20 * X* * X* This program is public domain. It may be copied freely, * X* but cannot be sold at a profit; all copies must contain * X* this restriction. * X\**************************************************************/ X X#include <stdio.h> X#include <sgtty.h> X#include <signal.h> X#include "fix.h" X X#define USAGE "Usage: %s [-ecuhs] [-f typofile] textfile [...]\n" X Xstruct sgttyb orgmode, newmode; Xshort preview_only, context_only, update; XFILE * histf; Xchar *spell = "/usr/bin/spell"; X Xmain (argc, argv) Xint argc; Xchar *argv[]; X{ X char * histfname = 0; X int v = 0, tell; X X void onintr(), onstop(); X char *getenv(), *name, *typofile = NULL; X short argnext; X X ioctl(1, TIOCGETP, (char *) &orgmode); X newmode = orgmode; /* structure copy */ X newmode.sg_flags &= ~ECHO; X newmode.sg_flags |= CBREAK; /* Equivalent to ICANON flag on SysV, X * I think... */ X X signal(SIGINT, onintr); X signal(SIGQUIT, onintr); X signal(SIGTSTP, onstop); X X if (name=getenv("SPELL")) X spell = name; X X if (name=getenv("SPELL_HIST")) X histfname = name; X X if (histfname && ((histf = fopen(histfname, "a")) == NULL)) X fatal("Cannot open history file `%s'\n", histfname); X X while (*argv[++v] == '-') { X while (*++(argv[v])) { X switch (*argv[v]) { X X case 'e': /* go through errorlist only */ X case '1': /* "pass 1" and "pass 2" */ X preview_only = 1; X update = 1; X break; X X case 'c': /* do not present words as they are read */ X case '2': X context_only = 1; X break; X X case 's': /* Specify spell program */ X spell = argv[++v]; X argnext = 1; X break; X X case 'h': /* specify history file for deleted words */ X histfname = argv[++v]; X if ((histf = fopen(histfname, "a")) == NULL) X fatal("Cannot open %s\n", histfname); X X argnext = 1; X break; X X case 'f': /* specify error file to use */ X typofile = argv[++v]; X argnext = 1; X break; X X case 'u': X update = 1; X break; X X default: X fatal(" -%c: Unknown option\n", (char *) *argv[v]); X } X if (!argnext) X continue; X X argnext = 0; X break; X } X } X X /* Check illogical requests */ X X if (preview_only && context_only) X fatal("That would do nothing!\n", NULL); X X if (!preview_only && update && (typofile == NULL)) X fatal ("Nothing to update\n", NULL); X X if (argc == v) X fatal(USAGE, argv[0]); X X tell = (argc - v - 1); X X if (preview_only) { X for (; argc > v; v++) X geterrfile(argv[v]); X exit (0); X } X X if (typofile && (geterrfile(typofile) == 0)) X fatal("No errors to correct from %s\n", typofile); X X for (; argc > v; v++) { X if (tell) X printf("Next file: %s\n", argv[v]); X X /* getspell() returns number of errors */ X if (!typofile && (getspell(argv[v]) == 0)) X continue; X X dofix(argv[v]); X X if ((argc - v) > 1) X cleanup(); X /* Don't bother after the last file */ X } X X} SHAR_EOF sed 's/^X//' << 'SHAR_EOF' > dofix.c X/* Handle the stages of fixing a file */ X X#include <stdio.h> X#include <sgtty.h> X#include <signal.h> X#include "fix.h" X X#define SAFE (SIGINT | SIGQUIT | SIGTSTP | SIGTERM) X Xextern struct sgttyb orgmode, newmode; Xextern short preview_only, context_only, update; Xextern int nword; Xextern char * spell; Xextern int * compare(); X Xdofix(textfile) Xchar * textfile; X{ X FILE *firstf, *cleanf; X int mask, sigsetmask(); X void onstop(); X long cl_start, ftell(); X static char tmp[] = "/tmp/fixXXXXXX"; X char *mktemp(); X X if ((firstf = fopen (textfile, "r+")) == NULL) X fatal ("Cannot open %s\n", textfile); X X if ((cleanf = fopen (mktemp (tmp), "w+")) == NULL) X fatal ("Cannot create temporary file %s\n", tmp); X X unlink (tmp); X X cl_start = ftell(cleanf); X X edit (firstf, cleanf); X X ftruncate(fileno(firstf), 0); X if (ftruncate(fileno(cleanf), (int) (ftell(cleanf) - cl_start)) == -1) X perror("Could not truncate text buffer"); X /* Truncate to expected size -- a seek may have left stuff past it */ X X rewind (cleanf); X rewind(firstf); X X mask = sigsetmask(SAFE); X cp (cleanf, firstf); X fclose(cleanf); X fclose(firstf); X sigsetmask(mask); X} X Xint getspell(textfile) Xchar * textfile; X{ X FILE *errf, *popen(); X char tbuf[128]; X X nword = 0; X strcpy(tbuf, spell); X strcat(tbuf, " "); X strcat(tbuf, textfile); X X puts("Now running spell..."); X X if ((errf = popen(tbuf, "r")) == NULL) X fatal("Cannot run spell program `%s'\n", spell); X X if (context_only) X quickerr(errf); X else { X ioctl(1, TIOCSETP, (char *) &newmode); X if (readerr(errf) == QUIT) X quickerr(errf); /* continue reading */ X ioctl(1, TIOCSETP, (char *) &orgmode); X } X X return nword; X} X X/* Read errors from file */ Xint geterrfile(typofile) Xchar * typofile; X{ X FILE * errf; X int mask; X X if ((errf = fopen(typofile, (update)? "r+" : "r")) == NULL) X fatal ("Cannot open %s\n", typofile); X X if (context_only) X quickerr(errf); X else { X ioctl(1, TIOCSETP, (char *) &newmode); X if (readerr(errf) == QUIT) X quickerr(errf); /* continue reading */ X X ioctl(1, TIOCSETP, (char *) &orgmode); X } X X if (update) { X rewind(errf); X ftruncate(fileno(errf), 0); X mask = sigsetmask(SAFE); X dumperr(errf); X sigsetmask(mask); X } X X fclose (errf); X return nword; X} SHAR_EOF sed 's/^X//' << 'SHAR_EOF' > readerr.c X#include <stdio.h> X#include <ctype.h> X#include <sgtty.h> X#include <signal.h> X#include "fix.h" X X/* Preview error list as it is read in. */ X Xstatic char * help1 = X"Commands:\n\ X<space> leave word in error list\n\ Xd delete word from list\n\ XD delete and save in history file (if specified)\n\ Xg enter replacement for global substitution\n\ X- go back to previous word\n\ X! enter a shell command\n\ Xq do not present remaining errors\n\ X\n\ XAfter you back up, you may go back to where you were by\n\ Xhitting spaces. Global changes are not lost. To CANCEL\n\ Xa global change, type g and then <return>\n"; X Xstruct err errlist[MAXWORD]; X Xextern struct sgttyb newmode, orgmode; Xextern short preview_only; Xextern int nword; Xextern FILE *histf; X Xreaderr (errf) XFILE *errf; X{ X char wordbuf[120]; X char *malloc(), *gets(), *getenv(), *prompt; X char cmdbuf[MAXCMD]; X extern void onstop(); X register char * cmd = cmdbuf; X short repeat, backup = 0; X register struct err * erptr = errlist; X X for (;;) { X if (fscanf (errf, "%120s", wordbuf) != 1) X break; /* from for loop */ X X if ((erptr->word = malloc((unsigned) strlen(wordbuf)+1)) == NULL) X fatal ("Out of space at word %d\n", (char *) nword); X strcpy(erptr->word, wordbuf); X X if (++nword >= MAXWORD) { X fprintf(stderr, "More than %d words in error file\n", X MAXWORD); X nword--; X return(0); X } X X repeat = 1; X while (repeat) { X repeat = 0; X X printf(" %-10s ", erptr->word); X switch (getchar()) { X X case '-': /* back up to previous word */ X repeat = 1; X if (erptr == errlist) { X puts("No words to back to"); X break; X } X puts("\r-"); X erptr--; X backup++; X if (!erptr->word) { /* had been deleted */ X erptr->word = erptr->global; X erptr->global = NULL; X } X X break; X X case '!': /* shell escape */ X fputs("\r!\n", stdout); X fputs((prompt = getenv("PS1")) ? prompt : "$ ", stdout); X /* If PS1 is unset, the user probably uses csh, X * so % would be more appropriate, IF callunix() X * didn't always call /bin/sh */ X X signal (SIGTSTP, SIG_DFL); X ioctl(1, TIOCSETP, (char *) &orgmode); X if (gets(cmdbuf) != NULL) X callunix (cmdbuf); X signal (SIGTSTP, onstop); X ioctl(1, TIOCSETP, (char *) &newmode); X repeat = 1; X break; X X case 'D': /* save word in history file, if it exists */ X if (histf) { X fputs(erptr->word, histf); X putc('\n', histf); X fputs("\r h", stdout); X } X /* no break: also delete word */ X X case 'd': /* delete word from list */ X delete(erptr); /* delete, saving &word in `global' */ X erptr++; X puts("\rd"); X if (backup) { X repeat = 1; X backup--; X } X break; X X case ':': X case 'g': X case 'G': /* substitute globally */ X if (preview_only) { X puts("You can't do that now"); X repeat = 1; X break; X } X X if (erptr->global) { X free((char *) erptr->global); X erptr->global = NULL; X } X cmd = cmdbuf; X fputs(": ", stdout); X while((*cmd = getchar()) != '\n') X putchar(*cmd++); X *cmd = '\0'; X backsp(cmdbuf); X cmd = cmdbuf; X while (isspace (*cmd)) X cmd++; X if (*cmd) { X if ((erptr->global = X malloc ((unsigned) strlen (cmd)+1)) == NULL) { X fatal ("Out of space at word %d\n", (char *) nword); X break; X } X strcpy (erptr->global, cmd); X } X /* no break: go through \n actions, too */ X X case ' ': /* put word in error list */ X case '\r': X case '\n': X if (backup) { X backup--; X repeat = 1; X } X if (erptr->global) X fputs("\rG\r", stdout); X putchar('\n'); X erptr++; X break; X X case '?': X putchar('\n'); X fputs(help1, stdout); X repeat = 1; X break; X X case 'q': /* quit interactive reading */ X case EOF: X putchar('\n'); X return(QUIT); X X default: X puts("Hit ? for a command list"); X repeat = 1; X } X } X } X erptr->word = NULL; /* just to be sure */ X return(0); X} X Xbacksp(wordbuf) /* act on backspaces in input */ Xchar wordbuf[]; X{ X char *wordptr, *fixptr; X X wordptr = fixptr = wordbuf; X while (*wordptr) { X if (*wordptr == BS) { X if (fixptr > wordbuf) X --fixptr; X } else X *fixptr++ = *wordptr; X wordptr++; X } X *fixptr = '\0'; X} SHAR_EOF sed 's/^X//' << 'SHAR_EOF' > edit.c X#include <stdio.h> X#include <ctype.h> X#include "fix.h" X Xstatic char line1[MAXLINE]; Xchar * lp = line1; Xint nword = 0; X Xextern struct err errlist[]; X Xextern FILE * histf; X Xstatic char * help2 = "COMMANDS:\n\ Xnewword change word in brackets to newword\n\ X<return> go on to next typo\n\ X:d delete word\n\ X:D delete and save in history file\n\ X:g word substitute globally\n\ X:-[n] back up to n-th previous line shown\n\ X:q make only global changes to rest of file\n\ X:x exit without making ANY changes\n\ X:!cmd execute cmd from shell\n\n"; /* a blank line */ X Xedit (inp, outp) XFILE *inp, *outp; X{ X char combuf[MAXCMD], * com; X char *malloc(), *gets(); X short quit = 0, repeat = 0; X register char * pos; X char * substitute(), * nextword(); X struct err * ind, * match(); X long t_seek, c_seek, ftell(); X int count, atoi(); X X for (;;) { X t_seek = ftell(inp); X if (fgets (lp, MAXLINE, inp) == NULL) X break; X X pos = lp; X if (!isalpha(*pos)) X if ((pos = nextword(pos)) == EOL) { X fputs (lp, outp); X continue; X } X X do { /* a do ... while loop */ X if((ind = match(pos)) == NOMATCH) X continue; X X if (ind->global) { X pos = substitute (pos, ind->global); X continue; X } X if (quit) X continue; X X c_seek = ftell(outp); X setseek(t_seek, c_seek); X X repeat = 1; X while (repeat) { X repeat = 0; X printline (lp, pos); X if (gets(combuf) == NULL) X strcpy (combuf, ":q"); /* fake quit on EOF */ X com = combuf; X while (*com == ' ' || *com == '\t') X ++com; X X if (*com == ':') { /* special command */ X switch (*++com) { X X case '-': X if ((count = atoi(++com)) == 0) X count++; X X if (seekback(inp, outp, count) == NULL) { X puts("Nothing to back up to"); X repeat = 1; X break; X } X else X goto abrupt; X X case 'D': X if (histf) X fputs(ind->word, histf); X case 'd': /* delete word from errlist */ X delete (ind); X break; X X case 'g': /* substitute globally */ X while (isspace (*++com)) X ; X if ((ind->global = X malloc ((unsigned)(strlen(com)+1))) X == NULL) { X puts ("Sorry, out of space."); X repeat = 1; X } X else { X strcpy (ind->global, com); X pos = substitute (pos, com); X } X break; X X case 'x': X fatal("Exiting without changes\n", NULL); X break; X X case 'q': /* copy rest of file as is */ X quit = 1; X break; X X case '!': /* shell escape */ X callunix (++com); X repeat = 1; X break; X X default: X fputs(help2, stdout); X repeat = 1; X break; X } X } X else if (*com != '\0') { /* non-empty line */ X /* substitute text for matched word */ X X pos = substitute (pos, com); X break; X } X /* else make no substitution */ X X } /* end of while (repeat) */ X } while ((pos = nextword(pos)) != EOL); X fputs (lp, outp); X X abrupt: ; /* We goto here when we seek to avoid outputting X * the old line to cleanf */ X } X} SHAR_EOF sed 's/^X//' << 'SHAR_EOF' > words.c X#include <stdio.h> X#include <ctype.h> X#include "fix.h" X Xextern struct err errlist[]; Xextern char * lp; Xextern int nword; X X/* Returns a pointer to the beginning of the next word after pos */ Xchar * nextword(pos) Xregister char * pos; X{ X while (INWORD(*pos)) X ++pos; X if (*pos == '\0') X return (EOL); X X while (!INWORD(*pos)) X if (*pos == '\0') X return (EOL); X else X pos++; X X while (*pos == '\'') /* spell discards initial single quotes */ X pos++; X X return(pos); X} X X Xstatic char * tmp; X#define SWAP(A, B) do {tmp = A; A = B; B = tmp;} while (0) X Xchar * substitute (pos, word) /* substitute word into lp at pos */ Xchar * pos, * word; X{ X static char swaplin[MAXLINE]; X static char * sp = swaplin; X register char * retp; /* resume here on new string */ X X strncpy (sp, lp, pos - lp); /* sp is NOT null-terminated */ X sp[pos - lp] = '\0'; X strcat (sp, word); X retp = sp + strlen(sp) - 1; /* points to last character */ X X while (INWORD(*pos)) X pos++; /* skip word being changed */ X X strcat (sp, pos); X SWAP(lp, sp); X return(retp); X} X Xdelete (ep) /* reversibly delete word at *ep from errlist */ Xstruct err * ep; X{ X if (ep->global) X free(ep->global); X X ep->global = ep->word; X ep->word = NULL; X} X X/* The following algorithm does not assume a sorted error X * list. (Since words may be deleted at any time, a quadratic X * search is too much of a nightmare) */ X Xstruct err * match(pos) /* look for words in errlist to X * match pos */ Xregister char * pos; X X{ X register struct err * listp = errlist + nword; X register char * wp = pos; X register int wsiz; X X if (nword == 0) X return(NOMATCH); X while (INWORD(*wp)) X wp++; X wsiz = wp - pos; X if (wsiz == 0) X return(NOMATCH); X X while (--listp >= errlist) X if ((*pos == *(listp->word)) && X (strncmp(pos, listp->word, wsiz) == 0) && X (strlen(listp->word) == wsiz)) X return(listp); X X return(NOMATCH); X} SHAR_EOF sed 's/^X//' << 'SHAR_EOF' > seek.c X#include "fix.h" X#include <stdio.h> X Xstatic struct seekbuf { X struct seekbuf * prev; X long first, clean; X} *bp = NULL; X X/* A pointer to a list of seek offsets for previous substitutions. X * It contains a pointer to the previous element of the list. X * Back up by seeking to the appropriate location, then setting X * bp = bp->prev */ X Xsetseek(f_seek, c_seek) Xlong f_seek, c_seek; X X{ X struct seekbuf * tm; X char * malloc(); X X /* If we record a line more than once, we get stuck there */ X if (bp->clean == c_seek) X return(1); X X if ((tm = (struct seekbuf *) malloc((unsigned) sizeof (struct seekbuf))) X == NULL) X return(0); X X tm->prev = bp; X bp = tm; X bp->first = f_seek; X bp->clean = c_seek; X return (1); X} X Xseekback(firstf, cleanf, count) XFILE *firstf, *cleanf; Xint count; X X{ X if ((bp == NULL)||(bp->prev == NULL)) X return (0); X X for (;count--;) X if (bp->prev) { X free((char *) bp); X bp = bp->prev; X } X else X break; X X fseek(firstf, bp->first, 0); X fseek(cleanf, bp->clean, 0); X return (1); X} X X/* Avoid cute bugs when processing more than one file */ Xclearseek() X{ X while (bp) { X free((char *) bp); X bp = bp->prev; X } X} SHAR_EOF sed 's/^X//' << 'SHAR_EOF' > misc.c X#include <ctype.h> X#include <stdio.h> X#include <signal.h> X#include <sgtty.h> X#include <sys/wait.h> X#include "fix.h" X Xextern struct sgttyb orgmode; Xextern struct err errlist[]; Xextern int nword; X Xquickerr (errf) XFILE *errf; X{ X char wordbuf[120]; X char *malloc(); X register struct err * errp = errlist + nword; X X while (fscanf (errf, "%s", wordbuf) == 1) { X if ((errp->word = malloc ((unsigned) strlen(wordbuf)+1)) == NULL) X fatal ("Out of space at word %d\n", (char *) nword); X strcpy (errp->word, wordbuf); X X ++errp; X X if (++nword >= MAXWORD) { X fprintf (stderr, "More than %d words in error file\n", MAXWORD); X return; X } X } X} X Xdumperr(errf) XFILE *errf; X X{ X register struct err * nw; X register count = nword; X X for (nw = errlist; --count; nw++) X if (nw->word) { X fputs(nw->word, errf); X putc('\n', errf); X } X} X X/* Reset fix2 to handle more than one file */ Xcleanup() X{ X register struct err * ep = errlist + nword; X X while (ep-- != errlist) { X if (ep->word) X free((char *) ep->word); X if (ep->global) X free((char *) ep->global); X ep->word = ep->global = NULL; X } X X nword = 0; X clearseek(); X} X Xprintline (l, p) /* print string l with '*' at *p */ Xregister char *l, *p; X X{ X register int i = 0; X X while (*l) { X if (i++ >= WIDTH) { X puts("\\"); X i = 1; X } X if (l == p) { X putchar('['); X i++; X while (INWORD(*l)) { X putchar(*l++); X if (i++ >= WIDTH) { X puts("\\"); X i = 1; X } X } X putchar(']'); X } X putchar(*l); X l++; X } X} X X/* VARARGS */ Xfatal (string, arg) Xchar *string; Xchar * arg; /* Usually... the true type is in `string' */ X{ X fprintf (stderr, string, arg); X ioctl(1, TIOCSETP, (char *) &orgmode); X exit (1); X} X Xvoid onstop() X{ X /* come here if SIGTSTP (^Z) is received */ X struct sgttyb thismode; X int mask; X X ioctl(1, TIOCGETP, (char *) &thismode); X ioctl(1, TIOCSETP, (char *) &orgmode); X signal (SIGTSTP, SIG_DFL); X mask = sigsetmask(0); X kill (0, SIGTSTP); X /* stop here */ X X /* resume here when restarted */ X sigsetmask(mask); X signal (SIGTSTP, onstop); X ioctl(1, TIOCSETP, (char *) &thismode); X} X Xvoid onintr() X{ X ioctl(1, TIOCSETP, (char *) &orgmode); X exit(1); X} X Xcp (fromf, tof) XFILE *fromf, *tof; X{ X register c; X X while ((c = getc (fromf)) != EOF) X putc (c, tof); X} X Xcallunix(command) Xchar *command; X{ X int pid, rpid; X union wait r, * retcode = &r; X /* char * shell, * getenv(); */ X X if ((pid = fork()) == 0) { X /* Temporary cshells are slow and obnoxious but possible: X * execl((shell = getenv("SHELL"))?shell:"/bin/sh", X * "sh", "-c", command, 0); */ X execl("/bin/sh", "sh", "-c", command, 0); X exit (1); X } X X signal(SIGINT, SIG_IGN); X signal(SIGQUIT, SIG_IGN); X while ((rpid = wait(retcode)) != pid && rpid != -1) X ; X puts("!"); X signal(SIGINT, onintr); X signal(SIGQUIT, onintr); X} SHAR_EOF exit -- _______________________________________________ As soon as I get a full time job, the opinions expressed above will attach themselves to my employer, who will never be rid of them again. alexis @ reed ...teneron! \ ...seismo!ihnp4! - tektronix! - reed.UUCP ...decvax! /