10a8c90248264a8b26970b4473770bcc3df8515fJosh Gao"""Extended file operations available in POSIX. 20a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 30a8c90248264a8b26970b4473770bcc3df8515fJosh Gaof = posixfile.open(filename, [mode, [bufsize]]) 40a8c90248264a8b26970b4473770bcc3df8515fJosh Gao will create a new posixfile object 50a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 60a8c90248264a8b26970b4473770bcc3df8515fJosh Gaof = posixfile.fileopen(fileobject) 70a8c90248264a8b26970b4473770bcc3df8515fJosh Gao will create a posixfile object from a builtin file object 80a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 90a8c90248264a8b26970b4473770bcc3df8515fJosh Gaof.file() 100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao will return the original builtin file object 110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 120a8c90248264a8b26970b4473770bcc3df8515fJosh Gaof.dup() 130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao will return a new file object based on a new filedescriptor 140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 150a8c90248264a8b26970b4473770bcc3df8515fJosh Gaof.dup2(fd) 160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao will return a new file object based on the given filedescriptor 170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 180a8c90248264a8b26970b4473770bcc3df8515fJosh Gaof.flags(mode) 190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao will turn on the associated flag (merge) 200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao mode can contain the following characters: 210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (character representing a flag) 230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao a append only flag 240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao c close on exec flag 250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao n no delay flag 260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao s synchronization flag 270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (modifiers) 280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ! turn flags 'off' instead of default 'on' 290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao = copy flags 'as is' instead of default 'merge' 300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ? return a string in which the characters represent the flags 310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao that are set 320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao note: - the '!' and '=' modifiers are mutually exclusive. 340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao - the '?' modifier will return the status of the flags after they 350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao have been changed by other characters in the mode string 360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 370a8c90248264a8b26970b4473770bcc3df8515fJosh Gaof.lock(mode [, len [, start [, whence]]]) 380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao will (un)lock a region 390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao mode can contain the following characters: 400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (character representing type of lock) 420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao u unlock 430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao r read lock 440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao w write lock 450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (modifiers) 460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao | wait until the lock can be granted 470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ? return the first lock conflicting with the requested lock 480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao or 'None' if there is no conflict. The lock returned is in the 490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao format (mode, len, start, whence, pid) where mode is a 500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao character representing the type of lock ('r' or 'w') 510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao note: - the '?' modifier prevents a region from being locked; it is 530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao query only 540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao""" 550a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport warnings 560a8c90248264a8b26970b4473770bcc3df8515fJosh Gaowarnings.warn("The posixfile module is deprecated; " 570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao "fcntl.lockf() provides better locking", DeprecationWarning, 2) 580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 590a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass _posixfile_: 600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """File wrapper class that provides extra POSIX file routines.""" 610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao states = ['open', 'closed'] 630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Internal routines 660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __repr__(self): 680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao file = self._file_ 690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return "<%s posixfile '%s', mode '%s' at %s>" % \ 700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (self.states[file.closed], file.name, file.mode, \ 710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao hex(id(self))[2:]) 720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Initialization routines 750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def open(self, name, mode='r', bufsize=-1): 770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao import __builtin__ 780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self.fileopen(__builtin__.open(name, mode, bufsize)) 790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def fileopen(self, file): 810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao import types 820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if repr(type(file)) != "<type 'file'>": 830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise TypeError, 'posixfile.fileopen() arg must be file object' 840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self._file_ = file 850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Copy basic file methods 860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for maybemethod in dir(file): 870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not maybemethod.startswith('_'): 880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao attr = getattr(file, maybemethod) 890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if isinstance(attr, types.BuiltinMethodType): 900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao setattr(self, maybemethod, attr) 910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self 920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # New methods 950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def file(self): 970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self._file_ 980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def dup(self): 1000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao import posix 1010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not hasattr(posix, 'fdopen'): 1030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise AttributeError, 'dup() method unavailable' 1040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return posix.fdopen(posix.dup(self._file_.fileno()), self._file_.mode) 1060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def dup2(self, fd): 1080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao import posix 1090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not hasattr(posix, 'fdopen'): 1110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise AttributeError, 'dup() method unavailable' 1120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao posix.dup2(self._file_.fileno(), fd) 1140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return posix.fdopen(fd, self._file_.mode) 1150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def flags(self, *which): 1170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao import fcntl, os 1180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if which: 1200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if len(which) > 1: 1210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise TypeError, 'Too many arguments' 1220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao which = which[0] 1230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: which = '?' 1240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_flags = 0 1260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if 'n' in which: l_flags = l_flags | os.O_NDELAY 1270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if 'a' in which: l_flags = l_flags | os.O_APPEND 1280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if 's' in which: l_flags = l_flags | os.O_SYNC 1290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao file = self._file_ 1310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if '=' not in which: 1330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao cur_fl = fcntl.fcntl(file.fileno(), fcntl.F_GETFL, 0) 1340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if '!' in which: l_flags = cur_fl & ~ l_flags 1350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: l_flags = cur_fl | l_flags 1360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_flags = fcntl.fcntl(file.fileno(), fcntl.F_SETFL, l_flags) 1380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if 'c' in which: 1400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao arg = ('!' not in which) # 0 is don't, 1 is do close on exec 1410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_flags = fcntl.fcntl(file.fileno(), fcntl.F_SETFD, arg) 1420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if '?' in which: 1440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao which = '' # Return current flags 1450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_flags = fcntl.fcntl(file.fileno(), fcntl.F_GETFL, 0) 1460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if os.O_APPEND & l_flags: which = which + 'a' 1470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if fcntl.fcntl(file.fileno(), fcntl.F_GETFD, 0) & 1: 1480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao which = which + 'c' 1490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if os.O_NDELAY & l_flags: which = which + 'n' 1500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if os.O_SYNC & l_flags: which = which + 's' 1510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return which 1520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def lock(self, how, *args): 1540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao import struct, fcntl 1550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if 'w' in how: l_type = fcntl.F_WRLCK 1570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif 'r' in how: l_type = fcntl.F_RDLCK 1580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif 'u' in how: l_type = fcntl.F_UNLCK 1590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: raise TypeError, 'no type of lock specified' 1600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if '|' in how: cmd = fcntl.F_SETLKW 1620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif '?' in how: cmd = fcntl.F_GETLK 1630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: cmd = fcntl.F_SETLK 1640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_whence = 0 1660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_start = 0 1670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_len = 0 1680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if len(args) == 1: 1700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_len = args[0] 1710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif len(args) == 2: 1720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_len, l_start = args 1730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif len(args) == 3: 1740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_len, l_start, l_whence = args 1750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif len(args) > 3: 1760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise TypeError, 'too many arguments' 1770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Hack by davem@magnet.com to get locking to go on freebsd; 1790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # additions for AIX by Vladimir.Marangozov@imag.fr 1800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao import sys, os 1810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if sys.platform in ('netbsd1', 1820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'openbsd2', 1830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'freebsd2', 'freebsd3', 'freebsd4', 'freebsd5', 1840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'freebsd6', 'freebsd7', 'freebsd8', 1850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'bsdos2', 'bsdos3', 'bsdos4'): 1860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao flock = struct.pack('lxxxxlxxxxlhh', \ 1870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_start, l_len, os.getpid(), l_type, l_whence) 1880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif sys.platform in ('aix3', 'aix4'): 1890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao flock = struct.pack('hhlllii', \ 1900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_type, l_whence, l_start, l_len, 0, 0, 0) 1910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 1920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao flock = struct.pack('hhllhh', \ 1930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_type, l_whence, l_start, l_len, 0, 0) 1940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao flock = fcntl.fcntl(self._file_.fileno(), cmd, flock) 1960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if '?' in how: 1980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if sys.platform in ('netbsd1', 1990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'openbsd2', 2000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'freebsd2', 'freebsd3', 'freebsd4', 'freebsd5', 2010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'bsdos2', 'bsdos3', 'bsdos4'): 2020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_start, l_len, l_pid, l_type, l_whence = \ 2030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao struct.unpack('lxxxxlxxxxlhh', flock) 2040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif sys.platform in ('aix3', 'aix4'): 2050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_type, l_whence, l_start, l_len, l_sysid, l_pid, l_vfs = \ 2060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao struct.unpack('hhlllii', flock) 2070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif sys.platform == "linux2": 2080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_type, l_whence, l_start, l_len, l_pid, l_sysid = \ 2090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao struct.unpack('hhllhh', flock) 2100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l_type, l_whence, l_start, l_len, l_sysid, l_pid = \ 2120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao struct.unpack('hhllhh', flock) 2130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if l_type != fcntl.F_UNLCK: 2150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if l_type == fcntl.F_RDLCK: 2160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return 'r', l_len, l_start, l_whence, l_pid 2170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return 'w', l_len, l_start, l_whence, l_pid 2190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2200a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef open(name, mode='r', bufsize=-1): 2210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Public routine to open a file as a posixfile object.""" 2220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return _posixfile_().open(name, mode, bufsize) 2230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2240a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef fileopen(file): 2250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Public routine to get a posixfile object from a Python file object.""" 2260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return _posixfile_().fileopen(file) 2270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# 2290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# Constants 2300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# 2310a8c90248264a8b26970b4473770bcc3df8515fJosh GaoSEEK_SET = 0 2320a8c90248264a8b26970b4473770bcc3df8515fJosh GaoSEEK_CUR = 1 2330a8c90248264a8b26970b4473770bcc3df8515fJosh GaoSEEK_END = 2 2340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# 2360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# End of posixfile.py 2370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# 238