From: Felix Fietkau Date: Fri, 4 Sep 2009 00:42:54 +0000 (+0200) Subject: add type safety checks for option maps X-Git-Url: http://pilppa.org/gitweb/?p=uci.git;a=commitdiff_plain;h=41d24d850ba51f6449f1f8abcf265f180792ee46 add type safety checks for option maps --- diff --git a/ucimap.c b/ucimap.c index 2bff545..7478b70 100644 --- a/ucimap.c +++ b/ucimap.c @@ -17,8 +17,10 @@ #include #include #include +#include #include #include "ucimap.h" +#include "uci_internal.h" struct uci_alloc { enum ucimap_type type; @@ -213,9 +215,10 @@ ucimap_handle_fixup(struct uci_map *map, struct uci_fixup *f) } static void -ucimap_add_fixup(struct uci_map *map, union ucimap_data *data, struct uci_optmap *om, const char *str) +ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str) { struct uci_fixup *f, tmp; + struct uci_map *map = sd->map; INIT_LIST_HEAD(&tmp.list); tmp.sm = om->data.sm; @@ -284,7 +287,7 @@ ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_s return; break; case UCIMAP_SECTION: - ucimap_add_fixup(sd->map, data, om, str); + ucimap_add_fixup(sd, data, om, str); return; case UCIMAP_CUSTOM: tdata.s = (char *) data; @@ -377,6 +380,73 @@ ucimap_add_section(struct ucimap_section_data *sd) list_add_tail(&sd->list, &map->sdata); } +static const char *ucimap_type_names[] = { + [UCIMAP_STRING] = "string", + [UCIMAP_INT] = "integer", + [UCIMAP_BOOL] = "boolean", + [UCIMAP_SECTION] = "section", + [UCIMAP_LIST] = "list", +}; + +static inline const char * +ucimap_get_type_name(int type) +{ + static char buf[32]; + const char *name; + + if (ucimap_is_list(type)) + return ucimap_type_names[UCIMAP_LIST]; + + name = ucimap_type_names[type & UCIMAP_SUBTYPE]; + if (!name) { + sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE); + name = buf; + } + + return name; +} + +static bool +ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om) +{ + unsigned int type; + + if (om->detected_type < 0) + return true; + + if (ucimap_is_custom(om->type)) + return true; + + if (ucimap_is_list(om->type) != + ucimap_is_list(om->detected_type)) + goto failed; + + if (ucimap_is_list(om->type)) + return true; + + type = om->type & UCIMAP_SUBTYPE; + switch(type) { + case UCIMAP_STRING: + case UCIMAP_INT: + case UCIMAP_BOOL: + if (type != om->detected_type) + goto failed; + break; + case UCIMAP_SECTION: + goto failed; + default: + break; + } + return true; + +failed: + DPRINTF("Invalid type in option '%s' of section type '%s', " + "declared type is %s, detected type is %s\n", + om->name, sm->type, + ucimap_get_type_name(om->type), + ucimap_get_type_name(om->detected_type)); + return false; +} int ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s) @@ -392,6 +462,9 @@ ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucim sd->sm = sm; ucimap_foreach_option(sm, om) { + if (!ucimap_check_optmap_type(sm, om)) + continue; + if (ucimap_is_list(om->type)) { union ucimap_data *data; struct uci_element *e; diff --git a/ucimap.h b/ucimap.h index d384ef6..4f80111 100644 --- a/ucimap.h +++ b/ucimap.h @@ -35,10 +35,46 @@ #define TEST_BIT(_name, _bit) \ (_name[(_bit) / 8] & (1 << ((_bit) % 8))) +#ifndef __GNUC__ +#define __optmap_gen_type(_type, _field) -1 +#else + +#define __compatible(_type, _field, _newtype) \ + __builtin_types_compatible_p(typeof(&(((_type *)0)->_field)), _newtype *) + +#define __list_compatible(_type, _field, __val, __else) \ + __builtin_choose_expr(__compatible(_type, _field, struct ucimap_list *), __val, __else) + +#define __int_compatible(_type, _field, __val, __else) \ + __builtin_choose_expr(__compatible(_type, _field, int), __val, \ + __builtin_choose_expr(__compatible(_type, _field, unsigned int), __val, \ + __else)) + +#define __string_compatible(_type, _field, __val, __else) \ + __builtin_choose_expr(__compatible(_type, _field, char *), __val, \ + __builtin_choose_expr(__compatible(_type, _field, unsigned char *), __val, \ + __builtin_choose_expr(__compatible(_type, _field, const char *), __val, \ + __builtin_choose_expr(__compatible(_type, _field, const unsigned char *), __val, \ + __else)))) + +#define __bool_compatible(_type, _field, __val, __else) \ + __builtin_choose_expr(__compatible(_type, _field, bool), __val, __else) + + +#define __optmap_gen_type(_type, _field) \ + __list_compatible(_type, _field, UCIMAP_LIST, \ + __int_compatible(_type, _field, UCIMAP_INT, \ + __string_compatible(_type, _field, UCIMAP_STRING, \ + __bool_compatible(_type, _field, UCIMAP_BOOL, \ + -1)))) + +#endif + #define UCIMAP_OPTION(_type, _field) \ .type = UCIMAP_CUSTOM, \ .name = #_field, \ - .offset = offsetof(_type, _field) + .offset = offsetof(_type, _field), \ + .detected_type = __optmap_gen_type(_type, _field) #define UCIMAP_SECTION(_name, _field) \ @@ -142,6 +178,7 @@ struct uci_optmap { unsigned int offset; const char *name; enum ucimap_type type; + int detected_type; int (*parse)(void *section, struct uci_optmap *om, union ucimap_data *data, const char *string); int (*format)(void *section, struct uci_optmap *om, union ucimap_data *data, char **string); union {