1 /*
2 FSearch - A fast file search utility
3 Copyright © 2016 Christian Boxdörfer
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, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <glib.h>
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "fsearch_config.h"
26 #include "fsearch_limits.h"
27 #include "debug.h"
28
29 const char *config_file_name = "fsearch.conf";
30 const char *config_folder_name = "fsearch";
31
32 void
config_build_dir(char * path,size_t len)33 config_build_dir (char *path, size_t len)
34 {
35 g_assert (path != NULL);
36 g_assert (len >= 0);
37
38 const gchar *xdg_conf_dir = g_get_user_config_dir ();
39 snprintf (path, len, "%s/%s", xdg_conf_dir, config_folder_name);
40 return;
41 }
42
43 static void
config_build_path(char * path,size_t len)44 config_build_path (char *path, size_t len)
45 {
46 g_assert (path != NULL);
47 g_assert (len >= 0);
48
49 const gchar *xdg_conf_dir = g_get_user_config_dir ();
50 snprintf (path, len, "%s/%s/%s", xdg_conf_dir, config_folder_name, config_file_name);
51 return;
52 }
53
54 bool
config_make_dir(void)55 config_make_dir (void)
56 {
57 gchar config_dir[PATH_MAX] = "";
58 config_build_dir (config_dir, sizeof (config_dir));
59 return !g_mkdir_with_parents (config_dir, 0700);
60 }
61
62 static void
config_load_handle_error(GError * error)63 config_load_handle_error (GError *error)
64 {
65 if (!error) {
66 return;
67 }
68 switch (error->code) {
69 case G_KEY_FILE_ERROR_INVALID_VALUE:
70 fprintf(stderr, "load_config: invalid value: %s\n", error->message);
71 break;
72 case G_KEY_FILE_ERROR_KEY_NOT_FOUND:
73 case G_KEY_FILE_ERROR_GROUP_NOT_FOUND:
74 // new config, use default value and don't report anything
75 break;
76 default:
77 fprintf(stderr, "load_config: unknown error: %s\n", error->message);
78 }
79 g_error_free (error);
80 }
81
82 static uint32_t
config_load_integer(GKeyFile * key_file,const char * group_name,const char * key,uint32_t default_value)83 config_load_integer (GKeyFile *key_file,
84 const char *group_name,
85 const char *key,
86 uint32_t default_value)
87 {
88 GError *error = NULL;
89 uint32_t result = g_key_file_get_integer (key_file, group_name, key, &error);
90 if (error != NULL) {
91 result = default_value;
92 config_load_handle_error (error);
93 }
94 return result;
95 }
96
97 static bool
config_load_boolean(GKeyFile * key_file,const char * group_name,const char * key,bool default_value)98 config_load_boolean (GKeyFile *key_file,
99 const char *group_name,
100 const char *key,
101 bool default_value)
102 {
103 GError *error = NULL;
104 bool result = g_key_file_get_boolean (key_file, group_name, key, &error);
105 if (error != NULL) {
106 result = default_value;
107 config_load_handle_error (error);
108 }
109 return result;
110 }
111
112 static char *
config_load_string(GKeyFile * key_file,const char * group_name,const char * key,char * default_value)113 config_load_string (GKeyFile *key_file,
114 const char *group_name,
115 const char *key,
116 char *default_value)
117 {
118 GError *error = NULL;
119 char *result = g_key_file_get_string (key_file, group_name, key, &error);
120 if (error != NULL) {
121 result = default_value;
122 config_load_handle_error (error);
123 }
124 return result;
125 }
126
127 bool
config_load(FsearchConfig * config)128 config_load (FsearchConfig *config)
129 {
130 g_assert (config != NULL);
131
132 bool result = false;
133 GKeyFile *key_file = g_key_file_new ();
134 g_assert (key_file != NULL);
135
136 gchar config_path[PATH_MAX] = "";
137 config_build_path (config_path, sizeof (config_path));
138
139 GError *error = NULL;
140 if (g_key_file_load_from_file (key_file, config_path, G_KEY_FILE_NONE, &error)) {
141 trace ("loaded config file\n");
142 // Interface
143 config->restore_column_config = config_load_boolean (key_file,
144 "Interface",
145 "restore_column_configuration",
146 false);
147 config->enable_list_tooltips = config_load_boolean (key_file,
148 "Interface",
149 "enable_list_tooltips",
150 true);
151 config->enable_dark_theme = config_load_boolean (key_file,
152 "Interface",
153 "enable_dark_theme",
154 false);
155 config->show_menubar = config_load_boolean (key_file,
156 "Interface",
157 "show_menubar",
158 true);
159 config->show_statusbar = config_load_boolean (key_file,
160 "Interface",
161 "show_statusbar",
162 true);
163 config->show_filter = config_load_boolean (key_file,
164 "Interface",
165 "show_filter",
166 true);
167 config->show_search_button = config_load_boolean (key_file,
168 "Interface",
169 "show_search_button",
170 true);
171 config->show_base_2_units = config_load_boolean (key_file,
172 "Interface",
173 "show_base_2_units",
174 false);
175 config->action_after_file_open = config_load_integer(key_file,
176 "Interface",
177 "action_after_file_open",
178 0);
179 config->action_after_file_open_keyboard = config_load_boolean (key_file,
180 "Interface",
181 "action_after_file_open_keyboard",
182 false);
183 config->action_after_file_open_mouse = config_load_boolean (key_file,
184 "Interface",
185 "action_after_file_open_mouse",
186 false);
187
188 // Warning Dialogs
189 config->show_dialog_failed_opening = config_load_boolean(key_file,
190 "Dialogs",
191 "show_dialog_failed_opening",
192 true);
193
194 // Applications
195 config->folder_open_cmd = config_load_string (key_file,
196 "Applications",
197 "folder_open_cmd",
198 NULL);
199
200 // Window
201 config->restore_window_size = config_load_boolean (key_file,
202 "Interface",
203 "restore_window_size",
204 false);
205 config->window_width = config_load_integer (key_file,
206 "Interface",
207 "window_width",
208 800);
209 config->window_height = config_load_integer (key_file,
210 "Interface",
211 "window_height",
212 600);
213
214 // Columns
215 config->show_listview_icons = config_load_boolean (key_file,
216 "Interface",
217 "show_listview_icons",
218 true);
219 config->show_path_column = config_load_boolean (key_file,
220 "Interface",
221 "show_path_column",
222 true);
223 config->show_type_column = config_load_boolean (key_file,
224 "Interface",
225 "show_type_column",
226 true);
227 config->show_size_column = config_load_boolean (key_file,
228 "Interface",
229 "show_size_column",
230 true);
231 config->show_modified_column = config_load_boolean (key_file,
232 "Interface",
233 "show_modified_column",
234 true);
235
236 // Column Size
237 config->name_column_width = config_load_integer (key_file,
238 "Interface",
239 "name_column_width",
240 250);
241 config->path_column_width = config_load_integer (key_file,
242 "Interface",
243 "path_column_width",
244 250);
245 config->type_column_width = config_load_integer (key_file,
246 "Interface",
247 "type_column_width",
248 100);
249 config->size_column_width = config_load_integer (key_file,
250 "Interface",
251 "size_column_width",
252 75);
253 config->modified_column_width = config_load_integer (key_file,
254 "Interface",
255 "modified_column_width",
256 75);
257
258 // Column position
259 config->name_column_pos = config_load_integer (key_file,
260 "Interface",
261 "name_column_pos",
262 0);
263 config->path_column_pos = config_load_integer (key_file,
264 "Interface",
265 "path_column_pos",
266 1);
267 config->type_column_pos = config_load_integer (key_file,
268 "Interface",
269 "type_column_pos",
270 2);
271 config->size_column_pos = config_load_integer (key_file,
272 "Interface",
273 "size_column_pos",
274 3);
275 config->modified_column_pos = config_load_integer (key_file,
276 "Interface",
277 "modified_column_pos",
278 4);
279
280 // Search
281 config->search_as_you_type = config_load_boolean (key_file,
282 "Search",
283 "search_as_you_type",
284 true);
285 config->auto_search_in_path = config_load_boolean (key_file,
286 "Search",
287 "auto_search_in_path",
288 true);
289 config->match_case = config_load_boolean (key_file,
290 "Search",
291 "match_case",
292 false);
293 config->enable_regex = config_load_boolean (key_file,
294 "Search",
295 "enable_regex",
296 false);
297 config->search_in_path = config_load_boolean (key_file,
298 "Search",
299 "search_in_path",
300 false);
301 config->hide_results_on_empty_search = config_load_boolean (key_file,
302 "Search",
303 "hide_results_on_empty_search",
304 true);
305 config->limit_results = config_load_boolean (key_file,
306 "Search",
307 "limit_results",
308 false);
309 config->num_results = config_load_integer (key_file,
310 "Search",
311 "num_results",
312 1000);
313
314 // Database
315 config->update_database_on_launch = config_load_boolean (key_file,
316 "Database",
317 "update_database_on_launch",
318 false);
319 config->exclude_hidden_items = config_load_boolean (key_file,
320 "Database",
321 "exclude_hidden_files_and_folders",
322 false);
323 config->follow_symlinks = config_load_boolean (key_file,
324 "Database",
325 "follow_symbolic_links",
326 false);
327
328 char *exclude_files_str = config_load_string (key_file, "Database", "exclude_files", NULL);
329 if (exclude_files_str) {
330 config->exclude_files = g_strsplit (exclude_files_str, ";", -1);
331 free (exclude_files_str);
332 exclude_files_str = NULL;
333 }
334
335 // Locations
336 uint32_t pos = 1;
337 while (true) {
338 char key[100] = "";
339 snprintf (key, sizeof (key), "location_%d", pos++);
340 char *value = config_load_string (key_file, "Database", key, NULL);
341 if (value) {
342 config->locations = g_list_append (config->locations, value);
343 }
344 else {
345 break;
346 }
347 }
348 // Exclude
349 pos = 1;
350 while (true) {
351 char key[100] = "";
352 snprintf (key, sizeof (key), "exclude_location_%d", pos++);
353 char *value = config_load_string (key_file, "Database", key, NULL);
354 if (value) {
355 config->exclude_locations = g_list_append (config->exclude_locations, value);
356 }
357 else {
358 break;
359 }
360 }
361
362 result = true;
363 }
364 else {
365 fprintf(stderr, "load config failed: %s\n", error->message);
366 g_error_free (error);
367 }
368
369 g_key_file_free (key_file);
370 return result;
371 }
372
373 bool
config_load_default(FsearchConfig * config)374 config_load_default (FsearchConfig *config)
375 {
376 g_assert (config != NULL);
377
378 // Search
379 config->auto_search_in_path = true;
380 config->search_as_you_type = true;
381 config->match_case = false;
382 config->enable_regex = false;
383 config->search_in_path = false;
384 config->hide_results_on_empty_search = true;
385 config->limit_results = true;
386 config->num_results = 1000;
387
388 // Interface
389 config->enable_dark_theme = false;
390 config->enable_list_tooltips = true;
391 config->restore_column_config = false;
392 config->show_menubar = true;
393 config->show_statusbar = true;
394 config->show_filter = true;
395 config->show_search_button = true;
396 config->show_base_2_units = false;
397 config->action_after_file_open = 0;
398 config->action_after_file_open_keyboard = false;
399 config->action_after_file_open_mouse = false;
400
401 // Columns
402 config->show_listview_icons = true;
403 config->show_path_column = true;
404 config->show_type_column = true;
405 config->show_size_column = true;
406 config->show_modified_column = true;
407
408 config->name_column_pos = 0;
409 config->path_column_pos = 1;
410 config->type_column_pos = 2;
411 config->size_column_pos = 3;
412 config->modified_column_pos = 4;
413
414 config->name_column_width = 250;
415 config->path_column_width = 250;
416 config->type_column_width = 100;
417 config->size_column_width = 75;
418 config->modified_column_width = 125;
419
420 // Warning Dialogs
421 config->show_dialog_failed_opening = true;
422
423 // Window
424 config->restore_window_size = false;
425 config->window_width = 800;
426 config->window_height = 600;
427
428 // Database
429 config->update_database_on_launch = false;
430 config->exclude_hidden_items = false;
431 config->follow_symlinks = false;
432
433 // Locations
434 config->locations = NULL;
435 config->exclude_locations = NULL;
436
437 return true;
438 }
439
440 bool
config_save(FsearchConfig * config)441 config_save (FsearchConfig *config)
442 {
443 g_assert (config != NULL);
444
445 bool result = false;
446 GKeyFile *key_file = g_key_file_new ();
447 g_assert (key_file != NULL);
448
449 // Interface
450 g_key_file_set_boolean (key_file, "Interface", "restore_column_configuration", config->restore_column_config);
451 g_key_file_set_boolean (key_file, "Interface", "enable_list_tooltips", config->enable_list_tooltips);
452 g_key_file_set_boolean (key_file, "Interface", "enable_dark_theme", config->enable_dark_theme);
453 g_key_file_set_boolean (key_file, "Interface", "show_menubar", config->show_menubar);
454 g_key_file_set_boolean (key_file, "Interface", "show_statusbar", config->show_statusbar);
455 g_key_file_set_boolean (key_file, "Interface", "show_filter", config->show_filter);
456 g_key_file_set_boolean (key_file, "Interface", "show_search_button", config->show_search_button);
457 g_key_file_set_boolean (key_file, "Interface", "show_base_2_units", config->show_base_2_units);
458 g_key_file_set_integer (key_file, "Interface", "action_after_file_open", config->action_after_file_open);
459 g_key_file_set_boolean (key_file, "Interface", "action_after_file_open_keyboard", config->action_after_file_open_keyboard);
460 g_key_file_set_boolean (key_file, "Interface", "action_after_file_open_mouse", config->action_after_file_open_mouse);
461
462 // Warning Dialogs
463 g_key_file_set_boolean (key_file, "Dialogs", "show_dialog_failed_opening", config->show_dialog_failed_opening);
464
465 // Window
466 g_key_file_set_boolean (key_file, "Interface", "restore_window_size", config->restore_window_size);
467 g_key_file_set_integer (key_file, "Interface", "window_width", config->window_width);
468 g_key_file_set_integer (key_file, "Interface", "window_height", config->window_height);
469
470 // Columns visibility
471 g_key_file_set_boolean (key_file, "Interface", "show_listview_icons", config->show_listview_icons);
472 g_key_file_set_boolean (key_file, "Interface", "show_path_column", config->show_path_column);
473 g_key_file_set_boolean (key_file, "Interface", "show_type_column", config->show_type_column);
474 g_key_file_set_boolean (key_file, "Interface", "show_size_column", config->show_size_column);
475 g_key_file_set_boolean (key_file, "Interface", "show_modified_column", config->show_modified_column);
476
477 // Column width
478 g_key_file_set_integer (key_file, "Interface", "name_column_width", config->name_column_width);
479 g_key_file_set_integer (key_file, "Interface", "path_column_width", config->path_column_width);
480 g_key_file_set_integer (key_file, "Interface", "type_column_width", config->type_column_width);
481 g_key_file_set_integer (key_file, "Interface", "size_column_width", config->size_column_width);
482 g_key_file_set_integer (key_file, "Interface", "modified_column_width", config->modified_column_width);
483
484 // Column position
485 g_key_file_set_integer (key_file, "Interface", "name_column_pos", config->name_column_pos);
486 g_key_file_set_integer (key_file, "Interface", "path_column_pos", config->path_column_pos);
487 g_key_file_set_integer (key_file, "Interface", "type_column_pos", config->type_column_pos);
488 g_key_file_set_integer (key_file, "Interface", "size_column_pos", config->size_column_pos);
489 g_key_file_set_integer (key_file, "Interface", "modified_column_pos", config->modified_column_pos);
490
491 // Applications
492 if (config->folder_open_cmd) {
493 g_key_file_set_string (key_file, "Applications", "folder_open_cmd", config->folder_open_cmd);
494 }
495
496 // Search
497 g_key_file_set_boolean (key_file, "Search", "search_as_you_type", config->search_as_you_type);
498 g_key_file_set_boolean (key_file, "Search", "auto_search_in_path", config->auto_search_in_path);
499 g_key_file_set_boolean (key_file, "Search", "search_in_path", config->search_in_path);
500 g_key_file_set_boolean (key_file, "Search", "enable_regex", config->enable_regex);
501 g_key_file_set_boolean (key_file, "Search", "match_case", config->match_case);
502 g_key_file_set_boolean (key_file, "Search", "hide_results_on_empty_search", config->hide_results_on_empty_search);
503 g_key_file_set_boolean (key_file, "Search", "limit_results", config->limit_results);
504 g_key_file_set_integer (key_file, "Search", "num_results", config->num_results);
505
506 // Database
507 g_key_file_set_boolean (key_file, "Database", "update_database_on_launch", config->update_database_on_launch);
508 g_key_file_set_boolean (key_file, "Database", "exclude_hidden_files_and_folders", config->exclude_hidden_items);
509 g_key_file_set_boolean (key_file, "Database", "follow_symbolic_links", config->follow_symlinks);
510
511 if (config->locations) {
512 uint32_t pos = 1;
513 for (GList *l = config->locations; l != NULL; l = l->next) {
514 char location[100] = "";
515 snprintf (location, sizeof (location), "location_%d", pos);
516 g_key_file_set_string (key_file, "Database", location, l->data);
517 pos++;
518 }
519 }
520
521 if (config->exclude_locations) {
522 uint32_t pos = 1;
523 for (GList *l = config->exclude_locations; l != NULL; l = l->next) {
524 char location[100] = "";
525 snprintf (location, sizeof (location), "exclude_location_%d", pos);
526 g_key_file_set_string (key_file, "Database", location, l->data);
527 pos++;
528 }
529 }
530
531 if (config->exclude_files) {
532 char *exclude_files_str = g_strjoinv (";", config->exclude_files);
533 g_key_file_set_string (key_file, "Database", "exclude_files", exclude_files_str);
534 free (exclude_files_str);
535 }
536
537 gchar config_path[PATH_MAX] = "";
538 config_build_path (config_path, sizeof (config_path));
539
540 GError *error = NULL;
541 if (g_key_file_save_to_file (key_file, config_path, &error)) {
542 trace ("saved config file\n");
543 result = true;
544 }
545 else {
546 fprintf(stderr, "save config failed: %s\n", error->message);
547 }
548
549 g_key_file_free (key_file);
550 return result;
551 }
552
553 void
config_free(FsearchConfig * config)554 config_free (FsearchConfig *config)
555 {
556 g_assert (config != NULL);
557
558 if (config->folder_open_cmd) {
559 free (config->folder_open_cmd);
560 config->folder_open_cmd = NULL;
561 }
562 if (config->locations) {
563 g_list_free_full (config->locations, (GDestroyNotify)free);
564 config->locations = NULL;
565 }
566 if (config->exclude_locations) {
567 g_list_free_full (config->exclude_locations, (GDestroyNotify)free);
568 config->exclude_locations = NULL;
569 }
570 if (config->exclude_files) {
571 g_strfreev (config->exclude_files);
572 config->exclude_files = NULL;
573 }
574 free (config);
575 config = NULL;
576 }
577
578