[9] | 1 | /* modify.c */
|
---|
| 2 |
|
---|
| 3 | /* This file contains the low-level file modification functions:
|
---|
| 4 | * delete(frommark, tomark) - removes line or portions of lines
|
---|
| 5 | * add(frommark, text) - inserts new text
|
---|
| 6 | * change(frommark, tomark, text) - delete, then add
|
---|
| 7 | */
|
---|
| 8 |
|
---|
| 9 | #include "config.h"
|
---|
| 10 | #include "vi.h"
|
---|
| 11 |
|
---|
| 12 | #ifdef DEBUG2
|
---|
| 13 | # include <stdio.h>
|
---|
| 14 | static FILE *dbg;
|
---|
| 15 |
|
---|
| 16 | /*VARARGS1*/
|
---|
| 17 | debout(msg, arg1, arg2, arg3, arg4, arg5)
|
---|
| 18 | char *msg, *arg1, *arg2, *arg3, *arg4, *arg5;
|
---|
| 19 | {
|
---|
| 20 | if (!dbg)
|
---|
| 21 | {
|
---|
| 22 | dbg = fopen("debug.out", "w");
|
---|
| 23 | if (!dbg)
|
---|
| 24 | return;
|
---|
| 25 | setbuf(dbg, (FILE *)0);
|
---|
| 26 | }
|
---|
| 27 | fprintf(dbg, msg, arg1, arg2, arg3, arg4, arg5);
|
---|
| 28 | }
|
---|
| 29 | #endif /* DEBUG2 */
|
---|
| 30 |
|
---|
| 31 | /* delete a range of text from the file */
|
---|
| 32 | void delete(frommark, tomark)
|
---|
| 33 | MARK frommark; /* first char to be deleted */
|
---|
| 34 | MARK tomark; /* AFTER last char to be deleted */
|
---|
| 35 | {
|
---|
| 36 | int i; /* used to move thru logical blocks */
|
---|
| 37 | REG char *scan; /* used to scan thru text of the blk */
|
---|
| 38 | REG char *cpy; /* used when copying chars */
|
---|
| 39 | BLK *blk; /* a text block */
|
---|
| 40 | long l; /* a line number */
|
---|
| 41 | MARK m; /* a traveling version of frommark */
|
---|
| 42 |
|
---|
| 43 | #ifdef DEBUG2
|
---|
| 44 | debout("delete(%ld.%d, %ld.%d)\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark));
|
---|
| 45 | #endif
|
---|
| 46 |
|
---|
| 47 | /* if not deleting anything, quit now */
|
---|
| 48 | if (frommark == tomark)
|
---|
| 49 | {
|
---|
| 50 | return;
|
---|
| 51 | }
|
---|
| 52 |
|
---|
| 53 | /* This is a change */
|
---|
| 54 | changes++;
|
---|
| 55 | significant = TRUE;
|
---|
| 56 |
|
---|
| 57 | /* supply clues to the redraw module */
|
---|
| 58 | redrawrange(markline(frommark), markline(tomark), markline(frommark));
|
---|
| 59 |
|
---|
| 60 | /* adjust marks 'a through 'z and '' as needed */
|
---|
| 61 | l = markline(tomark);
|
---|
| 62 | for (i = 0; i < NMARKS; i++)
|
---|
| 63 | {
|
---|
| 64 | if (mark[i] < frommark)
|
---|
| 65 | {
|
---|
| 66 | continue;
|
---|
| 67 | }
|
---|
| 68 | else if (mark[i] < tomark)
|
---|
| 69 | {
|
---|
| 70 | mark[i] = MARK_UNSET;
|
---|
| 71 | }
|
---|
| 72 | else if (markline(mark[i]) == l)
|
---|
| 73 | {
|
---|
| 74 | if (markline(frommark) == l)
|
---|
| 75 | {
|
---|
| 76 | mark[i] -= markidx(tomark) - markidx(frommark);
|
---|
| 77 | }
|
---|
| 78 | else
|
---|
| 79 | {
|
---|
| 80 | mark[i] -= markidx(tomark);
|
---|
| 81 | }
|
---|
| 82 | }
|
---|
| 83 | else
|
---|
| 84 | {
|
---|
| 85 | mark[i] -= MARK_AT_LINE(l - markline(frommark));
|
---|
| 86 | }
|
---|
| 87 | }
|
---|
| 88 |
|
---|
| 89 | /* Reporting... */
|
---|
| 90 | if (markidx(frommark) == 0 && markidx(tomark) == 0)
|
---|
| 91 | {
|
---|
| 92 | rptlines = markline(tomark) - markline(frommark);
|
---|
| 93 | rptlabel = "deleted";
|
---|
| 94 | }
|
---|
| 95 |
|
---|
| 96 | /* find the block containing frommark */
|
---|
| 97 | l = markline(frommark);
|
---|
| 98 | for (i = 1; lnum[i] < l; i++)
|
---|
| 99 | {
|
---|
| 100 | }
|
---|
| 101 |
|
---|
| 102 | /* process each affected block... */
|
---|
| 103 | for (m = frommark;
|
---|
| 104 | m < tomark && lnum[i] < INFINITY;
|
---|
| 105 | m = MARK_AT_LINE(lnum[i - 1] + 1))
|
---|
| 106 | {
|
---|
| 107 | /* fetch the block */
|
---|
| 108 | blk = blkget(i);
|
---|
| 109 |
|
---|
| 110 | /* find the mark in the block */
|
---|
| 111 | scan = blk->c;
|
---|
| 112 | for (l = markline(m) - lnum[i - 1] - 1; l > 0; l--)
|
---|
| 113 | {
|
---|
| 114 | while (*scan++ != '\n')
|
---|
| 115 | {
|
---|
| 116 | }
|
---|
| 117 | }
|
---|
| 118 | scan += markidx(m);
|
---|
| 119 |
|
---|
| 120 | /* figure out where the changes to this block end */
|
---|
| 121 | if (markline(tomark) > lnum[i])
|
---|
| 122 | {
|
---|
| 123 | cpy = blk->c + BLKSIZE;
|
---|
| 124 | }
|
---|
| 125 | else if (markline(tomark) == markline(m))
|
---|
| 126 | {
|
---|
| 127 | cpy = scan - markidx(m) + markidx(tomark);
|
---|
| 128 | }
|
---|
| 129 | else
|
---|
| 130 | {
|
---|
| 131 | cpy = scan;
|
---|
| 132 | for (l = markline(tomark) - markline(m);
|
---|
| 133 | l > 0;
|
---|
| 134 | l--)
|
---|
| 135 | {
|
---|
| 136 | while (*cpy++ != '\n')
|
---|
| 137 | {
|
---|
| 138 | }
|
---|
| 139 | }
|
---|
| 140 | cpy += markidx(tomark);
|
---|
| 141 | }
|
---|
| 142 |
|
---|
| 143 | /* delete the stuff by moving chars within this block */
|
---|
| 144 | while (cpy < blk->c + BLKSIZE)
|
---|
| 145 | {
|
---|
| 146 | *scan++ = *cpy++;
|
---|
| 147 | }
|
---|
| 148 | while (scan < blk->c + BLKSIZE)
|
---|
| 149 | {
|
---|
| 150 | *scan++ = '\0';
|
---|
| 151 | }
|
---|
| 152 |
|
---|
| 153 | /* adjust tomark to allow for lines deleted from this block */
|
---|
| 154 | tomark -= MARK_AT_LINE(lnum[i] + 1 - markline(m));
|
---|
| 155 |
|
---|
| 156 | /* if this block isn't empty now, then advance i */
|
---|
| 157 | if (*blk->c)
|
---|
| 158 | {
|
---|
| 159 | i++;
|
---|
| 160 | }
|
---|
| 161 |
|
---|
| 162 | /* the buffer has changed. Update hdr and lnum. */
|
---|
| 163 | blkdirty(blk);
|
---|
| 164 | }
|
---|
| 165 |
|
---|
| 166 | /* must have at least 1 line */
|
---|
| 167 | if (nlines == 0)
|
---|
| 168 | {
|
---|
| 169 | blk = blkadd(1);
|
---|
| 170 | blk->c[0] = '\n';
|
---|
| 171 | blkdirty(blk);
|
---|
| 172 | cursor = MARK_FIRST;
|
---|
| 173 | }
|
---|
| 174 | }
|
---|
| 175 |
|
---|
| 176 |
|
---|
| 177 | /* add some text at a specific place in the file */
|
---|
| 178 | void add(atmark, newtext)
|
---|
| 179 | MARK atmark; /* where to insert the new text */
|
---|
| 180 | char *newtext; /* NUL-terminated string to insert */
|
---|
| 181 | {
|
---|
| 182 | REG char *scan; /* used to move through string */
|
---|
| 183 | REG char *build; /* used while copying chars */
|
---|
| 184 | int addlines; /* number of lines we're adding */
|
---|
| 185 | int lastpart; /* size of last partial line */
|
---|
| 186 | BLK *blk; /* the block to be modified */
|
---|
| 187 | int blkno; /* the logical block# of (*blk) */
|
---|
| 188 | REG char *newptr; /* where new text starts in blk */
|
---|
| 189 | BLK buf; /* holds chars from orig blk */
|
---|
| 190 | BLK linebuf; /* holds part of line that didn't fit */
|
---|
| 191 | BLK *following; /* the BLK following the last BLK */
|
---|
| 192 | int i;
|
---|
| 193 | long l;
|
---|
| 194 |
|
---|
| 195 | #ifdef DEBUG2
|
---|
| 196 | debout("add(%ld.%d, \"%s\")\n", markline(atmark), markidx(atmark), newtext);
|
---|
| 197 | #endif
|
---|
| 198 | #ifdef lint
|
---|
| 199 | buf.c[0] = 0;
|
---|
| 200 | #endif
|
---|
| 201 | /* if not adding anything, return now */
|
---|
| 202 | if (!*newtext)
|
---|
| 203 | {
|
---|
| 204 | return;
|
---|
| 205 | }
|
---|
| 206 |
|
---|
| 207 | /* This is a change */
|
---|
| 208 | changes++;
|
---|
| 209 | significant = TRUE;
|
---|
| 210 |
|
---|
| 211 | /* count the number of lines in the new text */
|
---|
| 212 | for (scan = newtext, lastpart = addlines = 0; *scan; )
|
---|
| 213 | {
|
---|
| 214 | if (*scan++ == '\n')
|
---|
| 215 | {
|
---|
| 216 | addlines++;
|
---|
| 217 | lastpart = 0;
|
---|
| 218 | }
|
---|
| 219 | else
|
---|
| 220 | {
|
---|
| 221 | lastpart++;
|
---|
| 222 | }
|
---|
| 223 | }
|
---|
| 224 |
|
---|
| 225 | /* Reporting... */
|
---|
| 226 | if (lastpart == 0 && markidx(atmark) == 0)
|
---|
| 227 | {
|
---|
| 228 | rptlines = addlines;
|
---|
| 229 | rptlabel = "added";
|
---|
| 230 | }
|
---|
| 231 |
|
---|
| 232 | /* extract the line# from atmark */
|
---|
| 233 | l = markline(atmark);
|
---|
| 234 |
|
---|
| 235 | /* supply clues to the redraw module */
|
---|
| 236 | if ((markidx(atmark) == 0 && lastpart == 0) || addlines == 0)
|
---|
| 237 | {
|
---|
| 238 | redrawrange(l, l, l + addlines);
|
---|
| 239 | }
|
---|
| 240 | else
|
---|
| 241 | {
|
---|
| 242 | /* make sure the last line gets redrawn -- it was
|
---|
| 243 | * split, so its appearance has changed
|
---|
| 244 | */
|
---|
| 245 | redrawrange(l, l + 1L, l + addlines + 1L);
|
---|
| 246 | }
|
---|
| 247 |
|
---|
| 248 | /* adjust marks 'a through 'z and '' as needed */
|
---|
| 249 | for (i = 0; i < NMARKS; i++)
|
---|
| 250 | {
|
---|
| 251 | if (mark[i] < atmark)
|
---|
| 252 | {
|
---|
| 253 | /* earlier line, or earlier in same line: no change */
|
---|
| 254 | continue;
|
---|
| 255 | }
|
---|
| 256 | else if (markline(mark[i]) > l)
|
---|
| 257 | {
|
---|
| 258 | /* later line: move down a whole number of lines */
|
---|
| 259 | mark[i] += MARK_AT_LINE(addlines);
|
---|
| 260 | }
|
---|
| 261 | else
|
---|
| 262 | {
|
---|
| 263 | /* later in same line */
|
---|
| 264 | if (addlines > 0)
|
---|
| 265 | {
|
---|
| 266 | /* multi-line add, which split this line:
|
---|
| 267 | * move down, and possibly left or right,
|
---|
| 268 | * depending on where the split was and how
|
---|
| 269 | * much text was inserted after the last \n
|
---|
| 270 | */
|
---|
| 271 | mark[i] += MARK_AT_LINE(addlines) + lastpart - markidx(atmark);
|
---|
| 272 | }
|
---|
| 273 | else
|
---|
| 274 | {
|
---|
| 275 | /* totally within this line: move right */
|
---|
| 276 | mark[i] += lastpart;
|
---|
| 277 | }
|
---|
| 278 | }
|
---|
| 279 | }
|
---|
| 280 |
|
---|
| 281 | /* get the block to be modified */
|
---|
| 282 | for (blkno = 1; lnum[blkno] < l && lnum[blkno + 1] < INFINITY; blkno++)
|
---|
| 283 | {
|
---|
| 284 | }
|
---|
| 285 | blk = blkget(blkno);
|
---|
| 286 | buf = *blk;
|
---|
| 287 |
|
---|
| 288 | /* figure out where the new text starts */
|
---|
| 289 | for (newptr = buf.c, l = markline(atmark) - lnum[blkno - 1] - 1;
|
---|
| 290 | l > 0;
|
---|
| 291 | l--)
|
---|
| 292 | {
|
---|
| 293 | while (*newptr++ != '\n')
|
---|
| 294 | {
|
---|
| 295 | }
|
---|
| 296 | }
|
---|
| 297 | newptr += markidx(atmark);
|
---|
| 298 |
|
---|
| 299 | /* keep start of old block */
|
---|
| 300 | build = blk->c + (int)(newptr - buf.c);
|
---|
| 301 |
|
---|
| 302 | /* fill this block (or blocks) from the newtext string */
|
---|
| 303 | while (*newtext)
|
---|
| 304 | {
|
---|
| 305 | while (*newtext && build < blk->c + BLKSIZE - 1)
|
---|
| 306 | {
|
---|
| 307 | *build++ = *newtext++;
|
---|
| 308 | }
|
---|
| 309 | if (*newtext)
|
---|
| 310 | {
|
---|
| 311 | /* save the excess */
|
---|
| 312 | for (scan = linebuf.c + BLKSIZE;
|
---|
| 313 | build > blk->c && build[-1] != '\n';
|
---|
| 314 | )
|
---|
| 315 | {
|
---|
| 316 | *--scan = *--build;
|
---|
| 317 | }
|
---|
| 318 |
|
---|
| 319 | /* write the block */
|
---|
| 320 | while (build < blk->c + BLKSIZE)
|
---|
| 321 | {
|
---|
| 322 | *build++ = '\0';
|
---|
| 323 | }
|
---|
| 324 | blkdirty(blk);
|
---|
| 325 |
|
---|
| 326 | /* add another block */
|
---|
| 327 | blkno++;
|
---|
| 328 | blk = blkadd(blkno);
|
---|
| 329 |
|
---|
| 330 | /* copy in the excess from last time */
|
---|
| 331 | for (build = blk->c; scan < linebuf.c + BLKSIZE; )
|
---|
| 332 | {
|
---|
| 333 | *build++ = *scan++;
|
---|
| 334 | }
|
---|
| 335 | }
|
---|
| 336 | }
|
---|
| 337 |
|
---|
| 338 | /* fill this block(s) from remainder of orig block */
|
---|
| 339 | while (newptr < buf.c + BLKSIZE && *newptr)
|
---|
| 340 | {
|
---|
| 341 | while (newptr < buf.c + BLKSIZE
|
---|
| 342 | && *newptr
|
---|
| 343 | && build < blk->c + BLKSIZE - 1)
|
---|
| 344 | {
|
---|
| 345 | *build++ = *newptr++;
|
---|
| 346 | }
|
---|
| 347 | if (newptr < buf.c + BLKSIZE && *newptr)
|
---|
| 348 | {
|
---|
| 349 | /* save the excess */
|
---|
| 350 | for (scan = linebuf.c + BLKSIZE;
|
---|
| 351 | build > blk->c && build[-1] != '\n';
|
---|
| 352 | )
|
---|
| 353 | {
|
---|
| 354 | *--scan = *--build;
|
---|
| 355 | }
|
---|
| 356 |
|
---|
| 357 | /* write the block */
|
---|
| 358 | while (build < blk->c + BLKSIZE)
|
---|
| 359 | {
|
---|
| 360 | *build++ = '\0';
|
---|
| 361 | }
|
---|
| 362 | blkdirty(blk);
|
---|
| 363 |
|
---|
| 364 | /* add another block */
|
---|
| 365 | blkno++;
|
---|
| 366 | blk = blkadd(blkno);
|
---|
| 367 |
|
---|
| 368 | /* copy in the excess from last time */
|
---|
| 369 | for (build = blk->c; scan < linebuf.c + BLKSIZE; )
|
---|
| 370 | {
|
---|
| 371 | *build++ = *scan++;
|
---|
| 372 | }
|
---|
| 373 | }
|
---|
| 374 | }
|
---|
| 375 |
|
---|
| 376 | /* see if we can combine our last block with the following block */
|
---|
| 377 | if (lnum[blkno] < nlines && lnum[blkno + 1] - lnum[blkno] < (BLKSIZE >> 6))
|
---|
| 378 | {
|
---|
| 379 | /* hey, we probably can! Get the following block & see... */
|
---|
| 380 | following = blkget(blkno + 1);
|
---|
| 381 | if (strlen(following->c) + (build - blk->c) < BLKSIZE - 1)
|
---|
| 382 | {
|
---|
| 383 | /* we can! Copy text from following to blk */
|
---|
| 384 | for (scan = following->c; *scan; )
|
---|
| 385 | {
|
---|
| 386 | *build++ = *scan++;
|
---|
| 387 | }
|
---|
| 388 | while (build < blk->c + BLKSIZE)
|
---|
| 389 | {
|
---|
| 390 | *build++ = '\0';
|
---|
| 391 | }
|
---|
| 392 | blkdirty(blk);
|
---|
| 393 |
|
---|
| 394 | /* pretend the following was the last blk */
|
---|
| 395 | blk = following;
|
---|
| 396 | build = blk->c;
|
---|
| 397 | }
|
---|
| 398 | }
|
---|
| 399 |
|
---|
| 400 | /* that last block is dirty by now */
|
---|
| 401 | while (build < blk->c + BLKSIZE)
|
---|
| 402 | {
|
---|
| 403 | *build++ = '\0';
|
---|
| 404 | }
|
---|
| 405 | blkdirty(blk);
|
---|
| 406 | }
|
---|
| 407 |
|
---|
| 408 |
|
---|
| 409 | /* change the text of a file */
|
---|
| 410 | void change(frommark, tomark, newtext)
|
---|
| 411 | MARK frommark, tomark;
|
---|
| 412 | char *newtext;
|
---|
| 413 | {
|
---|
| 414 | int i;
|
---|
| 415 | long l;
|
---|
| 416 | char *text;
|
---|
| 417 | BLK *blk;
|
---|
| 418 |
|
---|
| 419 | #ifdef DEBUG2
|
---|
| 420 | debout("change(%ld.%d, %ld.%d, \"%s\")\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark), newtext);
|
---|
| 421 | #endif
|
---|
| 422 |
|
---|
| 423 | /* optimize for single-character replacement */
|
---|
| 424 | if (frommark + 1 == tomark && newtext[0] && !newtext[1] && newtext[0] != '\n')
|
---|
| 425 | {
|
---|
| 426 | /* find the block containing frommark */
|
---|
| 427 | l = markline(frommark);
|
---|
| 428 | for (i = 1; lnum[i] < l; i++)
|
---|
| 429 | {
|
---|
| 430 | }
|
---|
| 431 |
|
---|
| 432 | /* get the block */
|
---|
| 433 | blk = blkget(i);
|
---|
| 434 |
|
---|
| 435 | /* find the line within the block */
|
---|
| 436 | for (text = blk->c, i = l - lnum[i - 1] - 1; i > 0; text++)
|
---|
| 437 | {
|
---|
| 438 | if (*text == '\n')
|
---|
| 439 | {
|
---|
| 440 | i--;
|
---|
| 441 | }
|
---|
| 442 | }
|
---|
| 443 |
|
---|
| 444 | /* replace the char */
|
---|
| 445 | text += markidx(frommark);
|
---|
| 446 | if (*text == newtext[0])
|
---|
| 447 | {
|
---|
| 448 | /* no change was needed - same char */
|
---|
| 449 | return;
|
---|
| 450 | }
|
---|
| 451 | else if (*text != '\n')
|
---|
| 452 | {
|
---|
| 453 | /* This is a change */
|
---|
| 454 | changes++;
|
---|
| 455 | significant = TRUE;
|
---|
| 456 | ChangeText
|
---|
| 457 | {
|
---|
| 458 | *text = newtext[0];
|
---|
| 459 | blkdirty(blk);
|
---|
| 460 | }
|
---|
| 461 | redrawrange(markline(frommark), markline(tomark), markline(frommark));
|
---|
| 462 | return;
|
---|
| 463 | }
|
---|
| 464 | /* else it is a complex change involving newline... */
|
---|
| 465 | }
|
---|
| 466 |
|
---|
| 467 | /* couldn't optimize, so do delete & add */
|
---|
| 468 | ChangeText
|
---|
| 469 | {
|
---|
| 470 | delete(frommark, tomark);
|
---|
| 471 | add(frommark, newtext);
|
---|
| 472 | rptlabel = "changed";
|
---|
| 473 | }
|
---|
| 474 | }
|
---|