perl -i -npe "s/[ \t]+$//g" `find src -name "*.[ch]" -print`
[freeradius.git] / src / lib / dict.c
1 /*
2  * dict.c       Routines to read the dictionary file.
3  *
4  * Version:     $Id$
5  *
6  *   This library is free software; you can redistribute it and/or
7  *   modify it under the terms of the GNU Lesser General Public
8  *   License as published by the Free Software Foundation; either
9  *   version 2.1 of the License, or (at your option) any later version.
10  *
11  *   This library is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  *   Lesser General Public License for more details.
15  *
16  *   You should have received a copy of the GNU Lesser General Public
17  *   License along with this library; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  */
22
23 static const char rcsid[] = "$Id$";
24
25 #include        "autoconf.h"
26
27 #include        <stdlib.h>
28 #include        <ctype.h>
29 #include        <string.h>
30
31 #ifdef HAVE_MALLOC_H
32 #include        <malloc.h>
33 #endif
34
35 #include        "libradius.h"
36 #include        "missing.h"
37
38 /*
39  *      There are very few vendors, and they're looked up only when we
40  *      read the dictionaries.  So it's OK to have a singly linked
41  *      list here.
42  */
43 static DICT_VENDOR      *dictionary_vendors = NULL;
44
45 static rbtree_t *attributes_byname = NULL;
46 static rbtree_t *attributes_byvalue = NULL;
47
48 static rbtree_t *values_byvalue = NULL;
49 static rbtree_t *values_byname = NULL;
50
51 /*
52  *      So VALUEs in the dictionary can have forward references.
53  */
54 static rbtree_t *values_fixup = NULL;
55
56 static const LRAD_NAME_NUMBER type_table[] = {
57         { "string",     PW_TYPE_STRING },
58         { "integer",    PW_TYPE_INTEGER },
59         { "ipaddr",     PW_TYPE_IPADDR },
60         { "date",       PW_TYPE_DATE },
61         { "abinary",    PW_TYPE_ABINARY },
62         { "octets",     PW_TYPE_OCTETS },
63         { "ifid",       PW_TYPE_IFID },
64         { "ipv6addr",   PW_TYPE_IPV6ADDR },
65         { "ipv6prefix", PW_TYPE_IPV6PREFIX },
66         { NULL, 0 }
67 };
68
69 /*
70  *      Quick pointers to the base 0..255 attributes.
71  *
72  *      These attributes are referenced a LOT, especially during
73  *      decoding of the on-the-wire packets.  It's useful to keep a
74  *      cache of their dictionary entries, so looking them up is
75  *      O(1), instead of O(log(N)).  (N==number of dictionary entries...)
76  */
77 static DICT_ATTR *base_attributes[256];
78
79 /*
80  *      Free the dictionary_attributes and dictionary_values lists.
81  */
82 static void dict_free(void)
83 {
84         DICT_VENDOR     *dvend, *enext;
85
86         memset(base_attributes, 0, sizeof(base_attributes));
87
88         for (dvend = dictionary_vendors; dvend; dvend = enext) {
89                 enext = dvend->next;
90                 free(dvend);
91         }
92
93         dictionary_vendors = NULL;
94
95         /*
96          *      Free the tree of attributes by name and value.
97          */
98         rbtree_free(attributes_byname);
99         rbtree_free(attributes_byvalue);
100         attributes_byname = NULL;
101         attributes_byvalue = NULL;
102
103         rbtree_free(values_byname);
104         rbtree_free(values_byvalue);
105         values_byname = NULL;
106         values_byvalue = NULL;
107 }
108
109 /*
110  *      Add vendor to the list.
111  */
112 int dict_addvendor(const char *name, int value)
113 {
114         DICT_VENDOR *vval;
115
116         if (value >= (1 << 16)) {
117                 librad_log("dict_addvendor: Cannot handle vendor ID larger than 65535");
118                 return -1;
119         }
120
121         if (strlen(name) > (sizeof(vval->name) -1)) {
122                 librad_log("dict_addvendor: vendor name too long");
123                 return -1;
124         }
125
126         if ((vval =(DICT_VENDOR *)malloc(sizeof(DICT_VENDOR))) == NULL) {
127                 librad_log("dict_addvendor: out of memory");
128                 return -1;
129         }
130         strcpy(vval->name, name);
131         vval->vendorpec  = value;
132
133         /* Insert at front. */
134         vval->next = dictionary_vendors;
135         dictionary_vendors = vval;
136
137         return 0;
138 }
139
140 /*
141  *      Add an attribute to the dictionary.
142  */
143 int dict_addattr(const char *name, int vendor, int type, int value,
144                  ATTR_FLAGS flags)
145 {
146         static int      max_attr = 0;
147         DICT_ATTR       *attr;
148
149         if (strlen(name) > (sizeof(attr->name) -1)) {
150                 librad_log("dict_addattr: attribute name too long");
151                 return -1;
152         }
153
154         /*
155          *      If the value is '-1', that means use a pre-existing
156          *      one (if it already exists).  If one does NOT already exist,
157          *      then create a new attribute, with a non-conflicting value,
158          *      and use that.
159          */
160         if (value == -1) {
161                 attr = dict_attrbyname(name);
162                 if (attr != NULL) {
163                         return 0; /* exists, don't add it again */
164                 }
165
166                 value = ++max_attr;
167
168         } else if (vendor == 0) {
169                 /*
170                  *  Update 'max_attr'
171                  */
172                 if (value > max_attr) {
173                         max_attr = value;
174                 }
175         }
176
177
178         /*
179          *      Create a new attribute for the list
180          */
181         if ((attr = (DICT_ATTR *)malloc(sizeof(DICT_ATTR))) == NULL) {
182                 librad_log("dict_addattr: out of memory");
183                 return -1;
184         }
185         strcpy(attr->name, name);
186         attr->attr = value;
187         attr->type = type;
188         attr->flags = flags;
189
190         if (vendor) {
191                 attr->attr |= (vendor << 16);
192         } else if ((attr->attr >= 0) && (attr->attr < 256)) {
193                 /*
194                  *      If it's an on-the-wire base attribute,
195                  *      then keep a quick reference to it, for speed.
196                  */
197                 base_attributes[attr->attr] = attr;
198         }
199
200         /*
201          *      Insert the attribute, only if it's not a duplicate.
202          */
203         if (rbtree_insert(attributes_byname, attr) == 0) {
204                 DICT_ATTR       *a;
205
206                 /*
207                  *      If the attribute has identical number, then
208                  *      ignore the duplicate.
209                  */
210                 a = rbtree_finddata(attributes_byname, attr);
211                 if (a->attr == attr->attr) {
212                         free(attr);
213                         return 0;
214                 }
215
216                 librad_log("dict_addattr: Duplicate attribute %s", name);
217                 return -1;
218         }
219
220         /*
221          *      Insert the SAME pointer (not free'd when this tree is
222          *      deleted), into another tree.
223          *
224          *      If the newly inserted entry is a duplicate of an existing
225          *      entry, then the old entry is tossed, and the new one
226          *      replaces it.  This behaviour is configured in the
227          *      rbtree_create() function.
228          *
229          *      We want this behaviour because we want OLD names for
230          *      the attributes to be read from the configuration
231          *      files, but when we're printing them, (and looking up
232          *      by value) we want to use the NEW name.
233          */
234         rbtree_insert(attributes_byvalue, attr);
235
236         return 0;
237 }
238
239 /*
240  *      Add a value for an attribute to the dictionary.
241  */
242 int dict_addvalue(const char *namestr, char *attrstr, int value)
243 {
244         DICT_ATTR       *dattr;
245         DICT_VALUE      *dval;
246
247         if (strlen(namestr) > (sizeof(dval->name) -1)) {
248                 librad_log("dict_addvalue: value name too long");
249                 return -1;
250         }
251
252         if ((dval = (DICT_VALUE *)malloc(sizeof(DICT_VALUE))) == NULL) {
253                 librad_log("dict_addvalue: out of memory");
254                 return -1;
255         }
256
257         strcpy(dval->name, namestr);
258         dval->value = value;
259
260         /*
261          *      Remember which attribute is associated with this
262          *      value, if possible.
263          */
264         dattr = dict_attrbyname(attrstr);
265         if (dattr) {
266                 dval->attr = dattr->attr;
267         } else {
268                 dval->attr = (int) strdup(attrstr);
269                 rbtree_insert(values_fixup, dval);
270                 return 0;
271         }
272
273         /*
274          *      Add the value into the dictionary.
275          */
276         if (rbtree_insert(values_byname, dval) == 0) {
277                 librad_log("dict_addvalue: Duplicate value name %s for attribute %s", namestr, attrstr);
278                 return -1;
279         }
280         rbtree_insert(values_byvalue, dval);
281
282         return 0;
283 }
284
285 /*
286  *      Process the ATTRIBUTE command
287  */
288 static int process_attribute(const char* fn, const int line,
289                              const int block_vendor, const char* data)
290 {
291         int             vendor;
292         char            namestr[256];
293         char            valstr[256];
294         char            typestr[256];
295         char            optstr[256];
296         int             value;
297         int             type;
298         char            *s, *c;
299         ATTR_FLAGS      flags;
300
301         vendor = 0;
302         optstr[0] = 0;
303         if(sscanf(data, "%s%s%s%s", namestr, valstr, typestr, optstr) < 3) {
304                 librad_log("dict_init: %s[%d]: invalid ATTRIBUTE line",
305                         fn, line);
306                 return -1;
307         }
308
309         /*
310          *      Validate all entries
311          */
312         if (!isdigit((int) *valstr)) {
313                 librad_log("dict_init: %s[%d]: invalid value", fn, line);
314                 return -1;
315         }
316         if (valstr[0] != '0')
317                 value = atoi(valstr);
318         else
319                 sscanf(valstr, "%i", &value);
320
321         /*
322          *      find the type of the attribute.
323          */
324         type = lrad_str2int(type_table, typestr, -1);
325         if (type < 0) {
326                 librad_log("dict_init: %s[%d]: invalid type \"%s\"",
327                         fn, line, typestr);
328                 return -1;
329         }
330
331         /*
332          *      Ignore comments
333          */
334         if (optstr[0] == '#') optstr[0] = '\0';
335
336         /*
337          *      Only look up the vendor if the string
338          *      is non-empty.
339          */
340
341         memset(&flags, 0, sizeof(flags));
342         s = strtok(optstr, ",");
343         while(s) {
344                 if (strcmp(s, "has_tag") == 0 ||
345                     strcmp(s, "has_tag=1") == 0) {
346                          /* Boolean flag, means this is a
347                             tagged attribute */
348                          flags.has_tag = 1;
349                 }
350                 else if (strncmp(s, "len+=", 5) == 0 ||
351                          strncmp(s, "len-=", 5) == 0) {
352                           /* Length difference, to accomodate
353                              braindead NASes & their vendors */
354                           flags.len_disp = strtol(s + 5, &c, 0);
355                           if (*c) {
356                                 librad_log("dict_init: %s[%d] invalid option %s",
357                                            fn, line, s);
358                                 return -1;
359                           }
360                           if (s[3] == '-') {
361                                 flags.len_disp = -flags.len_disp;
362                           }
363                 }
364                 else if (strncmp(s, "encrypt=", 8) == 0) {
365                           /* Encryption method, defaults to 0 (none).
366                              Currently valid is just type 2,
367                              Tunnel-Password style, which can only
368                              be applied to strings. */
369                           flags.encrypt = strtol(s + 8, &c, 0);
370                           if (*c) {
371                                 librad_log( "dict_init: %s[%d] invalid option %s",
372                                            fn, line, s);
373                                 return -1;
374                           }
375                 }
376                 else {
377                           /* Must be a vendor 'flag'... */
378                           if (strncmp(s, "vendor=", 5) == 0) {
379                                 /* New format */
380                                 s += 5;
381                           }
382
383                           vendor = dict_vendorbyname(s);
384                           if (!vendor) {
385                                 librad_log( "dict_init: %s[%d]: unknown vendor %s",
386                                            fn, line, optstr);
387                                 return -1;
388                           }
389                           if (block_vendor && optstr[0] &&
390                               (block_vendor != vendor)) {
391                                 librad_log("dict_init: %s[%d]: mismatched vendor %s within BEGIN-VENDOR/END-VENDOR block",
392                                            fn, line, optstr);
393                                 return -1;
394                           }
395                 }
396                 s = strtok(NULL, ",");
397         }
398
399         if (block_vendor) vendor = block_vendor;
400
401         if (dict_addattr(namestr, vendor, type, value, flags) < 0) {
402                 librad_log("dict_init: %s[%d]: %s",
403                            fn, line, librad_errstr);
404                 return -1;
405         }
406
407         return 0;
408 }
409
410
411 /*
412  *      Process the VALUE command
413  */
414 static int process_value(const char* fn, const int line, const char* data)
415 {
416         char    namestr[256];
417         char    valstr[256];
418         char    attrstr[256];
419         int     value;
420
421         if (sscanf(data, "%s%s%s", attrstr, namestr, valstr) != 3) {
422                 librad_log("dict_init: %s[%d]: invalid VALUE line",
423                         fn, line);
424                 return -1;
425         }
426         /*
427          *      For Compatibility, skip "Server-Config"
428          */
429         if (strcasecmp(attrstr, "Server-Config") == 0)
430                 return 0;
431
432         /*
433          *      Validate all entries
434          */
435         if (!isdigit((int) *valstr)) {
436                 librad_log("dict_init: %s[%d]: invalid value",
437                         fn, line);
438                 return -1;
439         }
440         if (valstr[0] != '0')
441                 value = atoi(valstr);
442         else
443                 sscanf(valstr, "%i", &value);
444
445         if (dict_addvalue(namestr, attrstr, value) < 0) {
446                 librad_log("dict_init: %s[%d]: %s",
447                            fn, line, librad_errstr);
448                 return -1;
449         }
450
451         return 0;
452 }
453
454
455 /*
456  *      Process the VENDOR command
457  */
458 static int process_vendor(const char* fn, const int line, const char* data)
459 {
460         char    valstr[256];
461         char    attrstr[256];
462         int     value;
463
464         if (sscanf(data, "%s%s", attrstr, valstr) != 2) {
465                 librad_log(
466                 "dict_init: %s[%d] invalid VENDOR entry",
467                         fn, line);
468                 return -1;
469         }
470
471         /*
472          *       Validate all entries
473          */
474         if (!isdigit((int) *valstr)) {
475                 librad_log("dict_init: %s[%d]: invalid value",
476                         fn, line);
477                 return -1;
478         }
479         value = atoi(valstr);
480
481         /* Create a new VENDOR entry for the list */
482         if (dict_addvendor(attrstr, value) < 0) {
483                 librad_log("dict_init: %s[%d]: %s",
484                            fn, line, librad_errstr);
485                 return -1;
486         }
487
488         return 0;
489 }
490
491
492 /*
493  *      Initialize the dictionary.
494  */
495 static int my_dict_init(const char *dir, const char *fn,
496                         const char *src_file, int src_line)
497 {
498         FILE    *fp;
499         char    dirtmp[256];
500         char    buf[256];
501         char    optstr[256];
502         char    *p;
503         char    *keyword;
504         char    *data;
505         int     line = 0;
506         int     vendor;
507         int     block_vendor;
508
509         if (strlen(fn) >= sizeof(dirtmp) / 2 ||
510             strlen(dir) >= sizeof(dirtmp) / 2) {
511                 librad_log("dict_init: filename name too long");
512                 return -1;
513         }
514
515         /*
516          *      First see if fn is relative to dir. If so, create
517          *      new filename. If not, remember the absolute dir.
518          */
519         if ((p = strrchr(fn, '/')) != NULL) {
520                 strcpy(dirtmp, fn);
521                 dirtmp[p - fn] = 0;
522                 dir = dirtmp;
523         } else if (dir && dir[0] && strcmp(dir, ".") != 0) {
524                 snprintf(dirtmp, sizeof(dirtmp), "%s/%s", dir, fn);
525                 fn = dirtmp;
526         }
527
528         if ((fp = fopen(fn, "r")) == NULL) {
529                 if (!src_file) {
530                         librad_log("dict_init: Couldn't open dictionary \"%s\": %s",
531                                    fn, strerror(errno));
532                 } else {
533                         librad_log("dict_init: %s[%d]: Couldn't open dictionary \"%s\": %s",
534                                    src_file, src_line, fn, strerror(errno));
535                 }
536                 return -1;
537         }
538
539         block_vendor = 0;
540
541         while (fgets(buf, sizeof(buf), fp) != NULL) {
542                 line++;
543                 if (buf[0] == '#' || buf[0] == 0 ||
544                     buf[0] == '\n' || buf[0] == '\r')
545                         continue;
546
547                 /*
548                  *  Comment characters should NOT be appearing anywhere but
549                  *  as start of a comment;
550                  */
551                 p = strchr(buf, '#');
552                 if (p) *p = '\0';
553
554                 keyword = strtok(buf, " \t\r\n");
555                 if (keyword == NULL) {
556                         continue;
557                 }
558
559                 data    = strtok(NULL, "\r\n");
560                 if (data == NULL || data[0] == 0) {
561                         librad_log("dict_init: %s[%d]: invalid entry for keyword %s",
562                                 fn, line, keyword);
563                         fclose(fp);
564                         return -1;
565                 }
566
567                 /*
568                  *      See if we need to import another dictionary.
569                  */
570                 if (strcasecmp(keyword, "$INCLUDE") == 0) {
571                         p = strtok(data, " \t");
572                         if (my_dict_init(dir, data, fn, line) < 0) {
573                                 fclose(fp);
574                                 return -1;
575                         }
576                         continue;
577                 } /* $INCLUDE */
578
579                 /*
580                  *      Perhaps this is an attribute.
581                  */
582                 if (strcasecmp(keyword, "ATTRIBUTE") == 0) {
583                         if (process_attribute(fn, line, block_vendor, data) == -1) {
584                                 fclose(fp);
585                                 return -1;
586                         }
587                         continue;
588                 }
589
590                 /*
591                  *      Process VALUE lines.
592                  */
593                 if (strcasecmp(keyword, "VALUE") == 0) {
594                         if (process_value(fn, line, data) == -1) {
595                                 fclose(fp);
596                                 return -1;
597                         }
598                         continue;
599                 }
600
601                 /*
602                  *      Process VENDOR lines.
603                  */
604                 if (strcasecmp(keyword, "VENDOR") == 0) {
605                         if (process_vendor(fn, line, data) == -1) {
606                                 fclose(fp);
607                                 return -1;
608                         }
609                         continue;
610                 }
611
612                 if (strcasecmp(keyword, "BEGIN-VENDOR") == 0) {
613                         optstr[0] = 0;
614                         if (sscanf(data, "%s", optstr) != 1) {
615                                 librad_log(
616                                 "dict_init: %s[%d] invalid BEGIN-VENDOR entry",
617                                         fn, line);
618                                 fclose(fp);
619                                 return -1;
620                         }
621
622                         vendor = dict_vendorbyname(optstr);
623                         if (!vendor) {
624                                 librad_log(
625                                         "dict_init: %s[%d]: unknown vendor %s",
626                                         fn, line, optstr);
627                                 fclose(fp);
628                                 return -1;
629                         }
630                         block_vendor = vendor;
631                         continue;
632                 } /* BEGIN-VENDOR */
633
634                 if (strcasecmp(keyword, "END-VENDOR") == 0) {
635                         optstr[0] = 0;
636                         if (sscanf(data, "%s", optstr) != 1) {
637                                 librad_log(
638                                 "dict_init: %s[%d] invalid END-VENDOR entry",
639                                         fn, line);
640                                 fclose(fp);
641                                 return -1;
642                         }
643
644                         vendor = dict_vendorbyname(optstr);
645                         if (!vendor) {
646                                 librad_log(
647                                         "dict_init: %s[%d]: unknown vendor %s",
648                                         fn, line, optstr);
649                                 fclose(fp);
650                                 return -1;
651                         }
652
653                         if (vendor != block_vendor) {
654                                 librad_log(
655                                         "dict_init: %s[%d]: END-VENDOR %s does not match any previous BEGIN-VENDOR",
656                                         fn, line, optstr);
657                                 fclose(fp);
658                                 return -1;
659                         }
660                         block_vendor = 0;
661                         continue;
662                 } /* END-VENDOR */
663
664                 /*
665                  *      Any other string: We don't recognize it.
666                  */
667                 librad_log(
668                         "dict_init: %s[%d] invalid keyword \"%s\"",
669                         fn, line, keyword);
670                 fclose(fp);
671                 return -1;
672         }
673         fclose(fp);
674         return 0;
675 }
676
677 /*
678  *      Callbacks for red-black trees.
679  */
680 static int attrname_cmp(const void *a, const void *b)
681 {
682         return strcasecmp(((const DICT_ATTR *)a)->name,
683                           ((const DICT_ATTR *)b)->name);
684 }
685
686 /*
687  *      Return: < 0  if a < b,
688  *              == 0 if a == b
689  */
690 static int attrvalue_cmp(const void *a, const void *b)
691 {
692         return (((const DICT_ATTR *)a)->attr -
693                 ((const DICT_ATTR *)b)->attr);
694 }
695
696 /*
697  *      Compare values by name, keying off of the attribute number,
698  *      and then the value name.
699  */
700 static int valuename_cmp(const void *a, const void *b)
701 {
702         int rcode;
703         rcode = (((const DICT_VALUE *)a)->attr -
704                  ((const DICT_VALUE *)b)->attr);
705         if (rcode != 0) return rcode;
706
707         return strcasecmp(((const DICT_VALUE *)a)->name,
708                           ((const DICT_VALUE *)b)->name);
709 }
710
711 /*
712  *      Compare values by value, keying off of the attribute number,
713  *      and then the value number.
714  */
715 static int valuevalue_cmp(const void *a, const void *b)
716 {
717         int rcode;
718         rcode = (((const DICT_VALUE *)a)->attr -
719                  ((const DICT_VALUE *)b)->attr);
720         if (rcode != 0) return rcode;
721
722         return (((const DICT_VALUE *)a)->value -
723                  ((const DICT_VALUE *)b)->value);
724 }
725
726 /*
727  *      Compare values by name, keying off of the value number,
728  *      and then the value number.
729  */
730 static int valuefixup_cmp(const void *a, const void *b)
731 {
732         int rcode;
733         rcode = strcasecmp((const char *) ((const DICT_VALUE *)a)->attr,
734                            (const char *) ((const DICT_VALUE *)b)->attr);
735         if (rcode != 0) return rcode;
736
737         return (((const DICT_VALUE *)a)->value -
738                 ((const DICT_VALUE *)b)->value);
739 }
740
741 static int values_fixup_func(void *data)
742 {
743         DICT_ATTR  *a;
744         DICT_VALUE *v;
745         DICT_VALUE *dval = data;
746
747         a = dict_attrbyname((const char *) dval->attr);
748         if (!a) {
749                 librad_log("dict_addvalue: No attribute named %s for value %s", (const char *) dval->attr, dval->name);
750                 return -1;
751         }
752
753         free ((const char *) dval->attr);
754         dval->attr = a->attr;
755
756         /*
757          *      Add the value into the dictionary.
758          */
759
760         if (rbtree_insert(values_byname, dval) == 0) {
761                 librad_log("dict_addvalue: Duplicate value name %s for attribute %s", dval->name, a->name);
762                 return -1;
763         }
764
765         /*
766          *      Allow them to use the old name, but prefer the new name
767          *      when printing values.
768          */
769         v = rbtree_find(values_byvalue, dval);
770         if (!v) {
771                 rbtree_insert(values_byvalue, dval);
772         }
773
774         return 0;
775 }
776
777 /*
778  *      Initialize the directory, then fix the attr member of
779  *      all attributes.
780  */
781 int dict_init(const char *dir, const char *fn)
782 {
783         dict_free();
784
785         /*
786          *      Create the tree of attributes by name.   There MAY NOT
787          *      be multiple attributes of the same name.
788          *
789          *      Each attribute is malloc'd, so the free function is free.
790          */
791         attributes_byname = rbtree_create(attrname_cmp, free, 0);
792         if (!attributes_byname) {
793                 return -1;
794         }
795
796         /*
797          *      Create the tree of attributes by value.  There MAY
798          *      be attributes of the same value.  If there are, we
799          *      pick the latest one.
800          */
801         attributes_byvalue = rbtree_create(attrvalue_cmp, NULL, 1);
802         if (!attributes_byvalue) {
803                 return -1;
804         }
805
806         values_byname = rbtree_create(valuename_cmp, NULL, 1);
807         if (!values_byname) {
808                 return -1;
809         }
810
811         values_byvalue = rbtree_create(valuevalue_cmp, NULL, 1);
812         if (!values_byvalue) {
813                 return -1;
814         }
815
816         /*
817          *      ONLY used in this function!
818          */
819         values_fixup = rbtree_create(valuefixup_cmp, NULL, 1);
820         if (!values_fixup) {
821                 return -1;
822         }
823
824         if (my_dict_init(dir, fn, NULL, 0) < 0)
825                 return -1;
826
827         /*
828          *      Fix up the dictionary, based on values with an attribute
829          *      of zero.
830          */
831         if (rbtree_walk(values_fixup, values_fixup_func, InOrder) != 0) {
832                 return -1;
833         }
834
835         rbtree_free(values_fixup);
836         values_fixup = NULL;
837
838         return 0;
839 }
840
841 /*
842  *      Get an attribute by its numerical value.
843  */
844 DICT_ATTR *dict_attrbyvalue(int val)
845 {
846         /*
847          *      If it's an on-the-wire base attribute, return
848          *      the cached value for it.
849          */
850         if ((val >= 0) && (val < 256)) {
851                 return base_attributes[val];
852
853         } else {
854                 DICT_ATTR myattr;
855
856                 myattr.attr = val;
857                 return rbtree_finddata(attributes_byvalue, &myattr);
858         }
859
860         return NULL;            /* never reached, but useful */
861 }
862
863 /*
864  *      Get an attribute by its name.
865  */
866 DICT_ATTR *dict_attrbyname(const char *name)
867 {
868         DICT_ATTR myattr;
869
870         strNcpy(myattr.name, name, sizeof(myattr.name));
871
872         return rbtree_finddata(attributes_byname, &myattr);
873 }
874
875 /*
876  *      Associate a value with an attribute and return it.
877  */
878 DICT_VALUE *dict_valbyattr(int attr, int val)
879 {
880         DICT_VALUE      myval;
881
882         myval.attr = attr;
883         myval.value = val;
884
885         return rbtree_finddata(values_byvalue, &myval);
886 }
887
888 /*
889  *      Get a value by its name.
890  *      If you pass an actual attr, it will try to match it.
891  *      If you just want it to return on the first match,
892  *      send it 0 as the attr. I hope this works the way it
893  *      seems to. :) --kph
894  */
895 DICT_VALUE *dict_valbyname(int attr, const char *name)
896 {
897         DICT_VALUE      myval;
898
899         myval.attr = attr;
900         strNcpy(myval.name, name, sizeof(myval.name));
901
902         return rbtree_finddata(values_byname, &myval);
903 }
904
905 /*
906  *      Get the vendor PEC based on the vendor name
907  */
908 int dict_vendorbyname(const char *name)
909 {
910         DICT_VENDOR *v;
911
912         /*
913          *      Find the vendor, if any.
914          */
915         for (v = dictionary_vendors; v; v = v->next) {
916                 if (strcasecmp(name, v->name) == 0) {
917                         return v->vendorpec;
918                 }
919         }
920
921         return 0;
922 }
923
924 /*
925  *      Return the vendor struct based on the PEC.
926  */
927 DICT_VENDOR *dict_vendorbyvalue(int vendor)
928 {
929         DICT_VENDOR *v;
930
931         /*
932          *      Find the vendor, if any.
933          */
934         for (v = dictionary_vendors; v; v = v->next) {
935                 if (vendor == v->vendorpec) {
936                         return v;
937                 }
938         }
939
940         return NULL;
941
942 }