ex_addr.c revision f6db9f272f0061301cfaa1c0001b7d636eae31f4
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/* Copyright (c) 1981 Regents of the University of California */
#pragma ident "%Z%%M% %I% %E% SMI"
#include "ex.h"
#include "ex_re.h"
/*
* Routines for address parsing and assignment and checking of address bounds
* in command mode. The routine address is called from ex_cmds.c
* to parse each component of a command (terminated by , ; or the beginning
* of the command itself. It is also called by the scanning routine
* in ex_voperate.c from within open/visual.
*
* Other routines here manipulate the externals addr1 and addr2.
* These are the first and last lines for the current command.
*
* The variable bigmove remembers whether a non-local glitch of . was
* involved in an address expression, so we can set the previous context
* mark '' when such a motion occurs.
*/
static bool bigmove;
/*
* Set up addr1 and addr2 for commands whose default address is dot.
*/
void
setdot(void)
{
setdot1();
if (bigmove)
markDOT();
}
/*
* Call setdot1 to set up default addresses without ever
* setting the previous context mark.
*/
void
setdot1(void)
{
if (addr2 == 0)
addr1 = addr2 = dot;
if (addr1 > addr2) {
notempty();
error(value(vi_TERSE) ?
gettext("Addr1 > addr2") :
gettext("First address exceeds second"));
}
}
/*
* Ex allows you to say
* delete 5
* to delete 5 lines, etc.
* Such nonsense is implemented by setcount.
*/
void
setcount(void)
{
int cnt;
pastwh();
if (!isdigit(peekchar())) {
setdot();
return;
}
addr1 = addr2;
setdot();
cnt = getnum();
if (cnt <= 0)
error(value(vi_TERSE) ?
gettext("Bad count") :
gettext("Nonzero count required"));
addr2 += cnt - 1;
if (addr2 > dol)
addr2 = dol;
nonzero();
}
#ifdef XPG4
/*
* setcount2(): a version of setcount() which sets addr2 based on addr1 + cnt.
* description:
* this routine is responsible for setting addr1 (possibly) and addr2
* (always); using the [count] to compute addr2.
*
* this is similar setcount(), but it differs in that setcount() sets
* addr1 based upon addr2; here we set addr2 based upon addr1 and the
* [count].
*
* the reason for this is because some commands, of the form:
* [range] command [count]
* will use [count] to modify the range. E.g.:
* change, delete, join, list, yank.
*/
void
setcount2(void)
{
int cnt;
pastwh();
if (!isdigit(peekchar())) {
setdot();
return;
}
setdot();
cnt = getnum();
if (cnt <= 0)
error(value(vi_TERSE) ?
gettext("Bad count") :
gettext("Nonzero count required"));
addr2 = addr1 + (cnt - 1);
if (addr2 > dol)
addr2 = dol;
if (addr2 < zero) {
addr1 = addr2 = zero;
}
nonzero();
}
#endif /* XPG4 */
/*
* Parse a number out of the command input stream.
*/
int
getnum(void)
{
int cnt;
/*CSTYLED*/
for (cnt = 0; isdigit(peekcd());)
cnt = cnt * 10 + getchar() - '0';
return (cnt);
}
/*
* Set the default addresses for commands which use the whole
* buffer as default, notably write.
*/
void
setall(void)
{
if (addr2 == 0) {
addr1 = one;
addr2 = dol;
if (dol == zero) {
dot = zero;
return;
}
}
/*
* Don't want to set previous context mark so use setdot1().
*/
setdot1();
}
/*
* No address allowed on, e.g. the file command.
*/
void
setnoaddr(void)
{
if (addr2 != 0)
error(value(vi_TERSE) ?
gettext("No address allowed") :
gettext("No address allowed on this command"));
}
/*
* Parse an address.
* Just about any sequence of address characters is legal.
*
* If you are tricky you can use this routine and the = command
* to do simple addition and subtraction of cardinals less
* than the number of lines in the file.
*/
line *
address(inputline)
unsigned char *inputline;
{
line *addr;
int offset, c;
short lastsign;
bigmove = 0;
lastsign = 0;
offset = 0;
addr = 0;
for (;;) {
if (isdigit(peekcd())) {
if (addr == 0) {
addr = zero;
bigmove = 1;
}
loc1 = 0;
addr += offset;
offset = getnum();
if (lastsign >= 0)
addr += offset;
else
addr -= offset;
lastsign = 0;
offset = 0;
}
switch (c = getcd()) {
case '?':
case '/':
case '$':
case '\'':
case '\\':
bigmove++;
case '.':
if (addr || offset)
error(gettext("Badly formed address"));
}
offset += lastsign;
lastsign = 0;
switch (c) {
case ' ':
case '\t':
continue;
case ':':
while (peekchar() == ':')
ignchar();
continue;
case '+':
lastsign = 1;
if (addr == 0)
addr = dot;
continue;
case '^':
case '-':
lastsign = -1;
if (addr == 0)
addr = dot;
continue;
case '\\':
case '?':
case '/':
c = vi_compile(c, 1);
notempty();
savere(&scanre);
addr = dot;
if (inputline && execute(0, dot)) {
if (c == '/') {
while (loc1 <= (char *)inputline) {
if (loc1 == loc2)
loc2++;
if (!execute(1))
goto nope;
}
break;
} else if (loc1 < (char *)inputline) {
unsigned char *last;
doques:
do {
last = (unsigned char *)loc1;
if (loc1 == loc2)
loc2++;
if (!execute(1))
break;
} while (loc1 < (char *)inputline);
loc1 = (char *)last;
break;
}
}
nope:
for (;;) {
if (c == '/') {
addr++;
if (addr > dol) {
if (value(vi_WRAPSCAN) == 0)
error(value(vi_TERSE) ?
gettext("No match to BOTTOM") :
gettext("Address search hit BOTTOM without matching pattern"));
addr = zero;
}
} else {
addr--;
if (addr < zero) {
if (value(vi_WRAPSCAN) == 0)
error(value(vi_TERSE) ?
gettext("No match to TOP") :
gettext("Address search hit TOP without matching pattern"));
addr = dol;
}
}
if (execute(0, addr)) {
if (inputline && c == '?') {
inputline = &linebuf[LBSIZE];
goto doques;
}
break;
}
if (addr == dot)
error(value(vi_TERSE) ?
gettext("Fail") :
gettext("Pattern not found"));
}
continue;
case '$':
addr = dol;
continue;
case '.':
addr = dot;
continue;
case '\'':
c = markreg(getchar());
if (c == 0)
error(gettext("Marks are ' and a-z"));
addr = getmark(c);
if (addr == 0)
error(value(vi_TERSE) ?
gettext("Undefined mark") :
gettext("Undefined mark referenced"));
break;
default:
ungetchar(c);
if (offset) {
if (addr == 0)
addr = dot;
addr += offset;
loc1 = 0;
}
if (addr == 0) {
bigmove = 0;
return (0);
}
if (addr != zero)
notempty();
addr += lastsign;
if (addr < zero)
error(value(vi_TERSE) ?
gettext("Negative address") :
gettext("Negative address - "
"first buffer line is 1"));
if (addr > dol)
error(value(vi_TERSE) ?
gettext("Not that many lines") :
gettext("Not that many lines in buffer"));
return (addr);
}
}
}
/*
* Abbreviations to make code smaller
* Left over from squashing ex version 1.1 into
* 11/34's and 11/40's.
*/
void
setCNL(void)
{
setcount();
donewline();
}
void
setNAEOL(void)
{
setnoaddr();
eol();
}