~ubuntu-branches/ubuntu/precise/typespeed/precise

« back to all changes in this revision

Viewing changes to src/file.c

  • Committer: Bazaar Package Importer
  • Author(s): Dafydd Harries
  • Date: 2007-12-07 05:14:13 UTC
  • mto: (4.1.2 sid)
  • mto: This revision was merged to the branch mainline in revision 5.
  • Revision ID: james.westby@ubuntu.com-20071207051413-x6wtxujyiabvyuwu
Tags: upstream-0.6.4
ImportĀ upstreamĀ versionĀ 0.6.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  typespeed - measures your typing speed
 
3
 *  Copyright (C) 1999-2003   Jani Ollikainen  <bestis@iki.fi>
 
4
 *                          & Jaakko Manelius  <jman@iki.fi>
 
5
 *  Copyright (C) 2006-2007   Tobias Stoeckmann  <tobias@bugol.de>
 
6
 *
 
7
 *  This program is free software; you can redistribute it and/or modify
 
8
 *  it under the terms of the GNU General Public License as published by
 
9
 *  the Free Software Foundation; either version 2 of the License, or
 
10
 *  (at your option) any later version.
 
11
 *
 
12
 *  This program is distributed in the hope that it will be useful,
 
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
 *  GNU General Public License for more details.
 
16
 *
 
17
 *  You should have received a copy of the GNU General Public License
 
18
 *  along with this program; if not, write to the Free Software
 
19
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
20
 */
 
21
 
 
22
/*
 
23
 * file.c - file access functions
 
24
 */
 
25
 
 
26
#ifdef HAVE_CONFIG_H
 
27
        #include "config.h"
 
28
#endif /* HAVE_CONFIG_H */
 
29
 
 
30
#ifdef HAVE_SYS_FILE_H
 
31
        #include <sys/file.h>
 
32
#endif /* HAVE_SYS_FILE_H */
 
33
 
 
34
#ifdef HAVE_SYS_PARAM_H
 
35
        #include <sys/param.h>
 
36
#endif /* HAVE_SYS_PARAM_H */
 
37
 
 
38
#ifdef HAVE_SYS_STAT_H
 
39
        #include <sys/stat.h>
 
40
#endif /* HAVE_SYS_STAT_H */
 
41
 
 
42
#ifdef HAVE_SYS_TYPES_H
 
43
        #include <sys/types.h>
 
44
#endif /* HAVE_SYS_TYPES_H */
 
45
 
 
46
#ifdef HAVE_BITS_POSIX1_LIM_H
 
47
        #include <bits/posix1_lim.h>
 
48
#endif /* HAVE_BITS_POSIX1_LIM_H */
 
49
 
 
50
#include <ctype.h>
 
51
#include <curses.h>
 
52
 
 
53
#ifdef HAVE_DIRENT_H
 
54
        #include <dirent.h>
 
55
#endif /* HAVE_DIRENT_H */
 
56
 
 
57
#include <errno.h>
 
58
 
 
59
#ifdef HAVE_FCNTL_H
 
60
        #include <fcntl.h>
 
61
#endif /* HAVE_FCNTL_H */
 
62
 
 
63
#include <stdint.h>
 
64
#include <stdio.h>
 
65
 
 
66
#ifdef HAVE_STDLIB_H
 
67
        #include <stdlib.h>
 
68
#endif /* HAVE_STDLIB_H */
 
69
 
 
70
#ifdef HAVE_STRING_H
 
71
        #include <string.h>
 
72
#endif /* HAVE_STRING_H */
 
73
 
 
74
#ifdef HAVE_UNISTD_H
 
75
        #include <unistd.h>
 
76
#endif /* HAVE_UNISTD_H */
 
77
 
 
78
#include "gettext.h"
 
79
#include "pathnames.h"
 
80
#include "typespeed.h"
 
81
 
 
82
#define _(string)       gettext(string)
 
83
#define WORDFILE_MAX    100
 
84
 
 
85
struct hentry {
 
86
        uint32_t count;
 
87
        uint32_t tcount;
 
88
        uint32_t wordswritten;
 
89
        char name[41];
 
90
        char mod[FILENAME_MAX * 2 + 1];
 
91
        char wordlist[FILENAME_MAX * 2 + 1];
 
92
        clock_t duration;
 
93
        uint32_t sinit;
 
94
};
 
95
 
 
96
extern unsigned long     cstrl(char *);
 
97
extern void              defrule(void);
 
98
extern void              drawscreen(void);
 
99
extern int               fileselmenu(int, struct finfo *, const char *);
 
100
extern unsigned short    level(int);
 
101
extern void              liima_mvgetnstr(int, int, char *, int);
 
102
extern int               netrecv(int, int, int, int, char *, size_t);
 
103
extern int               netsend(char *);
 
104
extern int               netsendscore(struct stats *, char *);
 
105
extern void              nowordlist(void);
 
106
extern void              pressanykey(int, int);
 
107
extern int               typorankkaus(float);
 
108
extern void              xcolor_set(short);
 
109
extern void              xerr(int, const char *, ...);
 
110
extern void              xerrx(int, const char *, ...);
 
111
extern void             *xmalloc(size_t);
 
112
extern int               xsnprintf(char *, size_t, const char *, ...);
 
113
extern char             *xstrdup(char *);
 
114
extern void              xstrncpy(char *, char *, size_t);
 
115
 
 
116
int                      addscore(char *, struct stats *);
 
117
int                      chooseruleset(void);
 
118
int                      choosewordfile(int);
 
119
int                      compar(const void *, const void *);
 
120
void                     freewords(void);
 
121
static struct hentry    *getentry(char *);
 
122
static int               getfinfo(struct finfo *, int n, char *);
 
123
static int               getnum(char *);
 
124
int                      hcompar(const void *, const void *);
 
125
int                      loadscores(char *);
 
126
static int               parseline(char *, char **, char **);
 
127
void                     readconfig(void);
 
128
static void              readfile(char *, int);
 
129
static void              setoptions(char *, char *, int);
 
130
 
 
131
#ifdef TEST
 
132
char                    *escstr(char *);
 
133
int                      loadwords(char *);
 
134
char                    *unescstr(char *);
 
135
#else
 
136
static char             *escstr(char *);
 
137
static int               loadwords(char *);
 
138
static char             *unescstr(char *);
 
139
#endif /* TEST */
 
140
 
 
141
extern int       hfd;
 
142
extern char     *rankki[11];
 
143
extern char     *typorank[12];
 
144
extern char     *usedwordfile;
 
145
 
 
146
char ruledir[MAXPATHLEN];
 
147
char worddir[MAXPATHLEN];
 
148
 
 
149
/*
 
150
 * Asks player about his/her name. The entered name must be
 
151
 * changed so tabs are escaped. During write process, the
 
152
 * high score file will be locked.
 
153
 *
 
154
 * Returns 0 on success and 1 on failure.
 
155
 */
 
