home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / dos / directry / mv / front.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-05  |  14.7 KB  |  457 lines

  1. /* 8-Sep-86 16:13:42-PDT,14202;000000000000
  2. Return-Path: <pwu@unix.macc.wisc.edu>
  3. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:12:30 PDT
  4. Received: by unix.macc.wisc.edu;
  5.           id AA04958; 4.12/5; Mon, 8 Sep 86 17:31:19 cdt
  6. Date: Mon, 8 Sep 86 17:31:19 cdt
  7. From: Peter Wu <pwu@unix.macc.wisc.edu>
  8. Message-Id: <8609082231.AA04958@unix.macc.wisc.edu>
  9. To: info-ibmpc-request@mosis
  10. Subject: front.c
  11. */
  12. /* Program to move files and/or subdirectories - like mv on unix
  13. ** Written by Peter Wu July, 86 @ Faculty Support Center @ UW-Madison
  14. ** Compile with IBM C version 1.00 (or Microsoft C 3.00).
  15. ** Link with fstat, mv, normal, sector, absdr, func32h, break, putn /stack:5000
  16. **
  17. ** This is module "front.c", the front end of mv. It looks at the sources
  18. ** and destination supplied by the user and determines whether to call
  19. ** mvdir to move/rename sub-directories or call rename to move/rename files.
  20. */
  21.  
  22. /* programmer's notes:
  23. -- won't work on network disk
  24. --
  25. -- things to work on:
  26. --
  27. -- be interactive:
  28. --   ask if user wants to replace existing file/directory
  29. --   verbose mode where user must confirm each move
  30. -- better error handling
  31. -- make it work on network disks
  32. -- detect and report write-protected disk if possible
  33. -- allow /f option on destination?
  34. --
  35. -- bugs:
  36. +--------------------------------+
  37. |  c:                            |
  38. |  subst p: /user/peter/mv/test  |
  39. |  mv /usr/peter/mv/test .       |
  40. +--------------------------------+
  41. -- The above sequence will prevent mv from removing the source directory after
  42. -- the content is moved to the destination directory. I don't know any way to
  43. -- detect this BEFORE moving the content. So my solution is to print a message
  44. -- telling the user to remove the source directory (now empty) by hand after
  45. -- he got rid of the 'subst drive'.
  46. */
  47. #define LINT_ARGS
  48.  
  49. #define UNK_TYP 0
  50.  
  51. #define A_INT 0x80  /* interactive file attribute */
  52.  
  53. #define LASTDRV 26  /* drive# larger than this is network share disk */
  54. #define NULL (char *) 0
  55. #define PLEN 200        /* max. path length; hope it's long enough */
  56.  
  57. #include "dta.h"
  58. #include "date.h"
  59. #include <conio.h>
  60. #include <string.h>
  61. #include <stdlib.h>
  62.  
  63. char lastc(char *);
  64. char getkey(char *);
  65.  
  66. int brk_st;  /* original break status: 0=break off, 1=break on */
  67.  
  68.   /* When break is on, user can break the program when any DOS function is
  69.   ** called. When break is off, user can only break the program when a DOS
  70.   ** I/O function is called (if the user presses break before an I/O function,
  71.   ** DOS will remember the break but let the program keep running until the
  72.   ** program calls an IO function). It is undesirable to have break on
  73.   ** all the time since for instance if the user breaks the program after I
  74.   ** wrote one sector to disk (I need to write three sectors per sub-directory
  75.   ** moved), he'll end up with an inconsistent directory. The solution is to
  76.   ** make sure break is off when I'm doing disk writes, and restore it
  77.   ** to its orginal status when I'm done writing the disk.
  78.   ** If the user really wants to mess up his disk, he can still do it with
  79.   ** ctrl-alt-del or switching the power off in the middle of running mv.
  80.   */
  81.  
  82. main(argc,argv)
  83. int argc;
  84. char *argv[];
  85. {
  86.   unsigned int status, dtype, stype, status2, smask, dmask, i, drv, plimit,
  87.                doit;
  88.   union dtbuf mydta1, mydta2, tmpdta;
  89.   char fsource[PLEN], fdest[PLEN], *source, *dest, *tmp, fdest2[PLEN];
  90.  
  91.   if (argc < 3) {  /* print help */
  92.     putn(
  93.  
  94. "Usage: MV <source1> <source2> .. <sourceN> <dest>\n\15",
  95. "  MV renames/moves the source files/directories to the destination.\n\15",
  96. "  Wildcards ok. Specify source type with /d, /f, /h, and /i.\n\15",
  97. "  /d=sub-directories, /f=files, /h=search hidden, /i=interactive.\n\15",
  98. "    \"*.*\\.\" or \"*.*/d\" specifies all sub-directories only\n\15",
  99. "    \"*.*/f\" specifies all visible files only\n\15",
  100. "    \"*.*/hf\" specifies all visible and hidden files\n\15",
  101. "    \"*.*/fi\" will prompt you (move or not) for every file found\n\15",
  102. "  Version 1.20 made ", date, ". For DOS 2.xx and 3.xx.\n\n\15",
  103. "Please send comments/bug reports to one of the following addresses:\n\15",
  104. "  Arpanet: pwu@unix.macc.wisc.edu\n\15",
  105. "  Bitnet: WU at WISVMACC\n\15",
  106. "  CompuServe: 76377,1332\n\15",
  107. "  UUCP: {akgua|ihnp4|seismo|harvard|allegra|ucbvax}!uwvax!uwmacc!pwu\n\15",
  108. NULL);
  109.  
  110.     exit(0);
  111.   }
  112.  
  113.   /* test DOS version */
  114.   switch (_osmajor) {
  115.     case 2:  /* dos 2.xx */
  116.       break;
  117.     case 3:  /* dos 3.xx */
  118.       if (_osminor <= 20) {  /* make sure it's no later than version 3.20 */
  119.         break;
  120.       }
  121.     default:
  122.       cputs("need DOS between version 2.00 and 3.20\n\15");
  123.       exit(1);
  124.   }
  125.  
  126.   plimit = PLEN - 80;  /* limit on user supplied path name */
  127.  
  128.   /* process destination first */
  129.   dest = argv[argc-1];
  130.   if (strlen(dest) > plimit) {
  131.     cputs("destination path too long\n\15");
  132.     exit(1);
  133.   }
  134.  
  135.   strcpy(fdest,dest);
  136.   dtype = normal(fdest);  /* normalize destination path */
  137.  
  138.   if (dtype == 0) {  /* no specified type for destination */
  139.     dmask = A_FIL | A_DIR;
  140.   } else {
  141.     dmask = dtype;
  142.   }
  143.  
  144.   status2 = ffmf(fdest, A_MASK, &mydta2);  /* find info on dest */
  145. #ifdef debug
  146.   printf("fdest is %s, dmask=%d, status2 = %d\n", fdest, dmask, status2);
  147. #endif
  148.  
  149.   if (!status2) {  /* if destination exists  */
  150. #ifdef debug
  151.     printf("destination exists and has attr: %x\n", mydta2.dos.attr);
  152. #endif
  153.     /* check if destination is ambigious here */
  154.     if (lastc(fdest) != '\\') { /* root would cause error, so don't check it */
  155.       tmpdta = mydta2;  /* don't disturb mydta2, we need it later */
  156.       status = fnmf(&tmpdta);
  157.       if (!status) {  /* ha, there's more than one destination! */
  158.         cputs("destination is ambigious\n\15");
  159.         exit(1);
  160.       }
  161.     }
  162.  
  163.     /* if destination is an existing file, report error */
  164.     if ((mydta2.dos.attr & A_DIR) == 0) {
  165.       cputs("destination is an existing file!\n\15");
  166.       exit(1);
  167.     }
  168.  
  169.     /* fix fdest so that a destination of "*\." will have the expanded name
  170.     ** by removing the * and appending the directory name found by ffmf.
  171.     ** This will cause an error if fdest is the root of a 'subst' disk,
  172.     ** so we must make sure fdest is not a root.
  173.     */
  174.     if (lastc(fdest) != '\\') {  /* if not root then */
  175.       tmp = strrchr(fdest,'\\');  /* find last '\' */
  176.       if (tmp == NULL) {  /* no '\' ????!!!! */
  177.         cputs("error after strrchr: cannot find \\\n\15");
  178.         error("front",0);
  179.       }
  180.       strcpy(tmp+1, mydta2.dos.fn);  /* dest with wild cards expanded */
  181.     }
  182.  
  183.   } else {  /* destination not found */
  184.  
  185. #ifdef debug
  186.     printf("destination not exists\n");
  187. #endif
  188.  
  189.     if (dtype == A_DIR) {  /* specified directory not exist */
  190.       cputs("can't find destination directory\n\15");
  191.       exit(1);
  192.     } else {  /* destination type not specified or of file type */
  193.       /* check: source better be unambigious */
  194.       if (argc > 3) {
  195.         cputs("can't rename more than one source\n\15");
  196.         exit(1);
  197.       }
  198.       /* destination is not found, let's lookup destination's parent.
  199.       ** This has to exist (e.g. if C:\X\Y is destination and doesn't
  200.       ** exist, C:\X should still exists). This lookup also allows us
  201.       ** to compare the drive number between the destination and the
  202.       ** source to detect cross device move.
  203.       */
  204.       strcpy(fdest2, fdest);
  205.       chopath(fdest2);  /* remove last portion of path */
  206.       status = ffmf(fdest2, A_MASK, &mydta2);
  207.       if (status) {  /* if destination's parent doesn't exist */
  208.         putn("path \"", fdest2, "\" not exist!\n\15", 0);
  209.         exit(1);
  210.       } else if ((mydta2.dos.attr & A_DIR) == 0) { /* not a directory */
  211.         putn("\"", fdest2, "\" is not a directory!\n\15", 0);
  212.         exit(1);
  213.       }
  214.     }
  215.   }
  216.  
  217.   brk_st = bstat();  /* get current break status (on or off) */
  218.  
  219.   /***********************************************************/
  220.   /* now process SOURCE **************************************/
  221.   /***********************************************************/
  222.   for (i=1; i < argc - 1; i++) {  /* for all source specification */
  223.     source = argv[i];
  224.     if (strlen(source) > plimit) {  /* rare user error but just in case */
  225.       cputs("source path too long! skipped\n\15");
  226.       continue;  /* next argument */
  227.     }
  228.  
  229.     strcpy(fsource,source);
  230.     stype = extype(fsource);  /* extract type */
  231.     stype |= normal(fsource);  /* normalize source path */
  232.  
  233. #ifdef debug
  234.     printf("normalized source path: %s\n", fsource);
  235. #endif
  236.  
  237.     if ((stype & (A_DIR | A_FIL)) == 0) {  /* no specified type, use default */
  238.       smask = stype | A_FIL | A_DIR;  /* look for file or directory */
  239.     } else {
  240.       smask = stype;
  241.     }
  242.  
  243.     status = ffmf(fsource, smask, &mydta1);  /* find info on source */
  244.     if (status) {
  245.       putn(source, " not found\n\15", 0);
  246.       continue;
  247.     }
  248.  
  249.     /* see if source is on a network disk */
  250.     if ((mydta1.dos.drv_no < 0) || (mydta1.dos.drv_no >= LASTDRV)) {
  251.       cputs("sorry, mv doesn't work on network disks\n\15");
  252.       exit(1);
  253.     }
  254.  
  255.     /* is source and destination on same drive? */
  256.     if (mydta1.dos.drv_no != mydta2.dos.drv_no) {
  257.       cputs("source and destination must be on same physical drive\n\15");
  258.       exit(1);
  259.     }
  260.  
  261.     /* check: if destination does not exist, source better be unique */
  262.     if (status2) {  /* destination does not exist */
  263.       if (lastc(fsource) != '\\') { /* don't call fnmf on root */
  264.         tmpdta = mydta1;  /* do not change mydta1, mess with a copy */
  265.         status = fnmf(&tmpdta);  /* see if there's more than one source */
  266.         if (!status) {  /* source is ambigious */
  267.           cputs("can't rename more than one source\n\15");
  268.           exit(1);
  269.         }
  270.       }
  271.     }
  272.  
  273.     do {  /* repeat for all source wildcard */
  274.  
  275. #ifdef debug
  276.       printf("source found, fn: %s\n", mydta1.dos.fn);
  277.       printf("found attribute is %2xh\n", mydta1.dos.attr);
  278. #endif
  279.  
  280.       /* form source name with wild cards expanded */
  281.       chopath(fsource);  /* chop off wild cards */
  282.       catpath(fsource, mydta1.dos.fn);  /* append expanded name */
  283.  
  284.       if (status2) {  /* destination not exist */
  285.  
  286.         if (stype & A_INT) {  /* interactive mode */
  287.           doit = prompt(fsource,fdest);
  288.         } else {  /* in batch mode, always do it */
  289.           putn(fsource, " DD ", fdest, "  ", 0);
  290.           doit = 1;  /* do it! */
  291.         }
  292.  
  293.         if (doit) {
  294.  
  295.           if (mydta1.dos.attr & A_DIR) {  /* rename or move directory */
  296.  
  297.             status =  mvdir(mydta1, fsource, fdest);
  298.             if (!status) {
  299.               cputs("OK.\n\15");
  300.             }
  301.  
  302.           } else {  /* rename or move file */
  303.  
  304.             /* don't know what rename does when user presses break key,
  305.             ** so set break off to be safe
  306.             */
  307.             bset(0);  /* turn break off (disable break key) */
  308.             status = rename(fdest,fsource);  /* use fdest or dest? */
  309.             bset(brk_st);  /* restore break status to orginal value */
  310.  
  311.             if (status) {
  312.               putn("can't\n\15",
  313.                    "invalid file name/disk write protected\n\15", 0);
  314.             } else {
  315.               cputs("ok\n\15");
  316.             }
  317.             /* fall thru to fnmf */
  318.           }
  319.  
  320.         } else {  /* doit = 0, because the user press 'n' */
  321.  
  322.           cputs("not moved\15\n");
  323.  
  324.         }
  325.  
  326.       } else {  /* now handle the case when destination do exists */
  327.  
  328. #ifdef debug
  329.         printf("dest exists, let's see...\n");
  330. #endif
  331.  
  332.         if (mydta2.dos.attr & A_DIR) {  /* destination is a directory */
  333. #ifdef debug
  334.           printf("before strcat fdest is %s\n", fdest);
  335.           printf("now cat with %13.13s\n", mydta1.dos.fn);
  336. #endif
  337.  
  338.           /* create destination + tail of source in fdest2
  339.           ** e.g.      source = \user\peter\cat
  340.           **      destination = \junk\haha
  341.           **           fdest2 = \junk\haha\cat
  342.           */
  343.           strcpy(fdest2, fdest);  /* make a work copy */
  344.           catpath(fdest2, mydta1.dos.fn);
  345.  
  346.           if (stype & A_INT) {  /* interactive mode */
  347.             doit = prompt(fsource,fdest2);
  348.           } else {  /* in batch mode, always do it */
  349.             putn(fsource, " DD ", fdest2, "  ", 0);
  350.             doit = 1;  /* do it! */
  351.           }
  352.  
  353.           if (doit) {
  354.  
  355. #ifdef debug
  356.             printf("dest+tail of source: %s\n", fdest2);
  357. #endif
  358.  
  359.             if (mydta1.dos.attr & A_DIR) {  /* source is also a directory */
  360.  
  361.               status = mvdir(mydta1, fsource, fdest2);
  362.               if (!status) {
  363.                 cputs("OK.\n\15");
  364.               }
  365.               /* fall down to fnmf */
  366.  
  367.             } else {  /* move a file into a directory */
  368.  
  369.               bset(0);  /* turn break off (disable break key) */
  370.               status = rename(fdest2, fsource);
  371.               bset(brk_st);  /* set break status to orginal value */
  372.  
  373.               if (status) {
  374.                 cputs("can't; file exists already/disk write-protected\n\15");
  375.               } else {
  376.                 cputs("ok.\n\15");
  377.               }
  378.             }
  379.           } else {  /* doit = 0 */
  380.             cputs("not moved\15\n");
  381.           }
  382.  
  383.         } else {  /* dest is an existing file! */
  384.  
  385.           putn(fsource, " DD ", fdest,
  386.                "   can't rename: file exists already\n\15", 0);
  387.  
  388.         }
  389.  
  390.       }  /* if dest exist or not */
  391.  
  392.       status = fnmf(&mydta1);  /* find info on next matching source */
  393.  
  394.     } while (status == 0);  /* while there are matching files */
  395.   }  /* for different sources */
  396.  
  397.   exit(0);  /* no error */
  398. }
  399.  
  400. prompt(s,d)  /* return 1 for 'y'; 0 for 'n' */
  401. char *s, *d; /* part of the prompt */
  402. {
  403.   char c;
  404.  
  405.   do {
  406.     putn(s, " DD ", d, " (y/n/q; ?=help): ", 0);
  407.     c = getkey("yYnNqQ?");
  408.     putch(' ');
  409.     switch (c) {
  410.  
  411.       case '?':
  412.         putn("\15\n",
  413.              "y - yes, move it\15\n",
  414.              "n - no, skip it\15\n",
  415.              "q - quit to DOS\15\n",
  416.              "? - this help message\15\n\n",
  417.              0);
  418.         break;
  419.  
  420.       case 'y':
  421.       case 'Y':
  422.         return 1;
  423.  
  424.       case 'n':
  425.       case 'N':
  426.         return 0;
  427.  
  428.       case 'q':
  429.       case 'Q':
  430.         cputs("Quit.\15\n");
  431.         exit(0);
  432.  
  433.       default:
  434.         cputs("\15\nHow do I get here?\15\n");
  435.         error("prompt",0);
  436.  
  437.     }  /* end switch */
  438.   } while (1);
  439. }
  440.  
  441. char getkey(valid)  /* wait for valid key and echo it */
  442. char *valid;
  443. {
  444.   char c;
  445.   int i;
  446.  
  447.   do {
  448.     c = getch();
  449.     i = index(valid,c);
  450.     if (i > -1) {
  451.       putch(c);  /* echo valid key */
  452.       return c;
  453.     } else {  /* beep at invalid key */
  454.       putch(7);
  455.     }
  456.   } while (1);
  457. }