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
setdot(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
setdot1(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
setcount(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
setcount2(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
getnum(void)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
setall(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
setnoaddr(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 *
address(inputline)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
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