1*1931Sroot static char *sccsid = "@(#)mv.c 4.2 (Berkeley) 12/18/80"; 21216Sbill /* 31216Sbill * mv file1 file2 41216Sbill */ 51216Sbill 61216Sbill #include <stdio.h> 71216Sbill #include <sys/types.h> 81216Sbill #include <sys/stat.h> 91216Sbill #include <sys/dir.h> 101216Sbill #include <signal.h> 111216Sbill 121216Sbill #define DOT "." 131216Sbill #define DOTDOT ".." 141216Sbill #define DELIM '/' 151216Sbill #define SDELIM "/" 161216Sbill #define MAXN 100 171216Sbill #define MODEBITS 07777 181216Sbill #define ROOTINO 2 191216Sbill 201216Sbill char *pname(); 211216Sbill char *sprintf(); 221216Sbill char *dname(); 231216Sbill struct stat s1, s2; 241216Sbill int iflag = 0; /* interactive flag. If this flag is set, 251216Sbill * the user is queried before files are 261216Sbill * destroyed by cp. 271216Sbill */ 281216Sbill int fflag = 0; /* force flag. supercedes all restrictions */ 291216Sbill 301216Sbill main(argc, argv) 311216Sbill register char *argv[]; 321216Sbill { 331216Sbill register i, r; 34*1931Sroot register char *arg; 351216Sbill 361216Sbill /* get the flag(s) */ 371216Sbill 381216Sbill if (argc < 2) 391216Sbill goto usage; 40*1931Sroot while (argc>1 && *argv[1] == '-') { 411216Sbill argc--; 42*1931Sroot arg = *++argv; 431216Sbill 44*1931Sroot /* 45*1931Sroot * all files following a null option are considered file names 46*1931Sroot */ 47*1931Sroot if (*(arg+1) == '\0') break; 48*1931Sroot 49*1931Sroot while (*++arg != '\0') 50*1931Sroot switch (*arg) { 51*1931Sroot 521216Sbill /* interactive mode */ 531216Sbill case 'i': 541216Sbill iflag++; 551216Sbill break; 561216Sbill 571216Sbill /* force moves */ 581216Sbill case 'f': 591216Sbill fflag++; 601216Sbill break; 611216Sbill 621216Sbill /* don't live with bad options */ 631216Sbill default: 641216Sbill goto usage; 651216Sbill } 661216Sbill } 671216Sbill if (argc < 3) 681216Sbill goto usage; 691216Sbill if (stat(argv[1], &s1) < 0) { 701216Sbill fprintf(stderr, "mv: cannot access %s\n", argv[1]); 711216Sbill return(1); 721216Sbill } 731216Sbill if ((s1.st_mode & S_IFMT) == S_IFDIR) { 741216Sbill if (argc != 3) 751216Sbill goto usage; 761216Sbill return mvdir(argv[1], argv[2]); 771216Sbill } 781216Sbill setuid(getuid()); 791216Sbill if (argc > 3) 801216Sbill if (stat(argv[argc-1], &s2) < 0 || (s2.st_mode & S_IFMT) != S_IFDIR) 811216Sbill goto usage; 821216Sbill r = 0; 831216Sbill for (i=1; i<argc-1; i++) 841216Sbill r |= move(argv[i], argv[argc-1]); 851216Sbill return(r); 861216Sbill usage: 87*1931Sroot fprintf(stderr, "usage: mv [-if] f1 f2; or mv [-if] d1 d2; or mv [-if] f1 ... fn d1\n"); 881216Sbill return(1); 891216Sbill } 901216Sbill 911216Sbill move(source, target) 921216Sbill char *source, *target; 931216Sbill { 941216Sbill register c, i; 951216Sbill int status; 961216Sbill char buf[MAXN]; 971216Sbill 981216Sbill if (stat(source, &s1) < 0) { 991216Sbill fprintf(stderr, "mv: cannot access %s\n", source); 1001216Sbill return(1); 1011216Sbill } 1021216Sbill if ((s1.st_mode & S_IFMT) == S_IFDIR) { 1031216Sbill fprintf(stderr, "mv: directory rename only\n"); 1041216Sbill return(1); 1051216Sbill } 1061216Sbill if (stat(target, &s2) >= 0) { 1071216Sbill if ((s2.st_mode & S_IFMT) == S_IFDIR) { 1081216Sbill sprintf(buf, "%s/%s", target, dname(source)); 1091216Sbill target = buf; 1101216Sbill } 1111216Sbill if (stat(target, &s2) >= 0) { 1121216Sbill if ((s2.st_mode & S_IFMT) == S_IFDIR) { 1131216Sbill fprintf(stderr, "mv: %s is a directory\n", target); 1141216Sbill return(1); 1151216Sbill } else if (iflag && !fflag) { 1161216Sbill fprintf(stderr, "remove %s? ", target); 1171216Sbill i = c = getchar(); 1181216Sbill while (c != '\n' && c != EOF) 1191216Sbill c = getchar(); 1201216Sbill if (i != 'y') 1211216Sbill return(1); 1221216Sbill } 1231216Sbill if (s1.st_dev==s2.st_dev && s1.st_ino==s2.st_ino) { 1241216Sbill fprintf(stderr, "mv: %s and %s are identical\n", 1251216Sbill source, target); 1261216Sbill return(1); 1271216Sbill } 1281216Sbill if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) { 1291216Sbill fprintf(stderr, "override protection %o for %s? ", 1301216Sbill s2.st_mode & MODEBITS, target); 1311216Sbill i = c = getchar(); 1321216Sbill while (c != '\n' && c != EOF) 1331216Sbill c = getchar(); 1341216Sbill if (i != 'y') 1351216Sbill return(1); 1361216Sbill } 1371216Sbill if (unlink(target) < 0) { 1381216Sbill fprintf(stderr, "mv: cannot unlink %s\n", target); 1391216Sbill return(1); 1401216Sbill } 1411216Sbill } 1421216Sbill } 1431216Sbill if (link(source, target) < 0) { 1441216Sbill i = fork(); 1451216Sbill if (i == -1) { 1461216Sbill fprintf(stderr, "mv: try again\n"); 1471216Sbill return(1); 1481216Sbill } 1491216Sbill if (i == 0) { 1501216Sbill execl("/bin/cp", "cp", source, target, 0); 1511216Sbill fprintf(stderr, "mv: cannot exec cp\n"); 1521216Sbill exit(1); 1531216Sbill } 1541216Sbill while ((c = wait(&status)) != i && c != -1) 1551216Sbill ; 1561216Sbill if (status != 0) 1571216Sbill return(1); 1581216Sbill utime(target, &s1.st_atime); 1591216Sbill } 1601216Sbill if (unlink(source) < 0) { 1611216Sbill fprintf(stderr, "mv: cannot unlink %s\n", source); 1621216Sbill return(1); 1631216Sbill } 1641216Sbill return(0); 1651216Sbill } 1661216Sbill 1671216Sbill mvdir(source, target) 1681216Sbill char *source, *target; 1691216Sbill { 1701216Sbill register char *p; 1711216Sbill register i; 1721216Sbill char buf[MAXN]; 1731216Sbill char c,cc; 1741216Sbill 1751216Sbill if (stat(target, &s2) >= 0) { 1761216Sbill if ((s2.st_mode&S_IFMT) != S_IFDIR) { 1771216Sbill fprintf(stderr, "mv: %s exists\n", target); 1781216Sbill return(1); 1791216Sbill } else if (iflag && !fflag) { 1801216Sbill fprintf(stderr, "remove %s? ", target); 1811216Sbill cc = c = getchar(); 1821216Sbill while (c != '\n' && c != EOF) 1831216Sbill c = getchar(); 1841216Sbill if (cc != 'y') 1851216Sbill return(1); 1861216Sbill } 1871216Sbill if (strlen(target) > MAXN-DIRSIZ-2) { 1881216Sbill fprintf(stderr, "mv :target name too long\n"); 1891216Sbill return(1); 1901216Sbill } 1911216Sbill strcpy(buf, target); 1921216Sbill target = buf; 1931216Sbill strcat(buf, SDELIM); 1941216Sbill strcat(buf, dname(source)); 1951216Sbill if (stat(target, &s2) >= 0) { 1961216Sbill fprintf(stderr, "mv: %s exists\n", buf); 1971216Sbill return(1); 1981216Sbill } 1991216Sbill } 2001216Sbill if (strcmp(source, target) == 0) { 2011216Sbill fprintf(stderr, "mv: ?? source == target, source exists and target doesnt\n"); 2021216Sbill return(1); 2031216Sbill } 2041216Sbill p = dname(source); 2051216Sbill if (!strcmp(p, DOT) || !strcmp(p, DOTDOT) || !strcmp(p, "") || p[strlen(p)-1]=='/') { 2061216Sbill fprintf(stderr, "mv: cannot rename %s\n", p); 2071216Sbill return(1); 2081216Sbill } 2091216Sbill if (stat(pname(source), &s1) < 0 || stat(pname(target), &s2) < 0) { 2101216Sbill fprintf(stderr, "mv: cannot locate parent\n"); 2111216Sbill return(1); 2121216Sbill } 2131216Sbill if (access(pname(target), 2) < 0) { 2141216Sbill fprintf(stderr, "mv: no write access to %s\n", pname(target)); 2151216Sbill return(1); 2161216Sbill } 2171216Sbill if (access(pname(source), 2) < 0) { 2181216Sbill fprintf(stderr, "mv: no write access to %s\n", pname(source)); 2191216Sbill return(1); 2201216Sbill } 2211216Sbill if (access(source, 2) < 0) { 2221216Sbill fprintf(stderr, "mv: no write access to %s\n", source); 2231216Sbill return(1); 2241216Sbill } 2251216Sbill if (s1.st_dev != s2.st_dev) { 2261216Sbill fprintf(stderr, "mv: cannot move directories across devices\n"); 2271216Sbill return(1); 2281216Sbill } 2291216Sbill if (s1.st_ino != s2.st_ino) { 2301216Sbill char dst[MAXN+5]; 2311216Sbill 2321216Sbill if (chkdot(source) || chkdot(target)) { 2331216Sbill fprintf(stderr, "mv: Sorry, path names including %s aren't allowed\n", DOTDOT); 2341216Sbill return(1); 2351216Sbill } 2361216Sbill stat(source, &s1); 2371216Sbill if (check(pname(target), s1.st_ino)) 2381216Sbill return(1); 2391216Sbill for (i = 1; i <= NSIG; i++) 2401216Sbill signal(i, SIG_IGN); 2411216Sbill if (link(source, target) < 0) { 2421216Sbill fprintf(stderr, "mv: cannot link %s to %s\n", target, source); 2431216Sbill return(1); 2441216Sbill } 2451216Sbill if (unlink(source) < 0) { 2461216Sbill fprintf(stderr, "mv: %s: cannot unlink\n", source); 2471216Sbill unlink(target); 2481216Sbill return(1); 2491216Sbill } 2501216Sbill strcat(dst, target); 2511216Sbill strcat(dst, "/"); 2521216Sbill strcat(dst, DOTDOT); 2531216Sbill if (unlink(dst) < 0) { 2541216Sbill fprintf(stderr, "mv: %s: cannot unlink\n", dst); 2551216Sbill if (link(target, source) >= 0) 2561216Sbill unlink(target); 2571216Sbill return(1); 2581216Sbill } 2591216Sbill if (link(pname(target), dst) < 0) { 2601216Sbill fprintf(stderr, "mv: cannot link %s to %s\n", 2611216Sbill dst, pname(target)); 2621216Sbill if (link(pname(source), dst) >= 0) 2631216Sbill if (link(target, source) >= 0) 2641216Sbill unlink(target); 2651216Sbill return(1); 2661216Sbill } 2671216Sbill return(0); 2681216Sbill } 2691216Sbill if (link(source, target) < 0) { 2701216Sbill fprintf(stderr, "mv: cannot link %s and %s\n", 2711216Sbill source, target); 2721216Sbill return(1); 2731216Sbill } 2741216Sbill if (unlink(source) < 0) { 2751216Sbill fprintf(stderr, "mv: ?? cannot unlink %s\n", source); 2761216Sbill return(1); 2771216Sbill } 2781216Sbill return(0); 2791216Sbill } 2801216Sbill 2811216Sbill char * 2821216Sbill pname(name) 2831216Sbill register char *name; 2841216Sbill { 2851216Sbill register c; 2861216Sbill register char *p, *q; 2871216Sbill static char buf[MAXN]; 2881216Sbill 2891216Sbill p = q = buf; 2901216Sbill while (c = *p++ = *name++) 2911216Sbill if (c == DELIM) 2921216Sbill q = p-1; 2931216Sbill if (q == buf && *q == DELIM) 2941216Sbill q++; 2951216Sbill *q = 0; 2961216Sbill return buf[0]? buf : DOT; 2971216Sbill } 2981216Sbill 2991216Sbill char * 3001216Sbill dname(name) 3011216Sbill register char *name; 3021216Sbill { 3031216Sbill register char *p; 3041216Sbill 3051216Sbill p = name; 3061216Sbill while (*p) 3071216Sbill if (*p++ == DELIM && *p) 3081216Sbill name = p; 3091216Sbill return name; 3101216Sbill } 3111216Sbill 3121216Sbill check(spth, dinode) 3131216Sbill char *spth; 3141216Sbill ino_t dinode; 3151216Sbill { 3161216Sbill char nspth[MAXN]; 3171216Sbill struct stat sbuf; 3181216Sbill 3191216Sbill sbuf.st_ino = 0; 3201216Sbill 3211216Sbill strcpy(nspth, spth); 3221216Sbill while (sbuf.st_ino != ROOTINO) { 3231216Sbill if (stat(nspth, &sbuf) < 0) { 3241216Sbill fprintf(stderr, "mv: cannot access %s\n", nspth); 3251216Sbill return(1); 3261216Sbill } 3271216Sbill if (sbuf.st_ino == dinode) { 3281216Sbill fprintf(stderr, "mv: cannot move a directory into itself\n"); 3291216Sbill return(1); 3301216Sbill } 3311216Sbill if (strlen(nspth) > MAXN-2-sizeof(DOTDOT)) { 3321216Sbill fprintf(stderr, "mv: name too long\n"); 3331216Sbill return(1); 3341216Sbill } 3351216Sbill strcat(nspth, SDELIM); 3361216Sbill strcat(nspth, DOTDOT); 3371216Sbill } 3381216Sbill return(0); 3391216Sbill } 3401216Sbill 3411216Sbill chkdot(s) 3421216Sbill register char *s; 3431216Sbill { 3441216Sbill do { 3451216Sbill if (strcmp(dname(s), DOTDOT) == 0) 3461216Sbill return(1); 3471216Sbill s = pname(s); 3481216Sbill } while (strcmp(s, DOT) != 0 && strcmp(s, SDELIM) != 0); 3491216Sbill return(0); 3501216Sbill } 351