>#!perl >open(GIF, ":Images:picture.gif") || die "oops!\n"; > >print "HTTP/1.0 200 OK"; >print "Server: MacHTTP"; >print "MIME-Version: 1.0"; >print "Content-type: image/gif"; > >while ($image=<GIF>) { > print $image; >} >close(GIF); First of all, CGIs don't send the HTTP status in return; you start with the headers and trust the server to send the 200. I don't know about those "Server:" and "MIME-Version:" headers -- it might work with them, but they're unnecessary. Here's the actual code I use; it assumes that $ct has been set up with the content type of the data, which in this case would be "image/gif", and that $fpath contains the pathname of the file, which in your case would be ":Images:picture.gif" (I guess). sub print_file_raw { if (-r $fpath and $size = -s $fpath) { # # shove another header in there, just for fun - # some browsers make use of this # $ct .= "\nContent-length: $size"; } if (!open(FPATH, $fpath)) { &print_file_cant_open(); } else { &print_content_type($ct); while (read(FPATH, $buffer, 16384)) { print $buffer; } } } sub print_content_type { local($ct) = @_; $ct = "text/html" if !$ct; print "Content-type: $ct\n\n"; } Reading to and printing from a buffer of fixed size will give more predictable results than "while ($image=<GIF>)". If you do it that way, and you're reading a GIF that happens to have a huge chunk of bytes between a pair of carriage returns, it may try to read more into RAM than you want (could abort the script if you run out of RAM). Or, if your GIF happens to have ten thousand carriage returns in a row, it will run significantly less efficiently. -- Jamie McCarthy http://www.absence.prismatix.com/jamie/ jamie@voyager.net Co-Webmaster of http://www.nizkor.org/ Hate mail will be posted.