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