DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T

⟦6d0721589⟧ TextFile

    Length: 37520 (0x9290)
    Types: TextFile
    Names: »Tty.c,v«

Derivation

└─⟦a05ed705a⟧ Bits:30007078 DKUUG GNU 2/12/89
    └─⟦34cc4e2f7⟧ »./UNRELEASED/xgdb3.2.tar.Z« 
        └─⟦80fac5d7c⟧ 
            └─⟦this⟧ »./RCS/Tty.c,v« 

TextFile

head     1.1;
access   ;
symbols  ;
locks    hubbard:1.1; strict;
comment  @ * @;


1.1
date     89.07.05.15.35.40;  author hubbard;  state Exp;
branches ;
next     ;


desc
@Initial checkin, Beta version 0.1.
@



1.1
log
@Initial revision
@
text
@\f


/*-
 * Tty.c --
 *	Functions to implement the TTY widget -- a subclass of the
 *	text widget that acts much like a standard tty, except
 *	there's no process running under it.
 *
 * Copyright (c) 1988 by the Regents of the University of California
 *
 * 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.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 *
 * Author: Adam deBoor, U.C. Berkeley.
 *
 * Log
 */

#ifndef lint
static char rcsid[] =
"$Header: /usr/ben/jkh/src/xgdb/RCS/Tty.c,v 1.2 89/05/28 18:27:48 jkh Exp Locker: jkh $ SPRITE (Berkeley)";
#endif lint

#include    "IntrinsicP.h"
#include    "Xos.h"
#include    "StringDefs.h"
#include    "TtyP.h"
#include    "xgdb.h"

#include    <sys/file.h>
#include    <varargs.h>
#include    <ctype.h>

#define TTY_BUF_SIZE 1024

XtTextSource	  XtTtySourceCreate();
void		  XtTtySourceDestroy();
void		  XtTtySourceSetFence();

static int  	  	defOptions = scrollVertical|scrollOnOverflow|wordBreak;

static XtResource 	resources[] = {
{ XtNenterCallback, XtCCallback, XtRCallback, sizeof(caddr_t),
  XtOffset(TtyWidget, tty.enter), XtRCallback, (caddr_t)NULL },
{ XtNprompt, XtCPrompt, XtRString, sizeof(char *),
  XtOffset(TtyWidget, tty.prompt.ptr), XtRString, NULL },
{ XtNtextOptions, XtCTextOptions, XtRInt, sizeof(int),
  XtOffset(TtyWidget, text.options), XtRInt, (caddr_t)&defOptions },
{ XtNinterruptCallback, XtCCallback, XtRCallback, sizeof(caddr_t),
  XtOffset(TtyWidget, tty.interrupt), XtRCallback, (caddr_t)NULL },
};

static void	  	Enter(), KillLine(), SetMark(),
			SaveRegion(), Interrupt();

static XtActionsRec 	actions[] = {
{"enter", Enter},
{"backward-delete-to-prompt", KillLine },
{"set-mark", SetMark },
{"save-region", SaveRegion },
{"interrupt", Interrupt },
{NULL, NULL}
};

/*
 * New translations -- adds enter to the various newline() translations.
 */
static char defaultTranslations[] =
"Meta<Key>0xffff:	delete-previous-word()\n\
<Key>0xffff:		delete-previous-character()\n\
<Key>0xff0d:		newline() enter()\n\
<Key>0xff08:		delete-previous-character()\n\
Ctrl<Key>@@:		set-mark()\n\
Ctrl<Key>\32:		set-mark()\n\
Ctrl<Key>A:		beginning-of-line()\n\
Ctrl<Key>B:		backward-character()\n\
Meta<Key>B:		backward-word()\n\
Ctrl<Key>C: 	  	interrupt()\n\
Ctrl<Key>D:		delete-selection()\n\
Ctrl<Key>E:		end-of-line()\n\
Ctrl<Key>F:		forward-character()\n\
Meta<Key>F:		forward-word()\n\
Ctrl<Key>H:		delete-previous-character()\n\
Ctrl<Key>J:		newline() enter()\n\
Ctrl<Key>L:		redraw-display()\n\
Ctrl<Key>M:		newline() enter()\n\
Ctrl<Key>N:		next-line()\n\
Ctrl<Key>P:		previous-line()\n\
Ctrl Meta<Key>V:	next-page()\n\
Meta<Key>V:		previous-page()\n\
Ctrl<Key>W:		delete-previous-word()\n\
Meta<Key>W:		save-region()\n\
Ctrl<Key>X:		backward-delete-to-prompt()\n\
Ctrl<Key>Y:		unkill()\n\
Meta<Key>Y:		stuff()\n\
Ctrl<Key>V:		stuff()\n\
Meta<Key>\\<:		beginning-of-file()\n\
Meta<Key>\\>:		end-of-file()\n\
<FocusIn>:		focus-in()\n\
<FocusOut>:		focus-out()\n\
Ctrl<Btn1Down>:		select-start()\n\
Ctrl Button3<PtrMoved>:	extend-adjust()\n\
Ctrl<Btn3Up>:		extend-end()\n\
None<Btn1Down>:		select-start()\n\
Button3<PtrMoved>:	extend-adjust()\n\
None<Btn3Up>:		extend-end()\n\
!Button1<PtrMoved>:	extend-adjust()\n\
None<Btn1Up>:		extend-end()\n\
None<Btn2Down>:		stuff()\n\
<Key>:			insert-char()\n\
Shift<Key>:		insert-char()";

