/* MLIST.C by Frank J. Wancho ([email protected]) 02/14/87

MLIST normally takes no arguments.  However, if OEXEC is not defined
and an argument is given, the SUBMIT command will not be issued so
that the program may conveniently debugged.

MLIST was designed as one consolidated solution to two problems with
mailing list processing:

1. The first problem is that mailing lists tend to generate long
queues which interfere with the timely processing of "normal" mail
embedded in the same queue.  MLIST puts mailing list mail in its own
queue to be processed by a slightly modified version of MMAILR.  The
modified version of MMAILR simply contains "REDIST-" substituted for
all occurances of "QUEUED-" in a copy of the MMAILR source renamed to
RMAILR.MAC.

2. The second problem is that normally there is no way to specify that
notices and failures are to be sent to the mailing list maintainer.
MLIST specifies of the RETURN-PATH: to be listname-REQUEST@OHN when it
creates the new queued mail file.

MLIST takes advantage of the Special Network feature of MMAILR,
documented elsewhere, and the format of the message file files created
in the Special Network message file directory.  In particular, the
second line of the message file contains the username portion of the
redirected address, which is expected to be the name of the mailing
list.  MLIST also expects the actual mailing list address to be named
listname-MLIST.  The set of example entries for a hypothetical list
named INFO-TEST in MAIL:MAILING-LISTS.TXT for Special Network host
named MLIST is:

INFO-TEST= INFO-TEST-QUEUE
INFO-TEST-QUEUE= INFO-TEST@MLIST
INFO-TEST-MLIST= @PS:<list-maintainer-directory>listfilename
INFO-TEST-REQUEST= list-maintainer-address

MLIST is written to be compiled with KCC.  Edit the values for
QUEUENAME and OHN (the Official Host Name for you host), and compile
with -DOEXEC if your EXEC has not been modified to accept input from
its RSCAN buffer.  Copy the resulting MLIST.EXE file into the Special
Network directory for this "host".  Create a self-perpetuating batch
job in that directory and start it running under OPERATOR.  Finally,
create a version of MMAILR as RMAILR as described above and run it
under SYSJOB.

*/

#include <stdio.h>
#include <string.h>

/* src:<kccdist.kcc-6.lib.usys>exit.c */
extern void exit(int);

/* src:<kccdist.kcc-6.lib.usys>unlink.c */
extern int unlink(char *);

/*
* Forward(s).
*/
int domsg(int);
long gtad(void);
int strip(char *);
int system(char *);
int _xfork(char *,char *);


#define LINESIZE    1026                /* max line input size from msg */
#define FNSIZE      100                 /* max file name size */
#define OHN         "mathom.xkl.com"    /* Official Host Name of local host */
#define QUEUENAME   "REDIST"            /* part of RMAILR queue filenames */

FILE    *inmsg;                         /* input message file descriptor */
FILE    *outmsg;                        /* output message file descriptor */

char    line[LINESIZE];                 /* input line from message file */
char    infile[FNSIZE];                 /* input filename */
char    outfile[FNSIZE];                /* output filename */
char    tmp[FNSIZE];                    /* temporary string */
int     debug;                          /* runtime debug flag */


