1 /*
2     roxterm - VTE/GTK terminal emulator with tabs
3     Copyright (C) 2004-2015 Tony Houghton <h@realh.co.uk>
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 
20 
21 #include "dynopts.h"
22 #include "optsfile.h"
23 
24 #include <string.h>
25 
26 struct DynamicOptions {
27     char *family;
28     GHashTable *profiles;
29 };
30 
31 
dynamic_options_get(const char * family)32 DynamicOptions *dynamic_options_get(const char *family)
33 {
34     static GHashTable *all_dynopts = NULL;
35     DynamicOptions *dynopts;
36 
37     if (!all_dynopts)
38         all_dynopts = g_hash_table_new(g_str_hash, g_str_equal);
39 
40     dynopts = g_hash_table_lookup(all_dynopts, family);
41     if (!dynopts)
42     {
43         dynopts = g_new(DynamicOptions, 1);
44         dynopts->family = g_strdup(family);
45         dynopts->profiles = g_hash_table_new(g_str_hash, g_str_equal);
46         g_hash_table_insert(all_dynopts, (gpointer) family, dynopts);
47     }
48     return dynopts;
49 }
50 
dynamic_options_lookup(DynamicOptions * dynopts,const char * profile_name)51 Options *dynamic_options_lookup(DynamicOptions * dynopts,
52     const char *profile_name)
53 {
54     return g_hash_table_lookup(dynopts->profiles, profile_name);
55 }
56 
dynamic_options_lookup_and_ref(DynamicOptions * dynopts,const char * profile_name,const char * group_name)57 Options *dynamic_options_lookup_and_ref(DynamicOptions * dynopts,
58     const char *profile_name, const char *group_name)
59 {
60     Options *options = g_hash_table_lookup(dynopts->profiles, profile_name);
61 
62     if (!options)
63     {
64         char *leafname = g_build_filename(dynopts->family, profile_name,
65             NULL);
66 
67         options = options_open(leafname, group_name);
68         g_hash_table_insert(dynopts->profiles, g_strdup(profile_name), options);
69     }
70     else
71     {
72         options_ref(options);
73     }
74     return options;
75 }
76 
77 void
dynamic_options_forget(DynamicOptions * dynopts,const char * profile_name)78 dynamic_options_forget(DynamicOptions *dynopts, const char *profile_name)
79 {
80     g_hash_table_remove(dynopts->profiles, profile_name);
81 }
82 
83 gboolean
dynamic_options_unref(DynamicOptions * dynopts,const char * profile_name)84 dynamic_options_unref(DynamicOptions * dynopts, const char *profile_name)
85 {
86     /* Use generic pointers for these to avoid breaking strict aliasing (see
87      * man gcc) */
88     gpointer options;
89     gpointer key;
90     gboolean lookup_ok = g_hash_table_lookup_extended(dynopts->profiles,
91         profile_name, &key, &options);
92 
93     if (!lookup_ok)
94         return FALSE;
95 
96     /* Have to check ref and remove from hash first in case options_unref
97      * frees profile_name */
98     if (((Options *) options)->ref == 1)
99     {
100         g_hash_table_remove(dynopts->profiles, profile_name);
101         g_free(key);
102     }
103     return options_unref(options);
104 }
105 
dynopts_add_path_contents_to_list(GList * list,const char * path,const char * family,gboolean sorted)106 static GList *dynopts_add_path_contents_to_list(GList *list, const char *path,
107                 const char *family, gboolean sorted)
108 {
109     GError *err = NULL;
110     char *dirname = g_build_filename(path, family, NULL);
111 
112     if (g_file_test(dirname, G_FILE_TEST_IS_DIR))
113     {
114         GDir *dir = g_dir_open(dirname, 0, &err);
115 
116         if (!dir || err)
117         {
118             g_warning("%s", err->message);
119             g_error_free(err);
120         }
121         else
122         {
123             const char *filename;
124 
125             while ((filename = g_dir_read_name(dir)) != NULL)
126             {
127                 GList *link;
128 
129                 for (link = list; link; link = g_list_next(link))
130                 {
131                     if (!strcmp(link->data, filename))
132                         break;
133                 }
134                 if (!link)
135                 {
136                     char *pathname = g_build_filename(dirname, filename, NULL);
137 
138                     if (!g_file_test(pathname, G_FILE_TEST_IS_DIR))
139                         list = g_list_append(list, g_strdup(filename));
140                     g_free(pathname);
141                 }
142                 /* else duplicate */
143             }
144             g_dir_close(dir);
145         }
146     }
147     g_free(dirname);
148     if (sorted)
149     {
150         list = g_list_sort(list, (GCompareFunc) dynamic_options_strcmp);
151     }
152     return list;
153 }
154 
dynamic_options_list_full(DynamicOptions * dynopts,gboolean sorted)155 char **dynamic_options_list_full(DynamicOptions *dynopts, gboolean sorted)
156 {
157     int i;
158     const char * const *paths = options_file_get_pathv();
159     char **strv;
160     guint nmemb;
161     guint n;
162     GList *list = g_list_append(NULL, g_strdup("Default"));
163     GList *link;
164 
165     for (i = 0; paths[i]; ++i)
166     {
167         list = dynopts_add_path_contents_to_list(list, paths[i],
168                 dynopts->family, sorted);
169     }
170 
171     nmemb = g_list_length(list);
172     if (!nmemb)
173         return NULL;
174     strv = g_new(char *, nmemb + 1);
175     for (link = list, n = 0; link && n < nmemb; link = g_list_next(link), ++n)
176         strv[n] = link->data;
177     strv[n] = NULL;
178     g_list_free(list);
179 
180     return strv;
181 }
182 
dynamic_options_rename(DynamicOptions * dynopts,const char * old_name,const char * new_name)183 void dynamic_options_rename(DynamicOptions *dynopts,
184         const char *old_name, const char *new_name)
185 {
186     Options *opts = dynamic_options_lookup(dynopts, old_name);
187 
188     g_return_if_fail(opts);
189     dynamic_options_forget(dynopts, old_name);
190     g_hash_table_insert(dynopts->profiles, g_strdup(new_name), opts);
191 }
192 
dynamic_options_strcmp(const char * s1,const char * s2)193 int dynamic_options_strcmp(const char *s1, const char *s2)
194 {
195     char *u1, *u2;
196     int result;
197 
198     if (!g_strcmp0(s1, "Default"))
199         return g_strcmp0(s2, "Default") ? -1 : 0;
200     else if (!g_strcmp0(s2, "Default"))
201         return 1;
202     u1 = s1 ? g_utf8_casefold(s1, -1) : g_strdup("");
203     u2 = s2 ? g_utf8_casefold(s2, -1) : g_strdup("");
204     result = g_utf8_collate(u1, u2);
205     g_free(u2);
206     g_free(u1);
207     return result;
208 }
209 
210 /* vi:set sw=4 ts=4 noet cindent cino= */
211