Skip to content

Instantly share code, notes, and snippets.

@min4builder
Created May 22, 2023 20:28
Show Gist options
  • Save min4builder/55681d1a011286252f455e05013536c7 to your computer and use it in GitHub Desktop.
Save min4builder/55681d1a011286252f455e05013536c7 to your computer and use it in GitHub Desktop.
Gap buffer implementation
/* Hole manager for E. Written by: Joseph H. Allen, 9/10/89
Updated to ANSI C in 2023-05-12 by Lucas Ieks
Here's the buffer manager from an editor I wrote (and which I'm using right
now) and an explanation of the functions/macros provided. Like emacs, it does
not force the gap to be at the point.
The file is stored in memory like this:
lowest: buffer -> +---------+
|beginning|
| part |
hole -> +---------+
| Gap |
ehole -> +---------+
| end |
| part |
highest: filend -> +---------+
Other variables:
bufsiz size of malloc block which begins at 'buffer'
point where everything takes place
change becomes set after the buffer has changed
The functions and macros the rest of the editor would use have comments below.
types: position and size are of type 'size_t'
characters are of type 'char'
string is 'char *'
FILE is C library file pointer
All of these functions expect you not to goof: don't point past the end of
the buffer or try to read character from past the end.
In the newline search routines, the search stops at the beginning or end of file.
Also, they return true if the \n was found, false otherwise.
Missing from comp.editors listing:
fmsavfil(FILE,size) Save size bytes beginning at point into FILE
Do whatever you want with this but leave my name (JHA's) on it.
*/
#define _POSIX_C_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
size_t bufsiz; /* Size of buffer */
char *point; /* The point */
char *buffer; /* The buffer */
char *filend; /* First character not in buffer */
char *hole; /* Beginning of hole */
char *ehole; /* First character not in hole */
int changed=0; /* Set when file has been changed */
#define HOLESIZE 16384 /* Amount hole grows by */
/* Return size of gap. */
size_t fmholesize() { return ehole-hole; }
/* Return number of bytes between point and beg. of file */
size_t fmnote() { return point>=ehole?(point-buffer)-(ehole-hole):point-buffer; }
/* Return number of characters in file */
size_t fmsize() { return (filend-buffer)-(ehole-hole); }
/* Return true if at end of file */
int fmeof() { return point==hole?(ehole==filend):(point==filend); }
/* Set point to some offset from beginning of file */
void fmpoint(size_t x) { point=buffer+x; if(point>hole) point+=ehole-hole; }
/* Move point back one and return character under point */
char fmrgetc() { return point==ehole?*(point=hole-1):*--point; }
/* Initialize the buffer */
void fmopen()
{
buffer=(char *)malloc(bufsiz=HOLESIZE);
point=buffer;
hole=buffer;
ehole=buffer+HOLESIZE;
filend=ehole;
}
/* Make the malloc block 'buffer' at least s bytes larger than 'filend-buffer' */
void fmexpand(size_t amount)
{
if(filend+amount-buffer>bufsiz) {
char *old=buffer;
bufsiz=filend+amount+HOLESIZE-buffer;
buffer=(char *)realloc(buffer,bufsiz);
point+=buffer-old;
filend+=buffer-old;
hole+=buffer-old;
ehole+=buffer-old;
}
}
/* Position gap at the point */
void fmhole()
{
if(point==hole) return;
if(point==ehole) {
point=hole;
return;
}
if(point<hole) {
memmove(ehole-(hole-point),point,hole-point);
ehole=ehole-(hole-point);
hole=point;
} else {
memmove(hole,ehole,point-ehole);
hole+=point-ehole;
ehole=point;
point=hole;
}
}
/* Make gap at least s bytes in size */
void fmbig(size_t size)
{
if(size>fmholesize()) {
size+=HOLESIZE;
fmexpand(size);
memmove(ehole+size,ehole,filend-ehole);
ehole+=size;
filend+=size;
}
}
/* Get character under point */
char fmrc() { return point==hole?*(point=ehole):*point; }
/* Write a character at the point */
void fmwc(char c)
{
if(point==hole) point=ehole;
if(point==filend) {
fmexpand(1);
filend++;
}
*point=c;
changed=1;
}
/* Get character under point and advance point */
char fmgetc() { return point==hole?(point=ehole+1,*ehole):*point++; }
/* Write character under point and advance point */
void fmputc(char c)
{
if(point==hole) point=ehole;
if(point==filend) {
fmexpand(1);
filend++;
}
*point++=c;
changed=1;
}
/* Insert character at point */
void fminsc(char c)
{
if(point!=hole) fmhole();
if(hole==ehole) fmbig(1);
*hole++=c;
changed=1;
}
/* Delete x characters beginning at point */
void fmdel(size_t x)
{
if(point!=hole) fmhole();
ehole+=x;
changed=1;
}
/* Move point to first \n found (this won't move the point if there is a \n under it) */
int fmfnl()
{
while((point==hole ? point=ehole : point) != filend) {
if(*point=='\n')
return 1;
else
point++;
}
return 0;
}
/* Move point to first \n found in reverse direction
(this won't move move the point if there is a \n under it) */
int fmrnl()
{
if(fmrc()=='\n') return 1;
while((point==ehole?point=hole:point)!=buffer) {
if(*(--point)=='\n')
return 1;
}
return 0;
}
/* Advance point by one and then point to first \n found */
void fmnnl() { if(!fmeof()) { fmgetc(); fmfnl(); } }
/* Move point back one and then point to first \n found in reverse direction */
void fmnrnl() { if(fmnote()) { fmrgetc(); fmrnl(); } }
/* Insert a string of indicated size at the point */
void fminss(char *string, size_t size)
{
fmhole();
if(size>fmholesize())
fmbig(size);
memmove(hole,string,size);
hole+=size;
changed=1;
}
/* Return 0 if string matches the one under the point */
int fmcmp(char *string, size_t size)
{
char *x;
if(point==hole) point=ehole;
if(hole>point && hole<point+size && hole!=ehole) {
if(fmcmp(string,hole-point)) return 1;
else {
x=point;
point=ehole;
if(fmcmp(string+(hole-x),size-(hole-x))) {
point=x;
return 1;
} else {
point=x;
return 0;
}
}
} else {
x=point;
do
if(*(x++)!=*(string++)) return 1;
while(--size);
return 0;
}
}
int tupp(int c)
{
if(c>='a' && c<='z') return c+'A'-'a';
else return c;
}
/* Return 0 if string matches the one under the point (case insensitive) */
int fmicmp(char *string, size_t size)
{
char *x;
if(point==hole) point=ehole;
if(hole>point && hole<point+size && hole!=ehole) {
if(fmcmp(string,hole-point)) return 1;
else {
x=point;
point=ehole;
if(fmcmp(string+(hole-x),size-(hole-x))) {
point=x;
return 1;
} else {
point=x;
return 0;
}
}
} else {
x=point;
do
if(tupp(*(x++))!=tupp(*(string++))) return 1;
while(--size);
return 0;
}
}
int fmsave(FILE *file, size_t size)
{
if(!size) return 1;
if(point==hole) point=ehole;
if(hole>point && hole<point+size && hole!=ehole) {
if(hole-point!=fwrite(point,1,hole-point,file)) return 0;
if(size-(hole-point)!=fwrite(ehole,1,size-(hole-point),file)) return 0;
return 1;
} else
return size==fwrite(point,1,size,file);
}
/* Insert a file at the point (FILE is an open file)
returns true if no errors */
int fminsfil(FILE *file)
{
struct stat buf;
size_t amount;
fstat(fileno(file),&buf);
if(buf.st_size==0) return 1;
changed=1;
fmhole();
fmbig((size_t)buf.st_size);
amount=fread(hole,1,(size_t)buf.st_size,file);
hole+=amount;
return amount==(size_t)buf.st_size;
}
@min4builder
Copy link
Author

min4builder commented May 22, 2023

I found this on USENET. It's pretty simple, and could potentially be the base for a simple (or not) text editor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment