Recently in Computers Category

portable UI tip #1

| 1 Comment | No TrackBacks
if there is a blinking caret in the field then show a keyboard. There is nothing more annoying than having to 'repick' the text field that is currently active, with the blinking caret

what are you thinking android? focus is something explicit, not implicit. You do not have to have an active control on a form at all times. that and the fact that the keyboard is very tough to get rid of when you want to.

Shock! Horror! Bug found where Windows applications will open DLLs that are in the current working directory of a process!

Except it's not a bug. It's by design, and it's existed since NT.

Microsoft is being smacked in the head by a required feature of Windows due to the initial weakness of the LoadLibrary call. If you don't specify a path to the file to load, it uses the standard library search path.

Dear god, you would think that this was news. It is not news, nor has it been since the goddamned operating system shipped. Granted, the issue is severe, but the fact of the matter is if an application is executed using a working directory that isn't under your control, then what can you do? if there are libraries in the same directory that launched the program that happen to share the name of system libraries then you're hosed.

Hey, guess what asshole, if you link a linux binary with a search path containing '.', then you get the same problem. It's just as well that nobody links their binaries with -R. .... eh?

The documentation is blatant in this regard. I've known it was a security issue since I first learned of the LoadLibrary call, as any even half decent developer should have known when they started using the damned function.

The rule is simple. Resolve the full path to a library before you load it. Validate that it 'looks right' at that point. Then load it.

BTW .init section in .so files - so totally a security hole. You can't dlopen a file to determine if it's good without executing the .init code. Game over man, game f**king over!

My .init code does a setenv("LD_LIBRARY_PATH", "." + getenv("LD_LIBRARY_PATH")) ... now piss off and write secure code for once...

This advice is for Microsoft Virtual PC. When you use software like VMWare, it automatically allows the host to connect directly to the client using the virtual interfaces that have been created.

Most of the recommendations with regard to connection to/from the Virtual PC client recommend configuring the connection to share/bridge one of the network connections.

All very good and well when you're on a network. I regularly use the system when I have no network available - i.e. I'm completely disconnected. Most of the connection sensing code for network adaptors prevent you from using it while it's not active, plus I don't like having to configure the connection manually and then reconfigure it when I've got a real network.

The simple solution is to add a Microsoft Loopback Adaptor to the host machine, then create a second network interface on the Virtual PC that uses this interface. Manually configure the IP addresses to be on the same private network, making sure that you don't accidentally configure it to use an IP address range that you may end up using for a VPN connection.

  1. Add the Network Adaptor: XP, Vista, Windows 7
  2. Configure the IP address manually. Use a Private Address Range. I chose an IP address of 10.125.1.1 with a netmask of 255.255.255.0 for the host, then chose 10.125.1.2 for the Virtual machine. XP, Vista, Windows 7 - Use the instructions for Vista.
  3. Shutdown the Virtual Machine, Don't hibernate as you can't add the second network interface.
  4. Edit the properties of the virtual machine (in the Virtual Machines folder). Either Right Click on the Virtual Machine Icon, or use the Settings Option in the menu bar.
    Settings Option
  5. Configure the network to have 2 interfaces, one of which is linked to the 'Microsoft Loopback Adaptor'
    Settings Dialog
  6. Boot up the virtual machine, and follow the instructions for manually configuring the IP address of this new network interface.

Direct connections to the IP address of the client virtual machine now work, and you can use it for anything you want.

Following the instructions here, even if they're confusing, once you add a dword key called '*NdisDeviceType', with a value of 1, you don't see the connection as an unknown connection; thus enabling sharing and other features in Vista, Win 7.

Well I had a minor hiccup today when I decided it was 'password change day'. I duly went around changing the password on all my systems. Then I got back to work. 10 minutes later I turned to my other system and typed in the password.
... It didn't work ...
I smacked my head and said to myself "D'oh", I need to use the new password. But I couldn't remember all of it. All I had was a few characters I could remember and the fact that my mail program was checking the mail every few minutes and still working.

First I got the pid of thunderbird...

~% ps -fe | grep thunder
1000     17509     1  0 13:19 ?        00:00:00 /bin/sh /usr/bin/thunderbird
1000     17521 17509  0 13:19 ?        00:00:00 /bin/sh /usr/lib/thunderbird/run-mozilla.sh /usr/lib/thunderbird/thunderbird-bin
1000     17526 17521  0 13:19 ?        00:00:24 /usr/lib/thunderbird/thunderbird-bin
1000     19101 19006  0 14:09 pts/10   00:00:00 grep thunder

Then I got the address of the heap from the process' maps

~% grep 'heap' /proc/17526/maps
08d02000-0a9ad000 rw-p 08d02000 00:00 0          [heap]

I compiled up memory_dumper, and ran it against the process and heap addresses listed.

% ./memory_dumper 08d02000 0a46a000 17526 heap

Then I ran strings on the resulting file, looking for the pattern that matched my remembered password

% strings heap | grep t%7
cheat%7Ladel
cheat%7Ladel
cheat%7Ladel
cheat%7Ladel
%

4 copies of the password in memory in the program. That is just in-freaking-sane. It should be present in the program only once, and should probably be concealed using some form of obfuscation. Mind you, it has kept the new password in my mind now, so I should be grateful.

And just in case you feel like trying the password listed, don't. It's not the real password ;)

I have been buying sound cards for a loooooong time – my first add-on card was for a 512K Amsdrad PC512 and it produced either MIDI-based sound or replicated sample audio. It was not a cheap purchase at the time – I can't remember the price any more, but it was quite a bit of savings at the time.

It came with a literal 'wodge' of 5.25" driver diskettes. you could use it to steady a table there were so many of them.

Later on, the disks changed to 3.5". This meant that they were thicker than the older disks, and amounted to a pile that simply got progressively larger. By the purchase of my last soundblaster card, I was looking at IIRC 10 disks, only a few of which were usable for drivers for DOS, the remainder were 'assistant' programs such as Dr. Sbaitso, which were to purposes useless.

I spent a long time kind-of caring about my sound card. I bought an SB live card for my main desktop and for several years things just worked. About 2 years ago got an SoundBlaster X-Fi card for notebooks for my Dell Insipron M1710. Honestly, the internal card was better than the add-on card. I didn't really care as I paid for it in Yen, so it didn't count towards cost.

In the last 6 months I bought a new rig. Reasonable price, and harkening back to my memories, I got an SB X-Fi XtremeGamer card. Not a large outlay (<€80). It no longer comes with a wodge of disks – it downloads software and updates from the internet.

The smallest update for this software seems to be 50MB. The sum total of the latest software update (to fix problems and to increase compatibility on Vista) is 235MB. I am 44MB into the update and I'm being told that there's another 2.5 hours to go. I'm not on a slow link either. It just seems to be on their side.

Just to put this into perspective - The download for my soundcard is about 1/2 the size of a reasonable Linux distro… and it's as slow as a wet weekend in June. By the time this update has downloaded I could have watched the entirety of the latest Harry Potter movie and still had time for a pint. It's damned slow.

This is a sound card. Not the World Management Software Suite®. The update for my graphics card was 90MB and that was Driver + Support Software + PhysX Drivers. And it downloaded in less than 10 minutes.

Now that I recall, all the problems I seemed to have on the older machine could always be traced to limitations or issues with my sound card. A driver that wasn't playing by the rules. Maybe it thought it was being edgy? I've seen too many BSODs to want edgy. I just want something that works…. and doesn't need a 250MB update (that's twice the size of OpenOffice)…

Oh, and Windows Live Writer — please convert euro, trademark and em-dash symbols before posting… we're not all using UCS-16 encoding here. Some of us actually try to use the web in a platform independent manner…

Nothing more than a rant....
It's not that tough - when using most english locales, we sort case insensitively. a==A, B==B and so on. Pragmatically, the only reason for picking a locale other than UTF-8.generic is because I would really, really like these rules obeyed.
I am sick to death of having to work around stupidity.
I'm just complaining as I look at the output from ls and it's pretty much a case sensitive sort. I'm sure that accents are sorted correctly in EN_ie - after all á is the same as a, but apparently it's different to A.
Sorting it difficult... the rules are so complicated... stop complaining! you're able to perform at least 600 million operations per second, and a table lookup for a case insensitive sort is probably going to cost 20.
Bear in mind that the number above was a quick back of the envelope number of an iPhone. I'm sure a real computer will be able to do something a little better...

Update:

Looks like it's not Linux, it's only Leopard that doesn't understand EN_ie collation. Oh well, that's life I suppose…

