1 /*-
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rodney Ruddock of the University of Guelph.
7 *
8 * %sccs.include.redist.c%
9 */
10
11 #ifndef lint
12 static char sccsid[] = "@(#)g.c 8.1 (Berkeley) 05/31/93";
13 #endif /* not lint */
14
15 #include <sys/types.h>
16
17 #include <limits.h>
18 #include <regex.h>
19 #include <setjmp.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #ifdef DBI
27 #include <db.h>
28 #endif
29
30 #include "ed.h"
31 #include "extern.h"
32
33 static int find_line __P((LINE *));
34 static void w_cmd_l_file __P((FILE *, FILE *, int *));
35
36 /*
37 * Find a line that we noted matched the RE earlier in the current
38 * buffer (it may have disappeared because of the commands in the
39 * command list).
40 */
41 static int
find_line(dot)42 find_line(dot)
43 LINE *dot;
44 {
45 LINE *l_cl;
46
47 l_cl = top;
48 for (;;) {
49 if (l_cl == dot)
50 return (1);
51 if (l_cl == bottom)
52 return (0);
53 l_cl = l_cl->below;
54 }
55 }
56
57 /*
58 * Write the command line to a STDIO tmp file. See g() below.
59 * This allows us to use cmd_loop to run the command list because
60 * we "trick" cmd_loop into reading a STDIO file instead of stdin.
61 */
62
63 static void
w_cmd_l_file(fp,inputt,errnum)64 w_cmd_l_file(fp, inputt, errnum)
65 FILE *fp, *inputt;
66 int *errnum;
67 {
68 int sl=0, jmp_flag, l_cnt=0;
69
70 if (jmp_flag = setjmp(ctrl_position3))
71 return;
72
73 for (;;) {
74 sigspecial3 = 1;
75 ss = getc(inputt);
76 sigspecial3 = 0;
77 skip1:
78 if (ss == EOF)
79 goto skip2;
80 else if (ss == '\n') {
81 if (sl != '\\') {
82 skip2:
83 if (l_cnt == 0)
84 fputc('p', fp);
85 else
86 fputc(sl, fp);
87 break;
88 }
89 }
90 else if ((ss == '\\') && (sl == '\\')) {
91 sigspecial3 = 1;
92 sl = getc(inputt);
93 sigspecial3 = 0;
94 if (sl == '\\') {
95 sigspecial3 = 1;
96 sl = getc(inputt);
97 sigspecial3 = 0;
98 if (sl == EOF)
99 goto skip2;
100 if (sl == '\n') {
101 fputc('\\', fp);
102 ss = sl;
103 }
104 else {
105 fputc('\\', fp);
106 fputc('\\', fp);
107 ss = sl;
108 sl = '\\';
109 goto skip1;
110 }
111 }
112 else {
113 fputc('\\', fp);
114 fputc('\\', fp);
115 if ((sl == '\n') || (sl == EOF))
116 goto skip2;
117 else
118 ss = sl;
119 }
120 }
121 else if (l_cnt)
122 fputc(sl, fp);
123 sl = ss;
124 l_cnt++;
125 }
126
127 fputc('\n', fp);
128 if (ss == EOF)
129 clearerr(inputt);
130 }
131
132
133 /*
134 * The global function. All global commands (g, G, v, and V) are handled
135 * in here. The lines to be affected by the command list are 1st noted
136 * and then the command list is invoked for each line matching the RE.
137 * Note the trick of how the command list is executed. Saves a lot of
138 * code (and allows for \n's in substitutions).
139 */
140 void
g(inputt,errnum)141 g(inputt, errnum)
142 FILE *inputt;
143 int *errnum;
144 {
145 static char *l_template_g;
146 char *l_patt;
147 static int l_template_flag = 0;
148 int l_re_success, l_flag_v = 0, l_err, l_num;
149 register l_gut_cnt, a;
150 register LINE **l_gut=gut;
151 FILE *l_fp;
152 #ifdef POSIX
153 LINE *l_posix_cur;
154 #endif
155
156 if (Start_default && End_default) {
157 Start = top;
158 End = bottom;
159 } else
160 if (Start_default)
161 Start = End;
162 if (Start == NULL) {
163 strcpy(help_msg, "buffer empty");
164 *errnum = -1;
165 return;
166 }
167
168 if (l_template_flag == 0) {
169 sigspecial++;
170 l_template_flag = 1;
171 l_template_g = calloc(FILENAME_LEN, sizeof(char));
172 sigspecial--;
173 if (sigint_flag && (!sigspecial))
174 SIGINT_ACTION;
175 if (l_template_g == NULL) {
176 *errnum = -1;
177 strcpy(help_msg, "out of memory error");
178 return;
179 }
180 }
181 /* set up the STDIO command list file */
182 memmove(l_template_g, "/tmp/_4.4bsd_ed_g_XXXXXX\0", 24);
183 mktemp(l_template_g);
184
185 if ((ss == 'v') || (ss == 'V'))
186 l_flag_v = 1;
187
188 if ((ss == 'G') || (ss == 'V')) {
189 /*
190 * If it's an interactive global command we use stdin, not a
191 * file.
192 */
193 GV_flag = 1;
194 l_fp = stdin;
195 } else {
196 sigspecial++;
197 if ((l_fp = fopen(l_template_g, "w+")) == NULL) {
198 perror("ed: file I/O error, save buffer in ed.hup");
199 do_hup(); /* does not return */
200 }
201 sigspecial--;
202 if (sigint_flag && (!sigspecial))
203 goto point;
204 }
205
206 ss = getc(inputt);
207
208 /* Get the RE for the global command. */
209 l_patt = get_pattern(ss, inputt, errnum, 0);
210
211 /* Instead of: if ((*errnum == -1) && (ss == '\n'))... */
212 if (*errnum < -1)
213 return;
214 *errnum = 0;
215 if ((l_patt[1] == '\0') && (RE_flag == 0)) {
216 *errnum = -1;
217 ungetc(ss, inputt);
218 return;
219 } else
220 if (l_patt[1] || (RE_patt == NULL)) {
221 sigspecial++;
222 free(RE_patt);
223 RE_patt = l_patt;
224 sigspecial--;
225 if (sigint_flag && (!sigspecial))
226 goto point;
227 }
228 RE_sol = (RE_patt[1] == '^') ? 1 : 0;
229 if ((RE_patt[1]) &&
230 (regfree(&RE_comp), l_err = regcomp(&RE_comp, &RE_patt[1], 0))) {
231 regerror(l_err, &RE_comp, help_msg, 128);
232 *errnum = -1;
233 RE_flag = 0;
234 ungetc(ss, inputt);
235 return;
236 }
237 RE_flag = 1;
238
239 if (GV_flag)
240 ss = getc(inputt);
241
242 #ifdef POSIX
243 l_posix_cur = current;
244 #endif
245 current = Start;
246
247 sigspecial++;
248
249 if ((l_num = line_number(bottom)) > gut_num) {
250 sigspecial++;
251 gut_num = l_num + 512;
252 free(l_gut);
253 gut = l_gut = malloc(sizeof(LINE **) * gut_num);
254 sigspecial--;
255 if (l_gut == NULL) {
256 *errnum = -1;
257 strcpy(help_msg, "out of memory error");
258 #ifdef POSIX
259 current = l_posix_cur;
260 #endif
261 ungetc('\n', inputt);
262 return;
263 }
264 }
265 l_gut_cnt = 0;
266
267 for (;;) {
268 /*
269 * Find the lines in the buffer that the global command wants
270 * to work with.
271 */
272 get_line(current->handle, current->len);
273 if (sigint_flag && (!sigspecial))
274 goto point;
275 l_re_success =
276 regexec(&RE_comp, text, (size_t) RE_SEC, RE_match, 0);
277 /* l_re_success=0 => success */
278 if ( (l_re_success == 0 && l_flag_v == 0) ||
279 (l_re_success && l_flag_v)) {
280 l_gut[l_gut_cnt++] = current;
281 }
282 if (End == current)
283 break;
284 current = current->below;
285 }
286 sigspecial--;
287 if (sigint_flag && (!sigspecial))
288 goto point;
289
290 if (l_gut_cnt == 0) {
291 strcpy(help_msg, "no matches found");
292 #ifdef POSIX
293 current = l_posix_cur;
294 #endif
295 return;
296 }
297 /* if non-interactive, get the command list */
298 if (GV_flag == 0) {
299 sigspecial++;
300 w_cmd_l_file(l_fp, inputt, errnum);
301 sigspecial--;
302 if (sigint_flag)
303 goto point;
304 }
305
306 if (g_flag == 0)
307 u_clr_stk();
308
309 sigspecial++;
310 for (a=0; a<l_gut_cnt; a++) {
311 /*
312 * Execute the command list on the lines that still exist that
313 * we indicated earlier that global wants to work with.
314 */
315 if (sigint_flag)
316 goto point;
317 if (GV_flag == 0)
318 fseek(l_fp, (off_t)0, 0);
319 if (find_line(l_gut[a])) {
320 current = (l_gut[a]);
321 get_line(current->handle, current->len);
322 if (sigint_flag)
323 goto point;
324 if (GV_flag == 1)
325 printf("%s\n", text);
326 g_flag++;
327 explain_flag--;
328 sigspecial--;
329 cmd_loop(l_fp, errnum);
330 sigspecial++;
331 explain_flag++;
332 g_flag--;
333 if ((GV_flag == 1) && (*errnum < 0)) {
334 ungetc('\n', l_fp);
335 break;
336 }
337 *errnum = 0;
338 }
339 }
340
341 point:
342 if (GV_flag == 0) {
343 fclose(l_fp);
344 unlink(l_template_g);
345 }
346 else
347 ungetc('\n', inputt);
348
349 GV_flag = 0;
350
351 #ifdef POSIX
352 current = l_posix_cur;
353 #endif
354 sigspecial--;
355 if (sigint_flag)
356 SIGINT_ACTION;
357 }
358