diff --git a/Makefile.am b/Makefile.am index f626c0e4..b2e550c2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,8 +54,7 @@ HEADER_FILES = $(SOURCE_FILES:.c=.h) if ENABLE_LIBOCISPEC_INSTALL ocispec_includedir = $(includedir)/ocispec ocispec_include_HEADERS = $(HEADER_FILES) \ - src/ocispec/json_common.h \ - src/ocispec/read-file.h + src/ocispec/json_common.h endif src/runtime_spec_stamp: src/ocispec/json_common.h src/ocispec/json_common.c @@ -151,7 +150,6 @@ $(HEADER_FILES): %.h: %.c src/ocispec/generate.py BUILT_SOURCES = $(HEADER_FILES) $(SOURCE_FILES) libocispec_la_SOURCES = $(BUILT_SOURCES) \ - src/ocispec/read-file.c \ src/ocispec/json_common.c TMP_H_FILES = $(HEADER_FILES:.h=.h.tmp) @@ -272,7 +270,6 @@ EXTRA_DIST = autogen.sh \ src/ocispec/json_api.py \ src/ocispec/sources.py \ $(HEADER_FILES) \ - src/ocispec/read-file.h \ runtime-spec \ image-spec \ src/ocispec/json_common.h \ diff --git a/src/ocispec/json_api.py b/src/ocispec/json_api.py index df45bd6f..68eea482 100644 --- a/src/ocispec/json_api.py +++ b/src/ocispec/json_api.py @@ -150,6 +150,12 @@ def gen_free(gen): def doc_read(data, len_expr): return f"json_tokener_parse ({data})" +def doc_read_file(path): + return f"json_object_from_file ({path})" + +def doc_read_fd(fd_expr): + return f"json_object_from_fd ({fd_expr})" + def doc_get_root(doc): return doc diff --git a/src/ocispec/json_common.c b/src/ocispec/json_common.c index e92cb6ac..b61b07a7 100644 --- a/src/ocispec/json_common.c +++ b/src/ocispec/json_common.c @@ -1146,6 +1146,13 @@ gen_json_map_int_string (void *ctx, const json_map_int_string *map, const struct stat = json_gen_string (g, numstr, strlen (numstr)); if (json_gen_status_ok != stat) GEN_SET_ERROR_AND_RETURN (stat, err); + if (map->values[i] == NULL) + { + stat = json_gen_null (g); + if (json_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN (stat, err); + continue; + } stat = json_gen_string (g, map->values[i], strlen (map->values[i])); if (json_gen_status_ok != stat) GEN_SET_ERROR_AND_RETURN (stat, err); @@ -1304,6 +1311,8 @@ gen_json_map_string_int (void *ctx, const json_map_string_int *map, const struct GEN_SET_ERROR_AND_RETURN (stat, err); for (i = 0; i < len; i++) { + if (map->keys[i] == NULL) + continue; stat = json_gen_string (g, map->keys[i], strlen (map->keys[i])); if (json_gen_status_ok != stat) GEN_SET_ERROR_AND_RETURN (stat, err); @@ -1468,6 +1477,8 @@ gen_json_map_string_int64 (void *ctx, const json_map_string_int64 *map, const st for (i = 0; i < len; i++) { + if (map->keys[i] == NULL) + continue; stat = json_gen_string (g, map->keys[i], strlen (map->keys[i])); if (json_gen_status_ok != stat) GEN_SET_ERROR_AND_RETURN (stat, err); @@ -1607,6 +1618,8 @@ gen_json_map_string_bool (void *ctx, const json_map_string_bool *map, const stru GEN_SET_ERROR_AND_RETURN (stat, err); for (i = 0; i < len; i++) { + if (map->keys[i] == NULL) + continue; stat = json_gen_string (g, map->keys[i], strlen (map->keys[i])); if (json_gen_status_ok != stat) GEN_SET_ERROR_AND_RETURN (stat, err); @@ -1774,9 +1787,18 @@ gen_json_map_string_string (void *ctx, const json_map_string_string *map, const for (i = 0; i < len; i++) { + if (map->keys[i] == NULL) + continue; stat = json_gen_string (g, map->keys[i], strlen (map->keys[i])); if (json_gen_status_ok != stat) GEN_SET_ERROR_AND_RETURN (stat, err); + if (map->values[i] == NULL) + { + stat = json_gen_null (g); + if (json_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN (stat, err); + continue; + } stat = json_gen_string (g, map->values[i], strlen (map->values[i])); if (json_gen_status_ok != stat) GEN_SET_ERROR_AND_RETURN (stat, err); diff --git a/src/ocispec/read-file.c b/src/ocispec/read-file.c deleted file mode 100644 index 5f3c27c3..00000000 --- a/src/ocispec/read-file.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - Copyright 2017 Giuseppe Scrivano - - 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 - -#include "ocispec/read-file.h" - -#include -#include -#include -#include -#include -#include -#include - -char * -fread_file (FILE *stream, size_t *length) -{ - char *buf = NULL; - size_t alloc = BUFSIZ; - { - struct stat st; - - if (fstat (fileno (stream), &st) >= 0 && S_ISREG (st.st_mode)) - { - off_t pos = ftello (stream); - - if (pos >= 0 && pos < st.st_size) - { - off_t alloc_off = st.st_size - pos; - if (SIZE_MAX - 1 < (uintmax_t) (alloc_off)) - { - errno = ENOMEM; - return NULL; - } - - alloc = alloc_off + 1; - } - } - } - - if (! (buf = malloc (alloc))) - return NULL; - - { - size_t size = 0; - int save_errno; - - for (;;) - { - size_t requested = alloc - size; - size_t count = fread (buf + size, 1, requested, stream); - size += count; - - if (count != requested) - { - save_errno = errno; - if (ferror (stream)) - break; - - if (size < alloc - 1) - { - char *reduce_buf = realloc (buf, size + 1); - if (reduce_buf != NULL) - buf = reduce_buf; - } - - buf[size] = '\0'; - *length = size; - return buf; - } - - { - char *temp_buf; - - if (alloc == SIZE_MAX) - { - save_errno = ENOMEM; - break; - } - - if (alloc < SIZE_MAX - alloc / 2) - alloc = alloc + alloc / 2; - else - alloc = SIZE_MAX; - - if (! (temp_buf = realloc (buf, alloc))) - { - save_errno = errno; - break; - } - - buf = temp_buf; - } - } - - free (buf); - errno = save_errno; - return NULL; - } -} - -char * -read_file (const char *path, size_t *length) -{ - FILE *f = fopen (path, "r"); - char *buf; - int save_errno; - - if (! f) - return NULL; - - buf = fread_file (f, length); - - save_errno = errno; - - if (fclose (f) != 0) - { - if (buf) - { - save_errno = errno; - free (buf); - } - errno = save_errno; - return NULL; - } - - return buf; -} diff --git a/src/ocispec/read-file.h b/src/ocispec/read-file.h deleted file mode 100644 index 78578bab..00000000 --- a/src/ocispec/read-file.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - Copyright 2017 Giuseppe Scrivano - - 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. -*/ - -#ifndef READ_FILE_H -#define READ_FILE_H - -#include -#include - -extern char *fread_file (FILE *stream, size_t *length); - -extern char *read_file (const char *path, size_t *length); - -#endif diff --git a/src/ocispec/sources.py b/src/ocispec/sources.py index 4a4d7ce5..6bfea0d3 100755 --- a/src/ocispec/sources.py +++ b/src/ocispec/sources.py @@ -2045,7 +2045,6 @@ def src_reflect(structs, schema_info, c_file, root_typ): # define _GNU_SOURCE #endif #include - #include #include "ocispec/{schema_info.header.basename}" ''', indent=0) for define in json_api.get_prologue_defines(): @@ -2442,27 +2441,36 @@ def get_c_epilog(c_file, prefix, typ, obj): typename = helpers.get_top_array_type_name(obj.name, prefix) get_c_epilog_for_array(c_file, prefix, typ, obj) + emit(c_file, f''' + + {json_api.get_val_cleaner_define()} + ''', indent=0) + c_file.append("\n") + emit(c_file, f''' {typename} * {typename}_parse_file (const char *filename, const struct parser_context *ctx, parser_error *err) {{ {typename} *ptr = NULL; - size_t filesize; - __auto_free char *content = NULL; + __auto_cleanup ({json_api.DOC_FREE_FUNC}) {json_api.DOC_TYPE}tree = NULL; + struct parser_context tmp_ctx = {{ 0 }}; if (filename == NULL || err == NULL) return NULL; *err = NULL; - content = read_file (filename, &filesize); - if (content == NULL) + if (ctx == NULL) + ctx = (const struct parser_context *) (&tmp_ctx); + + tree = {json_api.doc_read_file('filename')}; + if (tree == NULL) {{ if (asprintf (err, "cannot read the file: %s", filename) < 0) *err = strdup ("error allocating memory"); return NULL; }} - ptr = {typename}_parse_data (content, ctx, err); + ptr = make_{typename} (tree, ctx, err); return ptr; }} ''', indent=0) @@ -2472,30 +2480,69 @@ def get_c_epilog(c_file, prefix, typ, obj): {typename}_parse_file_stream (FILE *stream, const struct parser_context *ctx, parser_error *err) {{ {typename} *ptr = NULL; - size_t filesize; - __auto_free char *content = NULL; + __auto_cleanup ({json_api.DOC_FREE_FUNC}) {json_api.DOC_TYPE}tree = NULL; + struct parser_context tmp_ctx = {{ 0 }}; + int fd; if (stream == NULL || err == NULL) return NULL; *err = NULL; - content = fread_file (stream, &filesize); - if (content == NULL) + if (ctx == NULL) + ctx = (const struct parser_context *) (&tmp_ctx); + + fd = fileno (stream); + if (fd >= 0) + {{ + tree = {json_api.doc_read_fd('fd')}; + }} + else + {{ + __auto_free char *buf = NULL; + size_t buf_len = 0, buf_alloc = 0; + char tmp[4096]; + size_t nread; + + while ((nread = fread (tmp, 1, sizeof (tmp), stream)) > 0) + {{ + if (buf_len + nread >= buf_alloc) + {{ + char *newbuf; + buf_alloc = (buf_len + nread) * 2 + 1; + newbuf = realloc (buf, buf_alloc); + if (newbuf == NULL) + {{ + *err = strdup ("error allocating memory"); + return NULL; + }} + buf = newbuf; + }} + memcpy (buf + buf_len, tmp, nread); + buf_len += nread; + }} + if (ferror (stream)) + {{ + *err = strdup ("error reading the file stream"); + return NULL; + }} + if (buf == NULL) + {{ + *err = strdup ("cannot read the file stream"); + return NULL; + }} + buf[buf_len] = '\\0'; + tree = {json_api.doc_read('buf', 'buf_len')}; + }} + if (tree == NULL) {{ - *err = strdup ("cannot read the file"); + *err = strdup ("cannot read the file stream"); return NULL; }} - ptr = {typename}_parse_data (content, ctx, err); + ptr = make_{typename} (tree, ctx, err); return ptr; }} ''', indent=0) - emit(c_file, f''' - - {json_api.get_val_cleaner_define()} - ''', indent=0) - c_file.append("\n") - emit(c_file, f''' {typename} * {typename}_parse_data (const char *jsondata, const struct parser_context *ctx, parser_error *err)