[alt.sources.amiga] Date Requester, Part 2/5

mrr@amanpt1.Newport.RI.US (Mark Rinfret) (10/12/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 5)."
# Contents:  MRDates.c minrexx.c
# Wrapped by mrr@mrramiga on Wed Oct 11 18:10:44 1989
# This shar was created for Amiga and may have pathnames which
# are incompatible with Unix.  Replace colon (:) with slash (/)
# in offending pathnames. 
if test -f MRDates.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"MRDates.c\"
else
echo shar: Extracting \"MRDates.c\" \(15277 characters\)
sed "s/^X//" >MRDates.c <<'END_OF_MRDates.c'
X/*
X    MRDates - AmigaDOS date support routines.
X    07/03/88
X
X    This package is a hybrid of code from Thad Floryan, Doug Merrit and
X    myself.  I wanted a reliable set of AmigaDOS date conversion routines
X    and this combination seems to work pretty well.  The star of the show
X    here is Thad's algorithm for converting the "days elapsed" field of
X    an AmigaDOS DateStamp, using an intermediate Julian date format.  I
X    lifted/embellished some of the data structures from Doug's ShowDate
X    package and wrote the DateToDS function.
X
X    History:    (most recent change first)
X
X    12/31/88 -MRR-
X        StrToDS was not handling the null string properly.
X
X    11/01/88 -MRR- Added Unix-style documentation.
X
X    07/03/88 - Changed some names:
X                  Str2DS => StrToDS
X                  DS2Str => DSToStr
X */
X
X#define MRDATES
X#include "MRDates.h"
X#include <exec/types.h>
X#include <ctype.h>
X
X
Xchar *index();
X
X#define DATE_SEPARATORS     "/:-."
X#define MINS_PER_HOUR       60
X#define SECS_PER_MIN        60
X#define SECS_PER_HOUR       (SECS_PER_MIN * MINS_PER_HOUR)
X#define TICS_PER_SEC        50
X
X#define YEARS_PER_CENTURY   100
X
X
X/*
X   definitions to calculate current date
X */
X#define FEB             1   /* index of feb. in table (for leap years) */
X#define DAYS_PER_WEEK   7
X#define DAYS_PER_YEAR   365
X#define YEARS_PER_LEAP  4
X#define START_YEAR      1978
X#define FIRST_LEAP_YEAR 1980
X#define LEAP_ADJUST     (FIRST_LEAP_YEAR - START_YEAR)
X#define LEAP_FEB_DAYS   29
X#define NORM_FEB_DAYS   28
X#define IsLeap(N)       (((N) % YEARS_PER_LEAP) ? 0 : 1)
X
X
X/*  FUNCTION
X        DSToDate - convert a DateStamp to an MRDate.
X
X    SYNOPSIS
X        int DSToDate(dateStamp, date)
X            struct DateStamp *dateStamp;
X            MRDate *date;
X
X    DESCRIPTION
X      Extracts the date components from an AmigaDOS datestamp.
X      The calculations herein use the following assertions:
X
X      146097 = number of days in 400 years per 400 * 365.2425 = 146097.00
X       36524 = number of days in 100 years per 100 * 365.2425 =  36524.25
X        1461 = number of days in   4 years per   4 * 365.2425 =   1460.97
X
X    AUTHOR
X      Thad Floryan, 12-NOV-85
X      Mods by Mark Rinfret, 04-JUL-88
X
X    SEE ALSO
X        Include file MRDates.h.
X
X */
X
X#define DDELTA 722449   /* days from Jan.1,0000 to Jan.1,1978 */
X
Xstatic int mthvec[] =
X   {-1, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364};
X
Xint
XDSToDate(ds, date)
X    long *ds; MRDate *date;
X
X{
X
X    long jdate, day0, day1, day2, day3;
X    long year, month, day, temp;
X
X    jdate = ds[0] + DDELTA;      /* adjust internal date to Julian */
X
X    year = (jdate / 146097) * 400;
X    day0  = day1 = jdate %= 146097;
X    year += (jdate / 36524) * 100;
X    day2  = day1 %= 36524;
X    year += (day2 / 1461) * 4;
X    day3  = day1 %= 1461;
X    year += day3 / 365;
X    month = 1 + (day1 %= 365);
X    day = month % 30;
X    month /= 30;
X
X    if ( ( day3 >= 59 && day0 < 59 ) ||
X        ( day3 <  59 && (day2 >= 59 || day0 < 59) ) )
X      ++day1;
X
X    if (day1 > mthvec[1 + month]) ++month;
X    day = day1 - mthvec[month];
X    date->Dyear = year;
X    date->Dmonth = month;
X    date->Dday = day;
X    date->Dweekday = ds[0] % DAYS_PER_WEEK;
X
X    temp = ds[1];               /* get ds_Minute value */
X    date->Dhour = temp / MINS_PER_HOUR;
X    date->Dminute = temp % MINS_PER_HOUR;
X    date->Dsecond = ds[2] / TICS_PER_SEC;
X    return 0;
X}
X
X/*  FUNCTION
X        DateToDS(date, dateStamp)
X
X    SYNOPSIS
X        void DateToDS(date, dateStamp)
X             MRDate *date;
X             struct DateStamp *dateStamp;
X
X    DESCRIPTION
X        DateToDS converts the special MRDate format to a DateStamp.
X */
X
XDateToDS(date, ds)
X    MRDate *date; long *ds;
X{
X    long daysElapsed, yearsElapsed, leapYears, thisMonth, thisDay, thisYear;
X    int month;
X
X    /* Note the special handling for year < START_YEAR.  In this case,
X     * the other fields are not even checked - the user just gets the
X     * "start of time".
X     */
X    if ((thisYear = date->Dyear) < START_YEAR) {
X        ds[0] = ds[1] = ds[2] = 0;
X        return;
X    }
X    if (IsLeap(thisYear))
X        calendar[FEB].Mdays = LEAP_FEB_DAYS;
X
X    thisDay = date->Dday - 1;
X    thisMonth = date->Dmonth -1;
X    yearsElapsed = thisYear - START_YEAR;
X    leapYears = (yearsElapsed + LEAP_ADJUST -1) / YEARS_PER_LEAP;
X    daysElapsed = (yearsElapsed * DAYS_PER_YEAR) + leapYears;
X    for (month = 0; month < thisMonth; ++month)
X        daysElapsed += calendar[month].Mdays;
X    daysElapsed += thisDay;
X    calendar[FEB].Mdays = NORM_FEB_DAYS;
X    ds[0] = daysElapsed;
X    ds[1] = date->Dhour * MINS_PER_HOUR + date->Dminute;
X    ds[2] = date->Dsecond * TICS_PER_SEC;
X}
X/*  FUNCTION
X        CompareDS - compare two DateStamp values.
X
X    SYNOPSIS
X        int CompareDS(date1, date2)
X            struct DateStamp *date1, *date2;
X
X    DESCRIPTION
X        CompareDS performs an ordered comparison between two DateStamp
X        values, returning the following result codes:
X
X            -1 => date1 < date2
X             0 => date1 == date2
X             1 => date1 > date2
X
X    NOTE:
X        This routine makes an assumption about the DateStamp structure,
X        specifically that it can be viewed as an array of 3 long integers
X        in days, minutes and ticks order.
X */
X
Xint
XCompareDS(d1, d2)
X    long *d1, *d2;
X{
X    USHORT i;
X    long compare;
X
X    for (i = 0; i < 3; ++i) {
X        if (compare = (d1[i] - d2[i])) {
X            if (compare < 0) return -1;
X            return 1;
X        }
X    }
X    return 0;                       /* dates match */
X}
X
X/*  FUNCTION
X        DSToStr - convert a DateStamp to a formatted string.
X
X    SYNOPSIS
X        void DSToStr(str,fmt,d)
X             char *str, *fmt;
X             struct DateStamp *d;
X
X    DESCRIPTION
X        DSToStr works a little like sprintf.  It converts a DateStamp
X        to an ascii formatted string.  The formatting style is dependent
X        upon the contents of the format string, fmt, passed to this
X        function.
X
X        The content of the format string is very similar to that
X        for printf, with the exception that the following letters
X        have special significance:
X            y => year minus 1900
X            Y => full year value
X            m => month value as integer
X            M => month name
X            d => day of month (1..31)
X            D => day name ("Monday".."Sunday")
X            h => hour in twenty-four hour notation
X            H => hour in twelve hour notation
X            i => 12 hour indicator for H notation (AM or PM)
X            I => same as i
X            n => minutes    (sorry...conflict with m = months)
X            N => same as n
X            s => seconds
X            S => same as s
X
X        All other characters are passed through as part of the normal
X        formatting process.  The following are some examples with
X        Saturday, July 18, 1987, 13:53 as an input date:
X
X            "%y/%m/%d"          => 87/7/18
X            "%02m/%02d/%2y"     => 07/18/87
X            "%D, %M %d, %Y"     => Saturday, July 18, 1987
X            "%02H:%02m i"       => 01:53 PM
X            "Time now: %h%m"    => Time now: 13:53
X
X */
Xvoid
XDSToStr(str,fmt,d)
X    char *str, *fmt; long *d;
X{
X    MRDate date;
X    char fc,*fs,*out;
X    USHORT ivalue;
X    char new_fmt[256];          /* make it big to be "safe" */
X    USHORT new_fmt_lng;
X    char *svalue;
X
X    DSToDate(d, &date);         /* convert DateStamp to MRDate format */
X
X    *str = '\0';                /* insure output is empty */
X    out = str;
X    fs = fmt;                   /* make copy of format string pointer */
X
X    while (fc = *fs++) {        /* get format characters */
X        if (fc == '%') {        /* formatting meta-character? */
X            new_fmt_lng = 0;
X            new_fmt[new_fmt_lng++] = fc;
X            /* copy width information */
X            while (isdigit(fc = *fs++) || fc == '-')
X                new_fmt[new_fmt_lng++] = fc;
X
X            switch (fc) {       /* what are we trying to do? */
X            case 'y':           /* year - 1980 */
X                ivalue = date.Dyear % 100;
Xwrite_int:
X                new_fmt[new_fmt_lng++] = 'd';
X                new_fmt[new_fmt_lng] = '\0';
X                sprintf(out,new_fmt,ivalue);
X                out = str + strlen(str);
X                break;
X            case 'Y':           /* full year value */
X                ivalue = date.Dyear;
X                goto write_int;
X
X            case 'm':           /* month */
X                ivalue = date.Dmonth;
X                goto write_int;
X
X            case 'M':           /* month name */
X                svalue = calendar[date.Dmonth - 1].Mname;
Xwrite_str:
X                new_fmt[new_fmt_lng++] = 's';
X                new_fmt[new_fmt_lng] = '\0';
X                sprintf(out,new_fmt,svalue);
X                out = str + strlen(str);
X                break;
X
X            case 'd':           /* day */
X                ivalue = date.Dday;
X                goto write_int;
X
X            case 'D':           /* day name */
X                svalue = dayNames[d[0] % DAYS_PER_WEEK];
X                goto write_str;
X
X            case 'h':           /* hour */
X                ivalue = date.Dhour;
X                goto write_int;
X
X            case 'H':           /* hour in 12 hour notation */
X                ivalue = date.Dhour;
X                if (ivalue >= 12) ivalue -= 12;
X                goto write_int;
X
X            case 'i':           /* AM/PM indicator */
X            case 'I':
X                if (date.Dhour >= 12)
X                    svalue = "PM";
X                else
X                    svalue = "AM";
X                goto write_str;
X
X            case 'n':           /* minutes */
X            case 'N':
X                ivalue = date.Dminute;
X                goto write_int;
X
X            case 's':           /* seconds */
X            case 'S':
X                ivalue = date.Dsecond;
X                goto write_int;
X
X            default:
X                /* We are in deep caca - don't know what to do with this
X                 * format character.  Copy the raw format string to the
X                 * output as debugging information.
X                 */
X                new_fmt[new_fmt_lng++] = fc;
X                new_fmt[new_fmt_lng] = '\0';
X                strcat(out, new_fmt);
X                out = out + strlen(out);    /* advance string pointer */
X                break;
X            }
X        }
X        else
X            *out++ = fc;        /* copy literal character */
X    }
X    *out = '\0';                /* terminating null */
X}
X
X/*  FUNCTION
X        StrToDS - convert a string to a DateStamp.
X
X    SYNOPSIS
X        int StrToDS(string, date)
X            char *string;
X            struct DateStamp *date;
X
X    DESCRIPTION
X        StrToDS expects its string argument to contain a date in
X        MM/DD/YY HH:MM:SS format.  The time portion is optional.
X        StrToDS will attempt to convert the string to a DateStamp
X        representation.  If successful, it will return 0.  On
X        failure, a 1 is returned.
X
X */
X
Xint
XStrToDS(str, d)
X    char *str; long *d;
X{
X    register char c;
X    int count;
X    int i, item;
X    MRDate date;              /* unpacked DateStamp */
X    char *s;
X
X    int values[3];
X    int value;
X
X    s = str;
X    for (item = 0; item < 2; ++item) {  /* item = date, then time */
X        for (i = 0; i < 3; ++i) values[i] = 0;
X        count = 0;
X        while (c = *s++) {          /* get date value */
X            if (c <= ' ')
X                break;
X
X            if (isdigit(c)) {
X                value = 0;
X                do {
X                    value = value*10 + c - '0';
X                    c = *s++;
X                } while (isdigit(c));
X                if (count == 3) {
X    bad_value:
X#ifdef DEBUG
X                    puts("Error in date-time format.\n");
X                    printf("at %s: values(%d) = %d, %d, %d\n",
X                        s, count, values[0], values[1], values[2]);
X#endif
X                    return 1;
X                }
X                values[count++] = value;
X                if (c <= ' ')
X                    break;
X            }
X            else if (! index(DATE_SEPARATORS, c) )
X                goto bad_value;     /* Illegal character - quit. */
X        }                           /* end while */
X        if (item) {                 /* Getting time? */
X            date.Dhour = values[0];
X            date.Dminute = values[1];
X            date.Dsecond = values[2];
X        }
X        else {                      /* Getting date? */
X
X/* It's OK to have a null date string, but it's not OK to specify only
X   1 or 2 of the date components.
X */
X            if (count && count != 3)
X                goto bad_value;
X            date.Dmonth = values[0];
X            date.Dday = values[1];
X            date.Dyear = values[2];
X            if (date.Dyear == 0) {
X                date.Dyear = START_YEAR;
X                date.Dday = 1;
X            }
X            else {
X                if (date.Dyear < (START_YEAR - 1900) )
X                    date.Dyear += 100;
X                date.Dyear += 1900;
X            }
X        }
X    }                               /* end for */
X    DateToDS(&date, d);
X    return 0;
X}                                   /* StrToDS */
X
X
X#ifdef DEBUG
X#include "stdio.h"
Xmain(ac, av)
X    int ac;
X    char    **av;
X{
X    long    datestamp[3];           /* A little dangerous with Aztec */
X    long    datestamp2[3];
X    MRDate    date, oldDate;
X    long    day, lastDay;
X    int     errors = 0;
X
X    /*
X     * display results from DateStamp() (hours:minutes:seconds)
X     */
X    DateStamp(datestamp);
X
X    /*
X     * display results from DSToDate() (e.g. "03-May-88")
X     */
X    DSToDate(datestamp, &date);
X    printf("Current date: %02d-%s-%02d\n",
X        date.Dday, calendar[ date.Dmonth - 1].Mname,
X        (date.Dyear % YEARS_PER_CENTURY));
X
X    printf("Current time: %02d:%02d:%02d\n",
X        date.Dhour, date.Dminute, date.Dsecond);
X
X    printf("\nDoing sanity check through year 2000...\n\t");
X    lastDay = (2000L - START_YEAR) * 365L;
X    lastDay += (2000L - START_YEAR) / YEARS_PER_LEAP;
X    for (day = 0; day <= lastDay; ++day) {
X        if (day % 1000 == 0) {
X            printf(" %ld", day);
X            fflush(stdout);
X        }
X        datestamp[0] = day;
X        datestamp[1] = MINS_PER_HOUR - 1;
X        datestamp[2] = TICS_PER_SEC * (SECS_PER_MIN - 1);
X        DSToDate(datestamp, &date);
X        if (day && date == oldDate) {
X            printf("Got same date for days %d, %d: %02d-%s-%02d\n",
X                    day - 1, day,
X                    date.Dday,
X                    calendar[ date.Dmonth - 1 ].Mname,
X                    (date.Dyear % YEARS_PER_CENTURY));
X
X            if (++errors == 10)
X                exit(1);
X        }
X        DateToDS(&date, datestamp2);
X        if (day != datestamp2[0]) {
X            printf("\nConversion mismatch at day %ld!\n", day);
X            printf("\tBad value = %ld", datestamp2[0]);
X            printf("\tDate: %02d-%s-%02d\n",
X                    date.Dday,
X                    calendar[ date.Dmonth  -1 ].Mname,
X                    (date.Dyear % YEARS_PER_CENTURY));
X            if (++errors == 10)
X                exit(1);
X        }
X        oldDate = date;
X    }
X    printf("\nSanity check passed.\n");
X} /* main() */
X#endif
X
END_OF_MRDates.c
if test 15277 -ne `wc -c <MRDates.c`; then
    echo shar: \"MRDates.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f minrexx.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"minrexx.c\"
