xref: /original-bsd/usr.bin/ex/ex_addr.c (revision de38840f)
1 /* Copyright (c) 1981 Regents of the University of California */
2 static char *sccsid = "@(#)ex_addr.c	7.1	07/08/81";
3 #include "ex.h"
4 #include "ex_re.h"
5 
6 /*
7  * Routines for address parsing and assignment and checking of address bounds
8  * in command mode.  The routine address is called from ex_cmds.c
9  * to parse each component of a command (terminated by , ; or the beginning
10  * of the command itself.  It is also called by the scanning routine
11  * in ex_voperate.c from within open/visual.
12  *
13  * Other routines here manipulate the externals addr1 and addr2.
14  * These are the first and last lines for the current command.
15  *
16  * The variable bigmove remembers whether a non-local glitch of . was
17  * involved in an address expression, so we can set the previous context
18  * mark '' when such a motion occurs.
19  */
20 
21 static	bool bigmove;
22 
23 /*
24  * Set up addr1 and addr2 for commands whose default address is dot.
25  */
26 setdot()
27 {
28 
29 	setdot1();
30 	if (bigmove)
31 		markDOT();
32 }
33 
34 /*
35  * Call setdot1 to set up default addresses without ever
36  * setting the previous context mark.
37  */
38 setdot1()
39 {
40 
41 	if (addr2 == 0)
42 		addr1 = addr2 = dot;
43 	if (addr1 > addr2) {
44 		notempty();
45 		error("Addr1 > addr2|First address exceeds second");
46 	}
47 }
48 
49 /*
50  * Ex allows you to say
51  *	delete 5
52  * to delete 5 lines, etc.
53  * Such nonsense is implemented by setcount.
54  */
55 setcount()
56 {
57 	register int cnt;
58 
59 	pastwh();
60 	if (!isdigit(peekchar())) {
61 		setdot();
62 		return;
63 	}
64 	addr1 = addr2;
65 	setdot();
66 	cnt = getnum();
67 	if (cnt <= 0)
68 		error("Bad count|Nonzero count required");
69 	addr2 += cnt - 1;
70 	if (addr2 > dol)
71 		addr2 = dol;
72 	nonzero();
73 }
74 
75 /*
76  * Parse a number out of the command input stream.
77  */
78 getnum()
79 {
80 	register int cnt;
81 
82 	for (cnt = 0; isdigit(peekcd());)
83 		cnt = cnt * 10 + getchar() - '0';
84 	return (cnt);
85 }
86 
87 /*
88  * Set the default addresses for commands which use the whole
89  * buffer as default, notably write.
90  */
91 setall()
92 {
93 
94 	if (addr2 == 0) {
95 		addr1 = one;
96 		addr2 = dol;
97 		if (dol == zero) {
98 			dot = zero;
99 			return;
100 		}
101 	}
102 	/*
103 	 * Don't want to set previous context mark so use setdot1().
104 	 */
105 	setdot1();
106 }
107 
108 /*
109  * No address allowed on, e.g. the file command.
110  */
111 setnoaddr()
112 {
113 
114 	if (addr2 != 0)
115 		error("No address allowed@on this command");
116 }
117 
118 /*
119  * Parse an address.
120  * Just about any sequence of address characters is legal.
121  *
122  * If you are tricky you can use this routine and the = command
123  * to do simple addition and subtraction of cardinals less
124  * than the number of lines in the file.
125  */
126 line *
127 address(inline)
128 	char *inline;
129 {
130 	register line *addr;
131 	register int offset, c;
132 	short lastsign;
133 
134 	bigmove = 0;
135 	lastsign = 0;
136 	offset = 0;
137 	addr = 0;
138 	for (;;) {
139 		if (isdigit(peekcd())) {
140 			if (addr == 0) {
141 				addr = zero;
142 				bigmove = 1;
143 			}
144 			loc1 = 0;
145 			addr += offset;
146 			offset = getnum();
147 			if (lastsign >= 0)
148 				addr += offset;
149 			else
150 				addr -= offset;
151 			lastsign = 0;
152 			offset = 0;
153 		}
154 		switch (c = getcd()) {
155 
156 		case '?':
157 		case '/':
158 		case '$':
159 		case '\'':
160 		case '\\':
161 			bigmove++;
162 		case '.':
163 			if (addr || offset)
164 				error("Badly formed address");
165 		}
166 		offset += lastsign;
167 		lastsign = 0;
168 		switch (c) {
169 
170 		case ' ':
171 		case '\t':
172 			continue;
173 
174 		case '+':
175 			lastsign = 1;
176 			if (addr == 0)
177 				addr = dot;
178 			continue;
179 
180 		case '^':
181 		case '-':
182 			lastsign = -1;
183 			if (addr == 0)
184 				addr = dot;
185 			continue;
186 
187 		case '\\':
188 		case '?':
189 		case '/':
190 			c = compile(c, 1);
191 			notempty();
192 			savere(scanre);
193 			addr = dot;
194 			if (inline && execute(0, dot)) {
195 				if (c == '/') {
196 					while (loc1 <= inline) {
197 						if (loc1 == loc2)
198 							loc2++;
199 						if (!execute(1))
200 							goto nope;
201 					}
202 					break;
203 				} else if (loc1 < inline) {
204 					char *last;
205 doques:
206 
207 					do {
208 						last = loc1;
209 						if (loc1 == loc2)
210 							loc2++;
211 						if (!execute(1))
212 							break;
213 					} while (loc1 < inline);
214 					loc1 = last;
215 					break;
216 				}
217 			}
218 nope:
219 			for (;;) {
220 				if (c == '/') {
221 					addr++;
222 					if (addr > dol) {
223 						if (value(WRAPSCAN) == 0)
224 error("No match to BOTTOM|Address search hit BOTTOM without matching pattern");
225 						addr = zero;
226 					}
227 				} else {
228 					addr--;
229 					if (addr < zero) {
230 						if (value(WRAPSCAN) == 0)
231 error("No match to TOP|Address search hit TOP without matching pattern");
232 						addr = dol;
233 					}
234 				}
235 				if (execute(0, addr)) {
236 					if (inline && c == '?') {
237 						inline = &linebuf[LBSIZE];
238 						goto doques;
239 					}
240 					break;
241 				}
242 				if (addr == dot)
243 					error("Fail|Pattern not found");
244 			}
245 			continue;
246 
247 		case '$':
248 			addr = dol;
249 			continue;
250 
251 		case '.':
252 			addr = dot;
253 			continue;
254 
255 		case '\'':
256 			c = markreg(getchar());
257 			if (c == 0)
258 				error("Marks are ' and a-z");
259 			addr = getmark(c);
260 			if (addr == 0)
261 				error("Undefined mark@referenced");
262 			break;
263 
264 		default:
265 			ungetchar(c);
266 			if (offset) {
267 				if (addr == 0)
268 					addr = dot;
269 				addr += offset;
270 				loc1 = 0;
271 			}
272 			if (addr == 0) {
273 				bigmove = 0;
274 				return (0);
275 			}
276 			if (addr != zero)
277 				notempty();
278 			addr += lastsign;
279 			if (addr < zero)
280 				error("Negative address@- first buffer line is 1");
281 			if (addr > dol)
282 				error("Not that many lines@in buffer");
283 			return (addr);
284 		}
285 	}
286 }
287 
288 /*
289  * Abbreviations to make code smaller
290  * Left over from squashing ex version 1.1 into
291  * 11/34's and 11/40's.
292  */
293 setCNL()
294 {
295 
296 	setcount();
297 	newline();
298 }
299 
300 setNAEOL()
301 {
302 
303 	setnoaddr();
304 	eol();
305 }
306