QZ qz thoughts
a blog from Eli the Bearded
Tag search results for code Page 1 of 3

Matched Pairs


In vi (and vim), there's a "motion" command, % that moves you to an enclosing symbol. In the previous sentence, with the cursor on the "v" of vim, using % will move the cursor to the "(", which is the start of the enclosed sequence. On that "(", the % motion moves to the matching ")".

Out of the box, vi knows the pairs "()", "[]", and "{}". You can change the pairs with the configuration variable matchpairs and people frequently do to add "<>" for XML or HTML work:

set matchpairs=(:),[:],{:},<,>

But there are a lot more, like quoting angles "«»" and smart quotes. And vim happily accepts UTF-8 characters for each half of a pair. So I could think up some Unicode pairs and stick them in there. Or I could look for all pairs that exist in Unicode.

Here's a stab at doing just that.

First off, we need the list of characters in Unicode. This is surprisingly easy to get. Unicode themselves provide an easy to parse list of characters in plain ASCII(!).

$ curl -sO http://www.unicode.org/Public/UNIDATA/UnicodeData.txt

The list is not fixed, new stuff gets added with each version of Unicode. Releases happen every 12 to 18 months. Refreshing that file is the major change needed to update my Unicode Toys for a new version.

So how about a quick script to find "LEFT" characters that have a matching "RIGHT" version?

#!/usr/bin/perl
# Read the UnicodeData.txt file to create a vim 
# matchpair list.
#
# May 2022 "Eli the Bearded"
use strict;
use warnings;

my $in = 'UnicodeData.txt';
my %found;
my %pool;
my %check;
my $id;
my $pid;
my $name;
my $count = 1; # first pair is the hardcoded one

# Tag characters are an obsolete invisible set of
# ASCII for hidden metadata. Modifiers and Combining
# should not be used on their own. Arabic are not
# left-to-right text, so I decided I don't need them.
# You may decide otherwise. The others, by inspection,
# don't have anything I'd want as a pair. (Some
# are part of of larger sets, like up/down/left/right
# quads, or parts of multicharacter pictures.) This
# still leaves some unlikely pairs including box drawing
# stuff. It's a quick list.
#
# These are checked with word boundaries, so CIRCLE
# will not skip CIRCLED.
my @skip = qw[ TAG MODIFIER COMBINING ARABIC IDEOGRAPH
	       ARROWHEAD ARROW
	       AFFIX CIRCLE HALF
	       UP UPWARDS
	       DOWN DOWNWARDS
	     ];
my $skip = join('|', @skip);
my $skip_re = qr/\b(?:$skip)\b/; # \b for boundary

# no boundary check, allow "leftfacing" and the like
my $keep_re = qr/(?:LEFT|RIGHT)/;

binmode(STDOUT, ':utf8');
open(STDIN, '<', $in) or die;

while(<>) {
  # keep code point and name only
  /^([^;]+);([^;]+);/ or next;
  $id = $1;
  $name = $2;

  # Stop checking if on skip list
  next if /$skip_re/;

  # if left or right, keep, but separately
  if (/$keep_re/) {
    if (/RIGHT/) {
      $check{$name} = $id;
    } else {
      $pool{$name} = $id;
    }
  }
}
close STDIN;

