42 Astoundingly Useful Scripts and Automations for the Macintosh

Check42 for verifying Astounding Scripts

If you’ve been wanting to type scripts in from the book but were worried about typos, this script will help you find errors in any scripts you’ve typed in from 42 Astounding Scripts.

Jerry Stratton, September 18, 2020

Check42 example run

As I wrote in the intro to 42 Astoundingly Useful Scripts and Automations for the Macintosh, there are two ways to get a script from the book to your computer. You can copy and paste it from the ebook. Or, you can go the traditional route and type it. It was old-school books with the latter sensibility that inspired me to write 42 Astounding Scripts, but I recognized that there were serious issues with that model, mainly to do with catching typos.

The best old-school computer magazine I read back in the day was The Rainbow for the TRS-80 Color Computer. Content-wise, it was very much like 80-Micro, which was the second-best: filled with amazingly cool BASIC programs, tutorials, and hardware projects. But unlike every other computer magazine that included BASIC code, The Rainbow had a program called rcheck and later, rcheck+.

Typos were inevitable typing in even short programs; the rcheck programs were designed to tell you not only that you had a typo but where the typo was, within a defined block of BASIC lines.

The rcheck+ program was so useful I’ve rewritten it in Perl, partly for the old-school community but mainly for myself.

It’s much more difficult to write something like that for modern scripting languages. BASIC had line numbers, which meant there were no blank lines. BASIC didn’t have indentation, which meant there was no worry about tabs vs. spaces. It didn’t even have tab characters in the code, at least on the Color Computer. And because memory was so tight, there weren’t very many spaces, either. There wasn’t even upper and lower case for the most part.

Still, I very seriously considered including a program like rcheck in 42 Astounding Scripts. Ultimately, I decided that it was likely to cause more problems than it solved. But last week I had the epiphany that an rcheck-style script could provide a literal line-by-line checksum, in fact a series of checksums that separate indentation from code, and that I could provide a zip file of those checksums.

Thus was check42 (Zip file, 107.4 KB) born. This script takes a script name as an argument and checks each line of that script against a corresponding line in a checksum file. It looks at (a) the level of indentation, (b) the number of characters in that line of code, (c) the sum of the ASCII values of those characters, and (d) an md5 hash of those characters.

It can thus tell you if you have too much or too little indentation1 and if you have too few or too many characters.

The sums warning is more complicated, but it is very useful once you get a sense of how your own typos tend to go. The most obvious is if check42 tells you you’ve got one too few (or too many) characters and the sum is off by 32. Most likely there’s a missing or an extra space.

If the number of characters in the line is correct and the checksum is correct, it will also warn if the md5 hash is off. This tells you nothing about the error other than that it exists, but at least it’s on a per-line basis.

The line number that it tells you the error is on should let you find the line in your text editor; Textastic, for example, provides the line number to the left of each line.

Let’s say that you accidentally typed a capital R on the fourth line, a lowercase l on the fifth line, and you accidentally typed in French on the sixth line:

4. mkdiR -p $HOME/bin
5. FIlE=$HOME/bin/$1
6. touche "$FILE"

The script will tell you almost exactly where the typos are:

  • % check42 edit
  • line 4’s character sum is low by 32
  • line 5’s character sum is high by 32
  • line 6 has 1 too many characters
  • line 6’s character sum is high by 101 (e)
  • %

It tells you that line 4 is low by 32, and line 5 high by 32. Thirty-two is the difference in ASCII value between upper and lower case characters. Because those lines have the correct number of characters, you can guess that whatever your typo is, it is an incorrect character or characters.

It tells you that line 6 has one too many characters, and then tells you what that character probably is: a lower-case ‘e’. If the line were off by more than one character, it becomes impossible to guess what the character is, so the script won’t try. Even when the character count is only off by one, the guess will be wrong if there are other typos in the line.

This script works only on command-line scripts. It won’t check (or find) AppleScripts.2 It looks in the ~/bin directory that the book directs you to create for storing your scripts.

The zip file (Zip file, 107.4 KB) includes checksum files for all ~/bin (command-line) scripts in 42 Astounding Scripts, as well as the latest copy of check42. The checksums are for the versions of the scripts in the fifth printing of the book, which is the first printing for Catalina.

It looks in ~/Documents/scripts/checksums for the checksum files. The zip file will create the scripts folder for you when you unzip it, but you’ll need to drag it to your Documents folder.

So if the idea of typing scripts in from the book appeals to you, but tracking down typos does not, try check42 and see if it helps!

