20221212
[devspec.git] / devspec.en_US / project / recutils / src / rec-fex.c
blob47910f0da7bffe5d028d98cdd5a6bfb2c011ae02
1 /* -*- mode: C -*-
3 * File: rec-fex.c
4 * Date: Sun Apr 11 21:31:32 2010
6 * GNU recutils - Field Expressions
8 */
10 /* Copyright (C) 2010-2019 Jose E. Marchesi */
12 /* This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include <config.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <gettext.h>
31 #define _(str) dgettext (PACKAGE, str)
33 #include <rec-utils.h>
34 #include <rec.h>
37 * Data types.
40 struct rec_fex_elem_s
42 char *str;
44 char *field_name;
45 char *rewrite_to;
46 int max;
47 int min;
49 char *function_name;
50 void *function_data;
53 #define REC_FEX_MAX_ELEMS 256
55 struct rec_fex_s
57 int num_elems;
58 char *str;
59 rec_fex_elem_t elems[REC_FEX_MAX_ELEMS];
63 * Static function declarations.
66 static void rec_fex_init (rec_fex_t fex);
68 static bool rec_fex_parse_str_simple (rec_fex_t new, const char *str, const char *sep);
69 static bool rec_fex_parse_str_subscripts (rec_fex_t new, const char *str);
70 static bool rec_fex_parse_elem (rec_fex_elem_t elem, const char *str);
73 * Public functions.
76 rec_fex_t
77 rec_fex_new (const char *str,
78 enum rec_fex_kind_e kind)
80 rec_fex_t new;
81 int i;
83 new = malloc (sizeof (struct rec_fex_s));
84 if (new)
86 rec_fex_init (new);
88 new->num_elems = 0;
89 new->str = NULL;
90 for (i = 0; i < REC_FEX_MAX_ELEMS; i++)
92 new->elems[i] = 0;
95 if (str != NULL)
97 /* Parse the string, using the proper parsing routine
98 depending on the kind of field expression requested by
99 the user. */
101 if (kind == REC_FEX_SUBSCRIPTS)
103 if (!rec_fex_parse_str_subscripts (new, str))
105 /* Out of memory or parse error. */
106 free (new);
107 return NULL;
110 else if (kind == REC_FEX_SIMPLE)
112 if (!rec_fex_parse_str_simple (new, str, " \t\n"))
114 /* Out of memory or parse error. */
115 free (new);
116 return NULL;
119 else /* REC_FEX_CSV */
121 if (!rec_fex_parse_str_simple (new, str, ","))
123 /* Out of memory or parse error. */
124 free (new);
125 return NULL;
131 return new;
134 void
135 rec_fex_destroy (rec_fex_t fex)
137 int i;
139 if (fex)
141 for (i = 0; i < fex->num_elems; i++)
143 free (fex->elems[i]->rewrite_to);
144 free (fex->elems[i]->field_name);
145 free (fex->elems[i]->str);
146 free (fex->elems[i]);
149 free (fex->str);
150 free (fex);
154 rec_fex_t
155 rec_fex_dup (rec_fex_t fex)
157 rec_fex_t copy = NULL;
158 size_t i = 0;
160 copy = malloc (sizeof (struct rec_fex_s));
161 if (copy)
163 rec_fex_init (copy);
165 copy->num_elems = fex->num_elems;
166 copy->str = strdup (fex->str);
167 if (!copy->str)
169 /* Out of memory. */
170 rec_fex_destroy (copy);
171 return NULL;
174 for (i = 0; i < fex->num_elems; i++)
176 if (fex->elems[i] == NULL)
178 copy->elems[i] = NULL;
179 continue;
182 copy->elems[i] = malloc (sizeof (struct rec_fex_elem_s));
183 if (!copy->elems[i])
185 /* Out of memory. */
186 rec_fex_destroy (copy);
187 return NULL;
190 copy->elems[i]->max = fex->elems[i]->max;
191 copy->elems[i]->min = fex->elems[i]->min;
193 #define REC_COPY_STR_MAYBE_RETURN(FNAME) \
194 do \
196 if (!fex->elems[i]->FNAME) \
198 copy->elems[i]->FNAME = NULL; \
200 else \
202 copy->elems[i]->FNAME = strdup (fex->elems[i]->FNAME); \
203 if (!copy->elems[i]->FNAME) \
205 /* Out of memory. */ \
206 rec_fex_destroy (copy); \
207 return NULL; \
210 } while (0)
212 REC_COPY_STR_MAYBE_RETURN (str);
213 REC_COPY_STR_MAYBE_RETURN (field_name);
214 REC_COPY_STR_MAYBE_RETURN (rewrite_to);
215 REC_COPY_STR_MAYBE_RETURN (function_name);
219 return copy;
222 bool
223 rec_fex_check (const char *str, enum rec_fex_kind_e kind)
225 char *regexp_str;
227 switch (kind)
229 case REC_FEX_SIMPLE:
231 regexp_str = "^" REC_FNAME_LIST_RE "$";
232 break;
234 case REC_FEX_CSV:
236 regexp_str = "^" REC_FNAME_LIST_CS_RE "$";
237 break;
239 case REC_FEX_SUBSCRIPTS:
241 regexp_str = "^" REC_FNAME_LIST_SUB_RE "$";
242 break;
244 default:
246 regexp_str = NULL;
247 break;
251 return rec_match (str, regexp_str);
254 size_t
255 rec_fex_size (rec_fex_t fex)
257 return fex->num_elems;
260 rec_fex_elem_t
261 rec_fex_get (rec_fex_t fex,
262 size_t position)
264 if ((position < 0) || (position >= fex->num_elems))
266 return NULL;
269 return fex->elems[position];
272 const char *
273 rec_fex_elem_field_name (rec_fex_elem_t elem)
275 return elem->field_name;
278 bool
279 rec_fex_elem_set_field_name (rec_fex_elem_t elem,
280 const char *fname)
282 free (elem->field_name);
283 elem->field_name = strdup (fname);
284 return (elem->field_name != NULL);
288 rec_fex_elem_min (rec_fex_elem_t elem)
290 return elem->min;
294 rec_fex_elem_max (rec_fex_elem_t elem)
296 return elem->max;
299 const char *
300 rec_fex_elem_rewrite_to (rec_fex_elem_t elem)
302 return elem->rewrite_to;
305 void
306 rec_fex_sort (rec_fex_t fex)
308 bool done;
309 rec_fex_elem_t aux;
310 int i, j;
312 /* XXX: this code only works when 'max' is not specified. */
314 for (i = 1; i < fex->num_elems; i++)
316 aux = fex->elems[i];
317 j = i - 1;
318 done = false;
320 while (!done)
322 /* If elems[j] > aux */
323 if ((fex->elems[j]->min == -1) || (fex->elems[j]->min > aux->min))
325 fex->elems[j + 1] = fex->elems[j];
326 j = j - 1;
327 if (j < 0)
329 done = true;
332 else
334 done = true;
338 fex->elems[j + 1] = aux;
342 char *
343 rec_fex_str (rec_fex_t fex,
344 enum rec_fex_kind_e kind)
346 char *result;
347 size_t result_size;
348 rec_buf_t buf;
349 size_t i;
350 char *tmp;
352 result = NULL;
353 buf = rec_buf_new (&result, &result_size);
354 if (buf)
356 char *field_str = NULL;
358 for (i = 0; i < fex->num_elems; i++)
360 if (i != 0)
362 if (kind == REC_FEX_SIMPLE)
364 rec_buf_putc (' ', buf);
366 else
368 rec_buf_putc (',', buf);
372 field_str = strdup (fex->elems[i]->field_name);
373 if (!field_str)
375 rec_buf_close (buf);
376 free (result);
377 return NULL;
380 rec_buf_puts (field_str, buf);
381 free (field_str);
383 if (kind == REC_FEX_SUBSCRIPTS)
385 if ((fex->elems[i]->min != -1)
386 || (fex->elems[i]->max != -1))
388 rec_buf_putc ('[', buf);
389 if (fex->elems[i]->min != -1)
391 if (asprintf (&tmp, "%d", fex->elems[i]->min) != -1)
393 rec_buf_puts (tmp, buf);
394 free (tmp);
397 if (fex->elems[i]->max != -1)
399 if (asprintf (&tmp, "-%d", fex->elems[i]->max) != -1)
401 rec_buf_puts (tmp, buf);
402 free (tmp);
406 rec_buf_putc (']', buf);
412 rec_buf_close (buf);
414 return result;
417 bool
418 rec_fex_member_p (rec_fex_t fex,
419 const char *fname,
420 int min,
421 int max)
423 bool res = false;
424 int i;
426 for (i = 0; i < fex->num_elems; i++)
428 if (rec_field_name_equal_p (fname,
429 fex->elems[i]->field_name)
430 && ((min == -1) || (fex->elems[i]->min == min))
431 && ((max == -1) || (fex->elems[i]->max == max)))
433 res = true;
434 break;
438 return res;
441 rec_fex_elem_t
442 rec_fex_append (rec_fex_t fex,
443 const char *fname,
444 int min,
445 int max)
447 rec_fex_elem_t new_elem;
449 if (fex->num_elems >= REC_FEX_MAX_ELEMS)
451 fprintf (stderr, _("internal error: REC_FEX_MAX_ELEMS exceeded. Please report this.\n"));
452 return NULL;
455 new_elem = malloc (sizeof (struct rec_fex_elem_s));
456 if (new_elem)
458 memset (new_elem, 0, sizeof (*new_elem));
459 new_elem->field_name = strdup (fname);
460 if (!new_elem->field_name)
462 /* Out of memory. */
463 free (new_elem);
464 return NULL;
467 new_elem->str = strdup (fname);
468 if (!new_elem->str)
470 /* Out of memory. */
471 free (new_elem->field_name);
472 free (new_elem);
473 return NULL;
476 new_elem->min = min;
477 new_elem->max = max;
478 fex->elems[fex->num_elems++] = new_elem;
481 return new_elem;
484 const char *
485 rec_fex_elem_function_name (rec_fex_elem_t elem)
487 return elem->function_name;
490 void **
491 rec_fex_elem_function_data (rec_fex_elem_t elem)
493 return elem->function_data;
496 bool
497 rec_fex_all_calls_p (rec_fex_t fex)
499 bool result = true;
500 size_t i = 0;
502 for (i = 0; i < fex->num_elems; i++)
504 if (fex->elems[i]->function_name == NULL)
506 result = false;
507 break;
511 return result;
515 * Private functions.
518 static void
519 rec_fex_init (rec_fex_t fex)
521 /* Initialize the field expression structure so it can be safely
522 passed to rec_fex_destroy even if its contents are not completely
523 initialized with real values. */
525 memset (fex, 0 /* NULL */, sizeof (struct rec_fex_s));
528 static bool
529 rec_fex_parse_str_simple (rec_fex_t new,
530 const char *str,
531 const char *sep)
533 bool res;
534 rec_fex_elem_t elem;
535 char *fex_str, *fex_str_orig;
536 char *elem_str;
537 size_t i;
539 if (!str)
541 return false;
544 fex_str = strdup (str);
545 if (!fex_str)
547 return false;
549 fex_str_orig = fex_str;
551 res = true;
553 elem_str = strsep (&fex_str, sep);
556 if (strlen (elem_str) > 0)
558 if ((elem = malloc (sizeof (struct rec_fex_elem_s))))
560 const char *p = elem_str;
562 /* Get the field name. */
564 if (!rec_parse_regexp (&p,
565 "^" REC_FNAME_RE,
566 &(elem->field_name)))
568 /* Parse error. */
569 free (elem);
570 res = false;
571 break;
574 /* Get the subname, if any, and modify the name
575 accordingly. */
577 if (*p == '.')
579 char *subname = NULL;
581 p++;
582 if (!rec_parse_regexp (&p,
583 "^" REC_FNAME_RE,
584 &subname))
586 /* Parse error. */
587 free (elem->field_name);
588 free (elem);
589 res = false;
590 break;
593 /* Concatenate the field name and the subname. */
594 elem->field_name = rec_concat_strings (elem->field_name, "_", subname);
597 /* Check that there are no extra stuff at the end of the
598 string. */
600 if (*p != '\0')
602 free (elem->field_name);
603 free (elem);
604 res = false;
605 break;
608 /* Initialize other attributes of the fex entry. */
610 elem->function_name = NULL;
611 elem->function_data = NULL;
612 elem->rewrite_to = NULL;
613 elem->str = strdup (elem_str);
614 elem->min = -1;
615 elem->max = -1;
616 new->elems[new->num_elems++] = elem;
618 else
620 res = false;
621 break;
625 while ((elem_str = strsep (&fex_str, sep)));
627 if (new->num_elems == 0)
629 /* No elements were recognized. */
630 res = false;
633 if (res)
635 new->str = strdup (str);
637 else
639 /* Destroy parsed elements. */
640 for (i = 0; i < new->num_elems; i++)
642 free (new->elems[i]->rewrite_to);
643 free (new->elems[i]->field_name);
644 free (new->elems[i]->str);
645 free (new->elems[i]);
649 free (fex_str_orig);
650 return res;
653 static bool
654 rec_fex_parse_str_subscripts (rec_fex_t new,
655 const char *str)
657 bool res;
658 char *elem_str;
659 char *fex_str, *fex_str_orig;
660 rec_fex_elem_t elem;
661 int i;
663 res = true;
665 fex_str = strdup (str);
666 if (!fex_str)
668 return false;
670 fex_str_orig = fex_str;
672 elem_str = strsep (&fex_str, ",");
675 elem = malloc (sizeof (struct rec_fex_elem_s));
676 if (!elem)
678 /* Out of memory. */
679 res = false;
680 break;
683 if (!rec_fex_parse_elem (elem, elem_str))
685 /* Parse error. */
686 for (i = 0; i < new->num_elems; i++)
688 free (new->elems[i]->field_name);
689 free (new->elems[i]->str);
690 free (new->elems[i]);
693 free (elem);
694 res = false;
695 break;
698 /* Add the elem to the FEX. */
699 new->elems[new->num_elems++] = elem;
701 while ((elem_str = strsep (&fex_str, ",")));
703 if (res)
705 new->str = strdup (str);
708 free (fex_str_orig);
709 return res;
712 static bool
713 rec_fex_parse_elem (rec_fex_elem_t elem,
714 const char *str)
716 bool ret;
717 const char *p;
719 ret = true;
720 p = str;
722 /* 'Empty' part. */
723 elem->field_name = NULL;
724 elem->function_name = NULL;
725 elem->function_data = NULL;
726 elem->str = NULL;
727 elem->rewrite_to = NULL;
728 elem->min = -1;
729 elem->max = -1;
731 /* The 'str' field keeps a copy of the textual entry. */
733 elem->str = strdup (str);
735 /* Each FEX element can be either a function call or a field name
736 with an optional subscript. */
738 if (rec_match (p, "^" REC_FEX_CALL))
740 /* Get the function name and the field argument and store them
741 in the FEX element. */
743 if (!rec_parse_regexp (&p,
744 "^" REC_FEX_FUNCTION_NAME,
745 &(elem->function_name)))
747 /* Parse error. */
748 free (elem->str);
749 return false;
752 p++; /* Skip the ( */
755 /* Get the field name. */
757 if (!rec_parse_regexp (&p,
758 "^" REC_FNAME_RE,
759 &(elem->field_name)))
761 /* Parse error. */
762 free (elem->str);
763 return false;
766 /* Get the subname and modify the name accordingly, if it
767 exists. */
769 if (*p == '.')
771 char *subname = NULL;
773 p++;
774 if (!rec_parse_regexp (&p,
775 "^" REC_FNAME_RE,
776 &subname))
778 /* Parse error. */
779 free (elem->str);
780 return false;
783 /* Concatenate the field_name and the subname. */
784 elem->field_name = rec_concat_strings (elem->field_name, "_", subname);
787 /* Get the subscripts if they are present. */
788 if (*p == '[')
790 p++;
791 /* First subscript in range. */
792 if (!rec_parse_int (&p, &(elem->min)))
794 /* Parse error. */
795 free (elem->str);
796 free (elem->field_name);
797 return false;
800 if (*p == '-')
802 p++;
803 /* Second subscript in range. */
804 if (!rec_parse_int (&p, &(elem->max)))
806 /* Parse error. */
807 free (elem->str);
808 free (elem->field_name);
809 return false;
813 if (*p != ']')
815 /* Parse error. */
816 free (elem->str);
817 free (elem->field_name);
818 return false;
820 p++; /* Skip the ] */
823 if (elem->function_name)
825 p++; /* Skip the ) */
828 /* Get the rewrite rule if it is present. */
829 if (*p == ':')
831 p++;
832 if (!rec_parse_regexp (&p,
833 "^" REC_FNAME_RE,
834 &(elem->rewrite_to)))
836 /* Parse error. */
837 free (elem->str);
838 free (elem->field_name);
839 return false;
843 if (*p != '\0')
845 free (elem->str);
846 free (elem->field_name);
847 free (elem->rewrite_to);
848 return false;
851 return ret;
854 /* End of rec-fex.c */