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