old-www/LDP/LG/issue58/okopnik.html

390 lines
21 KiB
HTML

<!--startcut ==============================================-->
<!-- *** BEGIN HTML header *** -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML><HEAD>
<title>Introduction to Shell Scripting LG #58</title>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#0000AF"
ALINK="#FF0000">
<!-- *** END HTML header *** -->
<CENTER>
<A HREF="http://www.linuxgazette.com/">
<H1><IMG ALT="LINUX GAZETTE" SRC="../gx/lglogo.jpg"
WIDTH="600" HEIGHT="124" border="0"></H1></A>
<!-- *** BEGIN navbar *** -->
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="nielsen2.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue58/okopnik.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../faq/index.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="okopnik2.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
<!-- *** END navbar *** -->
<P>
</CENTER>
<!--endcut ============================================================-->
<H4 ALIGN="center">
"Linux Gazette...<I>making Linux just a little more fun!</I>"
</H4>
<P> <HR> <P>
<!--===================================================================-->
<center>
<H1><font color="maroon">Introduction to Shell Scripting</font></H1>
<H4>By <a href="mailto:ben-fuzzybear@yahoo.com">Ben Okopnik</a></H4>
</center>
<P> <HR> <P>
<!-- END header -->
<BLOCKQUOTE>
<STRONG> "You wouldn't believe how many managers believe that you can
get a baby in one month by making nine women pregnant."</STRONG><BR>
<CITE> -- Marc Wilson</CITE>
</BLOCKQUOTE>
<H2>Random Wanderings</H2>
<p>Well, this should be the last article in the "Introduction to Shell
Scripting" series - I've had great feedback from a number of readers (and
thank you all for your kind comments!), but we've covered most of the <b>basics</b>
of shell scripting; that was the original purpose of the series. I may
yet pop up at some point in the future ("Oh, rats, I forgot to explain
XYZ!"), but those of you who've been following along should now consider
yourselves Big-Time Experts, qualified to carry a briefcase and sound important...
&lt;grin&gt; Well, at least you should have a pretty good idea
of how to write a script and make it work - and that's a handy skill.
<H2>A Valued Assistant</H2>
<p>Quite a while ago, I found myself in a quandary while writing a script
(NO-O-O! How unusual! &lt;grins&gt;); I had an array that contained a list
of command lines that I needed to execute based on certain conditions.
I could read the array easily enough, or print out any of the variables
- but what I needed was to execute them! What to do, what to do... as I
remember, I gave up for lack of that one capability, and rewrote the whole
(quite large) script (it was <u>not</u> a joyful experience). "eval" would
have been the solution.
<p>Here's how it works - create a variable called $cmd, like so:
<p><font face="Courier New,Courier">cmd='cat .bashrc|sort'</font>
<p>It's just an example - you could use any valid command(s). Now, you
can echo the thing -
<p><font face="Courier New,Courier">Odin:~$ echo $cmd</font>
<br><font face="Courier New,Courier">cat .bashrc|sort</font>
<br><font face="Courier New,Courier">Odin:~$</font>
<p>- but how do you execute it? Just running "cmd" produces an error:
<p><font face="Courier New,Courier">Odin:~$ $cmd</font>
<br><font face="Courier New,Courier">cat: .bashrc|sort: No such file or
directory</font>
<br><font face="Courier New,Courier">Odin:~$</font>
<p>This is where "eval" comes into its own: "<font face="Courier New,Courier">eval
$cmd"</font> would evaluate the content of the variable as if it had been
entered at the command line. This is not something that comes up too often...
but it is a capability of the shell that you need to be aware of.
<P> Note that "bash" has no problem executing a single command that is
stored as a variable, something like:
<PRE>
Odin:~$ N="cat .bashrc"
Odin:~$ $N
# ~/.bashrc: executed by bash(1) for non-login shells.
export PS1='\h:\w\$ '
umask 022
</PRE>
works fine. It's only when more complex commands, e.g., those that involve
+aliases or operators ("|", "&gt;", "&gt;&gt;", etc.) are used that
you would encounter problems - and for those times, "eval" is the answer.
<H2>Trapped Like a Rat</H2>
<p>One of the standard techniques in scripting (and in programming in general)
is that of writing data to temporary files - there are many reasons to
do this. But, and this is a big one, what happens when your users interrupt
that script halfway through execution? (For those of you who have scripts
like that and haven't thought of the issue, sorry to give you material
for nightmares. At least I'll show you the solution as well.)
<p>You guessed it: a mess. Lots of files in "/tmp", perhaps important data
left hanging in the breeze (to be deleted at next reboot), files thought
to be updated that are not... Yuck. How about a way for us to exit gracefully,
despite a frantic keyboard-pounding user who just <u>has</u> to run "Quake"
RIGHT NOW?
<p>The "trap" command provides an answer of sorts (shooting said user is
far more effective and enjoyable, but may get you talked about).
<p>
<hr WIDTH="100%">
<br><font face="Courier New,Courier">#!/bin/bash</font>
<p><font face="Courier New,Courier">function cleanup ()</font>
<br><font face="Courier New,Courier">{</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; stty intr "" #
Ignore 'Ctrl-C'; let him pound away...</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; echo "Wake up,
Neo."</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; sleep 2; clear</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; echo "The Matrix
has you."</font>
<p><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; echo "He's at it
again."|mail admin -s "Update stopped by $USER"</font>
<p><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; # Restore the original
data</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; tar xvzf /mnt/backup/accts_recvbl
-C /usr/local/acct</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; # Delete 'tmp'
stuff</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; rm -rf /tmp/in_process/</font>
<p><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; # OK, we've taken
care of the cleanup. Now, it's REVENGE time!!!</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; rm /usr/games/[xs]quake</font>
<p><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; # Give him a nice
new easy-to-remember password...</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; chpasswd $USER:~X%y!Z@zF%HG72F8b@Idiot&amp;(~64sfgrnntQwvff########^</font>
<p><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; # We'll back up
all his stuff... Oh, what's "--remove-files" do?</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; tar cvz --remove-files
-f /mnt/timbuktu/bye-bye.tgz /home/$USER</font>
<p><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; # Heh-heh-heh...</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; umount /mnt/timbuktu</font>
<p><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; stty intr ^C # Back
to normal</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; exit&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
# Yep, I meant to do that... Kill/hang the shell.</font>
<br><font face="Courier New,Courier">}</font>
<p><font face="Courier New,Courier">trap 'cleanup' 2</font>
<br>&nbsp;
<br><font face="Courier New,Courier">...</font>
<br>
<hr WIDTH="100%">
<p>There's a little of the BOfH inside every admin. &lt;grin&gt; (For those
of you not familiar with the "BOfH Saga", this is a <b>must</b> read for
every Unix admin; appalling and hideously funny. Search the Web.)
<p>DON'T run this script... yes, I know it's tempting. The point of "trap"
is, we can define a behavior whenever the user hits `Ctrl-Break' (or for
that matter, any time the script exits or is killed) that is much more
useful to us than just crashing out of the program; it gives us a chance
to clean up, generate warnings, etc.
<p>"trap" can also catch other signals; the fact is that even "kill", despite
its name, does not of itself `kill' a process - it sends it a signal. The
process then decides what to do with that signal (a crude description,
but generally correct). If you wish to see the entire list of signals,
just type "<font face="Courier New,Courier">trap -l</font>" or "<font face="Courier New,Courier">kill
-l</font>" or even "<font face="Courier New,Courier">killall -l</font>"
(which does not list the signal numbers, just names). The ones most commonly
used are 1)SIGHUP, 2)SIGINT, 3)SIGQUIT, 9)SIGKILL, and 15)SIGTERM.
<BLOCKQUOTE><EM>
[But SIGKILL is untrappable. -Ed.]
</EM></BLOCKQUOTE>
<p>There are also the `special' signals. They are: 0)EXIT, which traps
on any exit from the shell, and DEBUG (no number assigned), which can -
here's a nifty thing! - be used to troubleshoot shell scripts (it traps
every time a simple command is executed). DEBUG is actually more of an
"info only" item: you can have this exact action without writing any "trap"s,
simply by adding "-x" to your "hash-bang" (see "IN CASE OF TROUBLE..."
below).
<p>"trap" is a powerful little tool. In LG#37, Jim Dennis had a short script
fragment that created a
<A HREF="../issue37/lg_answer37.html#tag_greeting">secure directory under "/tmp"</A> for just this sort
of thing - temp files that you don't want exposed to the world. Pretty
cool gadget; I've used it a number of times already.
<H2>In Case of Trouble, Break Glass</H2>
<p>Speaking of troubleshooting, "bash" provides several very useful tools
that can help you find the errors in your script. These are switches -
part of the "set" command syntax - that are used in the "hash-bang" line
of the script itself. These switches are:
<p><font face="Courier New,Courier">-n&nbsp;&nbsp;&nbsp;&nbsp; Read the
shell script lines, but do not execute</font>
<br><font face="Courier New,Courier">-v&nbsp;&nbsp;&nbsp;&nbsp; Print the
lines as they're read</font>
<br><font face="Courier New,Courier">-x&nbsp;&nbsp;&nbsp;&nbsp; Prints
$PS4 (the "level of indirection" prompt) and the command just executed.</font>
<p>I've found that "-nv" and "-x" are the most useful invocations: one
gives you the exact location of a "bad" line (you can see where the script
would crash); the other, `noisy' though it is, is handy for seeing where
things aren't happening quite the right way (when, even though the syntax
is right, the action is not what you want). Good troubleshooting tools
both. As time passes and you get used to the quirks of error reporting,
you'll probably use them less and less, but they're invaluable to a new
shell script writer.
<br>To use them, simply modify the initial "hash-bang":
<br>
<hr WIDTH="100%">
<br><font face="Courier New,Courier">#!/bin/bash -nv</font>
<br><font face="Courier New,Courier">...</font>
<br>
<hr WIDTH="100%">
<H2>Use the Source, Luke</H2>
<p>Here's a line familiar to every "C" programmer:
<p><font face="Courier New,Courier">#include &lt;"stdio.h"&gt;</font>
<p>- a very useful concept, that of <u>sourcing external files</u>. What
that means is that a "C" programmer can write routines (functions) that
he'll use over and over again, store them in a `library' (an external file),
and bring them in as he needs them. Well - have I not said that shell scripting
is a mature, capable programming language? - we can do the same thing!
The file doesn't even have to be executable; the syntax that we use in
bringing it in takes care of that. The example below is a snippet of the
top of my function library, "Funky". Currently, it is a single file, a
couple of kB long, and growing apace. I try to keep it down to the most
useful functions, as I don't want to garbage up the environment space (is
the concept even applicable in Linux? Must find out...)
<p>There's a tricky little bit of "bash" maneuvering that's worth knowing:
if you create a variable called <font face="Courier New,Courier">BASH_ENV</font>
in your .bash_profile, like so:
<p><font face="Courier New,Courier">export BASH_ENV="~/.bash_env"</font>
<p>then create a file called ".bash_env" in your home directory, that file
will be re-read every time you start a `non-login non-interactive shell',
i.e., a shell script. A good place to put initialization stuff that is
shell-script specific; that's where I source "Funky" from - that way, any
changes in it are immediately available to any shell script.
<p>
<hr WIDTH="100%"><font face="Courier New,Courier">func_list ()&nbsp;&nbsp;&nbsp;&nbsp;
# lists all the functions in Funky</font>
<br><font face="Courier New,Courier">{</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; # Any time I need
a reminder of what functions I have, what</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; # they're called,
and what they do, I just type "func_list".</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; # A cute example
of recursion - a func that lists all funcs,</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; # including itself.</font>
<p><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; cat /usr/local/bin/Funky|grep
\(\)</font>
<br><font face="Courier New,Courier">}</font>
<p><font face="Courier New,Courier">getch ()&nbsp;&nbsp;&nbsp;&nbsp; #
gets one char from kbd, no "Enter" necessary</font>
<br><font face="Courier New,Courier">{</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; OLD_STTY=`stty
-g`</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; stty cbreak -echo</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; GETCH=`dd if=/dev/tty
bs=1 count=1 2&gt;/dev/null`</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; stty $OLD_STTY</font>
<br><font face="Courier New,Courier">}</font>
<p><font face="Courier New,Courier">...</font>
<p>
<hr WIDTH="100%">
<p>Not too different from a script, is it? No "hash-bang" is necessary,
since this file does not get executed by itself.&nbsp; So, how do we use
it in a script? Here it is (we'll pretend that I don't source "Funky" in
".bash_env"):
<p>
<hr WIDTH="100%"><font face="Courier New,Courier">#!/bin/bash</font>
<p><font face="Courier New,Courier">. Funky</font>
<p><font face="Courier New,Courier">declare -i Total=0</font>
<p><font face="Courier New,Courier">leave ()</font>
<br><font face="Courier New,Courier">{</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; echo "So youse
are done shoppin'?"</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; [ $Total -ne 0
] &amp;&amp; echo "Dat'll be $Total bucks, pal."</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; echo "Have a nice
day."</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; exit</font>
<br><font face="Courier New,Courier">}</font>
<p><font face="Courier New,Courier">trap 'leave' 0</font>
<br><font face="Courier New,Courier">clear</font>
<p><font face="Courier New,Courier">while [ 1 ]</font>
<br><font face="Courier New,Courier">do</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; echo</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; echo "Whaddaya
want? I got Cucumbers, Tomatoes, Lettuce, Onions,"</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; echo "and Radishes
today."</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; echo</font>
<p><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; # Here's where we
call a sourced function...</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; getch</font>
<p><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; # ...and reference
a variable created by that function.</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; case $GETCH</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; in</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
C|c) Total=$Total+1; echo "Them are good cukes." ;;</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
T|t) Total=$Total+2; echo "Ripe tomatoes, huh?" ;;</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
L|l) Total=$Total+2; echo "I picked da lettuce myself." ;;</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
O|o) Total=$Total+1; echo "Fresh enough to make youse cry!" ;;</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
R|r) Total=$Total+2; echo "Real crispy radishes." ;;</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
*) echo "Ain't got nuttin' like that today, mebbe tomorra." ;;</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; esac</font>
<p><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; sleep 2</font>
<br><font face="Courier New,Courier">&nbsp;&nbsp;&nbsp; clear</font>
<p><font face="Courier New,Courier">done</font>
<br>
<hr WIDTH="100%">
<p>Note the period before "Funky": that's an alias for the "source" command.
When sourced, "Funky" acquires an interesting property: just as if we had
asked "bash" to execute a file, it goes out and searches the path listed
in $PATH. Since I keep "Funky" in "/usr/local/bin" (part of my $PATH),
I don't need to give an explicit path to it.
<p>If you're going to be writing shell scripts, I strongly suggest that
you start your own `library' of functions. (HINT: Steal the functions from
the above example!) Rather than typing them over and over again, a single
"source" argument will get you lots and lots of `canned' goodies.
<H2>Wrapping Up the Series</H2>
<p>Well - overall, lots of topics covered, some "quirks" explained; all
good stuff, useful shell scripting info. There's a lot more to it - remember,
this series was only an <u>introduction</u> to shell scripting - but anyone
who's stuck with me from the beginning and persevered in following my brand
of pretzel-bending logic (poor fellows! irretrievably damaged, not even
the best psychologist in the world can help you now... :) should now be
able to design, write, and troubleshoot a fairly decent shell script. The
rest of it - understanding and writing the more complex, more involved
scripts - can only come with practice, otherwise known as "making lots
of mistakes". In that spirit, I wish you all lots of "mistakes"!
<br>&nbsp;
<p>Happy Linuxing!
<p>
<hr WIDTH="100%">Linux Quote of the Month:
<br><font face="Courier New,Courier">``The words "community" and "communication"
have the same root.</font>
<br><font face="Courier New,Courier">Wherever you put a communications
network, you put a community as</font>
<br><font face="Courier New,Courier">well. And whenever you <b>take away</b>
that network -- confiscate it, outlaw it, crash it, raise its price beyond
affordability -- then you hurt that community.</font><font face="Courier New,Courier"></font>
<p><font face="Courier New,Courier">Communities&nbsp; will fight to defend
themselves.&nbsp; People will fight harder and more bitterly to defend
their communities, than they will fight to defend their own individual
selves.''</font>
<br><font face="Courier New,Courier">&nbsp;-- Bruce Sterling, "Hacker Crackdown"</font>
<br>
<hr WIDTH="100%">
<br>REFERENCES
<p>The "man" pages for 'bash', 'builtins', 'stty'
<A HREF="../issue53/okopnik.html">"Introduction to Shell Scripting - The Basics", LG #53</A><BR>
<A HREF="../issue54/okopnik.html">"Introduction to Shell Scripting", LG #54</A><BR>
<A HREF="../issue55/okopnik.html">"Introduction to Shell Scripting", LG #55</A><BR>
<A HREF="../issue57/okopnik.html">"Introduction to Shell Scripting", LG #57</A><BR>
<!-- *** BEGIN copyright *** -->
<P> <hr> <!-- P -->
<H5 ALIGN=center>
Copyright &copy; 2000, Ben Okopnik<BR>
Published in Issue 58 of <i>Linux Gazette</i>, October 2000</H5>
<!-- *** END copyright *** -->
<!--startcut ==========================================================-->
<HR><P>
<CENTER>
<!-- *** BEGIN navbar *** -->
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="nielsen2.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue58/okopnik.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../faq/index.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="okopnik2.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
<!-- *** END navbar *** -->
</CENTER>
</BODY></HTML>
<!--endcut ============================================================-->