This is MiscSearchText.m in view mode; [Download] [Up]
/*
* Copyright (c) 1993 Christopher J. Kane. All rights reserved.
*
* This software is subject to the terms of the MiscKit license
* agreement. Refer to the license document included with the
* MiscKit distribution for these terms.
*
* Version: 1.2 (25 June 1994)
*
* Licinda Woudberg - licinda@Black_Albatross.otago.ac.nz
* 25 June 1994
* Changed: -replaceAll.... works so that it can handle both plain
* text & rtf style document. Still need to double check rtfd's
* Changed: -replaceSelection:
* now sends textDidChange: notice to the delegate
*/
#import <misckit/MiscSearchText.h>
#import <misckit/MiscTBMK.h>
#import <misckit/regexpr.h>
@implementation Text (SearchText)
- (oneway void)makeSelectionVisible
{
[self scrollSelToVisible];
}
- (int)replaceAll:(const char *)pattern with:(const char *)replacement mode:(SearchMode)mode regexpr:(BOOL)regexpr cases:(BOOL)cases
{
unsigned char fm[256], tr[256];
struct re_pattern_buffer rpat;
Misc_TBMKpattern lpat = NULL;
NXStream *in_strm = NULL;
int s1=0, e1=0, s2=0, e2=0, ret_val=0, plen=0, rlen=0, pos=0, size=0, p=0, searchTextMaxSize=0;
/* Known bug: the delegate will not get textWillChange:, textDidChange: notification messages */
if ( (sp0.cp < 0) && (mode != TextEdgeToTextEdge) )
return SEARCH_NO_SELECTION;
if ( ![self isEditable] )
return SEARCH_CANNOT_WRITE;
switch ( mode ) /* setup start and end points for the search */
{
case TextEdgeToTextEdge:
case SelStartToSelStart:
case SelEndToSelEnd:
s1 = 0; e1 = textLength; break;
case TextEdgeToSelStart:
s1 = 0; e1 = sp0.cp; break;
case TextEdgeToSelEnd:
s1 = 0; e1 = spN.cp; break;
case SelStartToSelEnd:
s1 = sp0.cp; e1 = spN.cp - sp0.cp; break;
case SelStartToTextEdge:
s1 = sp0.cp; e1 = textLength - sp0.cp; break;
case SelEndToTextEdge:
s1 = spN.cp; e1 = textLength - spN.cp; break;
case SelEndToSelStart:
s1 = 0; e1 = sp0.cp;
s2 = spN.cp; e2 = textLength - spN.cp; break;
default:
return SEARCH_INVALID_ARGUMENT;
}
searchTextMaxSize = s1 + e1;
plen = strlen(pattern); /* pattern length */
rlen = strlen(replacement); /* replacement length */
if (regexpr)
{
/* dealing with a regular expression */
char *str;
int i;
memset(&rpat, 0, sizeof(rpat));
for ( i = 256; i--; )
tr[i] = i;
if (!cases)
for ( i = 'A'; i <= 'Z'; i++ )
tr[i] = i - 'A' + 'a';
rpat.translate = tr;
rpat.fastmap = fm;
str = re_compile_pattern((char *)pattern, plen, &rpat);
if (str != NULL)
return (strcmp(str, "Out of memory") ? SEARCH_INVALID_REGEXPR : SEARCH_INTERNAL_ERROR);
}
else
{
/* dealing with a normal find & replace - setup the Pattern to search for */
lpat = Misc_TBMKpattern_alloc(pattern, plen, 0, !cases);
if (lpat == NULL)
return SEARCH_INTERNAL_ERROR;
}
in_strm = NXOpenMemory(NULL, 0, NX_READWRITE); /* open the stream */
if (in_strm == NULL)
{
ret_val = SEARCH_INTERNAL_ERROR;
goto exit;
}
[self writeText:in_strm]; /* read the text into the stream */
NXSeek(in_strm, 0, NX_FROMSTART); /* start at the begining */
ret_val = 0; /* set the counter to zero */
start_searching:
if (NXUserAborted())
{
ret_val = SEARCH_ABORTED;
goto exit;
}
if (regexpr)
{
/* regular expression search */
p = -1;
if ( s1 > e1)
/* reached the end of the search area */
goto exit;
if ( s1 >= 0 )
{
searchTextMaxSize = s1 + e1;
p = re_search_pattern(&rpat, in_strm->buf_base, searchTextMaxSize, s1, e1, 0);
}
if (p==-1 && e2!=0)
{
s1 = s2;
e1 = e2;
s2 = e2 = 0;
goto start_searching;
}
if (p==-2)
{
ret_val = SEARCH_INTERNAL_ERROR;
goto exit;
}
if (p>-1)
{
int newTextPos=0;
pos = p;
size = re_match_pattern(&rpat, in_strm->buf_base, searchTextMaxSize, pos, 0);
if (size<0)
{
ret_val = SEARCH_INTERNAL_ERROR;
goto exit;
}
newTextPos = (ret_val * (rlen - size)) + pos;
[self setSel:newTextPos :(newTextPos+size) ];
[self makeSelectionVisible];
[self replaceSel:replacement];
s1 = p + rlen;
ret_val++;
goto start_searching;
}
}
else
{
/* normal find & replace search */
p = 0;
if (s1 > e1)
/* reached the end of the search area */
goto exit;
if (s1 >= 0)
p = Misc_TBMKsearch_memory(lpat, in_strm->buf_base + s1, e1, 0, &pos);
if (p < 0)
{
/* couldn't find a match for some reason */
ret_val = SEARCH_INTERNAL_ERROR;
goto exit;
}
if (p == 0 && e2 != 0)
{
/* couldn't find a match in the 1st selection area - there is a 2nd area to check */
s1 = s2;
e1 = e2;
s2 = e2 = 0;
goto start_searching;
}
if (p > 0)
{
/* found a match */
int newTextPos=0;
pos = pos + s1; /* set selection variables */
newTextPos = (ret_val * (rlen - plen)) + pos;
size = plen;
ret_val++;
[self setSel:newTextPos :(newTextPos+size) ]; /* select the area */
[self makeSelectionVisible];
[self replaceSel:replacement]; /* replace the area */
s1 = pos + plen; /* reset the start position */
goto start_searching;
}
}
exit:
Misc_TBMKpattern_free(&lpat);
if (in_strm != NULL)
NXCloseMemory(in_strm, NX_FREEBUFFER);
return ret_val;
}
- (oneway void)replaceSelection:(const char *)replacement
{
if ([self isEditable]) {
if (delegate && [delegate respondsTo:@selector(textWillChange:)])
[delegate textWillChange:self];
[self replaceSel:replacement];
if (delegate && [delegate respondsTo:@selector(textDidChange:)])
[delegate textDidChange:self];
}
}
- (int)searchFor:(const char *)pattern mode:(SearchMode)mode reverse:(BOOL)rev regexpr:(BOOL)regexpr cases:(BOOL)cases position:(out int *)pos size:(out int *)size
{
unsigned char fm[256], tr[256];
struct re_pattern_buffer rpat;
Misc_TBMKpattern lpat=NULL;
NXStream *in_strm=NULL;
int s1=0, e1=0, s2=0, e2=0, ret_val, plen, p, position;
if (sp0.cp<0 && mode!=TextEdgeToTextEdge)
return SEARCH_NO_SELECTION;
if (rev)
switch (mode)
{
case TextEdgeToSelStart: s1 = textLength-1; e1 = sp0.cp-textLength; break;
case TextEdgeToSelEnd: s1 = textLength-1; e1 = spN.cp-textLength; break;
case TextEdgeToTextEdge: s1 = textLength-1; e1 = -textLength; break;
case SelStartToSelEnd: s1 = sp0.cp-1; e1 = -sp0.cp+1; s2 = textLength-1; e2 = spN.cp-textLength+1; break;
case SelStartToTextEdge: s1 = sp0.cp-1; e1 = -sp0.cp+1; break;
case SelStartToSelStart: s1 = sp0.cp-1; e1 = -sp0.cp+1; s2 = textLength-1; e2 = sp0.cp-textLength+1; break;
case SelEndToTextEdge: s1 = spN.cp-1; e1 = -spN.cp+1; break;
case SelEndToSelStart: s1 = spN.cp-1; e1 = sp0.cp-spN.cp+1; break;
case SelEndToSelEnd: s1 = spN.cp-1; e1 = -spN.cp+1; s2 = textLength-1; e2 = spN.cp-textLength+1; break;
default: return SEARCH_INVALID_ARGUMENT;
}
else
switch (mode)
{
case TextEdgeToSelStart: s1 = 0; e1 = sp0.cp; break;
case TextEdgeToSelEnd: s1 = 0; e1 = spN.cp; break;
case TextEdgeToTextEdge: s1 = 0; e1 = textLength; break;
case SelStartToSelEnd: s1 = sp0.cp; e1 = spN.cp-sp0.cp; break;
case SelStartToTextEdge: s1 = sp0.cp; e1 = textLength-sp0.cp; break;
case SelStartToSelStart: s1 = sp0.cp; e1 = textLength-sp0.cp; s2 = 0; e2 = sp0.cp; break;
case SelEndToTextEdge: s1 = spN.cp; e1 = textLength-spN.cp; break;
case SelEndToSelStart: s1 = spN.cp; e1 = textLength-spN.cp; s2 = 0; e2 = sp0.cp; break;
case SelEndToSelEnd: s1 = spN.cp; e1 = textLength-spN.cp; s2 = 0; e2 = spN.cp; break;
default: return SEARCH_INVALID_ARGUMENT;
}
plen = strlen(pattern);
if (regexpr)
{
char *str;
int i;
memset(&rpat, 0, sizeof(rpat));
for(i=256; i--;)
tr[i] = i;
if (!cases)
for(i='A'; i<='Z'; i++) tr[i] = i-'A'+'a';
rpat.translate = tr;
rpat.fastmap = fm;
str = re_compile_pattern((char *)pattern, plen, &rpat);
if (str!=NULL)
return (strcmp(str, "Out of memory")?SEARCH_INVALID_REGEXPR:SEARCH_INTERNAL_ERROR);
}
else
{
lpat = Misc_TBMKpattern_alloc(pattern, plen, rev, !cases);
if (lpat==NULL)
return SEARCH_INTERNAL_ERROR;
}
in_strm = NXOpenMemory(NULL, 0, NX_READWRITE);
if (in_strm==NULL)
{
ret_val = SEARCH_INTERNAL_ERROR;
goto exit;
}
[self writeText:in_strm];
NXSeek(in_strm, 0, NX_FROMSTART);
ret_val = 0;
start_searching:
if (NXUserAborted())
{
ret_val = SEARCH_ABORTED;
goto exit;
}
if (regexpr)
{
p = -1;
if (s1>=0)
p = re_search_pattern(&rpat, in_strm->buf_base, textLength, s1, e1, 0);
if (p==-1 && e2!=0)
{
s1 = s2;
e1 = e2;
s2 = e2 = 0;
goto start_searching;
}
if (p==-2)
{
ret_val = SEARCH_INTERNAL_ERROR;
goto exit;
}
if (p>-1)
{
*pos = p;
*size = re_match_pattern(&rpat, in_strm->buf_base, textLength, p, 0);
if (*size<0)
{
ret_val = SEARCH_INTERNAL_ERROR;
goto exit;
}
ret_val = 1;
}
}
else
{
p = 0;
if (s1>=0)
p = Misc_TBMKsearch_memory(lpat, in_strm->buf_base+s1, e1, 0, &position);
if (p<0)
{
ret_val = SEARCH_INTERNAL_ERROR;
goto exit;
}
if (p==0 && e2!=0)
{
s1 = s2;
e1 = e2;
s2 = e2 = 0;
goto start_searching;
}
if (p>0)
{
*pos = position+s1;
*size = plen;
ret_val = 1;
}
}
exit:
Misc_TBMKpattern_free(&lpat);
if (in_strm!=NULL)
NXCloseMemory(in_strm, NX_FREEBUFFER);
return ret_val;
}
- (oneway void)selectTextFrom:(int)start to:(int)end
{
if ([self isSelectable] && start<=end && 0<=start)
[self setSel:start :end];
}
- (int)setRegExprSyntax:(int)syntax
{
return re_set_syntax(syntax);
}
- (void)writeSelectionToPasteboard:(in Pasteboard *)pboard asType:(in NXAtom)type
{
char text[spN.cp-sp0.cp+1];
[self getSubstring:text start:sp0.cp length:spN.cp-sp0.cp];
text[spN.cp-sp0.cp] = '\0';
if (*text!='\0')
{
[pboard declareTypes:&type num:1 owner:NULL];
[pboard writeType:type data:text length:spN.cp-sp0.cp];
}
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.