403 lines
22 KiB
HTML
403 lines
22 KiB
HTML
|
<!--startcut ==============================================-->
|
||
|
<!-- *** BEGIN HTML header *** -->
|
||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||
|
<HTML><HEAD>
|
||
|
<title>Making Smalltalk: The Making of aPerson LG #62</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="silva.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/issue62/steffler.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="lg_backpage62.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">Making Smalltalk: The Making of aPerson</font></H1>
|
||
|
<H4>By <a href="mailto:jagwar@magma.ca">Jason Steffler</a></H4>
|
||
|
</center>
|
||
|
<P> <HR> <P>
|
||
|
|
||
|
<!-- END header -->
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
<table COLS=2 WIDTH="100%" NOSAVE >
|
||
|
<tr NOSAVE>
|
||
|
<td NOSAVE>
|
||
|
<center>
|
||
|
<h1>
|
||
|
<img SRC="misc/steffler/makingSmalltalk.png" ></h1></center>
|
||
|
|
||
|
<center>
|
||
|
<h3>
|
||
|
The Making of aPerson</h3></center>
|
||
|
|
||
|
<p>
|
||
|
<br>
|
||
|
<br>
|
||
|
<center>
|
||
|
<p>By <a href="mailto:jagwar@magma.ca">Jason Steffler</a></center>
|
||
|
</td>
|
||
|
|
||
|
<td WIDTH="200" NOSAVE>
|
||
|
<center><img SRC="misc/steffler/penguinInBalloon.gif" height=230 width=200></center>
|
||
|
</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
|
||
|
<div align=right>
|
||
|
<hr WIDTH="100%"><b>Article #4 - Feb 2000</b></div>
|
||
|
|
||
|
<h2>
|
||
|
<a NAME="abstract"></a>Abstract</h2>
|
||
|
For those who haven't read the previous articles, be
|
||
|
sure to read <font size=+1><a href="http://www.linuxgazette.com/issue60/steffler.html#statementOfPurpose">the
|
||
|
statement of purpose</a></font> first. This month, we're going to
|
||
|
discuss the making of <b>aPerson
|
||
|
</b>- how to use browsers to do so and
|
||
|
some OO considerations. For those looking to read the whole series
|
||
|
locally or information about upcoming articles, you can
|
||
|
<font size=+1><a href="http://www.magma.ca/~jagwar/makingSmalltalkForwardingPage.html">check
|
||
|
the MST page</a></font>. For those looking for further information
|
||
|
on learning Squeak, here are <a href="http://minnow.cc.gatech.edu/squeak/377">some
|
||
|
good resources</a>.
|
||
|
<br> Something different this month is that I'm dropping
|
||
|
the local links usage (was [LL]), as they weren't being used as much as
|
||
|
I'd anticipated.
|
||
|
<h2>
|
||
|
<a NAME="quoteOfTheDay"></a>Quote of the day</h2>
|
||
|
"The main problem for the C++ community today is to use Standard C++ in
|
||
|
the way it was intended rather than as a glorified C or a poor man's Smalltalk."
|
||
|
<br> - Bjarne Stroustrup (inventor of C++), "Getting From The
|
||
|
Past To The Future" , p. 23 C++ Report, SIGS Nov/Dec 1999 Vol1 No.10
|
||
|
<h2>
|
||
|
The Making of aPerson</h2>
|
||
|
Before we get started this month, the code-along folks
|
||
|
will want to file in the next version of the <b><a href="MakingSmalltalk-Article4.st">ScopedBrowser</a></b>
|
||
|
that was introduced last month. If you saved your image with the
|
||
|
browser in it - don't worry about conflicts as this new filein will replace<sup><a href="#footnotes">1</a></sup>
|
||
|
the previous <b>ScopedBrowser </b>(<a href="http://www.linuxgazette.com/issue60/steffler.html#puttingItAllTogether">see
|
||
|
article 2 on how to file in</a>) This time around I've added a few more
|
||
|
classes to the scope; the classes that are used for making a <b>Person</b>
|
||
|
class, and also a few more classes for good measure:
|
||
|
<ul>
|
||
|
<li>
|
||
|
<b>Boolean</b> (the super class of True and False)</li>
|
||
|
|
||
|
<li>
|
||
|
<b>False</b></li>
|
||
|
|
||
|
<li>
|
||
|
<b>Object</b> - the base object of Smalltalk that all other objects are
|
||
|
subclassed from<sup><a href="#footnotes">2</a></sup></li>
|
||
|
|
||
|
<li>
|
||
|
<b>True</b></li>
|
||
|
|
||
|
<li>
|
||
|
<b>Workspace</b> - the class we will be using to open textual windows</li>
|
||
|
|
||
|
<li>
|
||
|
<b>ScopedBrowser</b> - in case you're curious about the browser that we're
|
||
|
using. If you want to fiddle around with this guy, be careful to
|
||
|
save your image first, as it's not too hard to mess something up that affects
|
||
|
the browser that you're using to mess around in.</li>
|
||
|
</ul>
|
||
|
On the above note, you can fiddle with anything you
|
||
|
wish in Smalltalk, including the <a href="#articleGlossary">base classes</a>.
|
||
|
Though you can (and sometimes need to) do base class changes, it's generally
|
||
|
not advised as it's very easy to mess up the image or to break forwards
|
||
|
compatibility. If time/space allows in a future article we'll talk
|
||
|
more about base class changes. For now just be aware to save your
|
||
|
image before doing base class changes, and if you decide to keep a base
|
||
|
class change, keep a backup of your image before the change.
|
||
|
<br> Now getting to coding the <b>Person</b> class, if
|
||
|
you filed in the
|
||
|
<b>Person</b> code from article 2, then you'll either
|
||
|
want to remove that class from your system, or code this example as <b>Person2</b>,
|
||
|
or <b>JrsPerson
|
||
|
</b>(prefix with your initials), or some such thing to
|
||
|
avoid <a href="#articleGlossary">namespace collisions</a>.
|
||
|
<br> I'm going to remove the <b>Person</b> class and
|
||
|
start fresh, if you decide to to do this route, then open the <b>ScopedBrowser</b>
|
||
|
by doing this code:
|
||
|
<p> <b>ScopedBrowser openBrowserForArticle4</b>
|
||
|
<p> You'll notice that there are seven categories available,
|
||
|
including <b>MakingSmalltalk-Article2</b>. If the Person class exists,
|
||
|
it should be in this category. <b>Left-click</b> on the <b>Person</b>
|
||
|
class, then <b>middle-click>Remove</b>. You'll get a confirmation
|
||
|
dialog, click <b>yes</b> in this. You'll see now that the category
|
||
|
<b>MakingSmalltalk-Article2</b>
|
||
|
is empty.
|
||
|
<br> Now, to first declare our <b>Person</b> class, in
|
||
|
the <b>ScopedBrowser</b>, left click on the <b>MakingSmalltalk-Article4</b>
|
||
|
category, you'll see the code pane displays a nice class template for you.
|
||
|
It's pretty straightforward:
|
||
|
<blockquote><b>Object subclass: #NameOfClass</b>
|
||
|
<br><b>instanceVariableNames: 'instVarName1 instVarName2'</b>
|
||
|
<br><b>classVariableNames: 'ClassVarName1 ClassVarName2'</b>
|
||
|
<br><b>poolDictionaries: ''</b>
|
||
|
<br><b>category: 'MakingSmalltalk-Article4'</b></blockquote>
|
||
|
Don't worry about what a pool dictionary or class variable
|
||
|
is, just edit this template such that it looks like:
|
||
|
<br><i>[ex1a]</i>
|
||
|
<blockquote><b>Object subclass: #Person</b>
|
||
|
<br><b>instanceVariableNames: ''</b>
|
||
|
<br><b>classVariableNames: ''</b>
|
||
|
<br><b>poolDictionaries: ''</b>
|
||
|
<br><b>category: 'MakingSmalltalk-Article4'</b></blockquote>
|
||
|
<b> </b>Then <b>middle-click>accept.</b> You'll
|
||
|
notice that the <b>Person</b> class has been added to the article 4 category.
|
||
|
Now lets add a method, the first method we discussed in article 2 was <b>sing.Right-click>new
|
||
|
category...>new...</b> in the method categories pane, and type in: <b>Singing</b>
|
||
|
as the method category name, then click on the <b>Accept</b> button.
|
||
|
You'll notice that the code pane gives you a nice method template:
|
||
|
<blockquote><b>message selector and argument names</b>
|
||
|
<br><b> "comment stating purpose of message"</b>
|
||
|
<p><b> | temporary variable names |</b>
|
||
|
<br><b> statements</b></blockquote>
|
||
|
Copy over this method template with our <b>sing</b>
|
||
|
method, then <b>right-click>accept</b>.
|
||
|
<br><i>[ex1b]</i>
|
||
|
<blockquote><b>sing</b>
|
||
|
<br><b> (Workspace new contents: 'Do be do be doooooo.')
|
||
|
openLabel: 'A bad impression of Sinatra'.</b></blockquote>
|
||
|
<b> </b>You'll notice that the <b>sing</b> method appears
|
||
|
in the method category <b>Singing</b>. You can now go to a workspace,
|
||
|
and do: <b>Person new sing</b>, and the bad impression will appear.
|
||
|
For the read-along folks, the browser looks like:
|
||
|
<br><img SRC="misc/steffler/ScopedBrowser1.png" height=283 width=469>
|
||
|
<p> Now the next method we discussed was <b>sing:, </b>and
|
||
|
it could take a parameter: <b>'MaryHadALittleLamb'</b>. You can see the
|
||
|
obvious difficulty with our first sing method - the song and label are
|
||
|
hard-coded along with the activity that we're doing. It'd make more
|
||
|
sense to have one method that knows the activity we're doing (singing),
|
||
|
and be able to have it accept parameters from various methods to simplify/reuse
|
||
|
our code. That way, if we need to change how the singing is done
|
||
|
in the future, there are less spots to update. Lets make a new method
|
||
|
(remember, we're denoting private methods with a 'my' prefix) that isolates
|
||
|
how we're doing the singing from what is being sang:
|
||
|
<br><i>[ex1c]</i>
|
||
|
<blockquote><b>mySing: someLyrics withTitle: aTitle</b>
|
||
|
<br><b> (Workspace new contents: someLyrics) openLabel:
|
||
|
aTitle</b></blockquote>
|
||
|
Then, lets <a href="#articleGlossary">refactor</a> our
|
||
|
first <b>sing</b> method to reuse this private method, just copy right
|
||
|
over the sing method or edit the text directly and accept it:
|
||
|
<br><i>[ex1d]</i>
|
||
|
<blockquote><b>sing</b>
|
||
|
<br><b> self mySing: 'Do be do be doooooo.' withTitle:
|
||
|
'A bad impression of Sinatra'</b></blockquote>
|
||
|
Note that we still have the song lyrics and title hard
|
||
|
coded in the <b>sing</b> public method, so they're not directly accessible
|
||
|
by other objects or methods of <b>Person, </b>but it is better than it
|
||
|
originally was. We'll show a better way to organize your code when
|
||
|
we do our next song, and illustrate how factoring out the song lyrics can
|
||
|
be useful. With this in mind, lets create a lyrics method for our
|
||
|
next song:
|
||
|
<br><i>[ex1e]</i>
|
||
|
<blockquote><b>maryHadALittleLambLyrics</b>
|
||
|
<br><b> ^'Mary had a little lamb, little lamb, little
|
||
|
lamb, Mary had a little lamb whose fleece was white as snow.'</b></blockquote>
|
||
|
Now we can add our <b>sing:</b> method that reuses our
|
||
|
private <b>mySing:withTitle:</b> method. It has some very simple
|
||
|
logic that defaults to our bad impression if we don't understand what is
|
||
|
being requested to sing. Note: when we ask <b>aSong </b>if
|
||
|
it's equal to <b>'MaryHadALittleLamb', </b>a boolean object is returned
|
||
|
(true or false), and then we ask the boolean object if it's true or if
|
||
|
it's false.
|
||
|
<br><i>[ex1f]</i>
|
||
|
<blockquote><b>sing: aSong</b>
|
||
|
<br><b> aSong = 'MaryHadALittleLamb'</b>
|
||
|
<br><b> ifTrue: [self mySing:
|
||
|
self maryHadALittleLambLyrics withTitle: 'Mary had a little lamb']</b>
|
||
|
<br><b> ifFalse: [self sing].</b></blockquote>
|
||
|
Remember, as soon as you add a method, you can
|
||
|
execute it if you like, since no compile-n-link-n-run cycle is needed.
|
||
|
Finally, lets add our last public singing method, which is similar to the
|
||
|
above method:
|
||
|
<br><i>[ex1g]</i>
|
||
|
<blockquote><b>sing: aSong andDoIt: anAdjective</b>
|
||
|
<br><b> aSong = 'MaryHadALittleLamb'</b>
|
||
|
<br><b> ifTrue: [self mySing:
|
||
|
self maryHadALittleLambLyrics inManner: anAdjective withTitle: 'Mary had
|
||
|
a little lamb']</b>
|
||
|
<br><b> ifFalse: [self sing].</b></blockquote>
|
||
|
Which assumes another private method, which builds
|
||
|
on our first private method. You can see how having the lyrics factored
|
||
|
out into a separate method is useful, as we can access them as needed to
|
||
|
sing loudly (uppercase) or quietly (lowercase).
|
||
|
<br><i>[ex1h]</i>
|
||
|
<blockquote><b>mySing: someLyrics inManner: anAdjective withTitle: aTitle</b>
|
||
|
<br><b> "Using simple logic here for illustrative purposes - if the
|
||
|
adjective is not 'loudly' or 'quietly' just ignore how we're being asked
|
||
|
to sing"</b>
|
||
|
<br><b> | tmpLyrics |</b>
|
||
|
<br><b> anAdjective = 'loudly'</b>
|
||
|
<br><b> ifTrue: [tmpLyrics :=
|
||
|
someLyrics asUppercase].</b>
|
||
|
<br><b> anAdjective = 'quietly'</b>
|
||
|
<br><b> ifTrue: [tmpLyrics :=
|
||
|
someLyrics asLowercase].</b>
|
||
|
<br><b> self mySing: tmpLyrics withTitle: aTitle</b></blockquote>
|
||
|
I'm leaving out the <b>whatIsMyHeight</b> method,
|
||
|
as it's just more of the same of what we've covered. Note the simple
|
||
|
conditional logic that we've used here. This is something that can
|
||
|
be avoided by programming methods like double dispatching, but that's beyond
|
||
|
the scope of this article. Avoiding conditional logic is often cited
|
||
|
as one of the benefits of OO programming. To illustrate, image that
|
||
|
we have 30 different songs that we needed to sing, this would lead us to
|
||
|
have 30 different <b>ifTrue</b> statements, or a CASE statement (most Smalltalks
|
||
|
don't even have a CASE statement) with 30 cases. Then if we needed
|
||
|
to add 5 more songs, we'd need to track down all the places where we used
|
||
|
the 30 cases and add our additional 5; chances are there's more than one
|
||
|
place where there are these 30 cases, and you'll eventually have problems
|
||
|
keeping all the cases in sync. For simplicity though, we're using
|
||
|
conditional logic for illustrative purposes.
|
||
|
<br>
|
||
|
<h2>
|
||
|
<a NAME="lookingForward"></a>Looking forward</h2>
|
||
|
The next article will cover OO thinking now that we've
|
||
|
covered many of the basics, it'll cover some of the advantages and differences
|
||
|
of OO programming from other types of programming.
|
||
|
<h2>
|
||
|
<a NAME="sweetSqueak"></a>A Sweet Squeak - Luke, use the debugger</h2>
|
||
|
This section won't explore/explain code or example,
|
||
|
but merely present a neat thing to try out. This month, we're going
|
||
|
to get into debugging, as this is something I consider a sweet characteristic
|
||
|
of Smalltalk. Part and parcel of not having a compile-link-run
|
||
|
cycle in programming, is that you can debug and correct your programs in
|
||
|
realtime from the debugger.
|
||
|
<br> To illustrate this, we're going to insert a <b>halt</b>
|
||
|
message somewhere in <b>aPerson's </b>code. Edit the <b>sing</b>
|
||
|
method, and as the first line put <b>self halt. </b>So your method
|
||
|
should look like:
|
||
|
<br><i>[ex2]</i>
|
||
|
<p><b> self halt.</b>
|
||
|
<br><b> self mySing: 'Do be do be doooooo.' withTitle:
|
||
|
'A bad impression of Sinatra'.</b>
|
||
|
<p><b> </b>Then ask your person to sing by doing: <b>Person
|
||
|
new sing</b>, you'll see the debugger:
|
||
|
<br><img SRC="misc/steffler/debugger1.png" >
|
||
|
<p> Click on the Debug button, and click on the 2nd method on the
|
||
|
method stack. For the read-along folks, you'll see:
|
||
|
<br><img SRC="misc/steffler/debugger2.png" >
|
||
|
<p><b> </b>Now, you can delete the <b>self halt.</b>, then
|
||
|
<b>middle-click>accept</b>
|
||
|
- at this point your code is updated such that any requests to the <b>sing</b>
|
||
|
method get the new version. Now you can continue execution by moving your
|
||
|
pointer over the top pane, and
|
||
|
<b>middle-click>proceed</b>
|
||
|
<br> This can be a very powerful way of programming/debugging
|
||
|
your code. Remember, <u>the debugger is your friend</u> - many new
|
||
|
Smalltalkers don't use the debugger to it's full advantage because the
|
||
|
compile-link-run cycle is so ingrained. Following this model, you
|
||
|
use the debugger to step in and over code until you see the problem, then
|
||
|
you close the debugger and go back to your browser to make the code update,
|
||
|
then try running the code again. Why not just make the code update
|
||
|
while you're in the debugger and continue on your merry way!? Another
|
||
|
very powerful technique can be to insert a breakpoint in a troublesome
|
||
|
part of the code, then manually change the live objects to simulate various
|
||
|
conditions to help you debug (scribbling temp values for different scenarios
|
||
|
on scaps of paper can become a thing of the past). Sometimes it is
|
||
|
necessary to begin execution from the start because the context will be
|
||
|
so different, but more times than not you can just fix things right in
|
||
|
your debugging session.
|
||
|
<h2>
|
||
|
<a NAME="questionsAndAnswers"></a><b>Questions and Answers</b></h2>
|
||
|
No questions this month, I suppose everything was crystal clear last month
|
||
|
;-)
|
||
|
<h2>
|
||
|
<a NAME="articleGlossary"></a><b>Article Glossary</b></h2>
|
||
|
This is a glossary of terms that I've used for the first time in this series,
|
||
|
or a term that I want to refine. If you don't see a term defined
|
||
|
here, try the <a href="http://www.magma.ca/~jagwar/makingSmalltalkForwardingPage.html">ongoing
|
||
|
glossary</a>.
|
||
|
<p><b>Base Classes</b>
|
||
|
<br> The base Smalltalk classes
|
||
|
that you start a clean image with. For example, Object, Boolean,
|
||
|
Magnitude, Stream, etc. Though you can (and sometimes need to) do
|
||
|
base class changes, it's generally not advised as it's very easy to mess
|
||
|
up the image or to break forwards compatibility.
|
||
|
<p><b>Namespace</b>
|
||
|
<br> A namespace is much what
|
||
|
it sounds like - a finite space that names can be defined and identified
|
||
|
in. For example, if you're writing a program and you wanted to define
|
||
|
a class called Object, you'd be out of luck as the class Object already
|
||
|
exists. If you were able to define a second class called Object, how would
|
||
|
the computer know the difference between the two? In most Smalltalks
|
||
|
there is a single namespace (VisualWorks Smalltalk is the notable exception).
|
||
|
<p><b>Namespace collision</b>
|
||
|
<br> When two companies/groups/people
|
||
|
try to name thier classes with the same name, or define methods off of
|
||
|
a class with the same name. To help avoid namespace collision not
|
||
|
only within your own projects, but from other companies like third party
|
||
|
vendors, it's a common practice to prefix your classes with some acronym,
|
||
|
for example, if you work for MegaCorp you might prefix all your classes
|
||
|
with 'Mc'
|
||
|
<p><b>Refactor</b>
|
||
|
<br> To change/update/reorganize
|
||
|
your code to make it (hopefully ;-) cleaner, faster, more robust.
|
||
|
Refactoring typically moves around responsibilities and/or makes larger
|
||
|
methods into smaller methods.
|
||
|
<br>
|
||
|
<h2>
|
||
|
<a NAME="footnotes"></a><b>Footnotes</b></h2>
|
||
|
[1] More accurately, it will overwrite, but not remove any existing structure/behaviour.
|
||
|
For example, if you have a method on a class that doesn't exist in the
|
||
|
corresponding class for the filein, then the method will still be there
|
||
|
after the filein. Change sets are a little different, and we'll discuss
|
||
|
them in a future article if there's enough interest.
|
||
|
<p>[2] Well, this isn't technically true. As in many other parts
|
||
|
of this series I'm making a simplifying statement/assumption. To
|
||
|
be more clear, for the purposes of beginners it simplifies things to consider
|
||
|
Object the root object of Smalltalk.
|
||
|
<br>To be more precise, Smalltalk has an elegant method for bootstrapping
|
||
|
its class hierarchy that's rooted in meta classes and circular definition.
|
||
|
If you don't understand the next sentence, don't worry because typically
|
||
|
it's nothing you need worry about for regular Smalltalking. Briefly,
|
||
|
in Squeak there's actually a class called ProtoObject that is the super
|
||
|
class of Object, and ProtoObject inherits from nil - the sole instance
|
||
|
of UndefinedObject, which inherits from Object. Other Smalltalk flavours
|
||
|
have something similar to this.
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
<!-- *** BEGIN copyright *** -->
|
||
|
<P> <hr> <!-- P -->
|
||
|
<H5 ALIGN=center>
|
||
|
|
||
|
Copyright © 2001, Jason Steffler.<BR>
|
||
|
Copying license <A HREF="../copying.html">http://www.linuxgazette.com/copying.html</A><BR>
|
||
|
Published in Issue 62 of <i>Linux Gazette</i>, February 2001</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="silva.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/issue62/steffler.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="lg_backpage62.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 ============================================================-->
|