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