home *** CD-ROM | disk | FTP | other *** search
- /* 8-Sep-86 16:13:42-PDT,14202;000000000000
- Return-Path: <pwu@unix.macc.wisc.edu>
- Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:12:30 PDT
- Received: by unix.macc.wisc.edu;
- id AA04958; 4.12/5; Mon, 8 Sep 86 17:31:19 cdt
- Date: Mon, 8 Sep 86 17:31:19 cdt
- From: Peter Wu <pwu@unix.macc.wisc.edu>
- Message-Id: <8609082231.AA04958@unix.macc.wisc.edu>
- To: info-ibmpc-request@mosis
- Subject: front.c
- */
- /* Program to move files and/or subdirectories - like mv on unix
- ** Written by Peter Wu July, 86 @ Faculty Support Center @ UW-Madison
- ** Compile with IBM C version 1.00 (or Microsoft C 3.00).
- ** Link with fstat, mv, normal, sector, absdr, func32h, break, putn /stack:5000
- **
- ** This is module "front.c", the front end of mv. It looks at the sources
- ** and destination supplied by the user and determines whether to call
- ** mvdir to move/rename sub-directories or call rename to move/rename files.
- */
-
- /* programmer's notes:
- -- won't work on network disk
- --
- -- things to work on:
- --
- -- be interactive:
- -- ask if user wants to replace existing file/directory
- -- verbose mode where user must confirm each move
- -- better error handling
- -- make it work on network disks
- -- detect and report write-protected disk if possible
- -- allow /f option on destination?
- --
- -- bugs:
- +--------------------------------+
- | c: |
- | subst p: /user/peter/mv/test |
- | mv /usr/peter/mv/test . |
- +--------------------------------+
- -- The above sequence will prevent mv from removing the source directory after
- -- the content is moved to the destination directory. I don't know any way to
- -- detect this BEFORE moving the content. So my solution is to print a message
- -- telling the user to remove the source directory (now empty) by hand after
- -- he got rid of the 'subst drive'.
- */
- #define LINT_ARGS
-
- #define UNK_TYP 0
-
- #define A_INT 0x80 /* interactive file attribute */
-
- #define LASTDRV 26 /* drive# larger than this is network share disk */
- #define NULL (char *) 0
- #define PLEN 200 /* max. path length; hope it's long enough */
-
- #include "dta.h"
- #include "date.h"
- #include <conio.h>
- #include <string.h>
- #include <stdlib.h>
-
- char lastc(char *);
- char getkey(char *);
-
- int brk_st; /* original break status: 0=break off, 1=break on */
-
- /* When break is on, user can break the program when any DOS function is
- ** called. When break is off, user can only break the program when a DOS
- ** I/O function is called (if the user presses break before an I/O function,
- ** DOS will remember the break but let the program keep running until the
- ** program calls an IO function). It is undesirable to have break on
- ** all the time since for instance if the user breaks the program after I
- ** wrote one sector to disk (I need to write three sectors per sub-directory
- ** moved), he'll end up with an inconsistent directory. The solution is to
- ** make sure break is off when I'm doing disk writes, and restore it
- ** to its orginal status when I'm done writing the disk.
- ** If the user really wants to mess up his disk, he can still do it with
- ** ctrl-alt-del or switching the power off in the middle of running mv.
- */
-
- main(argc,argv)
- int argc;
- char *argv[];
- {
- unsigned int status, dtype, stype, status2, smask, dmask, i, drv, plimit,
- doit;
- union dtbuf mydta1, mydta2, tmpdta;
- char fsource[PLEN], fdest[PLEN], *source, *dest, *tmp, fdest2[PLEN];
-
- if (argc < 3) { /* print help */
- putn(
-
- "Usage: MV <source1> <source2> .. <sourceN> <dest>\n\15",
- " MV renames/moves the source files/directories to the destination.\n\15",
- " Wildcards ok. Specify source type with /d, /f, /h, and /i.\n\15",
- " /d=sub-directories, /f=files, /h=search hidden, /i=interactive.\n\15",
- " \"*.*\\.\" or \"*.*/d\" specifies all sub-directories only\n\15",
- " \"*.*/f\" specifies all visible files only\n\15",
- " \"*.*/hf\" specifies all visible and hidden files\n\15",
- " \"*.*/fi\" will prompt you (move or not) for every file found\n\15",
- " Version 1.20 made ", date, ". For DOS 2.xx and 3.xx.\n\n\15",
- "Please send comments/bug reports to one of the following addresses:\n\15",
- " Arpanet: pwu@unix.macc.wisc.edu\n\15",
- " Bitnet: WU at WISVMACC\n\15",
- " CompuServe: 76377,1332\n\15",
- " UUCP: {akgua|ihnp4|seismo|harvard|allegra|ucbvax}!uwvax!uwmacc!pwu\n\15",
- NULL);
-
- exit(0);
- }
-
- /* test DOS version */
- switch (_osmajor) {
- case 2: /* dos 2.xx */
- break;
- case 3: /* dos 3.xx */
- if (_osminor <= 20) { /* make sure it's no later than version 3.20 */
- break;
- }
- default:
- cputs("need DOS between version 2.00 and 3.20\n\15");
- exit(1);
- }
-
- plimit = PLEN - 80; /* limit on user supplied path name */
-
- /* process destination first */
- dest = argv[argc-1];
- if (strlen(dest) > plimit) {
- cputs("destination path too long\n\15");
- exit(1);
- }
-
- strcpy(fdest,dest);
- dtype = normal(fdest); /* normalize destination path */
-
- if (dtype == 0) { /* no specified type for destination */
- dmask = A_FIL | A_DIR;
- } else {
- dmask = dtype;
- }
-
- status2 = ffmf(fdest, A_MASK, &mydta2); /* find info on dest */
- #ifdef debug
- printf("fdest is %s, dmask=%d, status2 = %d\n", fdest, dmask, status2);
- #endif
-
- if (!status2) { /* if destination exists */
- #ifdef debug
- printf("destination exists and has attr: %x\n", mydta2.dos.attr);
- #endif
- /* check if destination is ambigious here */
- if (lastc(fdest) != '\\') { /* root would cause error, so don't check it */
- tmpdta = mydta2; /* don't disturb mydta2, we need it later */
- status = fnmf(&tmpdta);
- if (!status) { /* ha, there's more than one destination! */
- cputs("destination is ambigious\n\15");
- exit(1);
- }
- }
-
- /* if destination is an existing file, report error */
- if ((mydta2.dos.attr & A_DIR) == 0) {
- cputs("destination is an existing file!\n\15");
- exit(1);
- }
-
- /* fix fdest so that a destination of "*\." will have the expanded name
- ** by removing the * and appending the directory name found by ffmf.
- ** This will cause an error if fdest is the root of a 'subst' disk,
- ** so we must make sure fdest is not a root.
- */
- if (lastc(fdest) != '\\') { /* if not root then */
- tmp = strrchr(fdest,'\\'); /* find last '\' */
- if (tmp == NULL) { /* no '\' ????!!!! */
- cputs("error after strrchr: cannot find \\\n\15");
- error("front",0);
- }
- strcpy(tmp+1, mydta2.dos.fn); /* dest with wild cards expanded */
- }
-
- } else { /* destination not found */
-
- #ifdef debug
- printf("destination not exists\n");
- #endif
-
- if (dtype == A_DIR) { /* specified directory not exist */
- cputs("can't find destination directory\n\15");
- exit(1);
- } else { /* destination type not specified or of file type */
- /* check: source better be unambigious */
- if (argc > 3) {
- cputs("can't rename more than one source\n\15");
- exit(1);
- }
- /* destination is not found, let's lookup destination's parent.
- ** This has to exist (e.g. if C:\X\Y is destination and doesn't
- ** exist, C:\X should still exists). This lookup also allows us
- ** to compare the drive number between the destination and the
- ** source to detect cross device move.
- */
- strcpy(fdest2, fdest);
- chopath(fdest2); /* remove last portion of path */
- status = ffmf(fdest2, A_MASK, &mydta2);
- if (status) { /* if destination's parent doesn't exist */
- putn("path \"", fdest2, "\" not exist!\n\15", 0);
- exit(1);
- } else if ((mydta2.dos.attr & A_DIR) == 0) { /* not a directory */
- putn("\"", fdest2, "\" is not a directory!\n\15", 0);
- exit(1);
- }
- }
- }
-
- brk_st = bstat(); /* get current break status (on or off) */
-
- /***********************************************************/
- /* now process SOURCE **************************************/
- /***********************************************************/
- for (i=1; i < argc - 1; i++) { /* for all source specification */
- source = argv[i];
- if (strlen(source) > plimit) { /* rare user error but just in case */
- cputs("source path too long! skipped\n\15");
- continue; /* next argument */
- }
-
- strcpy(fsource,source);
- stype = extype(fsource); /* extract type */
- stype |= normal(fsource); /* normalize source path */
-
- #ifdef debug
- printf("normalized source path: %s\n", fsource);
- #endif
-
- if ((stype & (A_DIR | A_FIL)) == 0) { /* no specified type, use default */
- smask = stype | A_FIL | A_DIR; /* look for file or directory */
- } else {
- smask = stype;
- }
-
- status = ffmf(fsource, smask, &mydta1); /* find info on source */
- if (status) {
- putn(source, " not found\n\15", 0);
- continue;
- }
-
- /* see if source is on a network disk */
- if ((mydta1.dos.drv_no < 0) || (mydta1.dos.drv_no >= LASTDRV)) {
- cputs("sorry, mv doesn't work on network disks\n\15");
- exit(1);
- }
-
- /* is source and destination on same drive? */
- if (mydta1.dos.drv_no != mydta2.dos.drv_no) {
- cputs("source and destination must be on same physical drive\n\15");
- exit(1);
- }
-
- /* check: if destination does not exist, source better be unique */
- if (status2) { /* destination does not exist */
- if (lastc(fsource) != '\\') { /* don't call fnmf on root */
- tmpdta = mydta1; /* do not change mydta1, mess with a copy */
- status = fnmf(&tmpdta); /* see if there's more than one source */
- if (!status) { /* source is ambigious */
- cputs("can't rename more than one source\n\15");
- exit(1);
- }
- }
- }
-
- do { /* repeat for all source wildcard */
-
- #ifdef debug
- printf("source found, fn: %s\n", mydta1.dos.fn);
- printf("found attribute is %2xh\n", mydta1.dos.attr);
- #endif
-
- /* form source name with wild cards expanded */
- chopath(fsource); /* chop off wild cards */
- catpath(fsource, mydta1.dos.fn); /* append expanded name */
-
- if (status2) { /* destination not exist */
-
- if (stype & A_INT) { /* interactive mode */
- doit = prompt(fsource,fdest);
- } else { /* in batch mode, always do it */
- putn(fsource, " DD ", fdest, " ", 0);
- doit = 1; /* do it! */
- }
-
- if (doit) {
-
- if (mydta1.dos.attr & A_DIR) { /* rename or move directory */
-
- status = mvdir(mydta1, fsource, fdest);
- if (!status) {
- cputs("OK.\n\15");
- }
-
- } else { /* rename or move file */
-
- /* don't know what rename does when user presses break key,
- ** so set break off to be safe
- */
- bset(0); /* turn break off (disable break key) */
- status = rename(fdest,fsource); /* use fdest or dest? */
- bset(brk_st); /* restore break status to orginal value */
-
- if (status) {
- putn("can't\n\15",
- "invalid file name/disk write protected\n\15", 0);
- } else {
- cputs("ok\n\15");
- }
- /* fall thru to fnmf */
- }
-
- } else { /* doit = 0, because the user press 'n' */
-
- cputs("not moved\15\n");
-
- }
-
- } else { /* now handle the case when destination do exists */
-
- #ifdef debug
- printf("dest exists, let's see...\n");
- #endif
-
- if (mydta2.dos.attr & A_DIR) { /* destination is a directory */
- #ifdef debug
- printf("before strcat fdest is %s\n", fdest);
- printf("now cat with %13.13s\n", mydta1.dos.fn);
- #endif
-
- /* create destination + tail of source in fdest2
- ** e.g. source = \user\peter\cat
- ** destination = \junk\haha
- ** fdest2 = \junk\haha\cat
- */
- strcpy(fdest2, fdest); /* make a work copy */
- catpath(fdest2, mydta1.dos.fn);
-
- if (stype & A_INT) { /* interactive mode */
- doit = prompt(fsource,fdest2);
- } else { /* in batch mode, always do it */
- putn(fsource, " DD ", fdest2, " ", 0);
- doit = 1; /* do it! */
- }
-
- if (doit) {
-
- #ifdef debug
- printf("dest+tail of source: %s\n", fdest2);
- #endif
-
- if (mydta1.dos.attr & A_DIR) { /* source is also a directory */
-
- status = mvdir(mydta1, fsource, fdest2);
- if (!status) {
- cputs("OK.\n\15");
- }
- /* fall down to fnmf */
-
- } else { /* move a file into a directory */
-
- bset(0); /* turn break off (disable break key) */
- status = rename(fdest2, fsource);
- bset(brk_st); /* set break status to orginal value */
-
- if (status) {
- cputs("can't; file exists already/disk write-protected\n\15");
- } else {
- cputs("ok.\n\15");
- }
- }
- } else { /* doit = 0 */
- cputs("not moved\15\n");
- }
-
- } else { /* dest is an existing file! */
-
- putn(fsource, " DD ", fdest,
- " can't rename: file exists already\n\15", 0);
-
- }
-
- } /* if dest exist or not */
-
- status = fnmf(&mydta1); /* find info on next matching source */
-
- } while (status == 0); /* while there are matching files */
- } /* for different sources */
-
- exit(0); /* no error */
- }
-
- prompt(s,d) /* return 1 for 'y'; 0 for 'n' */
- char *s, *d; /* part of the prompt */
- {
- char c;
-
- do {
- putn(s, " DD ", d, " (y/n/q; ?=help): ", 0);
- c = getkey("yYnNqQ?");
- putch(' ');
- switch (c) {
-
- case '?':
- putn("\15\n",
- "y - yes, move it\15\n",
- "n - no, skip it\15\n",
- "q - quit to DOS\15\n",
- "? - this help message\15\n\n",
- 0);
- break;
-
- case 'y':
- case 'Y':
- return 1;
-
- case 'n':
- case 'N':
- return 0;
-
- case 'q':
- case 'Q':
- cputs("Quit.\15\n");
- exit(0);
-
- default:
- cputs("\15\nHow do I get here?\15\n");
- error("prompt",0);
-
- } /* end switch */
- } while (1);
- }
-
- char getkey(valid) /* wait for valid key and echo it */
- char *valid;
- {
- char c;
- int i;
-
- do {
- c = getch();
- i = index(valid,c);
- if (i > -1) {
- putch(c); /* echo valid key */
- return c;
- } else { /* beep at invalid key */
- putch(7);
- }
- } while (1);
- }