ERIC@UOFT02.BITNET (07/07/88)
/***********************************************************************/
/* This program will log any messages sent to you while you are out, */
/* and then send a reply message to the user. The reply can be passed */
/* to the program, or a default reply can be used. See REPLY.DOC for */
/* more information on how to use or install this program. */
/* */
/* Program by Eric Rostetter, University of Toledo, Toledo, Ohio 43606 */
/* */
/* This program will correctly handle the following types of messages: */
/* */
/* SEND messages from the current node (log and reply) */
/* SEND messages from another node (log and reply) */
/* MAIL messages (log, no reply) */
/* BATCH messages (log, no reply) */
/* ^T messages (log, no reply) */
/* */
/***********************************************************************/
/***********************************************************************/
/* Modification History: */
/* 21-JAN-1986 Finished initial coding of program version 1.0 */
/* 21-APR-1986 Update for the new version of JNET (user@node) */
/* 18-JUN-1986 Fix GET_NAME bug, make it ignore csnews@maine */
/* 16-SEP-1986 Added code to save and restore terminal stuffs */
/* 19-SEP-1986 Added code to allow the user to supply message */
/* 23-SEP-1986 Added code to display time and date on screen */
/* 9-OCT-1986 Removed SMG routines and added mailbox routine */
/* 14-OCT-1986 Cleaned up the screen I/O and what have you... */
/* 20-OCT-1986 Add code to stop the infinate loop problems... */
/* 2-DEC-1986 Fixed reply_to_msg so screen is always updated */
/* 17-DEC-1986 Fixed 'logout' bug caused by network/blank msg */
/* 11-FEB-1987 Kludged bug in UPDATE_TIME, added ^C/^Y traps */
/* 31-MAR-1987 Added logical name translation, fixed isvalid */
/* 9-APR-1987 Removed reference to LNMDEF.H for compatibilty */
/* 13-APR-1987 Added third counter, reset counters code, etc. */
/* 14-APR-1987 Cleaned up the code, made slight modifications */
/* 19-AUG-1987 Changed logicals for JNET version 3.2 supports */
/* 14-DEC-1987 Add startup message to let one know it's going */
/* 8-FEB-1988 Changed editor command to use callable editors */
/* 9-FEB-1988 Added '$' to list of good node/name characters */
/* 29-JUN-1988 Cleaned up code some, added reply for files... */
/***********************************************************************/
/***********************************************************************/
/* Before we start anything, lets have a word from our sponsors... */
/***********************************************************************/
#include <stdio>
#include <descrip>
#include <ctype>
#include <iodef>
#include <ttdef>
#include <tt2def>
#include <ssdef>
#define then
#define false 0
#define true 1
#define esc 27
#define LNM$_STRING 2
#define LNM$M_CASE_BLIND 33554432
/***********************************************************************/
/* First define all the needed global data structures for the programs */
/***********************************************************************/
FILE *fp; /* output file id for messages. */
int status; /* return status from functions */
int count1,count2,count0; /* number of messages that came */
int tt_chan,mbx_chan,msg_chan,pid; /* kb and mbx luns, process ids */
int old_term_char,old_extended; /* old terminal characteristics */
int term_type; /* terminal type,hardcopy/scope */
char send_msg[128]; /* the message we send to them! */
char name[16]; /* name of the sender, if any. */
char cmd[2]; /* command typed in at terminal */
char *node,*rscs; /* local and network node names */
char *tranlog( char *s, int flag); /* tranlate logicals subroutine */
char reply_string[] = "Out of office - message logged; Will reply ASAP. ";
char file_string[] =
"send foobart::foobarer \"Received file xxxxxxxx.xxxxxxxx - Thank you!";
struct { /* the date and time of message */
char weekday[10]; /* --> the day of the week here */
char filler[1]; /* --> always contains a space! */
char date[12]; /* --> the date in ascii please */
char time[08]; /* --> the time in ascii please */
} header;
struct { /* hold info about last message */
char name[16]; /* holds the last name and node */
int count; /* number of message sent to it */
} last; /* this will stop infinate loop */
struct {unsigned class :8; /* --> terminal class goes here */
unsigned type :8; /* --> the terminal type please */
unsigned page_width :16; /* --> terminal width goes here */
unsigned term_char :24; /* --> terminal characteristics */
unsigned page_length :8; /* --> page length goes in here */
unsigned extended :32; /* --> extended characteristics */
} charbuf; /* terminal characteristics buf */
struct {
char buffer[20]; /* the mailbox message info blk */
char length; /* the current message's length */
char filler; /* for some reason this is null */
char message[100]; /* the messages sent to mailbox */
} mbx;
struct dsc { long int len; /* descriptors needed sometimes */
char *addr; /* using only a len and address */
}; /* instead of the whole descrip */
union pointer { /* defines pointer type used in */
int *intbuf; /* in item lists of certain sys */
char *charbuf; /* routines and stuff like that */
};
struct itmlst {
unsigned short int bufferlength;
unsigned short int item_code;
union pointer addr;
int *retlength;
}; /* used in some system services */
struct itmlst trnlist[2]; /* Used in translating logicals */
$DESCRIPTOR(term, "SYS$OUTPUT"); /* device for in assign service */
$DESCRIPTOR(mbx_name, "REPLY_MBX"); /* mailbox to send messages out */
char *local_node = "SYS$NODE"; /* local node name (for DECNET) */
char *rscs_node = "SYS$RSCS_NODE"; /* network node name (for JNET) */
char *jan_node = "JAN_LOCAL_NODE"; /* new network name (JNET V3.2) */
struct dsc$descriptor_s header_dsc =
{ 20, DSC$K_DTYPE_T, DSC$K_CLASS_S, header.date };
/***********************************************************************/
/* Set up an AST handler for any trapped messages that come our way... */
/***********************************************************************/
get_msg()
{
get_time(); /* gets a time and date */
mbx.message[mbx.length] = 0; /* null terminates msgs */
if (mbx.length != 0) then { /* only continue if msg */
fprintf(fp,"%s\n", header.weekday); /* print time log notes */
fprintf(fp,"%s\n", mbx.message); /* puts message to file */
get_name(); /* gets a name and node */
if ((!strncmp(name,"Job ",4)) || /* if its batch message */
(!strncmp(name,"New ",4)) || /* or a mail message... */
(!strncmp(name,"CSNEWS",6)) || /* ignores csnews@maine */
(!strncmp(name," ",6)) || /* network msg, no name */
(!strcmp(name,rscs)) || /* or a transfer msg... */
(!strncmp(name,node,strlen(node)))) /* or ^T messages... */
then {
++count2; /* say we got a sys msg */
reply_to_file(); /* do received messages */
}
else {
++count1; /* must be user message */
reply_to_msg(); /* send a reply message */
}
}
status=sys$qio(3,msg_chan,IO$_READVBLK /* - do read on mailbox */
,0,get_msg,0,&mbx,120,0,0,0,0); /* - with an AST finish */
}
/***********************************************************************/
/* The main control loop... Set up message, initialize stuff, and wait */
/***********************************************************************/
main(argc, argv)
int argc;
char **argv;
{
/* First, get the reply message to send and set up the command line... */
if (argc > 2) then
{ printf("FORMAT: REPLY\n or REPLY \"REPLY STRING\"\n" ); exit(); }
if (argc == 2) then strcpy(reply_string, *++argv);
strcpy(send_msg, "send foobart@foobarer \"");
strcpy(send_msg+24, reply_string); /* the messages to send */
initialize(); /* open file, do screen */
while (status==true) { /* while no errors here */
status = sys$qiow(2,tt_chan,97,0,0,0,&cmd,1,0,0,0,0);
switch (toupper(cmd[0])) { /* input a character... */
case 'Q': leave_reply();/* quits if it is a 'Q' */
case 'E': call_editor();/* calls editors on 'E' */
} /* otherwise ignores it */
}
lib$signal(status); /* report the error msg */
}
/***********************************************************************/
/* Close files, clear screen, tie up loose ends, then exit this mess! */
/***********************************************************************/
leave_reply()
{
uninitialize(); /* kill everything here */
exit(); /* bye and good ridence */
}
/***********************************************************************/
/* Close up our log file and exit all the screen manager stuff please. */
/***********************************************************************/
uninitialize()
{
sys$delprc(&pid,0); /* kills the subprocess */
sys$delmbx(mbx_chan); /* kill its mailbox too */
sys$delmbx(msg_chan); /* zap terminal mailbox */
sys$cantim(0,3); /* cancels update timer */
reset_terminal(); /* resets user terminal */
sys$dassgn(tt_chan); /* deassign the channel */
}
/***********************************************************************/
/* Reset the terminal characteristics, kill ast traps, kill timer, etc */
/***********************************************************************/
reset_terminal()
{
sys$cancel(tt_chan); /* cancels terminal I/O */
charbuf.term_char = old_term_char; /* restore the old term */
charbuf.extended = old_extended; /* characteristics back */
sys$qiow(0,tt_chan,IO$_SETMODE,0,0,0, /* so they can get msgs */
&charbuf,12,0,0,0,0); /* again to their term! */
fclose(fp); /* closes the log files */
if (term_type) then /* if we're using a CRT */
printf("%c[2J%c[H",esc,esc); /* then clear screen... */
sys$setast(0); /* disable the ast call */
}
/***********************************************************************/
/* Sets up the display, open log file, sets up the mailbox stuff, etc. */
/***********************************************************************/
initialize()
{
int len;
printf("Vax Automatic Message Reply Facility started - Please wait.\n");
open_file(); /* open the file again */
/* Find out our local and rscs node names from translating logicals now */
node = tranlog(local_node,0); /* translate a logical */
len = strlen(node)-2; /* skip the '::' of it */
if (len <= 0) then { /* make sure it exists */
printf("?Logical name SYS$NODE must be defined to use REPLY");
exit(); /* if not then exit... */
}
*(node+len) = 0; /* skip over '::' in it */
rscs = tranlog(rscs_node,0); /* try translating this */
if (*rscs == 0) then /* if none then try new */
rscs = tranlog(jan_node,0); /* jnet version logical */
if (*rscs == 0) then strcpy(rscs,node); /* none? uses other one */
/* Create mailbox thru which we send replys, create subprocess to do it */
status = sys$crembx(0,&mbx_chan,0,0,240,0,&mbx_name);
if (status != true) then lib$signal(status);
status = lib$spawn(0,&mbx_name,0,&1,0,&pid);
if (status != true) then lib$signal(status);
status = lib$asn_wth_mbx(&term,&0,&0,&tt_chan,&msg_chan);
if (status != true) then lib$signal(status);
/* Set up the terminal so that everything works and draw a neat display */
setup_term();
}
/***********************************************************************/
/* Open the message log file in append mode, if an error, then exit... */
/***********************************************************************/
open_file()
{
char junk[128];
count0 = 0; count1 = 0; count2 = 0; /* assumes no messages */
fp=fopen("sys$login:messages.log","r"); /* try opening my file */
if (fp != NULL) then { /* if it exists, then */
count0--; /* (otherwise one off) */
do { fgets(junk,128,fp); /* gets line from file */
count0++; /* increments counters */
} /* repeat loop til eof */
while (feof(fp) != 1); /* count0 := msg's * 2 */
count0 = count0 / 2; /* gets the real count */
fclose(fp); /* close file up again */
}
fp=fopen("sys$login:messages.log","a+");/* open the file again */
if (fp == NULL) then { /* check for errors... */
printf("?Error opening file SYS$LOGIN:MESSAGES.LOG");
exit(); /* exit if an error... */
}
}
/***********************************************************************/
/* Set up the terminal display, and save/set terminal characteristics */
/***********************************************************************/
setup_term()
{
int mask[1];
term_type = (!strncmp(getenv("TERM"),"vt1",3) ||
!strncmp(getenv("TERM"),"vt2",3));
status = sys$qiow(0,tt_chan,IO$_SENSEMODE,0,0,0,&charbuf,12,0,0,0,0);
if (status != true) then lib$signal(status);
old_term_char = charbuf.term_char; /* saves chars for exit */
old_extended = charbuf.extended; /* saves extendeds too! */
charbuf.term_char = charbuf.term_char /* SET TERM/NOBROADCAST */
|TT$M_MBXDSABL|TT$M_NOBRDCST; /* (also sets MBXDSABL) */
charbuf.extended = charbuf.extended /* SET TERM/BRDCSTMBX */
|TT2$M_BRDCSTMBX; /* so we receive in mbx */
status = sys$qiow(0,tt_chan,IO$_SETMODE,0,0,0,&charbuf,12,0,0,0,0);
if (status != true) then lib$signal(status);
/* Sets up an out of band ast trap for any control C or control Y chars */
mask[0] = 0; mask[1] = 33554440; /* mask for ^C/^Y chars */
status = sys$qiow(4,tt_chan, /* sets up the ast trap */
IO$_SETMODE|IO$M_OUTBAND, /* for ^C/^Y characters */
0,0,0,leave_reply,mask,0,0,0,0);/* so as to exit nicely */
if (status != true) lib$signal(status); /* an error setting it? */
/* Fix a gap in the time/date/day header so that we have a neat display */
strcpy(header.filler," "); /* make display neat... */
/* Set up screen display -- works on hardcopy or on ANSI CRT terminals */
if (term_type) then printf("%c[2J%c[H",esc,esc);
printf(" Vax Automatic Message Reply Facility\n\n");
printf(" E to EDIT message file Q to QUIT this program");
printf("\n");
printf(" Enter Selection: ");
printf("\n");
printf(" --------------------------------------------------------------");
printf("\n");
reset_input();
/* Queue an ast routine to read from the terminal's message mailbox... */
sys$setast(1); /* enable ast delivery */
status=sys$qio(3,msg_chan,IO$_READVBLK,0,get_msg,0,&mbx,120,0,0,0,0);
/* This loads status, which is tested in the WHILE statement of main() */
}
/***********************************************************************/
/* get the time and date so we can log it to a file or screen display. */
/***********************************************************************/
get_time()
{
int i; /* counter variables... */
unsigned int j[4]; /* holds internal times */
static char day_list[] =
"Monday, Tuesday, Wednesday,Thursday, Friday, Saturday, Sunday, ";
lib$date_time(&header_dsc); /* get the current time */
sys$gettim(&j[0]); /* and an internal time */
lib$day_of_week(&j[0], &i); /* gets day of the week */
i = (i - 1) * 10; /* calculate offset now */
strncpy(header.weekday,&day_list[i],10);/* put in output buffer */
}
/***********************************************************************/
/* reset the cursor to command line, used after message or bad command */
/***********************************************************************/
reset_input()
{
if (term_type) then {
printf("%c[7;7H%d User messages received",esc,count1);
printf("\t\t %d System messages received",count2);
printf("%c[8;26H%d Old messages in file",esc,count0);
update_time();
}
}
/***********************************************************************/
/* Display the time and the date on screen every few seconds please... */
/***********************************************************************/
update_time()
{
int time[4];
$DESCRIPTOR(delta, "0 ::30");
get_time(); header.time[5] = 0; /* get time, terminate */
printf("%c[10;23H%s%c[4;46H", esc,header.weekday,esc);
sys$cantim(0,3); /* kill time interupts */
sys$bintim(&delta, &time); /* gets new time delay */
sys$setimr(0,&time,update_time,0); /* sets interupt timer */
}
/***********************************************************************/
/* Send the reply message to the subprocess to send it to the senders. */
/***********************************************************************/
reply_to_msg()
{
int i;
if (!strcmp(name,last.name)) then {
strcpy(name, last.name); last.count = 0;
}
last.count++;
if (last.count < 5) then {
for (i=0;i<16;i++) send_msg[i+5] = name[i];
status=sys$qiow(0,mbx_chan,IO$_WRITEVBLK,0,0,0,
&send_msg,strlen(send_msg),0,0,0,0);
if (status != true) then lib$signal(status);
}
else fprintf(fp,"?No reply sent...\n");
reset_input();
}
/***********************************************************************/
/* Send file received message to subprocess to send it back to sender. */
/***********************************************************************/
reply_to_file()
{
int i;
for (i=0;mbx.message[i] != '-'; i++);
status = sscanf(&mbx.message[i],
"- Received network file %s from %s",
file_string+38, file_string+5 );
for (i= 5+strlen(file_string+5); i<22; i++) file_string[i] = ' ';
for (i=38+strlen(file_string+38); i<55; i++) file_string[i] = ' ';
if (status == 2) then {
status=sys$qiow(0,mbx_chan,IO$_WRITEVBLK,0,0,0,
&file_string,strlen(file_string),0,0,0,0);
if (status != true) then lib$signal(status);
}
reset_input();
}
/***********************************************************************/
/* Call a callable editor to edit the message file --- uses EDIT$INIT */
/***********************************************************************/
call_editor()
{
char *junk;
char *editor = "REPLY$EDIT";
$DESCRIPTOR(tpu_dsc,"TPU SYS$LOGIN:MESSAGES.LOG");
$DESCRIPTOR(edt_dsc,"SYS$LOGIN:MESSAGES.LOG");
reset_terminal(); /* reset terminal now */
junk = tranlog(editor,1); /* try translating it */
if (!strcmp(junk,"TPU")) then /* if it equals "tpu" */
status = tpu$tpu(&tpu_dsc); /* then call "tpu" */
else /* otherwise, we just */
status = edt$edit(&edt_dsc); /* call this "edt" */
if (status != true) lib$signal(status); /* exit if any errors */
open_file(); /* open the file again */
setup_term(); /* sets terminal again */
}
/***********************************************************************/
/* get the users name and node if needed for the reply message to him. */
/***********************************************************************/
get_name()
{
int i,j;
char node[16];
for (i=0;i<16;i++) { /* before we start, we */
node[i]=32; /* move spaces to node */
name[i]=32; /* move spaces to name */
}
i=0;j=0; /* init our pointers... */
if (mbx.message[i]==7) then i=1; /* skip bell if present */
if (mbx.message[i]==40) then { /* if a node name, then */
node[j++] = 64; /* -- put in at sign... */
for (i++;mbx.message[i]!=41;i++) /* -- get the node name */
node[j++] = mbx.message[i];
node[j++]=32; i++; /* -- and add the space */
}
j=0;
for (;isvalid(mbx.message[i]);i++)
name[j++] = mbx.message[i]; /* copy the name please */
if (j != 0) then /* iff a name was found */
for (i=0;j<16;i++) name[j++]=node[i]; /* - copy the node name */
}
/***********************************************************************/
/* Sees if a character is valid for use in a user name or a node name. */
/***********************************************************************/
isvalid(ch)
char ch;
{
return( isalnum(ch) || ch == '$' || ch == '_' );
}
/***********************************************************************/
/* Translate a logical name to the real physical name, if there is one */
/***********************************************************************/
char *tranlog(s,flag)
char *s;
int flag;
{
$DESCRIPTOR(tab0desc,"LNM$SYSTEM_TABLE");
$DESCRIPTOR(tab1desc,"LNM$PROCESS_TABLE");
struct dsc name;
char *cp;
int loglen,status;
int attr_arg;
cp = malloc( 256 ); /* go get us some memory */
name.addr = s; /* address for string... */
name.len = strlen(s); /* length of the strings */
attr_arg = LNM$M_CASE_BLIND; /* don't care about case */
trnlist[0].bufferlength = 100; /* set up an itemlist... */
trnlist[0].item_code = LNM$_STRING; /* say it is a string... */
trnlist[0].addr.charbuf = cp; /* buffer for the result */
trnlist[0].retlength = &loglen; /* the length for result */
trnlist[1].bufferlength = 0;
trnlist[1].item_code = 0;
if (flag) then
status = sys$trnlnm(&attr_arg,&tab1desc,&name,0,&trnlist);
else
status = sys$trnlnm(&attr_arg,&tab0desc,&name,0,&trnlist);
if ( status == SS$_NOLOGNAM || ( status & 1 ) == 0) then loglen = 0;
*(cp + (loglen&255)) = 0; /* null terminate result */
return(cp); /* and return the result */
}