/* spawn.c -- implements spawn.h */ #include "spawn.h" /* spawn.c * * Various operating system access commands. * * <odified by Petri Kutvonen */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "defines.h" #include "buffer.h" #include "display.h" #include "estruct.h" #include "exec.h" #include "file.h" #include "flook.h" #include "input.h" #include "log.h" #include "terminal.h" #include "window.h" #if VMS #define EFN 0 /* Event flag. */ #include <ssdef.h> /* Random headers. */ #include <stsdef.h> #include <descrip.h> #include <iodef.h> extern int oldmode[3]; /* In "termio.c" */ extern int newmode[3]; /* In "termio.c" */ extern short iochan; /* In "termio.c" */ #endif #if V7 | USG | BSD #include <signal.h> #ifdef SIGWINCH #endif #endif #if MSDOS & (MSC | TURBO) #include <process.h> #endif /* * Create a subjob with a copy of the command intrepreter in it. When the * command interpreter exits, mark the screen as garbage so that you do a full * repaint. Bound to "^X C". The message at the start in VMS puts out a newline. * Under some (unknown) condition, you don't get one free when DCL starts up. */ int spawncli(int f, int n) { #if V7 | USG | BSD char *cp; #endif /* don't allow this command if restricted */ if (restflag) return resterr(); #if VMS movecursor(term.t_nrow, 0); /* In last line. */ mlputs("(Starting DCL)\r\n"); TTflush(); /* Ignore "ttcol". */ sgarbf = TRUE; sys(NULL); sleep(1); mlputs("\r\n(Returning from DCL)\r\n"); TTflush(); sleep(1); return TRUE; #endif #if MSDOS & (MSC | TURBO) movecursor(term.t_nrow, 0); /* Seek to last line. */ TTflush(); TTkclose(); shellprog(""); TTkopen(); sgarbf = TRUE; return TRUE; #endif #if V7 | USG | BSD movecursor(term.t_nrow, 0); /* Seek to last line. */ TTflush(); TTclose(); /* stty to old settings */ TTkclose(); /* Close "keyboard" */ if ((cp = getenv("SHELL")) != NULL && *cp != '\0') ue_system( cp) ; else #if BSD system("exec /bin/csh"); #else ue_system( "exec /bin/sh") ; #endif sgarbf = TRUE; usleep( 2000000L) ; TTopen(); TTkopen(); #ifdef SIGWINCH /* * This fools the update routines to force a full * redraw with complete window size checking. * -lbt */ chg_width = term.t_ncol; chg_height = term.t_nrow + 1; term.t_nrow = term.t_ncol = 0; #endif return TRUE; #endif } #if BSD | __hpux | SVR4 int bktoshell(int f, int n) { /* suspend MicroEMACS and wait to wake up */ vttidy(); /****************************** int pid; pid = getpid(); kill(pid,SIGTSTP); ******************************/ kill(0, SIGTSTP); return TRUE; } void rtfrmshell(void) { TTopen(); curwp->w_flag = WFHARD; sgarbf = TRUE; } #endif /* * Run a one-liner in a subjob. When the command returns, wait for a single * character to be typed, then mark the screen as garbage so a full repaint is * done. Bound to "C-X !". */ int spawn(int f, int n) { int s; char line[NLINE]; /* don't allow this command if restricted */ if (restflag) return resterr(); #if VMS if ((s = mlreply("!", line, NLINE)) != TRUE) return s; movecursor(term.t_nrow, 0); TTflush(); s = sys(line); /* Run the command. */ if (clexec == FALSE) { mlputs("\r\n\n(End)"); /* Pause. */ TTflush(); tgetc(); } sgarbf = TRUE; return s; #endif #if MSDOS if ((s = mlreply("!", line, NLINE)) != TRUE) return s; movecursor(term.t_nrow, 0); TTkclose(); shellprog(line); TTkopen(); /* if we are interactive, pause here */ if (clexec == FALSE) { mlputs("\r\n(End)"); tgetc(); } sgarbf = TRUE; return TRUE; #endif #if V7 | USG | BSD if ((s = mlreply("!", line, NLINE)) != TRUE) return s; TTflush(); TTclose(); /* stty to old modes */ TTkclose(); ue_system( line) ; fflush(stdout); /* to be sure P.K. */ TTopen(); if (clexec == FALSE) { mlputs("(End)"); /* Pause. */ TTflush(); while ((s = tgetc()) != '\r' && s != ' '); mlputs("\r\n"); } TTkopen(); sgarbf = TRUE; return TRUE; #endif } /* * Run an external program with arguments. When it returns, wait for a single * character to be typed, then mark the screen as garbage so a full repaint is * done. Bound to "C-X $". */ int execprg(int f, int n) { int s; char line[NLINE]; /* don't allow this command if restricted */ if (restflag) return resterr(); #if VMS if ((s = mlreply("!", line, NLINE)) != TRUE) return s; TTflush(); s = sys(line); /* Run the command. */ mlputs("\r\n\n(End)"); /* Pause. */ TTflush(); tgetc(); sgarbf = TRUE; return s; #endif #if MSDOS if ((s = mlreply("$", line, NLINE)) != TRUE) return s; movecursor(term.t_nrow, 0); TTkclose(); execprog(line); TTkopen(); /* if we are interactive, pause here */ if (clexec == FALSE) { mlputs("\r\n(End)"); tgetc(); } sgarbf = TRUE; return TRUE; #endif #if V7 | USG | BSD if ((s = mlreply("!", line, NLINE)) != TRUE) return s; TTputc('\n'); /* Already have '\r' */ TTflush(); TTclose(); /* stty to old modes */ TTkclose(); ue_system( line) ; fflush(stdout); /* to be sure P.K. */ TTopen(); mlputs("(End)"); /* Pause. */ TTflush(); while ((s = tgetc()) != '\r' && s != ' '); sgarbf = TRUE; return TRUE; #endif } /* * Pipe a one line command into a window * Bound to ^X @ */ int pipecmd(int f, int n) { int s; /* return status from CLI */ struct window *wp; /* pointer to new window */ struct buffer *bp; /* pointer to buffer to zot */ char line[NLINE]; /* command line send to shell */ static char bname[] = "command"; static char filnam[NSTRING] = "command"; #if MSDOS char *tmp; FILE *fp; int len; #endif /* don't allow this command if restricted */ if (restflag) return resterr(); #if MSDOS if ((tmp = getenv("TMP")) == NULL && (tmp = getenv("TEMP")) == NULL) strcpy(filnam, "command"); else { strcpy(filnam, tmp); len = strlen(tmp); if (len <= 0 || filnam[len - 1] != '\\' && filnam[len - 1] != '/') strcat(filnam, "\\"); strcat(filnam, "command"); } #endif #if VMS mlwrite("Not available under VMS"); return FALSE; #endif /* get the command to pipe in */ if ((s = mlreply("@", line, NLINE)) != TRUE) return s; /* get rid of the command output buffer if it exists */ if ((bp = bfind(bname, FALSE, 0)) != FALSE) { /* try to make sure we are off screen */ wp = wheadp; while (wp != NULL) { if (wp->w_bufp == bp) { #if PKCODE if (wp == curwp) delwind(FALSE, 1); else onlywind(FALSE, 1); break; #else onlywind(FALSE, 1); break; #endif } wp = wp->w_wndp; } if (zotbuf(bp) != TRUE) return FALSE; } #if MSDOS strcat(line, " >>"); strcat(line, filnam); movecursor(term.t_nrow, 0); TTkclose(); shellprog(line); TTkopen(); sgarbf = TRUE; if ((fp = fopen(filnam, "r")) == NULL) { s = FALSE; } else { fclose(fp); s = TRUE; } #endif #if V7 | USG | BSD TTflush(); TTclose(); /* stty to old modes */ TTkclose(); strcat(line, ">"); strcat(line, filnam); ue_system( line) ; TTopen(); TTkopen(); TTflush(); sgarbf = TRUE; s = TRUE; #endif if (s != TRUE) return s; /* split the current window to make room for the command output */ if (splitwind(FALSE, 1) == FALSE) return FALSE; /* and read the stuff in */ if (getfile(filnam, FALSE) == FALSE) return FALSE; /* make this window in VIEW mode, update all mode lines */ curwp->w_bufp->b_mode |= MDVIEW; wp = wheadp; while (wp != NULL) { wp->w_flag |= WFMODE; wp = wp->w_wndp; } /* and get rid of the temporary file */ unlink(filnam); return TRUE; } /* * filter a buffer through an external DOS program * Bound to ^X # */ int filter_buffer(int f, int n) { int s; /* return status from CLI */ struct buffer *bp; /* pointer to buffer to zot */ char line[NLINE]; /* command line send to shell */ fname_t tmpnam ; /* place to store real file name */ static char bname1[] = "fltinp"; static char filnam1[] = "fltinp"; static char filnam2[] = "fltout"; /* don't allow this command if restricted */ if (restflag) return resterr(); if (curbp->b_mode & MDVIEW) /* don't allow this command if */ return rdonly(); /* we are in read only mode */ #if VMS mlwrite("Not available under VMS"); return FALSE; #endif /* get the filter name and its args */ if ((s = mlreply("#", line, NLINE)) != TRUE) return s; /* setup the proper file names */ bp = curbp; strcpy(tmpnam, bp->b_fname); /* save the original name */ strcpy(bp->b_fname, bname1); /* set it to our new one */ /* write it out, checking for errors */ if (writeout(filnam1) != TRUE) { mlwrite("(Cannot write filter file)"); strcpy(bp->b_fname, tmpnam); return FALSE; } #if MSDOS strcat(line, " <fltinp >fltout"); movecursor(term.t_nrow - 1, 0); TTkclose(); shellprog(line); TTkopen(); sgarbf = TRUE; s = TRUE; #endif #if V7 | USG | BSD TTputc('\n'); /* Already have '\r' */ TTflush(); TTclose(); /* stty to old modes */ TTkclose(); strcat(line, " <fltinp >fltout"); ue_system( line) ; TTopen(); TTkopen(); TTflush(); sgarbf = TRUE; s = TRUE; #endif /* on failure, escape gracefully */ if (s != TRUE || (readin(filnam2, FALSE) == FALSE)) { mlwrite("(Execution failed)"); strcpy(bp->b_fname, tmpnam); unlink(filnam1); unlink(filnam2); return s; } /* reset file name */ strcpy(bp->b_fname, tmpnam); /* restore name */ bp->b_flag |= BFCHG; /* flag it as changed */ /* and get rid of the temporary file */ unlink(filnam1); unlink(filnam2); return TRUE; } #if VMS /* * Run a command. The "cmd" is a pointer to a command string, or NULL if you * want to run a copy of DCL in the subjob (this is how the standard routine * LIB$SPAWN works. You have to do wierd stuff with the terminal on the way in * and the way out, because DCL does not want the channel to be in raw mode. */ int sys(char *cmd) { struct dsc$descriptor cdsc; struct dsc$descriptor *cdscp; long status; long substatus; long iosb[2]; status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0, oldmode, sizeof(oldmode), 0, 0, 0, 0); if (status != SS$_NORMAL || (iosb[0] & 0xFFFF) != SS$_NORMAL) return FALSE; cdscp = NULL; /* Assume DCL. */ if (cmd != NULL) { /* Build descriptor. */ cdsc.dsc$a_pointer = cmd; cdsc.dsc$w_length = strlen(cmd); cdsc.dsc$b_dtype = DSC$K_DTYPE_T; cdsc.dsc$b_class = DSC$K_CLASS_S; cdscp = &cdsc; } status = LIB$SPAWN(cdscp, 0, 0, 0, 0, 0, &substatus, 0, 0, 0); if (status != SS$_NORMAL) substatus = status; status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0, newmode, sizeof(newmode), 0, 0, 0, 0); if (status != SS$_NORMAL || (iosb[0] & 0xFFFF) != SS$_NORMAL) return FALSE; if ((substatus & STS$M_SUCCESS) == 0) /* Command failed. */ return FALSE; return TRUE; } #endif #if MSDOS & (TURBO | MSC) /* * SHELLPROG: Execute a command in a subshell * * char *cmd; Incoming command line to execute */ int shellprog(char *cmd) { char *shell; /* Name of system command processor */ char *p; /* Temporary pointer */ char swchar; /* switch character to use */ union REGS regs; /* parameters for dos call */ char comline[NSTRING]; /* constructed command line */ /* detect current switch character and set us up to use it */ regs.h.ah = 0x37; /* get setting data */ regs.h.al = 0x00; /* get switch character */ intdos(®s, ®s); swchar = (char) regs.h.dl; /* get name of system shell */ if ((shell = getenv("COMSPEC")) == NULL) { return FALSE; /* No shell located */ } /* trim leading whitespace off the command */ while (*cmd == ' ' || *cmd == '\t') /* find out if null command */ cmd++; /** If the command line is not empty, bring up the shell **/ /** and execute the command. Otherwise, bring up the **/ /** shell in interactive mode. **/ if (*cmd) { strcpy(comline, shell); strcat(comline, " "); comline[strlen(comline) + 1] = 0; comline[strlen(comline)] = swchar; strcat(comline, "c "); strcat(comline, cmd); return execprog(comline); } else return execprog(shell); } /* * EXECPROG: * A function to execute a named program * with arguments * * char *cmd; Incoming command line to execute */ int execprog(char *cmd) { char *sp; /* temporary string pointer */ char f1[38]; /* FCB1 area (not initialized */ char f2[38]; /* FCB2 area (not initialized */ char prog[NSTRING]; /* program filespec */ char tail[NSTRING]; /* command tail with length byte */ union REGS regs; /* parameters for dos call */ struct SREGS segreg; /* segment registers for dis call */ struct pblock { /* EXEC parameter block */ short envptr; /* 2 byte pointer to environment string */ char *cline; /* 4 byte pointer to command line */ char *fcb1; /* 4 byte pointer to FCB at PSP+5Ch */ char *fcb2; /* 4 byte pointer to FCB at PSP+6Ch */ } pblock; /* parse the command name from the command line */ sp = prog; while (*cmd && (*cmd != ' ') && (*cmd != '\t')) *sp++ = *cmd++; *sp = 0; /* and parse out the command tail */ while (*cmd && ((*cmd == ' ') || (*cmd == '\t'))) ++cmd; *tail = (char) (strlen(cmd)); /* record the byte length */ strcpy(&tail[1], cmd); strcat(&tail[1], "\r"); /* look up the program on the path trying various extentions */ if ((sp = flook(prog, TRUE)) == NULL) if ((sp = flook(strcat(prog, ".exe"), TRUE)) == NULL) { strcpy(&prog[strlen(prog) - 4], ".com"); if ((sp = flook(prog, TRUE)) == NULL) return FALSE; } strcpy(prog, sp); /* get a pointer to this PSPs environment segment number */ segread(&segreg); /* set up the EXEC parameter block */ pblock.envptr = 0; /* make the child inherit the parents env */ pblock.fcb1 = f1; /* point to a blank FCB */ pblock.fcb2 = f2; /* point to a blank FCB */ pblock.cline = tail; /* parameter line pointer */ /* and make the call */ regs.h.ah = 0x4b; /* EXEC Load or Execute a Program */ regs.h.al = 0x00; /* load end execute function subcode */ segreg.ds = ((unsigned long) (prog) >> 16); /* program name ptr */ regs.x.dx = (unsigned int) (prog); segreg.es = ((unsigned long) (&pblock) >> 16); /* set up param block ptr */ regs.x.bx = (unsigned int) (&pblock); #if TURBO | MSC intdosx(®s, ®s, &segreg); if (regs.x.cflag == 0) { regs.h.ah = 0x4d; /* get child process return code */ intdos(®s, ®s); /* go do it */ rval = regs.x.ax; /* save child's return code */ } else #if MSC rval = -1; #else rval = -_doserrno; /* failed child call */ #endif #endif return (rval < 0) ? FALSE : TRUE; } #endif