1 /*
2 libspeechd.c - Shared library for easy acces to Speech Dispatcher functions
3 *
4 * Copyright (C) 2001, 2002, 2003, 2006, 2007, 2008 Brailcom, o.p.s.
5 *
6 * This is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1, or (at your option)
9 * any later version.
10 *
11 * This software 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 GNU
14 * 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, see <https://www.gnu.org/licenses/>.
18 *
19 * $Id: libspeechd.c,v 1.37 2008-12-23 09:15:32 pdm Exp $
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <sys/types.h>
27 #include <wchar.h>
28 #include <sys/socket.h>
29 #include <netinet/tcp.h>
30 #include <netinet/in.h>
31 #include <sys/un.h>
32 #include <arpa/inet.h>
33 #include <unistd.h>
34
35 #include <stdio.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <stdarg.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <glib.h>
42 #include <errno.h>
43 #include <assert.h>
44 #include <netdb.h>
45
46 /*
47 * This is needed because speechd_types.h is in a different location in
48 * the source tree's include directory than it will be when it is
49 * installed on the user's system.
50 */
51 #include <speechd_types.h>
52 #include <speechd_defines.h>
53 #include "libspeechd.h"
54
55 /* Comment/uncomment to switch debugging on/off */
56 // #define LIBSPEECHD_DEBUG 1
57
58 /* Unless there is an fatal error, it doesn't print anything */
59 #define SPD_FATAL(msg) { printf("Fatal error (libspeechd) [%s:%d]:"msg, __FILE__, __LINE__); fflush(stdout); exit(EXIT_FAILURE); }
60
61 /* -------------- Private functions headers ------------------------*/
62
63 #ifdef LIBSPEECHD_DEBUG
64 static FILE *spd_debug = NULL;
65 #endif
66
67 static int spd_set_priority(SPDConnection * connection, SPDPriority priority);
68 static char *escape_dot(const char *text);
69 static int isanum(char *str);
70 static char *get_reply(SPDConnection * connection);
71 static int get_err_code(char *reply);
72 static char *get_param_str(char *reply, int num, int *err);
73 static int get_param_int(char *reply, int num, int *err);
74 static int ret_ok(char *reply);
75 static void SPD_DBG(char *format, ...);
76 static void *spd_events_handler(void *);
77
78 const int range_low = -100;
79 const int range_high = 100;
80
81 pthread_mutex_t spd_logging_mutex;
82
83 struct SPDConnection_threaddata {
84 pthread_t events_thread;
85 pthread_cond_t cond_reply_ready;
86 pthread_mutex_t mutex_reply_ready;
87 pthread_cond_t cond_reply_ack;
88 pthread_mutex_t mutex_reply_ack;
89 };
90
91 /*
92 * Added by Willie Walker - strndup and getline were GNU libc extensions
93 * that were adopted in the POSIX.1-2008 standard, but are not yet found
94 * on all systems.
95 */
96 #ifndef HAVE_STRNDUP
strndup(const char * s,size_t n)97 char *strndup(const char *s, size_t n)
98 {
99 size_t nAvail;
100 char *p;
101
102 if (!s)
103 return 0;
104
105 if (strlen(s) > n)
106 nAvail = n + 1;
107 else
108 nAvail = strlen(s) + 1;
109 p = malloc(nAvail);
110 memcpy(p, s, nAvail);
111 p[nAvail - 1] = '\0';
112
113 return p;
114 }
115 #endif /* HAVE_STRNDUP */
116
117 #ifndef HAVE_GETLINE
118 #define BUFFER_LEN 256
getline(char ** lineptr,size_t * n,FILE * f)119 ssize_t getline(char **lineptr, size_t * n, FILE * f)
120 {
121 int ch;
122 size_t m = 0;
123 ssize_t buf_len = 0;
124 char *buf = NULL;
125 char *p = NULL;
126
127 if (errno != 0) {
128 SPD_DBG("getline: errno came in as %d!!!\n", errno);
129 errno = 0;
130 }
131 while ((ch = getc(f)) != EOF) {
132 if (errno != 0)
133 return -1;
134 if (m++ >= buf_len) {
135 buf_len += BUFFER_LEN;
136 buf = (char *)realloc(buf, buf_len + 1);
137 if (buf == NULL) {
138 SPD_DBG("buf==NULL");
139 return -1;
140 }
141 p = buf + buf_len - BUFFER_LEN;
142 }
143 *p = ch;
144 p++;
145 if (ch == '\n')
146 break;
147 }
148 if (m == 0) {
149 SPD_DBG("getline: m=%d!", m);
150 return -1;
151 } else {
152 *p = '\0';
153 *lineptr = buf;
154 *n = m;
155 return m;
156 }
157 }
158 #endif /* HAVE_GETLINE */
159
160 /* --------------------- Public functions ------------------------- */
161
162 #define SPD_REPLY_BUF_SIZE 65536
163
164 /* Determine address for the unix socket */
_get_default_unix_socket_name(void)165 static char *_get_default_unix_socket_name(void)
166 {
167 GString *socket_filename;
168 char *h;
169 const char *rundir = g_get_user_runtime_dir();
170 socket_filename = g_string_new("");
171 g_string_printf(socket_filename, "%s/speech-dispatcher/speechd.sock",
172 rundir);
173 // Do not return glib string, but glibc string...
174 h = strdup(socket_filename->str);
175 g_string_free(socket_filename, 1);
176 return h;
177 }
178
SPDConnectionAddress__free(SPDConnectionAddress * address)179 void SPDConnectionAddress__free(SPDConnectionAddress * address)
180 {
181 if (!address)
182 return;
183 free(address->unix_socket_name);
184 free(address->inet_socket_host);
185 free(address->dbus_bus);
186 free(address);
187 }
188
spd_get_default_address(char ** error)189 SPDConnectionAddress *spd_get_default_address(char **error)
190 {
191 const gchar *env_address = g_getenv("SPEECHD_ADDRESS");
192 gchar **pa; /* parsed address */
193 SPDConnectionAddress *address = malloc(sizeof(SPDConnectionAddress));
194 address->unix_socket_name = NULL;
195 address->inet_socket_host = NULL;
196 address->dbus_bus = NULL;
197
198 if (env_address == NULL) { // Default method = unix sockets
199 address->method = SPD_METHOD_UNIX_SOCKET;
200 address->unix_socket_name = _get_default_unix_socket_name();
201 } else {
202 pa = g_strsplit(env_address, ":", 0);
203 assert(pa);
204 if (!g_strcmp0(pa[0], "unix_socket") || pa[0] == NULL) { // Unix sockets
205 address->method = SPD_METHOD_UNIX_SOCKET;
206 if (pa[1] == NULL) {
207 address->unix_socket_name =
208 _get_default_unix_socket_name();
209 } else {
210 address->unix_socket_name = strdup(pa[1]);
211 }
212 } else if (!g_strcmp0(pa[0], "inet_socket")) { // Inet sockets
213 address->method = SPD_METHOD_INET_SOCKET;
214 if (pa[1] == NULL) {
215 address->inet_socket_host = strdup("127.0.0.1");
216 address->inet_socket_port = 6560;
217 } else {
218 address->inet_socket_host = strdup(pa[1]);
219 if (pa[2] == NULL) {
220 address->inet_socket_port =
221 SPEECHD_DEFAULT_PORT;
222 } else {
223 address->inet_socket_port = atoi(pa[2]);
224 }
225 }
226 } else { // Unknown or unsupported method requested
227 *error =
228 strdup
229 ("Unknown or unsupported communication method");
230 SPDConnectionAddress__free(address);
231 address = NULL;
232 }
233 g_strfreev(pa);
234 }
235 return address;
236 }
237
_init_debug(void)238 static void _init_debug(void)
239 {
240 #ifdef LIBSPEECHD_DEBUG
241 if (!spd_debug) {
242 spd_debug = fopen("/tmp/libspeechd.log", "w");
243 if (spd_debug == NULL)
244 SPD_FATAL("COULDN'T ACCES FILE INTENDED FOR DEBUG");
245
246 if (pthread_mutex_init(&spd_logging_mutex, NULL)) {
247 fprintf(stderr, "Mutex initialization failed");
248 fflush(stderr);
249 exit(1);
250 }
251 SPD_DBG("Debugging started");
252 }
253 #endif /* LIBSPEECHD_DEBUG */
254 }
255
256 /* Opens a new Speech Dispatcher connection.
257 * Returns socket file descriptor of the created connection
258 * or -1 if no connection was opened. */
259
spd_open(const char * client_name,const char * connection_name,const char * user_name,SPDConnectionMode mode)260 SPDConnection *spd_open(const char *client_name, const char *connection_name,
261 const char *user_name, SPDConnectionMode mode)
262 {
263 char *error;
264 int autospawn = 1;
265 SPDConnection *conn;
266 conn = spd_open2(client_name, connection_name, user_name,
267 mode, NULL, autospawn, &error);
268 if (!conn) {
269 _init_debug();
270 assert(error);
271 SPD_DBG("Could not connect to Speech Dispatcher: %s", error);
272 free(error);
273 }
274 return conn;
275 }
276
277 #define MAX_IP_SIZE 16+1
278 /* TODO: This only works in IPV4 */
resolve_host(char * host_name_or_ip,int * is_localhost,gchar ** error)279 static char *resolve_host(char *host_name_or_ip, int *is_localhost,
280 gchar ** error)
281 {
282 struct addrinfo *addr_result;
283 int err;
284 char *resolve_buffer = malloc(MAX_IP_SIZE * sizeof(char));
285 const char *resolved_ip = NULL;
286 char *ip;
287 *error = NULL;
288 struct sockaddr_in *addr_in;
289
290 if (resolve_buffer == NULL) {
291 *error = g_strdup("Failed to allocate memory.");
292 return NULL;
293 }
294
295 err = getaddrinfo(host_name_or_ip, 0, NULL, &addr_result);
296 if (err) {
297 *error =
298 g_strdup_printf("Can't resolve address %d due to error %s:",
299 err, gai_strerror(err));
300 free(resolve_buffer);
301 return NULL;
302 }
303 /* Take the first address returned as we are only interested in host ip */
304 addr_in = (struct sockaddr_in *)addr_result->ai_addr;
305 resolved_ip =
306 inet_ntop(AF_INET, &(addr_in->sin_addr.s_addr), resolve_buffer,
307 MAX_IP_SIZE);
308 if (resolved_ip == NULL) {
309 *error =
310 g_strdup_printf
311 ("Could not convert address, due to the following error: %s",
312 strerror(errno));
313 freeaddrinfo(addr_result);
314 free(resolve_buffer);
315 return NULL;
316 }
317
318 if (!strncmp(resolved_ip, "127.", 4)) {
319 *is_localhost = 1;
320 /* In case of local addresses, use 127.0.0.1 which is guaranteed
321 to be local and the server listens on it */
322 free(resolve_buffer);
323 ip = strdup("127.0.0.1");
324 } else {
325 *is_localhost = 0;
326 ip = resolve_buffer;
327 }
328 freeaddrinfo(addr_result);
329 return ip;
330 }
331
332 static int
spawn_server(SPDConnectionAddress * address,int is_localhost,gchar ** spawn_error)333 spawn_server(SPDConnectionAddress * address, int is_localhost,
334 gchar ** spawn_error)
335 {
336 gchar *speechd_cmd[16];
337 gchar *stderr_output;
338 gboolean spawn_ok;
339 GError *gerror = NULL;
340 int exit_status;
341 int i;
342
343 if ((address->method == SPD_METHOD_INET_SOCKET) && (!is_localhost)) {
344 *spawn_error =
345 g_strdup
346 ("Spawn failed, the given network address doesn't seem to be on localhost");
347 return 1;
348 }
349
350 speechd_cmd[0] = g_strdup(SPD_SPAWN_CMD);
351 speechd_cmd[1] = g_strdup("--spawn");
352 speechd_cmd[2] = g_strdup("--communication-method");
353 if (address->method == SPD_METHOD_INET_SOCKET) {
354 speechd_cmd[3] = g_strdup("inet_socket");
355 speechd_cmd[4] = g_strdup("--port");
356 speechd_cmd[5] =
357 g_strdup_printf("%d", address->inet_socket_port);
358 speechd_cmd[6] = NULL;
359 } else if (address->method == SPD_METHOD_UNIX_SOCKET) {
360 speechd_cmd[3] = g_strdup("unix_socket");
361 speechd_cmd[4] = g_strdup("--socket-path");
362 speechd_cmd[5] =
363 g_strdup_printf("%s", address->unix_socket_name);
364 speechd_cmd[6] = NULL;
365 } else
366 assert(0);
367
368 spawn_ok =
369 g_spawn_sync(NULL, (gchar **) speechd_cmd, NULL,
370 G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL, NULL,
371 NULL, NULL, &stderr_output, &exit_status, &gerror);
372 for (i = 0; speechd_cmd[i] != NULL; i++)
373 g_free(speechd_cmd[i]);
374 if (!spawn_ok) {
375 *spawn_error =
376 g_strdup_printf("Autospawn failed. Spawn error %d: %s",
377 gerror->code, gerror->message);
378 return 1;
379 } else {
380 if (exit_status) {
381 *spawn_error =
382 g_strdup_printf
383 ("Autospawn failed. Speech Dispatcher refused to start with error code, "
384 "stating this as a reason: %s", stderr_output);
385 return 1;
386 } else {
387 *spawn_error = NULL;
388 return 0;
389 }
390 }
391 assert(0);
392 }
393
spd_open2(const char * client_name,const char * connection_name,const char * user_name,SPDConnectionMode mode,SPDConnectionAddress * address,int autospawn,char ** error_result)394 SPDConnection *spd_open2(const char *client_name, const char *connection_name,
395 const char *user_name, SPDConnectionMode mode,
396 SPDConnectionAddress * address, int autospawn,
397 char **error_result)
398 {
399 SPDConnection *connection = NULL;
400 SPDConnectionAddress *defaultAddress = NULL;
401 char *set_client_name = NULL;
402 char *conn_name = NULL;
403 char *usr_name = NULL;
404 int ret;
405 int tcp_no_delay = 1;
406
407 /* Autospawn related */
408 int spawn_err;
409 gchar *spawn_report;
410 char *host_ip;
411 int is_localhost = 1;
412
413 struct sockaddr_in address_inet;
414 struct sockaddr_un address_unix;
415 struct sockaddr *sock_address;
416 size_t sock_address_len;
417
418 _init_debug();
419
420 if (client_name == NULL) {
421 *error_result = strdup("ERROR: Client name not specified");
422 SPD_DBG(*error_result);
423 return NULL;
424 }
425
426 if (user_name == NULL) {
427 usr_name = strdup((char *)g_get_user_name());
428 } else
429 usr_name = strdup(user_name);
430
431 if (connection_name == NULL)
432 conn_name = strdup("main");
433 else
434 conn_name = strdup(connection_name);
435
436 if (address == NULL) {
437 char *err = NULL;
438 defaultAddress = spd_get_default_address(&err);
439 address = defaultAddress;
440 if (!address) {
441 assert(err);
442 *error_result = err;
443 SPD_DBG(*error_result);
444 goto out;
445 }
446 }
447
448 /* Connect to server using the selected method */
449 connection = malloc(sizeof(SPDConnection));
450 if (address->method == SPD_METHOD_INET_SOCKET) {
451 gchar *resolve_error;
452 host_ip =
453 resolve_host(address->inet_socket_host, &is_localhost,
454 &resolve_error);
455 if (host_ip == NULL) {
456 *error_result = strdup(resolve_error);
457 g_free(resolve_error);
458 free(connection);
459 connection = NULL;
460 goto out;
461 }
462 address_inet.sin_addr.s_addr = inet_addr(host_ip);
463 free(host_ip);
464 address_inet.sin_port = htons(address->inet_socket_port);
465 address_inet.sin_family = AF_INET;
466 connection->socket = socket(AF_INET, SOCK_STREAM, 0);
467 sock_address = (struct sockaddr *)&address_inet;
468 sock_address_len = sizeof(address_inet);
469 } else if (address->method == SPD_METHOD_UNIX_SOCKET) {
470 /* Create the unix socket */
471 address_unix.sun_family = AF_UNIX;
472 strncpy(address_unix.sun_path, address->unix_socket_name,
473 sizeof(address_unix.sun_path));
474 address_unix.sun_path[sizeof(address_unix.sun_path) - 1] = '\0';
475 connection->socket = socket(AF_UNIX, SOCK_STREAM, 0);
476 sock_address = (struct sockaddr *)&address_unix;
477 sock_address_len = SUN_LEN(&address_unix);
478 } else
479 SPD_FATAL("Unsupported connection method for spd_open2()");
480
481 if (connection->socket < 0) {
482 free(connection);
483 connection = NULL;
484 goto out;
485 }
486 ret = connect(connection->socket, sock_address, sock_address_len);
487 if (ret == -1) {
488 /* Suppose server might not be running, try to autospawn (autostart) it */
489 if (autospawn) {
490 spawn_err =
491 spawn_server(address, is_localhost, &spawn_report);
492 if (!spawn_err)
493 spawn_report =
494 g_strdup("Server successfully autospawned");
495 ret =
496 connect(connection->socket, sock_address,
497 sock_address_len);
498 } else {
499 spawn_report = g_strdup("Autospawn disabled");
500 }
501 if (ret == -1) {
502 if (address->method == SPD_METHOD_INET_SOCKET)
503 *error_result =
504 g_strdup_printf
505 ("Error: Can't connect to %s on port %d using inet sockets: %s. "
506 "Autospawn: %s", address->inet_socket_host,
507 address->inet_socket_port, strerror(errno),
508 spawn_report);
509 else if (address->method == SPD_METHOD_UNIX_SOCKET)
510 *error_result =
511 g_strdup_printf
512 ("Error: Can't connect to unix socket %s: %s. Autospawn: %s",
513 address->unix_socket_name, strerror(errno),
514 spawn_report);
515 else
516 assert(0);
517 SPD_DBG(*error_result);
518 close(connection->socket);
519 free(connection);
520 connection = NULL;
521 goto out;
522 }
523 g_free(spawn_report);
524 }
525
526 if (address->method == SPD_METHOD_INET_SOCKET)
527 setsockopt(connection->socket, IPPROTO_TCP, TCP_NODELAY,
528 &tcp_no_delay, sizeof(int));
529
530 connection->callback_begin = NULL;
531 connection->callback_end = NULL;
532 connection->callback_im = NULL;
533 connection->callback_pause = NULL;
534 connection->callback_resume = NULL;
535 connection->callback_cancel = NULL;
536
537 connection->mode = mode;
538
539 /* Create a stream from the socket */
540 connection->stream = fdopen(connection->socket, "r");
541 if (!connection->stream)
542 SPD_FATAL("Can't create a stream for socket, fdopen() failed.");
543 /* Switch to line buffering mode */
544 ret = setvbuf(connection->stream, NULL, _IONBF, SPD_REPLY_BUF_SIZE);
545 if (ret)
546 SPD_FATAL("Can't set buffering, setvbuf failed.");
547
548 pthread_mutex_init(&connection->ssip_mutex, NULL);
549
550 if (mode == SPD_MODE_THREADED) {
551 SPD_DBG
552 ("Initializing threads, condition variables and mutexes...");
553 connection->td = malloc(sizeof(*connection->td));
554 pthread_cond_init(&connection->td->cond_reply_ready, NULL);
555 pthread_mutex_init(&connection->td->mutex_reply_ready, NULL);
556 pthread_cond_init(&connection->td->cond_reply_ack, NULL);
557 pthread_mutex_init(&connection->td->mutex_reply_ack, NULL);
558 ret =
559 pthread_create(&connection->td->events_thread, NULL,
560 spd_events_handler, connection);
561 if (ret != 0) {
562 *error_result = strdup("Thread initialization failed");
563 SPD_DBG(*error_result);
564 fclose(connection->stream);
565 close(connection->socket);
566 free(connection);
567 connection = NULL;
568 goto out;
569 }
570 }
571
572 /* By now, the connection is created and operational */
573 set_client_name =
574 g_strdup_printf("SET SELF CLIENT_NAME \"%s:%s:%s\"", usr_name,
575 client_name, conn_name);
576 ret = spd_execute_command_wo_mutex(connection, set_client_name);
577
578 out:
579 free(usr_name);
580 free(conn_name);
581 free(set_client_name);
582 SPDConnectionAddress__free(defaultAddress);
583 return connection;
584 }
585
586 #define RET(r) \
587 { \
588 pthread_mutex_unlock(&connection->ssip_mutex); \
589 return r; \
590 }
591
592 /* Get the client id of the connection */
spd_get_client_id(SPDConnection * connection)593 int spd_get_client_id(SPDConnection * connection)
594 {
595 int ret;
596 int err;
597 char *reply = NULL;
598 spd_execute_command_with_reply(connection, "HISTORY GET CLIENT_ID", &reply);
599 ret = get_param_int(reply, 1, &err);
600 free(reply);
601 return ret;
602 }
603
604 /* Close a Speech Dispatcher connection */
spd_close(SPDConnection * connection)605 void spd_close(SPDConnection * connection)
606 {
607
608 pthread_mutex_lock(&connection->ssip_mutex);
609
610 if (connection->mode == SPD_MODE_THREADED) {
611 pthread_cancel(connection->td->events_thread);
612 pthread_mutex_destroy(&connection->td->mutex_reply_ready);
613 pthread_mutex_destroy(&connection->td->mutex_reply_ack);
614 pthread_cond_destroy(&connection->td->cond_reply_ready);
615 pthread_cond_destroy(&connection->td->cond_reply_ack);
616 pthread_join(connection->td->events_thread, NULL);
617 connection->mode = SPD_MODE_SINGLE;
618 free(connection->td);
619 }
620
621 /* close the socket */
622 close(connection->socket);
623
624 pthread_mutex_unlock(&connection->ssip_mutex);
625
626 pthread_mutex_destroy(&connection->ssip_mutex);
627 free(connection);
628 }
629
630 /* Helper functions for spd_say. */
631 static inline int
spd_say_prepare(SPDConnection * connection,SPDPriority priority,const char * text,char ** escaped_text)632 spd_say_prepare(SPDConnection * connection, SPDPriority priority,
633 const char *text, char **escaped_text)
634 {
635 int ret = 0;
636
637 SPD_DBG("Text to say is: %s", text);
638
639 /* Insure that there is no escape sequence in the text */
640 *escaped_text = escape_dot(text);
641 /* Caller is now responsible for escaped_text. */
642 if (*escaped_text == NULL) { /* Out of memory. */
643 SPD_DBG("spd_say could not allocate memory.");
644 ret = -1;
645 } else {
646 /* Set priority */
647 SPD_DBG("Setting priority");
648 ret = spd_set_priority(connection, priority);
649 if (!ret) {
650 /* Start the data flow */
651 SPD_DBG("Sending SPEAK");
652 ret = spd_execute_command_wo_mutex(connection, "speak");
653 if (ret) {
654 SPD_DBG("Error: Can't start data flow!");
655 }
656 }
657 }
658
659 return ret;
660 }
661
spd_say_sending(SPDConnection * connection,const char * text)662 static inline int spd_say_sending(SPDConnection * connection, const char *text)
663 {
664 int msg_id = -1;
665 int err = 0;
666 char *reply = NULL;
667 char *pret = NULL;
668
669 /* Send data */
670 SPD_DBG("Sending data");
671 pret = spd_send_data_wo_mutex(connection, text, SPD_NO_REPLY);
672 if (pret == NULL) {
673 SPD_DBG("Can't send data wo mutex");
674 } else {
675 /* Terminate data flow */
676 SPD_DBG("Terminating data flow");
677 err =
678 spd_execute_command_with_reply(connection, "\r\n.", &reply);
679 if (err) {
680 SPD_DBG("Can't terminate data flow");
681 } else {
682 msg_id = get_param_int(reply, 1, &err);
683 if (err < 0) {
684 SPD_DBG
685 ("Can't determine SSIP message unique ID parameter.");
686 msg_id = -1;
687 }
688 }
689 }
690
691 free(reply);
692 free(pret);
693 return msg_id;
694 }
695
696 /* Say TEXT with priority PRIORITY.
697 * Returns msg_uid on success, -1 otherwise. */
spd_say(SPDConnection * connection,SPDPriority priority,const char * text)698 int spd_say(SPDConnection * connection, SPDPriority priority, const char *text)
699 {
700 char *escaped_text = NULL;
701 int msg_id = -1;
702 int prepare_failed = 0;
703
704 if (text != NULL) {
705 pthread_mutex_lock(&connection->ssip_mutex);
706
707 prepare_failed =
708 spd_say_prepare(connection, priority, text, &escaped_text);
709 if (!prepare_failed)
710 msg_id = spd_say_sending(connection, escaped_text);
711
712 free(escaped_text);
713 pthread_mutex_unlock(&connection->ssip_mutex);
714 } else {
715 SPD_DBG("spd_say called with a NULL argument for <text>");
716 }
717
718 SPD_DBG("Returning from spd_say");
719 return msg_id;
720 }
721
722 /* The same as spd_say, accepts also formated strings */
723 int
spd_sayf(SPDConnection * connection,SPDPriority priority,const char * format,...)724 spd_sayf(SPDConnection * connection, SPDPriority priority, const char *format,
725 ...)
726 {
727 static int ret;
728 va_list args;
729 char *buf;
730
731 if (format == NULL)
732 return -1;
733
734 /* Print the text to buffer */
735 va_start(args, format);
736 buf = g_strdup_vprintf(format, args);
737 va_end(args);
738
739 /* Send the buffer to Speech Dispatcher */
740 ret = spd_say(connection, priority, buf);
741 free(buf);
742
743 return ret;
744 }
745
spd_stop(SPDConnection * connection)746 int spd_stop(SPDConnection * connection)
747 {
748 return spd_execute_command(connection, "STOP SELF");
749 }
750
spd_stop_all(SPDConnection * connection)751 int spd_stop_all(SPDConnection * connection)
752 {
753 return spd_execute_command(connection, "STOP ALL");
754 }
755
spd_stop_uid(SPDConnection * connection,int target_uid)756 int spd_stop_uid(SPDConnection * connection, int target_uid)
757 {
758 static char command[16];
759
760 sprintf(command, "STOP %d", target_uid);
761 return spd_execute_command(connection, command);
762 }
763
spd_cancel(SPDConnection * connection)764 int spd_cancel(SPDConnection * connection)
765 {
766 return spd_execute_command(connection, "CANCEL SELF");
767 }
768
spd_cancel_all(SPDConnection * connection)769 int spd_cancel_all(SPDConnection * connection)
770 {
771 return spd_execute_command(connection, "CANCEL ALL");
772 }
773
spd_cancel_uid(SPDConnection * connection,int target_uid)774 int spd_cancel_uid(SPDConnection * connection, int target_uid)
775 {
776 static char command[16];
777
778 sprintf(command, "CANCEL %d", target_uid);
779 return spd_execute_command(connection, command);
780 }
781
spd_pause(SPDConnection * connection)782 int spd_pause(SPDConnection * connection)
783 {
784 return spd_execute_command(connection, "PAUSE SELF");
785 }
786
spd_pause_all(SPDConnection * connection)787 int spd_pause_all(SPDConnection * connection)
788 {
789 return spd_execute_command(connection, "PAUSE ALL");
790 }
791
spd_pause_uid(SPDConnection * connection,int target_uid)792 int spd_pause_uid(SPDConnection * connection, int target_uid)
793 {
794 char command[16];
795
796 sprintf(command, "PAUSE %d", target_uid);
797 return spd_execute_command(connection, command);
798 }
799
spd_resume(SPDConnection * connection)800 int spd_resume(SPDConnection * connection)
801 {
802 return spd_execute_command(connection, "RESUME SELF");
803 }
804
spd_resume_all(SPDConnection * connection)805 int spd_resume_all(SPDConnection * connection)
806 {
807 return spd_execute_command(connection, "RESUME ALL");
808 }
809
spd_resume_uid(SPDConnection * connection,int target_uid)810 int spd_resume_uid(SPDConnection * connection, int target_uid)
811 {
812 static char command[16];
813
814 sprintf(command, "RESUME %d", target_uid);
815 return spd_execute_command(connection, command);
816 }
817
818 int
spd_key(SPDConnection * connection,SPDPriority priority,const char * key_name)819 spd_key(SPDConnection * connection, SPDPriority priority, const char *key_name)
820 {
821 char *command_key;
822 int ret;
823
824 if (key_name == NULL)
825 return -1;
826
827 pthread_mutex_lock(&connection->ssip_mutex);
828
829 ret = spd_set_priority(connection, priority);
830 if (ret)
831 RET(-1);
832
833 command_key = g_strdup_printf("KEY %s", key_name);
834 ret = spd_execute_command_wo_mutex(connection, command_key);
835 free(command_key);
836 if (ret)
837 RET(-1);
838
839 pthread_mutex_unlock(&connection->ssip_mutex);
840
841 return 0;
842 }
843
844 int
spd_char(SPDConnection * connection,SPDPriority priority,const char * character)845 spd_char(SPDConnection * connection, SPDPriority priority,
846 const char *character)
847 {
848 static char command[16];
849 int ret;
850
851 if (character == NULL)
852 return -1;
853 if (strlen(character) > 6)
854 return -1;
855
856 pthread_mutex_lock(&connection->ssip_mutex);
857
858 ret = spd_set_priority(connection, priority);
859 if (ret)
860 RET(-1);
861
862 sprintf(command, "CHAR %s", character);
863 ret = spd_execute_command_wo_mutex(connection, command);
864 if (ret)
865 RET(-1);
866
867 pthread_mutex_unlock(&connection->ssip_mutex);
868
869 return 0;
870 }
871
872 int
spd_wchar(SPDConnection * connection,SPDPriority priority,wchar_t wcharacter)873 spd_wchar(SPDConnection * connection, SPDPriority priority, wchar_t wcharacter)
874 {
875 static char command[16];
876 char character[8];
877 int ret;
878
879 pthread_mutex_lock(&connection->ssip_mutex);
880
881 ret = wcrtomb(character, wcharacter, NULL);
882 if (ret <= 0)
883 RET(-1);
884
885 ret = spd_set_priority(connection, priority);
886 if (ret)
887 RET(-1);
888
889 assert(character != NULL);
890 sprintf(command, "CHAR %s", character);
891 ret = spd_execute_command_wo_mutex(connection, command);
892 if (ret)
893 RET(-1);
894
895 pthread_mutex_unlock(&connection->ssip_mutex);
896
897 return 0;
898 }
899
900 int
spd_sound_icon(SPDConnection * connection,SPDPriority priority,const char * icon_name)901 spd_sound_icon(SPDConnection * connection, SPDPriority priority,
902 const char *icon_name)
903 {
904 char *command;
905 int ret;
906
907 if (icon_name == NULL)
908 return -1;
909
910 pthread_mutex_lock(&connection->ssip_mutex);
911
912 ret = spd_set_priority(connection, priority);
913 if (ret)
914 RET(-1);
915
916 command = g_strdup_printf("SOUND_ICON %s", icon_name);
917 ret = spd_execute_command_wo_mutex(connection, command);
918 free(command);
919 if (ret)
920 RET(-1);
921
922 pthread_mutex_unlock(&connection->ssip_mutex);
923
924 return 0;
925 }
926
927 // Set functions for Punctuation
spd_w_set_punctuation(SPDConnection * connection,SPDPunctuation type,const char * who)928 int spd_w_set_punctuation(SPDConnection * connection, SPDPunctuation type,
929 const char *who)
930 {
931 char command[32];
932 int ret;
933
934 if (type == SPD_PUNCT_ALL)
935 sprintf(command, "SET %s PUNCTUATION all", who);
936 if (type == SPD_PUNCT_NONE)
937 sprintf(command, "SET %s PUNCTUATION none", who);
938 if (type == SPD_PUNCT_SOME)
939 sprintf(command, "SET %s PUNCTUATION some", who);
940 if (type == SPD_PUNCT_MOST)
941 sprintf(command, "SET %s PUNCTUATION most", who);
942
943 ret = spd_execute_command(connection, command);
944
945 return ret;
946 }
947
spd_set_punctuation(SPDConnection * connection,SPDPunctuation type)948 int spd_set_punctuation(SPDConnection * connection, SPDPunctuation type)
949 {
950 return spd_w_set_punctuation(connection, type, SPD_SELF);
951 }
952
spd_set_punctuation_all(SPDConnection * connection,SPDPunctuation type)953 int spd_set_punctuation_all(SPDConnection * connection, SPDPunctuation type)
954 {
955 return spd_w_set_punctuation(connection, type, SPD_ALLCLIENTS);
956 }
957
spd_set_punctuation_uid(SPDConnection * connection,SPDPunctuation type,unsigned int uid)958 int spd_set_punctuation_uid(SPDConnection * connection, SPDPunctuation type,
959 unsigned int uid)
960 {
961 char who[8];
962 sprintf(who, "%d", uid);
963 return spd_w_set_punctuation(connection, type, who);
964 }
965
966 // Set functions for Capital Letters
spd_w_set_capital_letters(SPDConnection * connection,SPDCapitalLetters type,const char * who)967 int spd_w_set_capital_letters(SPDConnection * connection,
968 SPDCapitalLetters type, const char *who)
969 {
970 char command[64];
971 int ret;
972
973 if (type == SPD_CAP_NONE)
974 sprintf(command, "SET %s CAP_LET_RECOGN none", who);
975 if (type == SPD_CAP_SPELL)
976 sprintf(command, "SET %s CAP_LET_RECOGN spell", who);
977 if (type == SPD_CAP_ICON)
978 sprintf(command, "SET %s CAP_LET_RECOGN icon", who);
979
980 ret = spd_execute_command(connection, command);
981
982 return ret;
983 }
984
spd_set_capital_letters(SPDConnection * connection,SPDCapitalLetters type)985 int spd_set_capital_letters(SPDConnection * connection, SPDCapitalLetters type)
986 {
987 return spd_w_set_capital_letters(connection, type, SPD_SELF);
988 }
989
spd_set_capital_letters_all(SPDConnection * connection,SPDCapitalLetters type)990 int spd_set_capital_letters_all(SPDConnection * connection,
991 SPDCapitalLetters type)
992 {
993 return spd_w_set_capital_letters(connection, type, SPD_ALLCLIENTS);
994 }
995
spd_set_capital_letters_uid(SPDConnection * connection,SPDCapitalLetters type,unsigned int uid)996 int spd_set_capital_letters_uid(SPDConnection * connection,
997 SPDCapitalLetters type, unsigned int uid)
998 {
999 char who[8];
1000 sprintf(who, "%d", uid);
1001 return spd_w_set_capital_letters(connection, type, who);
1002 }
1003
1004 // Set functions for Spelling
spd_w_set_spelling(SPDConnection * connection,SPDSpelling type,const char * who)1005 int spd_w_set_spelling(SPDConnection * connection, SPDSpelling type,
1006 const char *who)
1007 {
1008 char command[32];
1009 int ret;
1010
1011 if (type == SPD_SPELL_ON)
1012 sprintf(command, "SET %s SPELLING on", who);
1013 if (type == SPD_SPELL_OFF)
1014 sprintf(command, "SET %s SPELLING off", who);
1015
1016 ret = spd_execute_command(connection, command);
1017
1018 return ret;
1019 }
1020
spd_set_spelling(SPDConnection * connection,SPDSpelling type)1021 int spd_set_spelling(SPDConnection * connection, SPDSpelling type)
1022 {
1023 return spd_w_set_spelling(connection, type, SPD_SELF);
1024 }
1025
spd_set_spelling_all(SPDConnection * connection,SPDSpelling type)1026 int spd_set_spelling_all(SPDConnection * connection, SPDSpelling type)
1027 {
1028 return spd_w_set_spelling(connection, type, SPD_ALLCLIENTS);
1029 }
1030
spd_set_spelling_uid(SPDConnection * connection,SPDSpelling type,unsigned int uid)1031 int spd_set_spelling_uid(SPDConnection * connection, SPDSpelling type,
1032 unsigned int uid)
1033 {
1034 char who[8];
1035 sprintf(who, "%d", uid);
1036 return spd_w_set_spelling(connection, type, who);
1037 }
1038
spd_set_data_mode(SPDConnection * connection,SPDDataMode mode)1039 int spd_set_data_mode(SPDConnection * connection, SPDDataMode mode)
1040 {
1041 char command[32];
1042 int ret;
1043
1044 if (mode == SPD_DATA_TEXT)
1045 sprintf(command, "SET SELF SSML_MODE off");
1046 if (mode == SPD_DATA_SSML)
1047 sprintf(command, "SET SELF SSML_MODE on");
1048
1049 ret = spd_execute_command(connection, command);
1050
1051 return ret;
1052 }
1053
1054 // Set functions for Voice type
spd_w_set_voice_type(SPDConnection * connection,SPDVoiceType type,const char * who)1055 int spd_w_set_voice_type(SPDConnection * connection, SPDVoiceType type,
1056 const char *who)
1057 {
1058 static char command[64];
1059
1060 switch (type) {
1061 case SPD_MALE1:
1062 sprintf(command, "SET %s VOICE_TYPE MALE1", who);
1063 break;
1064 case SPD_MALE2:
1065 sprintf(command, "SET %s VOICE_TYPE MALE2", who);
1066 break;
1067 case SPD_MALE3:
1068 sprintf(command, "SET %s VOICE_TYPE MALE3", who);
1069 break;
1070 case SPD_FEMALE1:
1071 sprintf(command, "SET %s VOICE_TYPE FEMALE1", who);
1072 break;
1073 case SPD_FEMALE2:
1074 sprintf(command, "SET %s VOICE_TYPE FEMALE2", who);
1075 break;
1076 case SPD_FEMALE3:
1077 sprintf(command, "SET %s VOICE_TYPE FEMALE3", who);
1078 break;
1079 case SPD_CHILD_MALE:
1080 sprintf(command, "SET %s VOICE_TYPE CHILD_MALE", who);
1081 break;
1082 case SPD_CHILD_FEMALE:
1083 sprintf(command, "SET %s VOICE_TYPE CHILD_FEMALE", who);
1084 break;
1085 default:
1086 return -1;
1087 }
1088
1089 return spd_execute_command(connection, command);
1090 }
1091
spd_set_voice_type(SPDConnection * connection,SPDVoiceType type)1092 int spd_set_voice_type(SPDConnection * connection, SPDVoiceType type)
1093 {
1094 return spd_w_set_voice_type(connection, type, SPD_SELF);
1095 }
1096
spd_set_voice_type_all(SPDConnection * connection,SPDVoiceType type)1097 int spd_set_voice_type_all(SPDConnection * connection, SPDVoiceType type)
1098 {
1099 return spd_w_set_voice_type(connection, type, SPD_ALLCLIENTS);
1100 }
1101
spd_set_voice_type_uid(SPDConnection * connection,SPDVoiceType type,unsigned int uid)1102 int spd_set_voice_type_uid(SPDConnection * connection, SPDVoiceType type,
1103 unsigned int uid)
1104 {
1105 char who[8];
1106 sprintf(who, "%d", uid);
1107 return spd_w_set_voice_type(connection, type, who);
1108 }
1109
1110 // Set function for Voice type
spd_get_voice_type(SPDConnection * connection)1111 SPDVoiceType spd_get_voice_type(SPDConnection * connection)
1112 {
1113 char *command;
1114 SPDVoiceType ret;
1115 int err;
1116 char *reply = NULL;
1117 command = g_strdup_printf("GET voice_type");
1118 spd_execute_command_with_reply(connection, command, &reply);
1119 free(command);
1120 ret = get_param_int(reply, 1, &err);
1121 free(reply);
1122 return ret;
1123 }
1124
spd_w_set_command_int(SPDConnection * connection,const char * ssip_name,signed int val,const char * who)1125 static int spd_w_set_command_int(SPDConnection * connection,
1126 const char *ssip_name, signed int val,
1127 const char *who)
1128 {
1129 static char command[64];
1130 // NOTE: if any new int ssip_name are added that don't use -100 - 100 as their
1131 // range, these values will need to become parameters (or the new ssip_name)
1132 // methods will need to call a different helper method.
1133 if (val < range_low || val > range_high)
1134 return -1;
1135 sprintf(command, "SET %s %s %d", who, ssip_name, val);
1136 return spd_execute_command(connection, command);
1137 }
1138
spd_get_command_int(SPDConnection * connection,const char * ssip_name)1139 static int spd_get_command_int(SPDConnection * connection,
1140 const char *ssip_name)
1141 {
1142 char *command;
1143 int ret = 0;
1144 int err;
1145 char *reply = NULL;
1146 command = g_strdup_printf("GET %s", ssip_name);
1147 spd_execute_command_with_reply(connection, command, &reply);
1148 free(command);
1149 ret = get_param_int(reply, 1, &err);
1150 free(reply);
1151 return ret;
1152 }
1153
spd_w_set_command_str(SPDConnection * connection,const char * ssip_name,const char * str,const char * who)1154 static int spd_w_set_command_str(SPDConnection * connection,
1155 const char *ssip_name, const char *str,
1156 const char *who)
1157 {
1158 char *command;
1159 int ret;
1160 if (str == NULL)
1161 return -1;
1162 command = g_strdup_printf("SET %s %s %s", who, ssip_name, str);
1163 ret = spd_execute_command(connection, command);
1164 free(command);
1165 return ret;
1166 }
1167
spd_get_command_str(SPDConnection * connection,const char * ssip_name)1168 static char *spd_get_command_str(SPDConnection * connection,
1169 const char *ssip_name)
1170 {
1171 char *command;
1172 char *ret = NULL;
1173 int err;
1174 char *reply = NULL;
1175 command = g_strdup_printf("GET %s", ssip_name);
1176 spd_execute_command_with_reply(connection, command, &reply);
1177 free(command);
1178 ret = get_param_str(reply, 1, &err);
1179 free(reply);
1180 return ret;
1181 }
1182
1183 // Set functions for rate
spd_set_voice_rate(SPDConnection * connection,signed int rate)1184 int spd_set_voice_rate(SPDConnection * connection, signed int rate)
1185 {
1186 return spd_w_set_command_int(connection, SPD_RATE, rate, SPD_SELF);
1187 }
1188
spd_set_voice_rate_all(SPDConnection * connection,signed int rate)1189 int spd_set_voice_rate_all(SPDConnection * connection, signed int rate)
1190 {
1191 return spd_w_set_command_int(connection, SPD_RATE, rate,
1192 SPD_ALLCLIENTS);
1193 }
1194
spd_set_voice_rate_uid(SPDConnection * connection,signed int rate,unsigned int uid)1195 int spd_set_voice_rate_uid(SPDConnection * connection, signed int rate,
1196 unsigned int uid)
1197 {
1198 char who[8];
1199 sprintf(who, "%d", uid);
1200 return spd_w_set_command_int(connection, SPD_RATE, rate, who);
1201 }
1202
1203 // Get function for rate
spd_get_voice_rate(SPDConnection * connection)1204 int spd_get_voice_rate(SPDConnection * connection)
1205 {
1206 return spd_get_command_int(connection, SPD_RATE);
1207 }
1208
1209 // Set functions for pitch
spd_set_voice_pitch(SPDConnection * connection,signed int pitch)1210 int spd_set_voice_pitch(SPDConnection * connection, signed int pitch)
1211 {
1212 return spd_w_set_command_int(connection, SPD_PITCH, pitch, SPD_SELF);
1213 }
1214
spd_set_voice_pitch_all(SPDConnection * connection,signed int pitch)1215 int spd_set_voice_pitch_all(SPDConnection * connection, signed int pitch)
1216 {
1217 return spd_w_set_command_int(connection, SPD_PITCH, pitch,
1218 SPD_ALLCLIENTS);
1219 }
1220
spd_set_voice_pitch_uid(SPDConnection * connection,signed int pitch,unsigned int uid)1221 int spd_set_voice_pitch_uid(SPDConnection * connection, signed int pitch,
1222 unsigned int uid)
1223 {
1224 char who[8];
1225 sprintf(who, "%d", uid);
1226 return spd_w_set_command_int(connection, SPD_PITCH, pitch, who);
1227 }
1228
1229 // Get function for pitch
spd_get_voice_pitch(SPDConnection * connection)1230 int spd_get_voice_pitch(SPDConnection * connection)
1231 {
1232 return spd_get_command_int(connection, SPD_PITCH);
1233 }
1234
1235 // Set functions for pitch_range
spd_set_voice_pitch_range(SPDConnection * connection,signed int pitch_range)1236 int spd_set_voice_pitch_range(SPDConnection * connection,
1237 signed int pitch_range)
1238 {
1239 return spd_w_set_command_int(connection, SPD_PITCH_RANGE, pitch_range,
1240 SPD_SELF);
1241 }
1242
spd_set_voice_pitch_range_all(SPDConnection * connection,signed int pitch_range)1243 int spd_set_voice_pitch_range_all(SPDConnection * connection,
1244 signed int pitch_range)
1245 {
1246 return spd_w_set_command_int(connection, SPD_PITCH, pitch_range,
1247 SPD_ALLCLIENTS);
1248 }
1249
spd_set_voice_pitch_range_uid(SPDConnection * connection,signed int pitch_range,unsigned int uid)1250 int spd_set_voice_pitch_range_uid(SPDConnection * connection,
1251 signed int pitch_range, unsigned int uid)
1252 {
1253 char who[8];
1254 sprintf(who, "%d", uid);
1255 return spd_w_set_command_int(connection, SPD_PITCH, pitch_range, who);
1256 }
1257
1258 // Set functions for volume
spd_set_volume(SPDConnection * connection,signed int volume)1259 int spd_set_volume(SPDConnection * connection, signed int volume)
1260 {
1261 return spd_w_set_command_int(connection, SPD_VOLUME, volume, SPD_SELF);
1262 }
1263
spd_set_volume_all(SPDConnection * connection,signed int volume)1264 int spd_set_volume_all(SPDConnection * connection, signed int volume)
1265 {
1266 return spd_w_set_command_int(connection, SPD_VOLUME, volume,
1267 SPD_ALLCLIENTS);
1268 }
1269
spd_set_volume_uid(SPDConnection * connection,signed int volume,unsigned int uid)1270 int spd_set_volume_uid(SPDConnection * connection, signed int volume,
1271 unsigned int uid)
1272 {
1273 char who[8];
1274 sprintf(who, "%d", uid);
1275 return spd_w_set_command_int(connection, SPD_VOLUME, volume, who);
1276 }
1277
1278 // Get function for volume
spd_get_volume(SPDConnection * connection)1279 int spd_get_volume(SPDConnection * connection)
1280 {
1281 return spd_get_command_int(connection, SPD_VOLUME);
1282 }
1283
1284 // Set functions for language
spd_set_language(SPDConnection * connection,const char * language)1285 int spd_set_language(SPDConnection * connection, const char *language)
1286 {
1287 return spd_w_set_command_str(connection, SPD_LANGUAGE, language,
1288 SPD_SELF);
1289 }
1290
spd_set_language_all(SPDConnection * connection,const char * language)1291 int spd_set_language_all(SPDConnection * connection, const char *language)
1292 {
1293 return spd_w_set_command_str(connection, SPD_LANGUAGE, language,
1294 SPD_ALLCLIENTS);
1295 }
1296
spd_set_language_uid(SPDConnection * connection,const char * language,unsigned int uid)1297 int spd_set_language_uid(SPDConnection * connection, const char *language,
1298 unsigned int uid)
1299 {
1300 char who[8];
1301 sprintf(who, "%d", uid);
1302 return spd_w_set_command_str(connection, SPD_LANGUAGE, language, who);
1303 }
1304
1305 // Get function for language
spd_get_language(SPDConnection * connection)1306 char *spd_get_language(SPDConnection * connection)
1307 {
1308 return spd_get_command_str(connection, SPD_LANGUAGE);
1309 }
1310
1311 // Set functions for output_module
spd_set_output_module(SPDConnection * connection,const char * output_module)1312 int spd_set_output_module(SPDConnection * connection, const char *output_module)
1313 {
1314 return spd_w_set_command_str(connection, SPD_OUTPUT_MODULE,
1315 output_module, SPD_SELF);
1316 }
1317
spd_set_output_module_all(SPDConnection * connection,const char * output_module)1318 int spd_set_output_module_all(SPDConnection * connection,
1319 const char *output_module)
1320 {
1321 return spd_w_set_command_str(connection, SPD_OUTPUT_MODULE,
1322 output_module, SPD_ALLCLIENTS);
1323 }
1324
spd_set_output_module_uid(SPDConnection * connection,const char * output_module,unsigned int uid)1325 int spd_set_output_module_uid(SPDConnection * connection,
1326 const char *output_module, unsigned int uid)
1327 {
1328 char who[8];
1329 sprintf(who, "%d", uid);
1330 return spd_w_set_command_str(connection, SPD_OUTPUT_MODULE,
1331 output_module, who);
1332 }
1333
1334 // Get function for output_module
spd_get_output_module(SPDConnection * connection)1335 char *spd_get_output_module(SPDConnection * connection)
1336 {
1337 return spd_get_command_str(connection, SPD_OUTPUT_MODULE);
1338 }
1339
1340 // Set functions for synthesis_voice
spd_set_synthesis_voice(SPDConnection * connection,const char * voice_name)1341 int spd_set_synthesis_voice(SPDConnection * connection, const char *voice_name)
1342 {
1343 return spd_w_set_command_str(connection, SPD_SYNTHESIS_VOICE,
1344 voice_name, SPD_SELF);
1345 }
1346
spd_set_synthesis_voice_all(SPDConnection * connection,const char * voice_name)1347 int spd_set_synthesis_voice_all(SPDConnection * connection,
1348 const char *voice_name)
1349 {
1350 return spd_w_set_command_str(connection, SPD_SYNTHESIS_VOICE,
1351 voice_name, SPD_ALLCLIENTS);
1352 }
1353
spd_set_synthesis_voice_uid(SPDConnection * connection,const char * voice_name,unsigned int uid)1354 int spd_set_synthesis_voice_uid(SPDConnection * connection,
1355 const char *voice_name, unsigned int uid)
1356 {
1357 char who[8];
1358 sprintf(who, "%d", uid);
1359 return spd_w_set_command_str(connection, SPD_SYNTHESIS_VOICE,
1360 voice_name, who);
1361 }
1362
1363 int
spd_set_notification_on(SPDConnection * connection,SPDNotification notification)1364 spd_set_notification_on(SPDConnection * connection,
1365 SPDNotification notification)
1366 {
1367 if (connection->mode == SPD_MODE_THREADED)
1368 return spd_set_notification(connection, notification, "on");
1369 else
1370 return -1;
1371 }
1372
1373 int
spd_set_notification_off(SPDConnection * connection,SPDNotification notification)1374 spd_set_notification_off(SPDConnection * connection,
1375 SPDNotification notification)
1376 {
1377 if (connection->mode == SPD_MODE_THREADED)
1378 return spd_set_notification(connection, notification, "off");
1379 else
1380 return -1;
1381 }
1382
1383 #define NOTIFICATION_SET(val, ssip_val) \
1384 if (notification & val){ \
1385 sprintf(command, "SET SELF NOTIFICATION "ssip_val" %s", state);\
1386 ret = spd_execute_command_wo_mutex(connection, command);\
1387 if (ret < 0) RET(-1);\
1388 }
1389
1390 int
spd_set_notification(SPDConnection * connection,SPDNotification notification,const char * state)1391 spd_set_notification(SPDConnection * connection, SPDNotification notification,
1392 const char *state)
1393 {
1394 static char command[64];
1395 int ret;
1396
1397 if (connection->mode != SPD_MODE_THREADED)
1398 return -1;
1399
1400 if (state == NULL) {
1401 SPD_DBG("Requested state is NULL");
1402 return -1;
1403 }
1404 if (strcmp(state, "on") && strcmp(state, "off")) {
1405 SPD_DBG("Invalid argument for spd_set_notification: %s", state);
1406 return -1;
1407 }
1408
1409 pthread_mutex_lock(&connection->ssip_mutex);
1410
1411 NOTIFICATION_SET(SPD_INDEX_MARKS, "index_marks");
1412 NOTIFICATION_SET(SPD_BEGIN, "begin");
1413 NOTIFICATION_SET(SPD_END, "end");
1414 NOTIFICATION_SET(SPD_CANCEL, "cancel");
1415 NOTIFICATION_SET(SPD_PAUSE, "pause");
1416 NOTIFICATION_SET(SPD_RESUME, "resume");
1417 NOTIFICATION_SET(SPD_ALL, "all");
1418
1419 pthread_mutex_unlock(&connection->ssip_mutex);
1420
1421 return 0;
1422 }
1423
1424 #undef NOTIFICATION_SET
1425
1426 /* spd_list_modules retrieves information about the available output modules.
1427 The return value is a null-terminated array of strings containing output module
1428 names.
1429 */
1430
spd_list_modules(SPDConnection * connection)1431 char **spd_list_modules(SPDConnection * connection)
1432 {
1433 char **available_modules;
1434 available_modules =
1435 spd_execute_command_with_list_reply(connection,
1436 "LIST OUTPUT_MODULES");
1437 return available_modules;
1438 }
1439
free_spd_modules(char ** modules)1440 void free_spd_modules(char **modules)
1441 {
1442 int i = 0;
1443 while (modules != NULL && modules[i] != NULL) {
1444 free(modules[i]);
1445 ++i;
1446 }
1447 free(modules);
1448 }
1449
spd_list_voices(SPDConnection * connection)1450 char **spd_list_voices(SPDConnection * connection)
1451 {
1452 char **voices;
1453 voices = spd_execute_command_with_list_reply(connection, "LIST VOICES");
1454 return voices;
1455 }
1456
spd_list_synthesis_voices(SPDConnection * connection)1457 SPDVoice **spd_list_synthesis_voices(SPDConnection * connection)
1458 {
1459 char **svoices_str;
1460 SPDVoice **svoices;
1461 int i, num_items;
1462 svoices_str =
1463 spd_execute_command_with_list_reply(connection,
1464 "LIST SYNTHESIS_VOICES");
1465
1466 if (svoices_str == NULL)
1467 return NULL;
1468
1469 for (i = 0;; i++)
1470 if (svoices_str[i] == NULL)
1471 break;
1472 num_items = i;
1473 svoices = (SPDVoice **) malloc((num_items + 1) * sizeof(SPDVoice *));
1474
1475 for (i = 0; i <= num_items; i++) {
1476 const char delimiters[] = "\t";
1477 char *running;
1478
1479 if (svoices_str[i] == NULL)
1480 break;
1481 running = svoices_str[i];
1482
1483 svoices[i] = (SPDVoice *) malloc(sizeof(SPDVoice));
1484 svoices[i]->name = strsep(&running, delimiters);
1485 svoices[i]->language = strsep(&running, delimiters);
1486 svoices[i]->variant = strsep(&running, delimiters);
1487 assert(svoices[i]->name != NULL);
1488 }
1489 free(svoices_str);
1490
1491 svoices[num_items] = NULL;
1492
1493 return svoices;
1494 }
1495
free_spd_voices(SPDVoice ** voices)1496 void free_spd_voices(SPDVoice ** voices)
1497 {
1498 int i = 0;
1499 while (voices != NULL && voices[i] != NULL) {
1500 free(voices[i]->name);
1501 free(voices[i]);
1502 ++i;
1503 }
1504 free(voices);
1505 }
1506
spd_execute_command_with_list_reply(SPDConnection * connection,char * command)1507 char **spd_execute_command_with_list_reply(SPDConnection * connection,
1508 char *command)
1509 {
1510 char *reply = NULL;
1511 char *line;
1512 int err;
1513 int max_items = 50;
1514 char **result;
1515 int i;
1516
1517 spd_execute_command_with_reply(connection, command, &reply);
1518 if (!ret_ok(reply)) {
1519 if (reply != NULL)
1520 free(reply);
1521 return NULL;
1522 }
1523
1524 result = malloc((max_items + 1) * sizeof(char *));
1525
1526 for (i = 0;; i++) {
1527 line = get_param_str(reply, i + 1, &err);
1528 if ((err) || (line == NULL))
1529 break;
1530 result[i] = line;
1531 if (i >= max_items - 2) {
1532 max_items *= 2;
1533 result = realloc(result, max_items * sizeof(char *));
1534 }
1535 }
1536
1537 result[i] = NULL;
1538
1539 free(reply);
1540 return result;
1541 }
1542
1543 //int
1544 //spd_get_client_list(SPDConnection *connection, char **client_names, int *client_ids, int* active){
1545 // SPD_DBG("spd_get_client_list: History is not yet implemented.");
1546 // return -1;
1547 //
1548 //}
1549
1550 int
spd_get_message_list_fd(SPDConnection * connection,int target,int * msg_ids,char ** client_names)1551 spd_get_message_list_fd(SPDConnection * connection, int target, int *msg_ids,
1552 char **client_names)
1553 {
1554 SPD_DBG("spd_get_client_list: History is not yet implemented.");
1555 return -1;
1556 #if 0
1557 sprintf(command, "HISTORY GET MESSAGE_LIST %d 0 20\r\n", target);
1558 reply = spd_send_data(fd, command, 1);
1559
1560 /* header_ok = parse_response_header(reply);
1561 if(header_ok != 1){
1562 free(reply);
1563 return -1;
1564 } */
1565
1566 for (count = 0;; count++) {
1567 record = (char *)parse_response_data(reply, count + 1);
1568 if (record == NULL)
1569 break;
1570 record_int = get_rec_int(record, 0);
1571 msg_ids[count] = record_int;
1572 record_str = (char *)get_rec_str(record, 1);
1573 assert(record_str != NULL);
1574 client_names[count] = record_str;
1575 }
1576 return count;
1577 #endif
1578 }
1579
spd_execute_command(SPDConnection * connection,char * command)1580 int spd_execute_command(SPDConnection * connection, char *command)
1581 {
1582 char *reply;
1583 int ret;
1584
1585 pthread_mutex_lock(&connection->ssip_mutex);
1586
1587 ret = spd_execute_command_with_reply(connection, command, &reply);
1588 if (ret) {
1589 SPD_DBG("Can't execute command in spd_execute_command");
1590 }
1591 free(reply);
1592
1593 pthread_mutex_unlock(&connection->ssip_mutex);
1594
1595 return ret;
1596 }
1597
spd_execute_command_wo_mutex(SPDConnection * connection,char * command)1598 int spd_execute_command_wo_mutex(SPDConnection * connection, char *command)
1599 {
1600 char *reply;
1601 int ret;
1602
1603 SPD_DBG("Executing command wo_mutex");
1604 ret = spd_execute_command_with_reply(connection, command, &reply);
1605 if (ret)
1606 SPD_DBG
1607 ("Can't execute command in spd_execute_command_wo_mutex");
1608
1609 free(reply);
1610
1611 return ret;
1612 }
1613
1614 int
spd_execute_command_with_reply(SPDConnection * connection,char * command,char ** reply)1615 spd_execute_command_with_reply(SPDConnection * connection, char *command,
1616 char **reply)
1617 {
1618 char *buf;
1619 int r;
1620 SPD_DBG("Inside execute_command_with_reply");
1621
1622 buf = g_strdup_printf("%s\r\n", command);
1623 *reply = spd_send_data_wo_mutex(connection, buf, SPD_WAIT_REPLY);
1624 free(buf);
1625 buf = NULL;
1626 if (*reply == NULL) {
1627 SPD_DBG
1628 ("Can't send data wo mutex in spd_execute_command_with_reply");
1629 return -1;
1630 }
1631
1632 r = ret_ok(*reply);
1633
1634 if (!r)
1635 return -1;
1636 else
1637 return 0;
1638 }
1639
spd_send_data(SPDConnection * connection,const char * message,int wfr)1640 char *spd_send_data(SPDConnection * connection, const char *message, int wfr)
1641 {
1642 char *reply;
1643 pthread_mutex_lock(&connection->ssip_mutex);
1644
1645 if (connection->stream == NULL)
1646 RET(NULL);
1647
1648 reply = spd_send_data_wo_mutex(connection, message, wfr);
1649 if (reply == NULL) {
1650 SPD_DBG("Can't send data wo mutex in spd_send_data");
1651 RET(NULL);
1652 }
1653
1654 pthread_mutex_unlock(&connection->ssip_mutex);
1655 return reply;
1656 }
1657
spd_send_data_wo_mutex(SPDConnection * connection,const char * message,int wfr)1658 char *spd_send_data_wo_mutex(SPDConnection * connection, const char *message,
1659 int wfr)
1660 {
1661
1662 char *reply;
1663 int bytes;
1664
1665 SPD_DBG("Inside spd_send_data_wo_mutex");
1666
1667 if (connection->stream == NULL)
1668 return NULL;
1669
1670 if (connection->mode == SPD_MODE_THREADED) {
1671 /* Make sure we don't get the cond_reply_ready signal before we are in
1672 cond_wait() */
1673 pthread_mutex_lock(&connection->td->mutex_reply_ready);
1674 }
1675 /* write message to the socket */
1676 SPD_DBG("Writing to socket");
1677 if (!write(connection->socket, message, strlen(message))) {
1678 SPD_DBG("Can't write to socket: %s", strerror(errno));
1679 if (connection->mode == SPD_MODE_THREADED)
1680 pthread_mutex_unlock(&connection->td->mutex_reply_ready);
1681 return NULL;
1682 }
1683 SPD_DBG("Written to socket");
1684 SPD_DBG(">> : |%s|", message);
1685
1686 /* read reply to the buffer */
1687 if (wfr) {
1688 if (connection->mode == SPD_MODE_THREADED) {
1689 /* Wait until the reply is ready */
1690 SPD_DBG
1691 ("Waiting for cond_reply_ready in spd_send_data_wo_mutex");
1692 pthread_cond_wait(&connection->td->cond_reply_ready,
1693 &connection->td->mutex_reply_ready);
1694 SPD_DBG("Condition for cond_reply_ready satisfied");
1695 pthread_mutex_unlock(&connection->td->mutex_reply_ready);
1696 SPD_DBG
1697 ("Reading the reply in spd_send_data_wo_mutex threaded mode");
1698 /* Read the reply */
1699 if (connection->reply != NULL) {
1700 reply = connection->reply;
1701 connection->reply = NULL;
1702 } else {
1703 SPD_DBG
1704 ("Error: Can't read reply, broken socket in spd_send_data.");
1705 return NULL;
1706 }
1707 bytes = strlen(reply);
1708 if (bytes == 0) {
1709 free(reply);
1710 SPD_DBG("Error: Empty reply, broken socket.");
1711 return NULL;
1712 }
1713 /* Signal the reply has been read */
1714 pthread_mutex_lock(&connection->td->mutex_reply_ack);
1715 pthread_cond_signal(&connection->td->cond_reply_ack);
1716 pthread_mutex_unlock(&connection->td->mutex_reply_ack);
1717 } else {
1718 reply = get_reply(connection);
1719 }
1720 if (reply != NULL)
1721 SPD_DBG("<< : |%s|\n", reply);
1722 } else {
1723 if (connection->mode == SPD_MODE_THREADED)
1724 pthread_mutex_unlock(&connection->td->mutex_reply_ready);
1725 SPD_DBG("<< : no reply expected");
1726 return strdup("NO REPLY");
1727 }
1728
1729 if (reply == NULL)
1730 SPD_DBG
1731 ("Reply from get_reply is NULL in spd_send_data_wo_mutex");
1732
1733 SPD_DBG("Returning from spd_send_data_wo_mutex");
1734 return reply;
1735 }
1736
1737 /* --------------------- Internal functions ------------------------- */
1738
spd_set_priority(SPDConnection * connection,SPDPriority priority)1739 static int spd_set_priority(SPDConnection * connection, SPDPriority priority)
1740 {
1741 static char p_name[16];
1742 static char command[64];
1743
1744 switch (priority) {
1745 case SPD_IMPORTANT:
1746 strcpy(p_name, "IMPORTANT");
1747 break;
1748 case SPD_MESSAGE:
1749 strcpy(p_name, "MESSAGE");
1750 break;
1751 case SPD_TEXT:
1752 strcpy(p_name, "TEXT");
1753 break;
1754 case SPD_NOTIFICATION:
1755 strcpy(p_name, "NOTIFICATION");
1756 break;
1757 case SPD_PROGRESS:
1758 strcpy(p_name, "PROGRESS");
1759 break;
1760 default:
1761 SPD_DBG("Error: Can't set priority! Incorrect value.");
1762 return -1;
1763 }
1764
1765 sprintf(command, "SET SELF PRIORITY %s", p_name);
1766 return spd_execute_command_wo_mutex(connection, command);
1767 }
1768
get_reply(SPDConnection * connection)1769 static char *get_reply(SPDConnection * connection)
1770 {
1771 GString *str;
1772 char *line = NULL;
1773 size_t N = 0;
1774 int bytes;
1775 char *reply;
1776 gboolean errors = FALSE;
1777
1778 str = g_string_new("");
1779
1780 /* Wait for activity on the socket, when there is some,
1781 read all the message line by line */
1782 do {
1783 bytes = getline(&line, &N, connection->stream);
1784 if (bytes == -1) {
1785 SPD_DBG
1786 ("Error: Can't read reply, broken socket in get_reply!");
1787 if (connection->stream != NULL)
1788 fclose(connection->stream);
1789 connection->stream = NULL;
1790 errors = TRUE;
1791 } else {
1792 g_string_append(str, line);
1793 }
1794 /* terminate if we reached the last line (without '-' after numcode) */
1795 } while (!errors && !((strlen(line) < 4) || (line[3] == ' ')));
1796
1797 free(line); /* getline allocates with malloc. */
1798
1799 if (errors) {
1800 /* Free the GString and its character data, and return NULL. */
1801 g_string_free(str, TRUE);
1802 reply = NULL;
1803 } else {
1804 /* The resulting message received from the socket is stored in reply */
1805 reply = str->str;
1806 /* Free the GString, but not its character data. */
1807 g_string_free(str, FALSE);
1808 }
1809
1810 return reply;
1811 }
1812
spd_events_handler(void * conn)1813 static void *spd_events_handler(void *conn)
1814 {
1815 char *reply;
1816 int reply_code;
1817 SPDConnection *connection = conn;
1818
1819 while (1) {
1820
1821 /* Read the reply/event (block if none is available) */
1822 SPD_DBG("Getting reply in spd_events_handler");
1823 reply = get_reply(connection);
1824 if (reply == NULL) {
1825 SPD_DBG("ERROR: BROKEN SOCKET");
1826 reply_code = -1;
1827 } else {
1828 SPD_DBG("<< : |%s|\n", reply);
1829 reply_code = get_err_code(reply);
1830 }
1831
1832 if ((reply_code >= 700) && (reply_code < 800)) {
1833 int msg_id;
1834 int client_id;
1835 int err;
1836
1837 SPD_DBG("Callback detected: %s", reply);
1838
1839 /* This is an index mark */
1840 /* Extract message id */
1841 msg_id = get_param_int(reply, 1, &err);
1842 if (err < 0) {
1843 SPD_DBG
1844 ("Bad reply from Speech Dispatcher: %s (code %d)",
1845 reply, err);
1846 free(reply);
1847 break;
1848 }
1849 client_id = get_param_int(reply, 2, &err);
1850 if (err < 0) {
1851 SPD_DBG
1852 ("Bad reply from Speech Dispatcher: %s (code %d)",
1853 reply, err);
1854 free(reply);
1855 break;
1856 }
1857 /* Decide if we want to call a callback */
1858 if ((reply_code == 701) && (connection->callback_begin))
1859 connection->callback_begin(msg_id, client_id,
1860 SPD_EVENT_BEGIN);
1861 if ((reply_code == 702) && (connection->callback_end))
1862 connection->callback_end(msg_id, client_id,
1863 SPD_EVENT_END);
1864 if ((reply_code == 703)
1865 && (connection->callback_cancel))
1866 connection->callback_cancel(msg_id, client_id,
1867 SPD_EVENT_CANCEL);
1868 if ((reply_code == 704) && (connection->callback_pause))
1869 connection->callback_pause(msg_id, client_id,
1870 SPD_EVENT_PAUSE);
1871 if ((reply_code == 705)
1872 && (connection->callback_resume))
1873 connection->callback_resume(msg_id, client_id,
1874 SPD_EVENT_RESUME);
1875 if ((reply_code == 700) && (connection->callback_im)) {
1876 char *im;
1877 int err;
1878 im = get_param_str(reply, 3, &err);
1879 if ((err < 0) || (im == NULL)) {
1880 SPD_DBG
1881 ("Broken reply from Speech Dispatcher: %s",
1882 reply);
1883 free(reply);
1884 break;
1885 }
1886 /* Call the callback */
1887 connection->callback_im(msg_id, client_id,
1888 SPD_EVENT_INDEX_MARK,
1889 im);
1890 free(im);
1891 }
1892 free(reply);
1893
1894 } else {
1895 /* This is a protocol reply */
1896 pthread_mutex_lock(&connection->td->mutex_reply_ready);
1897 /* Prepare the reply to the reply buffer in connection */
1898 if (reply != NULL) {
1899 connection->reply = reply;
1900 } else {
1901 SPD_DBG("Connection reply is NULL");
1902 connection->reply = NULL;
1903 pthread_mutex_unlock(&connection->td->mutex_reply_ready);
1904 break;
1905 }
1906 /* Signal the reply is available on the condition variable */
1907 /* this order is correct and necessary */
1908 pthread_cond_signal(&connection->td->cond_reply_ready);
1909 pthread_mutex_lock(&connection->td->mutex_reply_ack);
1910 pthread_mutex_unlock(&connection->td->mutex_reply_ready);
1911 /* Wait until it has bean read */
1912 pthread_cond_wait(&connection->td->cond_reply_ack,
1913 &connection->td->mutex_reply_ack);
1914 pthread_mutex_unlock(&connection->td->mutex_reply_ack);
1915 /* Continue */
1916 }
1917 }
1918 /* In case of broken socket, we must still signal reply ready */
1919 if (connection->reply == NULL) {
1920 SPD_DBG("Signalling reply ready after communication failure");
1921 if (connection->stream != NULL)
1922 fclose(connection->stream);
1923 connection->stream = NULL;
1924 pthread_cond_signal(&connection->td->cond_reply_ready);
1925 pthread_exit(0);
1926 }
1927 return 0; /* to please gcc */
1928 }
1929
ret_ok(char * reply)1930 static int ret_ok(char *reply)
1931 {
1932 int err;
1933
1934 if (reply == NULL)
1935 return -1;
1936
1937 err = get_err_code(reply);
1938
1939 if ((err >= 100) && (err < 300))
1940 return 1;
1941 if (err >= 300)
1942 return 0;
1943
1944 SPD_FATAL("Internal error during communication.");
1945 }
1946
get_param_str(char * reply,int num,int * err)1947 static char *get_param_str(char *reply, int num, int *err)
1948 {
1949 int i;
1950 char *tptr;
1951 char *pos;
1952 char *pos_begin;
1953 char *pos_end;
1954 char *rep;
1955
1956 assert(err != NULL);
1957
1958 if (num < 1) {
1959 *err = -1;
1960 return NULL;
1961 }
1962
1963 pos = reply;
1964 for (i = 0; i <= num - 2; i++) {
1965 pos = strstr(pos, "\r\n");
1966 if (pos == NULL) {
1967 *err = -2;
1968 return NULL;
1969 }
1970 pos += 2;
1971 }
1972
1973 if (strlen(pos) < 4)
1974 return NULL;
1975
1976 *err = strtol(pos, &tptr, 10);
1977 if (*err >= 300 && *err <= 399)
1978 return NULL;
1979
1980 if ((*tptr != '-') || (tptr != pos + 3)) {
1981 *err = -3;
1982 return NULL;
1983 }
1984
1985 pos_begin = pos + 4;
1986 pos_end = strstr(pos_begin, "\r\n");
1987 if (pos_end == NULL) {
1988 *err = -4;
1989 return NULL;
1990 }
1991
1992 rep = (char *)strndup(pos_begin, pos_end - pos_begin);
1993 *err = 0;
1994
1995 return rep;
1996 }
1997
get_param_int(char * reply,int num,int * err)1998 static int get_param_int(char *reply, int num, int *err)
1999 {
2000 char *rep_str;
2001 char *tptr;
2002 int ret;
2003
2004 rep_str = get_param_str(reply, num, err);
2005 if (rep_str == NULL) {
2006 /* err is already set to the error return code, just return */
2007 return 0;
2008 }
2009
2010 ret = strtol(rep_str, &tptr, 10);
2011 if (*tptr != '\0') {
2012 /* this is not a number */
2013 *err = -3;
2014 free(rep_str);
2015 return 0;
2016 }
2017 free(rep_str);
2018
2019 return ret;
2020 }
2021
get_err_code(char * reply)2022 static int get_err_code(char *reply)
2023 {
2024 char err_code[4];
2025 int err;
2026
2027 if (reply == NULL)
2028 return -1;
2029 SPD_DBG("spd_send_data: reply: %s\n", reply);
2030
2031 err_code[0] = reply[0];
2032 err_code[1] = reply[1];
2033 err_code[2] = reply[2];
2034 err_code[3] = '\0';
2035
2036 SPD_DBG("ret_ok: err_code: |%s|\n", err_code);
2037
2038 if (isanum(err_code)) {
2039 err = atoi(err_code);
2040 } else {
2041 SPD_DBG("ret_ok: not a number\n");
2042 return -1;
2043 }
2044
2045 return err;
2046 }
2047
2048 /* isanum() tests if the given string is a number,
2049 * returns 1 if yes, 0 otherwise. */
isanum(char * str)2050 static int isanum(char *str)
2051 {
2052 int i;
2053 if (str == NULL)
2054 return 0;
2055 for (i = 0; i <= strlen(str) - 1; i++) {
2056 if (!isdigit(str[i]))
2057 return 0;
2058 }
2059 return 1;
2060 }
2061
2062 /*
2063 * escape_dot: Replace . with .. at the start of lines.
2064 * @text: text to escape
2065 * @Returns: An allocated string, containing the escaped text.
2066 */
escape_dot(const char * text)2067 static char *escape_dot(const char *text)
2068 {
2069 size_t orig_len = 0;
2070 const char *orig_end;
2071 char *result = NULL;
2072 char *result_ptr;
2073 static const char *ESCAPED_DOTLINE = "\r\n..";
2074 static const size_t ESCAPED_DOTLINELEN = 4;
2075 static const size_t DOTLINELEN = 3;
2076
2077 if (text == NULL)
2078 return NULL;
2079
2080 orig_len = strlen(text);
2081 orig_end = text + orig_len;
2082 result = malloc((orig_len * 2 + 1) * sizeof(char));
2083
2084 if (result == NULL)
2085 return NULL;
2086
2087 result_ptr = result;
2088
2089 /* We're over-allocating. Even if we replaced every character
2090 * in text with "..", the length of the escaped string can be no more
2091 * than orig_len * 2. We could tighten that upper bound with
2092 * a little more work.
2093 */
2094
2095 if ((orig_len >= 1) && (text[0] == '.')) {
2096 *(result_ptr++) = '.';
2097 *(result_ptr++) = '.';
2098 text += 1;
2099 }
2100
2101 while (text < orig_end) {
2102 if ((text[0] == '\r') && (text[1] == '\n') && (text[2] == '.')) {
2103 memcpy(result_ptr, ESCAPED_DOTLINE, ESCAPED_DOTLINELEN);
2104 result_ptr += ESCAPED_DOTLINELEN;
2105 text += DOTLINELEN;
2106 } else {
2107 *(result_ptr++) = *(text++);
2108 }
2109 }
2110
2111 *result_ptr = '\0';
2112 return result;
2113 }
2114
2115 #ifdef LIBSPEECHD_DEBUG
SPD_DBG(char * format,...)2116 static void SPD_DBG(char *format, ...)
2117 {
2118 va_list args;
2119
2120 pthread_mutex_lock(&spd_logging_mutex);
2121 va_start(args, format);
2122 vfprintf(spd_debug, format, args);
2123 va_end(args);
2124 fprintf(spd_debug, "\n");
2125 fflush(spd_debug);
2126 pthread_mutex_unlock(&spd_logging_mutex);
2127 }
2128 #else /* LIBSPEECHD_DEBUG */
SPD_DBG(char * format,...)2129 static void SPD_DBG(char *format, ...)
2130 {
2131 }
2132 #endif /* LIBSPEECHD_DEBUG */
2133