$compact
$title ('REN:  Rename File')
ren:
do;

/*
  Revised:
    19 Jan  80  by Thomas Rolander
    31 July 81  by Doug Huskey
     6 Aug  81  by Danny Horovitz
    23 Jun  82  by Bill Fitler
*/

$include (:f1:copyrt.lit)

$include (:f1:vaxcmd.lit)

$include (:f1:vermpm.lit)

declare
    true    literally '0FFh',
    false   literally '0',
    forever literally 'while true',
    lit     literally 'literally',
    proc    literally 'procedure',
    dcl     literally 'declare',
    addr    literally 'address',
    cr      literally '13',
    lf      literally '10',
    ctrlc   literally '3',
    ctrlx   literally '18h',
    bksp    literally '8';

$include (:f1:proces.lit)

$include (:f1:uda.lit)


  /**************************************
   *                                    *
   *       B D O S   INTERFACE          *
   *                                    *
   **************************************/


  mon1:
    procedure (func,info) external;
      declare func byte;
      declare info address;
    end mon1;

  mon2:
    procedure (func,info) byte external;
      declare func byte;
      declare info address;
    end mon2;

  mon3:
    procedure (func,info) address external;
      declare func byte;
      declare info address;
    end mon3;

  mon4:
    procedure (func,info) pointer external;
      declare func byte;
      declare info address;
    end mon4;



  declare cmdrv     byte    external;	/* command drive      */
  declare fcb (1)   byte    external;	/* 1st default fcb    */
  declare fcb16 (1) byte    external;	/* 2nd default fcb    */
  declare pass0     address external;	/* 1st password ptr   */
  declare len0      byte    external;	/* 1st passwd length  */
  declare pass1     address external;	/* 2nd password ptr   */
  declare len1      byte    external;	/* 2nd passwd length  */
  declare tbuff (1) byte    external;	/* default dma buffer */


  /**************************************
   *                                    *
   *       B D O S   Externals          *
   *                                    *
   **************************************/

  read$console:
    procedure byte;
      return mon2 (1,0);
    end read$console;

  conin: 
    procedure byte;
    return mon2(6,0fdh);
    end conin;

  printchar:
    procedure (char);
      declare char byte;
      call mon1 (2,char);
    end printchar;

  check$con$stat:
    procedure byte;
      return mon2(11,0);
    end check$con$stat;

  print$buf:
    procedure (buffer$address);
      declare buffer$address address;
      call mon1 (9,buffer$address);
    end print$buf;

  version: procedure address;
    /* returns current cp/m version # */
    return mon3(12,0);
    end version;

  search$first:
    procedure (fcb$address) byte;
      declare fcb$address address;
      return mon2 (17,fcb$address);
    end search$first;

  search$next:
    procedure byte;
      return mon2 (18,0);
    end search$next;

  delete$file:
    procedure (fcb$address);
      declare fcb$address address;
      call mon1 (19,fcb$address);
    end delete$file;

  rename$file:
    procedure (fcb$address) address;
      declare fcb$address address;
      return mon3 (23,fcb$address);
    end rename$file;

  setdma: procedure(dma);
    declare dma address;
    call mon1(26,dma);
    end setdma;

  /* 0ff => return BDOS errors */
  return$errors:
    procedure(mode);
    declare mode byte;
      call mon1 (45,mode);	
    end return$errors;

  terminate:
    procedure;
      call mon1 (143,0);
    end terminate;

  declare
    parse$fn structure (
      buff$adr  address,
      fcb$adr   address);

  parse: procedure address;
    return mon3(152,.parse$fn);
    end parse;

  declare
    pd$pointer      pointer,
    pd              based pd$pointer pd$structure;
  declare
    uda$pointer pointer,
    uda$ptr structure (
      offset word,
      segment word) at (@uda$pointer),
    uda based uda$pointer uda$structure;

  get$uda: procedure;
    pd$pointer = mon4(156,0);
    uda$ptr.segment = pd.uda;
    uda$ptr.offset = 0;
    end get$uda;


  /**************************************
   *                                    *
   *         GLOBAL VARIABLES           *
   *                                    *
   **************************************/

  /* Note: there are three fcbs used by
     this program:

         1) new$fcb: the new file name
            (this can be a wildcard if it
            has the same pattern of question
            marks as the old file name)
            Any question marks are replaced
            with the corresponding filename
            character in the old$fcb before
            doing the rename function.

         2) cur$fcb: the file to be renamed
            specified in the rename command.
            (any question marks must correspond
            to question marks in new$fcb).

         3) old$fcb: a fcb in the directory
            matching the cur$fcb and used in
            the bdos rename function.  This
            cannot contain any question marks.
  */         

  declare successful lit '0FFh';
  declare failed       (*) byte data(cr,lf,'Not renamed: $'),
          read$only    (*) byte data(cr,lf,'Drive Read Only$'),
          bad$wildcard (*) byte data('Invalid Wildcard$');
  declare passwd (8) byte;
  declare 
      new$fcb$adr address,       /* new name */
      new$fcb based  new$fcb$adr (32) byte;
  declare cur$fcb (33) byte;      /* current fcb (old name) */

  /**************************************
   *                                    *
   *       S U B R O U T I N E S        *
   *                                    *
   **************************************/


                  /* upper case character from console */
