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>
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.
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.
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.
23
* file.c - file access functions
28
#endif /* HAVE_CONFIG_H */
30
#ifdef HAVE_SYS_FILE_H
32
#endif /* HAVE_SYS_FILE_H */
34
#ifdef HAVE_SYS_PARAM_H
35
#include <sys/param.h>
36
#endif /* HAVE_SYS_PARAM_H */
38
#ifdef HAVE_SYS_STAT_H
40
#endif /* HAVE_SYS_STAT_H */
42
#ifdef HAVE_SYS_TYPES_H
43
#include <sys/types.h>
44
#endif /* HAVE_SYS_TYPES_H */
46
#ifdef HAVE_BITS_POSIX1_LIM_H
47
#include <bits/posix1_lim.h>
48
#endif /* HAVE_BITS_POSIX1_LIM_H */
55
#endif /* HAVE_DIRENT_H */
61
#endif /* HAVE_FCNTL_H */
68
#endif /* HAVE_STDLIB_H */
72
#endif /* HAVE_STRING_H */
76
#endif /* HAVE_UNISTD_H */
79
#include "pathnames.h"
80
#include "typespeed.h"
82
#define _(string) gettext(string)
83
#define WORDFILE_MAX 100
88
uint32_t wordswritten;
90
char mod[FILENAME_MAX * 2 + 1];
91
char wordlist[FILENAME_MAX * 2 + 1];
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);
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);
132
char *escstr(char *);
133
int loadwords(char *);
134
char *unescstr(char *);
136
static char *escstr(char *);
137
static int loadwords(char *);
138
static char *unescstr(char *);
142
extern char *rankki[11];
143
extern char *typorank[12];
144
extern char *usedwordfile;
146
char ruledir[MAXPATHLEN];
147
char worddir[MAXPATHLEN];
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.
154
* Returns 0 on success and 1 on failure.
157
addscore(char *wordlist, struct stats *stat)
164
/* Ask player until a name has been entered. */
167
mvaddstr(3, 2, _("Enter your name:"));
168
liima_mvgetnstr(3, 3 + strlen(_("Enter your name:")), name,
170
name[sizeof(name) - 1] = '\0';
171
} while (name[0] == '\0' || name[0] == ' ');
174
netsendscore(stat, name);
176
rname = escstr(rules.fname);
177
uname = escstr(name);
180
(void)flock(hfd, LOCK_EX);
183
if ((fd = dup(hfd)) == -1)
186
if ((highf = fdopen(fd, "r+")) == NULL)
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);
194
if (fclose(highf) == EOF)
198
(void)flock(hfd, LOCK_UN);
209
* This function asks player about rule set of choice. All rule set
210
* variables will be set.
214
* 1 if no rule set available
215
* -1 if path to rule set is too long
221
char fullpath[MAXPATHLEN];
222
struct finfo namelist[WORDFILE_MAX];
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);
231
k = getfinfo(&(namelist[1]), WORDFILE_MAX - 1, "rule.");
233
mvaddstr(1, 5, _("No rule sets found..."));
238
qsort((void *)(namelist + 1), k - 1, sizeof(namelist[0]), compar);
240
if (!(k = fileselmenu(k, namelist, _("rule set")))) {
247
if (opt.net == NET) {
248
if (xsnprintf(fullpath, sizeof(fullpath), "%s",
252
if (xsnprintf(fullpath, sizeof(fullpath), "%s/%s", ruledir,
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);
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
270
* 0: loads words out of word list
271
* 1: prints high scores for word list (and current rule set)
273
* Returns 0 on success or 1 on failure.
276
choosewordfile(int operation)
280
struct finfo namelist[WORDFILE_MAX];
282
k = getfinfo(namelist, WORDFILE_MAX, "words.");
285
mvaddstr(1, 5, _("No word lists found..."));
291
qsort((void *)namelist, k, sizeof(namelist[0]), compar);
293
if ((k = fileselmenu(k, namelist, _("word list"))) == -1)
298
a = loadwords(namelist[k].name);
301
loadscores(namelist[k].name);
311
case 0: /* success */
312
if (usedwordfile != NULL)
314
usedwordfile = xstrdup(namelist[k].name);
318
case 1: /* xsnprintf */
319
errstr = _("Path to file is too long.");
321
case 2: /* fopen - errno */
322
errstr = strerror(errno);
324
case 3: /* loadwords - not enough words */
325
errstr = _("Not enough words in word list for current settings.");
327
case 5: /* resetscorefile - error during write */
328
errstr = _("Cannot write to high score file.");
331
errstr = _("Something went wrong. Contact your mother.");
336
mvaddstr(3, 5, errstr);
342
/* helper function for qsort */
344
compar(const void *p1, const void *p2)
348
struct finfo *elem1, *elem2;
350
elem1 = (struct finfo *)p1;
351
elem2 = (struct finfo *)p2;
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);
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.
364
* Returns converted string that must be passed to free() later on.
373
char *new, *p, *retval;
378
if ((new = calloc(2, strlen(string) + 1)) == NULL)
379
xerr(1, "escstr: calloc");
381
for (pos = 0, p = string; *p != '\0'; p++) {
382
if (*p == '\\' || *p == '\t')
388
retval = xstrdup(new);
394
/* Frees all allocated memory in word. */
398
if (words.bulk != NULL) {
402
if (words.word != NULL) {
406
words.n = words.max = 0;
410
* This function converts string "line" into struct hentry. The converted
411
* struct will be returned and must be passed to free() later on.
413
* Returns converted struct.
415
static struct hentry *
420
struct hentry *entry;
422
entry = xmalloc(sizeof(struct hentry));
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");
430
for (i = 1; (q - i) >= p && *(q - i) == '\\'; i++)
441
if (!(entry->count = cstrl(line)) && errno)
442
xerrx(1, "count is invalid: %s\n", line);
445
if (!(entry->tcount = cstrl(line)) && errno)
446
xerrx(1, "tcount is invalid: %s\n", line);
448
case 2: /* wordswritten */
449
if (!(entry->wordswritten = cstrl(line)) && errno)
450
xerrx(1, "wordswritten is invalid: %s\n", line);
456
if (xsnprintf(entry->name, sizeof(entry->name),
458
xerrx(1, "name is invalid: %s\n", t);
461
case 4: /* word list */
465
if (xsnprintf(entry->wordlist, sizeof(entry->wordlist),
467
xerrx(1, "word list is invalid: %s\n", t);
474
if (xsnprintf(entry->mod, sizeof(entry->mod), "%s", t))
475
xerrx(1, "game rule is invalid: %s\n", t);
478
case 6: /* duration */
479
if (!(entry->duration = cstrl(line)) && errno)
480
xerrx(1, "duration is invalid: %s\n", line);
483
xerrx(1, "too many fields in high score file");
490
if (!(entry->sinit = cstrl(line)) && errno)
491
xerrx(1, "sinit is invalid: %s\n", line);
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.
501
* Returns number of game rules/word lists found or -1 on error.
504
getfinfo(struct finfo *namelist, int n, char *prefix)
510
char buf[1024], info[61], wordpath[MAXPATHLEN];
513
if (!strcmp(prefix, "rule.")) {
516
} else if (!strcmp(prefix, "words.")) {
524
if (opt.net == NET) {
527
netsend(" RULESETS GET");
528
if (netrecv(1, 1, 1, 1, buf, sizeof(buf)) != 10)
532
netsend(" WORDLISTS GET");
533
if (netrecv(1, 1, 1, 1, buf, sizeof(buf)) != 10)
541
p = q = buf + sizeof(" FILELIST ") - 1;
543
while (*p >= '0' && *p <= '9')
548
i = strtol(q, NULL, 10);
552
for (k = 0; k < i; k++) {
553
if (netrecv(1, 1, 1, 0, info, sizeof(info)) == -1)
555
if ((p = strchr(info, '\n')) != NULL)
557
xstrncpy(namelist[k].descr, info,
558
sizeof(namelist[k].descr) - 1);
560
xstrncpy(namelist[k].name, info,
561
sizeof(namelist[k].name) - 1);
564
if ((dirp = opendir(path)) == NULL)
567
while ((dp = readdir(dirp)) != NULL && k < n) {
568
if (strncmp(dp->d_name, prefix, strlen(prefix)))
570
if (xsnprintf(wordpath, sizeof(wordpath), "%s/%s",
573
if ((fileinfo = fopen(wordpath, "r")) == NULL)
575
if (fgets(info, sizeof(info), fileinfo) != NULL) {
576
if ((p = strchr(info, '\n')) != NULL)
578
if ((p = strchr(info, '\r')) != NULL)
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);
593
if (fclose(fileinfo) == EOF)
594
xerr(1, "getfinfo: fclose: %s", wordpath);
596
(void)closedir(dirp);
610
while (*p >= '0' && *p <= '9')
615
i = strtol(start, NULL, 10);
621
/* helper function for qsort */
623
hcompar(const void *entry1, const void *entry2)
625
struct hentry **en1, **en2;
626
unsigned long val1, val2;
629
en1 = (struct hentry **)entry1;
630
en2 = (struct hentry **)entry2;
631
val1 = (*en1)->count;
632
val2 = (*en2)->count;
637
if ((*en1)->duration)
638
rat1 = (float)((*en1)->count +
639
(*en1)->wordswritten) /
640
(*en1)->duration * 100.0f;
644
if ((*en2)->duration)
645
rat2 = (float)((*en2)->count +
646
(*en2)->wordswritten) /
647
(*en2)->duration * 100.0f;
656
if ((*en1)->duration)
657
rat1 = (float)((*en1)->tcount +
658
(*en1)->wordswritten) /
659
(*en1)->duration * 100.0f;
663
if ((*en2)->duration)
664
rat2 = (float)((*en2)->tcount +
665
(*en2)->wordswritten) /
666
(*en2)->duration * 100.0f;
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.
684
* Returns 0 on success and 1 on failure.
687
loadscores(char *filename)
690
int exitnow, fd, i, lvl, m, n, rank;
693
char buf[1024], line[2 * FILENAME_MAX + 100];
694
struct hentry *entry;
695
struct hentry **entries, **new;
699
if (opt.net == NET) {
700
if (xsnprintf(buf, sizeof(buf), " SCORES %s", filename))
705
i = netrecv(1, 1, 1, 1, buf, sizeof(buf) - 2);
717
if ((i = getnum(buf + sizeof(" SCORES ") - 1)) < 0)
722
if (netrecv(1, 1, 1, 0, line, sizeof(line)) == -1)
726
if ((p = strchr(line, '\n')) != NULL)
728
entry = getentry(line);
729
if (!strcmp(entry->wordlist, filename) &&
732
if ((new = realloc(entries, (n + 1) *
733
sizeof(struct hentry *))) == NULL)
736
entries[n++] = entry;
743
(void)flock(hfd, LOCK_EX);
746
if ((fd = dup(hfd)) == -1)
748
if ((highfile = fdopen(fd, "r+")) == NULL)
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");
758
(void)flock(hfd, LOCK_UN);
763
if ((p = strchr(line, '\r')) != NULL)
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)
772
entries[n++] = entry;
778
if (fclose(highfile) == EOF)
779
xerr(1, "loadscores: fclose");
782
(void)flock(hfd, LOCK_UN);
786
qsort(entries, n, sizeof(struct hentry *), hcompar);
788
if ((new = realloc(entries, (n + 1) * sizeof(struct hentry *)))
797
if (entries[0] == NULL) {
798
mvaddstr(4, 3, _("No High Scores"));
809
mvaddstr(0, 35, _("High Score List"));
810
mvaddstr(2, 19, _("Use cursor keys to scroll or ESC to leave"));
812
"Rank (score) (name) (level) (cps) (wpm) (typoinfos)"
815
for (i = 0; entries[i + m] != NULL && i < 17; i++) {
816
entry = entries[i + m];
817
/* XXX typespeed.c */
821
ratio = (1 - (float)(entry->count +
822
entry->wordswritten) / (entry->tcount +
823
entry->wordswritten)) * 100;
826
* These set colors on their own, so put them
827
* in front to safe expensive color_set call.
829
rank = typorankkaus(ratio);
830
if (!entries[i]->count)
832
mvaddstr(i + 5, 71, typorank[rank]);
834
lvl = level(entry->count);
835
if (lvl >= 0 && lvl < 11)
836
mvaddstr(i + 5, 40, rankki[lvl]);
839
mvprintw(i + 5, 2, "%2d.", i + m + 1);
840
mvprintw(i + 5, 7, "%6lu", entry->count);
841
mvaddstr(i + 5, 15, entry->name);
843
if (entry->duration) {
844
cps = (float)(entry->count +
845
entry->wordswritten) /
846
entry->duration * 100.0f;
848
mvprintw(i + 5, 50, "%2.3f", cps);
849
mvprintw(i + 5, 58, "%3lu", (uint32_t)(12.0f *
852
mvprintw(i + 5, 50, "%2.3f", 0.0f);
853
mvprintw(i + 5, 58, "%3lu", 0);
856
mvprintw(i + 5, 64, "%2.2f%%", ratio);
883
for (i = 0; entries[i] != NULL; i++)
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.
895
* First line of file "filename" will be ignored (description).
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.
907
loadwords(char *filename)
909
int fd, ignore, k, l;
910
char *curpos, *newpos, *p;
912
char buf[60], wordpath[MAXPATHLEN];
918
if (opt.net == NET) {
919
if (xsnprintf(buf, sizeof(buf), " WORDLIST %s", filename))
922
if (netrecv(1, 1, 1, 1, buf, sizeof(buf)) != 8)
925
if ((k = getnum(buf + sizeof(" WORDLIST ") - 1)) < 0)
928
words.bulk = xmalloc(j = 1024);
930
for (i = 0; k > 0; k--) {
931
if (netrecv(1, 1, 1, 0, buf, sizeof(buf)) == -1) {
935
buf[sizeof(buf) - 1] = '\0';
937
if (i + strlen(buf) + 2 >= j) {
939
if ((p = realloc(words.bulk, j)) == NULL) {
945
l = snprintf(words.bulk + i, j - i - 1, "%s\n", buf);
953
if (xsnprintf(wordpath, sizeof(wordpath), "%s/%s", worddir,
957
if ((fd = open(wordpath, O_RDONLY, 0)) == -1)
960
if (fstat(fd, &sb) == -1)
963
if (sb.st_size >= SSIZE_MAX)
966
words.bulk = xmalloc(sb.st_size + 1);
968
if (read(fd, words.bulk, sb.st_size) != sb.st_size) {
972
words.bulk[sb.st_size] = '\0';
975
xerr(1, "loadwords: fclose: %s", wordpath);
978
words.word = xmalloc(sizeof(char *) * 1024);
984
/* ignore title of word list */
985
if ((curpos = strchr(curpos, '\n')) == NULL) {
990
while ((p = strchr(curpos, '\n')) != NULL) {
994
if ((p = strchr(curpos, '\r')) != NULL)
997
if (curpos[0] == '\0' || strlen(curpos) > 19) {
1002
for (ignore = 0, p = curpos; *p != '\0'; p++)
1003
if (!isprint(*p) || isspace(*p)) {
1008
words.word[words.n++] = curpos;
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)
1016
words.word = pointer;
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];
1029
/* not enough words */
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.
1040
* IMPORTANT: "line" _will_ be changed, as option and value only point
1041
* at the correct position of line.
1043
* Returns 0 on success or 1 on failure.
1046
parseline(char *line, char **option, char **value)
1050
/* By default, no option and no value will be returned. */
1051
*option = *value = NULL;
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.
1060
if ((p = strchr(line, '\n')) == NULL)
1063
if ((p = strchr(line, '\r')) != NULL)
1065
if ((p = strchr(line, '#')) != NULL)
1067
while (*line != '\0' && isblank(*line))
1070
/* Anything left? */
1071
if (line[0] == '\0')
1075
* Locate the first "=" and use p for the oPtion
1076
* and v for the Value.
1078
if ((v = strchr(line, '=')) == NULL || v == line)
1085
/* Remove all trailing whitespaces between option and equal sign. */
1086
while (p != line && isblank(*p))
1089
/* Remove all whitespaces between equal sign and value. */
1090
while (*v != '\0' && isblank(*v))
1093
/* Remove all trailing whitespaces. */
1094
if ((p = strchr(v, '\0')) == NULL || p == v)
1097
while (p != v && isblank(*p))
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.
1115
char userhigh[MAXPATHLEN], userconf[MAXPATHLEN];
1118
if (xsnprintf(ruledir, sizeof(ruledir), "%s", RULEDIR)) {
1122
if (xsnprintf(worddir, sizeof(worddir), "%s", WORDDIR)) {
1127
readfile(CONFIGFILE, 1);
1129
if ((envhome = getenv("HOME")) == NULL)
1132
if (xsnprintf(userconf, sizeof(userconf), "%s/.typespeed/config",
1136
if (stat(userconf, &sb) || (sb.st_mode & S_IFMT) != S_IFREG)
1139
if (xsnprintf(userhigh, sizeof(userhigh), "%s/.typespeed/score",
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.
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);
1154
readfile(userconf, 1);
1158
* Function used to open configuration and game rule files and to
1159
* set options with function setoptions.
1162
readfile(char *filename, int op)
1166
unsigned long lineno;
1167
char *option, *value;
1168
char buf[1024], line[sizeof("worddir = ") + MAXPATHLEN + 2];
1172
if (opt.net == NET && !op) {
1173
if (xsnprintf(buf, sizeof(buf), " RULESET %s", filename))
1176
if (netrecv(1, 1, 1, 1, buf, sizeof(buf)) != 6)
1179
if ((i = getnum(buf + sizeof(" RULESET ") - 1)) < 0)
1182
for (; i >= 0; i--) {
1184
if (netrecv(1, 1, 1, 0, line, sizeof(line)) == -1)
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,
1191
if (option != NULL && value != NULL)
1192
setoptions(option, value, op);
1195
if ((file = fopen(filename, "r")) == NULL)
1198
while (fgets(line, sizeof(line), file) != NULL) {
1200
if (parseline(line, &option, &value))
1201
xerrx(1, "Error in %s line %lu\n", filename,
1203
if (option != NULL && value != NULL)
1204
setoptions(option, value, op);
1207
if (fclose(file) == EOF)
1208
xerr(1, "readfile: fclose: %s", filename);
1213
* setoptions sets actually configuration and rule sets. If a
1214
* configuration or rule set has been opened decides "op":
1216
* 1: configuration file
1219
setoptions(char *option, char *value, int op)
1223
if (op) { /* configuration file */
1224
if (!strcmp(option, "cheat")) {
1225
if (!strcmp(value, "yes"))
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);
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)
1244
else if (!strcmp(option, "min word length")) {
1245
if (!(val = cstrl(value)) && errno)
1246
xerrx(1, "min word length invalid: %s\n",
1248
if (val > 0 && val < 20)
1251
else if (!strcmp(option, "max word length")) {
1252
if (!(val = cstrl(value)) && errno)
1253
xerrx(1, "max word length invalid: %s\n",
1255
if (val > 0 && val < 20)
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;
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;
1270
else if (!strcmp(option, "highscore")) {
1271
if (!strcmp(value, "yes"))
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;
1281
else if (!strcmp(option, "min speed")) {
1282
if (!(val = cstrl(value)) && errno)
1283
xerrx(1, "min speed invalid: %s\n", value);
1285
rules.minspeed = val;
1287
else if (!strcmp(option, "max speed")) {
1288
if (!(val = cstrl(value)) && errno)
1289
xerrx(1, "max speed invalid: %s\n", value);
1291
rules.maxspeed = val;
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);
1301
else if (!strcmp(option, "smooth")) {
1302
if (!strcmp(value, "yes"))
1311
setruleset(char *path)
1314
struct finfo namelist[WORDFILE_MAX];
1315
char fullpath[MAXPATHLEN];
1317
k = getfinfo(&(namelist[1]), WORDFILE_MAX - 1, "rule.");
1319
xerrx(1, "setruleset: no rule sets found");
1320
for (i = 1; i < k; i++)
1321
if (!strcmp(path, namelist[i].name))
1324
xerrx(1, "setruleset: supplied default rule set not found");
1325
if (xsnprintf(fullpath, sizeof(fullpath), "%s/%s", ruledir,
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);
1334
* Removes escaped tabs and returns converted string.
1335
* This string must be passed to free later on!
1341
unescstr(char *string)
1344
char *new, *p, *retval;
1349
new = xmalloc(strlen(string) + 1);
1351
for (pos = 0, p = string; *p != '\0'; p++, pos++) {
1353
if (*(p + 1) == '\0')
1354
xerrx(1, "error in unescstr");
1362
retval = xstrdup(new);