lsystem.c revision 161475
1/*
2 * Copyright (C) 1984-2005  Mark Nudelman
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Less License, as specified in the README file.
6 *
7 * For more information about less, or for information on how to
8 * contact the author, see the README file.
9 */
10
11
12/*
13 * Routines to execute other programs.
14 * Necessarily very OS dependent.
15 */
16
17#include "less.h"
18#include <signal.h>
19#include "position.h"
20
21#if MSDOS_COMPILER
22#include <dos.h>
23#ifdef _MSC_VER
24#include <direct.h>
25#define setdisk(n) _chdrive((n)+1)
26#else
27#include <dir.h>
28#endif
29#endif
30
31extern int screen_trashed;
32extern IFILE curr_ifile;
33
34
35#if HAVE_SYSTEM
36
37/*
38 * Pass the specified command to a shell to be executed.
39 * Like plain "system()", but handles resetting terminal modes, etc.
40 */
41	public void
42lsystem(cmd, donemsg)
43	char *cmd;
44	char *donemsg;
45{
46	register int inp;
47#if HAVE_SHELL
48	register char *shell;
49	register char *p;
50#endif
51	IFILE save_ifile;
52#if MSDOS_COMPILER
53	char cwd[FILENAME_MAX+1];
54#endif
55
56	/*
57	 * Print the command which is to be executed,
58	 * unless the command starts with a "-".
59	 */
60	if (cmd[0] == '-')
61		cmd++;
62	else
63	{
64		clear_bot();
65		putstr("!");
66		putstr(cmd);
67		putstr("\n");
68	}
69
70#if MSDOS_COMPILER
71	/*
72	 * Working directory is global on MSDOS.
73	 * The child might change the working directory, so we
74	 * must save and restore CWD across calls to "system",
75	 * or else we won't find our file when we return and
76	 * try to "reedit_ifile" it.
77	 */
78	getcwd(cwd, FILENAME_MAX);
79#endif
80
81	/*
82	 * Close the current input file.
83	 */
84	save_ifile = save_curr_ifile();
85	(void) edit_ifile(NULL_IFILE);
86
87	/*
88	 * De-initialize the terminal and take out of raw mode.
89	 */
90	deinit();
91	flush();	/* Make sure the deinit chars get out */
92	raw_mode(0);
93#if MSDOS_COMPILER==WIN32C
94	close_getchr();
95#endif
96
97	/*
98	 * Restore signals to their defaults.
99	 */
100	init_signals(0);
101
102#if HAVE_DUP
103	/*
104	 * Force standard input to be the user's terminal
105	 * (the normal standard input), even if less's standard input
106	 * is coming from a pipe.
107	 */
108	inp = dup(0);
109	close(0);
110#if OS2
111	/* The __open() system call translates "/dev/tty" to "con". */
112	if (__open("/dev/tty", OPEN_READ) < 0)
113#else
114	if (open("/dev/tty", OPEN_READ) < 0)
115#endif
116		dup(inp);
117#endif
118
119	/*
120	 * Pass the command to the system to be executed.
121	 * If we have a SHELL environment variable, use
122	 * <$SHELL -c "command"> instead of just <command>.
123	 * If the command is empty, just invoke a shell.
124	 */
125#if HAVE_SHELL
126	p = NULL;
127	if ((shell = lgetenv("SHELL")) != NULL && *shell != '\0')
128	{
129		if (*cmd == '\0')
130			p = save(shell);
131		else
132		{
133			char *esccmd = shell_quote(cmd);
134			if (esccmd != NULL)
135			{
136				int len = strlen(shell) + strlen(esccmd) + 5;
137				p = (char *) ecalloc(len, sizeof(char));
138				SNPRINTF3(p, len, "%s %s %s", shell, shell_coption(), esccmd);
139				free(esccmd);
140			}
141		}
142	}
143	if (p == NULL)
144	{
145		if (*cmd == '\0')
146			p = save("sh");
147		else
148			p = save(cmd);
149	}
150	system(p);
151	free(p);
152#else
153#if MSDOS_COMPILER==DJGPPC
154	/*
155	 * Make stdin of the child be in cooked mode.
156	 */
157	setmode(0, O_TEXT);
158	/*
159	 * We don't need to catch signals of the child (it
160	 * also makes trouble with some DPMI servers).
161	 */
162	__djgpp_exception_toggle();
163  	system(cmd);
164	__djgpp_exception_toggle();
165#else
166	system(cmd);
167#endif
168#endif
169
170#if HAVE_DUP
171	/*
172	 * Restore standard input, reset signals, raw mode, etc.
173	 */
174	close(0);
175	dup(inp);
176	close(inp);
177#endif
178
179#if MSDOS_COMPILER==WIN32C
180	open_getchr();
181#endif
182	init_signals(1);
183	raw_mode(1);
184	if (donemsg != NULL)
185	{
186		putstr(donemsg);
187		putstr("  (press RETURN)");
188		get_return();
189		putchr('\n');
190		flush();
191	}
192	init();
193	screen_trashed = 1;
194
195#if MSDOS_COMPILER
196	/*
197	 * Restore the previous directory (possibly
198	 * changed by the child program we just ran).
199	 */
200	chdir(cwd);
201#if MSDOS_COMPILER != DJGPPC
202	/*
203	 * Some versions of chdir() don't change to the drive
204	 * which is part of CWD.  (DJGPP does this in chdir.)
205	 */
206	if (cwd[1] == ':')
207	{
208		if (cwd[0] >= 'a' && cwd[0] <= 'z')
209			setdisk(cwd[0] - 'a');
210		else if (cwd[0] >= 'A' && cwd[0] <= 'Z')
211			setdisk(cwd[0] - 'A');
212	}
213#endif
214#endif
215
216	/*
217	 * Reopen the current input file.
218	 */
219	reedit_ifile(save_ifile);
220
221#if defined(SIGWINCH) || defined(SIGWIND)
222	/*
223	 * Since we were ignoring window change signals while we executed
224	 * the system command, we must assume the window changed.
225	 * Warning: this leaves a signal pending (in "sigs"),
226	 * so psignals() should be called soon after lsystem().
227	 */
228	winch(0);
229#endif
230}
231
232#endif
233
234#if PIPEC
235
236/*
237 * Pipe a section of the input file into the given shell command.
238 * The section to be piped is the section "between" the current
239 * position and the position marked by the given letter.
240 *
241 * If the mark is after the current screen, the section between
242 * the top line displayed and the mark is piped.
243 * If the mark is before the current screen, the section between
244 * the mark and the bottom line displayed is piped.
245 * If the mark is on the current screen, or if the mark is ".",
246 * the whole current screen is piped.
247 */
248	public int
249pipe_mark(c, cmd)
250	int c;
251	char *cmd;
252{
253	POSITION mpos, tpos, bpos;
254
255	/*
256	 * mpos = the marked position.
257	 * tpos = top of screen.
258	 * bpos = bottom of screen.
259	 */
260	mpos = markpos(c);
261	if (mpos == NULL_POSITION)
262		return (-1);
263	tpos = position(TOP);
264	if (tpos == NULL_POSITION)
265		tpos = ch_zero();
266	bpos = position(BOTTOM);
267
268 	if (c == '.')
269 		return (pipe_data(cmd, tpos, bpos));
270 	else if (mpos <= tpos)
271 		return (pipe_data(cmd, mpos, bpos));
272 	else if (bpos == NULL_POSITION)
273 		return (pipe_data(cmd, tpos, bpos));
274 	else
275 		return (pipe_data(cmd, tpos, mpos));
276}
277
278/*
279 * Create a pipe to the given shell command.
280 * Feed it the file contents between the positions spos and epos.
281 */
282	public int
283pipe_data(cmd, spos, epos)
284	char *cmd;
285	POSITION spos;
286	POSITION epos;
287{
288	register FILE *f;
289	register int c;
290	extern FILE *popen();
291
292	/*
293	 * This is structured much like lsystem().
294	 * Since we're running a shell program, we must be careful
295	 * to perform the necessary deinitialization before running
296	 * the command, and reinitialization after it.
297	 */
298	if (ch_seek(spos) != 0)
299	{
300		error("Cannot seek to start position", NULL_PARG);
301		return (-1);
302	}
303
304	if ((f = popen(cmd, "w")) == NULL)
305	{
306		error("Cannot create pipe", NULL_PARG);
307		return (-1);
308	}
309	clear_bot();
310	putstr("!");
311	putstr(cmd);
312	putstr("\n");
313
314	deinit();
315	flush();
316	raw_mode(0);
317	init_signals(0);
318#if MSDOS_COMPILER==WIN32C
319	close_getchr();
320#endif
321#ifdef SIGPIPE
322	LSIGNAL(SIGPIPE, SIG_IGN);
323#endif
324
325	c = EOI;
326	while (epos == NULL_POSITION || spos++ <= epos)
327	{
328		/*
329		 * Read a character from the file and give it to the pipe.
330		 */
331		c = ch_forw_get();
332		if (c == EOI)
333			break;
334		if (putc(c, f) == EOF)
335			break;
336	}
337
338	/*
339	 * Finish up the last line.
340	 */
341 	while (c != '\n' && c != EOI )
342 	{
343 		c = ch_forw_get();
344 		if (c == EOI)
345 			break;
346 		if (putc(c, f) == EOF)
347 			break;
348 	}
349
350	pclose(f);
351
352#ifdef SIGPIPE
353	LSIGNAL(SIGPIPE, SIG_DFL);
354#endif
355#if MSDOS_COMPILER==WIN32C
356	open_getchr();
357#endif
358	init_signals(1);
359	raw_mode(1);
360	init();
361	screen_trashed = 1;
362#if defined(SIGWINCH) || defined(SIGWIND)
363	/* {{ Probably don't need this here. }} */
364	winch(0);
365#endif
366	return (0);
367}
368
369#endif
370