/* * Copyright (C) 1984-2012 Mark Nudelman * Modified for use with illumos by Garrett D'Amore. * Copyright 2014 Garrett D'Amore * * 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 #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) < 0) (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 . * 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 "sigs"), * 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); }