Fix for a crash which happened when a document couldn't be opened.
[AROS-Contrib.git] / fish / microemacs / line.c
blob69ed81513e35a5e2c76e4cadf265232eacc45f47
1 /*
2 * The functions in this file are a general set of line management utilities.
3 * They are the only routines that touch the text. They also touch the buffer
4 * and window structures, to make sure that the necessary updating gets done.
5 * There are routines in this file that handle the kill buffer too. It isn't
6 * here for any good reason.
8 * Note that this code only updates the dot and mark values in the window list.
9 * Since all the code acts on the current window, the buffer that we are
10 * editing must be being displayed, which means that "b_nwnd" is non zero,
11 * which means that the dot and mark values in the buffer headers are nonsense.
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include "ed.h"
18 #define NBLOCK 16 /* Line block chunk size */
19 #define KBLOCK 256 /* Kill buffer block size */
21 char *kbufp = NULL; /* Kill buffer data */
22 int kused = 0; /* # of bytes used in KB */
23 int ksize = 0; /* # of bytes allocated in KB */
26 * This routine allocates a block of memory large enough to hold a LINE
27 * containing "used" characters. The block is always rounded up a bit. Return
28 * a pointer to the new block, or NULL if there isn't any memory left. Print a
29 * message in the message line if no space.
31 LINE *
32 lalloc(used)
33 register int used;
35 register LINE *lp;
36 register int size;
38 size = (used+NBLOCK-1) & ~(NBLOCK-1);
39 if (size == 0) /* Assume that an empty */
40 size = NBLOCK; /* line is for type-in. */
41 if ((lp = (LINE *) malloc(sizeof(LINE)+size)) == NULL) {
42 mlwrite("Cannot allocate %d bytes", size);
43 return (NULL);
45 lp->l_size = size;
46 lp->l_used = used;
47 return (lp);
51 * Delete line "lp". Fix all of the links that might point at it (they are
52 * moved to offset 0 of the next line. Unlink the line from whatever buffer it
53 * might be in. Release the memory. The buffers are updated too; the magic
54 * conditions described in the above comments don't hold here.
56 void lfree(lp)
57 register LINE *lp;
59 register BUFFER *bp;
60 register WINDOW *wp;
62 wp = wheadp;
63 while (wp != NULL) {
64 if (wp->w_linep == lp)
65 wp->w_linep = lp->l_fp;
66 if (wp->w_dotp == lp) {
67 wp->w_dotp = lp->l_fp;
68 wp->w_doto = 0;
70 if (wp->w_markp == lp) {
71 wp->w_markp = lp->l_fp;
72 wp->w_marko = 0;
74 wp = wp->w_wndp;
76 bp = bheadp;
77 while (bp != NULL) {
78 if (bp->b_nwnd == 0) {
79 if (bp->b_dotp == lp) {
80 bp->b_dotp = lp->l_fp;
81 bp->b_doto = 0;
83 if (bp->b_markp == lp) {
84 bp->b_markp = lp->l_fp;
85 bp->b_marko = 0;
88 bp = bp->b_bufp;
90 lp->l_bp->l_fp = lp->l_fp;
91 lp->l_fp->l_bp = lp->l_bp;
92 free((char *) lp);
96 * This routine gets called when a character is changed in place in the current
97 * buffer. It updates all of the required flags in the buffer and window
98 * system. The flag used is passed as an argument; if the buffer is being
99 * displayed in more than 1 window we change EDIT t HARD. Set MODE if the
100 * mode line needs to be updated (the "*" has to be set).
102 void lchange(flag)
103 register int flag;
105 register WINDOW *wp;
107 if (curbp->b_nwnd != 1) /* Ensure hard. */
108 flag = WFHARD;
109 if ((curbp->b_flag&BFCHG) == 0) { /* First change, so */
110 flag |= WFMODE; /* update mode lines. */
111 curbp->b_flag |= BFCHG;
113 wp = wheadp;
114 while (wp != NULL) {
115 if (wp->w_bufp == curbp)
116 wp->w_flag |= flag;
117 wp = wp->w_wndp;
122 * Insert "n" copies of the character "c" at the current location of dot. In
123 * the easy case all that happens is the text is stored in the line. In the
124 * hard case, the line has to be reallocated. When the window list is updated,
125 * take special care; I screwed it up once. You always update dot in the
126 * current window. You update mark, and a dot in another window, if it is
127 * greater than the place where you did the insert. Return TRUE if all is
128 * well, and FALSE on errors.
130 int linsert(n, c)
131 int n;
132 int c;
134 register char *cp1;
135 register char *cp2;
136 register LINE *lp1;
137 register LINE *lp2;
138 register LINE *lp3;
139 register int doto;
140 register int i;
141 register WINDOW *wp;
143 lchange(WFEDIT);
144 lp1 = curwp->w_dotp; /* Current line */
145 if (lp1 == curbp->b_linep) { /* At the end: special */
146 if (curwp->w_doto != 0) {
147 mlwrite("bug: linsert");
148 return (FALSE);
150 if ((lp2=lalloc(n)) == NULL) /* Allocate new line */
151 return (FALSE);
152 lp3 = lp1->l_bp; /* Previous line */
153 lp3->l_fp = lp2; /* Link in */
154 lp2->l_fp = lp1;
155 lp1->l_bp = lp2;
156 lp2->l_bp = lp3;
157 for (i=0; i<n; ++i)
158 lp2->l_text[i] = c;
159 curwp->w_dotp = lp2;
160 curwp->w_doto = n;
161 return (TRUE);
163 doto = curwp->w_doto; /* Save for later. */
164 if (lp1->l_used+n > lp1->l_size) { /* Hard: reallocate */
165 if ((lp2=lalloc(lp1->l_used+n)) == NULL)
166 return (FALSE);
167 cp1 = &lp1->l_text[0];
168 cp2 = &lp2->l_text[0];
169 while (cp1 != &lp1->l_text[doto])
170 *cp2++ = *cp1++;
171 cp2 += n;
172 while (cp1 != &lp1->l_text[lp1->l_used])
173 *cp2++ = *cp1++;
174 lp1->l_bp->l_fp = lp2;
175 lp2->l_fp = lp1->l_fp;
176 lp1->l_fp->l_bp = lp2;
177 lp2->l_bp = lp1->l_bp;
178 free((char *) lp1);
179 } else { /* Easy: in place */
180 lp2 = lp1; /* Pretend new line */
181 lp2->l_used += n;
182 cp2 = &lp1->l_text[lp1->l_used];
183 cp1 = cp2-n;
184 while (cp1 != &lp1->l_text[doto])
185 *--cp2 = *--cp1;
187 for (i=0; i<n; ++i) /* Add the characters */
188 lp2->l_text[doto+i] = c;
189 wp = wheadp; /* Update windows */
190 while (wp != NULL) {
191 if (wp->w_linep == lp1)
192 wp->w_linep = lp2;
193 if (wp->w_dotp == lp1) {
194 wp->w_dotp = lp2;
195 if (wp==curwp || wp->w_doto>doto)
196 wp->w_doto += n;
198 if (wp->w_markp == lp1) {
199 wp->w_markp = lp2;
200 if (wp->w_marko > doto)
201 wp->w_marko += n;
203 wp = wp->w_wndp;
205 return (TRUE);
209 * Insert a newline into the buffer at the current location of dot in the
210 * current window. The funny ass-backwards way it does things is not a botch;
211 * it just makes the last line in the file not a special case. Return TRUE if
212 * everything works out and FALSE on error (memory allocation failure). The
213 * update of dot and mark is a bit easier then in the above case, because the
214 * split forces more updating.
216 int lnewline()
218 register char *cp1;
219 register char *cp2;
220 register LINE *lp1;
221 register LINE *lp2;
222 register int doto;
223 register WINDOW *wp;
225 lchange(WFHARD);
226 lp1 = curwp->w_dotp; /* Get the address and */
227 doto = curwp->w_doto; /* offset of "." */
228 if ((lp2=lalloc(doto)) == NULL) /* New first half line */
229 return (FALSE);
230 cp1 = &lp1->l_text[0]; /* Shuffle text around */
231 cp2 = &lp2->l_text[0];
232 while (cp1 != &lp1->l_text[doto])
233 *cp2++ = *cp1++;
234 cp2 = &lp1->l_text[0];
235 while (cp1 != &lp1->l_text[lp1->l_used])
236 *cp2++ = *cp1++;
237 lp1->l_used -= doto;
238 lp2->l_bp = lp1->l_bp;
239 lp1->l_bp = lp2;
240 lp2->l_bp->l_fp = lp2;
241 lp2->l_fp = lp1;
242 wp = wheadp; /* Windows */
243 while (wp != NULL) {
244 if (wp->w_linep == lp1)
245 wp->w_linep = lp2;
246 if (wp->w_dotp == lp1) {
247 if (wp->w_doto < doto)
248 wp->w_dotp = lp2;
249 else
250 wp->w_doto -= doto;
252 if (wp->w_markp == lp1) {
253 if (wp->w_marko < doto)
254 wp->w_markp = lp2;
255 else
256 wp->w_marko -= doto;
258 wp = wp->w_wndp;
260 return (TRUE);
264 * This function deletes "n" bytes, starting at dot. It understands how do deal
265 * with end of lines, etc. It returns TRUE if all of the characters were
266 * deleted, and FALSE if they were not (because dot ran into the end of the
267 * buffer. The "kflag" is TRUE if the text should be put in the kill buffer.
269 int ldelete(n,kflag)
270 int n;
271 int kflag;
273 register char *cp1;
274 register char *cp2;
275 register LINE *dotp;
276 register int doto;
277 register int chunk;
278 register WINDOW *wp;
280 while (n != 0) {
281 dotp = curwp->w_dotp;
282 doto = curwp->w_doto;
283 if (dotp == curbp->b_linep) /* Hit end of buffer. */
284 return (FALSE);
285 chunk = dotp->l_used-doto; /* Size of chunk. */
286 if (chunk > n)
287 chunk = n;
288 if (chunk == 0) { /* End of line, merge. */
289 lchange(WFHARD);
290 if (ldelnewline() == FALSE
291 || (kflag!=FALSE && kinsert('\n')==FALSE))
292 return (FALSE);
293 --n;
294 continue;
296 lchange(WFEDIT);
297 cp1 = &dotp->l_text[doto]; /* Scrunch text. */
298 cp2 = cp1 + chunk;
299 if (kflag != FALSE) { /* Kill? */
300 while (cp1 != cp2) {
301 if (kinsert(*cp1) == FALSE)
302 return (FALSE);
303 ++cp1;
305 cp1 = &dotp->l_text[doto];
307 while (cp2 != &dotp->l_text[dotp->l_used])
308 *cp1++ = *cp2++;
309 dotp->l_used -= chunk;
310 wp = wheadp; /* Fix windows */
311 while (wp != NULL) {
312 if (wp->w_dotp==dotp && wp->w_doto>=doto) {
313 wp->w_doto -= chunk;
314 if (wp->w_doto < doto)
315 wp->w_doto = doto;
317 if (wp->w_markp==dotp && wp->w_marko>=doto) {
318 wp->w_marko -= chunk;
319 if (wp->w_marko < doto)
320 wp->w_marko = doto;
322 wp = wp->w_wndp;
324 n -= chunk;
326 return (TRUE);
330 * Delete a newline. Join the current line with the next line. If the next line
331 * is the magic header line always return TRUE; merging the last line with the
332 * header line can be thought of as always being a successful operation, even
333 * if nothing is done, and this makes the kill buffer work "right". Easy cases
334 * can be done by shuffling data around. Hard cases require that lines be moved
335 * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
336 * "ldelete" only.
338 int ldelnewline()
340 register char *cp1;
341 register char *cp2;
342 register LINE *lp1;
343 register LINE *lp2;
344 register LINE *lp3;
345 register WINDOW *wp;
347 lp1 = curwp->w_dotp;
348 lp2 = lp1->l_fp;
349 if (lp2 == curbp->b_linep) { /* At the buffer end. */
350 if (lp1->l_used == 0) /* Blank line. */
351 lfree(lp1);
352 return (TRUE);
354 if (lp2->l_used <= lp1->l_size-lp1->l_used) {
355 cp1 = &lp1->l_text[lp1->l_used];
356 cp2 = &lp2->l_text[0];
357 while (cp2 != &lp2->l_text[lp2->l_used])
358 *cp1++ = *cp2++;
359 wp = wheadp;
360 while (wp != NULL) {
361 if (wp->w_linep == lp2)
362 wp->w_linep = lp1;
363 if (wp->w_dotp == lp2) {
364 wp->w_dotp = lp1;
365 wp->w_doto += lp1->l_used;
367 if (wp->w_markp == lp2) {
368 wp->w_markp = lp1;
369 wp->w_marko += lp1->l_used;
371 wp = wp->w_wndp;
373 lp1->l_used += lp2->l_used;
374 lp1->l_fp = lp2->l_fp;
375 lp2->l_fp->l_bp = lp1;
376 free((char *) lp2);
377 return (TRUE);
379 if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
380 return (FALSE);
381 cp1 = &lp1->l_text[0];
382 cp2 = &lp3->l_text[0];
383 while (cp1 != &lp1->l_text[lp1->l_used])
384 *cp2++ = *cp1++;
385 cp1 = &lp2->l_text[0];
386 while (cp1 != &lp2->l_text[lp2->l_used])
387 *cp2++ = *cp1++;
388 lp1->l_bp->l_fp = lp3;
389 lp3->l_fp = lp2->l_fp;
390 lp2->l_fp->l_bp = lp3;
391 lp3->l_bp = lp1->l_bp;
392 wp = wheadp;
393 while (wp != NULL) {
394 if (wp->w_linep==lp1 || wp->w_linep==lp2)
395 wp->w_linep = lp3;
396 if (wp->w_dotp == lp1)
397 wp->w_dotp = lp3;
398 else if (wp->w_dotp == lp2) {
399 wp->w_dotp = lp3;
400 wp->w_doto += lp1->l_used;
402 if (wp->w_markp == lp1)
403 wp->w_markp = lp3;
404 else if (wp->w_markp == lp2) {
405 wp->w_markp = lp3;
406 wp->w_marko += lp1->l_used;
408 wp = wp->w_wndp;
410 free((char *) lp1);
411 free((char *) lp2);
412 return (TRUE);
416 * Delete all of the text saved in the kill buffer. Called by commands when a
417 * new kill context is being created. The kill buffer array is released, just
418 * in case the buffer has grown to immense size. No errors.
420 void kdelete()
422 if (kbufp != NULL) {
423 free((char *) kbufp);
424 kbufp = NULL;
425 kused = 0;
426 ksize = 0;
431 * Insert a character to the kill buffer, enlarging the buffer if there isn't
432 * any room. Always grow the buffer in chunks, on the assumption that if you
433 * put something in the kill buffer you are going to put more stuff there too
434 * later. Return TRUE if all is well, and FALSE on errors.
436 int kinsert(c)
437 int c;
439 register char *nbufp;
440 register int i;
442 if (kused == ksize) {
443 if ((nbufp=(char *)malloc(ksize+KBLOCK)) == NULL)
444 return (FALSE);
445 for (i=0; i<ksize; ++i)
446 nbufp[i] = kbufp[i];
447 if (kbufp != NULL)
448 free((char *) kbufp);
449 kbufp = nbufp;
450 ksize += KBLOCK;
452 kbufp[kused++] = c;
453 return (TRUE);
457 * This function gets characters from the kill buffer. If the character index
458 * "n" is off the end, it returns "-1". This lets the caller just scan along
459 * until it gets a "-1" back.
461 int kremove(n)
462 int n;
464 if (n >= kused)
465 return (-1);
466 else
467 return (kbufp[n] & 0xFF);