Initial import from FreeBSD RELENG_4:
[dragonfly.git] / bin / ed / sub.c
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 #ifndef lint
30 #if 0
31 static char * const rcsid = "@(#)sub.c,v 1.1 1994/02/01 00:34:44 alm Exp";
32 #else
33 static char * const rcsid =
34   "$FreeBSD: src/bin/ed/sub.c,v 1.12 1999/08/27 23:14:15 peter Exp $";
35 #endif
36 #endif /* not lint */
37
38 #include "ed.h"
39
40
41 char *rhbuf;                    /* rhs substitution buffer */
42 int rhbufsz;                    /* rhs substitution buffer size */
43 int rhbufi;                     /* rhs substitution buffer index */
44
45 /* extract_subst_tail: extract substitution tail from the command buffer */
46 int
47 extract_subst_tail(flagp, np)
48         int *flagp;
49         long *np;
50 {
51         char delimiter;
52
53         *flagp = *np = 0;
54         if ((delimiter = *ibufp) == '\n') {
55                 rhbufi = 0;
56                 *flagp = GPR;
57                 return 0;
58         } else if (extract_subst_template() == NULL)
59                 return  ERR;
60         else if (*ibufp == '\n') {
61                 *flagp = GPR;
62                 return 0;
63         } else if (*ibufp == delimiter)
64                 ibufp++;
65         if ('1' <= *ibufp && *ibufp <= '9') {
66                 STRTOL(*np, ibufp);
67                 return 0;
68         } else if (*ibufp == 'g') {
69                 ibufp++;
70                 *flagp = GSG;
71                 return 0;
72         }
73         return 0;
74 }
75
76
77 /* extract_subst_template: return pointer to copy of substitution template
78    in the command buffer */
79 char *
80 extract_subst_template()
81 {
82         int n = 0;
83         int i = 0;
84         char c;
85         char delimiter = *ibufp++;
86
87         if (*ibufp == '%' && *(ibufp + 1) == delimiter) {
88                 ibufp++;
89                 if (!rhbuf) sprintf(errmsg, "no previous substitution");
90                 return rhbuf;
91         }
92         while (*ibufp != delimiter) {
93                 REALLOC(rhbuf, rhbufsz, i + 2, NULL);
94                 if ((c = rhbuf[i++] = *ibufp++) == '\n' && *ibufp == '\0') {
95                         i--, ibufp--;
96                         break;
97                 } else if (c != '\\')
98                         ;
99                 else if ((rhbuf[i++] = *ibufp++) != '\n')
100                         ;
101                 else if (!isglobal) {
102                         while ((n = get_tty_line()) == 0 ||
103                             (n > 0 && ibuf[n - 1] != '\n'))
104                                 clearerr(stdin);
105                         if (n < 0)
106                                 return NULL;
107                 }
108         }
109         REALLOC(rhbuf, rhbufsz, i + 1, NULL);
110         rhbuf[rhbufi = i] = '\0';
111         return  rhbuf;
112 }
113
114
115 char *rbuf;                     /* substitute_matching_text buffer */
116 int rbufsz;                     /* substitute_matching_text buffer size */
117
118 /* search_and_replace: for each line in a range, change text matching a pattern
119    according to a substitution template; return status  */
120 int
121 search_and_replace(pat, gflag, kth)
122         pattern_t *pat;
123         int gflag;
124         int kth;
125 {
126         undo_t *up;
127         char *txt;
128         char *eot;
129         long lc;
130         long xa = current_addr;
131         int nsubs = 0;
132         line_t *lp;
133         int len;
134
135         current_addr = first_addr - 1;
136         for (lc = 0; lc <= second_addr - first_addr; lc++) {
137                 lp = get_addressed_line_node(++current_addr);
138                 if ((len = substitute_matching_text(pat, lp, gflag, kth)) < 0)
139                         return ERR;
140                 else if (len) {
141                         up = NULL;
142                         if (delete_lines(current_addr, current_addr) < 0)
143                                 return ERR;
144                         txt = rbuf;
145                         eot = rbuf + len;
146                         SPL1();
147                         do {
148                                 if ((txt = put_sbuf_line(txt)) == NULL) {
149                                         SPL0();
150                                         return ERR;
151                                 } else if (up)
152                                         up->t = get_addressed_line_node(current_addr);
153                                 else if ((up = push_undo_stack(UADD,
154                                     current_addr, current_addr)) == NULL) {
155                                         SPL0();
156                                         return ERR;
157                                 }
158                         } while (txt != eot);
159                         SPL0();
160                         nsubs++;
161                         xa = current_addr;
162                 }
163         }
164         current_addr = xa;
165         if  (nsubs == 0 && !(gflag & GLB)) {
166                 sprintf(errmsg, "no match");
167                 return ERR;
168         } else if ((gflag & (GPR | GLS | GNP)) &&
169             display_lines(current_addr, current_addr, gflag) < 0)
170                 return ERR;
171         return 0;
172 }
173
174
175 /* substitute_matching_text: replace text matched by a pattern according to
176    a substitution template; return pointer to the modified text */
177 int
178 substitute_matching_text(pat, lp, gflag, kth)
179         pattern_t *pat;
180         line_t *lp;
181         int gflag;
182         int kth;
183 {
184         int off = 0;
185         int changed = 0;
186         int matchno = 0;
187         int i = 0;
188         regmatch_t rm[SE_MAX];
189         char *txt;
190         char *eot;
191
192         if ((txt = get_sbuf_line(lp)) == NULL)
193                 return ERR;
194         if (isbinary)
195                 NUL_TO_NEWLINE(txt, lp->len);
196         eot = txt + lp->len;
197         if (!regexec(pat, txt, SE_MAX, rm, 0)) {
198                 do {
199                         if (!kth || kth == ++matchno) {
200                                 changed++;
201                                 i = rm[0].rm_so;
202                                 REALLOC(rbuf, rbufsz, off + i, ERR);
203                                 if (isbinary)
204                                         NEWLINE_TO_NUL(txt, rm[0].rm_eo);
205                                 memcpy(rbuf + off, txt, i);
206                                 off += i;
207                                 if ((off = apply_subst_template(txt, rm, off,
208                                     pat->re_nsub)) < 0)
209                                         return ERR;
210                         } else {
211                                 i = rm[0].rm_eo;
212                                 REALLOC(rbuf, rbufsz, off + i, ERR);
213                                 if (isbinary)
214                                         NEWLINE_TO_NUL(txt, i);
215                                 memcpy(rbuf + off, txt, i);
216                                 off += i;
217                         }
218                         txt += rm[0].rm_eo;
219                 } while (*txt &&
220                         (!changed || ((gflag & GSG) && rm[0].rm_eo)) &&
221                         !regexec(pat, txt, SE_MAX, rm, REG_NOTBOL));
222                 i = eot - txt;
223                 REALLOC(rbuf, rbufsz, off + i + 2, ERR);
224                 if (i > 0 && !rm[0].rm_eo && (gflag & GSG)) {
225                         sprintf(errmsg, "infinite substitution loop");
226                         return  ERR;
227                 }
228                 if (isbinary)
229                         NEWLINE_TO_NUL(txt, i);
230                 memcpy(rbuf + off, txt, i);
231                 memcpy(rbuf + off + i, "\n", 2);
232         }
233         return changed ? off + i + 1 : 0;
234 }
235
236
237 /* apply_subst_template: modify text according to a substitution template;
238    return offset to end of modified text */
239 int
240 apply_subst_template(boln, rm, off, re_nsub)
241         char *boln;
242         regmatch_t *rm;
243         int off;
244         int re_nsub;
245 {
246         int j = 0;
247         int k = 0;
248         int n;
249         char *sub = rhbuf;
250
251         for (; sub - rhbuf < rhbufi; sub++)
252                 if (*sub == '&') {
253                         j = rm[0].rm_so;
254                         k = rm[0].rm_eo;
255                         REALLOC(rbuf, rbufsz, off + k - j, ERR);
256                         while (j < k)
257                                 rbuf[off++] = boln[j++];
258                 } else if (*sub == '\\' && '1' <= *++sub && *sub <= '9' &&
259                     (n = *sub - '0') <= re_nsub) {
260                         j = rm[n].rm_so;
261                         k = rm[n].rm_eo;
262                         REALLOC(rbuf, rbufsz, off + k - j, ERR);
263                         while (j < k)
264                                 rbuf[off++] = boln[j++];
265                 } else {
266                         REALLOC(rbuf, rbufsz, off + 1, ERR);
267                         rbuf[off++] = *sub;
268                 }
269         REALLOC(rbuf, rbufsz, off + 1, ERR);
270         rbuf[off] = '\0';
271         return off;
272 }