]> sigrok.org Git - libsigrok.git/blob - src/resource.c
resource: New internal API for accessing resource files
[libsigrok.git] / src / resource.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2015 Daniel Elstner <daniel.kitta@gmail.com>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <glib.h>
24 #include <glib/gstdio.h>
25 #include <libsigrok/libsigrok.h>
26 #include "libsigrok-internal.h"
27
28 /** @cond PRIVATE */
29 #define LOG_PREFIX "resource"
30 /** @endcond */
31
32 /**
33  * @file
34  *
35  * Access to resource files.
36  */
37
38 static FILE *try_open_file(const char *datadir, const char *subdir,
39                 const char *name)
40 {
41         char *filename;
42         FILE *file;
43
44         filename = g_build_filename(datadir, subdir, name, NULL);
45         file = g_fopen(filename, "rb");
46
47         if (file)
48                 sr_info("Opened '%s'.", filename);
49         else
50                 sr_spew("Attempt to open '%s' failed: %s",
51                         filename, g_strerror(errno));
52         g_free(filename);
53
54         return file;
55 }
56
57 static int resource_open_default(struct sr_resource *res,
58                 const char *name, void *cb_data)
59 {
60         int64_t filesize;
61         const char *builtindir, *subdir;
62         const char *const *datadirs;
63         FILE *file;
64
65         (void)cb_data;
66
67         switch (res->type) {
68         case SR_RESOURCE_FIRMWARE:
69                 builtindir = FIRMWARE_DIR;
70                 subdir = "sigrok-firmware";
71                 break;
72         default:
73                 sr_err("%s: unknown type %d.", __func__, res->type);
74                 return SR_ERR_ARG;
75         }
76
77         file = try_open_file(g_get_user_data_dir(), subdir, name);
78         /*
79          * Scan the hard-coded directory before the system directories to
80          * avoid picking up possibly outdated files from a system install.
81          */
82         if (!file)
83                 file = try_open_file(builtindir, "", name);
84
85         if (!file) {
86                 datadirs = g_get_system_data_dirs();
87                 while (*datadirs && !file)
88                         file = try_open_file(*datadirs++, subdir, name);
89         }
90         if (!file) {
91                 sr_err("Failed to locate '%s'.", name);
92                 return SR_ERR;
93         }
94
95         filesize = sr_file_get_size(file);
96         if (filesize < 0) {
97                 sr_err("Failed to obtain size of '%s': %s",
98                         name, g_strerror(errno));
99                 fclose(file);
100                 return SR_ERR;
101         }
102         res->size = filesize;
103         res->handle = file;
104
105         return SR_OK;
106 }
107
108 static int resource_close_default(struct sr_resource *res, void *cb_data)
109 {
110         FILE *file;
111
112         (void)cb_data;
113
114         file = res->handle;
115         if (!file) {
116                 sr_err("%s: invalid handle.", __func__);
117                 return SR_ERR_ARG;
118         }
119
120         if (fclose(file) < 0) {
121                 sr_err("Failed to close file: %s", g_strerror(errno));
122                 return SR_ERR;
123         }
124         res->handle = NULL;
125
126         return SR_OK;
127 }
128
129 static ssize_t resource_read_default(const struct sr_resource *res,
130                 void *buf, size_t count, void *cb_data)
131 {
132         FILE *file;
133         size_t n_read;
134
135         (void)cb_data;
136
137         file = res->handle;
138         if (!file) {
139                 sr_err("%s: invalid handle.", __func__);
140                 return SR_ERR_ARG;
141         }
142         if (count > SSIZE_MAX) {
143                 sr_err("%s: count %zu too large.", __func__, count);
144                 return SR_ERR_ARG;
145         }
146
147         n_read = fread(buf, 1, count, file);
148
149         if (n_read != count && ferror(file)) {
150                 sr_err("Failed to read resource file: %s", g_strerror(errno));
151                 return SR_ERR;
152         }
153         return n_read;
154 }
155
156 /**
157  * Install resource access hooks.
158  *
159  * @param ctx libsigrok context. Must not be NULL.
160  * @param open_cb Resource open callback, or NULL to unset.
161  * @param close_cb Resource close callback, or NULL to unset.
162  * @param read_cb Resource read callback, or NULL to unset.
163  * @param cb_data User data pointer passed to callbacks.
164  *
165  * @retval SR_OK Success.
166  * @retval SR_ERR_ARG Invalid argument.
167  *
168  * @since 0.4.0
169  */
170 SR_API int sr_resource_set_hooks(struct sr_context *ctx,
171                 sr_resource_open_callback open_cb,
172                 sr_resource_close_callback close_cb,
173                 sr_resource_read_callback read_cb, void *cb_data)
174 {
175         if (!ctx) {
176                 sr_err("%s: ctx was NULL.", __func__);
177                 return SR_ERR_ARG;
178         }
179         if (open_cb && close_cb && read_cb) {
180                 ctx->resource_open_cb  = open_cb;
181                 ctx->resource_close_cb = close_cb;
182                 ctx->resource_read_cb  = read_cb;
183                 ctx->resource_cb_data  = cb_data;
184         } else if (!open_cb && !close_cb && !read_cb) {
185                 ctx->resource_open_cb  = &resource_open_default;
186                 ctx->resource_close_cb = &resource_close_default;
187                 ctx->resource_read_cb  = &resource_read_default;
188                 ctx->resource_cb_data  = ctx;
189         } else {
190                 sr_err("%s: inconsistent callback pointers.", __func__);
191                 return SR_ERR_ARG;
192         }
193         return SR_OK;
194 }
195
196 /**
197  * Open resource.
198  *
199  * @param ctx libsigrok context. Must not be NULL.
200  * @param[out] res Resource descriptor to fill in. Must not be NULL.
201  * @param type Resource type ID.
202  * @param name Name of the resource. Must not be NULL.
203  *
204  * @retval SR_OK Success.
205  * @retval SR_ERR_ARG Invalid argument.
206  * @retval SR_ERR Other error.
207  *
208  * @private
209  */
210 SR_PRIV int sr_resource_open(struct sr_context *ctx,
211                 struct sr_resource *res, int type, const char *name)
212 {
213         int ret;
214
215         res->size = 0;
216         res->handle = NULL;
217         res->type = type;
218
219         ret = (*ctx->resource_open_cb)(res, name, ctx->resource_cb_data);
220
221         if (ret != SR_OK)
222                 sr_err("Failed to open resource '%s'.", name);
223
224         return ret;
225 }
226
227 /**
228  * Close resource.
229  *
230  * @param ctx libsigrok context. Must not be NULL.
231  * @param[inout] res Resource descriptor. Must not be NULL.
232  *
233  * @retval SR_OK Success.
234  * @retval SR_ERR_ARG Invalid argument.
235  * @retval SR_ERR Other error.
236  *
237  * @private
238  */
239 SR_PRIV int sr_resource_close(struct sr_context *ctx, struct sr_resource *res)
240 {
241         int ret;
242
243         ret = (*ctx->resource_close_cb)(res, ctx->resource_cb_data);
244
245         if (ret != SR_OK)
246                 sr_err("Failed to close resource.");
247
248         return ret;
249 }
250
251 /**
252  * Read resource data.
253  *
254  * @param ctx libsigrok context. Must not be NULL.
255  * @param[in] res Resource descriptor. Must not be NULL.
256  * @param[out] buf Buffer to store @a count bytes into. Must not be NULL.
257  * @param count Number of bytes to read.
258  *
259  * @return The number of bytes actually read, or a negative value on error.
260  * @retval SR_ERR_ARG Invalid argument.
261  * @retval SR_ERR Other error.
262  *
263  * @private
264  */
265 SR_PRIV ssize_t sr_resource_read(struct sr_context *ctx,
266                 const struct sr_resource *res, void *buf, size_t count)
267 {
268         ssize_t n_read;
269
270         n_read = (*ctx->resource_read_cb)(res, buf, count,
271                         ctx->resource_cb_data);
272         if (n_read < 0)
273                 sr_err("Failed to read resource.");
274
275         return n_read;
276 }
277
278 /**
279  * Load a resource into memory.
280  *
281  * @param ctx libsigrok context. Must not be NULL.
282  * @param type Resource type ID.
283  * @param name Name of the resource. Must not be NULL.
284  * @param[out] size Size in bytes of the returned buffer. Must not be NULL.
285  * @param max_size Size limit. Error out if the resource is larger than this.
286  *
287  * @return A buffer containing the resource data, or NULL on failure. Must
288  *  be freed by the caller using g_free().
289  *
290  * @private
291  */
292 SR_PRIV void *sr_resource_load(struct sr_context *ctx,
293                 int type, const char *name, size_t *size, size_t max_size)
294 {
295         struct sr_resource res;
296         void *buf;
297         ssize_t n_read;
298
299         if (sr_resource_open(ctx, &res, type, name) != SR_OK)
300                 return NULL;
301
302         if (res.size > max_size) {
303                 sr_err("Size %" PRIu64 " of '%s' exceeds limit %zu.",
304                         res.size, name, max_size);
305                 sr_resource_close(ctx, &res);
306                 return NULL;
307         }
308         buf = g_try_malloc(res.size);
309         if (!buf) {
310                 sr_err("Failed to allocate buffer for '%s'.", name);
311                 sr_resource_close(ctx, &res);
312                 return NULL;
313         }
314
315         n_read = sr_resource_read(ctx, &res, buf, res.size);
316         sr_resource_close(ctx, &res);
317
318         if (n_read < 0 || (size_t)n_read != res.size) {
319                 if (n_read >= 0)
320                         sr_err("Failed to read '%s': premature end of file.",
321                                 name);
322                 g_free(buf);
323                 return NULL;
324         }
325
326         *size = res.size;
327         return buf;
328 }
329
330 /** @} */