I was working on the idea of using land.c to generate a Gridarta tutorial map - basically, a somewhat small island off the coast. But. land.c had problems trying compile on Ubuntu Resolute - and the map file output could not be saved in to a specified directory. So, Claude came up with the following:
So, now a command syntax like this works as desired:
./land -x 200 -y 200 -s 42 -n 40 -l 300000 -p 50 -w 50000 -d ~/test_output -m
#include <time.h>Why: time() is called in main() to generate a default random seed when -s is not supplied. Modern GCC requires the header to be explicitly included; older compilers silently assumed it. Without it, GCC raises an implicit function declaration error and refuses to compile.
#include <unistd.h>Why: getopt() is declared in <unistd.h>. The original code relied on the compiler implicitly knowing about it — behaviour that is no longer permitted under C99 and later. Without this header, GCC cannot find the declaration and raises an implicit function declaration error.
#include <string.h>Why: Required for snprintf(), which is used throughout the patch to safely build output file paths. Strictly speaking snprintf is declared in <stdio.h> on most systems, but explicitly including <string.h> makes the dependency clear and is correct portable practice.
main() return typeWhy: The original declaration was main(int argc, char *argv) — missing both the int return type and the correct argv declaration. Modern GCC rejects the implicit int return type as an error. The correct form is int main(int argc, char *argv[]).
argv declarationWhy: The original char *argv declares argv as a pointer to a single character, not an array of string pointers. The correct type is char *argv[] (array of pointers to char). This was a latent bug in the original that happened to work on the original target platform due to calling convention coincidences.
char *outdirWhy: All output filenames were hardcoded without any path prefix, forcing the user to cd into the target directory before running. A global outdir variable (defaulting to ".") holds the user-supplied path and is accessible by both write_crossfire_maps() and the lmap/pmap block in main() without requiring a function signature change.
-d <directory> option to getopt()Why: There was no way to specify where output files should be written. The new -d flag accepts a directory path and stores it in outdir. Adding "d:" to the getopt format string (the colon meaning the flag takes an argument) follows the existing pattern used by all other flags. Default behaviour when -d is omitted is unchanged.
sprintf with snprintf for all filename constructionWhy: The original used sprintf(name, MAP_FORMAT, nx, ny) with a fixed 512-byte buffer. Once a directory path prefix is prepended this is insufficient and potentially unsafe. snprintf with a 4128-byte buffer (PATH_MAX 4096 + filename slack) prevents buffer overflow for any realistic path length.
bare_name for map file headersWhy: The Crossfire map header name field must contain the logical map name (world_NNN_NNN), not a filesystem path. When -d is used the full tile path includes the directory prefix, so a separate bare_name buffer is formatted using just MAP_FORMAT to keep the in-game identifier correct.
fopen() callsWhy: The original checked for a failed open on tile files but not on cmap, lmap, or pmap. A failed fopen() returns NULL; passing NULL to fprintf() is undefined behaviour and typically produces a segfault with no diagnostic. All four open sites now check for NULL and exit with a descriptive error message pointing to the likely cause (directory missing or not writable).
Why: The existing code already prints a summary of all generation parameters to stderr at startup. Adding the effective output directory to that summary lets the user confirm where files will land before a potentially long generation run completes.
Feedback regarding the AI patch:
In short the effort to review what AI did here took longer than just fixing the compile myself. So I did that and pushed in ebc9cb799.