static void Initialize(), Destroy(), InitializeHook();
static Boolean SetValues();

TtyClassRec ttyClassRec = {
{
     /* core fields */
     (WidgetClass) &textClassRec,	/* superclass       */
     "Tty",				/* class_name       */
     sizeof(TtyRec),    		/* widget_size      */
     NULL,		    		/* class_initialize */
     NULL,				/* class_part_init  */
     FALSE,				/* class_inited     */
     Initialize,			/* initialize       */
     InitializeHook,			/* initialize_hook  */
     XtInheritRealize,			/* realize          */
     actions,				/* actions          */
     XtNumber(actions),			/* num_actions      */
     resources,				/* resources        */
     XtNumber(resources),		/* num_ resource    */
     NULLQUARK,				/* xrm_class        */
     TRUE,				/* compress_motion  */
     FALSE,				/* compress_exposure*/
     TRUE,				/* compress_enterleave*/
     FALSE,				/* visible_interest */
     Destroy,				/* destroy          */
     XtInheritResize,			/* resize           */
     XtInheritExpose,			/* expose           */
     SetValues,				/* set_values       */
     NULL,				/* set_values_hook  */
     XtInheritSetValuesAlmost,		/* set_values_almost*/
     NULL,				/* get_values_hook  */
     XtInheritAcceptFocus,		/* accept_focus     */
     XtVersion,				/* version          */
     NULL,				/* callback_private */
     defaultTranslations,		/* tm_table         */
     XtInheritQueryGeometry,		/* query_geometry   */  	
     XtInheritDisplayAccelerator,	/* display_accelerator*/	
     NULL				/* extension	    */	
  },
};

WidgetClass ttyWidgetClass  = (WidgetClass)&ttyClassRec;

/*-
 *-----------------------------------------------------------------------
 * Initialize --
 *	Initialize a Tty widget -- duplicate prompt string, if any and
 *	initialize startPos and prompted.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	See above.
 *
 *-----------------------------------------------------------------------
 */
static void
Initialize(request, new)
    Widget  	  request;
    Widget	  new;
{
    TtyWidget	  tty = (TtyWidget)new;

    tty->tty.prompted = FALSE;
    if (tty->tty.prompt.ptr != (char *)NULL) {
	char	  *prompt;

	tty->tty.prompt.length = strlen(tty->tty.prompt.ptr);
	prompt = XtMalloc(tty->tty.prompt.length + 1);
	(void)strcpy(prompt, tty->tty.prompt.ptr);
	tty->tty.prompt.ptr = prompt;
    } else {
	tty->tty.prompt.ptr = "";
	tty->tty.prompt.length = 0;
    }
    tty->tty.prompt.firstPos = 0;
    tty->tty.markPos = 0;

    /*
     * Deal with geometry
     */
    
    /* superclass Initialize can't set the following,
     * as it didn't know the source or sink when it was called */
    if (request->core.height == DEFAULT_TEXT_HEIGHT)
	new->core.height = DEFAULT_TEXT_HEIGHT;
}

/*-
 *-----------------------------------------------------------------------
 * InitializeHook --
 *	Create the necessary sources and sinks for this beastie. Code
 *	snarfed from DiskCreateSourceSink in AsciiText.c since we must
 *	have editType set to append and it's not a proper resource.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	The source and sink are created for the widget.
 *
 *-----------------------------------------------------------------------
 */
static void
InitializeHook(w, args, num_args)
    Widget  	  w;
    ArgList	  args;
    Cardinal	  *num_args;
{
    TtyWidget	  tty = (TtyWidget)w;

    tty->text.source = XtTtySourceCreate(w, args, *num_args);
    tty->text.sink = XtAsciiSinkCreate(w, args, *num_args);

    /*
     * Seek to the end of the file.
     */
    tty->text.lastPos = /* GETLASTPOS */
      (*tty->text.source->Scan) (tty->text.source, 0, XtstAll,
				 XtsdRight, 1, TRUE );

    /*
     * If no height specified, stuff in a reasonable one --
     * 24 lines high.
     */
    if (tty->core.height == DEFAULT_TEXT_HEIGHT) {
        tty->core.height =
	    (2*yMargin) + 2 + (*tty->text.sink->MaxHeight)(tty, 24);
    }

    /*
     * Redisplay the entire thing
     */
    ForceBuildLineTable((TextWidget)tty);
}

/*-
 *-----------------------------------------------------------------------
 * Destroy --
 *	Free the prompt string.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	The prompt string c'est freed.
 *
 *-----------------------------------------------------------------------
 */
