Major Projectwide Restructuring
[rarslave2.git] / rarslave.py
1 #!/usr/bin/env python
2 # vim: set ts=4 sts=4 sw=4 textwidth=112 :
3
4 VERSION="2.0.0"
5 PROGRAM="rarslave2"
6
7 import os, sys, optparse
8 import RarslaveLogger
9 import RarslaveDetector
10 import RarslaveGlobals
11 from RarslaveCommon import *
12
13 # Global options from the RarslaveGlobals class
14 options = RarslaveGlobals.options
15 config = RarslaveGlobals.config
16 logger = RarslaveGlobals.logger
17
18 # A tiny class used to find unique PAR2 sets
19 class CompareSet (object):
20
21         def __init__ (self, dir, p2file):
22                 self.dir = dir
23                 self.p2file = p2file
24
25                 self.basename = get_basename (self.p2file)
26                 self.name_matches = find_name_matches (self.dir, self.basename)
27
28         def __eq__ (self, rhs):
29                 return (self.dir == rhs.dir) \
30                                 and (self.basename == rhs.basename) \
31                                 and list_eq (self.name_matches, rhs.name_matches)
32
33
34 def find_all_par2_files (dir):
35         """Finds all par2 files in a directory"""
36         # NOTE: does NOT return absolute paths
37
38         if not os.path.isdir (os.path.abspath (dir)):
39                 raise ValueError # bad directory given
40
41         dir = os.path.abspath (dir)
42         files = os.listdir (dir)
43
44         return find_par2_files (files)
45
46 def generate_all_parsets (dir):
47         # Generate all parsets in the given directory.
48
49         assert os.path.isdir (dir) # Directory MUST be valid
50
51         parsets = []
52         p2files = find_all_par2_files (dir)
53
54         for f in p2files:
55                 p = CompareSet (dir, f)
56                 if p not in parsets:
57                         parsets.append (p)
58
59         return [(p.dir, p.p2file) for p in parsets]
60
61 def check_required_progs():
62         """Check if the required programs are installed"""
63
64         shell_not_found = 32512
65         needed = []
66
67         if run_command ('par2repair --help > /dev/null 2>&1') == shell_not_found:
68                 needed.append ('par2repair')
69
70         if run_command ('unrar --help > /dev/null 2>&1') == shell_not_found:
71                 needed.append ('unrar')
72
73         if run_command ('unzip --help > /dev/null 2>&1') == shell_not_found:
74                 needed.append ('unzip')
75
76         if needed:
77                 for n in needed:
78                         print 'Needed program "%s" not found in $PATH' % (n, )
79
80                 sys.exit(1)
81
82 def run_options (options):
83
84         # Fix directories
85         options.work_dir = full_abspath (options.work_dir)
86
87         # Make sure that the directory is valid
88         if not os.path.isdir (options.work_dir):
89                 sys.stderr.write ('\"%s\" is not a valid directory. Use the \"-d\"\n' % options.work_dir)
90                 sys.stderr.write ('option to override the working directory temporarily, or edit the\n')
91                 sys.stderr.write ('configuration file to override the working directory permanently.\n')
92                 sys.exit (1)
93
94         if options.extract_dir != None:
95                 options.extract_dir = full_abspath (options.extract_dir)
96
97         if options.version:
98                 print PROGRAM + ' - ' + VERSION
99                 print
100                 print 'Copyright (c) 2005,2006 Ira W. Snyder (devel@irasnyder.com)'
101                 print
102                 print 'This program comes with ABSOLUTELY NO WARRANTY.'
103                 print 'This is free software, and you are welcome to redistribute it'
104                 print 'under certain conditions. See the file COPYING for details.'
105                 sys.exit (0)
106
107         if options.check_progs:
108                 check_required_progs ()
109
110         if options.write_def_config:
111                 config.write_config (default=True)
112
113         if options.write_config:
114                 config.write_config ()
115
116 def find_loglevel (options):
117
118         loglevel = options.verbose - options.quiet
119
120         if loglevel < RarslaveLogger.MessageType.Fatal:
121                 loglevel = RarslaveLogger.MessageType.Fatal
122
123         if loglevel > RarslaveLogger.MessageType.Debug:
124                 loglevel = RarslaveLogger.MessageType.Debug
125
126         return loglevel
127
128 def printMessageTable (loglevel):
129
130         if logger.hasFatalMessages ():
131                 print '\nFatal Messages\n' + '=' * 80
132                 logger.printLoglevel (RarslaveLogger.MessageType.Fatal)
133
134         if loglevel == RarslaveLogger.MessageType.Fatal:
135                 return
136
137         if logger.hasNormalMessages ():
138                 print '\nNormal Messages\n' + '=' * 80
139                 logger.printLoglevel (RarslaveLogger.MessageType.Normal)
140
141         if loglevel == RarslaveLogger.MessageType.Normal:
142                 return
143
144         if logger.hasVerboseMessages ():
145                 print '\nVerbose Messages\n' + '=' * 80
146                 logger.printLoglevel (RarslaveLogger.MessageType.Verbose)
147
148         if loglevel == RarslaveLogger.MessageType.Verbose:
149                 return
150
151         if logger.hasDebugMessages ():
152                 print '\nDebug Messages\n' + '=' * 80
153                 logger.printLoglevel (RarslaveLogger.MessageType.Debug)
154
155         return
156
157 def main ():
158
159         # Build the OptionParser
160         parser = optparse.OptionParser()
161         parser.add_option('-n', '--not-recursive',
162                                                 action='store_false', dest='recursive',
163                                                 default=config_get_value('options', 'recursive'),
164                                                 help="Don't run recursively")
165
166         parser.add_option('-d', '--work-dir',
167                                                 dest='work_dir', type='string',
168                                                 default=config_get_value('directories', 'working_directory'),
169                                                 help="Start running at DIR", metavar='DIR')
170
171         parser.add_option('-e', '--extract-dir',
172                                                 dest='extract_dir', type='string',
173                                                 default=config_get_value('directories', 'extract_directory'),
174                                                 help="Extract to DIR", metavar='DIR')
175
176         parser.add_option('-p', '--check-required-programs',
177                                                 action='store_true', dest='check_progs',
178                                                 default=False,
179                                                 help="Check for required programs")
180
181         parser.add_option('-f', '--write-default-config',
182                                                 action='store_true', dest='write_def_config',
183                                                 default=False, help="Write out a new default config")
184
185         parser.add_option('-c', '--write-new-config',
186                                                 action='store_true', dest='write_config',
187                                                 default=False, help="Write out the current config")
188
189         parser.add_option('-i', '--interactive', dest='interactive', action='store_true',
190                                                 default=config_get_value('options', 'interactive'),
191                                                 help="Confirm before removing files")
192
193         parser.add_option('-q', '--quiet', dest='quiet', action='count',
194                                                 default=0, help="Output fatal messages only")
195
196         parser.add_option('-v', '--verbose', dest='verbose', action='count',
197                                                 default=0, help="Output extra information")
198
199         parser.add_option('-V', '--version', dest='version', action='store_true',
200                                                 default=False, help="Output version information")
201
202         parser.version = VERSION
203
204         # Parse the given options
205         global options
206         (RarslaveGlobals.options, args) = parser.parse_args()
207         options = RarslaveGlobals.options
208
209         # Run any special actions that are needed on these options
210         run_options (options)
211
212         # Find the loglevel using the options given 
213         loglevel = find_loglevel (options)
214
215         # Run recursively
216         if options.recursive:
217                 for (dir, subdirs, files) in os.walk (options.work_dir):
218                         parsets = generate_all_parsets (dir)
219                         for (p2dir, p2file) in parsets:
220                                 detector = RarslaveDetector.RarslaveDetector (p2dir, p2file)
221                                 ret = detector.runMatchingTypes ()
222
223         # Non-recursive
224         else:
225                 parsets = generate_all_parsets (options.work_dir)
226                 for (p2dir, p2file) in parsets:
227                         detector = RarslaveDetector.RarslaveDetector (p2dir, p2file)
228                         ret = detector.runMatchingTypes ()
229
230         # Print the results
231         printMessageTable (loglevel)
232
233         # Done!
234         return 0
235
236 if __name__ == '__main__':
237         main ()
238