]> git.meshlink.io Git - catta/blob - avahi-core/util.c
* utf-8 collation of domain names
[catta] / avahi-core / util.c
1 /* $Id$ */
2
3 /***
4   This file is part of avahi.
5  
6   avahi is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10  
11   avahi is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14   Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with avahi; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <limits.h>
31 #include <stdio.h>
32
33 #include "util.h"
34
35 gchar *avahi_get_host_name(void) {
36 #ifdef HOST_NAME_MAX
37     char t[HOST_NAME_MAX];
38 #else
39     char t[256];
40 #endif
41     gethostname(t, sizeof(t));
42     t[sizeof(t)-1] = 0;
43     return avahi_normalize_name(t);
44 }
45
46 static gchar *unescape_uneeded(const gchar *src, gchar *ret_dest, size_t size) {
47     gboolean escaped = FALSE;
48     
49     g_assert(src);
50     g_assert(ret_dest);
51     g_assert(size > 0);
52     
53     for (; *src; src++) {
54
55         if (!escaped && *src == '\\')
56             escaped = TRUE;
57         else if (escaped && (*src == '.' || *src == '\\')) {
58
59             if ((size -= 2) <= 1) break;
60             
61             *(ret_dest++) = '\\';
62             *(ret_dest++) = *src;
63
64             escaped = FALSE;
65         } else {
66             if (--size <= 1) break;
67
68             *(ret_dest++) = *src;
69             escaped = FALSE;
70         }
71
72     }
73
74     *ret_dest = 0;
75     
76     return ret_dest;
77 }
78
79 gchar *avahi_normalize_name(const gchar *s) {
80     gchar tmp[256];
81     gchar *n, *t;
82     guint l;
83     g_assert(s);
84
85     unescape_uneeded(s, tmp, sizeof(tmp));
86
87     n = g_utf8_normalize(tmp, -1, G_NORMALIZE_DEFAULT);
88
89     if ((l = strlen(n)) == 0) {
90         g_free(n);
91         return g_strdup(".");
92     }
93
94     if (n[l-1] == '.')
95         return n;
96
97     t = g_strdup_printf("%s.", n);
98     g_free(n);
99     return t;
100 }
101
102 gint avahi_timeval_compare(const GTimeVal *a, const GTimeVal *b) {
103     g_assert(a);
104     g_assert(b);
105
106     if (a->tv_sec < b->tv_sec)
107         return -1;
108
109     if (a->tv_sec > b->tv_sec)
110         return 1;
111
112     if (a->tv_usec < b->tv_usec)
113         return -1;
114
115     if (a->tv_usec > b->tv_usec)
116         return 1;
117
118     return 0;
119 }
120
121 glong avahi_timeval_diff(const GTimeVal *a, const GTimeVal *b) {
122     g_assert(a);
123     g_assert(b);
124
125     if (avahi_timeval_compare(a, b) < 0)
126         return avahi_timeval_diff(b, a);
127
128     return ((glong) a->tv_sec - b->tv_sec)*1000000 + a->tv_usec - b->tv_usec;
129 }
130
131
132 gint avahi_set_cloexec(gint fd) {
133     gint n;
134
135     g_assert(fd >= 0);
136     
137     if ((n = fcntl(fd, F_GETFD)) < 0)
138         return -1;
139
140     if (n & FD_CLOEXEC)
141         return 0;
142
143     return fcntl(fd, F_SETFD, n|FD_CLOEXEC);
144 }
145
146 gint avahi_set_nonblock(gint fd) {
147     gint n;
148
149     g_assert(fd >= 0);
150
151     if ((n = fcntl(fd, F_GETFL)) < 0)
152         return -1;
153
154     if (n & O_NONBLOCK)
155         return 0;
156
157     return fcntl(fd, F_SETFL, n|O_NONBLOCK);
158 }
159
160 gint avahi_wait_for_write(gint fd) {
161     fd_set fds;
162     gint r;
163     
164     FD_ZERO(&fds);
165     FD_SET(fd, &fds);
166     
167     if ((r = select(fd+1, NULL, &fds, NULL, NULL)) < 0) {
168         g_message("select() failed: %s", strerror(errno));
169
170         return -1;
171     }
172     
173     g_assert(r > 0);
174
175     return 0;
176 }
177
178 GTimeVal *avahi_elapse_time(GTimeVal *tv, guint msec, guint jitter) {
179     g_assert(tv);
180
181     g_get_current_time(tv);
182
183     if (msec)
184         g_time_val_add(tv, msec*1000);
185
186     if (jitter)
187         g_time_val_add(tv, g_random_int_range(0, jitter) * 1000);
188         
189     return tv;
190 }
191
192 gint avahi_age(const GTimeVal *a) {
193     GTimeVal now;
194     
195     g_assert(a);
196
197     g_get_current_time(&now);
198
199     return avahi_timeval_diff(&now, a);
200 }
201
202 /* Read the first label from string *name, unescape "\" and write it to dest */
203 gchar *avahi_unescape_label(const gchar **name, gchar *dest, guint size) {
204     guint i = 0;
205     gchar *d;
206     
207     g_assert(dest);
208     g_assert(size > 0);
209     g_assert(name);
210
211     if (!**name)
212         return NULL;
213
214     d = dest;
215     
216     for (;;) {
217         if (i >= size)
218             return NULL;
219
220         if (**name == '.') {
221             (*name)++;
222             break;
223         }
224         
225         if (**name == 0)
226             break;
227         
228         if (**name == '\\') {
229             (*name) ++;
230             
231             if (**name == 0)
232                 break;
233         }
234         
235         *(d++) = *((*name) ++);
236         i++;
237     }
238
239     g_assert(i < size);
240
241     *d = 0;
242
243     return dest;
244 }
245
246 /* Escape "\" and ".", append \0 */
247 gchar *avahi_escape_label(const guint8* src, guint src_length, gchar **ret_name, guint *ret_size) {
248     gchar *r;
249
250     g_assert(src);
251     g_assert(ret_name);
252     g_assert(*ret_name);
253     g_assert(ret_size);
254     g_assert(*ret_size > 0);
255
256     r = *ret_name;
257
258     while (src_length > 0) {
259         if (*src == '.' || *src == '\\') {
260             if (*ret_size < 3)
261                 return NULL;
262             
263             *((*ret_name) ++) = '\\';
264             (*ret_size) --;
265         }
266
267         if (*ret_size < 2)
268             return NULL;
269         
270         *((*ret_name)++) = *src;
271         (*ret_size) --;
272
273         src_length --;
274         src++;
275     }
276
277     **ret_name = 0;
278
279     return r;
280 }
281
282 static gint utf8_strcasecmp(const gchar *a, const gchar *b) {
283     gchar *ta, *tb;
284     gint r;
285     
286     g_assert(a);
287     g_assert(b);
288
289     ta = g_utf8_casefold(a, -1);
290     tb = g_utf8_casefold(b, -1);
291     r = g_utf8_collate(ta, tb);
292     g_free(ta);
293     g_free(tb);
294
295     return r;
296 }
297
298 gboolean avahi_domain_equal(const gchar *a, const gchar *b) {
299     g_assert(a);
300     g_assert(b);
301
302     if (a == b)
303         return TRUE;
304     
305     for (;;) {
306         gchar ca[65], cb[65], *pa, *pb;
307
308         pa = avahi_unescape_label(&a, ca, sizeof(ca));
309         pb = avahi_unescape_label(&b, cb, sizeof(cb));
310
311         if (!pa && !pb)
312             return TRUE;
313         else if ((pa && !pb) || (!pa && pb))
314             return FALSE;
315
316         if (utf8_strcasecmp(pa, pb))
317             return FALSE;
318     }
319
320     return TRUE;
321 }
322
323 gint avahi_binary_domain_cmp(const gchar *a, const gchar *b) {
324     g_assert(a);
325     g_assert(b);
326
327     if (a == b)
328         return 0;
329
330     for (;;) {
331         gchar ca[65], cb[65], *pa, *pb;
332         gint r;
333
334         pa = avahi_unescape_label(&a, ca, sizeof(ca));
335         pb = avahi_unescape_label(&b, cb, sizeof(cb));
336
337         if (!pa && !pb)
338             return 0;
339         else if (pa && !pb)
340             return 1;
341         else if (!pa && pb)
342             return -1;
343         
344         if ((r = strcmp(pa, pb)))
345             return r;
346     }
347 }
348
349 void avahi_hexdump(gconstpointer p, guint size) {
350     const guint8 *c = p;
351     g_assert(p);
352
353     printf("Dumping %u bytes from %p:\n", size, p);
354     
355     while (size > 0) {
356         guint i;
357
358         for (i = 0; i < 16; i++) { 
359             if (i < size)
360                 printf("%02x ", c[i]);
361             else
362                 printf("   ");
363         }
364
365         for (i = 0; i < 16; i++) {
366             if (i < size)
367                 printf("%c", c[i] >= 32 && c[i] < 127 ? c[i] : '.');
368             else
369                 printf(" ");
370         }
371         
372         printf("\n");
373
374         c += 16;
375
376         if (size <= 16)
377             break;
378         
379         size -= 16;
380     }
381 }
382
383 gint avahi_domain_hash(const gchar *s) {
384     guint hash = 0;
385     
386     for (;;) {
387         gchar c[65], *n, *m;
388
389         if (!avahi_unescape_label(&s, c, sizeof(c)))
390             return hash;
391
392         if (!c[0])
393             continue;
394         
395         n = g_utf8_normalize(c, -1, G_NORMALIZE_DEFAULT);
396         m = g_utf8_strdown(n, -1);
397
398         hash += g_str_hash(m);
399
400         g_free(m);
401         g_free(n);
402     }
403 }