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; 508292754Spfg if (*fnp && *fnp != '!') 509292754Spfg strlcpy(old_filename, fnp, PATH_MAX); 5101057Salm#ifdef BACKWARDS 5111057Salm if (*fnp == '\0' && *old_filename == '\0') { 51281220Smike errmsg = "no current filename"; 5131057Salm return ERR; 5141057Salm } 5151057Salm#endif 5161057Salm if (read_file(*fnp ? fnp : old_filename, 0) < 0) 5171057Salm return ERR; 5181057Salm clear_undo_stack(); 5191057Salm modified = 0; 5201057Salm u_current_addr = u_addr_last = -1; 5211057Salm break; 5221057Salm case 'f': 5231057Salm if (addr_cnt > 0) { 52481220Smike errmsg = "unexpected address"; 5251057Salm return ERR; 52617516Sache } else if (!isspace((unsigned char)*ibufp)) { 52781220Smike errmsg = "unexpected command suffix"; 5281057Salm return ERR; 5291057Salm } else if ((fnp = get_filename()) == NULL) 5301057Salm return ERR; 5311057Salm else if (*fnp == '!') { 53281220Smike errmsg = "invalid redirection"; 5331057Salm return ERR; 5341057Salm } 5351057Salm GET_COMMAND_SUFFIX(); 536292754Spfg if (*fnp) 537292754Spfg strlcpy(old_filename, fnp, PATH_MAX); 5381057Salm printf("%s\n", strip_escapes(old_filename)); 5391057Salm break; 5401057Salm case 'g': 5411057Salm case 'v': 5421057Salm case 'G': 5431057Salm case 'V': 5441057Salm if (isglobal) { 54581220Smike errmsg = "cannot nest global commands"; 5461057Salm return ERR; 5471057Salm } else if (check_addr_range(1, addr_last) < 0) 5481057Salm return ERR; 5491057Salm else if (build_active_list(c == 'g' || c == 'G') < 0) 5501057Salm return ERR; 5517165Sjoerg else if ((n = (c == 'G' || c == 'V'))) 5521057Salm GET_COMMAND_SUFFIX(); 5531057Salm isglobal++; 5541057Salm if (exec_global(n, gflag) < 0) 5558855Srgrimes return ERR; 5561057Salm break; 5571057Salm case 'h': 5581057Salm if (addr_cnt > 0) { 55981220Smike errmsg = "unexpected address"; 5601057Salm return ERR; 5611057Salm } 5621057Salm GET_COMMAND_SUFFIX(); 5631057Salm if (*errmsg) fprintf(stderr, "%s\n", errmsg); 5641057Salm break; 5651057Salm case 'H': 5661057Salm if (addr_cnt > 0) { 56781220Smike errmsg = "unexpected address"; 5681057Salm return ERR; 5691057Salm } 5701057Salm GET_COMMAND_SUFFIX(); 5711057Salm if ((garrulous = 1 - garrulous) && *errmsg) 5721057Salm fprintf(stderr, "%s\n", errmsg); 5731057Salm break; 5741057Salm case 'i': 5751057Salm if (second_addr == 0) { 57681220Smike errmsg = "invalid address"; 5771057Salm return ERR; 5781057Salm } 5791057Salm GET_COMMAND_SUFFIX(); 5801057Salm if (!isglobal) clear_undo_stack(); 5811057Salm if (append_lines(second_addr - 1) < 0) 5821057Salm return ERR; 5831057Salm break; 5841057Salm case 'j': 5851057Salm if (check_addr_range(current_addr, current_addr + 1) < 0) 5861057Salm return ERR; 5871057Salm GET_COMMAND_SUFFIX(); 5881057Salm if (!isglobal) clear_undo_stack(); 5891057Salm if (first_addr != second_addr && 5901057Salm join_lines(first_addr, second_addr) < 0) 5911057Salm return ERR; 5921057Salm break; 5931057Salm case 'k': 5941057Salm c = *ibufp++; 5951057Salm if (second_addr == 0) { 59681220Smike errmsg = "invalid address"; 5971057Salm return ERR; 5981057Salm } 5991057Salm GET_COMMAND_SUFFIX(); 6001057Salm if (mark_line_node(get_addressed_line_node(second_addr), c) < 0) 6011057Salm return ERR; 6021057Salm break; 6031057Salm case 'l': 6041057Salm if (check_addr_range(current_addr, current_addr) < 0) 6051057Salm return ERR; 6061057Salm GET_COMMAND_SUFFIX(); 6071057Salm if (display_lines(first_addr, second_addr, gflag | GLS) < 0) 6081057Salm return ERR; 6091057Salm gflag = 0; 6101057Salm break; 6111057Salm case 'm': 6121057Salm if (check_addr_range(current_addr, current_addr) < 0) 6131057Salm return ERR; 6141057Salm GET_THIRD_ADDR(addr); 6151057Salm if (first_addr <= addr && addr < second_addr) { 61681220Smike errmsg = "invalid destination"; 6171057Salm return ERR; 6181057Salm } 6191057Salm GET_COMMAND_SUFFIX(); 6201057Salm if (!isglobal) clear_undo_stack(); 6211057Salm if (move_lines(addr) < 0) 6221057Salm return ERR; 6231057Salm break; 6241057Salm case 'n': 6251057Salm if (check_addr_range(current_addr, current_addr) < 0) 6261057Salm return ERR; 6271057Salm GET_COMMAND_SUFFIX(); 6281057Salm if (display_lines(first_addr, second_addr, gflag | GNP) < 0) 6291057Salm return ERR; 6301057Salm gflag = 0; 6311057Salm break; 6321057Salm case 'p': 6331057Salm if (check_addr_range(current_addr, current_addr) < 0) 6341057Salm return ERR; 6351057Salm GET_COMMAND_SUFFIX(); 6361057Salm if (display_lines(first_addr, second_addr, gflag | GPR) < 0) 6371057Salm return ERR; 6381057Salm gflag = 0; 6391057Salm break; 6401057Salm case 'P': 6411057Salm if (addr_cnt > 0) { 64281220Smike errmsg = "unexpected address"; 6431057Salm return ERR; 6441057Salm } 6451057Salm GET_COMMAND_SUFFIX(); 6461057Salm prompt = prompt ? NULL : optarg ? optarg : dps; 6471057Salm break; 6481057Salm case 'q': 6491057Salm case 'Q': 6501057Salm if (addr_cnt > 0) { 65181220Smike errmsg = "unexpected address"; 6521057Salm return ERR; 6531057Salm } 6541057Salm GET_COMMAND_SUFFIX(); 6551057Salm gflag = (modified && !scripted && c == 'q') ? EMOD : EOF; 6561057Salm break; 6571057Salm case 'r': 65817516Sache if (!isspace((unsigned char)*ibufp)) { 65981220Smike errmsg = "unexpected command suffix"; 6601057Salm return ERR; 6611057Salm } else if (addr_cnt == 0) 6621057Salm second_addr = addr_last; 6631057Salm if ((fnp = get_filename()) == NULL) 6641057Salm return ERR; 6651057Salm GET_COMMAND_SUFFIX(); 6661057Salm if (!isglobal) clear_undo_stack(); 6671057Salm if (*old_filename == '\0' && *fnp != '!') 668292754Spfg strlcpy(old_filename, fnp, PATH_MAX); 6691057Salm#ifdef BACKWARDS 6701057Salm if (*fnp == '\0' && *old_filename == '\0') { 67181220Smike errmsg = "no current filename"; 6721057Salm return ERR; 6731057Salm } 6741057Salm#endif 6751057Salm if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0) 6761057Salm return ERR; 6771057Salm else if (addr && addr != addr_last) 6781057Salm modified = 1; 6791057Salm break; 6801057Salm case 's': 6811057Salm do { 6821057Salm switch(*ibufp) { 6831057Salm case '\n': 6841057Salm sflags |=SGF; 6851057Salm break; 6861057Salm case 'g': 6871057Salm sflags |= SGG; 6881057Salm ibufp++; 6891057Salm break; 6901057Salm case 'p': 6911057Salm sflags |= SGP; 6921057Salm ibufp++; 6931057Salm break; 6941057Salm case 'r': 6951057Salm sflags |= SGR; 6961057Salm ibufp++; 6971057Salm break; 6988855Srgrimes case '0': case '1': case '2': case '3': case '4': 6991057Salm case '5': case '6': case '7': case '8': case '9': 7001057Salm STRTOL(sgnum, ibufp); 7011057Salm sflags |= SGF; 7021057Salm sgflag &= ~GSG; /* override GSG */ 7031057Salm break; 7041057Salm default: 7051057Salm if (sflags) { 70681220Smike errmsg = "invalid command suffix"; 7071057Salm return ERR; 7081057Salm } 7091057Salm } 7101057Salm } while (sflags && *ibufp != '\n'); 7111057Salm if (sflags && !pat) { 71281220Smike errmsg = "no previous substitution"; 7131057Salm return ERR; 7141057Salm } else if (sflags & SGG) 7151057Salm sgnum = 0; /* override numeric arg */ 7161057Salm if (*ibufp != '\n' && *(ibufp + 1) == '\n') { 71781220Smike errmsg = "invalid pattern delimiter"; 7181057Salm return ERR; 7191057Salm } 7201057Salm tpat = pat; 7211057Salm SPL1(); 7221057Salm if ((!sflags || (sflags & SGR)) && 7231057Salm (tpat = get_compiled_pattern()) == NULL) { 7241057Salm SPL0(); 7251057Salm return ERR; 7261057Salm } else if (tpat != pat) { 7271057Salm if (pat) { 7281057Salm regfree(pat); 7291057Salm free(pat); 7301057Salm } 7311057Salm pat = tpat; 7321057Salm patlock = 1; /* reserve pattern */ 7331057Salm } 7341057Salm SPL0(); 7351057Salm if (!sflags && extract_subst_tail(&sgflag, &sgnum) < 0) 7361057Salm return ERR; 7371057Salm else if (isglobal) 7381057Salm sgflag |= GLB; 7391057Salm else 7401057Salm sgflag &= ~GLB; 7411057Salm if (sflags & SGG) 7421057Salm sgflag ^= GSG; 7431057Salm if (sflags & SGP) 7441057Salm sgflag ^= GPR, sgflag &= ~(GLS | GNP); 7451057Salm do { 7461057Salm switch(*ibufp) { 7471057Salm case 'p': 7481057Salm sgflag |= GPR, ibufp++; 7491057Salm break; 7501057Salm case 'l': 7511057Salm sgflag |= GLS, ibufp++; 7521057Salm break; 7531057Salm case 'n': 7541057Salm sgflag |= GNP, ibufp++; 7551057Salm break; 7561057Salm default: 7571057Salm n++; 7581057Salm } 7591057Salm } while (!n); 7601057Salm if (check_addr_range(current_addr, current_addr) < 0) 7611057Salm return ERR; 7621057Salm GET_COMMAND_SUFFIX(); 7631057Salm if (!isglobal) clear_undo_stack(); 7641057Salm if (search_and_replace(pat, sgflag, sgnum) < 0) 7651057Salm return ERR; 7661057Salm break; 7671057Salm case 't': 7681057Salm if (check_addr_range(current_addr, current_addr) < 0) 7691057Salm return ERR; 7701057Salm GET_THIRD_ADDR(addr); 7711057Salm GET_COMMAND_SUFFIX(); 7721057Salm if (!isglobal) clear_undo_stack(); 7731057Salm if (copy_lines(addr) < 0) 7741057Salm return ERR; 7751057Salm break; 7761057Salm case 'u': 7771057Salm if (addr_cnt > 0) { 77881220Smike errmsg = "unexpected address"; 7791057Salm return ERR; 7801057Salm } 7811057Salm GET_COMMAND_SUFFIX(); 7821057Salm if (pop_undo_stack() < 0) 7831057Salm return ERR; 7841057Salm break; 7851057Salm case 'w': 7861057Salm case 'W': 7871057Salm if ((n = *ibufp) == 'q' || n == 'Q') { 7881057Salm gflag = EOF; 7891057Salm ibufp++; 7901057Salm } 79117516Sache if (!isspace((unsigned char)*ibufp)) { 79281220Smike errmsg = "unexpected command suffix"; 7931057Salm return ERR; 7941057Salm } else if ((fnp = get_filename()) == NULL) 7951057Salm return ERR; 7961057Salm if (addr_cnt == 0 && !addr_last) 7971057Salm first_addr = second_addr = 0; 7981057Salm else if (check_addr_range(1, addr_last) < 0) 7991057Salm return ERR; 8001057Salm GET_COMMAND_SUFFIX(); 8011057Salm if (*old_filename == '\0' && *fnp != '!') 802292754Spfg strlcpy(old_filename, fnp, PATH_MAX); 8031057Salm#ifdef BACKWARDS 8041057Salm if (*fnp == '\0' && *old_filename == '\0') { 80581220Smike errmsg = "no current filename"; 8061057Salm return ERR; 8071057Salm } 8081057Salm#endif 8098855Srgrimes if ((addr = write_file(*fnp ? fnp : old_filename, 8101057Salm (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0) 8111057Salm return ERR; 8121057Salm else if (addr == addr_last) 8131057Salm modified = 0; 8141057Salm else if (modified && !scripted && n == 'q') 8151057Salm gflag = EMOD; 8161057Salm break; 8171057Salm case 'x': 8181057Salm if (addr_cnt > 0) { 81981220Smike errmsg = "unexpected address"; 8201057Salm return ERR; 8211057Salm } 8221057Salm GET_COMMAND_SUFFIX(); 8231057Salm#ifdef DES 8241057Salm des = get_keyword(); 825140344Scharnier break; 8261057Salm#else 82781220Smike errmsg = "crypt unavailable"; 8281057Salm return ERR; 8291057Salm#endif 8301057Salm case 'z': 8311057Salm#ifdef BACKWARDS 8321057Salm if (check_addr_range(first_addr = 1, current_addr + 1) < 0) 8331057Salm#else 8341057Salm if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0) 8351057Salm#endif 8361057Salm return ERR; 8371057Salm else if ('0' < *ibufp && *ibufp <= '9') 8381057Salm STRTOL(rows, ibufp); 8391057Salm GET_COMMAND_SUFFIX(); 8401057Salm if (display_lines(second_addr, min(addr_last, 8411057Salm second_addr + rows), gflag) < 0) 8421057Salm return ERR; 8431057Salm gflag = 0; 8441057Salm break; 8451057Salm case '=': 8461057Salm GET_COMMAND_SUFFIX(); 8477165Sjoerg printf("%ld\n", addr_cnt ? second_addr : addr_last); 8481057Salm break; 8491057Salm case '!': 8501057Salm if (addr_cnt > 0) { 85181220Smike errmsg = "unexpected address"; 8521057Salm return ERR; 8531057Salm } else if ((sflags = get_shell_command()) < 0) 8541057Salm return ERR; 8551057Salm GET_COMMAND_SUFFIX(); 8561057Salm if (sflags) printf("%s\n", shcmd + 1); 8571057Salm system(shcmd + 1); 8581057Salm if (!scripted) printf("!\n"); 8591057Salm break; 8601057Salm case '\n': 8611057Salm#ifdef BACKWARDS 8621057Salm if (check_addr_range(first_addr = 1, current_addr + 1) < 0 8631057Salm#else 8641057Salm if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0 8651057Salm#endif 8661057Salm || display_lines(second_addr, second_addr, 0) < 0) 8671057Salm return ERR; 8681057Salm break; 8691057Salm default: 87081220Smike errmsg = "unknown command"; 8711057Salm return ERR; 8721057Salm } 8731057Salm return gflag; 8741057Salm} 8751057Salm 8761057Salm 8771057Salm/* check_addr_range: return status of address range check */ 8781057Salmint 87990109Simpcheck_addr_range(long n, long m) 8801057Salm{ 8811057Salm if (addr_cnt == 0) { 8821057Salm first_addr = n; 8831057Salm second_addr = m; 8841057Salm } 8851057Salm if (first_addr > second_addr || 1 > first_addr || 8861057Salm second_addr > addr_last) { 88781220Smike errmsg = "invalid address"; 8881057Salm return ERR; 8891057Salm } 8901057Salm return 0; 8911057Salm} 8921057Salm 8931057Salm 8948855Srgrimes/* get_matching_node_addr: return the address of the next line matching a 8951057Salm pattern in a given direction. wrap around begin/end of editor buffer if 8961057Salm necessary */ 8971057Salmlong 89890109Simpget_matching_node_addr(pattern_t *pat, int dir) 8991057Salm{ 9001057Salm char *s; 9011057Salm long n = current_addr; 9021057Salm line_t *lp; 9031057Salm 9041057Salm if (!pat) return ERR; 9051057Salm do { 9067165Sjoerg if ((n = dir ? INC_MOD(n, addr_last) : DEC_MOD(n, addr_last))) { 9071057Salm lp = get_addressed_line_node(n); 9081057Salm if ((s = get_sbuf_line(lp)) == NULL) 9091057Salm return ERR; 9101057Salm if (isbinary) 9111057Salm NUL_TO_NEWLINE(s, lp->len); 9121057Salm if (!regexec(pat, s, 0, NULL, 0)) 9131057Salm return n; 9147165Sjoerg } 9151057Salm } while (n != current_addr); 91681220Smike errmsg = "no match"; 9171057Salm return ERR; 9181057Salm} 9191057Salm 9201057Salm 9211057Salm/* get_filename: return pointer to copy of filename in the command buffer */ 9221057Salmchar * 92390109Simpget_filename(void) 9241057Salm{ 9251057Salm static char *file = NULL; 9261057Salm static int filesz = 0; 9271057Salm 9281057Salm int n; 9291057Salm 9301057Salm if (*ibufp != '\n') { 9311057Salm SKIP_BLANKS(); 9321057Salm if (*ibufp == '\n') { 93381220Smike errmsg = "invalid filename"; 9341057Salm return NULL; 9351057Salm } else if ((ibufp = get_extended_line(&n, 1)) == NULL) 9361057Salm return NULL; 9371057Salm else if (*ibufp == '!') { 9381057Salm ibufp++; 9391057Salm if ((n = get_shell_command()) < 0) 9401057Salm return NULL; 94177407Simp if (n) 94277407Simp printf("%s\n", shcmd + 1); 9431057Salm return shcmd; 94477407Simp } else if (n > PATH_MAX - 1) { 94581220Smike errmsg = "filename too long"; 9461057Salm return NULL; 9471057Salm } 9481057Salm } 9491057Salm#ifndef BACKWARDS 9501057Salm else if (*old_filename == '\0') { 95181220Smike errmsg = "no current filename"; 9521057Salm return NULL; 9531057Salm } 9541057Salm#endif 95577407Simp REALLOC(file, filesz, PATH_MAX, NULL); 9561057Salm for (n = 0; *ibufp != '\n';) 9571057Salm file[n++] = *ibufp++; 9581057Salm file[n] = '\0'; 9591057Salm return is_legal_filename(file) ? file : NULL; 9601057Salm} 9611057Salm 9621057Salm 9631057Salm/* get_shell_command: read a shell command from stdin; return substitution 9641057Salm status */ 9651057Salmint 96690109Simpget_shell_command(void) 9671057Salm{ 9681057Salm static char *buf = NULL; 9691057Salm static int n = 0; 9701057Salm 9711057Salm char *s; /* substitution char pointer */ 9721057Salm int i = 0; 9731057Salm int j = 0; 9741057Salm 9751057Salm if (red) { 97681220Smike errmsg = "shell access restricted"; 9771057Salm return ERR; 9781057Salm } else if ((s = ibufp = get_extended_line(&j, 1)) == NULL) 9791057Salm return ERR; 9801057Salm REALLOC(buf, n, j + 1, ERR); 9811057Salm buf[i++] = '!'; /* prefix command w/ bang */ 9821057Salm while (*ibufp != '\n') 9831057Salm switch (*ibufp) { 9841057Salm default: 9851057Salm REALLOC(buf, n, i + 2, ERR); 9861057Salm buf[i++] = *ibufp; 9871057Salm if (*ibufp++ == '\\') 9881057Salm buf[i++] = *ibufp++; 9891057Salm break; 9901057Salm case '!': 9911057Salm if (s != ibufp) { 9921057Salm REALLOC(buf, n, i + 1, ERR); 9931057Salm buf[i++] = *ibufp++; 9941057Salm } 9951057Salm#ifdef BACKWARDS 9961057Salm else if (shcmd == NULL || *(shcmd + 1) == '\0') 9971057Salm#else 9981057Salm else if (shcmd == NULL) 9991057Salm#endif 10001057Salm { 100181220Smike errmsg = "no previous command"; 10021057Salm return ERR; 10031057Salm } else { 10041057Salm REALLOC(buf, n, i + shcmdi, ERR); 10051057Salm for (s = shcmd + 1; s < shcmd + shcmdi;) 10061057Salm buf[i++] = *s++; 10071057Salm s = ibufp++; 10081057Salm } 10091057Salm break; 10101057Salm case '%': 10111057Salm if (*old_filename == '\0') { 101281220Smike errmsg = "no current filename"; 10131057Salm return ERR; 10141057Salm } 10151057Salm j = strlen(s = strip_escapes(old_filename)); 10161057Salm REALLOC(buf, n, i + j, ERR); 10171057Salm while (j--) 10181057Salm buf[i++] = *s++; 10191057Salm s = ibufp++; 10201057Salm break; 10211057Salm } 10221057Salm REALLOC(shcmd, shcmdsz, i + 1, ERR); 10231057Salm memcpy(shcmd, buf, i); 10241057Salm shcmd[shcmdi = i] = '\0'; 10251057Salm return *s == '!' || *s == '%'; 10261057Salm} 10271057Salm 10281057Salm 10291057Salm/* append_lines: insert text from stdin to after line n; stop when either a 10301057Salm single period is read or EOF; return status */ 10311057Salmint 103290109Simpappend_lines(long n) 10331057Salm{ 10341057Salm int l; 103581220Smike const char *lp = ibuf; 103681220Smike const char *eot; 10371057Salm undo_t *up = NULL; 10381057Salm 10391057Salm for (current_addr = n;;) { 10401057Salm if (!isglobal) { 10411057Salm if ((l = get_tty_line()) < 0) 10421057Salm return ERR; 10431057Salm else if (l == 0 || ibuf[l - 1] != '\n') { 10441057Salm clearerr(stdin); 10451057Salm return l ? EOF : 0; 10461057Salm } 10471057Salm lp = ibuf; 10481057Salm } else if (*(lp = ibufp) == '\0') 10491057Salm return 0; 10501057Salm else { 10511057Salm while (*ibufp++ != '\n') 10521057Salm ; 10531057Salm l = ibufp - lp; 10541057Salm } 10551057Salm if (l == 2 && lp[0] == '.' && lp[1] == '\n') { 10561057Salm return 0; 10571057Salm } 10581057Salm eot = lp + l; 10591057Salm SPL1(); 10601057Salm do { 10611057Salm if ((lp = put_sbuf_line(lp)) == NULL) { 10621057Salm SPL0(); 10631057Salm return ERR; 10641057Salm } else if (up) 10651057Salm up->t = get_addressed_line_node(current_addr); 10661057Salm else if ((up = push_undo_stack(UADD, current_addr, 10671057Salm current_addr)) == NULL) { 10681057Salm SPL0(); 10691057Salm return ERR; 10701057Salm } 10711057Salm } while (lp != eot); 10721057Salm modified = 1; 10731057Salm SPL0(); 10741057Salm } 10751057Salm /* NOTREACHED */ 10761057Salm} 10771057Salm 10781057Salm 10791057Salm/* join_lines: replace a range of lines with the joined text of those lines */ 10801057Salmint 108190109Simpjoin_lines(long from, long to) 10821057Salm{ 10831057Salm static char *buf = NULL; 10841057Salm static int n; 10851057Salm 10861057Salm char *s; 10871057Salm int size = 0; 10881057Salm line_t *bp, *ep; 10891057Salm 10901057Salm ep = get_addressed_line_node(INC_MOD(to, addr_last)); 10911057Salm bp = get_addressed_line_node(from); 10921057Salm for (; bp != ep; bp = bp->q_forw) { 10931057Salm if ((s = get_sbuf_line(bp)) == NULL) 10941057Salm return ERR; 10951057Salm REALLOC(buf, n, size + bp->len, ERR); 10961057Salm memcpy(buf + size, s, bp->len); 10971057Salm size += bp->len; 10981057Salm } 10991057Salm REALLOC(buf, n, size + 2, ERR); 11001057Salm memcpy(buf + size, "\n", 2); 11011057Salm if (delete_lines(from, to) < 0) 11021057Salm return ERR; 11031057Salm current_addr = from - 1; 11041057Salm SPL1(); 11051057Salm if (put_sbuf_line(buf) == NULL || 11061057Salm push_undo_stack(UADD, current_addr, current_addr) == NULL) { 11071057Salm SPL0(); 11081057Salm return ERR; 11091057Salm } 11101057Salm modified = 1; 11111057Salm SPL0(); 11121057Salm return 0; 11131057Salm} 11141057Salm 11151057Salm 11161057Salm/* move_lines: move a range of lines */ 11171057Salmint 111890109Simpmove_lines(long addr) 11191057Salm{ 11201057Salm line_t *b1, *a1, *b2, *a2; 11211057Salm long n = INC_MOD(second_addr, addr_last); 11221057Salm long p = first_addr - 1; 11231057Salm int done = (addr == first_addr - 1 || addr == second_addr); 11241057Salm 11251057Salm SPL1(); 11261057Salm if (done) { 11271057Salm a2 = get_addressed_line_node(n); 11281057Salm b2 = get_addressed_line_node(p); 11291057Salm current_addr = second_addr; 11301057Salm } else if (push_undo_stack(UMOV, p, n) == NULL || 11311057Salm push_undo_stack(UMOV, addr, INC_MOD(addr, addr_last)) == NULL) { 11321057Salm SPL0(); 11331057Salm return ERR; 11341057Salm } else { 11351057Salm a1 = get_addressed_line_node(n); 11361057Salm if (addr < first_addr) { 11371057Salm b1 = get_addressed_line_node(p); 11381057Salm b2 = get_addressed_line_node(addr); 11391057Salm /* this get_addressed_line_node last! */ 11401057Salm } else { 11411057Salm b2 = get_addressed_line_node(addr); 11421057Salm b1 = get_addressed_line_node(p); 11431057Salm /* this get_addressed_line_node last! */ 11441057Salm } 11451057Salm a2 = b2->q_forw; 11461057Salm REQUE(b2, b1->q_forw); 11471057Salm REQUE(a1->q_back, a2); 11481057Salm REQUE(b1, a1); 11498855Srgrimes current_addr = addr + ((addr < first_addr) ? 11501057Salm second_addr - first_addr + 1 : 0); 11511057Salm } 11521057Salm if (isglobal) 11531057Salm unset_active_nodes(b2->q_forw, a2); 11541057Salm modified = 1; 11551057Salm SPL0(); 11561057Salm return 0; 11571057Salm} 11581057Salm 11591057Salm 11601057Salm/* copy_lines: copy a range of lines; return status */ 11611057Salmint 116290109Simpcopy_lines(long addr) 11631057Salm{ 11641057Salm line_t *lp, *np = get_addressed_line_node(first_addr); 11651057Salm undo_t *up = NULL; 11661057Salm long n = second_addr - first_addr + 1; 11671057Salm long m = 0; 11681057Salm 11691057Salm current_addr = addr; 11701057Salm if (first_addr <= addr && addr < second_addr) { 11711057Salm n = addr - first_addr + 1; 11721057Salm m = second_addr - addr; 11731057Salm } 11741057Salm for (; n > 0; n=m, m=0, np = get_addressed_line_node(current_addr + 1)) 11751057Salm for (; n-- > 0; np = np->q_forw) { 11761057Salm SPL1(); 11771057Salm if ((lp = dup_line_node(np)) == NULL) { 11781057Salm SPL0(); 11791057Salm return ERR; 11801057Salm } 11811057Salm add_line_node(lp); 11821057Salm if (up) 11831057Salm up->t = lp; 11841057Salm else if ((up = push_undo_stack(UADD, current_addr, 11851057Salm current_addr)) == NULL) { 11861057Salm SPL0(); 11871057Salm return ERR; 11881057Salm } 11891057Salm modified = 1; 11901057Salm SPL0(); 11911057Salm } 11921057Salm return 0; 11931057Salm} 11941057Salm 11951057Salm 11961057Salm/* delete_lines: delete a range of lines */ 11971057Salmint 119890109Simpdelete_lines(long from, long to) 11991057Salm{ 12001057Salm line_t *n, *p; 12011057Salm 12021057Salm SPL1(); 12031057Salm if (push_undo_stack(UDEL, from, to) == NULL) { 12041057Salm SPL0(); 12051057Salm return ERR; 12061057Salm } 12071057Salm n = get_addressed_line_node(INC_MOD(to, addr_last)); 12081057Salm p = get_addressed_line_node(from - 1); 12091057Salm /* this get_addressed_line_node last! */ 12101057Salm if (isglobal) 12111057Salm unset_active_nodes(p->q_forw, n); 12121057Salm REQUE(p, n); 12131057Salm addr_last -= to - from + 1; 12141057Salm current_addr = from - 1; 12151057Salm modified = 1; 12161057Salm SPL0(); 12171057Salm return 0; 12181057Salm} 12191057Salm 12201057Salm 12211057Salm/* display_lines: print a range of lines to stdout */ 12221057Salmint 122390109Simpdisplay_lines(long from, long to, int gflag) 12241057Salm{ 12251057Salm line_t *bp; 12261057Salm line_t *ep; 12271057Salm char *s; 12281057Salm 12291057Salm if (!from) { 123081220Smike errmsg = "invalid address"; 12311057Salm return ERR; 12321057Salm } 12331057Salm ep = get_addressed_line_node(INC_MOD(to, addr_last)); 12341057Salm bp = get_addressed_line_node(from); 12351057Salm for (; bp != ep; bp = bp->q_forw) { 12361057Salm if ((s = get_sbuf_line(bp)) == NULL) 12371057Salm return ERR; 12381057Salm if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0) 12391057Salm return ERR; 12401057Salm } 12411057Salm return 0; 12421057Salm} 12431057Salm 12441057Salm 12451057Salm#define MAXMARK 26 /* max number of marks */ 12461057Salm 1247241720Sedstatic line_t *mark[MAXMARK]; /* line markers */ 1248241720Sedstatic int markno; /* line marker count */ 12491057Salm 12501057Salm/* mark_line_node: set a line node mark */ 12511057Salmint 125290109Simpmark_line_node(line_t *lp, int n) 12531057Salm{ 125417516Sache if (!islower((unsigned char)n)) { 125581220Smike errmsg = "invalid mark character"; 12561057Salm return ERR; 12571057Salm } else if (mark[n - 'a'] == NULL) 12581057Salm markno++; 12591057Salm mark[n - 'a'] = lp; 12601057Salm return 0; 12611057Salm} 12621057Salm 12631057Salm 12641057Salm/* get_marked_node_addr: return address of a marked line */ 12651057Salmlong 126690109Simpget_marked_node_addr(int n) 12671057Salm{ 126817516Sache if (!islower((unsigned char)n)) { 126981220Smike errmsg = "invalid mark character"; 12701057Salm return ERR; 12711057Salm } 12721057Salm return get_line_node_addr(mark[n - 'a']); 12731057Salm} 12741057Salm 12751057Salm 12761057Salm/* unmark_line_node: clear line node mark */ 12771057Salmvoid 127890109Simpunmark_line_node(line_t *lp) 12791057Salm{ 12801057Salm int i; 12811057Salm 12821057Salm for (i = 0; markno && i < MAXMARK; i++) 12831057Salm if (mark[i] == lp) { 12841057Salm mark[i] = NULL; 12851057Salm markno--; 12861057Salm } 12871057Salm} 12881057Salm 12891057Salm 12901057Salm/* dup_line_node: return a pointer to a copy of a line node */ 12911057Salmline_t * 129290109Simpdup_line_node(line_t *lp) 12931057Salm{ 12941057Salm line_t *np; 12951057Salm 12961057Salm if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) { 12971057Salm fprintf(stderr, "%s\n", strerror(errno)); 129881220Smike errmsg = "out of memory"; 12991057Salm return NULL; 13001057Salm } 13011057Salm np->seek = lp->seek; 13021057Salm np->len = lp->len; 13031057Salm return np; 13041057Salm} 13051057Salm 13061057Salm 13071057Salm/* has_trailing_escape: return the parity of escapes preceding a character 13081057Salm in a string */ 13091057Salmint 131090109Simphas_trailing_escape(char *s, char *t) 13111057Salm{ 13121057Salm return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1); 13131057Salm} 13141057Salm 13151057Salm 131677407Simp/* strip_escapes: return copy of escaped string of at most length PATH_MAX */ 13171057Salmchar * 131890109Simpstrip_escapes(char *s) 13191057Salm{ 13201057Salm static char *file = NULL; 13211057Salm static int filesz = 0; 13221057Salm 13231057Salm int i = 0; 13241057Salm 132577407Simp REALLOC(file, filesz, PATH_MAX, NULL); 132659797Sjoe while (i < filesz - 1 /* Worry about a possible trailing escape */ 132759797Sjoe && (file[i++] = (*s == '\\') ? *++s : *s)) 13281057Salm s++; 13291057Salm return file; 13301057Salm} 13311057Salm 13321057Salm 13331057Salmvoid 133490109Simpsignal_hup(int signo) 13351057Salm{ 13361057Salm if (mutex) 13371057Salm sigflags |= (1 << (signo - 1)); 133830233Seivind else 133930233Seivind handle_hup(signo); 13401057Salm} 13411057Salm 13421057Salm 13431057Salmvoid 134490109Simpsignal_int(int signo) 13451057Salm{ 13461057Salm if (mutex) 13471057Salm sigflags |= (1 << (signo - 1)); 134830233Seivind else 134930233Seivind handle_int(signo); 13501057Salm} 13511057Salm 13521057Salm 13531057Salmvoid 135490109Simphandle_hup(int signo) 13551057Salm{ 13561057Salm char *hup = NULL; /* hup filename */ 13571057Salm char *s; 135881220Smike char ed_hup[] = "ed.hup"; 13591057Salm int n; 13601057Salm 13611057Salm if (!sigactive) 13621057Salm quit(1); 13631057Salm sigflags &= ~(1 << (signo - 1)); 136481220Smike if (addr_last && write_file(ed_hup, "w", 1, addr_last) < 0 && 13651057Salm (s = getenv("HOME")) != NULL && 136677407Simp (n = strlen(s)) + 8 <= PATH_MAX && /* "ed.hup" + '/' */ 13671057Salm (hup = (char *) malloc(n + 10)) != NULL) { 13681057Salm strcpy(hup, s); 13691057Salm if (hup[n - 1] != '/') 13701057Salm hup[n] = '/', hup[n+1] = '\0'; 13711057Salm strcat(hup, "ed.hup"); 13721057Salm write_file(hup, "w", 1, addr_last); 13731057Salm } 137478939Sdd quit(2); 13751057Salm} 13761057Salm 13771057Salm 13781057Salmvoid 137990109Simphandle_int(int signo) 13801057Salm{ 13811057Salm if (!sigactive) 138278939Sdd quit(1); 13831057Salm sigflags &= ~(1 << (signo - 1)); 13841057Salm#ifdef _POSIX_SOURCE 13851057Salm siglongjmp(env, -1); 13861057Salm#else 13871057Salm longjmp(env, -1); 13881057Salm#endif 13891057Salm} 13901057Salm 13911057Salm 13921057Salmint cols = 72; /* wrap column */ 13931057Salm 13941057Salmvoid 139590109Simphandle_winch(int signo) 13961057Salm{ 139730233Seivind int save_errno = errno; 139830233Seivind 13991057Salm struct winsize ws; /* window size structure */ 14001057Salm 14011057Salm sigflags &= ~(1 << (signo - 1)); 14021057Salm if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) { 14031057Salm if (ws.ws_row > 2) rows = ws.ws_row - 2; 14041057Salm if (ws.ws_col > 8) cols = ws.ws_col - 8; 14051057Salm } 140630233Seivind errno = save_errno; 14071057Salm} 14081057Salm 14091057Salm 14101057Salm/* is_legal_filename: return a legal filename */ 14111057Salmint 141290109Simpis_legal_filename(char *s) 14131057Salm{ 14141057Salm if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) { 141581220Smike errmsg = "shell access restricted"; 14161057Salm return 0; 14171057Salm } 14181057Salm return 1; 14191057Salm} 1420