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