libarmutils  1.4
 All Data Structures Files Functions Variables Typedefs Enumerations Macros Groups
regex_time.c
Go to the documentation of this file.
1 /*******************************************************************************
2 *
3 * COPYRIGHT (C) 2015 Battelle Memorial Institute. All Rights Reserved.
4 *
5 ********************************************************************************
6 *
7 * Author:
8 * name: Brian Ermold
9 * phone: (509) 375-2277
10 * email: brian.ermold@pnl.gov
11 *
12 ********************************************************************************
13 *
14 * REPOSITORY INFORMATION:
15 * $Revision: 75245 $
16 * $Author: ermold $
17 * $Date: 2016-12-06 01:25:47 +0000 (Tue, 06 Dec 2016) $
18 *
19 ********************************************************************************
20 *
21 * NOTE: DOXYGEN is used to generate documentation for this file.
22 *
23 *******************************************************************************/
24 
25 /** @file regex_time.c
26  * Regex Time Utilities
27  */
28 
29 #include "armutils.h"
30 
31 /*******************************************************************************
32  * Private Data and Functions
33  */
34 /** @privatesection */
35 
36 /*
37 
38 Add Time Zone (Z | +hh:mm | -hh:mm | time_zone_code)
39 
40  'Z' = Z or time zone abbreviation
41  'z' = Z or +hh:mm or -hh:mm
42 
43  -> (Z| +[[:alpha:]{3}]|[+-][[:digit:]]{2}:[[[:digit:]]{2}])
44 
45 Add microseconds 'u' (this is not fractional seconds)
46 
47  0-999999
48 
49 Support for AM/PM (see strftime and strptime for format code)
50 
51 */
52 
53 typedef struct DateTimeCode {
54  char chr;
55  const char *str;
56 } DateTimeCode;
57 
58 static DateTimeCode _DateTimeCodes[] = {
59 
60  { 'C', "([[:digit:]]{2})" }, // century number (year/100) as a 2-digit integer
61  { 'd', "([[:digit:]]{1,2})" }, // day number in the month (1-31).
62  { 'e', "([[:digit:]]{1,2})" }, // day number in the month (1-31).
63  { 'h', "([[:digit:]]{1,4})" }, // hour and minute (0-2359)
64  { 'H', "([[:digit:]]{1,2})" }, // hour (0-23)
65  { 'j', "([[:digit:]]{1,3})" }, // day number in the year (1-366).
66  { 'm', "([[:digit:]]{1,2})" }, // month number (1-12)
67  { 'M', "([[:digit:]]{1,2})" }, // minute (0-59)
68  { 'n', "[[:space:]]+" }, // arbitrary whitespace
69  // time offset in seconds
70  { 'o', "([+-]*[[:digit:]]*\\.[[:digit:]]+|[+-]*[[:digit:]]+)" },
71  { 'p', "([aApP][mM])" }, // AM or PM (not case sensitive)
72  // Mac-Time: seconds since 1904-01-01 00:00:00 +0000 (UTC) with optional fractional seconds
73  { 'q', "([[:digit:]]+\\.[[:digit:]]+|[[:digit:]]+)" },
74  // seconds since Epoch, 1970-01-01 00:00:00 +0000 (UTC) with optional fractional seconds
75  { 's', "([[:digit:]]+\\.[[:digit:]]+|[[:digit:]]+)" },
76  // second (0-60; 60 may occur for leap seconds) with optional fractional seconds
77  { 'S', "([[:digit:]]{1,2}\\.[[:digit:]]+|[[:digit:]]{1,2})" },
78  { 't', "[[:space:]]+" }, // arbitrary whitespace
79  { 'y', "([[:digit:]]{1,2})" }, // year within century (0-99)
80  { 'Y', "([[:digit:]]{4})" }, // year with century as a 4-digit integer
81  { '%', "%" }, // a literal "%" character
82  { '\0', NULL }
83 };
84 
85 static DateTimeCode _DateTimeCodes0[] = {
86 
87  { 'C', "([[:digit:]]{2})" }, // century number (year/100) as a 2-digit integer
88  { 'd', "([[:digit:]]{2})" }, // day number in the month (range 01 to 31).
89  { 'e', "([[:digit:]]{2})" }, // day number in the month (range 01 to 31).
90  { 'h', "([[:digit:]]{4})" }, // hour and minute (0000-2359)
91  { 'H', "([[:digit:]]{2})" }, // hour (00-23)
92  { 'j', "([[:digit:]]{3})" }, // day number in the year (001-366).
93  { 'm', "([[:digit:]]{2})" }, // month number (01-12)
94  { 'M', "([[:digit:]]{2})" }, // minute (00-59)
95  { 'n', "[[:space:]]+" }, // arbitrary whitespace
96  { 'o', "([+-]*[[:digit:]]+)" }, // time offset in seconds
97  { 'p', "([aApP][mM])" }, // AM or PM (not case sensitive)
98  { 'q', "([[:digit:]]+)" }, // Mac-Time: seconds since 1904-01-01 00:00:00 +0000 (UTC)
99  { 's', "([[:digit:]]+)" }, // seconds since Epoch, 1970-01-01 00:00:00 +0000 (UTC)
100  { 'S', "([[:digit:]]{2})" }, // second (00-60; 60 may occur for leap seconds)
101  { 't', "[[:space:]]+" }, // arbitrary whitespace
102  { 'y', "([[:digit:]]{2})" }, // year within century (00-99)
103  { 'Y', "([[:digit:]]{4})" }, // year with century as a 4-digit integer
104  { '%', "%" }, // a literal "%" character
105  { '\0', NULL }
106 };
107 
108 /**
109  * PRIVATE: Parse a regex-time format string
110  *
111  * The memory used by the returned array is dynamically allocated and must be
112  * freed by the calling process when it is no longer needed.
113  *
114  * @param retime pointer to the RETime structure to use.
115  *
116  * @param pattern the time string pattern containing a mixture of regex and
117  * time format characters similar to the strptime function.
118  *
119  * @retval 1 if succesful
120  * @retval 0 if an error occurred
121  */
122 static int __retime_parse(RETime *retime, const char *pattern)
123 {
124  char *regex_pattern;
125  const char *sp;
126  char *rp;
127  int length;
128  int mi, dti;
129 
130  DateTimeCode *dtc;
131 
132  if (strchr(pattern, '(')) {
133 
134 // BDE: we can deal with this if/when we need to...
135 
137  "Invalid regex/time pattern: '%s'\n"
138  " -> regex/time pattern can not contain the '(' character.\n",
139  pattern);
140 
141  return(0);
142  }
143 
144  length = strlen(pattern) + 512;
145  regex_pattern = calloc(length, sizeof(char));
146 
147  if (!regex_pattern) {
148 
150  "Could not parse time string pattern: '%s'\n"
151  " -> memory allocation error\n",
152  pattern);
153 
154  return(0);
155  }
156 
157  sp = pattern;
158  rp = regex_pattern;
159  mi = 0;
160 
161  retime->codes[0] = -1;
162 
163  while (*sp != '\0') {
164 
165  if (*sp == '%') {
166 
167  ++sp;
168 
169  if (*sp == '0') {
170  dtc = _DateTimeCodes0;
171  ++sp;
172  }
173  else {
174  dtc = _DateTimeCodes;
175  }
176 
177  for (dti = 0; dtc[dti].chr; ++dti) {
178 
179  if (*sp == dtc[dti].chr) {
180 
181  strcpy(rp, dtc[dti].str);
182  if (*rp == '(') retime->codes[++mi] = *sp;
183 
184  rp = strchr(rp, '\0');
185 
186  break;
187  }
188  }
189 
190  if (!dtc[dti].chr) {
191 
192  if (dtc == _DateTimeCodes0) {
194  "Invalid time format code '%%0%c' found in: '%s'\n",
195  *sp, pattern);
196  }
197  else {
199  "Invalid time format code '%%%c' found in: '%s'\n",
200  *sp, pattern);
201  }
202 
203  free(regex_pattern);
204  return(0);
205  }
206 
207  ++sp;
208  }
209  else {
210  *rp++ = *sp++;
211  }
212  }
213 
214  retime->pattern = regex_pattern;
215  retime->nsubs = mi;
216 
217  return(1);
218 }
219 
220 /*******************************************************************************
221  * Public Functions
222  */
223 /** @publicsection */
224 
225 /**
226  * Compile a regex/time pattern.
227  *
228  * This function will compile a time string pattern containing a mixture of
229  * regex and time format codes similar to the strptime function. The time
230  * format codes recognized by this function begin with a % and are followed
231  * by one of the following characters:
232  *
233  * - 'C' century number (year/100) as a 2-digit integer
234  * - 'd' day number in the month (1-31).
235  * - 'e' day number in the month (1-31).
236  * - 'h' hour * 100 + minute (0-2359)
237  * - 'H' hour (0-23)
238  * - 'j' day number in the year (1-366).
239  * - 'm' month number (1-12)
240  * - 'M' minute (0-59)
241  * - 'n' arbitrary whitespace
242  * - 'o' time offset in seconds
243  * - 'p' AM or PM
244  * - 'q' Mac-Time: seconds since 1904-01-01 00:00:00 +0000 (UTC)
245  * - 's' seconds since Epoch, 1970-01-01 00:00:00 +0000 (UTC)
246  * - 'S' second (0-60; 60 may occur for leap seconds)
247  * - 't' arbitrary whitespace
248  * - 'y' year within century (0-99)
249  * - 'Y' year with century as a 4-digit integer
250  * - '%' a literal "%" character
251  *
252  * An optional 0 character can be used between the % and format code to
253  * specify that the number must be zero padded. For example, '%0d' specifies
254  * that the day range is 01 to 31.
255  *
256  * See the regex(7) man page for the descriptions of the regex patterns.
257  *
258  * The memory used by the returned RETime structure is dynamically allocated
259  * and must be freed by the calling process using the retime_free() function.
260  *
261  * Error messages from this function are sent to the message handler
262  * (see msngr_init_log() and msngr_init_mail()).
263  *
264  * @param pattern - the time string pattern containing a mixture of regex and
265  * time format characters similar to the strptime function.
266  *
267  * @param flags - reserved for control flags
268  *
269  * @return
270  * - pointer to the RETime structure
271  * - NULL if an error occurred
272  */
273 RETime *retime_compile(const char *pattern, int flags)
274 {
275  RETime *retime;
276  regex_t *preg;
277  size_t nsubs;
278  int cflags;
279 
280  flags = flags; // prevent compiler warning
281 
282  cflags = REG_EXTENDED;
283 
284  /* Create the RETime structure */
285 
286  retime = (RETime *)calloc(1, sizeof(RETime));
287  if (!retime) goto MEMORY_ERROR;
288 
289  /* Store a copy of the original time string pattern */
290 
291  retime->tspattern = strdup(pattern);
292  if (!retime->tspattern) goto MEMORY_ERROR;
293 
294  /* Allocate memory to store the match codes */
295 
296  retime->codes = calloc(32, sizeof(char));
297 
298  /* Parse the time string pattern */
299 
300  if (!__retime_parse(retime, pattern)) {
301  retime_free(retime);
302  return((RETime *)NULL);
303  }
304 
305  /* Compile the regular expression */
306 
307  preg = &(retime->preg);
308 
309  if (!re_compile(preg, retime->pattern, cflags)) {
310 
312  "Could not compile regex/time pattern\n"
313  " -> time string pattern: '%s'\n"
314  " -> regex string pattern: '%s'\n",
315  pattern, retime->pattern);
316 
317  retime_free(retime);
318  return((RETime *)NULL);
319  }
320 
321  /* Create array to store substring offsets */
322 
323  nsubs = retime->nsubs;
324 
325  if (!nsubs) {
326 
328  "Invalid regex/time pattern: '%s'\n"
329  " -> no time format codes found in pattern\n",
330  pattern);
331 
332  retime_free(retime);
333  return((RETime *)NULL);
334  }
335 
336  if (nsubs > RETIME_MAX_NSUBS) {
337 
339  "Invalid regex/time pattern: '%s'\n"
340  " -> number of subexpressions '%d' exceeds the maximum number allowed '%d'\n",
341  pattern, nsubs, RETIME_MAX_NSUBS);
342 
343  retime_free(retime);
344  return((RETime *)NULL);
345  }
346 
347  return(retime);
348 
349 MEMORY_ERROR:
350 
352  "Could not compile regex/time pattern: '%s'\n"
353  " -> memory allocation error\n",
354  pattern);
355 
356  if (retime) retime_free(retime);
357  return((RETime *)NULL);
358 }
359 
360 /**
361  * Compare a string with a compiled regex/time pattern.
362  *
363  * Results from the pattern match are stored in the following RETimeRes
364  * structure members:
365  *
366  * - year: year with century as a 4-digit integer
367  * - month: month number (1-12)
368  * - mday: day number in the month (1-31)
369  * - hour: hour (0-23)
370  * - min: minute (0-59)
371  * - sec: second (0-60; 60 may occur for leap seconds)
372  * - usec: micro-seconds
373  * - century: century number (year/100) as a 2-digit integer
374  * - yy: year number in century as a 2-digit integer
375  * - yday: day number in the year (1-366)
376  * - hhmm: hour * 100 + minute
377  * - secs1970: seconds since Epoch, 1970-01-01 00:00:00
378  * - offset: time offset in seconds
379  *
380  * All values that have not been set by the pattern match will be set to -1,
381  * except for the offset values which will be set to 0.
382  *
383  * Error messages from this function are sent to the message handler
384  * (see msngr_init_log() and msngr_init_mail()).
385  *
386  * @param retime - pointer to the compiled RETime structure
387  * @param string - string to compare with the regular expression
388  * @param res - pointer to the structure to store the results of
389  * the pattern match
390  *
391  * @return
392  * - 1 if match
393  * - 0 if no match
394  * - -1 if an error occurred
395  */
396 int retime_execute(RETime *retime, const char *string, RETimeRes *res)
397 {
398  regex_t *preg = &(retime->preg);
399  size_t nsubs = retime->nsubs;
400  regmatch_t *pmatch = res->pmatch;
401  size_t nmatch = nsubs + 1;
402 
403  char substr[RETIME_MAX_SUBSTR_LENGTH];
404  int negative;
405  int status;
406  int length;
407  char *chrp;
408  size_t mi;
409  char am_pm[8];
410 
411  /* Clear previous result */
412 
413  res->year = -1;
414  res->month = -1;
415  res->mday = -1;
416  res->hour = -1;
417  res->min = -1;
418  res->sec = -1;
419  res->usec = -1;
420  res->century = -1;
421  res->yy = -1;
422  res->yday = -1;
423  res->hhmm = -1;
424  res->secs1970 = -1;
425  res->offset.tv_sec = 0;
426  res->offset.tv_usec = 0;
427 
428  res->res_time = -1;
429  res->res_tv.tv_sec = -1;
430  res->res_tv.tv_usec = -1;
431 
432  /* Check for match */
433 
434  status = re_execute(preg, string, nmatch, pmatch, 0);
435  if (status < 0) return(-1);
436  if (status == 0) return (0);
437 
438  /* Set the results in the RETimeRes structure */
439 
440  am_pm[0] = '\0';
441 
442  for (mi = 1; mi < nmatch; mi++) {
443 
444  if (pmatch[mi].rm_so == -1) return(0);
445 
446  length = pmatch[mi].rm_eo - pmatch[mi].rm_so;
447 
448  if (length >= RETIME_MAX_SUBSTR_LENGTH) {
449 
451  "Invalid time string: '%s'\n"
452  " -> length of subexpression '%d' exceeds the maximum substring length '%d'\n",
453  string, mi, RETIME_MAX_SUBSTR_LENGTH);
454 
455  return(-1);
456  }
457 
458  strncpy(substr, (string + pmatch[mi].rm_so), length);
459  substr[length] = '\0';
460 
461  switch (retime->codes[mi]) {
462 
463  case 'C': // century number (year/100) as a 2-digit integer
464  res->century = atoi(substr);
465  break;
466  case 'd': // day number in the month (1-31).
467  case 'e': // day number in the month (1-31).
468  res->mday = atoi(substr);
469  break;
470  case 'h': // hour * 100 + minute (0-2400; 2400 is used by CDLs)
471  res->hhmm = atoi(substr);
472  break;
473  case 'H': // hour (0-23)
474  res->hour = atoi(substr);
475  break;
476  case 'j': // day number in the year (1-366).
477  res->yday = atoi(substr);
478  break;
479  case 'm': // month number (1-12)
480  res->month = atoi(substr);
481  break;
482  case 'M': // minute (0-59)
483  res->min = atoi(substr);
484  break;
485  case 'o': // time offset in seconds
486 
487  negative = (substr[0] == '-') ? 1 : 0;
488 
489  res->offset.tv_sec = atoi(substr);
490 
491  chrp = strchr(substr, '.');
492  if (chrp) {
493 
494  res->offset.tv_usec = (int)(atof(chrp) * 1.0E6 + 0.5);
495 
496  if (res->offset.tv_usec == 1.0E6) {
497 
498  if (negative) {
499  res->offset.tv_sec -= 1;
500  }
501  else {
502  res->offset.tv_sec += 1;
503  }
504 
505  res->offset.tv_usec = 0;
506  }
507 
508  if (negative) {
509  res->offset.tv_usec *= -1;
510  }
511  }
512 
513  break;
514  case 'p': // month number (1-12)
515  strcpy(am_pm, substr);
516  break;
517  case 'q': // seconds since Epoch, 1904-01-01 00:00:00 +0000 (UTC)
518 
519  res->secs1970 = (time_t)(atoll(substr) - 2082844800LL);
520 
521  chrp = strchr(substr, '.');
522  if (chrp) {
523  res->usec = (int)(atof(chrp) * 1.0E6 + 0.5);
524  if (res->usec == 1.0E6) {
525  res->secs1970 += 1;
526  res->usec = 0;
527  }
528  }
529 
530  break;
531 
532  case 's': // seconds since Epoch, 1970-01-01 00:00:00 +0000 (UTC)
533 
534  res->secs1970 = atoi(substr);
535 
536  chrp = strchr(substr, '.');
537  if (chrp) {
538  res->usec = (int)(atof(chrp) * 1.0E6 + 0.5);
539  if (res->usec == 1.0E6) {
540  res->secs1970 += 1;
541  res->usec = 0;
542  }
543  }
544 
545  break;
546 
547  case 'S': // second (0-60; 60 may occur for leap seconds)
548 
549  res->sec = atoi(substr);
550 
551  chrp = strchr(substr, '.');
552  if (chrp) {
553  res->usec = (int)(atof(chrp) * 1.0E6 + 0.5);
554  if (res->usec == 1.0E6) {
555  res->sec += 1;
556  res->usec = 0;
557  }
558  }
559 
560  break;
561  case 'y': // year within century (0-99)
562  res->yy = atoi(substr);
563  break;
564  case 'Y': // year with century as a 4-digit integer
565  res->year = atoi(substr);
566  break;
567  default:
568 
570  "Internal error in retime_execute() function\n"
571  " -> unsupported match code found: '%c'\n",
572  retime->codes[mi]);
573 
574  return(-1);
575  }
576  }
577 
578  /* Verify ranges and compute missing values where possible */
579 
580  if ((am_pm[0] != '\0') && (res->hour != -1)) {
581 
582  if (res->hour < 1 || res->hour > 12) return(0);
583 
584  if (strcasecmp(am_pm, "AM") == 0) {
585  if (res->hour == 12) res->hour = 0;
586  }
587  else { // PM
588  if (res->hour != 12) res->hour += 12;
589  }
590  }
591 
592  if ((res->month != -1) && (res->month > 12)) return(0);
593  if ((res->mday != -1) && (res->mday > 31)) return(0);
594  if ((res->hour != -1) && (res->hour > 23)) return(0);
595  if ((res->min != -1) && (res->min > 59)) return(0);
596  if ((res->sec != -1) && (res->sec > 60)) return(0);
597 
598  /* Compute year from century and/or year within century */
599 
600  if (res->year == -1) {
601 
602  if (res->yy != -1) {
603 
604  if (res->century != -1) {
605  res->year = (res->century * 100);
606  }
607  else {
608  res->year = (res->yy < 69) ? 2000 : 1900;
609  }
610 
611  res->year += res->yy;
612  }
613  }
614 
615  /* Compute month and day from day number in year */
616 
617  if (res->yday != -1) {
618 
619  if (res->yday > 366) return(0);
620 
621  if (res->year != -1) {
622 
623  yday_to_mday(res->yday,
624  &(res->year), &(res->month), &(res->mday));
625  }
626  }
627 
628  /* Compute hour and minute from hhmm format */
629 
630  if (res->hhmm != -1) {
631 
632  if (res->hhmm > 2400) return(0);
633 
634  res->hour = (int)(res->hhmm/100);
635  res->min = (int)(res->hhmm%100);
636 
637  if (res->min > 59) return(0);
638  }
639 
640  res->retime = retime;
641 
642  return(1);
643 }
644 
645 /**
646  * Free a RETime structure.
647  *
648  * @param retime - pointer to the RETime structure
649  */
650 void retime_free(RETime *retime)
651 {
652  if (retime) {
653  if (retime->tspattern) free(retime->tspattern);
654  if (retime->codes) free(retime->codes);
655  if (retime->pattern) free(retime->pattern);
656  regfree(&(retime->preg));
657  free(retime);
658  }
659 }
660 
661 /**
662  * Get the result from a regex/time pattern match.
663  *
664  * This function will use the result from retime_execute() stored in the
665  * RETime structure to compute the time in seconds since 1970.
666  *
667  * Error messages from this function are sent to the message handler
668  * (see msngr_init_log() and msngr_init_mail()).
669  *
670  * @param res - pointer to the RETimeRes structure
671  *
672  * @return
673  * - seconds since 1970
674  * - -1 if the year was not set in the structure
675  */
677 {
678  struct tm tm_time;
679  int usec;
680 
681  /* Don't compute result again */
682 
683  if (res->res_time != -1) {
684  return(res->res_time);
685  }
686 
687  /* Get seconds since 1970 */
688 
689  if (res->secs1970 != -1) {
690  res->res_time = res->secs1970;
691  }
692  else if (res->year == -1) {
693  return(-1);
694  }
695  else {
696 
697  memset(&tm_time, 0, sizeof(struct tm));
698 
699  tm_time.tm_year = res->year - 1900;
700 
701  if (res->month != -1) tm_time.tm_mon = res->month - 1;
702  if (res->mday != -1) tm_time.tm_mday = res->mday;
703  if (res->hour != -1) tm_time.tm_hour = res->hour;
704  if (res->min != -1) tm_time.tm_min = res->min;
705  if (res->sec != -1) tm_time.tm_sec = res->sec;
706 
707  res->res_time = timegm(&tm_time);
708  }
709 
710  /* Adjust time for offset and microseconds */
711 
712  usec = (res->usec == -1) ? 0: res->usec;
713 
714  if (res->offset.tv_sec || res->offset.tv_usec) {
715 
716  res->res_time += res->offset.tv_sec;
717  usec += res->offset.tv_usec;
718 
719  if (usec > 1.0E6) {
720  res->res_time += 1;
721  usec -= 1.0E6;
722  }
723  }
724 
725  if (usec) {
726  if (usec >= 0.5E6) {
727  res->res_time += 1;
728  }
729  else if (usec <= -0.5E6) {
730  res->res_time -= 1;
731  }
732  }
733 
734  return(res->res_time);
735 }
736 
737 /**
738  * Get the result from a regex/time pattern match.
739  *
740  * This function will use the result from retime_execute() stored in the
741  * RETime structure to compute the time in seconds since 1970.
742  *
743  * Error messages from this function are sent to the message handler
744  * (see msngr_init_log() and msngr_init_mail()).
745  *
746  * @param res - pointer to the RETimeRes structure
747  *
748  * @return
749  * - seconds since 1970
750  * - tv.tv_sec == -1 if the year was not set in the structure
751  */
753 {
754  struct tm tm_time;
755  int usec;
756 
757  /* Don't compute result again */
758 
759  if (res->res_tv.tv_sec != -1) {
760  return(res->res_tv);
761  }
762 
763  /* Get seconds since 1970 */
764 
765  if (res->secs1970 != -1) {
766  res->res_tv.tv_sec = res->secs1970;
767  }
768  else if (res->year == -1) {
769  return(res->res_tv);
770  }
771  else {
772 
773  memset(&tm_time, 0, sizeof(struct tm));
774 
775  tm_time.tm_year = res->year - 1900;
776 
777  if (res->month != -1) tm_time.tm_mon = res->month - 1;
778  if (res->mday != -1) tm_time.tm_mday = res->mday;
779  if (res->hour != -1) tm_time.tm_hour = res->hour;
780  if (res->min != -1) tm_time.tm_min = res->min;
781  if (res->sec != -1) tm_time.tm_sec = res->sec;
782 
783  res->res_tv.tv_sec = timegm(&tm_time);
784  }
785 
786  /* Compute microseconds and adjust for offsets */
787 
788  usec = (res->usec == -1) ? 0: res->usec;
789 
790  if (res->offset.tv_sec || res->offset.tv_usec) {
791 
792  res->res_tv.tv_sec += res->offset.tv_sec;
793  usec += res->offset.tv_usec;
794 
795  if (usec > 1.0E6) {
796  res->res_tv.tv_sec += 1;
797  usec -= 1.0E6;
798  }
799  }
800 
801  if (usec < 0) {
802  res->res_tv.tv_sec -= 1;
803  usec += 1.0E6;
804  }
805 
806  res->res_tv.tv_usec = usec;
807 
808  return(res->res_tv);
809 }
810 
811 /**
812  * Compile a list of regex/time patterns.
813  *
814  * See retime_compile() for a description of the pattern strings.
815  *
816  * The memory used by the returned RETimeList structure is dynamically
817  * allocated and must be freed by the calling process using the
818  * retime_list_free() function.
819  *
820  * Error messages from this function are sent to the message handler
821  * (see msngr_init_log() and msngr_init_mail()).
822  *
823  * @param npatterns - number of pattern strings in the list
824  * @param patterns - list of time string patterns containing a mixture of
825  * regex and time format characters similar to the
826  * strptime function.
827  * @param flags - reserved for control flags
828  *
829  * @return
830  * - pointer to the RETimeList structure
831  * - NULL if an error occurred
832  */
834  int npatterns,
835  const char **patterns,
836  int flags)
837 {
838  RETimeList *retime_list;
839  int pi;
840 
841  retime_list = calloc(1, sizeof(RETimeList));
842  if (!retime_list) {
844  "Memory allocation error creating RETimeList\n");
845  return((RETimeList *)NULL);
846  }
847 
848  retime_list->retimes = calloc(npatterns + 1, sizeof(RETime *));
849  if (!retime_list->retimes) {
851  "Memory allocation error creating RETimeList\n");
852  return((RETimeList *)NULL);
853  }
854 
855  retime_list->npatterns = npatterns;
856 
857  for (pi = 0; pi < npatterns; ++pi) {
858 
859  retime_list->retimes[pi] = retime_compile(patterns[pi], flags);
860  if (!retime_list->retimes[pi]) {
861  return((RETimeList *)NULL);
862  }
863  }
864 
865  return(retime_list);
866 }
867 
868 /**
869  * Compare a string with a list of regex/time patterns.
870  *
871  * Results from the pattern match are stored in the output RETime
872  * structure members:
873  *
874  * - year: year with century as a 4-digit integer
875  * - month: month number (1-12)
876  * - mday: day number in the month (1-31)
877  * - hour: hour (0-23)
878  * - min: minute (0-59)
879  * - sec: second (0-60; 60 may occur for leap seconds)
880  * - usec: micro-seconds
881  * - century: century number (year/100) as a 2-digit integer
882  * - yy: year number in century as a 2-digit integer
883  * - yday: day number in the year (1-366)
884  * - hhmm: hour * 100 + minute
885  * - secs1970: seconds since Epoch, 1970-01-01 00:00:00
886  * - offset: time offset in seconds
887  *
888  * All values that have not been set by the pattern match will be set to -1,
889  * except for the offset values which will be set to 0.
890  *
891  * Error messages from this function are sent to the message handler
892  * (see msngr_init_log() and msngr_init_mail()).
893  *
894  * @param retime_list - pointer to the RETimeList structure
895  * @param string - string to compare with the list of RETime patterns
896  * @param res - pointer to the structure to store the results of
897  * the first pattern that was matched
898  *
899  * @return
900  * - 1 if a match was found
901  * - 0 if no match was found
902  * - -1 if an error occurred
903  */
905  RETimeList *retime_list,
906  const char *string,
907  RETimeRes *res)
908 {
909  RETime *retime;
910  int status;
911  int pi;
912 
913  for (pi = 0; pi < retime_list->npatterns; ++pi) {
914 
915  retime = retime_list->retimes[pi];
916  status = retime_execute(retime, string, res);
917 
918  if (status < 0) return(-1);
919  if (status > 0) {
920  return(1);
921  }
922  }
923 
924  return(0);
925 }
926 
927 /**
928  * Free a RETimeList Structure
929  *
930  * @param retime_list - pointer to the RETimeList
931  */
932 void retime_list_free(RETimeList *retime_list)
933 {
934  int pi;
935 
936  if (retime_list) {
937 
938  if (retime_list->retimes) {
939 
940  for (pi = 0; pi < retime_list->npatterns; ++pi) {
941  retime_free(retime_list->retimes[pi]);
942  }
943 
944  free(retime_list->retimes);
945  }
946 
947  free(retime_list);
948  }
949 }