cols_per_screen wasn't set right if about to step through a line
[nvi.git] / ex / ex_global.c
blob0042bdd3f4a263014b67cfe495abc9d202b900c5
1 /*-
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
6 */
8 #ifndef lint
9 static char sccsid[] = "$Id: ex_global.c,v 8.26 1993/12/23 10:46:49 bostic Exp $ (Berkeley) $Date: 1993/12/23 10:46:49 $";
10 #endif /* not lint */
12 #include <sys/types.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
20 #include "vi.h"
21 #include "excmd.h"
23 enum which {GLOBAL, VGLOBAL};
25 static int global __P((SCR *, EXF *, EXCMDARG *, enum which));
26 static void global_intr __P((int));
29 * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands]
30 * Exec on lines matching a pattern.
32 int
33 ex_global(sp, ep, cmdp)
34 SCR *sp;
35 EXF *ep;
36 EXCMDARG *cmdp;
38 return (global(sp, ep,
39 cmdp, F_ISSET(cmdp, E_FORCE) ? VGLOBAL : GLOBAL));
43 * ex_vglobal -- [line [,line]] v[global] /pattern/ [commands]
44 * Exec on lines not matching a pattern.
46 int
47 ex_vglobal(sp, ep, cmdp)
48 SCR *sp;
49 EXF *ep;
50 EXCMDARG *cmdp;
52 return (global(sp, ep, cmdp, VGLOBAL));
55 static int
56 global(sp, ep, cmdp, cmd)
57 SCR *sp;
58 EXF *ep;
59 EXCMDARG *cmdp;
60 enum which cmd;
62 struct sigaction act, oact;
63 struct termios nterm, term;
64 recno_t elno, last1, last2, lno;
65 regmatch_t match[1];
66 regex_t *re, lre;
67 size_t clen, len;
68 u_int istate;
69 int delim, eval, isig, reflags, replaced, rval;
70 char *cb, *ptrn, *p, *t;
73 * Skip leading white space. Historic vi allowed any non-
74 * alphanumeric to serve as the global command delimiter.
76 for (p = cmdp->argv[0]->bp; isblank(*p); ++p);
77 if (*p == '\0' || isalnum(*p)) {
78 msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage);
79 return (1);
81 delim = *p++;
84 * Get the pattern string, toss escaped characters.
86 * QUOTING NOTE:
87 * Only toss an escaped character if it escapes a delimiter.
89 for (ptrn = t = p;;) {
90 if (p[0] == '\0' || p[0] == delim) {
91 if (p[0] == delim)
92 ++p;
94 * !!!
95 * Nul terminate the pattern string -- it's passed
96 * to regcomp which doesn't understand anything else.
98 *t = '\0';
99 break;
101 if (p[0] == '\\' && p[1] == delim)
102 ++p;
103 *t++ = *p++;
106 /* If the pattern string is empty, use the last one. */
107 if (*ptrn == '\0') {
108 if (!F_ISSET(sp, S_SRE_SET)) {
109 msgq(sp, M_ERR, "No previous regular expression.");
110 return (1);
112 re = &sp->sre;
113 } else {
114 /* Set RE flags. */
115 reflags = 0;
116 if (O_ISSET(sp, O_EXTENDED))
117 reflags |= REG_EXTENDED;
118 if (O_ISSET(sp, O_IGNORECASE))
119 reflags |= REG_ICASE;
121 /* Convert vi-style RE's to POSIX 1003.2 RE's. */
122 if (re_conv(sp, &ptrn, &replaced))
123 return (1);
125 /* Compile the RE. */
126 re = &lre;
127 eval = regcomp(re, ptrn, reflags);
129 /* Free up any allocated memory. */
130 if (replaced)
131 FREE_SPACE(sp, ptrn, 0);
133 if (eval) {
134 re_error(sp, eval, re);
135 return (1);
139 * Set saved RE. Historic practice is that
140 * globals set direction as well as the RE.
142 sp->sre = lre;
143 sp->searchdir = FORWARD;
144 F_SET(sp, S_SRE_SET);
147 /* Get the command string. */
148 if ((clen = strlen(p)) == 0) {
149 msgq(sp, M_ERR, "No command string specified.");
150 return (1);
152 MALLOC_RET(sp, cb, char *, clen);
153 memmove(cb, p, clen);
156 * The global commands sets the substitute RE as well as
157 * the everything-else RE.
159 sp->subre = sp->sre;
160 F_SET(sp, S_SUBRE_SET);
162 F_SET(sp, S_GLOBAL);
165 * Command interrupts.
167 * ISIG turns on VINTR, VQUIT and VSUSP. We want VINTR to interrupt
168 * the command, so we install a handler. VQUIT is ignored by main()
169 * because nvi never wants to catch it. A handler for VSUSP should
170 * have been installed by the screen code.
172 if (F_ISSET(sp->gp, G_ISFROMTTY)) {
173 act.sa_handler = global_intr;
174 sigemptyset(&act.sa_mask);
175 act.sa_flags = 0;
176 if (isig = !sigaction(SIGINT, &act, &oact)) {
177 istate = F_ISSET(sp, S_INTERRUPTIBLE);
178 F_CLR(sp, S_INTERRUPTED);
179 F_SET(sp, S_INTERRUPTIBLE);
180 if (tcgetattr(STDIN_FILENO, &term)) {
181 msgq(sp, M_SYSERR, "tcgetattr");
182 free(cb);
183 return (1);
185 nterm = term;
186 nterm.c_lflag |= ISIG;
187 if (tcsetattr(STDIN_FILENO,
188 TCSANOW | TCSASOFT, &nterm)) {
189 msgq(sp, M_SYSERR, "tcsetattr");
190 free(cb);
191 return (1);
196 /* For each line... */
197 for (rval = 0, lno = cmdp->addr1.lno,
198 elno = cmdp->addr2.lno; lno <= elno; ++lno) {
200 /* Get the line. */
201 if ((t = file_gline(sp, ep, lno, &len)) == NULL) {
202 GETLINE_ERR(sp, lno);
203 goto err;
206 /* Search for a match. */
207 match[0].rm_so = 0;
208 match[0].rm_eo = len;
209 switch(eval = regexec(re, t, 1, match, REG_STARTEND)) {
210 case 0:
211 if (cmd == VGLOBAL)
212 continue;
213 break;
214 case REG_NOMATCH:
215 if (cmd == GLOBAL)
216 continue;
217 break;
218 default:
219 re_error(sp, eval, re);
220 goto err;
224 * Execute the command, keeping track of the lines that
225 * change, and adjusting for inserted/deleted lines.
227 if (file_lline(sp, ep, &last1))
228 goto err;
230 sp->lno = lno;
233 * The cursor moves to last line sent to the command, by
234 * default. If the command created new lines, it moves
235 * to the last of the new lines, if it deleted lines, it
236 * moves to the line after the deleted line.
238 if (ex_cmd(sp, ep, cb, clen))
239 goto err;
240 if (file_lline(sp, ep, &last2)) {
241 err: rval = 1;
242 break;
244 if (last2 > last1) { /* Created lines. */
245 last2 -= last1;
246 sp->lno = lno += last2;
247 elno += last2;
248 } else if (last1 > last2) { /* Deleted lines. */
249 last1 -= last2;
250 sp->lno = lno -= last1;
251 if (sp->lno == 0)
252 sp->lno = 1;
253 elno -= last1;
254 } else
255 sp->lno = lno;
257 /* Someone's unhappy, time to stop. */
258 if (F_ISSET(sp, S_INTERRUPTED)) {
259 msgq(sp, M_INFO, "Interrupted.");
260 break;
263 F_CLR(sp, S_GLOBAL);
265 if (F_ISSET(sp->gp, G_ISFROMTTY) && isig) {
266 if (sigaction(SIGINT, &oact, NULL))
267 msgq(sp, M_SYSERR, "signal");
268 if (tcsetattr(STDIN_FILENO, TCSANOW | TCSASOFT, &term))
269 msgq(sp, M_SYSERR, "tcsetattr");
270 F_CLR(sp, S_INTERRUPTED);
271 if (!istate)
272 F_CLR(sp, S_INTERRUPTIBLE);
274 free(cb);
275 return (rval);
279 * global_intr --
280 * Set the interrupt bit in any screen that is running an interruptible
281 * global.
283 * XXX
284 * In the future this may be a problem. The user should be able to move to
285 * another screen and keep typing while this runs. If so, and the user has
286 * more than one global running, it will be hard to decide which one to
287 * stop.
289 static void
290 global_intr(signo)
291 int signo;
293 SCR *sp;
295 for (sp = __global_list->dq.cqh_first;
296 sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
297 if (F_ISSET(sp, S_GLOBAL) && F_ISSET(sp, S_INTERRUPTIBLE))
298 F_SET(sp, S_INTERRUPTED);