home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
531.lha
/
Less_v1.4Z
/
src.LZH
/
src
/
io.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-07-03
|
18KB
|
613 lines
/* io.c */
#ifdef AMIGA
/* Compile with -HPreHeader.q to get "less.h"! */
#else
#include "less.h"
#endif
#include <ctype.h>
#include <string.h>
#include <intuition/intuition.h>
#include <dos.h>
#undef TRUE
#undef FALSE
extern int sigs;
static struct ConCom *tty = NULL;
public int nrow = 0; /* Terminal size, rows. */
public int ncol; /* Terminal size, columns. */
static struct Window *LessWindow;
int Wind_Spec[4] = { 0, 0, 0, 0 };
/* User-supplied option specifying window size/position:
*
* [0]: Left edge X - coord.
* [1]: Top edge Y - coord.
* [2]: Right edge X - coord.
* [3]: Bottom edge Y - coord.
*
* If element 2 or 3 is negative, it is taken to be a relative value to be
* subtracted from the maximum screen size. If the resulting window
* would be smaller than the minimum, it is quietly enlarged to the
* minimum, by expanding to the lower right as far as possible, and then
* moving the window toward the upper left if necessary.
*/
#define W_MINWIDTH 200
#define W_MINHEIGHT 60
extern char version[];
/* Prototypes for functions defined in io.c */
static struct ConCom *OpenConsole __PROTO((struct Window *window));
static void CloseConsole __PROTO((struct ConCom *tty));
static void StartTtyTimer __PROTO((void));
static void StartConRead __PROTO((struct ConCom *tty));
static int ConGetC __PROTO((struct ConCom *tty));
static int ConLookC __PROTO((struct ConCom *tty));
static void ConWrite __PROTO((struct ConCom *tty,
char *data,
int length));
/****************************************************************/
/* Code to open a console on an existing Intuition window.
See RKM Libraries & Devices, pp 277 ff
*/
#include <exec/memory.h>
#include <devices/timer.h>
extern struct GfxBase *GfxBase;
extern struct IOStdReq * CreateStdIO();
extern struct MsgPort *CreatePort();
struct ConCom
{
struct IOStdReq *ConWriteReq; /* I/O write request */
struct IOStdReq *ConReadReq; /* I/O read request */
struct MsgPort *RdReplyPort; /* pointer to ReplyPort for console read */
struct timerequest *TimerMsg; /* If != NULL, timer is open */
};
static struct ConCom *OpenConsole ( struct Window *window );
static void CloseConsole ( struct ConCom *tty );
static void StartConRead ( struct ConCom *tty );
static int ConGetC ( struct ConCom *tty );
static int ConLookC ( struct ConCom *tty );
static void ConWrite ( struct ConCom *c, char *data, int length );
static struct ConCom *OpenConsole (struct Window *window)
{
struct ConCom *tty;
struct MsgPort *WrReplyPort, *TimerPort;
if ( tty = (struct ConCom *)
AllocMem(sizeof(struct ConCom), MEMF_CLEAR | MEMF_PUBLIC) )
{
if ( (WrReplyPort = CreatePort(0, 0)) /* reply port for write */
&& (tty->RdReplyPort = CreatePort(0, 0)) /* reply port for read */
&& (tty->ConWriteReq = CreateStdIO ( WrReplyPort ))
&& (tty->ConReadReq = CreateStdIO ( tty->RdReplyPort)) )
{
tty->ConWriteReq->io_Data = (APTR) window;
tty->ConWriteReq->io_Length = sizeof(struct Window);
if ( !(OpenDevice ( "console.device", 0, tty->ConWriteReq, 0 )) )
{
tty->ConReadReq->io_Device = tty->ConWriteReq->io_Device;
tty->ConReadReq->io_Unit = tty->ConWriteReq->io_Unit;
/* Try to set up regular interrupts for ^C check */
TimerPort = CreatePort(NULL, 0);
if (
TimerPort
&& ( tty->TimerMsg = (struct timerequest *)
CreateExtIO(TimerPort, sizeof(struct timerequest)) )
&& !OpenDevice (TIMERNAME, UNIT_VBLANK,
(struct IORequest *)(tty->TimerMsg), 0)
)
return tty; /* All ok! */
/* Main console ports ok, but no timer. We'll live
without it...
*/
if ( TimerPort ) DeletePort ( TimerPort );
if ( tty->TimerMsg )
DeleteExtIO( (struct IORequest *)(tty->TimerMsg),
(long)sizeof(struct timerequest) );
tty->TimerMsg = NULL;
return tty;
}
}
/* If get here, something went wrong */
if ( tty->ConReadReq ) DeleteStdIO(tty->ConReadReq);
if ( tty->RdReplyPort ) DeletePort(tty->RdReplyPort);
if ( tty->ConWriteReq ) DeleteStdIO(tty->ConWriteReq);
if ( WrReplyPort ) DeletePort(WrReplyPort);
}
if ( tty )
FreeMem(tty, sizeof(struct ConCom));
return NULL;
}
static void CloseConsole (struct ConCom *tty)
{
struct MsgPort *mp;
AbortIO(tty->ConReadReq); /* Abort any read in progress */
CloseDevice(tty->ConWriteReq); /* close console device */
mp = tty->ConWriteReq->io_Message.mn_ReplyPort;
DeleteStdIO(tty->ConWriteReq);
DeletePort(mp);
mp = tty->ConReadReq->io_Message.mn_ReplyPort;
DeleteStdIO(tty->ConReadReq);
DeletePort(mp);
if (tty->TimerMsg)
{
AbortIO((struct IORequest *)(tty->TimerMsg));
CloseDevice((struct IORequest *)(tty->TimerMsg));
DeletePort ( tty->TimerMsg->tr_node.io_Message.mn_ReplyPort );
DeleteExtIO ( (struct IORequest *)(tty->TimerMsg),
(long)sizeof(struct timerequest) );
}
FreeMem(tty, sizeof(struct ConCom));
return;
}
/* Request a timer interrupt in 0.25 seconds. Use this to poll for ^C */
static void StartTtyTimer (void)
{
if ( !tty->TimerMsg ) return;
tty->TimerMsg->tr_node.io_Command = TR_ADDREQUEST;
tty->TimerMsg->tr_time.tv_secs = 0;
tty->TimerMsg->tr_time.tv_micro = 250000L;
SendIO ( (struct IORequest *)(tty->TimerMsg) );
}
static char buffer; /* buffer for incoming console character */
/* asynchronous console read request--must be called once before
any ConGetC requests
*/
static void StartConRead (struct ConCom *tty)
{
struct IOStdReq *conr;
conr = tty->ConReadReq;
conr->io_Command = CMD_READ;
conr->io_Length = 1;
conr->io_Data = (APTR) &buffer;
SendIO(conr); /* asynchronous posting of a read request */
}
/* Get a character from the console.
*/
static int ConGetC (struct ConCom *tty)
{
struct MsgPort *mp, *tp;
struct IOStdReq *rddata;
int temp;
ULONG ReadMsgBit, ClockMsgBit, MsgBits;
mp = tty->RdReplyPort; /* get the read reply port */
ReadMsgBit = 1 << mp->mp_SigBit;
if ( tty->TimerMsg )
{
tp = tty->TimerMsg->tr_node.io_Message.mn_ReplyPort;
ClockMsgBit = 1 << tp->mp_SigBit;
}
else ClockMsgBit = 0;
rddata = NULL;
do /* Wait for a character. Wake up periodically so that ^C works */
{
MsgBits = Wait(ReadMsgBit | ClockMsgBit);
if ( MsgBits & ReadMsgBit )
rddata = (struct IOStdReq *) GetMsg ( mp );
if ( MsgBits & ClockMsgBit )
if ( GetMsg (tp) )
{
StartTtyTimer();
chkabort();
}
} while ( !rddata );
/* We've got a character... */
temp = buffer; /* get the character */
StartConRead ( tty ); /* set up next read */
return temp;
}
/* See if a character is available at the console.
If so, get it, else return -1.
*/
static int ConLookC (struct ConCom *tty)
{
struct MsgPort *mp;
struct IOStdReq *rddata;
int temp;
mp = tty->RdReplyPort; /* get the read reply port */
rddata = (struct IOStdReq *) GetMsg ( mp );
if ( !rddata ) return -1;
/* We've got a character... */
temp = buffer; /* get the character */
StartConRead ( tty ); /* set up next read */
return temp;
}
/* write a specified number of characters from a buffer to console device
*/
static void ConWrite (struct ConCom *tty, char *data, int length)
{
struct IOStdReq *wrdata;
wrdata = tty->ConWriteReq;
wrdata->io_Command = CMD_WRITE;
wrdata->io_Length = length;
wrdata->io_Data = (APTR) data;
DoIO(wrdata); /* waits until write completes before continuing */
}
/****************************************************************/
/*
* This routine gets called once, to set up the
* terminal channel.
*/
void ttopen (void)
{
int wl, wt; /* window upper left corner specification */
static struct Screen __aligned WBScreen, *wbsdata;
static int IsV2;
static struct NewWindow NewLessWindow =
{
0, 0, 640, 200, /* position & size (may be changed) */
-1, -1, /* pens */
0, /* IDCMP */
WINDOWDEPTH | WINDOWSIZING | WINDOWDRAG | WINDOWCLOSE | ACTIVATE
| SMART_REFRESH | NOCAREREFRESH,
NULL, NULL, /* Gadgets, Checkmark */
"Less 1.4Z: h for help",
NULL, NULL, /* Use WB screen */
W_MINWIDTH, W_MINHEIGHT, -1, -1,
WBENCHSCREEN
};
if (tty) return;
if (GfxBase = /* V2.0 + ? */
(struct GfxBase *)OpenLibrary("graphics.library", 36) )
{
CloseLibrary ( GfxBase );
IsV2 = 1;
if ( !(wbsdata = LockPubScreen("Workbench")) )
{
error ( "Can't find Workbench screen" );
quit();
}
}
else
{
IsV2 = 0;
if ( !GetScreenData ( &WBScreen, sizeof(struct Screen),
WBENCHSCREEN, NULL ) )
{
error ( "Can't find Workbench screen" );
quit();
}
wbsdata = &WBScreen;
}
if ( (wl = Wind_Spec[0]) < 0 ) wl += wbsdata->Width;
if ( wl < 0 ) wl = 0;
if ( wl > wbsdata->Width )
wl = wbsdata->Width - W_MINWIDTH;
if ( (wt = Wind_Spec[1]) < 0 ) wt += wbsdata->Height;
if ( wt < 0 ) wt = 0;
if ( wt > wbsdata->Height )
wt = wbsdata->Height - W_MINHEIGHT;
if ( wl < 0 || wt < 0 )
{
error ( "Window won't fit on screen" );
quit();
}
NewLessWindow.LeftEdge = wl;
NewLessWindow.TopEdge = wt;
NewLessWindow.Width = Wind_Spec[2];
if ( NewLessWindow.Width <= 0 )
NewLessWindow.Width += wbsdata->Width;
if ( NewLessWindow.Width <= 0 )
NewLessWindow.Width = 0;
NewLessWindow.Height = Wind_Spec[3];
if ( NewLessWindow.Height <= 0 )
NewLessWindow.Height += wbsdata->Height;
if ( NewLessWindow.Height <= 0 )
NewLessWindow.Height = 0;
if ( NewLessWindow.Width < W_MINWIDTH )
NewLessWindow.Width = W_MINWIDTH;
if ( NewLessWindow.Height < W_MINHEIGHT )
NewLessWindow.Height = W_MINHEIGHT;
if ( NewLessWindow.LeftEdge + NewLessWindow.Width > wbsdata->Width )
NewLessWindow.Width = wbsdata->Width - NewLessWindow.LeftEdge;
if ( NewLessWindow.TopEdge + NewLessWindow.Height > wbsdata->Height )
NewLessWindow.Height = wbsdata->Height - NewLessWindow.TopEdge;
if ( NewLessWindow.Width < W_MINWIDTH )
NewLessWindow.LeftEdge = wbsdata->Width - W_MINWIDTH;
if ( NewLessWindow.Height < W_MINHEIGHT )
NewLessWindow.TopEdge = wbsdata->Height - W_MINHEIGHT;
if ( NewLessWindow.LeftEdge < 0 || NewLessWindow.TopEdge < 0 )
{
error ( "Window won't fit on screen" );
quit();
}
if (IsV2)
UnlockPubScreen(NULL, wbsdata);
if (!(LessWindow = (struct Window *) OpenWindow (&NewLessWindow)) )
{
error ( "Can't open Less window" );
quit();
}
if (!(tty = OpenConsole(LessWindow)))
{
error ( "Can't open console device" );
quit();
}
StartConRead ( tty );
if ( tty->TimerMsg ) StartTtyTimer();
/* enable report of window resizeing and close gadget */
ConWrite(tty, "\x9b\x31\x32;11{", 7);
}
/* Request size of window information
*/
void getrowcol (void)
{
static unsigned char buf[16], *p;
if ( !tty) ttopen();
ConWrite(tty, "\x9b\x30\x20\x71", 4); /* request current window size */
p = buf;
while ( (*p = ConGetC(tty)) != 0x9b ) /* nothing */;
p++;
do
{
*p = ConGetC(tty);
} while ( p < &(buf[15]) && *p++ != '\x72' );
*p = '\0';
/* buf has "En;n;n;n|", where E is 0x9b, n is a decimal number */
p = buf+1;
do
{
if ( p = strchr ( p, ';' ) ) p++; /* skip window position */
else break;
if ( p = strchr ( p, ';' ) ) p++;
else break;
nrow = atoi ( p ); /* window height */
if ( p = strchr ( p, ';' ) ) p++;
else break;
ncol = atoi ( p ); /* window width */
} while (0); /* just once! */
/* arbitrary data integrity checks: */
if ( nrow < 2 || nrow > 99 || ncol < 10 || ncol > 200 )
{
/* Window probably too small for the font chosen. We may not
be able to even write an error message in the window! */
MyRequester ( "Screen/Font mismatch" );
quit();
}
}
/*
* This function gets called just
* before we go back home to the command interpreter.
* On the Amiga it closes up the virtual terminal window.
*/
void ttclose (void)
{
if (tty != (struct ConCom *) 0L) {
CloseConsole(tty);
CloseWindow(LessWindow);
}
tty = NULL;
nrow = 0;
}
/*
* Read a character from the terminal,
* performing no editing and doing conditional echo
* but interpretting window resize and close gadget reports
*/
int do_echo = 1; /* echo flag */
int ttgetc (void)
{
unsigned char c; /* must be unsigned! */
while ( (c = ConGetC(tty)) == '\x9b' )
{
switch (c = ConGetC(tty))
{
case '1': /* raw input event */
if ( (c = ConGetC(tty)) == '1' )
quit(); /* close gadget */
else if ( c == '2' ) /* window resize */
{
while ( (c = ConGetC(tty)) != '|') /* nothing */;
winch();
}
break;
case '?': /* Help key */
if ( (c = ConGetC(tty)) == '~' ) return 'h';
break;
case 'A':
case 'T': /* Arrow up */
c = 'b';
break;
case 'B':
case 'S': /* Arrow down */
c = ' ';
break;
case 'D': /* Arrow left */
c = 'k';
break;
case 'C': /* Arrow right */
c = '\r';
break;
case ' ': /* Shifted left or right */
if ( (c = ConGetC(tty)) == 'A' ) c = 'u';
else c = 'd';
break;
default:
continue;
}
break;
}
if ( c == 3 )
{
sigs |= 01;
psignals();
/* no return */
}
if (do_echo)
ttwrite(&c, 1);
return ((int) c);
}
/*
* Check for ^C in the tty window. This routine will throw
* away any characters waiting in the tty input buffer. Returns when
* there's nothing in the input queue or one of the following has been
* recognized:
*
* Close gadget (exits)
* Window resize (resets window and returns)
* ^C (sets sigs and returns)
*/
int chk_sigs (void)
{
int c;
for (;;)
{
if ( (c = ConLookC(tty)) < 0 ) return sigs;
switch ( c )
{
case '\x9b': /* raw input event */
if ( (c = ConGetC(tty)) != '1' ) break; /* unexpected raw input */
if ( (c = ConGetC(tty)) == '1' )
quit(); /* close gadget */
else if ( c == '2' ) /* window resize */
{
while ( (c = ConGetC(tty)) != '|') /* nothing */;
winch();
return sigs;
}
break;
case 3:
sigs |= 01;
return sigs;
}
}
}
/*
* Write a buffer of characters to the display.
*/
void ttwrite (char *buffer, int length)
{
ConWrite ( tty, buffer, length );
}
/*
* Write a string to the terminal
*/
void ttputs (char *s)
{
ConWrite ( tty, s, strlen(s) );
}
/* fake termcap output */
/* This takes the place of the original tputs(x,y,z), using only the
first parameter
*/
void Tputs (char *s)
{
flush();
if ( s ) ConWrite ( tty, s, strlen(s) );
}
/*
* We may not have a window: display a message in a requester
*/
int MyRequester (char *s)
{
static struct IntuiText __aligned
Ok =
{
AUTOFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
AUTOLEFTEDGE, AUTOTOPEDGE,
AUTOITEXTFONT,
"Ok!",
AUTONEXTTEXT
},
IMsg =
{
AUTOFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
AUTOLEFTEDGE, AUTOTOPEDGE + 20, /* Hope this is enough for default font */
AUTOITEXTFONT,
"",
AUTONEXTTEXT
},
LessMsg =
{
AUTOFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
AUTOLEFTEDGE, AUTOTOPEDGE,
AUTOITEXTFONT,
"Less 1.4Z:",
&IMsg
};
IMsg.IText = s;
return (int)AutoRequest(NULL, &LessMsg, &Ok, &Ok, NULL, NULL, 250, 80);
}