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 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 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 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