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