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-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.
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 memset((void *)lp
->l_text
, 0, size
*sizeof(CELL
));
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.
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
;
120 if (wp
->w_markp
== lp
) {
121 wp
->w_markp
= lp
->l_fp
;
125 if (wp
->w_imarkp
== lp
) {
126 wp
->w_imarkp
= lp
->l_fp
;
135 if (bp
->b_nwnd
== 0) {
136 if (bp
->b_dotp
== lp
) {
137 bp
->b_dotp
= lp
->l_fp
;
141 if (bp
->b_markp
== lp
) {
142 bp
->b_markp
= lp
->l_fp
;
150 lp
->l_bp
->l_fp
= lp
->l_fp
;
151 lp
->l_fp
->l_bp
= lp
->l_bp
;
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).
168 if (curbp
->b_nwnd
!= 1) /* Ensure hard. */
171 if ((curbp
->b_flag
&BFCHG
) == 0) { /* First change, so */
173 flag
|= WFMODE
; /* update mode lines. */
174 curbp
->b_flag
|= BFCHG
;
179 if (wp
->w_bufp
== curbp
)
187 * insert spaces forward into text
188 * default flag and numeric argument
191 insspace(int f
, int 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
)
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
;
220 if(!geninsert(&(curwp
->w_dotp
), &(curwp
->w_doto
), curbp
->b_linep
,
221 c
, (curwp
->w_markp
) ? 1 : 0, n
, &curbp
->b_linecnt
))
224 wp
= wheadp
; /* Update windows */
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
)
235 if (wp
->w_markp
== dotp
) {
237 if (wp
->w_marko
> doto
)
248 * geninsert - do the actual work of inserting a character into
252 geninsert(LINE
**dotp
, int *doto
, LINE
*linep
, UCS c
, int attb
, int n
, long *lines
)
261 if (*dotp
== linep
) { /* At the end: special */
263 emlwrite("Programmer botch: geninsert", NULL
);
267 if ((lp1
=lalloc(n
)) == NULL
) /* Allocate new line */
270 lp2
= (*dotp
)->l_bp
; /* Previous line */
271 lp2
->l_fp
= lp1
; /* Link in */
277 ac
.c
= (c
& CELLMASK
);
278 cp1
= &(*dotp
)->l_text
[0];
288 if ((*dotp
)->l_used
+n
> (*dotp
)->l_size
) { /* Hard: reallocate */
289 if ((lp1
=lalloc((*dotp
)->l_used
+n
)) == NULL
)
292 cp1
= &(*dotp
)->l_text
[0];
293 cp2
= &lp1
->l_text
[0];
294 while (cp1
!= &(*dotp
)->l_text
[*doto
])
298 while (cp1
!= &(*dotp
)->l_text
[(*dotp
)->l_used
])
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 */
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
));
317 } else { /* Easy: in place */
318 (*dotp
)->l_used
+= n
;
319 cp2
= &(*dotp
)->l_text
[(*dotp
)->l_used
];
321 while (cp1
!= &(*dotp
)->l_text
[*doto
])
325 ac
.c
= (c
& CELLMASK
);
326 while(n
--) /* add the chars */
327 (*dotp
)->l_text
[(*doto
)++] = ac
;
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.
351 if (curbp
->b_mode
&MDVIEW
) /* don't allow this command if */
352 return(rdonly()); /* we are in read only mode */
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 */
360 cp1
= &lp1
->l_text
[0]; /* Shuffle text around */
361 cp2
= &lp2
->l_text
[0];
362 while (cp1
!= &lp1
->l_text
[doto
])
365 cp2
= &lp1
->l_text
[0];
366 while (cp1
!= &lp1
->l_text
[lp1
->l_used
])
370 lp2
->l_bp
= lp1
->l_bp
;
372 lp2
->l_bp
->l_fp
= lp2
;
374 wp
= wheadp
; /* Windows */
376 if (wp
->w_linep
== lp1
)
379 if (wp
->w_dotp
== lp1
) {
380 if (wp
->w_doto
< doto
)
386 if (wp
->w_imarkp
== lp1
) { /* ADDED for internal mark */
387 if (wp
->w_imarko
< doto
)
390 wp
->w_imarko
-= doto
;
393 if (wp
->w_markp
== lp1
) {
394 if (wp
->w_marko
< doto
)
403 * Keep track of the number of lines in the buffer.
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
))
426 if (curbp
->b_mode
&MDVIEW
) /* don't allow this command if */
427 return(rdonly()); /* we are in read only mode */
430 dotp
= curwp
->w_dotp
;
431 doto
= curwp
->w_doto
;
432 if (dotp
== curbp
->b_linep
) /* Hit end of buffer. */
434 chunk
= dotp
->l_used
-doto
; /* Size of chunk. */
437 if (chunk
== 0) { /* End of line, merge. */
439 if (ldelnewline() == FALSE
440 || (preserve
? (*preserve
)('\n') == FALSE
: 0))
447 cp1
= &dotp
->l_text
[doto
]; /* Scrunch text. */
449 if (preserve
) { /* Kill? */
451 if ((*preserve
)(cp1
->c
) == FALSE
)
455 cp1
= &dotp
->l_text
[doto
];
458 while (cp2
!= &dotp
->l_text
[dotp
->l_used
])
461 dotp
->l_used
-= chunk
;
462 wp
= wheadp
; /* Fix windows */
464 if (wp
->w_dotp
==dotp
&& wp
->w_doto
>=doto
) {
466 if (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
)
476 if (wp
->w_imarkp
==dotp
&& wp
->w_imarko
>=doto
) {
477 wp
->w_imarko
-= chunk
;
478 if (wp
->w_imarko
< doto
)
488 if (preserve
== kinsert
&& ksize() > 0)
489 mswin_killbuftoclip (kremove
);
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
514 if (curbp
->b_mode
&MDVIEW
) /* don't allow this command if */
515 return(rdonly()); /* we are in read only mode */
519 if (lp2
== curbp
->b_linep
) { /* At the buffer end. */
520 if (lp1
->l_used
== 0) { /* Blank line. */
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
])
536 if (wp
->w_linep
== lp2
)
538 if (wp
->w_dotp
== lp2
) {
540 wp
->w_doto
+= lp1
->l_used
;
542 if (wp
->w_markp
== lp2
) {
544 wp
->w_marko
+= lp1
->l_used
;
546 if (wp
->w_imarkp
== lp2
) {
548 wp
->w_imarko
+= lp1
->l_used
;
552 lp1
->l_used
+= lp2
->l_used
;
553 lp1
->l_fp
= lp2
->l_fp
;
554 lp2
->l_fp
->l_bp
= lp1
;
560 if ((lp3
=lalloc(lp1
->l_used
+lp2
->l_used
)) == NULL
)
563 cp1
= &lp1
->l_text
[0];
564 cp2
= &lp3
->l_text
[0];
565 while (cp1
!= &lp1
->l_text
[lp1
->l_used
])
568 cp1
= &lp2
->l_text
[0];
569 while (cp1
!= &lp2
->l_text
[lp2
->l_used
])
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
;
578 if (wp
->w_linep
==lp1
|| wp
->w_linep
==lp2
)
580 if (wp
->w_dotp
== lp1
)
582 else if (wp
->w_dotp
== lp2
) {
584 wp
->w_doto
+= lp1
->l_used
;
586 if (wp
->w_markp
== lp1
)
588 else if (wp
->w_markp
== lp2
) {
590 wp
->w_marko
+= lp1
->l_used
;
592 if (wp
->w_imarkp
== lp1
)
594 else if (wp
->w_imarkp
== lp2
) {
596 wp
->w_imarko
+= lp1
->l_used
;
609 * Tell the caller if the given line is blank or not.
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
)
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.
649 pkbufdel(struct pkbuf
**buf
)
652 pkchunkdel(&(*buf
)->first
);
660 pkchunkdel(struct pkchunk
**chunk
)
664 pkchunkdel(&(*chunk
)->next
);
666 free((char *) *chunk
);
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.
681 return(pkbufinsert(c
, &kbufp
));
687 return(pkbufinsert(c
, &fbufp
));
691 pkbufinsert(UCS c
, struct pkbuf
**buf
)
694 if((*buf
= (struct pkbuf
*) malloc(sizeof(struct pkbuf
))) != NULL
)
695 memset(*buf
, 0, sizeof(struct pkbuf
));
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
));
705 p
->next
= (*buf
)->last
;
707 (*buf
)->first
= (*buf
)->last
;
713 (*buf
)->last
->bufp
[(*buf
)->last
->used
++] = c
;
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.
727 return(pkbufremove(n
, kbufp
));
733 return(pkbufremove(n
, fbufp
));
738 * This would be type UCS except that it needs to return -1.
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
;
751 return(p
->bufp
[n
% KBLOCK
]);
759 * This function just returns the current size of the kill buffer
764 return(kbufp
? (int) kbufp
->total
: 0);
768 static REGION last_region_added
;
771 set_last_region_added(REGION
*region
)
774 last_region_added
= (*region
);
776 memset(&last_region_added
, 0, sizeof(last_region_added
));
781 get_last_region_added(void)
783 return(&last_region_added
);