[toggle code]

  • #!/usr/bin/perl
  • # create checksums for scripts, per line
  • # verify checksums against checksum file
  • # Jerry Stratton, AstoundingScripts.com
  • use Digest::MD5 qw(md5_base64);
  • $binDir = $ENV{'HOME'} . '/bin';
  • $checksumDir = $ENV{'HOME'} . '/Documents/scripts/checksums';
  • $tab = "\t";
  • $scriptName = shift;
  • if ($scriptName eq '--create') {
    • $createChecksums = 1;
    • `mkdir -p $checksumDir`;
    • $scriptName = shift;
  • }
  • die("Scriptname must be provided.") if !$scriptName;
  • $scriptPath = "$binDir/$scriptName";
  • $checksumPath = "$checksumDir/$scriptName.txt";
  • die "No script $scriptName in $binDir.\n" if !-f $scriptPath;
  • die "No checksum file for $scriptName in $checksumDir.\n" if !$createChecksums && !-f $checksumPath;
  • @ARGV = ($scriptPath);
  • while (<>) {
    • $line++;
    • chomp;
    • s/^(($tab)*)//;
    • my $indentation = length($1)/length($tab);
    • my $lineSize = length();
    • my $checksum = 0;
    • for $character (split //) {
      • $checksum += ord($character);
    • }
    • my $hash = md5_base64($_);
    • $binLines[$#binLines+1] = "$line\t$indentation\t$lineSize\t$checksum\t$hash";
  • }
  • if ($createChecksums) {
    • die("Unable to open $checksumPath for writing: $!") unless open($checksumHandle, '>', $checksumPath);
    • print $checksumHandle join("\n", @binLines);
  • } else {
    • die("Unable to open $checksumPath for reading: $!") unless open($checksumHandle, '<', $checksumPath);
    • while (<$checksumHandle>) {
      • chomp;
      • next if /^$/;
      • ($line, $indentation, $lineSize, $checksum, $hash) = split("\t");
      • ($binLine, $binDentation, $binSize, $binSum, $binHash) = split("\t", $binLines[$line-1]);
      • die "source file $scriptPath ends too early at line $line\n" if $line > $#binLines+1;
      • die "line $line: line numbers do not match ($binLine)\n" if $line != $binLine;
      • $indentError = $binDentation - $indentation;
      • $sizeError = $binSize - $lineSize;
      • $sumError = $binSum - $checksum;
      • print "line $line has ", abs($indentError), " too few tabs\n" if $indentError < 0;
      • print "line $line has $indentError too many tabs\n" if $indentError > 0;
      • print "line $line has ", abs($sizeError), " too few characters\n" if $sizeError < 0;
      • print "line $line has $sizeError too many characters\n" if $sizeError > 0;
      • print "line $line’s character sum is low by ", abs($sumError) if $sumError < 0;
      • print "line $line’s character sum is high by $sumError" if $sumError > 0;
      • print ' (', chr(abs($sumError)), ')' if $sumError && $sizeError == 1;
      • print "\n" if $sumError;
      • print "line $line has a typo (digests unequal)\n" if !$sizeError && !$sumError && ($hash ne $binHash);
    • }
  • }
  • close $checksumHandle;

For Extra Credit

Here’s an example of the text file for the very simple edit script included in the book:

lineindentationlengthchecksumhash
1010816H2BePwrj+H8Z+tHxm6nneg
201917279HFCZrGlfwI4BGc0z5ojHA
30001B2M2Y8AsgTpgAmY7PhCfg
40181449rOcsbcccDNiCkwZYAio9LQ
50171174VpdxV22M9MpEQ4BrZhgWjg
60139710eSPX0y+MyD8PPcJx2lFdg
70171259AjdvdgOlQ2r7qxk7Bo+S9A
80151051ULiAfIWOJb9AMyP9NHoSLA

You shouldn’t have to ever look at it, but if you do, you can orient yourself by looking at the triple-zero lines. Those are lines with nothing in them. In the edit script, line 3 is an empty line. If you look at your own copy of the edit script, you should see an empty line at line 3, if you haven’t changed it from the version in the book.

If you prefer spaces to tabs for indentation, you can change the $tab variable to whatever number of spaces you prefer.

If you prefer to store your scripts in a directory other than bin, or you prefer to store your checksum files elsewhere, those variables can also be changed at the top of the script.

Any changes you make to check42 will, of course, mean that check42 will no longer match its own checksum file.

You can even use check42 for scripts you’ve written yourself, if you pass them out for people to type in. The command check42 --create <scriptname> will create a checksum file for that script and put it into your checksum folder.

  1. A minor problem for most languages, but not Python.

  2. Unless those AppleScripts were meant to be command-line scripts.

  1. <- Edit (Zsh)