Explanation of distprompter
[previous]
[next]
[table of contents] [index]
The Section distprompter Edits dist Drafts
gives an overview of this script.
Here is the distprompter script
(you might want to open it in a separate browser window).
To install it, see the Section
Programs in This Book's Archive.
The main part of distprompter is the while loop.
It reads the template draft file (that dist built; the
Section Making the Draft from the Template File
explains how).
The draft filename is in the first command-line parameter, $1.
The while loop writes the edited header into the $header file.
The loop is shown below.
The line numbers like 31> aren't part of the file; they're just for
reference.
30> # read COMMAND AT TOP OF LOOP GETS STDIN (FD 0), SO SAVE FD 0 NOW:
31> exec 4<&0 # SAVE ORIGINAL STDIN (USUALLY TTY) AS FD 4
32> while read label line
33> do
34> case "$label" in
35> [Rr]esent-?*:)
36> case "$line" in
37> ?*) # SHOW LINE ON SCREEN AND PUT INTO HEADER FILE:
38> echo "$label $line"
39> echo "$label $line" 1>&3
40> ;;
41> *) # FILL IT IN OURSELVES:
42> $echo "$label $nnl"
43> exec 5<&0 # SAVE DRAFT FILE FD; DO NOT CLOSE!
44> exec 0<&4 # RESTORE ORIGINAL STDIN
45> read ans
46> exec 0<&5 # RECONNECT DRAFT FILE TO STDIN
47> case "$ans" in
48> "") ;; # EMPTY; DO NOTHING
49> *) echo "$label $ans" 1>&3 ;;
50> esac
51> ;;
52> esac
53> ;;
54> ""|---*) # END OF HEADER
55> echo "-------" 1>&3
56> break # PROBABLY NOT NEEDED...
57> ;;
58> *) echo "$myname: illegal header component
59> '$label $line'" 1>&2
60> break
61> ;;
62> esac
63> done <$1 2>$err 3>$header
For efficiency, the script uses UNIX file descriptors to keep
several files open at once.
The loop reads and writes those open files, one line at a time.
The Bourne shell can manipulate file descriptors with its exec
command and operators like 2>&1 and 0<&-.
There isn't room here for a complete explanation; to get that,
see an advanced Bourne shell programming book like UNIX Power Tools.
The comments in the script and the explanation here will tell you
what each file descriptor redirection does.
The Table below lists the file descriptor numbers used in
operators like 4<&0.
Table: UNIX File Descriptor Numbers
Number Default Use
0 Standard input
1 Standard output
2 Standard error
3-9 Not assigned*
*
If your MH was built with the [OVERHEAD] configuration option,
some of the file descriptors 3-9 may already have been used.
(Section The -help Switches shows how to list your configuration options with
-help.)
This distprompter script uses file descriptors 3-5.
If you need to use those special [OVERHEAD] file descriptors
inside distprompter (and that's not too likely),
see the Table
Environment Variables that MH Sets
for an explanation
of the MHFD and MHCONTEXTFD descriptors.
Outside the loop, the standard input of
the commands is (by default) from your keyboard.
But the loop is different because of the redirection at the
done line (line 63 -- see below).
Every command in the loop reads its standard input from the template
draft file, not from your terminal.
It "takes over" standard input during the loop.
So, the read command at the top of the loop (line 32)
reads the template draft file.
But that means the read command inside the loop, on line 45,
couldn't read from your terminal.
The answer is to connect any other files you want to read to their
own file descriptors.
Line 31 does that.
Inside the loop, instead of reading its standard input, the read
on line 45 reads file descriptor 4.
Before reconnecting to the saved file descriptor 4, there's one more
thing the script needs to do.
Line 43 saves the open draft file template as file descriptor 5.
Because we don't close the file here (with exec 5<&-),
the next read command at the top of the loop (line 32) will read
the next line of the file.
If we'd simply closed the template file and reopened it, the next
read on line 32 would read the first line of the file instead
of the correct line.
Next, line 44 changes file descriptor 0 to point to the original
standard input before the loop -- it was saved as file descriptor 4 in
line 31.
Now, the read ans command on line 45 can read the
original standard input and get your answer to the prompt.
Line 46 rearranges descriptors for the rest of the loop -- it
reconnects the draft
template file from file descriptor 5 back to file descriptor 0.
So, the next read at the top of the loop will read the template file.
Some shells can solve the problem on line 45 directly.
They let you change what file descriptor the read command reads,
instead of the default (file descriptor 0, the standard input).
For instance, to read the value of the ans variable from file
descriptor 4, line 45 could look like one of these:
read ans 0<&4
read -u4 ans
But some Bourne shells won't let you redirect the input of read
on the same line.
That's why distprompter was written this way: the kind of
redirection around line 45 works on all Bourne shells.
Now, about the loop:
three file descriptors (fds) are redirected at line 63;
these affect the input and output of every command inside the loop:
- fd 0
-
The standard input; taken from the draft template file.
Each read command on line 32 will read the next line
from the template file.
Any other command inside the loop that reads its standard input
would read from this template file, too.
That would be bad here, of course.
See the explanation of lines 43-47, above.
- fd 2
-
The standard error; goes to an error-collecting file.
- fd 3
-
Written to the new message header file.
Anything that is written to file descriptor 3 during the loop will
be added to the new header.
The sidebar entitled
The Ins and Outs of Redirected I/O Loops
points out some things to keep in mind when you
write other loops like this one.
The read in line 32 reads two shell variables.
The first variable, label, always contains the first word on
the line -- usually a field label like Resent-Fcc:.
If the draft file has any other text (like the word outbox),
that goes into the line variable.
The body of the loop is a big case structure.
It tests the label and branches:
-
If the label starts with Resent-, lines 35-53 test
to see if the rest of the field had any text.
-
A complete field is echoed to the terminal (via standard output)
in line 38 and to the new header file (by file descriptor 3)
in line 39.
-
If the draft field was empty, line 42 echos it
without a newline.
This leaves the cursor to the right of the colon, ready for the
user's response.
The $echo and $nnl set at one of the lines 12-14
makes this work portably.
A simple read command won't read what you type at the prompt
because the loop's standard input comes from the draft file.
As you read at the start of this section,
some shells' built-in read command will not let you
redirect their standard input to another file descriptor.
A workaround that should take care of all Bourne shells starts at line 43.
It's explained in detail near the start of this section.
The case in lines 47-50 tests the response.
The field is only printed to the new header if the user
typed an answer.
-
The case in line 54 matches an empty line or a line
that starts with at least three dashes.
This is the end of the header.
The script echos its own row of dashes to the draft file.
Then it breaks the loop -- that probably isn't necessary because the
next read in line 32 should return a nonzero status when it
reads the end of the template file.
-
Line 58 matches anything else in the header.
Because MH is strict about the fields in the dist header,
this test does some early enforcement of the rules and avoids those
problems when you try to send the message.
After the loop finishes, the script tests the error file's size.
If there's something in the file, the script shows the errors
collected.
Otherwise, the script copies the new header file on top of the
original empty template.
If that succeeds, a zero exit status tells whatnow (the program
that called distprompter) that the edit went okay.
Otherwise the exit status remains 1 (the default value); whatnow
will remove the draft file.
[Table of Contents] [Index]
[Previous: Explanation of autoinc]
[Next: Explanation of edprofile]
|