xref: /original-bsd/contrib/ed/g.c (revision 97bd5884)
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.2 (Berkeley) 01/23/93";
13 #endif /* not lint */
14 
15 #include <sys/types.h>
16 
17 #include <db.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 #include "ed.h"
27 #include "extern.h"
28 
29 static int	find_line __P((LINE *));
30 static void	w_cmd_l_file __P((FILE *, FILE *, int *));
31 
32 /*
33  * Find a line that we noted matched the RE earlier in the current
34  * buffer (it may have disappeared because of the commands in the
35  * command list).
36  */
37 int
38 find_line(dot)
39 	LINE *dot;
40 {
41 	LINE *l_cl;
42 
43 	l_cl = top;
44 	for (;;) {
45 		if (l_cl == dot)
46 			return (1);
47 		if (l_cl == bottom)
48 			return (0);
49 		l_cl = l_cl->below;
50 	}
51 }
52 
53 /*
54  * Write the command line to a STDIO tmp file. See g() below.
55  * This allows us to use cmd_loop to run the command list because
56  * we "trick" cmd_loop into reading a STDIO file instead of stdin.
57  */
58 void
59 w_cmd_l_file(fp, inputt, errnum)
60 	FILE *fp, *inputt;
61 	int *errnum;
62 {
63 	int l_esc = 0, l_cnt = 0;
64 
65 	for (;;) {
66 		ss = getc(inputt);
67 		if (ss == '\\') {
68 			l_esc = 1;
69 			ss = getc(inputt);
70 		}
71 		if (((ss == '\n') && (l_esc == 0)) || (ss == EOF)) {
72 			/* if no command list default command list to 'p' */
73 			if (l_cnt == 0)
74 				fputc('p', fp);
75 			break;
76 		}
77 		l_esc = 0;
78 		fputc(ss, fp);
79 		l_cnt++;
80 	}
81 	if (ss == EOF)
82 		clearerr(inputt);
83 }
84 
85 /*
86  * The global function. All global commands (g, G, v, and V) are handled
87  * in here. The lines to be affected by the command list are 1st noted
88  * and then the command list is invoked for each line matching the RE.
89  * Note the trick of how the command list is executed. Saves a lot of
90  * code (and allows for \n's in substitutions).
91  */
92 void
93 g(inputt, errnum)
94 	FILE *inputt;
95 	int *errnum;
96 {
97 	struct g_list *l_Head, *l_Foot, *l_gut, *l_old;
98 	static char *l_template_g;
99 	char *l_patt;
100 	static int l_template_flag = 0;
101 	int l_re_success, l_flag_v = 0, l_err;
102 	FILE *l_fp;
103 #ifdef POSIX
104 	LINE *l_posix_cur;
105 #endif
106 
107 	if (start_default && End_default) {
108 		start = top;
109 		End = bottom;
110 	} else
111 		if (start_default)
112 			start = End;
113 	if (start == NULL) {
114 		strcpy(help_msg, "bad address");
115 		*errnum = -1;
116 		return;
117 	}
118 	if (sigint_flag)
119 		SIGINT_ACTION;
120 
121 	if (l_template_flag == 0) {
122 		l_template_flag = 1;
123 		l_template_g = calloc(FILENAME_LEN, sizeof(char));
124 		if (l_template_g == NULL) {
125 			*errnum = -1;
126 			strcpy(help_msg, "out of memory error");
127 			return;
128 		}
129 	}
130 	/* set up the STDIO command list file */
131 	bcopy("/tmp/_4.4bsd_ed_g_XXXXXX\0", l_template_g, 24);
132 	mktemp(l_template_g);
133 
134 	l_Head = l_Foot = l_gut = l_old = NULL;
135 
136 	if ((ss == 'v') || (ss == 'V'))
137 		l_flag_v = 1;
138 
139 	if ((ss == 'G') || (ss == 'V')) {
140 		/*
141 		 * If it's an interactive global command we use stdin, not a
142 		 * file.
143 		 */
144 		GV_flag = 1;
145 		l_fp = stdin;
146 	} else {
147 		if ((l_fp = fopen(l_template_g, "w+")) == NULL) {
148 			perror("ed: file I/O error");
149 			exit(1);
150 		}
151 	}
152 
153 	ss = getc(inputt);
154 
155 	/* Get the RE for the global command. */
156 	l_patt = get_pattern(ss, inputt, errnum, 0);
157 	if (sigint_flag)
158 		SIGINT_ACTION;
159 	/* Instead of: if ((*errnum == -1) && (ss == '\n'))... */
160 	if (*errnum < -1)
161 		return;
162 	*errnum = 0;
163 	if ((l_patt[1] == '\0') && (RE_flag == 0)) {
164 		*errnum = -1;
165 		ungetc(ss, inputt);
166 		return;
167 	} else
168 		if (l_patt[1] || (RE_patt == NULL)) {
169 			free(RE_patt);
170 			RE_patt = l_patt;
171 		}
172 	RE_sol = (RE_patt[1] == '^') ? 1 : 0;
173 	if ((RE_patt[1]) &&
174 	    (regfree(&RE_comp), l_err = regcomp(&RE_comp, &RE_patt[1], 0))) {
175 		regerror(l_err, &RE_comp, help_msg, 128);
176 		*errnum = -1;
177 		RE_flag = 0;
178 		ungetc(ss, inputt);
179 		return;
180 	}
181 	RE_flag = 1;
182 	if (sigint_flag)
183 		SIGINT_ACTION;
184 #ifdef POSIX
185 	l_posix_cur = current;
186 #endif
187 	current = start;
188 
189 	for (;;) {
190 		/*
191 		 * Find the lines in the buffer that the global command wants
192 		 * to work with.
193 		 */
194 		if (sigint_flag)
195 			goto point;
196 		get_line(current->handle, current->len);
197 		l_re_success =
198 		    regexec(&RE_comp, text, (size_t) RE_SEC, RE_match, 0);
199 		/* l_re_success=0 => success */
200 		if ((l_re_success != 0 && l_flag_v == 1) ||
201 		    (l_re_success == 0 && l_flag_v == 0)) {
202 			if (l_Head == NULL) {
203 				l_gut = malloc(sizeof(struct g_list));
204 				if (l_gut == NULL) {
205 					*errnum = -1;
206 					strcpy(help_msg, "out of memory error");
207 #ifdef POSIX
208 					current = l_posix_cur;
209 #endif
210 					return;
211 				}
212 				(l_gut->next) = NULL;
213 				(l_gut->cell) = current;
214 				l_Foot = l_Head = l_gut;
215 			} else {
216 				(l_gut->next) = malloc(sizeof(struct g_list));
217 				if ((l_gut->next) == NULL) {
218 					*errnum = -1;
219 					strcpy(help_msg, "out of memory error");
220 					goto clean;
221 				}
222 				l_gut = l_gut->next;
223 				(l_gut->cell) = current;
224 				(l_gut->next) = NULL;
225 				l_Foot = l_gut;
226 			}
227 		}
228 		if (End == current)
229 			break;
230 		current = current->below;
231 	}
232 
233 	if (l_Head == NULL) {
234 		strcpy(help_msg, "no matches found");
235 		*errnum = -1;
236 #ifdef POSIX
237 		current = l_posix_cur;
238 #endif
239 		return;
240 	}
241 	/* if non-interactive, get the command list */
242 	if (GV_flag == 0)
243 		w_cmd_l_file(l_fp, inputt, errnum);
244 	l_gut = l_Head;
245 
246 	if (g_flag == 0)
247 		u_clr_stk();
248 
249 	for (;;) {
250 		/*
251 		 * Execute the command list on the lines that still exist that
252 		 * we indicated earlier that global wants to work with.
253 		 */
254 		if (sigint_flag)
255 			goto point;
256 		if (GV_flag == 0)
257 			fseek(l_fp, (off_t)0, 0);
258 		if (find_line(l_gut->cell)) {
259 			current = (l_gut->cell);
260 			get_line(current->handle, current->len);
261 			if (GV_flag == 1)
262 				printf("%s\n", text);
263 			g_flag++;
264 			explain_flag--;
265 			cmd_loop(l_fp, errnum);
266 			explain_flag++;
267 			g_flag--;
268 			if ((GV_flag == 1) && (*errnum < 0)) {
269 				ungetc('\n', l_fp);
270 				break;
271 			}
272 			*errnum = 0;
273 			if (l_gut == l_Foot)
274 				break;
275 			l_gut = l_gut->next;
276 		}
277 	}
278 
279 point:
280 	if (GV_flag == 0) {
281 		fclose(l_fp);
282 		unlink(l_template_g);
283 	}
284 	GV_flag = 0;
285 clean:
286 	/* clean up */
287 	l_gut = l_Head;
288 	while (1) {
289 		if (l_gut == NULL)
290 			break;
291 		l_old = l_gut;
292 		l_gut = l_gut->next;
293 		free(l_old);
294 	}
295 #ifdef POSIX
296 	current = l_posix_cur;
297 #endif
298 }
299