1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: line.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
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.
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
44 short used
; /* # of characters used in this buffer */
45 UCS bufp
[KBLOCK
]; /* buffer containing text */
46 struct pkchunk
*next
; /* pointer to next chunk */
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 */
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.
76 static int displayed
= 0;
78 if((size
= (used
+NBLOCK
-1) & ~(NBLOCK
-1)) > NLINE
)
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
);
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 */
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.
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
;
121 if (wp
->w_markp
== lp
) {
122 wp
->w_markp
= lp
->l_fp
;
126 if (wp
->w_imarkp
== lp
) {
127 wp
->w_imarkp
= lp
->l_fp
;
136 if (bp
->b_nwnd
== 0) {
137 if (bp
->b_dotp
== lp
) {
138 bp
->b_dotp
= lp
->l_fp
;
142 if (bp
->b_markp
== lp
) {
143 bp
->b_markp
= lp
->l_fp
;
151 lp
->l_bp
->l_fp
= lp
->l_fp
;
152 lp
->l_fp
->l_bp
= lp
->l_bp
;
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).
169 if (curbp
->b_nwnd
!= 1) /* Ensure hard. */
172 if ((curbp
->b_flag
&BFCHG
) == 0) { /* First change, so */
174 flag
|= WFMODE
; /* update mode lines. */
175 curbp
->b_flag
|= BFCHG
;
180 if (wp
->w_bufp
== curbp
)
188 * insert spaces forward into text
189 * default flag and numeric argument
192 insspace(int f
, int 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
)
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
;
221 if(!geninsert(&(curwp
->w_dotp
), &(curwp
->w_doto
), curbp
->b_linep
,
222 c
, (curwp
->w_markp
) ? 1 : 0, n
, &curbp
->b_linecnt
))
225 wp
= wheadp
; /* Update windows */
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
)
236 if (wp
->w_markp
== dotp
) {
238 if (wp
->w_marko
> doto
)
249 * geninsert - do the actual work of inserting a character into
253 geninsert(LINE
**dotp
, int *doto
, LINE
*linep
, UCS c
, int attb
, int n
, long *lines
)
262 if (*dotp
== linep
) { /* At the end: special */
264 emlwrite("Programmer botch: geninsert", NULL
);
268 if ((lp1
=lalloc(n
)) == NULL
) /* Allocate new line */
271 lp2
= (*dotp
)->l_bp
; /* Previous line */
272 lp2
->l_fp
= lp1
; /* Link in */
278 ac
.c
= (c
& CELLMASK
);
279 cp1
= &(*dotp
)->l_text
[0];
289 if ((*dotp
)->l_used
+n
> (*dotp
)->l_size
) { /* Hard: reallocate */
290 if ((lp1
=lalloc((*dotp
)->l_used
+n
)) == NULL
)
293 cp1
= &(*dotp
)->l_text
[0];
294 cp2
= &lp1
->l_text
[0];
295 while (cp1
!= &(*dotp
)->l_text
[*doto
])
299 while (cp1
!= &(*dotp
)->l_text
[(*dotp
)->l_used
])
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 */
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
));
318 } else { /* Easy: in place */
319 (*dotp
)->l_used
+= n
;
320 cp2
= &(*dotp
)->l_text
[(*dotp
)->l_used
];
322 while (cp1
!= &(*dotp
)->l_text
[*doto
])
326 ac
.c
= (c
& CELLMASK
);
327 while(n
--) /* add the chars */
328 (*dotp
)->l_text
[(*doto
)++] = ac
;
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.
352 if (curbp
->b_mode
&MDVIEW
) /* don't allow this command if */
353 return(rdonly()); /* we are in read only mode */
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 */
361 cp1
= &lp1
->l_text
[0]; /* Shuffle text around */
362 cp2
= &lp2
->l_text
[0];
363 while (cp1
!= &lp1
->l_text
[doto
])
366 cp2
= &lp1
->l_text
[0];
367 while (cp1
!= &lp1
->l_text
[lp1
->l_used
])
371 lp2
->l_bp
= lp1
->l_bp
;
373 lp2
->l_bp
->l_fp
= lp2
;
375 wp
= wheadp
; /* Windows */
377 if (wp
->w_linep
== lp1
)
380 if (wp
->w_dotp
== lp1
) {
381 if (wp
->w_doto
< doto
)
387 if (wp
->w_imarkp
== lp1
) { /* ADDED for internal mark */
388 if (wp
->w_imarko
< doto
)
391 wp
->w_imarko
-= doto
;
394 if (wp
->w_markp
== lp1
) {
395 if (wp
->w_marko
< doto
)
404 * Keep track of the number of lines in the buffer.
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
))
427 if (curbp
->b_mode
&MDVIEW
) /* don't allow this command if */
428 return(rdonly()); /* we are in read only mode */
431 dotp
= curwp
->w_dotp
;
432 doto
= curwp
->w_doto
;
433 if (dotp
== curbp
->b_linep
) /* Hit end of buffer. */
435 chunk
= dotp
->l_used
-doto
; /* Size of chunk. */
438 if (chunk
== 0) { /* End of line, merge. */
440 if (ldelnewline() == FALSE
441 || (preserve
? (*preserve
)('\n') == FALSE
: 0))
448 cp1
= &dotp
->l_text
[doto
]; /* Scrunch text. */
450 if (preserve
) { /* Kill? */
452 if ((*preserve
)(cp1
->c
) == FALSE
)
456 cp1
= &dotp
->l_text
[doto
];
459 while (cp2
!= &dotp
->l_text
[dotp
->l_used
])
462 dotp
->l_used
-= chunk
;
463 wp
= wheadp
; /* Fix windows */
465 if (wp
->w_dotp
==dotp
&& wp
->w_doto
>=doto
) {
467 if (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
)
477 if (wp
->w_imarkp
==dotp
&& wp
->w_imarko
>=doto
) {
478 wp
->w_imarko
-= chunk
;
479 if (wp
->w_imarko
< doto
)
489 if (preserve
== kinsert
&& ksize() > 0)
490 mswin_killbuftoclip (kremove
);
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
515 if (curbp
->b_mode
&MDVIEW
) /* don't allow this command if */
516 return(rdonly()); /* we are in read only mode */
520 if (lp2
== curbp
->b_linep
) { /* At the buffer end. */
521 if (lp1
->l_used
== 0) { /* Blank line. */
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
])
537 if (wp
->w_linep
== lp2
)
539 if (wp
->w_dotp
== lp2
) {
541 wp
->w_doto
+= lp1
->l_used
;
543 if (wp
->w_markp
== lp2
) {
545 wp
->w_marko
+= lp1
->l_used
;
547 if (wp
->w_imarkp
== lp2
) {
549 wp
->w_imarko
+= lp1
->l_used
;
553 lp1
->l_used
+= lp2
->l_used
;
554 lp1
->l_fp
= lp2
->l_fp
;
555 lp2
->l_fp
->l_bp
= lp1
;
561 if ((lp3
=lalloc(lp1
->l_used
+lp2
->l_used
)) == NULL
)
564 cp1
= &lp1
->l_text
[0];
565 cp2
= &lp3
->l_text
[0];
566 while (cp1
!= &lp1
->l_text
[lp1
->l_used
])
569 cp1
= &lp2
->l_text
[0];
570 while (cp1
!= &lp2
->l_text
[lp2
->l_used
])
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
;
579 if (wp
->w_linep
==lp1
|| wp
->w_linep
==lp2
)
581 if (wp
->w_dotp
== lp1
)
583 else if (wp
->w_dotp
== lp2
) {
585 wp
->w_doto
+= lp1
->l_used
;
587 if (wp
->w_markp
== lp1
)
589 else if (wp
->w_markp
== lp2
) {
591 wp
->w_marko
+= lp1
->l_used
;
593 if (wp
->w_imarkp
== lp1
)
595 else if (wp
->w_imarkp
== lp2
) {
597 wp
->w_imarko
+= lp1
->l_used
;
610 * Tell the caller if the given line is blank or not.
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
)
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.
650 pkbufdel(struct pkbuf
**buf
)
653 pkchunkdel(&(*buf
)->first
);
661 pkchunkdel(struct pkchunk
**chunk
)
665 pkchunkdel(&(*chunk
)->next
);
667 free((char *) *chunk
);
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.
682 return(pkbufinsert(c
, &kbufp
));
688 return(pkbufinsert(c
, &fbufp
));
692 pkbufinsert(UCS c
, struct pkbuf
**buf
)
695 if((*buf
= (struct pkbuf
*) malloc(sizeof(struct pkbuf
))) != NULL
)
696 memset(*buf
, 0, sizeof(struct pkbuf
));
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
));
706 p
->next
= (*buf
)->last
;
708 (*buf
)->first
= (*buf
)->last
;
714 (*buf
)->last
->bufp
[(*buf
)->last
->used
++] = c
;
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.
728 return(pkbufremove(n
, kbufp
));
734 return(pkbufremove(n
, fbufp
));
739 * This would be type UCS except that it needs to return -1.
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
;
752 return(p
->bufp
[n
% KBLOCK
]);
760 * This function just returns the current size of the kill buffer
765 return(kbufp
? (int) kbufp
->total
: 0);
769 static REGION last_region_added
;
772 set_last_region_added(REGION
*region
)
775 last_region_added
= (*region
);
777 memset(&last_region_added
, 0, sizeof(last_region_added
));
782 get_last_region_added(void)
784 return(&last_region_added
);