Take UTF-8 spaces into account for empty strings
[smenu.git] / utils.c
blob010d16a31bb6ccd5b98a70c506a796316e2af7b3
1 /* ################################################################### */
2 /* Copyright 2015, Pierre Gentile (p.gen.progs@gmail.com) */
3 /* */
4 /* This Source Code Form is subject to the terms of the Mozilla Public */
5 /* License, v. 2.0. If a copy of the MPL was not distributed with this */
6 /* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 /* ################################################################### */
9 /* ******************************** */
10 /* Various small utility functions. */
11 /* ******************************** */
13 #include "config.h"
14 #include <stddef.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <limits.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include <stdarg.h>
21 #include <wctype.h>
22 #include "xmalloc.h"
23 #include "list.h"
24 #include "utf8.h"
25 #include "utils.h"
27 /* ******************* */
28 /* Interval functions. */
29 /* ******************* */
31 /* ======================= */
32 /* Creates a new interval. */
33 /* ======================= */
34 interval_t *
35 interval_new(void)
37 return xmalloc(sizeof(interval_t));
40 /* ======================================= */
41 /* Compares 2 intervals as integer couples */
42 /* same return values as for strcmp. */
43 /* ======================================= */
44 int
45 interval_comp(void const *a, void const *b)
47 interval_t const *ia = (interval_t *)a;
48 interval_t const *ib = (interval_t *)b;
50 if (ia->low < ib->low)
51 /* ia: [... */
52 /* ib: [... */
53 return -1;
54 if (ia->low > ib->low)
55 /* ia: [... */
56 /* ib: [... */
57 return 1;
58 if (ia->high < ib->high)
59 /* ia: ...] */
60 /* ib: ...] */
61 return -1;
62 if (ia->high > ib->high)
63 /* ia: ...] */
64 /* ib: ...] */
65 return 1;
67 return 0;
70 /* ================================== */
71 /* Swaps the values of two intervals. */
72 /* ================================== */
73 void
74 interval_swap(void **a, void **b)
76 interval_t *ia = (interval_t *)*a;
77 interval_t *ib = (interval_t *)*b;
78 long tmp;
80 tmp = ia->low;
81 ia->low = ib->low;
82 ib->low = tmp;
84 tmp = ia->high;
85 ia->high = ib->high;
86 ib->high = tmp;
89 /* ====================================================================== */
90 /* Merges the intervals from an interval list in order to get the minimum */
91 /* number of intervals to consider. */
92 /* ====================================================================== */
93 void
94 optimize_an_interval_list(ll_t *list)
96 ll_node_t *node1, *node2;
97 interval_t *data1, *data2;
99 if (!list || list->len < 2)
100 return;
102 /* Step 1: sort the intervals list. */
103 /* """""""""""""""""""""""""""""""" */
104 ll_sort(list, interval_comp, interval_swap);
106 /* Step 2: merge the list by merging the consecutive intervals. */
107 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
108 node1 = list->head;
109 node2 = node1->next;
111 while (node2)
113 data1 = (interval_t *)(node1->data);
114 data2 = (interval_t *)(node2->data);
116 if (data1->high >= data2->low - 1)
118 /* Interval 1 overlaps interval 2. */
119 /* ''''''''''''''''''''''''''''''' */
120 if (data2->high >= data1->high)
121 data1->high = data2->high;
122 ll_delete(list, node2);
123 free(data2);
124 node2 = node1->next;
126 else
128 /* No overlap. */
129 /* ''''''''''' */
130 node1 = node2;
131 node2 = node2->next;
136 /* ***************** */
137 /* String functions. */
138 /* ***************** */
140 /* ========================================================================= */
141 /* Allocates memory and safely concatenate strings. Stolen from a public */
142 /* domain implementation which can be found here: */
143 /* http://openwall.info/wiki/people/solar/software/public-domain-source-code */
144 /* ========================================================================= */
145 char *
146 concat(const char *s1, ...)
148 va_list args;
149 const char *s;
150 char *p, *result;
151 size_t l, m, n;
153 m = n = strlen(s1);
154 va_start(args, s1);
155 while ((s = va_arg(args, char *)))
157 l = strlen(s);
158 if ((m += l) < l)
159 break;
161 va_end(args);
162 if (s || m >= INT_MAX)
163 return NULL;
165 result = (char *)xmalloc(m + 1);
167 memcpy(p = result, s1, n);
168 p += n;
169 va_start(args, s1);
170 while ((s = va_arg(args, char *)))
172 l = strlen(s);
173 if ((n += l) < l || n > m)
174 break;
175 memcpy(p, s, l);
176 p += l;
178 va_end(args);
179 if (s || m != n || p != result + n)
181 free(result);
182 return NULL;
185 *p = 0;
186 return result;
189 /* =============================================== */
190 /* Is the string str2 a prefix of the string str1? */
191 /* Returns 1 if true, else 0. */
192 /* =============================================== */
194 strprefix(char *str1, char *str2)
196 while (*str1 != '\0' && *str1 == *str2)
198 str1++;
199 str2++;
202 return *str2 == '\0';
205 /* ========================= */
206 /* Trims leading characters. */
207 /* ========================= */
208 void
209 ltrim(char *str, const char *trim_str)
211 size_t len = strlen(str);
212 size_t begin = strspn(str, trim_str);
214 if (begin > 0)
215 for (size_t i = begin; i <= len; ++i)
216 str[i - begin] = str[i];
219 /* ==================================================================== */
220 /* Trims trailing characters. */
221 /* All (ASCII) characters in trim_str will be removed. */
222 /* The min argument guarantees that the length of the resulting string */
223 /* will not be smaller than this size if it was larger before, 0 is the */
224 /* usual value here. */
225 /* Note that when min is greater than 0, tail characters intended to be */
226 /* deleted may remain. */
227 /* ==================================================================== */
228 void
229 rtrim(char *str, const char *trim_str, size_t min)
231 size_t len = strlen(str);
232 while (len > min && strchr(trim_str, str[len - 1]))
233 str[--len] = '\0';
236 /* ========================================= */
237 /* Case insensitive strcmp. */
238 /* from http://c.snippets.org/code/stricmp.c */
239 /* ========================================= */
241 my_strcasecmp(const char *str1, const char *str2)
243 #ifdef HAVE_STRCASECMP
244 return strcasecmp(str1, str2);
245 #else
246 int retval = 0;
248 while (1)
250 retval = tolower(*str1++) - tolower(*str2++);
252 if (retval)
253 break;
255 if (*str1 && *str2)
256 continue;
257 else
258 break;
260 return retval;
261 #endif
264 /* ============================================= */
265 /* memmove based strcpy (tolerates overlapping). */
266 /* ============================================= */
267 char *
268 my_strcpy(char *str1, char *str2)
270 if (str1 == NULL || str2 == NULL)
271 return NULL;
273 memmove(str1, str2, strlen(str2) + 1);
275 return str1;
278 /* ================================ */
279 /* 7 bits aware version of isprint. */
280 /* ================================ */
282 isprint7(int i)
284 return i >= 0x20 && i <= 0x7e;
287 /* ================================ */
288 /* 8 bits aware version of isprint. */
289 /* ================================ */
291 isprint8(int i)
293 unsigned char c = i & (unsigned char)0xff;
295 return (c >= 0x20 && c < 0x7f) || (c >= (unsigned char)0xa0);
298 /* ==================================================== */
299 /* Private implementation of wcscasecmp missing in c99. */
300 /* ==================================================== */
302 my_wcscasecmp(const wchar_t *s1, const wchar_t *s2)
304 wchar_t c1, c2;
306 while (*s1)
308 c1 = towlower(*s1);
309 c2 = towlower(*s2);
311 if (c1 != c2)
312 return (int)(c1 - c2);
314 s1++;
315 s2++;
317 return -*s2;
320 /* ================================================================ */
321 /* Returns 1 if s can be converted into an int otherwise returns 0. */
322 /* ================================================================ */
324 is_integer(const char * const s)
326 long int n;
327 char *endptr;
329 n = strtol(s, &endptr, 10);
331 if (errno != ERANGE && n >= INT_MIN && n <= INT_MAX && *endptr == '\0')
332 return 1;
334 return 0;
337 /* ===================================================== */
338 /* Exchanges the start and end part of a string. */
339 /* The first part goes from char 0 to size-1. */
340 /* The second part goes from char size to the end of *s. */
341 /* Returns 1 on success. */
342 /* ===================================================== */
344 swap_string_parts(char **s, size_t first)
346 char *tmp;
347 size_t size;
349 if (*s == NULL || **s == '\0')
350 return 0;
352 tmp = xmalloc(strlen(*s) * 2 + 1);
353 size = strlen(*s);
355 if (first > size)
356 return 0;
358 strcpy(tmp, *s);
359 strcat(tmp, *s);
360 strncpy(*s, tmp + first, size);
362 free(tmp);
363 return 1;
366 /* ================================================================ */
367 /* Substitute all the characters c1 by c2 in the string s in place. */
368 /* ================================================================ */
369 void
370 strrep(char *s, const char c1, const char c2)
372 if (s != NULL)
373 while (*s)
375 if (*s == c1)
376 *s = c2;
377 s++;
381 /* ================================================================== */
382 /* Allocates and returns a string similar to s but with non printable */
383 /* character changed by their ASCII hexadecimal notation. */
384 /* ================================================================== */
385 char *
386 strprint(char const *s)
388 size_t l = strlen(s);
389 char *new = xcalloc(1, 4 * l + 1);
390 char *p = new;
392 while (*s)
394 if (isprint(*s))
395 *(p++) = *s++;
396 else
398 sprintf(p, "\\x%02X", (unsigned char)*s++);
399 p += 4;
403 if (p - new > (ptrdiff_t)l)
404 new = xrealloc(new, p - new + 1);
406 return new;
409 /* =============================================== */
410 /* Hexadecimal dump of part of a buffer to a file. */
411 /* */
412 /* buf : buffer to dump. */
413 /* fp : file to dump to. */
414 /* prefix: string to be printed before each line. */
415 /* size : length of the buffer to consider. */
416 /* =============================================== */
417 void
418 hexdump(const char *buf, FILE *fp, const char *prefix, size_t size)
420 unsigned int b;
421 unsigned char d[17];
422 unsigned int o, mo;
423 size_t l;
425 o = mo = 0;
426 l = strlen(prefix);
428 memset(d, '\0', 17);
429 for (b = 0; b < size; b++)
432 d[b % 16] = isprint(buf[b]) ? (unsigned char)buf[b] : '.';
434 if ((b % 16) == 0)
436 o = l + 7;
437 if (o > mo)
438 mo = o;
439 fprintf(fp, "%s: %04x:", prefix, b);
442 o += 3;
443 if (o > mo)
444 mo = o;
445 fprintf(fp, " %02x", (unsigned char)buf[b]);
447 if ((b % 16) == 15)
449 mo = o;
450 o = 0;
451 fprintf(fp, " |%s|", d);
452 memset(d, '\0', 17);
453 fprintf(fp, "\n");
456 if ((b % 16) != 0)
458 for (unsigned int i = 0; i < mo - o; i++)
459 fprintf(fp, "%c", ' ');
461 fprintf(fp, " |%s", d);
462 if (mo > o)
463 for (unsigned int i = 0; i < 16 - strlen((char *)d); i++)
464 fprintf(fp, "%c", ' ');
465 fprintf(fp, "%c", '|');
466 memset(d, '\0', 17);
467 fprintf(fp, "\n");