1 /* Copyright (C) 1992-1998 The Geometry Center
2  * Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips
3  * Copyright (C) 2006-2007 Claus-Justus Heine
4  *
5  * This file is part of Geomview.
6  *
7  * Geomview is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published
9  * by the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * Geomview is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with Geomview; see the file COPYING.  If not, write
19  * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
20  * USA, or visit http://www.gnu.org.
21  */
22 
23 #if HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26 
27 #if 0
28 static char copyright[] = "Copyright (C) 1992-1998 The Geometry Center\n\
29 Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips";
30 #endif
31 
32 
33 /* Authors: Stuart Levy, Tamara Munzner, Mark Phillips */
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #ifdef NeXT
38 #include <bsd/libc.h>
39 #else /* any other reasonable UNIX */
40 #include <unistd.h>
41 #endif
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <signal.h>
45 #include <sys/wait.h>
46 #include <string.h>
47 #include <ctype.h>
48 #include <limits.h>
49 #include <sys/socket.h>
50 #include <sys/un.h>
51 #include <errno.h>
52 #include "ooglutil.h"
53 #include "drawer.h"
54 #include "ui.h"
55 #include "handle.h"
56 #include "handleP.h"
57 #include "streampool.h"
58 #include "main.h"
59 #include "event.h"
60 #include "comm.h"
61 #include "lang.h"
62 #include "lisp.h"
63 #include "transobj.h"
64 #include "ntransobj.h"
65 
66 extern HandleOps CamOps, GeomOps, TransOps, NTransOps, CommandOps, WindowOps;
67 extern HandleOps ImageOps, AppearanceOps;
68 
69 int gv_debug = 0;
70 
71 static int commandimport(Pool *, Handle **, Ref **);
72 static int commandclose(Pool *);
73 static int emodule_commandclose(Pool *);
74 static int listenimport(Pool *, Handle **, Ref **);
75 static void useconnection( char *name, HandleOps *ops, Handle *h, Ref *obj, int unique );
76 
77 static void MyPoolDelete(Pool *p);
78 
79 
80 HandleOps CommandOps = {
81   "command_language",
82   commandimport,
83   NULL,
84   NULL,
85   NULL,
86   commandclose
87 };
88 
89 static HandleOps emoduleCommandOps = {
90   "command_language",
91   commandimport,
92   NULL,
93   NULL,
94   NULL,
95   emodule_commandclose,
96 };
97 
98 #if HAVE_UNIX_SOCKETS
99 static HandleOps listenOps = {	/* "Ops" structure for listening sockets */
100   "socket_listener",
101   listenimport,	/* "read" routine really spawns a new connection's
102 		 * data socket
103 		 */
104   NULL,
105   NULL,
106   NULL,
107   NULL
108 };
109 #endif
110 
ign_read(int fd,void * buffer,size_t size)111 static int ign_read(int fd, void *buffer, size_t size)
112 {
113   int result;
114 
115   result = read(fd, buffer, size);
116 
117   return result;
118 }
119 
120 static int
commandimport(Pool * p,Handle ** unused,Ref ** unused_too)121 commandimport(Pool *p, Handle **unused, Ref **unused_too )
122 {
123   char *w, *raww;
124   int c;
125   int ok = 0;
126   IOBFILE *inf;
127   Lake *lake;
128 
129   if ((inf = PoolInputFile(p)) == NULL)
130     goto done;
131 
132   if ((c = async_iobfnextc(inf,0)) == NODATA)
133     return 1;		/* pretend we got something. */
134 
135   if ((lake=(Lake*)PoolClientData(p)) == NULL) {
136     lake = LakeDefine(p->inf, p->outf, p);
137     PoolSetClientData(p, (void*)lake);
138   } else if (lake->streamin != inf) {
139     lake->streamin = inf;
140     lake->streamout = PoolOutputFile(p);
141   }
142 
143   switch(c) {
144   case '<':
145     iobfgetc(inf);
146     w = iobfdelimtok("()", inf, 0);
147     if (w == NULL)
148       goto done;
149     if (strcmp(w, "-") && (w = findfile(PoolName(p), raww = w)) == NULL) {
150       OOGLSyntax(inf, "Reading commands from \"%s\": can't find command file %s",
151 		 PoolName(p), raww);
152       goto done;
153     }
154     p = PoolStreamOpen(w, NULL, 0, &CommandOps);
155     if (iobfile(PoolInputFile(p)) == stdin && !PoolOutputFile(p))
156       p = PoolStreamOpen(PoolName(p), stdout, 1, &CommandOps);
157     if (p != NULL && inf != NULL)
158       ok = comm_object(w, &CommandOps, NULL, NULL, COMM_LATER);
159     break;
160   case '(':
161     {
162       LObject *val;
163       val = LEvalSexpr(lake);
164       ok = (val != Lnil) ? 1 : -1;
165       LFree(val);
166     }
167     break;
168   default:
169     {
170       LFree( LEvalSexpr(lake) );
171     }
172   }
173  done:
174   return ok;
175 }
176 
177 /*
178  * What is this file?  Returns
179  *  -1 : nonexistent
180  *   0 : is a plain file
181  *   1 : is something else, probably a named pipe or tty
182  */
183 static int
ispipe(char * fname)184 ispipe(char *fname)
185 {
186   struct stat st;
187 
188   if (stat(fname, &st) < 0)
189     return -1;
190   return (st.st_mode & S_IFMT) != S_IFREG;
191 }
192 
193 int
comm_object(char * str,HandleOps * ops,Handle ** hp,Ref ** rp,int now)194 comm_object(char *str, HandleOps *ops, Handle **hp, Ref **rp, int now)
195 {
196   int c, ok = 0;
197   Pool *p;
198 
199   if (str == NULL)
200     return 0;
201   if (strcmp(str, "-") == 0 || access(str, 0) == 0) {
202     Handle *h = HandleReferringTo('<', str, ops, hp);
203     /*
204      * If we haven't read something from this file yet,
205      * forget it.
206      */
207     if (h) {
208       if (HandleObject(h)) {
209 	ok = 1;
210 	if (rp) {
211 	  HandleUpdRef(&h, NULL, rp);
212 	}
213       } else if (((p = PoolByName(HandleName(h), ops))) == NULL ||
214 		 (p->flags & PF_ANY) || (!p->seekable && !now)) {
215 
216 	/* When reading plain files, always demand an object. When
217 	 * reading others (pipe, tty), demand one if 'now' set. Use
218 	 * PF_ANY flag as an alternate hint of reading an object,
219 	 * since reading commands leaves no object attached to h.
220 	 */
221 	ok = 1;
222       } else {
223 	/* Failed */
224 	HandleDelete(h);
225 	if (hp) {
226 	  *hp = NULL;
227 	}
228       }
229     }
230     /* If not ok, close the file.
231      * If 'now' and not some sort of pipe, also close the file.
232      */
233     if ((p = PoolByName(str, ops)) != NULL && (!ok || (now && p->seekable))) {
234       if (now && ok) {
235 	/* Read as much as possible if we need it right now. */
236 	while(PoolInputFile(p) != NULL &&
237 	      (c = async_iobfnextc(PoolInputFile(p), 0)) != NODATA &&
238 	      c != EOF && (*ops->strmin)(p, hp, rp))
239 	  ;
240       }
241       PoolClose(p);
242       MyPoolDelete(p);
243     } else if (iobfile(PoolInputFile(p)) == stdin
244 	       && PoolOutputFile(p) == NULL) {
245       p = PoolStreamOpen(PoolName(p), stdout, 1, ops);
246     }
247     return ok;
248   } else if (strpbrk(str, "({ \t\n")) {
249     static Pool *pcache;	/* Cache a pool for handling strings */
250     static bool inuse = false;	/* Use cached pool unless already in use */
251     IOBFILE *inf = iobfileopen(fmemopen(str, strlen(str), "rb"));
252     /* Caching implies this first pool has a long lifetime;
253      * suitable for expressing (interest (...))
254      */
255     if (!inuse) {
256       if ((p = pcache) == NULL) {
257 	p = pcache = PoolStreamTemp(str, inf, stdout, 2, ops);
258       } else {
259 	p->inf  = inf; /* hack */
260 	p->outf = stdout; /* hack */
261       }
262       inuse = true;
263     } else {
264       p = PoolStreamTemp(str, inf, stdout, 2, ops);
265     }
266     if (p == NULL) {
267       return 0;		/* Failed */
268     }
269     while(iobfnextc(inf, 0) != EOF) {
270       ok = (*ops->strmin)(p, hp, rp);
271     }
272     PoolClose(p);
273     if (p == pcache) {
274       inuse = false;
275     } else {
276       MyPoolDelete(p); /* Delete temp pool unless it's our cached one */
277     }
278   } else {
279     /* Print the "No such file..." error left by access() */
280     fprintf(stderr, "%s: %s\n", str, sperror());
281   }
282   return ok;
283 }
284 
285 
286 /*
287  * Arrange that later attempts to read the same file will actually re-read it.
288  *
289  * cH: above comment is crap: at least since v1.6 a PoolClose() would
290  * result in actually closing the file, so a new PoolStreamOpen()
291  * would position right at the start of the data. We just delete the
292  * pool.
293  */
294 static int
commandclose(Pool * p)295 commandclose(Pool *p)
296 {
297   PoolClose(p);
298   MyPoolDelete(p);
299   return 0;
300 }
301 
302 
303 LDEFINE(setenv, LVOID,
304 	"(setenv  name string)\n"
305 	"Sets the environment variable ``name'' to the value ``string''; the "
306 	"name is visible to geomview (as in pathnames containing $name) "
307 	"and to processes it creates, e.g. external modules.")
308 {
309   char *name, *string = NULL;
310   char buf[10240];
311 
312   LDECLARE(("setenv", LBEGIN,
313 	    LSTRING, &name,
314 	    LSTRING, &string,
315 	    LEND));
316   sprintf(buf, "%s=%s", name, string);
317   envexpand(buf+strlen(name)+1);
318   putenv(strdup(buf));
319   return Lt;
320 }
321 
322 
323 LDEFINE(input_translator, LVOID,
324 	"(input-translator  \"#prefix_string\"  \"Bourne-shell-command\")\n"
325 	"Defines an external translation program for special input types. "
326 	"When asked to read a file which begins with the specified string, "
327 	"Geomview invokes that program with standard input coming from the "
328 	"given file. The program is expected to emit OOGL geometric data to "
329 	"its standard output. In this implementation, only prefixes beginning "
330 	"with # are recognized.")
331 {
332   char *prefix, *cmd;
333   LDECLARE(("input-translator", LBEGIN,
334 	    LSTRING, &prefix,
335 	    LSTRING, &cmd,
336 	    LEND));
337   GeomAddTranslator(prefix, cmd);
338   return Lt;
339 }
340 
341 /************************************************************************/
342 
343 /*
344  * Signal handling routines.
345  * Any code here must be careful not to do anything unsafe at signal time,
346  * e.g. calls to stdio routines or malloc.  These routines just examine and
347  * change fields in existing data structures.
348  */
349 
350 /*
351  * Handle death of a child process -- possibly an emodule.
352  * If it is, mark it dead.
353  */
354 static void
comm_sigchld(int sig)355 comm_sigchld(int sig)
356 {
357 #if HAVE_WAITPID
358   int status;
359   int pid = waitpid(-1, &status, WNOHANG);
360 #elif HAVE_WAIT3
361   union wait status;
362   int pid = wait3(&status, WNOHANG|WUNTRACED, NULL);
363 #else
364 # error FIXME
365 #endif
366 
367   if (WIFEXITED(status) || WIFSIGNALED(status)) {
368     emodule *em = VVEC(uistate.emod, emodule);
369     int i;
370     for(i = 0; i < VVCOUNT(uistate.emod); i++, em++) {
371       if (em->pid == pid) {
372 	em->pid = -pid;
373 	uistate.emod_check = 1;
374       }
375     }
376   }
377   signal(SIGCHLD, comm_sigchld);
378 }
379 
380 /*
381  * SIGTTIN indicates we're reading from stdin, but we've been placed in the
382  * background.  Suspend reading from stdin for two seconds.
383  */
384 static void
comm_sigttin(int sig)385 comm_sigttin(int sig)
386 {
387   Pool *p = PoolByName("-", NULL);
388   if (p != NULL)
389     PoolSleepFor(p, 2.);
390 
391   signal(SIGTTIN, comm_sigttin);
392 
393 #ifdef SA_RESTART	/* Do we have sigaction()? If so, be sure
394 			 * SIGTTIN won't restart the read it interrupts!
395 			 */
396   {
397     struct sigaction sa;
398     if (sigaction(SIGTTIN, NULL, &sa) >= 0) {
399       sa.sa_flags &= ~SA_RESTART;
400       sigaction(SIGTTIN, &sa, NULL);
401     }
402   }
403 #endif
404 }
405 
signal_name(int sig)406 static const char *signal_name(int sig)
407 {
408   static char buffer[256];
409 
410   switch(sig) {
411   case SIGSEGV:
412     return "Segmentation violation";
413   case SIGBUS:
414     return "Bus error";
415   case SIGILL:
416     return "Illegal instruction";
417   default:
418     snprintf(buffer, sizeof(buffer), "Signal number %d\n", sig);
419     return buffer;
420   }
421 }
422 
423 static void
fatalsig(int sig)424 fatalsig(int sig)
425 {
426   char die = 'y';
427 
428   fprintf(stderr,
429 	  "Geomview(%d): internal error: \"%s\"; "
430 	  "dump core now (y/n) [n] ? ",
431 	  getpid(), signal_name(sig));
432   fflush(stderr);
433   ign_read(2, &die, 1);
434   fprintf(stderr, "got answer %c\n", (int)die % 0xff);
435   fflush(stderr);
436   if (die != 'y' && die != 'Y')
437     gv_exit();
438   /* else return, and hope the OS gives us a core dump. */
439   signal(sig, SIG_DFL);
440 }
441 
442 typedef void (*mysigfunc_t)();
443 
444 static void
catchsig(int sig,mysigfunc_t func)445 catchsig(int sig, mysigfunc_t func)
446 {
447   mysigfunc_t oldfunc = (mysigfunc_t)signal(sig, func);
448   if (oldfunc == (mysigfunc_t)SIG_IGN)
449     signal(sig, oldfunc);
450 }
451 
452 
453 void
comm_init()454 comm_init()
455 {
456   signal(SIGCHLD, comm_sigchld);
457   signal(SIGTTIN, comm_sigttin);
458   signal(SIGPIPE, SIG_IGN);	/* Write on broken pipe -> I/O error */
459   if (!gv_debug) {
460     catchsig(SIGINT, (mysigfunc_t)gv_exit);
461     catchsig(SIGSEGV, (mysigfunc_t)fatalsig);
462     catchsig(SIGBUS, (mysigfunc_t)fatalsig);
463     catchsig(SIGILL, (mysigfunc_t)fatalsig);
464   }
465   catchsig(SIGHUP, (mysigfunc_t)gv_exit);
466   catchsig(SIGTERM, (mysigfunc_t)gv_exit);
467 
468 
469   lang_init();
470 }
471 
472 int
comm_route(char * str)473 comm_route( char *str )
474 {
475   comm_object( str, &CommandOps, NULL, NULL, COMM_LATER );
476   return 0;
477 }
478 
479 
480 static int
emodule_commandclose(Pool * p)481 emodule_commandclose(Pool *p)
482 {
483   int i;
484   emodule *em;
485 
486   for(i = 0, em = VVEC(uistate.emod, emodule); i < VVCOUNT(uistate.emod); i++, em++) {
487     if (em->link == p && em->pid <= 0) {
488       emodule_reap(em);
489       return 1;
490     }
491   }
492   return 0;
493 }
494 
495 void
emodule_reap(emodule * em)496 emodule_reap(emodule *em)
497 {
498   Lake *lake;
499   Pool *p = em->link;
500 
501   if (p != NULL) {
502     em->link = NULL;
503     if ((lake=(Lake*)PoolClientData(p)) != NULL) {
504       RemoveLakeInterests(lake);
505     }
506     PoolClose(p);
507     MyPoolDelete(p);
508   }
509   ui_emodule_uninstall(em - VVEC(uistate.emod, emodule));
510   return;
511 }
512 
513 int
emodule_kill(emodule * em)514 emodule_kill(emodule *em)
515 {
516   kill(-em->pid, SIGHUP);   /* Kill child's process group in case it has one */
517   /* Kill child process itself.  comm_sigchld() ought to be invoked if it dies,
518    * but put this check here, just in case, since we're having trouble on the
519    * SGI.
520    */
521   if (kill(em->pid, SIGHUP) < 0 && errno == ESRCH) {
522     em->pid = -abs(em->pid);
523     uistate.emod_check = 1;
524   }
525   return 1;
526 }
527 
528 LDEFINE(emodule_run, LVOID,
529 	"(emodule-run  SHELL-COMMAND ARGS...)\n\
530 	Runs the given SHELL-COMMAND (a string containing a UNIX shell\n\
531 	command) as an external module.  The module's standard output\n\
532 	is taken as geomview commands; responses (written to filename\n\
533 	\"-\") are sent to the module's standard input.  The shell\n\
534 	command is interpreted by /bin/sh, so e.g. I/O redirection may\n\
535 	be used; a program which prompts the user for input from the\n\
536 	terminal could be run with:\n\
537 	  (emodule-run  yourprogram  <&2)\n\
538 	If not already set, the environment variable $MACHTYPE is set\n\
539 	to the name of the machine type.  Input and output\n\
540 	connections to geomview are dropped when the shell command\n\
541 	terminates.  Clicking on a running program's module-browser entry\n\
542 	sends the signal SIGHUP to the program.  For this to work, programs\n\
543 	should avoid running in the background; those using FORMS or GL\n\
544 	should call foreground() before the first FORMS or winopen() call.\n\
545 	See also emodule-define, emodule-start.")
546 {
547   char *cmd;
548   emodule em;
549 
550   LDECLARE(("emodule-run", LBEGIN,
551 	    LSTRINGS, &cmd,
552 	    LEND));
553 
554   em.text = em.name = cmd;
555   em.data = NULL;
556   em.dir = NULL;
557   emodule_run(&em);
558   return Lt;
559 }
560 
561 /* Does str appear to contain template?
562  * Check if str is either equal to the template, or contains template as a
563  * prefix followed by a blank.
564  */
matches(char * template,int len,char * str)565 static int matches(char *template, int len, char *str)
566 {
567   if (str == NULL) return 0;
568   return (strncasecmp(template, str, len) == 0 &&
569 	  (str[len] == '\0' || isspace(str[len])));
570 }
571 
572 /* This routine searches through the running emodules and returns the index
573  * of the first on matching modname.  Case sensitive.  modname is
574  * looked for first in the names as they appear in the browser
575  * (minus the number in []'s) then as they appear in the command used to
576  * run the module, minus the arguments.  Returns -1 if the module is not
577  * found.
578  * -cf */
emodule_running_index(char * modname)579 int emodule_running_index(char *modname)
580 {
581   char *name;
582   emodule *em;
583   int i, len;
584 
585   if (modname == NULL) return -1;
586   len = strlen(modname);
587   for (i = 0, em = VVEC(uistate.emod, emodule); i < VVCOUNT(uistate.emod);
588        i++, em++)
589     if (em->pid > 0) {
590       name = strchr(em->name, ']');
591       if (name == NULL) name = em->name;
592       else name++;
593       if (matches(modname, len, name) || matches(modname, len, em->name)
594 	  || matches(modname, len, em->text))
595 	return i;
596     }
597   return -1;
598 }
599 
600 
601 LDEFINE(emodule_isrunning, LVOID,
602 	"(emodule-isrunning NAME)\n\
603 	Returns Lt if the emodule NAME is running, or Lnil\n\
604 	if it is not running.  NAME is searched for in the\n\
605 	names as they appear in the browser and in the shell commands\n\
606 	used to execute the external modules (not including arguments).")
607 {
608   char *modname;
609 
610   LDECLARE(("emodule-isrunning", LBEGIN,
611 	    LSTRING, &modname,
612 	    LEND));
613 
614   if (emodule_running_index(modname) != -1) return Lt;
615   else return Lnil;
616 }
617 
618 /*
619  * NeXT applications need to know the absolute paths of their executables.
620  * If we got an emodule definition from a ``.geomview-X'' file in directory Y,
621  * and the definition begins with the name of a file living in Y,
622  * then prepend the directory name to the shell command.
623  * The strcspn() is an attempt to grab the first word of a shell command.
624  */
625 static char *
truepath(char * program,char * dir)626 truepath(char *program, char *dir)
627 {
628   static char *buf = NULL;
629   int len;
630   char path[10240];
631 
632   len = strcspn(program, "()<> \t;");
633   if (len == 0) return program;
634   sprintf(path, "%s/%.*s", dir, len, program);
635   envexpand(path);
636   if (access(path, X_OK) == 0) {
637     if (buf) free(buf);
638     strcat(path, program + len);
639     program = buf = strdup(path);
640   }
641   return program;
642 }
643 
644 
645 
646 emodule *
emodule_run(emodule * em)647 emodule_run(emodule *em)
648 {
649   struct pipe { int r, w; } pfrom, pto;
650   emodule *newem;
651   int i, pid;
652   char *program;
653   int otherpgrp = 1;
654   char seqname[128];
655 
656   program = em->text;
657   if (program[0] == '!') {
658     program++;
659     otherpgrp = 0;
660   }
661 
662   /* create the communication pipes */
663   pfrom.r = pfrom.w = -1;
664   if (pipe((int *)&pfrom) < 0 || pipe((int *)&pto) < 0) {
665     OOGLError(1, "Can't create pipe to external module: %s", sperror());
666     if (pfrom.r >= 0) {
667       close(pfrom.r); close(pfrom.w);
668     }
669     return NULL;
670   }
671 
672   signal(SIGCHLD, SIG_DFL);
673   /* invoke external module */
674   switch(pid = fork()) {
675   case -1:
676     OOGLError(1, "Can't fork external module: %s", sperror());
677     return NULL;
678 
679   case 0: {
680     char envbuf[10240];
681 
682     if (otherpgrp) {
683 #if SETPGRP_VOID
684       setpgrp();
685 #else
686       setpgrp(0,getpid());
687 #endif
688     }
689     if (em->dir) {
690       program = truepath(program, em->dir);
691       sprintf(envbuf, "PATH=%s:%s", em->dir, getenv("PATH"));
692       envexpand(envbuf);
693     } else {
694       /* Append known module directories to the subprocess' PATH.
695        * This lets us emodule-run an existing module program with
696        * special arguments, without having to specify its full path.
697        */
698       char *p = envbuf;
699       sprintf(envbuf, "PATH=%s", getenv("PATH"));
700       for(i = 0; i < emodule_path_count; i++) {
701 	p += strlen(p);
702 	*p++ = ':';
703 	strcpy(p, VVEC(vv_emodule_path, char *)[i]);
704       }
705     }
706     putenv(envbuf);
707 
708     close(pfrom.r);
709     close(pto.w);
710     dup2(pto.r, STDIN_FILENO);
711     close(pto.r);
712     dup2(pfrom.w, STDOUT_FILENO);
713     close(pfrom.w);
714     signal(SIGPIPE, SIG_DFL);
715     signal(SIGCHLD, SIG_DFL);
716     execl("/bin/sh", "sh", "-c", program, NULL);
717 
718     fprintf(stderr, "Can't exec external module: ");
719     perror(em->text);
720     exit(1);
721   }
722   default: /* parent */
723     break;
724   }
725 
726   close(pto.r);
727   close(pfrom.w);
728 
729   for(i=1; ; i++) {
730     sprintf(seqname, "[%d]%.100s", i, em->name);
731     if (ui_emodule_index(seqname,NULL) < 0)
732       break;
733   }
734 
735   /*
736    * Register new module in the UI's table.
737    * Setting its callback to emodule_kill() means that clicking on that
738    * browser entry will kill the module.
739    * We insert it before the entry that created it, if any;
740    * otherwise at the beginning of the table.
741    */
742   i = em - VVEC(uistate.emod, emodule);
743   if (i < 0 || i > VVCOUNT(uistate.emod))
744     i = 0;
745   newem = ui_emodule_install(i, seqname, emodule_kill);
746 
747   newem->link = PoolStreamOpen( seqname, fdopen(pfrom.r, "rb"), 0, &emoduleCommandOps );
748   if (newem->link) {
749     /* Attach output stream, too. */
750     PoolStreamOpen(seqname, fdopen(pto.w, "w"), 1, &emoduleCommandOps);
751 
752     /* Kludge.  We want to ensure that EOF indications are "hard", i.e.
753      * that we cease reading and drop the emodule as soon as we see EOF.
754      * Unfortunately I can't find any way in the refcomm library
755      * to distinguish nameless pipes (-> hard eof) from named ones
756      * (where eof may be a temporary condition).  So the library
757      * guesses they're soft.  We need to tell it otherwise. -slevy 921005
758      */
759     newem->link->softEOF = 0;
760   }
761   newem->pid = pid;
762   signal(SIGCHLD, comm_sigchld);
763   return newem;
764 }
765 
766 LDEFINE(command, LVOID,
767 	"(command        INFILE [OUTFILE])\n\
768 	Read commands from INFILE; send corresponding responses\n\
769 	(e.g. anything written to filename \"-\") to OUTFILE, stdout\n\
770 	by default.")
771 {
772   char *file, *ofile = NULL;
773 
774   LDECLARE(("command", LBEGIN,
775 	    LSTRING, &file,
776 	    LOPTIONAL,
777 	    LSTRING, &ofile,
778 	    LEND));
779 
780   if (PoolStreamOpen(file, NULL, 0, &CommandOps) == NULL) {
781     OOGLError(0,"command: cannot open input %s: %s\n", file, sperror());
782     return Lnil;
783   }
784   if (ofile) {
785     FILE *outf = (strcmp(ofile, "-")) ? fopen(ofile, "w") : stdout;
786     if (outf == NULL) {
787       OOGLError(0,"command: cannot open output %s: %s\n", ofile, sperror());
788       return Lnil;
789     }
790     else
791       PoolStreamOpen(file, outf, 1, &CommandOps);
792   }
793   return Lt;
794 }
795 
796 LDEFINE(sleep_for, LVOID,
797 	"(sleep-for  TIME)\n\
798 	Suspend reading commands from this stream for TIME seconds.\n\
799 	Commands already read will still be executed; ``sleep-for'' inside\n\
800 	``progn'' won't delay execution of the rest of the progn's contents.")
801 {
802   Lake *sweenie;
803   float time;
804 
805   LDECLARE(("sleep-for", LBEGIN,
806 	    LLAKE, &sweenie,
807 	    LFLOAT, &time,
808 	    LEND));
809 
810   PoolSleepFor(POOL(sweenie), time);
811   return Lt;
812 }
813 
814 LDEFINE(sleep_until, LFLOAT,
815 	"(sleep-until TIME)\n\
816 	Suspend reading commands from this stream until TIME (in seconds).\n\
817 	Commands already read will still be executed; ``sleep-until'' inside\n\
818 	``progn'' won't delay execution of the rest of the progn's contents.\n\
819 	Time is measured according to this stream's clock, as set by\n\
820 	``set-clock''; if never set, the first sleep-until sets it to 0\n\
821 	(so initially (sleep-until TIME) is the same as (sleep-for TIME)).\n\
822 	Returns the number of seconds until TIME.")
823 {
824   Lake *bass;
825   float time;
826 
827   LDECLARE(("sleep-until", LBEGIN,
828 	    LLAKE, &bass,
829 	    LFLOAT, &time,
830 	    LEND));
831 
832   PoolSleepUntil(POOL(bass), time);
833   time -= PoolTimeAt(POOL(bass), NULL);	/* NULL => now. */
834   return LNew(LFLOAT, &time);
835 }
836 
837 LDEFINE(set_clock, LVOID,
838 	"(set-clock TIME)\n\
839 	Adjusts the clock for this command stream to read TIME (in seconds)\n\
840 	as of the moment the command is received.  See also sleep-until, clock.")
841 {
842   Lake *bass;
843   float time;
844 
845   LDECLARE(("set-clock", LBEGIN,
846 	    LLAKE, &bass,
847 	    LFLOAT, &time,
848 	    LEND));
849 
850   PoolSetTime(POOL(bass), NULL, time);
851 
852   return Lt;
853 }
854 
855 LDEFINE(clock, LVOID,
856 	"(clock)\n"
857 	"Returns the current time, in seconds, as shown by this stream's "
858 	"clock.	See also set-clock and sleep-until.")
859 {
860   Lake *rainy;
861   float time;
862   LDECLARE(("clock", LBEGIN,
863 	    LLAKE, &rainy,
864 	    LEND));
865   time = PoolTimeAt(POOL(rainy), NULL);
866   return LNew(LFLOAT, &time);
867 }
868 
869 /* This used to be the guts of echo - it's now in a seperate procedure
870  * for use by emodule-transmit.
871  * -cf */
echo_to_fp(LList * arglist,FILE * fp)872 void echo_to_fp(LList *arglist, FILE *fp)
873 {
874   LObject *arg, *val;
875 
876   if (arglist == NULL) {
877     fputs("\n", fp);
878   } else {
879     for(;;) {
880       arg = arglist->car;
881       /* Note that -- for compatibility -- we distinguish the case of
882        * a function which evaluates to a string and a literal
883        * string. A literal string is written without quotes, a
884        * function evaluating to a string results in printing a quoted
885        * string. An example is (echo (read-id focus)) which used to
886        * result in printing the id of the active camera as quoted
887        * string.
888        */
889       if (arg->type == LSTRING) {
890 	/* omit the quotes */
891 	fputs(LSTRINGVAL(arg), fp);
892       } else {
893 	val = LEval(arg);
894 	LWrite(fp, val);
895 	LFree(val);
896       }
897       if ((arglist = arglist->cdr) == NULL) {
898 	break;
899       }
900       fputs(" ", fp);
901     }
902   }
903   fflush(fp);
904 }
905 
906 LDEFINE(echo, LVOID,
907 	"(echo ...)\n"
908 	"Write the given data to the special file \"-\".  Strings are written "
909 	"literally; lisp expressions are evaluated and their values written. "
910 	"If received from an external program, \"echo\" sends to the "
911 	"program's input. Otherwise writes to geomview's own standard output "
912 	"(typically the terminal).")
913 {
914   Lake *powderhorn;
915   LList *arglist = NULL;
916   FILE *fp;
917 
918   LDECLARE(("echo", LBEGIN,
919 	    LLAKE, &powderhorn,
920 	    LHOLD,
921 	    LREST, &arglist,
922 	    LEND));
923 
924   if ((fp = PoolOutputFile(POOL(powderhorn))) == NULL) {
925     fp = stdout;
926   }
927   echo_to_fp(arglist, fp);
928 
929   return Lt;
930 }
931 
932 
933 LDEFINE(emodule_transmit, LVOID,
934 	"(emodule-transmit NAME LIST)\n\
935 	Places LIST into external module NAME's standard input.  NAME is\n\
936 	searched for in the names of the modules as they appear in the\n\
937 	External Modules browser and then in the shell commands used to\n\
938 	execute the external modules.  Does nothing if modname is not\n\
939 	running.")
940 {
941   char *modname;
942   emodule *em;
943   LList *message = NULL;
944   int i;
945 
946   LDECLARE(("emodule-transmit", LBEGIN,
947 	    LSTRING, &modname,
948 	    LHOLD,
949 	    LREST, &message,
950 	    LEND));
951 
952   i = emodule_running_index(modname);
953   if (i == -1) {
954     return Lnil;
955   }
956   em = VVINDEX(uistate.emod, emodule, i);
957   if (em->link && em->link->outf) {
958     echo_to_fp(message, em->link->outf);
959     return Lt;
960   }
961   return Lnil;
962 }
963 
964 
965 
966 LDEFINE(read, LVOID,
967 	"(read {geometry|camera|image|appearance|transform|ntransform|command} "
968 	"{GEOMETRY or CAMERA or ...})\n"
969 	"Read and interpret the text in ... as containing the "
970 	"given type of data.  Useful for defining objects using OOGL "
971 	"reference syntax, e.g. "
972 	"\n\n\n\n"
973 	"(geometry  thing { INST  transform : T    geom : fred })\n\n"
974 	"(read  geometry  { define fred QUAD 1 0 0  0 1 0  0 0 1  1 0 0 })\n\n"
975 	"(read  transform { define T <myfile})")
976 /*
977   NO LDECLARE !
978   There is currently no C interface to this function.  Perhaps we
979   don't really want one since "read" is an inherently lisp thing.
980   Or perhaps one that takes a string and reads it?
981 
982   Since we don't use LDECLARE we must remember to LDefun()
983   this function manually in lang_init() in file lang.c.
984 */
985 {
986   char *opsname = NULL;
987   HandleOps *ops;
988   LObject *kw = NULL;
989 
990   if (LPARSEMODE) {
991     /* parse first arg [ops]: */
992     if (!LakeMore(lake) ||
993 	(kw = LSexpr(lake)) == Lnil ||
994 	!LSTRINGFROMOBJ(kw, &opsname) ||
995 	!(ops = str2ops(opsname))) {
996       OOGLSyntax(lake->streamin,
997 		 "\"read\" in \"%s\": keyword "
998 		 "{command|geometry|camera|window|transform|"
999 		 "ntransform|image|appearance} expected, "
1000 		 "got \"%s\"",
1001 		 LakeName(lake), opsname);
1002       goto fail;
1003     }
1004 
1005     /* parse 2nd arg, using ops determined by 1st arg.
1006        Note: we don't actually store the 1st arg because this function's
1007        work is all done during parsing.  */
1008     if (!LakeMore(lake) || (*ops->strmin)(POOL(lake), NULL, NULL) == 0) {
1009       OOGLSyntax(lake->streamin, "\"read %s\" in \"%s\": error reading %s's",
1010 		 opsname, PoolName(POOL(lake)), opsname);
1011       goto fail;
1012     }
1013   }
1014   /* if lake == NULL then we're evaluating, but this function does
1015      no evaluation work; it's all in the parsing */
1016   LFree(kw);
1017   return Lt;
1018 
1019  fail:
1020   LFree(kw);
1021   return Lnil;
1022 }
1023 
gv_merge(HandleOps * ops,int camid,Ref * object)1024 void gv_merge(HandleOps *ops, int camid, Ref *object)
1025 {
1026   CameraStruct cs;		/* Might be either camera or window, really */
1027 
1028   cs.h = NULL;
1029 #if 0
1030   cs.cam = REFGET(Camera, object);     /* Since (merge ...) will delete it */
1031 #endif
1032 
1033   LFree(LEvalFunc("merge",
1034 		  LSTRING, ops == &CamOps ? "camera" : "window",
1035 		  LID, camid,
1036 		  ops == &CamOps ? LCAMERA : LWINDOW, &cs,
1037 		  LEND) );
1038 }
1039 
1040 LDEFINE(merge, LVOID,
1041 	"(merge {window|camera} CAM-ID  { WINDOW or CAMERA ... } )\n"
1042 	"Modify the given window or camera, changing just those properties "
1043 	"specified in the last argument. E.g.\n\n"
1044 	"               (merge camera \"Camera\" { far 20 })\n\n"
1045 	"sets Camera's far clipping plane to 20 while leaving "
1046 	"other attributes untouched.")
1047 /*
1048   Since we don't use LDECLARE we must remember to LDefun()
1049   this function manually in lang_init() in file lang.c.
1050 */
1051 {
1052   char *opsname = NULL;
1053   HandleOps *ops;
1054   int id;
1055   LObject *kw = NULL, *idarg = NULL, *item = NULL;
1056 
1057   if (LPARSEMODE) {
1058     /* parse first arg [ops]: */
1059     if (!LakeMore(lake) || (kw = LSexpr(lake)) == Lnil ||
1060 	!LSTRINGFROMOBJ(kw, &opsname) ||
1061 	((ops = str2ops(opsname)) != &CamOps && ops != &WindowOps)) {
1062       OOGLSyntax(lake->streamin,
1063 		 "\"merge\" in \"%s\": expected \"camera\" or \"window\", "
1064 		 "got \"%s\"", LakeName(lake), opsname);
1065       goto parsefail;
1066     }
1067 
1068     /* parse 2nd arg; it's a string (id) */
1069     if (!LakeMore(lake) || (idarg = LEvalSexpr(lake)) == Lnil) {
1070       OOGLSyntax(lake->streamin,"\"merge\" in \"%s\": expected CAM-ID",
1071 		 LakeName(lake));
1072       goto parsefail;
1073     }
1074 
1075     item = LPARSE(((ops == &CamOps) ? LCAMERA : LWINDOW))(lake);
1076     if (item == Lnil) {
1077       OOGLSyntax(lake->streamin, "\"merge\" in \"%s\": error reading %s",
1078 		 LakeName(lake), LSTRINGVAL(kw));
1079       goto parsefail;
1080     }
1081     LListAppend(args, kw);
1082     LListAppend(args, idarg);
1083     LListAppend(args, item);
1084     return Lt;
1085   } else {
1086     LParseArgs("merge", LBEGIN, LREST, &args, LEND);
1087   }
1088 
1089   kw = LListEntry(args, 1);
1090   idarg = LListEntry(args, 2);
1091   item = LListEntry(args, 3);
1092   if (!LSTRINGFROMOBJ(kw, &opsname) ||
1093       ((ops = str2ops(opsname)) != &CamOps && ops != &WindowOps)) {
1094     OOGLError(0, "\"merge\": expected \"camera\" or \"window\", got %s",
1095 	      LSummarize(idarg));
1096     return Lnil;
1097   }
1098   if (!LFROMOBJ(LID)(idarg, &id) || !ISCAM(id)) {
1099     OOGLError(0, "\"merge\": expected CAM-ID in arg position 2, got %s",
1100 	      LSummarize(idarg));
1101     return Lnil;
1102   }
1103   if (ops == &CamOps) {
1104     CameraStruct *cs;
1105     if (!LFROMOBJ(LCAMERA)(item, &cs)) {
1106       OOGLError(0,"\"merge\": expected camera in arg position 3");
1107       return Lnil;
1108     }
1109     drawer_merge_camera(id, cs->cam);
1110     /* CamDelete(cs->cam); */
1111   } else {
1112     WindowStruct *ws;
1113     if (!LFROMOBJ(LWINDOW)(item, &ws)) {
1114       OOGLError(0,"\"merge\": expected window in arg position 3");
1115       return Lnil;
1116     }
1117     drawer_merge_window(id, ws->wn);
1118     /* WnDelete(ws->wn); */
1119   }
1120   return Lt;
1121 
1122  parsefail:
1123   LFree(kw);
1124   LFree(idarg);
1125   LFree(item);
1126   return Lnil;
1127 }
1128 
1129 static void
MyPoolDelete(Pool * p)1130 MyPoolDelete(Pool *p)
1131 {
1132   Lake *lake = (Lake*)PoolClientData(p);
1133   if (lake) {
1134     RemoveLakeInterests(lake);
1135     LakeFree(lake);
1136   }
1137   PoolDelete(p);
1138 }
1139 
1140 HandleOps *
str2ops(char * str)1141 str2ops(char *str)
1142 {
1143   if (str == NULL) return NULL;
1144   else if (!strncmp(str, "cam", 3)) return &CamOps;
1145   else if (!strncmp(str, "geom", 4)) return &GeomOps;
1146   else if (!strncmp(str, "comm", 4)) return &CommandOps;
1147   else if (!strncmp(str, "trans", 5)) return &TransOps;
1148   else if (!strncmp(str, "ntrans", 6)) return &NTransOps;
1149   else if (!strncmp(str, "win", 3)) return &WindowOps;
1150   else if (!strncmp(str, "image", sizeof("image")-1)) return &ImageOps;
1151   else if (!strncmp(str, "appearance",
1152 		    sizeof("appearance")-1)) return &AppearanceOps;
1153   else return NULL;
1154 }
1155 
1156 LType *
ops2ltype(HandleOps * ops)1157 ops2ltype(HandleOps *ops)
1158 {
1159   if (ops == &CamOps) return LCAMERA;
1160   else if (ops == &GeomOps) return LGEOM;
1161   else if (ops == &WindowOps) return LWINDOW;
1162   else if (ops == &TransOps) return LTRANSFORM;
1163   else if (ops == &NTransOps) return LTRANSFORMN;
1164   else if (ops == &CommandOps) return LLOBJECT;
1165   else if (ops == &ImageOps) return LIMAGE;
1166   else if (ops == &AppearanceOps) return LAP;
1167   else return NULL;
1168 }
1169 
1170 /************************************************************************/
1171 
1172 LDEFINE(load, LINT,
1173 	"(load  filename  [command|geometry|camera])\n\
1174 	Loads the given file into geomview.  The optional second argument\n\
1175 	specifies the type of data it contains, which may be \"command\"\n\
1176 	(geomview commands), \"geometry\" (OOGL geometric data), or\n\
1177 	\"camera\" (OOGL camera definition).  If omitted, attempts to guess\n\
1178 	about the file's contents.\n\
1179 	Loading geometric data creates a new visible object; loading a camera\n\
1180 	opens a new window; loading a command file executes those commands.\n")
1181 {
1182   char *file, *opsname = NULL;
1183   HandleOps *ops = &CommandOps;
1184   int guess = 1;
1185 
1186   LDECLARE(("load", LBEGIN,
1187 	    LSTRING, &file,
1188 	    LOPTIONAL,
1189 	    LSTRING, &opsname,
1190 	    LEND));
1191 
1192   if (opsname != NULL) {
1193     guess = 0;
1194     ops = str2ops(opsname);
1195     if (ops != &CommandOps && ops != &GeomOps && ops != &CamOps) {
1196       OOGLError(0, "load: expected \"command\" or \"geometry\" or \"camera\", got \"%s\"", opsname);
1197       return Lnil;
1198     }
1199   }
1200   loadfile(file, ops, guess);
1201   return Lt;
1202 }
1203 
1204 void
loadfile(char * name,HandleOps * defops,int guess)1205 loadfile(char *name, HandleOps *defops, int guess)
1206 {
1207   Handle *h = NULL;
1208   Ref *obj = NULL;
1209   HandleOps *ops = NULL;
1210   char *pathname;
1211   int freename = 0;
1212 
1213   if (strcmp(name, "-") == 0) {
1214     guess = 0;
1215   } else if ((pathname=findfile(NULL, name))) {
1216     name = strdup(pathname);
1217     freename = 1;
1218   } else if (strchr(name, ' ') == NULL && strchr(name, '(') == NULL
1219 	    && strchr(name, '<') == NULL) {
1220     OOGLError(0, "Can't find file %s", name);
1221     return;
1222   }
1223 
1224   if (ispipe(name)) {
1225     guess = 0;
1226   }
1227   if (!guess) {
1228     if (comm_object(name, defops, &h, &obj, COMM_LATER)) {
1229       ops = defops;
1230     } else {
1231       OOGLError(0, "Can't load %s's from %s", defops->prefix, name);
1232       return;
1233     }
1234   } else if (comm_object(name, &GeomOps, &h, &obj, COMM_LATER)) {
1235     ops = &GeomOps;
1236   } else if (comm_object(name, &CamOps, &h, &obj, COMM_LATER)) {
1237     ops = &CamOps;
1238   } else if (!(comm_object(name, &CommandOps, &h, NULL, COMM_LATER))) {
1239     OOGLError(0,"Can't load %s",name);
1240     return;
1241   }
1242   useconnection( name, ops, h, obj, 1 );
1243   if (freename) {
1244     free(name);
1245   }
1246   return;
1247 }
1248 
1249 LDEFINE(hdelete, LVOID,
1250 	"(hdelete [geometry|camera|window|image|appearance|transform|ntransform] name)\n"
1251 	"Deletes the given handle. Note that the handle will not actually be "
1252 	"deleted in case there are still other objects referring to the "
1253 	"handle, but once those objects are gone, the handle will also "
1254 	"automatically go away. The object the handle refers to (if any) "
1255 	"will only be deleted if there are no other references to that object."
1256 	"\n\n\n\n"
1257 	"If the optional first argument is omitted, then the first handle "
1258 	"matching \"name\" will be deleted, regardless of the type of the "
1259 	"object it is attached to. It is not an error to call this function "
1260 	"with a non-existent handle, but it is an error to call this funcion "
1261 	"with the name of a non-global handle, i.e. one that was not "
1262 	"created by (hdefine ...) or (read ... { define ...}).")
1263 {
1264   Handle *h = NULL;
1265   HandleOps *ops = NULL;
1266   char *hname = NULL, *opsname = NULL, *horo;
1267 
1268   LDECLARE(("hdelete", LBEGIN,
1269 	    LSTRING, &horo,
1270 	    LOPTIONAL,
1271 	    LSTRING, &hname,
1272 	    LEND));
1273 
1274   if (hname == NULL) {
1275     hname = horo;
1276   } else {
1277     opsname = horo;
1278   }
1279 
1280   if (opsname && ((ops = str2ops(opsname)) == NULL || ops2ltype(ops) == NULL)) {
1281     OOGLError(0, "\"hdelete\": "
1282 	      "expected \"camera\" or \"window\" or \"geometry\" or "
1283 	      "\"transform\" or \"ntransform\" or "
1284 	      "\"image\" or \"appearance\", got \"%s\"",
1285 	      opsname);
1286     return Lnil;
1287   }
1288 
1289   if ((h = HandleByName(hname, ops)) != NULL) {
1290     REFPUT(h); /* undo HandleByName() REFGET */
1291     if (!h->permanent) {
1292       OOGLError(0, "\"hdelete\": "
1293 		"attempt to delete the non-global handle \"%s[%s]\"",
1294 		opsname ? opsname : "", hname);
1295       return Lnil;
1296     }
1297     h->permanent = false;
1298     HandleDelete(h);
1299   }
1300   return Lt;
1301 }
1302 
1303 
1304 LDEFINE(hdefine, LVOID,
1305 	"(hdefine "
1306 	"{geometry|camera|window|image|appearance|transform|ntransform} "
1307 	"name value)\n"
1308 	"Sets the value of a handle of a given type. "
1309 	"(hdefine  <type>  <name>  <value>)  is generally equivalent to "
1310 	"(read <type>  { define <name> <value> }) "
1311 	"except that the assignment is done when hdefine is executed, "
1312 	"(possibly not at all if inside a conditional statement), "
1313 	"while the ``read ... define'' performs assignment as soon as the "
1314 	"text is read.")
1315 {
1316   Handle *h = NULL;
1317   HandleOps *ops = NULL;
1318   LType *ltype;
1319   char *hname;
1320   char *opsname = NULL;
1321   Ref *obj;
1322   LObject *kw = NULL, *name = NULL, *item = NULL;
1323   union {
1324     GeomStruct gs;
1325     CameraStruct cs;
1326     TransformStruct ts;
1327     TmNStruct tns;
1328     WindowStruct ws;
1329     ApStruct as;
1330     ImgStruct img;
1331     LObject lobj;
1332   } *s;
1333 
1334   if (LPARSEMODE) {
1335     /* parse first arg [ops]: */
1336     if (! LakeMore(lake) || (kw = LSexpr(lake)) == Lnil ||
1337 	!LSTRINGFROMOBJ(kw, &opsname) ||
1338 	(ops = str2ops(opsname)) == NULL ||
1339 	(ltype = ops2ltype(ops)) == NULL) {
1340       OOGLSyntax(lake->streamin,
1341 		 "\"hdefine\" in \"%s\": "
1342 		 "expected \"camera\" or \"window\" or \"geometry\" or "
1343 		 "\"transform\" or \"ntransform\" or "
1344 		 "\"image\" or \"appearance\", got \"%s\"",
1345 		 LakeName(lake), opsname);
1346       goto parsefail;
1347     }
1348 
1349     /* parse 2nd arg; it's a string (id) */
1350     if (!LakeMore(lake) || (name = LEvalSexpr(lake)) == Lnil) {
1351       OOGLSyntax(lake->streamin,
1352 		 "\"hdefine %s\" in \"%s\": expected handle name",
1353 		 LakeName(lake), opsname);
1354       goto parsefail;
1355     }
1356 
1357     item = LPARSE(ltype)(lake);
1358     if (item == Lnil) {
1359       OOGLSyntax(lake->streamin,
1360 		 "\"hdefine\" in \"%s\": error reading %s",
1361 		 LakeName(lake), LSTRINGVAL(kw));
1362       goto parsefail;
1363     }
1364     LListAppend(args, kw);
1365     LListAppend(args, name);
1366     LListAppend(args, item);
1367 
1368     return Lt;
1369   }
1370 
1371   LParseArgs("hdefine", LBEGIN, LREST, &args, LEND);
1372 
1373   kw = LListEntry(args, 1);
1374   name = LListEntry(args, 2);
1375   item = LListEntry(args, 3);
1376   if (!LSTRINGFROMOBJ(kw, &opsname) ||
1377       (ops = str2ops(opsname)) == NULL ||
1378       (ltype = ops2ltype(ops)) == NULL) {
1379     OOGLError(0, "\"hdefine\": expected data type, got %s",
1380 	      LSummarize(kw));
1381     return Lnil;
1382   }
1383   if (!LSTRINGFROMOBJ(name, &hname)) {
1384     OOGLError(0, "\"hdefine\": expected handle name, got %s", LSummarize(name));
1385     return Lnil;
1386   }
1387   if (!LFROMOBJ(ltype)(item, &s)) {
1388     OOGLError(0, "\"hdefine\": Can't extract %s from %s",
1389 	      ltype->name, LSummarize(item));
1390     return Lnil;
1391   }
1392   h = HandleCreateGlobal(hname, ops);
1393   REFPUT(h);
1394   if (ops == &CommandOps) {
1395     obj = NULL; /* (Ref *)LispCreate( s->lobj ) */
1396   } else if (ops == &TransOps) {
1397     obj = (Ref *)TransCreate(s->ts.tm);
1398   } else {
1399     obj = (Ref *)s->gs.geom; /* All other types resemble geoms */
1400   }
1401   HandleSetObject(h, obj);
1402   if (ops == &TransOps) {
1403     REFPUT(obj); /* otherwise obj will never be deleted */
1404   }
1405   return Lt;
1406 
1407  parsefail:
1408   LFree(kw);
1409   LFree(name);
1410   LFree(item);
1411   return Lnil;
1412 }
1413 
1414 
1415 /*****************************************************************************/
1416 
1417 #if HAVE_UNIX_SOCKETS || HAVE_INET_SOCKETS || HAVE_INET6_SOCKETS
1418 
1419 #if HAVE_SYS_SOCKET_H
1420 # include <sys/socket.h>
1421 #endif
1422 
1423 #if HAVE_NETINET_IN_H
1424 # include <netinet/in.h>
1425 #endif
1426 
1427 #if !HAVE_DECL_ACCEPT
1428 int accept(int sockfd, struct sockaddr *addr, ACCEPT_ARG3_TYPE *addrlen);
1429 #endif
1430 
1431 #if HAVE_UNIX_SOCKETS
1432 /*
1433  * makeunixsocket(name) makes a UNIX-domain listening socket and returns its fd.
1434  */
1435 static int
makeunixsocket(char * name)1436 makeunixsocket(char *name)
1437 {
1438   struct sockaddr_un un;
1439   int s;
1440 
1441   unlink(name);
1442   strcpy(un.sun_path, name);
1443   un.sun_family = AF_UNIX;
1444   if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
1445     OOGLError(0, "geomview: can't make UNIX domain socket: %s", sperror());
1446     return -1;
1447   }
1448   if (bind(s, (struct sockaddr *)&un, sizeof(un)) < 0 || listen(s, 4) < 0) {
1449     OOGLError(0, "geomview: can't listen on socket %s: %s",name,sperror());
1450     return -1;
1451   }
1452   return s;
1453 }
1454 #endif /* HAVE_UNIX_SOCKKETS */
1455 
1456 #if HAVE_INET_SOCKETS
1457 /*
1458  * makeinetsocket(name) makes a IPv4 listening socket and returns its fd.
1459  */
1460 static int
makeinetsocket(int port)1461 makeinetsocket(int port)
1462 {
1463   struct sockaddr_in in;
1464   int s;
1465 
1466   if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
1467     OOGLError(0, "geomview: can't make IPv4 socket: %s", sperror());
1468     return -1;
1469   }
1470   in.sin_family = AF_INET;
1471   in.sin_port = htons(port);
1472   in.sin_addr.s_addr = htonl(INADDR_ANY);
1473   if (bind(s, (struct sockaddr *)&in, sizeof(in)) < 0 || listen(s, 4) < 0) {
1474     OOGLError(0, "geomview: can't listen on IPv4 port %d: %s", port, sperror());
1475     return -1;
1476   }
1477   return s;
1478 }
1479 #endif /* HAVE_INET_SOCKETS */
1480 
1481 #if HAVE_INET6_SOCKETS
1482 /*
1483  * makeinet6socket(name) makes a IPv6 listening socket and returns its fd.
1484  */
1485 static int
makeinet6socket(int port)1486 makeinet6socket(int port)
1487 {
1488   struct sockaddr_in6 in;
1489   struct in6_addr addr = IN6ADDR_ANY_INIT;
1490   int s;
1491 
1492   if ((s = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {
1493     OOGLError(0, "geomview: can't make IPv6 socket: %s", sperror());
1494     return -1;
1495   }
1496   in.sin6_family = AF_INET6;
1497   in.sin6_port = htons(port);
1498   in.sin6_addr = addr;
1499   if (bind(s, (struct sockaddr *)&in, sizeof(in)) < 0 || listen(s, 4) < 0) {
1500     OOGLError(0, "geomview: can't listen on IPv6 port %d: %s", port, sperror());
1501     return -1;
1502   }
1503   return s;
1504 }
1505 #endif /* HAVE_INET6_SOCKETS */
1506 
1507 /*
1508  * This pretends to be a stream-import routine, but it really handles
1509  * new connections on UNIX-domain sockets!
1510  */
1511 int
listenimport(Pool * listenp,Handle ** hp,Ref ** rp)1512 listenimport(Pool *listenp, Handle **hp, Ref **rp)
1513 {
1514   struct sockaddr_un un;
1515   ACCEPT_ARG3_TYPE len = sizeof(un);
1516   char conname[10240];
1517   int i, ds;
1518   HandleOps *ops = (HandleOps *)PoolClientData(listenp);
1519 
1520   if ((ds =
1521       accept(
1522 	     iobfileno(listenp->inf),
1523 	     (struct sockaddr *)&un,
1524 	     &len)
1525       ) < 0) {
1526     OOGLError(0, "geomview: couldn't accept connection on %s: %s",
1527 	      listenp->poolname, sperror());
1528     return 0;
1529   }
1530   for(i=1; ; i++) {	/* Construct unique connection name */
1531     sprintf(conname, "[%d]%.500s", i, listenp->poolname);
1532     if (PoolByName(conname, ops) == NULL)
1533       break;
1534   }
1535   PoolStreamOpen(conname, fdopen(ds, "rb"), 0, ops);
1536   /*
1537    * Reply on the same pipe's return stream.
1538    * Don't do this yet; only user so far is "togeomview",
1539    * which isn't prepared to receive data back from us.
1540    * PoolStreamOpen(conname, fdopen(ds, "w"), 1, (HandleOps*)PoolClientData(listenp));
1541    */
1542   PoolStreamOpen(conname, fdopen(ds, "w"), 1, ops);
1543   useconnection( listenp->poolname, ops, HandleCreate(conname, ops), NULL, 0 );
1544   return 1;
1545 }
1546 
1547 #endif
1548 
1549 /*
1550  * usepipe(dir, suffix, type)
1551  * where the characters in "type" specify:
1552  * kind of connection (named-pipe 'p' or socket 's') and
1553  * type of data expected (command 'c' or geometry 'g').
1554  *
1555  * For sockets 's' may be followed by eigher "un" (Unix domain
1556  * socket), "in" (IPv4 socket) or "in6" (IPv6 socket). For IP sockets
1557  * "suffix" actually specifies the port-number to use. pipedir is
1558  * ignored in that case.
1559  */
1560 void
usepipe(char * pipedir,char * suffix,char * pipetype)1561 usepipe(char *pipedir, char *suffix, char *pipetype)
1562 {
1563   HandleOps *ops = &GeomOps;
1564   int s;
1565   char *tail;
1566   char pipename[PATH_MAX];
1567   char pdir[PATH_MAX];
1568   enum {
1569     nopipe = -1,
1570     namedpipe = 1,
1571     unixsocket = 2,
1572     inetsocket = 3,
1573     inet6socket = 4
1574   } usepipe = nopipe;
1575   int port = -1;
1576 
1577   while (*pipetype) switch(*pipetype++) {
1578   case 's':
1579     usepipe = unixsocket;
1580     /* Check for sin:PORT and sin6:PORT sockets, allow sun socket type */
1581     if (strncmp(pipetype, "un", 2) == 0) {
1582       pipetype += 2;
1583     } else if (strncmp(pipetype, "in6", 3) == 0) {
1584       usepipe = inet6socket;
1585       pipetype += 3;
1586     } else if (strncmp(pipetype, "in", 2) == 0) {
1587       usepipe = inetsocket;
1588       pipetype += 2;
1589     }
1590     break;
1591   case 'p': usepipe = namedpipe; break;
1592   case 'c': ops = &CommandOps; break;
1593   case 'g': ops = &GeomOps; break;
1594   default:
1595     OOGLError(0, "Unknown character '%c' in pipe type string: "
1596 	      "expected s, p, c, g", pipetype[-1]);
1597   }
1598   if (usepipe == nopipe) {
1599 #ifdef NeXT
1600     usepipe = unixsocket;
1601 #else
1602     usepipe = namedpipe;
1603 #endif
1604   }
1605 
1606   if (usepipe == unixsocket || usepipe == namedpipe) {
1607     if (suffix[0] == '/') {
1608       strcpy(pdir, suffix);
1609       tail = strrchr(pdir, '/');
1610       *tail = '\0';
1611       pipedir = pdir;
1612       suffix = tail + 1;
1613     }
1614     sprintf(pipename, "%s/%s", pipedir, suffix);
1615     mkdir(pipedir, 0777);
1616     chmod(pipedir, 0777);
1617   } else {
1618     port = atoi(suffix);
1619   }
1620 
1621   switch (usepipe) {
1622   case namedpipe: {
1623     /* Establish System-V style named pipe.
1624      * Expect data on it of type 'ops'.
1625      * If there's a non-pipe with that name, trash it.
1626      */
1627     struct stat st;
1628 
1629     if (stat(pipename, &st) == 0 && (st.st_mode & S_IFMT) != S_IFIFO)
1630       unlink(pipename);
1631     if (access(pipename, 0) < 0) {
1632       if (mknod(pipename, S_IFIFO, 0) < 0)
1633 	OOGLError(1, "Can't make pipe: %s: %s", suffix, sperror());
1634       chmod(pipename, 0666);
1635     }
1636     loadfile(pipename, ops, 0);
1637     break;
1638   }
1639 #if HAVE_UNIX_SOCKETS
1640   case unixsocket:
1641     /* Establish UNIX-domain listener socket.
1642      * When we get connections to it, expect data of type 'ops'.
1643      */
1644     s = makeunixsocket(pipename);
1645     if (s >= 0) {
1646       Pool *p = PoolStreamOpen(pipename, fdopen(s, "rb"), 0, &listenOps);
1647       if (p) PoolSetClientData(p, ops);
1648       p->flags |= PF_NOPREFETCH;
1649     }
1650     break;
1651 #endif
1652 #if HAVE_INET_SOCKETS
1653   case inetsocket:
1654     /* Establish UNIX-domain listener socket.
1655      * When we get connections to it, expect data of type 'ops'.
1656      */
1657     s = makeinetsocket(port);
1658     if (s >= 0) {
1659       Pool *p = PoolStreamOpen(pipename, fdopen(s, "rb"), 0, &listenOps);
1660       if (p) PoolSetClientData(p, ops);
1661       p->flags |= PF_NOPREFETCH;
1662     }
1663     break;
1664 #endif
1665 #if HAVE_INET6_SOCKETS
1666   case inet6socket:
1667     /* Establish UNIX-domain listener socket.
1668      * When we get connections to it, expect data of type 'ops'.
1669      */
1670     s = makeinet6socket(port);
1671     if (s >= 0) {
1672       Pool *p = PoolStreamOpen(pipename, fdopen(s, "rb"), 0, &listenOps);
1673       if (p) PoolSetClientData(p, ops);
1674       p->flags |= PF_NOPREFETCH;
1675     }
1676     break;
1677 #endif
1678   default:
1679     break;
1680   }
1681 }
1682 
1683 /*
1684  * Assume we have a connection open named "name", expecting data of
1685  * type "ops".  Wire it to an appropriate geomview object, if any.
1686  * (Data received on that connection will set the value of that object.)
1687  * Since (new_)geometry and (new_)camera take ownership of the object,
1688  * increasing its reference count, we decr the ref count after handing it over.
1689  */
1690 static void
useconnection(char * name,HandleOps * ops,Handle * h,Ref * obj,int unique)1691 useconnection(char *name, HandleOps *ops, Handle *h, Ref *obj, int unique)
1692 {
1693   char *tail;
1694   tail = strrchr(name, '/');
1695   if (tail)
1696     tail++;
1697   else
1698     tail = name;
1699 
1700   if (ops == &GeomOps) {
1701     GeomStruct gs;
1702     if(h && !h->permanent) {
1703 #if 0
1704       /* same logic as in geomparse() */
1705       HandleDelete(h);
1706       h = NULL;
1707 #endif
1708     }
1709     gs.h = h;
1710     gs.geom = (Geom *)obj;
1711     if (unique)
1712       gv_new_geometry( tail, &gs );
1713     else
1714       gv_geometry( tail, &gs );
1715     GeomDelete((Geom *)obj);	/* Maintain ref count */
1716     HandleDelete(h); /* Maintain ref count */
1717   } else if (ops == &CamOps) {
1718     CameraStruct cs;
1719     if(h && !h->permanent) {
1720 #if 0
1721       /* same logic as in geomparse() */
1722       HandleDelete(h);
1723       h = NULL;
1724 #endif
1725     }
1726     cs.h = h;
1727     cs.cam = (Camera *)obj;
1728     if (unique)
1729       gv_new_camera( tail, &cs );
1730     else
1731       gv_camera( tail, &cs );
1732     CamDelete((Camera *)obj);	/* Maintain ref count */
1733     HandleDelete(h); /* Maintain ref count */
1734   }
1735 }
1736 
1737 /*
1738  * Local Variables: ***
1739  * mode: c ***
1740  * c-basic-offset: 2 ***
1741  * End: ***
1742  */
1743