/*
 * find_position.c file contains following procedures:
 * 	find_string_pos_in_buffer
 *	find_line_pos
 *	textPosToLineColumn
 * They are used in the asedit program.
 *
 * (C) COPYRIGHT A. Stochniol, 1991
 * All Rights Reserved
 *
 * Last changes: 26.03.1992
 *
*/

#ifdef AMIGA
#include "amiga.h"
#endif

#include <stdio.h>
#include <string.h>
#include <ctype.h>		/* to define toupper function */
#include <X11/Intrinsic.h>	/* defines Boolean type */


/* find_string_pos_in_buffer returns position of the found string in the character buffer;
   if the text has not been found (or in case of error) returns negative number;
   The search is started from startpos and is carried forward when "forward"
   is True (when "forward" is False the search direction is backward).
   The search is case sensitive for case_sensitive=True and only whole
   words are found when whole_words_only=True.
   The startpos and endpos should contain the range where the string should
   be searched for. When startpos or endpos is < 0 the default value will be
   assigned for it inside the procedure (i.e. beginnning or end of the file );
 */

#ifdef _NO_PROTO
long find_string_pos_in_buffer(searchstring, buf,
			startpos, endpos, forward,
			case_sensitive, whole_words_only)
char *searchstring;
char *buf;
long startpos;
long endpos;
Boolean forward;
Boolean case_sensitive;
Boolean whole_words_only;
#else  /* _NO_PROTO */
long find_string_pos_in_buffer(char *searchstring, char *buf,
			long startpos, long endpos, Boolean forward,
			Boolean case_sensitive, Boolean whole_words_only)
#endif
{
    long pos = -1L;
    long searchlen = strlen(searchstring);	/* length of the search string
						   (should be > 0)*/
    long buf_size = (long) strlen(buf);
    long register i, n;
    char *s1, *s2;
    int  c1, c2;	/* integers used for not case-sensitive search (toupper
				is defined for integers; we use them to speed
				the process of search)*/

    if(searchlen <= 0L) return(-3L);
    if(buf_size <= 0L)      return(-2L);

    /* setting the default value for negative startpos and/or endpos */
    if(startpos < 0L)
    {
	if(forward) startpos = 0L;
	else	    startpos = buf_size;
    }
    if(endpos < 0L)
    {
	if(forward) endpos   = buf_size;
	else	    endpos   = 0L;
    }

    /* test (and correct) the search range */
    if(endpos > buf_size)     endpos=buf_size;
    if(startpos > buf_size) startpos=buf_size;

    /* there are two major cases case-sensitive and not (they are split to
       speed the process of the search */
    if(case_sensitive)
    {
      if(forward)
      {
	/* search forward starting from startpos position */

	for( i = startpos; i < endpos; i++)
	{
	  n = searchlen;
	  s1 = &buf[i];
	  s2 = searchstring;
	  while (--n >= 0L && *s1++ == *s2++);
	  if(n < 0L)
	  {
		pos = i;
		/* additional check for whole words only - check both ends*/
		if(whole_words_only)
		{
			if((i>0L && (isalnum(buf[i-1]) || buf[i-1] == '_')) ||
			    (i+searchlen<buf_size && (isalnum(buf[i+searchlen]) || buf[i+searchlen] == '_') ))
				pos = -1;	/* in one or another end there is a letter or digit 
					   or underscore - do not use that occurence */
			else	break;	/* on both ends there is no letter and no digit */
		}
		else break; /* string found: now "i" shows position of the search string
				in the buffer relatively to the buffer beginning */
	  }
	}
      }
      else
      {
	/* search backward starting from startpos-1 position */
	for( i = startpos-1; i >= endpos; i--)
	{
	  n = searchlen;
	  s1 = &buf[i];
	  s2 = searchstring;
	  while (--n >= 0L && *s1++ == *s2++);
	  if(n < 0L)
	  {
		pos = i;
		if(whole_words_only)  	/* as above ... */
		{
			if((i>0L && (isalnum(buf[i-1]) || buf[i-1] == '_')) ||
			    (i+searchlen<buf_size && (isalnum(buf[i+searchlen]) || buf[i+searchlen] == '_') ) )
				pos = -1;
			else	break;
		}
		else break; /* string found: now "i" shows position of the search string
				in the buffer relatively to the buffer beginning */
	  }
	}
      }
    }
    else
    {	/* not case-sensitive ... */
      if(forward)
      {
	/* search forward starting from startpos position */

	for( i = startpos; i < endpos; i++)
	{
	  n = searchlen;
	  s1 = &buf[i];
	  s2 = searchstring;
	  c1 = toupper(*s1);	c2=toupper(*s2);
	  while (--n >= 0L && c1 == c2)
	  {
		c1 = toupper( *(++s1));
		c2 = toupper( *(++s2));
	  }
	  if(n < 0L)
	  {
		pos = i;
		if(whole_words_only)  	/* as above ... */
		{
			if((i>0L && (isalnum(buf[i-1]) || buf[i-1] == '_')) ||
			    (i+searchlen<buf_size && (isalnum(buf[i+searchlen]) || buf[i+searchlen] == '_') ))
				pos = -1;	/* in one end or another there is a letter, or a digit
					   or the underscore - do not use that occurence */
			else	break;	/* on both ends there is no letter and no digit */
		}
		else break;
	  }
	}
      }
      else
      {
	/* search backward starting from startpos-1 position */
	for( i = startpos-1; i >= endpos; i--)
	{
	  n = searchlen;
	  s1 = &buf[i];
	  s2 = searchstring;
	  c1 = toupper(*s1);	c2=toupper(*s2);
	  while (--n >= 0L && c1 == c2)
	  {
		c1 = toupper( *(++s1));
		c2 = toupper( *(++s2));
	  }
	  if(n < 0L)
	  {
		pos = i;
		if(whole_words_only)  	/* as above ... */
		{
			if((i>0L && (isalnum(buf[i-1]) || buf[i-1] == '_')) ||
			    (i+searchlen<buf_size && (isalnum(buf[i+searchlen]) || buf[i+searchlen] == '_') ) )
				pos = -1;
			else	break;
		}
		else break;
	  }
	}
      }

    }
    /****** TEMP  - for backward DEBUG!!!!!!! 
    	fprintf(stderr, "find_string_pos_in_buffer, startpos , endpos, pos: %ld %ld %ld \n", startpos, endpos, pos);
    *******/
    return(pos);

}   /* find_string_pos_in_buffer */



