Chris@4
|
1 /*
|
Chris@4
|
2 * Copyright (C) 2004 Steve Harris
|
Chris@4
|
3 *
|
Chris@4
|
4 * This program is free software; you can redistribute it and/or modify
|
Chris@4
|
5 * it under the terms of the GNU Lesser General Public License as
|
Chris@4
|
6 * published by the Free Software Foundation; either version 2.1 of the
|
Chris@4
|
7 * License, or (at your option) any later version.
|
Chris@4
|
8 *
|
Chris@4
|
9 * This program is distributed in the hope that it will be useful,
|
Chris@4
|
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
Chris@4
|
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
Chris@4
|
12 * GNU Lesser General Public License for more details.
|
Chris@4
|
13 *
|
Chris@4
|
14 * $Id$
|
Chris@4
|
15 */
|
Chris@4
|
16
|
Chris@4
|
17 #ifdef HAVE_CONFIG_H
|
Chris@4
|
18 #include "config.h"
|
Chris@4
|
19 #endif
|
Chris@4
|
20
|
Chris@4
|
21 #include <stdarg.h>
|
Chris@4
|
22 #include <stdlib.h>
|
Chris@4
|
23 #include <stdio.h>
|
Chris@4
|
24 #include <string.h>
|
Chris@4
|
25 #include <errno.h>
|
Chris@4
|
26 #include <sys/types.h>
|
Chris@4
|
27
|
Chris@4
|
28 #ifdef _MSC_VER
|
Chris@4
|
29 #include <io.h>
|
Chris@4
|
30 #else
|
Chris@4
|
31 #include <unistd.h>
|
Chris@4
|
32 #endif
|
Chris@4
|
33
|
Chris@4
|
34 #ifdef WIN32
|
Chris@4
|
35 #include <winsock2.h>
|
Chris@4
|
36 #include <ws2tcpip.h>
|
Chris@4
|
37 #else
|
Chris@4
|
38 #include <netdb.h>
|
Chris@4
|
39 #include <sys/socket.h>
|
Chris@4
|
40 #include <sys/un.h>
|
Chris@4
|
41 #endif
|
Chris@4
|
42
|
Chris@4
|
43 #include "lo_types_internal.h"
|
Chris@4
|
44 #include "lo/lo.h"
|
Chris@4
|
45
|
Chris@4
|
46 #ifndef MSG_NOSIGNAL
|
Chris@4
|
47 #define MSG_NOSIGNAL 0
|
Chris@4
|
48 #endif
|
Chris@4
|
49
|
Chris@4
|
50 #ifdef WIN32
|
Chris@4
|
51 int initWSock();
|
Chris@4
|
52 #endif
|
Chris@4
|
53
|
Chris@4
|
54 #ifdef WIN32
|
Chris@4
|
55 #define geterror() WSAGetLastError()
|
Chris@4
|
56 #else
|
Chris@4
|
57 #define geterror() errno
|
Chris@4
|
58 #endif
|
Chris@4
|
59
|
Chris@4
|
60 static int resolve_address(lo_address a);
|
Chris@4
|
61 static int create_socket(lo_address a);
|
Chris@4
|
62 static int send_data(lo_address a, lo_server from, char *data, const size_t data_len);
|
Chris@4
|
63
|
Chris@4
|
64 // message.c
|
Chris@4
|
65 int lo_message_add_varargs_internal(lo_message m, const char *types, va_list ap,
|
Chris@4
|
66 const char *file, int line);
|
Chris@4
|
67
|
Chris@4
|
68
|
Chris@4
|
69
|
Chris@4
|
70 /* Don't call lo_send_internal directly, use lo_send, a macro wrapping this
|
Chris@4
|
71 * function with appropriate values for file and line */
|
Chris@4
|
72
|
Chris@4
|
73 #ifdef __GNUC__
|
Chris@4
|
74 int lo_send_internal(lo_address t, const char *file, const int line,
|
Chris@4
|
75 const char *path, const char *types, ...)
|
Chris@4
|
76 #else
|
Chris@4
|
77 int lo_send(lo_address t, const char *path, const char *types, ...)
|
Chris@4
|
78 #endif
|
Chris@4
|
79 {
|
Chris@4
|
80 va_list ap;
|
Chris@4
|
81 int ret;
|
Chris@4
|
82 #ifndef __GNUC__
|
Chris@4
|
83 const char *file = "";
|
Chris@4
|
84 int line = 0;
|
Chris@4
|
85 #endif
|
Chris@4
|
86
|
Chris@4
|
87 lo_message msg = lo_message_new();
|
Chris@4
|
88
|
Chris@4
|
89 t->errnum = 0;
|
Chris@4
|
90 t->errstr = NULL;
|
Chris@4
|
91
|
Chris@4
|
92 va_start(ap, types);
|
Chris@4
|
93 ret = lo_message_add_varargs_internal(msg, types, ap, file, line);
|
Chris@4
|
94
|
Chris@4
|
95 if (ret) {
|
Chris@4
|
96 lo_message_free(msg);
|
Chris@4
|
97 t->errnum = ret;
|
Chris@4
|
98 if (ret == -1) t->errstr = "unknown type";
|
Chris@4
|
99 else t->errstr = "bad format/args";
|
Chris@4
|
100 return ret;
|
Chris@4
|
101 }
|
Chris@4
|
102
|
Chris@4
|
103 ret = lo_send_message(t, path, msg);
|
Chris@4
|
104 lo_message_free(msg);
|
Chris@4
|
105
|
Chris@4
|
106 return ret;
|
Chris@4
|
107 }
|
Chris@4
|
108
|
Chris@4
|
109
|
Chris@4
|
110 /* Don't call lo_send_timestamped_internal directly, use lo_send_timestamped, a
|
Chris@4
|
111 * macro wrapping this function with appropriate values for file and line */
|
Chris@4
|
112
|
Chris@4
|
113 #ifdef __GNUC__
|
Chris@4
|
114 int lo_send_timestamped_internal(lo_address t, const char *file,
|
Chris@4
|
115 const int line, lo_timetag ts,
|
Chris@4
|
116 const char *path, const char *types, ...)
|
Chris@4
|
117 #else
|
Chris@4
|
118 int lo_send_timestamped(lo_address t, lo_timetag ts,
|
Chris@4
|
119 const char *path, const char *types, ...)
|
Chris@4
|
120 #endif
|
Chris@4
|
121 {
|
Chris@4
|
122 va_list ap;
|
Chris@4
|
123 int ret;
|
Chris@4
|
124
|
Chris@4
|
125 lo_message msg = lo_message_new();
|
Chris@4
|
126 lo_bundle b = lo_bundle_new(ts);
|
Chris@4
|
127
|
Chris@4
|
128 #ifndef __GNUC__
|
Chris@4
|
129 const char *file = "";
|
Chris@4
|
130 int line = 0;
|
Chris@4
|
131 #endif
|
Chris@4
|
132
|
Chris@4
|
133 t->errnum = 0;
|
Chris@4
|
134 t->errstr = NULL;
|
Chris@4
|
135
|
Chris@4
|
136 va_start(ap, types);
|
Chris@4
|
137 ret = lo_message_add_varargs_internal(msg, types, ap, file, line);
|
Chris@4
|
138
|
Chris@4
|
139 if (t->errnum) {
|
Chris@4
|
140 lo_message_free(msg);
|
Chris@4
|
141 return t->errnum;
|
Chris@4
|
142 }
|
Chris@4
|
143
|
Chris@4
|
144 lo_bundle_add_message(b, path, msg);
|
Chris@4
|
145 ret = lo_send_bundle(t, b);
|
Chris@4
|
146 lo_message_free(msg);
|
Chris@4
|
147 lo_bundle_free(b);
|
Chris@4
|
148
|
Chris@4
|
149 return ret;
|
Chris@4
|
150 }
|
Chris@4
|
151
|
Chris@4
|
152 /* Don't call lo_send_from_internal directly, use macros wrapping this
|
Chris@4
|
153 * function with appropriate values for file and line */
|
Chris@4
|
154
|
Chris@4
|
155 #ifdef __GNUC__
|
Chris@4
|
156 int lo_send_from_internal(lo_address to, lo_server from, const char *file,
|
Chris@4
|
157 const int line, lo_timetag ts,
|
Chris@4
|
158 const char *path, const char *types, ...)
|
Chris@4
|
159 #else
|
Chris@4
|
160 int lo_send_from(lo_address to, lo_server from, lo_timetag ts,
|
Chris@4
|
161 const char *path, const char *types, ...)
|
Chris@4
|
162 #endif
|
Chris@4
|
163 {
|
Chris@4
|
164 lo_bundle b = NULL;
|
Chris@4
|
165 va_list ap;
|
Chris@4
|
166 int ret;
|
Chris@4
|
167
|
Chris@4
|
168 #ifndef __GNUC__
|
Chris@4
|
169 const char *file = "";
|
Chris@4
|
170 int line = 0;
|
Chris@4
|
171 #endif
|
Chris@4
|
172
|
Chris@4
|
173 lo_message msg = lo_message_new();
|
Chris@4
|
174 if (ts.sec!=LO_TT_IMMEDIATE.sec || ts.frac!=LO_TT_IMMEDIATE.frac)
|
Chris@4
|
175 b = lo_bundle_new(ts);
|
Chris@4
|
176
|
Chris@4
|
177 // Clear any previous errors
|
Chris@4
|
178 to->errnum = 0;
|
Chris@4
|
179 to->errstr = NULL;
|
Chris@4
|
180
|
Chris@4
|
181 va_start(ap, types);
|
Chris@4
|
182 ret = lo_message_add_varargs_internal(msg, types, ap, file, line);
|
Chris@4
|
183
|
Chris@4
|
184 if (to->errnum) {
|
Chris@4
|
185 if (b) lo_bundle_free(b);
|
Chris@4
|
186 lo_message_free(msg);
|
Chris@4
|
187 return to->errnum;
|
Chris@4
|
188 }
|
Chris@4
|
189
|
Chris@4
|
190 if (b) {
|
Chris@4
|
191 lo_bundle_add_message(b, path, msg);
|
Chris@4
|
192 ret = lo_send_bundle_from(to, from, b);
|
Chris@4
|
193 } else {
|
Chris@4
|
194 ret = lo_send_message_from(to, from, path, msg);
|
Chris@4
|
195 }
|
Chris@4
|
196
|
Chris@4
|
197 // Free-up memory
|
Chris@4
|
198 lo_message_free(msg);
|
Chris@4
|
199 if (b) lo_bundle_free(b);
|
Chris@4
|
200
|
Chris@4
|
201 return ret;
|
Chris@4
|
202 }
|
Chris@4
|
203
|
Chris@4
|
204
|
Chris@4
|
205 #if 0
|
Chris@4
|
206
|
Chris@4
|
207 This (incomplete) function converts from printf-style formats to OSC typetags,
|
Chris@4
|
208 but I think its dangerous and mislieading so its not available at the moment.
|
Chris@4
|
209
|
Chris@4
|
210 static char *format_to_types(const char *format);
|
Chris@4
|
211
|
Chris@4
|
212 static char *format_to_types(const char *format)
|
Chris@4
|
213 {
|
Chris@4
|
214 const char *ptr;
|
Chris@4
|
215 char *types = malloc(sizeof(format) + 1);
|
Chris@4
|
216 char *out = types;
|
Chris@4
|
217 int inspec = 0;
|
Chris@4
|
218 int width = 0;
|
Chris@4
|
219 int number = 0;
|
Chris@4
|
220
|
Chris@4
|
221 if (!format) {
|
Chris@4
|
222 return NULL;
|
Chris@4
|
223 }
|
Chris@4
|
224
|
Chris@4
|
225 for (ptr = format; *ptr; ptr++) {
|
Chris@4
|
226 if (inspec) {
|
Chris@4
|
227 if (*ptr == 'l') {
|
Chris@4
|
228 width++;
|
Chris@4
|
229 } else if (*ptr >= '0' && *ptr <= '9') {
|
Chris@4
|
230 number *= 10;
|
Chris@4
|
231 number += *ptr - '0';
|
Chris@4
|
232 } else if (*ptr == 'd') {
|
Chris@4
|
233 if (width < 2 && number < 64) {
|
Chris@4
|
234 *out++ = LO_INT32;
|
Chris@4
|
235 } else {
|
Chris@4
|
236 *out++ = LO_INT64;
|
Chris@4
|
237 }
|
Chris@4
|
238 } else if (*ptr == 'f') {
|
Chris@4
|
239 if (width < 2 && number < 64) {
|
Chris@4
|
240 *out++ = LO_FLOAT;
|
Chris@4
|
241 } else {
|
Chris@4
|
242 *out++ = LO_DOUBLE;
|
Chris@4
|
243 }
|
Chris@4
|
244 } else if (*ptr == '%') {
|
Chris@4
|
245 fprintf(stderr, "liblo warning, unexpected '%%' in format\n");
|
Chris@4
|
246 inspec = 1;
|
Chris@4
|
247 width = 0;
|
Chris@4
|
248 number = 0;
|
Chris@4
|
249 } else {
|
Chris@4
|
250 fprintf(stderr, "liblo warning, unrecognised character '%c' "
|
Chris@4
|
251 "in format\n", *ptr);
|
Chris@4
|
252 }
|
Chris@4
|
253 } else {
|
Chris@4
|
254 if (*ptr == '%') {
|
Chris@4
|
255 inspec = 1;
|
Chris@4
|
256 width = 0;
|
Chris@4
|
257 number = 0;
|
Chris@4
|
258 } else if (*ptr == LO_TRUE || *ptr == LO_FALSE || *ptr == LO_NIL ||
|
Chris@4
|
259 *ptr == LO_INFINITUM) {
|
Chris@4
|
260 *out++ = *ptr;
|
Chris@4
|
261 } else {
|
Chris@4
|
262 fprintf(stderr, "liblo warning, unrecognised character '%c' "
|
Chris@4
|
263 "in format\n", *ptr);
|
Chris@4
|
264 }
|
Chris@4
|
265 }
|
Chris@4
|
266 }
|
Chris@4
|
267 *out++ = '\0';
|
Chris@4
|
268
|
Chris@4
|
269 return types;
|
Chris@4
|
270 }
|
Chris@4
|
271
|
Chris@4
|
272 #endif
|
Chris@4
|
273
|
Chris@4
|
274
|
Chris@4
|
275 static int resolve_address(lo_address a)
|
Chris@4
|
276 {
|
Chris@4
|
277 int ret;
|
Chris@4
|
278
|
Chris@4
|
279 if (a->protocol == LO_UDP || a->protocol == LO_TCP) {
|
Chris@4
|
280 struct addrinfo *ai;
|
Chris@4
|
281 struct addrinfo hints;
|
Chris@4
|
282
|
Chris@4
|
283 memset(&hints, 0, sizeof(hints));
|
Chris@4
|
284 #ifdef ENABLE_IPV6
|
Chris@4
|
285 hints.ai_family = PF_UNSPEC;
|
Chris@4
|
286 #else
|
Chris@4
|
287 hints.ai_family = PF_INET;
|
Chris@4
|
288 #endif
|
Chris@4
|
289 hints.ai_socktype = a->protocol == LO_UDP ? SOCK_DGRAM : SOCK_STREAM;
|
Chris@4
|
290
|
Chris@4
|
291 if ((ret = getaddrinfo(a->host, a->port, &hints, &ai))) {
|
Chris@4
|
292 a->errnum = ret;
|
Chris@4
|
293 a->errstr = gai_strerror(ret);
|
Chris@4
|
294 a->ai = NULL;
|
Chris@4
|
295 return -1;
|
Chris@4
|
296 }
|
Chris@4
|
297
|
Chris@4
|
298 a->ai = ai;
|
Chris@4
|
299 }
|
Chris@4
|
300
|
Chris@4
|
301 return 0;
|
Chris@4
|
302 }
|
Chris@4
|
303
|
Chris@4
|
304 static int create_socket(lo_address a)
|
Chris@4
|
305 {
|
Chris@4
|
306 if (a->protocol == LO_UDP || a->protocol == LO_TCP) {
|
Chris@4
|
307
|
Chris@4
|
308 a->socket = socket(a->ai->ai_family, a->ai->ai_socktype, 0);
|
Chris@4
|
309 if (a->socket == -1) {
|
Chris@4
|
310 a->errnum = geterror();
|
Chris@4
|
311 a->errstr = NULL;
|
Chris@4
|
312 return -1;
|
Chris@4
|
313 }
|
Chris@4
|
314
|
Chris@4
|
315 if (a->protocol == LO_TCP) {
|
Chris@4
|
316 // Only call connect() for TCP sockets - we use sendto() for UDP
|
Chris@4
|
317 if ((connect(a->socket, a->ai->ai_addr, a->ai->ai_addrlen))) {
|
Chris@4
|
318 a->errnum = geterror();
|
Chris@4
|
319 a->errstr = NULL;
|
Chris@4
|
320 close(a->socket);
|
Chris@4
|
321 a->socket = -1;
|
Chris@4
|
322 return -1;
|
Chris@4
|
323 }
|
Chris@4
|
324 }
|
Chris@4
|
325 // if UDP and destination address is broadcast allow broadcast on the
|
Chris@4
|
326 // socket
|
Chris@4
|
327 else if (a->protocol == LO_UDP && a->ai->ai_family == AF_INET)
|
Chris@4
|
328 {
|
Chris@4
|
329 // If UDP, and destination address is broadcast,
|
Chris@4
|
330 // then allow broadcast on the socket.
|
Chris@4
|
331 struct sockaddr_in* si = (struct sockaddr_in*)a->ai->ai_addr;
|
Chris@4
|
332 unsigned char* ip = (unsigned char*)&(si->sin_addr);
|
Chris@4
|
333
|
Chris@4
|
334 if (ip[0]==255 && ip[1]==255 && ip[2]==255 && ip[3]==255)
|
Chris@4
|
335 {
|
Chris@4
|
336 int opt = 1;
|
Chris@4
|
337 setsockopt(a->socket, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(int));
|
Chris@4
|
338 }
|
Chris@4
|
339 }
|
Chris@4
|
340
|
Chris@4
|
341 }
|
Chris@4
|
342 #ifndef WIN32
|
Chris@4
|
343 else if (a->protocol == LO_UNIX) {
|
Chris@4
|
344 struct sockaddr_un sa;
|
Chris@4
|
345
|
Chris@4
|
346 a->socket = socket(PF_UNIX, SOCK_DGRAM, 0);
|
Chris@4
|
347 if (a->socket == -1) {
|
Chris@4
|
348 a->errnum = geterror();
|
Chris@4
|
349 a->errstr = NULL;
|
Chris@4
|
350 return -1;
|
Chris@4
|
351 }
|
Chris@4
|
352
|
Chris@4
|
353 sa.sun_family = AF_UNIX;
|
Chris@4
|
354 strncpy(sa.sun_path, a->port, sizeof(sa.sun_path)-1);
|
Chris@4
|
355
|
Chris@4
|
356 if ((connect(a->socket, (struct sockaddr *)&sa, sizeof(sa))) < 0) {
|
Chris@4
|
357 a->errnum = geterror();
|
Chris@4
|
358 a->errstr = NULL;
|
Chris@4
|
359 close(a->socket);
|
Chris@4
|
360 a->socket = -1;
|
Chris@4
|
361 return -1;
|
Chris@4
|
362 }
|
Chris@4
|
363 }
|
Chris@4
|
364 #endif
|
Chris@4
|
365 else {
|
Chris@4
|
366 /* unknown protocol */
|
Chris@4
|
367 return -2;
|
Chris@4
|
368 }
|
Chris@4
|
369
|
Chris@4
|
370 return 0;
|
Chris@4
|
371 }
|
Chris@4
|
372
|
Chris@4
|
373 static int send_data(lo_address a, lo_server from, char *data, const size_t data_len)
|
Chris@4
|
374 {
|
Chris@4
|
375 int ret=0;
|
Chris@4
|
376 int sock=-1;
|
Chris@4
|
377
|
Chris@4
|
378 #ifdef WIN32
|
Chris@4
|
379 if(!initWSock()) return -1;
|
Chris@4
|
380 #endif
|
Chris@4
|
381
|
Chris@4
|
382 if (data_len > LO_MAX_MSG_SIZE) {
|
Chris@4
|
383 a->errnum = 99;
|
Chris@4
|
384 a->errstr = "Attempted to send message in excess of maximum "
|
Chris@4
|
385 "message size";
|
Chris@4
|
386 return -1;
|
Chris@4
|
387 }
|
Chris@4
|
388
|
Chris@4
|
389 // Resolve the destination address, if not done already
|
Chris@4
|
390 if (!a->ai) {
|
Chris@4
|
391 ret = resolve_address( a );
|
Chris@4
|
392 if (ret) return ret;
|
Chris@4
|
393 }
|
Chris@4
|
394
|
Chris@4
|
395 // Re-use existing socket?
|
Chris@4
|
396 if (from) {
|
Chris@4
|
397 sock = from->sockets[0].fd;
|
Chris@4
|
398 } else if (a->protocol == LO_UDP && lo_client_sockets.udp!=-1) {
|
Chris@4
|
399 sock = lo_client_sockets.udp;
|
Chris@4
|
400 } else {
|
Chris@4
|
401 if (a->socket==-1) {
|
Chris@4
|
402 ret = create_socket( a );
|
Chris@4
|
403 if (ret) return ret;
|
Chris@4
|
404 }
|
Chris@4
|
405 sock = a->socket;
|
Chris@4
|
406 }
|
Chris@4
|
407
|
Chris@4
|
408
|
Chris@4
|
409
|
Chris@4
|
410 // Send Length of the following data
|
Chris@4
|
411 if (a->protocol == LO_TCP) {
|
Chris@4
|
412 int32_t size = htonl(data_len);
|
Chris@4
|
413 ret = send(sock, &size, sizeof(size), MSG_NOSIGNAL);
|
Chris@4
|
414 }
|
Chris@4
|
415
|
Chris@4
|
416 // Send the data
|
Chris@4
|
417 if (a->protocol == LO_UDP) {
|
Chris@4
|
418 if (a->ttl >= 0) {
|
Chris@4
|
419 unsigned char ttl = (unsigned char)a->ttl;
|
Chris@4
|
420 setsockopt(sock,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl));
|
Chris@4
|
421 }
|
Chris@4
|
422 ret = sendto(sock, data, data_len, MSG_NOSIGNAL,
|
Chris@4
|
423 a->ai->ai_addr, a->ai->ai_addrlen);
|
Chris@4
|
424 } else {
|
Chris@4
|
425 ret = send(sock, data, data_len, MSG_NOSIGNAL);
|
Chris@4
|
426 }
|
Chris@4
|
427
|
Chris@4
|
428 if (a->protocol == LO_TCP && ret == -1) {
|
Chris@4
|
429 close(a->socket);
|
Chris@4
|
430 a->socket=-1;
|
Chris@4
|
431 }
|
Chris@4
|
432
|
Chris@4
|
433 if (ret == -1) {
|
Chris@4
|
434 a->errnum = geterror();
|
Chris@4
|
435 a->errstr = NULL;
|
Chris@4
|
436 } else {
|
Chris@4
|
437 a->errnum = 0;
|
Chris@4
|
438 a->errstr = NULL;
|
Chris@4
|
439 }
|
Chris@4
|
440
|
Chris@4
|
441 return ret;
|
Chris@4
|
442 }
|
Chris@4
|
443
|
Chris@4
|
444
|
Chris@4
|
445 int lo_send_message(lo_address a, const char *path, lo_message msg)
|
Chris@4
|
446 {
|
Chris@4
|
447 return lo_send_message_from( a, NULL, path, msg );
|
Chris@4
|
448 }
|
Chris@4
|
449
|
Chris@4
|
450 int lo_send_message_from(lo_address a, lo_server from, const char *path, lo_message msg)
|
Chris@4
|
451 {
|
Chris@4
|
452 const size_t data_len = lo_message_length(msg, path);
|
Chris@4
|
453 char *data = lo_message_serialise(msg, path, NULL, NULL);
|
Chris@4
|
454
|
Chris@4
|
455 // Send the message
|
Chris@4
|
456 int ret = send_data( a, from, data, data_len );
|
Chris@4
|
457
|
Chris@4
|
458 // For TCP, retry once if it failed. The first try will return
|
Chris@4
|
459 // error if the connection was closed, so the second try will
|
Chris@4
|
460 // attempt to re-open the connection.
|
Chris@4
|
461 if (ret == -1 && a->protocol == LO_TCP)
|
Chris@4
|
462 ret = send_data( a, from, data, data_len );
|
Chris@4
|
463
|
Chris@4
|
464 // Free the memory allocated by lo_message_serialise
|
Chris@4
|
465 if (data) free( data );
|
Chris@4
|
466
|
Chris@4
|
467 return ret;
|
Chris@4
|
468 }
|
Chris@4
|
469
|
Chris@4
|
470
|
Chris@4
|
471 int lo_send_bundle(lo_address a, lo_bundle b)
|
Chris@4
|
472 {
|
Chris@4
|
473 return lo_send_bundle_from( a, NULL, b );
|
Chris@4
|
474 }
|
Chris@4
|
475
|
Chris@4
|
476
|
Chris@4
|
477 int lo_send_bundle_from(lo_address a, lo_server from, lo_bundle b)
|
Chris@4
|
478 {
|
Chris@4
|
479 const size_t data_len = lo_bundle_length(b);
|
Chris@4
|
480 char *data = lo_bundle_serialise(b, NULL, NULL);
|
Chris@4
|
481
|
Chris@4
|
482 // Send the bundle
|
Chris@4
|
483 int ret = send_data( a, from, data, data_len );
|
Chris@4
|
484
|
Chris@4
|
485 // Free the memory allocated by lo_bundle_serialise
|
Chris@4
|
486 if (data) free( data );
|
Chris@4
|
487
|
Chris@4
|
488 return ret;
|
Chris@4
|
489 }
|
Chris@4
|
490
|
Chris@4
|
491 /* vi:set ts=8 sts=4 sw=4: */
|