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