xref: /openbsd-src/usr.bin/less/lsystem.c (revision a4afd6dad3fba28f80e70208181c06c482259988)
1 /*
2  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice in the documentation and/or other materials provided with
12  *    the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
20  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
21  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 
28 /*
29  * Routines to execute other programs.
30  * Necessarily very OS dependent.
31  */
32 
33 #include <signal.h>
34 #include "less.h"
35 #include "position.h"
36 
37 #if MSOFTC
38 #include <dos.h>
39 #endif
40 
41 extern int screen_trashed;
42 extern IFILE curr_ifile;
43 
44 
45 #if HAVE_SYSTEM
46 
47 /*
48  * Pass the specified command to a shell to be executed.
49  * Like plain "system()", but handles resetting terminal modes, etc.
50  */
51 	public void
52 lsystem(cmd)
53 	char *cmd;
54 {
55 	register int inp;
56 #if MSOFTC || OS2
57 	register int inp2;
58 #endif
59 	register char *shell;
60 	register char *p;
61 	IFILE save_ifile;
62 
63 	/*
64 	 * Print the command which is to be executed,
65 	 * unless the command starts with a "-".
66 	 */
67 	if (cmd[0] == '-')
68 		cmd++;
69 	else
70 	{
71 		clear_bot();
72 		putstr("!");
73 		putstr(cmd);
74 		putstr("\n");
75 	}
76 
77 	/*
78 	 * Close the current input file.
79 	 */
80 	save_ifile = curr_ifile;
81 	(void) edit_ifile(NULL_IFILE);
82 
83 	/*
84 	 * De-initialize the terminal and take out of raw mode.
85 	 */
86 	deinit();
87 	flush();	/* Make sure the deinit chars get out */
88 	raw_mode(0);
89 
90 	/*
91 	 * Restore signals to their defaults.
92 	 */
93 	init_signals(0);
94 
95 	/*
96 	 * Force standard input to be the user's terminal
97 	 * (the normal standard input), even if less's standard input
98 	 * is coming from a pipe.
99 	 */
100 	inp = dup(0);
101 	close(0);
102 	if (OPEN_TTYIN() < 0)
103 		dup(inp);
104 
105 	/*
106 	 * Pass the command to the system to be executed.
107 	 * If we have a SHELL environment variable, use
108 	 * <$SHELL -c "command"> instead of just <command>.
109 	 * If the command is empty, just invoke a shell.
110 	 */
111 #if HAVE_SHELL
112 	p = NULL;
113 	if ((shell = getenv("SHELL")) != NULL && *shell != '\0')
114 	{
115 		if (*cmd == '\0')
116 			p = save(shell);
117 		else
118 		{
119 			p = (char *) ecalloc(strlen(shell) + strlen(cmd) + 7,
120 					sizeof(char));
121 			sprintf(p, "%s -c \"%s\"", shell, cmd);
122 		}
123 	}
124 	if (p == NULL)
125 	{
126 		if (*cmd == '\0')
127 			p = save("sh");
128 		else
129 			p = save(cmd);
130 	}
131 
132 	system(p);
133 	free(p);
134 #else
135 #if OS2
136 	if (*cmd == '\0')
137 		cmd = "cmd.exe";
138 #endif
139 	system(cmd);
140 #endif
141 
142 	/*
143 	 * Restore standard input, reset signals, raw mode, etc.
144 	 */
145 	close(0);
146 	dup(inp);
147 	close(inp);
148 
149 	init_signals(1);
150 	raw_mode(1);
151 	init();
152 	screen_trashed = 1;
153 
154 	/*
155 	 * Reopen the current input file.
156 	 */
157 	if (edit_ifile(save_ifile))
158 		quit(QUIT_ERROR);
159 
160 #if defined(SIGWINCH) || defined(SIGWIND)
161 	/*
162 	 * Since we were ignoring window change signals while we executed
163 	 * the system command, we must assume the window changed.
164 	 * Warning: this leaves a signal pending (in "sigs"),
165 	 * so psignals() should be called soon after lsystem().
166 	 */
167 	winch(0);
168 #endif
169 }
170 
171 #endif
172 
173 #if PIPEC
174 
175 /*
176  * Pipe a section of the input file into the given shell command.
177  * The section to be piped is the section "between" the current
178  * position and the position marked by the given letter.
179  *
180  * The "current" position means the top line displayed if the mark
181  * is after the current screen, or the bottom line displayed if
182  * the mark is before the current screen.
183  * If the mark is on the current screen, the whole screen is displayed.
184  */
185 	public int
186 pipe_mark(c, cmd)
187 	int c;
188 	char *cmd;
189 {
190 	POSITION mpos, tpos, bpos;
191 
192 	/*
193 	 * mpos = the marked position.
194 	 * tpos = top of screen.
195 	 * bpos = bottom of screen.
196 	 */
197 	mpos = markpos(c);
198 	if (mpos == NULL_POSITION)
199 		return (-1);
200 	tpos = position(TOP);
201 	if (tpos == NULL_POSITION)
202 		tpos = ch_zero();
203 	bpos = position(BOTTOM);
204 
205  	if (c == '.')
206  		return (pipe_data(cmd, tpos, bpos));
207  	else if (mpos <= tpos)
208  		return (pipe_data(cmd, mpos, tpos));
209  	else if (bpos == NULL_POSITION)
210  		return (pipe_data(cmd, tpos, bpos));
211  	else
212  		return (pipe_data(cmd, tpos, mpos));
213 }
214 
215 /*
216  * Create a pipe to the given shell command.
217  * Feed it the file contents between the positions spos and epos.
218  */
219 	public int
220 pipe_data(cmd, spos, epos)
221 	char *cmd;
222 	POSITION spos;
223 	POSITION epos;
224 {
225 	register FILE *f;
226 	register int c;
227 	extern FILE *popen();
228 
229 	/*
230 	 * This is structured much like lsystem().
231 	 * Since we're running a shell program, we must be careful
232 	 * to perform the necessary deinitialization before running
233 	 * the command, and reinitialization after it.
234 	 */
235 	if (ch_seek(spos) != 0)
236 	{
237 		error("Cannot seek to start position", NULL_PARG);
238 		return (-1);
239 	}
240 
241 	if ((f = popen(cmd, "w")) == NULL)
242 	{
243 		error("Cannot create pipe", NULL_PARG);
244 		return (-1);
245 	}
246 	clear_bot();
247 	putstr("!");
248 	putstr(cmd);
249 	putstr("\n");
250 
251 	deinit();
252 	flush();
253 	raw_mode(0);
254 	init_signals(0);
255 #ifdef SIGPIPE
256 	SIGNAL(SIGPIPE, SIG_IGN);
257 #endif
258 
259 	c = EOI;
260 	while (epos == NULL_POSITION || spos++ <= epos)
261 	{
262 		/*
263 		 * Read a character from the file and give it to the pipe.
264 		 */
265 		c = ch_forw_get();
266 		if (c == EOI)
267 			break;
268 		if (putc(c, f) == EOF)
269 			break;
270 	}
271 
272 	/*
273 	 * Finish up the last line.
274 	 */
275  	while (c != '\n' && c != EOI )
276  	{
277  		c = ch_forw_get();
278  		if (c == EOI)
279  			break;
280  		if (putc(c, f) == EOF)
281  			break;
282  	}
283 
284 	pclose(f);
285 
286 #ifdef SIGPIPE
287 	SIGNAL(SIGPIPE, SIG_DFL);
288 #endif
289 	init_signals(1);
290 	raw_mode(1);
291 	init();
292 	screen_trashed = 1;
293 #if defined(SIGWINCH) || defined(SIGWIND)
294 	/* {{ Probably don't need this here. }} */
295 	winch(0);
296 #endif
297 	return (0);
298 }
299 
300 #endif
301