main.c revision 140344
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: head/bin/ed/main.c 140344 2005-01-16 11:10:21Z charnier $"); 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 691057Salmsigjmp_buf env; 701057Salm#else 711057Salmjmp_buf env; 721057Salm#endif 731057Salm 741057Salm/* static buffers */ 751057Salmchar stdinbuf[1]; /* stdin buffer */ 761057Salmchar *shcmd; /* shell command buffer */ 771057Salmint shcmdsz; /* shell command buffer size */ 781057Salmint 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 */ 851057Salmint 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" */ 901057Salmint 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 */ 931057Salmint sigactive = 0; /* if set, signal handlers are enabled */ 941057Salm 9577407Simpchar 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 */ 9981220Smikeconst char *prompt; /* command-line prompt */ 10081220Smikeconst char *dps = "*"; /* default command-line prompt */ 1011057Salm 10269247Skrisconst char usage[] = "usage: %s [-] [-sx] [-p string] [name]\n"; 1031057Salm 1041057Salm/* ed: line editor */ 1051057Salmint 10690109Simpmain(int argc, char *argv[]) 1071057Salm{ 1081057Salm int c, n; 1091057Salm long status = 0; 11020420Ssteve#if __GNUC__ 11120420Ssteve /* Avoid longjmp clobbering */ 11220420Ssteve (void) &argc; 11320420Ssteve (void) &argv; 11420420Ssteve#endif 1151057Salm 11617516Sache (void)setlocale(LC_ALL, ""); 11717516Sache 1181057Salm red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r'; 1191057Salmtop: 12024348Simp while ((c = getopt(argc, argv, "p:sx")) != -1) 1211057Salm switch(c) { 1221057Salm case 'p': /* set prompt */ 1231057Salm prompt = optarg; 1241057Salm break; 1251057Salm case 's': /* run script */ 1261057Salm scripted = 1; 1271057Salm break; 1281057Salm case 'x': /* use crypt */ 1291057Salm#ifdef DES 1301057Salm des = get_keyword(); 1311057Salm#else 1321057Salm fprintf(stderr, "crypt unavailable\n?\n"); 1331057Salm#endif 1341057Salm break; 1351057Salm 1361057Salm default: 1371057Salm fprintf(stderr, usage, argv[0]); 1381057Salm exit(1); 1391057Salm } 1401057Salm argv += optind; 1411057Salm argc -= optind; 1421057Salm if (argc && **argv == '-') { 1431057Salm scripted = 1; 1441057Salm if (argc > 1) { 1451057Salm optind = 1; 1461057Salm goto top; 1471057Salm } 1481057Salm argv++; 1491057Salm argc--; 1501057Salm } 1511057Salm /* assert: reliable signals! */ 1521057Salm#ifdef SIGWINCH 1531057Salm handle_winch(SIGWINCH); 1541057Salm if (isatty(0)) signal(SIGWINCH, handle_winch); 1551057Salm#endif 1561057Salm signal(SIGHUP, signal_hup); 1571057Salm signal(SIGQUIT, SIG_IGN); 1581057Salm signal(SIGINT, signal_int); 1591057Salm#ifdef _POSIX_SOURCE 1607165Sjoerg if ((status = sigsetjmp(env, 1))) 1611057Salm#else 1627165Sjoerg if ((status = setjmp(env))) 1631057Salm#endif 1641057Salm { 1651057Salm fputs("\n?\n", stderr); 16681220Smike errmsg = "interrupt"; 1671057Salm } else { 1681057Salm init_buffers(); 1691057Salm sigactive = 1; /* enable signal handlers */ 1701057Salm if (argc && **argv && is_legal_filename(*argv)) { 1711057Salm if (read_file(*argv, 0) < 0 && !isatty(0)) 1721057Salm quit(2); 1731057Salm else if (**argv != '!') 17459797Sjoe if (strlcpy(old_filename, *argv, sizeof(old_filename)) 17559797Sjoe >= sizeof(old_filename)) 17659797Sjoe quit(2); 1771057Salm } else if (argc) { 1781057Salm fputs("?\n", stderr); 1791057Salm if (**argv == '\0') 18081220Smike errmsg = "invalid filename"; 1811057Salm if (!isatty(0)) 1821057Salm quit(2); 1831057Salm } 1841057Salm } 1851057Salm for (;;) { 1861057Salm if (status < 0 && garrulous) 1871057Salm fprintf(stderr, "%s\n", errmsg); 1881057Salm if (prompt) { 1891057Salm printf("%s", prompt); 1901057Salm fflush(stdout); 1911057Salm } 1921057Salm if ((n = get_tty_line()) < 0) { 1931057Salm status = ERR; 1941057Salm continue; 1951057Salm } else if (n == 0) { 1961057Salm if (modified && !scripted) { 1971057Salm fputs("?\n", stderr); 19881220Smike errmsg = "warning: file modified"; 1991057Salm if (!isatty(0)) { 2008855Srgrimes fprintf(stderr, garrulous ? 2011057Salm "script, line %d: %s\n" : 2021057Salm "", lineno, errmsg); 2031057Salm quit(2); 2041057Salm } 2051057Salm clearerr(stdin); 2061057Salm modified = 0; 2071057Salm status = EMOD; 2081057Salm continue; 2091057Salm } else 2101057Salm quit(0); 2111057Salm } else if (ibuf[n - 1] != '\n') { 2121057Salm /* discard line */ 21381220Smike errmsg = "unexpected end-of-file"; 2141057Salm clearerr(stdin); 2151057Salm status = ERR; 2161057Salm continue; 2171057Salm } 2181057Salm isglobal = 0; 2191057Salm if ((status = extract_addr_range()) >= 0 && 2201057Salm (status = exec_command()) >= 0) 2218855Srgrimes if (!status || 2221057Salm (status = display_lines(current_addr, current_addr, 2231057Salm status)) >= 0) 2241057Salm continue; 2251057Salm switch (status) { 2261057Salm case EOF: 2271057Salm quit(0); 2281057Salm case EMOD: 2291057Salm modified = 0; 2301057Salm fputs("?\n", stderr); /* give warning */ 23181220Smike errmsg = "warning: file modified"; 2321057Salm if (!isatty(0)) { 2338855Srgrimes fprintf(stderr, garrulous ? 2348855Srgrimes "script, line %d: %s\n" : 2351057Salm "", lineno, errmsg); 2361057Salm quit(2); 2371057Salm } 2381057Salm break; 2391057Salm case FATAL: 2401057Salm if (!isatty(0)) 2418855Srgrimes fprintf(stderr, garrulous ? 2428855Srgrimes "script, line %d: %s\n" : "", 2431057Salm lineno, errmsg); 2441057Salm else 2451057Salm fprintf(stderr, garrulous ? "%s\n" : "", 2461057Salm errmsg); 2471057Salm quit(3); 2481057Salm default: 2491057Salm fputs("?\n", stderr); 2501057Salm if (!isatty(0)) { 2518855Srgrimes fprintf(stderr, garrulous ? 2521057Salm "script, line %d: %s\n" : "", 2531057Salm lineno, errmsg); 2541057Salm quit(2); 2551057Salm } 2561057Salm break; 2571057Salm } 2581057Salm } 2591057Salm /*NOTREACHED*/ 2601057Salm} 2611057Salm 2621057Salmlong first_addr, second_addr, addr_cnt; 2631057Salm 2648855Srgrimes/* extract_addr_range: get line addresses from the command buffer until an 2651057Salm illegal address is seen; return status */ 2661057Salmint 26790109Simpextract_addr_range(void) 2681057Salm{ 2691057Salm long addr; 2701057Salm 2711057Salm addr_cnt = 0; 2721057Salm first_addr = second_addr = current_addr; 2731057Salm while ((addr = next_addr()) >= 0) { 2741057Salm addr_cnt++; 2751057Salm first_addr = second_addr; 2761057Salm second_addr = addr; 2771057Salm if (*ibufp != ',' && *ibufp != ';') 2781057Salm break; 2791057Salm else if (*ibufp++ == ';') 2801057Salm current_addr = addr; 2811057Salm } 2821057Salm if ((addr_cnt = min(addr_cnt, 2)) == 1 || second_addr != addr) 2831057Salm first_addr = second_addr; 2841057Salm return (addr == ERR) ? ERR : 0; 2851057Salm} 2861057Salm 2871057Salm 28817516Sache#define SKIP_BLANKS() while (isspace((unsigned char)*ibufp) && *ibufp != '\n') ibufp++ 2891057Salm 29081220Smike#define MUST_BE_FIRST() do { \ 29181220Smike if (!first) { \ 29281220Smike errmsg = "invalid address"; \ 29381220Smike return ERR; \ 29481220Smike } \ 29581220Smike} while (0); 2961057Salm 2971057Salm/* next_addr: return the next line address in the command buffer */ 2981057Salmlong 29990109Simpnext_addr(void) 3001057Salm{ 30181220Smike const char *hd; 3021057Salm long addr = current_addr; 3031057Salm long n; 3041057Salm int first = 1; 3051057Salm int c; 3061057Salm 3071057Salm SKIP_BLANKS(); 3081057Salm for (hd = ibufp;; first = 0) 3091057Salm switch (c = *ibufp) { 3101057Salm case '+': 3111057Salm case '\t': 3121057Salm case ' ': 3131057Salm case '-': 3141057Salm case '^': 3151057Salm ibufp++; 3161057Salm SKIP_BLANKS(); 31717516Sache if (isdigit((unsigned char)*ibufp)) { 3181057Salm STRTOL(n, ibufp); 3191057Salm addr += (c == '-' || c == '^') ? -n : n; 32017516Sache } else if (!isspace((unsigned char)c)) 3211057Salm addr += (c == '-' || c == '^') ? -1 : 1; 3221057Salm break; 3231057Salm case '0': case '1': case '2': 3241057Salm case '3': case '4': case '5': 3251057Salm case '6': case '7': case '8': case '9': 3261057Salm MUST_BE_FIRST(); 3271057Salm STRTOL(addr, ibufp); 3281057Salm break; 3291057Salm case '.': 3301057Salm case '$': 3311057Salm MUST_BE_FIRST(); 3321057Salm ibufp++; 3331057Salm addr = (c == '.') ? current_addr : addr_last; 3341057Salm break; 3351057Salm case '/': 3361057Salm case '?': 3371057Salm MUST_BE_FIRST(); 3381057Salm if ((addr = get_matching_node_addr( 3391057Salm get_compiled_pattern(), c == '/')) < 0) 3401057Salm return ERR; 3411057Salm else if (c == *ibufp) 3421057Salm ibufp++; 3431057Salm break; 3441057Salm case '\'': 3451057Salm MUST_BE_FIRST(); 3461057Salm ibufp++; 3471057Salm if ((addr = get_marked_node_addr(*ibufp++)) < 0) 3481057Salm return ERR; 3491057Salm break; 3501057Salm case '%': 3511057Salm case ',': 3521057Salm case ';': 3531057Salm if (first) { 3541057Salm ibufp++; 3551057Salm addr_cnt++; 3561057Salm second_addr = (c == ';') ? current_addr : 1; 3571057Salm addr = addr_last; 3581057Salm break; 3591057Salm } 360102410Scharnier /* FALLTHROUGH */ 3611057Salm default: 3621057Salm if (ibufp == hd) 3631057Salm return EOF; 3641057Salm else if (addr < 0 || addr_last < addr) { 36581220Smike errmsg = "invalid address"; 3661057Salm return ERR; 3671057Salm } else 3681057Salm return addr; 3691057Salm } 3701057Salm /* NOTREACHED */ 3711057Salm} 3721057Salm 3731057Salm 3741057Salm#ifdef BACKWARDS 3751057Salm/* GET_THIRD_ADDR: get a legal address from the command buffer */ 3761057Salm#define GET_THIRD_ADDR(addr) \ 3771057Salm{ \ 3781057Salm long ol1, ol2; \ 3791057Salm\ 3801057Salm ol1 = first_addr, ol2 = second_addr; \ 3811057Salm if (extract_addr_range() < 0) \ 3821057Salm return ERR; \ 3831057Salm else if (addr_cnt == 0) { \ 38481220Smike errmsg = "destination expected"; \ 3851057Salm return ERR; \ 3861057Salm } else if (second_addr < 0 || addr_last < second_addr) { \ 38781220Smike errmsg = "invalid address"; \ 3881057Salm return ERR; \ 3891057Salm } \ 3901057Salm addr = second_addr; \ 3911057Salm first_addr = ol1, second_addr = ol2; \ 3921057Salm} 3931057Salm#else /* BACKWARDS */ 3941057Salm/* GET_THIRD_ADDR: get a legal address from the command buffer */ 3951057Salm#define GET_THIRD_ADDR(addr) \ 3961057Salm{ \ 3971057Salm long ol1, ol2; \ 3981057Salm\ 3991057Salm ol1 = first_addr, ol2 = second_addr; \ 4001057Salm if (extract_addr_range() < 0) \ 4011057Salm return ERR; \ 4021057Salm if (second_addr < 0 || addr_last < second_addr) { \ 40381220Smike errmsg = "invalid address"; \ 4041057Salm return ERR; \ 4051057Salm } \ 4061057Salm addr = second_addr; \ 4071057Salm first_addr = ol1, second_addr = ol2; \ 4081057Salm} 4091057Salm#endif 4101057Salm 4111057Salm 4121057Salm/* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */ 4131057Salm#define GET_COMMAND_SUFFIX() { \ 4141057Salm int done = 0; \ 4151057Salm do { \ 4161057Salm switch(*ibufp) { \ 4171057Salm case 'p': \ 4181057Salm gflag |= GPR, ibufp++; \ 4191057Salm break; \ 4201057Salm case 'l': \ 4211057Salm gflag |= GLS, ibufp++; \ 4221057Salm break; \ 4231057Salm case 'n': \ 4241057Salm gflag |= GNP, ibufp++; \ 4251057Salm break; \ 4261057Salm default: \ 4271057Salm done++; \ 4281057Salm } \ 4291057Salm } while (!done); \ 4301057Salm if (*ibufp++ != '\n') { \ 43181220Smike errmsg = "invalid command suffix"; \ 4321057Salm return ERR; \ 4331057Salm } \ 4341057Salm} 4351057Salm 4361057Salm 4371057Salm/* sflags */ 4381057Salm#define SGG 001 /* complement previous global substitute suffix */ 4391057Salm#define SGP 002 /* complement previous print suffix */ 4401057Salm#define SGR 004 /* use last regex instead of last pat */ 4411057Salm#define SGF 010 /* repeat last substitution */ 4421057Salm 4431057Salmint patlock = 0; /* if set, pattern not freed by get_compiled_pattern() */ 4441057Salm 4451057Salmlong rows = 22; /* scroll length: ws_row - 2 */ 4461057Salm 4471057Salm/* exec_command: execute the next command in command buffer; return print 4481057Salm request, if any */ 4491057Salmint 45090109Simpexec_command(void) 4511057Salm{ 4521057Salm static pattern_t *pat = NULL; 4531057Salm static int sgflag = 0; 45438161Sdfr static long sgnum = 0; 4551057Salm 4561057Salm pattern_t *tpat; 4571057Salm char *fnp; 4581057Salm int gflag = 0; 4591057Salm int sflags = 0; 4601057Salm long addr = 0; 4611057Salm int n = 0; 4621057Salm int c; 4631057Salm 4641057Salm SKIP_BLANKS(); 4651057Salm switch(c = *ibufp++) { 4661057Salm case 'a': 4671057Salm GET_COMMAND_SUFFIX(); 4681057Salm if (!isglobal) clear_undo_stack(); 4691057Salm if (append_lines(second_addr) < 0) 4701057Salm return ERR; 4711057Salm break; 4721057Salm case 'c': 4731057Salm if (check_addr_range(current_addr, current_addr) < 0) 4741057Salm return ERR; 4751057Salm GET_COMMAND_SUFFIX(); 4761057Salm if (!isglobal) clear_undo_stack(); 4771057Salm if (delete_lines(first_addr, second_addr) < 0 || 4781057Salm append_lines(current_addr) < 0) 4791057Salm return ERR; 4801057Salm break; 4811057Salm case 'd': 4821057Salm if (check_addr_range(current_addr, current_addr) < 0) 4831057Salm return ERR; 4841057Salm GET_COMMAND_SUFFIX(); 4851057Salm if (!isglobal) clear_undo_stack(); 4861057Salm if (delete_lines(first_addr, second_addr) < 0) 4871057Salm return ERR; 4881057Salm else if ((addr = INC_MOD(current_addr, addr_last)) != 0) 4891057Salm current_addr = addr; 4901057Salm break; 4911057Salm case 'e': 4921057Salm if (modified && !scripted) 4931057Salm return EMOD; 494102410Scharnier /* FALLTHROUGH */ 4951057Salm case 'E': 4961057Salm if (addr_cnt > 0) { 49781220Smike errmsg = "unexpected address"; 4981057Salm return ERR; 49917516Sache } else if (!isspace((unsigned char)*ibufp)) { 50081220Smike errmsg = "unexpected command suffix"; 5011057Salm return ERR; 5021057Salm } else if ((fnp = get_filename()) == NULL) 5031057Salm return ERR; 5041057Salm GET_COMMAND_SUFFIX(); 5051057Salm if (delete_lines(1, addr_last) < 0) 5061057Salm return ERR; 5071057Salm clear_undo_stack(); 5081057Salm if (close_sbuf() < 0) 5091057Salm return ERR; 5101057Salm else if (open_sbuf() < 0) 5111057Salm return FATAL; 5121057Salm if (*fnp && *fnp != '!') strcpy(old_filename, fnp); 5131057Salm#ifdef BACKWARDS 5141057Salm if (*fnp == '\0' && *old_filename == '\0') { 51581220Smike errmsg = "no current filename"; 5161057Salm return ERR; 5171057Salm } 5181057Salm#endif 5191057Salm if (read_file(*fnp ? fnp : old_filename, 0) < 0) 5201057Salm return ERR; 5211057Salm clear_undo_stack(); 5221057Salm modified = 0; 5231057Salm u_current_addr = u_addr_last = -1; 5241057Salm break; 5251057Salm case 'f': 5261057Salm if (addr_cnt > 0) { 52781220Smike errmsg = "unexpected address"; 5281057Salm return ERR; 52917516Sache } else if (!isspace((unsigned char)*ibufp)) { 53081220Smike errmsg = "unexpected command suffix"; 5311057Salm return ERR; 5321057Salm } else if ((fnp = get_filename()) == NULL) 5331057Salm return ERR; 5341057Salm else if (*fnp == '!') { 53581220Smike errmsg = "invalid redirection"; 5361057Salm return ERR; 5371057Salm } 5381057Salm GET_COMMAND_SUFFIX(); 5391057Salm if (*fnp) strcpy(old_filename, fnp); 5401057Salm printf("%s\n", strip_escapes(old_filename)); 5411057Salm break; 5421057Salm case 'g': 5431057Salm case 'v': 5441057Salm case 'G': 5451057Salm case 'V': 5461057Salm if (isglobal) { 54781220Smike errmsg = "cannot nest global commands"; 5481057Salm return ERR; 5491057Salm } else if (check_addr_range(1, addr_last) < 0) 5501057Salm return ERR; 5511057Salm else if (build_active_list(c == 'g' || c == 'G') < 0) 5521057Salm return ERR; 5537165Sjoerg else if ((n = (c == 'G' || c == 'V'))) 5541057Salm GET_COMMAND_SUFFIX(); 5551057Salm isglobal++; 5561057Salm if (exec_global(n, gflag) < 0) 5578855Srgrimes return ERR; 5581057Salm break; 5591057Salm case 'h': 5601057Salm if (addr_cnt > 0) { 56181220Smike errmsg = "unexpected address"; 5621057Salm return ERR; 5631057Salm } 5641057Salm GET_COMMAND_SUFFIX(); 5651057Salm if (*errmsg) fprintf(stderr, "%s\n", errmsg); 5661057Salm break; 5671057Salm case 'H': 5681057Salm if (addr_cnt > 0) { 56981220Smike errmsg = "unexpected address"; 5701057Salm return ERR; 5711057Salm } 5721057Salm GET_COMMAND_SUFFIX(); 5731057Salm if ((garrulous = 1 - garrulous) && *errmsg) 5741057Salm fprintf(stderr, "%s\n", errmsg); 5751057Salm break; 5761057Salm case 'i': 5771057Salm if (second_addr == 0) { 57881220Smike errmsg = "invalid address"; 5791057Salm return ERR; 5801057Salm } 5811057Salm GET_COMMAND_SUFFIX(); 5821057Salm if (!isglobal) clear_undo_stack(); 5831057Salm if (append_lines(second_addr - 1) < 0) 5841057Salm return ERR; 5851057Salm break; 5861057Salm case 'j': 5871057Salm if (check_addr_range(current_addr, current_addr + 1) < 0) 5881057Salm return ERR; 5891057Salm GET_COMMAND_SUFFIX(); 5901057Salm if (!isglobal) clear_undo_stack(); 5911057Salm if (first_addr != second_addr && 5921057Salm join_lines(first_addr, second_addr) < 0) 5931057Salm return ERR; 5941057Salm break; 5951057Salm case 'k': 5961057Salm c = *ibufp++; 5971057Salm if (second_addr == 0) { 59881220Smike errmsg = "invalid address"; 5991057Salm return ERR; 6001057Salm } 6011057Salm GET_COMMAND_SUFFIX(); 6021057Salm if (mark_line_node(get_addressed_line_node(second_addr), c) < 0) 6031057Salm return ERR; 6041057Salm break; 6051057Salm case 'l': 6061057Salm if (check_addr_range(current_addr, current_addr) < 0) 6071057Salm return ERR; 6081057Salm GET_COMMAND_SUFFIX(); 6091057Salm if (display_lines(first_addr, second_addr, gflag | GLS) < 0) 6101057Salm return ERR; 6111057Salm gflag = 0; 6121057Salm break; 6131057Salm case 'm': 6141057Salm if (check_addr_range(current_addr, current_addr) < 0) 6151057Salm return ERR; 6161057Salm GET_THIRD_ADDR(addr); 6171057Salm if (first_addr <= addr && addr < second_addr) { 61881220Smike errmsg = "invalid destination"; 6191057Salm return ERR; 6201057Salm } 6211057Salm GET_COMMAND_SUFFIX(); 6221057Salm if (!isglobal) clear_undo_stack(); 6231057Salm if (move_lines(addr) < 0) 6241057Salm return ERR; 6251057Salm break; 6261057Salm case 'n': 6271057Salm if (check_addr_range(current_addr, current_addr) < 0) 6281057Salm return ERR; 6291057Salm GET_COMMAND_SUFFIX(); 6301057Salm if (display_lines(first_addr, second_addr, gflag | GNP) < 0) 6311057Salm return ERR; 6321057Salm gflag = 0; 6331057Salm break; 6341057Salm case 'p': 6351057Salm if (check_addr_range(current_addr, current_addr) < 0) 6361057Salm return ERR; 6371057Salm GET_COMMAND_SUFFIX(); 6381057Salm if (display_lines(first_addr, second_addr, gflag | GPR) < 0) 6391057Salm return ERR; 6401057Salm gflag = 0; 6411057Salm break; 6421057Salm case 'P': 6431057Salm if (addr_cnt > 0) { 64481220Smike errmsg = "unexpected address"; 6451057Salm return ERR; 6461057Salm } 6471057Salm GET_COMMAND_SUFFIX(); 6481057Salm prompt = prompt ? NULL : optarg ? optarg : dps; 6491057Salm break; 6501057Salm case 'q': 6511057Salm case 'Q': 6521057Salm if (addr_cnt > 0) { 65381220Smike errmsg = "unexpected address"; 6541057Salm return ERR; 6551057Salm } 6561057Salm GET_COMMAND_SUFFIX(); 6571057Salm gflag = (modified && !scripted && c == 'q') ? EMOD : EOF; 6581057Salm break; 6591057Salm case 'r': 66017516Sache if (!isspace((unsigned char)*ibufp)) { 66181220Smike errmsg = "unexpected command suffix"; 6621057Salm return ERR; 6631057Salm } else if (addr_cnt == 0) 6641057Salm second_addr = addr_last; 6651057Salm if ((fnp = get_filename()) == NULL) 6661057Salm return ERR; 6671057Salm GET_COMMAND_SUFFIX(); 6681057Salm if (!isglobal) clear_undo_stack(); 6691057Salm if (*old_filename == '\0' && *fnp != '!') 6701057Salm strcpy(old_filename, fnp); 6711057Salm#ifdef BACKWARDS 6721057Salm if (*fnp == '\0' && *old_filename == '\0') { 67381220Smike errmsg = "no current filename"; 6741057Salm return ERR; 6751057Salm } 6761057Salm#endif 6771057Salm if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0) 6781057Salm return ERR; 6791057Salm else if (addr && addr != addr_last) 6801057Salm modified = 1; 6811057Salm break; 6821057Salm case 's': 6831057Salm do { 6841057Salm switch(*ibufp) { 6851057Salm case '\n': 6861057Salm sflags |=SGF; 6871057Salm break; 6881057Salm case 'g': 6891057Salm sflags |= SGG; 6901057Salm ibufp++; 6911057Salm break; 6921057Salm case 'p': 6931057Salm sflags |= SGP; 6941057Salm ibufp++; 6951057Salm break; 6961057Salm case 'r': 6971057Salm sflags |= SGR; 6981057Salm ibufp++; 6991057Salm break; 7008855Srgrimes case '0': case '1': case '2': case '3': case '4': 7011057Salm case '5': case '6': case '7': case '8': case '9': 7021057Salm STRTOL(sgnum, ibufp); 7031057Salm sflags |= SGF; 7041057Salm sgflag &= ~GSG; /* override GSG */ 7051057Salm break; 7061057Salm default: 7071057Salm if (sflags) { 70881220Smike errmsg = "invalid command suffix"; 7091057Salm return ERR; 7101057Salm } 7111057Salm } 7121057Salm } while (sflags && *ibufp != '\n'); 7131057Salm if (sflags && !pat) { 71481220Smike errmsg = "no previous substitution"; 7151057Salm return ERR; 7161057Salm } else if (sflags & SGG) 7171057Salm sgnum = 0; /* override numeric arg */ 7181057Salm if (*ibufp != '\n' && *(ibufp + 1) == '\n') { 71981220Smike errmsg = "invalid pattern delimiter"; 7201057Salm return ERR; 7211057Salm } 7221057Salm tpat = pat; 7231057Salm SPL1(); 7241057Salm if ((!sflags || (sflags & SGR)) && 7251057Salm (tpat = get_compiled_pattern()) == NULL) { 7261057Salm SPL0(); 7271057Salm return ERR; 7281057Salm } else if (tpat != pat) { 7291057Salm if (pat) { 7301057Salm regfree(pat); 7311057Salm free(pat); 7321057Salm } 7331057Salm pat = tpat; 7341057Salm patlock = 1; /* reserve pattern */ 7351057Salm } 7361057Salm SPL0(); 7371057Salm if (!sflags && extract_subst_tail(&sgflag, &sgnum) < 0) 7381057Salm return ERR; 7391057Salm else if (isglobal) 7401057Salm sgflag |= GLB; 7411057Salm else 7421057Salm sgflag &= ~GLB; 7431057Salm if (sflags & SGG) 7441057Salm sgflag ^= GSG; 7451057Salm if (sflags & SGP) 7461057Salm sgflag ^= GPR, sgflag &= ~(GLS | GNP); 7471057Salm do { 7481057Salm switch(*ibufp) { 7491057Salm case 'p': 7501057Salm sgflag |= GPR, ibufp++; 7511057Salm break; 7521057Salm case 'l': 7531057Salm sgflag |= GLS, ibufp++; 7541057Salm break; 7551057Salm case 'n': 7561057Salm sgflag |= GNP, ibufp++; 7571057Salm break; 7581057Salm default: 7591057Salm n++; 7601057Salm } 7611057Salm } while (!n); 7621057Salm if (check_addr_range(current_addr, current_addr) < 0) 7631057Salm return ERR; 7641057Salm GET_COMMAND_SUFFIX(); 7651057Salm if (!isglobal) clear_undo_stack(); 7661057Salm if (search_and_replace(pat, sgflag, sgnum) < 0) 7671057Salm return ERR; 7681057Salm break; 7691057Salm case 't': 7701057Salm if (check_addr_range(current_addr, current_addr) < 0) 7711057Salm return ERR; 7721057Salm GET_THIRD_ADDR(addr); 7731057Salm GET_COMMAND_SUFFIX(); 7741057Salm if (!isglobal) clear_undo_stack(); 7751057Salm if (copy_lines(addr) < 0) 7761057Salm return ERR; 7771057Salm break; 7781057Salm case 'u': 7791057Salm if (addr_cnt > 0) { 78081220Smike errmsg = "unexpected address"; 7811057Salm return ERR; 7821057Salm } 7831057Salm GET_COMMAND_SUFFIX(); 7841057Salm if (pop_undo_stack() < 0) 7851057Salm return ERR; 7861057Salm break; 7871057Salm case 'w': 7881057Salm case 'W': 7891057Salm if ((n = *ibufp) == 'q' || n == 'Q') { 7901057Salm gflag = EOF; 7911057Salm ibufp++; 7921057Salm } 79317516Sache if (!isspace((unsigned char)*ibufp)) { 79481220Smike errmsg = "unexpected command suffix"; 7951057Salm return ERR; 7961057Salm } else if ((fnp = get_filename()) == NULL) 7971057Salm return ERR; 7981057Salm if (addr_cnt == 0 && !addr_last) 7991057Salm first_addr = second_addr = 0; 8001057Salm else if (check_addr_range(1, addr_last) < 0) 8011057Salm return ERR; 8021057Salm GET_COMMAND_SUFFIX(); 8031057Salm if (*old_filename == '\0' && *fnp != '!') 8041057Salm strcpy(old_filename, fnp); 8051057Salm#ifdef BACKWARDS 8061057Salm if (*fnp == '\0' && *old_filename == '\0') { 80781220Smike errmsg = "no current filename"; 8081057Salm return ERR; 8091057Salm } 8101057Salm#endif 8118855Srgrimes if ((addr = write_file(*fnp ? fnp : old_filename, 8121057Salm (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0) 8131057Salm return ERR; 8141057Salm else if (addr == addr_last) 8151057Salm modified = 0; 8161057Salm else if (modified && !scripted && n == 'q') 8171057Salm gflag = EMOD; 8181057Salm break; 8191057Salm case 'x': 8201057Salm if (addr_cnt > 0) { 82181220Smike errmsg = "unexpected address"; 8221057Salm return ERR; 8231057Salm } 8241057Salm GET_COMMAND_SUFFIX(); 8251057Salm#ifdef DES 8261057Salm des = get_keyword(); 827140344Scharnier break; 8281057Salm#else 82981220Smike errmsg = "crypt unavailable"; 8301057Salm return ERR; 8311057Salm#endif 8321057Salm case 'z': 8331057Salm#ifdef BACKWARDS 8341057Salm if (check_addr_range(first_addr = 1, current_addr + 1) < 0) 8351057Salm#else 8361057Salm if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0) 8371057Salm#endif 8381057Salm return ERR; 8391057Salm else if ('0' < *ibufp && *ibufp <= '9') 8401057Salm STRTOL(rows, ibufp); 8411057Salm GET_COMMAND_SUFFIX(); 8421057Salm if (display_lines(second_addr, min(addr_last, 8431057Salm second_addr + rows), gflag) < 0) 8441057Salm return ERR; 8451057Salm gflag = 0; 8461057Salm break; 8471057Salm case '=': 8481057Salm GET_COMMAND_SUFFIX(); 8497165Sjoerg printf("%ld\n", addr_cnt ? second_addr : addr_last); 8501057Salm break; 8511057Salm case '!': 8521057Salm if (addr_cnt > 0) { 85381220Smike errmsg = "unexpected address"; 8541057Salm return ERR; 8551057Salm } else if ((sflags = get_shell_command()) < 0) 8561057Salm return ERR; 8571057Salm GET_COMMAND_SUFFIX(); 8581057Salm if (sflags) printf("%s\n", shcmd + 1); 8591057Salm system(shcmd + 1); 8601057Salm if (!scripted) printf("!\n"); 8611057Salm break; 8621057Salm case '\n': 8631057Salm#ifdef BACKWARDS 8641057Salm if (check_addr_range(first_addr = 1, current_addr + 1) < 0 8651057Salm#else 8661057Salm if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0 8671057Salm#endif 8681057Salm || display_lines(second_addr, second_addr, 0) < 0) 8691057Salm return ERR; 8701057Salm break; 8711057Salm default: 87281220Smike errmsg = "unknown command"; 8731057Salm return ERR; 8741057Salm } 8751057Salm return gflag; 8761057Salm} 8771057Salm 8781057Salm 8791057Salm/* check_addr_range: return status of address range check */ 8801057Salmint 88190109Simpcheck_addr_range(long n, long m) 8821057Salm{ 8831057Salm if (addr_cnt == 0) { 8841057Salm first_addr = n; 8851057Salm second_addr = m; 8861057Salm } 8871057Salm if (first_addr > second_addr || 1 > first_addr || 8881057Salm second_addr > addr_last) { 88981220Smike errmsg = "invalid address"; 8901057Salm return ERR; 8911057Salm } 8921057Salm return 0; 8931057Salm} 8941057Salm 8951057Salm 8968855Srgrimes/* get_matching_node_addr: return the address of the next line matching a 8971057Salm pattern in a given direction. wrap around begin/end of editor buffer if 8981057Salm necessary */ 8991057Salmlong 90090109Simpget_matching_node_addr(pattern_t *pat, int dir) 9011057Salm{ 9021057Salm char *s; 9031057Salm long n = current_addr; 9041057Salm line_t *lp; 9051057Salm 9061057Salm if (!pat) return ERR; 9071057Salm do { 9087165Sjoerg if ((n = dir ? INC_MOD(n, addr_last) : DEC_MOD(n, addr_last))) { 9091057Salm lp = get_addressed_line_node(n); 9101057Salm if ((s = get_sbuf_line(lp)) == NULL) 9111057Salm return ERR; 9121057Salm if (isbinary) 9131057Salm NUL_TO_NEWLINE(s, lp->len); 9141057Salm if (!regexec(pat, s, 0, NULL, 0)) 9151057Salm return n; 9167165Sjoerg } 9171057Salm } while (n != current_addr); 91881220Smike errmsg = "no match"; 9191057Salm return ERR; 9201057Salm} 9211057Salm 9221057Salm 9231057Salm/* get_filename: return pointer to copy of filename in the command buffer */ 9241057Salmchar * 92590109Simpget_filename(void) 9261057Salm{ 9271057Salm static char *file = NULL; 9281057Salm static int filesz = 0; 9291057Salm 9301057Salm int n; 9311057Salm 9321057Salm if (*ibufp != '\n') { 9331057Salm SKIP_BLANKS(); 9341057Salm if (*ibufp == '\n') { 93581220Smike errmsg = "invalid filename"; 9361057Salm return NULL; 9371057Salm } else if ((ibufp = get_extended_line(&n, 1)) == NULL) 9381057Salm return NULL; 9391057Salm else if (*ibufp == '!') { 9401057Salm ibufp++; 9411057Salm if ((n = get_shell_command()) < 0) 9421057Salm return NULL; 94377407Simp if (n) 94477407Simp printf("%s\n", shcmd + 1); 9451057Salm return shcmd; 94677407Simp } else if (n > PATH_MAX - 1) { 94781220Smike errmsg = "filename too long"; 9481057Salm return NULL; 9491057Salm } 9501057Salm } 9511057Salm#ifndef BACKWARDS 9521057Salm else if (*old_filename == '\0') { 95381220Smike errmsg = "no current filename"; 9541057Salm return NULL; 9551057Salm } 9561057Salm#endif 95777407Simp REALLOC(file, filesz, PATH_MAX, NULL); 9581057Salm for (n = 0; *ibufp != '\n';) 9591057Salm file[n++] = *ibufp++; 9601057Salm file[n] = '\0'; 9611057Salm return is_legal_filename(file) ? file : NULL; 9621057Salm} 9631057Salm 9641057Salm 9651057Salm/* get_shell_command: read a shell command from stdin; return substitution 9661057Salm status */ 9671057Salmint 96890109Simpget_shell_command(void) 9691057Salm{ 9701057Salm static char *buf = NULL; 9711057Salm static int n = 0; 9721057Salm 9731057Salm char *s; /* substitution char pointer */ 9741057Salm int i = 0; 9751057Salm int j = 0; 9761057Salm 9771057Salm if (red) { 97881220Smike errmsg = "shell access restricted"; 9791057Salm return ERR; 9801057Salm } else if ((s = ibufp = get_extended_line(&j, 1)) == NULL) 9811057Salm return ERR; 9821057Salm REALLOC(buf, n, j + 1, ERR); 9831057Salm buf[i++] = '!'; /* prefix command w/ bang */ 9841057Salm while (*ibufp != '\n') 9851057Salm switch (*ibufp) { 9861057Salm default: 9871057Salm REALLOC(buf, n, i + 2, ERR); 9881057Salm buf[i++] = *ibufp; 9891057Salm if (*ibufp++ == '\\') 9901057Salm buf[i++] = *ibufp++; 9911057Salm break; 9921057Salm case '!': 9931057Salm if (s != ibufp) { 9941057Salm REALLOC(buf, n, i + 1, ERR); 9951057Salm buf[i++] = *ibufp++; 9961057Salm } 9971057Salm#ifdef BACKWARDS 9981057Salm else if (shcmd == NULL || *(shcmd + 1) == '\0') 9991057Salm#else 10001057Salm else if (shcmd == NULL) 10011057Salm#endif 10021057Salm { 100381220Smike errmsg = "no previous command"; 10041057Salm return ERR; 10051057Salm } else { 10061057Salm REALLOC(buf, n, i + shcmdi, ERR); 10071057Salm for (s = shcmd + 1; s < shcmd + shcmdi;) 10081057Salm buf[i++] = *s++; 10091057Salm s = ibufp++; 10101057Salm } 10111057Salm break; 10121057Salm case '%': 10131057Salm if (*old_filename == '\0') { 101481220Smike errmsg = "no current filename"; 10151057Salm return ERR; 10161057Salm } 10171057Salm j = strlen(s = strip_escapes(old_filename)); 10181057Salm REALLOC(buf, n, i + j, ERR); 10191057Salm while (j--) 10201057Salm buf[i++] = *s++; 10211057Salm s = ibufp++; 10221057Salm break; 10231057Salm } 10241057Salm REALLOC(shcmd, shcmdsz, i + 1, ERR); 10251057Salm memcpy(shcmd, buf, i); 10261057Salm shcmd[shcmdi = i] = '\0'; 10271057Salm return *s == '!' || *s == '%'; 10281057Salm} 10291057Salm 10301057Salm 10311057Salm/* append_lines: insert text from stdin to after line n; stop when either a 10321057Salm single period is read or EOF; return status */ 10331057Salmint 103490109Simpappend_lines(long n) 10351057Salm{ 10361057Salm int l; 103781220Smike const char *lp = ibuf; 103881220Smike const char *eot; 10391057Salm undo_t *up = NULL; 10401057Salm 10411057Salm for (current_addr = n;;) { 10421057Salm if (!isglobal) { 10431057Salm if ((l = get_tty_line()) < 0) 10441057Salm return ERR; 10451057Salm else if (l == 0 || ibuf[l - 1] != '\n') { 10461057Salm clearerr(stdin); 10471057Salm return l ? EOF : 0; 10481057Salm } 10491057Salm lp = ibuf; 10501057Salm } else if (*(lp = ibufp) == '\0') 10511057Salm return 0; 10521057Salm else { 10531057Salm while (*ibufp++ != '\n') 10541057Salm ; 10551057Salm l = ibufp - lp; 10561057Salm } 10571057Salm if (l == 2 && lp[0] == '.' && lp[1] == '\n') { 10581057Salm return 0; 10591057Salm } 10601057Salm eot = lp + l; 10611057Salm SPL1(); 10621057Salm do { 10631057Salm if ((lp = put_sbuf_line(lp)) == NULL) { 10641057Salm SPL0(); 10651057Salm return ERR; 10661057Salm } else if (up) 10671057Salm up->t = get_addressed_line_node(current_addr); 10681057Salm else if ((up = push_undo_stack(UADD, current_addr, 10691057Salm current_addr)) == NULL) { 10701057Salm SPL0(); 10711057Salm return ERR; 10721057Salm } 10731057Salm } while (lp != eot); 10741057Salm modified = 1; 10751057Salm SPL0(); 10761057Salm } 10771057Salm /* NOTREACHED */ 10781057Salm} 10791057Salm 10801057Salm 10811057Salm/* join_lines: replace a range of lines with the joined text of those lines */ 10821057Salmint 108390109Simpjoin_lines(long from, long to) 10841057Salm{ 10851057Salm static char *buf = NULL; 10861057Salm static int n; 10871057Salm 10881057Salm char *s; 10891057Salm int size = 0; 10901057Salm line_t *bp, *ep; 10911057Salm 10921057Salm ep = get_addressed_line_node(INC_MOD(to, addr_last)); 10931057Salm bp = get_addressed_line_node(from); 10941057Salm for (; bp != ep; bp = bp->q_forw) { 10951057Salm if ((s = get_sbuf_line(bp)) == NULL) 10961057Salm return ERR; 10971057Salm REALLOC(buf, n, size + bp->len, ERR); 10981057Salm memcpy(buf + size, s, bp->len); 10991057Salm size += bp->len; 11001057Salm } 11011057Salm REALLOC(buf, n, size + 2, ERR); 11021057Salm memcpy(buf + size, "\n", 2); 11031057Salm if (delete_lines(from, to) < 0) 11041057Salm return ERR; 11051057Salm current_addr = from - 1; 11061057Salm SPL1(); 11071057Salm if (put_sbuf_line(buf) == NULL || 11081057Salm push_undo_stack(UADD, current_addr, current_addr) == NULL) { 11091057Salm SPL0(); 11101057Salm return ERR; 11111057Salm } 11121057Salm modified = 1; 11131057Salm SPL0(); 11141057Salm return 0; 11151057Salm} 11161057Salm 11171057Salm 11181057Salm/* move_lines: move a range of lines */ 11191057Salmint 112090109Simpmove_lines(long addr) 11211057Salm{ 11221057Salm line_t *b1, *a1, *b2, *a2; 11231057Salm long n = INC_MOD(second_addr, addr_last); 11241057Salm long p = first_addr - 1; 11251057Salm int done = (addr == first_addr - 1 || addr == second_addr); 11261057Salm 11271057Salm SPL1(); 11281057Salm if (done) { 11291057Salm a2 = get_addressed_line_node(n); 11301057Salm b2 = get_addressed_line_node(p); 11311057Salm current_addr = second_addr; 11321057Salm } else if (push_undo_stack(UMOV, p, n) == NULL || 11331057Salm push_undo_stack(UMOV, addr, INC_MOD(addr, addr_last)) == NULL) { 11341057Salm SPL0(); 11351057Salm return ERR; 11361057Salm } else { 11371057Salm a1 = get_addressed_line_node(n); 11381057Salm if (addr < first_addr) { 11391057Salm b1 = get_addressed_line_node(p); 11401057Salm b2 = get_addressed_line_node(addr); 11411057Salm /* this get_addressed_line_node last! */ 11421057Salm } else { 11431057Salm b2 = get_addressed_line_node(addr); 11441057Salm b1 = get_addressed_line_node(p); 11451057Salm /* this get_addressed_line_node last! */ 11461057Salm } 11471057Salm a2 = b2->q_forw; 11481057Salm REQUE(b2, b1->q_forw); 11491057Salm REQUE(a1->q_back, a2); 11501057Salm REQUE(b1, a1); 11518855Srgrimes current_addr = addr + ((addr < first_addr) ? 11521057Salm second_addr - first_addr + 1 : 0); 11531057Salm } 11541057Salm if (isglobal) 11551057Salm unset_active_nodes(b2->q_forw, a2); 11561057Salm modified = 1; 11571057Salm SPL0(); 11581057Salm return 0; 11591057Salm} 11601057Salm 11611057Salm 11621057Salm/* copy_lines: copy a range of lines; return status */ 11631057Salmint 116490109Simpcopy_lines(long addr) 11651057Salm{ 11661057Salm line_t *lp, *np = get_addressed_line_node(first_addr); 11671057Salm undo_t *up = NULL; 11681057Salm long n = second_addr - first_addr + 1; 11691057Salm long m = 0; 11701057Salm 11711057Salm current_addr = addr; 11721057Salm if (first_addr <= addr && addr < second_addr) { 11731057Salm n = addr - first_addr + 1; 11741057Salm m = second_addr - addr; 11751057Salm } 11761057Salm for (; n > 0; n=m, m=0, np = get_addressed_line_node(current_addr + 1)) 11771057Salm for (; n-- > 0; np = np->q_forw) { 11781057Salm SPL1(); 11791057Salm if ((lp = dup_line_node(np)) == NULL) { 11801057Salm SPL0(); 11811057Salm return ERR; 11821057Salm } 11831057Salm add_line_node(lp); 11841057Salm if (up) 11851057Salm up->t = lp; 11861057Salm else if ((up = push_undo_stack(UADD, current_addr, 11871057Salm current_addr)) == NULL) { 11881057Salm SPL0(); 11891057Salm return ERR; 11901057Salm } 11911057Salm modified = 1; 11921057Salm SPL0(); 11931057Salm } 11941057Salm return 0; 11951057Salm} 11961057Salm 11971057Salm 11981057Salm/* delete_lines: delete a range of lines */ 11991057Salmint 120090109Simpdelete_lines(long from, long to) 12011057Salm{ 12021057Salm line_t *n, *p; 12031057Salm 12041057Salm SPL1(); 12051057Salm if (push_undo_stack(UDEL, from, to) == NULL) { 12061057Salm SPL0(); 12071057Salm return ERR; 12081057Salm } 12091057Salm n = get_addressed_line_node(INC_MOD(to, addr_last)); 12101057Salm p = get_addressed_line_node(from - 1); 12111057Salm /* this get_addressed_line_node last! */ 12121057Salm if (isglobal) 12131057Salm unset_active_nodes(p->q_forw, n); 12141057Salm REQUE(p, n); 12151057Salm addr_last -= to - from + 1; 12161057Salm current_addr = from - 1; 12171057Salm modified = 1; 12181057Salm SPL0(); 12191057Salm return 0; 12201057Salm} 12211057Salm 12221057Salm 12231057Salm/* display_lines: print a range of lines to stdout */ 12241057Salmint 122590109Simpdisplay_lines(long from, long to, int gflag) 12261057Salm{ 12271057Salm line_t *bp; 12281057Salm line_t *ep; 12291057Salm char *s; 12301057Salm 12311057Salm if (!from) { 123281220Smike errmsg = "invalid address"; 12331057Salm return ERR; 12341057Salm } 12351057Salm ep = get_addressed_line_node(INC_MOD(to, addr_last)); 12361057Salm bp = get_addressed_line_node(from); 12371057Salm for (; bp != ep; bp = bp->q_forw) { 12381057Salm if ((s = get_sbuf_line(bp)) == NULL) 12391057Salm return ERR; 12401057Salm if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0) 12411057Salm return ERR; 12421057Salm } 12431057Salm return 0; 12441057Salm} 12451057Salm 12461057Salm 12471057Salm#define MAXMARK 26 /* max number of marks */ 12481057Salm 12491057Salmline_t *mark[MAXMARK]; /* line markers */ 12501057Salmint markno; /* line marker count */ 12511057Salm 12521057Salm/* mark_line_node: set a line node mark */ 12531057Salmint 125490109Simpmark_line_node(line_t *lp, int n) 12551057Salm{ 125617516Sache if (!islower((unsigned char)n)) { 125781220Smike errmsg = "invalid mark character"; 12581057Salm return ERR; 12591057Salm } else if (mark[n - 'a'] == NULL) 12601057Salm markno++; 12611057Salm mark[n - 'a'] = lp; 12621057Salm return 0; 12631057Salm} 12641057Salm 12651057Salm 12661057Salm/* get_marked_node_addr: return address of a marked line */ 12671057Salmlong 126890109Simpget_marked_node_addr(int n) 12691057Salm{ 127017516Sache if (!islower((unsigned char)n)) { 127181220Smike errmsg = "invalid mark character"; 12721057Salm return ERR; 12731057Salm } 12741057Salm return get_line_node_addr(mark[n - 'a']); 12751057Salm} 12761057Salm 12771057Salm 12781057Salm/* unmark_line_node: clear line node mark */ 12791057Salmvoid 128090109Simpunmark_line_node(line_t *lp) 12811057Salm{ 12821057Salm int i; 12831057Salm 12841057Salm for (i = 0; markno && i < MAXMARK; i++) 12851057Salm if (mark[i] == lp) { 12861057Salm mark[i] = NULL; 12871057Salm markno--; 12881057Salm } 12891057Salm} 12901057Salm 12911057Salm 12921057Salm/* dup_line_node: return a pointer to a copy of a line node */ 12931057Salmline_t * 129490109Simpdup_line_node(line_t *lp) 12951057Salm{ 12961057Salm line_t *np; 12971057Salm 12981057Salm if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) { 12991057Salm fprintf(stderr, "%s\n", strerror(errno)); 130081220Smike errmsg = "out of memory"; 13011057Salm return NULL; 13021057Salm } 13031057Salm np->seek = lp->seek; 13041057Salm np->len = lp->len; 13051057Salm return np; 13061057Salm} 13071057Salm 13081057Salm 13091057Salm/* has_trailing_escape: return the parity of escapes preceding a character 13101057Salm in a string */ 13111057Salmint 131290109Simphas_trailing_escape(char *s, char *t) 13131057Salm{ 13141057Salm return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1); 13151057Salm} 13161057Salm 13171057Salm 131877407Simp/* strip_escapes: return copy of escaped string of at most length PATH_MAX */ 13191057Salmchar * 132090109Simpstrip_escapes(char *s) 13211057Salm{ 13221057Salm static char *file = NULL; 13231057Salm static int filesz = 0; 13241057Salm 13251057Salm int i = 0; 13261057Salm 132777407Simp REALLOC(file, filesz, PATH_MAX, NULL); 132859797Sjoe while (i < filesz - 1 /* Worry about a possible trailing escape */ 132959797Sjoe && (file[i++] = (*s == '\\') ? *++s : *s)) 13301057Salm s++; 13311057Salm return file; 13321057Salm} 13331057Salm 13341057Salm 13351057Salmvoid 133690109Simpsignal_hup(int signo) 13371057Salm{ 13381057Salm if (mutex) 13391057Salm sigflags |= (1 << (signo - 1)); 134030233Seivind else 134130233Seivind handle_hup(signo); 13421057Salm} 13431057Salm 13441057Salm 13451057Salmvoid 134690109Simpsignal_int(int signo) 13471057Salm{ 13481057Salm if (mutex) 13491057Salm sigflags |= (1 << (signo - 1)); 135030233Seivind else 135130233Seivind handle_int(signo); 13521057Salm} 13531057Salm 13541057Salm 13551057Salmvoid 135690109Simphandle_hup(int signo) 13571057Salm{ 13581057Salm char *hup = NULL; /* hup filename */ 13591057Salm char *s; 136081220Smike char ed_hup[] = "ed.hup"; 13611057Salm int n; 13621057Salm 13631057Salm if (!sigactive) 13641057Salm quit(1); 13651057Salm sigflags &= ~(1 << (signo - 1)); 136681220Smike if (addr_last && write_file(ed_hup, "w", 1, addr_last) < 0 && 13671057Salm (s = getenv("HOME")) != NULL && 136877407Simp (n = strlen(s)) + 8 <= PATH_MAX && /* "ed.hup" + '/' */ 13691057Salm (hup = (char *) malloc(n + 10)) != NULL) { 13701057Salm strcpy(hup, s); 13711057Salm if (hup[n - 1] != '/') 13721057Salm hup[n] = '/', hup[n+1] = '\0'; 13731057Salm strcat(hup, "ed.hup"); 13741057Salm write_file(hup, "w", 1, addr_last); 13751057Salm } 137678939Sdd quit(2); 13771057Salm} 13781057Salm 13791057Salm 13801057Salmvoid 138190109Simphandle_int(int signo) 13821057Salm{ 13831057Salm if (!sigactive) 138478939Sdd quit(1); 13851057Salm sigflags &= ~(1 << (signo - 1)); 13861057Salm#ifdef _POSIX_SOURCE 13871057Salm siglongjmp(env, -1); 13881057Salm#else 13891057Salm longjmp(env, -1); 13901057Salm#endif 13911057Salm} 13921057Salm 13931057Salm 13941057Salmint cols = 72; /* wrap column */ 13951057Salm 13961057Salmvoid 139790109Simphandle_winch(int signo) 13981057Salm{ 139930233Seivind int save_errno = errno; 140030233Seivind 14011057Salm struct winsize ws; /* window size structure */ 14021057Salm 14031057Salm sigflags &= ~(1 << (signo - 1)); 14041057Salm if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) { 14051057Salm if (ws.ws_row > 2) rows = ws.ws_row - 2; 14061057Salm if (ws.ws_col > 8) cols = ws.ws_col - 8; 14071057Salm } 140830233Seivind errno = save_errno; 14091057Salm} 14101057Salm 14111057Salm 14121057Salm/* is_legal_filename: return a legal filename */ 14131057Salmint 141490109Simpis_legal_filename(char *s) 14151057Salm{ 14161057Salm if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) { 141781220Smike errmsg = "shell access restricted"; 14181057Salm return 0; 14191057Salm } 14201057Salm return 1; 14211057Salm} 1422