/*
MOV.C
demonstrate wildcard pathed renames via DOS indirect 
function call
by Ralf Brown, with thanks to Dan Lanciani for pointing out that 
indirect function call enables wildcards on rename and delete

This file contains a number of minor modifications from the version
printed in UNDOCUMENTED DOS

Usage: MOV old-filespec new-filespec
*/

#include <stdlib.h>
#include <stdio.h>
#include <dos.h>

typedef struct
{
   unsigned ax,bx,cx,dx,si,di,ds,es,reserved,computerID,processID ;
} DPL ;     /* DOS parameter list */

union REGS regs ;
struct SREGS segregs ;

void canonicalize(filespec,canonical,errorlevel)
char *filespec, *canonical ;
int errorlevel ;
{
   void far *lvalue;
   regs.h.ah = 0x60 ;
   lvalue = filespec;
   regs.x.si = FP_OFF(lvalue) ;
   segregs.ds = FP_SEG(lvalue) ;
   lvalue = canonical;
   regs.x.di = FP_OFF(lvalue) ;
   segregs.es = FP_SEG(lvalue) ;
   intdosx(&regs,&regs,&segregs) ;
   if (regs.x.cflag)
   {
      puts("invalid filespec") ;
      exit(errorlevel) ;
   }
}

int errorlevel = 0 ;
void ERROR(char *s, int x)     { puts(s); errorlevel = (x); }

int main(argc,argv)
int argc ;
char **argv ;
{
   DPL dpl ;
   void far *ptr ;
   char source[128], target[128] ;

   if (argc != 3)
   {
      puts("usage: MOV old-filespec new-filespec") ;
      puts("where old-filespec and new-filespec may contain") ;
      puts("wildcards. Wildcards in the new-filespec indicate") ;
      puts("that new name should contain same characters as") ;
      puts("old name in those positions.") ;
      errorlevel = 1 ;
   }
   else if (_osmajor < 3 || (_osmajor == 3 && _osminor < 10))
      ERROR("MOV requires DOS 3.10 or higher", 5);
   else
   {
      canonicalize(argv[1],source,3) ;
      canonicalize(argv[2],target,4) ;
      if (source[0] != target[0])    /* are they on the same drive? */
        ERROR("Source and target must be on the same drive", 6);
      else /* do the move/rename */
      {
        void far *lvalue;
        dpl.ax = 0x5600 ;   /* indirect function is rename */
        lvalue = &source;
        dpl.dx = FP_OFF(lvalue) ;
        dpl.ds = FP_SEG(lvalue) ; /* DS:DX old filespec */
        lvalue = &target;
        dpl.di = FP_OFF(lvalue) ;
        dpl.es = FP_SEG(lvalue) ; /* ES:DI new filespec */
        dpl.bx = dpl.cx = dpl.si = 0 ;
        dpl.computerID = 0 ;    /* local machine */
        dpl.processID = 0 ;     /* current process */

        regs.x.ax = 0x5D00 ;  /* invoke server function call */
        ptr = (void far *)&dpl ;
        regs.x.dx = FP_OFF(ptr) ;
        segregs.ds = FP_SEG(ptr) ;
        intdosx(&regs,&regs,&segregs) ;
        /* rename returns error 12h (no more files) on success */
        if (regs.x.cflag && regs.x.ax != 0x12)
            ERROR("rename failed", 2);
        /* NOTE: fails in OS/2 DOS box */
      }
   }
   return errorlevel ;
}
