424 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			424 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* system.c  -- UNIX version */
 | |
| 
 | |
| /* Author:
 | |
|  *	Steve Kirkendall
 | |
|  *	14407 SW Teal Blvd. #C
 | |
|  *	Beaverton, OR 97005
 | |
|  *	kirkenda@cs.pdx.edu
 | |
|  */
 | |
| 
 | |
| 
 | |
| /* This file contains a new version of the system() function and related stuff.
 | |
|  *
 | |
|  * Entry points are:
 | |
|  *	system(cmd)		- run a single shell command
 | |
|  *	wildcard(names)		- expand wildcard characters in filanames
 | |
|  *	filter(m,n,cmd,back)	- run text lines through a filter program
 | |
|  *
 | |
|  * This is probably the single least portable file in the program.  The code
 | |
|  * shown here should work correctly if it links at all; it will work on UNIX
 | |
|  * and any O.S./Compiler combination which adheres to UNIX forking conventions.
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| #include "vi.h"
 | |
| extern char	**environ;
 | |
| 
 | |
| #if ANY_UNIX
 | |
| 
 | |
| /* This is a new version of the system() function.  The only difference
 | |
|  * between this one and the library one is: this one uses the o_shell option.
 | |
|  */
 | |
| int system(cmd)
 | |
| 	char	*cmd;	/* a command to run */
 | |
