Provide a decent interface for finding out about the audio cache and
[jog] / rxglue.c
CommitLineData
2ec1e693 1/* -*-c-*-
2 *
af666e6f 3 * $Id: rxglue.c,v 1.4 2002/02/02 22:43:50 mdw Exp $
2ec1e693 4 *
5 * REXX glue for C core functionality
6 *
7 * (c) 2001 Mark Wooding
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of Jog: Programming for a jogging machine.
13 *
14 * Jog is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * Jog is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Jog; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: rxglue.c,v $
af666e6f 32 * Revision 1.4 2002/02/02 22:43:50 mdw
33 * Provide a decent interface for finding out about the audio cache and
34 * configuring its capacity.
35 *
e9060e7e 36 * Revision 1.3 2002/02/02 19:17:41 mdw
37 * New audio subsystem.
38 *
661c4bdc 39 * Revision 1.2 2002/01/30 09:22:48 mdw
40 * Use memory-allocation functions provided by the REXX interpreter.
41 * Now that configuration can be applied after initialization, allow
42 * @txconf@ to set parameters. Make @txsend@ add a newline to its output,
43 * unless forbidden.
44 *
2ec1e693 45 * Revision 1.1 2002/01/25 19:34:45 mdw
46 * Initial revision
47 *
48 */
49
50/*----- Header files ------------------------------------------------------*/
51
52#ifdef HAVE_CONFIG_H
53# include "config.h"
54#endif
55
56#include <ctype.h>
57#include <errno.h>
58#include <limits.h>
59#include <stdarg.h>
60#include <stdio.h>
61#include <stdlib.h>
62#include <string.h>
63#include <time.h>
64
65#include <sys/types.h>
66#include <sys/time.h>
67#include <unistd.h>
68
69#define INCL_RXFUNC
70#define RX_STRONGTYPING
71#include <rexxsaa.h>
72
73#include <mLib/alloc.h>
661c4bdc 74#include <mLib/exc.h>
2ec1e693 75#include <mLib/dstr.h>
76
e9060e7e 77#include "au.h"
78#include "aunum.h"
2ec1e693 79#include "err.h"
80#include "rxglue.h"
81#include "txport.h"
82
83/*----- Static variables --------------------------------------------------*/
84
85static txport *tx = 0;
86
661c4bdc 87/*----- Memory allocation functions ---------------------------------------*/
88
89static void *rx_alloc(size_t sz)
90{
91 void *p = RexxAllocateMemory(sz);
92 if (!p)
93 THROW(EXC_NOMEM);
94 return (p);
95}
96
97static void rx_free(void *p)
98{
99 RexxFreeMemory(p);
100}
101
2ec1e693 102/*----- Conversion functions ----------------------------------------------*/
103
104/* --- @rxs_putm@ --- *
105 *
106 * Arguments: @RXSTRING *x@ = pointer to REXX string structure
107 * For @rxs_putm@:
108 * @const void *p@ = pointer to data block
109 * @size_t sz@ = size of data
110 * For @rxs_putd@:
111 * @const dstr *d@ = pointer to source string
112 * For @rxs_putf@ and @rxs_vputf@:
113 * @const char *m@ = message format string
114 *
115 * Returns: ---
116 *
117 * Use: Stashes some text in an @RXSTRING@, overwriting whatever was
118 * there before. We assume that the previous contents don't
119 * require freeing.
120 */
121
122#define RXS_PUTM(x, p, sz) do { \
123 RXSTRING *_x = (x); \
124 const void *_p = (p); \
125 size_t _sz = (sz); \
126 if (!_x->strptr || _x->strlength < _sz) \
661c4bdc 127 _x->strptr = rx_alloc(_sz); \
2ec1e693 128 memcpy(_x->strptr, _p, _sz); \
129 _x->strlength = _sz; \
130} while (0)
131
132static void rxs_putm(RXSTRING *x, const void *p, size_t sz)
133{
134 RXS_PUTM(x, p, sz);
135}
136
137#define RXS_PUTD(x, d) do { \
138 dstr *_d = (d); \
139 RXS_PUTM((x), _d->buf, _d->len); \
140} while (0)
141
142static void rxs_putd(RXSTRING *x, dstr *d) { RXS_PUTD(x, d); }
143
144static void rxs_vputf(RXSTRING *x, const char *m, va_list *ap)
145{
146 dstr d = DSTR_INIT;
147 dstr_vputf(&d, m, ap);
148 RXS_PUTD(x, &d);
149 DDESTROY(&d);
150}
151
152static void rxs_putf(RXSTRING *x, const char *m, ...)
153{
154 va_list ap;
155 dstr d = DSTR_INIT;
156 va_start(ap, m);
157 dstr_vputf(&d, m, &ap);
158 RXS_PUTD(x, &d);
159 va_end(ap);
160 DDESTROY(&d);
161}
162
163/* --- @rxs_get@ --- *
164 *
165 * Arguments: @const RXSTRING *x@ = pointer to a REXX string
166 * @dstr *d@ = where to put it
167 *
168 * Returns: ---
169 *
170 * Use: Pulls a REXX string out and puts it in a dynamic string.
171 */
172
173#define RXS_GET(x, d) do { \
174 const RXSTRING *_x = (x); \
175 dstr *_dd = (d); \
176 DPUTM(_dd, _x->strptr, _x->strlength); \
177 DPUTZ(_dd); \
178} while (0)
179
180static void rxs_get(const RXSTRING *x, dstr *d) { RXS_GET(x, d); }
181
182/* --- @rxs_tol@ --- *
183 *
184 * Arguments: @const RXSTRING *x@ = pointer to a REXX string
185 * @long *ii@ = where to put the answer
186 *
187 * Returns: Zero on success, or nonzero on error.
188 *
189 * Use: Fetches an integer from a REXX string. This doesn't cope
190 * with multiprecision integers or similar silliness.
191 */
192
193static int rxs_tol(const RXSTRING *x, long *ii)
194{
195 long i = 0;
196 const char *p = x->strptr, *l = p + x->strlength;
197 unsigned f = 0;
198
199#define f_neg 1u
200#define f_ok 2u
201
202#define MINR (LONG_MIN/10)
203#define MIND (LONG_MIN%10)
204
205 while (p < l && isspace((unsigned char)*p))
206 p++;
207 if (p >= l)
208 return (-1);
209 if (*p == '+')
210 p++;
211 else if (*p == '-') {
212 f |= f_neg;
213 p++;
214 }
215 while (p < l && isspace((unsigned char)*p))
216 p++;
217 while (p < l && isdigit((unsigned char)*p)) {
218 int j = *p++ - '0';
219 if (i < MINR || (i == MINR && -j < MIND))
220 return (-1);
221 i = (i * 10) - j;
222 f |= f_ok;
223 }
224 while (p < l && isspace((unsigned char)*p))
225 p++;
226 if (p < l || !(f & f_ok))
227 return (-1);
228 if (!(f & f_neg)) {
229 if (i < -LONG_MAX)
230 return (-1);
231 i = -i;
232 }
233 *ii = i;
234 return (0);
235
236#undef MINR
237#undef MIND
238
239#undef f_neg
240#undef f_ok
241}
242
243/* --- @rxs_block@ --- *
244 *
245 * Arguments: @const RXSTRING *x@ = a REXX string
246 * @unsigned long *t@ = where to put the block spec
247 *
248 * Returns: Zero if OK, nonzero on error.
249 *
250 * Use: Picks out a blockingness spec.
251 */
252
253static int rxs_block(const RXSTRING *x, unsigned long *t)
254{
255 long i;
256
257 if (!x->strptr || x->strlength < 1)
258 return (-1);
259 switch (x->strptr[0]) {
260 case 'f':
261 case 'F':
262 *t = FOREVER;
263 break;
264 default:
265 if (rxs_tol(x, &i) || i < 0)
266 return (-1);
267 *t = i;
268 break;
269 }
270 return (0);
271}
272
273/*----- REXX functions ----------------------------------------------------*/
274
661c4bdc 275static APIRET APIENTRY rxfn_test(const char *fn, ULONG ac, RXSTRING *av,
276 const char *sn, RXSTRING *r)
2ec1e693 277{
278 ULONG i;
279
280 printf("test entry\n"
281 " fn = `%s'\n", fn);
282 for (i = 0; i < ac; i++) {
283 long l;
284
285 printf(" av[%lu] = `", i);
286 fwrite(av[i].strptr, 1, av[i].strlength, stdout);
287 if (rxs_tol(&av[i], &l))
288 printf("'\n");
289 else
290 printf("' (%ld)\n", l);
291 }
661c4bdc 292 printf("tx = `%s'; f = `%s'; c = `%s'.\n", txname, txfile, txconf);
2ec1e693 293 rxs_putf(r, "function `%s' completed ok", fn);
294 return (0);
295}
296
297/* --- @txname()@ ---
298 *
299 * Arguments: ---
300 *
301 * Returns: The currently-selected transport name.
302 */
303
661c4bdc 304static APIRET APIENTRY rxfn_txname(const char *fn, ULONG ac, RXSTRING *av,
305 const char *sn, RXSTRING *r)
2ec1e693 306{
307 if (ac)
308 return (-1);
309 rxs_putf(r, "%s", txname);
310 return (0);
311}
312
313/* --- @txfile()@ ---
314 *
315 * Arguments: ---
316 *
317 * Returns: The currently-selected transport filename.
318 */
319
661c4bdc 320static APIRET APIENTRY rxfn_txfile(const char *fn, ULONG ac, RXSTRING *av,
321 const char *sn, RXSTRING *r)
2ec1e693 322{
323 if (ac)
324 return (-1);
325 rxs_putf(r, "%s", txfile ? txfile : "");
326 return (0);
327}
328
661c4bdc 329/* --- @txconf([CONFIG])@ ---
2ec1e693 330 *
661c4bdc 331 * Arguments: @CONFIG@ = optional string to set
2ec1e693 332 *
333 * Returns: The currently-selected transport configuration string.
334 */
335
661c4bdc 336static APIRET APIENTRY rxfn_txconf(const char *fn, ULONG ac, RXSTRING *av,
337 const char *sn, RXSTRING *r)
2ec1e693 338{
661c4bdc 339 if (ac > 1)
2ec1e693 340 return (-1);
661c4bdc 341 if (ac > 0 && av[0].strptr) {
342 dstr d = DSTR_INIT;
343 int rc;
344 if (!tx)
345 return (-1);
346 rxs_get(&av[0], &d);
347 rc = tx_configure(tx, d.buf);
348 dstr_destroy(&d);
349 if (rc)
350 return (-1);
351 }
2ec1e693 352 rxs_putf(r, "%s", txconf ? txconf : "");
353 return (0);
354}
355
356/* --- @txinit([NAME], [FILE], [CONFIG])@ ---
357 *
358 * Arguments: @NAME@ = transport name to select
359 * @FILE@ = transport filename
360 * @CONFIG@ = transport configuration string
361 *
362 * Returns: ---
363 *
364 * Use: Initializes a transport using the given settings. Omitted
365 * arguments are filled in from the command line, or internal
366 * defaults.
367 */
368
661c4bdc 369static APIRET APIENTRY rxfn_txinit(const char *fn, ULONG ac, RXSTRING *av,
370 const char *sn, RXSTRING *r)
2ec1e693 371{
372 const char *n = txname, *f = txfile, *c = txconf;
373 dstr dn = DSTR_INIT, df = DSTR_INIT, dc = DSTR_INIT;
374
375 if (tx)
376 return (-1);
377 if (ac > 3)
378 return (-1);
379 if (ac >= 1 && av[0].strptr) {
380 rxs_get(&av[0], &dn);
381 n = dn.buf;
382 }
383 if (ac >= 2 && av[1].strptr) {
384 rxs_get(&av[1], &df);
385 f = df.buf;
386 }
387 if (ac >= 3 && av[2].strptr) {
661c4bdc 388 rxs_get(&av[2], &dc);
2ec1e693 389 c = dc.buf;
390 }
391 tx = tx_create(n, f, c);
392 dstr_destroy(&dn);
393 dstr_destroy(&df);
394 dstr_destroy(&dc);
395 if (!tx)
396 return (-1);
397 return (0);
398}
399
661c4bdc 400/* --- @txsend(STRING, [OPTION])@ --- *
2ec1e693 401 *
402 * Arguments: @STRING@ = string to send
661c4bdc 403 * @OPTION@ = `l' or `n' (for `linebreak' or `nolinebreak')
2ec1e693 404 *
405 * Returns: ---
406 *
407 * Use: Sends a string (exactly as written) to the transport.
408 */
409
661c4bdc 410static APIRET APIENTRY rxfn_txsend(const char *fn, ULONG ac, RXSTRING *av,
411 const char *sn, RXSTRING *r)
2ec1e693 412{
661c4bdc 413 if ((ac != 1 && ac != 2) || !tx || !av[0].strptr)
2ec1e693 414 return (-1);
415 tx_write(tx, av[0].strptr, av[0].strlength);
661c4bdc 416 if (ac == 1 || !av[1].strptr || !av[1].strlength ||
417 av[1].strptr[0] == 'l' || av[1].strptr[0] == 'L')
418 tx_newline(tx);
2ec1e693 419 return (0);
420}
421
422/* --- @txrecv([MILLIS])@ --- *
423 *
424 * Arguments: @MILLIS@ = how long (in milliseconds) to wait, or `forever'
425 *
426 * Returns: The string read (may be null if nothing available -- sorry).
427 *
428 * Use: Reads the next line from the transport. If @MILLIS@ is an
429 * integer, then give up after that many milliseconds of
430 * waiting; if it is `forever' (or anything beginning with an
431 * `f') then don't give up. The default is to wait forever.
432 */
433
661c4bdc 434static APIRET APIENTRY rxfn_txrecv(const char *fn, ULONG ac, RXSTRING *av,
435 const char *sn, RXSTRING *r)
2ec1e693 436{
437 txline *l;
438 unsigned long t = FOREVER;
439
440 if (ac > 1 || !tx)
441 return (-1);
442 if (ac >= 1 && rxs_block(&av[0], &t))
443 return (-1);
444
445 l = tx_read(tx, t);
446 if (!l)
447 r->strlength = 0;
448 else {
449 rxs_putm(r, l->s, l->len);
450 tx_freeline(l);
451 }
452 return (0);
453}
454
455/* --- @TXEOF()@ --- *
456 *
457 * Arguments: ---
458 *
459 * Returns: True if end-of-file has been seen on the transport, otherwise
460 * false.
461 */
462
661c4bdc 463static APIRET APIENTRY rxfn_txeof(const char *fn, ULONG ac, RXSTRING *av,
464 const char *sn, RXSTRING *r)
2ec1e693 465{
466 if (ac || !tx)
467 return (-1);
468 rxs_putf(r, "%d", tx->s == TX_CLOSED && !tx->ll);
469 return (0);
470}
471
472/* --- @txready([MILLIS])@ --- *
473 *
474 * Arguments: @MILLIS@ = how long (in milliseconds) to wait, or `forever'
475 *
476 * Returns: True if a line is ready, otherwise false.
477 *
478 * Use: Returns whether the transport is ready for reading. If
479 * @MILLIS@ is an integer, then wait for at most that many
480 * milliseconds before returning. If @MILLIS@ is `forever' (or
481 * anything beginning with `f') then wait forever for
482 * readiness. This isn't useless: it can trip the end-of-file
483 * detector. If @MILLIS@ is omitted, return immediately (as if
484 * 0 had been specified).
485 */
486
661c4bdc 487static APIRET APIENTRY rxfn_txready(const char *fn, ULONG ac, RXSTRING *av,
488 const char *sn, RXSTRING *r)
2ec1e693 489{
490 unsigned long t = 0;
491
492 if (ac > 1 || !tx)
493 return (-1);
494 if (ac >= 1 && rxs_block(&av[0], &t))
495 return (-1);
496 rxs_putf(r, "%d", !!tx_read(tx, t));
497 return (0);
498}
499
e9060e7e 500/* --- @AUPLAY(TAG, [FLAG])@ --- *
501 *
502 * Arguments: @TAG@ = audio sample tag to play
503 * @FLAG@ = a string to explain what to do more clearly.
504 *
505 * Returns: True if it succeeded.
506 *
507 * Use: Plays a sample. If @FLAG@ begins with `t', don't report
508 * errors if the sample can't be found.
509 */
510
511static APIRET APIENTRY rxfn_auplay(const char *fn, ULONG ac, RXSTRING *av,
512 const char *sn, RXSTRING *r)
513{
514 dstr d = DSTR_INIT;
515 int rc = 1;
516
517 if (ac < 1 || !av[0].strlength || ac > 2)
518 return (-1);
519 rxs_get(&av[0], &d);
520 if (ac > 1 && av[1].strlength >= 1 &&
521 (av[1].strptr[0] == 't' || av[1].strptr[0] == 'T'))
522 rc = au_tryplay(d.buf);
523 else
524 au_play(d.buf);
525 dstr_destroy(&d);
526 rxs_putf(r, "%d", rc);
527 return (0);
528}
529
530/* --- @AUFETCH(TAG)@ --- *
531 *
532 * Arguments: @TAG@ = audio sample tag to play
533 *
534 * Returns: True if it succeeded.
535 *
536 * Use: Prefetches a sample into the cache.
537 */
538
539static APIRET APIENTRY rxfn_aufetch(const char *fn, ULONG ac, RXSTRING *av,
540 const char *sn, RXSTRING *r)
541{
542 dstr d = DSTR_INIT;
543 int rc = 0;
544 au_sample *s;
545 au_data *a;
546
547 if (ac < 1 || !av[0].strlength || ac > 1)
548 return (-1);
549 rxs_get(&av[0], &d);
550 if ((s = au_find(d.buf)) != 0 &&
551 (a = au_fetch(s)) != 0) {
552 au_free(a);
553 rc = 1;
554 }
555 dstr_destroy(&d);
556 rxs_putf(r, "%d", rc);
557 return (0);
558}
559
560/* --- @AUNUM(TAG)@ --- *
561 *
562 * Arguments: @NUM@ = a number to be read
563 *
564 * Returns: ---
565 *
566 * Use: Reads a number aloud to the audio device.
567 */
568
569static APIRET APIENTRY rxfn_aunum(const char *fn, ULONG ac, RXSTRING *av,
570 const char *sn, RXSTRING *r)
571{
572 dstr d = DSTR_INIT;
573
574 if (ac < 1 || !av[0].strlength || ac > 1)
575 return (-1);
576 rxs_get(&av[0], &d);
577 aunum(d.buf);
578 dstr_destroy(&d);
579 return (0);
580}
581
af666e6f 582/* --- @AUCACHE([FLAG], [VALUE, ...]@ --- *
583 *
584 * Arguments: @FLAG@ = operation to perform
585 *
586 * Returns: Dependent on operation.
587 *
588 * Use: If @FLAG@ is omitted or `Info', returns audio cache usage
589 * information as words in the following order:
590 *
591 * sz_max Maximum allowed cache size
592 * sz_total Total size used by samples
593 * sz_spare Size used by `spare' samples
594 * sz_queue Size used by queued samples
595 * n_total Total number of cached samples
596 * n_spare Number of `spare' samples
597 * n_queue Number of queued samples
598 * hits Number of cache hits
599 * misses Number of cache misses
600 *
601 * If @FLAG@ is `Max', sets the maximum cache size to the first
602 * @VALUE@ (if set), and returns the old maximum on its own.
603 *
604 * If @FLAG@ is `Usage', returns the `sz_*' items, as a list of
605 * words.
606 *
607 * If @FLAGS@ is `Numbers', returns the `n_*' items, as a list
608 * of words.
609 *
610 * If @FLAGS@ is `Hits', returns `hits' and `misses' as a pair
611 * of words.
612 */
613
614static APIRET APIENTRY rxfn_aucache(const char *fn, ULONG ac, RXSTRING *av,
615 const char *sn, RXSTRING *r)
616{
617 int i = 1;
618 au_cacheinfo c;
619
620 au_getcacheinfo(&c);
621 if (ac < 1 || !av[0].strlength)
622 goto info;
623 switch (av[0].strptr[0]) {
624 case 'i': case 'I': info:
625 rxs_putf(r, "%lu %lu %lu %lu %u %u %u %lu %lu",
626 (unsigned long)c.sz_max, (unsigned long)c.sz_total,
627 (unsigned long)c.sz_spare, (unsigned long)c.sz_queue,
628 c.n_total, c.n_spare, c.n_total, c.hits, c.misses);
629 break;
630 case 'm': case 'M':
631 if (ac > i) {
632 long max;
633 if (rxs_tol(&av[i], &max))
634 return (-1);
635 au_setcachelimit(max);
636 i++;
637 }
638 rxs_putf(r, "%lu", (unsigned long)c.sz_max);
639 break;
640 case 'u': case 'U':
641 rxs_putf(r, "%lu %lu %lu %lu",
642 (unsigned long)c.sz_max, (unsigned long)c.sz_total,
643 (unsigned long)c.sz_spare, (unsigned long)c.sz_queue);
644 break;
645 case 'n': case 'N':
646 rxs_putf(r, "%u %u %u", c.n_total, c.n_spare, c.n_total);
647 break;
648 case 'h': case 'H':
649 rxs_putf(r, "%lu %lu", c.hits, c.misses);
650 break;
651 default:
652 return (-1);
653 }
654 if (i > ac)
655 return (-1);
656 return (0);
657}
658
2ec1e693 659/* --- @MILLIWAIT(MILLIS)@ --- *
660 *
661 * Arguments: @MILLIS@ = how long (in milliseconds) to wait
662 *
663 * Returns: ---
664 *
665 * Use: Waits for @MILLIS@ milliseconds. Always.
666 */
667
661c4bdc 668static APIRET APIENTRY rxfn_milliwait(const char *fn, ULONG ac, RXSTRING *av,
669 const char *sn, RXSTRING *r)
2ec1e693 670{
671 long l;
672 struct timeval tv;
673
674 if (ac != 1 || !av[0].strptr)
675 return (-1);
676 if (rxs_tol(&av[0], &l) || l < 0)
677 return (-1);
678 tv.tv_sec = l / 1000;
679 tv.tv_usec = (l % 1000) * 1000;
680 select(0, 0, 0, 0, &tv);
681 return (0);
682}
683
684/*----- Initialization ----------------------------------------------------*/
685
686struct rxfntab { char *name; RexxFunctionHandler *fn; };
687
688static const struct rxfntab rxfntab[] = {
661c4bdc 689 { "test", rxfn_test },
690 { "txname", rxfn_txname },
691 { "txfile", rxfn_txfile },
692 { "txconf", rxfn_txconf },
693 { "txinit", rxfn_txinit },
694 { "txsend", rxfn_txsend },
695 { "txrecv", rxfn_txrecv },
696 { "txeof", rxfn_txeof },
697 { "txready", rxfn_txready },
e9060e7e 698 { "auplay", rxfn_auplay },
699 { "aufetch", rxfn_aufetch },
af666e6f 700 { "aucache", rxfn_aucache },
e9060e7e 701 { "aunum", rxfn_aunum },
661c4bdc 702 { "milliwait", rxfn_milliwait },
2ec1e693 703 { 0, 0 }
704};
705
706/* --- @rx_init@ --- *
707 *
708 * Arguments: ---
709 *
710 * Returns: ---
711 *
712 * Use: Initializes the REXX external functions.
713 */
714
715void rx_init(void)
716{
717 const struct rxfntab *f;
718 int rc;
719
720 for (f = rxfntab; f->fn; f++) {
721 if ((rc = RexxRegisterFunctionExe(f->name, f->fn)) != 0) {
722 err_report(ERR_RXGLUE, ERRRX_INIT, rc,
723 "couldn't register function `%s' (code %d)", f->name, rc);
724 abort();
725 }
726 }
727}
728
729/*----- Running REXX programs ---------------------------------------------*/
730
731/* --- @rx_run@ --- *
732 *
733 * Arguments: @const char *name@ = pointer to filename (or null)
734 * @const void *p@ = pointer to program text
735 * @size_t sz@ = size of program text
736 * @int ac@ = number of arguments
737 * @const char *const *av@ = vector of command-line arguments
738 *
739 * Returns: Exit code from program.
740 *
741 * Use: Runs a REXX script from memory.
742 */
743
744int rx_run(const char *name, const void *p, size_t sz,
745 int ac, const char *const *av)
746{
747 RXSTRING prog[2];
748 RXSTRING *argv;
749 RXSTRING res;
750 dstr d = DSTR_INIT;
751 short badrc;
752 int rc;
753 int i;
754
755 /* --- Set things up --- */
756
757 if (!name)
758 name = "incore";
759 MAKERXSTRING(prog[0], (void *)p, sz);
760 MAKERXSTRING(prog[1], 0, 0);
661c4bdc 761 argv = rx_alloc(ac * sizeof(*argv));
2ec1e693 762 for (i = 0; i < ac; i++)
763 MAKERXSTRING(argv[i], (char *)av[i], strlen(av[i]));
764
765 /* --- Run the script --- */
766
767 MAKERXSTRING(res, 0, 0);
661c4bdc 768 rc = RexxStart(ac, argv, name, prog,
769 "SYSTEM", RXSUBROUTINE, 0, &badrc, &res);
2ec1e693 770 if (rc) {
661c4bdc 771 rx_free(RXSTRPTR(res));
772 rx_free(argv);
2ec1e693 773 if (rc < 0)
774 err_report(ERR_RXERR, 0, -rc, "rexx error from script `%s'", name);
775 else
776 err_report(ERR_RXGLUE, ERRRX_INTERP, rc, "intepreter internal error");
777 return (-1);
778 }
779
780 /* --- Pick apart the results --- */
781
782 dstr_putm(&d, RXSTRPTR(res), RXSTRLEN(res));
661c4bdc 783 rx_free(RXSTRPTR(res));
784 rx_free(argv);
2ec1e693 785 dstr_putz(&d);
786 rc = atoi(d.buf);
787 dstr_destroy(&d);
788 return (rc);
789}
790
791/* --- @rx_runfile@ --- *
792 *
793 * Arguments: @const char *name@ = pointer to filename
794 * @int ac@ = number of command-line arguments
795 * @const char *const *av@ = vector of command-line arguments
796 *
797 * Returns: Exit code from program.
798 *
799 * Use: Runs a REXX script from a file, given its name.
800 */
801
802int rx_runfile(const char *name, int ac, const char *const *av)
803{
804 FILE *fp;
805 dstr d = DSTR_INIT;
806 char buf[BUFSIZ];
807 size_t n;
808 int rc;
809
810 /* --- Read the file into memory --- *
811 *
812 * This way avoids any crapness in the REXX implementation and means we can
813 * report errors in a more sensible way.
814 */
815
816 if ((fp = fopen(name, "r")) == 0)
817 goto fail_0;
818 do {
819 n = fread(buf, 1, sizeof(buf), fp);
820 DPUTM(&d, buf, n);
821 } while (n == sizeof(buf));
822 if (ferror(fp))
823 goto fail_1;
824 fclose(fp);
825
826 /* --- Now do the from-memory thing --- */
827
828 rc = rx_run(name, d.buf, d.len, ac, av);
829 dstr_destroy(&d);
830 return (rc);
831
832 /* --- Tidy up on errors --- */
833
834fail_1:
835 dstr_destroy(&d);
836 fclose(fp);
837fail_0:
838 err_report(ERR_RXGLUE, ERRRX_SCRIPTREAD, errno,
839 "couldn't read script `%s': %s", name, strerror(errno));
840 return (-1);
841}
842
843/*----- That's all, folks -------------------------------------------------*/