http://www.perlmonks.org?node_id=529931

This about a general principle and a specific instance where the principle was ignored but perl saved the day.

The principle is that user data is precious and should be respected and preserved. This idea is not new, in fact it's fairly obvious but almost certainly ignored by the application you're currently staring at.

The first (only?) time I saw it explicitly stated was in the book The Humane Interface (an excellent read, unfortunately the author died last year) and the reason I'm writing about it now is because I've just been bitten again so I want a rant (and nobody reads my blog)!

So, there I was typing a long post into a text box on a webiste not unlike Perlmonks. I was not too familiar with their system to I clicked on Preview, spotted a mistake and immediately clicked Back to fix it. Doh! Where's my posting? Click Forward and I get some message about resending data, OK go ahead. Nothing. My posting was gone. My 30 minutes of typing all for nothing. Arghhh! We wouldn't tolerate this kind of behaviour from the crappiest text editor, why do we tolerate it from web broswers?

I notice that blogger.com's post editor has an onclose action that warns you that you're about to throw away your edits but really it shouldn't be up to every website in the world to script this basic and obvious safety measure. The bug I filed against Firefox has sat untouched since I filed it.

Anyway, now the perl connection...

The last time this happened I was none too happy. I did the usual creative swearing and name calling and I said "that data's got to be sitting inside Firefox somewhere, there must be way to recover it through /proc". My colleague replied that he'd tried doing this before but it hadn't worked for him. Well, God loves a trier so we spent a little while looking at man pages and came up with this

#! /usr/bin/perl use strict; use warnings; use POSIX qw ( SIGCONT ); my $pid = shift; my $base = "/proc/$pid"; open(my $mem_fh, "$base/mem") || die "$base/mem: $!"; open(my $maps_fh, "$base/maps") || die "$base/maps: $!"; require 'syscall.ph'; my $result = syscall(&SYS_ptrace, 16, int($pid), 1, 0); if ($result == -1) { die "Error: $!\n"; } while (<$maps_fh>) { my ($start, $end) = map {hex($_)} /^(\w+)-(\w+)/; dump_range_seek($mem_fh, $start, $end - $start); } # wake the patient back up kill(SIGCONT(), $pid); sub dump_range_seek { my $fh = shift; my $start = shift; my $len = shift; my $seek = sysseek($fh, $start, 0); die "seek, $!" unless defined($seek); # warn "seek = $seek"; my $buf; while ($len) { my $chunk = $len > 4096 ? 4096 : $len; # warn "reading $chunk\n"; my $read = sysread($fh, $buf, $chunk); die "$!" unless defined($read); die "read 0" unless $read; $len -= $read; print $buf; } }

Save it as catmem.pl and then run catmem.pl $PID > some_file and it will dump out the entire memory image of that process. The it's just a matter of applying the usual unix tools like strings and grep to find what you're looking for. Of course this isn't guaranteed, it highly dependent on how the application in question stores your data etc etc but for Firefox at least it tends to find a fairly recent revision of what you've lost.

So if you work on a project that allows users to input anything longer than word or two, please show some respect and don't ever throw their data away. That doesn't mean you have to badger them with prompts every time they change screens, it could just be a matter of providing an undo function are maintaining a history of strings that have been typed but not saved.

End rant.

P.S. Yes, I typed his all in via a Firefox text box :)