1 /* vifm
2 * Copyright (C) 2001 Ken Steen.
3 * Copyright (C) 2011 xaizek.
4 *
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.
9 *
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.
14 *
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 #include "color_scheme.h"
21
22 #include <curses.h>
23 #include <regex.h> /* regcomp() regexec() */
24
25 #include <assert.h> /* assert() */
26 #include <limits.h> /* INT_MAX */
27 #include <stddef.h> /* NULL size_t */
28 #include <stdio.h> /* snprintf() */
29 #include <stdlib.h> /* free() */
30 #include <string.h> /* strcpy() strlen() */
31
32 #include "../cfg/config.h"
33 #include "../compat/dtype.h"
34 #include "../compat/fs_limits.h"
35 #include "../compat/os.h"
36 #include "../compat/reallocarray.h"
37 #include "../engine/completion.h"
38 #include "../modes/dialogs/msg_dialog.h"
39 #include "../utils/fs.h"
40 #include "../utils/fsddata.h"
41 #include "../utils/macros.h"
42 #include "../utils/matchers.h"
43 #include "../utils/str.h"
44 #include "../utils/string_array.h"
45 #include "../utils/utils.h"
46 #include "../status.h"
47 #include "color_manager.h"
48 #include "statusbar.h"
49 #include "ui.h"
50
51 char *HI_GROUPS[] = {
52 [WIN_COLOR] = "Win",
53 [DIRECTORY_COLOR] = "Directory",
54 [LINK_COLOR] = "Link",
55 [BROKEN_LINK_COLOR] = "BrokenLink",
56 [HARD_LINK_COLOR] = "HardLink",
57 [SOCKET_COLOR] = "Socket",
58 [DEVICE_COLOR] = "Device",
59 [FIFO_COLOR] = "Fifo",
60 [EXECUTABLE_COLOR] = "Executable",
61 [SELECTED_COLOR] = "Selected",
62 [CURR_LINE_COLOR] = "CurrLine",
63 [TOP_LINE_COLOR] = "TopLine",
64 [TOP_LINE_SEL_COLOR] = "TopLineSel",
65 [STATUS_LINE_COLOR] = "StatusLine",
66 [WILD_MENU_COLOR] = "WildMenu",
67 [CMD_LINE_COLOR] = "CmdLine",
68 [ERROR_MSG_COLOR] = "ErrorMsg",
69 [BORDER_COLOR] = "Border",
70 [OTHER_LINE_COLOR] = "OtherLine",
71 [JOB_LINE_COLOR] = "JobLine",
72 [SUGGEST_BOX_COLOR] = "SuggestBox",
73 [MISMATCH_COLOR] = "CmpMismatch",
74 [AUX_WIN_COLOR] = "AuxWin",
75 [TAB_LINE_COLOR] = "TabLine",
76 [TAB_LINE_SEL_COLOR] = "TabLineSel",
77 [USER1_COLOR] = "User1",
78 [USER2_COLOR] = "User2",
79 [USER3_COLOR] = "User3",
80 [USER4_COLOR] = "User4",
81 [USER5_COLOR] = "User5",
82 [USER6_COLOR] = "User6",
83 [USER7_COLOR] = "User7",
84 [USER8_COLOR] = "User8",
85 [USER9_COLOR] = "User9",
86 [OTHER_WIN_COLOR] = "OtherWin",
87 [LINE_NUM_COLOR] = "LineNr",
88 [ODD_LINE_COLOR] = "OddLine",
89 };
90 ARRAY_GUARD(HI_GROUPS, MAXNUM_COLOR);
91
92 const char *HI_GROUPS_DESCR[] = {
93 [WIN_COLOR] = "base highlight of every window",
94 [DIRECTORY_COLOR] = "directories and links to directories",
95 [LINK_COLOR] = "symbolic links",
96 [BROKEN_LINK_COLOR] = "dangling symbolic links",
97 [HARD_LINK_COLOR] = "regular files with more than one hard link",
98 [SOCKET_COLOR] = "sockets",
99 [DEVICE_COLOR] = "device file",
100 [FIFO_COLOR] = "pipe",
101 [EXECUTABLE_COLOR] = "executable file",
102 [SELECTED_COLOR] = "selected item",
103 [CURR_LINE_COLOR] = "cursor in active pane",
104 [TOP_LINE_COLOR] = "base top line highlight",
105 [TOP_LINE_SEL_COLOR] = "active window title",
106 [STATUS_LINE_COLOR] = "status line",
107 [WILD_MENU_COLOR] = "completion box",
108 [CMD_LINE_COLOR] = "status/command-line bar",
109 [ERROR_MSG_COLOR] = "error message",
110 [BORDER_COLOR] = "horizontal borders",
111 [OTHER_LINE_COLOR] = "cursor in inactive pane",
112 [JOB_LINE_COLOR] = "job bar",
113 [SUGGEST_BOX_COLOR] = "suggestion box",
114 [MISMATCH_COLOR] = "mismatched diff entries",
115 [AUX_WIN_COLOR] = "auxiliary part of window",
116 [TAB_LINE_COLOR] = "tab line",
117 [TAB_LINE_SEL_COLOR] = "tip of selected tab",
118 [USER1_COLOR] = "user color #1",
119 [USER2_COLOR] = "user color #2",
120 [USER3_COLOR] = "user color #3",
121 [USER4_COLOR] = "user color #4",
122 [USER5_COLOR] = "user color #5",
123 [USER6_COLOR] = "user color #6",
124 [USER7_COLOR] = "user color #7",
125 [USER8_COLOR] = "user color #8",
126 [USER9_COLOR] = "user color #9",
127 [OTHER_WIN_COLOR] = "additional highlighting of inactive pane",
128 [LINE_NUM_COLOR] = "color of line number column in panes",
129 [ODD_LINE_COLOR] = "color of every second entry line in a pane",
130 };
131 ARRAY_GUARD(HI_GROUPS_DESCR, ARRAY_LEN(HI_GROUPS));
132
133 char *LIGHT_COLOR_NAMES[8] = {
134 [COLOR_BLACK] = "lightblack",
135 [COLOR_RED] = "lightred",
136 [COLOR_GREEN] = "lightgreen",
137 [COLOR_YELLOW] = "lightyellow",
138 [COLOR_BLUE] = "lightblue",
139 [COLOR_MAGENTA] = "lightmagenta",
140 [COLOR_CYAN] = "lightcyan",
141 [COLOR_WHITE] = "lightwhite",
142 };
143
144 char *XTERM256_COLOR_NAMES[256] = {
145 [COLOR_BLACK] = "black",
146 [COLOR_RED] = "red",
147 [COLOR_GREEN] = "green",
148 [COLOR_YELLOW] = "yellow",
149 [COLOR_BLUE] = "blue",
150 [COLOR_MAGENTA] = "magenta",
151 [COLOR_CYAN] = "cyan",
152 [COLOR_WHITE] = "white",
153 [8] = "lightblack",
154 [9] = "lightred",
155 [10] = "lightgreen",
156 [11] = "lightyellow",
157 [12] = "lightblue",
158 [13] = "lightmagenta",
159 [14] = "lightcyan",
160 [15] = "lightwhite",
161 [16] = "Grey0",
162 [17] = "NavyBlue",
163 [18] = "DarkBlue",
164 [19] = "Blue3",
165 [20] = "Blue3_2",
166 [21] = "Blue1",
167 [22] = "DarkGreen",
168 [23] = "DeepSkyBlue4",
169 [24] = "DeepSkyBlue4_2",
170 [25] = "DeepSkyBlue4_3",
171 [26] = "DodgerBlue3",
172 [27] = "DodgerBlue2",
173 [28] = "Green4",
174 [29] = "SpringGreen4",
175 [30] = "Turquoise4",
176 [31] = "DeepSkyBlue3",
177 [32] = "DeepSkyBlue3_2",
178 [33] = "DodgerBlue1",
179 [34] = "Green3",
180 [35] = "SpringGreen3",
181 [36] = "DarkCyan",
182 [37] = "LightSeaGreen",
183 [38] = "DeepSkyBlue2",
184 [39] = "DeepSkyBlue1",
185 [40] = "Green3_2",
186 [41] = "SpringGreen3_2",
187 [42] = "SpringGreen2",
188 [43] = "Cyan3",
189 [44] = "DarkTurquoise",
190 [45] = "Turquoise2",
191 [46] = "Green1",
192 [47] = "SpringGreen2_2",
193 [48] = "SpringGreen1",
194 [49] = "MediumSpringGreen",
195 [50] = "Cyan2",
196 [51] = "Cyan1",
197 [52] = "DarkRed",
198 [53] = "DeepPink4",
199 [54] = "Purple4",
200 [55] = "Purple4_2",
201 [56] = "Purple3",
202 [57] = "BlueViolet",
203 [58] = "Orange4",
204 [59] = "Grey37",
205 [60] = "MediumPurple4",
206 [61] = "SlateBlue3",
207 [62] = "SlateBlue3_2",
208 [63] = "RoyalBlue1",
209 [64] = "Chartreuse4",
210 [65] = "DarkSeaGreen4",
211 [66] = "PaleTurquoise4",
212 [67] = "SteelBlue",
213 [68] = "SteelBlue3",
214 [69] = "CornflowerBlue",
215 [70] = "Chartreuse3",
216 [71] = "DarkSeaGreen4_2",
217 [72] = "CadetBlue",
218 [73] = "CadetBlue_2",
219 [74] = "SkyBlue3",
220 [75] = "SteelBlue1",
221 [76] = "Chartreuse3_2",
222 [77] = "PaleGreen3",
223 [78] = "SeaGreen3",
224 [79] = "Aquamarine3",
225 [80] = "MediumTurquoise",
226 [81] = "SteelBlue1_2",
227 [82] = "Chartreuse2",
228 [83] = "SeaGreen2",
229 [84] = "SeaGreen1",
230 [85] = "SeaGreen1_2",
231 [86] = "Aquamarine1",
232 [87] = "DarkSlateGray2",
233 [88] = "DarkRed_2",
234 [89] = "DeepPink4_2",
235 [90] = "DarkMagenta",
236 [91] = "DarkMagenta_2",
237 [92] = "DarkViolet",
238 [93] = "Purple",
239 [94] = "Orange4_2",
240 [95] = "LightPink4",
241 [96] = "Plum4",
242 [97] = "MediumPurple3",
243 [98] = "MediumPurple3_2",
244 [99] = "SlateBlue1",
245 [100] = "Yellow4",
246 [101] = "Wheat4",
247 [102] = "Grey53",
248 [103] = "LightSlateGrey",
249 [104] = "MediumPurple",
250 [105] = "LightSlateBlue",
251 [106] = "Yellow4_2",
252 [107] = "DarkOliveGreen3",
253 [108] = "DarkSeaGreen",
254 [109] = "LightSkyBlue3",
255 [110] = "LightSkyBlue3_2",
256 [111] = "SkyBlue2",
257 [112] = "Chartreuse2_2",
258 [113] = "DarkOliveGreen3_2",
259 [114] = "PaleGreen3_2",
260 [115] = "DarkSeaGreen3",
261 [116] = "DarkSlateGray3",
262 [117] = "SkyBlue1",
263 [118] = "Chartreuse1",
264 [119] = "LightGreen_2",
265 [120] = "LightGreen_3",
266 [121] = "PaleGreen1",
267 [122] = "Aquamarine1_2",
268 [123] = "DarkSlateGray1",
269 [124] = "Red3",
270 [125] = "DeepPink4_3",
271 [126] = "MediumVioletRed",
272 [127] = "Magenta3",
273 [128] = "DarkViolet_2",
274 [129] = "Purple_2",
275 [130] = "DarkOrange3",
276 [131] = "IndianRed",
277 [132] = "HotPink3",
278 [133] = "MediumOrchid3",
279 [134] = "MediumOrchid",
280 [135] = "MediumPurple2",
281 [136] = "DarkGoldenrod",
282 [137] = "LightSalmon3",
283 [138] = "RosyBrown",
284 [139] = "Grey63",
285 [140] = "MediumPurple2_2",
286 [141] = "MediumPurple1",
287 [142] = "Gold3",
288 [143] = "DarkKhaki",
289 [144] = "NavajoWhite3",
290 [145] = "Grey69",
291 [146] = "LightSteelBlue3",
292 [147] = "LightSteelBlue",
293 [148] = "Yellow3",
294 [149] = "DarkOliveGreen3_3",
295 [150] = "DarkSeaGreen3_2",
296 [151] = "DarkSeaGreen2",
297 [152] = "LightCyan3",
298 [153] = "LightSkyBlue1",
299 [154] = "GreenYellow",
300 [155] = "DarkOliveGreen2",
301 [156] = "PaleGreen1_2",
302 [157] = "DarkSeaGreen2_2",
303 [158] = "DarkSeaGreen1",
304 [159] = "PaleTurquoise1",
305 [160] = "Red3_2",
306 [161] = "DeepPink3",
307 [162] = "DeepPink3_2",
308 [163] = "Magenta3_2",
309 [164] = "Magenta3_3",
310 [165] = "Magenta2",
311 [166] = "DarkOrange3_2",
312 [167] = "IndianRed_2",
313 [168] = "HotPink3_2",
314 [169] = "HotPink2",
315 [170] = "Orchid",
316 [171] = "MediumOrchid1",
317 [172] = "Orange3",
318 [173] = "LightSalmon3_2",
319 [174] = "LightPink3",
320 [175] = "Pink3",
321 [176] = "Plum3",
322 [177] = "Violet",
323 [178] = "Gold3_2",
324 [179] = "LightGoldenrod3",
325 [180] = "Tan",
326 [181] = "MistyRose3",
327 [182] = "Thistle3",
328 [183] = "Plum2",
329 [184] = "Yellow3_2",
330 [185] = "Khaki3",
331 [186] = "LightGoldenrod2",
332 [187] = "LightYellow3",
333 [188] = "Grey84",
334 [189] = "LightSteelBlue1",
335 [190] = "Yellow2",
336 [191] = "DarkOliveGreen1",
337 [192] = "DarkOliveGreen1_2",
338 [193] = "DarkSeaGreen1_2",
339 [194] = "Honeydew2",
340 [195] = "LightCyan1",
341 [196] = "Red1",
342 [197] = "DeepPink2",
343 [198] = "DeepPink1",
344 [199] = "DeepPink1_2",
345 [200] = "Magenta2_2",
346 [201] = "Magenta1",
347 [202] = "OrangeRed1",
348 [203] = "IndianRed1",
349 [204] = "IndianRed1_2",
350 [205] = "HotPink",
351 [206] = "HotPink_2",
352 [207] = "MediumOrchid1_2",
353 [208] = "DarkOrange",
354 [209] = "Salmon1",
355 [210] = "LightCoral",
356 [211] = "PaleVioletRed1",
357 [212] = "Orchid2",
358 [213] = "Orchid1",
359 [214] = "Orange1",
360 [215] = "SandyBrown",
361 [216] = "LightSalmon1",
362 [217] = "LightPink1",
363 [218] = "Pink1",
364 [219] = "Plum1",
365 [220] = "Gold1",
366 [221] = "LightGoldenrod2_2",
367 [222] = "LightGoldenrod2_3",
368 [223] = "NavajoWhite1",
369 [224] = "MistyRose1",
370 [225] = "Thistle1",
371 [226] = "Yellow1",
372 [227] = "LightGoldenrod1",
373 [228] = "Khaki1",
374 [229] = "Wheat1",
375 [230] = "Cornsilk1",
376 [231] = "Grey100",
377 [232] = "Grey3",
378 [233] = "Grey7",
379 [234] = "Grey11",
380 [235] = "Grey15",
381 [236] = "Grey19",
382 [237] = "Grey23",
383 [238] = "Grey27",
384 [239] = "Grey30",
385 [240] = "Grey35",
386 [241] = "Grey39",
387 [242] = "Grey42",
388 [243] = "Grey46",
389 [244] = "Grey50",
390 [245] = "Grey54",
391 [246] = "Grey58",
392 [247] = "Grey62",
393 [248] = "Grey66",
394 [249] = "Grey70",
395 [250] = "Grey74",
396 [251] = "Grey78",
397 [252] = "Grey82",
398 [253] = "Grey85",
399 [254] = "Grey89",
400 [255] = "Grey93",
401 };
402
403 /* Default color scheme definition. */
404 static const col_attr_t default_cs[] = {
405 /* fg bg attr */
406 [WIN_COLOR] = { COLOR_WHITE, COLOR_BLACK, 0 },
407 [DIRECTORY_COLOR] = { COLOR_CYAN, -1, A_BOLD },
408 [LINK_COLOR] = { COLOR_YELLOW, -1, A_BOLD },
409 [BROKEN_LINK_COLOR] = { COLOR_RED, -1, A_BOLD },
410 [HARD_LINK_COLOR] = { COLOR_YELLOW, -1, 0 },
411 [SOCKET_COLOR] = { COLOR_MAGENTA, -1, A_BOLD },
412 [DEVICE_COLOR] = { COLOR_RED, -1, A_BOLD },
413 [FIFO_COLOR] = { COLOR_CYAN, -1, A_BOLD },
414 [EXECUTABLE_COLOR] = { COLOR_GREEN, -1, A_BOLD },
415 [SELECTED_COLOR] = { COLOR_MAGENTA, -1, A_BOLD },
416 [CURR_LINE_COLOR] = { -1, -1, A_BOLD | A_REVERSE },
417 [TOP_LINE_COLOR] = { COLOR_BLACK, COLOR_WHITE, 0 },
418 [TOP_LINE_SEL_COLOR] = { COLOR_BLACK, -1, A_BOLD },
419 [STATUS_LINE_COLOR] = { COLOR_BLACK, COLOR_WHITE, A_BOLD },
420 [WILD_MENU_COLOR] = { COLOR_WHITE, COLOR_BLACK, A_UNDERLINE | A_REVERSE },
421 [CMD_LINE_COLOR] = { COLOR_WHITE, COLOR_BLACK, 0 },
422 [ERROR_MSG_COLOR] = { COLOR_RED, COLOR_BLACK, 0 },
423 [BORDER_COLOR] = { COLOR_BLACK, COLOR_WHITE, 0 },
424 [OTHER_LINE_COLOR] = { -1, -1, -1 },
425 [JOB_LINE_COLOR] = { COLOR_BLACK, COLOR_WHITE, A_BOLD | A_REVERSE },
426 [SUGGEST_BOX_COLOR] = { -1, -1, A_BOLD },
427 [MISMATCH_COLOR] = { COLOR_WHITE, COLOR_RED, A_BOLD },
428 [AUX_WIN_COLOR] = { -1, -1, -1 },
429 [TAB_LINE_COLOR] = { COLOR_WHITE, COLOR_BLACK, 0 },
430 [TAB_LINE_SEL_COLOR] = { -1, -1, A_BOLD | A_REVERSE },
431 [USER1_COLOR] = { -1, -1, -1 },
432 [USER2_COLOR] = { -1, -1, -1 },
433 [USER3_COLOR] = { -1, -1, -1 },
434 [USER4_COLOR] = { -1, -1, -1 },
435 [USER5_COLOR] = { -1, -1, -1 },
436 [USER6_COLOR] = { -1, -1, -1 },
437 [USER7_COLOR] = { -1, -1, -1 },
438 [USER8_COLOR] = { -1, -1, -1 },
439 [USER9_COLOR] = { -1, -1, -1 },
440 [OTHER_WIN_COLOR] = { -1, -1, -1 },
441 [LINE_NUM_COLOR] = { -1, -1, -1 },
442 [ODD_LINE_COLOR] = { -1, -1, -1 },
443 };
444 ARRAY_GUARD(default_cs, MAXNUM_COLOR);
445
446 static char ** list_cs_files(int *len);
447 static void restore_primary_cs(const col_scheme_t *cs);
448 static void reset_to_default_cs(col_scheme_t *cs);
449 static void free_cs_highlights(col_scheme_t *cs);
450 static file_hi_t * clone_cs_highlights(const col_scheme_t *from);
451 static void reset_cs_colors(col_scheme_t *cs);
452 static int source_cs(const char name[]);
453 static void get_cs_path(const char name[], char buf[], size_t buf_size);
454 static const char * get_global_colors_dir(void);
455 static void check_cs(col_scheme_t *cs);
456 static void load_color_pairs(col_scheme_t *cs);
457 static void ensure_dir_map_exists(void);
458
459 /* Mapping of color schemes associations onto file system tree. */
460 static fsddata_t *dir_map;
461
462 int
cs_have_no_extensions(void)463 cs_have_no_extensions(void)
464 {
465 int len;
466 char **list = list_cs_files(&len);
467 int i;
468
469 /* Check if extensions are in use. */
470 for(i = 0; i < len; ++i)
471 {
472 if(ends_with(list[i], ".vifm"))
473 {
474 break;
475 }
476 }
477
478 free_string_array(list, len);
479 return (len > 0 && i >= len);
480 }
481
482 void
cs_rename_all(void)483 cs_rename_all(void)
484 {
485 DIR *dir;
486 struct dirent *d;
487
488 dir = os_opendir(cfg.colors_dir);
489 if(dir == NULL)
490 {
491 return;
492 }
493
494 while((d = os_readdir(dir)) != NULL)
495 {
496 #ifndef _WIN32
497 unsigned char type;
498 #endif
499 char full_old_path[PATH_MAX + 32 + NAME_MAX];
500 char full_new_path[PATH_MAX + 32 + NAME_MAX];
501 snprintf(full_old_path, sizeof(full_old_path), "%s/%s", cfg.colors_dir,
502 d->d_name);
503 snprintf(full_new_path, sizeof(full_new_path), "%s/%s.vifm", cfg.colors_dir,
504 d->d_name);
505
506 #ifndef _WIN32
507 type = get_dirent_type(d, full_old_path);
508 if(type == DT_REG || (type == DT_UNKNOWN && is_regular_file(full_old_path)))
509 #else
510 if(is_regular_file(full_old_path))
511 #endif
512 {
513 (void)os_rename(full_old_path, full_new_path);
514 }
515 }
516 os_closedir(dir);
517 }
518
519 char **
cs_list(int * len)520 cs_list(int *len)
521 {
522 char **const list = list_cs_files(len);
523 int i, j;
524 int new_names;
525
526 /* Check if extensions are in use. */
527 new_names = 0;
528 for(i = 0; i < *len; ++i)
529 {
530 if(ends_with(list[i], ".vifm"))
531 {
532 new_names = 1;
533 }
534 }
535
536 /* Remove hidden files, maybe along with old style file names, maybe cut off
537 * extensions from new style file names. */
538 j = 0;
539 for(i = 0; i < *len; ++i)
540 {
541 if(list[i][0] == '.' || (new_names && !cut_suffix(list[i], ".vifm")))
542 {
543 free(list[i]);
544 }
545 else
546 {
547 list[j++] = list[i];
548 }
549 }
550 *len = j;
551
552 return list;
553 }
554
555 /* Lists names of all files in color schemes directories. Allocates an array of
556 * strings, which should be freed by the caller. Always sets *len. Returns
557 * NULL on error. */
558 static char **
list_cs_files(int * len)559 list_cs_files(int *len)
560 {
561 char **list = NULL;
562 *len = 0;
563
564 list = list_regular_files(cfg.colors_dir, list, len);
565 list = list_regular_files(get_global_colors_dir(), list, len);
566
567 return list;
568 }
569
570 int
cs_exists(const char name[])571 cs_exists(const char name[])
572 {
573 char cs_path[PATH_MAX + 32];
574 get_cs_path(name, cs_path, sizeof(cs_path));
575 return is_regular_file(cs_path);
576 }
577
578 void
cs_write(void)579 cs_write(void)
580 {
581 FILE *fp;
582 char def_cs_path[PATH_MAX + 32];
583 size_t i;
584
585 if(create_path(cfg.colors_dir, S_IRWXU) != 0)
586 {
587 /* Do nothing if local colors directory exists or we've failed to create
588 * it. */
589 return;
590 }
591
592 snprintf(def_cs_path, sizeof(def_cs_path), "%s/Default.vifm", cfg.colors_dir);
593 fp = os_fopen(def_cs_path, "w");
594 if(fp == NULL)
595 {
596 return;
597 }
598
599 fprintf(fp, "\" You can edit this file by hand.\n");
600 fprintf(fp, "\" The \" character at the beginning of a line comments out the line.\n");
601 fprintf(fp, "\" Blank lines are ignored.\n\n");
602
603 fprintf(fp, "\" The Default color scheme is used for any directory that does not have\n");
604 fprintf(fp, "\" a specified scheme and for parts of user interface like menus. A\n");
605 fprintf(fp, "\" color scheme set for a base directory will also\n");
606 fprintf(fp, "\" be used for the sub directories.\n\n");
607
608 fprintf(fp, "\" The standard ncurses colors are:\n");
609 fprintf(fp, "\" Default = -1 = None, can be used for transparency or default color\n");
610 fprintf(fp, "\" Black = 0\n");
611 fprintf(fp, "\" Red = 1\n");
612 fprintf(fp, "\" Green = 2\n");
613 fprintf(fp, "\" Yellow = 3\n");
614 fprintf(fp, "\" Blue = 4\n");
615 fprintf(fp, "\" Magenta = 5\n");
616 fprintf(fp, "\" Cyan = 6\n");
617 fprintf(fp, "\" White = 7\n\n");
618
619 fprintf(fp, "\" Light versions of colors are also available (set bold attribute):\n");
620 fprintf(fp, "\" LightBlack\n");
621 fprintf(fp, "\" LightRed\n");
622 fprintf(fp, "\" LightGreen\n");
623 fprintf(fp, "\" LightYellow\n");
624 fprintf(fp, "\" LightBlue\n");
625 fprintf(fp, "\" LightMagenta\n");
626 fprintf(fp, "\" LightCyan\n");
627 fprintf(fp, "\" LightWhite\n\n");
628
629 fprintf(fp, "\" Available attributes (some of them can be combined):\n");
630 fprintf(fp, "\" bold\n");
631 fprintf(fp, "\" underline\n");
632 fprintf(fp, "\" reverse or inverse\n");
633 fprintf(fp, "\" standout\n");
634 fprintf(fp, "\" italic (on unsupported systems becomes reverse)\n");
635 fprintf(fp, "\" none\n\n");
636
637 fprintf(fp, "\" Vifm supports 256 colors you can use color numbers 0-255\n");
638 fprintf(fp, "\" (requires properly set up terminal: set your TERM environment variable\n");
639 fprintf(fp, "\" (directly or using resources) to some color terminal name (e.g.\n");
640 fprintf(fp, "\" xterm-256color) from /usr/lib/terminfo/; you can check current number\n");
641 fprintf(fp, "\" of colors in your terminal with tput colors command)\n\n");
642
643 fprintf(fp, "\" highlight group cterm=attrs ctermfg=foreground_color ctermbg=background_color\n\n");
644
645 fprintf(fp, "highlight clear\n\n");
646
647 for(i = 0U; i < ARRAY_LEN(default_cs); ++i)
648 {
649 char fg_buf[16], bg_buf[16];
650 cs_color_to_str(default_cs[i].fg, sizeof(fg_buf), fg_buf);
651 cs_color_to_str(default_cs[i].bg, sizeof(bg_buf), bg_buf);
652
653 if(default_cs[i].attr == -1)
654 {
655 fprintf(fp, "highlight %s ctermfg=%s ctermbg=%s\n", HI_GROUPS[i], fg_buf,
656 bg_buf);
657 }
658 else
659 {
660 fprintf(fp, "highlight %s cterm=%s ctermfg=%s ctermbg=%s\n", HI_GROUPS[i],
661 cs_attrs_to_str(default_cs[i].attr), fg_buf, bg_buf);
662 }
663 }
664
665 fclose(fp);
666 }
667
668 void
cs_color_to_str(int color,size_t buf_len,char str_buf[])669 cs_color_to_str(int color, size_t buf_len, char str_buf[])
670 {
671 if(color == -1)
672 {
673 copy_str(str_buf, buf_len, "default");
674 }
675 else if(color >= 0 && color < (int)ARRAY_LEN(XTERM256_COLOR_NAMES))
676 {
677 copy_str(str_buf, buf_len, XTERM256_COLOR_NAMES[color]);
678 }
679 else
680 {
681 snprintf(str_buf, buf_len, "%d", color);
682 }
683 }
684
685 void
cs_load_primary(const char name[])686 cs_load_primary(const char name[])
687 {
688 col_scheme_t prev_cs = {};
689
690 if(!cs_exists(name))
691 {
692 show_error_msgf("Color Scheme", "No such color scheme: \"%s\"", name);
693 return;
694 }
695
696 cs_assign(&prev_cs, &cfg.cs);
697 curr_stats.cs = &cfg.cs;
698 cfg.cs.state = CSS_LOADING;
699
700 if(source_cs(name) != 0)
701 {
702 restore_primary_cs(&prev_cs);
703 show_error_msgf("Color Scheme Sourcing",
704 "An error occurred on sourcing color scheme: \"%s\"", name);
705 return;
706 }
707
708 check_cs(&cfg.cs);
709 if(cfg.cs.state == CSS_DEFAULTED)
710 {
711 restore_primary_cs(&prev_cs);
712 show_error_msgf("Color Scheme Error",
713 "\"%s\" color scheme is not supported by the terminal, restored \"%s\"",
714 name, prev_cs.name);
715 return;
716 }
717
718 copy_str(cfg.cs.name, sizeof(cfg.cs.name), name);
719 update_attributes();
720
721 free_cs_highlights(&prev_cs);
722 cfg.cs.state = CSS_NORMAL;
723 }
724
725 void
cs_load_primary_list(char * names[],int count)726 cs_load_primary_list(char *names[], int count)
727 {
728 col_scheme_t prev_cs = {};
729 cs_assign(&prev_cs, &cfg.cs);
730
731 while(count-- > 0)
732 {
733 const char *name = *names++;
734
735 cs_assign(&cfg.cs, &prev_cs);
736 curr_stats.cs = &cfg.cs;
737 cfg.cs.state = CSS_LOADING;
738
739 if(!cs_exists(name))
740 {
741 continue;
742 }
743
744 if(source_cs(name) != 0)
745 {
746 show_error_msgf("Color Scheme Sourcing",
747 "An error occurred on sourcing color scheme: \"%s\"", name);
748 continue;
749 }
750
751 check_cs(&cfg.cs);
752 if(cfg.cs.state == CSS_DEFAULTED)
753 {
754 continue;
755 }
756
757 copy_str(cfg.cs.name, sizeof(cfg.cs.name), name);
758 update_attributes();
759
760 free_cs_highlights(&prev_cs);
761 cfg.cs.state = CSS_NORMAL;
762
763 return;
764 }
765
766 restore_primary_cs(&prev_cs);
767 }
768
769 /* Restore previous state of primary color scheme. */
770 static void
restore_primary_cs(const col_scheme_t * cs)771 restore_primary_cs(const col_scheme_t *cs)
772 {
773 free_cs_highlights(&cfg.cs);
774 cfg.cs = *cs;
775 cs_load_pairs();
776 update_screen(UT_FULL);
777 }
778
779 void
cs_load_pairs(void)780 cs_load_pairs(void)
781 {
782 ensure_dir_map_exists();
783
784 load_color_pairs(&cfg.cs);
785 load_color_pairs(&lwin.cs);
786 load_color_pairs(&rwin.cs);
787 }
788
789 void
cs_load_defaults(void)790 cs_load_defaults(void)
791 {
792 fsddata_free(dir_map);
793 dir_map = NULL;
794
795 lwin.local_cs = 0;
796 rwin.local_cs = 0;
797
798 reset_to_default_cs(&cfg.cs);
799 reset_to_default_cs(&lwin.cs);
800 reset_to_default_cs(&rwin.cs);
801
802 load_color_pairs(&cfg.cs);
803 load_color_pairs(&lwin.cs);
804 load_color_pairs(&rwin.cs);
805 }
806
807 /* Completely resets the cs to builtin default color scheme. Changes: colors,
808 * name, state. */
809 static void
reset_to_default_cs(col_scheme_t * cs)810 reset_to_default_cs(col_scheme_t *cs)
811 {
812 reset_cs_colors(cs);
813
814 copy_str(cs->name, sizeof(cs->name), DEF_CS_NAME);
815
816 cs->state = CSS_NORMAL;
817 }
818
819 void
cs_reset(col_scheme_t * cs)820 cs_reset(col_scheme_t *cs)
821 {
822 reset_cs_colors(cs);
823 free_cs_highlights(cs);
824 load_color_pairs(cs);
825 }
826
827 void
cs_assign(col_scheme_t * to,const col_scheme_t * from)828 cs_assign(col_scheme_t *to, const col_scheme_t *from)
829 {
830 free_cs_highlights(to);
831 *to = *from;
832 to->file_hi = clone_cs_highlights(from);
833 }
834
835 /* Resets color scheme to default builtin values. */
836 static void
reset_cs_colors(col_scheme_t * cs)837 reset_cs_colors(col_scheme_t *cs)
838 {
839 size_t i;
840 for(i = 0U; i < ARRAY_LEN(default_cs); ++i)
841 {
842 cs->color[i] = default_cs[i];
843 }
844 }
845
846 /* Frees data structures of the color scheme that are related to filename
847 * specific highlight. */
848 static void
free_cs_highlights(col_scheme_t * cs)849 free_cs_highlights(col_scheme_t *cs)
850 {
851 int i;
852
853 for(i = 0; i < cs->file_hi_count; ++i)
854 {
855 matchers_free(cs->file_hi[i].matchers);
856 }
857
858 free(cs->file_hi);
859
860 cs->file_hi = NULL;
861 cs->file_hi_count = 0;
862 }
863
864 /* Clones filename specific highlight array of the *from color scheme and
865 * returns it. */
866 static file_hi_t *
clone_cs_highlights(const col_scheme_t * from)867 clone_cs_highlights(const col_scheme_t *from)
868 {
869 int i;
870 file_hi_t *const file_hi = reallocarray(NULL, from->file_hi_count + 1,
871 sizeof(*file_hi));
872
873 for(i = 0; i < from->file_hi_count; ++i)
874 {
875 const file_hi_t *const hi = &from->file_hi[i];
876 file_hi[i].matchers = matchers_clone(hi->matchers);
877 file_hi[i].hi = hi->hi;
878 }
879
880 return file_hi;
881 }
882
883 int
cs_load_local(int left,const char dir[])884 cs_load_local(int left, const char dir[])
885 {
886 if(dir_map == NULL || curr_stats.cs != &cfg.cs)
887 {
888 /* Either have nothing to do, or we're already doing it (sourcing a file can
889 * trigger view redraws). */
890 return 0;
891 }
892
893 curr_stats.cs = left ? &lwin.cs : &rwin.cs;
894 cs_assign(curr_stats.cs, &cfg.cs);
895
896 /* TODO: maybe use split_and_get() here as in io/iop:iop_mkdir(). */
897 char *const dir_copy = strdup(dir);
898 char *p = dir_copy;
899 int altered = 0;
900 char t;
901 do
902 {
903 void *name;
904
905 t = *p;
906 *p = '\0';
907
908 if(fsddata_get(dir_map, dir_copy, &name) == 0 && cs_exists(name))
909 {
910 (void)source_cs(name);
911 altered = 1;
912 }
913
914 *p = t;
915 p = (t == '\0' ? NULL : until_first(p + 1, '/'));
916 }
917 while(t != '\0');
918 free(dir_copy);
919
920 curr_stats.cs = &cfg.cs;
921
922 if(!altered)
923 {
924 return 0;
925 }
926
927 check_cs(curr_stats.cs);
928 load_color_pairs(curr_stats.cs);
929
930 return 1;
931 }
932
933 /* Sources color scheme file. Returns non-zero on error (e.g. file doesn't
934 * exist), and zero otherwise. */
935 static int
source_cs(const char name[])936 source_cs(const char name[])
937 {
938 char cs_path[PATH_MAX + 1];
939 get_cs_path(name, cs_path, sizeof(cs_path));
940 return cfg_source_file(cs_path);
941 }
942
943 /* Fill the buffer of specified size with path to color scheme, which might not
944 * exist. */
945 static void
get_cs_path(const char name[],char buf[],size_t buf_size)946 get_cs_path(const char name[], char buf[], size_t buf_size)
947 {
948 snprintf(buf, buf_size, "%s/%s.vifm", cfg.colors_dir, name);
949 if(is_regular_file(buf))
950 {
951 return;
952 }
953
954 (void)cut_suffix(buf, ".vifm");
955 if(is_regular_file(buf))
956 {
957 return;
958 }
959
960 snprintf(buf, buf_size, "%s/%s.vifm", get_global_colors_dir(),
961 name);
962 if(is_regular_file(buf))
963 {
964 return;
965 }
966
967 (void)cut_suffix(buf, ".vifm");
968 }
969
970 /* Retrieves path to global directory containing color schemes. Returns the
971 * path. */
972 static const char *
get_global_colors_dir(void)973 get_global_colors_dir(void)
974 {
975 #ifndef _WIN32
976 return GLOBAL_COLORS_DIR;
977 #else
978 static char dir_path[PATH_MAX + 1];
979 if(dir_path[0] == '\0')
980 {
981 snprintf(dir_path, sizeof(dir_path), "%s/colors", get_installed_data_dir());
982 }
983 return dir_path;
984 #endif
985 }
986
987 /* Checks whether colorscheme is in unusable state and resets it to normal
988 * state. */
989 static void
check_cs(col_scheme_t * cs)990 check_cs(col_scheme_t *cs)
991 {
992 if(cs->state == CSS_BROKEN)
993 {
994 reset_cs_colors(cs);
995 cs->state = CSS_DEFAULTED;
996 }
997 }
998
999 /* Loads color scheme settings into color pairs. */
1000 static void
load_color_pairs(col_scheme_t * cs)1001 load_color_pairs(col_scheme_t *cs)
1002 {
1003 int i;
1004 for(i = 0; i < MAXNUM_COLOR; ++i)
1005 {
1006 cs->pair[i] = colmgr_get_pair(cs->color[i].fg, cs->color[i].bg);
1007 }
1008 }
1009
1010 void
cs_complete(const char name[])1011 cs_complete(const char name[])
1012 {
1013 int i;
1014 size_t len;
1015 int schemes_len;
1016 char **schemes;
1017
1018 len = strlen(name);
1019 schemes = cs_list(&schemes_len);
1020
1021 for(i = 0; i < schemes_len; ++i)
1022 {
1023 if(schemes[i][0] != '.' || name[0] == '.')
1024 {
1025 if(strnoscmp(name, schemes[i], len) == 0)
1026 {
1027 vle_compl_add_match(schemes[i], "");
1028 }
1029 }
1030 }
1031
1032 free_string_array(schemes, schemes_len);
1033
1034 vle_compl_finish_group();
1035 vle_compl_add_last_match(name);
1036 }
1037
1038 const char *
cs_attrs_to_str(int attrs)1039 cs_attrs_to_str(int attrs)
1040 {
1041 static char result[64];
1042
1043 if(attrs == 0 || attrs == -1)
1044 {
1045 return strcpy(result, "none");
1046 }
1047
1048 result[0] = '\0';
1049 if((attrs & A_BOLD) == A_BOLD)
1050 strcat(result, "bold,");
1051 if((attrs & A_UNDERLINE) == A_UNDERLINE)
1052 strcat(result, "underline,");
1053 if((attrs & A_REVERSE) == A_REVERSE)
1054 strcat(result, "reverse,");
1055 if((attrs & A_STANDOUT) == A_STANDOUT)
1056 strcat(result, "standout,");
1057 #ifdef HAVE_A_ITALIC_DECL
1058 if((attrs & A_ITALIC) == A_ITALIC)
1059 strcat(result, "italic,");
1060 #endif
1061 if(result[0] != '\0')
1062 result[strlen(result) - 1] = '\0';
1063 return result;
1064 }
1065
1066 void
cs_assoc_dir(const char name[],const char dir[])1067 cs_assoc_dir(const char name[], const char dir[])
1068 {
1069 char *const copy = strdup(name);
1070
1071 ensure_dir_map_exists();
1072
1073 if(fsddata_set(dir_map, dir, copy) != 0)
1074 {
1075 free(copy);
1076 }
1077 }
1078
1079 /* Makes sure that dir_map variable is initialized. */
1080 static void
ensure_dir_map_exists(void)1081 ensure_dir_map_exists(void)
1082 {
1083 if(dir_map == NULL)
1084 {
1085 dir_map = fsddata_create(1, 1);
1086 }
1087 }
1088
1089 void
cs_mix_colors(col_attr_t * base,const col_attr_t * mixup)1090 cs_mix_colors(col_attr_t *base, const col_attr_t *mixup)
1091 {
1092 if(mixup->fg != -1)
1093 {
1094 base->fg = mixup->fg;
1095 }
1096
1097 if(mixup->bg != -1)
1098 {
1099 base->bg = mixup->bg;
1100 }
1101
1102 if(mixup->attr != -1)
1103 {
1104 base->attr = mixup->attr;
1105 }
1106 }
1107
1108 void
cs_add_file_hi(struct matchers_t * matchers,const col_attr_t * hi)1109 cs_add_file_hi(struct matchers_t *matchers, const col_attr_t *hi)
1110 {
1111 void *p;
1112 file_hi_t *file_hi;
1113 col_scheme_t *const cs = curr_stats.cs;
1114
1115 /* Try to find existing record that exactly matches the expression and update
1116 * the record. */
1117 int i;
1118 const char *expr = matchers_get_expr(matchers);
1119 for(i = 0; i < cs->file_hi_count; ++i)
1120 {
1121 if(strcmp(matchers_get_expr(cs->file_hi[i].matchers), expr) == 0)
1122 {
1123 matchers_free(matchers);
1124 cs->file_hi[i].hi = *hi;
1125 return;
1126 }
1127 }
1128
1129 p = reallocarray(cs->file_hi, cs->file_hi_count + 1, sizeof(*cs->file_hi));
1130 if(p == NULL)
1131 {
1132 matchers_free(matchers);
1133 show_error_msg("Color Scheme File Highlight", "Not enough memory");
1134 return;
1135 }
1136 cs->file_hi = p;
1137
1138 file_hi = &cs->file_hi[cs->file_hi_count];
1139
1140 file_hi->matchers = matchers;
1141 file_hi->hi = *hi;
1142
1143 ++cs->file_hi_count;
1144 }
1145
1146 const col_attr_t *
cs_get_file_hi(const col_scheme_t * cs,const char fname[],int * hi_hint)1147 cs_get_file_hi(const col_scheme_t *cs, const char fname[], int *hi_hint)
1148 {
1149 if(*hi_hint == INT_MAX)
1150 {
1151 return NULL;
1152 }
1153 if(*hi_hint != -1)
1154 {
1155 assert(*hi_hint >= 0 && "Wrong index.");
1156 assert(*hi_hint < cs->file_hi_count && "Wrong index.");
1157 return &cs->file_hi[*hi_hint].hi;
1158 }
1159
1160 int i;
1161 for(i = 0; i < cs->file_hi_count; ++i)
1162 {
1163 const file_hi_t *const file_hi = &cs->file_hi[i];
1164 if(matchers_match(file_hi->matchers, fname))
1165 {
1166 *hi_hint = i;
1167 return &file_hi->hi;
1168 }
1169 }
1170
1171 *hi_hint = INT_MAX;
1172 return NULL;
1173 }
1174
1175 int
cs_del_file_hi(const char matchers_expr[])1176 cs_del_file_hi(const char matchers_expr[])
1177 {
1178 col_scheme_t *const cs = curr_stats.cs;
1179
1180 int i;
1181 for(i = 0; i < cs->file_hi_count; ++i)
1182 {
1183 if(strcmp(matchers_get_expr(cs->file_hi[i].matchers), matchers_expr) == 0)
1184 {
1185 matchers_free(cs->file_hi[i].matchers);
1186 memmove(&cs->file_hi[i], &cs->file_hi[i + 1],
1187 sizeof(*cs->file_hi)*((cs->file_hi_count - 1) - i));
1188 --cs->file_hi_count;
1189 return 1;
1190 }
1191 }
1192
1193 return 0;
1194 }
1195
1196 int
cs_is_color_set(const col_attr_t * color)1197 cs_is_color_set(const col_attr_t *color)
1198 {
1199 return color->fg != -1 || color->bg != -1 || color->attr != -1;
1200 }
1201
1202 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
1203 /* vim: set cinoptions+=t0 filetype=c : */
1204