main (num, arg)
int     num;
char    *arg[];
{
   char   *d;                         /* pointer to string returned by dir() */
   char   *name[500];                 /* pointer to pointers to names */
   char   *dir();                     /* required declaration */
   char   *malloc();                  /* required declaration */
   int     nfiles;                    /* count of number of files found */
   int     n;                         /* loop counter */

   debug = 0;
   if (num > 1) {                     /* if any args */
       debug = 1;                     /* set flag */
   }

   /* First rename any left over files from some previous run */

   for (d = dir ("-MESSAGE.*.*", 1); *d; d = dir (NULL, 1)) {
       sprintf (infile, "-MAIL.0%o-%d", gtad(), ++nfiles);
       if (rename (d, infile)) {      /* if rename fails */
           exit (1);                  /* exit */
       }
   }
   nfiles = 0;                        /* initialize count */

   /* Find and save the names of all message files */

   for (d = dir ("-MAIL.*", 1); *d; d = dir (NULL, 1)) {
       name[nfiles] = malloc (strlen (d) + 1); /* allocate space */
       strcpy (name[nfiles], d);       /* save the name */
       if (nfiles++ > 200) {          /* found one - bump count */
           break;
       }
   }
   if (nfiles) {                      /* if any file found */
       /* Rename each file, one at a time, process and delete it */
       for (n = 0; n < nfiles; n++) {
           sprintf (infile, "-MESSAGE.%o-%d", gtad(), n+1);
           if (rename (name[n], infile)) { /* if rename failed */
               exit (1);              /* exit */
           }
           if ((inmsg = fopen (infile, "r")) == NULL) { /* open the file */
               exit (1);              /* exit if failed to open */
           }
           domsg (n);                 /* process the file */
           fclose (inmsg);            /* close it */
           unlink (infile);           /* delete and expunge it */
       }
   }
#ifndef OEXEC                          /* if EXEC modified, compile this */
   if (!debug) {                      /* if debug off */
system ("EXEC SUBMIT MLIST.CTL/OUTPUT:NOLOG/UNI:NO/BAT:SUPER/TIM:60/AFTER:+1:00/RESTART:YES"); /* self-perpetuate */
   }
#endif
   exit (0);                          /* done for now */
}

/* domsg() takes one arg - the current loop counter value for creating
a unique queue filename, creates the new queue file from the message file. */

int
domsg (n)
int     n;
{
   sprintf (outfile, "\026[--%s-MAIL--\026].NEW-%o-MLIST-%d",
            QUEUENAME, gtad(), n);    /* create the unique queue filename */
   if ((outmsg = fopen (outfile, "w")) == NULL) { /* open it for write */
       exit (1);                      /* exit on failure */
   }
   fgets (line, LINESIZE, inmsg);     /* read the first line from message */
   fgets (line, LINESIZE, inmsg);     /* read the second line */
   strip (line);                      /* remove any useless chars */
   fprintf(outmsg, "\f=RETURN-PATH:%s-REQUEST@%s\n", line, OHN);
   fprintf(outmsg, "\f_%s\n%s-REQUEST\n", OHN, line);
   fprintf(outmsg, "\f%s\n%s-MLIST\n", OHN, line); /* write envelope */
   while (fgets (line, LINESIZE, inmsg) != NULL) { /* copy rest of file */
       fputs (line, outmsg);          /* to new queue file */
   }
   fclose (outmsg);                   /* close queue file */
   sprintf(tmp, "MAILQ:%s", outfile); /* form target name in MAILQ: */
   rename (outfile, tmp);             /* rename it and ignore failures */
   return (0);
}

#include <jsys.h>

static char result[FNSIZE];

char *
dir(fname, flg) /* expands wildcard filename, returning the next  */
char *fname;    /* available filename on each successive call. */
               /* Returns NULL when list exhausted */
int flg;        /* If flag is 1, only the fn.typ is returned. */
{
   int     ablock[5];
   int     njfn;
   char    buf[FNSIZE];
   static int  first = 1;      /* true only on first call */
   static int  jfn;

   if (fname) {                /* if filename is given, use it */
       ablock[1] = GJ_SHT | GJ_OLD | GJ_IFG | T20_BIT (13);
       ablock[2] = (int) (fname - 1);
       if (!(jsys (GTJFN, ablock))) {
           return NULL;
       }
       jfn = ablock[1];
       njfn = ablock[1];
   } else {
       if (first) {            /* if no name and first call */
           return NULL;        /* then not much we can do */
       } else {                /* else search for next */
           ablock[1] = jfn;
           if (!(jsys (GNJFN, ablock))) {
               first = 1;
               return NULL;
           }
           njfn = ablock[1];
       }
   }
   first = 0;                  /* no longer first time */

   ablock[1] = (int) (buf - 1);
   ablock[2] = njfn & 0777777;
   if (flg) {
       ablock[3] = T20_BIT (8) | T20_BIT (11) | T20_BIT (35);
   } else {
       ablock[3] = 0;
   }
   if (!(jsys (JFNS, ablock))) {
       first = 1;
       return NULL;
   }
   strcpy (result, buf);       /* save name of file */
   return result;
}

