1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17  """ 
 18  Command line interface 
 19  ====================== 
 20   
 21  The svnmailer provides two different command line interfaces. On the one hand 
 22  there's the compatibility command line to the mailer.py script, which has some 
 23  limitations and problems because of its unflexibility. On the other hand 
 24  you'll find the new-style command line, which contains no subcommands and 
 25  fixed parameters at all. 
 26   
 27  The CLI simply transforms old-style command lines to the new format internally 
 28  and processes these further using the optparse module:: 
 29   
 30      svn-mailer commit <rep> <rev> [<config>] 
 31   -> svn-mailer --commit --repository <rep> --revision <rev> 
 32                [--config <config>] 
 33   
 34      svn-mailer propchange <rep> <rev> <author> <prop> [<conf>] 
 35   -> svn-mailer --propchange --repository <rep> --revision <rev> 
 36                 --author <author> --propname <prop> 
 37                [--config <conf>] 
 38   
 39      # (useful with svn 1.2 and later) 
 40      svn-mailer propchange2 <rep> <rev> <author> <prop> <action> [<conf>] 
 41   -> svn-mailer --propchange --repository <rep> --revision <rev> 
 42                 --author <author> --propname <prop> --action <action> 
 43                [--config <conf>] 
 44   
 45      svn-mailer lock <rep> <author> [<conf>] 
 46   -> svn-mailer --lock --repository <rep> --author <author> 
 47                [--config <conf>] 
 48   
 49      svn-mailer unlock <rep> <author> [<conf>] 
 50   -> svn-mailer --unlock --repository <rep> --author <author> 
 51                [--config <conf>] 
 52  """ 
 53  __author__    = "André Malo" 
 54  __docformat__ = "epytext en" 
 55  __all__       = ['getOptions', 'CommandlineError'] 
 56   
 57   
 58  import optparse 
 59   
 60   
 62      """ Base exception for this module """ 
 63      pass 
  64   
 66      """ Error in commandline """ 
 67      pass 
  68   
 69   
 71      """ Parse commandline options 
 72   
 73          @param argv: Command line list. If argv is None, 
 74              sys.argv[1:] is evaluated instead 
 75          @type argv: C{list} 
 76   
 77          @return: option object 
 78          @rtype: C{optparse.OptionParser} 
 79   
 80          @exception CommandlineError: Error in command line options 
 81      """ 
 82      from svnmailer import util 
 83      from svnmailer.settings import modes 
 84   
 85      usage = """%prog <options>""" 
 86      parser = SvnmailerOptionParser(usage = usage, version = True) 
 87      options, args = parser.parse_args(argv) 
 88   
 89       
 90      if args: 
 91          raise CommandlineError("Too much arguments") 
 92   
 93      if not options.repository: 
 94          raise CommandlineError("Missing repository path") 
 95   
 96      if not options.revision: 
 97          if options.mode in (modes.commit, modes.propchange): 
 98              raise CommandlineError("Missing revision number") 
 99   
100      if not options.author: 
101          if options.mode in (modes.propchange, modes.lock, modes.unlock): 
102              raise CommandlineError("Missing author parameter") 
103   
104      if not options.propname: 
105          if options.mode == modes.propchange: 
106              raise CommandlineError("Missing property name parameter") 
107   
108       
109      try: 
110          options.repository = util.filename.fromLocale( 
111              options.repository, options.path_encoding 
112          ) 
113      except UnicodeError, exc: 
114          raise CommandlineError("--repository recode problem: %s" % str(exc)) 
115   
116      if options.config: 
117          try: 
118              options.config = util.filename.fromLocale( 
119                  options.config, options.path_encoding 
120              ) 
121          except UnicodeError, exc: 
122              raise CommandlineError("--config recode problem: %s" % str(exc)) 
123   
124      return options 
 125   
126   
128      """ Fully initialized option parser 
129   
130          @ivar _svn: The svn version 
131          @type _svn: C{tuple} 
132      """ 
133   
141   
142   
143 -    def parse_args(self, args = None, *other_args, **kwargs): 
 144          """ Accepts also the old command line """ 
145          args = self._transformSvnmailerOldStyle(args) 
146          if not args: 
147              raise CommandlineError( 
148                  "Type '%s --help' for usage" % self.get_prog_name() 
149              ) 
150   
151          options, fixed = optparse.OptionParser.parse_args( 
152              self, args, *other_args, **kwargs 
153          ) 
154   
155           
156          if not self._svn.min_1_2: 
157              options.action = None 
158   
159          return (options, fixed) 
 160   
161   
163          """ We raise an exception instead of exiting 
164   
165              @param msg: The error message 
166              @type msg: C{str} 
167   
168              @exception CommandlineError: command line error 
169          """ 
170          raise CommandlineError(str(msg)) 
 171   
