Autojump2

Autojump2 Git Source Tree

Root/autojump2

1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# Copyright Grégory Soutadé 2012
4
5# This file is part of autojump2
6
7# autojump2 is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# autojump2 is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with autojump2. If not, see <http://www.gnu.org/licenses/>.
19
20# Original code has been written by Joel Schaerer (https://github.com/joelthelion/autojump)
21
22from sys import argv, exit, stderr, stdout
23import getopt
24import os
25from optparse import OptionParser
26import shutil
27import re
28
29COMPLETION_SEPARATOR = '__'
30CONFIG_DIR = os.environ.get("AUTOJUMP2_DATA_DIR", os.path.expanduser("~"))
31DATABASE_NAME = '.autojump2.dict'
32
33def _walk_path(path):
34 res = []
35 try:
36 for p in os.listdir(path):
37 p = path + p
38 if not os.path.islink(p) and not os.path.isdir(p):
39 continue
40 res.append(p)
41 except OSError:
42 print >> stderr, "Error listing %s" % path
43
44 return res
45
46def walk_path(path):
47 res = []
48 try:
49 pos = path.index('*')
50 sub_path = path[:pos]
51 for p in _walk_path(sub_path):
52 if '*' in path[pos+1:]:
53 for p2 in walk_path(p + path[pos+1:]):
54 res.append(p2)
55 else:
56 res.append(p + path[pos+1:])
57 except ValueError:
58 pass
59
60 return res
61
62def add_path(l, h, path):
63 path = path.replace('\\*', '*')
64
65 if path in l:
66 return False
67
68 tmp = []
69
70 l.append(path)
71 l.sort()
72
73 # Generic paths must be after normal path
74 for p in l:
75 if not '*' in p:
76 tmp.append(p)
77
78 for p in l:
79 if '*' in p:
80 tmp.append(p)
81
82 del l[:]
83
84 for p in tmp:
85 l.append(p)
86
87 if '*' in path:
88 paths = walk_path(p)
89 if len(paths) > 0:
90 h[path] = paths
91
92 return True
93
94def remove_path(l, h, path):
95 path = path.replace('\\*', '*')
96
97 if not path in l:
98 return False
99 else:
100 l.remove(path)
101 if path in h:
102 del h[path]
103
104 return True
105
106def modify_path(l, h, src, dest):
107 src = src.replace('\\*', '*')
108 dest = dest.replace('\\*', '*')
109
110 if not src in l:
111 return False
112
113 if not remove_path(l, h, src) or not add_path(l, h, dest):
114 return False
115
116 return True
117
118def save_database(l, h):
119 path = CONFIG_DIR + os.sep + DATABASE_NAME
120 bak = path + '.bak'
121
122 if os.path.exists(bak):
123 try:
124 os.remove(bak)
125 except OSError as e:
126 pass
127
128 try:
129 if os.path.exists(path):
130 shutil.copy(path, bak)
131 except OSError as e:
132 print >> stderr, "Error while creating backup of autojump2 dic @ \'%s\'. (%s)" % (path.bak, e)
133 raise
134
135 try:
136 if os.path.exists(path):
137 os.remove(path)
138 except OSError as e:
139 print >> stderr, "Error can't remove autojump2 database @ \'%s\'. (%s)" % (path, e)
140 raise
141
142 f = open(path, 'w')
143 try:
144 for p in l:
145 f.write(p + '\n')
146 if p in h:
147 for p2 in h[p]:
148 f.write('>>> ' + p2 + '\n')
149 f.flush()
150 f.close()
151 except OSError as e:
152 print >> stderr, "Error write autojump2 database @ \'%s\'. (%s)" % (path, e)
153 shutil.copy(bak, path)
154
155 return True
156
157def open_database():
158 l = []
159 h = {}
160 path = CONFIG_DIR + os.sep + DATABASE_NAME
161 prev_line = ''
162
163 if not os.path.exists(path): return l, h
164
165 f = open(path)
166 for line in f:
167 if line.startswith('>>> ') and prev_line != '':
168 if not prev_line in h: h[prev_line] = []
169 h[prev_line].append(line[4:][:-1]) # Remove '>>> ' and '\n'
170 else:
171 l.append(line[:-1]) # Remove last '\n'
172 prev_line = line[:-1]
173 f.close()
174
175 return l, h
176
177def list_database(l, h):
178 if not len(l):
179 print >> stderr, 'Any path saved'
180 return
181
182 print >> stderr, 'Saved paths :'
183 for p in l:
184 print >> stderr, '\t' + p
185 if p in h:
186 sublist = h[p]
187 sublist.sort()
188 for p2 in sublist:
189 print >> stderr, '\t>>> ' + p2
190
191 return True
192
193def path_matching(l, h, args, pos = -1):
194 res = []
195 exprs = []
196
197 if len(args) == 0: return res
198
199 # If args are : 'proj' and 'v2'
200 # exp1 is /a/b/c/projXXXv2
201 # exp2 is /a/b/proj/v2
202 exp1 = '^.*' + os.sep
203 for a in args:
204 exp1 += '[^' + os.sep + ']*' + a + '[^' + os.sep + ']*'
205 # exp1 += '$'
206 e1 = re.compile(exp1)
207 exprs.append(e1)
208
209 exp2 = ''
210 if len(args) > 1:
211 exp2 = '^.*' + os.sep
212 for a in args[:-1]:
213 exp2 += '[^' + os.sep + ']*' + a + '.*' + os.sep
214 exp2 += '[^' + os.sep + ']*' + args[-1] + '[^' + os.sep + ']*'
215 # exp2 += '[^' + os.sep + ']*' + args[-1] + '[^' + os.sep + ']*$'
216 e2 = re.compile(exp2)
217 exprs.append(e2)
218
219 for path in l:
220 if '*' in path:
221 try:
222 sublist = h[path]
223 sublist.sort()
224 for p in sublist:
225 for e in exprs:
226 if re.match(e, p):
227 if pos != -1:
228 pos -= 1
229 if pos == 0 and not p in res:
230 res.append(p)
231 elif not p in res:
232 res.append(p)
233 break
234 except KeyError as e:
235 print >> stderr, "Error database may be corrupted, invalid key (%s)" % e
236 else:
237 for e in exprs:
238 if re.match(e, path):
239 if pos != -1:
240 pos -= 1
241 if pos == 0 and not path in res:
242 res.append(path)
243 elif not path in res:
244 res.append(path)
245 break
246
247
248 # print >> stderr, 'Exprs : \n%s\n%s' % (exp1, exp2)
249 # print >> stderr, 'Matching paths :'
250 # for path in res:
251 # print >> stderr, path
252
253 return res
254
255#################################### Main code ####################################
256
257if __name__ == '__main__':
258 usage = '%prog [options]\n'\
259 'Navigate throw your filesystems with recorded links'
260
261 optparser = OptionParser(usage=usage)
262
263 optparser.add_option('-a', '--add', dest='add',
264 help='Add a new path to the database',
265 metavar="path")
266 optparser.add_option('-r', '--remove', dest='remove',
267 help='Remove a path from the database',
268 metavar="path")
269 optparser.add_option('-m', '--modify', dest='modify', nargs=2,
270 help='Modify key weight',
271 metavar="path_src path_dest")
272 optparser.add_option('-u', '--update', dest='update',
273 help='Update path with *',
274 metavar="path")
275 optparser.add_option('-l', '--list', dest='list',
276 action="store_true",
277 help='List database')
278 optparser.add_option('-c', '--completion', dest='completion',
279 action="store_true",
280 help='Use autojump\'s completion')
281 optparser.add_option('-b', '--bash', dest='bash',
282 action="store_true",
283 help='Current shell is bash')
284
285 (optlist, args) = optparser.parse_args(argv[1:])
286
287 l, h = open_database()
288
289 if optlist.add:
290 if optlist.completion: exit(1)
291 path = os.path.abspath(optlist.add)
292 if os.path.isfile(path):
293 print >> stderr, "Error, cannot add a file (%s) in database, directory needed" % path
294 elif add_path(l, h, path):
295 save_database(l, h)
296 print >> stderr, '>>> \'%s\' correctly added to database' % (path)
297 else:
298 print >> stderr, 'Error \'%s\' already exists in database' % (path)
299
300 elif optlist.remove:
301 if optlist.completion: exit(1)
302 if remove_path(l, h, os.path.abspath(optlist.remove)):
303 save_database(l, h)
304 print >> stderr, '>>> \'%s\' correctly removed from database' % (optlist.remove)
305 else:
306 print >> stderr, 'Error \'%s\' doesn\'t exists in database' % (optlist.remove)
307
308 elif optlist.modify:
309 if optlist.completion: exit(1)
310 if os.path.isfile(os.path.abspath(optlist.modify[1])):
311 print >> stderr, "Error, cannot add a file in database, directory needed"
312 elif modify_path(l, h, optlist.modify[0], os.path.abspath(optlist.modify[1])):
313 save_database(l, h)
314 print >> stderr, '>>> \'%s\' is now \'%s\'' % (optlist.modify[0], os.path.abspath(optlist.modify[1]))
315 else:
316 print >> stderr, 'Error \'%s\' doesn\'t exists in database' % (optlist.modify[0])
317
318 elif optlist.update:
319 if optlist.completion: exit(1)
320 if not modify_path(l, h, optlist.update, optlist.update):
321 print >> stderr, 'Error updating ' + optlist.update
322 else:
323 save_database(l, h)
324 print >> stderr, '>>> Database updated'
325
326 elif optlist.list:
327 if optlist.completion: exit(1)
328 list_database(l, h)
329
330 else:
331# Do the hard work
332 if optlist.bash: quotes = '"'
333 else: quotes = ""
334
335 if optlist.completion:
336 m = re.search('^.*(' + COMPLETION_SEPARATOR + '.*)$', args[-1])
337 if m: # Remove '__'
338 args[-1] = args[-1][:-len(m.group(1))]
339 matches = path_matching(l, h, args)
340 if len(matches) > 1:
341 print("\n" . join(("%s%s%d%s%s" % (args[-1], COMPLETION_SEPARATOR, n+1, COMPLETION_SEPARATOR, r)\
342 for n,r in enumerate(matches))))
343 elif len(matches) == 1:
344 print quotes + matches[0] + quotes
345 else:
346 m = re.search('^.*' + COMPLETION_SEPARATOR + '([0-9]+)$', args[-1])
347 if m:
348 args[-1] = args[-1][:-len(COMPLETION_SEPARATOR)-len(m.group(1))]
349 matches = path_matching(l, h, args, int(m.group(1)))
350 else:
351 matches = path_matching(l, h, args)
352 if len(matches) > 0:
353 print quotes + matches[0] + quotes
354 exit(0)
355 exit(1)

Archive Download this file

Branches