Shell programming and simple menus - part 2

Getting more mileage out of your menu

Summary
This month we build on the basic menu system explained in last month's column, showing you how to execute different menu files through one "menu driver." We take you through the code, step by step and, in the process, pass on a few tips on using the shell as a programming language. (3,200 words)

In last month's column we started a simple menu system, as useful utility that illustrated several shell programming concepts. Now let's take that a step further. In the next example, the menu system is broken into two parts: the shell script that executes the menu, and a separate "data" file that contains the menu prompts and commands to be executed.

Listing 2 looks very similar to the first menu in last month's Listing 1 (see sidebar) except that it is missing all of the menu prompts and commands to execute! Where are they?

Listing 3 is an example of a menu file to be processed by shellmenu. This file contains the missing information. You have to study both listings to see how these work together. Both listings include line numbers to simplify the explanations.

If you compare last months Listing 1 to Listing 2 and Listing 3 from this month, you will see that these two listings are almost identical, but the prompts and function definitions have been split out of Listing 2 and placed in Listing 3.

The key points to look at in Listing 2 are "TEST THE COMMAND LINE" at lines 68 through 80, "TEST THE MENU FILE" at lines 81 through 100 and "READ THE MENU FILE" at lines 132 through 138.

Be sure that shellmenu has been given execute privileges with the command:

chmod a+x shellmenu

To start this menu using the file named mf.mnu shown in Listing 3 as the menu, type the shellmenu command followed by the name of the file to use as the menu.

shellmenu mf.mnu

This causes the value mf.mnu to be assigned to a variable, $1, inside the shell script. The variables $0, $1, $2, $3 and so on exist in all shell scripts and contain any values that were listed on the command line that was used to start the script. $0 contains the name of the shell script itself, shellmenu. $1 contains the first word on the command line after shellmenu, in this case mf.mnu. The $2 variable would contain the next word or argument on the command line after mf.mnu if there were any. The shell also creates a variable named $#. This variable contains the number of arguments that were on the command line after the command (in this case, shellmenu).

For the command: shellmenu mf.mnu the $# variable should contain the value 1 for the 1 argument on the command line.

In "TEST THE COMMAND LINE" at line 75 through 79, the value of $# is tested, and if it is not 1 then a usage message is displayed and the script exits. The syntax for the test is

