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,¤tChunk,¤tOffset); // 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