Archive for the ‘Geekery’ Category

Windows Browser Ballot

Thursday, April 15th, 2010

Mildly controversially, MS have found themselves compelled to offer Windows users in the EU a 'browser ballot' screen in an effort to make IE a less default choice, which is fairly understandable (if perhaps not understandably fair).

But MS have decided for some reason that the best way to do this is is render it as a web page in IE8. So before making an unbiased decision with no leading questions, you've already had to configure IE8.

MS Windows Browser Chooser

I was going to post about the crapness of IE8's multiple configuration dialogues, but this is more amusing.

Windows’ find command

Tuesday, March 23rd, 2010

It would appear that, not-entirely-contrary to my common rant that Windows offers no text processing tools at all, Windows does offer a find command which is like a severely crippled grep.

It has five options:

U:\>find /?
Searches for a text string in a file or files.

FIND [/V] [/C] [/N] [/I] [/OFF[LINE]] "string" [[drive:][path]filename[ ...]]

  /V         Displays all lines NOT containing the specified string.
  /C         Displays only the count of lines containing the string.
  /N         Displays line numbers with the displayed lines.
  /I         Ignores the case of characters when searching for the string.
  /OFF[LINE] Do not skip files with offline attribute set.
  "string"   Specifies the text string to find.
  [drive:][path]filename
             Specifies a file or files to search.

If a path is not specified, FIND searches the text typed at the prompt
or piped from another command.

So it kind of does what I want about 70% of the time I use grep, and is probably a reasonable stand in afterwards. Certainly beats firing up notepad and ctrl-F ing.

Now to find an awk and a sed...

UI Fail: scanpst.exe’s incompatibility

Thursday, March 18th, 2010

Sometimes, on trying to scan a PST with MS Office's bundled scanpst.exe, you get the below error:

"An error has occurred which caused the scan to be stopped"

And a log that ends:

Fatal Error: 80040818

What MS meant to say was:

You're scanning an Office 2003 PST file with the scanpst tool that shipped with Office 2007. For some reason, we decided that while Outlook 2007 can cope with both, scanpst can't

In an attempt at usefulness:
On my WinXP/Office 2007 box, scanpst is at C:\Program Files (x86)\Microsoft Office\Office12\SCANPST.EXE and downloadable here.

On our Server03/Office03 box, it's at C:\Program Files\Common Files\System\MSMAPI\1033\SCANPST.EXE1 and downloadable here.

I've no idea if these downloads are of any real use. Try them and see.

  1. I'm told the '1033' pertains to geographic location, but I've no real idea. Browse if it's not there. []

Joining the Canonical =~ Microsoft fray

Tuesday, February 9th, 2010

I've had this knocking about for a while in various forms. Following TheOpenSourcerer's post, I figured I'd get it in while he's getting the flack.

About a year ago, I remember there being some rejoicing at the prospect of Canonical open-sourcing Launchpad, their bug/issue/ticket tracking web application. I also remember being a mite confused by it. Canonical is the company behind Ubuntu Linux, the popular open source operating system. Surely they, of all people, had opened the source from the start? What does it say when the company most loudly and successfully pushing open source as an efficient means of software development to your average computer user, develops its in-house software behind closed doors? And, accepting that, why is opening the source means for rejoicing? It is surely the belated Right Thing To Do. If anything, the response should have been along the lines of "Why so long?"

More recently, I decided that a hodge-podge of scripts to keep my files in sync between PCs wasn't a good idea, not least because it didn't actually work, and since my home PC and my laptop were both Ubuntu, and Ubuntu One seemed easy enough to install, that'd do the trick. So I installed it and started using it. Then I decided to get my work PC in on the game. And find this message:

Requirements: Because we want to give everyone using Ubuntu One the very best experience, we require that you run Ubuntu 9.04 (Jaunty Jackalope) or higher.