if [ ! $# = 1 ]
then
    usage
    exit
fi

Carefully copy the spacing used in the command there are spaces around the left bracket and the right bracket. The "!" means NOT in this test. The test reads "if the number of arguments is not 1 then execute a usage() function and exit the script."

The usage() function is defined at lines 10 through 20 and attempts to describe the correct usage for the shellmenu script.

In "TEST THE MENU FILE", at line 87, a new variable, $MENU, is created and is assigned the value of $1. Another test is done to determine if the file exists and is readable. The test:

if [ -r $MENU ]

uses the name of the file in $MENU, mf.mnu, and then tests two conditions. The -r tests that the file exists and that it is readable. If so it returns "true" logically. We want the opposite test, so a NOT (!) is added in:

if [ ! -r $MENU ]

Again notice spaces around the left and right brackets.

This tests if the file mf.mnu exists and is readable, provides a message for the user, and displays the usage information and exits.

Finally the trick as to how the this menu system works appears in "READ THE MENU FILE" at line 87. Note the period in the command:

. $MENU

The period is a special character in a shell script that basically says read the following named file (or the file named in the variable) on this line as if it were part of this current script. It acts very much like a "include" or "copy" directive. As the file is included, the commands in it are executed.

Listing 2: Sample shell script menu driver

1. #!/bin/sh
2. # shellmenu
3. # Simple shell script menu driver under UNIX
4. # Main logic starts at MAIN LOGIC
5.  
6. #---------------------------------------
7. # FUNCTION DEFINITIONS
8. #---------------------------------------
9.  
10. # A function for usage info
11. usage () {
12. echo "Usage: shellmenu menu_file"
13. echo "  A menu file must contain 12 variables"
14. echo "  amenu through l menu"
15. echo "  defining the prompts for a menu"
16. echo "  and the definitions for 12"
17. echo "  functions apick() through lpick()"
18. echo "  that define the commands to be"
19. echo "  executed for each of the 12 picks"
20. }
21. # Define a function for invalid menu picks
22. # The function loads an error message into a variable
23. badchoice ()
24. {
25. MSG="Invalid Selection ... Please Try Again" ;
26. }
27. 
28. #---------------------------------------
29. # DISPLAY FUNCTION DEFINITION
30. #---------------------------------------
31. # This function displays the menu.
32. # The routine clears the screen, echoes
33. # the logo and menu prompts
34. # and some additional messages.
35. # Note that this definition does
36. # not cause the function to
37. # be executed yet, it just defines
38. # it ready to be executed.
39.  
40. displaymenu () {
41. # clear the screen
42. clear
43. echo `date`
44. echo
45. echo "\t\t\t" $LOGO
46. echo
47. echo "\t\tPlease Select:"
48. echo
49. echo "\t\t\t" $amenu
50. echo "\t\t\t" $bmenu
51. echo "\t\t\t" $cmenu
52. echo "\t\t\t" $dmenu
53. echo "\t\t\t" $emenu
54. echo "\t\t\t" $fmenu
55. echo "\t\t\t" $gmenu
56. echo "\t\t\t" $hmenu
57. echo "\t\t\t" $imenu
58. echo "\t\t\t" $jmenu
59. echo "\t\t\t" $kmenu
60. echo "\t\t\t" $lmenu
61. echo "\t\t\tx. Exit"
62. echo
63. echo $MSG
64. echo
65. echo Select by pressing the letter and then ENTER ;
66. }
67.  
68. #---------------------------------------
69. # TEST THE COMMAND LINE
70. #---------------------------------------
71. # There should only be one argument
72. # on the command line, which 
73. # should be the name of a menu file
74.  
75. if [ ! $# = 1 ]
76. then
77.     usage
78.     exit
79. fi
80.  
81. #---------------------------------------
82. # TEST THE MENU FILE
83. #---------------------------------------
84. # Assign the value of the first argument
85. # on the command line,
86.  
87. MENU=$1
88.  
89. # Test that the file exists and is readable.
90. # Abort if it is not
91.  
92. if [ ! -r $MENU ]
93. then
94.     echo "Can't locate or read " $MENU
95.     echo "Press ENTER to acknowledge."
96.     read $answer
97.     usage
98.     exit
99. fi
100.  
101. #---------------------------------------
102. # MAIN LOGIC
103. #---------------------------------------
104. # Clear out the error message
105. MSG=
106.  
107. #---------------------------------------
108. # MAIN LOOP
109. #---------------------------------------
110. # We're here because we have a file
111. # that can be read, so OK to
112. # continue with the main loop of the menu.
113.  
114. # Repeat the menu over and over
115. # Steps are:
116. # 1. Display the menu
117. # 2. 'read' a line of input from the keyboard
118. # 3. Clear the error message
119. # 4. Check the answer for a or A or b or B, etc. and dispatch
120. #    to the appropriate program or function or exit
121. # 5. If the entry was invalid call the
122. #     badchoice() function
123. #    to initialize MSG to an error message
124. # 6. This error message is used when setting
125. #    up the menu for a menu pick that is
126. #    valid but has no command
127. #    associated with it.
128. 
129. while  true
130. do
131.  
132. #---------------------------------------
133. # READ THE MENU FILE
134. # define the menu by reading in the file
135. # named on the command line
136. #---------------------------------------
137.     . $MENU
138.  
139. #---------------------------------------
140. # DISPLAY THE MENU
141. # and read the user pick
142. #---------------------------------------
143.     displaymenu
144.     read answer
145.     MSG=
146. # dispatch to the users selection
147.     case $answer in
148.         a|A) apick;;
149.         b|B) bpick;;
150.         c|C) cpick;;
151.         d|D) dpick;;
152.         e|E) epick;;
153.         f|F) fpick;;
154.         g|G) gpick;;
155.         h|H) hpick;;
156.         i|I) ipick;;
157.         j|J) jpick;;
158.         k|K) kpick;;
159.         l|L) lpick;;
160.         x|X) break;;
161.         *) badchoice;;
162.     esac
163. done
164.  <<<End of Listing>>>
Listing 3: mf.mnu -- a file of menu prompts and picks
1. # mf.mnu a simple script menu for shellmenu
2. # This file is read directly into shellmenu
3. # using the dot (.) prefix as in
4. #       shellmenu this_file
5. #
6. # causing the contents
7. # of this file to be read as part of the shell script
8.  
9. # The logo for this menu will be displayed
10. # at the top of the screen
11. LOGO="More Functions"
12.  
13. # Here are the list of menu prompts and the
14. # commands that go with them
15. # In the first list, enter the menu prompt
16. # as it should appear
17. # on the menu for each of the letters A - L
18. # In the second list, replace badchoice
19. # with any commands to run
20. # when that letter is pressed.
21.  
22. #---------------------------------------
23. # MENU PROMPTS
24. #---------------------------------------
25. amenu="a.  Job Scheduling"                ;
26. bmenu="b.  Set Standard Defaults "        ;
27. cmenu="c.  Display Directory Listing "    ;
28. dmenu="d   Other Functions"               ;
29. emenu=" "                                 ;
30. fmenu=" "                                 ;
31. gmenu=" "                                 ;
32. hmenu=" "                                 ;
33. imenu=" "                                 ;
34. jmenu=" "                                 ;
35. kmenu=" "                                 ;
36. lmenu=" "                                 ;
37.  
38. #---------------------------------------
39. # MENU ACTIONS AND COMMANDS
40. #---------------------------------------
41. apick () { shmnu ; }
42. bpick () { defmnt ; }
43. cpick ()
44. {
45. ls -l| more ; echo Press Enter ; read DUMMY ;
46. }
47. dpick () { shellmenu sf.mnu ; }
48. epick () { badchoice ; }
49. fpick () { badchoice ; }
50. gpick () { badchoice ; }
51. hpick () { badchoice ; }
52. ipick () { badchoice ; }
53. jpick () { badchoice ; }
54. kpick () { badchoice ; }
55. lpick () { badchoice ; }
56.  <<<End of Listing>>>

