hfc

Hosts file client
git clone git://git.marlonivo.com/hfc
Log | Files | Refs | LICENSE

config.c (10082B)


      1 /* config.c - hfc config file
      2 *
      3 * headers and macros */
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 #include <ctype.h>
      8 #include <ncurses.h>
      9 #include <unistd.h>
     10 #include <pwd.h>
     11 
     12 #include <sys/types.h>
     13 #include "config.h"
     14 
     15 /* functions */
     16 /* 01.00 */ static void trim(char *str);
     17 /* 02.00 */ static int get_special_key_code(const char *name);
     18 /* 03.00 */ static void extract_color(const char *input, char *output);
     19 /* 04.00 */ void remove_keybindings_for_action(const char *action);
     20 /* 05.00 */ void load_config(void);
     21 /* 06.00 */ short get_color_code(const char *name);
     22 /* 07.00 */ void init_colors(void);
     23 /* 08.00 */ void apply_color(ColorSetting *setting, short pair_id);
     24 /* 09.00 */ void remove_color(ColorSetting *setting, short pair_id);
     25 /* 10.00 */ const char *get_config_path(const char *filename, char *buffer, size_t size);
     26 /* 11.00 */ const char *get_keys_for_action(const char *action);
     27 
     28 HFCConfig config;
     29 
     30 /* mapping table */
     31 typedef struct {
     32 	const char *name;
     33 	int code;
     34 } KeyName;
     35 
     36 static KeyName special_keys[] = {
     37 	{ "key_up", KEY_UP },
     38 	{ "key_down", KEY_DOWN },
     39 	{ "key_left", KEY_LEFT },
     40 	{ "key_right", KEY_RIGHT },
     41 	{ "key_home", KEY_HOME },
     42 	{ "key_end", KEY_END },
     43 	{ "key_npage", KEY_NPAGE },
     44 	{ "key_ppage", KEY_PPAGE },
     45 	{ "key_enter", 10 },
     46 	{ "space", ' ' },
     47 };
     48 
     49 /* 01.00 */ static void
     50 trim(char *str)
     51 {
     52 	char *end;
     53 	while (isspace((unsigned char)*str)) str++;
     54 	end = str + strlen(str) - 1;
     55 	while (end > str && isspace((unsigned char)*end)) end--;
     56 	end[1] = '\0';
     57 }
     58 
     59 /* 02.00 */ static int
     60 get_special_key_code(const char *name)
     61 {
     62 	for (size_t i = 0; i < sizeof(special_keys) / sizeof(special_keys[0]); i++) {
     63 		if (strcmp(name, special_keys[i].name) == 0)
     64 			return special_keys[i].code;
     65 	}
     66 	return -1;
     67 }
     68 
     69 /* 03.00 */ static void
     70 extract_color(const char *input, char *output)
     71 {
     72 	if (!input || !*input) {
     73 		*output = '\0';
     74 		return;
     75 	}
     76 
     77 	while (isspace((unsigned char)*input)) input++;
     78 
     79 	if (!*input) {
     80 		*output = '\0';
     81 		return;
     82 	}
     83 
     84 	sscanf(input, "%15s", output);
     85 }
     86 
     87 /* 04.00 */ void remove_keybindings_for_action(const char *action) {
     88 	for (int i = 0; i < keybinding_count; ) {
     89 		if (strcmp(keybindings[i].action, action) == 0) {
     90 			for (int j = i; j < keybinding_count - 1; j++) {
     91 				keybindings[j] = keybindings[j + 1];
     92 			}
     93 			keybinding_count--;
     94 		} else {
     95 			i++;
     96 		}
     97 	}
     98 }
     99 
    100 /* 05.00 */ void load_config(void)
    101 {
    102 	/* init config to "no colors" and default keybindings count */
    103 	memset(&config, 0, sizeof(HFCConfig));
    104 	keybinding_count = 0;
    105 
    106 	ColorSetting *all_colors[] = {
    107 		&config.header, &config.footer, &config.entry_selected,
    108 		&config.entry_highlight, &config.entry_default, &config.table_header
    109 	};
    110 	for (size_t i = 0; i < sizeof(all_colors)/sizeof(all_colors[0]); i++) {
    111 		all_colors[i]->fg[0] = '\0';
    112 		all_colors[i]->bg[0] = '\0';
    113 		all_colors[i]->bold = false;
    114 	}
    115 
    116 	char path[512];
    117 	get_config_path("conf", path, sizeof(path));
    118 
    119 	/* default keybindings */
    120 	static struct {
    121 		const char *key;
    122 		const char *action;
    123 	} default_keybindings[] = {
    124 		{ "j", "down" }, { "k", "up" }, { "q", "quit" }, { "a", "add" },
    125 		{ "r", "remove" }, { "e", "edit" }, { "u", "update" },
    126 		{ "U", "update_all" }, { "o", "order" }, { "m", "merge" },
    127 		{ "L", "refresh" }, { "+", "select_all" }, { "-", "unselect_all" },
    128 		{ "space", "select" }, { "?", "help" }
    129 	};
    130 
    131 	FILE *fp = fopen(path, "r");
    132 	if (!fp) return;
    133 
    134 	char line[512];
    135 	char current_section[32] = {0};
    136 
    137 	/* ui color config mapping */
    138 	typedef struct {
    139 		const char *key;
    140 		ColorSetting *setting;
    141 	} ColorConfigMap;
    142 
    143 	ColorConfigMap color_map[] = {
    144 		{ "header",         &config.header },
    145 		{ "footer",         &config.footer },
    146 		{ "entry_selected", &config.entry_selected },
    147 		{ "entry_highlight",&config.entry_highlight },
    148 		{ "entry_default",  &config.entry_default },
    149 		{ "table_header",   &config.table_header }
    150 	};
    151 
    152 	while (fgets(line, sizeof(line), fp)) {
    153 		trim(line);
    154 
    155 		if (line[0] == '#' || line[0] == '\0') continue;
    156 
    157 		if (line[0] == '[') {
    158 			sscanf(line, "[%31[^]]", current_section);
    159 			continue;
    160 		}
    161 
    162 		if (strcmp(current_section, "ui") == 0) {
    163 			char word1[64] = {0}, word2[64] = {0}, word3[64] = {0};
    164 			char word4[64] = {0}, word5[64] = {0};
    165 
    166 			if (sscanf(line, "%63s %63s %63s %63s %63s", word1, word2, word3, word4, word5) >= 3
    167 				&& strcmp(word1, "color") == 0) {
    168 
    169 				for (size_t i = 0; i < sizeof(color_map)/sizeof(color_map[0]); ++i) {
    170 					if (strcmp(word2, color_map[i].key) == 0) {
    171 						ColorSetting *set = color_map[i].setting;
    172 
    173 						strncpy(set->fg, word3, sizeof(set->fg));
    174 
    175 						if (strcmp(word4, "bold") == 0) {
    176 							set->bg[0] = '\0';
    177 							set->bold = true;
    178 						} else if (*word4) {
    179 							strncpy(set->bg, word4, sizeof(set->bg));
    180 							if (strcmp(word5, "bold") == 0)
    181 								set->bold = true;
    182 						}
    183 
    184 						break;
    185 					}
    186 				}
    187 				}
    188 		}
    189 
    190 		if (strcmp(current_section, "keys") == 0) {
    191 			if (strncmp(line, "bind ", 5) == 0 && keybinding_count < MAX_KEYBINDINGS) {
    192 				char key_str[32], action[32];
    193 				if (sscanf(line + 5, "%31s %31s", key_str, action) == 2) {
    194 					int key = get_special_key_code(key_str);
    195 					if (key == -1 && strlen(key_str) == 1)
    196 						key = key_str[0];
    197 
    198 					if (key != -1) {
    199 						remove_keybindings_for_action(action);
    200 						keybindings[keybinding_count].key = key;
    201 						strncpy(keybindings[keybinding_count].action, action,
    202 								sizeof(keybindings[keybinding_count].action));
    203 						keybinding_count++;
    204 					}
    205 				}
    206 			}
    207 		}
    208 	}
    209 
    210 	fclose(fp);
    211 
    212 	/* add fallback keybindings if missing */
    213 	for (size_t i = 0; i < sizeof(default_keybindings) / sizeof(default_keybindings[0]); i++) {
    214 		const char *action = default_keybindings[i].action;
    215 		int already_bound = 0;
    216 
    217 		for (int j = 0; j < keybinding_count; j++) {
    218 			if (strcmp(keybindings[j].action, action) == 0) {
    219 				already_bound = 1;
    220 				break;
    221 			}
    222 		}
    223 
    224 		if (!already_bound && keybinding_count < MAX_KEYBINDINGS) {
    225 			int key = get_special_key_code(default_keybindings[i].key);
    226 			if (key == -1 && strlen(default_keybindings[i].key) == 1)
    227 				key = default_keybindings[i].key[0];
    228 
    229 			keybindings[keybinding_count].key = key;
    230 			strncpy(keybindings[keybinding_count].action,
    231 					action,
    232 		   sizeof(keybindings[keybinding_count].action));
    233 			keybinding_count++;
    234 		}
    235 	}
    236 }
    237 
    238 /* 06.00 */ short
    239 get_color_code(const char *name)
    240 {
    241 	if (!name || !*name) return -1;
    242 
    243 	struct {
    244 		const char *name;
    245 		short code;
    246 	} colors[] = {
    247 		{ "black",   COLOR_BLACK },
    248 		{ "red",     COLOR_RED },
    249 		{ "green",   COLOR_GREEN },
    250 		{ "yellow",  COLOR_YELLOW },
    251 		{ "blue",    COLOR_BLUE },
    252 		{ "magenta", COLOR_MAGENTA },
    253 		{ "cyan",    COLOR_CYAN },
    254 		{ "white",   COLOR_WHITE },
    255 		{ "gray",    8 },
    256 	};
    257 
    258 	for (size_t i = 0; i < sizeof(colors)/sizeof(colors[0]); i++) {
    259 		if (strcmp(name, colors[i].name) == 0)
    260 			return colors[i].code;
    261 	}
    262 
    263 	return -1;
    264 }
    265 
    266 /* 07.00 */ void
    267 init_colors(void)
    268 {
    269 	if (!has_colors()) return;
    270 
    271 	start_color();
    272 	use_default_colors();
    273 
    274 	struct {
    275 		ColorSetting *setting;
    276 		short pair_id;
    277 	} pairs[] = {
    278 		{ &config.header,         1 },
    279 		{ &config.footer,         2 },
    280 		{ &config.entry_selected, 3 },
    281 		{ &config.entry_highlight,4 },
    282 		{ &config.table_header,   5 },
    283 		{ &config.entry_default,  6 }
    284 	};
    285 
    286 	char fg_buf[16], bg_buf[16];
    287 	short fg_code, bg_code;
    288 
    289 	for (size_t i = 0; i < sizeof(pairs)/sizeof(pairs[0]); i++) {
    290 		ColorSetting *set = pairs[i].setting;
    291 
    292 		/* extract color */
    293 		extract_color(set->fg, fg_buf);
    294 		extract_color(set->bg, bg_buf);
    295 
    296 		fg_code = get_color_code(fg_buf);
    297 		bg_code = get_color_code(bg_buf);
    298 
    299 		/* only initialize if at least one valid color */
    300 		if (fg_code != -1 || bg_code != -1) {
    301 			init_pair(pairs[i].pair_id,
    302 					  (bg_code != -1 ? bg_code : -1),
    303 					  (fg_code != -1 ? fg_code : -1));
    304 		}
    305 	}
    306 }
    307 
    308 /* 08.00 */ void
    309 apply_color(ColorSetting *setting, short pair_id)
    310 {
    311 	if (!*setting->fg && !*setting->bg)
    312 		return;
    313 
    314 	int attrs = COLOR_PAIR(pair_id);
    315 	if (setting->bold)
    316 		attrs |= A_BOLD;
    317 	attron(attrs);
    318 }
    319 
    320 /* 09.00 */ void
    321 remove_color(ColorSetting *setting, short pair_id)
    322 {
    323 	if (!*setting->fg && !*setting->bg)
    324 		return;
    325 
    326 	int attrs = COLOR_PAIR(pair_id);
    327 	if (setting->bold)
    328 		attrs |= A_BOLD;
    329 	attroff(attrs);
    330 }
    331 
    332 /* 10.00 */ const char *
    333 get_config_path(const char *filename, char *buffer, size_t size)
    334 {
    335 	const char *home = NULL;
    336 
    337 	if (geteuid() == 0) {
    338 		const char *sudo_user = getenv("SUDO_USER");
    339 		if (sudo_user && strlen(sudo_user) > 0) {
    340 			struct passwd *pw = getpwnam(sudo_user);
    341 			if (pw && pw->pw_dir) {
    342 				home = pw->pw_dir;
    343 			}
    344 		}
    345 	}
    346 
    347 	if (!home || strlen(home) == 0) {
    348 		home = getenv("HOME");
    349 	}
    350 
    351 	if (!home || strlen(home) == 0) {
    352 		fprintf(stderr, "[FATAL] Kein HOME-Verzeichnis gefunden!\n");
    353 		return NULL;
    354 	}
    355 
    356 	const char *xdg_config_home = getenv("XDG_CONFIG_HOME");
    357 	const char *xdg_state_home  = getenv("XDG_STATE_HOME");
    358 
    359 	if (strcmp(filename, "counts") == 0) {
    360 		if (xdg_state_home && strlen(xdg_state_home) > 0) {
    361 			snprintf(buffer, size, "%s/hfc/%s", xdg_state_home, filename);
    362 		} else {
    363 			snprintf(buffer, size, "%s/.local/state/hfc/%s", home, filename);
    364 		}
    365 	} else {
    366 		if (xdg_config_home && strlen(xdg_config_home) > 0) {
    367 			snprintf(buffer, size, "%s/hfc/%s", xdg_config_home, filename);
    368 		} else {
    369 			snprintf(buffer, size, "%s/.config/hfc/%s", home, filename);
    370 		}
    371 	}
    372 
    373 	return buffer;
    374 }
    375 
    376 /* 11.00 */ const char *
    377 get_keys_for_action(const char *action) {
    378 	static char buf[128];
    379 	buf[0] = '\0';
    380 
    381 	for (int i = 0; i < keybinding_count; i++) {
    382 		if (strcmp(keybindings[i].action, action) == 0) {
    383 			char keyname[32];
    384 
    385 			/* check special keys */
    386 			int found_special = 0;
    387 			for (size_t j = 0; j < sizeof(special_keys)/sizeof(special_keys[0]); j++) {
    388 				if (keybindings[i].key == special_keys[j].code) {
    389 					snprintf(keyname, sizeof(keyname), "%s", special_keys[j].name);
    390 					found_special = 1;
    391 					break;
    392 				}
    393 			}
    394 			if (!found_special) {
    395 				snprintf(keyname, sizeof(keyname), "%c", keybindings[i].key);
    396 			}
    397 
    398 			if (strstr(buf, keyname) == NULL) {
    399 				if (buf[0] != '\0') {
    400 					strncat(buf, "/", sizeof(buf) - strlen(buf) - 1);
    401 				}
    402 				strncat(buf, keyname, sizeof(buf) - strlen(buf) - 1);
    403 			}
    404 		}
    405 	}
    406 
    407 	return (buf[0] != '\0') ? buf : "?";
    408 }