diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a443803 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/symlinks diff --git a/Makefile b/Makefile index 086d58b..3b88792 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,21 @@ # Makefile for symlinks -CC=gcc -OWNER=root -GROUP=root -MANDIR=/usr/man/man8/symlinks.8 -BINDIR=/usr/local/bin - +CC := gcc +CFLAGS += $(shell getconf LFS_CFLAGS 2>/dev/null) +OWNER = root +GROUP = root +MANDIR = /usr/man/man8/symlinks.8 +BINDIR = /usr/local/bin + +.PHONY: all all: symlinks symlinks: symlinks.c $(CC) -Wall -Wstrict-prototypes -O2 $(CFLAGS) -o symlinks symlinks.c - + install: all symlinks.8 $(INSTALL) -c -o $(OWNER) -g $(GROUP) -m 755 symlinks $(BINDIR) $(INSTALL) -c -o $(OWNER) -g $(GROUP) -m 644 symlinks.8 $(MANDIR) +.PHONY: clean clean: rm -f symlinks *.o core diff --git a/Readme.md b/Readme.md index 71ab71a..3225401 100644 --- a/Readme.md +++ b/Readme.md @@ -36,7 +36,7 @@ Usage ### Convert absolute symlink to relative: - $ symlinks -rc [path] + $ symlinks -rc [path] ### More options: @@ -47,21 +47,27 @@ Usage Changes ------- -* v1.4-1 -Added Mac OS X compatibility. +#### v1.4.2 +- Reformatted for readability roughly based on Google style guide. +- Fixed loss of precision due to implicit type conversion. +- Minor documentation updates. -* v1.4 -Incorporate patches from Fedora. +#### v1.4-1 +- Added Mac OS X compatibility. -* v1.3 -More messy-link fixes, new -o flag for other_fs. +#### v1.4 +- Incorporate patches from Fedora. -* v1.2 -Added -s flag to shorten links with redundant path elements. Also includes code to remove excess slashes from paths. +#### v1.3 +- More messy-link fixes, new `-o` flag for other_fs. + +#### v1.2 +- Added `-s` flag to shorten links with redundant path elements. +- Also includes code to remove excess slashes from paths. Credit ------ -Symlinks was created by Mark Lord. -Minor modifications made by Jonathon Brandt Buckley. +Symlinks was created by **Mark Lord** . +Maintained by **J. Brandt Buckley** . diff --git a/symlinks.8 b/symlinks.8 index 77fb71b..79883ae 100644 --- a/symlinks.8 +++ b/symlinks.8 @@ -1,4 +1,4 @@ -.TH SYMLINKS 8 "July 2012" "Version 1.4-1" +.TH SYMLINKS 8 "April 2014" "Version 1.4.2" .SH NAME symlinks \- symbolic link maintenance utility @@ -10,10 +10,8 @@ symlinks \- symbolic link maintenance utility dirlist .SH DESCRIPTION .BI symlinks -is a useful utility for maintainers of FTP sites, CDROMs, -and Linux software distributions. -It scans directories for symbolic links and lists them on stdout, -often revealing flaws in the filesystem tree. +scans directories for symbolic links and lists them on stdout, +often revealing broken links in the filesystem tree. .PP Each link is output with a classification of .B relative, @@ -117,7 +115,6 @@ does not recurse or change links across filesystems. .PP .SH AUTHOR .B symlinks -has been written by Mark Lord , the original developer and maintainer -of the IDE Performance Package for linux, the Linux IDE Driver subsystem, hdparm, and a current day libata hacker. +was created by Mark Lord . The current maintainer is J. Brandt Buckley . .SH SEE ALSO .BR symlink (2) diff --git a/symlinks.c b/symlinks.c index 1f47b1d..68d4cbb 100644 --- a/symlinks.c +++ b/symlinks.c @@ -1,12 +1,16 @@ #include + #ifndef _POSIX_SOURCE #define _POSIX_SOURCE #endif + #include #include + #if !defined(__APPLE__) #include #endif + #include #include #include @@ -25,340 +29,445 @@ #define PATH_MAX 1024 #endif -#define progver "%s: scan/change symbolic links - v1.4-1 - by Mark Lord\n\n" +#define progver "%s: scan/change symbolic links - v1.4.2\n\n" + static char *progname; -static int verbose = 0, fix_links = 0, recurse = 0, delete = 0, shorten = 0, - testing = 0, single_fs = 1; +static int verbose = 0, + fix_links = 0, + recurse = 0, + delete = 0, + shorten = 0, + testing = 0, + single_fs = 1; /* * tidypath removes excess slashes and "." references from a path string */ -static int substr (char *s, char *old, char *new) -{ - char *tmp = NULL; - int oldlen = strlen(old), newlen = 0; - - if (NULL == strstr(s, old)) - return 0; - - if (new) - newlen = strlen(new); - - if (newlen > oldlen) { - if ((tmp = malloc(strlen(s))) == NULL) { - fprintf(stderr, "no memory\n"); - exit (1); - } - } - - while (NULL != (s = strstr(s, old))) { - char *p, *old_s = s; - - if (new) { - if (newlen > oldlen) - old_s = strcpy(tmp, s); - p = new; - while (*p) - *s++ = *p++; - } - p = old_s + oldlen; - while ((*s++ = *p++)); - } - if (tmp) - free(tmp); - return 1; +static int substr(char *s, char *old, char *new) { + char *tmp = NULL; + unsigned long oldlen = strlen(old), newlen = 0; + + if (NULL == strstr(s, old)) { + return 0; + } + + if (new) { + newlen = strlen(new); + } + + if (newlen > oldlen) { + if ((tmp = malloc(strlen(s))) == NULL) { + fprintf(stderr, "no memory\n"); + exit(1); + } + } + + while (NULL != (s = strstr(s, old))) { + char *p, *old_s = s; + + if (new) { + if (newlen > oldlen) { + old_s = strcpy(tmp, s); + } + + p = new; + + while (*p) { + *s++ = *p++; + } + } + + p = old_s + oldlen; + + while ((*s++ = *p++)); + } + + if (tmp) { + free(tmp); + } + + return 1; } -static int tidy_path (char *path) -{ - int tidied = 0; - char *s, *p; - - s = path + strlen(path) - 1; - if (s[0] != '/') { /* tmp trailing slash simplifies things */ - s[1] = '/'; - s[2] = '\0'; - } - while (substr(path, "/./", "/")) - tidied = 1; - while (substr(path, "//", "/")) - tidied = 1; - - while ((p = strstr(path,"/../")) != NULL) { - s = p+3; - for (p--; p != path; p--) if (*p == '/') break; - if (*p != '/') - break; - while ((*p++ = *s++)); - tidied = 1; - } - if (*path == '\0') - strcpy(path,"/"); - p = path + strlen(path) - 1; - if (p != path && *p == '/') - *p-- = '\0'; /* remove tmp trailing slash */ - while (p != path && *p == '/') { /* remove any others */ - *p-- = '\0'; - tidied = 1; - } - while (!strncmp(path,"./",2)) { - for (p = path, s = path+2; (*p++ = *s++);); - tidied = 1; - } - return tidied; +static int tidy_path(char *path) { + int tidied = 0; + char *s, *p; + s = path + strlen(path) - 1; + + if (s[0] != '/') { /* tmp trailing slash simplifies things */ + s[1] = '/'; + s[2] = '\0'; + } + + while (substr(path, "/./", "/")) { + tidied = 1; + } + + while (substr(path, "//", "/")) { + tidied = 1; + } + + while ((p = strstr(path, "/../")) != NULL) { + s = p + 3; + + for (p--; p != path; p--) { + if (*p == '/') { break; } + } + + if (*p != '/') { break; } + + while ((*p++ = *s++)); + + tidied = 1; + } + + if (*path == '\0') { + strcpy(path, "/"); + } + + p = path + strlen(path) - 1; + + if (p != path && *p == '/') { + *p-- = '\0'; /* remove tmp trailing slash */ + } + + while (p != path && *p == '/') { /* remove any others */ + *p-- = '\0'; + tidied = 1; + } + + while (!strncmp(path, "./", 2)) { + for (p = path, s = path + 2; (*p++ = *s++);); + + tidied = 1; + } + + return tidied; } -static int shorten_path (char *path, char *abspath) -{ - static char dir[PATH_MAX]; - int shortened = 0; - char *p; - /* get rid of unnecessary "../dir" sequences */ - while (abspath && strlen(abspath) > 1 && (p = strstr(path,"../"))) { - /* find innermost occurance of "../dir", and save "dir" */ - int slashes = 2; - char *a, *s, *d = dir; - while ((s = strstr(p+3, "../"))) { - ++slashes; - p = s; - } - s = p+3; - *d++ = '/'; - while (*s && *s != '/') - *d++ = *s++; - *d++ = '/'; - *d = '\0'; - if (!strcmp(dir,"//")) - break; - /* note: p still points at ../dir */ - if (*s != '/' || !*++s) - break; - a = abspath + strlen(abspath) - 1; - while (slashes-- > 0) { - if (a <= abspath) - goto ughh; - while (*--a != '/') { - if (a <= abspath) - goto ughh; - } - } - if (strncmp(dir, a, strlen(dir))) - break; - while ((*p++ = *s++)); /* delete the ../dir */ - shortened = 1; - } +static int shorten_path(char *path, char *abspath) { + static char dir[PATH_MAX]; + int shortened = 0; + char *p; + + /* get rid of unnecessary "../dir" sequences */ + while (abspath && strlen(abspath) > 1 && (p = strstr(path, "../"))) { + /* find innermost occurance of "../dir", and save "dir" */ + int slashes = 2; + char *a, *s, *d = dir; + + while ((s = strstr(p + 3, "../"))) { + ++slashes; + p = s; + } + + s = p + 3; + *d++ = '/'; + + while (*s && *s != '/') { + *d++ = *s++; + } + + *d++ = '/'; + *d = '\0'; + + if (!strcmp(dir, "//")) { + break; + } + + /* note: p still points at ../dir */ + if (*s != '/' || !*++s) { + break; + } + + a = abspath + strlen(abspath) - 1; + + while (slashes-- > 0) { + if (a <= abspath) { + goto ughh; + } + + while (*--a != '/') { + if (a <= abspath) { + goto ughh; + } + } + } + + if (strncmp(dir, a, strlen(dir))) { + break; + } + + while ((*p++ = *s++)); /* delete the ../dir */ + + shortened = 1; + } + ughh: - return shortened; + return shortened; } -static void fix_symlink (char *path, dev_t my_dev) -{ - static char lpath[PATH_MAX], new[PATH_MAX], abspath[PATH_MAX]; - char *p, *np, *lp, *tail, *msg; - struct stat stbuf, lstbuf; - int c, fix_abs = 0, fix_messy = 0, fix_long = 0; - - if ((c = readlink(path, lpath, sizeof(lpath))) == -1) { - perror(path); - return; - } - lpath[c] = '\0'; /* readlink does not null terminate it */ - - /* construct the absolute address of the link */ - abspath[0] = '\0'; - if (lpath[0] != '/') { - strcat(abspath,path); - c = strlen(abspath); - if ((c > 0) && (abspath[c-1] == '/')) - abspath[c-1] = '\0'; /* cut trailing / */ - if ((p = strrchr(abspath,'/')) != NULL) - *p = '\0'; /* cut last component */ - strcat(abspath,"/"); - } - strcat(abspath,lpath); - (void) tidy_path(abspath); - - /* check for various things */ - if (stat(abspath, &stbuf) == -1) { - printf("dangling: %s -> %s\n", path, lpath); - if (delete) { - if (unlink (path)) { - perror(path); - } else - printf("deleted: %s -> %s\n", path, lpath); - } - return; - } - - if (single_fs) - lstat(abspath, &lstbuf); /* if the above didn't fail, then this shouldn't */ - - if (single_fs && lstbuf.st_dev != my_dev) { - msg = "other_fs:"; - } else if (lpath[0] == '/') { - msg = "absolute:"; - fix_abs = 1; - } else if (verbose) { - msg = "relative:"; - } else - msg = NULL; - fix_messy = tidy_path(strcpy(new,lpath)); - if (shorten) - fix_long = shorten_path(new, path); - if (!fix_abs) { - if (fix_messy) - msg = "messy: "; - else if (fix_long) - msg = "lengthy: "; - } - if (msg != NULL) - printf("%s %s -> %s\n", msg, path, lpath); - if (!(fix_links || testing) || !(fix_messy || fix_abs || fix_long)) - return; - - if (fix_abs) { - /* convert an absolute link to relative: */ - /* point tail at first part of lpath that differs from path */ - /* point p at first part of path that differs from lpath */ - (void) tidy_path(lpath); - tail = lp = lpath; - p = path; - while (*p && (*p == *lp)) { - if (*lp++ == '/') { - tail = lp; - while (*++p == '/'); - } - } - - /* now create new, with "../"s followed by tail */ - np = new; - while (*p) { - if (*p++ == '/') { - *np++ = '.'; - *np++ = '.'; - *np++ = '/'; - while (*p == '/') ++p; - } - } - strcpy (np, tail); - (void) tidy_path(new); - if (shorten) (void) shorten_path(new, path); - } - shorten_path(new,path); - if (!testing) { - if (unlink (path)) { - perror(path); - return; - } - if (symlink(new, path)) { - perror(path); - return; - } - } - printf("changed: %s -> %s\n", path, new); +static void fix_symlink(char *path, dev_t my_dev) { + static char lpath[PATH_MAX], new[PATH_MAX], abspath[PATH_MAX]; + char *p, *np, *lp, *tail, *msg; + struct stat stbuf, lstbuf; + int fix_abs = 0, fix_messy = 0, fix_long = 0; + size_t c; + + if ((c = readlink(path, lpath, sizeof(lpath))) == -1) { + perror(path); + return; + } + + lpath[c] = '\0'; /* readlink does not null terminate it */ + /* construct the absolute address of the link */ + abspath[0] = '\0'; + + if (lpath[0] != '/') { + strcat(abspath, path); + c = strlen(abspath); + + if ((c > 0) && (abspath[c - 1] == '/')) { + abspath[c - 1] = '\0'; /* cut trailing / */ + } + + if ((p = strrchr(abspath, '/')) != NULL) { + *p = '\0'; /* cut last component */ + } + + strcat(abspath, "/"); + } + + strcat(abspath, lpath); + (void) tidy_path(abspath); + + /* check for various things */ + if (stat(abspath, &stbuf) == -1) { + printf("dangling: %s -> %s\n", path, lpath); + + if (delete) { + if (unlink(path)) { + perror(path); + } else { + printf("deleted: %s -> %s\n", path, lpath); + } + } + + return; + } + + if (single_fs) { + lstat(abspath, &lstbuf); /* if the above didn't fail, then this shouldn't */ + } + + if (single_fs && lstbuf.st_dev != my_dev) { + msg = "other_fs:"; + } else if (lpath[0] == '/') { + msg = "absolute:"; + fix_abs = 1; + } else if (verbose) { + msg = "relative:"; + } else { + msg = NULL; + } + + fix_messy = tidy_path(strcpy(new, lpath)); + + if (shorten) { + fix_long = shorten_path(new, path); + } + + if (!fix_abs) { + if (fix_messy) { + msg = "messy: "; + } else if (fix_long) { + msg = "lengthy: "; + } + } + + if (msg != NULL) { + printf("%s %s -> %s\n", msg, path, lpath); + } + + if (!(fix_links || testing) || !(fix_messy || fix_abs || fix_long)) { + return; + } + + if (fix_abs) { + /* convert an absolute link to relative: */ + /* point tail at first part of lpath that differs from path */ + /* point p at first part of path that differs from lpath */ + (void) tidy_path(lpath); + tail = lp = lpath; + p = path; + + while (*p && (*p == *lp)) { + if (*lp++ == '/') { + tail = lp; + + while (*++p == '/'); + } + } + + /* now create new, with "../"s followed by tail */ + np = new; + + while (*p) { + if (*p++ == '/') { + *np++ = '.'; + *np++ = '.'; + *np++ = '/'; + + while (*p == '/') { + ++p; + } + } + } + + strcpy(np, tail); + (void) tidy_path(new); + + if (shorten) { + (void) shorten_path(new, path); + } + } + + shorten_path(new, path); + + if (!testing) { + if (unlink(path)) { + perror(path); + return; + } + + if (symlink(new, path)) { + perror(path); + return; + } + } + + printf("changed: %s -> %s\n", path, new); } -static void dirwalk (char *path, int pathlen, dev_t dev) -{ - char *name; - DIR *dfd; - static struct stat st; - static struct dirent *dp; - - if ((dfd = opendir(path)) == NULL) { - perror(path); - return; - } - - name = path + pathlen; - if (*(name-1) != '/') - *name++ = '/'; - - while ((dp = readdir(dfd)) != NULL ) { - strcpy(name, dp->d_name); - if (strcmp(name, ".") && strcmp(name,"..")) { - if (lstat(path, &st) == -1) { - perror(path); - } else if (st.st_dev == dev) { - if (S_ISLNK(st.st_mode)) { - fix_symlink (path, dev); - } else if (recurse && S_ISDIR(st.st_mode)) { - dirwalk(path, strlen(path), dev); - } - } - } - } - closedir(dfd); - path[pathlen] = '\0'; +static void dirwalk(char *path, unsigned long pathlen, dev_t dev) { + char *name; + DIR *dfd; + static struct stat st; + static struct dirent *dp; + + if ((dfd = opendir(path)) == NULL) { + perror(path); + return; + } + + name = path + pathlen; + + if (*(name - 1) != '/') { + *name++ = '/'; + } + + while ((dp = readdir(dfd)) != NULL) { + strcpy(name, dp->d_name); + + if (strcmp(name, ".") && strcmp(name, "..")) { + if (lstat(path, &st) == -1) { + perror(path); + } else if (st.st_dev == dev) { + if (S_ISLNK(st.st_mode)) { + fix_symlink(path, dev); + } else if (recurse && S_ISDIR(st.st_mode)) { + dirwalk(path, strlen(path), dev); + } + } + } + } + + closedir(dfd); + path[pathlen] = '\0'; } -static void usage_error (void) -{ - fprintf(stderr, progver, progname); - fprintf(stderr, "Usage:\t%s [-cdorstv] dirlist\n\n", progname); - fprintf(stderr, "Flags:" - "\t-c == change absolute/messy links to relative\n" - "\t-d == delete dangling links\n" - "\t-o == warn about links across file systems\n" - "\t-r == recurse into subdirs\n" - "\t-s == shorten lengthy links (displayed in output only when -c not specified)\n" - "\t-t == show what would be done by -c\n" - "\t-v == verbose (show all symlinks)\n\n"); - exit(1); +static void usage_error(void) { + fprintf(stderr, progver, progname); + fprintf(stderr, "Usage:\t%s [-cdorstv] dirlist\n\n", progname); + fprintf(stderr, "Flags:" + "\t-c change absolute/messy links to relative\n" + "\t-d delete dangling links\n" + "\t-o warn about links across file systems\n" + "\t-r recurse into subdirs\n" + "\t-s shorten lengthy links (displayed in output only when -c not specified)\n" + "\t-t show what would be done by -c\n" + "\t-v verbose (show all symlinks)\n\n"); + exit(1); } -int main(int argc, char **argv) -{ - static char path[PATH_MAX+2], cwd[PATH_MAX+2]; - int dircount = 0; - char c, *p; - - if ((progname = (char *) strrchr(*argv, '/')) == NULL) - progname = *argv; - else - progname++; - - if (NULL == getcwd(cwd,PATH_MAX)) { - fprintf(stderr,"getcwd() failed\n"); - exit (1); - } - if (!*cwd || cwd[strlen(cwd)-1] != '/') - strcat(cwd,"/"); - - while (--argc) { - p = *++argv; - if (*p == '-') { - if (*++p == '\0') - usage_error(); - while ((c = *p++)) { - if (c == 'c') fix_links = 1; - else if (c == 'd') delete = 1; - else if (c == 'o') single_fs = 0; - else if (c == 'r') recurse = 1; - else if (c == 's') shorten = 1; - else if (c == 't') testing = 1; - else if (c == 'v') verbose = 1; - else usage_error(); - } - } else { - struct stat st; - if (*p == '/') - *path = '\0'; - else - strcpy(path,cwd); - tidy_path(strcat(path, p)); - if (lstat(path, &st) == -1) - perror(path); - else - dirwalk(path, strlen(path), st.st_dev); - ++dircount; - } - } - if (dircount == 0) - usage_error(); - exit (0); +int main(int argc, char **argv) { + static char path[PATH_MAX + 2], cwd[PATH_MAX + 2]; + int dircount = 0; + char c, *p; + + if ((progname = (char *) strrchr(*argv, '/')) == NULL) { + progname = *argv; + } else { + progname++; + } + + if (NULL == getcwd(cwd, PATH_MAX)) { + fprintf(stderr, "getcwd() failed\n"); + exit(1); + } + + if (!*cwd || cwd[strlen(cwd) - 1] != '/') { + strcat(cwd, "/"); + } + + while (--argc) { + p = *++argv; + + if (*p == '-') { + if (*++p == '\0') { + usage_error(); + } + + while ((c = *p++)) { + if (c == 'c') { fix_links = 1; + } else if (c == 'd') { delete = 1; + } else if (c == 'o') { single_fs = 0; + } else if (c == 'r') { recurse = 1; + } else if (c == 's') { shorten = 1; + } else if (c == 't') { testing = 1; + } else if (c == 'v') { verbose = 1; + } else { + usage_error(); + } + } + } else { + struct stat st; + + if (*p == '/') { + *path = '\0'; + } else { + strcpy(path, cwd); + } + + tidy_path(strcat(path, p)); + + if (lstat(path, &st) == -1) { + perror(path); + } else { + dirwalk(path, strlen(path), st.st_dev); + } + + ++dircount; + } + } + + if (dircount == 0) { + usage_error(); + } + + exit(0); }