...making Linux just a little more fun!

September 2004 (#106):


The Mailbag


HELP WANTED : Article Ideas
Submit comments about articles, or articles themselves (after reading our guidelines) to The Editors of Linux Gazette, and technical answers and tips about Linux to The Answer Gang.


Re: [SEAPY] Pie-thon

Mon, 2 Aug 2004 10:54:11 -0700
Brian Ingerson (ingy from ttul.org)
Question by A group of Python users in Seattle (seattle-python from lists.seapig.org)

Dan Sugalski (Parrot) gets a pie in the face by Guido van Rossum (Python). Dan lost a bet that Python would run faster on Parrot (Perl 6's virtual machine) than the standard C version.

http://www.oreillynet.com/pub/a/oscon2004/friday/index.html

-- -Mike Orr (aka. Sluggo)

FYI, The reason Dan lost was because he failed to complete 3 of the 7 tests. On the 4 he did complete, Parrot beat Python on 3 of them. Of those 3, Dan said Parrot was 2-3 times faster.

Cheers, Brian


Say please? Thank you...

Mon, 31 Aug 2004 10:54:11 -0000
Dean Earley (dean from earlsoft.co.uk)

What is the computer language that makes you say please? I used to know this. I might recognize it if I saw the name. A fellow LUG member suggested I look at http://sk.nvg.org/lang but nothing seemed obvious.

Someone out there knows, I hope.

Thank you!


GENERAL MAIL


MetaTheme

Mon, 23 Aug 2004 22:27:44 +0100
Jimmy O'Regan (The LG Answer Gang)

In my article "A guide to Windows technologies for Linux users" in issue 104 (http://linuxgazette.net/104/oregan2.html) I mentioned gtk-qt, which allows Gtk apps to use Qt themes. MetaTheme (http://metatheme.advel.cz) is a project to create a theme engine which is independant of the various toolkits. It currently supports Gtk and Qt, and work is under way to support Firefox. (http://metatheme.advel.cz)


Moaning goat meter

Sun, 8 Aug 2004 21:36:40 +0000
Predrag Ivanovic (predivan from ptt.yu)

Hi.

Linux Gazette is always a pleasure to read,thank you for that experience :-)

You're welcome, and thank you for taking the time to write to us. It's always nice to hear that people appreciate the work we do. :) -- Thomas
Glad you're enjoying it! -- Ben

In article "Which window manager" in #105 of LG MGM was mentioned.It looked interesting enough,so I looked it up.

I found it on http://www.linuxmafia.com/mgm

My mistake was that I started reading the FAQ while drinking coffee... :-)

Classic case of C|N>K

Whups. We should probably warn people about that. Me, I cautiously set aside any drinks and dangerous objects (err, well - except my mind) before opening any mail prefixed with [TAG]; I know this bunch. -- Ben

My nose is much better now,thank you...

Let me tell ya, be glad you weren't eating raisins or peanuts. Those can really hurt your nasal passages, and leave dents in your walls and furniture! -- Ben
It is amusing, isn't it. :) I remember it from way back. I do have to thank Rick Moen for resurrecting it. :) -- Thomas

You are really serious about this 'Making Linux a little more fun' thing,aren't you? Thank God for that.

Pedja

We have to be, Predrag, otherwise we'd be YAMP (Yet Another Man Page). :) -- Thomas
[grin] That's the LG to a tee. 'Fun' isn't our middle name - it would involve all sorts of boring paperwork, people would ask pointed questions about changes in marriage status, and applying to courts in various countries would be a real hassle - but we do enjoy ourselves, usually.
Thanks for writing! -- Ben
[Rick Moen] You're very welcome, Predrag. Furthermore, I'll take this opportunity to declare you an official Linux Gazette Evil Genius. We'll be sending you your white Persian cat and monocle via separate attachment. Go forth[1] and conquer.
[1] Or FORTH, perhaps.

Thank you,I'm honoured :-). Can I put that on a t-shirt?

Pedja


GAZETTE MATTERS


Ben's Boat...

Sat, 14 Aug 2004 13:07:59 -0400
Ben Okopnik (Linux Gazette Editor)
Wherein "The Answer Gang" express our general pleasure that the Editor In Chief's home survived the wake of the hurricanes pouring through Florida lately.
This is really Not Linux, but we send good wishes to any readers out there cleaning up after Mother Nature's mess. -- Heather

Ben,

I didn't catch from your earlier email where your anchorage was, did the ~ Charley course change spare your boat?

-- Brian Bilbrey

It made it worse, actually - Charlie's eye passed right through St. Augustine, from what I'm told - but my anchors held (I had a friend put out a third one; I normally ride to two anchors, but have eight on board.)
The winds were supposedly in the 70+ knot range (Category 1 hurricane) with 6' seas in the Matanzas river, where I'm actually anchored. The docks at the nearby city marina took a real pasting, but everything and everybody seems to be OK.
WHEW, big-time. When Charlie got up to 145mph, and shifted course to pass dead over St. Augustine, I was not a happy camper.

[Breen Mullins] Whew! indeed. Glad to hear all's okay -- I've been worried about you.

[John Karns] We're glad to hear that the boat is still in one piece. Our thoughts are with those who weren't quite so fortunate. At least it sounds like it went a little better than Andrew in 1992.

Yeah. Nineteen dead so far (no final count yet), lots of people hurt, at least $11B worth of property damage (again, not final figure); 300,000 people still without electricity; it was a rough one.
Quote from CNN:

...............

Despite the damage, Florida Lt. Gov. Toni Jennings said that her state was better prepared for Charley's onslaught than it had been in 1992, when Hurricane Andrew, a Category 5 storm with winds topping 155 mph, crushed the southern tip of the state from east to west.
"Where we are today is about where we were two weeks after Andrew," Jennings said.

...............

At this point you can hit up Google for all the gory details and then some; in addition to early storms it's been a heavy season so far too. -- Heather
My boat took some damage... from do-gooders without a clue - none from the hurricane. Nothing major; I spent last night on a tilt (50 degrees or so at 4 a.m. - low tide) and had to get her off the shoal this morning, then spent most of today grappling for my anchors (one line chafed through, and the idiots^Whelpful folks who moved her managed to lose a 45-lb. anchor and 300' of chain over the side.) Haven't found them yet - but hooked onto a big Fisherman-type anchor that somebody else lost here during the last few years, so that's what I'll be using temporarily.
All in all, normal life in the hurricane zone. :)

This page edited and maintained by the Editors of Linux Gazette
HTML script maintained by Heather Stern of Starshine Technical Services, http://www.starshine.org/

Published in Issue 106 of Linux Gazette, September 2004

More 2 Cent Tips!

See also: The Answer Gang's Knowledge Base and the LG Search Engine


[LG 105] 2c Tips #5: Open X-change

Jimmy O'Regan (The LG Answer Gang)

Serendipity - Linux Today are carrying a story about Open-Xchange Server, the engine of SuSE's Openexchange Server, today - Novell have convinced the owners to GPL it. Since it's Java based, it'll be accompanied by their Java Application Server too.

It'll be available to download at http://www.Open-Xchange.org and http://www.openexchange.com at the end of the month.

They consider it working but the feature set isn't frozen. The version name is "Janus" and they've numbered it 0.7.0 since they hope to cram in a few more features before 1.0.0. -- Heather


Converting a VCD file to MP3

Triyan W. Nugroho (iyan_kid from yahoo.com)

Hello Gang, here is my tips..

There is an easy way to convert VCD (Video CD) file into MP3. What you need are just MPlayer and Lame. MPlayer is used to convert the VCD file to WAV by using the PCM audio output, and then you can convert the WAV file to MP3 by using Lame.

First, you have to convert it to WAV by using the command:

$ mplayer -ao pcm /path/to/vcd/avseq01.dat

MPlayer will play the VCD file like usual, but with no sound. Just wait until it finished. You'll get a file 'audiodump.wav' that you can convert to MP3 by using the command:

$ lame -h audiodump.wav newfile.mp3

Switch -h is used to get high quality MP3 file, but bigger filesize.


Netkit

flavio ()

I just wanted to let you know about a nice project carried out at the Third University of Rome, namely "Netkit".

In a few words, Netkit lets you build a virtual network in order to do all the tests you would like to carry out, with special focus on learning how to use routing protocols and stuff like that. This virtual network is realised launching separate instances of Linux in different xterms - each being a full-fledged Linux box! There are methods to connect those Linux boxes via virtual Ethernets and also to communicate with them from the main host.

Netkit owes 90% of the stuff to UML, aka User Mode Linux, which is a project to let the user launch a Linux Box inside Linux. But I think that they had really an hard time to set up all the environment and create something really usable for these network experiences. Moreover, their website includes a lot of lectures about networking experiences using Netkit. To be honest, when I prepared my exams I found a couple of broken examples but everyone should be able to fix it or to... jump to the next example.

Given the fact that you're probably going to acquire tcpdump logs of the experiences if you decide to give Netkit a try, I also suggest to download and install Ethereal, which is a nice GUI to explore tcpdump sniffs.

You can find Netkit at http://www.netkit.org, more on UML at http://user-mode-linux.sourceforge.net. Ethereal is, of course, at http://www.ethereal.com.

Bye, Flavio.


That's not a bug, that's a feature

Jim Dennis (the LG Answer Guy)
Question by Gary Luker (gluker from southerndata.com)

I found an entry on google regarding a possible bug in bash. I have a script
that does the following.

#!/bin/sh

formatmonth=`date "+%m"` month="$(($formatmonth * 1))" formatday=`date "+%e"` day="$(($formatday - 1))"

when this is run if the current month is 08 it gives me this error. value too great for base (error token is "08")

have you found a fix for this? or know a workaround? thanks for any help!

Gary Luker: MCSE, MCSA, MCP, Linux+, SAIR/GNU Linux Pro

The arithmetic functions in bash (and C and some other programming languages) treat numeric literals with a leading zero as if they are in octal (base eight). Octal digits range from 0 to 7; therefore 08 is not a valid octal number.

Using GNU date you can use formatmonth=`date "+%-m"` to supress the zero padding of the month. That should take care of the problem. According the date man page the %e value is already "blank padded" so it will never be mis-interpreted as an octal number.

-- JimD


answer gang without the spam

