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
51static	bool bigmove;
52
53/*
54 * Set up addr1 and addr2 for commands whose default address is dot.
55 */
56void
57setdot(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 */
69void
70setdot1(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 */
89void
90setcount(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 */
128void
129setcount2(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 */
158int
159getnum(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 */
173void
174setall(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 */
194void
195setnoaddr(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 */
212line *
213address(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;
295doques:
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			}
308nope:
309			for (;;) {
310				if (c == '/') {
311					addr++;
312					if (addr > dol) {
313						if (value(vi_WRAPSCAN) == 0)
314error(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)
323error(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 */
396void
397setCNL(void)
398{
399
400	setcount();
401	donewline();
402}
403
404void
405setNAEOL(void)
406{
407
408	setnoaddr();
409	eol();
410}
411