home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Interactive Guide
/
c-cplusplus-interactive-guide.iso
/
c_ref
/
csource4
/
262_01
/
cmenu.txt
< prev
next >
Wrap
Text File
|
1988-03-30
|
12KB
|
300 lines
.ce 2
Help for Those Really Long Dumb Menu Programs
by Robert Ramey
I just got my Hewlett Packard Laser Jet II. It seems to have
everything. All I have to do is read the manual so I can
set up the correct printer control codes. This machine has a zillon
options, all with funny codes and rules about which combinations
of codes and options are valid.
If I want to avoid a lot of tedious work sifting through manual
everytime I want to change the printer setup, I'll have to
write a program. Since I'm a programmer I don't mind
This is an application made to order for a series of menus.
Now if you've written one menu driven program you've written
them all. Or at least it seems that way. This is another
tedious job. Its still better than sifting through the printer
manual all the time as its only done once. However, writing
a menu driven program is tedious in itself.
I decided that this was the last tedious menu program I was going
to write.
1. Table Driven Code.
My solution is to write once a small piece of code which
handles dialog with the operator using a table of menus as a
guide. Then for each new menu application all I have to
write are the menus themselves and some "action routines"
which are invoked when certain points in the menu dialog are
touched.
Listing 1 shows the menus used for my laser printer program.
The labels place each menu in its proper place in the hierarchy.
At any time the operator can respond with one of the menu
selections, return, or escape.
Generally, escape will end the menu sequence. Return will return
to the previous menu, and a valid menu selection will display
a sub menu.
For example, the top menu is the first to be displayed.
If the operator selects 2 the menu labeled 2 is displayed.
If the operator then selects 1 the menu labeled 21 is displayed.
This flow of control will end if a menu selection has no sub
menu or a specific "action routine" is specified.
In this example, For menu
selection "4. Just exit" I have chosen exit(0) as the action
routine. When this selection is chosen function exit() will
be called with argument 0.
If there is no sub menu nor is there any specific "action routine"
a function action() will be called with a character pointer argument.
This argument contains the sequence of responses that led to the
current position in the hierarchy.
The basic idea of this program is that the operator moves around
the hierarchy of menus at will.
Listing 2 shows an example of the laser printer program in action.
Listing 3 shows how my laser printer setup program functions with
the menus. The main program calls the library function menu()
then exits. The program doesn't specify the flow of control.
This was specified in the menus.
Later I'll show the code for menu() along with
how the menus get into the program itself.
Now the only thing left is to code the action routines.
As the operator makes selections from the menus below selection
"2. Alter print setup", function action() is executed. This records
his choices in the array p.
From time to time he may select
option "1. Display current printer setup" to see which combination
he has selected so far and to check that is valid.
This will invoke the function display().
When he is satisfied, he selects "3. Write printer control codes
and exit". The finish() function is then executed which writes
the proper codes to the output file.
Separating the menus and flow of control from the
action routines makes writing a program
much more like writing a suite of small almost independent
programs. It also makes it much easier to expand the program:
All one must do is add on to menus and the action routines.
The rest of the program need not be touched.
2. How It Works
Each menu is stored in a data structure like that shown in listing 4.
To initiate a menu dialog the main program calls menu() with the
address of the initial menu as the parameter.
Listing 5 shows the menu() function. menu() displays the menu
whose address it has recieved as an argument a : character for
a prompt and waits for a response. When a response is recieved
it is compared against the first character in each line of the
menu. If it matches, the corresponding action routine is called
with its associated parameter. A hierarchy of menus is implemented
by specifying menu() as the action routine with the address of the
sub menu as the parameter.
When an action routine (including menu()) returns, it should specify
which of the previously displayed menus should be repeated.
If it returns a value of 0, the immediatly previous menu will be
repeated. If it returns a value of 1, the menu two levels back
will be repeated, etc.
In our laser printer setup program example, the function copies()
returns a value of 0 so that the "What do you want to change" menu
will be repeated. The function action() returns 1 so that
menus such as
Choose One.
1. Portrait(vertical)
2. Landscape(horizontal)
will not be repeated immediatly after a choice is made.
When ever the operator responds to a menu with RETURN, menu()
returns a value of 0 which repeats the previous menu.
Whenever the operator responds with ESCAPE, menu()
returns a value of 99 which will normally return through all the menus
back to the initial menu() call.
Listing 6 show the C code which implements the menus originally
displayed in Listing 1. This should help to clarify the functioning
of the menu() function as well as the the laser printer setup
program. To summarize, A table driven menu program consists of
three separatly compiled modules:
.in +4
A set of action routines along with a main program which calls menu(&m)
A set of menu data structures containing the text of the menus,
action routine addresses and paramters
A library function menu()
.in -4
On my system, the laser printer setup program is produced by the
following commands:
cc slptrm
cc slptr
zlink slptr,slptrm,crunlib/
3. Polishing Up the System.
So far we have simplfied the preparation of menu driven programs
by separating the menus from the program code.
Now we are now left with
the task of coding the original menu into a C data structure.
This is almost as bad as coding the menus into the original program.
However, the job of translating the menus in listing 1 to the
C program module in listing 6 is completely mechanical. It can
be turned over to a program we might call a menu compiler.
Listing 7 shows the program cmenu.c. This programs reads a file
in the format of list 1 and writes a file in the format of listing 6.
If the menu in figure 1 is in a file named slptrm.mnu, the new
command sequence is:
cmenu <a:slptrm.mnu >slptrm.c
cc slptrm
cc slptr
zlink slptr,slptrm,crunlib/
Each menu in the input file to menu compiler should be in the
following format:
.in +4
The menu index indicating its position in the hierarchy
should start in column 1.
Subsequent lines should start with tab.
The menu question on one or more lines.
The menu selections one per line. Menu selections are
distinguished by a '.' in the second character
position of the text.
.in -4
Each menu selection may be followed by a ';' and an action routine
and parameter. If no action routine is specified, the menu
compiler will fill in a default action routine and parameter.
If the menu indices indicate that a sub menu exists, the
default action routine is menu() and the default parameter is
the address of the submenu. If there exists no sub menu,
the default action routine is action() and the default parameter
is a pointer to a string which contains the menu responses which
brought us to this point.
Sometimes I want to insert an action routine within the
hierarchy of menus. For example, consider the following
menu and code fragment from a modem transfer program:
.nf
...
4. send file;gfname(m4)
...
4
Which prototcal do you want to use?
1. XMODEM with check sum
2. XMODEM with crc
3. YMODEM
4. Kermit