/bin/ed/main.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 1416 lines · 1229 code · 101 blank · 86 comment · 461 complexity · 0a58accc4c917837a060587fc3dc93c2 MD5 · raw file

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