main.c revision 24348
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. 273044Sdg * 2824348Simp * $Id: main.c,v 1.9 1997/02/22 14:03:17 peter Exp $ 291057Salm */ 301057Salm 311057Salm#ifndef lint 3220420Sstevestatic char * const copyright = 331057Salm"@(#) Copyright (c) 1993 Andrew Moore, Talke Studio. \n\ 341057Salm All rights reserved.\n"; 351057Salm#endif /* not lint */ 361057Salm 371057Salm#ifndef lint 3820420Sstevestatic char * const rcsid = "@(#)main.c,v 1.1 1994/02/01 00:34:42 alm Exp"; 391057Salm#endif /* not lint */ 401057Salm 411057Salm/* 421057Salm * CREDITS 431057Salm * 441057Salm * This program is based on the editor algorithm described in 458855Srgrimes * Brian W. Kernighan and P. J. Plauger's book "Software Tools 461057Salm * in Pascal," Addison-Wesley, 1981. 471057Salm * 481057Salm * The buffering algorithm is attributed to Rodney Ruddock of 491057Salm * the University of Guelph, Guelph, Ontario. 501057Salm * 511057Salm * The cbc.c encryption code is adapted from 521057Salm * the bdes program by Matt Bishop of Dartmouth College, 531057Salm * Hanover, NH. 541057Salm * 551057Salm */ 561057Salm 571057Salm#include <sys/ioctl.h> 581057Salm#include <sys/wait.h> 591057Salm#include <ctype.h> 601057Salm#include <setjmp.h> 611057Salm#include <pwd.h> 6217516Sache#include <locale.h> 631057Salm 641057Salm#include "ed.h" 651057Salm 661057Salm 671057Salm#ifdef _POSIX_SOURCE 681057Salmsigjmp_buf env; 691057Salm#else 701057Salmjmp_buf env; 711057Salm#endif 721057Salm 731057Salm/* static buffers */ 741057Salmchar stdinbuf[1]; /* stdin buffer */ 751057Salmchar *shcmd; /* shell command buffer */ 761057Salmint shcmdsz; /* shell command buffer size */ 771057Salmint shcmdi; /* shell command buffer index */ 781057Salmchar *ibuf; /* ed command-line buffer */ 791057Salmint ibufsz; /* ed command-line buffer size */ 801057Salmchar *ibufp; /* pointer to ed command-line buffer */ 811057Salm 821057Salm/* global flags */ 831057Salmint des = 0; /* if set, use crypt(3) for i/o */ 841057Salmint garrulous = 0; /* if set, print all error messages */ 851057Salmint isbinary; /* if set, buffer contains ASCII NULs */ 861057Salmint isglobal; /* if set, doing a global command */ 871057Salmint modified; /* if set, buffer modified since last write */ 881057Salmint mutex = 0; /* if set, signals set "sigflags" */ 891057Salmint red = 0; /* if set, restrict shell/directory access */ 901057Salmint scripted = 0; /* if set, suppress diagnostics */ 911057Salmint sigflags = 0; /* if set, signals received while mutex set */ 921057Salmint sigactive = 0; /* if set, signal handlers are enabled */ 931057Salm 941057Salmchar old_filename[MAXPATHLEN + 1] = ""; /* default filename */ 951057Salmlong current_addr; /* current address in editor buffer */ 961057Salmlong addr_last; /* last address in editor buffer */ 971057Salmint lineno; /* script line number */ 981057Salmchar *prompt; /* command-line prompt */ 991057Salmchar *dps = "*"; /* default command-line prompt */ 1001057Salm 1011057Salmchar *usage = "usage: %s [-] [-sx] [-p string] [name]\n"; 1021057Salm 1031057Salmextern char errmsg[]; 1041057Salmextern int optind; 1051057Salmextern char *optarg; 1061057Salm 1071057Salm/* ed: line editor */ 1081057Salmint 1091057Salmmain(argc, argv) 1101057Salm int argc; 1111057Salm char **argv; 1121057Salm{ 1131057Salm int c, n; 1141057Salm long status = 0; 11520420Ssteve#if __GNUC__ 11620420Ssteve /* Avoid longjmp clobbering */ 11720420Ssteve (void) &argc; 11820420Ssteve (void) &argv; 11920420Ssteve#endif 1201057Salm 12117516Sache (void)setlocale(LC_ALL, ""); 12217516Sache 1231057Salm red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r'; 1241057Salmtop: 12524348Simp while ((c = getopt(argc, argv, "p:sx")) != -1) 1261057Salm switch(c) { 1271057Salm case 'p': /* set prompt */ 1281057Salm prompt = optarg; 1291057Salm break; 1301057Salm case 's': /* run script */ 1311057Salm scripted = 1; 1321057Salm break; 1331057Salm case 'x': /* use crypt */ 1341057Salm#ifdef DES 1351057Salm des = get_keyword(); 1361057Salm#else 1371057Salm fprintf(stderr, "crypt unavailable\n?\n"); 1381057Salm#endif 1391057Salm break; 1401057Salm 1411057Salm default: 1421057Salm fprintf(stderr, usage, argv[0]); 1431057Salm exit(1); 1441057Salm } 1451057Salm argv += optind; 1461057Salm argc -= optind; 1471057Salm if (argc && **argv == '-') { 1481057Salm scripted = 1; 1491057Salm if (argc > 1) { 1501057Salm optind = 1; 1511057Salm goto top; 1521057Salm } 1531057Salm argv++; 1541057Salm argc--; 1551057Salm } 1561057Salm /* assert: reliable signals! */ 1571057Salm#ifdef SIGWINCH 1581057Salm handle_winch(SIGWINCH); 1591057Salm if (isatty(0)) signal(SIGWINCH, handle_winch); 1601057Salm#endif 1611057Salm signal(SIGHUP, signal_hup); 1621057Salm signal(SIGQUIT, SIG_IGN); 1631057Salm signal(SIGINT, signal_int); 1641057Salm#ifdef _POSIX_SOURCE 1657165Sjoerg if ((status = sigsetjmp(env, 1))) 1661057Salm#else 1677165Sjoerg if ((status = setjmp(env))) 1681057Salm#endif 1691057Salm { 1701057Salm fputs("\n?\n", stderr); 1711057Salm sprintf(errmsg, "interrupt"); 1721057Salm } else { 1731057Salm init_buffers(); 1741057Salm sigactive = 1; /* enable signal handlers */ 1751057Salm if (argc && **argv && is_legal_filename(*argv)) { 1761057Salm if (read_file(*argv, 0) < 0 && !isatty(0)) 1771057Salm quit(2); 1781057Salm else if (**argv != '!') 1791057Salm strcpy(old_filename, *argv); 1801057Salm } else if (argc) { 1811057Salm fputs("?\n", stderr); 1821057Salm if (**argv == '\0') 1831057Salm sprintf(errmsg, "invalid filename"); 1841057Salm if (!isatty(0)) 1851057Salm quit(2); 1861057Salm } 1871057Salm } 1881057Salm for (;;) { 1891057Salm if (status < 0 && garrulous) 1901057Salm fprintf(stderr, "%s\n", errmsg); 1911057Salm if (prompt) { 1921057Salm printf("%s", prompt); 1931057Salm fflush(stdout); 1941057Salm } 1951057Salm if ((n = get_tty_line()) < 0) { 1961057Salm status = ERR; 1971057Salm continue; 1981057Salm } else if (n == 0) { 1991057Salm if (modified && !scripted) { 2001057Salm fputs("?\n", stderr); 2011057Salm sprintf(errmsg, "warning: file modified"); 2021057Salm if (!isatty(0)) { 2038855Srgrimes fprintf(stderr, garrulous ? 2041057Salm "script, line %d: %s\n" : 2051057Salm "", lineno, errmsg); 2061057Salm quit(2); 2071057Salm } 2081057Salm clearerr(stdin); 2091057Salm modified = 0; 2101057Salm status = EMOD; 2111057Salm continue; 2121057Salm } else 2131057Salm quit(0); 2141057Salm } else if (ibuf[n - 1] != '\n') { 2151057Salm /* discard line */ 2161057Salm sprintf(errmsg, "unexpected end-of-file"); 2171057Salm clearerr(stdin); 2181057Salm status = ERR; 2191057Salm continue; 2201057Salm } 2211057Salm isglobal = 0; 2221057Salm if ((status = extract_addr_range()) >= 0 && 2231057Salm (status = exec_command()) >= 0) 2248855Srgrimes if (!status || 2251057Salm (status = display_lines(current_addr, current_addr, 2261057Salm status)) >= 0) 2271057Salm continue; 2281057Salm switch (status) { 2291057Salm case EOF: 2301057Salm quit(0); 2311057Salm case EMOD: 2321057Salm modified = 0; 2331057Salm fputs("?\n", stderr); /* give warning */ 2341057Salm sprintf(errmsg, "warning: file modified"); 2351057Salm if (!isatty(0)) { 2368855Srgrimes fprintf(stderr, garrulous ? 2378855Srgrimes "script, line %d: %s\n" : 2381057Salm "", lineno, errmsg); 2391057Salm quit(2); 2401057Salm } 2411057Salm break; 2421057Salm case FATAL: 2431057Salm if (!isatty(0)) 2448855Srgrimes fprintf(stderr, garrulous ? 2458855Srgrimes "script, line %d: %s\n" : "", 2461057Salm lineno, errmsg); 2471057Salm else 2481057Salm fprintf(stderr, garrulous ? "%s\n" : "", 2491057Salm errmsg); 2501057Salm quit(3); 2511057Salm default: 2521057Salm fputs("?\n", stderr); 2531057Salm if (!isatty(0)) { 2548855Srgrimes fprintf(stderr, garrulous ? 2551057Salm "script, line %d: %s\n" : "", 2561057Salm lineno, errmsg); 2571057Salm quit(2); 2581057Salm } 2591057Salm break; 2601057Salm } 2611057Salm } 2621057Salm /*NOTREACHED*/ 2631057Salm} 2641057Salm 2651057Salmlong first_addr, second_addr, addr_cnt; 2661057Salm 2678855Srgrimes/* extract_addr_range: get line addresses from the command buffer until an 2681057Salm illegal address is seen; return status */ 2691057Salmint 2701057Salmextract_addr_range() 2711057Salm{ 2721057Salm long addr; 2731057Salm 2741057Salm addr_cnt = 0; 2751057Salm first_addr = second_addr = current_addr; 2761057Salm while ((addr = next_addr()) >= 0) { 2771057Salm addr_cnt++; 2781057Salm first_addr = second_addr; 2791057Salm second_addr = addr; 2801057Salm if (*ibufp != ',' && *ibufp != ';') 2811057Salm break; 2821057Salm else if (*ibufp++ == ';') 2831057Salm current_addr = addr; 2841057Salm } 2851057Salm if ((addr_cnt = min(addr_cnt, 2)) == 1 || second_addr != addr) 2861057Salm first_addr = second_addr; 2871057Salm return (addr == ERR) ? ERR : 0; 2881057Salm} 2891057Salm 2901057Salm 29117516Sache#define SKIP_BLANKS() while (isspace((unsigned char)*ibufp) && *ibufp != '\n') ibufp++ 2921057Salm 2931057Salm#define MUST_BE_FIRST() \ 2941057Salm if (!first) { sprintf(errmsg, "invalid address"); return ERR; } 2951057Salm 2961057Salm/* next_addr: return the next line address in the command buffer */ 2971057Salmlong 2981057Salmnext_addr() 2991057Salm{ 3001057Salm char *hd; 3011057Salm long addr = current_addr; 3021057Salm long n; 3031057Salm int first = 1; 3041057Salm int c; 3051057Salm 3061057Salm SKIP_BLANKS(); 3071057Salm for (hd = ibufp;; first = 0) 3081057Salm switch (c = *ibufp) { 3091057Salm case '+': 3101057Salm case '\t': 3111057Salm case ' ': 3121057Salm case '-': 3131057Salm case '^': 3141057Salm ibufp++; 3151057Salm SKIP_BLANKS(); 31617516Sache if (isdigit((unsigned char)*ibufp)) { 3171057Salm STRTOL(n, ibufp); 3181057Salm addr += (c == '-' || c == '^') ? -n : n; 31917516Sache } else if (!isspace((unsigned char)c)) 3201057Salm addr += (c == '-' || c == '^') ? -1 : 1; 3211057Salm break; 3221057Salm case '0': case '1': case '2': 3231057Salm case '3': case '4': case '5': 3241057Salm case '6': case '7': case '8': case '9': 3251057Salm MUST_BE_FIRST(); 3261057Salm STRTOL(addr, ibufp); 3271057Salm break; 3281057Salm case '.': 3291057Salm case '$': 3301057Salm MUST_BE_FIRST(); 3311057Salm ibufp++; 3321057Salm addr = (c == '.') ? current_addr : addr_last; 3331057Salm break; 3341057Salm case '/': 3351057Salm case '?': 3361057Salm MUST_BE_FIRST(); 3371057Salm if ((addr = get_matching_node_addr( 3381057Salm get_compiled_pattern(), c == '/')) < 0) 3391057Salm return ERR; 3401057Salm else if (c == *ibufp) 3411057Salm ibufp++; 3421057Salm break; 3431057Salm case '\'': 3441057Salm MUST_BE_FIRST(); 3451057Salm ibufp++; 3461057Salm if ((addr = get_marked_node_addr(*ibufp++)) < 0) 3471057Salm return ERR; 3481057Salm break; 3491057Salm case '%': 3501057Salm case ',': 3511057Salm case ';': 3521057Salm if (first) { 3531057Salm ibufp++; 3541057Salm addr_cnt++; 3551057Salm second_addr = (c == ';') ? current_addr : 1; 3561057Salm addr = addr_last; 3571057Salm break; 3581057Salm } 3591057Salm /* FALL THROUGH */ 3601057Salm default: 3611057Salm if (ibufp == hd) 3621057Salm return EOF; 3631057Salm else if (addr < 0 || addr_last < addr) { 3641057Salm sprintf(errmsg, "invalid address"); 3651057Salm return ERR; 3661057Salm } else 3671057Salm return addr; 3681057Salm } 3691057Salm /* NOTREACHED */ 3701057Salm} 3711057Salm 3721057Salm 3731057Salm#ifdef BACKWARDS 3741057Salm/* GET_THIRD_ADDR: get a legal address from the command buffer */ 3751057Salm#define GET_THIRD_ADDR(addr) \ 3761057Salm{ \ 3771057Salm long ol1, ol2; \ 3781057Salm\ 3791057Salm ol1 = first_addr, ol2 = second_addr; \ 3801057Salm if (extract_addr_range() < 0) \ 3811057Salm return ERR; \ 3821057Salm else if (addr_cnt == 0) { \ 3831057Salm sprintf(errmsg, "destination expected"); \ 3841057Salm return ERR; \ 3851057Salm } else if (second_addr < 0 || addr_last < second_addr) { \ 3861057Salm sprintf(errmsg, "invalid address"); \ 3871057Salm return ERR; \ 3881057Salm } \ 3891057Salm addr = second_addr; \ 3901057Salm first_addr = ol1, second_addr = ol2; \ 3911057Salm} 3921057Salm#else /* BACKWARDS */ 3931057Salm/* GET_THIRD_ADDR: get a legal address from the command buffer */ 3941057Salm#define GET_THIRD_ADDR(addr) \ 3951057Salm{ \ 3961057Salm long ol1, ol2; \ 3971057Salm\ 3981057Salm ol1 = first_addr, ol2 = second_addr; \ 3991057Salm if (extract_addr_range() < 0) \ 4001057Salm return ERR; \ 4011057Salm if (second_addr < 0 || addr_last < second_addr) { \ 4021057Salm sprintf(errmsg, "invalid address"); \ 4031057Salm return ERR; \ 4041057Salm } \ 4051057Salm addr = second_addr; \ 4061057Salm first_addr = ol1, second_addr = ol2; \ 4071057Salm} 4081057Salm#endif 4091057Salm 4101057Salm 4111057Salm/* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */ 4121057Salm#define GET_COMMAND_SUFFIX() { \ 4131057Salm int done = 0; \ 4141057Salm do { \ 4151057Salm switch(*ibufp) { \ 4161057Salm case 'p': \ 4171057Salm gflag |= GPR, ibufp++; \ 4181057Salm break; \ 4191057Salm case 'l': \ 4201057Salm gflag |= GLS, ibufp++; \ 4211057Salm break; \ 4221057Salm case 'n': \ 4231057Salm gflag |= GNP, ibufp++; \ 4241057Salm break; \ 4251057Salm default: \ 4261057Salm done++; \ 4271057Salm } \ 4281057Salm } while (!done); \ 4291057Salm if (*ibufp++ != '\n') { \ 4301057Salm sprintf(errmsg, "invalid command suffix"); \ 4311057Salm return ERR; \ 4321057Salm } \ 4331057Salm} 4341057Salm 4351057Salm 4361057Salm/* sflags */ 4371057Salm#define SGG 001 /* complement previous global substitute suffix */ 4381057Salm#define SGP 002 /* complement previous print suffix */ 4391057Salm#define SGR 004 /* use last regex instead of last pat */ 4401057Salm#define SGF 010 /* repeat last substitution */ 4411057Salm 4421057Salmint patlock = 0; /* if set, pattern not freed by get_compiled_pattern() */ 4431057Salm 4441057Salmlong rows = 22; /* scroll length: ws_row - 2 */ 4451057Salm 4461057Salm/* exec_command: execute the next command in command buffer; return print 4471057Salm request, if any */ 4481057Salmint 4491057Salmexec_command() 4501057Salm{ 4511057Salm extern long u_current_addr; 4521057Salm extern long u_addr_last; 4531057Salm 4541057Salm static pattern_t *pat = NULL; 4551057Salm static int sgflag = 0; 4561057Salm static int sgnum = 0; 4571057Salm 4581057Salm pattern_t *tpat; 4591057Salm char *fnp; 4601057Salm int gflag = 0; 4611057Salm int sflags = 0; 4621057Salm long addr = 0; 4631057Salm int n = 0; 4641057Salm int c; 4651057Salm 4661057Salm SKIP_BLANKS(); 4671057Salm switch(c = *ibufp++) { 4681057Salm case 'a': 4691057Salm GET_COMMAND_SUFFIX(); 4701057Salm if (!isglobal) clear_undo_stack(); 4711057Salm if (append_lines(second_addr) < 0) 4721057Salm return ERR; 4731057Salm break; 4741057Salm case 'c': 4751057Salm if (check_addr_range(current_addr, current_addr) < 0) 4761057Salm return ERR; 4771057Salm GET_COMMAND_SUFFIX(); 4781057Salm if (!isglobal) clear_undo_stack(); 4791057Salm if (delete_lines(first_addr, second_addr) < 0 || 4801057Salm append_lines(current_addr) < 0) 4811057Salm return ERR; 4821057Salm break; 4831057Salm case 'd': 4841057Salm if (check_addr_range(current_addr, current_addr) < 0) 4851057Salm return ERR; 4861057Salm GET_COMMAND_SUFFIX(); 4871057Salm if (!isglobal) clear_undo_stack(); 4881057Salm if (delete_lines(first_addr, second_addr) < 0) 4891057Salm return ERR; 4901057Salm else if ((addr = INC_MOD(current_addr, addr_last)) != 0) 4911057Salm current_addr = addr; 4921057Salm break; 4931057Salm case 'e': 4941057Salm if (modified && !scripted) 4951057Salm return EMOD; 4961057Salm /* fall through */ 4971057Salm case 'E': 4981057Salm if (addr_cnt > 0) { 4991057Salm sprintf(errmsg, "unexpected address"); 5001057Salm return ERR; 50117516Sache } else if (!isspace((unsigned char)*ibufp)) { 5021057Salm sprintf(errmsg, "unexpected command suffix"); 5031057Salm return ERR; 5041057Salm } else if ((fnp = get_filename()) == NULL) 5051057Salm return ERR; 5061057Salm GET_COMMAND_SUFFIX(); 5071057Salm if (delete_lines(1, addr_last) < 0) 5081057Salm return ERR; 5091057Salm clear_undo_stack(); 5101057Salm if (close_sbuf() < 0) 5111057Salm return ERR; 5121057Salm else if (open_sbuf() < 0) 5131057Salm return FATAL; 5141057Salm if (*fnp && *fnp != '!') strcpy(old_filename, fnp); 5151057Salm#ifdef BACKWARDS 5161057Salm if (*fnp == '\0' && *old_filename == '\0') { 5171057Salm sprintf(errmsg, "no current filename"); 5181057Salm return ERR; 5191057Salm } 5201057Salm#endif 5211057Salm if (read_file(*fnp ? fnp : old_filename, 0) < 0) 5221057Salm return ERR; 5231057Salm clear_undo_stack(); 5241057Salm modified = 0; 5251057Salm u_current_addr = u_addr_last = -1; 5261057Salm break; 5271057Salm case 'f': 5281057Salm if (addr_cnt > 0) { 5291057Salm sprintf(errmsg, "unexpected address"); 5301057Salm return ERR; 53117516Sache } else if (!isspace((unsigned char)*ibufp)) { 5321057Salm sprintf(errmsg, "unexpected command suffix"); 5331057Salm return ERR; 5341057Salm } else if ((fnp = get_filename()) == NULL) 5351057Salm return ERR; 5361057Salm else if (*fnp == '!') { 5371057Salm sprintf(errmsg, "invalid redirection"); 5381057Salm return ERR; 5391057Salm } 5401057Salm GET_COMMAND_SUFFIX(); 5411057Salm if (*fnp) strcpy(old_filename, fnp); 5421057Salm printf("%s\n", strip_escapes(old_filename)); 5431057Salm break; 5441057Salm case 'g': 5451057Salm case 'v': 5461057Salm case 'G': 5471057Salm case 'V': 5481057Salm if (isglobal) { 5491057Salm sprintf(errmsg, "cannot nest global commands"); 5501057Salm return ERR; 5511057Salm } else if (check_addr_range(1, addr_last) < 0) 5521057Salm return ERR; 5531057Salm else if (build_active_list(c == 'g' || c == 'G') < 0) 5541057Salm return ERR; 5557165Sjoerg else if ((n = (c == 'G' || c == 'V'))) 5561057Salm GET_COMMAND_SUFFIX(); 5571057Salm isglobal++; 5581057Salm if (exec_global(n, gflag) < 0) 5598855Srgrimes return ERR; 5601057Salm break; 5611057Salm case 'h': 5621057Salm if (addr_cnt > 0) { 5631057Salm sprintf(errmsg, "unexpected address"); 5641057Salm return ERR; 5651057Salm } 5661057Salm GET_COMMAND_SUFFIX(); 5671057Salm if (*errmsg) fprintf(stderr, "%s\n", errmsg); 5681057Salm break; 5691057Salm case 'H': 5701057Salm if (addr_cnt > 0) { 5711057Salm sprintf(errmsg, "unexpected address"); 5721057Salm return ERR; 5731057Salm } 5741057Salm GET_COMMAND_SUFFIX(); 5751057Salm if ((garrulous = 1 - garrulous) && *errmsg) 5761057Salm fprintf(stderr, "%s\n", errmsg); 5771057Salm break; 5781057Salm case 'i': 5791057Salm if (second_addr == 0) { 5801057Salm sprintf(errmsg, "invalid address"); 5811057Salm return ERR; 5821057Salm } 5831057Salm GET_COMMAND_SUFFIX(); 5841057Salm if (!isglobal) clear_undo_stack(); 5851057Salm if (append_lines(second_addr - 1) < 0) 5861057Salm return ERR; 5871057Salm break; 5881057Salm case 'j': 5891057Salm if (check_addr_range(current_addr, current_addr + 1) < 0) 5901057Salm return ERR; 5911057Salm GET_COMMAND_SUFFIX(); 5921057Salm if (!isglobal) clear_undo_stack(); 5931057Salm if (first_addr != second_addr && 5941057Salm join_lines(first_addr, second_addr) < 0) 5951057Salm return ERR; 5961057Salm break; 5971057Salm case 'k': 5981057Salm c = *ibufp++; 5991057Salm if (second_addr == 0) { 6001057Salm sprintf(errmsg, "invalid address"); 6011057Salm return ERR; 6021057Salm } 6031057Salm GET_COMMAND_SUFFIX(); 6041057Salm if (mark_line_node(get_addressed_line_node(second_addr), c) < 0) 6051057Salm return ERR; 6061057Salm break; 6071057Salm case 'l': 6081057Salm if (check_addr_range(current_addr, current_addr) < 0) 6091057Salm return ERR; 6101057Salm GET_COMMAND_SUFFIX(); 6111057Salm if (display_lines(first_addr, second_addr, gflag | GLS) < 0) 6121057Salm return ERR; 6131057Salm gflag = 0; 6141057Salm break; 6151057Salm case 'm': 6161057Salm if (check_addr_range(current_addr, current_addr) < 0) 6171057Salm return ERR; 6181057Salm GET_THIRD_ADDR(addr); 6191057Salm if (first_addr <= addr && addr < second_addr) { 6201057Salm sprintf(errmsg, "invalid destination"); 6211057Salm return ERR; 6221057Salm } 6231057Salm GET_COMMAND_SUFFIX(); 6241057Salm if (!isglobal) clear_undo_stack(); 6251057Salm if (move_lines(addr) < 0) 6261057Salm return ERR; 6271057Salm break; 6281057Salm case 'n': 6291057Salm if (check_addr_range(current_addr, current_addr) < 0) 6301057Salm return ERR; 6311057Salm GET_COMMAND_SUFFIX(); 6321057Salm if (display_lines(first_addr, second_addr, gflag | GNP) < 0) 6331057Salm return ERR; 6341057Salm gflag = 0; 6351057Salm break; 6361057Salm case 'p': 6371057Salm if (check_addr_range(current_addr, current_addr) < 0) 6381057Salm return ERR; 6391057Salm GET_COMMAND_SUFFIX(); 6401057Salm if (display_lines(first_addr, second_addr, gflag | GPR) < 0) 6411057Salm return ERR; 6421057Salm gflag = 0; 6431057Salm break; 6441057Salm case 'P': 6451057Salm if (addr_cnt > 0) { 6461057Salm sprintf(errmsg, "unexpected address"); 6471057Salm return ERR; 6481057Salm } 6491057Salm GET_COMMAND_SUFFIX(); 6501057Salm prompt = prompt ? NULL : optarg ? optarg : dps; 6511057Salm break; 6521057Salm case 'q': 6531057Salm case 'Q': 6541057Salm if (addr_cnt > 0) { 6551057Salm sprintf(errmsg, "unexpected address"); 6561057Salm return ERR; 6571057Salm } 6581057Salm GET_COMMAND_SUFFIX(); 6591057Salm gflag = (modified && !scripted && c == 'q') ? EMOD : EOF; 6601057Salm break; 6611057Salm case 'r': 66217516Sache if (!isspace((unsigned char)*ibufp)) { 6631057Salm sprintf(errmsg, "unexpected command suffix"); 6641057Salm return ERR; 6651057Salm } else if (addr_cnt == 0) 6661057Salm second_addr = addr_last; 6671057Salm if ((fnp = get_filename()) == NULL) 6681057Salm return ERR; 6691057Salm GET_COMMAND_SUFFIX(); 6701057Salm if (!isglobal) clear_undo_stack(); 6711057Salm if (*old_filename == '\0' && *fnp != '!') 6721057Salm strcpy(old_filename, fnp); 6731057Salm#ifdef BACKWARDS 6741057Salm if (*fnp == '\0' && *old_filename == '\0') { 6751057Salm sprintf(errmsg, "no current filename"); 6761057Salm return ERR; 6771057Salm } 6781057Salm#endif 6791057Salm if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0) 6801057Salm return ERR; 6811057Salm else if (addr && addr != addr_last) 6821057Salm modified = 1; 6831057Salm break; 6841057Salm case 's': 6851057Salm do { 6861057Salm switch(*ibufp) { 6871057Salm case '\n': 6881057Salm sflags |=SGF; 6891057Salm break; 6901057Salm case 'g': 6911057Salm sflags |= SGG; 6921057Salm ibufp++; 6931057Salm break; 6941057Salm case 'p': 6951057Salm sflags |= SGP; 6961057Salm ibufp++; 6971057Salm break; 6981057Salm case 'r': 6991057Salm sflags |= SGR; 7001057Salm ibufp++; 7011057Salm break; 7028855Srgrimes case '0': case '1': case '2': case '3': case '4': 7031057Salm case '5': case '6': case '7': case '8': case '9': 7041057Salm STRTOL(sgnum, ibufp); 7051057Salm sflags |= SGF; 7061057Salm sgflag &= ~GSG; /* override GSG */ 7071057Salm break; 7081057Salm default: 7091057Salm if (sflags) { 7101057Salm sprintf(errmsg, "invalid command suffix"); 7111057Salm return ERR; 7121057Salm } 7131057Salm } 7141057Salm } while (sflags && *ibufp != '\n'); 7151057Salm if (sflags && !pat) { 7161057Salm sprintf(errmsg, "no previous substitution"); 7171057Salm return ERR; 7181057Salm } else if (sflags & SGG) 7191057Salm sgnum = 0; /* override numeric arg */ 7201057Salm if (*ibufp != '\n' && *(ibufp + 1) == '\n') { 7211057Salm sprintf(errmsg, "invalid pattern delimiter"); 7221057Salm return ERR; 7231057Salm } 7241057Salm tpat = pat; 7251057Salm SPL1(); 7261057Salm if ((!sflags || (sflags & SGR)) && 7271057Salm (tpat = get_compiled_pattern()) == NULL) { 7281057Salm SPL0(); 7291057Salm return ERR; 7301057Salm } else if (tpat != pat) { 7311057Salm if (pat) { 7321057Salm regfree(pat); 7331057Salm free(pat); 7341057Salm } 7351057Salm pat = tpat; 7361057Salm patlock = 1; /* reserve pattern */ 7371057Salm } 7381057Salm SPL0(); 7391057Salm if (!sflags && extract_subst_tail(&sgflag, &sgnum) < 0) 7401057Salm return ERR; 7411057Salm else if (isglobal) 7421057Salm sgflag |= GLB; 7431057Salm else 7441057Salm sgflag &= ~GLB; 7451057Salm if (sflags & SGG) 7461057Salm sgflag ^= GSG; 7471057Salm if (sflags & SGP) 7481057Salm sgflag ^= GPR, sgflag &= ~(GLS | GNP); 7491057Salm do { 7501057Salm switch(*ibufp) { 7511057Salm case 'p': 7521057Salm sgflag |= GPR, ibufp++; 7531057Salm break; 7541057Salm case 'l': 7551057Salm sgflag |= GLS, ibufp++; 7561057Salm break; 7571057Salm case 'n': 7581057Salm sgflag |= GNP, ibufp++; 7591057Salm break; 7601057Salm default: 7611057Salm n++; 7621057Salm } 7631057Salm } while (!n); 7641057Salm if (check_addr_range(current_addr, current_addr) < 0) 7651057Salm return ERR; 7661057Salm GET_COMMAND_SUFFIX(); 7671057Salm if (!isglobal) clear_undo_stack(); 7681057Salm if (search_and_replace(pat, sgflag, sgnum) < 0) 7691057Salm return ERR; 7701057Salm break; 7711057Salm case 't': 7721057Salm if (check_addr_range(current_addr, current_addr) < 0) 7731057Salm return ERR; 7741057Salm GET_THIRD_ADDR(addr); 7751057Salm GET_COMMAND_SUFFIX(); 7761057Salm if (!isglobal) clear_undo_stack(); 7771057Salm if (copy_lines(addr) < 0) 7781057Salm return ERR; 7791057Salm break; 7801057Salm case 'u': 7811057Salm if (addr_cnt > 0) { 7821057Salm sprintf(errmsg, "unexpected address"); 7831057Salm return ERR; 7841057Salm } 7851057Salm GET_COMMAND_SUFFIX(); 7861057Salm if (pop_undo_stack() < 0) 7871057Salm return ERR; 7881057Salm break; 7891057Salm case 'w': 7901057Salm case 'W': 7911057Salm if ((n = *ibufp) == 'q' || n == 'Q') { 7921057Salm gflag = EOF; 7931057Salm ibufp++; 7941057Salm } 79517516Sache if (!isspace((unsigned char)*ibufp)) { 7961057Salm sprintf(errmsg, "unexpected command suffix"); 7971057Salm return ERR; 7981057Salm } else if ((fnp = get_filename()) == NULL) 7991057Salm return ERR; 8001057Salm if (addr_cnt == 0 && !addr_last) 8011057Salm first_addr = second_addr = 0; 8021057Salm else if (check_addr_range(1, addr_last) < 0) 8031057Salm return ERR; 8041057Salm GET_COMMAND_SUFFIX(); 8051057Salm if (*old_filename == '\0' && *fnp != '!') 8061057Salm strcpy(old_filename, fnp); 8071057Salm#ifdef BACKWARDS 8081057Salm if (*fnp == '\0' && *old_filename == '\0') { 8091057Salm sprintf(errmsg, "no current filename"); 8101057Salm return ERR; 8111057Salm } 8121057Salm#endif 8138855Srgrimes if ((addr = write_file(*fnp ? fnp : old_filename, 8141057Salm (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0) 8151057Salm return ERR; 8161057Salm else if (addr == addr_last) 8171057Salm modified = 0; 8181057Salm else if (modified && !scripted && n == 'q') 8191057Salm gflag = EMOD; 8201057Salm break; 8211057Salm case 'x': 8221057Salm if (addr_cnt > 0) { 8231057Salm sprintf(errmsg, "unexpected address"); 8241057Salm return ERR; 8251057Salm } 8261057Salm GET_COMMAND_SUFFIX(); 8271057Salm#ifdef DES 8281057Salm des = get_keyword(); 8291057Salm#else 8301057Salm sprintf(errmsg, "crypt unavailable"); 8311057Salm return ERR; 8321057Salm#endif 8331057Salm break; 8341057Salm case 'z': 8351057Salm#ifdef BACKWARDS 8361057Salm if (check_addr_range(first_addr = 1, current_addr + 1) < 0) 8371057Salm#else 8381057Salm if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0) 8391057Salm#endif 8401057Salm return ERR; 8411057Salm else if ('0' < *ibufp && *ibufp <= '9') 8421057Salm STRTOL(rows, ibufp); 8431057Salm GET_COMMAND_SUFFIX(); 8441057Salm if (display_lines(second_addr, min(addr_last, 8451057Salm second_addr + rows), gflag) < 0) 8461057Salm return ERR; 8471057Salm gflag = 0; 8481057Salm break; 8491057Salm case '=': 8501057Salm GET_COMMAND_SUFFIX(); 8517165Sjoerg printf("%ld\n", addr_cnt ? second_addr : addr_last); 8521057Salm break; 8531057Salm case '!': 8541057Salm if (addr_cnt > 0) { 8551057Salm sprintf(errmsg, "unexpected address"); 8561057Salm return ERR; 8571057Salm } else if ((sflags = get_shell_command()) < 0) 8581057Salm return ERR; 8591057Salm GET_COMMAND_SUFFIX(); 8601057Salm if (sflags) printf("%s\n", shcmd + 1); 8611057Salm system(shcmd + 1); 8621057Salm if (!scripted) printf("!\n"); 8631057Salm break; 8641057Salm case '\n': 8651057Salm#ifdef BACKWARDS 8661057Salm if (check_addr_range(first_addr = 1, current_addr + 1) < 0 8671057Salm#else 8681057Salm if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0 8691057Salm#endif 8701057Salm || display_lines(second_addr, second_addr, 0) < 0) 8711057Salm return ERR; 8721057Salm break; 8731057Salm default: 8741057Salm sprintf(errmsg, "unknown command"); 8751057Salm return ERR; 8761057Salm } 8771057Salm return gflag; 8781057Salm} 8791057Salm 8801057Salm 8811057Salm/* check_addr_range: return status of address range check */ 8821057Salmint 8831057Salmcheck_addr_range(n, m) 8841057Salm long n, m; 8851057Salm{ 8861057Salm if (addr_cnt == 0) { 8871057Salm first_addr = n; 8881057Salm second_addr = m; 8891057Salm } 8901057Salm if (first_addr > second_addr || 1 > first_addr || 8911057Salm second_addr > addr_last) { 8921057Salm sprintf(errmsg, "invalid address"); 8931057Salm return ERR; 8941057Salm } 8951057Salm return 0; 8961057Salm} 8971057Salm 8981057Salm 8998855Srgrimes/* get_matching_node_addr: return the address of the next line matching a 9001057Salm pattern in a given direction. wrap around begin/end of editor buffer if 9011057Salm necessary */ 9021057Salmlong 9031057Salmget_matching_node_addr(pat, dir) 9041057Salm pattern_t *pat; 9051057Salm int dir; 9061057Salm{ 9071057Salm char *s; 9081057Salm long n = current_addr; 9091057Salm line_t *lp; 9101057Salm 9111057Salm if (!pat) return ERR; 9121057Salm do { 9137165Sjoerg if ((n = dir ? INC_MOD(n, addr_last) : DEC_MOD(n, addr_last))) { 9141057Salm lp = get_addressed_line_node(n); 9151057Salm if ((s = get_sbuf_line(lp)) == NULL) 9161057Salm return ERR; 9171057Salm if (isbinary) 9181057Salm NUL_TO_NEWLINE(s, lp->len); 9191057Salm if (!regexec(pat, s, 0, NULL, 0)) 9201057Salm return n; 9217165Sjoerg } 9221057Salm } while (n != current_addr); 9231057Salm sprintf(errmsg, "no match"); 9241057Salm return ERR; 9251057Salm} 9261057Salm 9271057Salm 9281057Salm/* get_filename: return pointer to copy of filename in the command buffer */ 9291057Salmchar * 9301057Salmget_filename() 9311057Salm{ 9321057Salm static char *file = NULL; 9331057Salm static int filesz = 0; 9341057Salm 9351057Salm int n; 9361057Salm 9371057Salm if (*ibufp != '\n') { 9381057Salm SKIP_BLANKS(); 9391057Salm if (*ibufp == '\n') { 9401057Salm sprintf(errmsg, "invalid filename"); 9411057Salm return NULL; 9421057Salm } else if ((ibufp = get_extended_line(&n, 1)) == NULL) 9431057Salm return NULL; 9441057Salm else if (*ibufp == '!') { 9451057Salm ibufp++; 9461057Salm if ((n = get_shell_command()) < 0) 9471057Salm return NULL; 9481057Salm if (n) printf("%s\n", shcmd + 1); 9491057Salm return shcmd; 9501057Salm } else if (n - 1 > MAXPATHLEN) { 9511057Salm sprintf(errmsg, "filename too long"); 9521057Salm return NULL; 9531057Salm } 9541057Salm } 9551057Salm#ifndef BACKWARDS 9561057Salm else if (*old_filename == '\0') { 9571057Salm sprintf(errmsg, "no current filename"); 9581057Salm return NULL; 9591057Salm } 9601057Salm#endif 9611057Salm REALLOC(file, filesz, MAXPATHLEN + 1, NULL); 9621057Salm for (n = 0; *ibufp != '\n';) 9631057Salm file[n++] = *ibufp++; 9641057Salm file[n] = '\0'; 9651057Salm return is_legal_filename(file) ? file : NULL; 9661057Salm} 9671057Salm 9681057Salm 9691057Salm/* get_shell_command: read a shell command from stdin; return substitution 9701057Salm status */ 9711057Salmint 9721057Salmget_shell_command() 9731057Salm{ 9741057Salm static char *buf = NULL; 9751057Salm static int n = 0; 9761057Salm 9771057Salm char *s; /* substitution char pointer */ 9781057Salm int i = 0; 9791057Salm int j = 0; 9801057Salm 9811057Salm if (red) { 9821057Salm sprintf(errmsg, "shell access restricted"); 9831057Salm return ERR; 9841057Salm } else if ((s = ibufp = get_extended_line(&j, 1)) == NULL) 9851057Salm return ERR; 9861057Salm REALLOC(buf, n, j + 1, ERR); 9871057Salm buf[i++] = '!'; /* prefix command w/ bang */ 9881057Salm while (*ibufp != '\n') 9891057Salm switch (*ibufp) { 9901057Salm default: 9911057Salm REALLOC(buf, n, i + 2, ERR); 9921057Salm buf[i++] = *ibufp; 9931057Salm if (*ibufp++ == '\\') 9941057Salm buf[i++] = *ibufp++; 9951057Salm break; 9961057Salm case '!': 9971057Salm if (s != ibufp) { 9981057Salm REALLOC(buf, n, i + 1, ERR); 9991057Salm buf[i++] = *ibufp++; 10001057Salm } 10011057Salm#ifdef BACKWARDS 10021057Salm else if (shcmd == NULL || *(shcmd + 1) == '\0') 10031057Salm#else 10041057Salm else if (shcmd == NULL) 10051057Salm#endif 10061057Salm { 10071057Salm sprintf(errmsg, "no previous command"); 10081057Salm return ERR; 10091057Salm } else { 10101057Salm REALLOC(buf, n, i + shcmdi, ERR); 10111057Salm for (s = shcmd + 1; s < shcmd + shcmdi;) 10121057Salm buf[i++] = *s++; 10131057Salm s = ibufp++; 10141057Salm } 10151057Salm break; 10161057Salm case '%': 10171057Salm if (*old_filename == '\0') { 10181057Salm sprintf(errmsg, "no current filename"); 10191057Salm return ERR; 10201057Salm } 10211057Salm j = strlen(s = strip_escapes(old_filename)); 10221057Salm REALLOC(buf, n, i + j, ERR); 10231057Salm while (j--) 10241057Salm buf[i++] = *s++; 10251057Salm s = ibufp++; 10261057Salm break; 10271057Salm } 10281057Salm REALLOC(shcmd, shcmdsz, i + 1, ERR); 10291057Salm memcpy(shcmd, buf, i); 10301057Salm shcmd[shcmdi = i] = '\0'; 10311057Salm return *s == '!' || *s == '%'; 10321057Salm} 10331057Salm 10341057Salm 10351057Salm/* append_lines: insert text from stdin to after line n; stop when either a 10361057Salm single period is read or EOF; return status */ 10371057Salmint 10381057Salmappend_lines(n) 10391057Salm long n; 10401057Salm{ 10411057Salm int l; 10421057Salm char *lp = ibuf; 10431057Salm char *eot; 10441057Salm undo_t *up = NULL; 10451057Salm 10461057Salm for (current_addr = n;;) { 10471057Salm if (!isglobal) { 10481057Salm if ((l = get_tty_line()) < 0) 10491057Salm return ERR; 10501057Salm else if (l == 0 || ibuf[l - 1] != '\n') { 10511057Salm clearerr(stdin); 10521057Salm return l ? EOF : 0; 10531057Salm } 10541057Salm lp = ibuf; 10551057Salm } else if (*(lp = ibufp) == '\0') 10561057Salm return 0; 10571057Salm else { 10581057Salm while (*ibufp++ != '\n') 10591057Salm ; 10601057Salm l = ibufp - lp; 10611057Salm } 10621057Salm if (l == 2 && lp[0] == '.' && lp[1] == '\n') { 10631057Salm return 0; 10641057Salm } 10651057Salm eot = lp + l; 10661057Salm SPL1(); 10671057Salm do { 10681057Salm if ((lp = put_sbuf_line(lp)) == NULL) { 10691057Salm SPL0(); 10701057Salm return ERR; 10711057Salm } else if (up) 10721057Salm up->t = get_addressed_line_node(current_addr); 10731057Salm else if ((up = push_undo_stack(UADD, current_addr, 10741057Salm current_addr)) == NULL) { 10751057Salm SPL0(); 10761057Salm return ERR; 10771057Salm } 10781057Salm } while (lp != eot); 10791057Salm modified = 1; 10801057Salm SPL0(); 10811057Salm } 10821057Salm /* NOTREACHED */ 10831057Salm} 10841057Salm 10851057Salm 10861057Salm/* join_lines: replace a range of lines with the joined text of those lines */ 10871057Salmint 10881057Salmjoin_lines(from, to) 10891057Salm long from; 10901057Salm long to; 10911057Salm{ 10921057Salm static char *buf = NULL; 10931057Salm static int n; 10941057Salm 10951057Salm char *s; 10961057Salm int size = 0; 10971057Salm line_t *bp, *ep; 10981057Salm 10991057Salm ep = get_addressed_line_node(INC_MOD(to, addr_last)); 11001057Salm bp = get_addressed_line_node(from); 11011057Salm for (; bp != ep; bp = bp->q_forw) { 11021057Salm if ((s = get_sbuf_line(bp)) == NULL) 11031057Salm return ERR; 11041057Salm REALLOC(buf, n, size + bp->len, ERR); 11051057Salm memcpy(buf + size, s, bp->len); 11061057Salm size += bp->len; 11071057Salm } 11081057Salm REALLOC(buf, n, size + 2, ERR); 11091057Salm memcpy(buf + size, "\n", 2); 11101057Salm if (delete_lines(from, to) < 0) 11111057Salm return ERR; 11121057Salm current_addr = from - 1; 11131057Salm SPL1(); 11141057Salm if (put_sbuf_line(buf) == NULL || 11151057Salm push_undo_stack(UADD, current_addr, current_addr) == NULL) { 11161057Salm SPL0(); 11171057Salm return ERR; 11181057Salm } 11191057Salm modified = 1; 11201057Salm SPL0(); 11211057Salm return 0; 11221057Salm} 11231057Salm 11241057Salm 11251057Salm/* move_lines: move a range of lines */ 11261057Salmint 11271057Salmmove_lines(addr) 11281057Salm long addr; 11291057Salm{ 11301057Salm line_t *b1, *a1, *b2, *a2; 11311057Salm long n = INC_MOD(second_addr, addr_last); 11321057Salm long p = first_addr - 1; 11331057Salm int done = (addr == first_addr - 1 || addr == second_addr); 11341057Salm 11351057Salm SPL1(); 11361057Salm if (done) { 11371057Salm a2 = get_addressed_line_node(n); 11381057Salm b2 = get_addressed_line_node(p); 11391057Salm current_addr = second_addr; 11401057Salm } else if (push_undo_stack(UMOV, p, n) == NULL || 11411057Salm push_undo_stack(UMOV, addr, INC_MOD(addr, addr_last)) == NULL) { 11421057Salm SPL0(); 11431057Salm return ERR; 11441057Salm } else { 11451057Salm a1 = get_addressed_line_node(n); 11461057Salm if (addr < first_addr) { 11471057Salm b1 = get_addressed_line_node(p); 11481057Salm b2 = get_addressed_line_node(addr); 11491057Salm /* this get_addressed_line_node last! */ 11501057Salm } else { 11511057Salm b2 = get_addressed_line_node(addr); 11521057Salm b1 = get_addressed_line_node(p); 11531057Salm /* this get_addressed_line_node last! */ 11541057Salm } 11551057Salm a2 = b2->q_forw; 11561057Salm REQUE(b2, b1->q_forw); 11571057Salm REQUE(a1->q_back, a2); 11581057Salm REQUE(b1, a1); 11598855Srgrimes current_addr = addr + ((addr < first_addr) ? 11601057Salm second_addr - first_addr + 1 : 0); 11611057Salm } 11621057Salm if (isglobal) 11631057Salm unset_active_nodes(b2->q_forw, a2); 11641057Salm modified = 1; 11651057Salm SPL0(); 11661057Salm return 0; 11671057Salm} 11681057Salm 11691057Salm 11701057Salm/* copy_lines: copy a range of lines; return status */ 11711057Salmint 11721057Salmcopy_lines(addr) 11731057Salm long addr; 11741057Salm{ 11751057Salm line_t *lp, *np = get_addressed_line_node(first_addr); 11761057Salm undo_t *up = NULL; 11771057Salm long n = second_addr - first_addr + 1; 11781057Salm long m = 0; 11791057Salm 11801057Salm current_addr = addr; 11811057Salm if (first_addr <= addr && addr < second_addr) { 11821057Salm n = addr - first_addr + 1; 11831057Salm m = second_addr - addr; 11841057Salm } 11851057Salm for (; n > 0; n=m, m=0, np = get_addressed_line_node(current_addr + 1)) 11861057Salm for (; n-- > 0; np = np->q_forw) { 11871057Salm SPL1(); 11881057Salm if ((lp = dup_line_node(np)) == NULL) { 11891057Salm SPL0(); 11901057Salm return ERR; 11911057Salm } 11921057Salm add_line_node(lp); 11931057Salm if (up) 11941057Salm up->t = lp; 11951057Salm else if ((up = push_undo_stack(UADD, current_addr, 11961057Salm current_addr)) == NULL) { 11971057Salm SPL0(); 11981057Salm return ERR; 11991057Salm } 12001057Salm modified = 1; 12011057Salm SPL0(); 12021057Salm } 12031057Salm return 0; 12041057Salm} 12051057Salm 12061057Salm 12071057Salm/* delete_lines: delete a range of lines */ 12081057Salmint 12091057Salmdelete_lines(from, to) 12101057Salm long from, to; 12111057Salm{ 12121057Salm line_t *n, *p; 12131057Salm 12141057Salm SPL1(); 12151057Salm if (push_undo_stack(UDEL, from, to) == NULL) { 12161057Salm SPL0(); 12171057Salm return ERR; 12181057Salm } 12191057Salm n = get_addressed_line_node(INC_MOD(to, addr_last)); 12201057Salm p = get_addressed_line_node(from - 1); 12211057Salm /* this get_addressed_line_node last! */ 12221057Salm if (isglobal) 12231057Salm unset_active_nodes(p->q_forw, n); 12241057Salm REQUE(p, n); 12251057Salm addr_last -= to - from + 1; 12261057Salm current_addr = from - 1; 12271057Salm modified = 1; 12281057Salm SPL0(); 12291057Salm return 0; 12301057Salm} 12311057Salm 12321057Salm 12331057Salm/* display_lines: print a range of lines to stdout */ 12341057Salmint 12351057Salmdisplay_lines(from, to, gflag) 12361057Salm long from; 12371057Salm long to; 12381057Salm int gflag; 12391057Salm{ 12401057Salm line_t *bp; 12411057Salm line_t *ep; 12421057Salm char *s; 12431057Salm 12441057Salm if (!from) { 12451057Salm sprintf(errmsg, "invalid address"); 12461057Salm return ERR; 12471057Salm } 12481057Salm ep = get_addressed_line_node(INC_MOD(to, addr_last)); 12491057Salm bp = get_addressed_line_node(from); 12501057Salm for (; bp != ep; bp = bp->q_forw) { 12511057Salm if ((s = get_sbuf_line(bp)) == NULL) 12521057Salm return ERR; 12531057Salm if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0) 12541057Salm return ERR; 12551057Salm } 12561057Salm return 0; 12571057Salm} 12581057Salm 12591057Salm 12601057Salm#define MAXMARK 26 /* max number of marks */ 12611057Salm 12621057Salmline_t *mark[MAXMARK]; /* line markers */ 12631057Salmint markno; /* line marker count */ 12641057Salm 12651057Salm/* mark_line_node: set a line node mark */ 12661057Salmint 12671057Salmmark_line_node(lp, n) 12681057Salm line_t *lp; 12691057Salm int n; 12701057Salm{ 127117516Sache if (!islower((unsigned char)n)) { 12721057Salm sprintf(errmsg, "invalid mark character"); 12731057Salm return ERR; 12741057Salm } else if (mark[n - 'a'] == NULL) 12751057Salm markno++; 12761057Salm mark[n - 'a'] = lp; 12771057Salm return 0; 12781057Salm} 12791057Salm 12801057Salm 12811057Salm/* get_marked_node_addr: return address of a marked line */ 12821057Salmlong 12831057Salmget_marked_node_addr(n) 12841057Salm int n; 12851057Salm{ 128617516Sache if (!islower((unsigned char)n)) { 12871057Salm sprintf(errmsg, "invalid mark character"); 12881057Salm return ERR; 12891057Salm } 12901057Salm return get_line_node_addr(mark[n - 'a']); 12911057Salm} 12921057Salm 12931057Salm 12941057Salm/* unmark_line_node: clear line node mark */ 12951057Salmvoid 12961057Salmunmark_line_node(lp) 12971057Salm line_t *lp; 12981057Salm{ 12991057Salm int i; 13001057Salm 13011057Salm for (i = 0; markno && i < MAXMARK; i++) 13021057Salm if (mark[i] == lp) { 13031057Salm mark[i] = NULL; 13041057Salm markno--; 13051057Salm } 13061057Salm} 13071057Salm 13081057Salm 13091057Salm/* dup_line_node: return a pointer to a copy of a line node */ 13101057Salmline_t * 13111057Salmdup_line_node(lp) 13121057Salm line_t *lp; 13131057Salm{ 13141057Salm line_t *np; 13151057Salm 13161057Salm if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) { 13171057Salm fprintf(stderr, "%s\n", strerror(errno)); 13181057Salm sprintf(errmsg, "out of memory"); 13191057Salm return NULL; 13201057Salm } 13211057Salm np->seek = lp->seek; 13221057Salm np->len = lp->len; 13231057Salm return np; 13241057Salm} 13251057Salm 13261057Salm 13271057Salm/* has_trailing_escape: return the parity of escapes preceding a character 13281057Salm in a string */ 13291057Salmint 13301057Salmhas_trailing_escape(s, t) 13311057Salm char *s; 13321057Salm char *t; 13331057Salm{ 13341057Salm return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1); 13351057Salm} 13361057Salm 13371057Salm 13381057Salm/* strip_escapes: return copy of escaped string of at most length MAXPATHLEN */ 13391057Salmchar * 13401057Salmstrip_escapes(s) 13411057Salm char *s; 13421057Salm{ 13431057Salm static char *file = NULL; 13441057Salm static int filesz = 0; 13451057Salm 13461057Salm int i = 0; 13471057Salm 13481057Salm REALLOC(file, filesz, MAXPATHLEN + 1, NULL); 13491057Salm /* assert: no trailing escape */ 13507165Sjoerg while ((file[i++] = (*s == '\\') ? *++s : *s)) 13511057Salm s++; 13521057Salm return file; 13531057Salm} 13541057Salm 13551057Salm 13561057Salmvoid 13571057Salmsignal_hup(signo) 13581057Salm int signo; 13591057Salm{ 13601057Salm if (mutex) 13611057Salm sigflags |= (1 << (signo - 1)); 13621057Salm else handle_hup(signo); 13631057Salm} 13641057Salm 13651057Salm 13661057Salmvoid 13671057Salmsignal_int(signo) 13681057Salm int signo; 13691057Salm{ 13701057Salm if (mutex) 13711057Salm sigflags |= (1 << (signo - 1)); 13721057Salm else handle_int(signo); 13731057Salm} 13741057Salm 13751057Salm 13761057Salmvoid 13771057Salmhandle_hup(signo) 13781057Salm int signo; 13791057Salm{ 13801057Salm char *hup = NULL; /* hup filename */ 13811057Salm char *s; 13821057Salm int n; 13831057Salm 13841057Salm if (!sigactive) 13851057Salm quit(1); 13861057Salm sigflags &= ~(1 << (signo - 1)); 13871057Salm if (addr_last && write_file("ed.hup", "w", 1, addr_last) < 0 && 13881057Salm (s = getenv("HOME")) != NULL && 13891057Salm (n = strlen(s)) + 8 <= MAXPATHLEN && /* "ed.hup" + '/' */ 13901057Salm (hup = (char *) malloc(n + 10)) != NULL) { 13911057Salm strcpy(hup, s); 13921057Salm if (hup[n - 1] != '/') 13931057Salm hup[n] = '/', hup[n+1] = '\0'; 13941057Salm strcat(hup, "ed.hup"); 13951057Salm write_file(hup, "w", 1, addr_last); 13961057Salm } 13971057Salm quit(2); 13981057Salm} 13991057Salm 14001057Salm 14011057Salmvoid 14021057Salmhandle_int(signo) 14031057Salm int signo; 14041057Salm{ 14051057Salm if (!sigactive) 14061057Salm quit(1); 14071057Salm sigflags &= ~(1 << (signo - 1)); 14081057Salm#ifdef _POSIX_SOURCE 14091057Salm siglongjmp(env, -1); 14101057Salm#else 14111057Salm longjmp(env, -1); 14121057Salm#endif 14131057Salm} 14141057Salm 14151057Salm 14161057Salmint cols = 72; /* wrap column */ 14171057Salm 14181057Salmvoid 14191057Salmhandle_winch(signo) 14201057Salm int signo; 14211057Salm{ 14221057Salm struct winsize ws; /* window size structure */ 14231057Salm 14241057Salm sigflags &= ~(1 << (signo - 1)); 14251057Salm if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) { 14261057Salm if (ws.ws_row > 2) rows = ws.ws_row - 2; 14271057Salm if (ws.ws_col > 8) cols = ws.ws_col - 8; 14281057Salm } 14291057Salm} 14301057Salm 14311057Salm 14321057Salm/* is_legal_filename: return a legal filename */ 14331057Salmint 14341057Salmis_legal_filename(s) 14351057Salm char *s; 14361057Salm{ 14371057Salm if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) { 14381057Salm sprintf(errmsg, "shell access restricted"); 14391057Salm return 0; 14401057Salm } 14411057Salm return 1; 14421057Salm} 1443