Andrew du Preez (dups from neanderthal.ws)
As our readers may recall, The Answer Gang (tag@lists.linuxgazette.net) is really a mailing list where the regular subscribers get all queries and linuxy bits sent through, but it accepts questions and comments from anybody in the world. Of course this means spammers throughout the world email us too... -- Heather

Hello gang,

Here is a lurker's 2c worth on weeding out list spam. With a default set of rules, Spamassassin does a reasonable job of cleaning up my inbox. Recently though an increasing amount of spam sent to the TAG list has been getting past spamassassin. On a mailing list I am willing (more so than for personal mail) to accept the occasional false positive. So I wanted to try running all TAG emails through a stricter set of spam catching rules. Here is the configuration I have tried for the last week or so and found the spam:ham ratio greatly reduced.

A seperate spamassassin config file is used for TAG. From .procmailrc:

:0fw: spamassassin.lock
* ^Subject:.*\[TAG\]
| spamassassin -p ~/.spamassassin-tag/user_prefs

:0Efw: spamassassin.lock
| spamassassin

And the highlights from ~/.spamassassin-tag/user_prefs:

required_hits           4

(this is 5 by default)

score HTML_MESSAGE 3.0
score RCVD_IN_BL_SPAMCOP_NET 3.0
score RCVD_IN_SORBS 1.0
score BIZ_TLD 2.5

(arbitrarily cranking up the score for rules that seem to match the incoming spam)

This setup is working well for me so far (no false positives yet, and only a single junk mail getting through).

The answer gang mailing list is always a good read, and I was glad to find you guys hanging out at linuxgazette.net after being quite surprised at the new structure of the .com site.

'til later TAGsters

--andrew

We are looking into ways of solving it more directly. There'll hopefully be no need for this elaborateness when ( :) ) we do. People are rather busy this month, howwever. -- Thomas
The same problem exists for any decently worldwide mailing list, maybe the -users list from your favorite distribution or major app, so it's still a great Tip! Just beware setting your filtering a little too high, as Jimmy discovers... -- Heather
[Jimmy O'Regan] Heh. I only noticed a couple of days ago that my ISP is running SpamAssassin on my mail before it gets to me. I was wondering why everyone was complaining about the level of spam on the list :) Fortunately my spam is available for a week through their webmail setup, otherwise I'd never have got the contract a client sent me. Now to find a mail provider that doesn't take such liberties with my mail...


More on Linux for low end systems

John Murray (pursang from netwit.net.au)

Just thought I'd point out a piece I wrote a some time ago on getting good performance out of older hardware: http://users.netwit.net.au/~pursang/lofat.html

John

ps. Nice bike Ben! Hope you have as much fun on it as I do on my old GSXR750 :)

And Ben's going to be getting a first person taste of playing with low-end systems soon. Since his laptop has been been in and out of the silicon-lifeforms hospital, I'll be sending him my eldest still-operational laptop for his ongoing use as a proper spare. Thanks, John! -- Heather


Is your sync Hot or Not?

Paul Sephton (paul from inet.co.za)

Dear LinuxGazette

I am the lucky owner of a Palm m515 handheld. One of my frustrations in the past has been that under Linux, I need to establish a PPP connection to synchronise my palm with AvantGo web pages, whilst my preference is to use the normal HotSync button to synchronise the rest of the palm. A frustration, since I wanted the PPP connecttion to establish automatically without my having to do anything, but this conflicts with JPilot Hotsync.

I thought to share the following little bash script with you, which detects in which mode the Palm is attempting to communicate, and establishes a PPP link if that is what the Palm is attempting to do. Otherwise it waits until the Hotsync operation is over and repeats.

I hope that someone finds this useful. By the way, if Palm owners are experiencing trouble with USB drivers in the later (2.6.7+) kernels, I suggest they try 2.6.5. The USB support in that version is excellent.

Paul

See attached paul.palm_smartlink.bash.txt

That's all, folks


This page edited and maintained by the Editors of Linux Gazette
HTML script maintained by Heather Stern of Starshine Technical Services, http://www.starshine.org/

Published in Issue 106 of Linux Gazette, September 2004

The Answer Gang

LINUX GAZETTE
...making Linux just a little more fun!
(?) The Answer Gang (!)
By Jim Dennis, Karl-Heinz Herrmann, Breen, Chris, and... (meet the Gang) ... the Editors of Linux Gazette... and You!


We have guidelines for asking and answering questions. Linux questions only, please.
We make no guarantees about answers, but you can be anonymous on request.
See also: The Answer Gang's Knowledge Base and the LG Search Engine



Contents:

¶: Greetings From Heather Stern
(?)Language choice --or--
Generic installer locations
(?)Mail forwarding
(!)Process lifecycle --or--
Parent and Child
The birth and death of linux processes
(?)Upgrading KDE

(¶) Greetings from Heather Stern

Greetings, everyone, and welcome once more to the world of The Answer Gang. It's sunny September where I'm sittign but for some places the storms are rolling in. (See our Mailbag for soggy details.)

Meanwhile, it's getting toward Autumn. The blustering winds are starting to tug at the leaves, the blustering television sings of back to school and fall fashions. What have these to do with the world of techies? Not much...

Well, hold on a second there. Actually, the start of new academic seasons give the open source world a new batch of bored students and busy computer science departments with fat links to work on new projects. Techie bits have come up in the fashion world - depending on just how far away from the techie world you are, fashion might have been what dragged Linux into your view, as the Burlington Coat Factory sometime ago (about 5 years now) held a certain large hardware vendor over a barrel by taking them up on their system preload offer, but wanting the "ordinary consumer" class of systems en masse rather than a few rackmount servers. Since then, Burlington expanded their Linux use company wide and appear to be pretty darn happy with it.

Let me take a woman's intuition on a little shopping trip, then, and see where else Linux has come into fashion... I'm not talking T-shirts, mind you. I can get those at trade shows. I can buy them at ThinkGeek. I'm not talking about silly tidbits like a tie with 47 pictures of Tux on it. The ability to wear bumper sticker and /usr/share/games/fortunes sorts of wit is not fashion. I'm talking about the world where some crazy designers feel compelled to reinvent crayola colors every 6 months or so and force beautiful slinky babes (of both genders) to walk up and down a long stage wearing... err, well, sometimes it doesn't look silly, but often it does.

What doesn't help is how hard this can be to shop for. Search engines are with "in this fashion..." and "accessories" will get you peripherals. I really had to pull out the stops for this. You won't find it on freshmeat either (though I did find yet another lightweight wm called WMI).

You want jewelry, you have to look for jewelry - amd apparently it helps if you spell this the long way. Linux Jewellery has quite the debian collection, and some BSD stuff too.

You ladies who want to show off your fondness for Red Hat instead, consider these. Sorry it's not a fedora: http://www.thesilvermonkey.com/redhatsocietythemefashionjewelry.html

OTOH, sometimes a hat is just a hat. As early as 3 years ago some people were seeking geek chic that didn't include looking like "the techie" from several hundred feet away. THis article from that time relates, and I'm pleased to say many of its links are ever still good. Dress shirts wired for techie bits? My goodness:
http://www.geek.com/pdageek/features/chic/index.htm

That was 2001. In 2002, CNN wondered if wearable computing would hit the fall fashion highlights with an MP3 jacket. "We're just demonstrating the technology." they cried, it's not like we want money for this. Working with partners, etc. Get the giggles yourself while reding this:
http://news.com.com/2008-1082-941578.html

Will wonders not cease. It was shown off, among other curious inncations of the fashion world, at a fashion conference earlier this year. Infineon really has partnered with Rosner for a MP3 denim jacket (well it looks denim; I could be wrong) and O'Neill for a MP3 enabled snowboarding jacket -- now you "Extreme Linux" fans have something to wear.

No idea what OS the players use, sorry - fashion doesn't care about that. Is the open source concept affecting fashion more directly? Maybe not. But someone's thinking about it.

Amazing. Maybe we're getting somewhere. Until next month, folks. I and my red straw hat are off shopping.


(?) Generic installer locations

From Jay R. Ashworth

Answered By: Thomas Adam

I have a project, sitting here on the desk in front of me, for a menu program that I need to decide on an implementation language for.

The target environments are SCO OS5 and quite some different number of Linux distros; the possible languages are Python and Perl.

While I don't especially care if the buyer can see the code, what I do care about is not putting the installer into either dependency hell or conflict hell, while still having the pieces I need to do the work.

This seems like a reasonably common problem; any suggestions on how to approach it?

(!) [Thomas] Graphical, console or both?

(?) [Jay] I suppose that was pertinent, yes; it's a menu program; text, but full screen.

(!) [Thomas] Who cares what language you'll write it in? ncurses is ported to Solaris, as is slang. You can probably even use "dialog". All of these libaries have bindings to lots of languages. As for the "benefit" pffft. Just use what comes naturally to you.

(?) [Jay] I apologize; I thought I'd made my specific point clear on the first pass:

This program, when done, sold and shipped, needs to be able to install and run reliably on all target platforms without conflicting with, or, really, depending on, whatever language runtimes might or might not already be there. I don't object to depending on what's there, or brilliant installers, or any of that stuff, but on SCO, I'm likely to have an ancient Perl and no Python at all...

(!) [Thomas] Ok. So as long as you ensure you compile against a modern glibc, you should be fine. But what you cannot expect is something near perfect. If a potential target doesn't have XYZ installed then they should ensure so. Clearly versions of dependencies <= compiled_version will be OK, and you can test for that.

(?) [Jay] and on Linux, you never know what will be there -- or what you might break if you have library/module conflicts.

(!) [Thomas] Indeed.

(?) [Jay] So, again, going back to Fred Brooks' System Program Product concept (you know, the thing that takes 9 times as long as a simple hack? :-), what is the best approach to this?

(!) [Thomas] Well, if you do have an antiquated version of XYZ where you know that in most cases the software on the target machines might well be newer, it should be a safe [1] approach to assume that the code you have is "backwards compatible" with what you produced -- i.e. it won't be using the newer features possibily present.
[1] In theory, great. In practice, err.....
Skip various snide comments -- Thomas Adam

(?) [Jay] I propose to deploy as a commercial product a program written in a language which expects an installed interpreter, with a system-wide directory of modules.

If that infrastructure exists, I don't object to using it (assuming that everything's the right version). But if it's not there, it's not my place to install it -- the client isn't buying a-complete-perl-install, and I don't want to be responsible for that, anyway.

