This repository has been archived by the owner on Feb 14, 2020. It is now read-only.
/
effuse
executable file
·156 lines (128 loc) · 3.98 KB
/
effuse
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
#!/usr/bin/env ruby
require 'optparse'
require 'ostruct'
require 'fileutils'
options = OpenStruct.new(:verbose => false,
:clean => false,
:noconfirm => nil,
:backup => true,
:prefix => '',
:exclude => %w[.effuseignore .git .gitignore .gitmodules *~ .*~ .*.swp])
if File.exist?('.effuseignore')
File.open('.effuseignore').lines.each do |line| # Ruby 1.8 (.each)
options.exclude << line.chomp
end
end
OptionParser.new do |o|
o.banner = 'Usage: effuse [OPTION...] [DEST]'
o.on('-c', '--clean', 'Remove symlinks') do
options.clean = true
end
o.separator ''
o.on('-e', '--exclude GLOB', 'Exclude GLOB from symlinking') do |glob|
options.exclude << glob
end
o.on('-i', '--include GLOB', 'Include GLOB in symlinking') do |glob|
options.exclude.delete(glob)
end
o.separator ''
o.on('-y', '--noconfirm CHOICE', 'Assume CHOICE on file conflicts') do |choice|
options.noconfirm = choice.downcase[0].chr # Shakes fist at Ruby 1.8 (.chr)
end
o.on('-n', '--no-backup', 'Do not create backup files') do
options.backup = false
end
o.separator ''
o.on('-p', '--prefix PREFIX', 'Prefix relative paths with PREFIX') do |prefix|
options.prefix = prefix
end
o.separator ''
o.on('-v', '--verbose', 'Show verbose output') do
options.verbose = true
end
o.on_tail("-h", "--help", "Show this message") do
puts o
exit
end
end.parse!
dest_dir = ARGV[0] || ENV['HOME'] # Shakes fist at Ruby 1.8 (Dir.home)
if File.identical?(dest_dir, '.')
puts 'error: destination directory is current directory'
exit 1
end
files = {}
dirs = ['.']
dirs.each do |dir|
puts "scanning #{dir}" if options.verbose
Dir.foreach(dir) do |file|
next if ['.', '..'].include?(file)
if options.exclude.any? {|e| File.fnmatch(e, file)}
puts "excluding #{File.join(dir, file)}" if options.verbose
next
end
file = File.join(dir, file)
if File.directory?(file)
dirs << file
puts "adding #{file}" if options.verbose
next
end
puts "found #{file}" if options.verbose
relpath = file.split('/')[1..-1]
dest = File.join([dest_dir, options.prefix + relpath[0]] + relpath[1..-1])
files[File.expand_path(file)] = dest # Shakes fist at Ruby 1.8 (File.absolute_path)
end
end
if options.clean
files.each_pair do |src, dest|
if File.exist?(dest) && File.identical?(src, dest)
puts dest
File.delete(dest)
else
puts "not symlinked #{dest}" if options.verbose
end
end
else
files.each_pair do |src, dest|
if File.exist?(dest)
if File.identical?(src, dest)
puts "already symlinked #{dest}" if options.verbose
next
else
if options.noconfirm
input = options.noconfirm
puts "assuming '#{input}' for #{dest}" if options.verbose
else
puts "File already exists: #{dest}"
puts " [R] Backup and replace"
puts " [i] Import original file and symlink"
puts " [k] Keep original file"
loop do
input = gets.chomp.downcase[0].chr || 'r' # Shakes fist at Ruby 1.8 (.chr)
break if 'rik'.include?(input)
puts "Error: '#{input}' is not an option"
end
end
case input
when 'r'
if options.backup
puts "renaming #{dest} -> #{dest + '~'}" if options.verbose
File.rename(dest, dest + '~')
end
when 'i'
if options.backup
puts "renaming #{src} -> #{src + '~'}" if options.verbose
File.rename(src, src + '~')
end
puts "renaming #{dest} -> #{src}" if options.verbose
File.rename(dest, src)
when 'k'
puts "skipping #{dest}" if options.verbose
next
end
end
end
puts "#{dest} -> #{src}"
FileUtils.mkdir_p(File.dirname(dest))
File.symlink(src, dest)
end
end