libyang  6.3.1
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
time.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 
44 static void lyplg_type_free_time(const struct ly_ctx *ctx, struct lyd_value *value);
45 
49 static LY_ERR
50 lyplg_type_store_time(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, uint64_t value_size_bits,
51  uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
52  const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
53  struct ly_err_item **err)
54 {
55  LY_ERR ret = LY_SUCCESS;
56  struct lyd_value_time *val = NULL;
57  struct lyd_value_time_nz *val_nz = NULL;
58  struct tm tm = {0};
59  uint32_t i, value_size;
60  char c, *ptr, *str = NULL;
61  time_t t;
62 
63  /* init storage */
64  memset(storage, 0, sizeof *storage);
65  if (!strcmp(type->name, "time")) {
66  LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
67  } else {
68  LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val_nz);
69  }
70  LY_CHECK_ERR_GOTO(!val && !val_nz, ret = LY_EMEM, cleanup);
71  storage->realtype = type;
72 
73  if (format == LY_VALUE_LYB) {
74  /* validation */
75  if (val && (value_size_bits < 40)) {
76  ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB %s value size %" PRIu64
77  " b (expected at least 40 b).", type->name, value_size_bits);
78  goto cleanup;
79  } else if (val_nz && (value_size_bits < 32)) {
80  ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB %s value size %" PRIu64
81  " b (expected at least 32 b).", type->name, value_size_bits);
82  goto cleanup;
83  }
84  value_size = LYPLG_BITS2BYTES(value_size_bits);
85  for (i = val ? 5 : 4; i < value_size; ++i) {
86  c = ((char *)value)[i];
87  if (!isdigit(c)) {
88  ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB %s character '%c' "
89  "(expected a digit).", type->name, c);
90  goto cleanup;
91  }
92  }
93 
94  /* store timestamp */
95  if (val) {
96  memcpy(&val->seconds, value, sizeof val->seconds);
97  } else {
98  memcpy(&val_nz->seconds, value, sizeof val_nz->seconds);
99  }
100 
101  /* store unknown timezone */
102  if (val) {
103  val->unknown_tz = *(((uint8_t *)value) + 4) ? 1 : 0;
104  }
105 
106  /* store fractions of a second */
107  if (val && (value_size > 5)) {
108  val->fractions_s = strndup(((char *)value) + 5, value_size - 5);
109  LY_CHECK_ERR_GOTO(!val->fractions_s, ret = LY_EMEM, cleanup);
110  } else if (val_nz && (value_size > 4)) {
111  val_nz->fractions_s = strndup(((char *)value) + 4, value_size - 4);
112  LY_CHECK_ERR_GOTO(!val_nz->fractions_s, ret = LY_EMEM, cleanup);
113  }
114 
115  /* success */
116  goto cleanup;
117  }
118 
119  /* get value byte length */
120  if (val) {
121  ret = lyplg_type_check_value_size(type->name, format, value_size_bits, LYPLG_LYB_SIZE_VARIABLE_BITS, 0,
122  &value_size, err);
123  } else {
124  ret = lyplg_type_check_value_size(type->name, format, value_size_bits, LYPLG_LYB_SIZE_FIXED_BITS, 32,
125  &value_size, err);
126  }
127  LY_CHECK_GOTO(ret, cleanup);
128 
129  /* check hints */
130  ret = lyplg_type_check_hints(hints, value, value_size, type->basetype, NULL, err);
131  LY_CHECK_GOTO(ret, cleanup);
132 
133  if (!(options & LYPLG_TYPE_STORE_ONLY)) {
134  /* validate value */
135  ret = lyplg_type_validate_patterns(ctx, ((struct lysc_type_str *)type)->patterns, value, value_size, err);
136  LY_CHECK_RET(ret);
137  }
138 
139  if (val) {
140  /* create date-and-time value */
141  if (asprintf(&str, "1970-01-01T%.*s", (int)value_size, (char *)value) == -1) {
142  ret = LY_EMEM;
143  goto cleanup;
144  }
145 
146  /* convert to UNIX time and fractions of a second */
147  ret = ly_time_str2time(str, (time_t *)&t, &val->fractions_s);
148  if (ret) {
149  ret = ly_err_new(err, ret, 0, NULL, NULL, "%s", ly_last_logmsg());
150  goto cleanup;
151  }
152  val->seconds = t;
153  } else {
154  /* fill tm */
155  ptr = strptime(value, "%H:%M:%S", &tm);
156  if (!ptr || (ptr - (char *)value != 8)) {
157  ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to parse %s value \"%.*s\".", type->name,
158  (int)value_size, (char *)value);
159  goto cleanup;
160  }
161 
162  /* set the Epoch */
163  tm.tm_mday = 1;
164  tm.tm_mon = 0;
165  tm.tm_year = 70;
166  tm.tm_isdst = 0;
167 
168  /* convert to UNIX time */
169  val_nz->seconds = timegm(&tm);
170 
171  /* store fractions of a second */
172  if (ptr[0] == '.') {
173  ++ptr;
174  val_nz->fractions_s = strndup(ptr, value_size - (ptr - (char *)value));
175  if (!val_nz->fractions_s) {
176  ret = LY_EMEM;
177  goto cleanup;
178  }
179  }
180  }
181 
182  if (val && (((char *)value)[value_size - 1] == 'Z')) {
183  /* unknown timezone */
184  val->unknown_tz = 1;
185  }
186 
187  if (format == LY_VALUE_CANON) {
188  /* store canonical value */
189  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
190  ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
191  options &= ~LYPLG_TYPE_STORE_DYNAMIC;
192  LY_CHECK_GOTO(ret, cleanup);
193  } else {
194  ret = lydict_insert(ctx, value, value_size, &storage->_canonical);
195  LY_CHECK_GOTO(ret, cleanup);
196  }
197  }
198 
199 cleanup:
200  free(str);
201  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
202  free((void *)value);
203  }
204 
205  if (ret) {
206  lyplg_type_free_time(ctx, storage);
207  }
208  return ret;
209 }
210 
214 static LY_ERR
215 lyplg_type_compare_time(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1,
216  const struct lyd_value *val2)
217 {
218  struct lyd_value_time *v1, *v2;
219  struct lyd_value_time_nz *vn1, *vn2;
220  const char *fr1, *fr2;
221 
222  if (!strcmp(val1->realtype->name, "time")) {
223  LYD_VALUE_GET(val1, v1);
224  LYD_VALUE_GET(val2, v2);
225 
226  /* compare seconds and unknown tz */
227  if ((v1->seconds != v2->seconds) || (v1->unknown_tz != v2->unknown_tz)) {
228  return LY_ENOT;
229  }
230 
231  fr1 = v1->fractions_s;
232  fr2 = v2->fractions_s;
233  } else {
234  LYD_VALUE_GET(val1, vn1);
235  LYD_VALUE_GET(val2, vn2);
236 
237  /* compare seconds */
238  if (vn1->seconds != vn2->seconds) {
239  return LY_ENOT;
240  }
241 
242  fr1 = vn1->fractions_s;
243  fr2 = vn2->fractions_s;
244  }
245 
246  /* compare second fractions */
247  if ((!fr1 && !fr2) || (fr1 && fr2 && !strcmp(fr1, fr2))) {
248  return LY_SUCCESS;
249  }
250  return LY_ENOT;
251 }
252 
260 static ly_bool
261 lyplg_type_fractions_is_zero(char *frac)
262 {
263  char *iter;
264 
265  if (!frac) {
266  return 1;
267  }
268 
269  for (iter = frac; *iter; iter++) {
270  if (*iter != '0') {
271  return 0;
272  }
273  }
274 
275  return 1;
276 }
277 
287 static int
288 lyplg_type_sort_by_fractions(char *f1, char *f2)
289 {
290  ly_bool f1_is_zero, f2_is_zero;
291  int df;
292 
293  f1_is_zero = lyplg_type_fractions_is_zero(f1);
294  f2_is_zero = lyplg_type_fractions_is_zero(f2);
295 
296  if (f1_is_zero && !f2_is_zero) {
297  return -1;
298  } else if (!f1_is_zero && f2_is_zero) {
299  return 1;
300  } else if (f1_is_zero && f2_is_zero) {
301  return 0;
302  }
303 
304  /* both f1 and f2 have some non-zero number */
305  assert(!f1_is_zero && !f2_is_zero && f1 && f2);
306  df = strcmp(f1, f2);
307  if (df > 0) {
308  return 1;
309  } else if (df < 0) {
310  return -1;
311  } else {
312  return 0;
313  }
314 }
315 
319 static int
320 lyplg_type_sort_time(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
321 {
322  struct lyd_value_time *v1 = NULL, *v2;
323  struct lyd_value_time_nz *vn1 = NULL, *vn2;
324  double dt;
325 
326  if (!strcmp(val1->realtype->name, "time")) {
327  LYD_VALUE_GET(val1, v1);
328  LYD_VALUE_GET(val2, v2);
329 
330  /* compare seconds */
331  dt = difftime(v1->seconds, v2->seconds);
332  } else {
333  LYD_VALUE_GET(val1, vn1);
334  LYD_VALUE_GET(val2, vn2);
335 
336  /* compare seconds */
337  dt = difftime(vn1->seconds, vn2->seconds);
338  }
339  if (dt != 0) {
340  return dt;
341  }
342 
343  /* compare second fractions */
344  if (v1) {
345  return lyplg_type_sort_by_fractions(v1->fractions_s, v2->fractions_s);
346  } else {
347  return lyplg_type_sort_by_fractions(vn1->fractions_s, vn2->fractions_s);
348  }
349 }
350 
354 static const void *
355 lyplg_type_print_time(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
356  void *UNUSED(prefix_data), ly_bool *dynamic, uint64_t *value_size_bits)
357 {
358  struct lyd_value_time *val = NULL;
359  struct lyd_value_time_nz *val_nz = NULL;
360  struct tm tm;
361  uint32_t seconds;
362  time_t t;
363  ly_bool unknown_tz;
364  char *ret, *fractions_s;
365 
366  if (!strcmp(value->realtype->name, "time")) {
367  LYD_VALUE_GET(value, val);
368  } else {
369  LYD_VALUE_GET(value, val_nz);
370  }
371 
372  if (format == LY_VALUE_LYB) {
373  if (val_nz && !val_nz->fractions_s) {
374  /* static value */
375  *dynamic = 0;
376  if (value_size_bits) {
377  *value_size_bits = 32;
378  }
379  return (char *)&val_nz->seconds;
380  }
381 
382  /* dynamic value */
383  if (val) {
384  seconds = val->seconds;
385  unknown_tz = val->unknown_tz;
386  fractions_s = val->fractions_s;
387  } else {
388  seconds = val_nz->seconds;
389  fractions_s = val_nz->fractions_s;
390  }
391 
392  ret = malloc(4 + (val ? 1 : 0) + (fractions_s ? strlen(fractions_s) : 0));
393  LY_CHECK_ERR_RET(!ret, LOGMEM(ctx), NULL);
394 
395  *dynamic = 1;
396  if (value_size_bits) {
397  *value_size_bits = 32 + (val ? 8 : 0) + (fractions_s ? strlen(fractions_s) * 8 : 0);
398  }
399  memcpy(ret, &seconds, sizeof seconds);
400  if (val) {
401  memcpy(ret + 4, &unknown_tz, sizeof unknown_tz);
402  }
403  if (fractions_s) {
404  memcpy(ret + (val ? 5 : 4), fractions_s, strlen(fractions_s));
405  }
406  return ret;
407  }
408 
409  /* generate canonical value if not already */
410  if (!value->_canonical) {
411  if (val_nz || (val && val->unknown_tz)) {
412  if (val) {
413  t = val->seconds;
414  fractions_s = val->fractions_s;
415  } else {
416  t = val_nz->seconds;
417  fractions_s = val_nz->fractions_s;
418  }
419 
420  /* ly_time_time2str but always using GMT */
421  if (!gmtime_r(&t, &tm)) {
422  return NULL;
423  }
424 
425  if (asprintf(&ret, "%02d:%02d:%02d%s%s%s", tm.tm_hour, tm.tm_min, tm.tm_sec,
426  fractions_s ? "." : "", fractions_s ? fractions_s : "", val ? "Z" : "") == -1) {
427  return NULL;
428  }
429 
430  /* store it */
431  if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
432  LOGMEM(ctx);
433  return NULL;
434  }
435  } else {
436  if (ly_time_time2str(val->seconds, val->fractions_s, &ret)) {
437  return NULL;
438  }
439 
440  /* truncate the date segment */
441  assert(ret[10] == 'T');
442  memmove(ret, ret + 11, strlen(ret + 11) + 1);
443 
444  /* store it */
445  if (lydict_insert(ctx, ret, 0, (const char **)&value->_canonical)) {
446  free(ret);
447  LOGMEM(ctx);
448  return NULL;
449  }
450  free(ret);
451  }
452  }
453 
454  /* use the cached canonical value */
455  if (dynamic) {
456  *dynamic = 0;
457  }
458  if (value_size_bits) {
459  *value_size_bits = strlen(value->_canonical) * 8;
460  }
461  return value->_canonical;
462 }
463 
467 static LY_ERR
468 lyplg_type_dup_time(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
469 {
470  LY_ERR ret;
471  struct lyd_value_time *orig_val, *dup_val;
472  struct lyd_value_time *orig_val_nz, *dup_val_nz;
473 
474  memset(dup, 0, sizeof *dup);
475 
476  /* optional canonical value */
477  ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
478  LY_CHECK_GOTO(ret, error);
479 
480  /* allocate value */
481  if (!strcmp(original->realtype->name, "time")) {
482  LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
483  LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
484 
485  LYD_VALUE_GET(original, orig_val);
486 
487  /* copy seconds and unknown tz */
488  dup_val->seconds = orig_val->seconds;
489  dup_val->unknown_tz = orig_val->unknown_tz;
490 
491  /* duplicate second fractions */
492  if (orig_val->fractions_s) {
493  dup_val->fractions_s = strdup(orig_val->fractions_s);
494  LY_CHECK_ERR_GOTO(!dup_val->fractions_s, ret = LY_EMEM, error);
495  }
496  } else {
497  LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val_nz);
498  LY_CHECK_ERR_GOTO(!dup_val_nz, ret = LY_EMEM, error);
499 
500  LYD_VALUE_GET(original, orig_val_nz);
501 
502  /* copy seconds */
503  dup_val_nz->seconds = orig_val_nz->seconds;
504 
505  /* duplicate second fractions */
506  if (orig_val_nz->fractions_s) {
507  dup_val_nz->fractions_s = strdup(orig_val_nz->fractions_s);
508  LY_CHECK_ERR_GOTO(!dup_val_nz->fractions_s, ret = LY_EMEM, error);
509  }
510  }
511 
512  dup->realtype = original->realtype;
513  return LY_SUCCESS;
514 
515 error:
516  lyplg_type_free_time(ctx, dup);
517  return ret;
518 }
519 
523 static void
524 lyplg_type_free_time(const struct ly_ctx *ctx, struct lyd_value *value)
525 {
526  struct lyd_value_time *val, *val_nz;
527 
528  lydict_remove(ctx, value->_canonical);
529  value->_canonical = NULL;
530 
531  if (!strcmp(value->realtype->name, "time")) {
532  LYD_VALUE_GET(value, val);
533  if (val) {
534  free(val->fractions_s);
536  }
537  } else {
538  LYD_VALUE_GET(value, val_nz);
539  if (val_nz) {
540  free(val_nz->fractions_s);
542  }
543  }
544 }
545 
553 const struct lyplg_type_record plugins_time[] = {
554  {
555  .module = "ietf-yang-types",
556  .revision = NULL,
557  .name = "time",
558 
559  .plugin.id = "ly2 time",
560  .plugin.lyb_size = lyplg_type_lyb_size_variable_bytes,
561  .plugin.store = lyplg_type_store_time,
562  .plugin.validate_value = lyplg_type_validate_value_string,
563  .plugin.validate_tree = NULL,
564  .plugin.compare = lyplg_type_compare_time,
565  .plugin.sort = lyplg_type_sort_time,
566  .plugin.print = lyplg_type_print_time,
567  .plugin.duplicate = lyplg_type_dup_time,
568  .plugin.free = lyplg_type_free_time,
569  },
570  {
571  .module = "ietf-yang-types",
572  .revision = NULL,
573  .name = "time-no-zone",
574 
575  .plugin.id = "ly2 time",
576  .plugin.lyb_size = lyplg_type_lyb_size_variable_bytes,
577  .plugin.store = lyplg_type_store_time,
578  .plugin.validate_value = lyplg_type_validate_value_string,
579  .plugin.validate_tree = NULL,
580  .plugin.compare = lyplg_type_compare_time,
581  .plugin.sort = lyplg_type_sort_time,
582  .plugin.print = lyplg_type_print_time,
583  .plugin.duplicate = lyplg_type_dup_time,
584  .plugin.free = lyplg_type_free_time,
585  },
586  {0}
587 };
return difftime(vn1->time, vn2->time)
struct lysc_type * realtype
Definition: tree_data.h:615
Compiled YANG data node.
Definition: tree_schema.h:1430
memset(value->fixed_mem, 0, LYD_VALUE_FIXED_MEM_SIZE)
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...
return lyplg_type_sort_by_fractions(v1->fractions_s, v2->fractions_s)
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...
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.
libyang dictionary
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.
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.
LYPLG_TYPE_VAL_INLINE_DESTROY(val)
Definition: log.h:257
YANG data representation.
Definition: tree_data.h:611
const char * _canonical
Definition: tree_data.h:612
Libyang full error structure.
Definition: log.h:301
Definition: log.h:293
#define LYD_VALUE_GET(value, type_val)
Get the value in format specific to the type.
Definition: tree_data.h:654
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
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.
uint32_t seconds
Definition: tree_data.h:781
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...
Special lyd_value structure for ietf-yang-types time values.
Definition: tree_data.h:771
struct lyd_value_date_nz * vn2
Definition: date.c:226
const char * name
Definition: tree_schema.h:1293
uint32_t seconds
Definition: tree_data.h:772
Definition: log.h:263
const char * module
double dt
struct lyd_value_date_nz * vn1
Definition: date.c:226
char * fractions_s
Definition: tree_data.h:782
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.
Special lyd_value structure for ietf-yang-types time-no-zone values.
Definition: tree_data.h:780
struct lyplg_type_record plugins_time[]
Plugin information for time and time-no-zone type implementation.
Definition: time.c:553
LY_VALUE_FORMAT
All kinds of supported value formats and prefix mappings to modules.
Definition: utils.h:93
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.
ly_bool unknown_tz
Definition: tree_data.h:774
libyang context handler.
#define LYPLG_BITS2BYTES(bits)
Convert bits to bytes.
#define LYPLG_TYPE_STORE_ONLY
char * fractions_s
Definition: tree_data.h:773
assert(!value->_canonical)