mse/src/scratch.c

465 lines
8.8 KiB
C

/*
** scratch.c
**
** scratch file handling for se screen editor.
**
** This file will use the line handling methodology presented in Software
** Tools In Pascal, *without* changing the way any of the routines are
** called.
**
** Bascially, the lines are always kept in order in the Buf array.
** Thus, lines 1 through 5 are in Buf[1] through Buf[5]. blkmove() and
** reverse() do the work of moving lines around in the buffer. The alloc()
** routine, therefore, always allocates the first empty slot, which will be
** at Lastln + 1, if there is room.
**
** Deleted lines are kept at the end of the buffer. Limbo points to the first
** line in the group of lines which were last deleted, or else Limbo == NOMORE.
**
** It is a very good idea to read the chapters on editing in BOTH editions of
** Software Tools, before trying to muck with this. It also helps to be a
** little bit off the wall....
**
** In fact, I would go as far as saying, "If you touch this, it will break.
** It is held together with chewing gum, scotch tape, and bobby pins."
**
** This file is part of mse, under GPLv3.
*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "se.h"
#include "docmd1.h"
#include "extern.h"
#include "main.h"
#include "misc.h"
#include "scratch.h"
#include "screen.h"
/* alloc --- allocate space for a new pointer block */
LINEDESC *alloc (LINEDESC **ptr)
{
int limbo_index = Limbo - Buf; /* use indices instead of pointers */
/* N.B.: this statement is meaningless if Limbo == NOMORE */
/* but if so, we don't use limbo_index anyway */
if (Limbo == NOMORE)
if (Lastln < (MAXBUF - 1) - 1) /* dumb zero based indexing! */
*ptr = &Buf[Lastln + 1];
else
*ptr = NOMORE;
else if (limbo_index - Lastln > 1)
*ptr = &Buf[Lastln + 1];
else
*ptr = NOMORE;
return (*ptr);
}
/* bump --- advance line number and corresponding index simultaneously */
void bump (int *line, LINEDESC **ix, int way)
{
if (way == FORWARD) /* increment line number */
{
(*ix)++;
if (*ix == &Buf[Lastln+1])
{
*line = 0;
*ix = Line0;
}
else
(*line)++;
}
else /* decrement line number */
{
if (*ix == Line0)
*line = Lastln;
else
(*line)--;
if (*ix == Line0)
*ix = &Buf[Lastln];
else
(*ix)--;
}
}
/* closef --- close a file */
void closef (int fd)
{
close (fd);
}
/* clrbuf --- purge scratch file */
void clrbuf (void)
{
if (Lastln > 0)
svdel (1, Lastln);
closef (Scr);
unlink (Scrname);
}
/* garbage_collect --- compress scratch file */
void garbage_collect (void)
{
char new_name [MAXLINE];
int i, new_scrend;
int new_fd;
LINEDESC *p;
makscr (&new_fd, new_name, MAXLINE);
remark ("collecting garbage");
new_scrend = 0;
for (p = Limbo, i = 1; i <= Limcnt; p++, i++)
{
gtxt (p);
seekf ((long) new_scrend * 8, new_fd);
writef (Txt, (int) p->Lineleng, new_fd);
p->Seekaddr = new_scrend;
new_scrend += (p->Lineleng + 7) / 8;
}
for (p = Line0, i = 0; i <= Lastln; p++, i++)
{
gtxt (p);
seekf ((long) new_scrend * 8, new_fd);
writef (Txt, (int) p->Lineleng, new_fd);
p->Seekaddr = new_scrend;
new_scrend += (p->Lineleng + 7) / 8;
}
closef (Scr);
unlink (Scrname);
Scr = new_fd;
memset (Scrname, '\0', MAXLINE);
snprintf (Scrname, MAXLINE-1, "%s", new_name);
Scrend = new_scrend;
Lost_lines = 0;
remark ("");
}
/* se_gettxt --- locate text for line, copy to txt */
LINEDESC *se_gettxt (int line)
{
LINEDESC *k;
k = getind (line);
gtxt (k);
return (k);
}
/* gtxt --- retrieve a line from the scratch file */
int gtxt (LINEDESC *ptr)
{
seekf ((long) ptr->Seekaddr * 8, Scr); /* position to start of file */
/*
* rounded Seekaddr to 8 byte sections, giving larger
* buffer space for text (*8)
*/
return (readf (Txt, (int) ptr->Lineleng, Scr) - 1);
}
/* inject --- insert a new line after curln */
int inject (char lin[])
{
int i;
int maklin ();
LINEDESC *k1, *k2;
LINEDESC *k3;
LINEDESC *getind ();
for (i = 0; lin [i] != EOS; )
{
i = maklin (lin, i, &k3); /* create a single line */
if (i == ERR)
{
Errcode = ECANTINJECT;
return (ERR);
}
Lastln++; /* update Lastln */
blkmove (Lastln, Lastln, Curln);
svins (Curln, 1);
Curln++; /* update Curln */
}
return (OK);
}
/* maklin --- construct a new line, add to scratch file */
int maklin (char lin[], int i, LINEDESC **newind)
{
char text [MAXLINE];
int l, n;
LINEDESC *ptr;
if (alloc (&ptr) == NOMORE) /* get space for pointer block */
return (ERR);
for (n = i; lin [n] != EOS; n++) /* find end of line */
if (lin [n] == '\n')
{
n++;
break;
}
if (n - i >= MAXLINE ) /* can't handle more than MAXLINE chars/line */
n = i + MAXLINE - 1;
l = n - i + 1; /* length of new line (including EOS) */
move_ (&lin [i], text, l); /* move new line into text */
text [l - 1] = EOS; /* add EOS */
ptr->Seekaddr = Scrend; /* will be added to end of scratch file */
ptr->Lineleng = l; /* line length including EOS */
ptr->Globmark = NO; /* not marked for Global command */
ptr->Markname = DEFAULTNAME; /* give it default mark name */
seekf ((long) Scrend * 8, Scr); /* go to end of scratch file */
writef (text, l, Scr); /* write line on scratch file */
Scrend += (l + 7) / 8; /* update end-of-file pointer */
Buffer_changed = YES;
*newind = ptr; /* return index of new line */
return (n); /* return next char of interest in lin */
}
/* makscr --- create a new scratch file */
void makscr (int *fd, char str[], size_t strsize)
{
char *expanded;
/* old scratch files were in /tmp, some systems use /usr/tmp.
* There isn't an easy way to determine /tmp vs. /usr/tmp, so use
* the user's home directory which is probably safer than /tmp
*/
expanded = expand_env ("$HOME/.scratch-XXXXXX");
memset (str, EOS, strsize);
snprintf (str, strsize - 1, "%s", expanded);
*fd = mkstemp (str);
if (*fd == -1)
{
error (YES, "can't create scratch file");
}
}
/* nextln --- get line after "line" */
int nextln (int line)
{
int ret;
ret = line + 1;
if (ret > Lastln)
ret = 0;
return (ret);
}
/* prevln --- get line before "line" */
int prevln (int line)
{
int ret;
ret = line - 1;
if (ret < 0)
ret = Lastln;
return (ret);
}
/* readf --- read count words from fd into buf */
int readf (char buf[], int count, int fd)
{
int ret;
ret = read (fd, buf, count);
if (ret != count)
error (YES, "Fatal scratch file read error");
return (ret);
}
/* seekf --- position file open on fd to pos */
int seekf (long pos, int fd)
{
long ret;
ret = lseek (fd, pos, 0); /* abs seek */
if (ret != pos)
error (YES, "Fatal scratch file seek error");
return (OK);
}
/* mkbuf --- create scratch file, initialize line 0 */
void mkbuf (void)
{
LINEDESC *p;
makscr (&Scr, Scrname, MAXLINE); /* create a scratch file */
Scrend = 0; /* initially empty */
Curln = 0;
Lastln = -1; /* alloc depends on this... */
Limbo = NOMORE; /* no lines in limbo */
Limcnt = 0;
Lost_lines = 0; /* no garbage in scratch file yet */
maklin ("", 0, &p); /* create an empty line */
p->Markname = EOS; /* give it an illegal mark name */
Line0 = p; /* henceforth and forevermore */
Lastln = 0;
}
/* sp_inject --- special inject for reading files */
LINEDESC *sp_inject (char lin[], int len, LINEDESC *line)
{
LINEDESC *k, *ret;
LINEDESC *ptr;
ret = alloc (&ptr);
if (ptr == NOMORE)
{
Errcode = ECANTINJECT;
return (ret);
}
ptr->Seekaddr = Scrend;
ptr->Lineleng = len + 1;
ptr->Globmark = NO;
ptr->Markname = DEFAULTNAME;
seekf ((long) Scrend * 8, Scr);
writef (lin, len + 1, Scr);
Scrend += ((len + 1) + 7) / 8; /* fudge for larger buffer */
Lastln++;
Buffer_changed = YES;
/*
* this part dependant on the fact that we set
* Curln = line in the routine do_read.
*/
blkmove (ptr - Buf, ptr - Buf, Curln); /* need line no's */
Curln++;
return (ret);
}
/* writef --- write count words from buf onto fd */
int writef (char buf[], int count, int fd)
{
int ret;
ret = write (fd, buf, count);
if (ret != count)
error (YES, "Fatal scratch file write error");
return (ret);
}
/* getind --- locate line index in buffer */
LINEDESC *getind (int line)
{
return (&Buf[line]);
}
/* blkmove -- use SWT in Pascal line handling */
void blkmove (int n1, int n2, int n3) /* move block of lines n1..n2 to after n3 */
{
if (n3 < n1 -1)
{
reverse (n3 + 1, n1 - 1);
reverse (n1, n2);
reverse (n3 + 1, n2);
}
else if (n3 > n2)
{
reverse (n1, n2);
reverse (n2 + 1, n3);
reverse (n1, n3);
}
}
/* reverse -- reverse buf[n1]..buf[n2] */
void reverse (int n1, int n2)
{
LINEDESC temp;
while (n1 < n2)
{
temp = Buf[n1];
Buf[n1] = Buf[n2];
Buf[n2] = temp;
n1++;
n2--;
}
}