crlf:   proc;
    call printchar(cr);
    call printchar(lf);
    end crlf;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


                  /* fill string @ s for c bytes with f */
fill:   proc(s,f,c);
    dcl s addr,
        (f,c) byte,
        a based s byte;

        do while (c:=c-1)<>255;
        a = f;
        s = s+1;
        end;
    end fill;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


                  /* error message routine */
error:   proc(code);
   declare
      code byte;

    if code = 0 then do;  
        call print$buf(.('No such file to rename$'));
        call terminate;
        end;
    if code=1 then do;
        call print$buf(.(cr,lf,'BDOS Bad Sector$'));
        call terminate;
        end;
    if code=2 then do;
        call print$buf(.read$only);
        call terminate;
        end;
    if code = 3 then
        call print$buf(.read$only(8));
    if code = 5 then      
        call print$buf(.('Currently Opened$'));
    if code = 7 then
        call print$buf(.('Password Error$'));
    if code = 8 then      
        call print$buf(.('already exists$'));
    if code = 9 then do;
        call print$buf(.bad$wildcard);
        call terminate;
        end;
    end error;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


                 /* print file name */
print$file: procedure(fcbp);
        declare k byte;
        declare typ lit '9';        /* file type */
        declare fnam lit '11';        /* file type */
        declare
            fcbp   addr,
            fcbv   based fcbp (32) byte;

            do k = 1 to fnam;
            if k = typ then 
	       call printchar('.');
            call printchar(fcbv(k) and 7fh);
            end;
        end print$file;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


  /* try to rename fcb at old$fcb$adr to name at new$fcb$adr 
            return error code if unsuccessful */
  rename:
    procedure(old$fcb$adr) byte;
    declare
       old$fcb$adr address,
       old$fcb based  old$fcb$adr (32) byte,
       error$code  address,
       code        byte;
       
    call move (16,new$fcb$adr,old$fcb$adr+16);
    call setdma(.passwd);                   /* password    */
    call return$errors(0FFh);        /* return bdos errors */
    error$code = rename$file (old$fcb$adr);
    call return$errors(0);           /* normal error mode  */
    if low(error$code) = 0FFh then do;
        code = high(error$code);
        if code < 3 then 
            call error(code);
        return code;
        end;
    return successful;      
    end rename;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


                  /* upper case character from console */
ucase:   proc(c) byte;
    dcl c byte;

    if c >= 'a' then
       if c < '{' then
          return(c-20h);
    return c;
    end ucase;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


                  /* get password and place at fcb + 16 */
getpasswd:   proc;
    dcl (i,c) byte;

    call crlf;
    call print$buf(.('Password ? ','$'));
retry:
    call fill(.passwd,' ',8);
        do i = 0 to 7;
nxtchr:
        if (c:=ucase(conin)) >= ' ' then 
            passwd(i)=c;
        if c = cr then do;
            call crlf;
            goto exit;
            end;
        if c = ctrlx then
            goto retry;
        if c = bksp then do;
            if i<1 then
                goto retry;
            else do;
                passwd(i:=i-1)=' ';
                goto nxtchr;
                end;
            end;
        if c = ctrlc then
            call terminate;
        end;