Created a little icon (it's the white quote mark in the red loop), added MMS information and tethering support.
To install it, rename the downloaded file without the .zip extension (it should be Vodafone_ie.ipcc). Quit iTunes.
On the mac open a terminal and type: defaults write com.apple.iTunes carrier-testing -bool TRUE
On windows(32 bit) in a cmd prompt type: "%CommonProgramFiles%\Apple\Mobile Device Support\bin\defaults.exe" write com.apple.iTunes carrier-testing -bool TRUE
On windows(64 bit) type: "%CommonProgramFiles(x86)%\Apple\Mobile Device Support\bin\defaults.exe" write com.apple.iTunes carrier-testing -bool TRUE
Restart iTunes, option/alt click on the 'Check for updates' button in the iphone's Summary page.
Get the Vodafone Ireland IPCC file.

URL shortening...

| No Comments | No TrackBacks
Not a lot of code. We create a rewrite rule for apache to remap any 5 character requests at root to this script.
RewriteRule ^/([A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9])$ /cgi-bin/shorten.cgi?$1 [PT].
Requests for http://site/..... lookup the entry in the database, requests to shorten.cgi?URL return the shortened uri in a text/plain output when it works.
There isn't a lot of checking, and you probably need to create the db/ directory with mode 777 so you can update the database under cgi, but it... works on my box ;)
#!/usr/bin/perl -w
# shorten or unshorten a url passed in
use strict;
use DBI;
use Sys::Hostname;

my %keys;
my $value=0;
# 26 + 26 + 10 = 62
my $keys = join("", 'A'..'Z') . join("", 'a'..'z') . join("", '0'..'9');
my @keys = split(//, $keys);
for my $i (@keys) {
    $keys{$i} = $value++;
}

my $file = "shorten.db";
my $dir;
my $var;

if (defined($ENV{'SCRIPT_FILENAME'})) {
    $var = $ENV{'SCRIPT_FILENAME'};
} else {
    $var = $0;
}

($dir) = $var =~ m/(.*)\/[^\/]+/;

$file = $dir . "/db/" . $file;

if (! -d $dir . "/db") {
    mkdir($dir . "/db", 0777);
    chmod(0777, $dir . "/db");
}

my $dbh = DBI->connect("dbi:SQLite:dbname=$file", "", "") || die "Could not open $file";
chmod(0666, $file);

$dbh->do("create table if not exists mapping (id INTEGER PRIMARY KEY, url TEXT)");
$dbh->do("create index if not exists mappurl on mapping(url)");

exit(0) if (!defined($ENV{'QUERY_STRING'}));
my $qs = $ENV{'QUERY_STRING'};

if (length($qs) == 5) { # from short -> long
    my $key = 0;
    map { $key = $key * 62 + $keys{$_} } split(//, $qs);
    my $ary = $dbh->selectall_arrayref("select url from mapping where id = $key");
    if ($ary) {
        my @ary=@$ary;
        print "Location: " . $ary[0][0] . "\n\n";
    }
} else {
    my $sth = $dbh->prepare("select id from mapping where url = ?");
    my $ret = $sth->execute($qs);
    die "Failed to execute " . $sth->errstr unless($ret);
    my @row = $sth->fetchrow_array();
    my $value;
    if (!@row) {
        $sth = $dbh->prepare("insert or replace into mapping (url) values (?)");
        $sth->execute($qs) or die "Failed to insert" . $sth->errstr;
        $value = $dbh->last_insert_id("","","","");
    } else {
        $value = $row[0];
    }
    if (defined($value) && ($value > 0)) {
        my $op = "";
        while(length($op) != 5) {
            $op = $keys[$value % 62] . $op;
            $value /= 62;
        }
        my $base;
        if (!defined($ENV{'HTTP_HOST'})) {
            $base = hostname();
        } else {
            $base = $ENV{'HTTP_HOST'};
        }
        print "Content-Type: text/plain\n\nhttp://" . $base . "/" . $op . "\n";
    } else {
        print "Content-Type: text/plain\n\nFailed to shorten $qs.";
        exit(0);
    }
}

# vim: ts=4:sw=4:et
XcodeScreenSnapz001 - nullity null Apparently I forgot to plug out my null before quitting the application. That's a shame as I thought I had two or three nulls floating around
So there I am looking at the sysinfo from a particular machine and I look at the content of the procs field. It looked to be a bit out. Went hunting through the kernel source and noticed that the procs field is filled with the value of the number of threads in the system. This is a little bit odd, as I'm used to separating my threads from my processes.
Turns out that there is an nr_processes() call, which returns the number of processes in the system, rather than the number of threads. A little bit of a change, rebuild and testing now gives me the correct number of processes from the proc field, and I also have a separate result for the number of threads.
There we go, much more sensible ;)
diff -Naur linux-2.6.25.18/include/linux/kernel.h linux-2.6.25.18.new/include/linux/kernel.h
--- linux-2.6.25.18/include/linux/kernel.h      2008-10-09 03:58:32.000000000 +0100
+++ linux-2.6.25.18.new/include/linux/kernel.h  2009-03-16 16:23:39.000000000 +0000
@@ -415,7 +415,8 @@
        unsigned long totalhigh;        /* Total high memory size */
        unsigned long freehigh;         /* Available high memory size */
        unsigned int mem_unit;          /* Memory unit size in bytes */
-       char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding: libc5 uses this.. */
+       unsigned int threads;           /* Number of current threads */
+       char _f[20-2*sizeof(long)-2*sizeof(int)];       /* Padding: libc5 uses this.. */
 };
 
 /* Force a compilation error if condition is true */
diff -Naur linux-2.6.25.18/kernel/compat.c linux-2.6.25.18.new/kernel/compat.c
--- linux-2.6.25.18/kernel/compat.c     2008-10-09 03:58:32.000000000 +0100
+++ linux-2.6.25.18.new/kernel/compat.c 2009-03-16 16:43:31.000000000 +0000
@@ -1031,7 +1031,8 @@
        u32 totalhigh;
        u32 freehigh;
        u32 mem_unit;
-       char _f[20-2*sizeof(u32)-sizeof(int)];
+       u32 threads;
+       char _f[20-2*sizeof(u32)-2*sizeof(int)];
 };
 
 asmlinkage long
@@ -1076,7 +1077,8 @@
            __put_user (s.procs, &info->procs) ||
            __put_user (s.totalhigh, &info->totalhigh) ||
            __put_user (s.freehigh, &info->freehigh) ||
-           __put_user (s.mem_unit, &info->mem_unit))
+           __put_user (s.mem_unit, &info->mem_unit) ||
+           __put_user (s.threads, &info->threads))
                return -EFAULT;
 
        return 0;
diff -Naur linux-2.6.25.18/kernel/timer.c linux-2.6.25.18.new/kernel/timer.c
--- linux-2.6.25.18/kernel/timer.c      2008-10-09 03:58:32.000000000 +0100
+++ linux-2.6.25.18.new/kernel/timer.c  2009-03-16 16:20:02.000000000 +0000
@@ -37,6 +37,7 @@
 #include <linux/delay.h>
 #include <linux/tick.h>
 #include <linux/kallsyms.h>
+#include <linux/sched.h>
 
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
@@ -1166,7 +1167,8 @@
                info->loads[1] = avenrun[1] << (SI_LOAD_SHIFT - FSHIFT);
                info->loads[2] = avenrun[2] << (SI_LOAD_SHIFT - FSHIFT);
 
-               info->procs = nr_threads;
+               info->procs = nr_processes();
+               info->threads = nr_threads;
        } while (read_seqretry(&xtime_lock, seq));
 
        si_meminfo(info);

The Full patch.
defrag That's a huge amount of fragmentation. I think I may not be able to survive that percentage. It took < 5 seconds to defrag. Please people, percentages are supposed to mean something ;)
Using the natty little webtext script (updated to use an $HOME/.webtextrc file for the username/password) and a little bit of procmail magic, we can now send messages containing the subject line of a specific email message once it has been received.
Firstly, there's the .procmailrc file. The recipe I'm using looks like:
# send text messages
:0 W
* ^subject: IM:
| $HOME/bin/sendim
The script sendim is a simple bash script that checks the subject line contains "IM: " and then sends on the remainder of the subject line as a text message to my mobile phone.
As a security precaution, I've added a special header 'X-apikey' which is checked by the sendim. If the apikey doesn't match then the rule doesn't fire. You should replace the XXX item with your own value generated using echo <some text here> | sha1sum. By not putting the api check in the .procmailrc file you can quietly drop messages that don't have the correct key instead of keeping them in your inbox.
#!/bin/bash -p

export PATH=$HOME/bin:$PATH

subject=
apikey=
apikey_c="XXX"

while read foo; do
    [[ -z $foo ]] && break
    subject=${subject:-$(echo $foo | sed -n "s/[sS]ubject: IM: //p")}
    apikey=${apikey:-$(echo $foo | sed -n "s/X-apikey: //p")}
done

if [[ -n $subject && $apikey = $apikey_c ]]; then
    webtext -t 'YYY' "$subject" >/dev/null 2>&1
fi

Source of sendim. Don't forget to replace the XXX and YYY with your chosen items.
I've been testing the resizing of the drives located on a Dell MD3000, and i've seen errors when resizing after the 2TB mark. This is on the new firmware which supports > 2TB logical drives. I wrote a script to write to random locations of a block device. It can then read them back and verify that they're still the same as what was written. Rather than writing to the entire device I use random sampling, with a few fixed points on the block device. I pretty much get consistent failures. If I put in the failed locations into the next write run they come out again in the subsequent run. Kind of makes resizing a dangerous operation, even though it is stated that resizing is non-destructive.
I realize that the array is nothing more than a rebrand of another device, but it would be great if it was tested in a lab before something this bad got out to the customers.
#! /usr/bin/perl -w

use strict;
use Getopt::Long;
use Digest::MD5 qw(md5_hex);
use File::Basename;

my $fs;
my $readfile;
my $writefile;

my $numpatterns = 2048;
my $seed = undef;
my $size;
my $real_size;
my $help;

my %vars;
my @def_offsets = (0);

sub usage($) {
        print <<EOM;
Usage: $0 --fs=<filesystem> --read=<file>|--write=<file>
        [--num=<number of blocks>] [--offset=<offset to test>]
        [--seed=<random number seed>]
EOM
        exit ($_[0]);
}

