/*
 *	$Source: /u1/X/DECToolkit/src/RCS/AppendSource.c,v $
 *	$Header: AppendSource.c,v 1.1 86/12/17 08:59:58 swick Exp $
 */

#ifndef lint
static char *rcsid_AppendSource_c = "$Header: AppendSource.c,v 1.1 86/12/17 08:59:58 swick Exp $";
#endif	lint

#ifndef lint
static  char    *sccsid = "@(#)AppendSource.c	1.5          12/11/86";
#endif lint
/*
 *			  COPYRIGHT 1986
 *		   DIGITAL EQUIPMENT CORPORATION
 *		       MAYNARD, MASSACHUSETTS
 *			ALL RIGHTS RESERVED.
 *
 * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
 * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
 * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
 * ANY PURPOSE.  IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
 *
 * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT RIGHTS,
 * APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN ADDITION TO THAT
 * SET FORTH ABOVE.
 *
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting documentation,
 * and that the name of Digital Equipment Corporation not be used in advertising
 * or publicity pertaining to distribution of the software without specific, 
 * written prior permission.
 */

/* File: AppendSource.c */

#include <X/Xlib.h>
#include <stdio.h>
#include "Toolkit.h"	 
#include "TextDisplay.h"   /** included in all text subwindow files **/

/** private TAppendSource definitions **/

typedef struct _TAppendSourceData {
    FILE *file;		
    TTextPosition position, 	/*** read position for file ***/
    		  length; 	/*** length of file ***/
    char *buffer;		/*** piece of file in memory ***/
    int charsInBuffer;		/*** number of bytes used in memory ***/
} TAppendSourceData, *TAppendSourcePtr;

#define bufSize 1000

#define Increment(data, position, direction)\
{\
    if (direction == left) {\
	if (position > 0) \
	    position -= 1;\
    }\
    else {\
	if (position < data->length)\
	    position += 1;\
    }\
}

static char Look(data, position, direction)
  TAppendSourcePtr data;
  TTextPosition position;
  enum ScanDirection direction;
{
    TTextPosition pos;

    if (direction == left) {
	if (position == 0)
	    return('\n');
	else {
	    FillBuffer(data, position - 1);
	    return(data->buffer[position - data->position - 1]);
	}
    }
    else {
	if (position == data->length)
	    return('\n');
	else {
	    FillBuffer(data, position);
	    return(data->buffer[position - data->position]);
	}
    }
}



/***
 *** this routine will read source into "text", starting at "pos", for	
 *** "maxRead" characters or end.
 ***/
int AppendReadText (src, pos, text, maxRead)
  TTextSource *src;
  TTextPosition pos;	/** starting position ***/
  TTextBlock *text;	/** RETURNED: text read in ***/
  int maxRead;		/** max number of bytes to read **/
{
    TTextPosition nextPos, count;
    TAppendSourcePtr data;

    data = (TAppendSourcePtr) src->data;
    FillBuffer(data, pos);
    text->ptr = data->buffer + (pos - data->position);
    count = data->charsInBuffer - (pos - data->position);
    text->length = (maxRead > count) ? count : maxRead;
    return pos + text->length;
}

/***
 *** this routine reads text starting at "pos" into memory.
 ***/
static int FillBuffer (data, pos)
  TAppendSourcePtr data;
  TTextPosition pos;
{
    TTextPosition readPos;
    if ((pos < data->position ||
	    pos >= data->position + data->charsInBuffer - 100) &&
	    data->charsInBuffer != data->length) {
	if (pos < (bufSize / 2))
	    readPos = 0;
	else
	    if (pos >= data->length - bufSize)
		readPos = data->length - bufSize;
	    else
		if (pos >= data->position + data->charsInBuffer - 100)
		    readPos = pos - (bufSize / 2);
		else
		    readPos = pos;
	fseek(data->file, readPos, 0);
	data->charsInBuffer = fread(data->buffer, sizeof(char), bufSize,
				data->file);
	data->position = readPos;
    }
}


/***
 *** this routine will append text to the end of the file and to the end]
 *** of the buffer currently in memory.
 ***/
