main.c revision 59797
133965Sjdp/* main.c: This file contains the main control and user-interface routines 233965Sjdp for the ed line editor. */ 333965Sjdp/*- 433965Sjdp * Copyright (c) 1993 Andrew Moore, Talke Studio. 533965Sjdp * All rights reserved. 633965Sjdp * 733965Sjdp * Redistribution and use in source and binary forms, with or without 833965Sjdp * modification, are permitted provided that the following conditions 933965Sjdp * are met: 1033965Sjdp * 1. Redistributions of source code must retain the above copyright 1133965Sjdp * notice, this list of conditions and the following disclaimer. 1233965Sjdp * 2. Redistributions in binary form must reproduce the above copyright 1333965Sjdp * notice, this list of conditions and the following disclaimer in the 1433965Sjdp * documentation and/or other materials provided with the distribution. 1533965Sjdp * 1633965Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1733965Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1833965Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1933965Sjdp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2033965Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2133965Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2233965Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2333965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2433965Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2533965Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2633965Sjdp * SUCH DAMAGE. 2733965Sjdp */ 2833965Sjdp 2933965Sjdp#ifndef lint 3033965Sjdpstatic char * const copyright = 3133965Sjdp"@(#) Copyright (c) 1993 Andrew Moore, Talke Studio. \n\ 3233965Sjdp All rights reserved.\n"; 3333965Sjdp#endif /* not lint */ 3433965Sjdp 3533965Sjdp#ifndef lint 3633965Sjdp#if 0 3733965Sjdpstatic char * const rcsid = "@(#)main.c,v 1.1 1994/02/01 00:34:42 alm Exp"; 3833965Sjdp#else 3933965Sjdpstatic char * const rcsid = 4033965Sjdp "$FreeBSD: head/bin/ed/main.c 59797 2000-04-30 20:46:14Z joe $"; 4133965Sjdp#endif 4233965Sjdp#endif /* not lint */ 4333965Sjdp 4433965Sjdp/* 4533965Sjdp * CREDITS 4633965Sjdp * 4733965Sjdp * This program is based on the editor algorithm described in 4833965Sjdp * Brian W. Kernighan and P. J. Plauger's book "Software Tools 4933965Sjdp * in Pascal," Addison-Wesley, 1981. 5033965Sjdp * 5133965Sjdp * The buffering algorithm is attributed to Rodney Ruddock of 5233965Sjdp * the University of Guelph, Guelph, Ontario. 5333965Sjdp * 5433965Sjdp * The cbc.c encryption code is adapted from 5533965Sjdp * the bdes program by Matt Bishop of Dartmouth College, 5633965Sjdp * Hanover, NH. 5733965Sjdp * 5833965Sjdp */ 5933965Sjdp 6033965Sjdp#include <sys/ioctl.h> 6133965Sjdp#include <sys/wait.h> 6233965Sjdp#include <ctype.h> 6333965Sjdp#include <locale.h> 6433965Sjdp#include <pwd.h> 6533965Sjdp#include <setjmp.h> 6633965Sjdp 6733965Sjdp#include "ed.h" 6833965Sjdp 6933965Sjdp 7033965Sjdp#ifdef _POSIX_SOURCE 7133965Sjdpsigjmp_buf env; 7233965Sjdp#else 7333965Sjdpjmp_buf env; 7433965Sjdp#endif 7533965Sjdp 7633965Sjdp/* static buffers */ 7733965Sjdpchar stdinbuf[1]; /* stdin buffer */ 7833965Sjdpchar *shcmd; /* shell command buffer */ 7933965Sjdpint shcmdsz; /* shell command buffer size */ 8033965Sjdpint shcmdi; /* shell command buffer index */ 8133965Sjdpchar *ibuf; /* ed command-line buffer */ 8233965Sjdpint ibufsz; /* ed command-line buffer size */ 8333965Sjdpchar *ibufp; /* pointer to ed command-line buffer */ 8433965Sjdp 8533965Sjdp/* global flags */ 8633965Sjdpint des = 0; /* if set, use crypt(3) for i/o */ 8733965Sjdpint garrulous = 0; /* if set, print all error messages */ 8833965Sjdpint isbinary; /* if set, buffer contains ASCII NULs */ 8933965Sjdpint isglobal; /* if set, doing a global command */ 9033965Sjdpint modified; /* if set, buffer modified since last write */ 9133965Sjdpint mutex = 0; /* if set, signals set "sigflags" */ 9233965Sjdpint red = 0; /* if set, restrict shell/directory access */ 9333965Sjdpint scripted = 0; /* if set, suppress diagnostics */ 9433965Sjdpint sigflags = 0; /* if set, signals received while mutex set */ 9533965Sjdpint sigactive = 0; /* if set, signal handlers are enabled */ 9633965Sjdp 9733965Sjdpchar old_filename[MAXPATHLEN + 1] = ""; /* default filename */ 9833965Sjdplong current_addr; /* current address in editor buffer */ 9933965Sjdplong addr_last; /* last address in editor buffer */ 10033965Sjdpint lineno; /* script line number */ 10133965Sjdpchar *prompt; /* command-line prompt */ 10233965Sjdpchar *dps = "*"; /* default command-line prompt */ 10333965Sjdp 10433965Sjdpchar *usage = "usage: %s [-] [-sx] [-p string] [name]\n"; 10533965Sjdp 10633965Sjdp/* ed: line editor */ 10733965Sjdpint 10833965Sjdpmain(argc, argv) 10933965Sjdp int argc; 11033965Sjdp char **argv; 11133965Sjdp{ 11233965Sjdp int c, n; 11333965Sjdp long status = 0; 11433965Sjdp#if __GNUC__ 11533965Sjdp /* Avoid longjmp clobbering */ 11633965Sjdp (void) &argc; 11733965Sjdp (void) &argv; 11833965Sjdp#endif 11933965Sjdp 12033965Sjdp (void)setlocale(LC_ALL, ""); 12133965Sjdp 12233965Sjdp red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r'; 12333965Sjdptop: 12433965Sjdp while ((c = getopt(argc, argv, "p:sx")) != -1) 12533965Sjdp switch(c) { 12633965Sjdp case 'p': /* set prompt */ 12733965Sjdp prompt = optarg; 12833965Sjdp break; 12933965Sjdp case 's': /* run script */ 13033965Sjdp scripted = 1; 13133965Sjdp break; 13233965Sjdp case 'x': /* use crypt */ 13333965Sjdp#ifdef DES 13433965Sjdp des = get_keyword(); 13533965Sjdp#else 13633965Sjdp fprintf(stderr, "crypt unavailable\n?\n"); 13733965Sjdp#endif 13833965Sjdp break; 13933965Sjdp 14033965Sjdp default: 14133965Sjdp fprintf(stderr, usage, argv[0]); 14233965Sjdp exit(1); 14333965Sjdp } 14433965Sjdp argv += optind; 14533965Sjdp argc -= optind; 14633965Sjdp if (argc && **argv == '-') { 14733965Sjdp scripted = 1; 14833965Sjdp if (argc > 1) { 14933965Sjdp optind = 1; 15033965Sjdp goto top; 15133965Sjdp } 15233965Sjdp argv++; 15333965Sjdp argc--; 15433965Sjdp } 15533965Sjdp /* assert: reliable signals! */ 15633965Sjdp#ifdef SIGWINCH 15733965Sjdp handle_winch(SIGWINCH); 15833965Sjdp if (isatty(0)) signal(SIGWINCH, handle_winch); 15933965Sjdp#endif 16033965Sjdp signal(SIGHUP, signal_hup); 16133965Sjdp signal(SIGQUIT, SIG_IGN); 16233965Sjdp signal(SIGINT, signal_int); 16333965Sjdp#ifdef _POSIX_SOURCE 16433965Sjdp if ((status = sigsetjmp(env, 1))) 16533965Sjdp#else 16633965Sjdp if ((status = setjmp(env))) 16733965Sjdp#endif 16833965Sjdp { 16933965Sjdp fputs("\n?\n", stderr); 17033965Sjdp sprintf(errmsg, "interrupt"); 17133965Sjdp } else { 17233965Sjdp init_buffers(); 17333965Sjdp sigactive = 1; /* enable signal handlers */ 17433965Sjdp if (argc && **argv && is_legal_filename(*argv)) { 17533965Sjdp if (read_file(*argv, 0) < 0 && !isatty(0)) 17633965Sjdp quit(2); 17733965Sjdp else if (**argv != '!') 17833965Sjdp if (strlcpy(old_filename, *argv, sizeof(old_filename)) 17933965Sjdp >= sizeof(old_filename)) 18033965Sjdp quit(2); 18133965Sjdp } else if (argc) { 18233965Sjdp fputs("?\n", stderr); 18333965Sjdp if (**argv == '\0') 18433965Sjdp sprintf(errmsg, "invalid filename"); 18533965Sjdp if (!isatty(0)) 18633965Sjdp quit(2); 18733965Sjdp } 18833965Sjdp } 18933965Sjdp for (;;) { 19033965Sjdp if (status < 0 && garrulous) 19133965Sjdp fprintf(stderr, "%s\n", errmsg); 19233965Sjdp if (prompt) { 19333965Sjdp printf("%s", prompt); 19433965Sjdp fflush(stdout); 19533965Sjdp } 19633965Sjdp if ((n = get_tty_line()) < 0) { 19733965Sjdp status = ERR; 19833965Sjdp continue; 19933965Sjdp } else if (n == 0) { 20033965Sjdp if (modified && !scripted) { 20133965Sjdp fputs("?\n", stderr); 20233965Sjdp sprintf(errmsg, "warning: file modified"); 20333965Sjdp if (!isatty(0)) { 20433965Sjdp fprintf(stderr, garrulous ? 20533965Sjdp "script, line %d: %s\n" : 20633965Sjdp "", lineno, errmsg); 20733965Sjdp quit(2); 20833965Sjdp } 20933965Sjdp clearerr(stdin); 21033965Sjdp modified = 0; 21133965Sjdp status = EMOD; 21233965Sjdp continue; 21333965Sjdp } else 21433965Sjdp quit(0); 21533965Sjdp } else if (ibuf[n - 1] != '\n') { 21633965Sjdp /* discard line */ 21733965Sjdp sprintf(errmsg, "unexpected end-of-file"); 21833965Sjdp clearerr(stdin); 21933965Sjdp status = ERR; 22033965Sjdp continue; 22133965Sjdp } 22233965Sjdp isglobal = 0; 22333965Sjdp if ((status = extract_addr_range()) >= 0 && 22433965Sjdp (status = exec_command()) >= 0) 22533965Sjdp if (!status || 22633965Sjdp (status = display_lines(current_addr, current_addr, 22733965Sjdp status)) >= 0) 22833965Sjdp continue; 22933965Sjdp switch (status) { 23033965Sjdp case EOF: 23133965Sjdp quit(0); 23233965Sjdp case EMOD: 23333965Sjdp modified = 0; 23433965Sjdp fputs("?\n", stderr); /* give warning */ 23533965Sjdp sprintf(errmsg, "warning: file modified"); 23633965Sjdp if (!isatty(0)) { 23733965Sjdp fprintf(stderr, garrulous ? 23833965Sjdp "script, line %d: %s\n" : 23933965Sjdp "", lineno, errmsg); 24033965Sjdp quit(2); 24133965Sjdp } 24233965Sjdp break; 24333965Sjdp case FATAL: 24433965Sjdp if (!isatty(0)) 24533965Sjdp fprintf(stderr, garrulous ? 24633965Sjdp "script, line %d: %s\n" : "", 24733965Sjdp lineno, errmsg); 24833965Sjdp else 24933965Sjdp fprintf(stderr, garrulous ? "%s\n" : "", 25033965Sjdp errmsg); 25133965Sjdp quit(3); 25233965Sjdp default: 25333965Sjdp fputs("?\n", stderr); 25433965Sjdp if (!isatty(0)) { 25533965Sjdp fprintf(stderr, garrulous ? 25633965Sjdp "script, line %d: %s\n" : "", 25733965Sjdp lineno, errmsg); 25833965Sjdp quit(2); 25933965Sjdp } 26033965Sjdp break; 26133965Sjdp } 26233965Sjdp } 26333965Sjdp /*NOTREACHED*/ 26433965Sjdp} 26533965Sjdp 26633965Sjdplong first_addr, second_addr, addr_cnt; 26733965Sjdp 26833965Sjdp/* extract_addr_range: get line addresses from the command buffer until an 26933965Sjdp illegal address is seen; return status */ 27033965Sjdpint 27133965Sjdpextract_addr_range() 27233965Sjdp{ 27333965Sjdp long addr; 27433965Sjdp 27533965Sjdp addr_cnt = 0; 27633965Sjdp first_addr = second_addr = current_addr; 27733965Sjdp while ((addr = next_addr()) >= 0) { 27833965Sjdp addr_cnt++; 27933965Sjdp first_addr = second_addr; 28033965Sjdp second_addr = addr; 28133965Sjdp if (*ibufp != ',' && *ibufp != ';') 28233965Sjdp break; 28333965Sjdp else if (*ibufp++ == ';') 28433965Sjdp current_addr = addr; 28533965Sjdp } 28633965Sjdp if ((addr_cnt = min(addr_cnt, 2)) == 1 || second_addr != addr) 28733965Sjdp first_addr = second_addr; 28833965Sjdp return (addr == ERR) ? ERR : 0; 28933965Sjdp} 29033965Sjdp 29133965Sjdp 29233965Sjdp#define SKIP_BLANKS() while (isspace((unsigned char)*ibufp) && *ibufp != '\n') ibufp++ 29333965Sjdp 29433965Sjdp#define MUST_BE_FIRST() \ 29533965Sjdp if (!first) { sprintf(errmsg, "invalid address"); return ERR; } 29633965Sjdp 29733965Sjdp/* next_addr: return the next line address in the command buffer */ 29833965Sjdplong 29933965Sjdpnext_addr() 30033965Sjdp{ 30133965Sjdp char *hd; 30233965Sjdp long addr = current_addr; 30333965Sjdp long n; 30433965Sjdp int first = 1; 30533965Sjdp int c; 30633965Sjdp 30733965Sjdp SKIP_BLANKS(); 30833965Sjdp for (hd = ibufp;; first = 0) 30933965Sjdp switch (c = *ibufp) { 31033965Sjdp case '+': 31133965Sjdp case '\t': 31233965Sjdp case ' ': 31333965Sjdp case '-': 31433965Sjdp case '^': 31533965Sjdp ibufp++; 31633965Sjdp SKIP_BLANKS(); 31733965Sjdp if (isdigit((unsigned char)*ibufp)) { 31833965Sjdp STRTOL(n, ibufp); 31933965Sjdp addr += (c == '-' || c == '^') ? -n : n; 32033965Sjdp } else if (!isspace((unsigned char)c)) 32133965Sjdp addr += (c == '-' || c == '^') ? -1 : 1; 32233965Sjdp break; 32333965Sjdp case '0': case '1': case '2': 32433965Sjdp case '3': case '4': case '5': 32533965Sjdp case '6': case '7': case '8': case '9': 32633965Sjdp MUST_BE_FIRST(); 32733965Sjdp STRTOL(addr, ibufp); 32833965Sjdp break; 32933965Sjdp case '.': 33033965Sjdp case '$': 33133965Sjdp MUST_BE_FIRST(); 33233965Sjdp ibufp++; 33333965Sjdp addr = (c == '.') ? current_addr : addr_last; 33433965Sjdp break; 33533965Sjdp case '/': 33633965Sjdp case '?': 33733965Sjdp MUST_BE_FIRST(); 33833965Sjdp if ((addr = get_matching_node_addr( 33933965Sjdp get_compiled_pattern(), c == '/')) < 0) 34033965Sjdp return ERR; 34133965Sjdp else if (c == *ibufp) 34233965Sjdp ibufp++; 34333965Sjdp break; 34433965Sjdp case '\'': 34533965Sjdp MUST_BE_FIRST(); 34633965Sjdp ibufp++; 34733965Sjdp if ((addr = get_marked_node_addr(*ibufp++)) < 0) 34833965Sjdp return ERR; 34933965Sjdp break; 35033965Sjdp case '%': 35133965Sjdp case ',': 35233965Sjdp case ';': 35333965Sjdp if (first) { 35433965Sjdp ibufp++; 35533965Sjdp addr_cnt++; 35633965Sjdp second_addr = (c == ';') ? current_addr : 1; 35733965Sjdp addr = addr_last; 35833965Sjdp break; 35933965Sjdp } 36033965Sjdp /* FALL THROUGH */ 36133965Sjdp default: 36233965Sjdp if (ibufp == hd) 36333965Sjdp return EOF; 36433965Sjdp else if (addr < 0 || addr_last < addr) { 36533965Sjdp sprintf(errmsg, "invalid address"); 36633965Sjdp return ERR; 36733965Sjdp } else 36833965Sjdp return addr; 36933965Sjdp } 37033965Sjdp /* NOTREACHED */ 37133965Sjdp} 37233965Sjdp 37333965Sjdp 37433965Sjdp#ifdef BACKWARDS 37533965Sjdp/* GET_THIRD_ADDR: get a legal address from the command buffer */ 37633965Sjdp#define GET_THIRD_ADDR(addr) \ 37733965Sjdp{ \ 37833965Sjdp long ol1, ol2; \ 37933965Sjdp\ 38033965Sjdp ol1 = first_addr, ol2 = second_addr; \ 38133965Sjdp if (extract_addr_range() < 0) \ 38233965Sjdp return ERR; \ 38333965Sjdp else if (addr_cnt == 0) { \ 38433965Sjdp sprintf(errmsg, "destination expected"); \ 38533965Sjdp return ERR; \ 38633965Sjdp } else if (second_addr < 0 || addr_last < second_addr) { \ 38733965Sjdp sprintf(errmsg, "invalid address"); \ 38833965Sjdp return ERR; \ 38933965Sjdp } \ 39033965Sjdp addr = second_addr; \ 39133965Sjdp first_addr = ol1, second_addr = ol2; \ 39233965Sjdp} 39333965Sjdp#else /* BACKWARDS */ 39433965Sjdp/* GET_THIRD_ADDR: get a legal address from the command buffer */ 39533965Sjdp#define GET_THIRD_ADDR(addr) \ 39633965Sjdp{ \ 39733965Sjdp long ol1, ol2; \ 39833965Sjdp\ 39933965Sjdp ol1 = first_addr, ol2 = second_addr; \ 40033965Sjdp if (extract_addr_range() < 0) \ 40133965Sjdp return ERR; \ 40233965Sjdp if (second_addr < 0 || addr_last < second_addr) { \ 40333965Sjdp sprintf(errmsg, "invalid address"); \ 40433965Sjdp return ERR; \ 40533965Sjdp } \ 40633965Sjdp addr = second_addr; \ 40733965Sjdp first_addr = ol1, second_addr = ol2; \ 40833965Sjdp} 40933965Sjdp#endif 41033965Sjdp 41133965Sjdp 41233965Sjdp/* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */ 41333965Sjdp#define GET_COMMAND_SUFFIX() { \ 41433965Sjdp int done = 0; \ 41533965Sjdp do { \ 41633965Sjdp switch(*ibufp) { \ 41733965Sjdp case 'p': \ 41833965Sjdp gflag |= GPR, ibufp++; \ 41933965Sjdp break; \ 42033965Sjdp case 'l': \ 42133965Sjdp gflag |= GLS, ibufp++; \ 42233965Sjdp break; \ 42333965Sjdp case 'n': \ 42433965Sjdp gflag |= GNP, ibufp++; \ 42533965Sjdp break; \ 42633965Sjdp default: \ 42733965Sjdp done++; \ 42833965Sjdp } \ 42933965Sjdp } while (!done); \ 43033965Sjdp if (*ibufp++ != '\n') { \ 43133965Sjdp sprintf(errmsg, "invalid command suffix"); \ 43233965Sjdp return ERR; \ 43333965Sjdp } \ 43433965Sjdp} 43533965Sjdp 43633965Sjdp 43733965Sjdp/* sflags */ 43833965Sjdp#define SGG 001 /* complement previous global substitute suffix */ 43933965Sjdp#define SGP 002 /* complement previous print suffix */ 44033965Sjdp#define SGR 004 /* use last regex instead of last pat */ 44133965Sjdp#define SGF 010 /* repeat last substitution */ 44233965Sjdp 44333965Sjdpint patlock = 0; /* if set, pattern not freed by get_compiled_pattern() */ 44433965Sjdp 44533965Sjdplong rows = 22; /* scroll length: ws_row - 2 */ 44633965Sjdp 44733965Sjdp/* exec_command: execute the next command in command buffer; return print 44833965Sjdp request, if any */ 44933965Sjdpint 45033965Sjdpexec_command() 45133965Sjdp{ 45233965Sjdp extern long u_current_addr; 45333965Sjdp extern long u_addr_last; 45433965Sjdp 45533965Sjdp static pattern_t *pat = NULL; 45633965Sjdp static int sgflag = 0; 45733965Sjdp static long sgnum = 0; 45833965Sjdp 45933965Sjdp pattern_t *tpat; 46033965Sjdp char *fnp; 46133965Sjdp int gflag = 0; 46233965Sjdp int sflags = 0; 46333965Sjdp long addr = 0; 46433965Sjdp int n = 0; 46533965Sjdp int c; 46633965Sjdp 46733965Sjdp SKIP_BLANKS(); 46833965Sjdp switch(c = *ibufp++) { 46933965Sjdp case 'a': 47033965Sjdp GET_COMMAND_SUFFIX(); 47133965Sjdp if (!isglobal) clear_undo_stack(); 47233965Sjdp if (append_lines(second_addr) < 0) 47333965Sjdp return ERR; 47433965Sjdp break; 47533965Sjdp case 'c': 47633965Sjdp if (check_addr_range(current_addr, current_addr) < 0) 47733965Sjdp return ERR; 47833965Sjdp GET_COMMAND_SUFFIX(); 47933965Sjdp if (!isglobal) clear_undo_stack(); 48033965Sjdp if (delete_lines(first_addr, second_addr) < 0 || 48133965Sjdp append_lines(current_addr) < 0) 48233965Sjdp return ERR; 48333965Sjdp break; 48433965Sjdp case 'd': 48533965Sjdp if (check_addr_range(current_addr, current_addr) < 0) 48633965Sjdp return ERR; 48733965Sjdp GET_COMMAND_SUFFIX(); 48833965Sjdp if (!isglobal) clear_undo_stack(); 48933965Sjdp if (delete_lines(first_addr, second_addr) < 0) 49033965Sjdp return ERR; 49133965Sjdp else if ((addr = INC_MOD(current_addr, addr_last)) != 0) 49233965Sjdp current_addr = addr; 49333965Sjdp break; 49433965Sjdp case 'e': 49533965Sjdp if (modified && !scripted) 49633965Sjdp return EMOD; 49733965Sjdp /* fall through */ 49833965Sjdp case 'E': 49933965Sjdp if (addr_cnt > 0) { 50033965Sjdp sprintf(errmsg, "unexpected address"); 50133965Sjdp return ERR; 50233965Sjdp } else if (!isspace((unsigned char)*ibufp)) { 50333965Sjdp sprintf(errmsg, "unexpected command suffix"); 50433965Sjdp return ERR; 50533965Sjdp } else if ((fnp = get_filename()) == NULL) 50633965Sjdp return ERR; 50733965Sjdp GET_COMMAND_SUFFIX(); 50833965Sjdp if (delete_lines(1, addr_last) < 0) 50933965Sjdp return ERR; 51033965Sjdp clear_undo_stack(); 51133965Sjdp if (close_sbuf() < 0) 51233965Sjdp return ERR; 51333965Sjdp else if (open_sbuf() < 0) 51433965Sjdp return FATAL; 51533965Sjdp if (*fnp && *fnp != '!') strcpy(old_filename, fnp); 51633965Sjdp#ifdef BACKWARDS 51733965Sjdp if (*fnp == '\0' && *old_filename == '\0') { 51833965Sjdp sprintf(errmsg, "no current filename"); 51933965Sjdp return ERR; 52033965Sjdp } 52133965Sjdp#endif 52233965Sjdp if (read_file(*fnp ? fnp : old_filename, 0) < 0) 52333965Sjdp return ERR; 52433965Sjdp clear_undo_stack(); 52533965Sjdp modified = 0; 52633965Sjdp u_current_addr = u_addr_last = -1; 52733965Sjdp break; 52833965Sjdp case 'f': 52933965Sjdp if (addr_cnt > 0) { 53033965Sjdp sprintf(errmsg, "unexpected address"); 53133965Sjdp return ERR; 53233965Sjdp } else if (!isspace((unsigned char)*ibufp)) { 53333965Sjdp sprintf(errmsg, "unexpected command suffix"); 53433965Sjdp return ERR; 53533965Sjdp } else if ((fnp = get_filename()) == NULL) 53633965Sjdp return ERR; 53733965Sjdp else if (*fnp == '!') { 53833965Sjdp sprintf(errmsg, "invalid redirection"); 53933965Sjdp return ERR; 54033965Sjdp } 54133965Sjdp GET_COMMAND_SUFFIX(); 54233965Sjdp if (*fnp) strcpy(old_filename, fnp); 54333965Sjdp printf("%s\n", strip_escapes(old_filename)); 54433965Sjdp break; 54533965Sjdp case 'g': 54633965Sjdp case 'v': 54733965Sjdp case 'G': 54833965Sjdp case 'V': 54933965Sjdp if (isglobal) { 55033965Sjdp sprintf(errmsg, "cannot nest global commands"); 55133965Sjdp return ERR; 55233965Sjdp } else if (check_addr_range(1, addr_last) < 0) 55333965Sjdp return ERR; 55433965Sjdp else if (build_active_list(c == 'g' || c == 'G') < 0) 55533965Sjdp return ERR; 55633965Sjdp else if ((n = (c == 'G' || c == 'V'))) 55733965Sjdp GET_COMMAND_SUFFIX(); 55833965Sjdp isglobal++; 55933965Sjdp if (exec_global(n, gflag) < 0) 56033965Sjdp return ERR; 56133965Sjdp break; 56233965Sjdp case 'h': 56333965Sjdp if (addr_cnt > 0) { 56433965Sjdp sprintf(errmsg, "unexpected address"); 56533965Sjdp return ERR; 56633965Sjdp } 56733965Sjdp GET_COMMAND_SUFFIX(); 56833965Sjdp if (*errmsg) fprintf(stderr, "%s\n", errmsg); 56933965Sjdp break; 57033965Sjdp case 'H': 57133965Sjdp if (addr_cnt > 0) { 57233965Sjdp sprintf(errmsg, "unexpected address"); 57333965Sjdp return ERR; 57433965Sjdp } 57533965Sjdp GET_COMMAND_SUFFIX(); 57633965Sjdp if ((garrulous = 1 - garrulous) && *errmsg) 57733965Sjdp fprintf(stderr, "%s\n", errmsg); 57833965Sjdp break; 57933965Sjdp case 'i': 58033965Sjdp if (second_addr == 0) { 58133965Sjdp sprintf(errmsg, "invalid address"); 58233965Sjdp return ERR; 58333965Sjdp } 58433965Sjdp GET_COMMAND_SUFFIX(); 58533965Sjdp if (!isglobal) clear_undo_stack(); 58633965Sjdp if (append_lines(second_addr - 1) < 0) 58733965Sjdp return ERR; 58833965Sjdp break; 58933965Sjdp case 'j': 59033965Sjdp if (check_addr_range(current_addr, current_addr + 1) < 0) 59133965Sjdp return ERR; 59233965Sjdp GET_COMMAND_SUFFIX(); 59333965Sjdp if (!isglobal) clear_undo_stack(); 59433965Sjdp if (first_addr != second_addr && 59533965Sjdp join_lines(first_addr, second_addr) < 0) 59633965Sjdp return ERR; 59733965Sjdp break; 59833965Sjdp case 'k': 59933965Sjdp c = *ibufp++; 60033965Sjdp if (second_addr == 0) { 60133965Sjdp sprintf(errmsg, "invalid address"); 60233965Sjdp return ERR; 60333965Sjdp } 60433965Sjdp GET_COMMAND_SUFFIX(); 60533965Sjdp if (mark_line_node(get_addressed_line_node(second_addr), c) < 0) 60633965Sjdp return ERR; 60733965Sjdp break; 60833965Sjdp case 'l': 60933965Sjdp if (check_addr_range(current_addr, current_addr) < 0) 61033965Sjdp return ERR; 61133965Sjdp GET_COMMAND_SUFFIX(); 61233965Sjdp if (display_lines(first_addr, second_addr, gflag | GLS) < 0) 61333965Sjdp return ERR; 61433965Sjdp gflag = 0; 61533965Sjdp break; 61633965Sjdp case 'm': 61733965Sjdp if (check_addr_range(current_addr, current_addr) < 0) 61833965Sjdp return ERR; 61933965Sjdp GET_THIRD_ADDR(addr); 62033965Sjdp if (first_addr <= addr && addr < second_addr) { 62133965Sjdp sprintf(errmsg, "invalid destination"); 62233965Sjdp return ERR; 62333965Sjdp } 62433965Sjdp GET_COMMAND_SUFFIX(); 62533965Sjdp if (!isglobal) clear_undo_stack(); 62633965Sjdp if (move_lines(addr) < 0) 62733965Sjdp return ERR; 62833965Sjdp break; 62933965Sjdp case 'n': 63033965Sjdp if (check_addr_range(current_addr, current_addr) < 0) 63133965Sjdp return ERR; 63233965Sjdp GET_COMMAND_SUFFIX(); 63333965Sjdp if (display_lines(first_addr, second_addr, gflag | GNP) < 0) 63433965Sjdp return ERR; 63533965Sjdp gflag = 0; 63633965Sjdp break; 63733965Sjdp case 'p': 63833965Sjdp if (check_addr_range(current_addr, current_addr) < 0) 63933965Sjdp return ERR; 64033965Sjdp GET_COMMAND_SUFFIX(); 64133965Sjdp if (display_lines(first_addr, second_addr, gflag | GPR) < 0) 64233965Sjdp return ERR; 64333965Sjdp gflag = 0; 64433965Sjdp break; 64533965Sjdp case 'P': 64633965Sjdp if (addr_cnt > 0) { 64733965Sjdp sprintf(errmsg, "unexpected address"); 64833965Sjdp return ERR; 64933965Sjdp } 65033965Sjdp GET_COMMAND_SUFFIX(); 65133965Sjdp prompt = prompt ? NULL : optarg ? optarg : dps; 65233965Sjdp break; 65333965Sjdp case 'q': 65433965Sjdp case 'Q': 65533965Sjdp if (addr_cnt > 0) { 65633965Sjdp sprintf(errmsg, "unexpected address"); 65733965Sjdp return ERR; 65833965Sjdp } 65933965Sjdp GET_COMMAND_SUFFIX(); 66033965Sjdp gflag = (modified && !scripted && c == 'q') ? EMOD : EOF; 66133965Sjdp break; 66233965Sjdp case 'r': 66333965Sjdp if (!isspace((unsigned char)*ibufp)) { 66433965Sjdp sprintf(errmsg, "unexpected command suffix"); 66533965Sjdp return ERR; 66633965Sjdp } else if (addr_cnt == 0) 66733965Sjdp second_addr = addr_last; 66833965Sjdp if ((fnp = get_filename()) == NULL) 66933965Sjdp return ERR; 67033965Sjdp GET_COMMAND_SUFFIX(); 67133965Sjdp if (!isglobal) clear_undo_stack(); 67233965Sjdp if (*old_filename == '\0' && *fnp != '!') 67333965Sjdp strcpy(old_filename, fnp); 67433965Sjdp#ifdef BACKWARDS 67533965Sjdp if (*fnp == '\0' && *old_filename == '\0') { 67633965Sjdp sprintf(errmsg, "no current filename"); 67733965Sjdp return ERR; 67833965Sjdp } 67933965Sjdp#endif 68033965Sjdp if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0) 68133965Sjdp return ERR; 68233965Sjdp else if (addr && addr != addr_last) 68333965Sjdp modified = 1; 68433965Sjdp break; 68533965Sjdp case 's': 68633965Sjdp do { 68733965Sjdp switch(*ibufp) { 68833965Sjdp case '\n': 68933965Sjdp sflags |=SGF; 69033965Sjdp break; 69133965Sjdp case 'g': 69233965Sjdp sflags |= SGG; 69333965Sjdp ibufp++; 69433965Sjdp break; 69533965Sjdp case 'p': 69633965Sjdp sflags |= SGP; 69733965Sjdp ibufp++; 69833965Sjdp break; 69933965Sjdp case 'r': 70033965Sjdp sflags |= SGR; 70133965Sjdp ibufp++; 70233965Sjdp break; 70333965Sjdp case '0': case '1': case '2': case '3': case '4': 70433965Sjdp case '5': case '6': case '7': case '8': case '9': 70533965Sjdp STRTOL(sgnum, ibufp); 70633965Sjdp sflags |= SGF; 70733965Sjdp sgflag &= ~GSG; /* override GSG */ 70833965Sjdp break; 70933965Sjdp default: 71033965Sjdp if (sflags) { 71133965Sjdp sprintf(errmsg, "invalid command suffix"); 71233965Sjdp return ERR; 71333965Sjdp } 71433965Sjdp } 71533965Sjdp } while (sflags && *ibufp != '\n'); 71633965Sjdp if (sflags && !pat) { 71733965Sjdp sprintf(errmsg, "no previous substitution"); 71833965Sjdp return ERR; 71933965Sjdp } else if (sflags & SGG) 72033965Sjdp sgnum = 0; /* override numeric arg */ 72133965Sjdp if (*ibufp != '\n' && *(ibufp + 1) == '\n') { 72233965Sjdp sprintf(errmsg, "invalid pattern delimiter"); 72333965Sjdp return ERR; 72433965Sjdp } 72533965Sjdp tpat = pat; 72633965Sjdp SPL1(); 72733965Sjdp if ((!sflags || (sflags & SGR)) && 72833965Sjdp (tpat = get_compiled_pattern()) == NULL) { 72933965Sjdp SPL0(); 73033965Sjdp return ERR; 73133965Sjdp } else if (tpat != pat) { 73233965Sjdp if (pat) { 73333965Sjdp regfree(pat); 73433965Sjdp free(pat); 73533965Sjdp } 73633965Sjdp pat = tpat; 73733965Sjdp patlock = 1; /* reserve pattern */ 73833965Sjdp } 73933965Sjdp SPL0(); 74033965Sjdp if (!sflags && extract_subst_tail(&sgflag, &sgnum) < 0) 74133965Sjdp return ERR; 74233965Sjdp else if (isglobal) 74333965Sjdp sgflag |= GLB; 74433965Sjdp else 74533965Sjdp sgflag &= ~GLB; 74633965Sjdp if (sflags & SGG) 74733965Sjdp sgflag ^= GSG; 74833965Sjdp if (sflags & SGP) 74933965Sjdp sgflag ^= GPR, sgflag &= ~(GLS | GNP); 75033965Sjdp do { 75133965Sjdp switch(*ibufp) { 75233965Sjdp case 'p': 75333965Sjdp sgflag |= GPR, ibufp++; 75433965Sjdp break; 75533965Sjdp case 'l': 75633965Sjdp sgflag |= GLS, ibufp++; 75733965Sjdp break; 75833965Sjdp case 'n': 75933965Sjdp sgflag |= GNP, ibufp++; 76033965Sjdp break; 76133965Sjdp default: 76233965Sjdp n++; 76333965Sjdp } 76433965Sjdp } while (!n); 76533965Sjdp if (check_addr_range(current_addr, current_addr) < 0) 76633965Sjdp return ERR; 76733965Sjdp GET_COMMAND_SUFFIX(); 76833965Sjdp if (!isglobal) clear_undo_stack(); 76933965Sjdp if (search_and_replace(pat, sgflag, sgnum) < 0) 77033965Sjdp return ERR; 77133965Sjdp break; 77233965Sjdp case 't': 77333965Sjdp if (check_addr_range(current_addr, current_addr) < 0) 77433965Sjdp return ERR; 77533965Sjdp GET_THIRD_ADDR(addr); 77633965Sjdp GET_COMMAND_SUFFIX(); 77733965Sjdp if (!isglobal) clear_undo_stack(); 77833965Sjdp if (copy_lines(addr) < 0) 77933965Sjdp return ERR; 78033965Sjdp break; 78133965Sjdp case 'u': 78233965Sjdp if (addr_cnt > 0) { 78333965Sjdp sprintf(errmsg, "unexpected address"); 78433965Sjdp return ERR; 78533965Sjdp } 78633965Sjdp GET_COMMAND_SUFFIX(); 78733965Sjdp if (pop_undo_stack() < 0) 78833965Sjdp return ERR; 78933965Sjdp break; 79033965Sjdp case 'w': 79133965Sjdp case 'W': 79233965Sjdp if ((n = *ibufp) == 'q' || n == 'Q') { 79333965Sjdp gflag = EOF; 79433965Sjdp ibufp++; 79533965Sjdp } 79633965Sjdp if (!isspace((unsigned char)*ibufp)) { 79733965Sjdp sprintf(errmsg, "unexpected command suffix"); 79833965Sjdp return ERR; 79933965Sjdp } else if ((fnp = get_filename()) == NULL) 80033965Sjdp return ERR; 80133965Sjdp if (addr_cnt == 0 && !addr_last) 80233965Sjdp first_addr = second_addr = 0; 80333965Sjdp else if (check_addr_range(1, addr_last) < 0) 80433965Sjdp return ERR; 80533965Sjdp GET_COMMAND_SUFFIX(); 80633965Sjdp if (*old_filename == '\0' && *fnp != '!') 80733965Sjdp strcpy(old_filename, fnp); 80833965Sjdp#ifdef BACKWARDS 80933965Sjdp if (*fnp == '\0' && *old_filename == '\0') { 81033965Sjdp sprintf(errmsg, "no current filename"); 81133965Sjdp return ERR; 81233965Sjdp } 81333965Sjdp#endif 81433965Sjdp if ((addr = write_file(*fnp ? fnp : old_filename, 81533965Sjdp (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0) 81633965Sjdp return ERR; 81733965Sjdp else if (addr == addr_last) 81833965Sjdp modified = 0; 81933965Sjdp else if (modified && !scripted && n == 'q') 82033965Sjdp gflag = EMOD; 82133965Sjdp break; 82233965Sjdp case 'x': 82333965Sjdp if (addr_cnt > 0) { 82433965Sjdp sprintf(errmsg, "unexpected address"); 82533965Sjdp return ERR; 82633965Sjdp } 82733965Sjdp GET_COMMAND_SUFFIX(); 82833965Sjdp#ifdef DES 82933965Sjdp des = get_keyword(); 83033965Sjdp#else 83133965Sjdp sprintf(errmsg, "crypt unavailable"); 83233965Sjdp return ERR; 83333965Sjdp#endif 83433965Sjdp break; 83533965Sjdp case 'z': 83633965Sjdp#ifdef BACKWARDS 83733965Sjdp if (check_addr_range(first_addr = 1, current_addr + 1) < 0) 83833965Sjdp#else 83933965Sjdp if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0) 84033965Sjdp#endif 84133965Sjdp return ERR; 84233965Sjdp else if ('0' < *ibufp && *ibufp <= '9') 84333965Sjdp STRTOL(rows, ibufp); 84433965Sjdp GET_COMMAND_SUFFIX(); 84533965Sjdp if (display_lines(second_addr, min(addr_last, 84633965Sjdp second_addr + rows), gflag) < 0) 84733965Sjdp return ERR; 84833965Sjdp gflag = 0; 84933965Sjdp break; 85033965Sjdp case '=': 85133965Sjdp GET_COMMAND_SUFFIX(); 85233965Sjdp printf("%ld\n", addr_cnt ? second_addr : addr_last); 85333965Sjdp break; 85433965Sjdp case '!': 85533965Sjdp if (addr_cnt > 0) { 85633965Sjdp sprintf(errmsg, "unexpected address"); 85733965Sjdp return ERR; 85833965Sjdp } else if ((sflags = get_shell_command()) < 0) 85933965Sjdp return ERR; 86033965Sjdp GET_COMMAND_SUFFIX(); 86133965Sjdp if (sflags) printf("%s\n", shcmd + 1); 86233965Sjdp system(shcmd + 1); 86333965Sjdp if (!scripted) printf("!\n"); 86433965Sjdp break; 86533965Sjdp case '\n': 86633965Sjdp#ifdef BACKWARDS 86733965Sjdp if (check_addr_range(first_addr = 1, current_addr + 1) < 0 86833965Sjdp#else 86933965Sjdp if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0 87033965Sjdp#endif 87133965Sjdp || display_lines(second_addr, second_addr, 0) < 0) 87233965Sjdp return ERR; 87333965Sjdp break; 87433965Sjdp default: 87533965Sjdp sprintf(errmsg, "unknown command"); 87633965Sjdp return ERR; 87733965Sjdp } 87833965Sjdp return gflag; 87933965Sjdp} 88033965Sjdp 88133965Sjdp 88233965Sjdp/* check_addr_range: return status of address range check */ 88333965Sjdpint 88433965Sjdpcheck_addr_range(n, m) 88533965Sjdp long n, m; 88633965Sjdp{ 88733965Sjdp if (addr_cnt == 0) { 88833965Sjdp first_addr = n; 88933965Sjdp second_addr = m; 89033965Sjdp } 89133965Sjdp if (first_addr > second_addr || 1 > first_addr || 89233965Sjdp second_addr > addr_last) { 89333965Sjdp sprintf(errmsg, "invalid address"); 89433965Sjdp return ERR; 89533965Sjdp } 89633965Sjdp return 0; 89733965Sjdp} 89833965Sjdp 89933965Sjdp 90033965Sjdp/* get_matching_node_addr: return the address of the next line matching a 90133965Sjdp pattern in a given direction. wrap around begin/end of editor buffer if 90233965Sjdp necessary */ 90333965Sjdplong 90433965Sjdpget_matching_node_addr(pat, dir) 90533965Sjdp pattern_t *pat; 90633965Sjdp int dir; 90733965Sjdp{ 90833965Sjdp char *s; 90933965Sjdp long n = current_addr; 91033965Sjdp line_t *lp; 91133965Sjdp 91233965Sjdp if (!pat) return ERR; 91333965Sjdp do { 91433965Sjdp if ((n = dir ? INC_MOD(n, addr_last) : DEC_MOD(n, addr_last))) { 91533965Sjdp lp = get_addressed_line_node(n); 91633965Sjdp if ((s = get_sbuf_line(lp)) == NULL) 91733965Sjdp return ERR; 91833965Sjdp if (isbinary) 91933965Sjdp NUL_TO_NEWLINE(s, lp->len); 92033965Sjdp if (!regexec(pat, s, 0, NULL, 0)) 92133965Sjdp return n; 92233965Sjdp } 92333965Sjdp } while (n != current_addr); 92433965Sjdp sprintf(errmsg, "no match"); 92533965Sjdp return ERR; 92633965Sjdp} 92733965Sjdp 92833965Sjdp 92933965Sjdp/* get_filename: return pointer to copy of filename in the command buffer */ 93033965Sjdpchar * 93133965Sjdpget_filename() 93233965Sjdp{ 93333965Sjdp static char *file = NULL; 93433965Sjdp static int filesz = 0; 93533965Sjdp 93633965Sjdp int n; 93733965Sjdp 93833965Sjdp if (*ibufp != '\n') { 93933965Sjdp SKIP_BLANKS(); 94033965Sjdp if (*ibufp == '\n') { 94133965Sjdp sprintf(errmsg, "invalid filename"); 94233965Sjdp return NULL; 94333965Sjdp } else if ((ibufp = get_extended_line(&n, 1)) == NULL) 94433965Sjdp return NULL; 94533965Sjdp else if (*ibufp == '!') { 94633965Sjdp ibufp++; 94733965Sjdp if ((n = get_shell_command()) < 0) 94833965Sjdp return NULL; 94933965Sjdp if (n) printf("%s\n", shcmd + 1); 95033965Sjdp return shcmd; 95133965Sjdp } else if (n - 1 > MAXPATHLEN) { 95233965Sjdp sprintf(errmsg, "filename too long"); 95333965Sjdp return NULL; 95433965Sjdp } 95533965Sjdp } 95633965Sjdp#ifndef BACKWARDS 95733965Sjdp else if (*old_filename == '\0') { 95833965Sjdp sprintf(errmsg, "no current filename"); 95933965Sjdp return NULL; 96033965Sjdp } 96133965Sjdp#endif 96233965Sjdp REALLOC(file, filesz, MAXPATHLEN + 1, NULL); 96333965Sjdp for (n = 0; *ibufp != '\n';) 96433965Sjdp file[n++] = *ibufp++; 96533965Sjdp file[n] = '\0'; 96633965Sjdp return is_legal_filename(file) ? file : NULL; 96733965Sjdp} 96833965Sjdp 96933965Sjdp 97033965Sjdp/* get_shell_command: read a shell command from stdin; return substitution 97133965Sjdp status */ 97233965Sjdpint 97333965Sjdpget_shell_command() 97433965Sjdp{ 97533965Sjdp static char *buf = NULL; 97633965Sjdp static int n = 0; 97733965Sjdp 97833965Sjdp char *s; /* substitution char pointer */ 97933965Sjdp int i = 0; 98033965Sjdp int j = 0; 98133965Sjdp 98233965Sjdp if (red) { 98333965Sjdp sprintf(errmsg, "shell access restricted"); 98433965Sjdp return ERR; 98533965Sjdp } else if ((s = ibufp = get_extended_line(&j, 1)) == NULL) 98633965Sjdp return ERR; 98733965Sjdp REALLOC(buf, n, j + 1, ERR); 98833965Sjdp buf[i++] = '!'; /* prefix command w/ bang */ 98933965Sjdp while (*ibufp != '\n') 99033965Sjdp switch (*ibufp) { 99133965Sjdp default: 99233965Sjdp REALLOC(buf, n, i + 2, ERR); 99333965Sjdp buf[i++] = *ibufp; 99433965Sjdp if (*ibufp++ == '\\') 99533965Sjdp buf[i++] = *ibufp++; 99633965Sjdp break; 99733965Sjdp case '!': 99833965Sjdp if (s != ibufp) { 99933965Sjdp REALLOC(buf, n, i + 1, ERR); 100033965Sjdp buf[i++] = *ibufp++; 100133965Sjdp } 100233965Sjdp#ifdef BACKWARDS 100333965Sjdp else if (shcmd == NULL || *(shcmd + 1) == '\0') 100433965Sjdp#else 100533965Sjdp else if (shcmd == NULL) 100633965Sjdp#endif 100733965Sjdp { 100833965Sjdp sprintf(errmsg, "no previous command"); 100933965Sjdp return ERR; 101033965Sjdp } else { 101133965Sjdp REALLOC(buf, n, i + shcmdi, ERR); 101233965Sjdp for (s = shcmd + 1; s < shcmd + shcmdi;) 101333965Sjdp buf[i++] = *s++; 101433965Sjdp s = ibufp++; 101533965Sjdp } 101633965Sjdp break; 101733965Sjdp case '%': 101833965Sjdp if (*old_filename == '\0') { 101933965Sjdp sprintf(errmsg, "no current filename"); 102033965Sjdp return ERR; 102133965Sjdp } 102233965Sjdp j = strlen(s = strip_escapes(old_filename)); 102333965Sjdp REALLOC(buf, n, i + j, ERR); 102433965Sjdp while (j--) 102533965Sjdp buf[i++] = *s++; 102633965Sjdp s = ibufp++; 102733965Sjdp break; 102833965Sjdp } 102933965Sjdp REALLOC(shcmd, shcmdsz, i + 1, ERR); 103033965Sjdp memcpy(shcmd, buf, i); 103133965Sjdp shcmd[shcmdi = i] = '\0'; 103233965Sjdp return *s == '!' || *s == '%'; 103333965Sjdp} 103433965Sjdp 103533965Sjdp 103633965Sjdp/* append_lines: insert text from stdin to after line n; stop when either a 103733965Sjdp single period is read or EOF; return status */ 103833965Sjdpint 103933965Sjdpappend_lines(n) 104033965Sjdp long n; 104133965Sjdp{ 104233965Sjdp int l; 104333965Sjdp char *lp = ibuf; 104433965Sjdp char *eot; 104533965Sjdp undo_t *up = NULL; 104633965Sjdp 104733965Sjdp for (current_addr = n;;) { 104833965Sjdp if (!isglobal) { 104933965Sjdp if ((l = get_tty_line()) < 0) 105033965Sjdp return ERR; 105133965Sjdp else if (l == 0 || ibuf[l - 1] != '\n') { 105233965Sjdp clearerr(stdin); 105333965Sjdp return l ? EOF : 0; 105433965Sjdp } 105533965Sjdp lp = ibuf; 105633965Sjdp } else if (*(lp = ibufp) == '\0') 105733965Sjdp return 0; 105833965Sjdp else { 105933965Sjdp while (*ibufp++ != '\n') 106033965Sjdp ; 106133965Sjdp l = ibufp - lp; 106233965Sjdp } 106333965Sjdp if (l == 2 && lp[0] == '.' && lp[1] == '\n') { 106433965Sjdp return 0; 106533965Sjdp } 106633965Sjdp eot = lp + l; 106733965Sjdp SPL1(); 106833965Sjdp do { 106933965Sjdp if ((lp = put_sbuf_line(lp)) == NULL) { 107033965Sjdp SPL0(); 107133965Sjdp return ERR; 107233965Sjdp } else if (up) 107333965Sjdp up->t = get_addressed_line_node(current_addr); 107433965Sjdp else if ((up = push_undo_stack(UADD, current_addr, 107533965Sjdp current_addr)) == NULL) { 107633965Sjdp SPL0(); 107733965Sjdp return ERR; 107833965Sjdp } 107933965Sjdp } while (lp != eot); 108033965Sjdp modified = 1; 108133965Sjdp SPL0(); 108233965Sjdp } 108333965Sjdp /* NOTREACHED */ 108433965Sjdp} 108533965Sjdp 108633965Sjdp 108733965Sjdp/* join_lines: replace a range of lines with the joined text of those lines */ 108833965Sjdpint 108933965Sjdpjoin_lines(from, to) 109033965Sjdp long from; 109133965Sjdp long to; 109233965Sjdp{ 109333965Sjdp static char *buf = NULL; 109433965Sjdp static int n; 109533965Sjdp 109633965Sjdp char *s; 109733965Sjdp int size = 0; 109833965Sjdp line_t *bp, *ep; 109933965Sjdp 110033965Sjdp ep = get_addressed_line_node(INC_MOD(to, addr_last)); 110133965Sjdp bp = get_addressed_line_node(from); 110233965Sjdp for (; bp != ep; bp = bp->q_forw) { 110333965Sjdp if ((s = get_sbuf_line(bp)) == NULL) 110433965Sjdp return ERR; 110533965Sjdp REALLOC(buf, n, size + bp->len, ERR); 110633965Sjdp memcpy(buf + size, s, bp->len); 110733965Sjdp size += bp->len; 110833965Sjdp } 110933965Sjdp REALLOC(buf, n, size + 2, ERR); 111033965Sjdp memcpy(buf + size, "\n", 2); 111133965Sjdp if (delete_lines(from, to) < 0) 111233965Sjdp return ERR; 111333965Sjdp current_addr = from - 1; 111433965Sjdp SPL1(); 111533965Sjdp if (put_sbuf_line(buf) == NULL || 111633965Sjdp push_undo_stack(UADD, current_addr, current_addr) == NULL) { 111733965Sjdp SPL0(); 111833965Sjdp return ERR; 111933965Sjdp } 112033965Sjdp modified = 1; 112133965Sjdp SPL0(); 112233965Sjdp return 0; 112333965Sjdp} 112433965Sjdp 112533965Sjdp 112633965Sjdp/* move_lines: move a range of lines */ 112733965Sjdpint 112833965Sjdpmove_lines(addr) 112933965Sjdp long addr; 113033965Sjdp{ 113133965Sjdp line_t *b1, *a1, *b2, *a2; 113233965Sjdp long n = INC_MOD(second_addr, addr_last); 113333965Sjdp long p = first_addr - 1; 113433965Sjdp int done = (addr == first_addr - 1 || addr == second_addr); 113533965Sjdp 113633965Sjdp SPL1(); 113733965Sjdp if (done) { 113833965Sjdp a2 = get_addressed_line_node(n); 113933965Sjdp b2 = get_addressed_line_node(p); 114033965Sjdp current_addr = second_addr; 114133965Sjdp } else if (push_undo_stack(UMOV, p, n) == NULL || 114233965Sjdp push_undo_stack(UMOV, addr, INC_MOD(addr, addr_last)) == NULL) { 114333965Sjdp SPL0(); 114433965Sjdp return ERR; 114533965Sjdp } else { 114633965Sjdp a1 = get_addressed_line_node(n); 114733965Sjdp if (addr < first_addr) { 114833965Sjdp b1 = get_addressed_line_node(p); 114933965Sjdp b2 = get_addressed_line_node(addr); 115033965Sjdp /* this get_addressed_line_node last! */ 115133965Sjdp } else { 115233965Sjdp b2 = get_addressed_line_node(addr); 115333965Sjdp b1 = get_addressed_line_node(p); 115433965Sjdp /* this get_addressed_line_node last! */ 115533965Sjdp } 115633965Sjdp a2 = b2->q_forw; 115733965Sjdp REQUE(b2, b1->q_forw); 115833965Sjdp REQUE(a1->q_back, a2); 115933965Sjdp REQUE(b1, a1); 116033965Sjdp current_addr = addr + ((addr < first_addr) ? 116133965Sjdp second_addr - first_addr + 1 : 0); 116233965Sjdp } 116333965Sjdp if (isglobal) 116433965Sjdp unset_active_nodes(b2->q_forw, a2); 116533965Sjdp modified = 1; 116633965Sjdp SPL0(); 116733965Sjdp return 0; 116833965Sjdp} 116933965Sjdp 117033965Sjdp 117133965Sjdp/* copy_lines: copy a range of lines; return status */ 117233965Sjdpint 117333965Sjdpcopy_lines(addr) 117433965Sjdp long addr; 117533965Sjdp{ 117633965Sjdp line_t *lp, *np = get_addressed_line_node(first_addr); 117733965Sjdp undo_t *up = NULL; 117833965Sjdp long n = second_addr - first_addr + 1; 117933965Sjdp long m = 0; 118033965Sjdp 118133965Sjdp current_addr = addr; 118233965Sjdp if (first_addr <= addr && addr < second_addr) { 118333965Sjdp n = addr - first_addr + 1; 118433965Sjdp m = second_addr - addr; 118533965Sjdp } 118633965Sjdp for (; n > 0; n=m, m=0, np = get_addressed_line_node(current_addr + 1)) 118733965Sjdp for (; n-- > 0; np = np->q_forw) { 118833965Sjdp SPL1(); 118933965Sjdp if ((lp = dup_line_node(np)) == NULL) { 119033965Sjdp SPL0(); 119133965Sjdp return ERR; 119233965Sjdp } 119333965Sjdp add_line_node(lp); 119433965Sjdp if (up) 119533965Sjdp up->t = lp; 119633965Sjdp else if ((up = push_undo_stack(UADD, current_addr, 119733965Sjdp current_addr)) == NULL) { 119833965Sjdp SPL0(); 119933965Sjdp return ERR; 120033965Sjdp } 120133965Sjdp modified = 1; 120233965Sjdp SPL0(); 120333965Sjdp } 120433965Sjdp return 0; 120533965Sjdp} 120633965Sjdp 120733965Sjdp 120833965Sjdp/* delete_lines: delete a range of lines */ 120933965Sjdpint 121033965Sjdpdelete_lines(from, to) 121133965Sjdp long from, to; 121233965Sjdp{ 121333965Sjdp line_t *n, *p; 1214 1215 SPL1(); 1216 if (push_undo_stack(UDEL, from, to) == NULL) { 1217 SPL0(); 1218 return ERR; 1219 } 1220 n = get_addressed_line_node(INC_MOD(to, addr_last)); 1221 p = get_addressed_line_node(from - 1); 1222 /* this get_addressed_line_node last! */ 1223 if (isglobal) 1224 unset_active_nodes(p->q_forw, n); 1225 REQUE(p, n); 1226 addr_last -= to - from + 1; 1227 current_addr = from - 1; 1228 modified = 1; 1229 SPL0(); 1230 return 0; 1231} 1232 1233 1234/* display_lines: print a range of lines to stdout */ 1235int 1236display_lines(from, to, gflag) 1237 long from; 1238 long to; 1239 int gflag; 1240{ 1241 line_t *bp; 1242 line_t *ep; 1243 char *s; 1244 1245 if (!from) { 1246 sprintf(errmsg, "invalid address"); 1247 return ERR; 1248 } 1249 ep = get_addressed_line_node(INC_MOD(to, addr_last)); 1250 bp = get_addressed_line_node(from); 1251 for (; bp != ep; bp = bp->q_forw) { 1252 if ((s = get_sbuf_line(bp)) == NULL) 1253 return ERR; 1254 if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0) 1255 return ERR; 1256 } 1257 return 0; 1258} 1259 1260 1261#define MAXMARK 26 /* max number of marks */ 1262 1263line_t *mark[MAXMARK]; /* line markers */ 1264int markno; /* line marker count */ 1265 1266/* mark_line_node: set a line node mark */ 1267int 1268mark_line_node(lp, n) 1269 line_t *lp; 1270 int n; 1271{ 1272 if (!islower((unsigned char)n)) { 1273 sprintf(errmsg, "invalid mark character"); 1274 return ERR; 1275 } else if (mark[n - 'a'] == NULL) 1276 markno++; 1277 mark[n - 'a'] = lp; 1278 return 0; 1279} 1280 1281 1282/* get_marked_node_addr: return address of a marked line */ 1283long 1284get_marked_node_addr(n) 1285 int n; 1286{ 1287 if (!islower((unsigned char)n)) { 1288 sprintf(errmsg, "invalid mark character"); 1289 return ERR; 1290 } 1291 return get_line_node_addr(mark[n - 'a']); 1292} 1293 1294 1295/* unmark_line_node: clear line node mark */ 1296void 1297unmark_line_node(lp) 1298 line_t *lp; 1299{ 1300 int i; 1301 1302 for (i = 0; markno && i < MAXMARK; i++) 1303 if (mark[i] == lp) { 1304 mark[i] = NULL; 1305 markno--; 1306 } 1307} 1308 1309 1310/* dup_line_node: return a pointer to a copy of a line node */ 1311line_t * 1312dup_line_node(lp) 1313 line_t *lp; 1314{ 1315 line_t *np; 1316 1317 if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) { 1318 fprintf(stderr, "%s\n", strerror(errno)); 1319 sprintf(errmsg, "out of memory"); 1320 return NULL; 1321 } 1322 np->seek = lp->seek; 1323 np->len = lp->len; 1324 return np; 1325} 1326 1327 1328/* has_trailing_escape: return the parity of escapes preceding a character 1329 in a string */ 1330int 1331has_trailing_escape(s, t) 1332 char *s; 1333 char *t; 1334{ 1335 return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1); 1336} 1337 1338 1339/* strip_escapes: return copy of escaped string of at most length MAXPATHLEN */ 1340char * 1341strip_escapes(s) 1342 char *s; 1343{ 1344 static char *file = NULL; 1345 static int filesz = 0; 1346 1347 int i = 0; 1348 1349 REALLOC(file, filesz, MAXPATHLEN + 1, NULL); 1350 while (i < filesz - 1 /* Worry about a possible trailing escape */ 1351 && (file[i++] = (*s == '\\') ? *++s : *s)) 1352 s++; 1353 return file; 1354} 1355 1356 1357void 1358signal_hup(signo) 1359 int signo; 1360{ 1361 if (mutex) 1362 sigflags |= (1 << (signo - 1)); 1363 else 1364 handle_hup(signo); 1365} 1366 1367 1368void 1369signal_int(signo) 1370 int signo; 1371{ 1372 if (mutex) 1373 sigflags |= (1 << (signo - 1)); 1374 else 1375 handle_int(signo); 1376} 1377 1378 1379void 1380handle_hup(signo) 1381 int signo; 1382{ 1383 char *hup = NULL; /* hup filename */ 1384 char *s; 1385 int n; 1386 1387 if (!sigactive) 1388 quit(1); 1389 sigflags &= ~(1 << (signo - 1)); 1390 if (addr_last && write_file("ed.hup", "w", 1, addr_last) < 0 && 1391 (s = getenv("HOME")) != NULL && 1392 (n = strlen(s)) + 8 <= MAXPATHLEN && /* "ed.hup" + '/' */ 1393 (hup = (char *) malloc(n + 10)) != NULL) { 1394 strcpy(hup, s); 1395 if (hup[n - 1] != '/') 1396 hup[n] = '/', hup[n+1] = '\0'; 1397 strcat(hup, "ed.hup"); 1398 write_file(hup, "w", 1, addr_last); 1399 } 1400 quit(2); 1401} 1402 1403 1404void 1405handle_int(signo) 1406 int signo; 1407{ 1408 if (!sigactive) 1409 quit(1); 1410 sigflags &= ~(1 << (signo - 1)); 1411#ifdef _POSIX_SOURCE 1412 siglongjmp(env, -1); 1413#else 1414 longjmp(env, -1); 1415#endif 1416} 1417 1418 1419int cols = 72; /* wrap column */ 1420 1421void 1422handle_winch(signo) 1423 int signo; 1424{ 1425 int save_errno = errno; 1426 1427 struct winsize ws; /* window size structure */ 1428 1429 sigflags &= ~(1 << (signo - 1)); 1430 if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) { 1431 if (ws.ws_row > 2) rows = ws.ws_row - 2; 1432 if (ws.ws_col > 8) cols = ws.ws_col - 8; 1433 } 1434 errno = save_errno; 1435} 1436 1437 1438/* is_legal_filename: return a legal filename */ 1439int 1440is_legal_filename(s) 1441 char *s; 1442{ 1443 if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) { 1444 sprintf(errmsg, "shell access restricted"); 1445 return 0; 1446 } 1447 return 1; 1448} 1449