From: Robert K. <rl...@al...> - 2019-05-17 17:17:36
|
Solomon, do you think it would make sense to move the backend to its own directory under src/cups? Only thing is, as long as we're doing 5.2 releases, doing backports might be a bit more of a pain for you. -- Robert Krawitz <rl...@al...> *** MIT Engineers A Proud Tradition http://mitathletics.com *** Member of the League for Programming Freedom -- http://ProgFree.org Project lead for Gutenprint -- http://gimp-print.sourceforge.net "Linux doesn't dictate how I work, I dictate how Linux works." --Eric Crampton |
From: Solomon P. <pi...@sh...> - 2019-05-17 23:54:11
Attachments:
signature.asc
|
On Fri, May 17, 2019 at 01:17:26PM -0400, Robert Krawitz wrote: > Solomon, do you think it would make sense to move the backend to its > own directory under src/cups? No objections from me. > Only thing is, as long as we're doing 5.2 releases, doing backports > might be a bit more of a pain for you. Right now I'm only backporting what I consider to be critical bugfixes. Due to signifcant internal differences, it's already a mostly manual affair. To ease my maintainence burden, I've debated just syncing up the 5.2 branch with the master backend code; it's fully backwards compatible. But I have no plans to backport any of the dyesub printer additions. Most of the dyesub users that contact me are using LTS-type distros with relatively ancient gutenprint builds. Since they generally need to recompile gutenprint from source anyway, I've been handing them 5.3 snapshots. (And then only if recompiling the backend isn't sufficient) Granted, that still leaves out OSX users... - Solomon -- Solomon Peachy pizza at shaftnet dot org Coconut Creek, FL ^^ (email/xmpp) ^^ Quidquid latine dictum sit, altum videtur. |
From: Robert K. <rl...@al...> - 2019-05-18 01:05:41
|
On Fri, 17 May 2019 19:53:52 -0400, Solomon Peachy wrote: > On Fri, May 17, 2019 at 01:17:26PM -0400, Robert Krawitz wrote: >> Solomon, do you think it would make sense to move the backend to its >> own directory under src/cups? > > No objections from me. It's a a pretty big chunk of code just by its lonesome. >> Only thing is, as long as we're doing 5.2 releases, doing backports >> might be a bit more of a pain for you. > > Right now I'm only backporting what I consider to be critical bugfixes. > Due to signifcant internal differences, it's already a mostly manual > affair. > > To ease my maintainence burden, I've debated just syncing up the 5.2 > branch with the master backend code; it's fully backwards compatible. > > But I have no plans to backport any of the dyesub printer additions. > > Most of the dyesub users that contact me are using LTS-type distros with > relatively ancient gutenprint builds. Since they generally need to > recompile gutenprint from source anyway, I've been handing them 5.3 > snapshots. (And then only if recompiling the backend isn't sufficient) > > Granted, that still leaves out OSX users... Hopefully not for long, now. Maybe it's time to just declare that 5.2.15 will be the last 5.2 release barring disaster (like security hole) and as of 5.3.3 (or maybe 5.4.0?) that should be declared stable. I have more confidence now in 5.3 than 5.2; for one, we actually have a repeatable build/test pipeline. I usually manage to goof something doing a 5.2 build, and the backend_sinfonia.h thing likely would have slipped through unless I thought to do a make distcheck. The build-release script in 5.3 pretty well puts paid to all that. -- Robert Krawitz <rl...@al...> *** MIT Engineers A Proud Tradition http://mitathletics.com *** Member of the League for Programming Freedom -- http://ProgFree.org Project lead for Gutenprint -- http://gimp-print.sourceforge.net "Linux doesn't dictate how I work, I dictate how Linux works." --Eric Crampton |
From: Robert K. <rl...@al...> - 2019-05-18 01:21:53
|
On Fri, 17 May 2019 19:53:52 -0400, Solomon Peachy wrote: > On Fri, May 17, 2019 at 01:17:26PM -0400, Robert Krawitz wrote: >> Solomon, do you think it would make sense to move the backend to its >> own directory under src/cups? > > No objections from me. BTW, IIRC you were working on a test suite for the backend; is that something you could add in? -- Robert Krawitz <rl...@al...> *** MIT Engineers A Proud Tradition http://mitathletics.com *** Member of the League for Programming Freedom -- http://ProgFree.org Project lead for Gutenprint -- http://gimp-print.sourceforge.net "Linux doesn't dictate how I work, I dictate how Linux works." --Eric Crampton |
From: Solomon P. <pi...@sh...> - 2019-05-18 12:41:11
Attachments:
signature.asc
|
On Fri, May 17, 2019 at 09:21:42PM -0400, Robert Krawitz wrote: > BTW, IIRC you were working on a test suite for the backend; is that > something you could add in? What I have works well, but integrates poorly. It has two components: * The first works against a library of pre-generated test jobs of various sources (Windows, misc gutenprint releases) that exhibit quirks that might not be in the current gutenprint codebase. Here's a typical entry: #backend,vid,pid,filename,mediatype shinko-s1245,0x10ce,0x0007,shinko_s1245_8x10.raw,0x0 Each entry is run with valgrind, and 1-3 copies, 3 times total. (Takes 12 minutes on my E3-1230 V2 server) As this phase depends on a .5GB library of job files, there's probably little point in attempting to integrate it. * The second is more comprehensive, and relies on the *installed* gutenprint cups filter to generate test jobs. Here's the set of entries for the same model: #gp_printername,vid,pid,mediatype,gp_options shinko-chcs1245,0x10ce,0x0007,0,PageSize=w288h576 shinko-chcs1245,0x10ce,0x0007,0,PageSize=w360h576 shinko-chcs1245,0x10ce,0x0007,0,PageSize=w432h576 shinko-chcs1245,0x10ce,0x0007,0,PageSize=w576h576 shinko-chcs1245,0x10ce,0x0007,0,PageSize=w576h576-div2 shinko-chcs1245,0x10ce,0x0007,0,PageSize=c8x10 shinko-chcs1245,0x10ce,0x0007,0,PageSize=c8x10-div2 shinko-chcs1245,0x10ce,0x0007,0,PageSize=c8x10-w576h432_w576h288 shinko-chcs1245,0x10ce,0x0007,1,PageSize=w576h864 shinko-chcs1245,0x10ce,0x0007,1,PageSize=w576h864-div2 shinko-chcs1245,0x10ce,0x0007,1,PageSize=w576h864-div3 shinko-chcs1245,0x10ce,0x0007,0,PageSize=c8x10;StpLamination=Glossy shinko-chcs1245,0x10ce,0x0007,0,PageSize=c8x10;StpLamination=GlossyFine shinko-chcs1245,0x10ce,0x0007,0,PageSize=c8x10;StpLamination=Matte shinko-chcs1245,0x10ce,0x0007,0,PageSize=c8x10;StpLamination=MatteFine Jobs are generated with 1-3 pages, and with 1-3 copies, for a total of 9 jobs per entry. Current suite takes 51 minutes on the same Xeon box, without valgrind. I only bother with options that the job parsing/sanity checking code checks or otherwise affects the command stream sent to the printer. (stuff that's just blindly passed on is assumed to be okay) In both cases, I need the vid/pid to spoof the backend's printer scan/attach code, and the media code is used in lieu of a real printer reporting its loaded media type. Notable weaknesses: * Test metadata & option combos must be manually constructed. * Backend-centric; uses installed gutenprint filter. This is a reflection of how the backend code is developed, and is easily fixed. * No ability to handle an "expected failure" (eg print 5x7" with 6x4" media loaded) * Only tests through the parsing stage, as anything further requires emulating printer comms/behaviors. - Solomon -- Solomon Peachy pizza at shaftnet dot org Coconut Creek, FL ^^ (email/xmpp) ^^ Quidquid latine dictum sit, altum videtur. |
From: Robert K. <rl...@al...> - 2019-05-18 14:58:34
|
On Sat, 18 May 2019 08:40:59 -0400, Solomon Peachy wrote: > On Fri, May 17, 2019 at 09:21:42PM -0400, Robert Krawitz wrote: >> BTW, IIRC you were working on a test suite for the backend; is that >> something you could add in? > > What I have works well, but integrates poorly. It has two components: > > * The first works against a library of pre-generated test jobs of > various sources (Windows, misc gutenprint releases) that exhibit > quirks that might not be in the current gutenprint codebase. Here's > a typical entry: > > #backend,vid,pid,filename,mediatype > shinko-s1245,0x10ce,0x0007,shinko_s1245_8x10.raw,0x0 > > Each entry is run with valgrind, and 1-3 copies, 3 times total. > (Takes 12 minutes on my E3-1230 V2 server) Is there benefit to valgrinding all of these combinations? It's possible that there is; I've found a few obscure bugs only due to checking corner cases. > As this phase depends on a .5GB library of job files, there's > probably little point in attempting to integrate it. Not unless we had a separate repository for it, that's for certain! And obviously we wouldn't want that in the default pipeline. But hardware people have test data that make that look insignificant by comparison. > * The second is more comprehensive, and relies on the *installed* > gutenprint cups filter to generate test jobs. Here's the set of > entries for the same model: > > #gp_printername,vid,pid,mediatype,gp_options > shinko-chcs1245,0x10ce,0x0007,0,PageSize=w288h576 > shinko-chcs1245,0x10ce,0x0007,0,PageSize=w360h576 > shinko-chcs1245,0x10ce,0x0007,0,PageSize=w432h576 > shinko-chcs1245,0x10ce,0x0007,0,PageSize=w576h576 > shinko-chcs1245,0x10ce,0x0007,0,PageSize=w576h576-div2 > shinko-chcs1245,0x10ce,0x0007,0,PageSize=c8x10 > shinko-chcs1245,0x10ce,0x0007,0,PageSize=c8x10-div2 > shinko-chcs1245,0x10ce,0x0007,0,PageSize=c8x10-w576h432_w576h288 > shinko-chcs1245,0x10ce,0x0007,1,PageSize=w576h864 > shinko-chcs1245,0x10ce,0x0007,1,PageSize=w576h864-div2 > shinko-chcs1245,0x10ce,0x0007,1,PageSize=w576h864-div3 > shinko-chcs1245,0x10ce,0x0007,0,PageSize=c8x10;StpLamination=Glossy > shinko-chcs1245,0x10ce,0x0007,0,PageSize=c8x10;StpLamination=GlossyFine > shinko-chcs1245,0x10ce,0x0007,0,PageSize=c8x10;StpLamination=Matte > shinko-chcs1245,0x10ce,0x0007,0,PageSize=c8x10;StpLamination=MatteFine > > Jobs are generated with 1-3 pages, and with 1-3 copies, for a total > of 9 jobs per entry. Current suite takes 51 minutes on the same > Xeon box, without valgrind. I only bother with options that the job > parsing/sanity checking code checks or otherwise affects the > command stream sent to the printer. (stuff that's just blindly > passed on is assumed to be okay) Is this run parallel (which presumably wouldn't be that hard to arrange)? Test vectors like this are normally embarrassingly parallel, so with 8 core/16 threads I typically get in the range of 12x over single thread. And if the Ryzen 3000 really is as good as the rumors have it and if I upgrade off-cycle. Well. > In both cases, I need the vid/pid to spoof the backend's printer > scan/attach code, and the media code is used in lieu of a real printer > reporting its loaded media type. That's a reasonable mockup environment. This is part of the CUPS tree (even if we put it in a subtree), so we could set up the dependencies such that the CUPS driver is built in-tree for it. > Notable weaknesses: > > * Test metadata & option combos must be manually constructed. You probably don't need to run all 9 combinations; 1/1, 1/3, and 3/3 would seem likely to be what you'd need unless there's some specific reason to do more (maybe even 1/1, 1/2, and 2/2 or 2/3). That would cut the space down by a factor of 3. As for the VID and PID, we could either add those to printers.xml or you could have a separate .xml file that contains them (VID and PID are standard USB attributes, which are generic attributes and might at some point matter for other printers). Then you could have a program similar to src/testpattern/printer_options that extracts them and passes the data off to a run-testpattern-2 type program that generates the combinations. > * Backend-centric; uses installed gutenprint filter. This is > a reflection of how the backend code is developed, and is easily fixed. > * No ability to handle an "expected failure" (eg print 5x7" with 6x4" > media loaded) Again, that could be changed. > * Only tests through the parsing stage, as anything further requires > emulating printer comms/behaviors. Right now we don't have any actual printer emulation in our test suite. -- Robert Krawitz <rl...@al...> *** MIT Engineers A Proud Tradition http://mitathletics.com *** Member of the League for Programming Freedom -- http://ProgFree.org Project lead for Gutenprint -- http://gimp-print.sourceforge.net "Linux doesn't dictate how I work, I dictate how Linux works." --Eric Crampton |
From: Solomon P. <pi...@sh...> - 2019-05-20 01:22:43
Attachments:
signature.asc
|
On Sat, May 18, 2019 at 10:58:18AM -0400, Robert Krawitz wrote: > Is there benefit to valgrinding all of these combinations? It's > possible that there is; I've found a few obscure bugs only due to > checking corner cases. The 1-2 page transtion is most important, but some backends can now do job/copy combining so the 2-3 page transition matters for those too. So rather than trying to encode all of that complexity into the test definitions, it was just simpler to run it for everything. > Not unless we had a separate repository for it, that's for certain! > And obviously we wouldn't want that in the default pipeline. But > hardware people have test data that make that look insignificant by > comparison. I want to greatly expand that test suite, but now that the gutenprint-generated stuff is automated, a lot of the problem space gets encompassed by that. > Is this run parallel (which presumably wouldn't be that hard to > arrange)? Test vectors like this are normally embarrassingly > parallel, so with 8 core/16 threads I typically get in the range of > 12x over single thread. And if the Ryzen 3000 really is as good as > the rumors have it and if I upgrade off-cycle. Well. Both phases are set up to max out the core/thread count in the system, so on my box, those jobs run with 8 threads in parallel. > That's a reasonable mockup environment. This is part of the CUPS tree > (even if we put it in a subtree), so we could set up the dependencies > such that the CUPS driver is built in-tree for it. All the test script really cares about is where to find genpppd and rastertogutenprint. I now have those settable via an environment variable. > You probably don't need to run all 9 combinations; 1/1, 1/3, and 3/3 > would seem likely to be what you'd need unless there's some specific > reason to do more (maybe even 1/1, 1/2, and 2/2 or 2/3). That would > cut the space down by a factor of 3. Yeah, I don't envision a 3/3 job passing if 2/2 does not. > As for the VID and PID, we could either add those to printers.xml or > you could have a separate .xml file that contains them (VID and PID > are standard USB attributes, which are generic attributes and might at > some point matter for other printers). Then you could have a program > similar to src/testpattern/printer_options that extracts them and > passes the data off to a run-testpattern-2 type program that generates > the combinations. Probably makes the most sense to put VID/PIDs into printers.xml. The rest, probably not. > Right now we don't have any actual printer emulation in our test suite. There's still no substitute for actual hardrware. I now have all of the low-hanging stuff covered, but now it gets trickier.. - Solomon -- Solomon Peachy pizza at shaftnet dot org Coconut Creek, FL ^^ (email/xmpp) ^^ Quidquid latine dictum sit, altum videtur. |
From: Robert K. <rl...@al...> - 2019-05-20 02:16:21
|
On Sun, 19 May 2019 21:22:31 -0400, Solomon Peachy wrote: > On Sat, May 18, 2019 at 10:58:18AM -0400, Robert Krawitz wrote: >> Is there benefit to valgrinding all of these combinations? It's >> possible that there is; I've found a few obscure bugs only due to >> checking corner cases. > > The 1-2 page transtion is most important, but some backends can now do > job/copy combining so the 2-3 page transition matters for those too. So > rather than trying to encode all of that complexity into the test > definitions, it was just simpler to run it for everything. Understood. >> Is this run parallel (which presumably wouldn't be that hard to >> arrange)? Test vectors like this are normally embarrassingly >> parallel, so with 8 core/16 threads I typically get in the range of >> 12x over single thread. And if the Ryzen 3000 really is as good as >> the rumors have it and if I upgrade off-cycle. Well. > > Both phases are set up to max out the core/thread count in the > system, so on my box, those jobs run with 8 threads in parallel. So one question, is there something useful that can be done with a smaller test space (e. g. not the full N-dimensional space, just varying one parameter at a time)? >> That's a reasonable mockup environment. This is part of the CUPS tree >> (even if we put it in a subtree), so we could set up the dependencies >> such that the CUPS driver is built in-tree for it. > > All the test script really cares about is where to find genpppd and > rastertogutenprint. I now have those settable via an environment > variable. You can do that referenced to the build root. Look at the code in src/cups/test-rastertogutenprint.in. >> You probably don't need to run all 9 combinations; 1/1, 1/3, and 3/3 >> would seem likely to be what you'd need unless there's some specific >> reason to do more (maybe even 1/1, 1/2, and 2/2 or 2/3). That would >> cut the space down by a factor of 3. > > Yeah, I don't envision a 3/3 job passing if 2/2 does not. That would help a lot. >> As for the VID and PID, we could either add those to printers.xml or >> you could have a separate .xml file that contains them (VID and PID >> are standard USB attributes, which are generic attributes and might at >> some point matter for other printers). Then you could have a program >> similar to src/testpattern/printer_options that extracts them and >> passes the data off to a run-testpattern-2 type program that generates >> the combinations. > > Probably makes the most sense to put VID/PIDs into printers.xml. The > rest, probably not. That's hardware information that's relevant to all USB-connected printers, which are the large majority of printers we support. It's certainly something that could be useful for printer detection. >> Right now we don't have any actual printer emulation in our test suite. > > There's still no substitute for actual hardrware. I now have all of the > low-hanging stuff covered, but now it gets trickier.. Is there any way you could have your test capture the output from the backend and compute a SHA on it, so you could use it as a regression test? I understand it might not only be a byte stream that matters, but if you can find a way of encoding anything other than bytes into that stream, that would serve as well. Basically the same kind of thing run-testpattern-2 does for checksum generation. I could move compress-checksums and compress-checksums to some place more generic than src/testpattern. -- Robert Krawitz <rl...@al...> *** MIT Engineers A Proud Tradition http://mitathletics.com *** Member of the League for Programming Freedom -- http://ProgFree.org Project lead for Gutenprint -- http://gimp-print.sourceforge.net "Linux doesn't dictate how I work, I dictate how Linux works." --Eric Crampton |
From: Solomon P. <pi...@sh...> - 2019-05-24 14:54:03
Attachments:
signature.asc
|
On Sun, May 19, 2019 at 10:16:04PM -0400, Robert Krawitz wrote: > So one question, is there something useful that can be done with a > smaller test space (e. g. not the full N-dimensional space, just > varying one parameter at a time)? Yeah, there's no need to exhaustively test the N-dimensional space. I rarely bother test anything beyond resolution, print size, and (sometimes) lamination. Once I add support for "expected failures" that will expand a little bit. > That would help a lot. I changed it to only test 1/1, 1/3, 3/1, and 3/3, and even with recent printer additions the test time is down to about 7.5 minutes. > Is there any way you could have your test capture the output from the > backend and compute a SHA on it, so you could use it as a regression > test? I understand it might not only be a byte stream that matters, > but if you can find a way of encoding anything other than bytes into > that stream, that would serve as well. Basically the same kind of > thing run-testpattern-2 does for checksum generation. I could move > compress-checksums and compress-checksums to some place more generic > than src/testpattern. The backend doesn't emit any printer data streams in these debug/test modes, and I don't consider the backend's informative logging output (other than CUPS attribute messages) to be a stable interface. I have plans to change that (and also do things like emit JSON to make UI wrappers around the status inquires easier to work with) but that's pretty far down the to-do list.. - Solomon -- Solomon Peachy pizza at shaftnet dot org Coconut Creek, FL ^^ (email/xmpp) ^^ Quidquid latine dictum sit, altum videtur. |
From: Robert K. <rl...@al...> - 2019-05-24 23:26:26
|
On Fri, 24 May 2019 10:53:52 -0400, Solomon Peachy wrote: > On Sun, May 19, 2019 at 10:16:04PM -0400, Robert Krawitz wrote: >> So one question, is there something useful that can be done with a >> smaller test space (e. g. not the full N-dimensional space, just >> varying one parameter at a time)? > > Yeah, there's no need to exhaustively test the N-dimensional space. I > rarely bother test anything beyond resolution, print size, and > (sometimes) lamination. Once I add support for "expected failures" that > will expand a little bit. Sounds good. >> That would help a lot. > > I changed it to only test 1/1, 1/3, 3/1, and 3/3, and even with recent > printer additions the test time is down to about 7.5 minutes. That's a lot better. Can your test use the rotor setup (see the CUPS and run-testpattern-2 tests to see how that works), so jobs can be farmed off to different machines? Currently it's split 5 ways. >> Is there any way you could have your test capture the output from the >> backend and compute a SHA on it, so you could use it as a regression >> test? I understand it might not only be a byte stream that matters, >> but if you can find a way of encoding anything other than bytes into >> that stream, that would serve as well. Basically the same kind of >> thing run-testpattern-2 does for checksum generation. I could move >> compress-checksums and compress-checksums to some place more generic >> than src/testpattern. > > The backend doesn't emit any printer data streams in these debug/test > modes, and I don't consider the backend's informative logging output > (other than CUPS attribute messages) to be a stable interface. I have > plans to change that (and also do things like emit JSON to make UI > wrappers around the status inquires easier to work with) but that's > pretty far down the to-do list.. Would it be useful to have an option emit printer data streams, or are those simply the unprocessed data from the driver with some out of band protocol that can't be captured as a data stream? That would allow saving checksums from these tests like we do from the run-testpattern-2 tests. -- Robert Krawitz <rl...@al...> *** MIT Engineers A Proud Tradition http://mitathletics.com *** Member of the League for Programming Freedom -- http://ProgFree.org Project lead for Gutenprint -- http://gimp-print.sourceforge.net "Linux doesn't dictate how I work, I dictate how Linux works." --Eric Crampton |
From: Solomon P. <pi...@sh...> - 2019-06-02 02:13:49
Attachments:
signature.asc
|
On Fri, May 24, 2019 at 07:26:13PM -0400, Robert Krawitz wrote: > That's a lot better. Can your test use the rotor setup (see the CUPS > and run-testpattern-2 tests to see how that works), so jobs can be > farmed off to different machines? Currently it's split 5 ways. The tests implement a similar mechanism but the script does its own forking. I'll see about pulling that out so a specific rotor number can be invoked arbitrarily.. > Would it be useful to have an option emit printer data streams, or are > those simply the unprocessed data from the driver with some out of > band protocol that can't be captured as a data stream? That would > allow saving checksums from these tests like we do from the > run-testpattern-2 tests. I don't think so; most of the time the data going out is substantially similar to what goes in, but the backend has to do a bunch of interactive stuff to make sure it's okay to send the raster data and handle errors that inevitably pop up. That said, emulating the printer would be substantially usful, but it's really hard to justify that level of effort. :) - Solomon -- Solomon Peachy pizza at shaftnet dot org Coconut Creek, FL ^^ (email/xmpp) ^^ Quidquid latine dictum sit, altum videtur. |
From: Robert K. <rl...@al...> - 2019-06-02 21:18:19
|
On Sat, 1 Jun 2019 22:13:40 -0400, Solomon Peachy wrote: > On Fri, May 24, 2019 at 07:26:13PM -0400, Robert Krawitz wrote: >> That's a lot better. Can your test use the rotor setup (see the CUPS >> and run-testpattern-2 tests to see how that works), so jobs can be >> farmed off to different machines? Currently it's split 5 ways. > > The tests implement a similar mechanism but the script does its own > forking. I'll see about pulling that out so a specific rotor number can > be invoked arbitrarily.. As long as it uses the same environment variable interface (STP_PARALLEL, STP_TEST_ROTOR, STP_TEST_ROTOR_CIRCUMFERENCE), the implementation doesn't matter. It should also handle STP_TEST_PROFILE, which is full, fast, valgrind, checksums, minimal, valgrind_minimal, and valgrind_fast. Any profile that doesn't make sense (e. g. checksums) you should return status 77 from your script (that's what the GNU test framework uses to mean skipped). Figure on the minimal suites shouldn't take more than 1-2 minutes of CPU time, the fast ones should be in the 5 minute range, and full can be whatever you want (within reason). >> Would it be useful to have an option emit printer data streams, or >> are those simply the unprocessed data from the driver with some out >> of band protocol that can't be captured as a data stream? That >> would allow saving checksums from these tests like we do from the >> run-testpattern-2 tests. > > I don't think so; most of the time the data going out is > substantially similar to what goes in, but the backend has to do a > bunch of interactive stuff to make sure it's okay to send the raster > data and handle errors that inevitably pop up. Yes, that makes sense... > That said, emulating the printer would be substantially usful, but it's > really hard to justify that level of effort. :) That would indeed be a lot of effort. -- Robert Krawitz <rl...@al...> *** MIT Engineers A Proud Tradition http://mitathletics.com *** Member of the League for Programming Freedom -- http://ProgFree.org Project lead for Gutenprint -- http://gimp-print.sourceforge.net "Linux doesn't dictate how I work, I dictate how Linux works." --Eric Crampton |