lsystem.c revision 161475
1193323Sed/*
2193323Sed * Copyright (C) 1984-2005  Mark Nudelman
3193323Sed *
4193323Sed * You may distribute under the terms of either the GNU General Public
5193323Sed * License or the Less License, as specified in the README file.
6193323Sed *
7193323Sed * For more information about less, or for information on how to
8193323Sed * contact the author, see the README file.
9193323Sed */
10193323Sed
11193323Sed
12193323Sed/*
13193323Sed * Routines to execute other programs.
14210299Sed * Necessarily very OS dependent.
15193323Sed */
16218893Sdim
17193323Sed#include "less.h"
18226890Sdim#include <signal.h>
19193323Sed#include "position.h"
20193323Sed
21193323Sed#if MSDOS_COMPILER
22252723Sdim#include <dos.h>
23193323Sed#ifdef _MSC_VER
24193323Sed#include <direct.h>
25252723Sdim#define setdisk(n) _chdrive((n)+1)
26252723Sdim#else
27252723Sdim#include <dir.h>
28252723Sdim#endif
29252723Sdim#endif
30252723Sdim
31207631Srdivackyextern int screen_trashed;
32193323Sedextern IFILE curr_ifile;
33193323Sed
34198090Srdivacky
35198090Srdivacky#if HAVE_SYSTEM
36252723Sdim
37252723Sdim/*
38198090Srdivacky * Pass the specified command to a shell to be executed.
39193323Sed * Like plain "system()", but handles resetting terminal modes, etc.
40193323Sed */
41212904Sdim	public void
42212904Sdimlsystem(cmd, donemsg)
43212904Sdim	char *cmd;
44212904Sdim	char *donemsg;
45212904Sdim{
46218893Sdim	register int inp;
47218893Sdim#if HAVE_SHELL
48218893Sdim	register char *shell;
49221345Sdim	register char *p;
50218893Sdim#endif
51193323Sed	IFILE save_ifile;
52193323Sed#if MSDOS_COMPILER
53193323Sed	char cwd[FILENAME_MAX+1];
54193323Sed#endif
55193323Sed
56218893Sdim	/*
57218893Sdim	 * Print the command which is to be executed,
58218893Sdim	 * unless the command starts with a "-".
59218893Sdim	 */
60218893Sdim	if (cmd[0] == '-')
61218893Sdim		cmd++;
62193323Sed	else
63195098Sed	{
64193323Sed		clear_bot();
65193323Sed		putstr("!");
66193323Sed		putstr(cmd);
67193323Sed		putstr("\n");
68193323Sed	}
69193323Sed
70198090Srdivacky#if MSDOS_COMPILER
71198090Srdivacky	/*
72198090Srdivacky	 * Working directory is global on MSDOS.
73218893Sdim	 * The child might change the working directory, so we
74193323Sed	 * must save and restore CWD across calls to "system",
75193323Sed	 * or else we won't find our file when we return and
76193323Sed	 * try to "reedit_ifile" it.
77193323Sed	 */
78194612Sed	getcwd(cwd, FILENAME_MAX);
79194612Sed#endif
80252723Sdim
81252723Sdim	/*
82198090Srdivacky	 * Close the current input file.
83198090Srdivacky	 */
84194612Sed	save_ifile = save_curr_ifile();
85194612Sed	(void) edit_ifile(NULL_IFILE);
86194612Sed
87194612Sed	/*
88202375Srdivacky	 * De-initialize the terminal and take out of raw mode.
89203954Srdivacky	 */
90218893Sdim	deinit();
91218893Sdim	flush();	/* Make sure the deinit chars get out */
92218893Sdim	raw_mode(0);
93218893Sdim#if MSDOS_COMPILER==WIN32C
94226890Sdim	close_getchr();
95221345Sdim#endif
96221345Sdim
97226890Sdim	/*
98226890Sdim	 * Restore signals to their defaults.
99226890Sdim	 */
100221345Sdim	init_signals(0);
101221345Sdim
102226890Sdim#if HAVE_DUP
103221345Sdim	/*
104226890Sdim	 * Force standard input to be the user's terminal
105226890Sdim	 * (the normal standard input), even if less's standard input
106226890Sdim	 * is coming from a pipe.
107226890Sdim	 */
108226890Sdim	inp = dup(0);
109221345Sdim	close(0);
110218893Sdim#if OS2
111218893Sdim	/* The __open() system call translates "/dev/tty" to "con". */
112218893Sdim	if (__open("/dev/tty", OPEN_READ) < 0)
113218893Sdim#else
114218893Sdim	if (open("/dev/tty", OPEN_READ) < 0)
115218893Sdim#endif
116218893Sdim		dup(inp);
117218893Sdim#endif
118218893Sdim
119218893Sdim	/*
120218893Sdim	 * Pass the command to the system to be executed.
121218893Sdim	 * If we have a SHELL environment variable, use
122218893Sdim	 * <$SHELL -c "command"> instead of just <command>.
123218893Sdim	 * If the command is empty, just invoke a shell.
124218893Sdim	 */
125218893Sdim#if HAVE_SHELL
126218893Sdim	p = NULL;
127218893Sdim	if ((shell = lgetenv("SHELL")) != NULL && *shell != '\0')
128218893Sdim	{
129218893Sdim		if (*cmd == '\0')
130218893Sdim			p = save(shell);
131218893Sdim		else
132218893Sdim		{
133263509Sdim			char *esccmd = shell_quote(cmd);
134263509Sdim			if (esccmd != NULL)
135263509Sdim			{
136263509Sdim				int len = strlen(shell) + strlen(esccmd) + 5;
137263509Sdim				p = (char *) ecalloc(len, sizeof(char));
138263509Sdim				SNPRINTF3(p, len, "%s %s %s", shell, shell_coption(), esccmd);
139263509Sdim				free(esccmd);
140226890Sdim			}
141193323Sed		}
142226890Sdim	}
143226890Sdim	if (p == NULL)
144226890Sdim	{
145226890Sdim		if (*cmd == '\0')
146226890Sdim			p = save("sh");
147218893Sdim		else
148193323Sed			p = save(cmd);
149202375Srdivacky	}
150193323Sed	system(p);
151218893Sdim	free(p);
152193323Sed#else
153218893Sdim#if MSDOS_COMPILER==DJGPPC
154219077Sdim	/*
155193323Sed	 * Make stdin of the child be in cooked mode.
156218893Sdim	 */
157193323Sed	setmode(0, O_TEXT);
158218893Sdim	/*
159218893Sdim	 * We don't need to catch signals of the child (it
160218893Sdim	 * also makes trouble with some DPMI servers).
161218893Sdim	 */
162218893Sdim	__djgpp_exception_toggle();
163218893Sdim  	system(cmd);
164218893Sdim	__djgpp_exception_toggle();
165218893Sdim#else
166218893Sdim	system(cmd);
167218893Sdim#endif
168218893Sdim#endif
169218893Sdim
170218893Sdim#if HAVE_DUP
171218893Sdim	/*
172218893Sdim	 * Restore standard input, reset signals, raw mode, etc.
173218893Sdim	 */
174193323Sed	close(0);
175218893Sdim	dup(inp);
176218893Sdim	close(inp);
177195098Sed#endif
178218893Sdim
179218893Sdim#if MSDOS_COMPILER==WIN32C
180195340Sed	open_getchr();
181202375Srdivacky#endif
182195340Sed	init_signals(1);
183218893Sdim	raw_mode(1);
184195340Sed	if (donemsg != NULL)
185263509Sdim	{
186195340Sed		putstr(donemsg);
187218893Sdim		putstr("  (press RETURN)");
188218893Sdim		get_return();
189218893Sdim		putchr('\n');
190218893Sdim		flush();
191218893Sdim	}
192218893Sdim	init();
193218893Sdim	screen_trashed = 1;
194218893Sdim
195218893Sdim#if MSDOS_COMPILER
196218893Sdim	/*
197218893Sdim	 * Restore the previous directory (possibly
198218893Sdim	 * changed by the child program we just ran).
199218893Sdim	 */
200218893Sdim	chdir(cwd);
201218893Sdim#if MSDOS_COMPILER != DJGPPC
202218893Sdim	/*
203193323Sed	 * Some versions of chdir() don't change to the drive
204193323Sed	 * which is part of CWD.  (DJGPP does this in chdir.)
205193323Sed	 */
206193323Sed	if (cwd[1] == ':')
207195340Sed	{
208195340Sed		if (cwd[0] >= 'a' && cwd[0] <= 'z')
209202375Srdivacky			setdisk(cwd[0] - 'a');
210202375Srdivacky		else if (cwd[0] >= 'A' && cwd[0] <= 'Z')
211195340Sed			setdisk(cwd[0] - 'A');
212206083Srdivacky	}
213206083Srdivacky#endif
214198090Srdivacky#endif
215206083Srdivacky
216218893Sdim	/*
217245431Sdim	 * Reopen the current input file.
218245431Sdim	 */
219198090Srdivacky	reedit_ifile(save_ifile);
220198113Srdivacky
221206083Srdivacky#if defined(SIGWINCH) || defined(SIGWIND)
222198113Srdivacky	/*
223206083Srdivacky	 * Since we were ignoring window change signals while we executed
224218893Sdim	 * the system command, we must assume the window changed.
225245431Sdim	 * Warning: this leaves a signal pending (in "sigs"),
226245431Sdim	 * so psignals() should be called soon after lsystem().
227198113Srdivacky	 */
228198090Srdivacky	winch(0);
229198090Srdivacky#endif
230218893Sdim}
231218893Sdim
232218893Sdim#endif
233245431Sdim
234198090Srdivacky#if PIPEC
235218893Sdim
236218893Sdim/*
237218893Sdim * Pipe a section of the input file into the given shell command.
238218893Sdim * The section to be piped is the section "between" the current
239245431Sdim * position and the position marked by the given letter.
240218893Sdim *
241210299Sed * If the mark is after the current screen, the section between
242210299Sed * the top line displayed and the mark is piped.
243210299Sed * If the mark is before the current screen, the section between
244210299Sed * the mark and the bottom line displayed is piped.
245210299Sed * If the mark is on the current screen, or if the mark is ".",
246198090Srdivacky * the whole current screen is piped.
247207618Srdivacky */
248198090Srdivacky	public int
249226890Sdimpipe_mark(c, cmd)
250226890Sdim	int c;
251226890Sdim	char *cmd;
252252723Sdim{
253252723Sdim	POSITION mpos, tpos, bpos;
254208599Srdivacky
255208599Srdivacky	/*
256263509Sdim	 * mpos = the marked position.
257226890Sdim	 * tpos = top of screen.
258195340Sed	 * bpos = bottom of screen.
259195340Sed	 */
260195340Sed	mpos = markpos(c);
261195340Sed	if (mpos == NULL_POSITION)
262195340Sed		return (-1);
263198090Srdivacky	tpos = position(TOP);
264252723Sdim	if (tpos == NULL_POSITION)
265252723Sdim		tpos = ch_zero();
266252723Sdim	bpos = position(BOTTOM);
267252723Sdim
268252723Sdim 	if (c == '.')
269208599Srdivacky 		return (pipe_data(cmd, tpos, bpos));
270210299Sed 	else if (mpos <= tpos)
271252723Sdim 		return (pipe_data(cmd, mpos, bpos));
272252723Sdim 	else if (bpos == NULL_POSITION)
273252723Sdim 		return (pipe_data(cmd, tpos, bpos));
274208599Srdivacky 	else
275218893Sdim 		return (pipe_data(cmd, tpos, mpos));
276218893Sdim}
277193323Sed
278193323Sed/*
279193323Sed * Create a pipe to the given shell command.
280198090Srdivacky * Feed it the file contents between the positions spos and epos.
281198090Srdivacky */
282198090Srdivacky	public int
283198090Srdivackypipe_data(cmd, spos, epos)
284198090Srdivacky	char *cmd;
285198090Srdivacky	POSITION spos;
286198090Srdivacky	POSITION epos;
287198090Srdivacky{
288198090Srdivacky	register FILE *f;
289198090Srdivacky	register int c;
290198090Srdivacky	extern FILE *popen();
291198090Srdivacky
292198090Srdivacky	/*
293198090Srdivacky	 * This is structured much like lsystem().
294198090Srdivacky	 * Since we're running a shell program, we must be careful
295198090Srdivacky	 * to perform the necessary deinitialization before running
296198090Srdivacky	 * the command, and reinitialization after it.
297198090Srdivacky	 */
298198090Srdivacky	if (ch_seek(spos) != 0)
299198090Srdivacky	{
300198090Srdivacky		error("Cannot seek to start position", NULL_PARG);
301198090Srdivacky		return (-1);
302198090Srdivacky	}
303198090Srdivacky
304218893Sdim	if ((f = popen(cmd, "w")) == NULL)
305245431Sdim	{
306218893Sdim		error("Cannot create pipe", NULL_PARG);
307218893Sdim		return (-1);
308226890Sdim	}
309218893Sdim	clear_bot();
310218893Sdim	putstr("!");
311226890Sdim	putstr(cmd);
312198090Srdivacky	putstr("\n");
313218893Sdim
314218893Sdim	deinit();
315218893Sdim	flush();
316218893Sdim	raw_mode(0);
317218893Sdim	init_signals(0);
318218893Sdim#if MSDOS_COMPILER==WIN32C
319218893Sdim	close_getchr();
320218893Sdim#endif
321218893Sdim#ifdef SIGPIPE
322218893Sdim	LSIGNAL(SIGPIPE, SIG_IGN);
323218893Sdim#endif
324218893Sdim
325218893Sdim	c = EOI;
326252723Sdim	while (epos == NULL_POSITION || spos++ <= epos)
327252723Sdim	{
328252723Sdim		/*
329252723Sdim		 * Read a character from the file and give it to the pipe.
330252723Sdim		 */
331252723Sdim		c = ch_forw_get();
332252723Sdim		if (c == EOI)
333252723Sdim			break;
334252723Sdim		if (putc(c, f) == EOF)
335252723Sdim			break;
336252723Sdim	}
337252723Sdim
338252723Sdim	/*
339252723Sdim	 * Finish up the last line.
340252723Sdim	 */
341252723Sdim 	while (c != '\n' && c != EOI )
342252723Sdim 	{
343252723Sdim 		c = ch_forw_get();
344252723Sdim 		if (c == EOI)
345252723Sdim 			break;
346252723Sdim 		if (putc(c, f) == EOF)
347252723Sdim 			break;
348252723Sdim 	}
349252723Sdim
350252723Sdim	pclose(f);
351252723Sdim
352252723Sdim#ifdef SIGPIPE
353252723Sdim	LSIGNAL(SIGPIPE, SIG_DFL);
354252723Sdim#endif
355252723Sdim#if MSDOS_COMPILER==WIN32C
356252723Sdim	open_getchr();
357252723Sdim#endif
358263509Sdim	init_signals(1);
359252723Sdim	raw_mode(1);
360252723Sdim	init();
361252723Sdim	screen_trashed = 1;
362252723Sdim#if defined(SIGWINCH) || defined(SIGWIND)
363252723Sdim	/* {{ Probably don't need this here. }} */
364252723Sdim	winch(0);
365252723Sdim#endif
366252723Sdim	return (0);
367252723Sdim}
368252723Sdim
369252723Sdim#endif
370252723Sdim