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