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