Putting it all together
Listing 4 is a logical representation of combination of shellmenu including the . $MENU command. Once you look at the combination, there is little difference between the original Listing 1 last month and Listing 4

Listing 4: The logical combination of shellmenu and mf.mnu

1. #!/bin/sh
2. # shellmenu
3. # Simple shell script menu driver under UNIX
4. # Main logic starts at MAIN LOGIC
5.  
6. #---------------------------------------
7. # FUNCTION DEFINITIONS
8. #---------------------------------------
9.  
10. # A function for usage info
11. usage () {
12. echo "Usage: shellmenu menu_file"
13. echo "  A menu file must contain 12 variables"
14. echo "  amenu through l menu"
15. echo "  defining the prompts for a menu"
16. echo "  and the definitions for 12"
17. echo "  functions apick() through lpick()"
18. echo "  that define the commands to be"
19. echo "  executed for each of the 12 picks"
20. }
21. # Define a function for invalid menu picks
22. # The function loads an error message into a variable
23. badchoice ()
24. {
25. MSG="Invalid Selection ... Please Try Again" ;
26. }
27. 
28. #---------------------------------------
29. # DISPLAY FUNCTION DEFINITION
30. #---------------------------------------
31. # This function displays the menu.
32. # The routine clears the screen, echoes
33. # the logo and menu prompts
34. # and some additional messages.
35. # Note that this definition does
36. # not cause the function to
37. # be executed yet. It just defines
38. # it as ready to be executed.
39.  
40. displaymenu () {
41. # clear the screen
42. clear
43. echo `date`
44. echo
45. echo "\t\t\t" $LOGO
46. echo
47. echo "\t\tPlease Select:"
48. echo
49. echo "\t\t\t" $amenu
50. echo "\t\t\t" $bmenu
51. echo "\t\t\t" $cmenu
52. echo "\t\t\t" $dmenu
53. echo "\t\t\t" $emenu
54. echo "\t\t\t" $fmenu
55. echo "\t\t\t" $gmenu
56. echo "\t\t\t" $hmenu
57. echo "\t\t\t" $imenu
58. echo "\t\t\t" $jmenu
59. echo "\t\t\t" $kmenu
60. echo "\t\t\t" $lmenu
61. echo "\t\t\tx. Exit"
62. echo
63. echo $MSG
64. echo
65. echo Select by pressing the letter and then ENTER ;
66. }
67.  
68. #---------------------------------------
69. # TEST THE COMMAND LINE
70. #---------------------------------------
71. # There should be only one argument
72. # on the command line, which 
73. # should be the name of a menu file
74.  
75. if [ ! $# = 1 ]
76. then
77.     usage
78.     exit
79. fi
80.  
81. #---------------------------------------
82. # TEST THE MENU FILE
83. #---------------------------------------
84. # Assign the value of the first argument
85. # on the command line,
86.  
87. MENU=$1
88.  
89. # Test that the file exists and is readable.
90. # Abort if it is not
91.  
92. if [ ! -r $MENU ]
93. then
94.     echo "Can't locate or read " $MENU
95.     echo "Press ENTER to acknowledge."
96.     read $answer
97.     usage
98.     exit
99. fi
100.  
101. #---------------------------------------
102. # MAIN LOGIC
103. #---------------------------------------
104. # Clear out the error message
105. MSG=
106.  
107. #---------------------------------------
108. # MAIN LOOP
109. #---------------------------------------
110. # We're here because we have a file
111. # that can be read, so it's OK to
112. # continue with the main loop of the menu.
113.  
114. # Repeat the menu over and over
115. # Steps are:
116. # 1. Display the menu
117. # 2. 'read' a line of input from the keyboard
118. # 3. Clear the error message
119. # 4. Check the answer for a or A or b or B etc. and dispatch
120. #    to the appropriate program or function or exit
121. # 5. If the entry was invalid call the
122. #     badchoice() function
123. #    to initialize MSG to an error message
124. # 6. This error message is used when setting
125. #    up the menu for a menu pick that is
126. #    valid but has no command
127. #    associated with it.
128. 
129. while  true
130. do
131.  
132. #---------------------------------------
133. # READ THE MENU FILE
134. # define the menu by reading in the file
135. # named on the command line
136. #---------------------------------------
137.     . $MENU
138. # <<< mf.mnu begins here >>>>
139. # mf.mnu a simple script menu for shellmenu
140. # This file is read directly into shellmenu
141. # using the dot (.) prefix as in
142. #       shellmenu this_file
143. #
144. # causing the contents
145. # of this file to be read as part of the shell script
146.  
147. # The logo for this menu will be displayed
148. # at the top of the screen
149. LOGO="More Functions"
150.  
151. # Here are the list of menu prompts and the
152. # commands that go with them
153. # In the first list, enter the menu prompt
154. # as it should appear
155. # on the menu for each of the letters A - L
156. # In the second list, replace badchoice
157. # with any commands to run
158. # when that letter is pressed.
159.  
160. #---------------------------------------
161. # MENU PROMPTS
162. #---------------------------------------
163. amenu="a.  Job Scheduling"                ;
164. bmenu="b.  Set Standard Defaults "        ;
165. cmenu="c.  Display Directory Listing "    ;
166. dmenu="d   Other Functions"               ;
167. emenu=" "                                 ;
168. fmenu=" "                                 ;
169. gmenu=" "                                 ;
170. hmenu=" "                                 ;
171. imenu=" "                                 ;
172. jmenu=" "                                 ;
173. kmenu=" "                                 ;
174. lmenu=" "                                 ;
175.  
176. #---------------------------------------
177. # MENU ACTIONS AND COMMANDS
178. #---------------------------------------
179. apick () { shmnu ; }
180. bpick () { defmnt ; }
181. cpick ()
182. {
183. ls -l| more ; echo Press Enter ; read DUMMY ;
184. }
185. dpick () { shellmenu sf.mnu ; }
186. epick () { badchoice ; }
187. fpick () { badchoice ; }
188. gpick () { badchoice ; }
189. hpick () { badchoice ; }
190. ipick () { badchoice ; }
191. jpick () { badchoice ; }
192. kpick () { badchoice ; }
193. lpick () { badchoice ; }
194.  
195. # <<< mf.mnu ends here >>>>
196.  
197. #---------------------------------------
198. # DISPLAY THE MENU
199. # and read the user pick
200. #---------------------------------------
201.     displaymenu
202.     read answer
203.     MSG=
204. # dispatch to the users selection
205.     case $answer in
206.         a|A) apick;;
207.         b|B) bpick;;
208.         c|C) cpick;;
209.         d|D) dpick;;
210.         e|E) epick;;
211.         f|F) fpick;;
212.         g|G) gpick;;
213.         h|H) hpick;;
214.         i|I) ipick;;
215.         j|J) jpick;;
216.         k|K) kpick;;
217.         l|L) lpick;;
218.         x|X) break;;
219.         *) badchoice;;
220.     esac
221. done
222.  <<End of Listing>>

