Update FSF's address in the preamble.
[bpt/emacs.git] / src / vmsproc.c
CommitLineData
dcfdbac7 1/* Interfaces to subprocesses on VMS.
3a22ee35 2 Copyright (C) 1988, 1994 Free Software Foundation, Inc.
dcfdbac7
JB
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
7c938215 8the Free Software Foundation; either version 2, or (at your option)
dcfdbac7
JB
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
3b7ad313
EN
18the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
dcfdbac7
JB
20
21
22/*
23 Event flag and `select' emulation
24
25 0 is never used
26 1 is the terminal
27 23 is the timer event flag
28 24-31 are reserved by VMS
29*/
0bc439a0 30#include <config.h>
dcfdbac7
JB
31#include <ssdef.h>
32#include <iodef.h>
33#include <dvidef.h>
34#include <clidef.h>
35#include "vmsproc.h"
0bc439a0
RS
36#include "lisp.h"
37#include "buffer.h"
38#include <file.h>
39#include "process.h"
40#include "commands.h"
41#include <errno.h>
42extern Lisp_Object call_process_cleanup ();
43
dcfdbac7
JB
44
45#define KEYBOARD_EVENT_FLAG 1
46#define TIMER_EVENT_FLAG 23
47
48static VMS_PROC_STUFF procList[MAX_EVENT_FLAGS+1];
49
50get_kbd_event_flag ()
51{
52 /*
53 Return the first event flag for keyboard input.
54 */
55 VMS_PROC_STUFF *vs = &procList[KEYBOARD_EVENT_FLAG];
56
57 vs->busy = 1;
58 vs->pid = 0;
59 return (vs->eventFlag);
60}
61
62get_timer_event_flag ()
63{
64 /*
65 Return the last event flag for use by timeouts
66 */
67 VMS_PROC_STUFF *vs = &procList[TIMER_EVENT_FLAG];
68
69 vs->busy = 1;
70 vs->pid = 0;
71 return (vs->eventFlag);
72}
73
74VMS_PROC_STUFF *
75get_vms_process_stuff ()
76{
77 /*
78 Return a process_stuff structure
79
80 We use 1-23 as our event flags to simplify implementing
81 a VMS `select' call.
82 */
83 int i;
84 VMS_PROC_STUFF *vs;
85
86 for (i=1, vs = procList; i<MAX_EVENT_FLAGS; i++, vs++)
87 {
88 if (!vs->busy)
89 {
90 vs->busy = 1;
91 vs->inputChan = 0;
92 vs->pid = 0;
93 sys$clref (vs->eventFlag);
94 return (vs);
95 }
96 }
97 return ((VMS_PROC_STUFF *)0);
98}
99
100give_back_vms_process_stuff (vs)
101 VMS_PROC_STUFF *vs;
102{
103 /*
104 Return an event flag to our pool
105 */
106 vs->busy = 0;
107 vs->inputChan = 0;
108 vs->pid = 0;
109}
110
111VMS_PROC_STUFF *
112get_vms_process_pointer (pid)
113 int pid;
114{
115 /*
116 Given a pid, return the VMS_STUFF pointer
117 */
118 int i;
119 VMS_PROC_STUFF *vs;
120
121 /* Don't search the last one */
122 for (i=0, vs=procList; i<MAX_EVENT_FLAGS; i++, vs++)
123 {
124 if (vs->busy && vs->pid == pid)
125 return (vs);
126 }
127 return ((VMS_PROC_STUFF *)0);
128}
129
130start_vms_process_read (vs)
131 VMS_PROC_STUFF *vs;
132{
133 /*
134 Start an asynchronous read on a VMS process
135 We will catch up with the output sooner or later
136 */
137 int status;
138 int ProcAst ();
139
140 status = sys$qio (vs->eventFlag, vs->outputChan, IO$_READVBLK,
141 vs->iosb, 0, vs,
142 vs->inputBuffer, sizeof (vs->inputBuffer), 0, 0, 0, 0);
143 if (status != SS$_NORMAL)
144 return (0);
145 else
146 return (1);
147}
148
149extern int waiting_for_ast; /* in sysdep.c */
150extern int timer_ef;
151extern int input_ef;
152
153select (nDesc, rdsc, wdsc, edsc, timeOut)
154 int nDesc;
155 int *rdsc;
156 int *wdsc;
157 int *edsc;
158 int *timeOut;
159{
160 /* Emulate a select call
161
162 We know that we only use event flags 1-23
163
164 timeout == 100000 & bit 0 set means wait on keyboard input until
165 something shows up. If timeout == 0, we just read the event
166 flags and return what we find. */
167
168 int nfds = 0;
169 int status;
170 int time[2];
171 int delta = -10000000;
172 int zero = 0;
173 int timeout = *timeOut;
174 unsigned long mask, readMask, waitMask;
175
176 if (rdsc)
177 readMask = *rdsc << 1; /* Unix mask is shifted over 1 */
178 else
179 readMask = 0; /* Must be a wait call */
180
181 sys$clref (KEYBOARD_EVENT_FLAG);
182 sys$setast (0); /* Block interrupts */
183 sys$readef (KEYBOARD_EVENT_FLAG, &mask); /* See what is set */
184 mask &= readMask; /* Just examine what we need */
185 if (mask == 0)
186 { /* Nothing set, we must wait */
187 if (timeout != 0)
188 { /* Not just inspecting... */
189 if (!(timeout == 100000 &&
190 readMask == (1 << KEYBOARD_EVENT_FLAG)))
191 {
192 lib$emul (&timeout, &delta, &zero, time);
193 sys$setimr (TIMER_EVENT_FLAG, time, 0, 1);
194 waitMask = readMask | (1 << TIMER_EVENT_FLAG);
195 }
196 else
197 waitMask = readMask;
198 if (waitMask & (1 << KEYBOARD_EVENT_FLAG))
199 {
200 sys$clref (KEYBOARD_EVENT_FLAG);
201 waiting_for_ast = 1; /* Only if reading from 0 */
202 }
203 sys$setast (1);
204 sys$wflor (KEYBOARD_EVENT_FLAG, waitMask);
205 sys$cantim (1, 0);
206 sys$readef (KEYBOARD_EVENT_FLAG, &mask);
207 if (readMask & (1 << KEYBOARD_EVENT_FLAG))
208 waiting_for_ast = 0;
209 }
210 }
211 sys$setast (1);
212
213 /*
214 Count number of descriptors that are ready
215 */
216 mask &= readMask;
217 if (rdsc)
218 *rdsc = (mask >> 1); /* Back to Unix format */
219 for (nfds = 0; mask; mask >>= 1)
220 {
221 if (mask & 1)
222 nfds++;
223 }
224 return (nfds);
225}
226
227#define MAX_BUFF 1024
228
229write_to_vms_process (vs, buf, len)
230 VMS_PROC_STUFF *vs;
231 char *buf;
232 int len;
233{
234 /*
235 Write something to a VMS process.
236
237 We have to map newlines to carriage returns for VMS.
238 */
239 char ourBuff[MAX_BUFF];
240 short iosb[4];
241 int status;
242 int in, out;
243
244 while (len > 0)
245 {
246 out = map_nl_to_cr (buf, ourBuff, len, MAX_BUFF);
247 status = sys$qiow (0, vs->inputChan, IO$_WRITEVBLK|IO$M_NOFORMAT,
248 iosb, 0, 0, ourBuff, out, 0, 0, 0, 0);
249 if (status != SS$_NORMAL || (status = iosb[0]) != SS$_NORMAL)
250 {
251 error ("Could not write to subprocess: %x", status);
252 return (0);
253 }
0bc439a0 254 len -= out;
dcfdbac7
JB
255 }
256 return (1);
257}
258
259static
260map_nl_to_cr (in, out, maxIn, maxOut)
261 char *in;
262 char *out;
263 int maxIn;
264 int maxOut;
265{
266 /*
267 Copy `in' to `out' remapping `\n' to `\r'
268 */
269 int c;
270 int o;
271
272 for (o=0; maxIn-- > 0 && o < maxOut; o++)
273 {
274 c = *in++;
275 *out++ = (c == '\n') ? '\r' : c;
276 }
277 return (o);
278}
279
280clean_vms_buffer (buf, len)
281 char *buf;
282 int len;
283{
284 /*
285 Sanitize output from a VMS subprocess
286 Strip CR's and NULLs
287 */
288 char *oBuf = buf;
289 char c;
290 int l = 0;
291
292 while (len-- > 0)
293 {
294 c = *buf++;
295 if (c == '\r' || c == '\0')
296 ;
297 else
298 {
299 *oBuf++ = c;
300 l++;
301 }
302 }
303 return (l);
304}
305
306/*
307 For the CMU PTY driver
308*/
309#define PTYNAME "PYA0:"
310
311get_pty_channel (inDevName, outDevName, inChannel, outChannel)
312 char *inDevName;
313 char *outDevName;
314 int *inChannel;
315 int *outChannel;
316{
317 int PartnerUnitNumber;
318 int status;
319 struct {
320 int l;
321 char *a;
322 } d;
323 struct {
324 short BufLen;
325 short ItemCode;
326 int *BufAddress;
327 int *ItemLength;
328 } g[2];
329
330 d.l = strlen (PTYNAME);
331 d.a = PTYNAME;
332 *inChannel = 0; /* Should be `short' on VMS */
333 *outChannel = 0;
334 *inDevName = *outDevName = '\0';
335 status = sys$assign (&d, inChannel, 0, 0);
336 if (status == SS$_NORMAL)
337 {
338 *outChannel = *inChannel;
339 g[0].BufLen = sizeof (PartnerUnitNumber);
340 g[0].ItemCode = DVI$_UNIT;
341 g[0].BufAddress = &PartnerUnitNumber;
342 g[0].ItemLength = (int *)0;
343 g[1].BufLen = g[1].ItemCode = 0;
344 status = sys$getdviw (0, *inChannel, 0, &g, 0, 0, 0, 0);
345 if (status == SS$_NORMAL)
346 {
347 sprintf (inDevName, "_TPA%d:", PartnerUnitNumber);
348 strcpy (outDevName, inDevName);
349 }
350 }
351 return (status);
352}
353
354VMSgetwd (buf)
355 char *buf;
356{
357 /*
358 Return the current directory
359 */
360 char curdir[256];
361 char *getenv ();
362 char *s;
363 short len;
364 int status;
365 struct
366 {
367 int l;
368 char *a;
369 } d;
370
371 s = getenv ("SYS$DISK");
372 if (s)
373 strcpy (buf, s);
374 else
375 *buf = '\0';
376
377 d.l = 255;
378 d.a = curdir;
379 status = sys$setddir (0, &len, &d);
380 if (status & 1)
381 {
382 curdir[len] = '\0';
383 strcat (buf, curdir);
384 }
385}
386\f
387static
388call_process_ast (vs)
389 VMS_PROC_STUFF *vs;
390{
391 sys$setef (vs->eventFlag);
392}
393
394void
395child_setup (in, out, err, new_argv, env)
396 int in, out, err;
397 register char **new_argv;
398 char **env;
399{
400 /* ??? I suspect that maybe this shouldn't be done on VMS. */
401#ifdef subprocesses
402 /* Close Emacs's descriptors that this process should not have. */
403 close_process_descs ();
404#endif
405
c426bed2 406 if (STRINGP (current_buffer->directory))
dcfdbac7
JB
407 chdir (XSTRING (current_buffer->directory)->data);
408}
409
410DEFUN ("call-process", Fcall_process, Scall_process, 1, MANY, 0,
411 "Call PROGRAM synchronously in a separate process.\n\
412Program's input comes from file INFILE (nil means null device, `NLA0:').\n\
413Insert output in BUFFER before point; t means current buffer;\n\
414 nil for BUFFER means discard it; 0 means discard and don't wait.\n\
415Fourth arg DISPLAY non-nil means redisplay buffer as output is inserted.\n\
416Remaining arguments are strings passed as command arguments to PROGRAM.\n\
417This function waits for PROGRAM to terminate, unless BUFFER is 0;\n\
418if you quit, the process is killed.")
419 (nargs, args)
420 int nargs;
421 register Lisp_Object *args;
422{
423 Lisp_Object display, buffer, path;
424 char oldDir[512];
425 int inchannel, outchannel;
426 int len;
427 int call_process_ast ();
428 struct
429 {
430 int l;
431 char *a;
432 } dcmd, din, dout;
433 char inDevName[65];
434 char outDevName[65];
435 short iosb[4];
436 int status;
437 int SpawnFlags = CLI$M_NOWAIT;
438 VMS_PROC_STUFF *vs;
439 VMS_PROC_STUFF *get_vms_process_stuff ();
440 int fd[2];
441 int filefd;
442 register int pid;
443 char buf[1024];
444 int count = specpdl_ptr - specpdl;
445 register unsigned char **new_argv;
446 struct buffer *old = current_buffer;
447
448 CHECK_STRING (args[0], 0);
449
d427b66a 450 if (nargs <= 1 || NILP (args[1]))
dcfdbac7
JB
451 args[1] = build_string ("NLA0:");
452 else
453 args[1] = Fexpand_file_name (args[1], current_buffer->directory);
454
455 CHECK_STRING (args[1], 1);
456
457 {
458 register Lisp_Object tem;
459 buffer = tem = args[2];
460 if (nargs <= 2)
461 buffer = Qnil;
462 else if (!(EQ (tem, Qnil) || EQ (tem, Qt)
463 || XFASTINT (tem) == 0))
464 {
465 buffer = Fget_buffer (tem);
466 CHECK_BUFFER (buffer, 2);
467 }
468 }
469
470 display = nargs >= 3 ? args[3] : Qnil;
471
472 {
473 /*
474 if (args[0] == "*dcl*" then we need to skip pas the "-c",
475 else args[0] is the program to run.
476 */
477 register int i;
478 int arg0;
479 int firstArg;
480
481 if (strcmp (XSTRING (args[0])->data, "*dcl*") == 0)
482 {
483 arg0 = 5;
484 firstArg = 6;
485 }
486 else
487 {
488 arg0 = 0;
489 firstArg = 4;
490 }
491 len = XSTRING (args[arg0])->size + 1;
492 for (i = firstArg; i < nargs; i++)
493 {
494 CHECK_STRING (args[i], i);
495 len += XSTRING (args[i])->size + 1;
496 }
497 new_argv = alloca (len);
498 strcpy (new_argv, XSTRING (args[arg0])->data);
499 for (i = firstArg; i < nargs; i++)
500 {
501 strcat (new_argv, " ");
502 strcat (new_argv, XSTRING (args[i])->data);
503 }
504 dcmd.l = len-1;
505 dcmd.a = new_argv;
506
507 status = get_pty_channel (inDevName, outDevName, &inchannel, &outchannel);
508 if (!(status & 1))
509 error ("Error getting PTY channel: %x", status);
c426bed2 510 if (INTEGERP (buffer))
dcfdbac7
JB
511 {
512 dout.l = strlen ("NLA0:");
513 dout.a = "NLA0:";
514 }
515 else
516 {
517 dout.l = strlen (outDevName);
518 dout.a = outDevName;
519 }
520
521 vs = get_vms_process_stuff ();
522 if (!vs)
523 {
524 sys$dassgn (inchannel);
525 sys$dassgn (outchannel);
526 error ("Too many VMS processes");
527 }
528 vs->inputChan = inchannel;
529 vs->outputChan = outchannel;
530 }
531
532 filefd = open (XSTRING (args[1])->data, O_RDONLY, 0);
533 if (filefd < 0)
534 {
535 sys$dassgn (inchannel);
536 sys$dassgn (outchannel);
537 give_back_vms_process_stuff (vs);
538 report_file_error ("Opening process input file", Fcons (args[1], Qnil));
539 }
540 else
541 close (filefd);
542
543 din.l = XSTRING (args[1])->size;
544 din.a = XSTRING (args[1])->data;
545
546 /*
547 Start a read on the process channel
548 */
c426bed2 549 if (!INTEGERP (buffer))
dcfdbac7
JB
550 {
551 start_vms_process_read (vs);
552 SpawnFlags = CLI$M_NOWAIT;
553 }
554 else
555 SpawnFlags = 0;
556
557 /*
558 On VMS we need to change the current directory
559 of the parent process before forking so that
560 the child inherit that directory. We remember
561 where we were before changing.
562 */
563 VMSgetwd (oldDir);
564 child_setup (0, 0, 0, 0, 0);
565 status = lib$spawn (&dcmd, &din, &dout, &SpawnFlags, 0, &vs->pid,
566 &vs->exitStatus, 0, call_process_ast, vs);
567 chdir (oldDir);
568
569 if (status != SS$_NORMAL)
570 {
571 sys$dassgn (inchannel);
572 sys$dassgn (outchannel);
573 give_back_vms_process_stuff (vs);
574 error ("Error calling LIB$SPAWN: %x", status);
575 }
576 pid = vs->pid;
577
c426bed2 578 if (INTEGERP (buffer))
dcfdbac7
JB
579 {
580#ifndef subprocesses
581 wait_without_blocking ();
582#endif subprocesses
583 return Qnil;
584 }
585
696810ff
RS
586 if (!NILP (display) && INTERACTIVE)
587 prepare_menu_bars ();
588
dcfdbac7
JB
589 record_unwind_protect (call_process_cleanup,
590 Fcons (make_number (fd[0]), make_number (pid)));
591
592
c426bed2 593 if (BUFFERP (buffer))
dcfdbac7
JB
594 Fset_buffer (buffer);
595
596 immediate_quit = 1;
597 QUIT;
598
599 while (1)
600 {
601 sys$waitfr (vs->eventFlag);
602 if (vs->iosb[0] & 1)
603 {
604 immediate_quit = 0;
d427b66a 605 if (!NILP (buffer))
dcfdbac7
JB
606 {
607 vs->iosb[1] = clean_vms_buffer (vs->inputBuffer, vs->iosb[1]);
608 InsCStr (vs->inputBuffer, vs->iosb[1]);
609 }
d427b66a 610 if (!NILP (display) && INTERACTIVE)
dcfdbac7
JB
611 redisplay_preserve_echo_area ();
612 immediate_quit = 1;
613 QUIT;
614 if (!start_vms_process_read (vs))
615 break; /* The other side went away */
616 }
617 else
618 break;
619 }
696810ff
RS
620
621 sys$dassgn (inchannel);
622 sys$dassgn (outchannel);
623 give_back_vms_process_stuff (vs);
dcfdbac7
JB
624
625 /* Wait for it to terminate, unless it already has. */
626 wait_for_termination (pid);
627
628 immediate_quit = 0;
629
630 set_current_buffer (old);
631
1f924f99 632 return unbind_to (count, Qnil);
dcfdbac7
JB
633}
634
635create_process (process, new_argv)
636 Lisp_Object process;
637 char *new_argv;
638{
639 int pid, inchannel, outchannel, forkin, forkout;
640 char old_dir[512];
641 char in_dev_name[65];
642 char out_dev_name[65];
643 short iosb[4];
644 int status;
645 int spawn_flags = CLI$M_NOWAIT;
646 int child_sig ();
647 struct {
648 int l;
649 char *a;
650 } din, dout, dprompt, dcmd;
651 VMS_PROC_STUFF *vs;
652 VMS_PROC_STUFF *get_vms_process_stuff ();
653
654 status = get_pty_channel (in_dev_name, out_dev_name, &inchannel, &outchannel);
655 if (!(status & 1))
656 {
657 remove_process (process);
658 error ("Error getting PTY channel: %x", status);
659 }
660 dout.l = strlen (out_dev_name);
661 dout.a = out_dev_name;
662 dprompt.l = strlen (DCL_PROMPT);
663 dprompt.a = DCL_PROMPT;
664
665 if (strcmp (new_argv, "*dcl*") == 0)
666 {
667 din.l = strlen (in_dev_name);
668 din.a = in_dev_name;
669 dcmd.l = 0;
670 dcmd.a = (char *)0;
671 }
672 else
673 {
674 din.l = strlen ("NLA0:");
675 din.a = "NLA0:";
676 dcmd.l = strlen (new_argv);
677 dcmd.a = new_argv;
678 }
679
680 /* Delay interrupts until we have a chance to store
681 the new fork's pid in its process structure */
682 sys$setast (0);
683
684 vs = get_vms_process_stuff ();
685 if (vs == 0)
686 {
687 sys$setast (1);
688 remove_process (process);
689 error ("Too many VMS processes");
690 }
691 vs->inputChan = inchannel;
692 vs->outputChan = outchannel;
693
694 /* Start a read on the process channel */
695 start_vms_process_read (vs);
696
697 /* Switch current directory so that the child inherits it. */
698 VMSgetwd (old_dir);
699 child_setup (0, 0, 0, 0, 0);
700
701 status = lib$spawn (&dcmd, &din, &dout, &spawn_flags, 0, &vs->pid,
702 &vs->exitStatus, 0, child_sig, vs, &dprompt);
703 chdir (old_dir);
704
705 if (status != SS$_NORMAL)
706 {
707 sys$setast (1);
708 remove_process (process);
709 error ("Error calling LIB$SPAWN: %x", status);
710 }
711 vs->pid &= 0xffff; /* It needs to fit in a FASTINT,
712 we don't need the rest of the bits */
713 pid = vs->pid;
714
715 /*
716 ON VMS process->infd holds the (event flag-1)
717 that we use for doing I/O on that process.
718 `input_wait_mask' is the cluster of event flags
719 we can wait on.
720
721 Event flags returned start at 1 for the keyboard.
722 Since Unix expects descriptor 0 for the keyboard,
8e6208c5 723 we subtract one from the event flag.
dcfdbac7
JB
724 */
725 inchannel = vs->eventFlag-1;
726
727 /* Record this as an active process, with its channels.
728 As a result, child_setup will close Emacs's side of the pipes. */
729 chan_process[inchannel] = process;
64db3848
KH
730 XSETFASTINT (XPROCESS (process)->infd, inchannel);
731 XSETFASTINT (XPROCESS (process)->outfd, outchannel);
0bc439a0 732 XPROCESS (process)->status = Qrun
dcfdbac7
JB
733
734 /* Delay interrupts until we have a chance to store
735 the new fork's pid in its process structure */
736
737#define NO_ECHO "set term/noecho\r"
738 sys$setast (0);
739 /*
740 Send a command to the process to not echo input
741
742 The CMU PTY driver does not support SETMODEs.
743 */
744 write_to_vms_process (vs, NO_ECHO, strlen (NO_ECHO));
745
64db3848 746 XSETFASTINT (XPROCESS (process)->pid, pid);
dcfdbac7
JB
747 sys$setast (1);
748}
749
750child_sig (vs)
751 VMS_PROC_STUFF *vs;
752{
753 register int pid;
754 Lisp_Object tail, proc;
755 register struct Lisp_Process *p;
756 int old_errno = errno;
757
758 pid = vs->pid;
759 sys$setef (vs->eventFlag);
760
761 for (tail = Vprocess_alist; XSYMBOL (tail) != XSYMBOL (Qnil); tail = XCONS (tail)->cdr)
762 {
763 proc = XCONS (XCONS (tail)->car)->cdr;
764 p = XPROCESS (proc);
765 if (EQ (p->childp, Qt) && XFASTINT (p->pid) == pid)
766 break;
767 }
768
769 if (XSYMBOL (tail) == XSYMBOL (Qnil))
770 return;
771
0bc439a0 772 p->status = Fcons (Qexit, Fcons (make_number (vs->exitStatus), Qnil))
dcfdbac7
JB
773}
774
775syms_of_vmsproc ()
776{
777 defsubr (&Scall_process);
778}
779
780init_vmsproc ()
781{
782 char *malloc ();
783 int i;
784 VMS_PROC_STUFF *vs;
785
786 for (vs=procList, i=0; i<MAX_EVENT_FLAGS+1; i++, vs++)
787 {
788 vs->busy = 0;
789 vs->eventFlag = i;
790 sys$clref (i);
791 vs->inputChan = 0;
792 vs->pid = 0;
793 }
794 procList[0].busy = 1; /* Zero is reserved */
795}