(!) [Thomas] OK. Well, most distributions tend to put things like that in standard locations (/usr/share/$foo || /usr/lib/$foo)/ Often, these paths will have various environment variables defined depending on the platform you're installing it to, so that is definitely something to check. Of course that depends on the language it is written in.

(?) [Jay] I don't have 6 years and 40 machines.

(!) [Thomas] I know what you mean, time is short. What I really meant by this is getting a Solaris machine, a linux machine, an HP-UX machine, whatever and seeing what the differences are to compensate. How else will you know otherwise?

(?) [Jay] Because that doesn't take into account what might have already been done adminstratively to the machine.

(!) [Thomas] Ok.. well, what I would do in that case is rely on the fact that there are known locations for modules or what have you for the said language. Most distributions follow the LSB or FSH to an extent such that all packaged versions of the languages should adhere to the agreed paths. So you can make a default assumption of the likely place they'll be, and if so, use the intended language to test for the infrastructure of where everthing is (env vars, internal vars to the language, etc).
If the administrator has been messing around with standard locations, then he/she should have enough synapses left to know what to do. As a default though, you could always install in: /usr/local/ and export LD_LIBRARY_PATH as necessary, and let the administrator know that if they know of a better place to put them, to ensure that it is done.

(?) Mail forwarding

From Ben Okopnik

Answered By: Jay R. Ashworth, Breen Mullins, Rick Moen, Jimmy O'Regan, Thomas Adam

Damn, I'm starting to foam at the mouth and twitch uncontrollably. This Mutt + SMTP-via-SSH-tunnel thing's got me going batty...

Mutt doesn't want to know anything about ports or hosts; instead, it invokes Sendmail directly. The SSH tunnel is trivial to set up, but I can't use it - despite spending the entire evening Googling for a possible answer. I really don't want to switch to another email client, either. Do any of you folks have a suggestion?

(!) [Jay] It's often forgotten that ssh can be used to run a command at the other end; have you considered using it to run the local-injection program on the remote server?

(?) Local-injection program - define, please? I've got it all working now (with the exception of a warning message from fetchmail that I'm too busy to chase down ATM), but I'm still curious about other ways to handle it.

(!) [Jay] Pipe your mail message to
ssh remotehost /usr/bin/sendmail
and let sshd remotely run it with the piped stdin as the message source. /usr/bin/sendmail is still almost always a link to something that can deal with that, even these days, no?
Local injection is a phrase I generalized from the wmail and postfix doco...

(?) I think I may have it. :)))

Finally found the one page on the Net that simply explains exactly the process I needed:

http://revjim.net/comments/3734

(!) [Jimmy]
You could try using Msmtp: http://msmtp.sourceforge.net but that probably has issues of its own.

(?) [Ben] It does. I read about a guy who was using esmtp and msmtp, and he got them working with this kind of thing, but I don't really want to drop Exim, either; it's proven itself over time.

(!) [Breen]
I've only just started using Mutt myself. What I think you're going to have to do is get an MSA on your box -- something like http://msmtp.sourceforge.net "msmtp is an SMTP client that can be used as an "SMTP plugin" for Mutt and probably other MUAs (mail user agents). It forwards mails to an SMTP server (for example at a free mail provider) which does the delivery. To use this program, create a configuration file with your mail account(s) and tell your MUA to call msmtp instead of /usr/sbin/sendmail."

(?) Actually, that's one of the things I wanted to avoid doing - much as I like null-mailers. Picture this scenario: I'm at a Net cafe that's got SSH blocked but 25 open (it's happened). Whups! No outgoing mail for me, then, unless I resort to webmail (Nextel does not provide SMTP or POP - connectivity only.)

In essence, I want to retain full SMTP capability but be able to switch between doing that and forwarding 25 (and/or 995, if necessary.) This week's been sorta amusing in that regard: Earthlink blocks 25 but leaves 995 open; Sun's firewall is the opposite. Setting up Exim as I'd mentioned, plus adding another "poll" section to my ~/.fetchmailrc, and running

su -c 'sh ben@linuxgrrzette.net -L 2525:linuxgazette.net:25 -L 995:linuxgazette.net:995'

takes care of both - and should work fine with my cell setup when I get back home. The only thing I get to pay for is the additional SSH overhead for all my mail transactions, but it's not a big deal. The only thing I don't get is why I have to do the "su" bit; the first forward doesn't require it, but adding 995 - which is obviously not a low port - makes the connection fail.

(!) [Breen] Then you'll want to use a hook in mutt to conditionally set $sendmail to 'msmtp' instead of the default pointing to your sendmail so that the right program gets invoked at the right time. You'll point mstmp to deliver to your end of the tunnel, of course.
(!) [Thomas] So to cut the ramble down, it's YANM (Yet Another Null Mailer) :)
(!) [Rick] One of my favourite places to shop is the Linuxmafia Knowledgebase. See: "Nullmailers" on http://linuxmafia.com/kb/Mail

(!) Parent and Child

The birth and death of linux processes

From tag

Answered By: Jim Dennis, Heather Stern

(!) [Heather] Jim handed me a rough sketch earlier this month, and I figured the resulting discussion would be worthy of our readers.

Once again, I'm teaching a class on shell and Linux basics to a group and after many years of doing it I'm really getting tired of drawing the same old pictures on the whiteboard or flipsheets.

What we need is a nice drawing of this, something animated. Maybe you could whip something up in Flash, and tuck yet another feather in the webmastery cap?

(!) [Heather] Well, poking around for Flash on Freshmeat.net finds a few zillion apps, (Ha, only 120. I've gotten worse from single keyword searches before.) Some have nothing to do with a certain media format at all... flash cards, flash RAM :)
Now tossing out all the things that are apps written in Flash, libraries to make it work at all, and front-ends for the Flash viewer we have... ahhh... Generators won't do, I want this to have our images in it. Dancing text alone is worse than the old MARQUEE and BLINK tags. Oh, there we go. Some alpha quality sourceforge projects to convert various formats into flash.
Package system search time. bzzzt Hmm, guess I'll have to fetch the sources and debug why they don't just './configure; make; make install' in my copious spare time.
/me buys Wilbur the GIMP some coffee, munches on a few doughnuts, and jumps merrily back into the world of animated GIFs.

(!) Ok. So here's what I envision, About 12 slides, on a roughly square picture, as follows.

1) initial process. A rectangle somewhat toward the mid-upper left, saying "process"

(!) [Heather] Well, if it's going to be a little mini movie, it needs a title, so let's start of with "The Process Model" and have the word "process" wander up there.

(!) 2) system call.

The giant word fork() appears over the process. Transition: process memory map.

(!) System calls look nice in a monospace font so we'll have that pop out at the viewer, then fade out again as it highlights the edge of the process.

(!) 3) fork results.

Show the child - a similar sized process - seperating from the parent and the two being labelled, parent and child.

(!) The copying process shows a thinner dark blue rectangle moving to its new location, then growing another process of the same size. Looks very spiffy.

(!) 4) zoom!

Make the parent smaller and give the child most of the right hand side of the picture.

(!) Zooming's a little more work, but I can do that.

(!) 5) details.

The parent just says "parent", the child shows a memory map breakdown. From bottom to top... environment, text (code), BSS, and sharing a space where each grows toward center - heap and stack.

6) system call. The giant word exec() floats up...

(!) I have a bit of fun with setting up the memory map, and this doodle you offered me has the exec() coming off the stack/heap area. I made it come out of the text (code) area and it looks better as well as not bumping into things.

(!) 7) result point 1.

The process space is cleared entirely, except the environment. This is, of course, the space which bash' "export" command moves variables into so they will be inherited, just to bring up one clear example.

8) result point 2.

The child now has its own fresh copies of the other parts, but highlight the inherited environment.

(!) I show a wash of green as the child process replaces the forked clone with new material. For you readers, in Linux the fork() is really just a special case of the clone() system call, which can clone things a number of ways. Anyways, it's an easy highlight for the environment this way, at least assuming the viewer isn't color blind. Oops. I didn't think of how to handle that. I'll have to test with desaturation... but let me finish the pictures first.

(!) 9) zoom back out. Bring the parent and child back to their previous sizes.

10) meanwhile...

The parent exhibits the wait() system call. Now this could be blocking, or not blocking, I can spend quite a while on this part, but let's just make the drawing assume a blocking wait.

11) which waits until... The child does an exit() call.

(!) The wait() call actually needs to hang around while the child lives since you're blocking, so scribble scribble that'll really read: wait() blocked. I'll have to make the child do something brief and cute (goodness knows what) then exit() leaving... uhh...
12) the child dies. Highlight the parent, which unblocks as it catches a SIGCHLD signal.
(!) My first shot at this shows exit() turning into SIGCHLD, and the child process' border fading out.

(!) That's not really right; init generates SIGCHLD after cleaning up most of the mess. And the dead child shouldn't hang around. We'll need to show the remaining process table, too. Make the kernel float in from offside so we can see that, perhaps.

(!) My second shot (good enough for your first of several classes) shows the child process decaying to a partial border, the word "child" becoming SIGCHLD, and going into the wait() which clears.
Here's where the doodle breaks down, so time to re-doodle it, using the facts. The exit leaves a return code. But as you and I know, return codes aren't stored in the "dead body" of the child process. It only exists as a zombie - an entry in the process table, which is the only thing left of it after init sweeps away the main process. The process table itself is in the kernel somewhere. So I'm going to need to plot where to sneak in a kernel page. (I imagine a bag of popcorn... :D ) Doing that suggests having it show while the fork() is spawning the child too, so I can show the process id changing, and the parent's id assigned into ppid.

(!) Yes, that will work much better. Let's get the animation between slides in, then fix up the last part and it's good to go. I found you a nice gif to flash translator, which we can try on the resulting file for grins.

I don't suppose GIMP can output flash yet?

(!) Oooh, I will need to google for that :) Probably not. The GIMP's a raster image editor, and SWF is a vector based format. OTOH if the converter you're talking about is any good, maybe it can be merged onto the GIMP.
I'm sure there are more aspects of the process creation and lifecycle that can be drawn. I'm juggling a few things, but I'll post the drawing in progress...

(!) Just stuff them in SysadMoin on the page TaosLinuxTraining, at least at the moment. When I'm happier with the parts you can split them up into several images - one per stopping point in our final form. We'll split the Process Model commentary onto a new page, and the world can have some fun with it.

