# spellcheck.awk -- interactive spell checker
#
# AUTHOR: Dale Dougherty
#
# Usage: nawk -f spellcheck.awk [+dict] file
# (Use spellcheck as name of shell program)
# SPELLDICT = "dict"
# SPELLFILE = "file"
# BEGIN actions perform the following tasks:
# 1) process command line arguments
# 2) create temporary filenames
# 3) execute spell program to create wordlist file
# 4) display list of user responses
BEGIN {
# Process command line arguments
# Must be at least two args -- nawk and filename
if (ARGC > 1) {
# if more than two args, second arg is dict
if (ARGC > 2) {
# test to see if dict is specified with "+"
# and assign ARGV[1] to SPELLDICT
if (ARGV[1] ~ /^\+.*/)
SPELLDICT = ARGV[1]
else
SPELLDICT = "+" ARGV[1]
# assign file ARGV[2] to SPELLFILE
SPELLFILE = ARGV[2]
# delete args so awk does not open them as files
delete ARGV[1]
delete ARGV[2]
}
# not more than two args
else {
# assign file ARGV[1] to SPELLFILE
SPELLFILE = ARGV[1]
# test to see if local dict file exists
if (! system ("test -r dict")) {
# if it does, ask if we should use it
printf ("Use local dict file? (y/n)")
getline reply < "-"
# if reply is yes, use "dict"
if (reply ~ /[yY](es)?/){
SPELLDICT = "+dict"
}
}
}
} # end of processing args > 1
# if args not > 1, then print shell-command usage
else {
print "Usage: spellcheck [+dict] file"
exit 1
}
# end of processing command line arguments
# create temporary file names, each begin with sp_
wordlist = "sp_wordlist"
spellsource = "sp_input"
spellout = "sp_out"
# copy SPELLFILE to temporary input file
system("cp " SPELLFILE " " spellsource)
# now run spell program; output sent to wordlist
print "Running spell checker ..."
if (SPELLDICT)
SPELLCMD = "spell " SPELLDICT " "
else
SPELLCMD = "spell "
system(SPELLCMD spellsource " > " wordlist )
# test wordlist to see if misspelled words turned up
if ( system("test -s " wordlist ) ) {
# if wordlist is empty, (or spell command failed), exit
print "No misspelled words found."
system("rm " spellsource " " wordlist)
exit
}
# assign wordlist file to ARGV[1] so that awk will read it.
ARGV[1] = wordlist
# display list of user responses
responseList = "Responses: \n\tChange each occurrence,"
responseList = responseList "\n\tGlobal change,"
responseList = responseList "\n\tAdd to Dict,"
responseList = responseList "\n\tHelp,"
responseList = responseList "\n\tQuit"
responseList = responseList "\n\tCR to ignore: "
printf("%s", responseList)
} # end of BEGIN procedure
# main procedure, executed for each line in wordlist.
# Purpose is to show misspelled word and prompt user
# for appropriate action.
{
# assign word to misspelling
misspelling = $1
response = 1
++word
# print misspelling and prompt for response
while (response !~ /(^[cCgGaAhHqQ])|^$/ ) {
printf("\n%d - Found %s (C/G/A/H/Q/):", word, misspelling)
getline response < "-"
}
# now process the user's response
# CR - carriage return ignores current word
# Help
if (response ~ /[Hh](elp)?/) {
# Display list of responses and prompt again.
printf("%s", responseList)
printf("\n%d - Found %s (C/G/A/Q/):", word, misspelling)
getline response < "-"
}
# Quit
if (response ~ /[Qq](uit)?/) exit
# Add to dictionary
if ( response ~ /[Aa](dd)?/) {
dict[++dictEntry] = misspelling
}
# Change each occurrence
if ( response ~ /[cC](hange)?/) {
# read each line of the file we are correcting
newspelling = ""; changes = ""
while( (getline < spellsource) > 0){
# call function to show line with misspelled word
# and prompt user to make each correction
make_change($0)
# all lines go to temp output file
print > spellout
}
# all lines have been read
# close temp input and temp output file
close(spellout)
close(spellsource)
# if change was made
if (changes){
# show changed lines
for (j = 1; j <= changes; ++j)
print changedLines[j]
printf ("%d lines changed. ", changes)
# function to confirm before saving changes
confirm_changes()
}
}
# Globally change
if ( response ~ /[gG](lobal)?/) {
# call function to prompt for correction
# and display each line that is changed.
# Ask user to approve all changes before saving.
make_global_change()
}
} # end of Main procedure
# END procedure makes changes permanent.
# It overwrites the original file, and adds words
# to the dictionary.
# It also removes the temporary files.
END {
# if we got here after reading only one record,
# no changes were made, so exit.
if (NR <= 1) exit
# user must confirm saving corrections to file
while (saveAnswer !~ /([yY](es)?)|([nN]o?)/ ) {
printf "Save corrections in %s (y/n)? ", SPELLFILE
getline saveAnswer < "-"
}
# if answer is yes then mv temporary input file to SPELLFILE
# save old SPELLFILE, just in case
if (saveAnswer ~ /^[yY]/) {
system("cp " SPELLFILE " " SPELLFILE ".orig")
system("mv " spellsource " " SPELLFILE)
}
# if answer is no then rm temporary input file
if (saveAnswer ~ /^[nN]/)
system("rm " spellsource)
# if words have been added to dictionary array, then prompt
# to confirm saving in current dictionary.
if (dictEntry) {
printf "Make changes to dictionary (y/n)? "
getline response < "-"
if (response ~ /^[yY]/){
# if no dictionary defined, then use "dict"
if (! SPELLDICT) SPELLDICT = "dict"
# loop through array and append words to dictionary
sub(/^\+/, "", SPELLDICT)
for ( item in dict )
print dict[item] >> SPELLDICT
close(SPELLDICT)
# sort dictionary file
system("sort " SPELLDICT "> tmp_dict")
system("mv " "tmp_dict " SPELLDICT)
}
}
# remove word list
system("rm sp_wordlist")
} # end of END procedure
# function definitions
# make_change -- prompt user to correct misspelling
# for current input line. Calls itself
# to find other occurrences in string.
# stringToChange -- initially $0; then unmatched substring of $0
# len -- length from beginning of $0 to end of matched string
# Assumes that misspelling is defined.
function make_change (stringToChange, len, # parameters
line, OKmakechange, printstring, carets) # locals
{
# match misspelling in stringToChange; otherwise do nothing
if ( match(stringToChange, misspelling) ) {
# Display matched line
printstring = $0
gsub(/\t/, " ", printstring)
print printstring
carets = "^"
for (i = 1; i < RLENGTH; ++i)
carets = carets "^"
if (len)
FMT = "%" len+RSTART+RLENGTH-2 "s\n"
else
FMT = "%" RSTART+RLENGTH-1 "s\n"
printf(FMT, carets)
# Prompt user for correction, if not already defined
if (! newspelling) {
printf "Change to:"
getline newspelling < "-"
}
# A carriage return falls through
# If user enters correction, confirm
while (newspelling && ! OKmakechange) {
printf ("Change %s to %s? (y/n):", misspelling, newspelling)
getline OKmakechange < "-"
madechg = ""
# test response
if (OKmakechange ~ /[yY](es)?/ ) {
# make change (first occurrence only)
madechg = sub(misspelling, newspelling, stringToChange)
}
else if ( OKmakechange ~ /[nN]o?/ ) {
# offer chance to re-enter correction
printf "Change to:"
getline newspelling < "-"
OKmakechange = ""
}
} # end of while loop
# if len, we are working with substring of $0
if (len) {
# assemble it
line = substr($0,1,len-1)
$0 = line stringToChange
}
else {
$0 = stringToChange
if (madechg) ++changes
}
# put changed line in array for display
if (madechg)
changedLines[changes] = ">" $0
# create substring so we can try to match other occurrences
len += RSTART + RLENGTH
part1 = substr($0, 1, len-1)
part2 = substr($0, len)
# calls itself to see if misspelling is found in remaining part
make_change(part2, len)
} # end of if
} # end of make_change()
# make_global_change --
# prompt user to correct misspelling
# for all lines globally.
# Has no arguments
# Assumes that misspelling is defined.
function make_global_change( newspelling, OKmakechange, changes)
{
# prompt user to correct misspelled word
printf "Globally change to:"
getline newspelling < "-"
# carriage return falls through
# if there is an answer, confirm
while (newspelling && ! OKmakechange) {
printf ("Globally change %s to %s? (y/n):", misspelling,
newspelling)
getline OKmakechange < "-"
# test response and make change
if (OKmakechange ~ /[yY](es)?/ ) {
# open file, read all lines
while( (getline < spellsource) > 0){
# if match is found, make change using gsub
# and print each changed line.
if ($0 ~ misspelling) {
madechg = gsub(misspelling, newspelling)
print ">", $0
changes += 1 # counter for line changes
}
# write all lines to temp output file
print > spellout
} # end of while loop for reading file
# close temporary files
close(spellout)
close(spellsource)
# report the number of changes
printf ("%d lines changed. ", changes)
# function to confirm before saving changes
confirm_changes()
} # end of if (OKmakechange ~ y)
# if correction not confirmed, prompt for new word
else if ( OKmakechange ~ /[nN]o?/ ){
printf "Globally change to:"
getline newspelling < "-"
OKmakechange = ""
}
} # end of while loop for prompting user for correction
} # end of make_global_change()
# confirm_changes --
# confirm before saving changes
function confirm_changes( savechanges) {
# prompt to confirm saving changes
while (! savechanges ) {
printf ("Save changes? (y/n)")
getline savechanges < "-"
}
# if confirmed, mv output to input
if (savechanges ~ /[yY](es)?/)
system("mv " spellout " " spellsource)
}