156
int
 
157
addscore(char *wordlist, struct stats *stat)
 
158
{
 
159
        FILE *highf;
 
160
        int fd;
 
161
        char *rname, *uname;
 
162
        char name[21];
 
163
 
 
164
        /* Ask player until a name has been entered. */
 
165
        do {
 
166
                drawscreen();
 
167
                mvaddstr(3, 2, _("Enter your name:"));
 
168
                liima_mvgetnstr(3, 3 + strlen(_("Enter your name:")), name,
 
169
                    sizeof(name) - 1);
 
170
                name[sizeof(name) - 1] = '\0';
 
171
        } while (name[0] == '\0' || name[0] == ' ');
 
172
 
 
173
        if (opt.net == NET)
 
174
                netsendscore(stat, name);
 
175
        else {
 
176
                rname = escstr(rules.fname);
 
177
                uname = escstr(name);
 
178
 
 
179
#ifndef WIN32
 
180
                (void)flock(hfd, LOCK_EX);
 
181
#endif /* WIN32 */
 
182
 
 
183
                if ((fd = dup(hfd)) == -1)
 
184
                        return 1;
 
185
 
 
186
                if ((highf = fdopen(fd, "r+")) == NULL)
 
187
                        return 1;
 
188
 
 
189
                fseek(highf, 0, SEEK_END);
 
190
                fprintf(highf, "%u\t%u\t%u\t%s\t%s\t%s\t%u\t%u\n",
 
191
                    stat->score, stat->tcount, stat->wordswritten, uname,
 
192
                    wordlist, rname, (uint32_t)stat->duration, stat->sinit);
 
193
 
 
194
                if (fclose(highf) == EOF)
 
195
                        return 1;
 
196
 
 
197
#ifndef WIN32
 
198
                (void)flock(hfd, LOCK_UN);
 
199
#endif /* WIN32 */
 
200
 
 
201
                free(rname);
 
202
                free(uname);
 
203
        }
 
204
 
 
205
        return 0;
 
206
}
 
207
 
 
208
/*
 
209
 * This function asks player about rule set of choice. All rule set
 
210
 * variables will be set.
 
211
 *
 
212
 * Returns ...
 
213
 * 0 on success
 
214
 * 1 if no rule set available
 
215
 * -1 if path to rule set is too long
 
216
 */
 
217
int
 
218
chooseruleset(void)
 
219
{
 
220
        int k;
 
221
        char fullpath[MAXPATHLEN];
 
222
        struct finfo namelist[WORDFILE_MAX];
 
223
 
 
224
        drawscreen();
 
225
 
 
226
        /* Default rule set must be selectable. */
 
227
        xstrncpy(namelist[0].descr, _("default"),
 
228
            sizeof(namelist[0].descr) - 1);
 
229
        xstrncpy(namelist[0].name, "default", sizeof(namelist[0].name) - 1);
 
230
 
 
231
        k = getfinfo(&(namelist[1]), WORDFILE_MAX - 1, "rule.");
 
232
        if (k++ < 1) {
 
233
                mvaddstr(1, 5, _("No rule sets found..."));
 
234
                pressanykey(3, 5);
 
235
                return 1;
 
236
        }
 
237
 
 
238
        qsort((void *)(namelist + 1), k - 1, sizeof(namelist[0]), compar);
 
239
 
 
240
        if (!(k = fileselmenu(k, namelist, _("rule set")))) {
 
241
                defrule();
 
242
                return 0;
 
243
        }
 
244
        if (k == -1)
 
245
                return 0;
 
246
 
 
247
        if (opt.net == NET) {
 
248
                if (xsnprintf(fullpath, sizeof(fullpath), "%s",
 
249
                    namelist[k].name))
 
250
                        return -1;
 
251
        } else {
 
252
                if (xsnprintf(fullpath, sizeof(fullpath), "%s/%s", ruledir,
 
253
                    namelist[k].name))
 
254
                        return -1;
 
255
        }
 
256
 
 
257
        readfile(fullpath, 0);
 
258
        xstrncpy(rules.fname, namelist[k].name, sizeof(rules.fname) - 1);
 
259
        xstrncpy(rules.name, namelist[k].descr, sizeof(rules.name) - 1);
 
260
 
 
261
        return 0;
 
262
}
 
263
 
 
264
/*
 
265
 * A huge function that asks player about word list.
 
266
 * In this menu you can select found word lists for further
 
267
 * processing. The processing of the word list depends on
 
268
 * "operation":
 
269
 *
 
270
 * 0: loads words out of word list
 
271
 * 1: prints high scores for word list (and current rule set)
 
272
 *
 
273
 * Returns 0 on success or 1 on failure.
 
274
 */
 
275
int
 
276
choosewordfile(int operation)
 
277
{
 
278
        int a, k;
 
279
        const char *errstr;
 
280
        struct finfo namelist[WORDFILE_MAX];
 
281
 
 
282
        k = getfinfo(namelist, WORDFILE_MAX, "words.");
 
283
        if (k < 1) {
 
284
                drawscreen();
 
285
                mvaddstr(1, 5, _("No word lists found..."));
 
286
                pressanykey(3, 5);
 
287
                return 1;
 
288
                /* NOTREACHED */
 
289
        }
 
290
 
 
291
        qsort((void *)namelist, k, sizeof(namelist[0]), compar);
 
292
 
 
293
        if ((k = fileselmenu(k, namelist, _("word list"))) == -1)
 
294
                return 1;
 
295
 
 
296
        switch (operation) {
 
297
        case 0:
 
298
                a = loadwords(namelist[k].name);
 
299
                break;
 
300
        case 1:
 
301
                loadscores(namelist[k].name);
 
302
                return 0;
 
303
                /* NOTREACHED */
 
304
                break;
 
305
        default:
 
306
                a = -1;
 
307
                break;
 
308
        }
 
309
 
 
310
        switch (a) {
 
311
        case 0: /* success */
 
312
                if (usedwordfile != NULL)
 
313
                        free(usedwordfile);
 
314
                usedwordfile = xstrdup(namelist[k].name);
 
315
                return 0;
 
316
                /* NOTREACHED */
 
317
                break;
 
318
        case 1: /* xsnprintf */
 
319
                errstr = _("Path to file is too long.");
 
320
                break;
 
321
        case 2: /* fopen - errno */
 
322
                errstr = strerror(errno);
 
323
                break;
 
324
        case 3: /* loadwords - not enough words */
 
325
                errstr = _("Not enough words in word list for current settings.");
 
326
                break;
 
327
        case 5: /* resetscorefile - error during write */
 
328
                errstr = _("Cannot write to high score file.");
 
329
                break;
 
330
        default:
 
331
                errstr = _("Something went wrong. Contact your mother.");
 
332
                break;
 
333
        }
 
334
 
 
335
        xcolor_set(2);
 
336
        mvaddstr(3, 5, errstr);
 
337
        getch();
 
338
 
 
339
        return 1;
 
340
}
 
