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