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