341
 
 
342
/* helper function for qsort */
 
343
int
 
344
compar(const void *p1, const void *p2)
 
345
{
 
346
        int retval;
 
347
 
 
348
        struct finfo *elem1, *elem2;
 
349
 
 
350
        elem1 = (struct finfo *)p1;
 
351
        elem2 = (struct finfo *)p2;
 
352
 
 
353
        if (!(retval = strcmp(elem1->descr, elem2->descr)))
 
354
                xerrx(1, "%s and %s have the same description!\n",
 
355
                    elem1->name, worddir, elem2->name);
 
356
        return retval;
 
357
}
 
358
 
 
359
/*
 
360
 * All entries in high score file must be valid. If a player
 
361
 * has created a word list with a file name that contains a tab
 
362
 * (or rule set with tab), the tab must be escaped.
 
363
 *
 
364
 * Returns converted string that must be passed to free() later on.
 
365
 */
 
366
#ifndef TEST
 
367
static
 
368
#endif /* TEST */
 
369
char *
 
370
escstr(char *string)
 
371
{
 
372
        int pos;
 
373
        char *new, *p, *retval;
 
374
 
 
375
        if (string == NULL)
 
376
                return NULL;
 
377
 
 
378
        if ((new = calloc(2, strlen(string) + 1)) == NULL)
 
379
                xerr(1, "escstr: calloc");
 
380
 
 
381
        for (pos = 0, p = string; *p != '\0'; p++) {
 
382
                if (*p == '\\' || *p == '\t')
 
383
                        new[pos++] = '\\';
 
384
                new[pos++] = *p;
 
385
        }
 
386
        new[pos] = '\0';
 
387
 
 
388
        retval = xstrdup(new);
 
389
        free(new);
 
390
 
 
391
        return retval;
 
392
}
 
393
 
 
394
/* Frees all allocated memory in word. */
 
395
void
 
396
freewords(void)
 
397
{
 
398
        if (words.bulk != NULL) {
 
399
                free(words.bulk);
 
400
                words.bulk = NULL;
 
401
        }
 
402
        if (words.word != NULL) {
 
403
                free(words.word);
 
404
                words.word = NULL;
 
405
        }
 
406
        words.n = words.max = 0;
 
407
}
 
408
 
 
409
/*
 
410
 * This function converts string "line" into struct hentry. The converted
 
411
 * struct will be returned and must be passed to free() later on.
 
412
 *
 
413
 * Returns converted struct.
 
414
 */
 
415
static struct hentry *
 
416
getentry(char *line)
 
417
{
 
418
        int i, pos;
 
419
        char *p, *q, *t;
 
420
        struct hentry *entry;
 
421
 
 
422
        entry = xmalloc(sizeof(struct hentry));
 
423
        p = line;
 
424
 
 
425
        for (pos = 0; pos < 7; pos++) {
 
426
                /* do not allow empty field */
 
427
                if ((q = strchr(p, '\t')) == NULL || q == p)
 
428
                        xerrx(1, "bad entry in high score detected");
 
429
 
 
430
                for (i = 1; (q - i) >= p && *(q - i) == '\\'; i++)
 
431
                        ;
 
432
 
 
433
                if (!(i % 2)) {
 
434
                        p = q + 1;
 
435
                        continue;
 
436
                }
 
437
                *q = '\0';
 
438
 
 
439
                switch(pos) {
 
440
                case 0: /* count */
 
441
                        if (!(entry->count = cstrl(line)) && errno)
 
442
                                xerrx(1, "count is invalid: %s\n", line);
 
443
                        break;
 
444
                case 1: /* tcount */
 
445
                        if (!(entry->tcount = cstrl(line)) && errno)
 
446
                                xerrx(1, "tcount is invalid: %s\n", line);
 
447
                        break;
 
448
                case 2: /* wordswritten */
 
449
                        if (!(entry->wordswritten = cstrl(line)) && errno)
 
450
                                xerrx(1, "wordswritten is invalid: %s\n", line);
 
451
                        break;
 
452
                case 3: /* name */
 
453
                        while (*line == ' ')
 
454
                                line++;
 
455
                        t = unescstr(line);
 
456
                        if (xsnprintf(entry->name, sizeof(entry->name),
 
457
                            "%s", t))
 
458
                                xerrx(1, "name is invalid: %s\n", t);
 
459
                        free(t);
 
460
                        break;
 
461
                case 4: /* word list */
 
462
                        while (*line == ' ')
 
463
                                line++;
 
464
                        t = unescstr(line);
 
465
                        if (xsnprintf(entry->wordlist, sizeof(entry->wordlist),
 
466
                            "%s", t))
 
467
                                xerrx(1, "word list is invalid: %s\n", t);
 
468
                        free(t);
 
469
                        break;
 
470
                case 5: /* mod */
 
471
                        while (*line == ' ')
 
472
                                line++;
 
473
                        t = unescstr(line);
 
474
                        if (xsnprintf(entry->mod, sizeof(entry->mod), "%s", t))
 
475
                                xerrx(1, "game rule is invalid: %s\n", t);
 
476
                        free(t);
 
477
                        break;
 
478
                case 6: /* duration */
 
479
                        if (!(entry->duration = cstrl(line)) && errno)
 
480
                                xerrx(1, "duration is invalid: %s\n", line);
 
481
                        break;
 
482
                default:
 
483
                        xerrx(1, "too many fields in high score file");
 
484
                        /* NOTREACHED */
 
485
                        break;
 
486
                }
 
487
                line = p = q + 1;
 
488
        }
 
489
        /* sinit */
 
490
        if (!(entry->sinit = cstrl(line)) && errno)
 
491
                xerrx(1, "sinit is invalid: %s\n", line);
 
492
 
 
493
        return entry;
 
494
}
 
495
 
 
496
/*
 
497
 * getfinfo is used to gather specific file information. Possible
 
498
 * files are game rules and word lists - depending on "prefix".
 
499
 * namelist will be filled with all available options.
 
500
 *
 
501
 * Returns number of game rules/word lists found or -1 on error.
 
502
 */
 
503
static int
 
504
getfinfo(struct finfo *namelist, int n, char *prefix)
 
