File: [local] / src / usr.bin / mg / region.c (download)
Revision 1.26, Tue Jun 10 23:23:53 2008 UTC (16 years ago) by kjell
Branch: MAIN
CVS Tags: OPENBSD_4_4_BASE, OPENBSD_4_4 Changes since 1.25: +9 -6 lines
Add a clear-mark function.
Use it to clear the region it is copied (M-w), or yanked (C-w).
This matches xemacs behavior, is not horribly different from gnu
emacs, and way less wrong than the current behavior.
Noticed by Han Boetes. ok otto@
|
/* $OpenBSD: region.c,v 1.26 2008/06/10 23:23:53 kjell Exp $ */
/* This file is in the public domain. */
/*
* Region based commands.
* The routines in this file deal with the region, that magic space between
* "." and mark. Some functions are commands. Some functions are just for
* internal use.
*/
#include "def.h"
static int getregion(struct region *);
static int setsize(struct region *, RSIZE);
/*
* Kill the region. Ask "getregion" to figure out the bounds of the region.
* Move "." to the start, and kill the characters. Mark is cleared afterwards.
*/
/* ARGSUSED */
int
killregion(int f, int n)
{
int s;
struct region region;
if ((s = getregion(®ion)) != TRUE)
return (s);
/* This is a kill-type command, so do magic kill buffer stuff. */
if ((lastflag & CFKILL) == 0)
kdelete();
thisflag |= CFKILL;
curwp->w_dotp = region.r_linep;
curwp->w_doto = region.r_offset;
s = ldelete(region.r_size, KFORW);
clearmark(FFARG, 0);
return (s);
}
/*
* Copy all of the characters in the region to the kill buffer,
* clearing the mark afterwards.
* This is a bit like a kill region followed by a yank.
*/
/* ARGSUSED */
int
copyregion(int f, int n)
{
struct line *linep;
struct region region;
int loffs;
int s;
if ((s = getregion(®ion)) != TRUE)
return (s);
/* kill type command */
if ((lastflag & CFKILL) == 0)
kdelete();
thisflag |= CFKILL;
/* current line */
linep = region.r_linep;
/* current offset */
loffs = region.r_offset;
while (region.r_size--) {
if (loffs == llength(linep)) { /* End of line. */
if ((s = kinsert('\n', KFORW)) != TRUE)
return (s);
linep = lforw(linep);
loffs = 0;
} else { /* Middle of line. */
if ((s = kinsert(lgetc(linep, loffs), KFORW)) != TRUE)
return (s);
++loffs;
}
}
clearmark(FFARG, 0);
return (TRUE);
}
/*
* Lower case region. Zap all of the upper case characters in the region to
* lower case. Use the region code to set the limits. Scan the buffer, doing
* the changes. Call "lchange" to ensure that redisplay is done in all
* buffers.
*/
/* ARGSUSED */
int
lowerregion(int f, int n)
{
struct line *linep;
struct region region;
int loffs, c, s;
if (curbp->b_flag & BFREADONLY) {
ewprintf("Buffer is read-only");
return (FALSE);
}
if ((s = getregion(®ion)) != TRUE)
return (s);
undo_add_change(region.r_linep, region.r_offset, region.r_size);
lchange(WFFULL);
linep = region.r_linep;
loffs = region.r_offset;
while (region.r_size--) {
if (loffs == llength(linep)) {
linep = lforw(linep);
loffs = 0;
} else {
c = lgetc(linep, loffs);
if (ISUPPER(c) != FALSE)
lputc(linep, loffs, TOLOWER(c));
++loffs;
}
}
return (TRUE);
}
/*
* Upper case region. Zap all of the lower case characters in the region to
* upper case. Use the region code to set the limits. Scan the buffer,
* doing the changes. Call "lchange" to ensure that redisplay is done in all
* buffers.
*/
/* ARGSUSED */
int
upperregion(int f, int n)
{
struct line *linep;
struct region region;
int loffs, c, s;
if (curbp->b_flag & BFREADONLY) {
ewprintf("Buffer is read-only");
return (FALSE);
}
if ((s = getregion(®ion)) != TRUE)
return (s);
undo_add_change(region.r_linep, region.r_offset, region.r_size);
lchange(WFFULL);
linep = region.r_linep;
loffs = region.r_offset;
while (region.r_size--) {
if (loffs == llength(linep)) {
linep = lforw(linep);
loffs = 0;
} else {
c = lgetc(linep, loffs);
if (ISLOWER(c) != FALSE)
lputc(linep, loffs, TOUPPER(c));
++loffs;
}
}
return (TRUE);
}
/*
* This routine figures out the bound of the region in the current window,
* and stores the results into the fields of the REGION structure. Dot and
* mark are usually close together, but I don't know the order, so I scan
* outward from dot, in both directions, looking for mark. The size is kept
* in a long. At the end, after the size is figured out, it is assigned to
* the size field of the region structure. If this assignment loses any bits,
* then we print an error. This is "type independent" overflow checking. All
* of the callers of this routine should be ready to get an ABORT status,
* because I might add a "if regions is big, ask before clobbering" flag.
*/
static int
getregion(struct region *rp)
{
struct line *flp, *blp;
long fsize, bsize;
if (curwp->w_markp == NULL) {
ewprintf("No mark set in this window");
return (FALSE);
}
/* "r_size" always ok */
if (curwp->w_dotp == curwp->w_markp) {
rp->r_linep = curwp->w_dotp;
if (curwp->w_doto < curwp->w_marko) {
rp->r_offset = curwp->w_doto;
rp->r_size = (RSIZE)(curwp->w_marko - curwp->w_doto);
} else {
rp->r_offset = curwp->w_marko;
rp->r_size = (RSIZE)(curwp->w_doto - curwp->w_marko);
}
return (TRUE);
}
/* get region size */
flp = blp = curwp->w_dotp;
bsize = curwp->w_doto;
fsize = llength(flp) - curwp->w_doto + 1;
while (lforw(flp) != curbp->b_headp || lback(blp) != curbp->b_headp) {
if (lforw(flp) != curbp->b_headp) {
flp = lforw(flp);
if (flp == curwp->w_markp) {
rp->r_linep = curwp->w_dotp;
rp->r_offset = curwp->w_doto;
return (setsize(rp,
(RSIZE)(fsize + curwp->w_marko)));
}
fsize += llength(flp) + 1;
}
if (lback(blp) != curbp->b_headp) {
blp = lback(blp);
bsize += llength(blp) + 1;
if (blp == curwp->w_markp) {
rp->r_linep = blp;
rp->r_offset = curwp->w_marko;
return (setsize(rp,
(RSIZE)(bsize - curwp->w_marko)));
}
}
}
ewprintf("Bug: lost mark");
return (FALSE);
}
/*
* Set size, and check for overflow.
*/
static int
setsize(struct region *rp, RSIZE size)
{
rp->r_size = size;
if (rp->r_size != size) {
ewprintf("Region is too large");
return (FALSE);
}
return (TRUE);
}
#define PREFIXLENGTH 40
static char prefix_string[PREFIXLENGTH] = {'>', '\0'};
/*
* Prefix the region with whatever is in prefix_string. Leaves dot at the
* beginning of the line after the end of the region. If an argument is
* given, prompts for the line prefix string.
*/
/* ARGSUSED */
int
prefixregion(int f, int n)
{
struct line *first, *last;
struct region region;
char *prefix = prefix_string;
int nline;
int s;
if (curbp->b_flag & BFREADONLY) {
ewprintf("Buffer is read-only");
return (FALSE);
}
if ((f == TRUE) && ((s = setprefix(FFRAND, 1)) != TRUE))
return (s);
/* get # of lines to affect */
if ((s = getregion(®ion)) != TRUE)
return (s);
first = region.r_linep;
last = (first == curwp->w_dotp) ? curwp->w_markp : curwp->w_dotp;
for (nline = 1; first != last; nline++)
first = lforw(first);
/* move to beginning of region */
curwp->w_dotp = region.r_linep;
curwp->w_doto = region.r_offset;
/* for each line, go to beginning and insert the prefix string */
while (nline--) {
(void)gotobol(FFRAND, 1);
for (prefix = prefix_string; *prefix; prefix++)
(void)linsert(1, *prefix);
(void)forwline(FFRAND, 1);
}
(void)gotobol(FFRAND, 1);
return (TRUE);
}
/*
* Set line prefix string. Used by prefixregion.
*/
/* ARGSUSED */
int
setprefix(int f, int n)
{
char buf[PREFIXLENGTH], *rep;
int retval;
if (prefix_string[0] == '\0')
rep = eread("Prefix string: ", buf, sizeof(buf),
EFNEW | EFCR);
else
rep = eread("Prefix string (default %s): ", buf, sizeof(buf),
EFNUL | EFNEW | EFCR, prefix_string);
if (rep == NULL)
return (ABORT);
if (rep[0] != '\0') {
(void)strlcpy(prefix_string, rep, sizeof(prefix_string));
retval = TRUE;
} else if (rep[0] == '\0' && prefix_string[0] != '\0') {
/* CR -- use old one */
retval = TRUE;
} else
retval = FALSE;
return (retval);
}
int
region_get_data(struct region *reg, char *buf, int len)
{
int i, off;
struct line *lp;
off = reg->r_offset;
lp = reg->r_linep;
for (i = 0; i < len; i++) {
if (off == llength(lp)) {
lp = lforw(lp);
if (lp == curbp->b_headp)
break;
off = 0;
buf[i] = '\n';
} else {
buf[i] = lgetc(lp, off);
off++;
}
}
buf[i] = '\0';
return (i);
}
void
region_put_data(const char *buf, int len)
{
int i;
for (i = 0; buf[i] != '\0' && i < len; i++) {
if (buf[i] == '\n')
lnewline();
else
linsert(1, buf[i]);
}
}