| Commit | Line | Data |
|---|---|---|
| 131ccf9c | 1 | /* |
| a9adbba3 | 2 | * Copyright (C) 1984-2009 Mark Nudelman |
| 131ccf9c PA |
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 about less, or for information on how to | |
| 8 | * contact the author, see the README file. | |
| 9 | */ | |
| 10 | ||
| 11 | ||
| 12 | /* | |
| 13 | * Routines to execute other programs. | |
| 14 | * Necessarily very OS dependent. | |
| 15 | */ | |
| 16 | ||
| 17 | #include "less.h" | |
| 18 | #include <signal.h> | |
| 19 | #include "position.h" | |
| 20 | ||
| 21 | #if MSDOS_COMPILER | |
| 22 | #include <dos.h> | |
| 23 | #ifdef _MSC_VER | |
| 24 | #include <direct.h> | |
| 25 | #define setdisk(n) _chdrive((n)+1) | |
| 26 | #else | |
| 27 | #include <dir.h> | |
| 28 | #endif | |
| 29 | #endif | |
| 30 | ||
| 31 | extern int screen_trashed; | |
| 32 | extern IFILE curr_ifile; | |
| 33 | ||
| 34 | ||
| 35 | #if HAVE_SYSTEM | |
| 36 | ||
| 37 | /* | |
| 38 | * Pass the specified command to a shell to be executed. | |
| 39 | * Like plain "system()", but handles resetting terminal modes, etc. | |
| 40 | */ | |
| 41 | public void | |
| 42 | lsystem(cmd, donemsg) | |
| 43 | char *cmd; | |
| 44 | char *donemsg; | |
| 45 | { | |
| 46 | register int inp; | |
| 47 | #if HAVE_SHELL | |
| 48 | register char *shell; | |
| 49 | register char *p; | |
| 50 | #endif | |
| 51 | IFILE save_ifile; | |
| 8be36e5b | 52 | #if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C |
| 131ccf9c PA |
53 | char cwd[FILENAME_MAX+1]; |
| 54 | #endif | |
| 55 | ||
| 56 | /* | |
| 57 | * Print the command which is to be executed, | |
| 58 | * unless the command starts with a "-". | |
| 59 | */ | |
| 60 | if (cmd[0] == '-') | |
| 61 | cmd++; | |
| 62 | else | |
| 63 | { | |
| 64 | clear_bot(); | |
| 65 | putstr("!"); | |
| 66 | putstr(cmd); | |
| 67 | putstr("\n"); | |
| 68 | } | |
| 69 | ||
| 70 | #if MSDOS_COMPILER | |
| 8be36e5b PA |
71 | #if MSDOS_COMPILER==WIN32C |
| 72 | if (*cmd == '\0') | |
| 73 | cmd = getenv("COMSPEC"); | |
| 74 | #else | |
| 131ccf9c PA |
75 | /* |
| 76 | * Working directory is global on MSDOS. | |
| 77 | * The child might change the working directory, so we | |
| 78 | * must save and restore CWD across calls to "system", | |
| 79 | * or else we won't find our file when we return and | |
| 80 | * try to "reedit_ifile" it. | |
| 81 | */ | |
| 82 | getcwd(cwd, FILENAME_MAX); | |
| 83 | #endif | |
| 8be36e5b | 84 | #endif |
| 131ccf9c PA |
85 | |
| 86 | /* | |
| 87 | * Close the current input file. | |
| 88 | */ | |
| 89 | save_ifile = save_curr_ifile(); | |
| 90 | (void) edit_ifile(NULL_IFILE); | |
| 91 | ||
| 92 | /* | |
| 93 | * De-initialize the terminal and take out of raw mode. | |
| 94 | */ | |
| 95 | deinit(); | |
| 96 | flush(); /* Make sure the deinit chars get out */ | |
| 97 | raw_mode(0); | |
| 98 | #if MSDOS_COMPILER==WIN32C | |
| 99 | close_getchr(); | |
| 100 | #endif | |
| 101 | ||
| 102 | /* | |
| 103 | * Restore signals to their defaults. | |
| 104 | */ | |
| 105 | init_signals(0); | |
| 106 | ||
| 107 | #if HAVE_DUP | |
| 108 | /* | |
| 109 | * Force standard input to be the user's terminal | |
| 110 | * (the normal standard input), even if less's standard input | |
| 111 | * is coming from a pipe. | |
| 112 | */ | |
| 113 | inp = dup(0); | |
| 114 | close(0); | |
| 115 | #if OS2 | |
| 116 | /* The __open() system call translates "/dev/tty" to "con". */ | |
| 117 | if (__open("/dev/tty", OPEN_READ) < 0) | |
| 118 | #else | |
| 119 | if (open("/dev/tty", OPEN_READ) < 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 = 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 | ||
| 8be36e5b | 200 | #if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C |
| 131ccf9c PA |
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 | */ | |
| 253 | public int | |
| 254 | pipe_mark(c, cmd) | |
| 255 | int c; | |
| 256 | char *cmd; | |
| 257 | { | |
| 258 | POSITION mpos, tpos, bpos; | |
| 259 | ||
| 260 | /* | |
| 261 | * mpos = the marked position. | |
| 262 | * tpos = top of screen. | |
| 263 | * bpos = bottom of screen. | |
| 264 | */ | |
| 265 | mpos = markpos(c); | |
| 266 | if (mpos == NULL_POSITION) | |
| 267 | return (-1); | |
| 268 | tpos = position(TOP); | |
| 269 | if (tpos == NULL_POSITION) | |
| 270 | tpos = ch_zero(); | |
| 271 | bpos = position(BOTTOM); | |
| 272 | ||
| 273 | if (c == '.') | |
| 274 | return (pipe_data(cmd, tpos, bpos)); | |
| 275 | else if (mpos <= tpos) | |
| 276 | return (pipe_data(cmd, mpos, bpos)); | |
| 277 | else if (bpos == NULL_POSITION) | |
| 278 | return (pipe_data(cmd, tpos, bpos)); | |
| 279 | else | |
| 280 | return (pipe_data(cmd, tpos, mpos)); | |
| 281 | } | |
| 282 | ||
| 283 | /* | |
| 284 | * Create a pipe to the given shell command. | |
| 285 | * Feed it the file contents between the positions spos and epos. | |
| 286 | */ | |
| 287 | public int | |
| 288 | pipe_data(cmd, spos, epos) | |
| 289 | char *cmd; | |
| 290 | POSITION spos; | |
| 291 | POSITION epos; | |
| 292 | { | |
| 293 | register FILE *f; | |
| 294 | register int c; | |
| 295 | extern FILE *popen(); | |
| 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 |