diff options
Diffstat (limited to 'nx-X11/programs/xterm/vms.c')
-rw-r--r-- | nx-X11/programs/xterm/vms.c | 725 |
1 files changed, 725 insertions, 0 deletions
diff --git a/nx-X11/programs/xterm/vms.c b/nx-X11/programs/xterm/vms.c new file mode 100644 index 000000000..155cdb41b --- /dev/null +++ b/nx-X11/programs/xterm/vms.c @@ -0,0 +1,725 @@ +/* $XFree86: xc/programs/xterm/vms.c,v 1.2 2003/10/27 01:07:58 dickey Exp $ */ + +/* vms.c + * + * This module contains the VMS version of the routine SPAWN (from the module + * MAIN.C) and the routines that do IO to the pseudo terminal. + * + * Modification History: + * Stephan Jansen 1-Mar-1990 Original version + * Hal R. Brand 5-Sep-1990 Added code to propagate DECW$DISPLAY + * Aaron Leonard 11-Sep-1990 Fix string descriptor lengths + * Stephan Jansen 2-Dec-1991 Modify to use new Pseudo terminal drivers + * (patterned after photo.c by Forrest A. Kenney) + * Patrick Mahan 7-Jan-1991 Removed reference to <dvidef.h> from VMS.C + * Forced device type to be VT102 since that is + * what we are emulating. + */ + +#include <libdef.h> +#include <lnmdef.h> + +#include <stdio.h> +#include <string.h> + +#include "xterm.h" +#include "data.h" +#include "vms.h" + +#define PTD$C_SEND_XON 0 /* Pseudo Terminal Driver event */ +#define PTD$C_SEND_BELL 1 +#define PTD$C_SEND_XOFF 2 +#define PTD$C_STOP_OUTPUT 3 +#define PTD$C_RESUME_OUTPUT 4 +#define PTD$C_CHAR_CHANGED 5 +#define PTD$C_ABORT_OUTPUT 6 +#define PTD$C_START_READ 7 +#define PTD$C_MIDDLE_READ 8 +#define PTD$C_END_READ 9 +#define PTD$C_ENABLE_READ 10 +#define PTD$C_DISABLE_READ 11 +#define PTD$C_MAX_EVENTS 12 + +#define BUFFERS 6 +#define PAGE 512 + +typedef struct tt_buffer +{ +unsigned int flink; +unsigned int blink; +short int status; +short int length; +char data[VMS_TERM_BUFFER_SIZE]; +} TT_BUF_STRUCT; + +TT_BUF_STRUCT *tt_w_buff; +struct q_head _align(QUADWORD) buffer_queue = (0,0); +struct q_head _align(QUADWORD) read_queue = (0,0); + +static char tt_name[64]; +static $DESCRIPTOR (tt_name_desc, &tt_name); + +static char ws_name[64]; +static $DESCRIPTOR (ws_name_desc, &ws_name); + +static struct tt_char { + char class; + char type; + short int page_width; + char characteristics[3]; + char length; + int extended; + } tt_mode, tt_chars, orig_tt_chars; + +struct mem_region +{ + TT_BUF_STRUCT *start; + TT_BUF_STRUCT *end; +} ret_addr; + +int read_stopped = FALSE; +int write_stopped = FALSE; + +int tt_width; +int tt_length; +int tt_changed; +int tt_pasting=FALSE; /* drm */ +int tt_new_output=FALSE; /* Cleared by flushlog(), set whenever something new + goes to the screen through tt_write */ + +int trnlnm(char *in,int id,char *out); +int tt_write(char *tt_write_buf,int size); +void spawn (void); + +static void tt_echo_ast(TT_BUF_STRUCT *buff_addr); +static void tt_read_ast(TT_BUF_STRUCT *buff_addr); + +/* +static void tt_start_read(void); +*/ +void tt_start_read(void); +int tt_read(char *buffer); +static void send_xon(void); +static void send_xoff(void); +static void send_bell(void); +static void char_change(void); +static void freeBuff (TT_BUF_STRUCT *buff_addr); +TT_BUF_STRUCT *getBuff(void); +static void CloseDown(int exit_status); +static void mbx_read_ast(void); +static void mbx_read(void); + + + +#define DESCRIPTOR(name,string) struct dsc$descriptor_s name = \ +{ strlen(string), DSC$K_DTYPE_T, DSC$K_CLASS_S, string } + +int trnlnm(char *in, int id, char *out) +{ + int status, num, len, attr = LNM$M_CASE_BLIND, foo = id; + short outlen; + struct itemlist + { + short buffer_length; + short item_code; + char *buffer_addr; + int *return_length; + } itmlst[] = + { + 4 , LNM$_INDEX , &foo, 0, + 255, LNM$_STRING , out , &outlen, + 4 , LNM$_MAX_INDEX, &num, &len, + 0 , 0 + }; + DESCRIPTOR(lognam,in); + DESCRIPTOR(tabnam,"LNM$DCL_LOGICAL"); + + status = sys$trnlnm(&attr,&tabnam,&lognam,0,itmlst); + if(status != SS$_NORMAL) return(-1); /* error status */ + out[outlen] = 0; /* terminate the output string */ + return(++num); /* return number of translations */ +} + +static int pty; +static int Xsocket; + +void spawn (void) +{ + int status; + static $DESCRIPTOR (dtime, "0 00:00:00.01"); + static int delta[2]; + register TScreen *screen = &term->screen; + static struct IOSB iosb; + static unsigned int flags; + static unsigned int uic; + static char imagename[64]; + static int privs; + static $DESCRIPTOR(device, "FTA0:"); + static int type; + static int class; + static int devdepend; + static int mem_size; + int i; + + /* if pid and mbx_chan are nonzero then close them in CloseDown() */ + pid = 0; + mbx_chan = 0; + + status = SYS$EXPREG (BUFFERS, &ret_addr, 0, 0); + if(!(status & SS$_NORMAL)) lib$signal(status); + + tt_w_buff = (char *)ret_addr.end - PAGE + 1; + + /* use one buffer for writing, the reset go in the free buffer queue */ + for(i=0; i < BUFFERS-1; i++) + { + freeBuff((char *)ret_addr.start +i*PAGE); + } + + /* avoid double MapWindow requests, for wm's that care... */ + XtSetMappedWhenManaged( screen->TekEmu ? XtParent(tekWidget) : + XtParent(term), False ); + /* Realize the Tek or VT widget, depending on which mode we're in. + If VT mode, this calls VTRealize (the widget's Realize proc) */ + XtRealizeWidget (screen->TekEmu ? XtParent(tekWidget) : + XtParent(term)); + + /* get the default device characteristics of the pseudo terminal */ + + itemlist[0].buflen = 4; + itemlist[0].code = DVI$_DEVTYPE; + itemlist[0].buffer = &type; + itemlist[0].return_addr = &tt_name_desc.dsc$w_length; + + itemlist[1].buflen = 4; + itemlist[1].code = DVI$_DEVCLASS; + itemlist[1].buffer = &class; + itemlist[1].return_addr = &tt_name_desc.dsc$w_length; + + itemlist[2].buflen = 4; + itemlist[2].code = DVI$_DEVDEPEND; + itemlist[2].buffer = &devdepend; + itemlist[2].return_addr = &tt_name_desc.dsc$w_length; + + itemlist[3].buflen = 4; + itemlist[3].code = DVI$_DEVDEPEND2; + itemlist[3].buffer = &tt_chars.extended; + itemlist[3].return_addr = &tt_name_desc.dsc$w_length; + + itemlist[4].buflen = 0; + itemlist[4].code = 0; + + + status = sys$getdviw(0,0,&device,&itemlist,&iosb,0,0,0); + if(!(status & SS$_NORMAL)) lib$signal(status); + if(!(iosb.status & SS$_NORMAL)) lib$signal(iosb.status); + + tt_chars.type = DT$_VT102; /* XTerm supports VT102 mode */ + tt_chars.class = class; + tt_chars.page_width = screen->max_col+1; + tt_chars.length = screen->max_row+1; + + /* copy the default char's along with the created window size */ + + bcopy(&devdepend, &tt_chars.characteristics, 3); + + tt_chars.extended |= TT2$M_ANSICRT | TT2$M_AVO | TT2$M_DECCRT; + + + /* create the pseudo terminal with the proper char's */ + status = ptd$create(&tt_chan,0,&tt_chars,12,0,0,0,&ret_addr); + if(!(status & SS$_NORMAL)) lib$signal(status); + + + /* get the device name of the Pseudo Terminal */ + + itemlist[0].buflen = 64; + itemlist[0].code = DVI$_DEVNAM; + itemlist[0].buffer = &tt_name; + itemlist[0].return_addr = &tt_name_desc.dsc$w_length; + + /* terminate the list */ + itemlist[1].buflen = 0; + itemlist[1].code = 0; + + status = sys$getdviw(0,tt_chan,0,&itemlist,&iosb,0,0,0); + if(!(status & SS$_NORMAL)) CloseDown(status); + if(!(iosb.status & SS$_NORMAL)) CloseDown(iosb.status); + + /* + * set up AST's for XON, XOFF, BELL and characteristics change. + */ + + status = ptd$set_event_notification(tt_chan,&send_xon,0,0,PTD$C_SEND_XON); + if(!(status & SS$_NORMAL)) CloseDown(status); + + status = ptd$set_event_notification(tt_chan,&send_xoff,0,0,PTD$C_SEND_XOFF); + if(!(status & SS$_NORMAL)) CloseDown(status); + + status = ptd$set_event_notification(tt_chan,&send_bell,0,0,PTD$C_SEND_BELL); + if(!(status & SS$_NORMAL)) CloseDown(status); + + status = ptd$set_event_notification(tt_chan,&char_change,0,0,PTD$C_CHAR_CHANGED); + if(!(status & SS$_NORMAL)) CloseDown(status); + + /* create a mailbox for the detached process to detect hangup */ + + status = sys$crembx(0,&mbx_chan,ACC$K_TERMLEN,0,255,0,0); + if(!(status & SS$_NORMAL)) CloseDown(status); + + + /* + * get the device unit number for created process completion + * status to be sent to. + */ + + itemlist[0].buflen = 4; + itemlist[0].code = DVI$_UNIT; + itemlist[0].buffer = &mbxunit; + itemlist[0].return_addr = 0; + + /* terminate the list */ + itemlist[1].buflen = 0; + itemlist[1].code = 0; + + status = sys$getdviw(0,mbx_chan,0,&itemlist,&iosb,0,0,0); + if(!(status & SS$_NORMAL)) CloseDown(status); + if(!(iosb.status & SS$_NORMAL)) CloseDown(iosb.status); + + + tt_start_read(); + + /* + * find the current process's UIC so that it can be used in the + * call to sys$creprc + */ + itemlist[0].buflen = 4; + itemlist[0].code = JPI$_UIC; + itemlist[0].buffer = &uic; + itemlist[0].return_addr = 0; + + /* terminate the list */ + itemlist[1].buflen = 0; + itemlist[1].code = 0; + + status = sys$getjpiw(0,0,0,&itemlist,0,0,0); + if(!(status & SS$_NORMAL)) CloseDown(status); + + /* Complete a descriptor for the WS (DECW$DISPLAY) device */ + + trnlnm("DECW$DISPLAY",0,ws_name); + ws_name_desc.dsc$w_length = strlen(ws_name); + + /* create the process */ + /* Set sys$error to be the WS (DECW$DISPLAY) device. LOGINOUT */ + /* has special code for DECWINDOWS that will: */ + /* 1) do a DEFINE/JOB DECW$DISPLAY 'f$trnlnm(sys$error)' */ + /* 2) then redefine SYS$ERROR to match SYS$OUTPUT! */ + /* This will propogate DECW$DISPLAY to the XTERM process!!! */ + /* Thanks go to Joel M Snyder who posted this info to INFO-VAX */ + + flags = PRC$M_INTER | PRC$M_NOPASSWORD | PRC$M_DETACH; + status = sys$creprc(&pid,&image,&tt_name_desc,&tt_name_desc, + &ws_name_desc,0,0,0,4,uic,mbxunit,flags); + if(!(status & SS$_NORMAL)) CloseDown(status); + + + /* hang a read on the mailbox waiting for completion */ + mbx_read(); + + +/* set time value and schedule a periodic wakeup (every 1/100 of a second) + * this is used to prevent the controlling process from using up all the + * CPU. The controlling process will hibernate at strategic points in + * the program when it is just waiting for input. + */ + + status = sys$bintim(&dtime,&delta); + if (!(status & SS$_NORMAL)) CloseDown(status); + + status = sys$schdwk(0,0,&delta,&delta); + if (!(status & SS$_NORMAL)) CloseDown(status); + + + /* + * This is rather funky, but it saves me from having to totally + * rewrite some parts of the code (namely in_put in module CHARPROC.C) + */ + pty = 1; + screen->respond = pty; + pty_mask = 1 << pty; + Select_mask = pty_mask; + X_mask = 1 << Xsocket; + +} + + +/* + * This routine handles completion of write with echo. It takes the + * echo buffer and puts it on the read queue. It will then be processed + * by the routine tt_read. If the echo buffer is empty, it is put back + * on the free buffer queue. + */ + +static void tt_echo_ast(TT_BUF_STRUCT *buff_addr) +{ + int status; + + if (buff_addr->length != 0) + { + status = LIB$INSQTI(buff_addr, &read_queue); + if((status != SS$_NORMAL) && (status != LIB$_ONEENTQUE)) + { + CloseDown(status); + } + } + else + { + freeBuff(buff_addr); + } +} + + +/* + * This routine writes to the pseudo terminal. If there is a free + * buffer then write with an echo buffer completing asyncronously, else + * write syncronously using the buffer reserved for writing. All errors + * are fatal, except DATAOVERUN and DATALOST,these errors can be ignored. + + CAREFUL! Whatever calls this must NOT pass more than VMS_TERM_BUFFER_SIZE + bytes at a time. This definition has been moved to VMS.H + + */ + +int tt_write(char *tt_write_buf, int size) +{ + int status; + TT_BUF_STRUCT *echoBuff; + + /* if writing stopped, return 0 until Xon */ + if(write_stopped) return (0); + + memmove(&tt_w_buff->data,tt_write_buf,size); + + echoBuff = getBuff(); + if (echoBuff != LIB$_QUEWASEMP) + { + status = PTD$WRITE (tt_chan, &tt_echo_ast, echoBuff, + &tt_w_buff->status, size, + &echoBuff->status, VMS_TERM_BUFFER_SIZE); + } + else + { + status = PTD$WRITE (tt_chan, 0, 0, &tt_w_buff->status, size, 0, 0); + } + if (status & SS$_NORMAL) + { + if ((tt_w_buff->status != SS$_NORMAL) && + (tt_w_buff->status != SS$_DATAOVERUN) && + (tt_w_buff->status != SS$_DATALOST)) + { + CloseDown(tt_w_buff->status); + } + } + else + { + CloseDown(status); + } + + return(size); +} + + +/* + * This routine is called when a read to the pseudo terminal completes. + * Put the newly read buffer onto the read queue. It will be processed + * and freed in the routine tt_read. + */ + +static void tt_read_ast(TT_BUF_STRUCT *buff_addr) +{ + int status; + + if (buff_addr->status & SS$_NORMAL) + { + status = LIB$INSQTI(buff_addr, &read_queue); + if ((status != SS$_NORMAL) && (status != LIB$_ONEENTQUE)) + { + CloseDown(status); + } + } + else + CloseDown(buff_addr->status); + + tt_start_read(); + sys$wake(0,0); + return; +} + + +/* + * If there is a free buffer on the buffer queue then Start a read from + * the pseudo terminal, otherwise set a flag, the reading will be restarted + * in the routine freeBuff when a buffer is freed. + */ + +void tt_start_read(void) +{ + int status; + static int size; + TT_BUF_STRUCT *buff_addr; + + buff_addr = getBuff(); + if (buff_addr != LIB$_QUEWASEMP) + { + if(!tt_pasting){ + status = PTD$READ (0, tt_chan, &tt_read_ast, buff_addr, + &buff_addr->status, VMS_TERM_BUFFER_SIZE); + if ((status & SS$_NORMAL) != SS$_NORMAL) + { + CloseDown(status); + } + } + } + else + { + read_stopped = TRUE; + } + return; +} + + +/* + * Get data from the pseudo terminal. Return the data from the first item + * on the read queue, and put that buffer back onto the free buffer queue. + * Return the length or zero if the read queue is empty. + * + */ + +int tt_read(char *buffer) +{ + TT_BUF_STRUCT *read_buff; + int status; + int len; + + status = LIB$REMQHI(&read_queue, &read_buff); + if(status == LIB$_QUEWASEMP){ + return(0); + } + else if (status & SS$_NORMAL) + { + len = read_buff->length; + memmove(buffer,&read_buff->data,len); + freeBuff(read_buff); + tt_new_output=TRUE; /* DRM something will be written */ + } + else + CloseDown(status); + + return(len); +} + + +/* + * if xon then it is safe to start writing again. + */ + +static void send_xon(void) +{ + write_stopped = FALSE; +} + + +/* + * If Xoff then stop writing to the pseudo terminal until you get Xon. + */ +static void send_xoff(void) +{ + write_stopped = TRUE; +} + + + +/* + * Beep the terminal to let the user know data will be lost because + * of too much data. + */ + +static void send_bell(void) +{ + Bell(); +} + +/* + * if the pseudo terminal's characteristics change, check to see if the + * page size changed. If it did, resize the widget, otherwise, ignore + * it! This routine just gets the new term dimensions and sets a flag + * to indicate the term chars have changed. The widget gets resized in + * the routine in_put in the module CHARPROC.C. You cant resize the + * widget in this routine because this is an AST and X is not reenterent. + */ + +static void char_change(void) +{ + int status; + + /* + * Dont do anything if in Tek mode + */ + + if(!(term->screen.TekEmu)) + { + status = sys$qiow(0,tt_chan,IO$_SENSEMODE,0,0,0,&tt_mode,8,0,0,0,0); + if(!(status & SS$_NORMAL)) CloseDown(status); + + if((term->screen.max_row != tt_mode.length) || + (term->screen.max_col != tt_mode.page_width)) + { + tt_length = tt_mode.length; + tt_width = tt_mode.page_width; + + tt_changed = TRUE; + + } + } +} + + +/* + * Put a free buffer back onto the buffer queue. If reading was + * stopped for lack of free buffers, start reading again. + */ + +static void freeBuff (TT_BUF_STRUCT *buff_addr) +{ + int ast_stat; + int status; + + ast_stat = SYS$SETAST(0); + if (!read_stopped) + { + LIB$INSQHI(buff_addr, &buffer_queue); + } + else + { + status = PTD$READ (0, tt_chan, &tt_read_ast, buff_addr, + &buff_addr->status, VMS_TERM_BUFFER_SIZE); + if (status & SS$_NORMAL) + { + read_stopped = FALSE; + } + else + { + CloseDown(status); + } + } + if (ast_stat == SS$_WASSET) ast_stat = SYS$SETAST(1); +} + + +/* + * return a free buffer from the buffer queue. + */ + +TT_BUF_STRUCT *getBuff(void) +{ + int status; + TT_BUF_STRUCT *buff_addr; + + status = LIB$REMQHI(&buffer_queue, &buff_addr); + if (status & SS$_NORMAL) + { + return(buff_addr); + } + else + { + return(status); + } +} + + +/* + * Close down and exit. Kill the detached process (if it still + * exists), deassign mailbox channell (if assigned), cancel any + * waiting IO to the pseudo terminal and delete it, exit with any + * status information. + */ + +static void CloseDown(int exit_status) +{ + int status; + + /* if process has not terminated, do so now! */ + if(pid != 0) + { + status = sys$forcex(&pid,0,0); + if(!(status & SS$_NORMAL)) lib$signal(status); + } + + /* if mbx_chan is assigned, deassign it */ + if(mbx_chan != 0) + { + sys$dassgn(mbx_chan); + } + + /* cancel pseudo terminal IO requests */ + status = ptd$cancel(tt_chan); + if(!(status & SS$_NORMAL)) lib$signal(status); + + /* delete pseudo terminal */ + status = ptd$delete(tt_chan); + if(!(status & SS$_NORMAL)) lib$signal(status); + + if(!(exit_status & SS$_NORMAL)) lib$signal(exit_status); + + exit(1); + +} + + +/* + * This routine gets called when the detached process terminates (for + * whatever reason). The mailbox buffer has final exit status. Close + * down and exit. + */ + +static void mbx_read_ast(void) +{ + int status; + + pid = 0; + + status = mbx_read_iosb.status; + if (!(status & SS$_NORMAL)) CloseDown(status); + + status = (unsigned long int) mbx_buf.acc$l_finalsts; + if (!(status & SS$_NORMAL)) CloseDown(status); + + CloseDown(1); + +} + + +/* + * This routine starts a read on the mailbox associated with the detached + * process. The AST routine gets called when the detached process terminates. + */ + +static void mbx_read(void) +{ +int status; +static int size; + + size = ACC$K_TERMLEN; + status = sys$qio(0,mbx_chan, + IO$_READVBLK, + &mbx_read_iosb, + &mbx_read_ast, + 0, + &mbx_buf, + size,0,0,0,0); + + if (!(status & SS$_NORMAL)) CloseDown(status); + + return; +} |