xref: /original-bsd/contrib/ed/g.c (revision be48c784)
1 /*-
2  * Copyright (c) 1992 The Regents of the University of California.
3  * 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	5.10 (Berkeley) 05/11/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
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
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
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