Home

Modifying the "sl" program in Debian

This is a simple tutorial for learning the basics of downloading the source code of a Debian package, modifying it, and compiling it. It does not modify the installer package, or re-package the changes as a Debian package, or "install" the finished product; those things are beyond the scope of this document. This is just for getting your feet wet in the above-mentioned processes.

For my test box, I downloaded the debian-11.5.0-amd64-netinst.iso installer, and installed it in a virtual machine. When I installed it, I gave it a 50GB drive to play in, kept all my directories in one partition, took all the defaults of the installation, except that I unchecked everything I could when it asked if I wanted to install such things as desktop environments and the standard system utilities. This way I'm hoping that my testing catches anything that needs to be installed along the way, rather than relying on something that might be installed with some other more-full installation.

Of course this means that on the first boot, we'll be booting into a text-only environment; that's okay, 'cause sl is a text-based app, so text-only is all we need. If you have a graphical setup, that's okay; you can just do the same things in an xterm of some sort.

Install the Debian package to see what the binary does

Make sure you have the source packages accessible in /etc/apt/sources.list. If you do a cat /etc/apt/sources.list, you should see some lines that mention deb-src, similar to this:

deb-src http://deb.debian.org/debian/ bullseye main

Now update your repository. As root, run:

apt update

Then install the sl package. As root, run:

apt install sl

Now run sl to test it. You should see an ASCII train move across the screen.

sl
Screenshot of ASCII train

If you're curious about where the sl program was installed, you can run either of the two commands below (the first one of which I believe is being deprecated):

which sl

or

command -v sl

Either command should report back:

/usr/games/sl

Install the C Source Code

Now we're ready to install the source code.

One of the neat things about compiling your own source-code apps is that you don't need superuser capability. Without superuser privileges, you won't be able to install the finished product in the system directory /usr/games, but you could install it in some place you own, such as ~/bin. So if you ever find yourself working as a user on a computer on which you do not have admin privileges, and need to install a program, you can always download the source, compile it, store it some location to which you do have access, and run it from there.

To install the source code, as a non-root user, create a directory into which you want to download and tinker with the source, say, something like:

mkdir -p ~/projects/C-source/sl

or

mkdir ~/my-fun-stuff

Then cd into that directory, and, again as non-root, run:

apt source sl

This should download the source code for the sl package, but on my virtual Debian box, on which I elected to not install any extras beyond the bare minimum, I get an error saying that I need to check to see if dpgk-dev is installed. So I run:

apt list --installed | grep dpkg-dev

and sure enough, that package is not found. I could have instead used a different command:

dpkg-query -l dpkg-dev

which also tells me it is not installed, but in a way that is fairly cryptic to the non-initiated. This is one of the reasons I prefer aptitude ("apt install aptitude") over apt. The command:

aptitude show dpkg-dev

plainly says "State: not installed".

So I need to install dpkg-dev. As root, run either apt or aptitude:

apt[itude] install dpkg-dev

This will likely pull in several other packages.

Now I'll try again, as non-root, to grab the source for the sl package. This time, my command:

apt source sl

works.

If you ls (or ls -lah, or similar) in your working directory, you should now see three files and one directory. For what we're doing, we're only interested in that directory, which at the time of this writing is named sl-5.02. So change into that directory, and do another ls, and you'll see another directory named debian, a LICENSE file (which is just a text file containing the license for the sl program; you can cat or less it to see its contents), a Makefile, a couple of README.md files (one of which is in Japanese), a couple of sl.1 files (again, with one of them in Japanese), an sl.c file, and an sl.h file. There are also a couple of hidden items, which don't concern us for our project. We're also not interested in the debian directory, which is used for re-packaging the finished product into a Debian package, which we won't be doing.

The README files are in markdown format (hence, the ".md" file extensions); this is kind of a hybrid between a plain-text file and a formatted file. For our purposes, you can just treat them (at least the English-language one) as plain-text files, and cat or less them, if you're interested in seeing their contents.

The .1 files are man-page files. You can see them as they're intended to be seen with this command:

man -l sl.1

Since my Debian box is minimalistic, I had to aptitude install man to get the man application. The "-l" tells man to use the man-page file as a local file, not as a man-page that has been properly installed on the system.

