xref: /original-bsd/contrib/ed/g.c (revision 05990f4a)
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.9 (Berkeley) 04/30/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, l_ohm[130];
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 	strcpy(l_ohm, help_msg);
247 
248 	sigspecial++;
249 
250 	if ((l_num = line_number(bottom)) > gut_num) {
251 		sigspecial++;
252 		gut_num = l_num + 512;
253 		free(l_gut);
254 		gut = l_gut = malloc(sizeof(LINE **) * gut_num);
255 		sigspecial--;
256 		if (l_gut == NULL) {
257 			*errnum = -1;
258 			strcpy(help_msg, "out of memory error");
259 #ifdef POSIX
260 			current = l_posix_cur;
261 #endif
262 			ungetc('\n', inputt);
263 			return;
264 		}
265 	}
266 	l_gut_cnt = 0;
267 
268 	for (;;) {
269 		/*
270 		 * Find the lines in the buffer that the global command wants
271 		 * to work with.
272 		 */
273 		get_line(current->handle, current->len);
274 		if (sigint_flag && (!sigspecial))
275 			goto point;
276 		l_re_success =
277 		    regexec(&RE_comp, text, (size_t) RE_SEC, RE_match, 0);
278 		/* l_re_success=0 => success */
279 		if ( (l_re_success == 0 && l_flag_v == 0) ||
280 			(l_re_success && l_flag_v)) {
281 				l_gut[l_gut_cnt++] = current;
282 		}
283 		if (End == current)
284 			break;
285 		current = current->below;
286 	}
287 	sigspecial--;
288 	if (sigint_flag && (!sigspecial))
289 		goto point;
290 
291 	if (l_gut_cnt == 0) {
292 		strcpy(help_msg, "no matches found");
293 #ifdef POSIX
294 		current = l_posix_cur;
295 #endif
296 		return;
297 	}
298 	/* if non-interactive, get the command list */
299 	if (GV_flag == 0) {
300 		sigspecial++;
301 		w_cmd_l_file(l_fp, inputt, errnum);
302 		sigspecial--;
303 		if (sigint_flag)
304 			goto point;
305 	}
306 
307 	if (g_flag == 0)
308 		u_clr_stk();
309 
310 	sigspecial++;
311 	for (a=0; a<l_gut_cnt; a++) {
312 		/*
313 		 * Execute the command list on the lines that still exist that
314 		 * we indicated earlier that global wants to work with.
315 		 */
316 		if (sigint_flag)
317 			goto point;
318 		if (GV_flag == 0)
319 			fseek(l_fp, (off_t)0, 0);
320 		if (find_line(l_gut[a])) {
321 			current = (l_gut[a]);
322 			get_line(current->handle, current->len);
323 			if (sigint_flag)
324 				goto point;
325 			if (GV_flag == 1)
326 				printf("%s\n", text);
327 			g_flag++;
328 			explain_flag--;
329 			sigspecial--;
330 			cmd_loop(l_fp, errnum);
331 			sigspecial++;
332 			explain_flag++;
333 			g_flag--;
334 			if ((GV_flag == 1) && (*errnum < 0)) {
335 				ungetc('\n', l_fp);
336 				break;
337 			}
338 			*errnum = 0;
339 		}
340 	}
341 
342 point:
343 	strcpy(help_msg, l_ohm);
344 	if (GV_flag == 0) {
345 		fclose(l_fp);
346 		unlink(l_template_g);
347 	}
348 	else
349 		ungetc('\n', inputt);
350 
351 	GV_flag = 0;
352 
353 #ifdef POSIX
354 	current = l_posix_cur;
355 #endif
356 	sigspecial--;
357 	if (sigint_flag)
358 		SIGINT_ACTION;
359 }
360