Making a Snoopy calendar in Unix

For those of you who don’t know, the Snoopy calendar is a gimmick of the hacker culture. It basically refers to a line printer calendar for 1969 featuring the iconic beagle from Peanuts, that apparently hangs on the wall of every Real Programmer’s office. I’m not entirely certain of the origins of this meme, but it dates back at least to the humorous essay Real Programmers Don’t Use Pascal, which was posted to Usenet back in 1983.

snoopy

I have a certain fondness for this particular gimmick which I can’t quite explain. Perhaps it is the fact that it provides a feasible and exciting challenge for me – that of making my own Snoopy calendar. I have actually done this a couple of times.  This time I used a combination of C programming and several Unix programs like enscript, figlet, cal, and sed.

There are three components to the Snoopy calendar. The first is the Snoopy ASCII/line printer art, the second is the calendar, and the third is the year number banner. These components must be pasted together in the same text file, which can then be converted to Postscript for printing. The Unix paste program is not satisfactory for this, since it doesn’t align the text, so I wrote my own program. Here is the final result:


  1 /*************************************************
  2  * Paste V. 1.0                                  *
  3  *                                               *
  4  * Description: Pastes two files side-by-side    *
  5  * with the edges aligned.  Does not work with   *
  6  * files that contain tabs.                      *
  7  *                                               *
  8  * Author: Michael Warren                        *
  9  * License: Micheal Warren Free Software License *
 10  * Date: November 13, 2016                       *
 11  *************************************************/
 12 
 13 
 14 #include <stdio.h>
 15 #include <stdlib.h>
 16 #include <string.h>
 17 #include <errno.h>
 18 
 19 // Maximum line length
 20 #ifndef _MAXLL_
 21 # define _MAXLL_ 80
 22 #endif
 23 
 24 struct line{
 25         int linenum;
 26         int linelen;
 27         char text[_MAXLL_]; // Text of line
 28         struct line *next;  // Next line in current file
 29         struct line *corr;  // Corresponding line in other file
 30 };
 31 
 32 struct line *topl; // Top of left file
 33 struct line *topr; // Top of right file
 34 struct line *curl; // Current line in left file
 35 struct line *curr; // Current line in right file
 36 
 37 int mainint argc, char **argv ){
 38         FILE *l, *r;                // left and right files
 39         int llen = 0, rlen = 0;     // Number of lines in each file
 40         int rmaxll = 0, lmaxll = 0// Length of longest line in each file
 41         if( !argv[1] || !argv[2] ){
 42                 printf"\nUsage:\n%s <leftfile> <rightfile>\n\n", argv[0] );
 43                 return 1;
 44         }
 45         if( (l = fopen( argv[1], "r" )) == NULL ){
 46                 fprintfstderr"%s: ", argv[0] );
 47                 switch( errno ){
 48                         case EPERM   : fprintfstderr"Operation not permitted.\n" ); break;
 49                         case ENOENT  : fprintfstderr"%s: No such file or directory.\n", argv[1] ); break;
 50                         case EINTR   : fprintfstderr"Interrupted system call.\n" ); break;
 51                         case EIO     : fprintfstderr"Input/outpur error.\n" ); break;
 52                         case EDEADLK : fprintfstderr"Deadlock avoided.\n" ); break;
 53                         case ENOMEM  : fprintfstderr"Cannot allocate memory.\n" ); break;
 54                         case EACCES  : fprintfstderr"Permission denied.\n" ); break;
 55                         case ENODEV  : fprintfstderr"Operation not supported by device.\n" ); break;
 56                         case EISDIR  : fprintfstderr"%s is a directory.\n", argv[1] ); break;
 57                         case EINVAL  : fprintfstderr"%s: Invalid argument.\n", argv[1] ); break;
 58                         case ENFILE  : fprintfstderr"Too many open files in system.\n" ); break;
 59                         case EMFILE  : fprintfstderr"Too many open files.\n" ); break;
 60                         case EFBIG   : fprintfstderr"File %s is too large.\n", argv[1] ); break;
 61                         default      : fprintfstderr"An error occurred.  Error #: %d\n", errno );
 62                 }
 63                 return errno;
 64         }
 65         if( (r = fopen( argv[2], "r" )) == NULL ){
 66                 fprintfstderr"%s: ", argv[0] );
 67                 switch( errno ){
 68                         case EPERM   : fprintfstderr"Operation not permitted.\n" ); break;
 69                         case ENOENT  : fprintfstderr"%s: No such file or directory.\n", argv[2] ); break;
 70                         case EINTR   : fprintfstderr"Interrupted system call.\n" ); break;
 71                         case EIO     : fprintfstderr"Input/outpur error.\n" ); break;
 72                         case EDEADLK : fprintfstderr"Deadlock avoided.\n" ); break;
 73                         case ENOMEM  : fprintfstderr"Cannot allocate memory.\n" ); break;
 74                         case EACCES  : fprintfstderr"Permission denied.\n" ); break;
 75                         case ENODEV  : fprintfstderr"Operation not supported by device.\n" ); break;
 76                         case EISDIR  : fprintfstderr"%s is a directory.\n", argv[2] ); break;
 77                         case EINVAL  : fprintfstderr"%s: Invalid argument.\n", argv[2] ); break;
 78                         case ENFILE  : fprintfstderr"Too many open files in system.\n" ); break;
 79                         case EMFILE  : fprintfstderr"Too many open files.\n" ); break;
 80                         case EFBIG   : fprintfstderr"File %s is too large.\n", argv[2] ); break;
 81                         default      : fprintfstderr"An error occurred.  Error #: %d\n", errno );
 82                 }
 83                 return errno;
 84         }
 85         topl = (struct line *) mallocsizeofstruct line ) );
 86         topr = (struct line *) mallocsizeofstruct line ) );
 87         curl = topl; curr = topr;
 88         char c;
 89         // Build left file:
 90         while( (c = fgetc( l )) != EOF ){
 91                 ungetc( c, l );
 92                 curl->next = (struct line *) mallocsizeofstruct line ) );
 93                 curl = curl->next;
 94                 fgets( curl->text, _MAXLL_, l );
 95                 curl->linelen = strlen( curl->text );
 96                 lmaxll = (lmaxll < curl->linelen)?(curl->linelen):lmaxll;
 97                 curl->text[strlen( curl->text )-1] = '\0';
 98                 curl->linenum = ++llen;
 99         }
