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