xref: /original-bsd/contrib/ed/g.c (revision cde01d6c)
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.6 (Berkeley) 03/18/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 static void
63 w_cmd_l_file(fp, inputt, errnum)
64 	FILE *fp, *inputt;
65 	int *errnum;
66 {
67 	int l_esc = 0, l_cnt = 0;
68 
69 	for (;;) {
70 		ss = getc(inputt);
71 		if (ss == '\\') {
72 			l_esc = 1;
73 			ss = getc(inputt);
74 		}
75 		if (((ss == '\n') && (l_esc == 0)) || (ss == EOF)) {
76 			/* if no command list default command list to 'p' */
77 			if (l_cnt == 0)
78 				fputc('p', fp);
79 			break;
80 		}
81 		l_esc = 0;
82 		fputc(ss, fp);
83 		l_cnt++;
84 	}
85 	if (ss == EOF)
86 		clearerr(inputt);
87 }
88 
89 /*
90  * The global function. All global commands (g, G, v, and V) are handled
91  * in here. The lines to be affected by the command list are 1st noted
92  * and then the command list is invoked for each line matching the RE.
93  * Note the trick of how the command list is executed. Saves a lot of
94  * code (and allows for \n's in substitutions).
95  */
96 void
97 g(inputt, errnum)
98 	FILE *inputt;
99 	int *errnum;
100 {
101 	struct g_list *l_Head, *l_Foot, *l_gut, *l_old;
102 	static char *l_template_g;
103 	char *l_patt;
104 	static int l_template_flag = 0;
105 	int l_re_success, l_flag_v = 0, l_err;
106 	FILE *l_fp;
107 #ifdef POSIX
108 	LINE *l_posix_cur;
109 #endif
110 
111 	if (Start_default && End_default) {
112 		Start = top;
113 		End = bottom;
114 	} else
115 		if (Start_default)
116 			Start = End;
117 	if (Start == NULL) {
118 		strcpy(help_msg, "buffer empty");
119 		*errnum = -1;
120 		return;
121 	}
122 
123 	if (l_template_flag == 0) {
124 		sigspecial++;
125 		l_template_flag = 1;
126 		l_template_g = calloc(FILENAME_LEN, sizeof(char));
127 		sigspecial--;
128 		if (sigint_flag && (!sigspecial))
129 			SIGINT_ACTION;
130 		if (l_template_g == NULL) {
131 			*errnum = -1;
132 			strcpy(help_msg, "out of memory error");
133 			return;
134 		}
135 	}
136 	/* set up the STDIO command list file */
137 	bcopy("/tmp/_4.4bsd_ed_g_XXXXXX\0", l_template_g, 24);
138 	mktemp(l_template_g);
139 
140 	l_Head = l_Foot = l_gut = l_old = NULL;
141 
142 	if ((ss == 'v') || (ss == 'V'))
143 		l_flag_v = 1;
144 
145 	if ((ss == 'G') || (ss == 'V')) {
146 		/*
147 		 * If it's an interactive global command we use stdin, not a
148 		 * file.
149 		 */
150 		GV_flag = 1;
151 		l_fp = stdin;
152 	} else {
153 		sigspecial++;
154 		if ((l_fp = fopen(l_template_g, "w+")) == NULL) {
155 			perror("ed: file I/O error, save buffer in ed.hup");
156 			do_hup(); /* does not return */
157 		}
158 		sigspecial--;
159 		if (sigint_flag && (!sigspecial))
160 			goto point;
161 	}
162 
163 	ss = getc(inputt);
164 
165 	/* Get the RE for the global command. */
166 	l_patt = get_pattern(ss, inputt, errnum, 0);
167 
168 	/* Instead of: if ((*errnum == -1) && (ss == '\n'))... */
169 	if (*errnum < -1)
170 		return;
171 	*errnum = 0;
172 	if ((l_patt[1] == '\0') && (RE_flag == 0)) {
173 		*errnum = -1;
174 		ungetc(ss, inputt);
175 		return;
176 	} else
177 		if (l_patt[1] || (RE_patt == NULL)) {
178 			sigspecial++;
179 			free(RE_patt);
180 			RE_patt = l_patt;
181 			sigspecial--;
182 			if (sigint_flag && (!sigspecial))
183 				goto point;
184 		}
185 	RE_sol = (RE_patt[1] == '^') ? 1 : 0;
186 	if ((RE_patt[1]) &&
187 	    (regfree(&RE_comp), l_err = regcomp(&RE_comp, &RE_patt[1], 0))) {
188 		regerror(l_err, &RE_comp, help_msg, 128);
189 		*errnum = -1;
190 		RE_flag = 0;
191 		ungetc(ss, inputt);
192 		return;
193 	}
194 	RE_flag = 1;
195 
196 	if (GV_flag)
197 		ss = getc(inputt);
198 
199 #ifdef POSIX
200 	l_posix_cur = current;
201 #endif
202 	current = Start;
203 
204 	sigspecial++;
205 
206 	for (;;) {
207 		/*
208 		 * Find the lines in the buffer that the global command wants
209 		 * to work with.
210 		 */
211 		if (sigint_flag && (!sigspecial))
212 			goto point;
213 		get_line(current->handle, current->len);
214 		if (sigint_flag && (!sigspecial))
215 			goto point;
216 		l_re_success =
217 		    regexec(&RE_comp, text, (size_t) RE_SEC, RE_match, 0);
218 		/* l_re_success=0 => success */
219 		if ((l_re_success != 0 && l_flag_v == 1) ||
220 		    (l_re_success == 0 && l_flag_v == 0)) {
221 			if (l_Head == NULL) {
222 				l_gut = malloc(sizeof(struct g_list));
223 				if (l_gut == NULL) {
224 					*errnum = -1;
225 					strcpy(help_msg, "out of memory error");
226 #ifdef POSIX
227 					current = l_posix_cur;
228 #endif
229 					return;
230 				}
231 				(l_gut->next) = NULL;
232 				(l_gut->cell) = current;
233 				l_Foot = l_Head = l_gut;
234 			} else {
235 				(l_gut->next) = malloc(sizeof(struct g_list));
236 				if ((l_gut->next) == NULL) {
237 					*errnum = -1;
238 					strcpy(help_msg, "out of memory error");
239 					goto clean;
240 				}
241 				l_gut = l_gut->next;
242 				(l_gut->cell) = current;
243 				(l_gut->next) = NULL;
244 				l_Foot = l_gut;
245 			}
246 		}
247 		if (End == current)
248 			break;
249 		current = current->below;
250 	}
251 	sigspecial--;
252 	if (sigint_flag && (!sigspecial))
253 		goto point;
254 
255 	if (l_Head == NULL) {
256 		strcpy(help_msg, "no matches found");
257 		*errnum = -1;
258 #ifdef POSIX
259 		current = l_posix_cur;
260 #endif
261 		return;
262 	}
263 	/* if non-interactive, get the command list */
264 	if (GV_flag == 0) {
265 		sigspecial++;
266 		w_cmd_l_file(l_fp, inputt, errnum);
267 		sigspecial--;
268 		if (sigint_flag && (!sigspecial))
269 			goto point;
270 	}
271 	l_gut = l_Head;
272 
273 	if (g_flag == 0)
274 		u_clr_stk();
275 
276 	sigspecial++;
277 	for (;;) {
278 		/*
279 		 * Execute the command list on the lines that still exist that
280 		 * we indicated earlier that global wants to work with.
281 		 */
282 		if (sigint_flag && (!sigspecial))
283 			goto point;
284 		if (GV_flag == 0)
285 			fseek(l_fp, (off_t)0, 0);
286 		if (find_line(l_gut->cell)) {
287 			current = (l_gut->cell);
288 			get_line(current->handle, current->len);
289 			if (sigint_flag && (!sigspecial))
290 				goto point;
291 			if (GV_flag == 1)
292 				printf("%s\n", text);
293 			g_flag++;
294 			explain_flag--;
295 			sigspecial--;
296 			cmd_loop(l_fp, errnum);
297 			sigspecial++;
298 			explain_flag++;
299 			g_flag--;
300 			if ((GV_flag == 1) && (*errnum < 0)) {
301 				ungetc('\n', l_fp);
302 				break;
303 			}
304 			*errnum = 0;
305 			if (l_gut == l_Foot)
306 				break;
307 			l_gut = l_gut->next;
308 		}
309 	}
310 
311 point:
312 	if (GV_flag == 0) {
313 		fclose(l_fp);
314 		unlink(l_template_g);
315 	}
316 	else
317 		ungetc('\n', inputt);
318 
319 	GV_flag = 0;
320 clean:
321 	/* clean up */
322 	l_gut = l_Head;
323 	while (1) {
324 		if (l_gut == NULL)
325 			break;
326 		l_old = l_gut;
327 		l_gut = l_gut->next;
328 		free(l_old);
329 	}
330 #ifdef POSIX
331 	current = l_posix_cur;
332 #endif
333 	sigspecial--;
334 	if (sigint_flag && (!sigspecial))
335 		SIGINT_ACTION;
336 }
337