The advantage of this second method over the menu version created in last month's issue is that you create only one menu driver program, shellmenu. The menus themselves can be separated out in individual files.

In listing 4, pick "d" executes the command shellmenu sf.mnu. The script executes the same shellmenu program, but this time it is processing a different menu file.

Listing 5 is an example of what sf.mnu might look like. Listing 5 is stripped of all comments and contains only the menu prompts and commands. The shellmenu script takes care of the logic of displaying the menu and dispatching to the various menu actions.

Listing 5: sf.mnu is run by selecting "d" from mf.mnu

LOGO="Some More Functions"
amenu="a.  Edit This Menu"                ;
bmenu="b.  A Directory Listing Wide"      ;
cmenu="c.  Directory Listing Long"        ;
dmenu=" "                                 ;
emenu=" "                                 ;
fmenu=" "                                 ;
gmenu=" "                                 ;
hmenu=" "                                 ;
imenu=" "                                 ;
jmenu=" "                                 ;
kmenu=" "                                 ;
lmenu=" "                                 ;
 apick () { vi sf.mnu ; }
bpick ()
{
ls -C|more ;
echo Press ENTER ;
read DUMMY ;
}
cpick () {
ls -l| more ;
echo Press Enter ;
read DUMMY ;
}
dpick () { badchoice ; }
epick () { badchoice ; }
fpick () { badchoice ; }
gpick () { badchoice ; }
hpick () { badchoice ; }
ipick () { badchoice ; }
jpick () { badchoice ; }
kpick () { badchoice ; }
lpick () { badchoice ; }

