1 #include <stdio.h>
2 #include <pwd.h>
3 #include <X11/Xlib.h>
4 #include <X11/Xresource.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include <string.h>
8 #include <ctype.h>
9 
10 #include "externs.h"
11 #include "globals.h"
12 #include "options.h"
13 #include "errors.h"
14 #include "display.h"
15 #include "score.h"
16 #include "www.h"
17 
18 /* locally-defined functions */
19 static short VerifyScore(short optlevel);
20 static short GameLoop(void);
21 static short GetGamePassword(void);
22 static void Error(short);
23 static void Usage(void);
24 static short CheckCommandLine(int *, char **);
25 static char *FixUsername(char *name);
26 
27 /* exported globals */
28 Boolean scoring = _true_;
29 short level, packets, savepack, moves, pushes, rows, cols;
30 unsigned short scorelevel, scoremoves, scorepushes;
31 POS ppos;
32 char map[MAXROW + 1][MAXCOL + 1];
33 char *username = 0, *progname = 0, *bitpath = 0;
34 char *optfile = 0;
35 XrmDatabase rdb;
36 Boolean ownColormap = _false_, datemode = _false_, headermode = _false_;
37 
38 static short optlevel = 0, userlevel = 0;
39 static short line1 = 0, line2 = 0;
40 static Boolean opt_show_score = _false_, opt_make_score = _false_,
41 	       optrestore = _false_, owner = _false_,
42 	       opt_verify = _false_, opt_partial_score = _false_,
43 	       opt_user_level = _false_;
44 static struct passwd *pwd;
45 
46 
47 static int movelen;
48 /* Length of the verified move sequence waiting on stdin if -v is used */
49 
main(int argc,char ** argv)50 void main(int argc, char **argv)
51 {
52   short ret = 0;
53 
54   DEBUG_SERVER("starting");
55 
56   scorelevel = 0;
57   moves = pushes = packets = savepack = 0;
58 
59   /* make the program name be what it is invoked with */
60   progname = strrchr(argv[0], '/');
61   if (progname == NULL)
62     progname = argv[0];
63   else
64     progname++;
65 
66   /* Parse the command line */
67   ret = CheckCommandLine(&argc, argv);
68 
69   /* find out who is playing us. (pwd will be kept around in case we need to
70    * build the Xresources stuff later.
71    */
72   pwd = getpwuid(getuid());
73   if (pwd == NULL)
74     /* we MUST be being played by somebody, sorry */
75     ret = E_NOUSER;
76   else {
77     /* find out who we are. */
78 #if !WWW
79     if (!username) username = FixUsername(pwd->pw_name);
80     /* else we already got a fixed username from the -v option */
81 #else
82 /* If running in Web mode, append HERE to the username. */
83     if (!username) {
84 /* username might have already been set by the X resource mechanism */
85 	char *here = HERE;
86 	char *namebuf = (char *)malloc(strlen(pwd->pw_name) + strlen(here) + 1);
87 	strcpy(namebuf, pwd->pw_name);
88 	strcat(namebuf, here);
89 	username = FixUsername(namebuf);
90 	free(namebuf);
91     } else {
92 	username = FixUsername(username);
93     }
94 #endif
95     /* see if we are the owner */
96     owner = (strcmp(username, OWNER) == 0) ? _true_ : _false_;
97     if (ret == 0) {
98       if (opt_show_score) {
99 	DEBUG_SERVER("sending score file");
100 	ret = OutputScore(optlevel);
101       } else if (opt_verify) {
102 	DEBUG_SERVER("verifying score");
103 	ret = VerifyScore(optlevel);
104       } else if (opt_partial_score) {
105 	ret = OutputScoreLines(line1, line2);
106       } else if (opt_make_score) {
107 	if (owner) {
108 	  /* make sure of that, shall we? */
109 	  ret = GetGamePassword();
110 	  if (ret == 0)
111 	    ret = MakeNewScore(optfile);
112 	} else
113 	  /* sorry, BAD owner */
114 	  ret = E_NOSUPER;
115       } else if (optrestore) {
116 	ret = RestoreGame();
117       } else if (opt_user_level) {
118 	ret = GetUserLevel(&userlevel);
119 	if (ret == 0) {
120 	    printf("Level: %d\n", userlevel);
121 	    ret = E_ENDGAME;
122 	}
123       } else {
124 	ret = GetUserLevel(&userlevel);
125 	if (ret == 0) {
126 	    if (optlevel > 0) {
127 #if !ANYLEVEL
128 		if (userlevel < optlevel) {
129 		    if (owner) {
130 			/* owners can play any level (but not score),
131 			 * which is useful for testing out new boards.
132 			 */
133 			level = optlevel;
134 			scoring = _false_;
135 		    } else {
136 			ret = E_LEVELTOOHIGH;
137 		    }
138 		} else
139 #endif
140 		  level = optlevel;
141 	  } else
142 	    level = userlevel;
143 	}
144       }
145     }
146   }
147   if (ret == 0) {
148     /* play till we drop, then nuke the good stuff */
149     ret = GameLoop();
150     DestroyDisplay();
151     XCloseDisplay(dpy); /* factored this out to allow re-init */
152     display_alloc = _false_;
153   }
154   /* always report here since the game returns E_ENDGAME when the user quits.
155    * Sigh.. it would be so much easier to just do it right.
156    */
157   Error(ret);
158 
159   /* exit with whatever status we ended with */
160   switch(ret)
161   {
162   case E_ENDGAME:
163   case E_SAVED:
164 	ret = 0;	/* normal exits */
165 	break;
166   }
167   DEBUG_SERVER("ending");
168   exit(ret);
169 }
170 
171 /* FixUsername makes sure that the username contains no spaces or
172    unprintable characters, and is less than MAXUSERNAME characters
173    long. */
FixUsername(char * name)174 static char *FixUsername(char *name)
175 {
176     char namebuf[MAXUSERNAME];
177     char *c = namebuf;
178     strncpy(namebuf, name, MAXUSERNAME);
179     namebuf[MAXUSERNAME-1] = 0;
180     while (*c) {
181 	if (!isprint(*c) || *c == ' ' || *c == ',') *c = '_';
182 	c++;
183     }
184     return strdup(namebuf);
185 }
186 
mode_selected()187 static Boolean mode_selected()
188 {
189     return (opt_show_score || opt_make_score || optrestore || (optlevel > 0) ||
190 	     opt_verify || opt_partial_score || opt_user_level)
191 	   ? _true_ : _false_;
192 }
193 
194 /* Oh boy, the fun stuff.. Follow along boys and girls as we parse the command
195  * line up into little bitty pieces and merge in all the xdefaults that we
196  * need.
197  *
198  * May set "username" to some value.
199  */
CheckCommandLine(int * argcP,char ** argv)200 short CheckCommandLine(int *argcP, char **argv)
201 {
202   XrmDatabase command = NULL, temp = NULL;
203   char *res;
204   char buf[1024];
205   int option;
206 
207   /* let's do this the sensible way, Command line first! */
208   /* we will also OPEN the display here, though we won't do anything with it */
209   XrmInitialize();
210 
211   /* build an XrmDB from the command line based on the options (options.h) */
212   XrmParseCommand(&command, options, sizeof(options)/sizeof(*options),
213 		  progname, argcP, argv);
214 
215   /* okay, we now have the X command line options parsed, we might as well
216    * make sure we need to go further before we do.  These command line options
217    * are NOT caught by XrmParseCommand(), so we need to do them ourselves.
218    * Remember, they are all exclusive of one another.
219    */
220   for(option = 1; option < *argcP; option++) {
221     if (argv[option][0] == '-') {
222       char *optarg;
223       switch(argv[option][1]) {
224 	case 's':
225 	  if (mode_selected()) return E_USAGE;
226 	  opt_show_score = _true_;
227 	  optarg = &argv[option][2];
228 	  if (!isdigit(*optarg) && argv[option+1] && isdigit(argv[option+1][0]))
229 	  {
230 	    optarg = &argv[option+1][0];
231 	    option++;
232 	  }
233 	  optlevel = atoi(optarg);
234 	  break;
235 	case 'c':
236 	  if (mode_selected()) return E_USAGE;
237 	  optfile = 0;
238 	  if (argv[option][2] != 0)
239 	    optfile = &argv[option][2];
240 	  else if (argv[option+1] && argv[option + 1][0] != '-') {
241 	    optfile = &argv[option + 1][0];
242 	    option++;
243 	  }
244 	  opt_make_score = _true_;
245 	  break;
246 	case 'C':
247 	  ownColormap = _true_;
248 	  break;
249 	case 'r':
250 	  if (mode_selected()) return E_USAGE;
251 	  optrestore = _true_;
252 	  break;
253 	case 'v':
254 	  if (mode_selected()) return E_USAGE;
255 	  option++;
256 	  optlevel = atoi(argv[option++]);
257 	  if (!optlevel || !argv[option]) return E_USAGE;
258 	  username = FixUsername(argv[option++]);
259 	  if (!argv[option]) return E_USAGE;
260 	  movelen = atoi(argv[option++]);
261 	  if (!movelen) return E_USAGE;
262 	  opt_verify = _true_;
263 	  break;
264 	case 'L':
265 	  if (mode_selected()) return E_USAGE;
266 	  option++;
267 	  if (!argv[option]) return E_USAGE;
268 	  line1 = atoi(argv[option++]);
269 	  if (!argv[option]) return E_USAGE;
270 	  line2 = atoi(argv[option]);
271 	  if (line1 > line2) return E_USAGE;
272 	  opt_partial_score = _true_;
273 	  break;
274 	case 'u':
275 	  option++;
276 	  if (!argv[option]) return E_USAGE;
277 	  username = FixUsername(argv[option]);
278 	  break;
279 	case 'U':
280 	  if (mode_selected()) return E_USAGE;
281 	  opt_user_level = _true_;
282 	  break;
283 	case 'D':
284 	  datemode = _true_;
285 	  break;
286 	case 'H':
287 	  headermode = _true_;
288 	  break;
289 	default:
290 	  if (mode_selected()) return E_USAGE;
291 	  optlevel = atoi(argv[option]+1);
292 	  if (optlevel == 0) return E_USAGE;
293 	  break;
294       }
295     } else
296       /* found an option that didn't begin with a - (oops) */
297       return E_USAGE;
298   }
299 
300   if (opt_partial_score || opt_show_score || opt_make_score || opt_verify ||
301       opt_user_level)
302       return 0; /* Don't mess with X any more */
303   /* okay.. NOW, find out what display we are currently attached to. This
304    * allows us to put the display on another machine
305    */
306   res = GetDatabaseResource(command, "display");
307 
308   /* open up the display */
309   dpy = XOpenDisplay(res);
310   if (dpy == (Display *)NULL)
311     return E_NODISPLAY;
312   display_alloc = _true_;
313 
314   /* okay, we have a display, now we can get the std xdefaults and stuff */
315   res = XResourceManagerString(dpy);
316   if (res != NULL)
317     /* try to get it off the server first (ya gotta love R4) */
318     rdb = XrmGetStringDatabase(res);
319   else {
320     /* can't get it from the server, let's do it the slow way */
321     /* try HOME first in case you have people sharing accounts :) */
322     res = getenv("HOME");
323     if (res != NULL)
324       strcpy(buf, res);
325     else
326       /* no HOME, let's try and make one from the pwd (whee) */
327       strcpy(buf, pwd->pw_dir);
328     strcat(buf, "/.Xdefaults");
329     rdb = XrmGetFileDatabase(buf);
330   }
331 
332   /* let's merge in the X environment */
333   res = getenv("XENVIRONMENT");
334   if (res != NULL) {
335     temp = XrmGetFileDatabase(res);
336     XrmMergeDatabases(temp, &rdb);
337   }
338 
339   /* now merge in the rest of the X command line options! */
340   XrmMergeDatabases(command, &rdb);
341 
342   if (!username) username = GetResource("username");
343   return 0;
344 }
345 
346 /* Read a move sequence from stdin in a newly-allocated string. */
ReadMoveSeq()347 static char *ReadMoveSeq()
348 {
349     char *moveseq = (char *)malloc(movelen);
350     int ch = 0;
351     while (ch < movelen) {
352 	int n = read(0, moveseq + ch, movelen - ch); /* read from stdin */
353 	if (n <= 0) { perror("Move sequence"); return 0; }
354 	ch += n;
355     }
356     return moveseq;
357 }
358 
VerifyScore(short optlevel)359 short VerifyScore(short optlevel)
360 {
361     short ret;
362     char *moveseq = ReadMoveSeq();
363     if (!moveseq) { return E_WRITESCORE; }
364     level = optlevel;
365     ret = ReadScreen();
366     if (ret) return ret;
367     if (Verify(movelen, moveseq)) {
368 	ret = Score(_false_);
369 	scorelevel = 0; /* don't score again */
370 	if (ret == 0) ret = E_ENDGAME;
371     } else {
372 	ret = E_WRITESCORE;
373     }
374     free(moveseq);
375     return ret;
376 }
377 
378 /* we just sit here and keep playing level after level after level after .. */
GameLoop(void)379 static short GameLoop(void)
380 {
381     short ret = 0;
382 
383     /* make sure X is all set up and ready for us */
384     ret = InitX();
385     if (ret == E_NOCOLOR && !ownColormap) {
386 	DestroyDisplay();
387 	ownColormap = _true_;
388 	fprintf(stderr,
389 	"xsokoban: Couldn't allocate enough colors, trying own colormap\n");
390 	ret = InitX();
391     }
392 
393     if (ret != 0) return ret;
394 
395     /* get where we are starting from */
396     if (!optrestore) ret = ReadScreen();
397 
398     /* until we quit or get an error, just keep on going. */
399     while(ret == 0) {
400 	ret = Play();
401 	if ((scorelevel > 0) && scoring) {
402 	    int ret2;
403 	    ret2 = Score(_false_);
404 	    Error(ret2);
405 	    scorelevel = 0;
406 	}
407 	if (ret == 0 || ret == E_ABORTLEVEL) {
408 	    short newlev = 0;
409 	    short ret2;
410 	    ret2 = DisplayScores(&newlev);
411 	    if (ret2 == 0) {
412 		if (newlev > 0 &&
413 #if !ANYLEVEL
414 		    newlev <= userlevel &&
415 #endif
416 		    1) {
417 		    level = newlev;
418 		} else {
419 		    if (ret == 0) level++;
420 		}
421 		ret = 0;
422 	    } else {
423 		ret = ret2;
424 	    }
425 
426 	}
427 	if (ret == 0) {
428 	    moves = pushes = packets = savepack = 0;
429 	    ret = ReadScreen();
430 	}
431     }
432     return ret;
433 }
434 
435 /* Does this really need a comment :) */
GetGamePassword(void)436 static short GetGamePassword(void)
437 {
438   return ((strcmp(getpass("Password: "), PASSWORD) == 0) ? 0 : E_ILLPASSWORD);
439 }
440 
441 /* display the correct error message based on the error number given us.
442  * There are 2 special cases, E_ENDGAME (in which case we don't WANT a
443  * silly error message cause it's not really an error, and E_USAGE, in which
444  * case we want to give a really nice list of all the legal options.
445  */
Error(short err)446 static void Error(short err)
447 {
448   switch(err) {
449     case E_FOPENSCREEN:
450     case E_PLAYPOS1:
451     case E_ILLCHAR:
452     case E_PLAYPOS2:
453     case E_TOMUCHROWS:
454     case E_TOMUCHCOLS:
455     case E_NOUSER:
456     case E_FOPENSAVE:
457     case E_WRITESAVE:
458     case E_STATSAVE:
459     case E_READSAVE:
460     case E_ALTERSAVE:
461     case E_SAVED:
462     case E_TOMUCHSE:
463     case E_FOPENSCORE:
464     case E_READSCORE:
465     case E_WRITESCORE:
466     case E_USAGE:
467     case E_ILLPASSWORD:
468     case E_LEVELTOOHIGH:
469     case E_NOSUPER:
470     case E_NOSAVEFILE:
471     case E_NOBITMAP:
472     case E_NODISPLAY:
473     case E_NOFONT:
474     case E_NOMEM:
475     case E_NOCOLOR:
476       fprintf(stderr, "%s: %s\n", progname, errmess[err]);
477       if (err == E_USAGE)
478         Usage();
479       break;
480     default:
481       if (err != E_ENDGAME && err != E_ABORTLEVEL)
482 	fprintf(stderr, "%s: %s\n", progname, errmess[0]);
483       break;
484   }
485 }
486 
487 /* this simply prints out the usage string nicely. */
Usage(void)488 static void Usage(void)
489 {
490   short i;
491 
492   fprintf(stderr, USAGESTR, progname);
493   for (i = 0; usages[i] != NULL; i++)
494     fprintf(stderr, "%s", usages[i]);
495 }
496