import less(1)
[unleashed/tickless.git] / bin / less / lsystem.c
blobf53a5e45e9d6e3ad00975cbaf013c47743266da0
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>
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.
9 * For more information, see the README file.
13 * Routines to execute other programs.
14 * Necessarily very OS dependent.
17 #include <signal.h>
19 #include "less.h"
20 #include "position.h"
22 extern int screen_trashed;
23 extern IFILE curr_ifile;
25 static int pipe_data(char *cmd, off_t spos, off_t epos);
28 * Pass the specified command to a shell to be executed.
29 * Like plain "system()", but handles resetting terminal modes, etc.
31 void
32 lsystem(const char *cmd, const char *donemsg)
34 int inp;
35 char *shell;
36 char *p;
37 IFILE save_ifile;
40 * Print the command which is to be executed,
41 * unless the command starts with a "-".
43 if (cmd[0] == '-')
44 cmd++;
45 else {
46 clear_bot();
47 putstr("!");
48 putstr(cmd);
49 putstr("\n");
53 * Close the current input file.
55 save_ifile = save_curr_ifile();
56 (void) edit_ifile(NULL);
59 * De-initialize the terminal and take out of raw mode.
61 deinit();
62 flush(0); /* Make sure the deinit chars get out */
63 raw_mode(0);
66 * Restore signals to their defaults.
68 init_signals(0);
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.
75 inp = dup(0);
76 (void) close(0);
77 if (open("/dev/tty", O_RDONLY) < 0)
78 (void) dup(inp);
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.
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);
98 if (p == NULL) {
99 if (*cmd == '\0')
100 p = estrdup("sh");
101 else
102 p = estrdup(cmd);
104 (void) system(p);
105 free(p);
108 * Restore standard input, reset signals, raw mode, etc.
110 (void) close(0);
111 (void) dup(inp);
112 (void) close(inp);
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);
123 init();
124 screen_trashed = 1;
127 * Reopen the current input file.
129 reedit_ifile(save_ifile);
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 "sigs"),
135 * so psignals() should be called soon after lsystem().
137 sigwinch(0);
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.
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.
153 pipe_mark(int c, char *cmd)
155 off_t mpos, tpos, bpos;
158 * mpos = the marked position.
159 * tpos = top of screen.
160 * bpos = bottom of screen.
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);
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));
181 * Create a pipe to the given shell command.
182 * Feed it the file contents between the positions spos and epos.
184 static int
185 pipe_data(char *cmd, off_t spos, off_t epos)
187 FILE *f;
188 int c;
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.
196 if (ch_seek(spos) != 0) {
197 error("Cannot seek to start position", NULL);
198 return (-1);
201 if ((f = popen(cmd, "w")) == NULL) {
202 error("Cannot create pipe", NULL);
203 return (-1);
205 clear_bot();
206 putstr("!");
207 putstr(cmd);
208 putstr("\n");
210 deinit();
211 flush(0);
212 raw_mode(0);
213 init_signals(0);
214 lsignal(SIGPIPE, SIG_IGN);
216 c = EOI;
217 while (epos == -1 || spos++ <= epos) {
219 * Read a character from the file and give it to the pipe.
221 c = ch_forw_get();
222 if (c == EOI)
223 break;
224 if (putc(c, f) == EOF)
225 break;
229 * Finish up the last line.
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;
239 (void) pclose(f);
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);