[net.sources] TU58 emulator, part 2 of 2

dan@rna.UUCP (Dan Ts'o) (07/25/85)

#

#	This is part 2 of 2 of the TU58 emulation program.
#
#					Cheers,
#					Dan Ts'o
#					Dept. Neurobiology
#					Rockefeller Univ.
#					1230 York Ave.
#					NY, NY 10021
#					212-570-7671
#					...cmcl2!rna!dan
#					rna!dan@cmcl2.arpa

echo ./rtpip/rtpip.mk
sed 's/^X//' > ./rtpip/rtpip.mk << 'All work and no play makes Jack a dull boy'
XOPTIONS= -DVAX
XCFLAGS= -c ${OPTIONS}
Xrtpip: rtpip.o rtun.o
X	cc -o rtpip rtpip.o rtun.o
X	rm err
Xrtpip.o:	rtpip.c
X	cc ${CFLAGS} rtpip.c 2>err
X	echo 
Xrtun.o:	rtun.c
X	cc ${CFLAGS} rtun.c 2>err
X	echo 
All work and no play makes Jack a dull boy
echo ./rtpip/rtread.2
sed 's/^X//' > ./rtpip/rtread.2 << 'All work and no play makes Jack a dull boy'
X.th RTREAD II 1/1/76
X.sh NAME
Xrtread *- read from RT-11 file
X.sh SYNOPSIS
X.br
X.ft B
Xrtread (fildes, buffer, nbytes)
X.br
Xchar *buffer;
X.br
X.ft R
X.sh DESCRIPTION
XA file descriptor is a word returned from a successful
X.it rtopen.
X.it Buffer
Xis the address of
X.it nbytes
Xcontiguous bytes, into which the input will be placed.
XIt is not guaranteed that all
X.it nbytes
Xbytes will be read.
XIn any event, the number of characters read
Xis returned.
X.s3
XIf the returned value is 0,
Xthen end-of-file has been reached.
X.sh FILES
XIn Nymegen,
X.it rtread
Xis in the C-library (member name RTUN.O).
X.sh "SEE ALSO"
Xread(II), rtpip(I), rtmount(II), rtumount(II), rtopen(II), rtclose(II),
Xrtwrite(II), rtdelete(II)
X.sh AUTHOR
XT. Forgacs, Informatics Dept. Nymegen Univ. the Netherlands.
X.sh DIAGNOSTICS
XFrom C, a -1 value is returned on error: e.g. bad descriptor,
Xbuffer address or count, physical I/O errors etc.
X.sh GLOBALS
XDo not use the following external names:
X.S3
Xrttabl, rtdev, rtcurse, rtdirta, rtbuffe,
Xrtwrop, rtoldfl, rtsegfu, rtextb, rtconvr,
Xrtgetna, rtphio, rtpseek
All work and no play makes Jack a dull boy
echo ./rtpip/rtumount.2
sed 's/^X//' > ./rtpip/rtumount.2 << 'All work and no play makes Jack a dull boy'
X.th RTUMOUNT II 1/1/76
X.sh NAME
Xrtumount *- dismount RT-11 file structured device
X.sh SYNOPSIS
X.br
X.ft B
Xrtumount ()
X.br
X.ft R
X.sh DESCRIPTION
X.it Rtumount
Xannounces to the system that the special file mounted by
X.it rtmount
Xis no longer to contain a removable RT-11 file system.
X.sh FILES
XIn Nymegen,
X.it rtumount
Xis in the C-library (member name RTUN.O).
X.sh "SEE ALSO"
Xumount(II), rtpip(I), rtmount(II), rtopen(II), rtclose(II),
Xrtread(II), rtwrite(II), rtdelete(II)
X.sh AUTHOR
XT. Forgacs, Informatics Dept. Nymegen Univ. the Netherlands.
X.sh DIAGNOSTICS
XIf there was no special file mounted, 
X.it rtumount
Xis a dummy operation.
X.sh GLOBALS
XDo not use the following external names:
X.S3
Xrttabl, rtdev, rtcurse, rtdirta, rtbuffe,
Xrtwrop, rtoldfl, rtsegfu, rtextb, rtconvr,
Xrtgetna, rtphio, rtpseek, rtheade, rtinitd,
Xrtserdi, rtconde, rtucond, rtadjus.
X.sh BUGS
All work and no play makes Jack a dull boy
echo ./rtpip/rtun.c
sed 's/^X//' > ./rtpip/rtun.c << 'All work and no play makes Jack a dull boy'
X#
X/*************************************************************/
X/*  ********                                                 */
X/*  * rtun *                                                 */
X/*  ********                                                 */
X/*                                                           */
X/* status: 28 oct 75 version 1.                              */
X/* modified: jan 85 J. Kourlas; for VAX.                     */
X/*          compile with -DVAX flag                          */
X/* author: t.forgacs univ of nijmegen comp. graphics         */
X/* compiler: c unix version 6.                               */
X/* entrypoints: rtmount rtumount rtopen rtclose              */
X/*              rtread  rtwrite  rtseek rtmksp rtdelete      */
X/* function: this subr.package makes possible to handle      */
X/*          rt-11 files under unix                           */
X/*          rtmount: establishes a device on which the rt-11 */
X/*                   filestructure resides                   */
X/*          rtumount:deestablishes the device                */
X/*          rtopen: open rt-11 file for read,write,update    */
X/*          rtclose: closes an open rt-11 file               */
X/*          rtread: sequential read                          */
X/*          rtwrite: sequential write                        */
X/*          rtseek: locates read-write pointer               */
X/*          rtmksp: rt-11 garbage collection                 */
X/*          rtdelete: delete rt-11 file by name              */
X/* calling sequences,parameters,returns:                     */
X/*        rtmount(name,rwflag)                               */
X/*          char *name; special-file name                    */
X/*          int  rwflag; 0:mount for read, 1:write, 2:update */
X/*          returns: unix-filedescriptor for the spec file   */
X/*                   or -1 if rtmount fails                  */
X/*        rtumount();                                        */
X/*        rtopen(name,mode)                                  */
X/*          char *name; rt-11 filename                       */
X/*          int  mode;0:read, 1:write, 2:update              */
X/*          returns: rt-filedescripror used in rtread etc    */
X/*                   or -1 if open fails                     */
X/*        rtclose(fildes)                                    */
X/*          int  fildes; filedescriptor                      */
X/*        rtread(fildes,buffer,nbytes)                       */
X/*          int fildes; filedescriptor                       */
X/*          char *buffer;                                    */
X/*          int  nbytes; number of bytes to be read          */
X/*          returns  number of bytes actually  read          */
X/*                   0 if end of file was reached            */
X/*                   0r -1 if read fails                     */
X/*        rtseek=== to be implemented later                  */
X/*        rtmksp=== to be implemented later                  */
X/*        rtdelete(name)                                     */
X/*         char  *name; rt-11 file to be deleted             */
X/*         returns: -1 if fails, 0 otherwise                 */
X/* externals: rtun calls the following sysroutines(emt):     */
X/*              open close read write seek                   */
X/* notes: as a default convevtion: at most 4 files can be    */
X/*         open and only one of them for write(update).      */
X/*         to change that replace #define numop 3 (right     */
X/*         after this documentation) with #define numop n-1  */
X/*         wher n is the wanted number of max open files     */
X/*************************************************************/
X
X#define numop 3
X#define readfl 0
X#define writfl 1
X#define updatfl 2
X#define phread 0
X#define phwrite 1
X
X#ifdef VAX
X#define r_short short
X#else
X#define r_short int
X#endif
X
X/************************************************************/
X/* definition of external(global) data and functions
Xused by the routines*/
X/*************************************************************/
X
X                      /*---rtinode(rttabl)---*/
X
Xstruct rtinode /* data of open files */
X{  r_short pblkn; /* phis start blocknumb */
X   r_short filsiz; /*filesize in blocks */
X   r_short seekp[2]; /* seekpointer-offset: block,byte resp */
X   r_short curend[2];/*current endpointer for file to be written*/
X   r_short dira[2];/*dir adr block byte resp.*/
X   r_short opflg; /* openflag 0,1,2 for read,write,upd and -1 for closed*/
X} rttabl[numop+2];
X/*rttabl[numop+1] is for old file to be deleted after closing
Xthe one created instead of */
X
X                      /*---dev(rtdev)---*/
X
Xstruct dev /*data for the mounted device */
X{  int fdes; /* unix file descriptor for mounted spec file */
X   int rwflag; /* see rtmount */
X} rtdev = {-1,0};
X
X                      /*---cseek(rtcurseek)---*/
X
Xstruct cseek /* current physical seekpointer */
X{  int sblkn; /*blocknumb */
X   int sbyte /*bytenumb */;
X} rtcurseek;
X
X                      /*---dirs(rtdirtab)---*/
X
Xstruct dirs /*for directory searche */
X/* all data are in physical discadress*/
X{ r_short dirad[2];/*current searchadr block byte resp. */
X  r_short filst; /*startblock of current file */
X  r_short senumb[2]; /*number of opened segm; max, current resp. */
X
X  r_short rtextb; /*number of extrabytes per entry */
X  r_short curs; /*current dir segment */
X  r_short nexts; /*next dir segment */
X}rtdirtab;
X
X                      /*---workarea---*/
X
Xchar rtbuffer[1024]; /*internal buffer */
Xint  rtwrop  = -1; /* fildes. for write file or -1 if none */
Xint rtoldfl = 0;/*set if there is old fil to be deleted at close*/
Xint rtsegfull[31]; /*see rtserdir() func */
Xr_short rtextb;/*extrabytes per entry in dirsegm, containing
X           the file to be written */
X
X
X
X                    /*---rtconvrad---*/
X
Xrtconvrad(ascii,radix) char *ascii;  r_short *radix;
X/* converts at most 3 ascii char to rad50. conversion stops
Xafter the third char or after the first not in radix-set one
Xin the latter case the missing ones are taken to be space
Xreturns the number of really conv chars */
X
X{int i,retur; char temp[3];
Xretur=3;
Xfor (i=0;i!=3;i++)
X{if (retur != 3) {temp[i]=0;continue;}
X  switch (ascii[i])
X  { case 040: temp[i]=0;break;
X    case 044: temp[i]=033;break;
X    case 056: temp[i]=034;break;
X    default: if (ascii[i]<=0132 && ascii[i]>=0101)
X               temp[i]= ascii[i]-0100;
X             else if (ascii[i]<=071 && ascii[i]>= 060)
X               temp[i]= ascii[i]-022;
X             else if(ascii[i]<=0172 &&ascii[i]>=0141)
X               temp[i]=ascii[i]-0140;
X             else {retur=i; temp[i]=0;}
X  }/*switch*/
X}/*for*/
X
X*radix=temp[0]*050*050+temp[1]*050+temp[2];
Xreturn(retur);
X}/*rtconvrad*/
X
X                      /*---rtgetname---*/
X
Xrtgetname(name,radname)  char *name; r_short *radname;
X/*converts name (ascii rt11-filename) into radname(radix50)
Xsuitable for rt11 dictionary, i.e. 2 words for filnam 1 word
Xfor ext,padded with spaces */
X
X{ char  tempname[9];/*for name extended to 9 bytes */
X   int i,j,retur;/*returned value: 0 o.k. -1 invalid name */
X   for (i=9; i--;) tempname[i]=' ';/*fill with spaces */
X   j=0;
X   for (i=0; name[i] != 0;i++)
X     {if(i==10)return(-1);
X       if (name[i]=='.')
X         if(i>6)return(-1);
X         else {j=6; continue;}
X       tempname[j]=name[i];j++;
X     }
X   j=0;
X   for (i=0; i!=3;i++)
X     {if(rtconvrad(tempname+j,radname+i) !=3) return(-1);
X       j += 3;
X     }
X   return(0);
X}
X                      /*---rtphio---*/
X
Xrtphio(iofl,buffer,nbytes,endblk)
Xchar *buffer;
X/* this routine does phisical i0
X  reads(iofl=0) or writes(iofl=1) nbytes bytes to (from) buffer
Xto(from) rt11 device from current dev seekpointer(rtcurseek)
Xio stops after read(written) nbytes bytes or reaching the endblk
Xphysical blocknumber. after completion modifies rtcurseek.
X  returns : number of read(written) bytes or
X0 if end of file (endblk reached) or
X-1 if io fails */
X
X{int maxb;/*max numb of bytes could be read in */
X int actb;/*numb of bytes actually read in */
X int compb;/*number of free bytes on current block*/
Xif (endblk<= rtcurseek.sblkn) return(0);
Xcompb=512-rtcurseek.sbyte; 
Xmaxb= (endblk-rtcurseek.sblkn)*512-rtcurseek.sbyte;
Xif (nbytes==0) return(0);
Xif (maxb>0 && nbytes >maxb)nbytes=maxb;
Xif (iofl==0) /*test read or write*/
X  {if ((actb=read(rtdev.fdes,buffer,nbytes))== -1)return(-1);}
Xelse
X  if((actb=write(rtdev.fdes,buffer,nbytes))== -1)return(-1);
X/* here we compute new rtcurseek */
Xif (actb < compb) {rtcurseek.sbyte += actb;return(actb);}
Xactb -= compb;
Xrtcurseek.sblkn += actb/512+1;
Xrtcurseek.sbyte= actb % 512;
Xreturn(actb+compb);
X} /*rtphio end*/
X
X
X                      /*---rtpseek---*/
X
Xrtpseek(bloknu,bytnu)
X/*if rtcurseek differs from bloknu,bytnu then makes a seek
Xafter that modifies rtcurseek */
X{ if (rtcurseek.sblkn != bloknu || rtcurseek.sbyte != bytnu)
X        {
X#ifdef VAX
X        lseek(rtdev.fdes,bloknu*512 + bytnu,0);
X#else
X        seek(rtdev.fdes,bloknu,3);seek(rtdev.fdes,bytnu,1);
X#endif
X        rtcurseek.sblkn=bloknu;rtcurseek.sbyte=bytnu;
X        }
Xreturn;
X}
X
X
X                    /*---rtheader---*/
X
Xrtheader()/* processes rtheader of dir segments returns filestart-
X           adress referred by this segment */
X{int d;  char *cbuff;/*to use rtbuffer as char-array*/
X  cbuff=rtbuffer;
X  rtpseek(rtdirtab.nexts,0);
X  rtphio(0,cbuff,10,rtdirtab.nexts+2);/*read rtheader*/
X  rtdirtab.curs=rtdirtab.nexts;
X  rtdirtab.dirad[0]=rtdirtab.nexts;
X  rtdirtab.dirad[1]=10;
X  d=(rtdirtab.filst=((r_short *)rtbuffer)[4]);
X  if (rtdirtab.nexts==6) /*first segment*/
X    {rtdirtab.senumb[0]=((r_short *)rtbuffer)[0];
X      rtdirtab.senumb[1]=((r_short *)rtbuffer)[2];
X    }
X  rtdirtab.nexts=6+2*(((r_short *)rtbuffer)[1]-1);
X  rtdirtab.rtextb=((r_short *)rtbuffer)[3];
X  return(d);
X}
X
X                      /*---rtinitdir---*/
X
Xrtinitdir() /*init directory search */
X{ rtdirtab.curs=4;/*not real blocknumber since befor inited search
X                  ther is no current segment*/
X  rtdirtab.nexts=6;
X  rtheader();
X  return;
X}
X
X
X                     /*---rtserdir---*/
X
Xrtserdir(radname,filst,size,date,dirad)
X/*provides the attributes of the next directory entry */
X/*we implemented a side function here wich  is usefull
Xfor the rtadjust() function,i.e: this function fills out
Xthe rtsegfull table, for rtsegfull[i]=o if the i-th segment
Xis not full and 1 if it is */
Xr_short *radname;
Xint *filst,/*filestart*/ *size,*date,*dirad;/*current diradr*/
X/*func returns the status of file (permanent etc) and -1 if 
X  no more file could be found */
X{ int d;
X  char *cbuff;
X  cbuff=rtbuffer;
X  *filst=rtdirtab.filst;
X  dirad[0]=rtdirtab.dirad[0]; dirad[1]=rtdirtab.dirad[1];
X  rtpseek(rtdirtab.dirad[0],rtdirtab.dirad[1]);
X  rtphio(0,cbuff,14+rtdirtab.rtextb,rtdirtab.curs+2);
X  if(((r_short *)rtbuffer)[0]==04000) /*end of dirseg */
X    /*fill rtsegfull here*/
X    {if ((d=rtdirtab.dirad[0])%2==1
X           && rtdirtab.dirad[1]>=01000-14-rtdirtab.rtextb)
X       rtsegfull[(d-6)/2]=1;
X     else rtsegfull[(d-6)/2]=0;
X     if(rtdirtab.nexts==4)/*last segm*/ return(-1);
X     *filst=rtheader();
X     dirad[0]=rtdirtab.dirad[0];
X     dirad[1]=rtdirtab.dirad[1];
X     rtphio(0,cbuff,14+rtdirtab.rtextb,rtdirtab.curs+2);
X    }
X  /*modify rtdirtable*/
X  rtdirtab.filst += ((r_short *)rtbuffer)[4];
X  rtdirtab.dirad[0]=rtcurseek.sblkn;
X  rtdirtab.dirad[1]=rtcurseek.sbyte;
X  *size=((r_short *)rtbuffer)[4];
X  *date=((r_short *)rtbuffer)[6];
X  radname[0]=((r_short *)rtbuffer)[1];
X  radname[1]=((r_short *)rtbuffer)[2];
X  radname[2]=((r_short *)rtbuffer)[3];
X  return(((r_short *)rtbuffer)[0]);
X}
X
X
X                      /*---rtcondens,rtucondens ---*/
X
X/*rtcondens creates one dirsegm-adress offset from blockn byten
X  low bound 0, upper bound 2*0777; rtucondens does the invers conv*/
X
X
Xrtcondens(blkn,byten)
X{ 
X  if(blkn & 1)return(0777+byten);
X  else return(byten);
X}
X
Xrtucondens(blkn,offs) r_short *blkn/*addr of  loc contains  orig blkn*/;
X{int d,e;
X  d= *blkn &0177776;
X  if (offs/01000)
X    {d++;  e=offs % 01000; }
X  else e=offs;
X  *blkn=d;
X  *(blkn+1)=e;
X  return;
X}
X
X                      /*---rtadjust---*/
X
Xrtadjust(fildes,radname) r_short  *radname;
X/*it makes the opened file referenced by fildes to  be tentative
X  and creates an empty entry right after it.
X  if the segment is full, opens a new one and copies the half
X  of the old one into the new one. updates the directiry ad-
X  resses in rtttabl.dira-s if necessary. and now stats with the
X  normal rtadjust proc described above and wich assumes that the
X  directory is not full. rtadjust returns -1 if new segment
X  can not be opened though required,
X  and 0 in any other cases*/
X{int nextsegm,sbuf,d,e,filst,i,f,g,acl,c;
X  char *cbuff;
X  d=(rttabl[fildes].dira[0]-6)/2;/*make segmnumb from discadr*/
X  cbuff=rtbuffer;
X  if(rtsegfull[d]) /*sgm is full*/
X    {
X     if(rtdirtab.senumb[0]==rtdirtab.senumb[1])return(-1);
X
X    /*read the rtheader of dirsegm*/
X    rtpseek(d=d *2+6,0);
X    rtphio(0,cbuff,10,d+1);
X    rtextb=((r_short *)rtbuffer)[3];
X    filst=((r_short *)rtbuffer)[4];
X    nextsegm=((r_short *)rtbuffer)[1];
X    /*update the nextsegm pointer*/
X    sbuf = ++rtdirtab.senumb[1];
X    rtpseek(d,2);
X    rtphio(1,(char *)&sbuf,2,d+1);
X    rtpseek(6,4);
X    rtphio(1,(char *)&sbuf,2,7);
X    /*compute where the first entry of the second half starts, 
X      because we will copy from here. result is in e*/
X    e=14+rtextb-(502%(14+rtextb));
X    /*compute the accumulated length of the first half to
X      establish filestart-address for the second half*/
X    rtpseek(d,0);
X    rtphio(0,cbuff,512+e,d+2);
X    acl=0;
X    for(i=9;i<(512+e)/2;i += (14+rtextb+1)/2)
X      acl += ((r_short *)rtbuffer)[i];
X    /*now read the second half to the rtbuffer but leave space for the rtheader*/
X    rtpseek(d+1,e);
X    c=rtphio(0,cbuff+10,512,d+2);
X    /*fill out rtheader */
X    ((r_short *)rtbuffer)[0]=rtdirtab.senumb[0];
X    ((r_short *)rtbuffer)[1]=nextsegm;
X    ((r_short *)rtbuffer)[2]=rtdirtab.senumb[1];
X    ((r_short *)rtbuffer)[3]=rtextb;
X    /*compute new filst */
X    ((r_short *)rtbuffer)[4]=filst +acl;
X    /*now we write the rtbuffer to the new segment */
X    rtpseek(g=(rtdirtab.senumb[1]-1)*2+6,0);
X    rtphio(1,cbuff,c+10,g+2);
X    /*close the old segment*/
X    rtpseek(d+1,e);
X    sbuf=04000;
X    rtphio(1,(char *)&sbuf,2,d+2);
X    /*now modify the rttabl.dira-s*/
X    for(i=0;i!=numop+2;i++)
X      if(rttabl[i].dira[0]==d+1 && rttabl[i].dira[1]>=e)
X        {
X        rttabl[i].dira[0]=g;
X        rttabl[i].dira[1] -= e-10;
X        }
X    }
X
X  /*here we do the rtadjust in case of not full segment*/
X  /*read from the empty entry but leave space for the tentative
X    entry in the rtbuffer*/
X  rtpseek(rttabl[fildes].dira[0],rttabl[fildes].dira[1]);
X  rtphio(0,cbuff+14+rtextb,1024-14-rtextb,rttabl[fildes].dira[0]+2);
X  /*fill out the tentative entry in the rtbuffer */
X  ((r_short *)rtbuffer)[0]=0400;
X  ((r_short *)rtbuffer)[1]=radname[0];
X  ((r_short *)rtbuffer)[2]=radname[1];
X  ((r_short *)rtbuffer)[3]=radname[2];
X  ((r_short *)rtbuffer)[6]=0; /*date */
X  /*write back */
X  rtpseek(rttabl[fildes].dira[0],rttabl[fildes].dira[1]);
X  rtphio(1,cbuff,1024,rttabl[fildes].dira[0]+2);
X
X
X  /*modify rttabl.dira-s*/
X  d=rtcondens(rttabl[fildes].dira[0],rttabl[fildes].dira[1]);
X  e=rttabl[fildes].dira[0] &0177776;
X  for(i=0;i!= numop+2;i++)
X    {
X    if(i==fildes)continue;
X    if(rttabl[i].dira[0]== e||rttabl[i].dira[0]==e+1)
X     if((f=rtcondens(rttabl[i].dira[0],rttabl[i].dira[1]))>d)
X        {
X        f=f+14+rtextb;
X        rtucondens((rttabl[i].dira[0]),f);
X        }
X    }
X  return(0);
X}
X
X
X                      /*---rtcopy---*/
X
Xrtcopy(old,new,size)/*phis copy size in blocks */
X{
X  char *cbuff; int i;
X  cbuff=rtbuffer;
X  for(i=0;i!=size-1;i++)
X    {
X    rtpseek(old+1,0);
X    rtphio(0,cbuff,512,old+i+1);
X    rtpseek(new+i,0);
X    rtphio(1,cbuff,512,new+i+1);
X    }
X  return;
X}
X/************************************************************/
X/* now we start coding  rtun routines                        */
X
X                      /**********/
X                      /*rtmount */
X                      /**********/
X
Xrtmount(name,rwflag) char *name;
X{struct rtinode *i;
Xint srwf;
Xif(rtdev.fdes != -1) return(-1);
Xsrwf=(rwflag==1)?2:rwflag;
Xif((rtdev.fdes=open(name,srwf)) == -1)return(-1);/*open spec file*/
X/*seek to the start of rt-11 directory*/
X#ifdef VAX
Xlseek(rtdev.fdes,6*512,0);
X#else
Xseek(rtdev.fdes,6,3);
X#endif
X/*initialise some external values */
Xfor (i=rttabl;i!= &rttabl[numop+2];i++)
X  {i->opflg= -1;i->seekp[0]= 0;i->seekp[1]=0;}
Xrtdev.rwflag= rwflag;
Xrtcurseek.sblkn=06;
Xrtcurseek.sbyte =0;
Xrtwrop= -1;
Xreturn(rtdev.fdes);
X}
X
X
X                      /**********/
X                      /*rtumount*/
X                      /**********/
X
Xrtumount()
X{if(rtdev.fdes != -1)
Xclose(rtdev.fdes);
Xrtdev.fdes= -1;
Xreturn;
X}
X
X
X
X                      /**********/
X                      /*rtopen  */
X                      /**********/
X
X
Xrtopen(name,mode) char *name;
X{ int i,j,emptfl,permfl,retur/*temporary for the radname */;
X  int size,filst,date,dirad[2],/*for rtserdir call*/d;
X  r_short temprad[3],radname[3];
X  if (rtgetname(name,temprad)== -1)return(-1);/*invalid name */
X  switch(mode)
X    {
X    case writfl:;
X    case updatfl:
X      if(rtwrop!= -1)/*there is file opened for write or update*/
X        return(-1);
X      if(rtdev.rwflag==readfl)return(-1);/*device mounted for read*/
X    case readfl:
X      /*search for free rtinode */
X      for (i=0; i!=numop+1; i++)
X        {if (rttabl[i].opflg== -1) goto opencan;/*free rtinode found*/
X        }
X     /*no free rtinode */
X      default: return(-1);/* invalide mode*/
X    }
X  opencan: /*start open-procedure*/
X  switch(mode)
X    {
X    case writfl:;
X    case updatfl:rtwrop = i /*set fildescr to be written */;
X                rttabl[numop+1].filsiz=0;
X    case readfl:
X      retur=i;/*set fildescr to be returned*/
X     rttabl[i].opflg=mode;
X     rttabl[i].filsiz=0;
X    }
X/*look for the specified name and for empty space(in case of write)*/
X  rtinitdir();
X  emptfl=0;/*will be set if empty entry found */
X  permfl=0;/*will be set if permanent entry found*/
X/*
X * Feb 82 D. Tso
X *	Mask out 0100000 bit in directory entry
X */
X  while((d=rtserdir(radname,&filst,&size,&date,dirad))!= -1)
X    { switch (d&(~0100000))
X      {case 01000:/*empty entry*/
X          if(mode==readfl)goto endwhile;
X         emptfl=1;i=retur;break;
X        case 02000:/*permanent entry*/
X          for(i=3;i--;)/*compare filenames*/
X            if(radname[i]!=temprad[i]) goto endwhile;
X          if(mode==readfl)
X            {i=retur;permfl=1;}
X          else
X            {i=numop+1;permfl=1;rtoldfl=1;}
X          break;
X        default: goto endwhile;
X      }
X    if(rttabl[i].filsiz<size)/*incase of raed always true*/
X      {
X        rttabl[i].pblkn=filst;
X        rttabl[i].filsiz=size;
X        rttabl[i].seekp[0]=filst;
X        rttabl[i].seekp[1]=0;
X        rttabl[i].dira[0]=dirad[0];
X        rttabl[i].dira[1]=dirad[1];
X        if(mode==readfl)break;
X      }
X      endwhile:;
X    }
Xswitch   (mode)
X    {case readfl:
X       if(permfl==0)return(-1);
X     rttabl[retur].curend[0]=rttabl[retur].pblkn+rttabl[retur].filsiz;
X       rttabl[retur].curend[1]=0;
X       break;
X     case writfl:
X       if(emptfl==0)
X       return(-1);
X       rttabl[retur].curend[0]=rttabl[retur].pblkn;
X       rttabl[retur].curend[1]=0;
X       if(rtadjust(retur,temprad)== -1)/*no dir-space aval*/
X       return(-1);
X       break;
X     case updatfl:
X       if(emptfl==0 || permfl==0)return(-1);
X     rttabl[retur].curend[0]=rttabl[retur].pblkn+rttabl[numop+1].filsiz;
X       rttabl[retur].curend[1]=0;
X       if(rtadjust(retur,temprad)== -1)return(-1);
X       rtcopy(rttabl[numop+1].pblkn,rttabl[retur].pblkn,
X              rttabl[numop+1].filsiz);
X     }
Xreturn(retur);
X}
X
X
X                      /**********/
X                      /*rtclose */
X                     /**********/
X
Xrtclose(fildes)
X{int mode,sbuf,s,p,i;
X  int tentsize;/*size of tentative becoming permanent*/
X  int empsize;/*size of the following empty space*/
X  mode=rttabl[fildes].opflg;
X  if(mode== -1)return(-1);
X  switch (mode)
X    {case writfl:;
X      case updatfl:
X       rtwrop= -1;
X       /*delete old file if there is any*/
X      if(rtoldfl)
X       {rtpseek(rttabl[numop+1].dira[0],rttabl[numop+1].dira[1]);
X         sbuf=01000;
X         rtphio(1,(char *)&sbuf,2,rtcurseek.sblkn+1);
X         rtoldfl=0;
X       }
X/*make tentative to be permanent*/
X
X     rtpseek(rttabl[fildes].dira[0],rttabl[fildes].dira[1]);
X     sbuf=02000;
X     rtphio(1,(char *)&sbuf,2,rtcurseek.sblkn+1);
X/*compute size of tentative and that of the following
X  empty one and put them to the directory*/
X     empsize=rttabl[fildes].filsiz;
X     tentsize=rttabl[fildes].curend[0]-rttabl[fildes].pblkn;
X     if(s=rttabl[fildes].curend[1])/*clear resrt of block*/
X       {
X       tentsize++;
X       rtpseek(p=rttabl[fildes].curend[0],s);
X       sbuf=0;
X       while(rtphio(1,(char *)&sbuf,2,p+1));
X       }
X     empsize -= tentsize;
X     rtpseek(rttabl[fildes].dira[0],rttabl[fildes].dira[1]+8);
X     rtphio(1,(char *)&tentsize,2,rtcurseek.sblkn+1);
X     rtpseek(rttabl[fildes].dira[0],rttabl[fildes].dira[1]+22+rtextb);
X     rtphio(1,(char *)&empsize,2,rtcurseek.sblkn+1);
X    case readfl:
X     rttabl[fildes].opflg= -1;
X   }
X  return(0);
X}
X
X
X                      /**********/
X                      /*rtread   */
X                      /**********/
X
Xrtread(fildes,buffer,nbytes) char *buffer;
X{int retur;
X  if(rttabl[fildes].opflg==writfl)return(-1);
X  rtpseek(rttabl[fildes].seekp[0],rttabl[fildes].seekp[1]);
X
X  retur=rtphio(0,buffer,nbytes,rttabl[fildes].curend[0]);
X  rttabl[fildes].seekp[0]=rtcurseek.sblkn;
X  rttabl[fildes].seekp[1]=rtcurseek.sbyte;
X  return(retur);
X}
X
X
X                      /**********/
X                      /*rtwrite  */
X                      /**********/
X
Xrtwrite(fildes,buffer,nbytes)char *buffer;
X{int retur;
X   if(rttabl[fildes].opflg==readfl)return(-1);
X   rtpseek(rttabl[fildes].seekp[0],rttabl[fildes].seekp[1]);
X retur=rtphio(1,buffer,nbytes,rttabl[fildes].pblkn+rttabl[fildes].filsiz);
X   rttabl[fildes].seekp[0]=rtcurseek.sblkn;
X   rttabl[fildes].seekp[1]=rtcurseek.sbyte;
X   if(rtcurseek.sblkn>rttabl[fildes].curend[0])
X     {rttabl[fildes].curend[0]=rtcurseek.sblkn;
X       rttabl[fildes].curend[1]=rtcurseek.sbyte;
X    }
X   else if (rtcurseek.sblkn ==rttabl[fildes].curend[0])
X          if (rtcurseek.sbyte > rttabl[fildes].curend[1])
X             rttabl[fildes].curend[1]=rtcurseek.sbyte;
X   return(retur);
X}
X
X
X
X                      /**********/
X                      /*rtdelete*/
X                      /**********/
X
X
Xrtdelete(name) char *name;
X{int mess,/*dummy*/dirad[2];/*for rtserdir calls*/
X  r_short tempname[3],radname[3];
X  int sbuf,i,d;
X  if(rtgetname(name,radname)== -1)return(-1);
X  rtinitdir();
X  while((d=rtserdir(tempname,&mess,&mess,&mess,dirad))!= -1)
X    {
X     for(i=0; i!= 3; i++)
X        if(tempname[i]!=radname[i])goto endwhile;
X     if(d!= 02000)goto endwhile;/*file is not permanent*/
X     rtpseek(dirad[0],dirad[1]);
X     sbuf=01000;
X     rtphio(1,(char *)&sbuf,2,dirad[0]+1);
X     return(0);
X     endwhile:;
X    }
X  return(-1);/*given filename not found*/
X}
X
All work and no play makes Jack a dull boy
echo ./rtpip/rtwrite.2
sed 's/^X//' > ./rtpip/rtwrite.2 << 'All work and no play makes Jack a dull boy'
X.th RTWRITE II 1/1/76
X.sh NAME
Xrtwrite *- write on RT-11 file
X.sh SYNOPSIS
X.br
X.ft B
Xrtwrite (fildes, buffer, nbytes)
X.br
Xchar *buffer;
X.br
X.ft R
X.sh DESCRIPTION
XA file descriptor is a word returned from a successful
X.it rtopen.
X.it Buffer
Xis the address of
X.it nbytes
Xcontiguous bytes which are written on the output
Xfile.
XUnlike the normal UNIX 
X.it write
Xfunction, an end-of-file can occur because
XRT-11 has contiguous files of finite size. Therefore
Xthe returned value indicates the number of characters
Xactually written.
X.s3
XWrites which are multiples of 512 characters long
Xand begin on a 512-byte boundary in the file are more
Xefficient than any others.
X.sh FILES
XIn Nymegen,
X.it rtwrite
Xis in the C-library (member name RTUN.O).
X.sh "SEE ALSO"
Xwrite(II), rtpip(I), rtmount(II), rtumount(II), rtopen(II), rtclose(II),
Xrtread(II), rtdelete(II)
X.sh AUTHOR
XT. Forgacs, Informatics Dept. Nymegen Univ. the Netherlands.
X.sh DIAGNOSTICS
XFrom C, a -1 value is returned on error: e.g. bad descriptor,
Xbuffer address or count, physical I/O errors etc.
X.sh GLOBALS
XDo not use the following external names:
X.S3
Xrttabl, rtdev, rtcurse, rtdirta, rtbuffe,
Xrtwrop, rtoldfl, rtsegfu, rtextb, rtconvr,
Xrtgetna, rtphio, rtpseek, rtheade, rtinitd,
Xrtserdi, rtconde, rtucond, rtadjus.
X.sh BUGS
All work and no play makes Jack a dull boy
echo ./tu58.1
sed 's/^X//' > ./tu58.1 << 'All work and no play makes Jack a dull boy'
X.TH TU58 1 "21 January 1984"
X.UC 4
X.SH NAME
Xtu58 \- emulate a tu58 tape drive over a serial line
X.SH SYNOPSIS
X.B tu58 
X[
X\-options
X] serial_device file1 
X[
Xfile2
X]
X.SH DESCRIPTION
X.I Tu58
Xwill read and decode input from 
X.I serial_device
Xaccording to the radial serial protocol. It will act in
Xaccord with this protocol using 
X.I file1
Xand\/or
X.I file2
Xas a storage device. In this way these files resemble tu58 tapes.
X.PP
X.I Tu58
Xoptions include:
X.TP
X.B \-c
XCreate the files.
X.TP
X.B \-cn
XCreate file number n.
X.TP
X.B \-i
XInitialize the files as rt11 directories with no entries.
X.TP
X.B \-in
XInitialize file number n as an rt11 directory with no entries.
X.TP
X.B \-r
XFiles are read only.
X.TP
X.B \-rn
XFile number n is read only.
X.TP
X.B \-sn
XSet the speed for
X.I serial_device
Xat n baud.
X.PP
XIn place of
X.I serial_device
Xa \- can be used to indicate i/o from the standard input and output.
XIn addition error output is always sent to the standard error output.
X.SH FILES
X.ta 2i
X/usr/local/tu/dk\?	files in rt11 format suitable as tu58 files
X.br
X.SH "SEE ALSO"
Xrtpip(1)
X.SH BUGS
XIt can only output at 9600 baud. It has to be used at 4800 baud for
Xboth input and output. 
All work and no play makes Jack a dull boy
echo ./tu58.c
sed 's/^X//' > ./tu58.c << 'All work and no play makes Jack a dull boy'
X#include "tu58.h"
X#include <stdio.h>
X#include "debug.h"
X
X extern int file[]; 
X extern char ronly[];
X
X int state = TUS_IDLE;
X struct packet pk, dk, ek;
X
X/*
X * get requests from host
X */
X
Xtu58()
X{
X	dk.pk_flag = TUF_DATA;		/* only one data packet flag */
X	ek.pk_flag = TUF_CMD;
X	ek.pk_mcount = CMDLEN;
X	ek.pk_sw = 0;
X	ek.pk_seq = 0;
X
X	while( state = evalflag() );
X}
X
Xevalflag()		/* get flag & evaluate type of packet & execute */
X{
X	pk.pk_flag = cget();
X	debugd("flag:",pk.pk_flag);
X	switch (pk.pk_flag) {
X		case EOF:		/* finished */
X			return TUS_QUIT;
X		case TUF_NULL:		/* send during an break & init */
X			break;
X		case TUF_DATA:
X			if( state & TUS_HTT ) {
X				if( hosttu() )
X					return TUS_IDLE;
X				return TUS_HTT;
X			}
X			fprintf(stderr, "Data packet not expected\n");
X			break;
X		case TUF_CMD:			/* control packet */
X			if( state & TUS_IDLE ) {
X				state = TUS_HTT;
X				return cmd();
X			}
X			fprintf(stderr, "Command pack not expected\n");
X			break;
X		case TUF_INITF:			/* a 'single byte' packet */
X			cget();	/* discard first initf char (p 3-3) */
X			clearbuf();
X			cput(TUF_CONT);   /* send back 'single byte' */
X			cflush();
X			break;
X		case TUF_BOOT:
X			fprintf(stderr,"will read in boot block\n");
X			bootio(file[cget()]);
X			break;
X		case TUF_CONT:			/* a 'single byte' packet */
X		case TUF_XON:
X			state &= ~TUS_WAIT;
X			return state;
X		case TUF_XOFF:			/* a 'single byte' packet */
X			state |= TUS_WAIT;
X			return state;
X		default:
X			fprintf(stderr, "Unknown packet flag %o\n", pk.pk_flag);
X			cput(TUF_INITF);
X			cput(TUF_INITF);
X			cflush();
X	}
X	return TUS_IDLE;
X}
X
Xcmd()			 /* judge op code of command packet */
X{
X	pk.pk_mcount = cget();		/* mcount = CMDLEN */
X	if ((pk.pk_mcount < 0) || /* sanity check mcount */
X	  (pk.pk_mcount > (sizeof(struct packet)-(sizeof(int)+sizeof(char))))) {
X		debugd("bad mcount in cmd",pk.pk_mcount);
X		return TUS_IDLE;
X	}
X	/* bloody hack follows:
X		A pointer into a packet struct is passed to getpacket (arg2)
X		These elements should be acquired one by one and placed
X			more delicately into the struct, but this works
X			if the struct is packed, and bytes are in vax
X			order, etc.
X	*/
X	if( !getpacket(&pk,(u_char *)&pk.pk_op) ) {
X		debugs("cmd: checksum err");
X		return TUS_IDLE;
X	}
X
X	debugd("op code:",pk.pk_op);
X	switch(pk.pk_op){
X	case TUOP_NOOP:
X		state = TUS_TTH;
X		sendend( 0, TUE_SUC, 0, 0);
X		break;
X	case TUOP_INIT:
X		clearbuf();
X	case TUOP_DIAGNOSE:
X	case TUOP_END:
X		state = TUS_TTH;
X		sendend( TUOP_END, TUE_SUC, 0, 0);
X		break;
X	case TUOP_READ:
X		state =  TUS_TTH;
X		debugd("block",pk.pk_block); debugd("count",pk.pk_count);
X		if( fposition(pk.pk_unit,pk.pk_mod,pk.pk_block) ) {
X			frclear();
X			tuhost();
X		}
X		break;
X	case TUOP_WRITE:
X		if( ronly[pk.pk_unit] ) {
X			state =  TUS_TTH;
X			sendend(TUOP_END, TUE_WPRO,0,0);
X			break;
X		}
X		fposition(pk.pk_unit,pk.pk_mod,pk.pk_block);
X		ek.pk_count = 0;
X		fwclear();
X		cput(TUF_CONT); cflush();
X		return TUS_HTT;
X	case TUOP_SEEK:
X		state = TUS_TTH;
X		if( fposition(pk.pk_unit,pk.pk_mod,pk.pk_block) )
X			sendend(TUOP_END, TUE_SUC,0,0);
X		break;
X	default:
X		fprintf(stderr,"Incorrect op code %o\n",pk.pk_op);
X		state = TUS_TTH;
X		sendend( TUOP_END, TUE_BADO, 0, 0);
X	}
X	return TUS_IDLE;
X}
X
Xsendend(op, succ,count,sum)		/* tu58 sends end packet to host */
Xchar op, succ; u_short count, sum;
X{
X	ek.pk_op = op;
X	ek.pk_succ = succ;
X	ek.pk_unit = pk.pk_unit;
X	ek.pk_count = count;
X	ek.pk_stat = sum;
X	debugx("sendend: succ",succ);
X	/* see comment below in cmd() re: getpacket */
X	sendpacket( &ek, (u_char *)&ek.pk_op);
X	return 1;
X}
X
Xtuhost()				/* host reads from tu58 */
X{
X	register int cnt, peak;
X	char *cp, *fread();
X
X	debugd("tuhost: count",pk.pk_count);
X	for( cnt = pk.pk_count; cnt>0; cnt -= DATALEN) {
X		dk.pk_mcount = DATALEN < cnt ? DATALEN : cnt;
X		if( (cp = fread(pk.pk_unit)) )
X		   sendpacket(&dk,(u_char *)cp);
X		else {
X		   sendend(TUOP_END, TUE_DERR,pk.pk_count - cnt,0);
X		   return;
X		}
X#ifndef PWBTTY
X		if( (peak = cpeak()) != -1 && (peak == 0 || peak == TUF_INITF) )
X			return;
X#endif
X	}
X	sendend(TUOP_END, TUE_SUC, pk.pk_count, 0);
X}
X
Xhosttu()				/* host writes to tu58 */
X{
X	char *cp, *fwrite();
X
X	if( (dk.pk_mcount = cget()) > DATALEN )
X		dk.pk_mcount = DATALEN;
X	debugd("hosttu: total count",0377&dk.pk_mcount);
X	if( !(cp = fwrite(pk.pk_unit)) ) {
X		debugs("hosttu: write error");
X		sendend(TUOP_END, TUE_DERR,ek.pk_count,0);
X		return 0;
X	}
X	if( !getpacket( &dk, (u_char *)cp) ) {
X		debugs("hosttu: Checksum error");
X		return 0;
X	}
X	ek.pk_count += 0377&dk.pk_mcount;
X	pk.pk_count -= 0377&dk.pk_mcount;
X	if(pk.pk_mod & TUMD_WRV);	/* verify write */
X	
X	if(pk.pk_count > 0) {
X		cput(TUF_CONT); cflush();
X		return 0;
X	}
X
X	if( fwflush(pk.pk_unit) ) {
X		if(pk.pk_mod & TUMD_WRV);	/* verify write */
X		sendend(TUOP_END, TUE_SUC,ek.pk_count,0);
X	}
X	else
X		sendend(TUOP_END, TUE_DERR,ek.pk_count,0);
X	return 1;
X}
X
Xclearbuf()
X{
X	state &= ~(TUS_TTH | TUS_HTT );	    /* stop transfer to io buffers */
X	pk.pk_count = 0;		/* zero command count */
X	clearcbuf();			/* empty serial line io buffers */
X}
All work and no play makes Jack a dull boy
echo ./tu58.h
sed 's/^X//' > ./tu58.h << 'All work and no play makes Jack a dull boy'
X/*
X * TU58 Radial Serial Protocol
X */
X
X#define CMDLEN	10		/* # of chars after mcount & before chksum */
X#define DATALEN 128		/* max # chars after  "		"	"  */
X
X#define	u_char	char
X#define	u_short	unsigned short
X
X/* kc 7/85 */
X/* pk_flag changed to int so that char->int of 0377 in tu58 doesn't yield -1 */
X
X/*
X * Command packet
X */
Xstruct packet {
X	int	pk_flag;	/* packet type */ /* was u_char kc 7/85 */
X	u_char	pk_mcount;	/* message count */
X	int	pk_align;	/* This should align things so that there
X					isn't a hole following pk_sw
X				   Proper alignment is required */
X	u_char	pk_op;		/* operation code */
X	u_char	pk_mod;		/* modifier */
X	u_char	pk_unit;	/* unit number */
X	u_char	pk_sw;		/* switches */
X	u_short	pk_seq;		/* sequence number, always zero */
X	u_short	pk_count;	/* byte count for read or write */
X	u_short	pk_block;	/* block number for read, write, or seek */
X	u_short	pk_chksum;	/* checksum */
X};
X
X#define pk_succ	pk_mod		/* defs for end packet */
X#define pk_stat	pk_block
X
X/*
X * States
X */
X#define	TUS_QUIT	0	/* exit tu58 */
X#define	TUS_IDLE	1	/* initialized, no transfer in progress */
X#define	TUS_TTH		2	/* tu58 to host */
X#define	TUS_HTT		3	/* host to tu58 */
X#define	TUS_WAIT	4	/* waiting for continue */
X
X/*
X * Packet Flags
X */
X#define TUF_NULL	0
X#define	TUF_DATA	1		/* data packet */
X#define	TUF_CMD		2		/* command packet */
X#define	TUF_INITF	4		/* initialize */
X#define TUF_BOOT	010		/* boot */
X#define	TUF_CONT	020		/* continue */
X#define TUF_XON		021		/* flow control */
X#define	TUF_XOFF	023		/* flow control */
X
X/*
X * Op Codes
X */
X#define	TUOP_NOOP	0		/* no operation */
X#define	TUOP_INIT	1		/* initialize */
X#define	TUOP_READ	2		/* read block */
X#define	TUOP_WRITE	3		/* write block */
X#define	TUOP_SEEK	5		/* seek to block */
X#define TUOP_DIAGNOSE	7		/* run micro-diagnostics */
X#define	TUOP_END	0100		/* end packet */
X
X/*
X * Mod Flags
X */
X#define TUMD_WRV        1               /* write with read verify */
X#define TUMD_128	0x80		/* special addressing mode */
X
X/*
X * End packet success codes
X */
X#define TUE_SUC	0
X#define TUE_PAR 0376			/* failed at end of medium */
X#define TUE_BADU 0370			/* bad unit */
X#define TUE_BADF 0367			/* no cartridge (file) */
X#define TUE_WPRO 0365			/* write protected */
X#define TUE_DERR 0357			/* data check error */
X#define TUE_SKRR 0340			/* seek error */
X#define TUE_BADO 0320			/* bad op code */
X#define TUE_BADB 0311			/* bad block number */
X
X#define	NTU58	2			/* Number of drives to emulate */
All work and no play makes Jack a dull boy