exit from pyview by ESC
[lfm.git] / lfm / files.py
blob376e6302af748f17cbf1e73c7c47f6621df71c78
1 # -*- coding: utf-8 -*-
3 """files.py
5 This module defines files utilities for lfm.
6 """
9 import sys
10 import os
11 import os.path
12 import stat
13 import time
14 import pwd
15 import grp
16 import tempfile
18 from utils import get_shell_output2
21 ########################################################################
22 ##### constants
23 # File Type: dir, link to directory, link, nlink, char dev,
24 # block dev, fifo, socket, executable, file
25 (FTYPE_DIR, FTYPE_LNK2DIR, FTYPE_LNK, FTYPE_NLNK, FTYPE_CDEV, FTYPE_BDEV,
26 FTYPE_FIFO, FTYPE_SOCKET, FTYPE_EXE, FTYPE_REG) = xrange(10)
28 FILETYPES = { FTYPE_DIR: (os.sep, 'Directory'),
29 FTYPE_LNK2DIR: ('~', 'Link to Directory'),
30 FTYPE_LNK: ('@', 'Link'), FTYPE_NLNK: ('!', 'No Link'),
31 FTYPE_CDEV: ('-', 'Char Device'), FTYPE_BDEV: ('+', 'Block Device'),
32 FTYPE_FIFO: ('|', 'Fifo'), FTYPE_SOCKET: ('#', 'Socket'),
33 FTYPE_EXE: ('*', 'Executable'), FTYPE_REG: (' ', 'File') }
35 (FT_TYPE, FT_PERMS, FT_OWNER, FT_GROUP, FT_SIZE, FT_MTIME) = xrange(6)
37 # Sort Type: None, byName, bySize, byDate, byType
38 (SORTTYPE_None, SORTTYPE_byName, SORTTYPE_byName_rev, SORTTYPE_bySize,
39 SORTTYPE_bySize_rev, SORTTYPE_byDate, SORTTYPE_byDate_rev) = xrange(7)
42 ########################################################################
43 ##### general functions
44 # HACK: checks for st_rdev in stat_result, falling back to
45 # "ls -la %s" hack if Python before 2.2 or without st_rdev
46 try:
47 os.stat_result.st_rdev
48 except AttributeError:
49 def get_rdev(f):
50 """'ls -la' to get mayor and minor number of devices"""
51 try:
52 buf = os.popen('ls -la %s' % f).read().split()
53 except:
54 return 0
55 else:
56 try:
57 return int(buf[4][:-1]), int(buf[5])
58 except:
59 # HACK: found 0xff.. encoded device numbers, ignore them...
60 return 0, 0
61 else:
62 def get_rdev(f):
63 """mayor and minor number of devices"""
64 r = os.stat(f).st_rdev
65 return r >> 8, r & 255
68 def __get_size(f):
69 """return the size of the directory or file via 'du -sk' command"""
71 buf = get_shell_output2('du -sk \"%s\"' % f)
72 if buf:
73 return int(buf.split()[0]) * 1024
74 else:
75 return 0
78 def get_realpath(path, filename, filetype):
79 """return absolute path or, if path is a link, pointed file"""
81 if filetype in (FTYPE_LNK2DIR, FTYPE_LNK, FTYPE_NLNK):
82 try:
83 return '-> ' + os.readlink(os.path.join(path, filename))
84 except os.error:
85 return os.path.join(path, filename)
86 else:
87 return os.path.join(path, filename)
90 def get_linkpath(path, filename):
91 """return absolute path to the destination of a link"""
93 link_dest = os.readlink(os.path.join(path, filename))
94 return os.path.normpath(os.path.join(path, link_dest))
97 def join(directory, f):
98 if not os.path.isdir(directory):
99 directory = os.path.dirname(directory)
100 return os.path.join(directory, f)
103 def __get_filetype(f):
104 """get the type of the file. See listed types above"""
106 f = os.path.abspath(f)
107 lmode = os.lstat(f)[stat.ST_MODE]
108 if stat.S_ISDIR(lmode):
109 return FTYPE_DIR
110 if stat.S_ISLNK(lmode):
111 try:
112 mode = os.stat(f)[stat.ST_MODE]
113 except OSError:
114 return FTYPE_NLNK
115 else:
116 if stat.S_ISDIR(mode):
117 return FTYPE_LNK2DIR
118 else:
119 return FTYPE_LNK
120 if stat.S_ISCHR(lmode):
121 return FTYPE_CDEV
122 if stat.S_ISBLK(lmode):
123 return FTYPE_BDEV
124 if stat.S_ISFIFO(lmode):
125 return FTYPE_FIFO
126 if stat.S_ISSOCK(lmode):
127 return FTYPE_SOCKET
128 if stat.S_ISREG(lmode) and (lmode & 0111):
129 return FTYPE_EXE
130 else:
131 return FTYPE_REG # if no other type, regular file
134 def get_fileinfo(f, pardir_flag = False, show_dirs_size = False):
135 """return information about a file in next format:
136 (filetype, perms, owner, group, size, mtime)"""
138 st = os.lstat(f)
139 typ = __get_filetype(f)
140 if typ in (FTYPE_DIR, FTYPE_LNK2DIR) and not pardir_flag and show_dirs_size:
141 size = __get_size(f)
142 elif typ in (FTYPE_CDEV, FTYPE_BDEV):
143 # HACK: it's too time consuming to calculate all files' rdevs
144 # in a directory, so we just calculate what we need
145 # at show time
146 # maj_red, min_rdev = get_rdev(file)
147 size = 0
148 else:
149 size = st[stat.ST_SIZE]
150 try:
151 owner = pwd.getpwuid(st[stat.ST_UID])[0]
152 except:
153 owner = str(st[stat.ST_UID])
154 try:
155 group = grp.getgrgid(st[stat.ST_GID])[0]
156 except:
157 group = str(st[stat.ST_GID])
158 return (typ, stat.S_IMODE(st[stat.ST_MODE]), owner, group,
159 size, st[stat.ST_MTIME])
162 def perms2str(p):
163 permis = ['x', 'w', 'r']
164 perms = ['-'] * 9
165 for i in xrange(9):
166 if p & (0400 >> i):
167 perms[i] = permis[(8-i) % 3]
168 if p & 04000:
169 perms[2] = 's'
170 if p & 02000:
171 perms[5] = 's'
172 if p & 01000:
173 perms[8] = 't'
174 return ''.join(perms)
177 def get_fileinfo_dict(path, filename, filevalues):
178 """return a dict with file information"""
180 res = {}
181 res['filename'] = filename
182 typ = filevalues[FT_TYPE]
183 res['type_chr'] = FILETYPES[typ][0]
184 if typ == (FTYPE_CDEV, FTYPE_BDEV):
185 # HACK: it's too time consuming to calculate all files' rdevs
186 # in a directory, so we just calculate needed ones here
187 # at show time
188 maj_rdev, min_rdev = get_rdev(os.path.join(path, filename))
189 res['size'] = 0
190 res['maj_rdev'] = maj_rdev
191 res['min_rdev'] = min_rdev
192 res['dev'] = 1
193 else:
194 size = filevalues[FT_SIZE]
195 if size >= 1000000000L:
196 size = str(size/(1024*1024)) + 'M'
197 elif size >= 10000000L:
198 size = str(size/1024) + 'K'
199 else:
200 size = str(size)
201 res['size'] = size
202 res['maj_rdev'] = 0
203 res['min_rdev'] = 0
204 res['dev'] = 0
205 res['perms'] = perms2str(filevalues[1])
206 res['owner'] = filevalues[FT_OWNER]
207 res['group'] = filevalues[FT_GROUP]
208 if -15552000 < (time.time() - filevalues[FT_MTIME]) < 15552000:
209 # filedate < 6 months from now, past or future
210 mtime = time.strftime('%a %b %d %H:%M', time.localtime(filevalues[FT_MTIME]))
211 mtime2 = time.strftime('%d %b %H:%M', time.localtime(filevalues[FT_MTIME]))
212 else:
213 mtime = time.strftime('%a %d %b %Y', time.localtime(filevalues[FT_MTIME]))
214 mtime2 = time.strftime('%d %b %Y', time.localtime(filevalues[FT_MTIME]))
215 res['mtime'] = mtime
216 res['mtime2'] = mtime2
217 return res
220 def get_dir(path, show_dotfiles = 1):
221 """return a dict whose elements are formed by file name as key
222 and a (filetype, perms, owner, group, size, mtime) tuple as value"""
224 path = os.path.normpath(path)
225 files_dict = {}
226 if path != os.sep:
227 files_dict[os.pardir] = get_fileinfo(os.path.dirname(path), 1)
228 files_list = os.listdir(path)
229 if not show_dotfiles:
230 files_list = [f for f in files_list if f[0] != '.']
231 for f in files_list:
232 files_dict[f] = get_fileinfo(os.path.join(path, f))
233 return len(files_dict), files_dict
236 def get_owners():
237 """get a list with the users defined in the system"""
238 return [e[0] for e in pwd.getpwall()]
241 def get_user_fullname(user):
242 """return the fullname of an user"""
243 try:
244 return pwd.getpwnam(user)[4]
245 except KeyError:
246 return '<unknown user name>'
249 def get_groups():
250 """get a list with the groups defined in the system"""
251 return [e[0] for e in grp.getgrall()]
254 def set_perms(f, perms):
255 """set permissions to a file"""
256 ps, i = 0, 8
257 for p in perms:
258 if p == 'x':
259 ps += 1 * 8 ** int(i / 3)
260 elif p == 'w':
261 ps += 2 * 8 ** int(i / 3)
262 elif p == 'r':
263 ps += 4 * 8 ** int(i / 3)
264 elif p == 't' and i == 0:
265 ps += 1 * 8 ** 3
266 elif p == 's' and (i == 6 or i == 3):
267 if i == 6:
268 ps += 4 * 8 ** 3
269 else:
270 ps += 2 * 8 ** 3
271 i -= 1
272 try:
273 os.chmod(f, ps)
274 except (IOError, os.error), (errno, strerror):
275 return (strerror, errno)
278 def set_owner_group(f, owner, group):
279 """set owner and group to a file"""
280 try:
281 owner_n = pwd.getpwnam(owner)[2]
282 except:
283 owner_n = int(owner)
284 try:
285 group_n = grp.getgrnam(group)[2]
286 except:
287 group_n = int(group)
288 try:
289 os.chown(f, owner_n, group_n)
290 except (IOError, os.error), (errno, strerror):
291 return (strerror, errno)
293 def get_fs_info():
294 """return a list containing the info returned by 'df -k', i.e,
295 file systems size and occupation, in Mb. And the filesystem type:
296 [dev, size, used, available, use%, mount point, fs type]"""
298 try:
299 buf = os.popen('df -k').readlines()
300 except (IOError, os.error), (errno, strerror):
301 return (strerror, errno)
302 else:
303 fs = []
304 for l in buf:
305 if l[0] == os.sep:
306 e = l.split()
307 if len(e) > 1:
308 e[1] = str(int(e[1]) / 1024)
309 e[2] = str(int(e[2]) / 1024)
310 e[3] = str(int(e[3]) / 1024)
311 e[4] = e[4]
312 e[5] = e[5]
313 else:
314 continue
315 elif l[0] == ' ':
316 t = l.split()
317 e.append(str(int(t[0]) / 1024))
318 e.append(str(int(t[1]) / 1024))
319 e.append(str(int(t[2]) / 1024))
320 e.append(t[3])
321 e.append(t[4])
322 else:
323 continue
324 fs.append(e)
326 # get filesystems type
327 if sys.platform[:5] == 'linux':
328 es = open('/etc/fstab').readlines()
329 fstype_pos = 2
330 elif sys.platform[:5] == 'sunos':
331 es = open('/etc/vfstab').readlines()
332 fstype_pos = 3
333 else:
334 es = []
335 for f in fs:
336 for e in es:
337 if e.find(f[5]) != -1:
338 f.append(e.split()[fstype_pos])
339 break
340 else:
341 f.append('unknown')
342 return fs
345 ########################################################################
346 ##### temporary file
347 def mktemp():
348 return tempfile.mkstemp()[1]
350 def mkdtemp():
351 return tempfile.mkdtemp()
354 ########################################################################
355 ##### sort
356 def __do_sort(f_dict, sortmode, sort_mix_cases):
357 def __move_pardir_to_top(names):
358 if names.count(os.pardir) != 0:
359 names.remove(os.pardir)
360 names.insert(0, os.pardir)
361 return names
363 if sortmode == SORTTYPE_None:
364 names = f_dict.keys()
365 return __move_pardir_to_top(f_dict)
367 if sortmode in (SORTTYPE_byName, SORTTYPE_byName_rev):
368 if sort_mix_cases:
369 mycmp = lambda a, b: cmp(a.lower(), b.lower())
370 else:
371 mycmp = None
372 names = f_dict.keys()
373 names.sort(cmp=mycmp)
374 if sortmode == SORTTYPE_byName_rev:
375 names.reverse()
376 return __move_pardir_to_top(names)
378 mydict = {}
379 for k in f_dict.keys():
380 if sortmode in (SORTTYPE_bySize, SORTTYPE_bySize_rev):
381 size = f_dict[k][FT_SIZE]
382 while mydict.has_key(size): # can't be 2 entries with same key
383 size += 0.1
384 mydict[size] = k
385 elif sortmode in (SORTTYPE_byDate, SORTTYPE_byDate_rev):
386 tim = f_dict[k][FT_MTIME]
387 while mydict.has_key(tim): # can't be 2 entries with same key
388 tim += 0.1
389 mydict[tim] = k
390 else:
391 raise ValueError
392 values = mydict.keys()
393 values.sort()
394 names = [mydict[v] for v in values]
395 if sortmode in (SORTTYPE_bySize_rev, SORTTYPE_byDate_rev):
396 names.reverse()
397 return __move_pardir_to_top(names)
400 def sort_dir(files_dict, sortmode, sort_mix_dirs, sort_mix_cases):
401 """return an array of files which are sorted by mode"""
403 # separate directories and files
404 d, f = {}, {}
405 if sort_mix_dirs:
406 f = files_dict
407 else:
408 for k, v in files_dict.items():
409 if v[FT_TYPE] in (FTYPE_DIR, FTYPE_LNK2DIR):
410 d[k] = v
411 else:
412 f[k] = v
413 # sort
414 if d:
415 d1 = __do_sort(d, sortmode, sort_mix_cases)
416 else:
417 d1 = []
418 d2 = __do_sort(f, sortmode, sort_mix_cases)
419 d1.extend(d2)
420 return d1
423 ########################################################################
424 ##### complete
425 def complete(entrypath, panelpath):
426 if not entrypath:
427 path = panelpath
428 elif entrypath[0] == os.sep:
429 path = entrypath
430 else:
431 path = os.path.join(panelpath, entrypath)
432 # get elements
433 if os.path.isdir(path):
434 basedir = path
435 fs = os.listdir(path)
436 else:
437 basedir = os.path.dirname(path)
438 start = os.path.basename(path)
439 try:
440 entries = os.listdir(basedir)
441 except OSError:
442 entries = []
443 fs = [f for f in entries if f.find(start, 0) == 0]
444 # sort files with dirs first
445 d1, d2 = [], []
446 for f in fs:
447 if os.path.isdir(os.path.join(basedir, f)):
448 d1.append(f + os.sep)
449 else:
450 d2.append(f)
451 d1.sort()
452 d2.sort()
453 d1.extend(d2)
454 return d1
457 ########################################################################
458 ##### actions
459 # link
460 def do_create_link(pointto, link):
461 os.symlink(pointto, link)
464 def modify_link(pointto, linkname):
465 try:
466 os.unlink(linkname)
467 do_create_link(pointto, linkname)
468 except (IOError, os.error), (errno, strerror):
469 return (strerror, errno)
472 def create_link(pointto, linkname):
473 try:
474 do_create_link(pointto, linkname)
475 except (IOError, os.error), (errno, strerror):
476 return (strerror, errno)
479 # copy
480 def do_copy(source, dest):
481 import shutil
483 if os.path.islink(source):
484 dest = os.path.join(os.path.dirname(dest), os.path.basename(source))
485 try:
486 do_create_link(os.readlink(source), dest)
487 except (IOError, os.error), (errno, strerror):
488 return (strerror, errno)
489 elif os.path.isdir(source):
490 try:
491 os.mkdir(dest)
492 except (IOError, os.error), (errno, strerror):
493 pass # don't return if directory exists
494 else:
495 # # copy mode, times, owner and group
496 # st = os.lstat(source)
497 # os.chown(dest, st[stat.ST_UID], st[stat.ST_GID])
498 # shutil.copymode(source, dest)
499 # shutil.copystat(source, dest)
500 pass
501 for f in os.listdir(source):
502 do_copy(os.path.join(source, f), os.path.join(dest, f))
503 elif source == dest:
504 raise IOError, (0, "Source and destination are the same file")
505 else:
506 shutil.copy2(source, dest)
509 def copy(f, path, destdir, check_fileexists = 1):
510 """ copy file / dir to destdir"""
512 fullpath = os.path.join(path, f)
513 if destdir[0] != os.sep:
514 destdir = os.path.join(path, destdir)
515 if os.path.isdir(destdir):
516 destdir = os.path.join(destdir, f)
517 if os.path.exists(destdir) and check_fileexists:
518 return os.path.basename(destdir)
519 try:
520 do_copy(fullpath, destdir)
521 except (IOError, os.error), (errno, strerror):
522 return (strerror, errno)
525 # move
526 def move(f, path, destdir, check_fileexists = 1):
527 """delete file / dir"""
529 fullpath = os.path.join(path, f)
530 if destdir[0] != os.sep:
531 destdir = os.path.join(path, destdir)
532 if os.path.isdir(destdir):
533 destdir = os.path.join(destdir, f)
534 if os.path.exists(destdir) and check_fileexists:
535 return os.path.basename(destdir)
536 if os.path.dirname(fullpath) == os.path.dirname(destdir):
537 try:
538 os.rename(fullpath, destdir)
539 except (IOError, os.error), (errno, strerror):
540 return (strerror, errno)
541 return
542 try:
543 do_copy(fullpath, destdir)
544 except (IOError, os.error), (errno, strerror):
545 try:
546 do_delete(destdir)
547 except (IOError, os.error), (errno, strerror):
548 return (strerror, errno)
549 else:
550 return (strerror, errno)
551 else:
552 try:
553 do_delete(fullpath)
554 except (IOError, os.error), (errno, strerror):
555 return (strerror, errno)
558 # delete
559 def do_delete(f):
560 if os.path.islink(f):
561 os.unlink(f)
562 elif os.path.isdir(f):
563 for f2 in os.listdir(f):
564 do_delete(os.path.join(f, f2))
565 os.rmdir(f)
566 else:
567 os.unlink(f)
570 def delete(f, path):
571 """delete file / dir"""
573 fullpath = os.path.join(path, f)
574 try:
575 do_delete(fullpath)
576 except (IOError, os.error), (errno, strerror):
577 return (strerror, errno)
580 # mkdir
581 def mkdir(path, newdir):
582 """create directory"""
584 fullpath = os.path.join(path, newdir)
585 try:
586 os.makedirs(fullpath)
587 except (IOError, os.error), (errno, strerror):
588 return (strerror, errno)
591 ########################################################################