1 1.31 rillig /* $NetBSD: main.c,v 1.31 2025/09/17 20:35:11 rillig Exp $ */ 2 1.3 cgd 3 1.1 alm /* main.c: This file contains the main control and user-interface routines 4 1.1 alm for the ed line editor. */ 5 1.1 alm /*- 6 1.1 alm * Copyright (c) 1993 Andrew Moore, Talke Studio. 7 1.1 alm * All rights reserved. 8 1.1 alm * 9 1.1 alm * Redistribution and use in source and binary forms, with or without 10 1.1 alm * modification, are permitted provided that the following conditions 11 1.1 alm * are met: 12 1.1 alm * 1. Redistributions of source code must retain the above copyright 13 1.1 alm * notice, this list of conditions and the following disclaimer. 14 1.1 alm * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 alm * notice, this list of conditions and the following disclaimer in the 16 1.1 alm * documentation and/or other materials provided with the distribution. 17 1.1 alm * 18 1.1 alm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 1.1 alm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 1.1 alm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 1.1 alm * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 1.1 alm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 1.1 alm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 1.1 alm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 1.1 alm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 1.1 alm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 1.1 alm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 1.1 alm * SUCH DAMAGE. 29 1.1 alm */ 30 1.1 alm 31 1.4 thorpej #include <sys/cdefs.h> 32 1.1 alm #ifndef lint 33 1.4 thorpej __COPYRIGHT( 34 1.20 lukem "@(#) Copyright (c) 1993 Andrew Moore, Talke Studio.\ 35 1.20 lukem All rights reserved."); 36 1.1 alm #endif /* not lint */ 37 1.1 alm 38 1.1 alm #ifndef lint 39 1.3 cgd #if 0 40 1.1 alm static char *rcsid = "@(#)main.c,v 1.1 1994/02/01 00:34:42 alm Exp"; 41 1.3 cgd #else 42 1.31 rillig __RCSID("$NetBSD: main.c,v 1.31 2025/09/17 20:35:11 rillig Exp $"); 43 1.3 cgd #endif 44 1.1 alm #endif /* not lint */ 45 1.1 alm 46 1.1 alm /* 47 1.1 alm * CREDITS 48 1.1 alm * 49 1.1 alm * This program is based on the editor algorithm described in 50 1.31 rillig * Brian W. Kernighan and P. J. Plauger's book "Software Tools 51 1.1 alm * in Pascal," Addison-Wesley, 1981. 52 1.1 alm * 53 1.1 alm * The buffering algorithm is attributed to Rodney Ruddock of 54 1.1 alm * the University of Guelph, Guelph, Ontario. 55 1.1 alm * 56 1.1 alm * The cbc.c encryption code is adapted from 57 1.1 alm * the bdes program by Matt Bishop of Dartmouth College, 58 1.1 alm * Hanover, NH. 59 1.1 alm * 60 1.1 alm */ 61 1.1 alm 62 1.1 alm #include <sys/ioctl.h> 63 1.1 alm #include <sys/wait.h> 64 1.10 christos #include <termios.h> 65 1.1 alm #include <ctype.h> 66 1.1 alm #include <setjmp.h> 67 1.1 alm #include <pwd.h> 68 1.1 alm 69 1.1 alm #include "ed.h" 70 1.1 alm 71 1.1 alm 72 1.1 alm #ifdef _POSIX_SOURCE 73 1.1 alm sigjmp_buf env; 74 1.1 alm #else 75 1.1 alm jmp_buf env; 76 1.1 alm #endif 77 1.1 alm 78 1.1 alm /* static buffers */ 79 1.1 alm char stdinbuf[1]; /* stdin buffer */ 80 1.1 alm char *shcmd; /* shell command buffer */ 81 1.1 alm int shcmdsz; /* shell command buffer size */ 82 1.1 alm int shcmdi; /* shell command buffer index */ 83 1.1 alm char *ibuf; /* ed command-line buffer */ 84 1.1 alm int ibufsz; /* ed command-line buffer size */ 85 1.1 alm char *ibufp; /* pointer to ed command-line buffer */ 86 1.1 alm 87 1.1 alm /* global flags */ 88 1.1 alm int des = 0; /* if set, use crypt(3) for i/o */ 89 1.1 alm int garrulous = 0; /* if set, print all error messages */ 90 1.1 alm int isbinary; /* if set, buffer contains ASCII NULs */ 91 1.1 alm int isglobal; /* if set, doing a global command */ 92 1.1 alm int modified; /* if set, buffer modified since last write */ 93 1.1 alm int mutex = 0; /* if set, signals set "sigflags" */ 94 1.1 alm int red = 0; /* if set, restrict shell/directory access */ 95 1.13 atatat int ere = 0; /* if set, use extended regexes */ 96 1.1 alm int scripted = 0; /* if set, suppress diagnostics */ 97 1.29 christos int secure = 0; /* is set, ! is not allowed */ 98 1.1 alm int sigflags = 0; /* if set, signals received while mutex set */ 99 1.1 alm int sigactive = 0; /* if set, signal handlers are enabled */ 100 1.1 alm 101 1.1 alm char old_filename[MAXPATHLEN + 1] = ""; /* default filename */ 102 1.1 alm long current_addr; /* current address in editor buffer */ 103 1.1 alm long addr_last; /* last address in editor buffer */ 104 1.1 alm int lineno; /* script line number */ 105 1.17 christos const char *prompt; /* command-line prompt */ 106 1.17 christos const char *dps = "*"; /* default command-line prompt */ 107 1.1 alm 108 1.1 alm 109 1.29 christos static const char usage[] = "Usage: %s [-] [-ESsx] [-p string] [name]\n"; 110 1.25 christos 111 1.1 alm /* ed: line editor */ 112 1.1 alm int 113 1.19 christos main(int ac, char *av[]) 114 1.1 alm { 115 1.1 alm int c, n; 116 1.1 alm long status = 0; 117 1.19 christos volatile int argc = ac; 118 1.19 christos char ** volatile argv = av; 119 1.1 alm 120 1.1 alm red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r'; 121 1.1 alm top: 122 1.29 christos while ((c = getopt(argc, argv, "p:sxES")) != -1) 123 1.1 alm switch(c) { 124 1.1 alm case 'p': /* set prompt */ 125 1.1 alm prompt = optarg; 126 1.1 alm break; 127 1.1 alm case 's': /* run script */ 128 1.1 alm scripted = 1; 129 1.1 alm break; 130 1.1 alm case 'x': /* use crypt */ 131 1.1 alm #ifdef DES 132 1.1 alm des = get_keyword(); 133 1.1 alm #else 134 1.1 alm fprintf(stderr, "crypt unavailable\n?\n"); 135 1.1 alm #endif 136 1.1 alm break; 137 1.1 alm 138 1.13 atatat case 'E': 139 1.13 atatat ere = REG_EXTENDED; 140 1.13 atatat break; 141 1.29 christos case 'S': /* ! is not allowed */ 142 1.29 christos secure = 1; 143 1.29 christos break; 144 1.1 alm default: 145 1.25 christos fprintf(stderr, usage, getprogname()); 146 1.1 alm exit(1); 147 1.8 mycroft /* NOTREACHED */ 148 1.1 alm } 149 1.1 alm argv += optind; 150 1.1 alm argc -= optind; 151 1.1 alm if (argc && **argv == '-') { 152 1.1 alm scripted = 1; 153 1.1 alm if (argc > 1) { 154 1.1 alm optind = 1; 155 1.1 alm goto top; 156 1.1 alm } 157 1.1 alm argv++; 158 1.1 alm argc--; 159 1.1 alm } 160 1.1 alm /* assert: reliable signals! */ 161 1.1 alm #ifdef SIGWINCH 162 1.1 alm handle_winch(SIGWINCH); 163 1.1 alm if (isatty(0)) signal(SIGWINCH, handle_winch); 164 1.1 alm #endif 165 1.1 alm signal(SIGHUP, signal_hup); 166 1.1 alm signal(SIGQUIT, SIG_IGN); 167 1.1 alm signal(SIGINT, signal_int); 168 1.1 alm #ifdef _POSIX_SOURCE 169 1.4 thorpej if ((status = sigsetjmp(env, 1)) != 0) 170 1.1 alm #else 171 1.4 thorpej if ((status = setjmp(env)) != 0) 172 1.1 alm #endif 173 1.1 alm { 174 1.1 alm fputs("\n?\n", stderr); 175 1.26 dholland seterrmsg("interrupt"); 176 1.1 alm } else { 177 1.1 alm init_buffers(); 178 1.1 alm sigactive = 1; /* enable signal handlers */ 179 1.1 alm if (argc && **argv && is_legal_filename(*argv)) { 180 1.1 alm if (read_file(*argv, 0) < 0 && !isatty(0)) 181 1.1 alm quit(2); 182 1.1 alm else if (**argv != '!') 183 1.18 christos strlcpy(old_filename, *argv, 184 1.18 christos sizeof(old_filename) - 2); 185 1.1 alm } else if (argc) { 186 1.1 alm fputs("?\n", stderr); 187 1.1 alm if (**argv == '\0') 188 1.26 dholland seterrmsg("invalid filename"); 189 1.1 alm if (!isatty(0)) 190 1.1 alm quit(2); 191 1.1 alm } 192 1.1 alm } 193 1.1 alm for (;;) { 194 1.1 alm if (status < 0 && garrulous) 195 1.1 alm fprintf(stderr, "%s\n", errmsg); 196 1.1 alm if (prompt) { 197 1.1 alm printf("%s", prompt); 198 1.1 alm fflush(stdout); 199 1.1 alm } 200 1.1 alm if ((n = get_tty_line()) < 0) { 201 1.1 alm status = ERR; 202 1.1 alm continue; 203 1.1 alm } else if (n == 0) { 204 1.1 alm if (modified && !scripted) { 205 1.1 alm fputs("?\n", stderr); 206 1.26 dholland seterrmsg("warning: file modified"); 207 1.1 alm if (!isatty(0)) { 208 1.23 joerg if (garrulous) { 209 1.23 joerg fprintf(stderr, 210 1.23 joerg "script, line %d: %s\n", 211 1.23 joerg lineno, errmsg); 212 1.23 joerg } 213 1.1 alm quit(2); 214 1.1 alm } 215 1.1 alm clearerr(stdin); 216 1.1 alm modified = 0; 217 1.1 alm status = EMOD; 218 1.1 alm continue; 219 1.1 alm } else 220 1.1 alm quit(0); 221 1.1 alm } else if (ibuf[n - 1] != '\n') { 222 1.1 alm /* discard line */ 223 1.26 dholland seterrmsg("unexpected end-of-file"); 224 1.1 alm clearerr(stdin); 225 1.1 alm status = ERR; 226 1.1 alm continue; 227 1.1 alm } 228 1.1 alm isglobal = 0; 229 1.1 alm if ((status = extract_addr_range()) >= 0 && 230 1.28 christos (status = exec_command()) >= 0) { 231 1.28 christos if (status == 0) 232 1.1 alm continue; 233 1.28 christos status = display_lines(current_addr, current_addr, 234 1.28 christos status); 235 1.28 christos if (status >= 0) 236 1.28 christos continue; 237 1.28 christos } 238 1.1 alm switch (status) { 239 1.1 alm case EOF: 240 1.1 alm quit(0); 241 1.1 alm case EMOD: 242 1.1 alm modified = 0; 243 1.1 alm fputs("?\n", stderr); /* give warning */ 244 1.26 dholland seterrmsg("warning: file modified"); 245 1.1 alm if (!isatty(0)) { 246 1.23 joerg if (garrulous) { 247 1.23 joerg fprintf(stderr, 248 1.23 joerg "script, line %d: %s\n", 249 1.23 joerg lineno, errmsg); 250 1.23 joerg } 251 1.1 alm quit(2); 252 1.1 alm } 253 1.1 alm break; 254 1.1 alm case FATAL: 255 1.23 joerg if (garrulous) { 256 1.23 joerg if (!isatty(0)) { 257 1.23 joerg fprintf(stderr, 258 1.23 joerg "script, line %d: %s\n", 259 1.23 joerg lineno, errmsg); 260 1.23 joerg } else { 261 1.23 joerg fprintf(stderr, "%s\n", errmsg); 262 1.23 joerg } 263 1.23 joerg } 264 1.1 alm quit(3); 265 1.1 alm default: 266 1.1 alm fputs("?\n", stderr); 267 1.1 alm if (!isatty(0)) { 268 1.23 joerg if (garrulous) { 269 1.23 joerg fprintf(stderr, "script, line %d: %s\n", 270 1.23 joerg lineno, errmsg); 271 1.23 joerg } 272 1.1 alm quit(2); 273 1.1 alm } 274 1.1 alm break; 275 1.1 alm } 276 1.1 alm } 277 1.9 mycroft /* NOTREACHED */ 278 1.1 alm } 279 1.1 alm 280 1.1 alm long first_addr, second_addr, addr_cnt; 281 1.1 alm 282 1.31 rillig /* extract_addr_range: get line addresses from the command buffer until an 283 1.1 alm illegal address is seen; return status */ 284 1.1 alm int 285 1.14 xtraeme extract_addr_range(void) 286 1.1 alm { 287 1.1 alm long addr; 288 1.1 alm 289 1.1 alm addr_cnt = 0; 290 1.1 alm first_addr = second_addr = current_addr; 291 1.1 alm while ((addr = next_addr()) >= 0) { 292 1.1 alm addr_cnt++; 293 1.1 alm first_addr = second_addr; 294 1.1 alm second_addr = addr; 295 1.1 alm if (*ibufp != ',' && *ibufp != ';') 296 1.1 alm break; 297 1.1 alm else if (*ibufp++ == ';') 298 1.1 alm current_addr = addr; 299 1.1 alm } 300 1.1 alm if ((addr_cnt = min(addr_cnt, 2)) == 1 || second_addr != addr) 301 1.1 alm first_addr = second_addr; 302 1.1 alm return (addr == ERR) ? ERR : 0; 303 1.1 alm } 304 1.1 alm 305 1.1 alm 306 1.10 christos #define SKIP_BLANKS() while (isspace((unsigned char)*ibufp) && *ibufp != '\n') \ 307 1.10 christos ibufp++ 308 1.1 alm 309 1.1 alm #define MUST_BE_FIRST() \ 310 1.26 dholland if (!first) { seterrmsg("invalid address"); return ERR; } 311 1.1 alm 312 1.1 alm /* next_addr: return the next line address in the command buffer */ 313 1.1 alm long 314 1.14 xtraeme next_addr(void) 315 1.1 alm { 316 1.1 alm char *hd; 317 1.1 alm long addr = current_addr; 318 1.1 alm long n; 319 1.1 alm int first = 1; 320 1.1 alm int c; 321 1.1 alm 322 1.1 alm SKIP_BLANKS(); 323 1.1 alm for (hd = ibufp;; first = 0) 324 1.1 alm switch (c = *ibufp) { 325 1.1 alm case '+': 326 1.1 alm case '\t': 327 1.1 alm case ' ': 328 1.1 alm case '-': 329 1.1 alm case '^': 330 1.1 alm ibufp++; 331 1.1 alm SKIP_BLANKS(); 332 1.10 christos if (isdigit((unsigned char)*ibufp)) { 333 1.1 alm STRTOL(n, ibufp); 334 1.1 alm addr += (c == '-' || c == '^') ? -n : n; 335 1.16 rillig } else if (!isspace((unsigned char)c)) 336 1.1 alm addr += (c == '-' || c == '^') ? -1 : 1; 337 1.1 alm break; 338 1.1 alm case '0': case '1': case '2': 339 1.1 alm case '3': case '4': case '5': 340 1.1 alm case '6': case '7': case '8': case '9': 341 1.1 alm MUST_BE_FIRST(); 342 1.1 alm STRTOL(addr, ibufp); 343 1.1 alm break; 344 1.1 alm case '.': 345 1.1 alm case '$': 346 1.1 alm MUST_BE_FIRST(); 347 1.1 alm ibufp++; 348 1.1 alm addr = (c == '.') ? current_addr : addr_last; 349 1.1 alm break; 350 1.1 alm case '/': 351 1.1 alm case '?': 352 1.1 alm MUST_BE_FIRST(); 353 1.1 alm if ((addr = get_matching_node_addr( 354 1.1 alm get_compiled_pattern(), c == '/')) < 0) 355 1.1 alm return ERR; 356 1.1 alm else if (c == *ibufp) 357 1.1 alm ibufp++; 358 1.1 alm break; 359 1.1 alm case '\'': 360 1.1 alm MUST_BE_FIRST(); 361 1.1 alm ibufp++; 362 1.15 rillig if ((addr = get_marked_node_addr((unsigned char)*ibufp++)) < 0) 363 1.1 alm return ERR; 364 1.1 alm break; 365 1.1 alm case '%': 366 1.1 alm case ',': 367 1.1 alm case ';': 368 1.1 alm if (first) { 369 1.1 alm ibufp++; 370 1.1 alm addr_cnt++; 371 1.1 alm second_addr = (c == ';') ? current_addr : 1; 372 1.1 alm addr = addr_last; 373 1.1 alm break; 374 1.1 alm } 375 1.1 alm /* FALL THROUGH */ 376 1.1 alm default: 377 1.1 alm if (ibufp == hd) 378 1.1 alm return EOF; 379 1.1 alm else if (addr < 0 || addr_last < addr) { 380 1.26 dholland seterrmsg("invalid address"); 381 1.1 alm return ERR; 382 1.1 alm } else 383 1.1 alm return addr; 384 1.1 alm } 385 1.1 alm /* NOTREACHED */ 386 1.1 alm } 387 1.1 alm 388 1.1 alm 389 1.1 alm #ifdef BACKWARDS 390 1.1 alm /* GET_THIRD_ADDR: get a legal address from the command buffer */ 391 1.1 alm #define GET_THIRD_ADDR(addr) \ 392 1.1 alm { \ 393 1.1 alm long ol1, ol2; \ 394 1.1 alm \ 395 1.1 alm ol1 = first_addr, ol2 = second_addr; \ 396 1.1 alm if (extract_addr_range() < 0) \ 397 1.1 alm return ERR; \ 398 1.1 alm else if (addr_cnt == 0) { \ 399 1.26 dholland seterrmsg("destination expected"); \ 400 1.1 alm return ERR; \ 401 1.1 alm } else if (second_addr < 0 || addr_last < second_addr) { \ 402 1.26 dholland seterrmsg("invalid address"); \ 403 1.1 alm return ERR; \ 404 1.1 alm } \ 405 1.1 alm addr = second_addr; \ 406 1.1 alm first_addr = ol1, second_addr = ol2; \ 407 1.1 alm } 408 1.1 alm #else /* BACKWARDS */ 409 1.1 alm /* GET_THIRD_ADDR: get a legal address from the command buffer */ 410 1.1 alm #define GET_THIRD_ADDR(addr) \ 411 1.1 alm { \ 412 1.1 alm long ol1, ol2; \ 413 1.1 alm \ 414 1.1 alm ol1 = first_addr, ol2 = second_addr; \ 415 1.1 alm if (extract_addr_range() < 0) \ 416 1.1 alm return ERR; \ 417 1.1 alm if (second_addr < 0 || addr_last < second_addr) { \ 418 1.26 dholland seterrmsg("invalid address"); \ 419 1.1 alm return ERR; \ 420 1.1 alm } \ 421 1.1 alm addr = second_addr; \ 422 1.1 alm first_addr = ol1, second_addr = ol2; \ 423 1.1 alm } 424 1.1 alm #endif 425 1.1 alm 426 1.1 alm 427 1.1 alm /* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */ 428 1.1 alm #define GET_COMMAND_SUFFIX() { \ 429 1.1 alm int done = 0; \ 430 1.1 alm do { \ 431 1.1 alm switch(*ibufp) { \ 432 1.1 alm case 'p': \ 433 1.1 alm gflag |= GPR, ibufp++; \ 434 1.1 alm break; \ 435 1.1 alm case 'l': \ 436 1.1 alm gflag |= GLS, ibufp++; \ 437 1.1 alm break; \ 438 1.1 alm case 'n': \ 439 1.1 alm gflag |= GNP, ibufp++; \ 440 1.1 alm break; \ 441 1.1 alm default: \ 442 1.1 alm done++; \ 443 1.1 alm } \ 444 1.1 alm } while (!done); \ 445 1.1 alm if (*ibufp++ != '\n') { \ 446 1.26 dholland seterrmsg("invalid command suffix"); \ 447 1.1 alm return ERR; \ 448 1.1 alm } \ 449 1.1 alm } 450 1.1 alm 451 1.1 alm 452 1.1 alm /* sflags */ 453 1.1 alm #define SGG 001 /* complement previous global substitute suffix */ 454 1.1 alm #define SGP 002 /* complement previous print suffix */ 455 1.1 alm #define SGR 004 /* use last regex instead of last pat */ 456 1.1 alm #define SGF 010 /* repeat last substitution */ 457 1.1 alm 458 1.1 alm int patlock = 0; /* if set, pattern not freed by get_compiled_pattern() */ 459 1.1 alm 460 1.12 thorpej long rows = 22; /* scroll length: ws_row - 2 */ 461 1.1 alm 462 1.1 alm /* exec_command: execute the next command in command buffer; return print 463 1.1 alm request, if any */ 464 1.1 alm int 465 1.14 xtraeme exec_command(void) 466 1.1 alm { 467 1.1 alm static pattern_t *pat = NULL; 468 1.1 alm static int sgflag = 0; 469 1.2 cgd static long sgnum = 0; 470 1.1 alm 471 1.1 alm pattern_t *tpat; 472 1.1 alm char *fnp; 473 1.1 alm int gflag = 0; 474 1.1 alm int sflags = 0; 475 1.1 alm long addr = 0; 476 1.1 alm int n = 0; 477 1.1 alm int c; 478 1.1 alm 479 1.1 alm SKIP_BLANKS(); 480 1.1 alm switch(c = *ibufp++) { 481 1.1 alm case 'a': 482 1.1 alm GET_COMMAND_SUFFIX(); 483 1.1 alm if (!isglobal) clear_undo_stack(); 484 1.1 alm if (append_lines(second_addr) < 0) 485 1.1 alm return ERR; 486 1.1 alm break; 487 1.1 alm case 'c': 488 1.1 alm if (check_addr_range(current_addr, current_addr) < 0) 489 1.1 alm return ERR; 490 1.1 alm GET_COMMAND_SUFFIX(); 491 1.1 alm if (!isglobal) clear_undo_stack(); 492 1.1 alm if (delete_lines(first_addr, second_addr) < 0 || 493 1.1 alm append_lines(current_addr) < 0) 494 1.1 alm return ERR; 495 1.1 alm break; 496 1.1 alm case 'd': 497 1.1 alm if (check_addr_range(current_addr, current_addr) < 0) 498 1.1 alm return ERR; 499 1.1 alm GET_COMMAND_SUFFIX(); 500 1.1 alm if (!isglobal) clear_undo_stack(); 501 1.1 alm if (delete_lines(first_addr, second_addr) < 0) 502 1.1 alm return ERR; 503 1.1 alm else if ((addr = INC_MOD(current_addr, addr_last)) != 0) 504 1.1 alm current_addr = addr; 505 1.1 alm break; 506 1.1 alm case 'e': 507 1.1 alm if (modified && !scripted) 508 1.1 alm return EMOD; 509 1.1 alm /* fall through */ 510 1.1 alm case 'E': 511 1.1 alm if (addr_cnt > 0) { 512 1.26 dholland seterrmsg("unexpected address"); 513 1.1 alm return ERR; 514 1.10 christos } else if (!isspace((unsigned char)*ibufp)) { 515 1.26 dholland seterrmsg("unexpected command suffix"); 516 1.1 alm return ERR; 517 1.1 alm } else if ((fnp = get_filename()) == NULL) 518 1.1 alm return ERR; 519 1.1 alm GET_COMMAND_SUFFIX(); 520 1.1 alm if (delete_lines(1, addr_last) < 0) 521 1.1 alm return ERR; 522 1.1 alm clear_undo_stack(); 523 1.1 alm if (close_sbuf() < 0) 524 1.1 alm return ERR; 525 1.1 alm else if (open_sbuf() < 0) 526 1.1 alm return FATAL; 527 1.18 christos if (*fnp && *fnp != '!') strlcpy(old_filename, fnp, 528 1.18 christos sizeof(old_filename) - 2); 529 1.1 alm #ifdef BACKWARDS 530 1.1 alm if (*fnp == '\0' && *old_filename == '\0') { 531 1.26 dholland seterrmsg("no current filename"); 532 1.1 alm return ERR; 533 1.1 alm } 534 1.1 alm #endif 535 1.1 alm if (read_file(*fnp ? fnp : old_filename, 0) < 0) 536 1.1 alm return ERR; 537 1.1 alm clear_undo_stack(); 538 1.1 alm modified = 0; 539 1.1 alm u_current_addr = u_addr_last = -1; 540 1.1 alm break; 541 1.1 alm case 'f': 542 1.1 alm if (addr_cnt > 0) { 543 1.26 dholland seterrmsg("unexpected address"); 544 1.1 alm return ERR; 545 1.10 christos } else if (!isspace((unsigned char)*ibufp)) { 546 1.26 dholland seterrmsg("unexpected command suffix"); 547 1.1 alm return ERR; 548 1.1 alm } else if ((fnp = get_filename()) == NULL) 549 1.1 alm return ERR; 550 1.1 alm else if (*fnp == '!') { 551 1.26 dholland seterrmsg("invalid redirection"); 552 1.1 alm return ERR; 553 1.1 alm } 554 1.1 alm GET_COMMAND_SUFFIX(); 555 1.18 christos if (*fnp) strlcpy(old_filename, fnp, sizeof(old_filename) - 2); 556 1.1 alm printf("%s\n", strip_escapes(old_filename)); 557 1.1 alm break; 558 1.1 alm case 'g': 559 1.1 alm case 'v': 560 1.1 alm case 'G': 561 1.1 alm case 'V': 562 1.1 alm if (isglobal) { 563 1.26 dholland seterrmsg("cannot nest global commands"); 564 1.1 alm return ERR; 565 1.1 alm } else if (check_addr_range(1, addr_last) < 0) 566 1.1 alm return ERR; 567 1.1 alm else if (build_active_list(c == 'g' || c == 'G') < 0) 568 1.1 alm return ERR; 569 1.4 thorpej else if ((n = (c == 'G' || c == 'V')) != 0) 570 1.1 alm GET_COMMAND_SUFFIX(); 571 1.1 alm isglobal++; 572 1.1 alm if (exec_global(n, gflag) < 0) 573 1.31 rillig return ERR; 574 1.1 alm break; 575 1.1 alm case 'h': 576 1.1 alm if (addr_cnt > 0) { 577 1.26 dholland seterrmsg("unexpected address"); 578 1.1 alm return ERR; 579 1.1 alm } 580 1.1 alm GET_COMMAND_SUFFIX(); 581 1.1 alm if (*errmsg) fprintf(stderr, "%s\n", errmsg); 582 1.1 alm break; 583 1.1 alm case 'H': 584 1.1 alm if (addr_cnt > 0) { 585 1.26 dholland seterrmsg("unexpected address"); 586 1.1 alm return ERR; 587 1.1 alm } 588 1.1 alm GET_COMMAND_SUFFIX(); 589 1.1 alm if ((garrulous = 1 - garrulous) && *errmsg) 590 1.1 alm fprintf(stderr, "%s\n", errmsg); 591 1.1 alm break; 592 1.1 alm case 'i': 593 1.1 alm if (second_addr == 0) { 594 1.26 dholland seterrmsg("invalid address"); 595 1.1 alm return ERR; 596 1.1 alm } 597 1.1 alm GET_COMMAND_SUFFIX(); 598 1.1 alm if (!isglobal) clear_undo_stack(); 599 1.1 alm if (append_lines(second_addr - 1) < 0) 600 1.1 alm return ERR; 601 1.1 alm break; 602 1.1 alm case 'j': 603 1.1 alm if (check_addr_range(current_addr, current_addr + 1) < 0) 604 1.1 alm return ERR; 605 1.1 alm GET_COMMAND_SUFFIX(); 606 1.1 alm if (!isglobal) clear_undo_stack(); 607 1.1 alm if (first_addr != second_addr && 608 1.1 alm join_lines(first_addr, second_addr) < 0) 609 1.1 alm return ERR; 610 1.1 alm break; 611 1.1 alm case 'k': 612 1.1 alm c = *ibufp++; 613 1.1 alm if (second_addr == 0) { 614 1.26 dholland seterrmsg("invalid address"); 615 1.1 alm return ERR; 616 1.1 alm } 617 1.1 alm GET_COMMAND_SUFFIX(); 618 1.15 rillig if (mark_line_node(get_addressed_line_node(second_addr), (unsigned char)c) < 0) 619 1.1 alm return ERR; 620 1.1 alm break; 621 1.1 alm case 'l': 622 1.1 alm if (check_addr_range(current_addr, current_addr) < 0) 623 1.1 alm return ERR; 624 1.1 alm GET_COMMAND_SUFFIX(); 625 1.1 alm if (display_lines(first_addr, second_addr, gflag | GLS) < 0) 626 1.1 alm return ERR; 627 1.1 alm gflag = 0; 628 1.1 alm break; 629 1.1 alm case 'm': 630 1.1 alm if (check_addr_range(current_addr, current_addr) < 0) 631 1.1 alm return ERR; 632 1.1 alm GET_THIRD_ADDR(addr); 633 1.1 alm if (first_addr <= addr && addr < second_addr) { 634 1.26 dholland seterrmsg("invalid destination"); 635 1.1 alm return ERR; 636 1.1 alm } 637 1.1 alm GET_COMMAND_SUFFIX(); 638 1.1 alm if (!isglobal) clear_undo_stack(); 639 1.1 alm if (move_lines(addr) < 0) 640 1.1 alm return ERR; 641 1.1 alm break; 642 1.1 alm case 'n': 643 1.1 alm if (check_addr_range(current_addr, current_addr) < 0) 644 1.1 alm return ERR; 645 1.1 alm GET_COMMAND_SUFFIX(); 646 1.1 alm if (display_lines(first_addr, second_addr, gflag | GNP) < 0) 647 1.1 alm return ERR; 648 1.1 alm gflag = 0; 649 1.1 alm break; 650 1.1 alm case 'p': 651 1.1 alm if (check_addr_range(current_addr, current_addr) < 0) 652 1.1 alm return ERR; 653 1.1 alm GET_COMMAND_SUFFIX(); 654 1.1 alm if (display_lines(first_addr, second_addr, gflag | GPR) < 0) 655 1.1 alm return ERR; 656 1.1 alm gflag = 0; 657 1.1 alm break; 658 1.1 alm case 'P': 659 1.1 alm if (addr_cnt > 0) { 660 1.26 dholland seterrmsg("unexpected address"); 661 1.1 alm return ERR; 662 1.1 alm } 663 1.1 alm GET_COMMAND_SUFFIX(); 664 1.1 alm prompt = prompt ? NULL : optarg ? optarg : dps; 665 1.1 alm break; 666 1.1 alm case 'q': 667 1.1 alm case 'Q': 668 1.1 alm if (addr_cnt > 0) { 669 1.26 dholland seterrmsg("unexpected address"); 670 1.1 alm return ERR; 671 1.1 alm } 672 1.1 alm GET_COMMAND_SUFFIX(); 673 1.1 alm gflag = (modified && !scripted && c == 'q') ? EMOD : EOF; 674 1.1 alm break; 675 1.1 alm case 'r': 676 1.10 christos if (!isspace((unsigned char)*ibufp)) { 677 1.26 dholland seterrmsg("unexpected command suffix"); 678 1.1 alm return ERR; 679 1.1 alm } else if (addr_cnt == 0) 680 1.1 alm second_addr = addr_last; 681 1.1 alm if ((fnp = get_filename()) == NULL) 682 1.1 alm return ERR; 683 1.1 alm GET_COMMAND_SUFFIX(); 684 1.1 alm if (!isglobal) clear_undo_stack(); 685 1.1 alm if (*old_filename == '\0' && *fnp != '!') 686 1.18 christos strlcpy(old_filename, fnp, sizeof(old_filename) - 2); 687 1.1 alm #ifdef BACKWARDS 688 1.1 alm if (*fnp == '\0' && *old_filename == '\0') { 689 1.26 dholland seterrmsg("no current filename"); 690 1.1 alm return ERR; 691 1.1 alm } 692 1.1 alm #endif 693 1.1 alm if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0) 694 1.1 alm return ERR; 695 1.1 alm else if (addr && addr != addr_last) 696 1.1 alm modified = 1; 697 1.1 alm break; 698 1.1 alm case 's': 699 1.1 alm do { 700 1.1 alm switch(*ibufp) { 701 1.1 alm case '\n': 702 1.1 alm sflags |=SGF; 703 1.1 alm break; 704 1.1 alm case 'g': 705 1.1 alm sflags |= SGG; 706 1.1 alm ibufp++; 707 1.1 alm break; 708 1.1 alm case 'p': 709 1.1 alm sflags |= SGP; 710 1.1 alm ibufp++; 711 1.1 alm break; 712 1.1 alm case 'r': 713 1.1 alm sflags |= SGR; 714 1.1 alm ibufp++; 715 1.1 alm break; 716 1.31 rillig case '0': case '1': case '2': case '3': case '4': 717 1.1 alm case '5': case '6': case '7': case '8': case '9': 718 1.1 alm STRTOL(sgnum, ibufp); 719 1.1 alm sflags |= SGF; 720 1.1 alm sgflag &= ~GSG; /* override GSG */ 721 1.1 alm break; 722 1.1 alm default: 723 1.1 alm if (sflags) { 724 1.26 dholland seterrmsg("invalid command suffix"); 725 1.1 alm return ERR; 726 1.1 alm } 727 1.1 alm } 728 1.1 alm } while (sflags && *ibufp != '\n'); 729 1.1 alm if (sflags && !pat) { 730 1.26 dholland seterrmsg("no previous substitution"); 731 1.1 alm return ERR; 732 1.1 alm } else if (sflags & SGG) 733 1.1 alm sgnum = 0; /* override numeric arg */ 734 1.1 alm if (*ibufp != '\n' && *(ibufp + 1) == '\n') { 735 1.26 dholland seterrmsg("invalid pattern delimiter"); 736 1.1 alm return ERR; 737 1.1 alm } 738 1.1 alm tpat = pat; 739 1.1 alm SPL1(); 740 1.1 alm if ((!sflags || (sflags & SGR)) && 741 1.1 alm (tpat = get_compiled_pattern()) == NULL) { 742 1.31 rillig SPL0(); 743 1.1 alm return ERR; 744 1.1 alm } else if (tpat != pat) { 745 1.1 alm if (pat) { 746 1.1 alm regfree(pat); 747 1.1 alm free(pat); 748 1.1 alm } 749 1.1 alm pat = tpat; 750 1.1 alm patlock = 1; /* reserve pattern */ 751 1.1 alm } 752 1.1 alm SPL0(); 753 1.1 alm if (!sflags && extract_subst_tail(&sgflag, &sgnum) < 0) 754 1.1 alm return ERR; 755 1.1 alm else if (isglobal) 756 1.1 alm sgflag |= GLB; 757 1.1 alm else 758 1.1 alm sgflag &= ~GLB; 759 1.1 alm if (sflags & SGG) 760 1.1 alm sgflag ^= GSG; 761 1.1 alm if (sflags & SGP) 762 1.1 alm sgflag ^= GPR, sgflag &= ~(GLS | GNP); 763 1.1 alm do { 764 1.1 alm switch(*ibufp) { 765 1.1 alm case 'p': 766 1.1 alm sgflag |= GPR, ibufp++; 767 1.1 alm break; 768 1.1 alm case 'l': 769 1.1 alm sgflag |= GLS, ibufp++; 770 1.1 alm break; 771 1.1 alm case 'n': 772 1.1 alm sgflag |= GNP, ibufp++; 773 1.1 alm break; 774 1.1 alm default: 775 1.1 alm n++; 776 1.1 alm } 777 1.1 alm } while (!n); 778 1.1 alm if (check_addr_range(current_addr, current_addr) < 0) 779 1.1 alm return ERR; 780 1.1 alm GET_COMMAND_SUFFIX(); 781 1.1 alm if (!isglobal) clear_undo_stack(); 782 1.1 alm if (search_and_replace(pat, sgflag, sgnum) < 0) 783 1.1 alm return ERR; 784 1.1 alm break; 785 1.1 alm case 't': 786 1.1 alm if (check_addr_range(current_addr, current_addr) < 0) 787 1.1 alm return ERR; 788 1.1 alm GET_THIRD_ADDR(addr); 789 1.1 alm GET_COMMAND_SUFFIX(); 790 1.1 alm if (!isglobal) clear_undo_stack(); 791 1.1 alm if (copy_lines(addr) < 0) 792 1.1 alm return ERR; 793 1.1 alm break; 794 1.1 alm case 'u': 795 1.1 alm if (addr_cnt > 0) { 796 1.26 dholland seterrmsg("unexpected address"); 797 1.1 alm return ERR; 798 1.1 alm } 799 1.1 alm GET_COMMAND_SUFFIX(); 800 1.1 alm if (pop_undo_stack() < 0) 801 1.1 alm return ERR; 802 1.1 alm break; 803 1.1 alm case 'w': 804 1.1 alm case 'W': 805 1.1 alm if ((n = *ibufp) == 'q' || n == 'Q') { 806 1.1 alm gflag = EOF; 807 1.1 alm ibufp++; 808 1.1 alm } 809 1.10 christos if (!isspace((unsigned char)*ibufp)) { 810 1.26 dholland seterrmsg("unexpected command suffix"); 811 1.1 alm return ERR; 812 1.1 alm } else if ((fnp = get_filename()) == NULL) 813 1.1 alm return ERR; 814 1.1 alm if (addr_cnt == 0 && !addr_last) 815 1.1 alm first_addr = second_addr = 0; 816 1.1 alm else if (check_addr_range(1, addr_last) < 0) 817 1.1 alm return ERR; 818 1.1 alm GET_COMMAND_SUFFIX(); 819 1.1 alm if (*old_filename == '\0' && *fnp != '!') 820 1.18 christos strlcpy(old_filename, fnp, sizeof(old_filename) - 2); 821 1.1 alm #ifdef BACKWARDS 822 1.1 alm if (*fnp == '\0' && *old_filename == '\0') { 823 1.26 dholland seterrmsg("no current filename"); 824 1.1 alm return ERR; 825 1.1 alm } 826 1.1 alm #endif 827 1.31 rillig if ((addr = write_file(*fnp ? fnp : old_filename, 828 1.1 alm (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0) 829 1.1 alm return ERR; 830 1.1 alm else if (addr == addr_last) 831 1.1 alm modified = 0; 832 1.1 alm else if (modified && !scripted && n == 'q') 833 1.1 alm gflag = EMOD; 834 1.1 alm break; 835 1.1 alm case 'x': 836 1.1 alm if (addr_cnt > 0) { 837 1.26 dholland seterrmsg("unexpected address"); 838 1.1 alm return ERR; 839 1.1 alm } 840 1.1 alm GET_COMMAND_SUFFIX(); 841 1.1 alm #ifdef DES 842 1.1 alm des = get_keyword(); 843 1.1 alm #else 844 1.26 dholland seterrmsg("crypt unavailable"); 845 1.1 alm return ERR; 846 1.1 alm #endif 847 1.1 alm break; 848 1.1 alm case 'z': 849 1.1 alm #ifdef BACKWARDS 850 1.1 alm if (check_addr_range(first_addr = 1, current_addr + 1) < 0) 851 1.1 alm #else 852 1.1 alm if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0) 853 1.1 alm #endif 854 1.1 alm return ERR; 855 1.1 alm else if ('0' < *ibufp && *ibufp <= '9') 856 1.1 alm STRTOL(rows, ibufp); 857 1.1 alm GET_COMMAND_SUFFIX(); 858 1.1 alm if (display_lines(second_addr, min(addr_last, 859 1.1 alm second_addr + rows), gflag) < 0) 860 1.1 alm return ERR; 861 1.1 alm gflag = 0; 862 1.1 alm break; 863 1.1 alm case '=': 864 1.1 alm GET_COMMAND_SUFFIX(); 865 1.4 thorpej printf("%ld\n", addr_cnt ? second_addr : addr_last); 866 1.1 alm break; 867 1.1 alm case '!': 868 1.1 alm if (addr_cnt > 0) { 869 1.26 dholland seterrmsg("unexpected address"); 870 1.1 alm return ERR; 871 1.30 christos } 872 1.30 christos if ((sflags = get_shell_command()) < 0) 873 1.1 alm return ERR; 874 1.1 alm GET_COMMAND_SUFFIX(); 875 1.1 alm if (sflags) printf("%s\n", shcmd + 1); 876 1.1 alm system(shcmd + 1); 877 1.1 alm if (!scripted) printf("!\n"); 878 1.1 alm break; 879 1.1 alm case '\n': 880 1.1 alm #ifdef BACKWARDS 881 1.1 alm if (check_addr_range(first_addr = 1, current_addr + 1) < 0 882 1.1 alm #else 883 1.1 alm if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0 884 1.1 alm #endif 885 1.1 alm || display_lines(second_addr, second_addr, 0) < 0) 886 1.1 alm return ERR; 887 1.1 alm break; 888 1.1 alm default: 889 1.26 dholland seterrmsg("unknown command"); 890 1.1 alm return ERR; 891 1.1 alm } 892 1.1 alm return gflag; 893 1.1 alm } 894 1.1 alm 895 1.1 alm 896 1.1 alm /* check_addr_range: return status of address range check */ 897 1.1 alm int 898 1.14 xtraeme check_addr_range(long n, long m) 899 1.1 alm { 900 1.1 alm if (addr_cnt == 0) { 901 1.1 alm first_addr = n; 902 1.1 alm second_addr = m; 903 1.1 alm } 904 1.1 alm if (first_addr > second_addr || 1 > first_addr || 905 1.1 alm second_addr > addr_last) { 906 1.26 dholland seterrmsg("invalid address"); 907 1.1 alm return ERR; 908 1.1 alm } 909 1.1 alm return 0; 910 1.1 alm } 911 1.1 alm 912 1.1 alm 913 1.31 rillig /* get_matching_node_addr: return the address of the next line matching a 914 1.1 alm pattern in a given direction. wrap around begin/end of editor buffer if 915 1.1 alm necessary */ 916 1.1 alm long 917 1.14 xtraeme get_matching_node_addr(pattern_t *pat, int dir) 918 1.1 alm { 919 1.1 alm char *s; 920 1.1 alm long n = current_addr; 921 1.1 alm line_t *lp; 922 1.1 alm 923 1.1 alm if (!pat) return ERR; 924 1.1 alm do { 925 1.4 thorpej if ((n = dir ? INC_MOD(n, addr_last) : 926 1.4 thorpej DEC_MOD(n, addr_last)) != 0) { 927 1.1 alm lp = get_addressed_line_node(n); 928 1.1 alm if ((s = get_sbuf_line(lp)) == NULL) 929 1.1 alm return ERR; 930 1.1 alm if (isbinary) 931 1.1 alm NUL_TO_NEWLINE(s, lp->len); 932 1.1 alm if (!regexec(pat, s, 0, NULL, 0)) 933 1.1 alm return n; 934 1.1 alm } 935 1.1 alm } while (n != current_addr); 936 1.26 dholland seterrmsg("no match"); 937 1.1 alm return ERR; 938 1.1 alm } 939 1.1 alm 940 1.1 alm 941 1.1 alm /* get_filename: return pointer to copy of filename in the command buffer */ 942 1.1 alm char * 943 1.14 xtraeme get_filename(void) 944 1.1 alm { 945 1.1 alm static char *file = NULL; 946 1.1 alm static int filesz = 0; 947 1.1 alm 948 1.1 alm int n; 949 1.1 alm 950 1.1 alm if (*ibufp != '\n') { 951 1.1 alm SKIP_BLANKS(); 952 1.1 alm if (*ibufp == '\n') { 953 1.26 dholland seterrmsg("invalid filename"); 954 1.1 alm return NULL; 955 1.1 alm } else if ((ibufp = get_extended_line(&n, 1)) == NULL) 956 1.1 alm return NULL; 957 1.1 alm else if (*ibufp == '!') { 958 1.1 alm ibufp++; 959 1.1 alm if ((n = get_shell_command()) < 0) 960 1.1 alm return NULL; 961 1.1 alm if (n) printf("%s\n", shcmd + 1); 962 1.1 alm return shcmd; 963 1.1 alm } else if (n - 1 > MAXPATHLEN) { 964 1.26 dholland seterrmsg("filename too long"); 965 1.1 alm return NULL; 966 1.1 alm } 967 1.1 alm } 968 1.1 alm #ifndef BACKWARDS 969 1.1 alm else if (*old_filename == '\0') { 970 1.26 dholland seterrmsg("no current filename"); 971 1.1 alm return NULL; 972 1.1 alm } 973 1.1 alm #endif 974 1.1 alm REALLOC(file, filesz, MAXPATHLEN + 1, NULL); 975 1.1 alm for (n = 0; *ibufp != '\n';) 976 1.1 alm file[n++] = *ibufp++; 977 1.1 alm file[n] = '\0'; 978 1.1 alm return is_legal_filename(file) ? file : NULL; 979 1.1 alm } 980 1.1 alm 981 1.1 alm 982 1.1 alm /* get_shell_command: read a shell command from stdin; return substitution 983 1.1 alm status */ 984 1.1 alm int 985 1.14 xtraeme get_shell_command(void) 986 1.1 alm { 987 1.1 alm static char *buf = NULL; 988 1.1 alm static int n = 0; 989 1.1 alm 990 1.1 alm char *s; /* substitution char pointer */ 991 1.1 alm int i = 0; 992 1.1 alm int j = 0; 993 1.1 alm 994 1.30 christos if (red || secure) { 995 1.26 dholland seterrmsg("shell access restricted"); 996 1.1 alm return ERR; 997 1.1 alm } else if ((s = ibufp = get_extended_line(&j, 1)) == NULL) 998 1.1 alm return ERR; 999 1.1 alm REALLOC(buf, n, j + 1, ERR); 1000 1.1 alm buf[i++] = '!'; /* prefix command w/ bang */ 1001 1.1 alm while (*ibufp != '\n') 1002 1.1 alm switch (*ibufp) { 1003 1.1 alm default: 1004 1.1 alm REALLOC(buf, n, i + 2, ERR); 1005 1.1 alm buf[i++] = *ibufp; 1006 1.1 alm if (*ibufp++ == '\\') 1007 1.1 alm buf[i++] = *ibufp++; 1008 1.1 alm break; 1009 1.1 alm case '!': 1010 1.1 alm if (s != ibufp) { 1011 1.1 alm REALLOC(buf, n, i + 1, ERR); 1012 1.1 alm buf[i++] = *ibufp++; 1013 1.1 alm } 1014 1.1 alm #ifdef BACKWARDS 1015 1.1 alm else if (shcmd == NULL || *(shcmd + 1) == '\0') 1016 1.1 alm #else 1017 1.1 alm else if (shcmd == NULL) 1018 1.1 alm #endif 1019 1.1 alm { 1020 1.26 dholland seterrmsg("no previous command"); 1021 1.1 alm return ERR; 1022 1.1 alm } else { 1023 1.1 alm REALLOC(buf, n, i + shcmdi, ERR); 1024 1.1 alm for (s = shcmd + 1; s < shcmd + shcmdi;) 1025 1.1 alm buf[i++] = *s++; 1026 1.1 alm s = ibufp++; 1027 1.1 alm } 1028 1.1 alm break; 1029 1.1 alm case '%': 1030 1.1 alm if (*old_filename == '\0') { 1031 1.26 dholland seterrmsg("no current filename"); 1032 1.1 alm return ERR; 1033 1.1 alm } 1034 1.1 alm j = strlen(s = strip_escapes(old_filename)); 1035 1.1 alm REALLOC(buf, n, i + j, ERR); 1036 1.1 alm while (j--) 1037 1.1 alm buf[i++] = *s++; 1038 1.1 alm s = ibufp++; 1039 1.1 alm break; 1040 1.1 alm } 1041 1.1 alm REALLOC(shcmd, shcmdsz, i + 1, ERR); 1042 1.1 alm memcpy(shcmd, buf, i); 1043 1.1 alm shcmd[shcmdi = i] = '\0'; 1044 1.1 alm return *s == '!' || *s == '%'; 1045 1.1 alm } 1046 1.1 alm 1047 1.1 alm 1048 1.1 alm /* append_lines: insert text from stdin to after line n; stop when either a 1049 1.1 alm single period is read or EOF; return status */ 1050 1.1 alm int 1051 1.14 xtraeme append_lines(long n) 1052 1.1 alm { 1053 1.1 alm int l; 1054 1.1 alm char *lp = ibuf; 1055 1.1 alm char *eot; 1056 1.1 alm undo_t *up = NULL; 1057 1.1 alm 1058 1.1 alm for (current_addr = n;;) { 1059 1.1 alm if (!isglobal) { 1060 1.1 alm if ((l = get_tty_line()) < 0) 1061 1.1 alm return ERR; 1062 1.1 alm else if (l == 0 || ibuf[l - 1] != '\n') { 1063 1.1 alm clearerr(stdin); 1064 1.1 alm return l ? EOF : 0; 1065 1.1 alm } 1066 1.1 alm lp = ibuf; 1067 1.1 alm } else if (*(lp = ibufp) == '\0') 1068 1.1 alm return 0; 1069 1.1 alm else { 1070 1.1 alm while (*ibufp++ != '\n') 1071 1.1 alm ; 1072 1.1 alm l = ibufp - lp; 1073 1.1 alm } 1074 1.1 alm if (l == 2 && lp[0] == '.' && lp[1] == '\n') { 1075 1.1 alm return 0; 1076 1.1 alm } 1077 1.1 alm eot = lp + l; 1078 1.1 alm SPL1(); 1079 1.1 alm do { 1080 1.1 alm if ((lp = put_sbuf_line(lp)) == NULL) { 1081 1.1 alm SPL0(); 1082 1.1 alm return ERR; 1083 1.1 alm } else if (up) 1084 1.1 alm up->t = get_addressed_line_node(current_addr); 1085 1.1 alm else if ((up = push_undo_stack(UADD, current_addr, 1086 1.1 alm current_addr)) == NULL) { 1087 1.1 alm SPL0(); 1088 1.1 alm return ERR; 1089 1.1 alm } 1090 1.1 alm } while (lp != eot); 1091 1.1 alm modified = 1; 1092 1.1 alm SPL0(); 1093 1.1 alm } 1094 1.1 alm /* NOTREACHED */ 1095 1.1 alm } 1096 1.1 alm 1097 1.1 alm 1098 1.1 alm /* join_lines: replace a range of lines with the joined text of those lines */ 1099 1.1 alm int 1100 1.14 xtraeme join_lines(long from, long to) 1101 1.1 alm { 1102 1.1 alm static char *buf = NULL; 1103 1.1 alm static int n; 1104 1.1 alm 1105 1.1 alm char *s; 1106 1.1 alm int size = 0; 1107 1.1 alm line_t *bp, *ep; 1108 1.1 alm 1109 1.1 alm ep = get_addressed_line_node(INC_MOD(to, addr_last)); 1110 1.1 alm bp = get_addressed_line_node(from); 1111 1.1 alm for (; bp != ep; bp = bp->q_forw) { 1112 1.1 alm if ((s = get_sbuf_line(bp)) == NULL) 1113 1.1 alm return ERR; 1114 1.1 alm REALLOC(buf, n, size + bp->len, ERR); 1115 1.1 alm memcpy(buf + size, s, bp->len); 1116 1.1 alm size += bp->len; 1117 1.1 alm } 1118 1.1 alm REALLOC(buf, n, size + 2, ERR); 1119 1.1 alm memcpy(buf + size, "\n", 2); 1120 1.1 alm if (delete_lines(from, to) < 0) 1121 1.1 alm return ERR; 1122 1.1 alm current_addr = from - 1; 1123 1.1 alm SPL1(); 1124 1.1 alm if (put_sbuf_line(buf) == NULL || 1125 1.1 alm push_undo_stack(UADD, current_addr, current_addr) == NULL) { 1126 1.1 alm SPL0(); 1127 1.1 alm return ERR; 1128 1.1 alm } 1129 1.1 alm modified = 1; 1130 1.1 alm SPL0(); 1131 1.1 alm return 0; 1132 1.1 alm } 1133 1.1 alm 1134 1.1 alm 1135 1.1 alm /* move_lines: move a range of lines */ 1136 1.1 alm int 1137 1.14 xtraeme move_lines(long addr) 1138 1.1 alm { 1139 1.1 alm line_t *b1, *a1, *b2, *a2; 1140 1.1 alm long n = INC_MOD(second_addr, addr_last); 1141 1.1 alm long p = first_addr - 1; 1142 1.1 alm int done = (addr == first_addr - 1 || addr == second_addr); 1143 1.1 alm 1144 1.1 alm SPL1(); 1145 1.1 alm if (done) { 1146 1.1 alm a2 = get_addressed_line_node(n); 1147 1.1 alm b2 = get_addressed_line_node(p); 1148 1.1 alm current_addr = second_addr; 1149 1.1 alm } else if (push_undo_stack(UMOV, p, n) == NULL || 1150 1.1 alm push_undo_stack(UMOV, addr, INC_MOD(addr, addr_last)) == NULL) { 1151 1.1 alm SPL0(); 1152 1.1 alm return ERR; 1153 1.1 alm } else { 1154 1.1 alm a1 = get_addressed_line_node(n); 1155 1.1 alm if (addr < first_addr) { 1156 1.1 alm b1 = get_addressed_line_node(p); 1157 1.1 alm b2 = get_addressed_line_node(addr); 1158 1.1 alm /* this get_addressed_line_node last! */ 1159 1.1 alm } else { 1160 1.1 alm b2 = get_addressed_line_node(addr); 1161 1.1 alm b1 = get_addressed_line_node(p); 1162 1.1 alm /* this get_addressed_line_node last! */ 1163 1.1 alm } 1164 1.1 alm a2 = b2->q_forw; 1165 1.1 alm REQUE(b2, b1->q_forw); 1166 1.1 alm REQUE(a1->q_back, a2); 1167 1.1 alm REQUE(b1, a1); 1168 1.31 rillig current_addr = addr + ((addr < first_addr) ? 1169 1.1 alm second_addr - first_addr + 1 : 0); 1170 1.1 alm } 1171 1.1 alm if (isglobal) 1172 1.1 alm unset_active_nodes(b2->q_forw, a2); 1173 1.1 alm modified = 1; 1174 1.1 alm SPL0(); 1175 1.1 alm return 0; 1176 1.1 alm } 1177 1.1 alm 1178 1.1 alm 1179 1.1 alm /* copy_lines: copy a range of lines; return status */ 1180 1.1 alm int 1181 1.14 xtraeme copy_lines(long addr) 1182 1.1 alm { 1183 1.1 alm line_t *lp, *np = get_addressed_line_node(first_addr); 1184 1.1 alm undo_t *up = NULL; 1185 1.1 alm long n = second_addr - first_addr + 1; 1186 1.1 alm long m = 0; 1187 1.1 alm 1188 1.1 alm current_addr = addr; 1189 1.1 alm if (first_addr <= addr && addr < second_addr) { 1190 1.1 alm n = addr - first_addr + 1; 1191 1.1 alm m = second_addr - addr; 1192 1.1 alm } 1193 1.1 alm for (; n > 0; n=m, m=0, np = get_addressed_line_node(current_addr + 1)) 1194 1.1 alm for (; n-- > 0; np = np->q_forw) { 1195 1.1 alm SPL1(); 1196 1.1 alm if ((lp = dup_line_node(np)) == NULL) { 1197 1.1 alm SPL0(); 1198 1.1 alm return ERR; 1199 1.1 alm } 1200 1.1 alm add_line_node(lp); 1201 1.1 alm if (up) 1202 1.1 alm up->t = lp; 1203 1.1 alm else if ((up = push_undo_stack(UADD, current_addr, 1204 1.1 alm current_addr)) == NULL) { 1205 1.1 alm SPL0(); 1206 1.1 alm return ERR; 1207 1.1 alm } 1208 1.1 alm modified = 1; 1209 1.1 alm SPL0(); 1210 1.1 alm } 1211 1.1 alm return 0; 1212 1.1 alm } 1213 1.1 alm 1214 1.1 alm 1215 1.1 alm /* delete_lines: delete a range of lines */ 1216 1.1 alm int 1217 1.14 xtraeme delete_lines(long from, long to) 1218 1.1 alm { 1219 1.1 alm line_t *n, *p; 1220 1.1 alm 1221 1.1 alm SPL1(); 1222 1.1 alm if (push_undo_stack(UDEL, from, to) == NULL) { 1223 1.1 alm SPL0(); 1224 1.1 alm return ERR; 1225 1.1 alm } 1226 1.1 alm n = get_addressed_line_node(INC_MOD(to, addr_last)); 1227 1.1 alm p = get_addressed_line_node(from - 1); 1228 1.1 alm /* this get_addressed_line_node last! */ 1229 1.1 alm if (isglobal) 1230 1.1 alm unset_active_nodes(p->q_forw, n); 1231 1.1 alm REQUE(p, n); 1232 1.1 alm addr_last -= to - from + 1; 1233 1.1 alm current_addr = from - 1; 1234 1.1 alm modified = 1; 1235 1.1 alm SPL0(); 1236 1.1 alm return 0; 1237 1.1 alm } 1238 1.1 alm 1239 1.1 alm 1240 1.1 alm /* display_lines: print a range of lines to stdout */ 1241 1.1 alm int 1242 1.14 xtraeme display_lines(long from, long to, int gflag) 1243 1.1 alm { 1244 1.1 alm line_t *bp; 1245 1.1 alm line_t *ep; 1246 1.1 alm char *s; 1247 1.1 alm 1248 1.1 alm if (!from) { 1249 1.26 dholland seterrmsg("invalid address"); 1250 1.1 alm return ERR; 1251 1.1 alm } 1252 1.1 alm ep = get_addressed_line_node(INC_MOD(to, addr_last)); 1253 1.1 alm bp = get_addressed_line_node(from); 1254 1.1 alm for (; bp != ep; bp = bp->q_forw) { 1255 1.1 alm if ((s = get_sbuf_line(bp)) == NULL) 1256 1.1 alm return ERR; 1257 1.1 alm if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0) 1258 1.1 alm return ERR; 1259 1.1 alm } 1260 1.1 alm return 0; 1261 1.1 alm } 1262 1.1 alm 1263 1.1 alm 1264 1.1 alm #define MAXMARK 26 /* max number of marks */ 1265 1.1 alm 1266 1.1 alm line_t *mark[MAXMARK]; /* line markers */ 1267 1.1 alm int markno; /* line marker count */ 1268 1.1 alm 1269 1.1 alm /* mark_line_node: set a line node mark */ 1270 1.1 alm int 1271 1.14 xtraeme mark_line_node(line_t *lp, int n) 1272 1.1 alm { 1273 1.1 alm if (!islower(n)) { 1274 1.26 dholland seterrmsg("invalid mark character"); 1275 1.1 alm return ERR; 1276 1.1 alm } else if (mark[n - 'a'] == NULL) 1277 1.1 alm markno++; 1278 1.1 alm mark[n - 'a'] = lp; 1279 1.1 alm return 0; 1280 1.1 alm } 1281 1.1 alm 1282 1.1 alm 1283 1.1 alm /* get_marked_node_addr: return address of a marked line */ 1284 1.1 alm long 1285 1.14 xtraeme get_marked_node_addr(int n) 1286 1.1 alm { 1287 1.1 alm if (!islower(n)) { 1288 1.26 dholland seterrmsg("invalid mark character"); 1289 1.1 alm return ERR; 1290 1.1 alm } 1291 1.1 alm return get_line_node_addr(mark[n - 'a']); 1292 1.1 alm } 1293 1.1 alm 1294 1.1 alm 1295 1.1 alm /* unmark_line_node: clear line node mark */ 1296 1.1 alm void 1297 1.14 xtraeme unmark_line_node(line_t *lp) 1298 1.1 alm { 1299 1.1 alm int i; 1300 1.1 alm 1301 1.1 alm for (i = 0; markno && i < MAXMARK; i++) 1302 1.1 alm if (mark[i] == lp) { 1303 1.1 alm mark[i] = NULL; 1304 1.1 alm markno--; 1305 1.1 alm } 1306 1.1 alm } 1307 1.1 alm 1308 1.1 alm 1309 1.1 alm /* dup_line_node: return a pointer to a copy of a line node */ 1310 1.1 alm line_t * 1311 1.14 xtraeme dup_line_node(line_t *lp) 1312 1.1 alm { 1313 1.1 alm line_t *np; 1314 1.1 alm 1315 1.1 alm if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) { 1316 1.1 alm fprintf(stderr, "%s\n", strerror(errno)); 1317 1.26 dholland seterrmsg("out of memory"); 1318 1.1 alm return NULL; 1319 1.1 alm } 1320 1.1 alm np->seek = lp->seek; 1321 1.1 alm np->len = lp->len; 1322 1.1 alm return np; 1323 1.1 alm } 1324 1.1 alm 1325 1.1 alm 1326 1.1 alm /* has_trailing_escape: return the parity of escapes preceding a character 1327 1.1 alm in a string */ 1328 1.1 alm int 1329 1.14 xtraeme has_trailing_escape(char *s, char *t) 1330 1.1 alm { 1331 1.1 alm return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1); 1332 1.1 alm } 1333 1.1 alm 1334 1.1 alm 1335 1.1 alm /* strip_escapes: return copy of escaped string of at most length MAXPATHLEN */ 1336 1.1 alm char * 1337 1.17 christos strip_escapes(const char *s) 1338 1.1 alm { 1339 1.1 alm static char *file = NULL; 1340 1.1 alm static int filesz = 0; 1341 1.1 alm 1342 1.1 alm int i = 0; 1343 1.1 alm 1344 1.1 alm REALLOC(file, filesz, MAXPATHLEN + 1, NULL); 1345 1.21 ginsbach while ((i < (filesz - 1)) && 1346 1.21 ginsbach (file[i++] = (*s == '\\') != '\0' ? *++s : *s)) 1347 1.1 alm s++; 1348 1.22 ginsbach file[filesz - 1] = '\0'; 1349 1.1 alm return file; 1350 1.1 alm } 1351 1.1 alm 1352 1.1 alm 1353 1.1 alm void 1354 1.14 xtraeme signal_hup(int signo) 1355 1.1 alm { 1356 1.1 alm if (mutex) 1357 1.1 alm sigflags |= (1 << (signo - 1)); 1358 1.1 alm else handle_hup(signo); 1359 1.1 alm } 1360 1.1 alm 1361 1.1 alm 1362 1.1 alm void 1363 1.14 xtraeme signal_int(int signo) 1364 1.1 alm { 1365 1.1 alm if (mutex) 1366 1.1 alm sigflags |= (1 << (signo - 1)); 1367 1.1 alm else handle_int(signo); 1368 1.1 alm } 1369 1.1 alm 1370 1.1 alm 1371 1.1 alm void 1372 1.14 xtraeme handle_hup(int signo) 1373 1.1 alm { 1374 1.1 alm char *hup = NULL; /* hup filename */ 1375 1.1 alm char *s; 1376 1.1 alm int n; 1377 1.1 alm 1378 1.1 alm if (!sigactive) 1379 1.1 alm quit(1); 1380 1.1 alm sigflags &= ~(1 << (signo - 1)); 1381 1.1 alm if (addr_last && write_file("ed.hup", "w", 1, addr_last) < 0 && 1382 1.1 alm (s = getenv("HOME")) != NULL && 1383 1.1 alm (n = strlen(s)) + 8 <= MAXPATHLEN && /* "ed.hup" + '/' */ 1384 1.1 alm (hup = (char *) malloc(n + 10)) != NULL) { 1385 1.1 alm strcpy(hup, s); 1386 1.1 alm if (hup[n - 1] != '/') 1387 1.1 alm hup[n] = '/', hup[n+1] = '\0'; 1388 1.1 alm strcat(hup, "ed.hup"); 1389 1.1 alm write_file(hup, "w", 1, addr_last); 1390 1.1 alm } 1391 1.1 alm quit(2); 1392 1.1 alm } 1393 1.1 alm 1394 1.1 alm 1395 1.1 alm void 1396 1.14 xtraeme handle_int(int signo) 1397 1.1 alm { 1398 1.1 alm if (!sigactive) 1399 1.1 alm quit(1); 1400 1.1 alm sigflags &= ~(1 << (signo - 1)); 1401 1.1 alm #ifdef _POSIX_SOURCE 1402 1.1 alm siglongjmp(env, -1); 1403 1.1 alm #else 1404 1.1 alm longjmp(env, -1); 1405 1.1 alm #endif 1406 1.1 alm } 1407 1.1 alm 1408 1.1 alm 1409 1.1 alm int cols = 72; /* wrap column */ 1410 1.1 alm 1411 1.1 alm void 1412 1.14 xtraeme handle_winch(int signo) 1413 1.1 alm { 1414 1.1 alm struct winsize ws; /* window size structure */ 1415 1.1 alm 1416 1.1 alm sigflags &= ~(1 << (signo - 1)); 1417 1.1 alm if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) { 1418 1.1 alm if (ws.ws_row > 2) rows = ws.ws_row - 2; 1419 1.1 alm if (ws.ws_col > 8) cols = ws.ws_col - 8; 1420 1.1 alm } 1421 1.1 alm } 1422 1.1 alm 1423 1.1 alm 1424 1.1 alm /* is_legal_filename: return a legal filename */ 1425 1.1 alm int 1426 1.14 xtraeme is_legal_filename(char *s) 1427 1.1 alm { 1428 1.1 alm if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) { 1429 1.26 dholland seterrmsg("shell access restricted"); 1430 1.1 alm return 0; 1431 1.1 alm } 1432 1.1 alm return 1; 1433 1.1 alm } 1434