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