QZ qz thoughts
a blog from Eli the Bearded

slowcat.c and signature.c


A discussion in the Usenet group comp.sys.raspberry-pi about browsers turned to ASCII art when I brought up concerns about showing ASCII art in web pages (based on the recent notint screenshots here). And that discussion had one Charlie Gibbs reminisce about the "Andalusian Video Snail".

I found the file for him at https://grox.net/misc/vt/ — it's an ASCII art animation with VT100 escape codes so viewing it is tricky. Those animations are typically highly dependent on viewing on slow terminals, and modern hardware is anything but a slow terminal.

At grox.net there is also a slowcat.c which easily compiles. That code is essentially a loop of getchar();nanosleep();putchar(); to delay output. It worked okay for a few videos I tried, but the snails did not look so good. And it would not slow down STDIN, which would be handy for a program I wrote (more later).

So the biggest problem with the Grox slowcat.c is while it adds delays, it doesn't disable output buffering, so output ends up being large chunks showed without delay and then a delay for the next large chunk. That doesn't suit that particular animation well. Other problems include a "cat" that only shows exactly one file instead of arbitrary numbers, an awkward interface for delays (how many nanoseconds do I really want? and why does the code use usecs for presumably μseconds), and arg parsing oddness.

I decided to rewrite slowcat. I disabled buffering, switched to usleep(), employ getopt() and an arbitrary number of files, and added a option to have the program calculate a delay to emulate a particular baud rate, which is how terminal speeds were measured when these were written. Quite likely a baud of 1200 or 9600 was assumed by the animator. Baud is a measure of bits per second, so 9600 works out to 150 characters per second, or about 13 seconds for an 80x24 terminal screen. When I test time slocat -b 9600 snails.tv takes 3m33.23s while time cat snails.tv takes 0.47s. On my system stty tells me 38400 baud, which would be 4 times faster, but since it actually runs 453(ish) times faster, the baud equivalent is closer to 4,500,000 than 38,400. For most purposes, that faster speed is much appreciated.

So back to signature.c, by which I mean this program:

Elijah
------                                         /* gcc signature.c -lm */
main(){int i,j,k,l;for(i=-12;i<13;i++,printf("\033[H")){for(l=61968*457+
1,l*=5;l>8;l>>=4)p(97-(l>>22)+(l&15));for(j=-12;j<12;){for(k=j+12?-12:-6
;12>k;)p((l=k*k+++i*i+j*j)<12*12?(l=sqrt(l))[".,:;iIJYVSOM"]:32);j++<11?
p(10):0;}}puts("Elijah ");}p(int m){printf("%c",m);}

I use it in place of a .signature on select Usenet posts. I wrote it approximately 1995. Over the years, it's been used perhaps one or two dozen times, and I have made at least five variants with different email addresses, each of which has been reformated to have the lines the same length.

(An aside on .signatures: I have never used a proper .signature, that is to say a block of standard text appeneded automatically to the end of my posts or email. I have always done it manually, and, for Usenet posts, 99.9% of them are unique entries composed for that particular post. I have about 30 programs, most in Perl, that I have used and reused from time to time. Since I don't consider them true signatures, and do consider them part of the post, I do not use proper signature cut lines — dash dash space on a line by itself — which annoys some people, sometimes.)

The origins of that program are interesting. The program came to me in a dream where I visualized an ASCII art movie of cross-sections of a sphere that changes "color" closer to the core. The colors are represented by the characters in the string ".,:;iIJYVSOM" and the code uses some basic IOCCC obfuscation tricks, like using C arrays backwards: (offset)["array to index"], but is mostly pretty readable once reformatted. The use of +++ is very classic me, since I enjoy testing how far I can push a parser to accept the same character over and over. My extreme is perhaps this Perl one:

sub S(){@s=caller($/);$s[3]=~s s\w+:+ss&&print$s[3].q. .}$/=$^=~s/\S+/\n/;
$_="Just (eli) Another (the) Perl (bearded) Hacker";sub s($){eval$_[0];$/}
while(s&&&&& &s(qq&sub$^&.$&.q&{\&S}&)&& &{$&}&&s&&&){$/}$\=$^;print"\b,";

Five ampersands in a row, then a space because the parser choked on ampersand six. s&&& runs a substitution using the previous regexp, so the RE field between s& and the middle & is empty and replaces the match with an empty string, the middle & and third &. If that succeeds (&&) call subroutine s: &s. I needed a space on either side of the && and operator as a hint to the parser about what's going on, and I choose to go with after to get five in a row. Then I squeeze as many extra ampersands as I can in the rest of that line. I'm pleased to say that since I wrote that, the Perl parser has improved to the point that the six ampersand in a row version now works: a fragment like (s&&&&&&s( is either a nightmare or a wonderous sight depending on your tastes.

But I digress. The dream version of sphere.c did not include the use of the number 28319377 to represent "Elijah" printed on every frame, I added that when I decided that sphere.c should become signature.c. It's obfuscated enough that the average schmoe won't be able to change it, but could perhaps remove it.

But like those other ASCII art movies, signature suffered from the speed-up of terminals. The visualization cannot be appreciated at such lightning rates. The fix to have slowcat work on STDIN was added with my signature program in mind: signature | slowcat -b 9600

My slowcat and the latest version of signature.c, now fixed to have a more circular sphere, are available at github, along with various animations (the animations from grox.net and textfiles.com).