Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: f0b27d1210
Fetching contributors…

Cannot retrieve contributors at this time

442 lines (385 sloc) 10.816 kb
/* region.c */
/* Copyright 2001 by Steve Kirkendall */
#include "elvis.h"
#ifdef FEATURE_REGION
# if USE_PROTOTYPES
static region_t *rgnfind(MARK from, MARK to, _char_ font, region_t *after,
region_t **lag);
static void rgnfree(region_t *region);
static void rgnclean(BUFFER buf);
# endif
#ifndef DEBUG_REGION
# define RGNCHECK
# define RGNCHECKFULL
#else
# define RGNCHECK rgncheck(__FILE__, __LINE__, ElvFalse)
# define RGNCHECKFULL rgncheck(__FILE__, __LINE__, ElvTrue)
# if USE_PROTOTYPES
static void rgncheck(char *file, int line, ELVBOOL full);
# endif
/* this stores the most recent places that called RGNCHECK */
static struct {
char *file;
int line;
} history[50];
/* this checks the regions for any weirdnes */
static void rgncheck(file, line, full)
char *file;
int line;
ELVBOOL full; /* TRUE for extra checks */
{
BUFFER buf;
region_t *rgn;
/* shift the history */
memmove(history + 1, history, sizeof history - sizeof history[0]);
history[0].file = file;
history[0].line = line;
/* for each buffer... */
for (buf = buflist((BUFFER)0); buf; buf = buflist(buf))
{
/* for each region in that buffer... */
for (rgn = buf->regions; rgn; rgn = rgn->next)
{
/* check it */
if ((int)rgn->from < 0x100000) abort();
if (rgn->from->buffer != buf) abort();
if (rgn->from->offset < 0) abort();
if (rgn->from->offset > o_bufchars(buf)) abort();
if ((int)rgn->to < 0x100000) abort();
if (rgn->to->buffer != buf) abort();
if (rgn->to->offset < 0) abort();
if (rgn->to->offset > o_bufchars(buf)) abort();
if (rgn->comment && *rgn->comment <= ' ') abort();
if (rgn->font && rgn->font > colornpermanent) abort();
/* check for overlaps & abutments */
if (full && rgn->next)
{
if (markoffset(rgn->to) > markoffset(rgn->next->from))
abort();
if (markoffset(rgn->to) == markoffset(rgn->next->from)
&& rgn->font == rgn->next->font
&& !CHARcmp(rgn->comment, rgn->next->comment))
abort();
}
}
}
}
#endif
/* find an existing region between "from" and "to", of the given type */
static region_t *rgnfind(from, to, font, after, lag)
MARK from; /* start of the region to search */
MARK to; /* end of the region to search */
_char_ font; /* font to search for, or '\0' for any */
region_t *after;/* NULL to start new search, else previous return val */
region_t **lag; /* reference to a pointer to region before found one */
{
region_t *r;
/* for each region... */
if (lag) *lag = after;
for (r = after ? after->next : markbuffer(from)->regions;
r && markoffset(r->from) < markoffset(to);
r = r->next)
{
/* did we find a match? */
if (markoffset(r->to) > markoffset(from)
&& (!font || r->font == font))
/* Yes! return it */
return r;
/* update the lag pointer */
if (lag) *lag = r;
}
/* no matching region was found */
return NULL;
}
/* free a region's resources */
static void rgnfree(region)
region_t *region; /* the region to free */
{
markfree(region->from);
markfree(region->to);
safefree(region->comment);
safefree(region);
}
/* clean up various forms of ugliness in the region list */
static void rgnclean(buf)
BUFFER buf; /* buffer whose list should be cleaned */
{
region_t *r; /* the new region */
region_t *lag; /* the region before it in the list */
region_t *next; /* the next region */
RGNCHECK;
/* for each region... */
for (lag = NULL, r = buf->regions; r; lag = r, r = next)
{
next = r->next;
/* if zero-length region, then delete it */
if (markoffset(r->from) >= markoffset(r->to))
{
if (lag)
lag->next = next;
else
buf->regions = next;
rgnfree(r);
/* loop, without advancing "lag" */
r = lag;
continue;
}
/* if next region is same as this one, merge them */
if (next
&& markoffset(r->to) == markoffset(next->from)
&& r->font == next->font
&& !CHARcmp(r->comment, next->comment))
{
marksetoffset(r->to, markoffset(next->to));
r->next = next->next;
rgnfree(next);
/* loop, without advancing "lag" or "r" */
next = r;
r = lag;
continue;
}
}
RGNCHECKFULL;
}
/* Handle undo. The tricky thing here is that a region may have been added
* by operation that's about to be undone, and if so then we *MUST* delete
* that region. Trying to keep it would confuse other region code.
*/
void regionundo(buf, keep)
BUFFER buf; /* buffer containing the regions */
struct umark_s *keep; /* describes marks of regions to be kept */
{
region_t *lag, *scan, *next;
int i;
RGNCHECKFULL;
#ifdef REGION_UNDO
/* if keep==NULL then we just wanted to inspect regions for bugs,
* and we've done that so return now.
*/
if (!keep)
return;
#endif
/* for each region... */
for (lag = NULL, scan = buf->regions; scan; scan = next)
{
next = scan->next;
/* is this region's marks listed in the undo version? */
for (i = 0; keep[i].mark; i++)
if (scan->from == keep[i].mark || scan->to == keep[i].mark)
break;
if (!keep[i].mark)
{
/* no -- delete this region */
if (lag)
lag->next = next;
else
buf->regions = next;
rgnfree(scan);
}
else
{
/* yes -- keep it. Advance to the next */
lag = scan;
}
}
}
/* delete regions in a given range, optionally matching a given font */
void regiondel(from, to, font)
MARK from; /* start of region to delete */
MARK to; /* end of region to delete */
_char_ font; /* font to use, or '\0' for all fonts */
{
region_t *doomed, *lag;
MARKBUF above, below;
char abovefont, belowfont;
CHAR *abovecomment, *belowcomment;
RGNCHECK;
/* for each doomed region... */
abovecomment = belowcomment = NULL;
lag = NULL;
while ((doomed = rgnfind(from, to, font, lag, &lag)) != NULL)
{
/* remove it from the list */
if (lag)
lag->next = doomed->next;
else
markbuffer(from)->regions = doomed->next;
/* if part of it extends outside the given region, remember it
* so we can add that portion back later.
*/
if (markoffset(doomed->from) < markoffset(from))
{
above = *doomed->from;
abovefont = doomed->font;
assert(abovecomment == NULL);
abovecomment = CHARdup(doomed->comment);
}
if (markoffset(doomed->to) > markoffset(to))
{
below = *doomed->to;
belowfont = doomed->font;
assert(belowcomment == NULL);
belowcomment = CHARdup(doomed->comment);
}
/* free this one's resources */
rgnfree(doomed);
}
/* add back parts of regions above and below the change */
if (abovecomment)
{
regionadd(&above, from, abovefont, abovecomment);
safefree(abovecomment);
}
if (belowcomment)
{
regionadd(to, &below, belowfont, belowcomment);
safefree(belowcomment);
}
RGNCHECK;
}
/* add a region, or extend an overlapping identical region */
void regionadd(from, to, font, comment)
MARK from; /* start of region to add */
MARK to; /* end of region to add */
_char_ font; /* font to use */
CHAR *comment; /* comment to use */
{
region_t *r; /* the new region */
region_t *lag; /* the region before it in the list */
/* Do nothing if it would be zero-length */
if (markoffset(from) >= markoffset(to))
return;
/* Delete any existing regions here. NOTE: Although regionadd() and
* regiondel() are mutually recursive, there is no danger of infinite
* recursion since regiondel() only calls regionadd() for areas which
* are known to be free of existing regions.
*/
regiondel(from, to, '\0');
/* create a new region */
r = (region_t *)safealloc(1, sizeof(region_t));
r->from = markdup(from);
r->to = markdup(to);
r->font = font;
r->comment = CHARdup(comment);
/* decide where to insert it */
(void)rgnfind(from, to, '\0', NULL, &lag);
/* insert it into the region list */
if (lag)
{
r->next = lag->next;
lag->next = r;
}
else
{
r->next = markbuffer(from)->regions;
markbuffer(from)->regions = r;
}
/* clean up the list by deleting zero-length regions, and merging
* adjacent, identical regions.
*/
rgnclean(markbuffer(from));
}
/* return the region containing a given MARK, or NULL if there is none */
region_t *regionfind(mark)
MARK mark; /* position, possibly within a region */
{
MARKBUF tmp;
(void)marktmp(tmp, markbuffer(mark), markoffset(mark) + 1);
return rgnfind(mark, &tmp, '\0', NULL, NULL);
}
/* This implements the :region, :unregion, and :chregion commands. */
RESULT ex_region(xinf)
EXINFO *xinf; /* details about the command */
{
char font, newfont;
CHAR *comment;
region_t *r;
CHAR buf[100], *build;
ELVBOOL any;
assert(xinf->command == EX_REGION || xinf->command == EX_UNREGION ||
xinf->command == EX_CHREGION);
RGNCHECKFULL;
/* convert the font name to a font code */
if (xinf->lhs)
font = colorfind(xinf->lhs);
else if (xinf->command == EX_UNREGION)
font = '\0';
else if (xinf->command == EX_CHREGION)
{
msg(MSG_ERROR, "missing lhs");
return RESULT_ERROR;
}
else
{
/* :region with no args -- list the regions */
/* now list it */
any = ElvFalse;
r = NULL;
while ((r = rgnfind(xinf->fromaddr, xinf->toaddr, '\0', r, NULL)) != NULL)
{
/* list this region */
any = ElvTrue;
long2CHAR(buf, markline(r->from));
build = buf + CHARlen(buf);
*build++ = ',';
long2CHAR(build, markline(r->to) - 1);
CHARcat(build, toCHAR(" region "));
CHARcat(build, colorinfo[(int)r->font].name);
build += CHARlen(build);
if (CHARcmp(r->comment, colorinfo[(int)r->font].name))
{
*build++ = ' ';
CHARcpy(build, r->comment);
build += CHARlen(build);
}
*build++ = '\n';
drawextext(xinf->window, buf, (int)(build - buf));
}
if (!any)
msg(MSG_INFO, "no regions");
return RESULT_COMPLETE;
}
/* default comment is same as face name */
comment = xinf->rhs ? xinf->rhs : xinf->lhs;
/* add or delete regions */
if (xinf->command == EX_REGION)
regionadd(xinf->fromaddr, xinf->toaddr, font, comment);
else if (xinf->command == EX_UNREGION)
regiondel(xinf->fromaddr, xinf->toaddr, font);
else /* EX_CHREGION */
{
/* parse the newfont and comment */
if (!xinf->rhs)
{
msg(MSG_ERROR, "missing rhs");
return RESULT_ERROR;
}
comment = CHARchr(xinf->rhs, ' ');
if (comment)
{
*comment++ = '\0';
while (*comment == ' ')
comment++;
if (*comment == '\0')
comment = NULL;
}
newfont = colorfind(xinf->rhs);
/* for each matching region... */
r = NULL;
while ((r = rgnfind(xinf->fromaddr, xinf->toaddr, font, r, NULL)) != NULL)
{
/* change the font & comment */
r->font = newfont;
if (comment || !CHARcmp(r->comment, xinf->lhs))
{
safefree(r->comment);
r->comment = CHARdup(comment ? comment : xinf->rhs);
}
}
/* clean up abutting regions, etc. */
rgnclean(markbuffer(xinf->fromaddr));
}
RGNCHECKFULL;
return RESULT_COMPLETE;
}
#endif /* FEATURE_REGION */
Jump to Line
Something went wrong with that request. Please try again.