2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
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 $";
12 #include <sys/types.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.
33 ex_global(sp
, ep
, 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.
47 ex_vglobal(sp
, ep
, cmdp
)
52 return (global(sp
, ep
, cmdp
, VGLOBAL
));
56 global(sp
, ep
, cmdp
, cmd
)
62 struct sigaction act
, oact
;
63 struct termios nterm
, term
;
64 recno_t elno
, last1
, last2
, lno
;
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
);
84 * Get the pattern string, toss escaped characters.
87 * Only toss an escaped character if it escapes a delimiter.
89 for (ptrn
= t
= p
;;) {
90 if (p
[0] == '\0' || p
[0] == delim
) {
95 * Nul terminate the pattern string -- it's passed
96 * to regcomp which doesn't understand anything else.
101 if (p
[0] == '\\' && p
[1] == delim
)
106 /* If the pattern string is empty, use the last one. */
108 if (!F_ISSET(sp
, S_SRE_SET
)) {
109 msgq(sp
, M_ERR
, "No previous regular expression.");
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
))
125 /* Compile the RE. */
127 eval
= regcomp(re
, ptrn
, reflags
);
129 /* Free up any allocated memory. */
131 FREE_SPACE(sp
, ptrn
, 0);
134 re_error(sp
, eval
, re
);
139 * Set saved RE. Historic practice is that
140 * globals set direction as well as the RE.
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.");
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.
160 F_SET(sp
, S_SUBRE_SET
);
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
);
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");
186 nterm
.c_lflag
|= ISIG
;
187 if (tcsetattr(STDIN_FILENO
,
188 TCSANOW
| TCSASOFT
, &nterm
)) {
189 msgq(sp
, M_SYSERR
, "tcsetattr");
196 /* For each line... */
197 for (rval
= 0, lno
= cmdp
->addr1
.lno
,
198 elno
= cmdp
->addr2
.lno
; lno
<= elno
; ++lno
) {
201 if ((t
= file_gline(sp
, ep
, lno
, &len
)) == NULL
) {
202 GETLINE_ERR(sp
, lno
);
206 /* Search for a match. */
208 match
[0].rm_eo
= len
;
209 switch(eval
= regexec(re
, t
, 1, match
, REG_STARTEND
)) {
219 re_error(sp
, eval
, re
);
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
))
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
))
240 if (file_lline(sp
, ep
, &last2
)) {
244 if (last2
> last1
) { /* Created lines. */
246 sp
->lno
= lno
+= last2
;
248 } else if (last1
> last2
) { /* Deleted lines. */
250 sp
->lno
= lno
-= last1
;
257 /* Someone's unhappy, time to stop. */
258 if (F_ISSET(sp
, S_INTERRUPTED
)) {
259 msgq(sp
, M_INFO
, "Interrupted.");
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
);
272 F_CLR(sp
, S_INTERRUPTIBLE
);
280 * Set the interrupt bit in any screen that is running an interruptible
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
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
);