exit:
    c = check$con$stat;
    end getpasswd;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


                  /* check for wildcard in rename command */
wildcard:   proc byte;
    dcl (i,wild) byte;

    wild = false;
        do i=1 to 11;
        if cur$fcb(i) = '?' then
            if new$fcb(i) <> '?' then do;
                call print$buf(.failed);
                call print$buf(.bad$wildcard);
                call terminate;
                end;
            else
                wild = true;
        end;
    return wild;
    end wildcard;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


                  /* set up new name for rename function */
set$new$fcb:   proc(old$fcb$adr);
    dcl old$fcb$adr address,
        old$fcb based old$fcb$adr (32) byte;
    dcl i byte;

    old$fcb(0) = cur$fcb(0);    /* set up drive */
        do i=1 to 11;
        if cur$fcb(i) = '?' then 
            new$fcb(i) = old$fcb(i);
        end;
    end set$new$fcb;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


                  /* try deleting files one at a time */
  single$file:
    procedure;
    declare (code,dcnt,savsearchl) byte;
    declare (old$fcb$adr,savdcnt,savsearcha) addr;
    declare old$fcb based old$fcb$adr (32) byte;
 
    file$err: procedure(fcba);
        dcl fcba address;
        call print$buf(.failed);
        call print$file(fcba);
        call printchar(' ');
        call error(code);
        end file$err;

    call setdma(.tbuff);
    if (dcnt:=search$first(.cur$fcb)) = 0ffh then
        call error(0);

        do while dcnt <> 0ffh;
        old$fcb$adr = shl(dcnt,5) + .tbuff;
        savdcnt = uda.dcnt;
        savsearcha = uda.searcha;
        savsearchl = uda.searchl;
        call set$new$fcb(old$fcb$adr);
        if (code:=rename(old$fcb$adr)) = 8 then do;
            call file$err(new$fcb$adr);
            call print$buf(.(', delete (Y/N)?$'));
            if ucase(read$console) = 'Y' then do;
                call delete$file(new$fcb$adr);
                code = rename(old$fcb$adr);
                end;
            else
                go to next;
            end;
        if code = 7 then do;
            call file$err(old$fcb$adr);
            call getpasswd;
            code = rename(old$fcb$adr);
            end;
        if code <> successful then 
            call file$err(old$fcb$adr);
        else do;
            call crlf;
            call print$file(new$fcb$adr);
            call printchar('=');
            call print$file(old$fcb$adr);
            end;
next:
        call setdma(.tbuff);
        uda.dcnt = savdcnt;
        uda.searcha = savsearcha;
        uda.searchl = savsearchl;
        dcnt = search$next;
        end;
    end single$file;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


                  /* invalid rename command */
bad$entry:   proc;

    call print$buf(.failed);
    call print$buf(.('Invalid File','$'));
    call terminate;
    end bad$entry;


  /**************************************
   *                                    *
   *       M A I N  P R O G R A M       *
   *                                    *
   **************************************/

declare ver address;

declare last$dseg$byte byte
  initial (0);

plm$start:
  procedure public;
    ver = version;
    if low(ver) < Ver$BDOS  or  (high(ver) and Ver$Mask) = 0 then
        call print$buf (.(Ver$Needs$OS,'$'));
    else do;
        call get$uda;
        parse$fn.buff$adr = .tbuff(1);
        new$fcb$adr, parse$fn.fcb$adr = .fcb;
        if (parse$fn.fcb$adr:=parse) <> 0FFFFh then do; /* old file */
            parse$fn.buff$adr = parse$fn.fcb$adr + 1; /* skip delim */
            parse$fn.fcb$adr  = .cur$fcb;
            parse$fn.fcb$adr = parse;                /* new file */
            call move (8,.cur$fcb+16,.passwd);       /* password */
            end;
        if parse$fn.fcb$adr = 0ffffh then 
            call bad$entry;
        if fcb(0) <> 0 then
            if cur$fcb(0) <> 0 then do;
                if fcb(0) <> cur$fcb(0) then
                    call bad$entry;
                end;
            else
                cur$fcb(0) = new$fcb(0);        /* set drive */
        if wildcard then
            call singlefile;
        else if rename(.cur$fcb) <> successful then 
            call singlefile;
        end;
    call mon1(0,0);
    end plm$start;

end ren;

