xref: /illumos-gate/usr/src/cmd/vi/port/ex_addr.c (revision 9097ca5c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 /* Copyright (c) 1981 Regents of the University of California */
32 
33 #include "ex.h"
34 #include "ex_re.h"
35 
36 /*
37  * Routines for address parsing and assignment and checking of address bounds
38  * in command mode.  The routine address is called from ex_cmds.c
39  * to parse each component of a command (terminated by , ; or the beginning
40  * of the command itself.  It is also called by the scanning routine
41  * in ex_voperate.c from within open/visual.
42  *
43  * Other routines here manipulate the externals addr1 and addr2.
44  * These are the first and last lines for the current command.
45  *
46  * The variable bigmove remembers whether a non-local glitch of . was
47  * involved in an address expression, so we can set the previous context
48  * mark '' when such a motion occurs.
49  */
50 
51 static	bool bigmove;
52 
53 /*
54  * Set up addr1 and addr2 for commands whose default address is dot.
55  */
56 void
setdot(void)57 setdot(void)
58 {
59 
60 	setdot1();
61 	if (bigmove)
62 		markDOT();
63 }
64 
65 /*
66  * Call setdot1 to set up default addresses without ever
67  * setting the previous context mark.
68  */
69 void
setdot1(void)70 setdot1(void)
71 {
72 
73 	if (addr2 == 0)
74 		addr1 = addr2 = dot;
75 	if (addr1 > addr2) {
76 		notempty();
77 		error(value(vi_TERSE) ?
78 			gettext("Addr1 > addr2") :
79 			gettext("First address exceeds second"));
80 	}
81 }
82 
83 /*
84  * Ex allows you to say
85  *	delete 5
86  * to delete 5 lines, etc.
87  * Such nonsense is implemented by setcount.
88  */
89 void
setcount(void)90 setcount(void)
91 {
92 	int cnt;
93 
94 	pastwh();
95 	if (!isdigit(peekchar())) {
96 		setdot();
97 		return;
98 	}
99 	addr1 = addr2;
100 	setdot();
101 	cnt = getnum();
102 	if (cnt <= 0)
103 		error(value(vi_TERSE) ?
104 			gettext("Bad count") :
105 			gettext("Nonzero count required"));
106 	addr2 += cnt - 1;
107 	if (addr2 > dol)
108 		addr2 = dol;
109 	nonzero();
110 }
111 
112 #ifdef XPG4
113 /*
114  * setcount2():	a version of setcount() which sets addr2 based on addr1 + cnt.
115  * description:
116  *	this routine is responsible for setting addr1 (possibly) and addr2
117  *	(always); using the [count] to compute addr2.
118  *
119  *	this is similar setcount(), but it differs in that setcount() sets
120  *	addr1 based upon addr2; here we set addr2 based upon addr1 and the
121  *	[count].
122  *
123  *	the reason for this is because some commands, of the form:
124  *		[range] command [count]
125  *	will use [count] to modify the range. E.g.:
126  *		change, delete, join, list, yank.
127  */
128 void
setcount2(void)129 setcount2(void)
130 {
131 	int cnt;
132 
133 	pastwh();
134 	if (!isdigit(peekchar())) {
135 		setdot();
136 		return;
137 	}
138 	setdot();
139 	cnt = getnum();
140 	if (cnt <= 0)
141 		error(value(vi_TERSE) ?
142 			gettext("Bad count") :
143 			gettext("Nonzero count required"));
144 	addr2 = addr1 + (cnt - 1);
145 	if (addr2 > dol)
146 		addr2 = dol;
147 	if (addr2 < zero) {
148 		addr1 = addr2 = zero;
149 	}
150 	nonzero();
151 }
152 
153 #endif /* XPG4 */
154 
155 /*
156  * Parse a number out of the command input stream.
157  */
158 int
getnum(void)159 getnum(void)
160 {
161 	int cnt;
162 
163 	/*CSTYLED*/
164 	for (cnt = 0; isdigit(peekcd());)
165 		cnt = cnt * 10 + getchar() - '0';
166 	return (cnt);
167 }
168 
169 /*
170  * Set the default addresses for commands which use the whole
171  * buffer as default, notably write.
172  */
173 void
setall(void)174 setall(void)
175 {
176 
177 	if (addr2 == 0) {
178 		addr1 = one;
179 		addr2 = dol;
180 		if (dol == zero) {
181 			dot = zero;
182 			return;
183 		}
184 	}
185 	/*
186 	 * Don't want to set previous context mark so use setdot1().
187 	 */
188 	setdot1();
189 }
190 
191 /*
192  * No address allowed on, e.g. the file command.
193  */
194 void
setnoaddr(void)195 setnoaddr(void)
196 {
197 
198 	if (addr2 != 0)
199 		error(value(vi_TERSE) ?
200 			gettext("No address allowed") :
201 			gettext("No address allowed on this command"));
202 }
203 
204 /*
205  * Parse an address.
206  * Just about any sequence of address characters is legal.
207  *
208  * If you are tricky you can use this routine and the = command
209  * to do simple addition and subtraction of cardinals less
210  * than the number of lines in the file.
211  */
212 line *
address(inputline)213 address(inputline)
214 	unsigned char *inputline;
215 {
216 	line *addr;
217 	int offset, c;
218 	short lastsign;
219 
220 	bigmove = 0;
221 	lastsign = 0;
222 	offset = 0;
223 	addr = 0;
224 	for (;;) {
225 		if (isdigit(peekcd())) {
226 			if (addr == 0) {
227 				addr = zero;
228 				bigmove = 1;
229 			}
230 			loc1 = 0;
231 			addr += offset;
232 			offset = getnum();
233 			if (lastsign >= 0)
234 				addr += offset;
235 			else
236 				addr -= offset;
237 			lastsign = 0;
238 			offset = 0;
239 		}
240 		switch (c = getcd()) {
241 
242 		case '?':
243 		case '/':
244 		case '$':
245 		case '\'':
246 		case '\\':
247 			bigmove++;
248 			/* FALLTHROUGH */
249 		case '.':
250 			if (addr || offset)
251 				error(gettext("Badly formed address"));
252 		}
253 		offset += lastsign;
254 		lastsign = 0;
255 		switch (c) {
256 
257 		case ' ':
258 		case '\t':
259 			continue;
260 		case ':':
261 			while (peekchar() == ':')
262 				ignchar();
263 			continue;
264 		case '+':
265 			lastsign = 1;
266 			if (addr == 0)
267 				addr = dot;
268 			continue;
269 
270 		case '^':
271 		case '-':
272 			lastsign = -1;
273 			if (addr == 0)
274 				addr = dot;
275 			continue;
276 
277 		case '\\':
278 		case '?':
279 		case '/':
280 			c = vi_compile(c, 1);
281 			notempty();
282 			savere(&scanre);
283 			addr = dot;
284 			if (inputline && execute(0, dot)) {
285 				if (c == '/') {
286 					while (loc1 <= (char *)inputline) {
287 						if (loc1 == loc2)
288 							loc2++;
289 						if (!execute(1))
290 							goto nope;
291 					}
292 					break;
293 				} else if (loc1 < (char *)inputline) {
294 					unsigned char *last;
295 doques:
296 
297 					do {
298 						last = (unsigned char *)loc1;
299 						if (loc1 == loc2)
300 							loc2++;
301 						if (!execute(1))
302 							break;
303 					} while (loc1 < (char *)inputline);
304 					loc1 = (char *)last;
305 					break;
306 				}
307 			}
308 nope:
309 			for (;;) {
310 				if (c == '/') {
311 					addr++;
312 					if (addr > dol) {
313 						if (value(vi_WRAPSCAN) == 0)
314 error(value(vi_TERSE) ?
315 	gettext("No match to BOTTOM") :
316 	gettext("Address search hit BOTTOM without matching pattern"));
317 						addr = zero;
318 					}
319 				} else {
320 					addr--;
321 					if (addr < zero) {
322 						if (value(vi_WRAPSCAN) == 0)
323 error(value(vi_TERSE) ?
324 	gettext("No match to TOP") :
325 	gettext("Address search hit TOP without matching pattern"));
326 						addr = dol;
327 					}
328 				}
329 				if (execute(0, addr)) {
330 					if (inputline && c == '?') {
331 						inputline = &linebuf[LBSIZE];
332 						goto doques;
333 					}
334 					break;
335 				}
336 				if (addr == dot)
337 					error(value(vi_TERSE) ?
338 						gettext("Fail") :
339 						gettext("Pattern not found"));
340 			}
341 			continue;
342 
343 		case '$':
344 			addr = dol;
345 			continue;
346 
347 		case '.':
348 			addr = dot;
349 			continue;
350 
351 		case '\'':
352 			c = markreg(getchar());
353 			if (c == 0)
354 				error(gettext("Marks are ' and a-z"));
355 			addr = getmark(c);
356 			if (addr == 0)
357 				error(value(vi_TERSE) ?
358 				    gettext("Undefined mark") :
359 				    gettext("Undefined mark referenced"));
360 			break;
361 
362 		default:
363 			ungetchar(c);
364 			if (offset) {
365 				if (addr == 0)
366 					addr = dot;
367 				addr += offset;
368 				loc1 = 0;
369 			}
370 			if (addr == 0) {
371 				bigmove = 0;
372 				return (0);
373 			}
374 			if (addr != zero)
375 				notempty();
376 			addr += lastsign;
377 			if (addr < zero)
378 				error(value(vi_TERSE) ?
379 				    gettext("Negative address") :
380 				    gettext("Negative address - "
381 					"first buffer line is 1"));
382 			if (addr > dol)
383 				error(value(vi_TERSE) ?
384 				    gettext("Not that many lines") :
385 				    gettext("Not that many lines in buffer"));
386 			return (addr);
387 		}
388 	}
389 }
390 
391 /*
392  * Abbreviations to make code smaller
393  * Left over from squashing ex version 1.1 into
394  * 11/34's and 11/40's.
395  */
396 void
setCNL(void)397 setCNL(void)
398 {
399 
400 	setcount();
401 	donewline();
402 }
403 
404 void
setNAEOL(void)405 setNAEOL(void)
406 {
407 
408 	setnoaddr();
409 	eol();
410 }
411