my $result = GetOptions( 'fs=s' => \$fs,
        'num=i' => \$numpatterns,
        'seed=i' => \$seed,
        'read=s' => \$readfile,
        'offset=i' => \@def_offsets,
        'write=s' => \$writefile,
        'h|help' => \$help);

usage(0) if defined($help);
warn "Need file system to use" if (!defined($fs));
warn "Need either a read or write file" if (!(defined($readfile) || defined($writefile)));

usage (1) if (!defined($fs) || !(defined($readfile) || defined($writefile)));
my $base = basename($fs);

open (IN, "</proc/partitions") || die "Could not load partition tables";
while (<IN>) {
        chomp();
        my ($major, $minor, $blocks, $name) = m/(\w*)\s+(\w*)\s+(\w*)\s+(\w*)$/;
        next if (!defined($major));
        if ($name eq $base) {
                $real_size = $blocks;
                last;
        }
}
close(IN);

die "Could not get size" if (!defined($real_size));

# Write to the offset in blocks
sub write_to_offset($$) {
        my ($offset, $buffer) = @_;
        sysseek(INFS, $offset * 1024, 0);
        my $write = syswrite(INFS, $buffer, 1024);
        if (!defined($write) || $write != 1024) {
                warn "Failed to write: $offset $!\n";
        } else {
                $vars{$offset} = md5_hex($buffer);
        }
}

sub read_from_offset($) {
        my ($offset) = @_;
        my $buffer;
        sysseek(INFS, $offset * 1024, 0);
        my $read = sysread(INFS, $buffer, 1024);
        if (!defined($read) || $read != 1024) {
                warn "Could not read 1024 bytes at $offset $!";
                return (1);
        }
        if (md5_hex($buffer) ne $vars{$offset}) {
                warn "Data at offset $offset was not the same as expected";
                return (1);
        }
        return (0);
}

sub get_buffer {
        my $i = 0;
        my $buffer = "";
        while ($i++ < 256) {
                my $randval = int(rand(255 * 255 * 255 * 255));
                $buffer .= chr($randval >> 24) . chr(($randval >> 16) & 255) .
                        chr(($randval >> 8) & 255) . chr($randval & 255);
        }
        (length($buffer) == 1024) || die "Buffer was " . length($buffer);
        return $buffer;
}

if (defined($readfile)) {
        # reading from previous file
        open (INPUT, "<$readfile") || die "Could not open previous run log";
        while(<INPUT>) {
                chomp();
                my ($key, $value) = m/(.*)=(.*)/;
                if ($key eq "patterncount") {
                        $numpatterns = $value;
                        next;
                }
                if ($key eq "size") {
                        $size = $value;
                        next;
                }
                if ($key eq "seed") {
                        $seed = $value;
                        next;
                }
                $vars{$key} = $value;
        }
        close(INPUT);
} else {
        $seed = time ^ $$ ^ unpack "%L*", `ls -l /proc/ | gzip -f` if (!defined($seed));
        $size = $real_size if (!defined($size));
        open (OUTPUT, ">$writefile") || die "Could not open new run log";
        print OUTPUT "patterncount=$numpatterns\n" .
                "size=$size\n" .
                "seed=$seed\n";
}

print "Size: $real_size [$size] Seed: $seed\n";
srand($seed);

my $mode = "<";
$mode = "+<" if ($writefile);
open(INFS, "$mode$fs") || die "Could not open raw device";

if ($writefile) {
        map { write_to_offset($_, get_buffer()) } @def_offsets;
        write_to_offset($size - 1, get_buffer());
        while($numpatterns > 0) {
                my $offset = int(rand($size));
                print "Writing pattern: $numpatterns           \r";
                next if defined($vars{$offset});
                write_to_offset($offset, get_buffer());
                $numpatterns--;
        }
        map { print OUTPUT "$_=" . $vars{$_} . "\n" } keys(%vars);
        close(OUTPUT);
} else {
        my $failcount = 0;
        my $tocount = scalar(keys(%vars));
        map { $failcount += read_from_offset($_); printf("To Count: %0.7d\r", $tocount--); } sort(keys(%vars));
        print "Count difference: $failcount\n";
}

consistency.pl.txt

signal versus sigaction

| No Comments | No TrackBacks
the use of the
signal(int signum, void (*handler)(int))
is a smidgin dangerous on various operating systems. Under Solaris, for example once the signal has been delivered to the process the signal handler is reset, so a typical piece of code that wants to reuse the signal handler repeatedly will typically set the signal handler again when receiving the signal. This leads to a minor race condition where upon receipt of the signal and the re-setting of the handler the process receives another copy of the same signal. Some of these signals cause Bad things to happen - such as the stopping of the process (SIGTSTP for example). Under Linux it keeps the signal handler in place, so you have no fear of the event triggering an unwanted event.
The manual page for
signal
under Linux makes it clear that the call is deprecated in favour of the much more functional
sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact)
call, which keeps signal handlers in place when you don't pass the SA_RESETHAND parameter as part of the sa_flags parameter of the sigaction structure. So you get to explicitly choose to accept a signal once, and then have the system deal with it in the default manner afterwards.
Signals, are of course a real pain in the ass when dealing with sub-processes. For example the use of ptrace to perform profiling works well until you fork. If another SIGPROF signal arrives before you can create your signal handler then the child process is terminated as that's the default behaviour in that situation.
Under Solaris (and Leopard) you can make use of dtrace to perform profiling on a set of processes without needing to deal with vagaries of signal handling, making this a non-issue. For those of you stuck in LD_PRELOAD land, probably the only thing that can be done is to set the signal disposition to be ignored before execing the new process. you have a small window where the profiling is missing, but the overall increased stability of the application is improved by preventing it from accidentally being terminated due to a profiling signal being received too soon. I know the accuracy nuts would hate that, but it's part of the price of dealing with standards.
CreativeWhine Oh get over yourself! I do not need to install the music management software on my computer and not having it installed is not the end of the world. It's almost as bad as the apple updater suggesting you install Safari. Mind you, it's nowhere near as annoying about it, and it doesn't suggest that the world will end if you don't download it (but, you know, it just might...)
Vista's built in search box on the start menu is a boon for launching applications. The general accessibility of windows applications to users of the keyboard is a huge boon to those of us who try to keep our grubby little fingers on the keyboard.
This does not seem to be quite the case on the mac. I'm probably unaware of all the keyboard accelerators that are available - after all, I've been using Windows for a lot longer. A lot of my use of the keyboard was prompted by a long use of FVWM while in Sun, where practically everything was usable without having to stray to the mouse. Mind you on laptops, the location of the touchpad is a lot better in this regard - you just drag your claw-like thumb over the tracking surface.
#!/bin/bash -p

if [ $# -eq 0 ]; then
    echo "Usage: $(basename $0) " 1>&2
    exit 1
fi

while [ -d /proc/$1 ]; do sleep 0.5; done
If I implemented it using inotify, I presume I can get rid of the sleep, but that entails compiled code.
I like supporting Irish websites, so I tend to use url.ie for links. the algorithm for generating the link seems to be sequential, so I was happy yesterday when my link for the perfect coffee went to http://url.ie/pdc, or as Dale Cooper would (hopefully) say - perfect damn coffee.
I have to laugh when I see the great data recovery 'challenge'. Lets be honest here folks, businesses are in it to make some level of profit from their efforts. To that extent they have facilities in place to recover data from damaged drives due to a variety of problems from simple surface level damage all the way through to failed drive electronics (swapping out logic boards).
The price quoted is generally based on the amount of effort that needs to be gone thorough. Accidental erasure is probably the cheapest. Simple disk-level damage (e.g. a few dodgy sectors) can be resolved using tools like Steve Gibson's Spinrite; which is pretty much a good example of what these companies would be doing. Drive electronics failures would cost more - for example they may need to disassemble the drive in a protected atmosphere to replace something. Large scale physical damage to the drive may entail extracting it from the original housing and essentially replicating the internals of the drive in order to read the data from it. This would be very expensive, but would succeed in the face of quite significant damage.
The intentional erasure of the data using utilities like dd are pretty much a non-starter. For the first part, you need insanely expensive specialist equipment, the rate of data recovery is slow (we're probably talking in the order of bits per second) and the chances of actually recovering anything useful on a typical hard drive is nil.
For any typical person trying to wipe their data any of the secure erasure utilities available for purchase or for free are more than adequate to prevent the data being recovered by any agencies.

Not a lot of font choice

| No Comments | No TrackBacks
Adobe Buzzword Font List This is the list of typefaces available in Adobe's new Buzzword. It is really, really pretty; implemented in Flash, but when it comes to using it we discover that the two main fonts are missing - Times & Helvetica (or Times New Roman & Arial for 'softies).
All the online offerings from the Adobe Beta are pretty nice, and cover the most fundamental of things, and some of the more useful features - like change tracking in Buzzword. It's all flash; so I have the fear that it will crash my browser.
It's yet to happen me on the mac, though; even though I keep losing the browser on Linux
Chrome Incognito All the popular kids seem to be doing it. IE8 has the feature. Chrome seems nippy too.
Not really stated anywhere obvious, but apparently Xgl isn't the preferred mechanism for all the special effects in Xorg post 7.1, it's AIGLX that you should be using. It's integrated too :)
So after wasting a lot of time trying to get the nvidia driver working properly with XGL after the latest updates I just searched the docs here, there and everywhere and finally picked up on the 'just use AIGLX' vibe.
Maybe I should be reading the release notes... or are they in the release notes at all? I managed to survive gutsy and hardy without changing over to AIGLX and it still worked, so I may just have missed this in the notes somewhere.
Sitting in the Galway Hooker bar, having a pint waiting until my train starts boarding.
Using an Acer Aspire One laptop, running Ubuntu Intrepid Ibex and my USB 3g data dongle.
It's actually usable. It may be small, but it's cabable.
Oh, and it looks like my train is boarding. At least I have a seat reserved so I don't need to queue with the other folks.
I don't want to keep manually syncing my address book on Vodafone with that of my mac, so I use this script to pull out the phone numbers of people when I want to send them a text message using webtext (or my webtext script).
#!/bin/sh

search_for=name
return_val=phone

args=$(getopt s:r: $*)

set -- $args

while :; do
    case $1 in
        -s) search_for=$2; shift;;
        -r) return_val=$2; shift;;
        --) shift; break;;
    esac
    shift
