1 /* 2 * Copyright (C) 1984-2011 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 about less, or for information on how to 8 * contact the author, see the README file. 9 */ 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 21 #if MSDOS_COMPILER 22 #include <dos.h> 23 #ifdef _MSC_VER 24 #include <direct.h> 25 #define setdisk(n) _chdrive((n)+1) 26 #else 27 #include <dir.h> 28 #endif 29 #endif 30 31 extern int screen_trashed; 32 extern IFILE curr_ifile; 33 34 35 #if HAVE_SYSTEM 36 37 /* 38 * Pass the specified command to a shell to be executed. 39 * Like plain "system()", but handles resetting terminal modes, etc. 40 */ 41 public void 42 lsystem(cmd, donemsg) 43 char *cmd; 44 char *donemsg; 45 { 46 register int inp; 47 #if HAVE_SHELL 48 register char *shell; 49 register char *p; 50 #endif 51 IFILE save_ifile; 52 #if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C 53 char cwd[FILENAME_MAX+1]; 54 #endif 55 56 /* 57 * Print the command which is to be executed, 58 * unless the command starts with a "-". 59 */ 60 if (cmd[0] == '-') 61 cmd++; 62 else 63 { 64 clear_bot(); 65 putstr("!"); 66 putstr(cmd); 67 putstr("\n"); 68 } 69 70 #if MSDOS_COMPILER 71 #if MSDOS_COMPILER==WIN32C 72 if (*cmd == '\0') 73 cmd = getenv("COMSPEC"); 74 #else 75 /* 76 * Working directory is global on MSDOS. 77 * The child might change the working directory, so we 78 * must save and restore CWD across calls to "system", 79 * or else we won't find our file when we return and 80 * try to "reedit_ifile" it. 81 */ 82 getcwd(cwd, FILENAME_MAX); 83 #endif 84 #endif 85 86 /* 87 * Close the current input file. 88 */ 89 save_ifile = save_curr_ifile(); 90 (void) edit_ifile(NULL_IFILE); 91 92 /* 93 * De-initialize the terminal and take out of raw mode. 94 */ 95 deinit(); 96 flush(); /* Make sure the deinit chars get out */ 97 raw_mode(0); 98 #if MSDOS_COMPILER==WIN32C 99 close_getchr(); 100 #endif 101 102 /* 103 * Restore signals to their defaults. 104 */ 105 init_signals(0); 106 107 #if HAVE_DUP 108 /* 109 * Force standard input to be the user's terminal 110 * (the normal standard input), even if less's standard input 111 * is coming from a pipe. 112 */ 113 inp = dup(0); 114 close(0); 115 #if OS2 116 /* The __open() system call translates "/dev/tty" to "con". */ 117 if (__open("/dev/tty", OPEN_READ) < 0) 118 #else 119 if (open("/dev/tty", OPEN_READ) < 0) 120 #endif 121 dup(inp); 122 #endif 123 124 /* 125 * Pass the command to the system to be executed. 126 * If we have a SHELL environment variable, use 127 * <$SHELL -c "command"> instead of just <command>. 128 * If the command is empty, just invoke a shell. 129 */ 130 #if HAVE_SHELL 131 p = NULL; 132 if ((shell = lgetenv("SHELL")) != NULL && *shell != '\0') 133 { 134 if (*cmd == '\0') 135 p = save(shell); 136 else 137 { 138 char *esccmd = shell_quote(cmd); 139 if (esccmd != NULL) 140 { 141 int len = strlen(shell) + strlen(esccmd) + 5; 142 p = (char *) ecalloc(len, sizeof(char)); 143 SNPRINTF3(p, len, "%s %s %s", shell, shell_coption(), esccmd); 144 free(esccmd); 145 } 146 } 147 } 148 if (p == NULL) 149 { 150 if (*cmd == '\0') 151 p = save("sh"); 152 else 153 p = save(cmd); 154 } 155 system(p); 156 free(p); 157 #else 158 #if MSDOS_COMPILER==DJGPPC 159 /* 160 * Make stdin of the child be in cooked mode. 161 */ 162 setmode(0, O_TEXT); 163 /* 164 * We don't need to catch signals of the child (it 165 * also makes trouble with some DPMI servers). 166 */ 167 __djgpp_exception_toggle(); 168 system(cmd); 169 __djgpp_exception_toggle(); 170 #else 171 system(cmd); 172 #endif 173 #endif 174 175 #if HAVE_DUP 176 /* 177 * Restore standard input, reset signals, raw mode, etc. 178 */ 179 close(0); 180 dup(inp); 181 close(inp); 182 #endif 183 184 #if MSDOS_COMPILER==WIN32C 185 open_getchr(); 186 #endif 187 init_signals(1); 188 raw_mode(1); 189 if (donemsg != NULL) 190 { 191 putstr(donemsg); 192 putstr(" (press RETURN)"); 193 get_return(); 194 putchr('\n'); 195 flush(); 196 } 197 init(); 198 screen_trashed = 1; 199 200 #if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C 201 /* 202 * Restore the previous directory (possibly 203 * changed by the child program we just ran). 204 */ 205 chdir(cwd); 206 #if MSDOS_COMPILER != DJGPPC 207 /* 208 * Some versions of chdir() don't change to the drive 209 * which is part of CWD. (DJGPP does this in chdir.) 210 */ 211 if (cwd[1] == ':') 212 { 213 if (cwd[0] >= 'a' && cwd[0] <= 'z') 214 setdisk(cwd[0] - 'a'); 215 else if (cwd[0] >= 'A' && cwd[0] <= 'Z') 216 setdisk(cwd[0] - 'A'); 217 } 218 #endif 219 #endif 220 221 /* 222 * Reopen the current input file. 223 */ 224 reedit_ifile(save_ifile); 225 226 #if defined(SIGWINCH) || defined(SIGWIND) 227 /* 228 * Since we were ignoring window change signals while we executed 229 * the system command, we must assume the window changed. 230 * Warning: this leaves a signal pending (in "sigs"), 231 * so psignals() should be called soon after lsystem(). 232 */ 233 winch(0); 234 #endif 235 } 236 237 #endif 238 239 #if PIPEC 240 241 /* 242 * Pipe a section of the input file into the given shell command. 243 * The section to be piped is the section "between" the current 244 * position and the position marked by the given letter. 245 * 246 * If the mark is after the current screen, the section between 247 * the top line displayed and the mark is piped. 248 * If the mark is before the current screen, the section between 249 * the mark and the bottom line displayed is piped. 250 * If the mark is on the current screen, or if the mark is ".", 251 * the whole current screen is piped. 252 */ 253 public int 254 pipe_mark(c, cmd) 255 int c; 256 char *cmd; 257 { 258 POSITION mpos, tpos, bpos; 259 260 /* 261 * mpos = the marked position. 262 * tpos = top of screen. 263 * bpos = bottom of screen. 264 */ 265 mpos = markpos(c); 266 if (mpos == NULL_POSITION) 267 return (-1); 268 tpos = position(TOP); 269 if (tpos == NULL_POSITION) 270 tpos = ch_zero(); 271 bpos = position(BOTTOM); 272 273 if (c == '.') 274 return (pipe_data(cmd, tpos, bpos)); 275 else if (mpos <= tpos) 276 return (pipe_data(cmd, mpos, bpos)); 277 else if (bpos == NULL_POSITION) 278 return (pipe_data(cmd, tpos, bpos)); 279 else 280 return (pipe_data(cmd, tpos, mpos)); 281 } 282 283 /* 284 * Create a pipe to the given shell command. 285 * Feed it the file contents between the positions spos and epos. 286 */ 287 public int 288 pipe_data(cmd, spos, epos) 289 char *cmd; 290 POSITION spos; 291 POSITION epos; 292 { 293 register FILE *f; 294 register int c; 295 extern FILE *popen(); 296 297 /* 298 * This is structured much like lsystem(). 299 * Since we're running a shell program, we must be careful 300 * to perform the necessary deinitialization before running 301 * the command, and reinitialization after it. 302 */ 303 if (ch_seek(spos) != 0) 304 { 305 error("Cannot seek to start position", NULL_PARG); 306 return (-1); 307 } 308 309 if ((f = popen(cmd, "w")) == NULL) 310 { 311 error("Cannot create pipe", NULL_PARG); 312 return (-1); 313 } 314 clear_bot(); 315 putstr("!"); 316 putstr(cmd); 317 putstr("\n"); 318 319 deinit(); 320 flush(); 321 raw_mode(0); 322 init_signals(0); 323 #if MSDOS_COMPILER==WIN32C 324 close_getchr(); 325 #endif 326 #ifdef SIGPIPE 327 LSIGNAL(SIGPIPE, SIG_IGN); 328 #endif 329 330 c = EOI; 331 while (epos == NULL_POSITION || spos++ <= epos) 332 { 333 /* 334 * Read a character from the file and give it to the pipe. 335 */ 336 c = ch_forw_get(); 337 if (c == EOI) 338 break; 339 if (putc(c, f) == EOF) 340 break; 341 } 342 343 /* 344 * Finish up the last line. 345 */ 346 while (c != '\n' && c != EOI ) 347 { 348 c = ch_forw_get(); 349 if (c == EOI) 350 break; 351 if (putc(c, f) == EOF) 352 break; 353 } 354 355 pclose(f); 356 357 #ifdef SIGPIPE 358 LSIGNAL(SIGPIPE, SIG_DFL); 359 #endif 360 #if MSDOS_COMPILER==WIN32C 361 open_getchr(); 362 #endif 363 init_signals(1); 364 raw_mode(1); 365 init(); 366 screen_trashed = 1; 367 #if defined(SIGWINCH) || defined(SIGWIND) 368 /* {{ Probably don't need this here. }} */ 369 winch(0); 370 #endif 371 return (0); 372 } 373 374 #endif 375