(!) Wiki rocks. Chalkboards and whiteboards of the world, unite!
Sysadmoin is our house wiki, used for our system administration natter. (http://www.starshine.org/sysadmoin) It's based on moinmoin, and I'm pleased to say that our dark theme is actually part of the upstream sources. Very pleased, after all the trouble I went to in order to http://www.starshine.org/sysadmoin/MakeMoinMoinDark in older revisions. The moin people are very active in development, so for this project at least, it's worth popping into #moin on freenode. Helps to show up on European hours though.
Maybe I'll even post the xcf file and let people enjoy paging around in it. I realize xcf isn't readable by anythign but gimp, but that's where the real animation lies, in the seperated frames. :)
For you dithering vidiots out there, frames of about 50 milliseconds distances seem to be fairly smooth going, slower's a little more obvious but somewhere between that and 140 ms are good for skidding to a halt. Anything slower is painful. Your eyeballs may vary, but recall that american TV is about 24 or 28 frames a second, and more is better. 50ms is a mere 20 frames per. I just don't want my file to get too big.
For gimp folk specifically, adding (50ms)(replace) to the end of the name of your frame gives fine grain control on the animation. You could use (combine) instead when you need it. And gimp does let you resize layers down again (it's tricky) which might save space in the result. The last tip is that (50 ms) with a space doesn't do. Leave the space out. (50ms) (replace) spaced does work though.
Share and enjoy...

(?) Upgrading KDE

From Tom Brown

Answered By: Thomas Adam, Heather Stern

OK, I'm about at my wit's end here (doesn't take much, I know). I'm currently running KDE 3.1, and want to upgrade it. I've yet to figure out how, even using binary RPM's from Suse (my distro). No matter where I start, I'm getting lots and lots of dependency errors. I've looked on the KDE web site for some sort of help or tutorial, but haven't found anything useful.

(!) [Thomas] Welcome to dependency hell. Oh, do I have to guess what these errors are? Please, Tom -- supplying accurate information should be a matter of course for you. You are a member of TAG. Consider it as though you were answering the question. You'd want to see some evidence of what was going on.

(?) First, is there a shell script that will automate the process (assuming I've downloaded the huge pile of individual KDE packages), or is there a way to force Suse's YOU to upgrade from KDE 3.1 to either 3.2 or 3.3? Failing that, is there documentation that takes me step by step through the process without assuming I'm a kernel programmer or something?

(!) [Thomas] Nope. YOU should be able to do it. Indeed, you can probably even use apt-get for SuSE There is always the '--nodeps' option to RPM, but I cannot stress ENOUGH the inherent danger that brings with it. It is not a recommended thing.

(?) The best I've found is instructions on installing KDE packages from source, but nothing I've found tells me in what order the packages go. I've tried starting with the "base" RPM, but that mustn't be it, since I get so many dependency errors.

(!) [Thomas] Well, I had a quick look through some files I have stored locally. I found this in SuSE's RPM source packages for KDE:
#
# KDE 3.2 packages for SuSE distributions
#

Disclaimer:

  These packages have NOT been tested at all and SuSE do NOT recommend to
  upgrade to these. However we build these packages for convenience, but
  it is your risc to use them ;)
    You should not update, if you want to have a stable system and expect
  to get security updates for your installation.

To install these packages via command line you need to
  * rpm -e kdebase3-SuSE kdenetwork3-mail
    (kdebase3-SuSE has not yet been ported to KDE 3.2 and plain rpm
     can not handle the move from kmail to kdepim3 package correct).
  * rpm -Uvh taglib*rpm
  * install flac and gnokii from your CD/DVD
  * rpm -Fvh *.rpm

An alternative way would be to use the yast_source from ftp.suse.com KDE
update packages.

Known issues:
  * Qt 3.3.0 final has not been released yet, we expect it next week
    and it will be avaible on ftp.suse.com.
    The qt packages in these directories contain a late snapshot
    (Qt 3.2 would need many patches, so we decided to go the 3.3 way)
  * Several issues with updating old configurations, these will be
    addressed with SuSE 9.1. However SuSE 9.1 will not fix these, when
    you have already updated to KDE 3.2. So you might backup your
    ~/.kde directory first.
  * kdebindings packages are missing atm, they need further fixes and
    will appear on ftp.suse.com later
(!) [Heather] Woes with upgrading a major desktop beyond the supported scope of your vendor are not limited to one distribution. The lesson here is simple: when taking a package (or in this case a big, big set of packages) beyond the package manager's scope, backup everything you'd need to set things back on the package track, and don't just prepare for glitches but expect to walk through a gantlet of them.
The effort to stray off the beaten path is paid off in getting the nice new features, fixes, and i18n updates far ahead of your neighbors. With care during setup you can also make it easier to update yourself from the project source trees directly with less fuss. The scuffs and scratches will heal, and you'll learn to hang-glide with open source wings...
Midnight commander (mc) with its ability to pop open rpm files and let you look at the post-install scripts, may be invaluable to helping you play safely on the bleeding edge too; look at what merges are made into menu systems and system-config directories. What you learn from that may help you with merging other off-brand packages from their true sources.


This page edited and maintained by the Editors of Linux Gazette
Copyright © its authors, 2004
Published in issue 106 of Linux Gazette September 2004
HTML script maintained by Heather Stern of Starshine Technical Services, http://www.starshine.org/

Published in Issue 106 of Linux Gazette, September 2004

Windows Defectors: Upgrading a KDE Installation to Version 3.3

By Tom Brown

[This article is a follow-up to this TAG thread]

There's an old joke about how a burgler makes a cake. The recipe begins "To bake a cake, first steal two eggs". Of course, if you really don't want to do any baking in the first place, and all you really wanted was a simple pre-packaged cupcake, all this recipe stuff (not to mention hunting down the different ingredients needed) may be more trouble than it's worth. Occasionally, that's been my experience migrating from Windows to Linux. One such occasion has been my recent attempt to upgrade KDE from version 3.1 to 3.3. Only in this case, the instructions were: "To install KDE, first find one recipe".

Now, Windows users are a picky bunch. We expect to double-click on an icon, usually "setup.exe", and everything works automagically. The logic of what to uninstall, what to install, and what questions need to be asked of the person doing the installation, are all built into the process. With KDE, there's no such thing. Apparently, nobody feels it's needed, despite the multitude of different packages which must be installed (and uninstalled) in a particular order. But some sort of automation is desperately needed here. There is no excuse, in my opinion, for the installation of software to be difficult. Now, a lot of people who read LG will wonder what the fuss is about. This is Linux, after all, and you need to learn how to do all this. At the risk of being called a troll, I say that's just the wrong attitude to have. Installation (of device drivers as well as software) is an area in which Linux still has a very long way to go. I don't want to bake the cupcake, blast it, I just want to eat it!

First, my thanks go to Faber Fedor for pointing me in the right direction in my KDE upgrade. There are days when my googling skills fail me miserably, and I appreciate Faber's help. I still didn't have an easy time doing the upgrade, and haven't gotten everything working (a few tiny annoyances still remain), but on the whole I managed it.

Technical details: I'm running SuSe 9.0, with the original KDE 3.1 installation that came with the distro. I upgraded using the binary RPM files from the SuSe website. That website still claims the files are for KDE version 3.2, when in fact most of them are now for version 3.3 (dated August 19, 2004). Unfortunately, the KDevelop RPM there has not been updated, and won't install into KDE 3.3. That's one I'll tackle later, since for the moment, I don't need it. Using RPM files built specifically for your distro is the easiest way to do the upgrade, but if you can't find any, then you'll either have to build from source or wait for the right RPMs to become available. Notice that I'm installing the KDE development packages. Even if you don't intend to do any software development in KDE, some of this might be needed if you install software from the sourcecode.

Before beginning, you should backup your system. If you're using drag-and-drop to do this, remember that the ".kde" directory in each user's home directory is normally hidden, so make sure you're showing hidden files in Konqueror before doing so.

Now, because I'm a Windows Defector using SuSe (and therefore, Yast2), I did the entire installation from inside KDE, not the command line. I had to do one extra step at the end to compensate for this rash and risky approach, but the benefit was double-clicking on the RPM file to perform the installation, instead of all that command-line nonsense (sorry, it's a Windows Thing).

Now it's only fair to tell you that a lot of folks recommend a different procedure for installing KDE. First, boot to the Linux command line instead of the GUI. In SuSe's Yast2, click the "System" icon, then the "Runlevel Editor" icon. Select the "Expert Mode" radio button in the subsequent dialog box, then change the drop-down list from "5: Full multiuser with network and xdm" (or what is called "runlevel 5") to "3: Full multiuser with network" (or what is called "runlevel 3"). If you're not using SuSe, then you'll have to edit the bootup runlevel setting in the file "/etc/inittab". Either way, the next time you reboot the machine, you'll get a command-line logon prompt, instead of the GUI one. From here, logon as root, uninstall all of KDE (and QT as well), install the new KDE (in the order I'm about to present), and reset your logon to runlevel 5. If you're running SuSe, you can do this from Yast2, either directly from the command line, or through the GUI by using the command "startx" to run the X server, and KDE. Now that Yast2 has been released under the GPL, hopefully it will be more widely available.

Note: Read this entire document carefully before trying any of it on your computer! Make sure you have all the materials necessary, and don't close any windows or reboot until you're done.

The order in which you install KDE is important, no matter how you do it. The first thing that gets installed isn't KDE, but QT, which is the graphics layer beneath KDE. Install the package "qt3.rpm". In SuSe, this is done by double-clicking on the file's icon. Trying to install this package results in a lot of dependency warnings/errors, which is to be expected for such a low-level package. In Yast2, a dialog box will list these dependencies, and offer three alternatives: don't install, force the install, ignoring the dependencies, and remove the offending packages. The dependency problem with qt3 is primarily with qt3_devel and qt3-non-mt. You're going to replace those two anyway in a moment, so go ahead and remove them before installing qt3 (forcing the install may also be ok in this circumstance, but I thought removal was safer. It might not make any difference). Before you do, though, take a good look at the dependencies on your machine. If you see anything other than QT or KDE stuff in that list, stop right there until you know what it is you're removing. On my machine, Yast2 informed me that 20 packages were in conflict, every one of which being some part of QT or KDE that was going to be replaced anyway.

Once "qt3.rpm" is installed, go ahead and install "qt3-devel.rpm", followed by "qt3-non-mt.rpm". QT has now been updated.

You now have to update arts, the audio support used by KDE. Install "arts.rpm", followed by "arts-gmcop.rpm", and then "arts-devel.rpm. I'm not sure if these last two have to be done in that order, but that's what worked for me.

Next on the agenda are the KDE libs. First install "kdelibs3.rpm", and then "kdelibs3-devel.rpm"

We're getting to the good stuff now. Install the file "kdebase3.rpm". Once more, you will get dependency errors. Two of these, the kate text editor and the konqueror browser you can safely remove, as you're going to replace them with 3.3 versions anyway before you're done. The nasty bit in my case was the reference to "kdebase-suse", which isn't part of the KDE release, but an add-on in the original SuSe distro. In this case, I simply forced the installation, ignoring the error, and Nothing Bad Happened.

Install the file "kdebase3-devel.rpm" to finish the base installation.

Now, re-install kate with "kdeaddons3-kate.rpm" and konqueror with "kdeaddons3-konqueror.rpm", so you don't forget.

Finally, install the remaining KDE packages. It doesn't matter in which order.

If you performed the upgrade from within KDE, as I did, you need to do one more thing, or you'll see lots of error messages that say "Unable to create io-slave". Delete the directory "/tmp/kde-user", where "user" is your login id (for example, I had to delete "/tmp/kde-tbrown"). Log out then back in to restart the X server and KDE. The upgrade is done.

My KDE upgrade works just fine, except for the KDevelop and screensaver issues. I did install an updated "kdeartwork3-xscreensaver.rpm" file, but there must be something that I missed. One additional oddity is that windows generated by Superkaramba are now always on top, with no setting I could find that would make them behave like any other window. Obviously, something has changed between version 3.1 and 3.3 in the way KDE treats those windows, since they worked fine before the upgrade.

I may be in the minority here, but I'd welcome a single-file installer for KDE, however large it would have to be to include all the necessary pieces. Most Linux programs don't need an installer, but KDE isn't one of them. Still, with a little information and experimentation, I managed to do without.


[BIO] Tom has been a software developer since the early days of the Commodore 64, with such commercial classics as Prototerm-64 and Colorez-128, and has seen lots of operating systems come and go. Every one he's liked is either discontinued (OS/2) or out of business (Commodore Amiga). He currently likes Red Hat Linux, which won't be supported after April '04. As a result, we've been trying to get him to fall in love with Windows, but so far no luck.

Copyright © 2004, Tom Brown. Released under the Open Publication license unless otherwise noted in the body of the article. Linux Gazette is not produced, sponsored, or endorsed by its prior host, SSC, Inc.

Published in Issue 106 of Linux Gazette, September 2004

Lemon Parser Generator Tutorial

By Mike Chirico

Lemon is a compact, thread safe, well-tested parser generator written by Dr. Richard Hipp. Using a parser generator, along with a scanner like flex, can be advantageous because there is less code to write. You just write the grammar for the parser.

Example 1: Getting started

Compare this to writing all of the raw code from scratch. For instance, compare the basic C++ desktop calculator to the file below. Below is a syntax file which contains the grammar that the parser uses. "example1.y" is an example syntax file for a very basic calculator.

example1.y


    1  %token_type {int}
    2
    3  %left PLUS MINUS.
    4  %left DIVIDE TIMES.
    5
    6  %include {
    7  #include <iostream>
    8  #include "example1.h"
    9  }
    10
    11  %syntax_error {
    12    std::cout << "Syntax error!" << std::endl;
    13  }
    14
    15  program ::= expr(A).   { std::cout << "Result=" << A << std::endl; }
    16
    17  expr(A) ::= expr(B) MINUS  expr(C).   { A = B - C; }
    18  expr(A) ::= expr(B) PLUS  expr(C).   { A = B + C; }
    19  expr(A) ::= expr(B) TIMES  expr(C).   { A = B * C; }
    20  expr(A) ::= expr(B) DIVIDE expr(C).  {
    
    
    21           if(C != 0){
    22             A = B / C;
    23            }else{
    24             std::cout << "divide by zero" << std::endl;
    25             }
    26  }  /* end of DIVIDE */
    
    
    27  expr(A) ::= INTEGER(B). { A = B; }

As you can see, this file is only 27 lines of code, not counting spaces. It is much easer to modify the grammar than it is to rewrite larger sections of raw code.

The parser generator (lemon.c and lempar.c) takes the input from the syntax file "example1.y" and creates the parser file "example1.c", along with two other files "example1.h", which contains definitions for the tokens, and "example1.out", which gives a detailed listing of the shift reduce states for the grammar listed in "example1.y".

Let's run through the steps, starting first with compiling the source code of lemon (available here). The first step is to compile lemon.c:


    $ gcc -o lemon lemon.c

Now we have our parser generator, lemon, so run the syntax file "example1.y" through it.


    $ ./lemon example1.y

This will create example1.c, example1.h, and example1.out. What about lempar.c? Compare "example1.c" with "lempar.c", and you will see that it contains a lot of the same code. "lempar.c" is a template file. You can modify the code if you want, and all modifications will be passed to "example1.c" (including any comments).

But "example1.c" is not complete. We'll append the contents of the file "main_part", which contains a main function and tokens. "main_part" is called a driver.

main_part


    1  int main()
    2  {
    3    void* pParser = ParseAlloc (malloc);
    
    4    /* First input:
    5        15 / 5
    6                                  */
    7    Parse (pParser, INTEGER, 15);
    8    Parse (pParser, DIVIDE, 0);
    9    Parse (pParser, INTEGER, 5);
    10    Parse (pParser, 0, 0);
    
    11    /*  Second input:
    12          50 + 125
    13                                 */
    
    14    Parse (pParser, INTEGER, 50);
    15    Parse (pParser, PLUS, 0);
    16    Parse (pParser, INTEGER, 125);
    17    Parse (pParser, 0, 0);
    
    18    /*  Third input:
    19          50 * 125 + 125
    20                                 */
    
    
    
    21    Parse (pParser, INTEGER, 50);
    22    Parse (pParser, TIMES, 0);
    23    Parse (pParser, INTEGER, 125);
    24    Parse (pParser, PLUS, 0);
    25    Parse (pParser, INTEGER, 125);
    26    Parse (pParser, 0, 0);
    
    27    ParseFree(pParser, free );
    
    28  }

So, what is main_part doing? Well, line 3 initializes the parser. You'll note that pParser is passed to each call of the "Parse" function starting at line 7. The expression 15/5, or 15 DIVIDE 5, is performed in lines 7 through 10, sending first the INTEGER 15, then the identifier DIVIDE, which doesn't need a value, so 0 is chosen as the third parameter on line 8. Finally, line 10, with 0 as the second parameter in "Parse(pParser, 0, 0);" signals the end of the input for this expression. (Please note that in "example4.y", the grammar will handle this with a NEWLINE, and "Parse(pParser,0,...);" will only be called at the very end of the syntax file.)

"main_part" is appended to "example1.c". You may want to reference the Makefile with the downloadable example, which has this step:


    $ cat main_part >> example1.c

Next, just compile example1.c, and it's good to go.


    $ g++ -o ex1 example1.c

Now execute "ex1", and we'll see that the result of 15/5 is, of course, 3. And 50+125 is equal to 175, and 50*125+125 is indeed equal to (50*125)+125= 6375. This last result verifies that TIMES has higher precedence than PLUS.


    $ ./ex1
    Result=3
    Result=175
    Result=6375

Now for a closer look at the syntax file (example1.y). Why does TIMES have higher precedence than PLUS? Line 3 and line 4 determine the precedence of the operators PLUS, MINUS, DIVIDE, and TIMES.


    3  %left PLUS MINUS.
    4  %left DIVIDE TIMES.

Lines at the top have a lower operator precedence. This is very important to note. PLUS and MINUS have less operator precedence than DIVIDE and TIMES because PLUS and MINUS are on line 3, whereas DIVIDE and TIMES are on line 4. If, for example, exponentiation (EXP) were added, since EXP has even higher operator precedence than TIMES and DIVIDE, it would be added below line 4.

What if you wanted real numbers in the input, 15.5,5.2 instead of integers? How would you do that? It's easy. These tokens are currently integers because of the following line in "example1.y":


    1  %token_type {int}

So to accommodate a double, line 1 would be changed to:


    %token_type {double}

Moving further down the lines of "example1.y", there is an "include" directive on line 6. The include statement in "example1.y" passes along any C statements, which are inserted at the beginning of the parse file "example1.c". Again, the contents are inserted into the beginning of "example1.c", which is necessary for declarations and headers.


    ...
    6  %include {
    7  #include <iostream>
    8  #include "example1.h"
    9  }
    ...

Note that "example1.h" is generated from "$ ./lemon example1.y". It defines the tokens, or, put another way, assigns integer values to the token names starting at 1. Why start at 1, and not 0? Because 0 is reserved for the Parse function. Remember, "Parse (pParser, 0, 0);", with the second parameter set to zero, signals an end to the input.

example1.h (note, this is a generated file; do not add code to it):


    #define PLUS                            1
    #define MINUS                           2
    #define DIVIDE                          3
    #define TIMES                           4
    #define INTEGER                         5

Example 2: Creating a custom token type, or structure

"example2.y" differs from "example1.y" by defining the token type as a structure. Specifically, this token type is defined in the file "ex2def.h". Defining our own structure can give us flexibility in the semantic action, or the piece of code on the right of the production rule. Here is an example:


    expr(A) ::= expr(B) TIMES  expr(C).   { A.value = B.value * C.value;
    A.n = B.n+1  + C.n+1;
    }

The token_type in "example2.y" is defined as Token in line 6.


    6  %token_type {Token}

This structure Token is defined in "ex2def.h" as follows:

ex2def.h


    struct Token {
    const char *z;
    int value;
    unsigned n;
    };
    

Special note: "const char *z" is not used in these examples, but I've kept it in this structure, since it's the next logical step in a calculator, assigning an expression to a variable. For instance, variable1=2+5, where variable1 would be some value in a symbol table. See this reference.

Again, note the change in the include directive, the addition of "ex2def.h", which defines struct Token.

example2.y


    1  #include {
    2  #include <iostream>
    3  #include "ex2def.h"
    4  #include "example2.h"
    5  }
    
    6  %token_type {Token}
    7  %default_type {Token}
    
    8  %type expr {Token}
    9  %type NUM {Token}
    10
    11  %left PLUS MINUS.
    12  %left DIVIDE TIMES.
    13
    14
    15  %syntax_error {
    16    std::cout << "Syntax error!" << std::endl;
    17  }
    18
    19  program ::= expr(A).   {
    20                          std::cout << "Result.value=" << A.value << std::endl;
    21                          std::cout << "Result.n=" << A.n << std::endl;
    
    22                           }
    
    23  expr(A) ::= expr(B) MINUS  expr(C).   { A.value = B.value - C.value;
    24                                         A.n = B.n+1  + C.n+1;
    25                                        }
    
    
    26  expr(A) ::= expr(B) PLUS  expr(C).   { A.value = B.value + C.value;
    27                                         A.n = B.n+1  + C.n+1;
    28                                        }
    
    29  expr(A) ::= expr(B) TIMES  expr(C).   { A.value = B.value * C.value;
    30                                          A.n = B.n+1  + C.n+1;
    
    31                                           }
    32  expr(A) ::= expr(B) DIVIDE expr(C).  {
    
    
    33           if(C.value != 0){
    34             A.value = B.value / C.value;
    35             A.n = B.n+1 + C.n+1;
    36            }else{
    37             std::cout << "divide by zero" << std::endl;
    38             }
    39  }  /* end of DIVIDE */
    40  expr(A) ::= NUM(B). { A.value = B.value; A.n = B.n+1; }

