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