1 /*
2 * Copyright (C) 1984-2012 Mark Nudelman
3 * Modified for use with illumos by Garrett D'Amore.
4 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
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 * Routines to execute other programs.
14 * Necessarily very OS dependent.
15 */
16
17 #include <signal.h>
18
19 #include "less.h"
20 #include "position.h"
21
22 extern int screen_trashed;
23 extern IFILE curr_ifile;
24
25 static int pipe_data(char *cmd, off_t spos, off_t epos);
26
27 /*
28 * Pass the specified command to a shell to be executed.
29 * Like plain "system()", but handles resetting terminal modes, etc.
30 */
31 void
lsystem(const char * cmd,const char * donemsg)32 lsystem(const char *cmd, const char *donemsg)
33 {
34 int inp;
35 char *shell;
36 char *p;
37 IFILE save_ifile;
38
39 /*
40 * Print the command which is to be executed,
41 * unless the command starts with a "-".
42 */
43 if (cmd[0] == '-')
44 cmd++;
45 else {
46 clear_bot();
47 putstr("!");
48 putstr(cmd);
49 putstr("\n");
50 }
51
52 /*
53 * Close the current input file.
54 */
55 save_ifile = save_curr_ifile();
56 (void) edit_ifile(NULL);
57
58 /*
59 * De-initialize the terminal and take out of raw mode.
60 */
61 deinit();
62 flush(0); /* Make sure the deinit chars get out */
63 raw_mode(0);
64
65 /*
66 * Restore signals to their defaults.
67 */
68 init_signals(0);
69
70 /*
71 * Force standard input to be the user's terminal
72 * (the normal standard input), even if less's standard input
73 * is coming from a pipe.
74 */
75 inp = dup(0);
76 (void) close(0);
77 if (open("/dev/tty", O_RDONLY) == -1)
78 (void) dup(inp);
79
80 /*
81 * Pass the command to the system to be executed.
82 * If we have a SHELL environment variable, use
83 * <$SHELL -c "command"> instead of just <command>.
84 * If the command is empty, just invoke a shell.
85 */
86 p = NULL;
87 if ((shell = lgetenv("SHELL")) != NULL && *shell != '\0') {
88 if (*cmd == '\0') {
89 p = estrdup(shell);
90 } else {
91 char *esccmd = shell_quote(cmd);
92 if (esccmd != NULL) {
93 p = easprintf("%s -c %s", shell, esccmd);
94 free(esccmd);
95 }
96 }
97 }
98 if (p == NULL) {
99 if (*cmd == '\0')
100 p = estrdup("sh");
101 else
102 p = estrdup(cmd);
103 }
104 (void) system(p);
105 free(p);
106
107 /*
108 * Restore standard input, reset signals, raw mode, etc.
109 */
110 (void) close(0);
111 (void) dup(inp);
112 (void) close(inp);
113
114 init_signals(1);
115 raw_mode(1);
116 if (donemsg != NULL) {
117 putstr(donemsg);
118 putstr(" (press RETURN)");
119 get_return();
120 (void) putchr('\n');
121 flush(0);
122 }
123 init();
124 screen_trashed = 1;
125
126 /*
127 * Reopen the current input file.
128 */
129 reedit_ifile(save_ifile);
130
131 /*
132 * Since we were ignoring window change signals while we executed
133 * the system command, we must assume the window changed.
134 * Warning: this leaves a signal pending (in "signal_winch"),
135 * so psignals() should be called soon after lsystem().
136 */
137 sigwinch(0);
138 }
139
140 /*
141 * Pipe a section of the input file into the given shell command.
142 * The section to be piped is the section "between" the current
143 * position and the position marked by the given letter.
144 *
145 * If the mark is after the current screen, the section between
146 * the top line displayed and the mark is piped.
147 * If the mark is before the current screen, the section between
148 * the mark and the bottom line displayed is piped.
149 * If the mark is on the current screen, or if the mark is ".",
150 * the whole current screen is piped.
151 */
152 int
pipe_mark(int c,char * cmd)153 pipe_mark(int c, char *cmd)
154 {
155 off_t mpos, tpos, bpos;
156
157 /*
158 * mpos = the marked position.
159 * tpos = top of screen.
160 * bpos = bottom of screen.
161 */
162 mpos = markpos(c);
163 if (mpos == -1)
164 return (-1);
165 tpos = position(TOP);
166 if (tpos == -1)
167 tpos = ch_zero();
168 bpos = position(BOTTOM);
169
170 if (c == '.')
171 return (pipe_data(cmd, tpos, bpos));
172 else if (mpos <= tpos)
173 return (pipe_data(cmd, mpos, bpos));
174 else if (bpos == -1)
175 return (pipe_data(cmd, tpos, bpos));
176 else
177 return (pipe_data(cmd, tpos, mpos));
178 }
179
180 /*
181 * Create a pipe to the given shell command.
182 * Feed it the file contents between the positions spos and epos.
183 */
184 static int
pipe_data(char * cmd,off_t spos,off_t epos)185 pipe_data(char *cmd, off_t spos, off_t epos)
186 {
187 FILE *f;
188 int c;
189
190 /*
191 * This is structured much like lsystem().
192 * Since we're running a shell program, we must be careful
193 * to perform the necessary deinitialization before running
194 * the command, and reinitialization after it.
195 */
196 if (ch_seek(spos) != 0) {
197 error("Cannot seek to start position", NULL);
198 return (-1);
199 }
200
201 if ((f = popen(cmd, "w")) == NULL) {
202 error("Cannot create pipe", NULL);
203 return (-1);
204 }
205 clear_bot();
206 putstr("!");
207 putstr(cmd);
208 putstr("\n");
209
210 deinit();
211 flush(0);
212 raw_mode(0);
213 init_signals(0);
214 lsignal(SIGPIPE, SIG_IGN);
215
216 c = EOI;
217 while (epos == -1 || spos++ <= epos) {
218 /*
219 * Read a character from the file and give it to the pipe.
220 */
221 c = ch_forw_get();
222 if (c == EOI)
223 break;
224 if (putc(c, f) == EOF)
225 break;
226 }
227
228 /*
229 * Finish up the last line.
230 */
231 while (c != '\n' && c != EOI) {
232 c = ch_forw_get();
233 if (c == EOI)
234 break;
235 if (putc(c, f) == EOF)
236 break;
237 }
238
239 (void) pclose(f);
240
241 lsignal(SIGPIPE, SIG_DFL);
242 init_signals(1);
243 raw_mode(1);
244 init();
245 screen_trashed = 1;
246 /* {{ Probably don't need this here. }} */
247 sigwinch(0);
248 return (0);
249 }
250