for $name (keys %check) {
  my $pair = $name;
  # %check has RIGHTs, see if there is a matching left
  $pair =~ s/RIGHT/LEFT/g;
  if (length( $pid = $pool{$pair} )) {
     $id = $check{$name};
     $found{$pid} = $id;

     # In .exrc or .vimrc " is used to begin a comment.
     # These three printf()s just document the pairs.
     printf(qq{" U+%s\t%c\t%s\n}, $pid, hex($pid), $pair);
     printf(qq{" U+%s\t%c\t%s\n}, $id, hex($id), $name);
     printf "\"\n";
     $count ++;
  }
}
print STDERR "Found $count pairs\n";

# Unfortunately < and > are not named with LEFT and RIGHT
# so hardcode that.
printf "set matchpairs=<:>";
for $id (sort { $a cmp $b } (keys %found)) {
  $pid = $found{$id};
  printf ",%c:%c", hex($id), hex($pid);
}
printf "\n";
__END__

Saved as matchmaker, with the Unicode data file in same directory, let's try it.

$ perl matchmaker >> .vimrc
Found 186 pairs
$ tail -1 .vimrc
set matchpairs=<:>,(:),[:],{:},«:»,֎:֍,܆:܇,࿖:࿕,࿘:࿗,𐡷:𐡸,𝄆:𝄇,𝅊:𝅌,𝅋:𝅍,👈:👉,🔍:🔎,🕃:🕄,🕻:🕽,🖉:✎,🖘:🖙,🖚:🖛,🖜:🖝,🗦:🗧,🗨:🗩,🗬:🗭,🗮:🗯,🙬:🙮,🤛:🤜,🫲:🫱,🭪:🭨,🭬:🭮,🭼:🭿,🭽:🭾,🮜:🮝,🮟:🮞,🮠:🮡,🮢:🮣,🮤:🮥,🯇:🯈,‘:’,“:”,‹:›,⁅:⁆,⁌:⁍,⁽:⁾,₍:₎,⇇:⇉,⊣:⊢,⋉:⋊,⋋:⋌,⌈:⌉,⌊:⌋,⌍:⌌,⌏:⌎,⌜:⌝,⌞:⌟,〈:〉,⌫:⌦,⍅:⍆,⎛:⎞,⎜:⎟,⎝:⎠,⎡:⎤,⎢:⎥,⎣:⎦,⎧:⎫,⎨:⎬,⎩:⎭,⎸:⎹,⏋:⎾,⏌:⎿,⏪:⏩,⏮:⏭,⏴:⏵,┤:├,┥:┝,┨:┠,┫:┣,╡:╞,╢:╟,╣:╠,╴:╶,╸:╺,▉:🮋,▊:🮊,▋:🮉,▍:🮈,▎:🮇,▏:▕,▖:▗,▘:▝,◀:▶,◁:▷,◂:▸,◃:▹,◄:►,◅:▻,◜:◝,◟:◞,◣:◢,◤:◥,◰:◳,◱:◲,◸:◹,◺:◿,☚:☛,☜:☞,⚟:⚞,⛦:⛥,❨:❩,❪:❫,❬:❭,❮:❯,❰:❱,❲:❳,❴:❵,⟅:⟆,⟕:⟖,⟞:⟝,⟢
 :⟣,⟤:⟥,⟦:⟧,⟨:⟩,⟪:⟫,⟬:⟭,⟮:⟯,⥼:⥽,⦃:⦄,⦅:⦆,⦇:⦈,⦉:⦊,⦋:⦌,⦍:⦐,⦏:⦎,⦑:⦒,⦗:⦘,⧘:⧙,⧚:⧛,⧼:⧽,⫍:⫎,⫥:⊫,⬱:⇶,⮄:⮆,⮐:⮑,⮒:⮓,⯇:⯈,⸂:⸃,⸄:⸅,⸉:⸊,⸌:⸍,⸜:⸝,⸠:⸡,⸦:⸧,⸨:⸩,⸶:⸷,⹑:⹐,⹕:⹖,⹗:⹘,⿸:⿹,〈:〉,《:》,「:」,『:』,【:】,〔:〕,〖:〗,〘:〙,〚:〛,꧁:꧂,﴾:﴿,︵:︶,︷:︸,︹:︺,︻:︼,︽:︾,︿:﹀,﹁:﹂,﹃:﹄,﹇:﹈,﹙:﹚,﹛:﹜,﹝:﹞,(:),[:],{:},⦅:⦆,「:」
$

There are a lot of good pairs in that. But some pairs might need to be switched for taste. (Looking at those hands.)

getcert


I've typed these commands in by hand dozens of times over the years. But today I decided, "No more, script it."

So here's a simple script that takes a hostname, with optional port, and optional output filter. It makes an SSL connection to the given host, and extracts the certificate presented. With the validity filter, you can see the dates the cert is good for. With the dns filter, you can see hostnames the cert covers.

#!/bin/sh

host=$1
port=443

full() { cat; }
valid() { grep -A2 'Validity'; }
dns() { grep -E '(Subject.*CN *=|DNS *:)'; }

case "$host" in
        *:*) connect="$host" ;;
        ?*)   connect="$host:$port" ;;
        *) echo "usage: host[:port] { full | valid | dns }"
           exit 2
           ;;
esac
filter="full"

case "$2" in
        full) filter="full" ;;
        val*) filter="valid" ;;
        dns*) filter="dns" ;;
esac

: | openssl s_client -connect "$connect" 2> /dev/null |
    openssl x509 -noout -text |
    $filter
exit

Some examples of it in use:

$ getcert www.cvs.com dns      
        Subject: C = US, ST = Rhode Island, L = Woonsocket, O = CVS
Pharmacy Inc, OU = CVS Caremark Corporation, CN = www.cvs.com
                DNS:www.cvs.com, DNS:www.minuteclinic.com,
DNS:www.cvshealth.com, DNS:voices.cvshealth.com,
DNS:videovisit.cvs.com, DNS:vaccines.cvs.com,
DNS:vaccines-west.cvs.com, DNS:vaccineclinicscheduler.cvs.com,
DNS:tnl-photo.cvs.com, DNS:t.cvs.com, DNS:static.cvs.com,
DNS:services.cvshealth.com, DNS:services.cvs.com,
DNS:scheduling.minuteclinic.com, DNS:r.cvs.com, DNS:photohelp.cvs.com,
DNS:photo-store.cvs.com, DNS:photo-smetrics.cvs.com,
DNS:photo-metrics.cvs.com, DNS:payments.cnpapi.cvs.com,
DNS:optical.cvs.com, DNS:mypassword.cvshealth.com,
DNS:mypassword.cvs.com, DNS:myhr.cvs.com,
DNS:mychart.minuteclinic.com, DNS:mobile.cvs.com,
DNS:minuteclinic.com, DNS:message.cvs.com, DNS:meddplanfinder.cvs.com,
DNS:m.cvs.com, DNS:i.cvs.com, DNS:health.cvs.com, DNS:flushot.cvs.com,
DNS:express.cvs.com, DNS:es.minuteclinic.com, DNS:es.cvs.com,
DNS:entservices.cvs.com, DNS:e.cvs.com, DNS:depservices.cvs.com,
DNS:ddl.cvs.com, DNS:d.cvs.com, DNS:cvs.com,
DNS:citrix.minuteclinic.com, DNS:circular.cvs.com,
DNS:catools-photo.cvs.com, DNS:cvsh-tv.cvs.com, DNS:c.cvs.com,
DNS:seo.cvs.com, DNS:s.cvs.com, DNS:ds.cvs.com, DNS:design.cvs.com,
DNS:alvie.cvs.com, DNS:smartbytes.minuteclinic.com, DNS:care.cvs.com,
DNS:www.care.cvs.com
$
$
$ getcert www.nato.int valid
        Validity
            Not Before: Dec 17 14:09:55 2020 GMT
            Not After : Dec 31 22:59:59 2021 GMT
