[comp.os.vms] new reply.c source code

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 */
}