Include files used to build the server are now <freeradius-devel/*.h>
[freeradius.git] / src / lib / valuepair.c
1 /*
2  * valuepair.c  Functions to handle VALUE_PAIRs
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        <freeradius-devel/autoconf.h>
26
27 #include        <sys/types.h>
28
29 #include        <stdio.h>
30 #include        <stdlib.h>
31 #include        <string.h>
32 #include        <ctype.h>
33
34 #ifdef HAVE_MALLOC_H
35 #  include      <malloc.h>
36 #endif
37
38 #ifdef HAVE_REGEX_H
39 #  include      <regex.h>
40 #endif
41
42 #include        <freeradius-devel/missing.h>
43 #include        <freeradius-devel/libradius.h>
44
45 static const char *months[] = {
46         "jan", "feb", "mar", "apr", "may", "jun",
47         "jul", "aug", "sep", "oct", "nov", "dec" };
48
49
50 /*
51  *      Create a new valuepair.
52  */
53 VALUE_PAIR *paircreate(int attr, int type)
54 {
55         VALUE_PAIR      *vp;
56         DICT_ATTR       *da;
57
58         if ((vp = malloc(sizeof(VALUE_PAIR))) == NULL) {
59                 librad_log("out of memory");
60                 return NULL;
61         }
62         memset(vp, 0, sizeof(VALUE_PAIR));
63         vp->attribute = attr;
64         vp->operator = T_OP_EQ;
65         vp->type = type;
66
67         /*
68          *      Dictionary type over-rides what the caller says.
69          */
70         if ((da = dict_attrbyvalue(attr)) != NULL) {
71                 strcpy(vp->name, da->name);
72                 vp->type = da->type;
73                 vp->flags = da->flags;
74         } else if (VENDOR(attr) == 0) {
75                 sprintf(vp->name, "Attr-%u", attr);
76         } else {
77                 DICT_VENDOR *v;
78
79                 v = dict_vendorbyvalue(VENDOR(attr));
80                 if (v) {
81                         sprintf(vp->name, "%s-Attr-%u",
82                                 v->name, attr & 0xffff);
83                 } else {
84                         sprintf(vp->name, "Vendor-%u-Attr-%u",
85                                 VENDOR(attr), attr & 0xffff);
86                 }
87         }
88         switch (vp->type) {
89                 case PW_TYPE_INTEGER:
90                 case PW_TYPE_IPADDR:
91                 case PW_TYPE_DATE:
92                         vp->length = 4;
93                         break;
94
95                 case PW_TYPE_IFID:
96                         vp->length = sizeof(vp->vp_ifid);
97                         break;
98
99                 case PW_TYPE_IPV6ADDR:
100                         vp->length = sizeof(vp->vp_ipv6addr);
101                         break;
102
103                 case PW_TYPE_IPV6PREFIX:
104                         vp->length = sizeof(vp->vp_ipv6prefix);
105                         break;
106
107                 default:
108                         vp->length = 0;
109                         break;
110         }
111
112         return vp;
113 }
114
115 /*
116  *      release the memory used by a single attribute-value pair
117  *      just a wrapper around free() for now.
118  */
119 void pairbasicfree(VALUE_PAIR *pair)
120 {
121         /* clear the memory here */
122         memset(pair, 0, sizeof(*pair));
123         free(pair);
124 }
125
126 /*
127  *      Release the memory used by a list of attribute-value
128  *      pairs, and sets the pair pointer to NULL.
129  */
130 void pairfree(VALUE_PAIR **pair_ptr)
131 {
132         VALUE_PAIR      *next, *pair;
133
134         if (!pair_ptr) return;
135         pair = *pair_ptr;
136
137         while (pair != NULL) {
138                 next = pair->next;
139                 pairbasicfree(pair);
140                 pair = next;
141         }
142
143         *pair_ptr = NULL;
144 }
145
146
147 /*
148  *      Find the pair with the matching attribute
149  */
150 VALUE_PAIR * pairfind(VALUE_PAIR *first, int attr)
151 {
152         while(first && first->attribute != attr)
153                 first = first->next;
154         return first;
155 }
156
157
158 /*
159  *      Delete the pair(s) with the matching attribute
160  */
161 void pairdelete(VALUE_PAIR **first, int attr)
162 {
163         VALUE_PAIR *i, *next;
164         VALUE_PAIR **last = first;
165
166         for(i = *first; i; i = next) {
167                 next = i->next;
168                 if (i->attribute == attr) {
169                         *last = next;
170                         pairbasicfree(i);
171                 } else {
172                         last = &i->next;
173                 }
174         }
175 }
176
177 /*
178  *      Add a pair at the end of a VALUE_PAIR list.
179  */
180 void pairadd(VALUE_PAIR **first, VALUE_PAIR *add)
181 {
182         VALUE_PAIR *i;
183
184         if (!add) return;
185
186         if (*first == NULL) {
187                 *first = add;
188                 return;
189         }
190         for(i = *first; i->next; i = i->next)
191                 ;
192         i->next = add;
193 }
194
195 /*
196  *      Add or replace a pair at the end of a VALUE_PAIR list.
197  */
198 void pairreplace(VALUE_PAIR **first, VALUE_PAIR *replace)
199 {
200         VALUE_PAIR *i, *next;
201         VALUE_PAIR **prev = first;
202
203         if (*first == NULL) {
204                 *first = replace;
205                 return;
206         }
207
208         /*
209          *      Not an empty list, so find item if it is there, and
210          *      replace it. Note, we always replace the first one, and
211          *      we ignore any others that might exist.
212          */
213         for(i = *first; i; i = next) {
214                 next = i->next;
215
216                 /*
217                  *      Found the first attribute, replace it,
218                  *      and return.
219                  */
220                 if (i->attribute == replace->attribute) {
221                         *prev = replace;
222
223                         /*
224                          *      Should really assert that replace->next == NULL
225                          */
226                         replace->next = next;
227                         pairbasicfree(i);
228                         return;
229                 }
230
231                 /*
232                  *      Point to where the attribute should go.
233                  */
234                 prev = &i->next;
235         }
236
237         /*
238          *      If we got here, we didn't find anything to replace, so
239          *      stopped at the last item, which we just append to.
240          */
241         *prev = replace;
242 }
243
244 /*
245  *      Copy just a certain type of pairs.
246  */
247 VALUE_PAIR *paircopy2(VALUE_PAIR *vp, int attr)
248 {
249         VALUE_PAIR      *first, *n, **last;
250
251         first = NULL;
252         last = &first;
253
254         while (vp) {
255                 if (attr >= 0 && vp->attribute != attr) {
256                         vp = vp->next;
257                         continue;
258                 }
259                 if ((n = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
260                         librad_log("out of memory");
261                         return first;
262                 }
263                 memcpy(n, vp, sizeof(VALUE_PAIR));
264                 n->next = NULL;
265                 *last = n;
266                 last = &n->next;
267                 vp = vp->next;
268         }
269         return first;
270 }
271
272
273 /*
274  *      Copy a pairlist.
275  */
276 VALUE_PAIR *paircopy(VALUE_PAIR *vp)
277 {
278         return paircopy2(vp, -1);
279 }
280
281
282 /*
283  *      Move attributes from one list to the other
284  *      if not already present.
285  */
286 void pairmove(VALUE_PAIR **to, VALUE_PAIR **from)
287 {
288         VALUE_PAIR **tailto, *i, *j, *next;
289         VALUE_PAIR *tailfrom = NULL;
290         VALUE_PAIR *found;
291         int has_password = 0;
292
293         if (*to == NULL) {
294                 *to = *from;
295                 *from = NULL;
296                 return;
297         }
298
299         /*
300          *      First, see if there are any passwords here, and
301          *      point "tailto" to the end of the "to" list.
302          */
303         tailto = to;
304         for(i = *to; i; i = i->next) {
305                 if (i->attribute == PW_PASSWORD ||
306                     i->attribute == PW_CRYPT_PASSWORD)
307                         has_password = 1;
308                 tailto = &i->next;
309         }
310
311         /*
312          *      Loop over the "from" list.
313          */
314         for(i = *from; i; i = next) {
315                 next = i->next;
316                 /*
317                  *      If there was a password in the "to" list,
318                  *      do not move any other password from the
319                  *      "from" to the "to" list.
320                  */
321                 if (has_password &&
322                     (i->attribute == PW_PASSWORD ||
323                      i->attribute == PW_CRYPT_PASSWORD)) {
324                         tailfrom = i;
325                         continue;
326                 }
327                 /*
328                  *      If the attribute is already present in "to",
329                  *      do not move it from "from" to "to". We make
330                  *      an exception for "Hint" which can appear multiple
331                  *      times, and we never move "Fall-Through".
332                  */
333                 if (i->attribute == PW_FALL_THROUGH ||
334                     (i->attribute != PW_HINT && i->attribute != PW_FRAMED_ROUTE)) {
335
336                         found = pairfind(*to, i->attribute);
337                         switch (i->operator) {
338
339                           /*
340                            *    If matching attributes are found,
341                            *    delete them.
342                            */
343                         case T_OP_SUB:          /* -= */
344                                 if (found) {
345                                         if (!i->vp_strvalue[0] ||
346                                             (strcmp((char *)found->vp_strvalue,
347                                                     (char *)i->vp_strvalue) == 0)){
348                                                 pairdelete(to, found->attribute);
349
350                                                 /*
351                                                  *      'tailto' may have been
352                                                  *      deleted...
353                                                  */
354                                                 tailto = to;
355                                                 for(j = *to; j; j = j->next) {
356                                                         tailto = &j->next;
357                                                 }
358                                         }
359                                 }
360                                 tailfrom = i;
361                                 continue;
362                                 break;
363
364 /* really HAVE_REGEX_H */
365 #if 0
366                                 /*
367                                  *  Attr-Name =~ "s/find/replace/"
368                                  *
369                                  *  Very bad code.  Barely working,
370                                  *  if at all.
371                                  */
372
373                         case T_OP_REG_EQ:
374                           if (found &&
375                               (i->vp_strvalue[0] == 's')) {
376                             regex_t             reg;
377                             regmatch_t          match[1];
378
379                             char *str;
380                             char *p, *q;
381
382                             p = i->vp_strvalue + 1;
383                             q = strchr(p + 1, *p);
384                             if (!q || (q[strlen(q) - 1] != *p)) {
385                               tailfrom = i;
386                               continue;
387                             }
388                             str = strdup(i->vp_strvalue + 2);
389                             q = strchr(str, *p);
390                             *(q++) = '\0';
391                             q[strlen(q) - 1] = '\0';
392
393                             regcomp(&reg, str, 0);
394                             if (regexec(&reg, found->vp_strvalue,
395                                         1, match, 0) == 0) {
396                               fprintf(stderr, "\"%s\" will have %d to %d replaced with %s\n",
397                                       found->vp_strvalue, match[0].rm_so,
398                                       match[0].rm_eo, q);
399
400                             }
401                             regfree(&reg);
402                             free(str);
403                           }
404                           tailfrom = i; /* don't copy it over */
405                           continue;
406                           break;
407 #endif
408                         case T_OP_EQ:           /* = */
409                                 /*
410                                  *  FIXME: Tunnel attributes with
411                                  *  different tags are different
412                                  *  attributes.
413                                  */
414                                 if (found) {
415                                         tailfrom = i;
416                                         continue; /* with the loop */
417                                 }
418                                 break;
419
420                           /*
421                            *  If a similar attribute is found,
422                            *  replace it with the new one.  Otherwise,
423                            *  add the new one to the list.
424                            */
425                         case T_OP_SET:          /* := */
426                                 if (found) {
427                                         VALUE_PAIR *mynext = found->next;
428
429                                         /*
430                                          *      Do NOT call pairdelete()
431                                          *      here, due to issues with
432                                          *      re-writing "request->username".
433                                          *
434                                          *      Everybody calls pairmove,
435                                          *      and expects it to work.
436                                          *      We can't update request->username
437                                          *      here, so instead we over-write
438                                          *      the vp that it's pointing to.
439                                          */
440                                         memcpy(found, i, sizeof(*found));
441                                         found->next = mynext;
442
443                                         pairdelete(&found->next, found->attribute);
444
445                                         /*
446                                          *      'tailto' may have been
447                                          *      deleted...
448                                          */
449                                         for(j = found; j; j = j->next) {
450                                                 tailto = &j->next;
451                                         }
452                                         continue;
453                                 }
454                                 break;
455
456                           /*
457                            *  Add the new element to the list, even
458                            *  if similar ones already exist.
459                            */
460                         default:
461                         case T_OP_ADD: /* += */
462                                 break;
463                         }
464                 }
465                 if (tailfrom)
466                         tailfrom->next = next;
467                 else
468                         *from = next;
469
470                 /*
471                  *      If ALL of the 'to' attributes have been deleted,
472                  *      then ensure that the 'tail' is updated to point
473                  *      to the head.
474                  */
475                 if (!*to) {
476                         tailto = to;
477                 }
478                 *tailto = i;
479                 if (i) {
480                         i->next = NULL;
481                         tailto = &i->next;
482                 }
483         }
484 }
485
486 /*
487  *      Move one kind of attributes from one list to the other
488  */
489 void pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, int attr)
490 {
491         VALUE_PAIR *to_tail, *i, *next;
492         VALUE_PAIR *iprev = NULL;
493
494         /*
495          *      Find the last pair in the "to" list and put it in "to_tail".
496          */
497         if (*to != NULL) {
498                 to_tail = *to;
499                 for(i = *to; i; i = i->next)
500                         to_tail = i;
501         } else
502                 to_tail = NULL;
503
504         for(i = *from; i; i = next) {
505                 next = i->next;
506
507
508                 /*
509                  *      If the attribute to move is NOT a VSA, then it
510                  *      ignores any attributes which do not match exactly.
511                  */
512                 if ((attr != PW_VENDOR_SPECIFIC) &&
513                     (i->attribute != attr)) {
514                         iprev = i;
515                         continue;
516                 }
517
518                 /*
519                  *      If the attribute to move IS a VSA, then it ignores
520                  *      any non-VSA attribute.
521                  */
522                 if ((attr == PW_VENDOR_SPECIFIC) &&
523                     (VENDOR(i->attribute) == 0)) {
524                         iprev = i;
525                         continue;
526                 }
527
528                 /*
529                  *      Remove the attribute from the "from" list.
530                  */
531                 if (iprev)
532                         iprev->next = next;
533                 else
534                         *from = next;
535
536                 /*
537                  *      Add the attribute to the "to" list.
538                  */
539                 if (to_tail)
540                         to_tail->next = i;
541                 else
542                         *to = i;
543                 to_tail = i;
544                 i->next = NULL;
545         }
546 }
547
548
549 /*
550  *      Sort of strtok/strsep function.
551  */
552 static char *mystrtok(char **ptr, const char *sep)
553 {
554         char    *res;
555
556         if (**ptr == 0)
557                 return NULL;
558         while (**ptr && strchr(sep, **ptr))
559                 (*ptr)++;
560         if (**ptr == 0)
561                 return NULL;
562         res = *ptr;
563         while (**ptr && strchr(sep, **ptr) == NULL)
564                 (*ptr)++;
565         if (**ptr != 0)
566                 *(*ptr)++ = 0;
567         return res;
568 }
569
570 /*
571  *      Turn printable string into time_t
572  *      Returns -1 on error, 0 on OK.
573  */
574 static int gettime(const char *valstr, time_t *lvalue)
575 {
576         int             i;
577         time_t          t;
578         struct tm       *tm, s_tm;
579         char            buf[64];
580         char            *p;
581         char            *f[4];
582         char            *tail = '\0';
583
584         /*
585          * Test for unix timestamp date
586          */
587         *lvalue = strtoul(valstr, &tail, 10);
588         if (*tail == '\0') {
589                 return 0;
590         }
591
592         tm = &s_tm;
593         memset(tm, 0, sizeof(*tm));
594         tm->tm_isdst = -1;      /* don't know, and don't care about DST */
595
596         strNcpy(buf, valstr, sizeof(buf));
597
598         p = buf;
599         f[0] = mystrtok(&p, " \t");
600         f[1] = mystrtok(&p, " \t");
601         f[2] = mystrtok(&p, " \t");
602         f[3] = mystrtok(&p, " \t"); /* may, or may not, be present */
603         if (!f[0] || !f[1] || !f[2]) return -1;
604
605         /*
606          *      The time has a colon, where nothing else does.
607          *      So if we find it, bubble it to the back of the list.
608          */
609         if (f[3]) {
610                 for (i = 0; i < 3; i++) {
611                         if (strchr(f[i], ':')) {
612                                 p = f[3];
613                                 f[3] = f[i];
614                                 f[i] = p;
615                                 break;
616                         }
617                 }
618         }
619
620         /*
621          *  The month is text, which allows us to find it easily.
622          */
623         tm->tm_mon = 12;
624         for (i = 0; i < 3; i++) {
625                 if (isalpha( (int) *f[i])) {
626                         /*
627                          *  Bubble the month to the front of the list
628                          */
629                         p = f[0];
630                         f[0] = f[i];
631                         f[i] = p;
632
633                         for (i = 0; i < 12; i++) {
634                                 if (strncasecmp(months[i], f[0], 3) == 0) {
635                                         tm->tm_mon = i;
636                                         break;
637                                 }
638                         }
639                 }
640         }
641
642         /* month not found? */
643         if (tm->tm_mon == 12) return -1;
644
645         /*
646          *  The year may be in f[1], or in f[2]
647          */
648         tm->tm_year = atoi(f[1]);
649         tm->tm_mday = atoi(f[2]);
650
651         if (tm->tm_year >= 1900) {
652                 tm->tm_year -= 1900;
653
654         } else {
655                 /*
656                  *  We can't use 2-digit years any more, they make it
657                  *  impossible to tell what's the day, and what's the year.
658                  */
659                 if (tm->tm_mday < 1900) return -1;
660
661                 /*
662                  *  Swap the year and the day.
663                  */
664                 i = tm->tm_year;
665                 tm->tm_year = tm->tm_mday - 1900;
666                 tm->tm_mday = i;
667         }
668
669         /*
670          *  If the day is out of range, die.
671          */
672         if ((tm->tm_mday < 1) || (tm->tm_mday > 31)) {
673                 return -1;
674         }
675
676         /*
677          *      There may be %H:%M:%S.  Parse it in a hacky way.
678          */
679         if (f[3]) {
680                 f[0] = f[3];    /* HH */
681                 f[1] = strchr(f[0], ':'); /* find : separator */
682                 if (!f[1]) return -1;
683
684                 *(f[1]++) = '\0'; /* nuke it, and point to MM:SS */
685
686                 f[2] = strchr(f[1], ':'); /* find : separator */
687                 if (f[2]) {
688                   *(f[2]++) = '\0';     /* nuke it, and point to SS */
689                 } else {
690                   f[2] = "0";
691                 }
692
693                 tm->tm_hour = atoi(f[0]);
694                 tm->tm_min = atoi(f[1]);
695                 tm->tm_sec = atoi(f[2]);
696         }
697
698         /*
699          *  Returns -1 on error.
700          */
701         t = mktime(tm);
702         if (t == (time_t) -1) return -1;
703
704         *lvalue = t;
705
706         return 0;
707 }
708
709
710 /*
711  *  Parse a string value into a given VALUE_PAIR
712  *
713  *  FIXME: we probably want to fix this function to accept
714  *  octets as values for any type of attribute.  We should then
715  *  double-check the parsed value, to be sure it's legal for that
716  *  type (length, etc.)
717  */
718 VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value)
719 {
720         char            *p, *s=0;
721         const char      *cp, *cs;
722         DICT_VALUE      *dval;
723
724         /*
725          *      Even for integers, dates and ip addresses we
726          *      keep the original string in vp->vp_strvalue.
727          */
728         strNcpy((char *)vp->vp_strvalue, value, sizeof(vp->vp_strvalue));
729         vp->length = strlen(vp->vp_strvalue);
730
731         switch(vp->type) {
732                 case PW_TYPE_STRING:
733                         /*
734                          *      Already handled above.
735                          */
736                         break;
737
738                 case PW_TYPE_IPADDR:
739                         /*
740                          *      It's a comparison, not a real IP.
741                          */
742                         if ((vp->operator == T_OP_REG_EQ) ||
743                             (vp->operator == T_OP_REG_NE)) {
744                                 break;
745                         }
746
747                         /*
748                          *      FIXME: complain if hostname
749                          *      cannot be resolved, or resolve later!
750                          */
751                         if ((p = strrchr(value, '+')) != NULL && !p[1]) {
752                                 cs = s = strdup(value);
753                                 p = strrchr(s, '+');
754                                 *p = 0;
755                                 vp->flags.addport = 1;
756                         } else {
757                                 p = NULL;
758                                 cs = value;
759                         }
760                         
761                         {
762                                 lrad_ipaddr_t ipaddr;
763
764                                 if (ip_hton(cs, AF_INET, &ipaddr) < 0) {
765                                         librad_log("Failed to find IP address for %s", cs);
766                                         return NULL;
767                                 }
768
769                                 vp->lvalue = ipaddr.ipaddr.ip4addr.s_addr;
770                         }
771                         if (s) free(s);
772                         vp->length = 4;
773                         break;
774                 case PW_TYPE_INTEGER:
775                         /*
776                          *      If it starts with a digit, it must
777                          *      be a number (or a range).
778                          *
779                          *      Note that ALL integers are unsigned!
780                          */
781                         if (isdigit((int) *value)) {
782                                 vp->lvalue = (uint32_t) strtoul(value, NULL, 10);
783                                 vp->length = 4;
784                         }
785                         /*
786                          *      Look for the named value for the given
787                          *      attribute.
788                          */
789                         else if ((dval = dict_valbyname(vp->attribute, value)) == NULL) {
790                                 librad_log("Unknown value %s for attribute %s",
791                                            value, vp->name);
792                                 return NULL;
793                         } else {
794                                 vp->lvalue = dval->value;
795                                 vp->length = 4;
796                         }
797                         break;
798
799                 case PW_TYPE_DATE:
800                         if (gettime(value, (time_t *)&vp->lvalue) < 0) {
801                                 librad_log("failed to parse time string "
802                                            "\"%s\"", value);
803                                 return NULL;
804                         }
805                         vp->length = 4;
806                         break;
807                 case PW_TYPE_ABINARY:
808 #ifdef ASCEND_BINARY
809                         /*
810                          *      Special case to convert filter to binary
811                          */
812                         strNcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue));
813                         if (ascend_parse_filter(vp) < 0 ) {
814                                 librad_log("failed to parse Ascend binary attribute: %s",
815                                            librad_errstr);
816                                 return NULL;
817                         }
818                         break;
819                         /*
820                          *      If Ascend binary is NOT defined,
821                          *      then fall through to raw octets, so that
822                          *      the user can at least make them by hand...
823                          */
824 #endif
825                         /* raw octets: 0x01020304... */
826                 case PW_TYPE_OCTETS:
827                         if (strncasecmp(value, "0x", 2) == 0) {
828                                 u_char *us;
829                                 cp = value + 2;
830                                 us = vp->vp_strvalue;
831                                 vp->length = 0;
832
833
834                                 /*
835                                  *      There is only one character,
836                                  *      die.
837                                  */
838                                 if ((strlen(cp) & 0x01) != 0) {
839                                         librad_log("Hex string is not an even length string.");
840                                         return NULL;
841                                 }
842                                 
843                                 
844                                 while (*cp && vp->length < MAX_STRING_LEN) {
845                                         unsigned int tmp;
846
847                                         if (sscanf(cp, "%02x", &tmp) != 1) {
848                                                 librad_log("Non-hex characters at %c%c", cp[0], cp[1]);
849                                                 return NULL;
850                                         }
851
852                                         cp += 2;
853                                         *(us++) = tmp;
854                                         vp->length++;
855                                 }
856                                 *us = '\0';
857                         }
858                         break;
859
860                 case PW_TYPE_IFID:
861                         if (ifid_aton(value, vp->vp_strvalue) == NULL) {
862                                 librad_log("failed to parse interface-id "
863                                            "string \"%s\"", value);
864                                 return NULL;
865                         }
866                         vp->length = 8;
867                         vp->vp_strvalue[vp->length] = '\0';
868                         break;
869
870                 case PW_TYPE_IPV6ADDR:
871                         if (inet_pton(AF_INET6, value, vp->vp_strvalue) <= 0) {
872                                 librad_log("failed to parse IPv6 address "
873                                            "string \"%s\"", value);
874                                 return NULL;
875                         }
876                         vp->length = 16; /* length of IPv6 address */
877                         vp->vp_strvalue[vp->length] = '\0';
878                         break;
879                         /*
880                          *  Anything else.
881                          */
882                 case PW_TYPE_IPV6PREFIX:
883                         p = strchr(value, '/');
884                         if (!p || ((p - value) >= 256)) {
885                                 librad_log("invalid IPv6 prefix "
886                                            "string \"%s\"", value);
887                                 return NULL;
888                         } else {
889                                 unsigned int prefix;
890                                 char buffer[256], *eptr;
891                                 
892                                 memcpy(buffer, value, p - value);
893                                 buffer[p - value] = '\0';
894                                 
895                                 if (inet_pton(AF_INET6, buffer, vp->vp_strvalue + 2) <= 0) {
896                                         librad_log("failed to parse IPv6 address "
897                                                    "string \"%s\"", value);
898                                         return NULL;
899                                 }
900                                 
901                                 prefix = strtoul(p + 1, &eptr, 10);
902                                 if ((prefix > 128) || *eptr) {
903                                         librad_log("failed to parse IPv6 address "
904                                                    "string \"%s\"", value);
905                                         return NULL;
906                                 }
907                                 vp->vp_strvalue[1] = prefix;
908                         }
909                         vp->vp_strvalue[0] = '\0';
910                         vp->length = 16 + 2;
911                         break;
912
913                 default:
914                         librad_log("unknown attribute type %d", vp->type);
915                         return NULL;
916         }
917
918         return vp;
919 }
920
921 /*
922  *      Create a VALUE_PAIR from an ASCII attribute and value,
923  *      where the attribute name is in the form:
924  *
925  *      Attr-%d
926  *      Vendor-%d-Attr-%d
927  */
928 static VALUE_PAIR *pairmake_any(const char *attribute, const char *value,
929                                 int operator)
930 {
931         int             attr;
932         const char      *p;
933         VALUE_PAIR      *vp;
934         DICT_ATTR       *da;
935
936         /*
937          *      Unknown attributes MUST be of type 'octets'
938          */
939         if (value && (strncasecmp(value, "0x", 2) != 0)) {
940                 goto error;
941         }
942
943         /*
944          *      Attr-%d
945          */
946         if (strncasecmp(attribute, "Attr-", 5) == 0) {
947                 attr = atoi(attribute + 5);
948                 p = attribute + 5;
949                 p += strspn(p, "0123456789");
950                 if (*p != 0) goto error;
951
952
953                 /*
954                  *      Vendor-%d-Attr-%d
955                  */
956         } else if (strncasecmp(attribute, "Vendor-", 7) == 0) {
957                 int vendor;
958
959                 vendor = atoi(attribute + 7);
960                 if ((vendor == 0) || (vendor > 65535)) goto error;
961
962                 p = attribute + 7;
963                 p += strspn(p, "0123456789");
964
965                 /*
966                  *      Not Vendor-%d-Attr-%d
967                  */
968                 if (strncasecmp(p, "-Attr-", 6) != 0) goto error;
969
970                 p += 6;
971                 attr = atoi(p);
972
973                 p += strspn(p, "0123456789");
974                 if (*p != 0) goto error;
975
976                 if ((attr == 0) || (attr > 65535)) goto error;
977
978                 attr |= (vendor << 16);
979
980                 /*
981                  *      VendorName-Attr-%d
982                  */
983         } else if (((p = strchr(attribute, '-')) != NULL) &&
984                    (strncasecmp(p, "-Attr-", 6) == 0)) {
985                 int vendor;
986                 char buffer[256];
987
988                 if (((size_t) (p - attribute)) >= sizeof(buffer)) goto error;
989
990                 memcpy(buffer, attribute, p - attribute);
991                 buffer[p - attribute] = '\0';
992
993                 vendor = dict_vendorbyname(buffer);
994                 if (vendor == 0) goto error;
995
996                 p += 6;
997                 attr = atoi(p);
998
999                 p += strspn(p, "0123456789");
1000                 if (*p != 0) goto error;
1001
1002                 if ((attr == 0) || (attr > 65535)) goto error;
1003
1004                 attr |= (vendor << 16);
1005
1006         } else {                /* very much unknown: die */
1007         error:
1008                 librad_log("Unknown attribute \"%s\"", attribute);
1009                 return NULL;
1010         }
1011
1012         /*
1013          *      We've now parsed the attribute properly, and verified
1014          *      it to have value 'octets'.  Let's create it.
1015          */
1016         if ((vp = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
1017                 librad_log("out of memory");
1018                 return NULL;
1019         }
1020         memset(vp, 0, sizeof(VALUE_PAIR));
1021         vp->type = PW_TYPE_OCTETS;
1022
1023         /*
1024          *      It may not be valid hex characters.  If not, die.
1025          */
1026         if (pairparsevalue(vp, value) == NULL) {
1027                 pairfree(&vp);
1028                 return NULL;
1029         }
1030
1031         /*
1032          *      Dictionary type over-rides what the caller says.
1033          *      This "converts" the parsed value into the appropriate
1034          *      type.
1035          *
1036          *      Also, normalize the name of the attribute...
1037          *
1038          *      Much of this code is copied from paircreate()
1039          */
1040         if ((da = dict_attrbyvalue(attr)) != NULL) {
1041                 strcpy(vp->name, da->name);
1042                 vp->type = da->type;
1043                 vp->flags = da->flags;
1044
1045                 /*
1046                  *      Sanity check the type for length.  We don't
1047                  *      want to look at attributes which are of the
1048                  *      wrong length.
1049                  */
1050                 switch (vp->type) {
1051                 case PW_TYPE_DATE:
1052                 case PW_TYPE_INTEGER:
1053                 case PW_TYPE_IPADDR: /* always kept in network byte order */
1054                         if (vp->length != 4) {
1055                         length_error:
1056                                 pairfree(&vp);
1057                                 librad_log("Attribute has invalid length");
1058                                 return NULL;
1059                         }
1060                         memcpy(&vp->lvalue, vp->vp_strvalue, sizeof(vp->lvalue));
1061                         break;
1062
1063                 case PW_TYPE_IFID:
1064                         if (vp->length != 8) goto length_error;
1065                         break;
1066
1067                 case PW_TYPE_IPV6ADDR:
1068                         if (vp->length != 16) goto length_error;
1069                         break;
1070
1071 #ifdef ASCEND_BINARY
1072                 case PW_TYPE_ABINARY:
1073                         if (vp->length != 32) goto length_error;
1074                         break;
1075 #endif
1076                 default:        /* string, octets, etc. */
1077                         break;
1078                 }
1079
1080         } else if (VENDOR(attr) == 0) {
1081                 sprintf(vp->name, "Attr-%u", attr);
1082         } else {
1083                 sprintf(vp->name, "Vendor-%u-Attr-%u",
1084                         VENDOR(attr), attr & 0xffff);
1085         }
1086
1087         vp->attribute = attr;
1088         vp->operator = (operator == 0) ? T_OP_EQ : operator;
1089         vp->next = NULL;
1090
1091         return vp;
1092 }
1093
1094
1095 /*
1096  *      Create a VALUE_PAIR from an ASCII attribute and value.
1097  */
1098 VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
1099 {
1100         DICT_ATTR       *da;
1101         VALUE_PAIR      *vp;
1102         char            *tc, *ts;
1103         signed char     tag;
1104         int             found_tag;
1105 #ifdef HAVE_REGEX_H
1106         int             res;
1107         regex_t         cre;
1108 #endif
1109
1110         /*
1111          *    Check for tags in 'Attribute:Tag' format.
1112          */
1113         found_tag = 0;
1114         tag = 0;
1115
1116         ts = strrchr( attribute, ':' );
1117         if (ts && ts[1]) {
1118                  /* Colon found with something behind it */
1119                  if (ts[1] == '*' && ts[2] == 0) {
1120                          /* Wildcard tag for check items */
1121                          tag = TAG_ANY;
1122                          *ts = 0;
1123                  } else if ((ts[1] >= '0') && (ts[1] <= '9')) {
1124                          /* It's not a wild card tag */
1125                          tag = strtol(ts + 1, &tc, 0);
1126                          if (tc && !*tc && TAG_VALID_ZERO(tag))
1127                                  *ts = 0;
1128                          else tag = 0;
1129                  } else {
1130                          librad_log("Invalid tag for attribute %s", attribute);
1131                          return NULL;
1132                  }
1133                  found_tag = 1;
1134         }
1135
1136         /*
1137          *      It's not found in the dictionary, so we use
1138          *      another method to create the attribute.
1139          */
1140         if ((da = dict_attrbyname(attribute)) == NULL) {
1141                 return pairmake_any(attribute, value, operator);
1142         }
1143
1144         if ((vp = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
1145                 librad_log("out of memory");
1146                 return NULL;
1147         }
1148
1149         memset(vp, 0, sizeof(VALUE_PAIR));
1150         vp->attribute = da->attr;
1151         vp->type = da->type;
1152         vp->operator = (operator == 0) ? T_OP_EQ : operator;
1153         strcpy(vp->name, da->name);
1154         vp->flags = da->flags;
1155         vp->next = NULL;
1156
1157         /*      Check for a tag in the 'Merit' format of:
1158          *      :Tag:Value.  Print an error if we already found
1159          *      a tag in the Attribute.
1160          */
1161
1162         if (value && (*value == ':' && da->flags.has_tag)) {
1163                 /* If we already found a tag, this is invalid */
1164                 if(found_tag) {
1165                         pairbasicfree(vp);
1166                         librad_log("Duplicate tag %s for attribute %s",
1167                                    value, vp->name);
1168                         DEBUG("Duplicate tag %s for attribute %s\n",
1169                                    value, vp->name);
1170                         return NULL;
1171
1172                 }
1173                 /* Colon found and attribute allows a tag */
1174                 if (value[1] == '*' && value[2] == ':') {
1175                        /* Wildcard tag for check items */
1176                        tag = TAG_ANY;
1177                        value += 3;
1178                 } else {
1179                        /* Real tag */
1180                        tag = strtol(value + 1, &tc, 0);
1181                        if (tc && *tc==':' && TAG_VALID_ZERO(tag))
1182                             value = tc + 1;
1183                        else tag = 0;
1184                 }
1185                 found_tag = 1;
1186         }
1187
1188         if (found_tag) {
1189           vp->flags.tag = tag;
1190         }
1191
1192         switch (vp->operator) {
1193         default:
1194                 break;
1195
1196                 /*
1197                  *      For =* and !* operators, the value is irrelevant
1198                  *      so we return now.
1199                  */
1200         case T_OP_CMP_TRUE:
1201         case T_OP_CMP_FALSE:
1202                 vp->vp_strvalue[0] = '\0';
1203                 vp->length = 0;
1204                 return vp;
1205                 break;
1206
1207                 /*
1208                  *      Regular expression comparison of integer attributes
1209                  *      does a STRING comparison of the names of their
1210                  *      integer attributes.
1211                  */
1212         case T_OP_REG_EQ:       /* =~ */
1213         case T_OP_REG_NE:       /* !~ */
1214                 if (vp->type == PW_TYPE_INTEGER) {
1215                         return vp;
1216                 }
1217 #ifdef HAVE_REGEX_H
1218                 /*
1219                  *      Regular expression match with no regular
1220                  *      expression is wrong.
1221                  */
1222                 if (!value) {
1223                         pairfree(&vp);
1224                         return NULL;
1225                 }
1226
1227                 res = regcomp(&cre, value, REG_EXTENDED|REG_NOSUB);
1228                 if (res != 0) {
1229                         char    msg[128];
1230
1231                         regerror(res, &cre, msg, sizeof(msg));
1232                         librad_log("Illegal regular expression in attribute: %s: %s",
1233                                 vp->name, msg);
1234                         pairbasicfree(vp);
1235                         return NULL;
1236                 }
1237                 regfree(&cre);
1238 #else
1239                 librad_log("Regelar expressions not enabled in this build, error in attribute %s",
1240                                 vp->name);
1241                 pairbasicfree(vp);
1242                 return NULL;
1243 #endif
1244         }
1245
1246         /*
1247          *      FIXME: if (strcasecmp(attriobute, vp->name) != 0)
1248          *      then the user MAY have typed in the attribute name
1249          *      as Vendor-%d-Attr-%d, and the value MAY be octets.
1250          *
1251          *      We probably want to fix pairparsevalue to accept
1252          *      octets as values for any attribute.
1253          */
1254         if (value && (pairparsevalue(vp, value) == NULL)) {
1255                 pairbasicfree(vp);
1256                 return NULL;
1257         }
1258
1259         return vp;
1260 }
1261
1262 /*
1263  *      Read a valuepair from a buffer, and advance pointer.
1264  *      Sets *eol to T_EOL if end of line was encountered.
1265  */
1266 VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol)
1267 {
1268         char            buf[64];
1269         char            attr[64];
1270         char            value[512];
1271         char            *p;
1272         LRAD_TOKEN      token, t, xlat;
1273         VALUE_PAIR      *vp;
1274
1275         *eol = T_OP_INVALID;
1276
1277         /* Get attribute. */
1278         token = gettoken(ptr, attr, sizeof(attr));
1279
1280         /*  If it's a comment, then exit, as we haven't read a pair */
1281         if (token == T_HASH) {
1282                 *eol = token;
1283                 librad_log("Read a comment instead of a token");
1284                 return NULL;
1285         }
1286
1287         /*  It's not a comment, so it MUST be an attribute */
1288         if ((token == T_EOL) ||
1289             (attr[0] == 0)) {
1290                 librad_log("No token read where we expected an attribute name");
1291                 return NULL;
1292         }
1293
1294         /* Now we should have an '=' here. */
1295         token = gettoken(ptr, buf, sizeof(buf));
1296         if (token < T_EQSTART || token > T_EQEND) {
1297                 librad_log("expecting '='");
1298                 return NULL;
1299         }
1300
1301         /* Read value.  Note that empty string values are allowed */
1302         xlat = gettoken(ptr, value, sizeof(value));
1303         if (xlat == T_EOL) {
1304                 librad_log("failed to get value");
1305                 return NULL;
1306         }
1307
1308         /*
1309          *      Peek at the next token. Must be T_EOL, T_COMMA, or T_HASH
1310          */
1311         p = *ptr;
1312         t = gettoken(&p, buf, sizeof(buf));
1313         if (t != T_EOL && t != T_COMMA && t != T_HASH) {
1314                 librad_log("Expected end of line or comma");
1315                 return NULL;
1316         }
1317
1318         *eol = t;
1319         if (t == T_COMMA) {
1320                 *ptr = p;
1321         }
1322
1323         switch (xlat) {
1324                 /*
1325                  *      Make the full pair now.
1326                  */
1327         default:
1328                 vp = pairmake(attr, value, token);
1329                 break;
1330
1331                 /*
1332                  *      Perhaps do xlat's
1333                  */
1334         case T_DOUBLE_QUOTED_STRING:
1335                 p = strchr(value, '%');
1336                 if (p && (p[1] == '{')) {
1337                         if (strlen(value) >= sizeof(vp->vp_strvalue)) {
1338                                 librad_log("Value too long");
1339                                 return NULL;
1340                         }
1341                         vp = pairmake(attr, NULL, token);
1342                         if (!vp) {
1343                                 *eol = T_OP_INVALID;
1344                                 return NULL;
1345                         }
1346
1347                         strNcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue));
1348                         vp->flags.do_xlat = 1;
1349                         vp->length = 0;
1350                 } else {
1351                         vp = pairmake(attr, value, token);
1352                 }
1353                 break;
1354
1355
1356                 /*
1357                  *      Mark the pair to be allocated later.
1358                  */
1359         case T_BACK_QUOTED_STRING:
1360                 if (strlen(value) >= sizeof(vp->vp_strvalue)) {
1361                         librad_log("Value too long");
1362                         return NULL;
1363                 }
1364
1365                 vp = pairmake(attr, NULL, token);
1366                 if (!vp) {
1367                         *eol = T_OP_INVALID;
1368                         return NULL;
1369                 }
1370
1371                 vp->flags.do_xlat = 1;
1372                 strNcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue));
1373                 vp->length = 0;
1374                 break;
1375         }
1376
1377         return vp;
1378 }
1379
1380 /*
1381  *      Read one line of attribute/value pairs. This might contain
1382  *      multiple pairs seperated by comma's.
1383  */
1384 LRAD_TOKEN userparse(char *buffer, VALUE_PAIR **first_pair)
1385 {
1386         VALUE_PAIR      *vp;
1387         char            *p;
1388         LRAD_TOKEN      last_token = T_OP_INVALID;
1389         LRAD_TOKEN      previous_token;
1390
1391         /*
1392          *      We allow an empty line.
1393          */
1394         if (buffer[0] == 0)
1395                 return T_EOL;
1396
1397         p = buffer;
1398         do {
1399                 previous_token = last_token;
1400                 if ((vp = pairread(&p, &last_token)) == NULL) {
1401                         return last_token;
1402                 }
1403                 pairadd(first_pair, vp);
1404         } while (*p && (last_token == T_COMMA));
1405
1406         /*
1407          *      Don't tell the caller that there was a comment.
1408          */
1409         if (last_token == T_HASH) {
1410                 return previous_token;
1411         }
1412
1413         /*
1414          *      And return the last token which we read.
1415          */
1416         return last_token;
1417 }
1418
1419 /*
1420  *      Read valuepairs from the fp up to End-Of-File.
1421  *
1422  *      Hmm... this function is only used by radclient..
1423  */
1424 VALUE_PAIR *readvp2(FILE *fp, int *pfiledone, const char *errprefix)
1425 {
1426         char buf[8192];
1427         LRAD_TOKEN last_token = T_EOL;
1428         VALUE_PAIR *vp;
1429         VALUE_PAIR *list;
1430         int error = 0;
1431
1432         list = NULL;
1433
1434         while (!error && fgets(buf, sizeof(buf), fp) != NULL) {
1435                 /*
1436                  *      If we get a '\n' by itself, we assume that's
1437                  *      the end of that VP
1438                  */
1439                 if ((buf[0] == '\n') && (list)) {
1440                         return list;
1441                 }
1442                 if ((buf[0] == '\n') && (!list)) {
1443                         continue;
1444                 }
1445
1446                 /*
1447                  *      Comments get ignored
1448                  */
1449                 if (buf[0] == '#') continue;
1450
1451                 /*
1452                  *      Read all of the attributes on the current line.
1453                  */
1454                 vp = NULL;
1455                 last_token = userparse(buf, &vp);
1456                 if (!vp) {
1457                         if (last_token != T_EOL) {
1458                                 librad_perror("%s", errprefix);
1459                                 error = 1;
1460                                 break;
1461                         }
1462                         break;
1463                 }
1464
1465                 pairadd(&list, vp);
1466                 buf[0] = '\0';
1467         }
1468
1469         if (error) pairfree(&list);
1470
1471         *pfiledone = 1;
1472
1473         return error ? NULL: list;
1474 }
1475
1476
1477
1478 /*
1479  *      Compare two pairs, using the operator from "one".
1480  *
1481  *      i.e. given two attributes, it does:
1482  *
1483  *      (two->data) (one->operator) (one->data)
1484  *
1485  *      e.g. "foo" != "bar"
1486  *
1487  *      Returns true (comparison is true), or false (comparison is not true);
1488  */
1489 int paircmp(VALUE_PAIR *one, VALUE_PAIR *two)
1490 {
1491         int compare;
1492
1493         switch (one->operator) {
1494         case T_OP_CMP_TRUE:
1495                 return (two != NULL);
1496
1497         case T_OP_CMP_FALSE:
1498                 return (two == NULL);
1499
1500                 /*
1501                  *      One is a regex, compile it, print two to a string,
1502                  *      and then do string comparisons.
1503                  */
1504         case T_OP_REG_EQ:
1505         case T_OP_REG_NE:
1506 #ifndef HAVE_REGEX_H
1507                 return -1;
1508 #else
1509                 {
1510                         regex_t reg;
1511                         char buffer[MAX_STRING_LEN * 4 + 1];
1512                         
1513                         compare = regcomp(&reg, one->vp_strvalue,
1514                                           REG_EXTENDED);
1515                         if (compare != 0) {
1516                                 regerror(compare, &reg, buffer, sizeof(buffer));
1517                                 librad_log("Illegal regular expression in attribute: %s: %s",
1518                                            one->name, buffer);
1519                                 return -1;
1520                         }
1521
1522                         vp_prints_value(buffer, sizeof(buffer), two, 0);
1523
1524                         /*
1525                          *      Don't care about substring matches,
1526                          *      oh well...
1527                          */
1528                         compare = regexec(&reg, buffer, 0, NULL, 0);
1529
1530                         regfree(&reg);
1531                         if (one->operator == T_OP_REG_EQ) return (compare == 0);
1532                         return (compare != 0);
1533                 }
1534 #endif
1535
1536         default:                /* we're OK */
1537                 break;
1538         }
1539                 
1540         /*
1541          *      After doing the previous check for special comparisons,
1542          *      do the per-type comparison here.
1543          */
1544         switch (one->type) {
1545         case PW_TYPE_ABINARY:
1546         case PW_TYPE_OCTETS:
1547         {
1548                 size_t length;
1549                 const uint8_t *p, *q;
1550
1551                 if (one->length < two->length) {
1552                         length = one->length;
1553                 } else {
1554                         length = two->length;
1555                 }
1556                 
1557                 p = two->vp_octets;
1558                 q = one->vp_octets;
1559                 while (length) {
1560                         compare = ((int) *p) - ((int) *q);
1561                         if (compare != 0) goto type_switch;
1562                 }
1563
1564                 /*
1565                  *      Contents are the same.  The return code
1566                  *      is therefore the difference in lengths.
1567                  *
1568                  *      i.e. "0x00" is smaller than "0x0000"
1569                  */
1570                 compare = two->length - one->length;
1571         }
1572                 break;
1573
1574         case PW_TYPE_STRING:
1575                 if (one->flags.caseless) {
1576                         compare = strcasecmp(two->vp_strvalue,
1577                                              one->vp_strvalue);
1578                 } else {
1579                         compare = strcmp(two->vp_strvalue,
1580                                          one->vp_strvalue);
1581                 }
1582                 break;
1583                 
1584         case PW_TYPE_INTEGER:
1585         case PW_TYPE_DATE:
1586                 compare = two->lvalue - one->lvalue;
1587                 break;
1588
1589         case PW_TYPE_IPADDR:
1590                 compare = ntohl(two->vp_ipaddr) - ntohl(one->vp_ipaddr);
1591                 break;
1592
1593         case PW_TYPE_IPV6ADDR:
1594                 compare = memcmp(&two->vp_ipv6addr, &one->vp_ipv6addr,
1595                                  sizeof(two->vp_ipv6addr));
1596                 break;
1597                 
1598         case PW_TYPE_IPV6PREFIX:
1599                 compare = memcmp(&two->vp_ipv6prefix, &one->vp_ipv6prefix,
1600                                  sizeof(two->vp_ipv6prefix));
1601                 break;
1602
1603         case PW_TYPE_IFID:
1604                 compare = memcmp(&two->vp_ifid, &one->vp_ifid,
1605                                  sizeof(two->vp_ifid));
1606                 break;
1607
1608         default:
1609                 return 0;       /* unknown type */
1610         }
1611
1612         /*
1613          *      Now do the operator comparison.
1614          */
1615  type_switch:
1616         switch (one->operator) {
1617         case T_OP_CMP_EQ:
1618                 return (compare == 0);
1619                 
1620         case T_OP_NE:
1621                 return (compare != 0);
1622                 
1623         case T_OP_LT:
1624                 return (compare < 0);
1625                 
1626         case T_OP_GT:
1627                 return (compare > 0);
1628
1629         case T_OP_LE:
1630                 return (compare <= 0);
1631
1632         case T_OP_GE:
1633                 return (compare >= 0);
1634
1635         default:
1636                 return 0;
1637         }
1638
1639         return 0;
1640 }