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