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 51 static bool bigmove; 52 53 /* 54 * Set up addr1 and addr2 for commands whose default address is dot. 55 */ 56 void 57 setdot(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 */ 69 void 70 setdot1(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 */ 89 void 90 setcount(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 */ 128 void 129 setcount2(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 */ 158 int 159 getnum(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 */ 173 void 174 setall(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 */ 194 void 195 setnoaddr(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 */ 212 line * 213 address(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; 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