505
{
 
506
        DIR *dirp;
 
507
        FILE *fileinfo;
 
508
        int i, k, op;
 
509
        char *p, *path, *q;
 
510
        char buf[1024], info[61], wordpath[MAXPATHLEN];
 
511
        struct dirent *dp;
 
512
 
 
513
        if (!strcmp(prefix, "rule.")) {
 
514
                op = 0;
 
515
                path = ruledir;
 
516
        } else if (!strcmp(prefix, "words.")) {
 
517
                op = 1;
 
518
                path = worddir;
 
519
        } else
 
520
                return -1;
 
521
 
 
522
        k = 0;
 
523
 
 
524
        if (opt.net == NET) {
 
525
                switch(op) {
 
526
                case 0:
 
527
                        netsend(" RULESETS GET");
 
528
                        if (netrecv(1, 1, 1, 1, buf, sizeof(buf)) != 10)
 
529
                                return -1;
 
530
                        break;
 
531
                case 1:
 
532
                        netsend(" WORDLISTS GET");
 
533
                        if (netrecv(1, 1, 1, 1, buf, sizeof(buf)) != 10)
 
534
                                return -1;
 
535
                        break;
 
536
                default:
 
537
                        return -1;
 
538
                        /* NOTREACHED */
 
539
                        break;
 
540
                }
 
541
                p = q = buf + sizeof(" FILELIST ") - 1;
 
542
 
 
543
                while (*p >= '0' && *p <= '9')
 
544
                        p++;
 
545
                if (*p != '\0')
 
546
                        return -1;
 
547
 
 
548
                i = strtol(q, NULL, 10);
 
549
                if (n < i)
 
550
                        i = n;
 
551
 
 
552
                for (k = 0; k < i; k++) {
 
553
                        if (netrecv(1, 1, 1, 0, info, sizeof(info)) == -1)
 
554
                                return -1;
 
555
                        if ((p = strchr(info, '\n')) != NULL)
 
556
                                *p = '\0';
 
557
                        xstrncpy(namelist[k].descr, info,
 
558
                            sizeof(namelist[k].descr) - 1);
 
559
                        /* XXX */
 
560
                        xstrncpy(namelist[k].name, info,
 
561
                            sizeof(namelist[k].name) - 1);
 
562
                }
 
563
        } else {
 
564
                if ((dirp = opendir(path)) == NULL)
 
565
                        return -1;
 
566
 
 
567
                while ((dp = readdir(dirp)) != NULL && k < n) {
 
568
                        if (strncmp(dp->d_name, prefix, strlen(prefix)))
 
569
                                continue;
 
570
                        if (xsnprintf(wordpath, sizeof(wordpath), "%s/%s",
 
571
                            path, dp->d_name))
 
572
                                continue;
 
573
                        if ((fileinfo = fopen(wordpath, "r")) == NULL)
 
574
                                continue;
 
575
                        if (fgets(info, sizeof(info), fileinfo) != NULL) {
 
576
                                if ((p = strchr(info, '\n')) != NULL)
 
577
                                        *p = '\0';
 
578
                                if ((p = strchr(info, '\r')) != NULL)
 
579
                                        *p = '\0';
 
580
                                p = info;
 
581
                                if (!op) {
 
582
                                        if (info[0] != '#')
 
583
                                                goto fileclose;
 
584
                                        p++;
 
585
                                }
 
586
                                xstrncpy(namelist[k].descr, p,
 
587
                                    sizeof(namelist[k].descr) - 1);
 
588
                                xstrncpy(namelist[k].name, dp->d_name,
 
589
                                    sizeof(namelist[k].name) - 1);
 
590
                                k++;
 
591
                        }
 
592
fileclose:
 
593
                        if (fclose(fileinfo) == EOF)
 
594
                                xerr(1, "getfinfo: fclose: %s", wordpath);
 
595
                }
 
596
                (void)closedir(dirp);
 
597
        }
 
598
 
 
599
        return k;
 
600
}
 
601
 
 
602
static int
 
603
getnum(char *p)
 
604
{
 
605
        char *start;
 
606
        int i;
 
607
 
 
608
        start = p;
 
609
 
 
610
        while (*p >= '0' && *p <= '9')
 
611
                p++;
 
612
        if (*p != '\0')
 
613
                return -1;
 
614
 
 
615
        i = strtol(start, NULL, 10);
 
616
        if (i < 0)
 
617
                return -1;
 
618
        return i;
 
619
}
 
620
 
 
621
/* helper function for qsort */
 
622
int
 
623
hcompar(const void *entry1, const void *entry2)
 
624
{
 
625
        struct hentry **en1, **en2;
 
626
        unsigned long val1, val2;
 
627
        float rat1, rat2;
 
628
 
 
629
        en1 = (struct hentry **)entry1;
 
630
        en2 = (struct hentry **)entry2;
 
631
        val1 = (*en1)->count;
 
632
        val2 = (*en2)->count;
 
633
 
 
634
        if (val1 < val2)
 
635
                return 1;
 
636
        if (val1 == val2) {
 
637
                if ((*en1)->duration)
 
638
                        rat1 = (float)((*en1)->count +
 
639
                            (*en1)->wordswritten) /
 
640
                            (*en1)->duration * 100.0f;
 
641
                else
 
642
                        rat1 = 0.0f;
 
643
 
 
644
                if ((*en2)->duration)
 
645
                        rat2 = (float)((*en2)->count +
 
646
                            (*en2)->wordswritten) /
 
647
                            (*en2)->duration * 100.0f;
 
648
                else
 
649
                        rat2 = 0.0f;
 
650
 
 
651
                if (rat1 < rat2)
 
652
                        return 1;
 
653
                if (rat1 > rat2)
 
654
                        return -1;
 
655
 
 
656
                if ((*en1)->duration)
 
657
                        rat1 = (float)((*en1)->tcount +
 
658
                            (*en1)->wordswritten) /
 
659
                            (*en1)->duration * 100.0f;
 
660
                else
 
661
                        rat1 = 0.0f;
 
662
 
 
663
                if ((*en2)->duration)
 
664
                        rat2 = (float)((*en2)->tcount +
 
665
                            (*en2)->wordswritten) /
 
666
                            (*en2)->duration * 100.0f;
 
667
                else
 
668
                        rat2 = 0.0f;
 
669
 
 
670
                if (rat1 > rat2)
 
671
                        return 1;
 
672
                if (rat1 < rat2)
 
673
                        return -1;
 
674
 
 
675
                return 0;
 
676
        }
 
677
        return -1;
 
678
}
 
679
 
 
680
/*
 
681
 * Loads high score for "filename", where "filename" contains the name
 
682
 * of the word list and gives out a high score list for current rule set.
 
683
 *
 
684
 * Returns 0 on success and 1 on failure.
 
685
 */
 
686
int
 
687
loadscores(char *filename)
 
