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