/* This file is part of the Q programming system. The Q programming system is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. The Q programming system is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #if defined (HAVE_CONFIG_H) # include "config.h" #endif /* system headers */ #include #include #include #include /* check for standard C headers */ #if STDC_HEADERS # include # include #else # ifndef HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); #endif #ifdef HAVE_MALLOC_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #include #if HAVE_SYS_WAIT_H # include #endif #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif #ifndef WIFEXITED # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef WIN32 #include #endif #include #include #ifdef DMALLOC #include #endif MODULE(dxl) #ifndef HAVE_STRDUP static char *strdup(char *s) { char *t; return ((t=malloc(strlen(s)+1))?strcpy(t, s):NULL); } #endif /* handle SIGINT and SIGTERM */ #if RETSIGTYPE == void #define SIGHANDLER_RETURN(status) return #else #define SIGHANDLER_RETURN(status) return status #endif #ifdef MUST_REINSTALL_SIGHANDLERS #define SIGHANDLER_RESTORE(sig,handler) syssignal(sig,handler) #else #define SIGHANDLER_RESTORE(sig,handler) /* nop */ #endif typedef RETSIGTYPE (*sighandler_t)(int); static sighandler_t syssignal(sig, handler) int sig; sighandler_t handler; { #ifdef HAVE_POSIX_SIGNALS struct sigaction new_action, old_action; new_action.sa_handler = handler; sigemptyset(&new_action.sa_mask); sigemptyset(&old_action.sa_mask); new_action.sa_flags = 0; sigaction(sig, &new_action, &old_action); return old_action.sa_handler; #else return signal(sig, handler); #endif } static volatile int brkflag = 0; static sighandler_t int_handler = NULL, term_handler = NULL, hup_handler = NULL; static RETSIGTYPE break_handler(int sig) { if (sig == SIGINT && int_handler) int_handler(sig); if (sig == SIGTERM && term_handler) term_handler(sig); #ifdef SIGHUP if (sig == SIGHUP && hup_handler) hup_handler(sig); #endif SIGHANDLER_RESTORE(sig, break_handler); brkflag = 1; SIGHANDLER_RETURN(0); } /* take a short sleep (approx 1/100 secs) */ static void sleep_some(void) { #ifdef WIN32 Sleep(10); #else #ifdef HAVE_NANOSLEEP struct timespec req; req.tv_sec = 0; req.tv_nsec = 10000000; nanosleep(&req, NULL); #else #ifdef HAVE_USLEEP usleep(10000); #else sleep(1); #endif #endif #endif } /* DXHandle struct */ typedef struct qentry { char *s, *t; struct qentry *next; } qentry; typedef struct { volatile int rdy; /* lost connection indicator */ DXLConnection *conn; /* DXLink connection */ qentry *head, *tail; /* message queue */ } DXLHandle; /* message queue operations */ static void initqueue(DXLHandle *h) { h->head = h->tail = NULL; } static void freequeue(DXLHandle *h) { qentry *head, *next; for (head = h->head; head; head = next) { next = head->next; if (head->s) free(head->s); if (head->t) free(head->t); free(head); } h->head = h->tail = NULL; } static int emptyqueue(DXLHandle *h) { return h->head == NULL; } static char *head_s(DXLHandle *h) { if (h->head) return h->head->s; else return NULL; } static char *head_t(DXLHandle *h) { if (h->head) return h->head->t; else return NULL; } static void enqueue(DXLHandle *h, char *s, char *t) { qentry *next = malloc(sizeof(qentry)); if (!next) { /* FIXME: do some more appropriate error handling here */ h->rdy = 0; return; } next->s = s; next->t = t; next->next = NULL; if (h->head) { h->tail->next = next; h->tail = next; } else h->head = h->tail = next; } static void dequeue(DXLHandle *h) { qentry *next; if (!h->head) return; next = h->head->next; free(h->head); if (next) h->head = next; else h->head = h->tail = NULL; } /* check handle for validity */ static int valid(DXLHandle *h) { if (h->rdy) if (h->conn) return 1; else return (h->rdy = 0); else if (h->conn) { /* close the connection on our side */ DXLCloseConnection(h->conn); h->conn = NULL; freequeue(h); return 0; } else { freequeue(h); return 0; } } /* close a DXLHandle structure */ static void close_handle(DXLHandle *h) { h->rdy = 0; valid(h); } /* message handlers */ static void conn_lost(DXLConnection *conn, void *data) { DXLHandle *h = data; h->rdy = 0; } static void err_handler(DXLConnection *conn, const char *msg, void *data) { DXLHandle *h = data; enqueue(h, NULL, strdup(msg)); } static void val_handler(DXLConnection *conn, const char *label, const char *value, void *data) { DXLHandle *h = data; char *s = strdup(label), *t = strdup(value); if (s && t) enqueue(h, s, t); else { if (s) { free(s); h->head->s = NULL; } if (t) { free(t); h->head->t = NULL; } } } DESTRUCTOR(dxl,DXLHandle,v) { DXLHandle *h = v; close_handle(h); free(h); } FUNCTION(dxl,dxl_start,argc,argv) { char *s; if (argc == 1 && isstr(argv[0], &s)) { DXLHandle *h; if ((h = (DXLHandle*)malloc(sizeof(DXLHandle)))) { h->conn = DXLStartDX(s, NULL); if (h->conn) { h->rdy = 1; initqueue(h); DXLSetBrokenConnectionCallback(h->conn, conn_lost, h); DXLSetErrorHandler(h->conn, err_handler, h); return mkobj(type(DXLHandle), h); } else { free(h); return __FAIL; } } else return __ERROR; } else return __FAIL; } FUNCTION(dxl,dxl_close,argc,argv) { DXLHandle *h; if (argc == 1 && isobj(argv[0], type(DXLHandle), (void**)&h)) { if (valid(h)) { close_handle(h); return mkvoid; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_exit,argc,argv) { DXLHandle *h; if (argc == 1 && isobj(argv[0], type(DXLHandle), (void**)&h)) { if (valid(h)) { DXLError res = DXLExitDX(h->conn); h->conn = NULL; close_handle(h); return (res == OK)?mkvoid:__FAIL; } else { return __FAIL; } } else return __FAIL; } FUNCTION(dxl,dxl_load,argc,argv) { DXLHandle *h; char *s; if (argc == 2 && isobj(argv[0], type(DXLHandle), (void**)&h) && isstr(argv[1], &s)) { if (valid(h)) { DXLError res = DXLLoadVisualProgram(h->conn, s); return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_load_macro,argc,argv) { DXLHandle *h; char *s; if (argc == 2 && isobj(argv[0], type(DXLHandle), (void**)&h) && isstr(argv[1], &s)) { if (valid(h)) { DXLError res = DXLLoadMacroFile(h->conn, s); return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_load_script,argc,argv) { DXLHandle *h; char *s; if (argc == 2 && isobj(argv[0], type(DXLHandle), (void**)&h) && isstr(argv[1], &s)) { if (valid(h)) { DXLError res = exDXLLoadScript(h->conn, s); return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_load_config,argc,argv) { DXLHandle *h; char *s; if (argc == 2 && isobj(argv[0], type(DXLHandle), (void**)&h) && isstr(argv[1], &s)) { if (valid(h)) { DXLError res = uiDXLLoadConfig(h->conn, s); return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl,argc,argv) { DXLHandle *h; char *s; if (argc == 2 && isobj(argv[0], type(DXLHandle), (void**)&h) && isstr(argv[1], &s)) { if (valid(h)) { DXLError res = DXLSend(h->conn, s); return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_begin_macro,argc,argv) { DXLHandle *h; char *s; if (argc == 2 && isobj(argv[0], type(DXLHandle), (void**)&h) && isstr(argv[1], &s)) { if (valid(h)) { DXLError res = exDXLBeginMacroDefinition(h->conn, s); return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_end_macro,argc,argv) { DXLHandle *h; if (argc == 1 && isobj(argv[0], type(DXLHandle), (void**)&h)) { if (valid(h)) { DXLError res = exDXLEndMacroDefinition(h->conn); return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_exec_once,argc,argv) { DXLHandle *h; if (argc == 1 && isobj(argv[0], type(DXLHandle), (void**)&h)) { if (valid(h)) { DXLError res = DXLExecuteOnce(h->conn); return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_exec_on_change,argc,argv) { DXLHandle *h; if (argc == 1 && isobj(argv[0], type(DXLHandle), (void**)&h)) { if (valid(h)) { DXLError res = DXLExecuteOnChange(h->conn); return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_exec_once_named,argc,argv) { DXLHandle *h; char *s, *t; if (argc == 3 && isobj(argv[0], type(DXLHandle), (void**)&h) && isstr(argv[1], &s)) { if (valid(h)) { expr x, x1, x2; long i; int n; for (n = 0, x = argv[2]; iscons(x, &x1, &x2) && isstr(x1, &t); x = x2, n++) if (n == INT_MAX-1) { return __ERROR; } if (isnil(x)) { char **a = (char**)malloc((n+1)*sizeof(char*)); if (a) { int res; for (n = 0, x = argv[2]; iscons(x, &x1, &x2) && isstr(x1, &t); x = x2, n++) a[n] = t; a[n] = NULL; res = exDXLExecuteOnceNamedWithArgsV(h->conn, s, a); free(a); return (res == OK)?mkvoid:__FAIL; } else { return __ERROR; } } else return __FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_exec_on_change_named,argc,argv) { DXLHandle *h; char *s, *t; if (argc == 3 && isobj(argv[0], type(DXLHandle), (void**)&h) && isstr(argv[1], &s)) { if (valid(h)) { expr x, x1, x2; long i; int n; for (n = 0, x = argv[2]; iscons(x, &x1, &x2) && isstr(x1, &t); x = x2, n++) if (n == INT_MAX-1) { return __ERROR; } if (isnil(x)) { char **a = (char**)malloc((n+1)*sizeof(char*)); if (a) { int res; for (n = 0, x = argv[2]; iscons(x, &x1, &x2) && isstr(x1, &t); x = x2, n++) a[n] = t; a[n] = NULL; res = exDXLExecuteOnChangeNamedWithArgsV(h->conn, s, a); free(a); return (res == OK)?mkvoid:__FAIL; } else { return __ERROR; } } else return __FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_end_exec,argc,argv) { DXLHandle *h; if (argc == 1 && isobj(argv[0], type(DXLHandle), (void**)&h)) { if (valid(h)) { DXLError res = DXLEndExecution(h->conn); return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_end_exec_on_change,argc,argv) { DXLHandle *h; if (argc == 1 && isobj(argv[0], type(DXLHandle), (void**)&h)) { if (valid(h)) { DXLError res = DXLEndExecuteOnChange(h->conn); return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_seq_ctl,argc,argv) { static int seq_loop_off, seq_loop_on, seq_palindrome_off, seq_palindrome_on, seq_play_backward, seq_play_forward, seq_step, seq_pause, seq_stop; static int init = 0; DXLHandle *h; if (!init) { init = 1; seq_loop_off = sym(seq_loop_off); seq_loop_on = sym(seq_loop_on); seq_palindrome_off = sym(seq_palindrome_off); seq_palindrome_on = sym(seq_palindrome_on); seq_play_backward = sym(seq_play_backward); seq_play_forward = sym(seq_play_forward); seq_step = sym(seq_step); seq_pause = sym(seq_pause); seq_stop = sym(seq_stop); } if (argc == 2 && isobj(argv[0], type(DXLHandle), (void**)&h)) { if (valid(h)) { DXLError res; if (exprsym(argv[1]) == seq_step) res = DXLSequencerCtl(h->conn, SeqStep); else if (exprsym(argv[1]) == seq_pause) res = DXLSequencerCtl(h->conn, SeqPause); else if (exprsym(argv[1]) == seq_stop) res = DXLSequencerCtl(h->conn, SeqStop); else if (exprsym(argv[1]) == seq_loop_off) res = DXLSequencerCtl(h->conn, SeqLoopOff); else if (exprsym(argv[1]) == seq_loop_on) res = DXLSequencerCtl(h->conn, SeqLoopOn); else if (exprsym(argv[1]) == seq_palindrome_off) res = DXLSequencerCtl(h->conn, SeqPalindromeOff); else if (exprsym(argv[1]) == seq_palindrome_on) res = DXLSequencerCtl(h->conn, SeqPalindromeOn); else if (exprsym(argv[1]) == seq_play_backward) res = DXLSequencerCtl(h->conn, SeqPlayBackward); else if (exprsym(argv[1]) == seq_play_forward) res = DXLSequencerCtl(h->conn, SeqPlayForward); else return __FAIL; return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_render_mode,argc,argv) { DXLHandle *h; char *s; long n; if (argc == 3 && isobj(argv[0], type(DXLHandle), (void**)&h) && isstr(argv[1], &s) && isint(argv[2], &n) && n >= 0 && n <= 1) { if (valid(h)) { DXLError res = uiDXLSetRenderMode(h->conn, s, n); return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_reset,argc,argv) { DXLHandle *h; if (argc == 1 && isobj(argv[0], type(DXLHandle), (void**)&h)) { if (valid(h)) { DXLError res = uiDXLResetServer(h->conn); return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } static int chk(DXLHandle *h) { brkflag = 0; while (!brkflag && valid(h) && DXLIsMessagePending(h->conn)) DXLHandlePendingMessages(h->conn); return !brkflag && valid(h); } FUNCTION(dxl,dxl_ready,argc,argv) { DXLHandle *h; if (argc == 1 && isobj(argv[0], type(DXLHandle), (void**)&h)) { chk(h); if (brkflag) return __FAIL; else { int res = valid(h); return res?mktrue:mkfalse; } } else return __FAIL; } FUNCTION(dxl,dxl_busy,argc,argv) { DXLHandle *h; if (argc == 1 && isobj(argv[0], type(DXLHandle), (void**)&h)) { if (chk(h)) { int status; if (DXLGetExecutionStatus(h->conn, &status) == OK) { return status?mktrue:mkfalse; } else return __FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_wait,argc,argv) { DXLHandle *h; if (argc == 1 && isobj(argv[0], type(DXLHandle), (void**)&h)) { int status; while (chk(h) && DXLGetExecutionStatus(h->conn, &status) == OK && status) { release_lock(); sleep_some(); acquire_lock(); } if (brkflag) return __FAIL; else return mkvoid; } else return __FAIL; } FUNCTION(dxl,dxl_check,argc,argv) { DXLHandle *h; if (argc == 1 && isobj(argv[0], type(DXLHandle), (void**)&h)) { if (chk(h)) { int res = emptyqueue(h); return res?mkfalse:mktrue; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_input,argc,argv) { DXLHandle *h; char *s, *t; if (argc == 3 && isobj(argv[0], type(DXLHandle), (void**)&h) && isstr(argv[1], &s) && isstr(argv[2], &t)) { if (valid(h)) { DXLError res = DXLSetValue(h->conn, s, t); return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_output,argc,argv) { DXLHandle *h; char *s; if (argc == 2 && isobj(argv[0], type(DXLHandle), (void**)&h) && isstr(argv[1], &s)) { if (valid(h)) { DXLError res = DXLSetValueHandler(h->conn, s, val_handler, h); return (res == OK)?mkvoid:__FAIL; } else return __FAIL; } else return __FAIL; } FUNCTION(dxl,dxl_read,argc,argv) { DXLHandle *h; if (argc == 1 && isobj(argv[0], type(DXLHandle), (void**)&h)) { while (chk(h) && emptyqueue(h)) { release_lock(); sleep_some(); acquire_lock(); } if (emptyqueue(h)) return __FAIL; else if (!head_s(h) && !head_t(h)) { dequeue(h); return __ERROR; } else { expr x; if (head_s(h)) x = mktuplel(2, mkstr(head_s(h)), mkstr(head_t(h))); else x = mkapp(mksym(sym(dxl_error)), mkstr(head_t(h))); dequeue(h); if (x) return x; else return __ERROR; } } else return __FAIL; } INIT(dxl) { int_handler = syssignal(SIGINT, break_handler); term_handler = syssignal(SIGTERM, break_handler); #ifdef SIGHUP hup_handler = syssignal(SIGHUP, break_handler); #endif }