As you can see below, taking a close look at lines 23 through 25, the Token structure A now takes on members "A.value" and "A.n", with ".value" taking on the value of the expression and ".n" the number of times an assignment is made:


    23  expr(A) ::= expr(B) MINUS  expr(C).   { A.value = B.value - C.value;
    24                                         A.n = B.n+1  + C.n+1;
    25                                        }

This is a quick way to see the "shift" and "reduce" dynamically. A "shift" is the number of times a token is pushed on the stack. A "reduce" is the number of times an expression rule has been matched. Once it's matched, it can be reduced. As you will recall, when lemon is run, three files are normally created: *.c, *.h, and *.out. This ".out" file contains each step of the grammar, along with the shift and reduce states. If you want a simple summary, run lemon with the "-s" option:


    $ ./lemon -s example2.y
    Parser statistics: 6 terminals, 3 nonterminals, 6 rules
    11 states, 0 parser table entries, 0 conflicts

Again, as in the previous example, "main_part2", the driver, is appended to "example2.c":


    $ cat main_part2 >> example2.c

Now "example2.c" can be compiled and executed:


    $ g++ -o ex2  example2.c
    
    $ ./ex2
    Result.value=17
    Result.n=4
    Result.value=-9
    Result.n=4
    Result.value=78
    Result.n=10

