1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44: #include <err.h>
45: #include <errno.h>
46: #include <fcntl.h>
47: #include <pwd.h>
48: #include <stdio.h>
49: #include <stdlib.h>
50: #include <string.h>
51: #include <sys/file.h>
52: #include <sys/stat.h>
53: #include <time.h>
54: #include <termcap.h>
55: #include <unistd.h>
56:
57: #include "pathnames.h"
58: #include "screen.h"
59: #include "scores.h"
60: #include "tetris.h"
61:
62:
63:
64:
65:
66:
67:
68:
69:
70: #define NUMSPOTS (MAXHISCORES + 1)
71: #define NLEVELS (MAXLEVEL + 1)
72:
73: static time_t now;
74: static int nscores;
75: static int gotscores;
76: static struct highscore scores[NUMSPOTS];
77:
78: static int checkscores(struct highscore *, int);
79: static int cmpscores(const void *, const void *);
80: static void getscores(FILE **);
81: static void printem(int, int, struct highscore *, int, const char *);
82: static char *thisuser(void);
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93: static void
94: getscores(fpp)
95: FILE **fpp;
96: {
97: int sd, mint, lck;
98: mode_t mask;
99: const char *mstr, *human;
100: FILE *sf;
101:
102: if (fpp != NULL) {
103: mint = O_RDWR | O_CREAT;
104: mstr = "r+";
105: human = "read/write";
106: lck = LOCK_EX;
107: } else {
108: mint = O_RDONLY;
109: mstr = "r";
110: human = "reading";
111: lck = LOCK_SH;
112: }
113: setegid(egid);
114: mask = umask(S_IWOTH);
115: sd = open(_PATH_SCOREFILE, mint, 0666);
116: (void)umask(mask);
117: if (sd < 0) {
118: if (fpp == NULL) {
119: nscores = 0;
120: setegid(gid);
121: return;
122: }
123: err(1, "cannot open %s for %s", _PATH_SCOREFILE, human);
124: }
125: if ((sf = fdopen(sd, mstr)) == NULL) {
126: err(1, "cannot fdopen %s for %s", _PATH_SCOREFILE, human);
127: }
128: setegid(gid);
129:
130:
131:
132:
133: if (flock(sd, lck))
134: warn("warning: score file %s cannot be locked",
135: _PATH_SCOREFILE);
136:
137: nscores = fread(scores, sizeof(scores[0]), MAXHISCORES, sf);
138: if (ferror(sf)) {
139: err(1, "error reading %s", _PATH_SCOREFILE);
140: }
141:
142: if (fpp)
143: *fpp = sf;
144: else
145: (void)fclose(sf);
146: }
147:
148: void
149: savescore(level)
150: int level;
151: {
152: struct highscore *sp;
153: int i;
154: int change;
155: FILE *sf;
156: const char *me;
157:
158: getscores(&sf);
159: gotscores = 1;
160: (void)time(&now);
161:
162:
163:
164:
165:
166:
167: change = 0;
168: me = thisuser();
169: for (i = 0, sp = &scores[0]; i < nscores; i++, sp++) {
170: if (sp->hs_level != level || strcmp(sp->hs_name, me) != 0)
171: continue;
172: if (score > sp->hs_score) {
173: (void)printf("%s bettered %s %d score of %d!\n",
174: "\nYou", "your old level", level,
175: sp->hs_score * sp->hs_level);
176: sp->hs_score = score;
177: sp->hs_time = now;
178: change = 1;
179: } else if (score == sp->hs_score) {
180: (void)printf("%s tied %s %d high score.\n",
181: "\nYou", "your old level", level);
182: sp->hs_time = now;
183: change = 1;
184: }
185: break;
186: }
187: if (i >= nscores) {
188: strcpy(sp->hs_name, me);
189: sp->hs_level = level;
190: sp->hs_score = score;
191: sp->hs_time = now;
192: nscores++;
193: change = 1;
194: }
195:
196: if (change) {
197:
198:
199:
200: nscores = checkscores(scores, nscores);
201: rewind(sf);
202: if (fwrite(scores, sizeof(*sp), nscores, sf) != (size_t)nscores ||
203: fflush(sf) == EOF)
204: warnx("error writing %s: %s -- %s",
205: _PATH_SCOREFILE, strerror(errno),
206: "high scores may be damaged");
207: }
208: (void)fclose(sf);
209: }
210:
211:
212:
213:
214:
215: static char *
216: thisuser()
217: {
218: const char *p;
219: struct passwd *pw;
220: size_t l;
221: static char u[sizeof(scores[0].hs_name)];
222:
223: if (u[0])
224: return (u);
225: p = getlogin();
226: if (p == NULL || *p == '\0') {
227: pw = getpwuid(getuid());
228: if (pw != NULL)
229: p = pw->pw_name;
230: else
231: p = " ???";
232: }
233: l = strlen(p);
234: if (l >= sizeof(u))
235: l = sizeof(u) - 1;
236: memcpy(u, p, l);
237: u[l] = '\0';
238: return (u);
239: }
240:
241:
242:
243:
244:
245:
246:
247: static int
248: cmpscores(x, y)
249: const void *x, *y;
250: {
251: const struct highscore *a, *b;
252: long l;
253:
254: a = x;
255: b = y;
256: l = (long)b->hs_level * b->hs_score - (long)a->hs_level * a->hs_score;
257: if (l < 0)
258: return (-1);
259: if (l > 0)
260: return (1);
261: if (a->hs_time < b->hs_time)
262: return (-1);
263: if (a->hs_time > b->hs_time)
264: return (1);
265: return (0);
266: }
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277: static int
278: checkscores(hs, num)
279: struct highscore *hs;
280: int num;
281: {
282: struct highscore *sp;
283: int i, j, k, numnames;
284: int levelfound[NLEVELS];
285: struct peruser {
286: char *name;
287: int times;
288: } count[NUMSPOTS];
289: struct peruser *pu;
290:
291:
292:
293:
294:
295:
296:
297: qsort((void *)hs, nscores, sizeof(*hs), cmpscores);
298: for (i = MINLEVEL; i < NLEVELS; i++)
299: levelfound[i] = 0;
300: numnames = 0;
301: for (i = 0, sp = hs; i < num;) {
302:
303:
304:
305: for (j = 0, pu = count; j < numnames; j++, pu++)
306: if (strcmp(sp->hs_name, pu->name) == 0)
307: break;
308: if (j == numnames) {
309:
310:
311:
312: pu->name = sp->hs_name;
313: pu->times = 1;
314: numnames++;
315: } else {
316:
317:
318:
319:
320:
321:
322: if ((pu->times < MAXSCORES &&
323: getpwnam(sp->hs_name) != NULL &&
324: sp->hs_time + EXPIRATION >= now) ||
325: levelfound[sp->hs_level] == 0)
326: pu->times++;
327: else {
328:
329:
330:
331:
332: num--;
333: for (k = i; k < num; k++)
334: hs[k] = hs[k + 1];
335: continue;
336: }
337: }
338: levelfound[sp->hs_level] = 1;
339: i++, sp++;
340: }
341: return (num > MAXHISCORES ? MAXHISCORES : num);
342: }
343:
344:
345:
346:
347:
348:
349:
350:
351: void
352: showscores(level)
353: int level;
354: {
355: struct highscore *sp;
356: int i, n, c;
357: const char *me;
358: int levelfound[NLEVELS];
359:
360: if (!gotscores)
361: getscores((FILE **)NULL);
362: (void)printf("\n\t\t\t Tetris High Scores\n");
363:
364:
365:
366:
367:
368:
369: me = level && SOstr ? thisuser() : NULL;
370:
371:
372:
373:
374: for (i = MINLEVEL; i < NLEVELS; i++)
375: levelfound[i] = 0;
376: for (i = 0, sp = scores; i < nscores; i++, sp++) {
377: if (levelfound[sp->hs_level])
378: sp->hs_time = 0;
379: else {
380: sp->hs_time = 1;
381: levelfound[sp->hs_level] = 1;
382: }
383: }
384:
385:
386:
387:
388: for (i = 0, sp = scores; i < nscores; sp += n) {
389: n = 40;
390: if (i + n > nscores)
391: n = nscores - i;
392: printem(level, i + 1, sp, n, me);
393: if ((i += n) < nscores) {
394: (void)printf("\nHit RETURN to continue.");
395: (void)fflush(stdout);
396: while ((c = getchar()) != '\n')
397: if (c == EOF)
398: break;
399: (void)printf("\n");
400: }
401: }
402: }
403:
404: static void
405: printem(level, offset, hs, n, me)
406: int level, offset;
407: struct highscore *hs;
408: int n;
409: const char *me;
410: {
411: struct highscore *sp;
412: int nrows, row, col, item, i, highlight;
413: char buf[100];
414: #define TITLE "Rank Score Name (points/level)"
415:
416:
417:
418:
419:
420: printf("%s %s\n", TITLE, n > 1 ? TITLE : "");
421:
422: highlight = 0;
423: nrows = (n + 1) / 2;
424:
425: for (row = 0; row < nrows; row++) {
426: for (col = 0; col < 2; col++) {
427: item = col * nrows + row;
428: if (item >= n) {
429:
430:
431:
432: (void)putchar('\n');
433: continue;
434: }
435: sp = &hs[item];
436: (void)sprintf(buf,
437: "%3d%c %6d %-11s (%6d on %d)",
438: item + offset, sp->hs_time ? '*' : ' ',
439: sp->hs_score * sp->hs_level,
440: sp->hs_name, sp->hs_score, sp->hs_level);
441:
442:
443:
444:
445: if (me != NULL &&
446: sp->hs_level == level &&
447: sp->hs_score == score &&
448: strcmp(sp->hs_name, me) == 0) {
449: putpad(SOstr);
450: highlight = 1;
451: }
452: (void)printf("%s", buf);
453: if (highlight) {
454: putpad(SEstr);
455: highlight = 0;
456: }
457:
458:
459: if (col == 0)
460: for (i = 40 - strlen(buf); --i >= 0;)
461: (void)putchar(' ');
462: else
463: (void)putchar('\n');
464: }
465: }
466: }