/* find_line_pos returns position of the line in the character buffer;
   if such a line has not been found - returns -1 and the maximum
   number of lines in the buffer buf is passed in found_lines  for message
   purposes
 */

#ifdef _NO_PROTO
long find_line_pos(line, buf, found_lines)
long line;
char *buf;
long *found_lines;
#else  /* _NO_PROTO */
long find_line_pos(long line, char *buf, long *found_lines)
#endif
{
    long pos = -1L;
    char new_line = '\n';
    long size = strlen(buf);
    long register i;

    *found_lines = 0L;

    if(line == 1L)
	pos = 0L;
    else
    {
	for( i = 0L; i < size; i++)
	{
	  if(buf[i] == new_line) (*found_lines)++;
	  if(*found_lines == (line - 1L))	/* we are on the line ... */
		{pos = i + 1L; break; }		/* position of the first character of
						   that line */
	}
	/* extra check for the last line (if the last character was different from
	   line feed we have one extra line, because the last line did not end with
	   the line feed) */
	if(buf[size-1L] != new_line) (*found_lines)++;
    }
    return(pos);
}   /* find_line_pos */


/* textPosToLineColumns converts the text position in the buffer to the
   appropriate line and column values; buf_size was specially introduce
   to allow using this procedure for character buffers which may be not
   NULL terminated but with known size (usually buf_size is simply calculated
   as (long) strlen(buf) for NULL terminated strings).
 */


#ifdef _NO_PROTO
void textPosToLineColumn(buf, buf_size, pos, line, column)
char *buf;
long buf_size;
long pos;
long *line;
long *column;
#else  /* _NO_PROTO */
void textPosToLineColumn(char *buf, long buf_size, long pos, long *line, long *column)
#endif
{
    char new_line = '\n';
    char tab_char = '\t';

    long register i, col;
    long latest_lf_pos = -1L;	/* remeber the position of the latest found line feed */
				/* in Motif XmTextPosition is defined as long ... */
    int tab_size = 8;		/* specify how many characters will move the cursor
				   for each tab stop character (default - 8;
				   for programmers editors usually between 2 and 16)
				   This value should be exactly the same as used
				   internally in the text widget !!  - in Motif
				   it probably can not be changed so we
				   specify it internally in this procedure !!*/


    *line = 1L;
    *column = 1L;

    if(pos <= 0L)   return;
    else
    {
	for( i = 0L; i < buf_size; i++)
	{

	  if(i == pos) break;   /* we have reached the right position */
	  if(buf[i] == new_line)
	  { (*line)++;
	   latest_lf_pos = i; }

	}
	/* setting the column number  - version without taking into account tab characters*/
	*column = pos - latest_lf_pos;

	/* let's find column with tab characters taken into account ... */
	col = 1L;
	for( i = latest_lf_pos+1L; i <pos; i++)
	{
	  if(buf[i] == tab_char) col = ((col+tab_size-1)/tab_size)*tab_size + 1L;
	  else			 col++;
	}
	*column = col;
    }
    return;
}   /* textPosToLineColumn */
