Add functions to iterate over number sets.
[ttfautohint.git] / lib / numberset.c
blob786c2d261bbe03b08d799e0b1ae8c186b6d35a9f
1 /* numberset.c */
3 /*
4 * Copyright (C) 2012-2014 by Werner Lemberg.
6 * This file is part of the ttfautohint library, and may only be used,
7 * modified, and distributed under the terms given in `COPYING'. By
8 * continuing to use, modify, or distribute this file you indicate that you
9 * have read `COPYING' and understand and accept it fully.
11 * The file `COPYING' mentioned in the previous paragraph is distributed
12 * with the ttfautohint library.
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include <limits.h>
22 #include <numberset.h>
25 const char*
26 number_set_parse(const char* s,
27 number_range** number_set,
28 int min,
29 int max)
31 number_range* cur = NULL;
32 number_range* new_range;
33 number_range* tmp;
35 const char* last_pos = s;
36 int last_start = -1;
37 int last_end = -1;
38 int t;
39 number_range* error_code = NULL;
42 if (!s)
43 return NULL;
45 if (min < 0)
46 min = 0;
47 if (max < 0)
48 max = INT_MAX;
49 if (min > max)
51 t = min;
52 min = max;
53 max = t;
56 for (;;)
58 int digit;
59 int n = -1;
60 int m = -1;
63 while (isspace(*s))
64 s++;
66 if (*s == ',')
68 s++;
69 continue;
71 else if (*s == '-')
73 last_pos = s;
74 n = min;
76 else if (isdigit(*s))
78 last_pos = s;
79 n = 0;
82 digit = *s - '0';
83 if (n > INT_MAX / 10
84 || (n == INT_MAX / 10 && digit > 5))
86 error_code = NUMBERSET_OVERFLOW;
87 break;
90 n = n * 10 + digit;
91 s++;
92 } while (isdigit(*s));
94 if (error_code)
95 break;
97 while (isspace(*s))
98 s++;
100 else if (*s == '\0')
101 break; /* end of data */
102 else
104 error_code = NUMBERSET_INVALID_CHARACTER;
105 break;
108 if (*s == '-')
110 s++;
112 while (isspace(*s))
113 s++;
115 if (isdigit(*s))
117 m = 0;
120 digit = *s - '0';
121 if (m > INT_MAX / 10
122 || (m == INT_MAX / 10 && digit > 5))
124 error_code = NUMBERSET_OVERFLOW;
125 break;
128 m = m * 10 + digit;
129 s++;
130 } while (isdigit(*s));
132 if (error_code)
133 break;
136 else
137 m = n;
139 if (m == -1)
140 m = max;
142 if (m < n)
144 t = n;
145 n = m;
146 m = t;
149 if (n < min || m > max)
151 error_code = NUMBERSET_INVALID_RANGE;
152 break;
155 if (last_end >= n)
157 if (last_start >= m)
158 error_code = NUMBERSET_NOT_ASCENDING;
159 else
160 error_code = NUMBERSET_OVERLAPPING_RANGES;
161 break;
164 if (cur
165 && last_end + 1 == n)
167 /* merge adjacent ranges */
168 cur->end = m;
170 else
172 if (number_set)
174 new_range = (number_range*)malloc(sizeof (number_range));
175 if (!new_range)
177 error_code = NUMBERSET_ALLOCATION_ERROR;
178 break;
181 /* prepend new range to list */
182 new_range->start = n;
183 new_range->end = m;
184 new_range->next = cur;
185 cur = new_range;
189 last_start = n;
190 last_end = m;
191 } /* end of loop */
193 if (error_code)
195 number_set_free(cur);
197 s = last_pos;
198 if (number_set)
199 *number_set = error_code;
201 else
203 /* success; now reverse list to have elements in ascending order */
204 number_range* list = NULL;
207 while (cur)
209 tmp = cur;
210 cur = cur->next;
211 tmp->next = list;
212 list = tmp;
215 if (number_set)
216 *number_set = list;
219 return s;
223 void
224 number_set_free(number_range* number_set)
226 number_range* nr = number_set;
229 while (nr)
231 number_range* tmp;
234 tmp = nr;
235 nr = nr->next;
236 free(tmp);
241 char*
242 number_set_show(number_range* number_set,
243 int min,
244 int max)
246 char* s;
247 char* s_new;
248 size_t s_len;
249 size_t s_len_new;
251 number_range* nr = number_set;
253 char tmp[256];
254 int tmp_len;
255 int t;
256 const char *comma;
259 if (min < 0)
260 min = 0;
261 if (max < 0)
262 max = INT_MAX;
263 if (min > max)
265 t = min;
266 min = max;
267 max = t;
270 /* we return an empty string for an empty number set */
271 /* (this is, number_set == NULL or unsuitable `min' and `max' values) */
272 s = (char*)malloc(1);
273 if (!s)
274 return NULL;
275 *s = '\0';
277 s_len = 1;
279 while (nr)
281 if (nr->start > max)
282 return s;
283 if (nr->end < min)
284 goto Again;
286 comma = (s_len == 1) ? "" : ", ";
288 if (nr->start <= min
289 && nr->end >= max)
290 tmp_len = sprintf(tmp, "-");
291 else if (nr->start <= min)
292 tmp_len = sprintf(tmp, "-%i",
293 nr->end);
294 else if (nr->end >= max)
295 tmp_len = sprintf(tmp, "%s%i-",
296 comma, nr->start);
297 else
299 if (nr->start == nr->end)
300 tmp_len = sprintf(tmp, "%s%i",
301 comma, nr->start);
302 else
303 tmp_len = sprintf(tmp, "%s%i-%i",
304 comma, nr->start, nr->end);
307 s_len_new = s_len + tmp_len;
308 s_new = (char*)realloc(s, s_len_new);
309 if (!s_new)
311 free(s);
312 return NULL;
314 strcpy(s_new + s_len - 1, tmp);
315 s_len = s_len_new;
316 s = s_new;
318 Again:
319 nr = nr->next;
322 return s;
327 number_set_is_element(number_range* number_set,
328 int number)
330 number_range* nr = number_set;
333 while (nr)
335 if (number < nr->start)
336 return 0;
337 if (nr->start <= number
338 && number <= nr->end)
339 return 1;
340 nr = nr->next;
343 return 0;
348 number_set_get_first(number_set_iter* iter_p)
350 if (!iter_p || !iter_p->range)
351 return 0;
353 iter_p->val = iter_p->range->start;
355 return iter_p->val;
360 number_set_get_next(number_set_iter* iter_p)
362 if (!iter_p || !iter_p->range)
363 return 0;
365 iter_p->val++;
367 if (iter_p->val > iter_p->range->end)
369 iter_p->range = iter_p->range->next;
371 if (iter_p->range)
372 iter_p->val = iter_p->range->start;
375 return iter_p->val;
378 /* end of numberset.c */