* clear out some warnings by gcc 9.3.1.
[alpine.git] / pico / line.c
blob109f52d9cbc8a0cde3a8c00888e7d2ba535a64f3
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-2020 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 memset((void *)lp->l_text, 0, size*sizeof(CELL));
95 return (lp);
99 * Delete line "lp". Fix all of the links that might point at it (they are
100 * moved to offset 0 of the next line. Unlink the line from whatever buffer it
101 * might be in. Release the memory. The buffers are updated too; the magic
102 * conditions described in the above comments don't hold here.
104 void
105 lfree(LINE *lp)
107 register BUFFER *bp;
108 register WINDOW *wp;
110 wp = wheadp;
111 while (wp != NULL) {
112 if (wp->w_linep == lp)
113 wp->w_linep = lp->l_fp;
115 if (wp->w_dotp == lp) {
116 wp->w_dotp = lp->l_fp;
117 wp->w_doto = 0;
120 if (wp->w_markp == lp) {
121 wp->w_markp = lp->l_fp;
122 wp->w_marko = 0;
125 if (wp->w_imarkp == lp) {
126 wp->w_imarkp = lp->l_fp;
127 wp->w_imarko = 0;
130 wp = wp->w_wndp;
133 bp = bheadp;
134 while (bp != NULL) {
135 if (bp->b_nwnd == 0) {
136 if (bp->b_dotp == lp) {
137 bp->b_dotp = lp->l_fp;
138 bp->b_doto = 0;
141 if (bp->b_markp == lp) {
142 bp->b_markp = lp->l_fp;
143 bp->b_marko = 0;
147 bp = bp->b_bufp;
150 lp->l_bp->l_fp = lp->l_fp;
151 lp->l_fp->l_bp = lp->l_bp;
152 free((char *) lp);
157 * This routine gets called when a character is changed in place in the current
158 * buffer. It updates all of the required flags in the buffer and window
159 * system. The flag used is passed as an argument; if the buffer is being
160 * displayed in more than 1 window we change EDIT to HARD. Set MODE if the
161 * mode line needs to be updated (the "*" has to be set).
163 void
164 lchange(int flag)
166 register WINDOW *wp;
168 if (curbp->b_nwnd != 1) /* Ensure hard. */
169 flag = WFHARD;
171 if ((curbp->b_flag&BFCHG) == 0) { /* First change, so */
172 if(Pmaster == NULL)
173 flag |= WFMODE; /* update mode lines. */
174 curbp->b_flag |= BFCHG;
177 wp = wheadp;
178 while (wp != NULL) {
179 if (wp->w_bufp == curbp)
180 wp->w_flag |= flag;
181 wp = wp->w_wndp;
187 * insert spaces forward into text
188 * default flag and numeric argument
190 void
191 insspace(int f, int n)
193 linsert(n, ' ');
194 backchar(f, n);
198 * Insert "n" copies of the character "c" at the current location of dot. In
199 * the easy case all that happens is the text is stored in the line. In the
200 * hard case, the line has to be reallocated. When the window list is updated,
201 * take special care; I screwed it up once. You always update dot in the
202 * current window. You update mark, and a dot in another window, if it is
203 * greater than the place where you did the insert. Return TRUE if all is
204 * well, and FALSE on errors.
207 linsert(int n, UCS c)
209 register LINE *dotp;
210 register int doto;
211 register WINDOW *wp;
213 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
214 return(rdonly()); /* we are in read only mode */
216 dotp = curwp->w_dotp;
217 doto = curwp->w_doto;
218 lchange(WFEDIT);
220 if(!geninsert(&(curwp->w_dotp), &(curwp->w_doto), curbp->b_linep,
221 c, (curwp->w_markp) ? 1 : 0, n, &curbp->b_linecnt))
222 return(FALSE);
224 wp = wheadp; /* Update windows */
225 while (wp != NULL) {
226 if (wp->w_linep == dotp)
227 wp->w_linep = wp->w_dotp;
229 if (wp->w_imarkp == dotp) { /* added for internal mark */
230 wp->w_imarkp = wp->w_dotp;
231 if (wp->w_imarko > doto)
232 wp->w_imarko += n;
235 if (wp->w_markp == dotp) {
236 wp->w_markp = dotp;
237 if (wp->w_marko > doto)
238 wp->w_marko += n;
240 wp = wp->w_wndp;
243 return (TRUE);
248 * geninsert - do the actual work of inserting a character into
249 * the list of lines.
252 geninsert(LINE **dotp, int *doto, LINE *linep, UCS c, int attb, int n, long *lines)
254 register LINE *lp1;
255 register LINE *lp2;
256 register CELL *cp1;
257 register CELL *cp2;
258 CELL ac;
260 ac.a = attb;
261 if (*dotp == linep) { /* At the end: special */
262 if (*doto != 0) {
263 emlwrite("Programmer botch: geninsert", NULL);
264 return (FALSE);
267 if ((lp1=lalloc(n)) == NULL) /* Allocate new line */
268 return (FALSE);
270 lp2 = (*dotp)->l_bp; /* Previous line */
271 lp2->l_fp = lp1; /* Link in */
272 lp1->l_fp = *dotp;
273 (*dotp)->l_bp = lp1;
274 lp1->l_bp = lp2;
275 *doto = n;
276 *dotp = lp1;
277 ac.c = (c & CELLMASK);
278 cp1 = &(*dotp)->l_text[0];
279 while(n--)
280 *cp1++ = ac;
282 if(lines)
283 (*lines)++;
285 return (TRUE);
288 if ((*dotp)->l_used+n > (*dotp)->l_size) { /* Hard: reallocate */
289 if ((lp1=lalloc((*dotp)->l_used+n)) == NULL)
290 return (FALSE);
292 cp1 = &(*dotp)->l_text[0];
293 cp2 = &lp1->l_text[0];
294 while (cp1 != &(*dotp)->l_text[*doto])
295 *cp2++ = *cp1++;
297 cp2 += n;
298 while (cp1 != &(*dotp)->l_text[(*dotp)->l_used])
299 *cp2++ = *cp1++;
301 (*dotp)->l_bp->l_fp = lp1;
302 lp1->l_fp = (*dotp)->l_fp;
303 (*dotp)->l_fp->l_bp = lp1;
304 lp1->l_bp = (*dotp)->l_bp;
306 /* global may be keeping track of mark/imark */
307 if(wheadp){
308 if (wheadp->w_imarkp == *dotp)
309 wheadp->w_imarkp = lp1;
311 if (wheadp->w_markp == *dotp)
312 wheadp->w_markp = lp1;
315 free((char *) (*dotp));
316 *dotp = lp1;
317 } else { /* Easy: in place */
318 (*dotp)->l_used += n;
319 cp2 = &(*dotp)->l_text[(*dotp)->l_used];
320 cp1 = cp2-n;
321 while (cp1 != &(*dotp)->l_text[*doto])
322 *--cp2 = *--cp1;
325 ac.c = (c & CELLMASK);
326 while(n--) /* add the chars */
327 (*dotp)->l_text[(*doto)++] = ac;
329 return(TRUE);
334 * Insert a newline into the buffer at the current location of dot in the
335 * current window. The funny ass-backwards way it does things is not a botch;
336 * it just makes the last line in the file not a special case. Return TRUE if
337 * everything works out and FALSE on error (memory allocation failure). The
338 * update of dot and mark is a bit easier than in the above case, because the
339 * split forces more updating.
342 lnewline(void)
344 register CELL *cp1;
345 register CELL *cp2;
346 register LINE *lp1;
347 register LINE *lp2;
348 register int doto;
349 register WINDOW *wp;
351 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
352 return(rdonly()); /* we are in read only mode */
354 lchange(WFHARD);
355 lp1 = curwp->w_dotp; /* Get the address and */
356 doto = curwp->w_doto; /* offset of "." */
357 if ((lp2=lalloc(doto)) == NULL) /* New first half line */
358 return (FALSE);
360 cp1 = &lp1->l_text[0]; /* Shuffle text around */
361 cp2 = &lp2->l_text[0];
362 while (cp1 != &lp1->l_text[doto])
363 *cp2++ = *cp1++;
365 cp2 = &lp1->l_text[0];
366 while (cp1 != &lp1->l_text[lp1->l_used])
367 *cp2++ = *cp1++;
369 lp1->l_used -= doto;
370 lp2->l_bp = lp1->l_bp;
371 lp1->l_bp = lp2;
372 lp2->l_bp->l_fp = lp2;
373 lp2->l_fp = lp1;
374 wp = wheadp; /* Windows */
375 while (wp != NULL) {
376 if (wp->w_linep == lp1)
377 wp->w_linep = lp2;
379 if (wp->w_dotp == lp1) {
380 if (wp->w_doto < doto)
381 wp->w_dotp = lp2;
382 else
383 wp->w_doto -= doto;
386 if (wp->w_imarkp == lp1) { /* ADDED for internal mark */
387 if (wp->w_imarko < doto)
388 wp->w_imarkp = lp2;
389 else
390 wp->w_imarko -= doto;
393 if (wp->w_markp == lp1) {
394 if (wp->w_marko < doto)
395 wp->w_markp = lp2;
396 else
397 wp->w_marko -= doto;
399 wp = wp->w_wndp;
403 * Keep track of the number of lines in the buffer.
405 ++curbp->b_linecnt;
406 return (TRUE);
411 * This function deletes "n" characters, starting at dot. It understands how do deal
412 * with end of lines, etc. It returns TRUE if all of the characters were
413 * deleted, and FALSE if they were not (because dot ran into the end of the
414 * buffer. The "preserve" function is used to save what was deleted.
417 ldelete(long n, int (*preserve)(UCS))
419 register CELL *cp1;
420 register CELL *cp2;
421 register LINE *dotp;
422 register int doto;
423 register int chunk;
424 register WINDOW *wp;
426 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
427 return(rdonly()); /* we are in read only mode */
429 while (n != 0) {
430 dotp = curwp->w_dotp;
431 doto = curwp->w_doto;
432 if (dotp == curbp->b_linep) /* Hit end of buffer. */
433 return (FALSE);
434 chunk = dotp->l_used-doto; /* Size of chunk. */
435 if (chunk > n)
436 chunk = n;
437 if (chunk == 0) { /* End of line, merge. */
438 lchange(WFHARD);
439 if (ldelnewline() == FALSE
440 || (preserve ? (*preserve)('\n') == FALSE : 0))
441 return (FALSE);
442 --n;
443 continue;
446 lchange(WFEDIT);
447 cp1 = &dotp->l_text[doto]; /* Scrunch text. */
448 cp2 = cp1 + chunk;
449 if (preserve) { /* Kill? */
450 while (cp1 != cp2) {
451 if ((*preserve)(cp1->c) == FALSE)
452 return (FALSE);
453 ++cp1;
455 cp1 = &dotp->l_text[doto];
458 while (cp2 != &dotp->l_text[dotp->l_used])
459 *cp1++ = *cp2++;
461 dotp->l_used -= chunk;
462 wp = wheadp; /* Fix windows */
463 while (wp != NULL) {
464 if (wp->w_dotp==dotp && wp->w_doto>=doto) {
465 wp->w_doto -= chunk;
466 if (wp->w_doto < doto)
467 wp->w_doto = doto;
470 if (wp->w_markp==dotp && wp->w_marko>=doto) {
471 wp->w_marko -= chunk;
472 if (wp->w_marko < doto)
473 wp->w_marko = doto;
476 if (wp->w_imarkp==dotp && wp->w_imarko>=doto) {
477 wp->w_imarko -= chunk;
478 if (wp->w_imarko < doto)
479 wp->w_imarko = doto;
482 wp = wp->w_wndp;
484 n -= chunk;
487 #ifdef _WINDOWS
488 if (preserve == kinsert && ksize() > 0)
489 mswin_killbuftoclip (kremove);
490 #endif
492 return (TRUE);
496 * Delete a newline. Join the current line with the next line. If the next line
497 * is the magic header line always return TRUE; merging the last line with the
498 * header line can be thought of as always being a successful operation, even
499 * if nothing is done, and this makes the kill buffer work "right". Easy cases
500 * can be done by shuffling data around. Hard cases require that lines be moved
501 * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
502 * "ldelete" only.
505 ldelnewline(void)
507 register CELL *cp1;
508 register CELL *cp2;
509 register LINE *lp1;
510 register LINE *lp2;
511 register LINE *lp3;
512 register WINDOW *wp;
514 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
515 return(rdonly()); /* we are in read only mode */
517 lp1 = curwp->w_dotp;
518 lp2 = lp1->l_fp;
519 if (lp2 == curbp->b_linep) { /* At the buffer end. */
520 if (lp1->l_used == 0) { /* Blank line. */
521 lfree(lp1);
522 --curbp->b_linecnt;
525 return (TRUE);
528 if (lp2->l_used <= lp1->l_size-lp1->l_used) {
529 cp1 = &lp1->l_text[lp1->l_used];
530 cp2 = &lp2->l_text[0];
531 while (cp2 != &lp2->l_text[lp2->l_used])
532 *cp1++ = *cp2++;
534 wp = wheadp;
535 while (wp != NULL) {
536 if (wp->w_linep == lp2)
537 wp->w_linep = lp1;
538 if (wp->w_dotp == lp2) {
539 wp->w_dotp = lp1;
540 wp->w_doto += lp1->l_used;
542 if (wp->w_markp == lp2) {
543 wp->w_markp = lp1;
544 wp->w_marko += lp1->l_used;
546 if (wp->w_imarkp == lp2) {
547 wp->w_imarkp = lp1;
548 wp->w_imarko += lp1->l_used;
550 wp = wp->w_wndp;
552 lp1->l_used += lp2->l_used;
553 lp1->l_fp = lp2->l_fp;
554 lp2->l_fp->l_bp = lp1;
555 free((char *) lp2);
556 --curbp->b_linecnt;
557 return (TRUE);
560 if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
561 return (FALSE);
563 cp1 = &lp1->l_text[0];
564 cp2 = &lp3->l_text[0];
565 while (cp1 != &lp1->l_text[lp1->l_used])
566 *cp2++ = *cp1++;
568 cp1 = &lp2->l_text[0];
569 while (cp1 != &lp2->l_text[lp2->l_used])
570 *cp2++ = *cp1++;
572 lp1->l_bp->l_fp = lp3;
573 lp3->l_fp = lp2->l_fp;
574 lp2->l_fp->l_bp = lp3;
575 lp3->l_bp = lp1->l_bp;
576 wp = wheadp;
577 while (wp != NULL) {
578 if (wp->w_linep==lp1 || wp->w_linep==lp2)
579 wp->w_linep = lp3;
580 if (wp->w_dotp == lp1)
581 wp->w_dotp = lp3;
582 else if (wp->w_dotp == lp2) {
583 wp->w_dotp = lp3;
584 wp->w_doto += lp1->l_used;
586 if (wp->w_markp == lp1)
587 wp->w_markp = lp3;
588 else if (wp->w_markp == lp2) {
589 wp->w_markp = lp3;
590 wp->w_marko += lp1->l_used;
592 if (wp->w_imarkp == lp1)
593 wp->w_imarkp = lp3;
594 else if (wp->w_imarkp == lp2) {
595 wp->w_imarkp = lp3;
596 wp->w_imarko += lp1->l_used;
598 wp = wp->w_wndp;
601 free((char *) lp1);
602 free((char *) lp2);
603 --curbp->b_linecnt;
604 return (TRUE);
609 * Tell the caller if the given line is blank or not.
612 lisblank(LINE *line)
614 int n = 0;
615 UCS qstr[NLINE];
617 n = (glo_quote_str
618 && quote_match(glo_quote_str, line, qstr, NLINE))
619 ? ucs4_strlen(qstr) : 0;
621 for(; n < llength(line); n++)
622 if(!ucs4_isspace(lgetc(line, n).c)
623 || lgetc(line, n).c >= 0xff
624 || (unsigned char) lgetc(line,n).c != (unsigned char) NBSPC)
625 return(FALSE);
627 return(TRUE);
632 * Delete all of the text saved in the kill buffer. Called by commands when a
633 * new kill context is being created. The kill buffer array is released, just
634 * in case the buffer has grown to immense size. No errors.
636 void
637 kdelete(void)
639 pkbufdel(&kbufp);
642 void
643 fdelete(void)
645 pkbufdel(&fbufp);
648 void
649 pkbufdel(struct pkbuf **buf)
651 if (*buf) {
652 pkchunkdel(&(*buf)->first);
653 free((char *) *buf);
654 *buf = NULL;
659 void
660 pkchunkdel(struct pkchunk **chunk)
662 if(chunk){
663 if((*chunk)->next)
664 pkchunkdel(&(*chunk)->next);
666 free((char *) *chunk);
667 *chunk = NULL;
673 * Insert a character to the kill buffer, enlarging the buffer if there isn't
674 * any room. Always grow the buffer in chunks, on the assumption that if you
675 * put something in the kill buffer you are going to put more stuff there too
676 * later. Return TRUE if all is well, and FALSE on errors.
679 kinsert(UCS c)
681 return(pkbufinsert(c, &kbufp));
685 finsert(UCS c)
687 return(pkbufinsert(c, &fbufp));
691 pkbufinsert(UCS c, struct pkbuf **buf)
693 if(!*buf){
694 if((*buf = (struct pkbuf *) malloc(sizeof(struct pkbuf))) != NULL)
695 memset(*buf, 0, sizeof(struct pkbuf));
696 else
697 return(FALSE);
700 if((*buf)->total % KBLOCK == 0){
701 struct pkchunk *p = (*buf)->last;
702 if(((*buf)->last = (struct pkchunk *) malloc(sizeof(struct pkchunk))) != NULL){
703 memset((*buf)->last, 0, sizeof(struct pkchunk));
704 if(p)
705 p->next = (*buf)->last;
706 else
707 (*buf)->first = (*buf)->last;
709 else
710 return(FALSE);
713 (*buf)->last->bufp[(*buf)->last->used++] = c;
714 (*buf)->total++;
715 return (TRUE);
720 * These functions get characters from the requested buffer. If the
721 * character index "n" is off the end, it returns "-1". This lets the
722 * caller just scan along until it gets a "-1" back.
724 long
725 kremove(int n)
727 return(pkbufremove(n, kbufp));
730 long
731 fremove(int n)
733 return(pkbufremove(n, fbufp));
738 * This would be type UCS except that it needs to return -1.
740 long
741 pkbufremove(int n, struct pkbuf *buf)
743 if(n >= 0 && buf && n < buf->total){
744 register struct pkchunk *p = buf->first;
745 int block = n / KBLOCK;
747 while(block--)
748 if(!(p = p->next))
749 return(-1);
751 return(p->bufp[n % KBLOCK]);
753 else
754 return(-1);
759 * This function just returns the current size of the kill buffer
762 ksize(void)
764 return(kbufp ? (int) kbufp->total : 0);
768 static REGION last_region_added;
770 void
771 set_last_region_added(REGION *region)
773 if(region)
774 last_region_added = (*region);
775 else
776 memset(&last_region_added, 0, sizeof(last_region_added));
780 REGION *
781 get_last_region_added(void)
783 return(&last_region_added);