Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Files, unpack and text values

by BBQ (Curate)
on Jun 25, 2000 at 04:01 UTC ( #19735=perlquestion: print w/replies, xml ) Need Help??

BBQ has asked for the wisdom of the Perl Monks concerning the following question:

I am trying to build a "self sufficient" perl script that will run from generic webservers. The idea is to make installation as easy as humanly possible by placing the file in the cgi directory and chmoding it, period. Since the script uses a few images, I decided to "embed" them into the script itself, and this is where I'm having problems...

I have never dealt directly with binary data, so I am not absolutely sure that I know what I'm doing most of the time, please bear that in mind. After doing some reading, it seemed to me that the easiest way to embed these images into the script itself would be to produce text versions of them by using unpack, assigning the text to variables and then printing them back with pack, when requested by a query string (ie: foo.pl?img=bar). If anyone has a better idea, please feel free to speak up.

The "trouble" I'm having is when I unpack the image file. According to perlfunc, unpack should return an list, and all I'm getting is a variable.
Unpack() does the reverse of pack(): it takes a string representing a +structure and expands it out into a list value, returning the array value. (In scala +r context, it returns merely the first value produced.) The TEMPLATE has the same fo +rmat as in the pack() function. (...)
So this is (a snippet) what I've been up to:
open(IMG,"foo.gif"); foreach (<IMG>) { # something wrong here I suppose @hex = unpack("H*",$_); foreach $b (@hex) { # this array on has one item print "$b "; # which gets printed here } # shouldn't it be a list? print "\n"; } close(IMG);
The examples from perlfunc didn't help much. Anyone have any ideas of what I'm doing wrong? Or maybe, I'm not doing anything wrong per say, but the result isn't quite what I had expected. I was expecting a list of paired hex values, and I got a few BIG hexed strings instead.

Thank you again my perl brethren.

Replies are listed 'Best First'.
RE: Files, unpack and text values
by httptech (Chaplain) on Jun 25, 2000 at 04:51 UTC
    I would use uuencode/uudecode. Don't forget the single-quotes around the here-doc label, or else it will try to interpolate variables in the uuencoding.
    #!/usr/bin/perl print "Content-type: image/gif\n\n"; print uudecode(<<'END_OF_GIF'); begin 664 test.gif M1TE&.#EA*0`4`.<``/______S/__F?__9O__,___`/_,___,S/_,F?_,9O_, M,__,`/^9__^9S/^9F?^99O^9,_^9`/]F__]FS/]FF?]F9O]F,_]F`/\S__\S MS/\SF?\S9O\S,_\S`/\`__\`S/\`F?\`9O\`,_\``,S__\S_S,S_F<S_9LS_ M,\S_`,S,_\S,S,S,F<S,9LS,,\S,`,R9_\R9S,R9F<R99LR9,\R9`,QF_\QF MS,QFF<QF9LQF,\QF`,PS_\PSS,PSF<PS9LPS,\PS`,P`_\P`S,P`F<P`9LP` M,\P``)G__YG_S)G_F9G_9IG_,YG_`)G,_YG,S)G,F9G,9IG,,YG,`)F9_YF9 MS)F9F9F99IF9,YF9`)EF_YEFS)EFF9EF9IEF,YEF`)DS_YDSS)DSF9DS9IDS M,YDS`)D`_YD`S)D`F9D`9ID`,YD``&;__V;_S&;_F6;_9F;_,V;_`&;,_V;, MS&;,F6;,9F;,,V;,`&:9_V:9S&:9F6:99F:9,V:9`&9F_V9FS&9FF69F9F9F M,V9F`&8S_V8SS&8SF68S9F8S,V8S`&8`_V8`S&8`F68`9F8`,V8``#/__S/_ MS#/_F3/_9C/_,S/_`#/,_S/,S#/,F3/,9C/,,S/,`#.9_S.9S#.9F3.99C.9 M,S.9`#-F_S-FS#-FF3-F9C-F,S-F`#,S_S,SS#,SF3,S9C,S,S,S`#,`_S,` MS#,`F3,`9C,`,S,```#__P#_S`#_F0#_9@#_,P#_``#,_P#,S`#,F0#,9@#, M,P#,``"9_P"9S`"9F0"99@"9,P"9``!F_P!FS`!FF0!F9@!F,P!F```S_P`S MS``SF0`S9@`S,P`S````_P``S```F0``9@``,P```/__________________ M____________________________________________________________ M____________________________________________________________ M_____________________R'^#DUA9&4@=VET:"!'24U0`"'Y!`$*``$`+``` M```I`!0`0`B9`*\)'$BPH,&#"`T"6,BPX<)K``A&A$AQHL.+`B]JW,B18\:" M%CN*;$8M`00(%B"$%,FRI46$+C>6/&G!`K67'U?&;$BM9P(%"6YF9`FQ(<6A M+9%Z'!@1YT.=#I7NG/JP*-6K4J]J;/:3IE"K6F665'!2`52-3"L6S0D68]NP =2=^Z33NQ8MRD3=D.M8M6+MR.6?\"3DBX<.&``#L` ` end END_OF_GIF # uudecode subroutine shamelessly copied from elsewhere on the net sub uudecode { my $in = shift; my $out = ""; foreach( split(/\n/,$in) ) { $_ .= "\n"; last if /^end/; next if /[a-z]/; next unless int((((ord() - 32) & 077) + 2) / 3) == int(length( +) / 4); $out .= unpack("u",$_); } return $out; }
      Man, that looks like a great way to put it! BUT, since I'm a very stuborn person, I'm going to keep trying to learn what the hell unpack is all about before I finaly give up and cut/paste your suggestion (which I more or less understand). Is there any particular reason why you suggested uuencode/uudecode over pack/unpack, or would you just prefer to do it that way?

      #!/home/bbq/bin/perl
      # Trust no1!
        Simply because uuencode is available as a unix command-line tool, so creating the uuencoded GIF would not require me to write any code.
Re: Files
by davorg (Chancellor) on Jun 25, 2000 at 13:15 UTC
      I can see clearly what the problem with my unpack template is now. Its not unpack at all that's giving me the trouble, its just that I don't fully understand what to expect from a binary file! As you pointed out I only have one element in my template, but how many should I have? I can't just guess a number I want and force the result back, or can I?

      I've done some experimenting with unpack to try and obtain the same results I got from opening the file in a hex editor (UltraEdit). The file contents (accodring to the editor) should be something like this:
      47 49 46 38 39 61 0f 00 10 00 a2 ff 00 84 86 84 df e0 df c6 c7 c6 c0 c0 c0 00 00 00 00 00 00 00 00 00 00 00 00 21 f9 04 01 00 00 03 00 2c 00 00 00 00 0f 00 10 00 40 03 40 48 ba ac f3 60 80 49 a9 88 24 ca 0a ae 78 d9 b3 75 a4 16 4a 92 47 0e 01 6a 8d de 10 72 1c 48 df c0 29 ee 52 8b 0a c0 e0 67 17 12 1a 81 32 cd 67 29 b4 ed 8c c4 c8 91 97 a4 5a 09 b2 86 56 91 00 00 3b
      and my script is giving me this output with a "H1024" template (line breaks I inserted myself just for the sake of comparing them both at the 16th position):
      4749463839610f001000a2ff00848684\n dfe0dfc6c7c6c0c0c000000000000000\n 000000000021f90401000003002c0000\n 00000f00100040034048baacf3608049\n a98824ca0a ae78d9b375a4164a92470e016a8dde10\n 721c48dfc029ee528b0a c0e0671712
      My unpack result seems to be munching the last line, insert line breaks in odd places, all of which I don't quite grasp... and by the way, you can find the image I'm using as an example here. I have tried using the 1st block of hexes with pack to see if I could get an image output, but that didn't help much either. So I have reached a dead end with the unpack method so far, and I refuse to beleive that this cannot be acheived with perl. Newly revised questions:
      • How can one offer unpack a template without knowing what to expect in return?
      • Is it correct to run a loop on the file handle in this case?
      • Why does my hex editor break the strings at every 16th position?
      • Should I run a break on the 16th position with substr, or similar?
      • Is there any documentation out there that covers this?
      Thank you again for your patience, attention, and coping with my stuborness!
        Answers to questions:
        • If you don't know what to expect in return, unpack's not the right function for the job.
        • No. A file (whether binary or text) is just a stream of bytes. Text files are said to be stored line-by-line because, in certain places, they have specific characters. $/ is also known as the Input Record Separator because it contains that special character -- usually a newline. The I/O routines read a chunk of bytes from a file and split it up based on the presence of whatever's in $/. That happens to default to \n. In a binary file, there are no lines.
        • It's a nice power of two, and it fits nicely across the screen. No real technical reason of which I'm aware.
        • Nope.
        • Low-level I/O handling routine documentation, probably.
        i believe that a single element consisting of 'H*' is the template you are interested in.

        i'm not sure if it is correct to loop on a binary file handle like that, but it certainly doesn't make much sense to me: "for each line in this binary file" is a contradiction in terms, since binary files don't have any "lines".

        i don't know why your hex editor breaks strings at that interval, but it seems a little narrow for you to store it that way. i would use 36 bytes per line with no spaces, myself.

        the following code works fine for me. however, i would probably use MIME::Base64 to encode the data if it were my choice. plain hex is just a little too fat for my taste.

        open IMG, "foo.gif" or die "Couldn't open image: $!\n"; undef $/; $image = <IMG>; print unpack("H*", $image); close IMG;
Re: Files
by t0mas (Priest) on Jun 25, 2000 at 12:51 UTC
    Depending of the nature of the images, one idea is to use GD or ImageMagick to print a "reverse-enginered" image. If the images is of many different types, this is probably not a good method, but for a number of button images that differs only textwise it might be.
    Otherwise the uuencode thingy above seems nice.


    /brother t0mas
Re: Files
by Anonymous Monk on Jun 26, 2000 at 07:25 UTC
    I'd agree with MIME::Base64 .. i use it extensively to store subroutines that are grabbed on the fly from a SQL database, decode, eval into the running script, so that any number of scripts across can use the same code without me having to update each script manually. It would work well for your application.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://19735]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (3)
As of 2023-09-21 12:09 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?