libyang  5.7.2
libyang is YANG data modelling language parser and toolkit written (and providing API) in C.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
date.c
Go to the documentation of this file.
1 
15 #define _GNU_SOURCE /* strdup */
16 #define _XOPEN_SOURCE /* strptime */
17 
18 #include "plugins_types.h"
19 
20 #include <assert.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 
28 #include "libyang.h"
29 
30 #include "compat.h"
31 #include "ly_common.h"
32 #include "plugins_internal.h" /* LY_TYPE_*_STR */
33 
44 static void lyplg_type_free_date(const struct ly_ctx *ctx, struct lyd_value *value);
45 
46 static void
47 lyplg_type_lyb_size_date_nz(const struct lysc_type *UNUSED(type), enum lyplg_lyb_size_type *size_type,
48  uint64_t *fixed_size_bits)
49 {
50  *size_type = LYPLG_LYB_SIZE_FIXED_BITS;
51  *fixed_size_bits = 64;
52 }
53 
57 static LY_ERR
58 lyplg_type_store_date(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, uint64_t value_size_bits,
59  uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
60  const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
61  struct ly_err_item **err)
62 {
63  LY_ERR ret = LY_SUCCESS;
64  struct lyd_value_date *val = NULL;
65  struct lyd_value_date_nz *val_nz = NULL;
66  struct tm tm = {0};
67  char *ptr;
68  uint32_t value_size;
69  char *str = NULL;
70 
71  /* init storage */
72  memset(storage, 0, sizeof *storage);
73  if (!strcmp(type->name, "date")) {
74  LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
75  } else {
76  LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val_nz);
77  }
78  LY_CHECK_ERR_GOTO(!val && !val_nz, ret = LY_EMEM, cleanup);
79  storage->realtype = type;
80 
81  if (format == LY_VALUE_LYB) {
82  /* validation */
83  if (val) {
84  if ((value_size_bits != 64) && (value_size_bits != 65)) {
85  ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB %s value size %" PRIu64
86  " b (expected 64 or 65 b).", type->name, value_size_bits);
87  goto cleanup;
88  }
89  } else {
90  if (value_size_bits != 64) {
91  ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB %s value size %" PRIu64
92  " b (expected 64 b).", type->name, value_size_bits);
93  goto cleanup;
94  }
95  }
96  value_size = LYPLG_BITS2BYTES(value_size_bits);
97 
98  /* store timestamp */
99  if (val) {
100  memcpy(&val->time, value, sizeof val->time);
101  } else {
102  memcpy(&val_nz->time, value, sizeof val_nz->time);
103  }
104 
105  /* store unknown timezone */
106  if (val && (value_size > 8)) {
107  val->unknown_tz = *(((uint8_t *)value) + 8) ? 1 : 0;
108  }
109 
110  /* success */
111  goto cleanup;
112  }
113 
114  /* get value byte length */
115  if (val) {
116  ret = lyplg_type_check_value_size(type->name, format, value_size_bits, LYPLG_LYB_SIZE_VARIABLE_BITS, 0,
117  &value_size, err);
118  } else {
119  ret = lyplg_type_check_value_size(type->name, format, value_size_bits, LYPLG_LYB_SIZE_FIXED_BITS, 64,
120  &value_size, err);
121  }
122  LY_CHECK_GOTO(ret, cleanup);
123 
124  /* check hints */
125  ret = lyplg_type_check_hints(hints, value, value_size, type->basetype, NULL, err);
126  LY_CHECK_GOTO(ret, cleanup);
127 
128  if (!(options & LYPLG_TYPE_STORE_ONLY)) {
129  /* validate value */
130  ret = lyplg_type_validate_patterns(ctx, ((struct lysc_type_str *)type)->patterns, value, value_size, err);
131  LY_CHECK_RET(ret);
132  }
133 
134  if (val) {
135  /* create date-and-time value */
136  if (asprintf(&str, "%.*sT00:00:00%.*s", 10, (char *)value, (int)(value_size - 10), (char *)value + 10) == -1) {
137  ret = LY_EMEM;
138  goto cleanup;
139  }
140 
141  /* convert to UNIX time */
142  ret = ly_time_str2time(str, &val->time, NULL);
143  if (ret) {
144  ret = ly_err_new(err, ret, 0, NULL, NULL, "%s", ly_last_logmsg());
145  goto cleanup;
146  }
147  } else {
148  /* fill tm */
149  ptr = strptime(value, "%Y-%m-%d", &tm);
150  if (!ptr || (ptr - (char *)value != value_size)) {
151  ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to parse %s value \"%.*s\".", type->name,
152  (int)value_size, (char *)value);
153  goto cleanup;
154  }
155 
156  /* convert to UNIX time */
157  val_nz->time = timegm(&tm);
158  }
159 
160  if (val && (((char *)value)[value_size - 1] == 'Z')) {
161  /* unknown timezone */
162  val->unknown_tz = 1;
163  }
164 
165  if (format == LY_VALUE_CANON) {
166  /* store canonical value */
167  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
168  ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
169  options &= ~LYPLG_TYPE_STORE_DYNAMIC;
170  LY_CHECK_GOTO(ret, cleanup);
171  } else {
172  ret = lydict_insert(ctx, value, value_size, &storage->_canonical);
173  LY_CHECK_GOTO(ret, cleanup);
174  }
175  }
176 
177 cleanup:
178  free(str);
179  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
180  free((void *)value);
181  }
182 
183  if (ret) {
184  lyplg_type_free_date(ctx, storage);
185  }
186  return ret;
187 }
188 
192 static LY_ERR
193 lyplg_type_compare_date(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1,
194  const struct lyd_value *val2)
195 {
196  struct lyd_value_date *v1, *v2;
197  struct lyd_value_date_nz *vn1, *vn2;
198 
199  if (!strcmp(val1->realtype->name, "date")) {
200  LYD_VALUE_GET(val1, v1);
201  LYD_VALUE_GET(val2, v2);
202 
203  /* compare timestamp and unknown tz */
204  if ((v1->time != v2->time) || (v1->unknown_tz != v2->unknown_tz)) {
205  return LY_ENOT;
206  }
207  } else {
208  LYD_VALUE_GET(val1, vn1);
209  LYD_VALUE_GET(val2, vn2);
210 
211  /* compare timestamp */
212  if (vn1->time != vn2->time) {
213  return LY_ENOT;
214  }
215  }
216 
217  return LY_SUCCESS;
218 }
219 
223 static int
224 lyplg_type_sort_date(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
225 {
226  struct lyd_value_date *v1, *v2;
228 
229  if (!strcmp(val1->realtype->name, "date")) {
230  LYD_VALUE_GET(val1, v1);
231  LYD_VALUE_GET(val2, v2);
232 
233  /* compare timestamps */
234  return difftime(v1->time, v2->time);
235  } else {
236  LYD_VALUE_GET(val1, vn1);
237  LYD_VALUE_GET(val2, vn2);
238 
239  /* compare timestamps */
240  return difftime(vn1->time, vn2->time);
241  }
242 }
243 
247 static const void *
248 lyplg_type_print_date(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
249  void *UNUSED(prefix_data), ly_bool *dynamic, uint64_t *value_size_bits)
250 {
251  struct lyd_value_date *val = NULL;
252  struct lyd_value_date_nz *val_nz = NULL;
253  struct tm tm;
254  char *ret;
255 
256  if (!strcmp(value->realtype->name, "date")) {
257  LYD_VALUE_GET(value, val);
258  } else {
259  LYD_VALUE_GET(value, val_nz);
260  }
261 
262  if (format == LY_VALUE_LYB) {
263  if (val && val->unknown_tz) {
264  ret = malloc(8 + 1);
265  LY_CHECK_ERR_RET(!ret, LOGMEM(ctx), NULL);
266 
267  *dynamic = 1;
268  if (value_size_bits) {
269  *value_size_bits = 64 + 8;
270  }
271  memcpy(ret, &val->time, sizeof val->time);
272  memcpy(ret + 8, &val->unknown_tz, sizeof val->unknown_tz);
273  } else {
274  *dynamic = 0;
275  if (value_size_bits) {
276  *value_size_bits = 64;
277  }
278  ret = val ? (char *)&val->time : (char *)&val_nz->time;
279  }
280  return ret;
281  }
282 
283  /* generate canonical value if not already */
284  if (!value->_canonical) {
285  if (val_nz || (val && val->unknown_tz)) {
286  /* ly_time_time2str but always using GMT */
287  if (!gmtime_r(val_nz ? &val_nz->time : &val->time, &tm)) {
288  return NULL;
289  }
290 
291  if (asprintf(&ret, "%04d-%02d-%02d%s", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, val ? "Z" : "") == -1) {
292  return NULL;
293  }
294  } else {
295  if (ly_time_time2str(val->time, NULL, &ret)) {
296  return NULL;
297  }
298 
299  /* truncate the time segment */
300  assert(ret[10] == 'T');
301  memmove(ret + 10, ret + 19, strlen(ret + 19) + 1);
302  }
303 
304  /* store it */
305  if (lydict_insert(ctx, ret, 0, (const char **)&value->_canonical)) {
306  free(ret);
307  LOGMEM(ctx);
308  return NULL;
309  }
310  free(ret);
311  }
312 
313  /* use the cached canonical value */
314  if (dynamic) {
315  *dynamic = 0;
316  }
317  if (value_size_bits) {
318  *value_size_bits = strlen(value->_canonical) * 8;
319  }
320  return value->_canonical;
321 }
322 
326 static LY_ERR
327 lyplg_type_dup_date(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
328 {
329  LY_ERR ret;
330  struct lyd_value_date *orig_val, *dup_val;
331  struct lyd_value_date *orig_val_nz, *dup_val_nz;
332 
333  memset(dup, 0, sizeof *dup);
334 
335  /* optional canonical value */
336  ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
337  LY_CHECK_GOTO(ret, error);
338 
339  /* allocate value */
340  if (!strcmp(original->realtype->name, "date")) {
341  LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
342  LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
343 
344  LYD_VALUE_GET(original, orig_val);
345 
346  /* copy timestamp and unknown tz */
347  dup_val->time = orig_val->time;
348  dup_val->unknown_tz = orig_val->unknown_tz;
349  } else {
350  LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val_nz);
351  LY_CHECK_ERR_GOTO(!dup_val_nz, ret = LY_EMEM, error);
352 
353  LYD_VALUE_GET(original, orig_val_nz);
354 
355  /* copy timestamp */
356  dup_val_nz->time = orig_val_nz->time;
357  }
358 
359  dup->realtype = original->realtype;
360  return LY_SUCCESS;
361 
362 error:
363  lyplg_type_free_date(ctx, dup);
364  return ret;
365 }
366 
370 static void
371 lyplg_type_free_date(const struct ly_ctx *ctx, struct lyd_value *value)
372 {
373  struct lyd_value_date *val, *val_nz;
374 
375  lydict_remove(ctx, value->_canonical);
376  value->_canonical = NULL;
377 
378  if (!strcmp(value->realtype->name, "date")) {
379  LYD_VALUE_GET(value, val);
380  if (val) {
382  }
383  } else {
384  LYD_VALUE_GET(value, val_nz);
385  if (val_nz) {
387  }
388  }
389 }
390 
398 const struct lyplg_type_record plugins_date[] = {
399  {
400  .module = "ietf-yang-types",
401  .revision = NULL,
402  .name = "date",
403 
404  .plugin.id = "ly2 date",
405  .plugin.lyb_size = lyplg_type_lyb_size_variable_bytes,
406  .plugin.store = lyplg_type_store_date,
407  .plugin.validate_value = lyplg_type_validate_value_string,
408  .plugin.validate_tree = NULL,
409  .plugin.compare = lyplg_type_compare_date,
410  .plugin.sort = lyplg_type_sort_date,
411  .plugin.print = lyplg_type_print_date,
412  .plugin.duplicate = lyplg_type_dup_date,
413  .plugin.free = lyplg_type_free_date,
414  },
415  {
416  .module = "ietf-yang-types",
417  .revision = NULL,
418  .name = "date-no-zone",
419 
420  .plugin.id = "ly2 date",
421  .plugin.lyb_size = lyplg_type_lyb_size_date_nz,
422  .plugin.store = lyplg_type_store_date,
423  .plugin.validate_value = lyplg_type_validate_value_string,
424  .plugin.validate_tree = NULL,
425  .plugin.compare = lyplg_type_compare_date,
426  .plugin.sort = lyplg_type_sort_date,
427  .plugin.print = lyplg_type_print_date,
428  .plugin.duplicate = lyplg_type_dup_date,
429  .plugin.free = lyplg_type_free_date,
430  },
431  {0}
432 };
return difftime(vn1->time, vn2->time)
Special lyd_value structure for ietf-yang-types date-no-zone values.
Definition: tree_data.h:676
struct lysc_type * realtype
Definition: tree_data.h:527
LIBYANG_API_DECL LY_ERR ly_time_time2str(time_t time, const char *fractions_s, char **str)
Convert UNIX timestamp and fractions of a second into canonical date-and-time string value...
Compiled YANG data node.
Definition: tree_schema.h:1430
memset(value->fixed_mem, 0, LYD_VALUE_FIXED_MEM_SIZE)
lyplg_lyb_size_type
Type of the LYB size of a value of a particular type.
Special lyd_value structure for ietf-yang-types date values.
Definition: tree_data.h:668
LIBYANG_API_DECL void lyplg_type_lyb_size_variable_bytes(const struct lysc_type *type, enum lyplg_lyb_size_type *size_type, uint64_t *fixed_size_bits)
Implementation of lyplg_type_lyb_size_clb for a type with variable length rounded to bytes...
struct lyplg_type_record plugins_date[]
Plugin information for date and date-no-zone type implementation.
Definition: date.c:398
LY_ERR
libyang&#39;s error codes returned by the libyang functions.
Definition: log.h:255
uint8_t ly_bool
Type to indicate boolean value.
Definition: log.h:29
LIBYANG_API_DECL LY_ERR lyplg_type_check_value_size(const char *type_name, LY_VALUE_FORMAT format, uint64_t value_size_bits, enum lyplg_lyb_size_type lyb_size_type, uint64_t lyb_fixed_size_bits, uint32_t *value_size, struct ly_err_item **err)
Check a value type in bits is correct and as expected.
Definition: log.h:269
#define LYPLG_TYPE_STORE_DYNAMIC
LIBYANG_API_DECL LY_ERR lyplg_type_validate_patterns(const struct ly_ctx *ctx, struct lysc_pattern **patterns, const char *str, uint32_t str_len, struct ly_err_item **err)
Data type validator for pattern-restricted string values.
#define LOGMEM(CTX)
Definition: tree_edit.h:22
ly_bool unknown_tz
Definition: tree_data.h:670
LYPLG_TYPE_VAL_INLINE_DESTROY(val)
Definition: log.h:257
The main libyang public header.
YANG data representation.
Definition: tree_data.h:523
const char * _canonical
Definition: tree_data.h:524
Libyang full error structure.
Definition: log.h:300
Definition: log.h:292
#define LYD_VALUE_GET(value, type_val)
Get the value in format specific to the type.
Definition: tree_data.h:566
LIBYANG_API_DECL LY_ERR lyplg_type_validate_value_string(const struct ly_ctx *ctx, const struct lysc_type *type, struct lyd_value *storage, struct ly_err_item **err)
Implementation of lyplg_type_validate_value_clb for the string type.
Definition: string.c:118
LIBYANG_API_DECL LY_ERR ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *data_path, char *apptag, const char *err_format,...) _FORMAT_PRINTF(6
Create and fill error structure.
LIBYANG_API_DECL LY_ERR lydict_remove(const struct ly_ctx *ctx, const char *value)
Remove specified string from the dictionary. It decrement reference counter for the string and if it ...
LIBYANG_API_DECL LY_ERR lydict_insert(const struct ly_ctx *ctx, const char *value, size_t len, const char **str_p)
Insert string into dictionary. If the string is already present, only a reference counter is incremen...
struct lyd_value_date_nz * vn2
Definition: date.c:227
const char * name
Definition: tree_schema.h:1293
LIBYANG_API_DECL LY_ERR ly_time_str2time(const char *value, time_t *time, char **fractions_s)
Convert date-and-time from string to UNIX timestamp and fractions of a second.
Definition: log.h:263
const char * module
struct lyd_value_date_nz * vn1
Definition: date.c:227
LIBYANG_API_DECL LY_ERR lydict_insert_zc(const struct ly_ctx *ctx, char *value, const char **str_p)
Insert string into dictionary - zerocopy version. If the string is already present, only a reference counter is incremented and no memory allocation is performed. This insert function variant avoids duplication of specified value - it is inserted into the dictionary directly.
LY_VALUE_FORMAT
All kinds of supported value formats and prefix mappings to modules.
Definition: tree.h:234
LIBYANG_API_DECL const char * ly_last_logmsg(void)
Get the last (thread-specific) full logged error message.
#define LYPLG_TYPE_VAL_INLINE_PREPARE(storage, type_val)
Prepare value memory for storing a specific type value, may be allocated dynamically.
LY_DATA_TYPE basetype
Definition: tree_schema.h:1296
LIBYANG_API_DECL LY_ERR lyplg_type_check_hints(uint32_t hints, const char *value, uint32_t value_len, LY_DATA_TYPE type, int *base, struct ly_err_item **err)
Check that the type is suitable for the parser&#39;s hints (if any) in the specified format.
API for (user) types plugins.
libyang context handler.
#define LYPLG_BITS2BYTES(bits)
Convert bits to bytes.
#define LYPLG_TYPE_STORE_ONLY
assert(!value->_canonical)