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