static char rcsid[] = "$Id: word.c,v 1.1 1992/09/05 01:13:32 mike Exp $";

/* $Log: word.c,v $
 * Revision 1.1  1992/09/05  01:13:32  mike
 * Initial revision
 *
 */

/* 
 *
 *	WORD.C MODULE
 *
 * The routines in this file implement commands that work word at a time.
 * There are all sorts of word mode commands. If I do any sentence and/or
 * paragraph mode commands, they are likely to be put in this file.
 */

/* Copyright 1990, 1991, 1992 Craig Durland
 *   Distributed under the terms of the GNU General Public License.
 *   Distributed "as is", without warranties of any kind, but comments,
 *     suggestions and bug reports are welcome.
 */

#include <char.h>
#include "me2.h"

static int inword();

/* Word wrap:  Special case for typing in text.
 * The following must be true: At end of line.
 * Back-over whatever precedes the point on the current line and stop on the
 *   first word-break or the beginning of the line.  If we reach the
 *   beginning of the line, jump back to the end of the word and start a new
 *   line.  Otherwise, break the line at the word-break, eat it, and jump
 *   back to the end of the word.
 * Returns TRUE on success, FALSE on errors.
 */
wrapword()
{
  register int i;
  int s;
  Line *line;

  line = the_dot->line; i = llength(line) -1;
  while (i >= 0 && !isspace(lgetc(line,i))) i--;	/* backup over word */
  if (i < 0) return FALSE;				/* no white space */
  while (i >= 0 && isspace(lgetc(line,i))) i--;	/* backup over white space */
  the_dot->offset = i +1;			/* set cursor between words */
  run_key((KeyCode)(CTRL|'M'), FALSE,1, &s);
	/* del leading ws */  
  while (the_dot->offset < llength(the_dot->line) &&
	 isspace(lgetc(the_dot->line,the_dot->offset)))
     ldelete((int32)1,FALSE);
  goto_EoL(FALSE,1);
  return TRUE;
}

    /* Move over words.  If n is less than zero, move backwords.
     * Input:
     *   nwords:  number of words to move over.
     *   n : pointer to a int.
     * Output:
     *   n :  number of characters moved over.  Only valid if TRUE is
     *        returned.
     * Returns:
     *   TRUE:  Everything went as expected.
     *   FALSE:  Hit the end or begining of the buffer before moved over
     *     nwords.
     */
static int next_word(nwords,n) int nwords, *n;
{
  int x = 0, tick = 1, backwards = FALSE;

  if (nwords == 0) { *n = 0; return TRUE; }  /* easy to move over no words */

  if (nwords < 0)
  {
    backwards = TRUE;
    tick = -1;
    nwords = -nwords;
    if (!next_character(-1)) return FALSE;
  }

  while (nwords--)
  {
    while (!inword())			/* move over white space */
    {
      if (!next_character(tick)) return FALSE;
      x++;
    }
    while (inword())			/* move over word */
    {
      if (!next_character(tick))	/* hit buffer edge */
      {
	*n = x;
	return (nwords == 0);		/* OK if last word butts upto edge */
      }
      x++;
    }
  }
  *n = x;

  if (backwards) return next_character(1);
  return TRUE;
}

/*
 * Move the cursor forward by the specified number of words.
 * Error if you try and move beyond the buffer's end.
 */
forwword(f,n) int f,n; { return next_word(n,&n); }

/*
 * Move the cursor backward by n words.
 * Returns:
 *   TRUE:   OK
 *   FALSE:  stuck at the start of the buffer.
 */
backword(f,n) int f,n; { return next_word(-n,&n); }

   /* Delete n words starting from the dot.  If n is less than zero, delete
    *   backwards.
    * To delete forward:  Remember the location of dot.  Move forward by the
    *   right number of words.  Put dot back where it was and delete the
    *   right number of characters.
    * To delete backwards:  Move backwards by the desired number of words,
    *   counting the characters.  When dot is finally moved to its resting
    *   place, fire off the delete characters command.
    * The deleted words are put into the cut buffer.
    */
delete_words(n) int n;
{
  if (n == 0) return TRUE;		/* delete no words */

  if (n < 0)				/* delete back words */
	{ if (!next_word(n,&n)) return FALSE; }
  else					/* delete fore words */
  {
    register Mark save_dot;

    save_dot = *the_dot;
    if (!next_word(n,&n)) return FALSE;
    *the_dot = save_dot;
  }

  if (!(lastflag & CFCUT)) clear_bag(cut_buffer);
  thisflag |= CFCUT;
  return ldelete((int32)n,TRUE);
}

/*
 * Delete forward by n words.
 * Bound to "M-d".
 */
delfword(f,n) int f,n; { return delete_words(n); }

/*
 * Delete backwards by n words.
 * Bound to "M-Rubout" and to "M-Backspace".
 */
delbword(f,n) int f,n; { return delete_words(-n); }

/*
 * Return TRUE if the character at dot is a character that is considered to be
 *   part of a word.
 */
static int inword()
{
  if (the_dot->offset == llength(the_dot->line)) return FALSE;
  return (int)isword(lgetc(the_dot->line, the_dot->offset));
}


#if 0
   /* Munge n words.  Only works in the forward direction.
    * Input:
    *   n : number of words to munge.
    *   munge_char : a pointer to a routine that munges a character.  It is
    *     called like so: munge_char(c,flag,start_word) where c is the
    *     character to munge, flag is described below, start_word is TRUE if
    *     c is the first character of a word.  munge_char() return a char to
    *     replace c with.
    *   flag:  a arbitrary int passed to munge_char().
    * Output:
    *   Munged chars.
    * Returns:
    *   TRUE  : OK
    *   FALSE : Hit end of buffer.
    */
static int munge_word(n,munge_char,flag) int n; char (*munge_char)(); int flag;
{
  register unsigned char c;
  register int start_word;

  if (n < 0) return FALSE;
  lchange(WFHARD);	/* assume lines are going to change */
  while (n--)
  {
    while (!inword()) if (!next_character(1)) return FALSE;
    start_word = TRUE;
    do
    {
      lputc(the_dot->line,the_dot->offset,
	  (*munge_char)
		(lgetc(the_dot->line,the_dot->offset), flag, start_word));
      start_word = FALSE;
      if (!next_character(1)) return FALSE;
    } while (inword());
  }
  return TRUE;
}

static char casechar(c,lower,ignore) char c; int lower, ignore;
{
  return (lower ? tolower(c) : toupper(c));
}

static char capchar(c,ignore,start_word) char c; int ignore, start_word;
{
  return (start_word ? toupper(c) : tolower(c));
}

/*
 * Move the cursor forward by the specified number of words.  As you move
 *   convert characters to lower case.  Error if you try and move over the
 *   end of the buffer.
 * Bound to "M-l".
 */
lowerword(f,n) { return munge_word(n,casechar,TRUE); }

/*
 * Move the cursor forward by the specified number of words.  As you move,
 *   convert any characters to uppercase.  Error if you try and move beyond
 *   the end of the buffer.
 * Bound to "M-u".
 */
upperword(f,n) { return munge_word(n,casechar,FALSE); }

/*
 * Move the cursor forward by the specified number of words.  As you move
 *   convert the first character of the word to upper case, and subsequent
 *   characters to lower case.  Error if you try and move past the end of
 *   the buffer.
 * Bound to "M-c".
 */
capword(f,n) int f,n; { return munge_word(n,capchar,TRUE); }
#endif
