lsystem.c revision 161475
1193323Sed/* 2193323Sed * Copyright (C) 1984-2005 Mark Nudelman 3193323Sed * 4193323Sed * You may distribute under the terms of either the GNU General Public 5193323Sed * License or the Less License, as specified in the README file. 6193323Sed * 7193323Sed * For more information about less, or for information on how to 8193323Sed * contact the author, see the README file. 9193323Sed */ 10193323Sed 11193323Sed 12193323Sed/* 13193323Sed * Routines to execute other programs. 14210299Sed * Necessarily very OS dependent. 15193323Sed */ 16218893Sdim 17193323Sed#include "less.h" 18226890Sdim#include <signal.h> 19193323Sed#include "position.h" 20193323Sed 21193323Sed#if MSDOS_COMPILER 22252723Sdim#include <dos.h> 23193323Sed#ifdef _MSC_VER 24193323Sed#include <direct.h> 25252723Sdim#define setdisk(n) _chdrive((n)+1) 26252723Sdim#else 27252723Sdim#include <dir.h> 28252723Sdim#endif 29252723Sdim#endif 30252723Sdim 31207631Srdivackyextern int screen_trashed; 32193323Sedextern IFILE curr_ifile; 33193323Sed 34198090Srdivacky 35198090Srdivacky#if HAVE_SYSTEM 36252723Sdim 37252723Sdim/* 38198090Srdivacky * Pass the specified command to a shell to be executed. 39193323Sed * Like plain "system()", but handles resetting terminal modes, etc. 40193323Sed */ 41212904Sdim public void 42212904Sdimlsystem(cmd, donemsg) 43212904Sdim char *cmd; 44212904Sdim char *donemsg; 45212904Sdim{ 46218893Sdim register int inp; 47218893Sdim#if HAVE_SHELL 48218893Sdim register char *shell; 49221345Sdim register char *p; 50218893Sdim#endif 51193323Sed IFILE save_ifile; 52193323Sed#if MSDOS_COMPILER 53193323Sed char cwd[FILENAME_MAX+1]; 54193323Sed#endif 55193323Sed 56218893Sdim /* 57218893Sdim * Print the command which is to be executed, 58218893Sdim * unless the command starts with a "-". 59218893Sdim */ 60218893Sdim if (cmd[0] == '-') 61218893Sdim cmd++; 62193323Sed else 63195098Sed { 64193323Sed clear_bot(); 65193323Sed putstr("!"); 66193323Sed putstr(cmd); 67193323Sed putstr("\n"); 68193323Sed } 69193323Sed 70198090Srdivacky#if MSDOS_COMPILER 71198090Srdivacky /* 72198090Srdivacky * Working directory is global on MSDOS. 73218893Sdim * The child might change the working directory, so we 74193323Sed * must save and restore CWD across calls to "system", 75193323Sed * or else we won't find our file when we return and 76193323Sed * try to "reedit_ifile" it. 77193323Sed */ 78194612Sed getcwd(cwd, FILENAME_MAX); 79194612Sed#endif 80252723Sdim 81252723Sdim /* 82198090Srdivacky * Close the current input file. 83198090Srdivacky */ 84194612Sed save_ifile = save_curr_ifile(); 85194612Sed (void) edit_ifile(NULL_IFILE); 86194612Sed 87194612Sed /* 88202375Srdivacky * De-initialize the terminal and take out of raw mode. 89203954Srdivacky */ 90218893Sdim deinit(); 91218893Sdim flush(); /* Make sure the deinit chars get out */ 92218893Sdim raw_mode(0); 93218893Sdim#if MSDOS_COMPILER==WIN32C 94226890Sdim close_getchr(); 95221345Sdim#endif 96221345Sdim 97226890Sdim /* 98226890Sdim * Restore signals to their defaults. 99226890Sdim */ 100221345Sdim init_signals(0); 101221345Sdim 102226890Sdim#if HAVE_DUP 103221345Sdim /* 104226890Sdim * Force standard input to be the user's terminal 105226890Sdim * (the normal standard input), even if less's standard input 106226890Sdim * is coming from a pipe. 107226890Sdim */ 108226890Sdim inp = dup(0); 109221345Sdim close(0); 110218893Sdim#if OS2 111218893Sdim /* The __open() system call translates "/dev/tty" to "con". */ 112218893Sdim if (__open("/dev/tty", OPEN_READ) < 0) 113218893Sdim#else 114218893Sdim if (open("/dev/tty", OPEN_READ) < 0) 115218893Sdim#endif 116218893Sdim dup(inp); 117218893Sdim#endif 118218893Sdim 119218893Sdim /* 120218893Sdim * Pass the command to the system to be executed. 121218893Sdim * If we have a SHELL environment variable, use 122218893Sdim * <$SHELL -c "command"> instead of just <command>. 123218893Sdim * If the command is empty, just invoke a shell. 124218893Sdim */ 125218893Sdim#if HAVE_SHELL 126218893Sdim p = NULL; 127218893Sdim if ((shell = lgetenv("SHELL")) != NULL && *shell != '\0') 128218893Sdim { 129218893Sdim if (*cmd == '\0') 130218893Sdim p = save(shell); 131218893Sdim else 132218893Sdim { 133263509Sdim char *esccmd = shell_quote(cmd); 134263509Sdim if (esccmd != NULL) 135263509Sdim { 136263509Sdim int len = strlen(shell) + strlen(esccmd) + 5; 137263509Sdim p = (char *) ecalloc(len, sizeof(char)); 138263509Sdim SNPRINTF3(p, len, "%s %s %s", shell, shell_coption(), esccmd); 139263509Sdim free(esccmd); 140226890Sdim } 141193323Sed } 142226890Sdim } 143226890Sdim if (p == NULL) 144226890Sdim { 145226890Sdim if (*cmd == '\0') 146226890Sdim p = save("sh"); 147218893Sdim else 148193323Sed p = save(cmd); 149202375Srdivacky } 150193323Sed system(p); 151218893Sdim free(p); 152193323Sed#else 153218893Sdim#if MSDOS_COMPILER==DJGPPC 154219077Sdim /* 155193323Sed * Make stdin of the child be in cooked mode. 156218893Sdim */ 157193323Sed setmode(0, O_TEXT); 158218893Sdim /* 159218893Sdim * We don't need to catch signals of the child (it 160218893Sdim * also makes trouble with some DPMI servers). 161218893Sdim */ 162218893Sdim __djgpp_exception_toggle(); 163218893Sdim system(cmd); 164218893Sdim __djgpp_exception_toggle(); 165218893Sdim#else 166218893Sdim system(cmd); 167218893Sdim#endif 168218893Sdim#endif 169218893Sdim 170218893Sdim#if HAVE_DUP 171218893Sdim /* 172218893Sdim * Restore standard input, reset signals, raw mode, etc. 173218893Sdim */ 174193323Sed close(0); 175218893Sdim dup(inp); 176218893Sdim close(inp); 177195098Sed#endif 178218893Sdim 179218893Sdim#if MSDOS_COMPILER==WIN32C 180195340Sed open_getchr(); 181202375Srdivacky#endif 182195340Sed init_signals(1); 183218893Sdim raw_mode(1); 184195340Sed if (donemsg != NULL) 185263509Sdim { 186195340Sed putstr(donemsg); 187218893Sdim putstr(" (press RETURN)"); 188218893Sdim get_return(); 189218893Sdim putchr('\n'); 190218893Sdim flush(); 191218893Sdim } 192218893Sdim init(); 193218893Sdim screen_trashed = 1; 194218893Sdim 195218893Sdim#if MSDOS_COMPILER 196218893Sdim /* 197218893Sdim * Restore the previous directory (possibly 198218893Sdim * changed by the child program we just ran). 199218893Sdim */ 200218893Sdim chdir(cwd); 201218893Sdim#if MSDOS_COMPILER != DJGPPC 202218893Sdim /* 203193323Sed * Some versions of chdir() don't change to the drive 204193323Sed * which is part of CWD. (DJGPP does this in chdir.) 205193323Sed */ 206193323Sed if (cwd[1] == ':') 207195340Sed { 208195340Sed if (cwd[0] >= 'a' && cwd[0] <= 'z') 209202375Srdivacky setdisk(cwd[0] - 'a'); 210202375Srdivacky else if (cwd[0] >= 'A' && cwd[0] <= 'Z') 211195340Sed setdisk(cwd[0] - 'A'); 212206083Srdivacky } 213206083Srdivacky#endif 214198090Srdivacky#endif 215206083Srdivacky 216218893Sdim /* 217245431Sdim * Reopen the current input file. 218245431Sdim */ 219198090Srdivacky reedit_ifile(save_ifile); 220198113Srdivacky 221206083Srdivacky#if defined(SIGWINCH) || defined(SIGWIND) 222198113Srdivacky /* 223206083Srdivacky * Since we were ignoring window change signals while we executed 224218893Sdim * the system command, we must assume the window changed. 225245431Sdim * Warning: this leaves a signal pending (in "sigs"), 226245431Sdim * so psignals() should be called soon after lsystem(). 227198113Srdivacky */ 228198090Srdivacky winch(0); 229198090Srdivacky#endif 230218893Sdim} 231218893Sdim 232218893Sdim#endif 233245431Sdim 234198090Srdivacky#if PIPEC 235218893Sdim 236218893Sdim/* 237218893Sdim * Pipe a section of the input file into the given shell command. 238218893Sdim * The section to be piped is the section "between" the current 239245431Sdim * position and the position marked by the given letter. 240218893Sdim * 241210299Sed * If the mark is after the current screen, the section between 242210299Sed * the top line displayed and the mark is piped. 243210299Sed * If the mark is before the current screen, the section between 244210299Sed * the mark and the bottom line displayed is piped. 245210299Sed * If the mark is on the current screen, or if the mark is ".", 246198090Srdivacky * the whole current screen is piped. 247207618Srdivacky */ 248198090Srdivacky public int 249226890Sdimpipe_mark(c, cmd) 250226890Sdim int c; 251226890Sdim char *cmd; 252252723Sdim{ 253252723Sdim POSITION mpos, tpos, bpos; 254208599Srdivacky 255208599Srdivacky /* 256263509Sdim * mpos = the marked position. 257226890Sdim * tpos = top of screen. 258195340Sed * bpos = bottom of screen. 259195340Sed */ 260195340Sed mpos = markpos(c); 261195340Sed if (mpos == NULL_POSITION) 262195340Sed return (-1); 263198090Srdivacky tpos = position(TOP); 264252723Sdim if (tpos == NULL_POSITION) 265252723Sdim tpos = ch_zero(); 266252723Sdim bpos = position(BOTTOM); 267252723Sdim 268252723Sdim if (c == '.') 269208599Srdivacky return (pipe_data(cmd, tpos, bpos)); 270210299Sed else if (mpos <= tpos) 271252723Sdim return (pipe_data(cmd, mpos, bpos)); 272252723Sdim else if (bpos == NULL_POSITION) 273252723Sdim return (pipe_data(cmd, tpos, bpos)); 274208599Srdivacky else 275218893Sdim return (pipe_data(cmd, tpos, mpos)); 276218893Sdim} 277193323Sed 278193323Sed/* 279193323Sed * Create a pipe to the given shell command. 280198090Srdivacky * Feed it the file contents between the positions spos and epos. 281198090Srdivacky */ 282198090Srdivacky public int 283198090Srdivackypipe_data(cmd, spos, epos) 284198090Srdivacky char *cmd; 285198090Srdivacky POSITION spos; 286198090Srdivacky POSITION epos; 287198090Srdivacky{ 288198090Srdivacky register FILE *f; 289198090Srdivacky register int c; 290198090Srdivacky extern FILE *popen(); 291198090Srdivacky 292198090Srdivacky /* 293198090Srdivacky * This is structured much like lsystem(). 294198090Srdivacky * Since we're running a shell program, we must be careful 295198090Srdivacky * to perform the necessary deinitialization before running 296198090Srdivacky * the command, and reinitialization after it. 297198090Srdivacky */ 298198090Srdivacky if (ch_seek(spos) != 0) 299198090Srdivacky { 300198090Srdivacky error("Cannot seek to start position", NULL_PARG); 301198090Srdivacky return (-1); 302198090Srdivacky } 303198090Srdivacky 304218893Sdim if ((f = popen(cmd, "w")) == NULL) 305245431Sdim { 306218893Sdim error("Cannot create pipe", NULL_PARG); 307218893Sdim return (-1); 308226890Sdim } 309218893Sdim clear_bot(); 310218893Sdim putstr("!"); 311226890Sdim putstr(cmd); 312198090Srdivacky putstr("\n"); 313218893Sdim 314218893Sdim deinit(); 315218893Sdim flush(); 316218893Sdim raw_mode(0); 317218893Sdim init_signals(0); 318218893Sdim#if MSDOS_COMPILER==WIN32C 319218893Sdim close_getchr(); 320218893Sdim#endif 321218893Sdim#ifdef SIGPIPE 322218893Sdim LSIGNAL(SIGPIPE, SIG_IGN); 323218893Sdim#endif 324218893Sdim 325218893Sdim c = EOI; 326252723Sdim while (epos == NULL_POSITION || spos++ <= epos) 327252723Sdim { 328252723Sdim /* 329252723Sdim * Read a character from the file and give it to the pipe. 330252723Sdim */ 331252723Sdim c = ch_forw_get(); 332252723Sdim if (c == EOI) 333252723Sdim break; 334252723Sdim if (putc(c, f) == EOF) 335252723Sdim break; 336252723Sdim } 337252723Sdim 338252723Sdim /* 339252723Sdim * Finish up the last line. 340252723Sdim */ 341252723Sdim while (c != '\n' && c != EOI ) 342252723Sdim { 343252723Sdim c = ch_forw_get(); 344252723Sdim if (c == EOI) 345252723Sdim break; 346252723Sdim if (putc(c, f) == EOF) 347252723Sdim break; 348252723Sdim } 349252723Sdim 350252723Sdim pclose(f); 351252723Sdim 352252723Sdim#ifdef SIGPIPE 353252723Sdim LSIGNAL(SIGPIPE, SIG_DFL); 354252723Sdim#endif 355252723Sdim#if MSDOS_COMPILER==WIN32C 356252723Sdim open_getchr(); 357252723Sdim#endif 358263509Sdim init_signals(1); 359252723Sdim raw_mode(1); 360252723Sdim init(); 361252723Sdim screen_trashed = 1; 362252723Sdim#if defined(SIGWINCH) || defined(SIGWIND) 363252723Sdim /* {{ Probably don't need this here. }} */ 364252723Sdim winch(0); 365252723Sdim#endif 366252723Sdim return (0); 367252723Sdim} 368252723Sdim 369252723Sdim#endif 370252723Sdim