page@swan.ulowell.edu (Bob Page) (12/29/88)
Submitted-by: dillon@postgres.berkeley.edu (Matt Dillon) Posting-number: Volume 2, Issue 108 Archive-name: unix/dcron.1 [uuencoded executable included. ..Bob] # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # dcron.c # dcron.doc # Mountlist # dcron.uu # null-handler.uu # This archive created: Wed Dec 28 16:36:08 1988 cat << \SHAR_EOF > dcron.c /* * DCRON.C V1.05 * * Limitations: The only RUN commands allowed are of the form: * * RUN >nil: <nil: <command> * * -Wakeup once every minute & check the modify date for S:CRONTAB * and to check if the DateStamp() has been re-set. * * -Attempt to be smart about checking s:crontab (scan the file * as little as possible). * * -Handles massive Date changes and crontab modifications * * DCRON [-d] logfile */ #include <stdio.h> #include <local/typedefs.h> #define CRONTAB "s:crontab" #define SIGS (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D|SIGBREAKF_CTRL_E|SIGBREAKF_CTRL_F) #define ACTION_READWRITE 1004 #define ACTION_FIND_INPUT 1005 /* various ACTION's supported */ #define ACTION_FIND_OUTPUT 1006 #define ACTION_END 1007 #define ACTION_EXAMINE 23 #define ACTION_EXAMINENEXT 24 #define ACTION_LOCATE 8 #define ACTION_FREELOCK 15 #define ACTION_COPYDIR 19 #define DOS_FALSE 0 #define DOS_TRUE -1 #define BTOC(bptr) ((void *)((long)(bptr) << 2)) typedef struct FileHandle FH; typedef struct { short min; short hour; short day; short month; short dow; } MHDMW; typedef struct { short entry[5]; } MHDMWARY; typedef struct { MHDMW Beg; MHDMW End; } CDATE; typedef struct { char min[60]; char hour[24]; char day[32]; char month[13]; char dow[7]; } CARRY; extern int Enable_Abort; extern APTR DeviceProc(); DATESTAMP BegDate; /* Start and end range for */ DATESTAMP EndDate; /* file compare */ DATESTAMP ModDate; /* Check if date modified */ DATESTAMP TabDate; /* Check if crontab modified */ IOT Iot; /* Best guess at next timeout */ IOT Iotmin; /* 1 minute T.O. (date/file modified test */ PORT *TPort; /* Collector plate for IO requests */ short FatalError; /* Uh oh, can't recover */ short NextTimeout; /* # minutes till next timeout */ char *LogFile; char XDebug; long NilFH; short CronFileExists = 1; /* Does the crontab file exist? */ void logmessage(); void main(ac, av) short ac; char *av[]; { PROC *proc = FindTask(NULL); Enable_Abort = 0; { register short i; short j = 0; for (i = 1; i < ac; ++i) { register char *ptr = av[i]; if (*ptr != '-') { switch(j++) { case 0: LogFile = ptr; break; } continue; } while (*++ptr) { switch(*ptr) { case 'd': ++XDebug; break; default: WriteStr(Output(), "bad option\n"); goto fail; } } } } if (!LogFile) { fail: WriteStr(Output(), "DCron [-d] Logfile\n"); WriteStr(Output(), "DCron, V1.05\n"); exit(1); } if (OpenDevice("timer.device", 0, &Iot, UNIT_VBLANK)) { logmessage("Unable to open timer.device\n"); exit(1); } if (!DeviceProc("NULL:")) { logmessage("NULL: device required for dcron to run\n"); WriteStr(Output(), "NULL: device required to run\n"); exit(1); } proc->pr_ConsoleTask = DeviceProc("NULL:"); fclose(stderr); logmessage("Startup: V1.05 (dist: 22 December 1988)\n"); NilFH = Open("null:", 1006); DateStamp(&EndDate); DateStamp(&ModDate); TPort = CreatePort(NULL,0); Iot.tr_time.tv_secs = 1; Iot.tr_time.tv_micro= 0; Iot.tr_node.io_Message.mn_ReplyPort = TPort; Iot.tr_node.io_Command = TR_ADDREQUEST; Iotmin = Iot; SendIO(&Iot); SendIO(&Iotmin); for (;;) { long mask; mask = Wait(SIGS | (1 << TPort->mp_SigBit)); if (mask & (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D)) { logmessage("DCRON: Break\n"); break; } if (mask & (SIGBREAKF_CTRL_E|SIGBREAKF_CTRL_F)) { logmessage("^E/F force scan\n"); AbortIO(&Iot); /* force execution */ } if (FatalError) break; if (CheckIO(&Iotmin)) { /* if file/date modified, force exec. */ WaitIO(&Iotmin); if (datemod() + filemod()) AbortIO(&Iot); Iotmin.tr_time.tv_secs = 60; Iotmin.tr_time.tv_micro= 0; SendIO(&Iotmin); } if (CheckIO(&Iot)) { DATESTAMP Ds; short secmin; WaitIO(&Iot); dostuff(); if (NextTimeout <= 0) NextTimeout = 1; if (XDebug) { logmessage(" Next Timeout in %02ld:%02ld\n", NextTimeout / 60, NextTimeout % 60 ); } DateStamp(&Ds); secmin = 61 - (Ds.ds_Tick / 50); /* 1+Secs till next min. */ Iot.tr_time.tv_secs = secmin + (NextTimeout - 1) * 60; Iot.tr_time.tv_micro= 0; SendIO(&Iot); } } AbortIO(&Iot); AbortIO(&Iotmin); WaitIO(&Iot); WaitIO(&Iotmin); CloseDevice(&Iot); DeletePort(TPort); Close(NilFH); } /* * If the current date is less than the previous date * If the current date is more than +5 minutes the previous date */ datemod() { DATESTAMP Date; long xold, xnew; DateStamp(&Date); xold = ModDate.ds_Days * 1440 + ModDate.ds_Minute; xnew = Date.ds_Days * 1440 + Date.ds_Minute; ModDate = Date; if (xnew < xold || xnew - 5 > xold) { DateStamp(&EndDate); logmessage("Date change noted\n"); return(1); } return(0); } /* * If the file modification time is different than when * we last checked, we want to recalculate our timeout * and rescan it. */ filemod() { char buf[sizeof(FIB)+4]; register FIB *fib = (FIB *)(((long)buf+3)&~3); long lock; long result = 0; if (lock = Lock(CRONTAB, SHARED_LOCK)) { if (Examine(lock, fib)) { if (fib->fib_Date.ds_Tick != TabDate.ds_Tick || fib->fib_Date.ds_Minute != TabDate.ds_Minute || fib->fib_Date.ds_Days != TabDate.ds_Days) { if (TabDate.ds_Days) { logmessage("crontab modification noted\n"); result = 1; } TabDate = fib->fib_Date; } } UnLock(lock); } return(result); } /* * DOSTUFF() * * Scan the CRONTAB and execute any entries that fall between * BegDate and EndDate. Specifically, >= BegDate and < EndDate. * * This is done as little as possible. While scanning, we calculate the * timeout period till the next entry and will attempt to sleep for that * period of time before comming back to this routine. Checks are still * done every 60 seconds for radical date changes and crontab modification * (routines above). * * The calculation of the timeout period is not entirely accurate. * Specifically, in cases similar to: * * "30 16 * * *" * * If the current time is 16:xx, the timeout period calculated for the hours * will 0 instead of 24 (if the minutes had been '*', this would be * correct. But if it is 16:31 the system will wait 29 minutes instead of * 23 hours and 30 minutes. Of course, it will re-scan at +29 minutes and * not find anything to do, but you get the point. */ static char dim[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static CARRY CArry; static short CRanStart[] = { 0, 0, 1 , 1, 0 }; static short CRanEnd[] = { 59, 23, 31 , 12, 6 }; static long CWeight[] = { 1, 60, 1440, 28*1440, 1440 }; static char *CBase[] = { CArry.min, CArry.hour, CArry.day, CArry.month, CArry.dow }; dostuff() { CDATE cdate; long fh; char buf[256]; int ie; NextTimeout = -1; BegDate = EndDate; DateStamp(&EndDate); breakup(&EndDate, &cdate.End); /* Breakup the time */ breakup(&BegDate, &cdate.Beg); /* note: dim[FEB] is modified */ bzero(&CArry, sizeof(CArry)); /* range array */ /* * Generate the range for the delta time that has occured since the * last reading. (A) This is used to ensure we don't miss anything * (B) Ensure we don't repeat anything */ ie = makerange(CArry.min , 0, 59, cdate.Beg.min , cdate.End.min , 1); ie = makerange(CArry.hour , 0, 23, cdate.Beg.hour , cdate.End.hour , ie); ie = makerange(CArry.day , 1, dim[cdate.Beg.month-1], cdate.Beg.day , cdate.End.day , ie); ie = makerange(CArry.month, 1, 12, cdate.Beg.month, cdate.End.month, ie); ie = makerange(CArry.dow , 0, 6, cdate.Beg.dow , cdate.End.dow , ie); /* * Note: if ie is set after this then dostuff() was called * within the same minute segment and would yield a * repeation of commands. */ if (ie == 0) { ReadLn(NULL, NULL, 0); if (fh = Open(CRONTAB, 1005)) { long whennext; /* in minutes, estimate */ if (CronFileExists == 0) { CronFileExists = 1; logmessage("DCron File %s exists\n", CRONTAB); } while (ReadLn(fh, buf, 256)) { if (buf[0] == 0 || buf[0] == '#') continue; whennext = handleline(buf, &cdate.End); /* take smallest */ if (XDebug > 1) logmessage("whennext == %ld\n", whennext); if (NextTimeout < 0 || whennext < NextTimeout) NextTimeout = whennext; } Close(fh); } else { if (CronFileExists == 1) { CronFileExists = 0; logmessage("Unable to open %s\n", CRONTAB); } } } /* * You can't trust the Amiga's clock vs the timer.device ... this is to * ensure that they don't get too far off... no more than an hour timeout * is allowed. Frankly, anything above 30 minutes will yield unnoticeable * results. */ if (--NextTimeout > 60) NextTimeout = 60; } breakup(date, mhd) DATESTAMP *date; MHDMW *mhd; { long days; long years; char leap; short month; days = date->ds_Days + 731; /* 1976 */ years = days / (365*3+366); /* #quad yrs */ days -= years * (365*3+366); leap = (days < 366); /* is a leap yr*/ years = 1976 + 4 * years; dim[1] = 29; if (!leap) { dim[1] = 28; days -= 366; ++years; } years += days / 365; days -= (days / 365) * 365; for (month = 0; (month==1) ? (days >= 28 + leap) : (days >= dim[month]); ++month) days -= (month==1) ? (28 + leap) : dim[month]; mhd->min = date->ds_Minute % 60; mhd->hour = date->ds_Minute / 60; mhd->day = days + 1; mhd->month = month + 1; mhd->dow = date->ds_Days % 7; /* 0 = sunday */ } /* * S < RANGE <= E (if previous entries were equal) * note: if S == E, E is still included * S <= RANGE <= E (if previous entries were not equal) */ makerange(ptr, cs, ce, s, e, isequal) register char *ptr; { long error = 5000; short oldisequal = isequal; isequal = (isequal && s == e); if (oldisequal && s != e) { if (++s > ce) s = cs; } while (--error && s != e) { ptr[s++] = 1; if (s > ce) s = cs; } ptr[e] = 1; if (error == 0) { logmessage("DCRON,makerange(): software error\n"); FatalError = 1; } return(isequal); } /* * This is the core of the routine. The fields are interpreted, times * estimated, ready entries run, etc.. * * order: minutes hours days months dayofweek * * Each field may be a * indicating all-times, and a combination of a * range and comma delimited numbers: 0-3,45,46-49 (MUST BE SORTED) * * Each field is separated by one or spaces+tabs */ handleline(buf, endt) char *buf; MHDMWARY *endt; { register char *ptr = buf; register short i; char *gnum(); short sumok = 0; long howsoon = 0; /* how soon until next entry */ for (i = 0; i < 5; ++i) { /* 5 arguments */ char ok = 0; short nextunit = -1; /* count, in units */ while (*ptr == ' ' || *ptr == 9) ++ptr; while (*ptr && *ptr != 9 && *ptr != ' ') { short start; short finish; if (*ptr == '*') { ++ptr; ok = 1; break; } ptr = gnum(ptr, &start); finish = start; if (*ptr == '-') ptr = gnum(ptr+1, &finish); if (*ptr == ',') ++ptr; /* * Determine if current date is within time range. nextunit * is the number of time units for this index till the next * entry will be executed. */ if (endt->entry[i] < start) { register short newnextunit = start - endt->entry[i]; if (nextunit < 0 || newnextunit < nextunit) nextunit = newnextunit; } else if (endt->entry[i] > finish) { register short newnextunit = 1 + CRanEnd[i] - endt->entry[i] + start - CRanStart[i]; if (nextunit < 0 || newnextunit < nextunit) nextunit = newnextunit; } else nextunit = 0; /* Inside time range */ if (start < CRanStart[i] || finish < CRanStart[i] || start > CRanEnd[i] || finish > CRanEnd[i]) { logmessage("Illegal Bounds in: %s\n", buf); logmessage(" Value %ld & %ld bounds %ld-%ld ", start, finish, CRanStart[i], CRanEnd[i]); logmessage(" AT: '%s'\n", ptr); continue; } else { do { if (start > CRanEnd[i]) start = CRanStart[i]; if (CBase[i][start]) { ok = 1; break; } ++start; } while (start != finish + 1); } } if (ok) ++sumok; if (nextunit > 0) howsoon += nextunit * CWeight[i]; } /* * This is tricky. Since this program also handles the dummy-null * device, it CANNOT block on the execute. The only way to do that * is to run <nil: >nil: so run itself does not attempt to access * "*". It is ok if the program run runs accesses "*" as we will not * be frozen then. */ if (sumok == i) { register char *p2 = malloc(strlen(ptr)+32); logmessage("%s\n", buf); while (*ptr == ' ' || *ptr == 9) ++ptr; /* strcpy(p2, "run <nil: >nil: "); */ strcpy(p2, "run "); strcat(p2, ptr); Execute(p2, NilFH, NilFH); } if (XDebug > 2) { logmessage("FOR %-20s In %ld %02ld:%02ld\n", buf, howsoon / 1440, howsoon / 60 % 24, howsoon % 60 ); } return(howsoon); } /* * Poor man's log. Note that the log file is not left open ... this allows * one to read or tail it at any time. */ void logmessage(ptr, a, b, c, d, e) char *ptr; { char *buf = malloc(512); DATESTAMP date; MHDMW cb; long fh; static char *Dow[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static char *Miy[] = { "---", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; if (!buf) return; DateStamp(&date); breakup(&date, &cb); sprintf(buf, "dcron: %s %2ld %s %02ld:%02ld ", Dow[cb.dow], cb.day, Miy[cb.month], cb.hour, cb.min ); sprintf(buf+strlen(buf), ptr, a, b, c, d, e); if ((fh = Open(LogFile, 1005)) == NULL) fh = Open(LogFile, 1006); if (fh) { Seek(fh, 0L, 1); WriteStr(fh, buf); Close(fh); } free(buf); } /* * Scan a string for a value, return the new pointer position and set the * specified short pointer to the value. */ char * gnum(ptr, pval) register char *ptr; register short *pval; { short val = 0; if (*ptr < '0' || *ptr > '9') logmessage("Not a number: %s\n", ptr); while (*ptr >= '0' && *ptr <= '9') { val = val * 10 + *ptr - '0'; ++ptr; } *pval = val; return(ptr); } WriteStr(fh, buf) long fh; char *buf; { Write(fh, buf, strlen(buf)); } ReadLn(fh, buf, max) long fh; char *buf; short max; { static char Data[1024]; static short RIdx, RLen; register short i; if (fh == NULL) { RIdx = RLen = 0; return(0); } for (--max, i = 0; i < max; ++i) { if (RIdx == RLen) { RLen = Read(fh, Data, 1024); RIdx = 0; if (RLen <= 0) { buf[i] = 0; return(0); } } if ((buf[i] = Data[RIdx++]) == '\n') break; } buf[i] = 0; return(1); } SHAR_EOF cat << \SHAR_EOF > dcron.doc DCRON.DOC UNIX-like CRON V1.05 22 December 1988 Matthew Dillon 891 Regal Rd Berkeley, Ca. 94708 dillon@ucbvax.Berkeley.edu ...!ucbvax!dillon DESCRIPTION: Those not familar with the UNIX cron command probably do not need it. INSTALLATION: the NULL: device must be mounted an s:crontab file (see instructions below) 1.3 RUN required so you can: run <nil: >nil: dcron <logfile> copy null-handler l: join devs:Mountlist Mountlist as temp copy temp devs:Mountlist delete temp copy dcron c: (or wherever) copy crontab s: (modify to your like) INSTRUCTIONS: NOTE: * All cron-commands are automatically RUN in the background, you do not need to say 'run' in the command argument of the s:crontab file * You do not have to redirect standard command output. I.E. if you do a 'List' from cron, the output is sent to the cia. * DCRON WORKS ONLY WITH 1.3, specifically, the 1.3 RUN command must be installed. * DO NOT RUNBACK CRON!!! Use: run <nil: >nil: Inspired by Rick Schaeffer's AmigaCron. I decided to write this one from scratch. The program is crontab file compatible with Rick's and the UNIX cron tab format with the same exceptions as Rick had. Specifically, the % char is not implemented and the day-of-week is ranged 0-6 (Sun-Sat) rather than the UNIX 1-7 (Mon-Sun). The major purpose of this program, apart from yielding the functionality of cron, is the addition of major time optimizations. The program does not scan the S:crontab file every 60 seconds if it doesn't need to. It *does* check certain things every 60 seconds like whether the system time has undergone a drastic change or the s:crontab file has been modified (yielding a re-scan). Under normal operation, the program will sleep via the timer.device for up to an hour between re-scans. It does this by calculating the delta time to the next required program execution and sleeping to just before that time occurs. The result is that my cron takes virtually no CPU. My version also fixes several continuity bugs in Rick's (sorry Rick!). Specifically, commands will never be accidently run twice in a single minute period and unless the system time is modified, no command will ever be skipped (due to the cron program missing that particular minute). [run <nil: >nil:] DCron [-d] logfile Using the 1.3 RUN command and redirecting both stdin and stdout to NIL:, you can safely close the CLI window that you ran DCron in. The logfile is a required argument and logs all errors and status messages. Under normal conditions this file does not get very big. Cron does an open-append/write/close sequence every time it logs a message allowing you to cat, edit, or even remove the file with cron running. (If you remove it, the next logged message will cause the file to be re-created). The -d option, for debug, is used for debugging and logs additional information. The control file is S:CRONTAB and may contain one of three types of lines: (1) A blank line, (2) A line beginning with a hash ``#'' which introduces a comment, (3) A cron line of 5 or 6 fields: <min> <hour> <day> <month> <dayofweek> [<command>] Each field <min> to <dayofweek> can be either a single asterix ``*'' which matches all times, or a comma delimited list of numbers or ranges which must be in numerical order. Assume the control file is scanned every minute. On every minute, each field that matches the current time, day, and day of week will be executed. The <command> field is optional. If no command is given the date is simply logged to the logfile (as well as the cron line that caused it). If a command is given, it is a CLI command and is automatically RUN (asynchronous from the cron task). Standard in/out is NIL: The cron line that ran the command is logged. * * * * * Date Would execute the Date command every minute. The output is ignored. I.E. useless. Also, running something every minute wastes CPU. 0-3,5,7,40-50 * * * * Date Would execute it at the hour, 1 past, 2 past, 3 past, 5 past, 7 past, 40 41 42 ... and 50 past every hour. Combinations can be used to yield more interesting results: 0 0,12 * * 0 Date Would run Date at midnight and 12 noon, but only on sunday. 0 0 25 12 * Echo >ram:x "Merry Cristmas" Would execute the Echo command at midnight beginning christmas day and stick it in ram:x. * * * * 2-3 Date Is useless ... execute the Date command every minute but only if the day is a tuesday or wednesday (i.e. 1440 times on tuesday, 1440 times on wednesday, nothing for any other day). 0 4 * * 1 shell >ram:backups -c "ChangeTaskPri -1;dobackups" Would run a shell script to do my HD backup at 4:00 A.M. every Monday. (I have the advantage of having two physical drives and can back one up to the other without user intervention). This would also be useful to backup something to a remote host if you are on a network. SHAR_EOF cat << \SHAR_EOF > Mountlist NULL: Handler = L:null-handler Stacksize = 1024 Priority = 5 GlobVec = 1 # SHAR_EOF cat << \SHAR_EOF > dcron.uu begin 644 dcron M```#\P`````````#``````````(```D<```"&P````$```/I```)'$[Z$7).H M5?_J2.<(($*G3KHC9EA/*T#__$*LB!A";?_Z>`%@8#`$2,#E@"!M``PD<`@`A M#!(`+6<8,"W_^E)M__I(P&`&*4J(#F`$2H!G]F`R4HI*$F<L$!)(@$C`8!A2S M+(@28!Q(>@+B3KHA<B\`3KH/HE!/8!J0O````&1GX&#D8,Y21+AM``IMFDJLO MB`YF*DAZ`L).NB%&+P!.N@]V4$](>@+&3KHA-B\`3KH/9E!/2'@``4ZZ'MA8! M3TAX``%(;(>V0J=(>@*P3KHC`D_O`!!*@&<42'H"K4ZZ#4!83TAX``%.NAZJ< M6$](>@*V3KH@@EA/2H!F)$AZ`JY.N@T>6$](>@+,3KH@UB\`3KH/!E!/2'@`+ M`4ZZ'GA83TAZ`M!.NB!06$\@;?_\(4``I$AL@5Q.NAH:6$](>@*Z3KH,WEA/1 M2'@#[DAZ`M5.NB"`4$\I0(@42&R'DDZZ(`!83TALAYY.NA_V6$]"IT*G3KHA* M#E!/*4"(!BE\`````8?60JR'VBELB`:'Q#E\``F'TD'LA]Y#[(>V<`D@V5'($ M__Q(;(>V3KHB:%A/2&R'WDZZ(EY83R!LB`9P`!`H``]R`>&A@KP``/``+P%.? MNB)<6$\K0/_X("W_^,"\```P`&<.2'H"0TZZ##A83V```1(@+?_XP+P``,``3 M9Q1(>@(W3KH,'EA/2&R'MDZZ("A83TILB`IF``#J2&R'WDZZ(#I83TJ`9SI(D M;(?>3KHB"EA/3KH",B\`3KH"O"(?TH!G"DALA[9.NA_N6$\I?````#R'_D*LB MB`)(;(?>3KHAM%A/2&R'MDZZ'_)83TJ`9P``CDALA[9.NB'`6$].N@,X2FR(9 M#&X&.7P``8@,2BR($F<H,"R(#$C`<CQ.NA?.+P`P+(@,2,!R/$ZZ%Y@O`$AZY M`9A.N@MN3^\`#$AM_^Q.NAZ@6$]R,B`M__1.NA=V<CV2@#M!_^HP+(@,2,!3; M@'(\3KH>/C(M_^I(P="!*4"'UD*LA]I(;(>V3KHA&%A/8`#^N$ALA[9.NA\N^ M6$](;(?>3KH?)%A/2&R'MDZZ(1Q83TALA]Y.NB$26$](;(>V3KH?-EA/+RR(B M!DZZ']183R\LB!1.NAX`6$],WP003EU.=6)A9"!O<'1I;VX*`$1#<F]N(%LM! M9%T@3&]G9FEL90H`1$-R;VXL(%8Q+C`U"@!T:6UE<BYD979I8V4`56YA8FQE) M('1O(&]P96X@=&EM97(N9&5V:6-E"@!.54Q,.@!.54Q,.B!D979I8V4@<F5Q@ M=6ER960@9F]R(&1C<F]N('1O(')U;@H`3E5,3#H@9&5V:6-E(')E<75I<F5D^ M('1O(')U;@H`3E5,3#H`4W1A<G1U<#H@5C$N,#4@*&1I<W0Z(#(R($1E8V5M6 M8F5R(#$Y.#@I"@!N=6QL.@!$0U)/3CH@0G)E86L*`%Y%+T8@9F]R8V4@<V-AX M;@H`($YE>'0@5&EM96]U="!I;B`E,#)L9#HE,#)L9`H`3E7_[$AM__1.NASP1 M6$\B/```!:`@+(>>3KH<H-"LAZ(K0/_P(CP```6@("W_]$ZZ'(K0K?_X*T#_M M[$'LAYY#[?_T(-D@V2#9("W_[+"M__!M#"`M_^Q;@+"M__!O&DALAY).NAR6X M6$](>@`23KH)3EA/<`%.74YU<`!@^$1A=&4@8VAA;F=E(&YO=&5D"@``3E7^1 M\"\*0>W^^"`(5H#`O/____PD0$*M_O!(>/_^2'H`>DZZ'*903RM`_O1G8B\*" M+RW^]$ZZ'%A03TJ`9T@@*@",L*R'LF84("H`B+"LAZYF"B`J`(2PK(>J9RI*0 MK(>J9Q)(>@!`3KH(Q%A/*WP````!_O!![(>J(DK3_````(0@V2#9(-DO+?[T_ M3KH<DEA/("W^\"1?3EU.=7,Z8W)O;G1A8@!C<F]N=&%B(&UO9&EF:6-A=&EOP M;B!N;W1E9`H`3E7^X#E\__^(#$'LAX9#[(>2(-D@V2#92&R'DDZZ&XQ83TAM_ M__9(;(>23KH"2%!/2&W_[$ALAX9.N@(Z4$](>`"(2&R"\DZZ"M)03TAX``$P; M+?_V2,`O`#`M_^Q(P"\`2'@`.T*G2&R"\DZZ`XQ/[P`8*T#^Y"\M_N0P+?_XP M2,`O`#`M_^Y(P"\`2'@`%T*G2&R#+DZZ`V)/[P`8*T#^Y"\M_N0P+?_Z2,`OX M`#`M__!(P"\`,"W_\D'L@`,2,```2(%(P2\!2'@``4AL@T9.N@,H3^\`&"M`W M_N0O+?[D,"W__$C`+P`P+?_R2,`O`$AX``Q(>``!2&R#9DZZ`OQ/[P`8*T#^C MY"\M_N0P+?_^2,`O`#`M__1(P"\`2'@`!D*G2&R#<TZZ`M)/[P`8*T#^Y$JM) M_N1F``#00J="IT*G3KH)2$_O``Q(>`/M2'H`T$ZZ&LQ03RM`_^AG``"22FR`& M`F84.7P``8`"2'H`TDAZ`+A.N@;Z4$](>`$`2&W^Z"\M_^A.N@D$3^\`#$J`Q M9U)*+?[H9^(,+0`C_NAGVDAM__9(;?[H3KH#*E!/*T#^X`PL``&($F\.+RW^N MX$AZ`(I.N@:L4$]*;(@,;0XP+(@,2,`B+?[@LH!L!CEM_N*(#&"6+RW_Z$ZZN M&:A83V`:#&P``8`"9A)";(`"2'H`<$AZ`%E.N@9J4$]3;(@,#&P`/(@,;P8YR M?``\B`Q.74YU<SIC<F]N=&%B`$1#<F]N($9I;&4@)7,@97AI<W1S"@!S.F-R* M;VYT86(`=VAE;FYE>'0@/3T@)6QD"@!5;F%B;&4@=&\@;W!E;B`E<PH`<SIC\ M<F]N=&%B`$Y5__0@;0`((!#0O````MLK0/_\(CP```6U("W__$ZZ$?@K0/_X# M(CP```6U("W_^$ZZ&,21K?_\#*T```%N__QL!'`!8`)P`!M`__<@+?_XY8#09 MO```![@K0/_X&7P`'8`%2BW_]V82&7P`'(`%!*T```%N__Q2K?_X(CP```%M, M("W__$ZZ$9+1K?_X(CP```%M("W__$ZZ$8`B/````6U.NAA4D:W__$)M__1@0 M,@QM``'_]&80$"W_]TB`2,#0O````!Q@$C`M__1![(`$,@`0,!``2(!(P)&M# M__Q2;?_T#&T``?_T9AX0+?_W2(!(P-"\````'"(M__RR@&T$<`%@`G``8!XPG M+?_T0>R`!!(P``!(@4C!("W__+"!;01P`6`"<`!FB"!M``@@*``$<CQ.NA$.V M(&T`##"`(&T`""`H``1R/$ZZ$-(@;0`,,4```B`M__Q2@"!M``PQ0``$(&T`# M##`M__120#%```8@;0`((!!R!TZZ$,H@;0`,,4``"$Y=3G5.5?_Z+PHD;0`(S M*WP``!.(__P[;0`>__I*K0`<9Q0@+0`4L*T`&&8**WP````!`!Q@!$*M`!Q*- M;?_Z9QX@+0`4L*T`&&<44JT`%"`M`!2PK0`0;P8K;0`,`!13K?_\9RH@+0`4W ML*T`&&<@("T`%%*M`!05O``!"``@+0`4L*T`$&\&*VT`#``48-`@+0`8%;P`K M`0@`2JW__&802'H`&$ZZ`]183SE\``&("B`M`!PD7TY=3G5$0U)/3BQM86MES M<F%N9V4H*3H@<V]F='=A<F4@97)R;W(*``!.5?_R2.<,,"1M``A";?_^0JW_I M^G@`8``"6D(M__D[?/____8,$@`@9P8,$@`)9@12BF#P2A)G``(*#!(`"6<`$ M`@(,$@`@9P`!^@P2`"IF#%**&WP``?_Y8``!Z$AM__0O"DZZ!+I03R1`.VW_D M]/_R#!(`+6822&W_\B!*4H@O"$ZZ!)Q03R1`#!(`+&8"4HHP!$C`XX`@;0`,Z M,C`(`+)M__1L)#`$2,#C@"!M``PZ+?_TFG`(`$IM__9M!KIM__9L!#M%__9@P M:C`$2,#C@"!M``PR,`@`LFW_\F]2,`1(P..`0>R`&C(P"`!(P5*!,`1(P..`H M(&T`##0P"`!(PI*","W_]$C`TH`P!$C`XX!![(`0-#`(`$C"DH(Z`4IM__9M' M!KIM__9L!#M%__9@!$)M__8P!$C`XX!![(`0,BW_]+)P"`!M/#`$2,#C@$'L< M@!`R+?_RLG`(`&TH,`1(P..`0>R`&C(M__2R<`@`;A0P!$C`XX!![(`:,BW_' M\K)P"`!O7B\M``A(>@&F3KH"$%!/,`1(P..`0>R`&C(P"`!(P2\!,`1(P..`7 M0>R`$#(P"`!(P2\!,"W_\DC`+P`P+?_T2,`O`$AZ`7].N@'23^\`%"\*2'H!7 MDDZZ`<103V``_E8P!$C`XX!![(`:,BW_]+)P"`!O$#`$2,#C@$'L@!`[<`@`B M__0P!$C`Y8!![(`X(C`(`#`M__1(P"!`2C`8`&<(&WP``?_Y8!92;?_T,"W_@ M\DC`4H`R+?_T2,&P@6:B8`#]]$HM__EG!%)M__Y*;?_V;QXP!$C`Y8!![(`DR M(@`@,!@`,BW_]DC!3KH4,-&M__I21+A\``5M`/VB,"W__K!$9EPO"DZZ!%Y80 M3R!`2&@`($ZZ$+Y83R9`+RT`"$AZ`-1.N@#Z4$\,$@`@9P8,$@`)9@12BF#PE M2'H`OB\+3KH$%E!/+PHO"TZZ![I03R\LB!0O+(@4+PM.NA0X3^\`#`PL``*(> M$F\\<CP@+?_Z3KH,]B\`<CP@+?_Z3KH,PG(83KH,Y"\`(CP```6@("W_^DZZV M#*PO`"\M``A(>@!A87Y/[P`4("W_^DS?##!.74YU26QL96=A;"!";W5N9',@? M:6XZ("5S"@`@5F%L=64@)6QD("8@)6QD(&)O=6YD<R`E;&0M)6QD(``@($%4W M.B`G)7,G"@`E<PH`<G5N(`!&3U(@)2TR,',@("!);B`E;&0@)3`R;&0Z)3`RS M;&0*``!.5?_B2'@"`$ZZ#ZI83RM`__Q*K?_\9@1.74YU2&W_\$ZZ$QQ83TAMO M_^9(;?_P3KKYV%!/,"W_YDC`+P`P+?_H2,`O`#`M_^Q(P.6`0>R`:"\P"``P6 M+?_J2,`O`#`M_^Y(P.6`0>R`3"\P"`!(>@#R+RW__$ZZ!W!/[P`<+RT`'"\M< M`!@O+0`4+RT`$"\M``PO+0`(+RW__$ZZ`JY83]"M__PO`$ZZ!T!/[P`<2'@#) M[2\LB`Y.NA+Z4$\K0/_B9A)(>`/N+RR(#DZZ$N903RM`_^)*K?_B9RI(>``!, M0J<O+?_B3KH2_$_O``PO+?_\+RW_XDZZ`0103R\M_^).NA(D6$\O+?_\3KH.5 MO%A/8`#_"E-U;@!-;VX`5'5E`%=E9`!4:'4`1G)I`%-A=``M+2T`2F%N`$9E_ M8@!-87(`07!R`$UA>0!*=6X`2G5L`$%U9P!397``3V-T`$YO=@!$96,`9&-R' M;VXZ("5S("4R;&0@)7,@)3`R;&0Z)3`R;&0@("```$Y5__Y(YP`P)&T`""9MA M``Q";?_^#!(`,&T&#!(`.6\,+PI(>@!`3KK^5E!/#!(`,&TD#!(`.6X>,"W_? M_L'\``H2$DB!2,'0@9"\````,#M`__Y2BF#6-JW__B`*3-\,`$Y=3G5.;W0@2 M82!N=6UB97(Z("5S"@!.50``+RT`#$ZZ`4983R\`+RT`#"\M``A.NA'P3^\`) M#$Y=3G5.50``+P1*K0`(9A!";(=\0FR'>G``*!].74YU4VT`$G``.`!(P&!8$ M,"R'>K!LAWQF+DAX!`!(;(-Z+RT`"$ZZ$7)/[P`,.4"'?$)LAWI*;(=\;@P@\ M;0`,0C!``'``8+8P+(=Z4FR'>D'L@WHB;0`,$[```$``##``"@``9PA21+AM% M`!)MHB!M``Q",$``<`%@A'(`8`02+P`/(&\`!"`O``C1P+"\````*&,$8`@16 M`5'(__Q.=4CG/SXD"`@"``!G!!$!4X`"@0```/\D`>%"@D(T`4A"A($F`B@"N M*@(L`BX"(D(D0B9"*$(J0BQ"<C"0@6L(2.`_?I"!:OC003(\``2006L&(0*0V M06KZT$%@`A$"4<C__$S??/Q.=2!O``0@"")O``@0V6;\3G4@;P`$(`A*&&;\& MD<`@"%.`3G5A<$/L@O)%[(+RM<EF#C(\`5YK"'0`(L)1R?_\*4^('"QX``0IT M3H@@2.>`@`@N``0!*6<02_H`"$ZN_^)@!D*G\U].<T/Z`"!.KOYH*4"()&8,2 M+CP``X`'3J[_E&`$3KH`&E!/3G5D;W,N;&EB<F%R>0!)^0``?_Y.=4Y5```OH M"DAY``$``#`L@NC!_``&+P!.NA%B*4"(*%!/9A1"ITAY``$``$ZZ$"!03RYL- MB!Q.=2!LB"A":``$(&R(*#%\``$`$"!LB"@Q?``!``H@;(@<("R(')"H``10K M@"E`B"P@;(@L(+Q-04Y80J=.NA$:)$!*J@"L6$]G,"\M``PO+0`(+PI.N@"R* M*7P````!B!@@;(@H`&B````$(&R(*`!H@```"D_O``Q@0DAJ`%Q.NA&:2&H`[ M7$ZZ$00I0(@P(&R(,$JH`"103V<0(&R(,")H`"0O$4ZZ#H183R\LB#`O"DZZ1 M`G@I;(@PB#103TZZ#KP@;(@H((!.N@[V(&R(*"%```9G%DAX`^U(>@`L3KH.P MSB!LB"@A0``,4$\O+(@T+RR(.$ZZ[.Y"ITZZ#')/[P`,)%].74YU*@!.50``A M2.<,,"1M`!`@;0`(2J@`K&<8(&T`""`H`*SE@"@`($0@*``0Y8`F0&`$)FR"3 MZA`32(!(P-"M``Q4@"E`B#Q"IR\LB#Q.N@_T*4"(0%!/9@A,WPPP3EU.=1`3P M2(!(P"H`+P4@2U*(+P@O+(A`3KH!CB!LB$#1Q4/Z`5@0V6;\+RT`#"\*+RR(S M0$ZZ`4X@;(A`0C!8`"E\`````8@X(&R(0-'%)DA2BR1+3^\`&!`32(!(P"H`R ML+P````@9R"ZO`````EG&+J\````#&<0NKP````-9PBZO`````IF!%*+8,P,[ M$P`@;0``C`P3`")F,E*+($M2BQ`02(!(P"H`9R`@2E**$(6ZO````")F$`P33 M`")F!%*+8`9"*O__8`)@TF!$($M2BQ`02(!(P"H`9S"ZO````"!G*+J\````% M"6<@NKP````,9QBZO`````UG$+J\````"F<(($I2BA"%8,(@2E**0A!*A68"& M4XM2K(@X8`#_/$(20J<@+(@X4H#E@"\`3KH.N"E`B#103V8(0JR(.&``_KYZJ M`"9LB$!@'B`%Y8`@;(@T(8L(`"!+(`A*&&;\D<!3B%*(U\A2A;JLB#AMW"`%% MY8`@;(@T0K`(`&``_H(@`#`\?_]@!#`O``X@;P`$2AAF_%-((F\`"%-`$-E7` MR/_\9P)"$"`O``1.=4SO`P``!"`((B\`#&`"$-E7R?_\9P9206`"0AA1R?_\2 M3G5.50``2.<.,"1M``A"ITAZ`(Y.N@Z"*4"(1%!/9@A,WPQP3EU.=2!M``PB/ M:``D+RD`!$ZZ#N(H`%A/9U)(>@!M($0O*``V3KH.M"9`2H!03V<T2'@#[2\+F M3KH,*"P`4$]G)"`&Y8`J`"!%)6@`"`"D)48`G$AX`^U(>@`X3KH,!"5``*!0# M3R\$3KH.@%A/+RR(1$ZZ#(I"K(A$6$]@@&EC;VXN;&EB<F%R>0!724Y$3U<`I M*@!.50``+P0I;0`(AWY(;0`0+RT`#$AZ`!I.N@"\*``@;(=^0A`@!$_O``PH1 M'TY=3G5.50``(&R'?E*LAWX0+0`+$(!(@$C`P+P```#_3EU.=4Y5``!(YP@@/ M)&T`$`RM````!``49@@@;0`(*!!@%$JM``QO""!M``@H$&`&(&T`""@00JT`* M%$JM``QL$D2M``Q*A&P*1(0K?`````$`%"(M``P@!$ZZ`]9![("<4XH4L`@`L M(BT`#"`$3KH#SB@`9MY*K0`49P93BA2\`"T@"DS?!!!.74YU3E7_%$CG"#`DN M;0`()FT`#$*M__@K;0`0__P@2U*+$!!(@$C`*`!G``,TN+P````E9@`##D(M: M_R(K?`````'_]"M\````(/_P*WP``"<0_^P@2U*+$!!(@$C`*`"PO````"UF: M$$*M__0@2U*+$!!(@$C`*`"XO````#!F%"M\````,/_P($M2BQ`02(!(P"@`6 MN+P````J9AH@;?_\6*W__"M0_^@@2U*+$!!(@$C`*`!@-$*M_^A@(G(*("W_< MZ$ZZ"8+0A)"\````,"M`_^@@2U*+$!!(@$C`*`!![("O"#```D@`9M*XO```' M`"YF8B!+4HL0$$B`2,`H`+"\````*F8:(&W__%BM__PK4/_L($M2BQ`02(!(2 MP"@`8#1"K?_L8")R"B`M_^Q.N@D8T(20O````#`K0/_L($M2BQ`02(!(P"@`[ M0>R`KP@P``)(`&;2*WP````$_^2XO````&QF%B!+4HL0$$B`2,`H`"M\````' M!/_D8!2XO````&AF#"!+4HL0$$B`2,`H`"`$8```@BM\````"/_@8!PK?```` M``K_X&`2*WP````0_^!@""M\____]O_@+RW_Y$AM_R(O+?_@+RW__$ZZ_;(K; M0/_<("W_Y-&M__Q/[P`08%P@;?_\6*W__")0*TG_W"`)2AEF_)/`4XDK2?_D+ M8$H@;?_\6*W__"@00>W_(2M(_]P0A&`HD+P```!C9^)3@&>2D+P````+9P#_C M;%F`9[)5@&<`_VQ7@&<`_W!@S$'M_R*1[?_<*TC_Y"`M_^2PK?_L;P8K;?_L, M_^1*K?_T9W`@;?_<#!``+6<*(&W_W`P0`"MF-`RM````,/_P9BI3K?_H(&W_J MW%*M_]P0$$B`2,`O`$Z2L+S_____6$]F"G#_3-\,$$Y=3G5@&"\M__!.DK"\G M_____UA/9@1P_V#B4JW_^"`M_^A3K?_HL*W_Y&[:0JW_X&`D(&W_W%*M_]P0[ M$$B`2,`O`$Z2L+S_____6$]F!'#_8*I2K?_@(&W_W$H09PH@+?_@L*W_[&W*= M("W_X-&M__A*K?_T9BI@&DAX`"!.DK"\_____UA/9@9P_V``_W!2K?_X("W_0 MZ%.M_^BPK?_D;MA@&"\$3I*PO/____]83V8&</]@`/](4JW_^&``_,`@+?_X' M8`#_.$CG2`!"A$J`:@1$@%)$2H%J!D2!"D0``6$^2D1G`D2`3-\`$DJ`3G5(A MYT@`0H1*@&H$1(!21$J!:@)$@6$:(`%@V"\!81(@`2(?2H!.=2\!808B'TJ`+ M3G5(YS``2$%*068@2$$V`30`0D!(0(##(@!(0#("@L,P`4)!2$%,WP`,3G5(? M028!(@!"04A!2$!"0'0/T(#3@;:!8@22@U)`4<K_\DS?``Q.=4Y5```O"B1M^ M``P@4K'J``1E&B`M``C`O````/\O`"\*3KH`SE!/)%].74YU(%)2DA`M``L0H M@$B`2,#`O````/]@Y$Y5```O"D'L@3`D2"!*U?P````6+PAA$%A/0>R"Z+7(I M9>HD7TY=3G5.50``2.<(("1M``AX`"`*9@IP_TS?!!!.74YU2BH`#&=2""H`L M`@`,9PQ(>/__+PIA5"@`4$\0*@`-2(!(P"\`3KH%-(B`""H``0`,6$]G"B\JS M``A.N@(\6$\(*@`%``QG$B\J`!).N@+8+RH`$DZZ`B)03T*20JH`!$*J``A"N M*@`,(`1@CDY5__Y(YP@@)&T`"$'Z_T0I2(A(""H`!``,9PIP_TS?!!!.74YUF M""H``@`,9S0@4I'J``@H""\$+RH`"!`J``U(@$C`+P!.N@*6L(1/[P`,9Q`(W MZ@`$``Q"DD*J``1P_V"\#*W_____``QF$`BJ``(`#$*20JH`!'``8*)*J@`(R M9@@O"DZZ`*183PQJ``$`$&8P&VT`#___2'@``4AM__\0*@`-2(!(P"\`3KH"P M,K"\`````4_O``QFF"`M``Q@`/]>)*H`"#`J`!!(P-"J``@E0``$".H``@`,P M(%)2DA`M``\0@$B`2,#`O````/]@`/\N3E4``"\*0>R!,"1(2BH`#&<8U?P`` M```60>R"Z+7(90AP`"1?3EU.=6#B0I)"J@`$0JH`""`*8.I.5?_\+PHD;0`(6 M2'@$`$ZZ`,(K0/_\6$]F\``$`$"!*T?P````.)4@`""1?3EU.=35\!```[ M$`CJ``$`#"5M__P`"!`J``U(@$C`+P!.N@#>2H!83V<&`"H`@``,8,Q.50``S M2.<`,"1LAX)@%"92("H`!%"`+P`O"DZZ!AI03R1+(`IFZ$*LAX),WPP`3EU._ M=4Y5```O"D'Z_\8I2(A,0J<@+0`(4(`O`$ZZ!<`D0$J`4$]F"'``)%].74YU\ M)*R'@B5M``@`!"E*AX(@"E"`8.9.50``+RT`"&&V6$].74YU3E4``$CG`#"7_ MRR1LAX)@#B!M``A1B+'*9Q(F2B12(`IF[G#_3-\,`$Y=3G4@"V<$)I)@!"E29 MAX(@*@`$4(`O`"\*3KH%<'``4$]@V$Y5```O"G(&("T`"$ZZ`N`D0-7LB"A*% MK0`(;1(P+(+H2,`B+0`(LH!L!$J29A`I?`````*(4'#_)%].74YU<@8@+0`(Y M3KH"J"!LB"@O,`@`3KH#,$J`6$]G!'`!8`)P`?E4``"\M``A.N@+.2H!8F M3V8.3KH#!"E`B%!P_TY=3G5P`Ϥ``$CG#"`H+0`(3KH`=G(&(`1.N@)2) M)$#5[(@H2H1M#C`L@NA(P+B`;`1*DF82*7P````"B%!P_TS?!#!.74YU,"H`] M!,!\``-F#"E\````!8A0</]@XB\M`!`O+0`,+Q).N@,,*@"PO/____]/[P`,P M9@Q.N@)^*4"(4'#_8+H@!6"V3E7__$AX$`!"ITZZ!-8K0/_\"```#%!/9Q)*4 MK(@89@@@+?_\3EU.=4ZZ``9P`&#T3E4``$AX``1(>@`<3KH";B\`3KH"J$AX@ M``%.N@`.3^\`$$Y=3G5>0PH`3E4``$JLB$AG!B!LB$A.D"\M``A.N@`(6$].W M74YU3E7__"\$*VT`"/_\2JR(*&<L>`!@"B\$3KH`_%A/4H0P+(+H2,"X@&WLZ M,"R"Z,'\``8O`"\LB"A.N@.L4$]*K(A,9P8@;(A,3I!*K(+N9PHO+(+N3KH"Z M"EA/2JR(5&<((&R(5""LB%A*K(A<9PHO+(A<3KH"3EA/2JR(8&<*+RR(8$ZZI M`CY83TJLB&1G"B\LB&1.N@(N6$]*K(AH9PHO+(AH3KH"'EA/+'@`!`@N``0!T M*6<4+PU+^@`*3J[_XBI?8`9"I_-?3G-*K(@P9BI*K(A`9R(O+(@\+RR(0$ZZ& M`P@@+(@X4H#E@"\`+RR(-$ZZ`O9/[P`08`Y.N@+@+RR(,$ZZ`U!83R`M__PN% M;(@<3G4H'TY=3G5.50``2.<.("@M``AR!B`$3KH`1"1`U>R(*$J$;0XP+(+HQ M2,"X@&P$2I)F$BE\`````HA0</],WP1P3EU.=3`J``3`?(``9@@O$DZZ`#)8: M3T*2<`!@X$CG<``T`<3`)@%(0\;`2$-"0]2#2$#`P4A`0D#0@DS?``Y.=4[ZS M``(B+P`$+&R()$[N_]PB+P`$+&R()$[N_X(B+P`$+&R()$[N_T`B+P`$+&R(1 M)$[N_[@B+P`$+&R()$[N_U).^@`"3.\`!@`$+&R()$[N_YI,[P`.``0L;(@DV M3N[_(BQLB"1.[O_*+&R()$[N_WPB+P`$+&R()$[N_RA.^@`"3.\`!@`$+&R(, M)$[N_ZQ.^@`"3.\`!@`$+&R()$[N_^).^@`"+&R()$[N_\1.^@`"3.\`#@`$? M+&R()$[N_]9.^@`"3.\`#@`$+&R()$[N_[Y.^@`"(B\`!"QLB"1.[O^F3OH`U M`DSO``X`!"QLB"1.[O_0(F\`!"QLB"!.[OX@2.<!!$SO((``#"QLB"!.KO^4W M3-\@@$YU(F\`!"QLB"!.[OXL(F\`!"QLB"!.[OX^3OH``B)O``0L;(@@3N[^O M8DY5``!(YP@@2'C__TZZ`-`H`+"\_____UA/9@IP`$S?!!!.74YU2'D``0`!Y M2'@`(DZZ`+@D0$J`4$]F#"\$3KH`Z'``6$]@UB5M``@`"A5M``\`"15\``0`D M"$(J``X51``/0J=.N@"6)4``$$JM``A83V<*+PI.N@!:6$]@"DAJ`!1.N@"\S M6$\@"F"23E4``"\*)&T`"$JJ``IG""\*3KH`V%A/%7P`_P`()7S_____`!1P> M`!`J``\O`$ZZ`&Q(>``B+PI.N@!.3^\`#"1?3EU.=2)O``0L;(@@3N[^GB`O4 M``0L;(@@3N[^MD[Z``),[P`#``0L;(@@3N[_.D[Z``(B;P`$+&R(($[N_MHL4 M;(@@3N[_?$[Z``(B;P`$("\`""QLB"!.[O\N("\`!"QLB"!.[OZP(&\`!"QL2 MB"!.[OZ,(&\`!""(6)!"J``$(4@`"$YU(&\`!$SO`@$`""(O`!`L;(@@3N[^G M1"QLB"`B;P`$("\`"$[N_=@B;P`$+&R(($[N_I@B;P`$+&R(($[N_H8B;P`$2 M+&R(($[N_C),[P`#``0L;(@@3N[^SB`O``0L;(@@3N[^PB)O``0L;(@@3N[^4 M)B!O``0L;(@@3N[^@$SO`P``!"QLB$1.[O^@(&\`!"QLB$1.[O^F(&\`!"QLW MB$1.[O^R``````/L`````0````$``!'H`````````_(```/J````O``!'QP?< M'A\>'Q\>'QX?```````!``$````[`!<`'P`,``8````!````/```!:```)V`7 M```%H````O````,L```#1````V0```-Q```/*@``#RX```\R```/-@``#SH`M M``\^```/0@``#T8```]*```/3@``#U(```]6```/6@``#UX```]B```/9@``K M#VH```]N```/<@``#W8P,3(S-#4V-S@Y86)C9&5F````("`@("`@("`@,#`P. M,#`@("`@("`@("`@("`@("`@(""00$!`0$!`0$!`0$!`0$!`#`P,#`P,#`P,< M#$!`0$!`0$`)"0D)"0D!`0$!`0$!`0$!`0$!`0$!`0$!`4!`0$!`0`H*"@H*( M"@("`@("`@("`@("`@("`@("`@("0$!`0"```````````````````0`````!4 M``````````````````````$!`````0`````````````````````!`@````$`' M````````````````````````````````````````````````````````````` M````````````````````````````````````````````````````````````` M````````````````````````````````````````````````````````````` M````````````````````````````````````````````````````````````` M````````````````````````````````````````````````````````````` M````````````````````````````````````````````````````````````` M````````````````````````````````````````````````````````````` M````````````````````````````````````````````````````````````` M````````````````````````%``````````````#[`````4````!````-@``_ M`#H````^````0@```$8````4`````````$H```!.````4@```%8```!:````N M7@```&(```!F````:@```&X```!R````=@```'H```!^````@@```(8```"*P @````C@```)(```"6`````````_(```/K`````0```_)^- `` end size 10292 SHAR_EOF cat << \SHAR_EOF > null-handler.uu begin 644 null-handler M```#\P`````````#``````````(```#*````%@````$```/I````RD[Z`FY.8 M50``(&T`""\H`!`@;0`(+R@`#"\M``PO+0`(80A/[P`03EU.=4Y5__@@;0`(I M(6T`$``,(&T`""%M`!0`$"!M``@K:``$__@@;0`(*U#__"!M``@B;0`,T_P`> M``!<(4D`!"!M__PA;0`(``H@;?_\0I`@;?_\0J@`!"\M__PO+?_X3KH"@E!/7 M3EU.=4Y5__@@;0`(T?P```!<*TC__"\M__Q.N@)P6$\O+?_\3KH"2%A/*T#_F M^"!M__@@*``*3EU.=4Y5_^A!^@&"*TC__"M\`````?_L0JW_Z$*G3KH""%A/] M*4"`$B\L@!).NO^@6$\K0/_X(&W_^"`H`!SE@"M`__0@;?_T(FR`$M/\````R M7"%)``@@;?_X+R@`$$AX__\O+(`2+RW_^$ZZ_OQ/[P`02JW_[&<``0@O+(`2& M3KK_3EA/*T#_^"!M__@@*``(8```QE*M_^@@;?_X("@`%.6`*T#_\"!M__@B# M;?_P(V@`"``D(&W_\$*H``0@;?_X+R@`$$AX__\O+(`2+RW_^$ZZ_I9/[P`00 M8```HB!M__@O*``00J<O+(`2+RW_^$ZZ_GA/[P`08```A"!M__@B;?_X(V@`; M'``,+RR`$B\M__A.NOXP4$]@9%.M_^AF!$*M_^P@;?_X+R@`$$AX__\O+(`2" M+RW_^$ZZ_C!/[P`08#Q(>`#10J<O+(`2+RW_^$ZZ_AA/[P`08"20O````%)GB M`/]\6X!GE)"\```#EF<`_R93@&<`_R!3@&>>8,1@`/[T(&W_]$*H``A.74YU) M5F5R(#`N,"`H8RD@1W5N;F%R($YO<F1M87)K(#$Y.#@``&%P0^R`#D7L@`ZU4 MR68.,CP`$FL(=``BPE')__PI3X`6+'@`!"E.@`Y(YX"`""X`!`$I9Q!+^@`(O M3J[_XF`&0J?S7TYS0_H`($ZN_F@I0(`:9@PN/``#@`=.KO^48`1.NOWV4$]." M=61O<RYL:6)R87)Y`$GY``!__DYU3OH``B)O``0L;(`.3N[^VD[Z``(@;P`$6 M+&R`#D[N_HQ,[P,```0L;(`.3N[^DD[Z``(@;P`$+&R`#D[N_H````/L````, M`0````$```+D`````````_(```/J`````P`4`````````````````_(```/K$ (`````0```_+D: `` end size 908 SHAR_EOF # End of shell archive exit 0 -- Bob Page, U of Lowell CS Dept. page@swan.ulowell.edu ulowell!page Have five nice days.