11057Salm/* main.c: This file contains the main control and user-interface routines 21057Salm for the ed line editor. */ 31057Salm/*- 41057Salm * Copyright (c) 1993 Andrew Moore, Talke Studio. 51057Salm * All rights reserved. 61057Salm * 71057Salm * Redistribution and use in source and binary forms, with or without 81057Salm * modification, are permitted provided that the following conditions 91057Salm * are met: 101057Salm * 1. Redistributions of source code must retain the above copyright 111057Salm * notice, this list of conditions and the following disclaimer. 121057Salm * 2. Redistributions in binary form must reproduce the above copyright 131057Salm * notice, this list of conditions and the following disclaimer in the 141057Salm * documentation and/or other materials provided with the distribution. 151057Salm * 161057Salm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 171057Salm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 181057Salm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 191057Salm * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 201057Salm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 211057Salm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 221057Salm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 231057Salm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 241057Salm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 251057Salm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 261057Salm * SUCH DAMAGE. 271057Salm */ 281057Salm 291057Salm#ifndef lint 30114433Sobrien#if 0 3181220Smikestatic const char copyright[] = 321057Salm"@(#) Copyright (c) 1993 Andrew Moore, Talke Studio. \n\ 331057Salm All rights reserved.\n"; 34114433Sobrien#endif 351057Salm#endif /* not lint */ 361057Salm 3799109Sobrien#include <sys/cdefs.h> 3899109Sobrien__FBSDID("$FreeBSD$"); 391057Salm 401057Salm/* 411057Salm * CREDITS 421057Salm * 431057Salm * This program is based on the editor algorithm described in 448855Srgrimes * Brian W. Kernighan and P. J. Plauger's book "Software Tools 451057Salm * in Pascal," Addison-Wesley, 1981. 461057Salm * 471057Salm * The buffering algorithm is attributed to Rodney Ruddock of 481057Salm * the University of Guelph, Guelph, Ontario. 491057Salm * 501057Salm * The cbc.c encryption code is adapted from 511057Salm * the bdes program by Matt Bishop of Dartmouth College, 521057Salm * Hanover, NH. 531057Salm * 541057Salm */ 551057Salm 5667183Sbrian#include <sys/types.h> 5767183Sbrian 581057Salm#include <sys/ioctl.h> 591057Salm#include <sys/wait.h> 601057Salm#include <ctype.h> 6127963Ssteve#include <locale.h> 6227963Ssteve#include <pwd.h> 631057Salm#include <setjmp.h> 641057Salm 651057Salm#include "ed.h" 661057Salm 671057Salm 681057Salm#ifdef _POSIX_SOURCE 69241720Sedstatic sigjmp_buf env; 701057Salm#else 71241720Sedstatic jmp_buf env; 721057Salm#endif 731057Salm 741057Salm/* static buffers */ 751057Salmchar stdinbuf[1]; /* stdin buffer */ 76241720Sedstatic char *shcmd; /* shell command buffer */ 77241720Sedstatic int shcmdsz; /* shell command buffer size */ 78241720Sedstatic int shcmdi; /* shell command buffer index */ 791057Salmchar *ibuf; /* ed command-line buffer */ 801057Salmint ibufsz; /* ed command-line buffer size */ 811057Salmchar *ibufp; /* pointer to ed command-line buffer */ 821057Salm 831057Salm/* global flags */ 841057Salmint des = 0; /* if set, use crypt(3) for i/o */ 85241720Sedstatic int garrulous = 0; /* if set, print all error messages */ 861057Salmint isbinary; /* if set, buffer contains ASCII NULs */ 871057Salmint isglobal; /* if set, doing a global command */ 881057Salmint modified; /* if set, buffer modified since last write */ 891057Salmint mutex = 0; /* if set, signals set "sigflags" */ 90241720Sedstatic int red = 0; /* if set, restrict shell/directory access */ 911057Salmint scripted = 0; /* if set, suppress diagnostics */ 921057Salmint sigflags = 0; /* if set, signals received while mutex set */ 93241720Sedstatic int sigactive = 0; /* if set, signal handlers are enabled */ 941057Salm 95241720Sedstatic char old_filename[PATH_MAX] = ""; /* default filename */ 961057Salmlong current_addr; /* current address in editor buffer */ 971057Salmlong addr_last; /* last address in editor buffer */ 981057Salmint lineno; /* script line number */ 99241720Sedstatic const char *prompt; /* command-line prompt */ 100241720Sedstatic const char *dps = "*"; /* default command-line prompt */ 1011057Salm 102241720Sedstatic const char *usage = "usage: %s [-] [-sx] [-p string] [file]\n"; 1031057Salm 1041057Salm/* ed: line editor */ 1051057Salmint 106204711Suqsmain(volatile int argc, char ** volatile argv) 1071057Salm{ 1081057Salm int c, n; 1091057Salm long status = 0; 1101057Salm 11117516Sache (void)setlocale(LC_ALL, ""); 11217516Sache 1131057Salm red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r'; 1141057Salmtop: 11524348Simp while ((c = getopt(argc, argv, "p:sx")) != -1) 1161057Salm switch(c) { 1171057Salm case 'p': /* set prompt */ 1181057Salm prompt = optarg; 1191057Salm break; 1201057Salm case 's': /* run script */ 1211057Salm scripted = 1; 1221057Salm break; 1231057Salm case 'x': /* use crypt */ 1241057Salm#ifdef DES 1251057Salm des = get_keyword(); 1261057Salm#else 1271057Salm fprintf(stderr, "crypt unavailable\n?\n"); 1281057Salm#endif 1291057Salm break; 1301057Salm 1311057Salm default: 132141578Sru fprintf(stderr, usage, red ? "red" : "ed"); 1331057Salm exit(1); 1341057Salm } 1351057Salm argv += optind; 1361057Salm argc -= optind; 1371057Salm if (argc && **argv == '-') { 1381057Salm scripted = 1; 1391057Salm if (argc > 1) { 1401057Salm optind = 1; 1411057Salm goto top; 1421057Salm } 1431057Salm argv++; 1441057Salm argc--; 1451057Salm } 1461057Salm /* assert: reliable signals! */ 1471057Salm#ifdef SIGWINCH 1481057Salm handle_winch(SIGWINCH); 1491057Salm if (isatty(0)) signal(SIGWINCH, handle_winch); 1501057Salm#endif 1511057Salm signal(SIGHUP, signal_hup); 1521057Salm signal(SIGQUIT, SIG_IGN); 1531057Salm signal(SIGINT, signal_int); 1541057Salm#ifdef _POSIX_SOURCE 1557165Sjoerg if ((status = sigsetjmp(env, 1))) 1561057Salm#else 1577165Sjoerg if ((status = setjmp(env))) 1581057Salm#endif 1591057Salm { 1601057Salm fputs("\n?\n", stderr); 16181220Smike errmsg = "interrupt"; 1621057Salm } else { 1631057Salm init_buffers(); 1641057Salm sigactive = 1; /* enable signal handlers */ 1651057Salm if (argc && **argv && is_legal_filename(*argv)) { 1661057Salm if (read_file(*argv, 0) < 0 && !isatty(0)) 1671057Salm quit(2); 1681057Salm else if (**argv != '!') 16959797Sjoe if (strlcpy(old_filename, *argv, sizeof(old_filename)) 17059797Sjoe >= sizeof(old_filename)) 17159797Sjoe quit(2); 1721057Salm } else if (argc) { 1731057Salm fputs("?\n", stderr); 1741057Salm if (**argv == '\0') 17581220Smike errmsg = "invalid filename"; 1761057Salm if (!isatty(0)) 1771057Salm quit(2); 1781057Salm } 1791057Salm } 1801057Salm for (;;) { 1811057Salm if (status < 0 && garrulous) 1821057Salm fprintf(stderr, "%s\n", errmsg); 1831057Salm if (prompt) { 1841057Salm printf("%s", prompt); 1851057Salm fflush(stdout); 1861057Salm } 1871057Salm if ((n = get_tty_line()) < 0) { 1881057Salm status = ERR; 1891057Salm continue; 1901057Salm } else if (n == 0) { 1911057Salm if (modified && !scripted) { 1921057Salm fputs("?\n", stderr); 19381220Smike errmsg = "warning: file modified"; 1941057Salm if (!isatty(0)) { 195228595Sdim if (garrulous) 196228595Sdim fprintf(stderr, 197228595Sdim "script, line %d: %s\n", 198228595Sdim lineno, errmsg); 1991057Salm quit(2); 2001057Salm } 2011057Salm clearerr(stdin); 2021057Salm modified = 0; 2031057Salm status = EMOD; 2041057Salm continue; 2051057Salm } else 2061057Salm quit(0); 2071057Salm } else if (ibuf[n - 1] != '\n') { 2081057Salm /* discard line */ 20981220Smike errmsg = "unexpected end-of-file"; 2101057Salm clearerr(stdin); 2111057Salm status = ERR; 2121057Salm continue; 2131057Salm } 2141057Salm isglobal = 0; 2151057Salm if ((status = extract_addr_range()) >= 0 && 2161057Salm (status = exec_command()) >= 0) 2178855Srgrimes if (!status || 2181057Salm (status = display_lines(current_addr, current_addr, 2191057Salm status)) >= 0) 2201057Salm continue; 2211057Salm switch (status) { 2221057Salm case EOF: 2231057Salm quit(0); 2241057Salm case EMOD: 2251057Salm modified = 0; 2261057Salm fputs("?\n", stderr); /* give warning */ 22781220Smike errmsg = "warning: file modified"; 2281057Salm if (!isatty(0)) { 229228595Sdim if (garrulous) 230228595Sdim fprintf(stderr, "script, line %d: %s\n", 231228595Sdim lineno, errmsg); 2321057Salm quit(2); 2331057Salm } 2341057Salm break; 2351057Salm case FATAL: 236228595Sdim if (!isatty(0)) { 237228595Sdim if (garrulous) 238228595Sdim fprintf(stderr, "script, line %d: %s\n", 239228595Sdim lineno, errmsg); 240228595Sdim } else if (garrulous) 241228595Sdim fprintf(stderr, "%s\n", errmsg); 2421057Salm quit(3); 2431057Salm default: 2441057Salm fputs("?\n", stderr); 2451057Salm if (!isatty(0)) { 246228595Sdim if (garrulous) 247228595Sdim fprintf(stderr, "script, line %d: %s\n", 248228595Sdim lineno, errmsg); 2491057Salm quit(2); 2501057Salm } 2511057Salm break; 2521057Salm } 2531057Salm } 2541057Salm /*NOTREACHED*/ 2551057Salm} 2561057Salm 257241720Sedlong first_addr, second_addr; 258241720Sedstatic long addr_cnt; 2591057Salm 2608855Srgrimes/* extract_addr_range: get line addresses from the command buffer until an 2611057Salm illegal address is seen; return status */ 2621057Salmint 26390109Simpextract_addr_range(void) 2641057Salm{ 2651057Salm long addr; 2661057Salm 2671057Salm addr_cnt = 0; 2681057Salm first_addr = second_addr = current_addr; 2691057Salm while ((addr = next_addr()) >= 0) { 2701057Salm addr_cnt++; 2711057Salm first_addr = second_addr; 2721057Salm second_addr = addr; 2731057Salm if (*ibufp != ',' && *ibufp != ';') 2741057Salm break; 2751057Salm else if (*ibufp++ == ';') 2761057Salm current_addr = addr; 2771057Salm } 2781057Salm if ((addr_cnt = min(addr_cnt, 2)) == 1 || second_addr != addr) 2791057Salm first_addr = second_addr; 2801057Salm return (addr == ERR) ? ERR : 0; 2811057Salm} 2821057Salm 2831057Salm 28417516Sache#define SKIP_BLANKS() while (isspace((unsigned char)*ibufp) && *ibufp != '\n') ibufp++ 2851057Salm 28681220Smike#define MUST_BE_FIRST() do { \ 28781220Smike if (!first) { \ 28881220Smike errmsg = "invalid address"; \ 28981220Smike return ERR; \ 29081220Smike } \ 291161427Simp} while (0) 2921057Salm 2931057Salm/* next_addr: return the next line address in the command buffer */ 2941057Salmlong 29590109Simpnext_addr(void) 2961057Salm{ 29781220Smike const char *hd; 2981057Salm long addr = current_addr; 2991057Salm long n; 3001057Salm int first = 1; 3011057Salm int c; 3021057Salm 3031057Salm SKIP_BLANKS(); 3041057Salm for (hd = ibufp;; first = 0) 3051057Salm switch (c = *ibufp) { 3061057Salm case '+': 3071057Salm case '\t': 3081057Salm case ' ': 3091057Salm case '-': 3101057Salm case '^': 3111057Salm ibufp++; 3121057Salm SKIP_BLANKS(); 31317516Sache if (isdigit((unsigned char)*ibufp)) { 3141057Salm STRTOL(n, ibufp); 3151057Salm addr += (c == '-' || c == '^') ? -n : n; 31617516Sache } else if (!isspace((unsigned char)c)) 3171057Salm addr += (c == '-' || c == '^') ? -1 : 1; 3181057Salm break; 3191057Salm case '0': case '1': case '2': 3201057Salm case '3': case '4': case '5': 3211057Salm case '6': case '7': case '8': case '9': 3221057Salm MUST_BE_FIRST(); 3231057Salm STRTOL(addr, ibufp); 3241057Salm break; 3251057Salm case '.': 3261057Salm case '$': 3271057Salm MUST_BE_FIRST(); 3281057Salm ibufp++; 3291057Salm addr = (c == '.') ? current_addr : addr_last; 3301057Salm break; 3311057Salm case '/': 3321057Salm case '?': 3331057Salm MUST_BE_FIRST(); 3341057Salm if ((addr = get_matching_node_addr( 3351057Salm get_compiled_pattern(), c == '/')) < 0) 3361057Salm return ERR; 3371057Salm else if (c == *ibufp) 3381057Salm ibufp++; 3391057Salm break; 3401057Salm case '\'': 3411057Salm MUST_BE_FIRST(); 3421057Salm ibufp++; 3431057Salm if ((addr = get_marked_node_addr(*ibufp++)) < 0) 3441057Salm return ERR; 3451057Salm break; 3461057Salm case '%': 3471057Salm case ',': 3481057Salm case ';': 3491057Salm if (first) { 3501057Salm ibufp++; 3511057Salm addr_cnt++; 3521057Salm second_addr = (c == ';') ? current_addr : 1; 3531057Salm addr = addr_last; 3541057Salm break; 3551057Salm } 356102410Scharnier /* FALLTHROUGH */ 3571057Salm default: 3581057Salm if (ibufp == hd) 3591057Salm return EOF; 3601057Salm else if (addr < 0 || addr_last < addr) { 36181220Smike errmsg = "invalid address"; 3621057Salm return ERR; 3631057Salm } else 3641057Salm return addr; 3651057Salm } 3661057Salm /* NOTREACHED */ 3671057Salm} 3681057Salm 3691057Salm 3701057Salm#ifdef BACKWARDS 3711057Salm/* GET_THIRD_ADDR: get a legal address from the command buffer */ 3721057Salm#define GET_THIRD_ADDR(addr) \ 3731057Salm{ \ 3741057Salm long ol1, ol2; \ 3751057Salm\ 3761057Salm ol1 = first_addr, ol2 = second_addr; \ 3771057Salm if (extract_addr_range() < 0) \ 3781057Salm return ERR; \ 3791057Salm else if (addr_cnt == 0) { \ 38081220Smike errmsg = "destination expected"; \ 3811057Salm return ERR; \ 3821057Salm } else if (second_addr < 0 || addr_last < second_addr) { \ 38381220Smike errmsg = "invalid address"; \ 3841057Salm return ERR; \ 3851057Salm } \ 3861057Salm addr = second_addr; \ 3871057Salm first_addr = ol1, second_addr = ol2; \ 3881057Salm} 3891057Salm#else /* BACKWARDS */ 3901057Salm/* GET_THIRD_ADDR: get a legal address from the command buffer */ 3911057Salm#define GET_THIRD_ADDR(addr) \ 3921057Salm{ \ 3931057Salm long ol1, ol2; \ 3941057Salm\ 3951057Salm ol1 = first_addr, ol2 = second_addr; \ 3961057Salm if (extract_addr_range() < 0) \ 3971057Salm return ERR; \ 3981057Salm if (second_addr < 0 || addr_last < second_addr) { \ 39981220Smike errmsg = "invalid address"; \ 4001057Salm return ERR; \ 4011057Salm } \ 4021057Salm addr = second_addr; \ 4031057Salm first_addr = ol1, second_addr = ol2; \ 4041057Salm} 4051057Salm#endif 4061057Salm 4071057Salm 4081057Salm/* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */ 4091057Salm#define GET_COMMAND_SUFFIX() { \ 4101057Salm int done = 0; \ 4111057Salm do { \ 4121057Salm switch(*ibufp) { \ 4131057Salm case 'p': \ 4141057Salm gflag |= GPR, ibufp++; \ 4151057Salm break; \ 4161057Salm case 'l': \ 4171057Salm gflag |= GLS, ibufp++; \ 4181057Salm break; \ 4191057Salm case 'n': \ 4201057Salm gflag |= GNP, ibufp++; \ 4211057Salm break; \ 4221057Salm default: \ 4231057Salm done++; \ 4241057Salm } \ 4251057Salm } while (!done); \ 4261057Salm if (*ibufp++ != '\n') { \ 42781220Smike errmsg = "invalid command suffix"; \ 4281057Salm return ERR; \ 4291057Salm } \ 4301057Salm} 4311057Salm 4321057Salm 4331057Salm/* sflags */ 4341057Salm#define SGG 001 /* complement previous global substitute suffix */ 4351057Salm#define SGP 002 /* complement previous print suffix */ 4361057Salm#define SGR 004 /* use last regex instead of last pat */ 4371057Salm#define SGF 010 /* repeat last substitution */ 4381057Salm 4391057Salmint patlock = 0; /* if set, pattern not freed by get_compiled_pattern() */ 4401057Salm 4411057Salmlong rows = 22; /* scroll length: ws_row - 2 */ 4421057Salm 4431057Salm/* exec_command: execute the next command in command buffer; return print 4441057Salm request, if any */ 4451057Salmint 44690109Simpexec_command(void) 4471057Salm{ 4481057Salm static pattern_t *pat = NULL; 4491057Salm static int sgflag = 0; 45038161Sdfr static long sgnum = 0; 4511057Salm 4521057Salm pattern_t *tpat; 4531057Salm char *fnp; 4541057Salm int gflag = 0; 4551057Salm int sflags = 0; 4561057Salm long addr = 0; 4571057Salm int n = 0; 4581057Salm int c; 4591057Salm 4601057Salm SKIP_BLANKS(); 4611057Salm switch(c = *ibufp++) { 4621057Salm case 'a': 4631057Salm GET_COMMAND_SUFFIX(); 4641057Salm if (!isglobal) clear_undo_stack(); 4651057Salm if (append_lines(second_addr) < 0) 4661057Salm return ERR; 4671057Salm break; 4681057Salm case 'c': 4691057Salm if (check_addr_range(current_addr, current_addr) < 0) 4701057Salm return ERR; 4711057Salm GET_COMMAND_SUFFIX(); 4721057Salm if (!isglobal) clear_undo_stack(); 4731057Salm if (delete_lines(first_addr, second_addr) < 0 || 4741057Salm append_lines(current_addr) < 0) 4751057Salm return ERR; 4761057Salm break; 4771057Salm case 'd': 4781057Salm if (check_addr_range(current_addr, current_addr) < 0) 4791057Salm return ERR; 4801057Salm GET_COMMAND_SUFFIX(); 4811057Salm if (!isglobal) clear_undo_stack(); 4821057Salm if (delete_lines(first_addr, second_addr) < 0) 4831057Salm return ERR; 4841057Salm else if ((addr = INC_MOD(current_addr, addr_last)) != 0) 4851057Salm current_addr = addr; 4861057Salm break; 4871057Salm case 'e': 4881057Salm if (modified && !scripted) 4891057Salm return EMOD; 490102410Scharnier /* FALLTHROUGH */ 4911057Salm case 'E': 4921057Salm if (addr_cnt > 0) { 49381220Smike errmsg = "unexpected address"; 4941057Salm return ERR; 49517516Sache } else if (!isspace((unsigned char)*ibufp)) { 49681220Smike errmsg = "unexpected command suffix"; 4971057Salm return ERR; 4981057Salm } else if ((fnp = get_filename()) == NULL) 4991057Salm return ERR; 5001057Salm GET_COMMAND_SUFFIX(); 5011057Salm if (delete_lines(1, addr_last) < 0) 5021057Salm return ERR; 5031057Salm clear_undo_stack(); 5041057Salm if (close_sbuf() < 0) 5051057Salm return ERR; 5061057Salm else if (open_sbuf() < 0) 5071057Salm return FATAL; 5081057Salm if (*fnp && *fnp != '!') strcpy(old_filename, fnp); 5091057Salm#ifdef BACKWARDS 5101057Salm if (*fnp == '\0' && *old_filename == '\0') { 51181220Smike errmsg = "no current filename"; 5121057Salm return ERR; 5131057Salm } 5141057Salm#endif 5151057Salm if (read_file(*fnp ? fnp : old_filename, 0) < 0) 5161057Salm return ERR; 5171057Salm clear_undo_stack(); 5181057Salm modified = 0; 5191057Salm u_current_addr = u_addr_last = -1; 5201057Salm break; 5211057Salm case 'f': 5221057Salm if (addr_cnt > 0) { 52381220Smike errmsg = "unexpected address"; 5241057Salm return ERR; 52517516Sache } else if (!isspace((unsigned char)*ibufp)) { 52681220Smike errmsg = "unexpected command suffix"; 5271057Salm return ERR; 5281057Salm } else if ((fnp = get_filename()) == NULL) 5291057Salm return ERR; 5301057Salm else if (*fnp == '!') { 53181220Smike errmsg = "invalid redirection"; 5321057Salm return ERR; 5331057Salm } 5341057Salm GET_COMMAND_SUFFIX(); 5351057Salm if (*fnp) strcpy(old_filename, fnp); 5361057Salm printf("%s\n", strip_escapes(old_filename)); 5371057Salm break; 5381057Salm case 'g': 5391057Salm case 'v': 5401057Salm case 'G': 5411057Salm case 'V': 5421057Salm if (isglobal) { 54381220Smike errmsg = "cannot nest global commands"; 5441057Salm return ERR; 5451057Salm } else if (check_addr_range(1, addr_last) < 0) 5461057Salm return ERR; 5471057Salm else if (build_active_list(c == 'g' || c == 'G') < 0) 5481057Salm return ERR; 5497165Sjoerg else if ((n = (c == 'G' || c == 'V'))) 5501057Salm GET_COMMAND_SUFFIX(); 5511057Salm isglobal++; 5521057Salm if (exec_global(n, gflag) < 0) 5538855Srgrimes return ERR; 5541057Salm break; 5551057Salm case 'h': 5561057Salm if (addr_cnt > 0) { 55781220Smike errmsg = "unexpected address"; 5581057Salm return ERR; 5591057Salm } 5601057Salm GET_COMMAND_SUFFIX(); 5611057Salm if (*errmsg) fprintf(stderr, "%s\n", errmsg); 5621057Salm break; 5631057Salm case 'H': 5641057Salm if (addr_cnt > 0) { 56581220Smike errmsg = "unexpected address"; 5661057Salm return ERR; 5671057Salm } 5681057Salm GET_COMMAND_SUFFIX(); 5691057Salm if ((garrulous = 1 - garrulous) && *errmsg) 5701057Salm fprintf(stderr, "%s\n", errmsg); 5711057Salm break; 5721057Salm case 'i': 5731057Salm if (second_addr == 0) { 57481220Smike errmsg = "invalid address"; 5751057Salm return ERR; 5761057Salm } 5771057Salm GET_COMMAND_SUFFIX(); 5781057Salm if (!isglobal) clear_undo_stack(); 5791057Salm if (append_lines(second_addr - 1) < 0) 5801057Salm return ERR; 5811057Salm break; 5821057Salm case 'j': 5831057Salm if (check_addr_range(current_addr, current_addr + 1) < 0) 5841057Salm return ERR; 5851057Salm GET_COMMAND_SUFFIX(); 5861057Salm if (!isglobal) clear_undo_stack(); 5871057Salm if (first_addr != second_addr && 5881057Salm join_lines(first_addr, second_addr) < 0) 5891057Salm return ERR; 5901057Salm break; 5911057Salm case 'k': 5921057Salm c = *ibufp++; 5931057Salm if (second_addr == 0) { 59481220Smike errmsg = "invalid address"; 5951057Salm return ERR; 5961057Salm } 5971057Salm GET_COMMAND_SUFFIX(); 5981057Salm if (mark_line_node(get_addressed_line_node(second_addr), c) < 0) 5991057Salm return ERR; 6001057Salm break; 6011057Salm case 'l': 6021057Salm if (check_addr_range(current_addr, current_addr) < 0) 6031057Salm return ERR; 6041057Salm GET_COMMAND_SUFFIX(); 6051057Salm if (display_lines(first_addr, second_addr, gflag | GLS) < 0) 6061057Salm return ERR; 6071057Salm gflag = 0; 6081057Salm break; 6091057Salm case 'm': 6101057Salm if (check_addr_range(current_addr, current_addr) < 0) 6111057Salm return ERR; 6121057Salm GET_THIRD_ADDR(addr); 6131057Salm if (first_addr <= addr && addr < second_addr) { 61481220Smike errmsg = "invalid destination"; 6151057Salm return ERR; 6161057Salm } 6171057Salm GET_COMMAND_SUFFIX(); 6181057Salm if (!isglobal) clear_undo_stack(); 6191057Salm if (move_lines(addr) < 0) 6201057Salm return ERR; 6211057Salm break; 6221057Salm case 'n': 6231057Salm if (check_addr_range(current_addr, current_addr) < 0) 6241057Salm return ERR; 6251057Salm GET_COMMAND_SUFFIX(); 6261057Salm if (display_lines(first_addr, second_addr, gflag | GNP) < 0) 6271057Salm return ERR; 6281057Salm gflag = 0; 6291057Salm break; 6301057Salm case 'p': 6311057Salm if (check_addr_range(current_addr, current_addr) < 0) 6321057Salm return ERR; 6331057Salm GET_COMMAND_SUFFIX(); 6341057Salm if (display_lines(first_addr, second_addr, gflag | GPR) < 0) 6351057Salm return ERR; 6361057Salm gflag = 0; 6371057Salm break; 6381057Salm case 'P': 6391057Salm if (addr_cnt > 0) { 64081220Smike errmsg = "unexpected address"; 6411057Salm return ERR; 6421057Salm } 6431057Salm GET_COMMAND_SUFFIX(); 6441057Salm prompt = prompt ? NULL : optarg ? optarg : dps; 6451057Salm break; 6461057Salm case 'q': 6471057Salm case 'Q': 6481057Salm if (addr_cnt > 0) { 64981220Smike errmsg = "unexpected address"; 6501057Salm return ERR; 6511057Salm } 6521057Salm GET_COMMAND_SUFFIX(); 6531057Salm gflag = (modified && !scripted && c == 'q') ? EMOD : EOF; 6541057Salm break; 6551057Salm case 'r': 65617516Sache if (!isspace((unsigned char)*ibufp)) { 65781220Smike errmsg = "unexpected command suffix"; 6581057Salm return ERR; 6591057Salm } else if (addr_cnt == 0) 6601057Salm second_addr = addr_last; 6611057Salm if ((fnp = get_filename()) == NULL) 6621057Salm return ERR; 6631057Salm GET_COMMAND_SUFFIX(); 6641057Salm if (!isglobal) clear_undo_stack(); 6651057Salm if (*old_filename == '\0' && *fnp != '!') 6661057Salm strcpy(old_filename, fnp); 6671057Salm#ifdef BACKWARDS 6681057Salm if (*fnp == '\0' && *old_filename == '\0') { 66981220Smike errmsg = "no current filename"; 6701057Salm return ERR; 6711057Salm } 6721057Salm#endif 6731057Salm if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0) 6741057Salm return ERR; 6751057Salm else if (addr && addr != addr_last) 6761057Salm modified = 1; 6771057Salm break; 6781057Salm case 's': 6791057Salm do { 6801057Salm switch(*ibufp) { 6811057Salm case '\n': 6821057Salm sflags |=SGF; 6831057Salm break; 6841057Salm case 'g': 6851057Salm sflags |= SGG; 6861057Salm ibufp++; 6871057Salm break; 6881057Salm case 'p': 6891057Salm sflags |= SGP; 6901057Salm ibufp++; 6911057Salm break; 6921057Salm case 'r': 6931057Salm sflags |= SGR; 6941057Salm ibufp++; 6951057Salm break; 6968855Srgrimes case '0': case '1': case '2': case '3': case '4': 6971057Salm case '5': case '6': case '7': case '8': case '9': 6981057Salm STRTOL(sgnum, ibufp); 6991057Salm sflags |= SGF; 7001057Salm sgflag &= ~GSG; /* override GSG */ 7011057Salm break; 7021057Salm default: 7031057Salm if (sflags) { 70481220Smike errmsg = "invalid command suffix"; 7051057Salm return ERR; 7061057Salm } 7071057Salm } 7081057Salm } while (sflags && *ibufp != '\n'); 7091057Salm if (sflags && !pat) { 71081220Smike errmsg = "no previous substitution"; 7111057Salm return ERR; 7121057Salm } else if (sflags & SGG) 7131057Salm sgnum = 0; /* override numeric arg */ 7141057Salm if (*ibufp != '\n' && *(ibufp + 1) == '\n') { 71581220Smike errmsg = "invalid pattern delimiter"; 7161057Salm return ERR; 7171057Salm } 7181057Salm tpat = pat; 7191057Salm SPL1(); 7201057Salm if ((!sflags || (sflags & SGR)) && 7211057Salm (tpat = get_compiled_pattern()) == NULL) { 7221057Salm SPL0(); 7231057Salm return ERR; 7241057Salm } else if (tpat != pat) { 7251057Salm if (pat) { 7261057Salm regfree(pat); 7271057Salm free(pat); 7281057Salm } 7291057Salm pat = tpat; 7301057Salm patlock = 1; /* reserve pattern */ 7311057Salm } 7321057Salm SPL0(); 7331057Salm if (!sflags && extract_subst_tail(&sgflag, &sgnum) < 0) 7341057Salm return ERR; 7351057Salm else if (isglobal) 7361057Salm sgflag |= GLB; 7371057Salm else 7381057Salm sgflag &= ~GLB; 7391057Salm if (sflags & SGG) 7401057Salm sgflag ^= GSG; 7411057Salm if (sflags & SGP) 7421057Salm sgflag ^= GPR, sgflag &= ~(GLS | GNP); 7431057Salm do { 7441057Salm switch(*ibufp) { 7451057Salm case 'p': 7461057Salm sgflag |= GPR, ibufp++; 7471057Salm break; 7481057Salm case 'l': 7491057Salm sgflag |= GLS, ibufp++; 7501057Salm break; 7511057Salm case 'n': 7521057Salm sgflag |= GNP, ibufp++; 7531057Salm break; 7541057Salm default: 7551057Salm n++; 7561057Salm } 7571057Salm } while (!n); 7581057Salm if (check_addr_range(current_addr, current_addr) < 0) 7591057Salm return ERR; 7601057Salm GET_COMMAND_SUFFIX(); 7611057Salm if (!isglobal) clear_undo_stack(); 7621057Salm if (search_and_replace(pat, sgflag, sgnum) < 0) 7631057Salm return ERR; 7641057Salm break; 7651057Salm case 't': 7661057Salm if (check_addr_range(current_addr, current_addr) < 0) 7671057Salm return ERR; 7681057Salm GET_THIRD_ADDR(addr); 7691057Salm GET_COMMAND_SUFFIX(); 7701057Salm if (!isglobal) clear_undo_stack(); 7711057Salm if (copy_lines(addr) < 0) 7721057Salm return ERR; 7731057Salm break; 7741057Salm case 'u': 7751057Salm if (addr_cnt > 0) { 77681220Smike errmsg = "unexpected address"; 7771057Salm return ERR; 7781057Salm } 7791057Salm GET_COMMAND_SUFFIX(); 7801057Salm if (pop_undo_stack() < 0) 7811057Salm return ERR; 7821057Salm break; 7831057Salm case 'w': 7841057Salm case 'W': 7851057Salm if ((n = *ibufp) == 'q' || n == 'Q') { 7861057Salm gflag = EOF; 7871057Salm ibufp++; 7881057Salm } 78917516Sache if (!isspace((unsigned char)*ibufp)) { 79081220Smike errmsg = "unexpected command suffix"; 7911057Salm return ERR; 7921057Salm } else if ((fnp = get_filename()) == NULL) 7931057Salm return ERR; 7941057Salm if (addr_cnt == 0 && !addr_last) 7951057Salm first_addr = second_addr = 0; 7961057Salm else if (check_addr_range(1, addr_last) < 0) 7971057Salm return ERR; 7981057Salm GET_COMMAND_SUFFIX(); 7991057Salm if (*old_filename == '\0' && *fnp != '!') 8001057Salm strcpy(old_filename, fnp); 8011057Salm#ifdef BACKWARDS 8021057Salm if (*fnp == '\0' && *old_filename == '\0') { 80381220Smike errmsg = "no current filename"; 8041057Salm return ERR; 8051057Salm } 8061057Salm#endif 8078855Srgrimes if ((addr = write_file(*fnp ? fnp : old_filename, 8081057Salm (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0) 8091057Salm return ERR; 8101057Salm else if (addr == addr_last) 8111057Salm modified = 0; 8121057Salm else if (modified && !scripted && n == 'q') 8131057Salm gflag = EMOD; 8141057Salm break; 8151057Salm case 'x': 8161057Salm if (addr_cnt > 0) { 81781220Smike errmsg = "unexpected address"; 8181057Salm return ERR; 8191057Salm } 8201057Salm GET_COMMAND_SUFFIX(); 8211057Salm#ifdef DES 8221057Salm des = get_keyword(); 823140344Scharnier break; 8241057Salm#else 82581220Smike errmsg = "crypt unavailable"; 8261057Salm return ERR; 8271057Salm#endif 8281057Salm case 'z': 8291057Salm#ifdef BACKWARDS 8301057Salm if (check_addr_range(first_addr = 1, current_addr + 1) < 0) 8311057Salm#else 8321057Salm if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0) 8331057Salm#endif 8341057Salm return ERR; 8351057Salm else if ('0' < *ibufp && *ibufp <= '9') 8361057Salm STRTOL(rows, ibufp); 8371057Salm GET_COMMAND_SUFFIX(); 8381057Salm if (display_lines(second_addr, min(addr_last, 8391057Salm second_addr + rows), gflag) < 0) 8401057Salm return ERR; 8411057Salm gflag = 0; 8421057Salm break; 8431057Salm case '=': 8441057Salm GET_COMMAND_SUFFIX(); 8457165Sjoerg printf("%ld\n", addr_cnt ? second_addr : addr_last); 8461057Salm break; 8471057Salm case '!': 8481057Salm if (addr_cnt > 0) { 84981220Smike errmsg = "unexpected address"; 8501057Salm return ERR; 8511057Salm } else if ((sflags = get_shell_command()) < 0) 8521057Salm return ERR; 8531057Salm GET_COMMAND_SUFFIX(); 8541057Salm if (sflags) printf("%s\n", shcmd + 1); 8551057Salm system(shcmd + 1); 8561057Salm if (!scripted) printf("!\n"); 8571057Salm break; 8581057Salm case '\n': 8591057Salm#ifdef BACKWARDS 8601057Salm if (check_addr_range(first_addr = 1, current_addr + 1) < 0 8611057Salm#else 8621057Salm if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0 8631057Salm#endif 8641057Salm || display_lines(second_addr, second_addr, 0) < 0) 8651057Salm return ERR; 8661057Salm break; 8671057Salm default: 86881220Smike errmsg = "unknown command"; 8691057Salm return ERR; 8701057Salm } 8711057Salm return gflag; 8721057Salm} 8731057Salm 8741057Salm 8751057Salm/* check_addr_range: return status of address range check */ 8761057Salmint 87790109Simpcheck_addr_range(long n, long m) 8781057Salm{ 8791057Salm if (addr_cnt == 0) { 8801057Salm first_addr = n; 8811057Salm second_addr = m; 8821057Salm } 8831057Salm if (first_addr > second_addr || 1 > first_addr || 8841057Salm second_addr > addr_last) { 88581220Smike errmsg = "invalid address"; 8861057Salm return ERR; 8871057Salm } 8881057Salm return 0; 8891057Salm} 8901057Salm 8911057Salm 8928855Srgrimes/* get_matching_node_addr: return the address of the next line matching a 8931057Salm pattern in a given direction. wrap around begin/end of editor buffer if 8941057Salm necessary */ 8951057Salmlong 89690109Simpget_matching_node_addr(pattern_t *pat, int dir) 8971057Salm{ 8981057Salm char *s; 8991057Salm long n = current_addr; 9001057Salm line_t *lp; 9011057Salm 9021057Salm if (!pat) return ERR; 9031057Salm do { 9047165Sjoerg if ((n = dir ? INC_MOD(n, addr_last) : DEC_MOD(n, addr_last))) { 9051057Salm lp = get_addressed_line_node(n); 9061057Salm if ((s = get_sbuf_line(lp)) == NULL) 9071057Salm return ERR; 9081057Salm if (isbinary) 9091057Salm NUL_TO_NEWLINE(s, lp->len); 9101057Salm if (!regexec(pat, s, 0, NULL, 0)) 9111057Salm return n; 9127165Sjoerg } 9131057Salm } while (n != current_addr); 91481220Smike errmsg = "no match"; 9151057Salm return ERR; 9161057Salm} 9171057Salm 9181057Salm 9191057Salm/* get_filename: return pointer to copy of filename in the command buffer */ 9201057Salmchar * 92190109Simpget_filename(void) 9221057Salm{ 9231057Salm static char *file = NULL; 9241057Salm static int filesz = 0; 9251057Salm 9261057Salm int n; 9271057Salm 9281057Salm if (*ibufp != '\n') { 9291057Salm SKIP_BLANKS(); 9301057Salm if (*ibufp == '\n') { 93181220Smike errmsg = "invalid filename"; 9321057Salm return NULL; 9331057Salm } else if ((ibufp = get_extended_line(&n, 1)) == NULL) 9341057Salm return NULL; 9351057Salm else if (*ibufp == '!') { 9361057Salm ibufp++; 9371057Salm if ((n = get_shell_command()) < 0) 9381057Salm return NULL; 93977407Simp if (n) 94077407Simp printf("%s\n", shcmd + 1); 9411057Salm return shcmd; 94277407Simp } else if (n > PATH_MAX - 1) { 94381220Smike errmsg = "filename too long"; 9441057Salm return NULL; 9451057Salm } 9461057Salm } 9471057Salm#ifndef BACKWARDS 9481057Salm else if (*old_filename == '\0') { 94981220Smike errmsg = "no current filename"; 9501057Salm return NULL; 9511057Salm } 9521057Salm#endif 95377407Simp REALLOC(file, filesz, PATH_MAX, NULL); 9541057Salm for (n = 0; *ibufp != '\n';) 9551057Salm file[n++] = *ibufp++; 9561057Salm file[n] = '\0'; 9571057Salm return is_legal_filename(file) ? file : NULL; 9581057Salm} 9591057Salm 9601057Salm 9611057Salm/* get_shell_command: read a shell command from stdin; return substitution 9621057Salm status */ 9631057Salmint 96490109Simpget_shell_command(void) 9651057Salm{ 9661057Salm static char *buf = NULL; 9671057Salm static int n = 0; 9681057Salm 9691057Salm char *s; /* substitution char pointer */ 9701057Salm int i = 0; 9711057Salm int j = 0; 9721057Salm 9731057Salm if (red) { 97481220Smike errmsg = "shell access restricted"; 9751057Salm return ERR; 9761057Salm } else if ((s = ibufp = get_extended_line(&j, 1)) == NULL) 9771057Salm return ERR; 9781057Salm REALLOC(buf, n, j + 1, ERR); 9791057Salm buf[i++] = '!'; /* prefix command w/ bang */ 9801057Salm while (*ibufp != '\n') 9811057Salm switch (*ibufp) { 9821057Salm default: 9831057Salm REALLOC(buf, n, i + 2, ERR); 9841057Salm buf[i++] = *ibufp; 9851057Salm if (*ibufp++ == '\\') 9861057Salm buf[i++] = *ibufp++; 9871057Salm break; 9881057Salm case '!': 9891057Salm if (s != ibufp) { 9901057Salm REALLOC(buf, n, i + 1, ERR); 9911057Salm buf[i++] = *ibufp++; 9921057Salm } 9931057Salm#ifdef BACKWARDS 9941057Salm else if (shcmd == NULL || *(shcmd + 1) == '\0') 9951057Salm#else 9961057Salm else if (shcmd == NULL) 9971057Salm#endif 9981057Salm { 99981220Smike errmsg = "no previous command"; 10001057Salm return ERR; 10011057Salm } else { 10021057Salm REALLOC(buf, n, i + shcmdi, ERR); 10031057Salm for (s = shcmd + 1; s < shcmd + shcmdi;) 10041057Salm buf[i++] = *s++; 10051057Salm s = ibufp++; 10061057Salm } 10071057Salm break; 10081057Salm case '%': 10091057Salm if (*old_filename == '\0') { 101081220Smike errmsg = "no current filename"; 10111057Salm return ERR; 10121057Salm } 10131057Salm j = strlen(s = strip_escapes(old_filename)); 10141057Salm REALLOC(buf, n, i + j, ERR); 10151057Salm while (j--) 10161057Salm buf[i++] = *s++; 10171057Salm s = ibufp++; 10181057Salm break; 10191057Salm } 10201057Salm REALLOC(shcmd, shcmdsz, i + 1, ERR); 10211057Salm memcpy(shcmd, buf, i); 10221057Salm shcmd[shcmdi = i] = '\0'; 10231057Salm return *s == '!' || *s == '%'; 10241057Salm} 10251057Salm 10261057Salm 10271057Salm/* append_lines: insert text from stdin to after line n; stop when either a 10281057Salm single period is read or EOF; return status */ 10291057Salmint 103090109Simpappend_lines(long n) 10311057Salm{ 10321057Salm int l; 103381220Smike const char *lp = ibuf; 103481220Smike const char *eot; 10351057Salm undo_t *up = NULL; 10361057Salm 10371057Salm for (current_addr = n;;) { 10381057Salm if (!isglobal) { 10391057Salm if ((l = get_tty_line()) < 0) 10401057Salm return ERR; 10411057Salm else if (l == 0 || ibuf[l - 1] != '\n') { 10421057Salm clearerr(stdin); 10431057Salm return l ? EOF : 0; 10441057Salm } 10451057Salm lp = ibuf; 10461057Salm } else if (*(lp = ibufp) == '\0') 10471057Salm return 0; 10481057Salm else { 10491057Salm while (*ibufp++ != '\n') 10501057Salm ; 10511057Salm l = ibufp - lp; 10521057Salm } 10531057Salm if (l == 2 && lp[0] == '.' && lp[1] == '\n') { 10541057Salm return 0; 10551057Salm } 10561057Salm eot = lp + l; 10571057Salm SPL1(); 10581057Salm do { 10591057Salm if ((lp = put_sbuf_line(lp)) == NULL) { 10601057Salm SPL0(); 10611057Salm return ERR; 10621057Salm } else if (up) 10631057Salm up->t = get_addressed_line_node(current_addr); 10641057Salm else if ((up = push_undo_stack(UADD, current_addr, 10651057Salm current_addr)) == NULL) { 10661057Salm SPL0(); 10671057Salm return ERR; 10681057Salm } 10691057Salm } while (lp != eot); 10701057Salm modified = 1; 10711057Salm SPL0(); 10721057Salm } 10731057Salm /* NOTREACHED */ 10741057Salm} 10751057Salm 10761057Salm 10771057Salm/* join_lines: replace a range of lines with the joined text of those lines */ 10781057Salmint 107990109Simpjoin_lines(long from, long to) 10801057Salm{ 10811057Salm static char *buf = NULL; 10821057Salm static int n; 10831057Salm 10841057Salm char *s; 10851057Salm int size = 0; 10861057Salm line_t *bp, *ep; 10871057Salm 10881057Salm ep = get_addressed_line_node(INC_MOD(to, addr_last)); 10891057Salm bp = get_addressed_line_node(from); 10901057Salm for (; bp != ep; bp = bp->q_forw) { 10911057Salm if ((s = get_sbuf_line(bp)) == NULL) 10921057Salm return ERR; 10931057Salm REALLOC(buf, n, size + bp->len, ERR); 10941057Salm memcpy(buf + size, s, bp->len); 10951057Salm size += bp->len; 10961057Salm } 10971057Salm REALLOC(buf, n, size + 2, ERR); 10981057Salm memcpy(buf + size, "\n", 2); 10991057Salm if (delete_lines(from, to) < 0) 11001057Salm return ERR; 11011057Salm current_addr = from - 1; 11021057Salm SPL1(); 11031057Salm if (put_sbuf_line(buf) == NULL || 11041057Salm push_undo_stack(UADD, current_addr, current_addr) == NULL) { 11051057Salm SPL0(); 11061057Salm return ERR; 11071057Salm } 11081057Salm modified = 1; 11091057Salm SPL0(); 11101057Salm return 0; 11111057Salm} 11121057Salm 11131057Salm 11141057Salm/* move_lines: move a range of lines */ 11151057Salmint 111690109Simpmove_lines(long addr) 11171057Salm{ 11181057Salm line_t *b1, *a1, *b2, *a2; 11191057Salm long n = INC_MOD(second_addr, addr_last); 11201057Salm long p = first_addr - 1; 11211057Salm int done = (addr == first_addr - 1 || addr == second_addr); 11221057Salm 11231057Salm SPL1(); 11241057Salm if (done) { 11251057Salm a2 = get_addressed_line_node(n); 11261057Salm b2 = get_addressed_line_node(p); 11271057Salm current_addr = second_addr; 11281057Salm } else if (push_undo_stack(UMOV, p, n) == NULL || 11291057Salm push_undo_stack(UMOV, addr, INC_MOD(addr, addr_last)) == NULL) { 11301057Salm SPL0(); 11311057Salm return ERR; 11321057Salm } else { 11331057Salm a1 = get_addressed_line_node(n); 11341057Salm if (addr < first_addr) { 11351057Salm b1 = get_addressed_line_node(p); 11361057Salm b2 = get_addressed_line_node(addr); 11371057Salm /* this get_addressed_line_node last! */ 11381057Salm } else { 11391057Salm b2 = get_addressed_line_node(addr); 11401057Salm b1 = get_addressed_line_node(p); 11411057Salm /* this get_addressed_line_node last! */ 11421057Salm } 11431057Salm a2 = b2->q_forw; 11441057Salm REQUE(b2, b1->q_forw); 11451057Salm REQUE(a1->q_back, a2); 11461057Salm REQUE(b1, a1); 11478855Srgrimes current_addr = addr + ((addr < first_addr) ? 11481057Salm second_addr - first_addr + 1 : 0); 11491057Salm } 11501057Salm if (isglobal) 11511057Salm unset_active_nodes(b2->q_forw, a2); 11521057Salm modified = 1; 11531057Salm SPL0(); 11541057Salm return 0; 11551057Salm} 11561057Salm 11571057Salm 11581057Salm/* copy_lines: copy a range of lines; return status */ 11591057Salmint 116090109Simpcopy_lines(long addr) 11611057Salm{ 11621057Salm line_t *lp, *np = get_addressed_line_node(first_addr); 11631057Salm undo_t *up = NULL; 11641057Salm long n = second_addr - first_addr + 1; 11651057Salm long m = 0; 11661057Salm 11671057Salm current_addr = addr; 11681057Salm if (first_addr <= addr && addr < second_addr) { 11691057Salm n = addr - first_addr + 1; 11701057Salm m = second_addr - addr; 11711057Salm } 11721057Salm for (; n > 0; n=m, m=0, np = get_addressed_line_node(current_addr + 1)) 11731057Salm for (; n-- > 0; np = np->q_forw) { 11741057Salm SPL1(); 11751057Salm if ((lp = dup_line_node(np)) == NULL) { 11761057Salm SPL0(); 11771057Salm return ERR; 11781057Salm } 11791057Salm add_line_node(lp); 11801057Salm if (up) 11811057Salm up->t = lp; 11821057Salm else if ((up = push_undo_stack(UADD, current_addr, 11831057Salm current_addr)) == NULL) { 11841057Salm SPL0(); 11851057Salm return ERR; 11861057Salm } 11871057Salm modified = 1; 11881057Salm SPL0(); 11891057Salm } 11901057Salm return 0; 11911057Salm} 11921057Salm 11931057Salm 11941057Salm/* delete_lines: delete a range of lines */ 11951057Salmint 119690109Simpdelete_lines(long from, long to) 11971057Salm{ 11981057Salm line_t *n, *p; 11991057Salm 12001057Salm SPL1(); 12011057Salm if (push_undo_stack(UDEL, from, to) == NULL) { 12021057Salm SPL0(); 12031057Salm return ERR; 12041057Salm } 12051057Salm n = get_addressed_line_node(INC_MOD(to, addr_last)); 12061057Salm p = get_addressed_line_node(from - 1); 12071057Salm /* this get_addressed_line_node last! */ 12081057Salm if (isglobal) 12091057Salm unset_active_nodes(p->q_forw, n); 12101057Salm REQUE(p, n); 12111057Salm addr_last -= to - from + 1; 12121057Salm current_addr = from - 1; 12131057Salm modified = 1; 12141057Salm SPL0(); 12151057Salm return 0; 12161057Salm} 12171057Salm 12181057Salm 12191057Salm/* display_lines: print a range of lines to stdout */ 12201057Salmint 122190109Simpdisplay_lines(long from, long to, int gflag) 12221057Salm{ 12231057Salm line_t *bp; 12241057Salm line_t *ep; 12251057Salm char *s; 12261057Salm 12271057Salm if (!from) { 122881220Smike errmsg = "invalid address"; 12291057Salm return ERR; 12301057Salm } 12311057Salm ep = get_addressed_line_node(INC_MOD(to, addr_last)); 12321057Salm bp = get_addressed_line_node(from); 12331057Salm for (; bp != ep; bp = bp->q_forw) { 12341057Salm if ((s = get_sbuf_line(bp)) == NULL) 12351057Salm return ERR; 12361057Salm if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0) 12371057Salm return ERR; 12381057Salm } 12391057Salm return 0; 12401057Salm} 12411057Salm 12421057Salm 12431057Salm#define MAXMARK 26 /* max number of marks */ 12441057Salm 1245241720Sedstatic line_t *mark[MAXMARK]; /* line markers */ 1246241720Sedstatic int markno; /* line marker count */ 12471057Salm 12481057Salm/* mark_line_node: set a line node mark */ 12491057Salmint 125090109Simpmark_line_node(line_t *lp, int n) 12511057Salm{ 125217516Sache if (!islower((unsigned char)n)) { 125381220Smike errmsg = "invalid mark character"; 12541057Salm return ERR; 12551057Salm } else if (mark[n - 'a'] == NULL) 12561057Salm markno++; 12571057Salm mark[n - 'a'] = lp; 12581057Salm return 0; 12591057Salm} 12601057Salm 12611057Salm 12621057Salm/* get_marked_node_addr: return address of a marked line */ 12631057Salmlong 126490109Simpget_marked_node_addr(int n) 12651057Salm{ 126617516Sache if (!islower((unsigned char)n)) { 126781220Smike errmsg = "invalid mark character"; 12681057Salm return ERR; 12691057Salm } 12701057Salm return get_line_node_addr(mark[n - 'a']); 12711057Salm} 12721057Salm 12731057Salm 12741057Salm/* unmark_line_node: clear line node mark */ 12751057Salmvoid 127690109Simpunmark_line_node(line_t *lp) 12771057Salm{ 12781057Salm int i; 12791057Salm 12801057Salm for (i = 0; markno && i < MAXMARK; i++) 12811057Salm if (mark[i] == lp) { 12821057Salm mark[i] = NULL; 12831057Salm markno--; 12841057Salm } 12851057Salm} 12861057Salm 12871057Salm 12881057Salm/* dup_line_node: return a pointer to a copy of a line node */ 12891057Salmline_t * 129090109Simpdup_line_node(line_t *lp) 12911057Salm{ 12921057Salm line_t *np; 12931057Salm 12941057Salm if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) { 12951057Salm fprintf(stderr, "%s\n", strerror(errno)); 129681220Smike errmsg = "out of memory"; 12971057Salm return NULL; 12981057Salm } 12991057Salm np->seek = lp->seek; 13001057Salm np->len = lp->len; 13011057Salm return np; 13021057Salm} 13031057Salm 13041057Salm 13051057Salm/* has_trailing_escape: return the parity of escapes preceding a character 13061057Salm in a string */ 13071057Salmint 130890109Simphas_trailing_escape(char *s, char *t) 13091057Salm{ 13101057Salm return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1); 13111057Salm} 13121057Salm 13131057Salm 131477407Simp/* strip_escapes: return copy of escaped string of at most length PATH_MAX */ 13151057Salmchar * 131690109Simpstrip_escapes(char *s) 13171057Salm{ 13181057Salm static char *file = NULL; 13191057Salm static int filesz = 0; 13201057Salm 13211057Salm int i = 0; 13221057Salm 132377407Simp REALLOC(file, filesz, PATH_MAX, NULL); 132459797Sjoe while (i < filesz - 1 /* Worry about a possible trailing escape */ 132559797Sjoe && (file[i++] = (*s == '\\') ? *++s : *s)) 13261057Salm s++; 13271057Salm return file; 13281057Salm} 13291057Salm 13301057Salm 13311057Salmvoid 133290109Simpsignal_hup(int signo) 13331057Salm{ 13341057Salm if (mutex) 13351057Salm sigflags |= (1 << (signo - 1)); 133630233Seivind else 133730233Seivind handle_hup(signo); 13381057Salm} 13391057Salm 13401057Salm 13411057Salmvoid 134290109Simpsignal_int(int signo) 13431057Salm{ 13441057Salm if (mutex) 13451057Salm sigflags |= (1 << (signo - 1)); 134630233Seivind else 134730233Seivind handle_int(signo); 13481057Salm} 13491057Salm 13501057Salm 13511057Salmvoid 135290109Simphandle_hup(int signo) 13531057Salm{ 13541057Salm char *hup = NULL; /* hup filename */ 13551057Salm char *s; 135681220Smike char ed_hup[] = "ed.hup"; 13571057Salm int n; 13581057Salm 13591057Salm if (!sigactive) 13601057Salm quit(1); 13611057Salm sigflags &= ~(1 << (signo - 1)); 136281220Smike if (addr_last && write_file(ed_hup, "w", 1, addr_last) < 0 && 13631057Salm (s = getenv("HOME")) != NULL && 136477407Simp (n = strlen(s)) + 8 <= PATH_MAX && /* "ed.hup" + '/' */ 13651057Salm (hup = (char *) malloc(n + 10)) != NULL) { 13661057Salm strcpy(hup, s); 13671057Salm if (hup[n - 1] != '/') 13681057Salm hup[n] = '/', hup[n+1] = '\0'; 13691057Salm strcat(hup, "ed.hup"); 13701057Salm write_file(hup, "w", 1, addr_last); 13711057Salm } 137278939Sdd quit(2); 13731057Salm} 13741057Salm 13751057Salm 13761057Salmvoid 137790109Simphandle_int(int signo) 13781057Salm{ 13791057Salm if (!sigactive) 138078939Sdd quit(1); 13811057Salm sigflags &= ~(1 << (signo - 1)); 13821057Salm#ifdef _POSIX_SOURCE 13831057Salm siglongjmp(env, -1); 13841057Salm#else 13851057Salm longjmp(env, -1); 13861057Salm#endif 13871057Salm} 13881057Salm 13891057Salm 13901057Salmint cols = 72; /* wrap column */ 13911057Salm 13921057Salmvoid 139390109Simphandle_winch(int signo) 13941057Salm{ 139530233Seivind int save_errno = errno; 139630233Seivind 13971057Salm struct winsize ws; /* window size structure */ 13981057Salm 13991057Salm sigflags &= ~(1 << (signo - 1)); 14001057Salm if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) { 14011057Salm if (ws.ws_row > 2) rows = ws.ws_row - 2; 14021057Salm if (ws.ws_col > 8) cols = ws.ws_col - 8; 14031057Salm } 140430233Seivind errno = save_errno; 14051057Salm} 14061057Salm 14071057Salm 14081057Salm/* is_legal_filename: return a legal filename */ 14091057Salmint 141090109Simpis_legal_filename(char *s) 14111057Salm{ 14121057Salm if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) { 141381220Smike errmsg = "shell access restricted"; 14141057Salm return 0; 14151057Salm } 14161057Salm return 1; 14171057Salm} 1418