1 """Common utility functions
4 import errno, os, os.path, re, sys
5 from stgit.config import config
6 from stgit.out import *
9 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License version 2 as
13 published by the Free Software Foundation.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 def mkdir_file(filename, mode):
26 """Opens filename with the given mode, creating the directory it's
27 in if it doesn't already exist."""
28 create_dirs(os.path.dirname(filename))
29 return file(filename, mode)
31 def read_strings(filename):
32 """Reads the lines from a file
34 f = file(filename, 'r')
35 lines = [line.strip() for line in f.readlines()]
39 def read_string(filename, multiline = False):
40 """Reads the first line from a file
42 f = file(filename, 'r')
46 result = f.readline().strip()
50 def write_strings(filename, lines):
51 """Write 'lines' sequence to file
53 f = file(filename, 'w+')
54 f.writelines([line + '\n' for line in lines])
57 def write_string(filename, line, multiline = False):
58 """Writes 'line' to file and truncates it
60 f = mkdir_file(filename, 'w+')
67 def append_strings(filename, lines):
68 """Appends 'lines' sequence to file
70 f = mkdir_file(filename, 'a+')
75 def append_string(filename, line):
76 """Appends 'line' to file
78 f = mkdir_file(filename, 'a+')
82 def insert_string(filename, line):
83 """Inserts 'line' at the beginning of the file
85 f = mkdir_file(filename, 'r+')
87 f.seek(0); f.truncate()
92 def create_empty_file(name):
93 """Creates an empty file
95 mkdir_file(name, 'w+').close()
97 def list_files_and_dirs(path):
98 """Return the sets of filenames and directory names in a
101 for fd in os.listdir(path):
102 full_fd = os.path.join(path, fd)
103 if os.path.isfile(full_fd):
105 elif os.path.isdir(full_fd):
109 def walk_tree(basedir):
110 """Starting in the given directory, iterate through all its
111 subdirectories. For each subdirectory, yield the name of the
112 subdirectory (relative to the base directory), the list of
113 filenames in the subdirectory, and the list of directory names in
117 subdir = subdirs.pop()
118 files, dirs = list_files_and_dirs(os.path.join(basedir, subdir))
120 subdirs.append(os.path.join(subdir, d))
121 yield subdir, files, dirs
123 def strip_prefix(prefix, string):
124 """Return string, without the prefix. Blow up if string doesn't
125 start with prefix."""
126 assert string.startswith(prefix)
127 return string[len(prefix):]
129 def strip_suffix(suffix, string):
130 """Return string, without the suffix. Blow up if string doesn't
132 assert string.endswith(suffix)
133 return string[:-len(suffix)]
135 def remove_file_and_dirs(basedir, file):
136 """Remove join(basedir, file), and then remove the directory it
137 was in if empty, and try the same with its parent, until we find a
138 nonempty directory or reach basedir."""
139 os.remove(os.path.join(basedir, file))
141 os.removedirs(os.path.join(basedir, os.path.dirname(file)))
143 # file's parent dir may not be empty after removal
146 def create_dirs(directory):
147 """Create the given directory, if the path doesn't already exist."""
148 if directory and not os.path.isdir(directory):
149 create_dirs(os.path.dirname(directory))
153 if e.errno != errno.EEXIST:
156 def rename(basedir, file1, file2):
157 """Rename join(basedir, file1) to join(basedir, file2), not
158 leaving any empty directories behind and creating any directories
160 full_file2 = os.path.join(basedir, file2)
161 create_dirs(os.path.dirname(full_file2))
162 os.rename(os.path.join(basedir, file1), full_file2)
164 os.removedirs(os.path.join(basedir, os.path.dirname(file1)))
166 # file1's parent dir may not be empty after move
169 class EditorException(Exception):
172 def call_editor(filename):
173 """Run the editor on the specified filename."""
176 editor = config.get('stgit.editor')
179 elif 'EDITOR' in os.environ:
180 editor = os.environ['EDITOR']
183 editor += ' %s' % filename
185 out.start('Invoking the editor: "%s"' % editor)
186 err = os.system(editor)
188 raise EditorException, 'editor failed, exit code: %d' % err
191 def patch_name_from_msg(msg):
192 """Return a string to be used as a patch name. This is generated
193 from the top line of the string passed as argument, and is at most
194 30 characters long."""
198 subject_line = msg.split('\n', 1)[0].lstrip().lower()
199 return re.sub('[\W]+', '-', subject_line).strip('-')[:30]
201 def make_patch_name(msg, unacceptable, default_name = 'patch'):
202 """Return a patch name generated from the given commit message,
203 guaranteed to make unacceptable(name) be false. If the commit
204 message is empty, base the name on default_name instead."""
205 patchname = patch_name_from_msg(msg)
207 patchname = default_name
208 if unacceptable(patchname):
210 while unacceptable('%s-%d' % (patchname, suffix)):
212 patchname = '%s-%d' % (patchname, suffix)