subroutine rpl C.. RPL - Replace File Copy. C.. C.. Copyright (c) 1987, Richard A. Holmes (aka Holmes Compleat Computing) C.. 4845 San Sebastian Avenue C.. Las Vegas, Nevada 89121 C.. (702) 458-4933 C.. C.. Public domain. Permission is hereby granted for non-commercial use C.. and distribution of this program. No warranties, guarantees or promises C.. are made as to its functioning. It works for me. If you find it C.. useful, you may feel free to send $5.00 to the above address. C.. C.. This program is used to copy a disk file to another disk file. C.. If the destination file already exists, it will be overwritten with C.. the new one. It is neither first deleted and then created, nor is C.. any renaming performed. This is useful to copy new versions of C.. programs onto a carefully laid out disk. If the new file is longer C.. than the old one, first (nominally) 4K of disk space is reserved C.. (presumably on the inner tracks) and then the new sectors will C.. come from CP/Ms standard allocation of disk space. If the old file C.. is longer than the new file, unused sectors will exist at the end C.. of the file. C.. If you modify this code, please don't use REAL variables or C.. any sort of FORTRAN Reads or Writes. include rpl.dcl integer fcount include rpl.blk data fcount /0/, nomore /.false./ C.. Say hi. call strout('RPL - Replace Copy V1.0 $') call strend(' (c) 1987, Holmes Compleat Computing$') C.. General program initialization. call inirun C.. Copy the file(s). 100 continue call setfil(nomore) if (nomore) go to 200 call cpyini call copy fcount = fcount + 1 if (ambig) go to 100 C.. That's all folks. 200 continue call endrun end subroutine copy C.. This feller does the copying of the file. integer size,inrec,outrec,maxsec,curptr,cur1,cursec byte rsectr,stat,wsectr include rpl.dcl include rpl.blk C.. Open the input file. call setusr(iuser) call opin C.. Open the output file. call setusr(ouser) call opout C.. Initialize the counters. maxsec = 100 inrec = -1 outrec = -1 90 continue call setusr(iuser) curptr = -128 cursec = 0 100 continue curptr = curptr + 128 if (curptr.ge.12800) go to 110 inrec = inrec + 1 stat = rsectr(curptr,inrec) if (stat.lt.0) go to 900 if (stat.gt.0) go to 800 cursec = cursec + 1 go to 100 C.. The buffer filled up. Write it to disk. 110 continue call setusr(ouser) curptr = -128 do 120 i = 1,maxsec outrec = outrec + 1 curptr = curptr + 128 stat = wsectr(curptr,outrec) if (stat.ne.0) go to 700 115 continue 120 continue go to 90 C.. Error on writing. 700 continue call ioerr(stat) call chrout(10) call chrout(13) call strout (' Unknown I/O error $') call decbyt(stat) call strout(' in writing sector $') call decint(outrec) call strend('$') call endrun C.. Error on reading. 800 continue call ioerr(stat) call strout (' Unknown I/O error $') call decbyt(stat) call strout(' in reading sector $') call decint(outrec) call strend('$') call endrun C.. End of file on input. Flush out the buffer. 900 continue call setusr(ouser) inrec = inrec - 1 if (curptr.le.0) go to 920 curptr = -128 do 910 i = 1,cursec curptr = curptr + 128 outrec = outrec + 1 stat = wsectr(curptr,outrec) if (stat.ne.0) go to 700 910 continue C.. Close the output file. 920 continue call setusr(ouser) call clout C.. Report on status of copy. if (insize.eq.osize) go to 930 call strout(' -- $') if (insize.gt.osize) go to 940 if ((osize-insize).ne.1) go to 925 call strend('1 unused sector exists.$') go to 990 925 continue call decint(osize - insize) call strend(' unused sectors exist.$') go to 990 C.. Both files had the same size. 930 continue if (insize.ne.0) go to 935 call strend(' -- File is vacuous.$') go to 990 935 continue call strend('$') go to 990 940 continue if (insize-osize.ne.1) go to 950 call strend('1 new sector written.$') go to 990 950 continue call decint(insize-osize) call strend(' new sectors written.$') 990 continue return end subroutine ioerr(stat) C.. Show what an I/O error means. End program if code is known. C.. Return otherwise. byte stat if ((stat.lt.1).or.(stat.gt.6)) return call chrout(13) call chrout(10) call strout(' I/O error - $') if (stat.eq.1) call strend + ('reading unwritten data.$') if (stat.eq.2) call strend + ('disk is full.$') if (stat.eq.3) call strend + ('cannot close current extent.$') if (stat.eq.4) call strend + ('seek to unwritten extent.$') if (stat.eq.5) call strend + ('directory space exhaused.$') if (stat.eq.6) call strend + ('seek past physical end of disk.$') call endrun end subroutine inirun C.. Parse the command string. include rpl.dcl byte blank,peek,sfirst,status,str(7),stat byte svfile(11) integer start,maxpos,ksize,fsize,reserv logical iambig,oambig,badopt common /svfile/svfile /reserv/ reserv include rpl.blk include rpl.dat data blank /1h / data str /6*1h ,1h$/ data ambig /.false./ C.. Start picking characters from 82H in memory. start = x'82' C.. Set the maximum position to be examined. maxpos = peek(x'80') maxpos = maxpos + 128 C.. Get the output file specification. call fspec(outfil,outdev,start,maxpos,ouser,oambig) if (oambig) go to 80 if ((outdev.eq.0).and.(outfil(1).eq.blank)) go to 80 C.. Get the input file specifications. call fspec(infile,indev,start,maxpos,iuser,iambig) if (.not.iambig) go to 60 if (outfil(1).ne.blank) go to 80 ambig = .true. do 50 i = 1,11 svfile(i) = infile(i) 50 continue C.. The source file may not be omitted. 60 continue if (infile(1).ne.blank) go to 90 C.. Give mini-help on syntax type errors. 80 continue call help call endrun C.. If the destination file was omitted, copy the input file spec. 90 continue if (outfil(1).ne.blank) go to 200 do 100 i = 1,11 outfil(i) = infile(i) 100 continue C.. Check out the options. 200 continue badopt = .false. call setopt(start,maxpos,badopt) if (badopt) go to 80 if (ambig) call makres(reserv) return end subroutine cpyini C.. Initialize for a file copy. byte status,sfirst integer fsize,reserv include rpl.dcl common /reserv/ reserv include rpl.blk C.. See if the input file exists. 200 continue call setusr(iuser) status = sfirst(indev,infile) if (status.eq.-1) call error(' No files matched.$') 300 continue osize = 0 insize = fsize(indev,infile) C.. Determine how big the output file is now. call setusr(ouser) status = sfirst(outdev,outfil) if (status.eq.-1) go to 350 osize = fsize(outdev,outfil) 350 continue call strout(' ($') call decint((insize+7)/8) call strout('k)$') C.. Reserve space if necessary. if ((.not.ambig).and.(insize.gt.osize)) call makres(reserv) return end subroutine fspec(filnam,device,start,maxpos,usrnum,ambig) C.. This routine will extract a file specification from the command line. byte filnam(11),device,peek,equals,blank,dot,aa,char,colon byte usrnum,aster,hook integer start,pos,maxpos logical ambig data equals,blank,dot /1h=,1h ,1h./, aa /1hA/, colon /1h:/ data aster,hook /1h*,1h?/ ambig = .false. do 50 i = 1,11 filnam(i) = blank 50 continue C.. POS will reflect the current position in the command line. pos = start C.. Get the device and user number. call getdu(device,usrnum,pos,maxpos) if (pos.gt.maxpos) go to 600 C.. Gather up the file name. do 300 i = 1,9 char = peek(pos) C.. Check for ambiguous file name. if (char.eq.hook) ambig = .true. if (char.ne.aster) go to 70 do 60 ii = i,8 filnam(ii) = hook 60 continue ambig = .true. go to 80 C.. Blanks and equals terminate the file name. 70 continue if ((char.eq.blank).or.(char.eq.equals)) go to 500 C.. Dots start the extension. if (char.eq.dot) go to 400 filnam(i) = char 80 continue pos = pos + 1 if (pos.gt.maxpos) go to 600 300 continue call error(' File name is too long.$') C.. Put together the file extension. 400 continue pos = pos + 1 if (pos.gt.maxpos) go to 600 do 450 i = 1,3 char = peek(pos) if (char.eq.hook) ambig = .true. if (char.ne.aster)go to 420 do 410 ii = i,3 filnam(ii+8) = hook 410 continue ambig = .true. go to 430 420 continue if ((char.eq.blank).or.(char.eq.equals)) go to 500 filnam(i+8) = char 430 continue pos = pos + 1 if (pos.gt.maxpos) go to 600 450 continue C.. The end of the file spec was found. 500 continue 600 continue pos = pos + 1 510 continue start = pos return end subroutine getdu(device,usrnum,pos,maxpos) C.. Get device and usernumber of a file. byte device,usrnum,char,peek,char0,char9,chara,charp,colon integer pos,maxpos data char0,char9,chara,charp,colon /1h0,1h9,1hA,1hP,1h:/ C.. Default drive. call getdev(device) C.. Get current user number. call getusr(usrnum) if (pos.gt.maxpos) go to 900 C.. We should see a colon in the first four characters. do 10 i = 0,3 if (peek(pos+i).eq.colon) go to 20 10 continue go to 900 C.. Get the user number and device. 20 continue if (pos.gt.maxpos)go to 900 char = peek(pos) pos = pos + 1 if (char.eq.colon) go to 900 if (char.lt.char0) go to 40 if (char.gt.char9) go to 30 usrnum = usrnum*10 + (char-char0) go to 20 30 continue if ((char.ge.chara).and.(char.le.charp)) go to 50 40 continue call error('Bad device/user specified.$') 50 continue device = char - chara + 1 go to 20 900 continue if ((usrnum.gt.15).or.(usrnum.lt.0)) + call error (' User number is out of range.$') return end subroutine encdxx(string,positn,value) C.. This routine will encode the provided VALUE into a decimal C.. string of characters, stored in STRING, with the leftmost C.. in column POSITN. Leading blanks are used. Three digitss C.. are always used. VALUE is treated as an unsigned, integer C.. value. Value are obviously limited to 0 through 999. byte string(1),positn,zero,blank,d1,d2,d3 byte ascii(10) integer int,value data zero,blank /1h0,1h /,ascii/1h0,1h1,1h2,1h3,1h4, + 1h5,1h6,1h7,1h8,1h9/ C.. Isolate the individual digits. int = value d1 = int / 100 d2 = (int - d1*100) / 10 d3 = int / 10 d3 = int - (d2*10) - (d1*100) C.. Convert the digits to ASCII characters. d1 = ascii(d1+1) d2 = ascii(d2+1) d3 = ascii(d3+1) C.. Blank fill the leading zeros. if (d1.ne.ascii(1)) go to 100 d1 = blank if (d2.ne.ascii(1)) go to 100 d2 = blank 100 continue C.. Fill the characters in the user area. string(positn) = d1 string(positn+1) = d2 string(positn+2) = d3 return end subroutine encod6(string,pos,value) C.. This routine will convert a integer value to C.. ASCII for displaying. It is like ENCODE, but C.. uses six character positions. byte string(1),pos,chzero,blank,pos1 integer value,part1,part2 data chzero,blank/1h0,1h / C.. Isolate the two parts. (The base routine can handle C.. exactly three digits.) part1 = value / 1000 part2 = value - (part1 * 1000) pos1 = pos + 5 C.. Clear out the whole message area. do 100 i = pos,pos1 string(i) = blank 100 continue C.. Fill in the bottom three characters. call encdxx(string,pos+3,part2) C.. If the top three digits are all zero, we are done. if (part1.eq.0) go to 900 C.. Change leading blanks in the bottom three positions C.. to zeros. if (string(pos+4).eq.blank) string(pos+4) = chzero if (string(pos+3).eq.blank) string(pos+3) = chzero C.. Now do the top three digits. call encdxx(string,pos,part1) 900 continue return end subroutine decint(value) C.. Show integer in decimal form. integer value byte str(7),blank data str /6*1h ,1h$/,blank /1h / call encod6(str,1,value) do 100 i = 1,6 if (str(i).ne.blank) go to 200 100 continue 200 continue call strout(str(i)) return end subroutine decbyt(byte) C.. Show byte in decimal form. byte byte integer value,mask data mask /x'00ff'/ value = byte value = value.and.mask call decint(value) return end subroutine setfil(nomore) C.. This routine will set up the input and output FCBs C.. from the next file found which matches the ambiguous C.. specification from the user. When no more match, C.. NOMORE is returned true. include rpl.dcl logical nomore,first integer dirptr,fcount,reserv byte amb1st,ambnxt,code,nofile,blank,chara,status,sfirst byte svfile(11),fcb(36),dirsec(128) include rpl.blk common /svfile/svfile /reserv/ reserv data first /.true./,nofile /x'ff'/ data fcb /36*0/,blank /1h /,chara /1hA/ nomore = .false. if (ambig) go to 10 C.. This is not an ambiguous run. Allow only one pass through here. nomore = .true. if (.not.first) return nomore = .false. first = .false. C.. Make sure that the file exists. call setusr(iuser) status = sfirst(indev,infile) if (status.ne.-1) go to 500 call strend('Input file does not exist.$') call chrout(7) call endrun C.. This is an ambiguous run. The source file had wildcards. 10 continue if (.not.first) go to 400 C.. On the first call, scan through the directory for the specified C.. user and build a list of matching file names. Up to 255 are allowed. call setusr(iuser) do 50 i = 1,11 fcb(i+1) = svfile(i) 50 continue fcb(1) = indev first = .false. fcount = 0 C.. Find the first file using the ambiguous specification. code = amb1st(fcb) go to 200 C.. Find the next file. 100 continue code = ambnxt(fcb) 200 continue if (code.ne.nofile) go to 250 go to 350 C.. A file was found. Copy the file name from the directory sector. 250 continue dirptr = code*32 do 300 i = 1,11 infile(i) = peek(x'81'+dirptr) dirptr = dirptr + 1 300 continue C.. Stash the file name in the list. call putfil(infile) fcount = fcount + 1 go to 100 C.. If more than one file matched, tell ahead of time how many C.. files will be processed. 350 continue if (fcount.eq.0) go to 360 if (fcount.eq.1) go to 370 call strout(' $') call decint(fcount) call strend(' files will be processed.$') go to 400 C.. No files found in an wildcard specification. 360 continue call strend('No such file.$') call endrun 370 continue CCC call strend(' 1 file will be processed.$') C.. Reserve the saved space now. 380 continue call makres(reserv) C.. This run used ambiguous filespec. The file list has been build. C.. Extract the next file name. 400 continue call getfil(infile) do 450 i = 1,11 outfil(i) = infile(i) 450 continue if (infile(1).ne.0) go to 500 nomore = .true. return C.. Make sure we don't copy back onto ourself. This works ok but C.. is senseless. 500 continue if (indev.ne.outdev) go to 550 if (ouser.ne.iuser) go to 550 do 520 i = 1,11 if (outfil(i).ne.infile(i)) go to 550 520 continue call error (' Identity copy ignored.$') C.. Tell what is going on. 550 continue call strout(' Copying $') call chrout(outdev + chara-1) call decbyt(ouser) call chrout(':') call shofil(outfil) call strout(' <-- $') call chrout(indev + chara-1) call decbyt(iuser) call strout(':$') call shofil(infile) return end subroutine shofil(name) C.. This routine sends a file spec to the display. byte name(11),blank data blank /1h / do 100 i = 1,8 if (name(i).ne.blank) call chrout(name(i)) 100 continue if ((name(9).eq.blank).and.(name(10).eq.blank).and. + (name(11).eq.blank)) return call chrout('.') do 200 i = 9,11 if (name(i).ne.blank) call chrout(name(i)) 200 continue return end subroutine setopt(start,maxpos,error) C.. This routine parses the options following the source file spec. C.. If I knew how to do it, I would allow "T" to indicate trimming C.. off of extra sectors. "-T" would turn off trimming. If you come C.. up with a good way to do it, please let me know. byte ch0,ch9,cht,dash,chk,blank,dollar byte peek,char integer reserv,pos,start,maxpos,defres logical trim,error common /trim/ trim common /reserv/ reserv data ch0,ch9,chk,cht,dash,blank,dollar + /1h0,1h9,1hK,1hT,1h-,1h ,1h$/ data trim /.true./ C.. Change DEFRES on the next line for a new default reserve size. data reserv /0/,defres/4/ pos = start error = .false. C.. See if we are already beyond the end of line if (pos.gt.maxpos) go to 800 C.. Ignore leading blanks and wait for a dollar sign. 10 continue if (peek(pos).ne.blank) go to 20 pos = pos + 1 if (pos.gt.maxpos) go to 800 20 continue if (peek(pos).ne.dollar) go to 900 C.. We have the dollar sign. Check out what is after it. 100 continue pos = pos + 1 if (pos.gt.maxpos) go to 800 char = peek(pos) CCC if (char.eq.cht) go to 200 CCC if (char.eq.dash) go to 300 if ((char.ge.ch0).and.(char.le.ch9)) go to 400 if (char.eq.chk) go to 100 go to 900 C.. T - set trim status active (the default). CCC200 continue CCC go to 100 C.. minus sign, turn off trim. CCC300 continue CCC trim = .false. CCC go to 100 C.. Number. Build up the reserve space amount. 400 continue reserv = reserv*10 + (char - ch0) go to 100 800 continue if (reserv.eq.0) reserv = defres return C.. Invalid something was found. 900 continue error = .true. return end