Example 3: Working with the token destructor

One advantage of lemon over bison is the ability to free memory used by a non-terminal. You can call the function of your choice. "expr" is an example of a non-terminal. When the program is done with the non-terminal, the function defined by token_destructor is called.

example3.y


    1  %include {
    2  #include <iostream>
    3  #include "ex3def.h"
    4  #include "example3.h"
    
    
    5    void token_destructor(Token t)
    6      {
    7        std::cout << "In token_destructor t.value= " << t.value << std::endl;
    8        std::cout << "In token_destructor t.n= " << t.n << std::endl;
    9      }
    
    
    10  }
    
    
    11  %token_type {Token}
    12  %default_type {Token}
    13  %token_destructor { token_destructor($$); }
    ...

In line 13, token_destructor is the function "token_destructor($$);". The function "token_destructor" is defined in lines 5 through 9. For this simple example, no memory is allocated, so there is no need to call free. Instead, to see what is happening, output will be written to std::cout.

After the program is compiled, it can be executed as follows. Note that I have added line numbers to the output of "ex3" for easy reference.


    $ ./ex3
    1  t0.value=4  PLUS t1.value=13
    2  In token_destructor t.value= 4
    3  In token_destructor t.n= 0
    4  Result.value=17
    5  Result.n=4
    6  parsing complete!
    ...

After the expression has been reduced, the destructor is called, but it is only called for the token.value=4. Why? For an answer we will have to take a look at "main_part3".