static void
Destroy(w)
    Widget  	  w;
{
    TtyWidget	  tty = (TtyWidget)w;

    XtTtySourceDestroy(tty->text.source);
    XtAsciiSinkDestroy(tty->text.sink);
    
    /* XtFree(tty->tty.prompt.ptr); This tosses its cookies */
}

/*-
 *-----------------------------------------------------------------------
 * SetValues --
 *	Change the values for the given tty widget. Can only change the
 *	prompt string.
 *
 * Results:
 *	FALSE.
 *
 * Side Effects:
 *	Frees old prompt string and duplicates new.
 *
 *-----------------------------------------------------------------------
 */
static Boolean
SetValues(current, request, new)
    Widget  	  current;
    Widget  	  request;
    Widget  	  new;
{
    TtyWidget	  curTty = (TtyWidget)current;
    TtyWidget	  reqTty = (TtyWidget)request;
    TtyWidget	  newTty = (TtyWidget)new;

    if (reqTty->tty.prompt.ptr != curTty->tty.prompt.ptr) {
	char	  *prompt;
	
	XtFree(curTty->tty.prompt.ptr);

	newTty->tty.prompt.length = strlen(newTty->tty.prompt.ptr);
	prompt = XtMalloc(newTty->tty.prompt.length + 1);
	(void)strcpy(prompt, newTty->tty.prompt.ptr);
	newTty->tty.prompt.ptr = prompt;
    }
    return (FALSE);
}

/*-
 *-----------------------------------------------------------------------
 * XtTtyNumColumns --
 *	Return the number of columns in the given tty widget.
 *
 * Results:
 *	The number of columns in the widget.
 *
 * Side Effects:
 *	None.
 *
 *-----------------------------------------------------------------------
 */
int
XtTtyNumColumns(w)
    Widget  	  	w;
{
    TtyWidget	  	tty = (TtyWidget)w;
    int	    	  	charWidth;
    int	    	  	charHeight;
    XtTextPosition	nlPos;
    XtTextPosition	junkPos;

    Entry("XtTtyNumColumns");

    /*
     * AsciiSink treats a newline as a space, giving us the average
     * character width, so find the most-recent newline in the file and
     * get its width, then divide the total width of the window by that
     * to get the number of columns.
     */
    nlPos = (* tty->text.source->Scan)(tty->text.source,
				       tty->text.insertPos,
				       XtstEOL,
				       XtsdLeft, 1, TRUE);
    (* tty->text.sink->FindDistance)(tty, nlPos, 0, nlPos + 1,
				     &charWidth, &junkPos, &charHeight);

    Leave((tty->core.width - tty->text.leftmargin) / charWidth);
}
    
/*-
 *-----------------------------------------------------------------------
 * XtTtyPrompt --
 *	Place a prompt in the window.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	The prompt string is put in the window at its end.
 *
 *-----------------------------------------------------------------------
 */
void
XtTtyPrompt(w)
Widget  	  w;
{
    TtyWidget	  tty = (TtyWidget)w;
    XtTextPosition pos;

    Entry("XtTtyPrompt");

    pos = tty->text.lastPos;
    XtTextReplace(w, pos, pos, &tty->tty.prompt);
    tty->tty.startPos = pos + tty->tty.prompt.length;
    XtTextSetInsertionPoint(w, tty->tty.startPos);
    XtTtySourceSetFence(tty->text.source, tty->tty.startPos);
    tty->tty.prompted = TRUE;
    Leave_void;
}

/*-
 *-----------------------------------------------------------------------
 * XtTtyPrintf --
 *	Perform a printf to the given widget at its end. The buffer used
 *	is as big as that used in the Tty source. This is a kludge,
 *	however. What should happen is either the proper-sized buffer
 *	should be determined, allocated, printed-to and broken into
 *	decent-sized chunks for TtyReplaceText, or the thing printed
 *	piecemeal, thus replicating printf into here...
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	The characters are added to the window.
 *
 *-----------------------------------------------------------------------
 */
