main.c revision 256281
120253Sjoerg/* main.c: This file contains the main control and user-interface routines 220302Sjoerg for the ed line editor. */ 320302Sjoerg/*- 420253Sjoerg * Copyright (c) 1993 Andrew Moore, Talke Studio. 520253Sjoerg * All rights reserved. 620253Sjoerg * 720253Sjoerg * Redistribution and use in source and binary forms, with or without 820253Sjoerg * modification, are permitted provided that the following conditions 920302Sjoerg * are met: 1020253Sjoerg * 1. Redistributions of source code must retain the above copyright 1120253Sjoerg * notice, this list of conditions and the following disclaimer. 1220253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright 1320253Sjoerg * notice, this list of conditions and the following disclaimer in the 1420302Sjoerg * documentation and/or other materials provided with the distribution. 1520253Sjoerg * 1620253Sjoerg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1720302Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1820253Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1920253Sjoerg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2020253Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2120253Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2220253Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2320253Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2420253Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2544229Sdavidn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2620253Sjoerg * SUCH DAMAGE. 2720253Sjoerg */ 2830259Scharnier 2930259Scharnier#ifndef lint 3050479Speter#if 0 3130259Scharnierstatic const char copyright[] = 3230259Scharnier"@(#) Copyright (c) 1993 Andrew Moore, Talke Studio. \n\ 33286201Sbapt All rights reserved.\n"; 34286201Sbapt#endif 35286201Sbapt#endif /* not lint */ 3630259Scharnier 37286201Sbapt#include <sys/cdefs.h> 3830259Scharnier__FBSDID("$FreeBSD: stable/10/bin/ed/main.c 241720 2012-10-19 05:43:38Z ed $"); 39286982Sbapt 4020253Sjoerg/* 41286201Sbapt * CREDITS 42286201Sbapt * 43286201Sbapt * This program is based on the editor algorithm described in 44286201Sbapt * Brian W. Kernighan and P. J. Plauger's book "Software Tools 4530259Scharnier * in Pascal," Addison-Wesley, 1981. 46286201Sbapt * 47286201Sbapt * The buffering algorithm is attributed to Rodney Ruddock of 4820253Sjoerg * the University of Guelph, Guelph, Ontario. 49286201Sbapt * 50286201Sbapt * The cbc.c encryption code is adapted from 5120253Sjoerg * the bdes program by Matt Bishop of Dartmouth College, 5220253Sjoerg * Hanover, NH. 53286201Sbapt * 5420253Sjoerg */ 5523318Sache 5622394Sdavidn#include <sys/types.h> 5752512Sdavidn 5824214Sache#include <sys/ioctl.h> 59286196Sbapt#include <sys/wait.h> 60286196Sbapt#include <ctype.h> 61286196Sbapt#include <locale.h> 62286196Sbapt#include <pwd.h> 63286196Sbapt#include <setjmp.h> 64286196Sbapt 65286196Sbapt#include "ed.h" 66286196Sbapt 67286196Sbapt 68286196Sbapt#ifdef _POSIX_SOURCE 69286196Sbaptstatic sigjmp_buf env; 70286196Sbapt#else 71286196Sbaptstatic jmp_buf env; 7220253Sjoerg#endif 73286196Sbapt 74286196Sbapt/* static buffers */ 75286196Sbaptchar stdinbuf[1]; /* stdin buffer */ 76286196Sbaptstatic char *shcmd; /* shell command buffer */ 77286196Sbaptstatic int shcmdsz; /* shell command buffer size */ 78286196Sbaptstatic int shcmdi; /* shell command buffer index */ 79286196Sbaptchar *ibuf; /* ed command-line buffer */ 80286196Sbaptint ibufsz; /* ed command-line buffer size */ 81286196Sbaptchar *ibufp; /* pointer to ed command-line buffer */ 82286196Sbapt 83286196Sbapt/* global flags */ 84286196Sbaptint des = 0; /* if set, use crypt(3) for i/o */ 85286196Sbaptstatic int garrulous = 0; /* if set, print all error messages */ 86283961Sbaptint isbinary; /* if set, buffer contains ASCII NULs */ 87286982Sbaptint isglobal; /* if set, doing a global command */ 88286982Sbaptint modified; /* if set, buffer modified since last write */ 89286982Sbaptint mutex = 0; /* if set, signals set "sigflags" */ 90286982Sbaptstatic int red = 0; /* if set, restrict shell/directory access */ 91286982Sbaptint scripted = 0; /* if set, suppress diagnostics */ 92286982Sbaptint sigflags = 0; /* if set, signals received while mutex set */ 93286982Sbaptstatic int sigactive = 0; /* if set, signal handlers are enabled */ 94286982Sbapt 95286982Sbaptstatic char old_filename[PATH_MAX] = ""; /* default filename */ 96286982Sbaptlong current_addr; /* current address in editor buffer */ 97286982Sbaptlong addr_last; /* last address in editor buffer */ 98286982Sbaptint lineno; /* script line number */ 99286982Sbaptstatic const char *prompt; /* command-line prompt */ 100286982Sbaptstatic const char *dps = "*"; /* default command-line prompt */ 101286982Sbapt 102286982Sbaptstatic const char *usage = "usage: %s [-] [-sx] [-p string] [file]\n"; 103286982Sbapt 104286982Sbapt/* ed: line editor */ 105286982Sbaptint 106286982Sbaptmain(volatile int argc, char ** volatile argv) 107286982Sbapt{ 108290153Sbdrewery int c, n; 109290153Sbdrewery long status = 0; 110286982Sbapt 111290153Sbdrewery (void)setlocale(LC_ALL, ""); 112286982Sbapt 113286982Sbapt red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r'; 114286982Sbapttop: 115286982Sbapt while ((c = getopt(argc, argv, "p:sx")) != -1) 116286982Sbapt switch(c) { 117286982Sbapt case 'p': /* set prompt */ 118286982Sbapt prompt = optarg; 119286982Sbapt break; 120286982Sbapt case 's': /* run script */ 121286982Sbapt scripted = 1; 122286982Sbapt break; 123286982Sbapt case 'x': /* use crypt */ 124286982Sbapt#ifdef DES 125286982Sbapt des = get_keyword(); 126286986Sbapt#else 127286982Sbapt fprintf(stderr, "crypt unavailable\n?\n"); 128286982Sbapt#endif 129286982Sbapt break; 130286982Sbapt 131286982Sbapt default: 132286982Sbapt fprintf(stderr, usage, red ? "red" : "ed"); 133286982Sbapt exit(1); 134286982Sbapt } 135286982Sbapt argv += optind; 136286982Sbapt argc -= optind; 137286982Sbapt if (argc && **argv == '-') { 138286982Sbapt scripted = 1; 139286982Sbapt if (argc > 1) { 140286982Sbapt optind = 1; 141286982Sbapt goto top; 142286982Sbapt } 143286982Sbapt argv++; 144286982Sbapt argc--; 145286982Sbapt } 146286982Sbapt /* assert: reliable signals! */ 147286982Sbapt#ifdef SIGWINCH 148286982Sbapt handle_winch(SIGWINCH); 149286982Sbapt if (isatty(0)) signal(SIGWINCH, handle_winch); 150286982Sbapt#endif 151286196Sbapt signal(SIGHUP, signal_hup); 152286196Sbapt signal(SIGQUIT, SIG_IGN); 153283961Sbapt signal(SIGINT, signal_int); 154285430Sbapt#ifdef _POSIX_SOURCE 155283961Sbapt if ((status = sigsetjmp(env, 1))) 156286982Sbapt#else 157286982Sbapt if ((status = setjmp(env))) 158286982Sbapt#endif 159285430Sbapt { 160285434Sbapt fputs("\n?\n", stderr); 161285434Sbapt errmsg = "interrupt"; 162285434Sbapt } else { 163283961Sbapt init_buffers(); 164283961Sbapt sigactive = 1; /* enable signal handlers */ 165286196Sbapt if (argc && **argv && is_legal_filename(*argv)) { 166285430Sbapt if (read_file(*argv, 0) < 0 && !isatty(0)) 167286196Sbapt quit(2); 168286196Sbapt else if (**argv != '!') 169283961Sbapt if (strlcpy(old_filename, *argv, sizeof(old_filename)) 170283961Sbapt >= sizeof(old_filename)) 171285133Sbapt quit(2); 172286196Sbapt } else if (argc) { 173285133Sbapt fputs("?\n", stderr); 174285133Sbapt if (**argv == '\0') 175285133Sbapt errmsg = "invalid filename"; 176285133Sbapt if (!isatty(0)) 177285133Sbapt quit(2); 178285133Sbapt } 179285133Sbapt } 180286196Sbapt for (;;) { 181285133Sbapt if (status < 0 && garrulous) 182285133Sbapt fprintf(stderr, "%s\n", errmsg); 183285133Sbapt if (prompt) { 184285133Sbapt printf("%s", prompt); 185285133Sbapt fflush(stdout); 186285133Sbapt } 187285133Sbapt if ((n = get_tty_line()) < 0) { 188286196Sbapt status = ERR; 189286196Sbapt continue; 190285133Sbapt } else if (n == 0) { 191285133Sbapt if (modified && !scripted) { 192285137Sbapt fputs("?\n", stderr); 193285133Sbapt errmsg = "warning: file modified"; 194286196Sbapt if (!isatty(0)) { 195285133Sbapt if (garrulous) 196285133Sbapt fprintf(stderr, 197286196Sbapt "script, line %d: %s\n", 198285133Sbapt lineno, errmsg); 199285133Sbapt quit(2); 200285133Sbapt } 201285133Sbapt clearerr(stdin); 202286196Sbapt modified = 0; 203285133Sbapt status = EMOD; 204286196Sbapt continue; 205285133Sbapt } else 206285133Sbapt quit(0); 207285133Sbapt } else if (ibuf[n - 1] != '\n') { 208285133Sbapt /* discard line */ 209285133Sbapt errmsg = "unexpected end-of-file"; 210285133Sbapt clearerr(stdin); 211286196Sbapt status = ERR; 212285133Sbapt continue; 213285133Sbapt } 214285133Sbapt isglobal = 0; 215285133Sbapt if ((status = extract_addr_range()) >= 0 && 216285133Sbapt (status = exec_command()) >= 0) 217286196Sbapt if (!status || 218286196Sbapt (status = display_lines(current_addr, current_addr, 219285133Sbapt status)) >= 0) 220285137Sbapt continue; 221286196Sbapt switch (status) { 222285133Sbapt case EOF: 223285133Sbapt quit(0); 224285133Sbapt case EMOD: 225285133Sbapt modified = 0; 226285133Sbapt fputs("?\n", stderr); /* give warning */ 227285133Sbapt errmsg = "warning: file modified"; 228285133Sbapt if (!isatty(0)) { 229285133Sbapt if (garrulous) 230285133Sbapt fprintf(stderr, "script, line %d: %s\n", 231285133Sbapt lineno, errmsg); 232285133Sbapt quit(2); 233285405Sbapt } 234286196Sbapt break; 235285405Sbapt case FATAL: 236285405Sbapt if (!isatty(0)) { 237286196Sbapt if (garrulous) 238285405Sbapt fprintf(stderr, "script, line %d: %s\n", 239286196Sbapt lineno, errmsg); 240286196Sbapt } else if (garrulous) 241286196Sbapt fprintf(stderr, "%s\n", errmsg); 242286196Sbapt quit(3); 243285405Sbapt default: 244285405Sbapt fputs("?\n", stderr); 245285405Sbapt if (!isatty(0)) { 246285405Sbapt if (garrulous) 247285405Sbapt fprintf(stderr, "script, line %d: %s\n", 248285405Sbapt lineno, errmsg); 249286196Sbapt quit(2); 250286196Sbapt } 251285405Sbapt break; 252285405Sbapt } 253285984Sbapt } 254285405Sbapt /*NOTREACHED*/ 255285405Sbapt} 256285405Sbapt 257285405Sbaptlong first_addr, second_addr; 258285405Sbaptstatic long addr_cnt; 259285405Sbapt 260285405Sbapt/* extract_addr_range: get line addresses from the command buffer until an 261285405Sbapt illegal address is seen; return status */ 262285405Sbaptint 263285405Sbaptextract_addr_range(void) 264285405Sbapt{ 265285405Sbapt long addr; 266285405Sbapt 267285405Sbapt addr_cnt = 0; 268285405Sbapt first_addr = second_addr = current_addr; 269286196Sbapt while ((addr = next_addr()) >= 0) { 270285405Sbapt addr_cnt++; 271285405Sbapt first_addr = second_addr; 272285405Sbapt second_addr = addr; 273286196Sbapt if (*ibufp != ',' && *ibufp != ';') 274285405Sbapt break; 275291657Sbapt else if (*ibufp++ == ';') 276285405Sbapt current_addr = addr; 277286196Sbapt } 278286196Sbapt if ((addr_cnt = min(addr_cnt, 2)) == 1 || second_addr != addr) 279286196Sbapt first_addr = second_addr; 280286196Sbapt return (addr == ERR) ? ERR : 0; 281285405Sbapt} 282285405Sbapt 283291657Sbapt 284291657Sbapt#define SKIP_BLANKS() while (isspace((unsigned char)*ibufp) && *ibufp != '\n') ibufp++ 285291657Sbapt 286286196Sbapt#define MUST_BE_FIRST() do { \ 287291657Sbapt if (!first) { \ 288291657Sbapt errmsg = "invalid address"; \ 289291657Sbapt return ERR; \ 290291657Sbapt } \ 291291657Sbapt} while (0) 292291657Sbapt 293285405Sbapt/* next_addr: return the next line address in the command buffer */ 294291657Sbaptlong 295291657Sbaptnext_addr(void) 296285405Sbapt{ 297285405Sbapt const char *hd; 298285405Sbapt long addr = current_addr; 299285405Sbapt long n; 300285405Sbapt int first = 1; 301285405Sbapt int c; 302285405Sbapt 303285405Sbapt SKIP_BLANKS(); 304285405Sbapt for (hd = ibufp;; first = 0) 305285405Sbapt switch (c = *ibufp) { 306285405Sbapt case '+': 307285405Sbapt case '\t': 308285405Sbapt case ' ': 309285405Sbapt case '-': 310285405Sbapt case '^': 311285405Sbapt ibufp++; 312285405Sbapt SKIP_BLANKS(); 313285405Sbapt if (isdigit((unsigned char)*ibufp)) { 314285405Sbapt STRTOL(n, ibufp); 315285405Sbapt addr += (c == '-' || c == '^') ? -n : n; 316285405Sbapt } else if (!isspace((unsigned char)c)) 317285405Sbapt addr += (c == '-' || c == '^') ? -1 : 1; 318286196Sbapt break; 319285405Sbapt case '0': case '1': case '2': 320285405Sbapt case '3': case '4': case '5': 321285405Sbapt case '6': case '7': case '8': case '9': 322285405Sbapt MUST_BE_FIRST(); 323285405Sbapt STRTOL(addr, ibufp); 324286196Sbapt break; 325286196Sbapt case '.': 32620253Sjoerg case '$': 327286196Sbapt MUST_BE_FIRST(); 328286196Sbapt ibufp++; 329286196Sbapt addr = (c == '.') ? current_addr : addr_last; 33020253Sjoerg break; 33120267Sjoerg case '/': 332286196Sbapt case '?': 33320253Sjoerg MUST_BE_FIRST(); 334286196Sbapt if ((addr = get_matching_node_addr( 335286196Sbapt get_compiled_pattern(), c == '/')) < 0) 33620253Sjoerg return ERR; 337286196Sbapt else if (c == *ibufp) 338286196Sbapt ibufp++; 339286196Sbapt break; 340286196Sbapt case '\'': 34120253Sjoerg MUST_BE_FIRST(); 34221052Sdavidn ibufp++; 343286196Sbapt if ((addr = get_marked_node_addr(*ibufp++)) < 0) 344286196Sbapt return ERR; 345286196Sbapt break; 34621052Sdavidn case '%': 347286196Sbapt case ',': 348286196Sbapt case ';': 349286196Sbapt if (first) { 350286196Sbapt ibufp++; 35121052Sdavidn addr_cnt++; 352286196Sbapt second_addr = (c == ';') ? current_addr : 1; 35321052Sdavidn addr = addr_last; 35420253Sjoerg break; 355286196Sbapt } 35620253Sjoerg /* FALLTHROUGH */ 357286196Sbapt default: 358286196Sbapt if (ibufp == hd) 359286196Sbapt return EOF; 360286196Sbapt else if (addr < 0 || addr_last < addr) { 361286196Sbapt errmsg = "invalid address"; 36252527Sdavidn return ERR; 36320253Sjoerg } else 364286196Sbapt return addr; 36520253Sjoerg } 366286196Sbapt /* NOTREACHED */ 367286196Sbapt} 36820253Sjoerg 36920267Sjoerg 370286196Sbapt#ifdef BACKWARDS 37120267Sjoerg/* GET_THIRD_ADDR: get a legal address from the command buffer */ 372286196Sbapt#define GET_THIRD_ADDR(addr) \ 373286196Sbapt{ \ 374286196Sbapt long ol1, ol2; \ 375286196Sbapt\ 37620253Sjoerg ol1 = first_addr, ol2 = second_addr; \ 37720253Sjoerg if (extract_addr_range() < 0) \ 378286196Sbapt return ERR; \ 379286196Sbapt else if (addr_cnt == 0) { \ 38020253Sjoerg errmsg = "destination expected"; \ 38120253Sjoerg return ERR; \ 38220253Sjoerg } else if (second_addr < 0 || addr_last < second_addr) { \ 38320253Sjoerg errmsg = "invalid address"; \ 38420253Sjoerg return ERR; \ 38520253Sjoerg } \ 38620253Sjoerg addr = second_addr; \ 38744229Sdavidn first_addr = ol1, second_addr = ol2; \ 388286196Sbapt} 389286196Sbapt#else /* BACKWARDS */ 390286196Sbapt/* GET_THIRD_ADDR: get a legal address from the command buffer */ 391286196Sbapt#define GET_THIRD_ADDR(addr) \ 39220253Sjoerg{ \ 39320253Sjoerg long ol1, ol2; \ 394262865Sjulian\ 395262865Sjulian ol1 = first_addr, ol2 = second_addr; \ 39620267Sjoerg if (extract_addr_range() < 0) \ 39720253Sjoerg return ERR; \ 398286196Sbapt if (second_addr < 0 || addr_last < second_addr) { \ 39920253Sjoerg errmsg = "invalid address"; \ 40020253Sjoerg return ERR; \ 40120253Sjoerg } \ 40220253Sjoerg addr = second_addr; \ 40320253Sjoerg first_addr = ol1, second_addr = ol2; \ 40420253Sjoerg} 40520253Sjoerg#endif 40620253Sjoerg 40720253Sjoerg 40820253Sjoerg/* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */ 409285414Sbapt#define GET_COMMAND_SUFFIX() { \ 410285414Sbapt int done = 0; \ 411286196Sbapt do { \ 412285395Sbapt switch(*ibufp) { \ 413285395Sbapt case 'p': \ 414286196Sbapt gflag |= GPR, ibufp++; \ 415286196Sbapt break; \ 416286196Sbapt case 'l': \ 41744229Sdavidn gflag |= GLS, ibufp++; \ 41820267Sjoerg break; \ 41920267Sjoerg case 'n': \ 42020253Sjoerg gflag |= GNP, ibufp++; \ 42144229Sdavidn break; \ 422286196Sbapt default: \ 42320253Sjoerg done++; \ 42420253Sjoerg } \ 425286196Sbapt } while (!done); \ 426286196Sbapt if (*ibufp++ != '\n') { \ 42720253Sjoerg errmsg = "invalid command suffix"; \ 428282699Sbapt return ERR; \ 42920253Sjoerg } \ 430286196Sbapt} 431286196Sbapt 43220253Sjoerg 433282699Sbapt/* sflags */ 434282699Sbapt#define SGG 001 /* complement previous global substitute suffix */ 435282699Sbapt#define SGP 002 /* complement previous print suffix */ 436282699Sbapt#define SGR 004 /* use last regex instead of last pat */ 437282699Sbapt#define SGF 010 /* repeat last substitution */ 43820253Sjoerg 43920253Sjoergint patlock = 0; /* if set, pattern not freed by get_compiled_pattern() */ 440286196Sbapt 44120253Sjoerglong rows = 22; /* scroll length: ws_row - 2 */ 44220253Sjoerg 44320253Sjoerg/* exec_command: execute the next command in command buffer; return print 44420253Sjoerg request, if any */ 44520253Sjoergint 44620253Sjoergexec_command(void) 44720253Sjoerg{ 44820253Sjoerg static pattern_t *pat = NULL; 44920253Sjoerg static int sgflag = 0; 45020253Sjoerg static long sgnum = 0; 45120253Sjoerg 452130633Srobert pattern_t *tpat; 45320253Sjoerg char *fnp; 45420253Sjoerg int gflag = 0; 45520253Sjoerg int sflags = 0; 45620253Sjoerg long addr = 0; 45720253Sjoerg int n = 0; 458282700Sbapt int c; 45920253Sjoerg 46020253Sjoerg SKIP_BLANKS(); 46120253Sjoerg switch(c = *ibufp++) { 46220253Sjoerg case 'a': 463282700Sbapt GET_COMMAND_SUFFIX(); 46420253Sjoerg if (!isglobal) clear_undo_stack(); 46520253Sjoerg if (append_lines(second_addr) < 0) 46620253Sjoerg return ERR; 46720253Sjoerg break; 46820253Sjoerg case 'c': 46930259Scharnier if (check_addr_range(current_addr, current_addr) < 0) 47030259Scharnier return ERR; 47120253Sjoerg GET_COMMAND_SUFFIX(); 47220253Sjoerg if (!isglobal) clear_undo_stack(); 47320253Sjoerg if (delete_lines(first_addr, second_addr) < 0 || 47420253Sjoerg append_lines(current_addr) < 0) 475286196Sbapt return ERR; 476286196Sbapt break; 47720253Sjoerg case 'd': 47820253Sjoerg if (check_addr_range(current_addr, current_addr) < 0) 479286196Sbapt return ERR; 48020253Sjoerg GET_COMMAND_SUFFIX(); 48120253Sjoerg if (!isglobal) clear_undo_stack(); 482179365Santoine if (delete_lines(first_addr, second_addr) < 0) 48320253Sjoerg return ERR; 484179365Santoine else if ((addr = INC_MOD(current_addr, addr_last)) != 0) 485179365Santoine current_addr = addr; 486286196Sbapt break; 48720253Sjoerg case 'e': 48820253Sjoerg if (modified && !scripted) 48920253Sjoerg return EMOD; 490179365Santoine /* FALLTHROUGH */ 491231994Skevlo case 'E': 49220253Sjoerg if (addr_cnt > 0) { 49320253Sjoerg errmsg = "unexpected address"; 49420253Sjoerg return ERR; 49520253Sjoerg } else if (!isspace((unsigned char)*ibufp)) { 49620253Sjoerg errmsg = "unexpected command suffix"; 497179365Santoine return ERR; 498181785Sache } else if ((fnp = get_filename()) == NULL) 499179365Santoine return ERR; 50020253Sjoerg GET_COMMAND_SUFFIX(); 501231994Skevlo if (delete_lines(1, addr_last) < 0) 502231994Skevlo return ERR; 503231994Skevlo clear_undo_stack(); 504231994Skevlo if (close_sbuf() < 0) 50520253Sjoerg return ERR; 50620253Sjoerg else if (open_sbuf() < 0) 507286196Sbapt return FATAL; 508286196Sbapt if (*fnp && *fnp != '!') strcpy(old_filename, fnp); 50920253Sjoerg#ifdef BACKWARDS 51020253Sjoerg if (*fnp == '\0' && *old_filename == '\0') { 51120253Sjoerg errmsg = "no current filename"; 51220253Sjoerg return ERR; 51320253Sjoerg } 51420253Sjoerg#endif 51573563Skris if (read_file(*fnp ? fnp : old_filename, 0) < 0) 51620253Sjoerg return ERR; 517181785Sache clear_undo_stack(); 51820253Sjoerg modified = 0; 51920253Sjoerg u_current_addr = u_addr_last = -1; 52020253Sjoerg break; 52120253Sjoerg case 'f': 52220253Sjoerg if (addr_cnt > 0) { 523286196Sbapt errmsg = "unexpected address"; 52461957Sache return ERR; 52520712Sdavidn } else if (!isspace((unsigned char)*ibufp)) { 52620253Sjoerg errmsg = "unexpected command suffix"; 52720253Sjoerg return ERR; 52820253Sjoerg } else if ((fnp = get_filename()) == NULL) 52920253Sjoerg return ERR; 53020253Sjoerg else if (*fnp == '!') { 53120253Sjoerg errmsg = "invalid redirection"; 53220253Sjoerg return ERR; 53320253Sjoerg } 53420253Sjoerg GET_COMMAND_SUFFIX(); 53520253Sjoerg if (*fnp) strcpy(old_filename, fnp); 53620253Sjoerg printf("%s\n", strip_escapes(old_filename)); 53720253Sjoerg break; 53820253Sjoerg case 'g': 539130633Srobert case 'v': 54020253Sjoerg case 'G': 54120253Sjoerg case 'V': 54220253Sjoerg if (isglobal) { 54320253Sjoerg errmsg = "cannot nest global commands"; 54420253Sjoerg return ERR; 545284111Sbapt } else if (check_addr_range(1, addr_last) < 0) 546286196Sbapt return ERR; 547284111Sbapt else if (build_active_list(c == 'g' || c == 'G') < 0) 548286196Sbapt return ERR; 549286196Sbapt else if ((n = (c == 'G' || c == 'V'))) 550286196Sbapt GET_COMMAND_SUFFIX(); 551286196Sbapt isglobal++; 552286196Sbapt if (exec_global(n, gflag) < 0) 553286196Sbapt return ERR; 554286196Sbapt break; 555286196Sbapt case 'h': 556286196Sbapt if (addr_cnt > 0) { 557286196Sbapt errmsg = "unexpected address"; 558286196Sbapt return ERR; 559286196Sbapt } 560286196Sbapt GET_COMMAND_SUFFIX(); 561286196Sbapt if (*errmsg) fprintf(stderr, "%s\n", errmsg); 562286196Sbapt break; 563286196Sbapt case 'H': 564286196Sbapt if (addr_cnt > 0) { 565286196Sbapt errmsg = "unexpected address"; 566286196Sbapt return ERR; 567286196Sbapt } 568286196Sbapt GET_COMMAND_SUFFIX(); 569286196Sbapt if ((garrulous = 1 - garrulous) && *errmsg) 570286196Sbapt fprintf(stderr, "%s\n", errmsg); 571286196Sbapt break; 572286196Sbapt case 'i': 573286196Sbapt if (second_addr == 0) { 574286196Sbapt errmsg = "invalid address"; 575286196Sbapt return ERR; 576286196Sbapt } 577286196Sbapt GET_COMMAND_SUFFIX(); 578286196Sbapt if (!isglobal) clear_undo_stack(); 579286196Sbapt if (append_lines(second_addr - 1) < 0) 580286196Sbapt return ERR; 581286196Sbapt break; 582286196Sbapt case 'j': 583286196Sbapt if (check_addr_range(current_addr, current_addr + 1) < 0) 584286196Sbapt return ERR; 585286196Sbapt GET_COMMAND_SUFFIX(); 586286196Sbapt if (!isglobal) clear_undo_stack(); 587286196Sbapt if (first_addr != second_addr && 588286196Sbapt join_lines(first_addr, second_addr) < 0) 589286196Sbapt return ERR; 590286196Sbapt break; 591286196Sbapt case 'k': 592286196Sbapt c = *ibufp++; 593286196Sbapt if (second_addr == 0) { 594286196Sbapt errmsg = "invalid address"; 595286196Sbapt return ERR; 596286196Sbapt } 597286196Sbapt GET_COMMAND_SUFFIX(); 598286196Sbapt if (mark_line_node(get_addressed_line_node(second_addr), c) < 0) 599286196Sbapt return ERR; 600286196Sbapt break; 601286196Sbapt case 'l': 602286196Sbapt if (check_addr_range(current_addr, current_addr) < 0) 603286196Sbapt return ERR; 604286196Sbapt GET_COMMAND_SUFFIX(); 605286196Sbapt if (display_lines(first_addr, second_addr, gflag | GLS) < 0) 606286196Sbapt return ERR; 607286196Sbapt gflag = 0; 608286196Sbapt break; 609286196Sbapt case 'm': 610286196Sbapt if (check_addr_range(current_addr, current_addr) < 0) 611286196Sbapt return ERR; 612286196Sbapt GET_THIRD_ADDR(addr); 613286196Sbapt if (first_addr <= addr && addr < second_addr) { 614286196Sbapt errmsg = "invalid destination"; 615286196Sbapt return ERR; 616286196Sbapt } 617286196Sbapt GET_COMMAND_SUFFIX(); 618286196Sbapt if (!isglobal) clear_undo_stack(); 619286196Sbapt if (move_lines(addr) < 0) 620286196Sbapt return ERR; 621286196Sbapt break; 622286196Sbapt case 'n': 623286196Sbapt if (check_addr_range(current_addr, current_addr) < 0) 624286196Sbapt return ERR; 625286196Sbapt GET_COMMAND_SUFFIX(); 626286196Sbapt if (display_lines(first_addr, second_addr, gflag | GNP) < 0) 627286196Sbapt return ERR; 628286196Sbapt gflag = 0; 629286196Sbapt break; 630286196Sbapt case 'p': 631286196Sbapt if (check_addr_range(current_addr, current_addr) < 0) 632286196Sbapt return ERR; 633286196Sbapt GET_COMMAND_SUFFIX(); 634286196Sbapt if (display_lines(first_addr, second_addr, gflag | GPR) < 0) 635286196Sbapt return ERR; 636286196Sbapt gflag = 0; 637286196Sbapt break; 638286196Sbapt case 'P': 639286196Sbapt if (addr_cnt > 0) { 640286196Sbapt errmsg = "unexpected address"; 641286196Sbapt return ERR; 642286196Sbapt } 643291658Sbapt GET_COMMAND_SUFFIX(); 644291658Sbapt prompt = prompt ? NULL : optarg ? optarg : dps; 645286196Sbapt break; 646286196Sbapt case 'q': 647286196Sbapt case 'Q': 648286196Sbapt if (addr_cnt > 0) { 649286196Sbapt errmsg = "unexpected address"; 650286196Sbapt return ERR; 651286196Sbapt } 652286196Sbapt GET_COMMAND_SUFFIX(); 653286196Sbapt gflag = (modified && !scripted && c == 'q') ? EMOD : EOF; 654286196Sbapt break; 655286196Sbapt case 'r': 656286196Sbapt if (!isspace((unsigned char)*ibufp)) { 657286196Sbapt errmsg = "unexpected command suffix"; 658286196Sbapt return ERR; 659286196Sbapt } else if (addr_cnt == 0) 660286196Sbapt second_addr = addr_last; 661286196Sbapt if ((fnp = get_filename()) == NULL) 662286196Sbapt return ERR; 663286196Sbapt GET_COMMAND_SUFFIX(); 664286196Sbapt if (!isglobal) clear_undo_stack(); 665286196Sbapt if (*old_filename == '\0' && *fnp != '!') 666286196Sbapt strcpy(old_filename, fnp); 667286196Sbapt#ifdef BACKWARDS 668286196Sbapt if (*fnp == '\0' && *old_filename == '\0') { 669286196Sbapt errmsg = "no current filename"; 670286196Sbapt return ERR; 671286196Sbapt } 672286196Sbapt#endif 673286196Sbapt if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0) 674286196Sbapt return ERR; 675286196Sbapt else if (addr && addr != addr_last) 676286196Sbapt modified = 1; 677286196Sbapt break; 678286196Sbapt case 's': 679286196Sbapt do { 680286196Sbapt switch(*ibufp) { 681286196Sbapt case '\n': 682286196Sbapt sflags |=SGF; 683286196Sbapt break; 684286196Sbapt case 'g': 685286196Sbapt sflags |= SGG; 686286196Sbapt ibufp++; 687286196Sbapt break; 688286196Sbapt case 'p': 689286196Sbapt sflags |= SGP; 690286196Sbapt ibufp++; 691286196Sbapt break; 692286196Sbapt case 'r': 693286196Sbapt sflags |= SGR; 694286196Sbapt ibufp++; 695286196Sbapt break; 696286196Sbapt case '0': case '1': case '2': case '3': case '4': 697286202Sbapt case '5': case '6': case '7': case '8': case '9': 698286202Sbapt STRTOL(sgnum, ibufp); 699286196Sbapt sflags |= SGF; 700286196Sbapt sgflag &= ~GSG; /* override GSG */ 701286196Sbapt break; 702286196Sbapt default: 703286196Sbapt if (sflags) { 704286196Sbapt errmsg = "invalid command suffix"; 705286196Sbapt return ERR; 706286196Sbapt } 707286196Sbapt } 708286196Sbapt } while (sflags && *ibufp != '\n'); 709286196Sbapt if (sflags && !pat) { 710286196Sbapt errmsg = "no previous substitution"; 711286196Sbapt return ERR; 712286196Sbapt } else if (sflags & SGG) 713286196Sbapt sgnum = 0; /* override numeric arg */ 714286196Sbapt if (*ibufp != '\n' && *(ibufp + 1) == '\n') { 715286196Sbapt errmsg = "invalid pattern delimiter"; 716286196Sbapt return ERR; 717286196Sbapt } 718286196Sbapt tpat = pat; 719286196Sbapt SPL1(); 720286196Sbapt if ((!sflags || (sflags & SGR)) && 721286196Sbapt (tpat = get_compiled_pattern()) == NULL) { 722286196Sbapt SPL0(); 723286196Sbapt return ERR; 724286196Sbapt } else if (tpat != pat) { 725286196Sbapt if (pat) { 726286196Sbapt regfree(pat); 727286196Sbapt free(pat); 728286196Sbapt } 729286196Sbapt pat = tpat; 730286196Sbapt patlock = 1; /* reserve pattern */ 731286196Sbapt } 732286196Sbapt SPL0(); 733286196Sbapt if (!sflags && extract_subst_tail(&sgflag, &sgnum) < 0) 734286196Sbapt return ERR; 735286196Sbapt else if (isglobal) 736286196Sbapt sgflag |= GLB; 737286196Sbapt else 738286196Sbapt sgflag &= ~GLB; 739286196Sbapt if (sflags & SGG) 740286196Sbapt sgflag ^= GSG; 741286196Sbapt if (sflags & SGP) 742286196Sbapt sgflag ^= GPR, sgflag &= ~(GLS | GNP); 743286196Sbapt do { 744286196Sbapt switch(*ibufp) { 745301367Sbapt case 'p': 746286196Sbapt sgflag |= GPR, ibufp++; 747286196Sbapt break; 748286196Sbapt case 'l': 749286196Sbapt sgflag |= GLS, ibufp++; 750286196Sbapt break; 751286217Sadrian case 'n': 752286196Sbapt sgflag |= GNP, ibufp++; 753286196Sbapt break; 754286196Sbapt default: 755286196Sbapt n++; 756286196Sbapt } 757286196Sbapt } while (!n); 758286196Sbapt if (check_addr_range(current_addr, current_addr) < 0) 759286196Sbapt return ERR; 760286196Sbapt GET_COMMAND_SUFFIX(); 761286196Sbapt if (!isglobal) clear_undo_stack(); 762286196Sbapt if (search_and_replace(pat, sgflag, sgnum) < 0) 763286196Sbapt return ERR; 764286196Sbapt break; 765286196Sbapt case 't': 766286196Sbapt if (check_addr_range(current_addr, current_addr) < 0) 767286196Sbapt return ERR; 768286196Sbapt GET_THIRD_ADDR(addr); 769286196Sbapt GET_COMMAND_SUFFIX(); 770286196Sbapt if (!isglobal) clear_undo_stack(); 771286196Sbapt if (copy_lines(addr) < 0) 772285401Sbapt return ERR; 773286196Sbapt break; 774286218Sbapt case 'u': 775286196Sbapt if (addr_cnt > 0) { 776286196Sbapt errmsg = "unexpected address"; 777286196Sbapt return ERR; 778286196Sbapt } 779286196Sbapt GET_COMMAND_SUFFIX(); 780286196Sbapt if (pop_undo_stack() < 0) 78120253Sjoerg return ERR; 782286196Sbapt break; 783286259Sed case 'w': 784286196Sbapt case 'W': 785286196Sbapt if ((n = *ibufp) == 'q' || n == 'Q') { 786286196Sbapt gflag = EOF; 787286196Sbapt ibufp++; 788286196Sbapt } 789286196Sbapt if (!isspace((unsigned char)*ibufp)) { 790286196Sbapt errmsg = "unexpected command suffix"; 791286196Sbapt return ERR; 792286196Sbapt } else if ((fnp = get_filename()) == NULL) 793286196Sbapt return ERR; 794286196Sbapt if (addr_cnt == 0 && !addr_last) 795286196Sbapt first_addr = second_addr = 0; 796286196Sbapt else if (check_addr_range(1, addr_last) < 0) 797286196Sbapt return ERR; 798286196Sbapt GET_COMMAND_SUFFIX(); 799286196Sbapt if (*old_filename == '\0' && *fnp != '!') 800286196Sbapt strcpy(old_filename, fnp); 801286196Sbapt#ifdef BACKWARDS 802286196Sbapt if (*fnp == '\0' && *old_filename == '\0') { 803286196Sbapt errmsg = "no current filename"; 804286196Sbapt return ERR; 805286196Sbapt } 806286196Sbapt#endif 807286196Sbapt if ((addr = write_file(*fnp ? fnp : old_filename, 808286196Sbapt (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0) 809286196Sbapt return ERR; 810286196Sbapt else if (addr == addr_last) 811286196Sbapt modified = 0; 812287799Sbapt else if (modified && !scripted && n == 'q') 813286196Sbapt gflag = EMOD; 814286196Sbapt break; 815286196Sbapt case 'x': 816286196Sbapt if (addr_cnt > 0) { 817286196Sbapt errmsg = "unexpected address"; 818286196Sbapt return ERR; 819286196Sbapt } 820286196Sbapt GET_COMMAND_SUFFIX(); 821286196Sbapt#ifdef DES 822286196Sbapt des = get_keyword(); 823286196Sbapt break; 824286196Sbapt#else 825286196Sbapt errmsg = "crypt unavailable"; 826286196Sbapt return ERR; 827286196Sbapt#endif 828286196Sbapt case 'z': 829285401Sbapt#ifdef BACKWARDS 830285401Sbapt if (check_addr_range(first_addr = 1, current_addr + 1) < 0) 831285401Sbapt#else 832285401Sbapt if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0) 833285401Sbapt#endif 834286196Sbapt return ERR; 835286196Sbapt else if ('0' < *ibufp && *ibufp <= '9') 836286196Sbapt STRTOL(rows, ibufp); 837286196Sbapt GET_COMMAND_SUFFIX(); 838286196Sbapt if (display_lines(second_addr, min(addr_last, 839286196Sbapt second_addr + rows), gflag) < 0) 840286196Sbapt return ERR; 841286196Sbapt gflag = 0; 842286196Sbapt break; 843286196Sbapt case '=': 844286196Sbapt GET_COMMAND_SUFFIX(); 845286196Sbapt printf("%ld\n", addr_cnt ? second_addr : addr_last); 846286196Sbapt break; 847286196Sbapt case '!': 848286196Sbapt if (addr_cnt > 0) { 849286196Sbapt errmsg = "unexpected address"; 850286196Sbapt return ERR; 851286196Sbapt } else if ((sflags = get_shell_command()) < 0) 852286196Sbapt return ERR; 853286196Sbapt GET_COMMAND_SUFFIX(); 854286196Sbapt if (sflags) printf("%s\n", shcmd + 1); 855286196Sbapt system(shcmd + 1); 856286196Sbapt if (!scripted) printf("!\n"); 857286196Sbapt break; 858286196Sbapt case '\n': 859286196Sbapt#ifdef BACKWARDS 860286218Sbapt if (check_addr_range(first_addr = 1, current_addr + 1) < 0 861286196Sbapt#else 862286196Sbapt if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0 863286196Sbapt#endif 864286196Sbapt || display_lines(second_addr, second_addr, 0) < 0) 865286196Sbapt return ERR; 866286196Sbapt break; 867286259Sed default: 868286196Sbapt errmsg = "unknown command"; 869286196Sbapt return ERR; 870286196Sbapt } 871286196Sbapt return gflag; 872286196Sbapt} 873286196Sbapt 874286196Sbapt 875286196Sbapt/* check_addr_range: return status of address range check */ 876286196Sbaptint 877286196Sbaptcheck_addr_range(long n, long m) 878286196Sbapt{ 879286196Sbapt if (addr_cnt == 0) { 880286196Sbapt first_addr = n; 881286196Sbapt second_addr = m; 882286196Sbapt } 883286196Sbapt if (first_addr > second_addr || 1 > first_addr || 884286196Sbapt second_addr > addr_last) { 885286196Sbapt errmsg = "invalid address"; 886286196Sbapt return ERR; 887286196Sbapt } 888286196Sbapt return 0; 889286196Sbapt} 890286196Sbapt 891286196Sbapt 892286196Sbapt/* get_matching_node_addr: return the address of the next line matching a 893286196Sbapt pattern in a given direction. wrap around begin/end of editor buffer if 894286196Sbapt necessary */ 895286196Sbaptlong 896286196Sbaptget_matching_node_addr(pattern_t *pat, int dir) 897286196Sbapt{ 898286196Sbapt char *s; 899286196Sbapt long n = current_addr; 900286196Sbapt line_t *lp; 901286196Sbapt 902286196Sbapt if (!pat) return ERR; 903286196Sbapt do { 904286196Sbapt if ((n = dir ? INC_MOD(n, addr_last) : DEC_MOD(n, addr_last))) { 905286196Sbapt lp = get_addressed_line_node(n); 906286196Sbapt if ((s = get_sbuf_line(lp)) == NULL) 907286196Sbapt return ERR; 908286196Sbapt if (isbinary) 909286196Sbapt NUL_TO_NEWLINE(s, lp->len); 910286196Sbapt if (!regexec(pat, s, 0, NULL, 0)) 911286196Sbapt return n; 912285401Sbapt } 913286196Sbapt } while (n != current_addr); 914285401Sbapt errmsg = "no match"; 915285401Sbapt return ERR; 916285989Sbapt} 917285989Sbapt 918286196Sbapt 919286196Sbapt/* get_filename: return pointer to copy of filename in the command buffer */ 920286196Sbaptchar * 921286196Sbaptget_filename(void) 922286196Sbapt{ 923286196Sbapt static char *file = NULL; 924286196Sbapt static int filesz = 0; 925286196Sbapt 926286196Sbapt int n; 927286196Sbapt 928285989Sbapt if (*ibufp != '\n') { 929286196Sbapt SKIP_BLANKS(); 930285401Sbapt if (*ibufp == '\n') { 931285401Sbapt errmsg = "invalid filename"; 932285401Sbapt return NULL; 933284111Sbapt } else if ((ibufp = get_extended_line(&n, 1)) == NULL) 934284111Sbapt return NULL; 935284111Sbapt else if (*ibufp == '!') { 936286196Sbapt ibufp++; 937285433Sbapt if ((n = get_shell_command()) < 0) 938284111Sbapt return NULL; 939284111Sbapt if (n) 940285433Sbapt printf("%s\n", shcmd + 1); 941285433Sbapt return shcmd; 942284111Sbapt } else if (n > PATH_MAX - 1) { 943284111Sbapt errmsg = "filename too long"; 944286202Sbapt return NULL; 945286202Sbapt } 946284111Sbapt } 947284111Sbapt#ifndef BACKWARDS 948284111Sbapt else if (*old_filename == '\0') { 949286196Sbapt errmsg = "no current filename"; 950284111Sbapt return NULL; 951284111Sbapt } 952284111Sbapt#endif 953284111Sbapt REALLOC(file, filesz, PATH_MAX, NULL); 954284111Sbapt for (n = 0; *ibufp != '\n';) 955284111Sbapt file[n++] = *ibufp++; 956284111Sbapt file[n] = '\0'; 957284111Sbapt return is_legal_filename(file) ? file : NULL; 958284111Sbapt} 959284111Sbapt 960284111Sbapt 961284111Sbapt/* get_shell_command: read a shell command from stdin; return substitution 962284111Sbapt status */ 963284111Sbaptint 964284111Sbaptget_shell_command(void) 965284111Sbapt{ 966284111Sbapt static char *buf = NULL; 967284111Sbapt static int n = 0; 968286196Sbapt 969286196Sbapt char *s; /* substitution char pointer */ 970284111Sbapt int i = 0; 971285401Sbapt int j = 0; 972285401Sbapt 973284111Sbapt if (red) { 974284111Sbapt errmsg = "shell access restricted"; 975284111Sbapt return ERR; 976284111Sbapt } else if ((s = ibufp = get_extended_line(&j, 1)) == NULL) 977284128Sbapt return ERR; 978284111Sbapt REALLOC(buf, n, j + 1, ERR); 979284111Sbapt buf[i++] = '!'; /* prefix command w/ bang */ 980284128Sbapt while (*ibufp != '\n') 981284128Sbapt switch (*ibufp) { 982284111Sbapt default: 983284111Sbapt REALLOC(buf, n, i + 2, ERR); 984284111Sbapt buf[i++] = *ibufp; 985284111Sbapt if (*ibufp++ == '\\') 986284113Sbapt buf[i++] = *ibufp++; 987284113Sbapt break; 988284113Sbapt case '!': 989284113Sbapt if (s != ibufp) { 990284128Sbapt REALLOC(buf, n, i + 1, ERR); 991284113Sbapt buf[i++] = *ibufp++; 992284113Sbapt } 993284113Sbapt#ifdef BACKWARDS 994284113Sbapt else if (shcmd == NULL || *(shcmd + 1) == '\0') 995284113Sbapt#else 996284113Sbapt else if (shcmd == NULL) 997284111Sbapt#endif 998284111Sbapt { 999284111Sbapt errmsg = "no previous command"; 1000284111Sbapt return ERR; 1001286196Sbapt } else { 1002286196Sbapt REALLOC(buf, n, i + shcmdi, ERR); 1003284111Sbapt for (s = shcmd + 1; s < shcmd + shcmdi;) 1004285433Sbapt buf[i++] = *s++; 1005285433Sbapt s = ibufp++; 1006285433Sbapt } 1007284111Sbapt break; 1008286196Sbapt case '%': 1009286196Sbapt if (*old_filename == '\0') { 1010286196Sbapt errmsg = "no current filename"; 1011284111Sbapt return ERR; 1012285433Sbapt } 1013286196Sbapt j = strlen(s = strip_escapes(old_filename)); 1014286196Sbapt REALLOC(buf, n, i + j, ERR); 1015285433Sbapt while (j--) 1016286196Sbapt buf[i++] = *s++; 1017286196Sbapt s = ibufp++; 1018286196Sbapt break; 1019285433Sbapt } 1020285433Sbapt REALLOC(shcmd, shcmdsz, i + 1, ERR); 1021284111Sbapt memcpy(shcmd, buf, i); 1022284111Sbapt shcmd[shcmdi = i] = '\0'; 1023284111Sbapt return *s == '!' || *s == '%'; 1024284111Sbapt} 1025284111Sbapt 1026286196Sbapt 1027286196Sbapt/* append_lines: insert text from stdin to after line n; stop when either a 102820253Sjoerg single period is read or EOF; return status */ 1029286196Sbaptint 103020253Sjoergappend_lines(long n) 1031286196Sbapt{ 1032286196Sbapt int l; 1033286196Sbapt const char *lp = ibuf; 1034286196Sbapt const char *eot; 1035286196Sbapt undo_t *up = NULL; 1036286196Sbapt 103720253Sjoerg for (current_addr = n;;) { 1038286196Sbapt if (!isglobal) { 103920253Sjoerg if ((l = get_tty_line()) < 0) 1040286196Sbapt return ERR; 1041286196Sbapt else if (l == 0 || ibuf[l - 1] != '\n') { 1042286196Sbapt clearerr(stdin); 1043286196Sbapt return l ? EOF : 0; 1044286196Sbapt } 1045286196Sbapt lp = ibuf; 1046286196Sbapt } else if (*(lp = ibufp) == '\0') 1047286196Sbapt return 0; 1048286196Sbapt else { 1049286196Sbapt while (*ibufp++ != '\n') 1050286196Sbapt ; 1051286196Sbapt l = ibufp - lp; 1052286196Sbapt } 1053286196Sbapt if (l == 2 && lp[0] == '.' && lp[1] == '\n') { 105420253Sjoerg return 0; 105520253Sjoerg } 1056286196Sbapt eot = lp + l; 1057286196Sbapt SPL1(); 105820253Sjoerg do { 105920253Sjoerg if ((lp = put_sbuf_line(lp)) == NULL) { 1060286196Sbapt SPL0(); 1061286196Sbapt return ERR; 106220253Sjoerg } else if (up) 1063286196Sbapt up->t = get_addressed_line_node(current_addr); 1064286196Sbapt else if ((up = push_undo_stack(UADD, current_addr, 1065286196Sbapt current_addr)) == NULL) { 106620253Sjoerg SPL0(); 1067286196Sbapt return ERR; 1068286196Sbapt } 1069286196Sbapt } while (lp != eot); 1070286196Sbapt modified = 1; 1071286196Sbapt SPL0(); 1072286196Sbapt } 1073286196Sbapt /* NOTREACHED */ 107420253Sjoerg} 1075286196Sbapt 1076286196Sbapt 1077286196Sbapt/* join_lines: replace a range of lines with the joined text of those lines */ 1078286196Sbaptint 1079286196Sbaptjoin_lines(long from, long to) 1080286196Sbapt{ 1081286196Sbapt static char *buf = NULL; 1082286196Sbapt static int n; 1083286196Sbapt 1084286196Sbapt char *s; 1085286196Sbapt int size = 0; 1086286196Sbapt line_t *bp, *ep; 1087286196Sbapt 1088286196Sbapt ep = get_addressed_line_node(INC_MOD(to, addr_last)); 1089286196Sbapt bp = get_addressed_line_node(from); 1090286196Sbapt for (; bp != ep; bp = bp->q_forw) { 1091286196Sbapt if ((s = get_sbuf_line(bp)) == NULL) 1092286196Sbapt return ERR; 1093286196Sbapt REALLOC(buf, n, size + bp->len, ERR); 1094286196Sbapt memcpy(buf + size, s, bp->len); 1095286196Sbapt size += bp->len; 1096286196Sbapt } 1097286196Sbapt REALLOC(buf, n, size + 2, ERR); 1098286196Sbapt memcpy(buf + size, "\n", 2); 1099286196Sbapt if (delete_lines(from, to) < 0) 1100286196Sbapt return ERR; 1101286196Sbapt current_addr = from - 1; 1102286196Sbapt SPL1(); 1103286196Sbapt if (put_sbuf_line(buf) == NULL || 1104286196Sbapt push_undo_stack(UADD, current_addr, current_addr) == NULL) { 1105286196Sbapt SPL0(); 1106286196Sbapt return ERR; 1107286196Sbapt } 1108286196Sbapt modified = 1; 1109286196Sbapt SPL0(); 1110286196Sbapt return 0; 1111286196Sbapt} 1112286196Sbapt 1113286196Sbapt 1114286196Sbapt/* move_lines: move a range of lines */ 1115286196Sbaptint 1116286196Sbaptmove_lines(long addr) 1117286196Sbapt{ 1118286196Sbapt line_t *b1, *a1, *b2, *a2; 1119286196Sbapt long n = INC_MOD(second_addr, addr_last); 1120286196Sbapt long p = first_addr - 1; 1121286196Sbapt int done = (addr == first_addr - 1 || addr == second_addr); 1122286196Sbapt 1123286196Sbapt SPL1(); 1124286196Sbapt if (done) { 1125286196Sbapt a2 = get_addressed_line_node(n); 1126286196Sbapt b2 = get_addressed_line_node(p); 1127286196Sbapt current_addr = second_addr; 1128286196Sbapt } else if (push_undo_stack(UMOV, p, n) == NULL || 1129286196Sbapt push_undo_stack(UMOV, addr, INC_MOD(addr, addr_last)) == NULL) { 1130286196Sbapt SPL0(); 1131286196Sbapt return ERR; 1132286196Sbapt } else { 1133286196Sbapt a1 = get_addressed_line_node(n); 1134286196Sbapt if (addr < first_addr) { 1135286196Sbapt b1 = get_addressed_line_node(p); 1136286196Sbapt b2 = get_addressed_line_node(addr); 1137286196Sbapt /* this get_addressed_line_node last! */ 1138286196Sbapt } else { 1139286196Sbapt b2 = get_addressed_line_node(addr); 1140286196Sbapt b1 = get_addressed_line_node(p); 1141286196Sbapt /* this get_addressed_line_node last! */ 1142286196Sbapt } 1143286196Sbapt a2 = b2->q_forw; 1144286196Sbapt REQUE(b2, b1->q_forw); 1145286196Sbapt REQUE(a1->q_back, a2); 1146286196Sbapt REQUE(b1, a1); 1147286196Sbapt current_addr = addr + ((addr < first_addr) ? 1148286196Sbapt second_addr - first_addr + 1 : 0); 1149286196Sbapt } 1150286196Sbapt if (isglobal) 1151286196Sbapt unset_active_nodes(b2->q_forw, a2); 1152286196Sbapt modified = 1; 1153286196Sbapt SPL0(); 1154286196Sbapt return 0; 1155286196Sbapt} 1156286196Sbapt 1157286196Sbapt 1158286196Sbapt/* copy_lines: copy a range of lines; return status */ 1159286196Sbaptint 1160286196Sbaptcopy_lines(long addr) 1161286196Sbapt{ 1162286196Sbapt line_t *lp, *np = get_addressed_line_node(first_addr); 1163286196Sbapt undo_t *up = NULL; 1164286196Sbapt long n = second_addr - first_addr + 1; 1165286196Sbapt long m = 0; 1166286196Sbapt 1167286196Sbapt current_addr = addr; 1168286196Sbapt if (first_addr <= addr && addr < second_addr) { 1169286196Sbapt n = addr - first_addr + 1; 1170286196Sbapt m = second_addr - addr; 1171286196Sbapt } 1172286196Sbapt for (; n > 0; n=m, m=0, np = get_addressed_line_node(current_addr + 1)) 1173286196Sbapt for (; n-- > 0; np = np->q_forw) { 1174286196Sbapt SPL1(); 1175286196Sbapt if ((lp = dup_line_node(np)) == NULL) { 1176286196Sbapt SPL0(); 1177286196Sbapt return ERR; 1178286196Sbapt } 1179286196Sbapt add_line_node(lp); 1180286196Sbapt if (up) 1181286196Sbapt up->t = lp; 1182286196Sbapt else if ((up = push_undo_stack(UADD, current_addr, 1183286196Sbapt current_addr)) == NULL) { 1184286196Sbapt SPL0(); 1185286196Sbapt return ERR; 1186286196Sbapt } 1187286196Sbapt modified = 1; 1188286196Sbapt SPL0(); 1189286196Sbapt } 1190286196Sbapt return 0; 1191286196Sbapt} 1192286196Sbapt 1193286196Sbapt 1194286196Sbapt/* delete_lines: delete a range of lines */ 1195286196Sbaptint 1196286196Sbaptdelete_lines(long from, long to) 1197286196Sbapt{ 1198286259Sed line_t *n, *p; 1199286196Sbapt 1200286196Sbapt SPL1(); 1201286196Sbapt if (push_undo_stack(UDEL, from, to) == NULL) { 1202286196Sbapt SPL0(); 1203286196Sbapt return ERR; 1204286196Sbapt } 1205286196Sbapt n = get_addressed_line_node(INC_MOD(to, addr_last)); 1206286196Sbapt p = get_addressed_line_node(from - 1); 1207286196Sbapt /* this get_addressed_line_node last! */ 1208286196Sbapt if (isglobal) 1209286196Sbapt unset_active_nodes(p->q_forw, n); 1210286196Sbapt REQUE(p, n); 1211286196Sbapt addr_last -= to - from + 1; 1212286196Sbapt current_addr = from - 1; 1213286196Sbapt modified = 1; 1214286196Sbapt SPL0(); 1215286196Sbapt return 0; 1216286196Sbapt} 1217286196Sbapt 1218286196Sbapt 1219286196Sbapt/* display_lines: print a range of lines to stdout */ 1220286196Sbaptint 1221286196Sbaptdisplay_lines(long from, long to, int gflag) 1222286196Sbapt{ 1223286196Sbapt line_t *bp; 1224286196Sbapt line_t *ep; 1225286196Sbapt char *s; 1226286196Sbapt 1227286196Sbapt if (!from) { 1228286196Sbapt errmsg = "invalid address"; 1229286196Sbapt return ERR; 1230286196Sbapt } 1231286196Sbapt ep = get_addressed_line_node(INC_MOD(to, addr_last)); 1232286196Sbapt bp = get_addressed_line_node(from); 1233286196Sbapt for (; bp != ep; bp = bp->q_forw) { 1234286196Sbapt if ((s = get_sbuf_line(bp)) == NULL) 1235286196Sbapt return ERR; 1236286196Sbapt if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0) 1237286196Sbapt return ERR; 1238286196Sbapt } 1239286196Sbapt return 0; 1240286196Sbapt} 1241286196Sbapt 1242286196Sbapt 1243286196Sbapt#define MAXMARK 26 /* max number of marks */ 1244286196Sbapt 1245286196Sbaptstatic line_t *mark[MAXMARK]; /* line markers */ 1246286196Sbaptstatic int markno; /* line marker count */ 1247286196Sbapt 1248286196Sbapt/* mark_line_node: set a line node mark */ 1249286196Sbaptint 1250286196Sbaptmark_line_node(line_t *lp, int n) 1251286196Sbapt{ 1252286196Sbapt if (!islower((unsigned char)n)) { 1253286196Sbapt errmsg = "invalid mark character"; 1254286196Sbapt return ERR; 1255286196Sbapt } else if (mark[n - 'a'] == NULL) 1256286196Sbapt markno++; 1257286196Sbapt mark[n - 'a'] = lp; 1258286196Sbapt return 0; 1259286196Sbapt} 1260286196Sbapt 1261286196Sbapt 1262286196Sbapt/* get_marked_node_addr: return address of a marked line */ 1263286196Sbaptlong 1264286196Sbaptget_marked_node_addr(int n) 1265286196Sbapt{ 1266286196Sbapt if (!islower((unsigned char)n)) { 1267286196Sbapt errmsg = "invalid mark character"; 1268286196Sbapt return ERR; 1269286196Sbapt } 1270286196Sbapt return get_line_node_addr(mark[n - 'a']); 1271286196Sbapt} 1272286196Sbapt 1273286196Sbapt 1274286196Sbapt/* unmark_line_node: clear line node mark */ 1275286196Sbaptvoid 1276286196Sbaptunmark_line_node(line_t *lp) 1277286196Sbapt{ 1278286196Sbapt int i; 1279286196Sbapt 1280286196Sbapt for (i = 0; markno && i < MAXMARK; i++) 1281286196Sbapt if (mark[i] == lp) { 1282286196Sbapt mark[i] = NULL; 1283286196Sbapt markno--; 1284286196Sbapt } 1285286196Sbapt} 1286286196Sbapt 1287286196Sbapt 1288286196Sbapt/* dup_line_node: return a pointer to a copy of a line node */ 1289286196Sbaptline_t * 1290286196Sbaptdup_line_node(line_t *lp) 1291286196Sbapt{ 1292286196Sbapt line_t *np; 1293286196Sbapt 1294286196Sbapt if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) { 1295286196Sbapt fprintf(stderr, "%s\n", strerror(errno)); 1296286196Sbapt errmsg = "out of memory"; 1297286196Sbapt return NULL; 1298286196Sbapt } 1299286196Sbapt np->seek = lp->seek; 1300286196Sbapt np->len = lp->len; 1301286196Sbapt return np; 1302286196Sbapt} 1303286196Sbapt 1304286196Sbapt 1305109961Sgad/* has_trailing_escape: return the parity of escapes preceding a character 1306109961Sgad in a string */ 1307286196Sbaptint 1308286196Sbapthas_trailing_escape(char *s, char *t) 1309286196Sbapt{ 1310286196Sbapt return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1); 1311286196Sbapt} 1312286196Sbapt 1313286196Sbapt 1314286196Sbapt/* strip_escapes: return copy of escaped string of at most length PATH_MAX */ 1315286196Sbaptchar * 1316286196Sbaptstrip_escapes(char *s) 1317286196Sbapt{ 1318305741Sasomers static char *file = NULL; 1319286196Sbapt static int filesz = 0; 1320286196Sbapt 1321286196Sbapt int i = 0; 1322286196Sbapt 1323286196Sbapt REALLOC(file, filesz, PATH_MAX, NULL); 1324286196Sbapt while (i < filesz - 1 /* Worry about a possible trailing escape */ 1325286196Sbapt && (file[i++] = (*s == '\\') ? *++s : *s)) 1326286196Sbapt s++; 1327286196Sbapt return file; 1328286196Sbapt} 1329286196Sbapt 1330286196Sbapt 1331286196Sbaptvoid 1332109961Sgadsignal_hup(int signo) 1333286196Sbapt{ 1334286196Sbapt if (mutex) 1335286196Sbapt sigflags |= (1 << (signo - 1)); 1336286196Sbapt else 1337286196Sbapt handle_hup(signo); 1338286196Sbapt} 1339286196Sbapt 1340286196Sbapt 1341286196Sbaptvoid 1342286196Sbaptsignal_int(int signo) 1343286196Sbapt{ 1344286196Sbapt if (mutex) 1345286196Sbapt sigflags |= (1 << (signo - 1)); 1346109961Sgad else 1347286196Sbapt handle_int(signo); 1348286196Sbapt} 1349286196Sbapt 1350286196Sbapt 1351286196Sbaptvoid 1352286196Sbapthandle_hup(int signo) 1353286196Sbapt{ 1354286196Sbapt char *hup = NULL; /* hup filename */ 1355286196Sbapt char *s; 1356286198Sbapt char ed_hup[] = "ed.hup"; 1357286198Sbapt int n; 1358286198Sbapt 1359286196Sbapt if (!sigactive) 1360286196Sbapt quit(1); 1361286196Sbapt sigflags &= ~(1 << (signo - 1)); 1362286196Sbapt if (addr_last && write_file(ed_hup, "w", 1, addr_last) < 0 && 1363286196Sbapt (s = getenv("HOME")) != NULL && 1364286196Sbapt (n = strlen(s)) + 8 <= PATH_MAX && /* "ed.hup" + '/' */ 1365286196Sbapt (hup = (char *) malloc(n + 10)) != NULL) { 1366286196Sbapt strcpy(hup, s); 1367286196Sbapt if (hup[n - 1] != '/') 1368286196Sbapt hup[n] = '/', hup[n+1] = '\0'; 1369286196Sbapt strcat(hup, "ed.hup"); 1370286196Sbapt write_file(hup, "w", 1, addr_last); 1371286196Sbapt } 1372286196Sbapt quit(2); 1373286196Sbapt} 1374286196Sbapt 1375286196Sbapt 1376286196Sbaptvoid 1377286196Sbapthandle_int(int signo) 1378286196Sbapt{ 1379286196Sbapt if (!sigactive) 1380286196Sbapt quit(1); 1381286196Sbapt sigflags &= ~(1 << (signo - 1)); 1382286196Sbapt#ifdef _POSIX_SOURCE 1383286196Sbapt siglongjmp(env, -1); 1384286196Sbapt#else 1385286196Sbapt longjmp(env, -1); 1386286196Sbapt#endif 1387286196Sbapt} 1388286196Sbapt 1389286196Sbapt 1390286196Sbaptint cols = 72; /* wrap column */ 1391286196Sbapt 1392109961Sgadvoid 1393286196Sbapthandle_winch(int signo) 1394286196Sbapt{ 1395286196Sbapt int save_errno = errno; 1396286196Sbapt 1397286202Sbapt struct winsize ws; /* window size structure */ 1398286202Sbapt 1399286196Sbapt sigflags &= ~(1 << (signo - 1)); 1400286196Sbapt if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) { 1401286196Sbapt if (ws.ws_row > 2) rows = ws.ws_row - 2; 1402286196Sbapt if (ws.ws_col > 8) cols = ws.ws_col - 8; 1403284110Sbapt } 1404286196Sbapt errno = save_errno; 1405286196Sbapt} 1406286196Sbapt 1407286196Sbapt 1408286196Sbapt/* is_legal_filename: return a legal filename */ 1409286196Sbaptint 1410286196Sbaptis_legal_filename(char *s) 1411286196Sbapt{ 1412286196Sbapt if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) { 1413286196Sbapt errmsg = "shell access restricted"; 1414286196Sbapt return 0; 1415286196Sbapt } 1416286196Sbapt return 1; 1417286196Sbapt} 1418286196Sbapt