160786Sps/*
2240121Sdelphij * Copyright (C) 1984-2012  Mark Nudelman
360786Sps *
460786Sps * You may distribute under the terms of either the GNU General Public
560786Sps * License or the Less License, as specified in the README file.
660786Sps *
7240121Sdelphij * For more information, see the README file.
860786Sps */
960786Sps
1060786Sps
1160786Sps/*
1260786Sps * Routines to execute other programs.
1360786Sps * Necessarily very OS dependent.
1460786Sps */
1560786Sps
1689019Sps#include "less.h"
1760786Sps#include <signal.h>
1860786Sps#include "position.h"
1960786Sps
2060786Sps#if MSDOS_COMPILER
2160786Sps#include <dos.h>
2260786Sps#ifdef _MSC_VER
2360786Sps#include <direct.h>
2460786Sps#define setdisk(n) _chdrive((n)+1)
2560786Sps#else
2660786Sps#include <dir.h>
2760786Sps#endif
2860786Sps#endif
2960786Sps
3060786Spsextern int screen_trashed;
3160786Spsextern IFILE curr_ifile;
3260786Sps
3360786Sps
3460786Sps#if HAVE_SYSTEM
3560786Sps
3660786Sps/*
3760786Sps * Pass the specified command to a shell to be executed.
3860786Sps * Like plain "system()", but handles resetting terminal modes, etc.
3960786Sps */
4060786Sps	public void
4160786Spslsystem(cmd, donemsg)
4260786Sps	char *cmd;
4360786Sps	char *donemsg;
4460786Sps{
4560786Sps	register int inp;
4660786Sps#if HAVE_SHELL
4760786Sps	register char *shell;
4860786Sps	register char *p;
4960786Sps#endif
5060786Sps	IFILE save_ifile;
51191930Sdelphij#if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C
5260786Sps	char cwd[FILENAME_MAX+1];
5360786Sps#endif
5460786Sps
5560786Sps	/*
5660786Sps	 * Print the command which is to be executed,
5760786Sps	 * unless the command starts with a "-".
5860786Sps	 */
5960786Sps	if (cmd[0] == '-')
6060786Sps		cmd++;
6160786Sps	else
6260786Sps	{
6360786Sps		clear_bot();
6460786Sps		putstr("!");
6560786Sps		putstr(cmd);
6660786Sps		putstr("\n");
6760786Sps	}
6860786Sps
6960786Sps#if MSDOS_COMPILER
70191930Sdelphij#if MSDOS_COMPILER==WIN32C
71191930Sdelphij	if (*cmd == '\0')
72191930Sdelphij		cmd = getenv("COMSPEC");
73191930Sdelphij#else
7460786Sps	/*
7560786Sps	 * Working directory is global on MSDOS.
7660786Sps	 * The child might change the working directory, so we
7760786Sps	 * must save and restore CWD across calls to "system",
7860786Sps	 * or else we won't find our file when we return and
7960786Sps	 * try to "reedit_ifile" it.
8060786Sps	 */
8160786Sps	getcwd(cwd, FILENAME_MAX);
8260786Sps#endif
83191930Sdelphij#endif
8460786Sps
8560786Sps	/*
8660786Sps	 * Close the current input file.
8760786Sps	 */
8860786Sps	save_ifile = save_curr_ifile();
8960786Sps	(void) edit_ifile(NULL_IFILE);
9060786Sps
9160786Sps	/*
9260786Sps	 * De-initialize the terminal and take out of raw mode.
9360786Sps	 */
9460786Sps	deinit();
9560786Sps	flush();	/* Make sure the deinit chars get out */
9660786Sps	raw_mode(0);
9760786Sps#if MSDOS_COMPILER==WIN32C
9860786Sps	close_getchr();
9960786Sps#endif
10060786Sps
10160786Sps	/*
10260786Sps	 * Restore signals to their defaults.
10360786Sps	 */
10460786Sps	init_signals(0);
10560786Sps
10660786Sps#if HAVE_DUP
10760786Sps	/*
10860786Sps	 * Force standard input to be the user's terminal
10960786Sps	 * (the normal standard input), even if less's standard input
11060786Sps	 * is coming from a pipe.
11160786Sps	 */
11260786Sps	inp = dup(0);
11360786Sps	close(0);
11489019Sps#if OS2
11589019Sps	/* The __open() system call translates "/dev/tty" to "con". */
11689019Sps	if (__open("/dev/tty", OPEN_READ) < 0)
11789019Sps#else
11860786Sps	if (open("/dev/tty", OPEN_READ) < 0)
11989019Sps#endif
12060786Sps		dup(inp);
12160786Sps#endif
12260786Sps
12360786Sps	/*
12460786Sps	 * Pass the command to the system to be executed.
12560786Sps	 * If we have a SHELL environment variable, use
12660786Sps	 * <$SHELL -c "command"> instead of just <command>.
12760786Sps	 * If the command is empty, just invoke a shell.
12860786Sps	 */
12960786Sps#if HAVE_SHELL
13060786Sps	p = NULL;
13160786Sps	if ((shell = lgetenv("SHELL")) != NULL && *shell != '\0')
13260786Sps	{
13360786Sps		if (*cmd == '\0')
13460786Sps			p = save(shell);
13560786Sps		else
13660786Sps		{
137128345Stjr			char *esccmd = shell_quote(cmd);
138128345Stjr			if (esccmd != NULL)
13960786Sps			{
140161475Sdelphij				int len = strlen(shell) + strlen(esccmd) + 5;
141161475Sdelphij				p = (char *) ecalloc(len, sizeof(char));
142161475Sdelphij				SNPRINTF3(p, len, "%s %s %s", shell, shell_coption(), esccmd);
14360786Sps				free(esccmd);
14460786Sps			}
14560786Sps		}
14660786Sps	}
14760786Sps	if (p == NULL)
14860786Sps	{
14960786Sps		if (*cmd == '\0')
15060786Sps			p = save("sh");
15160786Sps		else
15260786Sps			p = save(cmd);
15360786Sps	}
15460786Sps	system(p);
15560786Sps	free(p);
15660786Sps#else
15760786Sps#if MSDOS_COMPILER==DJGPPC
15860786Sps	/*
15960786Sps	 * Make stdin of the child be in cooked mode.
16060786Sps	 */
16160786Sps	setmode(0, O_TEXT);
16260786Sps	/*
16360786Sps	 * We don't need to catch signals of the child (it
16460786Sps	 * also makes trouble with some DPMI servers).
16560786Sps	 */
16660786Sps	__djgpp_exception_toggle();
16760786Sps  	system(cmd);
16860786Sps	__djgpp_exception_toggle();
16960786Sps#else
17060786Sps	system(cmd);
17160786Sps#endif
17260786Sps#endif
17360786Sps
17460786Sps#if HAVE_DUP
17560786Sps	/*
17660786Sps	 * Restore standard input, reset signals, raw mode, etc.
17760786Sps	 */
17860786Sps	close(0);
17960786Sps	dup(inp);
18060786Sps	close(inp);
18160786Sps#endif
18260786Sps
18360786Sps#if MSDOS_COMPILER==WIN32C
18460786Sps	open_getchr();
18560786Sps#endif
18660786Sps	init_signals(1);
18760786Sps	raw_mode(1);
18860786Sps	if (donemsg != NULL)
18960786Sps	{
19060786Sps		putstr(donemsg);
19160786Sps		putstr("  (press RETURN)");
19260786Sps		get_return();
19360786Sps		putchr('\n');
19460786Sps		flush();
19560786Sps	}
19660786Sps	init();
19760786Sps	screen_trashed = 1;
19860786Sps
199191930Sdelphij#if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C
20060786Sps	/*
20160786Sps	 * Restore the previous directory (possibly
20260786Sps	 * changed by the child program we just ran).
20360786Sps	 */
20460786Sps	chdir(cwd);
20560786Sps#if MSDOS_COMPILER != DJGPPC
20660786Sps	/*
20760786Sps	 * Some versions of chdir() don't change to the drive
20860786Sps	 * which is part of CWD.  (DJGPP does this in chdir.)
20960786Sps	 */
21060786Sps	if (cwd[1] == ':')
21160786Sps	{
21260786Sps		if (cwd[0] >= 'a' && cwd[0] <= 'z')
21360786Sps			setdisk(cwd[0] - 'a');
21460786Sps		else if (cwd[0] >= 'A' && cwd[0] <= 'Z')
21560786Sps			setdisk(cwd[0] - 'A');
21660786Sps	}
21760786Sps#endif
21860786Sps#endif
21960786Sps
22060786Sps	/*
22160786Sps	 * Reopen the current input file.
22260786Sps	 */
22360786Sps	reedit_ifile(save_ifile);
22460786Sps
22560786Sps#if defined(SIGWINCH) || defined(SIGWIND)
22660786Sps	/*
22760786Sps	 * Since we were ignoring window change signals while we executed
22860786Sps	 * the system command, we must assume the window changed.
22960786Sps	 * Warning: this leaves a signal pending (in "sigs"),
23060786Sps	 * so psignals() should be called soon after lsystem().
23160786Sps	 */
23260786Sps	winch(0);
23360786Sps#endif
23460786Sps}
23560786Sps
23660786Sps#endif
23760786Sps
23860786Sps#if PIPEC
23960786Sps
24060786Sps/*
24160786Sps * Pipe a section of the input file into the given shell command.
24260786Sps * The section to be piped is the section "between" the current
24360786Sps * position and the position marked by the given letter.
24460786Sps *
24560786Sps * If the mark is after the current screen, the section between
24660786Sps * the top line displayed and the mark is piped.
24760786Sps * If the mark is before the current screen, the section between
24860786Sps * the mark and the bottom line displayed is piped.
24960786Sps * If the mark is on the current screen, or if the mark is ".",
25060786Sps * the whole current screen is piped.
25160786Sps */
25260786Sps	public int
25360786Spspipe_mark(c, cmd)
25460786Sps	int c;
25560786Sps	char *cmd;
25660786Sps{
25760786Sps	POSITION mpos, tpos, bpos;
25860786Sps
25960786Sps	/*
26060786Sps	 * mpos = the marked position.
26160786Sps	 * tpos = top of screen.
26260786Sps	 * bpos = bottom of screen.
26360786Sps	 */
26460786Sps	mpos = markpos(c);
26560786Sps	if (mpos == NULL_POSITION)
26660786Sps		return (-1);
26760786Sps	tpos = position(TOP);
26860786Sps	if (tpos == NULL_POSITION)
26960786Sps		tpos = ch_zero();
27060786Sps	bpos = position(BOTTOM);
27160786Sps
27260786Sps 	if (c == '.')
27360786Sps 		return (pipe_data(cmd, tpos, bpos));
27460786Sps 	else if (mpos <= tpos)
27560786Sps 		return (pipe_data(cmd, mpos, bpos));
27660786Sps 	else if (bpos == NULL_POSITION)
27760786Sps 		return (pipe_data(cmd, tpos, bpos));
27860786Sps 	else
27960786Sps 		return (pipe_data(cmd, tpos, mpos));
28060786Sps}
28160786Sps
28260786Sps/*
28360786Sps * Create a pipe to the given shell command.
28460786Sps * Feed it the file contents between the positions spos and epos.
28560786Sps */
28660786Sps	public int
28760786Spspipe_data(cmd, spos, epos)
28860786Sps	char *cmd;
28960786Sps	POSITION spos;
29060786Sps	POSITION epos;
29160786Sps{
29260786Sps	register FILE *f;
29360786Sps	register int c;
29460786Sps	extern FILE *popen();
29560786Sps
29660786Sps	/*
29760786Sps	 * This is structured much like lsystem().
29860786Sps	 * Since we're running a shell program, we must be careful
29960786Sps	 * to perform the necessary deinitialization before running
30060786Sps	 * the command, and reinitialization after it.
30160786Sps	 */
30260786Sps	if (ch_seek(spos) != 0)
30360786Sps	{
30460786Sps		error("Cannot seek to start position", NULL_PARG);
30560786Sps		return (-1);
30660786Sps	}
30760786Sps
30860786Sps	if ((f = popen(cmd, "w")) == NULL)
30960786Sps	{
31060786Sps		error("Cannot create pipe", NULL_PARG);
31160786Sps		return (-1);
31260786Sps	}
31360786Sps	clear_bot();
31460786Sps	putstr("!");
31560786Sps	putstr(cmd);
31660786Sps	putstr("\n");
31760786Sps
31860786Sps	deinit();
31960786Sps	flush();
32060786Sps	raw_mode(0);
32160786Sps	init_signals(0);
32260786Sps#if MSDOS_COMPILER==WIN32C
32360786Sps	close_getchr();
32460786Sps#endif
32560786Sps#ifdef SIGPIPE
32660786Sps	LSIGNAL(SIGPIPE, SIG_IGN);
32760786Sps#endif
32860786Sps
32960786Sps	c = EOI;
33060786Sps	while (epos == NULL_POSITION || spos++ <= epos)
33160786Sps	{
33260786Sps		/*
33360786Sps		 * Read a character from the file and give it to the pipe.
33460786Sps		 */
33560786Sps		c = ch_forw_get();
33660786Sps		if (c == EOI)
33760786Sps			break;
33860786Sps		if (putc(c, f) == EOF)
33960786Sps			break;
34060786Sps	}
34160786Sps
34260786Sps	/*
34360786Sps	 * Finish up the last line.
34460786Sps	 */
34560786Sps 	while (c != '\n' && c != EOI )
34660786Sps 	{
34760786Sps 		c = ch_forw_get();
34860786Sps 		if (c == EOI)
34960786Sps 			break;
35060786Sps 		if (putc(c, f) == EOF)
35160786Sps 			break;
35260786Sps 	}
35360786Sps
35460786Sps	pclose(f);
35560786Sps
35660786Sps#ifdef SIGPIPE
35760786Sps	LSIGNAL(SIGPIPE, SIG_DFL);
35860786Sps#endif
35960786Sps#if MSDOS_COMPILER==WIN32C
36060786Sps	open_getchr();
36160786Sps#endif
36260786Sps	init_signals(1);
36360786Sps	raw_mode(1);
36460786Sps	init();
36560786Sps	screen_trashed = 1;
36660786Sps#if defined(SIGWINCH) || defined(SIGWIND)
36760786Sps	/* {{ Probably don't need this here. }} */
36860786Sps	winch(0);
36960786Sps#endif
37060786Sps	return (0);
37160786Sps}
37260786Sps
37360786Sps#endif
374