688
{
 
689
        FILE *highfile;
 
690
        int exitnow, fd, i, lvl, m, n, rank;
 
691
        float ratio, cps;
 
692
        char *p;
 
693
        char buf[1024], line[2 * FILENAME_MAX + 100];
 
694
        struct hentry *entry;
 
695
        struct hentry **entries, **new;
 
696
 
 
697
        entries = NULL;
 
698
 
 
699
        if (opt.net == NET) {
 
700
                if (xsnprintf(buf, sizeof(buf), " SCORES %s", filename))
 
701
                        return 1;
 
702
                netsend(buf);
 
703
 
 
704
                do {
 
705
                        i = netrecv(1, 1, 1, 1, buf, sizeof(buf) - 2);
 
706
                        switch (i) {
 
707
                        case 2:
 
708
                        case 11:
 
709
                                break;
 
710
                        default:
 
711
                                return 1;
 
712
                                /* NOTREACHED */
 
713
                                break;
 
714
                        }
 
715
                } while (i != 11);
 
716
 
 
717
                if ((i = getnum(buf + sizeof(" SCORES ") - 1)) < 0)
 
718
                        return 1;
 
719
 
 
720
                n = 0;
 
721
                for (; i > 0; i--) {
 
722
                        if (netrecv(1, 1, 1, 0, line, sizeof(line)) == -1)
 
723
                                return 1;
 
724
 
 
725
/* XXX only top10 */
 
726
                        if ((p = strchr(line, '\n')) != NULL)
 
727
                                *p = '\0';
 
728
                        entry = getentry(line);
 
729
                        if (!strcmp(entry->wordlist, filename) &&
 
730
                            !strcmp(entry->mod,
 
731
                            rules.fname)) {
 
732
                                if ((new = realloc(entries, (n + 1) *
 
733
                                sizeof(struct hentry *))) == NULL)
 
734
                                        xerr(1, NULL);
 
735
                                entries = new;
 
736
                                entries[n++] = entry;
 
737
                        }
 
738
                        else
 
739
                                free(entry);
 
740
                }
 
741
        } else {
 
742
#ifndef WIN32
 
743
                (void)flock(hfd, LOCK_EX);
 
744
#endif /* WIN32 */
 
745
 
 
746
                if ((fd = dup(hfd)) == -1)
 
747
                        return 1;
 
748
                if ((highfile = fdopen(fd, "r+")) == NULL)
 
749
                        return 1;
 
750
                rewind(highfile);
 
751
 
 
752
                n = 0;
 
753
                while (fgets(line, sizeof(line), highfile) != NULL) {
 
754
                        if ((p = strchr(line, '\n')) == NULL) {
 
755
                                if (fclose(highfile) == EOF)
 
756
                                        xerr(1, "loadscores: fclose");
 
757
#ifndef WIN32
 
758
                                (void)flock(hfd, LOCK_UN);
 
759
#endif /* WIN32 */
 
760
                                return 1;
 
761
                        }
 
762
                        *p = '\0';
 
763
                        if ((p = strchr(line, '\r')) != NULL)
 
764
                                *p = '\0';
 
765
                        entry = getentry(line);
 
766
                        if (!strcmp(entry->wordlist, filename) &&
 
767
                            !strcmp(entry->mod, rules.fname)) {
 
768
                                if ((new = realloc(entries, (n + 1) *
 
769
                                sizeof(struct hentry *))) == NULL)
 
770
                                        xerr(1, NULL);
 
771
                                entries = new;
 
772
                                entries[n++] = entry;
 
773
                        }
 
774
                        else
 
775
                                free(entry);
 
776
                }
 
777
 
 
778
                if (fclose(highfile) == EOF)
 
779
                        xerr(1, "loadscores: fclose");
 
780
 
 
781
#ifndef WIN32
 
782
                (void)flock(hfd, LOCK_UN);
 
783
#endif /* WIN32 */
 
784
        }
 
785
 
 
786
        qsort(entries, n, sizeof(struct hentry *), hcompar);
 
787
 
 
788
        if ((new = realloc(entries, (n + 1) * sizeof(struct hentry *)))
 
789
            == NULL)
 
790
                xerr(1, NULL);
 
791
        entries = new;
 
792
        entries[n] = NULL;
 
793
 
 
794
        drawscreen();
 
795
        xcolor_set(4);
 
796
 
 
797
        if (entries[0] == NULL) {
 
798
                mvaddstr(4, 3, _("No High Scores"));
 
799
                pressanykey(19, 3);
 
800
                free(entries);
 
801
                return 0;
 
802
        }
 
803
 
 
804
        m = 0;
 
805
        exitnow = 0;
 
806
        do {
 
807
                drawscreen();
 
808
                xcolor_set(4);
 
809
                mvaddstr(0, 35, _("High Score List"));
 
810
                mvaddstr(2, 19, _("Use cursor keys to scroll or ESC to leave"));
 
811
                mvaddstr(4, 0, _(
 
812
"Rank   (score) (name)                   (level)   (cps)  (wpm)  (typoinfos)"
 
813
                ));
 
814
 
 
815
                for (i = 0; entries[i + m] != NULL && i < 17; i++) {
 
816
                        entry = entries[i + m];
 
817
                        /* XXX typespeed.c */
 
818
                        if (!entry->tcount)
 
819
                                ratio = 0;
 
820
                        else
 
821
                                ratio = (1 - (float)(entry->count +
 
822
                                    entry->wordswritten) / (entry->tcount +
 
823
                                    entry->wordswritten)) * 100;
 
824
 
 
825
                        /*
 
826
                         * These set colors on their own, so put them
 
827
                         * in front to safe expensive color_set call.
 
828
                         */
 
829
                        rank = typorankkaus(ratio);
 
830
                        if (!entries[i]->count)
 
831
                                rank = 0;
 
832
                        mvaddstr(i + 5, 71, typorank[rank]);
 
833
 
 
834
                        lvl = level(entry->count);
 
835
                        if (lvl >= 0 && lvl < 11)
 
836
                                mvaddstr(i + 5, 40, rankki[lvl]);
 
837
 
 
838
                        xcolor_set(5);
 
839
                        mvprintw(i + 5, 2, "%2d.", i + m + 1);
 
840
                        mvprintw(i + 5, 7, "%6lu", entry->count);
 
841
                        mvaddstr(i + 5, 15, entry->name);
 
842
 
 
843
                        if (entry->duration) {
 
844
                                cps = (float)(entry->count +
 
845
                                    entry->wordswritten) /
 
846
                                    entry->duration * 100.0f;
 
847
 
 
848
                                mvprintw(i + 5, 50, "%2.3f", cps);
 
849
                                mvprintw(i + 5, 58, "%3lu", (uint32_t)(12.0f *
 
850
                                    cps));
 
851
                        } else {
 
852
                                mvprintw(i + 5, 50, "%2.3f", 0.0f);
 
853
                                mvprintw(i + 5, 58, "%3lu", 0);
 
854
                        }
 
855
 
 
856
                        mvprintw(i + 5, 64, "%2.2f%%", ratio);
 
857
 
 
858
                        move(23, 2);
 
859
                }
 
860
 
 
861
                switch(getch()) {
 
862
                case KEY_DOWN:
 
863
                case 'j':
 
864
                        if (m + 17 < n)
 
865
                                m += 17;
 
866
                        break;
 
867
                case KEY_UP:
 
868
                case 'k':
 
869
                        m -= 17;
 
870
                        if (m < 0)
 
871
                                m = 0;
 
872
                        break;
 
873
                case 27:
 
874
                case ' ':
 
875
                case '\n':
 
876
                        exitnow = 1;
 
877
                        break;
 
878
                default:
 
879
                        break;
 
880
                }
 
881
        } while (!exitnow);
 
882
 
 
883
        for (i = 0; entries[i] != NULL; i++)
 
884
                free(entries[i]);
 
885
        free(entries);
 
886
 
 
887
        return 0;
 
888
}
 
