You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

473 lines
11 KiB

13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
  1. #include <unistd.h>
  2. #ifndef _POSIX_SOURCE
  3. #define _POSIX_SOURCE
  4. #endif
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #if !defined(__APPLE__)
  8. #include <malloc.h>
  9. #endif
  10. #include <string.h>
  11. #include <fcntl.h>
  12. #include <sys/param.h>
  13. #include <sys/types.h>
  14. #include <sys/stat.h>
  15. #include <dirent.h>
  16. #include <time.h>
  17. #include <stddef.h>
  18. #include <errno.h>
  19. #ifndef S_ISLNK
  20. #define S_ISLNK(mode) (((mode) & (_S_IFMT)) == (_S_IFLNK))
  21. #endif
  22. #ifndef PATH_MAX
  23. #define PATH_MAX 1024
  24. #endif
  25. #define progver "%s: scan/change symbolic links - v1.4.2\n\n"
  26. static char *progname;
  27. static int verbose = 0,
  28. fix_links = 0,
  29. recurse = 0,
  30. delete = 0,
  31. shorten = 0,
  32. testing = 0,
  33. single_fs = 1;
  34. /*
  35. * tidypath removes excess slashes and "." references from a path string
  36. */
  37. static int substr(char *s, char *old, char *new) {
  38. char *tmp = NULL;
  39. unsigned long oldlen = strlen(old), newlen = 0;
  40. if (NULL == strstr(s, old)) {
  41. return 0;
  42. }
  43. if (new) {
  44. newlen = strlen(new);
  45. }
  46. if (newlen > oldlen) {
  47. if ((tmp = malloc(strlen(s))) == NULL) {
  48. fprintf(stderr, "no memory\n");
  49. exit(1);
  50. }
  51. }
  52. while (NULL != (s = strstr(s, old))) {
  53. char *p, *old_s = s;
  54. if (new) {
  55. if (newlen > oldlen) {
  56. old_s = strcpy(tmp, s);
  57. }
  58. p = new;
  59. while (*p) {
  60. *s++ = *p++;
  61. }
  62. }
  63. p = old_s + oldlen;
  64. while ((*s++ = *p++));
  65. }
  66. if (tmp) {
  67. free(tmp);
  68. }
  69. return 1;
  70. }
  71. static int tidy_path(char *path) {
  72. int tidied = 0;
  73. char *s, *p;
  74. s = path + strlen(path) - 1;
  75. if (s[0] != '/') { /* tmp trailing slash simplifies things */
  76. s[1] = '/';
  77. s[2] = '\0';
  78. }
  79. while (substr(path, "/./", "/")) {
  80. tidied = 1;
  81. }
  82. while (substr(path, "//", "/")) {
  83. tidied = 1;
  84. }
  85. while ((p = strstr(path, "/../")) != NULL) {
  86. s = p + 3;
  87. for (p--; p != path; p--) {
  88. if (*p == '/') { break; }
  89. }
  90. if (*p != '/') { break; }
  91. while ((*p++ = *s++));
  92. tidied = 1;
  93. }
  94. if (*path == '\0') {
  95. strcpy(path, "/");
  96. }
  97. p = path + strlen(path) - 1;
  98. if (p != path && *p == '/') {
  99. *p-- = '\0'; /* remove tmp trailing slash */
  100. }
  101. while (p != path && *p == '/') { /* remove any others */
  102. *p-- = '\0';
  103. tidied = 1;
  104. }
  105. while (!strncmp(path, "./", 2)) {
  106. for (p = path, s = path + 2; (*p++ = *s++););
  107. tidied = 1;
  108. }
  109. return tidied;
  110. }
  111. static int shorten_path(char *path, char *abspath) {
  112. static char dir[PATH_MAX];
  113. int shortened = 0;
  114. char *p;
  115. /* get rid of unnecessary "../dir" sequences */
  116. while (abspath && strlen(abspath) > 1 && (p = strstr(path, "../"))) {
  117. /* find innermost occurance of "../dir", and save "dir" */
  118. int slashes = 2;
  119. char *a, *s, *d = dir;
  120. while ((s = strstr(p + 3, "../"))) {
  121. ++slashes;
  122. p = s;
  123. }
  124. s = p + 3;
  125. *d++ = '/';
  126. while (*s && *s != '/') {
  127. *d++ = *s++;
  128. }
  129. *d++ = '/';
  130. *d = '\0';
  131. if (!strcmp(dir, "//")) {
  132. break;
  133. }
  134. /* note: p still points at ../dir */
  135. if (*s != '/' || !*++s) {
  136. break;
  137. }
  138. a = abspath + strlen(abspath) - 1;
  139. while (slashes-- > 0) {
  140. if (a <= abspath) {
  141. goto ughh;
  142. }
  143. while (*--a != '/') {
  144. if (a <= abspath) {
  145. goto ughh;
  146. }
  147. }
  148. }
  149. if (strncmp(dir, a, strlen(dir))) {
  150. break;
  151. }
  152. while ((*p++ = *s++)); /* delete the ../dir */
  153. shortened = 1;
  154. }
  155. ughh:
  156. return shortened;
  157. }
  158. static void fix_symlink(char *path, dev_t my_dev) {
  159. static char lpath[PATH_MAX], new[PATH_MAX], abspath[PATH_MAX];
  160. char *p, *np, *lp, *tail, *msg;
  161. struct stat stbuf, lstbuf;
  162. int fix_abs = 0, fix_messy = 0, fix_long = 0;
  163. size_t c;
  164. if ((c = readlink(path, lpath, sizeof(lpath))) == -1) {
  165. perror(path);
  166. return;
  167. }
  168. lpath[c] = '\0'; /* readlink does not null terminate it */
  169. /* construct the absolute address of the link */
  170. abspath[0] = '\0';
  171. if (lpath[0] != '/') {
  172. strcat(abspath, path);
  173. c = strlen(abspath);
  174. if ((c > 0) && (abspath[c - 1] == '/')) {
  175. abspath[c - 1] = '\0'; /* cut trailing / */
  176. }
  177. if ((p = strrchr(abspath, '/')) != NULL) {
  178. *p = '\0'; /* cut last component */
  179. }
  180. strcat(abspath, "/");
  181. }
  182. strcat(abspath, lpath);
  183. (void) tidy_path(abspath);
  184. /* check for various things */
  185. if (stat(abspath, &stbuf) == -1) {
  186. printf("dangling: %s -> %s\n", path, lpath);
  187. if (delete) {
  188. if (unlink(path)) {
  189. perror(path);
  190. } else {
  191. printf("deleted: %s -> %s\n", path, lpath);
  192. }
  193. }
  194. return;
  195. }
  196. if (single_fs) {
  197. lstat(abspath, &lstbuf); /* if the above didn't fail, then this shouldn't */
  198. }
  199. if (single_fs && lstbuf.st_dev != my_dev) {
  200. msg = "other_fs:";
  201. } else if (lpath[0] == '/') {
  202. msg = "absolute:";
  203. fix_abs = 1;
  204. } else if (verbose) {
  205. msg = "relative:";
  206. } else {
  207. msg = NULL;
  208. }
  209. fix_messy = tidy_path(strcpy(new, lpath));
  210. if (shorten) {
  211. fix_long = shorten_path(new, path);
  212. }
  213. if (!fix_abs) {
  214. if (fix_messy) {
  215. msg = "messy: ";
  216. } else if (fix_long) {
  217. msg = "lengthy: ";
  218. }
  219. }
  220. if (msg != NULL) {
  221. printf("%s %s -> %s\n", msg, path, lpath);
  222. }
  223. if (!(fix_links || testing) || !(fix_messy || fix_abs || fix_long)) {
  224. return;
  225. }
  226. if (fix_abs) {
  227. /* convert an absolute link to relative: */
  228. /* point tail at first part of lpath that differs from path */
  229. /* point p at first part of path that differs from lpath */
  230. (void) tidy_path(lpath);
  231. tail = lp = lpath;
  232. p = path;
  233. while (*p && (*p == *lp)) {
  234. if (*lp++ == '/') {
  235. tail = lp;
  236. while (*++p == '/');
  237. }
  238. }
  239. /* now create new, with "../"s followed by tail */
  240. np = new;
  241. while (*p) {
  242. if (*p++ == '/') {
  243. *np++ = '.';
  244. *np++ = '.';
  245. *np++ = '/';
  246. while (*p == '/') {
  247. ++p;
  248. }
  249. }
  250. }
  251. strcpy(np, tail);
  252. (void) tidy_path(new);
  253. if (shorten) {
  254. (void) shorten_path(new, path);
  255. }
  256. }
  257. shorten_path(new, path);
  258. if (!testing) {
  259. if (unlink(path)) {
  260. perror(path);
  261. return;
  262. }
  263. if (symlink(new, path)) {
  264. perror(path);
  265. return;
  266. }
  267. }
  268. printf("changed: %s -> %s\n", path, new);
  269. }
  270. static void dirwalk(char *path, unsigned long pathlen, dev_t dev) {
  271. char *name;
  272. DIR *dfd;
  273. static struct stat st;
  274. static struct dirent *dp;
  275. if ((dfd = opendir(path)) == NULL) {
  276. perror(path);
  277. return;
  278. }
  279. name = path + pathlen;
  280. if (*(name - 1) != '/') {
  281. *name++ = '/';
  282. }
  283. while ((dp = readdir(dfd)) != NULL) {
  284. strcpy(name, dp->d_name);
  285. if (strcmp(name, ".") && strcmp(name, "..")) {
  286. if (lstat(path, &st) == -1) {
  287. perror(path);
  288. } else if (st.st_dev == dev) {
  289. if (S_ISLNK(st.st_mode)) {
  290. fix_symlink(path, dev);
  291. } else if (recurse && S_ISDIR(st.st_mode)) {
  292. dirwalk(path, strlen(path), dev);
  293. }
  294. }
  295. }
  296. }
  297. closedir(dfd);
  298. path[pathlen] = '\0';
  299. }
  300. static void usage_error(void) {
  301. fprintf(stderr, progver, progname);
  302. fprintf(stderr, "Usage:\t%s [-cdorstv] dirlist\n\n", progname);
  303. fprintf(stderr, "Flags:"
  304. "\t-c change absolute/messy links to relative\n"
  305. "\t-d delete dangling links\n"
  306. "\t-o warn about links across file systems\n"
  307. "\t-r recurse into subdirs\n"
  308. "\t-s shorten lengthy links (displayed in output only when -c not specified)\n"
  309. "\t-t show what would be done by -c\n"
  310. "\t-v verbose (show all symlinks)\n\n");
  311. exit(1);
  312. }
  313. int main(int argc, char **argv) {
  314. static char path[PATH_MAX + 2], cwd[PATH_MAX + 2];
  315. int dircount = 0;
  316. char c, *p;
  317. if ((progname = (char *) strrchr(*argv, '/')) == NULL) {
  318. progname = *argv;
  319. } else {
  320. progname++;
  321. }
  322. if (NULL == getcwd(cwd, PATH_MAX)) {
  323. fprintf(stderr, "getcwd() failed\n");
  324. exit(1);
  325. }
  326. if (!*cwd || cwd[strlen(cwd) - 1] != '/') {
  327. strcat(cwd, "/");
  328. }
  329. while (--argc) {
  330. p = *++argv;
  331. if (*p == '-') {
  332. if (*++p == '\0') {
  333. usage_error();
  334. }
  335. while ((c = *p++)) {
  336. if (c == 'c') { fix_links = 1;
  337. } else if (c == 'd') { delete = 1;
  338. } else if (c == 'o') { single_fs = 0;
  339. } else if (c == 'r') { recurse = 1;
  340. } else if (c == 's') { shorten = 1;
  341. } else if (c == 't') { testing = 1;
  342. } else if (c == 'v') { verbose = 1;
  343. } else {
  344. usage_error();
  345. }
  346. }
  347. } else {
  348. struct stat st;
  349. if (*p == '/') {
  350. *path = '\0';
  351. } else {
  352. strcpy(path, cwd);
  353. }
  354. tidy_path(strcat(path, p));
  355. if (lstat(path, &st) == -1) {
  356. perror(path);
  357. } else {
  358. dirwalk(path, strlen(path), st.st_dev);
  359. }
  360. ++dircount;
  361. }
  362. }
  363. if (dircount == 0) {
  364. usage_error();
  365. }
  366. exit(0);
  367. }