done

if [ $# -lt 1 ]; then
    echo 1>&2 "usage: $(basename $0) -s <search> -r <return> YOUR_SEARCH"
    exit 127
fi

search=
for i in $@; do
    if [[ -z $search ]]; then
        search="($search_for contains \"$i\")"
    else
        search="$search and ($search_for contains \"$i\")"
    fi
done

scriptcode="tell app \"Address Book\" to get (value of $return_val) of every person where $search"

osascript -s "s" -e "$scriptcode"

exit 0


You can get it Here.

Trust me...

| No Comments | No TrackBacks
Plaxo Assistant Cert It tells me to trust it. After all, it's a certificate that's signed by a CA that isn't in the list of known certificate authorities.
I don't trust certificates. There is a list of certificate authorities a mile long stored on my computer of groups who are to be trusted when a certificate is presented. I don't know them from adam, and the certs from the Hong Kong post office are about as trusted as the ones from the Apple Root CA - get real people this is not security, this is just posturing. I trust them about as much as I trust the digital quicksand upon which they are based.
I've stopped caring anymore. The only thing that these certificates establish is a temporary private channel between me and the web server. The rest; it's just smoke and mirrors.
Firefox 'dialog' The suggestion by Joel to not hide or disable menu items is a good one. There's only one issue. With the way it's worded you could end up with a swarm of dialogs. I would suggest some form of stackable notification item. The status bar isn't really suitable for this as there's no way to get stuff back from it historically.
The 'in frame' dialog that's becoming popular these days in such browsers as Firefox. This is a reasonable 'dialog', and you could remove them automatically after a period of time (say 30 seconds). as long as they are differentiated from the other items on the screen it would be reasonable.
Flash Update Message Apparently, it needed to install a security update. I don't believe I'm using any flash applications that would keep the player in use, so why the pathetic dialog on the left after I installed it? This is one of those cases where pushing through the update makes more sense. As it is this only tells me that I need to reboot my computer to be safe from 'flash viruses'.
Is it that the flash component is so embedded in the operating system that updating it requires a reboot? If that's the case then why? it's only a little thing for displaying animations; not the end of the fricking world.
Busybox is pretty much essential if you're using a small, embedded linux. It's small footprint and complete replication of most of coreutils (and a bunch of other packages) makes it great. All you end up with are a bunch of symlinks.
The problem is, though, that pretty much all the commands are 'slightly different'. They don't take long options. Help is not helpful (I've regularly had to look at the source to tell what's the problem).
The real problem comes when you decide you want to rip it out of your system. All those minor things that had to be changed to work under the busybox system now have to be re-checked under coreutils and family.
Fun for all the family.
It's great. simply prettier and a lot more usable than Firefox 2. The awesome bar (the address bar) kicks ass. Much easier to use than the previous one. Bookmark management has been improved. The look and feel is nicer. I even 'kind of' prefer the subtle dialog box improvement which turns up at the top of the form, which is like a wide series of websites that perform the same thing themselves.
This definitely has replaced my web browsers in Windows and Linux. There's a very high chance that it will replace Safari on the Mac. The only niggle I have is that it doesn't store your passwords in the Mac keychain, which I still feel is the better place to have them.
Damn the electric fence...
Defeating the laughing octopus this time round was significantly easier, simply due to knowing all the patterns already. I do like the leaking the color out of the scene effect that is used at certain sections. Very artsy and very well done.
Then I played for at least an hour after that and remembered to save.
Why can't these folks auto-save upon zone transitions (like half-life 2). I was saving every time I could simply to be on the safe side.

gravatar URIs

| No Comments | No TrackBacks
Short and simple: http://en.gravatar.com/avatar/<md5 hash of email address>?.
e.g. echo -e 'bob@email.com' | md5sum gives: c961431faea38ed65bfd982cf2e31bd0. Optional add-ons are size (s=<Number of pixels>), content rating (r=<g, pg, r, or x>), and default (d=<escape encoded URI of an image or one of identicon, monsterid or wavatar>).
great place to do something akin to the 'imitate a lotus notes password entry trick'.
And when you get to the end of the first act there's another 3 minute hard-drive install while the game chastises you for staying up too late. Nice of it to do that.
This is a simple perl script that checks if the passed in input data is binary or not by checking if, within the first 8k the number of non-ascii characters exceeds the number of non-ascii characters by a factor of 2. It's fundamental in that it doesn't check for unicode files, so is probably broken in that case.
#!/usr/bin/perl -w

use strict;

my $buffer;
my $nread;
$nread = sysread(STDIN, $buffer, 8192);
my $nchars = 0;
my $nbinchars = 0;

foreach (split(/ */, $buffer)) {
        my $value = ord;
        if ($value < 32 || $value >= 127) {
                $nbinchars++;
        } else {
                $nchars++;
        }
}

exit ($nbinchars * 2 > $nchars);

Installers

| No Comments | No TrackBacks
I like the general way of installing applications on the apple. Drag program into Applications folder, and you're done!
The only problem comes in when you actually have to use installers, as they want to put things in various locations in system-protected locations. 9 times out of 10 they don't come with an uninstaller, so removing them can be a bit of a difficult task, especially if they spew themselves everywhere.
Another issue with the mac installers is that they're shell scripts. People keep forgetting that the mac file system hierarchy contains loads of spaces. Don't forget to trap filenames with quote marks "$foo"! It's broken quite a few systems as a result of forgetting this fact.
Nerd commentary really.

If you heart unicode too, pass it on

Toolbox essentials

| No Comments | No TrackBacks
Well, I was reading '5 more essentials for your programming toolbox' and the first entry has my favourite - unfolded linked lists. It's just that it's broken in at least one way. You should embed the list of 'void *' pointers at the end of the entry using the magic of the zero length array at the end.
struct unrolled_listitem {
    int num_elements;
    struct unrolled_listitem *next;
    void *pointers[0];
}
You get the size of a struct item from (sizeof(struct unrolled_listitem) + num_elements * sizeof(void *)). You need to min/max it to the size of a cache line.
The issue is that if you want to embed this structure in another one, you need to add padding after it to the max of the size of the structure that you are planning on allocating.

Several GSODs later...

| No Comments | No TrackBacks
Well, the mac doesn't get BSODs it gets a slightly differently colored grey screen of death. Thankfully I had saved before yanking out the plug for the external monitor. It greyscreened on me with the multi-lingual press the power button for a few seconds message on-screen.
I mean really guys, this is grade-A simple stuff, people should be expected to plug these things in and out on a regular basis so it shouldn't be a kernel panic level issue when something goes wrong in that case.
At least you get a 'graceful' video driver restart when things go horribly messy on that platform (unlike XP where you just have a brick).
Ok, I ponied up a wodge of cash for a ps3 and a few games (and paid the tax for a few movies too). I got Uncharted:Drake's Fortune and Assassins Creed (grammar note: this is a creed that covers all Assassins, so I think the apostrophe should come after the s).
Well, Uncharted is just fun. I'm still a newbie to controller based gaming, but over all, I am impressed. The puzzles and combat just seem to work well; mind you I'd be hard pressed to find that many mercs on any one island. You would need to pay them a hell of a lot of money to stay once they start getting killed with any degree of regularity. Reality aside, it just works as a game. The visuals are great and the game play is well paced and just combines to give us a good experience
Not so Assassins Creed. Booooring is probably the best expression for it. Boring in the same way that performing the same, repetitive missions time and time again gets really damned boring. You get to the city, save the person in distress and then sneak in in the company of a bunch of monks. That's the only way in. Then once you get in you have to perform a minimal set of a handful of styles of missions in order to get to the real mission.
You can go everywhere.... so bloody what, it doesn't help in the complete absence of variety in the missions.
The visuals are great... No, they're good, put a few more pixels on Outcast and it would probably beat Assassins Creed hands down.
For a company like Ubisoft who have produced an excellent run of 3D games in the Prince of Persia series (which got boring, but made up for it in the puzzles) I am stunned that they could produce such a band title. I'm left wondering if they were just scared to produce something that had a bit of excitement in it due to the fact that they set it in a contentious time period (which even then is a huge cop-out, god how I have another rant stored about that).
Oh for another Beyond Good And Evil, Damn, that game is a milestone that needs to be shown to people as an example of how to make a game that reaches out to the player.

Where's the SDK, huh?

| No Comments | No TrackBacks
I'm just wondering when Apple will be shipping the SDK for the ipod touch/iphone. Just being nosey really. I've veered away from jailbreaking it simply because I don't want to end up with an expensive brick next firmware update.
They say February, but of course remembering that the Leopard launch being the end of the month it could be anywhere up to the 29th.
Another thing I'd like to see is the UI guidelines. I'm a bit of a nerd when it comes to reading design guidelines, simply because there are a lot of good points in them. Mind yo, you should not be slavishly obeying them, as, after all, they are only guidelines, and not commandments.
On guidelines, I'm get miffed with applications that require the use of the mouse to accomplish things. Vista's keyboard usable everywhere is a charm to use, even while it's gobbling up all those cpu and disk resources with the indexer.

Stolen from SVGL

| No Comments | No TrackBacks
I enjoy reading SVGL, and the current article is no exception. Considering that I went and pre-ordered Devil May Cry 4, when I read this it made me almost want to cancel the pre-order (not!).
Devil May Cry 4 also has a part where these evil naked snow chicks make out with each other and kiss each other all over. Okay, okay, that was kind of awesome. But I also sternly raised my eyebrows! I did!
To replace my motherboard, graphics card, CPU, heatsinks, fans.... pretty much everything except the actual case the machine comes in.
Apple have obviously made some significant backwards compatibility errors. Firstly, there's the firewall - altering the on-disk content of applications to make them signed when you accept them. Its an interesting approach, but it's complete pants. You don't go around altering binaries on disk. You create a detached signature! It's not really bloody difficult.
On Vista, you can see *every* rule that exists for the firewall. On Leopard, you only get to see the exceptions you created yourself.
I've been having random application crashes. They seem to be related to drag and drop operations that went wrong.
the calendar application does not want to talk to my instance of davical properly (all the calendars disappear after restarting, and I get an error every time I create a calendar). Then there's the 'the application terminated unexpectedly' - no, it didn't, I used the <Apple>Q menu item to quit the application.
Context sensitivity on the mail application is kinda limited - It doesn't detect URL links properly - I have a site that's called http://foo4/..., and all the link comes up with is http://foo. As I said, a bit limited.
Overall, though, the experience is positive. I would have preferred if apple had simply spent some more time testing the damned thing against anything other than their own applications and services.
And, as soon as they allow a replacement for .mac that can be replaced with an external, non-proprietary service I'll be a happier person

Plucking dell battery!

| 1 Comment | No TrackBacks
Aargh, I just saved my laptop from exploding/catching fire. Literally minutes/seconds away from a potential disaster (losing my hard drive - time to do a backup today).
A dell laptop, with a battery model of C5447 - one larger than the number listed in the dell battery recall program. The battery was really really hot - I mean pretty much frying-pan hot heat on the battery. I've contacted dell support. I wonder what's going to happen with this
I was reading the 'Basics on how shims work', and all I could think about was the old linux/unix trick of using LD_PRELOAD to intercept library calls in dynamically linked applications (used to great effect to bypass timebombed applications).
It is one of the reasons why I think that static linking is just horrible - after all, it breaks things, and makes you have to use terribly complicated tricks like disassembling and binary patching to fix problems because you don't have the source code. You don't get the advantages of page sharing, which is kind of important when you consider how many libraries are loaded by the typical gnome application (last check on nautilus was 117 libraries, excluding VDSO and the binary itself, firefox has 151, excluding fonts and other pango related nonsense). Yes, indeed, winners don't use static linking.
As a tangent, it's one of the reasons for not using application compression tools on binaries - after all, the only thing you save is on the distribution size, not on the run-time. The binary itself acts like a static linked blob, and doesn't share the text segment (program code) amongst the other instances, like other binaries, so every instance can use a lot of private memory, leaving less physical memory on the machine for other things like playing high resolution video.
Every time you reinstall vmware it seems to recreate your network interfaces, and at the same time reassigns the ip addresses that you had set up. If you want to move them then you need to edit a file and a couple of registry entries.
The first file is %APPDATA%\VMware\vmnetdhcp.conf. On XP it's normally C:\Documents and Settings\All Users\Application Data, Under Vista that's C:\ProgramData. Note, however that when UAC is enabled, this folder experiences redirection on write by unprivileged users, so editing this file as an ordinary user will have no effect, so make sure that you use a privileged editor when altering this file.
The content you want to change are the Subnet and Range entries to match your original subnet entries you had. You can also put in entries for the domain-name and router. When you add this information it gives you the ability to mark a the subnet as identified under Vista, so you can be in an identified network, and thus be discoverable. Please note that doing this and then putting an insecure OS on the client vm is your own fault.
The other entries that need to be altered are in the registry. The first one is HKLM\Software\VMware, Inc.\VMnetLib\VMnetConfig\vmnet? entries - the IPSubnetAddress entry needs to be changed to match the entries that you set in the .conf file. The next one is a little bit tricky - it's HKLM\SYSTEM\CurrentControlSet\Services\VMnetDHCP\Parameters\VirtualEthernetSegments\?, the value is HostIpAddress - and it needs to be mapped. The value is a endian-reversed representation of your address so if your ip address is 192.168.22.1 the value would be 0x0116A8C0, C0==192, a8==168, 16=22, 01=01. Use Calc to get the values that you should put in there.
Restart the service "vmware dhcp service", and then you should be OK.

vmnetnat.conf musings

| No Comments | No TrackBacks
I wonder does the 'allowAnyOUI = 1' option that's commented out allows me to pick an oui from a previously created virtual machine that's part of the pre-allocated set.
The network interfaces that get configured by vmware always fall into the 'unidentified realm'. The reason seems to be tied to the lack of a router or default gateway entry for the address.
So we cheat, and add in a router entry for the networks that is the same as the IP address of the connection and bingo, you get the option to put it into another network and give it a label! So I put both the VMware network interfaces and the Microsoft loopback interface into a group called 'local only', and providing I don't use any grotty windows virtual machines, it should service me well. Of course, this is all purely academic for those who don't use vista
It strikes me as a little odd that a company got a patent in 1991 covering parallel processing of a form that seems to look suspiciously like the Inmos Transputer hardware from the 80's. They are now suing Sony for their cell architecture (hey, try suing IBM instead; they designed the damned thing). The patent in question is 5,056,000, Synchronized parallel processing with shared memory.
I'm replaying network traffic at 1000 packets per second into a vmware client that's hosted on a vista machine. It's losing quite a few packets. the Vista OS does not appear to be losing the packets, they are simple missing on the guest operating system. This is a lot like crap, really.
Let's see. I have copernic desktop search, which injects itself into pretty much every process that's running on my desktop.
then we have the nvidia nview desktop manager, which is pretty useful in a multi-monitor setting. It also insinuates itself into every process that runs on the desktop.
The end result ... they keep hitting each other over the back of the head.
Need to convert from celcius to fahrenheit? From miles to kilometres? From yards to stadia? units will give you this.
You have: tempF(41)
You want: tempC
        5
What could be simpler?

The more we build

| No Comments | No TrackBacks
The more things stay the same. I am a 100% pass person. Any deviation from that is considered failure.
Steam is a great idea. You get to download your games and you can play anywhere that you can log in to steam. The problem is that the prices of the games are a bit on the high side. For example today Eidos have announced a load of their games are now on steam. Hitman: blood money is $35.95. It's cheaper to walk into your local game store and get it there.
For some reason the re-release on steam pumps the price up over what you can get in the stores. And then it stays there. You get the occasional reduction in price, but overall the price of each game remains reasonably static for it's life on the system. Which is frustrating.

it's a tomato

| No Comments | No TrackBacks
Well, having had enough of the dd-wrt firmware for the linksys router. It was annoying, slow and irritating and every time we enabled QOS it lost the connection to the WAN I upgraded to the tomato firmware. So far it works. It successfully QOSes the p2p stuff down to the lowest category. It has pretty graphs! Whoopee!
Apparently the PS3 has this new thing called 'home'. It's basically second life for all those people who bought a PS3. Or there, or something equally as silly. The benefit of it is that sony don't have to program for every GPU on the planet - they only need to get it working for the PS3 and they're golden. Everyone on the site has at least one thing in common, and they don't need to congratulate each other again and again. Based on the sweet graphics, I'm presuming that just about everything on it is small-c configurable, rather than big-c configurable (involving lots of downloading).
Someone is bound to rat me out for this one. I've resorted to using a sun type 6 USB keyboard when typing on the small laptop. For those times when I couldn't be bothered dragging the XPS out of the backpack I just need to reach over to the keyboard and plug it in. Dayamm, but it makes a huge difference. The response is just right. The laptop's keys are just that little bit too wussy.

It's a clip from idolm@ster, a bit like dungeon master except with your own idol group. it is so kawaii.
If you use windows then this is probably going to be very, very boring.
Every now and again you find yourself needing to install some piece of software on your computer from a source package. You tar xjf the package and descend into the subdirectory and type ./configure.
At this point I would yell stop! Rather than putting it into the default location of /usr/local, consider putting it in /usr/local/<package-version>.
How does this help I hear you ask. Well, using a simple script (in the extended entry), you create a set of symbolic links in the /usr/local directories which reference the files in the /usr/local/<package-version> directories.
If you decide to remove the package then simply remove the /usr/local/<package-version> directory and all the symlinks become broken. By using symlinks -rd /usr/local you clean the file system up and everything is peachy. If you don't have a copy of symlinks, it is available from the debian repository, where you should find the source package somewhere near the bottom.
#!/bin/bash -p

package=$1
destdir=${2:-/usr/local}
me=${0##*/}

[[ -z $package ]] && {
        echo "Usage: $me <package> [destination = /usr/local]"
        exit 2
}

cd $destdir/$package || {
        echo "$me: package $package does not seem to be installed"
        exit 1
}

# build the directory structure - this is a weakness
find . -type d | cpio -o | (cd $destdir; cpio -id)

find . -type f -exec $echo ln -s $destdir/$package/{} ../{} \;

Oh, and for solaris, as I'm using the file in various locations surrounded by symbols you will have to just pass it into a sub-program to execute the link command. Apparently solaris doesn't just substitute the name of the target for the link; instead it will only substitute the name of the target when it is isolated (i.e. you would need to use the {} on their own without anything surrounding them - which explains the space between the closing brace and the backslashed semicolon - old habits). I supposed I could throw a bit of perl at this problem but... it works on my box so frell the rest of you :).
Meh, the entire problem is annoying; generally I would always have to create a program to process the {} operation anyway to prevent space characters from getting in the way but as we say in the trade 99% is better than 0%. If you want a 100% solution you need to add a script that performs the link - one per line produced from the find.

My Frelling Documents

| No Comments
The old new thing has a short article about the use of the My Documents, which links to a short entry about the use of the Documents folder on the Mac.
Let's see how many folders I have on my little box that are not of my creation
05/05/2006  13:39    <DIR>          ACT Projects
05/05/2006  13:40    <DIR>          AdobeStockPhotos
24/11/2006  19:30    <DIR>          Bluetooth Exchange Folder
04/12/2006  22:29    <DIR>          Borland Studio Projects
23/08/2006  17:15    <DIR>          History
04/05/2006  14:02    <DIR>          InterVideo
06/12/2006  22:01    <DIR>          Java Development
12/10/2006  13:07    <DIR>          My Albums
05/05/2006  13:49    <DIR>          My Data Sources
01/11/2006  15:29    <DIR>          My Digital Editions
29/12/2006  03:06    <DIR>          My Downloads
05/08/2006  16:19    <DIR>          My DVDs
08/08/2006  18:26    <DIR>          My Games
05/05/2006  13:53    <DIR>          My MMS
25/12/2006  21:04    <DIR>          My Music
28/12/2006  11:42    <DIR>          My Pictures
11/05/2006  10:19    <DIR>          My Received Files
05/05/2006  13:53    <DIR>          My Shapes
05/05/2006  13:53    <DIR>          My Skype Content
11/05/2006  10:19    <DIR>          My Skype Pictures
27/12/2006  22:10    <DIR>          My Videos
07/12/2006  14:43    <DIR>          My Virtual Machines
21/12/2006  09:34    <DIR>          My Widgets
12/12/2006  10:11    <DIR>          Nero Recode
05/05/2006  13:53    <DIR>          NeroVision
27/11/2006  14:40    <DIR>          PSP Games
27/11/2006  14:56    <DIR>          PSP Sync
06/10/2006  17:18    <DIR>          Rogue Trooper
05/05/2006  18:23    <DIR>          SimCity 4
15/10/2006  23:57    <DIR>          Source Insight
05/10/2006  20:16    <DIR>          Tomb Raider - Legend
05/05/2006  13:59    <DIR>          TT Installer Logs
14/11/2006  12:14    <DIR>          Updater
05/05/2006  13:37    <DIR>          Visual FoxPro Projects
17/11/2006  22:39    <DIR>          Visual Studio 2005
25/09/2006  18:02    <DIR>          Visual Studio Projects
I mean, what the frel is TT Installer logs? For the most part, all code goes into a version controlled sub directory, which is not under my documents (that would be silly). Bleugh... the save game location being under My Games is fine, but not in my documents; maybe under Application Data/Local Settings/Games would make more sense. You can't load them except from the game so why have them there.

Isn't youtube great. It's a Dell XPS laptop, the display does this wierd tearing thing like it can't determine the refresh rate of the display. It happens occasionally on Windows, normally in a game when switching display modes. It happens with annoying regularity on Linux under XGL/Compiz, which leads me to the belief that it's a driver problem.

There is no notepad conspiracy

| No Comments
Apparently there is a Notepad conspiracy where it hides certain text from the user. The problem is that it isn't hiding it, it's just guessing the character set incorrectly. Raymond explains things a lot more eloquently than I. It seems to be a similar problem in the gnome notepad (gedit) utility - when it can't guess the character set it puts up a D'oh, I can't interpret this file message.

And another thing...

| 1 Comment
It's the world's most advanced operating system? I mean really, that's an overstatement. Hello! that's nothing more than a brag as there's nothing to back up the statement. Advanced for what? File systems? zfs! observability? dtrace! pretties on the screen? XGL! The ability to perform more than one name service lookup at a time? [ok, that's a cheap shot, I'm sure they fixed this]
Pants, complete pants I tell you!

Commander, my commander

| No Comments
It's an interesting one. I seem to be less capable of commanding troops in Company of Heroes than other games. The problem stems, I think from the feeling I have that the troops in CofH are real people, I empathize with them when I see them being cut to ribbons by machine guns. I hate it when my snipers get shot, and I get angry when a tank runs my men over.
Then we have Warhammer 40k, Dawn of War. I have no compunctions to sending wave after wave of troops at the opposition, slowly eroding their numbers until I can actually wipe them out. Entire squads get wiped out and I just send more in. I just don't have the same connection to them.
Strange that, and it's only a computer game.
It's significantly slower to access a disk than it is to access memory. When given a choice between using a bit more processor power to decompress the information once it's been read into memory or pulling more information from the drive then you really should make the smart choice.
Even if disk space is cheap/free, it doesn't make sense to spend the time reading the information whan you could be using it. The problem is of course, using a sensible and fast compression/decompression algorighm, and also realizing that it really screws with the whole ease of write on the disk. You end up with some change one byte means completely altering the on-disk image so you need to write a lot more information. This is probably why we use transparent compression on files that don't change that much.
Whee, ain't science fun.
Trivial, I know, but some people need to know these things.
From the start we have the header, appinfo (optional), sortinfo (optional) followed by the record entry headers.
The header is:
FieldSizeValue
Db Name32Ascii Database Name
attributes2See DataMgr.h for meaning (dmHdrAttrResDB)
version2Version number an application would use this to decide if the content was compatible
creationDate4Creation date of the database - time from the palm epoch when it was created
modificationDate4Last modifiied date of the db. creationDate by default.
lastBackupDate4date last backed up ina hotsync
modificationNumber4when changed this gets bumped
appInfoID4Offset in this DB to the appinfo block (0 if it does not exist).
sortInfoId4Offset in the DB to the sortinfo block (0 if it does not exist)
type4the 4 character type of the pdb
creator4the 4 character creator of the pdb
uniqueIDSeed4Stumped - I don't know what this is for, leave it at 0
This is the end of the header structure, the next is the record entry information
Record list:
FieldSizeValue
nextrecordList4Address of the next record list (only used for really bit pdbs)
nRecords2Number of records in the recordlist
pad2Number of records in the recordlist
nRecords items4 * nRecordsAddresses of the records in the pdb

The address of the first record usually lies immediately following the appinfo and sortinfo data. As this data is consumer defined, the only way of determining it's size is to use relative calculations. For example, the size of the appinfo structure it's local address up to min(addr(sortinfo, Address of any data records)) - 1. The size of the sortinfo structure is it's local address up to the min(Address of any data records) - 1. They are application defined, and as such should not be messed with.

Finally got XGL working properly

| No Comments
It started off not displaying on the screen correctly - it turns out that I had 16 bit visuals enabled. Changing to a default depth of 24 made that go away. It is really pretty. Prettier than vista. All I need to do is not waste time on it.

credit for gambling thoughts

| No Comments
Well, it's all about the legislation. There has been renewed interest in the legislation considering banning the use of credit cards to pay for gambling debts. Personally, I think that bannig it is incorrect. It should instead be treated as another form of a cash advance. I am reminded of an accountant friend of mine (yes, they do exist) who told me that it's illegal to use one credit card to pay off another (without an agreement). Basically it's all about credit, cash and debt.
Using cash for a bill means you owe nothing after the transaction.
Using credit for a bill means you owe something after the transaction.
Using credit to pay for a pure credit transaction is just plain cheating (and the banks and law says so most of the time, and in most places).
Using credit to pay for gambling debts is nothing more than an extension of the previous point. You should not be using credit to pay for what should be a cash transaction only; because the last thing you want to have is people owing money to 'easy' firms when they should really be owing money to 'hard' firms.
Sarcastic statement? somewhat. I'm a bit of a pragmatist on things like this. The only problem is that I don't believe you should be cheating by laying off a debt for a month when you should be paying money for it.

New power brick

| No Comments
Next business day does mean next business day.

Dead power brick on the laptop

| No Comments
The power brick on the laptop died. I have about 4 hours of juice left. It's the weekend, and support is closed. Thank crap for the warranty and NBD support. Grrrrr.
When I converted the id3 tags to v2.3 all the album art went to the correct format. Interesting issue that. By noting it here I hope that it will help someone else. iTunes has been great for this as it can convert between various tag types (from v1.1 through). I've shoved my collection to version 2.3 tags, and it seems to have solved the problem with the screwed up album art.
Let's see, a bulk change of many thousands of tracks should take all night.

Waiting for Vista

| No Comments
It's my own limited homage to Samuel Beckett. :-)
Direct3D 10 (AKA DirectX 10) is a Vista only item. Considering the driver model changes for Vista I can see how you would have a problem shoehorning the API back into XP. The problem is that they keep delaying the release for various reasons meaning that the games keep being delayed. The longer you delay the OS the later it will be adopted. I'm not planning on an upgrade until I've heard a reasonable amount of feedback as I can't afford to waste time troubleshooting problems. Considering that everything native I'm developing has to be backwards compatible with Windows 2000, my upgrade path has been restricted somewhat.
The new toy has the dreaded 'Windows Vista Capable', which is a tad suspicious.
Christ, but it was annoying - I close the laptop case and shove it in the bag. I pull it out a while later and there's a dialog saying: 'Insufficient system resources exist to complete the API. And my hibernate tab is missing. A bit of googling led me to and entry in Bryce Yehl's weblog. I followed the guide, contacted Microsoft, got the hotfix and now hibernate seems to be working just fine. I'm running Delphi, SQL server Visual Studio have a few mounted CD images and things seem to be just fine - everything hibernates just dandily. All this because I have more than a gig of RAM? That's annoying to say the least.
Disable the Composite extension in /etc/X11/xorg.conf
For me, this solved the problem.
There's a little message in the middle of Adam Curry's Daily Source Code, from a man in Greece about dynamically adding ID3 tags to the file just prior to downloading. I just remembered that Version 2 id3 tags are put at the beginning of the file. All you need to know is how long the tag is, add it to the content-length for the download and the feed the tag out before the file. Adam's idea of the golden ticket is doable without too much processor overhead.
Theoretically, you can embed ID tags into mp3 files once they are out of frame. The property of the tag is that it appears as junk for non-supporting applications. Embedding audio bumpers at the start and finish is trivial (you may need to eat a v1.1 tag at the end of the file).
Other trivial pieces of information I discovered today - WiFi is a pun-term of HiFi, and doesn't stand for Wireless Fidelity, it was simply the name that the ad execs came up with when asked (as 802.11 doesn't have a ring to it). [via The old new thing]
[Listening to: Untitled 4 - Sigur Rós - ( ) (7:33)]

