169 for index, code in enumerate(self.exit_codes): |
169 for index, code in enumerate(self.exit_codes): |
170 if code != 0: |
170 if code != 0: |
171 return index |
171 return index |
172 return None |
172 return None |
173 |
173 |
|
174 def add_process(self, processes, command, stdin, stdout): |
|
175 try: |
|
176 processes.append(subprocess.Popen(command, stdin=stdin, |
|
177 stdout=stdout, |
|
178 stderr=self.stderr)) |
|
179 except OSError, error: |
|
180 if error.errno == errno.ENOENT: |
|
181 raise ExtractorUnusable("could not run %s" % (command[0],)) |
|
182 raise |
|
183 |
174 def run_pipes(self, final_stdout=None): |
184 def run_pipes(self, final_stdout=None): |
175 if not self.pipes: |
185 if not self.pipes: |
176 return |
186 return |
177 elif final_stdout is None: |
187 elif final_stdout is None: |
178 # FIXME: Buffering this might be dumb. |
188 final_stdout = open('/dev/null', 'w') |
179 final_stdout = tempfile.TemporaryFile() |
|
180 num_pipes = len(self.pipes) |
189 num_pipes = len(self.pipes) |
181 last_pipe = num_pipes - 1 |
190 last_pipe = num_pipes - 1 |
182 processes = [] |
191 processes = [] |
183 for index, command in enumerate([pipe[0] for pipe in self.pipes]): |
192 for index, command in enumerate([pipe[0] for pipe in self.pipes]): |
184 if index == 0: |
193 if index == 0: |
187 stdin = processes[-1].stdout |
196 stdin = processes[-1].stdout |
188 if index == last_pipe: |
197 if index == last_pipe: |
189 stdout = final_stdout |
198 stdout = final_stdout |
190 else: |
199 else: |
191 stdout = subprocess.PIPE |
200 stdout = subprocess.PIPE |
192 try: |
201 self.add_process(processes, command, stdin, stdout) |
193 processes.append(subprocess.Popen(command, stdin=stdin, |
|
194 stdout=stdout, |
|
195 stderr=self.stderr)) |
|
196 except OSError, error: |
|
197 if error.errno == errno.ENOENT: |
|
198 raise ExtractorUnusable("could not run %s" % (command[0],)) |
|
199 raise |
|
200 self.exit_codes = [pipe.wait() for pipe in processes] |
202 self.exit_codes = [pipe.wait() for pipe in processes] |
201 self.archive.close() |
203 self.archive.close() |
202 for index in range(last_pipe): |
204 for index in range(last_pipe): |
203 processes[index].stdout.close() |
205 processes[index].stdout.close() |
204 self.archive = final_stdout |
206 self.archive = final_stdout |
288 self.archive.close() |
290 self.archive.close() |
289 os.chdir(old_path) |
291 os.chdir(old_path) |
290 |
292 |
291 def get_filenames(self): |
293 def get_filenames(self): |
292 self.pipe(self.list_pipe, "listing") |
294 self.pipe(self.list_pipe, "listing") |
293 self.run_pipes() |
295 processes = [] |
|
296 stdin = self.archive |
|
297 for command in [pipe[0] for pipe in self.pipes]: |
|
298 self.add_process(processes, command, stdin, subprocess.PIPE) |
|
299 stdin = processes[-1].stdout |
|
300 get_output_line = processes[-1].stdout.readline |
|
301 while True: |
|
302 line = get_output_line() |
|
303 if not line: |
|
304 break |
|
305 yield line.rstrip('\n') |
|
306 self.exit_codes = [pipe.wait() for pipe in processes] |
|
307 self.archive.close() |
|
308 for process in processes: |
|
309 process.stdout.close() |
294 self.check_success(False) |
310 self.check_success(False) |
295 self.archive.seek(0, 0) |
|
296 while True: |
|
297 line = self.archive.readline() |
|
298 if not line: |
|
299 self.archive.close() |
|
300 return |
|
301 yield line.rstrip('\n') |
|
302 |
311 |
303 |
312 |
304 class CompressionExtractor(BaseExtractor): |
313 class CompressionExtractor(BaseExtractor): |
305 file_type = 'compressed file' |
314 file_type = 'compressed file' |
306 name_checker = FilenameChecker |
315 name_checker = FilenameChecker |
950 class BaseAction(object): |
959 class BaseAction(object): |
951 def __init__(self, options, filenames): |
960 def __init__(self, options, filenames): |
952 self.options = options |
961 self.options = options |
953 self.filenames = filenames |
962 self.filenames = filenames |
954 self.target = None |
963 self.target = None |
|
964 self.do_print = False |
955 |
965 |
956 def report(self, function, *args): |
966 def report(self, function, *args): |
957 try: |
967 try: |
958 error = function(*args) |
968 error = function(*args) |
959 except EXTRACTION_ERRORS, exception: |
969 except EXTRACTION_ERRORS, exception: |
960 error = str(exception) |
970 error = str(exception) |
961 logger.debug(''.join(traceback.format_exception(*sys.exc_info()))) |
971 logger.debug(''.join(traceback.format_exception(*sys.exc_info()))) |
962 return error |
972 return error |
963 |
973 |
|
974 def show_filename(self, filename): |
|
975 if len(self.filenames) < 2: |
|
976 return |
|
977 elif self.do_print: |
|
978 print |
|
979 else: |
|
980 self.do_print = True |
|
981 print "%s:" % (filename,) |
|
982 |
964 |
983 |
965 class ExtractionAction(BaseAction): |
984 class ExtractionAction(BaseAction): |
966 handlers = [FlatHandler, OverwriteHandler, MatchHandler, EmptyHandler, |
985 handlers = [FlatHandler, OverwriteHandler, MatchHandler, EmptyHandler, |
967 BombHandler] |
986 BombHandler] |
968 |
|
969 def __init__(self, options, filenames): |
|
970 BaseAction.__init__(self, options, filenames) |
|
971 self.did_print = False |
|
972 |
987 |
973 def get_handler(self, extractor): |
988 def get_handler(self, extractor): |
974 if extractor.content_type in ONE_ENTRY_UNKNOWN: |
989 if extractor.content_type in ONE_ENTRY_UNKNOWN: |
975 self.options.one_entry_policy.prep(self.current_filename, |
990 self.options.one_entry_policy.prep(self.current_filename, |
976 extractor) |
991 extractor) |
981 break |
996 break |
982 |
997 |
983 def show_extraction(self, extractor): |
998 def show_extraction(self, extractor): |
984 if self.options.log_level > logging.INFO: |
999 if self.options.log_level > logging.INFO: |
985 return |
1000 return |
986 elif self.did_print: |
1001 self.show_filename(self.current_filename) |
987 print |
|
988 else: |
|
989 self.did_print = True |
|
990 print "%s:" % (self.current_filename,) |
|
991 if extractor.contents is None: |
1002 if extractor.contents is None: |
992 print self.current_handler.target |
1003 print self.current_handler.target |
993 return |
1004 return |
994 def reverser(x, y): |
1005 def reverser(x, y): |
995 return cmp(y, x) |
1006 return cmp(y, x) |
1021 self.target = self.current_handler.target |
1032 self.target = self.current_handler.target |
1022 return error |
1033 return error |
1023 |
1034 |
1024 |
1035 |
1025 class ListAction(BaseAction): |
1036 class ListAction(BaseAction): |
1026 def __init__(self, options, filenames): |
1037 def list_filenames(self, extractor, filename): |
1027 BaseAction.__init__(self, options, filenames) |
1038 # We get a line first to make sure there's not going to be some |
1028 self.count = 0 |
1039 # basic error before we show what filename we're listing. |
1029 |
1040 filename_lister = extractor.get_filenames() |
1030 def get_list(self, extractor): |
1041 try: |
1031 # Note: The reason I'm getting all the filenames up front is |
1042 first_line = filename_lister.next() |
1032 # because if we run into trouble partway through the archive, we'll |
1043 except StopIteration: |
1033 # try another extractor. So before we display anything we have to |
1044 self.show_filename(filename) |
1034 # be sure this one is successful. We maybe don't have to be quite |
1045 else: |
1035 # this conservative but this is the easy way out for now. |
1046 self.did_list = True |
1036 self.filelist = list(extractor.get_filenames()) |
1047 self.show_filename(filename) |
1037 |
1048 print first_line |
1038 def show_list(self, filename): |
1049 for line in filename_lister: |
1039 self.count += 1 |
1050 print line |
1040 if len(self.filenames) != 1: |
1051 |
1041 if self.count > 1: |
|
1042 print |
|
1043 print "%s:" % (filename,) |
|
1044 print '\n'.join(self.filelist) |
|
1045 |
|
1046 def run(self, filename, extractor): |
1052 def run(self, filename, extractor): |
1047 return (self.report(self.get_list, extractor) or |
1053 self.did_list = False |
1048 self.report(self.show_list, filename)) |
1054 error = self.report(self.list_filenames, extractor, filename) |
|
1055 if error and self.did_list: |
|
1056 logger.error("lister failed: ignore above listing for %s" % |
|
1057 (filename,)) |
|
1058 return error |
1049 |
1059 |
1050 |
1060 |
1051 class ExtractorApplication(object): |
1061 class ExtractorApplication(object): |
1052 def __init__(self, arguments): |
1062 def __init__(self, arguments): |
1053 for signal_num in (signal.SIGINT, signal.SIGTERM): |
1063 for signal_num in (signal.SIGINT, signal.SIGTERM): |