Annotation of src/usr.bin/mg/line.c, Revision 1.56
1.56 ! bcallah 1: /* $OpenBSD: line.c,v 1.55 2015/03/19 21:22:15 bcallah Exp $ */
1.23 kjell 2:
3: /* This file is in the public domain. */
1.5 niklas 4:
1.1 deraadt 5: /*
6: * Text line handling.
1.8 mickey 7: *
1.4 millert 8: * The functions in this file are a general set of line management
1.8 mickey 9: * utilities. They are the only routines that touch the text. They
10: * also touch the buffer and window structures to make sure that the
1.51 lum 11: * necessary updating gets done.
1.1 deraadt 12: *
1.8 mickey 13: * Note that this code only updates the dot and mark values in the window
14: * list. Since all the code acts on the current window, the buffer that
15: * we are editing must be displayed, which means that "b_nwnd" is non-zero,
16: * which means that the dot and mark values in the buffer headers are
1.1 deraadt 17: * nonsense.
18: */
19:
1.55 bcallah 20: #include <sys/queue.h>
1.54 guenther 21: #include <limits.h>
1.55 bcallah 22: #include <signal.h>
23: #include <stdio.h>
1.32 kjell 24: #include <stdlib.h>
25: #include <string.h>
1.55 bcallah 26:
27: #include "def.h"
1.32 kjell 28:
1.4 millert 29: /*
1.9 vincent 30: * Allocate a new line of size `used'. lrealloc() can be called if the line
31: * ever needs to grow beyond that.
1.1 deraadt 32: */
1.28 deraadt 33: struct line *
1.9 vincent 34: lalloc(int used)
1.3 millert 35: {
1.28 deraadt 36: struct line *lp;
1.1 deraadt 37:
1.22 db 38: if ((lp = malloc(sizeof(*lp))) == NULL)
39: return (NULL);
1.9 vincent 40: lp->l_text = NULL;
41: lp->l_size = 0;
42: lp->l_used = used; /* XXX */
43: if (lrealloc(lp, used) == FALSE) {
44: free(lp);
1.22 db 45: return (NULL);
1.1 deraadt 46: }
1.22 db 47: return (lp);
1.1 deraadt 48: }
49:
1.9 vincent 50: int
1.28 deraadt 51: lrealloc(struct line *lp, int newsize)
1.1 deraadt 52: {
1.9 vincent 53: char *tmp;
1.1 deraadt 54:
1.18 millert 55: if (lp->l_size < newsize) {
56: if ((tmp = realloc(lp->l_text, newsize)) == NULL)
1.22 db 57: return (FALSE);
1.18 millert 58: lp->l_text = tmp;
59: lp->l_size = newsize;
60: }
1.22 db 61: return (TRUE);
1.1 deraadt 62: }
63:
64: /*
1.4 millert 65: * Delete line "lp". Fix all of the links that might point to it (they are
1.8 mickey 66: * moved to offset 0 of the next line. Unlink the line from whatever buffer
67: * it might be in, and release the memory. The buffers are updated too; the
1.4 millert 68: * magic conditions described in the above comments don't hold here.
1.1 deraadt 69: */
1.6 art 70: void
1.28 deraadt 71: lfree(struct line *lp)
1.3 millert 72: {
1.28 deraadt 73: struct buffer *bp;
74: struct mgwin *wp;
1.1 deraadt 75:
1.3 millert 76: for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
1.1 deraadt 77: if (wp->w_linep == lp)
78: wp->w_linep = lp->l_fp;
1.3 millert 79: if (wp->w_dotp == lp) {
80: wp->w_dotp = lp->l_fp;
81: wp->w_doto = 0;
1.1 deraadt 82: }
83: if (wp->w_markp == lp) {
84: wp->w_markp = lp->l_fp;
85: wp->w_marko = 0;
86: }
87: }
1.3 millert 88: for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
1.1 deraadt 89: if (bp->b_nwnd == 0) {
1.3 millert 90: if (bp->b_dotp == lp) {
1.1 deraadt 91: bp->b_dotp = lp->l_fp;
92: bp->b_doto = 0;
93: }
94: if (bp->b_markp == lp) {
95: bp->b_markp = lp->l_fp;
96: bp->b_marko = 0;
97: }
98: }
99: }
100: lp->l_bp->l_fp = lp->l_fp;
101: lp->l_fp->l_bp = lp->l_bp;
1.9 vincent 102: if (lp->l_text != NULL)
103: free(lp->l_text);
104: free(lp);
1.1 deraadt 105: }
106:
107: /*
1.8 mickey 108: * This routine is called when a character changes in place in the current
109: * buffer. It updates all of the required flags in the buffer and window
110: * system. The flag used is passed as an argument; if the buffer is being
111: * displayed in more than 1 window we change EDIT to HARD. Set MODE if the
1.4 millert 112: * mode line needs to be updated (the "*" has to be set).
1.1 deraadt 113: */
1.6 art 114: void
1.15 vincent 115: lchange(int flag)
1.3 millert 116: {
1.28 deraadt 117: struct mgwin *wp;
1.1 deraadt 118:
1.4 millert 119: /* update mode lines if this is the first change. */
120: if ((curbp->b_flag & BFCHG) == 0) {
121: flag |= WFMODE;
1.1 deraadt 122: curbp->b_flag |= BFCHG;
123: }
1.3 millert 124: for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
1.1 deraadt 125: if (wp->w_bufp == curbp) {
1.47 kjell 126: wp->w_rflag |= flag;
1.3 millert 127: if (wp != curwp)
1.47 kjell 128: wp->w_rflag |= WFFULL;
1.1 deraadt 129: }
130: }
1.20 vincent 131: }
132:
133: /*
1.8 mickey 134: * Insert "n" copies of the character "c" at the current location of dot.
135: * In the easy case all that happens is the text is stored in the line.
136: * In the hard case, the line has to be reallocated. When the window list
137: * is updated, take special care; I screwed it up once. You always update
1.4 millert 138: * dot in the current window. You update mark and a dot in another window
139: * if it is greater than the place where you did the insert. Return TRUE
1.1 deraadt 140: * if all is well, and FALSE on errors.
141: */
1.4 millert 142: int
1.15 vincent 143: linsert(int n, int c)
1.1 deraadt 144: {
1.28 deraadt 145: struct line *lp1;
146: struct mgwin *wp;
1.4 millert 147: RSIZE i;
148: int doto;
1.45 kjell 149: int s;
1.1 deraadt 150:
1.20 vincent 151: if (!n)
152: return (TRUE);
153:
1.45 kjell 154: if ((s = checkdirty(curbp)) != TRUE)
155: return (s);
156:
1.15 vincent 157: if (curbp->b_flag & BFREADONLY) {
1.53 lum 158: dobeep();
1.15 vincent 159: ewprintf("Buffer is read only");
1.22 db 160: return (FALSE);
1.15 vincent 161: }
162:
1.1 deraadt 163: lchange(WFEDIT);
1.4 millert 164:
165: /* current line */
166: lp1 = curwp->w_dotp;
1.15 vincent 167:
1.4 millert 168: /* special case for the end */
1.41 kjell 169: if (lp1 == curbp->b_headp) {
1.28 deraadt 170: struct line *lp2, *lp3;
1.11 deraadt 171:
1.4 millert 172: /* now should only happen in empty buffer */
1.1 deraadt 173: if (curwp->w_doto != 0) {
1.53 lum 174: dobeep();
1.1 deraadt 175: ewprintf("bug: linsert");
1.22 db 176: return (FALSE);
1.1 deraadt 177: }
1.4 millert 178: /* allocate a new line */
1.9 vincent 179: if ((lp2 = lalloc(n)) == NULL)
1.22 db 180: return (FALSE);
1.4 millert 181: /* previous line */
182: lp3 = lp1->l_bp;
183: /* link in */
184: lp3->l_fp = lp2;
1.1 deraadt 185: lp2->l_fp = lp1;
186: lp1->l_bp = lp2;
187: lp2->l_bp = lp3;
1.3 millert 188: for (i = 0; i < n; ++i)
1.1 deraadt 189: lp2->l_text[i] = c;
1.3 millert 190: for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
1.1 deraadt 191: if (wp->w_linep == lp1)
192: wp->w_linep = lp2;
193: if (wp->w_dotp == lp1)
194: wp->w_dotp = lp2;
195: if (wp->w_markp == lp1)
196: wp->w_markp = lp2;
197: }
1.16 vincent 198: undo_add_insert(lp2, 0, n);
1.1 deraadt 199: curwp->w_doto = n;
1.22 db 200: return (TRUE);
1.1 deraadt 201: }
1.4 millert 202: /* save for later */
203: doto = curwp->w_doto;
1.9 vincent 204:
205: if ((lp1->l_used + n) > lp1->l_size) {
206: if (lrealloc(lp1, lp1->l_used + n) == FALSE)
1.22 db 207: return (FALSE);
1.11 deraadt 208: }
1.9 vincent 209: lp1->l_used += n;
1.11 deraadt 210: if (lp1->l_used != n)
1.9 vincent 211: memmove(&lp1->l_text[doto + n], &lp1->l_text[doto],
212: lp1->l_used - n - doto);
213:
1.4 millert 214: /* Add the characters */
215: for (i = 0; i < n; ++i)
1.9 vincent 216: lp1->l_text[doto + i] = c;
1.3 millert 217: for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
1.1 deraadt 218: if (wp->w_dotp == lp1) {
1.3 millert 219: if (wp == curwp || wp->w_doto > doto)
1.1 deraadt 220: wp->w_doto += n;
221: }
222: if (wp->w_markp == lp1) {
223: if (wp->w_marko > doto)
224: wp->w_marko += n;
225: }
226: }
1.16 vincent 227: undo_add_insert(curwp->w_dotp, doto, n);
1.22 db 228: return (TRUE);
1.1 deraadt 229: }
230:
1.44 kjell 231: /*
232: * Do the work of inserting a newline at the given line/offset.
233: * If mark is on the current line, we may have to move the markline
234: * to keep line numbers in sync.
235: * lnewline_at assumes the current buffer is writable. Checking for
236: * this fact should be done by the caller.
237: */
1.4 millert 238: int
1.28 deraadt 239: lnewline_at(struct line *lp1, int doto)
1.1 deraadt 240: {
1.28 deraadt 241: struct line *lp2;
242: struct mgwin *wp;
1.52 florian 243: int nlen, tcurwpdotline;
1.1 deraadt 244:
1.39 kjell 245: lchange(WFFULL);
1.14 vincent 246:
1.44 kjell 247: curwp->w_bufp->b_lines++;
248: /* Check if mark is past dot (even on current line) */
249: if (curwp->w_markline > curwp->w_dotline ||
250: (curwp->w_dotline == curwp->w_markline &&
251: curwp->w_marko >= doto))
252: curwp->w_markline++;
1.52 florian 253:
254: tcurwpdotline = curwp->w_dotline;
1.44 kjell 255:
1.35 kjell 256: /* If start of line, allocate a new line instead of copying */
1.4 millert 257: if (doto == 0) {
258: /* new first part */
1.35 kjell 259: if ((lp2 = lalloc(0)) == NULL)
260: return (FALSE);
1.1 deraadt 261: lp2->l_bp = lp1->l_bp;
262: lp1->l_bp->l_fp = lp2;
263: lp2->l_fp = lp1;
264: lp1->l_bp = lp2;
1.52 florian 265: for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
1.3 millert 266: if (wp->w_linep == lp1)
267: wp->w_linep = lp2;
1.52 florian 268: if (wp->w_dotline >= tcurwpdotline)
269: wp->w_dotline++;
270: }
1.46 kjell 271: undo_add_boundary(FFRAND, 1);
1.35 kjell 272: undo_add_insert(lp2, 0, 1);
1.46 kjell 273: undo_add_boundary(FFRAND, 1);
1.35 kjell 274: return (TRUE);
1.1 deraadt 275: }
1.4 millert 276:
277: /* length of new part */
278: nlen = llength(lp1) - doto;
279:
280: /* new second half line */
1.35 kjell 281: if ((lp2 = lalloc(nlen)) == NULL)
282: return (FALSE);
1.3 millert 283: if (nlen != 0)
284: bcopy(&lp1->l_text[doto], &lp2->l_text[0], nlen);
1.1 deraadt 285: lp1->l_used = doto;
286: lp2->l_bp = lp1;
287: lp2->l_fp = lp1->l_fp;
288: lp1->l_fp = lp2;
289: lp2->l_fp->l_bp = lp2;
1.4 millert 290: /* Windows */
291: for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
1.1 deraadt 292: if (wp->w_dotp == lp1 && wp->w_doto >= doto) {
293: wp->w_dotp = lp2;
294: wp->w_doto -= doto;
1.52 florian 295: wp->w_dotline++;
296: } else if (wp->w_dotline > tcurwpdotline)
297: wp->w_dotline++;
1.1 deraadt 298: if (wp->w_markp == lp1 && wp->w_marko >= doto) {
299: wp->w_markp = lp2;
300: wp->w_marko -= doto;
301: }
302: }
1.46 kjell 303: undo_add_boundary(FFRAND, 1);
1.24 kjell 304: undo_add_insert(lp1, llength(lp1), 1);
1.46 kjell 305: undo_add_boundary(FFRAND, 1);
1.35 kjell 306: return (TRUE);
1.1 deraadt 307: }
308:
309: /*
1.21 vincent 310: * Insert a newline into the buffer at the current location of dot in the
311: * current window.
312: */
313: int
314: lnewline(void)
315: {
1.45 kjell 316: int s;
317:
318: if ((s = checkdirty(curbp)) != TRUE)
319: return (s);
1.21 vincent 320: if (curbp->b_flag & BFREADONLY) {
1.53 lum 321: dobeep();
1.21 vincent 322: ewprintf("Buffer is read only");
1.22 db 323: return (FALSE);
1.21 vincent 324: }
1.22 db 325: return (lnewline_at(curwp->w_dotp, curwp->w_doto));
1.21 vincent 326: }
327:
328: /*
1.36 kjell 329: * This function deletes "n" bytes, starting at dot. (actually, n+1, as the
330: * newline is included) It understands how to deal with end of lines, etc.
331: * It returns TRUE if all of the characters were deleted, and FALSE if
332: * they were not (because dot ran into the end of the buffer).
333: * The "kflag" indicates either no insertion, or direction of insertion
334: * into the kill buffer.
1.1 deraadt 335: */
1.4 millert 336: int
1.15 vincent 337: ldelete(RSIZE n, int kflag)
1.3 millert 338: {
1.28 deraadt 339: struct line *dotp;
1.36 kjell 340: RSIZE chunk;
1.28 deraadt 341: struct mgwin *wp;
1.36 kjell 342: int doto;
343: char *cp1, *cp2;
344: size_t len;
1.49 millert 345: char *sv = NULL;
1.36 kjell 346: int end;
1.45 kjell 347: int s;
1.49 millert 348: int rval = FALSE;
1.1 deraadt 349:
1.45 kjell 350: if ((s = checkdirty(curbp)) != TRUE)
351: return (s);
1.15 vincent 352: if (curbp->b_flag & BFREADONLY) {
1.53 lum 353: dobeep();
1.15 vincent 354: ewprintf("Buffer is read only");
1.49 millert 355: goto out;
1.15 vincent 356: }
1.32 kjell 357: len = n;
358: if ((sv = calloc(1, len + 1)) == NULL)
1.49 millert 359: goto out;
1.32 kjell 360: end = 0;
1.15 vincent 361:
1.48 kjell 362: undo_add_delete(curwp->w_dotp, curwp->w_doto, n, (kflag & KREG));
1.15 vincent 363:
1.1 deraadt 364: while (n != 0) {
365: dotp = curwp->w_dotp;
366: doto = curwp->w_doto;
1.4 millert 367: /* Hit the end of the buffer */
1.41 kjell 368: if (dotp == curbp->b_headp)
1.49 millert 369: goto out;
1.4 millert 370: /* Size of the chunk */
371: chunk = dotp->l_used - doto;
1.20 vincent 372:
1.1 deraadt 373: if (chunk > n)
374: chunk = n;
1.4 millert 375: /* End of line, merge */
376: if (chunk == 0) {
1.42 kjell 377: if (dotp == blastlp(curbp))
1.49 millert 378: goto out;
1.39 kjell 379: lchange(WFFULL);
1.32 kjell 380: if (ldelnewline() == FALSE)
1.49 millert 381: goto out;
1.32 kjell 382: end = strlcat(sv, "\n", len + 1);
1.1 deraadt 383: --n;
384: continue;
385: }
386: lchange(WFEDIT);
1.4 millert 387: /* Scrunch text */
388: cp1 = &dotp->l_text[doto];
1.32 kjell 389: memcpy(&sv[end], cp1, chunk);
390: end += chunk;
391: sv[end] = '\0';
1.20 vincent 392: for (cp2 = cp1 + chunk; cp2 < &dotp->l_text[dotp->l_used];
393: cp2++)
394: *cp1++ = *cp2;
1.4 millert 395: dotp->l_used -= (int)chunk;
1.3 millert 396: for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
397: if (wp->w_dotp == dotp && wp->w_doto >= doto) {
398: /* NOSTRICT */
1.1 deraadt 399: wp->w_doto -= chunk;
400: if (wp->w_doto < doto)
401: wp->w_doto = doto;
402: }
1.3 millert 403: if (wp->w_markp == dotp && wp->w_marko >= doto) {
404: /* NOSTRICT */
1.1 deraadt 405: wp->w_marko -= chunk;
406: if (wp->w_marko < doto)
407: wp->w_marko = doto;
408: }
409: }
410: n -= chunk;
411: }
1.34 kjell 412: if (kchunk(sv, (RSIZE)len, kflag) != TRUE)
1.49 millert 413: goto out;
414: rval = TRUE;
415: out:
1.32 kjell 416: free(sv);
1.49 millert 417: return (rval);
1.1 deraadt 418: }
419:
420: /*
1.8 mickey 421: * Delete a newline and join the current line with the next line. If the next
1.4 millert 422: * line is the magic header line always return TRUE; merging the last line
1.8 mickey 423: * with the header line can be thought of as always being a successful
424: * operation. Even if nothing is done, this makes the kill buffer work
1.44 kjell 425: * "right". If the mark is past the dot (actually, markline > dotline),
426: * decrease the markline accordingly to keep line numbers in sync.
427: * Easy cases can be done by shuffling data around. Hard cases
1.8 mickey 428: * require that lines be moved about in memory. Return FALSE on error and
1.40 kjell 429: * TRUE if all looks ok. We do not update w_dotline here, as deletes are done
430: * after moves.
1.1 deraadt 431: */
1.4 millert 432: int
1.15 vincent 433: ldelnewline(void)
1.3 millert 434: {
1.28 deraadt 435: struct line *lp1, *lp2, *lp3;
436: struct mgwin *wp;
1.45 kjell 437: int s;
1.1 deraadt 438:
1.45 kjell 439: if ((s = checkdirty(curbp)) != TRUE)
440: return (s);
1.15 vincent 441: if (curbp->b_flag & BFREADONLY) {
1.53 lum 442: dobeep();
1.15 vincent 443: ewprintf("Buffer is read only");
1.22 db 444: return (FALSE);
1.15 vincent 445: }
446:
1.1 deraadt 447: lp1 = curwp->w_dotp;
448: lp2 = lp1->l_fp;
1.4 millert 449: /* at the end of the buffer */
1.41 kjell 450: if (lp2 == curbp->b_headp)
1.22 db 451: return (TRUE);
1.44 kjell 452: /* Keep line counts in sync */
1.40 kjell 453: curwp->w_bufp->b_lines--;
1.44 kjell 454: if (curwp->w_markline > curwp->w_dotline)
455: curwp->w_markline--;
1.1 deraadt 456: if (lp2->l_used <= lp1->l_size - lp1->l_used) {
457: bcopy(&lp2->l_text[0], &lp1->l_text[lp1->l_used], lp2->l_used);
1.3 millert 458: for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
1.1 deraadt 459: if (wp->w_linep == lp2)
460: wp->w_linep = lp1;
461: if (wp->w_dotp == lp2) {
1.3 millert 462: wp->w_dotp = lp1;
1.1 deraadt 463: wp->w_doto += lp1->l_used;
464: }
465: if (wp->w_markp == lp2) {
1.3 millert 466: wp->w_markp = lp1;
1.1 deraadt 467: wp->w_marko += lp1->l_used;
468: }
469: }
470: lp1->l_used += lp2->l_used;
471: lp1->l_fp = lp2->l_fp;
472: lp2->l_fp->l_bp = lp1;
1.37 kjell 473: free(lp2);
1.22 db 474: return (TRUE);
1.1 deraadt 475: }
1.3 millert 476: if ((lp3 = lalloc(lp1->l_used + lp2->l_used)) == NULL)
1.22 db 477: return (FALSE);
1.1 deraadt 478: bcopy(&lp1->l_text[0], &lp3->l_text[0], lp1->l_used);
479: bcopy(&lp2->l_text[0], &lp3->l_text[lp1->l_used], lp2->l_used);
480: lp1->l_bp->l_fp = lp3;
481: lp3->l_fp = lp2->l_fp;
482: lp2->l_fp->l_bp = lp3;
483: lp3->l_bp = lp1->l_bp;
1.3 millert 484: for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
485: if (wp->w_linep == lp1 || wp->w_linep == lp2)
1.1 deraadt 486: wp->w_linep = lp3;
487: if (wp->w_dotp == lp1)
1.3 millert 488: wp->w_dotp = lp3;
1.1 deraadt 489: else if (wp->w_dotp == lp2) {
1.3 millert 490: wp->w_dotp = lp3;
1.1 deraadt 491: wp->w_doto += lp1->l_used;
492: }
493: if (wp->w_markp == lp1)
1.3 millert 494: wp->w_markp = lp3;
1.1 deraadt 495: else if (wp->w_markp == lp2) {
1.3 millert 496: wp->w_markp = lp3;
1.1 deraadt 497: wp->w_marko += lp1->l_used;
498: }
499: }
1.37 kjell 500: free(lp1);
501: free(lp2);
1.22 db 502: return (TRUE);
1.1 deraadt 503: }
1.9 vincent 504:
1.1 deraadt 505: /*
1.8 mickey 506: * Replace plen characters before dot with argument string. Control-J
507: * characters in st are interpreted as newlines. There is a casehack
508: * disable flag (normally it likes to match case of replacement to what
1.4 millert 509: * was there).
1.1 deraadt 510: */
1.4 millert 511: int
1.27 kjell 512: lreplace(RSIZE plen, char *st)
1.4 millert 513: {
514: RSIZE rlen; /* replacement length */
1.45 kjell 515: int s;
1.15 vincent 516:
1.45 kjell 517: if ((s = checkdirty(curbp)) != TRUE)
518: return (s);
1.15 vincent 519: if (curbp->b_flag & BFREADONLY) {
1.53 lum 520: dobeep();
1.15 vincent 521: ewprintf("Buffer is read only");
1.22 db 522: return (FALSE);
1.15 vincent 523: }
1.46 kjell 524: undo_boundary_enable(FFRAND, 0);
1.25 deraadt 525:
1.6 art 526: (void)backchar(FFARG | FFRAND, (int)plen);
1.24 kjell 527: (void)ldelete(plen, KNONE);
1.15 vincent 528:
1.1 deraadt 529: rlen = strlen(st);
1.24 kjell 530: region_put_data(st, rlen);
1.39 kjell 531: lchange(WFFULL);
1.1 deraadt 532:
1.46 kjell 533: undo_boundary_enable(FFRAND, 1);
1.29 kjell 534: return (TRUE);
1.38 kjell 535: }
536:
537: /*
538: * Allocate and return the supplied line as a C string
539: */
540: char *
541: linetostr(const struct line *ln)
542: {
1.50 kjell 543: int len;
1.38 kjell 544: char *line;
545:
546: len = llength(ln);
1.50 kjell 547: if (len == INT_MAX) /* (len + 1) overflow */
1.38 kjell 548: return (NULL);
549:
550: if ((line = malloc(len + 1)) == NULL)
551: return (NULL);
552:
553: (void)memcpy(line, ltext(ln), len);
554: line[len] = '\0';
555:
556: return (line);
1.1 deraadt 557: }