/*VARARGS*/
void
XtTtyPrintf(va_alist)
va_dcl
{
    TtyWidget	  	tty;
    char    	  	string[TTY_BUF_SIZE];
    XtTextBlock	  	text;
    XtTextPosition	last;
    register char	*cp;
    char		*cpStart;
    va_list		args;
    va_list		nextArgs;
    char		savec;
    Boolean 	  	fancy;

    Entry("XtTtyPrintf");

    va_start(args);

    tty = va_arg(args, TtyWidget);
    cpStart = cp = va_arg(args, char *);

    last = tty->text.lastPos;
    
    while (*cp != '\0') {
	if (*cp != '%') {
	    cp++;
	} else {
	    if (cp != cpStart) {
		if (cp - cpStart < TTY_BUF_SIZE) {
		    text.firstPos = 0;
		    text.ptr = cpStart;
		    text.length = cp - cpStart;
		    XtTextReplace((Widget)tty, last, last, &text);
		    last += text.length;
		} else {
		    *cp = '\0';
		    XtTtyPutString((Widget)tty, cpStart);
		    *cp = '%';
		    last += cp - cpStart;
		}
	    }
	    cpStart = cp;
	    cp++;
	    nextArgs = args;
	    fancy = FALSE;
	charswitch:
	    switch(*cp) {
		case '*':
		    /*
		     * Argument specifies width -- pop it now
		     */
		    (void)va_arg(nextArgs, int);
		    fancy = TRUE;
		case '+':
		case '-':
		case ' ':
		case '#':
		case '.':
		case 'l':
		case 'h':
		    cp++;
		    fancy = TRUE;
		    goto charswitch;
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		    cp++;
		    while (isdigit(*cp)) {
			cp++;
		    }
		    fancy = TRUE;
		    goto charswitch;
		    
		case 'd':
		    cp++;
		    savec = *cp;
		    *cp = '\0';
		    (void)va_arg(nextArgs, int);
		    vsprintf(string, cpStart, args);
		    XtTtyPutString(tty, string);
		    *cp = savec;
		    break;
		    
		case 'u':
		case 'o':
		case 'X':
		case 'x':
		    cp++;
		    savec = *cp;
		    *cp = '\0';
		    (void)va_arg(nextArgs, unsigned int);
		    vsprintf(string, cpStart, args);
		    XtTtyPutString(tty, string);
		    *cp = savec;
		    break;
		    
		case 'E':
		case 'e':
		case 'f':
		case 'G':
		case 'g':
		    cp++;
		    savec = *cp;
		    *cp = '\0';
		    (void)va_arg(nextArgs, double);
		    vsprintf(string, cpStart, args);
		    XtTtyPutString(tty, string);
		    *cp = savec;
		    break;
		case 'c':
		    (void)va_arg(nextArgs, int);
		case '%':
		    cp++;
		    savec = *cp;
		    *cp = '\0';
		    vsprintf(string, cpStart, args);
		    XtTtyPutString(tty, string);
		    *cp = savec;
		    break;
		    
		case 's':
		    cp++;
		    if ((args == nextArgs) && !fancy) {
			char *s = va_arg(nextArgs, char *);
			XtTtyPutString(tty, s);
		    } else {
			/*
			 * XXX: Should worry about length here, but what
			 * the heck?
			 */
			savec = *cp;
			*cp = '\0';
			(void)va_arg(nextArgs, char *);
			vsprintf(string, cpStart, args);
			XtTtyPutString(tty, string);
			*cp = savec;
		    }
		    break;
	    }
	    cpStart = cp;
	    args = nextArgs;
	    last = tty->text.lastPos;
	}
    }
    if (cp - cpStart < TTY_BUF_SIZE) {
	text.firstPos = 0;
	text.ptr = cpStart;
	text.length = cp - cpStart;
	XtTextReplace((Widget)tty, last, last, &text);
	last += text.length;
    } else {
	XtTtyPutString((Widget)tty, cpStart);
	last += cp - cpStart;
    }

    XtTextSetInsertionPoint((Widget)tty, last);
    Leave_void;
}

/*-
 *-----------------------------------------------------------------------
 * XtTtyPutString --
 *	Outputs the string, without formatting, to the given widget.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	See above.
 *
 *-----------------------------------------------------------------------
 */
void
XtTtyPutString(w, string)
Widget  	  	w;
char		*string;
{
    TtyWidget	  	tty = (TtyWidget)w;
    XtTextBlock		text;
    XtTextPosition	last;

    Entry("XtTtyPutString");

    /* expand any tabs and control characters "in situ". */
    string = (String)expand_string(string, '\0');
    text.ptr = string;
    text.firstPos = 0;
    text.length = strlen(string);

    last = tty->text.lastPos;
    if (text.length < TTY_BUF_SIZE) {
	 XtTextReplace((Widget)tty, last, last, &text);
	 XtTextSetInsertionPoint((Widget)tty, last + text.length);
    } else {
	 int 	  length;

#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif
	length = text.length;
	while (length != 0) {
	    text.length = min(TTY_BUF_SIZE-1, length);
	    XtTextReplace((Widget)tty, last, last, &text);
	    length -= text.length;
	    text.firstPos += text.length;
	    last += text.length;
	}
	XtTextSetInsertionPoint((Widget)tty, last);
    }
    Leave_void;
}

/*XXX: No function officially exported to snag the text, so use one
 * that's unofficially exported (though the comments say it's sort of ok) */
extern char *_XtTextGetText( /* ctx, left, right */ );

/*************************************************************
 *
 * Action procedures
 *
 ************************************************************/