<<End of Listing>>
This should give you some simple examples of menus as well as useful tips on using the shell as a programming language. 

 

Listing 1: Sample shell script menu

1.  #!/bin/sh
2.  # sample.mnu
3.  # A simple script menu under Unix
4.  # Main logic starts at MAIN LOGIC
5.  # The logo will be displayed at the top of the screen
6.  LOGO="Sample Menu"
7. 
8.  #------------------------------------------------------
9.  # MENU PROMPTS
10. #------------------------------------------------------
11. # A list of menu prompts to be displayed for the user.
12. # The list can be modified.
13. # In this first list, enter the menu prompt as it should appear
14. # on the screen for each of the letters A - L. In this example
15. # menu pick variables emenu through lmenu are blank as there
16. # are no menu selections for keys E through L.
17. 
18. amenu="a.  Job Scheduling"                ;
19. bmenu="b.  Set Standard Defaults "        ; 
20. cmenu="c.  Display Directory Listing "    ; 
21. dmenu="d   Payroll Menu "                 ;
22. emenu=" "                                 ;
23. fmenu=" "                                 ;
24. gmenu=" "                                 ;
25. hmenu=" "                                 ;
26. imenu=" "                                 ;
27. jmenu=" "                                 ;
28. kmenu=" "                                 ;
29. lmenu=" "                                 ;
30. 
31. #------------------------------------------------------
32. # MENU FUNCTION DEFINITIONS
33. #------------------------------------------------------
34.  
35. # Define a function for invalid menu picks
36. # The function loads an error message into a variable
37. badchoice () { MSG="Invalid Selection ... Please Try Again" ; } 
38.  
39. # For each prompt displayed above, there is a list of 
40. # commands to execute in response to the user picking the
41. # associated letter.
42. # They are defined as functions
43. # apick () through lpick () where
44. # apick () corresponds to the menu
45. # prompt amenu which is selected
46. # selected by pressing a or A.
47. # bpick () corresponds to the menu
48. # prompt bmenu which is selected by
49. # pressing b or B and so on.
50. # Any menu item that is not
51. # assigned a set of commands, is
52. # assigned
53. # the function badchoice () as a default for that pick.
54. # If the user
55. # selects a menu key that is assigned
56. # to badchoice (). This function
57. # causes an error message to be
58. # displayed on the screen.
59. # To add items to this second
60. # list, replace badchoice ()
61. # with the commands to run when
62. # that letter is pressed.
63. # The following steps simply define
64. # the functions, but do not cause
65. # any shell program steps to be executed.
66. 
67. apick () { sched ; }
68. bpick () { defmnt ; }
69. cpick () { ls -l| more ; echo Press Enter ; read DUMMY ; }
70. dpick () { payroll.mnu ; }
71. epick () { badchoice ; }
72. fpick () { badchoice ; }
73. gpick () { badchoice ; }
74. hpick () { badchoice ; }
75. ipick () { badchoice ; }
76. jpick () { badchoice ; }
77. kpick () { badchoice ; }
78. lpick () { badchoice ; }
79. 
80. #------------------------------------------------------
81. # DISPLAY FUNCTION DEFINITION
82. #------------------------------------------------------
83. # This function displays the menu.
84. # The routine clears the screen, echoes
85. # the logo and menu prompts
86. # and some additional messages.
87. # Note that this definition does
88. # not cause the function to
89. # be executed yet, it just defines
90. # it ready to be executed.
91. 
92. themenu () {
93. # clear the screen
94. clear
95. echo `date`
96. echo
97. echo "\t\t\t" $LOGO
98. echo
99. echo "\t\tPlease Select:"
100. echo
101. echo "\t\t\t" $amenu
102. echo "\t\t\t" $bmenu
103. echo "\t\t\t" $cmenu
104. echo "\t\t\t" $dmenu
105. echo "\t\t\t" $emenu
106. echo "\t\t\t" $fmenu
107. echo "\t\t\t" $gmenu
108. echo "\t\t\t" $hmenu
109. echo "\t\t\t" $imenu
110. echo "\t\t\t" $jmenu
111. echo "\t\t\t" $kmenu
112. echo "\t\t\t" $lmenu
113. echo "\t\t\tx. Exit"
114. echo
115. echo $MSG
116. echo
117. echo Select by pressing the letter and then ENTER ;
118. }
119.  
120. #------------------------------------------------------
121. # MAIN LOGIC
122. #------------------------------------------------------
123. # Every thing up to this point has been to define
124. # variables or functions.
125. # The program actually starts running here.
126. 
127. # Clear out the error message variable
128. MSG=
129. 
130. # Repeat the menu over and over
131. # Steps are:
132. # 1. Display the menu
133. # 2. 'read' a line of input from the key board
134. # 3. Clear the error message
135. # 4. Check the answer for a or A or b or B etc. and dispatch
136. #    to the appropriate program or function or exit
137. # 5. If the entry was invalid call the badchoice () function
138. #    to initialize MSG to an error message
139. # 6. This error message is used when setting up the menu
140. #    for a menu pick that is valid but has no command
141. #    associated with it.
142. 
143. while  true
144. do
145. # 1. display the menu
146.   themenu
147. 
148. # 2. read a line of input from the keyboard
149.   read answer
150. 
151. # 3. Clear any error message
152.   MSG=
153. 
154. # 4. Execute one of the defined functions based on the
155. #    letter entered by the user.
156. 
157. # 5. If the choice was E through L, the pre-defined
158. #    function for that pick will execute badchoice ()
159. #    which loads an error message into MSG  
160. 
161.   case $answer in
162.       a|A) apick;;
163.       b|B) bpick;;
164.       c|C) cpick;;
165.       d|D) dpick;;
166.       e|E) epick;;
167.       f|F) fpick;;
168.       g|G) gpick;;
169.       h|H) hpick;;
170.       i|I) ipick;;
171.       j|J) jpick;;
172.       k|K) kpick;;
173.       l|L) lpick;;
174. 
175. #      If the user selects =91x=92 to exit then break out
176. #      of this loop
177.       x|X) break;;
178.  
179. # 6. If the entry was invalid call the badchoice function
180. #    to initialize MSG to an error message
181.         *) badchoice;;
182.  
183.   esac
184. 
185. #     Do it again until the user enters =91x=92.
186. done
187. <<>>

Contact us for a free consultation.

 

MENU:

 
SOFTWARE DEVELOPMENT:
    • EXPERIENCE
PRODUCTS:
UNIX: 

   • UNIX TUTORIALS

LEGACY SYSTEMS:

    • LEARN COBOL
    • PRODUCTS
    • GEN-CODE
    • COMPILERS   

INTERNET:
    • CYBERSUITE   
WINDOWS:

    • PRODUCTS


Search Now:
 
In Association with Amazon.com

Copyright©2001 King Computer Services Inc. All rights reserved.