strmod-devel Mailing List for The StreamModule System
Status: Alpha
Brought to you by:
omnifarious
You can subscribe to this list here.
2000 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(2) |
Oct
(6) |
Nov
(5) |
Dec
(2) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2001 |
Jan
(4) |
Feb
(5) |
Mar
(4) |
Apr
|
May
|
Jun
|
Jul
(4) |
Aug
|
Sep
(4) |
Oct
|
Nov
|
Dec
|
2002 |
Jan
(2) |
Feb
|
Mar
(3) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(2) |
Oct
|
Nov
|
Dec
|
2003 |
Jan
(1) |
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(1) |
Aug
(3) |
Sep
|
Oct
|
Nov
(1) |
Dec
|
2004 |
Jan
|
Feb
|
Mar
|
Apr
(3) |
May
(1) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: Jim H. <jh...@iu...> - 2004-05-04 19:12:33
|
Hi Eric, I'm doing the design today on adding multicast support to StrMod. If a proper design isn't too bad, I'll do it correct right away. Otherwise at least for my presentation this Friday, I'll do it in a quick temporary way. If I have to resort to that, then I will implement it correctly after this week, so don't worry at all. :) I'm thinking that there are two ways to do this. First, I could create 2 UDPSocketModules, one for sending and one for receiving (the explanation of a program that does both in the Unix Network Programming book says that there needs to be two different sockets to both send and receive multicast messages on if you bind to an address). I could then have a member function that would set each UDPSocketModule to join a given multicast interface and group. This seems like the quickest way, but it really doesn't add multicast support to StrMod in a very C++ way. It's more like a functional C way. So my second thought is to create a new derived class from UDPSocketModule called MCastSocketModule which would simply override the parts of the UDPSocketModule that need to be to implement multicasting. My thought then was to create the two UDP sockets within MCastSocketModule that would do the sending and receiving specifically. My question is, if this is the correct approach in your opinion to implement multicasting, with the two sockets in the one class for sending and receiving, can this be done without using threads? Will it work just fine by creating non-blocking socket descriptors? Thanks! Jim Hodapp -- Full-time student of Electrical Engineering Purdue School of Engineering @ Indiana University Purdue University Indianapolis |
From: Jim H. <jh...@iu...> - 2004-04-29 05:52:18
|
Thanks for looking at these Eric. Jim Hodapp -- Full-time student of Electrical Engineering Purdue School of Engineering @ Indiana University Purdue University Indianapolis |
From: Jim H. <jh...@iu...> - 2004-04-28 15:14:59
|
Here are my 4 class file: Thanks Eric! Jim Hodapp -- Full-time student of Electrical Engineering Purdue School of Engineering @ Indiana University Purdue University Indianapolis |
From: Jim H. <jh...@iu...> - 2004-04-28 02:50:06
|
Hey Eric, I was looking over how to implement Length() in my StrChunkUDPDecorator class and got confused on one thing. I know that from UDPSocketModule::doReadFD() that the buffed_read_ is a DynamicBuffer. The way I see it, this is the class that should return how big my StrChunk is that I'm passing in by reference to my StrChunkUDPDecorator constructor. However, there is no such way that I can see on how to get the current length of a DynamicBuffer. I imagine this is by design, but what do I do in my case? Also, for StrChunkUDPDecorator::Length(), should it report the combined octal length of the StrChunk + string that stores the address + U2Byte that stores the port? Or should it only report the length of the StrChunk? Since I'm not sure what exactly calls the StrChunkUDPDecorator::Length() function, I do not know what it is looking for. Thanks for all of the help! Jim Hodapp -- Full-time student of Electrical Engineering Purdue School of Engineering @ Indiana University Purdue University Indianapolis |
From: Jim H. <jh...@iu...> - 2003-11-12 00:01:28
|
Here's the line that I'm getting from the StrMod library for the assert: We're in i_Read. lookup_server: StrMod/StreamFDModule.cxx:722: virtual void strmod::strmod::StreamFDModule::doWriteFD(): Assertion `curbuflist_.bytesLeft() > 0' failed. Aborted (core dumped) This is confusing since the part in my code that I believe is generating this assert only differs from another part of code by the case. One is case 00001 and the other is case 00101. Other than that they are the same as far as interaction with the StrMod library goes. Thanks, Jim -- Jim Hodapp Electrical Engineering Purdue School of Engineering @ Indiana University Purdue University Indianapolis |
From: Eric M. H. <ho...@om...> - 2003-08-31 01:33:20
|
This is in response to a query from someone on IRC. I tried to engineer plugs and modules so that plugs could be created either dynamically, or be direct, non-pointer member variables of the module they're a part of. Many of the functions dealing with plugs, modules and their relationship exist to support this goal. Typically, when writing a StreamModule, you will derive your own plug type (or possibly types) for your own module. The reasons for all of this will become clear when I explain the i_Read and i_Write functions for a plug. Also, for reasons related to the four functions described immediately below, you are advised to put the declaration for the plug class in the protected section, as users of your Module will not be able to usefully create Plugs. You are also advised to put the constructor and _destructor_ in the protected section of the Plug class and make the Module class a friend of the Plug class. Protected destructors are highly unusual, but, in this case, it's an excellent idea to do this to prevent users of your Plug from attempting to use normal C++ techniques to manage the lifetime of Plugs, and force them to call the module's createPlug and deletePlug functions instead. First, the functions the need to be implemented in your Module class (MyModule) are these: // Ask if a plug can be created on the given side for this module // instance. bool MyModule::canCreate(int side) const; // Ask if the plug instance belongs to this module instance. bool MyModule::ownsPlug(StreamModule::Plug *plug); // A protected function used by the public, non-virtual function // createPlug to actually create the plug. Your plug creation code // (even if it's just setting a flag) should go here. StreamModule::Plug *MyModule::i_makePlug(int side); // Called by a user when a plug should be deleted. This returns false // if the deletion fails. It should only fail if the module instance // doesn't own the plug instance. bool MyModule::deletePlug(StreamModule::Plug *plug) These functions all exist to provide users a way to create plugs that's mediated by the Module class. This mediation is necessary for two reasons. First, the Module class needs to be aware of all the plugs associated with it. Second, the Module class needs to control how plugs are created, since some plugs will be created by the Module as part of it's own state, and it needs to manage their lifetimes carefully. A 'side', as explained elsewhere, represents a protocol role. Often you will have one Plug class per side. A side refers to the set of actions a Module will take with regards to input or output to a plug on that side. It doesn't refer to the identity of any individual plug. The 'side' concept is a rather abstract concept, so here are a few concrete examples from Unix: The 'egrep' command has two 'sides'. One side only recieves lines of text to check against the regular expression. The other side only produces lines that match the regular expression. The 'tee' command also only has two sides, though it may have many plug instances on one of the two sides. One side of 'tee' reads in data, the other side writes it out. Since tee allows you to specify a number of files to write to in addition to stdout, there are many instances of the 'write it out' side, but since each instance of 'write it out' does essentially the same thing, they're on a single side since they only fulfill one role in the 'tee' protocol. The echo service in Unix has one side. That side has both input and output, it's bidirectional. Anything that comes in on that side also goes out on that side. The side is the network socket. The irc service has at least two sides, possibly more. I don't know it very well. But, I do know there's a host<->client protocol, and a host<->host protocol. So there are many clients, but each client speaks the same protocol. And, there are many other hosts to talk to, but (I think anyway) only one protocol used to talk from host to host. The base Plug class needs to know which StreamModule it's a part of, so its constructor takes a reference to the parent StreamModule. This means that the constructor for your derived plug class should also take a reference to the module it's a part of so it can call the base Plug constructor. This means that the minimum constructor should look something like this: MyPlug::MyPlug(MyPlugParentModule &parent) : Plug(parent) { } I'm tired of writing now, but this should be some useful information to start with. :-) Here are links to the source to a couple fairly simple modules and its associated plugs. http://www.omnifarious.org/StrMod/doxygen/ProcessorModule_8h-source.html http://www.omnifarious.org/StrMod/doxygen/ProcessorModule_8cxx-source.html As you might notice, this module has the plugs as member variables and uses flags to track their created/deleted state. Have fun (if at all possible), -- There's an excellent C/C++/Python/Unix/Linux programmer with a wide range of other experience and system admin skills who needs work. Namely, me. http://www.omnifarious.org/~hopper/resume.html -- Eric Hopper <ho...@om...> |
From: Eric M. H. <ho...@om...> - 2003-08-06 20:16:12
|
On Fri, 2003-08-01 at 17:42, Jim Hodapp wrote: > Eric, >=20 > How exactly do I use the StrChunk iterator to iterate through the > incoming_ StrChunk in processIncoming()? I want to get potential > incoming data out so I can analyze it, cout it to the screen, do > comparisons, etc. Then, how do I connect my processIncoming() code to > actually hook into the incoming accepted connection(s) that the program > might establish? Hopefully I can catch you online in #sockets. The easiest way to do this is to copy the StrChunk into a string. This isn't very efficient, but will get you going. The code to copy the contents of a StrChunk into an STL string looks something like this: StrChunkPtr chunkptr =3D incoming_; ::std::string chunkstr(chunkptr->begin(), chunkptr->end()); This will construct a new STL string, chunkstr, that will contain the contents of the StrChunk pointed to by incoming_. Then you can manipulate the data using standard string operations. There are better ways to do this, but that's the easiest to code and understand. Have fun (if at all possible), -- There's an excellent C/C++/Python/Unix/Linux programmer with a wide range of other experience and system admin skills who needs work. Namely, me. http://www.omnifarious.org/~hopper/resume.html -- Eric Hopper <ho...@om...> |
From: Jim H. <jh...@iu...> - 2003-08-02 03:42:12
|
Eric, How exactly do I use the StrChunk iterator to iterate through the incoming_ StrChunk in processIncoming()? I want to get potential incoming data out so I can analyze it, cout it to the screen, do comparisons, etc. Then, how do I connect my processIncoming() code to actually hook into the incoming accepted connection(s) that the program might establish? Hopefully I can catch you online in #sockets. Thanks, Jim -- Jim Hodapp Electrical Engineering Purdue School of Engineering @ Indiana University Purdue University Indianapolis |
From: Jim H. <jh...@iu...> - 2003-07-23 04:25:14
|
Test email! You said to send you an email so here it is. :) -- Jim Hodapp Electrical Engineering Purdue School of Engineering @ Indiana University Purdue University Indianapolis |
From: Eric M. H. <ho...@om...> - 2003-01-29 06:47:22
|
=46rom the Recent Notes section of the website: (http://www.omnifarious.org/StrMod/recent.html) libNet 0.5.0 has been released. Mostly I've added some new classes for parsing XML, and a bunch of unit tests. The classes for parsing XML haven't been well integrated into StreamModule as a whole yet, but they will be, and the interfaces to the currently existing classes shouldn't change all that much. =20 I'm also switching over to using Subversion as my version control system. Still no publicly accessible server though as I'm not willing to upgrade Apache to being bleeding edge in order to do it. *grin*, --=20 The best we can hope for concerning the people at large is that they be properly armed. -- Alexander Hamilton -- Eric Hopper (ho...@om... http://www.omnifarious.org/~hopper)= -- |
From: Eric M. H. <ho...@om...> - 2002-09-16 02:58:28
|
I've begun writing unit tests using CppUnit (http://cppunit.sourceforge.net). They're located in the tests-cppunit subdirectory. Writing the unit tests has already made me tighten up a lot of things about how ::strmod::strmod::LinearExtent worked. The nightly releases on http://www.omnifarious.org/StrMod/dl.html should have the test code in it if any of you care to look at it. I just looked, and realized that the nightly release script hasn't worked since I upgraded to RedHat 7.3. Oops, it's been fixed now. :-)=20 Have fun (if at all possible), --=20 The best we can hope for concerning the people at large is that they be properly armed. -- Alexander Hamilton -- Eric Hopper (ho...@om...=20 http://www.omnifarious.org/~hopper) -- |
From: Eric M. H. <ho...@om...> - 2002-09-11 13:16:19
|
libNet 0.4.0 has just been released. The reason it gets a new major release number is that the interfaces have all changed and code needs to be updated. The changes are minor ones, but still require a lot of code to be updated. Mainly, I've moved the LCore and EHnet++ classes into their own namespaces. I've also made some minor code cleanups and added some previously missing class documentation. But, that's about it. In conjuction with this release, a new version (0.8) of the PortForward tutorial has been released. This one includes only changes from version 0.7 that were needed to make it work with libNet 0.4.0 vs. libNet 0.3.x. Anyway, visit the downloads page to download your copy of libNet 0.4.0 today! As an aside, the code is now only guaranteed to compile and run under gcc-3.2 Have fun (if at all possible), --=20 The best we can hope for concerning the people at large is that they be properly armed. -- Alexander Hamilton -- Eric Hopper (ho...@om...=20 http://www.omnifarious.org/~hopper) -- |
From: Eric M. H. <ho...@om...> - 2002-03-27 03:05:05
|
I've released libNet 0.3.0 and PortForward 0.7. libNet 0.3.0 has all the new UnixEventPoll code in it, and PortForward both corresponds exactly to the tutorial, and uses the new UnixEventPoll stuff too. I may announce on Freshmeat soon. Most of the requirements I wanted to meet before I posted it have been met now. Have fun (if at all possible), --=20 The best we can hope for concerning the people at large is that they be properly armed. -- Alexander Hamilton -- Eric Hopper (ho...@om...=20 http://www.omnifarious.org/~hopper) -- |
From: Eric M. H. <ho...@om...> - 2002-03-22 00:09:28
|
As part of a move towards a slightly different philosophy in dealing with operating system events, I've create UnixEventPoll, UnixEventRegistry, and TimerEventTracker in the strmod::unievent namespace. These supercede UNIXpollManager, UNIXpollManagerImp, UNIXSignalHandler, and UNIXTimer, also in the strmod::unievent namespace. The only new class you'll instantiate directly is UnixEventPoll. The new interface to use is UnixEventRegistry. UnixEventPoll will do signals and file descriptors (interface inhereted from UnixEventRegistry) and timers based on setting its timeout value in the poll system call (interface inherited from Timer). It uses TimerEventTracker as a private base class to do all the Timer bookkeeping work. Unlike UNIXpollManagerImp, UnixEventPoll doesn't automatically register itself with a dispatcher or call it periodically. I need to write a glue class that does this. This is partly because I'm making some changes in how Dispatcher works, and the glue class will be a good idea once those changes are in place. The StreamFDModule, and other classes that use UNIXpollManager or UNIXpollManagerImp have not been converted to use UnixEventPoll yet. Have fun (if at all possible), --=20 "It does me no injury for my neighbor to say there are twenty gods or no Go= d. It neither picks my pocket nor breaks my leg." --- Thomas Jefferson "Go to Heaven for the climate, Hell for the company." -- Mark Twain -- Eric Hopper (ho...@om... http://www.omnifarious.org/~hopper)= -- |
From: Eric M. H. <ho...@om...> - 2002-03-07 12:35:40
|
The tutorial is mostly done now, and can be found at: http://www.omnifarious.org/StrMod/docs/Tutorial.html There's still more do be done to it, but it's now complete enough to give you a running program at the end. Have fun (if at all possible), --=20 The best we can hope for concerning the people at large is that they be properly armed. -- Alexander Hamilton -- Eric Hopper (ho...@om...=20 http://www.omnifarious.org/~hopper) -- |
From: Eric M. H. <ho...@om...> - 2002-01-24 18:34:36
|
I'm back on the #StrMod channel on irc.openprojects.net on a regular basis again. Feel free to log in if you feel like asking a question or just talking. Have fun (if at all possible), --=20 "It does me no injury for my neighbor to say there are twenty gods or no Go= d. It neither picks my pocket nor breaks my leg." --- Thomas Jefferson "Go to Heaven for the climate, Hell for the company." -- Mark Twain -- Eric Hopper (ho...@om... http://www.omnifarious.org/~hopper)= -- |
From: Eric M. H. <ho...@om...> - 2002-01-23 19:52:29
|
I'm updating and adding to the documentation on my StreamModule system. Better documentation can be found at: http://www.omnifarious.org/StrMod/docs/ I hope to be releasing a number of long brewing changes over the next few months. Have fun (if at all possible), --=20 "It does me no injury for my neighbor to say there are twenty gods or no Go= d. It neither picks my pocket nor breaks my leg." --- Thomas Jefferson "Go to Heaven for the climate, Hell for the company." -- Mark Twain -- Eric Hopper (ho...@om... http://www.omnifarious.org/~hopper)= -- |
From: Eric M. H. <ho...@om...> - 2001-09-25 03:58:52
|
A new PortForward has been released that works with the libNet 0.2.5 library. It's a crude adaptation, but better one that doesn't work at all. :-) Have fun (if at all possible), --=20 The best we can hope for concerning the people at large is that they be properly armed. -- Alexander Hamilton -- Eric Hopper (ho...@om...=20 http://www.omnifarious.org/~hopper) -- |
From: Eric M. H. <ho...@om...> - 2001-09-23 23:21:34
|
The next nightly checkout of the StrMod system is going to have a lot of new changes. First, the StrMod library has been moved into the strmod::strmod namespace. Also, when that library references things that should be in the std:: namespace, it should work in a compiler that enforces the use of that namespace (like gcc 3.0). Second, a new event source class has been added to UniEvent. There is an interface class, Timer, and an implementation of that interface, UNIXTimer. These classes should allow you to cause events to be posted at particular times. UNIXTimer currently relies on UNIXSignalHandler and the SIGLARM signal to work. The Timer class and the UNIXSignalHandler should be used with caution. Their interfaces are unlikely to change much, but they have highlighted some significant defects in the implementation of SimpleDispatcher. I will have to rewrite SimpleDispatcher yet again to fix the problems.=20 The problem is that the SimpleDispatcher won't check for interrupts (which is how the signal handler gets its even to run when a signal occurs) unless there is already an event on the queue. This is obviously bad behavior, but the logic for SimpleDispatcher is so dependent on the system of queues and events it had that adding this new bit of logic would be very hard, not the least because an interrupt may occur while SimpleDispatcher is trying to decide what event to dispatch. Have fun (if at all possible), --=20 The best we can hope for concerning the people at large is that they be properly armed. -- Alexander Hamilton -- Eric Hopper (ho...@om...=20 http://www.omnifarious.org/~hopper) -- |
From: Eric M. H. <ho...@om...> - 2001-09-07 13:09:42
|
I've moved everything in the StrMod subdirectory to be in the strmod::strmod namespace. I've also been updating and improving documentation as I go along. If anybody wants to tackle moving the LCore subdirectory into the strmod::lcore namespace, it'd be a wonderful thing. The best way to do this is to download the event2 branch and make the changes there and send in diffs. Someday, I'll do some kind of hardening thing and have anonymous CVS access, but right now I don't trust it. I think I will also make a 'names.h' header in each subsystem that declares all the names used in that subsystem without actually declaring the definitions. So, it'll be fast to include and will eliminate things like: namespace strmod { namespace strmod { class StreamModule; }; }; to forward declare the StreamModule class. You could just include <StrMod/StreamModule.h>, but that introduces an unnecessary dependency on a file who's contents you probably are not particularily concerned with. Have fun (if at all possible), --=20 "It does me no injury for my neighbor to say there are twenty gods or no Go= d. It neither picks my pocket nor breaks my leg." --- Thomas Jefferson "Go to Heaven for the climate, Hell for the company." -- Mark Twain -- Eric Hopper (ho...@om... http://www.omnifarious.org/~hopper)= -- |
From: Eric M. H. <ho...@om...> - 2001-09-04 13:30:41
|
I just merged a bunch of new code in the UniEvent system into the main branch. This code moves UniEvent itself into the strmod::unievent namespace, and renames some of the classes previously prefixed with UNI as this is no longer needed with namespaces. This is part of a bigger move to move the entire library to use namespaces. I also added UNIXSignalHandler. This can be used to turn signals into events handled by a subclass of Dispatcher. Dispatcher itself has the 'interrupt' and 'onInterrupt' methods added to its interface to support this usage. The main difficulty is that because of how I wrote the Dispatcher, it will only recognize 'interrupts' if it has events on its queue that can be interrupted. Since the Dispatcher is usually also used with a UNIXpollManager that sets an 'onQueueEmpty' event that does a 'poll', this is not a problem in many programs. I should be packaging this up into a new release sometime soon. I don't know yet if I consider the problem mentioned in the above paragraph severe enough to prevent release. To those who've recently unsubscribed and are getting this... This is the last message I'll send out. I just didn't want you to have unsubscribed over the seeming complete lack of activity. :-) Have fun (if at all possible), --=20 The best we can hope for concerning the people at large is that they be properly armed. -- Alexander Hamilton -- Eric Hopper (ho...@om...=20 http://www.omnifarious.org/~hopper) -- |
From: Eric M. H. <ho...@om...> - 2001-07-12 15:48:27
|
A new branch has been added to CVS. The branch tag is: new_event-branch The tag for the pre-branch mainline versions of the files is: new_event-pre-branch This branch is so I can re-work the event subsystem to be more coherent and support some of the new features I would like to add like multi-threading and signal handling. Also, I would like to improve the platform independence so the main event loops for Unix, BeOS, EROS, and other platforms like NT will be the same. Have fun (if at all possible), --=20 "It does me no injury for my neighbor to say there are twenty gods or no Go= d. It neither picks my pocket nor breaks my leg." --- Thomas Jefferson "Go to Heaven for the climate, Hell for the company." -- Mark Twain -- Eric Hopper (ho...@om... http://www.omnifarious.org/~hopper)= -- |
From: Eric M. H. <ho...@om...> - 2001-07-11 03:15:26
|
Version 0.2.5 of the StreamModule system has been released. It isn't on SourceForge yet, but it can be found on the new website at: http://www.omnifarious.org/StrMod/ It has all the previously mentioned changes. A summary can be found on the webpage. Have fun (if at all possible), --=20 "It does me no injury for my neighbor to say there are twenty gods or no Go= d. It neither picks my pocket nor breaks my leg." --- Thomas Jefferson "Go to Heaven for the climate, Hell for the company." -- Mark Twain -- Eric Hopper (ho...@om... http://www.omnifarious.org/~hopper)= -- |
From: Eric M. H. <ho...@om...> - 2001-07-05 18:09:24
|
On Thu, Jul 05, 2001 at 12:11:41AM -0000, Roz Thomas wrote: > What's a starvation bug? I can't find a definition in my Unix book. Starvation is a problem that can occur in systems that have some mechanism of allocating resources (usually CPU resources) to a process or part of the program. If resources are never assigned to a certain part of the program, that part 'starves'. In the case of StreamModule, there was a bug that caused high bandwidth connections to constantly use the CPU and not let any other connections process any of their data. It was solved by making various things voluntarily give up the CPU if it appears, from some simple heuristic, that they are hogging it. I tested it using PortForward by having a forwarded connection to localhost:9, the discard service. The discard service discards every character it reads, so it's a perfect service to send tons of data to. I then had a local client connect to the port that PortForward was forwarding to port 9. It sent an endless stream of zeros. Then I connected to a different port that was forwarded to port 23 (the telnet port). If I could log in, the problem wasn't there. If I couldn't, it was. Before my fix, a telnet from a remote host to port 23 would hang in that situation. After my fix, it worked with no noticeable difficulty. Have fun (if at all possible), --=20 "It does me no injury for my neighbor to say there are twenty gods or no Go= d. It neither picks my pocket nor breaks my leg." --- Thomas Jefferson "Go to Heaven for the climate, Hell for the company." -- Mark Twain -- Eric Hopper (ho...@om... http://www.omnifarious.org/~hopper)= -- |
From: Eric M. H. <ho...@om...> - 2001-07-04 13:56:54
|
The current CVS for StreamModule contains a fix for a starvation bug I realized could happen and subsequently proved. I will be making a new release soon with this fix in it. Also in the current CVS is a preliminary change to StreamFDModule to use an event based error handling system instead of the current one which requires polling. There are important features missing from this first cut, but it should work. The error handling change caused me to rethink errors in general, and I have a new class LCoreError for general errors, and a UNIXError class for errors generated by Unix system calls. I may be rethinking this some more as I incorporate ideas about how to do exception handling. Also, Chad has discovered that gcc 3.0 is going to be strict about the std namespace. Since this will require a lot of change, it is perhaps a good time to incorporate namespaces into the design. Remember, feel free to ask any questions about stuff. Have fun (if at all possible), --=20 "It does me no injury for my neighbor to say there are twenty gods or no Go= d. It neither picks my pocket nor breaks my leg." --- Thomas Jefferson "Go to Heaven for the climate, Hell for the company." -- Mark Twain -- Eric Hopper (ho...@om... http://www.omnifarious.org/~hopper)= -- |