/*-
 *-----------------------------------------------------------------------
 * Enter --
 *	Extract the text typed since the last prompt and pass it off
 *	to the enterCallback function(s).
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	The enterCallback function(s) are called.
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
static void
Enter(w, event, params, num_params)
    Widget  	  w;
    XEvent	  *event;
    String	  *params;
    Cardinal	  *num_params;
{
    TtyWidget	  tty = (TtyWidget)w;
    XtTextPosition cur;
    char	   *text;

    if (tty->tty.prompted) {
	cur = tty->text.lastPos;
	tty->tty.prompted = FALSE;
	text = _XtTextGetText((TextWidget)w, tty->tty.startPos, cur);
	XtCallCallbacks(w, XtNenterCallback, (caddr_t)text);
	XtFree(text);
    }
}

/*-
 *-----------------------------------------------------------------------
 * KillLine --
 *	Kill all the text back to the prompt.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	The text from the last prompt to the end of the file is
 *	nuked.
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
static void
KillLine(w, event, params, num_params)
    Widget  	  w;
    XEvent  	  *event;
    String	  *params;
    Cardinal	  *num_params;
{
    TtyWidget	  tty = (TtyWidget)w;
    XtTextPosition cur;
    XtTextBlock	   text;

    if (tty->tty.prompted) {
	cur = tty->text.lastPos;
	text.length = text.firstPos = 0;
	text.ptr = "";
	XtTextReplace(w, tty->tty.startPos, cur, &text);
    }
}

/*-
 *-----------------------------------------------------------------------
 * SetMark --
 *	Record the current point for later reference.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	tty->tty.markPos is changed.
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
static void
SetMark(w, event, params, num_params)
    Widget  	  w;
    XEvent	  *event;
    String	  *params;
    Cardinal	  *num_params;
{
    TtyWidget	  tty = (TtyWidget)w;

    tty->tty.markPos = XtTextGetInsertionPoint(w);
}

/*-
 *-----------------------------------------------------------------------
 * SaveRegion --
 *	Save the text between the current point and the last mark in
 *	cut buffer 1.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Cut buffer 1 is changed.
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
static void
SaveRegion(w, event, params, num_params)
    Widget  	  w;
    XEvent	  *event;
    String	  *params;
    Cardinal	  *num_params;
{
    TtyWidget	  tty = (TtyWidget)w;
    char    	  *ptr;
    XtTextPosition cur;

    cur = XtTextGetInsertionPoint(w);
    if (cur < tty->tty.markPos) {
	ptr = _XtTextGetText((TextWidget)w, cur, tty->tty.markPos);
    } else {
	ptr = _XtTextGetText((TextWidget)w, tty->tty.markPos, cur);
    }

    XStoreBuffer(XtDisplay(w), ptr, strlen(ptr), 1);
    XtFree(ptr);
}

/*-
 *-----------------------------------------------------------------------
 * Interrupt --
 *	Handle an interrupt. Calls the functions in the interruptCallback
 *	list for the widget.
 *
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
static void
Interrupt(w, event, params, num_params)
    Widget  	  w;
    XEvent	  *event;
    String	  *params;
    Cardinal	  *num_params;
{
    XtCallCallbacks(w, XtNinterruptCallback, (caddr_t)NULL);
}
    

/*************************************************************
 *
 * Tty Source Functions
 *
 *************************************************************/

  /*
 * The following code is modified from DiskSrc.c. Aside from renaming,
 * it also allows text to be removed from the end of the file, thus
 * allowing for line-editing.
 */
#define TMPSIZ 32		/* bytes to allocate for tmpnam */

extern char *tmpnam();

/** private TtySource definitions **/

typedef struct _TtySourceData {
    /* resources */
    char       	  *fileName;
    /* private data */
    Boolean	   is_tempfile;
    int		   fd;		
    XtTextPosition position, 	    /* file position of first char in buffer */
 		   length, 	    /* length of file */
		   fence;   	    /* file position beyond which characters
				     * may not be deleted */
    char	   buffer[TTY_BUF_SIZE]; /* piece of file in memory */
    int		   charsInBuffer;   /* number of bytes used in memory */
    int		   dirtyStart;	    /* Start of dirty section (buffer index) */
    int		   dirtyEnd;	    /* End of dirty section (buffer index) */
} TtySourceData, *TtySourcePtr;

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

static XtResource diskResources[] = {
{XtNfile, XtCFile, XtRString, sizeof (char *),
 XtOffset(TtySourcePtr, fileName), XtRString, NULL},
};

/*-
 *-----------------------------------------------------------------------
 * TtyFillBuffer --
 *	Read text starting at the given position into memory. Attempts
 *	to keep the read position centered in the buffer (for some reason)
 *
 * Results:
 *	?
 *
 * Side Effects:
 *	The buffer is filled with characters.
 *
 *-----------------------------------------------------------------------
 */
