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.

362 lines
7.7 KiB

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