1 /* -*- mode: c ; c-file-style: "canonware-c-style" -*-
2  ******************************************************************************
3  *
4  * Copyright (C) 1996-2005 Jason Evans <jasone@canonware.com>.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice(s), this list of conditions and the following disclaimer
12  *    unmodified other than the allowable addition of one or more
13  *    copyright notices.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice(s), this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
26  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
28  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
29  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  ******************************************************************************
32  *
33  * Version: Onyx 5.1.2
34  *
35  ******************************************************************************/
36 
37 #include "libonyx/libonyx.h"
38 #include "../include/modprompt.h"
39 
40 #include <errno.h>
41 #include <histedit.h>
42 
43 /* LinuxThreads (the precursor to NPTL) appears to do the wrong thing for
44  * SIGSTOP.  Defining CW_MODPROMPT_LINUXTHREADS works around this problem.
45  *
46  * XXX This workaround should be removed once NPTL-based systems are the
47  * norm. */
48 /* #define CW_MODPROMPT_LINUXTHREADS */
49 
50 #define CW_PROMPT_STRLEN 80
51 #define CW_BUFFER_SIZE 512
52 
53 struct cw_modprompt_synth_s {
54 #ifdef CW_DBG
55 #define CW_MODPROMPT_SYNTH_MAGIC 0x32ad81a5
56     uint32_t magic;
57 #endif
58     cw_nxo_t modload_handle;
59     cw_nxo_t *thread;
60     cw_nxo_threadp_t threadp;
61     bool continuation;
62 #ifdef CW_THREADS
63     cw_thd_t *read_thd;
64     volatile bool quit;
65     volatile bool resize;
66 
67     cw_mtx_t mtx;
68     bool have_data; /* The writer wrote data. */
69     cw_cnd_t put_cnd; /* The writer (libedit thread) waits on this. */
70     bool want_data; /* The reader wants data. */
71     cw_cnd_t get_cnd; /* The reader (interpreter thread) waits on this. */
72 #endif
73 
74     char *buffer;
75     uint32_t buffer_size;
76     uint32_t buffer_count;
77 
78     EditLine *el;
79     History *hist;
80     HistEvent hevent;
81     char prompt_str[CW_PROMPT_STRLEN];
82 };
83 
84 /* Globals. */
85 /* This must be global so that the signal handler can get to it. */
86 static struct cw_modprompt_synth_s *synth;
87 
88 /* Function prototypes. */
89 static cw_nxoe_t *
90 modprompt_synth_ref_iter(void *a_data, bool a_reset);
91 
92 static void
93 modprompt_synth_delete(void *a_data);
94 
95 static int32_t
96 modprompt_read(void *a_data, cw_nxo_t *a_file, uint32_t a_len,
97 	       char *r_str);
98 
99 static void
100 modprompt_promptstring(struct cw_modprompt_synth_s *a_synth);
101 
102 static char *
103 modprompt_prompt(EditLine *a_el);
104 
105 static void
106 modprompt_handlers_install(void);
107 
108 static void
109 modprompt_signal_handle(int a_signal);
110 
111 #ifdef CW_THREADS
112 static void *
113 modprompt_entry(void *a_arg);
114 #endif
115 
116 void
modprompt_init(void * a_arg,cw_nxo_t * a_thread)117 modprompt_init(void *a_arg, cw_nxo_t *a_thread)
118 {
119     cw_nxo_t *estack, *file;
120     char *editor;
121 #ifdef CW_THREADS
122     sigset_t set, oset;
123 #endif
124 
125     synth = (struct cw_modprompt_synth_s *)
126 	cw_calloc(1, sizeof(struct cw_modprompt_synth_s));
127 
128     /* Initialize stdin.  Only convert the initial thread's stdin, since it
129      * isn't safe for multiple threads to use the synthetic file.  If the
130      * application is crazy enough use the initial thread's stdin in another
131      * thread, crashes are likely. */
132     file = nxo_thread_stdin_get(a_thread);
133     nxo_file_new(file, true);
134     nxo_file_synthetic(file, modprompt_read, NULL, modprompt_synth_ref_iter,
135 		       modprompt_synth_delete, synth);
136     nxo_file_origin_set(file, "*stdin*", sizeof("*stdin*") - 1);
137 
138     nxo_attr_set(file, NXOA_EXECUTABLE);
139 
140     /* The interpreter is currently executing a handle that holds a reference to
141      * the dynamically loaded module.  Keep a reference to it, so that this
142      * module will not be unloaded until after the synthetic file object has
143      * been destroyed. */
144     estack = nxo_thread_estack_get(a_thread);
145     nxo_no_new(&synth->modload_handle);
146     nxo_dup(&synth->modload_handle, nxo_stack_get(estack));
147 
148     /* Finish initializing synth. */
149     synth->thread = a_thread;
150     nxo_threadp_new(&synth->threadp);
151 #ifdef CW_THREADS
152     mtx_new(&synth->mtx);
153     cnd_new(&synth->put_cnd);
154     cnd_new(&synth->get_cnd);
155 #endif
156 
157     /* Initialize the command editor. */
158     synth->hist = history_init();
159     history(synth->hist, &synth->hevent, H_SETSIZE, 512);
160 
161     synth->el = el_init("onyx", stdin, stdout, stderr);
162     el_set(synth->el, EL_HIST, history, synth->hist);
163     el_set(synth->el, EL_PROMPT, modprompt_prompt);
164     el_set(synth->el, EL_CLIENTDATA, synth);
165 
166     editor = getenv("ONYX_EDITOR");
167     if (editor == NULL || (strcmp(editor, "emacs") && strcmp(editor, "vi")))
168     {
169 	/* Default to emacs key bindings, since they're more intuitive to the
170 	 * uninitiated. */
171 	editor = "emacs";
172     }
173     el_set(synth->el, EL_EDITOR, editor);
174 #ifdef CW_DBG
175     synth->magic = CW_MODPROMPT_SYNTH_MAGIC;
176 #endif
177 
178 #ifdef CW_THREADS
179     /* Mask all signals that the read thread handles, so that they're always
180      * delivered there. */
181     sigemptyset(&set);
182     sigaddset(&set, SIGINT);
183 #ifdef CW_MODPROMPT_LINUXTHREADS
184     sigaddset(&set, SIGTSTP);
185     sigaddset(&set, SIGSTOP);
186 #endif
187     sigaddset(&set, SIGQUIT);
188     sigaddset(&set, SIGHUP);
189     sigaddset(&set, SIGTERM);
190 #ifdef CW_MODPROMPT_LINUXTHREADS
191     sigaddset(&set, SIGCONT);
192 #endif
193     sigaddset(&set, SIGWINCH);
194     thd_sigmask(SIG_BLOCK, &set, &oset);
195     synth->read_thd = thd_new(modprompt_entry, synth, true);
196 #else
197     /* Install signal handlers. */
198     modprompt_handlers_install();
199 #endif
200 
201     /* stdin is now a synthetic file.  This does not change any file objects
202      * which may already be on estack, but under normal circumstances, this
203      * module is loaded by the bootstrap code, and stdin is not pushed onto
204      * estack and executed until after the bootstrap code has been completely
205      * executed.
206      *
207      * If this module is loaded later on, once stdin is being read from, loading
208      * this module will have no apparent effect unless something like the
209      * following code is recursed into:
210      *
211      *   stdin cvx eval
212      *
213      * Strange things may happen though, since the file object already on estack
214      * may have already buffered data, but the data will not be evaluated until
215      * EOF is returned by the synthetic stdin. */
216 }
217 
218 static cw_nxoe_t *
modprompt_synth_ref_iter(void * a_data,bool a_reset)219 modprompt_synth_ref_iter(void *a_data, bool a_reset)
220 {
221     cw_nxoe_t *retval;
222     struct cw_modprompt_synth_s *synth = (struct cw_modprompt_synth_s *) a_data;
223 
224     cw_check_ptr(synth);
225     cw_dassert(synth->magic == CW_MODPROMPT_SYNTH_MAGIC);
226 
227     if (a_reset)
228     {
229 	retval = nxo_nxoe_get(&synth->modload_handle);
230     }
231     else
232     {
233 	retval = NULL;
234     }
235 
236     return retval;
237 }
238 
239 static void
modprompt_synth_delete(void * a_data)240 modprompt_synth_delete(void *a_data)
241 {
242     struct cw_modprompt_synth_s *synth = (struct cw_modprompt_synth_s *) a_data;
243 
244     cw_check_ptr(synth);
245     cw_dassert(synth->magic == CW_MODPROMPT_SYNTH_MAGIC);
246 
247 #ifdef CW_THREADS
248     /* Tell the read thread to start shutting down. */
249     mtx_lock(&synth->mtx);
250     synth->quit = true;
251     cnd_signal(&synth->put_cnd);
252     mtx_unlock(&synth->mtx);
253     /* Wait for the read thread to exit. */
254     thd_join(synth->read_thd);
255     /* Clean up. */
256     mtx_delete(&synth->mtx);
257     cnd_delete(&synth->put_cnd);
258     cnd_delete(&synth->get_cnd);
259 #endif
260 
261     if (synth->buffer != NULL)
262     {
263 	cw_free(synth->buffer);
264     }
265 
266     /* Clean up the command editor. */
267     el_end(synth->el);
268     history_end(synth->hist);
269 
270     cw_free(synth);
271 }
272 
273 #ifdef CW_THREADS
274 static int32_t
modprompt_read(void * a_data,cw_nxo_t * a_file,uint32_t a_len,char * r_str)275 modprompt_read(void *a_data, cw_nxo_t *a_file, uint32_t a_len,
276 	       char *r_str)
277 {
278     int32_t retval;
279     struct cw_modprompt_synth_s *synth = (struct cw_modprompt_synth_s *) a_data;
280 
281     cw_check_ptr(synth);
282     cw_dassert(synth->magic == CW_MODPROMPT_SYNTH_MAGIC);
283     cw_assert(a_len > 0);
284 
285     mtx_lock(&synth->mtx);
286 
287     if (synth->buffer_count == 0)
288     {
289 	/* Tell the main thread to read more data, then wait for it. */
290 	synth->want_data = true;
291 	cnd_signal(&synth->put_cnd);
292 	synth->have_data = false;
293 	while (synth->have_data == false)
294 	{
295 	    cnd_wait(&synth->get_cnd, &synth->mtx);
296 	}
297     }
298     cw_assert(synth->buffer_count > 0);
299 
300     /* Return as much of the data as possible. */
301     if (synth->buffer_count > a_len)
302     {
303 	/* There are more data than we can return. */
304 	retval = a_len;
305 	memcpy(r_str, synth->buffer, a_len);
306 	synth->buffer_count -= a_len;
307 	memmove(synth->buffer, &synth->buffer[a_len], synth->buffer_count);
308     }
309     else
310     {
311 	/* Return all the data. */
312 	retval = synth->buffer_count;
313 	memcpy(r_str, synth->buffer, synth->buffer_count);
314 	synth->buffer_count = 0;
315     }
316 
317     mtx_unlock(&synth->mtx);
318     return retval;
319 }
320 #else
321 static int32_t
modprompt_read(void * a_data,cw_nxo_t * a_file,uint32_t a_len,char * r_str)322 modprompt_read(void *a_data, cw_nxo_t *a_file, uint32_t a_len,
323 	       char *r_str)
324 {
325     int32_t retval;
326     struct cw_modprompt_synth_s *synth = (struct cw_modprompt_synth_s *) a_data;
327     const char *str;
328     int count = 0;
329 
330     cw_check_ptr(synth);
331     cw_dassert(synth->magic == CW_MODPROMPT_SYNTH_MAGIC);
332     cw_assert(a_len > 0);
333 
334     if (synth->buffer_count == 0)
335     {
336 	/* Update the promptstring if necessary. */
337 	modprompt_promptstring(synth);
338 
339 	/* Read more data. */
340 	while ((str = el_gets(synth->el, &count)) == NULL)
341 	{
342 	    /* An interrupted system call (EINTR) caused an error in
343 	     * el_gets(). */
344 	}
345 	cw_assert(count > 0);
346 
347 	/* Update the command line history. */
348 	if ((nxo_thread_deferred(synth->thread) == false)
349 	    && (nxo_thread_state(synth->thread) == THREADTS_START))
350 	{
351 	    /* Completion of a history element.  Insert it, taking care to avoid
352 	     * simple (non-continued) duplicates and empty lines (simple
353 	     * carriage returns). */
354 	    if (synth->continuation)
355 	    {
356 		history(synth->hist, &synth->hevent, H_ENTER, str);
357 		synth->continuation = false;
358 	    }
359 	    else
360 	    {
361 		if ((history(synth->hist, &synth->hevent, H_FIRST) == -1
362 		     || strcmp(str, synth->hevent.str)) && strlen(str) > 1)
363 		{
364 		    history(synth->hist, &synth->hevent, H_ENTER, str);
365 		}
366 	    }
367 	}
368 	else
369 	{
370 	    /* Continuation.  Append it to the current history element. */
371 	    history(synth->hist, &synth->hevent, H_ADD, str);
372 	    synth->continuation = true;
373 	}
374 
375 	/* Copy the data to the synth buffer. */
376 	if (count > synth->buffer_size)
377 	{
378 	    /* Expand the buffer. */
379 	    if (synth->buffer == NULL)
380 	    {
381 		synth->buffer = (char *) cw_malloc(count);
382 	    }
383 	    else
384 	    {
385 		synth->buffer = (char *) cw_realloc(synth->buffer, count);
386 	    }
387 	    synth->buffer_size = count;
388 	}
389 	memcpy(synth->buffer, str, count);
390 	synth->buffer_count = count;
391     }
392     cw_assert(synth->buffer_count > 0);
393 
394     /* Return as much of the data as possible. */
395     if (synth->buffer_count > a_len)
396     {
397 	/* There are more data than we can return. */
398 	retval = a_len;
399 	memcpy(r_str, synth->buffer, a_len);
400 	synth->buffer_count -= a_len;
401 	memmove(synth->buffer, &synth->buffer[a_len], synth->buffer_count);
402     }
403     else
404     {
405 	/* Return all the data. */
406 	retval = synth->buffer_count;
407 	memcpy(r_str, synth->buffer, synth->buffer_count);
408 	synth->buffer_count = 0;
409     }
410 
411     return retval;
412 }
413 #endif
414 
415 static void
modprompt_promptstring(struct cw_modprompt_synth_s * a_synth)416 modprompt_promptstring(struct cw_modprompt_synth_s *a_synth)
417 {
418     /* Call promptstring if the conditions are right.  Take lots of care not to
419      * let an error in promptstring cause recursion into the error handling
420      * machinery. */
421     if ((nxo_thread_deferred(synth->thread) == false)
422 	&& (nxo_thread_state(synth->thread) == THREADTS_START))
423     {
424 	char *pstr;
425 	uint32_t plen, maxlen;
426 	cw_nxo_t *nxo;
427 	cw_nxo_t *stack;
428 	static const char code[] =
429 	    "{$promptstring where {\n"
430 	    "pop\n"
431 	    /* Save the current contents of errordict into promptdict. */
432 	    "$promptdict errordict dict copy def\n"
433 	    /* Temporarily reconfigure errordict. */
434 	    "errordict $handleerror {} put\n"
435 	    "errordict $stop $stop load put\n"
436 	    /* Actually call promptstring. */
437 	    "{promptstring} stopped {`'} if\n"
438 	    /* Restore errordict's configuration. */
439 	    "promptdict errordict copy pop\n"
440 	    /* Remove the definition of promptdict. */
441 	    "$promptdict where {$promptdict undef} if\n"
442 	    "}{`'} ifelse} start";
443 
444 	stack = nxo_thread_ostack_get(synth->thread);
445 
446 	/* Push the prompt onto the data stack. */
447 	nxo_thread_interpret(synth->thread, &synth->threadp, code,
448 			     sizeof(code) - 1);
449 	nxo_thread_flush(synth->thread, &synth->threadp);
450 
451 	/* Get the actual prompt string. */
452 	nxo = nxo_stack_get(stack);
453 	if (nxo == NULL)
454 	{
455 	    nxo_thread_nerror(synth->thread, NXN_stackunderflow);
456 	    maxlen = 0;
457 	}
458 	else if (nxo_type_get(nxo) != NXOT_STRING)
459 	{
460 	    nxo_thread_nerror(synth->thread, NXN_typecheck);
461 	    maxlen = 0;
462 	}
463 	else
464 	{
465 	    pstr = nxo_string_get(nxo);
466 	    plen = nxo_string_len_get(nxo);
467 
468 	    /* Copy the prompt string to a global buffer. */
469 	    maxlen = (plen > CW_PROMPT_STRLEN - 1)
470 		? CW_PROMPT_STRLEN - 1 : plen;
471 	    strncpy(synth->prompt_str, pstr, maxlen);
472 	}
473 
474 	synth->prompt_str[maxlen] = '\0';
475 
476 	/* Pop the prompt string off the data stack. */
477 	nxo_stack_pop(stack);
478     }
479 }
480 
481 static char *
modprompt_prompt(EditLine * a_el)482 modprompt_prompt(EditLine *a_el)
483 {
484     struct cw_modprompt_synth_s *synth;
485 
486     el_get(a_el, EL_CLIENTDATA, (void **)&synth);
487 
488     cw_check_ptr(synth);
489     cw_dassert(synth->magic == CW_MODPROMPT_SYNTH_MAGIC);
490 
491     if ((nxo_thread_deferred(synth->thread))
492 	|| (nxo_thread_state(synth->thread) != THREADTS_START))
493     {
494 	/* One or both of:
495 	 *
496 	 * - Continuation of a string or similarly parsed token.
497 	 * - Execution is deferred due to unmatched {}'s.
498 	 *
499 	 * Don't print a prompt. */
500 	synth->prompt_str[0] = '\0';
501     }
502 
503     return synth->prompt_str;
504 }
505 
506 static void
modprompt_handlers_install(void)507 modprompt_handlers_install(void)
508 {
509     struct sigaction action;
510 #ifdef CW_THREADS
511     sigset_t set, oset;
512 #endif
513 
514     /* Set up a signal handler for various signals that are important to an
515      * interactive program. */
516     memset(&action, 0, sizeof(struct sigaction));
517     action.sa_handler = modprompt_signal_handle;
518     sigemptyset(&action.sa_mask);
519 #define HANDLER_INSTALL(a_signal)					\
520     if (sigaction((a_signal), &action, NULL) == -1)			\
521     {									\
522 	fprintf(stderr, "Error in sigaction(%s, ...): %s\n",		\
523 		#a_signal, strerror(errno));				\
524 	abort();							\
525     }
526     HANDLER_INSTALL(SIGHUP);
527     HANDLER_INSTALL(SIGWINCH);
528 #ifdef CW_MODPROMPT_LINUXTHREADS
529     HANDLER_INSTALL(SIGTSTP);
530     HANDLER_INSTALL(SIGCONT);
531 #endif
532     HANDLER_INSTALL(SIGINT);
533     HANDLER_INSTALL(SIGQUIT);
534     HANDLER_INSTALL(SIGTERM);
535 #undef HANDLER_INSTALL
536 
537 #ifdef CW_THREADS
538     sigemptyset(&set);
539     sigaddset(&set, SIGINT);
540 #ifdef CW_MODPROMPT_LINUXTHREADS
541     sigaddset(&set, SIGTSTP);
542     sigaddset(&set, SIGSTOP);
543 #endif
544     sigaddset(&set, SIGQUIT);
545     sigaddset(&set, SIGHUP);
546     sigaddset(&set, SIGTERM);
547 #ifdef CW_MODPROMPT_LINUXTHREADS
548     sigaddset(&set, SIGCONT);
549 #endif
550     sigaddset(&set, SIGWINCH);
551     thd_sigmask(SIG_UNBLOCK, &set, &oset);
552 #endif
553 }
554 
555 static void
modprompt_signal_handle(int a_signal)556 modprompt_signal_handle(int a_signal)
557 {
558     switch (a_signal)
559     {
560 	case SIGWINCH:
561 	{
562 	    el_resize(synth->el);
563 	    break;
564 	}
565 #ifdef CW_MODPROMPT_LINUXTHREADS
566 	case SIGTSTP:
567 	{
568 	    /* Signal the process group. */
569 	    kill(0, SIGSTOP);
570 	    break;
571 	}
572 	case SIGCONT:
573 	{
574 	    break;
575 	}
576 #endif
577 	case SIGHUP:
578 	case SIGINT:
579 	case SIGQUIT:
580 	case SIGTERM:
581 	{
582 	    el_end(synth->el);
583 	    _exit(0);
584 	}
585 	default:
586 	{
587 	    fprintf(stderr, "Unexpected signal %d\n", a_signal);
588 	    abort();
589 	}
590     }
591 }
592 
593 #ifdef CW_THREADS
594 static void *
modprompt_entry(void * a_arg)595 modprompt_entry(void *a_arg)
596 {
597     struct cw_modprompt_synth_s *synth = (struct cw_modprompt_synth_s *) a_arg;
598     const char *str;
599     int count = 0;
600 
601     cw_check_ptr(synth);
602     cw_dassert(synth->magic == CW_MODPROMPT_SYNTH_MAGIC);
603 
604     /* Install signal handlers. */
605     modprompt_handlers_install();
606 
607     mtx_lock(&synth->mtx);
608     for (;;)
609     {
610 	/* Wait for the interpreter thread to request data. */
611 	while (synth->want_data == false && synth->quit == false)
612 	{
613 	    cnd_wait(&synth->put_cnd, &synth->mtx);
614 	}
615 	synth->want_data = false;
616 	if (synth->quit)
617 	{
618 	    break;
619 	}
620 
621 	/* Update the promptstring if necessary. */
622 	modprompt_promptstring(synth);
623 
624 	/* Read data. */
625 	cw_assert(synth->buffer_count == 0);
626 	while ((str = el_gets(synth->el, &count)) == NULL)
627 	{
628 	    /* An interrupted system call (EINTR) caused an error in el_gets().
629 	     * Check to see if we should quit. */
630 	}
631 	cw_assert(count > 0);
632 
633 	/* Update the command line history. */
634 	if ((nxo_thread_deferred(synth->thread) == false)
635 	    && (nxo_thread_state(synth->thread) == THREADTS_START))
636 	{
637 	    /* Completion of a history element.  Insert it, taking care to avoid
638 	     * simple (non-continued) duplicates and empty lines (simple
639 	     * carriage returns). */
640 	    if (synth->continuation)
641 	    {
642 		history(synth->hist, &synth->hevent, H_ENTER, str);
643 		synth->continuation = false;
644 	    }
645 	    else
646 	    {
647 		if ((history(synth->hist, &synth->hevent, H_FIRST) == -1
648 		     || strcmp(str, synth->hevent.str)) && strlen(str) > 1)
649 		{
650 		    history(synth->hist, &synth->hevent, H_ENTER, str);
651 		}
652 	    }
653 	}
654 	else
655 	{
656 	    /* Continuation.  Append it to the current history element. */
657 	    history(synth->hist, &synth->hevent, H_ADD, str);
658 	    synth->continuation = true;
659 	}
660 
661 	/* Copy the data to the synth buffer. */
662 	if (count > synth->buffer_size)
663 	{
664 	    /* Expand the buffer. */
665 	    if (synth->buffer == NULL)
666 	    {
667 		synth->buffer = (char *) cw_malloc(count);
668 	    }
669 	    else
670 	    {
671 		synth->buffer = (char *) cw_realloc(synth->buffer, count);
672 	    }
673 	    synth->buffer_size = count;
674 	}
675 	memcpy(synth->buffer, str, count);
676 	synth->buffer_count = count;
677 
678 	/* Tell the interpreter thread that there are data available. */
679 	synth->have_data = true;
680 	cnd_signal(&synth->get_cnd);
681     }
682     mtx_unlock(&synth->mtx);
683 
684     return NULL;
685 }
686 #endif
687