$

There you have it. CVS has a list of subject alternative names (SANs) as long as a CVS receipt, and you now know what to get NATO for xmas.

ASCII art and "ASCII" art


I first had email, and first saw a :-) smiley face in 1990. It was about that time, also, that I was exposed to concrete poetry. Both the smiley and the pictures of words poetry were influential on my mind.

I've had a long lasting love of ASCII art and things near to it since. The first program I saw that could create ASCII art from another image was pbmtoascii from the netpnm package. It worked, but it was kinda disappointing. I had seen, and even made myself, hand drawn ASCII art that was much better than the output of that tool.

$ pnmscale -w 60 < fine.pgm |pgmtopbm |pnminvert |pbmtoascii
 ooo" M o"o" oMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMo"
M oo"o" M o" MMMMM"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM "
o"o M "o M "oMMMMM"oM "MoM oMMMMMoo""MMM"ooM"o""" oMMMMMMM "
o" " M o"o"  MMMMMMMoMoMoMMoMMMMMoMMoMMMMoMMMoMMoM"MMMMMMM "
o""oo o o o"o""MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM o"
o"M M M"oM M "o""MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"o"o"
"o"" "" o"" ""o"ooo""""M"M"MMMMMMMMMMMMMMMMMMMMMMM""" o"o"oM
MM"MMMMMoo"M"oo ooo"oooMoMoooooo MMM M"o"oo" ooooo"o"M o ooo
MMMM"MM oM"oM"oMoM""MMMMM"MMM"MM MM MMMMMMMMMooooooooooMMM o
""MMMMM Mo"" "   o"M MoMMMMMMMMo"MM MMMoMMM"MM"MM"MM"MMMo"oM
 MM"MM" o     o MoM"o"M"MMoMMMM MM"MMM"MMoMMMMMMMMMMM"MM"o"M
 MMM o"Mo"Mo"o"M Mo" M""MMMMMoM M"oMMMMM"MMMo"MoMMoMMM"o"M"M
 M"o"o"oM" """" "ooM"oMo"MoMMMM"oMMMMM"MMMMM"o"M"MMM""oMMM"M
o"oM"oMo "M"M"M"Mo"oM"o"ooM""MooMMMoMMMMMoM"oM MMM"o"Mo"o"Mo
M Mo"M" "M"oM oMoo""o"oMM  " "MMMM"MMM"MMM"oMM MM MM"Mo"M"oM
"MoM"   o"M MMM"""MM MM"     "M"MMMMM""""M"M"Mo"oM"M"oMoM"Mo
"Mo"     "oMMM     "oMMo    o MM"""       "M MoM"M"oMMooMM"M
"M    o  M MMM      "oMo "   o"oM     "    MM MM"oMMo"MoMM"M
M        "o"MMo    o M M""" M"Mo     o    M"MoM"oMMoMMMoM"M"
o   "    ""o""Mo"o o"M"oMM"M"oMoo o     o MMMoM MMo"MoMM"M"M
         M"M"oooMo"M M"oM M"oMo"ooo oo"M MMoMM"MMoMM"MoM"MoM
o "  "   M"o"M M M "oo" M"oMo"oMo"oM"o"o"MoM"M"MoMMMMMoM"MoM
M       M"M M"oMoo" M "oo " ""o M oooMM"MMo"MMM"MoMoMM"M"MoM
o Moo"o"M M"o"oM M"o" o   "o"MMM"oMoMoMMMo"MMoMM"o"" """"o""
 "oo" M"o"M M"o"oM Mo"oo oo"o"Mo"MooMMoM"M"MoM"Moo""""" " M
o"Mo "o" M "o"o"o"o"oM M oo"Mo" MM MoMM"M"Mo"M"Mo"MMoMoMMMM
 MM "M M M M M M M"o"o"oM M "oMo"Mo"MoMM"M"oMM"Mo"M"M"MoMMM
"oMo""M "o M M M M M M"o"o"o"oM"Mo"MMo"MM"M"oMMMo""MMM"MoMM
"oM "M M Mo M Mo"oM M M M Mo "Mo"oMMoM"MoMM"M "oo"""o"""o"o"
"o"o"oMo M M M"oM M"o"oM Mo" MM"M"oM"M"Mo" oo"M M""Mo"M"oM M

Eventually I learned there is a higher quality option to the program.

$ pnmscale -w 120 < fine.pgm |pgmtopbm |pnminvert |pbmtoascii -2x4
//,|>|i:(<>/|,HMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM?`S
SSS?!:?</:?? MMMMH##MMMMMMMHMMMMMHMHMMMMMMMMMMMMMMMMMMMMMH`S
??|!|::?://S!MMMMM}{&|'RTk:=MMMMM|?>dMMMk|qHT|9:?/:MMMMMMM S
::\\>\\\:i|>`HMMMM6HHJbM|Md?MMMMHJ6$dMMMkdMRJLHd?P&M]MMMM}i:
SSS>\:\,,:<??:HMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMH,\:
SS|$?S4?(L||/S>/*MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM",}$\
\\</|+`:?:`^`<??>,??"*##HHHHMMMMMMMMMMMMMMMMMMMMHH*"?:,/($([
RH&HHHH6'<&ST&v:$???$vd&pbqdoooo.MMP$$$?$$??::<v>\\\%%>/.vv,
M9MHH6R|S}&\S1%$?H*/H9H9RM9MH6M&:MM:HHH6H6H6H#\b????vo#H&M!?
<9&6M9M:F1>?^'''.<$[\9R6MHHHDM&}|MH:MRHRM6RMH9R6H6MHH&H9H/?k
|HHHHH/\:.    ./$S1S|JRHMHH6M9M!HM\M&RMHHHH&H6MHHHHHRMH&P|&H
:&M$?\$$$Z$}1?$S$>$?v??#RMMHHMH.H\HRMHH&RM9M9T&RM9M&M&F?/Z&&
:H?&|;4S%\??<^????>$&$S+:#HMHHP|?MH6M9RM96M&P??&6HHM>?d&1&$k
|&iD?D#d:&)&ZZ$\?F1%F$$$$?Z?HModH6MH9MHR6M&R|H/H6M$?S&H$|19|
&:/S&]>-?$S$>???$?<$Z+dM*"`"\?MHHHHHHHHHMH$d9&|9*>&$H&?${&$9
HFS$/-  :$S\MMM*"*Hb`HMT .- -`MM9M9RM**+#bR&$&L\{S6H>b$L$&kR
$Fk? .` :Z|MMR- .  ?|&M: .. -:Md#>*'  . .``H?&d1H&>b&kH+RH$&
DZ' . .  &:MM|.- ` .??Mc: ., $v$/-  `. .. .&9)DH$?HH&1&FkM&&
R  - . . |\?MM\ : -.\<?$PP??{47Z- . - .  .d&1c>$$9HH&1&FkkHZ
: .- -   |S}?*&qv\`,&Z/S$$$Z$k&S:   .   >,H&1&k?6&9H&H&S&$9{
 -. `.`  %&4%F>><1Z/|%$$$?FS$$Z/S\;_\v\&\/RH&H$HH&&DR66&&9S&
:- - . `.9%&4SFS$?!?o:<?$$\%Tk$S1%Z$$>::&6kM&FHH&RH&RH19&$9&
b.- -  /7/Z|$7/$?://?$-\b:?/'!???:?v/&M&&kH\6&HH&19H1&&19&19
//?+>|\$$%}S1?$S|k%|//:   '|?HRMP/FPd9&RH17H&&&FR/+^$???$$?\
:S$|S:%Z>}S$?($??|}Z\\\/,v$>??HT/&ZS&&9&1H?M%HH&|???"":`""?:
!ZF$ /||:S:(|||\\\4|?%|$S;v?&1;:H&>&FDFRH$1}&FkR|&9&H&#9&RM:
i$&||)S/?(|))))))?S??S4|%Z/|Z}&/&7/FRHFDF&k$RH&&|:M&DHHH&RH-
i&$||S}!%&:?SS?((((((((|$$?<:$$FR%FFk&9&S$&H$$R6k|9HFRH9&&M.
|&?||SF>|%Z:$S?SSSS?\\\??\\\`&$$|&&&&ZH&19?&&<???:?+?\<+*++|
|Z$\:S$$ $Z:\(}S4%$?L$$)?Z%||&ZH&&&&Z&&&1^`v>T/Z\\$&)F$$$14|

But that still seemed lack luster to me. About 21 years ago, the date I have in the file is 27 Dec 1999, I wrote my own tool that turns gray scale ("pgm") images instead of bitmap ("pbm") images into ASCII art.

pnmscale -w 60 -h 30 < /tmp/fine.pgm | pgmtoascii
zzxxxzmmzzmzzxOWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWmxz
mmmmmzzzzzzz;WWWWMOOWWWWWWMMWWWWWWWMWWWWWWWWWWWWWWWWWWWWWM,z
mmmmzzzzzzzzxWWWWWYYUmxOYCxXWWWWMXXzUWWWUzUOUXUmYzzWWWWWWM,z
zxzzzzzzzzxz,MWWWWOMOUYMYWUXWWWWOYOUUWWWCUWOUUOUXYUWOWWWWY+z
mmmzx++;++xzmxOWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWO+z+
XXXYYYYYYXmmzmxzUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWMYzUXm
mmmmmzzzmxmzxzXXmzmXYUCOMMMWWWWWWWWWWWWWWWWWWWWWWOUYXzxmXYYY
OOCCCMMOzmYUYXz+XmmmXmXUUYYYXXXm;WWUYYYYYYYmxmXmzmXXXXmx,xzz
MMMMMMMxYUUUUUUUmOUUOMMMMMMMMMMO+WWxMMMMMMMMMUXXXmmzzXUCOMmm
UCMMMMM+UUYmx;,.,zUYzCMMMMMMMMMUXWMzMMMMMMMMMMMMMMMMMMMMMUmC
+CMMMMUz+,    ,zUUUUmYMMMMMMMMMzCWzOMMMMMMMMMMMMMMMMMMMMUmCC
+OMUmzYUUUYXmXUUUUYmmXXCWMWWWMWxMmOMMMMMMMMMMYOMMMMMMMCmYCCU
+OYUmxUYXmmmmmmmmzXUUUUmzCWWWWUmXMMMMMMMMMMMCmmMMMMMCXXCCCCC
xUmCXCUY+UUUUUUUUUUUUUYYUmUUOWXCMWMMMMMMMMMCmCzOMMUXYCOYXCCY
U+UYUUU;mUUUXXXXXXmYUXCWUx;++UMMWMMMMMMMMMUYCOmCUXUCCCYYUCUC
OCUUX;. +UUXMWWUXYOYzOWX.....;MMMMWMMUUUUCOCUOYxYCOOUYUXUCCC
CUCz... ,UmMWM;....XYUW;.....+MOUUX;......zCXCUUOOUYCCCUCCCC
UU;.... .YzWWU......mXWU,..,,zXXX;  .......CCXOOUYCOCCCCCCCC
U,.....  mXYWW+....,+XmYYXmzYUUU,   ...  .YOOUUUUOOCCOOOCCCU
+......  zUXmUCXx;;zYUUUUUUUUUUU,      .x+OOOOUXOOOOOOOCCCCU
.......  XUUUUXXXXYYYYUUUYUUUUUUYz+;+xmUzUOOOOCOOOOOOOCCCCUC
+.....  ;UUYUUUYYXzmmxmXYYYUUUUUUUUUYXmmUOCOOOOOOOOOOOOCCCCC
U,.... ;YYYXYYYYXmxzXY,Xm+zxzzzmmxmmmUOOOCOUOOOOOOOOOCOOCCCC
xzXzz;XYYYYYYYYYYYYzxz,...,mXMMWUYOUUOOOOOCUCOOOUYXXXXXXYYYY
+XXXm+YYXYXYYYXYYYYYYmz+;xXXXXOUXCUYOOOOCOUOUOOOmXmx+++++xm+
xUUY;XXmzmxmXXXXXXXYYYYXmzzmCUzxCCXCOOOOOUCUCOOOXUMOCUUCOOO+
mUUmxYYzmX+zXXXXXXXXYYYYYYYxYUUYCUYCCCCOCCUUCCOOXXOOMOOOOOO+
mUUzmUUxmYzzXXXXXXXXXXXXXXXX+UUCCYCCCCCCCCUCCOOOUzOOOOOOOOO;
mUUxzUUzzYX+YYXXXXXXmXXXXXXX,CCUXUCCCCCCCCUCUXXXXxXXYYYYYYmm
mUUm+UUY;YU+YYYYYYYYYYYYXXYmxCCCCCCCCUCCCz+xmYYYUUUUUUYYYYYY

Later I did things like convert ASCII art back into images in various ways. asciiversion2color is not a great program (and didn't even work for me today, because pamarith has been is named pnmarith on this system, a hazard of the netpbm tools being old and reaching different places from different paths, but when I did git it working:

$ pnmscale -w 120 fine300.ppm > fine.ppm
$ asciiversion2color fine.ppm textfine.ppm
pnmarith: promoting second file to PPM
pnmarith: promoting second file to PPM
pnmarith: promoting second file to PPM
HTML version in textfine.ppm.html
$  cjpeg textfine.ppm > textfine.jpg
This is fine, in three color ASCII art

asciiversion2color creates three pbmascii images, one for each RGB channel (in the "ppm" pixmap), converts the ASCII to text-in-image form, then combines the three with a slight offset.

And texttopnm lets you specify an arbitrary tile for each character, as a way of introducing fonts. It has some code for UTF-8 fonts, but I've mostly used it with historic fonts.

$ (echo scale=5; echo 9/14 ) | bc
.64285
$ pnmscale -w 60 fine300.pgm | pnmscale -yscale .64285 > fine60.pgm
$ pgmtoascii fine60.pgm > fine60.txt
$ texttopnm -s .ppm -d font/9x14 fine60.txt 9x14.pgm
$ pnminvert 9x14.pgm |pnmtopng > 9x14.png
This is fine, IBM circa 1981 style

The 9x14 font is the IBM Monochrome Display Adapter hardware font from circa 1981. Quoting Wikipedia:

Each character is rendered in a box of 9×14 pixels, of which 7×11 depicts the character itself and the other pixels provide space between character columns and lines. [...] Each character cell can be set to one of 256 bitmap characters stored in ROM on the card, and this character set cannot be altered from the built-in hardware code page 437. The only way to simulate "graphics" is through ASCII art.

Most recently, I found someone else's code that was created for emulating monochrome bitmap graphics using Braille characters in Unicode in terminal windows. I loved it immediately, and forked it to add a pbmtodots still image converter.

⢅⢌⢔⠔⡔⡪⢊⢆⢣⢒⢕⢱⢑⢡⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣆⠑⡪
⠕⡕⢕⠕⡱⡡⡃⡇⢕⠜⡰⡱⢀⣿⣿⣿⣿⡿⠿⠿⣿⣿⣿⣿⣿⣿⢿⢿⣿⣿⣿⣿⣿⢿⣿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⠈⡪
⡣⡣⡣⢃⡣⡊⡢⢃⢕⢡⠪⡰⢨⣿⣿⣿⣿⣿⡗⢼⣞⠸⠃⣟⢹⣯⡐⠶⣿⣿⣿⣿⣿⢸⣅⡒⠿⣿⣿⣿⣗⠰⢶⣾⢹⡇⢻⠩⠏⠔⣂⣿⣿⣿⣿⣿⣿⣿ ⡪
⢌⢌⠪⠪⡰⢑⢅⠇⡆⡣⠱⠱ ⣿⣿⣿⣿⣿⣯⣽⣷⣸⣥⣿⢸⣿⣍⣥⣿⣿⣿⣿⣷⣸⣯⣩⣵⣿⣿⣿⣗⣼⣿⣟⣸⣇⣷⣼⣌⡛⣛⣿⣹⣿⣿⣿⣿⡗⠠⡣
⢕⢜⢜⢔⠔⡰⡠⡀⡄⡔⢔⢔⢕⡈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠠⡅⣁
⡪⡪⡪⡎⡏⡮⡺⡱⡝⣜⡜⡔⡅⡣⡢⡉⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠛⡰⡽⢜⠔
⠕⠏⠞⠎⠇⠗⠙⣁⣍⡈⠚⠓⠑⠣⡣⣣⣒⢆⢍⣉⠛⠟⠿⠿⢿⢿⢿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡿⠟⠛⢋⢡⢄⠞⠝⡵⣱⢫
⡿⣾⣳⣷⣯⣿⡿⣯⠃⣔⢏⣏⡯⣒⢆⡡⢬⣡⣡⣡⣩⣤⢦⣾⣴⡶⣴⣴⣤⣤⣤⣤⠠⣿⣿⡟⣩⣩⣩⣉⣍⣩⣉⣉⠡⠥⣥⢤⡢⡆⡧⡫⡣⡳⡱⠑⠂⡤⣄⣅
⣿⢿⣽⡷⣿⢾⣟⠇⢼⢕⡯⣪⣺⢕⡯⣲⡩⢷⠟⠞⢿⡾⣿⢷⣯⣿⣻⣾⣻⣯⣿⡽⢐⣿⣿⡂⣽⡿⣽⣯⡿⣯⣿⣻⢿⣶⢦⣥⣡⣉⣌⣨⢤⣤⣴⡿⣽⣻⠇⡙
⡟⢻⣯⣿⣻⣿⣿⡈⢧⡳⡫⠚⠘⠈⢀ ⢀⢴⢝⡵⣊⢽⣟⣿⡷⣿⣻⣯⣿⡾⣷⡏⢸⣿⡿⢐⣿⣟⡿⣾⣟⡿⣾⣻⢿⣽⢿⣽⡿⣯⡿⣯⣟⣿⣯⡿⣯⡟⣨⢷
⡂⣽⡷⣿⣻⣾⠻⡃⡁⡁  ⠐ ⢀⡴⡹⣕⢯⢎⡇⣸⣿⣽⣿⢿⣯⡿⣾⣟⣿⡃⣽⣿⢃⣿⣻⣽⣿⢿⣽⡿⣯⣿⢿⣽⡿⣷⢿⣯⡿⣯⣿⢾⡷⣟⡟⣠⣟⣽
⡐⣽⣿⢏⠴⢡⢺⣪⢗⡽⣱⣣⢖⡞⣕⣏⢯⢎⡗⠝⣠⢕⡙⠷⣿⣟⣿⡿⣿⣽⣿⠠⡿⢃⣾⣿⣽⡷⣿⣻⣷⣿⢿⣽⣿⡋⢿⣻⡿⣯⣿⢿⣽⣻⡻⢃⢼⣺⣺⣺
⠠⡷⡏⣼⢨⡘⢵⢕⣕⢩⢚⠸⠑⠫⢚⢘⣡⢥⢴⢝⡵⣝⢼⢢⡑⢻⣿⣻⣟⣿⡝⡜⢥⣿⡷⣿⣽⢿⣯⡿⣾⣻⡿⣷⢟⢰⡩⣟⣿⢿⣽⣿⠯⢓⡼⣽⢽⣺⡺⣞
⡆⢟⢨⣗⡇⣟⣶⣥⢁⢞⡵⣫⢏⢯⡺⡕⡧⣳⣫⢞⡞⡮⡫⣃⣏⣤⣋⡛⢿⢿⣤⣾⡿⣷⣿⣯⡿⣿⣽⢿⣯⣿⢿⡽⡢⣟⡆⣟⣿⣟⢗⢣⢮⣳⣯⢓⢱⣻⣽⢱
⢷⡈⣼⢪⢗⡵⡛⠂⠸⡕⡯⣺⠹⣘⣨⣌⣍⣌⡚⠵⣫⢚⣼⣿⠟⠉⠈⠉⢂⠻⣿⡾⣿⡿⣾⣻⣿⣯⣿⣻⣿⣽⢛⡴⡯⣗⡇⢽⡞⢥⢾⢽⢽⣺⡪⣎⢮⡗⡧⣟
⢯⣟⢵⣫⡞⠈  ⠩⣫⢮⢣⣾⣿⣿⠛⠛⠛⢿⣦⠑⣽⣿⠇⠂⠈ ⠂ ⠈⣿⢿⣯⣿⣻⡿⣾⠻⠫⠟⠽⢷⣻⣟⠽⡽⣕⠁⣮⢿⢽⢯⠟⣴⢽⢆⢯⡺⣯⢷
⢟⣺⣽⠊ ⢀⠈ ⠈⢮⡃⣿⣿⡿⠁⢀ ⠂ ⠹⣇⢽⣿⡁⠠ ⠂⠈⡀⠨⣿⡻⠿⠝⠟⠁⠠ ⠄ ⢀ ⠱⣳⡏⣽⢵⣫⢷⣻⡛⣥⡯⣗⣿⢵⣫⡯⣗⣟
⡧⣗⠁ ⠄ ⠄ ⠈⠧⡃⣿⣿⣗ ⡀⠠⠈⢀ ⠗⠸⣿⣧⡀⠄⠐⢀⢄⢨⡢⣖⣕⠃  ⠠ ⡀⠐ ⡀ ⡷⡷⡱⣯⢿⡝⣢⣯⡷⡯⣗⣿⢽⣺⢯⢗⣟
⣟  ⠂ ⠂ ⠄ ⢭⢇⡹⣿⣷⣄ ⠠ ⢀⠠⠡⡧⣩⣉⡛⡓⢛⡡⣜⢮⡫⣞   ⠂ ⢀ ⠄  ⡮⣟⣯⣧⠫⡟⢬⣷⣯⢯⡯⣗⣿⢽⣺⣻⢽⡺
⠅ ⠐ ⠁ ⠂  ⢸⢕⣖⢍⠻⠷⡮⡤⠤⠐⣨⣺⢕⢗⣕⢗⡽⣕⢽⡪⡗⣝⡮⡂⠠ ⠠   ⢀⢤⢨⢯⣗⡷⣯⢷⣡⢿⣺⣞⣟⣾⣳⣻⢽⡺⣺⣻⢼
 ⠈ ⡈ ⠐ ⠂ ⢕⢗⡵⡫⣳⢕⡖⣖⢵⢻⢜⢜⢝⢵⢕⢯⢪⣺⡱⣫⢏⡮⣺⢕⡦⣄⢄⢄⣔⢼⣪⠃⣾⢽⣺⣻⡯⣟⣽⣽⣺⢾⣻⡾⣽⣺⡯⡯⣗⡧⣟
⡌ ⠄ ⠈ ⠄ ⠨⣫⢧⢫⡫⣺⢕⢽⢜⡕⢙⢬⣤⡑⠫⠺⡕⣗⢵⡹⣜⢵⢝⡼⣱⡣⡗⡯⡧⠓⡋⡤⣞⣿⢽⣺⣯⢯⣗⣷⢿⡽⣯⢷⣻⣷⢽⣺⡯⣗⣯⢷
⠷⠂ ⠈ ⠂ ⢤⢝⢎⢞⢕⡝⣜⢕⡯⣪⢔⠡⡒⡍⣋⠂⢶⢦⠌⣈⠊⢃⢋⢃⢋⣘⡈⡥⡤⡆⣷⣻⣽⣳⡯⣯⢺⣺⣻⢾⡽⣽⡽⣽⡽⣞⣞⣟⢾⣫⣗⣿⢽
⡒⢬⢺⠴⡤⡂⢕⡧⡫⡮⣣⡳⣹⢜⢵⢕⢧⢫⡣⡆⡡⠣⠂  ⡀⠁⢜⢪⡿⣿⢿⡏⢼⡽⡏⣾⣳⣟⣞⢷⣻⣝⢾⣫⣯⣯⡯⣗⢛⠳⡙⢓⢋⢋⣋⣚⣊⣚⢹
⠐⡵⡱⣹⠜⢐⢇⢏⠞⡎⡞⡜⣎⢞⢕⢕⢏⢎⡞⡵⢕⡕⡦⡡⡠⡠⣪⠏⢦⡙⢿⡏⢼⢽⡺⣱⣽⣺⡷⡯⣟⣾⢣⣟⢞⣾⡺⣯⢇⢢⣍⡈⡉⠉⠉⢈⠁⢉⣨⠐
⠸⣕⢽⡸⢀⢇⢇⡇⠕⡕⡅⢪⢪⢪⢪⢣⢫⢪⡪⡺⡱⣹⢜⢍⣊⢥⢔⠸⡯⣗⢍⠢⡯⣟⢕⣽⣺⣳⢯⣟⡷⣏⣾⡳⢽⣺⣽⢯⡇⢝⣷⡿⡾⡾⣞⣾⡾⣿⢽⡂
⠸⡵⣻⡊⠤⡫⡮⡊⢪⢪⡂⢕⢕⢕⢕⢕⢵⢱⢕⢝⡜⡎⡮⡣⣣⢇⡗⡅⢫⡳⣳⣪⡿⡽⣸⢮⣗⢿⡽⡾⣝⣞⣞⢼⣻⡺⣞⣿⢎⠸⣷⣻⣟⣿⣻⣾⡽⣯⢿⡀
⢸⡽⣺⡂⢹⡹⣪⡃⢹⢜⡆⠜⣜⢜⢜⢕⢕⢕⢕⢇⢇⢏⢎⢞⢜⡜⣜⢕⠨⣞⡕⣟⣞⡣⣟⣽⣺⡯⡯⡯⣗⡯⡮⣟⣼⢯⢷⢯⣗⠸⣻⣞⣷⣻⣞⣷⣻⣯⢿⠠
⠸⣺⢕⡇⠸⣪⢧⡃⠥⣳⢣⠘⡮⡺⣸⢜⢜⢜⢜⢜⢜⢜⢜⡜⡜⡜⡜⣜ ⡷⣝⣎⢖⡽⣕⣗⣷⢽⣫⡯⣗⣯⡫⣞⠷⠛⢋⣋⢬⣈⢙⠚⠞⠳⠝⠞⠗⠯⠓⢸
⡸⣕⢗⢇⠘⡵⡕⣗⠈⡞⣕⡂⢏⢞⢮⢺⡱⡫⣺⢱⡣⣳⢕⢵⢱⢕⢵⡑⢨⣽⢮⢗⣟⡮⣗⣗⣗⢟⡾⣽⢵⠓⠡⠤⡪⡝⡵⡱⣫⢺⡹⡝⣝⢞⡵⡳⡕⣗⢽⢱

It's no longer ASCII art, by a long strech, but it's very clearly identifiable in a modern text window.

My Alarm Clock


When I was a kid, maybe twelve, I got a German made wind-up alarm clock, the type with the two bells on top and separate springs for the clock and the alarm. That was my "daily driver" of an alarm for a long time. After I married, my wife hated it because the ticking was "loud" when she was trying to get to sleep. When one of the springs finally broke, probably about 2005ish, I switched to using my cellphone as an alarm. Some Nokia brick. Eventually I switched to a Audiovox flip "feature" phone.

The Audiovox was Nokia quality indestructable. (But it was in many ways a terrible phone.) I used it as a phone for years and then when I finally upgraded, I continued to use the Audiovox as an alarm clock for some more years. I replaced it only in 2016.

On the Audiovox the alarm mode had three alarms that could be set, and then enabled or disabled. I found that three different wake-up times covered 90+% of my usage: wake up on a day when the kids have school, wake up on a day when I have work but the kids don't have school, and weekend. Then there would be 5 to 10% of the time when I'd need a special time for something.

So when it came to replacing it, I wanted an alarm clock that would give me three different standard times and one special, and I wanted an interface that could let me set a week of them at once. I turned to a "pocketCHIP" (hereafter Pocketchip) tiny portable computer.

Screenshot of the program

Pink buttons show alarms on, white ones show alarms off. The day of week button turns off all alarms for that day. Time of day is shown on the current day of week. Code available from my github page, it's Perl with Tk for the X11 UI.

The Chip was a $9 computer, the "Pocket" part was an attachment to add a keyboard, touchscreen, battery pack, headers for GPIO etc, and a small hacking space. Overall it cost about $60, which was (and is) a steal compared to what a Raspberry Pi outfited as a laptop / tablet / netbook / other portable computer costs. As of 2020, you can still find new-old-stock on ebay for $50ish. Unlike a Pi, Chip was low-res: no HDMI option; and has built-in wifi and bluetooth and built-in storage. Now Pocketchip isn't great, it's got a take-you-back-to-the-late-1980s pixel count (480x272), a painfully awkward keyboard, no built-in speakers, and poor audio volume from the audio jack.

I modified mine adding a basic mono amplifier (PAM8230 from Adafruit) and a speaker attached to that. I also connected one of the GPIO pins to a standard 1/8th inch (2.5mm) audio jack, to use with an external button. There was ample room in the case for the amp, speaker, and extra jack. I did need to cut the plastic a bit to get get the proper parts sticking out.

Back of the modified Pocketchip

The green board in the upper left is for the GPIO 2.5mm jack, the blue board near it is the add-on amp, there's an on-off switch for the amp in the far upper left, and the speaker is squeezed in on the bottom right. The larger black board is the Chip $9 computer, and the silver thing below it is the rechargable battery.

Then I built a button out of a wooden box and Cherry D42 switch with a lever that I pulled out of some device. The lever holds the top of the box slightly open, pushing the lid down closes the switch. Big easy to push button for sleepy alarm silencing.

When the alarm fires, a program plays a sound file (repeatedly up to 30 times or 60 seconds as configured) and listens to a GPIO pin. Every 1/100th second the GPIO pin is sampled. With ten high samples and ten low samples — to allow for a switch normally open or normally closed — it decides that's enough and kills the sound playback and quits.

A look inside the button

This part was made entirely with reused stuff I had laying around.

I wrote the alarm software sometime in 2016 and have made only slight modifications to it since then. It works, and it works well. When I built myself a new bedside table last year, I designed it to have a place for the Pocket chip to hang, and suitable space for all the wires.

View of the installed alarm

I made the lamp first, then the alarm button to fit under the lamp, then the table that this all lives on. The drawer holds pencils and bookmarks, the current night time reading is on the table, the next books to read on the first shelf, books done reading go on the shelf below that. All of this is from scavenged or left-over material.

As this setup gets long in the tooth, I'm starting to think about what I do next. In particular, I worry about the Chip failing and/or the battery becoming useless. I've been thinking it might make a good learn Arduino project. I'd prefer to spend less than $100 on all parts, have a system with at least the current capabilties, including normally plugged in but with at least an hour's backup power to ride out blackouts. And a screen that I can easily turn off for darkness. It's been fun browsing, but I don't have a parts list yet.