xref: /titanic_44/usr/src/cmd/vi/port/ex_addr.c (revision b7f45089ccbe01bab3d7c7377b49d80d2ae18a69)
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 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /* Copyright (c) 1981 Regents of the University of California */
27 
28 /*
29  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
30  * Use is subject to license terms.
31  */
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.7	*/
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 setdot()
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 setdot1()
70 {
71 
72 	if (addr2 == 0)
73 		addr1 = addr2 = dot;
74 	if (addr1 > addr2) {
75 		notempty();
76 		error(value(vi_TERSE) ?
77 			gettext("Addr1 > addr2") :
78 			gettext("First address exceeds second"));
79 	}
80 }
81 
82 /*
83  * Ex allows you to say
84  *	delete 5
85  * to delete 5 lines, etc.
86  * Such nonsense is implemented by setcount.
87  */
88 setcount()
89 {
90 	register int cnt;
91 
92 	pastwh();
93 	if (!isdigit(peekchar())) {
94 		setdot();
95 		return;
96 	}
97 	addr1 = addr2;
98 	setdot();
99 	cnt = getnum();
100 	if (cnt <= 0)
101 		error(value(vi_TERSE) ?
102 			gettext("Bad count") :
103 			gettext("Nonzero count required"));
104 	addr2 += cnt - 1;
105 	if (addr2 > dol)
106 		addr2 = dol;
107 	nonzero();
108 }
109 
110 #ifdef XPG4
111 /*
112  * setcount2():	a version of setcount() which sets addr2 based on addr1 + cnt.
113  * description:
114  *	this routine is responsible for setting addr1 (possibly) and addr2
115  *	(always); using the [count] to compute addr2.
116  *
117  *	this is similar setcount(), but it differs in that setcount() sets
118  *	addr1 based upon addr2; here we set addr2 based upon addr1 and the
119  *	[count].
120  *
121  *	the reason for this is because some commands, of the form:
122  *		[range] command [count]
123  *	will use [count] to modify the range. E.g.:
124  *		change, delete, join, list, yank.
125  */
126 setcount2()
127 {
128 	register int cnt;
129 
130 	pastwh();
131 	if (!isdigit(peekchar())) {
132 		setdot();
133 		return;
134 	}
135 	setdot();
136 	cnt = getnum();
137 	if (cnt <= 0)
138 		error(value(vi_TERSE) ?
139 			gettext("Bad count") :
140 			gettext("Nonzero count required"));
141 	addr2 = addr1 + (cnt - 1);
142 	if (addr2 > dol)
143 		addr2 = dol;
144 	if (addr2 < zero) {
145 		addr1 = addr2 = zero;
146 	}
147 	nonzero();
148 }
149 
150 #endif /* XPG4 */
151 
152 /*
153  * Parse a number out of the command input stream.
154  */
155 getnum()
156 {
157 	register int cnt;
158 
159 	/*CSTYLED*/
160 	for (cnt = 0; isdigit(peekcd());)
161 		cnt = cnt * 10 + getchar() - '0';
162 	return (cnt);
163 }
164 
165 /*
166  * Set the default addresses for commands which use the whole
167  * buffer as default, notably write.
168  */
169 setall()
170 {
171 
172 	if (addr2 == 0) {
173 		addr1 = one;
174 		addr2 = dol;
175 		if (dol == zero) {
176 			dot = zero;
177 			return;
178 		}
179 	}
180 	/*
181 	 * Don't want to set previous context mark so use setdot1().
182 	 */
183 	setdot1();
184 }
185 
186 /*
187  * No address allowed on, e.g. the file command.
188  */
189 setnoaddr()
190 {
191 
192 	if (addr2 != 0)
193 		error(value(vi_TERSE) ?
194 			gettext("No address allowed") :
195 			gettext("No address allowed on this command"));
196 }
197 
198 /*
199  * Parse an address.
200  * Just about any sequence of address characters is legal.
201  *
202  * If you are tricky you can use this routine and the = command
203  * to do simple addition and subtraction of cardinals less
204  * than the number of lines in the file.
205  */
206 line *
207 address(inputline)
208 	unsigned char *inputline;
209 {
210 	register line *addr;
211 	register int offset, c;
212 	short lastsign;
213 
214 	bigmove = 0;
215 	lastsign = 0;
216 	offset = 0;
217 	addr = 0;
218 	for (;;) {
219 		if (isdigit(peekcd())) {
220 			if (addr == 0) {
221 				addr = zero;
222 				bigmove = 1;
223 			}
224 			loc1 = 0;
225 			addr += offset;
226 			offset = getnum();
227 			if (lastsign >= 0)
228 				addr += offset;
229 			else
230 				addr -= offset;
231 			lastsign = 0;
232 			offset = 0;
233 		}
234 		switch (c = getcd()) {
235 
236 		case '?':
237 		case '/':
238 		case '$':
239 		case '\'':
240 		case '\\':
241 			bigmove++;
242 		case '.':
243 			if (addr || offset)
244 				error(gettext("Badly formed address"));
245 		}
246 		offset += lastsign;
247 		lastsign = 0;
248 		switch (c) {
249 
250 		case ' ':
251 		case '\t':
252 			continue;
253 		case ':':
254 			while (peekchar() == ':')
255 				ignchar();
256 			continue;
257 		case '+':
258 			lastsign = 1;
259 			if (addr == 0)
260 				addr = dot;
261 			continue;
262 
263 		case '^':
264 		case '-':
265 			lastsign = -1;
266 			if (addr == 0)
267 				addr = dot;
268 			continue;
269 
270 		case '\\':
271 		case '?':
272 		case '/':
273 			c = vi_compile(c, 1);
274 			notempty();
275 			savere(&scanre);
276 			addr = dot;
277 			if (inputline && execute(0, dot)) {
278 				if (c == '/') {
279 					while (loc1 <= (char *)inputline) {
280 						if (loc1 == loc2)
281 							loc2++;
282 						if (!execute(1))
283 							goto nope;
284 					}
285 					break;
286 				} else if (loc1 < (char *)inputline) {
287 					unsigned char *last;
288 doques:
289 
290 					do {
291 						last = (unsigned char *)loc1;
292 						if (loc1 == loc2)
293 							loc2++;
294 						if (!execute(1))
295 							break;
296 					} while (loc1 < (char *)inputline);
297 					loc1 = (char *)last;
298 					break;
299 				}
300 			}
301 nope:
302 			for (;;) {
303 				if (c == '/') {
304 					addr++;
305 					if (addr > dol) {
306 						if (value(vi_WRAPSCAN) == 0)
307 error(value(vi_TERSE) ?
308 	gettext("No match to BOTTOM") :
309 	gettext("Address search hit BOTTOM without matching pattern"));
310 						addr = zero;
311 					}
312 				} else {
313 					addr--;
314 					if (addr < zero) {
315 						if (value(vi_WRAPSCAN) == 0)
316 error(value(vi_TERSE) ?
317 	gettext("No match to TOP") :
318 	gettext("Address search hit TOP without matching pattern"));
319 						addr = dol;
320 					}
321 				}
322 				if (execute(0, addr)) {
323 					if (inputline && c == '?') {
324 						inputline = &linebuf[LBSIZE];
325 						goto doques;
326 					}
327 					break;
328 				}
329 				if (addr == dot)
330 					error(value(vi_TERSE) ?
331 						gettext("Fail") :
332 						gettext("Pattern not found"));
333 			}
334 			continue;
335 
336 		case '$':
337 			addr = dol;
338 			continue;
339 
340 		case '.':
341 			addr = dot;
342 			continue;
343 
344 		case '\'':
345 			c = markreg(getchar());
346 			if (c == 0)
347 				error(gettext("Marks are ' and a-z"));
348 			addr = getmark(c);
349 			if (addr == 0)
350 				error(value(vi_TERSE) ?
351 				    gettext("Undefined mark") :
352 				    gettext("Undefined mark referenced"));
353 			break;
354 
355 		default:
356 			ungetchar(c);
357 			if (offset) {
358 				if (addr == 0)
359 					addr = dot;
360 				addr += offset;
361 				loc1 = 0;
362 			}
363 			if (addr == 0) {
364 				bigmove = 0;
365 				return (0);
366 			}
367 			if (addr != zero)
368 				notempty();
369 			addr += lastsign;
370 			if (addr < zero)
371 				error(value(vi_TERSE) ?
372 				    gettext("Negative address") :
373 				    gettext("Negative address - "
374 					"first buffer line is 1"));
375 			if (addr > dol)
376 				error(value(vi_TERSE) ?
377 				    gettext("Not that many lines") :
378 				    gettext("Not that many lines in buffer"));
379 			return (addr);
380 		}
381 	}
382 }
383 
384 /*
385  * Abbreviations to make code smaller
386  * Left over from squashing ex version 1.1 into
387  * 11/34's and 11/40's.
388  */
389 setCNL()
390 {
391 
392 	setcount();
393 	donewline();
394 }
395 
396 setNAEOL()
397 {
398 
399 	setnoaddr();
400 	eol();
401 }
402