sok.c

#include <curses.h> #include <pwd.h> #include "sokoban.h" The above include the specs for various packages. ``curses.h'' declares stuff for screen handling. ``pwd.h'' declares password functions. Note that ``stdio.h'' is omitted by mistake, as some functions from it are used later. extern char *strrchr(), *getlogin(), *getpass(); extern short readscreen(), play(), outputscore(), getuserlevel(), makenewscore(), restoregame(), score(); The declaration for ``strrchr'' is in ``string.h''. This should have been include'd too. The other declarations are for functions in the program but in different files. short scoring = 1; short level, packets, savepack, moves, pushes, rows, cols; short scorelevel, scoremoves, scorepushes; char map[MAXROW+1][MAXCOL+1]; POS ppos; char *username, *prgname; That defined a whole lot of globals used in all files. static short optshowscore = 0, optmakescore = 0, optrestore = 0, optlevel = 0; static short superuser = 0; static short userlevel; That defined variables with scope of this file only main( argc, argv) short argc; char *argv[]; That was a K&R style function definition, rather than an ANSI C one of int main(int argc, char *argv[]) Sigh :-( { short ret, ret2; struct passwd *passwdent; scorelevel = 0; moves = pushes = packets = savepack = 0; if( (prgname = strrchr( argv[0], '/')) == NULL) prgname = argv[0]; else prgname++; That got the program name, losing any leading directories if( (passwdent = getpwuid(getuid())) == NULL) ret = E_NOUSER; else { username = passwdent->pw_name; printf("User is %s\n", username); That found out who is running this program superuser = (strcmp( username, SUPERUSER) == 0); if( (ret = checkcmdline( argc, argv)) == 0) { That parsed the command line arguments. What follows takes action based on that parse. if( optshowscore) ret = outputscore(); else if( optmakescore) { if( superuser) { if( (ret = getpassword()) == 0) ret = makenewscore(); } else ret = E_NOSUPER; } else if( optrestore) { ret = restoregame(); } else if( (ret = getuserlevel( &userlevel)) == 0) { if( optlevel > 0) { if( superuser) { level = optlevel; scoring = 0; } else if( userlevel < optlevel) ret = E_LEVELTOOHIGH; else level = optlevel; } else level = userlevel; } } } if( ret == 0) ret = gameloop(); If all the above stuff worked ok, go into the gameloop. If not, print an error and cleanup errmess( ret); if( scorelevel && scoring) { ret2 = score(); errmess( ret2); } exit( ret); } checkcmdline( argc, argv) short argc; char *argv[]; { short ret = 0; This parses the command line. Because there are only a small number of options, it does it in detail. Note how we only change a value to ``true'' (1) - the definition of each option intialised them all to ``false''.

Another common way for single char options is be to loop:

for (n = 1; n < argc; n++) { if (argv[n][0] == '-') switch (argv[n][1]) { ... } } There are library packages to handle complicated command lines. if( argc > 1) if( (argc == 2) && (argv[1][0] == '-')) { if( (argv[1][1] == 's') && (argv[1][2] == '\0')) optshowscore = 1; else if( (argv[1][1] == 'c') && (argv[1][2] == '\0')) optmakescore = 1; else if( (argv[1][1] == 'r') && (argv[1][2] == '\0')) optrestore = 1; else if( (optlevel = atoi( &(argv[1][1]))) == 0) ret = E_USAGE; } else ret = E_USAGE; return( ret); } gameloop() { short ret = 0; initscr(); crmode(); noecho(); That was screen handling stuff if( ! optrestore) ret = readscreen(); while( ret == 0) { if( (ret = play()) == 0) { level++; moves = pushes = packets = savepack = 0; ret = readscreen(); } } play() handles one level. The loop increments levels. Note that play() is expected to return a suitable code to signal end of play.

The next stuff restores the screen to a sane state after play is over.

clear(); refresh(); nocrmode(); echo(); endwin(); return( ret); } getpassword() { return( (strcmp(getpass("Password: "), PASSWORD) == 0) ? 0 : E_ILLPASSWORD); This uses a construct I don't like much (too hard to read). condition ? true-value : false-value It is an expression that contains a conditional, and has one of the two values. This author likes it a lot. } char *message[] = { "illegal error number", "cannot open screen file", "more than one player position in screen file", "illegal char in screen file", "no player position in screenfile", "too much rows in screen file", "too much columns in screenfile", "quit the game", NULL, /* errmessage deleted */ "cannot get your username", "cannot open savefile", "error writing to savefile", "cannot stat savefile", "error reading savefile", "cannot restore, your savefile has been altered", "game saved", "too much users in score table", "cannot open score file", "error reading scorefile", "error writing scorefile", "illegal command line syntax", "illegal password", "level number too big in command line", "only superuser is allowed to make a new score table", "cannot find file to restore" }; This array of string is indexed on the ``E_'' defines on ``sokoban.h''. There isn't really a nice way of ensuring consistency of this. Note that there is no special terminator to this list, because we know all the legal elements from the ``E_'' defines.

The indexing is done in ``errmess()'' below.

errmess( ret) register short ret; { if( ret != E_ENDGAME) { fprintf( stderr, "%s: ", prgname); switch( ret) { case E_FOPENSCREEN: case E_PLAYPOS1: case E_ILLCHAR: case E_PLAYPOS2: case E_TOMUCHROWS: case E_TOMUCHCOLS: case E_ENDGAME: case E_NOUSER: case E_FOPENSAVE: case E_WRITESAVE: case E_STATSAVE: case E_READSAVE: case E_ALTERSAVE: case E_SAVED: case E_TOMUCHSE: case E_FOPENSCORE: case E_READSCORE: case E_WRITESCORE: case E_USAGE: case E_ILLPASSWORD: case E_LEVELTOOHIGH: case E_NOSUPER: case E_NOSAVEFILE: fprintf( stderr, "%s\n", message[ret]); break; default: fprintf( stderr, "%s\n", message[0]); break; } if( ret == E_USAGE) usage(); } } The following stuff handles what to do if the user types illegal options. It defines an array of strings, that will be printed one per line. This is common too. Note the NULL terminator for the list, so we can walk it with a ``while'' loop even though we don't know its size (but we could have found it using ``sizeof''). static char *usagestr[] = { " -c: create new score table (superuser only)\n", " -r: restore saved game\n", " -s: show score table\n", " -<nn>: play this level (<nn> must be greater 0)\n", NULL }; usage() { register short i; fprintf( stderr, "Usage: %s [-{s|c|r|<nn>}]\n\n", prgname); for( i = 0; usagestr[i] != NULL; i++) fprintf( stderr, "%s", usagestr[i]); }