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