]> granicus.if.org Git - nethack/commitdiff
*** empty log message ***
authorjwalz <jwalz>
Sat, 5 Jan 2002 21:06:00 +0000 (21:06 +0000)
committerjwalz <jwalz>
Sat, 5 Jan 2002 21:06:00 +0000 (21:06 +0000)
sys/vms/vmsmail.c [new file with mode: 0644]

diff --git a/sys/vms/vmsmail.c b/sys/vms/vmsmail.c
new file mode 100644 (file)
index 0000000..9c9ef60
--- /dev/null
@@ -0,0 +1,486 @@
+/*     SCCS Id: @(#)vmsmail.c  3.3     1995/06/01      */
+/* Copyright (c) Robert Patrick Rankin, 1991.                    */
+/* NetHack may be freely redistributed.  See license for details. */
+
+#include "config.h"
+#include "mail.h"
+
+/* lint supression due to lack of extern.h */
+unsigned long NDECL(init_broadcast_trapping);
+unsigned long NDECL(enable_broadcast_trapping);
+unsigned long NDECL(disable_broadcast_trapping);
+struct mail_info *NDECL(parse_next_broadcast);
+
+#ifdef MAIL
+#include "wintype.h"
+#include "winprocs.h"
+#include <ctype.h>
+#include <descrip.h>
+#include <errno.h>
+# ifndef __GNUC__
+#include <msgdef.h>
+# else
+#  define MSG$_TRMHANGUP  6
+#  define MSG$_TRMBRDCST 83
+# endif /*__GNUC__*/
+#include <signal.h>
+/* #include <string.h> */
+# define vms_ok(sts) ((sts)&1)
+
+static struct mail_info *FDECL(parse_brdcst, (char *));
+static void FDECL(filter_brdcst, (char *));
+static void NDECL(flush_broadcasts);
+static void FDECL(broadcast_ast, (int));
+extern char *FDECL(eos, (char *));
+extern char *FDECL(strstri, (const char *,const char *));
+extern int FDECL(strncmpi, (const char *,const char *,int));
+
+extern size_t FDECL(strspn, (const char *,const char *));
+#ifndef __DECC
+extern int VDECL(sscanf, (const char *,const char *,...));
+#endif
+extern unsigned long
+       smg$create_pasteboard(),
+       smg$get_broadcast_message(),
+       smg$set_broadcast_trapping(),
+       smg$disable_broadcast_trapping();
+
+extern volatile int broadcasts;                /* defining declaration in mail.c */
+
+static long pasteboard_id = 0;         /* SMG's magic cookie */
+
+/*
+ * Mail (et al) overview:
+ *
+ *     When a broadcast is asynchronously captured, a volatile counter
+ * ('broadcasts') is incremented.  Each player turn, ckmailstatus() polls
+ * the counter and calls parse_next_broadcast() if it's positive; this
+ * returns some display text, object name, and response command, which is
+ * passed to newmail().  Routine newmail() generates a mail-daemon monster
+ * who approaches the character, "speaks" the display text, and delivers
+ * a scroll of mail pre-named to the object name; the response command is
+ * initially appended to the name, so that the object is tagged with both
+ * of them; a NUL is inserted to terminate the ordinary name and hide the
+ * command.  (If the player renames such a scroll, the hidden command will
+ * be lost; who cares?)  Unrecognized broadcasts result in the mail-daemon
+ * arriving and announcing the display text, but no scroll being created.
+ * If SHELL is undefined, then all broadcasts are treated as 'other'; since
+ * no subproceses are allowed, there'd be no way to respond to the scroll.
+ *
+ *     When a scroll of mail is read by the character, readmail() extracts
+ * the hidden command string and uses it for the default when prompting the
+ * player for a system command to spawn.  The player may enter any command
+ * he or she chooses, or just <return> to accept the default or <escape> to
+ * avoid executing any command.  If the command is "SPAWN", a regular shell
+ * escape to DCL is performed; otherwise, the indicated single command is
+ * spawned.  Either way, NetHack resumes play when the subprocess terminates
+ * or explicitly reattaches to its parent.
+ *
+ * Broadcast parsing:
+ *
+ *     The following broadcast messages are [attempted to be] recognized:
+ *    text fragment          name for scroll         default command
+ *     New mail                VMSmail                 MAIL
+ *     New ALL-IN-1 MAIL       A1mail                  A1M
+ *     Software Tools mail     STmail                  MSG [+folder]
+ *     MM mail                 MMmail                  MM
+ *     WPmail: New mail        WPmail                  OFFICE/MAIL
+ *     **M400 mail             M400mail                M400
+ *     " mail", ^"mail "       unknown mail            SPAWN
+ *     " phoning"              Phone call              PHONE ANSWER
+ *     talk-daemon...by...foo  Talk request            TALK[/OLD] foo@bar
+ *     (node)user -            Bitnet noise            XYZZY user@node
+ * Anything else results in just the message text being passed along, no
+ * scroll of mail so consequently no command to execute when scroll read.
+ * The user can set up ``$ XYZZY :== SEND'' prior to invoking NetHack if
+ * vanilla JNET responses to Bitnet messages are prefered.
+ *
+ *     Static return buffers are used because only one broadcast gets
+ * processed at a time, and the essential information in each one is
+ * either displayed and discarded or copied into a scroll-of-mail object.
+ *
+ *     The test driver code below can be used to check out potential new
+ * entries without rebuilding NetHack itself.  CC/DEFINE="TEST_DRIVER"
+ * Link it with hacklib.obj or nethack.olb/incl=hacklib (not nethack/lib).
+ */
+
+static struct mail_info msg;   /* parse_*()'s return buffer */
+static char nam_cmd_buf[63],   /* maximum onamelth, size of ONAME(object) */
+           txt_buf[255+1];     /* same size as used for message buf[] */
+
+/* try to decipher and categorize broadcast message text
+*/
+static struct mail_info *
+parse_brdcst(buf)              /* called by parse_next_broadcast() */
+char *buf;                     /* input: filtered broadcast text */
+{
+    int typ;
+    char *txt;
+    const char *nam, *cmd;
+# ifdef SHELL          /* only parse if spawned commands are enabled */
+    register char *p, *q;
+    boolean is_jnet_send;
+    char cmd_buf[127+1], user[127+1], node[127+1], sentinel;
+
+    /* Check these first; otherwise, their arbitrary text would enable
+       easy spoofing of some other message patterns.  Unfortunately,
+       any home-grown broadcast delivery program poses a similar risk. */
+    if (!strncmpi(buf, "reply received", 14)) goto other;
+    is_jnet_send = (sscanf(buf, "(%[^)])%s -%c", node, user, &sentinel) == 3);
+    if (is_jnet_send) goto jnet_send;
+
+    /* scan the text more or less by brute force */
+    if ((q = strstri(buf, " mail")) != 0 ||    /* all known mail broadcasts */
+       !strncmpi(q = buf, "mail ", 5)) {       /* unexpected alternative */
+       typ = MSG_MAIL;
+       p = strstri(q, " from");
+       txt = p ? strcat(strcpy(txt_buf, "Mail for you"), p) : (char *) 0;
+
+       if (!strncmpi(buf, "new mail", 8)) {
+/*
+New mail [on node FOO] from [SPAM::]BAR [\"personal_name\"] [\(HH:MM:SS\)]
+*/
+           nam = "VMSmail";            /* assume VMSmail */
+           cmd = "MAIL";
+           if (txt && (p = strrchr(txt, '(')) > txt && /* discard time */
+               (--p, strspn(p, "0123456789( :.)") == strlen(p))) *p = '\0';
+       } else if (!strncmpi(buf, "new all-in-1", 12)) {
+           int i;
+/*
+New ALL-IN-1 MAIL message [on node FOO] from Personal Name \(BAR@SPAM\) [\(DD-MMM-YYYY HH:MM:SS\)]
+*/
+           nam = "A1mail";
+           cmd = "A1M";
+           if (txt && (p = strrchr(txt, '(')) > txt && /* discard date+time */
+               sscanf(p-1," (%*d-%*[^-]-%*d %*d:%*d:%d) %c",&i,&sentinel) == 1)
+               *--p = '\0';
+       } else if (!strncmpi(buf, "software tools", 14)) {
+/*
+Software Tools mail has arrived on FOO from \'BAR\' [in SPAM]
+*/
+           nam = "STmail";
+           cmd = "MSG";
+           if (txt && (p = strstri(p, " in ")) != 0)   /* specific folder */
+               cmd = strcat(strcpy(cmd_buf, "MSG +"), p + 4);
+       } else if (q - 2 >= buf && !strncmpi(q - 2, "mm", 2)) {
+/*
+{MultiNet\ |PMDF\/}MM mail has arrived on FOO from BAR\n
+[Subject: subject_text] (PMDF only)
+*/
+           nam = "MMmail";             /* MultiNet's version of MM */
+           cmd = "MM";                 /*{ perhaps "MM READ"? }*/
+       } else if (!strncmpi(buf, "wpmail:", 7)) {
+/*
+WPmail: New mail from BAR.  subject_text
+*/
+           nam = "WPmail";             /* WordPerfect [sic] Office */
+           cmd = "OFFICE/MAIL";
+       } else if (!strncmpi(buf, "**m400 mail", 7)) {
+/*
+**M400 mail waiting**
+*/
+           nam = "M400mail";           /* Messenger 400 [not seen] */
+           cmd = "M400";
+       } else {
+           /* not recognized, but presumed to be mail */
+           nam = "unknown mail";
+           cmd = "SPAWN";              /* generic escape back to DCL */
+           txt = (char *) 0;           /* don't rely on "from" info here */
+       }
+
+       if (!txt) txt = strcat(strcpy(txt_buf, "Mail for you: "), buf);
+    /*
+     : end of mail recognition; now check for call-type interruptions...
+     */
+    } else if ((q = strstri(buf, " phoning")) != 0) {
+/*
+BAR is phoning you [on FOO] \(HH:MM:SS\)
+*/
+       typ = MSG_CALL;
+       nam = "Phone call";
+       cmd = "PHONE ANSWER";
+       if (!strncmpi(q + 8, " you", 4)) q += (8 + 4), *q = '\0';
+       txt = strcat(strcpy(txt_buf, "Do you hear ringing?  "), buf);
+    } else if ((q = strstri(buf, " talk-daemon")) != 0 ||
+              (q = strstri(buf, " talk_daemon")) != 0) {
+/*
+Message from TALK-DAEMON@FOO at HH:MM:SS\n
+Connection request by BAR@SPAM\n
+\[Respond with: TALK[/OLD] BAR@SPAM\]
+*/
+       typ = MSG_CALL;
+       nam = "Talk request";           /* MultiNet's TALK and/or TALK/OLD */
+       cmd = "TALK";
+       if ((p = strstri(q, " by ")) != 0) {
+           txt = strcat(strcpy(txt_buf, "Talk request from"), p + 3);
+           if ((p = strstri(p, "respond with")) != 0) {
+               if (*(p-1) == '[') *(p-1) = '\0'; else *p = '\0'; /* terminate */
+               p += (sizeof "respond with" - sizeof "");
+               if (*p == ':') p++;
+               if (*p == ' ') p++;
+               cmd = strcpy(cmd_buf, p);       /* "TALK[/OLD] bar@spam" */
+               p = eos(cmd_buf);
+               if (*--p == ']') *p = '\0';
+           }
+       } else
+           txt = strcat(strcpy(txt_buf, "Pardon the interruption: "), buf);
+    } else if (is_jnet_send) { /* sscanf(,"(%[^)])%s -%c",,,)==3 */
+jnet_send:
+/*
+\(SPAM\)BAR - arbitrary_message_text (from BAR@SPAM)
+*/
+       typ = MSG_CALL;
+       nam = "Bitnet noise";           /* RSCS/NJE message received via JNET */
+       Sprintf(cmd_buf, "XYZZY %s@%s", user, node);
+       cmd = cmd_buf;
+       /*{ perhaps just vanilla SEND instead of XYZZY? }*/
+       Sprintf(txt_buf, "Message from %s@%s:%s", user, node,
+               &buf[1+strlen(node)+1+strlen(user)+2-1]);  /* "(node)user -" */
+       txt = txt_buf;
+    /*
+     : end of call recognition; anything else is none-of-the-above...
+     */
+    } else {
+other:
+# endif /* SHELL */
+/* arbitrary broadcast: batch job completed, system shutdown imminent, &c */
+       typ = MSG_OTHER;
+       nam = (char *) 0; /*"captured broadcast message"*/
+       cmd = (char *) 0;
+       txt = strcat(strcpy(txt_buf, "Message for you: "), buf);
+# ifdef SHELL
+    }
+    /* Daemon in newmail() will append period when the text is displayed */
+    if ((p = eos(txt)) > txt && *--p == '.') *p = '\0';
+
+    /* newmail() and readmail() assume that nam and cmd are concatenated */
+    if (nam) {         /* object name to attach to scroll of mail */
+       char *join = strcpy(nam_cmd_buf, nam);
+       if (cmd) {      /* append command to name; readmail() requires it */
+           int len = sizeof nam_cmd_buf - sizeof "" - (strlen(join) + 1);
+           cmd_buf[len] = '\0';        /* possibly truncate */
+           (void) strcat(join, " ");
+           cmd = strcpy(eos(join), cmd);
+       }
+       nam = join;
+    }
+# endif /* SHELL */
+    /* truncate really long messages to prevent verbalize() from blowing up */
+    if (txt && strlen(txt) > BUFSZ - 50) txt[BUFSZ - 50] = '\0';
+
+    msg.message_typ  = typ;    /* simple index */
+    msg.display_txt  = txt;    /* text for daemon to pline() */
+    msg.object_nam   = nam;    /* 'name' for mail scroll */
+    msg.response_cmd = cmd;    /* command to spawn when scroll read */
+    return &msg;
+}
+
+/* filter out non-printable characters and redundant noise
+*/
+static void
+filter_brdcst(buf)             /* called by parse_next_broadcast() */
+register char *buf;            /* in: original text; out: filtered text */
+{
+    register char c, *p, *buf_p;
+
+    /* filter the text; restrict consecutive spaces or dots to just two */
+    for (p = buf_p = buf; *buf_p; buf_p++) {
+       c = *buf_p & '\177';
+       if (c == ' ' || c == '\t' || c == '\n')
+           if (p == buf ||             /* ignore leading whitespace */
+               (p >= buf+2 && *(p-1) == ' ' && *(p-2) == ' ')) continue;
+           else c = ' ';
+       else if (c == '.' || c < ' ' || c == '\177')
+           if (p == buf ||             /* skip leading beeps & such */
+               (p >= buf+2 && *(p-1) == '.' && *(p-2) == '.')) continue;
+           else c = '.';
+       else if (c == '%' &&            /* trim %%% OPCOM verbosity %%% */
+               p >= buf+2 && *(p-1) == '%' && *(p-2) == '%') continue;
+       *p++ = c;
+    }
+    *p = '\0';                 /* terminate, then strip trailing junk */
+    while (p > buf && (*--p == ' ' || *p == '.')) *p = '\0';
+    return;
+}
+
+static char empty_string[] = "";
+
+/* fetch the text of a captured broadcast, then mangle and decipher it
+*/
+struct mail_info *
+parse_next_broadcast()         /* called by ckmailstatus(mail.c) */
+{
+    short length, msg_type;
+    $DESCRIPTOR(message, empty_string);        /* string descriptor for buf[] */
+    struct mail_info *result = 0;
+    /* messages could actually be longer; let long ones be truncated */
+    char buf[255+1];
+
+    message.dsc$a_pointer = buf,  message.dsc$w_length = sizeof buf - 1;
+    msg_type = length = 0;
+    smg$get_broadcast_message(&pasteboard_id, &message, &length, &msg_type);
+    if (msg_type == MSG$_TRMBRDCST) {
+       buf[length] = '\0';
+       filter_brdcst(buf);             /* mask non-printable characters */
+       result = parse_brdcst(buf);     /* do the real work */
+    } else if (msg_type == MSG$_TRMHANGUP) {
+       (void) gsignal(SIGHUP);
+    }
+    return result;
+}
+
+/* spit out any pending broadcast messages whenever we leave
+*/
+static void
+flush_broadcasts()     /* called from disable_broadcast_trapping() */
+{
+    if (broadcasts > 0) {
+       short len, typ;
+       $DESCRIPTOR(msg_dsc, empty_string);
+       char buf[512+1];
+
+       msg_dsc.dsc$a_pointer = buf,  msg_dsc.dsc$w_length = sizeof buf - 1;
+       raw_print("");          /* print at least one line for wait_synch() */
+       do {
+           typ = len = 0;
+           smg$get_broadcast_message(&pasteboard_id, &msg_dsc, &len, &typ);
+           if (typ == MSG$_TRMBRDCST) buf[len] = '\0',  raw_print(buf);
+       } while (--broadcasts);
+       wait_synch();           /* prompt with "Hit return to continue: " */
+    }
+}
+
+/* AST routine called when the terminal's associated mailbox receives a message
+*/
+/*ARGSUSED*/
+static void
+broadcast_ast(dummy)           /* called asynchronously by terminal driver */
+int dummy;     /* not used */
+{
+    broadcasts++;
+}
+
+/* initialize the broadcast manipulation code; SMG makes this easy
+*/
+unsigned long init_broadcast_trapping()   /* called by setftty() [once only] */
+{
+    unsigned long sts, preserve_screen_flag = 1;
+
+    /* we need a pasteboard to pass to the broadcast setup/teardown routines */
+    sts = smg$create_pasteboard(&pasteboard_id, 0, 0, 0, &preserve_screen_flag);
+    if (!vms_ok(sts)) {
+       errno = EVMSERR,  vaxc$errno = sts;
+       raw_print("");
+       perror("?can't create SMG pasteboard for broadcast trapping");
+       wait_synch();
+       broadcasts = -1;        /* flag that trapping is currently broken */
+    }
+    return sts;
+}
+
+/* set up the terminal driver to deliver $brkthru data to a mailbox device
+*/
+unsigned long enable_broadcast_trapping()      /* called by setftty() */
+{
+    unsigned long sts = 1;
+
+    if (broadcasts >= 0) {     /* (-1 => no pasteboard, so don't even try) */
+       /* register callback routine to be triggered when broadcasts arrive */
+       /* Note side effect:  also intercepts hangup notification. */
+       /* Another note:  TMPMBX privilege is required. */
+       sts = smg$set_broadcast_trapping(&pasteboard_id, broadcast_ast, 0);
+       if (!vms_ok(sts)) {
+           errno = EVMSERR,  vaxc$errno = sts;
+           raw_print("");
+           perror("?can't enable broadcast trapping");
+           wait_synch();
+       }
+    }
+    return sts;
+}
+
+/* return to 'normal'; $brkthru data goes straight to the terminal
+*/
+unsigned long disable_broadcast_trapping()     /* called by settty() */
+{
+    unsigned long sts = 1;
+
+    if (broadcasts >= 0) {
+       /* disable trapping; releases associated MBX so that SPAWN can work */
+       sts = smg$disable_broadcast_trapping(&pasteboard_id);
+       if (!vms_ok(sts)) errno = EVMSERR,  vaxc$errno = sts;
+       flush_broadcasts();     /* don't hold on to any buffered ones */
+    }
+    return sts;
+}
+#else  /* MAIL */
+       /* simple stubs for non-mail configuration */
+unsigned long init_broadcast_trapping() { return 1; }
+unsigned long enable_broadcast_trapping() { return 1; }
+unsigned long disable_broadcast_trapping() { return 1; }
+struct mail_info *parse_next_broadcast() { return 0; }
+#endif /* MAIL */
+
+/*----------------------------------------------------------------------*/
+
+#ifdef TEST_DRIVER
+       /* (Take parse_next_broadcast for a spin. :-) */
+
+volatile int broadcasts = 0;
+
+void newmail(foo)
+struct mail_info *foo;
+{
+# define STRING(s) ((s) ? (s) : "<null>")
+    printf("\n\
+  message type = %d\n\
+  display text = \"%s\"\n\
+  object name  = \"%.*s\"\n\
+  response cmd = \"%s\"\n\
+",      foo->message_typ, STRING(foo->display_txt),
+       (foo->object_nam && foo->response_cmd) ?
+               (foo->response_cmd - foo->object_nam - 1) :
+               strlen(STRING(foo->object_nam)),
+       STRING(foo->object_nam), STRING(foo->response_cmd));
+# undef STRING
+}
+
+void ckmailstatus()
+{
+    struct mail_info *brdcst, *parse_next_broadcast();
+
+    while (broadcasts > 0) {   /* process all trapped broadcasts [until] */
+       broadcasts--;
+       if ((brdcst = parse_next_broadcast()) != 0) {
+           newmail(brdcst);
+           break;              /* only handle one real message at a time */
+       } else
+           printf("\n--< non-broadcast encountered >--\n");
+    }
+}
+
+int main()
+{
+    char dummy[BUFSIZ];
+
+    init_broadcast_trapping();
+    enable_broadcast_trapping();
+    for (;;) {
+       ckmailstatus();
+       printf("> "), fflush(stdout);   /* issue a prompt */
+       if (!gets(dummy)) break;        /* wait for a response */
+    }
+    disable_broadcast_trapping();
+    return 1;
+}
+
+void panic(s) char *s; { raw_print(s); exit(EXIT_FAILURE); }
+
+void raw_print(s) char *s; { puts(s); fflush(stdout); }
+
+void wait_synch() { char dummy[BUFSIZ];
+  printf("\nPress <return> to continue: "); fflush(stdout); (void) gets(dummy);
+}
+#endif /* TEST_DRIVER */
+
+/*vmsmail.c*/