stgit.el: Set patch names to be word syntax throughout
[stgit] / stgit / utils.py
index 837c6c2..1fa96c2 100644 (file)
@@ -1,7 +1,7 @@
 """Common utility functions
 """
 
-import errno, optparse, os, os.path, re, sys
+import errno, os, os.path, re, sys
 from stgit.exception import *
 from stgit.config import config
 from stgit.out import *
@@ -170,32 +170,52 @@ def rename(basedir, file1, file2):
 class EditorException(StgException):
     pass
 
+def get_editor():
+    for editor in [os.environ.get('GIT_EDITOR'),
+                   config.get('stgit.editor'), # legacy
+                   config.get('core.editor'),
+                   os.environ.get('VISUAL'),
+                   os.environ.get('EDITOR'),
+                   'vi']:
+        if editor:
+            return editor
+
 def call_editor(filename):
     """Run the editor on the specified filename."""
-
-    # the editor
-    editor = config.get('stgit.editor')
-    if editor:
-        pass
-    elif 'EDITOR' in os.environ:
-        editor = os.environ['EDITOR']
-    else:
-        editor = 'vi'
-    editor += ' %s' % filename
-
-    out.start('Invoking the editor: "%s"' % editor)
-    err = os.system(editor)
+    cmd = '%s %s' % (get_editor(), filename)
+    out.start('Invoking the editor: "%s"' % cmd)
+    err = os.system(cmd)
     if err:
         raise EditorException, 'editor failed, exit code: %d' % err
     out.done()
 
+def edit_string(s, filename):
+    f = file(filename, 'w')
+    f.write(s)
+    f.close()
+    call_editor(filename)
+    f = file(filename)
+    s = f.read()
+    f.close()
+    os.remove(filename)
+    return s
+
+def find_patch_name(patchname, unacceptable):
+    """Find a patch name which is acceptable."""
+    if unacceptable(patchname):
+        suffix = 0
+        while unacceptable('%s-%d' % (patchname, suffix)):
+            suffix += 1
+        patchname = '%s-%d' % (patchname, suffix)
+    return patchname
+
 def patch_name_from_msg(msg):
     """Return a string to be used as a patch name. This is generated
     from the top line of the string passed as argument."""
     if not msg:
         return None
 
-    name_len = config.get('stgit.namelength')
+    name_len = config.getint('stgit.namelength')
     if not name_len:
         name_len = 30
 
@@ -209,12 +229,7 @@ def make_patch_name(msg, unacceptable, default_name = 'patch'):
     patchname = patch_name_from_msg(msg)
     if not patchname:
         patchname = default_name
-    if unacceptable(patchname):
-        suffix = 0
-        while unacceptable('%s-%d' % (patchname, suffix)):
-            suffix += 1
-        patchname = '%s-%d' % (patchname, suffix)
-    return patchname
+    return find_patch_name(patchname, unacceptable)
 
 # any and all functions are builtin in Python 2.5 and higher, but not
 # in 2.4.
@@ -231,21 +246,6 @@ if not 'all' in dir(__builtins__):
                 return False
         return True
 
-def make_sign_options():
-    def callback(option, opt_str, value, parser, sign_str):
-        if parser.values.sign_str not in [None, sign_str]:
-            raise optparse.OptionValueError(
-                '--ack and --sign were both specified')
-        parser.values.sign_str = sign_str
-    return [optparse.make_option('--sign', action = 'callback',
-                                 callback = callback, dest = 'sign_str',
-                                 callback_args = ('Signed-off-by',),
-                                 help = 'add Signed-off-by line'),
-            optparse.make_option('--ack', action = 'callback',
-                                 callback = callback, dest = 'sign_str',
-                                 callback_args = ('Acked-by',),
-                                 help = 'add Acked-by line')]
-
 def add_sign_line(desc, sign_str, name, email):
     if not sign_str:
         return desc
@@ -257,47 +257,37 @@ def add_sign_line(desc, sign_str, name, email):
         desc = desc + '\n'
     return '%s\n%s\n' % (desc, sign_str)
 
-def make_message_options():
-    def no_dup(parser):
-        if parser.values.message != None:
-            raise optparse.OptionValueError(
-                'Cannot give more than one --message or --file')
-    def no_combine(parser):
-        if (parser.values.message != None
-            and parser.values.save_template != None):
-            raise optparse.OptionValueError(
-                'Cannot give both --message/--file and --save-template')
-    def msg_callback(option, opt_str, value, parser):
-        no_dup(parser)
-        parser.values.message = value
-        no_combine(parser)
-    def file_callback(option, opt_str, value, parser):
-        no_dup(parser)
-        if value == '-':
-            parser.values.message = sys.stdin.read()
-        else:
-            f = file(value)
-            parser.values.message = f.read()
-            f.close()
-        no_combine(parser)
-    def templ_callback(option, opt_str, value, parser):
-        if value == '-':
-            def w(s):
-                sys.stdout.write(s)
-        else:
-            def w(s):
-                f = file(value, 'w+')
-                f.write(s)
-                f.close()
-        parser.values.save_template = w
-        no_combine(parser)
-    m = optparse.make_option
-    return [m('-m', '--message', action = 'callback', callback = msg_callback,
-              dest = 'message', type = 'string',
-              help = 'use MESSAGE instead of invoking the editor'),
-            m('-f', '--file', action = 'callback', callback = file_callback,
-              dest = 'message', type = 'string', metavar = 'FILE',
-              help = 'use FILE instead of invoking the editor'),
-            m('--save-template', action = 'callback', callback = templ_callback,
-              metavar = 'FILE', dest = 'save_template', type = 'string',
-              help = 'save the message template to FILE and exit')]
+def parse_name_email(address):
+    """Return a tuple consisting of the name and email parsed from a
+    standard 'name <email>' or 'email (name)' string."""
+    address = re.sub(r'[\\"]', r'\\\g<0>', address)
+    str_list = re.findall(r'^(.*)\s*<(.*)>\s*$', address)
+    if not str_list:
+        str_list = re.findall(r'^(.*)\s*\((.*)\)\s*$', address)
+        if not str_list:
+            return None
+        return (str_list[0][1], str_list[0][0])
+    return str_list[0]
+
+def parse_name_email_date(address):
+    """Return a tuple consisting of the name, email and date parsed
+    from a 'name <email> date' string."""
+    address = re.sub(r'[\\"]', r'\\\g<0>', address)
+    str_list = re.findall('^(.*)\s*<(.*)>\s*(.*)\s*$', address)
+    if not str_list:
+        return None
+    return str_list[0]
+
+# Exit codes.
+STGIT_SUCCESS = 0        # everything's OK
+STGIT_GENERAL_ERROR = 1  # seems to be non-command-specific error
+STGIT_COMMAND_ERROR = 2  # seems to be a command that failed
+STGIT_CONFLICT = 3       # merge conflict, otherwise OK
+STGIT_BUG_ERROR = 4      # a bug in StGit
+
+def add_dict(d1, d2):
+    """Return a new dict with the contents of both d1 and d2. In case of
+    conflicting mappings, d2 takes precedence."""
+    d = dict(d1)
+    d.update(d2)
+    return d