1 /* 2 * Copyright (C) 1984-2022 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 (open_tty() < 0) 121 #endif 122 dup(inp); 123 #endif 124 125 /* 126 * Pass the command to the system to be executed. 127 * If we have a SHELL environment variable, use 128 * <$SHELL -c "command"> instead of just <command>. 129 * If the command is empty, just invoke a shell. 130 */ 131 #if HAVE_SHELL 132 p = NULL; 133 if ((shell = lgetenv("SHELL")) != NULL && *shell != '\0') 134 { 135 if (*cmd == '\0') 136 p = save(shell); 137 else 138 { 139 char *esccmd = shell_quote(cmd); 140 if (esccmd != NULL) 141 { 142 int len = (int) (strlen(shell) + strlen(esccmd) + 5); 143 p = (char *) ecalloc(len, sizeof(char)); 144 SNPRINTF3(p, len, "%s %s %s", shell, shell_coption(), esccmd); 145 free(esccmd); 146 } 147 } 148 } 149 if (p == NULL) 150 { 151 if (*cmd == '\0') 152 p = save("sh"); 153 else 154 p = save(cmd); 155 } 156 system(p); 157 free(p); 158 #else 159 #if MSDOS_COMPILER==DJGPPC 160 /* 161 * Make stdin of the child be in cooked mode. 162 */ 163 setmode(0, O_TEXT); 164 /* 165 * We don't need to catch signals of the child (it 166 * also makes trouble with some DPMI servers). 167 */ 168 __djgpp_exception_toggle(); 169 system(cmd); 170 __djgpp_exception_toggle(); 171 #else 172 system(cmd); 173 #endif 174 #endif 175 176 #if HAVE_DUP 177 /* 178 * Restore standard input, reset signals, raw mode, etc. 179 */ 180 close(0); 181 dup(inp); 182 close(inp); 183 #endif 184 185 #if MSDOS_COMPILER==WIN32C 186 open_getchr(); 187 #endif 188 init_signals(1); 189 raw_mode(1); 190 if (donemsg != NULL) 191 { 192 putstr(donemsg); 193 putstr(" (press RETURN)"); 194 get_return(); 195 putchr('\n'); 196 flush(); 197 } 198 init(); 199 screen_trashed = 1; 200 201 #if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C 202 /* 203 * Restore the previous directory (possibly 204 * changed by the child program we just ran). 205 */ 206 chdir(cwd); 207 #if MSDOS_COMPILER != DJGPPC 208 /* 209 * Some versions of chdir() don't change to the drive 210 * which is part of CWD. (DJGPP does this in chdir.) 211 */ 212 if (cwd[1] == ':') 213 { 214 if (cwd[0] >= 'a' && cwd[0] <= 'z') 215 setdisk(cwd[0] - 'a'); 216 else if (cwd[0] >= 'A' && cwd[0] <= 'Z') 217 setdisk(cwd[0] - 'A'); 218 } 219 #endif 220 #endif 221 222 /* 223 * Reopen the current input file. 224 */ 225 reedit_ifile(save_ifile); 226 227 #if defined(SIGWINCH) || defined(SIGWIND) 228 /* 229 * Since we were ignoring window change signals while we executed 230 * the system command, we must assume the window changed. 231 * Warning: this leaves a signal pending (in "sigs"), 232 * so psignals() should be called soon after lsystem(). 233 */ 234 winch(0); 235 #endif 236 } 237 238 #endif 239 240 #if PIPEC 241 242 /* 243 * Pipe a section of the input file into the given shell command. 244 * The section to be piped is the section "between" the current 245 * position and the position marked by the given letter. 246 * 247 * If the mark is after the current screen, the section between 248 * the top line displayed and the mark is piped. 249 * If the mark is before the current screen, the section between 250 * the mark and the bottom line displayed is piped. 251 * If the mark is on the current screen, or if the mark is ".", 252 * the whole current screen is piped. 253 */ 254 public int 255 pipe_mark(c, cmd) 256 int c; 257 char *cmd; 258 { 259 POSITION mpos, tpos, bpos; 260 261 /* 262 * mpos = the marked position. 263 * tpos = top of screen. 264 * bpos = bottom of screen. 265 */ 266 mpos = markpos(c); 267 if (mpos == NULL_POSITION) 268 return (-1); 269 tpos = position(TOP); 270 if (tpos == NULL_POSITION) 271 tpos = ch_zero(); 272 bpos = position(BOTTOM); 273 274 if (c == '.') 275 return (pipe_data(cmd, tpos, bpos)); 276 else if (mpos <= tpos) 277 return (pipe_data(cmd, mpos, bpos)); 278 else if (bpos == NULL_POSITION) 279 return (pipe_data(cmd, tpos, bpos)); 280 else 281 return (pipe_data(cmd, tpos, mpos)); 282 } 283 284 /* 285 * Create a pipe to the given shell command. 286 * Feed it the file contents between the positions spos and epos. 287 */ 288 public int 289 pipe_data(cmd, spos, epos) 290 char *cmd; 291 POSITION spos; 292 POSITION epos; 293 { 294 FILE *f; 295 int c; 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