424 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			424 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/* 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;
 | 
						|
}
 |