config.c (10654B)
1 /* 2 * src/config.c from hirc 3 * 4 * Copyright (c) 2021-2022 hhvn <dev@hhvn.uk> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 */ 19 20 #include <ncurses.h> 21 #include <string.h> 22 #include <stdlib.h> 23 #include <errno.h> 24 #include <limits.h> 25 #include "hirc.h" 26 27 /* 28 * Handlers. 29 * These can be attached to a config variable in order to performs actions 30 * when changing the value, or validate the new value before setting. 31 * A non-zero return value sets the variable, whilst a 0 keeps the old value. 32 */ 33 static int config_window_hide(struct Config *conf, long num); 34 static int config_window_location(struct Config *conf, long num); 35 static int config_window_width(struct Config *conf, long num); 36 static int config_nickcolour_self(struct Config *conf, long num); 37 static int config_nickcolour_range(struct Config *conf, long a, long b); 38 static int config_redrawl(struct Config *conf, long num); 39 static int config_redraws(struct Config *conf, char *str); 40 static int config_formats(struct Config *conf, char *str); 41 42 static char *strbool[] = { "true", "false", NULL }; 43 static char *strlocation[] = { 44 [Location_left] = "left", 45 [Location_right] = "right", 46 NULL 47 }; 48 49 static struct { 50 char *name; 51 long min, max; 52 } vals[] = { 53 [Val_string] = {"a string"}, 54 [Val_bool] = {"boolean (true/false)", 0, 1}, 55 [Val_colour] = {"a number from 0 to 99", 0, 99}, 56 [Val_signed] = {"a numeric value", LONG_MIN, LONG_MAX}, 57 [Val_unsigned] = {"positive", 0, LONG_MAX}, 58 [Val_nzunsigned] = {"greater than zero", 1, LONG_MAX}, 59 [Val_pair] = {"a pair", LONG_MIN, LONG_MAX}, 60 [Val_colourpair] = {"pair with numbers from 0 to 99", 0, 99}, 61 [Val_location] = {"a location (left/right)", Location_left, Location_right}, 62 }; 63 64 #include "data/config.h" 65 66 struct Config * 67 config_getp(char *name) { 68 int i; 69 70 assert_warn(name, NULL); 71 72 for (i = 0; config[i].name; i++) 73 if (strcmp(config[i].name, name) == 0) 74 return &config[i]; 75 return NULL; 76 } 77 78 char * 79 config_get_pretty(struct Config *conf, int pairbrace) { 80 static char ret[8192]; 81 82 if (conf) { 83 if (conf->valtype == Val_string) 84 return conf->str; 85 else if (conf->valtype == Val_location) 86 return strlocation[conf->num]; 87 else if (conf->valtype == Val_bool) 88 return strbool[conf->num]; 89 else if (conf->valtype == Val_pair || conf->valtype == Val_colourpair) 90 snprintf(ret, sizeof(ret), pairbrace ? "{%ld, %ld}" : "%ld %ld", conf->pair[0], conf->pair[1]); 91 else 92 snprintf(ret, sizeof(ret), "%ld", conf->num); 93 return ret; 94 } else return NULL; 95 } 96 97 long 98 config_getl(char *name) { 99 struct Config *conf = config_getp(name); 100 if (conf && (conf->valtype == Val_bool || 101 conf->valtype == Val_colour || 102 conf->valtype == Val_signed || 103 conf->valtype == Val_unsigned || 104 conf->valtype == Val_nzunsigned || 105 conf->valtype == Val_location)) 106 return conf->num; 107 108 return 0; 109 } 110 111 char * 112 config_gets(char *name) { 113 struct Config *conf = config_getp(name); 114 if (conf && conf->valtype == Val_string) 115 return conf->str; 116 117 return NULL; 118 } 119 120 void 121 config_getr(char *name, long *a, long *b) { 122 struct Config *conf = config_getp(name); 123 if (conf && (conf->valtype == Val_pair || conf->valtype == Val_colourpair)) { 124 if (a) *a = conf->pair[0]; 125 if (b) *b = conf->pair[1]; 126 } 127 128 /* return an int for success? */ 129 } 130 131 void 132 config_setl(struct Config *conf, long num) { 133 assert_warn(conf,); 134 if (num >= vals[conf->valtype].min && num <= vals[conf->valtype].max && ( 135 conf->valtype == Val_bool || 136 conf->valtype == Val_colour || 137 conf->valtype == Val_signed || 138 conf->valtype == Val_unsigned || 139 conf->valtype == Val_nzunsigned || 140 conf->valtype == Val_location)) { 141 if (conf->numhandle) 142 if (!conf->numhandle(conf, num)) 143 return; 144 conf->isdef = 0; 145 conf->num = num; 146 } else { 147 ui_error("%s must be %s", conf->name, vals[conf->valtype].name); 148 } 149 } 150 151 void 152 config_sets(struct Config *conf, char *str) { 153 assert_warn(conf,); 154 if (conf->valtype != Val_string) { 155 ui_error("%s must be %s", conf->name, vals[conf->valtype].name); 156 return; 157 } 158 if (conf->strhandle) 159 if (!conf->strhandle(conf, str)) 160 return; 161 if (!conf->isdef) 162 pfree(&conf->str); 163 else 164 conf->isdef = 0; 165 conf->str = estrdup(str); 166 } 167 168 void 169 config_setr(struct Config *conf, long a, long b) { 170 assert_warn(conf,); 171 if (a >= vals[conf->valtype].min && b <= vals[conf->valtype].max && 172 (conf->valtype == Val_pair || conf->valtype == Val_colourpair)) { 173 if (conf->pairhandle) 174 if (!conf->pairhandle(conf, a, b)) 175 return; 176 conf->isdef = 0; 177 conf->pair[0] = a; 178 conf->pair[1] = b; 179 } else { 180 ui_error("%s must be %s", conf->name, vals[conf->valtype].name); 181 } 182 } 183 184 void 185 config_set(char *name, char *val) { 186 char *str = val ? estrdup(val) : NULL; 187 char *tok[3], *save; 188 struct Config *conf; 189 int i, found; 190 191 tok[0] = strtok_r(val, " ", &save); 192 tok[1] = strtok_r(NULL, " ", &save); 193 tok[2] = strtok_r(NULL, " ", &save); 194 195 if (!(conf = config_getp(name))) { 196 if (tok[0]) { 197 ui_error("no such configuration variable", NULL); 198 goto end; 199 } 200 } 201 202 if (strisnum(tok[0], 1) && strisnum(tok[1], 1) && !tok[2]) { 203 config_setr(conf, strtol(tok[0], NULL, 10), strtol(tok[1], NULL, 10)); 204 } else if (strisnum(tok[0], 1) && !tok[1]) { 205 config_setl(conf, strtol(tok[0], NULL, 10)); 206 } else if (tok[0] && !tok[1] && conf->valtype == Val_bool) { 207 if (strcmp(tok[0], "true") == 0) 208 config_setl(conf, 1); 209 else if (strcmp(tok[0], "false") == 0) 210 config_setl(conf, 0); 211 else 212 goto inval; 213 } else if (tok[0] && !tok[1] && conf->valtype == Val_location) { 214 if (strcmp(tok[0], "left") == 0) 215 config_setl(conf, Location_left); 216 else if (strcmp(tok[0], "right") == 0) 217 config_setl(conf, Location_right); 218 else 219 goto inval; 220 } else if (tok[0]) { 221 config_sets(conf, str); 222 } else { 223 for (i = found = 0; config[i].name; i++) { 224 if (strncmp(config[i].name, name, strlen(name)) == 0) { 225 hist_format(selected.history, Activity_status, HIST_UI, "SELF_UI :%s: %s", 226 config[i].name, config_get_pretty(&config[i], 1)); 227 found = 1; 228 } 229 } 230 231 if (!found) 232 ui_error("no such configuration variable", NULL); 233 } 234 235 end: 236 pfree(&str); 237 return; 238 239 inval: 240 ui_error("%s must be %s", name, vals[conf->valtype].name); 241 goto end; 242 } 243 244 int 245 config_read(char *filename) { 246 static char **bt = NULL; 247 static int btoffset = 0; 248 int ret = 0, serrno; 249 char buf[8192]; 250 char *path; 251 FILE *file; 252 int save, i; 253 254 assert_warn(filename, -2); 255 256 path = realpath(filename, NULL); 257 258 /* Check if file is already being read */ 259 if (bt && btoffset) { 260 for (i = 0; i < btoffset; i++) { 261 if (strcmp_n(path, *(bt + i)) == 0) { 262 ui_error("recursive read of '%s' is not allowed", filename); 263 pfree(&path); 264 return -2; 265 } 266 } 267 } 268 269 /* Expand bt and add real path */ 270 if (!bt) 271 bt = emalloc((sizeof(char *)) * (btoffset + 1)); 272 else 273 bt = erealloc(bt, (sizeof(char *)) * (btoffset + 1)); 274 275 *(bt + btoffset) = path; 276 btoffset++; 277 278 /* Read and execute */ 279 if ((file = fopen(filename, "rb")) == NULL) { 280 serrno = errno; 281 ret = -1; 282 ui_error("cannot open file '%s': %s", filename, strerror(errno)); 283 goto shrink; 284 } 285 286 save = nouich; 287 nouich = 1; 288 while (fgets(buf, sizeof(buf), file)) { 289 buf[strlen(buf) - 1] = '\0'; /* remove \n */ 290 if (*buf == '/') 291 command_eval(NULL, buf); 292 } 293 fclose(file); 294 nouich = save; 295 296 shrink: 297 /* Remove path from bt and shrink */ 298 pfree(&path); 299 btoffset--; 300 if (btoffset == 0) { 301 pfree(&bt); 302 bt = NULL; 303 } else { 304 bt = erealloc(bt, (sizeof(char *)) * btoffset); 305 assert(bt != NULL); 306 } 307 308 errno = serrno; 309 return ret; 310 } 311 312 static int 313 config_window_hide(struct Config *conf, long num) { 314 enum Windows win; 315 enum WindowLocation loc = Location_hidden; 316 if (strcmp(conf->name, "buflist.hidden") == 0) { 317 win = Win_buflist; 318 if (!num) 319 loc = config_getl("buflist.location"); 320 } else if (strcmp(conf->name, "nicklist.hidden") == 0) { 321 win = Win_nicklist; 322 if (!num) 323 loc = config_getl("nicklist.location"); 324 } 325 if (win != Win_nicklist || selected.hasnicks) 326 windows[win].location = loc; 327 conf->isdef = 0; 328 ui_redraw(); 329 return 1; 330 } 331 332 /* prevent nicklist/buflist being drawn in same location */ 333 static int 334 config_window_location(struct Config *conf, long num) { 335 struct Config *otherloc, *otherhide; 336 enum Windows win, otherwin; 337 if (strcmp(conf->name, "buflist.location") == 0) { 338 win = Win_buflist; 339 otherwin = Win_nicklist; 340 otherloc = config_getp("nicklist.location"); 341 otherhide = config_getp("nicklist.hidden"); 342 } else if (strcmp(conf->name, "nicklist.location") == 0) { 343 win = Win_nicklist; 344 otherwin = Win_buflist; 345 otherloc = config_getp("buflist.location"); 346 otherhide = config_getp("buflist.hidden"); 347 } 348 if (num == otherloc->num) { 349 otherloc->num = (num == Location_left) ? Location_right : Location_left; 350 otherloc->isdef = 0; 351 if (!otherhide->num && (otherwin != Win_nicklist || selected.hasnicks)) 352 windows[otherwin].location = otherloc->num; 353 } 354 conf->num = num; 355 if (win != Win_nicklist || selected.hasnicks) 356 windows[win].location = num; 357 conf->isdef = 0; 358 ui_redraw(); 359 return 0; 360 } 361 362 static int 363 config_window_width(struct Config *conf, long num) { 364 enum WindowLocation win; 365 if (strcmp(conf->name, "buflist.width") == 0) 366 win = Win_buflist; 367 else if (strcmp(conf->name, "nicklist.width") == 0) 368 win = Win_nicklist; 369 if (num <= COLS - (windows[win].location ? windows[win].w : 0) - 2) { 370 uineedredraw = 1; 371 return 1; 372 } else { 373 ui_error("window will be too big", NULL); 374 return 0; 375 } 376 } 377 378 static int 379 config_nickcolour_self(struct Config *conf, long num) { 380 windows[Win_nicklist].refresh = 1; 381 return 1; 382 } 383 384 static int 385 config_nickcolour_range(struct Config *conf, long a, long b) { 386 windows[Win_nicklist].refresh = 1; 387 return 1; 388 } 389 390 static int 391 config_redraws(struct Config *conf, char *str) { 392 ui_redraw(); 393 return 1; 394 } 395 396 static int 397 config_redrawl(struct Config *conf, long num) { 398 ui_redraw(); 399 return 1; 400 } 401 402 /* Don't set formats with syntax errors */ 403 static int 404 config_formats(struct Config *conf, char *str) { 405 int ret = format(NULL, str, NULL) ? 1 : 0; 406 ui_redraw(); /* for success & error msg */ 407 return ret; 408 }