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