* More changes to make Valgrind happy. Work in progress.
[alpine.git] / pico / line.c
blob57c39b04e7e9786420f182fe8476dc7b06017470
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: line.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2007 University of Washington
8 * Copyright 2013-2018 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
18 * Program: Line management routines
22 * The functions in this file are a general set of line management utilities.
23 * They are the only routines that touch the text. They also touch the buffer
24 * and window structures, to make sure that the necessary updating gets done.
25 * There are routines in this file that handle the kill buffer too. It isn't
26 * here for any good reason.
28 * Note that this code only updates the dot and mark values in the window list.
29 * Since all the code acts on the current window, the buffer that we are
30 * editing must be being displayed, which means that "b_nwnd" is non zero,
31 * which means that the dot and mark values in the buffer headers are nonsense.
34 #include "headers.h"
36 #define NBLOCK 16 /* Line block chunk size */
37 #define KBLOCK 1024 /* Kill buffer block size */
41 * Struct to manage the kill and justify buffers
43 struct pkchunk {
44 short used; /* # of characters used in this buffer */
45 UCS bufp[KBLOCK]; /* buffer containing text */
46 struct pkchunk *next; /* pointer to next chunk */
49 static struct pkbuf {
50 long total; /* # of UCS characters used in buffer */
51 struct pkchunk *first; /* first one of these in the chain */
52 struct pkchunk *last; /* last one of these in the chain */
53 } *kbufp, *fbufp;
56 void insspace(int, int);
57 int ldelnewline(void);
58 void pkbufdel(struct pkbuf **);
59 void pkchunkdel(struct pkchunk **);
60 int pkbufinsert(UCS, struct pkbuf **);
61 long pkbufremove(int, struct pkbuf *);
65 * This routine allocates a block of memory large enough to hold a LINE
66 * containing "used" characters. The block is always rounded up a bit. Return
67 * a pointer to the new block, or NULL if there isn't any memory left. Print a
68 * message in the message line if no space.
70 LINE *
71 lalloc(int used)
73 register LINE *lp;
74 register int size;
75 EML eml;
76 static int displayed = 0;
78 if((size = (used+NBLOCK-1) & ~(NBLOCK-1)) > NLINE)
79 size *= 2;
81 if (size == 0) /* Assume that an empty */
82 size = NBLOCK; /* line is for type-in. */
84 if (displayed == 0 && (lp = (LINE *) malloc(sizeof(LINE)+(size*sizeof(CELL)))) == NULL){
85 eml.s = comatose(size);
86 emlwrite("Cannot allocate %s bytes (read file incomplete)", &eml);
87 displayed++;
88 return (NULL);
91 lp->l_size = size;
92 lp->l_used = used;
93 lp->l_sig = 0; /* assume it is not a signature line */
94 lp->l_text[0].c = '\0'; /* this is not necessary */
95 lp->l_text[0].a = lp->l_text[0].d = 0; /* but it makes valgrind happy */
96 return (lp);
100 * Delete line "lp". Fix all of the links that might point at it (they are
101 * moved to offset 0 of the next line. Unlink the line from whatever buffer it
102 * might be in. Release the memory. The buffers are updated too; the magic
103 * conditions described in the above comments don't hold here.
105 void
106 lfree(LINE *lp)
108 register BUFFER *bp;
109 register WINDOW *wp;
111 wp = wheadp;
112 while (wp != NULL) {
113 if (wp->w_linep == lp)
114 wp->w_linep = lp->l_fp;
116 if (wp->w_dotp == lp) {
117 wp->w_dotp = lp->l_fp;
118 wp->w_doto = 0;
121 if (wp->w_markp == lp) {
122 wp->w_markp = lp->l_fp;
123 wp->w_marko = 0;
126 if (wp->w_imarkp == lp) {
127 wp->w_imarkp = lp->l_fp;
128 wp->w_imarko = 0;
131 wp = wp->w_wndp;
134 bp = bheadp;
135 while (bp != NULL) {
136 if (bp->b_nwnd == 0) {
137 if (bp->b_dotp == lp) {
138 bp->b_dotp = lp->l_fp;
139 bp->b_doto = 0;
142 if (bp->b_markp == lp) {
143 bp->b_markp = lp->l_fp;
144 bp->b_marko = 0;
148 bp = bp->b_bufp;
151 lp->l_bp->l_fp = lp->l_fp;
152 lp->l_fp->l_bp = lp->l_bp;
153 free((char *) lp);
158 * This routine gets called when a character is changed in place in the current
159 * buffer. It updates all of the required flags in the buffer and window
160 * system. The flag used is passed as an argument; if the buffer is being
161 * displayed in more than 1 window we change EDIT to HARD. Set MODE if the
162 * mode line needs to be updated (the "*" has to be set).
164 void
165 lchange(int flag)
167 register WINDOW *wp;
169 if (curbp->b_nwnd != 1) /* Ensure hard. */
170 flag = WFHARD;
172 if ((curbp->b_flag&BFCHG) == 0) { /* First change, so */
173 if(Pmaster == NULL)
174 flag |= WFMODE; /* update mode lines. */
175 curbp->b_flag |= BFCHG;
178 wp = wheadp;
179 while (wp != NULL) {
180 if (wp->w_bufp == curbp)
181 wp->w_flag |= flag;
182 wp = wp->w_wndp;
188 * insert spaces forward into text
189 * default flag and numeric argument
191 void
192 insspace(int f, int n)
194 linsert(n, ' ');
195 backchar(f, n);
199 * Insert "n" copies of the character "c" at the current location of dot. In
200 * the easy case all that happens is the text is stored in the line. In the
201 * hard case, the line has to be reallocated. When the window list is updated,
202 * take special care; I screwed it up once. You always update dot in the
203 * current window. You update mark, and a dot in another window, if it is
204 * greater than the place where you did the insert. Return TRUE if all is
205 * well, and FALSE on errors.
208 linsert(int n, UCS c)
210 register LINE *dotp;
211 register int doto;
212 register WINDOW *wp;
214 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
215 return(rdonly()); /* we are in read only mode */
217 dotp = curwp->w_dotp;
218 doto = curwp->w_doto;
219 lchange(WFEDIT);
221 if(!geninsert(&(curwp->w_dotp), &(curwp->w_doto), curbp->b_linep,
222 c, (curwp->w_markp) ? 1 : 0, n, &curbp->b_linecnt))
223 return(FALSE);
225 wp = wheadp; /* Update windows */
226 while (wp != NULL) {
227 if (wp->w_linep == dotp)
228 wp->w_linep = wp->w_dotp;
230 if (wp->w_imarkp == dotp) { /* added for internal mark */
231 wp->w_imarkp = wp->w_dotp;
232 if (wp->w_imarko > doto)
233 wp->w_imarko += n;
236 if (wp->w_markp == dotp) {
237 wp->w_markp = dotp;
238 if (wp->w_marko > doto)
239 wp->w_marko += n;
241 wp = wp->w_wndp;
244 return (TRUE);
249 * geninsert - do the actual work of inserting a character into
250 * the list of lines.
253 geninsert(LINE **dotp, int *doto, LINE *linep, UCS c, int attb, int n, long *lines)
255 register LINE *lp1;
256 register LINE *lp2;
257 register CELL *cp1;
258 register CELL *cp2;
259 CELL ac;
261 ac.a = attb;
262 if (*dotp == linep) { /* At the end: special */
263 if (*doto != 0) {
264 emlwrite("Programmer botch: geninsert", NULL);
265 return (FALSE);
268 if ((lp1=lalloc(n)) == NULL) /* Allocate new line */
269 return (FALSE);
271 lp2 = (*dotp)->l_bp; /* Previous line */
272 lp2->l_fp = lp1; /* Link in */
273 lp1->l_fp = *dotp;
274 (*dotp)->l_bp = lp1;
275 lp1->l_bp = lp2;
276 *doto = n;
277 *dotp = lp1;
278 ac.c = (c & CELLMASK);
279 cp1 = &(*dotp)->l_text[0];
280 while(n--)
281 *cp1++ = ac;
283 if(lines)
284 (*lines)++;
286 return (TRUE);
289 if ((*dotp)->l_used+n > (*dotp)->l_size) { /* Hard: reallocate */
290 if ((lp1=lalloc((*dotp)->l_used+n)) == NULL)
291 return (FALSE);
293 cp1 = &(*dotp)->l_text[0];
294 cp2 = &lp1->l_text[0];
295 while (cp1 != &(*dotp)->l_text[*doto])
296 *cp2++ = *cp1++;
298 cp2 += n;
299 while (cp1 != &(*dotp)->l_text[(*dotp)->l_used])
300 *cp2++ = *cp1++;
302 (*dotp)->l_bp->l_fp = lp1;
303 lp1->l_fp = (*dotp)->l_fp;
304 (*dotp)->l_fp->l_bp = lp1;
305 lp1->l_bp = (*dotp)->l_bp;
307 /* global may be keeping track of mark/imark */
308 if(wheadp){
309 if (wheadp->w_imarkp == *dotp)
310 wheadp->w_imarkp = lp1;
312 if (wheadp->w_markp == *dotp)
313 wheadp->w_markp = lp1;
316 free((char *) (*dotp));
317 *dotp = lp1;
318 } else { /* Easy: in place */
319 (*dotp)->l_used += n;
320 cp2 = &(*dotp)->l_text[(*dotp)->l_used];
321 cp1 = cp2-n;
322 while (cp1 != &(*dotp)->l_text[*doto])
323 *--cp2 = *--cp1;
326 ac.c = (c & CELLMASK);
327 while(n--) /* add the chars */
328 (*dotp)->l_text[(*doto)++] = ac;
330 return(TRUE);
335 * Insert a newline into the buffer at the current location of dot in the
336 * current window. The funny ass-backwards way it does things is not a botch;
337 * it just makes the last line in the file not a special case. Return TRUE if
338 * everything works out and FALSE on error (memory allocation failure). The
339 * update of dot and mark is a bit easier than in the above case, because the
340 * split forces more updating.
343 lnewline(void)
345 register CELL *cp1;
346 register CELL *cp2;
347 register LINE *lp1;
348 register LINE *lp2;
349 register int doto;
350 register WINDOW *wp;
352 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
353 return(rdonly()); /* we are in read only mode */
355 lchange(WFHARD);
356 lp1 = curwp->w_dotp; /* Get the address and */
357 doto = curwp->w_doto; /* offset of "." */
358 if ((lp2=lalloc(doto)) == NULL) /* New first half line */
359 return (FALSE);
361 cp1 = &lp1->l_text[0]; /* Shuffle text around */
362 cp2 = &lp2->l_text[0];
363 while (cp1 != &lp1->l_text[doto])
364 *cp2++ = *cp1++;
366 cp2 = &lp1->l_text[0];
367 while (cp1 != &lp1->l_text[lp1->l_used])
368 *cp2++ = *cp1++;
370 lp1->l_used -= doto;
371 lp2->l_bp = lp1->l_bp;
372 lp1->l_bp = lp2;
373 lp2->l_bp->l_fp = lp2;
374 lp2->l_fp = lp1;
375 wp = wheadp; /* Windows */
376 while (wp != NULL) {
377 if (wp->w_linep == lp1)
378 wp->w_linep = lp2;
380 if (wp->w_dotp == lp1) {
381 if (wp->w_doto < doto)
382 wp->w_dotp = lp2;
383 else
384 wp->w_doto -= doto;
387 if (wp->w_imarkp == lp1) { /* ADDED for internal mark */
388 if (wp->w_imarko < doto)
389 wp->w_imarkp = lp2;
390 else
391 wp->w_imarko -= doto;
394 if (wp->w_markp == lp1) {
395 if (wp->w_marko < doto)
396 wp->w_markp = lp2;
397 else
398 wp->w_marko -= doto;
400 wp = wp->w_wndp;
404 * Keep track of the number of lines in the buffer.
406 ++curbp->b_linecnt;
407 return (TRUE);
412 * This function deletes "n" characters, starting at dot. It understands how do deal
413 * with end of lines, etc. It returns TRUE if all of the characters were
414 * deleted, and FALSE if they were not (because dot ran into the end of the
415 * buffer. The "preserve" function is used to save what was deleted.
418 ldelete(long n, int (*preserve)(UCS))
420 register CELL *cp1;
421 register CELL *cp2;
422 register LINE *dotp;
423 register int doto;
424 register int chunk;
425 register WINDOW *wp;
427 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
428 return(rdonly()); /* we are in read only mode */
430 while (n != 0) {
431 dotp = curwp->w_dotp;
432 doto = curwp->w_doto;
433 if (dotp == curbp->b_linep) /* Hit end of buffer. */
434 return (FALSE);
435 chunk = dotp->l_used-doto; /* Size of chunk. */
436 if (chunk > n)
437 chunk = n;
438 if (chunk == 0) { /* End of line, merge. */
439 lchange(WFHARD);
440 if (ldelnewline() == FALSE
441 || (preserve ? (*preserve)('\n') == FALSE : 0))
442 return (FALSE);
443 --n;
444 continue;
447 lchange(WFEDIT);
448 cp1 = &dotp->l_text[doto]; /* Scrunch text. */
449 cp2 = cp1 + chunk;
450 if (preserve) { /* Kill? */
451 while (cp1 != cp2) {
452 if ((*preserve)(cp1->c) == FALSE)
453 return (FALSE);
454 ++cp1;
456 cp1 = &dotp->l_text[doto];
459 while (cp2 != &dotp->l_text[dotp->l_used])
460 *cp1++ = *cp2++;
462 dotp->l_used -= chunk;
463 wp = wheadp; /* Fix windows */
464 while (wp != NULL) {
465 if (wp->w_dotp==dotp && wp->w_doto>=doto) {
466 wp->w_doto -= chunk;
467 if (wp->w_doto < doto)
468 wp->w_doto = doto;
471 if (wp->w_markp==dotp && wp->w_marko>=doto) {
472 wp->w_marko -= chunk;
473 if (wp->w_marko < doto)
474 wp->w_marko = doto;
477 if (wp->w_imarkp==dotp && wp->w_imarko>=doto) {
478 wp->w_imarko -= chunk;
479 if (wp->w_imarko < doto)
480 wp->w_imarko = doto;
483 wp = wp->w_wndp;
485 n -= chunk;
488 #ifdef _WINDOWS
489 if (preserve == kinsert && ksize() > 0)
490 mswin_killbuftoclip (kremove);
491 #endif
493 return (TRUE);
497 * Delete a newline. Join the current line with the next line. If the next line
498 * is the magic header line always return TRUE; merging the last line with the
499 * header line can be thought of as always being a successful operation, even
500 * if nothing is done, and this makes the kill buffer work "right". Easy cases
501 * can be done by shuffling data around. Hard cases require that lines be moved
502 * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
503 * "ldelete" only.
506 ldelnewline(void)
508 register CELL *cp1;
509 register CELL *cp2;
510 register LINE *lp1;
511 register LINE *lp2;
512 register LINE *lp3;
513 register WINDOW *wp;
515 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
516 return(rdonly()); /* we are in read only mode */
518 lp1 = curwp->w_dotp;
519 lp2 = lp1->l_fp;
520 if (lp2 == curbp->b_linep) { /* At the buffer end. */
521 if (lp1->l_used == 0) { /* Blank line. */
522 lfree(lp1);
523 --curbp->b_linecnt;
526 return (TRUE);
529 if (lp2->l_used <= lp1->l_size-lp1->l_used) {
530 cp1 = &lp1->l_text[lp1->l_used];
531 cp2 = &lp2->l_text[0];
532 while (cp2 != &lp2->l_text[lp2->l_used])
533 *cp1++ = *cp2++;
535 wp = wheadp;
536 while (wp != NULL) {
537 if (wp->w_linep == lp2)
538 wp->w_linep = lp1;
539 if (wp->w_dotp == lp2) {
540 wp->w_dotp = lp1;
541 wp->w_doto += lp1->l_used;
543 if (wp->w_markp == lp2) {
544 wp->w_markp = lp1;
545 wp->w_marko += lp1->l_used;
547 if (wp->w_imarkp == lp2) {
548 wp->w_imarkp = lp1;
549 wp->w_imarko += lp1->l_used;
551 wp = wp->w_wndp;
553 lp1->l_used += lp2->l_used;
554 lp1->l_fp = lp2->l_fp;
555 lp2->l_fp->l_bp = lp1;
556 free((char *) lp2);
557 --curbp->b_linecnt;
558 return (TRUE);
561 if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
562 return (FALSE);
564 cp1 = &lp1->l_text[0];
565 cp2 = &lp3->l_text[0];
566 while (cp1 != &lp1->l_text[lp1->l_used])
567 *cp2++ = *cp1++;
569 cp1 = &lp2->l_text[0];
570 while (cp1 != &lp2->l_text[lp2->l_used])
571 *cp2++ = *cp1++;
573 lp1->l_bp->l_fp = lp3;
574 lp3->l_fp = lp2->l_fp;
575 lp2->l_fp->l_bp = lp3;
576 lp3->l_bp = lp1->l_bp;
577 wp = wheadp;
578 while (wp != NULL) {
579 if (wp->w_linep==lp1 || wp->w_linep==lp2)
580 wp->w_linep = lp3;
581 if (wp->w_dotp == lp1)
582 wp->w_dotp = lp3;
583 else if (wp->w_dotp == lp2) {
584 wp->w_dotp = lp3;
585 wp->w_doto += lp1->l_used;
587 if (wp->w_markp == lp1)
588 wp->w_markp = lp3;
589 else if (wp->w_markp == lp2) {
590 wp->w_markp = lp3;
591 wp->w_marko += lp1->l_used;
593 if (wp->w_imarkp == lp1)
594 wp->w_imarkp = lp3;
595 else if (wp->w_imarkp == lp2) {
596 wp->w_imarkp = lp3;
597 wp->w_imarko += lp1->l_used;
599 wp = wp->w_wndp;
602 free((char *) lp1);
603 free((char *) lp2);
604 --curbp->b_linecnt;
605 return (TRUE);
610 * Tell the caller if the given line is blank or not.
613 lisblank(LINE *line)
615 int n = 0;
616 UCS qstr[NLINE];
618 n = (glo_quote_str
619 && quote_match(glo_quote_str, line, qstr, NLINE))
620 ? ucs4_strlen(qstr) : 0;
622 for(; n < llength(line); n++)
623 if(!ucs4_isspace(lgetc(line, n).c)
624 || lgetc(line, n).c >= 0xff
625 || (unsigned char) lgetc(line,n).c != NBSPC)
626 return(FALSE);
628 return(TRUE);
633 * Delete all of the text saved in the kill buffer. Called by commands when a
634 * new kill context is being created. The kill buffer array is released, just
635 * in case the buffer has grown to immense size. No errors.
637 void
638 kdelete(void)
640 pkbufdel(&kbufp);
643 void
644 fdelete(void)
646 pkbufdel(&fbufp);
649 void
650 pkbufdel(struct pkbuf **buf)
652 if (*buf) {
653 pkchunkdel(&(*buf)->first);
654 free((char *) *buf);
655 *buf = NULL;
660 void
661 pkchunkdel(struct pkchunk **chunk)
663 if(chunk){
664 if((*chunk)->next)
665 pkchunkdel(&(*chunk)->next);
667 free((char *) *chunk);
668 *chunk = NULL;
674 * Insert a character to the kill buffer, enlarging the buffer if there isn't
675 * any room. Always grow the buffer in chunks, on the assumption that if you
676 * put something in the kill buffer you are going to put more stuff there too
677 * later. Return TRUE if all is well, and FALSE on errors.
680 kinsert(UCS c)
682 return(pkbufinsert(c, &kbufp));
686 finsert(UCS c)
688 return(pkbufinsert(c, &fbufp));
692 pkbufinsert(UCS c, struct pkbuf **buf)
694 if(!*buf){
695 if((*buf = (struct pkbuf *) malloc(sizeof(struct pkbuf))) != NULL)
696 memset(*buf, 0, sizeof(struct pkbuf));
697 else
698 return(FALSE);
701 if((*buf)->total % KBLOCK == 0){
702 struct pkchunk *p = (*buf)->last;
703 if(((*buf)->last = (struct pkchunk *) malloc(sizeof(struct pkchunk))) != NULL){
704 memset((*buf)->last, 0, sizeof(struct pkchunk));
705 if(p)
706 p->next = (*buf)->last;
707 else
708 (*buf)->first = (*buf)->last;
710 else
711 return(FALSE);
714 (*buf)->last->bufp[(*buf)->last->used++] = c;
715 (*buf)->total++;
716 return (TRUE);
721 * These functions get characters from the requested buffer. If the
722 * character index "n" is off the end, it returns "-1". This lets the
723 * caller just scan along until it gets a "-1" back.
725 long
726 kremove(int n)
728 return(pkbufremove(n, kbufp));
731 long
732 fremove(int n)
734 return(pkbufremove(n, fbufp));
739 * This would be type UCS except that it needs to return -1.
741 long
742 pkbufremove(int n, struct pkbuf *buf)
744 if(n >= 0 && buf && n < buf->total){
745 register struct pkchunk *p = buf->first;
746 int block = n / KBLOCK;
748 while(block--)
749 if(!(p = p->next))
750 return(-1);
752 return(p->bufp[n % KBLOCK]);
754 else
755 return(-1);
760 * This function just returns the current size of the kill buffer
763 ksize(void)
765 return(kbufp ? (int) kbufp->total : 0);
769 static REGION last_region_added;
771 void
772 set_last_region_added(REGION *region)
774 if(region)
775 last_region_added = (*region);
776 else
777 memset(&last_region_added, 0, sizeof(last_region_added));
781 REGION *
782 get_last_region_added(void)
784 return(&last_region_added);