page@swan.ulowell.edu (Bob Page) (03/16/89)
Submitted-by: well!ewhac (Leo 'Bols Ewhac' Schwab) Posting-number: Volume 89, Issue 65 Archive-name: dos/proc.1 # 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: # README # proc.c # proc.uu # This archive created: Wed Mar 15 16:53:01 1989 cat << \SHAR_EOF > README This is a little goodie I cooked up as a simple diversion before I started back into working on "Onion". This is written in Manx C 3.4b. Translation to Lattice should be straightforward. MANUFACTURE: 1> cc proc.c 1> ln proc.o -lc That's all there is to it. The source code has copious comments, which I hope illustrate what's going on. Perhaps some CATS people would care to comment on just how supportable this trick is. I hope you like it. _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ Leo L. Schwab -- The Guy in The Cape INET: well!ewhac@ucbvax.Berkeley.EDU \_ -_ Recumbent Bikes: UUCP: pacbell > !{well,unicom}!ewhac O----^o The Only Way To Fly. hplabs / (pronounced "AE-wack") "Work FOR? I don't work FOR anybody! I'm just having fun." -- The Doctor SHAR_EOF cat << \SHAR_EOF > proc.c /* :ts=8 bk=0 * * proc.c: Illustration of how to create a full-fledged DOS process * without needing to LoadSeg() it first. Based on an idea * presented at BADGE (don't remember the name of the guy * who thought it up). * * Leo L. Schwab 8903.01 */ #include <exec/types.h> #include <libraries/dosextens.h> extern void *AllocMem(), *CreatePort(), *GetMsg(), *FindTask(); extern LONG Open(), Output(), CreateProc(); /* * Cursor manipulation strings. */ char fwdcursor[] = "\033[C"; char backcursor = '\b'; /* * Buried deep in the AmigaDOS Technical Reference Manual, the structure of * of memory lists and SegLists are described. A SegList consists of a BPTR * to the next element in the list (NULL-terminated), followed by code. * The pointers point to the NextSeg field. The size of the node in bytes * is stored in the longword just before NextSeg field. However, the * size is only of real interest to UnLoadSeg(), which you won't be calling * in this case. So we don't need it. * * So the PhonySegList structure consists merely of a NextSeg field, which * is initialized to NULL (CreateProc() ignores this field, anyway), followed * by code to execute, which CreateProc() will jump to. The code we give it * to execute is an absolute jump to the real entry point, which you * initialize appropriately. */ struct PhonySegList { BPTR psl_NextSeg; /* BPTR to next element in list */ UWORD psl_JMP; /* A 68000 JMP abs.l instruction */ LONG (*psl_EntryPoint)(); /* The address of the function */ }; /* * This is NOT the structure that will actually get passed to CreateProc(). * AmigaDOS demands that everything, including SegLists, be on longword * boundaries. Short of compiler-dependent switches, the only way to * guarantee correct alignment is to AllocMem() a PhonySegList structure, * and copy this template into it. You should also remember to initialize * the psl_EntryPoint field (the argument to the JMP instruction) in the * allocated structure, for obvious reasons. */ struct PhonySegList template = { NULL, /* No next element. */ 0x4EF9, /* JMP abs.l */ NULL /* Argument for JMP instruction */ }; /* * This is the routine that will be CreateProc()ed. Due to the global nature * of library base variables, any library routines this routine calls will * be using library base variables that, strictly speaking, belong to the * "parent" process. Despite the fact that it will work, this is * nevertheless a dangerous practice. In an ideal world, you would want to * convince the linker to resolve all library base pointer references to * your co-process's own copies. Lattice, on the other hand, due to its * #pragmas, may not have this problem (can someone confirm this?). */ LONG coprocess () { register int i, n; struct Process *me; struct Message *startupmsg; LONG doswin; char buf[256]; #ifdef AZTEC_C #ifndef _LARGE_DATA /* * This gets to be a bloody nuisance. */ geta4 (); #endif #endif /* * Startup messages are important. Not only can they provide * configuration information for the newly-created process (what * directory you're in, what size to make your window, etc.), but * they also serve to inform the program that started you that * you have finished executing. This is important, because the * parent program will want to know when it's okay to free the * resources it allocated to start you. In this example, we * grab the message so that we can reply it. The act of replying * the startup message will inform the parent that we're done. */ me = FindTask (NULL); WaitPort (&me -> pr_MsgPort); startupmsg = GetMsg (&me -> pr_MsgPort); /* * Recall that, because a global DOSBase pointer has been initialized * by the parent, and because the stubs will reference it at the link * stage, we don't need to open dos.library here. */ if (!(doswin = Open ("CON:0/0/320/100/Sub-Process", MODE_NEWFILE))) goto xit; /* * Say hello. */ Write (doswin, "This is the child speaking.\n", 28L); /* * Print the value of FindTask(NULL) to prove we're a separate * task, and print some values in the Process structure to prove * we're a real process. * * (Another caveat: The stdio functions in this example (like * sprintf()) are being shared by both processes. Some stdio * routines utilize a set of global variables, access to which is * not arbitrated. Therefore, it is possible for the processes to * collide when calling stdio functions, causing Bad Things to * happen. Be aware of this when doing your own multiprogramming. * (I'm pretty sure sprintf() is safe.)) */ sprintf (buf, "I'm at 0x%lx\n", me); Write (doswin, buf, (LONG) strlen (buf)); sprintf (buf, "My stack size appears to be\n%ld bytes.\n", me -> pr_StackSize); Write (doswin, buf, (LONG) strlen (buf)); sprintf (buf, "pr_StackBase is 0x%lx\n", me -> pr_StackBase); Write (doswin, buf, (LONG) strlen (buf)); /* * Make the cursor in the window zot back and forth, which will * happen concurrently with the same action in the CLI window, * proving beyond a shadow of a doubt that there really are two * separate programs running. */ for (n = 20; n--; ) { for (i = 35; i--; ) Write (doswin, fwdcursor, sizeof (fwdcursor) - 1L); for (i = 35; i--; ) Write (doswin, &backcursor, 1L); } /* * We've proved our existence. We now reply our startup message, * and exit. Note the use of Forbid() without a corresponding * Permit(). This prevents this process from being switched out * before exiting completely. When this process is totally gone, * Exec will notice, and do the equivalent of a Permit() internally. */ Close (doswin); /* Get ridda da window. */ xit: Forbid (); ReplyMsg (startupmsg); return (0); } /* * Here is the main program. Its job will be to create the support * structures to fire off the sub-process, print out some relevant * information, then while away the time until the child exits. */ main () { register int i; struct Process *me; struct MsgPort *replyport = NULL; struct Message startupmsg; struct PhonySegList *fakelist = NULL; LONG child, priority, term; /* * Get a handle on the current output stream so that we can Write() * to it. Note that this implies this program really ought to be * run from a CLI. */ if (!(term = Output ())) goto xit; /* * Create a message port for the startup message to be replied to. */ if (!(replyport = CreatePort (NULL, NULL))) goto xit; /* * Allocate a PhonySegList structure. */ if (!(fakelist = AllocMem ((LONG) sizeof (*fakelist), NULL))) goto xit; /* * Copy the template into the allocated memory, and set the entry * point to the sub-process. */ CopyMem (&template, fakelist, (LONG) sizeof (template)); fakelist -> psl_EntryPoint = coprocess; /* * Initialize the startup message. There's nothing really amazing * happening here. Its sole purpose in life is to be replied. */ startupmsg.mn_Node.ln_Type = NT_MESSAGE; startupmsg.mn_Node.ln_Pri = 0; startupmsg.mn_ReplyPort = replyport; /* * Find ourselves. Discover what priority we're running at, so that * we can start the sub-process at the same priority. */ me = FindTask (NULL); priority = me -> pr_Task.tc_Node.ln_Pri; /* * Print some information about ourselves. */ puts ("This is the parent speaking."); printf ("I'm at 0x%lx\n", me); printf ("My stack size appears to be\n%ld bytes.\n", me -> pr_StackSize); printf ("pr_StackBase is 0x%lx\n", me -> pr_StackBase); /* * Now, create and start the sub-process. The current release of * AmigaDOS CreateProc() does nothing special with SegLists. * All it uses it for is to find the first bit of code to execute. * By passing it 'fakelist', we're essentially feeding it a JMP * instruction which jumps to the real start of our sub-process. * (Note that we have to convert 'fakelist' into a BPTR.) * Thus, we get a full-fledged DOS process, which we can do things * with, and we didn't have to LoadSeg() it. */ if (!(child = CreateProc ("Sub-Process", priority, (LONG) fakelist >> 2, 2048L))) goto xit; /* * Send the startup message. This will get the sub-process doing * its thing. * (Note that CreateProc() returns a pointer, not to the process * structure, but to the pr_MsgPort structure *within* the process * structure.) */ PutMsg (child, &startupmsg); /* * Make our cursor fly back and forth, which hopefully will happen * concurrently with the sub-process's activity. We continue to * do this until the sub-process replies its message, making * GetMsg() return a non-NULL value. Since we "know" what's arriving * at the replyport, we can safely throw the result from GetMsg() * away. */ while (!GetMsg (replyport)) { for (i = 64; i--; ) Write (term, fwdcursor, sizeof (fwdcursor) - 1L); for (i = 64; i--; ) Write (term, &backcursor, 1L); } /* * At this point, the sub-process has completely exited. We may * now safely deallocate all the support structures, and exit. */ puts ("Child terminated."); xit: if (fakelist) FreeMem (fakelist, (LONG) sizeof (*fakelist)); if (replyport) DeletePort (replyport); } SHAR_EOF cat << \SHAR_EOF > proc.uu begin 644 proc M```#\P`````````#``````````(```61````J@````$```/I```%D4[Z`_I.1 M5?[T2.<,`$ZZ!&!"ITZZ%5183RM`__P@;?_\2&@`7$ZZ%>A83R!M__Q(:`!<I M3KH5<%A/*T#_^$AX`^Y(>@$P3KH3O%!/*T#_]&<``0I(>``<2'H!-B\M__1.R MNA/`3^\`#"\M__Q(>@$_2&W^]$ZZ"#9/[P`,2&W^]$ZZ#.)83TC`+P!(;?[T) M+RW_]$ZZ$XY/[P`,(&W__"\H`(1(>@$72&W^]$ZZ"`!/[P`,2&W^]$ZZ#*Q8_ M3TC`+P!(;?[T+RW_]$ZZ$UA/[P`,(&W__"\H`)!(>@$)2&W^]$ZZ!\I/[P`,^ M2&W^]$ZZ#'983TC`+P!(;?[T+RW_]$ZZ$R)/[P`,>A1@0'@C8!1(>``#2&R`` M`B\M__1.NA,&3^\`##`$4T1*0&;D>"-@%$AX``%(;(`&+RW_]$ZZ$N9/[P`,^ M,`131$I`9N0P!5-%2D!FN"\M__1.NA)46$].NA0<+RW_^$ZZ%(Q83W``3-\`M M,$Y=3G5#3TXZ,"\P+S,R,"\Q,#`O4W5B+5!R;V-E<W,`5&AI<R!I<R!T:&4@% M8VAI;&0@<W!E86MI;F<N"@!))VT@870@,'@E;'@*`$UY('-T86-K('-I>F4@2 M87!P96%R<R!T;R!B90HE;&0@8GET97,N"@!P<E]3=&%C:T)A<V4@:7,@,'@EO M;'@*`$Y5_]0O!$*M__A"K?_@3KH2#"M`_]1G``$Z0J="ITZZ$E103RM`__AGY M``$H0J=(>``*3KH3*E!/*T#_X&<``11(>``*+RW_X$AL@`A.NA(43^\`#$'ZZ M_;8B;?_@(T@`!AM\``7_[$(M_^TK;?_X__)"ITZZ$OQ83RM`__P@;?_\$"@`` M"4B`2,`K0/_82'H`[DZZ!3)83R\M__Q(>@#]3KH&7E!/(&W__"\H`(1(>@#YP M3KH&3%!/(&W__"\H`)!(>@$/3KH&.E!/2'@(`"`M_^#D@"\`+RW_V$AZ`0Q.X MNA#D3^\`$"M`_]QG:$AM_^0O+?_<3KH2ZE!/+RW_^$ZZ$JY83TJ`9D)X0&`4C M2'@``TAL@`(O+?_43KH1$$_O``PP!%-$2D!FY'A`8!1(>``!2&R`!B\M_]1.> MNA#P3^\`##`$4T1*0&;D8+!(>@"H3KH$=EA/2JW_X&<.2'@`"B\M_^!.NA(J: M4$]*K?_X9PHO+?_X3KH1CEA/*!].74YU5&AI<R!I<R!T:&4@<&%R96YT('-PV M96%K:6YG+@!))VT@870@,'@E;'@*`$UY('-T86-K('-I>F4@87!P96%R<R!T! M;R!B90HE;&0@8GET97,N"@!P<E]3=&%C:T)A<V4@:7,@,'@E;'@*`%-U8BU0$ M<F]C97-S`$-H:6QD('1E<FUI;F%T960N`&%P0^R"8D7L@F*UR68.,CP`$6L(+ M=``BPE')__PI3X)J+'@`!"E.@FY(YX"`""X`!`$I9Q!+^@`(3J[_XF`&0J?S6 M7TYS0_H`($ZN_F@I0()R9@PN/``#@`=.KO^48`1.N@`:4$].=61O<RYL:6)RV M87)Y`$GY``!__DYU3E4``"\*2'D``0``,"R"7L'\``8O`$ZZ$,PI0()V4$]FD M%$*G2'D``0``3KH/D%!/+FR":DYU(&R"=D)H``0@;()V,7P``0`0(&R"=C%\S M``$`"B!L@FH@+()JD*@`!%"`*4"">B!L@GH@O$U!3EA"ITZZ$(0D0$JJ`*Q8E M3V<N+RT`#"\M``@O"DZZ`*XY?``!@GX@;()V`&B````$(&R"=@!H@```"D_OY M``Q@0DAJ`%Q.NA#J2&H`7$ZZ$'@I0(*`(&R"@$JH`"103V<0(&R"@")H`"0OL M$4ZZ#H!83R\L@H`O"DZZ`IPI;(*`@H103TZZ#H`@;()V((!.N@ZH(&R"=B%`C M``9G%DAX`^U(>@`J3KH.@"!L@G8A0``,4$\O+(*$/RR"B$ZZ_%Q"9TZZ#+!0X M3R1?3EU.=2H`3E4``$CG##`D;0`0(&T`""`H`*SE@"@`($0@*``0Y8`F0!`39 M2(!(P-"M``Q4@#E`@HI"IS`L@HI(P"\`3KH/;BE`@HQ03V8(3-\,,$Y=3G40H M$TB`.@`_!2!+4H@O""\L@HQ.N@%.2'H!2#`%2,#0K(*,+P!.N@&(/RT`#B\*: M+RR"C$ZZ`58@;(*,0C!0`#E\``&"B#`%2,#0K(*,)D!2BR1+3^\`'!`32(`Z< M`+!\`"!G&+I\``EG$KI\``QG#+I\``UG!KI\``IF!%*+8-@,$P`@;7H,$P`B. M9BY2BR!+4HL0$$B`.@!G'B!*4HH0A;I\`")F$`P3`")F!%*+8`9"*O__8`)@C MUF`X($M2BQ`02(`Z`&<FNGP`(&<@NGP`"6<:NGP`#&<4NGP`#6<.NGP`"F<(F M($I2BA"%8,X@2E**0A!*168"4XM2;(*(8`#_6D(20J<P+(*(4D!(P.6`+P!.6 MN@Y,*4""A%!/9@A";(*(8`#^V'H`)FR"C&`>,`5(P.6`(&R"A"&+"``O"TZZ` M!BA20$C`U\!83U)%NFR"B&W<,`5(P.6`(&R"A$*P"`!@`/Z:(`!,[P,```0@^ M"#(O``Q@`A#95\G__&<&4D%@`D(84<G__$YU,#Q__V`$,"\`#"!O``1*&&;\_ M4T@B;P`(4T`0V5?(__QG`D(0("\`!$YU(&\`!"`((F\`"!#99OQ.=4Y5```O/ M"B1M``A*$F<@($I2BA`02(`_`$ZZ!9ZP?/__5$]F"'#_)%].74YU8-P_/``*Z M3KH%A%1/8.Q.50``2.<.,"1M``A"ITAZ`(Y.N@V\*4""D%!/9@A,WPQP3EU.; M=2!M``PB:``D+RD`!$ZZ#@XH`%A/9U)(>@!M($0O*``V3KH-X"9`2H!03V<T# M2'@#[2\+3KH+MBP`4$]G)"`&Y8`J`"!%)6@`"`"D)48`G$AX`^U(>@`X3KH+@ MDB5``*!03R\$3KH-K%A/+RR"D$ZZ"\!"K(*06$]@@&EC;VXN;&EB<F%R>0!7[ M24Y$3U<`*@!.50``+P0I;0`(@F)(;0`0+RT`#$AZ`!I.N@#<.``@;()B0A`P: M!$_O``PH'TY=3G5.50``(&R"8E*L@F(0+0`)$(!(@,!\`/].74YU3E4``$AM7 M``PO+0`(2'H$<$ZZ`)A/[P`,3EU.=4Y5``!(YP@@)&T`#@QM``0`$F8((&T`A M""@08!Q*;0`,;PP@;0`(<``P$"@`8`H@;0`(,!!(P"@`0FT`$DIM``QL$$1M' M``Q*A&P(1(0[?``!`!(R+0`,2,$@!$ZZ`XY![(`24XH4L```,BT`#$C!(`1.7 MN@.$*`!FVDIM`!)G!E.*%+P`+2`*3-\$$$Y=3G5.5?\B2.<(,"1M``@F;0`,S M0FW_^BMM`!#__"!+4HL0$$B`.`!G``+LN'P`)68``LI"+?\P.WP``?_X.WP`7 M(/_V.WPG$/_T($M2BQ`02(`X`+!\`"UF#D)M__@@2U*+$!!(@#@`N'P`,&803 M.WP`,/_V($M2BQ`02(`X`+A\`"IF&"!M__Q4K?_\.U#_\B!+4HL0$$B`.`!@H M,D)M__)@'#`M__+!_``*T$20?``P.T#_\B!+4HL0$$B`.``P!%)`0>R`)`@P6 M``(``&;4N'P`+F9:($M2BQ`02(`X`+!\`"IF&"!M__Q4K?_\.U#_]"!+4HL0T M$$B`.`!@,D)M__1@'#`M__3!_``*T$20?``P.T#_]"!+4HL0$$B`.``P!%)`# M0>R`)`@P``(``&;4.WP``O_PN'P`;&82($M2BQ`02(`X`#M\``3_\&`0N'P`[ M:&8*($M2BQ`02(`X`#`$2,!@>#M\``C_[F`6.WP`"O_N8`X[?``0_^Y@!CM\C M__;_[C\M__!(;?\P/RW_[B\M__Q.NOWD*T#_ZC`M__!(P-&M__Q/[P`,8%H@_ M;?_\6*W__"M0_^HO+?_J3KH"##M`__!83V!*(&W__%2M__PX$$'M_R\K2/_JF M$(1@*)"\````8V?B4X!GE)"\````"V<`_W19@&>T58!G`/]R5X!G`/]T8,Q!2 M[?\PD>W_ZCM(__`P+?_PL&W_]&\&.VW_]/_P2FW_^&=H(&W_Z@P0`"UG"B!M* M_^H,$``K9BX,;0`P__9F)E-M__(@;?_J4JW_ZA`02(`_`$Z2L'S__U1/9@IPQ M_TS?#!!.74YU8!8_+?_V3I*P?/__5$]F!'#_8.12;?_Z,"W_\E-M__*P;?_P8 M;MQ";?_N8"`@;?_J4JW_ZA`02(`_`$Z2L'S__U1/9@1P_V"P4FW_[B!M_^I*M M$&<*,"W_[K!M__1MSC`M_^[1;?_Z2FW_^&8H8!@_/``@3I*P?/__5$]F!G#_N M8`#_>%)M__HP+?_R4VW_\K!M__!NVF`6/P1.DK!\__]43V8&</]@`/]24FW_2 M^F``_0HP+?_Z8`#_0DCG2`!"A$J`:@1$@%)$2H%J!D2!"D0``6$^2D1G`D2`0 M3-\`$DJ`3G5(YT@`0H1*@&H$1(!21$J!:@)$@6$:(`%@V"\!81(@`2(?2H!.& M=2\!808B'TJ`3G5(YS``2$%*068@2$$V`30`0D!(0(##(@!(0#("@L,P`4)!K M2$%,WP`,3G5(028!(@!"04A!2$!"0'0/T(#3@;:!8@22@U)`4<K_\DS?``Q.G M=2!O``0@"$H89OR1P"`(4X!.=4Y5``!(;("\/RT`"$ZZ``A<3TY=3G5.50``6 M+P0X+0`(+RT`"C\$3KH`,+A\``I<3V8D(&T`"A`H``Q(@`@```=G%#\\__\O) M+0`*3KH`]%Q/*!].74YU8/A.50``+PHD;0`*(%*QZ@`$91@P+0`(P'P`_S\`/ M+PI.N@#(7$\D7TY=3G4@4E*2$"T`"1"`2(#`?`#_8.A.50``+PI![("F)$@@7 M2M7\````%B\(81!83T'L@EZUR&7J)%].74YU3E4``$CG""`D;0`(>``@"F8*O M</],WP003EU.=4HJ``QG4`@J``(`#&<,/SS__R\*85(X`%Q/$"H`#4B`/P!.J MN@3NB$`(*@`!``Q43V<*+RH`"$ZZ`BY83P@J``4`#&<2+RH`$DZZ`L`O*@`2C M3KH"%%!/0I)"J@`$0JH`"$(J``PP!&"03E7__DCG""`D;0`(0?K_1BE(@I0(P M*@`$``QG"G#_3-\$$$Y=3G4(*@`"``QG,"`2D*H`"#@`/P0O*@`($"H`#4B`] M/P!.N@*`L$103V<0".H`!``,0I)"J@`$</]@P`QM__\`#&80"*H``@`,0I)"W MJ@`$<`!@J$JJ``AF""\*3KH`FEA/#&H``0`09BH;;0`-__\_/``!2&W__Q`JE M``U(@#\`3KH"(K!\``%03V:@,"T`#&``_VHDJ@`(,"H`$$C`T*H`""5```0(_ MZ@`"``P@4E*2$"T`#1"`2(#`?`#_8`#_/DY5```O"D'L@*8D2$HJ``QG&-7\S M````%D'L@EZUR&4(<``D7TY=3G5@XD*20JH`!$*J``@@"F#J3E7__"\*)&T`$ M"#\\!`!.N@#`*T#__%1/9A@U?``!`!`@2M'\````#B5(``@D7TY=3G4U?`0`X M`!`(Z@`!``PE;?_\``@0*@`-2(`_`$ZZ`.)*0%1/9P8`*@"```Q@SDY5``!(% MYP`P)&R"9F`4)E(@*@`$4(`O`"\*3KH%0%!/)$L@"F;H0JR"9DS?#`!.74YU/ M3E4``"\*0?K_QBE(@IA"IR`M``A0@"\`3KH$XB1`2H!03V8(<``D7TY=3G4D2 MK()F)6T`"``$*4J"9B`*4(!@YDY5``!P`#`M``@O`&&R6$].74YU3E4``$CGN M`#"7RR1L@F9@#B!M``A1B+'*9Q(F2B12(`IF[G#_3-\,`$Y=3G4@"V<$)I)@` M!"E2@F8@*@`$4(`O`"\*3KH$DG``4$]@V$Y5```O"C`M``C!_``&)$#5[()VS M2FT`"&T.,"T`"+!L@EYL!$J29@XY?``"@IQP_R1?3EU.=3`M``C!_``&(&R"& M=B\P"`!.N@*P2H!83V<$<`%@`G``8-A.50``+RT`"$ZZ`GI*@%A/9@Y.N@*$L M.4""G'#_3EU.=7``8/A.50``2.<,(#@M``A.N@!P,`3!_``&)$#5[()V2D1MN M"KAL@EYL!$J29A`Y?``"@IQP_TS?!#!.74YU,"H`!,!\``-F"CE\``6"G'#_U M8.1P`#`M``XO`"\M``HO$DZZ`DPJ`+"\_____T_O``QF#$ZZ`@0Y0(*<</]@- MN"`%8+1.5?_\2'@0`$*G3KH#_"M`__P(```,4$]G$DIL@GYF""`M__Q.74YU[ M3KH`!G``8/1.50``2'@`!$AZ`!Q.N@'B+P!.N@'H/SP``4ZZ``Y/[P`.3EU.Y M=5Y#"@!.50``2JR"E&<&(&R"E$Z0/RT`"$ZZ``A43TY=3G5.5?_\+P0P+0`(' M2,`K0/_\2JR"=F<H>`!@"C\$3KH`T%1/4D2X;()>;?`P+()>P?P`!B\`+RR"- M=DZZ`MY03TJL@IAG!B!L@IA.D$JL@IYG"B\L@IY.N@&(6$]*K(*B9PHO+(*B" M3KH!>%A/2JR"IF<*+RR"IDZZ`6A83RQX``0(+@`$`2EG%"\-2_H`"DZN_^(JK M7V`&0J?S7TYS2JR"@&8P2JR"C&<H,"R"BDC`+P`O+(*,3KH"9#`L@HA20$C`8 MY8`O`"\L@H1.N@)03^\`$&`.3KH".B\L@H!.N@*J6$\@+?_\+FR":DYU*!].M M74YU3E4``$CG#B`X+0`(,`3!_``&)$#5[()V2D1M"KAL@EYL!$J29A`Y?``"C M@IQP_TS?!'!.74YU""H`!P`$9@@O$DZZ``Y83T*2<`!@XD[Z``(B+P`$+&R"" M<D[N_]PO!$SO`!X`""QL@G).KO]V*!].=2(O``0L;()R3N[_@B(O``0L;()RC M3N[_N"QL@G).[O_*+&R"<D[N_WPB+P`$+&R"<D[N_RA.^@`"3.\`!@`$+&R"T M<D[N_^).^@`"+&R"<D[N_\1.^@`"3.\`#@`$+&R"<D[N_]!(YP$$3.\@@``,M M+&R";DZN_Y1,WR"`3G5.^@`"(F\`!"QL@FY.[OYB3.\#```$("\`#"QL@FY.; M[OV03E4``$CG""!(>/__3KH`T"@`L+S_____6$]F"G``3-\$$$Y=3G5(>0`!1 M``%(>``B3KH`N"1`2H!03V8,+P1.N@#L<`!83V#6)6T`"``*%6T`#P`)%7P`E M!``(0BH`#A5$``]"ITZZ`)8E0``02JT`"%A/9PHO"DZZ`%I83V`*2&H`%$ZZ[ M`,183R`*8)).50``+PHD;0`(2JH`"F<(+PI.N@#86$\5?`#_``@E?/____\`> M%'``$"H`#R\`3KH`<$AX`"(O"DZZ`%)/[P`,)%].74YU(F\`!"QL@FY.[OZ>9 M("\`!"QL@FY.[OZV3OH``DSO``,`!"QL@FY.[O\Z3OH``B)O``0L;()N3N[^U MVD[Z``(L;()N3N[_?$[Z``(B;P`$("\`""QL@FY.[O\N("\`!"QL@FY.[OZP/ M3OH``B!O``0L;()N3N[^C"!O``0@B%B00J@`!"%(``A.=2QL@FXB;P`$("\`< M"$[N_=A,[P,```0L;()N3N[^DB)O``0L;()N3N[^F$[Z``(B;P`$+&R";D[N! M_H9,[P`#``0L;()N3N[^SD[Z``(@;P`$+&R";D[N_H!,[P,```0L;(*03N[_< MH"!O``0L;(*03N[_IB!O``0L;(*03N[_L@```^P````!`````0``!'``````- M```#\@```^H```"8&UM#``@``````$[Y`````#`Q,C,T-38W.#EA8F-D968`D M```@("`@("`@("`P,#`P,"`@("`@("`@("`@("`@("`@()!`0$!`0$!`0$!`@ M0$!`0$`,#`P,#`P,#`P,0$!`0$!`0`D)"0D)"0$!`0$!`0$!`0$!`0$!`0$!_ M`0$!0$!`0$!`"@H*"@H*`@("`@("`@("`@("`@("`@("`@)`0$!`(```````' M```````````!``````$``````````````````````0$````!````````````% M``````````$"`````0``````````````````````````````````````````$ M````````````````````````````````````````````````````````````` M````````````````````````````````````````````````````````````` M````````````````````````````````````````````````````````````` M````````````````````````````````````````````````````````````` M````````````````````````````````````````````````````````````` M````````````````````````````````````````````````````````````` M````````````````````````````````````````````````````````````` M```````````````````````````````````````````4``````/R```#ZP``W &``$```/RV `` end size 6396 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.