XP on the mac

| No Comments
It's unofficially doable; a few folks won the $14,000 odd prize for it. Practically nothing on the machine works, though. I am reminded of the situation of Solaris on X86 - noone wants it because the hardware support is so poor. The reason for the poor hardware support is that there isn't anywhere the number of driver developers as there would be for such beasts as Linux or Windows.
640x480 (or 800x600 I think) VGA graphics drive, no wifi, no networking, no bluetooth. Pretty much useless from the usable laptop front. I'd take Linux on it before Windows if that's the case. Of course theres a fully functional unix machine under the hood for Mac OS X, and while someone will probably want to shoot me for it, the fact that it's proprietary isn't too much of a big loss.
I was talking to my mate Mark on St. Paddys day about Hyper Threading processors, and he was mentioning that they're not the best at high-performance computation (without extensive and expensive hints in the code). I agreed, mentioning that the latest generation of multi-core processors offer roughly equivalent cost and scale almost multi-processorly. I then went on to explain that the multi-threaded processors are better for I/O workloads, you shove a lot of the scheduling cost back into the silicon where it belongs, rather than having the OS deal with it in software.
For a big server, performing lots of I/O, a multi-core and multi-threaded processor would be the best of both worlds, and based on the direction that Sun is taking with the Niagra system, one can see that this can be taken to a scary extreme - consider 8 core with 8 threads per core all on the one processor module. The power-savings alone would be enough to warrant buying these machines.
Im still waiting for quotes on a few more laptops. I can wait, I just don't know for how much longer. Meanwhile I'll probably buy a phone. Nokia 6230i looks like a cheap and easy option - buyable from €260.19. Or maybe an annoying smartphone like the iMate PDA 2K (it's the original of the O2 XDA IIs).

Network protocol design

| No Comments
Let's make this one easy for the protocol writers. Front load the important information in the packet. That way we can more easily detect it and send it on to the correct handler. Stop putting the decision making information in the middle of the packet. We don't have infinite processor time on these handlers.
This complaint was brought to you by people who care how network bandwidth is being consumed.

I had a nightmare last night

| No Comments
It started out quite simply. I was with a few friends in an internet café just shooting the breeze when I noticed this perceptual shiver run through all the people there. When I asked what was going on nobody was talking. Finaly I convinced one of my friends to tell me and he informed me that one of the folks from the data retention section of the Gardaí was here to install the recording software for the shop.
This was in foot of the new legislation that had been introduced for the storage of all internet communications for an arbitrary time. Every bit was being recorded just in case it needed to be checked at a later time for terrorist activities.
This nightmare took a strange turn when I examined the data gathering software. It was performing a simple data dump of everything that was passing through. Because of the vast quantity of data, nothing was being done to ensure that it could not be tampered with by anyone should they have access to the data. At a later point one of my friends found himself in court facing a criminal charge of conspiracy to commit murder based on the content of one of the logs that had been recorded.
It's scary, but it is possible for it to happen. The question beomes how do we ensure the integrity of the data that is in the recording? If you wanted to prevent accidental tampering with the data, then using some form of checksum on individual blocks of data would provide for that, however a malicious tamperer could simply alter the checksum for the given blocks to prevent their detection. Based on the quantity of information being gathered, you could chain the checksums. Initialize the first block to some random piece of information. checksum it. For the next checksum initialize it from the content of the previous checksum. The principle is used in various encryption systems (Cipher Block Chaining). If you wish to tamper with the data in-stream you need to alter the checksum from the point of alteration to the end of the recording.
As simulteneously you have a program continually writing new blocks of information to the storage device, you would need to either (a) insinuate a program that would alter the checksums as they are written to the device, or (b) interfere with the recording program to possess the new checksum just prior to the next write to the device, thereby having it perform the updating for you.
Both techniques are not impossible to perform, in fact the first is downright trivial. The only way of bypassing this sort of tampering is to ensure that the recording device is isolated in some way from the data that it is recording.
For this purpose, it would need to be a specially assembled recording device which possesses two fail-hot network interfaces as it's only method of communication to the outside world. A fail-hot network interface pair is one that when the power is removed simply keeps the network traffic passing through without interruption.
Secondly it would just record the data, it would have no interpretation capabilities. The reason for this is to remove any chance that it could be subverted through maliciously formed network packets.
The box should be tamper-evident. by having this facility, any efforts to extract the data through physical manipulation of the recording device would be easily noticed, thus rendering the data recorded inadmissable in a court. Tampering with the device would be a criminal offence.
The device would need to be regularly inspected, hot-swapping new devices for old ones so the data recording could carry on uninterrupted.
I encountered a real dunderhead of a program. It claims to be completely NT, 2K and XP happy, yet it doesn't tell us it needs administrator access because it creates it's temp files in C:\, yes, the root of the C drive. There is a perfectly good API available for making good, clean temp files - it's called GetTempFileName. for a bonus there's GetTempPath, which gets you a directory for creating temp files, and this directory stands a really good chance of being user isolated (being that it's %USERPROFILE%\Local Settings\Temp on most NT based OSes). But no, you go and ruin my perfectly working ordinary user program by insisting that you run as administrator. Bloody not written by me sub-programs. You deserve great pain for what you have done.
Uninstalling Visual Studio .NET. It's taking an age. All this so I can install visual studio 2005 and hockey things up again :)
I was experiencing this occasionally when implementing the delete functionality for a browse window. It turns out that I was not positioned on a record, but instead was either before first or after the last record. Simple problem, really but a bit of a pain to discover the reason as the error isn't really informative.
The solution was to do move to either the first or last record. Thanks to the magic of the 'meta bof/eof records'. I have to use the description loosely, as they definitely ain't records and the correctness police would be rapping my knuckles for such a statement.
Discovering the solution was not helped by the browse grid control I was using - it seemed to indicate that I was on a record when in reality it wasn't. Another case of model does not match the implementation.
It turns out that there is a mobile phone version of Wonder Boy. My little sister wants it for her phone dammit! Anyone know how you can get it in Ireland? Legally?

Who's living in what apartment?

| No Comments
It's the COM apartment models. They're related to the threads that make use of COM objects. What happens is that when you initialize COM for a specific thread you declare that it's either Apartment Threaded (AKA Single Threaded Apartment) or Multi Threaded.
When you use the Apartment threading model, it means that the COM object is isolated within the thread that created it. The most important piece of information about this model is that you should never use that object in another thread - it causes brokenness.
When you use the multi-threading model, what you're pretty much saying is that I'm probably going to use this COM object in several threads. The way it works is that a multi threaded model, then the context is shared within the process.
The model you support also puts extra complications on you, the creator of the object. COM objects with a declared MT support must use some synchronization to protect shared information within the object, otherwise you'll suffer from data corruption due to threads walking over the data. You don't have any of these considerations in a Single threaded model - you're guaranteed safe and sane interactions.
Additionally, when you're in COM land, remember never just WaitFor*, but instead MsgWaitFor* things. This also applies to using DDE. This is because the Apartment model uses windows messages under the hood.
[Listening to: TWiT 38: MacWorld Expo - Leo Laporte and the TWiTs - this WEEK in TECH (1:19:01)]
The laptop does suspend and resume to disk, but I have issues with the video card - I use the 855resolution program to allow the video card to set the native 1400x1050 resolution of the screen. When I resume once X kicks in and can't set the video mode correctly I am booted back to a login screen.
I put in an entry in the powersaved post resume from suspend2disk script, and it correctly repatches the video mode before the X server tries to reinitialize the video. The problem is that I need to use the powersave command to shut it down correctly.
I took the alternate route and installed the software suspend2 patch. It provides a cleaner interface to hibernation (the hibernate command); it compresses the file; it supports using a file on disk instead of the swap partition; you can encrypt the hibernation file. It will unmount the windows paritions when I suspend, remounting them when I resume (corruption issues avoided) and runs the 855resolution command before X kicks in. All in all a nicer way to deal with hibernation.
I still haven't got suspend to ram working. Standby works, though.
Now all I need is to get 855resolution working on Solaris and most of my complaints will just vanish into the distance.
Well, tricked out with the new procexp.exe, I wondered how it did it's magic 'replace the task manager' option. It turns out that this magic is from the Image File Execution Options registry key entries. There's a blog entry which describes this feature. process explorer sets itself up as the debugger application for taskmgr.exe, when you try to launch taskmgr, it launches procexp first as it's debugger, which then doesn't launch taskmgr.

Valid windows file names

| No Comments
I keep forgetting this when I decide to accidentally create files with names that turn out to be undeletable under windows. What makes a valid Windows file name? is a blog entry on Brian Dewey's blog and it reminds me that using the \\?\ feature bypasses the MAX_PATH check for a file name's length when trying to manipulate it (or delete it). I've accidentally created files with long names a few times, and been unable to delete them. Thankfully I've created a little application that allows me to do this now.
I wandered into the store yesterday and they have sold out of Xboxes. Now they have a sign up telling people that they can pay for the box now, and expect it in mid January. Well, I would just love to see the face of some snotty little kid on Christmas day, expecitng and Xbox and getting a card saying: 'iou in mid January'.
Liver liver on the wall, who's the swollenest of them all.

New Xboxes for sale in Tralee

| No Comments
Considering al the insanity that followed the launch in the USA with people having to get the next generation console it is refreshing to note that there are Xbox consoles for sale in the Smyths toy shop in Manor West in Tralee. Of course, I'm not going to buy one. I did buy a television on Sunday - the smell of fried capacitors on the old one convinced me that it needed to be replaced.
The folks at Widow PC are building their dual core laptops already - the text on the Sting laptop page tells people that they're starting to ship before Christmas.
Alienware are listing a delivery date of 30 Jan 2006. Cough, no chance then.
Yawn, an article on zdnet news tells us that hyperthreading isn't all that for overloaded servers. All I have to say is duh! that's not what it's intended for. Mind you the benchmark was:
Ocks then detailed testing which showed this behaviour where a system thread — in this case one cleaning out blocks of disk cache memory — is running at the same time as worker threads. "With Intel HT technology, logical processors share L1 & L2 caches. As you would guess [this] behaviour can potentially trash L1 & L2 caches," he said.
It looks like all this information came from a blog posting from a SQL server bod in Microsoft. Hyperthreading was never intended to be a replacement for multiple processors. It is evident from the examples cited that the systems in question were not designed with a two-tier processor model in mind, it treats both processors as if they were actual separate processors, and as such makes utterly bone-headed decisions in scheduling that cause massive performance degredation.
That and the fact that there is a thread that churns through vast amounts of memory deciding to store certain database pages out to disk. Ouch! That sounds a tad slow and broken.
Considering the blog entry and the two comments at the end, I'd have to say that yes, indeed, treating a multi-threaded processor like multiple processors is a bad thing to do.
I just want to wait until after Christmas before making any decisions. Of course by that time the Intel dual core laptops will be just around the corner. Rockdirect have been selling dual core laptops for a while. I need to see some reviews about them. Fun for all the family.
Ooh! this one really annoys me. I'm using iTunes for syncing with my iPod. But when I'm playing music on the desktop if I right-click on anything in the explorer and don't select something really fast iTunes stops playing the music. What a piece of junk.

More phishing

| No Comments
I got an email purporting to be from paypal, asking me to download a security tool that will help protect my computer. Well guess what, it doesn't (well duh!). It turns out to be changing the NameServer entries for all the interfaces in in the registry keys under SYSTEM\ControlSet001\Services\Tcpip\Parameters\Interfaces to something else. Firing up a disassembler, we find out that the value it's trying to set is:
aRstpuucpuucpur	db 90h			; DATA XREF: start+Ao start+101o ...
		db  98h	; 
		db  92h	; 
		db  8Fh	; 
		db  93h	; 
		db  93h	; 
		db  96h	; 
		db  8Fh	; 
		db  93h	; 
		db  93h	; 
		db  96h	; 
		db  8Fh	; 
		db  93h	; 
		db  90h	; 
		db  99h	; 
		db    0	;  
Not surprisingly, this is not a kosher string, it's been encoded. One of the first things the code does is decode this, using the following bit of code
sub_0_40107E	proc near		; CODE XREF: start+Fp

arg_0		= dword	ptr  4

		mov	eax, [esp+arg_0]
		mov	ecx, eax
		mov	dl, [eax]

loc_0_401086:				; CODE XREF: sub_0_40107E+15j
		test	dl, dl
		jz	short locret_0_401095
		xor	dl, 0A1h
		mov	[ecx], dl
		mov	dl, [ecx+1]
		inc	ecx
		jmp	short loc_0_401086
<sarcasm>Terribly complicated</sarcasm>. The code xor's each value in the string with 0A1h. We can step by step this, or you can trust me that it's 193.227.227.218. A traceroute reveals that it's probably somewhere in romania (.ro top level domain)
Tracing route to 193.227.227.218 over a maximum of 30 hops

  1     1 ms    <1 ms    <1 ms  10.0.0.71 
  2    57 ms    21 ms    23 ms  b-ras1.lmk.limerick.eircom.net [159.134.155.24] 
  3    21 ms    22 ms    20 ms  ge15-2.corea.lmk.limerick.eircom.net [83.71.114.97] 
  4    36 ms    36 ms    35 ms  83.71.112.94 
  5    36 ms    40 ms    39 ms  london1-br2-fe0-0.rdsnet.ro [195.66.226.46] 
  6    54 ms    53 ms    52 ms  fra2-cr1-ge5-0.539.rdsnet.ro [62.231.127.89] 
  7     *        *        *     Request timed out.
  8    89 ms    90 ms    87 ms  constanta1-cr1-vlan25.rdsct.ro [212.93.137.51] 
  9    94 ms    88 ms    96 ms  constanta1-fo-vlan10.rdsct.ro [212.93.137.10] 
 10    89 ms   101 ms    90 ms  81.196.163.89 
 11    95 ms    95 ms   101 ms  cr1.micos.ro [193.227.226.254] 
 12    91 ms    96 ms    97 ms  193.227.227.218 

The short answer is never run code that you're uncertain of the origin of, and 2. never run code that you're uncertain of the origin of.

Quick XOR tutorial. Exclusive OR. Either A is true or B is true but not both. so 0 xor 0 = 0, 0 xor 1 = 1, 1 xor 0 = 1, 1 xor 1 = 0. Each hex digit is a run of 4 binary numbers. 0 = 0000, 9 = 1001, A = 1010, F = 1111.
90h xor 0A1h == 1001 0000 xor 1010 0001 = 0011 0001 = 31
Conveniently, 30h = '0', 39h = '9' and 2eh = '.', so it's easy to translate them.

Patch Wednesday

| No Comments
9 XP patches today. I think it was the same last month. This is not a good track record, and wreaks havok on attempting to create a pre-patched OS image for reinstallation.

All Upgraded

| No Comments
Well we're all upgraded to MT-3.2 here. There are a few changes. There's a bug in the archive listing code - The loop counter is incremented three times, during the loop, rather than the once it should be.
I've introduced dynamic theming. The menu option is yet to come, but click the links below to see the different themes on offer.
Old Theme (Using Helvetica) Theme, Old Theme (Using Verdana) Theme, Default 3.2 Theme (Vicksburg) Theme, Beckett Theme, Folio Theme, Masala Theme Powell Street Theme
*** ContextHandlers.pm  2005-09-20 22:34:21.000000000 +0100
--- /home/petesh/.backup/ContextHandlers.pm~    2005-09-20 22:32:47.000000000 +0100
***************
*** 2162,2171 ****
              my ($next_cnt, @next_grp) = $group_iter->();
            while ((($cnt, @grp) = ($next_cnt, @next_grp)) && defined($cnt)) {
                my($start, $end) = $sec_ts->($ctx, \@grp);
-               # Changed by Pete Shanahan - too many i++'s
                  $i++;
                  ($next_cnt, @next_grp) = $group_iter->();
!                 $last = 1 if $n && $i >= $n-1;
                  $last = 1 unless $next_cnt;
                local $ctx->{current_timestamp} = $start;
                local $ctx->{current_timestamp_end} = $end;
--- 2162,2170 ----
              my ($next_cnt, @next_grp) = $group_iter->();
            while ((($cnt, @grp) = ($next_cnt, @next_grp)) && defined($cnt)) {
                my($start, $end) = $sec_ts->($ctx, \@grp);
                  $i++;
                  ($next_cnt, @next_grp) = $group_iter->();
!                 $last = 1 if $n && $i++ >= $n-1;
                  $last = 1 unless $next_cnt;
                local $ctx->{current_timestamp} = $start;
                local $ctx->{current_timestamp_end} = $end;
***************
*** 2182,2188 ****
                    return $ctx->error( $builder->errstr );
                $res .= $out;
                  $first = 0;
!               last if $n && $i >= $n-1;
            }
        } else {
            my $iter = MT::Entry->load_iter({ blog_id => $blog->id,
--- 2181,2187 ----
                    return $ctx->error( $builder->errstr );
                $res .= $out;
                  $first = 0;
!               last if $n && $i++ >= $n-1;
            }
        } else {
            my $iter = MT::Entry->load_iter({ blog_id => $blog->id,

About this Archive

This page is a archive of recent entries in the Computers category.

Complaints is the previous category.

Details is the next category.

Find recent content on the main index or look in the archives to find all content.