The Makefile is a file that tells the make command (which we haven't talked about) how to "make" the program from source. In our case, it basically tells make to use the gcc (GNU C Compiler) to compile the sl.c and sl.h files, using certain compiler flags, under what conditions, or in the case of make clean, to clean out previous attempts at compiling in order to start over with a clean slate.

The sl.h file defines several values, which the compiler recognizes as sort of "non-varying variables", or "constants". Most of these are used to store "snapshots" of each row (top to bottom) of a train or railroad car, with the bottom three or four rows of each train being the different "animation cels" of animated (moving) wheels. The main part of a train is drawn on the screen, along with one set of wheels; then that whole image is drawn over by the next frame of the moving picture of the train, shifting the drawing one column to the left, replacing that set of wheels with the next set of wheels, to give the illusion that the wheels are chug-chugging along as they push the train from the right side of the screen to the left.

The final piece of our puzzle is the sl.c file, which is where the core part of the program code resides. Strictly speaking, the #defines of the sl.h could be moved into the sl.c file, so that we would no longer even need an sl.h file, but some times it's better to separate a program's code into different "modular" pieces, for easier troubleshooting/maintenance/etc.

Compile the Code

Now let's actually compile this code. As your non-root user, simply run:

make

D'oh! On my minimalistic Debian box, I'm told I'm missing curses.h. No problem; I know how to get it installed. Although the new version of curses, ncurses is already installed on my minimalistic Debian box, the development library (pre-written code routines for program developers) for ncurses is not. So I'll install that:

aptitude install ncurses-dev

Now my make command works. I don't see much output, but if I do an ls, I see that I now have a new file, which is an executable binary, named simply sl. This is the binary that is created by compiling the source files sl.c and sl.h.

Test Your Compiled Binary

If you simply run sl, chances are real good that you'll run the version of sl that you installed earlier, the one that is stored in /usr/games. This is because you probably don't have your current directory as part of your path statement, and even if you do, it probably comes after the part of the path statement that specifies /usr/games. (You can see your current path with echo $PATH.)

You could change your path to include whatever directory you're currently in, but that has some negative security fallout, the discussion of which is beyond the scope of this document. Suffice it to say that I recommend that you don't do that.

The better option is to simply specify in your command that you want to run the version of sl that is in your current directory, by prefixing a dot-slash to the command, like so:

./sl

If everything worked, you should see your just-compiled train move across your screen.

Make Changes to the Source

Now we'll make two changes to the source code, very minor, but enough for you to see that you can edit the C source and make changes to a Debian application.

First, we'll just add the call-sign "NCC-1701" near the rear of the first train, by editing the sl.h file. Take care to overwrite the existing spaces as you type, making sure to delete as many characters as you input, so that the length of the line does not change. (Otherwise the rest of the program may get confused by the line-change and behave in unexpected (and undesired) ways.) The before and after images are here:

Train before name-change. Train after name-change.

Now compile your program with its change (with the make command), and run it to test (with the ./sl command). You should see the NCC-1701 train scroll by on your screen.

Now for our second change.

Actually, we're going to make two changes here. In the sl.c file, you can use your editor's "Search" (or "Find", etc) function (or just use your eyeballs; it's a pretty small program) to find the line that says usleep(40000). This number is the number of microseconds (thus the "u", which is Greek/Latin-ish for "micro") that the program "sleeps" (doing nothing but dreaming about laying on the coast with a drink having an itty-bitty umbrella in it) between animation frames. By increasing this number, the program will sleep longer, making the train move slower across the screen. Likewise, by decreasing the number, the train will speed up.

However, it's bad programming form to have "magic numbers" in your code. A "magic number" is a number (or other item) that just shows up in the code, giving no indication of where that number came from or what it means. When writing a program, it's handy to just throw in numbers when you need them, but a few days, or months, or years down the road, when you need to read the program code and make changes, you're not likely to remember what this magic number means. And other readers of your code won't know either. So let's replace this magic number with a more meaningful name. Change the 40000 to DELAY. (This time you don't need to worry about keeping the length of the line the same; that was only true in the "drawing" of the train, so the other pieces of the "drawing" didn't get out of alignment.) Then move to the top of the file, where you see int INTR = 0;, and add a line just below that line that says:

int DELAY = 20000;

There! You've made your two changes. You got rid of a "magic number", and you changed the value of that number so that the train will move faster across the screen.

Compile and Test Your Changes

By now you should know the routine:

make

and

./sl

Watch that NCC-1701 zip by!

If you want the train to go slower, just change the int DELAY = 20000; to a higher number, say, 60000. Experiment!

Final Comments

You now know enough to do basic editing of the source code, and to compile it, and to run it. This, of course, does not change the sl program that Debian provides to other users. There's a lot more to learn/do before you can get your changes accepted into the Debian distribution (not the least of which is to make changes that are worth distributing).

But it is enough to get you a start. And no matter the case, most everyone finds it fun to do. So go have fun!