static void
TtyFillBuffer (data, pos, length, modify)
    TtySourcePtr  	data;
    XtTextPosition	pos;
    long		length;
    Boolean		modify;	    /* TRUE if intend to modify the buffer.
				     * i.e. there must be length bytes available
				     * in the buffer when we return */
{
    long    	  	readPos;
    
#ifdef DEBUG_TTY
    printf("FillBuffer: pos = %d, length = %d, cur=%d, curLength = %d\n",
	   pos, length, data->position, data->charsInBuffer);
#endif

    if (!modify && ((pos + length) > data->length)) {
	/*
	 * If not going to write the buffer, don't worry if we haven't
	 * got exactly the right amount of data if the dude is trying to
	 * read beyond the end of the file...
	 */
	length = data->length - pos;
#ifdef DEBUG_TTY
	printf("FillBuffer: length = %d\n", length);
#endif
    }
    
    if ((pos < data->position) ||
	(pos >= (data->position + data->charsInBuffer - length)))
    {
	if (length > TTY_BUF_SIZE / 2) {
	    /*
	     * If the caller wants more characters than half a buffer's worth,
	     * don't bother trying to center the position in the buffer --
	     * just place it at the beginning.
	     */
	    readPos = pos;
	} else if (pos < (TTY_BUF_SIZE / 2)) {
	    /*
	     * Position before halfway point of buffer -- read from start
	     * of file.
	     */
	    readPos = 0;
	} else if (pos >= data->length - TTY_BUF_SIZE) {
	    /*
	     * Position in last buffer of file -- read last buffer,
	     * but leave room enough at the end for length bytes.
	     */
	    readPos = data->length - (TTY_BUF_SIZE - length);
	} else {
	    /*
	     * Read so pos is in center.
	     */
	    readPos = pos - (TTY_BUF_SIZE / 2);
	}
	if (readPos < 0) {
	    readPos = 0;
	}
	if (readPos != data->position) {
	    if (data->dirtyStart >= 0) {
		/*
		 * If any data are dirty, flush them to disk before changing
		 * positions.
		 */
#ifdef DEBUG_TTY
		printf("FillBuffer: dirty = [%d, %d]\n", data->dirtyStart,
		       data->dirtyEnd);
#endif	    
		lseek(data->fd, data->position + data->dirtyStart, L_SET);
		write(data->fd, data->buffer + data->dirtyStart,
		      data->dirtyEnd - data->dirtyStart + 1);
		data->dirtyStart = data->dirtyEnd = -1;
	    }
	    
	    lseek(data->fd, readPos, L_SET);
	    data->charsInBuffer = read(data->fd, data->buffer, TTY_BUF_SIZE);
	    data->position = readPos;
#ifdef DEBUG_TTY
	    printf("FillBuffer: cur = %d, curLength = %d\n", data->position,
		   data->charsInBuffer);
#endif
	}
    }
}

/*-
 *-----------------------------------------------------------------------
 * TtyLook --
 *	TtyLook to the right or left (based on 'direction') of the given
 *	position and return the character found there.
 *
 * Results:
 *	The appropriate character.
 *
 * Side Effects:
 *	The buffer is filled as necessary.
 *
 *-----------------------------------------------------------------------
 */
static char
TtyLook(data, position, direction)
TtySourcePtr  	data;	    /* Private data */
XtTextPosition	position;   /* Current position (between two
				     * characters, you know) */
XtTextScanDirection direction;  /* Direction to look */
{
     if (direction == XtsdLeft) {
	if (position == 0) {
	    return('\n');
	} else {
	    if ((position <= data->position) ||
		(position > data->position + data->charsInBuffer))
	    {
		TtyFillBuffer(data, position - 1, 1, FALSE);
	    }
	    return(data->buffer[position - data->position - 1]);
	}
    } else {
	if (position == data->length) {
	    return('\n');
	} else {
	    if ((position < data->position) ||
		(position >= data->position + data->charsInBuffer))
	    {
		TtyFillBuffer(data, position, 1, FALSE);
	    }
	    return(data->buffer[position - data->position]);
	}
    }
}

/*-
 *-----------------------------------------------------------------------
 * TtyReadText --
 *	Read text from the file.
 *
 * Results:
 *	The position after the characters read.
 *
 * Side Effects:
 *	The buffer is filled and *text changed.
 *
 *-----------------------------------------------------------------------
 */
static XtTextPosition
TtyReadText (src, pos, text, length)
    XtTextSource  	src;
    XtTextPosition	pos;	/** starting position */
    XtTextBlock		*text;	/** RETURNED: text read in */
    int			length;	/** max number of bytes to read **/
{
    XtTextPosition 	count;
    TtySourcePtr	data;
    
    data = (TtySourcePtr) src->data;
    if (length > TTY_BUF_SIZE) {
	XtError("TtyReadText: length > buffer size");
    }
    if ((pos < data->position) ||
	(data->position + data->charsInBuffer < pos + length))
    {
	TtyFillBuffer(data, pos, length, FALSE);
    }
    text->firstPos = pos;
    text->ptr = data->buffer + (pos - data->position);
    count = data->charsInBuffer - (pos - data->position);
    text->length = (length > count) ? count : length;
    if ((text->length == 0) && (pos != data->length) && (length != 0)){
	XtError("Premature EOF");
    }
    return pos + text->length;
}


/*-
 *-----------------------------------------------------------------------
 * TtyReplaceText --
 *	Replace text at the end of the file. If endPos is not the end of
 *	the file, an error is generated.
 *
 * Results:
 *	PositionError or EditDone if successful.
 *
 * Side Effects:
 *	Characters may be removed from the end of the file and/or added
 *	to it.
 *
 *-----------------------------------------------------------------------
 */
