1 /* 2 * Copyright (C) 1984-2021 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 !MSDOS_COMPILER 120 #if OS2 121 /* The __open() system call translates "/dev/tty" to "con". */ 122 if (__open(tty_device(), OPEN_READ) < 0) 123 #else 124 if (open(tty_device(), OPEN_READ) < 0) 125 #endif 126 #endif 127 dup(inp); 128 #endif 129 130 /* 131 * Pass the command to the system to be executed. 132 * If we have a SHELL environment variable, use 133 * <$SHELL -c "command"> instead of just <command>. 134 * If the command is empty, just invoke a shell. 135 */ 136 #if HAVE_SHELL 137 p = NULL; 138 if ((shell = lgetenv("SHELL")) != NULL && *shell != '\0') 139 { 140 if (*cmd == '\0') 141 p = save(shell); 142 else 143 { 144 char *esccmd = shell_quote(cmd); 145 if (esccmd != NULL) 146 { 147 int len = (int) (strlen(shell) + strlen(esccmd) + 5); 148 p = (char *) ecalloc(len, sizeof(char)); 149 SNPRINTF3(p, len, "%s %s %s", shell, shell_coption(), esccmd); 150 free(esccmd); 151 } 152 } 153 } 154 if (p == NULL) 155 { 156 if (*cmd == '\0') 157 p = save("sh"); 158 else 159 p = save(cmd); 160 } 161 system(p); 162 free(p); 163 #else 164 #if MSDOS_COMPILER==DJGPPC 165 /* 166 * Make stdin of the child be in cooked mode. 167 */ 168 setmode(0, O_TEXT); 169 /* 170 * We don't need to catch signals of the child (it 171 * also makes trouble with some DPMI servers). 172 */ 173 __djgpp_exception_toggle(); 174 system(cmd); 175 __djgpp_exception_toggle(); 176 #else 177 system(cmd); 178 #endif 179 #endif 180 181 #if HAVE_DUP 182 /* 183 * Restore standard input, reset signals, raw mode, etc. 184 */ 185 close(0); 186 dup(inp); 187 close(inp); 188 #endif 189 190 #if MSDOS_COMPILER==WIN32C 191 open_getchr(); 192 #endif 193 init_signals(1); 194 raw_mode(1); 195 if (donemsg != NULL) 196 { 197 putstr(donemsg); 198 putstr(" (press RETURN)"); 199 get_return(); 200 putchr('\n'); 201 flush(); 202 } 203 init(); 204 screen_trashed = 1; 205 206 #if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C 207 /* 208 * Restore the previous directory (possibly 209 * changed by the child program we just ran). 210 */ 211 chdir(cwd); 212 #if MSDOS_COMPILER != DJGPPC 213 /* 214 * Some versions of chdir() don't change to the drive 215 * which is part of CWD. (DJGPP does this in chdir.) 216 */ 217 if (cwd[1] == ':') 218 { 219 if (cwd[0] >= 'a' && cwd[0] <= 'z') 220 setdisk(cwd[0] - 'a'); 221 else if (cwd[0] >= 'A' && cwd[0] <= 'Z') 222 setdisk(cwd[0] - 'A'); 223 } 224 #endif 225 #endif 226 227 /* 228 * Reopen the current input file. 229 */ 230 reedit_ifile(save_ifile); 231 232 #if defined(SIGWINCH) || defined(SIGWIND) 233 /* 234 * Since we were ignoring window change signals while we executed 235 * the system command, we must assume the window changed. 236 * Warning: this leaves a signal pending (in "sigs"), 237 * so psignals() should be called soon after lsystem(). 238 */ 239 winch(0); 240 #endif 241 } 242 243 #endif 244 245 #if PIPEC 246 247 /* 248 * Pipe a section of the input file into the given shell command. 249 * The section to be piped is the section "between" the current 250 * position and the position marked by the given letter. 251 * 252 * If the mark is after the current screen, the section between 253 * the top line displayed and the mark is piped. 254 * If the mark is before the current screen, the section between 255 * the mark and the bottom line displayed is piped. 256 * If the mark is on the current screen, or if the mark is ".", 257 * the whole current screen is piped. 258 */ 259 public int 260 pipe_mark(c, cmd) 261 int c; 262 char *cmd; 263 { 264 POSITION mpos, tpos, bpos; 265 266 /* 267 * mpos = the marked position. 268 * tpos = top of screen. 269 * bpos = bottom of screen. 270 */ 271 mpos = markpos(c); 272 if (mpos == NULL_POSITION) 273 return (-1); 274 tpos = position(TOP); 275 if (tpos == NULL_POSITION) 276 tpos = ch_zero(); 277 bpos = position(BOTTOM); 278 279 if (c == '.') 280 return (pipe_data(cmd, tpos, bpos)); 281 else if (mpos <= tpos) 282 return (pipe_data(cmd, mpos, bpos)); 283 else if (bpos == NULL_POSITION) 284 return (pipe_data(cmd, tpos, bpos)); 285 else 286 return (pipe_data(cmd, tpos, mpos)); 287 } 288 289 /* 290 * Create a pipe to the given shell command. 291 * Feed it the file contents between the positions spos and epos. 292 */ 293 public int 294 pipe_data(cmd, spos, epos) 295 char *cmd; 296 POSITION spos; 297 POSITION epos; 298 { 299 FILE *f; 300 int c; 301 302 /* 303 * This is structured much like lsystem(). 304 * Since we're running a shell program, we must be careful 305 * to perform the necessary deinitialization before running 306 * the command, and reinitialization after it. 307 */ 308 if (ch_seek(spos) != 0) 309 { 310 error("Cannot seek to start position", NULL_PARG); 311 return (-1); 312 } 313 314 if ((f = popen(cmd, "w")) == NULL) 315 { 316 error("Cannot create pipe", NULL_PARG); 317 return (-1); 318 } 319 clear_bot(); 320 putstr("!"); 321 putstr(cmd); 322 putstr("\n"); 323 324 deinit(); 325 flush(); 326 raw_mode(0); 327 init_signals(0); 328 #if MSDOS_COMPILER==WIN32C 329 close_getchr(); 330 #endif 331 #ifdef SIGPIPE 332 LSIGNAL(SIGPIPE, SIG_IGN); 333 #endif 334 335 c = EOI; 336 while (epos == NULL_POSITION || spos++ <= epos) 337 { 338 /* 339 * Read a character from the file and give it to the pipe. 340 */ 341 c = ch_forw_get(); 342 if (c == EOI) 343 break; 344 if (putc(c, f) == EOF) 345 break; 346 } 347 348 /* 349 * Finish up the last line. 350 */ 351 while (c != '\n' && c != EOI ) 352 { 353 c = ch_forw_get(); 354 if (c == EOI) 355 break; 356 if (putc(c, f) == EOF) 357 break; 358 } 359 360 pclose(f); 361 362 #ifdef SIGPIPE 363 LSIGNAL(SIGPIPE, SIG_DFL); 364 #endif 365 #if MSDOS_COMPILER==WIN32C 366 open_getchr(); 367 #endif 368 init_signals(1); 369 raw_mode(1); 370 init(); 371 screen_trashed = 1; 372 #if defined(SIGWINCH) || defined(SIGWIND) 373 /* {{ Probably don't need this here. }} */ 374 winch(0); 375 #endif 376 return (0); 377 } 378 379 #endif 380