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