##// END OF EJS Templates
osutil.c: Support for py3k added....
Renato Cunha -
r11359:4eaacccb default
parent child Browse files
Show More
@@ -1,534 +1,559
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 <fcntl.h>
13 13 #include <stdio.h>
14 14 #include <string.h>
15 15
16 16 #ifdef _WIN32
17 17 #include <windows.h>
18 18 #include <io.h>
19 19 #else
20 20 #include <dirent.h>
21 21 #include <sys/stat.h>
22 22 #include <sys/types.h>
23 23 #include <unistd.h>
24 24 #endif
25 25
26 #include "util.h"
27
26 28 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
27 29 #ifndef PATH_MAX
28 30 #define PATH_MAX 4096
29 31 #endif
30 32
31 33 #ifdef _WIN32
32 34 /*
33 35 stat struct compatible with hg expectations
34 36 Mercurial only uses st_mode, st_size and st_mtime
35 37 the rest is kept to minimize changes between implementations
36 38 */
37 39 struct hg_stat {
38 40 int st_dev;
39 41 int st_mode;
40 42 int st_nlink;
41 43 __int64 st_size;
42 44 int st_mtime;
43 45 int st_ctime;
44 46 };
45 47 struct listdir_stat {
46 48 PyObject_HEAD
47 49 struct hg_stat st;
48 50 };
49 51 #else
50 52 struct listdir_stat {
51 53 PyObject_HEAD
52 54 struct stat st;
53 55 };
54 56 #endif
55 57
56 58 #define listdir_slot(name) \
57 59 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
58 60 { \
59 61 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
60 62 }
61 63
62 64 listdir_slot(st_dev)
63 65 listdir_slot(st_mode)
64 66 listdir_slot(st_nlink)
65 67 #ifdef _WIN32
66 68 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
67 69 {
68 70 return PyLong_FromLongLong(
69 71 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
70 72 }
71 73 #else
72 74 listdir_slot(st_size)
73 75 #endif
74 76 listdir_slot(st_mtime)
75 77 listdir_slot(st_ctime)
76 78
77 79 static struct PyGetSetDef listdir_stat_getsets[] = {
78 80 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
79 81 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
80 82 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
81 83 {"st_size", listdir_stat_st_size, 0, 0, 0},
82 84 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
83 85 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
84 86 {0, 0, 0, 0, 0}
85 87 };
86 88
87 89 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
88 90 {
89 91 return t->tp_alloc(t, 0);
90 92 }
91 93
92 94 static void listdir_stat_dealloc(PyObject *o)
93 95 {
94 96 o->ob_type->tp_free(o);
95 97 }
96 98
97 99 static PyTypeObject listdir_stat_type = {
98 PyObject_HEAD_INIT(NULL)
99 0, /*ob_size*/
100 PyVarObject_HEAD_INIT(NULL, 0)
100 101 "osutil.stat", /*tp_name*/
101 102 sizeof(struct listdir_stat), /*tp_basicsize*/
102 103 0, /*tp_itemsize*/
103 104 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
104 105 0, /*tp_print*/
105 106 0, /*tp_getattr*/
106 107 0, /*tp_setattr*/
107 108 0, /*tp_compare*/
108 109 0, /*tp_repr*/
109 110 0, /*tp_as_number*/
110 111 0, /*tp_as_sequence*/
111 112 0, /*tp_as_mapping*/
112 113 0, /*tp_hash */
113 114 0, /*tp_call*/
114 115 0, /*tp_str*/
115 116 0, /*tp_getattro*/
116 117 0, /*tp_setattro*/
117 118 0, /*tp_as_buffer*/
118 119 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
119 120 "stat objects", /* tp_doc */
120 121 0, /* tp_traverse */
121 122 0, /* tp_clear */
122 123 0, /* tp_richcompare */
123 124 0, /* tp_weaklistoffset */
124 125 0, /* tp_iter */
125 126 0, /* tp_iternext */
126 127 0, /* tp_methods */
127 128 0, /* tp_members */
128 129 listdir_stat_getsets, /* tp_getset */
129 130 0, /* tp_base */
130 131 0, /* tp_dict */
131 132 0, /* tp_descr_get */
132 133 0, /* tp_descr_set */
133 134 0, /* tp_dictoffset */
134 135 0, /* tp_init */
135 136 0, /* tp_alloc */
136 137 listdir_stat_new, /* tp_new */
137 138 };
138 139
139 140 #ifdef _WIN32
140 141
141 142 static int to_python_time(const FILETIME *tm)
142 143 {
143 144 /* number of seconds between epoch and January 1 1601 */
144 145 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
145 146 /* conversion factor from 100ns to 1s */
146 147 const __int64 a1 = 10000000;
147 148 /* explicit (int) cast to suspend compiler warnings */
148 149 return (int)((((__int64)tm->dwHighDateTime << 32)
149 150 + tm->dwLowDateTime) / a1 - a0);
150 151 }
151 152
152 153 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
153 154 {
154 155 PyObject *py_st;
155 156 struct hg_stat *stp;
156 157
157 158 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
158 159 ? _S_IFDIR : _S_IFREG;
159 160
160 161 if (!wantstat)
161 162 return Py_BuildValue("si", fd->cFileName, kind);
162 163
163 164 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
164 165 if (!py_st)
165 166 return NULL;
166 167
167 168 stp = &((struct listdir_stat *)py_st)->st;
168 169 /*
169 170 use kind as st_mode
170 171 rwx bits on Win32 are meaningless
171 172 and Hg does not use them anyway
172 173 */
173 174 stp->st_mode = kind;
174 175 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
175 176 stp->st_ctime = to_python_time(&fd->ftCreationTime);
176 177 if (kind == _S_IFREG)
177 178 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
178 179 + fd->nFileSizeLow;
179 180 return Py_BuildValue("siN", fd->cFileName,
180 181 kind, py_st);
181 182 }
182 183
183 184 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
184 185 {
185 186 PyObject *rval = NULL; /* initialize - return value */
186 187 PyObject *list;
187 188 HANDLE fh;
188 189 WIN32_FIND_DATAA fd;
189 190 char *pattern;
190 191
191 192 /* build the path + \* pattern string */
192 193 pattern = malloc(plen + 3); /* path + \* + \0 */
193 194 if (!pattern) {
194 195 PyErr_NoMemory();
195 196 goto error_nomem;
196 197 }
197 198 strcpy(pattern, path);
198 199
199 200 if (plen > 0) {
200 201 char c = path[plen-1];
201 202 if (c != ':' && c != '/' && c != '\\')
202 203 pattern[plen++] = '\\';
203 204 }
204 205 strcpy(pattern + plen, "*");
205 206
206 207 fh = FindFirstFileA(pattern, &fd);
207 208 if (fh == INVALID_HANDLE_VALUE) {
208 209 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
209 210 goto error_file;
210 211 }
211 212
212 213 list = PyList_New(0);
213 214 if (!list)
214 215 goto error_list;
215 216
216 217 do {
217 218 PyObject *item;
218 219
219 220 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
220 221 if (!strcmp(fd.cFileName, ".")
221 222 || !strcmp(fd.cFileName, ".."))
222 223 continue;
223 224
224 225 if (skip && !strcmp(fd.cFileName, skip)) {
225 226 rval = PyList_New(0);
226 227 goto error;
227 228 }
228 229 }
229 230
230 231 item = make_item(&fd, wantstat);
231 232 if (!item)
232 233 goto error;
233 234
234 235 if (PyList_Append(list, item)) {
235 236 Py_XDECREF(item);
236 237 goto error;
237 238 }
238 239
239 240 Py_XDECREF(item);
240 241 } while (FindNextFileA(fh, &fd));
241 242
242 243 if (GetLastError() != ERROR_NO_MORE_FILES) {
243 244 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
244 245 goto error;
245 246 }
246 247
247 248 rval = list;
248 249 Py_XINCREF(rval);
249 250 error:
250 251 Py_XDECREF(list);
251 252 error_list:
252 253 FindClose(fh);
253 254 error_file:
254 255 free(pattern);
255 256 error_nomem:
256 257 return rval;
257 258 }
258 259
259 260 #else
260 261
261 262 int entkind(struct dirent *ent)
262 263 {
263 264 #ifdef DT_REG
264 265 switch (ent->d_type) {
265 266 case DT_REG: return S_IFREG;
266 267 case DT_DIR: return S_IFDIR;
267 268 case DT_LNK: return S_IFLNK;
268 269 case DT_BLK: return S_IFBLK;
269 270 case DT_CHR: return S_IFCHR;
270 271 case DT_FIFO: return S_IFIFO;
271 272 case DT_SOCK: return S_IFSOCK;
272 273 }
273 274 #endif
274 275 return -1;
275 276 }
276 277
277 278 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
278 279 {
279 280 PyObject *list, *elem, *stat, *ret = NULL;
280 281 char fullpath[PATH_MAX + 10];
281 282 int kind, err;
282 283 struct stat st;
283 284 struct dirent *ent;
284 285 DIR *dir;
285 286 #ifdef AT_SYMLINK_NOFOLLOW
286 287 int dfd = -1;
287 288 #endif
288 289
289 290 if (pathlen >= PATH_MAX) {
290 291 PyErr_SetString(PyExc_ValueError, "path too long");
291 292 goto error_value;
292 293 }
293 294 strncpy(fullpath, path, PATH_MAX);
294 295 fullpath[pathlen] = '/';
295 296
296 297 #ifdef AT_SYMLINK_NOFOLLOW
297 298 dfd = open(path, O_RDONLY);
298 299 if (dfd == -1) {
299 300 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
300 301 goto error_value;
301 302 }
302 303 dir = fdopendir(dfd);
303 304 #else
304 305 dir = opendir(path);
305 306 #endif
306 307 if (!dir) {
307 308 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
308 309 goto error_dir;
309 310 }
310 311
311 312 list = PyList_New(0);
312 313 if (!list)
313 314 goto error_list;
314 315
315 316 while ((ent = readdir(dir))) {
316 317 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
317 318 continue;
318 319
319 320 kind = entkind(ent);
320 321 if (kind == -1 || keepstat) {
321 322 #ifdef AT_SYMLINK_NOFOLLOW
322 323 err = fstatat(dfd, ent->d_name, &st,
323 324 AT_SYMLINK_NOFOLLOW);
324 325 #else
325 326 strncpy(fullpath + pathlen + 1, ent->d_name,
326 327 PATH_MAX - pathlen);
327 328 fullpath[PATH_MAX] = 0;
328 329 err = lstat(fullpath, &st);
329 330 #endif
330 331 if (err == -1) {
331 332 strncpy(fullpath + pathlen + 1, ent->d_name,
332 333 PATH_MAX - pathlen);
333 334 fullpath[PATH_MAX] = 0;
334 335 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
335 336 fullpath);
336 337 goto error;
337 338 }
338 339 kind = st.st_mode & S_IFMT;
339 340 }
340 341
341 342 /* quit early? */
342 343 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
343 344 ret = PyList_New(0);
344 345 goto error;
345 346 }
346 347
347 348 if (keepstat) {
348 349 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
349 350 if (!stat)
350 351 goto error;
351 352 memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st));
352 353 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
353 354 } else
354 355 elem = Py_BuildValue("si", ent->d_name, kind);
355 356 if (!elem)
356 357 goto error;
357 358
358 359 PyList_Append(list, elem);
359 360 Py_DECREF(elem);
360 361 }
361 362
362 363 ret = list;
363 364 Py_INCREF(ret);
364 365
365 366 error:
366 367 Py_DECREF(list);
367 368 error_list:
368 369 closedir(dir);
369 370 error_dir:
370 371 #ifdef AT_SYMLINK_NOFOLLOW
371 372 close(dfd);
372 373 #endif
373 374 error_value:
374 375 return ret;
375 376 }
376 377
377 378 #endif /* ndef _WIN32 */
378 379
379 380 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
380 381 {
381 382 PyObject *statobj = NULL; /* initialize - optional arg */
382 383 PyObject *skipobj = NULL; /* initialize - optional arg */
383 384 char *path, *skip = NULL;
384 385 int wantstat, plen;
385 386
386 387 static char *kwlist[] = {"path", "stat", "skip", NULL};
387 388
388 389 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
389 390 kwlist, &path, &plen, &statobj, &skipobj))
390 391 return NULL;
391 392
392 393 wantstat = statobj && PyObject_IsTrue(statobj);
393 394
394 395 if (skipobj && skipobj != Py_None) {
395 skip = PyString_AsString(skipobj);
396 skip = PyBytes_AsString(skipobj);
396 397 if (!skip)
397 398 return NULL;
398 399 }
399 400
400 401 return _listdir(path, plen, wantstat, skip);
401 402 }
402 403
403 404 #ifdef _WIN32
404 405 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
405 406 {
406 407 static char *kwlist[] = {"name", "mode", "buffering", NULL};
407 408 PyObject *file_obj = NULL;
408 409 char *name = NULL;
409 410 char *mode = "rb";
410 411 DWORD access = 0;
411 412 DWORD creation;
412 413 HANDLE handle;
413 414 int fd, flags = 0;
414 415 int bufsize = -1;
415 416 char m0, m1, m2;
416 417 char fpmode[4];
417 418 int fppos = 0;
418 419 int plus;
419 420 FILE *fp;
420 421
421 422 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
422 423 Py_FileSystemDefaultEncoding,
423 424 &name, &mode, &bufsize))
424 425 return NULL;
425 426
426 427 m0 = mode[0];
427 428 m1 = m0 ? mode[1] : '\0';
428 429 m2 = m1 ? mode[2] : '\0';
429 430 plus = m1 == '+' || m2 == '+';
430 431
431 432 fpmode[fppos++] = m0;
432 433 if (m1 == 'b' || m2 == 'b') {
433 434 flags = _O_BINARY;
434 435 fpmode[fppos++] = 'b';
435 436 }
436 437 else
437 438 flags = _O_TEXT;
438 439 if (plus) {
439 440 flags |= _O_RDWR;
440 441 access = GENERIC_READ | GENERIC_WRITE;
441 442 fpmode[fppos++] = '+';
442 443 }
443 444 fpmode[fppos++] = '\0';
444 445
445 446 switch (m0) {
446 447 case 'r':
447 448 creation = OPEN_EXISTING;
448 449 if (!plus) {
449 450 flags |= _O_RDONLY;
450 451 access = GENERIC_READ;
451 452 }
452 453 break;
453 454 case 'w':
454 455 creation = CREATE_ALWAYS;
455 456 if (!plus) {
456 457 access = GENERIC_WRITE;
457 458 flags |= _O_WRONLY;
458 459 }
459 460 break;
460 461 case 'a':
461 462 creation = OPEN_ALWAYS;
462 463 flags |= _O_APPEND;
463 464 if (!plus) {
464 465 flags |= _O_WRONLY;
465 466 access = GENERIC_WRITE;
466 467 }
467 468 break;
468 469 default:
469 470 PyErr_Format(PyExc_ValueError,
470 471 "mode string must begin with one of 'r', 'w', "
471 472 "or 'a', not '%c'", m0);
472 473 goto bail;
473 474 }
474 475
475 476 handle = CreateFile(name, access,
476 477 FILE_SHARE_READ | FILE_SHARE_WRITE |
477 478 FILE_SHARE_DELETE,
478 479 NULL,
479 480 creation,
480 481 FILE_ATTRIBUTE_NORMAL,
481 482 0);
482 483
483 484 if (handle == INVALID_HANDLE_VALUE) {
484 485 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
485 486 goto bail;
486 487 }
487 488
488 489 fd = _open_osfhandle((intptr_t)handle, flags);
490
489 491 if (fd == -1) {
490 492 CloseHandle(handle);
491 493 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
492 494 goto bail;
493 495 }
494
496 #ifndef IS_PY3K
495 497 fp = _fdopen(fd, fpmode);
496 498 if (fp == NULL) {
497 499 _close(fd);
498 500 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
499 501 goto bail;
500 502 }
501 503
502 504 file_obj = PyFile_FromFile(fp, name, mode, fclose);
503 505 if (file_obj == NULL) {
504 506 fclose(fp);
505 507 goto bail;
506 508 }
507 509
508 510 PyFile_SetBufSize(file_obj, bufsize);
511 #else
512 file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
513 if (file_obj == NULL)
514 goto bail;
515 #endif
509 516 bail:
510 517 PyMem_Free(name);
511 518 return file_obj;
512 519 }
513 520 #endif
514 521
515 522 static char osutil_doc[] = "Native operating system services.";
516 523
517 524 static PyMethodDef methods[] = {
518 525 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
519 526 "list a directory\n"},
520 527 #ifdef _WIN32
521 528 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
522 529 "Open a file with POSIX-like semantics.\n"
523 530 "On error, this function may raise either a WindowsError or an IOError."},
524 531 #endif
525 532 {NULL, NULL}
526 533 };
527 534
535 #ifdef IS_PY3K
536 static struct PyModuleDef osutil_module = {
537 PyModuleDef_HEAD_INIT,
538 "osutil",
539 osutil_doc,
540 -1,
541 methods
542 };
543
544 PyMODINIT_FUNC PyInit_osutil(void)
545 {
546 if (PyType_Ready(&listdir_stat_type) < 0)
547 return NULL;
548
549 return PyModule_Create(&osutil_module);
550 }
551 #else
528 552 PyMODINIT_FUNC initosutil(void)
529 553 {
530 554 if (PyType_Ready(&listdir_stat_type) == -1)
531 555 return;
532 556
533 557 Py_InitModule3("osutil", methods, osutil_doc);
534 558 }
559 #endif
General Comments 0
You need to be logged in to leave comments. Login now