X-Git-Url: https://sigrok.org/gitweb/?p=libsigrok.git;a=blobdiff_plain;f=src%2Fresource.c;h=a30b4b3f744ee0d154926038e6dfce67dcb029ba;hp=b29be60863b0eb83584d04bfe777f75b6a666d46;hb=2622b4297fd4cc4bed5c06bb6ae0aaa8b40e0ece;hpb=bee246665b05a59f91ff7d51040cbaee614ab0c7 diff --git a/src/resource.c b/src/resource.c index b29be608..a30b4b3f 100644 --- a/src/resource.c +++ b/src/resource.c @@ -35,13 +35,102 @@ * Access to resource files. */ +/** + * Get a list of paths where we look for resource (e.g. firmware) files. + * + * @param res_type The type of resource to get the search paths for. + * + * @return List of strings that must be freed after use, including the strings. + * + * @since 0.6.0 + */ +SR_API GSList *sr_resourcepaths_get(int res_type) +{ + const char *subdir = NULL; + GSList *l = NULL; + const char *env; + const char *const *datadirs; + + if (res_type == SR_RESOURCE_FIRMWARE) { + subdir = "sigrok-firmware"; + + env = g_getenv("SIGROK_FIRMWARE_DIR"); + if (env) + l = g_slist_append(l, g_strdup(env)); + } + + l = g_slist_append(l, g_build_filename(g_get_user_data_dir(), subdir, NULL)); + +#ifdef FIRMWARE_DIR + if (res_type == SR_RESOURCE_FIRMWARE) { + /* + * Scan the hard-coded directory before the system directories to + * avoid picking up possibly outdated files from a system install. + */ + l = g_slist_append(l, g_strdup(FIRMWARE_DIR)); + } +#endif + + datadirs = g_get_system_data_dirs(); + while (*datadirs) + l = g_slist_append(l, g_build_filename(*datadirs++, subdir, NULL)); + + return l; +} + +/** + * Retrieve the size of the open stream @a file. + * + * This function only works on seekable streams. However, the set of seekable + * streams is generally congruent with the set of streams that have a size. + * Code that needs to work with any type of stream (including pipes) should + * require neither seekability nor advance knowledge of the size. + * On failure, the return value is negative and errno is set. + * + * @param file An I/O stream opened in binary mode. + * @return The size of @a file in bytes, or a negative value on failure. + * + * @private + */ +SR_PRIV int64_t sr_file_get_size(FILE *file) +{ + off_t filepos, filesize; + + /* ftello() and fseeko() are not standard C, but part of POSIX.1-2001. + * Thus, if these functions are available at all, they can reasonably + * be expected to also conform to POSIX semantics. In particular, this + * means that ftello() after fseeko(..., SEEK_END) has a defined result + * and can be used to get the size of a seekable stream. + * On Windows, the result is fully defined only for binary streams. + */ + filepos = ftello(file); + if (filepos < 0) + return -1; + + if (fseeko(file, 0, SEEK_END) < 0) + return -1; + + filesize = ftello(file); + if (filesize < 0) + return -1; + + if (fseeko(file, filepos, SEEK_SET) < 0) + return -1; + + return filesize; +} + static FILE *try_open_file(const char *datadir, const char *subdir, const char *name) { char *filename; FILE *file; - filename = g_build_filename(datadir, subdir, name, NULL); + if (subdir) + filename = g_build_filename(datadir, subdir, name, NULL); + else + filename = g_build_filename(datadir, name, NULL); + file = g_fopen(filename, "rb"); if (file) @@ -57,38 +146,29 @@ static FILE *try_open_file(const char *datadir, const char *subdir, static int resource_open_default(struct sr_resource *res, const char *name, void *cb_data) { + GSList *paths, *p = NULL; int64_t filesize; - const char *builtindir, *subdir; - const char *const *datadirs; - FILE *file; + FILE *file = NULL; (void)cb_data; - switch (res->type) { - case SR_RESOURCE_FIRMWARE: - builtindir = FIRMWARE_DIR; - subdir = "sigrok-firmware"; - break; - default: + paths = sr_resourcepaths_get(res->type); + + /* Currently, the enum only defines SR_RESOURCE_FIRMWARE. */ + if (res->type != SR_RESOURCE_FIRMWARE) { sr_err("%s: unknown type %d.", __func__, res->type); return SR_ERR_ARG; } - file = try_open_file(g_get_user_data_dir(), subdir, name); - /* - * Scan the hard-coded directory before the system directories to - * avoid picking up possibly outdated files from a system install. - */ - if (!file) - file = try_open_file(builtindir, "", name); - - if (!file) { - datadirs = g_get_system_data_dirs(); - while (*datadirs && !file) - file = try_open_file(*datadirs++, subdir, name); + p = paths; + while (p && !file) { + file = try_open_file((const char *)(p->data), NULL, name); + p = p->next; } + g_slist_free_full(paths, g_free); + if (!file) { - sr_err("Failed to locate '%s'.", name); + sr_dbg("Failed to locate '%s'.", name); return SR_ERR; } @@ -126,7 +206,7 @@ static int resource_close_default(struct sr_resource *res, void *cb_data) return SR_OK; } -static ssize_t resource_read_default(const struct sr_resource *res, +static gssize resource_read_default(const struct sr_resource *res, void *buf, size_t count, void *cb_data) { FILE *file; @@ -139,7 +219,7 @@ static ssize_t resource_read_default(const struct sr_resource *res, sr_err("%s: invalid handle.", __func__); return SR_ERR_ARG; } - if (count > SSIZE_MAX) { + if (count > G_MAXSSIZE) { sr_err("%s: count %zu too large.", __func__, count); return SR_ERR_ARG; } @@ -177,15 +257,15 @@ SR_API int sr_resource_set_hooks(struct sr_context *ctx, return SR_ERR_ARG; } if (open_cb && close_cb && read_cb) { - ctx->resource_open_cb = open_cb; + ctx->resource_open_cb = open_cb; ctx->resource_close_cb = close_cb; - ctx->resource_read_cb = read_cb; - ctx->resource_cb_data = cb_data; + ctx->resource_read_cb = read_cb; + ctx->resource_cb_data = cb_data; } else if (!open_cb && !close_cb && !read_cb) { - ctx->resource_open_cb = &resource_open_default; + ctx->resource_open_cb = &resource_open_default; ctx->resource_close_cb = &resource_close_default; - ctx->resource_read_cb = &resource_read_default; - ctx->resource_cb_data = ctx; + ctx->resource_read_cb = &resource_read_default; + ctx->resource_cb_data = ctx; } else { sr_err("%s: inconsistent callback pointers.", __func__); return SR_ERR_ARG; @@ -219,7 +299,8 @@ SR_PRIV int sr_resource_open(struct sr_context *ctx, ret = (*ctx->resource_open_cb)(res, name, ctx->resource_cb_data); if (ret != SR_OK) - sr_err("Failed to open resource '%s'.", name); + sr_err("Failed to open resource '%s' (use loglevel 5/spew for" + " details).", name); return ret; } @@ -262,10 +343,10 @@ SR_PRIV int sr_resource_close(struct sr_context *ctx, struct sr_resource *res) * * @private */ -SR_PRIV ssize_t sr_resource_read(struct sr_context *ctx, +SR_PRIV gssize sr_resource_read(struct sr_context *ctx, const struct sr_resource *res, void *buf, size_t count) { - ssize_t n_read; + gssize n_read; n_read = (*ctx->resource_read_cb)(res, buf, count, ctx->resource_cb_data); @@ -285,7 +366,7 @@ SR_PRIV ssize_t sr_resource_read(struct sr_context *ctx, * @param max_size Size limit. Error out if the resource is larger than this. * * @return A buffer containing the resource data, or NULL on failure. Must - * be freed by the caller using g_free(). + * be freed by the caller using g_free(). * * @private */ @@ -294,7 +375,8 @@ SR_PRIV void *sr_resource_load(struct sr_context *ctx, { struct sr_resource res; void *buf; - ssize_t n_read; + size_t res_size; + gssize n_read; if (sr_resource_open(ctx, &res, type, name) != SR_OK) return NULL; @@ -305,17 +387,19 @@ SR_PRIV void *sr_resource_load(struct sr_context *ctx, sr_resource_close(ctx, &res); return NULL; } - buf = g_try_malloc(res.size); + res_size = res.size; + + buf = g_try_malloc(res_size); if (!buf) { sr_err("Failed to allocate buffer for '%s'.", name); sr_resource_close(ctx, &res); return NULL; } - n_read = sr_resource_read(ctx, &res, buf, res.size); + n_read = sr_resource_read(ctx, &res, buf, res_size); sr_resource_close(ctx, &res); - if (n_read < 0 || (size_t)n_read != res.size) { + if (n_read < 0 || (size_t)n_read != res_size) { if (n_read >= 0) sr_err("Failed to read '%s': premature end of file.", name); @@ -323,8 +407,6 @@ SR_PRIV void *sr_resource_load(struct sr_context *ctx, return NULL; } - *size = res.size; + *size = res_size; return buf; } - -/** @} */