|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T
Length: 37520 (0x9290)
Types: TextFile
Names: »Tty.c,v«
└─⟦a05ed705a⟧ Bits:30007078 DKUUG GNU 2/12/89
└─⟦34cc4e2f7⟧ »./UNRELEASED/xgdb3.2.tar.Z«
└─⟦80fac5d7c⟧
└─⟦this⟧ »./RCS/Tty.c,v«
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);
}
@