Browse Source

Merge branch 'refs/heads/fix-32bit-lfs' into devel

* refs/heads/fix-32bit-lfs:
  Fix large file support. fixes #1
  Update Readme
  Increment version, update docs
  Ignore compiled symlinks binary
  Fix implicit conversion precision loss
  Reformat code style
devel
J. Brandt Buckley 11 years ago
parent
commit
f814d01036
  1. 1
      .gitignore
  2. 17
      Makefile
  3. 28
      Readme.md
  4. 11
      symlinks.8
  5. 737
      symlinks.c

1
.gitignore

@ -0,0 +1 @@
/symlinks

17
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

28
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** <mlord@pobox.com>.
Maintained by **J. Brandt Buckley** <brandt@runlevel1.com>.

11
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 <mlord@pobox.com>, 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 <mlord@pobox.com>. The current maintainer is J. Brandt Buckley <brandt@runlevel1.com>.
.SH SEE ALSO
.BR symlink (2)

737
symlinks.c

@ -1,12 +1,16 @@
#include <unistd.h>
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#if !defined(__APPLE__)
#include <malloc.h>
#endif
#include <string.h>
#include <fcntl.h>
#include <sys/param.h>
@ -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);
}
Loading…
Cancel
Save