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 }