1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
#!/usr/bin/env perl
use strict;
use warnings;
use Term::ANSIColor;
my $base = $ENV{HOME};
my ($type, $src, $dst);
my $quiet = 0;
my $remove = 0;
my $linkfile;
if (-f '.links') {
$linkfile = '.links';
} elsif (-f 'links') {
$linkfile = 'links';
} else {
exit(0);
}
if (defined($ARGV[0])) {
if ($ARGV[0] eq '-q') {
$quiet = 1;
}
if ($ARGV[0] eq '-r') {
$remove = 1;
}
}
open(LINKS, '<', $linkfile) or die($!);
while(<LINKS>) {
chomp;
($type, $src, $dst) = split;
next unless ($type eq 'soft' or $type eq 'hard');
if ($remove) {
remove_link($src);
} elsif ($type eq 'soft') {
check_symlink($src, $dst);
} elsif ($type eq 'hard') {
check_hardlink($src, $dst);
}
}
close(LINKS);
sub remove_link {
my $link = shift;
if (-l "$base/$link") {
unlink("$base/$link") or warn("cannot unlink $base/$link: $!");
print_format('removed', $link, '', 'red');
}
}
sub check_symlink {
my $src = shift;
my $dst = shift;
#if (not -e "$base/$dst") {
# print_format('no dst!!', $src, $dst, 'red bold');
#}
if (not -l "$base/$src" and not -e "$base/$src") {
symlink($dst, "$base/$src");
print_format('created', $src, $dst, 'cyan');
}
elsif (readlink("$base/$src") eq $dst) {
print_format('ok', $src, $dst, 'green') unless $quiet;
}
elsif (readlink("$base/$src") eq "$base/$dst") {
print_format('absolute', $src, $dst, 'yellow') unless $quiet;
}
elsif (not -l "$base/$src" and -e "$base/$src") {
print colored ("$base/$src: File exists but is not a symlink. Not updating.\n", 'bold red');
}
elsif (-l "$base/$src") {
unlink("$base/$src");
symlink($dst, "$base/$src");
print_format('fixed', $src, $dst, 'cyan');
}
}
sub check_hardlink {
my $src = shift;
my $dst = shift;
if (not -e "$base/$dst") {
print_format('no dst!!', $src, $dst, 'red bold');
}
elsif (not -f "$base/$src") {
link("$base/$dst", "$base/$src") or warn($!);
print_format('created', $src, $dst, 'cyan');
}
elsif ((stat("$base/$src"))[1] != (stat("$base/$dst"))[1]) {
unlink("$base/$src");
link("$base/$dst", "$base/$src") or warn($!);
print_format('updated', $src, $dst, 'cyan');
}
elsif ((stat("$base/$src"))[1] == (stat("$base/$dst"))[1]) {
print_format('ok', $src, $dst, 'green') unless $quiet;
}
}
sub print_format {
my ($message, $src, $dst, $color) = @_;
$message .= ' 'x(9-length($message));
$src .= ' 'x(15-length($src));
$dst .= ' 'x(15-length($dst));
if (defined($color)) {
print colored ($message, $color);
} else {
print $message;
}
print "$src -> $dst\n";
}
__END__
=head1 NAME
checklinks - create/update symlinks
=head1 SYNOPSIS
B<checklinks> [ B<-q> ]
=head1 DESCRIPTION
Create or update symlinks based on a file
=head1 OPTIONS
=over
=item B<-q>
quiet. Hide unchanged symlinks
=back
=head1 FILES
The symlink definitions are read from the file F<.links> or F<links> in the
current working directory. Each line contains, separated by spaces:
=over
=item the symlink type
This may be one of 'soft' or 'hard', indicating either a symlink or a hardlink.
Note: The use of hardlinks is discouraged and not documented. The following
definitions only apply to symlinks. For hardlinks, may the source be with you.
=item the source
path of the source, i.e. the symlink. Relative to $HOME
=item the target
path of the target, i.e. the symlink's destination.
This is relativ to the source. See L<path_resolution>(7)
=back
Lines beginning with an invalid symlink type will be ignored.
=head1 BUGS
There are no checks whether the symlink target actually exists
|