static int
TtyReplaceText (src, startPos, endPos, text)
    XtTextSource  	src;
    XtTextPosition	startPos;
    XtTextPosition	endPos;
    XtTextBlock		*text;
{
    char		*tmpPtr;
    TtySourcePtr	data;

    data = (TtySourcePtr) src->data;
    if ((endPos != data->length) || (startPos < data->fence)) {
	return (PositionError);
    }

    if (startPos != endPos) {
	/*
	 * Seek to the new end position and truncate the file to that
	 * length.
	 */
	data->length = startPos;
	ftruncate(data->fd, data->length);
	if (data->position + data->charsInBuffer > data->length) {
	    /*
	     * Reduce the number of characters in the buffer if it contains
	     * the last block of the file
	     */
	    data->charsInBuffer = data->length - data->position;
	}
	if (data->dirtyEnd > data->charsInBuffer) {
	    /*
	     * Trim dirty area to fit buffer
	     */
	    data->dirtyEnd = data->charsInBuffer - 1;
	}
	if (data->dirtyStart > data->charsInBuffer) {
	    /*
	     * If dirty area is now beyond buffer, nothing's
	     * dirty anymore
	     */
	    data->dirtyStart = data->dirtyEnd = -1;
	}
    }

    /* write the new text to the end of the file */
    if (text->length > 0) {
	int	  tmp;
	
	if (data->charsInBuffer + text->length > TTY_BUF_SIZE) {
	    /*
	     * Text won't fit in buffer -- refill it so it will.
	     */
	    if (text->length > TTY_BUF_SIZE) {
		XtError("TtyReplaceText: length > buffer size");
	    }
	    TtyFillBuffer(data, startPos, text->length, TRUE);
	}
	    
	tmpPtr = data->buffer + (startPos - data->position);
	bcopy(text->ptr + text->firstPos, tmpPtr, text->length);

	tmp = tmpPtr - data->buffer;
	if ((tmp < data->dirtyStart) || (data->dirtyStart < 0))
	{
	    data->dirtyStart = tmp;
	}

	tmp = (tmpPtr - data->buffer) + text->length - 1;
	if (tmp > data->dirtyEnd) {
	    data->dirtyEnd = tmp;
	}
	data->charsInBuffer += text->length;
	data->length += text->length;
    }
    
    return (EditDone);
}

/*-
 *-----------------------------------------------------------------------
 * TtySetLastPos --
 *	Set the bounding point for the file.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	The length recorded in the private data for this source
 *	is altered.
 *
 *-----------------------------------------------------------------------
 */
static int
TtySetLastPos (src, lastPos)
    XtTextSource  	src;
    XtTextPosition	lastPos;
{
    ((TtySourceData *)(src->data))->length = lastPos;
    return(EditDone);
}

/*-
 *-----------------------------------------------------------------------
 * TtyGetLastPos --
 *	Return the last position in the source.
 *
 * Results:
 *	The last position in the file is returned.
 *
 * Side Effects:
 *	None.
 *
 *-----------------------------------------------------------------------
 */
static XtTextPosition
TtyGetLastPos(src)
    XtTextSource  src;
{
    return ((TtySourceData *)(src->data))->length;
}

/*-
 *-----------------------------------------------------------------------
 * TtyScan --
 *	Scan through the file in the proper direction for the desired
 *	thing.
 *
 * Results:
 *	The new position.
 *
 * Side Effects:
 *	May refill the internal buffer.
 *
 *-----------------------------------------------------------------------
 */
static XtTextPosition
TtyScan (src, pos, sType, dir, count, include)
    XtTextSource 	 src;	    /* Source to use */
    XtTextPosition	 pos;	    /* Starting position */
    XtTextScanType	 sType;	    /* Type of scan desired */
    XtTextScanDirection  dir;	    /* Direction in which to go */
    int			 count;	    /* Number of sTypes to skip */
    Boolean		 include;   /* Scan inclusively? */
{
    TtySourcePtr  	data;
    XtTextPosition	position;
    int			i,
			incr;
    char		c;
    
    data = (TtySourcePtr) src->data;
    position = pos;
    switch (sType) {
	case XtstPositions: 
	    /*
	     * Scan count positions in the desired direction.
	     */
	    if (!include && count > 0) {
		count -= 1;
	    }
	    if (dir == XtsdLeft) {
		position -= count;
		if (position < 0) {
		    position = 0;
		}
	    } else {
		position += count;
		if (position > data->length) {
		    position = data->length;
		}
	    }
	    break;
	case XtstWhiteSpace:
	    /*
	     * Skip to and over count bits of whitespace
	     */
	    incr = (dir == XtsdLeft) ? -1 : 1;
	    for (i = 0; i < count; i++) {
		while (position >= 0 && position <= data->length) {
		    c = TtyLook(data, position, dir);
		    if ((c == ' ') || (c == '\t') || (c == '\n')) {
			break;
		    }
		    position += incr;
		}
		if (i + 1 != count) {
		    position += incr;
		}
	    }
	    if (include) {
		position += incr;
	    }
	    if (position < 0) {
		position = 0;
	    } else if (position > data->length) {
		position = data->length;
	    }
	    break;
	case XtstEOL: 
	    for (i = 0; i < count; i++) {
		while (position >= 0 && position <= data->length) {
		    if (TtyLook(data, position, dir) == '\n')
			break;
		    Increment(data, position, dir);
		}
		if (i + 1 != count)
		    Increment(data, position, dir);
	    }
	    if (include) {
		/* later!!!check for last char in file # eol */
		Increment(data, position, dir);
	    }
	    break;
	case XtstAll: 
	    if (dir == XtsdLeft)
		position = 0;
	    else
		position = data->length;
    }
    return(position);
}

