[BACK]Return to lsystem.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

File: [local] / src / usr.bin / less / lsystem.c (download)

Revision 1.20, Wed Sep 4 09:19:39 2019 UTC (4 years, 8 months ago) by schwarze
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, OPENBSD_6_6_BASE, OPENBSD_6_6, HEAD
Changes since 1.19: +1 -1 lines

adjust a variable name in a comment after the reorg in signal.c rev. 1.18

/*
 * Copyright (C) 1984-2012  Mark Nudelman
 * Modified for use with illumos by Garrett D'Amore.
 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
 *
 * You may distribute under the terms of either the GNU General Public
 * License or the Less License, as specified in the README file.
 *
 * For more information, see the README file.
 */

/*
 * Routines to execute other programs.
 * Necessarily very OS dependent.
 */

#include <signal.h>

#include "less.h"
#include "position.h"

extern int screen_trashed;
extern IFILE curr_ifile;

static int pipe_data(char *cmd, off_t spos, off_t epos);

/*
 * Pass the specified command to a shell to be executed.
 * Like plain "system()", but handles resetting terminal modes, etc.
 */
void
lsystem(const char *cmd, const char *donemsg)
{
	int inp;
	char *shell;
	char *p;
	IFILE save_ifile;

	/*
	 * Print the command which is to be executed,
	 * unless the command starts with a "-".
	 */
	if (cmd[0] == '-')
		cmd++;
	else {
		clear_bot();
		putstr("!");
		putstr(cmd);
		putstr("\n");
	}

	/*
	 * Close the current input file.
	 */
	save_ifile = save_curr_ifile();
	(void) edit_ifile(NULL);

	/*
	 * De-initialize the terminal and take out of raw mode.
	 */
	deinit();
	flush(0);	/* Make sure the deinit chars get out */
	raw_mode(0);

	/*
	 * Restore signals to their defaults.
	 */
	init_signals(0);

	/*
	 * Force standard input to be the user's terminal
	 * (the normal standard input), even if less's standard input
	 * is coming from a pipe.
	 */
	inp = dup(0);
	(void) close(0);
	if (open("/dev/tty", O_RDONLY) == -1)
		(void) dup(inp);

	/*
	 * Pass the command to the system to be executed.
	 * If we have a SHELL environment variable, use
	 * <$SHELL -c "command"> instead of just <command>.
	 * If the command is empty, just invoke a shell.
	 */
	p = NULL;
	if ((shell = lgetenv("SHELL")) != NULL && *shell != '\0') {
		if (*cmd == '\0') {
			p = estrdup(shell);
		} else {
			char *esccmd = shell_quote(cmd);
			if (esccmd != NULL) {
				p = easprintf("%s -c %s", shell, esccmd);
				free(esccmd);
			}
		}
	}
	if (p == NULL) {
		if (*cmd == '\0')
			p = estrdup("sh");
		else
			p = estrdup(cmd);
	}
	(void) system(p);
	free(p);

	/*
	 * Restore standard input, reset signals, raw mode, etc.
	 */
	(void) close(0);
	(void) dup(inp);
	(void) close(inp);

	init_signals(1);
	raw_mode(1);
	if (donemsg != NULL) {
		putstr(donemsg);
		putstr("  (press RETURN)");
		get_return();
		(void) putchr('\n');
		flush(0);
	}
	init();
	screen_trashed = 1;

	/*
	 * Reopen the current input file.
	 */
	reedit_ifile(save_ifile);

	/*
	 * Since we were ignoring window change signals while we executed
	 * the system command, we must assume the window changed.
	 * Warning: this leaves a signal pending (in "signal_winch"),
	 * so psignals() should be called soon after lsystem().
	 */
	sigwinch(0);
}

/*
 * Pipe a section of the input file into the given shell command.
 * The section to be piped is the section "between" the current
 * position and the position marked by the given letter.
 *
 * If the mark is after the current screen, the section between
 * the top line displayed and the mark is piped.
 * If the mark is before the current screen, the section between
 * the mark and the bottom line displayed is piped.
 * If the mark is on the current screen, or if the mark is ".",
 * the whole current screen is piped.
 */
int
pipe_mark(int c, char *cmd)
{
	off_t mpos, tpos, bpos;

	/*
	 * mpos = the marked position.
	 * tpos = top of screen.
	 * bpos = bottom of screen.
	 */
	mpos = markpos(c);
	if (mpos == -1)
		return (-1);
	tpos = position(TOP);
	if (tpos == -1)
		tpos = ch_zero();
	bpos = position(BOTTOM);

	if (c == '.')
		return (pipe_data(cmd, tpos, bpos));
	else if (mpos <= tpos)
		return (pipe_data(cmd, mpos, bpos));
	else if (bpos == -1)
		return (pipe_data(cmd, tpos, bpos));
	else
		return (pipe_data(cmd, tpos, mpos));
}

/*
 * Create a pipe to the given shell command.
 * Feed it the file contents between the positions spos and epos.
 */
static int
pipe_data(char *cmd, off_t spos, off_t epos)
{
	FILE *f;
	int c;

	/*
	 * This is structured much like lsystem().
	 * Since we're running a shell program, we must be careful
	 * to perform the necessary deinitialization before running
	 * the command, and reinitialization after it.
	 */
	if (ch_seek(spos) != 0) {
		error("Cannot seek to start position", NULL);
		return (-1);
	}

	if ((f = popen(cmd, "w")) == NULL) {
		error("Cannot create pipe", NULL);
		return (-1);
	}
	clear_bot();
	putstr("!");
	putstr(cmd);
	putstr("\n");

	deinit();
	flush(0);
	raw_mode(0);
	init_signals(0);
	lsignal(SIGPIPE, SIG_IGN);

	c = EOI;
	while (epos == -1 || spos++ <= epos) {
		/*
		 * Read a character from the file and give it to the pipe.
		 */
		c = ch_forw_get();
		if (c == EOI)
			break;
		if (putc(c, f) == EOF)
			break;
	}

	/*
	 * Finish up the last line.
	 */
	while (c != '\n' && c != EOI) {
		c = ch_forw_get();
		if (c == EOI)
			break;
		if (putc(c, f) == EOF)
			break;
	}

	(void) pclose(f);

	lsignal(SIGPIPE, SIG_DFL);
	init_signals(1);
	raw_mode(1);
	init();
	screen_trashed = 1;
	/* {{ Probably don't need this here. }} */
	sigwinch(0);
	return (0);
}