xref: /dflybsd-src/contrib/less/lsystem.c (revision e0f238eda64c20d98364903e0c003825fd0b66dd)
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