Which is something I don't think I've come across before - a Free Software company producing software and inventing restrictions. Why shouldn't Ubuntu One work on my Debian desktop?
This incompatibility for the sake of it is something I remember from Windows, and it's not a good memory. I know it's possible to write a client for it - the client is at least open source - but the message that I am required to use Ubuntu to use it? What good does that do anyone?

Most recently came the news that on the netbook edition Canonical have decided to drop OpenOffice.org (which *is* undeniably bloated) and use Google docs in its place. Google Docs is completely proprietary. It's about as closed source as software can get, since you can't even study its behavior, only those interfaces you're permitted with it.
Why wasn't AbiWord used, with it's online service, for example? Or a pared down OpenOffice, perhaps? Canonical has shown in the past that it has the developer hours to make fantastic, awesome, changes to software. Why not do that now?

Ubuntu is the most popular desktop Linux distro. I'm sure there are ways of counting such that Fedora wins, but if something's packaged for Linux, it's available in a Ubuntu-pointed deb. And so it occupies a unique position for free software - it's an opportunity to be a fantastic demonstration of what is possible with free software. It is possible to make commercial progress without restricting user freedom, and it is possible to make a wonderfully usable operating system under these conditions.

Except Ubuntu's not demonstrating that. It's showing that using a billionaire benefactor and a bunch of closed source software we can turn a free operating system into a mostly-freeish wonderful one.

And I'd rather like Canonical to stop doing that, and get back to making free software look good.

Splitting massive MySQL dumps

Wednesday, January 13th, 2010

As I posted yesterday, I have a massive MySQL dump to import. I tried BigDump, but one of the tables kept producing errors and so BigDump would exit. I don't need the whole db imported, so I wrote this to split it by table. It produces a new sql file for every table it finds, numbered sequentially so if you process them in alphabetical order it's the equivalent of the whole dump. USE statements get their own files in the same sequence.

#! /usr/bin/perl
 
use strict;
use warnings;
use 5.010;
 
my $dump_file = $ARGV[0];
&usage() if !$dump_file;
 
say "using ".$dump_file;
 
my ($line, $table,@query, $file_number,$file_name);
my $line_number = 1;
my $find_count = 0;
 
open(DUMP_IN, "< $dump_file");
        while(<DUMP_IN>){
                my $line = $_;
                if (/^USE\s.(\w+)./){
                        say "changing db: ".$1;
                        $file_name = &make_file_name("USE_$1", "$find_count");
                        &write_USE($file_name, $line);
                        $find_count++;
                }elsif (/^-- Table structure for table .(.+)./){
			## If the current line is the beginning of a table definition
			## and @query is defined, then @query must be full of the previous
			## table, so we want to process it now:
                        if (@query){
                        $file_name = &make_file_name("$table", "$find_count");
                                open(OUTPUT, ">$file_name");
                                        foreach(@query){
                                                print OUTPUT $_;
                                        }
                                close OUTPUT;
                                undef @query;
                        }
                        $table = $1;
                        $find_count++;
                }
                next unless $table;
                push @query, $line;
 
                $line_number++;
        }
close DUMP_IN;
say $line_number;
 
## Subroutines!
sub write_USE() {
        my($filename, $line) = @_[0,1];
        open (OUTPUT, ">$filename");
        print OUTPUT $line;
        close OUTPUT;
}
 
sub make_file_name() {
        my ($type, $number) = @_[0,1];
        $number = sprintf("%05d", $number);
        $file_name=$number."_".$type.".sql";
        return $file_name;
}
 
sub usage() {
        say "Error: missing arguments.";
	say "Usage:";
	say "$0 [MYSQL_DUMP]";
        exit 1;
}
 

A small downside is that this replaces my 2.5Gb file with about 1800 smaller ones. A scripted importer is to follow.

Massive dumps with MySQL

Tuesday, January 12th, 2010

hurr. *insert FLUSH TABLES joke here*

I have a 2.5GB sql dump to import to my MySQL server. MySQL doesn't like me giving it work to do, and the box it's running on only has 3GB of memory. So, I stumbled across bigdump, which is brilliant. It's a PHP script that splits massive SQL dumps into smaller statements, and runs them one at a time against the server. Always the way: 10 lines into duct-taping together something to do the job for you, you find that someone else has done it rather elegantly.1