| {
 | |
| 	int	pid;	/* process ID of child */
 | |
| 	int	died;
 | |
| 	int	status;	/* exit status of the command */
 | |
| 
 | |
| 
 | |
| 	signal(SIGINT, SIG_IGN);
 | |
| 	pid = fork();
 | |
| 	switch (pid)
 | |
| 	{
 | |
| 	  case -1:						/* error */
 | |
| 		msg("fork() failed");
 | |
| 		status = -1;
 | |
| 		break;
 | |
| 
 | |
| 	  case 0:						/* child */
 | |
| 		/* for the child, close all files except stdin/out/err */
 | |
| 		for (status = 3; status < 60 && (close(status), errno != EINVAL); status++)
 | |
| 		{
 | |
| 		}
 | |
| 
 | |
| 		signal(SIGINT, SIG_DFL);
 | |
| 		if (cmd == o_shell)
 | |
| 		{
 | |
| 			execle(o_shell, o_shell, (char *)0, environ);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
 | |
| 		}
 | |
| 		msg("execle(\"%s\", ...) failed", o_shell);
 | |
| 		exit(1); /* if we get here, the exec failed */
 | |
| 
 | |
| 	  default:						/* parent */
 | |
| 		do
 | |
| 		{
 | |
| 			died = wait(&status);
 | |
| 		} while (died >= 0 && died != pid);
 | |
| 		if (died < 0)
 | |
| 		{
 | |
| 			status = -1;
 | |
| 		}
 | |
| #if __GNUC__ || _ANSI
 | |
| 		signal(SIGINT, (void (*)()) trapint);
 | |
| #else
 | |
| 		signal(SIGINT, trapint);
 | |
| #endif
 | |
| 	}
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| /* This private function opens a pipe from a filter.  It is similar to the
 | |
|  * system() function above, and to popen(cmd, "r").
 | |
|  */
 | |
| int rpipe(cmd, in)
 | |
| 	char	*cmd;	/* the filter command to use */
 | |
| 	int	in;	/* the fd to use for stdin */
 | |
| {
 | |
| 	int	r0w1[2];/* the pipe fd's */
 | |
| 
 | |
| 	/* make the pipe */
 | |
| 	if (pipe(r0w1) < 0)
 | |
| 	{
 | |
| 		return -1;	/* pipe failed */
 | |
| 	}
 | |
| 
 | |
| 	/* The parent process (elvis) ignores signals while the filter runs.
 | |
| 	 * The child (the filter program) will reset this, so that it can
 | |
| 	 * catch the signal.
 | |
| 	 */
 | |
| 	signal(SIGINT, SIG_IGN);
 | |
| 
 | |
| 	switch (fork())
 | |
| 	{
 | |
| 	  case -1:						/* error */
 | |
| 		return -1;
 | |
| 
 | |
| 	  case 0:						/* child */
 | |
| 		/* close the "read" end of the pipe */
 | |
| 		close(r0w1[0]);
 | |
| 
 | |
| 		/* redirect stdout to go to the "write" end of the pipe */
 | |
| 		close(1);
 | |
| 		dup(r0w1[1]);
 | |
| 		close(2);
 | |
| 		dup(r0w1[1]);
 | |
| 		close(r0w1[1]);
 | |
| 
 | |
| 		/* redirect stdin */
 | |
| 		if (in != 0)
 | |
| 		{
 | |
| 			close(0);
 | |
| 			dup(in);
 | |
| 			close(in);
 | |
| 		}
 | |
| 
 | |
| 		/* the filter should accept SIGINT signals */
 | |
| 		signal(SIGINT, SIG_DFL);
 | |
| 
 | |
| 		/* exec the shell to run the command */
 | |
| 		execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
 | |
| 		exit(1); /* if we get here, exec failed */
 | |
| 
 | |
| 	  default:						/* parent */
 | |
| 		/* close the "write" end of the pipe */	
 | |
| 		close(r0w1[1]);
 | |
| 
 | |
| 		return r0w1[0];
 | |
| 	}
 | |
| }
 | |
| 
 | |
| #endif /* non-DOS */
 | |
| 
 | |
| #if OSK
 | |
| 
 | |
| /* This private function opens a pipe from a filter.  It is similar to the
 | |
|  * system() function above, and to popen(cmd, "r").
 | |
|  */
 | |
| int rpipe(cmd, in)
 | |
| 	char	*cmd;	/* the filter command to use */
 | |
| 	int	in;	/* the fd to use for stdin */
 | |
| {
 | |
| 	return osk_popen(cmd, "r", in, 0);
 | |
| }	
 | |
| #endif
 | |
| 
 | |
| #if ANY_UNIX || OSK
 | |
| 
 | |
| /* This function closes the pipe opened by rpipe(), and returns 0 for success */
 | |
| int rpclose(fd)
 | |
| 	int	fd;
 | |
| {
 | |
| 	int	status;
 | |
| 
 | |
| 	close(fd);
 | |
| 	wait(&status);
 | |
| #if __GNUC__ || _ANSI
 | |
| 	signal(SIGINT, (void (*)()) trapint);
 | |
| #else
 | |
| 	signal(SIGINT, trapint);
 | |
| #endif
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| #endif /* non-DOS */
 | |
| 
 | |
| /* This function expands wildcards in a filename or filenames.  It does this
 | |
|  * by running the "echo" command on the filenames via the shell; it is assumed
 | |
|  * that the shell will expand the names for you.  If for any reason it can't
 | |
|  * run echo, then it returns the names unmodified.
 | |
|  */
 | |
| 
 | |
| #if MSDOS || TOS
 | |
| #define	PROG	"wildcard "
 | |
| #define	PROGLEN	9
 | |
| #include <string.h>
 | |
| #else
 | |
| #define	PROG	"echo "
 | |
| #define	PROGLEN	5
 | |
| #endif
 | |
| 
 | |
| #if !AMIGA
 | |
| char *wildcard(names)
 | |
| 	char	*names;
 | |
| {
 | |
| 
 | |
| # if VMS
 | |
| /* 
 | |
|    We could use expand() [vmswild.c], but what's the point on VMS? 
 | |
|    Anyway, echo is the wrong thing to do, it takes too long to build
 | |
|    a subprocess on VMS and any "echo" program would have to be supplied
 | |
|    by elvis.  More importantly, many VMS utilities expand names 
 | |
|    themselves (the shell doesn't do any expansion) so the concept is
 | |
|    non-native.  jdc
 | |
| */
 | |
| 	return names;
 | |
| # else
 | |
| 
 | |
| 	int	i, j, fd;
 | |
| 	REG char *s, *d;
 | |
| 
 | |
| 
 | |
| 	/* build the echo command */
 | |
| 	if (names != tmpblk.c)
 | |
| 	{
 | |
| 		/* the names aren't in tmpblk.c, so we can do it the easy way */
 | |
| 		strcpy(tmpblk.c, PROG);
 | |
| 		strcat(tmpblk.c, names);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		/* the names are already in tmpblk.c, so shift them to make
 | |
| 		 * room for the word "echo "
 | |
| 		 */
 | |
| 		for (s = names + strlen(names) + 1, d = s + PROGLEN; s > names; )
 | |
| 		{
 | |
| 			*--d = *--s;
 | |
| 		}
 | |
| 		strncpy(names, PROG, PROGLEN);
 | |
| 	}
 | |
| 
 | |
| 	/* run the command & read the resulting names */
 | |
| 	fd = rpipe(tmpblk.c, 0);
 | |
| 	if (fd < 0) return names;
 | |
| 	i = 0;
 | |
| 	do
 | |
| 	{
 | |
| 		j = tread(fd, tmpblk.c + i, BLKSIZE - i);
 | |
| 		i += j;
 | |
| 	} while (j > 0);
 | |
| 
 | |
| 	/* successful? */
 | |
| 	if (rpclose(fd) == 0 && j == 0 && i < BLKSIZE && i > 0)
 | |
| 	{
 | |
| 		tmpblk.c[i-1] = '\0'; /* "i-1" so we clip off the newline */
 | |
| 		return tmpblk.c;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		return names;
 | |
| 	}
 | |
| # endif
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /* This function runs a range of lines through a filter program, and replaces
 | |
|  * the original text with the filtered version.  As a special case, if "to"
 | |
|  * is MARK_UNSET, then it runs the filter program with stdin coming from
 | |
|  * /dev/null, and inserts any output lines.
 | |
|  */
 | |
| int filter(from, to, cmd, back)
 | |
| 	MARK	from, to;	/* the range of lines to filter */
 | |
| 	char	*cmd;		/* the filter command */
 | |
| 	int	back;		/* boolean: will we read lines back? */
 | |
| {
 | |
| 	int	scratch;	/* fd of the scratch file */
 | |
| 	int	fd;		/* fd of the pipe from the filter */
 | |
| 	char	scrout[50];	/* name of the scratch out file */
 | |
| 	MARK	new;		/* place where new text should go */
 | |
| 	long	sent, rcvd;	/* number of lines sent/received */
 | |
| 	int	i, j;
 | |
| 
 | |
| 	/* write the lines (if specified) to a temp file */
 | |
| 	if (to)
 | |
| 	{
 | |
| 		/* we have lines */
 | |
| #if MSDOS || TOS
 | |
| 		strcpy(scrout, o_directory);
 | |
| 		if ((i=strlen(scrout)) && !strchr("\\/:", scrout[i-1]))
 | |
| 			scrout[i++]=SLASH;
 | |
| 		strcpy(scrout+i, SCRATCHOUT+3);
 | |
| #else
 | |
| 		sprintf(scrout, SCRATCHOUT, o_directory);
 | |
| #endif
 | |
| 		mktemp(scrout);
 | |
| 		cmd_write(from, to, CMD_BANG, FALSE, scrout);
 | |
| 		sent = markline(to) - markline(from) + 1L;
 | |
| 
 | |
| 		/* use those lines as stdin */
 | |
| 		scratch = open(scrout, O_RDONLY);
 | |
| 		if (scratch < 0)
 | |
| 		{
 | |
| 			unlink(scrout);
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		scratch = 0;
 | |
| 		sent = 0L;
 | |
| 	}
 | |
| 
 | |
| 	/* start the filter program */
 | |
| #if VMS
 | |
| 	/* 
 | |
| 	   VMS doesn't know a thing about file descriptor 0.  The rpipe
 | |
| 	   concept is non-portable.  Hence we need a file name argument.
 | |
| 	*/
 | |
| 	fd = rpipe(cmd, scratch, scrout);
 | |
| #else
 | |
| 	fd = rpipe(cmd, scratch);
 | |
| #endif
 | |
| 	if (fd < 0)
 | |
| 	{
 | |
| 		if (to)
 | |
| 		{
 | |
| 			close(scratch);
 | |
| 			unlink(scrout);
 | |
| 		}
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (back)
 | |
| 	{
 | |
| 		ChangeText
 | |
| 		{
 | |
| 			/* adjust MARKs for whole lines, and set "new" */
 | |
| 			from &= ~(BLKSIZE - 1);
 | |
| 			if (to)
 | |
| 			{
 | |
| 				to &= ~(BLKSIZE - 1);
 | |
| 				to += BLKSIZE;
 | |
| 				new = to;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				new = from + BLKSIZE;
 | |
| 			}
 | |
| 
 | |
| #if VMS
 | |
| /* Reading from a VMS mailbox (pipe) is record oriented... */
 | |
| # define tread vms_pread
 | |
| #endif
 | |
| 
 | |
| 			/* repeatedly read in new text and add it */
 | |
| 			rcvd = 0L;
 | |
| 			while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
 | |
| 			{
 | |
| 				tmpblk.c[i] = '\0';
 | |
| 				add(new, tmpblk.c);
 | |
| #if VMS
 | |
| 				/* What!  An advantage to record oriented reads? */
 | |
| 				new += (i - 1);
 | |
| 				new = (new & ~(BLKSIZE - 1)) + BLKSIZE;
 | |
| 				rcvd++;
 | |
| #else
 | |
| 				for (i = 0; tmpblk.c[i]; i++)
 | |
| 				{
 | |
| 					if (tmpblk.c[i] == '\n')
 | |
| 					{
 | |
| 						new = (new & ~(BLKSIZE - 1)) + BLKSIZE;
 | |
| 						rcvd++;
 | |
| 					}
 | |
| 					else
 | |
| 					{
 | |
| 						new++;
 | |
| 					}
 | |
| 				}
 | |
| #endif
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/* delete old text, if any */
 | |
| 		if (to)
 | |
| 		{
 | |
| 			cut(from, to);
 | |
| 			delete(from, to);
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		/* read the command's output, and copy it to the screen */
 | |
| 		while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
 | |
| 		{
 | |
| 			for (j = 0; j < i; j++)
 | |
| 			{
 | |
| 				addch(tmpblk.c[j]);
 | |
| 			}
 | |
| 		}
 | |
| 		rcvd = 0;
 | |
| 	}
 | |
| 
 | |
| 	/* Reporting... */
 | |
| 	if (sent >= *o_report || rcvd >= *o_report)
 | |
| 	{
 | |
| 		if (sent > 0L && rcvd > 0L)
 | |
| 		{
 | |
| 			msg("%ld lines out, %ld lines back", sent, rcvd);
 | |
| 		}
 | |
| 		else if (sent > 0)
 | |
| 		{
 | |
| 			msg("%ld lines written to filter", sent);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			msg("%ld lines read from filter", rcvd);
 | |
| 		}
 | |
| 	}
 | |
| 	rptlines = 0L;
 | |
| 
 | |
| 	/* cleanup */
 | |
| 	rpclose(fd);
 | |
| 	if (to)
 | |
| 	{
 | |
| 		close(scratch);
 | |
| 		unlink(scrout);
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | 