100         // Build right file:
101         while( (c = fgetc( r )) != EOF ){
102                 ungetc( c, r );
103                 curr->next = (struct line *) mallocsizeofstruct line ) );
104                 curr = curr->next;
105                 fgets( curr->text, _MAXLL_, r );
106                 curr->linelen = strlen( curr->text );
107                 rmaxll = (rmaxll < curr->linelen)?(curr->linelen):rmaxll;
108                 curr->text[strlen( curr->text )-1] = '\0';
109                 curr->linenum = ++rlen;
110         }
111         const int llenc = llen;
112         const int rlenc = rlen;
113         // Extend right file if shorter:
114         if( llen > rlen ){
115                 int diff = llen - rlen;
116                 forint i = 0; i < diff; i++ ){
117                         curr->next = (struct line *) mallocsizeofstruct line ) );
118                         curr = curr->next;
119                         forint j = 0; j < rmaxll; j++ ){
120                                 (curr->text)[j] = ' ';
121                         }
122                         (curr->text)[rmaxll] = '\0';
123                         curr->linenum = ++rlen;
124                 }
125         }
126         // Extend left file if shorter:
127         else if( llen < rlen ){
128                 int diff = rlen - llen;
129                 forint i = 0; i < diff; i++ ){
130                         curl->next = (struct line *) mallocsizeofstruct line ) );
131                         curl = curl->next;
132                         forint j = 0; j < lmaxll; j++ ){
133                                 (curl->text)[j] = ' ';
134                         }
135                         (curl->text)[lmaxll] = '\0';
136                         curl->linenum = ++llen;
137                 }
138         }
139         // Begin paste operation
140         curl = topl; curr = topr;
141         unsigned int len = (llenc < rlenc )?llenc:rlenc;
142         forint i = 0; i < len; i++ ){
143                 curl = curl->next;
144                 curr = curr->next;
145                 printf"%s  ", curl->text );
146                 int lendif = lmaxll - curl->linelen;
147                 forint j = 0; j < lendif; j++ ){
148                         putchar' ' );
149                 }
150                 printf"%s", curr->text );
151                 lendif = rmaxll - curr->linelen;
152                 forint j = 0; j < lendif; j++ ){
153                         putchar' ' );
154                 }
155                 putchar'\n' );
156         }
157         while( (curl = curl->next) != NULL ){
158                 curr = curr->next;
159                 printf"%s %s\n", curl->text, curr->text );
160         }
161         // Cleanup:
162         curl = topl->next; curr = topr->next;
163         while( curl != NULL ){
164                 struct line *auxl = curl->next;
165                 free( curl );
166                 curl = auxl;
167         }
168         while( curr != NULL ){
169                 struct line *auxr = curr->next;
170                 free( curr );
171                 curr = auxr;
172         }
173         free( topl ); free( topr ); fclose( l ); fclose( r );
174         return 0;
175 }