In short, we extract the directory to a publicly http-accessible location, stick the sql dump there and tell it to go.

In long, installation is approximately as follows:

avi@jup-linux2:~$ cd www
avi@jup-linux2:~/www$ mkdir bigdump
avi@jup-linux2:~/www$ chmod 777 bigdump
avi@jup-linux2:~/www$ cd bigdump/
avi@jup-linux2:~/www$ wget -q http://www.ozerov.de/bigdump.zip
avi@jup-linux2:~/www$ unzip bigdump.zip
avi@jup-linux2:~/www/bigdump$ ls
bigdump.php  bigdump.zip

Where ~/www is my apache UserDir (i.e. when I visit http://localhost/~avi, i see the contents of ~/www). We need permissions to execute PHP scripts in this dir, too (which I have already). We also need to give everyone permissions to do everything - don't do this on the internet!2

Configuration involves editing bigdump.php with the hostname of our MySQL server, the name of the DB we want to manipulate and our credentials. The following is lines 40-45 of mine:

// Database configuration
 
$db_server   = 'localhost';
$db_name     = 'KBDB';
$db_username = 'kbox';
$db_password = 'imnottellingyou';

Finally, we need to give it a dump to process. For dumps of less than 2Mb3, we can upload through the web browser, else we need to upload or link our sql dump to the same directory as bigdump:

avi@jup-linux2:~/www/bigdump$ ln -s /home/avi/kbox/kbox_dbdata ./dump.sql

Now, we visit the php page through a web browser, and get a pretty interface:

BigDump lists all the files in its working directory, and for any that are SQL dumps provides a 'Start Import' link. To import one of them, click the link and wait.

  1. Yes, you Perl people, it's in PHP. But it's not written by me. So on balance turns out more elegant. []
  2. Those permissions aside - anyone can execute whatever SQL they like with your credentials through this page. Seriously, not on the internet! []
  3. Or whatever's smaller out of upload_max_filesize and post_max_size in your php.ini []

Whoo! Theme update!

Tuesday, January 12th, 2010

I've updated the theme, and applied my handy modifications that make it more grey. Here's the diff on the css file, if you're wondering what I did (and for next time when I forget). I despise CSS, so it's all nice and easy.

The changes are to give the <pre> tags a grey (#EEE) background colour, and to make the posts individual white boxes on grey, rather than an all-white page.

 
avi@avi:whiteasmilk_1.8$ diff style.css style.css.original
18,35d17
< /* Added to make the code blocks pretty. Stupid CSS implementations mean this will break
<    any form of validity in order to actually make it work. Stupid browser people.
<    I can't remember where I got this from, but if you reckon I might've found it on your site
<    let me know and if I believe you I'll stick a URL here
< */
< pre {
< 	border 0
< 	padding: 0.2em 0.5em;
< 	background-color:#EEE;
< 	white-space:pre-wrap;
< 	white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
< 	white-space: -pre-wrap;      /* Opera 4-6 */
< 	white-space: -o-pre-wrap;    /* Opera 7 */
< 	word-wrap: break-word;       /* Internet Explorer 5.5+ */
< }
<
<
<
40c22
<   body {background-color:#c9c9c9;}
---
>   body {background-color:white;}
323,326c305
< border-bottom:15px solid #c9c9c9;
<   padding-left:10px;
<   padding-right:10px;
<   background-color:#fff;
---
>   border-bottom:1px solid #999;

Generating Fluxbox menus for VNC (Vinagre) connections

Wednesday, December 16th, 2009

One of the lovely things about Fluxbox is the text-driven menu. One of the nice things about Vinagre (Gnome's VNC client) is the xml-based bookmarks file. Here's a handy script to create a Fluxbox submenu out of your Vinagre bookmarks:

 
#! /usr/bin/perl
 
use strict;
use warnings;
use XML::Simple;
my $HOME = $ENV{ HOME };
 
my $bookmarks_file = "$HOME/.local/share/vinagre/vinagre-bookmarks.xml";
my $menu_file = "$HOME/.fluxbox/vnc_menu";
 
my $xml = new XML::Simple (KeyAttr=>[]);
my $data = $xml->XMLin("$bookmarks_file");
 
open(MENU, ">$menu_file") || die "Error opening \$menu_file: $menu_file $0";
 
print MENU "[begin]\n";
 
foreach my $b(@{$data->{"item"}}){
	print MENU "[exec] ($b->{name}) {vinagre $b->{host}:$b->{port}}\n";
}
print MENU "[end]\n";
close MENU;
 

Dell Warranty Info

Monday, December 7th, 2009

I hate navigating the Dell website. It's inconsistent and messy and noisy, and all I generally want is a single date (when the warranty expires or expired on a given box). So I wrote this. It scrapes the Dell website, and returns the warranty info for the service tag it's been passed.
I've CGI'd it here.

#! /usr/bin/perl
 
use strict;
use warnings;
 
die "$0\n\tGet warranty info from dell.\nUsage\n$0 [SERVICE TAG]\n" if !$ARGV[0];
 
my $service_tag = $ARGV[0];
 
use LWP::Simple;
use HTML::TableExtract; # Is in the CPAN, and exists in the debian repositories as libhtml-tableextract-perl
 
## Make a URL:
my $url_base = "http://support.euro.dell.com/support/topics/topic.aspx/emea/shared/support/my_systems_info/en/details";
my $url_params = "?c=uk&cs=ukbsdt1&l=en&s=gen";
my $url = $url_base.$url_params."&servicetag=".$service_tag;
my $content = get($url);
 
# Tell HTML::TableExtract to pick out the table(s) whose class is 'contract_table':
my $table = HTML::TableExtract->new( attribs => { class => "contract_table" } );
$table->parse($content);
 
## Gimme infos!
foreach my $ts ($table->tables) {
	foreach my $row ($ts->rows) {
		print "", join("\t", @$row), "\n";
	}
}

Getopt in Perl

Sunday, November 29th, 2009

Oddly, it's taken me until this afternoon to have real need for using getopts in Perl. After a not-overly-brief look around, I've settled on Getopt::Long for the purpose. It's marginally more complicated than the alternative (Getopt::Std), but more flexible and better at error checking.

To use it, you pass a hash of valid options to GetOptions, where the keys are options and the values are the references to variables in which to put their arguments.
The name of the option dictates what value(s) it can hold: the final character indicates type (i - integer, f - float, s - string), and the penultimate whether it is optional or not (= - required, : - optional). Flags are indicated by not following this pattern - they're just given a name with no symbols.

Getopt::Long allows for the shortest unambiguous switch to be used, doesn't distinguish between -o and --o, and allows for the negation of flags (if -flag sets a flag to 1, -noflag will set it to 0). It also doesn't touch @ARGV when it's done getting its flags out of it.

Here's a brief script hopefully helping explain the above:

#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
 
# This is only neccesary when using strict. Which is always.
my ($flag, $compulsory_string, $optional_string, $compulsory_integer, $optional_integer, $compulsory_
 
GetOptions(
        "o"=>\$flag,
        "cfloat=f"=>\$compulsory_float,
        "cint=i"=>\$compulsory_integer,
        "cstring=s"=>\$compulsory_string,
        "ofloat:f"=>\$optional_float,
        "oint:i"=>\$optional_integer,
        "ostring:s"=>\$optional_string,
);
 
print "flag set\n" if $flag;
print $compulsory_float."\n" if $compulsory_float;
print $compulsory_integer."\n" if $compulsory_integer;
print $compulsory_string."\n" if $compulsory_string;
print $optional_float."\n" if $optional_float;
print $optional_integer."\n" if $optional_integer;
print $optional_string."\n" if $optional_string;