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