1 # Written by Arno Bakker, ABC authors
2 # see LICENSE.txt for license information
4 OS-independent utility functions
6 get_home_dir() : Returns CSIDL_APPDATA i.e. App data directory on win32
12 # Multiple methods for getting free diskspace
19 if sys.platform == "win32":
21 from win32com.shell import shell
23 # http://www.mvps.org/access/api/api0054.htm
24 # CSIDL_PROFILE = &H28
25 # C:\Documents and Settings\username
26 return shell.SHGetSpecialFolderPath(0, 0x28)
28 def get_appstate_dir():
29 # http://www.mvps.org/access/api/api0054.htm
30 # CSIDL_APPDATA = &H1A
31 # C:\Documents and Settings\username\Application Data
32 return shell.SHGetSpecialFolderPath(0, 0x1a)
34 def get_picture_dir():
35 # http://www.mvps.org/access/api/api0054.htm
36 # CSIDL_MYPICTURES = &H27
37 # C:\Documents and Settings\username\My Documents\My Pictures
38 return shell.SHGetSpecialFolderPath(0, 0x27)
40 def get_desktop_dir():
41 # http://www.mvps.org/access/api/api0054.htm
42 # CSIDL_DESKTOPDIRECTORY = &H10
43 # C:\Documents and Settings\username\Desktop
44 return shell.SHGetSpecialFolderPath(0, 0x10)
49 # when there are special unicode characters in the username,
50 # the following will fail on python 2.4, 2.5, 2.x this will
51 # always succeed on python 3.x
52 return os.path.expanduser(u"~")
53 except Exception, unicode_error:
57 home = os.path.expanduser("~")
58 head, tail = os.path.split(home)
60 dirs = os.listdir(head)
61 udirs = os.listdir(unicode(head))
63 # the character set may be different, but the string length is
65 islen = lambda dir: len(dir) == len(tail)
66 dirs = filter(islen, dirs)
67 udirs = filter(islen, udirs)
68 if len(dirs) == 1 and len(udirs) == 1:
69 return os.path.join(head, udirs[0])
71 # remove all dirs that are equal in unicode and non-unicode. we
72 # know that we don't need these dirs because the initial
73 # expandusers would not have failed on them
78 if len(dirs) == 1 and len(udirs) == 1:
79 return os.path.join(head, udirs[0])
81 # assume that the user has write access in her own
82 # directory. therefore we can filter out any non-writable
84 writable_udir = [udir for udir in udirs if os.access(udir, os.W_OK)]
85 if len(writable_udir) == 1:
86 return os.path.join(head, writable_udir[0])
88 # fallback: assume that the order of entries in dirs is the same
90 for dir, udir in zip(dirs, udirs):
92 return os.path.join(head, udir)
97 def get_appstate_dir():
98 homedir = get_home_dir()
100 # [E1101] Module 'sys' has no 'getwindowsversion' member
101 # pylint: disable-msg=E1101
102 winversion = sys.getwindowsversion()
103 # pylint: enable-msg=E1101
104 if winversion[0] == 6:
105 appdir = os.path.join(homedir,u"AppData",u"Roaming")
107 appdir = os.path.join(homedir,u"Application Data")
110 def get_picture_dir():
111 return get_home_dir()
113 def get_desktop_dir():
114 home = get_home_dir()
115 return os.path.join(home,u"Desktop")
118 # linux or darwin (mac)
120 return os.path.expanduser(u"~")
122 def get_appstate_dir():
123 return get_home_dir()
125 def get_picture_dir():
126 return get_desktop_dir()
128 def get_desktop_dir():
129 home = get_home_dir()
130 desktop = os.path.join(home, "Desktop")
131 if os.path.exists(desktop):
136 if sys.version.startswith("2.4"):
143 from os import statvfs
145 def getfreespace(path):
146 s = os.statvfs(path.encode("utf-8"))
147 size = s[statvfs.F_BAVAIL] * long(s[statvfs.F_BSIZE])
150 if (sys.platform == 'win32'):
152 # Windows if win32all extensions are installed
156 # Arno: this code was totally broken as the method returns
157 # a list of values indicating 1. free space for the user,
158 # 2. total space for the user and 3. total free space, so
159 # not a single value.
160 win32file.GetDiskFreeSpaceEx(".")
161 def getfreespace(path):
162 # Boudewijn: the win32file module is NOT unicode
163 # safe! We will try directories further up the
164 # directory tree in the hopes of getting a path on
165 # the same disk without the unicode...
168 return win32file.GetDiskFreeSpaceEx(path)[0]
170 path = os.path.split(path)[0]
175 # (2GB limit on partition size, so this should be
176 # accurate except for mapped network drives)
177 # Arno: see http://aspn.activestate.com/ASPN/docs/ActivePython/2.4/pywin32/win32file__GetDiskFreeSpace_meth.html
178 def getfreespace(path):
179 [spc, bps, nfc, tnc] = win32file.GetDiskFreeSpace(path)
180 return long(nfc) * long(spc) * long(bps)
183 # Windows if win32all extensions aren't installed
184 # (parse the output from the dir command)
185 def getfreespace(path):
187 mystdin, mystdout = os.popen2(u"dir " + u"\"" + path + u"\"")
191 for line in mystdout:
193 # Arno: FIXME: this won't work on non-English Windows, as reported by the IRT
194 index = line.rfind("bytes free")
195 if index > -1 and line[index:] == "bytes free":
196 parts = line.split(" ")
199 part = part.replace(",", "")
203 size = long(sizestring)
206 print >>sys.stderr,"getfreespace: can't determine freespace of ",path
207 for line in mystdout:
208 print >>sys.stderr,line
212 # If in doubt, just return something really large
219 # TODO: support for Mac? (will statvfs work with OS X?)
220 def getfreespace(path):
221 # If in doubt, just return something really large
226 invalidwinfilenamechars = ''
228 invalidwinfilenamechars += chr(i)
229 invalidwinfilenamechars += '"*/:<>?\\|'
230 invalidlinuxfilenamechars = '/'
232 def fix_filebasename(name, unit=False, maxlen=255):
233 """ Check if str is a valid Windows file name (or unit name if unit is true)
234 * If the filename isn't valid: returns a corrected name
235 * If the filename is valid: returns the filename
237 if unit and (len(name) != 2 or name[1] != ':'):
239 if not name or name == '.' or name == '..':
245 if len(name) > maxlen:
252 if sys.platform.startswith('win'):
253 invalidchars = invalidwinfilenamechars
255 invalidchars = invalidlinuxfilenamechars
257 if c in invalidchars:
265 file_dir, basename = os.path.split(fixedname)
266 while file_dir != '':
268 file_dir, basename = os.path.split(fixedname)
276 return last_minute_filename_clean(fixedname)
277 elif spaces == len(name):
278 # contains only spaces
281 return last_minute_filename_clean(name)
283 def last_minute_filename_clean(name):
284 s = name.strip() # Arno: remove initial or ending space
285 if sys.platform == 'win32' and s.endswith('..'):
290 def get_readable_torrent_name(infohash, raw_filename):
291 # return name__infohash.torrent
292 hex_infohash = binascii.hexlify(infohash)
293 suffix = '__' + hex_infohash + '.torrent'
294 save_name = ' ' + fix_filebasename(raw_filename, maxlen=254-len(suffix)) + suffix
295 # use a space ahead to distinguish from previous collected torrents
299 if sys.platform == "win32":
303 """ Returns total CPU usage as fraction (0..1).
304 Warning: side-effect: sleeps for 0.1 second to do diff """
305 #mempath = win32pdh.MakeCounterPath((None, "Memory", None, None, -1, "Available MBytes"))
306 cpupath = win32pdh.MakeCounterPath((None, "Processor", "_Total", None, -1, "% Processor Time"))
307 query = win32pdh.OpenQuery(None, 0)
308 counter = win32pdh.AddCounter(query, cpupath, 0)
310 win32pdh.CollectQueryData(query)
311 # Collect must be called twice for CPU, see http://support.microsoft.com/kb/262938
313 win32pdh.CollectQueryData(query)
315 status, value = win32pdh.GetFormattedCounterValue(counter,win32pdh.PDH_FMT_LONG)
317 return float(value)/100.0
319 elif sys.platform == "linux2":
320 def read_proc_stat():
321 """ Read idle and total CPU time counters from /proc/stat, see
323 f = open("/proc/stat","rb")
329 if line.startswith("cpu "): # note space
333 total += int(words[i])
341 """ Returns total CPU usage as fraction (0..1).
342 Warning: side-effect: sleeps for 0.1 second to do diff """
343 (total1,idle1) = read_proc_stat()
345 (total2,idle2) = read_proc_stat()
346 total = total2 - total1
348 return 1.0-(float(idle))/float(total)
352 raise ValueError("Not yet implemented")