889
 
 
890
/*
 
891
 * Reads in words out of file "filename" into word. If less than 22
 
892
 * words are parsed, all words are freed and 1 is returned.
 
893
 * Words cannot be longer than 19 chars. No word duplicates allowed.
 
894
 *
 
895
 * First line of file "filename" will be ignored (description).
 
896
 *
 
897
 * Returns ...
 
898
 * 0 on success.
 
899
 * 1 on xsnprintf failure (file path too long).
 
900
 * 2 on failure during file opening (errno set by fopen).
 
901
 * 3 if not enough words in wordfile.
 
902
 */
 
903
#ifndef TEST
 
904
static
 
905
#endif /* TEST */
 
906
int
 
907
loadwords(char *filename)
 
908
{
 
909
        int fd, ignore, k, l;
 
910
        char *curpos, *newpos, *p;
 
911
        char **pointer;
 
912
        char buf[60], wordpath[MAXPATHLEN];
 
913
        size_t i, j;
 
914
        struct stat sb;
 
915
 
 
916
        freewords();
 
917
 
 
918
        if (opt.net == NET) {
 
919
                if (xsnprintf(buf, sizeof(buf), " WORDLIST %s", filename))
 
920
                        return 1;
 
921
                netsend(buf);
 
922
                if (netrecv(1, 1, 1, 1, buf, sizeof(buf)) != 8)
 
923
                        return 3;
 
924
 
 
925
                if ((k = getnum(buf + sizeof(" WORDLIST ") - 1)) < 0)
 
926
                        return 3;
 
927
 
 
928
                words.bulk = xmalloc(j = 1024);
 
929
 
 
930
                for (i = 0; k > 0; k--) {
 
931
                        if (netrecv(1, 1, 1, 0, buf, sizeof(buf)) == -1) {
 
932
                                freewords();
 
933
                                return 3;
 
934
                        }
 
935
                        buf[sizeof(buf) - 1] = '\0';
 
936
 
 
937
                        if (i + strlen(buf) + 2 >= j) {
 
938
                                j += 1024;
 
939
                                if ((p = realloc(words.bulk, j)) == NULL) {
 
940
                                        freewords();
 
941
                                        return 3;
 
942
                                }
 
943
                                words.bulk = p;
 
944
                        }
 
945
                        l = snprintf(words.bulk + i, j - i - 1, "%s\n", buf);
 
946
                        if (l < 0) {
 
947
                                freewords();
 
948
                                return 3;
 
949
                        }
 
950
                        i += l;
 
951
                }
 
952
        } else {
 
953
                if (xsnprintf(wordpath, sizeof(wordpath), "%s/%s", worddir,
 
954
                    filename))
 
955
                        return 1;
 
956
 
 
957
                if ((fd = open(wordpath, O_RDONLY, 0)) == -1)
 
958
                        return 2;
 
959
 
 
960
                if (fstat(fd, &sb) == -1)
 
961
                        return 2;
 
962
 
 
963
                if (sb.st_size >= SSIZE_MAX)
 
964
                        return 3;
 
965
 
 
966
                words.bulk = xmalloc(sb.st_size + 1);
 
967
 
 
968
                if (read(fd, words.bulk, sb.st_size) != sb.st_size) {
 
969
                        freewords();
 
970
                        return 2;
 
971
                }
 
972
                words.bulk[sb.st_size] = '\0';
 
973
 
 
974
                if (close(fd) == -1)
 
975
                        xerr(1, "loadwords: fclose: %s", wordpath);
 
976
        }
 
977
 
 
978
        words.word = xmalloc(sizeof(char *) * 1024);
 
979
        words.max = 1024;
 
980
        words.n = 0;
 
981
 
 
982
        curpos = words.bulk;
 
983
 
 
984
        /* ignore title of word list */
 
985
        if ((curpos = strchr(curpos, '\n')) == NULL) {
 
986
                freewords();
 
987
                return 3;
 
988
        }
 
989
 
 
990
        while ((p = strchr(curpos, '\n')) != NULL) {
 
991
                *p = '\0';
 
992
                newpos = p + 1;
 
993
 
 
994
                if ((p = strchr(curpos, '\r')) != NULL)
 
995
                        *p = '\0';
 
996
 
 
997
                if (curpos[0] == '\0' || strlen(curpos) > 19) {
 
998
                        curpos = newpos;
 
999
                        continue;
 
1000
                }
 
1001
 
 
1002
                for (ignore = 0, p = curpos; *p != '\0'; p++)
 
1003
                        if (!isprint(*p) || isspace(*p)) {
 
1004
                                ignore = 1;
 
1005
                                break;
 
1006
                        }
 
1007
                if (!ignore)
 
1008
                        words.word[words.n++] = curpos;
 
1009
 
 
1010
                if (words.n >= words.max) {
 
1011
                        if ((size_t)-1 - 1024 < words.max)
 
1012
                                xerrx(1, "loadwords: too many words!");
 
1013
                        pointer = realloc(words.word, words.max + 1024);
 
1014
                        if (pointer == NULL)
 
1015
                                xerr(1, NULL);
 
1016
                        words.word = pointer;
 
1017
                        words.max += 1024;
 
1018
                }
 
1019
 
 
1020
                curpos = newpos;
 
1021
        }
 
1022
 
 
1023
        for (i = 0; i < words.n; i++)
 
1024
                for (j = i + 1; j < words.n; j++)
 
1025
                        if (!strcmp(words.word[i], words.word[j]))
 
1026
                                words.word[j] = words.word[--words.n];
 
1027
 
 
1028
        if (words.n < 22) {
 
1029
                /* not enough words */
 
1030
                freewords();
 
1031
                return 3;
 
1032
        }
 
1033
        return 0;
 
1034
}
 
1035
 
 
1036
/*
 
1037
 * Used to fill in option and value out of line. "line" should be a line
 
1038
 * out of configuration file or rule set file.
 
1039
 *
 
1040
 * IMPORTANT: "line" _will_ be changed, as option and value only point
 
1041
 * at the correct position of line.
 
1042
 *
 
1043
 * Returns 0 on success or 1 on failure.
 
1044
 */
 
1045
static int
 
1046
parseline(char *line, char **option, char **value)
 
