BSD 4_4 release
[unix-history] / usr / src / usr.bin / ex / ex_addr.c
/*-
* Copyright (c) 1980, 1993
* The Regents of the University of California. All rights reserved.
*
* This module is believed to contain source code proprietary to AT&T.
* Use and redistribution is subject to the Berkeley Software License
* Agreement and your Software Agreement with AT&T (Western Electric).
*/
#ifndef lint
static char sccsid[] = "@(#)ex_addr.c 8.1 (Berkeley) 6/9/93";
#endif /* not lint */
#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.
*/
setdot()
{
setdot1();
if (bigmove)
markDOT();
}
/*
* Call setdot1 to set up default addresses without ever
* setting the previous context mark.
*/
setdot1()
{
if (addr2 == 0)
addr1 = addr2 = dot;
if (addr1 > addr2) {
notempty();
error("Addr1 > addr2|First address exceeds second");
}
}
/*
* Ex allows you to say
* delete 5
* to delete 5 lines, etc.
* Such nonsense is implemented by setcount.
*/
setcount()
{
register int cnt;
pastwh();
if (!isdigit(peekchar())) {
setdot();
return;
}
addr1 = addr2;
setdot();
cnt = getnum();
if (cnt <= 0)
error("Bad count|Nonzero count required");
addr2 += cnt - 1;
if (addr2 > dol)
addr2 = dol;
nonzero();
}
/*
* Parse a number out of the command input stream.
*/
getnum()
{
register int cnt;
for (cnt = 0; isdigit(peekcd());)
cnt = cnt * 10 + ex_getchar() - '0';
return (cnt);
}
/*
* Set the default addresses for commands which use the whole
* buffer as default, notably write.
*/
setall()
{
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.
*/
setnoaddr()
{
if (addr2 != 0)
error("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)
char *inputline;
{
register line *addr;
register 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("Badly formed address");
}
offset += lastsign;
lastsign = 0;
switch (c) {
case ' ':
case '\t':
continue;
case '+':
lastsign = 1;
if (addr == 0)
addr = dot;
continue;
case '^':
case '-':
lastsign = -1;
if (addr == 0)
addr = dot;
continue;
case '\\':
case '?':
case '/':
c = compile(c, 1);
notempty();
savere(scanre);
addr = dot;
if (inputline && execute(0, dot)) {
if (c == '/') {
while (loc1 <= inputline) {
if (loc1 == loc2)
loc2++;
if (!execute(1))
goto nope;
}
break;
} else if (loc1 < inputline) {
char *last;
doques:
do {
last = loc1;
if (loc1 == loc2)
loc2++;
if (!execute(1))
break;
} while (loc1 < inputline);
loc1 = last;
break;
}
}
nope:
for (;;) {
if (c == '/') {
addr++;
if (addr > dol) {
if (value(WRAPSCAN) == 0)
error("No match to BOTTOM|Address search hit BOTTOM without matching pattern");
addr = zero;
}
} else {
addr--;
if (addr < zero) {
if (value(WRAPSCAN) == 0)
error("No match to TOP|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("Fail|Pattern not found");
}
continue;
case '$':
addr = dol;
continue;
case '.':
addr = dot;
continue;
case '\'':
c = markreg(ex_getchar());
if (c == 0)
error("Marks are ' and a-z");
addr = getmark(c);
if (addr == 0)
error("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("Negative address@- first buffer line is 1");
if (addr > dol)
error("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.
*/
setCNL()
{
setcount();
newline();
}
setNAEOL()
{
setnoaddr();
eol();
}