1 /*
2 * vim:ts=4:sw=4:expandtab
3 *
4 * Copyright © 2016 Ingo Bürk
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 * Except as contained in this notice, the names of the authors or their
24 * institutions shall not be used in advertising or otherwise to promote the
25 * sale, use or other dealings in this Software without prior written
26 * authorization from the authors.
27 *
28 */
29 #include "externals.h"
30
31 #include "resource.h"
32 #include "database.h"
33 #include "match.h"
34 #include "util.h"
35
36 /* Forward declarations */
37 static int __resource_get(xcb_xrm_database_t *database, const char *res_name, const char *res_class,
38 xcb_xrm_resource_t **_resource);
39 static void __resource_free(xcb_xrm_resource_t *resource);
40
41 /*
42 * Find the string value of a resource.
43 *
44 * Note that the string is owned by the caller and must be free'd.
45 *
46 * @param database The database to query.
47 * @param res_name The fully qualified resource name string.
48 * @param res_class The fully qualified resource class string. This argument
49 * may be left empty / NULL, but if given, it must contain the same number of
50 * components as res_name.
51 * @param out Out parameter to which the value will be written.
52 * @returns 0 if the resource was found, a negative error code otherwise.
53 */
xcb_xrm_resource_get_string(xcb_xrm_database_t * database,const char * res_name,const char * res_class,char ** out)54 int xcb_xrm_resource_get_string(xcb_xrm_database_t *database,
55 const char *res_name, const char *res_class, char **out) {
56 xcb_xrm_resource_t *resource;
57 if (__resource_get(database, res_name, res_class, &resource) < 0) {
58 __resource_free(resource);
59 *out = NULL;
60 return -1;
61 }
62
63 assert(resource->value != NULL);
64 *out = strdup(resource->value);
65 __resource_free(resource);
66
67 return 0;
68 }
69
70 /*
71 * Find the long value of a resource.
72 *
73 * @param database The database to query.
74 * @param res_name The fully qualified resource name string.
75 * @param res_class The fully qualified resource class string. This argument
76 * may be left empty / NULL, but if given, it must contain the same number of
77 * components as res_name.
78 * @param out Out parameter to which the converted value will be written.
79 * @returns 0 if the resource was found and converted, -1 if the resource was
80 * found but could not be converted and -2 if the resource was not found.
81 */
xcb_xrm_resource_get_long(xcb_xrm_database_t * database,const char * res_name,const char * res_class,long * out)82 int xcb_xrm_resource_get_long(xcb_xrm_database_t *database,
83 const char *res_name, const char *res_class, long *out) {
84 char *value;
85 if (xcb_xrm_resource_get_string(database, res_name, res_class, &value) < 0 || value == NULL) {
86 *out = LONG_MIN;
87 return -2;
88 }
89
90 if (str2long(out, value, 10) < 0) {
91 *out = LONG_MIN;
92 FREE(value);
93 return -1;
94 }
95
96 FREE(value);
97 return 0;
98 }
99
100 /*
101 * Find the bool value of a resource.
102 *
103 * The conversion to a bool is done by applying the following steps in order:
104 * - If the value can be converted to a long, return the truthiness of the
105 * converted number.
106 * - If the value is one of "true", "on" or "yes" (case-insensitive), return
107 * true.
108 * - If the value is one of "false", "off" or "no" (case-insensitive), return
109 * false.
110 *
111 * @param database The database to query.
112 * @param res_name The fully qualified resource name string.
113 * @param res_class The fully qualified resource class string. This argument
114 * may be left empty / NULL, but if given, it must contain the same number of
115 * components as res_name.
116 * @param out Out parameter to which the converted value will be written.
117 * @returns 0 if the resource was found and converted, -1 if the resource was
118 * found but could not be converted and -2 if the resource was not found.
119 */
xcb_xrm_resource_get_bool(xcb_xrm_database_t * database,const char * res_name,const char * res_class,bool * out)120 int xcb_xrm_resource_get_bool(xcb_xrm_database_t *database,
121 const char *res_name, const char *res_class, bool *out) {
122 char *value;
123 long converted;
124
125 if (xcb_xrm_resource_get_string(database, res_name, res_class, &value) < 0 || value == NULL) {
126 *out = false;
127 return -2;
128 }
129
130 /* Let's first see if the value can be parsed into an integer directly. */
131 if (str2long(&converted, value, 10) == 0) {
132 FREE(value);
133 *out = converted;
134 return 0;
135 }
136
137 /* Next up, we take care of signal words. */
138 if (strcasecmp(value, "true") == 0 ||
139 strcasecmp(value, "on") == 0 ||
140 strcasecmp(value, "yes") == 0) {
141 FREE(value);
142 *out = true;
143 return 0;
144 }
145
146 if (strcasecmp(value, "false") == 0 ||
147 strcasecmp(value, "off") == 0 ||
148 strcasecmp(value, "no") == 0) {
149 FREE(value);
150 *out = false;
151 return 0;
152 }
153
154 FREE(value);
155 *out = false;
156 return -1;
157 }
158
__resource_get(xcb_xrm_database_t * database,const char * res_name,const char * res_class,xcb_xrm_resource_t ** _resource)159 static int __resource_get(xcb_xrm_database_t *database, const char *res_name, const char *res_class,
160 xcb_xrm_resource_t **_resource) {
161 xcb_xrm_resource_t *resource;
162 xcb_xrm_entry_t *query_name = NULL;
163 xcb_xrm_entry_t *query_class = NULL;
164 int result = SUCCESS;
165
166 if (database == NULL || TAILQ_EMPTY(database)) {
167 *_resource = NULL;
168 return -FAILURE;
169 }
170
171 *_resource = calloc(1, sizeof(struct xcb_xrm_resource_t));
172 if (_resource == NULL) {
173 result = -FAILURE;
174 goto done;
175 }
176 resource = *_resource;
177
178 if (res_name == NULL || xcb_xrm_entry_parse(res_name, &query_name, true) < 0) {
179 result = -FAILURE;
180 goto done;
181 }
182
183 /* For the resource class input, we allow NULL and empty string as
184 * placeholders for not specifying this string. Technically this is
185 * violating the spec, but it seems to be widely used. */
186 if (res_class != NULL && strlen(res_class) > 0 &&
187 xcb_xrm_entry_parse(res_class, &query_class, true) < 0) {
188 result = -1;
189 goto done;
190 }
191
192 /* We rely on name and class query strings to have the same number of
193 * components, so let's check that this is the case. The specification
194 * backs us up here. */
195 if (query_class != NULL &&
196 __xcb_xrm_entry_num_components(query_name) != __xcb_xrm_entry_num_components(query_class)) {
197 result = -1;
198 goto done;
199 }
200
201 result = __xcb_xrm_match(database, query_name, query_class, resource);
202 done:
203 xcb_xrm_entry_free(query_name);
204 xcb_xrm_entry_free(query_class);
205 return result;
206 }
207
__resource_free(xcb_xrm_resource_t * resource)208 static void __resource_free(xcb_xrm_resource_t *resource) {
209 if (resource == NULL)
210 return;
211
212 FREE(resource->value);
213 FREE(resource);
214 }
215