diff options
author | Daniel Friesel <derf@derf.homelinux.org> | 2010-03-27 18:44:59 +0100 |
---|---|---|
committer | Daniel Friesel <derf@derf.homelinux.org> | 2010-03-27 18:44:59 +0100 |
commit | 5eab75f69c3a816489e0ee3bbd60e0b53d833bb3 (patch) | |
tree | 7117bcdc9c9f8841535f63da278ca422874c30ff | |
parent | 7e0d3757436d37c238d535df12933d3875d1d331 (diff) |
checklinks: Add "auto" link typo for automatic symlinking
-rwxr-xr-x | bin/checklinks | 206 |
1 files changed, 153 insertions, 53 deletions
diff --git a/bin/checklinks b/bin/checklinks index 68700d9..358b3e1 100755 --- a/bin/checklinks +++ b/bin/checklinks @@ -3,6 +3,8 @@ ## License: WTFPL <http://sam.zoy.org/wtfpl> use strict; use warnings; +use 5.010; +use Cwd; use Getopt::Long; use Term::ANSIColor; @@ -13,52 +15,76 @@ my %substitute; my $linkfile; my $exit = 0; -if (-f '.links') { - $linkfile = '.links'; -} elsif (-f 'links') { - $linkfile = 'links'; -} else { - exit(0); + +sub mkdirs { + my $source = shift; + my $path = $base; + my @dirs = split(/\//, $source); + + # the last element is the file + pop(@dirs); + + foreach(@dirs) { + unless(-d "$path/$_") { + mkdir("$path/$_") or die("Can't create $path/$_: $!"); + } + $path .= $_; + } + return; } -GetOptions( - 'm|msglevel=i' => \$msglevel, - 'p|parameter=s' => \%substitute, - 'q|quiet' => sub {$msglevel = 1}, - 'r|remove' => \$remove, -); +sub dir_content { + my ($dir) = @_; + my @return; + my ($normal_file, $dot_file); -open(my $links, '<', $linkfile) or die("Can't open $linkfile: $!"); -while (my $line = <$links>) { - chomp($line); + opendir(my $dh, $dir) or die("Cannot opendir $dir: $!"); + while (my $entry = readdir($dh)) { + next if ($entry ~~ ['.', '..', '.git', '.hg', '.links', 'links']); - foreach my $key (keys(%substitute)) { - $line =~ s/\$$key/$substitute{$key}/g; + push(@return, $entry); + + if (!$dot_file and $entry =~ /^\./) { + $dot_file = 1; + } + elsif (!$normal_file) { + $normal_file = 1 + } } - my ($type, $src, $dst) = split(/\s+/, $line); + if ($normal_file and $dot_file) { + @return = grep { /^\./ } @return; + } - next unless ($type eq 'soft' or $type eq 'hard'); + return @return; +} - if ($remove) { - remove_link($type, $src, $dst); +sub print_format { + my ($message, $src, $dst, $color, $level) = @_; + + if ($level > 1) { + $exit++; } - elsif ($type eq 'soft') { - check_symlink($src, $dst); + + return if ($level < $msglevel); + + if (defined($color)) { + printf(colored('%-9s', $color), $message); } - elsif ($type eq 'hard') { - check_hardlink($src, $dst); + else { + printf('%-9s', $message); } + + printf(" %-15s -> %-15s\n", $src, $dst); + return; } -close($links); sub remove_link { my ($type, $src, $dst) = @_; - if ( - ($type eq 'soft' and -l "$base/$src") - or ($type eq 'hard' and -e "$base/$src" and -e "$base/$dst") - ) { + if (($type eq 'soft' and -l "$base/$src") + or ($type eq 'hard' and -e "$base/$src" and -e "$base/$dst")) + { unlink("$base/$src") or warn("cannot unlink $base/$src: $!"); print_format('removed', $src, '', 'red', 1); } @@ -116,42 +142,79 @@ sub check_hardlink { return; } -sub mkdirs { - my $source = shift; - my $path = $base; - my @dirs = split(/\//, $source); +sub loop_links { + my $dir; + my $cwd = cwd(); - # the last element is the file - pop(@dirs); + if (-e 'etc') { + $dir = 'etc'; + } + else { + $dir = '.'; + } - foreach(@dirs) { - unless(-d "$path/$_") { - mkdir("$path/$_") or die("Can't create $path/$_: $!"); + foreach my $entry (dir_content($dir)) { + my $source = $entry; + if ($source !~ /^\./) { + $source = ".$source"; + } + + if ($remove) { + remove_link('soft', $source); + } + else { + check_symlink($source, "$cwd/$dir/$entry"); } - $path .= $_; } - return; } -sub print_format { - my ($message, $src, $dst, $color, $level) = @_; - if ($level > 1) { - $exit++; +if (-f '.links') { + $linkfile = '.links'; +} +elsif (-f 'links') { + $linkfile = 'links'; +} +else { + exit(0); +} + +GetOptions( + 'm|msglevel=i' => \$msglevel, + 'p|parameter=s' => \%substitute, + 'q|quiet' => sub {$msglevel = 1}, + 'r|remove' => \$remove, +); + +open(my $links, '<', $linkfile) or die("Can't open $linkfile: $!"); +while (my $line = <$links>) { + chomp($line); + + foreach my $key (keys(%substitute)) { + $line =~ s/\$$key/$substitute{$key}/g; } - return if ($level < $msglevel); + my ($type, $src, $dst) = split(/\s+/, $line); - if (defined($color)) { - printf(colored('%-9s', $color), $message); + next unless ($type ~~ ['soft', 'hard', 'auto']); + + if ($type eq 'auto') { + loop_links($src, $dst); } else { - printf('%-9s', $message); + if ($remove) { + remove_link($type, $src, $dst); + } + elsif ($type eq 'soft') { + check_symlink($src, $dst); + } + elsif ($type eq 'hard') { + check_hardlink($src, $dst); + } } - - printf(" %-15s -> %-15s\n", $src, $dst); - return; } +close($links); + exit($exit); @@ -257,7 +320,12 @@ current working directory. Each line contains, separated by spaces: =item the symlink type -This may be one of 'soft' or 'hard', indicating either a symlink or a hardlink. +This may either be 'soft' or 'hard' (symlink / hardlink) or 'auto'. + +If the type is B<auto>, the following fields may be omitted. Instead, the +notes in L</"AUTO SYMLINKS"> apply. + + =item the source @@ -275,7 +343,10 @@ This is relative to the source. See L<path_resolution>(7) =back + Lines beginning with an invalid symlink type will be ignored. +I recommend using a # to introduce comments, though, and not simply write +something into the file just because that is (technically) also ok ;) Example: @@ -286,6 +357,35 @@ Example: soft .zlogout $etc/logout soft .zshenv $etc/env +=head2 AUTO SYMLINKS + +If the link type is 'auto', B<checklinks> will attempt to guess which files are +meant to be symlinked where. However, be aware that this feature is highly +experimental and may be changed or removed in the future. So far it will +only create absolute links. Also, do not mix 'auto' with other link types. + +Automatic symlinking works this way: +B<checklinks> takes all files in either F<etc/>, or (if that doesn't exist) +the current directory. All files in there will be symlinked as a dotfile from +your home. If the directory only contains normal files, their corresponding +symlink will have a . as prefix; if dotfiles are present, only these will be +symlinked to (without adding another . as prefix, of course). +This way, it is possibly to have various files in a directory, but only +symlink those which are dotfiles. + +Example: + + remnant ~/p/zsh > ls -A etc + completions functions hosts .zlogout .zshenv + completions.zwc functions.zwc startx .zprofile .zshrc + remnant ~/p/zsh > cat links + auto + remnant ~/p/zsh > checklinks + ok .zshenv -> /home/derf/packages/zsh/etc/.zshenv + ok .zlogout -> /home/derf/packages/zsh/etc/.zlogout + ok .zshrc -> /home/derf/packages/zsh/etc/.zshrc + ok .zprofile -> /home/derf/packages/zsh/etc/.zprofile + =head1 DIAGNOSTICS The exit value is the number of files with grave errors |