From b92d4df5095b18bc82a4641143b39dde143e24cf Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Thu, 24 Sep 2026 19:46:29 +0200 Subject: [PATCH 1/5] lib: update proplib --- include/xbps/xbps_array.h | 2 - include/xbps/xbps_dictionary.h | 3 - lib/Makefile | 3 +- lib/portableproplib/prop/prop_array.h | 117 ++- lib/portableproplib/prop/prop_bool.h | 4 +- lib/portableproplib/prop/prop_data.h | 20 +- lib/portableproplib/prop/prop_dictionary.h | 90 +- lib/portableproplib/prop/prop_ingest.h | 1 - lib/portableproplib/prop/prop_number.h | 43 +- lib/portableproplib/prop/prop_object.h | 29 +- lib/portableproplib/prop/prop_string.h | 40 +- lib/portableproplib/prop/proplib.h | 5 +- lib/portableproplib/prop/rbtree.h | 5 - lib/portableproplib/prop_array.c | 228 ++-- lib/portableproplib/prop_array_util.c | 485 +++++---- lib/portableproplib/prop_bool.c | 57 +- lib/portableproplib/prop_data.c | 306 ++++-- lib/portableproplib/prop_dictionary.c | 395 ++++--- lib/portableproplib/prop_dictionary_util.c | 388 ++++--- lib/portableproplib/prop_extern.c | 697 +++++++++++++ lib/portableproplib/prop_ingest.c | 16 +- lib/portableproplib/prop_intern.c | 1094 ++++++++++++++++++++ lib/portableproplib/prop_number.c | 269 +++-- lib/portableproplib/prop_object.c | 966 +---------------- lib/portableproplib/prop_object_impl.h | 215 ++-- lib/portableproplib/prop_rb_impl.h | 46 - lib/portableproplib/prop_stack.c | 4 +- lib/portableproplib/prop_stack.h | 4 +- lib/portableproplib/prop_string.c | 518 ++++++--- lib/portableproplib/prop_zlib.c | 129 --- lib/proplib_wrapper.c | 40 +- 31 files changed, 3949 insertions(+), 2270 deletions(-) create mode 100644 lib/portableproplib/prop_extern.c create mode 100644 lib/portableproplib/prop_intern.c delete mode 100644 lib/portableproplib/prop_rb_impl.h delete mode 100644 lib/portableproplib/prop_zlib.c diff --git a/include/xbps/xbps_array.h b/include/xbps/xbps_array.h index 1d404baaa..671fcf89b 100644 --- a/include/xbps/xbps_array.h +++ b/include/xbps/xbps_array.h @@ -68,9 +68,7 @@ char * xbps_array_externalize(xbps_array_t); xbps_array_t xbps_array_internalize(const char *); bool xbps_array_externalize_to_file(xbps_array_t, const char *); -bool xbps_array_externalize_to_zfile(xbps_array_t, const char *); xbps_array_t xbps_array_internalize_from_file(const char *); -xbps_array_t xbps_array_internalize_from_zfile(const char *); /* * Utility routines to make it more convenient to work with values diff --git a/include/xbps/xbps_dictionary.h b/include/xbps/xbps_dictionary.h index ab5f54174..fb57438d3 100644 --- a/include/xbps/xbps_dictionary.h +++ b/include/xbps/xbps_dictionary.h @@ -78,10 +78,7 @@ xbps_dictionary_t xbps_dictionary_internalize(const char *); bool xbps_dictionary_externalize_to_file(xbps_dictionary_t, const char *); -bool xbps_dictionary_externalize_to_zfile(xbps_dictionary_t, - const char *); xbps_dictionary_t xbps_dictionary_internalize_from_file(const char *); -xbps_dictionary_t xbps_dictionary_internalize_from_zfile(const char *); const char * xbps_dictionary_keysym_cstring_nocopy(xbps_dictionary_keysym_t); diff --git a/lib/Makefile b/lib/Makefile index 2b4efe450..f4ae12d55 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -14,8 +14,9 @@ LIBPROP_OBJS += portableproplib/prop_dictionary.o portableproplib/prop_ingest.o LIBPROP_OBJS += portableproplib/prop_object.o LIBPROP_OBJS += portableproplib/prop_stack.o portableproplib/prop_string.o LIBPROP_OBJS += portableproplib/prop_array_util.o portableproplib/prop_number.o -LIBPROP_OBJS += portableproplib/prop_dictionary_util.o portableproplib/prop_zlib.o +LIBPROP_OBJS += portableproplib/prop_dictionary_util.o LIBPROP_OBJS += portableproplib/prop_data.o +LIBPROP_OBJS += portableproplib/prop_intern.o portableproplib/prop_extern.o LIBPROP_CFLAGS = -Wno-unused-parameter ifdef HAVE_VISIBILITY LIBPROP_CFLAGS += -fvisibility=hidden diff --git a/lib/portableproplib/prop/prop_array.h b/lib/portableproplib/prop/prop_array.h index 436ee3b38..6fbac3eb8 100644 --- a/lib/portableproplib/prop/prop_array.h +++ b/lib/portableproplib/prop/prop_array.h @@ -1,8 +1,8 @@ -/* $NetBSD: prop_array.h,v 1.8 2008/09/11 13:15:13 haad Exp $ */ +/* $NetBSD: prop_array.h,v 1.17 2020/06/06 21:25:59 thorpej Exp $ */ /*- * Copyright (c) 2014 Juan Romero Pardines. - * Copyright (c) 2006 The NetBSD Foundation, Inc. + * Copyright (c) 2006, 2009, 2020 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -33,7 +33,6 @@ #ifndef _PROPLIB_PROP_ARRAY_H_ #define _PROPLIB_PROP_ARRAY_H_ -#include #include typedef struct _prop_array *prop_array_t; @@ -69,9 +68,7 @@ char * prop_array_externalize(prop_array_t); prop_array_t prop_array_internalize(const char *); bool prop_array_externalize_to_file(prop_array_t, const char *); -bool prop_array_externalize_to_zfile(prop_array_t, const char *); prop_array_t prop_array_internalize_from_file(const char *); -prop_array_t prop_array_internalize_from_zfile(const char *); /* * Utility routines to make it more convenient to work with values @@ -82,6 +79,60 @@ bool prop_array_get_bool(prop_array_t, unsigned int, bool prop_array_set_bool(prop_array_t, unsigned int, bool); +bool prop_array_get_schar(prop_array_t, unsigned int, + signed char *); +bool prop_array_get_uchar(prop_array_t, unsigned int, + unsigned char *); +bool prop_array_set_schar(prop_array_t, unsigned int, + signed char); +bool prop_array_set_uchar(prop_array_t, unsigned int, + unsigned char); + +bool prop_array_get_short(prop_array_t, unsigned int, + short *); +bool prop_array_get_ushort(prop_array_t, unsigned int, + unsigned short *); +bool prop_array_set_short(prop_array_t, unsigned int, + short); +bool prop_array_set_ushort(prop_array_t, unsigned int, + unsigned short); + +bool prop_array_get_int(prop_array_t, unsigned int, + int *); +bool prop_array_get_uint(prop_array_t, unsigned int, + unsigned int *); +bool prop_array_set_int(prop_array_t, unsigned int, + int); +bool prop_array_set_uint(prop_array_t, unsigned int, + unsigned int); + +bool prop_array_get_long(prop_array_t, unsigned int, + long *); +bool prop_array_get_ulong(prop_array_t, unsigned int, + unsigned long *); +bool prop_array_set_long(prop_array_t, unsigned int, + long); +bool prop_array_set_ulong(prop_array_t, unsigned int, + unsigned long); + +bool prop_array_get_longlong(prop_array_t, unsigned int, + long long *); +bool prop_array_get_ulonglong(prop_array_t, unsigned int, + unsigned long long *); +bool prop_array_set_longlong(prop_array_t, unsigned int, + long long); +bool prop_array_set_ulonglong(prop_array_t, unsigned int, + unsigned long long); + +bool prop_array_get_intptr(prop_array_t, unsigned int, + intptr_t *); +bool prop_array_get_uintptr(prop_array_t, unsigned int, + uintptr_t *); +bool prop_array_set_intptr(prop_array_t, unsigned int, + intptr_t); +bool prop_array_set_uintptr(prop_array_t, unsigned int, + uintptr_t); + bool prop_array_get_int8(prop_array_t, unsigned int, int8_t *); bool prop_array_get_uint8(prop_array_t, unsigned int, @@ -118,6 +169,29 @@ bool prop_array_set_int64(prop_array_t, unsigned int, bool prop_array_set_uint64(prop_array_t, unsigned int, uint64_t); +bool prop_array_set_and_rel(prop_array_t, unsigned int, + prop_object_t); + +bool prop_array_add_bool(prop_array_t, bool); + +bool prop_array_add_schar(prop_array_t, signed char); +bool prop_array_add_uchar(prop_array_t, unsigned char); + +bool prop_array_add_short(prop_array_t, short); +bool prop_array_add_ushort(prop_array_t, unsigned short); + +bool prop_array_add_int(prop_array_t, int); +bool prop_array_add_uint(prop_array_t, unsigned int); + +bool prop_array_add_long(prop_array_t, long); +bool prop_array_add_ulong(prop_array_t, unsigned long); + +bool prop_array_add_longlong(prop_array_t, long long); +bool prop_array_add_ulonglong(prop_array_t, unsigned long long); + +bool prop_array_add_intptr(prop_array_t, intptr_t); +bool prop_array_add_uintptr(prop_array_t, uintptr_t); + bool prop_array_add_int8(prop_array_t, int8_t); bool prop_array_add_uint8(prop_array_t, uint8_t); @@ -130,21 +204,44 @@ bool prop_array_add_uint32(prop_array_t, uint32_t); bool prop_array_add_int64(prop_array_t, int64_t); bool prop_array_add_uint64(prop_array_t, uint64_t); +bool prop_array_get_string(prop_array_t, unsigned int, + const char **); +bool prop_array_set_string(prop_array_t, unsigned int, + const char *); +bool prop_array_add_string(prop_array_t, const char *); +bool prop_array_set_string_nocopy(prop_array_t, unsigned int, + const char *); +bool prop_array_add_string_nocopy(prop_array_t, const char *); + +bool prop_array_get_data(prop_array_t, unsigned int, + const void **, size_t *); +bool prop_array_set_data(prop_array_t, unsigned int, + const void *, size_t); +bool prop_array_add_data(prop_array_t, + const void *, size_t); +bool prop_array_set_data_nocopy(prop_array_t, unsigned int, + const void *, size_t); +bool prop_array_add_data_nocopy(prop_array_t, + const void *, size_t); + +bool prop_array_add_and_rel(prop_array_t, prop_object_t); + + +/* Deprecated functions. */ + +bool prop_array_add_cstring(prop_array_t, const char *); bool prop_array_get_cstring(prop_array_t, unsigned int, char **); bool prop_array_set_cstring(prop_array_t, unsigned int, const char *); -bool prop_array_add_cstring(prop_array_t, const char *); -bool prop_array_add_cstring_nocopy(prop_array_t, - const char *); + +bool prop_array_add_cstring_nocopy(prop_array_t, const char *); bool prop_array_get_cstring_nocopy(prop_array_t, unsigned int, const char **); bool prop_array_set_cstring_nocopy(prop_array_t, unsigned int, const char *); -bool prop_array_add_and_rel(prop_array_t, prop_object_t); - #ifdef __cplusplus } #endif diff --git a/lib/portableproplib/prop/prop_bool.h b/lib/portableproplib/prop/prop_bool.h index f6b1250d7..4c6956471 100644 --- a/lib/portableproplib/prop/prop_bool.h +++ b/lib/portableproplib/prop/prop_bool.h @@ -1,4 +1,4 @@ -/* $NetBSD: prop_bool.h,v 1.4 2008/04/28 20:22:51 martin Exp $ */ +/* $NetBSD: prop_bool.h,v 1.5 2020/06/06 21:25:59 thorpej Exp $ */ /*- * Copyright (c) 2006 The NetBSD Foundation, Inc. @@ -32,7 +32,6 @@ #ifndef _PROPLIB_PROP_BOOL_H_ #define _PROPLIB_PROP_BOOL_H_ -#include #include typedef struct _prop_bool *prop_bool_t; @@ -45,6 +44,7 @@ prop_bool_t prop_bool_create(bool); prop_bool_t prop_bool_copy(prop_bool_t); bool prop_bool_true(prop_bool_t); +bool prop_bool_value(prop_bool_t); bool prop_bool_equals(prop_bool_t, prop_bool_t); diff --git a/lib/portableproplib/prop/prop_data.h b/lib/portableproplib/prop/prop_data.h index b2450fdac..8edc999c1 100644 --- a/lib/portableproplib/prop/prop_data.h +++ b/lib/portableproplib/prop/prop_data.h @@ -1,4 +1,4 @@ -/* $NetBSD: prop_data.h,v 1.3 2008/04/28 20:22:51 martin Exp $ */ +/* $NetBSD: prop_data.h,v 1.4 2020/06/06 21:25:59 thorpej Exp $ */ /*- * Copyright (c) 2006 The NetBSD Foundation, Inc. @@ -32,9 +32,7 @@ #ifndef _PROPLIB_PROP_DATA_H_ #define _PROPLIB_PROP_DATA_H_ -#include #include -#include typedef struct _prop_data *prop_data_t; @@ -42,19 +40,27 @@ typedef struct _prop_data *prop_data_t; extern "C" { #endif -prop_data_t prop_data_create_data(const void *, size_t); -prop_data_t prop_data_create_data_nocopy(const void *, size_t); +prop_data_t prop_data_create_copy(const void *, size_t); +prop_data_t prop_data_create_nocopy(const void *, size_t); prop_data_t prop_data_copy(prop_data_t); size_t prop_data_size(prop_data_t); -void * prop_data_data(prop_data_t); -const void * prop_data_data_nocopy(prop_data_t); +const void * prop_data_value(prop_data_t); +bool prop_data_copy_value(prop_data_t, void *, size_t); bool prop_data_equals(prop_data_t, prop_data_t); bool prop_data_equals_data(prop_data_t, const void *, size_t); + +/* Deprecated functions. */ +prop_data_t prop_data_create_data(const void *, size_t); +prop_data_t prop_data_create_data_nocopy(const void *, size_t); + +void * prop_data_data(prop_data_t); +const void * prop_data_data_nocopy(prop_data_t); + #ifdef __cplusplus } #endif diff --git a/lib/portableproplib/prop/prop_dictionary.h b/lib/portableproplib/prop/prop_dictionary.h index 1d506ba91..635984f99 100644 --- a/lib/portableproplib/prop/prop_dictionary.h +++ b/lib/portableproplib/prop/prop_dictionary.h @@ -1,7 +1,7 @@ -/* $NetBSD: prop_dictionary.h,v 1.9 2008/04/28 20:22:51 martin Exp $ */ +/* $NetBSD: prop_dictionary.h,v 1.17 2020/06/06 21:25:59 thorpej Exp $ */ /*- - * Copyright (c) 2006 The NetBSD Foundation, Inc. + * Copyright (c) 2006, 2009, 2020 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -32,7 +32,6 @@ #ifndef _PROPLIB_PROP_DICTIONARY_H_ #define _PROPLIB_PROP_DICTIONARY_H_ -#include #include #include @@ -79,12 +78,9 @@ prop_dictionary_t prop_dictionary_internalize(const char *); bool prop_dictionary_externalize_to_file(prop_dictionary_t, const char *); -bool prop_dictionary_externalize_to_zfile(prop_dictionary_t, - const char *); prop_dictionary_t prop_dictionary_internalize_from_file(const char *); -prop_dictionary_t prop_dictionary_internalize_from_zfile(const char *); -const char * prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t); +const char * prop_dictionary_keysym_value(prop_dictionary_keysym_t); bool prop_dictionary_keysym_equals(prop_dictionary_keysym_t, prop_dictionary_keysym_t); @@ -95,11 +91,66 @@ bool prop_dictionary_keysym_equals(prop_dictionary_keysym_t, */ bool prop_dictionary_get_dict(prop_dictionary_t, const char *, prop_dictionary_t *); + bool prop_dictionary_get_bool(prop_dictionary_t, const char *, bool *); bool prop_dictionary_set_bool(prop_dictionary_t, const char *, bool); +bool prop_dictionary_get_schar(prop_dictionary_t, const char *, + signed char *); +bool prop_dictionary_get_uchar(prop_dictionary_t, const char *, + unsigned char *); +bool prop_dictionary_set_schar(prop_dictionary_t, const char *, + signed char); +bool prop_dictionary_set_uchar(prop_dictionary_t, const char *, + unsigned char); + +bool prop_dictionary_get_short(prop_dictionary_t, const char *, + short *); +bool prop_dictionary_get_ushort(prop_dictionary_t, const char *, + unsigned short *); +bool prop_dictionary_set_short(prop_dictionary_t, const char *, + short); +bool prop_dictionary_set_ushort(prop_dictionary_t, const char *, + unsigned short); + +bool prop_dictionary_get_int(prop_dictionary_t, const char *, + int *); +bool prop_dictionary_get_uint(prop_dictionary_t, const char *, + unsigned int *); +bool prop_dictionary_set_int(prop_dictionary_t, const char *, + int); +bool prop_dictionary_set_uint(prop_dictionary_t, const char *, + unsigned int); + +bool prop_dictionary_get_long(prop_dictionary_t, const char *, + long *); +bool prop_dictionary_get_ulong(prop_dictionary_t, const char *, + unsigned long *); +bool prop_dictionary_set_long(prop_dictionary_t, const char *, + long); +bool prop_dictionary_set_ulong(prop_dictionary_t, const char *, + unsigned long); + +bool prop_dictionary_get_longlong(prop_dictionary_t, const char *, + long long *); +bool prop_dictionary_get_ulonglong(prop_dictionary_t, const char *, + unsigned long long *); +bool prop_dictionary_set_longlong(prop_dictionary_t, const char *, + long long); +bool prop_dictionary_set_ulonglong(prop_dictionary_t, const char *, + unsigned long long); + +bool prop_dictionary_get_intptr(prop_dictionary_t, const char *, + intptr_t *); +bool prop_dictionary_get_uintptr(prop_dictionary_t, const char *, + uintptr_t *); +bool prop_dictionary_set_intptr(prop_dictionary_t, const char *, + intptr_t); +bool prop_dictionary_set_uintptr(prop_dictionary_t, const char *, + uintptr_t); + bool prop_dictionary_get_int8(prop_dictionary_t, const char *, int8_t *); bool prop_dictionary_get_uint8(prop_dictionary_t, const char *, @@ -136,6 +187,26 @@ bool prop_dictionary_set_int64(prop_dictionary_t, const char *, bool prop_dictionary_set_uint64(prop_dictionary_t, const char *, uint64_t); +bool prop_dictionary_get_string(prop_dictionary_t, const char *, + const char **cpp); +bool prop_dictionary_set_string(prop_dictionary_t, const char *, + const char *); +bool prop_dictionary_set_string_nocopy(prop_dictionary_t, + const char *, const char *); + +bool prop_dictionary_get_data(prop_dictionary_t, const char *, + const void **, size_t *); +bool prop_dictionary_set_data(prop_dictionary_t, const char *, + const void *, size_t); +bool prop_dictionary_set_data_nocopy(prop_dictionary_t, const char *, + const void *, size_t); + +bool prop_dictionary_set_and_rel(prop_dictionary_t, + const char *, + prop_object_t); + + +/* Deprecated functions. */ bool prop_dictionary_get_cstring(prop_dictionary_t, const char *, char **); bool prop_dictionary_set_cstring(prop_dictionary_t, const char *, @@ -147,9 +218,8 @@ bool prop_dictionary_get_cstring_nocopy(prop_dictionary_t, bool prop_dictionary_set_cstring_nocopy(prop_dictionary_t, const char *, const char *); -bool prop_dictionary_set_and_rel(prop_dictionary_t, - const char *, - prop_object_t); + +const char * prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t); #ifdef __cplusplus } diff --git a/lib/portableproplib/prop/prop_ingest.h b/lib/portableproplib/prop/prop_ingest.h index fd8f941bb..0f1c09406 100644 --- a/lib/portableproplib/prop/prop_ingest.h +++ b/lib/portableproplib/prop/prop_ingest.h @@ -32,7 +32,6 @@ #ifndef _PROPLIB_PROP_INGEST_H_ #define _PROPLIB_PROP_INGEST_H_ -#include #include typedef enum { diff --git a/lib/portableproplib/prop/prop_number.h b/lib/portableproplib/prop/prop_number.h index 86d16db2b..9fad11289 100644 --- a/lib/portableproplib/prop/prop_number.h +++ b/lib/portableproplib/prop/prop_number.h @@ -1,7 +1,7 @@ -/* $NetBSD: prop_number.h,v 1.6 2008/04/28 20:22:51 martin Exp $ */ +/* $NetBSD: prop_number.h,v 1.7 2020/06/06 21:25:59 thorpej Exp $ */ /*- - * Copyright (c) 2006 The NetBSD Foundation, Inc. + * Copyright (c) 2006, 2020 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -41,18 +41,51 @@ typedef struct _prop_number *prop_number_t; extern "C" { #endif -prop_number_t prop_number_create_integer(int64_t); -prop_number_t prop_number_create_unsigned_integer(uint64_t); +prop_number_t prop_number_create_signed(intmax_t); +prop_number_t prop_number_create_unsigned(uintmax_t); + +intmax_t prop_number_signed_value(prop_number_t); +uintmax_t prop_number_unsigned_value(prop_number_t); + +bool prop_number_schar_value(prop_number_t, signed char *); +bool prop_number_short_value(prop_number_t, short *); +bool prop_number_int_value(prop_number_t, int *); +bool prop_number_long_value(prop_number_t, long *); +bool prop_number_longlong_value(prop_number_t, long long *); +bool prop_number_intptr_value(prop_number_t, intptr_t *); +bool prop_number_int8_value(prop_number_t, int8_t *); +bool prop_number_int16_value(prop_number_t, int16_t *); +bool prop_number_int32_value(prop_number_t, int32_t *); +bool prop_number_int64_value(prop_number_t, int64_t *); + +bool prop_number_uchar_value(prop_number_t, unsigned char *); +bool prop_number_ushort_value(prop_number_t, unsigned short *); +bool prop_number_uint_value(prop_number_t, unsigned int *); +bool prop_number_ulong_value(prop_number_t, unsigned long *); +bool prop_number_ulonglong_value(prop_number_t, + unsigned long long *); +bool prop_number_uintptr_value(prop_number_t, uintptr_t *); +bool prop_number_uint8_value(prop_number_t, uint8_t *); +bool prop_number_uint16_value(prop_number_t, uint16_t *); +bool prop_number_uint32_value(prop_number_t, uint32_t *); +bool prop_number_uint64_value(prop_number_t, uint64_t *); prop_number_t prop_number_copy(prop_number_t); int prop_number_size(prop_number_t); bool prop_number_unsigned(prop_number_t); +bool prop_number_equals(prop_number_t, prop_number_t); +bool prop_number_equals_signed(prop_number_t, intmax_t); +bool prop_number_equals_unsigned(prop_number_t, uintmax_t); + + +/* Deprecated functions. */ +prop_number_t prop_number_create_integer(int64_t); +prop_number_t prop_number_create_unsigned_integer(uint64_t); int64_t prop_number_integer_value(prop_number_t); uint64_t prop_number_unsigned_integer_value(prop_number_t); -bool prop_number_equals(prop_number_t, prop_number_t); bool prop_number_equals_integer(prop_number_t, int64_t); bool prop_number_equals_unsigned_integer(prop_number_t, uint64_t); diff --git a/lib/portableproplib/prop/prop_object.h b/lib/portableproplib/prop/prop_object.h index ce6229a5f..eefcaea15 100644 --- a/lib/portableproplib/prop/prop_object.h +++ b/lib/portableproplib/prop/prop_object.h @@ -1,7 +1,7 @@ -/* $NetBSD: prop_object.h,v 1.7 2008/04/28 20:22:51 martin Exp $ */ +/* $NetBSD: prop_object.h,v 1.9 2025/04/23 02:58:52 thorpej Exp $ */ /*- - * Copyright (c) 2006 The NetBSD Foundation, Inc. + * Copyright (c) 2006, 2025 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -32,13 +32,15 @@ #ifndef _PROPLIB_PROP_OBJECT_H_ #define _PROPLIB_PROP_OBJECT_H_ -#include +#include + #include typedef void *prop_object_t; typedef enum { PROP_TYPE_UNKNOWN = 0x00000000, +#ifndef _PROPLIB_ZFS_CONFLICT PROP_TYPE_BOOL = 0x626f6f6c, /* 'bool' */ PROP_TYPE_NUMBER = 0x6e6d6272, /* 'nmbr' */ PROP_TYPE_STRING = 0x73746e67, /* 'stng' */ @@ -46,9 +48,15 @@ typedef enum { PROP_TYPE_ARRAY = 0x61726179, /* 'aray' */ PROP_TYPE_DICTIONARY = 0x64696374, /* 'dict' */ PROP_TYPE_DICT_KEYSYM = 0x646b6579 /* 'dkey' */ +#endif /* !_PROPLIB_ZFS_CONFLICT */ } prop_type_t; -#ifdef __cplusplus +typedef enum { + PROP_FORMAT_XML = 0, + PROP_FORMAT_JSON = 1 +} prop_format_t; + +#ifdef __cplusplus extern "C" { #endif @@ -58,7 +66,8 @@ void prop_object_release(prop_object_t); prop_type_t prop_object_type(prop_object_t); bool prop_object_equals(prop_object_t, prop_object_t); -bool prop_object_equals_with_error(prop_object_t, prop_object_t, bool *); +bool prop_object_equals_with_error(prop_object_t, prop_object_t, + bool *); typedef struct _prop_object_iterator *prop_object_iterator_t; @@ -66,6 +75,16 @@ prop_object_t prop_object_iterator_next(prop_object_iterator_t); void prop_object_iterator_reset(prop_object_iterator_t); void prop_object_iterator_release(prop_object_iterator_t); +char * prop_object_externalize(prop_object_t); +char * prop_object_externalize_with_format(prop_object_t, + prop_format_t); +bool prop_object_externalize_to_file(prop_object_t, const char *); +bool prop_object_externalize_to_file_with_format(prop_object_t, + const char *, prop_format_t); + +prop_object_t prop_object_internalize(const char *); +prop_object_t prop_object_internalize_from_file(const char *); + #ifdef __cplusplus } #endif diff --git a/lib/portableproplib/prop/prop_string.h b/lib/portableproplib/prop/prop_string.h index 1a56ea563..749e59fdf 100644 --- a/lib/portableproplib/prop/prop_string.h +++ b/lib/portableproplib/prop/prop_string.h @@ -1,7 +1,7 @@ -/* $NetBSD: prop_string.h,v 1.3 2008/04/28 20:22:51 martin Exp $ */ +/* $NetBSD: prop_string.h,v 1.4 2020/06/06 21:25:59 thorpej Exp $ */ /*- - * Copyright (c) 2006 The NetBSD Foundation, Inc. + * Copyright (c) 2006, 2020 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -32,9 +32,13 @@ #ifndef _PROPLIB_PROP_STRING_H_ #define _PROPLIB_PROP_STRING_H_ -#include #include -#include + +#if __has_attribute(format) +# define PRINTF_LIKE(a, b) __attribute__ ((format (printf, a, b))) +#else +# define PRINTF_LIKE(a, b) /* printflike */ +#endif typedef struct _prop_string *prop_string_t; @@ -42,25 +46,39 @@ typedef struct _prop_string *prop_string_t; extern "C" { #endif +prop_string_t prop_string_create_format(const char *, ...) PRINTF_LIKE(1, 2); +prop_string_t prop_string_create_copy(const char *); +prop_string_t prop_string_create_nocopy(const char *); + +prop_string_t prop_string_copy(prop_string_t); +bool prop_string_copy_value(prop_string_t, void *, size_t); + +size_t prop_string_size(prop_string_t); +const char * prop_string_value(prop_string_t); + +bool prop_string_equals(prop_string_t, prop_string_t); +bool prop_string_equals_string(prop_string_t, const char *); +int prop_string_compare(prop_string_t, prop_string_t); +int prop_string_compare_string(prop_string_t, const char *); + + +/* Deprecated functions. */ prop_string_t prop_string_create(void); prop_string_t prop_string_create_cstring(const char *); prop_string_t prop_string_create_cstring_nocopy(const char *); -prop_string_t prop_string_copy(prop_string_t); prop_string_t prop_string_copy_mutable(prop_string_t); -size_t prop_string_size(prop_string_t); -bool prop_string_mutable(prop_string_t); - char * prop_string_cstring(prop_string_t); const char * prop_string_cstring_nocopy(prop_string_t); -bool prop_string_append(prop_string_t, prop_string_t); -bool prop_string_append_cstring(prop_string_t, const char *); +bool prop_string_mutable(prop_string_t); -bool prop_string_equals(prop_string_t, prop_string_t); bool prop_string_equals_cstring(prop_string_t, const char *); +bool prop_string_append(prop_string_t, prop_string_t); +bool prop_string_append_cstring(prop_string_t, const char *); + #ifdef __cplusplus } #endif diff --git a/lib/portableproplib/prop/proplib.h b/lib/portableproplib/prop/proplib.h index 74770ddce..0d25b66a5 100644 --- a/lib/portableproplib/prop/proplib.h +++ b/lib/portableproplib/prop/proplib.h @@ -1,4 +1,4 @@ -/* $NetBSD: proplib.h,v 1.6 2008/04/28 20:22:51 martin Exp $ */ +/* $NetBSD: proplib.h,v 1.7 2009/09/13 18:45:10 pooka Exp $ */ /*- * Copyright (c) 2006 The NetBSD Foundation, Inc. @@ -32,14 +32,13 @@ #ifndef _PROPLIB_PROPLIB_H_ #define _PROPLIB_PROPLIB_H_ -#include - #include #include #include #include #include #include + #include #endif /* _PROPLIB_PROPLIB_H_ */ diff --git a/lib/portableproplib/prop/rbtree.h b/lib/portableproplib/prop/rbtree.h index 298cc4123..f96533810 100644 --- a/lib/portableproplib/prop/rbtree.h +++ b/lib/portableproplib/prop/rbtree.h @@ -32,13 +32,8 @@ #ifndef _SYS_RBTREE_H_ #define _SYS_RBTREE_H_ -#if defined(_KERNEL) || defined(_STANDALONE) -#include - -#else #include #include -#endif #include /* GCC version checking borrowed from glibc. */ diff --git a/lib/portableproplib/prop_array.c b/lib/portableproplib/prop_array.c index 489a3bddf..88f155112 100644 --- a/lib/portableproplib/prop_array.c +++ b/lib/portableproplib/prop_array.c @@ -1,7 +1,7 @@ -/* $NetBSD: prop_array.c,v 1.21 2012/07/27 09:10:59 pooka Exp $ */ +/* $NetBSD: prop_array.c,v 1.27 2025/05/14 03:25:45 thorpej Exp $ */ /*- - * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc. + * Copyright (c) 2006, 2007, 2025 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -48,6 +48,20 @@ struct _prop_array { #define PA_F_IMMUTABLE 0x01 /* array is immutable */ _PROP_POOL_INIT(_prop_array_pool, sizeof(struct _prop_array), "proparay") +_PROP_MALLOC_DEFINE(M_PROP_ARRAY, "prop array", + "property array container object") + +static const struct _prop_object_type_tags _prop_array_type_tags = { + .xml_tag = "array", + .json_open_tag = "[", + .json_close_tag = "]", + .json_empty_sep = " ", +}; + +struct _prop_array_iterator { + struct _prop_object_iterator pai_base; + unsigned int pai_index; +}; static _prop_object_free_rv_t _prop_array_free(prop_stack_t, prop_object_t *); @@ -60,7 +74,7 @@ static _prop_object_equals_rv_t void **, void **, prop_object_t *, prop_object_t *); static void _prop_array_equals_finish(prop_object_t, prop_object_t); -static prop_object_iterator_t +static struct _prop_array_iterator * _prop_array_iterator_locked(prop_array_t); static prop_object_t _prop_array_iterator_next_object_locked(void *); @@ -80,11 +94,6 @@ static const struct _prop_object_type _prop_object_type_array = { #define prop_array_is_immutable(x) (((x)->pa_flags & PA_F_IMMUTABLE) != 0) -struct _prop_array_iterator { - struct _prop_object_iterator pai_base; - unsigned int pai_index; -}; - #define EXPAND_STEP 16 static _prop_object_free_rv_t @@ -147,45 +156,53 @@ _prop_array_externalize(struct _prop_object_externalize_context *ctx, { prop_array_t pa = v; struct _prop_object *po; - prop_object_iterator_t pi; - unsigned int i; + struct _prop_array_iterator *pai; bool rv = false; + const char * const sep = + ctx->poec_format == PROP_FORMAT_JSON ? "," : NULL; + + _PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML || + ctx->poec_format == PROP_FORMAT_JSON); _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); if (pa->pa_count == 0) { _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); - return (_prop_object_externalize_empty_tag(ctx, "array")); + return (_prop_extern_append_empty_tag(ctx, + &_prop_array_type_tags)); } - /* XXXJRT Hint "count" for the internalize step? */ - if (_prop_object_externalize_start_tag(ctx, "array") == false || - _prop_object_externalize_append_char(ctx, '\n') == false) + if (_prop_extern_append_start_tag(ctx, + &_prop_array_type_tags, NULL) == false || + _prop_extern_end_line(ctx, NULL) == false) goto out; - pi = _prop_array_iterator_locked(pa); - if (pi == NULL) + pai = _prop_array_iterator_locked(pa); + if (pai == NULL) goto out; ctx->poec_depth++; _PROP_ASSERT(ctx->poec_depth != 0); - while ((po = _prop_array_iterator_next_object_locked(pi)) != NULL) { - if ((*po->po_type->pot_extern)(ctx, po) == false) { - prop_object_iterator_release(pi); + while ((po = _prop_array_iterator_next_object_locked(pai)) != NULL) { + if (_prop_extern_start_line(ctx) == false || + (*po->po_type->pot_extern)(ctx, po) == false || + _prop_extern_end_line(ctx, + pai->pai_index < pa->pa_count ? + sep : NULL) == false) { + prop_object_iterator_release(&pai->pai_base); goto out; } } - prop_object_iterator_release(pi); + prop_object_iterator_release(&pai->pai_base); ctx->poec_depth--; - for (i = 0; i < ctx->poec_depth; i++) { - if (_prop_object_externalize_append_char(ctx, '\t') == false) - goto out; - } - if (_prop_object_externalize_end_tag(ctx, "array") == false) + if (_prop_extern_start_line(ctx) == false || + _prop_extern_append_end_tag(ctx, + &_prop_array_type_tags) == false) { goto out; + } rv = true; @@ -376,7 +393,7 @@ _prop_array_iterator_reset(void *v) * prop_array_create -- * Create an empty array. */ -prop_array_t +_PROP_EXPORT prop_array_t prop_array_create(void) { @@ -387,7 +404,7 @@ prop_array_create(void) * prop_array_create_with_capacity -- * Create an array with the capacity to store N objects. */ -prop_array_t +_PROP_EXPORT prop_array_t prop_array_create_with_capacity(unsigned int capacity) { @@ -401,7 +418,7 @@ prop_array_create_with_capacity(unsigned int capacity) * array contains references to the original array's objects, not * copies of those objects (i.e. a shallow copy). */ -prop_array_t +_PROP_EXPORT prop_array_t prop_array_copy(prop_array_t opa) { prop_array_t pa; @@ -431,7 +448,7 @@ prop_array_copy(prop_array_t opa) * prop_array_copy_mutable -- * Like prop_array_copy(), but the resulting array is mutable. */ -prop_array_t +_PROP_EXPORT prop_array_t prop_array_copy_mutable(prop_array_t opa) { prop_array_t pa; @@ -447,7 +464,7 @@ prop_array_copy_mutable(prop_array_t opa) * prop_array_capacity -- * Return the capacity of the array. */ -unsigned int +_PROP_EXPORT unsigned int prop_array_capacity(prop_array_t pa) { unsigned int rv; @@ -466,7 +483,7 @@ prop_array_capacity(prop_array_t pa) * prop_array_count -- * Return the number of objects stored in the array. */ -unsigned int +_PROP_EXPORT unsigned int prop_array_count(prop_array_t pa) { unsigned int rv; @@ -484,10 +501,10 @@ prop_array_count(prop_array_t pa) /* * prop_array_ensure_capacity -- * Ensure that the array has the capacity to store the specified - * total number of objects (inluding the objects already stored + * total number of objects (including the objects already stored * in the array). */ -bool +_PROP_EXPORT bool prop_array_ensure_capacity(prop_array_t pa, unsigned int capacity) { bool rv; @@ -505,7 +522,7 @@ prop_array_ensure_capacity(prop_array_t pa, unsigned int capacity) return (rv); } -static prop_object_iterator_t +static struct _prop_array_iterator * _prop_array_iterator_locked(prop_array_t pa) { struct _prop_array_iterator *pai; @@ -522,7 +539,7 @@ _prop_array_iterator_locked(prop_array_t pa) pai->pai_base.pi_obj = pa; _prop_array_iterator_reset_locked(pai); - return (&pai->pai_base); + return pai; } /* @@ -530,22 +547,22 @@ _prop_array_iterator_locked(prop_array_t pa) * Return an iterator for the array. The array is retained by * the iterator. */ -prop_object_iterator_t +_PROP_EXPORT prop_object_iterator_t prop_array_iterator(prop_array_t pa) { - prop_object_iterator_t pi; + struct _prop_array_iterator *pai; _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); - pi = _prop_array_iterator_locked(pa); + pai = _prop_array_iterator_locked(pa); _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); - return (pi); + return &pai->pai_base; } /* * prop_array_make_immutable -- * Make the array immutable. */ -void +_PROP_EXPORT void prop_array_make_immutable(prop_array_t pa) { @@ -559,7 +576,7 @@ prop_array_make_immutable(prop_array_t pa) * prop_array_mutable -- * Returns true if the array is mutable. */ -bool +_PROP_EXPORT bool prop_array_mutable(prop_array_t pa) { bool rv; @@ -575,7 +592,7 @@ prop_array_mutable(prop_array_t pa) * prop_array_get -- * Return the object stored at the specified array index. */ -prop_object_t +_PROP_EXPORT prop_object_t prop_array_get(prop_array_t pa, unsigned int idx) { prop_object_t po = NULL; @@ -659,7 +676,7 @@ _prop_array_add_first(prop_array_t pa, prop_object_t po) * caller must either be setting the object just beyond the existing * count or replacing an already existing object reference. */ -bool +_PROP_EXPORT bool prop_array_set(prop_array_t pa, unsigned int idx, prop_object_t po) { prop_object_t opo; @@ -701,7 +718,7 @@ prop_array_set(prop_array_t pa, unsigned int idx, prop_object_t po) * Add a reference to an object to the specified array, appending * to the end and growing the array's capacity, if necessary. */ -bool +_PROP_EXPORT bool prop_array_add(prop_array_t pa, prop_object_t po) { bool rv; @@ -741,7 +758,7 @@ prop_array_add_first(prop_array_t pa, prop_object_t po) * Remove the reference to an object from an array at the specified * index. The array will be compacted following the removal. */ -void +_PROP_EXPORT void prop_array_remove(prop_array_t pa, unsigned int idx) { prop_object_t po; @@ -777,7 +794,7 @@ prop_array_remove(prop_array_t pa, unsigned int idx) * Return true if the two arrays are equivalent. Note we do a * by-value comparison of the objects in the array. */ -bool +_PROP_EXPORT bool prop_array_equals(prop_array_t array1, prop_array_t array2) { if (!prop_object_is_array(array1) || !prop_object_is_array(array2)) @@ -788,33 +805,12 @@ prop_array_equals(prop_array_t array1, prop_array_t array2) /* * prop_array_externalize -- - * Externalize an array, return a NUL-terminated buffer - * containing the XML-style representation. The buffer is allocated - * with the M_TEMP memory type. + * Externalize an array in XML format. */ -char * +_PROP_EXPORT char * prop_array_externalize(prop_array_t pa) { - struct _prop_object_externalize_context *ctx; - char *cp; - - ctx = _prop_object_externalize_context_alloc(); - if (ctx == NULL) - return (NULL); - - if (_prop_object_externalize_header(ctx) == false || - (*pa->pa_obj.po_type->pot_extern)(ctx, pa) == false || - _prop_object_externalize_footer(ctx) == false) { - /* We are responsible for releasing the buffer. */ - _PROP_FREE(ctx->poec_buf, M_TEMP); - _prop_object_externalize_context_free(ctx); - return (NULL); - } - - cp = ctx->poec_buf; - _prop_object_externalize_context_free(ctx); - - return (cp); + return _prop_object_externalize(&pa->pa_obj, PROP_FORMAT_XML); } /* @@ -869,7 +865,29 @@ _prop_array_internalize_continue(prop_stack_t stack, /* * Current element is processed and added, look for next. + * For JSON, we'll skip the comma separator, if present. + * + * By doing this here, we correctly error out if a separator + * is found other than after an element, but this does mean + * that we do allow a trailing comma after the final element + * which isn't allowed in the JSON spec, but seems pretty + * harmless (and there are other JSON parsers that also allow + * it). + * + * Conversely, we don't want to *require* the separator if the + * spec doesn't require it, and we don't know what's next in + * the buffer, so we basically treat the separator as completely + * optional. Since there does not appear to be any ambiguity, + * this also seems pretty harmless. + * + * (FWIW, RFC 8259 section 9 seems to specifically allow this.) */ + if (ctx->poic_format == PROP_FORMAT_JSON) { + ctx->poic_cp = _prop_intern_skip_whitespace(ctx->poic_cp); + if (*ctx->poic_cp == ',') { + ctx->poic_cp++; + } + } return (_prop_array_internalize_body(stack, obj, ctx)); bad: @@ -886,16 +904,27 @@ _prop_array_internalize_body(prop_stack_t stack, prop_object_t *obj, _PROP_ASSERT(array != NULL); - /* Fetch the next tag. */ - if (_prop_object_internalize_find_tag(ctx, NULL, - _PROP_TAG_TYPE_EITHER) == false) - goto bad; + if (ctx->poic_format == PROP_FORMAT_JSON) { + ctx->poic_cp = _prop_intern_skip_whitespace(ctx->poic_cp); - /* Check to see if this is the end of the array. */ - if (_PROP_TAG_MATCH(ctx, "array") && - ctx->poic_tag_type == _PROP_TAG_TYPE_END) { - /* It is, so don't iterate any further. */ - return (true); + /* Check to see if this is the end of the array. */ + if (*ctx->poic_cp == ']') { + /* It is, so don't iterate any further. */ + ctx->poic_cp++; + return true; + } + } else { + /* Fetch the next tag. */ + if (_prop_xml_intern_find_tag(ctx, NULL, + _PROP_TAG_TYPE_EITHER) == false) + goto bad; + + /* Check to see if this is the end of the array. */ + if (_PROP_TAG_MATCH(ctx, "array") && + ctx->poic_tag_type == _PROP_TAG_TYPE_END) { + /* It is, so don't iterate any further. */ + return (true); + } } if (_prop_stack_push(stack, array, @@ -910,53 +939,32 @@ _prop_array_internalize_body(prop_stack_t stack, prop_object_t *obj, /* * prop_array_internalize -- - * Create an array by parsing the XML-style representation. + * Create an array by parsing the external representation. */ -prop_array_t -prop_array_internalize(const char *xml) +_PROP_EXPORT prop_array_t +prop_array_internalize(const char *data) { - return _prop_generic_internalize(xml, "array"); + return _prop_object_internalize(data, &_prop_array_type_tags); } /* * prop_array_externalize_to_file -- * Externalize an array to the specified file. */ -bool +_PROP_EXPORT bool prop_array_externalize_to_file(prop_array_t array, const char *fname) { - char *xml; - bool rv; - int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */ - - xml = prop_array_externalize(array); - if (xml == NULL) - return (false); - rv = _prop_object_externalize_write_file(fname, xml, strlen(xml), false); - if (rv == false) - save_errno = errno; - _PROP_FREE(xml, M_TEMP); - if (rv == false) - errno = save_errno; - - return (rv); + return _prop_object_externalize_to_file(&array->pa_obj, fname, + PROP_FORMAT_XML); } /* * prop_array_internalize_from_file -- * Internalize an array from a file. */ -prop_array_t +_PROP_EXPORT prop_array_t prop_array_internalize_from_file(const char *fname) { - struct _prop_object_internalize_mapped_file *mf; - prop_array_t array; - - mf = _prop_object_internalize_map_file(fname); - if (mf == NULL) - return (NULL); - array = prop_array_internalize(mf->poimf_xml); - _prop_object_internalize_unmap_file(mf); - - return (array); + return _prop_object_internalize_from_file(fname, + &_prop_array_type_tags); } diff --git a/lib/portableproplib/prop_array_util.c b/lib/portableproplib/prop_array_util.c index 66f20c449..634a78237 100644 --- a/lib/portableproplib/prop_array_util.c +++ b/lib/portableproplib/prop_array_util.c @@ -1,7 +1,7 @@ -/* $NetBSD: prop_array_util.c,v 1.4 2012/07/27 09:10:59 pooka Exp $ */ +/* $NetBSD: prop_array_util.c,v 1.10 2025/04/26 17:13:23 thorpej Exp $ */ /*- - * Copyright (c) 2006 The NetBSD Foundation, Inc. + * Copyright (c) 2006, 2020 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -41,226 +41,335 @@ #include "prop_object_impl.h" /* hide kernel vs. not-kernel vs. standalone */ #include -bool -prop_array_get_bool(prop_array_t array, - unsigned int indx, - bool *valp) +_PROP_EXPORT bool +prop_array_get_bool(prop_array_t array, unsigned int indx, bool *valp) { prop_bool_t b; b = prop_array_get(array, indx); if (prop_object_type(b) != PROP_TYPE_BOOL) return (false); - + *valp = prop_bool_true(b); return (true); } -bool -prop_array_set_bool(prop_array_t array, - unsigned int indx, - bool val) +_PROP_EXPORT bool +prop_array_set_bool(prop_array_t array, unsigned int indx, bool val) { - prop_bool_t b; - int rv; - b = prop_bool_create(val); - if (b == NULL) - return (false); - rv = prop_array_set(array, indx, b); - prop_object_release(b); + return prop_array_set_and_rel(array, indx, prop_bool_create(val)); +} - return (rv); +_PROP_EXPORT bool +prop_array_add_bool(prop_array_t array, bool val) +{ + + return prop_array_add_and_rel(array, prop_bool_create(val)); } -#define TEMPLATE(size) \ -bool \ -prop_array_get_int ## size (prop_array_t array, \ - unsigned int indx, \ - int ## size ## _t *valp) \ -{ \ - prop_number_t num; \ - \ - num = prop_array_get(array, indx); \ - if (prop_object_type(num) != PROP_TYPE_NUMBER) \ - return (false); \ - \ - if (prop_number_unsigned(num) && \ - prop_number_unsigned_integer_value(num) > \ - /*CONSTCOND*/((size) == 8 ? INT8_MAX : \ - (size) == 16 ? INT16_MAX : \ - (size) == 32 ? INT32_MAX : INT64_MAX)) { \ - return (false); \ - } \ - \ - if (prop_number_size(num) > (size)) \ - return (false); \ - \ - *valp = (int ## size ## _t) prop_number_integer_value(num); \ - \ - return (true); \ -} \ - \ -bool \ -prop_array_get_uint ## size (prop_array_t array, \ - unsigned int indx, \ - uint ## size ## _t *valp) \ +#define TEMPLATE(name, typ) \ +_PROP_EXPORT bool \ +prop_array_get_ ## name (prop_array_t array, \ + unsigned int indx, \ + typ *valp) \ { \ - prop_number_t num; \ - \ - num = prop_array_get(array, indx); \ - if (prop_object_type(num) != PROP_TYPE_NUMBER) \ - return (false); \ - \ - if (prop_number_unsigned(num) == false && \ - prop_number_integer_value(num) < 0) { \ - return (false); \ - } \ - \ - if (prop_number_size(num) > (size)) \ - return (false); \ - \ - *valp = (uint ## size ## _t) \ - prop_number_unsigned_integer_value(num); \ - \ - return (true); \ -} \ - \ -bool \ - prop_array_set_int ## size (prop_array_t array, \ - unsigned int indx, \ - int ## size ## _t val) \ -{ \ - prop_number_t num; \ - int rv; \ - \ - num = prop_number_create_integer((int64_t) val); \ - if (num == NULL) \ - return (false); \ - rv = prop_array_set(array, indx, num); \ - prop_object_release(num); \ - \ - return (rv); \ -} \ - \ -bool \ -prop_array_set_uint ## size (prop_array_t array, \ - unsigned int indx, \ - uint ## size ## _t val) \ -{ \ - prop_number_t num; \ - int rv; \ - \ - num = prop_number_create_unsigned_integer((uint64_t) val); \ - if (num == NULL) \ - return (false); \ - rv = prop_array_set(array, indx, num); \ - prop_object_release(num); \ - \ - return (rv); \ -} \ - \ -bool \ -prop_array_add_int ## size (prop_array_t array, \ - int ## size ## _t val) \ -{ \ - prop_number_t num; \ - int rv; \ - \ - num = prop_number_create_integer((int64_t) val); \ - if (num == NULL) \ - return (false); \ - rv = prop_array_add(array, num); \ - prop_object_release(num); \ - \ - return (rv); \ -} \ - \ -bool \ -prop_array_add_uint ## size (prop_array_t array, \ - uint ## size ## _t val) \ -{ \ - prop_number_t num; \ - int rv; \ - \ - num = prop_number_create_integer((int64_t) val); \ - if (num == NULL) \ - return (false); \ - rv = prop_array_add(array, num); \ - prop_object_release(num); \ - \ - return (rv); \ + return prop_number_ ## name ## _value( \ + prop_array_get(array, indx), valp); \ } +TEMPLATE(schar, signed char) +TEMPLATE(short, short) +TEMPLATE(int, int) +TEMPLATE(long, long) +TEMPLATE(longlong, long long) +TEMPLATE(intptr, intptr_t) +TEMPLATE(int8, int8_t) +TEMPLATE(int16, int16_t) +TEMPLATE(int32, int32_t) +TEMPLATE(int64, int64_t) -TEMPLATE(8) -TEMPLATE(16) -TEMPLATE(32) -TEMPLATE(64) +TEMPLATE(uchar, unsigned char) +TEMPLATE(ushort, unsigned short) +TEMPLATE(uint, unsigned int) +TEMPLATE(ulong, unsigned long) +TEMPLATE(ulonglong, unsigned long long) +TEMPLATE(uintptr, uintptr_t) +TEMPLATE(uint8, uint8_t) +TEMPLATE(uint16, uint16_t) +TEMPLATE(uint32, uint32_t) +TEMPLATE(uint64, uint64_t) #undef TEMPLATE -#define TEMPLATE(variant, qualifier) \ -bool \ -prop_array_get_cstring ## variant (prop_array_t array, \ - unsigned int indx, \ - qualifier char **cpp) \ -{ \ - prop_string_t str; \ - \ - str = prop_array_get(array, indx); \ - if (prop_object_type(str) != PROP_TYPE_STRING) \ - return (false); \ - \ - *cpp = prop_string_cstring ## variant (str); \ - \ - return (*cpp == NULL ? false : true); \ -} \ - \ -bool \ -prop_array_add_cstring ## variant (prop_array_t array, \ - const char *cp) \ +static bool +prop_array_set_signed_number(prop_array_t array, unsigned int indx, + intmax_t val) +{ + return prop_array_set_and_rel(array, indx, + prop_number_create_signed(val)); +} + +static bool +prop_array_add_signed_number(prop_array_t array, intmax_t val) +{ + return prop_array_add_and_rel(array, prop_number_create_signed(val)); +} + +static bool +prop_array_set_unsigned_number(prop_array_t array, unsigned int indx, + uintmax_t val) +{ + return prop_array_set_and_rel(array, indx, + prop_number_create_unsigned(val)); +} + +static bool +prop_array_add_unsigned_number(prop_array_t array, uintmax_t val) +{ + return prop_array_add_and_rel(array, prop_number_create_unsigned(val)); +} + +#define TEMPLATE(name, which, typ) \ +_PROP_EXPORT bool \ +prop_array_set_ ## name (prop_array_t array, \ + unsigned int indx, \ + typ val) \ { \ - prop_string_t str; \ - bool rv; \ - \ - str = prop_string_create_cstring ## variant (cp); \ - if (str == NULL) \ - return false; \ - rv = prop_array_add(array, str); \ - prop_object_release(str); \ - return rv; \ + /*LINTED: for conversion from 'long long' to 'long'*/ \ + return prop_array_set_ ## which ## _number(array, indx, val); \ } \ \ -bool \ -prop_array_set_cstring ## variant (prop_array_t array, \ - unsigned int indx, \ - const char *cp) \ +_PROP_EXPORT bool \ +prop_array_add_ ## name (prop_array_t array, \ + typ val) \ { \ - prop_string_t str; \ - int rv; \ - \ - str = prop_string_create_cstring ## variant (cp); \ - if (str == NULL) \ - return (false); \ - rv = prop_array_set(array, indx, str); \ - prop_object_release(str); \ - \ - return (rv); \ + /*LINTED: for conversion from 'long long' to 'long'*/ \ + return prop_array_add_ ## which ## _number(array, val); \ } -TEMPLATE(,) -TEMPLATE(_nocopy,const) +#define STEMPLATE(name, typ) TEMPLATE(name, signed, typ) +#define UTEMPLATE(name, typ) TEMPLATE(name, unsigned, typ) +STEMPLATE(schar, signed char) +STEMPLATE(short, short) +STEMPLATE(int, int) +STEMPLATE(long, long) +STEMPLATE(longlong, long long) +STEMPLATE(intptr, intptr_t) +STEMPLATE(int8, int8_t) +STEMPLATE(int16, int16_t) +STEMPLATE(int32, int32_t) +STEMPLATE(int64, int64_t) + +UTEMPLATE(uchar, unsigned char) +UTEMPLATE(ushort, unsigned short) +UTEMPLATE(uint, unsigned int) +UTEMPLATE(ulong, unsigned long) +UTEMPLATE(ulonglong, unsigned long long) +UTEMPLATE(uintptr, uintptr_t) +UTEMPLATE(uint8, uint8_t) +UTEMPLATE(uint16, uint16_t) +UTEMPLATE(uint32, uint32_t) +UTEMPLATE(uint64, uint64_t) + +#undef STEMPLATE +#undef UTEMPLATE #undef TEMPLATE -bool +_PROP_EXPORT bool +prop_array_get_string(prop_array_t array, unsigned int indx, const char **cpp) +{ + prop_string_t str; + const char *cp; + + str = prop_array_get(array, indx); + if (prop_object_type(str) != PROP_TYPE_STRING) + return (false); + + cp = prop_string_value(str); + if (cp == NULL) + return (false); + + *cpp = cp; + return (true); +} + +_PROP_EXPORT bool +prop_array_set_string(prop_array_t array, unsigned int indx, const char *cp) +{ + return prop_array_set_and_rel(array, indx, + prop_string_create_copy(cp)); +} + +_PROP_EXPORT bool +prop_array_add_string(prop_array_t array, const char *cp) +{ + return prop_array_add_and_rel(array, prop_string_create_copy(cp)); +} + +_PROP_EXPORT bool +prop_array_set_string_nocopy(prop_array_t array, unsigned int indx, + const char *cp) +{ + return prop_array_set_and_rel(array, indx, + prop_string_create_nocopy(cp)); +} + +_PROP_EXPORT bool +prop_array_add_string_nocopy(prop_array_t array, const char *cp) +{ + return prop_array_add_and_rel(array, prop_string_create_nocopy(cp)); +} + +_PROP_EXPORT bool +prop_array_get_data(prop_array_t array, unsigned int indx, const void **vp, + size_t *sizep) +{ + prop_data_t data; + const void *v; + + data = prop_array_get(array, indx); + if (prop_object_type(data) != PROP_TYPE_DATA) + return (false); + + v = prop_data_value(data); + if (v == NULL) + return (false); + + *vp = v; + if (sizep != NULL) + *sizep = prop_data_size(data); + return (true); +} + +_PROP_EXPORT bool +prop_array_set_data(prop_array_t array, unsigned int indx, const void *v, + size_t size) +{ + return prop_array_set_and_rel(array, indx, + prop_data_create_copy(v, size)); +} + +_PROP_EXPORT bool +prop_array_set_data_nocopy(prop_array_t array, unsigned int indx, const void *v, + size_t size) +{ + return prop_array_set_and_rel(array, indx, + prop_data_create_nocopy(v, size)); +} + +_PROP_EXPORT bool +prop_array_add_data(prop_array_t array, const void *v, size_t size) +{ + return prop_array_add_and_rel(array, + prop_data_create_copy(v, size)); +} + +_PROP_EXPORT bool +prop_array_add_data_nocopy(prop_array_t array, const void *v, size_t size) +{ + return prop_array_add_and_rel(array, + prop_data_create_nocopy(v, size)); +} + +_PROP_DEPRECATED(prop_array_get_cstring, + "this program uses prop_array_get_cstring(), " + "which is deprecated; use prop_array_get_string() and copy instead.") +_PROP_EXPORT bool +prop_array_get_cstring(prop_array_t array, unsigned int indx, char **cpp) +{ + prop_string_t str; + char *cp; + size_t len; + bool rv; + + str = prop_array_get(array, indx); + if (prop_object_type(str) != PROP_TYPE_STRING) + return (false); + + len = prop_string_size(str); + cp = _PROP_MALLOC(len + 1, M_TEMP); + if (cp == NULL) + return (false); + + rv = prop_string_copy_value(str, cp, len + 1); + if (rv) + *cpp = cp; + else + _PROP_FREE(cp, M_TEMP); + + return (rv); +} + +_PROP_DEPRECATED(prop_array_get_cstring_nocopy, + "this program uses prop_array_get_cstring_nocopy(), " + "which is deprecated; use prop_array_get_string() instead.") +_PROP_EXPORT bool +prop_array_get_cstring_nocopy(prop_array_t array, unsigned int indx, + const char **cpp) +{ + return prop_array_get_string(array, indx, cpp); +} + +_PROP_DEPRECATED(prop_array_set_cstring, + "this program uses prop_array_set_cstring(), " + "which is deprecated; use prop_array_set_string() instead.") +_PROP_EXPORT bool +prop_array_set_cstring(prop_array_t array, unsigned int indx, const char *cp) +{ + return prop_array_set_string(array, indx, cp); +} + +_PROP_DEPRECATED(prop_array_add_cstring, + "this program uses prop_array_add_cstring(), " + "which is deprecated; use prop_array_add_string() instead.") +_PROP_EXPORT bool +prop_array_add_cstring(prop_array_t array, const char *cp) +{ + return prop_array_add_string(array, cp); +} + +_PROP_DEPRECATED(prop_array_set_cstring_nocopy, + "this program uses prop_array_set_cstring_nocopy(), " + "which is deprecated; use prop_array_set_string_nocopy() instead.") +_PROP_EXPORT bool +prop_array_set_cstring_nocopy(prop_array_t array, unsigned int indx, + const char *cp) +{ + return prop_array_set_string_nocopy(array, indx, cp); +} + +_PROP_DEPRECATED(prop_array_add_cstring_nocopy, + "this program uses prop_array_add_cstring_nocopy(), " + "which is deprecated; use prop_array_add_string_nocopy() instead.") +_PROP_EXPORT bool +prop_array_add_cstring_nocopy(prop_array_t array, const char *cp) +{ + return prop_array_add_string_nocopy(array, cp); +} + +_PROP_EXPORT bool prop_array_add_and_rel(prop_array_t array, prop_object_t po) { - bool ret; + bool rv; + + if (po == NULL) + return false; + rv = prop_array_add(array, po); + prop_object_release(po); + return rv; +} + +_PROP_EXPORT bool +prop_array_set_and_rel(prop_array_t array, unsigned int indx, + prop_object_t po) +{ + bool rv; + if (po == NULL) return false; - ret = prop_array_add(array, po); + rv = prop_array_set(array, indx, po); prop_object_release(po); - return ret; + return rv; } diff --git a/lib/portableproplib/prop_bool.c b/lib/portableproplib/prop_bool.c index d9e912b2c..db740334d 100644 --- a/lib/portableproplib/prop_bool.c +++ b/lib/portableproplib/prop_bool.c @@ -1,7 +1,7 @@ -/* $NetBSD: prop_bool.c,v 1.17 2009/01/03 18:31:33 pooka Exp $ */ +/* $NetBSD: prop_bool.c,v 1.24 2025/05/14 03:25:45 thorpej Exp $ */ /*- - * Copyright (c) 2006 The NetBSD Foundation, Inc. + * Copyright (c) 2006, 2025 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -29,17 +29,29 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include #include "prop_object_impl.h" +#include struct _prop_bool { struct _prop_object pb_obj; - bool pb_value; + bool pb_value; }; static struct _prop_bool _prop_bool_true; static struct _prop_bool _prop_bool_false; +static const char truestr[] = "true"; +static const char falsestr[] = "false"; + +static const struct _prop_object_type_tags _prop_bool_true_type_tags = { + .xml_tag = truestr, + .json_open_tag = truestr, +}; +static const struct _prop_object_type_tags _prop_bool_false_type_tags = { + .xml_tag = falsestr, + .json_open_tag = falsestr, +}; + static _prop_object_free_rv_t _prop_bool_free(prop_stack_t, prop_object_t *); static bool _prop_bool_externalize( @@ -78,9 +90,11 @@ _prop_bool_externalize(struct _prop_object_externalize_context *ctx, void *v) { prop_bool_t pb = v; + const struct _prop_object_type_tags *tags = + pb->pb_value ? &_prop_bool_true_type_tags + : &_prop_bool_false_type_tags; - return (_prop_object_externalize_empty_tag(ctx, - pb->pb_value ? "true" : "false")); + return _prop_extern_append_empty_tag(ctx, tags); } /* ARGSUSED */ @@ -140,7 +154,7 @@ _prop_bool_alloc(bool val) * Create a prop_bool_t and initialize it with the * provided boolean value. */ -prop_bool_t +_PROP_EXPORT prop_bool_t prop_bool_create(bool val) { @@ -151,7 +165,7 @@ prop_bool_create(bool val) * prop_bool_copy -- * Copy a prop_bool_t. */ -prop_bool_t +_PROP_EXPORT prop_bool_t prop_bool_copy(prop_bool_t opb) { @@ -167,11 +181,11 @@ prop_bool_copy(prop_bool_t opb) } /* - * prop_bool_true -- + * prop_bool_value -- * Get the value of a prop_bool_t. */ -bool -prop_bool_true(prop_bool_t pb) +_PROP_EXPORT bool +prop_bool_value(prop_bool_t pb) { if (! prop_object_is_bool(pb)) @@ -180,11 +194,21 @@ prop_bool_true(prop_bool_t pb) return (pb->pb_value); } +/* + * prop_bool_true -- + * Historical alias for prop_bool_value(). + */ +_PROP_EXPORT bool +prop_bool_true(prop_bool_t pb) +{ + return prop_bool_value(pb); +} + /* * prop_bool_equals -- * Return true if the boolean values are equivalent. */ -bool +_PROP_EXPORT bool prop_bool_equals(prop_bool_t b1, prop_bool_t b2) { if (!prop_object_is_bool(b1) || !prop_object_is_bool(b2)) @@ -206,15 +230,20 @@ _prop_bool_internalize(prop_stack_t stack, prop_object_t *obj, { bool val; + /* + * N.B. For internalizing JSON, the layer above us has + * made it look like XML for this object type. + */ + /* No attributes, and it must be an empty element. */ if (ctx->poic_tagattr != NULL || ctx->poic_is_empty_element == false) return (true); - if (_PROP_TAG_MATCH(ctx, "true")) + if (_PROP_TAG_MATCH(ctx, truestr)) val = true; else { - _PROP_ASSERT(_PROP_TAG_MATCH(ctx, "false")); + _PROP_ASSERT(_PROP_TAG_MATCH(ctx, falsestr)); val = false; } *obj = prop_bool_create(val); diff --git a/lib/portableproplib/prop_data.c b/lib/portableproplib/prop_data.c index e743438b4..f83733084 100644 --- a/lib/portableproplib/prop_data.c +++ b/lib/portableproplib/prop_data.c @@ -1,7 +1,7 @@ -/* $NetBSD: prop_data.c,v 1.13 2008/08/03 04:00:12 thorpej Exp $ */ +/* $NetBSD: prop_data.c,v 1.24 2025/09/23 22:37:13 rillig Exp $ */ /*- - * Copyright (c) 2006 The NetBSD Foundation, Inc. + * Copyright (c) 2006, 2020, 2025 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -29,8 +29,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include #include "prop_object_impl.h" +#include #include #include @@ -49,8 +49,15 @@ struct _prop_data { }; #define PD_F_NOCOPY 0x01 +#define PD_F_MUTABLE 0x02 _PROP_POOL_INIT(_prop_data_pool, sizeof(struct _prop_data), "propdata") +_PROP_MALLOC_DEFINE(M_PROP_DATA, "prop data", + "property data container object") + +static const struct _prop_object_type_tags _prop_data_type_tags = { + .xml_tag = "data", +}; static _prop_object_free_rv_t _prop_data_free(prop_stack_t, prop_object_t *); @@ -98,10 +105,22 @@ _prop_data_externalize(struct _prop_object_externalize_context *ctx, void *v) uint8_t output[4]; uint8_t input[3]; + _PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML || + ctx->poec_format == PROP_FORMAT_JSON); + + /* + * JSON does not have a syntax for serialized binary data. + */ + if (ctx->poec_format == PROP_FORMAT_JSON) { + return false; + } + if (pd->pd_size == 0) - return (_prop_object_externalize_empty_tag(ctx, "data")); + return (_prop_extern_append_empty_tag(ctx, + &_prop_data_type_tags)); - if (_prop_object_externalize_start_tag(ctx, "data") == false) + if (_prop_extern_append_start_tag(ctx, + &_prop_data_type_tags, NULL) == false) return (false); for (src = pd->pd_immutable, srclen = pd->pd_size; @@ -121,13 +140,13 @@ _prop_data_externalize(struct _prop_object_externalize_context *ctx, void *v) _PROP_ASSERT(output[2] < 64); _PROP_ASSERT(output[3] < 64); - if (_prop_object_externalize_append_char(ctx, + if (_prop_extern_append_char(ctx, _prop_data_base64[output[0]]) == false || - _prop_object_externalize_append_char(ctx, + _prop_extern_append_char(ctx, _prop_data_base64[output[1]]) == false || - _prop_object_externalize_append_char(ctx, + _prop_extern_append_char(ctx, _prop_data_base64[output[2]]) == false || - _prop_object_externalize_append_char(ctx, + _prop_extern_append_char(ctx, _prop_data_base64[output[3]]) == false) return (false); } @@ -146,21 +165,22 @@ _prop_data_externalize(struct _prop_object_externalize_context *ctx, void *v) _PROP_ASSERT(output[1] < 64); _PROP_ASSERT(output[2] < 64); - if (_prop_object_externalize_append_char(ctx, + if (_prop_extern_append_char(ctx, _prop_data_base64[output[0]]) == false || - _prop_object_externalize_append_char(ctx, + _prop_extern_append_char(ctx, _prop_data_base64[output[1]]) == false || - _prop_object_externalize_append_char(ctx, + _prop_extern_append_char(ctx, srclen == 1 ? _prop_data_pad64 : _prop_data_base64[output[2]]) == false || - _prop_object_externalize_append_char(ctx, + _prop_extern_append_char(ctx, _prop_data_pad64) == false) return (false); } - if (_prop_object_externalize_end_tag(ctx, "data") == false) + if (_prop_extern_append_end_tag(ctx, + &_prop_data_type_tags) == false) return (false); - + return (true); } @@ -189,7 +209,7 @@ _prop_data_equals(prop_object_t v1, prop_object_t v2, } static prop_data_t -_prop_data_alloc(void) +_prop_data_alloc(int const flags) { prop_data_t pd; @@ -199,23 +219,37 @@ _prop_data_alloc(void) pd->pd_mutable = NULL; pd->pd_size = 0; - pd->pd_flags = 0; + pd->pd_flags = flags; } return (pd); } -/* - * prop_data_create_data -- - * Create a data container that contains a copy of the data. - */ -prop_data_t +static prop_data_t +_prop_data_instantiate(int const flags, const void * const data, + size_t const len) +{ + prop_data_t pd; + + pd = _prop_data_alloc(flags); + if (pd != NULL) { + pd->pd_immutable = data; + pd->pd_size = len; + } + + return (pd); +} + +_PROP_DEPRECATED(prop_data_create_data, + "this program uses prop_data_create_data(); all functions " + "supporting mutable prop_data objects are deprecated.") +_PROP_EXPORT prop_data_t prop_data_create_data(const void *v, size_t size) { prop_data_t pd; void *nv; - pd = _prop_data_alloc(); + pd = _prop_data_alloc(PD_F_MUTABLE); if (pd != NULL && size != 0) { nv = _PROP_MALLOC(size, M_PROP_DATA); if (nv == NULL) { @@ -229,31 +263,67 @@ prop_data_create_data(const void *v, size_t size) return (pd); } +_PROP_DEPRECATED(prop_data_create_data_nocopy, + "this program uses prop_data_create_data_nocopy(), " + "which is deprecated; use prop_data_create_nocopy() instead.") +_PROP_EXPORT prop_data_t +prop_data_create_data_nocopy(const void *v, size_t size) +{ + return prop_data_create_nocopy(v, size); +} + /* - * prop_data_create_data_nocopy -- - * Create an immutable data container that contains a refrence to the - * provided external data. + * prop_data_create_copy -- + * Create a data object with a copy of the provided data. */ -prop_data_t -prop_data_create_data_nocopy(const void *v, size_t size) +_PROP_EXPORT prop_data_t +prop_data_create_copy(const void *v, size_t size) { prop_data_t pd; - - pd = _prop_data_alloc(); - if (pd != NULL) { - pd->pd_immutable = v; - pd->pd_size = size; - pd->pd_flags |= PD_F_NOCOPY; + void *nv; + + /* Tolerate the creation of empty data objects. */ + if (v != NULL && size != 0) { + nv = _PROP_MALLOC(size, M_PROP_DATA); + if (nv == NULL) + return (NULL); + + memcpy(nv, v, size); + } else { + nv = NULL; + size = 0; } + + pd = _prop_data_instantiate(0, nv, size); + if (pd == NULL && nv == NULL) + _PROP_FREE(nv, M_PROP_DATA); + return (pd); } +/* + * prop_data_create_nocopy -- + * Create a data object using the provided external data reference. + */ +_PROP_EXPORT prop_data_t +prop_data_create_nocopy(const void *v, size_t size) +{ + + /* Tolerate the creation of empty data objects. */ + if (v == NULL || size == 0) { + v = NULL; + size = 0; + } + + return _prop_data_instantiate(PD_F_NOCOPY, v, size); +} + /* * prop_data_copy -- * Copy a data container. If the original data is external, then * the copy is also references the same external data. */ -prop_data_t +_PROP_EXPORT prop_data_t prop_data_copy(prop_data_t opd) { prop_data_t pd; @@ -261,22 +331,19 @@ prop_data_copy(prop_data_t opd) if (! prop_object_is_data(opd)) return (NULL); - pd = _prop_data_alloc(); + if ((opd->pd_flags & PD_F_NOCOPY) != 0 || + (opd->pd_flags & PD_F_MUTABLE) == 0) { + /* Just retain and return the original. */ + prop_object_retain(opd); + return (opd); + } + + pd = prop_data_create_copy(opd->pd_immutable, opd->pd_size); if (pd != NULL) { - pd->pd_size = opd->pd_size; - pd->pd_flags = opd->pd_flags; - if (opd->pd_flags & PD_F_NOCOPY) - pd->pd_immutable = opd->pd_immutable; - else if (opd->pd_size != 0) { - void *nv = _PROP_MALLOC(pd->pd_size, M_PROP_DATA); - if (nv == NULL) { - prop_object_release(pd); - return (NULL); - } - memcpy(nv, opd->pd_immutable, opd->pd_size); - pd->pd_mutable = nv; - } + /* Preserve deprecated mutability semantics. */ + pd->pd_flags |= PD_F_MUTABLE; } + return (pd); } @@ -284,7 +351,7 @@ prop_data_copy(prop_data_t opd) * prop_data_size -- * Return the size of the data. */ -size_t +_PROP_EXPORT size_t prop_data_size(prop_data_t pd) { @@ -295,12 +362,47 @@ prop_data_size(prop_data_t pd) } /* - * prop_data_data -- - * Return a copy of the contents of the data container. - * The data is allocated with the M_TEMP malloc type. - * If the data container is empty, NULL is returned. + * prop_data_value -- + * Returns a pointer to the data object's value. This pointer + * remains valid only as long as the data object. */ -void * +_PROP_EXPORT const void * +prop_data_value(prop_data_t pd) +{ + + if (! prop_object_is_data(pd)) + return (0); + + return (pd->pd_immutable); +} + +/* + * prop_data_copy_value -- + * Copy the data object's value into the supplied buffer. + */ +_PROP_EXPORT bool +prop_data_copy_value(prop_data_t pd, void *buf, size_t buflen) +{ + + if (! prop_object_is_data(pd)) + return (false); + + if (buf == NULL || buflen < pd->pd_size) + return (false); + + /* Tolerate empty data objects. */ + if (pd->pd_immutable == NULL || pd->pd_size == 0) + return (false); + + memcpy(buf, pd->pd_immutable, pd->pd_size); + + return (true); +} + +_PROP_DEPRECATED(prop_data_data, + "this program uses prop_data_data(), " + "which is deprecated; use prop_data_copy_value() instead.") +_PROP_EXPORT void * prop_data_data(prop_data_t pd) { void *v; @@ -318,33 +420,24 @@ prop_data_data(prop_data_t pd) v = _PROP_MALLOC(pd->pd_size, M_TEMP); if (v != NULL) memcpy(v, pd->pd_immutable, pd->pd_size); - + return (v); } -/* - * prop_data_data_nocopy -- - * Return an immutable reference to the contents of the data - * container. - */ -const void * +_PROP_DEPRECATED(prop_data_data_nocopy, + "this program uses prop_data_data_nocopy(), " + "which is deprecated; use prop_data_value() instead.") +_PROP_EXPORT const void * prop_data_data_nocopy(prop_data_t pd) { - - if (! prop_object_is_data(pd)) - return (NULL); - - _PROP_ASSERT((pd->pd_size == 0 && pd->pd_immutable == NULL) || - (pd->pd_size != 0 && pd->pd_immutable != NULL)); - - return (pd->pd_immutable); + return prop_data_value(pd); } /* * prop_data_equals -- - * Return true if two strings are equivalent. + * Return true if two data objects are equivalent. */ -bool +_PROP_EXPORT bool prop_data_equals(prop_data_t pd1, prop_data_t pd2) { if (!prop_object_is_data(pd1) || !prop_object_is_data(pd2)) @@ -358,15 +451,16 @@ prop_data_equals(prop_data_t pd1, prop_data_t pd2) * Return true if the contained data is equivalent to the specified * external data. */ -bool +_PROP_EXPORT bool prop_data_equals_data(prop_data_t pd, const void *v, size_t size) { if (! prop_object_is_data(pd)) return (false); - if (pd->pd_size != size) + if (pd->pd_size != size || v == NULL) return (false); + return (memcmp(pd->pd_immutable, v, size) == 0); } @@ -376,9 +470,9 @@ _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx, const char **cpp) { const char *src; - size_t tarindex; + size_t tarindex, pos; int state, ch; - const char *pos; + const char *posptr; state = 0; tarindex = 0; @@ -397,17 +491,17 @@ _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx, if (ch == _prop_data_pad64) break; - pos = strchr(_prop_data_base64, ch); - if (pos == NULL) + posptr = strchr(_prop_data_base64, ch); + if (posptr == NULL) return (false); + pos = posptr - _prop_data_base64; switch (state) { case 0: if (target) { if (tarindex >= targsize) return (false); - target[tarindex] = - (uint8_t)((pos - _prop_data_base64) << 2); + target[tarindex] = (uint8_t)(pos << 2); } state = 1; break; @@ -416,11 +510,8 @@ _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx, if (target) { if (tarindex + 1 >= targsize) return (false); - target[tarindex] |= - (uint32_t)(pos - _prop_data_base64) >> 4; - target[tarindex + 1] = - (uint8_t)(((pos - _prop_data_base64) & 0xf) - << 4); + target[tarindex] |= (uint8_t)(pos >> 4); + target[tarindex + 1] = (pos & 0xf) << 4; } tarindex++; state = 2; @@ -430,11 +521,8 @@ _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx, if (target) { if (tarindex + 1 >= targsize) return (false); - target[tarindex] |= - (uint32_t)(pos - _prop_data_base64) >> 2; - target[tarindex + 1] = - (uint8_t)(((pos - _prop_data_base64) - & 0x3) << 6); + target[tarindex] |= (uint8_t)(pos >> 2); + target[tarindex + 1] = (pos & 0x3) << 6; } tarindex++; state = 3; @@ -444,15 +532,14 @@ _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx, if (target) { if (tarindex >= targsize) return (false); - target[tarindex] |= (uint8_t) - (pos - _prop_data_base64); + target[tarindex] |= (uint8_t)pos; } tarindex++; state = 0; break; default: - _PROP_ASSERT(/*CONSTCOND*/0); + _PROP_ASSERT(0); } } @@ -482,8 +569,9 @@ _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx, /* Make sure there is another trailing = */ if (ch != _prop_data_pad64) return (false); + ch = (unsigned char) *src; /* FALLTHROUGH */ - + case 3: /* Valid, two bytes of info */ /* * We know this char is a =. Is there anything but @@ -535,7 +623,12 @@ _prop_data_internalize(prop_stack_t stack, prop_object_t *obj, uint8_t *buf; size_t len, alen; - /* + /* No JSON binary data object representation. */ + if (ctx->poic_format == PROP_FORMAT_JSON) { + return true; + } + + /* * We don't accept empty elements. * This actually only checks for the node to be * (Which actually causes another error if found.) @@ -565,8 +658,6 @@ _prop_data_internalize(prop_stack_t stack, prop_object_t *obj, NULL) == false) return (true); - if (len >= SIZE_MAX) - len = 1; /* * Always allocate one extra in case we don't land on an even byte * boundary during the decode. @@ -574,7 +665,7 @@ _prop_data_internalize(prop_stack_t stack, prop_object_t *obj, buf = _PROP_MALLOC(len + 1, M_PROP_DATA); if (buf == NULL) return (true); - + if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen, &ctx->poic_cp) == false) { _PROP_FREE(buf, M_PROP_DATA); @@ -585,14 +676,8 @@ _prop_data_internalize(prop_stack_t stack, prop_object_t *obj, return (true); } - if (_prop_object_internalize_find_tag(ctx, "data", - _PROP_TAG_TYPE_END) == false) { - _PROP_FREE(buf, M_PROP_DATA); - return (true); - } - - data = _prop_data_alloc(); - if (data == NULL) { + if (_prop_xml_intern_find_tag(ctx, "data", + _PROP_TAG_TYPE_END) == false) { _PROP_FREE(buf, M_PROP_DATA); return (true); } @@ -603,11 +688,12 @@ _prop_data_internalize(prop_stack_t stack, prop_object_t *obj, */ if (alen == 0) { _PROP_FREE(buf, M_PROP_DATA); - data->pd_mutable = NULL; - } else { - data->pd_mutable = buf; + buf = NULL; } - data->pd_size = len; + + data = _prop_data_instantiate(0, buf, len); + if (data == NULL && buf != NULL) + _PROP_FREE(buf, M_PROP_DATA); *obj = data; return (true); diff --git a/lib/portableproplib/prop_dictionary.c b/lib/portableproplib/prop_dictionary.c index 715eb3ba8..be8da991a 100644 --- a/lib/portableproplib/prop_dictionary.c +++ b/lib/portableproplib/prop_dictionary.c @@ -1,7 +1,7 @@ -/* $NetBSD: prop_dictionary.c,v 1.39 2013/10/18 18:26:20 martin Exp $ */ +/* $NetBSD: prop_dictionary.c,v 1.51 2025/05/14 03:25:46 thorpej Exp $ */ /*- - * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc. + * Copyright (c) 2006, 2007, 2020, 2025 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -33,7 +33,8 @@ #include #include #include -#include "prop_rb_impl.h" + +#include "rbtree.h" #include @@ -95,6 +96,26 @@ struct _prop_dictionary { _PROP_POOL_INIT(_prop_dictionary_pool, sizeof(struct _prop_dictionary), "propdict") +_PROP_MALLOC_DEFINE(M_PROP_DICT, "prop dictionary", + "property dictionary container object") + +static const struct _prop_object_type_tags _prop_dictionary_type_tags = { + .xml_tag = "dict", + .json_open_tag = "{", + .json_close_tag = "}", + .json_empty_sep = " ", +}; + +static const struct _prop_object_type_tags _prop_dict_key_type_tags = { + .xml_tag = "key", + .json_open_tag = "\"", + .json_close_tag = "\"", +}; + +struct _prop_dictionary_iterator { + struct _prop_object_iterator pdi_base; + unsigned int pdi_index; +}; static _prop_object_free_rv_t _prop_dictionary_free(prop_stack_t, prop_object_t *); @@ -107,7 +128,7 @@ static _prop_object_equals_rv_t void **, void **, prop_object_t *, prop_object_t *); static void _prop_dictionary_equals_finish(prop_object_t, prop_object_t); -static prop_object_iterator_t +static struct _prop_dictionary_iterator * _prop_dictionary_iterator_locked(prop_dictionary_t); static prop_object_t _prop_dictionary_iterator_next_object_locked(void *); @@ -128,7 +149,7 @@ static const struct _prop_object_type _prop_object_type_dictionary = { .pot_equals = _prop_dictionary_equals, .pot_equals_finish = _prop_dictionary_equals_finish, .pot_lock = _prop_dictionary_lock, - .pot_unlock = _prop_dictionary_unlock, + .pot_unlock = _prop_dictionary_unlock, }; static _prop_object_free_rv_t @@ -156,11 +177,6 @@ static const struct _prop_object_type _prop_object_type_dict_keysym = { #define prop_dictionary_is_immutable(x) \ (((x)->pd_flags & PD_F_IMMUTABLE) != 0) -struct _prop_dictionary_iterator { - struct _prop_object_iterator pdi_base; - unsigned int pdi_index; -}; - /* * Dictionary key symbols are immutable, and we are likely to have many * duplicated key symbols. So, to save memory, we unique'ify key symbols @@ -206,7 +222,7 @@ _prop_dict_init(void) { _PROP_MUTEX_INIT(_prop_dict_keysym_tree_mutex); - _prop_rb_tree_init(&_prop_dict_keysym_tree, + rb_tree_init(&_prop_dict_keysym_tree, &_prop_dict_keysym_rb_tree_ops); return 0; } @@ -231,7 +247,7 @@ _prop_dict_keysym_free(prop_stack_t stack, prop_object_t *obj) { prop_dictionary_keysym_t pdk = *obj; - _prop_rb_tree_remove_node(&_prop_dict_keysym_tree, pdk); + rb_tree_remove_node(&_prop_dict_keysym_tree, pdk); _prop_dict_keysym_put(pdk); return _PROP_OBJECT_FREE_DONE; @@ -247,13 +263,8 @@ _prop_dict_keysym_externalize(struct _prop_object_externalize_context *ctx, _PROP_ASSERT(pdk->pdk_key[0] != '\0'); - if (_prop_object_externalize_start_tag(ctx, "string") == false || - _prop_object_externalize_append_encoded_cstring(ctx, - pdk->pdk_key) == false || - _prop_object_externalize_end_tag(ctx, "string") == false) - return (false); - - return (true); + return _prop_string_externalize_internal(ctx, &_prop_dict_key_type_tags, + pdk->pdk_key); } /* ARGSUSED */ @@ -288,7 +299,7 @@ _prop_dict_keysym_alloc(const char *key) * we just retain it and return it. */ _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex); - opdk = _prop_rb_tree_find(&_prop_dict_keysym_tree, key); + opdk = rb_tree_find_node(&_prop_dict_keysym_tree, key); if (opdk != NULL) { prop_object_retain(opdk); _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex); @@ -324,14 +335,14 @@ _prop_dict_keysym_alloc(const char *key) * we have to check again if it is in the tree. */ _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex); - opdk = _prop_rb_tree_find(&_prop_dict_keysym_tree, key); + opdk = rb_tree_find_node(&_prop_dict_keysym_tree, key); if (opdk != NULL) { prop_object_retain(opdk); _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex); _prop_dict_keysym_put(pdk); return (opdk); } - rpdk = _prop_rb_tree_insert_node(&_prop_dict_keysym_tree, pdk); + rpdk = rb_tree_insert_node(&_prop_dict_keysym_tree, pdk); _PROP_ASSERT(rpdk == pdk); _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex); return (rpdk); @@ -418,6 +429,38 @@ _prop_dictionary_emergency_free(prop_object_t obj) prop_object_release(pdk); } +static bool +_prop_dictionary_externalize_one(struct _prop_object_externalize_context *ctx, + prop_dictionary_keysym_t pdk, struct _prop_object *po) +{ + if (po == NULL) { + return false; + } + + if (_prop_string_externalize_internal(ctx, + &_prop_dict_key_type_tags, + pdk->pdk_key) == false) { + return false; + } + + switch (ctx->poec_format) { + case PROP_FORMAT_JSON: + if (_prop_extern_append_cstring(ctx, ": ") == false) { + return false; + } + break; + + default: /* XML */ + if (_prop_extern_end_line(ctx, NULL) == false || + _prop_extern_start_line(ctx) == false) { + return false; + } + break; + } + + return (*po->po_type->pot_extern)(ctx, po); +} + static bool _prop_dictionary_externalize(struct _prop_object_externalize_context *ctx, void *v) @@ -425,52 +468,57 @@ _prop_dictionary_externalize(struct _prop_object_externalize_context *ctx, prop_dictionary_t pd = v; prop_dictionary_keysym_t pdk; struct _prop_object *po; - prop_object_iterator_t pi; - unsigned int i; + struct _prop_dictionary_iterator *pdi; bool rv = false; + const char * const sep = + ctx->poec_format == PROP_FORMAT_JSON ? "," : NULL; + + _PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML || + ctx->poec_format == PROP_FORMAT_JSON); _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); if (pd->pd_count == 0) { _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); - return (_prop_object_externalize_empty_tag(ctx, "dict")); + return (_prop_extern_append_empty_tag(ctx, + &_prop_dictionary_type_tags)); } - if (_prop_object_externalize_start_tag(ctx, "dict") == false || - _prop_object_externalize_append_char(ctx, '\n') == false) + if (_prop_extern_append_start_tag(ctx, + &_prop_dictionary_type_tags, NULL) == false || + _prop_extern_end_line(ctx, NULL) == false) goto out; - pi = _prop_dictionary_iterator_locked(pd); - if (pi == NULL) + pdi = _prop_dictionary_iterator_locked(pd); + if (pdi == NULL) goto out; - + ctx->poec_depth++; _PROP_ASSERT(ctx->poec_depth != 0); - while ((pdk = _prop_dictionary_iterator_next_object_locked(pi)) + while ((pdk = _prop_dictionary_iterator_next_object_locked(pdi)) != NULL) { po = _prop_dictionary_get_keysym(pd, pdk, true); - if (po == NULL || - _prop_object_externalize_start_tag(ctx, "key") == false || - _prop_object_externalize_append_encoded_cstring(ctx, - pdk->pdk_key) == false || - _prop_object_externalize_end_tag(ctx, "key") == false || - (*po->po_type->pot_extern)(ctx, po) == false) { - prop_object_iterator_release(pi); + if (_prop_extern_start_line(ctx) == false || + _prop_dictionary_externalize_one(ctx, pdk, po) == false || + _prop_extern_end_line(ctx, + pdi->pdi_index < pd->pd_count ? + sep : NULL) == false) { + prop_object_iterator_release(&pdi->pdi_base); goto out; } } - prop_object_iterator_release(pi); + prop_object_iterator_release(&pdi->pdi_base); ctx->poec_depth--; - for (i = 0; i < ctx->poec_depth; i++) { - if (_prop_object_externalize_append_char(ctx, '\t') == false) - goto out; - } - if (_prop_object_externalize_end_tag(ctx, "dict") == false) + if (_prop_extern_start_line(ctx) == false || + _prop_extern_append_end_tag(ctx, + &_prop_dictionary_type_tags) == false) { + goto out; - + } + rv = true; out: @@ -592,7 +640,7 @@ _prop_dictionary_expand(prop_dictionary_t pd, unsigned int capacity) if (oarray != NULL) _PROP_FREE(oarray, M_PROP_DICT); - + return (true); } @@ -662,7 +710,7 @@ _prop_dictionary_iterator_reset(void *v) * prop_dictionary_create -- * Create a dictionary. */ -prop_dictionary_t +_PROP_EXPORT prop_dictionary_t prop_dictionary_create(void) { @@ -673,7 +721,7 @@ prop_dictionary_create(void) * prop_dictionary_create_with_capacity -- * Create a dictionary with the capacity to store N objects. */ -prop_dictionary_t +_PROP_EXPORT prop_dictionary_t prop_dictionary_create_with_capacity(unsigned int capacity) { @@ -684,10 +732,10 @@ prop_dictionary_create_with_capacity(unsigned int capacity) * prop_dictionary_copy -- * Copy a dictionary. The new dictionary has an initial capacity equal * to the number of objects stored int the original dictionary. The new - * dictionary contains refrences to the original dictionary's objects, + * dictionary contains references to the original dictionary's objects, * not copies of those objects (i.e. a shallow copy). */ -prop_dictionary_t +_PROP_EXPORT prop_dictionary_t prop_dictionary_copy(prop_dictionary_t opd) { prop_dictionary_t pd; @@ -724,7 +772,7 @@ prop_dictionary_copy(prop_dictionary_t opd) * Like prop_dictionary_copy(), but the resulting dictionary is * mutable. */ -prop_dictionary_t +_PROP_EXPORT prop_dictionary_t prop_dictionary_copy_mutable(prop_dictionary_t opd) { prop_dictionary_t pd; @@ -743,7 +791,7 @@ prop_dictionary_copy_mutable(prop_dictionary_t opd) * prop_dictionary_make_immutable -- * Set the immutable flag on that dictionary. */ -void +_PROP_EXPORT void prop_dictionary_make_immutable(prop_dictionary_t pd) { @@ -757,7 +805,7 @@ prop_dictionary_make_immutable(prop_dictionary_t pd) * prop_dictionary_count -- * Return the number of objects stored in the dictionary. */ -unsigned int +_PROP_EXPORT unsigned int prop_dictionary_count(prop_dictionary_t pd) { unsigned int rv; @@ -778,7 +826,7 @@ prop_dictionary_count(prop_dictionary_t pd) * total number of objects (including the objects already stored in * the dictionary). */ -bool +_PROP_EXPORT bool prop_dictionary_ensure_capacity(prop_dictionary_t pd, unsigned int capacity) { bool rv; @@ -795,7 +843,7 @@ prop_dictionary_ensure_capacity(prop_dictionary_t pd, unsigned int capacity) return (rv); } -static prop_object_iterator_t +static struct _prop_dictionary_iterator * _prop_dictionary_iterator_locked(prop_dictionary_t pd) { struct _prop_dictionary_iterator *pdi; @@ -812,7 +860,7 @@ _prop_dictionary_iterator_locked(prop_dictionary_t pd) pdi->pdi_base.pi_obj = pd; _prop_dictionary_iterator_reset_locked(pdi); - return (&pdi->pdi_base); + return pdi; } /* @@ -820,15 +868,15 @@ _prop_dictionary_iterator_locked(prop_dictionary_t pd) * Return an iterator for the dictionary. The dictionary is retained by * the iterator. */ -prop_object_iterator_t +_PROP_EXPORT prop_object_iterator_t prop_dictionary_iterator(prop_dictionary_t pd) { - prop_object_iterator_t pi; + struct _prop_dictionary_iterator *pdi; _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); - pi = _prop_dictionary_iterator_locked(pd); + pdi = _prop_dictionary_iterator_locked(pd); _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); - return (pi); + return &pdi->pdi_base; } /* @@ -836,7 +884,7 @@ prop_dictionary_iterator(prop_dictionary_t pd) * Return an array containing a snapshot of all of the keys * in the dictionary. */ -prop_array_t +_PROP_EXPORT prop_array_t prop_dictionary_all_keys(prop_dictionary_t pd) { prop_array_t array; @@ -910,22 +958,24 @@ _prop_dictionary_get(prop_dictionary_t pd, const char *key, bool locked) if (! prop_object_is_dictionary(pd)) return (NULL); - if (!locked) + if (!locked) { _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); + } pde = _prop_dict_lookup(pd, key, NULL); if (pde != NULL) { _PROP_ASSERT(pde->pde_objref != NULL); po = pde->pde_objref; } - if (!locked) + if (!locked) { _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); + } return (po); } /* * prop_dictionary_get -- * Return the object stored with specified key. */ -prop_object_t +_PROP_EXPORT prop_object_t prop_dictionary_get(prop_dictionary_t pd, const char *key) { prop_object_t po = NULL; @@ -955,7 +1005,7 @@ _prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk, * prop_dictionary_get_keysym -- * Return the object stored at the location encoded by the keysym. */ -prop_object_t +_PROP_EXPORT prop_object_t prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk) { @@ -965,9 +1015,9 @@ prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk) /* * prop_dictionary_set -- * Store a reference to an object at with the specified key. - * If the key already exisit, the original object is released. + * If the key already exist, the original object is released. */ -bool +_PROP_EXPORT bool prop_dictionary_set(prop_dictionary_t pd, const char *key, prop_object_t po) { struct _prop_dict_entry *pde; @@ -1063,7 +1113,7 @@ prop_dictionary_set(prop_dictionary_t pd, const char *key, prop_object_t po) * Replace the object in the dictionary at the location encoded by * the keysym. */ -bool +_PROP_EXPORT bool prop_dictionary_set_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk, prop_object_t po) { @@ -1107,7 +1157,7 @@ _prop_dictionary_remove(prop_dictionary_t pd, struct _prop_dict_entry *pde, * Remove the reference to an object with the specified key from * the dictionary. */ -void +_PROP_EXPORT void prop_dictionary_remove(prop_dictionary_t pd, const char *key) { struct _prop_dict_entry *pde; @@ -1137,7 +1187,7 @@ prop_dictionary_remove(prop_dictionary_t pd, const char *key) * Remove a reference to an object stored in the dictionary at the * location encoded by the keysym. */ -void +_PROP_EXPORT void prop_dictionary_remove_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk) { @@ -1154,7 +1204,7 @@ prop_dictionary_remove_keysym(prop_dictionary_t pd, * Return true if the two dictionaries are equivalent. Note we do a * by-value comparison of the objects in the dictionary. */ -bool +_PROP_EXPORT bool prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2) { if (!prop_object_is_dictionary(dict1) || @@ -1165,10 +1215,23 @@ prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2) } /* - * prop_dictionary_keysym_cstring_nocopy -- - * Return an immutable reference to the keysym's value. + * prop_dictionary_keysym_value -- + * Return a reference to the keysym's value. */ -const char * +_PROP_EXPORT const char * +prop_dictionary_keysym_value(prop_dictionary_keysym_t pdk) +{ + + if (! prop_object_is_dictionary_keysym(pdk)) + return (NULL); + + return (pdk->pdk_key); +} + +_PROP_DEPRECATED(prop_dictionary_keysym_cstring_nocopy, + "this program uses prop_dictionary_keysym_cstring_nocopy(), " + "which is deprecated; use prop_dictionary_keysym_value() instead.") +_PROP_EXPORT const char * prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk) { @@ -1183,7 +1246,7 @@ prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk) * Return true if the two dictionary key symbols are equivalent. * Note: We do not compare the object references. */ -bool +_PROP_EXPORT bool prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1, prop_dictionary_keysym_t pdk2) { @@ -1196,33 +1259,12 @@ prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1, /* * prop_dictionary_externalize -- - * Externalize a dictionary, returning a NUL-terminated buffer - * containing the XML-style representation. The buffer is allocated - * with the M_TEMP memory type. + * Externalize a dictionary in XML format. */ -char * +_PROP_EXPORT char * prop_dictionary_externalize(prop_dictionary_t pd) { - struct _prop_object_externalize_context *ctx; - char *cp; - - ctx = _prop_object_externalize_context_alloc(); - if (ctx == NULL) - return (NULL); - - if (_prop_object_externalize_header(ctx) == false || - (*pd->pd_obj.po_type->pot_extern)(ctx, pd) == false || - _prop_object_externalize_footer(ctx) == false) { - /* We are responsible for releasing the buffer. */ - _PROP_FREE(ctx->poec_buf, M_TEMP); - _prop_object_externalize_context_free(ctx); - return (NULL); - } - - cp = ctx->poec_buf; - _prop_object_externalize_context_free(ctx); - - return (cp); + return _prop_object_externalize(&pd->pd_obj, PROP_FORMAT_XML); } /* @@ -1298,8 +1340,30 @@ _prop_dictionary_internalize_continue(prop_stack_t stack, prop_object_t *obj, /* * key, value was added, now continue looking for the next key - * or the closing tag. + * or the closing tag. For JSON, we'll skip the comma separator, + * if present. + * + * By doing this here, we correctly error out if a separator + * is found other than after an element, but this does mean + * that we do allow a trailing comma after the final element + * which isn't allowed in the JSON spec, but seems pretty + * harmless (and there are other JSON parsers that also allow + * it). + * + * Conversely, we don't want to *require* the separator if the + * spec doesn't require it, and we don't know what's next in + * the buffer, so we basically treat the separator as completely + * optional. Since there does not appear to be any ambiguity, + * this also seems pretty harmless. + * + * (FWIW, RFC 8259 section 9 seems to specifically allow this.) */ + if (ctx->poic_format == PROP_FORMAT_JSON) { + ctx->poic_cp = _prop_intern_skip_whitespace(ctx->poic_cp); + if (*ctx->poic_cp == ',') { + ctx->poic_cp++; + } + } return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey); } @@ -1310,39 +1374,78 @@ _prop_dictionary_internalize_body(prop_stack_t stack, prop_object_t *obj, prop_dictionary_t dict = *obj; size_t keylen; - /* Fetch the next tag. */ - if (_prop_object_internalize_find_tag(ctx, NULL, _PROP_TAG_TYPE_EITHER) == false) - goto bad; + if (ctx->poic_format == PROP_FORMAT_JSON) { + ctx->poic_cp = _prop_intern_skip_whitespace(ctx->poic_cp); - /* Check to see if this is the end of the dictionary. */ - if (_PROP_TAG_MATCH(ctx, "dict") && - ctx->poic_tag_type == _PROP_TAG_TYPE_END) { - _PROP_FREE(tmpkey, M_TEMP); - return (true); - } + /* Check to see if this is the end of the dictionary. */ + if (*ctx->poic_cp == '}') { + /* It is, so don't iterate any further. */ + ctx->poic_cp++; + return true; + } + + /* It must be the key. */ + if (*ctx->poic_cp != '"') { + goto bad; + } + ctx->poic_cp++; + + /* Empty keys are not allowed. */ + if (*ctx->poic_cp == '"') { + goto bad; + } + } else { + /* Fetch the next tag. */ + if (_prop_xml_intern_find_tag(ctx, NULL, + _PROP_TAG_TYPE_EITHER) == false) + goto bad; + + /* Check to see if this is the end of the dictionary. */ + if (_PROP_TAG_MATCH(ctx, "dict") && + ctx->poic_tag_type == _PROP_TAG_TYPE_END) { + _PROP_FREE(tmpkey, M_TEMP); + return (true); + } - /* Ok, it must be a non-empty key start tag. */ - if (!_PROP_TAG_MATCH(ctx, "key") || - ctx->poic_tag_type != _PROP_TAG_TYPE_START || - ctx->poic_is_empty_element) - goto bad; + /* Ok, it must be a non-empty key start tag. */ + if (!_PROP_TAG_MATCH(ctx, "key") || + ctx->poic_tag_type != _PROP_TAG_TYPE_START || + ctx->poic_is_empty_element) + goto bad; + } - if (_prop_object_internalize_decode_string(ctx, - tmpkey, PDK_MAXKEY, &keylen, - &ctx->poic_cp) == false) + if (_prop_intern_decode_string(ctx, tmpkey, PDK_MAXKEY, &keylen, + &ctx->poic_cp) == false) goto bad; _PROP_ASSERT(keylen <= PDK_MAXKEY); tmpkey[keylen] = '\0'; - if (_prop_object_internalize_find_tag(ctx, "key", - _PROP_TAG_TYPE_END) == false) - goto bad; - - /* ..and now the beginning of the value. */ - if (_prop_object_internalize_find_tag(ctx, NULL, - _PROP_TAG_TYPE_START) == false) - goto bad; + if (ctx->poic_format == PROP_FORMAT_JSON) { + if (*ctx->poic_cp != '"') { + goto bad; + } + ctx->poic_cp++; + + /* + * Next thing we counter needs to be the key/value + * separator. + */ + ctx->poic_cp = _prop_intern_skip_whitespace(ctx->poic_cp); + if (*ctx->poic_cp != ':') { + goto bad; + } + ctx->poic_cp++; + } else { + if (_prop_xml_intern_find_tag(ctx, "key", + _PROP_TAG_TYPE_END) == false) + goto bad; + + /* ..and now the beginning of the value. */ + if (_prop_xml_intern_find_tag(ctx, NULL, + _PROP_TAG_TYPE_START) == false) + goto bad; + } /* * Key is found, now wait for value to be parsed. @@ -1361,54 +1464,32 @@ _prop_dictionary_internalize_body(prop_stack_t stack, prop_object_t *obj, /* * prop_dictionary_internalize -- - * Create a dictionary by parsing the NUL-terminated XML-style - * representation. + * Create a dictionary by parsing the external representation. */ -prop_dictionary_t -prop_dictionary_internalize(const char *xml) +_PROP_EXPORT prop_dictionary_t +prop_dictionary_internalize(const char *data) { - return _prop_generic_internalize(xml, "dict"); + return _prop_object_internalize(data, &_prop_dictionary_type_tags); } /* * prop_dictionary_externalize_to_file -- * Externalize a dictionary to the specified file. */ -bool +_PROP_EXPORT bool prop_dictionary_externalize_to_file(prop_dictionary_t dict, const char *fname) { - char *xml; - bool rv; - int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */ - - xml = prop_dictionary_externalize(dict); - if (xml == NULL) - return (false); - rv = _prop_object_externalize_write_file(fname, xml, strlen(xml), false); - if (rv == false) - save_errno = errno; - _PROP_FREE(xml, M_TEMP); - if (rv == false) - errno = save_errno; - - return (rv); + return _prop_object_externalize_to_file(&dict->pd_obj, fname, + PROP_FORMAT_XML); } /* * prop_dictionary_internalize_from_file -- * Internalize a dictionary from a file. */ -prop_dictionary_t +_PROP_EXPORT prop_dictionary_t prop_dictionary_internalize_from_file(const char *fname) { - struct _prop_object_internalize_mapped_file *mf; - prop_dictionary_t dict; - - mf = _prop_object_internalize_map_file(fname); - if (mf == NULL) - return (NULL); - dict = prop_dictionary_internalize(mf->poimf_xml); - _prop_object_internalize_unmap_file(mf); - - return (dict); + return _prop_object_internalize_from_file(fname, + &_prop_dictionary_type_tags); } diff --git a/lib/portableproplib/prop_dictionary_util.c b/lib/portableproplib/prop_dictionary_util.c index 65baf73cc..7570fd3d4 100644 --- a/lib/portableproplib/prop_dictionary_util.c +++ b/lib/portableproplib/prop_dictionary_util.c @@ -1,7 +1,7 @@ -/* $NetBSD: prop_dictionary_util.c,v 1.4 2011/03/24 17:05:39 bouyer Exp $ */ +/* $NetBSD: prop_dictionary_util.c,v 1.10 2025/04/26 17:13:23 thorpej Exp $ */ /*- - * Copyright (c) 2006 The NetBSD Foundation, Inc. + * Copyright (c) 2006, 2020 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -38,195 +38,279 @@ * exactly what we're doing here. */ -#include #include "prop_object_impl.h" /* only to hide kernel vs. not-kernel */ +#include -bool -prop_dictionary_get_dict(prop_dictionary_t dict, const char *key, prop_dictionary_t *dp) +_PROP_EXPORT bool +prop_dictionary_get_dict(prop_dictionary_t dict, const char *key, + prop_dictionary_t *dp) { prop_object_t o; + o = prop_dictionary_get(dict, key); - if (o == NULL || prop_object_type(o) != PROP_TYPE_DICTIONARY) + if (prop_object_type(o) != PROP_TYPE_DICTIONARY) return false; *dp = o; return true; } -bool -prop_dictionary_get_bool(prop_dictionary_t dict, - const char *key, - bool *valp) +_PROP_EXPORT bool +prop_dictionary_get_bool(prop_dictionary_t dict, const char *key, bool *valp) { prop_bool_t b; b = prop_dictionary_get(dict, key); if (prop_object_type(b) != PROP_TYPE_BOOL) return (false); - + *valp = prop_bool_true(b); return (true); } -bool -prop_dictionary_set_bool(prop_dictionary_t dict, - const char *key, - bool val) +_PROP_EXPORT bool +prop_dictionary_set_bool(prop_dictionary_t dict, const char *key, bool val) { - prop_bool_t b; - int rv; - b = prop_bool_create(val); - if (b == NULL) - return (false); - rv = prop_dictionary_set(dict, key, b); - prop_object_release(b); - - return (rv); + return prop_dictionary_set_and_rel(dict, key, prop_bool_create(val)); } -#define TEMPLATE(size) \ -bool \ -prop_dictionary_get_int ## size (prop_dictionary_t dict, \ - const char *key, \ - int ## size ## _t *valp) \ -{ \ - prop_number_t num; \ - \ - num = prop_dictionary_get(dict, key); \ - if (prop_object_type(num) != PROP_TYPE_NUMBER) \ - return (false); \ - \ - if (prop_number_unsigned(num) && \ - prop_number_unsigned_integer_value(num) > \ - /*CONSTCOND*/((size) == 8 ? INT8_MAX : \ - (size) == 16 ? INT16_MAX : \ - (size) == 32 ? INT32_MAX : INT64_MAX)) { \ - return (false); \ - } \ - \ - if (prop_number_size(num) > (size)) \ - return (false); \ - \ - *valp = (int ## size ## _t) prop_number_integer_value(num); \ - \ - return (true); \ -} \ - \ -bool \ -prop_dictionary_get_uint ## size (prop_dictionary_t dict, \ - const char *key, \ - uint ## size ## _t *valp) \ +#define TEMPLATE(name, typ) \ +_PROP_EXPORT bool \ +prop_dictionary_get_ ## name (prop_dictionary_t dict, \ + const char *key, \ + typ *valp) \ { \ - prop_number_t num; \ - \ - num = prop_dictionary_get(dict, key); \ - if (prop_object_type(num) != PROP_TYPE_NUMBER) \ - return (false); \ - \ - if (prop_number_unsigned(num) == false && \ - prop_number_integer_value(num) < 0) { \ - return (false); \ - } \ - \ - if (prop_number_size(num) > (size)) \ - return (false); \ - \ - *valp = (uint ## size ## _t) \ - prop_number_unsigned_integer_value(num); \ - \ - return (true); \ -} \ - \ -bool \ -prop_dictionary_set_int ## size (prop_dictionary_t dict, \ - const char *key, \ - int ## size ## _t val) \ -{ \ - prop_number_t num; \ - int rv; \ - \ - num = prop_number_create_integer((int64_t) val); \ - if (num == NULL) \ - return (false); \ - rv = prop_dictionary_set(dict, key, num); \ - prop_object_release(num); \ - \ - return (rv); \ -} \ - \ -bool \ -prop_dictionary_set_uint ## size (prop_dictionary_t dict, \ - const char *key, \ - uint ## size ## _t val) \ -{ \ - prop_number_t num; \ - int rv; \ - \ - num = prop_number_create_unsigned_integer((uint64_t) val); \ - if (num == NULL) \ - return (false); \ - rv = prop_dictionary_set(dict, key, num); \ - prop_object_release(num); \ - \ - return (rv); \ -} - -TEMPLATE(8) -TEMPLATE(16) -TEMPLATE(32) -TEMPLATE(64) + return prop_number_ ## name ## _value( \ + prop_dictionary_get(dict, key), valp); \ +} +TEMPLATE(schar, signed char) +TEMPLATE(short, short) +TEMPLATE(int, int) +TEMPLATE(long, long) +TEMPLATE(longlong, long long) +TEMPLATE(intptr, intptr_t) +TEMPLATE(int8, int8_t) +TEMPLATE(int16, int16_t) +TEMPLATE(int32, int32_t) +TEMPLATE(int64, int64_t) + +TEMPLATE(uchar, unsigned char) +TEMPLATE(ushort, unsigned short) +TEMPLATE(uint, unsigned int) +TEMPLATE(ulong, unsigned long) +TEMPLATE(ulonglong, unsigned long long) +TEMPLATE(uintptr, uintptr_t) +TEMPLATE(uint8, uint8_t) +TEMPLATE(uint16, uint16_t) +TEMPLATE(uint32, uint32_t) +TEMPLATE(uint64, uint64_t) #undef TEMPLATE -#define TEMPLATE(variant, qualifier) \ -bool \ -prop_dictionary_get_cstring ## variant (prop_dictionary_t dict, \ - const char *key, \ - qualifier char **cpp) \ -{ \ - prop_string_t str; \ - \ - str = prop_dictionary_get(dict, key); \ - if (prop_object_type(str) != PROP_TYPE_STRING) \ - return (false); \ - \ - *cpp = prop_string_cstring ## variant (str); \ - \ - return (*cpp == NULL ? false : true); \ -} \ - \ -bool \ -prop_dictionary_set_cstring ## variant (prop_dictionary_t dict, \ - const char *key, \ - const char *cp) \ +static bool +prop_dictionary_set_signed_number(prop_dictionary_t dict, const char *key, + intmax_t val) +{ + return prop_dictionary_set_and_rel(dict, key, + prop_number_create_signed(val)); +} + +static bool +prop_dictionary_set_unsigned_number(prop_dictionary_t dict, const char *key, + uintmax_t val) +{ + /*LINTED: for conversion from 'long long' to 'long'*/ \ + return prop_dictionary_set_and_rel(dict, key, + prop_number_create_unsigned(val)); +} + +#define TEMPLATE(name, which, typ) \ +_PROP_EXPORT bool \ +prop_dictionary_set_ ## name (prop_dictionary_t dict, \ + const char *key, \ + typ val) \ { \ - prop_string_t str; \ - int rv; \ - \ - str = prop_string_create_cstring ## variant (cp); \ - if (str == NULL) \ - return (false); \ - rv = prop_dictionary_set(dict, key, str); \ - prop_object_release(str); \ - \ - return (rv); \ + /*LINTED: for conversion from long long to 'long'*/ \ + return prop_dictionary_set_ ## which ## _number(dict, key, val);\ } -TEMPLATE(,) -TEMPLATE(_nocopy,const) +#define STEMPLATE(name, typ) TEMPLATE(name, signed, typ) +#define UTEMPLATE(name, typ) TEMPLATE(name, unsigned, typ) + +STEMPLATE(schar, signed char) +STEMPLATE(short, short) +STEMPLATE(int, int) +STEMPLATE(long, long) +STEMPLATE(longlong, long long) +STEMPLATE(intptr, intptr_t) +STEMPLATE(int8, int8_t) +STEMPLATE(int16, int16_t) +STEMPLATE(int32, int32_t) +STEMPLATE(int64, int64_t) +UTEMPLATE(uchar, unsigned char) +UTEMPLATE(ushort, unsigned short) +UTEMPLATE(uint, unsigned int) +UTEMPLATE(ulong, unsigned long) +UTEMPLATE(ulonglong, unsigned long long) +UTEMPLATE(uintptr, uintptr_t) +UTEMPLATE(uint8, uint8_t) +UTEMPLATE(uint16, uint16_t) +UTEMPLATE(uint32, uint32_t) +UTEMPLATE(uint64, uint64_t) + +#undef STEMPLATE +#undef UTEMPLATE #undef TEMPLATE -bool +_PROP_EXPORT bool +prop_dictionary_get_string(prop_dictionary_t dict, const char *key, + const char **cpp) +{ + prop_string_t str; + const char *cp; + + str = prop_dictionary_get(dict, key); + if (prop_object_type(str) != PROP_TYPE_STRING) + return (false); + + cp = prop_string_value(str); + if (cp == NULL) + return (false); + + *cpp = cp; + return (true); +} + +_PROP_EXPORT bool +prop_dictionary_set_string(prop_dictionary_t dict, const char *key, + const char *cp) +{ + return prop_dictionary_set_and_rel(dict, key, + prop_string_create_copy(cp)); +} + +_PROP_EXPORT bool +prop_dictionary_set_string_nocopy(prop_dictionary_t dict, + const char *key, + const char *cp) +{ + return prop_dictionary_set_and_rel(dict, key, + prop_string_create_nocopy(cp)); +} + +_PROP_EXPORT bool +prop_dictionary_get_data(prop_dictionary_t dict, const char *key, + const void **vp, size_t *sizep) +{ + prop_data_t data; + const void *v; + + data = prop_dictionary_get(dict, key); + if (prop_object_type(data) != PROP_TYPE_DATA) + return (false); + + v = prop_data_value(data); + if (v == NULL) + return (false); + + *vp = v; + if (sizep != NULL) + *sizep = prop_data_size(data); + return (true); +} + +_PROP_EXPORT bool +prop_dictionary_set_data(prop_dictionary_t dict, const char *key, + const void *v, size_t size) +{ + return prop_dictionary_set_and_rel(dict, key, + prop_data_create_copy(v, size)); +} + +_PROP_EXPORT bool +prop_dictionary_set_data_nocopy(prop_dictionary_t dict, const char *key, + const void *v, size_t size) +{ + return prop_dictionary_set_and_rel(dict, key, + prop_data_create_nocopy(v, size)); +} + +_PROP_DEPRECATED(prop_dictionary_get_cstring, + "this program uses prop_dictionary_get_cstring(), " + "which is deprecated; use prop_dictionary_get_string() and copy instead.") +_PROP_EXPORT bool +prop_dictionary_get_cstring(prop_dictionary_t dict, + const char *key, + char **cpp) +{ + prop_string_t str; + char *cp; + size_t len; + bool rv; + + str = prop_dictionary_get(dict, key); + if (prop_object_type(str) != PROP_TYPE_STRING) + return (false); + + len = prop_string_size(str); + cp = _PROP_MALLOC(len + 1, M_TEMP); + if (cp == NULL) + return (false); + + rv = prop_string_copy_value(str, cp, len + 1); + if (rv) + *cpp = cp; + else + _PROP_FREE(cp, M_TEMP); + + return (rv); +} + +_PROP_DEPRECATED(prop_string_get_cstring_nocopy, + "this program uses prop_string_get_cstring_nocopy(), " + "which is deprecated; use prop_dictionary_get_string() instead.") +_PROP_EXPORT bool +prop_dictionary_get_cstring_nocopy(prop_dictionary_t dict, + const char *key, + const char **cpp) +{ + return prop_dictionary_get_string(dict, key, cpp); +} + +_PROP_DEPRECATED(prop_dictionary_set_cstring, + "this program uses prop_dictionary_set_cstring(), " + "which is deprecated; use prop_dictionary_set_string() instead.") +_PROP_EXPORT bool +prop_dictionary_set_cstring(prop_dictionary_t dict, + const char *key, + const char *cp) +{ + return prop_dictionary_set_string(dict, key, cp); +} + +_PROP_DEPRECATED(prop_dictionary_set_cstring_nocopy, + "this program uses prop_dictionary_set_cstring_nocopy(), " + "which is deprecated; use prop_dictionary_set_string_nocopy() instead.") +_PROP_EXPORT bool +prop_dictionary_set_cstring_nocopy(prop_dictionary_t dict, + const char *key, + const char *cp) +{ + return prop_dictionary_set_string_nocopy(dict, key, cp); +} + +_PROP_EXPORT bool prop_dictionary_set_and_rel(prop_dictionary_t dict, const char *key, - prop_object_t po) + prop_object_t po) { - bool ret; + bool rv; + if (po == NULL) return false; - ret = prop_dictionary_set(dict, key, po); + rv = prop_dictionary_set(dict, key, po); prop_object_release(po); - return ret; + return rv; } diff --git a/lib/portableproplib/prop_extern.c b/lib/portableproplib/prop_extern.c new file mode 100644 index 000000000..8abe35380 --- /dev/null +++ b/lib/portableproplib/prop_extern.c @@ -0,0 +1,697 @@ +/* $NetBSD: prop_extern.c,v 1.3 2025/12/10 21:33:01 andvar Exp $ */ + +/*- + * Copyright (c) 2006, 2007, 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "prop_object_impl.h" +#include + +#include +#include +#include +#include +#include + +#define BUF_EXPAND 256 +#define PLISTTMP "/.plistXXXXXX" + +static prop_format_t _prop_format_default = PROP_FORMAT_XML; + +/* + * _prop_extern_append_char -- + * Append a single character to the externalize buffer. + */ +bool +_prop_extern_append_char( + struct _prop_object_externalize_context *ctx, unsigned char c) +{ + + _PROP_ASSERT(ctx->poec_capacity != 0); + _PROP_ASSERT(ctx->poec_buf != NULL); + _PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity); + + if (ctx->poec_len == ctx->poec_capacity) { + char *cp = _PROP_REALLOC(ctx->poec_buf, + ctx->poec_capacity + BUF_EXPAND, + M_TEMP); + if (cp == NULL) { + return false; + } + ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND; + ctx->poec_buf = cp; + } + + ctx->poec_buf[ctx->poec_len++] = c; + + return true; +} + +/* + * _prop_extern_append_cstring -- + * Append a C string to the externalize buffer. + */ +bool +_prop_extern_append_cstring( + struct _prop_object_externalize_context *ctx, const char *cp) +{ + + while (*cp != '\0') { + if (_prop_extern_append_char(ctx, + (unsigned char)*cp) == false) { + return false; + } + cp++; + } + return true; +} + +/* + * _prop_json_extern_append_encoded_cstring -- + * Append a C string to the externalize buffer, JSON-encoded. + */ +static bool +_prop_json_extern_append_escu( + struct _prop_object_externalize_context *ctx, uint16_t val) +{ + char tmpstr[sizeof("\\uXXXX")]; + + snprintf(tmpstr, sizeof(tmpstr), "\\u%04X", val); + return _prop_extern_append_cstring(ctx, tmpstr); +} + +static bool +_prop_json_extern_append_encoded_cstring( + struct _prop_object_externalize_context *ctx, const char *cp) +{ + bool esc; + unsigned char ch; + + for (; (ch = *cp) != '\0'; cp++) { + esc = true; + switch (ch) { + /* + * First, the two explicit exclusions. They must be + * escaped. + */ + case '"': /* U+0022 quotation mark */ + goto emit; + + case '\\': /* U+005C reverse solidus */ + goto emit; + + /* + * And some special cases that are explcit in the grammar. + */ + case '/': /* U+002F solidus (XXX this one seems silly) */ + goto emit; + + case 0x08: /* U+0008 backspace */ + ch = 'b'; + goto emit; + + case 0x0c: /* U+000C form feed */ + ch = 'f'; + goto emit; + + case 0x0a: /* U+000A line feed */ + ch = 'n'; + goto emit; + + case 0x0d: /* U+000D carriage return */ + ch = 'r'; + goto emit; + + case 0x09: /* U+0009 tab */ + ch = 't'; + goto emit; + + default: + /* + * \u-escape all other single-byte ASCII control + * characters, per RFC 8259: + * + * + * All Unicode characters may be placed within the + * quotation marks, except for the characters that + * MUST be escaped: quotation mark, reverse solidus, + * and the control characters (U+0000 through U+001F). + * + */ + if (ch < 0x20) { + if (_prop_json_extern_append_escu(ctx, + ch) == false) { + return false; + } + break; + } + /* + * We're going to just treat everything else like + * UTF-8 (we've been handed a C-string, after all) + * and pretend it will be OK. + */ + esc = false; + emit: + if ((esc && _prop_extern_append_char(ctx, + '\\') == false) || + _prop_extern_append_char(ctx, ch) == false) { + return false; + } + break; + } + } + + return true; +} + +/* + * _prop_xml_extern_append_encoded_cstring -- + * Append a C string to the externalize buffer, XML-encoded. + */ +static bool +_prop_xml_extern_append_encoded_cstring( + struct _prop_object_externalize_context *ctx, const char *cp) +{ + bool rv; + unsigned char ch; + + for (rv = true; rv && (ch = *cp) != '\0'; cp++) { + switch (ch) { + case '<': + rv = _prop_extern_append_cstring(ctx, "<"); + break; + case '>': + rv = _prop_extern_append_cstring(ctx, ">"); + break; + case '&': + rv = _prop_extern_append_cstring(ctx, "&"); + break; + default: + rv = _prop_extern_append_char(ctx, ch); + break; + } + } + + return rv; +} + +/* + * _prop_extern_append_encoded_cstring -- + * Append a C string to the externalize buffer, encoding it for + * the selected format. + */ +bool +_prop_extern_append_encoded_cstring( + struct _prop_object_externalize_context *ctx, const char *cp) +{ + _PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML || + ctx->poec_format == PROP_FORMAT_JSON); + + switch (ctx->poec_format) { + case PROP_FORMAT_JSON: + return _prop_json_extern_append_encoded_cstring(ctx, cp); + + default: /* PROP_FORMAT_XML */ + return _prop_xml_extern_append_encoded_cstring(ctx, cp); + } +} + +/* + * _prop_extern_start_line -- + * Append the start-of-line character sequence. + */ +bool +_prop_extern_start_line( + struct _prop_object_externalize_context *ctx) +{ + unsigned int i; + + for (i = 0; i < ctx->poec_depth; i++) { + if (_prop_extern_append_char(ctx, '\t') == false) { + return false; + } + } + return true; +} + +/* + * _prop_extern_end_line -- + * Append the end-of-line character sequence. + */ +bool +_prop_extern_end_line( + struct _prop_object_externalize_context *ctx, const char *trailer) +{ + if (trailer != NULL && + _prop_extern_append_cstring(ctx, trailer) == false) { + return false; + } + return _prop_extern_append_char(ctx, '\n'); +} + +/* + * _prop_extern_append_start_tag -- + * Append an item's start tag to the externalize buffer. + */ +bool +_prop_extern_append_start_tag( + struct _prop_object_externalize_context *ctx, + const struct _prop_object_type_tags *tags, + const char *tagattrs) +{ + bool rv; + + _PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML || + ctx->poec_format == PROP_FORMAT_JSON); + + switch (ctx->poec_format) { + case PROP_FORMAT_JSON: + rv = tags->json_open_tag == NULL || + _prop_extern_append_cstring(ctx, tags->json_open_tag); + break; + + default: /* PROP_FORMAT_XML */ + rv = _prop_extern_append_char(ctx, '<') && + _prop_extern_append_cstring(ctx, tags->xml_tag) && + (tagattrs == NULL || + (_prop_extern_append_char(ctx, ' ') && + _prop_extern_append_cstring(ctx, tagattrs))) && + _prop_extern_append_char(ctx, '>'); + break; + } + + return rv; +} + +/* + * _prop_extern_append_end_tag -- + * Append an item's end tag to the externalize buffer. + */ +bool +_prop_extern_append_end_tag( + struct _prop_object_externalize_context *ctx, + const struct _prop_object_type_tags *tags) +{ + bool rv; + + _PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML || + ctx->poec_format == PROP_FORMAT_JSON); + + switch (ctx->poec_format) { + case PROP_FORMAT_JSON: + rv = tags->json_close_tag == NULL || + _prop_extern_append_cstring(ctx, tags->json_close_tag); + break; + + default: /* PROP_FORMAT_XML */ + rv = _prop_extern_append_char(ctx, '<') && + _prop_extern_append_char(ctx, '/') && + _prop_extern_append_cstring(ctx, tags->xml_tag) && + _prop_extern_append_char(ctx, '>'); + break; + } + + return rv; +} + +/* + * _prop_extern_append_empty_tag -- + * Append an item's empty tag to the externalize buffer. + */ +bool +_prop_extern_append_empty_tag( + struct _prop_object_externalize_context *ctx, + const struct _prop_object_type_tags *tags) +{ + bool rv; + + _PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML || + ctx->poec_format == PROP_FORMAT_JSON); + + switch (ctx->poec_format) { + case PROP_FORMAT_JSON: + if (tags->json_open_tag == NULL || + _prop_extern_append_cstring(ctx, + tags->json_open_tag) == false) { + return false; + } + if (tags->json_empty_sep != NULL && + _prop_extern_append_cstring(ctx, + tags->json_empty_sep) == false) { + return false; + } + if (tags->json_close_tag != NULL) { + rv = _prop_extern_append_cstring(ctx, + tags->json_close_tag); + } else { + rv = true; + } + break; + + default: /* PROP_FORMAT_XML */ + rv = _prop_extern_append_char(ctx, '<') && + _prop_extern_append_cstring(ctx, tags->xml_tag) && + _prop_extern_append_char(ctx, '/') && + _prop_extern_append_char(ctx, '>'); + break; + } + + return rv; +} + +static const struct _prop_object_type_tags _plist_type_tags = { + .xml_tag = "plist", +}; + +/* + * _prop_extern_append_header -- + * Append the header to the externalize buffer. + */ +static bool +_prop_extern_append_header(struct _prop_object_externalize_context *ctx) +{ + static const char _plist_xml_header[] = +"\n" +"\n"; + + if (ctx->poec_format != PROP_FORMAT_XML) { + return true; + } + + if (_prop_extern_append_cstring(ctx, _plist_xml_header) == false || + _prop_extern_append_start_tag(ctx, + &_plist_type_tags, + "version=\"1.0\"") == false || + _prop_extern_append_char(ctx, '\n') == false) { + return false; + } + + return true; +} + +/* + * _prop_extern_append_footer -- + * Append the footer to the externalize buffer. This also + * NUL-terminates the buffer. + */ +static bool +_prop_extern_append_footer(struct _prop_object_externalize_context *ctx) +{ + if (_prop_extern_end_line(ctx, NULL) == false) { + return false; + } + + if (ctx->poec_format == PROP_FORMAT_XML) { + if (_prop_extern_append_end_tag(ctx, + &_plist_type_tags) == false || + _prop_extern_end_line(ctx, NULL) == false) { + return false; + } + } + + return _prop_extern_append_char(ctx, '\0'); +} + +/* + * _prop_extern_context_alloc -- + * Allocate an externalize context. + */ +static struct _prop_object_externalize_context * +_prop_extern_context_alloc(prop_format_t fmt) +{ + struct _prop_object_externalize_context *ctx; + + ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP); + if (ctx != NULL) { + ctx->poec_buf = _PROP_MALLOC(BUF_EXPAND, M_TEMP); + if (ctx->poec_buf == NULL) { + _PROP_FREE(ctx, M_TEMP); + return NULL; + } + ctx->poec_len = 0; + ctx->poec_capacity = BUF_EXPAND; + ctx->poec_depth = 0; + ctx->poec_format = fmt; + } + return ctx; +} + +/* + * _prop_extern_context_free -- + * Free an externalize context. + */ +static void +_prop_extern_context_free(struct _prop_object_externalize_context *ctx) +{ + /* Buffer is always freed by the caller. */ + _PROP_FREE(ctx, M_TEMP); +} + +/* + * _prop_object_externalize -- + * Externalize an object, returning a NUL-terminated buffer + * containing the serialized data in either XML or JSON format. + * The buffer is allocated with the M_TEMP memory type. + */ +char * +_prop_object_externalize(struct _prop_object *obj, prop_format_t fmt) +{ + struct _prop_object_externalize_context *ctx; + char *cp = NULL; + + if (obj == NULL || obj->po_type->pot_extern == NULL) { + return NULL; + } + if (fmt != PROP_FORMAT_XML && fmt != PROP_FORMAT_JSON) { + return NULL; + } + + ctx = _prop_extern_context_alloc(fmt); + if (ctx == NULL) { + return NULL; + } + + if (_prop_extern_append_header(ctx) == false || + obj->po_type->pot_extern(ctx, obj) == false || + _prop_extern_append_footer(ctx) == false) { + /* We are responsible for releasing the buffer. */ + _PROP_FREE(ctx->poec_buf, M_TEMP); + goto bad; + } + + cp = ctx->poec_buf; + bad: + _prop_extern_context_free(ctx); + return cp; +} + +/* + * _prop_extern_file_dirname -- + * dirname(3), basically. We have to roll our own because the + * system dirname(3) isn't reentrant. + */ +static void +_prop_extern_file_dirname(const char *path, char *result) +{ + const char *lastp; + size_t len; + + /* + * If `path' is a NULL pointer or points to an empty string, + * return ".". + */ + if (path == NULL || *path == '\0') { + goto singledot; + } + + /* Strip trailing slashes, if any. */ + lastp = path + strlen(path) - 1; + while (lastp != path && *lastp == '/') { + lastp--; + } + + /* Terminate path at the last occurrence of '/'. */ + do { + if (*lastp == '/') { + /* Strip trailing slashes, if any. */ + while (lastp != path && *lastp == '/') { + lastp--; + } + + /* ...and copy the result into the result buffer. */ + len = (lastp - path) + 1 /* last char */; + if (len > (PATH_MAX - 1)) { + len = PATH_MAX - 1; + } + + memcpy(result, path, len); + result[len] = '\0'; + return; + } + } while (--lastp >= path); + + /* No /'s found, return ".". */ + singledot: + strcpy(result, "."); +} + +/* + * _prop_extern_write_file -- + * Write an externalized object to the specified file. + * The file is written atomically from the caller's perspective, + * and the mode set to 0666 modified by the caller's umask. + */ +static bool +_prop_extern_write_file(const char *fname, const char *data, size_t len) +{ + char tname_store[PATH_MAX]; + char *tname = NULL; + int fd = -1; + int save_errno; + mode_t myumask; + bool rv = false; + + if (len > SSIZE_MAX) { + errno = EFBIG; + return false; + } + + /* + * Get the directory name where the file is to be written + * and create the temporary file. + */ + _prop_extern_file_dirname(fname, tname_store); + if (strlen(tname_store) + strlen(PLISTTMP) >= sizeof(tname_store)) { + errno = ENAMETOOLONG; + return false; + } + strcat(tname_store, PLISTTMP); + + if ((fd = mkstemp(tname_store)) == -1) { + return false; + } + tname = tname_store; + + if (write(fd, data, len) != (ssize_t)len) { + goto bad; + } + + if (fsync(fd) == -1) { + goto bad; + } + + myumask = umask(0); + (void)umask(myumask); + if (fchmod(fd, 0666 & ~myumask) == -1) { + goto bad; + } + + if (rename(tname, fname) == -1) { + goto bad; + } + tname = NULL; + + rv = true; + + bad: + save_errno = errno; + if (fd != -1) { + (void) close(fd); + } + if (tname != NULL) { + (void) unlink(tname); + } + errno = save_errno; + return rv; +} + +/* + * _prop_object_externalize_to_file -- + * Externalize an object to the specified file. + */ +bool +_prop_object_externalize_to_file(struct _prop_object *obj, const char *fname, + prop_format_t fmt) +{ + bool rv; + int save_errno; + char *data = _prop_object_externalize(obj, fmt); + if (data == NULL) { + return false; + } + rv = _prop_extern_write_file(fname, data, strlen(data)); + save_errno = errno; + _PROP_FREE(data, M_TEMP); + errno = save_errno; + + return rv; +} + +/* + * prop_object_externalize_to_file -- + * Externalize an object to the specified file in the default format. + */ +_PROP_EXPORT bool +prop_object_externalize_to_file(prop_object_t po, const char *fname) +{ + return _prop_object_externalize_to_file((struct _prop_object *)po, + fname, _prop_format_default); +} + +/* + * prop_object_externalize_to_file_with_format -- + * Externalize an object to the specified file in the specified format. + */ +_PROP_EXPORT bool +prop_object_externalize_to_file_with_format(prop_object_t po, + const char *fname, prop_format_t fmt) +{ + return _prop_object_externalize_to_file((struct _prop_object *)po, + fname, fmt); +} + +/* + * prop_object_externalize -- + * Externalize an object in the default format. + */ +_PROP_EXPORT char * +prop_object_externalize(prop_object_t po) +{ + return _prop_object_externalize((struct _prop_object *)po, + _prop_format_default); +} + +/* + * prop_object_externalize_with_format -- + * Externalize an object in the specified format. + */ +_PROP_EXPORT char * +prop_object_externalize_with_format(prop_object_t po, prop_format_t fmt) +{ + return _prop_object_externalize((struct _prop_object *)po, fmt); +} diff --git a/lib/portableproplib/prop_ingest.c b/lib/portableproplib/prop_ingest.c index 5d3c63a04..48f013c45 100644 --- a/lib/portableproplib/prop_ingest.c +++ b/lib/portableproplib/prop_ingest.c @@ -1,4 +1,4 @@ -/* $NetBSD: prop_ingest.c,v 1.5 2014/09/05 05:19:24 matt Exp $ */ +/* $NetBSD: prop_ingest.c,v 1.6 2025/04/26 17:13:23 thorpej Exp $ */ /*- * Copyright (c) 2006 The NetBSD Foundation, Inc. @@ -43,7 +43,7 @@ struct _prop_ingest_context { * prop_ingest_context_alloc -- * Allocate and initialize an ingest context. */ -prop_ingest_context_t +_PROP_EXPORT prop_ingest_context_t prop_ingest_context_alloc(void *xprivate) { prop_ingest_context_t ctx; @@ -62,7 +62,7 @@ prop_ingest_context_alloc(void *xprivate) * prop_ingest_context_free -- * Free an ingest context. */ -void +_PROP_EXPORT void prop_ingest_context_free(prop_ingest_context_t ctx) { @@ -73,7 +73,7 @@ prop_ingest_context_free(prop_ingest_context_t ctx) * prop_ingest_context_error -- * Get the error code from an ingest context. */ -prop_ingest_error_t +_PROP_EXPORT prop_ingest_error_t prop_ingest_context_error(prop_ingest_context_t ctx) { @@ -84,7 +84,7 @@ prop_ingest_context_error(prop_ingest_context_t ctx) * prop_ingest_context_type -- * Return the type of last object visisted by an ingest context. */ -prop_type_t +_PROP_EXPORT prop_type_t prop_ingest_context_type(prop_ingest_context_t ctx) { @@ -95,7 +95,7 @@ prop_ingest_context_type(prop_ingest_context_t ctx) * prop_ingest_context_key -- * Return the last key looked up by an ingest context. */ -const char * +_PROP_EXPORT const char * prop_ingest_context_key(prop_ingest_context_t ctx) { @@ -106,7 +106,7 @@ prop_ingest_context_key(prop_ingest_context_t ctx) * prop_ingest_context_private -- * Return the caller-private data associated with an ingest context. */ -void * +_PROP_EXPORT void * prop_ingest_context_private(prop_ingest_context_t ctx) { @@ -118,7 +118,7 @@ prop_ingest_context_private(prop_ingest_context_t ctx) * Ingest a dictionary using handlers for each object to translate * into an arbitrary binary format. */ -bool +_PROP_EXPORT bool prop_dictionary_ingest(prop_dictionary_t dict, const prop_ingest_table_entry rules[], prop_ingest_context_t ctx) diff --git a/lib/portableproplib/prop_intern.c b/lib/portableproplib/prop_intern.c new file mode 100644 index 000000000..1534d6cdd --- /dev/null +++ b/lib/portableproplib/prop_intern.c @@ -0,0 +1,1094 @@ +/* $NetBSD: prop_intern.c,v 1.2 2025/05/14 03:25:46 thorpej Exp $ */ + +/*- + * Copyright (c) 2006, 2007, 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "prop_object_impl.h" +#include + +#include +#include +#include +#include + +/* + * _prop_intern_skip_whitespace -- + * Skip and span of whitespace. + */ +const char * +_prop_intern_skip_whitespace(const char *cp) +{ + while (_PROP_ISSPACE(*cp)) { + cp++; + } + return cp; +} + +/* + * _prop_intern_match -- + * Returns true if the two character streams match. + */ +bool +_prop_intern_match(const char *str1, size_t len1, + const char *str2, size_t len2) +{ + return (len1 == len2 && memcmp(str1, str2, len1) == 0); +} + +/* + * _prop_xml_intern_skip_comment -- + * Skip the body and end tag of an XML comment. + */ +static bool +_prop_xml_intern_skip_comment(struct _prop_object_internalize_context *ctx) +{ + const char *cp = ctx->poic_cp; + + for (cp = ctx->poic_cp; !_PROP_EOF(*cp); cp++) { + if (cp[0] == '-' && + cp[1] == '-' && + cp[2] == '>') { + ctx->poic_cp = cp + 3; + return true; + } + } + + return false; /* ran out of buffer */ +} + +/* + * _prop_xml_intern_find_tag -- + * Find the next tag in an XML stream. Optionally compare the found + * tag to an expected tag name. State of the context is undefined + * if this routine returns false. Upon success, the context points + * to the first octet after the tag. + */ +bool +_prop_xml_intern_find_tag(struct _prop_object_internalize_context *ctx, + const char *tag, _prop_tag_type_t type) +{ + const char *cp; + size_t taglen; + + taglen = tag != NULL ? strlen(tag) : 0; + + start_over: + cp = ctx->poic_cp; + + /* + * Find the start of the tag. + */ + cp = _prop_intern_skip_whitespace(cp); + if (*cp != '<') { + return false; + } + + ctx->poic_tag_start = cp++; + if (_PROP_EOF(*cp)) { + return false; + } + + if (*cp == '!') { + if (cp[1] != '-' || cp[2] != '-') { + return false; + } + /* + * Comment block -- only allowed if we are allowed to + * return a start tag. + */ + if (type == _PROP_TAG_TYPE_END) { + return false; + } + ctx->poic_cp = cp + 3; + if (_prop_xml_intern_skip_comment(ctx) == false) { + return false; + } + goto start_over; + } + + if (*cp == '/') { + if (type != _PROP_TAG_TYPE_END && + type != _PROP_TAG_TYPE_EITHER) { + return false; + } + cp++; + if (_PROP_EOF(*cp)) { + return false; + } + ctx->poic_tag_type = _PROP_TAG_TYPE_END; + } else { + if (type != _PROP_TAG_TYPE_START && + type != _PROP_TAG_TYPE_EITHER) { + return false; + } + ctx->poic_tag_type = _PROP_TAG_TYPE_START; + } + + ctx->poic_tagname = cp; + + while (!_PROP_ISSPACE(*cp) && *cp != '/' && *cp != '>') { + if (_PROP_EOF(*cp)) { + return false; + } + cp++; + } + + ctx->poic_tagname_len = cp - ctx->poic_tagname; + + /* Make sure this is the tag we're looking for. */ + if (tag != NULL && + (taglen != ctx->poic_tagname_len || + memcmp(tag, ctx->poic_tagname, taglen) != 0)) { + return false; + } + + /* Check for empty tag. */ + if (*cp == '/') { + if (ctx->poic_tag_type != _PROP_TAG_TYPE_START) { + return false; /* only valid on start tags */ + } + ctx->poic_is_empty_element = true; + cp++; + if (_PROP_EOF(*cp) || *cp != '>') { + return false; + } + } else { + ctx->poic_is_empty_element = false; + } + + /* Easy case of no arguments. */ + if (*cp == '>') { + ctx->poic_tagattr = NULL; + ctx->poic_tagattr_len = 0; + ctx->poic_tagattrval = NULL; + ctx->poic_tagattrval_len = 0; + ctx->poic_cp = cp + 1; + return true; + } + + _PROP_ASSERT(!_PROP_EOF(*cp)); + cp++; + if (_PROP_EOF(*cp)) { + return false; + } + + cp = _prop_intern_skip_whitespace(cp); + if (_PROP_EOF(*cp)) { + return false; + } + + ctx->poic_tagattr = cp; + + while (!_PROP_ISSPACE(*cp) && *cp != '=') { + if (_PROP_EOF(*cp)) { + return false; + } + cp++; + } + + ctx->poic_tagattr_len = cp - ctx->poic_tagattr; + + cp++; + if (*cp != '\"') { + return false; + } + cp++; + if (_PROP_EOF(*cp)) { + return false; + } + + ctx->poic_tagattrval = cp; + while (*cp != '\"') { + if (_PROP_EOF(*cp)) { + return false; + } + cp++; + } + ctx->poic_tagattrval_len = cp - ctx->poic_tagattrval; + + cp++; + if (*cp != '>') { + return false; + } + + ctx->poic_cp = cp + 1; + return true; +} + +#define INTERNALIZER(t, f) \ +{ t, sizeof(t) - 1, f } + +static const struct _prop_object_internalizer { + const char *poi_tag; + size_t poi_taglen; + prop_object_internalizer_t poi_intern; +} _prop_object_internalizer_table[] = { + INTERNALIZER("array", _prop_array_internalize), + + INTERNALIZER("true", _prop_bool_internalize), + INTERNALIZER("false", _prop_bool_internalize), + + INTERNALIZER("data", _prop_data_internalize), + + INTERNALIZER("dict", _prop_dictionary_internalize), + + INTERNALIZER("integer", _prop_number_internalize), + + INTERNALIZER("string", _prop_string_internalize), + + { 0, 0, NULL } +}; + +#undef INTERNALIZER + +/* + * _prop_xml_intern_by_tag -- + * Determine the object type from the tag in the context and + * internalize it. + */ +static prop_object_t +_prop_xml_intern_by_tag(struct _prop_object_internalize_context *ctx) +{ + const struct _prop_object_internalizer *poi; + prop_object_t obj, parent_obj; + void *data, *iter; + prop_object_internalizer_continue_t iter_func; + struct _prop_stack stack; + + _prop_stack_init(&stack); + +match_start: + for (poi = _prop_object_internalizer_table; + poi->poi_tag != NULL; poi++) { + if (_prop_intern_match(ctx->poic_tagname, + ctx->poic_tagname_len, + poi->poi_tag, + poi->poi_taglen)) { + break; + } + } + if (poi == NULL || poi->poi_tag == NULL) { + while (_prop_stack_pop(&stack, &obj, &iter, &data, NULL)) { + iter_func = (prop_object_internalizer_continue_t)iter; + (*iter_func)(&stack, &obj, ctx, data, NULL); + } + return NULL; + } + + obj = NULL; + if (!(*poi->poi_intern)(&stack, &obj, ctx)) { + goto match_start; + } + + parent_obj = obj; + while (_prop_stack_pop(&stack, &parent_obj, &iter, &data, NULL)) { + iter_func = (prop_object_internalizer_continue_t)iter; + if (!(*iter_func)(&stack, &parent_obj, ctx, data, obj)) { + goto match_start; + } + obj = parent_obj; + } + + return parent_obj; +} + +#define ADDCHAR(x) \ + do { \ + if (target) { \ + if (tarindex >= targsize) { \ + return false; \ + } \ + target[tarindex] = (x); \ + } \ + tarindex++; \ + } while (/*CONSTCOND*/0) + +/* + * _prop_json_intern_decode_uesc_getu16 -- + * Get the 16-bit value from a "u-escape" ("\uXXXX"). + */ +static unsigned int +_prop_json_intern_decode_uesc_getu16(const char *src, unsigned int idx, + uint16_t *valp) +{ + unsigned int i; + uint16_t val; + unsigned char c; + + if (src[idx] != '\\' || src[idx + 1] != 'u') { + return 0; + } + + for (val = 0, i = 2; i < 6; i++) { + val <<= 4; + c = src[idx + i]; + if (c >= 'A' && c <= 'F') { + val |= 10 + (c - 'A'); + } else if (c >= 'a' && c <= 'f') { + val |= 10 + (c - 'a'); + } else if (c >= '0' && c <= '9') { + val |= c - '0'; + } else { + return 0; + } + } + + *valp = val; + return idx + i; +} + +#define HS_FIRST 0xd800 +#define HS_LAST 0xdbff +#define HS_SHIFT 10 +#define LS_FIRST 0xdc00 +#define LS_LAST 0xdfff + +#define HIGH_SURROGAGE_P(x) \ + ((x) >= HS_FIRST && (x) <= HS_LAST) +#define LOW_SURROGATE_P(x) \ + ((x) >= LS_FIRST && (x) <= LS_LAST) +#define SURROGATE_P(x) \ + (HIGH_SURROGAGE_P(x) || LOW_SURROGATE_P(x)) + +/* + * _prop_json_intern_decode_uesc -- + * Decode a JSON UTF-16 "u-escape" ("\uXXXX"). + */ +static int +_prop_json_intern_decode_uesc(const char *src, char *c, unsigned int *cszp) +{ + unsigned int idx = 0; + uint32_t code; + uint16_t code16[2] = { 0, 0 }; + uint8_t u, v, w,x, y, z; + + idx = _prop_json_intern_decode_uesc_getu16(src, idx, &code16[0]); + if (idx == 0) { + return 0; + } + if (! SURROGATE_P(code16[0])) { + /* Simple case: not a surrogate pair */ + code = code16[0]; + } else if (HIGH_SURROGAGE_P(code16[0])) { + idx = _prop_json_intern_decode_uesc_getu16(src, idx, + &code16[1]); + if (idx == 0) { + return 0; + } + /* Next code must be the low surrogate. */ + if (! LOW_SURROGATE_P(code16[1])) { + return 0; + } + code = (((uint32_t)code16[0] - HS_FIRST) << HS_SHIFT) + + ( code16[1] - LS_FIRST) + + 0x10000; + } else { + /* Got the low surrogate first; this is an error. */ + return 0; + } + + /* + * Ok, we have the code point. Now convert it to UTF-8. + * First we'll just split into nybbles. + */ + u = (code >> 20) & 0xf; + v = (code >> 16) & 0xf; + w = (code >> 12) & 0xf; + x = (code >> 8) & 0xf; + y = (code >> 4) & 0xf; + z = (code ) & 0xf; + + /* + * ...and swizzle the nybbles accordingly. + * + * N.B. we expcitly disallow inserting a NUL into the string + * by way of a \uXXXX escape. + */ + if (code == 0) { + /* Not allowed. */ + return 0; + } else if (/*code >= 0x0000 &&*/ code <= 0x007f) { + c[0] = (char)code; /* == (y << 4) | z */ + *cszp = 1; + } else if (/*code >= 0x0080 &&*/ code <= 0x07ff) { + c[0] = 0xc0 | (x << 2) | (y >> 2); + c[1] = 0x80 | ((y & 3) << 4) | z; + *cszp = 2; + } else if (/*code >= 0x0800 &&*/ code <= 0xffff) { + c[0] = 0xe0 | w; + c[1] = 0x80 | (x << 2) | (y >> 2); + c[2] = 0x80 | ((y & 3) << 4) | z; + *cszp = 3; + } else if (/*code >= 0x010000 &&*/ code <= 0x10ffff) { + c[0] = 0xf0 | ((u & 1) << 2) | (v >> 2); + c[1] = 0x80 | ((v & 3) << 4) | w; + c[2] = 0x80 | (x << 2) | (y >> 2); + c[3] = 0x80 | ((y & 3) << 4) | z; + *cszp = 4; + } else { + /* Invalid code. */ + return 0; + } + + return idx; /* advance input by this much */ +} + +#undef HS_FIRST +#undef HS_LAST +#undef LS_FIRST +#undef LS_LAST +#undef HIGH_SURROGAGE_P +#undef LOW_SURROGATE_P +#undef SURROGATE_P + +/* + * _prop_json_intern_decode_string -- + * Decode a JSON-encoded string. + */ +static bool +_prop_json_intern_decode_string(struct _prop_object_internalize_context *ctx, + char *target, size_t targsize, size_t *sizep, + const char **cpp) +{ + const char *src; + size_t tarindex; + char c[4]; + unsigned int csz; + + tarindex = 0; + src = ctx->poic_cp; + + for (;;) { + if (_PROP_EOF(*src)) { + return false; + } + if (*src == '"') { + break; + } + + csz = 1; + if ((c[0] = *src) == '\\') { + int advance = 2; + + switch ((c[0] = src[1])) { + case '"': /* quotation mark */ + case '\\': /* reverse solidus */ + case '/': /* solidus */ + /* identity mapping */ + break; + + case 'b': /* backspace */ + c[0] = 0x08; + break; + + case 'f': /* form feed */ + c[0] = 0x0c; + break; + + case 'n': /* line feed */ + c[0] = 0x0a; + break; + + case 'r': /* carriage return */ + c[0] = 0x0d; + break; + + case 't': /* tab */ + c[0] = 0x09; + break; + + case 'u': + advance = _prop_json_intern_decode_uesc( + src, c, &csz); + if (advance == 0) { + return false; + } + break; + + default: + /* invalid escape */ + return false; + } + src += advance; + } else { + src++; + } + for (unsigned int i = 0; i < csz; i++) { + ADDCHAR(c[i]); + } + } + + _PROP_ASSERT(*src == '"'); + if (sizep != NULL) { + *sizep = tarindex; + } + if (cpp != NULL) { + *cpp = src; + } + + return true; +} + +/* + * _prop_xml_intern_decode_string -- + * Decode an XML-encoded string. + */ +static bool +_prop_xml_intern_decode_string(struct _prop_object_internalize_context *ctx, + char *target, size_t targsize, size_t *sizep, + const char **cpp) +{ + const char *src; + size_t tarindex; + char c; + + tarindex = 0; + src = ctx->poic_cp; + + for (;;) { + if (_PROP_EOF(*src)) { + return true; + } + if (*src == '<') { + break; + } + + if ((c = *src) == '&') { + if (src[1] == 'a' && + src[2] == 'm' && + src[3] == 'p' && + src[4] == ';') { + c = '&'; + src += 5; + } else if (src[1] == 'l' && + src[2] == 't' && + src[3] == ';') { + c = '<'; + src += 4; + } else if (src[1] == 'g' && + src[2] == 't' && + src[3] == ';') { + c = '>'; + src += 4; + } else if (src[1] == 'a' && + src[2] == 'p' && + src[3] == 'o' && + src[4] == 's' && + src[5] == ';') { + c = '\''; + src += 6; + } else if (src[1] == 'q' && + src[2] == 'u' && + src[3] == 'o' && + src[4] == 't' && + src[5] == ';') { + c = '\"'; + src += 6; + } else { + return false; + } + } else { + src++; + } + ADDCHAR(c); + } + + _PROP_ASSERT(*src == '<'); + if (sizep != NULL) { + *sizep = tarindex; + } + if (cpp != NULL) { + *cpp = src; + } + + return true; +} + +#undef ADDCHAR + +/* + * _prop_intern_decode_string -- + * Decode an encoded string. + */ +bool +_prop_intern_decode_string(struct _prop_object_internalize_context *ctx, + char *target, size_t targsize, size_t *sizep, + const char **cpp) +{ + _PROP_ASSERT(ctx->poic_format == PROP_FORMAT_XML || + ctx->poic_format == PROP_FORMAT_JSON); + + switch (ctx->poic_format) { + case PROP_FORMAT_JSON: + return _prop_json_intern_decode_string(ctx, target, targsize, + sizep, cpp); + + default: /* PROP_FORMAT_XML */ + return _prop_xml_intern_decode_string(ctx, target, targsize, + sizep, cpp); + } +} + +/* + * _prop_intern_context_alloc -- + * Allocate an internalize context. + */ +static struct _prop_object_internalize_context * +_prop_intern_context_alloc(const char *data, prop_format_t fmt) +{ + struct _prop_object_internalize_context *ctx; + + ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP); + if (ctx == NULL) { + return NULL; + } + + ctx->poic_format = fmt; + ctx->poic_data = ctx->poic_cp = data; + + /* + * If we're digesting JSON, check for a byte order mark and + * skip it, if present. We should never see one, but we're + * allowed to detect and ignore it. (RFC 8259 section 8.1) + */ + if (fmt == PROP_FORMAT_JSON) { + if (((unsigned char)data[0] == 0xff && + (unsigned char)data[1] == 0xfe) || + ((unsigned char)data[0] == 0xfe && + (unsigned char)data[1] == 0xff)) { + ctx->poic_cp = data + 2; + } + + /* No additional processing work to do for JSON. */ + return ctx; + } + + /* + * Skip any whitespace and XML preamble stuff that we don't + * know about / care about. + */ + for (;;) { + data = _prop_intern_skip_whitespace(data); + if (_PROP_EOF(*data) || *data != '<') { + goto bad; + } + +#define MATCH(str) (strncmp(&data[1], str, strlen(str)) == 0) + + /* + * Skip over the XML preamble that Apple XML property + * lists usually include at the top of the file. + */ + if (MATCH("?xml ") || + MATCH("!DOCTYPE plist")) { + while (*data != '>' && !_PROP_EOF(*data)) { + data++; + } + if (_PROP_EOF(*data)) { + goto bad; + } + data++; /* advance past the '>' */ + continue; + } + + if (MATCH("