1 /* Routines for dealing with field definitions and contents.
2 Copyright (C) 2000 Free Software Foundation, Inc.
3 Contributed by Bob Manson (manson@juniper.net).
4
5 This file is part of GNU GNATS.
6
7 GNU GNATS is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU GNATS is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU GNATS; see the file COPYING. If not, write to the Free
19 Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
20
21 #include "gnats.h"
22 #include "field.h"
23
24 enum complex_field_index_datatype {
25 InvalidComplexFieldIndexDatatype, HeaderNameValue, FieldIndexValue,
26 FieldTypeValue, StringValue
27 };
28
29 struct complex_field_index
30 {
31 DatabaseInfo database;
32 char *name;
33 enum complex_field_index_datatype dtype;
34 union field_list_u {
35 Header_Name headerIndex;
36 FieldIndex index;
37 FieldType fieldType;
38 } datum;
39 char *admSubfield;
40 int isReason;
41 int isOldPr;
42 int isRaw;
43 };
44
45 struct databaseFieldInfo
46 {
47 FieldDef *fieldList;
48 int fieldListSize;
49 int lastFieldEnt;
50 int *namePosition;
51 };
52
53 static void
insertNewFieldDef(DatabaseInfo database,FieldDef field)54 insertNewFieldDef (DatabaseInfo database, FieldDef field)
55 {
56 DatabaseFieldInfo *dinfoPtr = getDatabaseFieldInfoWrite (database);
57 DatabaseFieldInfo dinfo = *dinfoPtr;
58
59 field->database = database;
60 if (dinfoPtr == NULL)
61 {
62 abort ();
63 }
64 if (dinfo == NULL)
65 {
66 dinfo = (*dinfoPtr =
67 (DatabaseFieldInfo)
68 xmalloc (sizeof (struct databaseFieldInfo)));
69 dinfo->fieldList = (FieldDef *) xmalloc (sizeof (FieldDef) * 10);
70 dinfo->namePosition = (int *) xmalloc (sizeof (int) * 10);
71 dinfo->fieldListSize = 10;
72 dinfo->lastFieldEnt = 0;
73 }
74 else if (dinfo->lastFieldEnt >= dinfo->fieldListSize)
75 {
76 dinfo->fieldListSize += 10;
77 dinfo->fieldList =
78 (FieldDef *) xrealloc (dinfo->fieldList,
79 sizeof (FieldDef) * dinfo->fieldListSize);
80 dinfo->namePosition =
81 (int *) xrealloc (dinfo->namePosition,
82 sizeof (int) * dinfo->fieldListSize);
83 }
84 field->number = dinfo->lastFieldEnt;
85 dinfo->fieldList[dinfo->lastFieldEnt] = field;
86
87 {
88 int x, y;
89
90 for (x = 0; x < dinfo->lastFieldEnt; x++)
91 {
92 char *name = dinfo->fieldList[dinfo->namePosition[x]]->name;
93 if (strcasecmp (name, field->name) > 0)
94 {
95 break;
96 }
97 }
98 for (y = dinfo->lastFieldEnt - 1; y >= x; y--)
99 {
100 dinfo->namePosition[y + 1] = dinfo->namePosition[y];
101 }
102 dinfo->namePosition[x] = dinfo->lastFieldEnt;
103 dinfo->lastFieldEnt++;
104 }
105 }
106
107 FieldDef
newFieldDef(DatabaseInfo database,char * name)108 newFieldDef (DatabaseInfo database, char *name)
109 {
110 if (find_field_index (database, name) == InvalidFieldIndex)
111 {
112 FieldDef res = (FieldDef) xmalloc (sizeof (struct field_def));
113 memset (res, 0, sizeof (struct field_def));
114 res->name = name;
115 insertNewFieldDef (database, res);
116 return res;
117 }
118 else
119 {
120 return NULL;
121 }
122 }
123
124 static void
freeFieldDef(FieldDef def)125 freeFieldDef (FieldDef def)
126 {
127 if (def->name != NULL)
128 {
129 free (def->name);
130 }
131 if (def->description != NULL)
132 {
133 free (def->description);
134 }
135 if (def->default_value != NULL)
136 {
137 free (def->default_value);
138 }
139 freeStringList (def->enumValues);
140 if (def->adm_db_name != NULL)
141 {
142 free (def->adm_db_name);
143 }
144 if (def->multiEnumSeparator != NULL)
145 {
146 free (def->multiEnumSeparator);
147 }
148
149 if (def->auxFlags != NULL)
150 {
151 free (def->auxFlags);
152 }
153
154 if (def->input_default_value != NULL)
155 {
156 free (def->input_default_value);
157 }
158
159 freeAdmFieldDesc (def->adm_field_des);
160 freeAdmEntryChain (def->adm_contents);
161 freeStringList (def->regex);
162 freeChangeActions (def->changeActions);
163 freeQueryFormat (def->virtualFormat);
164 free (def);
165 }
166
167 FieldIndex
getNthField(const DatabaseInfo database,int n)168 getNthField (const DatabaseInfo database, int n)
169 {
170 DatabaseFieldInfo dinfo = getDatabaseFieldInfo (database);
171
172 if (dinfo == NULL)
173 {
174 return InvalidFieldIndex;
175 }
176 else if (n < 0 || n >= dinfo->lastFieldEnt)
177 {
178 return InvalidFieldIndex;
179 }
180 else
181 {
182 return dinfo->fieldList[n];
183 }
184 }
185
186 char *
getFieldFlags(const FieldIndex field)187 getFieldFlags (const FieldIndex field)
188 {
189 FieldDef def = fieldDefForIndex (field);
190 char *res = xstrdup ("");
191
192 if (def == NULL)
193 {
194 return NULL;
195 }
196 if (def->textsearch)
197 {
198 append_string (&res, "textsearch ");
199 }
200 if (def->allow_any_value)
201 {
202 append_string (&res, "allowAnyValue ");
203 }
204 if (requiresChangeReason (def))
205 {
206 append_string (&res, "requireChangeReason ");
207 }
208 if (def->readonly)
209 {
210 append_string (&res, "readonly ");
211 }
212 if (def->auxFlags != NULL)
213 {
214 StringList *l = def->auxFlags;
215 while (l != NULL)
216 {
217 append_string (&res, l->name);
218 l = l->next;
219 }
220 }
221 return res;
222 }
223
224 int
get_num_fields(const DatabaseInfo database)225 get_num_fields (const DatabaseInfo database)
226 {
227 DatabaseFieldInfo dinfo = getDatabaseFieldInfo (database);
228
229 if (dinfo == NULL)
230 {
231 return -1;
232 }
233 return dinfo->lastFieldEnt;
234 }
235
236 static ComplexFieldIndex emptyEnt = NULL;
237
238 void
freeComplexFieldIndex(ComplexFieldIndex i)239 freeComplexFieldIndex (ComplexFieldIndex i)
240 {
241 if (i != NULL)
242 {
243 if (i->name != NULL)
244 {
245 free (i->name);
246 }
247 if (i->admSubfield != NULL)
248 {
249 free (i->admSubfield);
250 }
251 if (emptyEnt == NULL)
252 {
253 emptyEnt = i;
254 }
255 else
256 {
257 free (i);
258 }
259 }
260 }
261
262 static ComplexFieldIndex
allocComplexFieldIndex(void)263 allocComplexFieldIndex (void)
264 {
265 ComplexFieldIndex res;
266
267 if (emptyEnt != NULL)
268 {
269 res = emptyEnt;
270 emptyEnt = NULL;
271 }
272 else
273 {
274 res = (ComplexFieldIndex) xmalloc (sizeof (struct complex_field_index));
275 }
276 return res;
277 }
278
279
280 void
freeFieldList(FieldList list)281 freeFieldList (FieldList list)
282 {
283 FieldList n;
284 while (list != NULL)
285 {
286 n = list->next;
287 freeComplexFieldIndex (list->ent);
288 free (list);
289 list = n;
290 }
291 }
292
293 void
freeFieldEdit(FieldEdit * edit)294 freeFieldEdit (FieldEdit *edit)
295 {
296 FieldEdit *n;
297 while (edit != NULL)
298 {
299 n = edit->next;
300 if (edit->fieldToEditName != NULL)
301 {
302 free (edit->fieldToEditName);
303 }
304 if (edit->textFormat != NULL)
305 {
306 free (edit->textFormat);
307 }
308 freeFieldList (edit->fieldsForFormat);
309 free (edit);
310 edit = n;
311 }
312 }
313
314 void
clearFieldList(DatabaseInfo database)315 clearFieldList (DatabaseInfo database)
316 {
317 DatabaseFieldInfo *dinfoPtr = getDatabaseFieldInfoWrite (database);
318 if (dinfoPtr != NULL && *dinfoPtr != NULL)
319 {
320 DatabaseFieldInfo dinfo = *dinfoPtr;
321 int x;
322
323 for (x = 0; x < dinfo->lastFieldEnt; x++)
324 {
325 freeFieldDef (dinfo->fieldList[x]);
326 }
327 free (dinfo->fieldList);
328 dinfo->fieldList = NULL;
329 dinfo->fieldListSize = 0;
330 dinfo->lastFieldEnt = 0;
331 if (dinfo->namePosition != NULL)
332 {
333 free (dinfo->namePosition);
334 dinfo->namePosition = NULL;
335 }
336 free (dinfo);
337 *dinfoPtr = NULL;
338 }
339 }
340
341 FieldList
newFieldListEnt(const DatabaseInfo database,const char * name,FieldList next)342 newFieldListEnt (const DatabaseInfo database, const char *name, FieldList next)
343 {
344 FieldList res = (FieldList) xmalloc (sizeof (struct field_list));
345 res->ent = newComplexFieldIndex (database, name);
346 res->next = next;
347 return res;
348 }
349
350 ComplexFieldIndex
newComplexFieldIndex(const DatabaseInfo database,const char * name)351 newComplexFieldIndex (const DatabaseInfo database, const char *name)
352 {
353 ComplexFieldIndex res = allocComplexFieldIndex ();
354
355 res->database = database;
356 res->name = xstrdup (name);
357 res->dtype = InvalidComplexFieldIndexDatatype;
358 res->admSubfield = NULL;
359 res->isReason = 0;
360 res->isOldPr = 0;
361 res->isRaw = 0;
362 return res;
363 }
364
365 ComplexFieldIndex
newComplexFieldLiteralString(const char * string)366 newComplexFieldLiteralString (const char *string)
367 {
368 ComplexFieldIndex res = allocComplexFieldIndex ();
369
370 res->database = NULL;
371 res->name = xstrdup (string);
372 res->dtype = StringValue;
373 res->admSubfield = NULL;
374 res->isReason = 0;
375 res->isOldPr = 0;
376 res->isRaw = 0;
377 return res;
378 }
379
380 ComplexFieldIndex
simpleComplexFieldIndex(FieldIndex index)381 simpleComplexFieldIndex (FieldIndex index)
382 {
383 ComplexFieldIndex res = allocComplexFieldIndex ();
384
385 res->database = index->database;
386 res->name = NULL;
387 res->datum.index = index;
388 res->dtype = FieldIndexValue;
389 res->admSubfield = NULL;
390 res->isReason = 0;
391 res->isOldPr = 0;
392 res->isRaw = 0;
393 return res;
394 }
395
396 /* Fill in the missing fields of ENT. */
397
398 int
parseComplexFieldIndex(ComplexFieldIndex ent)399 parseComplexFieldIndex (ComplexFieldIndex ent)
400 {
401 if (ent->dtype == StringValue)
402 {
403 return 0;
404 }
405 else if (ent->name != NULL)
406 {
407 size_t nlen = strlen (ent->name);
408 char *name = ent->name;
409
410 if (name[0] == '$')
411 {
412 return 0;
413 }
414
415 ent->admSubfield = NULL;
416 ent->dtype = InvalidComplexFieldIndexDatatype;
417 ent->isReason = 0;
418 ent->isRaw = 0;
419 ent->isOldPr = 0;
420
421 if (name[nlen - 1] == ']')
422 {
423 char *ptr = strchr (name, '[');
424
425 if (ptr != NULL && name[nlen - 1])
426 {
427 name[nlen - 1] = '\0';
428 ent->admSubfield = xstrdup (ptr + 1);
429 ptr[0] = '\0';
430 nlen = ptr - name;
431 }
432 }
433 if (nlen > 12 && strcmp (name + nlen - 12, "-Changed-Why") == 0)
434 {
435 name[nlen - 12] = '\0';
436 ent->isReason = 1;
437 }
438 else
439 {
440 ent->isReason = 0;
441 }
442
443 if (strncasecmp (name, "raw:", 4) == 0)
444 {
445 ent->isRaw = 1;
446 name += 4;
447 }
448
449 if (strncasecmp (name, "oldpr:", 6) == 0)
450 {
451 ent->isOldPr = 1;
452 name += 6;
453 }
454
455 if (strncasecmp (name, "fieldtype:", 10) == 0)
456 {
457 ent->datum.fieldType = stringToFieldType (name + 10);
458 if (ent->datum.fieldType != InvalidFieldType)
459 {
460 ent->dtype = FieldTypeValue;
461 }
462 }
463 else if (strncasecmp (name, "builtinfield:", 13) == 0)
464 {
465 ent->dtype = FieldIndexValue;
466 ent->datum.index = findBuiltinField (ent->database, name + 13);
467 }
468 else
469 {
470 ent->datum.index = find_field_index (ent->database, name);
471 if (ent->datum.index == InvalidFieldIndex && ! ent->isReason)
472 {
473 ent->datum.headerIndex = find_header_index (name);
474 if (ent->datum.headerIndex != InvalidHeaderName)
475 {
476 ent->dtype = HeaderNameValue;
477 }
478 }
479 else
480 {
481 ent->dtype = FieldIndexValue;
482 }
483 }
484 free (ent->name);
485 ent->name = NULL;
486 }
487 if (ent->dtype == InvalidComplexFieldIndexDatatype)
488 {
489 return -1;
490 }
491 else
492 {
493 return 0;
494 }
495 }
496
497 const char *
get_field_value(PR * newPR,PR * oldPR,ComplexFieldIndex ent,FormatNamedParameter * params,int * mustBeFreed)498 get_field_value (PR *newPR, PR *oldPR, ComplexFieldIndex ent,
499 FormatNamedParameter *params, int *mustBeFreed)
500 {
501 PR *pr = newPR;
502
503 if (mustBeFreed != NULL)
504 {
505 *mustBeFreed = 0;
506 }
507
508 if (ent->isOldPr && oldPR != NULL)
509 {
510 pr = oldPR;
511 }
512
513 if (ent->dtype == StringValue)
514 {
515 return ent->name;
516 }
517
518 if (ent->name != NULL && ent->name[0] == '$')
519 {
520 return getNamedParameterValue (params, ent->name);
521 }
522
523 if (pr == NULL)
524 {
525 return "";
526 }
527
528 if (ent->name == NULL || parseComplexFieldIndex (ent) == 0)
529 {
530 const char *value;
531
532 if (ent->dtype == HeaderNameValue)
533 {
534 value = header_value (pr, ent->datum.headerIndex);
535 }
536 else if (ent->dtype == FieldTypeValue)
537 {
538 value = xstrdup ("invalid");
539 }
540 else if (ent->admSubfield != NULL)
541 {
542 value = getAdmSubfieldValue (pr, ent->datum.index, ent->admSubfield);
543 }
544 else if (ent->isReason)
545 {
546 value = field_change_reason (pr, ent->datum.index);
547 }
548 else if (ent->datum.index != InvalidFieldIndex)
549 {
550 if (ent->isRaw || (mustBeFreed == NULL)
551 || (fieldDefForIndex (ent->datum.index)->virtualFormat == NULL))
552 {
553 value = field_value (pr, ent->datum.index);
554 }
555 else
556 {
557 char *res = NULL;
558 FieldIndex indx = ent->datum.index;
559
560 process_format (NULL, &res, pr, oldPR,
561 fieldDefForIndex (indx)->virtualFormat,
562 "\n", params);
563 value = res;
564 *mustBeFreed = 1;
565 }
566 }
567 else
568 {
569 value = NULL;
570 }
571 return value;
572 }
573 else
574 {
575 return NULL;
576 }
577 }
578
579 int
isConstantFieldValue(ComplexFieldIndex ent)580 isConstantFieldValue (ComplexFieldIndex ent)
581 {
582 if (ent->dtype == StringValue)
583 {
584 return 1;
585 }
586 return 0;
587 }
588
589 FieldIndex
simpleFieldIndexValue(ComplexFieldIndex field)590 simpleFieldIndexValue (ComplexFieldIndex field)
591 {
592 if (field->name != NULL)
593 {
594 parseComplexFieldIndex (field);
595 }
596 if (field->dtype != FieldIndexValue)
597 {
598 return InvalidFieldIndex;
599 }
600 else
601 {
602 return field->datum.index;
603 }
604 }
605
606
607 /* Return the string name equivalent of FIELD. */
608 char *
complexFieldIndexToString(ComplexFieldIndex field)609 complexFieldIndexToString (ComplexFieldIndex field)
610 {
611 switch (field->dtype)
612 {
613 case FieldTypeValue:
614 {
615 const char *name = fieldTypeAsString (field->datum.fieldType);
616 char *res;
617 asprintf (&res, "fieldtype:%s", name);
618 return res;
619 }
620 break;
621 case StringValue:
622 {
623 char *res;
624
625 asprintf (&res, "\"%s\"", field->name);
626 return res;
627 }
628 break;
629 case HeaderNameValue:
630 {
631 return xstrdup (header_name (field->datum.headerIndex));
632 }
633
634 case FieldIndexValue:
635 {
636 char *res;
637
638 if (field->datum.index == InvalidFieldIndex)
639 {
640 res = xstrdup ("invalid");
641 }
642 else
643 {
644 const char *changedWhy = (field->isReason ? "-Changed-Why" : "");
645
646 if (field->admSubfield != NULL)
647 {
648 asprintf (&res, "%s%s[%s]",
649 fieldDefForIndex (field->datum.index)->name,
650 changedWhy,
651 field->admSubfield);
652 }
653 else
654 {
655 asprintf (&res, "%s%s",
656 fieldDefForIndex (field->datum.index)->name,
657 changedWhy);
658 }
659 }
660 return res;
661 }
662 break;
663
664 default:
665 {
666 if (field->name != NULL)
667 {
668 return xstrdup (field->name);
669 }
670 else
671 {
672 return xstrdup ("invalid");
673 }
674 }
675 break;
676 }
677 }
678
679 FieldIndex
find_field_index_with_len(const DatabaseInfo database,const char * name,size_t len)680 find_field_index_with_len (const DatabaseInfo database, const char *name,
681 size_t len)
682 {
683 int pr_fields = get_num_fields (database);
684 int start = 0;
685 int end = pr_fields - 1;
686 DatabaseFieldInfo dinfo = getDatabaseFieldInfo (database);
687
688 if (dinfo == NULL)
689 {
690 return InvalidFieldIndex;
691 }
692
693 while (start <= end)
694 {
695 int pos = (start + end) / 2;
696 int val = strncasecmp (dinfo->fieldList[dinfo->namePosition[pos]]->name,
697 name, len);
698 if (val == 0 && dinfo->fieldList[dinfo->namePosition[pos]]->name[len]
699 != '\0')
700 {
701 val = 1;
702 }
703 if (val == 0)
704 {
705 return getNthField (database, dinfo->namePosition[pos]);
706 }
707 else if (val < 0)
708 {
709 start = pos + 1;
710 }
711 else
712 {
713 end = pos - 1;
714 }
715 }
716 return InvalidFieldIndex;
717 }
718
719 FieldIndex
find_field_index(const DatabaseInfo database,const char * name)720 find_field_index (const DatabaseInfo database, const char *name)
721 {
722 return find_field_index_with_len (database, name, strlen (name));
723 }
724
725 FieldType
complexFieldType(ComplexFieldIndex field)726 complexFieldType (ComplexFieldIndex field)
727 {
728 if (field->dtype == FieldTypeValue)
729 {
730 return field->datum.fieldType;
731 }
732 else
733 {
734 return InvalidFieldType;
735 }
736 }
737
738 struct FieldTypeInfo {
739 const char *name;
740 FieldType type;
741 } fieldTypeInfo[] =
742 {
743 { "Text", Text },
744 { "MultiText", MultiText },
745 { "Enum", Enum },
746 { "MultiEnum", MultiEnum },
747 { "Integer", Integer },
748 { "Date", Date },
749 { "TextWithRegex", TextWithRegex } ,
750 { NULL, InvalidFieldType }
751 };
752
753 FieldType
stringToFieldType(const char * string)754 stringToFieldType (const char *string)
755 {
756 int x;
757
758 for (x = 0; fieldTypeInfo[x].name != NULL; x++)
759 {
760 if (strcasecmp (fieldTypeInfo[x].name, string) == 0)
761 {
762 return fieldTypeInfo[x].type;
763 }
764 }
765 return InvalidFieldType;
766 }
767
768 const char *
fieldTypeAsString(FieldType type)769 fieldTypeAsString (FieldType type)
770 {
771 int x;
772
773 for (x = 0; fieldTypeInfo[x].name != NULL; x++)
774 {
775 if (fieldTypeInfo[x].type == type)
776 {
777 return fieldTypeInfo[x].name;
778 }
779 }
780 return "InvalidFieldType";
781 }
782