2 """Parse output of FTP LIST command.
3 Pure python implementation.
5 See http://cr.yp.to/ftpparse.html, http://effbot.org/downloads#ftpparse,
6 http://c0re.23.nu/c0de/ftpparsemodule/ and http://www.ocgy.ubc.ca/~tang/treeftp
8 Currently covered formats:
9 UNIX ls, with or without gid;
17 Definitely not covered:
18 EPLF (show me an FTP server first...);
19 Long VMS filenames, with information split across two lines;
20 NCSA Telnet FTP server. Has LIST = NLST (and bad NLST for directories).
24 from __future__ import print_function
26 from mx import DateTime
28 _parse_datetime = False
30 _parse_datetime = True
33 class parse_error(Exception): pass
37 def __init__(self, name=None, perm=None, nlink=None, user=None, group=None, \
38 size=None, mtime=None, links_to=None, file_type=None):
41 mtime = ' '.join(mtime)
44 mtime = DateTime.DateTimeFrom(mtime)
45 except DateTime.Error:
55 self.links_to = links_to
56 self.file_type = file_type # f - regular file, d - directory, l - symlink
60 return """<%s: name=%s, perm=%s, nlink=%s, user=%s, group=%s, size=%s, mtime=%s, links-to=%s, type=%s at 0x%x>""" % (
61 self.__class__.__name__, self.name, self.perm, self.nlink,
62 self.user, self.group, self.size, self.mtime,
63 self.links_to, self.file_type, id(self))
66 def _parse_unix(line, parts):
79 if parts[1][0] == '[': # NetWare
86 perm = perm + ' ' + parts[1]
91 parts = line.split(None, 7) # resplit the original line...
92 name = parts[7] # ...in case the filename contains whitespaces
94 elif parts[1] == "folder": # NetPresenz for the Mac
99 parts = line.split(None, 6)
102 elif parts[0][0] == 'l': # symlink
104 nlink = int(parts[1])
110 parts = line.split(None, 8)
112 parts = link.split(" -> ")
123 nlink = int(parts[1])
129 parts = line.split(None, 7)
131 parts = name.split(' ')[1:]
132 name = ' '.join(parts)
135 if parts[2].isdigit(): # NetPresenz
146 nlink = int(parts[1])
151 parts = line.split(None, 7)
154 return entry(name, perm, nlink, user, group, size, mtime, links_to, file_type)
157 def _parse_vms(parts):
161 user = parts[4][1:-1]
169 parts = user.split(',')
173 return entry(name, perm, nlink, user, group, size, mtime, links_to, file_type)
176 def _parse_dos(parts):
186 # Change %m-%d-%y format to %y-%m-%d
188 date_parts = date.split('-')
189 date = "%s-%s-%s" % (date_parts[2], date_parts[0], date_parts[1])
195 if parts[2] == "<DIR>":
201 return entry(name, perm, nlink, user, group, size, mtime, links_to, file_type)
208 if c == '+': # EPLF format is not supported
211 if c in ('b', 'c', 'd', 'l', 'p', 's', '-'): # UNIX-like
212 return _parse_unix(line, parts)
214 if ';' in parts[0]: # VMS
215 return _parse_vms(parts)
217 if '0' <= c <= '9': # DOS
218 return _parse_dos(parts)
221 #Some useless lines, safely ignored:
222 #"Total of 11 Files, 10966 Blocks." (VMS)
223 #"total 14786" (UNIX)
224 #"DISK$ANONFTP:[ANONYMOUS]" (VMS)
225 #"Directory DISK$PCSA:[ANONYM]" (VMS)
231 #UNIX-style listing, without inum and without blocks
232 print(ftpparse("-rw-r--r-- 1 root other 531 Jan 29 03:26 README"))
233 print(ftpparse("dr-xr-xr-x 2 root other 512 Apr 8 1994 etc"))
234 print(ftpparse("dr-xr-xr-x 2 root 512 Apr 8 1994 etc"))
235 print(ftpparse("lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin"))
236 #FTP servers for Windoze:
237 print(ftpparse("---------- 1 owner group 1803128 Jul 10 10:18 ls-lR.Z"))
238 print(ftpparse("d--------- 1 owner group 0 May 9 19:45 Softlib"))
240 print(ftpparse("-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 message.ftp"))
242 print(ftpparse("d [R----F--] supervisor 512 Jan 16 18:53 login"))
243 print(ftpparse("- [R----F--] rhesus 214059 Oct 20 15:27 cx.exe"))
244 #NetPresenz for the Mac:
245 print(ftpparse("-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit"))
246 print(ftpparse("drwxrwxr-x folder 2 May 10 1996 network"))
248 #MultiNet (some spaces removed from examples)
249 print(ftpparse("00README.TXT;1 2 30-DEC-1996 17:44 [SYSTEM] (RWED,RWED,RE,RE)"))
250 print(ftpparse("CORE.DIR;1 1 8-SEP-1996 16:09 [SYSTEM] (RWE,RWE,RE,RE)"))
252 print(ftpparse("CII-MANUAL.TEX;1 213/216 29-JAN-1996 03:33:12 [ANONYMOU,ANONYMOUS] (RWED,RWED,,)"))
255 print(ftpparse("04-27-00 09:09PM <DIR> licensed"))
256 print(ftpparse("07-18-00 10:16AM <DIR> pub"))
257 print(ftpparse("04-14-00 03:47PM 589 readme.htm"))
259 print(ftpparse("-rw-r--r-- 1 root other 531 Jan 29 03:26 READ ME"))
260 print(ftpparse("-rw-r--r-- 1 root other 531 Jan 29 03:26 DO NOT READ ME "))
262 if __name__ == "__main__":