xref: /illumos-gate/usr/src/cmd/vi/port/ex_addr.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 #include "ex.h"
35 #include "ex_re.h"
36 
37 /*
38  * Routines for address parsing and assignment and checking of address bounds
39  * in command mode.  The routine address is called from ex_cmds.c
40  * to parse each component of a command (terminated by , ; or the beginning
41  * of the command itself.  It is also called by the scanning routine
42  * in ex_voperate.c from within open/visual.
43  *
44  * Other routines here manipulate the externals addr1 and addr2.
45  * These are the first and last lines for the current command.
46  *
47  * The variable bigmove remembers whether a non-local glitch of . was
48  * involved in an address expression, so we can set the previous context
49  * mark '' when such a motion occurs.
50  */
51 
52 static	bool bigmove;
53 
54 /*
55  * Set up addr1 and addr2 for commands whose default address is dot.
56  */
57 void
58 setdot(void)
59 {
60 
61 	setdot1();
62 	if (bigmove)
63 		markDOT();
64 }
65 
66 /*
67  * Call setdot1 to set up default addresses without ever
68  * setting the previous context mark.
69  */
70 void
71 setdot1(void)
72 {
73 
74 	if (addr2 == 0)
75 		addr1 = addr2 = dot;
76 	if (addr1 > addr2) {
77 		notempty();
78 		error(value(vi_TERSE) ?
79 			gettext("Addr1 > addr2") :
80 			gettext("First address exceeds second"));
81 	}
82 }
83 
84 /*
85  * Ex allows you to say
86  *	delete 5
87  * to delete 5 lines, etc.
88  * Such nonsense is implemented by setcount.
89  */
90 void
91 setcount(void)
92 {
93 	int cnt;
94 
95 	pastwh();
96 	if (!isdigit(peekchar())) {
97 		setdot();
98 		return;
99 	}
100 	addr1 = addr2;
101 	setdot();
102 	cnt = getnum();
103 	if (cnt <= 0)
104 		error(value(vi_TERSE) ?
105 			gettext("Bad count") :
106 			gettext("Nonzero count required"));
107 	addr2 += cnt - 1;
108 	if (addr2 > dol)
109 		addr2 = dol;
110 	nonzero();
111 }
112 
113 #ifdef XPG4
114 /*
115  * setcount2():	a version of setcount() which sets addr2 based on addr1 + cnt.
116  * description:
117  *	this routine is responsible for setting addr1 (possibly) and addr2
118  *	(always); using the [count] to compute addr2.
119  *
120  *	this is similar setcount(), but it differs in that setcount() sets
121  *	addr1 based upon addr2; here we set addr2 based upon addr1 and the
122  *	[count].
123  *
124  *	the reason for this is because some commands, of the form:
125  *		[range] command [count]
126  *	will use [count] to modify the range. E.g.:
127  *		change, delete, join, list, yank.
128  */
129 void
130 setcount2(void)
131 {
132 	int cnt;
133 
134 	pastwh();
135 	if (!isdigit(peekchar())) {
136 		setdot();
137 		return;
138 	}
139 	setdot();
140 	cnt = getnum();
141 	if (cnt <= 0)
142 		error(value(vi_TERSE) ?
143 			gettext("Bad count") :
144 			gettext("Nonzero count required"));
145 	addr2 = addr1 + (cnt - 1);
146 	if (addr2 > dol)
147 		addr2 = dol;
148 	if (addr2 < zero) {
149 		addr1 = addr2 = zero;
150 	}
151 	nonzero();
152 }
153 
154 #endif /* XPG4 */
155 
156 /*
157  * Parse a number out of the command input stream.
158  */
159 int
160 getnum(void)
161 {
162 	int cnt;
163 
164 	/*CSTYLED*/
165 	for (cnt = 0; isdigit(peekcd());)
166 		cnt = cnt * 10 + getchar() - '0';
167 	return (cnt);
168 }
169 
170 /*
171  * Set the default addresses for commands which use the whole
172  * buffer as default, notably write.
173  */
174 void
175 setall(void)
176 {
177 
178 	if (addr2 == 0) {
179 		addr1 = one;
180 		addr2 = dol;
181 		if (dol == zero) {
182 			dot = zero;
183 			return;
184 		}
185 	}
186 	/*
187 	 * Don't want to set previous context mark so use setdot1().
188 	 */
189 	setdot1();
190 }
191 
192 /*
193  * No address allowed on, e.g. the file command.
194  */
195 void
196 setnoaddr(void)
197 {
198 
199 	if (addr2 != 0)
200 		error(value(vi_TERSE) ?
201 			gettext("No address allowed") :
202 			gettext("No address allowed on this command"));
203 }
204 
205 /*
206  * Parse an address.
207  * Just about any sequence of address characters is legal.
208  *
209  * If you are tricky you can use this routine and the = command
210  * to do simple addition and subtraction of cardinals less
211  * than the number of lines in the file.
212  */
213 line *
214 address(inputline)
215 	unsigned char *inputline;
216 {
217 	line *addr;
218 	int offset, c;
219 	short lastsign;
220 
221 	bigmove = 0;
222 	lastsign = 0;
223 	offset = 0;
224 	addr = 0;
225 	for (;;) {
226 		if (isdigit(peekcd())) {
227 			if (addr == 0) {
228 				addr = zero;
229 				bigmove = 1;
230 			}
231 			loc1 = 0;
232 			addr += offset;
233 			offset = getnum();
234 			if (lastsign >= 0)
235 				addr += offset;
236 			else
237 				addr -= offset;
238 			lastsign = 0;
239 			offset = 0;
240 		}
241 		switch (c = getcd()) {
242 
243 		case '?':
244 		case '/':
245 		case '$':
246 		case '\'':
247 		case '\\':
248 			bigmove++;
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
397 setCNL(void)
398 {
399 
400 	setcount();
401 	donewline();
402 }
403 
404 void
405 setNAEOL(void)
406 {
407 
408 	setnoaddr();
409 	eol();
410 }
411