Slightly improve `number_set_show'.
[ttfautohint.git] / lib / numberset.c
blob424e3a30dcc24b70f6fabe7e8de391b06d9d20c6
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 number_range*
26 number_set_new(int start,
27 int end,
28 int min,
29 int max)
31 number_range* nr;
34 if (start > end)
36 int tmp;
39 tmp = start;
40 start = end;
41 end = tmp;
44 if (start < min || end > max)
45 return NUMBERSET_INVALID_RANGE;
47 nr = (number_range*)malloc(sizeof (number_range));
48 if (!nr)
49 return NUMBERSET_ALLOCATION_ERROR;
51 nr->start = start;
52 nr->end = end;
53 nr->next = NULL;
55 return nr;
59 number_range*
60 number_set_prepend(number_range* list,
61 number_range* element)
63 if (!element)
64 return list;
66 if (element->start <= list->end)
68 if (element->end < list->start)
69 return NUMBERSET_NOT_ASCENDING;
70 else
71 return NUMBERSET_OVERLAPPING_RANGES;
74 if (list
75 && element->start == list->end + 1)
77 /* merge adjacent ranges */
78 list->end = element->end;
80 free(element);
82 return list;
85 element->next = list;
87 return element;
91 number_range*
92 number_set_reverse(number_range* list)
94 number_range* cur;
97 cur = list;
98 list = NULL;
100 while (cur)
102 number_range* tmp;
105 tmp = cur;
106 cur = cur->next;
107 tmp->next = list;
108 list = tmp;
111 return list;
115 const char*
116 number_set_parse(const char* s,
117 number_range** number_set,
118 int min,
119 int max)
121 number_range* cur = NULL;
122 number_range* new_range;
123 number_range* tmp;
125 const char* last_pos = s;
126 int last_start = -1;
127 int last_end = -1;
128 int t;
129 number_range* error_code = NULL;
132 if (!s)
133 return NULL;
135 if (min < 0)
136 min = 0;
137 if (max < 0)
138 max = INT_MAX;
139 if (min > max)
141 t = min;
142 min = max;
143 max = t;
146 for (;;)
148 int digit;
149 int n = -1;
150 int m = -1;
153 while (isspace(*s))
154 s++;
156 if (*s == ',')
158 s++;
159 continue;
161 else if (*s == '-')
163 last_pos = s;
164 n = min;
166 else if (isdigit(*s))
168 last_pos = s;
169 n = 0;
172 digit = *s - '0';
173 if (n > INT_MAX / 10
174 || (n == INT_MAX / 10 && digit > 5))
176 error_code = NUMBERSET_OVERFLOW;
177 break;
180 n = n * 10 + digit;
181 s++;
182 } while (isdigit(*s));
184 if (error_code)
185 break;
187 while (isspace(*s))
188 s++;
190 else if (*s == '\0')
191 break; /* end of data */
192 else
194 error_code = NUMBERSET_INVALID_CHARACTER;
195 break;
198 if (*s == '-')
200 s++;
202 while (isspace(*s))
203 s++;
205 if (isdigit(*s))
207 m = 0;
210 digit = *s - '0';
211 if (m > INT_MAX / 10
212 || (m == INT_MAX / 10 && digit > 5))
214 error_code = NUMBERSET_OVERFLOW;
215 break;
218 m = m * 10 + digit;
219 s++;
220 } while (isdigit(*s));
222 if (error_code)
223 break;
226 else
227 m = n;
229 if (m == -1)
230 m = max;
232 if (m < n)
234 t = n;
235 n = m;
236 m = t;
239 if (n < min || m > max)
241 error_code = NUMBERSET_INVALID_RANGE;
242 break;
245 if (last_end >= n)
247 if (last_start >= m)
248 error_code = NUMBERSET_NOT_ASCENDING;
249 else
250 error_code = NUMBERSET_OVERLAPPING_RANGES;
251 break;
254 if (cur
255 && last_end + 1 == n)
257 /* merge adjacent ranges */
258 cur->end = m;
260 else
262 if (number_set)
264 new_range = (number_range*)malloc(sizeof (number_range));
265 if (!new_range)
267 error_code = NUMBERSET_ALLOCATION_ERROR;
268 break;
271 /* prepend new range to list */
272 new_range->start = n;
273 new_range->end = m;
274 new_range->next = cur;
275 cur = new_range;
279 last_start = n;
280 last_end = m;
281 } /* end of loop */
283 if (error_code)
285 number_set_free(cur);
287 s = last_pos;
288 if (number_set)
289 *number_set = error_code;
291 else
293 /* success; now reverse list to have elements in ascending order */
294 number_range* list = NULL;
297 while (cur)
299 tmp = cur;
300 cur = cur->next;
301 tmp->next = list;
302 list = tmp;
305 if (number_set)
306 *number_set = list;
309 return s;
313 void
314 number_set_free(number_range* number_set)
316 number_range* nr = number_set;
319 while (nr)
321 number_range* tmp;
324 tmp = nr;
325 nr = nr->next;
326 free(tmp);
331 char*
332 number_set_show(number_range* number_set,
333 int min,
334 int max)
336 char* s;
337 char* s_new;
338 size_t s_len;
339 size_t s_len_new;
341 number_range* nr = number_set;
343 char tmp[256];
344 int tmp_len;
345 int t;
346 const char *comma;
349 if (min < 0)
350 min = 0;
351 if (max < 0)
352 max = INT_MAX;
353 if (min > max)
355 t = min;
356 min = max;
357 max = t;
360 /* we return an empty string for an empty number set */
361 /* (this is, number_set == NULL or unsuitable `min' and `max' values) */
362 s = (char*)malloc(1);
363 if (!s)
364 return NULL;
365 *s = '\0';
367 s_len = 1;
369 while (nr)
371 if (nr->start > max)
372 return s;
373 if (nr->end < min)
374 goto Again;
376 comma = (s_len == 1) ? "" : ", ";
378 if (nr->start == nr->end)
379 tmp_len = sprintf(tmp, "%s%i",
380 comma, nr->start);
381 else if (nr->start <= min
382 && nr->end >= max)
383 tmp_len = sprintf(tmp, "-");
384 else if (nr->start <= min)
385 tmp_len = sprintf(tmp, "-%i",
386 nr->end);
387 else if (nr->end >= max)
388 tmp_len = sprintf(tmp, "%s%i-",
389 comma, nr->start);
390 else
391 tmp_len = sprintf(tmp, "%s%i-%i",
392 comma, nr->start, nr->end);
394 s_len_new = s_len + tmp_len;
395 s_new = (char*)realloc(s, s_len_new);
396 if (!s_new)
398 free(s);
399 return NULL;
401 strcpy(s_new + s_len - 1, tmp);
402 s_len = s_len_new;
403 s = s_new;
405 Again:
406 nr = nr->next;
409 return s;
414 number_set_is_element(number_range* number_set,
415 int number)
417 number_range* nr = number_set;
420 while (nr)
422 if (number < nr->start)
423 return 0;
424 if (nr->start <= number
425 && number <= nr->end)
426 return 1;
427 nr = nr->next;
430 return 0;
435 number_set_get_first(number_set_iter* iter_p)
437 if (!iter_p || !iter_p->range)
438 return 0;
440 iter_p->val = iter_p->range->start;
442 return iter_p->val;
447 number_set_get_next(number_set_iter* iter_p)
449 if (!iter_p || !iter_p->range)
450 return 0;
452 iter_p->val++;
454 if (iter_p->val > iter_p->range->end)
456 iter_p->range = iter_p->range->next;
458 if (iter_p->range)
459 iter_p->val = iter_p->range->start;
462 return iter_p->val;
465 /* end of numberset.c */