12.1.4. Supporting Functions
There are three supporting functions, two of which are large and do
the bulk of the work of making changes in the document. The third
function supports that work by confirming that the user wants to save
the changes that were made.
When the user wants to "Change each occurrence" in the document, the
main procedure has a while loop that reads the
document one line at a time. (This line becomes $0.) It calls the
make_change() function to see if the line contains
the misspelled word. If it does, the line is displayed and the user
is prompted to enter the correct spelling of the word.
# 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()
If the misspelled word is not found in the current input line, nothing
is done. If it is found, this function shows the line containing the
misspelling and asks the user if it should be corrected.
Underneath the display of the current line is a row of carets
that indicates the misspelled word.
Two other utlitities that are found on the UNIX system
^^^^^^^^^^
The current input line is copied to printstring
because it is necessary to change the line for display purposes. If
the line contains any tabs, each tab in this copy of the line is
temporarily replaced by a single space. This solves a problem of
aligning the carets when tabs were present. (A tab counts as a single
character when determining the length of a line but actually occupies
greater space when displayed, usually five to eight characters long.)
After displaying the line, the function prompts the user to enter a
correction. It then follows up by displaying what the user has
entered and asks for confirmation. If the correction is approved, the
sub() function is called to make the
change. If not approved, the user is given another chance to enter
the correct word.
Remember that the sub() function only
changes the first occurrence on a line. The
gsub() function changes all occurrences on
a line, but we want to allow the user to confirm
each change. Therefore, we have to try to match
the misspelled word against the remaining part of the line. And we
have to be able to match the next occurrence regardless of whether or
not the first occurrence was changed.
To do this, make_change() is designed as a
recursive function; it calls itself to look for additional occurrences
on the same line. In other words, the first time
make_change() is called, it looks at all of
$0 and matches the first misspelled word on that line. Then it splits
the line into two parts--the first part contains the characters
up to the end of the first occurrence and the second part contains the
characters that immediately follow up to the end of the line. Then it
calls itself to try and match the misspelled word in the second part.
When called recursively, the function takes two arguments.
make_change(part2, len)
The first is the string to be changed, which is initially $0 when
called from the main procedure but each time thereafter is the
remaining part of $0. The second argument is len
or the length of the first part, which we use to extract the substring
and reassemble the two parts at the end.
The make_change() function also collects an
array of lines that were changed.
# put changed line in array for display
if (madechg)
changedLines[changes] = ">" $0
The variable madechg will have a value if the
sub() function was successful. $0 (the two
parts have been rejoined) is assigned to an element of the array.
When all of the lines of the document have been read, the main
procedure loops through this array to display all the changed lines.
Then it calls the confirm_changes()
function to ask if these changes should be saved. It copies the
temporary output file over the temporary input file, keeping intact
the corrections made for the current misspelled word.
If a user decides to make a "Global change," the
make_global_change() function is called to
do it. This function is similar to the
make_change() function, but is simpler
because we can make the change globally on each line.
# 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()
This function prompts the user to enter a correction. A
while loop is set up to read all the lines of the
document and apply the gsub() function to
make the changes. The main difference is that all the changes are
made at once--the user is not prompted to confirm them. When all
lines have been read, the function displays the lines that were
changed and calls confirm_changes() to get
the user to approve this batch of changes before saving them.
The confirm_changes() function is a routine
called to get approval of the changes made when the
make_change() or
make_global_change() function is called.
# 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)
}
The reason for creating this function is to prevent the duplication of
code. Its purpose is simply to require the user to acknowledge the
changes before replacing the old version of the document file
(spellsource) with the new version
(spellout).