lsystem.c revision 89019
1/*
2 * Copyright (C) 1984-2000  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;
134			if ((esccmd = esc_metachars(cmd)) == NULL)
135			{
136				p = (char *) ecalloc(strlen(shell) +
137					strlen(cmd) + 7, sizeof(char));
138				sprintf(p, "%s -c \"%s\"", shell, cmd);
139			} else
140			{
141				p = (char *) ecalloc(strlen(shell) +
142					strlen(esccmd) + 5, sizeof(char));
143				sprintf(p, "%s -c %s", shell, 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
156	system(p);
157	free(p);
158#else
159#if MSDOS_COMPILER==DJGPPC
160	/*
161	 * Make stdin of the child be in cooked mode.
162	 */
163	setmode(0, O_TEXT);
164	/*
165	 * We don't need to catch signals of the child (it
166	 * also makes trouble with some DPMI servers).
167	 */
168	__djgpp_exception_toggle();
169  	system(cmd);
170	__djgpp_exception_toggle();
171#else
172	system(cmd);
173#endif
174#endif
175
176#if HAVE_DUP
177	/*
178	 * Restore standard input, reset signals, raw mode, etc.
179	 */
180	close(0);
181	dup(inp);
182	close(inp);
183#endif
184
185#if MSDOS_COMPILER==WIN32C
186	open_getchr();
187#endif
188	init_signals(1);
189	raw_mode(1);
190	if (donemsg != NULL)
191	{
192		putstr(donemsg);
193		putstr("  (press RETURN)");
194		get_return();
195		putchr('\n');
196		flush();
197	}
198	init();
199	screen_trashed = 1;
200
201#if MSDOS_COMPILER
202	/*
203	 * Restore the previous directory (possibly
204	 * changed by the child program we just ran).
205	 */
206	chdir(cwd);
207#if MSDOS_COMPILER != DJGPPC
208	/*
209	 * Some versions of chdir() don't change to the drive
210	 * which is part of CWD.  (DJGPP does this in chdir.)
211	 */
212	if (cwd[1] == ':')
213	{
214		if (cwd[0] >= 'a' && cwd[0] <= 'z')
215			setdisk(cwd[0] - 'a');
216		else if (cwd[0] >= 'A' && cwd[0] <= 'Z')
217			setdisk(cwd[0] - 'A');
218	}
219#endif
220#endif
221
222	/*
223	 * Reopen the current input file.
224	 */
225	reedit_ifile(save_ifile);
226
227#if defined(SIGWINCH) || defined(SIGWIND)
228	/*
229	 * Since we were ignoring window change signals while we executed
230	 * the system command, we must assume the window changed.
231	 * Warning: this leaves a signal pending (in "sigs"),
232	 * so psignals() should be called soon after lsystem().
233	 */
234	winch(0);
235#endif
236}
237
238#endif
239
240#if PIPEC
241
242/*
243 * Pipe a section of the input file into the given shell command.
244 * The section to be piped is the section "between" the current
245 * position and the position marked by the given letter.
246 *
247 * If the mark is after the current screen, the section between
248 * the top line displayed and the mark is piped.
249 * If the mark is before the current screen, the section between
250 * the mark and the bottom line displayed is piped.
251 * If the mark is on the current screen, or if the mark is ".",
252 * the whole current screen is piped.
253 */
254	public int
255pipe_mark(c, cmd)
256	int c;
257	char *cmd;
258{
259	POSITION mpos, tpos, bpos;
260
261	/*
262	 * mpos = the marked position.
263	 * tpos = top of screen.
264	 * bpos = bottom of screen.
265	 */
266	mpos = markpos(c);
267	if (mpos == NULL_POSITION)
268		return (-1);
269	tpos = position(TOP);
270	if (tpos == NULL_POSITION)
271		tpos = ch_zero();
272	bpos = position(BOTTOM);
273
274 	if (c == '.')
275 		return (pipe_data(cmd, tpos, bpos));
276 	else if (mpos <= tpos)
277 		return (pipe_data(cmd, mpos, bpos));
278 	else if (bpos == NULL_POSITION)
279 		return (pipe_data(cmd, tpos, bpos));
280 	else
281 		return (pipe_data(cmd, tpos, mpos));
282}
283
284/*
285 * Create a pipe to the given shell command.
286 * Feed it the file contents between the positions spos and epos.
287 */
288	public int
289pipe_data(cmd, spos, epos)
290	char *cmd;
291	POSITION spos;
292	POSITION epos;
293{
294	register FILE *f;
295	register int c;
296	extern FILE *popen();
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
377#ifdef _OSK
378/*
379 *    Popen, and Pclose, for OS-9.
380 *
381 *    Based on code copyright (c) 1988 by Wolfgang Ocker, Puchheim,
382 *                                        Ulli Dessauer, Germering and
383 *                                        Reimer Mellin, Muenchen
384 *                                        (W-Germany)
385 *
386 *    These functions can be copied and distributed freely for any
387 *    non-commercial purposes.  It can only be incorporated into
388 *    commercial software with the written permission of the authors.
389 *
390 *    TOP-specific code stripped out and adapted for less by M.Gregorie, 1996
391 *
392 *    address:    Wolfgang Ocker
393 *                Lochhauserstrasse 35a
394 *                D-8039 Puchheim
395 *                West Germany
396 *
397 *    e-mail:     weo@altger.UUCP, ud@altger.UUCP, ram@altger.UUCP
398 *                pyramid!tmpmbx!recco!weo
399 *                pyramid!tmpmbx!nitmar!ud
400 *                pyramid!tmpmbx!ramsys!ram
401 *
402 *                Martin Gregorie
403 *                10 Sadlers Mead
404 *                Harlow
405 *                Essex, CM18 6HG
406 *                U.K.
407 *
408 *                gregorie@logica.com
409 */
410#include <strings.h>
411#include <errno.h>
412extern char **environ;
413extern char *getenv();
414extern int  os9forkc();
415static int pids[_NFILE] = { 0, 0, 0, 0, 0, 0, 0, 0,
416                            0, 0, 0, 0, 0, 0, 0, 0,
417                            0, 0, 0, 0, 0, 0, 0, 0,
418                            0, 0, 0, 0, 0, 0, 0, 0 };
419/*
420 * p o p e n
421 */
422FILE *popen(name, mode)
423	char *name;
424	char *mode;
425{
426    int          fd, fd2, fdsav, pid;
427    static char  *argv[] = {NULL, NULL, NULL };
428    static char  cmd[200];
429    static char  cmd_path[200];
430    char         *cp;
431    char         *shell;
432    FILE         *r;
433    if ((shell = getenv("SHELL")) == NULL)
434        return(NULL);
435    cp = name;
436    while (*cp == ' ')
437        cp++;
438    strcpy(cmd_path, cp);
439    if (cp = index(cmd_path, ' '))
440        *cp++ = '\0';
441    strcpy(cmd, "ex ");
442    strcat(cmd, cmd_path);
443    if (cp)
444    {
445        strcat(cmd, " ");
446        strcat(cmd, cp);
447    }
448    argv[0] = shell;
449    argv[1] = cmd;
450    /*
451         mode is "r" (stdout) or "w" (stdin)
452    */
453    switch(mode[0])
454    {
455        case 'w':   fd = 0;
456                    break;
457        case 'r':   fd = 1;
458                    break;
459        default:    return(NULL);
460    }
461    if (fd == 1)
462        fflush(stdout);
463    fdsav = dup(fd);
464    close(fd);
465
466    creat("/pipe", S_IWRITE+S_IREAD);
467    pid = os9exec(os9forkc, argv[0], argv, environ, 0, 0, 3);
468    fd2 = dup(fd);
469    close(fd);
470    dup(fdsav);
471    close(fdsav);
472    if (pid > 0)
473    {
474        pids[fd2] = pid;
475        r = fdopen(fd2, mode);
476    }
477    else
478    {
479        close(fd2);
480        r = NULL;
481    }
482    return(r);
483}
484
485/*
486 * p c l o s e
487 */
488int pclose(fp)
489	FILE *fp;
490{
491    unsigned int    status;
492    int             pid;
493    int             fd,
494                    i;
495    fd = fileno(fp);
496    if (pids[fd] == 0)
497        return(-1);
498    fflush(fp);
499    fclose(fp);
500    while ((pid = wait(&status)) != -1)
501        if (pid == pids[fd])
502            break;
503        else
504            for (i = 0; i < _NFILE; i++)
505                if (pids[i] == pid)
506                {
507                    pids[i] = 0;
508                    break;
509                }
510    if (pid == -1)
511        status = -1;
512    pids[fd] = 0;
513    return(status);
514}
515#endif /* _OSK */
516