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