172   
174          """ Returns the version string """ 
175          from svnmailer import version 
176   
177          return "svnmailer-%s\nwith svn %d.%d.%d%s" % ( 
178              version, self._svn.major, self._svn.minor, self._svn.patch, 
179              self._svn.tag 
180          ) 
 181   
182   
184          """ Returns the program name """ 
185          try: 
186               
187              return optparse.OptionParser.get_prog_name(self) 
188          except AttributeError: 
189              try: 
190                   
191                  return optparse.OptionParser._get_prog_name(self) 
192              except AttributeError: 
193                   
194                  if self.prog: 
195                      return self.prog 
196                  else: 
197                      import os, sys 
198                      return os.path.basename(sys.argv[0]) 
 199   
200   
246   
247   
249          """ Returns terminal width if determined, None otherwise 
250   
251              @return: The width 
252              @rtype: C{int} 
253          """ 
254          try: 
255              import errno, fcntl, struct, sys, termios 
256   
257              def getwidth(fd): 
258                  """ Returns the width for descriptor fd """ 
259                   
260                   
261                   
262                   
263                   
264                   
265                  return struct.unpack("4H", fcntl.ioctl( 
266                      fd, termios.TIOCGWINSZ, struct.pack("4H", 0, 0, 0, 0) 
267                  ))[1] 
 268   
269              try: 
270                  return getwidth(sys.stdout.fileno()) 
271              except IOError, exc: 
272                  if exc[0] == errno.EINVAL: 
273                      return getwidth(sys.stdin.fileno()) 
274                  raise  
275   
276          except (SystemExit, KeyboardInterrupt): 
277              raise 
278   
279          except: 
280               
281              pass 
282   
283          return None 
 284   
285   
291   
292   
294          """ Adds the common options group """ 
295          common_options = optparse.OptionGroup( 
296              self, 'COMMON PARAMETERS' 
297          ) 
298          common_options.add_option('--debug', 
299              action = 'store_true', 
300              default = False, 
301              help = "Run in debug mode (means basically that all messages " 
302                  "are sent to STDOUT)", 
303          ) 
304          common_options.add_option('-d', '--repository', 
305              help = 'The repository directory', 
306          ) 
307          common_options.add_option('-f', '--config', 
308              help = 'The configuration file', 
309          ) 
310          common_options.add_option('-e', '--path-encoding', 
311              help = 'Specifies the character encoding to be used for ' 
312                  'filenames. By default the encoding is tried to be ' 
313                  'determined automatically depending on the locale.' 
314          ) 
315          self.add_option_group(common_options) 
 316   
317   
319          """ Adds the behaviour options group """ 
320          from svnmailer.settings import modes 
321   
322          behaviour_options = optparse.OptionGroup( 
323              self, 'BEHAVIOUR OPTIONS', 
324              description = "The behaviour options are mutually exclusive, " 
325                  "i.e. the last one wins." 
326          ) 
327          behaviour_options.add_option('-c', '--commit', 
328              dest = 'mode', 
329              action = 'store_const', 
330              const = modes.commit, 
331              default = modes.commit, 
332              help = 'This is a regular commit of versioned data ' 
333                     '(post-commit hook). This is default.', 
334          ) 
335          behaviour_options.add_option('-p', '--propchange', 
336              dest = 'mode', 
337              action = 'store_const', 
338              const = modes.propchange, 
339              help = 'This is a modification of unversioned properties ' 
340                  '(post-revprop-change hook)', 
341          ) 
342   
343          if self._svn.min_1_2: 
344              behaviour_options.add_option('-l', '--lock', 
345                  dest = 'mode', 
346                  action = 'store_const', 
347                  const = modes.lock, 
348                  help = '(svn 1.2 and later) This is a locking call ' 
349                      '(post-lock hook)', 
350              ) 
351              behaviour_options.add_option('-u', '--unlock', 
352                  dest = 'mode', 
353                  action = 'store_const', 
354                  const = modes.unlock, 
355                  help = '(svn 1.2 and later) This is a unlocking call ' 
356                      '(post-unlock hook)', 
357              ) 
358   
359          self.add_option_group(behaviour_options) 
 360   
361   
363          """ Adds the supplemental options """ 
364          supp_options = optparse.OptionGroup( 
365              self, 'SUPPLEMENTAL PARAMETERS' 
366          ) 
367           
368          supp_options.add_option('-r', '--revision', 
369              action = 'store', 
370              type = 'int', 
371              help = 'The modified/committed revision number', 
372          ) 
373          supp_options.add_option('-a', '--author', 
374              help = 'The author of the modification', 
375          ) 
376          supp_options.add_option('-n', '--propname', 
377              help = 'The name of the modified property', 
378          ) 
379   
380          if self._svn.min_1_2: 
381              supp_options.add_option('-o', '--action', 
382                  help = '(svn 1.2 and later) The property change action', 
383              ) 
384   
385          self.add_option_group(supp_options) 
 386   
387   
449