1047
{
 
1048
        char *p, *v;
 
1049
 
 
1050
        /* By default, no option and no value will be returned. */
 
1051
        *option = *value = NULL;
 
1052
 
 
1053
        /*
 
1054
         * First off, the line will be "normalized", i.e. all
 
1055
         * unneeded characters (white spaces in front of option,
 
1056
         * \n and possible \r and comments [beginning with #]).
 
1057
         * If there is no \n, the line was not fully red, so
 
1058
         * let's drop it completely.
 
1059
         */
 
1060
        if ((p = strchr(line, '\n')) == NULL)
 
1061
                return 1;
 
1062
        *p = '\0';
 
1063
        if ((p = strchr(line, '\r')) != NULL)
 
1064
                *p = '\0';
 
1065
        if ((p = strchr(line, '#')) != NULL)
 
1066
                *p = '\0';
 
1067
        while (*line != '\0' && isblank(*line))
 
1068
                line++;
 
1069
 
 
1070
        /* Anything left? */
 
1071
        if (line[0] == '\0')
 
1072
                return 0;
 
1073
 
 
1074
        /*
 
1075
         * Locate the first "=" and use p for the oPtion
 
1076
         * and v for the Value.
 
1077
         */
 
1078
        if ((v = strchr(line, '=')) == NULL || v == line)
 
1079
                return 1;
 
1080
 
 
1081
        *v = '\0';
 
1082
        p = v - 1;
 
1083
        v++;
 
1084
 
 
1085
        /* Remove all trailing whitespaces between option and equal sign. */
 
1086
        while (p != line && isblank(*p))
 
1087
                *(p--) = '\0';
 
1088
 
 
1089
        /* Remove all whitespaces between equal sign and value. */
 
1090
        while (*v != '\0' && isblank(*v))
 
1091
                v++;
 
1092
 
 
1093
        /* Remove all trailing whitespaces. */
 
1094
        if ((p = strchr(v, '\0')) == NULL || p == v)
 
1095
                return 1;
 
1096
        p--;
 
1097
        while (p != v && isblank(*p))
 
1098
                *(p--) = '\0';
 
1099
 
 
1100
        *option = line;
 
1101
        *value = v;
 
1102
 
 
1103
        return 0;
 
1104
}
 
1105
 
 
1106
/*
 
1107
 * Opens system wide and user specific configuration file. If there is
 
1108
 * a user configuration file, a user specific high score file will be
 
1109
 * opened, too. This way, the system wide high score won't be tampered with.
 
1110
 */
 
1111
void
 
1112
readconfig(void)
 
1113
{
 
1114
        char *envhome;
 
1115
        char userhigh[MAXPATHLEN], userconf[MAXPATHLEN];
 
1116
        struct stat sb;
 
1117
 
 
1118
        if (xsnprintf(ruledir, sizeof(ruledir), "%s", RULEDIR)) {
 
1119
                ruledir[0] = '.';
 
1120
                ruledir[1] = '\0';
 
1121
        }
 
1122
        if (xsnprintf(worddir, sizeof(worddir), "%s", WORDDIR)) {
 
1123
                worddir[0] = '.';
 
1124
                worddir[1] = '\0';
 
1125
        }
 
1126
 
 
1127
        readfile(CONFIGFILE, 1);
 
1128
 
 
1129
        if ((envhome = getenv("HOME")) == NULL)
 
1130
                return;
 
1131
 
 
1132
        if (xsnprintf(userconf, sizeof(userconf), "%s/.typespeed/config",
 
1133
            envhome))
 
1134
                return;
 
1135
 
 
1136
        if (stat(userconf, &sb) || (sb.st_mode & S_IFMT) != S_IFREG)
 
1137
                return;
 
1138
 
 
1139
        if (xsnprintf(userhigh, sizeof(userhigh), "%s/.typespeed/score",
 
1140
            envhome))
 
1141
                return;
 
1142
 
 
1143
        /*
 
1144
         * Open a user writable high score.
 
1145
         * This can be a symbolic link to system-wide high score
 
1146
         * file. Protect system-wide high score file with group
 
1147
         * write permissions: privileged gid already dropped.
 
1148
         */
 
1149
        if (close(hfd) == -1)
 
1150
                xerr(1, "readconfig: close");
 
1151
        if ((hfd = open(userhigh, O_RDWR, 0)) == -1)
 
1152
                xerr(1, "readconfig: open: %s", userhigh);
 
1153
 
 
1154
        readfile(userconf, 1);
 
1155
}
 
1156
 
 
1157
/*
 
1158
 * Function used to open configuration and game rule files and to
 
1159
 * set options with function setoptions.
 
1160
 */
 
1161
static void
 
1162
readfile(char *filename, int op)
 
1163
{
 
1164
        int i;
 
1165
        FILE *file;
 
1166
        unsigned long lineno;
 
1167
        char *option, *value;
 
1168
        char buf[1024], line[sizeof("worddir = ") + MAXPATHLEN + 2];
 
1169
 
 
1170
        lineno = 0;
 
1171
 
 
1172
        if (opt.net == NET && !op) {
 
1173
                if (xsnprintf(buf, sizeof(buf), " RULESET %s", filename))
 
1174
                        return;
 
1175
                netsend(buf);
 
1176
                if (netrecv(1, 1, 1, 1, buf, sizeof(buf)) != 6)
 
1177
                        return;
 
1178
 
 
1179
                if ((i = getnum(buf + sizeof(" RULESET ") - 1)) < 0)
 
1180
                        return;
 
1181
 
 
1182
                for (; i >= 0; i--) {
 
1183
                        lineno++;
 
1184
                        if (netrecv(1, 1, 1, 0, line, sizeof(line)) == -1)
 
1185
                                return;
 
1186
                        line[sizeof(line) - 3] = '\0';
 
1187
                        strncat(line, "\n", 1);
 
1188
                        if (parseline(line, &option, &value))
 
1189
                                xerrx(1, "Error in %s line %lu\n", filename,
 
1190
                                    lineno);
 
1191
                        if (option != NULL && value != NULL)
 
1192
                                setoptions(option, value, op);
 
1193
                }
 
1194
        } else {
 
1195
                if ((file = fopen(filename, "r")) == NULL)
 
1196
                        return;
 
1197
 
 
1198
                while (fgets(line, sizeof(line), file) != NULL) {
 
1199
                        lineno++;
 
1200
                        if (parseline(line, &option, &value))
 
1201
                                xerrx(1, "Error in %s line %lu\n", filename,
 
1202
                                    lineno);
 
1203
                        if (option != NULL && value != NULL)
 
1204
                                setoptions(option, value, op);
 
1205
                }
 
1206
 
 
1207
                if (fclose(file) == EOF)
 
1208
                        xerr(1, "readfile: fclose: %s", filename);
 
1209
        }
 
1210
}
 
1211
 
 
1212
/*
 
1213
 * setoptions sets actually configuration and rule sets. If a
 
1214
 * configuration or rule set has been opened decides "op":
 
1215
 * 0: game rule
 
1216
 * 1: configuration file
 
1217
 */
 
