/* Copyright 2006 Garrett Rooney. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "etl_resolver.h" #include #include struct etl_resolver_t { etl_resolver_resolve_func_t resolve; etl_resolver_destroy_func_t destroy; void *baton; }; etl_error_t * etl_resolver_resolve(etl_stream_t **stream, etl_resolver_t *resolver, const char *fname) { return resolver->resolve(stream, resolver->baton, fname); } etl_resolver_t * etl_resolver_create(etl_resolver_resolve_func_t resolve_func, etl_resolver_destroy_func_t destroy_func, void *resolve_baton) { etl_resolver_t *resolver = calloc(1, sizeof(*resolver)); resolver->resolve = resolve_func; resolver->destroy = destroy_func; resolver->baton = resolve_baton; return resolver; } void etl_resolver_destroy(etl_resolver_t *resolver) { if (! resolver) return; resolver->destroy(resolver->baton); free(resolver); } static etl_error_t * file_resolver_func(etl_stream_t **stream, void *baton, const char *fname) { etl_array_t *search_paths = baton; int idx; for (idx = 0; idx < etl_array_nelts(search_paths); ++idx) { etl_error_t *err; const char *dir; char *path; dir = etl_array_idx(search_paths, idx); path = calloc(1, strlen(fname) + 1 + 1); strcat(path, "/"); strcat(path, fname); /* First, resolve all the //, /./ and /../ junk in the filename. */ { char *tmp; while ((tmp = strstr(path, "//")) != NULL) memmove(tmp, tmp + 1, strlen(tmp + 1) + 1); while ((tmp = strstr(path, "/./")) != NULL) memmove(tmp, tmp + 2, strlen(tmp + 2) + 1); while ((tmp = strstr(path, "/../")) != NULL) { char *tmp1; *tmp = '\0'; if ((tmp1 = strrchr(path, '/')) == NULL) return etl_error_create( ETL_EINVAL, "Attempt to include above root of include path" ); memmove(tmp1, tmp + 3, strlen(tmp + 3) + 1); } } /* Now that we know it doesn't go over the top, stick the dir on the * front so we can use it. */ { char *path2 = calloc(1, strlen(dir) + strlen(path) + 1); strcat(path2, dir); strcat(path2, path); free(path); path = path2; } err = etl_stream_file_open(stream, path, etl_stream_open_read); free(path); if (err) { etl_error_clear(err); continue; } return ETL_SUCCESS; } return etl_error_createf(ETL_ENOENT, "Couldn't resolve file '%s'", fname); } static void destroy_strings(void *str, void *baton) { free(str); } static void file_destroy_func(void *baton) { etl_array_destroy(baton, destroy_strings, NULL); } etl_resolver_t * etl_resolver_file_create(etl_array_t *search_paths) { return etl_resolver_create(file_resolver_func, file_destroy_func, search_paths); }