long
gtad()          /* Calls GTAD and returns the value in AC1, the */
               /* current system date. */
{
   int ac[5];

   jsys (GTAD, ac);
   return (ac[1]);
}

int
strip(s)        /* Returns s stripped of any leading or trailing */
char *s;        /* blanks and trailing CRs */
{
   int i;
   char *p;

   if (strlen (s) == 0) {
       return (1);
   }
   p = s;
   while (*p == ' ')           /* strip leading blanks */
       for (i = 0; *(p+i); i++)
           *(p+i) = *(p+i+1);
   while (*p) p++;
   --p;
   while ((*p == ' ') || (*p == '\n')) {/* strip trailing blanks an cr */
       *p = 0;
       p--;
   }
}

system(s)       /* Processes the command line, s, composed of a */
char *s;        /* program name to run in an inferior fork with the */
               /* line passed to the program via RSCAN.  If the first */
               /* characters are "EXEC ", these characters are */
               /* stripped off the string and SYSTEM:EXEC.EXE is the */
               /* program loaded in the inferior fork and the */
               /* remainder of the string is passed to EXEC via */
               /* RSCAN, assuming that it has been modifed to read */
               /* its rescan buffer. */
{
       char    *p, t[256], q[256];
       char    *index ();

       if (!strncmp (s, "EXEC ", 5)) {
           strcpy (t, "SYSTEM:");
       } else {
           strcpy (t, "SYS:");
       }
       strcat (t, s);
       p = t;
       if (p = index (t, ' '))
               *p = '\0';
       strcat (t, ".EXE");
       strcpy (q, s);
       strcat (q, "\n");

       _xfork (t, q);
}

int `jfn`;
int `frk`;                              /* fork handle */
int `pc`;                               /* starting pc of process */

int
_xfork (pgmname, cmdline)
char    *pgmname;               /* Program name to run */
char    *cmdline;               /* Command line */
{
#asm
       SEARCH  MONSYM
       EXTERN $RETZ, $RETT, $RETF      /* From CRT */

       movsi   1,(GJ%OLD+GJ%SHT)
       seto    2,0
       adjbp   2,-1(17)
;[rwf]  %CHRBP  2,-1(17)                ; get pointer to program name
       gtjfn%                          ; get JFN of program file
        ERJMP  $RETZ                   ; cannot open exe file
       hrrzm   1,JFN                   ; save JFN

       hrli    1,(CR%CAP)              ; run subfork
       cfork%                          ; create inferior with same capability
        ERJMP  RELJFN                  ; cannot create fork
       movem   1,FRK                   ; save fork handle
       ffork%                          ; and freeze the fork

       hrlz    1,FRK                   ; fork handle
       hrr     1,JFN                   ; JFN of program file
       setzm   2
       get%                            ; load program image into inferior fork
        ERJMP  RELJFN

       hrrz    1,FRK
       gevec%                          ; get entry vector
        ERJMP  RELJFN                  ; oops??
       hrrz    2,2
       movem   2,PC

;[Rwf]  %CHRBP  1,-2(17)                ; Get RSCAN buffer as ILDB pointer
       rscan%                          ; Set it
        ERJMP  RELJFN                  ; Lost

       hrrz    1,FRK
       move    2,PC
       sfork%                          ; start fork
       rfork%                          ; resume frozen fork
       wfork%                          ; wait for fork to terminate
       kfork%                          ; then kill fork
       jrst    $RETT

RELJFN: hrrz    1,JFN
       rljfn%
       jrst    $RETF

#endasm
}