ftp.nice.ch/pub/next/developer/languages/smalltalk/smalltalk.1.2.alpha5.s.tar.gz#/smalltalk-1.2.alpha5/objc/objclib/MiscSearchText.m

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.