This is slvmstty.c in view mode; [Download] [Up]
/* Copyright (c) 1992, 1995 John E. Davis
* All rights reserved.
*
* You may distribute under the terms of either the GNU General Public
* License or the Perl Artistic License.
*/
#include "config.h"
#include <stdio.h>
#include <ssdef.h>
#include <rmsdef.h>
#include <dvidef.h>
#include <jpidef.h>
#include <descrip.h>
#include <iodef.h>
#include <ttdef.h>
#include <tt2def.h>
#include <rms.h>
#include <errno.h>
#ifdef __DECC
#include <starlet.h>
#include <stdlib.h>
#include <lib$routines>
#endif
#include <string.h>
#include "slang.h"
#include "_slang.h"
typedef struct { /* I/O status block */
short i_cond; /* Condition value */
short i_xfer; /* Transfer count */
long i_info; /* Device information */
} Iosb_Type;
typedef struct { /* Terminal characteristics */
char t_class; /* Terminal class */
char t_type; /* Terminal type */
short t_width; /* Terminal width in characters */
long t_mandl; /* Terminal's mode and length */
long t_extend; /* Extended terminal characteristics */
} TermChar_Type;
static TermChar_Type Old_Term_Char, New_Term_Char;
/* This serves to identify the channel we are reading input from. */
static short This_Term;
typedef struct
{
short buflen;
short item_code;
int *buf_addr;
int *len_addr;
} item_list_3;
static int TTY_Inited;
/*
* Exit Handler Control Block
*/
static struct argument_block
{
int forward_link;
int (*exit_routine)();
int arg_count;
int *status_address;
int exit_status;
}
exit_block =
{
0,
NULL,
1,
&exit_block.exit_status,
0
};
static void vms_cancel_exithandler()
{
sys$canexh(exit_block);
}
static int vms_exit_handler ()
{
if (TTY_Inited == 0) return 0;
SLang_reset_tty ();
return 0;
}
static int vms_input_buffer;
static struct vms_ast_iosb
{
short status;
short offset;
short termlen;
short term;
} vms_ast_iosb;
static void vms_que_key_ast();
static int Ast_Fired_Event_Flag;
static int Timer_Event_Flag;
static int Event_Flag_Mask;
static int Ast_Stop_Input;
static int Waiting_For_Ast;
static int getkey_ast(int not_used)
{
unsigned int c = 1000;
if (vms_ast_iosb.offset)
{
c = (unsigned int) vms_input_buffer;
}
if (c <= 255)
{
if (c == SLang_Abort_Char)
{
if (SLang_Ignore_User_Abort == 0) SLang_Error = USER_BREAK;
SLKeyBoard_Quit = 1;
}
if (SLang_Input_Buffer_Len < MAX_INPUT_BUFFER_LEN - 3)
SLang_Input_Buffer[SLang_Input_Buffer_Len++] = c;
}
if (Waiting_For_Ast) sys$setef (Ast_Fired_Event_Flag);
Waiting_For_Ast = 0;
vms_que_key_ast();
return (1);
}
static void vms_que_key_ast()
{
static int trmmsk [2] = { 0, 0 };
int status;
if (Ast_Stop_Input) return;
status = sys$qio (0, This_Term,
IO$_READVBLK | IO$M_NOECHO | IO$_TTYREADALL,
&vms_ast_iosb, getkey_ast, 1,
&vms_input_buffer, 1, 0, trmmsk, 0, 0);
}
static char TTY_Name[8];
static int This_Process_Pid;
int SLang_init_tty (int a, int flow, int out)
{
Iosb_Type iostatus;
int tmp, name_len, status, lastppid, ppid;
item_list_3 itmlst[3];
$DESCRIPTOR ( term, TTY_Name);
itmlst[0].buflen = sizeof(int);
itmlst[0].item_code = JPI$_PID;
itmlst[0].buf_addr = &This_Process_Pid;
itmlst[0].len_addr = &tmp;
itmlst[1].buflen = 7;
itmlst[1].item_code = JPI$_TERMINAL;
itmlst[1].buf_addr = (int *) TTY_Name;
itmlst[1].len_addr = &name_len;
itmlst[2].buflen = 0;
itmlst[2].item_code = 0;
itmlst[2].buf_addr = 0;
itmlst[2].len_addr = 0;
if (a == -1) a = 3; /* ^C */
SLang_Abort_Char = a;
TTY_Inited = 1;
ppid = 0, lastppid = -1;
/* Here I get this process pid then I get the master process pid
and use the controlling terminal of that process. */
while (1)
{
status = sys$getjpiw(0, /* event flag */
&ppid, /* pid address */
0, /* proc name address */
itmlst,
0, 0, 0);
if (status != SS$_NORMAL)
{
fprintf(stderr, "PID: %X, status: %X\n", This_Process_Pid, status);
exit(1);
}
if (lastppid == ppid) break;
lastppid = ppid;
itmlst[0].item_code = JPI$_MASTER_PID;
itmlst[0].buf_addr = &ppid;
}
term.dsc$w_length = name_len;
status = sys$assign ( &term, &This_Term, 0, 0 );
if (status != SS$_NORMAL)
{
fprintf(stderr,"Unable to assign input channel\n");
fprintf(stderr,"PID: %X, DEV %s, status: %d\n", This_Process_Pid, TTY_Name, status);
exit(0);
}
if (NULL == exit_block.exit_routine)
{
exit_block.exit_routine = (int (*)()) vms_exit_handler;
sys$dclexh(&exit_block);
}
/* allocate an event flag and clear it--- used by ast routines. Since
* I am only using a few local event flags, there is really no need to
* worry about freeing these.
*
* The event flags are used to avoid timing problems with the getkey AST
* as well as for a form of time out.
*/
if (!Ast_Fired_Event_Flag) lib$get_ef (&Ast_Fired_Event_Flag);
sys$clref (Ast_Fired_Event_Flag);
if (!Timer_Event_Flag) lib$get_ef (&Timer_Event_Flag);
sys$clref (Timer_Event_Flag);
/* The working assumption here is that the event flags are in the same
* cluster. They need not be but it is very likely that they are.
*/
Event_Flag_Mask = ((unsigned) 1 << (Ast_Fired_Event_Flag % 32));
Event_Flag_Mask |= ((unsigned) 1 << (Timer_Event_Flag % 32));
Waiting_For_Ast = 0;
Ast_Stop_Input = 0;
/* Get the startup terminal characteristics */
status = sys$qiow(0, /* Wait on event flag zero */
This_Term, /* Channel to input terminal */
IO$_SENSEMODE, /* Get current characteristic */
&iostatus, /* Status after operation */
0, 0, /* No AST service */
&Old_Term_Char, /* Terminal characteristics buf */
sizeof(Old_Term_Char),/* Size of the buffer */
0, 0, 0, 0);
New_Term_Char = Old_Term_Char;
New_Term_Char.t_mandl |= TT$M_EIGHTBIT | TT$M_NOECHO;
New_Term_Char.t_extend |= TT2$M_PASTHRU | TT2$M_XON;
status = sys$qiow(0, /* Wait on event flag zero */
This_Term, /* Channel to input terminal */
IO$_SETMODE, /* Set current characteristic */
&iostatus, /* Status after operation */
0, 0, /* No AST service */
&New_Term_Char, /* Terminal characteristics buf */
sizeof(New_Term_Char),/* Size of the buffer */
0, 0, 0, 0);
vms_que_key_ast(); /* set up the key ast */
return 0;
}
static void cancel_ast (void)
{
if (TTY_Inited == 0) return;
/* stop the keyboard ast */
sys$setast (0); /* disable AST delivery */
sys$clref (Ast_Fired_Event_Flag);
Waiting_For_Ast = 1;
Ast_Stop_Input = 1;
/* cancel all i/o on this channel. This canels pending, as well as those
* already in progress and queued. In particular, according to the
* manuals, cancelling I/O on the channel will cause the getkey AST
* to fire even though the sys$qio call was aborted. This is crucial
* because below we wait for the AST to set the event flag.
*/
sys$cancel (This_Term);
sys$setast (1); /* enable ASTs again */
sys$waitfr (Ast_Fired_Event_Flag); /* sleep until it fires */
Waiting_For_Ast = 0;
}
void SLang_reset_tty (void)
{
Iosb_Type iostatus;
if (!TTY_Inited) return;
cancel_ast ();
TTY_Inited = 0;
/* reset the terminal characteristics */
sys$qiow(0, /* event flag 0 */
This_Term, /* Channel to input terminal */
IO$_SETMODE, /* Set current characteristic */
&iostatus, /* Status after operation */
0, 0, /* No AST service */
&Old_Term_Char, /* Terminal characteristics buf */
sizeof(Old_Term_Char), /* Size of the buffer */
0, 0, 0, 0); /* unused */
}
unsigned int SLsys_getkey()
{
unsigned int c;
if (SLKeyBoard_Quit) return((unsigned int) SLang_Abort_Char);
/* On VMS, the keyboard ast routine should be stuffing the buffer, so
do nothing except sleep */
/* clear the flag which ast will set */
Waiting_For_Ast = 0;
if (SLang_Input_Buffer_Len) return(SLang_getkey());
while (!SLsys_input_pending(450));
c = SLang_getkey();
return(c);
}
/* waits *secs tenth of seconds for input */
int SLsys_input_pending(int tsecs)
{
unsigned long daytim[2];
if (SLang_Input_Buffer_Len) return(SLang_Input_Buffer_Len);
if (tsecs)
{
/* takes a quad word time. If negative, use a relative time. */
daytim[1] = 0xFFFFFFFF;
daytim[0] = -(tsecs * 1000 * 1000); /* 1000 * 1000 is a tenth of a sec */
sys$clref (Ast_Fired_Event_Flag);
/* sys$clref (Timer_Event_Flag); sys$setimr call clears this */
/* set up a flag for the ast so it knows to set the event flag */
Waiting_For_Ast = 1;
sys$setimr(Timer_Event_Flag, daytim, 0, 1);
/* this will return when ast does its job or timer expires.
* The first argument simply serves to identify the cluster for
* the event flag and that is all. The second argument serves
* to identify the event flags to wait for.
*/
sys$wflor (Ast_Fired_Event_Flag, Event_Flag_Mask);
Waiting_For_Ast = 0;
/* cancel the timer */
sys$cantim(1, 3); /* 3 is user mode */
}
return (SLang_Input_Buffer_Len);
}
void SLang_set_abort_signal (void (*f)(int))
{
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.