11dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project# common python utility routines for the Bionic tool scripts 21dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 31dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectimport sys, os, commands, string, commands 41dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 51dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project# basic debugging trace support 61dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project# call D_setlevel to set the verbosity level 71dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project# and D(), D2(), D3(), D4() to add traces 81dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project# 91dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectverbose = 0 101dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 111dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectdef panic(msg): 121dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project sys.stderr.write( find_program_name() + ": error: " ) 131dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project sys.stderr.write( msg ) 141dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project sys.exit(1) 151dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 161dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectdef D(msg): 171dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project global verbose 181dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if verbose > 0: 191dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project print msg 201dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 211dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectdef D2(msg): 221dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project global verbose 231dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if verbose >= 2: 241dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project print msg 251dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 261dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectdef D3(msg): 271dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project global verbose 281dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if verbose >= 3: 291dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project print msg 301dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 311dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectdef D4(msg): 321dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project global verbose 331dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if verbose >= 4: 341dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project print msg 351dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 361dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectdef D_setlevel(level): 371dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project global verbose 381dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project verbose = level 391dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 401dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 411dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project# other stuff 421dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project# 431dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project# 441dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectdef find_program_name(): 451dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return os.path.basename(sys.argv[0]) 461dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 471dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectdef find_program_dir(): 481dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return os.path.dirname(sys.argv[0]) 491dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 501dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectdef find_file_from_upwards(from_path,target_file): 511dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project """find a file in the current directory or its parents. if 'from_path' is None, 521dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project seach from the current program's directory""" 531dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project path = from_path 541dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if path == None: 551dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project path = os.path.realpath(sys.argv[0]) 561dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project path = os.path.dirname(path) 571dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D("this script seems to be located in: %s" % path) 581dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 591dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project while 1: 601dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D("probing "+path) 611dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if path == "": 621dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project file = target_file 631dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project else: 641dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project file = path + "/" + target_file 651dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 661dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if os.path.isfile(file): 671dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D("found %s in %s" % (target_file, path)) 681dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return file 691dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 701dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if path == "": 711dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return None 721dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 731dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project path = os.path.dirname(path) 741dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 751dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectdef find_bionic_root(): 761dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project file = find_file_from_upwards(None, "SYSCALLS.TXT") 771dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if file: 781dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return os.path.dirname(file) 791dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project else: 801dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return None 811dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 821dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectdef find_kernel_headers(): 831dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project """try to find the directory containing the kernel headers for this machine""" 841dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project status, version = commands.getstatusoutput( "uname -r" ) # get Linux kernel version 851dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if status != 0: 861dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D("could not execute 'uname -r' command properly") 871dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return None 881dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 891dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project # get rid of the "-xenU" suffix that is found in Xen virtual machines 901dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if len(version) > 5 and version[-5:] == "-xenU": 911dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project version = version[:-5] 921dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 931dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project path = "/usr/src/linux-headers-" + version 941dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D("probing %s for kernel headers" % (path+"/include")) 951dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project ret = os.path.isdir( path ) 961dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if ret: 971dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D("found kernel headers in: %s" % (path + "/include")) 981dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return path 991dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return None 1001dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1011dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1021dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project# parser for the SYSCALLS.TXT file 1031dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project# 1041dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectclass SysCallsTxtParser: 1051dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def __init__(self): 1061dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.syscalls = [] 1071dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.lineno = 0 1081dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1091dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def E(msg): 1101dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project print "%d: %s" % (self.lineno, msg) 1111dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1121dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def parse_line(self, line): 1131dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project pos_lparen = line.find('(') 1141dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project E = self.E 1151dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if pos_lparen < 0: 1161dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project E("missing left parenthesis in '%s'" % line) 1171dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 1181dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1191dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project pos_rparen = line.rfind(')') 1201dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if pos_rparen < 0 or pos_rparen <= pos_lparen: 1211dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project E("missing or misplaced right parenthesis in '%s'" % line) 1221dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 1231dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1241dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return_type = line[:pos_lparen].strip().split() 1251dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if len(return_type) < 2: 1261dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project E("missing return type in '%s'" % line) 1271dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 1281dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1291dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project syscall_func = return_type[-1] 1301dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return_type = string.join(return_type[:-1],' ') 1311dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1321dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project pos_colon = syscall_func.find(':') 1331dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if pos_colon < 0: 1341dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project syscall_name = syscall_func 1351dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project else: 1361dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if pos_colon == 0 or pos_colon+1 >= len(syscall_func): 1371dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project E("misplaced colon in '%s'" % line) 1381dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 1391dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project syscall_name = syscall_func[pos_colon+1:] 1401dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project syscall_func = syscall_func[:pos_colon] 1411dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1421dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if pos_rparen > pos_lparen+1: 1431dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project syscall_params = line[pos_lparen+1:pos_rparen].split(',') 1441dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project params = string.join(syscall_params,',') 1451dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project else: 1461dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project syscall_params = [] 1471dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project params = "void" 1481dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1491dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project number = line[pos_rparen+1:].strip() 1501dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if number == "stub": 1511dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project syscall_id = -1 1521dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project syscall_id2 = -1 1531dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project else: 1541dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project try: 1551dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if number[0] == '#': 1561dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project number = number[1:].strip() 1571dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project numbers = string.split(number,',') 1581dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project syscall_id = int(numbers[0]) 1591dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project syscall_id2 = syscall_id 1601dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if len(numbers) > 1: 1611dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project syscall_id2 = int(numbers[1]) 1621dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project except: 1631dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project E("invalid syscall number in '%s'" % line) 1641dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 1651dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1661dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project t = { "id" : syscall_id, 1671dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project "id2" : syscall_id2, 1681dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project "name" : syscall_name, 1691dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project "func" : syscall_func, 1701dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project "params" : syscall_params, 1711dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project "decl" : "%-15s %s (%s);" % (return_type, syscall_func, params) } 1721dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1731dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.syscalls.append(t) 1741dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1751dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def parse_file(self, file_path): 1761dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project fp = open(file_path) 1771dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for line in fp.xreadlines(): 1781dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.lineno += 1 1791dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project line = line.strip() 1801dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if not line: continue 1811dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if line[0] == '#': continue 1821dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.parse_line(line) 1831dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1841dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project fp.close() 1851dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1861dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1871dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectclass Output: 1881dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def __init__(self,out=sys.stdout): 1891dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.out = out 1901dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1911dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def write(self,msg): 1921dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.out.write(msg) 1931dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1941dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def writeln(self,msg): 1951dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.out.write(msg) 1961dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.out.write("\n") 1971dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1981dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectclass StringOutput: 1991dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def __init__(self): 2001dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.line = "" 2011dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2021dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def write(self,msg): 2031dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.line += msg 2041dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D2("write '%s'" % msg) 2051dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2061dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def writeln(self,msg): 2071dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.line += msg + '\n' 2081dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D2("write '%s\\n'"% msg) 2091dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2101dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def get(self): 2111dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return self.line 2121dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2131dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2141dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectdef create_file_path(path): 2151dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project dirs = [] 2161dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project while 1: 2171dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project parent = os.path.dirname(path) 2181dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project #print "parent: %s <- %s" % (parent, path) 2191dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if parent == "/" or parent == "": 2201dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project break 2211dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project dirs.append(parent) 2221dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project path = parent 2231dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2241dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project dirs.reverse() 2251dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for dir in dirs: 2261dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project #print "dir %s" % dir 2271dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if os.path.isdir(dir): 2281dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project continue 2291dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project os.mkdir(dir) 2301dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2311dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectdef walk_source_files(paths,callback,args,excludes=[]): 2321dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project """recursively walk a list of paths and files, only keeping the source files in directories""" 2331dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for path in paths: 234fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner if len(path) > 0 and path[0] == '@': 235fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner # this is the name of another file, include it and parse it 236fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner path = path[1:] 237fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner if os.path.exists(path): 238fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner for line in open(path): 239fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner if len(line) > 0 and line[-1] == '\n': 240fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner line = line[:-1] 241fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner walk_source_files([line],callback,args,excludes) 242fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner continue 2431dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if not os.path.isdir(path): 2441dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project callback(path,args) 2451dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project else: 2461dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for root, dirs, files in os.walk(path): 2471dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project #print "w-- %s (ex: %s)" % (repr((root,dirs)), repr(excludes)) 2481dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if len(excludes): 2491dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for d in dirs[:]: 250fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner if os.path.join(root,d) in excludes: 2511dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project dirs.remove(d) 2521dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for f in files: 2531dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project r, ext = os.path.splitext(f) 2541dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if ext in [ ".h", ".c", ".cpp", ".S" ]: 2551dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project callback( "%s/%s" % (root,f), args ) 2561dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2571dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectdef cleanup_dir(path): 2581dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project """create a directory if needed, and ensure that it is totally empty 2591dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project by removing any existing content in it""" 2601dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if not os.path.exists(path): 2611dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project os.mkdir(path) 2621dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project else: 2631dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for root, dirs, files in os.walk(path, topdown=False): 2641dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if root.endswith("kernel_headers/"): 2651dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project # skip 'kernel_headers' 2661dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project continue 2671dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for name in files: 2681dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project os.remove(os.path.join(root, name)) 2691dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for name in dirs: 2701dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project os.rmdir(os.path.join(root, name)) 2711dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2721dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectdef update_file( path, newdata ): 2731dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project """update a file on disk, only if its content has changed""" 2741dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if os.path.exists( path ): 2751dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project try: 2761dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project f = open( path, "r" ) 2771dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project olddata = f.read() 2781dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project f.close() 2791dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project except: 2801dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D("update_file: cannot read existing file '%s'" % path) 2811dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0 2821dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2831dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if oldata == newdata: 2841dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D2("update_file: no change to file '%s'" % path ) 2851dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0 2861dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2871dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project update = 1 2881dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project else: 2891dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project try: 2901dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project create_file_path(path) 2911dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project except: 2921dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D("update_file: cannot create path to '%s'" % path) 2931dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0 2941dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2951dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project f = open( path, "w" ) 2961dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project f.write( newdata ) 2971dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project f.close() 2981dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 2991dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 1 3001dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3011dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3021dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectclass BatchFileUpdater: 3031dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project """a class used to edit several files at once""" 3041dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def __init__(self): 3051dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.old_files = set() 3061dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.new_files = set() 3071dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.new_data = {} 3081dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3091dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def readFile(self,path): 3101dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project #path = os.path.realpath(path) 3111dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if os.path.exists(path): 3121dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.old_files.add(path) 3131dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3141dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def readDir(self,path): 3151dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project #path = os.path.realpath(path) 3161dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for root, dirs, files in os.walk(path): 3171dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for f in files: 3181dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project dst = "%s/%s" % (root,f) 3191dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.old_files.add(dst) 3201dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3211dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def editFile(self,dst,data): 3221dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project """edit a destination file. if the file is not mapped from a source, 3231dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project it will be added. return 0 if the file content wasn't changed, 3241dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 1 if it was edited, or 2 if the file is new""" 3251dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project #dst = os.path.realpath(dst) 3261dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project result = 1 3271dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if os.path.exists(dst): 3281dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project f = open(dst, "r") 3291dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project olddata = f.read() 3301dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project f.close() 3311dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if olddata == data: 3321dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.old_files.remove(dst) 3331dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return 0 3341dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project else: 3351dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project result = 2 3361dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3371dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.new_data[dst] = data 3381dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self.new_files.add(dst) 3391dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return result 3401dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3411dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def getChanges(self): 3421dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project """determine changes, returns (adds, deletes, edits)""" 3431dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project adds = set() 3441dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project edits = set() 3451dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project deletes = set() 3461dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3471dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for dst in self.new_files: 3481dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if not (dst in self.old_files): 3491dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project adds.add(dst) 3501dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project else: 3511dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project edits.add(dst) 3521dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3531dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for dst in self.old_files: 3541dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if not dst in self.new_files: 3551dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project deletes.add(dst) 3561dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3571dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project return (adds, deletes, edits) 3581dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3591dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def _writeFile(self,dst,data=None): 3601dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if not os.path.exists(os.path.dirname(dst)): 3611dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project create_file_path(dst) 3621dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if data == None: 3631dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project data = self.new_data[dst] 3641dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project f = open(dst, "w") 3651dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project f.write(self.new_data[dst]) 3661dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project f.close() 3671dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3681dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def updateFiles(self): 3691dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project adds, deletes, edits = self.getChanges() 3701dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3711dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for dst in sorted(adds): 3721dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self._writeFile(dst) 3731dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3741dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for dst in sorted(edits): 3751dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self._writeFile(dst) 3761dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3771dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for dst in sorted(deletes): 3781dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project os.remove(dst) 3791dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3801dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project def updateP4Files(self): 3811dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project adds, deletes, edits = self.getChanges() 3821dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3831dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if len(adds): 3841dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project files = string.join(sorted(adds)," ") 3851dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D( "%d new files will be p4 add-ed" % len(adds) ) 3861dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for dst in adds: 3871dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self._writeFile(dst) 3881dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D2("P4 ADDS: %s" % files) 3891dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project o = commands.getoutput( "p4 add " + files ) 3901dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D2( o ) 3911dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 3921dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if len(edits): 3931dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project files = string.join(sorted(edits)," ") 3941dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D( "%d files will be p4 edit-ed" % len(edits) ) 3951dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D2("P4 EDITS: %s" % files) 3961dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project o = commands.getoutput( "p4 edit " + files ) 3971dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D2( o ) 3981dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project for dst in edits: 3991dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project self._writeFile(dst) 4001dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project 4011dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project if len(deletes): 4021dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project files = string.join(sorted(deletes)," ") 4031dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D( "%d files will be p4 delete-d" % len(deletes) ) 4041dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D2("P4 DELETES: %s" % files) 4051dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project o = commands.getoutput( "p4 delete " + files ) 4061dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project D2( o ) 407fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner 408fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner def updateGitFiles(self): 409fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner adds, deletes, edits = self.getChanges() 410fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner 411fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner if adds: 412fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner for dst in sorted(adds): 413fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner self._writeFile(dst) 414fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner commands.getoutput("git add " + " ".join(adds)) 415fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner 416fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner if deletes: 417fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner commands.getoutput("git rm " + " ".join(deletes)) 418fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner 419fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner if edits: 420fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner for dst in sorted(edits): 421fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner self._writeFile(dst) 422fc2693110ee8a2ba22a445ad9855fbe9e118d439David 'Digit' Turner commands.getoutput("git add " + " ".join(edits)) 423