X-Git-Url: https://www.irasnyder.com/gitweb/?p=rarslave2.git;a=blobdiff_plain;f=rarslave.py;h=a8e69bcc38f03d935a821c5463691680b1f30aab;hp=2e9861a78e097f3e4e888ea261a5c2d16d0527e2;hb=c05c6922b1e23456c194f49b9646615a7a9a75c8;hpb=4eeb9e9a81a129b93ed11eb6023c5e1ec3767693 diff --git a/rarslave.py b/rarslave.py index 2e9861a..a8e69bc 100644 --- a/rarslave.py +++ b/rarslave.py @@ -1,60 +1,154 @@ #!/usr/bin/env python # vim: set ts=4 sts=4 sw=4 textwidth=112 : -import re, os, sys -import par2parser +VERSION="2.0.0" +PROGRAM="rarslave2" + +import re, os, sys, optparse +import Par2Parser +import RarslaveConfig +import RarslaveLogger # Global Variables -(TYPE_OLDRAR, TYPE_NEWRAR, TYPE_ZIP, TYPE_NOEXTRACT) = range (4) -(ECHECK, EEXTRACT, EDELETE) = range(1,4) +(TYPE_OLDRAR, TYPE_NEWRAR, TYPE_ZIP, TYPE_NOEXTRACT, TYPE_UNKNOWN) = range (5) +(SUCCESS, ECHECK, EEXTRACT, EDELETE) = range(4) +config = RarslaveConfig.RarslaveConfig() +logger = RarslaveLogger.RarslaveLogger () + +# Global options to be set / used later. +options = None class RarslaveExtractor (object): - def __init__ (self, type): - self.type = type - self.heads = [] + type = None + heads = [] - def addHead (self, dir, head): - assert os.path.isdir (dir) - # REQUIRES that the dir is valid, but not that the file is valid, so that - # we can move a file that doesn't exist yet. - # FIXME: probably CAN add this back, since we should be running this AFTER repair. - #assert os.path.isfile (os.path.join (dir, head)) + def __init__ (self, dir, p2files, name_files, prot_files): + + self.dir = dir + self.p2files = p2files + self.name_matched_files = name_files + self.prot_matched_files = prot_files + + # Find the type + self.type = self.__find_type () + + logger.addMessage ('Detected set of type: %s' % self, RarslaveLogger.MessageType.Debug) + + # Find the heads + self.heads = self.__find_heads () + + for h in self.heads: + logger.addMessage ('Adding extraction head: %s' % h, RarslaveLogger.MessageType.Debug) + + def __repr__ (self): + return \ + { TYPE_OLDRAR : 'Old RAR', + TYPE_NEWRAR : 'New RAR', + TYPE_ZIP : 'Zip', + TYPE_NOEXTRACT : 'No Extract', + TYPE_UNKNOWN : 'Unknown' } [self.type] + + def __find_type (self): + + all_files = no_duplicates (self.name_matched_files + self.prot_matched_files) + + if self.is_oldrar (all_files): + return TYPE_OLDRAR + elif self.is_newrar (all_files): + return TYPE_NEWRAR + elif self.is_zip (all_files): + return TYPE_ZIP + elif self.is_noextract (all_files): + return TYPE_NOEXTRACT + + return TYPE_UNKNOWN + + def __generic_find_heads (self, regex, ignorecase=True): + + heads = [] + + if ignorecase: + cregex = re.compile (regex, re.IGNORECASE) + else: + cregex = re.compile (regex) + + all_files = no_duplicates (self.name_matched_files + self.prot_matched_files) + + for f in all_files: + if cregex.match (f): + heads.append (f) + + return heads + + def __find_heads (self): + + if self.type == TYPE_OLDRAR: + return self.__generic_find_heads ('^.*\.rar$') + elif self.type == TYPE_NEWRAR: + return self.__generic_find_heads ('^.*\.part0*1\.rar$') + elif self.type == TYPE_ZIP: + return self.__generic_find_heads ('^.*\.zip$') + elif self.type == TYPE_NOEXTRACT: + return self.prot_matched_files + + return [] + + def __create_directory (self, dir): + if dir == None: + return SUCCESS - self.heads.append (os.path.join (dir, head)) + if os.path.isdir (dir): + return SUCCESS - def extract (self, todir=None): + try: + os.makedirs (dir) + logger.addMessage ('Created directory: %s' % dir, RarslaveLogger.MessageType.Verbose) + except OSError: + logger.addMessage ('FAILED to create directory: %s' % dir, RarslaveLogger.MessageType.Fatal) + return -EEXTRACT + + return SUCCESS + + def runExtract (self, todir=None): # Extract all heads of this set + # Extract to the head's dir if we don't care where to extract + if todir == None: + todir = self.dir + # Create the directory $todir if it doesn't exist - if todir != None and not os.path.isdir (todir): - # TODO: LOGGER - try: - os.makedirs (todir) - except OSError: - # TODO: LOGGER - return -EEXTRACT + ret = self.__create_directory (todir) + + if ret != SUCCESS: + return -EEXTRACT # Extract all heads extraction_func = \ { TYPE_OLDRAR : self.__extract_rar, TYPE_NEWRAR : self.__extract_rar, TYPE_ZIP : self.__extract_zip, - TYPE_NOEXTRACT : self.__extract_noextract }[self.type] + TYPE_NOEXTRACT : self.__extract_noextract, + TYPE_UNKNOWN : self.__extract_unknown }[self.type] # Call the extraction function on each head for h in self.heads: - if todir == None: - # Run in the head's directory - extraction_func (h, os.path.dirname (h)) - else: - extraction_func (h, todir) + full_head = full_abspath (h) + ret = extraction_func (full_head, todir) + logger.addMessage ('Extraction Function returned: %d' % ret, RarslaveLogger.MessageType.Debug) + + # Check error code + if ret != SUCCESS: + logger.addMessage ('Failed extracting: %s' % h, RarslaveLogger.MessageType.Fatal) + return -EEXTRACT + + return SUCCESS def __extract_rar (self, file, todir): assert os.path.isfile (file) assert os.path.isdir (todir) - RAR_CMD = 'unrar x -o+ -- ' + RAR_CMD = config.get_value ('commands', 'unrar') cmd = '%s \"%s\"' % (RAR_CMD, file) ret = run_command (cmd, todir) @@ -63,8 +157,10 @@ class RarslaveExtractor (object): if ret != 0: return -EEXTRACT + return SUCCESS + def __extract_zip (self, file, todir): - ZIP_CMD = 'unzip \"%s\" -d \"%s\"' + ZIP_CMD = config.get_value ('commands', 'unzip') cmd = ZIP_CMD % (file, todir) ret = run_command (cmd) @@ -73,123 +169,305 @@ class RarslaveExtractor (object): if ret != 0: return -EEXTRACT + return SUCCESS + def __extract_noextract (self, file, todir): # Just move this file to the $todir, since no extraction is needed # FIXME: NOTE: mv will fail by itself if you're moving to the same dir! - cmd = 'mv \"%s\" \"%s\"' % (file, todir) + NOEXTRACT_CMD = config.get_value ('commands', 'noextract') + + # Make sure that both files are not the same file. If they are, don't run at all. + if os.path.samefile (file, os.path.join (todir, file)): + return SUCCESS + + cmd = NOEXTRACT_CMD % (file, todir) ret = run_command (cmd) # Check error code if ret != 0: return -EEXTRACT + return SUCCESS + + def __extract_unknown (self, file, todir): + return SUCCESS + + def __generic_matcher (self, files, regex, nocase=False): + """Run the regex over the files, and see if one matches or not. + NOTE: this does not return the matches, just if a match occurred.""" + if nocase: + cregex = re.compile (regex, re.IGNORECASE) + else: + cregex = re.compile (regex) + + for f in files: + if cregex.match (f): + return True -class RarslaveRepairer (object): - # Verify (and repair) the set - # Make sure it worked, otherwise clean up and return failure + return False - def __init__ (self, dir, file, join=False): - self.dir = dir # the directory containing the par2 file - self.file = file # the par2 file - self.join = join # True if the par2 set is 001 002 ... + def is_oldrar (self, files): + return self.__generic_matcher (files, '^.*\.r00$') + def is_newrar (self, files): + return self.__generic_matcher (files, '^.*\.part0*1\.rar$') + + def is_zip (self, files): + return self.__generic_matcher (files, '^.*\.zip$') + + def is_noextract (self, files): + # Type that needs no extraction. + # TODO: Add others ??? + return self.__generic_matcher (files, '^.*\.001$') + +class PAR2Set (object): + + dir = None + p2file = None # The starting par2 + basename = None # The p2file's basename + all_p2files = [] + name_matched_files = [] # Files that match by basename of the p2file + prot_matched_files = [] # Files that match by being protected members + + def __init__ (self, dir, p2file): assert os.path.isdir (dir) - assert os.path.isfile (os.path.join (dir, file)) + assert os.path.isfile (os.path.join (dir, p2file)) - def checkAndRepair (self): - # Form the command: - # par2repair -- PAR2 PAR2_EXTRA [JOIN_FILES] - PAR2_CMD = 'par2repair -- ' + self.dir = dir + self.p2file = p2file + self.basename = self.__get_basename (p2file) + + # Find files that match by name only + self.name_matched_files = self.__find_name_matches (self.dir, self.basename) + + # Find all par2 files for this set using name matches + self.all_p2files = find_par2_files (self.name_matched_files) + + # Try to get the protected files for this set + self.prot_matched_files = self.__parse_all_par2 () + + def __list_eq (self, l1, l2): + + if len(l1) != len(l2): + return False + + for e in l1: + if e not in l2: + return False + + return True + + def __eq__ (self, rhs): + return (self.dir == rhs.dir) and (self.basename == rhs.basename) and \ + self.__list_eq (self.name_matched_files, rhs.name_matched_files) and \ + self.__list_eq (self.prot_matched_files, rhs.prot_matched_files) + + def __get_basename (self, name): + """Strips most kinds of endings from a filename""" + + regex = config.get_value ('regular expressions', 'basename_regex') + r = re.compile (regex, re.IGNORECASE) + done = False + + while not done: + done = True + + if r.match (name): + g = r.match (name).groups() + name = g[0] + done = False + + return name + + def __parse_all_par2 (self): + """Searches though self.all_p2files and tries to parse at least one of them""" + done = False + files = [] + + for f in self.all_p2files: + + # Exit early if we've found a good file + if done: + break + + try: + files = Par2Parser.get_protected_files (self.dir, f) + done = True + except (EnvironmentError, OSError, OverflowError): + logger.addMessage ('Corrupt PAR2 file: %s' % f, RarslaveLogger.MessageType.Fatal) + + # Now that we're out of the loop, check if we really finished + if not done: + logger.addMessage ('All PAR2 files corrupt for: %s' % self.p2file, RarslaveLogger.MessageType.Fatal) + + # Return whatever we've got, empty or not + return files + + def __find_name_matches (self, dir, basename): + """Finds files which are likely to be part of the set corresponding + to $name in the directory $dir""" + + assert os.path.isdir (dir) + + ename = re.escape (basename) + regex = re.compile ('^%s.*$' % (ename, )) + + return [f for f in os.listdir (dir) if regex.match (f)] + + def __update_name_matches (self): + """Updates the self.name_matched_files variable with the most current information. + This should be called after the directory contents are likely to change.""" + + self.name_matched_files = self.__find_name_matches (self.dir, self.basename) + + def __is_joinfile (self, filename): + regex = re.compile ('^.*\.\d\d\d$', re.IGNORECASE) + if regex.match (filename): + return True + + return False + + def __should_be_joined (self, files): + for f in files: + if self.__is_joinfile (f): + return True + + def runCheckAndRepair (self): + PAR2_CMD = config.get_value ('commands', 'par2repair') # Get set up - basename = get_basename (self.file) - all_files = find_likely_files (basename, self.dir) - all_files.sort () - par2_files = find_par2_files (all_files) + all_files = no_duplicates (self.name_matched_files + self.prot_matched_files) + join = self.__should_be_joined (all_files) # assemble the command - command = "%s \"%s\" " % (PAR2_CMD, self.file) + # par2repair -- PAR2 PAR2_EXTRA [JOIN_FILES] + command = "%s \"%s\" " % (PAR2_CMD, self.p2file) - for f in par2_files: - if f != self.file: - command += "\"%s\" " % get_filename(f) + for f in self.all_p2files: + if f != self.p2file: + command += "\"%s\" " % os.path.split (f)[1] - if self.join: + # Only needed when using par2 to join + if join: for f in all_files: - if f not in par2_files: - command += "\"%s\" " % get_filename(f) + if self.__is_joinfile (f): + command += "\"%s\" " % os.path.split (f)[1] # run the command ret = run_command (command, self.dir) # check the result if ret != 0: - # TODO: logger - print 'error during checkAndRepair()' + logger.addMessage ('PAR2 Check / Repair failed: %s' % self.p2file, RarslaveLogger.MessageType.Fatal) return -ECHECK -def run_command (cmd, indir=None): - # Runs the specified command-line in the directory given (or, in the current directory - # if none is given). It returns the status code given by the application. + return SUCCESS - pwd = os.getcwd () + def __find_deleteable_files (self): + all_files = no_duplicates (self.name_matched_files + self.prot_matched_files) + DELETE_REGEX = config.get_value ('regular expressions', 'delete_regex') + dregex = re.compile (DELETE_REGEX, re.IGNORECASE) - if indir != None: - assert os.path.isdir (indir) # MUST be a directory! - os.chdir (indir) + return [f for f in all_files if dregex.match (f)] - # FIXME: re-enable this after testing - print 'RUNNING (%s): %s' % (indir, cmd) - return 0 + def __delete_list_of_files (self, dir, files, interactive=False): + # Delete a list of files - # return os.system (cmd) + assert os.path.isdir (dir) + done = False + valid_y = ['Y', 'YES'] + valid_n = ['N', 'NO', ''] -def full_abspath (p): - return os.path.abspath (os.path.expanduser (p)) + if interactive: + while not done: + print 'Do you want to delete the following?:' + for f in files: + print f + s = raw_input ('Delete [y/N]: ').upper() -def get_filename (f): - # TODO: I don't think that we should enforce this... - # TODO: ... because I think we should be able to get the filename, regardless - # TODO: of whether this is a legit filename RIGHT NOW or not. - # assert os.path.isfile (f) - return os.path.split (f)[1] + if s in valid_y + valid_n: + done = True -def get_basename (name): - """Strips most kinds of endings from a filename""" + if s in valid_n: + return SUCCESS - regex = '^(.+)\.(par2|vol\d+\+\d+|\d\d\d|part\d+|rar|zip|avi|mp4|mkv|ogm)$' - r = re.compile (regex, re.IGNORECASE) - done = False + for f in files: + try: + os.remove (os.path.join (dir, f)) + logger.addMessage ('Deleteing: %s' % os.path.join (dir, f), RarslaveLogger.MessageType.Debug) + except: + logger.addMessage ('Failed to delete: %s' % os.path.join (dir, f), + RarslaveLogger.MessageType.Fatal) + return -EDELETE - while not done: - done = True + return SUCCESS - if r.match (name): - g = r.match (name).groups() - name = g[0] - done = False + def runDelete (self): + deleteable_files = self.__find_deleteable_files () + ret = self.__delete_list_of_files (self.dir, deleteable_files, options.interactive) - return name + return ret -def find_likely_files (name, dir): - """Finds files which are likely to be part of the set corresponding - to $name in the directory $dir""" + def run_all (self): + all_files = no_duplicates (self.name_matched_files + self.prot_matched_files) - if not os.path.isdir (os.path.abspath (dir)): - raise ValueError # bad directory given + # Repair Stage + ret = self.runCheckAndRepair () - dir = os.path.abspath (dir) - ename = re.escape (name) - regex = re.compile ('^%s.*$' % (ename, )) + if ret != SUCCESS: + logger.addMessage ('Repair stage failed for: %s' % self.p2file, RarslaveLogger.MessageType.Fatal) + return -ECHECK + + self.__update_name_matches () + all_files = no_duplicates (self.name_matched_files + self.prot_matched_files) + + # Extraction Stage + extractor = RarslaveExtractor (self.dir, self.all_p2files, \ + self.name_matched_files, self.prot_matched_files) + ret = extractor.runExtract (options.extract_dir) - return [f for f in os.listdir (dir) if regex.match (f)] + if ret != SUCCESS: + logger.addMessage ('Extraction stage failed for: %s' % self.p2file, RarslaveLogger.MessageType.Fatal) + return -EEXTRACT + + self.__update_name_matches () + all_files = no_duplicates (self.name_matched_files + self.prot_matched_files) + + # Deletion Stage + ret = self.runDelete () + + if ret != SUCCESS: + logger.addMessage ('Deletion stage failed for: %s' % self.p2file, RarslaveLogger.MessageType.Fatal) + return -EDELETE + + logger.addMessage ('Successfully completed: %s' % self.p2file) + return SUCCESS + +def run_command (cmd, indir=None): + # Runs the specified command-line in the directory given (or, in the current directory + # if none is given). It returns the status code given by the application. + + pwd = os.getcwd () + + if indir != None: + assert os.path.isdir (indir) # MUST be a directory! + os.chdir (indir) + + print 'RUNNING (%s): %s' % (indir, cmd) + ret = os.system (cmd) + os.chdir (pwd) + return ret + +def full_abspath (p): + return os.path.abspath (os.path.expanduser (p)) def find_par2_files (files): """Find all par2 files in the list $files""" - regex = re.compile ('^.*\.par2$', re.IGNORECASE) + PAR2_REGEX = config.get_value ('regular expressions', 'par2_regex') + regex = re.compile (PAR2_REGEX, re.IGNORECASE) return [f for f in files if regex.match (f)] def find_all_par2_files (dir): @@ -204,215 +482,197 @@ def find_all_par2_files (dir): return find_par2_files (files) -def has_extension (f, ext): - """Checks if f has the extension ext""" +def no_duplicates (li): + """Removes all duplicates from a list""" + return list(set(li)) - if ext[0] != '.': - ext = '.' + ext - - ext = re.escape (ext) - regex = re.compile ('^.*%s$' % (ext, ), re.IGNORECASE) - return regex.match (f) - -def find_extraction_heads (dir, files): - """Takes a list of possible files and finds likely heads of - extraction.""" +def generate_all_parsets (dir): + # Generate all parsets in the given directory. - # NOTE: perhaps this should happen AFTER repair is - # NOTE: successful. That way all files would already exist + assert os.path.isdir (dir) # Directory MUST be valid - # According to various sources online: - # 1) pre rar-3.0: .rar .r00 .r01 ... - # 2) post rar-3.0: .part01.rar .part02.rar - # 3) zip all ver: .zip + parsets = [] + p2files = find_all_par2_files (dir) - extractor = None - p2files = find_par2_files (files) + for f in p2files: + p = PAR2Set (dir, f) + if p not in parsets: + parsets.append (p) - # Old RAR type, find all files ending in .rar - if is_oldrar (files): - extractor = RarslaveExtractor (TYPE_OLDRAR) - regex = re.compile ('^.*\.rar$', re.IGNORECASE) - for f in files: - if regex.match (f): - extractor.addHead (dir, f) + return parsets - if is_newrar (files): - extractor = RarslaveExtractor (TYPE_NEWRAR) - regex = re.compile ('^.*\.part01.rar$', re.IGNORECASE) - for f in files: - if regex.match (f): - extractor.addHead (dir, f) +def check_required_progs(): + """Check if the required programs are installed""" - if is_zip (files): - extractor = RarslaveExtractor (TYPE_ZIP) - regex = re.compile ('^.*\.zip$', re.IGNORECASE) - for f in files: - if regex.match (f): - extractor.addHead (dir, f) + shell_not_found = 32512 + needed = [] - if is_noextract (files): - # Use the Par2 Parser (from cfv) here to find out what files are protected. - # Since these are not being extracted, they will be mv'd to another directory - # later. - extractor = RarslaveExtractor (TYPE_NOEXTRACT) + if run_command ('par2repair --help > /dev/null 2>&1') == shell_not_found: + needed.append ('par2repair') - for f in p2files: - done = False - try: - prot_files = par2parser.get_protected_files (dir, f) - done = True - except: #FIXME: add the actual exceptions - print 'ERROR PARSING P2FILE ...', f - continue + if run_command ('unrar --help > /dev/null 2>&1') == shell_not_found: + needed.append ('unrar') - if done: - break + if run_command ('unzip --help > /dev/null 2>&1') == shell_not_found: + needed.append ('unzip') - if done: - for f in prot_files: - extractor.addHead (dir, f) - else: - print 'BADNESS' + if needed: + for n in needed: + print 'Needed program "%s" not found in $PATH' % (n, ) - # Make sure we found the type - assert extractor != None + sys.exit(1) - return extractor +def run_options (options): -def is_oldrar (files): - for f in files: - if has_extension (f, '.r00'): - return True + # Fix directories + options.work_dir = full_abspath (options.work_dir) -def is_newrar (files): - for f in files: - if has_extension (f, '.part01.rar'): - return True + # Make sure that the directory is valid + if not os.path.isdir (options.work_dir): + sys.stderr.write ('\"%s\" is not a valid directory. Use the \"-d\"\n' % options.work_dir) + sys.stderr.write ('option to override the working directory temporarily, or edit the\n') + sys.stderr.write ('configuration file to override the working directory permanently.\n') + sys.exit (1) -def is_zip (files): - for f in files: - if has_extension (f, '.zip'): - return True + if options.extract_dir != None: + options.extract_dir = full_abspath (options.extract_dir) -def is_noextract (files): - # Type that needs no extraction. - # TODO: Add others ??? - for f in files: - if has_extension (f, '.001'): - return True + if options.version: + print PROGRAM + ' - ' + VERSION + print + print 'Copyright (c) 2005,2006 Ira W. Snyder (devel@irasnyder.com)' + print + print 'This program comes with ABSOLUTELY NO WARRANTY.' + print 'This is free software, and you are welcome to redistribute it' + print 'under certain conditions. See the file COPYING for details.' + sys.exit (0) -def find_deleteable_files (files): - # Deleteable types regex should come from the config - dfiles = [] - dregex = re.compile ('^.*\.(par2|\d|\d\d\d|rar|r\d\d|zip)$', re.IGNORECASE) + if options.check_progs: + check_required_progs () - return [f for f in files if dregex.match (f)] + if options.write_def_config: + config.write_config (default=True) -def printlist (li): - for f in li: - print f + if options.write_config: + config.write_config () -class PAR2Set (object): +def find_loglevel (options): - dir = None - file = None - likely_files = [] + loglevel = options.verbose - options.quiet - def __init__ (self, dir, file): - assert os.path.isdir (dir) - assert os.path.isfile (os.path.join (dir, file)) + if loglevel < RarslaveLogger.MessageType.Fatal: + loglevel = RarslaveLogger.MessageType.Fatal - self.dir = dir - self.file = file + if loglevel > RarslaveLogger.MessageType.Debug: + loglevel = RarslaveLogger.MessageType.Debug - basename = get_basename (file) - self.likely_files = find_likely_files (basename, dir) + return loglevel - def __list_eq (self, l1, l2): +def printMessageTable (loglevel): - if len(l1) != len(l2): - return False + if logger.hasFatalMessages (): + print '\nFatal Messages\n' + '=' * 80 + logger.printLoglevel (RarslaveLogger.MessageType.Fatal) - for e in l1: - if e not in l2: - return False + if loglevel == RarslaveLogger.MessageType.Fatal: + return - return True + if logger.hasNormalMessages (): + print '\nNormal Messages\n' + '=' * 80 + logger.printLoglevel (RarslaveLogger.MessageType.Normal) - def __eq__ (self, rhs): - return self.__list_eq (self.likely_files, rhs.likely_files) + if loglevel == RarslaveLogger.MessageType.Normal: + return - def run_all (self): - par2files = find_par2_files (self.likely_files) - par2head = par2files[0] + if logger.hasVerboseMessages (): + print '\nVerbose Messages\n' + '=' * 80 + logger.printLoglevel (RarslaveLogger.MessageType.Verbose) - join = is_noextract (self.likely_files) + if loglevel == RarslaveLogger.MessageType.Verbose: + return - # Repair Stage - repairer = RarslaveRepairer (self.dir, par2head, join) - ret = repairer.checkAndRepair () # FIXME: Check return value + if logger.hasDebugMessages (): + print '\nDebug Messages\n' + '=' * 80 + logger.printLoglevel (RarslaveLogger.MessageType.Debug) - if ret: # FAILURE - return -ECHECK + return - # Extraction Stage - extractor = find_extraction_heads (self.dir, self.likely_files) - ret = extractor.extract ('extract_dir') # FIXME: Get it from the config +def main (): - if ret: # FAILURE - return -EEXTRACT + # Build the OptionParser + parser = optparse.OptionParser() + parser.add_option('-n', '--not-recursive', + action='store_false', dest='recursive', + default=config.get_value('options', 'recursive'), + help="Don't run recursively") - # Deletion Stage - deleteable_files = find_deleteable_files (self.likely_files) - ret = delete_list (deleteable_files) + parser.add_option('-d', '--work-dir', + dest='work_dir', type='string', + default=config.get_value('directories', 'working_directory'), + help="Start running at DIR", metavar='DIR') - if ret: # FAILURE - return -EDELETE + parser.add_option('-e', '--extract-dir', + dest='extract_dir', type='string', + default=config.get_value('directories', 'extract_directory'), + help="Extract to DIR", metavar='DIR') - return 0 + parser.add_option('-p', '--check-required-programs', + action='store_true', dest='check_progs', + default=False, + help="Check for required programs") -def delete_list (files, interactive=False): - # Delete a list of files - # TODO: Add the ability to confirm deletion, like in the original rarslave + parser.add_option('-f', '--write-default-config', + action='store_true', dest='write_def_config', + default=False, help="Write out a new default config") - if interactive: - # TODO: prompt here - # prompt -> OK_TO_DELETE -> do nothing, fall through - # prompt -> NOT_OK -> return immediately - pass + parser.add_option('-c', '--write-new-config', + action='store_true', dest='write_config', + default=False, help="Write out the current config") - for f in files: - # FIXME: re-enable this in production - # os.remove (f) - print 'rm', f + parser.add_option('-i', '--interactive', dest='interactive', action='store_true', + default=config.get_value('options', 'interactive'), + help="Confirm before removing files") - return 0 + parser.add_option('-q', '--quiet', dest='quiet', action='count', + default=0, help="Output fatal messages only") + parser.add_option('-v', '--verbose', dest='verbose', action='count', + default=0, help="Output extra information") -def generate_all_parsets (dir): - # Generate all parsets in the given directory. + parser.add_option('-V', '--version', dest='version', action='store_true', + default=False, help="Output version information") - assert os.path.isdir (dir) # Directory MUST be valid + parser.version = VERSION - parsets = [] - p2files = find_all_par2_files (dir) + # Parse the given options + global options + (options, args) = parser.parse_args() - for f in p2files: - p = PAR2Set (dir, f) - if p not in parsets: - parsets.append (p) + # Run any special actions that are needed on these options + run_options (options) - return parsets + # Find the loglevel using the options given + loglevel = find_loglevel (options) -def main (): - TOPDIR = os.path.abspath ('test_material') + # Run recursively + if options.recursive: + for (dir, subdirs, files) in os.walk (options.work_dir): + parsets = generate_all_parsets (dir) + for p in parsets: + p.run_all () - for (dir, subdirs, files) in os.walk (TOPDIR): - print 'DEBUG: IN DIRECTORY:', dir - parsets = generate_all_parsets (dir) + # Non-recursive + else: + parsets = generate_all_parsets (options.work_dir) for p in parsets: p.run_all () + # Print the results + printMessageTable (loglevel) + + # Done! + return 0 + if __name__ == '__main__': main () +