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