Convert 'sl" Source Code from C to Rust

Download the Source Code for "sl"

For a more complete explanation of downloading and working with the source code for "sl", see here.

Get the Source

Create, and move into, a working directory suitable for playing with the source files, and as a sudo-capable user, use apt (or aptitude, etc) to grab the source code for "sl", say, somewhat like this:

  $ mkdir -p ~/projects/C/sl
  $ cd ~/projects/C/sl
  $ sudo apt update
  $ apt source sl

(Make sure to not use "sudo" on the "apt source sl" line.)

Understand the Source

The above should result in several items appearing in your working directory, including a directory named (at this time of writing) "sl-5.02". For our purposes, this is the only item in which we are interested. Move into that directory with:

$ cd sl-5.02

There are only five files here in which we have interest (unless you speak Japanese):

Of these five files, only two of them have direct relevance to the conversion of the program from C to Rust, those two being "sl.c" and "sl.h". Eventually we'd want to also update the man page and the README, but those are not integral to the actual conversion process.

The "sl.h" file contains the ASCII-art images of the various trains that choo-choo across the screen. If you take a look at that file (less sl.h), you'll see something like this:

~/projects/c/sl/sl-5.02$ less sl.h
/*========================================
 *    sl.h: SL version 5.02
 *      Copyright 1993,2002,2014
 *                Toyoda Masashi
 *                (mtoyoda@acm.org)
 *      Last Modified: 2014/06/03
 *========================================
 */

#define D51HIGHT        10
#define D51FUNNEL        7
#define D51LENGTH       83
#define D51PATTERNS      6


#define D51STR1  "      ====        ________                ___________ "
#define D51STR2  "  _D _|  |_______/        \\__I_I_____===__|_________| "
#define D51STR3  "   |(_)---  |   H\\________/ |   |        =|___ ___|   "
#define D51STR4  "   /     |  |   H  |  |     |   |         ||_| |_||   "
#define D51STR5  "  |      |  |   H  |__--------------------| [___] |   "
#define D51STR6  "  | ________|___H__/__|_____/[][]~\\_______|       |   "
#define D51STR7  "  |/ |   |-----------I_____I [][] []  D   |=======|__ "

#define D51WHL11 "__/ =| o |=-~~\\  /~~\\  /~~\\  /~~\\ ____Y___________|__ "
#define D51WHL12 " |/-=|___|=    ||    ||    ||    |_____/~\\___/        "
#define D51WHL13 "  \\_/      \\O=====O=====O=====O_/      \\_/            "

#define D51WHL21 "__/ =| o |=-~~\\  /~~\\  /~~\\  /~~\\ ____Y___________|__ "
#define D51WHL22 " |/-=|___|=O=====O=====O=====O   |_____/~\\___/        "
#define D51WHL23 "  \\_/      \\__/  \\__/  \\__/  \\__/      \\_/            "

#define D51WHL31 "__/ =| o |=-O=====O=====O=====O \\ ____Y___________|__ "
#define D51WHL32 " |/-=|___|=    ||    ||    ||    |_____/~\\___/        "
#define D51WHL33 "  \\_/      \\__/  \\__/  \\__/  \\__/      \\_/            "

#define D51WHL41 "__/ =| o |=-~O=====O=====O=====O\\ ____Y___________|__ "
#define D51WHL42 " |/-=|___|=    ||    ||    ||    |_____/~\\___/        "
#define D51WHL43 "  \\_/      \\__/  \\__/  \\__/  \\__/      \\_/            "

#define D51WHL51 "__/ =| o |=-~~\\  /~~\\  /~~\\  /~~\\ ____Y___________|__ "
#define D51WHL52 " |/-=|___|=   O=====O=====O=====O|_____/~\\___/        "
#define D51WHL53 "  \\_/      \\__/  \\__/  \\__/  \\__/      \\_/            "

#define D51WHL61 "__/ =| o |=-~~\\  /~~\\  /~~\\  /~~\\ ____Y___________|__ "
#define D51WHL62 " |/-=|___|=    ||    ||    ||    |_____/~\\___/        "
#define D51WHL63 "  \\_/      \\_O=====O=====O=====O/      \\_/            "

