1 //	Search/replace functions
2 //	Copyright (C) 2000 Core Technologies.
3 
4 // This file is part of e93.
5 //
6 // e93 is free software; you can redistribute it and/or modify
7 // it under the terms of the e93 LICENSE AGREEMENT.
8 //
9 // e93 is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // e93 LICENSE AGREEMENT for more details.
13 //
14 // You should have received a copy of the e93 LICENSE AGREEMENT
15 // along with e93; see the file "LICENSE.TXT".
16 
17 #include	"includes.h"
18 
19 enum
20 {
21 	MATCHED_NOTHING,
22 };
23 
24 static const char *errorDescriptions[]=
25 {
26 	"Find matched nothing -- could not continue",
27 };
28 
29 typedef struct
30 {
31 	bool
32 		ignoreCase;					// true if we should ignore case, false if not
33 	TEXT_UNIVERSE
34 		*searchForText;				// pointer to the search for text universe
35 	TEXT_UNIVERSE
36 		*replaceWithText;			// pointer to the replace with text universe
37 	COMPILED_EXPRESSION
38 		*expression;				// if type is regex, this points to the compiled expression, otherwise it is NULL
39 	char
40 		*procedure;					// if non NULL, this points to a procedure to use instead of the replace text
41 } SEARCH_RECORD;
42 
LiteralMatch(CHUNK_HEADER * chunk,UINT32 offset,CHUNK_HEADER * forTextChunk,UINT32 forTextOffset,UINT32 numToSearch,CHUNK_HEADER ** endChunk,UINT32 * endOffset)43 bool LiteralMatch(CHUNK_HEADER *chunk,UINT32 offset,CHUNK_HEADER *forTextChunk,UINT32 forTextOffset,UINT32 numToSearch,CHUNK_HEADER **endChunk,UINT32 *endOffset)
44 // See if the text at chunk/offset matches the text in forTextChunk/forTextOffset
45 // if there is a match, return true with endChunk, and endOffset filled in to point to one
46 // past the last place looked in inText
47 // NOTE: there must be at least numToSearch bytes remaining in both chunk/offset and forTextChunk/forTextOffset
48 {
49 	UINT8
50 		*inPointer,
51 		*forPointer;
52 	UINT32
53 		inTotal,
54 		forTotal;
55 
56 	if(numToSearch)
57 	{
58 		if(forTextChunk->data[forTextOffset]==chunk->data[offset])			// for speed, make initial check to see if this can match
59 		{
60 			inTotal=chunk->totalBytes;										// cache total bytes, and pointer
61 			inPointer=&(chunk->data[offset+1]);
62 			forTotal=forTextChunk->totalBytes;
63 			forPointer=&(forTextChunk->data[forTextOffset+1]);
64 			if((++offset)>=inTotal)											// move past bytes just checked
65 			{
66 				offset=0;
67 				if((chunk=chunk->nextHeader))
68 				{
69 					inTotal=chunk->totalBytes;
70 					inPointer=chunk->data;
71 				}
72 			}
73 			if((++forTextOffset)>=forTotal)
74 			{
75 				forTextOffset=0;
76 				if((forTextChunk=forTextChunk->nextHeader))
77 				{
78 					forTotal=forTextChunk->totalBytes;
79 					forPointer=forTextChunk->data;
80 				}
81 			}
82 			numToSearch--;
83 			while(numToSearch&&(*inPointer++==*forPointer++))				// go until mismatch, or until we run out of for data
84 			{
85 				if((++offset)>=inTotal)
86 				{
87 					offset=0;
88 					if((chunk=chunk->nextHeader))
89 					{
90 						inTotal=chunk->totalBytes;
91 						inPointer=chunk->data;
92 					}
93 				}
94 				if((++forTextOffset)>=forTotal)
95 				{
96 					forTextOffset=0;
97 					if((forTextChunk=forTextChunk->nextHeader))
98 					{
99 						forTotal=forTextChunk->totalBytes;
100 						forPointer=forTextChunk->data;
101 					}
102 				}
103 				numToSearch--;
104 			}
105 			if(!numToSearch)
106 			{
107 				*endChunk=chunk;
108 				*endOffset=offset;
109 				return(true);
110 			}
111 		}
112 	}
113 	else
114 	{
115 		*endChunk=chunk;
116 		*endOffset=offset;
117 		return(true);
118 	}
119 	return(false);
120 }
121 
LiteralMatchTT(CHUNK_HEADER * chunk,UINT32 offset,CHUNK_HEADER * forTextChunk,UINT32 forTextOffset,UINT32 numToSearch,const UINT8 * translateTable,CHUNK_HEADER ** endChunk,UINT32 * endOffset)122 bool LiteralMatchTT(CHUNK_HEADER *chunk,UINT32 offset,CHUNK_HEADER *forTextChunk,UINT32 forTextOffset,UINT32 numToSearch,const UINT8 *translateTable,CHUNK_HEADER **endChunk,UINT32 *endOffset)
123 // See if the text at chunk/offset matches the text in forTextChunk/forTextOffset
124 // if there is a match, return true with endChunk, and endOffset filled in to point to one
125 // past the last place looked in inText
126 // NOTE: there must be at least numToSearch bytes remaining in both chunk/offset and forTextChunk/forTextOffset
127 // NOTE: translate characters when matching
128 {
129 	UINT8
130 		*inPointer,
131 		*forPointer;
132 	UINT32
133 		inTotal,
134 		forTotal;
135 
136 	if(numToSearch)
137 	{
138 		if(translateTable[forTextChunk->data[forTextOffset]]==translateTable[chunk->data[offset]])		// for speed, make initial check to see if this can match
139 		{
140 			inTotal=chunk->totalBytes;										// cache total bytes, and pointer
141 			inPointer=&(chunk->data[offset+1]);
142 			forTotal=forTextChunk->totalBytes;
143 			forPointer=&(forTextChunk->data[forTextOffset+1]);
144 			if((++offset)>=inTotal)											// move past bytes just checked
145 			{
146 				offset=0;
147 				if((chunk=chunk->nextHeader))
148 				{
149 					inTotal=chunk->totalBytes;
150 					inPointer=chunk->data;
151 				}
152 			}
153 			if((++forTextOffset)>=forTotal)
154 			{
155 				forTextOffset=0;
156 				if((forTextChunk=forTextChunk->nextHeader))
157 				{
158 					forTotal=forTextChunk->totalBytes;
159 					forPointer=forTextChunk->data;
160 				}
161 			}
162 			numToSearch--;
163 			while(numToSearch&&(translateTable[*inPointer++]==translateTable[*forPointer++]))	// go until mismatch, or until we run out of for data
164 			{
165 				if((++offset)>=inTotal)
166 				{
167 					offset=0;
168 					if((chunk=chunk->nextHeader))
169 					{
170 						inTotal=chunk->totalBytes;
171 						inPointer=chunk->data;
172 					}
173 				}
174 				if((++forTextOffset)>=forTotal)
175 				{
176 					forTextOffset=0;
177 					if((forTextChunk=forTextChunk->nextHeader))
178 					{
179 						forTotal=forTextChunk->totalBytes;
180 						forPointer=forTextChunk->data;
181 					}
182 				}
183 				numToSearch--;
184 			}
185 			if(!numToSearch)
186 			{
187 				*endChunk=chunk;
188 				*endOffset=offset;
189 				return(true);
190 			}
191 		}
192 	}
193 	else
194 	{
195 		*endChunk=chunk;
196 		*endOffset=offset;
197 		return(true);
198 	}
199 	return(false);
200 }
201 
SearchForwardLiteral(CHUNK_HEADER * chunk,UINT32 offset,CHUNK_HEADER * forTextChunk,UINT32 forTextOffset,UINT32 forTextLength,UINT32 numToSearch,bool ignoreCase,bool * foundMatch,UINT32 * matchOffset,UINT32 * numMatched,CHUNK_HEADER ** startChunk,UINT32 * startOffset,CHUNK_HEADER ** endChunk,UINT32 * endOffset,ABORT_TEST_FUNCTION * abortFunction)202 bool SearchForwardLiteral(CHUNK_HEADER *chunk,UINT32 offset,CHUNK_HEADER *forTextChunk,UINT32 forTextOffset,UINT32 forTextLength,UINT32 numToSearch,bool ignoreCase,bool *foundMatch,UINT32 *matchOffset,UINT32 *numMatched,CHUNK_HEADER **startChunk,UINT32 *startOffset,CHUNK_HEADER **endChunk,UINT32 *endOffset,ABORT_TEST_FUNCTION *abortFunction)
203 // Search forward from chunk/offset for forTextChunk/forTextOffset
204 // look at up to numToSearch bytes of source data
205 // if a match is located, return *foundMatch=true, with matchOffset set to the number
206 // of bytes in from chunk/offset where the match was found, and
207 // startChunk/startOffset set to the start of the match, and
208 // endChunk/endOffset set to one past the last byte of source that was matched
209 // If there is a failure, SetError, and return false.
210 {
211 	UINT32
212 		numSearched;
213 	bool
214 		fail;
215 
216 	fail=false;
217 	numSearched=0;
218 	*foundMatch=false;
219 	if(ignoreCase)
220 	{
221 		while(!fail&&!(*foundMatch)&&((forTextLength+numSearched)<=numToSearch))
222 		{
223 			if(LiteralMatchTT(chunk,offset,forTextChunk,forTextOffset,forTextLength,upperCaseTable,endChunk,endOffset))
224 			{
225 				*matchOffset=numSearched;
226 				*startChunk=chunk;
227 				*startOffset=offset;
228 				*foundMatch=true;
229 				*numMatched=forTextLength;
230 			}
231 			else
232 			{
233 				if((++offset)>=chunk->totalBytes)			// move forward in the input
234 				{
235 					offset=0;
236 					chunk=chunk->nextHeader;
237 					if(abortFunction&&abortFunction())
238 					{
239 						SetError("Search Aborted");
240 						fail=true;
241 					}
242 				}
243 				numSearched++;
244 			}
245 		}
246 	}
247 	else
248 	{
249 		while(!fail&&!(*foundMatch)&&((forTextLength+numSearched)<=numToSearch))
250 		{
251 			if(LiteralMatch(chunk,offset,forTextChunk,forTextOffset,forTextLength,endChunk,endOffset))
252 			{
253 				*matchOffset=numSearched;
254 				*startChunk=chunk;
255 				*startOffset=offset;
256 				*foundMatch=true;
257 				*numMatched=forTextLength;
258 			}
259 			else
260 			{
261 				if((++offset)>=chunk->totalBytes)			// move forward in the input
262 				{
263 					offset=0;
264 					chunk=chunk->nextHeader;
265 					if(abortFunction&&abortFunction())
266 					{
267 						SetError("Search Aborted");
268 						fail=true;
269 					}
270 				}
271 				numSearched++;
272 			}
273 		}
274 	}
275 	return(!fail);
276 }
277 
SearchBackwardLiteral(CHUNK_HEADER * chunk,UINT32 offset,CHUNK_HEADER * forTextChunk,UINT32 forTextOffset,UINT32 forTextLength,UINT32 numToSearch,bool ignoreCase,bool * foundMatch,UINT32 * matchOffset,UINT32 * numMatched,CHUNK_HEADER ** startChunk,UINT32 * startOffset,CHUNK_HEADER ** endChunk,UINT32 * endOffset,ABORT_TEST_FUNCTION * abortFunction)278 bool SearchBackwardLiteral(CHUNK_HEADER *chunk,UINT32 offset,CHUNK_HEADER *forTextChunk,UINT32 forTextOffset,UINT32 forTextLength,UINT32 numToSearch,bool ignoreCase,bool *foundMatch,UINT32 *matchOffset,UINT32 *numMatched,CHUNK_HEADER **startChunk,UINT32 *startOffset,CHUNK_HEADER **endChunk,UINT32 *endOffset,ABORT_TEST_FUNCTION *abortFunction)
279 // Search backward from chunk/offset for forTextChunk/forTextOffset
280 // look at up to numToSearch bytes of source data
281 // if a match is located, return *foundMatch=true, with matchOffset set to the number
282 // of bytes back from chunk/offset where the match was found, and
283 // startChunk/startOffset set to chunk/offset at the point of the match
284 // endChunk/endOffset set to position just after the last match
285 // If there is a failure, SetError, and return false.
286 {
287 	UINT32
288 		numSearched;
289 	UINT32
290 		amountToBackUp;
291 	bool
292 		fail;
293 
294 	fail=false;
295 	numSearched=0;
296 	*foundMatch=false;
297 	if(forTextLength<=numToSearch)
298 	{
299 		amountToBackUp=forTextLength;				// back up in the source by forTextLength
300 		while(amountToBackUp)
301 		{
302 			if(offset>=amountToBackUp)
303 			{
304 				offset-=amountToBackUp;
305 				amountToBackUp=0;
306 			}
307 			else
308 			{
309 				amountToBackUp-=(offset+1);
310 				if((chunk=chunk->previousHeader))
311 				{
312 					offset=chunk->totalBytes-1;
313 				}
314 			}
315 		}
316 		numSearched=forTextLength;
317 		if(ignoreCase)
318 		{
319 			while(!fail&&!(*foundMatch)&&(numSearched<=numToSearch))
320 			{
321 				if(LiteralMatchTT(chunk,offset,forTextChunk,forTextOffset,forTextLength,upperCaseTable,endChunk,endOffset))
322 				{
323 					*matchOffset=numSearched;
324 					*startChunk=chunk;
325 					*startOffset=offset;
326 					*foundMatch=true;
327 					*numMatched=forTextLength;
328 				}
329 				else
330 				{
331 					if(!offset)					// move backward in the input
332 					{
333 						if((chunk=chunk->previousHeader))
334 						{
335 							offset=chunk->totalBytes;
336 						}
337 						if(abortFunction&&abortFunction())
338 						{
339 							SetError("Search Aborted");
340 							fail=true;
341 						}
342 					}
343 					offset--;
344 					numSearched++;
345 				}
346 			}
347 		}
348 		else
349 		{
350 			while(!fail&&!(*foundMatch)&&(numSearched<=numToSearch))
351 			{
352 				if(LiteralMatch(chunk,offset,forTextChunk,forTextOffset,forTextLength,endChunk,endOffset))
353 				{
354 					*matchOffset=numSearched;
355 					*startChunk=chunk;
356 					*startOffset=offset;
357 					*foundMatch=true;
358 					*numMatched=forTextLength;
359 				}
360 				else
361 				{
362 					if(!offset)					// move backward in the input
363 					{
364 						if((chunk=chunk->previousHeader))
365 						{
366 							offset=chunk->totalBytes;
367 						}
368 						if(abortFunction&&abortFunction())
369 						{
370 							SetError("Search Aborted");
371 							fail=true;
372 						}
373 					}
374 					offset--;
375 					numSearched++;
376 				}
377 			}
378 		}
379 	}
380 	return(!fail);
381 }
382 
SearchForward(CHUNK_HEADER * chunk,UINT32 offset,SEARCH_RECORD * searchRecord,bool leftEdge,bool rightEdge,UINT32 numToSearch,bool * foundMatch,UINT32 * matchOffset,UINT32 * numMatched,CHUNK_HEADER ** startChunk,UINT32 * startOffset,CHUNK_HEADER ** endChunk,UINT32 * endOffset,ABORT_TEST_FUNCTION * abortFunction)383 static bool SearchForward(CHUNK_HEADER *chunk,UINT32 offset,SEARCH_RECORD *searchRecord,bool leftEdge,bool rightEdge,UINT32 numToSearch,bool *foundMatch,UINT32 *matchOffset,UINT32 *numMatched,CHUNK_HEADER **startChunk,UINT32 *startOffset,CHUNK_HEADER **endChunk,UINT32 *endOffset,ABORT_TEST_FUNCTION *abortFunction)
384 // Search forward given searchRecord
385 // return foundMatch true if a match was found
386 // return false if there was some sort of hard failure
387 // if a match is found, startChunk/startOffset point to the start of the match
388 // matchOffset is the number of bytes searched
389 // endChunk/endOffset point to one past the end of the matched text
390 {
391 	bool
392 		fail;
393 
394 	fail=false;
395 	if(searchRecord->expression)
396 	{
397 		fail=!SearchForwardRE(chunk,offset,searchRecord->expression,leftEdge,rightEdge,numToSearch,numToSearch,searchRecord->ignoreCase,foundMatch,matchOffset,numMatched,startChunk,startOffset,endChunk,endOffset,abortFunction);
398 	}
399 	else
400 	{
401 		fail=!SearchForwardLiteral(chunk,offset,searchRecord->searchForText->firstChunkHeader,0,searchRecord->searchForText->totalBytes,numToSearch,searchRecord->ignoreCase,foundMatch,matchOffset,numMatched,startChunk,startOffset,endChunk,endOffset,abortFunction);
402 	}
403 	return(!fail);
404 }
405 
SearchBackward(CHUNK_HEADER * chunk,UINT32 offset,SEARCH_RECORD * searchRecord,bool leftEdge,bool rightEdge,UINT32 numToSearch,bool * foundMatch,UINT32 * matchOffset,UINT32 * numMatched,CHUNK_HEADER ** startChunk,UINT32 * startOffset,CHUNK_HEADER ** endChunk,UINT32 * endOffset,ABORT_TEST_FUNCTION * abortFunction)406 static bool SearchBackward(CHUNK_HEADER *chunk,UINT32 offset,SEARCH_RECORD *searchRecord,bool leftEdge,bool rightEdge,UINT32 numToSearch,bool *foundMatch,UINT32 *matchOffset,UINT32 *numMatched,CHUNK_HEADER **startChunk,UINT32 *startOffset,CHUNK_HEADER **endChunk,UINT32 *endOffset,ABORT_TEST_FUNCTION *abortFunction)
407 // Search backward given searchRecord
408 // return foundMatch true if a match was found
409 // return false if there was some sort of hard failure
410 // if a match is found, startChunk/startOffset point to the start of the match
411 // matchOffset is the number of bytes searched
412 // endChunk/endOffset point to one past the end of the matched text
413 {
414 	bool
415 		fail;
416 
417 	fail=false;
418 	if(searchRecord->expression)
419 	{
420 		fail=!SearchBackwardRE(chunk,offset,searchRecord->expression,leftEdge,rightEdge,numToSearch,numToSearch,searchRecord->ignoreCase,foundMatch,matchOffset,numMatched,startChunk,startOffset,endChunk,endOffset,abortFunction);
421 	}
422 	else
423 	{
424 		fail=!SearchBackwardLiteral(chunk,offset,searchRecord->searchForText->firstChunkHeader,0,searchRecord->searchForText->totalBytes,numToSearch,searchRecord->ignoreCase,foundMatch,matchOffset,numMatched,startChunk,startOffset,endChunk,endOffset,abortFunction);
425 	}
426 	return(!fail);
427 }
428 
DisposeSearchRecord(SEARCH_RECORD * searchRecord)429 static void DisposeSearchRecord(SEARCH_RECORD *searchRecord)
430 // Get rid of searchRecord, and anything allocated under it
431 {
432 	if(searchRecord->expression)		// if there is a selection expression, get rid of it
433 	{
434 		REFree(searchRecord->expression);
435 	}
436 	if(searchRecord->procedure)			// if there is a procedure, get rid of it
437 	{
438 		MDisposePtr(searchRecord->procedure);
439 	}
440 	MDisposePtr(searchRecord);
441 }
442 
CreateSearchRecord(TEXT_UNIVERSE * searchForText,TEXT_UNIVERSE * replaceWithText,bool selectionExpr,bool ignoreCase,bool replaceProc)443 static SEARCH_RECORD *CreateSearchRecord(TEXT_UNIVERSE *searchForText,TEXT_UNIVERSE *replaceWithText,bool selectionExpr,bool ignoreCase,bool replaceProc)
444 // Create a search record that describes the search, and remembers some stuff about it
445 // if there is a problem, SetError, and return NULL
446 {
447 	SEARCH_RECORD
448 		*searchRecord;
449 	COMPILED_EXPRESSION
450 		*compiledExpression;
451 	UINT8
452 		*expressionText;
453 	CHUNK_HEADER
454 		*chunk;
455 	UINT32
456 		offset;
457 	bool
458 		fail;
459 
460 	fail=false;
461 	if((searchRecord=(SEARCH_RECORD *)MNewPtr(sizeof(SEARCH_RECORD))))
462 	{
463 		searchRecord->searchForText=searchForText;			// remember these for later
464 		searchRecord->replaceWithText=replaceWithText;
465 		searchRecord->ignoreCase=ignoreCase;
466 		searchRecord->expression=NULL;						// assume no selection expression yet
467 		searchRecord->procedure=NULL;						// assume no procedure yet
468 		if(replaceProc)										// if treating replace as procedure, we need to grab the text from the replace buffer, and place it into memory
469 		{
470 			if((searchRecord->procedure=(char *)MNewPtr(replaceWithText->totalBytes+1)))	// make a buffer
471 			{
472 				fail=!ExtractUniverseText(replaceWithText,replaceWithText->firstChunkHeader,0,(UINT8 *)searchRecord->procedure,replaceWithText->totalBytes,&chunk,&offset);
473 				searchRecord->procedure[replaceWithText->totalBytes]='\0';				// terminate the string
474 			}
475 			else
476 			{
477 				fail=true;
478 			}
479 		}
480 		if(!fail)
481 		{
482 			if(selectionExpr)
483 			{
484 				if((expressionText=(UINT8 *)MNewPtr(searchForText->totalBytes)))	// make a buffer to temporarily hold the expression text
485 				{
486 					if(ExtractUniverseText(searchForText,searchForText->firstChunkHeader,0,expressionText,searchForText->totalBytes,&chunk,&offset))
487 					{
488 						if((compiledExpression=RECompile(&(expressionText[0]),searchForText->totalBytes,ignoreCase?upperCaseTable:NULL)))
489 						{
490 							searchRecord->expression=compiledExpression;
491 							MDisposePtr(expressionText);				// get rid of temporary buffer
492 							return(searchRecord);
493 						}
494 					}
495 					MDisposePtr(expressionText);
496 				}
497 			}
498 			else
499 			{
500 				return(searchRecord);
501 			}
502 		}
503 		if(searchRecord->procedure)			// if there is a procedure, get rid of it
504 		{
505 			MDisposePtr(searchRecord->procedure);
506 		}
507 		MDisposePtr(searchRecord);
508 	}
509 	return(NULL);
510 }
511 
EditorFind(EDITOR_BUFFER * buffer,SELECTION_UNIVERSE * selectionUniverse,TEXT_UNIVERSE * searchForText,bool backward,bool wrapAround,bool selectionExpr,bool ignoreCase,bool * foundMatch,SELECTION_UNIVERSE * resultSelectionUniverse,ABORT_TEST_FUNCTION * abortFunction)512 bool EditorFind(EDITOR_BUFFER *buffer,SELECTION_UNIVERSE *selectionUniverse,TEXT_UNIVERSE *searchForText,bool backward,bool wrapAround,bool selectionExpr,bool ignoreCase,bool *foundMatch,SELECTION_UNIVERSE *resultSelectionUniverse,ABORT_TEST_FUNCTION *abortFunction)
513 // Find searchForText in buffer, starting at the cursor position of selectionUniverse, or the end of
514 // the current selection
515 // If anything is found, return it in resultSelectionUniverse, else, do not modify resultSelectionUniverse.
516 // foundMatch reflects the status of the search.
517 // if there is some problem during the search, SetError, return false
518 // NOTE: resultSelectionUniverse must be different from selectionUniverse
519 {
520 	CHUNK_HEADER
521 		*inChunk,
522 		*startChunk,
523 		*endChunk;
524 	UINT32
525 		inOffset,
526 		startOffset,
527 		endOffset;
528 	SEARCH_RECORD
529 		*searchRecord;
530 	UINT32
531 		startPosition,
532 		endPosition;
533 	UINT32
534 		locatedPosition,
535 		locatedLength;
536 	TEXT_UNIVERSE
537 		*searchInText;
538 	bool
539 		fail;
540 
541 	fail=*foundMatch=false;
542 	if((searchRecord=CreateSearchRecord(searchForText,NULL,selectionExpr,ignoreCase,false)))
543 	{
544 		ShowBusy();
545 		searchInText=buffer->textUniverse;
546 
547 		GetSelectionEndPositions(selectionUniverse,&startPosition,&endPosition);		// find ends of old selection, or cursor position
548 		if(!backward)
549 		{
550 			PositionToChunkPosition(searchInText,endPosition,&inChunk,&inOffset);		// locate place to start the search
551 			if(SearchForward(inChunk,inOffset,searchRecord,endPosition==0,true,searchInText->totalBytes-endPosition,foundMatch,&locatedPosition,&locatedLength,&startChunk,&startOffset,&endChunk,&endOffset,abortFunction))
552 			{
553 				locatedPosition+=endPosition;											// make located position absolute
554 				if((!(*foundMatch))&&wrapAround)
555 				{
556 					if(!SearchForward(searchInText->firstChunkHeader,0,searchRecord,true,startPosition==searchInText->totalBytes,startPosition,foundMatch,&locatedPosition,&locatedLength,&startChunk,&startOffset,&endChunk,&endOffset,abortFunction))
557 					{
558 						fail=true;
559 					}
560 				}
561 			}
562 			else
563 			{
564 				fail=true;
565 			}
566 		}
567 		else
568 		{
569 			PositionToChunkPositionPastEnd(searchInText,startPosition,&inChunk,&inOffset);	// locate place to start the search
570 			if(SearchBackward(inChunk,inOffset,searchRecord,true,startPosition==searchInText->totalBytes,startPosition,foundMatch,&locatedPosition,&locatedLength,&startChunk,&startOffset,&endChunk,&endOffset,abortFunction))
571 			{
572 				locatedPosition=startPosition-locatedPosition;							// make located position absolute
573 				if((!(*foundMatch))&&wrapAround)
574 				{
575 					PositionToChunkPositionPastEnd(searchInText,searchInText->totalBytes,&inChunk,&inOffset);	// locate place to start the search
576 					if(SearchBackward(inChunk,inOffset,searchRecord,endPosition==0,true,searchInText->totalBytes-endPosition,foundMatch,&locatedPosition,&locatedLength,&startChunk,&startOffset,&endChunk,&endOffset,abortFunction))
577 					{
578 						locatedPosition=searchInText->totalBytes-locatedPosition;
579 					}
580 					else
581 					{
582 						fail=true;
583 					}
584 				}
585 			}
586 			else
587 			{
588 				fail=true;
589 			}
590 		}
591 		if((!fail)&&(*foundMatch))
592 		{
593 
594 			DeleteAllSelections(resultSelectionUniverse);	// remove all old selections
595 			if(SetSelectionRange(resultSelectionUniverse,locatedPosition,locatedLength))
596 			{
597 				SetSelectionCursorPosition(resultSelectionUniverse,locatedPosition);
598 			}
599 			else
600 			{
601 				fail=true;
602 			}
603 		}
604 		DisposeSearchRecord(searchRecord);
605 		ShowNotBusy();
606 	}
607 	else
608 	{
609 		fail=true;
610 	}
611 	return(!fail);
612 }
613 
FindAllForward(TEXT_UNIVERSE * searchInText,bool leftEdge,bool rightEdge,UINT32 lowPosition,UINT32 highPosition,SEARCH_RECORD * searchRecord,SELECTION_UNIVERSE * selectionUniverse,bool * foundMatch,UINT32 * firstMatchPosition,ABORT_TEST_FUNCTION * abortFunction)614 static bool FindAllForward(TEXT_UNIVERSE *searchInText,bool leftEdge,bool rightEdge,UINT32 lowPosition,UINT32 highPosition,SEARCH_RECORD *searchRecord,SELECTION_UNIVERSE *selectionUniverse,bool *foundMatch,UINT32 *firstMatchPosition,ABORT_TEST_FUNCTION *abortFunction)
615 // Find all occurrences of searchRecord between lowPosition, and highPosition, adding the selections
616 // to selectionUniverse
617 // if there is a problem, SetError and return false
618 // if a matches were found, return true in foundMatch, and the position of the first match found
619 // in firstMatchPosition
620 // NOTE: even if false is returned, foundMatch, firstMatchPosition, and selectionUniverse are
621 // valid
622 {
623 	bool
624 		fail;
625 	CHUNK_HEADER
626 		*inChunk,
627 		*startChunk;
628 	UINT32
629 		inOffset,
630 		startOffset;
631 	bool
632 		matching;
633 	UINT32
634 		nextSearchPosition,
635 		locatedPosition,
636 		locatedLength;
637 
638 	fail=*foundMatch=false;
639 	nextSearchPosition=lowPosition;
640 	PositionToChunkPosition(searchInText,nextSearchPosition,&inChunk,&inOffset);	// locate place to start the search
641 	matching=true;
642 	while(!fail&&matching)
643 	{
644 		if(SearchForward(inChunk,inOffset,searchRecord,leftEdge,rightEdge,highPosition-nextSearchPosition,&matching,&locatedPosition,&locatedLength,&startChunk,&startOffset,&inChunk,&inOffset,abortFunction))
645 		{
646 			if(matching)
647 			{
648 				if(locatedLength)											// finding an empty string is fatal to find all
649 				{
650 					leftEdge=false;											// no matter what, we just moved off the left edge
651 					locatedPosition+=nextSearchPosition;					// make located position absolute
652 					nextSearchPosition=locatedPosition+locatedLength;		// move to next place to search
653 					if(!(*foundMatch))
654 					{
655 						(*firstMatchPosition)=locatedPosition;
656 						(*foundMatch)=true;
657 					}
658 					fail=!SetSelectionRange(selectionUniverse,locatedPosition,locatedLength);
659 				}
660 				else
661 				{
662 					SetError(errorDescriptions[MATCHED_NOTHING]);
663 					fail=true;
664 				}
665 			}
666 		}
667 		else
668 		{
669 			fail=true;
670 		}
671 	}
672 	return(!fail);
673 }
674 
FindAllBackward(TEXT_UNIVERSE * searchInText,bool leftEdge,bool rightEdge,UINT32 lowPosition,UINT32 highPosition,SEARCH_RECORD * searchRecord,SELECTION_UNIVERSE * selectionUniverse,bool * foundMatch,UINT32 * firstMatchPosition,ABORT_TEST_FUNCTION * abortFunction)675 static bool FindAllBackward(TEXT_UNIVERSE *searchInText,bool leftEdge,bool rightEdge,UINT32 lowPosition,UINT32 highPosition,SEARCH_RECORD *searchRecord,SELECTION_UNIVERSE *selectionUniverse,bool *foundMatch,UINT32 *firstMatchPosition,ABORT_TEST_FUNCTION *abortFunction)
676 // Find all occurrences of searchRecord between lowPosition, and highPosition, adding the selections
677 // to selectionUniverse
678 // if there is a problem, SetError and return false
679 // if a matches were found, return true in foundMatch, and the position of the first match found
680 // in firstMatchPosition
681 // NOTE: even if false is returned, foundMatch, firstMatchPosition, and selectionUniverse are
682 // valid
683 {
684 	bool
685 		fail;
686 	UINT32
687 		nextSearchPosition;
688 	CHUNK_HEADER
689 		*inChunk,
690 		*endChunk;
691 	UINT32
692 		inOffset,
693 		endOffset;
694 	bool
695 		matching;
696 	UINT32
697 		locatedPosition,
698 		locatedLength;
699 
700 	fail=*foundMatch=false;
701 	nextSearchPosition=highPosition;
702 	PositionToChunkPositionPastEnd(searchInText,nextSearchPosition,&inChunk,&inOffset);			// locate place to start the search
703 	matching=true;
704 	while(!fail&&matching)
705 	{
706 		if(SearchBackward(inChunk,inOffset,searchRecord,leftEdge,rightEdge,nextSearchPosition-lowPosition,&matching,&locatedPosition,&locatedLength,&inChunk,&inOffset,&endChunk,&endOffset,abortFunction))
707 		{
708 			if(matching)
709 			{
710 				if(locatedLength)										// finding an empty string is fatal to find all
711 				{
712 					rightEdge=false;									// no matter what, just left the right edge
713 					locatedPosition=nextSearchPosition-locatedPosition;	// make located position absolute
714 					nextSearchPosition=locatedPosition;
715 					if(!(*foundMatch))
716 					{
717 						(*firstMatchPosition)=locatedPosition;
718 						(*foundMatch)=true;
719 					}
720 					fail=!SetSelectionRange(selectionUniverse,locatedPosition,locatedLength);
721 				}
722 				else
723 				{
724 					SetError(errorDescriptions[MATCHED_NOTHING]);
725 					fail=true;
726 				}
727 			}
728 		}
729 		else
730 		{
731 			fail=true;
732 		}
733 	}
734 	return(!fail);
735 }
736 
EditorFindAll(EDITOR_BUFFER * buffer,SELECTION_UNIVERSE * selectionUniverse,TEXT_UNIVERSE * searchForText,bool backward,bool wrapAround,bool selectionExpr,bool ignoreCase,bool limitScope,bool * foundMatch,SELECTION_UNIVERSE * resultSelectionUniverse,ABORT_TEST_FUNCTION * abortFunction)737 bool EditorFindAll(EDITOR_BUFFER *buffer,SELECTION_UNIVERSE *selectionUniverse,TEXT_UNIVERSE *searchForText,bool backward,bool wrapAround,bool selectionExpr,bool ignoreCase,bool limitScope,bool *foundMatch,SELECTION_UNIVERSE *resultSelectionUniverse,ABORT_TEST_FUNCTION *abortFunction)
738 // Find all occurrences of searchForText using the given rules, if there is an error,
739 // or the user aborts, SetError, and return false. If nothing is found, set found to false
740 // if found returns true, resultSelectionUniverse will be modified to contain the
741 // list of selections of found text.
742 // NOTE: resultSelectionUniverse must be different from selectionUniverse
743 {
744 	SEARCH_RECORD
745 		*searchRecord;
746 	UINT32
747 		startPosition,
748 		endPosition,
749 		selectionLength;
750 	UINT32
751 		firstMatchPosition,
752 		tempFirstMatchPosition;
753 	TEXT_UNIVERSE
754 		*searchInText;
755 	bool
756 		hadMatch,
757 		fail;
758 
759 	fail=*foundMatch=false;
760 	if((searchRecord=CreateSearchRecord(searchForText,NULL,selectionExpr,ignoreCase,false)))
761 	{
762 		ShowBusy();
763 		searchInText=buffer->textUniverse;
764 		if(!backward)
765 		{
766 			if(!limitScope)
767 			{
768 				GetSelectionEndPositions(selectionUniverse,&startPosition,&endPosition);		// find ends of old selection, or cursor position
769 				if(FindAllForward(searchInText,endPosition==0,true,endPosition,searchInText->totalBytes,searchRecord,resultSelectionUniverse,foundMatch,&firstMatchPosition,abortFunction))
770 				{
771 					if(wrapAround)
772 					{
773 						fail=!FindAllForward(searchInText,true,startPosition==searchInText->totalBytes,0,startPosition,searchRecord,resultSelectionUniverse,&hadMatch,&tempFirstMatchPosition,abortFunction);
774 						if(!(*foundMatch))
775 						{
776 							*foundMatch=hadMatch;
777 							firstMatchPosition=tempFirstMatchPosition;
778 						}
779 					}
780 				}
781 				else
782 				{
783 					fail=true;
784 				}
785 			}
786 			else
787 			{
788 				startPosition=0;
789 				selectionLength=0;
790 				while(!fail&&GetSelectionAtOrAfterPosition(selectionUniverse,startPosition,&startPosition,&selectionLength))
791 				{
792 					if(FindAllForward(searchInText,true,true,startPosition,startPosition+selectionLength,searchRecord,resultSelectionUniverse,&hadMatch,&tempFirstMatchPosition,abortFunction))
793 					{
794 						if(!(*foundMatch))
795 						{
796 							*foundMatch=hadMatch;
797 							firstMatchPosition=tempFirstMatchPosition;
798 						}
799 					}
800 					else
801 					{
802 						fail=true;
803 					}
804 					startPosition+=selectionLength;
805 				}
806 			}
807 		}
808 		else
809 		{
810 			if(!limitScope)
811 			{
812 				GetSelectionEndPositions(selectionUniverse,&startPosition,&endPosition);		// find ends of old selection, or cursor position
813 				if(FindAllBackward(searchInText,true,startPosition==searchInText->totalBytes,0,startPosition,searchRecord,resultSelectionUniverse,foundMatch,&firstMatchPosition,abortFunction))
814 				{
815 					if(wrapAround)
816 					{
817 						fail=!FindAllBackward(searchInText,endPosition==0,true,endPosition,searchInText->totalBytes,searchRecord,resultSelectionUniverse,&hadMatch,&tempFirstMatchPosition,abortFunction);
818 						if(!(*foundMatch))
819 						{
820 							*foundMatch=hadMatch;
821 							firstMatchPosition=tempFirstMatchPosition;
822 						}
823 					}
824 				}
825 				else
826 				{
827 					fail=true;
828 				}
829 			}
830 			else
831 			{
832 				GetSelectionEndPositions(selectionUniverse,&startPosition,&endPosition);
833 				startPosition=endPosition;	// start at the end
834 
835 				while(!fail&&startPosition&&GetSelectionAtOrBeforePosition(selectionUniverse,startPosition-1,&startPosition,&selectionLength))
836 				{
837 					if(FindAllBackward(searchInText,true,true,startPosition,startPosition+selectionLength,searchRecord,resultSelectionUniverse,&hadMatch,&tempFirstMatchPosition,abortFunction))
838 					{
839 						if(!(*foundMatch))
840 						{
841 							*foundMatch=hadMatch;
842 							firstMatchPosition=tempFirstMatchPosition;
843 						}
844 					}
845 					else
846 					{
847 						fail=true;
848 					}
849 				}
850 			}
851 		}
852 		if(*foundMatch)																				// got something, so change result universe
853 		{
854 			SetSelectionCursorPosition(resultSelectionUniverse,firstMatchPosition);
855 		}
856 		DisposeSearchRecord(searchRecord);
857 		ShowNotBusy();
858 	}
859 	else
860 	{
861 		fail=true;
862 	}
863 	return(!fail);
864 }
865 
ReplaceRegisterReferences(TEXT_UNIVERSE * replaceWithUniverse,SEARCH_RECORD * searchedForText)866 static bool ReplaceRegisterReferences(TEXT_UNIVERSE *replaceWithUniverse,SEARCH_RECORD *searchedForText)
867 // Run through replaceWithUniverse looking for register specifications, if any are seen,
868 // replace them with what searchForText found
869 // if there is a problem, SetError, and return false
870 // NOTE: registers are specified in replaceWithUniverse using \0 through \9
871 // ignore any other \ in replaceWithUniverse
872 {
873 	CHUNK_HEADER
874 		*foundChunk,
875 		*currentChunk,
876 		*nextChunk;
877 	UINT32
878 		foundOffset,
879 		currentOffset,
880 		currentPosition,
881 		nextOffset;
882 	UINT32
883 		matchLength;
884 	UINT8
885 		character;
886 	bool
887 		fail;
888 
889 	fail=false;
890 	currentChunk=replaceWithUniverse->firstChunkHeader;
891 	currentPosition=currentOffset=0;
892 	while(currentChunk&&!fail)
893 	{
894 		if(currentChunk->data[currentOffset]!='\\')
895 		{
896 			currentPosition++;
897 			currentOffset++;
898 			if(currentOffset>currentChunk->totalBytes)
899 			{
900 				currentChunk=currentChunk->nextHeader;
901 				currentOffset=0;
902 			}
903 		}
904 		else
905 		{
906 			nextChunk=currentChunk;
907 			nextOffset=currentOffset+1;
908 			if(nextOffset>nextChunk->totalBytes)
909 			{
910 				nextChunk=nextChunk->nextHeader;
911 				nextOffset=0;
912 			}
913 			if(nextChunk)
914 			{
915 				character=nextChunk->data[nextOffset];
916 				if(character>='0'&&character<='9')
917 				{
918 					character-='0';														// make into an index
919 					if(DeleteUniverseText(replaceWithUniverse,currentPosition,2))		// remove the register specification
920 					{
921 						if(GetRERegisterMatchChunkAndOffset(searchedForText->expression,character,&foundChunk,&foundOffset,&matchLength))	// see if anything to insert
922 						{
923 							fail=!InsertUniverseChunks(replaceWithUniverse,currentPosition,foundChunk,foundOffset,matchLength);
924 							currentPosition+=matchLength;								// move past stuff just inserted
925 						}
926 						PositionToChunkPosition(replaceWithUniverse,currentPosition,&currentChunk,&currentOffset);		// locate place to continue looking
927 					}
928 					else
929 					{
930 						fail=true;
931 					}
932 				}
933 				else
934 				{
935 					currentChunk=nextChunk;		// push past the quote character
936 					currentOffset=nextOffset;
937 					currentPosition++;
938 				}
939 			}
940 			else
941 			{
942 				currentChunk=nextChunk;			// \ as last character, just terminate scan
943 				currentOffset=nextOffset;
944 			}
945 		}
946 	}
947 	return(!fail);
948 }
949 
CreateFoundVariable(EDITOR_BUFFER * replaceInUniverse,UINT32 replaceOffset,UINT32 replaceLength,SEARCH_RECORD * searchRecord)950 static bool CreateFoundVariable(EDITOR_BUFFER *replaceInUniverse,UINT32 replaceOffset,UINT32 replaceLength,SEARCH_RECORD *searchRecord)
951 // Create the "found" variable, and $n variables which are passed to the Tcl procedure as
952 // globals.
953 // If there is a problem, SetError, and return NULL
954 // NOTE: this routine, and ReplaceSelectedText are the ONLY routines in this file that
955 // rely on Tcl...
956 // if there is a problem, SetError, and return false
957 {
958 	CHUNK_HEADER
959 		*chunk,
960 		*foundChunk;
961 	UINT32
962 		offset,
963 		foundOffset;
964 	UINT32
965 		matchLength;
966 	char
967 		*locatedText;
968 	int
969 		varCount;
970 	char
971 		varName[32];
972 	bool
973 		fail;
974 
975 	fail=false;
976 	if((locatedText=(char *)MNewPtr(replaceLength+1)))	// make pointer to text that was located (this pointer will used to hold all the extracted text)
977 	{
978 		PositionToChunkPosition(replaceInUniverse->textUniverse,replaceOffset,&chunk,&offset);		// locate place where match was found
979 		if(ExtractUniverseText(replaceInUniverse->textUniverse,chunk,offset,(UINT8 *)locatedText,replaceLength,&chunk,&offset))
980 		{
981 			locatedText[replaceLength]='\0';			// terminate the string
982 			if(!Tcl_SetVar(tclInterpreter,"found",locatedText,TCL_LEAVE_ERR_MSG))
983 			{
984 				SetError("%s",Tcl_GetStringResult(tclInterpreter));
985 				fail=true;
986 			}
987 		}
988 		else
989 		{
990 			fail=true;
991 		}
992 		MDisposePtr(locatedText);
993 	}
994 	else
995 	{
996 		fail=true;
997 	}
998 
999 	if(!fail&&searchRecord->expression)			// if it is an expression, make a list of 10 more variables (one for each tagged subexpression)
1000 	{
1001 		for(varCount=0;!fail&&varCount<MAX_REGISTERS;varCount++)
1002 		{
1003 			sprintf(varName,"%d",varCount);		// create variable name
1004 
1005 			if(GetRERegisterMatchChunkAndOffset(searchRecord->expression,varCount,&foundChunk,&foundOffset,&matchLength))	// see if anything to add
1006 			{
1007 				if((locatedText=(char *)MNewPtr(replaceLength+1)))		// make pointer to the text
1008 				{
1009 					if(ExtractUniverseText(replaceInUniverse->textUniverse,foundChunk,foundOffset,(UINT8 *)locatedText,matchLength,&chunk,&offset))
1010 					{
1011 						locatedText[matchLength]='\0';					// terminate the string
1012 						if(!Tcl_SetVar(tclInterpreter,varName,locatedText,TCL_LEAVE_ERR_MSG))
1013 						{
1014 							SetError("%s",Tcl_GetStringResult(tclInterpreter));
1015 							fail=true;
1016 						}
1017 					}
1018 					else
1019 					{
1020 						fail=true;
1021 					}
1022 					MDisposePtr(locatedText);
1023 				}
1024 				else
1025 				{
1026 					fail=true;
1027 				}
1028 			}
1029 			else
1030 			{
1031 				if(!Tcl_SetVar(tclInterpreter,varName,"",TCL_LEAVE_ERR_MSG))
1032 				{
1033 					SetError("%s",Tcl_GetStringResult(tclInterpreter));
1034 					fail=true;
1035 				}
1036 			}
1037 		}
1038 	}
1039 	return(!fail);
1040 }
1041 
ReplaceSearchedText(EDITOR_BUFFER * replaceInUniverse,UINT32 replaceOffset,UINT32 replaceLength,CHUNK_HEADER ** replaceAtChunk,UINT32 * replaceAtChunkOffset,SEARCH_RECORD * searchRecord,UINT32 * bytesReplaced)1042 static bool ReplaceSearchedText(EDITOR_BUFFER *replaceInUniverse,UINT32 replaceOffset,UINT32 replaceLength,CHUNK_HEADER **replaceAtChunk,UINT32 *replaceAtChunkOffset,SEARCH_RECORD *searchRecord,UINT32 *bytesReplaced)
1043 // Given text that was just searched for by searchForText, replace it
1044 // based on the search type
1045 // if there is a problem, attempt to leave the text unmodified
1046 // SetError, and return false
1047 {
1048 	bool
1049 		fail;
1050 	UINT32
1051 		length;
1052 	TEXT_UNIVERSE
1053 		*alternateUniverse;
1054 	char
1055 		*stringResult;
1056 
1057 	fail=false;
1058 	if(searchRecord->procedure)								// handle replacement by calling a Tcl procedure
1059 	{
1060 		if(CreateFoundVariable(replaceInUniverse,replaceOffset,replaceLength,searchRecord))
1061 		{
1062 			if(Tcl_Eval(tclInterpreter,searchRecord->procedure)==TCL_OK)
1063 			{
1064 				stringResult=(char *)Tcl_GetStringResult(tclInterpreter);
1065 				length=strlen(stringResult);
1066 				fail=!ReplaceEditorText(replaceInUniverse,replaceOffset,replaceOffset+replaceLength,(UINT8 *)stringResult,length,false);
1067 				(*bytesReplaced)=length;
1068 			}
1069 			else
1070 			{
1071 				SetError("%s",Tcl_GetStringResult(tclInterpreter));
1072 				fail=true;
1073 			}
1074 			Tcl_ResetResult(tclInterpreter);				// do not leave any Tcl result around (other stuff may append to it)
1075 		}
1076 		else
1077 		{
1078 			fail=true;
1079 		}
1080 	}
1081 	else
1082 	{
1083 		if(searchRecord->expression)
1084 		{
1085 			if((alternateUniverse=OpenTextUniverse()))		// create alternate universe where we make the real text to replace
1086 			{
1087 				if(InsertUniverseChunks(alternateUniverse,alternateUniverse->totalBytes,searchRecord->replaceWithText->firstChunkHeader,0,searchRecord->replaceWithText->totalBytes))	// copy replace with text into alternate universe
1088 				{
1089 					if(ReplaceRegisterReferences(alternateUniverse,searchRecord))
1090 					{
1091 						fail=!ReplaceEditorChunks(replaceInUniverse,replaceOffset,replaceOffset+replaceLength,alternateUniverse->firstChunkHeader,0,alternateUniverse->totalBytes,false);
1092 						(*bytesReplaced)=alternateUniverse->totalBytes;
1093 					}
1094 					else
1095 					{
1096 						fail=true;
1097 					}
1098 				}
1099 				else
1100 				{
1101 					fail=true;
1102 				}
1103 				CloseTextUniverse(alternateUniverse);
1104 			}
1105 			else
1106 			{
1107 				fail=true;
1108 			}
1109 		}
1110 		else
1111 		{
1112 			fail=!ReplaceEditorChunks(replaceInUniverse,replaceOffset,replaceOffset+replaceLength,searchRecord->replaceWithText->firstChunkHeader,0,searchRecord->replaceWithText->totalBytes,false);
1113 			(*bytesReplaced)=searchRecord->replaceWithText->totalBytes;
1114 		}
1115 	}
1116 	return(!fail);
1117 }
1118 
EditorReplace(EDITOR_BUFFER * buffer,SELECTION_UNIVERSE * selectionUniverse,TEXT_UNIVERSE * searchForText,TEXT_UNIVERSE * replaceWithText,bool backward,bool wrapAround,bool selectionExpr,bool ignoreCase,bool replaceProc,bool * foundMatch,SELECTION_UNIVERSE * resultSelectionUniverse,ABORT_TEST_FUNCTION * abortFunction)1119 bool EditorReplace(EDITOR_BUFFER *buffer,SELECTION_UNIVERSE *selectionUniverse,TEXT_UNIVERSE *searchForText,TEXT_UNIVERSE *replaceWithText,bool backward,bool wrapAround,bool selectionExpr,bool ignoreCase,bool replaceProc,bool *foundMatch,SELECTION_UNIVERSE *resultSelectionUniverse,ABORT_TEST_FUNCTION *abortFunction)
1120 // Find searchForText in buffer, starting at the cursor position, or the _start_ of
1121 // the current selection of selectionUniverse
1122 // if searchForText is not located, return false in foundMatch
1123 // if it is located, replace what was found with replaceWithText, modify resultSelectionUniverse to
1124 // be the selection of what was replaced, and return true in foundMatch
1125 // if there is a problem, SetError, and return false
1126 // NOTE: resultSelectionUniverse must be different from selectionUniverse
1127 {
1128 	CHUNK_HEADER
1129 		*inChunk,
1130 		*startChunk,
1131 		*endChunk;
1132 	UINT32
1133 		inOffset,
1134 		startOffset,
1135 		endOffset;
1136 	SEARCH_RECORD
1137 		*searchRecord;
1138 	UINT32
1139 		startPosition,
1140 		endPosition,
1141 		bytesReplaced;
1142 	UINT32
1143 		locatedPosition,
1144 		locatedLength;
1145 	TEXT_UNIVERSE
1146 		*searchInText;
1147 	bool
1148 		fail;
1149 
1150 	fail=*foundMatch=false;
1151 	if((searchRecord=CreateSearchRecord(searchForText,replaceWithText,selectionExpr,ignoreCase,replaceProc)))
1152 	{
1153 		ShowBusy();
1154 		searchInText=buffer->textUniverse;
1155 
1156 		GetSelectionEndPositions(selectionUniverse,&startPosition,&endPosition);		// find ends of old selection, or cursor position
1157 		if(!backward)
1158 		{
1159 			PositionToChunkPosition(searchInText,startPosition,&inChunk,&inOffset);		// locate place to start the search
1160 			if(SearchForward(inChunk,inOffset,searchRecord,startPosition==0,true,searchInText->totalBytes-startPosition,foundMatch,&locatedPosition,&locatedLength,&startChunk,&startOffset,&endChunk,&endOffset,abortFunction))
1161 			{
1162 				locatedPosition+=startPosition;											// make located position absolute
1163 				if(!(*foundMatch)&&wrapAround)
1164 				{
1165 					if(!SearchForward(searchInText->firstChunkHeader,0,searchRecord,true,startPosition==searchInText->totalBytes,startPosition,foundMatch,&locatedPosition,&locatedLength,&startChunk,&startOffset,&endChunk,&endOffset,abortFunction))
1166 					{
1167 						fail=true;
1168 					}
1169 				}
1170 			}
1171 			else
1172 			{
1173 				fail=true;
1174 			}
1175 		}
1176 		else
1177 		{
1178 			PositionToChunkPositionPastEnd(searchInText,endPosition,&inChunk,&inOffset);	// locate place to start the search
1179 			if(SearchBackward(inChunk,inOffset,searchRecord,true,endPosition==searchInText->totalBytes,endPosition,foundMatch,&locatedPosition,&locatedLength,&startChunk,&startOffset,&endChunk,&endOffset,abortFunction))
1180 			{
1181 				locatedPosition=endPosition-locatedPosition;							// make located position absolute
1182 				if(!(*foundMatch)&&wrapAround)
1183 				{
1184 					PositionToChunkPositionPastEnd(searchInText,searchInText->totalBytes,&inChunk,&inOffset);	// locate place to start the search
1185 					if(SearchBackward(inChunk,inOffset,searchRecord,endPosition==0,true,searchInText->totalBytes-endPosition,foundMatch,&locatedPosition,&locatedLength,&startChunk,&startOffset,&endChunk,&endOffset,abortFunction))
1186 					{
1187 						locatedPosition=searchInText->totalBytes-locatedPosition;
1188 					}
1189 					else
1190 					{
1191 						fail=true;
1192 					}
1193 				}
1194 			}
1195 			else
1196 			{
1197 				fail=true;
1198 			}
1199 		}
1200 		if(!fail&&(*foundMatch))
1201 		{
1202 			BeginUndoGroup(buffer);
1203 			EditorStartReplace(buffer);
1204 			if(!ReplaceSearchedText(buffer,locatedPosition,locatedLength,&startChunk,&startOffset,searchRecord,&bytesReplaced))
1205 			{
1206 				bytesReplaced=0;	// if the replace failed for some reason, select nothing
1207 				fail=true;			// report the failure
1208 			}
1209 			EditorEndReplace(buffer);
1210 			StrictEndUndoGroup(buffer);
1211 
1212 			DeleteAllSelections(resultSelectionUniverse);	// remove all old selections
1213 			if(SetSelectionRange(resultSelectionUniverse,locatedPosition,bytesReplaced))
1214 			{
1215 				SetSelectionCursorPosition(resultSelectionUniverse,locatedPosition);
1216 			}
1217 			else
1218 			{
1219 				fail=true;
1220 			}
1221 		}
1222 		DisposeSearchRecord(searchRecord);
1223 		ShowNotBusy();
1224 	}
1225 	else
1226 	{
1227 		fail=true;
1228 	}
1229 	return(!fail);
1230 }
1231 
ReplaceAllForward(EDITOR_BUFFER * buffer,bool leftEdge,bool rightEdge,UINT32 lowPosition,UINT32 * highPosition,SEARCH_RECORD * searchRecord,SELECTION_UNIVERSE * selectionUniverse,bool * foundMatch,UINT32 * firstMatchPosition,ABORT_TEST_FUNCTION * abortFunction)1232 static bool ReplaceAllForward(EDITOR_BUFFER *buffer,bool leftEdge,bool rightEdge,UINT32 lowPosition,UINT32 *highPosition,SEARCH_RECORD *searchRecord,SELECTION_UNIVERSE *selectionUniverse,bool *foundMatch,UINT32 *firstMatchPosition,ABORT_TEST_FUNCTION *abortFunction)
1233 // Replace all occurrences of searchRecord between lowPosition, and highPosition, adding the selections
1234 // to selectionUniverse before selectionChunk, selectionOffset
1235 // if there is a problem, SetError and return false
1236 // if a matches were found, return true in foundMatch, and the position of the first match found
1237 // in firstMatchPosition
1238 // highPosition is modified to reflect the new highPosition after all replacements have been made
1239 // NOTE: even if false is returned, foundMatch, highPosition, and selectionUniverse are
1240 // valid
1241 {
1242 	bool
1243 		fail;
1244 	CHUNK_HEADER
1245 		*inChunk,
1246 		*startChunk;
1247 	UINT32
1248 		inOffset,
1249 		startOffset;
1250 	bool
1251 		matching;
1252 	TEXT_UNIVERSE
1253 		*searchInText;
1254 	UINT32
1255 		nextSearchPosition,
1256 		locatedPosition,
1257 		locatedLength,
1258 		bytesReplaced;
1259 	UINT32
1260 		newHighPosition;
1261 
1262 	fail=*foundMatch=false;
1263 	searchInText=buffer->textUniverse;
1264 	nextSearchPosition=lowPosition;
1265 	matching=true;
1266 	newHighPosition=*highPosition;
1267 	while(!fail&&matching)
1268 	{
1269 		PositionToChunkPosition(searchInText,nextSearchPosition,&inChunk,&inOffset);	// locate place to start the search
1270 
1271 		if(nextSearchPosition!=lowPosition)
1272 		{
1273 			leftEdge=false;													// no longer at left edge
1274 		}
1275 
1276 		if(SearchForward(inChunk,inOffset,searchRecord,leftEdge,rightEdge,newHighPosition-nextSearchPosition,&matching,&locatedPosition,&locatedLength,&startChunk,&startOffset,&inChunk,&inOffset,abortFunction))
1277 		{
1278 			if(matching)													// see if a match was found, if not, we are done
1279 			{
1280 				if(locatedLength)											// finding an empty string is fatal to replace all
1281 				{
1282 					locatedPosition+=nextSearchPosition;					// make located position absolute
1283 					if(!(*foundMatch))
1284 					{
1285 						(*firstMatchPosition)=locatedPosition;				// remember the first match position so we can home to it when done
1286 						(*foundMatch)=true;
1287 					}
1288 					if(ReplaceSearchedText(buffer,locatedPosition,locatedLength,&startChunk,&startOffset,searchRecord,&bytesReplaced))
1289 					{
1290 						inChunk=startChunk;
1291 						inOffset=startOffset;
1292 						nextSearchPosition=locatedPosition+bytesReplaced;	// move to the new position after the replacement
1293 						newHighPosition-=locatedLength;						// subtract off bytes that were replaced
1294 						newHighPosition+=bytesReplaced;						// add back number that replaced them
1295 
1296 						DeleteSelectionRange(selectionUniverse,locatedPosition,locatedLength);
1297 						if(InsertSelectionRange(selectionUniverse,locatedPosition,bytesReplaced))
1298 						{
1299 							fail=!SetSelectionRange(selectionUniverse,locatedPosition,bytesReplaced);
1300 						}
1301 						else
1302 						{
1303 							fail=true;
1304 						}
1305 					}
1306 					else
1307 					{
1308 						fail=true;
1309 					}
1310 				}
1311 				else
1312 				{
1313 					SetError(errorDescriptions[MATCHED_NOTHING]);
1314 					fail=true;
1315 				}
1316 			}
1317 		}
1318 		else
1319 		{
1320 			fail=true;
1321 		}
1322 	}
1323 	*highPosition=newHighPosition;
1324 	return(!fail);
1325 }
1326 
ReplaceAllBackward(EDITOR_BUFFER * buffer,bool leftEdge,bool rightEdge,UINT32 lowPosition,UINT32 * highPosition,SEARCH_RECORD * searchRecord,SELECTION_UNIVERSE * selectionUniverse,bool * foundMatch,UINT32 * firstMatchPosition,ABORT_TEST_FUNCTION * abortFunction)1327 static bool ReplaceAllBackward(EDITOR_BUFFER *buffer,bool leftEdge,bool rightEdge,UINT32 lowPosition,UINT32 *highPosition,SEARCH_RECORD *searchRecord,SELECTION_UNIVERSE *selectionUniverse,bool *foundMatch,UINT32 *firstMatchPosition,ABORT_TEST_FUNCTION *abortFunction)
1328 // Replace all occurrences of searchRecord between lowPosition, and highPosition, adding the selections
1329 // to selectionUniverse
1330 // if there is a problem, SetError and return false
1331 // if a matches were found, return true in foundMatch, and the position of the first match found
1332 // in firstMatchPosition
1333 // highPosition is modified to reflect the new highPosition after all replacements have been made
1334 // NOTE: even if false is returned, foundMatch, highPosition, and selectionUniverse are
1335 // valid
1336 {
1337 	bool
1338 		fail;
1339 	CHUNK_HEADER
1340 		*inChunk,
1341 		*endChunk;
1342 	UINT32
1343 		inOffset,
1344 		endOffset;
1345 	bool
1346 		matching;
1347 	TEXT_UNIVERSE
1348 		*searchInText;
1349 	UINT32
1350 		nextSearchPosition,
1351 		locatedPosition,
1352 		locatedLength,
1353 		bytesReplaced;
1354 	UINT32
1355 		newHighPosition;
1356 
1357 	fail=*foundMatch=false;
1358 	searchInText=buffer->textUniverse;
1359 	nextSearchPosition=*highPosition;
1360 	matching=true;
1361 	newHighPosition=*highPosition;
1362 	while(!fail&&matching)
1363 	{
1364 		PositionToChunkPositionPastEnd(searchInText,nextSearchPosition,&inChunk,&inOffset);		// locate place to start the search
1365 
1366 		if(nextSearchPosition!=newHighPosition)
1367 		{
1368 			rightEdge=false;												// no longer at the right edge
1369 		}
1370 
1371 		if(SearchBackward(inChunk,inOffset,searchRecord,leftEdge,rightEdge,nextSearchPosition-lowPosition,&matching,&locatedPosition,&locatedLength,&inChunk,&inOffset,&endChunk,&endOffset,abortFunction))
1372 		{
1373 			if(matching)													// see if a match was found, if not, we are done
1374 			{
1375 				if(locatedLength)											// finding an empty string is fatal to replace all
1376 				{
1377 					locatedPosition=nextSearchPosition-locatedPosition;		// make located position absolute
1378 					nextSearchPosition=locatedPosition;
1379 					if(!(*foundMatch))										// if no matches found so far ...
1380 					{
1381 						(*firstMatchPosition)=locatedPosition;				// remember end of selections (sort of, will be adjusted at the end)
1382 						(*foundMatch)=true;
1383 					}
1384 					if(ReplaceSearchedText(buffer,locatedPosition,locatedLength,&inChunk,&inOffset,searchRecord,&bytesReplaced))
1385 					{
1386 						newHighPosition-=locatedLength;						// subtract off bytes that were replaced
1387 						newHighPosition+=bytesReplaced;						// add back number that replaced them
1388 
1389 						DeleteSelectionRange(selectionUniverse,locatedPosition,locatedLength);
1390 						if(InsertSelectionRange(selectionUniverse,locatedPosition,bytesReplaced))
1391 						{
1392 							fail=!SetSelectionRange(selectionUniverse,locatedPosition,bytesReplaced);
1393 						}
1394 						else
1395 						{
1396 							fail=true;
1397 						}
1398 					}
1399 					else
1400 					{
1401 						fail=true;
1402 					}
1403 				}
1404 				else
1405 				{
1406 					SetError(errorDescriptions[MATCHED_NOTHING]);
1407 					fail=true;
1408 				}
1409 			}
1410 		}
1411 		else
1412 		{
1413 			fail=true;
1414 		}
1415 	}
1416 	*firstMatchPosition+=newHighPosition-(*highPosition);
1417 	*highPosition=newHighPosition;
1418 	return(!fail);
1419 }
1420 
EditorReplaceAll(EDITOR_BUFFER * buffer,SELECTION_UNIVERSE * selectionUniverse,TEXT_UNIVERSE * searchForText,TEXT_UNIVERSE * replaceWithText,bool backward,bool wrapAround,bool selectionExpr,bool ignoreCase,bool limitScope,bool replaceProc,bool * foundMatch,SELECTION_UNIVERSE * resultSelectionUniverse,ABORT_TEST_FUNCTION * abortFunction)1421 bool EditorReplaceAll(EDITOR_BUFFER *buffer,SELECTION_UNIVERSE *selectionUniverse,TEXT_UNIVERSE *searchForText,TEXT_UNIVERSE *replaceWithText,bool backward,bool wrapAround,bool selectionExpr,bool ignoreCase,bool limitScope,bool replaceProc,bool *foundMatch,SELECTION_UNIVERSE *resultSelectionUniverse,ABORT_TEST_FUNCTION *abortFunction)
1422 // Replace all occurrences of searchForText with replaceWithText using the given rules, if none are found,
1423 // return false in foundMatch
1424 // if foundMatch is true, modify resultSelectionUniverse to be the selection of all replaced text
1425 // if there is a problem, SetError, return false
1426 // NOTE: resultSelectionUniverse must be different from selectionUniverse
1427 {
1428 	SEARCH_RECORD
1429 		*searchRecord;
1430 	INT32
1431 		replaceOffset;
1432 	UINT32
1433 		startPosition,
1434 		endPosition,
1435 		selectionLength;
1436 	UINT32
1437 		firstMatchPosition,
1438 		tempFirstMatchPosition,
1439 		highPosition;
1440 	TEXT_UNIVERSE
1441 		*searchInText;
1442 	bool
1443 		hadMatch,
1444 		fail;
1445 	SELECTION_UNIVERSE
1446 		*oldSelectionUniverse;
1447 
1448 	fail=*foundMatch=false;
1449 	if((searchRecord=CreateSearchRecord(searchForText,replaceWithText,selectionExpr,ignoreCase,replaceProc)))
1450 	{
1451 		ShowBusy();
1452 		if((oldSelectionUniverse=OpenSelectionUniverse()))						// copy this, since it must not change during the replacement
1453 		{
1454 			if(CopySelectionUniverse(selectionUniverse,oldSelectionUniverse))	// copy the old one into it
1455 			{
1456 				searchInText=buffer->textUniverse;
1457 				firstMatchPosition=0;
1458 				BeginUndoGroup(buffer);
1459 				EditorStartReplace(buffer);
1460 				if(!backward)
1461 				{
1462 					if(!limitScope)
1463 					{
1464 						GetSelectionEndPositions(oldSelectionUniverse,&startPosition,&endPosition);		// find ends of old selection, or cursor position
1465 						highPosition=searchInText->totalBytes;
1466 						if(ReplaceAllForward(buffer,startPosition==0,true,startPosition,&highPosition,searchRecord,resultSelectionUniverse,foundMatch,&firstMatchPosition,abortFunction))
1467 						{
1468 							if(wrapAround)
1469 							{
1470 								highPosition=startPosition;
1471 								fail=!ReplaceAllForward(buffer,true,highPosition==searchInText->totalBytes,0,&highPosition,searchRecord,resultSelectionUniverse,&hadMatch,&tempFirstMatchPosition,abortFunction);
1472 								if(!(*foundMatch))
1473 								{
1474 									*foundMatch=hadMatch;
1475 									firstMatchPosition=tempFirstMatchPosition;
1476 								}
1477 								else
1478 								{
1479 									firstMatchPosition+=highPosition-startPosition;	// move home position due to text changes above
1480 								}
1481 							}
1482 						}
1483 						else
1484 						{
1485 							fail=true;
1486 						}
1487 					}
1488 					else
1489 					{
1490 						replaceOffset=0;
1491 						startPosition=0;
1492 						selectionLength=0;
1493 						while(!fail&&GetSelectionAtOrAfterPosition(oldSelectionUniverse,startPosition,&startPosition,&selectionLength))
1494 						{
1495 							endPosition=startPosition+replaceOffset+selectionLength;
1496 							if(ReplaceAllForward(buffer,true,true,startPosition+replaceOffset,&endPosition,searchRecord,resultSelectionUniverse,&hadMatch,&tempFirstMatchPosition,abortFunction))
1497 							{
1498 								replaceOffset+=endPosition-(startPosition+replaceOffset+selectionLength);
1499 								if(!(*foundMatch))
1500 								{
1501 									*foundMatch=hadMatch;
1502 									firstMatchPosition=tempFirstMatchPosition;
1503 								}
1504 							}
1505 							else
1506 							{
1507 								fail=true;
1508 							}
1509 							startPosition+=selectionLength;
1510 						}
1511 					}
1512 				}
1513 				else
1514 				{
1515 					if(!limitScope)
1516 					{
1517 						GetSelectionEndPositions(oldSelectionUniverse,&startPosition,&endPosition);		// find ends of old selection, or cursor position
1518 						highPosition=endPosition;
1519 						if(ReplaceAllBackward(buffer,true,highPosition==searchInText->totalBytes,0,&highPosition,searchRecord,resultSelectionUniverse,foundMatch,&firstMatchPosition,abortFunction))
1520 						{
1521 							if(wrapAround)
1522 							{
1523 								endPosition=highPosition;
1524 								highPosition=searchInText->totalBytes;
1525 								fail=!ReplaceAllBackward(buffer,endPosition==0,true,endPosition,&highPosition,searchRecord,resultSelectionUniverse,&hadMatch,&tempFirstMatchPosition,abortFunction);
1526 								if(!(*foundMatch))
1527 								{
1528 									*foundMatch=hadMatch;
1529 									firstMatchPosition=tempFirstMatchPosition;
1530 								}
1531 							}
1532 						}
1533 						else
1534 						{
1535 							fail=true;
1536 						}
1537 					}
1538 					else
1539 					{
1540 						GetSelectionEndPositions(selectionUniverse,&startPosition,&endPosition);
1541 						startPosition=endPosition;	// start at the end
1542 
1543 						while(!fail&&startPosition&&GetSelectionAtOrBeforePosition(selectionUniverse,startPosition-1,&startPosition,&selectionLength))
1544 						{
1545 							endPosition=startPosition+selectionLength;
1546 							highPosition=endPosition;
1547 							if(ReplaceAllBackward(buffer,true,true,startPosition,&highPosition,searchRecord,resultSelectionUniverse,&hadMatch,&tempFirstMatchPosition,abortFunction))
1548 							{
1549 								if(!(*foundMatch))
1550 								{
1551 									*foundMatch=hadMatch;
1552 									firstMatchPosition=tempFirstMatchPosition;
1553 								}
1554 								else
1555 								{
1556 									firstMatchPosition+=highPosition-endPosition;		// adjust for any replacements just made
1557 								}
1558 							}
1559 							else
1560 							{
1561 								fail=true;
1562 							}
1563 						}
1564 					}
1565 				}
1566 				EditorEndReplace(buffer);
1567 				StrictEndUndoGroup(buffer);
1568 				if(*foundMatch)																	// got something, so change selection universe
1569 				{
1570 					SetSelectionCursorPosition(resultSelectionUniverse,firstMatchPosition);		// put the cursor at the start
1571 				}
1572 			}
1573 			else
1574 			{
1575 				fail=true;
1576 			}
1577 			CloseSelectionUniverse(oldSelectionUniverse);									// close this universe
1578 		}
1579 		else
1580 		{
1581 			fail=true;
1582 		}
1583 		DisposeSearchRecord(searchRecord);
1584 		ShowNotBusy();
1585 	}
1586 	else
1587 	{
1588 		fail=true;
1589 	}
1590 	return(!fail);
1591 }
1592