xref: /original-bsd/contrib/ed/g.c (revision 515cb0e3)
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.8 (Berkeley) 04/28/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 	if (ss == EOF)
127 		clearerr(inputt);
128 }
129 
130 
131 /*
132  * The global function. All global commands (g, G, v, and V) are handled
133  * in here. The lines to be affected by the command list are 1st noted
134  * and then the command list is invoked for each line matching the RE.
135  * Note the trick of how the command list is executed. Saves a lot of
136  * code (and allows for \n's in substitutions).
137  */
138 void
139 g(inputt, errnum)
140 	FILE *inputt;
141 	int *errnum;
142 {
143 	static char *l_template_g;
144 	char *l_patt;
145 	static int l_template_flag = 0;
146 	int l_re_success, l_flag_v = 0, l_err, l_num;
147 	register l_gut_cnt, a;
148 	register LINE **l_gut=gut;
149 	FILE *l_fp;
150 #ifdef POSIX
151 	LINE *l_posix_cur;
152 #endif
153 
154 	if (Start_default && End_default) {
155 		Start = top;
156 		End = bottom;
157 	} else
158 		if (Start_default)
159 			Start = End;
160 	if (Start == NULL) {
161 		strcpy(help_msg, "buffer empty");
162 		*errnum = -1;
163 		return;
164 	}
165 
166 	if (l_template_flag == 0) {
167 		sigspecial++;
168 		l_template_flag = 1;
169 		l_template_g = calloc(FILENAME_LEN, sizeof(char));
170 		sigspecial--;
171 		if (sigint_flag && (!sigspecial))
172 			SIGINT_ACTION;
173 		if (l_template_g == NULL) {
174 			*errnum = -1;
175 			strcpy(help_msg, "out of memory error");
176 			return;
177 		}
178 	}
179 	/* set up the STDIO command list file */
180 	memmove(l_template_g, "/tmp/_4.4bsd_ed_g_XXXXXX\0", 24);
181 	mktemp(l_template_g);
182 
183 	if ((ss == 'v') || (ss == 'V'))
184 		l_flag_v = 1;
185 
186 	if ((ss == 'G') || (ss == 'V')) {
187 		/*
188 		 * If it's an interactive global command we use stdin, not a
189 		 * file.
190 		 */
191 		GV_flag = 1;
192 		l_fp = stdin;
193 	} else {
194 		sigspecial++;
195 		if ((l_fp = fopen(l_template_g, "w+")) == NULL) {
196 			perror("ed: file I/O error, save buffer in ed.hup");
197 			do_hup(); /* does not return */
198 		}
199 		sigspecial--;
200 		if (sigint_flag && (!sigspecial))
201 			goto point;
202 	}
203 
204 	ss = getc(inputt);
205 
206 	/* Get the RE for the global command. */
207 	l_patt = get_pattern(ss, inputt, errnum, 0);
208 
209 	/* Instead of: if ((*errnum == -1) && (ss == '\n'))... */
210 	if (*errnum < -1)
211 		return;
212 	*errnum = 0;
213 	if ((l_patt[1] == '\0') && (RE_flag == 0)) {
214 		*errnum = -1;
215 		ungetc(ss, inputt);
216 		return;
217 	} else
218 		if (l_patt[1] || (RE_patt == NULL)) {
219 			sigspecial++;
220 			free(RE_patt);
221 			RE_patt = l_patt;
222 			sigspecial--;
223 			if (sigint_flag && (!sigspecial))
224 				goto point;
225 		}
226 	RE_sol = (RE_patt[1] == '^') ? 1 : 0;
227 	if ((RE_patt[1]) &&
228 	    (regfree(&RE_comp), l_err = regcomp(&RE_comp, &RE_patt[1], 0))) {
229 		regerror(l_err, &RE_comp, help_msg, 128);
230 		*errnum = -1;
231 		RE_flag = 0;
232 		ungetc(ss, inputt);
233 		return;
234 	}
235 	RE_flag = 1;
236 
237 	if (GV_flag)
238 		ss = getc(inputt);
239 
240 #ifdef POSIX
241 	l_posix_cur = current;
242 #endif
243 	current = Start;
244 
245 	sigspecial++;
246 
247 	if ((l_num = line_number(bottom)) > gut_num) {
248 		sigspecial++;
249 		gut_num = l_num + 512;
250 		free(l_gut);
251 		l_gut = malloc(sizeof(LINE **) * gut_num);
252 		sigspecial--;
253 		if (l_gut == NULL) {
254 			*errnum = -1;
255 			strcpy(help_msg, "out of memory error");
256 #ifdef POSIX
257 			current = l_posix_cur;
258 #endif
259 			return;
260 		}
261 	}
262 	l_gut_cnt = 0;
263 
264 	for (;;) {
265 		/*
266 		 * Find the lines in the buffer that the global command wants
267 		 * to work with.
268 		 */
269 		get_line(current->handle, current->len);
270 		if (sigint_flag && (!sigspecial))
271 			goto point;
272 		l_re_success =
273 		    regexec(&RE_comp, text, (size_t) RE_SEC, RE_match, 0);
274 		/* l_re_success=0 => success */
275 		if ( (l_re_success == 0 && l_flag_v == 0) ||
276 			(l_re_success && l_flag_v)) {
277 				l_gut[l_gut_cnt++] = current;
278 		}
279 		if (End == current)
280 			break;
281 		current = current->below;
282 	}
283 	sigspecial--;
284 	if (sigint_flag && (!sigspecial))
285 		goto point;
286 
287 	if (l_gut_cnt == 0) {
288 		strcpy(help_msg, "no matches found");
289 #ifdef POSIX
290 		current = l_posix_cur;
291 #endif
292 		return;
293 	}
294 	/* if non-interactive, get the command list */
295 	if (GV_flag == 0) {
296 		sigspecial++;
297 		w_cmd_l_file(l_fp, inputt, errnum);
298 		sigspecial--;
299 		if (sigint_flag)
300 			goto point;
301 	}
302 
303 	if (g_flag == 0)
304 		u_clr_stk();
305 
306 	sigspecial++;
307 	for (a=0; a<l_gut_cnt; a++) {
308 		/*
309 		 * Execute the command list on the lines that still exist that
310 		 * we indicated earlier that global wants to work with.
311 		 */
312 		if (sigint_flag)
313 			goto point;
314 		if (GV_flag == 0)
315 			fseek(l_fp, (off_t)0, 0);
316 		if (find_line(l_gut[a])) {
317 			current = (l_gut[a]);
318 			get_line(current->handle, current->len);
319 			if (sigint_flag)
320 				goto point;
321 			if (GV_flag == 1)
322 				printf("%s\n", text);
323 			g_flag++;
324 			explain_flag--;
325 			sigspecial--;
326 			cmd_loop(l_fp, errnum);
327 			sigspecial++;
328 			explain_flag++;
329 			g_flag--;
330 			if ((GV_flag == 1) && (*errnum < 0)) {
331 				ungetc('\n', l_fp);
332 				break;
333 			}
334 			*errnum = 0;
335 		}
336 	}
337 
338 point:
339 	if (GV_flag == 0) {
340 		fclose(l_fp);
341 		unlink(l_template_g);
342 	}
343 	else
344 		ungetc('\n', inputt);
345 
346 	GV_flag = 0;
347 
348 #ifdef POSIX
349 	current = l_posix_cur;
350 #endif
351 	sigspecial--;
352 	if (sigint_flag)
353 		SIGINT_ACTION;
354 }
355