The "sl.c" is the main program, and it reads these ASCII-art images from the "sl.h" file, and builds an "animation frame". It's like drawing a train on a transparent sheet of paper ("cel", or "celluloid", like the old-timey overhead transparency). In this case, the main body of the train is drawn on one transparency sheet, and one of the six sets of wheels is drawn at the bottom of that main body of train. This forms one animation frame. The below shows what the first frame would look like:

      ====        ________                ___________ 
  _D _|  |_______/        \__I_I_____===__|_________| 
   |(_)---  |   H\________/ |   |        =|___ ___|   
   /     |  |   H  |  |     |   |         ||_| |_||   
  |      |  |   H  |__--------------------| [___] |   
  | ________|___H__/__|_____/[][]~\_______|       |   
  |/ |   |-----------I_____I [][] []  D   |=======|__ 
__/ =| o |=-~~\  /~~\  /~~\  /~~\ ____Y___________|__ 
 |/-=|___|=    ||    ||    ||    |_____/~\___/        
  \_/      \O=====O=====O=====O_/      \_/            

This frame is then displayed on the screen. Then the program builds another frame, only replacing the first wheel set with the second wheel set. The result looks like this:

      ====        ________                ___________ 
  _D _|  |_______/        \__I_I_____===__|_________| 
   |(_)---  |   H\________/ |   |        =|___ ___|   
   /     |  |   H  |  |     |   |         ||_| |_||   
  |      |  |   H  |__--------------------| [___] |   
  | ________|___H__/__|_____/[][]~\_______|       |   
  |/ |   |-----------I_____I [][] []  D   |=======|__ 
__/ =| o |=-~~\  /~~\  /~~\  /~~\ ____Y___________|__ 
 |/-=|___|=O=====O=====O=====O   |_____/~\___/        
  \_/      \__/  \__/  \__/  \__/      \_/            

Notice that the wheels are different between these two frames. We do this four more times, one time for each of the next four wheel sets, and when we rapidly flip through these six images, this creates the illusion of the wheels revolving. When we finish with the sixth wheel set, we start over again with the first wheel set, to keep the illusion going. As each frame is built and then drawn on the screen, it is drawn one character-sized space to the left on the screen, and overwrites the frame that was there before. This creates the illusion that the revolving wheels are pushing the train across the screen.

Notice also that in the "sl.h" file, the parts of the drawing that involve a backslash (\) have two backslashes. This is because in many situations, a backslash is not treated like a backslash, but is rather treated as a special instruction. For example, when printing something from Rust (or C), such as "Hello, world!", a backslash followed by the letter "n" means to add an extra "newline", which is roughly equivalent to hitting the ENTER key when you're typing text on a keyboard. The double-backslash then simply means, "treat the next backslash as a normal backslash, not as a special-instruction backslash". The end result is that the image in the "sl.h" file looks a bit janky (notice the jagged back wall of the train in "sl.h" vs how it prints onto the screen), but it prints out fine to the screen.

The "sl.c" main program has two conceptual parts to it: it has the main program, which moves the frame from right to left across the screen, and it has a second part (composed of several sub-functions) which build each frame. So we can think of the overall process as having three parts:

In the C-version of the program, the storage of the ASCII-art drawings is kept in the file "sl.h", and the other two items are handled in the "sl.c" file. That probably made the most sense in the earliest version of the program, when there was only one train being drawn, without extras like smoke coming out of the smokestack and people hollering for "help" in the "Accident" mode, etc. But as features get added, it kind of makes sense to move all the data and code for a particular train set into its own compartment. This would also make it easier to add different trains (or planes, or automobiles, or jumping-jack stick-figures, or etc) in the future. I had originally thought to do a more-or-less one-to-one conversion of the syntax of the C program into Rust, but the more I worked with it, the less that makes sense, in favor of the more modular approach just mentioned.

So rather than just copying the C version into Rust language, we'll copy the functionality rather than the methodology (while borrowing from the current C-version methodology where it makes sense to do so). But we've got a lot to learn before we're ready for that. First, let's take a look at using ANSI "Graphics" for Text-mode Screens.