libchess: Make sure the move is null terminated after fscanf() in
[cboard.git] / src / rcfile.c
blob4f0b126d12ab1fcd129f9844cf5fc32ade04ba93
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2006 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <err.h>
23 #include <ctype.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <errno.h>
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
32 #ifdef HAVE_NCURSES_H
33 #include <ncurses.h>
34 #endif
36 #include "chess.h"
37 #include "conf.h"
38 #include "misc.h"
39 #include "colors.h"
40 #include "rcfile.h"
42 #ifdef WITH_DMALLOC
43 #include <dmalloc.h>
44 #endif
46 static int attributes(const char *filename, int line, char *str)
48 char *tmp;
49 int attrs = 0;
51 while ((tmp = strsep(&str, ",")) != NULL) {
52 if (strcasecmp(tmp, "BOLD") == 0)
53 attrs |= A_BOLD;
54 else if (strcasecmp(tmp, "REVERSE") == 0)
55 attrs |= A_REVERSE;
56 else if (strcasecmp(tmp, "NONE") == 0)
57 attrs |= A_NORMAL;
58 else if (strcasecmp(tmp, "DIM") == 0)
59 attrs |= A_DIM;
60 else if (strcasecmp(tmp, "STANDOUT") == 0)
61 attrs |= A_STANDOUT;
62 else if (strcasecmp(tmp, "UNDERLINE") == 0)
63 attrs |= A_UNDERLINE;
64 else if (strcasecmp(tmp, "BLINK") == 0)
65 attrs |= A_BLINK;
66 else if (strcasecmp(tmp, "INVISIBLE") == 0)
67 attrs |= A_INVIS;
68 else
69 errx(EXIT_FAILURE, "%s(%i): invalid attribute \"%s\"", filename,
70 line, tmp);
73 return attrs;
76 static short color_name(const char *filename, int line, const char *color)
78 if (strcasecmp(color, "BLACK") == 0)
79 return COLOR_BLACK;
80 else if (strcasecmp(color, "WHITE") == 0)
81 return COLOR_WHITE;
82 else if (strcasecmp(color, "GREEN") == 0)
83 return COLOR_GREEN;
84 else if (strcasecmp(color, "YELLOW") == 0)
85 return COLOR_YELLOW;
86 else if (strcasecmp(color, "MAGENTA") == 0)
87 return COLOR_MAGENTA;
88 else if (strcasecmp(color, "BLUE") == 0)
89 return COLOR_BLUE;
90 else if (strcasecmp(color, "RED") == 0)
91 return COLOR_RED;
92 else if (strcasecmp(color, "CYAN") == 0)
93 return COLOR_CYAN;
94 else
95 errx(EXIT_FAILURE, "%s(%i): invalid color \"%s\"", filename, line,
96 color);
98 return -1;
101 static void parse_color(const char *filename, int line, const char *str,
102 struct color_s *c)
104 char fg[16], bg[16], attr[64], nattr[64];
105 struct color_s ctmp = *c;
106 int n;
108 if ((n = sscanf(str, "%[a-zA-Z] %[a-zA-Z] %[a-zA-Z,] %[a-zA-Z,]", fg, bg,
109 attr, nattr)) < 2)
110 errx(EXIT_FAILURE, "%s(%i): parse error", filename, line);
112 ctmp.fg = color_name(filename, line, fg);
113 ctmp.bg = color_name(filename, line, bg);
114 ctmp.attrs = ctmp.nattrs = 0;
116 if (n > 2)
117 ctmp.attrs = attributes(filename, line, attr);
119 if (n > 3)
120 ctmp.nattrs = attributes(filename, line, nattr);
122 *c = ctmp;
125 static int on_or_off(const char *filename, int lines, const char *str)
127 if (strcasecmp(str, "on") == 0 || strcasecmp(str, "1") == 0 ||
128 strcasecmp(str, "yes") == 0 || strcasecmp(str, "true") == 0)
129 return 1;
131 if (strcasecmp(str, "off") == 0 || strcasecmp(str, "0") == 0 ||
132 strcasecmp(str, "no") == 0 || strcasecmp(str, "false") == 0)
133 return 0;
135 errx(EXIT_FAILURE, "%s(%i): invalid value \"%s\"", filename, lines, str);
138 void copydatafile(const char *dst, const char *src)
140 FILE *fp, *ofp;
141 char buf[LINE_MAX], *s;
143 snprintf(buf, sizeof(buf), "%s/%s", DATA_PATH, src);
145 if ((fp = fopen(buf, "r")) == NULL) {
146 if (errno != ENOENT)
147 warn("%s", buf);
148 return;
151 if ((ofp = fopen(dst, "w+")) == NULL) {
152 fclose(fp);
153 warn("%s", dst);
154 return;
157 while ((s = fgets(buf, sizeof(buf), fp)) != NULL)
158 fprintf(ofp, "%s", s);
160 fclose(fp);
161 fclose(ofp);
164 void set_config_defaults()
166 struct stat st;
168 config.pattern = strdup("*.pgn");
169 config.engine_cmd = strdup("gnuchess --xboard");
170 config.jumpcount = 5;
171 config.linegraphics = 1;
172 config.saveprompt = 1;
173 config.deleteprompt = 1;
174 config.validmoves = 1;
176 set_default_colors();
178 if (stat(config.nagfile, &st) == -1) {
179 if (errno == ENOENT)
180 copydatafile(config.nagfile, "nag.data");
181 else
182 warn("%s", config.nagfile);
185 if (stat(config.ccfile, &st) == -1) {
186 if (errno == ENOENT)
187 copydatafile(config.ccfile, "cc.data");
188 else
189 warn("%s", config.ccfile);
193 void parse_rcfile(const char *filename)
195 FILE *fp;
196 char *line, buf[LINE_MAX];
197 int lines = 0;
198 char *altengine = NULL;
199 int k = 0;
200 int init = 0;
202 if ((fp = fopen(filename, "r")) == NULL)
203 err(EXIT_FAILURE, "%s", filename);
205 while ((line = fgets(buf, sizeof(buf), fp)) != NULL) {
206 int n, c;
207 char var[30], val[50];
208 char token[MAX_PGN_LINE_LEN + 1], value[MAX_PGN_LINE_LEN + 1];
209 char *p;
211 lines++;
212 line = trim(line);
214 if (!line[0] || line[0] == '#')
215 continue;
217 if ((n = sscanf(line, "%s %[^\n]", var, val)) != 2)
218 errx(EXIT_FAILURE, "%s(%i): parse error %i", filename, lines,n);
220 p = strdup(trim(val));
221 strncpy(val, p, sizeof(val));
222 free(p);
223 p = strdup(trim(var));
224 strncpy(var, p, sizeof(var));
225 free(p);
227 if (strcmp(var, "jump_count") == 0) {
228 if (!isinteger(val))
229 errx(EXIT_FAILURE, "%s(%i): value is not an integer", filename,
230 lines);
232 config.jumpcount = atoi(val);
234 else if (strcmp(var, "bind") == 0) {
235 config.keys = Realloc(config.keys, (k + 2) *
236 sizeof(struct key_s *));
237 config.keys[k] = Calloc(1, sizeof(struct key_s));
238 p = val;
239 n = 0;
241 while (*p && !isspace(*p))
242 p++, n++;
244 c = *p;
245 *p = 0;
246 p -= n;
248 if (strcasecmp(p, "none") == 0)
249 config.keys[k]->type = KEY_DEFAULT;
250 else if (strcasecmp(p, "repeat") == 0)
251 config.keys[k]->type = KEY_REPEAT;
252 else if (strcasecmp(p, "set") == 0)
253 config.keys[k]->type = KEY_SET;
254 else
255 errx(EXIT_FAILURE, "%s(%i): invalid value \"%s\"", filename,
256 lines, p);
258 p = val + n;
259 *p = c;
261 while (*p && isspace(*p))
262 p++;
264 config.keys[k]->c = *p++;
265 config.keys[k++]->str = strdup(p);
266 config.keys[k] = NULL;
268 else if (strcmp(var, "engine_init") == 0) {
269 config.einit = Realloc(config.einit, (init + 2) * sizeof(char *));
270 config.einit[init++] = strdup(val);
271 config.einit[init] = NULL;
273 else if (strcmp(var, "pattern") == 0) {
274 free(config.pattern);
275 config.pattern = strdup(val);
277 else if (strcmp(var, "mpl") == 0) {
278 if (!isinteger(val))
279 errx(EXIT_FAILURE, "%s(%i): value is not an integer", filename,
280 lines);
281 pgn_config_set(PGN_MPL, atoi(val));
283 else if (strcmp(var, "stop_on_error") == 0)
284 pgn_config_set(PGN_STOP_ON_ERROR, on_or_off(filename, lines, val));
285 else if (strcmp(var, "tag") == 0) {
286 if ((n = sscanf(val, "%s %s ", token, value)) < 1 ||
287 n > 2)
288 errx(EXIT_FAILURE, "%s(%i): invalid value \"%s\"", filename,
289 lines, val);
291 if (n == 1)
292 value[0] = 0;
293 else {
294 p = val + strlen(token);
295 strncpy(value, p, sizeof(value));
298 for (n = 0; n < strlen(token); n++) {
299 if (!isalnum(token[n]) && token[n] != '_')
300 errx(EXIT_FAILURE,
301 "%s(%i): token names must match 0-9A-Za-z_.",
302 filename, lines);
305 token[0] = toupper(token[0]);
306 pgn_tag_add(&config.tag, token, value);
308 else if (strcmp(var, "save_directory") == 0)
309 config.savedirectory = strdup(val);
310 else if (strcmp(var, "line_graphics") == 0)
311 config.linegraphics = on_or_off(filename, lines, val);
312 else if (strcmp(var, "save_prompt") == 0)
313 config.saveprompt = on_or_off(filename, lines, val);
314 else if (strcmp(var, "delete_prompt") == 0)
315 config.deleteprompt = on_or_off(filename, lines, val);
316 else if (strcmp(var, "valid_moves") == 0)
317 config.validmoves = on_or_off(filename, lines, val);
318 else if (strcmp(var, "board_details") == 0)
319 config.details = on_or_off(filename, lines, val);
320 else if (strcmp(var, "engine_cmd") == 0)
321 altengine = strdup(val);
322 else if (strcmp(var, "color_board_window") == 0)
323 parse_color(filename, lines, val, &config.color[CONF_BDWINDOW]);
324 else if (strcmp(var, "color_board_selected") == 0)
325 parse_color(filename, lines, val, &config.color[CONF_BSELECTED]);
326 else if (strcmp(var, "color_board_white_moves") == 0)
327 parse_color(filename, lines, val, &config.color[CONF_BMOVESW]);
328 else if (strcmp(var, "color_board_black_moves") == 0)
329 parse_color(filename, lines, val, &config.color[CONF_BMOVESB]);
330 else if (strcmp(var, "color_board_count") == 0)
331 parse_color(filename, lines, val, &config.color[CONF_BCOUNT]);
332 else if (strcmp(var, "color_board_cursor") == 0)
333 parse_color(filename, lines, val, &config.color[CONF_BCURSOR]);
334 else if (strcmp(var, "color_board_black") == 0)
335 parse_color(filename, lines, val, &config.color[CONF_BBLACK]);
336 else if (strcmp(var, "color_board_white") == 0)
337 parse_color(filename, lines, val, &config.color[CONF_BWHITE]);
338 else if (strcmp(var, "color_board_graphics") == 0)
339 parse_color(filename, lines, val, &config.color[CONF_BGRAPHICS]);
340 else if (strcmp(var, "color_board_coords") == 0)
341 parse_color(filename, lines, val, &config.color[CONF_BCOORDS]);
342 else if (strcmp(var, "color_status_window") == 0)
343 parse_color(filename, lines, val, &config.color[CONF_SWINDOW]);
344 else if (strcmp(var, "color_status_title") == 0)
345 parse_color(filename, lines, val, &config.color[CONF_STITLE]);
346 else if (strcmp(var, "color_status_border") == 0)
347 parse_color(filename, lines, val, &config.color[CONF_SBORDER]);
348 else if (strcmp(var, "color_status_notify") == 0)
349 parse_color(filename, lines, val, &config.color[CONF_SNOTIFY]);
350 else if (strcmp(var, "color_status_engine") == 0)
351 parse_color(filename, lines, val, &config.color[CONF_SENGINE]);
352 else if (strcmp(var, "color_tag_window") == 0)
353 parse_color(filename, lines, val, &config.color[CONF_TWINDOW]);
354 else if (strcmp(var, "color_tag_title") == 0)
355 parse_color(filename, lines, val, &config.color[CONF_TTITLE]);
356 else if (strcmp(var, "color_tag_border") == 0)
357 parse_color(filename, lines, val, &config.color[CONF_TBORDER]);
358 else if (strcmp(var, "color_history_window") == 0)
359 parse_color(filename, lines, val, &config.color[CONF_HWINDOW]);
360 else if (strcmp(var, "color_history_title") == 0)
361 parse_color(filename, lines, val, &config.color[CONF_HTITLE]);
362 else if (strcmp(var, "color_history_border") == 0)
363 parse_color(filename, lines, val, &config.color[CONF_HBORDER]);
364 else if (strcmp(var, "color_message_window") == 0)
365 parse_color(filename, lines, val, &config.color[CONF_MWINDOW]);
366 else if (strcmp(var, "color_message_title") == 0)
367 parse_color(filename, lines, val, &config.color[CONF_MTITLE]);
368 else if (strcmp(var, "color_message_border") == 0)
369 parse_color(filename, lines, val, &config.color[CONF_MBORDER]);
370 else if (strcmp(var, "color_message_prompt") == 0)
371 parse_color(filename, lines, val, &config.color[CONF_MPROMPT]);
372 else if (strcmp(var, "color_input_window") == 0)
373 parse_color(filename, lines, val, &config.color[CONF_IWINDOW]);
374 else if (strcmp(var, "color_input_title") == 0)
375 parse_color(filename, lines, val, &config.color[CONF_ITITLE]);
376 else if (strcmp(var, "color_input_border") == 0)
377 parse_color(filename, lines, val, &config.color[CONF_IBORDER]);
378 else if (strcmp(var, "color_input_prompt") == 0)
379 parse_color(filename, lines, val, &config.color[CONF_IPROMPT]);
380 else if (strcmp(var, "color_menu") == 0)
381 parse_color(filename, lines, val, &config.color[CONF_MENU]);
382 else if (strcmp(var, "color_menu_selected") == 0)
383 parse_color(filename, lines, val, &config.color[CONF_MENUS]);
384 else if (strcmp(var, "color_menu_highlight") == 0)
385 parse_color(filename, lines, val, &config.color[CONF_MENUH]);
386 else
387 errx(EXIT_FAILURE, "%s(%i): invalid parameter \"%s\"", filename,
388 lines, var);
391 fclose(fp);
393 if (altengine) {
394 free(config.engine_cmd);
395 config.engine_cmd = NULL;
396 config.engine_cmd = altengine;