1 
2 /*-
3 # X-BASED MISSING LINK(tm)
4 #
5 #  xmlink.c
6 #
7 ###
8 #
9 #  Copyright (c) 1994 - 99	David Albert Bagley, bagleyd@tux.org
10 #
11 #                   All Rights Reserved
12 #
13 #  Permission to use, copy, modify, and distribute this software and
14 #  its documentation for any purpose and without fee is hereby granted,
15 #  provided that the above copyright notice appear in all copies and
16 #  that both that copyright notice and this permission notice appear in
17 #  supporting documentation, and that the name of the author not be
18 #  used in advertising or publicity pertaining to distribution of the
19 #  software without specific, written prior permission.
20 #
21 #  This program is distributed in the hope that it will be "playable",
22 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
23 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
24 #
25 */
26 
27 /*-
28   Version 1: 94/08/30 Xt
29 */
30 
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <errno.h>
34 #ifdef VMS
35 #include <unixlib.h>
36 #define getlogin() cuserid(NULL)
37 #else
38 #if HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #endif
42 #if HAVE_FCNTL_H
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <fcntl.h>
46 #endif
47 #include <X11/Intrinsic.h>
48 #include <X11/StringDefs.h>
49 #include <X11/Shell.h>
50 #include <X11/cursorfont.h>
51 #include "Mlink.h"
52 #include "mlink.xbm"
53 
54 #ifndef SCOREFILE
55 #define SCOREFILE "/usr/games/lib/mlink.scores"
56 #endif
57 
58 /* The following are in MlinkP.h also */
59 #define MINFACES 1
60 #define MAXFACES 8
61 #define MINTILES 1
62 
63 #define MAXTILES 8
64 #define MAXRECORD 32767
65 #define FILENAMELEN 1024
66 #define USERNAMELEN 128
67 #define NOACCESS "noaccess"
68 #define NOBODY "nobody"
69 
70 typedef struct {
71 	int         score;
72 	char        name[USERNAMELEN];
73 } GameRecord;
74 
75 static void Initialize(Widget w);
76 static void CallbackMlink(Widget w, caddr_t clientData,
77 			  mlinkCallbackStruct * callData);
78 
79 static void PrintRecord(int tiles, int faces, Boolean orient, Boolean middle,
80 			char *record);
81 static Boolean HandleSolved(int counter,
82 		       int tiles, int faces, Boolean orient, Boolean middle);
83 static void PrintState(Widget w,
84 		 char *prog, int tiles, int faces, Boolean middle, int moves,
85 		       char *record, char *message);
86 static void InitRecords(void);
87 static void ReadRecords(void);
88 static void WriteRecords(void);
89 
90 static Arg  arg[2];
91 static GameRecord mlinkRecord[2][2][MAXFACES - MINFACES + 1]
92 [MAXTILES - MINTILES + 1];
93 static int  movesDsp = 0;
94 static char progDsp[64] = "xmlink";
95 static char recordDsp[16] = "INF";
96 static char messageDsp[128] = "Welcome";
97 static char titleDsp[256] = "";
98 static char usernameDsp[USERNAMELEN] = "";
99 
100 static void
Usage(void)101 Usage(void)
102 {
103 	(void) fprintf(stderr, "usage: xmlink\n");
104 	(void) fprintf(stderr,
105 	     "\t[-geometry [{width}][x{height}][{+-}{xoff}[{+-}{yoff}]]]\n");
106 	(void) fprintf(stderr,
107 	   "\t[-display [{host}]:[{vs}]] [-[no]mono] [-[no]{reverse|rv}]\n");
108 	(void) fprintf(stderr,
109 		"\t[-{foreground|fg} {color}] [-{background|bg} {color}]\n");
110 	(void) fprintf(stderr,
111 		       "\t[-{border|bd} {color}] [-tile {color}]\n");
112 	(void) fprintf(stderr,
113 		       "\t[-face{0|1|2|3|4|5|6|7} {color}]\n");
114 	(void) fprintf(stderr,
115 	    "\t[-tiles {int}] [-faces {int}] [-[no]orient] [-[no]middle]\n");
116 	(void) fprintf(stderr,
117 		       "\t[-base {int}] [-username {string}]\n");
118 	exit(1);
119 }
120 
121 static XrmOptionDescRec options[] =
122 {
123 	{"-mono", "*mlink.mono", XrmoptionNoArg, "TRUE"},
124 	{"-nomono", "*mlink.mono", XrmoptionNoArg, "FALSE"},
125 	{"-rv", "*mlink.reverse", XrmoptionNoArg, "TRUE"},
126 	{"-reverse", "*mlink.reverse", XrmoptionNoArg, "TRUE"},
127 	{"-norv", "*mlink.reverse", XrmoptionNoArg, "FALSE"},
128 	{"-noreverse", "*mlink.reverse", XrmoptionNoArg, "FALSE"},
129 	{"-fg", "*mlink.Foreground", XrmoptionSepArg, NULL},
130 	{"-foreground", "*mlink.Foreground", XrmoptionSepArg, NULL},
131 	{"-bg", "*Background", XrmoptionSepArg, NULL},
132 	{"-background", "*Background", XrmoptionSepArg, NULL},
133 	{"-bd", "*mlink.tileBorder", XrmoptionSepArg, NULL},
134 	{"-border", "*mlink.tileBorder", XrmoptionSepArg, NULL},
135 	{"-tile", "*mlink.tileColor", XrmoptionSepArg, NULL},
136 	{"-face0", "*mlink.faceColor0", XrmoptionSepArg, NULL},
137 	{"-face1", "*mlink.faceColor1", XrmoptionSepArg, NULL},
138 	{"-face2", "*mlink.faceColor2", XrmoptionSepArg, NULL},
139 	{"-face3", "*mlink.faceColor3", XrmoptionSepArg, NULL},
140 	{"-face4", "*mlink.faceColor4", XrmoptionSepArg, NULL},
141 	{"-face5", "*mlink.faceColor5", XrmoptionSepArg, NULL},
142 	{"-face6", "*mlink.faceColor6", XrmoptionSepArg, NULL},
143 	{"-face7", "*mlink.faceColor7", XrmoptionSepArg, NULL},
144 	{"-tiles", "*mlink.tiles", XrmoptionSepArg, NULL},
145 	{"-faces", "*mlink.faces", XrmoptionSepArg, NULL},
146 	{"-orient", "*mlink.orient", XrmoptionNoArg, "TRUE"},
147 	{"-noorient", "*mlink.orient", XrmoptionNoArg, "FALSE"},
148 	{"-middle", "*mlink.middle", XrmoptionNoArg, "TRUE"},
149 	{"-nomiddle", "*mlink.middle", XrmoptionNoArg, "FALSE"},
150 	{"-base", "*mlink.base", XrmoptionSepArg, NULL},
151 	{"-username", "*mlink.userName", XrmoptionSepArg, NULL}
152 };
153 
154 int
main(int argc,char ** argv)155 main(int argc, char **argv)
156 {
157 	Widget      toplevel, mlink;
158 
159 	toplevel = XtInitialize(argv[0], "Mlink",
160 				options, XtNumber(options), &argc, argv);
161 	if (argc != 1)
162 		Usage();
163 
164 	XtSetArg(arg[0],
165 		 XtNiconPixmap, XCreateBitmapFromData(XtDisplay(toplevel),
166 				      RootWindowOfScreen(XtScreen(toplevel)),
167 			    (char *) mlink_bits, mlink_width, mlink_height));
168 	XtSetArg(arg[1], XtNinput, True);
169 	XtSetValues(toplevel, arg, 2);
170 	mlink = XtCreateManagedWidget("mlink",
171 				      mlinkWidgetClass, toplevel, NULL, 0);
172 	XtAddCallback(mlink,
173 	XtNselectCallback, (XtCallbackProc) CallbackMlink, (XtPointer) NULL);
174 	Initialize(mlink);
175 	XtRealizeWidget(toplevel);
176 	XGrabButton(XtDisplay(mlink), (unsigned int) AnyButton, AnyModifier,
177 		    XtWindow(mlink), TRUE,
178 		    (unsigned int) (ButtonPressMask | ButtonMotionMask | ButtonReleaseMask),
179 		    GrabModeAsync, GrabModeAsync, XtWindow(mlink),
180 		    XCreateFontCursor(XtDisplay(mlink), XC_crosshair));
181 	XtMainLoop();
182 
183 #ifdef VMS
184 	return 1;
185 #else
186 	return 0;
187 #endif
188 }
189 
190 static void
Initialize(Widget w)191 Initialize(Widget w)
192 {
193 	int         tiles, faces;
194 	Boolean     orient, middle;
195 	String      username;
196 
197 	XtVaSetValues(w,
198 		      XtNstart, False, NULL);
199 	XtVaGetValues(w,
200 		      XtNuserName, &username,
201 		      XtNtiles, &tiles,
202 		      XtNfaces, &faces,
203 		      XtNorient, &orient,
204 		      XtNmiddle, &middle, NULL);
205 	InitRecords();
206 	ReadRecords();
207 	(void) strcpy(usernameDsp, username);
208 	if (!strcmp(usernameDsp, "") || !strcmp(usernameDsp, NOACCESS) ||
209 	    !strcmp(usernameDsp, NOBODY)) {
210 		/* The NOACCESS is not necasary, but it stops people from being cute. */
211 		(void) sprintf(usernameDsp, "%s", getlogin());
212 		if (!strcmp(usernameDsp, "") || !strcmp(usernameDsp, NOACCESS))
213 			(void) sprintf(usernameDsp, "%s", NOBODY);	/* It really IS nobody */
214 	}
215 	PrintRecord(tiles, faces, orient, middle, recordDsp);
216 	PrintState(XtParent(w), progDsp, tiles, faces, middle, movesDsp,
217 		   recordDsp, messageDsp);
218 }
219 
220 static void
CallbackMlink(Widget w,caddr_t clientData,mlinkCallbackStruct * callData)221 CallbackMlink(Widget w, caddr_t clientData, mlinkCallbackStruct * callData)
222 {
223 	int         tiles, faces;
224 	Boolean     orient, middle;
225 
226 	XtVaGetValues(w,
227 		      XtNtiles, &tiles,
228 		      XtNfaces, &faces,
229 		      XtNorient, &orient,
230 		      XtNmiddle, &middle, NULL);
231 	(void) strcpy(messageDsp, "");
232 	switch (callData->reason) {
233 		case MLINK_RESTORE:
234 		case MLINK_RESET:
235 			movesDsp = 0;
236 			break;
237 		case MLINK_BLOCKED:
238 			(void) strcpy(messageDsp, "Blocked");
239 			break;
240 		case MLINK_SPACE:
241 #if 0
242 			/* Too annoying */
243 			(void) strcpy(messageDsp, "A space can not slide");
244 #endif
245 			break;
246 		case MLINK_IGNORE:
247 			(void) strcpy(messageDsp, "Randomize to start");
248 			break;
249 		case MLINK_MOVED:
250 			movesDsp++;
251 			XtSetArg(arg[0], XtNstart, True);
252 			XtSetValues(w, arg, 1);
253 			break;
254 		case MLINK_CONTROL:
255 			return;
256 		case MLINK_SOLVED:
257 			if (HandleSolved(movesDsp, tiles, faces, orient, middle))
258 				(void) sprintf(messageDsp, "Congratulations %s!!", usernameDsp);
259 			else
260 				(void) strcpy(messageDsp, "Solved!");
261 			XtSetArg(arg[0], XtNstart, False);
262 			XtSetValues(w, arg, 1);
263 			break;
264 		case MLINK_RANDOMIZE:
265 			movesDsp = 0;
266 			XtSetArg(arg[0], XtNstart, False);
267 			XtSetValues(w, arg, 1);
268 			break;
269 		case MLINK_ORIENT:
270 			movesDsp = 0;
271 			orient = !orient;
272 			PrintRecord(tiles, faces, orient, middle, recordDsp);
273 			XtSetArg(arg[0], XtNorient, orient);
274 			XtSetValues(w, arg, 1);
275 			break;
276 		case MLINK_MIDDLE:
277 			movesDsp = 0;
278 			middle = !middle;
279 			PrintRecord(tiles, faces, orient, middle, recordDsp);
280 			XtSetArg(arg[0], XtNmiddle, middle);
281 			XtSetValues(w, arg, 1);
282 			break;
283 		case MLINK_DEC_X:
284 			movesDsp = 0;
285 			tiles--;
286 			PrintRecord(tiles, faces, orient, middle, recordDsp);
287 			XtSetArg(arg[0], XtNtiles, tiles);
288 			XtSetValues(w, arg, 1);
289 			break;
290 		case MLINK_INC_X:
291 			movesDsp = 0;
292 			tiles++;
293 			PrintRecord(tiles, faces, orient, middle, recordDsp);
294 			XtSetArg(arg[0], XtNtiles, tiles);
295 			XtSetValues(w, arg, 1);
296 			break;
297 		case MLINK_DEC_Y:
298 			movesDsp = 0;
299 			faces--;
300 			PrintRecord(tiles, faces, orient, middle, recordDsp);
301 			XtSetArg(arg[0], XtNfaces, faces);
302 			XtSetValues(w, arg, 1);
303 			break;
304 		case MLINK_INC_Y:
305 			movesDsp = 0;
306 			faces++;
307 			PrintRecord(tiles, faces, orient, middle, recordDsp);
308 			XtSetArg(arg[0], XtNfaces, faces);
309 			XtSetValues(w, arg, 1);
310 			break;
311 		case MLINK_COMPUTED:
312 			XtSetArg(arg[0], XtNstart, False);
313 			XtSetValues(w, arg, 1);
314 			break;
315 		case MLINK_UNDO:
316 			movesDsp--;
317 			XtSetArg(arg[0], XtNstart, True);
318 			XtSetValues(w, arg, 1);
319 			break;
320 	}
321 	PrintState(XtParent(w), progDsp, tiles, faces, middle, movesDsp,
322 		   recordDsp, messageDsp);
323 }
324 
325 static void
PrintRecord(int tiles,int faces,Boolean orient,Boolean middle,char * record)326 PrintRecord(int tiles, int faces, Boolean orient, Boolean middle, char *record)
327 {
328 	int         i = tiles - MINTILES, j = faces - MINFACES;
329 	int         k = (orient) ? 1 : 0, l = (middle) ? 1 : 0;
330 
331 	if (tiles > MAXTILES)
332 		(void) strcpy(record, "NOT RECORDED");
333 	else if (mlinkRecord[l][k][j][i].score >= MAXRECORD)
334 		(void) sprintf(record, "NEVER %s", NOACCESS);
335 	else
336 		(void) sprintf(record, "%d %s",
337 		mlinkRecord[l][k][j][i].score, mlinkRecord[l][k][j][i].name);
338 }
339 
340 static      Boolean
HandleSolved(int counter,int tiles,int faces,Boolean orient,Boolean middle)341 HandleSolved(int counter, int tiles, int faces, Boolean orient, Boolean middle)
342 {
343 	int         i = tiles - MINTILES, j = faces - MINFACES;
344 	int         k = (orient) ? 1 : 0, l = (middle) ? 1 : 0;
345 
346 	if (tiles <= MAXTILES && counter < mlinkRecord[l][k][j][i].score) {
347 		mlinkRecord[l][k][j][i].score = counter;
348 		(void) strcpy(mlinkRecord[l][k][j][i].name, usernameDsp);
349 		if (tiles < 4 || faces < 2) {
350 			mlinkRecord[!l][k][j][i].score = counter;
351 			(void) strcpy(mlinkRecord[!l][k][j][i].name, usernameDsp);
352 		}
353 		WriteRecords();
354 		PrintRecord(tiles, faces, orient, middle, recordDsp);
355 		return True;
356 	}
357 	return False;
358 }
359 
360 static void
PrintState(Widget w,char * prog,int tiles,int faces,Boolean middle,int moves,char * record,char * message)361 PrintState(Widget w, char *prog, int tiles, int faces, Boolean middle, int moves, char *record, char *message)
362 {
363 	if (middle)
364 		(void) sprintf(titleDsp, "%s: %dx%d norm@ (%d/%s) - %s",
365 			       prog, tiles, faces, moves, record, message);
366 	else
367 		(void) sprintf(titleDsp, "%s: %dx%d ind@ (%d/%s) - %s",
368 			       prog, tiles, faces, moves, record, message);
369 	XtSetArg(arg[0], XtNtitle, titleDsp);
370 	XtSetValues(w, arg, 1);
371 }
372 
373 static void
InitRecords(void)374 InitRecords(void)
375 {
376 	int         i, j, k, l;
377 
378 	for (l = 0; l < 2; l++)
379 		for (k = 0; k < 2; k++)
380 			for (j = 0; j < MAXFACES - MINFACES + 1; j++)
381 				for (i = 0; i < MAXTILES - MINTILES + 1; i++) {
382 					mlinkRecord[l][k][j][i].score = MAXRECORD;
383 					(void) strcpy(mlinkRecord[l][k][j][i].name, NOACCESS);
384 				}
385 }
386 
387 static void
ReadRecords(void)388 ReadRecords(void)
389 {
390 	FILE       *fp;
391 	int         i, j, k, l, n;
392 	char        username[USERNAMELEN];
393 
394 	if ((fp = fopen(SCOREFILE, "r")) == NULL)
395 		(void) sprintf(messageDsp, "Can not open %s, taking defaults.", SCOREFILE);
396 	else {
397 		for (l = 0; l < 2; l++)
398 			for (k = 0; k < 2; k++)
399 				for (j = 0; j < MAXFACES - MINFACES + 1; j++)
400 					for (i = 0; i < MAXTILES - MINTILES + 1; i++) {
401 						(void) fscanf(fp, "%d %s\n", &n, username);
402 						if (n <= mlinkRecord[l][k][j][i].score) {
403 							mlinkRecord[l][k][j][i].score = n;
404 							(void) strcpy(mlinkRecord[l][k][j][i].name, username);
405 						}
406 					}
407 		(void) fclose(fp);
408 	}
409 }
410 
411 static void
WriteRecords(void)412 WriteRecords(void)
413 {
414 	FILE       *fp;
415 	int         i, j, k, l;
416 
417 	ReadRecords();		/* Maybe its been updated by another */
418 	if ((fp = fopen(SCOREFILE, "w")) == NULL)
419 		(void) sprintf(messageDsp, "Can not write to %s.", SCOREFILE);
420 	else {
421 #if HAVE_FCNTL_H
422 		int         lfd;
423 		char        lockfile[FILENAMELEN];
424 
425 		(void) strcpy(lockfile, SCOREFILE);
426 		(void) strcat(lockfile, ".lock");
427 		while (((lfd = open(lockfile, O_CREAT | O_EXCL, 0644)) < 0) &&
428 		       errno == EEXIST)
429 			(void) sleep(1);
430 		if (lfd < 0) {
431 #if 1
432 			(void) fprintf(stderr, "Lock file exists... guessing its an old one.\n");
433 #else
434 			(void) fprintf(stderr, "Lock file exists... score not recorded - sorry.\n"
435 				);
436 			return;
437 #endif
438 		}
439 #endif
440 		for (l = 0; l < 2; l++) {
441 			for (k = 0; k < 2; k++) {
442 				for (j = 0; j < MAXFACES - MINFACES + 1; j++) {
443 					for (i = 0; i < MAXTILES - MINTILES + 1; i++)
444 						(void) fprintf(fp, "%d %s\n",
445 							       mlinkRecord[l][k][j][i].score, mlinkRecord[l][k][j][i].name);
446 					(void) fprintf(fp, "\n");
447 				}
448 				(void) fprintf(fp, "\n");
449 			}
450 			(void) fprintf(fp, "\n");
451 		}
452 #if HAVE_FCNTL_H
453 		(void) close(lfd);
454 		(void) unlink(lockfile);
455 #endif
456 		(void) fclose(fp);
457 	}
458 }
459