/*-
 *-----------------------------------------------------------------------
 * TtyAddWidget --
 *	Warn that this function was called.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	A warning is printed.
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
static void
TtyAddWidget(src, w)
    XtTextSource  	src;
    Widget		w;
{
    XtWarning("TtyAddWidget called");
}

/*-
 *-----------------------------------------------------------------------
 * TtyRemoveWidget --
 *	Warn that this function was called.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	A warning is printed.
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
static void
TtyRemoveWidget(src, w)
    XtTextSource  	src;
    Widget		w;
{
    XtWarning("TtyRemoveWidget called");
}

/*-
 *-----------------------------------------------------------------------
 * TtySetSelection --
 *	Warn that this function was called.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	A warning is printed.
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
static void
TtySetSelection(src, left, right)
    XtTextSource  	src;
    XtTextPosition	left;
    XtTextPosition	right;
{
     /* XtWarning("TtySetSelection called"); */
}

/*-
 *-----------------------------------------------------------------------
 * TtyGetSelection --
 *	Warn that this function was called.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	A warning is printed.
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
static Boolean
TtyGetSelection(src, left, right)
    XtTextSource  	src;
    XtTextPosition	left;
    XtTextPosition	right;
{
    XtWarning("TtyGetSelection called");
}

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

/*-
 *-----------------------------------------------------------------------
 * XtTtySourceCreate --
 *	Create a tty source. Only allows edit_mode XttextAppend, though
 *	the caller may specify a file to use either in args or as a
 *	fileName resource.
 *
 * Results:
 *	The new source.
 *
 * Side Effects:
 *	The file is opened and, if no fileName resource was found, a
 *	temporary file is created. The widget must be properly destroyed
 *	for the temporary file to be unlinked.
 *
 *-----------------------------------------------------------------------
 */
XtTextSource
XtTtySourceCreate(parent, args, num_args)
    Widget	parent;
    ArgList	args;
    Cardinal	num_args;
{
    XtTextSource src;
    TtySourcePtr data;

    src = XtNew(XtTextSourceRec);
    data = XtNew(TtySourceData);
    
    src->Read =   	TtyReadText;
    src->Replace =	TtyReplaceText;
    src->GetLastPos =	TtyGetLastPos;
    src->SetLastPos =	TtySetLastPos;
    src->Scan =		TtyScan;
    src->AddWidget =	TtyAddWidget;
    src->RemoveWidget =	TtyRemoveWidget;
    src->SetSelection = TtySetSelection;
    src->GetSelection = TtyGetSelection;
    src->edit_mode =	XttextAppend;
    src->data =   	(caddr_t)data;
    
    XtGetSubresources (parent, (caddr_t)data, XtNtextSource, XtCTextSource,
		       diskResources, XtNumber(diskResources),
		       args, num_args);
    
    if (data->fileName == NULL) {
	data->fileName = tmpnam (XtMalloc((unsigned)TMPSIZ));
	data->is_tempfile = TRUE;
    } else {
        data->is_tempfile = FALSE;
    }
    
    if ((data->fd = open(data->fileName, O_RDWR|O_CREAT|O_TRUNC, 0666)) == 0) {
	XtError("Cannot open source file in XtTtySourceCreate");
    }
    data->fence = data->length = 0;
    data->position = data->length;
    data->dirtyStart = data->dirtyEnd = -1;
    data->charsInBuffer = 0;
    return src;
}

/*-
 *-----------------------------------------------------------------------
 * XtTtySourceSetFence --
 *	Set the boundary beyond which characters may not be deleted.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	data->fence is set to the given position.
 *
 *-----------------------------------------------------------------------
 */
void
XtTtySourceSetFence(src, fence)
    XtTextSource  	src;
    XtTextPosition	fence;
{
    TtySourcePtr  	data;

    data = (TtySourcePtr)src->data;

    if (fence <= data->length && fence >= 0) {
	data->fence = fence;
    }
}

/*-
 *-----------------------------------------------------------------------
 * XtTtySourceDestroy --
 *	Free up data associated with this source, including the
 *	XtTextSource structure itself.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Memory is freed and any temporary file opened is unlinked.
 *
 *-----------------------------------------------------------------------
 */
void
XtTtySourceDestroy (src)
    XtTextSource src;
{
    TtySourcePtr data;
    
    data = (TtySourcePtr) src->data;

    if (data->is_tempfile) {
        unlink(data->fileName);
	XtFree((char *) data->fileName);
    }
    close(data->fd);
    
    XtFree((char *) data);
    XtFree((char *) src);
}
@