Simplify error handling thanks to the last change in the `sds' library.
[ttfautohint.git] / lib / numberset.c
blobae91c4a081460e4289475457be1f170ee95c69cd
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 <config.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include <limits.h>
24 #include <sds.h>
25 #include <numberset.h>
28 number_range*
29 number_set_new(int start,
30 int end,
31 int min,
32 int max)
34 number_range* nr;
37 if (start > end)
39 int tmp;
42 tmp = start;
43 start = end;
44 end = tmp;
47 if (start < min || end > max)
48 return NUMBERSET_INVALID_RANGE;
50 nr = (number_range*)malloc(sizeof (number_range));
51 if (!nr)
52 return NUMBERSET_ALLOCATION_ERROR;
54 nr->start = start;
55 nr->end = end;
56 nr->next = NULL;
58 return nr;
62 number_range*
63 number_set_prepend(number_range* list,
64 number_range* element)
66 if (!element)
67 return list;
69 if (element->start <= list->end)
71 if (element->end < list->start)
72 return NUMBERSET_NOT_ASCENDING;
73 else
74 return NUMBERSET_OVERLAPPING_RANGES;
77 if (list
78 && element->start == list->end + 1)
80 /* merge adjacent ranges */
81 list->end = element->end;
83 free(element);
85 return list;
88 element->next = list;
90 return element;
94 number_range*
95 number_set_reverse(number_range* list)
97 number_range* cur;
100 cur = list;
101 list = NULL;
103 while (cur)
105 number_range* tmp;
108 tmp = cur;
109 cur = cur->next;
110 tmp->next = list;
111 list = tmp;
114 return list;
118 const char*
119 number_set_parse(const char* s,
120 number_range** number_set,
121 int min,
122 int max)
124 number_range* cur = NULL;
125 number_range* new_range;
126 number_range* tmp;
128 const char* last_pos = s;
129 int last_start = -1;
130 int last_end = -1;
131 int t;
132 number_range* error_code = NULL;
135 if (!s)
136 return NULL;
138 if (min < 0)
139 min = 0;
140 if (max < 0)
141 max = INT_MAX;
142 if (min > max)
144 t = min;
145 min = max;
146 max = t;
149 for (;;)
151 int digit;
152 int n = -1;
153 int m = -1;
156 while (isspace(*s))
157 s++;
159 if (*s == ',')
161 s++;
162 continue;
164 else if (*s == '-')
166 last_pos = s;
167 n = min;
169 else if (isdigit(*s))
171 last_pos = s;
172 n = 0;
175 digit = *s - '0';
176 if (n > INT_MAX / 10
177 || (n == INT_MAX / 10 && digit > 5))
179 error_code = NUMBERSET_OVERFLOW;
180 break;
183 n = n * 10 + digit;
184 s++;
185 } while (isdigit(*s));
187 if (error_code)
188 break;
190 while (isspace(*s))
191 s++;
193 else if (*s == '\0')
194 break; /* end of data */
195 else
197 error_code = NUMBERSET_INVALID_CHARACTER;
198 break;
201 if (*s == '-')
203 s++;
205 while (isspace(*s))
206 s++;
208 if (isdigit(*s))
210 m = 0;
213 digit = *s - '0';
214 if (m > INT_MAX / 10
215 || (m == INT_MAX / 10 && digit > 5))
217 error_code = NUMBERSET_OVERFLOW;
218 break;
221 m = m * 10 + digit;
222 s++;
223 } while (isdigit(*s));
225 if (error_code)
226 break;
229 else
230 m = n;
232 if (m == -1)
233 m = max;
235 if (m < n)
237 t = n;
238 n = m;
239 m = t;
242 if (n < min || m > max)
244 error_code = NUMBERSET_INVALID_RANGE;
245 break;
248 if (last_end >= n)
250 if (last_start >= m)
251 error_code = NUMBERSET_NOT_ASCENDING;
252 else
253 error_code = NUMBERSET_OVERLAPPING_RANGES;
254 break;
257 if (cur
258 && last_end + 1 == n)
260 /* merge adjacent ranges */
261 cur->end = m;
263 else
265 if (number_set)
267 new_range = (number_range*)malloc(sizeof (number_range));
268 if (!new_range)
270 error_code = NUMBERSET_ALLOCATION_ERROR;
271 break;
274 /* prepend new range to list */
275 new_range->start = n;
276 new_range->end = m;
277 new_range->next = cur;
278 cur = new_range;
282 last_start = n;
283 last_end = m;
284 } /* end of loop */
286 if (error_code)
288 number_set_free(cur);
290 s = last_pos;
291 if (number_set)
292 *number_set = error_code;
294 else
296 /* success; now reverse list to have elements in ascending order */
297 number_range* list = NULL;
300 while (cur)
302 tmp = cur;
303 cur = cur->next;
304 tmp->next = list;
305 list = tmp;
308 if (number_set)
309 *number_set = list;
312 return s;
316 void
317 number_set_free(number_range* number_set)
319 number_range* nr = number_set;
322 while (nr)
324 number_range* tmp;
327 tmp = nr;
328 nr = nr->next;
329 free(tmp);
334 char*
335 number_set_show(number_range* number_set,
336 int min,
337 int max)
339 sds s;
340 size_t len;
341 char* res;
343 number_range* nr = number_set;
345 const char *comma;
348 if (min < 0)
349 min = 0;
350 if (max < 0)
351 max = INT_MAX;
352 if (min > max)
354 int t;
357 t = min;
358 min = max;
359 max = t;
362 s = sdsempty();
364 while (nr)
366 if (nr->start > max)
367 goto Exit;
368 if (nr->end < min)
369 goto Again;
371 comma = *s ? ", " : "";
373 if (nr->start == nr->end)
374 s = sdscatprintf(s, "%s%i", comma, nr->start);
375 else if (nr->start <= min
376 && nr->end >= max)
377 s = sdscatprintf(s, "-");
378 else if (nr->start <= min)
379 s = sdscatprintf(s, "-%i", nr->end);
380 else if (nr->end >= max)
381 s = sdscatprintf(s, "%s%i-", comma, nr->start);
382 else
383 s = sdscatprintf(s, "%s%i-%i", comma, nr->start, nr->end);
385 Again:
386 nr = nr->next;
389 Exit:
390 if (!s)
391 return NULL;
393 /* we return an empty string for an empty number set */
394 /* (this is, number_set == NULL or unsuitable `min' and `max' values) */
395 len = sdslen(s) + 1;
396 res = (char*)malloc(len);
397 if (res)
398 memcpy(res, s, len);
400 sdsfree(s);
402 return res;
407 number_set_is_element(number_range* number_set,
408 int number)
410 number_range* nr = number_set;
413 while (nr)
415 if (number < nr->start)
416 return 0;
417 if (nr->start <= number
418 && number <= nr->end)
419 return 1;
420 nr = nr->next;
423 return 0;
428 number_set_get_first(number_set_iter* iter_p)
430 if (!iter_p || !iter_p->range)
431 return 0;
433 iter_p->val = iter_p->range->start;
435 return iter_p->val;
440 number_set_get_next(number_set_iter* iter_p)
442 if (!iter_p || !iter_p->range)
443 return 0;
445 iter_p->val++;
447 if (iter_p->val > iter_p->range->end)
449 iter_p->range = iter_p->range->next;
451 if (iter_p->range)
452 iter_p->val = iter_p->range->start;
455 return iter_p->val;
458 /* end of numberset.c */