else
echo shar: Extracting \"minrexx.c\" \(15445 characters\)
sed "s/^X//" >minrexx.c <<'END_OF_minrexx.c'
X/*
X *   This is an example of how REXX messages might be handled.  This is
X *   a `minimum' example that both accepts asynchronous REXX messages and
X *   can request REXX service.
X *
X *   Read this entire file!  It's short enough.
X *
X *   It is written in such a fashion that it can be attached to a program
X *   with a minimum of fuss.  The only external symbols it makes available
X *   are the seven functions and RexxSysBase.
X *
X *   This code is by Radical Eye Software, but it is put in the public
X *   domain.  I would appreciate it if the following string was left in
X *   both as a version check and as thanks from you for the use of this
X *   code.
X *
X *   If you modify this file for your own use, don't bump the version
X *   number; add a suffix, such as 1.0a or 1.0.3 or something, so we
X *   don't have fake `versions' floating around.
X */
Xstatic char *blurb = "Radical Eye MinRexx 0.4" ;
X/*
X *   We read in our own personal little include.
X */
X#include "minrexx.h"
X/*
X *   All of our local globals, hidden from sight.
X */
Xstatic struct MsgPort *rexxPort ;          /* this is *our* rexx port */
Xstatic int bringerdown ;                   /* are we trying to shut down? */
Xstatic struct rexxCommandList *globalrcl ; /* our command association list */
Xstatic long stillNeedReplies ;             /* how many replies are pending? */
Xstatic long rexxPortBit ;                  /* what bit to wait on for Rexx? */
Xstatic char *extension ;                   /* the extension for macros */
Xstatic int (*userdisp)() ;                 /* the user's dispatch function */
Xstatic struct RexxMsg *oRexxMsg ;          /* the outstanding Rexx message */
X/*
X *   Our library base.  Don't you dare close this!
X */
Xstruct RxsLib *RexxSysBase ;
X/*
X *   This is the main entry point into this code.
X */
Xlong upRexxPort(s, rcl, exten, uf)
X/*
X *   The first argument is the name of your port to be registered;
X *   this will be used, for instance, with the `address' command of ARexx.
X */
Xchar *s ;
X/*
X *   The second argument is an association list of command-name/user-data
X *   pairs.  It's an array of struct rexxCommandList, terminated by a
X *   structure with a NULL in the name field. The commands are case
X *   sensitive.  The user-data field can contain anything appropriate,
X *   perhaps a function to call or some other data.
X */
Xstruct rexxCommandList *rcl ;
X/*
X *   The third argument is the file extension for ARexx macros invoked
X *   by this program.  If you supply this argument, any `primitive' not
X *   in the association list rcl will be sent out to ARexx for
X *   interpretation, thus allowing macro programs to work just like
X *   primitives.  If you do not want this behavior, supply a `NULL'
X *   here, and those commands not understood will be replied with an
X *   error value of RXERRORNOCMD.
X */
Xchar *exten ;
X/*
X *   The fourth argument is the user dispatch function.  This function
X *   will *only* be called from rexxDisp(), either from the user calling
X *   this function directly, or from dnRexxPort().  Anytime a command
X *   match is found in the association list, this user-supplied function
X *   will be called with two arguments---the Rexx message that was
X *   received, and a pointer to the association pair.  This function
X *   should return a `1' if the message was replied to by the function
X *   and a `0' if the default success code of (0, 0) should be returned.
X *   Note that the user function should never ReplyMsg() the message;
X *   instead he should indicate the return values with replyRexxCmd();
X *   otherwise we lose track of the messages that still lack replies.
X */
Xint (*uf)() ;
X/*
X *   upRexxPort() returns the signal bit to wait on for Rexx messages.
X *   If something goes wrong, it simply returns a `0'.  Note that this
X *   function is safe to call multiple times because we check to make
X *   sure we haven't opened already.  It's also a quick way to change
X *   the association list or dispatch function.
X */
X{
X   struct MsgPort *FindPort() ;
X   struct MsgPort *CreatePort() ;
X
X/*
X *   Some basic error checking.
X */
X   if (rcl == NULL || uf == NULL)
X      return(0L) ;
X/*
X *   If we aren't open, we make sure no one else has opened a port with
X *   this name already.  If that works, and the createport succeeds, we
X *   fill rexxPortBit with the value to return.
X *
X *   Note that rexxPortBit will be 0 iff rexxPort is NULL, so the check
X *   for rexxPort == NULL also insures that our rexxPortBit is 0.
X */
X   if (rexxPort == NULL) {
X      Forbid() ;
X      if (FindPort(s)==NULL)
X         rexxPort = CreatePort(s, 0L) ;
X      Permit() ;
X      if (rexxPort != NULL)
X         rexxPortBit = 1L << rexxPort->mp_SigBit ;
X   }
X/*
X *   Squirrel away these values for our own internal access, and return
X *   the wait bit.
X */
X   globalrcl = rcl ;
X   extension = exten ;
X   userdisp = uf ;
X   return(rexxPortBit) ;
X}
X/*
X *   This function closes the rexx library, but only if it is open
X *   and we aren't expecting further replies from REXX.  It's
X *   *private*, but it doesn't have to be; it's pretty safe to
X *   call anytime.
X */
Xstatic void closeRexxLib() {
X   if (stillNeedReplies == 0 && RexxSysBase) {
X      CloseLibrary(RexxSysBase) ;
X      RexxSysBase = NULL ;
X   }
X}
X/*
X *   This function closes down the Rexx port.  It is always safe to
X *   call, and should *definitely* be made a part of your cleanup
X *   routine.  No arguments and no return.  It removes the Rexx port,
X *   replies to all of the messages and insures that we get replies
X *   to all the ones we sent out, closes the Rexx library, deletes the
X *   port, clears a few flags, and leaves.
X */
Xvoid dnRexxPort() {
X   if (rexxPort) {
X      RemPort(rexxPort) ;
X      bringerdown = 1 ;
X/*
X *   A message still hanging around?  We kill it off.
X */
X      if (oRexxMsg) {
X         oRexxMsg->rm_Result1 = RXERRORIMGONE ;
X         ReplyMsg(oRexxMsg) ;
X         oRexxMsg = NULL ;
X      }
X      while (stillNeedReplies) {
X         WaitPort(rexxPort) ;
X         dispRexxPort() ;
X      }
X      closeRexxLib() ;
X      DeletePort(rexxPort) ;
X      rexxPort = NULL ;
X   }
X   rexxPortBit = 0 ;
X}
X/*
X *   Here we dispatch any REXX messages that might be outstanding.
X *   This is the main routine for handling Rexx messages.
X *   This function is fast if no messages are outstanding, so it's
X *   pretty safe to call fairly often.
X *
X *   If we are bring the system down and flushing messages, we reply
X *   with a pretty serious return code RXERRORIMGONE.
X *
X *   No arguments, no returns.
X */
Xvoid dispRexxPort() {
X   register struct RexxMsg *GetMsg() ;
X   register struct RexxMsg *RexxMsg ;
X   int cmdcmp() ;
X   register struct rexxCommandList *rcl ;
X   register char *p ;
X   register int dontreply ;
X
X/*
X *   If there's no rexx port, we're out of here.
X */
X   if (rexxPort == NULL)
X      return ;
X/*
X *   Otherwise we have our normal loop on messages.
X */
X   while (RexxMsg = (struct RexxMsg *)GetMsg(rexxPort)) {
X/*
X *   If we have a reply to a message we sent, we look at the second
X *   argument.  If it's set, it's a function we are supposed to call
X *   so we call it.  Then, we kill the argstring and the message
X *   itself, decrement the outstanding count, and attempt to close
X *   down the Rexx library.  Note that this call only succeeds if
X *   there are no outstanding messages.  Also, it's pretty quick, so
X *   don't talk to me about efficiency.
X */
X      if (RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
X         if (RexxMsg->rm_Args[1]) {
X            ((int (*)())(RexxMsg->rm_Args[1]))(RexxMsg) ;
X         }
X         DeleteArgstring(RexxMsg->rm_Args[0]) ;
X         DeleteRexxMsg(RexxMsg) ;
X         stillNeedReplies-- ;
X         closeRexxLib() ;
X/*
X *   The default case is we got a message and we need to check it for
X *   primitives.  We skip past any initial tabs or spaces and initialize
X *   the return code fields.
X */
X      } else {
X         p = (char *)RexxMsg->rm_Args[0] ;
X         while (*p > 0 && *p <= ' ')
X            p++ ;
X         RexxMsg->rm_Result1 = 0 ;
X         RexxMsg->rm_Result2 = 0 ;
X/*
X *   If somehow the reply is already done or postponed, `dontreply' is
X *   set.
X */
X         dontreply = 0 ;
X/*
X *   If the sky is falling, we just blow up and replymsg.
X */
X         if (bringerdown) {
X            RexxMsg->rm_Result1 = RXERRORIMGONE ;
X/*
X *   Otherwise we cdr down our association list, comparing commands,
X *   until we get a match.  If we get a match, we call the dispatch
X *   function with the appropriate arguments, and break out.
X */
X         } else {
X            oRexxMsg = RexxMsg ;
X            for (rcl = globalrcl; rcl->name; rcl++) {
X               if (cmdcmp(rcl->name, p) == 0) {
X                  userdisp(RexxMsg, rcl, p+strlen(rcl->name)) ;
X                  break ;
X               }
X            }
X/*
X *   If we broke out, rcl will point to the command we executed; if we
X *   are at the end of the list, we didn't understand the command.  In
X *   this case, if we were supplied an extension in upRexxPort, we know
X *   that we should send the command out, so we do so, synchronously.
X *   The synchronous send takes care of our reply.  If we were given a
X *   NULL extension, we bitch that the command didn't make sense to us.
X */
X            if (rcl->name == NULL) {
X               if (extension) {
X                  syncRexxCmd(RexxMsg->rm_Args[0], RexxMsg) ;
X                  dontreply = 1 ;
X               } else {
X                  RexxMsg->rm_Result1 = RXERRORNOCMD ;
X               }
X            }
X         }
X/*
X *   Finally, reply if appropriate.
X */
X         oRexxMsg = NULL ;
X         if (! dontreply)
X            ReplyMsg(RexxMsg) ;
X      }
X   }
X}
X/*
X *   This is the function we use to see if the command matches
X *   the command string.  Not case sensitive, and the real command only
X *   need be a prefix of the command string.  Make sure all commands
X *   are given in lower case!
X */
Xstatic int cmdcmp(c, m)
Xregister char *c, *m ;
X{
X   while (*c && ((*c == *m) || (*c == *m + 32 && ('a' <= *c && *c <= 'z')))) {
X      c++ ;
X      m++ ;
X   }
X   return(*c) ;
X}
X/*
X *   Opens the Rexx library if unopened.  Returns success (1) or
X *   failure (0).  This is another function that is *private* but
X *   that doesn't have to be.
X */
Xstatic int openRexxLib() {
X   struct RxsLib *OpenLibrary() ;
X
X   if (RexxSysBase)
X      return(1) ;
X   return((RexxSysBase = OpenLibrary(RXSNAME, 0L)) != NULL) ;
X}
X/*
X *   This is the general ARexx command interface, but is not the one
X *   you will use most of the time; ones defined later are easier to
X *   understand and use.  But they all go through here.
X */
Xstruct RexxMsg *sendRexxCmd(s, f, p1, p2, p3)
Xchar *s ;
X/*
X *   The first parameter is the command to send to Rexx.
X */
Xint (*f)() ;
X/*
X *   The second parameter is either NULL, indicating that the command
X *   should execute asynchronously, or a function to be called when the
X *   message we build up and send out here finally returns.  Please note
X *   that the function supplied here could be called during cleanup after
X *   a fatal error, so make sure it is `safe'.  This function always is
X *   passed one argument, the RexxMsg that is being replied.
X */
XSTRPTR p1, p2, p3 ;
X/*
X *   These are up to three arguments to be stuffed into the RexxMsg we
X *   are building up, making the values available when the message is
X *   finally replied to.  The values are stuffed into Args[2]..Args[4].
X */
X{
X   struct RexxMsg *CreateRexxMsg() ;
X   STRPTR CreateArgstring() ;
X   register struct MsgPort *rexxport ;
X   register struct RexxMsg *RexxMsg ;
X
X/*
X *   If we have too many replies out there, we just return failure.
X *   Note that you should check the return code to make sure your
X *   message got out!  Then, we forbid, and make sure that:
X *      - we have a rexx port open
X *      - Rexx is out there
X *      - the library is open
X *      - we can create a message
X *      - we can create an argstring
X *
X *   If all of these succeed, we stuff a few values and send the
X *   message, permit, and return.
X */
X   if (rexxPort == NULL || stillNeedReplies > MAXRXOUTSTANDING-1)
X      return(NULL) ;
X   RexxMsg = NULL ;
X   if (openRexxLib() && (RexxMsg =
X             CreateRexxMsg(rexxPort, extension, rexxPort->mp_Node.ln_Name)) &&
X             (RexxMsg->rm_Args[0] = CreateArgstring(s, (long)strlen(s)))) {
X      RexxMsg->rm_Action = RXCOMM ;
X      RexxMsg->rm_Args[1] = (STRPTR)f ;
X      RexxMsg->rm_Args[2] = p1 ;
X      RexxMsg->rm_Args[3] = p2 ;
X      RexxMsg->rm_Args[4] = p3 ;
X      RexxMsg->rm_Node.mn_Node.ln_Name = RXSDIR ;
X      Forbid() ;
X      if (rexxport = FindPort(RXSDIR))
X         PutMsg(rexxport, RexxMsg) ;
X      Permit() ;
X      if (rexxport) {
X         stillNeedReplies++ ;
X         return(RexxMsg) ;
X      } else
X         DeleteArgstring(RexxMsg->rm_Args[0]) ;
X   }
X   if (RexxMsg)
X      DeleteRexxMsg(RexxMsg) ;
X   closeRexxLib() ;
X   return(NULL) ;
X}
X/*
X *   This function is used to send out an ARexx message and return
X *   immediately.  Its single parameter is the command to send.
X */
Xstruct RexxMsg *asyncRexxCmd(s)
Xchar *s ;
X{
X   return(sendRexxCmd(s, NULL, NULL, NULL, NULL)) ;
X}
X/*
X *   This function sets things up to reply to the message that caused
X *   it when we get a reply to the message we are sending out here.
X *   But first the function we pass in, which actually handles the reply.
X *   Note how we get the message from the Args[2]; Args[0] is the command,
X *   Args[1] is this function, and Args[2]..Args[4] are any parameters
X *   passed to sendRexxCmd() as p1..p3.  We pass the result codes right
X *   along.
X */
Xstatic void replytoit(msg)
Xregister struct RexxMsg *msg ;
X{
X   register struct RexxMsg *omsg ;
X
X   omsg = (struct RexxMsg *)(msg->rm_Args[2]) ;
X   replyRexxCmd(omsg, msg->rm_Result1, msg->rm_Result2, NULL) ;
X   ReplyMsg(omsg) ;
X}
X/*
X *   This function makes use of everything we've put together so far,
X *   and functions as a synchronous Rexx call; as soon as the macro
X *   invoked here returns, we reply to `msg', passing the return codes
X *   back.
X */
Xstruct RexxMsg *syncRexxCmd(s, msg)
Xchar *s ;
Xstruct RexxMsg *msg ;
X{
X   return(sendRexxCmd(s, (APTR)&replytoit, msg, NULL, NULL)) ;
X}
X/*
X *   There are times when you want to pass back return codes or a
X *   return string; call this function when you want to do that,
X *   and return `1' from the user dispatch function so the main
X *   event loop doesn't reply (because we reply here.)  This function
X *   always returns 1.
X */
Xvoid replyRexxCmd(msg, primary, secondary, string)
X/*
X *   The first parameter is the message we are replying to.
X */
Xregister struct RexxMsg *msg ;
X/*
X *   The next two parameters are the primary and secondary return
X *   codes.
X */
Xregister long primary, secondary ;
X/*
X *   The final parameter is a return string.  This string is only
X *   returned if the primary return code is 0, and a string was
X *   requested.
X *
X *   We also note that we have replied to the message that came in.
X */
Xregister char *string ;
X{
X   STRPTR CreateArgstring() ;
X
X/*
X *   Note how we make sure the Rexx Library is open before calling
X *   CreateArgstring . . . and we close it down at the end, if possible.
X */
X   if (primary == 0 && (msg->rm_Action & (1L << RXFB_RESULT))) {
X      if (string && openRexxLib())
X         secondary = (long)CreateArgstring(string, (long)strlen(string)) ;
X      else
X         secondary = 0L ;
X   }
X   msg->rm_Result1 = primary ;
X   msg->rm_Result2 = secondary ;
X   closeRexxLib() ;
X}
END_OF_minrexx.c
if test 15445 -ne `wc -c <minrexx.c`; then
    echo shar: \"minrexx.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 2 \(of 5\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 5 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 5 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
# Mark R. Rinfret            mrr@amanpt1.Newport.RI.US
# HyperView Systems Corp.    Hypermedia Solutions for Documentation/Training
# 28 Jacome Way              Work: 401-849-9390 x301
# Middletown, RI 02840       Home: 401-846-7639