home *** CD-ROM | disk | FTP | other *** search
- /* modify.c */
-
- /* This file contains the low-level file modification functions:
- * delete(frommark, tomark) - removes line or portions of lines
- * add(frommark, text) - inserts new text
- * change(frommark, tomark, text) - delete, then add
- */
-
- #include "config.h"
- #include "vi.h"
-
- #ifdef DEBUG
- # include <stdio.h>
- static FILE *dbg;
-
- /*VARARGS1*/
- debout(msg, arg1, arg2, arg3, arg4, arg5)
- char *msg, *arg1, *arg2, *arg3, *arg4, *arg5;
- {
- if (!dbg)
- {
- dbg = fopen("debug.out", "w");
- setbuf(dbg, (FILE *)0);
- }
- fprintf(dbg, msg, arg1, arg2, arg3, arg4, arg5);
- }
- #endif /* DEBUG */
-
- /* delete a range of text from the file */
- void delete(frommark, tomark)
- MARK frommark; /* first char to be deleted */
- MARK tomark; /* AFTER last char to be deleted */
- {
- int i; /* used to move thru logical blocks */
- REG char *scan; /* used to scan thru text of the blk */
- REG char *cpy; /* used when copying chars */
- BLK *blk; /* a text block */
- long l; /* a line number */
- MARK m; /* a traveling version of frommark */
-
- #ifdef DEBUG
- debout("delete(%ld.%d, %ld.%d)\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark));
- #endif
-
- /* if not deleting anything, quit now */
- if (frommark == tomark)
- {
- return;
- }
-
- /* This is a change */
- changes++;
- significant = TRUE;
-
- /* if this is a multi-line change, then we'll have to redraw */
- if (markline(frommark) != markline(tomark))
- {
- mustredraw = TRUE;
- redrawrange(markline(frommark), markline(tomark), markline(frommark));
- }
-
- /* adjust marks 'a through 'z and '' as needed */
- l = markline(tomark);
- for (i = 0; i < NMARKS; i++)
- {
- if (mark[i] < frommark)
- {
- continue;
- }
- else if (mark[i] < tomark)
- {
- mark[i] = MARK_UNSET;
- }
- else if (markline(mark[i]) == l)
- {
- if (markline(frommark) == l)
- {
- mark[i] -= markidx(tomark) - markidx(frommark);
- }
- else
- {
- mark[i] -= markidx(tomark);
- }
- }
- else
- {
- mark[i] -= MARK_AT_LINE(l - markline(frommark));
- }
- }
-
- /* Reporting... */
- if (markidx(frommark) == 0 && markidx(tomark) == 0)
- {
- rptlines = markline(tomark) - markline(frommark);
- rptlabel = "deleted";
- }
-
- /* find the block containing frommark */
- l = markline(frommark);
- for (i = 1; lnum[i] < l; i++)
- {
- }
-
- /* process each affected block... */
- for (m = frommark;
- m < tomark && lnum[i] < INFINITY;
- m = MARK_AT_LINE(lnum[i - 1] + 1))
- {
- /* fetch the block */
- blk = blkget(i);
-
- /* find the mark in the block */
- scan = blk->c;
- for (l = markline(m) - lnum[i - 1] - 1; l > 0; l--)
- {
- while (*scan++ != '\n')
- {
- }
- }
- scan += markidx(m);
-
- /* figure out where the changes to this block end */
- if (markline(tomark) > lnum[i])
- {
- cpy = blk->c + BLKSIZE;
- }
- else if (markline(tomark) == markline(m))
- {
- cpy = scan - markidx(m) + markidx(tomark);
- }
- else
- {
- cpy = scan;
- for (l = markline(tomark) - markline(m);
- l > 0;
- l--)
- {
- while (*cpy++ != '\n')
- {
- }
- }
- cpy += markidx(tomark);
- }
-
- /* delete the stuff by moving chars within this block */
- while (cpy < blk->c + BLKSIZE)
- {
- *scan++ = *cpy++;
- }
- while (scan < blk->c + BLKSIZE)
- {
- *scan++ = '\0';
- }
-
- /* adjust tomark to allow for lines deleted from this block */
- tomark -= MARK_AT_LINE(lnum[i] + 1 - markline(m));
-
- /* if this block isn't empty now, then advance i */
- if (*blk->c)
- {
- i++;
- }
-
- /* the buffer has changed. Update hdr and lnum. */
- blkdirty(blk);
- }
-
- /* must have at least 1 line */
- if (nlines == 0)
- {
- blk = blkadd(1);
- blk->c[0] = '\n';
- blkdirty(blk);
- cursor = MARK_FIRST;
- }
- }
-
-
- /* add some text at a specific place in the file */
- void add(atmark, newtext)
- MARK atmark; /* where to insert the new text */
- char *newtext; /* NUL-terminated string to insert */
- {
- REG char *scan; /* used to move through string */
- REG char *build; /* used while copying chars */
- int addlines; /* number of lines we're adding */
- int lastpart; /* size of last partial line */
- BLK *blk; /* the block to be modified */
- int blkno; /* the logical block# of (*blk) */
- REG char *newptr; /* where new text starts in blk */
- BLK buf; /* holds chars from orig blk */
- BLK linebuf; /* holds part of line that didn't fit */
- BLK *following; /* the BLK following the last BLK */
- int i;
- long l;
-
- #ifdef DEBUG
- debout("add(%ld.%d, \"%s\")\n", markline(atmark), markidx(atmark), newtext);
- #endif
- #ifdef lint
- buf.c[0] = 0;
- #endif
- /* if not adding anything, return now */
- if (!*newtext)
- {
- return;
- }
-
- /* This is a change */
- changes++;
- significant = TRUE;
-
- /* count the number of lines in the new text */
- for (scan = newtext, lastpart = addlines = 0; *scan; )
- {
- if (*scan++ == '\n')
- {
- addlines++;
- lastpart = 0;
- }
- else
- {
- lastpart++;
- }
- }
-
- /* Reporting... */
- if (lastpart == 0 && markidx(atmark) == 0)
- {
- rptlines = addlines;
- rptlabel = "added";
- }
-
- /* extract the line# from atmark */
- l = markline(atmark);
-
- /* if more than 0 lines, then we'll have to redraw the screen */
- if (addlines > 0)
- {
- mustredraw = TRUE;
- if (markidx(atmark) == 0 && lastpart == 0)
- {
- redrawrange(l, l, l + addlines);
- }
- else
- {
- /* make sure the last line gets redrawn -- it was
- * split, so its appearance has changed
- */
- redrawrange(l, l + 1L, l + addlines + 1L);
- }
- }
-
- /* adjust marks 'a through 'z and '' as needed */
- for (i = 0; i < NMARKS; i++)
- {
- if (mark[i] < atmark)
- {
- /* earlier line, or earlier in same line: no change */
- continue;
- }
- else if (markline(mark[i]) > l)
- {
- /* later line: move down a whole number of lines */
- mark[i] += MARK_AT_LINE(addlines);
- }
- else
- {
- /* later in same line */
- if (addlines > 0)
- {
- /* multi-line add, which split this line:
- * move down, and possibly left or right,
- * depending on where the split was and how
- * much text was inserted after the last \n
- */
- mark[i] += MARK_AT_LINE(addlines) + lastpart - markidx(atmark);
- }
- else
- {
- /* totally within this line: move right */
- mark[i] += lastpart;
- }
- }
- }
-
- /* get the block to be modified */
- for (blkno = 1; lnum[blkno] < l && lnum[blkno + 1] < INFINITY; blkno++)
- {
- }
- blk = blkget(blkno);
- buf = *blk;
-
- /* figure out where the new text starts */
- for (newptr = buf.c, l = markline(atmark) - lnum[blkno - 1] - 1;
- l > 0;
- l--)
- {
- while (*newptr++ != '\n')
- {
- }
- }
- newptr += markidx(atmark);
-
- /* keep start of old block */
- build = blk->c + (newptr - buf.c);
-
- /* fill this block (or blocks) from the newtext string */
- while (*newtext)
- {
- while (*newtext && build < blk->c + BLKSIZE - 1)
- {
- *build++ = *newtext++;
- }
- if (*newtext)
- {
- /* save the excess */
- for (scan = linebuf.c + BLKSIZE;
- build > blk->c && build[-1] != '\n';
- )
- {
- *--scan = *--build;
- }
-
- /* write the block */
- while (build < blk->c + BLKSIZE)
- {
- *build++ = '\0';
- }
- blkdirty(blk);
-
- /* add another block */
- blkno++;
- blk = blkadd(blkno);
-
- /* copy in the excess from last time */
- for (build = blk->c; scan < linebuf.c + BLKSIZE; )
- {
- *build++ = *scan++;
- }
- }
- }
-
- /* fill this block(s) from remainder of orig block */
- while (newptr < buf.c + BLKSIZE && *newptr)
- {
- while (newptr < buf.c + BLKSIZE
- && *newptr
- && build < blk->c + BLKSIZE - 1)
- {
- *build++ = *newptr++;
- }
- if (newptr < buf.c + BLKSIZE && *newptr)
- {
- /* save the excess */
- for (scan = linebuf.c + BLKSIZE;
- build > blk->c && build[-1] != '\n';
- )
- {
- *--scan = *--build;
- }
-
- /* write the block */
- while (build < blk->c + BLKSIZE)
- {
- *build++ = '\0';
- }
- blkdirty(blk);
-
- /* add another block */
- blkno++;
- blk = blkadd(blkno);
-
- /* copy in the excess from last time */
- for (build = blk->c; scan < linebuf.c + BLKSIZE; )
- {
- *build++ = *scan++;
- }
- }
- }
-
- /* see if we can combine our last block with the following block */
- if (lnum[blkno] < nlines && lnum[blkno + 1] - lnum[blkno] < (BLKSIZE >> 6))
- {
- /* hey, we probably can! Get the following block & see... */
- following = blkget(blkno + 1);
- if (strlen(following->c) + (build - blk->c) < BLKSIZE - 1)
- {
- /* we can! Copy text from following to blk */
- for (scan = following->c; *scan; )
- {
- *build++ = *scan++;
- }
- while (build < blk->c + BLKSIZE)
- {
- *build++ = '\0';
- }
- blkdirty(blk);
-
- /* pretend the following was the last blk */
- blk = following;
- build = blk->c;
- }
- }
-
- /* that last block is dirty by now */
- while (build < blk->c + BLKSIZE)
- {
- *build++ = '\0';
- }
- blkdirty(blk);
- }
-
-
- /* change the text of a file */
- void change(frommark, tomark, newtext)
- MARK frommark, tomark;
- char *newtext;
- {
- int i;
- long l;
- char *text;
- BLK *blk;
-
- #ifdef DEBUG
- debout("change(%ld.%d, %ld.%d, \"%s\")\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark), newtext);
- #endif
-
- /* optimize for single-character replacement */
- if (frommark + 1 == tomark && newtext[0] && !newtext[1] && newtext[0] != '\n')
- {
- /* find the block containing frommark */
- l = markline(frommark);
- for (i = 1; lnum[i] < l; i++)
- {
- }
-
- /* get the block */
- blk = blkget(i);
-
- /* find the line within the block */
- for (text = blk->c, i = l - lnum[i - 1] - 1; i > 0; text++)
- {
- if (*text == '\n')
- {
- i--;
- }
- }
-
- /* replace the char */
- text += markidx(frommark);
- if (*text == newtext[0])
- {
- /* no change was needed - same char */
- return;
- }
- else if (*text != '\n')
- {
- /* This is a change */
- changes++;
- significant = TRUE;
- ChangeText
- {
- *text = newtext[0];
- blkdirty(blk);
- }
- return;
- }
- /* else it is a complex change involving newline... */
- }
-
- /* couldn't optimize, so do delete & add */
- ChangeText
- {
- delete(frommark, tomark);
- add(frommark, newtext);
- rptlabel = "changed";
- }
- }
-