2004-04-04-2309Z


Just finished another job at RAC. This one was in Perl; funny, all 4 jobs so far have been different languages! C, PHP, Java, and Perl.

This had some interesting problems, and my customer was very helpful; he told me about these two lines to use which give you PHP-style error messages in the page, even if the bug is a compile-time problem such as a missing module:

use CGI::Carp 'fatalsToBrowser';
use CGI ':standard';

As you may have guessed from the above paragraph, two of the modules I wanted were missing and the customer's host would not install them: Mail::POP3Client and Image::Size. So I had to code both the JPEG size finder and a pop3 client routine in low-level Perl. Yummy. Took me an extra day, but then the customer was nice enough to pay me double! No, I'm not telling you how much "double" is. Suffice it to say it's nothing like the $100/hour I normally charge. As I may have mentioned before, I'm bidding against guys from Eastern Europe, India, Africa, etc., places where a dollar still means something. Luckily, here in Baja a dollar is still pretty good too.

This was the second time I've written a POP3 state machine in low-level socket code. The first time was a few years back, at Dialtone, can't remember what for exactly but it had to do with my Rubegoldbergian monitoring or automation setups. I think it came out a little less klunky this time.

Another Windows server snag I didn't know about: it doesn't chdir you to the CGI script directory, you have to do that in the script. Bugger. The server didn't allow setpgrp(0, 0) either, so the parent process had to hang around until the child completed. Here's how I got around those two problems, not saying it's elegant nor the best way to do things:

    print "<!-- trying to setpgrp(0, 0)...\n";
    eval "setpgrp(0, 0)"; # make this a top-level process on its own
    print " $@ -->\n";

and

sub cdcorrect {
 if ($ENV{'SERVER_SOFTWARE'} =~ /IIS/) {
  chdir(File::Spec->catpath(
   (File::Spec->splitpath($ENV{'PATH_TRANSLATED'}))[0, 1], '')) or
  die "Cannot change to script directory: $!\n";
 }
}

Luckily they had the File::Spec module installed, that would have been a lot nastier than it already is.

Determining the size of a JPEG: I dumped a few of them, found the height and width at the weird offset of 0xa3 from the start of the file. I hardcoded that as the seek() value in the program. Splat! As soon as another, different image was downloaded the program broke. So I had to poke into it a little better. Without googling, I tried to figure it out myself, remembering that JPEG format follows the IFF spec to at least some degree. So the first count word (16 bits) should be, in big-endian byte order, at offset 4. I found out that by following those chunk sizes, and looking for a byte of 8 just past the size word, I could find the height and width (in that order) as the next two 16-bit values following the 8. Does that make any sense? Here's the routine:

sub imgsize {
 my($file, $type, $pointer, $chunk, $size, $width, $height);
 foreach $file (@_) {
  open(JPEG, $file) or die "cannot open: $!\n";
  binmode(JPEG); # necessary on Windows!
  $pointer = 4; # location of first size word
  while (!eof(JPEG)) {
   seek(JPEG, $pointer, 0) or die "cannot seek: $!\n";
   read(JPEG, $chunk, 7) or die "cannot read: $!\n";
   ($size, $type, $height, $width) = unpack('nCnn', $chunk);
   if ($type == 8) {
    last;
   } else {
    # take into account the size of the size word itself, 2;
    $pointer += ($size + 2);
   }
  }
 }
 return ($width, $height);
}

All of this can be assumed to be in the public domain at this point. I'll never use them again myself, in all likelihood; I almost always rewrite stuff clean the next time I need something.

Well, I'm already getting the old 'boxed-in blues'; I'll probably go to San Diego tomorrow and get some stuff I need. Then 3 more weeks or so and it's back to Deming!

Back to blog or home page

last updated 2013-01-10 20:16:18. served from tektonic.jcomeau.com