home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / d / dlcpmv23.zoo / cpmv.c < prev    next >
C/C++ Source or Header  |  1992-03-29  |  20KB  |  721 lines

  1. /*************************************************************************
  2. * DL_CPMV v2.2 92/02/15 by Dennis Lo
  3. *     This program and its source code are in the public domain.
  4. *
  5. * DESCRIPTION:
  6. *    DL_CPMV is a BSD Unix-like cp and mv for MS-DOS.  Unlike most 
  7. *    other MS-DOS cp and mv programs, this one:
  8. *        - has the -p and -v options from BSD/SunOS.
  9. *        - can move files across drives.
  10. *        - can rename directories as well as files
  11. *        - supports environment variable references in file names
  12. *        - allows forward slashes to be used in path names 
  13. *
  14. * TO COMPILE:
  15. *    The same source file contains both MV and CP.  To compile, #define 
  16. *    either CP or MV below, rename this file to cp.c or mv.c, and compile
  17. *    with Turbo C++ v1.01, small memory model:
  18. *        eg.    tcc cp.c     or     
  19. *            tcc mv.c
  20. *    Other versions of Turbo C should also work but have not been tested.
  21. *    User documentation is embedded in HelpExit().
  22. *    Notes: a "filename" means something like "c:/etc/zansi.sys".  A 
  23. *    "filespec" means something like "c:/etc/*.sys".
  24. *
  25. * TEST SUITE:
  26. *    - mv/cp within current dir:        mv mv.obj mv.2
  27. *    - mv/cp from current dir to other dir:    mv mv.obj ..
  28. *    - mv/cp from other dir to another dir:    mv c:/d/work/mv.obj /tmp/mv.o
  29. *    - mv/cp across drives:            mv mv.obj e:
  30. *    - mv/cp across drives with paths:    mv c:/d/work/mv.obj e:/tmp
  31. *    - test -f/-i flags            cp -p cp.obj mv.obj
  32. *    - test -p/-n flags:            mv -p mv.obj a:
  33. *    - test -v/-q flags:            mv -q mv.obj mv.2
  34. *    - test several flags together:        mv -ipn mv.obj mv.2
  35. *    - test flags in env variable:        set MV=piv
  36. *    - mv/cp -r across drives        mv -r c:/tmp/1 e:/
  37. *    - mv/cp -r on same drive        mv -r /tmp/1/*.* /tmp/2
  38. *    - mv/cp no -r: subdirs not moved    mv *.* ..
  39. *
  40. * CHANGE LOG:
  41. *    Version 2.3 92/03/30:
  42. *    - return error code of 0 when successful 
  43. *    Version 2.2 92/01/08:
  44. *    - fixed cp -r bug that tries to remove subdirectories
  45. *    Version 2.1 92/01/01:
  46. *    - added -r option
  47. *    - added explicit wildcard expansion to allow cases like $junk/*.*
  48. *    Version 2.0 91/12/28:
  49. *    - switched from Datalight C to Turbo C++ (and the .exe grew by 5K !)
  50. *    - mv defaults to -p instead of -n
  51. *    - mv can rename directories now
  52. *    Version 1.6 91/01/20:
  53. *    - allow moving across drives
  54. * TODO:
  55. *    - Switch disks if src<>dst drive, and dest drive is a floppy, and
  56. *      dest free space < current file size
  57. *    - package for release
  58. *************************************************************************/
  59. /* 
  60.  * Define one of the following to indicate whether this is 'mv' or 'cp' 
  61.  */
  62. #define CP 
  63. /* 
  64. #define MV 
  65. */
  66.  
  67. #include <io.h>
  68. #include <fcntl.h>
  69. #include <sys/stat.h>
  70. #include <stdio.h>
  71. #include <dos.h>
  72. #include <dir.h>
  73. #include <conio.h>
  74. #include <stdlib.h>
  75.  
  76. /*=====================================================================
  77.  * Macros
  78.  *=====================================================================
  79.  */
  80. /* Macro to write a string to stdout (avoids the need to use printf) */
  81. #define PUT(str) fputs (str, stdout)
  82.  
  83. /*=====================================================================
  84.  * Global Variables
  85.  *=====================================================================
  86.  */
  87. int Force_flag = 1;
  88. int Verbose_flag = 0;
  89. int Recursive_flag = 0;
  90. #ifdef CP
  91.     int Preserve_flag = 0;    /* CP: use new timestamp by default */
  92. #else
  93.     int Preserve_flag = 1;    /* MV: preserve timestamp by default */
  94. #endif
  95.  
  96. /*=====================================================================
  97.  * Print a help message and exit
  98.  *=====================================================================
  99.  */
  100. void
  101. HelpExit ()
  102. {
  103. #ifdef CP
  104.     PUT ("DL_CP - a BSD Unix style file copy utility   v2.3 92/03/30 Dennis Lo\n");
  105.     PUT ("USAGE: cp  [-fipnvq] file dest_file\n");    
  106.     PUT ("   or: cp  [-fipnvq] file1 [file2...fileN] dest_directory\n");    
  107.     PUT ("  -f = Force overwrites without prompting (default).\n");
  108.     PUT ("  -i = Interactively prompt before overwriting (opposite of -f).\n");
  109.     PUT ("  -p = Preserve original timestamp when copying.\n");
  110.     PUT ("  -n = use New timestamp when copying (opposite of -p). (default)\n");
  111.     PUT ("  -v = Verbose - show each file copied.\n");
  112.     PUT ("  -q = Quiet - opposite of -v (default).\n");
  113.     PUT ("  -r = Recursively descend & copy sub-directories.\n");
  114.     PUT ("Default arguments can be put in the CP environment variable. (eg. 'set CP=ipv')\n");
  115.     PUT ("File names can contain environment variable references (eg. 'cp $work/*.c a:')\n");
  116. #else
  117.     PUT ("DL_MV - a BSD Unix style file move utility   v2.3 92/03/30 Dennis Lo\n");
  118.     PUT ("USAGE: mv  [-fipnvq] old_file new_file\n");    
  119.     PUT ("   or: mv  [-fipnvq] file1 [file2...fileN] dest_directory\n");
  120.     PUT ("  -f = Force overwrites without prompting (default).\n");
  121.     PUT ("  -i = Interactively prompt before overwriting (opposite of -f).\n");
  122.     PUT ("  -p = Preserve original timestamp when copying. (default)\n");
  123.     PUT ("  -n = use New timestamp when copying (opposite of -p).\n");
  124.     PUT ("  -v = Verbose - show each file copied.\n");
  125.     PUT ("  -q = Quiet - opposite of -v (default).\n");
  126.     PUT ("  -r = Recursively descend & move sub-directories.\n");
  127.     PUT ("Can move directories.  Can move files from one drive to another.\n");
  128.     PUT ("Default arguments can be put in the MV environment variable. (eg. 'set MV=ipv')\n");
  129.     PUT ("File names can contain environment variable references (eg. 'mv $work/*.c a:')\n");
  130. #endif
  131.     exit (1);
  132. }
  133.  
  134. /*=====================================================================
  135.  * Return TRUE if a filespec is a file
  136.  *=====================================================================
  137.  */
  138. IsFile (filespec)
  139.     char *filespec;
  140. {
  141.     struct ffblk findblk;
  142.  
  143.     return (findfirst (filespec, &findblk, 0) == 0);
  144.     /* maybe should change the 0 to FA_HIDDEN|FA_SYSTEM */
  145. }
  146.  
  147. /*=====================================================================
  148.  * Return TRUE if a filespec is a directory
  149.  *=====================================================================
  150.  */
  151. IsDir (filespec)
  152.     char *filespec;
  153. {
  154.     struct ffblk findblk;
  155.     int lastchar = filespec [strlen (filespec) - 1];
  156.  
  157.     return ((findfirst (filespec, &findblk, FA_DIREC) == 0  
  158.          &&  !IsFile (filespec))
  159.         || lastchar == '.'  ||  lastchar == '/'  ||  lastchar == '\\');
  160. }
  161.  
  162. /*=====================================================================
  163.  * Interpret an arguments string of the form "fiv" (not "-f -iv")
  164.  *=====================================================================
  165.  */
  166. void
  167. ParseArgs (str)
  168.     char *str;
  169. {
  170.     char *param;
  171.     static char errmsg[] = "-?\n";
  172.  
  173.     for (param = str;  *param;    param++)
  174.     {
  175.     switch (*param)
  176.     {
  177.         case 'f':            /* Force overwrites */
  178.         Force_flag = 1;
  179.         break;
  180.         case 'i':            /* Interactive */
  181.         Force_flag = 0;
  182.         break;
  183.         case 'n':            /* cp: create New file */
  184.         Preserve_flag = 0;
  185.         break;
  186.         case 'p':            /* cp: Preserve timestamp & modes */
  187.         Preserve_flag = 1;
  188.         break;
  189.         case 'q':            /* Quiet */
  190.         Verbose_flag = 0;
  191.         break;
  192.         case 'r':            /* Recursive */
  193.         Recursive_flag = FA_DIREC;
  194.         break;
  195.         case 'v':            /* Verbose */
  196.         Verbose_flag = 1;
  197.         break;
  198.         default:
  199.         errmsg[1] = *param;
  200.         PUT ("Invalid parameter "); PUT (errmsg);
  201.         HelpExit ();
  202.     }
  203.     }
  204. }
  205.  
  206. /*=====================================================================
  207.  * Return the drive letter of a filename
  208.  *=====================================================================
  209.  */
  210. char
  211. DriveName (filename)
  212.     char *filename;
  213. {
  214.     char drive;
  215.  
  216.     /* 
  217.      * If the drive name is embedded in the given file name then
  218.      * extract it
  219.      */
  220.     if (filename[1] == ':')
  221.     {
  222.     drive = filename[0];
  223.     }
  224.     /* else return current drive */
  225.     else 
  226.     {
  227.     char path [80];
  228.     getcwd (path, 79);
  229.     drive = *path;
  230.     }
  231.     return (toupper (drive));
  232. }
  233.  
  234. /*=====================================================================
  235.  * Return a pointer to the filename portion of a filespec.
  236.  * (Skips the drive specifier and the path)
  237.  *=====================================================================
  238.  */
  239. char *
  240. FileName (filename)
  241.     char *filename;
  242. {
  243.     char *p; 
  244.  
  245.     /* Start at end of filename */
  246.     p = filename + strlen (filename) - 1;
  247.  
  248.     /* Scan for first '/' or '\' or ':' */
  249.     while (p >= filename)
  250.     {
  251.     if (*p == '/'  ||  *p == '\\'  ||  *p == ':')
  252.         break;
  253.     p--;
  254.     }
  255.     return (p + 1);
  256. }
  257.  
  258. /*=====================================================================
  259.  * Copy the path portion of a filename (including the last '/').
  260.  *=====================================================================
  261.  */
  262. void
  263. PathName (filename, pathname)
  264.     char *filename;    /* src */
  265.     char *pathname;    /* dest: caller provides storage */
  266. {
  267.     char *path_end = FileName (filename);
  268.     int path_len = path_end - filename;
  269.  
  270.     if (path_len < 0)
  271.         path_len = 0;
  272.  
  273.     strncpy (pathname, filename, path_len);
  274.     pathname [path_len] = '\0';
  275. }
  276.  
  277. /*=====================================================================
  278.  * Expand the environment varible references in a filespec.
  279.  * The caller must provide storage for the expanded filespec.
  280.  * Only one environment variable is allowed per filespec.
  281.  *=====================================================================
  282.  */
  283. void
  284. ExpandEnvVar (oldname, newname)
  285.     char *oldname;
  286.     char *newname;
  287. {
  288.     int len, i;
  289.     char *envvar_start;
  290.     char *expanded_name;
  291.     char envvar [80];
  292.  
  293.     while (*oldname)
  294.     {
  295.     /* 
  296.      * Scan for the start of an environment variable reference, 
  297.      * copying to newname at the same time.
  298.      */
  299.     while (*oldname  &&  *oldname != '$')
  300.         *newname++ = *oldname++;
  301.     if (!*oldname) /* if at end of old name */
  302.     {
  303.         *newname = '\0';
  304.         break;
  305.     }
  306.     envvar_start = ++oldname;
  307.  
  308.     /* 
  309.      * Scan for the end of the environment variable name
  310.      */
  311.     len = 0;
  312.     while (*oldname  &&    
  313.            *oldname != '/'    &&  *oldname != '\\'  &&
  314.            *oldname != ':'    &&  *oldname != '$'  &&  *oldname != ' ')
  315.     {
  316.         oldname++;
  317.         len++;
  318.     }
  319.  
  320.     /*
  321.      * Convert the env variable name to upper case, and
  322.      * Expand the environment variable and add it to newname 
  323.      */
  324.     for (i=0; i<len; i++)
  325.         envvar [i] = envvar_start [i] & (255-32);
  326.     envvar [len] = '\0';
  327.     expanded_name = getenv (envvar);
  328.     if (expanded_name)
  329.     {
  330.         strcpy (newname, expanded_name);
  331.         newname += strlen (expanded_name);
  332.     }
  333.     else
  334.         *newname = '\0';
  335.  
  336.     /*
  337.      * Copy the rest of oldname to the newname
  338.      */
  339.     while (*oldname  &&  *oldname != ' ')
  340.         *newname++ = *oldname++;
  341.         *newname = '\0';
  342.     }
  343. }
  344.  
  345. /*=====================================================================
  346.  * Copy a file
  347.  *=====================================================================
  348.  */
  349. int
  350. Copy (src, dest, preserve_date)
  351.     char *src;
  352.     char *dest;
  353.     int preserve_date;        /* non-zero = preserve src date on dest file */
  354. {
  355.     int     src_fd, dst_fd;
  356.     unsigned    bytes_read;
  357.     union REGS    regs;
  358.  
  359.     /* File copying buffer */
  360. #   define BUFSIZE 49152
  361.     static char *Filebuf = NULL;
  362.  
  363.     /* Allocate the file buffer if not already done */
  364.     if (Filebuf == NULL)
  365.     {
  366.     Filebuf = malloc (BUFSIZE);
  367.     if (Filebuf == NULL)
  368.     {
  369.         PUT ("Not enough memory!\n");
  370.         exit (1);
  371.     }
  372.     }
  373.  
  374.     /* Open the source file */
  375.     if ((src_fd = open (src, O_BINARY)) == -1)
  376.     {
  377.     PUT ("Can't open source file "); PUT(src); 
  378.     return (1);
  379.     }
  380.  
  381.     /* Open the destination file */
  382.     if ((dst_fd = open (dest, O_CREAT|O_TRUNC|O_BINARY, S_IREAD|S_IWRITE)) 
  383.         == -1)
  384.     {
  385.     PUT ("Can't create destination file!");
  386.     return (1);
  387.     }
  388.  
  389.     /* Copy the contents of the source file into the destination file */
  390.     while ((bytes_read = read (src_fd, Filebuf, BUFSIZE)) > 0)
  391.     {
  392.     if (write (dst_fd, Filebuf, bytes_read) < bytes_read)
  393.     {
  394.         PUT ("Error writing file "); PUT (dest);
  395.         return (1);
  396.     }
  397.     }
  398.  
  399.     /* Set the dest file's date to the source file's date */
  400.     if (preserve_date)
  401.     {
  402.     /* Fetch the timestamp of the src file */
  403.     regs.x.ax = 0x5700;
  404.     regs.x.bx = src_fd;
  405.     int86 (0x21, ®s, ®s);
  406.  
  407.     /* Set the dest file's timestamp to the timestamp just fetched */
  408.     regs.x.ax = 0x5701;
  409.     regs.x.bx = dst_fd;
  410.     int86 (0x21, ®s, ®s);
  411.     }
  412.  
  413.     /* Close the src and dest files */
  414.     if (close (src_fd) == -1)
  415.     {
  416.     PUT ("Error closing "); PUT (src);
  417.     return (1);
  418.     }
  419.     if (close (dst_fd) == -1)
  420.     {
  421.     PUT ("Error closing "); PUT (dest);
  422.     return (1);
  423.     }
  424.  
  425.     return (0);
  426. }
  427.  
  428. /*=====================================================================
  429.  * Copy a file and print informative msgs.  Returns 0 if failed.
  430.  *=====================================================================
  431.  */
  432. int
  433. DoCopy (src_name, dst_name)
  434.     char *src_name;
  435.     char *dst_name;
  436. {
  437.     if (IsDir (src_name))
  438.         return 0;
  439.  
  440.     /* 
  441.      * Print status message if verbose flag is on 
  442.      */
  443.     if (Verbose_flag)
  444.     {
  445.     PUT("Copying ") ;PUT(src_name); PUT(" to "); PUT(dst_name); PUT("\n");
  446.     }
  447.  
  448.     /*
  449.      * Do the copy; if it fails then print a msg and delete the incomplete
  450.      * dest file.
  451.      */
  452.     if (strcmp (src_name, dst_name) == 0
  453.     || Copy (src_name, dst_name, Preserve_flag) != 0)
  454.     {
  455.     PUT(" ...Copy to "); PUT(dst_name); PUT(" failed\n");
  456.     unlink (dst_name);
  457.     return 0;
  458.     }
  459.     return 1;
  460. }
  461.  
  462. #ifndef CP
  463. /*=====================================================================
  464.  * Move a file and print informative msgs.
  465.  *=====================================================================
  466.  */
  467. void
  468. DoMove (src_name, dst_name)
  469.     char *src_name;
  470.     char *dst_name;
  471. {
  472.     /*
  473.      * If the destination file is on a different drive, then copy the
  474.      * src to the dest and then delete the src.
  475.      */
  476.     if (DriveName (dst_name) != DriveName (src_name))
  477.     {
  478.     if (DoCopy (src_name, dst_name))
  479.     {
  480.         if (Verbose_flag)
  481.         {
  482.         PUT("Removing "); PUT(src_name); PUT("\n");
  483.         }
  484.         unlink (src_name);
  485.         rmdir (src_name);
  486.     }
  487.     }
  488.     /* 
  489.      * Else (on the same drive) rename the file 
  490.      */
  491.     else 
  492.     {
  493.     if (Verbose_flag)
  494.     {
  495.         PUT("Moving "); PUT(src_name); 
  496.         PUT(" to "); PUT(dst_name); PUT("\n");
  497.     }
  498.     if (rename (src_name, dst_name) != 0)
  499.     {
  500.         /*
  501.          * If the move failed because the destination is on a different
  502.          * drive, copy the file and delete the source file.
  503.          */
  504.         PUT(" ...Move to "); PUT(dst_name); PUT(" failed\n");
  505.     }
  506.     }
  507. }
  508. #endif
  509.  
  510. /*=====================================================================
  511.  * Do the copy/move for 1 source filename 
  512.  *=====================================================================
  513.  */
  514. void
  515. Do1File (src_name, dst_base)
  516.     char *src_name;
  517.     char *dst_base;
  518. {
  519.     char dst_name[80];         /* current destination file name */
  520.     int ch;
  521.  
  522.     /* 
  523.      * Build the destination file's name.
  524.      * If the destination is a directory then append the src filename.
  525.      * (preceded by a '/' if necessary)
  526.      */
  527.     strcpy (dst_name, dst_base);
  528.     if (IsDir (dst_base)) 
  529.     {
  530.     if (dst_name [strlen (dst_name)-1] != '/'  &&
  531.         dst_name [strlen (dst_name)-1] != '\\')
  532.         strcat (dst_name, "/");
  533.     strcat (dst_name, FileName (src_name));
  534.     }
  535.  
  536.     /* 
  537.      * if the destination is a file that already exists 
  538.      */
  539.     if (IsFile (dst_name))
  540.     {
  541.     /* if force flag is off then prompt before deleting */
  542.     if (!Force_flag)
  543.     {
  544.         PUT ("Overwrite ");  PUT (dst_name); PUT (" ? ");
  545.         fflush (stdout);
  546.         ch = getche();
  547.  
  548.         /* If the answer is not YES then skip this file */
  549.         if (ch != 'y'  &&  ch != 'Y')
  550.         {
  551.         PUT ("\n");
  552.         return;
  553.         }
  554.         if (Verbose_flag)
  555.         PUT ("      ");
  556.         else
  557.         PUT ("\n");
  558.     }
  559.     /* delete the existing file */
  560.     unlink (dst_name);
  561.     }
  562.  
  563.     /*
  564.      * if the source is a directory and the -r option is set 
  565.      *       Create the dest directory if it doesn't already exist.
  566.      *     Recursively copy/move the source directory's contents.
  567.      *       Delete the src directory if in 'mv'
  568.      * else
  569.      *     Copy or move the file 
  570.      */
  571.     if (IsDir (src_name))
  572.     {
  573.         if (Recursive_flag)
  574.     {
  575.         mkdir (dst_name);
  576.         strcat (src_name, "/*.*");
  577.         Do1Filespec (src_name, dst_name);
  578.  
  579. #ifndef CP
  580.         /* remove the "/*.*" from src_name and rmdir it */
  581.         src_name [strlen(src_name) - 4] = '\0';
  582.         if (Verbose_flag)
  583.         {
  584.         PUT("Removing directory "); PUT(src_name); PUT("\n");
  585.         }
  586.         rmdir (src_name);
  587. #endif
  588.     }
  589. #ifndef CP
  590.     /* else ignore src directories unless renaming a directory */
  591.     else if (!IsDir (dst_base))
  592.         DoMove (src_name, dst_name);
  593. #endif
  594.     }
  595.     else
  596. #ifdef CP
  597.     DoCopy (src_name, dst_name);
  598. #else
  599.     DoMove (src_name, dst_name);
  600. #endif
  601. }
  602.  
  603. /*=====================================================================
  604.  * Do the copy/move for 1 source filespec (eg. c:/etc/*.*)
  605.  *=====================================================================
  606.  */
  607. int
  608. Do1Filespec (src_base, dst_base)
  609.     char *src_base;
  610.     char *dst_base;
  611. {
  612.     char src_name[80];        /* current source file name */
  613.     struct ffblk findblk;
  614.  
  615.     /* Start the wildcard expansion process */
  616.     if (findfirst (src_base, &findblk, FA_DIREC) != 0)
  617.     return 0;
  618.  
  619.     /* Loop once for each expanded wildcard file name */
  620.     do 
  621.     {
  622.     /* Skip the '.' and '..' files */
  623.     if (findblk.ff_name[0] == '.')
  624.         continue;
  625.  
  626.     /* Prepend the src path to the found file name */
  627.     PathName (src_base, src_name);
  628.     strcat (src_name, findblk.ff_name);
  629.  
  630.     Do1File (src_name, dst_base);
  631.     } while (findnext (&findblk) == 0);
  632.  
  633.     return 1;
  634. }
  635.  
  636. /*=====================================================================
  637.  * Main
  638.  *=====================================================================
  639.  */
  640. void
  641. main (argc, argv)
  642.     int argc;
  643.     char **argv;
  644. {
  645.     int srci;            /* argv index of current source name */
  646.     int desti = argc - 1;    /* argv index of destination name */
  647.     char *env_opt;        /* environment variable options string */
  648.     char src_base[80];         /* source file name base */
  649.     char dst_base[80];         /* destination file name base */
  650.     int len;
  651.     int par;
  652.  
  653.     /*
  654.      * Get the options from the environment variables, then get the options
  655.      * from the command line switches.    Command line options can be either 
  656.      * in the form '-f -i -v' or the form '-fiv'.
  657.      */
  658. #ifdef CP
  659.     env_opt = getenv ("CP");
  660. #else
  661.     env_opt = getenv ("MV");
  662. #endif
  663.     if (env_opt)
  664.     ParseArgs (env_opt);
  665.     for (par=1;  par<argc && *argv[par] == '-';  par++)
  666.     ParseArgs (argv[par] + 1);
  667.  
  668.     /* 
  669.      * If no filename arguments given then print help.
  670.      * If not enough filename arguments given then print error message.
  671.      */
  672.     if (desti - par < 0)
  673.     HelpExit();
  674.     if (desti - par < 1)
  675.     {
  676.     PUT (argv[0]); PUT (": Not enough files specified.\n");
  677.     exit (1);
  678.     }
  679.  
  680.     /*
  681.      * Expand the destination name for env vars. 
  682.      * For filenames ending in ':', add a '.' (eg. "a:" becomes "a:.")
  683.      */
  684.     ExpandEnvVar (argv [desti], dst_base);
  685.     len = strlen (dst_base);
  686.     if (dst_base [len-1] == ':')
  687.     {
  688.     dst_base [len] = '.';
  689.     dst_base [len+1] = '\0';
  690.     }
  691.  
  692.     /* 
  693.      * Error if the destination is not a dir and there are > 1 source files
  694.      */
  695.     if (desti - par > 1  &&  !IsDir (dst_base))
  696.     {
  697. #ifdef CP
  698.     PUT ("Can't copy multiple files to the same destination file\n");
  699. #else
  700.     PUT ("Can't move multiple files to the same destination file\n");
  701. #endif
  702.     exit (1);
  703.     }
  704.  
  705.     /* 
  706.      * Loop once for each source filespec on the command line
  707.      */
  708.     for (srci = par;  srci < desti;  srci++)
  709.     {
  710.     /* Expand the env variables in the source filename */
  711.     ExpandEnvVar (argv [srci], src_base);
  712.  
  713.         if (Do1Filespec (src_base, dst_base) == 0)
  714.     {
  715.         PUT (src_base); PUT(": file(s) not found.\n");
  716.     }
  717.     }
  718.     exit (0);
  719. }
  720.  
  721.