From Harald Welte:
[obnox/wireshark/wip.git] / epan / range.c
1 /* range.c
2  * Range routines
3  *
4  * $Id$
5  *
6  * Dick Gooris <gooris@lucent.com>
7  * Ulf Lamping <ulf.lamping@web.de>
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <string.h>
33 #include <ctype.h>
34 #include <errno.h>
35
36 #include <glib.h>
37
38 #include <epan/frame_data.h>
39
40 #include <epan/range.h>
41 #include <epan/emem.h>
42 #include <stdio.h>
43
44 /*
45  * Size of the header of a range_t.
46  */
47 #define RANGE_HDR_SIZE (sizeof (range_t) - sizeof (range_admin_t))
48
49 /* Allocate an empty range. */
50 range_t *range_empty(void)
51 {
52    range_t *range;
53
54    range = g_malloc(RANGE_HDR_SIZE);
55    range->nranges = 0;
56    return range;
57 }
58
59 /******************** Range Entry Parser *********************************/
60
61 /* Converts a range string to a fast comparable array of ranges.
62  * The parameter 'es' points to the string to be converted.
63  * The parameter 'max_value' specifies the maximum value in a
64  * range.
65  *
66  * This function allocates a range_t large enough to hold the number
67  * of ranges specified, and fills the array range->ranges containing
68  * low and high values with the number of ranges being range->nranges.
69  * After having called this function, the function value_is_in_range()
70  * determines whether a given number is within the range or not.
71  *
72  * In case of a single number, we make a range where low is equal to high.
73  * We take care on wrongly entered ranges; opposite order will be taken
74  * care of.
75  *
76  * The following syntax is accepted :
77  *
78  *   1-20,30-40     Range from 1 to 20, and packets 30 to 40
79  *   -20,30         Range from 1 to 20, and packet 30
80  *   20,30,40-      20, 30, and the range from 40 to the end
81  *   20-10,30-25    Range from 10 to 20, and from 25 to 30
82  *   -              All values
83  */
84
85 convert_ret_t
86 range_convert_str(range_t **rangep, const gchar *es, guint32 max_value)
87 {
88    return range_convert_str_work(rangep, es, max_value, TRUE);
89 }
90
91 /*  This version of range_convert_str() allows the caller to specify whether
92  *  values in excess of the range's specified maximum should cause an error or
93  *  be silently lowered.
94  *  XXX - both the function and the variable could probably use better names.
95  */
96 convert_ret_t
97 range_convert_str_work(range_t **rangep, const gchar *es, guint32 max_value,
98                        gboolean err_on_max)
99 {
100
101    range_t       *range;
102    guint         nranges;
103    const gchar   *p;
104    char          *endp;
105    gchar         c;
106    guint         i;
107    guint32       tmp;
108    unsigned long val;
109
110    if ( (rangep == NULL) || (es == NULL) )
111        return CVT_SYNTAX_ERROR;
112    
113    /* Allocate a range; this has room for one subrange. */
114    range = g_malloc(RANGE_HDR_SIZE + sizeof (range_admin_t));
115    range->nranges = 0;
116    nranges = 1;
117
118    /* Process the ranges separately until we get a comma or end of string.
119     *
120     * We build a structure array called ranges of high and low values. After the
121     * following loop, we have the nranges variable which tells how many ranges
122     * were found. The number of individual ranges is limited to 'MaxRanges'
123     */
124
125    p = es;
126    for (;;) {
127       /* Skip white space. */
128       while ((c = *p) == ' ' || c == '\t')
129          p++;
130       if (c == '\0')
131          break;
132
133       /* This must be a subrange.  Make sure we have room for it. */
134       if (range->nranges >= nranges) {
135          /* Grow the structure.
136           * 4 is an arbitrarily chosen number.
137           * We start with 1, under the assumption that people
138           * will often give a single number or range, and then
139           * proceed to keep it a multiple of 4.
140           */
141          if (nranges == 1)
142             nranges = 4;
143          else
144             nranges += 4;
145          range = g_realloc(range, RANGE_HDR_SIZE +
146                            nranges*sizeof (range_admin_t));
147       }
148
149       if (c == '-') {
150          /* Subrange starts with 1. */
151          range->ranges[range->nranges].low = 1;
152       } else if (isdigit((unsigned char)c)) {
153          /* Subrange starts with the specified number */
154          errno = 0;
155          val = strtoul(p, &endp, 10);
156          if (p == endp) {
157             /* That wasn't a valid number. */
158             g_free(range);
159             return CVT_SYNTAX_ERROR;
160          }
161          if (errno == ERANGE || val > max_value) {
162             /* That was valid, but it's too big.  Return an error if requested
163              * (e.g., except when reading from the preferences file).
164              */
165             if (err_on_max) {
166                 g_free(range);
167                 return CVT_NUMBER_TOO_BIG;
168             } else {
169                 /* Silently use the range's maximum value */
170                 val = max_value;
171             }
172          }
173          p = endp;
174          range->ranges[range->nranges].low = val;
175
176          /* Skip white space. */
177          while ((c = *p) == ' ' || c == '\t')
178             p++;
179       } else {
180          /* Neither empty nor a number. */
181          g_free(range);
182          return CVT_SYNTAX_ERROR;
183       }
184
185       if (c == '-') {
186          /* There's a hyphen in the range.  Skip past it. */
187          p++;
188
189          /* Skip white space. */
190          while ((c = *p) == ' ' || c == '\t')
191             p++;
192
193          if (c == ',' || c == '\0') {
194            /* End of subrange string; that means the subrange ends
195             * with max_value.
196             */
197            range->ranges[range->nranges].high = max_value;
198          } else if (isdigit((unsigned char)c)) {
199             /* Subrange ends with the specified number. */
200             errno = 0;
201             val = strtoul(p, &endp, 10);
202             if (p == endp) {
203                /* That wasn't a valid number. */
204                g_free(range);
205                return CVT_SYNTAX_ERROR;
206             }
207             if (errno == ERANGE || val > max_value) {
208                 /* That was valid, but it's too big.  Return an error if requested
209                  * (e.g., except when reading from the preferences file).
210                  */
211                 if (err_on_max) {
212                     g_free(range);
213                     return CVT_NUMBER_TOO_BIG;
214                 } else {
215                     /* Silently use the range's maximum value */
216                     val = max_value;
217                 }
218             }
219             p = endp;
220             range->ranges[range->nranges].high = val;
221
222             /* Skip white space. */
223             while ((c = *p) == ' ' || c == '\t')
224                p++;
225          } else {
226             /* Neither empty nor a number. */
227             g_free(range);
228             return CVT_SYNTAX_ERROR;
229          }
230       } else if (c == ',' || c == '\0') {
231          /* End of subrange string; that means there's no hyphen
232           * in the subrange, so the start and the end are the same.
233           */
234          range->ranges[range->nranges].high = range->ranges[range->nranges].low;
235       } else {
236          /* Invalid character. */
237          g_free(range);
238          return CVT_SYNTAX_ERROR;
239       }
240       range->nranges++;
241
242       if (c == ',') {
243          /* Subrange is followed by a comma; skip it. */
244          p++;
245       }
246    }
247
248    /* Now we are going through the low and high values, and check
249     * whether they are in a proper order. Low should be equal or lower
250     * than high. So, go through the loop and swap if needed.
251     */
252    for (i=0; i < range->nranges; i++) {
253       if (range->ranges[i].low > range->ranges[i].high) {
254          tmp = range->ranges[i].low;
255          range->ranges[i].low  = range->ranges[i].high;
256          range->ranges[i].high = tmp;
257       }
258    }
259
260    /* In case we want to know what the result ranges are :
261     *
262     * for (i=0; i < range->nranges; i++) {
263     *  printf("Function : range_convert_str L=%u \t H=%u\n",range->ranges[i].low,range->ranges[i].high);
264     * }
265     *
266     */
267    *rangep = range;
268    return CVT_NO_ERROR;
269 } /* range_convert_str */
270
271 /* This function returns TRUE if a given value is within one of the ranges
272  * stored in the ranges array.
273  */
274 gboolean
275 value_is_in_range(range_t *range, guint32 val)
276 {
277    guint i;
278
279    if (range) {
280       for (i=0; i < range->nranges; i++) {
281          if (val >= range->ranges[i].low && val <= range->ranges[i].high)
282              return TRUE;
283       }
284    }
285    return(FALSE);
286 }
287
288 /* This function returns TRUE if the two given range_t's are equal.
289  */
290 gboolean
291 ranges_are_equal(range_t *a, range_t *b)
292 {
293    guint i;
294
295    if ( (a == NULL) || (b == NULL) )
296        return FALSE;
297    
298    if (a->nranges != b->nranges)
299       return FALSE;
300
301    for (i=0; i < a->nranges; i++) {
302       if (a->ranges[i].low != b->ranges[i].low)
303          return FALSE;
304
305       if (a->ranges[i].high != b->ranges[i].high)
306          return FALSE;
307    }
308
309    return TRUE;
310
311 }
312
313 /* This function calls the provided callback function for each value in
314  * in the range.
315  */
316 void
317 range_foreach(range_t *range, void (*callback)(guint32 val))
318 {
319    guint32 i, j;
320    
321    if (range && callback) {
322       for (i=0; i < range->nranges; i++) {
323          for (j = range->ranges[i].low; j <= range->ranges[i].high; j++)
324             callback(j);
325       }
326    }
327 }
328
329 /* This function converts a range_t to a (ep_alloc()-allocated) string.  */
330 char *
331 range_convert_range(range_t *range)
332 {
333    guint32 i;
334    gboolean prepend_comma = FALSE;
335    emem_strbuf_t *strbuf;
336
337    strbuf=ep_strbuf_new(NULL);
338
339    if (range) {
340       for (i=0; i < range->nranges; i++) {
341          if (range->ranges[i].low == range->ranges[i].high) {
342             ep_strbuf_append_printf(strbuf, "%s%u", prepend_comma?",":"", range->ranges[i].low);
343          } else {
344             ep_strbuf_append_printf(strbuf, "%s%u-%u", prepend_comma?",":"", range->ranges[i].low, range->ranges[i].high);
345          }
346          prepend_comma = TRUE;
347       }
348    }
349    return strbuf->str;
350 }
351
352 /* Create a copy of a range. */
353 range_t *
354 range_copy(range_t *src)
355 {
356    range_t *dst;
357    size_t range_size;
358
359    if (src == NULL)
360        return NULL;
361    
362    range_size = RANGE_HDR_SIZE + src->nranges*sizeof (range_admin_t);
363    dst = g_malloc(range_size);
364    memcpy(dst, src, range_size);
365    return dst;
366 }
367
368 #if 0
369 /* This is a debug function to check the range functionality */
370 static void
371 value_is_in_range_check(range_t *range, guint32 val)
372 {
373   /* Print the result for a given value */
374   printf("Function : value_is_in_range_check Number %u\t",val);
375
376   if (value_is_in_range(range, val)) {
377      printf("is in range\n");
378   } else {
379      printf("is not in range\n");
380   }
381 }
382 #endif
383