1 /*******************************************************************************
2 *                                                                              *
3 * textBuf.c - Manage source text for one or more text areas                    *
4 *                                                                              *
5 * Copyright (C) 1999 Mark Edel						       *
6 *									       *
7 * This is free software; you can redistribute it and/or modify it under the    *
8 * terms of the GNU General Public License as published by the Free Software    *
9 * Foundation; either version 2 of the License, or (at your option) any later   *
10 * version. In addition, you may distribute version of this program linked to   *
11 * Motif or Open Motif. See README for details.                                 *
12 * 									       *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or        *
15 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License        *
16 * for more details.							       *
17 * 									       *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple     *
20 * Place, Suite 330, Boston, MA  02111-1307 USA		                       *
21 *									       *
22 * Nirvana Text Editor	    						       *
23 * June 15, 1995                                                                *
24 *                                                                              *
25 * Written by Mark Edel                                                         *
26 *                                                                              *
27 *******************************************************************************/
28 
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
32 
33 #include "textBuf.h"
34 #include "rangeset.h"
35 #include "../util/nedit_malloc.h"
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <ctype.h>
41 
42 #ifdef HAVE_DEBUG_H
43 #include "../debug.h"
44 #endif
45 
46 #define PREFERRED_GAP_SIZE 80	/* Initial size for the buffer gap (empty space
47                                    in the buffer where text might be inserted
48                                    if the user is typing sequential chars) */
49 
50 static void histogramCharacters(const char *string, int length, char hist[256],
51 	int init);
52 static void subsChars(char *string, int length, char fromChar, char toChar);
53 static char chooseNullSubsChar(char hist[256]);
54 static int insert(textBuffer *buf, int pos, const char *text);
55 static void delete(textBuffer *buf, int start, int end);
56 static void deleteRect(textBuffer *buf, int start, int end, int rectStart,
57 	int rectEnd, int *replaceLen, int *endPos);
58 static void insertCol(textBuffer *buf, int column, int startPos, const char *insText,
59 	int *nDeleted, int *nInserted, int *endPos);
60 static void overlayRect(textBuffer *buf, int startPos, int rectStart,
61     	int rectEnd, const char *insText, int *nDeleted, int *nInserted, int *endPos);
62 static void insertColInLine(const char *line, const char *insLine, int column, int insWidth,
63 	int tabDist, int useTabs, char nullSubsChar, char *outStr, int *outLen,
64 	int *endOffset);
65 static void deleteRectFromLine(const char *line, int rectStart, int rectEnd,
66 	int tabDist, int useTabs, char nullSubsChar, char *outStr, int *outLen,
67 	int *endOffset);
68 static void overlayRectInLine(const char *line, const char *insLine, int rectStart,
69     	int rectEnd, int tabDist, int useTabs, char nullSubsChar, char *outStr,
70     	int *outLen, int *endOffset);
71 static void callPreDeleteCBs(textBuffer *buf, int pos, int nDeleted);
72 static void callModifyCBs(textBuffer *buf, int pos, int nDeleted,
73 	int nInserted, int nRestyled, const char *deletedText);
74 static void redisplaySelection(textBuffer *buf, selection *oldSelection,
75 	selection *newSelection);
76 static void moveGap(textBuffer *buf, int pos);
77 static void reallocateBuf(textBuffer *buf, int newGapStart, int newGapLen);
78 static void setSelection(selection *sel, int start, int end);
79 static void setRectSelect(selection *sel, int start, int end,
80 	int rectStart, int rectEnd);
81 static void updateSelections(textBuffer *buf, int pos, int nDeleted,
82 	int nInserted);
83 static void updateSelection(selection *sel, int pos, int nDeleted,
84 	int nInserted);
85 static int getSelectionPos(selection *sel, int *start, int *end,
86         int *isRect, int *rectStart, int *rectEnd);
87 static char *getSelectionText(textBuffer *buf, selection *sel);
88 static void removeSelected(textBuffer *buf, selection *sel);
89 static void replaceSelected(textBuffer *buf, selection *sel, const char *text);
90 static void addPadding(char *string, int startIndent, int toIndent,
91 	int tabDist, int useTabs, char nullSubsChar, int *charsAdded);
92 static int searchForward(textBuffer *buf, int startPos, char searchChar,
93 	int *foundPos);
94 static int searchBackward(textBuffer *buf, int startPos, char searchChar,
95 	int *foundPos);
96 static char *copyLine(const char *text, int *lineLen);
97 static int countLines(const char *string);
98 static int textWidth(const char *text, int tabDist, char nullSubsChar);
99 static void findRectSelBoundariesForCopy(textBuffer *buf, int lineStartPos,
100 	int rectStart, int rectEnd, int *selStart, int *selEnd);
101 static char *realignTabs(const char *text, int origIndent, int newIndent,
102 	int tabDist, int useTabs, char nullSubsChar, int *newLength);
103 static char *expandTabs(const char *text, int startIndent, int tabDist,
104 	char nullSubsChar, int *newLen);
105 static char *unexpandTabs(const char *text, int startIndent, int tabDist,
106 	char nullSubsChar, int *newLen);
107 static int max(int i1, int i2);
108 static int min(int i1, int i2);
109 
110 #ifdef __MVS__
111 static const char *ControlCodeTable[64] = {
112      "nul", "soh", "stx", "etx", "sel", "ht", "rnl", "del",
113      "ge", "sps", "rpt", "vt", "ff", "cr", "so", "si",
114      "dle", "dc1", "dc2", "dc3", "res", "nl", "bs", "poc",
115      "can", "em", "ubs", "cu1", "ifs", "igs", "irs", "ius",
116      "ds", "sos", "fs", "wus", "byp", "lf", "etb", "esc",
117      "sa", "sfe", "sm", "csp", "mfa", "enq", "ack", "bel",
118      "x30", "x31", "syn", "ir", "pp", "trn", "nbs", "eot",
119      "sbs", "it", "rff", "cu3", "dc4", "nak", "x3e", "sub"};
120 #else
121 static const char *ControlCodeTable[32] = {
122      "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
123      "bs", "ht", "nl", "vt", "np", "cr", "so", "si",
124      "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
125      "can", "em", "sub", "esc", "fs", "gs", "rs", "us"};
126 #endif
127 
128 /*
129 ** Create an empty text buffer
130 */
BufCreate(void)131 textBuffer *BufCreate(void)
132 {
133     textBuffer *buf = BufCreatePreallocated(0);
134     return buf;
135 }
136 
137 /*
138 ** Create an empty text buffer of a pre-determined size (use this to
139 ** avoid unnecessary re-allocation if you know exactly how much the buffer
140 ** will need to hold
141 */
BufCreatePreallocated(int requestedSize)142 textBuffer *BufCreatePreallocated(int requestedSize)
143 {
144     textBuffer *buf;
145 
146     buf = (textBuffer *)NEditMalloc(sizeof(textBuffer));
147     buf->length = 0;
148     buf->buf = (char*) NEditMalloc(requestedSize + PREFERRED_GAP_SIZE + 1);
149     buf->buf[requestedSize + PREFERRED_GAP_SIZE] = '\0';
150     buf->gapStart = 0;
151     buf->gapEnd = PREFERRED_GAP_SIZE;
152     buf->tabDist = 8;
153     buf->useTabs = True;
154     buf->primary.selected = False;
155     buf->primary.zeroWidth = False;
156     buf->primary.rectangular = False;
157     buf->primary.start = buf->primary.end = 0;
158     buf->secondary.selected = False;
159     buf->secondary.zeroWidth = False;
160     buf->secondary.start = buf->secondary.end = 0;
161     buf->secondary.rectangular = False;
162     buf->highlight.selected = False;
163     buf->highlight.zeroWidth = False;
164     buf->highlight.start = buf->highlight.end = 0;
165     buf->highlight.rectangular = False;
166     buf->modifyProcs = NULL;
167     buf->cbArgs = NULL;
168     buf->nModifyProcs = 0;
169     buf->preDeleteProcs = NULL;
170     buf->preDeleteCbArgs = NULL;
171     buf->nPreDeleteProcs = 0;
172     buf->nullSubsChar = '\0';
173 #ifdef PURIFY
174     {int i; for (i=buf->gapStart; i<buf->gapEnd; i++) buf->buf[i] = '.';}
175 #endif
176     buf->rangesetTable = NULL;
177     return buf;
178 }
179 
180 /*
181 ** Free a text buffer
182 */
BufFree(textBuffer * buf)183 void BufFree(textBuffer *buf)
184 {
185     NEditFree(buf->buf);
186     if (buf->nModifyProcs != 0) {
187     	NEditFree(buf->modifyProcs);
188     	NEditFree(buf->cbArgs);
189     }
190     if (buf->rangesetTable)
191 	RangesetTableFree(buf->rangesetTable);
192     if (buf->nPreDeleteProcs != 0) {
193     	NEditFree(buf->preDeleteProcs);
194     	NEditFree(buf->preDeleteCbArgs);
195     }
196     NEditFree(buf);
197 }
198 
199 /*
200 ** Get the entire contents of a text buffer.  Memory is allocated to contain
201 ** the returned string, which the caller must free with NEditFree.
202 */
BufGetAll(textBuffer * buf)203 char *BufGetAll(textBuffer *buf)
204 {
205     char *text;
206 
207     text = (char*)NEditMalloc(buf->length+1);
208     memcpy(text, buf->buf, buf->gapStart);
209     memcpy(&text[buf->gapStart], &buf->buf[buf->gapEnd],
210             buf->length - buf->gapStart);
211     text[buf->length] = '\0';
212     return text;
213 }
214 
215 /*
216 ** Get the entire contents of a text buffer as a single string.  The gap is
217 ** moved so that the buffer data can be accessed as a single contiguous
218 ** character array.
219 ** NB DO NOT ALTER THE TEXT THROUGH THE RETURNED POINTER!
220 ** (we make an exception in BufSubstituteNullChars() however)
221 ** This function is intended ONLY to provide a searchable string without copying
222 ** into a temporary buffer.
223 */
BufAsString(textBuffer * buf)224 const char *BufAsString(textBuffer *buf)
225 {
226     char *text;
227     int bufLen = buf->length;
228     int leftLen = buf->gapStart;
229     int rightLen = bufLen - leftLen;
230 
231     /* find where best to put the gap to minimise memory movement */
232     if (leftLen != 0 && rightLen != 0) {
233         leftLen = (leftLen < rightLen) ? 0 : bufLen;
234         moveGap(buf, leftLen);
235     }
236     /* get the start position of the actual data */
237     text = &buf->buf[(leftLen == 0) ? buf->gapEnd : 0];
238     /* make sure it's null-terminated */
239     text[bufLen] = 0;
240 
241     return text;
242 }
243 
244 /*
245 ** Replace the entire contents of the text buffer
246 */
BufSetAll(textBuffer * buf,const char * text)247 void BufSetAll(textBuffer *buf, const char *text)
248 {
249     int length, deletedLength;
250     char *deletedText;
251     length = strlen(text);
252 
253     callPreDeleteCBs(buf, 0, buf->length);
254 
255     /* Save information for redisplay, and get rid of the old buffer */
256     deletedText = BufGetAll(buf);
257     deletedLength = buf->length;
258     NEditFree(buf->buf);
259 
260     /* Start a new buffer with a gap of PREFERRED_GAP_SIZE in the center */
261     buf->buf = (char*)NEditMalloc(length + PREFERRED_GAP_SIZE + 1);
262     buf->buf[length + PREFERRED_GAP_SIZE] = '\0';
263     buf->length = length;
264     buf->gapStart = length/2;
265     buf->gapEnd = buf->gapStart + PREFERRED_GAP_SIZE;
266     memcpy(buf->buf, text, buf->gapStart);
267     memcpy(&buf->buf[buf->gapEnd], &text[buf->gapStart], length-buf->gapStart);
268 #ifdef PURIFY
269     {int i; for (i=buf->gapStart; i<buf->gapEnd; i++) buf->buf[i] = '.';}
270 #endif
271 
272     /* Zero all of the existing selections */
273     updateSelections(buf, 0, deletedLength, 0);
274 
275     /* Call the saved display routine(s) to update the screen */
276     callModifyCBs(buf, 0, deletedLength, length, 0, deletedText);
277     NEditFree(deletedText);
278 }
279 
280 /*
281 ** Return a copy of the text between "start" and "end" character positions
282 ** from text buffer "buf".  Positions start at 0, and the range does not
283 ** include the character pointed to by "end"
284 */
BufGetRange(const textBuffer * buf,int start,int end)285 char* BufGetRange(const textBuffer* buf, int start, int end)
286 {
287     char *text;
288     int length, part1Length;
289 
290     /* Make sure start and end are ok, and allocate memory for returned string.
291        If start is bad, return "", if end is bad, adjust it. */
292     if (start < 0 || start > buf->length) {
293     	text = (char*)NEditMalloc(1);
294 	text[0] = '\0';
295         return text;
296     }
297     if (end < start) {
298     	int temp = start;
299     	start = end;
300     	end = temp;
301     }
302     if (end > buf->length)
303         end = buf->length;
304     length = end - start;
305     text = (char*)NEditMalloc(length+1);
306 
307     /* Copy the text from the buffer to the returned string */
308     if (end <= buf->gapStart) {
309         memcpy(text, &buf->buf[start], length);
310     } else if (start >= buf->gapStart) {
311         memcpy(text, &buf->buf[start+(buf->gapEnd-buf->gapStart)], length);
312     } else {
313         part1Length = buf->gapStart - start;
314         memcpy(text, &buf->buf[start], part1Length);
315         memcpy(&text[part1Length], &buf->buf[buf->gapEnd], length-part1Length);
316     }
317     text[length] = '\0';
318     return text;
319 }
320 
321 /*
322 ** Return the character at buffer position "pos".  Positions start at 0.
323 */
BufGetCharacter(const textBuffer * buf,int pos)324 char BufGetCharacter(const textBuffer* buf, int pos)
325 {
326     if (pos < 0 || pos >= buf->length)
327         return '\0';
328     if (pos < buf->gapStart)
329         return buf->buf[pos];
330     else
331     	return buf->buf[pos + buf->gapEnd-buf->gapStart];
332 }
333 
334 /*
335 ** Insert null-terminated string "text" at position "pos" in "buf"
336 */
BufInsert(textBuffer * buf,int pos,const char * text)337 void BufInsert(textBuffer *buf, int pos, const char *text)
338 {
339     int nInserted;
340 
341     /* if pos is not contiguous to existing text, make it */
342     if (pos > buf->length) pos = buf->length;
343     if (pos < 0 ) pos = 0;
344 
345     /* Even if nothing is deleted, we must call these callbacks */
346     callPreDeleteCBs(buf, pos, 0);
347 
348     /* insert and redisplay */
349     nInserted = insert(buf, pos, text);
350     buf->cursorPosHint = pos + nInserted;
351     callModifyCBs(buf, pos, 0, nInserted, 0, NULL);
352 }
353 
354 /*
355 ** Delete the characters between "start" and "end", and insert the
356 ** null-terminated string "text" in their place in in "buf"
357 */
BufReplace(textBuffer * buf,int start,int end,const char * text)358 void BufReplace(textBuffer *buf, int start, int end, const char *text)
359 {
360     char *deletedText;
361     int nInserted = strlen(text);
362 
363     callPreDeleteCBs(buf, start, end-start);
364     deletedText = BufGetRange(buf, start, end);
365     delete(buf, start, end);
366     insert(buf, start, text);
367     buf->cursorPosHint = start + nInserted;
368     callModifyCBs(buf, start, end-start, nInserted, 0, deletedText);
369     NEditFree(deletedText);
370 }
371 
BufRemove(textBuffer * buf,int start,int end)372 void BufRemove(textBuffer *buf, int start, int end)
373 {
374     char *deletedText;
375 
376     /* Make sure the arguments make sense */
377     if (start > end) {
378     	int temp = start;
379 	start = end;
380 	end = temp;
381     }
382     if (start > buf->length) start = buf->length;
383     if (start < 0) start = 0;
384     if (end > buf->length) end = buf->length;
385     if (end < 0) end = 0;
386 
387     callPreDeleteCBs(buf, start, end-start);
388     /* Remove and redisplay */
389     deletedText = BufGetRange(buf, start, end);
390     delete(buf, start, end);
391     buf->cursorPosHint = start;
392     callModifyCBs(buf, start, end-start, 0, 0, deletedText);
393     NEditFree(deletedText);
394 }
395 
BufCopyFromBuf(textBuffer * fromBuf,textBuffer * toBuf,int fromStart,int fromEnd,int toPos)396 void BufCopyFromBuf(textBuffer *fromBuf, textBuffer *toBuf, int fromStart,
397     	int fromEnd, int toPos)
398 {
399     int length = fromEnd - fromStart;
400     int part1Length;
401 
402     /* Prepare the buffer to receive the new text.  If the new text fits in
403        the current buffer, just move the gap (if necessary) to where
404        the text should be inserted.  If the new text is too large, reallocate
405        the buffer with a gap large enough to accomodate the new text and a
406        gap of PREFERRED_GAP_SIZE */
407     if (length > toBuf->gapEnd - toBuf->gapStart)
408     	reallocateBuf(toBuf, toPos, length + PREFERRED_GAP_SIZE);
409     else if (toPos != toBuf->gapStart)
410 	moveGap(toBuf, toPos);
411 
412     /* Insert the new text (toPos now corresponds to the start of the gap) */
413     if (fromEnd <= fromBuf->gapStart) {
414         memcpy(&toBuf->buf[toPos], &fromBuf->buf[fromStart], length);
415     } else if (fromStart >= fromBuf->gapStart) {
416         memcpy(&toBuf->buf[toPos],
417             	&fromBuf->buf[fromStart+(fromBuf->gapEnd-fromBuf->gapStart)],
418             	length);
419     } else {
420         part1Length = fromBuf->gapStart - fromStart;
421         memcpy(&toBuf->buf[toPos], &fromBuf->buf[fromStart], part1Length);
422         memcpy(&toBuf->buf[toPos+part1Length], &fromBuf->buf[fromBuf->gapEnd],
423             	length-part1Length);
424     }
425     toBuf->gapStart += length;
426     toBuf->length += length;
427     updateSelections(toBuf, toPos, 0, length);
428 }
429 
430 /*
431 ** Insert "text" columnwise into buffer starting at displayed character
432 ** position "column" on the line beginning at "startPos".  Opens a rectangular
433 ** space the width and height of "text", by moving all text to the right of
434 ** "column" right.  If charsInserted and charsDeleted are not NULL, the
435 ** number of characters inserted and deleted in the operation (beginning
436 ** at startPos) are returned in these arguments
437 */
BufInsertCol(textBuffer * buf,int column,int startPos,const char * text,int * charsInserted,int * charsDeleted)438 void BufInsertCol(textBuffer *buf, int column, int startPos, const char *text,
439     	int *charsInserted, int *charsDeleted)
440 {
441     int nLines, lineStartPos, nDeleted, insertDeleted, nInserted;
442     char *deletedText;
443 
444     nLines = countLines(text);
445     lineStartPos = BufStartOfLine(buf, startPos);
446     nDeleted = BufEndOfLine(buf, BufCountForwardNLines(buf, startPos, nLines)) -
447     	    lineStartPos;
448     callPreDeleteCBs(buf, lineStartPos, nDeleted);
449     deletedText = BufGetRange(buf, lineStartPos, lineStartPos + nDeleted);
450     insertCol(buf, column, lineStartPos, text, &insertDeleted, &nInserted,
451     	    &buf->cursorPosHint);
452     if (nDeleted != insertDeleted)
453     	fprintf(stderr, "NEdit internal consistency check ins1 failed");
454     callModifyCBs(buf, lineStartPos, nDeleted, nInserted, 0, deletedText);
455     NEditFree(deletedText);
456     if (charsInserted != NULL)
457     	*charsInserted = nInserted;
458     if (charsDeleted != NULL)
459     	*charsDeleted = nDeleted;
460 }
461 
462 /*
463 ** Overlay "text" between displayed character positions "rectStart" and
464 ** "rectEnd" on the line beginning at "startPos".  If charsInserted and
465 ** charsDeleted are not NULL, the number of characters inserted and deleted
466 ** in the operation (beginning at startPos) are returned in these arguments.
467 ** If rectEnd equals -1, the width of the inserted text is measured first.
468 */
BufOverlayRect(textBuffer * buf,int startPos,int rectStart,int rectEnd,const char * text,int * charsInserted,int * charsDeleted)469 void BufOverlayRect(textBuffer *buf, int startPos, int rectStart,
470     	int rectEnd, const char *text, int *charsInserted, int *charsDeleted)
471 {
472     int nLines, lineStartPos, nDeleted, insertDeleted, nInserted;
473     char *deletedText;
474 
475     nLines = countLines(text);
476     lineStartPos = BufStartOfLine(buf, startPos);
477     if(rectEnd == -1)
478         rectEnd = rectStart + textWidth(text, buf->tabDist, buf->nullSubsChar);
479     lineStartPos = BufStartOfLine(buf, startPos);
480     nDeleted = BufEndOfLine(buf, BufCountForwardNLines(buf, startPos, nLines)) -
481     	    lineStartPos;
482     callPreDeleteCBs(buf, lineStartPos, nDeleted);
483     deletedText = BufGetRange(buf, lineStartPos, lineStartPos + nDeleted);
484     overlayRect(buf, lineStartPos, rectStart, rectEnd, text, &insertDeleted,
485     	    &nInserted, &buf->cursorPosHint);
486     if (nDeleted != insertDeleted)
487     	fprintf(stderr, "NEdit internal consistency check ovly1 failed");
488     callModifyCBs(buf, lineStartPos, nDeleted, nInserted, 0, deletedText);
489     NEditFree(deletedText);
490     if (charsInserted != NULL)
491     	*charsInserted = nInserted;
492     if (charsDeleted != NULL)
493     	*charsDeleted = nDeleted;
494 }
495 
496 /*
497 ** Replace a rectangular area in buf, given by "start", "end", "rectStart",
498 ** and "rectEnd", with "text".  If "text" is vertically longer than the
499 ** rectangle, add extra lines to make room for it.
500 */
BufReplaceRect(textBuffer * buf,int start,int end,int rectStart,int rectEnd,const char * text)501 void BufReplaceRect(textBuffer *buf, int start, int end, int rectStart,
502 	int rectEnd, const char *text)
503 {
504     char *deletedText;
505     char *insText=NULL;
506     int i, nInsertedLines, nDeletedLines, insLen, hint;
507     int insertDeleted, insertInserted, deleteInserted;
508     int linesPadded = 0;
509 
510     /* Make sure start and end refer to complete lines, since the
511        columnar delete and insert operations will replace whole lines */
512     start = BufStartOfLine(buf, start);
513     end = BufEndOfLine(buf, end);
514 
515     callPreDeleteCBs(buf, start, end-start);
516 
517     /* If more lines will be deleted than inserted, pad the inserted text
518        with newlines to make it as long as the number of deleted lines.  This
519        will indent all of the text to the right of the rectangle to the same
520        column.  If more lines will be inserted than deleted, insert extra
521        lines in the buffer at the end of the rectangle to make room for the
522        additional lines in "text" */
523     nInsertedLines = countLines(text);
524     nDeletedLines = BufCountLines(buf, start, end);
525     if (nInsertedLines < nDeletedLines) {
526         char *insPtr;
527 
528     	insLen = strlen(text);
529     	insText = (char*)NEditMalloc(insLen + nDeletedLines - nInsertedLines + 1);
530     	strcpy(insText, text);
531     	insPtr = insText + insLen;
532     	for (i=0; i<nDeletedLines-nInsertedLines; i++)
533     	    *insPtr++ = '\n';
534     	*insPtr = '\0';
535     } else if (nDeletedLines < nInsertedLines) {
536     	linesPadded = nInsertedLines-nDeletedLines;
537     	for (i=0; i<linesPadded; i++)
538     	    insert(buf, end, "\n");
539     } else /* nDeletedLines == nInsertedLines */ {
540     }
541 
542     /* Save a copy of the text which will be modified for the modify CBs */
543     deletedText = BufGetRange(buf, start, end);
544 
545     /* Delete then insert */
546     deleteRect(buf, start, end, rectStart, rectEnd, &deleteInserted, &hint);
547     if (insText) {
548     	insertCol(buf, rectStart, start, insText, &insertDeleted, &insertInserted,
549     		    &buf->cursorPosHint);
550         NEditFree(insText);
551     }
552     else
553     	insertCol(buf, rectStart, start, text, &insertDeleted, &insertInserted,
554     		    &buf->cursorPosHint);
555 
556     /* Figure out how many chars were inserted and call modify callbacks */
557     if (insertDeleted != deleteInserted + linesPadded)
558     	fprintf(stderr, "NEdit: internal consistency check repl1 failed\n");
559     callModifyCBs(buf, start, end-start, insertInserted, 0, deletedText);
560     NEditFree(deletedText);
561 }
562 
563 /*
564 ** Remove a rectangular swath of characters between character positions start
565 ** and end and horizontal displayed-character offsets rectStart and rectEnd.
566 */
BufRemoveRect(textBuffer * buf,int start,int end,int rectStart,int rectEnd)567 void BufRemoveRect(textBuffer *buf, int start, int end, int rectStart,
568 	int rectEnd)
569 {
570     char *deletedText;
571     int nInserted;
572 
573     start = BufStartOfLine(buf, start);
574     end = BufEndOfLine(buf, end);
575     callPreDeleteCBs(buf, start, end-start);
576     deletedText = BufGetRange(buf, start, end);
577     deleteRect(buf, start, end, rectStart, rectEnd, &nInserted,
578     	    &buf->cursorPosHint);
579     callModifyCBs(buf, start, end-start, nInserted, 0, deletedText);
580     NEditFree(deletedText);
581 }
582 
583 /*
584 ** Clear a rectangular "hole" out of the buffer between character positions
585 ** start and end and horizontal displayed-character offsets rectStart and
586 ** rectEnd.
587 */
BufClearRect(textBuffer * buf,int start,int end,int rectStart,int rectEnd)588 void BufClearRect(textBuffer *buf, int start, int end, int rectStart,
589 	int rectEnd)
590 {
591     int i, nLines;
592     char *newlineString;
593 
594     nLines = BufCountLines(buf, start, end);
595     newlineString = (char*)NEditMalloc(nLines+1);
596     for (i=0; i<nLines; i++)
597     	newlineString[i] = '\n';
598     newlineString[i] = '\0';
599     BufOverlayRect(buf, start, rectStart, rectEnd, newlineString,
600     	    NULL, NULL);
601     NEditFree(newlineString);
602 }
603 
BufGetTextInRect(textBuffer * buf,int start,int end,int rectStart,int rectEnd)604 char *BufGetTextInRect(textBuffer *buf, int start, int end,
605 	int rectStart, int rectEnd)
606 {
607     int lineStart, selLeft, selRight, len;
608     char *textOut, *textIn, *outPtr, *retabbedStr;
609 
610     start = BufStartOfLine(buf, start);
611     end = BufEndOfLine(buf, end);
612     textOut = (char*)NEditMalloc((end - start) + 1);
613     lineStart = start;
614     outPtr = textOut;
615     while (lineStart <= end) {
616         findRectSelBoundariesForCopy(buf, lineStart, rectStart, rectEnd,
617         	&selLeft, &selRight);
618         textIn = BufGetRange(buf, selLeft, selRight);
619         len = selRight - selLeft;
620         memcpy(outPtr, textIn, len);
621         NEditFree(textIn);
622         outPtr += len;
623         lineStart = BufEndOfLine(buf, selRight) + 1;
624         *outPtr++ = '\n';
625     }
626     if (outPtr != textOut)
627     	outPtr--;  /* don't leave trailing newline */
628     *outPtr = '\0';
629 
630     /* If necessary, realign the tabs in the selection as if the text were
631        positioned at the left margin */
632     retabbedStr = realignTabs(textOut, rectStart, 0, buf->tabDist,
633     	    buf->useTabs, buf->nullSubsChar, &len);
634     NEditFree(textOut);
635     return retabbedStr;
636 }
637 
638 /*
639 ** Get the hardware tab distance used by all displays for this buffer,
640 ** and used in computing offsets for rectangular selection operations.
641 */
BufGetTabDistance(textBuffer * buf)642 int BufGetTabDistance(textBuffer *buf)
643 {
644     return buf->tabDist;
645 }
646 
647 /*
648 ** Set the hardware tab distance used by all displays for this buffer,
649 ** and used in computing offsets for rectangular selection operations.
650 */
BufSetTabDistance(textBuffer * buf,int tabDist)651 void BufSetTabDistance(textBuffer *buf, int tabDist)
652 {
653     const char *deletedText;
654 
655     /* First call the pre-delete callbacks with the previous tab setting
656        still active. */
657     callPreDeleteCBs(buf, 0, buf->length);
658 
659     /* Change the tab setting */
660     buf->tabDist = tabDist;
661 
662     /* Force any display routines to redisplay everything */
663     deletedText = BufAsString(buf);
664     callModifyCBs(buf, 0, buf->length, buf->length, 0, deletedText);
665 }
666 
BufCheckDisplay(textBuffer * buf,int start,int end)667 void BufCheckDisplay(textBuffer *buf, int start, int end)
668 {
669     /* just to make sure colors in the selected region are up to date */
670     callModifyCBs(buf, start, 0, 0, end-start, NULL);
671 }
672 
BufSelect(textBuffer * buf,int start,int end)673 void BufSelect(textBuffer *buf, int start, int end)
674 {
675     selection oldSelection = buf->primary;
676 
677     setSelection(&buf->primary, start, end);
678     redisplaySelection(buf, &oldSelection, &buf->primary);
679 }
680 
BufUnselect(textBuffer * buf)681 void BufUnselect(textBuffer *buf)
682 {
683     selection oldSelection = buf->primary;
684 
685     buf->primary.selected = False;
686     buf->primary.zeroWidth = False;
687     redisplaySelection(buf, &oldSelection, &buf->primary);
688 }
689 
BufRectSelect(textBuffer * buf,int start,int end,int rectStart,int rectEnd)690 void BufRectSelect(textBuffer *buf, int start, int end, int rectStart,
691         int rectEnd)
692 {
693     selection oldSelection = buf->primary;
694 
695     setRectSelect(&buf->primary, start, end, rectStart, rectEnd);
696     redisplaySelection(buf, &oldSelection, &buf->primary);
697 }
698 
BufGetSelectionPos(textBuffer * buf,int * start,int * end,int * isRect,int * rectStart,int * rectEnd)699 int BufGetSelectionPos(textBuffer *buf, int *start, int *end,
700         int *isRect, int *rectStart, int *rectEnd)
701 {
702     return getSelectionPos(&buf->primary, start, end, isRect, rectStart,
703     	    rectEnd);
704 }
705 
706 /* Same as above, but also returns TRUE for empty selections */
BufGetEmptySelectionPos(textBuffer * buf,int * start,int * end,int * isRect,int * rectStart,int * rectEnd)707 int BufGetEmptySelectionPos(textBuffer *buf, int *start, int *end,
708         int *isRect, int *rectStart, int *rectEnd)
709 {
710     return getSelectionPos(&buf->primary, start, end, isRect, rectStart,
711     	    rectEnd) || buf->primary.zeroWidth;
712 }
713 
BufGetSelectionText(textBuffer * buf)714 char *BufGetSelectionText(textBuffer *buf)
715 {
716     return getSelectionText(buf, &buf->primary);
717 }
718 
BufRemoveSelected(textBuffer * buf)719 void BufRemoveSelected(textBuffer *buf)
720 {
721     removeSelected(buf, &buf->primary);
722 }
723 
BufReplaceSelected(textBuffer * buf,const char * text)724 void BufReplaceSelected(textBuffer *buf, const char *text)
725 {
726     replaceSelected(buf, &buf->primary, text);
727 }
728 
BufSecondarySelect(textBuffer * buf,int start,int end)729 void BufSecondarySelect(textBuffer *buf, int start, int end)
730 {
731     selection oldSelection = buf->secondary;
732 
733     setSelection(&buf->secondary, start, end);
734     redisplaySelection(buf, &oldSelection, &buf->secondary);
735 }
736 
BufSecondaryUnselect(textBuffer * buf)737 void BufSecondaryUnselect(textBuffer *buf)
738 {
739     selection oldSelection = buf->secondary;
740 
741     buf->secondary.selected = False;
742     buf->secondary.zeroWidth = False;
743     redisplaySelection(buf, &oldSelection, &buf->secondary);
744 }
745 
BufSecRectSelect(textBuffer * buf,int start,int end,int rectStart,int rectEnd)746 void BufSecRectSelect(textBuffer *buf, int start, int end,
747         int rectStart, int rectEnd)
748 {
749     selection oldSelection = buf->secondary;
750 
751     setRectSelect(&buf->secondary, start, end, rectStart, rectEnd);
752     redisplaySelection(buf, &oldSelection, &buf->secondary);
753 }
754 
BufGetSecSelectPos(textBuffer * buf,int * start,int * end,int * isRect,int * rectStart,int * rectEnd)755 int BufGetSecSelectPos(textBuffer *buf, int *start, int *end,
756         int *isRect, int *rectStart, int *rectEnd)
757 {
758     return getSelectionPos(&buf->secondary, start, end, isRect, rectStart,
759     	    rectEnd);
760 }
761 
BufGetSecSelectText(textBuffer * buf)762 char *BufGetSecSelectText(textBuffer *buf)
763 {
764     return getSelectionText(buf, &buf->secondary);
765 }
766 
BufRemoveSecSelect(textBuffer * buf)767 void BufRemoveSecSelect(textBuffer *buf)
768 {
769     removeSelected(buf, &buf->secondary);
770 }
771 
BufReplaceSecSelect(textBuffer * buf,const char * text)772 void BufReplaceSecSelect(textBuffer *buf, const char *text)
773 {
774     replaceSelected(buf, &buf->secondary, text);
775 }
776 
BufHighlight(textBuffer * buf,int start,int end)777 void BufHighlight(textBuffer *buf, int start, int end)
778 {
779     selection oldSelection = buf->highlight;
780 
781     setSelection(&buf->highlight, start, end);
782     redisplaySelection(buf, &oldSelection, &buf->highlight);
783 }
784 
BufUnhighlight(textBuffer * buf)785 void BufUnhighlight(textBuffer *buf)
786 {
787     selection oldSelection = buf->highlight;
788 
789     buf->highlight.selected = False;
790     buf->highlight.zeroWidth = False;
791     redisplaySelection(buf, &oldSelection, &buf->highlight);
792 }
793 
BufRectHighlight(textBuffer * buf,int start,int end,int rectStart,int rectEnd)794 void BufRectHighlight(textBuffer *buf, int start, int end,
795         int rectStart, int rectEnd)
796 {
797     selection oldSelection = buf->highlight;
798 
799     setRectSelect(&buf->highlight, start, end, rectStart, rectEnd);
800     redisplaySelection(buf, &oldSelection, &buf->highlight);
801 }
802 
BufGetHighlightPos(textBuffer * buf,int * start,int * end,int * isRect,int * rectStart,int * rectEnd)803 int BufGetHighlightPos(textBuffer *buf, int *start, int *end,
804         int *isRect, int *rectStart, int *rectEnd)
805 {
806     return getSelectionPos(&buf->highlight, start, end, isRect, rectStart,
807     	    rectEnd);
808 }
809 
810 /*
811 ** Add a callback routine to be called when the buffer is modified
812 */
BufAddModifyCB(textBuffer * buf,bufModifyCallbackProc bufModifiedCB,void * cbArg)813 void BufAddModifyCB(textBuffer *buf, bufModifyCallbackProc bufModifiedCB,
814 	void *cbArg)
815 {
816     bufModifyCallbackProc *newModifyProcs;
817     void **newCBArgs;
818     int i;
819 
820     newModifyProcs = (bufModifyCallbackProc *)
821     	    NEditMalloc(sizeof(bufModifyCallbackProc *) * (buf->nModifyProcs+1));
822     newCBArgs = (void **)NEditMalloc(sizeof(void *) * (buf->nModifyProcs+1));
823     for (i=0; i<buf->nModifyProcs; i++) {
824     	newModifyProcs[i] = buf->modifyProcs[i];
825     	newCBArgs[i] = buf->cbArgs[i];
826     }
827     if (buf->nModifyProcs != 0) {
828 	NEditFree(buf->modifyProcs);
829 	NEditFree(buf->cbArgs);
830     }
831     newModifyProcs[buf->nModifyProcs] = bufModifiedCB;
832     newCBArgs[buf->nModifyProcs] = cbArg;
833     buf->nModifyProcs++;
834     buf->modifyProcs = newModifyProcs;
835     buf->cbArgs = newCBArgs;
836 }
837 
838 /*
839 ** Similar to the above, but makes sure that the callback is called before
840 ** normal priority callbacks.
841 */
BufAddHighPriorityModifyCB(textBuffer * buf,bufModifyCallbackProc bufModifiedCB,void * cbArg)842 void BufAddHighPriorityModifyCB(textBuffer *buf, bufModifyCallbackProc bufModifiedCB,
843 	void *cbArg)
844 {
845     bufModifyCallbackProc *newModifyProcs;
846     void **newCBArgs;
847     int i;
848 
849     newModifyProcs = (bufModifyCallbackProc *)
850     	    NEditMalloc(sizeof(bufModifyCallbackProc *) * (buf->nModifyProcs+1));
851     newCBArgs = (void **)NEditMalloc(sizeof(void *) * (buf->nModifyProcs+1));
852     for (i=0; i<buf->nModifyProcs; i++) {
853     	newModifyProcs[i+1] = buf->modifyProcs[i];
854     	newCBArgs[i+1] = buf->cbArgs[i];
855     }
856     if (buf->nModifyProcs != 0) {
857 	NEditFree(buf->modifyProcs);
858 	NEditFree(buf->cbArgs);
859     }
860     newModifyProcs[0] = bufModifiedCB;
861     newCBArgs[0] = cbArg;
862     buf->nModifyProcs++;
863     buf->modifyProcs = newModifyProcs;
864     buf->cbArgs = newCBArgs;
865 }
866 
BufRemoveModifyCB(textBuffer * buf,bufModifyCallbackProc bufModifiedCB,void * cbArg)867 void BufRemoveModifyCB(textBuffer *buf, bufModifyCallbackProc bufModifiedCB,
868 	void *cbArg)
869 {
870     int i, toRemove = -1;
871     bufModifyCallbackProc *newModifyProcs;
872     void **newCBArgs;
873 
874     /* find the matching callback to remove */
875     for (i=0; i<buf->nModifyProcs; i++) {
876     	if (buf->modifyProcs[i] == bufModifiedCB && buf->cbArgs[i] == cbArg) {
877     	    toRemove = i;
878     	    break;
879     	}
880     }
881     if (toRemove == -1) {
882     	fprintf(stderr, "NEdit Internal Error: Can't find modify CB to remove\n");
883     	return;
884     }
885 
886     /* Allocate new lists for remaining callback procs and args (if
887        any are left) */
888     buf->nModifyProcs--;
889     if (buf->nModifyProcs == 0) {
890     	buf->nModifyProcs = 0;
891     	NEditFree(buf->modifyProcs);
892     	buf->modifyProcs = NULL;
893 	NEditFree(buf->cbArgs);
894 	buf->cbArgs = NULL;
895 	return;
896     }
897     newModifyProcs = (bufModifyCallbackProc *)
898     	    NEditMalloc(sizeof(bufModifyCallbackProc *) * (buf->nModifyProcs));
899     newCBArgs = (void **)NEditMalloc(sizeof(void *) * (buf->nModifyProcs));
900 
901     /* copy out the remaining members and free the old lists */
902     for (i=0; i<toRemove; i++) {
903     	newModifyProcs[i] = buf->modifyProcs[i];
904     	newCBArgs[i] = buf->cbArgs[i];
905     }
906     for (; i<buf->nModifyProcs; i++) {
907 	newModifyProcs[i] = buf->modifyProcs[i+1];
908     	newCBArgs[i] = buf->cbArgs[i+1];
909     }
910     NEditFree(buf->modifyProcs);
911     NEditFree(buf->cbArgs);
912     buf->modifyProcs = newModifyProcs;
913     buf->cbArgs = newCBArgs;
914 }
915 
916 /*
917 ** Add a callback routine to be called before text is deleted from the buffer.
918 */
BufAddPreDeleteCB(textBuffer * buf,bufPreDeleteCallbackProc bufPreDeleteCB,void * cbArg)919 void BufAddPreDeleteCB(textBuffer *buf, bufPreDeleteCallbackProc bufPreDeleteCB,
920 	void *cbArg)
921 {
922     bufPreDeleteCallbackProc *newPreDeleteProcs;
923     void **newCBArgs;
924     int i;
925 
926     newPreDeleteProcs = (bufPreDeleteCallbackProc *)
927     	    NEditMalloc(sizeof(bufPreDeleteCallbackProc *) * (buf->nPreDeleteProcs+1));
928     newCBArgs = (void **)NEditMalloc(sizeof(void *) * (buf->nPreDeleteProcs+1));
929     for (i=0; i<buf->nPreDeleteProcs; i++) {
930     	newPreDeleteProcs[i] = buf->preDeleteProcs[i];
931     	newCBArgs[i] = buf->preDeleteCbArgs[i];
932     }
933     if (buf->nPreDeleteProcs != 0) {
934 	NEditFree(buf->preDeleteProcs);
935 	NEditFree(buf->preDeleteCbArgs);
936     }
937     newPreDeleteProcs[buf->nPreDeleteProcs] =  bufPreDeleteCB;
938     newCBArgs[buf->nPreDeleteProcs] = cbArg;
939     buf->nPreDeleteProcs++;
940     buf->preDeleteProcs = newPreDeleteProcs;
941     buf->preDeleteCbArgs = newCBArgs;
942 }
943 
BufRemovePreDeleteCB(textBuffer * buf,bufPreDeleteCallbackProc bufPreDeleteCB,void * cbArg)944 void BufRemovePreDeleteCB(textBuffer *buf, bufPreDeleteCallbackProc bufPreDeleteCB,
945 	void *cbArg)
946 {
947     int i, toRemove = -1;
948     bufPreDeleteCallbackProc *newPreDeleteProcs;
949     void **newCBArgs;
950 
951     /* find the matching callback to remove */
952     for (i=0; i<buf->nPreDeleteProcs; i++) {
953     	if (buf->preDeleteProcs[i] == bufPreDeleteCB &&
954 	    buf->preDeleteCbArgs[i] == cbArg) {
955     	    toRemove = i;
956     	    break;
957     	}
958     }
959     if (toRemove == -1) {
960     	fprintf(stderr, "NEdit Internal Error: Can't find pre-delete CB to remove\n");
961     	return;
962     }
963 
964     /* Allocate new lists for remaining callback procs and args (if
965        any are left) */
966     buf->nPreDeleteProcs--;
967     if (buf->nPreDeleteProcs == 0) {
968     	buf->nPreDeleteProcs = 0;
969     	NEditFree(buf->preDeleteProcs);
970     	buf->preDeleteProcs = NULL;
971 	NEditFree(buf->preDeleteCbArgs);
972 	buf->preDeleteCbArgs = NULL;
973 	return;
974     }
975     newPreDeleteProcs = (bufPreDeleteCallbackProc *)
976     	    NEditMalloc(sizeof(bufPreDeleteCallbackProc *) * (buf->nPreDeleteProcs));
977     newCBArgs = (void **)NEditMalloc(sizeof(void *) * (buf->nPreDeleteProcs));
978 
979     /* copy out the remaining members and free the old lists */
980     for (i=0; i<toRemove; i++) {
981     	newPreDeleteProcs[i] = buf->preDeleteProcs[i];
982     	newCBArgs[i] = buf->preDeleteCbArgs[i];
983     }
984     for (; i<buf->nPreDeleteProcs; i++) {
985 	newPreDeleteProcs[i] = buf->preDeleteProcs[i+1];
986     	newCBArgs[i] = buf->preDeleteCbArgs[i+1];
987     }
988     NEditFree(buf->preDeleteProcs);
989     NEditFree(buf->preDeleteCbArgs);
990     buf->preDeleteProcs = newPreDeleteProcs;
991     buf->preDeleteCbArgs = newCBArgs;
992 }
993 
994 /*
995 ** Find the position of the start of the line containing position "pos"
996 */
BufStartOfLine(textBuffer * buf,int pos)997 int BufStartOfLine(textBuffer *buf, int pos)
998 {
999     int startPos;
1000 
1001     if (!searchBackward(buf, pos, '\n', &startPos))
1002     	return 0;
1003     return startPos + 1;
1004 }
1005 
1006 
1007 /*
1008 ** Find the position of the end of the line containing position "pos"
1009 ** (which is either a pointer to the newline character ending the line,
1010 ** or a pointer to one character beyond the end of the buffer)
1011 */
BufEndOfLine(textBuffer * buf,int pos)1012 int BufEndOfLine(textBuffer *buf, int pos)
1013 {
1014     int endPos;
1015 
1016     if (!searchForward(buf, pos, '\n', &endPos))
1017     	endPos = buf->length;
1018     return endPos;
1019 }
1020 
1021 /*
1022 ** Get a character from the text buffer expanded into it's screen
1023 ** representation (which may be several characters for a tab or a
1024 ** control code).  Returns the number of characters written to "outStr".
1025 ** "indent" is the number of characters from the start of the line
1026 ** for figuring tabs.  Output string is guranteed to be shorter or
1027 ** equal in length to MAX_EXP_CHAR_LEN
1028 */
BufGetExpandedChar(const textBuffer * buf,int pos,int indent,char * outStr)1029 int BufGetExpandedChar(const textBuffer* buf, int pos, int indent,
1030         char* outStr)
1031 {
1032     return BufExpandCharacter(BufGetCharacter(buf, pos), indent, outStr,
1033     	    buf->tabDist, buf->nullSubsChar);
1034 }
1035 
1036 /*
1037 ** Expand a single character from the text buffer into it's screen
1038 ** representation (which may be several characters for a tab or a
1039 ** control code).  Returns the number of characters added to "outStr".
1040 ** "indent" is the number of characters from the start of the line
1041 ** for figuring tabs.  Output string is guranteed to be shorter or
1042 ** equal in length to MAX_EXP_CHAR_LEN
1043 */
BufExpandCharacter(char c,int indent,char * outStr,int tabDist,char nullSubsChar)1044 int BufExpandCharacter(char c, int indent, char *outStr,
1045         int tabDist, char nullSubsChar)
1046 {
1047     int i, nSpaces;
1048 
1049     /* Convert tabs to spaces */
1050     if (c == '\t') {
1051 	nSpaces = tabDist - (indent % tabDist);
1052 	for (i=0; i<nSpaces; i++)
1053 	    outStr[i] = ' ';
1054 	return nSpaces;
1055     }
1056 
1057     /* Convert ASCII (and EBCDIC in the __MVS__ (OS/390) case) control
1058        codes to readable character sequences */
1059     if (c == nullSubsChar) {
1060 	sprintf(outStr, "<nul>");
1061     	return 5;
1062     }
1063 #ifdef __MVS__
1064     if (((unsigned char)c) <= 63) {
1065     	sprintf(outStr, "<%s>", ControlCodeTable[(unsigned char)c]);
1066     	return strlen(outStr);
1067     }
1068 #else
1069     if (((unsigned char)c) <= 31) {
1070     	sprintf(outStr, "<%s>", ControlCodeTable[(unsigned char)c]);
1071     	return strlen(outStr);
1072     } else if (c == 127) {
1073     	sprintf(outStr, "<del>");
1074     	return 5;
1075     }
1076 #endif
1077 
1078     /* Otherwise, just return the character */
1079     *outStr = c;
1080     return 1;
1081 }
1082 
1083 /*
1084 ** Return the length in displayed characters of character "c" expanded
1085 ** for display (as discussed above in BufGetExpandedChar).  If the
1086 ** buffer for which the character width is being measured is doing null
1087 ** substitution, nullSubsChar should be passed as that character (or nul
1088 ** to ignore).
1089 */
BufCharWidth(char c,int indent,int tabDist,char nullSubsChar)1090 int BufCharWidth(char c, int indent, int tabDist, char nullSubsChar)
1091 {
1092     /* Note, this code must parallel that in BufExpandCharacter */
1093     if (c == nullSubsChar)
1094     	return 5;
1095     else if (c == '\t')
1096 	return tabDist - (indent % tabDist);
1097     else if (((unsigned char)c) <= 31)
1098     	return strlen(ControlCodeTable[(unsigned char)c]) + 2;
1099     else if (c == 127)
1100     	return 5;
1101     return 1;
1102 }
1103 
1104 /*
1105 ** Count the number of displayed characters between buffer position
1106 ** "lineStartPos" and "targetPos". (displayed characters are the characters
1107 ** shown on the screen to represent characters in the buffer, where tabs and
1108 ** control characters are expanded)
1109 */
BufCountDispChars(const textBuffer * buf,int lineStartPos,int targetPos)1110 int BufCountDispChars(const textBuffer* buf, int lineStartPos,
1111         int targetPos)
1112 {
1113     int pos, charCount = 0;
1114     char expandedChar[MAX_EXP_CHAR_LEN];
1115 
1116     pos = lineStartPos;
1117     while (pos < targetPos && pos < buf->length)
1118     	charCount += BufGetExpandedChar(buf, pos++, charCount, expandedChar);
1119     return charCount;
1120 }
1121 
1122 /*
1123 ** Count forward from buffer position "startPos" in displayed characters
1124 ** (displayed characters are the characters shown on the screen to represent
1125 ** characters in the buffer, where tabs and control characters are expanded)
1126 */
BufCountForwardDispChars(textBuffer * buf,int lineStartPos,int nChars)1127 int BufCountForwardDispChars(textBuffer *buf, int lineStartPos, int nChars)
1128 {
1129     int pos, charCount = 0;
1130     char c;
1131 
1132     pos = lineStartPos;
1133     while (charCount < nChars && pos < buf->length) {
1134     	c = BufGetCharacter(buf, pos);
1135     	if (c == '\n')
1136     	    return pos;
1137     	charCount += BufCharWidth(c, charCount, buf->tabDist,buf->nullSubsChar);
1138     	pos++;
1139     }
1140     return pos;
1141 }
1142 
1143 /*
1144 ** Count the number of newlines between startPos and endPos in buffer "buf".
1145 ** The character at position "endPos" is not counted.
1146 */
BufCountLines(textBuffer * buf,int startPos,int endPos)1147 int BufCountLines(textBuffer *buf, int startPos, int endPos)
1148 {
1149     int pos, gapLen = buf->gapEnd - buf->gapStart;
1150     int lineCount = 0;
1151 
1152     pos = startPos;
1153     while (pos < buf->gapStart) {
1154         if (pos == endPos)
1155             return lineCount;
1156         if (buf->buf[pos++] == '\n')
1157             lineCount++;
1158     }
1159     while (pos < buf->length) {
1160         if (pos == endPos)
1161             return lineCount;
1162     	if (buf->buf[pos++ + gapLen] == '\n')
1163             lineCount++;
1164     }
1165     return lineCount;
1166 }
1167 
1168 /*
1169 ** Find the first character of the line "nLines" forward from "startPos"
1170 ** in "buf" and return its position
1171 */
BufCountForwardNLines(const textBuffer * buf,int startPos,unsigned nLines)1172 int BufCountForwardNLines(const textBuffer* buf, int startPos,
1173         unsigned nLines)
1174 {
1175     int pos, gapLen = buf->gapEnd - buf->gapStart;
1176     int lineCount = 0;
1177 
1178     if (nLines == 0)
1179     	return startPos;
1180 
1181     pos = startPos;
1182     while (pos < buf->gapStart) {
1183         if (buf->buf[pos++] == '\n') {
1184             lineCount++;
1185             if (lineCount == nLines)
1186             	return pos;
1187         }
1188     }
1189     while (pos < buf->length) {
1190     	if (buf->buf[pos++ + gapLen] == '\n') {
1191             lineCount++;
1192             if (lineCount >= nLines)
1193             	return pos;
1194         }
1195     }
1196     return pos;
1197 }
1198 
1199 /*
1200 ** Find the position of the first character of the line "nLines" backwards
1201 ** from "startPos" (not counting the character pointed to by "startpos" if
1202 ** that is a newline) in "buf".  nLines == 0 means find the beginning of
1203 ** the line
1204 */
BufCountBackwardNLines(textBuffer * buf,int startPos,int nLines)1205 int BufCountBackwardNLines(textBuffer *buf, int startPos, int nLines)
1206 {
1207     int pos, gapLen = buf->gapEnd - buf->gapStart;
1208     int lineCount = -1;
1209 
1210     pos = startPos - 1;
1211     if (pos <= 0)
1212     	return 0;
1213 
1214     while (pos >= buf->gapStart) {
1215     	if (buf->buf[pos + gapLen] == '\n') {
1216             if (++lineCount >= nLines)
1217             	return pos + 1;
1218         }
1219         pos--;
1220     }
1221     while (pos >= 0) {
1222         if (buf->buf[pos] == '\n') {
1223             if (++lineCount >= nLines)
1224             	return pos + 1;
1225         }
1226         pos--;
1227     }
1228     return 0;
1229 }
1230 
1231 /*
1232 ** Search forwards in buffer "buf" for characters in "searchChars", starting
1233 ** with the character "startPos", and returning the result in "foundPos"
1234 ** returns True if found, False if not.
1235 */
BufSearchForward(textBuffer * buf,int startPos,const char * searchChars,int * foundPos)1236 int BufSearchForward(textBuffer *buf, int startPos, const char *searchChars,
1237 	int *foundPos)
1238 {
1239     int pos, gapLen = buf->gapEnd - buf->gapStart;
1240     const char *c;
1241 
1242     pos = startPos;
1243     while (pos < buf->gapStart) {
1244         for (c=searchChars; *c!='\0'; c++) {
1245 	    if (buf->buf[pos] == *c) {
1246         	*foundPos = pos;
1247         	return True;
1248             }
1249         }
1250         pos++;
1251     }
1252     while (pos < buf->length) {
1253     	for (c=searchChars; *c!='\0'; c++) {
1254     	    if (buf->buf[pos + gapLen] == *c) {
1255         	*foundPos = pos;
1256         	return True;
1257             }
1258         }
1259         pos++;
1260     }
1261     *foundPos = buf->length;
1262     return False;
1263 }
1264 
1265 /*
1266 ** Search backwards in buffer "buf" for characters in "searchChars", starting
1267 ** with the character BEFORE "startPos", returning the result in "foundPos"
1268 ** returns True if found, False if not.
1269 */
BufSearchBackward(textBuffer * buf,int startPos,const char * searchChars,int * foundPos)1270 int BufSearchBackward(textBuffer *buf, int startPos, const char *searchChars,
1271 	int *foundPos)
1272 {
1273     int pos, gapLen = buf->gapEnd - buf->gapStart;
1274     const char *c;
1275 
1276     if (startPos == 0) {
1277     	*foundPos = 0;
1278     	return False;
1279     }
1280     pos = startPos == 0 ? 0 : startPos - 1;
1281     while (pos >= buf->gapStart) {
1282     	for (c=searchChars; *c!='\0'; c++) {
1283     	    if (buf->buf[pos + gapLen] == *c) {
1284         	*foundPos = pos;
1285         	return True;
1286             }
1287         }
1288         pos--;
1289     }
1290     while (pos >= 0) {
1291     	for (c=searchChars; *c!='\0'; c++) {
1292             if (buf->buf[pos] == *c) {
1293         	*foundPos = pos;
1294         	return True;
1295             }
1296         }
1297         pos--;
1298     }
1299     *foundPos = 0;
1300     return False;
1301 }
1302 
1303 /*
1304 ** A horrible design flaw in NEdit (from the very start, before we knew that
1305 ** NEdit would become so popular), is that it uses C NULL terminated strings
1306 ** to hold text.  This means editing text containing NUL characters is not
1307 ** possible without special consideration.  Here is the special consideration.
1308 ** The routines below maintain a special substitution-character which stands
1309 ** in for a null, and translates strings an buffers back and forth from/to
1310 ** the substituted form, figure out what to substitute, and figure out
1311 ** when we're in over our heads and no translation is possible.
1312 */
1313 
1314 /*
1315 ** The primary routine for integrating new text into a text buffer with
1316 ** substitution of another character for ascii nuls.  This substitutes null
1317 ** characters in the string in preparation for being copied or replaced
1318 ** into the buffer, and if neccessary, adjusts the buffer as well, in the
1319 ** event that the string contains the character it is currently using for
1320 ** substitution.  Returns False, if substitution is no longer possible
1321 ** because all non-printable characters are already in use.
1322 */
BufSubstituteNullChars(char * string,int length,textBuffer * buf)1323 int BufSubstituteNullChars(char *string, int length, textBuffer *buf)
1324 {
1325     char histogram[256];
1326 
1327     /* Find out what characters the string contains */
1328     histogramCharacters(string, length, histogram, True);
1329 
1330     /* Does the string contain the null-substitute character?  If so, re-
1331        histogram the buffer text to find a character which is ok in both the
1332        string and the buffer, and change the buffer's null-substitution
1333        character.  If none can be found, give up and return False */
1334     if (histogram[(unsigned char)buf->nullSubsChar] != 0) {
1335         char *bufString, newSubsChar;
1336         /* here we know we can modify the file buffer directly,
1337            so we cast away constness */
1338         bufString = (char *)BufAsString(buf);
1339         histogramCharacters(bufString, buf->length, histogram, False);
1340         newSubsChar = chooseNullSubsChar(histogram);
1341         if (newSubsChar == '\0') {
1342             return False;
1343         }
1344         /* bufString points to the buffer's data, so we substitute in situ */
1345         subsChars(bufString, buf->length, buf->nullSubsChar, newSubsChar);
1346         buf->nullSubsChar = newSubsChar;
1347     }
1348 
1349     /* If the string contains null characters, substitute them with the
1350        buffer's null substitution character */
1351     if (histogram[0] != 0)
1352 	subsChars(string, length, '\0', buf->nullSubsChar);
1353     return True;
1354 }
1355 
1356 /*
1357 ** Convert strings obtained from buffers which contain null characters, which
1358 ** have been substituted for by a special substitution character, back to
1359 ** a null-containing string.  There is no time penalty for calling this
1360 ** routine if no substitution has been done.
1361 */
BufUnsubstituteNullChars(char * string,textBuffer * buf)1362 void BufUnsubstituteNullChars(char *string, textBuffer *buf)
1363 {
1364     register char *c, subsChar = buf->nullSubsChar;
1365 
1366     if (subsChar == '\0')
1367 	return;
1368     for (c=string; *c != '\0'; c++)
1369     	if (*c == subsChar)
1370 	    *c = '\0';
1371 }
1372 
1373 /*
1374 ** Compares len Bytes contained in buf starting at Position pos with
1375 ** the contens of cmpText. Returns 0 if there are no differences,
1376 ** != 0 otherwise.
1377 **
1378 */
BufCmp(textBuffer * buf,int pos,int len,const char * cmpText)1379 int BufCmp(textBuffer * buf, int pos, int len, const char *cmpText)
1380 {
1381     int     posEnd;
1382     int     part1Length;
1383     int     result;
1384 
1385     posEnd = pos + len;
1386     if (posEnd > buf->length) {
1387         return (1);
1388     }
1389     if (pos < 0) {
1390         return (-1);
1391     }
1392 
1393     if (posEnd <= buf->gapStart) {
1394         return (strncmp(&(buf->buf[pos]), cmpText, len));
1395     } else if (pos >= buf->gapStart) {
1396         return (strncmp (&buf->buf[pos + (buf->gapEnd - buf->gapStart)],
1397 	    	    	cmpText, len));
1398     } else {
1399         part1Length = buf->gapStart - pos;
1400         result = strncmp(&buf->buf[pos], cmpText, part1Length);
1401         if (result) {
1402             return (result);
1403 	}
1404         return (strncmp(&buf->buf[buf->gapEnd], &cmpText[part1Length],
1405 	    	    	len - part1Length));
1406     }
1407 }
1408 
1409 /*
1410 ** Create a pseudo-histogram of the characters in a string (don't actually
1411 ** count, because we don't want overflow, just mark the character's presence
1412 ** with a 1).  If init is true, initialize the histogram before acumulating.
1413 ** if not, add the new data to an existing histogram.
1414 */
histogramCharacters(const char * string,int length,char hist[256],int init)1415 static void histogramCharacters(const char *string, int length, char hist[256],
1416 	int init)
1417 {
1418     int i;
1419     const char *c;
1420 
1421     if (init)
1422 	for (i=0; i<256; i++)
1423 	    hist[i] = 0;
1424     for (c=string; c < &string[length]; c++)
1425         hist[*((unsigned char *)c)] |= 1;
1426 }
1427 
1428 /*
1429 ** Substitute fromChar with toChar in string.
1430 */
subsChars(char * string,int length,char fromChar,char toChar)1431 static void subsChars(char *string, int length, char fromChar, char toChar)
1432 {
1433     char *c;
1434 
1435     for (c=string; c < &string[length]; c++)
1436 	if (*c == fromChar) *c = toChar;
1437 }
1438 
1439 /*
1440 ** Search through ascii control characters in histogram in order of least
1441 ** likelihood of use, find an unused character to use as a stand-in for a
1442 ** null.  If the character set is full (no available characters outside of
1443 ** the printable set, return the null character.
1444 */
chooseNullSubsChar(char hist[256])1445 static char chooseNullSubsChar(char hist[256])
1446 {
1447 #define N_REPLACEMENTS 25
1448     static char replacements[N_REPLACEMENTS] = {1,2,3,4,5,6,14,15,16,17,18,19,
1449 	    20,21,22,23,24,25,26,28,29,30,31,11,7};
1450     int i;
1451     for (i = 0; i < N_REPLACEMENTS; i++)
1452 	if (hist[(unsigned char)replacements[i]] == 0)
1453 	    return replacements[i];
1454     return '\0';
1455 }
1456 
1457 /*
1458 ** Internal (non-redisplaying) version of BufInsert.  Returns the length of
1459 ** text inserted (this is just strlen(text), however this calculation can be
1460 ** expensive and the length will be required by any caller who will continue
1461 ** on to call redisplay).  pos must be contiguous with the existing text in
1462 ** the buffer (i.e. not past the end).
1463 */
insert(textBuffer * buf,int pos,const char * text)1464 static int insert(textBuffer *buf, int pos, const char *text)
1465 {
1466     int length = strlen(text);
1467 
1468     /* Prepare the buffer to receive the new text.  If the new text fits in
1469        the current buffer, just move the gap (if necessary) to where
1470        the text should be inserted.  If the new text is too large, reallocate
1471        the buffer with a gap large enough to accomodate the new text and a
1472        gap of PREFERRED_GAP_SIZE */
1473     if (length > buf->gapEnd - buf->gapStart)
1474     	reallocateBuf(buf, pos, length + PREFERRED_GAP_SIZE);
1475     else if (pos != buf->gapStart)
1476 	moveGap(buf, pos);
1477 
1478     /* Insert the new text (pos now corresponds to the start of the gap) */
1479     memcpy(&buf->buf[pos], text, length);
1480     buf->gapStart += length;
1481     buf->length += length;
1482     updateSelections(buf, pos, 0, length);
1483 
1484     return length;
1485 }
1486 
1487 /*
1488 ** Internal (non-redisplaying) version of BufRemove.  Removes the contents
1489 ** of the buffer between start and end (and moves the gap to the site of
1490 ** the delete).
1491 */
delete(textBuffer * buf,int start,int end)1492 static void delete(textBuffer *buf, int start, int end)
1493 {
1494     /* if the gap is not contiguous to the area to remove, move it there */
1495     if (start > buf->gapStart)
1496     	moveGap(buf, start);
1497     else if (end < buf->gapStart)
1498     	moveGap(buf, end);
1499 
1500     /* expand the gap to encompass the deleted characters */
1501     buf->gapEnd += end - buf->gapStart;
1502     buf->gapStart -= buf->gapStart - start;
1503 
1504     /* update the length */
1505     buf->length -= end - start;
1506 
1507     /* fix up any selections which might be affected by the change */
1508     updateSelections(buf, start, end-start, 0);
1509 }
1510 
1511 /*
1512 ** Insert a column of text without calling the modify callbacks.  Note that
1513 ** in some pathological cases, inserting can actually decrease the size of
1514 ** the buffer because of spaces being coalesced into tabs.  "nDeleted" and
1515 ** "nInserted" return the number of characters deleted and inserted beginning
1516 ** at the start of the line containing "startPos".  "endPos" returns buffer
1517 ** position of the lower left edge of the inserted column (as a hint for
1518 ** routines which need to set a cursor position).
1519 */
insertCol(textBuffer * buf,int column,int startPos,const char * insText,int * nDeleted,int * nInserted,int * endPos)1520 static void insertCol(textBuffer *buf, int column, int startPos,
1521         const char *insText, int *nDeleted, int *nInserted, int *endPos)
1522 {
1523     int nLines, start, end, insWidth, lineStart, lineEnd;
1524     int expReplLen, expInsLen, len, endOffset;
1525     char *outStr, *outPtr, *line, *replText, *expText, *insLine;
1526     const char *insPtr;
1527 
1528     if (column < 0)
1529     	column = 0;
1530 
1531     /* Allocate a buffer for the replacement string large enough to hold
1532        possibly expanded tabs in both the inserted text and the replaced
1533        area, as well as per line: 1) an additional 2*MAX_EXP_CHAR_LEN
1534        characters for padding where tabs and control characters cross the
1535        column of the selection, 2) up to "column" additional spaces per
1536        line for padding out to the position of "column", 3) padding up
1537        to the width of the inserted text if that must be padded to align
1538        the text beyond the inserted column.  (Space for additional
1539        newlines if the inserted text extends beyond the end of the buffer
1540        is counted with the length of insText) */
1541     start = BufStartOfLine(buf, startPos);
1542     nLines = countLines(insText) + 1;
1543     insWidth = textWidth(insText, buf->tabDist, buf->nullSubsChar);
1544     end = BufEndOfLine(buf, BufCountForwardNLines(buf, start, nLines-1));
1545     replText = BufGetRange(buf, start, end);
1546     expText = expandTabs(replText, 0, buf->tabDist, buf->nullSubsChar,
1547 	    &expReplLen);
1548     NEditFree(replText);
1549     NEditFree(expText);
1550     expText = expandTabs(insText, 0, buf->tabDist, buf->nullSubsChar,
1551 	    &expInsLen);
1552     NEditFree(expText);
1553     outStr = (char*) NEditMalloc(expReplLen + expInsLen +
1554     	    nLines * (column + insWidth + MAX_EXP_CHAR_LEN) + 1);
1555 
1556     /* Loop over all lines in the buffer between start and end inserting
1557        text at column, splitting tabs and adding padding appropriately */
1558     outPtr = outStr;
1559     lineStart = start;
1560     insPtr = insText;
1561     while (True) {
1562     	lineEnd = BufEndOfLine(buf, lineStart);
1563     	line = BufGetRange(buf, lineStart, lineEnd);
1564     	insLine = copyLine(insPtr, &len);
1565     	insPtr += len;
1566     	insertColInLine(line, insLine, column, insWidth, buf->tabDist,
1567     		buf->useTabs, buf->nullSubsChar, outPtr, &len, &endOffset);
1568     	NEditFree(line);
1569     	NEditFree(insLine);
1570 #if 0   /* Earlier comments claimed that trailing whitespace could multiply on
1571         the ends of lines, but insertColInLine looks like it should never
1572         add space unnecessarily, and this trimming interfered with
1573         paragraph filling, so lets see if it works without it. MWE */
1574         {
1575             char *c;
1576     	    for (c=outPtr+len-1; c>outPtr && (*c == ' ' || *c == '\t'); c--)
1577                 len--;
1578         }
1579 #endif
1580 	outPtr += len;
1581 	*outPtr++ = '\n';
1582     	lineStart = lineEnd < buf->length ? lineEnd + 1 : buf->length;
1583     	if (*insPtr == '\0')
1584     	    break;
1585     	insPtr++;
1586     }
1587     if (outPtr != outStr)
1588     	outPtr--; /* trim back off extra newline */
1589     *outPtr = '\0';
1590 
1591     /* replace the text between start and end with the new stuff */
1592     delete(buf, start, end);
1593     insert(buf, start, outStr);
1594     *nInserted = outPtr - outStr;
1595     *nDeleted = end - start;
1596     *endPos = start + (outPtr - outStr) - len + endOffset;
1597     NEditFree(outStr);
1598 }
1599 
1600 /*
1601 ** Delete a rectangle of text without calling the modify callbacks.  Returns
1602 ** the number of characters replacing those between start and end.  Note that
1603 ** in some pathological cases, deleting can actually increase the size of
1604 ** the buffer because of tab expansions.  "endPos" returns the buffer position
1605 ** of the point in the last line where the text was removed (as a hint for
1606 ** routines which need to position the cursor after a delete operation)
1607 */
deleteRect(textBuffer * buf,int start,int end,int rectStart,int rectEnd,int * replaceLen,int * endPos)1608 static void deleteRect(textBuffer *buf, int start, int end, int rectStart,
1609 	int rectEnd, int *replaceLen, int *endPos)
1610 {
1611     int nLines, lineStart, lineEnd, len, endOffset;
1612     char *outStr, *outPtr, *line, *text, *expText;
1613 
1614     /* allocate a buffer for the replacement string large enough to hold
1615        possibly expanded tabs as well as an additional  MAX_EXP_CHAR_LEN * 2
1616        characters per line for padding where tabs and control characters cross
1617        the edges of the selection */
1618     start = BufStartOfLine(buf, start);
1619     end = BufEndOfLine(buf, end);
1620     nLines = BufCountLines(buf, start, end) + 1;
1621     text = BufGetRange(buf, start, end);
1622     expText = expandTabs(text, 0, buf->tabDist, buf->nullSubsChar, &len);
1623     NEditFree(text);
1624     NEditFree(expText);
1625     outStr = (char*)NEditMalloc(len + nLines * MAX_EXP_CHAR_LEN * 2 + 1);
1626 
1627     /* loop over all lines in the buffer between start and end removing
1628        the text between rectStart and rectEnd and padding appropriately */
1629     lineStart = start;
1630     outPtr = outStr;
1631     while (lineStart <= buf->length && lineStart <= end) {
1632     	lineEnd = BufEndOfLine(buf, lineStart);
1633     	line = BufGetRange(buf, lineStart, lineEnd);
1634     	deleteRectFromLine(line, rectStart, rectEnd, buf->tabDist,
1635     		buf->useTabs, buf->nullSubsChar, outPtr, &len, &endOffset);
1636     	NEditFree(line);
1637 	outPtr += len;
1638 	*outPtr++ = '\n';
1639     	lineStart = lineEnd + 1;
1640     }
1641     if (outPtr != outStr)
1642     	outPtr--; /* trim back off extra newline */
1643     *outPtr = '\0';
1644 
1645     /* replace the text between start and end with the newly created string */
1646     delete(buf, start, end);
1647     insert(buf, start, outStr);
1648     *replaceLen = outPtr - outStr;
1649     *endPos = start + (outPtr - outStr) - len + endOffset;
1650     NEditFree(outStr);
1651 }
1652 
1653 /*
1654 ** Overlay a rectangular area of text without calling the modify callbacks.
1655 ** "nDeleted" and "nInserted" return the number of characters deleted and
1656 ** inserted beginning at the start of the line containing "startPos".
1657 ** "endPos" returns buffer position of the lower left edge of the inserted
1658 ** column (as a hint for routines which need to set a cursor position).
1659 */
overlayRect(textBuffer * buf,int startPos,int rectStart,int rectEnd,const char * insText,int * nDeleted,int * nInserted,int * endPos)1660 static void overlayRect(textBuffer *buf, int startPos, int rectStart,
1661     	int rectEnd, const char *insText,
1662 	int *nDeleted, int *nInserted, int *endPos)
1663 {
1664     int nLines, start, end, lineStart, lineEnd;
1665     int expInsLen, len, endOffset;
1666     char *c, *outStr, *outPtr, *line, *expText, *insLine;
1667     const char *insPtr;
1668 
1669     /* Allocate a buffer for the replacement string large enough to hold
1670        possibly expanded tabs in the inserted text, as well as per line: 1)
1671        an additional 2*MAX_EXP_CHAR_LEN characters for padding where tabs
1672        and control characters cross the column of the selection, 2) up to
1673        "column" additional spaces per line for padding out to the position
1674        of "column", 3) padding up to the width of the inserted text if that
1675        must be padded to align the text beyond the inserted column.  (Space
1676        for additional newlines if the inserted text extends beyond the end
1677        of the buffer is counted with the length of insText) */
1678     start = BufStartOfLine(buf, startPos);
1679     nLines = countLines(insText) + 1;
1680     end = BufEndOfLine(buf, BufCountForwardNLines(buf, start, nLines-1));
1681     expText = expandTabs(insText, 0, buf->tabDist, buf->nullSubsChar,
1682 	    &expInsLen);
1683     NEditFree(expText);
1684     outStr = (char*)NEditMalloc(end-start + expInsLen +
1685     	    nLines * (rectEnd + MAX_EXP_CHAR_LEN) + 1);
1686 
1687     /* Loop over all lines in the buffer between start and end overlaying the
1688        text between rectStart and rectEnd and padding appropriately.  Trim
1689        trailing space from line (whitespace at the ends of lines otherwise
1690        tends to multiply, since additional padding is added to maintain it */
1691     outPtr = outStr;
1692     lineStart = start;
1693     insPtr = insText;
1694     while (True) {
1695     	lineEnd = BufEndOfLine(buf, lineStart);
1696     	line = BufGetRange(buf, lineStart, lineEnd);
1697     	insLine = copyLine(insPtr, &len);
1698     	insPtr += len;
1699     	overlayRectInLine(line, insLine, rectStart, rectEnd, buf->tabDist,
1700 		buf->useTabs, buf->nullSubsChar, outPtr, &len, &endOffset);
1701     	NEditFree(line);
1702     	NEditFree(insLine);
1703     	for (c=outPtr+len-1; c>outPtr && (*c == ' ' || *c == '\t'); c--)
1704     	    len--;
1705 	outPtr += len;
1706 	*outPtr++ = '\n';
1707     	lineStart = lineEnd < buf->length ? lineEnd + 1 : buf->length;
1708     	if (*insPtr == '\0')
1709     	    break;
1710     	insPtr++;
1711     }
1712     if (outPtr != outStr)
1713     	outPtr--; /* trim back off extra newline */
1714     *outPtr = '\0';
1715 
1716     /* replace the text between start and end with the new stuff */
1717     delete(buf, start, end);
1718     insert(buf, start, outStr);
1719     *nInserted = outPtr - outStr;
1720     *nDeleted = end - start;
1721     *endPos = start + (outPtr - outStr) - len + endOffset;
1722     NEditFree(outStr);
1723 }
1724 
1725 /*
1726 ** Insert characters from single-line string "insLine" in single-line string
1727 ** "line" at "column", leaving "insWidth" space before continuing line.
1728 ** "outLen" returns the number of characters written to "outStr", "endOffset"
1729 ** returns the number of characters from the beginning of the string to
1730 ** the right edge of the inserted text (as a hint for routines which need
1731 ** to position the cursor).
1732 */
insertColInLine(const char * line,const char * insLine,int column,int insWidth,int tabDist,int useTabs,char nullSubsChar,char * outStr,int * outLen,int * endOffset)1733 static void insertColInLine(const char *line, const char *insLine,
1734         int column, int insWidth, int tabDist, int useTabs, char nullSubsChar,
1735 	char *outStr, int *outLen, int *endOffset)
1736 {
1737     char *c, *outPtr, *retabbedStr;
1738     const char *linePtr;
1739     int indent, toIndent, len, postColIndent;
1740 
1741     /* copy the line up to "column" */
1742     outPtr = outStr;
1743     indent = 0;
1744     for (linePtr=line; *linePtr!='\0'; linePtr++) {
1745 	len = BufCharWidth(*linePtr, indent, tabDist, nullSubsChar);
1746 	if (indent + len > column)
1747     	    break;
1748     	indent += len;
1749 	*outPtr++ = *linePtr;
1750     }
1751 
1752     /* If "column" falls in the middle of a character, and the character is a
1753        tab, leave it off and leave the indent short and it will get padded
1754        later.  If it's a control character, insert it and adjust indent
1755        accordingly. */
1756     if (indent < column && *linePtr != '\0') {
1757     	postColIndent = indent + len;
1758     	if (*linePtr == '\t')
1759     	    linePtr++;
1760     	else {
1761     	    *outPtr++ = *linePtr++;
1762     	    indent += len;
1763     	}
1764     } else
1765     	postColIndent = indent;
1766 
1767     /* If there's no text after the column and no text to insert, that's all */
1768     if (*insLine == '\0' && *linePtr == '\0') {
1769     	*outLen = *endOffset = outPtr - outStr;
1770     	return;
1771     }
1772 
1773     /* pad out to column if text is too short */
1774     if (indent < column) {
1775 	addPadding(outPtr, indent, column, tabDist, useTabs, nullSubsChar,&len);
1776 	outPtr += len;
1777 	indent = column;
1778     }
1779 
1780     /* Copy the text from "insLine" (if any), recalculating the tabs as if
1781        the inserted string began at column 0 to its new column destination */
1782     if (*insLine != '\0') {
1783 	retabbedStr = realignTabs(insLine, 0, indent, tabDist, useTabs,
1784 		nullSubsChar, &len);
1785 	for (c=retabbedStr; *c!='\0'; c++) {
1786     	    *outPtr++ = *c;
1787     	    len = BufCharWidth(*c, indent, tabDist, nullSubsChar);
1788     	    indent += len;
1789 	}
1790 	NEditFree(retabbedStr);
1791     }
1792 
1793     /* If the original line did not extend past "column", that's all */
1794     if (*linePtr == '\0') {
1795     	*outLen = *endOffset = outPtr - outStr;
1796     	return;
1797     }
1798 
1799     /* Pad out to column + width of inserted text + (additional original
1800        offset due to non-breaking character at column) */
1801     toIndent = column + insWidth + postColIndent-column;
1802     addPadding(outPtr, indent, toIndent, tabDist, useTabs, nullSubsChar, &len);
1803     outPtr += len;
1804     indent = toIndent;
1805 
1806     /* realign tabs for text beyond "column" and write it out */
1807     retabbedStr = realignTabs(linePtr, postColIndent, indent, tabDist,
1808     	useTabs, nullSubsChar, &len);
1809     strcpy(outPtr, retabbedStr);
1810     NEditFree(retabbedStr);
1811     *endOffset = outPtr - outStr;
1812     *outLen = (outPtr - outStr) + len;
1813 }
1814 
1815 /*
1816 ** Remove characters in single-line string "line" between displayed positions
1817 ** "rectStart" and "rectEnd", and write the result to "outStr", which is
1818 ** assumed to be large enough to hold the returned string.  Note that in
1819 ** certain cases, it is possible for the string to get longer due to
1820 ** expansion of tabs.  "endOffset" returns the number of characters from
1821 ** the beginning of the string to the point where the characters were
1822 ** deleted (as a hint for routines which need to position the cursor).
1823 */
deleteRectFromLine(const char * line,int rectStart,int rectEnd,int tabDist,int useTabs,char nullSubsChar,char * outStr,int * outLen,int * endOffset)1824 static void deleteRectFromLine(const char *line, int rectStart, int rectEnd,
1825 	int tabDist, int useTabs, char nullSubsChar, char *outStr, int *outLen,
1826 	int *endOffset)
1827 {
1828     int indent, preRectIndent, postRectIndent, len;
1829     const char *c;
1830     char *outPtr;
1831     char *retabbedStr;
1832 
1833     /* copy the line up to rectStart */
1834     outPtr = outStr;
1835     indent = 0;
1836     for (c=line; *c!='\0'; c++) {
1837 	if (indent > rectStart)
1838 	    break;
1839 	len = BufCharWidth(*c, indent, tabDist, nullSubsChar);
1840 	if (indent + len > rectStart && (indent == rectStart || *c == '\t'))
1841     	    break;
1842     	indent += len;
1843 	*outPtr++ = *c;
1844     }
1845     preRectIndent = indent;
1846 
1847     /* skip the characters between rectStart and rectEnd */
1848     for(; *c!='\0' && indent<rectEnd; c++)
1849 	indent += BufCharWidth(*c, indent, tabDist, nullSubsChar);
1850     postRectIndent = indent;
1851 
1852     /* If the line ended before rectEnd, there's nothing more to do */
1853     if (*c == '\0') {
1854     	*outPtr = '\0';
1855     	*outLen = *endOffset = outPtr - outStr;
1856     	return;
1857     }
1858 
1859     /* fill in any space left by removed tabs or control characters
1860        which straddled the boundaries */
1861     indent = max(rectStart + postRectIndent-rectEnd, preRectIndent);
1862     addPadding(outPtr, preRectIndent, indent, tabDist, useTabs, nullSubsChar,
1863 	    &len);
1864     outPtr += len;
1865 
1866     /* Copy the rest of the line.  If the indentation has changed, preserve
1867        the position of non-whitespace characters by converting tabs to
1868        spaces, then back to tabs with the correct offset */
1869     retabbedStr = realignTabs(c, postRectIndent, indent, tabDist, useTabs,
1870     	    nullSubsChar, &len);
1871     strcpy(outPtr, retabbedStr);
1872     NEditFree(retabbedStr);
1873     *endOffset = outPtr - outStr;
1874     *outLen = (outPtr - outStr) + len;
1875 }
1876 
1877 /*
1878 ** Overlay characters from single-line string "insLine" on single-line string
1879 ** "line" between displayed character offsets "rectStart" and "rectEnd".
1880 ** "outLen" returns the number of characters written to "outStr", "endOffset"
1881 ** returns the number of characters from the beginning of the string to
1882 ** the right edge of the inserted text (as a hint for routines which need
1883 ** to position the cursor).
1884 **
1885 ** This code does not handle control characters very well, but oh well.
1886 */
overlayRectInLine(const char * line,const char * insLine,int rectStart,int rectEnd,int tabDist,int useTabs,char nullSubsChar,char * outStr,int * outLen,int * endOffset)1887 static void overlayRectInLine(const char *line, const char *insLine,
1888         int rectStart, int rectEnd, int tabDist, int useTabs,
1889 	char nullSubsChar, char *outStr, int *outLen, int *endOffset)
1890 {
1891     char *c, *outPtr, *retabbedStr;
1892     const char *linePtr;
1893     int inIndent, outIndent, len, postRectIndent;
1894 
1895     /* copy the line up to "rectStart" or just before the char that
1896         contains it*/
1897     outPtr = outStr;
1898     inIndent = outIndent = 0;
1899     for (linePtr=line; *linePtr!='\0'; linePtr++) {
1900 	len = BufCharWidth(*linePtr, inIndent, tabDist, nullSubsChar);
1901 	if (inIndent + len > rectStart)
1902     	    break;
1903     	inIndent += len;
1904     	outIndent += len;
1905 	*outPtr++ = *linePtr;
1906     }
1907 
1908     /* If "rectStart" falls in the middle of a character, and the character
1909        is a tab, leave it off and leave the outIndent short and it will get
1910        padded later.  If it's a control character, insert it and adjust
1911        outIndent accordingly. */
1912     if (inIndent < rectStart && *linePtr != '\0') {
1913     	if (*linePtr == '\t') {
1914             /* Skip past the tab */
1915     	    linePtr++;
1916     	    inIndent += len;
1917     	} else {
1918     	    *outPtr++ = *linePtr++;
1919     	    outIndent += len;
1920     	    inIndent += len;
1921     	}
1922     }
1923 
1924     /* skip the characters between rectStart and rectEnd */
1925     for(; *linePtr!='\0' && inIndent < rectEnd; linePtr++)
1926 	inIndent += BufCharWidth(*linePtr, inIndent, tabDist, nullSubsChar);
1927     postRectIndent = inIndent;
1928 
1929     /* After this inIndent is dead and linePtr is supposed to point at the
1930         character just past the last character that will be altered by
1931         the overlay, whether that's a \t or otherwise.  postRectIndent is
1932         the position at which that character is supposed to appear */
1933 
1934     /* If there's no text after rectStart and no text to insert, that's all */
1935     if (*insLine == '\0' && *linePtr == '\0') {
1936     	*outLen = *endOffset = outPtr - outStr;
1937     	return;
1938     }
1939 
1940     /* pad out to rectStart if text is too short */
1941     if (outIndent < rectStart) {
1942 	addPadding(outPtr, outIndent, rectStart, tabDist, useTabs, nullSubsChar,
1943 		&len);
1944 	outPtr += len;
1945     }
1946     outIndent = rectStart;
1947 
1948     /* Copy the text from "insLine" (if any), recalculating the tabs as if
1949        the inserted string began at column 0 to its new column destination */
1950     if (*insLine != '\0') {
1951 	retabbedStr = realignTabs(insLine, 0, rectStart, tabDist, useTabs,
1952 		nullSubsChar, &len);
1953 	for (c=retabbedStr; *c!='\0'; c++) {
1954     	    *outPtr++ = *c;
1955     	    len = BufCharWidth(*c, outIndent, tabDist, nullSubsChar);
1956     	    outIndent += len;
1957 	}
1958 	NEditFree(retabbedStr);
1959     }
1960 
1961     /* If the original line did not extend past "rectStart", that's all */
1962     if (*linePtr == '\0') {
1963     	*outLen = *endOffset = outPtr - outStr;
1964     	return;
1965     }
1966 
1967     /* Pad out to rectEnd + (additional original offset
1968        due to non-breaking character at right boundary) */
1969     addPadding(outPtr, outIndent, postRectIndent, tabDist, useTabs,
1970 	    nullSubsChar, &len);
1971     outPtr += len;
1972     outIndent = postRectIndent;
1973 
1974     /* copy the text beyond "rectEnd" */
1975     strcpy(outPtr, linePtr);
1976     *endOffset = outPtr - outStr;
1977     *outLen = (outPtr - outStr) + strlen(linePtr);
1978 }
1979 
setSelection(selection * sel,int start,int end)1980 static void setSelection(selection *sel, int start, int end)
1981 {
1982     sel->selected = start != end;
1983     sel->zeroWidth = (start == end) ? 1 : 0;
1984     sel->rectangular = False;
1985     sel->start = min(start, end);
1986     sel->end = max(start, end);
1987 }
1988 
setRectSelect(selection * sel,int start,int end,int rectStart,int rectEnd)1989 static void setRectSelect(selection *sel, int start, int end,
1990 	int rectStart, int rectEnd)
1991 {
1992     sel->selected = rectStart < rectEnd;
1993     sel->zeroWidth = (rectStart == rectEnd) ? 1 : 0;
1994     sel->rectangular = True;
1995     sel->start = start;
1996     sel->end = end;
1997     sel->rectStart = rectStart;
1998     sel->rectEnd = rectEnd;
1999 }
2000 
getSelectionPos(selection * sel,int * start,int * end,int * isRect,int * rectStart,int * rectEnd)2001 static int getSelectionPos(selection *sel, int *start, int *end,
2002         int *isRect, int *rectStart, int *rectEnd)
2003 {
2004     /* Always fill in the parameters (zero-width can be requested too). */
2005     *isRect = sel->rectangular;
2006     *start = sel->start;
2007     *end = sel->end;
2008     if (sel->rectangular) {
2009 	*rectStart = sel->rectStart;
2010 	*rectEnd = sel->rectEnd;
2011     }
2012     return sel->selected;
2013 }
2014 
getSelectionText(textBuffer * buf,selection * sel)2015 static char *getSelectionText(textBuffer *buf, selection *sel)
2016 {
2017     int start, end, isRect, rectStart, rectEnd;
2018     char *text;
2019 
2020     /* If there's no selection, return an allocated empty string */
2021     if (!getSelectionPos(sel, &start, &end, &isRect, &rectStart, &rectEnd)) {
2022     	text = (char*)NEditMalloc(1);
2023     	*text = '\0';
2024     	return text;
2025     }
2026 
2027     /* If the selection is not rectangular, return the selected range */
2028     if (isRect)
2029     	return BufGetTextInRect(buf, start, end, rectStart, rectEnd);
2030     else
2031     	return BufGetRange(buf, start, end);
2032 }
2033 
removeSelected(textBuffer * buf,selection * sel)2034 static void removeSelected(textBuffer *buf, selection *sel)
2035 {
2036     int start, end;
2037     int isRect, rectStart, rectEnd;
2038 
2039     if (!getSelectionPos(sel, &start, &end, &isRect, &rectStart, &rectEnd))
2040     	return;
2041     if (isRect)
2042         BufRemoveRect(buf, start, end, rectStart, rectEnd);
2043     else
2044         BufRemove(buf, start, end);
2045 }
2046 
replaceSelected(textBuffer * buf,selection * sel,const char * text)2047 static void replaceSelected(textBuffer *buf, selection *sel, const char *text)
2048 {
2049     int start, end, isRect, rectStart, rectEnd;
2050     selection oldSelection = *sel;
2051 
2052     /* If there's no selection, return */
2053     if (!getSelectionPos(sel, &start, &end, &isRect, &rectStart, &rectEnd))
2054     	return;
2055 
2056     /* Do the appropriate type of replace */
2057     if (isRect)
2058     	BufReplaceRect(buf, start, end, rectStart, rectEnd, text);
2059     else
2060     	BufReplace(buf, start, end, text);
2061 
2062     /* Unselect (happens automatically in BufReplace, but BufReplaceRect
2063        can't detect when the contents of a selection goes away) */
2064     sel->selected = False;
2065     redisplaySelection(buf, &oldSelection, sel);
2066 }
2067 
addPadding(char * string,int startIndent,int toIndent,int tabDist,int useTabs,char nullSubsChar,int * charsAdded)2068 static void addPadding(char *string, int startIndent, int toIndent,
2069 	int tabDist, int useTabs, char nullSubsChar, int *charsAdded)
2070 {
2071     char *outPtr;
2072     int len, indent;
2073 
2074     indent = startIndent;
2075     outPtr = string;
2076     if (useTabs) {
2077 	while (indent < toIndent) {
2078 	    len = BufCharWidth('\t', indent, tabDist, nullSubsChar);
2079 	    if (len > 1 && indent + len <= toIndent) {
2080 		*outPtr++ = '\t';
2081 		indent += len;
2082 	    } else {
2083 		*outPtr++ = ' ';
2084 		indent++;
2085 	    }
2086 	}
2087     } else {
2088     	while (indent < toIndent) {
2089 	    *outPtr++ = ' ';
2090 	    indent++;
2091 	}
2092     }
2093     *charsAdded = outPtr - string;
2094 }
2095 
2096 /*
2097 ** Call the stored modify callback procedure(s) for this buffer to update the
2098 ** changed area(s) on the screen and any other listeners.
2099 */
callModifyCBs(textBuffer * buf,int pos,int nDeleted,int nInserted,int nRestyled,const char * deletedText)2100 static void callModifyCBs(textBuffer *buf, int pos, int nDeleted,
2101 	int nInserted, int nRestyled, const char *deletedText)
2102 {
2103     int i;
2104 
2105     for (i=0; i<buf->nModifyProcs; i++)
2106     	(*buf->modifyProcs[i])(pos, nInserted, nDeleted, nRestyled,
2107     		deletedText, buf->cbArgs[i]);
2108 }
2109 
2110 /*
2111 ** Call the stored pre-delete callback procedure(s) for this buffer to update
2112 ** the changed area(s) on the screen and any other listeners.
2113 */
callPreDeleteCBs(textBuffer * buf,int pos,int nDeleted)2114 static void callPreDeleteCBs(textBuffer *buf, int pos, int nDeleted)
2115 {
2116     int i;
2117 
2118     for (i=0; i<buf->nPreDeleteProcs; i++)
2119     	(*buf->preDeleteProcs[i])(pos, nDeleted, buf->preDeleteCbArgs[i]);
2120 }
2121 
2122 /*
2123 ** Call the stored redisplay procedure(s) for this buffer to update the
2124 ** screen for a change in a selection.
2125 */
redisplaySelection(textBuffer * buf,selection * oldSelection,selection * newSelection)2126 static void redisplaySelection(textBuffer *buf, selection *oldSelection,
2127 	selection *newSelection)
2128 {
2129     int oldStart, oldEnd, newStart, newEnd, ch1Start, ch1End, ch2Start, ch2End;
2130 
2131     /* If either selection is rectangular, add an additional character to
2132        the end of the selection to request the redraw routines to wipe out
2133        the parts of the selection beyond the end of the line */
2134     oldStart = oldSelection->start;
2135     newStart = newSelection->start;
2136     oldEnd = oldSelection->end;
2137     newEnd = newSelection->end;
2138     if (oldSelection->rectangular)
2139     	oldEnd++;
2140     if (newSelection->rectangular)
2141     	newEnd++;
2142 
2143     /* If the old or new selection is unselected, just redisplay the
2144        single area that is (was) selected and return */
2145     if (!oldSelection->selected && !newSelection->selected)
2146     	return;
2147     if (!oldSelection->selected) {
2148     	callModifyCBs(buf, newStart, 0, 0, newEnd-newStart, NULL);
2149     	return;
2150     }
2151     if (!newSelection->selected) {
2152     	callModifyCBs(buf, oldStart, 0, 0, oldEnd-oldStart, NULL);
2153     	return;
2154     }
2155 
2156     /* If the selection changed from normal to rectangular or visa versa, or
2157        if a rectangular selection changed boundaries, redisplay everything */
2158     if ((oldSelection->rectangular && !newSelection->rectangular) ||
2159     	    (!oldSelection->rectangular && newSelection->rectangular) ||
2160     	    (oldSelection->rectangular && (
2161     	    	(oldSelection->rectStart != newSelection->rectStart) ||
2162     	    	(oldSelection->rectEnd != newSelection->rectEnd)))) {
2163     	callModifyCBs(buf, min(oldStart, newStart), 0, 0,
2164     		max(oldEnd, newEnd) - min(oldStart, newStart), NULL);
2165     	return;
2166     }
2167 
2168     /* If the selections are non-contiguous, do two separate updates
2169        and return */
2170     if (oldEnd < newStart || newEnd < oldStart) {
2171 	callModifyCBs(buf, oldStart, 0, 0, oldEnd-oldStart, NULL);
2172 	callModifyCBs(buf, newStart, 0, 0, newEnd-newStart, NULL);
2173 	return;
2174     }
2175 
2176     /* Otherwise, separate into 3 separate regions: ch1, and ch2 (the two
2177        changed areas), and the unchanged area of their intersection,
2178        and update only the changed area(s) */
2179     ch1Start = min(oldStart, newStart);
2180     ch2End = max(oldEnd, newEnd);
2181     ch1End = max(oldStart, newStart);
2182     ch2Start = min(oldEnd, newEnd);
2183     if (ch1Start != ch1End)
2184     	callModifyCBs(buf, ch1Start, 0, 0, ch1End-ch1Start, NULL);
2185     if (ch2Start != ch2End)
2186     	callModifyCBs(buf, ch2Start, 0, 0, ch2End-ch2Start, NULL);
2187 }
2188 
moveGap(textBuffer * buf,int pos)2189 static void moveGap(textBuffer *buf, int pos)
2190 {
2191     int gapLen = buf->gapEnd - buf->gapStart;
2192 
2193     if (pos > buf->gapStart)
2194     	memmove(&buf->buf[buf->gapStart], &buf->buf[buf->gapEnd],
2195 		pos - buf->gapStart);
2196     else
2197     	memmove(&buf->buf[pos + gapLen], &buf->buf[pos], buf->gapStart - pos);
2198     buf->gapEnd += pos - buf->gapStart;
2199     buf->gapStart += pos - buf->gapStart;
2200 }
2201 
2202 /*
2203 ** reallocate the text storage in "buf" to have a gap starting at "newGapStart"
2204 ** and a gap size of "newGapLen", preserving the buffer's current contents.
2205 */
reallocateBuf(textBuffer * buf,int newGapStart,int newGapLen)2206 static void reallocateBuf(textBuffer *buf, int newGapStart, int newGapLen)
2207 {
2208     char *newBuf;
2209     int newGapEnd;
2210 
2211     newBuf = (char*)NEditMalloc(buf->length + newGapLen + 1);
2212     newBuf[buf->length + PREFERRED_GAP_SIZE] = '\0';
2213     newGapEnd = newGapStart + newGapLen;
2214     if (newGapStart <= buf->gapStart) {
2215 	memcpy(newBuf, buf->buf, newGapStart);
2216 	memcpy(&newBuf[newGapEnd], &buf->buf[newGapStart],
2217 		buf->gapStart - newGapStart);
2218 	memcpy(&newBuf[newGapEnd + buf->gapStart - newGapStart],
2219 		&buf->buf[buf->gapEnd], buf->length - buf->gapStart);
2220     } else { /* newGapStart > buf->gapStart */
2221 	memcpy(newBuf, buf->buf, buf->gapStart);
2222 	memcpy(&newBuf[buf->gapStart], &buf->buf[buf->gapEnd],
2223 		newGapStart - buf->gapStart);
2224 	memcpy(&newBuf[newGapEnd],
2225 		&buf->buf[buf->gapEnd + newGapStart - buf->gapStart],
2226 		buf->length - newGapStart);
2227     }
2228     NEditFree(buf->buf);
2229     buf->buf = newBuf;
2230     buf->gapStart = newGapStart;
2231     buf->gapEnd = newGapEnd;
2232 #ifdef PURIFY
2233     {int i; for (i=buf->gapStart; i<buf->gapEnd; i++) buf->buf[i] = '.';}
2234 #endif
2235 }
2236 
2237 /*
2238 ** Update all of the selections in "buf" for changes in the buffer's text
2239 */
updateSelections(textBuffer * buf,int pos,int nDeleted,int nInserted)2240 static void updateSelections(textBuffer *buf, int pos, int nDeleted,
2241 	int nInserted)
2242 {
2243     updateSelection(&buf->primary, pos, nDeleted, nInserted);
2244     updateSelection(&buf->secondary, pos, nDeleted, nInserted);
2245     updateSelection(&buf->highlight, pos, nDeleted, nInserted);
2246 }
2247 
2248 /*
2249 ** Update an individual selection for changes in the corresponding text
2250 */
updateSelection(selection * sel,int pos,int nDeleted,int nInserted)2251 static void updateSelection(selection *sel, int pos, int nDeleted,
2252 	int nInserted)
2253 {
2254     if ((!sel->selected && !sel->zeroWidth) || pos > sel->end)
2255     	return;
2256     if (pos+nDeleted <= sel->start) {
2257     	sel->start += nInserted - nDeleted;
2258 	sel->end += nInserted - nDeleted;
2259     } else if (pos <= sel->start && pos+nDeleted >= sel->end) {
2260     	sel->start = pos;
2261     	sel->end = pos;
2262     	sel->selected = False;
2263         sel->zeroWidth = False;
2264     } else if (pos <= sel->start && pos+nDeleted < sel->end) {
2265     	sel->start = pos;
2266     	sel->end = nInserted + sel->end - nDeleted;
2267     } else if (pos < sel->end) {
2268     	sel->end += nInserted - nDeleted;
2269 	if (sel->end <= sel->start)
2270 	    sel->selected = False;
2271     }
2272 }
2273 
2274 /*
2275 ** Search forwards in buffer "buf" for character "searchChar", starting
2276 ** with the character "startPos", and returning the result in "foundPos"
2277 ** returns True if found, False if not.  (The difference between this and
2278 ** BufSearchForward is that it's optimized for single characters.  The
2279 ** overall performance of the text widget is dependent on its ability to
2280 ** count lines quickly, hence searching for a single character: newline)
2281 */
searchForward(textBuffer * buf,int startPos,char searchChar,int * foundPos)2282 static int searchForward(textBuffer *buf, int startPos, char searchChar,
2283 	int *foundPos)
2284 {
2285     int pos, gapLen = buf->gapEnd - buf->gapStart;
2286 
2287     pos = startPos;
2288     while (pos < buf->gapStart) {
2289         if (buf->buf[pos] == searchChar) {
2290             *foundPos = pos;
2291             return True;
2292         }
2293         pos++;
2294     }
2295     while (pos < buf->length) {
2296     	if (buf->buf[pos + gapLen] == searchChar) {
2297             *foundPos = pos;
2298             return True;
2299         }
2300         pos++;
2301     }
2302     *foundPos = buf->length;
2303     return False;
2304 }
2305 
2306 /*
2307 ** Search backwards in buffer "buf" for character "searchChar", starting
2308 ** with the character BEFORE "startPos", returning the result in "foundPos"
2309 ** returns True if found, False if not.  (The difference between this and
2310 ** BufSearchBackward is that it's optimized for single characters.  The
2311 ** overall performance of the text widget is dependent on its ability to
2312 ** count lines quickly, hence searching for a single character: newline)
2313 */
searchBackward(textBuffer * buf,int startPos,char searchChar,int * foundPos)2314 static int searchBackward(textBuffer *buf, int startPos, char searchChar,
2315 	int *foundPos)
2316 {
2317     int pos, gapLen = buf->gapEnd - buf->gapStart;
2318 
2319     if (startPos == 0) {
2320     	*foundPos = 0;
2321     	return False;
2322     }
2323     pos = startPos == 0 ? 0 : startPos - 1;
2324     while (pos >= buf->gapStart) {
2325     	if (buf->buf[pos + gapLen] == searchChar) {
2326             *foundPos = pos;
2327             return True;
2328         }
2329         pos--;
2330     }
2331     while (pos >= 0) {
2332         if (buf->buf[pos] == searchChar) {
2333             *foundPos = pos;
2334             return True;
2335         }
2336         pos--;
2337     }
2338     *foundPos = 0;
2339     return False;
2340 }
2341 
2342 /*
2343 ** Copy from "text" to end up to but not including newline (or end of "text")
2344 ** and return the copy as the function value, and the length of the line in
2345 ** "lineLen"
2346 */
copyLine(const char * text,int * lineLen)2347 static char *copyLine(const char *text, int *lineLen)
2348 {
2349     int len = 0;
2350     const char *c;
2351     char *outStr;
2352 
2353     for (c=text; *c!='\0' && *c!='\n'; c++)
2354     	len++;
2355     outStr = (char*)NEditMalloc(len + 1);
2356     strncpy(outStr, text, len);
2357     outStr[len] = '\0';
2358     *lineLen = len;
2359     return outStr;
2360 }
2361 
2362 /*
2363 ** Count the number of newlines in a null-terminated text string;
2364 */
countLines(const char * string)2365 static int countLines(const char *string)
2366 {
2367     const char *c;
2368     int lineCount = 0;
2369 
2370     for (c=string; *c!='\0'; c++)
2371     	if (*c == '\n') lineCount++;
2372     return lineCount;
2373 }
2374 
2375 /*
2376 ** Measure the width in displayed characters of string "text"
2377 */
textWidth(const char * text,int tabDist,char nullSubsChar)2378 static int textWidth(const char *text, int tabDist, char nullSubsChar)
2379 {
2380     int width = 0, maxWidth = 0;
2381     const char *c;
2382 
2383     for (c=text; *c!='\0'; c++) {
2384     	if (*c == '\n') {
2385     	    if (width > maxWidth)
2386     	    	maxWidth = width;
2387     	    width = 0;
2388     	} else
2389     	    width += BufCharWidth(*c, width, tabDist, nullSubsChar);
2390     }
2391     if (width > maxWidth)
2392     	return width;
2393     return maxWidth;
2394 }
2395 
2396 /*
2397 ** Find the first and last character position in a line withing a rectangular
2398 ** selection (for copying).  Includes tabs which cross rectStart, but not
2399 ** control characters which do so.  Leaves off tabs which cross rectEnd.
2400 **
2401 ** Technically, the calling routine should convert tab characters which
2402 ** cross the right boundary of the selection to spaces which line up with
2403 ** the edge of the selection.  Unfortunately, the additional memory
2404 ** management required in the parent routine to allow for the changes
2405 ** in string size is not worth all the extra work just for a couple of
2406 ** shifted characters, so if a tab protrudes, just lop it off and hope
2407 ** that there are other characters in the selection to establish the right
2408 ** margin for subsequent columnar pastes of this data.
2409 */
findRectSelBoundariesForCopy(textBuffer * buf,int lineStartPos,int rectStart,int rectEnd,int * selStart,int * selEnd)2410 static void findRectSelBoundariesForCopy(textBuffer *buf, int lineStartPos,
2411 	int rectStart, int rectEnd, int *selStart, int *selEnd)
2412 {
2413     int pos, width, indent = 0;
2414     char c;
2415 
2416     /* find the start of the selection */
2417     for (pos=lineStartPos; pos<buf->length; pos++) {
2418     	c = BufGetCharacter(buf, pos);
2419     	if (c == '\n')
2420     	    break;
2421     	width = BufCharWidth(c, indent, buf->tabDist, buf->nullSubsChar);
2422     	if (indent + width > rectStart) {
2423     	    if (indent != rectStart && c != '\t') {
2424     	    	pos++;
2425     	    	indent += width;
2426     	    }
2427     	    break;
2428     	}
2429     	indent += width;
2430     }
2431     *selStart = pos;
2432 
2433     /* find the end */
2434     for (; pos<buf->length; pos++) {
2435     	c = BufGetCharacter(buf, pos);
2436     	if (c == '\n')
2437     	    break;
2438     	width = BufCharWidth(c, indent, buf->tabDist, buf->nullSubsChar);
2439     	indent += width;
2440     	if (indent > rectEnd) {
2441     	    if (indent-width != rectEnd && c != '\t')
2442     	    	pos++;
2443     	    break;
2444     	}
2445     }
2446     *selEnd = pos;
2447 }
2448 
2449 /*
2450 ** Adjust the space and tab characters from string "text" so that non-white
2451 ** characters remain stationary when the text is shifted from starting at
2452 ** "origIndent" to starting at "newIndent".  Returns an allocated string
2453 ** which must be freed by the caller with NEditFree.
2454 */
realignTabs(const char * text,int origIndent,int newIndent,int tabDist,int useTabs,char nullSubsChar,int * newLength)2455 static char *realignTabs(const char *text, int origIndent, int newIndent,
2456 	int tabDist, int useTabs, char nullSubsChar, int *newLength)
2457 {
2458     char *expStr, *outStr;
2459     int len;
2460 
2461     /* If the tabs settings are the same, retain original tabs */
2462     if (origIndent % tabDist == newIndent %tabDist) {
2463     	len = strlen(text);
2464     	outStr = (char*)NEditMalloc(len + 1);
2465     	strcpy(outStr, text);
2466     	*newLength = len;
2467     	return outStr;
2468     }
2469 
2470     /* If the tab settings are not the same, brutally convert tabs to
2471        spaces, then back to tabs in the new position */
2472     expStr = expandTabs(text, origIndent, tabDist, nullSubsChar, &len);
2473     if (!useTabs) {
2474     	*newLength = len;
2475     	return expStr;
2476     }
2477     outStr = unexpandTabs(expStr, newIndent, tabDist, nullSubsChar, newLength);
2478     NEditFree(expStr);
2479     return outStr;
2480 }
2481 
2482 /*
2483 ** Expand tabs to spaces for a block of text.  The additional parameter
2484 ** "startIndent" if nonzero, indicates that the text is a rectangular selection
2485 ** beginning at column "startIndent"
2486 */
expandTabs(const char * text,int startIndent,int tabDist,char nullSubsChar,int * newLen)2487 static char *expandTabs(const char *text, int startIndent, int tabDist,
2488 	char nullSubsChar, int *newLen)
2489 {
2490     char *outStr, *outPtr;
2491     const char *c;
2492     int indent, len, outLen = 0;
2493 
2494     /* rehearse the expansion to figure out length for output string */
2495     indent = startIndent;
2496     for (c=text; *c!='\0'; c++) {
2497     	if (*c == '\t') {
2498     	    len = BufCharWidth(*c, indent, tabDist, nullSubsChar);
2499     	    outLen += len;
2500     	    indent += len;
2501     	} else if (*c == '\n') {
2502     	    indent = startIndent;
2503     	    outLen++;
2504     	} else {
2505     	    indent += BufCharWidth(*c, indent, tabDist, nullSubsChar);
2506     	    outLen++;
2507     	}
2508     }
2509 
2510     /* do the expansion */
2511     outStr = (char*)NEditMalloc(outLen+1);
2512     outPtr = outStr;
2513     indent = startIndent;
2514     for (c=text; *c!= '\0'; c++) {
2515     	if (*c == '\t') {
2516     	    len = BufExpandCharacter(*c, indent, outPtr, tabDist, nullSubsChar);
2517     	    outPtr += len;
2518     	    indent += len;
2519     	} else if (*c == '\n') {
2520     	    indent = startIndent;
2521     	    *outPtr++ = *c;
2522     	} else {
2523     	    indent += BufCharWidth(*c, indent, tabDist, nullSubsChar);
2524     	    *outPtr++ = *c;
2525     	}
2526     }
2527     outStr[outLen] = '\0';
2528     *newLen = outLen;
2529     return outStr;
2530 }
2531 
2532 /*
2533 ** Convert sequences of spaces into tabs.  The threshold for conversion is
2534 ** when 3 or more spaces can be converted into a single tab, this avoids
2535 ** converting double spaces after a period withing a block of text.
2536 */
unexpandTabs(const char * text,int startIndent,int tabDist,char nullSubsChar,int * newLen)2537 static char *unexpandTabs(const char *text, int startIndent, int tabDist,
2538 	char nullSubsChar, int *newLen)
2539 {
2540     char *outStr, *outPtr, expandedChar[MAX_EXP_CHAR_LEN];
2541     const char *c;
2542     int indent, len;
2543 
2544     outStr = (char*)NEditMalloc(strlen(text)+1);
2545     outPtr = outStr;
2546     indent = startIndent;
2547     for (c=text; *c!='\0';) {
2548     	if (*c == ' ') {
2549     	    len = BufExpandCharacter('\t', indent, expandedChar, tabDist,
2550 		    nullSubsChar);
2551     	    if (len >= 3 && !strncmp(c, expandedChar, len)) {
2552     	    	c += len;
2553     	    	*outPtr++ = '\t';
2554     	    	indent += len;
2555     	    } else {
2556     	    	*outPtr++ = *c++;
2557     	    	indent++;
2558     	    }
2559     	} else if (*c == '\n') {
2560     	    indent = startIndent;
2561     	    *outPtr++ = *c++;
2562     	} else {
2563     	    *outPtr++ = *c++;
2564     	    indent++;
2565     	}
2566     }
2567     *outPtr = '\0';
2568     *newLen = outPtr - outStr;
2569     return outStr;
2570 }
2571 
max(int i1,int i2)2572 static int max(int i1, int i2)
2573 {
2574     return i1 >= i2 ? i1 : i2;
2575 }
2576 
min(int i1,int i2)2577 static int min(int i1, int i2)
2578 {
2579     return i1 <= i2 ? i1 : i2;
2580 }
2581