I created the banner using the following command:


figlet -f banner 1969 | sed "s/ /  /g" | sed "s/#/##/g" | sed "p"

The three sed commands are there to double the width and height of the banner. It results in the following output:


    ##        ##########      ##########      ##########    
    ##        ##########      ##########      ##########    
  ####      ##          ##  ##          ##  ##          ##  
  ####      ##          ##  ##          ##  ##          ##  
##  ##      ##          ##  ##              ##          ##  
##  ##      ##          ##  ##              ##          ##  
    ##        ############  ############      ############  
    ##        ############  ############      ############  
    ##                  ##  ##          ##              ##  
    ##                  ##  ##          ##              ##  
    ##      ##          ##  ##          ##  ##          ##  
    ##      ##          ##  ##          ##  ##          ##  
##########    ##########      ##########      ##########    
##########    ##########      ##########      ##########    


I then did some manual editing to round out the corners. I put this output above the output of cal -y 1969 in Vim, and then ran the program that I had written to paste it onto a Snoopy ASCII picture (I used a different picture this time). I then ran it through enscript, using landscape orientation and reducing the text size to it would all fit on one page, and also telling it to run in line printer emulation mode.

The final result:

snoopy4-cal

Retro game review: Super Stupid Space Invaders

This was a game I discovered on dosgames.com.  It’s based on the classic Space Invaders arcade game, except it’s text-based and runs on MS-DOS.  I  downloaded it and started playing it in DOSBox.  First, the stats:

Creator: PLBM Games
Year: 1997
Graphics mode: ASCII
Download link: http://www.dosgames.com/dl.php?filename=http://www.dosgames.com/files/sssi.zip

I found this game to be extremely entertaining and fun to play. It’s a fast-paced game with exciting text-mode graphics and good-old 8-bit sounds. This is a game that truly brings me back to the 90’s and my older brother’s Apple ][c.

In this game there are two players. The players take turns fighting waves of aliens. You have multiple ships in this game, so if your ship gets destroyed, the game isn’t automatically over. For a game titled “Super Stupid Space Invaders” and deemed a “not-serious” game by its creators, this game is actually fairly complex, interesting, and well-written.

Here is a screenshot of the first level. This level looks basically like your typical arcade Space Invaders, except without the shields:

204567-super-stupid-space-invaders-dos-screenshot-got-one

Here is a screenshot of the second level, or wave:

204570-super-stupid-space-invaders-dos-screenshot-i-guess-this-is

In this level, you have one space invader, and when you hit it, it splits into two more, those split into two more, and finally those each split into two more, so in the end you have sixteen invaders to contend with, and they all move very quickly – not just side-to-side but also up and down. This makes for very challenging game play.

All in all, I love this game. In fact I can’t think of anything not to love about it. It’s a fun game to play, and it brings me back to the good old days, and that’s why I love it.