hirc

[archived] IRC client
git clone https://hhvn.uk/hirc
git clone git://hhvn.uk/hirc
Log | Files | Refs

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 }