1218
static void
 
1219
setoptions(char *option, char *value, int op)
 
1220
{
 
1221
        unsigned long val;
 
1222
 
 
1223
        if (op) { /* configuration file */
 
1224
                if (!strcmp(option, "cheat")) {
 
1225
                        if (!strcmp(value, "yes"))
 
1226
                                opt.cheat = 1;
 
1227
                        else
 
1228
                                opt.cheat = 0;
 
1229
                }
 
1230
                else if (!strcmp(option, "ruledir"))
 
1231
                        xstrncpy(ruledir, value, sizeof(ruledir) - 1);
 
1232
                else if (!strcmp(option, "worddir"))
 
1233
                        xstrncpy(worddir, value, sizeof(worddir) - 1);
 
1234
                else if (!strcmp(option, "rule"))
 
1235
                        xstrncpy(rules.fname, value, sizeof(rules.fname) - 1);
 
1236
        }
 
1237
        else { /* game rule */
 
1238
                if (!strcmp(option, "misses")) {
 
1239
                        if (!(val = cstrl(value)) && errno)
 
1240
                                xerrx(1, "misses invalid: %s\n", value);
 
1241
                        if (val > 0 && val < 100)
 
1242
                                rules.misses = val;
 
1243
                }
 
1244
                else if (!strcmp(option, "min word length")) {
 
1245
                        if (!(val = cstrl(value)) && errno)
 
1246
                                xerrx(1, "min word length invalid: %s\n",
 
1247
                                    value);
 
1248
                        if (val > 0 && val < 20)
 
1249
                                rules.minlen = val;
 
1250
                }
 
1251
                else if (!strcmp(option, "max word length")) {
 
1252
                        if (!(val = cstrl(value)) && errno)
 
1253
                                xerrx(1, "max word length invalid: %s\n",
 
1254
                                    value);
 
1255
                        if (val > 0 && val < 20)
 
1256
                                rules.maxlen = val;
 
1257
                }
 
1258
                else if (!strcmp(option, "min words")) {
 
1259
                        if (!(val = cstrl(value)) && errno)
 
1260
                                xerrx(1, "min words invalid: %s\n", value);
 
1261
                        if (val > 0 && val < 23)
 
1262
                                rules.minwords = val;
 
1263
                }
 
1264
                else if (!strcmp(option, "max words")) {
 
1265
                        if (!(val = cstrl(value)) && errno)
 
1266
                                xerrx(1, "max words invalid: %s\n", value);
 
1267
                        if (val > 0 && val < 23)
 
1268
                                rules.maxwords = val;
 
1269
                }
 
1270
                else if (!strcmp(option, "highscore")) {
 
1271
                        if (!strcmp(value, "yes"))
 
1272
                                rules.hightype = 1;
 
1273
                        else
 
1274
                                rules.hightype = 0;
 
1275
                }
 
1276
                else if (!strcmp(option, "min score")) {
 
1277
                        if (!(val = cstrl(value)) && errno)
 
1278
                                xerrx(1, "min score invalid: %s\n", value);
 
1279
                        rules.minscore = val;
 
1280
                }
 
1281
                else if (!strcmp(option, "min speed")) {
 
1282
                        if (!(val = cstrl(value)) && errno)
 
1283
                                xerrx(1, "min speed invalid: %s\n", value);
 
1284
                        if (val < 100)
 
1285
                                rules.minspeed = val;
 
1286
                }
 
1287
                else if (!strcmp(option, "max speed")) {
 
1288
                        if (!(val = cstrl(value)) && errno)
 
1289
                                xerrx(1, "max speed invalid: %s\n", value);
 
1290
                        if (val < 100)
 
1291
                                rules.maxspeed = val;
 
1292
                }
 
1293
                else if (!strcmp(option, "name"))
 
1294
                        xstrncpy(rules.name, value, sizeof(rules.name) - 1);
 
1295
                else if (!strcmp(option, "step")) {
 
1296
                        if (!(val = cstrl(value)) && errno)
 
1297
                                xerrx(1, "step invalid: %s\n", value);
 
1298
                        if (val < 1000)
 
1299
                                rules.step = val;
 
1300
                }
 
1301
                else if (!strcmp(option, "smooth")) {
 
1302
                        if (!strcmp(value, "yes"))
 
1303
                                rules.smooth = 1;
 
1304
                        else
 
1305
                                rules.smooth = 0;
 
1306
                }
 
1307
        }
 
1308
}
 
1309
 
 
1310
void
 
1311
setruleset(char *path)
 
1312
{
 
1313
        int i, k;
 
1314
        struct finfo namelist[WORDFILE_MAX];
 
1315
        char fullpath[MAXPATHLEN];
 
1316
 
 
1317
        k = getfinfo(&(namelist[1]), WORDFILE_MAX - 1, "rule.");
 
1318
        if (k++ < 1)
 
1319
                xerrx(1, "setruleset: no rule sets found");
 
1320
        for (i = 1; i < k; i++)
 
1321
                if (!strcmp(path, namelist[i].name))
 
1322
                        break;
 
1323
        if (i == k)
 
1324
                xerrx(1, "setruleset: supplied default rule set not found");
 
1325
        if (xsnprintf(fullpath, sizeof(fullpath), "%s/%s", ruledir,
 
1326
            namelist[i].name))
 
1327
                xerrx(1, "setruleset: supplied default rule set name too long");
 
1328
        readfile(fullpath, 0);
 
1329
        xstrncpy(rules.fname, namelist[i].name, sizeof(rules.fname) - 1);
 
1330
        xstrncpy(rules.name, namelist[i].descr, sizeof(rules.name) - 1);
 
1331
}
 
1332
 
 
1333
/*
 
1334
 * Removes escaped tabs and returns converted string.
 
1335
 * This string must be passed to free later on!
 
1336
 */
 
1337
#ifndef TEST
 
1338
static
 
1339
#endif /* TEST */
 
1340
char *
 
1341
unescstr(char *string)
 
1342
{
 
1343
        int pos;
 
1344
        char *new, *p, *retval;
 
1345
 
 
1346
        if (string == NULL)
 
1347
                return NULL;
 
1348
 
 
1349
        new = xmalloc(strlen(string) + 1);
 
1350
 
 
1351
        for (pos = 0, p = string; *p != '\0'; p++, pos++) {
 
1352
                if (*p == '\\') {
 
1353
                        if (*(p + 1) == '\0')
 
1354
                                xerrx(1, "error in unescstr");
 
1355
                        else
 
1356
                                p++;
 
1357
                }
 
1358
                new[pos] = *p;
 
1359
        }
 
1360
        new[pos] = '\0';
 
1361
 
 
1362
        retval = xstrdup(new);
 
1363
        free(new);
 
1364
 
 
1365
        return retval;
 
1366
}
 
1367