1 /* 2 * Copyright (C) 1984-2015 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 374