/*1* Copyright (C) 1984-2025 Mark Nudelman2*3* You may distribute under the terms of either the GNU General Public4* License or the Less License, as specified in the README file.5*6* For more information, see the README file.7*/8910/*11* Routines to execute other programs.12* Necessarily very OS dependent.13*/1415#include "less.h"16#include <signal.h>17#include "position.h"1819#if MSDOS_COMPILER20#include <dos.h>21#if MSDOS_COMPILER==WIN32C && defined(MINGW)22#include <direct.h>23#define setdisk(n) _chdrive((n)+1)24#else25#ifdef _MSC_VER26#include <direct.h>27#define setdisk(n) _chdrive((n)+1)28#else29#include <dir.h>30#endif31#endif32#endif3334extern IFILE curr_ifile;353637#if HAVE_SYSTEM3839/*40* Pass the specified command to a shell to be executed.41* Like plain "system()", but handles resetting terminal modes, etc.42*/43public void lsystem(constant char *cmd, constant char *donemsg)44{45int inp;46#if HAVE_SHELL47constant char *shell;48char *p;49#endif50IFILE save_ifile;51#if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C52char cwd[FILENAME_MAX+1];53#endif5455/*56* Print the command which is to be executed,57* unless the command starts with a "-".58*/59if (cmd[0] == '-')60cmd++;61else62{63clear_bot();64putstr("!");65putstr(cmd);66putstr("\n");67}6869#if MSDOS_COMPILER70#if MSDOS_COMPILER==WIN32C71if (*cmd == '\0')72cmd = getenv("COMSPEC");73#else74/*75* Working directory is global on MSDOS.76* The child might change the working directory, so we77* must save and restore CWD across calls to "system",78* or else we won't find our file when we return and79* try to "reedit_ifile" it.80*/81getcwd(cwd, FILENAME_MAX);82#endif83#endif8485/*86* Close the current input file.87*/88save_ifile = save_curr_ifile();89(void) edit_ifile(NULL_IFILE);9091/*92* De-initialize the terminal and take out of raw mode.93*/94deinit();95flush(); /* Make sure the deinit chars get out */96raw_mode(0);97#if MSDOS_COMPILER==WIN32C98close_getchr();99#endif100101/*102* Restore signals to their defaults.103*/104init_signals(0);105106#if HAVE_DUP107/*108* Force standard input to be the user's terminal109* (the normal standard input), even if less's standard input110* is coming from a pipe.111*/112inp = dup(0);113close(0);114#if !MSDOS_COMPILER115if (open_tty() < 0)116#endif117dup(inp);118#endif119120/*121* Pass the command to the system to be executed.122* If we have a SHELL environment variable, use123* <$SHELL -c "command"> instead of just <command>.124* If the command is empty, just invoke a shell.125*/126#if HAVE_SHELL127p = NULL;128if ((shell = lgetenv("SHELL")) != NULL && *shell != '\0')129{130if (*cmd == '\0')131p = save(shell);132else133{134char *esccmd = shell_quote(cmd);135if (esccmd != NULL)136{137size_t len = strlen(shell) + strlen(esccmd) + 5;138p = (char *) ecalloc(len, sizeof(char));139SNPRINTF3(p, len, "%s %s %s", shell, shell_coption(), esccmd);140free(esccmd);141}142}143}144if (p == NULL)145{146if (*cmd == '\0')147p = save("sh");148else149p = save(cmd);150}151system(p);152free(p);153#else154#if MSDOS_COMPILER==DJGPPC155/*156* Make stdin of the child be in cooked mode.157*/158setmode(0, O_TEXT);159/*160* We don't need to catch signals of the child (it161* also makes trouble with some DPMI servers).162*/163__djgpp_exception_toggle();164system(cmd);165__djgpp_exception_toggle();166#else167system(cmd);168#endif169#endif170171#if HAVE_DUP172/*173* Restore standard input, reset signals, raw mode, etc.174*/175close(0);176dup(inp);177close(inp);178#endif179180#if MSDOS_COMPILER==WIN32C181open_getchr();182#endif183init_signals(1);184raw_mode(1);185if (donemsg != NULL)186{187putstr(donemsg);188putstr(" (press RETURN)");189get_return();190putchr('\n');191flush();192}193init();194screen_trashed();195196#if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C197/*198* Restore the previous directory (possibly199* changed by the child program we just ran).200*/201chdir(cwd);202#if MSDOS_COMPILER != DJGPPC203/*204* Some versions of chdir() don't change to the drive205* which is part of CWD. (DJGPP does this in chdir.)206*/207if (cwd[1] == ':')208{209if (cwd[0] >= 'a' && cwd[0] <= 'z')210setdisk(cwd[0] - 'a');211else if (cwd[0] >= 'A' && cwd[0] <= 'Z')212setdisk(cwd[0] - 'A');213}214#endif215#endif216217/*218* Reopen the current input file.219*/220reedit_ifile(save_ifile);221222#if defined(SIGWINCH) || defined(SIGWIND)223/*224* Since we were ignoring window change signals while we executed225* the system command, we must assume the window changed.226* Warning: this leaves a signal pending (in "sigs"),227* so psignals() should be called soon after lsystem().228*/229winch(0);230#endif231}232233#endif234235#if PIPEC236237/*238* Pipe a section of the input file into the given shell command.239* The section to be piped is the section "between" the current240* position and the position marked by the given letter.241*242* If the mark is after the current screen, the section between243* the top line displayed and the mark is piped.244* If the mark is before the current screen, the section between245* the mark and the bottom line displayed is piped.246* If the mark is on the current screen, or if the mark is ".",247* the whole current screen is piped.248*/249public int pipe_mark(char c, constant char *cmd)250{251POSITION mpos, tpos, bpos;252253/*254* mpos = the marked position.255* tpos = top of screen.256* bpos = bottom of screen.257*/258mpos = markpos(c);259if (mpos == NULL_POSITION)260return (-1);261tpos = position(TOP);262if (tpos == NULL_POSITION)263tpos = ch_zero();264bpos = position(BOTTOM);265266if (c == '.')267return (pipe_data(cmd, tpos, bpos));268else if (mpos <= tpos)269return (pipe_data(cmd, mpos, bpos));270else if (bpos == NULL_POSITION)271return (pipe_data(cmd, tpos, bpos));272else273return (pipe_data(cmd, tpos, mpos));274}275276/*277* Create a pipe to the given shell command.278* Feed it the file contents between the positions spos and epos.279*/280public int pipe_data(constant char *cmd, POSITION spos, POSITION epos)281{282FILE *f;283int c;284285/*286* This is structured much like lsystem().287* Since we're running a shell program, we must be careful288* to perform the necessary deinitialization before running289* the command, and reinitialization after it.290*/291if (ch_seek(spos) != 0)292{293error("Cannot seek to start position", NULL_PARG);294return (-1);295}296297if ((f = popen(cmd, "w")) == NULL)298{299error("Cannot create pipe", NULL_PARG);300return (-1);301}302clear_bot();303putstr("!");304putstr(cmd);305putstr("\n");306307deinit();308flush();309raw_mode(0);310init_signals(0);311#if MSDOS_COMPILER==WIN32C312close_getchr();313#endif314#ifdef SIGPIPE315LSIGNAL(SIGPIPE, SIG_IGN);316#endif317318c = EOI;319while (epos == NULL_POSITION || spos++ <= epos)320{321/*322* Read a character from the file and give it to the pipe.323*/324c = ch_forw_get();325if (c == EOI)326break;327if (putc(c, f) == EOF)328break;329}330331/*332* Finish up the last line.333*/334while (c != '\n' && c != EOI )335{336c = ch_forw_get();337if (c == EOI)338break;339if (putc(c, f) == EOF)340break;341}342343pclose(f);344345#ifdef SIGPIPE346LSIGNAL(SIGPIPE, SIG_DFL);347#endif348#if MSDOS_COMPILER==WIN32C349open_getchr();350#endif351init_signals(1);352raw_mode(1);353init();354screen_trashed();355#if defined(SIGWINCH) || defined(SIGWIND)356/* {{ Probably don't need this here. }} */357winch(0);358#endif359return (0);360}361362#endif363364365