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