dillon@CORY.BERKELEY.EDU.UUCP (02/06/87)
Here's a PIPE device. I would like to thank Phillip Lindsay for providing the skeleton device driver which I used as a base. This will ONLY WORK WITH 1.2!!. Example (run all at once): CLI window 1: copy hugefile pipe:a CLI window 2: copy pipe:a pipe:b CLI window 3: copy pipe:b pipe:c CLI window 4: copy pipe:c pipe:d CLI window 5: copy pipe:d pipe:e CLI window 6: wordcount pipe:e (or something similar) You can, of course, use any rendezvous name you wish. The device uses a 4K internal buffer per name, but is optimized to take advantage of the situation in which there is a pending read and a pending write. In this case it copies direct rather than go through its internal buffer. This is a true pipe, and thus the source and destination processes must be distinct (i.e. not the same process) so as to prevent lockout situations. The buffer is transparent in that data written, no matter how little, is immediately available to be read by the other process. You must place PIPE.DEVICE in 'DEVS:'. Append the MOUNTLIST file to DEVS:MOUNTLIST (or just put it there if you do not have a DEVS:MOUNTLIST), and then do a 'MOUNT PIPE:' in your startup script. The source is provided also. Remember, since this is a device, you do NOT link with any startup module. I have done a couple of other tests and have found that you get a huge efficiency increase when piping an IO bound program through a CPU bound program or vise versa. This is totally public domain except for MISC.C, which is (C) to Phillip (but redistributable). The binary itself is totally public domain. USES: I hope to have my shell use the PIPE: device in it's next release, but this will require huge modifications to the shell so don't expect the next release anytime soon. Apart from that, the PIPE: device can be useful when you have, say, two application programs and want to transfer huge amounts of data from one (write) to the other (read) without using a temporary file in RAM: or on disk. Assuming the application does not attempt a Seek(), you simply specify 'PIPE:name' and it looks like an ordinary file to the application. For those terminal programs which do not use asyncronous writes, you can fix the jerkyness in CAPTURE by capturing to a pipe, and having another CLI Copy command running from the pipe to a file. Have fun and report any bugs to me, -Matt #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # misc.c # mountlist # pipe.c # pipe.device.uue # This archive created: Fri Feb 6 11:46:11 1987 export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'misc.c'" '(1790 characters)' if test -f 'misc.c' then echo shar: "will not over-write existing file 'misc.c'" else cat << \!Funky!Stuff! > 'misc.c' /* * misc.c - support routines - Phillip Lindsay (C) Commodore 1986 * You may freely distribute this source and use it for Amiga Development - * as long as the Copyright notice is left intact. * * 30-SEP-86 * * Modified by Matthew Dillon for my PIPE: device. */ #include <exec/types.h> #include <exec/nodes.h> #include <exec/lists.h> #include <exec/ports.h> #include <libraries/dos.h> #include <libraries/dosextens.h> extern void returnpkt(); /* returnpkt() - packet support routine * here is the guy who sends the packet back to the sender... * * (I modeled this just like the BCPL routine [so its a little redundant] ) */ void returnpktplain(packet, myproc) struct DosPacket *packet; struct Process *myproc; { returnpkt(packet, myproc, packet->dp_Res1, packet->dp_Res2); } void returnpkt(packet, myproc, res1, res2) struct DosPacket *packet; struct Process *myproc; ULONG res1, res2; { struct Message *mess; struct MsgPort *replyport; packet->dp_Res1 = res1; packet->dp_Res2 = res2; replyport = packet->dp_Port; mess = packet->dp_Link; packet->dp_Port = &myproc->pr_MsgPort; mess->mn_Node.ln_Name = (char *) packet; mess->mn_Node.ln_Succ = NULL; mess->mn_Node.ln_Pred = NULL; PutMsg(replyport, mess); } /* * taskwait() ... Waits for a message to arrive at your port and * extracts the packet address which is returned to you. */ struct DosPacket * taskwait(myproc) struct Process *myproc; { struct MsgPort *myport; struct Message *mymess; myport = &myproc->pr_MsgPort; WaitPort(myport); mymess = (struct Message *)GetMsg(myport); return((struct DosPacket *)mymess->mn_Node.ln_Name); } /* end of misc.c */ !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'mountlist'" '(119 characters)' if test -f 'mountlist' then echo shar: "will not over-write existing file 'mountlist'" else cat << \!Funky!Stuff! > 'mountlist' PIPE: Handler = devs:pipe.device Stacksize = 5000 Priority = 5 GlobVec = 1 # !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'pipe.c'" '(13238 characters)' if test -f 'pipe.c' then echo shar: "will not over-write existing file 'pipe.c'" else cat << \!Funky!Stuff! > 'pipe.c' /* * PIPE: device driver. * * Usage: * The writer opens PIPE:somename and begins writing to it. The reader * opens PIPE:samename and begins reading from it. It doesn't matter * who opens PIPE:somename first. Note that if the writer opens the * handle first, writes <BUFSIZE bytes, then closes, the Close() will * not return until a reader has openned the same pipe. * * -Only two opens can be made on a specific pipe * -One of the opens must always write while the other must always * read. * * If the reader closed, any further writes will return an error * If the writer closed, any further reads (after the buffer empties) * will return 0. * * NOTE: Like the filesystem DOS device, I assume that no more than one * request for a specific file handle will be queued at a time. This makes * things a lot easier for me. * * */ #include <exec/types.h> #include <exec/nodes.h> #include <exec/lists.h> #include <exec/ports.h> #include <exec/libraries.h> #include <exec/devices.h> #include <exec/io.h> #include <exec/memory.h> #include <devices/console.h> #include <libraries/dos.h> #include <libraries/dosextens.h> #include <libraries/filehandler.h> typedef struct DosPacket DOSPACKET; typedef struct Process PROC; typedef struct DeviceNode DEVNODE; typedef struct FileHandle FH; typedef unsigned char u_char; #define BUFSIZE 4096 #undef BADDR #define BADDR(x) ((APTR)((long)x << 2)) #define ACTION_FIND_INPUT 1005L #define ACTION_FIND_OUTPUT 1006L #define ACTION_END 1007L #define DOS_FALSE 0 #define DOS_TRUE -1 #define ST_EOF 0x01 /* Handle has been closed */ #define ST_WPEND 0x04 /* pending packet is a write */ #define ST_RPEND 0x08 /* pending packet is a read */ #define ST_CPEND 0x10 /* close pending (writer) */ #define OC_FIRST 1 /* first open, needs to be another */ #define OC_BOTH 2 /* both reader and writer open */ #define OC_LAST 3 /* one closed, one remaining */ #define OC_WAITSECOND 4 /* first open was closed before second was openned */ extern long AbsExecBase; extern DOSPACKET *taskwait(); extern char *AllocMem(); long SysBase; typedef struct _PIPE { struct _PIPE *next, **prev; DOSPACKET *pkt; /* Current pending packet, if any */ char buf[BUFSIZE]; /* Output Buffer */ char *name; /* name (allocated strlen(name)+1) */ short s, e, l; /* FIFO start, end, size */ char state; /* Current state */ char openstate; } PIPE; _main() { PROC *myproc; /* my process */ DOSPACKET *mypkt; /* a pointer to the dos packet sent */ BSTR parmdevname; /* pointer to device name in parmpkt Arg1 */ long parmextra; /* extra info passed in parmpkt Arg2 */ DEVNODE *mynode; /* our device node passed in parmpkt Arg3 */ FH *fh; /* a pointer to our file handle */ PIPE *pipe; /* current PIPE handle */ PIPE *Pipe = NULL;/* linked list base for all pipes */ char *str; u_char *ptr; long run = TRUE; /* handler main loop flag */ int ret; /* nominal packet return value */ int totalcnt = 0;/* total # active pipes */ SysBase = AbsExecBase; myproc = (PROC *)FindTask(0L); /* find myself */ mypkt = taskwait(myproc); /* Wait for startup message */ parmdevname = (BSTR)mypkt->dp_Arg1; /* BSTR name passed to handler */ parmextra = mypkt->dp_Arg2; /* Extra Info passed */ mynode = (DEVNODE *)BADDR(mypkt->dp_Arg3); /* ptr to device node */ /* if taskid NOT installed, every ref creates new */ /* code must be reentrant */ mynode->dn_Task = &myproc->pr_MsgPort; returnpkt(mypkt, myproc, DOS_TRUE, mypkt->dp_Res2); while(run) { mypkt = taskwait(myproc); ret = DOS_TRUE; pipe = (PIPE *)mypkt->dp_Arg1; switch(mypkt->dp_Type) { case ACTION_FIND_INPUT: case ACTION_FIND_OUTPUT: fh = (FH *)BADDR(mypkt->dp_Arg1); /* File handle */ ptr = (u_char *)BADDR(mypkt->dp_Arg3); /* File name */ str = AllocMem(*ptr + 1, 0); if (str == NULL) { ret = DOS_FALSE; goto opfail; } bmov(ptr+1, str, *ptr); str[*ptr] = 0; for (pipe = Pipe; pipe; pipe = pipe->next) { if (strcmp(pipe->name, str) == 0) { FreeMem(str, *ptr + 1); goto openok; } } pipe = (PIPE *)AllocMem(sizeof(PIPE), 0); if (pipe == NULL) { ret = DOS_FALSE; FreeMem(str, *ptr + 1); goto opfail; } ++totalcnt; bzero(pipe, sizeof(*pipe)); pipe->l = BUFSIZE; pipe->name = str; if (Pipe) Pipe->prev = &pipe->next; pipe->next = Pipe; pipe->prev = &Pipe; Pipe = pipe; openok: switch(pipe->openstate) { case 0: pipe->openstate = OC_FIRST; break; case OC_FIRST: pipe->openstate = OC_BOTH; break; case OC_WAITSECOND: pipe->openstate = OC_LAST; pipe->state &= ~ST_CPEND; returnpkt(pipe->pkt, myproc, DOS_TRUE, pipe->pkt->dp_Res2); break; case OC_BOTH: case OC_LAST: ret = DOS_FALSE; /* more than 2 opens */ goto opfail; } fh->fh_Arg1 = (long)pipe; fh->fh_Port = (struct MsgPort *)DOS_TRUE; opfail: returnpkt(mypkt, myproc, ret, mypkt->dp_Res2); break; case ACTION_END: /* If pending read, return pend bytes read * If pending write, return pend bytes written * return pending message, if any */ switch(pipe->openstate) { case OC_FIRST: pipe->openstate = OC_WAITSECOND; break; case OC_BOTH: pipe->openstate = OC_LAST; break; case OC_LAST: pipe->openstate = 0; break; } pipe->state |= ST_EOF; if (pipe->openstate == OC_WAITSECOND) { pipe->pkt = mypkt; pipe->state |= ST_CPEND; break; } if (pipe->state & (ST_RPEND|ST_WPEND)) { returnpktplain(pipe->pkt, myproc); pipe->state &= ~(ST_RPEND|ST_WPEND); } if (pipe->openstate == 0) { FreeMem(pipe->name, strlen(pipe->name)+1); *pipe->prev = pipe->next; if (pipe->next) pipe->next->prev = pipe->prev; FreeMem(pipe, sizeof(*pipe)); --totalcnt; if (totalcnt == 0) run = 0; } returnpkt(mypkt, myproc, ret, mypkt->dp_Res2); break; case ACTION_READ: /* Take chars from buffer. If buffer empty * and read not satisfied, check for pending * write and take bytes from it's buffer. * When done, must check to see if buffer will * hold ALL of pending write. * When done, if read still is not satisfied, * make it pending. */ mypkt->dp_Res1 = 0; /* * Load from buffer until empty or read fulfilled */ while (pipe->s != pipe->e && mypkt->dp_Res1 != mypkt->dp_Arg3) { int avail = (pipe->s < pipe->e) ? pipe->e - pipe->s : pipe->l - pipe->s; int bytes = mypkt->dp_Arg3 - mypkt->dp_Res1; if (bytes < avail) avail = bytes; bmov(pipe->buf+pipe->s, mypkt->dp_Arg2+mypkt->dp_Res1,avail); pipe->s += avail; mypkt->dp_Res1 += avail; if (pipe->s == pipe->l) pipe->s = 0; } /* * If write packet was pending, the read will either exhaust * the write and possibly become pending, or not exhaust the * write and be returned. */ if (mypkt->dp_Res1 != mypkt->dp_Arg3 && (pipe->state & ST_WPEND)) { int bytes = mypkt->dp_Arg3 - mypkt->dp_Res1; int avail = pipe->pkt->dp_Arg3 - pipe->pkt->dp_Res1; if (bytes > avail) bytes = avail; bmov(pipe->pkt->dp_Arg2 + pipe->pkt->dp_Res1, mypkt->dp_Arg2 + mypkt->dp_Res1, bytes); mypkt->dp_Res1 += bytes; pipe->pkt->dp_Res1 += bytes; if (pipe->pkt->dp_Res1 == pipe->pkt->dp_Arg3) { returnpktplain(pipe->pkt, myproc); pipe->state &= ~ST_WPEND; } } /* If read packet is made pending, buffer is always empty */ if (mypkt->dp_Res1 != mypkt->dp_Arg3 && !(pipe->state&ST_EOF)) { if (pipe->state & (ST_RPEND|ST_WPEND|ST_CPEND)) { returnpkt(pipe->pkt, myproc, DOS_FALSE, ERROR_OBJECT_IN_USE); pipe->state &= ~(ST_RPEND|ST_WPEND|ST_CPEND); } pipe->pkt = mypkt; pipe->state |= ST_RPEND; } else { returnpktplain(mypkt, myproc); } break; case ACTION_WRITE: /* * If pending read then buffer is empty, place * chars directly into pending read. If pending * write, error. * If nothing pending and write buffer not big * enough, make the write pend without filling * the buffer. Otherwise, move write data into * the buffer and return the packet. */ mypkt->dp_Res1 = 0; if (pipe->state & ST_EOF) { returnpkt(mypkt, myproc, DOS_FALSE, ERROR_SEEK_ERROR); break; } if (pipe->state & ST_RPEND) { /* write->read */ int avail = mypkt->dp_Arg3 - mypkt->dp_Res1; int bytes = pipe->pkt->dp_Arg3 - pipe->pkt->dp_Res1; if (avail < bytes) bytes = avail; bmov(mypkt->dp_Arg2+mypkt->dp_Res1, pipe->pkt->dp_Arg2+pipe->pkt->dp_Res1, bytes); mypkt->dp_Res1 += bytes; pipe->pkt->dp_Res1 += bytes; if (pipe->pkt->dp_Res1 == pipe->pkt->dp_Arg3) { returnpktplain(pipe->pkt, myproc); pipe->state &= ~ST_RPEND; } } /* write into buffer */ while (mypkt->dp_Res1 != mypkt->dp_Arg3 && pipe->s != ((pipe->e+1)%pipe->l)) { int avail = mypkt->dp_Arg3 - mypkt->dp_Res1; int bytes; if (pipe->e < pipe->s) bytes = pipe->s - pipe->e - 1; else bytes = pipe->l - pipe->e - (pipe->s == 0); if (avail < bytes) bytes = avail; bmov(mypkt->dp_Arg2 + mypkt->dp_Res1, pipe->buf + pipe->e, bytes); pipe->e += bytes; mypkt->dp_Res1 += bytes; if (pipe->e == pipe->l) pipe->e = 0; } if (mypkt->dp_Res1 != mypkt->dp_Arg3) { if (pipe->state & (ST_RPEND|ST_WPEND|ST_CPEND)) { returnpkt(pipe->pkt, myproc, DOS_FALSE, ERROR_OBJECT_IN_USE); pipe->state &= ~(ST_RPEND|ST_WPEND|ST_CPEND); } pipe->pkt = mypkt; pipe->state |= ST_WPEND; } else { returnpktplain(mypkt, myproc); } break; default: returnpkt(mypkt, myproc, DOS_FALSE, ERROR_ACTION_NOT_KNOWN); break; } } mynode->dn_Task = FALSE; /* we are a process "so we fall off the end of the world" */ /* MUST fall through */ } !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'pipe.device.uue'" '(4502 characters)' if test -f 'pipe.device.uue' then echo shar: "will not over-write existing file 'pipe.device.uue'" else cat << \!Funky!Stuff! > 'pipe.device.uue' begin 644 pipe.device M```#\P`````````(``````````<````"`````0```EP````&````#`````4` M```A````$0```^D````"3OD```````````/L`````0````(````"```````` M`_(```/K`````0```_(```/I```"7$Y6_\1(YR`@D<@M2/_@<`$M0/_4(_D` M```$`````"\(+4C_S$ZY````,%B/+P`M0/_\3KD```CJ6(\@0"UH`!3_]"UH M`!C_\"(H`!SE@2)N__S2_`!<)$$E20`(+R@`$'3_+P(O+O_\+P`M0/_X+4'_ M[$ZY```(F$_O`!!*KO_49P`'Y"\N__Q.N0``".I8CW+_+4'_T"!`+6@`%/_D M+4#_^"`H``AR*`2!````"&L`!YBPNQ@(9O!.^Q@&````5V``!3@```!28``# M*````^]@``'N```#[F````H```/M8````B!N__@@*``4Y8`B*``<Y8%T`"!! M%!!2@D*G+P(M0/_H+4'_V$ZY`````%"/+4#_W$J`9@A"KO_08``!@B!N_]A2 MB'``(F[_V!`1+P`O+O_<+PA.N0````!/[P`,<``@;O_8$!`@;O_<T<!"$"UN M_^#_Y$JN_^1G/B\N_]P@;O_D+R@0#$ZY```)'%"/2H!F''``(&[_V!`04H`O M`"\N_]Q.N0```!A0CV```(P@;O_D+5#_Y&"\0J<O/```$!A.N0````!0CRU` M_^1*@&8@0J[_T'``(&[_V!`04H`O`"\N_]Q.N0```!A0CV```-!2KO_,+SP` M`!`8+R[_Y$ZY`````%"/(&[_Y#%\$``0%"%N_]P0#$JN_^!G"")N_^`C2``$ M(&[_Y""N_^!#[O_@(4D`!"U(_^`@;O_D$"@0%TB`2,`,@`````5D8N.`3OL( M`F`(8!)@4&!.8!@@;O_D$7P``1`78$8@;O_D$7P``A`78#H@;O_D$7P``Q`7 M$"@0%@(`_^\10!`6(&@`""\H`!!P_R\`+R[__"\(3KD```B83^\`$&`&0J[_ MT&`0(&[_Z"%N_^0`)'#_(4``!"!N__@O*``0+R[_T"\N__PO"$ZY```(F$_O M`!!@`/V^(&[_Y!`H$!=(@$C`#(`````#9R@,@`````)G%`R``````68@(&[_ MY!%\``00%V`4(&[_Y!%\``,0%V`((&[_Y$(H$!<@;O_D$"@0%@````$10!`6 M$B@0%UD!9A8A;O_X``@0*!`6````$!%`$!9@`/U,(&[_Y!`H$!8"```,2@!G M("\N__PO*``(3KD```AX4(\@;O_D$"@0%@(`__,10!`6(&[_Y!`H$!=*`&9D M+R@0#$ZY`````%B/4H`O`"!N_^0O*!`,3KD````84(\B;O_D(&D`!")1((D@ M;O_D2I!G#"!0(F[_Y"%I``0`!"\\```0&"\N_^1.N0```!A0CR`N_\Q3@"U` M_\Q*@&8$0J[_U"!N__@O*``0+R[_T"\N__PO"$ZY```(F$_O`!!@`/R,(&[_ M^$*H``P@;O_D,"@0$#(H$!*P06<``+8@;O_X("@`#+"H`!QG``"F(&[_Y#`H M$!!(P$C!L(%L!)*`8!0@;O_D,"@0%$C`,B@0$$C!D($B`"!N__@@*``<D*@` M#"U`_\0M0?_(L(%L!"U`_\@@;O_D,"@0$$C`T/P`#-'`(F[_^"`I`!C0J0`, M+R[_R"\`+PA.N0````!/[P`,(&[_Y#`H$!!(P"(N_\C0@3%`$!`B;O_XTZD` M##`H$!`R*!`4L$%F`/]$0F@0$&``_SP@;O_X("@`#+"H`!QG``"P(&[_Y!`H M$!8(```"9P``H"!N__@@*``<D*@`#")N_^0@:0`((B@`')*H``PM0/_(+4'_ MQ+"!;P0M0?_((F[_Y"!I``@@*``8T*@`#"!N__@B*``8TJ@`#"\N_\@O`2\` M3KD`````3^\`#"`N_\@@;O_XT:@`#")N_^0@:0`((B@`#-"!(4``#+"H`!QF M("\N__PO*0`(3KD```AX4(\@;O_D$"@0%@(`__L10!`6(&[_^"`H``RPJ``< M9UH@;O_D$"@0%@@```!F3`(``!Q*`&<J+SP```#*0J<O+O_\+R@`"$ZY```( MF$_O`!`@;O_D$"@0%@(`_^,10!`6(&[_Y"%N__@`"!`H$!8````($4`0%F`` M^I@O+O_\+R[_^$ZY```(>%"/8`#ZA'``(&[_^"%```PB;O_D$"D0%@@```!G M'"\\````VT*G+R[__"\(3KD```B83^\`$&``^E`@;O_D$"@0%@@```-G``"@ M(&[_^"`H`!R0J``,(F[_Y"!I``@B*``<DJ@`#"U`_\@M0?_$L(%L!"U`_\0@ M;O_X("@`&-"H``PB;O_D(&D`""(H`!C2J``,+R[_Q"\!+P!.N0````!/[P`, M("[_Q"!N__C1J``,(F[_Y"!I``@B*``,T($A0``,L*@`'&8@+R[__"\I``A. MN0``"'A0CR!N_^00*!`6`@#_]Q%`$!8@;O_X("@`#+"H`!QG``#H(&[_Y#`H M$!)(P%*`,B@0%$C!3KD`````,"@0$$C`L(%G``#$(&[_^"`H`!R0J``,(&[_ MY#(H$!)(P30H$!!(PBU`_\BR@FP*E(%3@BU"_\1@)B!N_^0P*!`42,`R*!`2 M2,&0@3(H$!!*05?"1`)(@DC"D((M0/_$("[_R+"N_\1L!"U`_\0@;O_X("@` M&-"H``P@;O_D,B@0$DC!T/P`#-'!+R[_Q"\(+P!.N0````!/[P`,(&[_Y#`H M$!)(P"(N_\30@3%`$!(B;O_XTZD`##`H$!(R*!`4L$%F`/\40F@0$F``_PP@ M;O_X("@`#+"H`!QG5"!N_^00*!`6`@``'$H`9RHO/````,I"IR\N__PO*``( M3KD```B83^\`$"!N_^00*!`6`@#_XQ%`$!8@;O_D(6[_^``($"@0%@````01 M0!`68`#X2B\N__PO+O_X3KD```AX4(]@`/@V+SP```#10J<O+O_\+R[_^$ZY M```(F$_O`!!@`/@8(&[_[$*H``A,WP0$3EY.=0``3E8``"!N``@O*``0+R@` M#"\N``PO"&$(3^\`$$Y>3G5.5O_X2.<`,"!N``@A;@`0``PA;@`4`!`B:``$ M)%`F;@`,UOP`7"%+``0E2``*D<@DB"5(``0O"B\)+4G_^"U*__Q.N0```$10 MCTS?#`!.7DYU3E;_^"!N``C0_`!<+P@M2/_\3KD```!P6(\O+O_\3KD```!< M6(\B0"!I``H@"$Y>3G5.5@``2.<`#"IN``@H;@`,2A5G+DH49RH0%1(4L`%D M"G#_3-\P`$Y>3G40%1(4L`%C"G`!3-\P`$Y>3G52C5*,8,X0%1(4L`%FSG`` M3-\P`$Y>3G4```/L`````0````$````:````$`````(```@^```&Q@``!?`` M``5H```#3````6H```A:```(`@``!B(```6T```#Y@```K0```*`````=``` M`(H````T`````0````,```-T````!0````0```>8```&D```!3(```2&```! M.`````$````%```!X@````H````&```)"@``"/X```C<```#N@```X@```'( M```!A@```:0```$*````)@````$````'```'``````````/R```#Z0````8@ M;P`$2AAF_)'O``21_`````$@"$YU``````/R```#Z0````P@;P`$(F\`""`O M``QG```>L\AG```8;P``#M'`T\`3(%.`9OI.=1+84X!F^DYU``````/R```# MZ0````4@;P`$("\`"&<```A"&%.`9OI.=0```_(```/I````(2\.+'D````` M3.\``P`(3J[_.BQ?3G4``"\.+'D`````(F\`""`O``Q.KO\N+%].=2\.+'D` M````(F\`"$ZN_MHL7TYU+PXL>0````!,[P,```A.KOZ2+%].=0``+PXL>0`` M```@;P`(3J[^C"Q?3G4O#BQY`````"!O``A.KOZ`+%].=0```^P````&```` M`0```'0```!@````2````#0````<````!`````````/P`````U]786ET4&]R M=````````'`````"7T=E=$US9P````!<`````E]0=71-<V<`````1`````-? M1FEN9%1A<VL````````P`````E]&<F5E365M````&`````-?06QL;V--96T` M`````````````````_(```/I````$4CG/``J`6<R:@)$@2@`9RAJ`D2`0H)V M'^.`XY*T@64$E(%2@%'+__(B`KF%:@)$@+.$:@A$@6`$0H%"@$S?`#Q.=0`` $```#\I*T ` end !Funky!Stuff! fi # end of overwriting check exit 0 # End of shell archive
FATQW@USU.BITNET (02/19/88)
Could somebody send me a copy of a PIPE: device? I need one for some stuff I'm doing. Thanks in advance Bryan Bryan Ford //// A computer does what \\\\ Snail: 1790 East 1400 North //// you tell it to do, not \\\\ Logan, UT 84321 \\\XX/// what you want it to do. \\\XX/// Email: FATQW@USU.BITNET \XXXX/ Murphy's Law Calendar 1986 \XXXX/