xref: /csrg-svn/contrib/ed/sub.c (revision 57710)
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[] = "@(#)sub.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 <string.h>
23 
24 #include "ed.h"
25 #include "extern.h"
26 
27 /*
28  * The substitute command. It's big because of the backward compatability.
29  */
30 void
31 s(inputt, errnum)
32 	FILE *inputt;
33 	int *errnum;
34 {
35 	static int l_count2 = 1, l_global = 0, l_print = 0;
36 	static int l_first_pass_flag = 0;
37 	static char *l_match = NULL, *l_repl = NULL;
38 	LINE *l_s_ret, *l_temp_line, *l_temp_line2, *l_kval, *l_last;
39 	int l_s_flag, l_count, l_matched, l_nflag, l_cnt, yy, l_sr_flag = 0;
40 	int l_err, l_sl;
41 	char *l_match2 = NULL, *l_local = NULL, *l_local_temp = NULL;
42 #ifndef REG_STARTEND
43 	size_t l_offset = 0;
44 #endif
45 
46 	if (start_default && End_default)
47 		start = End = current;
48 	else
49 		if (start_default)
50 			start = End;
51 	if (start == NULL) {
52 		*errnum = -1;
53 		return;
54 	}
55 	start_default = End_default = 0;
56 
57 	l_sl = ss = getc(inputt);
58 	if (l_first_pass_flag == 0)
59 		l_match = l_repl = NULL;
60 	l_match2 = get_pattern(l_sl, inputt, errnum, 0);
61 	if (sigint_flag)
62 		SIGINT_ACTION;
63 	if (*errnum < 0) {
64 		if ((*errnum == -2) && (l_sl != '\n'))
65 			return;
66 		if ((l_match2 == NULL) ||
67 		    (strlen(l_match2) > 4) || (l_first_pass_flag == 0))
68 			return;
69 		*errnum = 0;
70 		l_sr_flag = -1;
71 		for (yy = 0; yy < (strlen(l_match2)); yy++) {
72 			switch (l_match2[yy]) {
73 			case '\n':
74 				ss = getc(inputt);
75 				goto bcg1;
76 				break;
77 			case 'r':
78 				l_sr_flag = 1;
79 				break;
80 			case 'p':
81 				l_print = (l_print) ? 0 : 1;
82 				break;
83 			case 'g':
84 				l_global = (l_global) ? 0 : 1;
85 				break;
86 			case 'N':
87 				l_count2 = 1;
88 				break;
89 			default:
90 				*errnum = -1;
91 				strcpy(help_msg, "illegal modifier to s");
92 				return;
93 			}
94 		}
95 		ss = getc(inputt);
96 		if (l_sr_flag == 1)
97 			goto bcg2;
98 		else
99 			goto bcg1;
100 	}
101 	if (l_first_pass_flag) {
102 		free(l_match);
103 		free(l_repl);
104 	} else
105 		l_first_pass_flag = 1;
106 	l_match = l_match2;
107 	*errnum = 0;
108 	l_repl = get_pattern(ss, inputt, errnum, 1);
109 	if (sigint_flag)
110 		SIGINT_ACTION;
111 	l_global = l_print = 0;
112 	if (*errnum < 0)
113 		if ((*errnum == -1) && (ss == '\n'))
114 			/* Note, \n still in stream for next getc. */
115 			l_print = 1;
116 		else
117 			return;
118 	*errnum = 0;
119 
120 	l_count2 = 1;
121 
122 	while (((ss = getc(inputt)) != '\n') && (ss != EOF))
123 		if (ss == 'g')
124 			l_global = 1;
125 		else
126 			switch (ss) {
127 			case 'p':
128 				l_print = (l_print | (int) 1);
129 				break;
130 			case 'n':
131 				l_print = (l_print | (int) 2);
132 				break;
133 			case 'l':
134 				l_print = (l_print | (int) 4);
135 				break;
136 			default:
137 				if ((ss > ('0' - 1)) && (ss < ('9' + 1)))
138 					l_count2 = dig_num_conv(inputt, errnum);
139 				else {
140 					*errnum = -1;
141 					strcpy(help_msg,
142 					    "illegal command option");
143 					return;
144 				}
145 		}
146 
147 bcg1:
148 	if ((RE_flag == 0) && (l_match[1] == '\0')) {
149 		*errnum = -1;
150 		ungetc(ss, inputt);
151 		return;
152 	} else
153 		if ((l_sr_flag == 0) && (l_match[1] || (RE_patt == NULL))) {
154 			free(RE_patt);
155 			RE_patt = malloc(sizeof(char) * (2 + strlen(l_match)));
156 			bcopy(l_match, RE_patt, strlen(l_match + 1));
157 		}
158 	RE_sol = (l_match[1] == '^') ? 1 : 0;
159 	if ((l_match[1]) &&
160 	    (regfree(&RE_comp), l_err = regcomp(&RE_comp, &l_match[1], 0))) {
161 		regerror(l_err, &RE_comp, help_msg, 128);
162 		*errnum = -1;
163 		RE_flag = 0;
164 		ungetc(ss, inputt);
165 		return;
166 	}
167 	RE_flag = 1;
168 	if (sigint_flag)
169 		SIGINT_ACTION;
170 bcg2:
171 	current = start;
172 	l_s_flag = 0;
173 	do {
174 		if (sigint_flag)
175 			SIGINT_ACTION;
176 		RE_match[0].rm_eo = 0;
177 		get_line(current->handle, current->len);
178 		l_count = l_count2;
179 		l_local = text;
180 #ifndef REG_STARTEND
181 		l_offset = 0;
182 #endif
183 		do {
184 			RE_match[0].rm_so = RE_match[0].rm_eo;
185 #ifdef REG_STARTEND
186 			l_matched = regexec_n(&RE_comp, l_local,
187 			    (size_t)RE_SEC, RE_match, 0, l_count,
188 			    (size_t)current->len, 0);
189 #else
190 			l_matched = regexec_n(&RE_comp, l_local,
191 			    (size_t)RE_SEC, RE_match, 0, l_count,
192 			    &l_offset, 0);
193 #endif
194 			if (l_matched == 0) {
195 				if ((l_s_flag == 0) && (g_flag == 0))
196 					u_clr_stk();
197 				l_count = l_s_flag = 1;
198 				/*
199 				 * The l_local passed into re_replace is not
200 				 * freed in re_replace because it is "text",
201 				 * the global line holder, for the first pass
202 				 * through this loop. The value returned by
203 				 * re_replace is a new string (with the first
204 				 * replacement in it). If the 'g' flag was
205 				 * set with substitute then this new string
206 				 * is passed in for the second pass and can
207 				 * be freed once re_replace is done with it.
208 				 * (...and so on for the rest of the 'g'
209 				 * passes. RE_match[0].rm_eo is changed in
210 				 * re_replace to be the new location of the
211 				 * next character immediately after the
212 				 * replacement since it is likely the
213 				 * position of that character has changed
214 				 * because of the replacement.
215 				 */
216 #ifdef REG_STARTEND
217 				l_local = re_replace(l_local,
218 				    (size_t)(RE_SEC - 1), RE_match, &l_repl[1]);
219 #else
220 				l_local = re_replace(l_local,
221 				    (size_t)(RE_SEC - 1), RE_match, &l_repl[1],
222 				    l_offset);
223 #endif
224 			}
225 			if (l_global == 0)
226 				break;
227 			if (l_local[RE_match[0].rm_eo] == '\0')
228 				break;
229 		} while (!l_matched);
230 
231 		l_cnt = l_nflag = 0;
232 		l_kval = current;
233 		l_temp_line = current->above;
234 		l_temp_line2 = current->below;
235 		l_local_temp = l_local;
236 		for (;;) {
237 			/*
238 			 * Make the new string the one for this line.  Check if
239 			 * it needs to be split.
240 			 */
241 			if (l_local[l_cnt] == '\n' || l_local[l_cnt] == '\0') {
242 				if (l_local[l_cnt] == '\0')
243 					l_nflag = 1;
244 				l_local[l_cnt] = '\0';
245 				l_s_ret = malloc(sizeof(LINE));
246 				if (l_s_ret == NULL) {
247 					*errnum = -1;
248 					strcpy(help_msg, "out of memory error");
249 					return;
250 				}
251 				(l_s_ret->len) = strlen(l_local);
252 				(l_s_ret->handle) = add_line(l_local, l_s_ret->len);
253 				(l_s_ret->above) = l_temp_line;
254 				(l_s_ret->below) = NULL;
255 				if (l_temp_line == NULL)
256 					top = l_s_ret;
257 				else {
258 					u_add_stk(&(l_temp_line->below));
259 					(l_temp_line->below) = l_s_ret;
260 				}
261 				l_temp_line = l_s_ret;
262 				if ((l_local[l_cnt] == '\0') && (l_nflag == 1))
263 					break;
264 				else {
265 					l_local = &(l_local[l_cnt + 1]);
266 					l_cnt = 0;
267 				}
268 			} else
269 				l_cnt++;
270 		}
271 		(l_s_ret->below) = l_temp_line2;
272 		ku_chk(current, current, l_kval->below);
273 		if (current == End)
274 			End = l_s_ret;
275 		current = l_s_ret;
276 		l_last = current;
277 		if (l_temp_line2 == NULL)
278 			bottom = l_s_ret;
279 		else {
280 			u_add_stk(&(l_temp_line2->above));
281 			(l_temp_line2->above) = current;
282 		}
283 		if (l_local_temp != text)
284 			free(l_local_temp);
285 		current = current->below;
286 	} while (current != (End->below));
287 
288 	if (l_s_flag == 0) {
289 		current = start;
290 		strcpy(help_msg, "no matches found for substitution");
291 		*errnum = -1;
292 		ungetc('\n', inputt);
293 		return;
294 	}
295 	change_flag = 1;
296 	current = l_last;
297 
298 	if (l_print > 0) {
299 		start = End = current;
300 		ungetc(ss, inputt);
301 		if (l_print == (l_print | (int) 1))
302 			p(inputt, errnum, 0);
303 		if (l_print == (l_print | (int) 2))
304 			p(inputt, errnum, 1);
305 		if (l_print == (l_print | (int) 4))
306 			l(inputt, errnum);
307 		if (*errnum < 0)
308 			return;
309 	}
310 	if (l_sr_flag == -1) {
311 		regfree(&RE_comp);
312 		regcomp(&RE_comp, &RE_patt[1], 0);
313 	}
314 	*errnum = 1;
315 }
316