11133e27eSPeter Avalos /*
2*e433da38SAaron LI * Copyright (C) 1984-2024 Mark Nudelman
31133e27eSPeter Avalos *
41133e27eSPeter Avalos * You may distribute under the terms of either the GNU General Public
51133e27eSPeter Avalos * License or the Less License, as specified in the README file.
61133e27eSPeter Avalos *
7e639dc31SJohn Marino * For more information, see the README file.
81133e27eSPeter Avalos */
91133e27eSPeter Avalos
101133e27eSPeter Avalos
111133e27eSPeter Avalos /*
121133e27eSPeter Avalos * Routines to execute other programs.
131133e27eSPeter Avalos * Necessarily very OS dependent.
141133e27eSPeter Avalos */
151133e27eSPeter Avalos
161133e27eSPeter Avalos #include "less.h"
171133e27eSPeter Avalos #include <signal.h>
181133e27eSPeter Avalos #include "position.h"
191133e27eSPeter Avalos
201133e27eSPeter Avalos #if MSDOS_COMPILER
211133e27eSPeter Avalos #include <dos.h>
2202d62a0fSDaniel Fojt #if MSDOS_COMPILER==WIN32C && defined(MINGW)
2302d62a0fSDaniel Fojt #include <direct.h>
2402d62a0fSDaniel Fojt #define setdisk(n) _chdrive((n)+1)
2502d62a0fSDaniel Fojt #else
261133e27eSPeter Avalos #ifdef _MSC_VER
271133e27eSPeter Avalos #include <direct.h>
281133e27eSPeter Avalos #define setdisk(n) _chdrive((n)+1)
291133e27eSPeter Avalos #else
301133e27eSPeter Avalos #include <dir.h>
311133e27eSPeter Avalos #endif
321133e27eSPeter Avalos #endif
3302d62a0fSDaniel Fojt #endif
341133e27eSPeter Avalos
351133e27eSPeter Avalos extern IFILE curr_ifile;
361133e27eSPeter Avalos
371133e27eSPeter Avalos
381133e27eSPeter Avalos #if HAVE_SYSTEM
391133e27eSPeter Avalos
401133e27eSPeter Avalos /*
411133e27eSPeter Avalos * Pass the specified command to a shell to be executed.
421133e27eSPeter Avalos * Like plain "system()", but handles resetting terminal modes, etc.
431133e27eSPeter Avalos */
lsystem(constant char * cmd,constant char * donemsg)44*e433da38SAaron LI public void lsystem(constant char *cmd, constant char *donemsg)
451133e27eSPeter Avalos {
4602d62a0fSDaniel Fojt int inp;
471133e27eSPeter Avalos #if HAVE_SHELL
48*e433da38SAaron LI constant char *shell;
4902d62a0fSDaniel Fojt char *p;
501133e27eSPeter Avalos #endif
511133e27eSPeter Avalos IFILE save_ifile;
528be36e5bSPeter Avalos #if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C
531133e27eSPeter Avalos char cwd[FILENAME_MAX+1];
541133e27eSPeter Avalos #endif
551133e27eSPeter Avalos
561133e27eSPeter Avalos /*
571133e27eSPeter Avalos * Print the command which is to be executed,
581133e27eSPeter Avalos * unless the command starts with a "-".
591133e27eSPeter Avalos */
601133e27eSPeter Avalos if (cmd[0] == '-')
611133e27eSPeter Avalos cmd++;
621133e27eSPeter Avalos else
631133e27eSPeter Avalos {
641133e27eSPeter Avalos clear_bot();
651133e27eSPeter Avalos putstr("!");
661133e27eSPeter Avalos putstr(cmd);
671133e27eSPeter Avalos putstr("\n");
681133e27eSPeter Avalos }
691133e27eSPeter Avalos
701133e27eSPeter Avalos #if MSDOS_COMPILER
718be36e5bSPeter Avalos #if MSDOS_COMPILER==WIN32C
728be36e5bSPeter Avalos if (*cmd == '\0')
738be36e5bSPeter Avalos cmd = getenv("COMSPEC");
748be36e5bSPeter Avalos #else
751133e27eSPeter Avalos /*
761133e27eSPeter Avalos * Working directory is global on MSDOS.
771133e27eSPeter Avalos * The child might change the working directory, so we
781133e27eSPeter Avalos * must save and restore CWD across calls to "system",
791133e27eSPeter Avalos * or else we won't find our file when we return and
801133e27eSPeter Avalos * try to "reedit_ifile" it.
811133e27eSPeter Avalos */
821133e27eSPeter Avalos getcwd(cwd, FILENAME_MAX);
831133e27eSPeter Avalos #endif
848be36e5bSPeter Avalos #endif
851133e27eSPeter Avalos
861133e27eSPeter Avalos /*
871133e27eSPeter Avalos * Close the current input file.
881133e27eSPeter Avalos */
891133e27eSPeter Avalos save_ifile = save_curr_ifile();
901133e27eSPeter Avalos (void) edit_ifile(NULL_IFILE);
911133e27eSPeter Avalos
921133e27eSPeter Avalos /*
931133e27eSPeter Avalos * De-initialize the terminal and take out of raw mode.
941133e27eSPeter Avalos */
951133e27eSPeter Avalos deinit();
961133e27eSPeter Avalos flush(); /* Make sure the deinit chars get out */
971133e27eSPeter Avalos raw_mode(0);
981133e27eSPeter Avalos #if MSDOS_COMPILER==WIN32C
991133e27eSPeter Avalos close_getchr();
1001133e27eSPeter Avalos #endif
1011133e27eSPeter Avalos
1021133e27eSPeter Avalos /*
1031133e27eSPeter Avalos * Restore signals to their defaults.
1041133e27eSPeter Avalos */
1051133e27eSPeter Avalos init_signals(0);
1061133e27eSPeter Avalos
1071133e27eSPeter Avalos #if HAVE_DUP
1081133e27eSPeter Avalos /*
1091133e27eSPeter Avalos * Force standard input to be the user's terminal
1101133e27eSPeter Avalos * (the normal standard input), even if less's standard input
1111133e27eSPeter Avalos * is coming from a pipe.
1121133e27eSPeter Avalos */
1131133e27eSPeter Avalos inp = dup(0);
1141133e27eSPeter Avalos close(0);
1150c7ad07eSAntonio Huete Jimenez #if !MSDOS_COMPILER
1160c7ad07eSAntonio Huete Jimenez if (open_tty() < 0)
1171133e27eSPeter Avalos #endif
1181133e27eSPeter Avalos dup(inp);
1191133e27eSPeter Avalos #endif
1201133e27eSPeter Avalos
1211133e27eSPeter Avalos /*
1221133e27eSPeter Avalos * Pass the command to the system to be executed.
1231133e27eSPeter Avalos * If we have a SHELL environment variable, use
1241133e27eSPeter Avalos * <$SHELL -c "command"> instead of just <command>.
1251133e27eSPeter Avalos * If the command is empty, just invoke a shell.
1261133e27eSPeter Avalos */
1271133e27eSPeter Avalos #if HAVE_SHELL
1281133e27eSPeter Avalos p = NULL;
1291133e27eSPeter Avalos if ((shell = lgetenv("SHELL")) != NULL && *shell != '\0')
1301133e27eSPeter Avalos {
1311133e27eSPeter Avalos if (*cmd == '\0')
1321133e27eSPeter Avalos p = save(shell);
1331133e27eSPeter Avalos else
1341133e27eSPeter Avalos {
1351133e27eSPeter Avalos char *esccmd = shell_quote(cmd);
1361133e27eSPeter Avalos if (esccmd != NULL)
1371133e27eSPeter Avalos {
138*e433da38SAaron LI size_t len = strlen(shell) + strlen(esccmd) + 5;
1391133e27eSPeter Avalos p = (char *) ecalloc(len, sizeof(char));
1401133e27eSPeter Avalos SNPRINTF3(p, len, "%s %s %s", shell, shell_coption(), esccmd);
1411133e27eSPeter Avalos free(esccmd);
1421133e27eSPeter Avalos }
1431133e27eSPeter Avalos }
1441133e27eSPeter Avalos }
1451133e27eSPeter Avalos if (p == NULL)
1461133e27eSPeter Avalos {
1471133e27eSPeter Avalos if (*cmd == '\0')
1481133e27eSPeter Avalos p = save("sh");
1491133e27eSPeter Avalos else
1501133e27eSPeter Avalos p = save(cmd);
1511133e27eSPeter Avalos }
1521133e27eSPeter Avalos system(p);
1531133e27eSPeter Avalos free(p);
1541133e27eSPeter Avalos #else
1551133e27eSPeter Avalos #if MSDOS_COMPILER==DJGPPC
1561133e27eSPeter Avalos /*
1571133e27eSPeter Avalos * Make stdin of the child be in cooked mode.
1581133e27eSPeter Avalos */
1591133e27eSPeter Avalos setmode(0, O_TEXT);
1601133e27eSPeter Avalos /*
1611133e27eSPeter Avalos * We don't need to catch signals of the child (it
1621133e27eSPeter Avalos * also makes trouble with some DPMI servers).
1631133e27eSPeter Avalos */
1641133e27eSPeter Avalos __djgpp_exception_toggle();
1651133e27eSPeter Avalos system(cmd);
1661133e27eSPeter Avalos __djgpp_exception_toggle();
1671133e27eSPeter Avalos #else
1681133e27eSPeter Avalos system(cmd);
1691133e27eSPeter Avalos #endif
1701133e27eSPeter Avalos #endif
1711133e27eSPeter Avalos
1721133e27eSPeter Avalos #if HAVE_DUP
1731133e27eSPeter Avalos /*
1741133e27eSPeter Avalos * Restore standard input, reset signals, raw mode, etc.
1751133e27eSPeter Avalos */
1761133e27eSPeter Avalos close(0);
1771133e27eSPeter Avalos dup(inp);
1781133e27eSPeter Avalos close(inp);
1791133e27eSPeter Avalos #endif
1801133e27eSPeter Avalos
1811133e27eSPeter Avalos #if MSDOS_COMPILER==WIN32C
1821133e27eSPeter Avalos open_getchr();
1831133e27eSPeter Avalos #endif
1841133e27eSPeter Avalos init_signals(1);
1851133e27eSPeter Avalos raw_mode(1);
1861133e27eSPeter Avalos if (donemsg != NULL)
1871133e27eSPeter Avalos {
1881133e27eSPeter Avalos putstr(donemsg);
1891133e27eSPeter Avalos putstr(" (press RETURN)");
1901133e27eSPeter Avalos get_return();
1911133e27eSPeter Avalos putchr('\n');
1921133e27eSPeter Avalos flush();
1931133e27eSPeter Avalos }
1941133e27eSPeter Avalos init();
195*e433da38SAaron LI screen_trashed();
1961133e27eSPeter Avalos
1978be36e5bSPeter Avalos #if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C
1981133e27eSPeter Avalos /*
1991133e27eSPeter Avalos * Restore the previous directory (possibly
2001133e27eSPeter Avalos * changed by the child program we just ran).
2011133e27eSPeter Avalos */
2021133e27eSPeter Avalos chdir(cwd);
2031133e27eSPeter Avalos #if MSDOS_COMPILER != DJGPPC
2041133e27eSPeter Avalos /*
2051133e27eSPeter Avalos * Some versions of chdir() don't change to the drive
2061133e27eSPeter Avalos * which is part of CWD. (DJGPP does this in chdir.)
2071133e27eSPeter Avalos */
2081133e27eSPeter Avalos if (cwd[1] == ':')
2091133e27eSPeter Avalos {
2101133e27eSPeter Avalos if (cwd[0] >= 'a' && cwd[0] <= 'z')
2111133e27eSPeter Avalos setdisk(cwd[0] - 'a');
2121133e27eSPeter Avalos else if (cwd[0] >= 'A' && cwd[0] <= 'Z')
2131133e27eSPeter Avalos setdisk(cwd[0] - 'A');
2141133e27eSPeter Avalos }
2151133e27eSPeter Avalos #endif
2161133e27eSPeter Avalos #endif
2171133e27eSPeter Avalos
2181133e27eSPeter Avalos /*
2191133e27eSPeter Avalos * Reopen the current input file.
2201133e27eSPeter Avalos */
2211133e27eSPeter Avalos reedit_ifile(save_ifile);
2221133e27eSPeter Avalos
2231133e27eSPeter Avalos #if defined(SIGWINCH) || defined(SIGWIND)
2241133e27eSPeter Avalos /*
2251133e27eSPeter Avalos * Since we were ignoring window change signals while we executed
2261133e27eSPeter Avalos * the system command, we must assume the window changed.
2271133e27eSPeter Avalos * Warning: this leaves a signal pending (in "sigs"),
2281133e27eSPeter Avalos * so psignals() should be called soon after lsystem().
2291133e27eSPeter Avalos */
2301133e27eSPeter Avalos winch(0);
2311133e27eSPeter Avalos #endif
2321133e27eSPeter Avalos }
2331133e27eSPeter Avalos
2341133e27eSPeter Avalos #endif
2351133e27eSPeter Avalos
2361133e27eSPeter Avalos #if PIPEC
2371133e27eSPeter Avalos
2381133e27eSPeter Avalos /*
2391133e27eSPeter Avalos * Pipe a section of the input file into the given shell command.
2401133e27eSPeter Avalos * The section to be piped is the section "between" the current
2411133e27eSPeter Avalos * position and the position marked by the given letter.
2421133e27eSPeter Avalos *
2431133e27eSPeter Avalos * If the mark is after the current screen, the section between
2441133e27eSPeter Avalos * the top line displayed and the mark is piped.
2451133e27eSPeter Avalos * If the mark is before the current screen, the section between
2461133e27eSPeter Avalos * the mark and the bottom line displayed is piped.
2471133e27eSPeter Avalos * If the mark is on the current screen, or if the mark is ".",
2481133e27eSPeter Avalos * the whole current screen is piped.
2491133e27eSPeter Avalos */
pipe_mark(char c,constant char * cmd)250*e433da38SAaron LI public int pipe_mark(char c, constant char *cmd)
2511133e27eSPeter Avalos {
2521133e27eSPeter Avalos POSITION mpos, tpos, bpos;
2531133e27eSPeter Avalos
2541133e27eSPeter Avalos /*
2551133e27eSPeter Avalos * mpos = the marked position.
2561133e27eSPeter Avalos * tpos = top of screen.
2571133e27eSPeter Avalos * bpos = bottom of screen.
2581133e27eSPeter Avalos */
2591133e27eSPeter Avalos mpos = markpos(c);
2601133e27eSPeter Avalos if (mpos == NULL_POSITION)
2611133e27eSPeter Avalos return (-1);
2621133e27eSPeter Avalos tpos = position(TOP);
2631133e27eSPeter Avalos if (tpos == NULL_POSITION)
2641133e27eSPeter Avalos tpos = ch_zero();
2651133e27eSPeter Avalos bpos = position(BOTTOM);
2661133e27eSPeter Avalos
2671133e27eSPeter Avalos if (c == '.')
2681133e27eSPeter Avalos return (pipe_data(cmd, tpos, bpos));
2691133e27eSPeter Avalos else if (mpos <= tpos)
2701133e27eSPeter Avalos return (pipe_data(cmd, mpos, bpos));
2711133e27eSPeter Avalos else if (bpos == NULL_POSITION)
2721133e27eSPeter Avalos return (pipe_data(cmd, tpos, bpos));
2731133e27eSPeter Avalos else
2741133e27eSPeter Avalos return (pipe_data(cmd, tpos, mpos));
2751133e27eSPeter Avalos }
2761133e27eSPeter Avalos
2771133e27eSPeter Avalos /*
2781133e27eSPeter Avalos * Create a pipe to the given shell command.
2791133e27eSPeter Avalos * Feed it the file contents between the positions spos and epos.
2801133e27eSPeter Avalos */
pipe_data(constant char * cmd,POSITION spos,POSITION epos)281*e433da38SAaron LI public int pipe_data(constant char *cmd, POSITION spos, POSITION epos)
2821133e27eSPeter Avalos {
28302d62a0fSDaniel Fojt FILE *f;
28402d62a0fSDaniel Fojt int c;
2851133e27eSPeter Avalos
2861133e27eSPeter Avalos /*
2871133e27eSPeter Avalos * This is structured much like lsystem().
2881133e27eSPeter Avalos * Since we're running a shell program, we must be careful
2891133e27eSPeter Avalos * to perform the necessary deinitialization before running
2901133e27eSPeter Avalos * the command, and reinitialization after it.
2911133e27eSPeter Avalos */
2921133e27eSPeter Avalos if (ch_seek(spos) != 0)
2931133e27eSPeter Avalos {
2941133e27eSPeter Avalos error("Cannot seek to start position", NULL_PARG);
2951133e27eSPeter Avalos return (-1);
2961133e27eSPeter Avalos }
2971133e27eSPeter Avalos
2981133e27eSPeter Avalos if ((f = popen(cmd, "w")) == NULL)
2991133e27eSPeter Avalos {
3001133e27eSPeter Avalos error("Cannot create pipe", NULL_PARG);
3011133e27eSPeter Avalos return (-1);
3021133e27eSPeter Avalos }
3031133e27eSPeter Avalos clear_bot();
3041133e27eSPeter Avalos putstr("!");
3051133e27eSPeter Avalos putstr(cmd);
3061133e27eSPeter Avalos putstr("\n");
3071133e27eSPeter Avalos
3081133e27eSPeter Avalos deinit();
3091133e27eSPeter Avalos flush();
3101133e27eSPeter Avalos raw_mode(0);
3111133e27eSPeter Avalos init_signals(0);
3121133e27eSPeter Avalos #if MSDOS_COMPILER==WIN32C
3131133e27eSPeter Avalos close_getchr();
3141133e27eSPeter Avalos #endif
3151133e27eSPeter Avalos #ifdef SIGPIPE
3161133e27eSPeter Avalos LSIGNAL(SIGPIPE, SIG_IGN);
3171133e27eSPeter Avalos #endif
3181133e27eSPeter Avalos
3191133e27eSPeter Avalos c = EOI;
3201133e27eSPeter Avalos while (epos == NULL_POSITION || spos++ <= epos)
3211133e27eSPeter Avalos {
3221133e27eSPeter Avalos /*
3231133e27eSPeter Avalos * Read a character from the file and give it to the pipe.
3241133e27eSPeter Avalos */
3251133e27eSPeter Avalos c = ch_forw_get();
3261133e27eSPeter Avalos if (c == EOI)
3271133e27eSPeter Avalos break;
3281133e27eSPeter Avalos if (putc(c, f) == EOF)
3291133e27eSPeter Avalos break;
3301133e27eSPeter Avalos }
3311133e27eSPeter Avalos
3321133e27eSPeter Avalos /*
3331133e27eSPeter Avalos * Finish up the last line.
3341133e27eSPeter Avalos */
3351133e27eSPeter Avalos while (c != '\n' && c != EOI )
3361133e27eSPeter Avalos {
3371133e27eSPeter Avalos c = ch_forw_get();
3381133e27eSPeter Avalos if (c == EOI)
3391133e27eSPeter Avalos break;
3401133e27eSPeter Avalos if (putc(c, f) == EOF)
3411133e27eSPeter Avalos break;
3421133e27eSPeter Avalos }
3431133e27eSPeter Avalos
3441133e27eSPeter Avalos pclose(f);
3451133e27eSPeter Avalos
3461133e27eSPeter Avalos #ifdef SIGPIPE
3471133e27eSPeter Avalos LSIGNAL(SIGPIPE, SIG_DFL);
3481133e27eSPeter Avalos #endif
3491133e27eSPeter Avalos #if MSDOS_COMPILER==WIN32C
3501133e27eSPeter Avalos open_getchr();
3511133e27eSPeter Avalos #endif
3521133e27eSPeter Avalos init_signals(1);
3531133e27eSPeter Avalos raw_mode(1);
3541133e27eSPeter Avalos init();
355*e433da38SAaron LI screen_trashed();
3561133e27eSPeter Avalos #if defined(SIGWINCH) || defined(SIGWIND)
3571133e27eSPeter Avalos /* {{ Probably don't need this here. }} */
3581133e27eSPeter Avalos winch(0);
3591133e27eSPeter Avalos #endif
3601133e27eSPeter Avalos return (0);
3611133e27eSPeter Avalos }
3621133e27eSPeter Avalos
3631133e27eSPeter Avalos #endif
364