gav's Journal gav's use Perl Journal en-us use Perl; is Copyright 1998-2006, Chris Nandor. Stories, comments, journals, and other submissions posted on use Perl; are Copyright their respective owners. 2012-01-25T02:00:47+00:00 pudge Technology hourly 1 1970-01-01T00:00+00:00 gav's Journal On misunderstanding copyright <blockquote><div><p> <i> I&#8217;m serious. Anyone who cherry-picks content from this piece without playing by the rule above can count on hearing from an attorney PDQ.</i></p></div> </blockquote><p>-- <a href="">Tim Bray "On OOXML"</a></p><p>Consider this cherry-picking to discuss ludicrous copyright warnings against using excerpts. Tim please set your lawyers on me, this week is pretty boring.</p><p>Also see <a href="">Fair Use.</a></p> gav 2008-04-02T14:08:49+00:00 journal Winter Scripting Games II <p>So far not doing great, missed the deadline of the 2nd and 4th events and was too hasty in submitting the 3rd to get it right.</p><p>This quiz is a good feeler for what Perl other people write, and if you look at the difference from the <a href="">official solution</a>, <a href="">Jan Dubois'</a>, and mine, there's a world of difference looking at Perl written by a non-Perl programmer. It's strange and alien looking to me, and explains why people harp on about Perl's ugliness.</p><p>Anyway here is my (fixed) solution to <a href="">Advanced Event 3: Instant (Runoff) Winner</a>: </p><blockquote><div><p> <tt>#!usr/bin/perl -w<br>use strict;<br>use List::Util qw( sum first );<br> <br>my %C; # candidates<br> <br>open my $in, '&lt;c:/scripts/votes.txt'<br>&nbsp; &nbsp; or die "Can't open 'votes.txt': $!\n";<br> <br>while (1) {<br>&nbsp; &nbsp; while (&lt;$in&gt;) {<br>&nbsp; &nbsp; &nbsp; &nbsp; chomp;<br>&nbsp; &nbsp; &nbsp; &nbsp; my @vote = split<nobr> <wbr></nobr>/,/;<br>&nbsp; &nbsp; &nbsp; &nbsp; @C{@vote} = (0) x @vote if keys %C == 0;<br>&nbsp; &nbsp; &nbsp; &nbsp; $C{$_}++ for first { exists $C{$_} } @vote;<br>&nbsp; &nbsp; }<br>&nbsp; &nbsp; my @c = sort { $C{$b} &lt;=&gt; $C{$a} } keys %C;<br>&nbsp; &nbsp; my $percent = ($C{$c[0]} / (sum values %C)) * 100;<br>&nbsp; &nbsp; if ($percent &gt; 50) {<br>&nbsp; &nbsp; &nbsp; &nbsp; printf "The winner is %s with %.2f%% of the vote.\n", $c[0], $percent;<br>&nbsp; &nbsp; &nbsp; &nbsp; last;<br>&nbsp; &nbsp; }<br>&nbsp; &nbsp; delete $C{pop @c};<br>&nbsp; &nbsp; @C{@c} = (0) x @c;<br>&nbsp; &nbsp; seek $in, 0, 0;<br>}<br> <br>close $in;</tt></p></div> </blockquote> gav 2008-02-23T01:11:07+00:00 journal Winter Scripting Games <p> I decided to enter the <a href="">2008 Winter Scripting Games</a>. I managed to get full points for the first event but screwed up and didn't notice the deadline for the second. <br> Here's my entry for the first. It seems a bit simpler than <a href="">Jan's solution</a> and avoids an eval. I'll try to do a better job with commentary for the latter events. </p><blockquote><div><p> <tt>#!/usr/bin/perl -w<br>use strict;<br> <br>print "Enter phone number: ";<br>chomp(my $phone = &lt;STDIN&gt;);<br> <br>my %letters;<br>{<br>&nbsp; &nbsp; my @L = ('A'<nobr> <wbr></nobr>.. 'P', 'R'<nobr> <wbr></nobr>.. 'Y');<br>&nbsp; &nbsp; my $n = 2;<br>&nbsp; &nbsp; while (my @t = splice @L, 0 =&gt; 3) {<br>&nbsp; &nbsp; &nbsp; &nbsp; $letters{$n++} = sprintf '[%s%s%s]', @t;<br>&nbsp; &nbsp; }<br>}<br> <br>my $match = '^' . join('', map $letters{$_}, split<nobr> <wbr></nobr>//, $phone) . '$';<br> <br>open my $in, '&lt;c:/Scripts/WordList.txt'<br>&nbsp; &nbsp; or die "Can't read wordlist: $!\n";<br>while (&lt;$in&gt;) {<br>&nbsp; &nbsp; tr/a-z/A-Z/;<br>&nbsp; &nbsp; if (/$match/o) { print; last }<br>}<br>close $in;</tt></p></div> </blockquote> gav 2008-02-22T01:12:34+00:00 journal Customer Service In my inbox today:<blockquote><div><p> <i>Dear TiVo&reg; Customer, <br>Yikes! We hate to be the bearer of bad news, but...</i></p></div> </blockquote><p> It seems that when I moved in the beginning of December 2005 instead of canceling my TiVo service I got a 3 month credit and ended up paying the $12.95 until May 2007 when I happened to cancel the credit card they used for automatic billing. <br> <br> Now I owe them $12.95 for the month that they couldn't bill, even though they admit that my TiVo unit didn't transmit anything to them after November 24th 2005. As TiVo are all about customer service they gave me two options-- pay or go to collections. They are unable to issue a credit for the month that they were unable to charge me for the service I wasn't using. <br> <br> I guess I'll have fun fighting the collections agency.</p> gav 2007-10-27T00:58:42+00:00 journal Wireless Extender <p>File under things-that-surprise-by-actually-working.</p><p> In my basement apartment I was unable to make cell calls with AT&amp;T. Most of the time I'd just get "call failed" and it wouldn't go through. </p><p> The <a href="">zBoost</a> actually works. I attached the antenna on the outside of the building and ran the cable to the unit inside. I now get full bars and perfect phone calls. Can be picked up for around $230, not inexpensive but preferable to paying for a landline. </p> gav 2007-10-19T23:07:55+00:00 journal Programming Erlang <p>I thought it would be a nice break from the Perl and C# programming I do at work to learn something new, and the Erlang seemed an interesting choice. I picked up Programming Erlang*.</p><p>It's a quiet Sunday, and I can learn about Erlang in lieu of chores. The problem is that this book is just boring. I've got through the first 150 of 383 pages (excluding appendices) and the author hasn't demonstrated a single program. The point of an introductory programming book is to evangelize and make your reader excited about solving their programming problems with this new language.</p><p>One of the things I don't think many people appreciate is just how good the llama and camel books are at getting people into Perl. I've read a lot of books about programming languages, and very few have sparked such motivation to want to learn more and start writing programs.</p><p>*</p> gav 2007-08-05T21:44:19+00:00 journal Interesting spam tricks Using Google's "I'm Feeling Lucky" feature to make links that take you straight to their website without having to include their url in the spam. gav 2007-07-14T17:40:15+00:00 journal Business Activity Monitoring <p>Business Activity Monitoring is a fancy term for writing rules that look for problems in the systems that run your business and sending people messages messages to (hopefully) fix them.</p><p>It's something I've been working on for the last year, and it's something that's helped fix a lot of things that we were making mistakes with in the day-to-day running of the business. We have a system that runs a SQL query, takes out any rows it's seen before, then if there's any left feed it through a template and send an email.</p><p>It's allowed me to take every mistake that we've found in the way that we entered data into the system, or processes, and turn it into a rule that will catch it next time. This is something I've always been keen on, every time you catch a mistake it's a positive thing. Computers can have fun checking thousands of updated records every day, and humans can do more useful things with their time, or more importantly, grow the business without having to hire more people.</p><p>It's interesting to me as I never really worked with business systems before. Almost all my previous focus was in a very narrow part of the business, trying to make websites that made it easy for customers to place orders, and then giving that order to somebody and letting it be their problem. Now I get to think about purchase orders, tracking numbers, inventory counts, profit margins, etc, things that were somebody else's problem.</p><p>One of the things I've learnt about reporting is that if you can't be lazy then there isn't any point. Our accounting package has a whole bunch of useful reports, but they rely on somebody running them. Most of the checks I've added could have written a simple script to handle, but I can just copy and paste a SQL query into the tools GUI, check off the recipients, pick a schedule from a drop down list, and be done in a couple of minutes.</p> gav 2007-06-28T04:51:31+00:00 journal Square wheels <p>Why would somebody implement a database in a database? I've seen this anti-pattern a few times before and it's confusing to me, when do you wake up and say "obviously the answer is to put the data a database, but let's invent our own abstraction".</p><p>Instead of nice code that looks like:</p><p>SELECT some_value FROM some_table WHERE some_other_value = 'something';</p><p>Figure out how many SQL statements your poor database will have to execute if the schema looks like:</p><p>object(object_id, object_type)<br>object_type_to_attribute(object_type, attribute_id)<br>attribute(attribute_id, attribute_name, data_type)<br>table_bit(object_id, attribute_id, value)<br>table_string(object_id, attribute_id, value)<br>table_number(object_id, attribute_id, value)<br>table_text(object_id, attribute_id, value)</p> gav 2007-06-19T02:33:50+00:00 journal Signal to Noise <p>Email used to be a simple text* channel for the exchange of ideas. Of course, over time, like many channels the signal to noise ratio approaches zero.</p><p>I think the email I just received is a sign of impending doom. A simple message, just three short paragraphs containing 201 words. Unfortunately the alternative HTML rendering with leaf motif stationary increased this 1,222 byte message to 33,547-- 96.35% of the message was fluff.</p><p>* I remember the joys of UUDECODE.</p> gav 2007-06-16T02:36:58+00:00 journal Back from the dead! <p>So it's been six months without a journal entry, it doesn't seem like that long.</p><p>I've been suffering from a career crisis, one so bad I was too busy to notice it. Now that I've made the decision to seek employment elsewhere, I feel more positive about being a Perl programmer again. I just have to solve the minor problem of finding that 'elsewhere'.</p> gav 2005-07-12T19:41:39+00:00 journal Hackers &amp; Painters <p>I'd been flicking through Paul Graham's <a href="">Hackers &amp; Painters</a> over the weekend when I came to this paragraph:</p><blockquote><div><p> <i>Not every kind of hard is good. There is good pain and bad pain. You want the kind of pain you get from going running, not the kind you get from stepping on a nail. A difficult problem could be good for a designer, but a fickle client or unreliable materials would not be.</i></p></div> </blockquote><p>Strangely enough I was trying to explain the same thing last week.</p> gav 2005-01-18T15:07:33+00:00 journal rsync include/exclude <p>I'm a big fan of <a href="">rsync</a> and use it on a daily basis, but I just can't get my head around the include/exclude syntax. It seems to work in an opposite way to how I think. For example, to recursively find all files beginning with 'foo':</p><p> <tt>find . -name 'foo*'</tt> </p><p>But with rsync you can't really tell it what you want, just what you don't want and end up with this madness:</p><p> <tt>rsync --include='*/' --include='foo*' --exclude='*' .<nobr> <wbr></nobr>/dest</tt> </p> gav 2005-01-05T22:39:53+00:00 journal i don't trust you <p> <a href="">i don't trust you</a> </p> gav 2004-12-28T18:13:02+00:00 journal Javascript woes <p>Given the following form:</p><blockquote><div><p> <tt>&lt;form id="test" action="" method="post"&gt;<br>&lt;input type="hidden" name="action" value="example"&gt;<br>&lt;/form&gt;</tt></p></div> </blockquote><p>What will <tt>document.forms.test.action</tt> and <tt>document.forms.test.getAttribute('action')</tt> be?</p><p>The former is the hidden field, the latter is the form's action.</p><p>The problem was that I couldn't change the HTML and had to pass the form to somebody else's javascript which assumed that <tt>form.action</tt> would work. After a bit of head scratching, I found the workaround isn't too painful:</p><blockquote><div><p> <tt>var form = new Object();<br>form.action = test.getAttribute('action');<br>some_function(form);<br>test.setAttribute('action', form.action);</tt></p></div> </blockquote> gav 2004-12-21T21:43:09+00:00 journal Paging Modules on CPAN <p>There's three modules I've used from CPAN for paging, <a href="">Data::Page</a>, <a href="">Data::Pageset</a>, and <a href="">Data::SpreadPagination</a> (the later two are subclasses of the first). One of the advantages of these modules is they work really well as something to pass into Template Toolkit.</p><p>These are three useful modules to add to your CPAN toolkit, each one does things a little differently and solves what at first looks like a simple problem, but saves you from both typing and embarassing off-by-one errors.</p><p> <b>Data::Page by Leon Brocard</b> </p><blockquote><div><p> <tt>my $page = Data::Page-&gt;new($total_entries, $entries_per_page, $current_page);<br> <br>[% FOREACH page IN [page.first_page<nobr> <wbr></nobr>.. page.last_page] %]<br>&nbsp; &nbsp; [% IF page == current_page %]<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;b&gt;[% page %]&lt;/b&gt;<br>&nbsp; &nbsp; [% ELSE %]<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;a href="?page=[% page %]"&gt;[% page %]&lt;/a&gt;<br>&nbsp; &nbsp; [% END %]<br>[% END %]</tt></p></div> </blockquote><p> With total_entries = 100, entries_per_page = 10, current_page = 3, you'll get something looking like: </p><blockquote><div><p>1 2 <b>3</b> 4 5 6 7 8 9 10</p></div> </blockquote><p> <b>Data::Pageset by Leo Lapworth</b> </p><p>One of the issues I ran into with Data::Page is that if you have a large number of pages then you have to somehow control the set of pages you output. Data::Pageset takes care of this for you. In this example <tt>$pages_per_set</tt> are the number of pages you want to display at any one time.</p><blockquote><div><p> <tt>my $pageset = Data::Pageset-&gt;new({<br>&nbsp; &nbsp; total_entries&nbsp; &nbsp; =&gt; $total_entries,<br>&nbsp; &nbsp; entries_per_page =&gt; $entries_per_page,<br>&nbsp; &nbsp; current_page&nbsp; &nbsp; &nbsp;=&gt; $current_page,<br>&nbsp; &nbsp; pages_per_set&nbsp; &nbsp; =&gt; $pages_per_set,<br>});<br> <br>[% IF pageset.previous_set %]<br>&nbsp; &nbsp; &lt;a href="?page=[% page.previous_page %]"&gt;&amp;lt;&lt;/a&gt;<br>[% END %]<br>[% FOREACH page IN pageset.pages_in_set %]<br>&nbsp; &nbsp; [% IF page == current_page %]<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;b&gt;[% page %]&lt;/b&gt;<br>&nbsp; &nbsp; [% ELSE %]<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;a href="?page=[% page %]"&gt;[% page %]&lt;/a&gt;<br>&nbsp; &nbsp; [% END %]<br>[% END %]<br>[% IF pageset.next_set %]<br>&nbsp; &nbsp; &lt;a href="?page=[% page.next_set %]"&gt;&amp;lt;&lt;/a&gt;<br>[% END %]</tt></p></div> </blockquote><p> With total_entries = 1000, entries_per_page = 10, current_page = 13, pages_per_set = 10, you'll get something looking like: </p><blockquote><div><p>&lt; 11 12 <b>13</b> 14 15 16 17 18 9 20 &gt;</p></div> </blockquote><p> <b>Data::SpreadPagination by Jody Belka</b> </p><p>This works in similar way to Data::Pageset, in that you're going to be showing a subset of the total number of pages. The difference is that Data::SpreadPagination allows you to output paging that allows the user to jump to the start and end of the results easily.</p><blockquote><div><p> <tt>my $pagination = Data::SpreadPagination-&gt;new({<br>&nbsp; &nbsp; totalEntries&nbsp; &nbsp; &nbsp;=&gt; 1000,<br>&nbsp; &nbsp; entriesPerPage&nbsp; &nbsp;=&gt; 10,<br>&nbsp; &nbsp; currentPage&nbsp; &nbsp; &nbsp; =&gt; 13,<br>&nbsp; &nbsp; maxPages&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;=&gt; 10<br>});<br> <br>[% FOREACH page IN pagination.pages_in_spread %]<br>&nbsp; &nbsp; [% IF !page.defined %]<br>&nbsp; &nbsp; &nbsp; &nbsp;<nobr> <wbr></nobr>...<br>&nbsp; &nbsp; [% ELSIF page == current_page %]<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;b&gt;[% page %]&lt;/b&gt;<br>&nbsp; &nbsp; [% ELSE %]<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;a href="?page=[% page %]"&gt;[% page %]&lt;/a&gt;<br>&nbsp; &nbsp; [% END %]<br>[% END %]</tt></p></div> </blockquote><p> With the same data as before, you'll end up with: </p><blockquote><div><p>1 2<nobr> <wbr></nobr>... 10 11 12 <b>13</b> 14 15 16<nobr> <wbr></nobr>... 99 100</p></div> </blockquote> gav 2004-12-13T23:03:26+00:00 journal Hash::AsObject <p>I've been finding more and more uses for <a href="">Hash::AsObject</a> recently, it's great when you need an object, but you don't really care what that object is. For example today I had to write some reporting code, and call some existing code that expected a "cart" object to be passed in. It did something like:</p><blockquote><div><p> <tt>foreach my $item ($cart-&gt;contents) {<br>&nbsp; &nbsp; printf "%d of %s for \$%.2f\n",<br>&nbsp; &nbsp; &nbsp; &nbsp; $item-&gt;qty, $item-&gt;code, $item-&gt;unit_price;<br>}</tt></p></div> </blockquote><p>The problem was that I didn't have a "cart" to pass it. One of the great things about dynamic languages is that it doesn't matter if it's a "cart" or not, just that it <i>looks</i> like a cart:</p><blockquote><div><p> <tt>my @cart = map {<br>&nbsp; &nbsp; $_-&gt;{unit_price} = defined $_-&gt;{sale_price}<br>&nbsp; &nbsp; &nbsp; &nbsp; ? $_-&gt;{sale_price} : $_-&gt;{price};<br>&nbsp; &nbsp; Hash::AsObject-&gt;new($_)<br>} @{ $dbh-&gt;selectall_arrayref(<br>&nbsp; &nbsp; &nbsp; &nbsp; 'SELECT qty, code, price, sale_price FROM cart',<br>&nbsp; &nbsp; &nbsp; &nbsp; { Slice =&gt; {} }<br>&nbsp; &nbsp; )};<br> <br>my $cart = Class::Object-&gt;new;<br>$cart-&gt;sub('contents', sub { return @cart });</tt></p></div> </blockquote><p> <a href="">Class::Object</a> is useful in this case to get around the issue of not being able to return a list.</p> gav 2004-12-08T19:36:23+00:00 journal scrolling in screen <p>I'm a big fan of <a href="">screen</a>, but something happened from version 3.09.11 (RH 7.3) to version 4.00.02 (FC3) that stopped putty being able to scroll the window. After some poking I found that adding:</p><blockquote><div><p> <tt>termcapinfo xterm* ti@:te@</tt></p></div> </blockquote><p>To my<nobr> <wbr></nobr><tt>.screenrc</tt> fixes the problem.</p> gav 2004-12-07T17:39:43+00:00 journal Domain Specific Testing <p> One of the things I do is implement tax and shipping rules for clients. As an interesting aside, we use a early web services technique invented at Yahoo! where we recieve a POST of all the data and return the results in custom headers. Anyway, as you can expect, it's often a challenge to capture the exact rules required and they tend to be pretty complicated. </p><p> I'd just been working on adding rules for AK and HI for a client that only previously shipped to the 48 contiguous states and the added complexity made me worried that I made a mistake. I wanted to write tests, but I wanted something at a higher level than just writing Perl code, and more importantly I wanted a test suite that the client can add to. I came up with the following format: </p><p> <tt> product: 9848rdc <br>quantity: 1 <br>options: Size: 148 <br>product: 9852rdc <br>quantity: 2 <br>options: Size: 152 <br>state: AK <br>country: US <br>method: Ground <br>cost: 24.95 <br>-- <br>product: bk7ma <br>product: 9848rdc <br>options: Size: 148 <br>state: AK <br>cost: 39.95 <br>method: Ground </tt> </p><p> I then wrote a script that reads this in, turns each section into three tests (using <tt>Test::More</tt>): we were able to POST, we didn't get back an error, and we got back the right cost. We've now got a format that allows non-programmers to write tests, and they're often the best people to write them because they know what they expect the system to do. </p> gav 2004-12-01T04:09:24+00:00 journal World of Warcraft (day 2) <p>I finally get to play and spend an enjoyable few hours getting the hang of things and getting my character to level 7. Then the difficulty changes from "I can kill most things without too much of a problem" to "I can't kill anything without a problem and if I meet two or more monsters I'm fucked".</p><p>Then I find out that while it may be cool that the world is huge but when I die and become a ghost and have to spend five minutes running back to my body to respawn with 50% health, and then die, then spend five minutes running back, that this isn't fun. Pairing up with a wizard I found that I can die just as quickly but only get half the XP for killing anything.</p> gav 2004-11-26T04:45:38+00:00 journal World of Warcraft (day 1) <p>I picked up the eagerly awaited MMORPG <a href="">World of Warcraft</a> up on the way home from work. Then I waited while 3.9 gigabytes of data was installed from four cds, I registered, found my wallet so I could sign up to pay $14.99 a month, downloaded* a 20 megabyte patch, waited for it to patch 11,393 files, created a character, and then found all the game servers down. I finally found a server to play on, got on it 3 minutes before it went down for "maintanance" and managed to kill a rat. So far I'm feeling pretty good about my $49.99 (+tax) investment.</p><p>* They use <a href="">Bittorrent</a> for downloading updates which is pretty neat.</p> gav 2004-11-25T04:24:16+00:00 journal Killer Mockingbirds <p> <a href="">How to Kill a Mockingbird</a>, the BESTEST flash ever!</p> gav 2004-11-25T00:11:59+00:00 journal Apache::Session::MySQL_Inno <p>I basically just ripped off Apache::Session::Store::Postgres and created <a href="">Apache::Session::MySQL_Inno</a> which should work better if you have InnoDB support. Any feedback would be appreciated, I'll send it off to <a href="">Casey</a> if everything seems to be working.</p> gav 2004-11-09T19:11:23+00:00 journal Covering all the bases <blockquote><div><p> <i>Ultimately, the TCO and ROI of Linux may <b>be less than, comparable to, or more expensive</b> than Unix or Windows depending on the individual corporate deployment circumstances</i></p></div> </blockquote><p>-- <a href="">The Yankee Group</a> </p><p>I wonder how long it took for them to discover that?</p> gav 2004-11-04T22:36:20+00:00 journal CHECK ENGINE SOON <p>My car was at the garage today for attempt three at making the dastardly <b>CHECK ENGINE SOON</b> light stay off. It stayed off until I was a few minutes down the road. After some more digging into what the code could mean, something about a fan in the exhaust system, they think they might be able to fix it permenantly this next Friday. This little orange light has greeted me everytime I've started my car for nearly six months now, and it's getting on my nerves. So far it's cost me over $200 and three wasted trips to two different garages (the first garage gave up on it), thank you Ford for your wonderful engineering.</p> gav 2004-11-04T01:04:50+00:00 journal Advert <p>A pleasant <a href="">little advert</a> (safe for work).</p> gav 2004-10-27T03:04:43+00:00 journal Netflix++ <p>When was the last time you got an email to let you know that something is getting <b>cheaper</b>?</p><blockquote><div><p> <i>Since our price increase in June, some of our members have expressed concerns about the new pricing. We've listened to this feedback and are pleased to inform you that we're lowering the price of your Netflix 3-at-a-time program from $21.99 per month to 17.99&#160;per month.</i></p></div> </blockquote><p>Thank you for the $48 a year saving, <a href="">NetFlix</a>! </p> gav 2004-10-24T21:34:17+00:00 journal America, fuck yeah! <p> <b>For graphic crude and sexual behavior, violent images and strong language - all involving puppets.</b> </p><blockquote><div><p> <i>America. America. America Fuck Yeah. <br>Comin' again to save the motherfucking day, yeah. <br>America, fuck yeah!</i></p></div> </blockquote><p>I just got back from seeing Trey and Matt's <a href="">Team America: World Police</a>. It's worth watching just for the puppet sex.</p> gav 2004-10-24T04:13:55+00:00 journal explodingdog <p>My <a href="">explodingdog</a> prints came the other day; they are now framed and on the wall at work.</p><p>[ <a href="">image</a> ]</p> gav 2004-10-19T02:02:16+00:00 journal Regexps for reverse engineering? <p>I got roped into trying to help a friend of a friend extract some reports from their billing application data. I got given a 26 meg data file to play with, and some digging with 'strings' helped me find bits of data mixed in with the binary gibberish. I came up with the following code:</p><blockquote><div><p> <tt>my $person&nbsp; = qr/[\x08\x09]([A-Z]{8})/;<br>my $lo&nbsp; &nbsp; &nbsp; = qr/[\x00-x20]{1,3}/;<br>my $id&nbsp; &nbsp; &nbsp; = qr/OH${lo}7${lo}(\d{5})${lo}AA/;<br>my $date&nbsp; &nbsp; = qr/\x06(\d\d)(\d\d)(\d\d)/;<br>my $time&nbsp; &nbsp; = qr/\x08(\d\d):(\d\d):(\d\d)/;<br>my $money&nbsp; &nbsp;= qr/([1-9]\d+\.\d\d)/;<br>my $chars&nbsp; &nbsp;= qr/[\x20-\x7E]+?/;<br>my $desc&nbsp; &nbsp; = qr/\x17($chars)\x00/;<br> <br>while ($data =~<nobr> <wbr></nobr>/$person.*?$id.*?$date.*?$desc.*?$time.*?$time.*?$money/gs) {<br>&nbsp; &nbsp; my ($p_id, $r_id, $d, $text, $t1, $t2, $cost)<br>&nbsp; &nbsp; &nbsp; &nbsp; = ($1, $2, "$3/$4/$5", $6, "$7:$8:$9", "$10:$11:$12", $13);<br>&nbsp; &nbsp; print "$p_id: $r_id -- $d $t1 -&gt; $t2 -- [$text] \$$cost\n";<br>}</tt></p></div> </blockquote><p>I was happy, I'd got back a bunch of records that looked sensible. The problem was that I wasn't getting some of the records that I saw in the file. Changing my $person to qr/[\x08\x09](G[A-Z]{7})/ or qr/[\x08\x09](S[A-Z]{7})/ gives me a bunch of different records. But why would (S[A-Z]{7}) give different results than ([A-Z]{8})?. I'm stumped.</p> gav 2004-10-16T18:33:09+00:00 journal