#!/usr/bin/python #Author: Stephen Moon #Date: 3/5/09 #Last Modified Date: 3/11/09 #Retrieves Perforce binaries or ASCII files from ftp.perforce.com # #Usage: # # In Windows, remove she-bang line # import ftplib, re, os, sys HOST = 'ftp.perforce.com' DIR_NAME = 'perforce' #holds each displayed line entry class DirEntry(object): #inherits from class object def __init__(self,line): #only accept one argument line self.parts = line.split(None,8) #split into 8 parts for each line def isValid(self): return len(self.parts) >= 6 #if num of parts greater than 6 then it's valid def getType(self): return self.parts[0][0] #get the very first character on the line (i.e. def getFilename(self): if self.getType() != 'l': #if it is not a link return self.parts[-1] #return the last part else: return self.parts[-1].split(' -> ', 1)[0] #if it is, return shortcut def getSize(self): if self.getType() == '-': return int(self.parts[4])/1024 def getMonth(self): if self.getType() == '-': return self.parts[5] def getDay(self): if self.getType() == '-': return self.parts[6] def getLinkDest(self): #return the original destination of link if self.getType() == 'l': return self.parts[-1].split(' -> ', 1)[1] else: raise RuntimeError, "getLinkDest() called on non-link item" #creates a key (fname) and value (line for the filename) class DirScanner(dict): #inherits from class dict def addline(self,line): #only accepts one arugment line obj = DirEntry(line) if obj.isValid(): #line object contains more than 6 parts self[obj.getFilename()] = obj #creates a key/value pair, key = fname #downloads a file def downloadFile(ftpobj, filename): ftpobj.voidcmd("Type I") #based on the filename, it returns a tuple of the data connection and the expected #size of data datasock, estimatedSize = ftpobj.ntransfercmd("RETR %s" % filename) transbytes = 0 fd = file(filename, 'wb') while True: buf = datasock.recv(2048) if not len(buf): break; fd.write(buf) transbytes += len(buf) sys.stdout.write("%s: Received %d " % (filename, transbytes)) if estimatedSize: sys.stdout.write("of %d bytes (%.1f%%)\n" % (estimatedSize, 100.0 * float(transbytes) / float(estimatedSize))) else: sys.stdout.write("bytes") fd.close() datasock.close() ftpobj.voidresp() sys.stdout.write("\n") #log in anonymously to ftp.perforce.com and then change directory to perforce def initialize(): try: ftp = ftplib.FTP(HOST) except (socket.error, socket.gaierror), e: print 'Error: cannot reach "%s"' % HOST return print '\n*** Connected to host "%s" ***' % HOST try: ftp.login() except ftplib.error_perm: print 'Error: cannot login anonymously' return try: ftp.cwd(DIR_NAME) except ftplib.error_perm: print 'Error: cannot change directory to "%s"' % DIR_NAME ftp.quit() return return ftp #displays the entries for a chosen directory def displayFiles(ftp): data = {} data = DirScanner() #create data object of class DirScanner ftp.dir(data.addline) #add lines to dict when dir is run f = '[FILE]' d = '[DIR]' keys = data.keys() #get the keys keys.sort() #sort it #print it to the sorted filenames print "\nCurrent Working Directory: ",ftp.pwd(),"\n" for i,eachEntry in enumerate(keys): if data[eachEntry].getType() == '-': #DirEntry object using fileName key print '%2d:%6s %5sK %-3s %2s %s' % (i,f,data[eachEntry].getSize(), data[eachEntry].getMonth(),data[eachEntry].getDay(),eachEntry) elif data[eachEntry].getType() == 'd': #same as above but directory print '%2d:%6s %s' % (i,d,eachEntry) #print '('+str(i)+'):\t [DIR] ',eachEntry print '\n%2s: go up one directory' % 'u' print '%2s: exit\n' % 'e' return data #main starts here def main(): ftp = initialize() data = displayFiles(ftp) choice = raw_input("Choose a directory or file from the above choices: ") while True: if choice is 'u': ftp.cwd("..") elif choice is 'e': break; elif choice.isalpha(): print "\n*** You have entered alpha character other than given choices ***\n" elif(int(choice) <= len(data) - 1): keys = data.keys() keys.sort() # filenames sorted for i, fileName in enumerate(keys): if i == int(choice): if data[fileName].getType() == '-': #DirEntry object using fileName key try: downloadFile(ftp,fileName) except ftplib.error_perm: print 'Error: cannot download file "%s"' % fileName os.unlink(fileName) else: print '*** Downloaded "%s" to current local directory ***' % fileName elif data[fileName].getType() == 'd': #same as above but directory try: ftp.cwd(fileName) except ftplib.error_perm: print 'Error: cannot cd to "%s"' % fileName return else: print "\n*** You have made an invalid choice ***\n" data = displayFiles(ftp) choice = raw_input("Choose a directory or file from the above choices: ") ftplib.FTP(HOST).quit() if __name__ == '__main__': main()