1 /***************************************************************************
2 * selection.c:
3 *
4 * Generic routines to manage selection lists.
5 *
6 * Written by Chad Trabant unless otherwise noted
7 * IRIS Data Management Center
8 *
9 * modified: 2014.197
10 ***************************************************************************/
11
12 #include <errno.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <time.h>
17
18 #include "libmseed.h"
19
20 static int ms_globmatch (char *string, char *pattern);
21
22 /***************************************************************************
23 * ms_matchselect:
24 *
25 * Test the specified parameters for a matching selection entry. The
26 * srcname parameter may contain globbing characters. The NULL value
27 * (matching any times) for the start and end times is HPTERROR.
28 *
29 * Return Selections pointer to matching entry on successful match and
30 * NULL for no match or error.
31 ***************************************************************************/
32 Selections *
ms_matchselect(Selections * selections,char * srcname,hptime_t starttime,hptime_t endtime,SelectTime ** ppselecttime)33 ms_matchselect (Selections *selections, char *srcname, hptime_t starttime,
34 hptime_t endtime, SelectTime **ppselecttime)
35 {
36 Selections *findsl = NULL;
37 SelectTime *findst = NULL;
38 SelectTime *matchst = NULL;
39
40 if (selections)
41 {
42 findsl = selections;
43 while (findsl)
44 {
45 if (ms_globmatch (srcname, findsl->srcname))
46 {
47 findst = findsl->timewindows;
48 while (findst)
49 {
50 if (starttime != HPTERROR && findst->starttime != HPTERROR &&
51 (starttime < findst->starttime && !(starttime <= findst->starttime && endtime >= findst->starttime)))
52 {
53 findst = findst->next;
54 continue;
55 }
56 else if (endtime != HPTERROR && findst->endtime != HPTERROR &&
57 (endtime > findst->endtime && !(starttime <= findst->endtime && endtime >= findst->endtime)))
58 {
59 findst = findst->next;
60 continue;
61 }
62
63 matchst = findst;
64 break;
65 }
66 }
67
68 if (matchst)
69 break;
70 else
71 findsl = findsl->next;
72 }
73 }
74
75 if (ppselecttime)
76 *ppselecttime = matchst;
77
78 return (matchst) ? findsl : NULL;
79 } /* End of ms_matchselect() */
80
81 /***************************************************************************
82 * msr_matchselect:
83 *
84 * A simple wrapper for calling ms_matchselect() using details from a
85 * MSRecord struct.
86 *
87 * Return Selections pointer to matching entry on successful match and
88 * NULL for no match or error.
89 ***************************************************************************/
90 Selections *
msr_matchselect(Selections * selections,MSRecord * msr,SelectTime ** ppselecttime)91 msr_matchselect (Selections *selections, MSRecord *msr, SelectTime **ppselecttime)
92 {
93 char srcname[50];
94 hptime_t endtime;
95
96 if (!selections || !msr)
97 return NULL;
98
99 msr_srcname (msr, srcname, 1);
100 endtime = msr_endtime (msr);
101
102 return ms_matchselect (selections, srcname, msr->starttime, endtime,
103 ppselecttime);
104 } /* End of msr_matchselect() */
105
106 /***************************************************************************
107 * ms_addselect:
108 *
109 * Add select parameters to a specified selection list. The srcname
110 * argument may contain globbing parameters. The NULL value (matching
111 * any value) for the start and end times is HPTERROR.
112 *
113 * Return 0 on success and -1 on error.
114 ***************************************************************************/
115 int
ms_addselect(Selections ** ppselections,char * srcname,hptime_t starttime,hptime_t endtime)116 ms_addselect (Selections **ppselections, char *srcname,
117 hptime_t starttime, hptime_t endtime)
118 {
119 Selections *newsl = NULL;
120 SelectTime *newst = NULL;
121
122 if (!ppselections || !srcname)
123 return -1;
124
125 /* Allocate new SelectTime and populate */
126 if (!(newst = (SelectTime *)calloc (1, sizeof (SelectTime))))
127 {
128 ms_log (2, "Cannot allocate memory\n");
129 return -1;
130 }
131
132 newst->starttime = starttime;
133 newst->endtime = endtime;
134
135 /* Add new Selections struct to begining of list */
136 if (!*ppselections)
137 {
138 /* Allocate new Selections and populate */
139 if (!(newsl = (Selections *)calloc (1, sizeof (Selections))))
140 {
141 ms_log (2, "Cannot allocate memory\n");
142 return -1;
143 }
144
145 strncpy (newsl->srcname, srcname, sizeof (newsl->srcname));
146 newsl->srcname[sizeof (newsl->srcname) - 1] = '\0';
147
148 /* Add new Selections struct as first in list */
149 *ppselections = newsl;
150 newsl->timewindows = newst;
151 }
152 else
153 {
154 Selections *findsl = *ppselections;
155 Selections *matchsl = 0;
156
157 /* Search for matching Selectlink entry */
158 while (findsl)
159 {
160 if (!strcmp (findsl->srcname, srcname))
161 {
162 matchsl = findsl;
163 break;
164 }
165
166 findsl = findsl->next;
167 }
168
169 if (matchsl)
170 {
171 /* Add time window selection to beginning of window list */
172 newst->next = matchsl->timewindows;
173 matchsl->timewindows = newst;
174 }
175 else
176 {
177 /* Allocate new Selections and populate */
178 if (!(newsl = (Selections *)calloc (1, sizeof (Selections))))
179 {
180 ms_log (2, "Cannot allocate memory\n");
181 return -1;
182 }
183
184 strncpy (newsl->srcname, srcname, sizeof (newsl->srcname));
185 newsl->srcname[sizeof (newsl->srcname) - 1] = '\0';
186
187 /* Add new Selections to beginning of list */
188 newsl->next = *ppselections;
189 *ppselections = newsl;
190 newsl->timewindows = newst;
191 }
192 }
193
194 return 0;
195 } /* End of ms_addselect() */
196
197 /***************************************************************************
198 * ms_addselect_comp:
199 *
200 * Add select parameters to a specified selection list based on
201 * separate name components. The network, station, location, channel
202 * and quality arguments may contain globbing parameters. The NULL
203 * value (matching any value) for the start and end times is HPTERROR.
204 *
205 * If any of the naming parameters are not supplied (pointer is NULL)
206 * a wildcard for all matches is substituted. As a special case, if
207 * the location ID (loc) is set to "--" to match a space-space/blank
208 * ID it will be translated to an empty string to match libmseed's
209 * notation.
210 *
211 * Return 0 on success and -1 on error.
212 ***************************************************************************/
213 int
ms_addselect_comp(Selections ** ppselections,char * net,char * sta,char * loc,char * chan,char * qual,hptime_t starttime,hptime_t endtime)214 ms_addselect_comp (Selections **ppselections, char *net, char *sta, char *loc,
215 char *chan, char *qual, hptime_t starttime, hptime_t endtime)
216 {
217 char srcname[100];
218 char selnet[20];
219 char selsta[20];
220 char selloc[20];
221 char selchan[20];
222 char selqual[20];
223
224 if (!ppselections)
225 return -1;
226
227 if (net)
228 {
229 strncpy (selnet, net, sizeof (selnet));
230 selnet[sizeof (selnet) - 1] = '\0';
231 }
232 else
233 strcpy (selnet, "*");
234
235 if (sta)
236 {
237 strncpy (selsta, sta, sizeof (selsta));
238 selsta[sizeof (selsta) - 1] = '\0';
239 }
240 else
241 strcpy (selsta, "*");
242
243 if (loc)
244 {
245 /* Test for special case blank location ID */
246 if (!strcmp (loc, "--"))
247 selloc[0] = '\0';
248 else
249 {
250 strncpy (selloc, loc, sizeof (selloc));
251 selloc[sizeof (selloc) - 1] = '\0';
252 }
253 }
254 else
255 strcpy (selloc, "*");
256
257 if (chan)
258 {
259 strncpy (selchan, chan, sizeof (selchan));
260 selchan[sizeof (selchan) - 1] = '\0';
261 }
262 else
263 strcpy (selchan, "*");
264
265 if (qual)
266 {
267 strncpy (selqual, qual, sizeof (selqual));
268 selqual[sizeof (selqual) - 1] = '\0';
269 }
270 else
271 strcpy (selqual, "?");
272
273 /* Create the srcname globbing match for this entry */
274 snprintf (srcname, sizeof (srcname), "%s_%s_%s_%s_%s",
275 selnet, selsta, selloc, selchan, selqual);
276
277 /* Add selection to list */
278 if (ms_addselect (ppselections, srcname, starttime, endtime))
279 return -1;
280
281 return 0;
282 } /* End of ms_addselect_comp() */
283
284 /***************************************************************************
285 * ms_readselectionsfile:
286 *
287 * Read a list of data selections from a file and them to the
288 * specified selections list. On errors this routine will leave
289 * allocated memory unreachable (leaked), it is expected that this is
290 * a program failing condition.
291 *
292 * As a special case if the filename is "-", selection lines will be
293 * read from stdin.
294 *
295 * Returns count of selections added on success and -1 on error.
296 ***************************************************************************/
297 int
ms_readselectionsfile(Selections ** ppselections,char * filename)298 ms_readselectionsfile (Selections **ppselections, char *filename)
299 {
300 FILE *fp;
301 hptime_t starttime;
302 hptime_t endtime;
303 char selectline[200];
304 char *selnet;
305 char *selsta;
306 char *selloc;
307 char *selchan;
308 char *selqual;
309 char *selstart;
310 char *selend;
311 char *cp;
312 char next;
313 int selectcount = 0;
314 int linecount = 0;
315
316 if (!ppselections || !filename)
317 return -1;
318
319 if (strcmp (filename, "-"))
320 {
321 if (!(fp = fopen (filename, "rb")))
322 {
323 ms_log (2, "Cannot open file %s: %s\n", filename, strerror (errno));
324 return -1;
325 }
326 }
327 else
328 {
329 /* Use stdin as special case */
330 fp = stdin;
331 }
332
333 while (fgets (selectline, sizeof (selectline) - 1, fp))
334 {
335 selnet = 0;
336 selsta = 0;
337 selloc = 0;
338 selchan = 0;
339 selqual = 0;
340 selstart = 0;
341 selend = 0;
342
343 linecount++;
344
345 /* Guarantee termination */
346 selectline[sizeof (selectline) - 1] = '\0';
347
348 /* End string at first newline character if any */
349 if ((cp = strchr (selectline, '\n')))
350 *cp = '\0';
351
352 /* Skip empty lines */
353 if (!strlen (selectline))
354 continue;
355
356 /* Skip comment lines */
357 if (*selectline == '#')
358 continue;
359
360 /* Parse: identify components of selection and terminate */
361 cp = selectline;
362 next = 1;
363 while (*cp)
364 {
365 if (*cp == ' ' || *cp == '\t')
366 {
367 *cp = '\0';
368 next = 1;
369 }
370 else if (*cp == '#')
371 {
372 *cp = '\0';
373 break;
374 }
375 else if (next && !selnet)
376 {
377 selnet = cp;
378 next = 0;
379 }
380 else if (next && !selsta)
381 {
382 selsta = cp;
383 next = 0;
384 }
385 else if (next && !selloc)
386 {
387 selloc = cp;
388 next = 0;
389 }
390 else if (next && !selchan)
391 {
392 selchan = cp;
393 next = 0;
394 }
395 else if (next && !selqual)
396 {
397 selqual = cp;
398 next = 0;
399 }
400 else if (next && !selstart)
401 {
402 selstart = cp;
403 next = 0;
404 }
405 else if (next && !selend)
406 {
407 selend = cp;
408 next = 0;
409 }
410 else if (next)
411 {
412 *cp = '\0';
413 break;
414 }
415 cp++;
416 }
417
418 /* Skip line if network, station, location and channel are not defined */
419 if (!selnet || !selsta || !selloc || !selchan)
420 {
421 ms_log (2, "[%s] Skipping data selection line number %d\n", filename, linecount);
422 continue;
423 }
424
425 if (selstart)
426 {
427 starttime = ms_seedtimestr2hptime (selstart);
428 if (starttime == HPTERROR)
429 {
430 ms_log (2, "Cannot convert data selection start time (line %d): %s\n", linecount, selstart);
431 return -1;
432 }
433 }
434 else
435 {
436 starttime = HPTERROR;
437 }
438
439 if (selend)
440 {
441 endtime = ms_seedtimestr2hptime (selend);
442 if (endtime == HPTERROR)
443 {
444 ms_log (2, "Cannot convert data selection end time (line %d): %s\n", linecount, selend);
445 return -1;
446 }
447 }
448 else
449 {
450 endtime = HPTERROR;
451 }
452
453 /* Add selection to list */
454 if (ms_addselect_comp (ppselections, selnet, selsta, selloc, selchan, selqual, starttime, endtime))
455 {
456 ms_log (2, "[%s] Error adding selection on line %d\n", filename, linecount);
457 return -1;
458 }
459
460 selectcount++;
461 }
462
463 if (fp != stdin)
464 fclose (fp);
465
466 return selectcount;
467 } /* End of ms_readselectionsfile() */
468
469 /***************************************************************************
470 * ms_freeselections:
471 *
472 * Free all memory associated with a Selections struct.
473 ***************************************************************************/
474 void
ms_freeselections(Selections * selections)475 ms_freeselections (Selections *selections)
476 {
477 Selections *select;
478 Selections *selectnext;
479 SelectTime *selecttime;
480 SelectTime *selecttimenext;
481
482 if (selections)
483 {
484 select = selections;
485
486 while (select)
487 {
488 selectnext = select->next;
489
490 selecttime = select->timewindows;
491
492 while (selecttime)
493 {
494 selecttimenext = selecttime->next;
495
496 free (selecttime);
497
498 selecttime = selecttimenext;
499 }
500
501 free (select);
502
503 select = selectnext;
504 }
505 }
506
507 } /* End of ms_freeselections() */
508
509 /***************************************************************************
510 * ms_printselections:
511 *
512 * Print the selections list using the ms_log() facility.
513 ***************************************************************************/
514 void
ms_printselections(Selections * selections)515 ms_printselections (Selections *selections)
516 {
517 Selections *select;
518 SelectTime *selecttime;
519 char starttime[50];
520 char endtime[50];
521
522 if (!selections)
523 return;
524
525 select = selections;
526 while (select)
527 {
528 ms_log (0, "Selection: %s\n", select->srcname);
529
530 selecttime = select->timewindows;
531 while (selecttime)
532 {
533 if (selecttime->starttime != HPTERROR)
534 ms_hptime2seedtimestr (selecttime->starttime, starttime, 1);
535 else
536 strncpy (starttime, "No start time", sizeof (starttime) - 1);
537
538 if (selecttime->endtime != HPTERROR)
539 ms_hptime2seedtimestr (selecttime->endtime, endtime, 1);
540 else
541 strncpy (endtime, "No end time", sizeof (endtime) - 1);
542
543 ms_log (0, " %30s %30s\n", starttime, endtime);
544
545 selecttime = selecttime->next;
546 }
547
548 select = select->next;
549 }
550 } /* End of ms_printselections() */
551
552 /***********************************************************************
553 * robust glob pattern matcher
554 * ozan s. yigit/dec 1994
555 * public domain
556 *
557 * glob patterns:
558 * * matches zero or more characters
559 * ? matches any single character
560 * [set] matches any character in the set
561 * [^set] matches any character NOT in the set
562 * where a set is a group of characters or ranges. a range
563 * is written as two characters seperated with a hyphen: a-z denotes
564 * all characters between a to z inclusive.
565 * [-set] set matches a literal hypen and any character in the set
566 * []set] matches a literal close bracket and any character in the set
567 *
568 * char matches itself except where char is '*' or '?' or '['
569 * \char matches char, including any pattern character
570 *
571 * examples:
572 * a*c ac abc abbc ...
573 * a?c acc abc aXc ...
574 * a[a-z]c aac abc acc ...
575 * a[-a-z]c a-c aac abc ...
576 *
577 * Revision 1.4 2004/12/26 12:38:00 ct
578 * Changed function name (amatch -> globmatch), variables and
579 * formatting for clarity. Also add matching header globmatch.h.
580 *
581 * Revision 1.3 1995/09/14 23:24:23 oz
582 * removed boring test/main code.
583 *
584 * Revision 1.2 94/12/11 10:38:15 oz
585 * charset code fixed. it is now robust and interprets all
586 * variations of charset [i think] correctly, including [z-a] etc.
587 *
588 * Revision 1.1 94/12/08 12:45:23 oz
589 * Initial revision
590 ***********************************************************************/
591
592 #define GLOBMATCH_TRUE 1
593 #define GLOBMATCH_FALSE 0
594 #define GLOBMATCH_NEGATE '^' /* std char set negation char */
595
596 /***********************************************************************
597 * ms_globmatch:
598 *
599 * Check if a string matches a globbing pattern.
600 *
601 * Return 0 if string does not match pattern and non-zero otherwise.
602 **********************************************************************/
603 static int
ms_globmatch(char * string,char * pattern)604 ms_globmatch (char *string, char *pattern)
605 {
606 int negate;
607 int match;
608 int c;
609
610 while (*pattern)
611 {
612 if (!*string && *pattern != '*')
613 return GLOBMATCH_FALSE;
614
615 switch (c = *pattern++)
616 {
617
618 case '*':
619 while (*pattern == '*')
620 pattern++;
621
622 if (!*pattern)
623 return GLOBMATCH_TRUE;
624
625 if (*pattern != '?' && *pattern != '[' && *pattern != '\\')
626 while (*string && *pattern != *string)
627 string++;
628
629 while (*string)
630 {
631 if (ms_globmatch (string, pattern))
632 return GLOBMATCH_TRUE;
633 string++;
634 }
635 return GLOBMATCH_FALSE;
636
637 case '?':
638 if (*string)
639 break;
640 return GLOBMATCH_FALSE;
641
642 /* set specification is inclusive, that is [a-z] is a, z and
643 * everything in between. this means [z-a] may be interpreted
644 * as a set that contains z, a and nothing in between.
645 */
646 case '[':
647 if (*pattern != GLOBMATCH_NEGATE)
648 negate = GLOBMATCH_FALSE;
649 else
650 {
651 negate = GLOBMATCH_TRUE;
652 pattern++;
653 }
654
655 match = GLOBMATCH_FALSE;
656
657 while (!match && (c = *pattern++))
658 {
659 if (!*pattern)
660 return GLOBMATCH_FALSE;
661
662 if (*pattern == '-') /* c-c */
663 {
664 if (!*++pattern)
665 return GLOBMATCH_FALSE;
666 if (*pattern != ']')
667 {
668 if (*string == c || *string == *pattern ||
669 (*string > c && *string < *pattern))
670 match = GLOBMATCH_TRUE;
671 }
672 else
673 { /* c-] */
674 if (*string >= c)
675 match = GLOBMATCH_TRUE;
676 break;
677 }
678 }
679 else /* cc or c] */
680 {
681 if (c == *string)
682 match = GLOBMATCH_TRUE;
683 if (*pattern != ']')
684 {
685 if (*pattern == *string)
686 match = GLOBMATCH_TRUE;
687 }
688 else
689 break;
690 }
691 }
692
693 if (negate == match)
694 return GLOBMATCH_FALSE;
695
696 /* If there is a match, skip past the charset and continue on */
697 while (*pattern && *pattern != ']')
698 pattern++;
699 if (!*pattern++) /* oops! */
700 return GLOBMATCH_FALSE;
701 break;
702
703 case '\\':
704 if (*pattern)
705 c = *pattern++;
706 default:
707 if (c != *string)
708 return GLOBMATCH_FALSE;
709 break;
710 }
711
712 string++;
713 }
714
715 return !*string;
716 } /* End of ms_globmatch() */
717