Saturday, October 3, 2015

VerySillyMUD: Compilation

This post is part of my "VerySillyMUD" series, chronicling an attempt to refactor an old MUD into working condition[1].

The first goal is just to get the thing to compile. The source code is early 1990s C, and I have this C compiler on my computer at home:

The compiler doesn't quite like the source code here; it generates 21 warnings and 20 errors on the first file when we run make! I haven't included the full output here for readability's sake, but you can peruse the full gist if you like. Our goal here is to get compilation to succeed while making only minor edits that we think are safe and do not change intended functionality.

The first error is here:

The error in question points out a conflict between the prototype included here and the system-level include file string.h. A quick grep of the source code shows that strdup() is not implemented in the source code here, so this should just be a matter of removing the prototypes where we find them in the source code and instead just including them via the system level library, as seen in this commit. The next error/warning is:

If we look at the source code in question, we see:

This could either be a mistake in the function definition (i.e. it should return void instead of int) or this is an oversight and the function ought to be returning some kind of status code as an integer. A quick grep shows this is actually only called from one place (elsewhere in comm.c) where the return value is not captured or inspected. Therefore we can take care of this in the short term by correcting the function declaration, as captured in this commit. What's next?

Ok, from looking at the error message, the compiler seems to be confusing a locally-defined log() function that outputs some kind of logging message with the one from the math.h library for computing algorithms. The easiest way to fix this is to rename the local function for clarity to something like log_msg(), as captured in this commit. Ah, rats, now we see that this function didn't actually have a prototype anywhere, or at least not in enough places:

Adding the prototype to the protos.h include file seems to fix that up. Now, I just noticed at this point the compilation of comm.c still generates 25 warnings but only 2 errors! It turns out the very first fix we did for strdup cleaned most of them up. As I am running out of time for this session, let's press on to try to clean up the last two errors and at least get one file compiling. I'm generally a proponent of the "treat compiler warnings as errors" rule of thumb, but I think we're not quite ready to add that rule. I'd really like to get to the point where we can get some unit tests in place before doing too many more modifications. The first remaining error is:

Hmm, this is a new one for me. The bind() function connects a socket to a particular address so that we can start accepting connections from clients there. The system library's bind() on Mac OS X only takes 3 arguments, as does Linux's. A look at the source code shows there's an extra 0 argument being passed. Maybe this was for an older version of bind? According to running man 2 bind on my Mac OS X computer, in the HISTORY section it says "The bind() function call appeared in 4.2BSD." It turns out if we actually look at the man page for bind() from 4.2BSD it also only takes 3 arguments! What the heck? Without really knowing what the compilation target was for the codebase, it's hard to say what to do here. I'm going to assume one of the compilation targets had a bind() call that maybe took some optional flags as a fourth argument, but the source code didn't opt into any of them (hence the zero). According to this, extra arguments to functions were probably safely ignored, so I think we can just get rid of the extra argument here and see if that works. It'll certainly clean up the error at least. Ok, the last error, then is:

I think this dates me as a C programmer, because I thought that was still a valid way to #ifdef-out a section of code. I'll just comment the code out instead. Update: when I mentioned this on Twitter, Steve Vinoski pointed out:

This was interesting because Steve was right in that I had forgotten the idiom I was used to (#if 0). But it was also interesting that this code, which apparently worked at one point, used this incorrect idiom (#ifdef 0). A little experiment shows that this is not valid in C89 ("ANSI C"):

If I attempt to compile this using C89 compatibility, I get:

So this was written for a compiler with non-ANSI C support. It doesn't even appear to be valid K&R C, according to the list of gcc incompatibilities between it and ANSI C (the version of gcc I have installed is based on the clang compiler, which doesn't support the -traditional flag, and I'm not motivated to go install an older version to try it out). There are hints in the source code that this was targeted towards Sun computers; perhaps their early-1990s-vintage proprietary system C compilers accepted this syntax. Anyway, it's an easy enough fix, so let's not dwell further on it.

(Partial) success! At this point, the make run actually builds 10 object files (including comm.o) before erroring out (full gist). Next time out we'll continue fixing compilation errors in the hopes of getting at least a clean compilation run.

[1] SillyMUD was a derivative of DikuMUD, which was originally created by Sebastian Hammer, Michael Seifert, Hans Henrik Stærfeldt, Tom Madsen, and Katja Nyboe.


Anonymous said...

The code probably meant `#if 0` instead of `#ifdef 0`.