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