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