1/* sub.c: This file contains the substitution routines for the ed
2   line editor */
3/*-
4 * Copyright (c) 1993 Andrew Moore, Talke Studio.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30#include "ed.h"
31
32
33static char *rhbuf;		/* rhs substitution buffer */
34static int rhbufsz;		/* rhs substitution buffer size */
35static int rhbufi;		/* rhs substitution buffer index */
36
37/* extract_subst_tail: extract substitution tail from the command buffer */
38int
39extract_subst_tail(int *flagp, long *np)
40{
41	char delimiter;
42
43	*flagp = *np = 0;
44	if ((delimiter = *ibufp) == '\n') {
45		rhbufi = 0;
46		*flagp = GPR;
47		return 0;
48	} else if (extract_subst_template() == NULL)
49		return  ERR;
50	else if (*ibufp == '\n') {
51		*flagp = GPR;
52		return 0;
53	} else if (*ibufp == delimiter)
54		ibufp++;
55	if ('1' <= *ibufp && *ibufp <= '9') {
56		STRTOL(*np, ibufp);
57		return 0;
58	} else if (*ibufp == 'g') {
59		ibufp++;
60		*flagp = GSG;
61		return 0;
62	}
63	return 0;
64}
65
66
67/* extract_subst_template: return pointer to copy of substitution template
68   in the command buffer */
69char *
70extract_subst_template(void)
71{
72	int n = 0;
73	int i = 0;
74	char c;
75	char delimiter = *ibufp++;
76
77	if (*ibufp == '%' && *(ibufp + 1) == delimiter) {
78		ibufp++;
79		if (!rhbuf)
80			errmsg = "no previous substitution";
81		return rhbuf;
82	}
83	while (*ibufp != delimiter) {
84		REALLOC(rhbuf, rhbufsz, i + 2, NULL);
85		if ((c = rhbuf[i++] = *ibufp++) == '\n' && *ibufp == '\0') {
86			i--, ibufp--;
87			break;
88		} else if (c != '\\')
89			;
90		else if ((rhbuf[i++] = *ibufp++) != '\n')
91			;
92		else if (!isglobal) {
93			while ((n = get_tty_line()) == 0 ||
94			    (n > 0 && ibuf[n - 1] != '\n'))
95				clearerr(stdin);
96			if (n < 0)
97				return NULL;
98		}
99	}
100	REALLOC(rhbuf, rhbufsz, i + 1, NULL);
101	rhbuf[rhbufi = i] = '\0';
102	return  rhbuf;
103}
104
105
106static char *rbuf;		/* substitute_matching_text buffer */
107static int rbufsz;		/* substitute_matching_text buffer size */
108
109/* search_and_replace: for each line in a range, change text matching a pattern
110   according to a substitution template; return status  */
111int
112search_and_replace(pattern_t *pat, int gflag, int kth)
113{
114	undo_t *up;
115	const char *txt;
116	const char *eot;
117	long lc;
118	long xa = current_addr;
119	int nsubs = 0;
120	line_t *lp;
121	int len;
122
123	current_addr = first_addr - 1;
124	for (lc = 0; lc <= second_addr - first_addr; lc++) {
125		lp = get_addressed_line_node(++current_addr);
126		if ((len = substitute_matching_text(pat, lp, gflag, kth)) < 0)
127			return ERR;
128		else if (len) {
129			up = NULL;
130			if (delete_lines(current_addr, current_addr) < 0)
131				return ERR;
132			txt = rbuf;
133			eot = rbuf + len;
134			SPL1();
135			do {
136				if ((txt = put_sbuf_line(txt)) == NULL) {
137					SPL0();
138					return ERR;
139				} else if (up)
140					up->t = get_addressed_line_node(current_addr);
141				else if ((up = push_undo_stack(UADD,
142				    current_addr, current_addr)) == NULL) {
143					SPL0();
144					return ERR;
145				}
146			} while (txt != eot);
147			SPL0();
148			nsubs++;
149			xa = current_addr;
150		}
151	}
152	current_addr = xa;
153	if  (nsubs == 0 && !(gflag & GLB)) {
154		errmsg = "no match";
155		return ERR;
156	} else if ((gflag & (GPR | GLS | GNP)) &&
157	    display_lines(current_addr, current_addr, gflag) < 0)
158		return ERR;
159	return 0;
160}
161
162
163/* substitute_matching_text: replace text matched by a pattern according to
164   a substitution template; return pointer to the modified text */
165int
166substitute_matching_text(pattern_t *pat, line_t *lp, int gflag, int kth)
167{
168	int off = 0;
169	int changed = 0;
170	int matchno = 0;
171	int i = 0;
172	regmatch_t rm[SE_MAX];
173	char *txt;
174	char *eot;
175
176	if ((txt = get_sbuf_line(lp)) == NULL)
177		return ERR;
178	if (isbinary)
179		NUL_TO_NEWLINE(txt, lp->len);
180	eot = txt + lp->len;
181	if (!regexec(pat, txt, SE_MAX, rm, 0)) {
182		do {
183			if (!kth || kth == ++matchno) {
184				changed++;
185				i = rm[0].rm_so;
186				REALLOC(rbuf, rbufsz, off + i, ERR);
187				if (isbinary)
188					NEWLINE_TO_NUL(txt, rm[0].rm_eo);
189				memcpy(rbuf + off, txt, i);
190				off += i;
191				if ((off = apply_subst_template(txt, rm, off,
192				    pat->re_nsub)) < 0)
193					return ERR;
194			} else {
195				i = rm[0].rm_eo;
196				REALLOC(rbuf, rbufsz, off + i, ERR);
197				if (isbinary)
198					NEWLINE_TO_NUL(txt, i);
199				memcpy(rbuf + off, txt, i);
200				off += i;
201			}
202			txt += rm[0].rm_eo;
203		} while (*txt &&
204                        (!changed || ((gflag & GSG) && rm[0].rm_eo)) &&
205		        !regexec(pat, txt, SE_MAX, rm, REG_NOTBOL));
206		i = eot - txt;
207		REALLOC(rbuf, rbufsz, off + i + 2, ERR);
208		if (i > 0 && !rm[0].rm_eo && (gflag & GSG)) {
209			errmsg = "infinite substitution loop";
210			return  ERR;
211		}
212		if (isbinary)
213			NEWLINE_TO_NUL(txt, i);
214		memcpy(rbuf + off, txt, i);
215		memcpy(rbuf + off + i, "\n", 2);
216	}
217	return changed ? off + i + 1 : 0;
218}
219
220
221/* apply_subst_template: modify text according to a substitution template;
222   return offset to end of modified text */
223int
224apply_subst_template(const char *boln, regmatch_t *rm, int off, int re_nsub)
225{
226	int j = 0;
227	int k = 0;
228	int n;
229	char *sub = rhbuf;
230
231	for (; sub - rhbuf < rhbufi; sub++)
232		if (*sub == '&') {
233			j = rm[0].rm_so;
234			k = rm[0].rm_eo;
235			REALLOC(rbuf, rbufsz, off + k - j, ERR);
236			while (j < k)
237				rbuf[off++] = boln[j++];
238		} else if (*sub == '\\' && '1' <= *++sub && *sub <= '9' &&
239		    (n = *sub - '0') <= re_nsub) {
240			j = rm[n].rm_so;
241			k = rm[n].rm_eo;
242			REALLOC(rbuf, rbufsz, off + k - j, ERR);
243			while (j < k)
244				rbuf[off++] = boln[j++];
245		} else {
246			REALLOC(rbuf, rbufsz, off + 1, ERR);
247			rbuf[off++] = *sub;
248		}
249	REALLOC(rbuf, rbufsz, off + 1, ERR);
250	rbuf[off] = '\0';
251	return off;
252}
253