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.

364 lines
7.7 KiB

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