+++ /dev/null
-/*
- * Tiddles: a tiny editor
- *
- * Work in progress. Just keeping it in git while it is worked on
- */
-
-struct line {
- uint32_t offset; /* Offset in base file */
- uint32_t newoff; /* Offset during reconstruct */
- uint8_t *ptr; /* If in memory */
- uint8_t flags;
-#define L_DIRTY 1
-#define L_EXTEND 2 /* In the spew file not the original */
- uint8_t len; /* Disc len */
- uint8_t mlen; /* Length in memory */
- uint8_t mspc; /* Allocation size in memory */
-};
-
-
-/* Multi purpose disc buffer */
-static char ibuf[512];
-static int ibuflen;
-static char *ibufptr;
-static int ibuffd;
-
-/* Multipurpose second buffer */
-static char lbuf[256];
-
-static int in_fd, out_fd, spew_fd;
-static off_t spew_pos;
-
-/* Simple interface for buffered file reading */
-static int ibuf_getmore(void)
-{
- ibuflen = read(ibuffd, ibuf, 512);
- if (ibuflen <= 0)
- return 0;
- ibufptr = ibuf;
- return ibuflen;
-}
-
-static int getch(void)
-{
- if (ibufptr == ibuf + ibuflen)
- if (ibuf_getmore(ibuffd) == 0)
- return -1;
-
- return *ibufptr++;
-}
-
-static int open_in(int in_fd)
-{
- ibuffd = fd;
- return ibuf_getmore();
-}
-
-/* Tunable allocation strategy */
-
-/* Bytes of spew space to allocate for a given line. We want to avoid
- excess but also avoid lots of extra spew creation */
-static uint8_t spew_size(uint8_t l)
-{
- if (l < 128)
- return 128;
- return 256;
-}
-
-/* Bytes of memory to allocate for a given line */
-static uint8_t spew_size(uint8_t l)
-{
- if (l < 48)
- return 64;
- if (l < 80)
- return 96;
- if (l < 128)
- return 160;
- return 256;
-}
-
-/*
- * Add a record to the spew file and update its line information
- * accordingly.
- */
-
-static int append_spew(struct line *l)
-{
- int bytes;
- if (lseek(spew_fd, spew_pos, 0)) != spew_pos)
- return -1;
- bytes = write(spew_fd, l->buf, l->mlen);
- if (bytes != l->mlen)
- return -1;
- l->offset = spew_pos;
- l->flags |= L_EXTEND;
- l->len = l->mlen;
- spew_pos += spew_size(l);
-}
-
-/*
- * Update a line in the disc file
- */
-
-static int write_bytes(int ofd, int infd, struct line *l)
-{
- uint8_t *bp = l->ptr;
- /* FIXME: needs buffer algorithms */
-
- if (bp != NULL)
- l->len = l->mlen; /* Disc length becomes mem length */
- else {
- /* Doing a disc to disc transfer */
- bp = lbuf;
- if (infd == -1)
- panic("no in no buf");
- if (read(infd, lbuf, l->len) != len)
- return -1;
- }
- if (write(ofd, bp, l->len) != len)
- return -1;
- return 0;
-}
-
-/*
- * Reconstruct the edits we have made into a single coherent
- * file on out_fd. At this point spew can be discarded. in and out
- * must not be the same.
- */
-
-static int reconstruct(void)
-{
- struct line *lp = lines;
- uint32_t pos = 0; /* In output */
- int err;
-
- while(lp < lines_end) {
- if (lp->flags & L_EXTEND)
- err = write_bytes(out_fd, spew_fd, lp);
- else
- err = write_bytes(out_fd, in_fd, lp);
- if (err)
- return -1;
- lp->newoff = pos;
- pos += len;
- lp++;
- }
-
- lp = lines;
- while (lp < lines_end) {
- lp->offset = lp->newoff;
- lp->flags &= ~(L_DIRTY|L_EXTEND);
- }
- spew_pos = 0L;
- return 0;
-
-}
-
-/*
- * Write a line back to disc. If it fits in its existing space in the
- * spew file then put it back there. If not then add it to the spew file
- * and we will rebuild later. Don't write back to in, in is the input file
- * and sacrosanct until we correctly write out a final new file.
- */
-
-static int write_back(struct line *l)
-{
- int spc = spew_size(l->len);
- if (l->ptr == NULL)
- panic("dirty no ptr");
- if (!(l->flags & L_EXTEND) || spc < l->mlen) {
- if (append_spew(l) == -1)
- return -1;
- } else {
- /* Reuse existing spew */
- if (write_bytes(spew_fd, -1, l) == -1)
- return -1;
- }
- l->flags &= ~L_DIRTY;
- return 0;
-}
-
-/* Flush out the lines we don't need.
- We should be smarter about this - keep a pool measure and
- trim when we hit it ??
- */
-
-static int trim_window(struct line *s, struct line *e)
-{
- if (s == NULL)
- return;
- while (s < e) {
- if (s->flags & L_DIRTY) {
- if (writeback(s) == -1)
- return -1;
- free(s->buf);
- s->buf = NULL;
- }
- s++;
- }
-}
-
-/* Make sure the visible window of lines is present in memory */
-static int load_window(struct line *l, int num)
-{
- struct line *le = l + num;
-
- if (trim_buffer(viewstart, l))
- return -1;
- if (trim_buffer(le, viewend))
- return -1;
-
- while (l < le) {
- if (l->buf == NULL) {
- readin(l);
- l++;
- }
- viewstart = l;
- viewend = le;
- return 0;
-}
-
-static int delete_line(struct line *l)
-{
- /* We take a hit here but it saves a lot of data structure crap elsehwere.
- We could defer deletes but it makes things like goto harder, unless we
- defer until we next need a line finding etc .. still icky */
- if (l->ptr)
- free(l->ptr);
- memmove(l, l + 1, lines_end - (l + 1));
- lines_end--;
- /* FIXME: count this into spew rebuild hints ? */
- return 0;
-}
-
-static int insert_line(struct line *l)
-{
- void *p;
-
- if (lines_end >= lines_realend) {
- /* We don't want to do this every few lines */
- lines_realend += 256;
-
- /* TODO: Flush everything we can here and retry on fail. If that
- fails we could write the entire thing out and restart the load
- and edit */
-
- p = malloc((lines_realend - lines) * sizeof(struct line));
- if (p == NULL) {
- lines_realend -= 256;
- /* Consider flushing the file entirely and reloading our window */
- return NULL;
- }
- memcpy(p, lines, (lines_end - lines) * sizeof(struct line));
- }
- lines_end++;
- memmove(l, l+1, lines_end - (l + 1));
- l->flags = L_DIRTY;
- l->offset = 0L; /* Doesn't matter as will go to spew */
- l->ptr = malloc(64);
- l->len = 0;
- l->mlen = 1;
- *l->ptr = '\n';
- l->mspc = 64;
- if (l->ptr == NULL) {
- delete_line(l);
- return NULL;
- }
- return l;
-}
-
-static int line_insert(struct line *l, int off, uint8_t c)
-{
- if (!l->ptr)
- panic("insnoptr");
- l->flags |= L_DIRTY;
- /* Maximum line size */
- if (off > 255)
- return -1;
- if (off >= l->mspc) {
- size = line_size(off);
- uint8_t *p = malloc(size);
- if (p == NULL)
- return -1;
- l->mspc = size;
- l->mlen++;
- memmove(p, l->ptr, off);
- p[off] = c;
- memmove(p + off + 1, l->ptr + off, l->mlen - off);
- free(l->ptr);
- l->ptr = p;
- return 0;
- }
- memmove(l->ptr + off + 1, l->ptr + off, l->mlen - off);
- l->ptr[off] = c;
- return 0;
-}
-
-static int line_delete(struct line *l, int off)
-{
- memove(l->ptr + off, l->ptr + off + 1, l->mlen - off);
- l->mlen --;
- l->flags |= L_DIRTY;
- return 0;
-}
-
-static int line_wipe(struct line *l, int off)
-{
- if (l->mlen) {
- l->mlen = 0;
- l->flags |= L_DIRTY;
- }
- return 0;
-}
-
-static int line_replace(struct line *l, int off, uint8_t c)
-{
- uint8_t *p = l->ptr + off;
- if (off >= l->mlen)
- panic("reptl");
- if (*p != c) {
- *p = c;
- l->flags |= L_DIRTY;
- }
-}
-
-/* Join two lines */
-
-static int line_join(struct line *a, struct line *b)
-{
- int n = 0;
- int nlen = a->mlen + b->mlen;
-
- if (nlen > a->mspc) {
- uint8_t *p;
- size = line_size(nlen);
- p = malloc(size);
- if (p == NULL)
- return -1;
- a->mspc = size;
- }
- if (a->ptr[a->mlen-1] == '\n')
- a->mlen--;
- memcpy(a->ptr + a->mlen, b->ptr, b->mlen);
- a->mlen += b->mlen;
- return line_delete(b);
-}
-
-/* Load the input file: FIXME: what to do about overlong lines */
-
-static int load_file(int infd)
-{
- struct stat s;
- int lnum; /* Guess of size */
- uint8_t *lp = lbuf;
- uint8_t *le = lbuf + sizeof(lbuf);
- int c;
- long pos = 0;
- long lpos;
-
- if (fstat(infd, &s) == -1 || !S_ISREG(s.st_mode))
- return -1;
- lnum = s.st_size / 32;
- lnum /= 32;
- lnum += 256;
- /* FIXME: overflow check ? */
- lines = malloc(lnum * sizeof(struct line));
- if (lines == NULL)
- return -1;
- memset(lines, 0, lnum * sizeof(struct line));
- lines_realend = lines + lnum;
- lines_end = lines;
-
- if (open_in(infd) == -1)
- return -1;
-
- lpos = pos; /* Save offset */
- while((c = getch()) != -1) {
- if (c == '\n') {
- l = insert_line(lines_end);
- if (l == NULL)
- return -1;
- l->pos = lpos;
- l->len = pos - lpos + 1; /* We keep the \n */
- lpos = pos + 1;
- } else {
- *lp++ = c;
- if (lp == le) {
- /* line too long ?? truncate or wrap ?? FIXME */
- lp--;
- }
- }
- pos++;
- }
- if (pos != lpos) {
- insert_line(lines_end);
- l->pos = lpos;
- l->len = pos - lpos;
- }
- return 0;
-}
-
-/* Video layer: we need termcap in the end */
-
-void crlf(void)
-{
- write(1, "\n");
-}
-
-void home(void)
-{
- write(1, "\033H",2);
-}
-
-static char moveto_buf[4] = "\033Y ";
-
-void moveto(int y, int x)
-{
- moveto_buf[2] = y + ' ';
- moveto_buf[3] = x + ' ';
- write(1, moveto_buf, 4);
-}
-
-
-void setcursor(void)
-{
- moveto(cursory, cursorx);
- cursor_valid = 1;
-}
-
-/* FIXME: end line without \n ?? */
-
-static void reload_window(void)
-{
- load_window(cursorline, num_lines);
-}
-
-static void redraw_line(struct line *l)
-{
- int n = l->mlen - viewleft;
- n--;
- if (n)
- write(1, l->ptr + viewleft, n);
- /* Actually it might be valid in a few cases.. so maybe check one day */
- cursorvalid = 0;
-}
-
-static void redraw_status()
-{
-}
-
-/* FIXME: need to blank lines below the end of file ! */
-
-static void redraw(void)
-{
- struct line *l = viewstart;
- home();
- while(l < viewend)
- redraw_line(l);
- crlf();
- l++;
- }
- redraw_status();
- crlf();
-}
-
-static void redraw_down(void)
-{
- struct line *l = cursorline;
- moveto(cursory, 0);
- while(l < viewend)
- redraw_line(l);
- crlf();
- l++;
- }
- redraw_status();
- crlf();
-}
-
-static void view_adjust(int shift)
-{
- if (viewleft + shift < 0)
- shift = -viewleft;
- cursorx -= shift;
- viewleft += shift;
- async_dirty = 1; /* Catch up display when we get a gap */
- cursorvalid = 0;
-}
-
-static void vert_adjust_up(void)
-{
- int n = cursorline - lines;
- if (n > bottom_line / 2)
- n = bottom_line / 2;
- cursory += n;
- cursorline -= n;
- async_dirty = 1;
- cursorvalid = 0;
-}
-
-static void vert_adjust_down(void)
-{
- int n = lines_end - cursorline;
- if (n > bottom_line / 2)
- n = bottom_line / 2;
- cursory -= n;
- cursorline += n;
- async_dirty = 1;
- cursorvalid = 0;
-}
-
-
-/* Special characters are not handled here yet */
-static void ins_at_cursor(uint8_t c)
-{
-
- if (c == '\n') {
- if(insert_line(cursorline + 1) == -1) {
- beep();
- return;
- }
- redraw_below();
- return;
- }
-
- /* FIXME: play with this - probably better to look ahead if line right
- is off screen */
- if (cursorx == right_column)
- /* Assume display >= 16 wide! */
- view_adjust(16);
-
- if (line_insert(cursorline, cursorx, c) == -1) {
- beep();
- return;
- }
-
- if (cursorx == cursorline->mlen - viewleft - 1 && cursorvalid)
- write(1, &c, 1);
- else if (cursorvalid) {
- /* Bytes left in line */
- int n = cursorline->mlen - viewleft - cursorx - 1;
- int r = right_column - cursorright;
- if (n > r)
- n = r;
- if (n) {
- write(1, cursorline->ptr + viewleft, n);
- cursorvalid = 0;
- }
- }
- else /* Hard .. */
- redraw_line(cursorline);
- cursorx++;
-}
-
-static void del_at_cursor(uint8_t c)
-{
- int i;
- if (cursorx == 0 && viewleft == 0) {
- if (cursorline == line) /* Start of file */
- return;
- cursorline--;
- if (line_join(cursorline, cursorline + 1) == -1) {
- beep();
- return;
- }
- reload_window();
- redraw_down(cursorline);
- return;
- }
- if (cursorx < 16 && viewleft)
- view_adjust(-16); /* Assumes we always work in 16s! */
- if (line_delete(cursorline, cursorx + viewleft) == -1) {
- beep();
- return;
- }
- if (cursorx == cursorline->mlen - viewleft - 1 && cursorvalid)
- backspace();
- write(1, " ", 1);
- backspace();
- /* Below is common in ins/del - split this bit out */
- } else if (cursorvalid) {
- /* Bytes left in line */
- int n = cursorline->mlen - viewleft - cursorx - 1;
- int r = right_column - cursorright;
- if (n > r)
- n = r;
- if (n) {
- write(1, cursorline->ptr + viewleft, n);
- cursorvalid = 0;
- }
- }
- else /* Hard .. */
- redraw_line(cursorline);
-}
-
-static void cursor_left(void)
-{
- if (cursorx < 16 && viewleft)
- view_adjust(-16);
- if (cursorx) {
- cursorx--;
- setcursor();
- }
- else
- cursor_up();
-}
-
-static void cursor_right(void)
-{
- if (cursorx == right_column)
- view_adjust(16);
- if (cursorx < cursorline->mlen) {
- cursorx++;
- setcursor();
- }
- else
- cursor_down();
-}
-
-static void cursor_up(void)
-{
- if (cursorline == line)
- return;
-
- if (cursory < 2 && viewdown)
- vert_adjust_up();
- cursory--;
- cursorline--;
- if (cursorx > cursorline->mlen)
- cursorx = cursorline->mlen;
- setcursor();
-}
-
-static void cursor_down(void)
-{
- if (cursorline == line_end - 1)
- return;
- if (cursory >= bottom_row) /* FIXME need to consider if lines below
- screen and scroll earlier */
- vert_adjust_down();
- cursory++;
- cursorline++;
- if (cursorx > cursorline->mlen)
- cursorx = cursorline->mlen;
- setcursor();
-}
-