main_part3


    1  int main()
    2  {
    3    void* pParser = ParseAlloc (malloc);
    
    4    struct Token t0,t1;
    5    struct Token mToken;
    
    6    t0.value=4;
    7    t0.n=0;
    
    8    t1.value=13;
    9    t1.n=0;
    
    10    std::cout << " t0.value=4  PLUS t1.value=13 " << std::endl;
    
    11    Parse (pParser, NUM, t0);
    12    Parse (pParser, PLUS, t0);
    13    Parse (pParser, NUM, t1);
    14    Parse (pParser, 0, t0);
    
    15    std::cout << " t0.value=4  DIVIDE t1.value=13 " << std::endl;
    
    16    Parse (pParser, NUM, t0);
    17    Parse (pParser, DIVIDE, t0);
    18    Parse (pParser, NUM, t1);
    19    Parse (pParser, 0, t1);
    ...

Line 14 terminates the grammar with t0 as the third parameter. That third parameter is passed as "$$" to the defined destructor function, "token_destructor(...". When calling "Parse" a second time immediately, it is undefined, so you should only call the destructor function once after you're done passing tokens to complete an expression. In other words, you would never call "Parse (pParser, 0, t0);", immediately followed by another "Parse (pParser, 0, t0);".

In line 19, token_destructor is called for t1.value= 13. If you look at "main_part3", line 19, you'll see that Parse is called with t1 as the third parameter and 0 and the second parameter.

Continuation of the output from the program:


    7
    8
    9   t1.value=13  PLUS  t0.value=4
    10   In token_destructor t.value= 13
    11   In token_destructor t.n= 0
    12   Result.value=17
    13   Result.n=4
    14   parsing complete!

So t0 is called at the third parameter position in line 14 and t1 is called in line 19. This shouldn't be a problem. One variable could hold the value of the tokens. For instance, main_part3 could have had Token t0 used for both the values 4 and 14 as follows:


    ...
    struct Token t0;
    
    t0.value=4;
    t0.n=0;
    
    Parse (pParser, NUM, t0);
    Parse (pParser, PLUS, t0);
    
    t0.value=13;
    t0.n=0;
    
    Parse (pParser, NUM, t0);
    Parse (pParser, 0, t0);
    ...
    

Example 4: Ending the grammar with a NEWLINE

Notice that in the last three examples, Parse(pParse,0.. had to be called to signal the end of the input for an expression. This is awkward. Instead, the grammar should dictate when the expression can no longer be reduced.

"example4.y" contains the following lines:

example4.y


    1  %include {
    2  #include <iostream>
    3  #include "ex4def.h"
    4  #include "example4.h"
    
    ...
    
    23
    24  %syntax_error {
    25    std::cout << "Syntax error!" << std::endl;
    26  }
    27
    28  /*  This is to terminate with a new line */
    29  main ::= in.
    30  in ::= .
    31  in ::= in state NEWLINE.
    
    
    32  state ::= expr(A).   {
    33                          std::cout << "Result.value=" << A.value << std::end
    34                          std::cout << "Result.n=" << A.n << std::endl;
    
    
    35                           }
    
    
    
    36  expr(A) ::= expr(B) MINUS  expr(C).   { A.value = B.value - C.value;
    37                                         A.n = B.n+1  + C.n+1;
    38                                        }
    
    ...

Note lines 29 through 35. "main" and "in" must be defined (lines 29-31). If you're a Bison user, you could get away without having to define the non-terminal main, but lemon currently requires it.

With this change made to the grammar in "example4.y", "main_part4" can now terminate each expression by passing the token NEWLINE.

Here is a section of main_part4:

main_part4


    1  int main()
    2  {
    3    void* pParser = ParseAlloc (malloc);
    
    4    struct Token t0,t1;
    5    struct Token mToken;
    
    6    t0.value=4;
    7    t0.n=0;
    
    8    t1.value=13;
    9    t1.n=0;
    
    10    std::cout << std::endl <<" t0.value=4  PLUS t1.value=13 " << std::endl << std::endl;
    
    11    Parse (pParser, NUM, t0);
    12    Parse (pParser, PLUS, t0);
    13    Parse (pParser, NUM, t1);
    14    Parse (pParser, NEWLINE, t1);
    
    
    15    std::cout << std::endl <<" t0.value=4  TIMES t1.value=13 " << std::endl << std::endl;

Note that line 14 is passing the token NEWLINE and checking "example4.h". NEWLINE in this case is defined as an integer, 6.

So, looking at the output of "ex4", with line numbers added for clarification, we get the following:


    $ ./ex4
    
    1  t0.value=4  PLUS t1.value=13
    2
    3  In token_destructor t.value= 4
    4  In token_destructor t.n= 0
    5  Result.value=17
    6  Result.n=4
    7
    8   t0.value=4  TIMES t1.value=13
    9
    10  In token_destructor t.value= 4
    11  In token_destructor t.n= 0
    12  Result.value=52
    13  Result.n=4
    14  parsing complete!

We get the result on line 5, and there was no need to call Parse (pParser, 0, t0);. Instead, Parse( pParse, NEWLINE, t0) worked.

Example 5: Using flex for the tokenizer

The next example takes input directly from the terminal, and flex will create a scanner for finding the appropriate tokens.

First, a quick look at the flex program "lexer.l", again with line numbers added for clarification:

lexer.l


    1  %{
    2  #include "lexglobal.h"
    3  #include "example5.h"
    4  #include <string.h>
    5  #include <math.h>
    
    6  int line = 1, col = 1;
    
    7  %}
    8  %%
    
    9  [0-9]+|[0-9]*\.[0-9]+    {                      col += (int) strlen(yytext);
    10                                                  yylval.dval = atof(yytext);
    11                                                  return NUM; }
    12  [ \t]   { col += (int) strlen(yytext); }               /* ignore but count white space */
    13  [A-Za-z][A-Za-z0-9]*                           { /* ignore but needed for variables */
    
    14                                                  return 0;
    15                                                 }
    
    16  "+"           {  return PLUS; }
    17  "-"           {  return MINUS; }
    18  "*"           {  return TIMES; }
    19  "/"           {  return DIVIDE; }
    
    20  \n      { col = 0; ++line; return NEWLINE; }
    
    21  .       { col += (int) strlen(yytext); return yytext[0]; }
    22  %%
    23  /**
    24   * reset the line and column count
    25   *
    26   *
    27   */
    28  void reset_lexer(void)
    29  {
    
    30    line = 1;
    31    col  = 1;
    
    32  }
    
    33  /**
    34   * yyerror() is invoked when the lexer or the parser encounter
    35   * an error. The error message is passed via *s
    36   *
    37   *
    38   */
    39  void yyerror(char *s)
    40  {
    41    printf("error: %s at line: %d col: %d\n",s,line,col);
    
    42  }
    
    43  int yywrap(void)
    44  {
    45    return 1;
    46  }
    

The format for flex is basically a rule on the left side followed by C code to execute on the right side. Take line 9, "[0-9]+|[0-9]*\.[0-9]+", which will match any of 3, .3, 0.3, and 23.4 and will return NUM. What's the value of NUM? It's taken from line 3, which includes the file "example5.h", generated from the lemon parser. On Line 10, yylval.dval is assigned the value of "yytext" after it's converted to a float. The structure of yylval is defined in "lexglobal.h" on line 2.

"lexglobal.h" with line numbers added:

lexglobal.h


    1  #ifndef YYSTYPE
    2  typedef union {
    3    double    dval;
    4    struct symtab *symp;
    5  } yystype;
    6  # define YYSTYPE yystype
    7  # define YYSTYPE_IS_TRIVIAL 1
    8  #endif
    
    9  /* extern YYSTYPE yylval; */
    10  YYSTYPE yylval;
    

yystype is the union of dval and symtab. Again, symtab is not used in these examples, but should you move to a calculator with variables that can be assigned, you'd use this. See Reference 3 for a full calculator example with flex and bison.

Again looking at lines 9 through 11 in lexer.l;


    ...
    9  [0-9]+|[0-9]*\.[0-9]+    {                      col += (int) strlen(yytext);
    10                                                  yylval.dval = atof(yytext);
    11                                                  return NUM; }
    ...

Both the type of token, NUM, and its value must be passed along. We need to know it's a number, but we also need to know the value of the number.

Unlike what we need with PLUS, MINUS, TIME, and DIVIDE, we only need to know the particular identifier has been found. Therefore, in lexer.l, lines 16 through 19 only return the token value.


    16  "+"           {  return PLUS; }
    17  "-"           {  return MINUS; }
    18  "*"           {  return TIMES; }
    19  "/"           {  return DIVIDE; }
    
    20  \n      { col = 0; ++line; return NEWLINE; }
    
    21  .       { col += (int) strlen(yytext); return yytext[0]; }
    22  %%

Line 20 will match on a NEWLINE. Although not used, line numbers keep track of the variable "line" and col is used to track the number of columns. This is a good idea; it is helpful when debugging.

The driver, main_part5, contains a lot more code. The low level read statement is used on stdin. This could easily be changed to accept input coming in on a socket descriptor, so if you had a Web scraping program that scans input from a TCP socket, the socket descriptor would replace "fileno(stdin)" on line 33.

main_part5


    
    1  #include <stdio.h>
    2  #include <unistd.h>
    3  #include <sys/types.h>
    4  #include <sys/stat.h>
    5  #include <fcntl.h>
    6  #include <stdlib.h>
    
    7  #define BUFS 1024
    
    8  /**
    9   * We have to declare these here - they're not  in any header files
    10   * we can include.  yyparse() is declared with an empty argument list
    11   * so that it is compatible with the generated C code from bison.
    12   *
    13   */
    
    14  extern FILE *yyin;
    15  typedef struct yy_buffer_state *YY_BUFFER_STATE;
    
    16  extern "C" {
    17    int             yylex( void );
    18    YY_BUFFER_STATE yy_scan_string( const char * );
    19    void            yy_delete_buffer( YY_BUFFER_STATE );
    20  }
    
    21  int main(int argc,char** argv)
    22  {
    23    int n;
    24    int yv;
    25    char buf[BUFS+1];
    26    void* pParser = ParseAlloc (malloc);
    
    27    struct Token t0,t1;
    28    struct Token mToken;
    
    29    t0.n=0;
    30    t0.value=0;
    
    31    std::cout << "Enter an expression like 3+5 <return>" << std::endl;
    32    std::cout << "  Terminate with ^D" << std::endl;
    
    33    while ( ( n=read(fileno(stdin), buf, BUFS )) >  0)
    34      {
    35        buf[n]='\0';
    36        yy_scan_string(buf);
    37        // on EOF yylex will return 0
    38        while( (yv=yylex()) != 0)
    39          {
    40            std::cout << " yylex() " << yv << " yylval.dval " << yylval.dval << std::endl;
    41            t0.value=yylval.dval;
    42            Parse (pParser, yv, t0);
    43          }
    
    44      }
    
    45    Parse (pParser, 0, t0);
    46    ParseFree(pParser, free );
    
    47  }

Line 16, 'extern "C"', is necessary because "lexer.l" was run through flex to create C code, as opposed to C++ code:


    $ flex lexer.l

See the flex manual, Reference 7. Yes, "flex++" will output C++ code. However, for complex scanning, C code may be faster. "main_part5", which is compiled as a C++ program, makes the transition smoothly.

The parser should always terminate input with 0 in the second parameter to "Parse(pParser,0,..". When there is no more input coming into flex, it will return a zero, so the while loop below on line 38 terminates with a zero. Then the read statement, line 33, looks for more input. This is something you would want to do when reading from a socket, since it may have been delayed.

But if the initial read (line 33 for the first time) isn't successful, flex has no chance of returning a zero. Therefore, line 45 has a zero as the second parameter.


    ...
    33    while ( ( n=read(fileno(stdin), buf, BUFS )) >  0)
    
    ...
    38        while( (yv=yylex()) != 0)
    39          {
    40            std::cout << " yylex() " << yv << " yylval.dval " << yylval.dval << std::endl;
    41            t0.value=yylval.dval;
    42            Parse (pParser, yv, t0);
    43          }
    ...
    45    Parse (pParser, 0, t0);
    46    ParseFree(pParser, free );

Summary

lemon is fast, completely in the public domain, well tested in SQLite, and thread safe. Parser generators can help developers write reusable code for complex tasks in a fraction of the time they would need for writing the complete program from scratch. The syntax file, the file that holds the grammar, can be modified to suit multiple needs.

Although I have had no problems with lemon.c, there are a few compiler warnings regarding signed and unsigned integers when compiling it with the -Wall -W flags:


    [chirico@third-fl-71 lemon_examples]$ gcc -Wall -W -O2 -s -pipe lemon.c
    lemon.c: In function `resolve_conflict':
    lemon.c:973: warning: unused parameter `errsym'
    lemon.c: In function `main':
    lemon.c:1342: warning: unused parameter `argc'
    lemon.c: At top level:
    lemon.c:2308: warning: return type defaults to `int'
    lemon.c: In function `preprocess_input':
    lemon.c:2334: warning: comparison between signed and unsigned
    lemon.c:2352: warning: control reaches end of non-void function
    lemon.c:2311: warning: `start' might be used uninitialized in this function
    lemon.c:2313: warning: `start_lineno' might be used uninitialized in this function
    lemon.c: In function `Parse':
    lemon.c:2393: warning: comparison between signed and unsigned
    lemon.c: In function `tplt_open':
    lemon.c:2904: warning: implicit declaration of function `access'
    lemon.c: In function `append_str':
    lemon.c:3019: warning: comparison between signed and unsigned
    lemon.c:3011: warning: unused variable `i'
    lemon.c: In function `translate_code':
    lemon.c:3109: warning: control reaches end of non-void function

This can be an inconvenience when adding the parse.c file to existing code. A fix is on the way. Since I expect the changes to be cleaned up soon, this version of lemon.c is the same version that you'd get from the author's site, which will make it easier to apply the patch.

There are times when a parser like lemon or bison may be a little too much. These are powerful tools. An interesting alternative, if you're a C++ programmer and you only need to do inline parsing, is the spirit library. See Reference 9.

Examples for this article

The complete source for these examples, including the parser itself, can be downloaded here.

References

  1. An example desktop calculator from scratch
  2. An example of a flex and bison parser
  3. The home of the lemon parser generator
  4. The home of SQLite
  5. A glossary of parser terms
  6. A good introduction to parsers
  7. The GNU flex manual
  8. The GNU bison manual
  9. The spirit parser
  10. Getting a C++ Bison parser to use a C Flex lexer
  11. The Lex-YACC-HOWTO


[BIO] Mike Chirico, a father of triplets (all girls) lives outside of Philadelphia, PA, USA. He has worked with Linux since 1996, has a Masters in Computer Science and Mathematics from Villanova University, and has worked in computer-related jobs from Wall Street to the University of Pennsylvania. His hero is Paul Erdos, a brilliant number theorist who was known for his open collaboration with others.
Mike's notes page is souptonuts.

Copyright © 2004, Mike Chirico. Released under the Open Publication license unless otherwise noted in the body of the article. Linux Gazette is not produced, sponsored, or endorsed by its prior host, SSC, Inc.

Published in Issue 106 of Linux Gazette, September 2004

Building A Lo-Fat Linux Desktop

By John Murray

A Newbies Guide to a Less Lardy Linux

Note: When I wrote the first version of this article in 2001, I was using a 233mhz machine with 32megs of RAM. And while my current box has specs of about ten times those numbers, I mainly still use the same speedy applications covered in the story below...

Introduction

I first started playing with Linux a few years ago, after reading several Introduction-To-Linux articles in computer magazines and on the web. In almost all of these articles, low hardware requirements are listed as one of Linux's advantages. Usually the authors then go on to show how easy it is to use Linux on the desktop with the GNOME or KDE desktop environments.

So I set up my machine to dual-boot Win95 and Linux, and experimented with several different distros. Initially I was disappointed with the performance of Linux, and it took me a while to discover the performance gains made possible by running leaner software. The fact that most of the newbie-oriented documentation emphasised GNOME/KDE while ignoring everything else only made things harder. That's what this page is all about - a newbie's guide to good, lightweight software that runs well on boxes that are less than state-of-the-art.

GNOME and KDE are good-looking, feature-packed environments that are as easy to use as the desktop on that other OS, but they aren't the best choice for an older machine - they can actually be quite sluggish unless you have some fairly recent hardware to run them. That doesn't mean you're stuck with a text-only console though, as it's easy to set up a nice looking Linux desktop that has plenty of speed on something like an early Pentium with 32megs of RAM, though 64megs is even better.

A speedy desktop is largely just a matter of using a window manager and applications that suit your hardware. And by the way, just because you don't use the KDE or GNOME desktop environments doesn't mean you shouldn't install them, or at least their core libraries. KDE and GNOME apps will run quite well under a lightweight window manager, so if you have the disk space, I recommend installing both. In my experience though, GNOME/GTK apps load appreciably quicker than the KDE equivalents. Listed below are some suggestions for the type of apps. that most people use everyday, all of which work nicely on my 233/64 box - and most of this stuff should be fine with just 32megs of RAM. Keep in mind that these suggestions are only my own personal preferences; they certainly aren't the only way to do things.

The Selection Criteria

Where to Get Packages

You'll find a lot of this stuff is included on the installation cd's of most distro's. Wherever possible, I've provided a link to the projects homepage.

The Window Manager

The choice of window manager can have a dramatic effect on system performance, and there are several good, lightweight WMs available, my favourite being IceWm . As well as having a small memory footprint, IceWm can be made to look quite good with wallpapers and themes . It also has that familiar Win95 layout with the corner start button, menus, toolbar and so on. There are certainly lighter WMs around, but for me IceWm provides a good balance of useful features and performance - and it is lighter on RAM than some other window managers with far fewer features. Don't assume that IceWm has to be plain or ugly - current versions can support XFT antialiasing, gradients and so on, and with a theme like Silver XP can be quite good-looking.

Configuring IceWm is extremely easy, and while there are graphical tools available for this, it's just as easy to edit its configuration files manually. The global configuration files are usually in /usr/X11R6/lib/X11/icewm/ and are named preferences , menu and toolbar . Make a hidden folder called .icewm in your home directory and copy these three files into it. Then it's just a matter of editing them to suit your own needs and tastes.

IceWm is included with many recent distros, and includes very good documentation in /usr/doc/icewm.

Xfce is another popular, fast window manager, though strictly speaking it's really a desktop environment. Worth a look if you want something with more bells and whistles - but I still prefer IceWm.

The File Manager

I've tried plenty of graphical file managers, but I always come back to XWC (X Windows Commander) because of its speed, stability, and again for its familiar interface. XWC is a clone of the Win95 style Explorer that supports drag'n'drop to copy/move/symlink, file associations and so on. Although it lacks many of the features of say, Nautilus or Konqueror, its got everything I need, without the bloat. Like IceWm, it is very easy to configure using the built in options menu or by editing the ~/.foxrc/XWC file.

While I'd prefer something that doesn't look quite so Windows-like, XWC works very well and is quite speedy. It also includes a handy archiving tool, a media mounting tool and a rather ordinary front end to RPM. XWC can be used in two-panel mode, like all good file managers.

Work has recently resumed on XWC, mainly in an effort to support a greater variety of languages. A couple of file managers have evolved from XWC, namely XFE and Fox Commander, and although they are more modern looking, I still find XWC to be the best for stability and performance.

Another fast, good looking filer that is highly recommended is rox

Terminal Emulators

Forget about the korpulent konsole, rxvt has a combination of features and speed that make it my favourite, plus you can customise its appearance if you are into that sort of thing. An even lighter alternative is aterm.

Text Editors

While XWC comes with its own (very) basic editor, I much prefer Nedit . Nedit is fairly small, is fast and has lots of useful features built in, including: syntax highlighting, search and replace, macro support, shell access and much more. The built in help is very good as well. I know some people get passionate about their editors ( especially the vi crowd ), but if you want a good WYSIWYG style editor, Nedit is very nice indeed.

Another editor that I like to have available is Nano, an exceptionally light, basic app. that will be instantly familiar to Pine/Pico users. Unlike Nedit, Nano will run from a console as well as an X-terminal, and the value of this will be appreciated by anyone who's misconfigured their XF86-Config file to the point that X won't start ;)

Internet Stuff

Linux users now have a choice of quality browsers that match or beat the performance of those on other platforms, though some are a little resource hungry. These range from basic (but speedy) browsers like Dillo and Links-Graphic to the ones based on a stripped down Mozilla, such as Galeon and Firefox.

At the lighter end of the range, links-graphic is hard to beat. Speedy, and with a small footprint, it is surprisingly capable, and can handle frames and tables as well as javascript. Its interface is a little different to most other graphical browsers, but quite good once you get used to it. Dillo is another one worth trying. Dillo is extremely fast, and quite good looking as well. Still under development, it doesn't yet handle frames, java or javascript, so you probably won't be able to do your online banking with it. It's brilliant for reading local html files (like helpfiles and /usr/doc/html/ stuff).

Opera is a very popular browser that fits somewhere between the superlight apps like links-graphic or Dillo, and the heavy duty Mozilla based browsers. The free-download version of Opera includes banner advertising, though it's not really intrusive. It performs well, and supports most popular plugins.


If you have 64meg or more, you might want to try one of the Mozilla based browsers. Galeon and Firefox are two very good browsers based on the Mozilla engine. Unlike Mozilla, Galeon and Firefox are web browsers only, making them significantly more usable on slower machines, though they will still be slowish to start on an old machine. They are exceptionally capable and stable, and support most plugins.

As for email, I like Sylpheed. Sylpheed is very fast, and has a nice clear interface. It is also a basic newsreader. There is also a related mail client named SylpheedClaws that extends the capabilities of Sylpheed via the use of plugins and enables things like HTML and so on.

If you'd like a more fully featured news client, so you might want to try Pan, a GTK news app. capable of handling binary attachments.

I know there are several graphical ftp clients, and I did play briefly with gFTP (which ran fine), but I can't really recommend anything else as I still prefer the command-line ncftp.

Image Viewers

I use xli as my default image viewer. It's quick, and I like the way I can directly scroll big images with the mouse; qiv (Quick Image Viewer) is nice as well. For simple manipulations xv works well and requires little memory, though the interface is showing its age.


Music and Video

The hugely popular XMMS is a WinAmp clone that can play mp3, ogg-vorbis, wav and cdr files etc. It also supports skins, including WinAmp skins. As for video mpegs, I use mtvp as the default player. It's a free player that's part of the mtv package and works very well on lower end machines. If you view lots of videos in different formats, MPlayer plays nearly all of them and is surprisingly quick on older boxes. The officially supported version is only available as source, though binary packages are available as well. MPlayer can even make use of MS Windows dlls to play Windows media files.

There are also plenty of graphical front ends around for cd recording software. I have played around with the very popular xcdroast , but mainly I still use command line tools like cdrecord, mpg123, bladeenc etc. Again, let me know if you have recommendations.

Office Type Stuff

Word Processing

Abiword is GNOME's word processor, and is notable for its speed and light memory usage. It is also lighter on features than say OpenOffice-Writer or KWrite, but for some this simplicity may be appealing. At present Abiword seems to be able to open the less complex MS-Word documents without problems, but chokes on .doc files with complicated formatting.

TextMaker

TextMaker is a commercial product, and while I haven't tried it personally, it does receive glowing reviews. Both speed and .doc file compatibility are said to be very good, so this should be a good choice for older hardware. If you think this sounds appealing, and you don't mind paying for software, this might be for you. There is a 30 day trial version available from the TextMaker website.

Wordperfect

The last available version (version 9, part of WordPerfect Office2000) was actually just the Windows version running under a built-in version of Wine. While this version had a reputation for instability and other problems, an older, Linux native version (Wordperfect8) still enjoys some popularity - some still consider it as the best Linux word processor available. WP8 is stable, fast and full featured, and light on memory usage. The Download Personal Edition is still available, though its age dictates that some older libraries also need to be installed in order to get it working on newer distros - more info here.

KWord is the KDE project's word processor, and it looks and works very well, however it also has limited compatability with MS .doc files at present.

If MSWord compatability is critical, you'll really have no choice but to run OpenOffice.org or StarOffice. These suites are big and extremely slow to load, though recent versions are much improved in this regard. Even so, firing them up on a box with only 32megs of ram may take up to a minute... Once loaded though, they are stable and run fine. They seem to be able to handle nearly any .doc format file much better than any of the others, so even if you choose to use a different word processor for your regular work, it may pay to install Star/Open Office so you can open those difficult .doc files that your usual WP has trouble with.

For those who don't require any MS Word compatability, Netscape (or Mozilla) Composer can do a pretty good job of producing printed documents. While it's not really a word processor, it can do tables, embed images and links as well as spell check. Plus the html output is readable on just about anything. Keep Composer in mind if you just want to write the occasional letter without installing a full-blown WP program.

Spreadsheets

Gnumeric should probably be your first choice here; it's a mature app. that seems to handle Excel files exceptionally well without hogging resources. KSpread , like KWord, also runs well enough but doesn't completely work with Microsoft formats just yet.

Performance

The table below shows the approximate startup times for some of the software mentioned above. These times were measured on a 233 mHz AMD with 64meg of RAM and Linux 2.2, using the highly unscientific method of clicking on the button and then counting the delay using the toolbar clock. Of course, a calendar might be more appropriate for timing Star/Open Office...The figures are obviously only rough approximations in view of the measurement technique, but they do give a good indication of just how responsive an old Linux box can be.
 

Program

First Startup

Subsequent Starts

XWindowsCommander

1 sec

0.5 sec

Nedit

2 secs

1.5 sec

Netscape 4.77

9 secs

4 secs

Dillo

1 sec

0.5 sec

Sylpheed

1.5 sec

1 sec

xli (XLoadImage)

<1 sec

0.5 sec

XMMS

3 sec

2.5 sec

mtvp

1 sec

0.5 sec

Gnumeric

6 secs

4 secs

AbiWord

2.5 secs

2 secs

Miscellaneous

Screen Savers are probably more of a nicety than a necessity. Xscreensaver works very well with lightweight window managers and is easy to set up. It can run a randomly picked screensaver after a user-set period, and continue to change it at pre-set intervals. Run xscreensaver-demo to set the preferences, or see the man pages for more details. The easiest way to start xscreensaver automatically at login is by adding the xscreensaver & command to your window manager's startup script, eg. /usr/X11R6/bin/icewm.

Unnecessary Services or daemons can slow your machine down and lengthen bootup times. Default installations often run all sorts of servers and other stuff that you don't need. As well as using resources, these things can increase your security risk.

Screenshots

Here are some screenshots of some of the things mentioned above.

Links

Some sites that might be useful:

LoFat Part 2 - A guide to the post install tune-up
The Linux Terminal Server Project - a