1 /*
2  * oscsend - Send OpenSound Control message.
3  *
4  * Copyright (C) 2008 Kentaro Fukuchi <kentaro@fukuchi.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it 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  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  * TODO:
21  * - support binary blob.
22  * - support TimeTag.
23  * - receive replies.
24  */
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <math.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <config.h>
33 #include <limits.h>
34 #include <lo/lo.h>
35 
usage(void)36 void usage(void)
37 {
38     printf("oscsend version %s\n"
39            "Copyright (C) 2008 Kentaro Fukuchi\n\n"
40            "Usage: oscsend hostname port address types values...\n"
41            "or     oscsend url address types values...\n"
42            "Send OpenSound Control message via UDP.\n\n"
43            "Description\n"
44            "hostname: specifies the remote host's name.\n"
45            "port    : specifies the port number to connect to the remote host.\n"
46            "url     : specifies the destination parameters using a liblo URL.\n"
47            "          e.g. UDP        \"osc.udp://localhost:9000\"\n"
48            "               Multicast  \"osc.udp://224.0.1.9:9000\"\n"
49            "               TCP        \"osc.tcp://localhost:9000\"\n"
50            "               File       \"file:///tmp/dump.osc\"\n"
51            "               stdout     -\n\n"
52 
53            "address : the OSC address where the message to be sent.\n"
54            "types   : specifies the types of the following values.\n",
55            VERSION);
56     printf("          %c - 32bit integer\n", LO_INT32);
57     printf("          %c - 64bit integer\n", LO_INT64);
58     printf("          %c - 32bit floating point number\n", LO_FLOAT);
59     printf("          %c - 64bit (double) floating point number\n",
60            LO_DOUBLE);
61     printf("          %c - string\n", LO_STRING);
62     printf("          %c - symbol\n", LO_SYMBOL);
63     printf("          %c - char\n", LO_CHAR);
64     printf("          %c - 4 byte midi packet (8 digits hexadecimal)\n",
65            LO_MIDI);
66     printf("          %c - TRUE (no value required)\n", LO_TRUE);
67     printf("          %c - FALSE (no value required)\n", LO_FALSE);
68     printf("          %c - NIL (no value required)\n", LO_NIL);
69     printf("          %c - INFINITUM (no value required)\n", LO_INFINITUM);
70     printf("values  : space separated values.\n\n"
71            "Example\n"
72            "$ oscsend localhost 7777 /sample/address %c%c%c%c 1 3.14 hello\n",
73            LO_INT32, LO_TRUE, LO_FLOAT, LO_STRING);
74 }
75 
create_message(char ** argv)76 lo_message create_message(char **argv)
77 {
78     /* Note:
79      * argv[0] <- types
80      * argv[1..] <- values
81      */
82     int i, argi;
83     lo_message message;
84     const char *types, *arg;
85     int values;
86 
87     message = lo_message_new();
88     if (argv[0] == NULL) {
89         /* empty message is allowed. */
90         values = 0;
91     } else {
92         types = argv[0];
93         values = strlen(types);
94     }
95 
96     argi = 1;
97     arg = NULL;
98     for (i = 0; i < values; i++) {
99 		switch(types[i]) {
100 		case LO_INT32:
101 		case LO_FLOAT:
102 		case LO_STRING:
103 		case LO_BLOB:
104 		case LO_INT64:
105 		case LO_TIMETAG:
106 		case LO_DOUBLE:
107 		case LO_SYMBOL:
108 		case LO_CHAR:
109 		case LO_MIDI:
110 			arg = argv[argi];
111 			if (arg == NULL) {
112 				fprintf(stderr, "Value #%d is not given.\n", i + 1);
113 				goto EXIT;
114 			}
115 			break;
116 		default:
117 			break;
118 		}
119         switch (types[i]) {
120         case LO_INT32:
121             {
122                 char *endp;
123                 int64_t v;
124 
125                 v = strtol(arg, &endp, 10);
126                 if (*endp != '\0') {
127                     fprintf(stderr, "An invalid value was given: '%s'\n",
128                             arg);
129                     goto EXIT;
130                 }
131                 if ((v == LONG_MAX || v == LONG_MIN) && errno == ERANGE) {
132                     fprintf(stderr, "Value out of range: '%s'\n", arg);
133                     goto EXIT;
134                 }
135                 if (v > INT_MAX || v < INT_MIN) {
136                     fprintf(stderr, "Value out of range: '%s'\n", arg);
137                     goto EXIT;
138                 }
139                 lo_message_add_int32(message, (int32_t) v);
140                 argi++;
141                 break;
142             }
143         case LO_INT64:
144             {
145                 char *endp;
146                 int64_t v;
147 
148                 v = strtoll(arg, &endp, 10);
149                 if (*endp != '\0') {
150                     fprintf(stderr, "An invalid value was given: '%s'\n",
151                             arg);
152                     goto EXIT;
153                 }
154                 if ((v == LONG_MAX || v == LONG_MIN) && errno == ERANGE) {
155                     fprintf(stderr, "Value out of range: '%s'\n", arg);
156                     goto EXIT;
157                 }
158                 lo_message_add_int64(message, v);
159                 argi++;
160                 break;
161             }
162         case LO_FLOAT:
163             {
164                 char *endp;
165                 float v;
166 
167 #ifdef __USE_ISOC99
168                 v = strtof(arg, &endp);
169 #else
170                 v = (float) strtod(arg, &endp);
171 #endif                          /* __USE_ISOC99 */
172                 if (*endp != '\0') {
173                     fprintf(stderr, "An invalid value was given: '%s'\n",
174                             arg);
175                     goto EXIT;
176                 }
177                 lo_message_add_float(message, v);
178                 argi++;
179                 break;
180             }
181         case LO_DOUBLE:
182             {
183                 char *endp;
184                 double v;
185 
186                 v = strtod(arg, &endp);
187                 if (*endp != '\0') {
188                     perror(NULL);
189                     fprintf(stderr, "An invalid value was given: '%s'\n",
190                             arg);
191                     goto EXIT;
192                 }
193                 lo_message_add_double(message, v);
194                 argi++;
195                 break;
196             }
197         case LO_STRING:
198             lo_message_add_string(message, arg);
199             argi++;
200             break;
201         case LO_SYMBOL:
202             lo_message_add_symbol(message, arg);
203             argi++;
204             break;
205         case LO_CHAR:
206             lo_message_add_char(message, arg[0]);
207             argi++;
208             break;
209         case LO_MIDI:
210             {
211                 unsigned int midi;
212                 uint8_t packet[4];
213                 int ret;
214 
215                 ret = sscanf(arg, "%08x", &midi);
216                 if (ret != 1) {
217                     fprintf(stderr,
218                             "An invalid hexadecimal value was given: '%s'\n",
219                             arg);
220                     goto EXIT;
221                 }
222                 packet[0] = (midi >> 24) & 0xff;
223                 packet[1] = (midi >> 16) & 0xff;
224                 packet[2] = (midi >> 8) & 0xff;
225                 packet[3] = midi & 0xff;
226                 lo_message_add_midi(message, packet);
227                 argi++;
228                 break;
229             }
230         case LO_TRUE:
231             lo_message_add_true(message);
232             break;
233         case LO_FALSE:
234             lo_message_add_false(message);
235             break;
236         case LO_NIL:
237             lo_message_add_nil(message);
238             break;
239         case LO_INFINITUM:
240             lo_message_add_infinitum(message);
241             break;
242         default:
243             fprintf(stderr, "Type '%c' is not supported or invalid.\n",
244                     types[i]);
245             goto EXIT;
246             break;
247         }
248     }
249 
250     return message;
251   EXIT:
252     lo_message_free(message);
253     return NULL;
254 }
255 
main(int argc,char ** argv)256 int main(int argc, char **argv)
257 {
258     lo_address target;
259     lo_message message;
260     int ret, i=1, dump_to_file=0, dump_to_stdout=0;
261     char file_uri[256];
262 
263     if (argc < 3) {
264         usage();
265         exit(1);
266     }
267 
268     if (argv[i] == NULL) {
269         fprintf(stderr, "No hostname is given.\n");
270         exit(1);
271     }
272 
273     if (strstr(argv[i], "://")!=0) {
274         if(strncmp(argv[i], "file://", 7)==0 && strlen(argv[i])>7) {
275             dump_to_file=1;
276             memset(file_uri, 0, sizeof(file_uri));
277             strncpy(file_uri, argv[i]+7, sizeof(file_uri)-1);
278         }
279         else {
280             target = lo_address_new_from_url(argv[i]);
281             if (target == NULL) {
282                 fprintf(stderr, "Failed to open %s\n", argv[i]);
283                 exit(1);
284             }
285         }
286         i++;
287     }
288     else if(strncmp(argv[i], "-", 1)==0 && strlen(argv[i])==1) {
289         dump_to_stdout=1;
290         i++;
291     }
292     else if (argv[i+1] == NULL) {
293         fprintf(stderr, "No port number is given.\n");
294         exit(1);
295     }
296     else {
297         target = lo_address_new(argv[i], argv[i+1]);
298         if (target == NULL) {
299             fprintf(stderr, "Failed to open %s:%s\n", argv[i], argv[i+1]);
300             exit(1);
301         }
302         i += 2;
303     }
304 
305     if (argv[i] == NULL) {
306         fprintf(stderr, "No path is given.\n");
307         exit(1);
308     }
309 
310     message = create_message(&argv[i+1]);
311     if (message == NULL) {
312         fprintf(stderr, "Failed to create OSC message.\n");
313         exit(1);
314     }
315 
316     if(!dump_to_file && !dump_to_stdout) {
317         lo_address_set_ttl(target, 1);
318 
319         ret = lo_send_message(target, argv[i], message);
320         if (ret == -1) {
321             fprintf(stderr, "An error occurred: %s\n",
322                     lo_address_errstr(target));
323             lo_message_free(message);
324             exit(1);
325         }
326     }
327     else {
328 	FILE *fout = NULL;
329 
330         if(dump_to_file) {
331             FILE *f=fopen(file_uri, "w+");
332             if(f == NULL) {
333                 fprintf(stderr, "An error occurred: Could not open file for writing OSC message: '%s'\n",
334                     file_uri);
335                 lo_message_free(message);
336                 exit(1);
337             }
338             fout=f;
339         }
340         else if(dump_to_stdout) {
341             fout=stdout;
342         }
343 
344         size_t size;
345         void *msg_ptr=lo_message_serialise(message, argv[i], NULL, &size);
346         fwrite(msg_ptr, 1, size, fout);
347         fflush(fout);
348         fclose(fout);
349         free(msg_ptr);
350     }
351 
352     lo_message_free(message);
353 
354     return 0;
355 }
356