lsystem.c revision 128345
1/*
2 * Copyright (C) 1984-2002  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 about less, or for information on how to
8 * contact the author, see the README file.
9 */
10
11
12/*
13 * Routines to execute other programs.
14 * Necessarily very OS dependent.
15 */
16
17#include "less.h"
18#include <signal.h>
19#include "position.h"
20
21#if MSDOS_COMPILER
22#include <dos.h>
23#ifdef _MSC_VER
24#include <direct.h>
25#define setdisk(n) _chdrive((n)+1)
26#else
27#include <dir.h>
28#endif
29#endif
30
31extern int screen_trashed;
32extern IFILE curr_ifile;
33
34
35#if HAVE_SYSTEM
36
37/*
38 * Pass the specified command to a shell to be executed.
39 * Like plain "system()", but handles resetting terminal modes, etc.
40 */
41	public void
42lsystem(cmd, donemsg)
43	char *cmd;
44	char *donemsg;
45{
46	register int inp;
47#if HAVE_SHELL
48	register char *shell;
49	register char *p;
50#endif
51	IFILE save_ifile;
52#if MSDOS_COMPILER
53	char cwd[FILENAME_MAX+1];
54#endif
55
56	/*
57	 * Print the command which is to be executed,
58	 * unless the command starts with a "-".
59	 */
60	if (cmd[0] == '-')
61		cmd++;
62	else
63	{
64		clear_bot();
65		putstr("!");
66		putstr(cmd);
67		putstr("\n");
68	}
69
70#if MSDOS_COMPILER
71	/*
72	 * Working directory is global on MSDOS.
73	 * The child might change the working directory, so we
74	 * must save and restore CWD across calls to "system",
75	 * or else we won't find our file when we return and
76	 * try to "reedit_ifile" it.
77	 */
78	getcwd(cwd, FILENAME_MAX);
79#endif
80
81	/*
82	 * Close the current input file.
83	 */
84	save_ifile = save_curr_ifile();
85	(void) edit_ifile(NULL_IFILE);
86
87	/*
88	 * De-initialize the terminal and take out of raw mode.
89	 */
90	deinit();
91	flush();	/* Make sure the deinit chars get out */
92	raw_mode(0);
93#if MSDOS_COMPILER==WIN32C
94	close_getchr();
95#endif
96
97	/*
98	 * Restore signals to their defaults.
99	 */
100	init_signals(0);
101
102#if HAVE_DUP
103	/*
104	 * Force standard input to be the user's terminal
105	 * (the normal standard input), even if less's standard input
106	 * is coming from a pipe.
107	 */
108	inp = dup(0);
109	close(0);
110#if OS2
111	/* The __open() system call translates "/dev/tty" to "con". */
112	if (__open("/dev/tty", OPEN_READ) < 0)
113#else
114	if (open("/dev/tty", OPEN_READ) < 0)
115#endif
116		dup(inp);
117#endif
118
119	/*
120	 * Pass the command to the system to be executed.
121	 * If we have a SHELL environment variable, use
122	 * <$SHELL -c "command"> instead of just <command>.
123	 * If the command is empty, just invoke a shell.
124	 */
125#if HAVE_SHELL
126	p = NULL;
127	if ((shell = lgetenv("SHELL")) != NULL && *shell != '\0')
128	{
129		if (*cmd == '\0')
130			p = save(shell);
131		else
132		{
133			char *esccmd = shell_quote(cmd);
134			if (esccmd != NULL)
135			{
136				p = (char *) ecalloc(strlen(shell) +
137					strlen(esccmd) + 5, sizeof(char));
138				sprintf(p, "%s %s %s", shell, shell_coption(), esccmd);
139				free(esccmd);
140			}
141		}
142	}
143	if (p == NULL)
144	{
145		if (*cmd == '\0')
146			p = save("sh");
147		else
148			p = save(cmd);
149	}
150	system(p);
151	free(p);
152#else
153#if MSDOS_COMPILER==DJGPPC
154	/*
155	 * Make stdin of the child be in cooked mode.
156	 */
157	setmode(0, O_TEXT);
158	/*
159	 * We don't need to catch signals of the child (it
160	 * also makes trouble with some DPMI servers).
161	 */
162	__djgpp_exception_toggle();
163  	system(cmd);
164	__djgpp_exception_toggle();
165#else
166	system(cmd);
167#endif
168#endif
169
170#if HAVE_DUP
171	/*
172	 * Restore standard input, reset signals, raw mode, etc.
173	 */
174	close(0);
175	dup(inp);
176	close(inp);
177#endif
178
179#if MSDOS_COMPILER==WIN32C
180	open_getchr();
181#endif
182	init_signals(1);
183	raw_mode(1);
184	if (donemsg != NULL)
185	{
186		putstr(donemsg);
187		putstr("  (press RETURN)");
188		get_return();
189		putchr('\n');
190		flush();
191	}
192	init();
193	screen_trashed = 1;
194
195#if MSDOS_COMPILER
196	/*
197	 * Restore the previous directory (possibly
198	 * changed by the child program we just ran).
199	 */
200	chdir(cwd);
201#if MSDOS_COMPILER != DJGPPC
202	/*
203	 * Some versions of chdir() don't change to the drive
204	 * which is part of CWD.  (DJGPP does this in chdir.)
205	 */
206	if (cwd[1] == ':')
207	{
208		if (cwd[0] >= 'a' && cwd[0] <= 'z')
209			setdisk(cwd[0] - 'a');
210		else if (cwd[0] >= 'A' && cwd[0] <= 'Z')
211			setdisk(cwd[0] - 'A');
212	}
213#endif
214#endif
215
216	/*
217	 * Reopen the current input file.
218	 */
219	reedit_ifile(save_ifile);
220
221#if defined(SIGWINCH) || defined(SIGWIND)
222	/*
223	 * Since we were ignoring window change signals while we executed
224	 * the system command, we must assume the window changed.
225	 * Warning: this leaves a signal pending (in "sigs"),
226	 * so psignals() should be called soon after lsystem().
227	 */
228	winch(0);
229#endif
230}
231
232#endif
233
234#if PIPEC
235
236/*
237 * Pipe a section of the input file into the given shell command.
238 * The section to be piped is the section "between" the current
239 * position and the position marked by the given letter.
240 *
241 * If the mark is after the current screen, the section between
242 * the top line displayed and the mark is piped.
243 * If the mark is before the current screen, the section between
244 * the mark and the bottom line displayed is piped.
245 * If the mark is on the current screen, or if the mark is ".",
246 * the whole current screen is piped.
247 */
248	public int
249pipe_mark(c, cmd)
250	int c;
251	char *cmd;
252{
253	POSITION mpos, tpos, bpos;
254
255	/*
256	 * mpos = the marked position.
257	 * tpos = top of screen.
258	 * bpos = bottom of screen.
259	 */
260	mpos = markpos(c);
261	if (mpos == NULL_POSITION)
262		return (-1);
263	tpos = position(TOP);
264	if (tpos == NULL_POSITION)
265		tpos = ch_zero();
266	bpos = position(BOTTOM);
267
268 	if (c == '.')
269 		return (pipe_data(cmd, tpos, bpos));
270 	else if (mpos <= tpos)
271 		return (pipe_data(cmd, mpos, bpos));
272 	else if (bpos == NULL_POSITION)
273 		return (pipe_data(cmd, tpos, bpos));
274 	else
275 		return (pipe_data(cmd, tpos, mpos));
276}
277
278/*
279 * Create a pipe to the given shell command.
280 * Feed it the file contents between the positions spos and epos.
281 */
282	public int
283pipe_data(cmd, spos, epos)
284	char *cmd;
285	POSITION spos;
286	POSITION epos;
287{
288	register FILE *f;
289	register int c;
290	extern FILE *popen();
291
292	/*
293	 * This is structured much like lsystem().
294	 * Since we're running a shell program, we must be careful
295	 * to perform the necessary deinitialization before running
296	 * the command, and reinitialization after it.
297	 */
298	if (ch_seek(spos) != 0)
299	{
300		error("Cannot seek to start position", NULL_PARG);
301		return (-1);
302	}
303
304	if ((f = popen(cmd, "w")) == NULL)
305	{
306		error("Cannot create pipe", NULL_PARG);
307		return (-1);
308	}
309	clear_bot();
310	putstr("!");
311	putstr(cmd);
312	putstr("\n");
313
314	deinit();
315	flush();
316	raw_mode(0);
317	init_signals(0);
318#if MSDOS_COMPILER==WIN32C
319	close_getchr();
320#endif
321#ifdef SIGPIPE
322	LSIGNAL(SIGPIPE, SIG_IGN);
323#endif
324
325	c = EOI;
326	while (epos == NULL_POSITION || spos++ <= epos)
327	{
328		/*
329		 * Read a character from the file and give it to the pipe.
330		 */
331		c = ch_forw_get();
332		if (c == EOI)
333			break;
334		if (putc(c, f) == EOF)
335			break;
336	}
337
338	/*
339	 * Finish up the last line.
340	 */
341 	while (c != '\n' && c != EOI )
342 	{
343 		c = ch_forw_get();
344 		if (c == EOI)
345 			break;
346 		if (putc(c, f) == EOF)
347 			break;
348 	}
349
350	pclose(f);
351
352#ifdef SIGPIPE
353	LSIGNAL(SIGPIPE, SIG_DFL);
354#endif
355#if MSDOS_COMPILER==WIN32C
356	open_getchr();
357#endif
358	init_signals(1);
359	raw_mode(1);
360	init();
361	screen_trashed = 1;
362#if defined(SIGWINCH) || defined(SIGWIND)
363	/* {{ Probably don't need this here. }} */
364	winch(0);
365#endif
366	return (0);
367}
368
369#endif
370
371#ifdef _OSK
372/*
373 *    Popen, and Pclose, for OS-9.
374 *
375 *    Based on code copyright (c) 1988 by Wolfgang Ocker, Puchheim,
376 *                                        Ulli Dessauer, Germering and
377 *                                        Reimer Mellin, Muenchen
378 *                                        (W-Germany)
379 *
380 *    These functions can be copied and distributed freely for any
381 *    non-commercial purposes.  It can only be incorporated into
382 *    commercial software with the written permission of the authors.
383 *
384 *    TOP-specific code stripped out and adapted for less by M.Gregorie, 1996
385 *
386 *    address:    Wolfgang Ocker
387 *                Lochhauserstrasse 35a
388 *                D-8039 Puchheim
389 *                West Germany
390 *
391 *    e-mail:     weo@altger.UUCP, ud@altger.UUCP, ram@altger.UUCP
392 *                pyramid!tmpmbx!recco!weo
393 *                pyramid!tmpmbx!nitmar!ud
394 *                pyramid!tmpmbx!ramsys!ram
395 *
396 *                Martin Gregorie
397 *                10 Sadlers Mead
398 *                Harlow
399 *                Essex, CM18 6HG
400 *                U.K.
401 *
402 *                gregorie@logica.com
403 */
404#include <strings.h>
405#include <errno.h>
406extern char **environ;
407extern char *getenv();
408extern int  os9forkc();
409static int pids[_NFILE] = { 0, 0, 0, 0, 0, 0, 0, 0,
410                            0, 0, 0, 0, 0, 0, 0, 0,
411                            0, 0, 0, 0, 0, 0, 0, 0,
412                            0, 0, 0, 0, 0, 0, 0, 0 };
413/*
414 * p o p e n
415 */
416FILE *popen(name, mode)
417	char *name;
418	char *mode;
419{
420    int          fd, fd2, fdsav, pid;
421    static char  *argv[] = {NULL, NULL, NULL };
422    static char  cmd[200];
423    static char  cmd_path[200];
424    char         *cp;
425    char         *shell;
426    FILE         *r;
427    if ((shell = getenv("SHELL")) == NULL)
428        return(NULL);
429    cp = name;
430    while (*cp == ' ')
431        cp++;
432    strcpy(cmd_path, cp);
433    if (cp = index(cmd_path, ' '))
434        *cp++ = '\0';
435    strcpy(cmd, "ex ");
436    strcat(cmd, cmd_path);
437    if (cp)
438    {
439        strcat(cmd, " ");
440        strcat(cmd, cp);
441    }
442    argv[0] = shell;
443    argv[1] = cmd;
444    /*
445         mode is "r" (stdout) or "w" (stdin)
446    */
447    switch(mode[0])
448    {
449        case 'w':   fd = 0;
450                    break;
451        case 'r':   fd = 1;
452                    break;
453        default:    return(NULL);
454    }
455    if (fd == 1)
456        fflush(stdout);
457    fdsav = dup(fd);
458    close(fd);
459
460    creat("/pipe", S_IWRITE+S_IREAD);
461    pid = os9exec(os9forkc, argv[0], argv, environ, 0, 0, 3);
462    fd2 = dup(fd);
463    close(fd);
464    dup(fdsav);
465    close(fdsav);
466    if (pid > 0)
467    {
468        pids[fd2] = pid;
469        r = fdopen(fd2, mode);
470    }
471    else
472    {
473        close(fd2);
474        r = NULL;
475    }
476    return(r);
477}
478
479/*
480 * p c l o s e
481 */
482int pclose(fp)
483	FILE *fp;
484{
485    unsigned int    status;
486    int             pid;
487    int             fd,
488                    i;
489    fd = fileno(fp);
490    if (pids[fd] == 0)
491        return(-1);
492    fflush(fp);
493    fclose(fp);
494    while ((pid = wait(&status)) != -1)
495        if (pid == pids[fd])
496            break;
497        else
498            for (i = 0; i < _NFILE; i++)
499                if (pids[i] == pid)
500                {
501                    pids[i] = 0;
502                    break;
503                }
504    if (pid == -1)
505        status = -1;
506    pids[fd] = 0;
507    return(status);
508}
509#endif /* _OSK */
510