Mercurial > hg > sv-dependency-builds
comparison src/liblo-0.26/src/server.c @ 89:8a15ff55d9af
Add bzip2, zlib, liblo, portaudio sources
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Wed, 20 Mar 2013 13:59:52 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
88:fe7c3a0b0259 | 89:8a15ff55d9af |
---|---|
1 /* | |
2 * Copyright (C) 2004 Steve Harris | |
3 * | |
4 * This program is free software; you can redistribute it and/or modify | |
5 * it under the terms of the GNU Lesser General Public License as | |
6 * published by the Free Software Foundation; either version 2.1 of the | |
7 * License, or (at your option) any later version. | |
8 * | |
9 * This program is distributed in the hope that it will be useful, | |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 * GNU Lesser General Public License for more details. | |
13 * | |
14 * $Id$ | |
15 */ | |
16 | |
17 #ifdef HAVE_CONFIG_H | |
18 #include "config.h" | |
19 #endif | |
20 | |
21 #include <stdlib.h> | |
22 #include <stdio.h> | |
23 #include <string.h> | |
24 #include <errno.h> | |
25 #include <float.h> | |
26 #include <sys/types.h> | |
27 | |
28 #ifdef _MSC_VER | |
29 #define _WINSOCKAPI_ | |
30 #define snprintf _snprintf | |
31 #else | |
32 #include <unistd.h> | |
33 #endif | |
34 | |
35 #ifdef WIN32 | |
36 #include <winsock2.h> | |
37 #include <ws2tcpip.h> | |
38 #define EADDRINUSE WSAEADDRINUSE | |
39 #else | |
40 #include <netdb.h> | |
41 #include <sys/socket.h> | |
42 #ifdef HAVE_POLL | |
43 #include <sys/poll.h> | |
44 #endif | |
45 #include <sys/un.h> | |
46 #include <arpa/inet.h> | |
47 #endif | |
48 | |
49 #ifdef WIN32 | |
50 #define geterror() WSAGetLastError() | |
51 #else | |
52 #define geterror() errno | |
53 #endif | |
54 | |
55 #include "lo_types_internal.h" | |
56 #include "lo_internal.h" | |
57 #include "lo/lo.h" | |
58 #include "lo/lo_throw.h" | |
59 | |
60 #define LO_HOST_SIZE 1024 | |
61 | |
62 typedef struct { | |
63 lo_timetag ts; | |
64 char *path; | |
65 lo_message msg; | |
66 void *next; | |
67 } queued_msg_list; | |
68 | |
69 struct lo_cs lo_client_sockets = {-1, -1}; | |
70 | |
71 static int lo_can_coerce_spec(const char *a, const char *b); | |
72 static int lo_can_coerce(char a, char b); | |
73 static void dispatch_method(lo_server s, const char *path, | |
74 lo_message msg); | |
75 static int dispatch_queued(lo_server s); | |
76 static void queue_data(lo_server s, lo_timetag ts, const char *path, | |
77 lo_message msg); | |
78 static lo_server lo_server_new_with_proto_internal(const char *group, | |
79 const char *port, int proto, | |
80 lo_err_handler err_h); | |
81 static int lo_server_add_socket(lo_server s, int socket); | |
82 static void lo_server_del_socket(lo_server s, int index, int socket); | |
83 static int lo_server_join_multicast_group(lo_server s, const char *group); | |
84 | |
85 #ifdef WIN32 | |
86 #ifndef gai_strerror | |
87 // Copied from the Win32 SDK | |
88 | |
89 // WARNING: The gai_strerror inline functions below use static buffers, | |
90 // and hence are not thread-safe. We'll use buffers long enough to hold | |
91 // 1k characters. Any system error messages longer than this will be | |
92 // returned as empty strings. However 1k should work for the error codes | |
93 // used by getaddrinfo(). | |
94 #define GAI_STRERROR_BUFFER_SIZE 1024 | |
95 | |
96 char *WSAAPI gai_strerrorA(int ecode) | |
97 { | |
98 DWORD dwMsgLen; | |
99 static char buff[GAI_STRERROR_BUFFER_SIZE + 1]; | |
100 | |
101 dwMsgLen = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | |
102 |FORMAT_MESSAGE_IGNORE_INSERTS | |
103 |FORMAT_MESSAGE_MAX_WIDTH_MASK, | |
104 NULL, | |
105 ecode, | |
106 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
107 (LPSTR)buff, | |
108 GAI_STRERROR_BUFFER_SIZE, | |
109 NULL); | |
110 return buff; | |
111 } | |
112 #endif | |
113 | |
114 static int stateWSock = -1; | |
115 | |
116 int initWSock() | |
117 { | |
118 WORD reqversion; | |
119 WSADATA wsaData; | |
120 if(stateWSock >= 0) return stateWSock; | |
121 /* TODO - which version of Winsock do we actually need? */ | |
122 | |
123 reqversion = MAKEWORD( 2, 2 ); | |
124 if(WSAStartup(reqversion,&wsaData) != 0) { | |
125 /* Couldn't initialize Winsock */ | |
126 stateWSock = 0; | |
127 } | |
128 else if ( LOBYTE( wsaData.wVersion ) != LOBYTE(reqversion) || | |
129 HIBYTE( wsaData.wVersion ) != HIBYTE(reqversion) ) { | |
130 /* wrong version */ | |
131 WSACleanup(); | |
132 stateWSock = 0; | |
133 } | |
134 else | |
135 stateWSock = 1; | |
136 | |
137 return stateWSock; | |
138 } | |
139 #endif | |
140 | |
141 lo_server lo_server_new(const char *port, lo_err_handler err_h) | |
142 { | |
143 return lo_server_new_with_proto(port, LO_DEFAULT, err_h); | |
144 } | |
145 | |
146 lo_server lo_server_new_multicast(const char *group, const char *port, | |
147 lo_err_handler err_h) | |
148 { | |
149 return lo_server_new_with_proto_internal(group, port, LO_UDP, err_h); | |
150 } | |
151 | |
152 lo_server lo_server_new_with_proto(const char *port, int proto, | |
153 lo_err_handler err_h) | |
154 { | |
155 return lo_server_new_with_proto_internal(NULL, port, proto, err_h); | |
156 } | |
157 | |
158 lo_server lo_server_new_with_proto_internal(const char *group, | |
159 const char *port, int proto, | |
160 lo_err_handler err_h) | |
161 { | |
162 lo_server s; | |
163 struct addrinfo *ai = NULL, *it, *used; | |
164 struct addrinfo hints; | |
165 int ret = -1; | |
166 int tries = 0; | |
167 char pnum[16]; | |
168 const char *service; | |
169 char hostname[LO_HOST_SIZE]; | |
170 | |
171 // Set real protocol, if Default is requested | |
172 if (proto==LO_DEFAULT) { | |
173 #ifndef WIN32 | |
174 if (port && *port == '/') proto = LO_UNIX; | |
175 else | |
176 #endif | |
177 proto = LO_UDP; | |
178 } | |
179 | |
180 | |
181 #ifdef WIN32 | |
182 if(!initWSock()) return NULL; | |
183 #endif | |
184 | |
185 s = calloc(1, sizeof(struct _lo_server)); | |
186 if (!s) return 0; | |
187 | |
188 s->err_h = err_h; | |
189 s->first = NULL; | |
190 s->ai = NULL; | |
191 s->hostname = NULL; | |
192 s->protocol = proto; | |
193 s->port = 0; | |
194 s->path = NULL; | |
195 s->queued = NULL; | |
196 s->sockets_len = 1; | |
197 s->sockets_alloc = 2; | |
198 s->sockets = calloc(2, sizeof(*(s->sockets))); | |
199 | |
200 if (!s->sockets) { | |
201 free(s); | |
202 return 0; | |
203 } | |
204 | |
205 s->sockets[0].fd = -1; | |
206 | |
207 memset(&hints, 0, sizeof(hints)); | |
208 | |
209 if (proto == LO_UDP) { | |
210 hints.ai_socktype = SOCK_DGRAM; | |
211 } else if (proto == LO_TCP) { | |
212 hints.ai_socktype = SOCK_STREAM; | |
213 } | |
214 #ifndef WIN32 | |
215 else if (proto == LO_UNIX) { | |
216 | |
217 struct sockaddr_un sa; | |
218 | |
219 s->sockets[0].fd = socket(PF_UNIX, SOCK_DGRAM, 0); | |
220 if (s->sockets[0].fd == -1) { | |
221 int err = geterror(); | |
222 used = NULL; | |
223 lo_throw(s, err, strerror(err), "socket()"); | |
224 lo_server_free(s); | |
225 | |
226 return NULL; | |
227 } | |
228 | |
229 sa.sun_family = AF_UNIX; | |
230 strncpy(sa.sun_path, port, sizeof(sa.sun_path)-1); | |
231 | |
232 if ((ret = bind(s->sockets[0].fd, | |
233 (struct sockaddr *)&sa, sizeof(sa))) < 0) { | |
234 int err = geterror(); | |
235 lo_throw(s, err, strerror(err), "bind()"); | |
236 | |
237 lo_server_free(s); | |
238 return NULL; | |
239 } | |
240 | |
241 s->path = strdup(port); | |
242 | |
243 return s; | |
244 } | |
245 #endif | |
246 else { | |
247 lo_throw(s, LO_UNKNOWNPROTO, "Unknown protocol", NULL); | |
248 lo_server_free(s); | |
249 | |
250 return NULL; | |
251 } | |
252 | |
253 #ifdef ENABLE_IPV6 | |
254 hints.ai_family = PF_UNSPEC; | |
255 #else | |
256 hints.ai_family = PF_INET; | |
257 #endif | |
258 hints.ai_flags = AI_PASSIVE; | |
259 | |
260 if (!port) { | |
261 service = pnum; | |
262 } else { | |
263 service = port; | |
264 } | |
265 do { | |
266 if (!port) { | |
267 /* not a good way to get random numbers, but its not critical */ | |
268 snprintf(pnum, 15, "%ld", 10000 + ((unsigned int)rand() + | |
269 time(NULL)) % 10000); | |
270 } | |
271 | |
272 if ((ret = getaddrinfo(NULL, service, &hints, &ai))) { | |
273 lo_throw(s, ret, gai_strerror(ret), NULL); | |
274 freeaddrinfo(ai); | |
275 | |
276 return NULL; | |
277 } | |
278 | |
279 used = NULL; | |
280 s->ai = ai; | |
281 s->sockets[0].fd = -1; | |
282 s->port = 0; | |
283 | |
284 for (it = ai; it && s->sockets[0].fd == -1; it = it->ai_next) { | |
285 used = it; | |
286 s->sockets[0].fd = socket(it->ai_family, hints.ai_socktype, 0); | |
287 } | |
288 if (s->sockets[0].fd == -1) { | |
289 int err = geterror(); | |
290 used = NULL; | |
291 lo_throw(s, err, strerror(err), "socket()"); | |
292 | |
293 lo_server_free(s); | |
294 return NULL; | |
295 } | |
296 | |
297 /* Join multicast group if specified. */ | |
298 /* This must be done before bind() on POSIX, but after bind() Windows. */ | |
299 #ifndef WIN32 | |
300 if (group != NULL) | |
301 if (lo_server_join_multicast_group(s, group)) | |
302 return NULL; | |
303 #endif | |
304 | |
305 if ((ret = bind(s->sockets[0].fd, used->ai_addr, used->ai_addrlen)) < 0) { | |
306 int err = geterror(); | |
307 if (err == EINVAL || err == EADDRINUSE) { | |
308 used = NULL; | |
309 | |
310 continue; | |
311 } | |
312 lo_throw(s, err, strerror(err), "bind()"); | |
313 | |
314 lo_server_free(s); | |
315 | |
316 return NULL; | |
317 } | |
318 } while (!used && tries++ < 16); | |
319 | |
320 /* Join multicast group if specified (see above). */ | |
321 #ifdef WIN32 | |
322 if (group != NULL) | |
323 if (lo_server_join_multicast_group(s, group)) | |
324 return NULL; | |
325 #endif | |
326 | |
327 if (proto == LO_TCP) { | |
328 listen(s->sockets[0].fd, 8); | |
329 } | |
330 | |
331 if (!used) { | |
332 lo_throw(s, LO_NOPORT, "cannot find free port", NULL); | |
333 | |
334 lo_server_free(s); | |
335 return NULL; | |
336 } | |
337 | |
338 if (proto == LO_UDP) { | |
339 lo_client_sockets.udp = s->sockets[0].fd; | |
340 } else if (proto == LO_TCP) { | |
341 lo_client_sockets.tcp = s->sockets[0].fd; | |
342 } | |
343 | |
344 /* Set hostname to empty string */ | |
345 hostname[0] = '\0'; | |
346 | |
347 #ifdef ENABLE_IPV6 | |
348 /* Try it the IPV6 friendly way first */ | |
349 for (it = ai; it; it = it->ai_next) { | |
350 if (getnameinfo(it->ai_addr, it->ai_addrlen, hostname, | |
351 sizeof(hostname), NULL, 0, NI_NAMEREQD) == 0) { | |
352 break; | |
353 } | |
354 } | |
355 | |
356 /* check to make sure getnameinfo() didn't just set the hostname to "::". | |
357 Needed on Darwin. */ | |
358 if (hostname[0] == ':') { | |
359 hostname[0] = '\0'; | |
360 } | |
361 #endif | |
362 | |
363 | |
364 /* Fallback to the oldschool (i.e. more reliable) way */ | |
365 if (!hostname[0]) { | |
366 struct hostent *he; | |
367 | |
368 gethostname(hostname, sizeof(hostname)); | |
369 he = gethostbyname(hostname); | |
370 if (he) { | |
371 strncpy(hostname, he->h_name, sizeof(hostname)); | |
372 } | |
373 } | |
374 | |
375 /* soethings gone really wrong, just hope its local only */ | |
376 if (!hostname[0]) { | |
377 strcpy(hostname, "localhost"); | |
378 } | |
379 s->hostname = strdup(hostname); | |
380 | |
381 if (used->ai_family == PF_INET6) { | |
382 struct sockaddr_in6 *addr = (struct sockaddr_in6 *)used->ai_addr; | |
383 | |
384 s->port = htons(addr->sin6_port); | |
385 } else if (used->ai_family == PF_INET) { | |
386 struct sockaddr_in *addr = (struct sockaddr_in *)used->ai_addr; | |
387 | |
388 s->port = htons(addr->sin_port); | |
389 } else { | |
390 lo_throw(s, LO_UNKNOWNPROTO, "unknown protocol family", NULL); | |
391 s->port = atoi(port); | |
392 } | |
393 | |
394 return s; | |
395 } | |
396 | |
397 int lo_server_join_multicast_group(lo_server s, const char *group) | |
398 { | |
399 struct ip_mreq mreq; | |
400 unsigned int yes = 1; | |
401 memset(&mreq, 0, sizeof(mreq)); | |
402 #ifdef HAVE_INET_ATON | |
403 if (inet_aton(group, &mreq.imr_multiaddr)==0) { | |
404 int err = geterror(); | |
405 lo_throw(s, err, strerror(err), "inet_aton()"); | |
406 lo_server_free(s); | |
407 return err; | |
408 } | |
409 #else | |
410 mreq.imr_multiaddr.s_addr = inet_addr(group); | |
411 if (mreq.imr_multiaddr.s_addr == INADDR_ANY | |
412 || mreq.imr_multiaddr.s_addr == INADDR_NONE) | |
413 { | |
414 int err = geterror(); | |
415 lo_throw(s, err, strerror(err), "inet_addr()"); | |
416 lo_server_free(s); | |
417 return err; | |
418 } | |
419 #endif | |
420 mreq.imr_interface.s_addr=htonl(INADDR_ANY); | |
421 | |
422 if (setsockopt(s->sockets[0].fd,IPPROTO_IP,IP_ADD_MEMBERSHIP, | |
423 &mreq,sizeof(mreq)) < 0) | |
424 { | |
425 int err = geterror(); | |
426 lo_throw(s, err, strerror(err), "setsockopt(IP_ADD_MEMBERSHIP)"); | |
427 lo_server_free(s); | |
428 return err; | |
429 } | |
430 | |
431 if (setsockopt(s->sockets[0].fd,SOL_SOCKET,SO_REUSEADDR, | |
432 &yes,sizeof(yes)) < 0) | |
433 { | |
434 int err = geterror(); | |
435 lo_throw(s, err, strerror(err), "setsockopt(SO_REUSEADDR)"); | |
436 lo_server_free(s); | |
437 return err; | |
438 } | |
439 | |
440 #ifdef SO_REUSEPORT | |
441 if (setsockopt(s->sockets[0].fd,SOL_SOCKET,SO_REUSEPORT, | |
442 &yes,sizeof(yes)) < 0) | |
443 { | |
444 int err = geterror(); | |
445 lo_throw(s, err, strerror(err), "setsockopt(SO_REUSEPORT)"); | |
446 lo_server_free(s); | |
447 return err; | |
448 } | |
449 #endif | |
450 | |
451 return 0; | |
452 } | |
453 | |
454 void lo_server_free(lo_server s) | |
455 { | |
456 if (s) { | |
457 lo_method it; | |
458 lo_method next; | |
459 int i; | |
460 | |
461 for (i=s->sockets_len-1; i >= 0; i--) | |
462 { | |
463 if (s->sockets[i].fd != -1) { | |
464 if (s->protocol == LO_UDP | |
465 && s->sockets[i].fd == lo_client_sockets.udp) | |
466 { | |
467 lo_client_sockets.udp = -1; | |
468 } | |
469 else if (s->protocol == LO_TCP | |
470 && s->sockets[0].fd == lo_client_sockets.tcp) | |
471 { | |
472 lo_client_sockets.tcp = -1; | |
473 } | |
474 | |
475 close(s->sockets[i].fd); | |
476 s->sockets[i].fd = -1; | |
477 } | |
478 } | |
479 if (s->ai) { | |
480 freeaddrinfo(s->ai); | |
481 s->ai=NULL; | |
482 } | |
483 if (s->hostname) { | |
484 free(s->hostname); | |
485 s->hostname = NULL; | |
486 } | |
487 if (s->path) { | |
488 if (s->protocol == LO_UNIX) unlink( s->path ); | |
489 free(s->path); | |
490 s->path = NULL; | |
491 } | |
492 for (it = s->first; it; it = next) { | |
493 next = it->next; | |
494 free((char *)it->path); | |
495 free((char *)it->typespec); | |
496 free(it); | |
497 } | |
498 free(s->sockets); | |
499 free(s); | |
500 } | |
501 } | |
502 | |
503 void *lo_server_recv_raw(lo_server s, size_t *size) | |
504 { | |
505 char buffer[LO_MAX_MSG_SIZE]; | |
506 int ret; | |
507 void *data = NULL; | |
508 | |
509 #ifdef WIN32 | |
510 if(!initWSock()) return NULL; | |
511 #endif | |
512 | |
513 s->addr_len = sizeof(s->addr); | |
514 | |
515 ret = recvfrom(s->sockets[0].fd, buffer, LO_MAX_MSG_SIZE, 0, | |
516 (struct sockaddr *)&s->addr, &s->addr_len); | |
517 if (ret <= 0) { | |
518 return NULL; | |
519 } | |
520 data = malloc(ret); | |
521 memcpy(data, buffer, ret); | |
522 | |
523 if (size) *size = ret; | |
524 | |
525 return data; | |
526 } | |
527 | |
528 void *lo_server_recv_raw_stream(lo_server s, size_t *size) | |
529 { | |
530 struct sockaddr_storage addr; | |
531 socklen_t addr_len = sizeof(addr); | |
532 char buffer[LO_MAX_MSG_SIZE]; | |
533 int32_t read_size; | |
534 int ret=0, i; | |
535 void *data = NULL; | |
536 int sock = -1; | |
537 int repeat = 1; | |
538 #ifdef HAVE_SELECT | |
539 #ifndef HAVE_POLL | |
540 fd_set ps; | |
541 int nfds=0; | |
542 #endif | |
543 #endif | |
544 | |
545 /* check sockets in reverse order so that already-open sockets | |
546 * have priority. this allows checking for closed sockets even | |
547 * when new connections are being requested. it also allows to | |
548 * continue looping through the list of sockets after closing and | |
549 * deleting a socket, since deleting sockets doesn't affect the | |
550 * order of the array to the left of the index. */ | |
551 | |
552 #ifdef HAVE_POLL | |
553 for (i=0; i < s->sockets_len; i++) { | |
554 s->sockets[i].events = POLLIN | POLLPRI; | |
555 s->sockets[i].revents = 0; | |
556 } | |
557 | |
558 poll(s->sockets, s->sockets_len, -1); | |
559 | |
560 for (i=(s->sockets_len-1); i >= 0; --i) { | |
561 if (s->sockets[i].revents == POLLERR | |
562 || s->sockets[i].revents == POLLHUP) | |
563 { | |
564 if (i>0) { | |
565 close(s->sockets[i].fd); | |
566 lo_server_del_socket(s, i, s->sockets[i].fd); | |
567 continue; | |
568 } | |
569 else | |
570 return NULL; | |
571 } | |
572 if (s->sockets[i].revents) { | |
573 sock = s->sockets[i].fd; | |
574 | |
575 #else | |
576 #ifdef HAVE_SELECT | |
577 if(!initWSock()) return NULL; | |
578 | |
579 FD_ZERO(&ps); | |
580 for (i=(s->sockets_len-1); i >= 0; --i) { | |
581 FD_SET(s->sockets[i].fd, &ps); | |
582 if (s->sockets[i].fd > nfds) | |
583 nfds = s->sockets[i].fd; | |
584 } | |
585 | |
586 if (select(nfds+1,&ps,NULL,NULL,NULL) == SOCKET_ERROR) | |
587 return NULL; | |
588 | |
589 for (i=0; i < s->sockets_len; i++) { | |
590 if (FD_ISSET(s->sockets[i].fd, &ps)) { | |
591 sock = s->sockets[i].fd; | |
592 | |
593 #endif | |
594 #endif | |
595 | |
596 if (sock == -1 || !repeat) | |
597 return NULL; | |
598 | |
599 /* zeroeth socket is listening for new connections */ | |
600 if (sock == s->sockets[0].fd) { | |
601 sock = accept(sock, (struct sockaddr *)&addr, &addr_len); | |
602 i = lo_server_add_socket(s, sock); | |
603 | |
604 /* only repeat this loop for sockets other than the listening | |
605 * socket, (otherwise i will be wrong next time around) */ | |
606 repeat = 0; | |
607 } | |
608 | |
609 if (i<0) { | |
610 close(sock); | |
611 return NULL; | |
612 } | |
613 | |
614 ret = recv(sock, &read_size, sizeof(read_size), 0); | |
615 read_size = ntohl(read_size); | |
616 if (read_size > LO_MAX_MSG_SIZE || ret <= 0) { | |
617 close(sock); | |
618 lo_server_del_socket(s, i, sock); | |
619 if (ret > 0) | |
620 lo_throw(s, LO_TOOBIG, "Message too large", "recv()"); | |
621 continue; | |
622 } | |
623 ret = recv(sock, buffer, read_size, 0); | |
624 if (ret <= 0) { | |
625 close(sock); | |
626 lo_server_del_socket(s, i, sock); | |
627 continue; | |
628 } | |
629 | |
630 /* end of loop over sockets: successfully read data */ | |
631 break; | |
632 } | |
633 } | |
634 | |
635 data = malloc(ret); | |
636 memcpy(data, buffer, ret); | |
637 | |
638 if (size) *size = ret; | |
639 | |
640 return data; | |
641 } | |
642 | |
643 int lo_server_wait(lo_server s, int timeout) | |
644 { | |
645 int sched_timeout = lo_server_next_event_delay(s) * 1000; | |
646 int i; | |
647 #ifdef HAVE_SELECT | |
648 #ifndef HAVE_POLL | |
649 fd_set ps; | |
650 struct timeval stimeout; | |
651 #endif | |
652 #endif | |
653 | |
654 #ifdef HAVE_POLL | |
655 for (i=0; i < s->sockets_len; i++) { | |
656 s->sockets[i].events = POLLIN | POLLPRI | POLLERR | POLLHUP; | |
657 s->sockets[i].revents = 0; | |
658 } | |
659 | |
660 poll(s->sockets, s->sockets_len, | |
661 timeout > sched_timeout ? sched_timeout : timeout); | |
662 | |
663 if (lo_server_next_event_delay(s) < 0.01) | |
664 return 1; | |
665 | |
666 for (i=0; i < s->sockets_len; i++) { | |
667 if (s->sockets[i].revents == POLLERR | |
668 || s->sockets[i].revents == POLLHUP) | |
669 return 0; | |
670 if (s->sockets[i].revents) | |
671 return 1; | |
672 } | |
673 #else | |
674 #ifdef HAVE_SELECT | |
675 int res,to,nfds=0; | |
676 | |
677 if(!initWSock()) return 0; | |
678 | |
679 to = timeout > sched_timeout ? sched_timeout : timeout; | |
680 stimeout.tv_sec = to/1000; | |
681 stimeout.tv_usec = (to%1000)*1000; | |
682 | |
683 FD_ZERO(&ps); | |
684 for (i=0; i < s->sockets_len; i++) { | |
685 FD_SET(s->sockets[i].fd,&ps); | |
686 if (s->sockets[i].fd > nfds) | |
687 nfds = s->sockets[i].fd; | |
688 } | |
689 | |
690 res = select(nfds+1,&ps,NULL,NULL,&stimeout); | |
691 | |
692 if(res == SOCKET_ERROR) | |
693 return 0; | |
694 | |
695 if (res || lo_server_next_event_delay(s) < 0.01) | |
696 return 1; | |
697 #endif | |
698 #endif | |
699 | |
700 return 0; | |
701 } | |
702 | |
703 int lo_server_recv_noblock(lo_server s, int timeout) | |
704 { | |
705 int result = lo_server_wait(s,timeout); | |
706 if (result>0) { | |
707 return lo_server_recv(s); | |
708 } else { | |
709 return 0; | |
710 } | |
711 } | |
712 | |
713 int lo_server_recv(lo_server s) | |
714 { | |
715 void *data; | |
716 size_t size; | |
717 double sched_time = lo_server_next_event_delay(s); | |
718 int i; | |
719 #ifdef HAVE_SELECT | |
720 #ifndef HAVE_POLL | |
721 fd_set ps; | |
722 struct timeval stimeout; | |
723 int res,nfds=0; | |
724 #endif | |
725 #endif | |
726 | |
727 again: | |
728 if (sched_time > 0.01) { | |
729 if (sched_time > 10.0) { | |
730 sched_time = 10.0; | |
731 } | |
732 | |
733 #ifdef HAVE_POLL | |
734 for (i=0; i < s->sockets_len; i++) { | |
735 s->sockets[i].events = POLLIN | POLLPRI | POLLERR | POLLHUP; | |
736 s->sockets[i].revents = 0; | |
737 } | |
738 | |
739 poll(s->sockets, s->sockets_len, (int)(sched_time * 1000.0)); | |
740 | |
741 for (i=0; i < s->sockets_len; i++) | |
742 { | |
743 if ( s->sockets[i].revents == POLLERR | |
744 || s->sockets[i].revents == POLLHUP) | |
745 return 0; | |
746 | |
747 if (s->sockets[i].revents) | |
748 break; | |
749 } | |
750 | |
751 if (i >= s->sockets_len) | |
752 { | |
753 sched_time = lo_server_next_event_delay(s); | |
754 | |
755 if (sched_time > 0.01) | |
756 goto again; | |
757 | |
758 return dispatch_queued(s); | |
759 } | |
760 #else | |
761 #ifdef HAVE_SELECT | |
762 if(!initWSock()) return 0; | |
763 | |
764 FD_ZERO(&ps); | |
765 for (i=0; i < s->sockets_len; i++) { | |
766 FD_SET(s->sockets[i].fd,&ps); | |
767 if (s->sockets[i].fd > nfds) | |
768 nfds = s->sockets[i].fd; | |
769 } | |
770 | |
771 stimeout.tv_sec = sched_time; | |
772 stimeout.tv_usec = (sched_time-stimeout.tv_sec)*1.e6; | |
773 res = select(nfds+1,&ps,NULL,NULL,&stimeout); | |
774 if(res == SOCKET_ERROR) { | |
775 return 0; | |
776 } | |
777 | |
778 if(!res) { | |
779 sched_time = lo_server_next_event_delay(s); | |
780 | |
781 if (sched_time > 0.01) | |
782 goto again; | |
783 | |
784 return dispatch_queued(s); | |
785 } | |
786 #endif | |
787 #endif | |
788 } else { | |
789 return dispatch_queued(s); | |
790 } | |
791 if (s->protocol == LO_TCP) { | |
792 data = lo_server_recv_raw_stream(s, &size); | |
793 } else { | |
794 data = lo_server_recv_raw(s, &size); | |
795 } | |
796 | |
797 if (!data) { | |
798 return 0; | |
799 } | |
800 if (lo_server_dispatch_data(s, data, size) < 0) { | |
801 free(data); | |
802 return -1; | |
803 } | |
804 free(data); | |
805 return size; | |
806 } | |
807 | |
808 /** \internal \brief Add a socket to this server's list of sockets. | |
809 * \param s The lo_server | |
810 * \param socket The socket number to add. | |
811 * \return The index number of the added socket, or -1 on failure. | |
812 */ | |
813 int lo_server_add_socket(lo_server s, int socket) | |
814 { | |
815 if ((s->sockets_len+1) > s->sockets_alloc) { | |
816 void *sp = realloc(s->sockets, | |
817 sizeof(*(s->sockets))*(s->sockets_alloc*2)); | |
818 if (!sp) | |
819 return -1; | |
820 s->sockets = sp; | |
821 s->sockets_alloc *= 2; | |
822 } | |
823 | |
824 s->sockets[s->sockets_len].fd = socket; | |
825 s->sockets_len ++; | |
826 | |
827 return s->sockets_len-1; | |
828 } | |
829 | |
830 /** \internal \brief Delete a socket from this server's list of sockets. | |
831 * \param s The lo_server | |
832 * \param index The index of the socket to delete, -1 if socket is provided. | |
833 * \param socket The socket number to delete, -1 if index is provided. | |
834 * \return The index number of the added socket. | |
835 */ | |
836 void lo_server_del_socket(lo_server s, int index, int socket) | |
837 { | |
838 int i; | |
839 | |
840 if (index < 0 && socket != -1) { | |
841 for (index=0; index < s->sockets_len; index++) | |
842 if (s->sockets[index].fd == socket) | |
843 break; | |
844 } | |
845 | |
846 if (index < 0 || index >= s->sockets_len) | |
847 return; | |
848 | |
849 for (i=index+1; i < s->sockets_len; i++) | |
850 s->sockets[i-1] = s->sockets[i]; | |
851 s->sockets_len --; | |
852 } | |
853 | |
854 int lo_server_dispatch_data(lo_server s, void *data, size_t size) | |
855 { | |
856 int result = 0; | |
857 char *path = data; | |
858 ssize_t len = lo_validate_string(data, size); | |
859 if (len < 0) { | |
860 lo_throw(s, -len, "Invalid message path", NULL); | |
861 return len; | |
862 } | |
863 | |
864 if (!strcmp(data, "#bundle")) { | |
865 char *pos; | |
866 int remain; | |
867 uint32_t elem_len; | |
868 lo_timetag ts, now; | |
869 | |
870 ssize_t bundle_result = lo_validate_bundle(data, size); | |
871 if (bundle_result < 0) { | |
872 lo_throw(s, -bundle_result, "Invalid bundle", NULL); | |
873 return bundle_result; | |
874 } | |
875 pos = (char *)data + len; | |
876 remain = size - len; | |
877 | |
878 lo_timetag_now(&now); | |
879 ts.sec = lo_otoh32(*((uint32_t *)pos)); | |
880 pos += 4; | |
881 ts.frac = lo_otoh32(*((uint32_t *)pos)); | |
882 pos += 4; | |
883 remain -= 8; | |
884 | |
885 while (remain >= 4) { | |
886 lo_message msg; | |
887 elem_len = lo_otoh32(*((uint32_t *)pos)); | |
888 pos += 4; | |
889 remain -= 4; | |
890 msg = lo_message_deserialise(pos, elem_len, &result); | |
891 if (!msg) { | |
892 lo_throw(s, result, "Invalid bundle element received", path); | |
893 return -result; | |
894 } | |
895 | |
896 // set timetag from bundle | |
897 msg->ts = ts; | |
898 | |
899 // test for immediate dispatch | |
900 if ((ts.sec == LO_TT_IMMEDIATE.sec | |
901 && ts.frac == LO_TT_IMMEDIATE.frac) || | |
902 lo_timetag_diff(ts, now) <= 0.0) { | |
903 dispatch_method(s, pos, msg); | |
904 lo_message_free(msg); | |
905 } else { | |
906 queue_data(s, ts, pos, msg); | |
907 } | |
908 pos += elem_len; | |
909 remain -= elem_len; | |
910 } | |
911 } else { | |
912 lo_message msg = lo_message_deserialise(data, size, &result); | |
913 if (NULL == msg) { | |
914 lo_throw(s, result, "Invalid message received", path); | |
915 return -result; | |
916 } | |
917 dispatch_method(s, data, msg); | |
918 lo_message_free(msg); | |
919 } | |
920 return size; | |
921 } | |
922 | |
923 /* returns the time in seconds until the next scheduled event */ | |
924 double lo_server_next_event_delay(lo_server s) | |
925 { | |
926 if (s->queued) { | |
927 lo_timetag now; | |
928 double delay; | |
929 | |
930 lo_timetag_now(&now); | |
931 delay = lo_timetag_diff(((queued_msg_list *)s->queued)->ts, now); | |
932 | |
933 delay = delay > 100.0 ? 100.0 : delay; | |
934 delay = delay < 0.0 ? 0.0 : delay; | |
935 | |
936 return delay; | |
937 } | |
938 | |
939 return 100.0; | |
940 } | |
941 | |
942 static void dispatch_method(lo_server s, const char *path, | |
943 lo_message msg) | |
944 { | |
945 char *types = msg->types + 1; | |
946 int argc = strlen(types); | |
947 lo_arg **argv = msg->argv; | |
948 lo_method it; | |
949 int ret = 1; | |
950 int err; | |
951 int pattern = strpbrk(path, " #*,?[]{}") != NULL; | |
952 lo_address src = lo_address_new(NULL, NULL); | |
953 char hostname[LO_HOST_SIZE]; | |
954 char portname[32]; | |
955 const char *pptr; | |
956 | |
957 msg->source = src; | |
958 | |
959 //inet_ntop(s->addr.ss_family, &s->addr.padding, hostname, sizeof(hostname)); | |
960 if (s->protocol == LO_UDP && s->addr_len>0) { | |
961 err = getnameinfo((struct sockaddr *)&s->addr, sizeof(s->addr), | |
962 hostname, sizeof(hostname), portname, sizeof(portname), | |
963 NI_NUMERICHOST | NI_NUMERICSERV); | |
964 if (err) { | |
965 switch (err) { | |
966 case EAI_AGAIN: | |
967 lo_throw(s, err, "Try again", path); | |
968 break; | |
969 case EAI_BADFLAGS: | |
970 lo_throw(s, err, "Bad flags", path); | |
971 break; | |
972 case EAI_FAIL: | |
973 lo_throw(s, err, "Failed", path); | |
974 break; | |
975 case EAI_FAMILY: | |
976 lo_throw(s, err, "Cannot resolve address family", path); | |
977 break; | |
978 case EAI_MEMORY: | |
979 lo_throw(s, err, "Out of memory", path); | |
980 break; | |
981 case EAI_NONAME: | |
982 lo_throw(s, err, "Cannot resolve", path); | |
983 break; | |
984 #ifndef WIN32 | |
985 case EAI_SYSTEM: | |
986 lo_throw(s, err, strerror(err), path); | |
987 break; | |
988 #endif | |
989 default: | |
990 lo_throw(s, err, "Unknown error", path); | |
991 break; | |
992 } | |
993 | |
994 return; | |
995 } | |
996 } else { | |
997 hostname[0] = '\0'; | |
998 portname[0] = '\0'; | |
999 } | |
1000 | |
1001 | |
1002 // Store the source information in the lo_address | |
1003 if (src->host) free(src->host); | |
1004 if (src->host) free(src->port); | |
1005 src->host = strdup(hostname); | |
1006 src->port = strdup(portname); | |
1007 src->protocol = s->protocol; | |
1008 | |
1009 for (it = s->first; it; it = it->next) { | |
1010 /* If paths match or handler is wildcard */ | |
1011 if (!it->path || !strcmp(path, it->path) || | |
1012 (pattern && lo_pattern_match(it->path, path))) { | |
1013 /* If types match or handler is wildcard */ | |
1014 if (!it->typespec || !strcmp(types, it->typespec)) { | |
1015 /* Send wildcard path to generic handler, expanded path | |
1016 to others. | |
1017 */ | |
1018 pptr = path; | |
1019 if (it->path) pptr = it->path; | |
1020 ret = it->handler(pptr, types, argv, argc, msg, | |
1021 it->user_data); | |
1022 | |
1023 } else if (lo_can_coerce_spec(types, it->typespec)) { | |
1024 int i; | |
1025 int opsize = 0; | |
1026 char *ptr = msg->data; | |
1027 char *data_co, *data_co_ptr; | |
1028 | |
1029 argv = calloc(argc, sizeof(lo_arg *)); | |
1030 for (i=0; i<argc; i++) { | |
1031 opsize += lo_arg_size(it->typespec[i], ptr); | |
1032 ptr += lo_arg_size(types[i], ptr); | |
1033 } | |
1034 | |
1035 data_co = malloc(opsize); | |
1036 data_co_ptr = data_co; | |
1037 ptr = msg->data; | |
1038 for (i=0; i<argc; i++) { | |
1039 argv[i] = (lo_arg *)data_co_ptr; | |
1040 lo_coerce(it->typespec[i], (lo_arg *)data_co_ptr, | |
1041 types[i], (lo_arg *)ptr); | |
1042 data_co_ptr += lo_arg_size(it->typespec[i], data_co_ptr); | |
1043 ptr += lo_arg_size(types[i], ptr); | |
1044 } | |
1045 | |
1046 /* Send wildcard path to generic handler, expanded path | |
1047 to others. | |
1048 */ | |
1049 pptr = path; | |
1050 if (it->path) pptr = it->path; | |
1051 ret = it->handler(pptr, it->typespec, argv, argc, msg, | |
1052 it->user_data); | |
1053 free(argv); | |
1054 free(data_co); | |
1055 argv = NULL; | |
1056 } | |
1057 | |
1058 if (ret == 0 && !pattern) { | |
1059 break; | |
1060 } | |
1061 } | |
1062 } | |
1063 | |
1064 /* If we find no matching methods, check for protocol level stuff */ | |
1065 if (ret == 1 && s->protocol == LO_UDP) { | |
1066 char *pos = strrchr(path, '/'); | |
1067 | |
1068 /* if its a method enumeration call */ | |
1069 if (pos && *(pos+1) == '\0') { | |
1070 lo_message reply = lo_message_new(); | |
1071 int len = strlen(path); | |
1072 lo_strlist *sl = NULL, *slit, *slnew, *slend; | |
1073 | |
1074 if (!strcmp(types, "i")) { | |
1075 lo_message_add_int32(reply, argv[0]->i); | |
1076 } | |
1077 lo_message_add_string(reply, path); | |
1078 | |
1079 for (it = s->first; it; it = it->next) { | |
1080 /* If paths match */ | |
1081 if (it->path && !strncmp(path, it->path, len)) { | |
1082 char *tmp; | |
1083 char *sec; | |
1084 | |
1085 tmp = malloc(strlen(it->path + len) + 1); | |
1086 strcpy(tmp, it->path + len); | |
1087 #ifdef WIN32 | |
1088 sec = strchr(tmp,'/'); | |
1089 #else | |
1090 sec = index(tmp, '/'); | |
1091 #endif | |
1092 if (sec) *sec = '\0'; | |
1093 slend = sl; | |
1094 for (slit = sl; slit; slend = slit, slit = slit->next) { | |
1095 if (!strcmp(slit->str, tmp)) { | |
1096 free(tmp); | |
1097 tmp = NULL; | |
1098 break; | |
1099 } | |
1100 } | |
1101 if (tmp) { | |
1102 slnew = calloc(1, sizeof(lo_strlist)); | |
1103 slnew->str = tmp; | |
1104 slnew->next = NULL; | |
1105 if (!slend) { | |
1106 sl = slnew; | |
1107 } else { | |
1108 slend->next = slnew; | |
1109 } | |
1110 } | |
1111 } | |
1112 } | |
1113 | |
1114 slit = sl; | |
1115 while(slit) { | |
1116 lo_message_add_string(reply, slit->str); | |
1117 slnew = slit; | |
1118 slit = slit->next; | |
1119 free(slnew->str); | |
1120 free(slnew); | |
1121 } | |
1122 lo_send_message(src, "#reply", reply); | |
1123 lo_message_free(reply); | |
1124 } | |
1125 } | |
1126 | |
1127 lo_address_free(src); | |
1128 msg->source = NULL; | |
1129 } | |
1130 | |
1131 int lo_server_events_pending(lo_server s) | |
1132 { | |
1133 return s->queued != 0; | |
1134 } | |
1135 | |
1136 static void queue_data(lo_server s, lo_timetag ts, const char *path, | |
1137 lo_message msg) | |
1138 { | |
1139 /* insert blob into future dispatch queue */ | |
1140 queued_msg_list *it = s->queued; | |
1141 queued_msg_list *prev = NULL; | |
1142 queued_msg_list *ins = calloc(1, sizeof(queued_msg_list)); | |
1143 | |
1144 ins->ts = ts; | |
1145 ins->path = strdup(path); | |
1146 ins->msg = msg; | |
1147 | |
1148 while (it) { | |
1149 if (lo_timetag_diff(it->ts, ts) > 0.0) { | |
1150 if (prev) { | |
1151 prev->next = ins; | |
1152 } else { | |
1153 s->queued = ins; | |
1154 ins->next = NULL; | |
1155 } | |
1156 ins->next = it; | |
1157 | |
1158 return; | |
1159 } | |
1160 prev = it; | |
1161 it = it->next; | |
1162 } | |
1163 | |
1164 /* fell through, so this event is last */ | |
1165 if (prev) { | |
1166 prev->next = ins; | |
1167 } else { | |
1168 s->queued = ins; | |
1169 } | |
1170 ins->next = NULL; | |
1171 } | |
1172 | |
1173 static int dispatch_queued(lo_server s) | |
1174 { | |
1175 queued_msg_list *head = s->queued; | |
1176 queued_msg_list *tailhead; | |
1177 lo_timetag disp_time; | |
1178 | |
1179 if (!head) { | |
1180 lo_throw(s, LO_INT_ERR, "attempted to dispatch with empty queue", | |
1181 "timeout"); | |
1182 return 1; | |
1183 } | |
1184 | |
1185 disp_time = head->ts; | |
1186 | |
1187 do { | |
1188 char *path; | |
1189 lo_message msg; | |
1190 tailhead = head->next; | |
1191 path = ((queued_msg_list *)s->queued)->path; | |
1192 msg = ((queued_msg_list *)s->queued)->msg; | |
1193 dispatch_method(s, path, msg); | |
1194 free(path); | |
1195 lo_message_free(msg); | |
1196 free((queued_msg_list *)s->queued); | |
1197 | |
1198 s->queued = tailhead; | |
1199 head = tailhead; | |
1200 } while (head && lo_timetag_diff(head->ts, disp_time) < FLT_EPSILON); | |
1201 | |
1202 return 0; | |
1203 } | |
1204 | |
1205 lo_method lo_server_add_method(lo_server s, const char *path, | |
1206 const char *typespec, lo_method_handler h, | |
1207 void *user_data) | |
1208 { | |
1209 lo_method m = calloc(1, sizeof(struct _lo_method)); | |
1210 lo_method it; | |
1211 | |
1212 if (path && strpbrk(path, " #*,?[]{}")) { | |
1213 return NULL; | |
1214 } | |
1215 | |
1216 if (path) { | |
1217 m->path = strdup(path); | |
1218 } else { | |
1219 m->path = NULL; | |
1220 } | |
1221 | |
1222 if (typespec) { | |
1223 m->typespec = strdup(typespec); | |
1224 } else { | |
1225 m->typespec = NULL; | |
1226 } | |
1227 | |
1228 m->handler = h; | |
1229 m->user_data = user_data; | |
1230 m->next = NULL; | |
1231 | |
1232 /* append the new method to the list */ | |
1233 if (!s->first) { | |
1234 s->first = m; | |
1235 } else { | |
1236 /* get to the last member of the list */ | |
1237 for (it=s->first; it->next; it=it->next); | |
1238 it->next = m; | |
1239 } | |
1240 | |
1241 return m; | |
1242 } | |
1243 | |
1244 void lo_server_del_method(lo_server s, const char *path, | |
1245 const char *typespec) | |
1246 { | |
1247 lo_method it, prev, next; | |
1248 int pattern = 0; | |
1249 | |
1250 if (!s->first) return; | |
1251 if (path) pattern = strpbrk(path, " #*,?[]{}") != NULL; | |
1252 | |
1253 it = s->first; | |
1254 prev = it; | |
1255 while (it) { | |
1256 /* incase we free it */ | |
1257 next = it->next; | |
1258 | |
1259 /* If paths match or handler is wildcard */ | |
1260 if ((it->path == path) || | |
1261 (path && it->path && !strcmp(path, it->path)) || | |
1262 (pattern && it->path && lo_pattern_match(it->path, path))) { | |
1263 /* If types match or handler is wildcard */ | |
1264 if ((it->typespec == typespec) || | |
1265 (typespec && it->typespec && !strcmp(typespec, it->typespec)) | |
1266 ) { | |
1267 /* Take care when removing the head. */ | |
1268 if (it == s->first) { | |
1269 s->first = it->next; | |
1270 } else { | |
1271 prev->next = it->next; | |
1272 } | |
1273 next = it->next; | |
1274 free((void *)it->path); | |
1275 free((void *)it->typespec); | |
1276 free(it); | |
1277 it = prev; | |
1278 } | |
1279 } | |
1280 prev = it; | |
1281 if (it) it = next; | |
1282 } | |
1283 } | |
1284 | |
1285 int lo_server_get_socket_fd(lo_server s) | |
1286 { | |
1287 if (s->protocol != LO_UDP && | |
1288 s->protocol != LO_TCP | |
1289 #ifndef WIN32 | |
1290 && s->protocol != LO_UNIX | |
1291 #endif | |
1292 ) { | |
1293 return -1; /* assume it is not supported */ | |
1294 } | |
1295 return s->sockets[0].fd; | |
1296 } | |
1297 | |
1298 int lo_server_get_port(lo_server s) | |
1299 { | |
1300 if (!s) { | |
1301 return 0; | |
1302 } | |
1303 | |
1304 return s->port; | |
1305 } | |
1306 | |
1307 int lo_server_get_protocol(lo_server s) | |
1308 { | |
1309 if (!s) { | |
1310 return -1; | |
1311 } | |
1312 | |
1313 return s->protocol; | |
1314 } | |
1315 | |
1316 | |
1317 char *lo_server_get_url(lo_server s) | |
1318 { | |
1319 int ret=0; | |
1320 char *buf; | |
1321 | |
1322 if (!s) { | |
1323 return NULL; | |
1324 } | |
1325 | |
1326 if (s->protocol == LO_UDP || s->protocol == LO_TCP) { | |
1327 char *proto = s->protocol == LO_UDP ? "udp" : "tcp"; | |
1328 | |
1329 #ifndef _MSC_VER | |
1330 ret = snprintf(NULL, 0, "osc.%s://%s:%d/", proto, s->hostname, s->port); | |
1331 #endif | |
1332 if (ret <= 0) { | |
1333 /* this libc is not C99 compliant, guess a size */ | |
1334 ret = 1023; | |
1335 } | |
1336 buf = malloc((ret + 2) * sizeof(char)); | |
1337 snprintf(buf, ret+1, "osc.%s://%s:%d/", proto, s->hostname, s->port); | |
1338 | |
1339 return buf; | |
1340 } | |
1341 #ifndef WIN32 | |
1342 else if (s->protocol == LO_UNIX) { | |
1343 ret = snprintf(NULL, 0, "osc.unix:///%s", s->path); | |
1344 if (ret <= 0) { | |
1345 /* this libc is not C99 compliant, guess a size */ | |
1346 ret = 1023; | |
1347 } | |
1348 buf = malloc((ret + 2) * sizeof(char)); | |
1349 snprintf(buf, ret+1, "osc.unix:///%s", s->path); | |
1350 | |
1351 return buf; | |
1352 } | |
1353 #endif | |
1354 return NULL; | |
1355 } | |
1356 | |
1357 void lo_server_pp(lo_server s) | |
1358 { | |
1359 lo_method it; | |
1360 | |
1361 printf("socket: %d\n\n", s->sockets[0].fd); | |
1362 printf("Methods\n"); | |
1363 for (it = s->first; it; it = it->next) { | |
1364 printf("\n"); | |
1365 lo_method_pp_prefix(it, " "); | |
1366 } | |
1367 } | |
1368 | |
1369 static int lo_can_coerce_spec(const char *a, const char *b) | |
1370 { | |
1371 unsigned int i; | |
1372 | |
1373 if (strlen(a) != strlen(b)) { | |
1374 return 0; | |
1375 } | |
1376 | |
1377 for (i=0; a[i]; i++) { | |
1378 if (!lo_can_coerce(a[i], b[i])) { | |
1379 return 0; | |
1380 } | |
1381 } | |
1382 | |
1383 return 1; | |
1384 } | |
1385 | |
1386 static int lo_can_coerce(char a, char b) | |
1387 { | |
1388 return ((a == b) || | |
1389 (lo_is_numerical_type(a) && lo_is_numerical_type(b)) || | |
1390 (lo_is_string_type(a) && lo_is_string_type (b))); | |
1391 } | |
1392 | |
1393 void lo_throw(lo_server s, int errnum, const char *message, const char *path) | |
1394 { | |
1395 if (s->err_h) { | |
1396 (*s->err_h)(errnum, message, path); | |
1397 } | |
1398 } | |
1399 | |
1400 /* vi:set ts=8 sts=4 sw=4: */ |