2 * ========================================================================
3 * Copyright 2006-2007 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
14 * Program: Line management routines
18 * The functions in this file are a general set of line management utilities.
19 * They are the only routines that touch the text. They also touch the buffer
20 * and window structures, to make sure that the necessary updating gets done.
21 * There are routines in this file that handle the kill buffer too. It isn't
22 * here for any good reason.
24 * Note that this code only updates the dot and mark values in the window list.
25 * Since all the code acts on the current window, the buffer that we are
26 * editing must be being displayed, which means that "b_nwnd" is non zero,
27 * which means that the dot and mark values in the buffer headers are nonsense.
32 #define NBLOCK 16 /* Line block chunk size */
33 #define KBLOCK 1024 /* Kill buffer block size */
37 * Struct to manage the kill and justify buffers
40 short used
; /* # of characters used in this buffer */
41 UCS bufp
[KBLOCK
]; /* buffer containing text */
42 struct pkchunk
*next
; /* pointer to next chunk */
46 long total
; /* # of UCS characters used in buffer */
47 struct pkchunk
*first
; /* first one of these in the chain */
48 struct pkchunk
*last
; /* last one of these in the chain */
52 void insspace(int, int);
53 int ldelnewline(void);
54 void pkbufdel(struct pkbuf
**);
55 void pkchunkdel(struct pkchunk
**);
56 int pkbufinsert(UCS
, struct pkbuf
**);
57 long pkbufremove(int, struct pkbuf
*);
61 * This routine allocates a block of memory large enough to hold a LINE
62 * containing "used" characters. The block is always rounded up a bit. Return
63 * a pointer to the new block, or NULL if there isn't any memory left. Print a
64 * message in the message line if no space.
69 register LINE
*lp
= NULL
;
72 static int displayed
= 0;
74 if((size
= (used
+NBLOCK
-1) & ~(NBLOCK
-1)) > NLINE
)
77 if (size
== 0) /* Assume that an empty */
78 size
= NBLOCK
; /* line is for type-in. */
80 if (displayed
== 0 && (lp
= (LINE
*) malloc(sizeof(LINE
)+(size
*sizeof(CELL
)))) == NULL
){
81 eml
.s
= comatose(size
);
82 emlwrite("Cannot allocate %s bytes (read file incomplete)", &eml
);
89 lp
->l_sig
= 0; /* assume it is not a signature line */
90 memset((void *)lp
->l_text
, 0, size
*sizeof(CELL
));
95 * Delete line "lp". Fix all of the links that might point at it (they are
96 * moved to offset 0 of the next line. Unlink the line from whatever buffer it
97 * might be in. Release the memory. The buffers are updated too; the magic
98 * conditions described in the above comments don't hold here.
108 if (wp
->w_linep
== lp
)
109 wp
->w_linep
= lp
->l_fp
;
111 if (wp
->w_dotp
== lp
) {
112 wp
->w_dotp
= lp
->l_fp
;
116 if (wp
->w_markp
== lp
) {
117 wp
->w_markp
= lp
->l_fp
;
121 if (wp
->w_imarkp
== lp
) {
122 wp
->w_imarkp
= lp
->l_fp
;
131 if (bp
->b_nwnd
== 0) {
132 if (bp
->b_dotp
== lp
) {
133 bp
->b_dotp
= lp
->l_fp
;
137 if (bp
->b_markp
== lp
) {
138 bp
->b_markp
= lp
->l_fp
;
146 lp
->l_bp
->l_fp
= lp
->l_fp
;
147 lp
->l_fp
->l_bp
= lp
->l_bp
;
153 * This routine gets called when a character is changed in place in the current
154 * buffer. It updates all of the required flags in the buffer and window
155 * system. The flag used is passed as an argument; if the buffer is being
156 * displayed in more than 1 window we change EDIT to HARD. Set MODE if the
157 * mode line needs to be updated (the "*" has to be set).
164 if (curbp
->b_nwnd
!= 1) /* Ensure hard. */
167 if ((curbp
->b_flag
&BFCHG
) == 0) { /* First change, so */
169 flag
|= WFMODE
; /* update mode lines. */
170 curbp
->b_flag
|= BFCHG
;
175 if (wp
->w_bufp
== curbp
)
183 * insert spaces forward into text
184 * default flag and numeric argument
187 insspace(int f
, int n
)
194 * Insert "n" copies of the character "c" at the current location of dot. In
195 * the easy case all that happens is the text is stored in the line. In the
196 * hard case, the line has to be reallocated. When the window list is updated,
197 * take special care; I screwed it up once. You always update dot in the
198 * current window. You update mark, and a dot in another window, if it is
199 * greater than the place where you did the insert. Return TRUE if all is
200 * well, and FALSE on errors.
203 linsert(int n
, UCS c
)
209 if (curbp
->b_mode
&MDVIEW
) /* don't allow this command if */
210 return(rdonly()); /* we are in read only mode */
212 dotp
= curwp
->w_dotp
;
213 doto
= curwp
->w_doto
;
216 if(!geninsert(&(curwp
->w_dotp
), &(curwp
->w_doto
), curbp
->b_linep
,
217 c
, (curwp
->w_markp
) ? 1 : 0, n
, &curbp
->b_linecnt
))
220 wp
= wheadp
; /* Update windows */
222 if (wp
->w_linep
== dotp
)
223 wp
->w_linep
= wp
->w_dotp
;
225 if (wp
->w_imarkp
== dotp
) { /* added for internal mark */
226 wp
->w_imarkp
= wp
->w_dotp
;
227 if (wp
->w_imarko
> doto
)
231 if (wp
->w_markp
== dotp
) {
233 if (wp
->w_marko
> doto
)
244 * geninsert - do the actual work of inserting a character into
248 geninsert(LINE
**dotp
, int *doto
, LINE
*linep
, UCS c
, int attb
, int n
, long *lines
)
257 if (*dotp
== linep
) { /* At the end: special */
259 emlwrite("Programmer botch: geninsert", NULL
);
263 if ((lp1
=lalloc(n
)) == NULL
) /* Allocate new line */
266 lp2
= (*dotp
)->l_bp
; /* Previous line */
267 lp2
->l_fp
= lp1
; /* Link in */
273 ac
.c
= (c
& CELLMASK
);
274 cp1
= &(*dotp
)->l_text
[0];
284 if ((*dotp
)->l_used
+n
> (*dotp
)->l_size
) { /* Hard: reallocate */
285 if ((lp1
=lalloc((*dotp
)->l_used
+n
)) == NULL
)
288 cp1
= &(*dotp
)->l_text
[0];
289 cp2
= &lp1
->l_text
[0];
290 while (cp1
!= &(*dotp
)->l_text
[*doto
])
294 while (cp1
!= &(*dotp
)->l_text
[(*dotp
)->l_used
])
297 (*dotp
)->l_bp
->l_fp
= lp1
;
298 lp1
->l_fp
= (*dotp
)->l_fp
;
299 (*dotp
)->l_fp
->l_bp
= lp1
;
300 lp1
->l_bp
= (*dotp
)->l_bp
;
302 /* global may be keeping track of mark/imark */
304 if (wheadp
->w_imarkp
== *dotp
)
305 wheadp
->w_imarkp
= lp1
;
307 if (wheadp
->w_markp
== *dotp
)
308 wheadp
->w_markp
= lp1
;
311 free((char *) (*dotp
));
313 } else { /* Easy: in place */
314 (*dotp
)->l_used
+= n
;
315 cp2
= &(*dotp
)->l_text
[(*dotp
)->l_used
];
317 while (cp1
!= &(*dotp
)->l_text
[*doto
])
321 ac
.c
= (c
& CELLMASK
);
322 while(n
--) /* add the chars */
323 (*dotp
)->l_text
[(*doto
)++] = ac
;
330 * Insert a newline into the buffer at the current location of dot in the
331 * current window. The funny ass-backwards way it does things is not a botch;
332 * it just makes the last line in the file not a special case. Return TRUE if
333 * everything works out and FALSE on error (memory allocation failure). The
334 * update of dot and mark is a bit easier than in the above case, because the
335 * split forces more updating.
347 if (curbp
->b_mode
&MDVIEW
) /* don't allow this command if */
348 return(rdonly()); /* we are in read only mode */
351 lp1
= curwp
->w_dotp
; /* Get the address and */
352 doto
= curwp
->w_doto
; /* offset of "." */
353 if ((lp2
=lalloc(doto
)) == NULL
) /* New first half line */
356 cp1
= &lp1
->l_text
[0]; /* Shuffle text around */
357 cp2
= &lp2
->l_text
[0];
358 while (cp1
!= &lp1
->l_text
[doto
])
361 cp2
= &lp1
->l_text
[0];
362 while (cp1
!= &lp1
->l_text
[lp1
->l_used
])
366 lp2
->l_bp
= lp1
->l_bp
;
368 lp2
->l_bp
->l_fp
= lp2
;
370 wp
= wheadp
; /* Windows */
372 if (wp
->w_linep
== lp1
)
375 if (wp
->w_dotp
== lp1
) {
376 if (wp
->w_doto
< doto
)
382 if (wp
->w_imarkp
== lp1
) { /* ADDED for internal mark */
383 if (wp
->w_imarko
< doto
)
386 wp
->w_imarko
-= doto
;
389 if (wp
->w_markp
== lp1
) {
390 if (wp
->w_marko
< doto
)
399 * Keep track of the number of lines in the buffer.
407 * This function deletes "n" characters, starting at dot. It understands how do deal
408 * with end of lines, etc. It returns TRUE if all of the characters were
409 * deleted, and FALSE if they were not (because dot ran into the end of the
410 * buffer. The "preserve" function is used to save what was deleted.
413 ldelete(long n
, int (*preserve
)(UCS
))
422 if (curbp
->b_mode
&MDVIEW
) /* don't allow this command if */
423 return(rdonly()); /* we are in read only mode */
426 dotp
= curwp
->w_dotp
;
427 doto
= curwp
->w_doto
;
428 if (dotp
== curbp
->b_linep
) /* Hit end of buffer. */
430 chunk
= dotp
->l_used
-doto
; /* Size of chunk. */
433 if (chunk
== 0) { /* End of line, merge. */
435 if (ldelnewline() == FALSE
436 || (preserve
? (*preserve
)('\n') == FALSE
: 0))
443 cp1
= &dotp
->l_text
[doto
]; /* Scrunch text. */
445 if (preserve
) { /* Kill? */
447 if ((*preserve
)(cp1
->c
) == FALSE
)
451 cp1
= &dotp
->l_text
[doto
];
454 while (cp2
!= &dotp
->l_text
[dotp
->l_used
])
457 dotp
->l_used
-= chunk
;
458 wp
= wheadp
; /* Fix windows */
460 if (wp
->w_dotp
==dotp
&& wp
->w_doto
>=doto
) {
462 if (wp
->w_doto
< doto
)
466 if (wp
->w_markp
==dotp
&& wp
->w_marko
>=doto
) {
467 wp
->w_marko
-= chunk
;
468 if (wp
->w_marko
< doto
)
472 if (wp
->w_imarkp
==dotp
&& wp
->w_imarko
>=doto
) {
473 wp
->w_imarko
-= chunk
;
474 if (wp
->w_imarko
< doto
)
484 if (preserve
== kinsert
&& ksize() > 0)
485 mswin_killbuftoclip (kremove
);
492 * Delete a newline. Join the current line with the next line. If the next line
493 * is the magic header line always return TRUE; merging the last line with the
494 * header line can be thought of as always being a successful operation, even
495 * if nothing is done, and this makes the kill buffer work "right". Easy cases
496 * can be done by shuffling data around. Hard cases require that lines be moved
497 * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
510 if (curbp
->b_mode
&MDVIEW
) /* don't allow this command if */
511 return(rdonly()); /* we are in read only mode */
515 if (lp2
== curbp
->b_linep
) { /* At the buffer end. */
516 if (lp1
->l_used
== 0) { /* Blank line. */
524 if (lp2
->l_used
<= lp1
->l_size
-lp1
->l_used
) {
525 cp1
= &lp1
->l_text
[lp1
->l_used
];
526 cp2
= &lp2
->l_text
[0];
527 while (cp2
!= &lp2
->l_text
[lp2
->l_used
])
532 if (wp
->w_linep
== lp2
)
534 if (wp
->w_dotp
== lp2
) {
536 wp
->w_doto
+= lp1
->l_used
;
538 if (wp
->w_markp
== lp2
) {
540 wp
->w_marko
+= lp1
->l_used
;
542 if (wp
->w_imarkp
== lp2
) {
544 wp
->w_imarko
+= lp1
->l_used
;
548 lp1
->l_used
+= lp2
->l_used
;
549 lp1
->l_fp
= lp2
->l_fp
;
550 lp2
->l_fp
->l_bp
= lp1
;
556 if ((lp3
=lalloc(lp1
->l_used
+lp2
->l_used
)) == NULL
)
559 cp1
= &lp1
->l_text
[0];
560 cp2
= &lp3
->l_text
[0];
561 while (cp1
!= &lp1
->l_text
[lp1
->l_used
])
564 cp1
= &lp2
->l_text
[0];
565 while (cp1
!= &lp2
->l_text
[lp2
->l_used
])
568 lp1
->l_bp
->l_fp
= lp3
;
569 lp3
->l_fp
= lp2
->l_fp
;
570 lp2
->l_fp
->l_bp
= lp3
;
571 lp3
->l_bp
= lp1
->l_bp
;
574 if (wp
->w_linep
==lp1
|| wp
->w_linep
==lp2
)
576 if (wp
->w_dotp
== lp1
)
578 else if (wp
->w_dotp
== lp2
) {
580 wp
->w_doto
+= lp1
->l_used
;
582 if (wp
->w_markp
== lp1
)
584 else if (wp
->w_markp
== lp2
) {
586 wp
->w_marko
+= lp1
->l_used
;
588 if (wp
->w_imarkp
== lp1
)
590 else if (wp
->w_imarkp
== lp2
) {
592 wp
->w_imarko
+= lp1
->l_used
;
605 * Tell the caller if the given line is blank or not.
614 && quote_match(glo_quote_str
, line
, qstr
, NLINE
))
615 ? ucs4_strlen(qstr
) : 0;
617 for(; n
< llength(line
); n
++)
618 if(!ucs4_isspace(lgetc(line
, n
).c
)
619 || lgetc(line
, n
).c
>= 0xff
620 || (unsigned char) lgetc(line
,n
).c
!= (unsigned char) NBSPC
)
628 * Delete all of the text saved in the kill buffer. Called by commands when a
629 * new kill context is being created. The kill buffer array is released, just
630 * in case the buffer has grown to immense size. No errors.
645 pkbufdel(struct pkbuf
**buf
)
648 pkchunkdel(&(*buf
)->first
);
656 pkchunkdel(struct pkchunk
**chunk
)
660 pkchunkdel(&(*chunk
)->next
);
662 free((char *) *chunk
);
669 * Insert a character to the kill buffer, enlarging the buffer if there isn't
670 * any room. Always grow the buffer in chunks, on the assumption that if you
671 * put something in the kill buffer you are going to put more stuff there too
672 * later. Return TRUE if all is well, and FALSE on errors.
677 return(pkbufinsert(c
, &kbufp
));
683 return(pkbufinsert(c
, &fbufp
));
687 pkbufinsert(UCS c
, struct pkbuf
**buf
)
690 if((*buf
= (struct pkbuf
*) malloc(sizeof(struct pkbuf
))) != NULL
)
691 memset(*buf
, 0, sizeof(struct pkbuf
));
696 if((*buf
)->total
% KBLOCK
== 0){
697 struct pkchunk
*p
= (*buf
)->last
;
698 if(((*buf
)->last
= (struct pkchunk
*) malloc(sizeof(struct pkchunk
))) != NULL
){
699 memset((*buf
)->last
, 0, sizeof(struct pkchunk
));
701 p
->next
= (*buf
)->last
;
703 (*buf
)->first
= (*buf
)->last
;
709 (*buf
)->last
->bufp
[(*buf
)->last
->used
++] = c
;
716 * These functions get characters from the requested buffer. If the
717 * character index "n" is off the end, it returns "-1". This lets the
718 * caller just scan along until it gets a "-1" back.
723 return(pkbufremove(n
, kbufp
));
729 return(pkbufremove(n
, fbufp
));
734 * This would be type UCS except that it needs to return -1.
737 pkbufremove(int n
, struct pkbuf
*buf
)
739 if(n
>= 0 && buf
&& n
< buf
->total
){
740 register struct pkchunk
*p
= buf
->first
;
741 int block
= n
/ KBLOCK
;
747 return(p
->bufp
[n
% KBLOCK
]);
755 * This function just returns the current size of the kill buffer
760 return(kbufp
? (int) kbufp
->total
: 0);
764 static REGION last_region_added
;
767 set_last_region_added(REGION
*region
)
770 last_region_added
= (*region
);
772 memset(&last_region_added
, 0, sizeof(last_region_added
));
777 get_last_region_added(void)
779 return(&last_region_added
);