static int AppendReplaceText (src, startPos, endPos, text)
  TTextSource *src;
  TTextPosition startPos, endPos;
  TTextBlock *text;
{
    char temp_str[bufSize], buf_format[25];
    TAppendSourcePtr data;
    data = (TAppendSourcePtr) src->data;

    /*** write the new text to the end of the file ***/
    if (text->length > 0) {
	fseek(data->file, data->length, 0);
	fwrite(text->ptr, sizeof(char), text->length, data->file);
    } else
	/*** if the delete key was hit, blank out last char in the file ***/
	if (text->length < 0) {
		fseek(data->file, data->length-1, 0);
		fwrite(" ", sizeof(char), 1, data->file);
	}
     fseek(data->file, 0, 2);	/** need this for anyone else trying to seek
				    to end of file.  - mary **/
     
    /*** put the new text into the buffer in memory ***/
    data->length += text->length;
    if (data->charsInBuffer + text->length <= bufSize) {
	if (text->length > 0) {
		strncpy(temp_str, data->buffer, data->charsInBuffer);
		sprintf(buf_format, "%%.%ds%%.%ds", 
			data->charsInBuffer, text->length);
		sprintf(data->buffer, buf_format, temp_str, text->ptr);
	}
	data->charsInBuffer += text->length;
    } else
	FillBuffer(data, data->length - text->length);

    return (text->length);
}


/***
 *** returns the length of the file.
 ***/
static TTextPosition AppendGetLastPos (src)
  TTextSource *src;
{
    return (((TAppendSourceData *)(src->data))->length);
}


/***
 *** lets you modify the source length.
 ***/
static int AppendSetLastPos (src, lastPos)
  TTextSource *src;
  TTextPosition lastPos;
{
    ((TAppendSourceData *)(src->data))->length = lastPos;
}

/***
 *** what time of text insertion is allowed for this source.
 ***/
static enum InsertionType AppendEditType()
{
 return(append);
}

/***
 *** This routine will start at
 *** the "pos" position of the source and scan in the appropriate
 *** direction until it finds something of the right sType.  It returns 
 *** the new position.  If upon reading it hits the end of the buffer
 *** in memory, it will refill the buffer.
 ***/
static TTextPosition AppendScan (src, pos, sType, dir, word_break_symbols)
  TTextSource *src;
  TTextPosition pos;		/*** starting position ***/
  enum SelectionType sType;	/*** char, word, eoln **/
  enum ScanDirection dir;	/*** left or right ***/
  char *word_break_symbols;
{
    TAppendSourcePtr data;
    TTextPosition position;
    int whiteSpace;
    char    c;

    data = (TAppendSourcePtr) src->data;
    position = pos;
    switch (sType) {
	case charSelection: 
	    Increment(data, position, dir);
	    break;
	case wordSelection: 
	    whiteSpace = 0; 
	    while (position > 0 && position < data->length) {
		FillBuffer(data, position);
		c = Look(data, position, dir);
		whiteSpace = (c == ' ') || (c == '\t') || (c == '\n');
		if (whiteSpace)
		    break;
		else
		   if (index(word_break_symbols, c) != 0)
			break;
		Increment(data, position, dir);
	    }
	    break;
	case lineSelection: 
	    position = pos;
	    while (position > 0 && position < data->length) {
		FillBuffer(data, position);
		if (Look(data, position, dir) == '\n')
		    break;
		Increment(data, position, dir);
	    }
	    break;
	case allSelection: 
	    if (dir == left)
		position = 0;
	    else
		position = data->length;
    }
    return(position);
}



/********************* Public routines ******************************/

int *TCreateAppendSource (name)
  char *name;
{
    TTextSource *src;
    TAppendSourcePtr data;

    src = (TTextSource *) Tmalloc(sizeof(TTextSource));
    src->read = AppendReadText;
    src->replace = AppendReplaceText;
    src->getLastPos = AppendGetLastPos;
    src->setLastPos = AppendSetLastPos;
    src->getEditType = AppendEditType;
    src->scan = AppendScan;
    src->data = (int *) (Tmalloc(sizeof(TAppendSourceData)));
    data = (TAppendSourcePtr) src->data;
    if ((data->file = fopen(name, "w+")) == 0)
	fprintf(stderr, "XToolkit: error opening appendable source file \n");
    fseek (data->file, 0, 2);  
    data->length = ftell (data->file);  
    data->buffer = (char *) Tmalloc(bufSize);
    data->position = 0;
    data->charsInBuffer = 0;
    src->data = (int *) (data);
    return(int *) src;
}

void TDestroyAppendSource (src)
  TTextSource *src;
{
    free(src->data);
    free(src);
}
