2 """Parse output of FTP LIST command.
3 Pure python implementation.
4 Author: Oleg Broytman <phd@phd.pp.ru>.
5 Copyright (C) 2003-2005 PhiloSoft Design.
7 See http://cr.yp.to/ftpparse.html, http://effbot.org/downloads#ftpparse,
8 http://c0re.23.nu/c0de/ftpparsemodule/ and http://www.ocgy.ubc.ca/~tang/treeftp
10 Currently covered formats:
11 UNIX ls, with or without gid;
19 Definitely not covered:
20 EPLF (show me an FTP server first...);
21 Long VMS filenames, with information split across two lines;
22 NCSA Telnet FTP server. Has LIST = NLST (and bad NLST for directories).
27 __author__ = "Oleg Broytman <phd@phd.pp.ru>"
28 __copyright__ = "Copyright (C) 2003-2005 PhiloSoft Design"
32 # 2005-04-26 version 1.1.2 [phd] Changed some comments and URLs.
33 # 2003-07-23 version 1.1.1 [phd] Upgrade to Python 2.2: 0/1 => False/True.
34 # 2003-07-17 version 1.1.0 [phd] Great renaming.
35 # 2003-07-17 version 1.0.1 [phd] Preserve leading spaces in UNIX format.
36 # 2003-02-17 version 1.0.0 [phd] First public version.
37 # 2003-02-07 version 0.0.1 [phd] started the project.
41 from mx import DateTime
43 _parse_datetime = False
45 _parse_datetime = True
48 class parse_error(Exception): pass
52 def __init__(self, name=None, perm=None, nlink=None, user=None, group=None, \
53 size=None, mtime=None, links_to=None, file_type=None):
56 mtime = ' '.join(mtime)
59 mtime = DateTime.DateTimeFrom(mtime)
60 except DateTime.Error:
70 self.links_to = links_to
71 self.file_type = file_type # f - regular file, d - directory, l - symlink
75 return """<%s: name=%s, perm=%s, nlink=%s, user=%s, group=%s, size=%s, mtime=%s, links-to=%s, type=%s at 0x%x>""" % (
76 self.__class__.__name__, self.name, self.perm, self.nlink,
77 self.user, self.group, self.size, self.mtime,
78 self.links_to, self.file_type, id(self))
81 def _parse_unix(line, parts):
94 if parts[1][0] == '[': # NetWare
101 perm = perm + ' ' + parts[1]
106 parts = line.split(None, 7) # resplit the original line...
107 name = parts[7] # ...in case the filename contains whitespaces
109 elif parts[1] == "folder": # NetPresenz for the Mac
114 parts = line.split(None, 6)
117 elif parts[0][0] == 'l': # symlink
119 nlink = int(parts[1])
125 parts = line.split(None, 8)
127 parts = link.split(" -> ")
138 nlink = int(parts[1])
144 parts = line.split(None, 7)
146 parts = name.split(' ')[1:]
147 name = ' '.join(parts)
150 if parts[2].isdigit(): # NetPresenz
161 nlink = int(parts[1])
166 parts = line.split(None, 7)
169 return entry(name, perm, nlink, user, group, size, mtime, links_to, file_type)
172 def _parse_vms(parts):
176 user = parts[4][1:-1]
184 parts = user.split(',')
188 return entry(name, perm, nlink, user, group, size, mtime, links_to, file_type)
191 def _parse_dos(parts):
201 # Change %m-%d-%y format to %y-%m-%d
203 date_parts = date.split('-')
204 date = "%s-%s-%s" % (date_parts[2], date_parts[0], date_parts[1])
210 if parts[2] == "<DIR>":
216 return entry(name, perm, nlink, user, group, size, mtime, links_to, file_type)
223 if c == '+': # EPLF format is not supported
226 if c in ('b', 'c', 'd', 'l', 'p', 's', '-'): # UNIX-like
227 return _parse_unix(line, parts)
229 if ';' in parts[0]: # VMS
230 return _parse_vms(parts)
232 if '0' <= c <= '9': # DOS
233 return _parse_dos(parts)
236 #Some useless lines, safely ignored:
237 #"Total of 11 Files, 10966 Blocks." (VMS)
238 #"total 14786" (UNIX)
239 #"DISK$ANONFTP:[ANONYMOUS]" (VMS)
240 #"Directory DISK$PCSA:[ANONYM]" (VMS)
246 #UNIX-style listing, without inum and without blocks
247 print ftpparse("-rw-r--r-- 1 root other 531 Jan 29 03:26 README")
248 print ftpparse("dr-xr-xr-x 2 root other 512 Apr 8 1994 etc")
249 print ftpparse("dr-xr-xr-x 2 root 512 Apr 8 1994 etc")
250 print ftpparse("lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin")
251 #FTP servers for Windoze:
252 print ftpparse("---------- 1 owner group 1803128 Jul 10 10:18 ls-lR.Z")
253 print ftpparse("d--------- 1 owner group 0 May 9 19:45 Softlib")
255 print ftpparse("-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 message.ftp")
257 print ftpparse("d [R----F--] supervisor 512 Jan 16 18:53 login")
258 print ftpparse("- [R----F--] rhesus 214059 Oct 20 15:27 cx.exe")
259 #NetPresenz for the Mac:
260 print ftpparse("-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit")
261 print ftpparse("drwxrwxr-x folder 2 May 10 1996 network")
263 #MultiNet (some spaces removed from examples)
264 print ftpparse("00README.TXT;1 2 30-DEC-1996 17:44 [SYSTEM] (RWED,RWED,RE,RE)")
265 print ftpparse("CORE.DIR;1 1 8-SEP-1996 16:09 [SYSTEM] (RWE,RWE,RE,RE)")
267 print ftpparse("CII-MANUAL.TEX;1 213/216 29-JAN-1996 03:33:12 [ANONYMOU,ANONYMOUS] (RWED,RWED,,)")
270 print ftpparse("04-27-00 09:09PM <DIR> licensed")
271 print ftpparse("07-18-00 10:16AM <DIR> pub")
272 print ftpparse("04-14-00 03:47PM 589 readme.htm")
274 print ftpparse("-rw-r--r-- 1 root other 531 Jan 29 03:26 READ ME")
275 print ftpparse("-rw-r--r-- 1 root other 531 Jan 29 03:26 DO NOT READ ME ")
277 if __name__ == "__main__":