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