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