PageRenderTime 1230ms CodeModel.GetById 212ms app.highlight 636ms RepoModel.GetById 115ms app.codeStats 15ms

/Modules/posixmodule.c

http://unladen-swallow.googlecode.com/
C | 9117 lines | 8157 code | 540 blank | 420 comment | 866 complexity | 45aba128b4b8ee359a4c42560351554e MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1
   2/* POSIX module implementation */
   3
   4/* This file is also used for Windows NT/MS-Win and OS/2.  In that case the
   5   module actually calls itself 'nt' or 'os2', not 'posix', and a few
   6   functions are either unimplemented or implemented differently.  The source
   7   assumes that for Windows NT, the macro 'MS_WINDOWS' is defined independent
   8   of the compiler used.  Different compilers define their own feature
   9   test macro, e.g. '__BORLANDC__' or '_MSC_VER'.  For OS/2, the compiler
  10   independent macro PYOS_OS2 should be defined.  On OS/2 the default
  11   compiler is assumed to be IBM's VisualAge C++ (VACPP).  PYCC_GCC is used
  12   as the compiler specific macro for the EMX port of gcc to OS/2. */
  13
  14/* See also ../Dos/dosmodule.c */
  15
  16#ifdef __APPLE__
  17   /*
  18    * Step 1 of support for weak-linking a number of symbols existing on 
  19    * OSX 10.4 and later, see the comment in the #ifdef __APPLE__ block
  20    * at the end of this file for more information.
  21    */
  22#  pragma weak lchown
  23#  pragma weak statvfs
  24#  pragma weak fstatvfs
  25
  26#endif /* __APPLE__ */
  27
  28#define PY_SSIZE_T_CLEAN
  29
  30#include "Python.h"
  31#include "structseq.h"
  32
  33#if defined(__VMS)
  34#    include <unixio.h>
  35#endif /* defined(__VMS) */
  36
  37#ifdef __cplusplus
  38extern "C" {
  39#endif
  40
  41PyDoc_STRVAR(posix__doc__,
  42"This module provides access to operating system functionality that is\n\
  43standardized by the C Standard and the POSIX standard (a thinly\n\
  44disguised Unix interface).  Refer to the library manual and\n\
  45corresponding Unix manual entries for more information on calls.");
  46
  47#ifndef Py_USING_UNICODE
  48/* This is used in signatures of functions. */
  49#define Py_UNICODE void
  50#endif
  51
  52#if defined(PYOS_OS2)
  53#define  INCL_DOS
  54#define  INCL_DOSERRORS
  55#define  INCL_DOSPROCESS
  56#define  INCL_NOPMAPI
  57#include <os2.h>
  58#if defined(PYCC_GCC)
  59#include <ctype.h>
  60#include <io.h>
  61#include <stdio.h>
  62#include <process.h>
  63#endif
  64#include "osdefs.h"
  65#endif
  66
  67#ifdef HAVE_SYS_TYPES_H
  68#include <sys/types.h>
  69#endif /* HAVE_SYS_TYPES_H */
  70
  71#ifdef HAVE_SYS_STAT_H
  72#include <sys/stat.h>
  73#endif /* HAVE_SYS_STAT_H */
  74
  75#ifdef HAVE_SYS_WAIT_H
  76#include <sys/wait.h>		/* For WNOHANG */
  77#endif
  78
  79#ifdef HAVE_SIGNAL_H
  80#include <signal.h>
  81#endif
  82
  83#ifdef HAVE_FCNTL_H
  84#include <fcntl.h>
  85#endif /* HAVE_FCNTL_H */
  86
  87#ifdef HAVE_GRP_H
  88#include <grp.h>
  89#endif
  90
  91#ifdef HAVE_SYSEXITS_H
  92#include <sysexits.h>
  93#endif /* HAVE_SYSEXITS_H */
  94
  95#ifdef HAVE_SYS_LOADAVG_H
  96#include <sys/loadavg.h>
  97#endif
  98
  99/* Various compilers have only certain posix functions */
 100/* XXX Gosh I wish these were all moved into pyconfig.h */
 101#if defined(PYCC_VACPP) && defined(PYOS_OS2)
 102#include <process.h>
 103#else
 104#if defined(__WATCOMC__) && !defined(__QNX__)		/* Watcom compiler */
 105#define HAVE_GETCWD     1
 106#define HAVE_OPENDIR    1
 107#define HAVE_SYSTEM	1
 108#if defined(__OS2__)
 109#define HAVE_EXECV      1
 110#define HAVE_WAIT       1
 111#endif
 112#include <process.h>
 113#else
 114#ifdef __BORLANDC__		/* Borland compiler */
 115#define HAVE_EXECV      1
 116#define HAVE_GETCWD     1
 117#define HAVE_OPENDIR    1
 118#define HAVE_PIPE       1
 119#define HAVE_POPEN      1
 120#define HAVE_SYSTEM	1
 121#define HAVE_WAIT       1
 122#else
 123#ifdef _MSC_VER		/* Microsoft compiler */
 124#define HAVE_GETCWD     1
 125#define HAVE_SPAWNV	1
 126#define HAVE_EXECV      1
 127#define HAVE_PIPE       1
 128#define HAVE_POPEN      1
 129#define HAVE_SYSTEM	1
 130#define HAVE_CWAIT	1
 131#define HAVE_FSYNC	1
 132#define fsync _commit
 133#else
 134#if defined(PYOS_OS2) && defined(PYCC_GCC) || defined(__VMS)
 135/* Everything needed is defined in PC/os2emx/pyconfig.h or vms/pyconfig.h */
 136#else			/* all other compilers */
 137/* Unix functions that the configure script doesn't check for */
 138#define HAVE_EXECV      1
 139#define HAVE_FORK       1
 140#if defined(__USLC__) && defined(__SCO_VERSION__)	/* SCO UDK Compiler */
 141#define HAVE_FORK1      1
 142#endif
 143#define HAVE_GETCWD     1
 144#define HAVE_GETEGID    1
 145#define HAVE_GETEUID    1
 146#define HAVE_GETGID     1
 147#define HAVE_GETPPID    1
 148#define HAVE_GETUID     1
 149#define HAVE_KILL       1
 150#define HAVE_OPENDIR    1
 151#define HAVE_PIPE       1
 152#ifndef __rtems__
 153#define HAVE_POPEN      1
 154#endif
 155#define HAVE_SYSTEM	1
 156#define HAVE_WAIT       1
 157#define HAVE_TTYNAME	1
 158#endif  /* PYOS_OS2 && PYCC_GCC && __VMS */
 159#endif  /* _MSC_VER */
 160#endif  /* __BORLANDC__ */
 161#endif  /* ! __WATCOMC__ || __QNX__ */
 162#endif /* ! __IBMC__ */
 163
 164#ifndef _MSC_VER
 165
 166#if defined(__sgi)&&_COMPILER_VERSION>=700
 167/* declare ctermid_r if compiling with MIPSPro 7.x in ANSI C mode
 168   (default) */
 169extern char        *ctermid_r(char *);
 170#endif
 171
 172#ifndef HAVE_UNISTD_H
 173#if defined(PYCC_VACPP)
 174extern int mkdir(char *);
 175#else
 176#if ( defined(__WATCOMC__) || defined(_MSC_VER) ) && !defined(__QNX__)
 177extern int mkdir(const char *);
 178#else
 179extern int mkdir(const char *, mode_t);
 180#endif
 181#endif
 182#if defined(__IBMC__) || defined(__IBMCPP__)
 183extern int chdir(char *);
 184extern int rmdir(char *);
 185#else
 186extern int chdir(const char *);
 187extern int rmdir(const char *);
 188#endif
 189#ifdef __BORLANDC__
 190extern int chmod(const char *, int);
 191#else
 192extern int chmod(const char *, mode_t);
 193#endif
 194/*#ifdef HAVE_FCHMOD
 195extern int fchmod(int, mode_t);
 196#endif*/
 197/*#ifdef HAVE_LCHMOD
 198extern int lchmod(const char *, mode_t);
 199#endif*/
 200extern int chown(const char *, uid_t, gid_t);
 201extern char *getcwd(char *, int);
 202extern char *strerror(int);
 203extern int link(const char *, const char *);
 204extern int rename(const char *, const char *);
 205extern int stat(const char *, struct stat *);
 206extern int unlink(const char *);
 207extern int pclose(FILE *);
 208#ifdef HAVE_SYMLINK
 209extern int symlink(const char *, const char *);
 210#endif /* HAVE_SYMLINK */
 211#ifdef HAVE_LSTAT
 212extern int lstat(const char *, struct stat *);
 213#endif /* HAVE_LSTAT */
 214#endif /* !HAVE_UNISTD_H */
 215
 216#endif /* !_MSC_VER */
 217
 218#ifdef HAVE_UTIME_H
 219#include <utime.h>
 220#endif /* HAVE_UTIME_H */
 221
 222#ifdef HAVE_SYS_UTIME_H
 223#include <sys/utime.h>
 224#define HAVE_UTIME_H /* pretend we do for the rest of this file */
 225#endif /* HAVE_SYS_UTIME_H */
 226
 227#ifdef HAVE_SYS_TIMES_H
 228#include <sys/times.h>
 229#endif /* HAVE_SYS_TIMES_H */
 230
 231#ifdef HAVE_SYS_PARAM_H
 232#include <sys/param.h>
 233#endif /* HAVE_SYS_PARAM_H */
 234
 235#ifdef HAVE_SYS_UTSNAME_H
 236#include <sys/utsname.h>
 237#endif /* HAVE_SYS_UTSNAME_H */
 238
 239#ifdef HAVE_DIRENT_H
 240#include <dirent.h>
 241#define NAMLEN(dirent) strlen((dirent)->d_name)
 242#else
 243#if defined(__WATCOMC__) && !defined(__QNX__)
 244#include <direct.h>
 245#define NAMLEN(dirent) strlen((dirent)->d_name)
 246#else
 247#define dirent direct
 248#define NAMLEN(dirent) (dirent)->d_namlen
 249#endif
 250#ifdef HAVE_SYS_NDIR_H
 251#include <sys/ndir.h>
 252#endif
 253#ifdef HAVE_SYS_DIR_H
 254#include <sys/dir.h>
 255#endif
 256#ifdef HAVE_NDIR_H
 257#include <ndir.h>
 258#endif
 259#endif
 260
 261#ifdef _MSC_VER
 262#ifdef HAVE_DIRECT_H
 263#include <direct.h>
 264#endif
 265#ifdef HAVE_IO_H
 266#include <io.h>
 267#endif
 268#ifdef HAVE_PROCESS_H
 269#include <process.h>
 270#endif
 271#include "osdefs.h"
 272#include <windows.h>
 273#include <shellapi.h>	/* for ShellExecute() */
 274#define popen	_popen
 275#define pclose	_pclose
 276#endif /* _MSC_VER */
 277
 278#if defined(PYCC_VACPP) && defined(PYOS_OS2)
 279#include <io.h>
 280#endif /* OS2 */
 281
 282#ifndef MAXPATHLEN
 283#if defined(PATH_MAX) && PATH_MAX > 1024
 284#define MAXPATHLEN PATH_MAX
 285#else
 286#define MAXPATHLEN 1024
 287#endif
 288#endif /* MAXPATHLEN */
 289
 290#ifdef UNION_WAIT
 291/* Emulate some macros on systems that have a union instead of macros */
 292
 293#ifndef WIFEXITED
 294#define WIFEXITED(u_wait) (!(u_wait).w_termsig && !(u_wait).w_coredump)
 295#endif
 296
 297#ifndef WEXITSTATUS
 298#define WEXITSTATUS(u_wait) (WIFEXITED(u_wait)?((u_wait).w_retcode):-1)
 299#endif
 300
 301#ifndef WTERMSIG
 302#define WTERMSIG(u_wait) ((u_wait).w_termsig)
 303#endif
 304
 305#define WAIT_TYPE union wait
 306#define WAIT_STATUS_INT(s) (s.w_status)
 307
 308#else /* !UNION_WAIT */
 309#define WAIT_TYPE int
 310#define WAIT_STATUS_INT(s) (s)
 311#endif /* UNION_WAIT */
 312
 313/* Issue #1983: pid_t can be longer than a C long on some systems */
 314#if !defined(SIZEOF_PID_T) || SIZEOF_PID_T == SIZEOF_INT
 315#define PARSE_PID "i"
 316#define PyLong_FromPid PyInt_FromLong
 317#define PyLong_AsPid PyInt_AsLong
 318#elif SIZEOF_PID_T == SIZEOF_LONG
 319#define PARSE_PID "l"
 320#define PyLong_FromPid PyInt_FromLong
 321#define PyLong_AsPid PyInt_AsLong
 322#elif defined(SIZEOF_LONG_LONG) && SIZEOF_PID_T == SIZEOF_LONG_LONG
 323#define PARSE_PID "L"
 324#define PyLong_FromPid PyLong_FromLongLong
 325#define PyLong_AsPid PyInt_AsLongLong
 326#else
 327#error "sizeof(pid_t) is neither sizeof(int), sizeof(long) or sizeof(long long)"
 328#endif /* SIZEOF_PID_T */
 329
 330/* Don't use the "_r" form if we don't need it (also, won't have a
 331   prototype for it, at least on Solaris -- maybe others as well?). */
 332#if defined(HAVE_CTERMID_R) && defined(WITH_THREAD)
 333#define USE_CTERMID_R
 334#endif
 335
 336#if defined(HAVE_TMPNAM_R) && defined(WITH_THREAD)
 337#define USE_TMPNAM_R
 338#endif
 339
 340/* choose the appropriate stat and fstat functions and return structs */
 341#undef STAT
 342#if defined(MS_WIN64) || defined(MS_WINDOWS)
 343#	define STAT win32_stat
 344#	define FSTAT win32_fstat
 345#	define STRUCT_STAT struct win32_stat
 346#else
 347#	define STAT stat
 348#	define FSTAT fstat
 349#	define STRUCT_STAT struct stat
 350#endif
 351
 352#if defined(MAJOR_IN_MKDEV)
 353#include <sys/mkdev.h>
 354#else
 355#if defined(MAJOR_IN_SYSMACROS)
 356#include <sys/sysmacros.h>
 357#endif
 358#if defined(HAVE_MKNOD) && defined(HAVE_SYS_MKDEV_H)
 359#include <sys/mkdev.h>
 360#endif
 361#endif
 362
 363/* Return a dictionary corresponding to the POSIX environment table */
 364#ifdef WITH_NEXT_FRAMEWORK
 365/* On Darwin/MacOSX a shared library or framework has no access to
 366** environ directly, we must obtain it with _NSGetEnviron().
 367*/
 368#include <crt_externs.h>
 369static char **environ;
 370#elif !defined(_MSC_VER) && ( !defined(__WATCOMC__) || defined(__QNX__) )
 371extern char **environ;
 372#endif /* !_MSC_VER */
 373
 374static PyObject *
 375convertenviron(void)
 376{
 377	PyObject *d;
 378	char **e;
 379	d = PyDict_New();
 380	if (d == NULL)
 381		return NULL;
 382#ifdef WITH_NEXT_FRAMEWORK
 383	if (environ == NULL)
 384		environ = *_NSGetEnviron();
 385#endif
 386	if (environ == NULL)
 387		return d;
 388	/* This part ignores errors */
 389	for (e = environ; *e != NULL; e++) {
 390		PyObject *k;
 391		PyObject *v;
 392		char *p = strchr(*e, '=');
 393		if (p == NULL)
 394			continue;
 395		k = PyString_FromStringAndSize(*e, (int)(p-*e));
 396		if (k == NULL) {
 397			PyErr_Clear();
 398			continue;
 399		}
 400		v = PyString_FromString(p+1);
 401		if (v == NULL) {
 402			PyErr_Clear();
 403			Py_DECREF(k);
 404			continue;
 405		}
 406		if (PyDict_GetItem(d, k) == NULL) {
 407			if (PyDict_SetItem(d, k, v) != 0)
 408				PyErr_Clear();
 409		}
 410		Py_DECREF(k);
 411		Py_DECREF(v);
 412	}
 413#if defined(PYOS_OS2)
 414    {
 415        APIRET rc;
 416        char   buffer[1024]; /* OS/2 Provides a Documented Max of 1024 Chars */
 417
 418        rc = DosQueryExtLIBPATH(buffer, BEGIN_LIBPATH);
 419	if (rc == NO_ERROR) { /* (not a type, envname is NOT 'BEGIN_LIBPATH') */
 420            PyObject *v = PyString_FromString(buffer);
 421		    PyDict_SetItemString(d, "BEGINLIBPATH", v);
 422            Py_DECREF(v);
 423        }
 424        rc = DosQueryExtLIBPATH(buffer, END_LIBPATH);
 425        if (rc == NO_ERROR) { /* (not a typo, envname is NOT 'END_LIBPATH') */
 426            PyObject *v = PyString_FromString(buffer);
 427		    PyDict_SetItemString(d, "ENDLIBPATH", v);
 428            Py_DECREF(v);
 429        }
 430    }
 431#endif
 432	return d;
 433}
 434
 435
 436/* Set a POSIX-specific error from errno, and return NULL */
 437
 438static PyObject *
 439posix_error(void)
 440{
 441	return PyErr_SetFromErrno(PyExc_OSError);
 442}
 443static PyObject *
 444posix_error_with_filename(char* name)
 445{
 446	return PyErr_SetFromErrnoWithFilename(PyExc_OSError, name);
 447}
 448
 449#ifdef Py_WIN_WIDE_FILENAMES
 450static PyObject *
 451posix_error_with_unicode_filename(Py_UNICODE* name)
 452{
 453	return PyErr_SetFromErrnoWithUnicodeFilename(PyExc_OSError, name);
 454}
 455#endif /* Py_WIN_WIDE_FILENAMES */
 456
 457
 458static PyObject *
 459posix_error_with_allocated_filename(char* name)
 460{
 461	PyObject *rc = PyErr_SetFromErrnoWithFilename(PyExc_OSError, name);
 462	PyMem_Free(name);
 463	return rc;
 464}
 465
 466#ifdef MS_WINDOWS
 467static PyObject *
 468win32_error(char* function, char* filename)
 469{
 470	/* XXX We should pass the function name along in the future.
 471	   (_winreg.c also wants to pass the function name.)
 472	   This would however require an additional param to the
 473	   Windows error object, which is non-trivial.
 474	*/
 475	errno = GetLastError();
 476	if (filename)
 477		return PyErr_SetFromWindowsErrWithFilename(errno, filename);
 478	else
 479		return PyErr_SetFromWindowsErr(errno);
 480}
 481
 482#ifdef Py_WIN_WIDE_FILENAMES
 483static PyObject *
 484win32_error_unicode(char* function, Py_UNICODE* filename)
 485{
 486	/* XXX - see win32_error for comments on 'function' */
 487	errno = GetLastError();
 488	if (filename)
 489		return PyErr_SetFromWindowsErrWithUnicodeFilename(errno, filename);
 490	else
 491		return PyErr_SetFromWindowsErr(errno);
 492}
 493
 494static PyObject *_PyUnicode_FromFileSystemEncodedObject(register PyObject *obj)
 495{
 496}
 497
 498static int
 499convert_to_unicode(PyObject **param)
 500{
 501	if (PyUnicode_CheckExact(*param))
 502		Py_INCREF(*param);
 503	else if (PyUnicode_Check(*param))
 504		/* For a Unicode subtype that's not a Unicode object,
 505		   return a true Unicode object with the same data. */
 506		*param = PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(*param),
 507					       PyUnicode_GET_SIZE(*param));
 508	else
 509		*param = PyUnicode_FromEncodedObject(*param,
 510				                     Py_FileSystemDefaultEncoding,
 511					             "strict");
 512	return (*param) != NULL;
 513}
 514
 515#endif /* Py_WIN_WIDE_FILENAMES */
 516
 517#endif
 518
 519#if defined(PYOS_OS2)
 520/**********************************************************************
 521 *         Helper Function to Trim and Format OS/2 Messages
 522 **********************************************************************/
 523    static void
 524os2_formatmsg(char *msgbuf, int msglen, char *reason)
 525{
 526    msgbuf[msglen] = '\0'; /* OS/2 Doesn't Guarantee a Terminator */
 527
 528    if (strlen(msgbuf) > 0) { /* If Non-Empty Msg, Trim CRLF */
 529        char *lastc = &msgbuf[ strlen(msgbuf)-1 ];
 530
 531        while (lastc > msgbuf && isspace(Py_CHARMASK(*lastc)))
 532            *lastc-- = '\0'; /* Trim Trailing Whitespace (CRLF) */
 533    }
 534
 535    /* Add Optional Reason Text */
 536    if (reason) {
 537        strcat(msgbuf, " : ");
 538        strcat(msgbuf, reason);
 539    }
 540}
 541
 542/**********************************************************************
 543 *             Decode an OS/2 Operating System Error Code
 544 *
 545 * A convenience function to lookup an OS/2 error code and return a
 546 * text message we can use to raise a Python exception.
 547 *
 548 * Notes:
 549 *   The messages for errors returned from the OS/2 kernel reside in
 550 *   the file OSO001.MSG in the \OS2 directory hierarchy.
 551 *
 552 **********************************************************************/
 553    static char *
 554os2_strerror(char *msgbuf, int msgbuflen, int errorcode, char *reason)
 555{
 556    APIRET rc;
 557    ULONG  msglen;
 558
 559    /* Retrieve Kernel-Related Error Message from OSO001.MSG File */
 560    Py_BEGIN_ALLOW_THREADS
 561    rc = DosGetMessage(NULL, 0, msgbuf, msgbuflen,
 562                       errorcode, "oso001.msg", &msglen);
 563    Py_END_ALLOW_THREADS
 564
 565    if (rc == NO_ERROR)
 566        os2_formatmsg(msgbuf, msglen, reason);
 567    else
 568        PyOS_snprintf(msgbuf, msgbuflen,
 569        	      "unknown OS error #%d", errorcode);
 570
 571    return msgbuf;
 572}
 573
 574/* Set an OS/2-specific error and return NULL.  OS/2 kernel
 575   errors are not in a global variable e.g. 'errno' nor are
 576   they congruent with posix error numbers. */
 577
 578static PyObject * os2_error(int code)
 579{
 580    char text[1024];
 581    PyObject *v;
 582
 583    os2_strerror(text, sizeof(text), code, "");
 584
 585    v = Py_BuildValue("(is)", code, text);
 586    if (v != NULL) {
 587        PyErr_SetObject(PyExc_OSError, v);
 588        Py_DECREF(v);
 589    }
 590    return NULL; /* Signal to Python that an Exception is Pending */
 591}
 592
 593#endif /* OS2 */
 594
 595/* POSIX generic methods */
 596
 597static PyObject *
 598posix_fildes(PyObject *fdobj, int (*func)(int))
 599{
 600	int fd;
 601	int res;
 602	fd = PyObject_AsFileDescriptor(fdobj);
 603	if (fd < 0)
 604		return NULL;
 605	Py_BEGIN_ALLOW_THREADS
 606	res = (*func)(fd);
 607	Py_END_ALLOW_THREADS
 608	if (res < 0)
 609		return posix_error();
 610	Py_INCREF(Py_None);
 611	return Py_None;
 612}
 613
 614#ifdef Py_WIN_WIDE_FILENAMES
 615static int
 616unicode_file_names(void)
 617{
 618	static int canusewide = -1;
 619	if (canusewide == -1) {
 620		/* As per doc for ::GetVersion(), this is the correct test for
 621		   the Windows NT family. */
 622		canusewide = (GetVersion() < 0x80000000) ? 1 : 0;
 623	}
 624	return canusewide;
 625}
 626#endif
 627
 628static PyObject *
 629posix_1str(PyObject *args, char *format, int (*func)(const char*))
 630{
 631	char *path1 = NULL;
 632	int res;
 633	if (!PyArg_ParseTuple(args, format,
 634	                      Py_FileSystemDefaultEncoding, &path1))
 635		return NULL;
 636	Py_BEGIN_ALLOW_THREADS
 637	res = (*func)(path1);
 638	Py_END_ALLOW_THREADS
 639	if (res < 0)
 640		return posix_error_with_allocated_filename(path1);
 641	PyMem_Free(path1);
 642	Py_INCREF(Py_None);
 643	return Py_None;
 644}
 645
 646static PyObject *
 647posix_2str(PyObject *args,
 648	   char *format,
 649	   int (*func)(const char *, const char *))
 650{
 651	char *path1 = NULL, *path2 = NULL;
 652	int res;
 653	if (!PyArg_ParseTuple(args, format,
 654	                      Py_FileSystemDefaultEncoding, &path1,
 655	                      Py_FileSystemDefaultEncoding, &path2))
 656		return NULL;
 657	Py_BEGIN_ALLOW_THREADS
 658	res = (*func)(path1, path2);
 659	Py_END_ALLOW_THREADS
 660	PyMem_Free(path1);
 661	PyMem_Free(path2);
 662	if (res != 0)
 663		/* XXX how to report both path1 and path2??? */
 664		return posix_error();
 665	Py_INCREF(Py_None);
 666	return Py_None;
 667}
 668
 669#ifdef Py_WIN_WIDE_FILENAMES
 670static PyObject*
 671win32_1str(PyObject* args, char* func, 
 672	   char* format, BOOL (__stdcall *funcA)(LPCSTR), 
 673	   char* wformat, BOOL (__stdcall *funcW)(LPWSTR))
 674{
 675	PyObject *uni;
 676	char *ansi;
 677	BOOL result;
 678	if (unicode_file_names()) {
 679		if (!PyArg_ParseTuple(args, wformat, &uni))
 680			PyErr_Clear();
 681		else {
 682			Py_BEGIN_ALLOW_THREADS
 683			result = funcW(PyUnicode_AsUnicode(uni));
 684			Py_END_ALLOW_THREADS
 685			if (!result)
 686				return win32_error_unicode(func, PyUnicode_AsUnicode(uni));
 687			Py_INCREF(Py_None);
 688			return Py_None;
 689		}
 690	}
 691	if (!PyArg_ParseTuple(args, format, &ansi))
 692		return NULL;
 693	Py_BEGIN_ALLOW_THREADS
 694	result = funcA(ansi);
 695	Py_END_ALLOW_THREADS
 696	if (!result)
 697		return win32_error(func, ansi);
 698	Py_INCREF(Py_None);
 699	return Py_None;
 700
 701}
 702
 703/* This is a reimplementation of the C library's chdir function,
 704   but one that produces Win32 errors instead of DOS error codes.
 705   chdir is essentially a wrapper around SetCurrentDirectory; however,
 706   it also needs to set "magic" environment variables indicating
 707   the per-drive current directory, which are of the form =<drive>: */
 708BOOL __stdcall
 709win32_chdir(LPCSTR path)
 710{
 711	char new_path[MAX_PATH+1];
 712	int result;
 713	char env[4] = "=x:";
 714
 715	if(!SetCurrentDirectoryA(path))
 716		return FALSE;
 717	result = GetCurrentDirectoryA(MAX_PATH+1, new_path);
 718	if (!result)
 719		return FALSE;
 720	/* In the ANSI API, there should not be any paths longer
 721	   than MAX_PATH. */
 722	assert(result <= MAX_PATH+1);
 723	if (strncmp(new_path, "\\\\", 2) == 0 ||
 724	    strncmp(new_path, "//", 2) == 0)
 725	    /* UNC path, nothing to do. */
 726	    return TRUE;
 727	env[1] = new_path[0];
 728	return SetEnvironmentVariableA(env, new_path);
 729}
 730
 731/* The Unicode version differs from the ANSI version
 732   since the current directory might exceed MAX_PATH characters */
 733BOOL __stdcall
 734win32_wchdir(LPCWSTR path)
 735{
 736	wchar_t _new_path[MAX_PATH+1], *new_path = _new_path;
 737	int result;
 738	wchar_t env[4] = L"=x:";
 739
 740	if(!SetCurrentDirectoryW(path))
 741		return FALSE;
 742	result = GetCurrentDirectoryW(MAX_PATH+1, new_path);
 743	if (!result)
 744		return FALSE;
 745	if (result > MAX_PATH+1) {
 746		new_path = malloc(result * sizeof(wchar_t));
 747		if (!new_path) {
 748			SetLastError(ERROR_OUTOFMEMORY);
 749			return FALSE;
 750		}
 751		result = GetCurrentDirectoryW(result, new_path);
 752		if (!result) {
 753			free(new_path);
 754			return FALSE;
 755		}
 756	}
 757	if (wcsncmp(new_path, L"\\\\", 2) == 0 ||
 758	    wcsncmp(new_path, L"//", 2) == 0)
 759	    /* UNC path, nothing to do. */
 760	    return TRUE;
 761	env[1] = new_path[0];
 762	result = SetEnvironmentVariableW(env, new_path);
 763	if (new_path != _new_path)
 764		free(new_path);
 765	return result;
 766}
 767#endif
 768
 769#ifdef MS_WINDOWS
 770/* The CRT of Windows has a number of flaws wrt. its stat() implementation:
 771   - time stamps are restricted to second resolution
 772   - file modification times suffer from forth-and-back conversions between
 773     UTC and local time
 774   Therefore, we implement our own stat, based on the Win32 API directly.
 775*/
 776#define HAVE_STAT_NSEC 1 
 777
 778struct win32_stat{
 779    int st_dev;
 780    __int64 st_ino;
 781    unsigned short st_mode;
 782    int st_nlink;
 783    int st_uid;
 784    int st_gid;
 785    int st_rdev;
 786    __int64 st_size;
 787    int st_atime;
 788    int st_atime_nsec;
 789    int st_mtime;
 790    int st_mtime_nsec;
 791    int st_ctime;
 792    int st_ctime_nsec;
 793};
 794
 795static __int64 secs_between_epochs = 11644473600; /* Seconds between 1.1.1601 and 1.1.1970 */
 796
 797static void
 798FILE_TIME_to_time_t_nsec(FILETIME *in_ptr, int *time_out, int* nsec_out)
 799{
 800	/* XXX endianness. Shouldn't matter, as all Windows implementations are little-endian */
 801	/* Cannot simply cast and dereference in_ptr, 
 802	   since it might not be aligned properly */
 803	__int64 in;
 804	memcpy(&in, in_ptr, sizeof(in));
 805	*nsec_out = (int)(in % 10000000) * 100; /* FILETIME is in units of 100 nsec. */
 806	/* XXX Win32 supports time stamps past 2038; we currently don't */
 807	*time_out = Py_SAFE_DOWNCAST((in / 10000000) - secs_between_epochs, __int64, int);
 808}
 809
 810static void
 811time_t_to_FILE_TIME(int time_in, int nsec_in, FILETIME *out_ptr)
 812{
 813	/* XXX endianness */
 814	__int64 out;
 815	out = time_in + secs_between_epochs;
 816	out = out * 10000000 + nsec_in / 100;
 817	memcpy(out_ptr, &out, sizeof(out));
 818}
 819
 820/* Below, we *know* that ugo+r is 0444 */
 821#if _S_IREAD != 0400
 822#error Unsupported C library
 823#endif
 824static int
 825attributes_to_mode(DWORD attr)
 826{
 827	int m = 0;
 828	if (attr & FILE_ATTRIBUTE_DIRECTORY)
 829		m |= _S_IFDIR | 0111; /* IFEXEC for user,group,other */
 830	else
 831		m |= _S_IFREG;
 832	if (attr & FILE_ATTRIBUTE_READONLY)
 833		m |= 0444;
 834	else
 835		m |= 0666;
 836	return m;
 837}
 838
 839static int
 840attribute_data_to_stat(WIN32_FILE_ATTRIBUTE_DATA *info, struct win32_stat *result)
 841{
 842	memset(result, 0, sizeof(*result));
 843	result->st_mode = attributes_to_mode(info->dwFileAttributes);
 844	result->st_size = (((__int64)info->nFileSizeHigh)<<32) + info->nFileSizeLow;
 845	FILE_TIME_to_time_t_nsec(&info->ftCreationTime, &result->st_ctime, &result->st_ctime_nsec);
 846	FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
 847	FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec);
 848
 849	return 0;
 850}
 851
 852/* Emulate GetFileAttributesEx[AW] on Windows 95 */
 853static int checked = 0;
 854static BOOL (CALLBACK *gfaxa)(LPCSTR, GET_FILEEX_INFO_LEVELS, LPVOID);
 855static BOOL (CALLBACK *gfaxw)(LPCWSTR, GET_FILEEX_INFO_LEVELS, LPVOID);
 856static void
 857check_gfax()
 858{
 859	HINSTANCE hKernel32;
 860	if (checked)
 861	    return;
 862	checked = 1;
 863	hKernel32 = GetModuleHandle("KERNEL32");
 864	*(FARPROC*)&gfaxa = GetProcAddress(hKernel32, "GetFileAttributesExA");
 865	*(FARPROC*)&gfaxw = GetProcAddress(hKernel32, "GetFileAttributesExW");
 866}
 867
 868static BOOL
 869attributes_from_dir(LPCSTR pszFile, LPWIN32_FILE_ATTRIBUTE_DATA pfad)
 870{
 871	HANDLE hFindFile;
 872	WIN32_FIND_DATAA FileData;
 873	hFindFile = FindFirstFileA(pszFile, &FileData);
 874	if (hFindFile == INVALID_HANDLE_VALUE)
 875		return FALSE;
 876	FindClose(hFindFile);
 877	pfad->dwFileAttributes = FileData.dwFileAttributes;
 878	pfad->ftCreationTime   = FileData.ftCreationTime;
 879	pfad->ftLastAccessTime = FileData.ftLastAccessTime;
 880	pfad->ftLastWriteTime  = FileData.ftLastWriteTime;
 881	pfad->nFileSizeHigh    = FileData.nFileSizeHigh;
 882	pfad->nFileSizeLow     = FileData.nFileSizeLow;
 883	return TRUE;
 884}
 885
 886static BOOL
 887attributes_from_dir_w(LPCWSTR pszFile, LPWIN32_FILE_ATTRIBUTE_DATA pfad)
 888{
 889	HANDLE hFindFile;
 890	WIN32_FIND_DATAW FileData;
 891	hFindFile = FindFirstFileW(pszFile, &FileData);
 892	if (hFindFile == INVALID_HANDLE_VALUE)
 893		return FALSE;
 894	FindClose(hFindFile);
 895	pfad->dwFileAttributes = FileData.dwFileAttributes;
 896	pfad->ftCreationTime   = FileData.ftCreationTime;
 897	pfad->ftLastAccessTime = FileData.ftLastAccessTime;
 898	pfad->ftLastWriteTime  = FileData.ftLastWriteTime;
 899	pfad->nFileSizeHigh    = FileData.nFileSizeHigh;
 900	pfad->nFileSizeLow     = FileData.nFileSizeLow;
 901	return TRUE;
 902}
 903
 904static BOOL WINAPI
 905Py_GetFileAttributesExA(LPCSTR pszFile, 
 906		       GET_FILEEX_INFO_LEVELS level,
 907                       LPVOID pv)
 908{
 909	BOOL result;
 910	LPWIN32_FILE_ATTRIBUTE_DATA pfad = pv;
 911	/* First try to use the system's implementation, if that is
 912	   available and either succeeds to gives an error other than
 913	   that it isn't implemented. */
 914	check_gfax();
 915	if (gfaxa) {
 916		result = gfaxa(pszFile, level, pv);
 917		if (result || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
 918			return result;
 919	}
 920	/* It's either not present, or not implemented.
 921	   Emulate using FindFirstFile. */
 922	if (level != GetFileExInfoStandard) {
 923		SetLastError(ERROR_INVALID_PARAMETER);
 924		return FALSE;
 925	}
 926	/* Use GetFileAttributes to validate that the file name
 927	   does not contain wildcards (which FindFirstFile would
 928	   accept). */
 929	if (GetFileAttributesA(pszFile) == 0xFFFFFFFF)
 930		return FALSE;
 931	return attributes_from_dir(pszFile, pfad);
 932}
 933
 934static BOOL WINAPI
 935Py_GetFileAttributesExW(LPCWSTR pszFile, 
 936		       GET_FILEEX_INFO_LEVELS level,
 937                       LPVOID pv)
 938{
 939	BOOL result;
 940	LPWIN32_FILE_ATTRIBUTE_DATA pfad = pv;
 941	/* First try to use the system's implementation, if that is
 942	   available and either succeeds to gives an error other than
 943	   that it isn't implemented. */
 944	check_gfax();
 945	if (gfaxa) {
 946		result = gfaxw(pszFile, level, pv);
 947		if (result || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
 948			return result;
 949	}
 950	/* It's either not present, or not implemented.
 951	   Emulate using FindFirstFile. */
 952	if (level != GetFileExInfoStandard) {
 953		SetLastError(ERROR_INVALID_PARAMETER);
 954		return FALSE;
 955	}
 956	/* Use GetFileAttributes to validate that the file name
 957	   does not contain wildcards (which FindFirstFile would
 958	   accept). */
 959	if (GetFileAttributesW(pszFile) == 0xFFFFFFFF)
 960		return FALSE;
 961	return attributes_from_dir_w(pszFile, pfad);
 962}
 963
 964static int 
 965win32_stat(const char* path, struct win32_stat *result)
 966{
 967	WIN32_FILE_ATTRIBUTE_DATA info;
 968	int code;
 969	char *dot;
 970	/* XXX not supported on Win95 and NT 3.x */
 971	if (!Py_GetFileAttributesExA(path, GetFileExInfoStandard, &info)) {
 972		if (GetLastError() != ERROR_SHARING_VIOLATION) {
 973			/* Protocol violation: we explicitly clear errno, instead of
 974			   setting it to a POSIX error. Callers should use GetLastError. */
 975			errno = 0;
 976			return -1;
 977		} else {
 978			/* Could not get attributes on open file. Fall back to
 979			   reading the directory. */
 980			if (!attributes_from_dir(path, &info)) {
 981				/* Very strange. This should not fail now */
 982				errno = 0;
 983				return -1;
 984			}
 985		}
 986	}
 987	code = attribute_data_to_stat(&info, result);
 988	if (code != 0)
 989		return code;
 990	/* Set S_IFEXEC if it is an .exe, .bat, ... */
 991	dot = strrchr(path, '.');
 992	if (dot) {
 993		if (stricmp(dot, ".bat") == 0 ||
 994		stricmp(dot, ".cmd") == 0 ||
 995		stricmp(dot, ".exe") == 0 ||
 996		stricmp(dot, ".com") == 0)
 997		result->st_mode |= 0111;
 998	}
 999	return code;
1000}
1001
1002static int 
1003win32_wstat(const wchar_t* path, struct win32_stat *result)
1004{
1005	int code;
1006	const wchar_t *dot;
1007	WIN32_FILE_ATTRIBUTE_DATA info;
1008	/* XXX not supported on Win95 and NT 3.x */
1009	if (!Py_GetFileAttributesExW(path, GetFileExInfoStandard, &info)) {
1010		if (GetLastError() != ERROR_SHARING_VIOLATION) {
1011			/* Protocol violation: we explicitly clear errno, instead of
1012			   setting it to a POSIX error. Callers should use GetLastError. */
1013			errno = 0;
1014			return -1;
1015		} else {
1016			/* Could not get attributes on open file. Fall back to
1017			   reading the directory. */
1018			if (!attributes_from_dir_w(path, &info)) {
1019				/* Very strange. This should not fail now */
1020				errno = 0;
1021				return -1;
1022			}
1023		}
1024	}
1025	code = attribute_data_to_stat(&info, result);
1026	if (code < 0)
1027		return code;
1028	/* Set IFEXEC if it is an .exe, .bat, ... */
1029	dot = wcsrchr(path, '.');
1030	if (dot) {
1031		if (_wcsicmp(dot, L".bat") == 0 ||
1032		    _wcsicmp(dot, L".cmd") == 0 ||
1033		    _wcsicmp(dot, L".exe") == 0 ||
1034		    _wcsicmp(dot, L".com") == 0)
1035			result->st_mode |= 0111;
1036	}
1037	return code;
1038}
1039
1040static int
1041win32_fstat(int file_number, struct win32_stat *result)
1042{
1043	BY_HANDLE_FILE_INFORMATION info;
1044	HANDLE h;
1045	int type;
1046    
1047	h = (HANDLE)_get_osfhandle(file_number);
1048    
1049	/* Protocol violation: we explicitly clear errno, instead of
1050	   setting it to a POSIX error. Callers should use GetLastError. */
1051	errno = 0;
1052
1053	if (h == INVALID_HANDLE_VALUE) {
1054    		/* This is really a C library error (invalid file handle).
1055		   We set the Win32 error to the closes one matching. */
1056		SetLastError(ERROR_INVALID_HANDLE);
1057		return -1;
1058	}
1059	memset(result, 0, sizeof(*result));
1060
1061	type = GetFileType(h);
1062	if (type == FILE_TYPE_UNKNOWN) {
1063	    DWORD error = GetLastError();
1064	    if (error != 0) {
1065		return -1;
1066	    }
1067	    /* else: valid but unknown file */
1068	}
1069
1070	if (type != FILE_TYPE_DISK) {
1071		if (type == FILE_TYPE_CHAR)
1072			result->st_mode = _S_IFCHR;
1073		else if (type == FILE_TYPE_PIPE)
1074			result->st_mode = _S_IFIFO;
1075		return 0;
1076	}
1077
1078	if (!GetFileInformationByHandle(h, &info)) {
1079		return -1;
1080	}
1081
1082	/* similar to stat() */
1083	result->st_mode = attributes_to_mode(info.dwFileAttributes);
1084	result->st_size = (((__int64)info.nFileSizeHigh)<<32) + info.nFileSizeLow;
1085	FILE_TIME_to_time_t_nsec(&info.ftCreationTime, &result->st_ctime, &result->st_ctime_nsec);
1086	FILE_TIME_to_time_t_nsec(&info.ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
1087	FILE_TIME_to_time_t_nsec(&info.ftLastAccessTime, &result->st_atime, &result->st_atime_nsec);
1088	/* specific to fstat() */
1089	result->st_nlink = info.nNumberOfLinks;
1090	result->st_ino = (((__int64)info.nFileIndexHigh)<<32) + info.nFileIndexLow;
1091	return 0;
1092}
1093
1094#endif /* MS_WINDOWS */
1095
1096PyDoc_STRVAR(stat_result__doc__,
1097"stat_result: Result from stat or lstat.\n\n\
1098This object may be accessed either as a tuple of\n\
1099  (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)\n\
1100or via the attributes st_mode, st_ino, st_dev, st_nlink, st_uid, and so on.\n\
1101\n\
1102Posix/windows: If your platform supports st_blksize, st_blocks, st_rdev,\n\
1103or st_flags, they are available as attributes only.\n\
1104\n\
1105See os.stat for more information.");
1106
1107static PyStructSequence_Field stat_result_fields[] = {
1108	{"st_mode",    "protection bits"},
1109	{"st_ino",     "inode"},
1110	{"st_dev",     "device"},
1111	{"st_nlink",   "number of hard links"},
1112	{"st_uid",     "user ID of owner"},
1113	{"st_gid",     "group ID of owner"},
1114	{"st_size",    "total size, in bytes"},
1115	/* The NULL is replaced with PyStructSequence_UnnamedField later. */
1116	{NULL,   "integer time of last access"},
1117	{NULL,   "integer time of last modification"},
1118	{NULL,   "integer time of last change"},
1119	{"st_atime",   "time of last access"},
1120	{"st_mtime",   "time of last modification"},
1121	{"st_ctime",   "time of last change"},
1122#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1123	{"st_blksize", "blocksize for filesystem I/O"},
1124#endif
1125#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1126	{"st_blocks",  "number of blocks allocated"},
1127#endif
1128#ifdef HAVE_STRUCT_STAT_ST_RDEV
1129	{"st_rdev",    "device type (if inode device)"},
1130#endif
1131#ifdef HAVE_STRUCT_STAT_ST_FLAGS
1132	{"st_flags",   "user defined flags for file"},
1133#endif
1134#ifdef HAVE_STRUCT_STAT_ST_GEN
1135	{"st_gen",    "generation number"},
1136#endif
1137#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
1138	{"st_birthtime",   "time of creation"},
1139#endif
1140	{0}
1141};
1142
1143#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1144#define ST_BLKSIZE_IDX 13
1145#else
1146#define ST_BLKSIZE_IDX 12
1147#endif
1148
1149#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1150#define ST_BLOCKS_IDX (ST_BLKSIZE_IDX+1)
1151#else
1152#define ST_BLOCKS_IDX ST_BLKSIZE_IDX
1153#endif
1154
1155#ifdef HAVE_STRUCT_STAT_ST_RDEV
1156#define ST_RDEV_IDX (ST_BLOCKS_IDX+1)
1157#else
1158#define ST_RDEV_IDX ST_BLOCKS_IDX
1159#endif
1160
1161#ifdef HAVE_STRUCT_STAT_ST_FLAGS
1162#define ST_FLAGS_IDX (ST_RDEV_IDX+1)
1163#else
1164#define ST_FLAGS_IDX ST_RDEV_IDX
1165#endif
1166
1167#ifdef HAVE_STRUCT_STAT_ST_GEN
1168#define ST_GEN_IDX (ST_FLAGS_IDX+1)
1169#else
1170#define ST_GEN_IDX ST_FLAGS_IDX
1171#endif
1172
1173#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
1174#define ST_BIRTHTIME_IDX (ST_GEN_IDX+1)
1175#else
1176#define ST_BIRTHTIME_IDX ST_GEN_IDX
1177#endif
1178
1179static PyStructSequence_Desc stat_result_desc = {
1180	"stat_result", /* name */
1181	stat_result__doc__, /* doc */
1182	stat_result_fields,
1183	10
1184};
1185
1186PyDoc_STRVAR(statvfs_result__doc__,
1187"statvfs_result: Result from statvfs or fstatvfs.\n\n\
1188This object may be accessed either as a tuple of\n\
1189  (bsize, frsize, blocks, bfree, bavail, files, ffree, favail, flag, namemax),\n\
1190or via the attributes f_bsize, f_frsize, f_blocks, f_bfree, and so on.\n\
1191\n\
1192See os.statvfs for more information.");
1193
1194static PyStructSequence_Field statvfs_result_fields[] = {
1195        {"f_bsize",  },
1196        {"f_frsize", },
1197        {"f_blocks", },
1198        {"f_bfree",  },
1199        {"f_bavail", },
1200        {"f_files",  },
1201        {"f_ffree",  },
1202        {"f_favail", },
1203        {"f_flag",   },
1204        {"f_namemax",},
1205        {0}
1206};
1207
1208static PyStructSequence_Desc statvfs_result_desc = {
1209	"statvfs_result", /* name */
1210	statvfs_result__doc__, /* doc */
1211	statvfs_result_fields,
1212	10
1213};
1214
1215static int initialized;
1216static PyTypeObject StatResultType;
1217static PyTypeObject StatVFSResultType;
1218static newfunc structseq_new;
1219
1220static PyObject *
1221statresult_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1222{
1223	PyStructSequence *result;
1224	int i;
1225
1226	result = (PyStructSequence*)structseq_new(type, args, kwds);
1227	if (!result)
1228		return NULL;
1229	/* If we have been initialized from a tuple,
1230	   st_?time might be set to None. Initialize it
1231	   from the int slots.  */
1232	for (i = 7; i <= 9; i++) {
1233		if (result->ob_item[i+3] == Py_None) {
1234			Py_DECREF(Py_None);
1235			Py_INCREF(result->ob_item[i]);
1236			result->ob_item[i+3] = result->ob_item[i];
1237		}
1238	}
1239	return (PyObject*)result;
1240}
1241
1242
1243
1244/* If true, st_?time is float. */
1245static int _stat_float_times = 1;
1246
1247PyDoc_STRVAR(stat_float_times__doc__,
1248"stat_float_times([newval]) -> oldval\n\n\
1249Determine whether os.[lf]stat represents time stamps as float objects.\n\
1250If newval is True, future calls to stat() return floats, if it is False,\n\
1251future calls return ints. \n\
1252If newval is omitted, return the current setting.\n");
1253
1254static PyObject*
1255stat_float_times(PyObject* self, PyObject *args)
1256{
1257	int newval = -1;
1258	if (!PyArg_ParseTuple(args, "|i:stat_float_times", &newval))
1259		return NULL;
1260	if (newval == -1)
1261		/* Return old value */
1262		return PyBool_FromLong(_stat_float_times);
1263	_stat_float_times = newval;
1264	Py_INCREF(Py_None);
1265	return Py_None;
1266}
1267
1268static void
1269fill_time(PyObject *v, int index, time_t sec, unsigned long nsec)
1270{
1271	PyObject *fval,*ival;
1272#if SIZEOF_TIME_T > SIZEOF_LONG
1273	ival = PyLong_FromLongLong((PY_LONG_LONG)sec);
1274#else
1275	ival = PyInt_FromLong((long)sec);
1276#endif
1277	if (!ival)
1278		return;
1279	if (_stat_float_times) {
1280		fval = PyFloat_FromDouble(sec + 1e-9*nsec);
1281	} else {
1282		fval = ival;
1283		Py_INCREF(fval);
1284	}
1285	PyStructSequence_SET_ITEM(v, index, ival);
1286	PyStructSequence_SET_ITEM(v, index+3, fval);
1287}
1288
1289/* pack a system stat C structure into the Python stat tuple
1290   (used by posix_stat() and posix_fstat()) */
1291static PyObject*
1292_pystat_fromstructstat(STRUCT_STAT *st)
1293{
1294	unsigned long ansec, mnsec, cnsec;
1295	PyObject *v = PyStructSequence_New(&StatResultType);
1296	if (v == NULL)
1297		return NULL;
1298
1299        PyStructSequence_SET_ITEM(v, 0, PyInt_FromLong((long)st->st_mode));
1300#ifdef HAVE_LARGEFILE_SUPPORT
1301        PyStructSequence_SET_ITEM(v, 1,
1302				  PyLong_FromLongLong((PY_LONG_LONG)st->st_ino));
1303#else
1304        PyStructSequence_SET_ITEM(v, 1, PyInt_FromLong((long)st->st_ino));
1305#endif
1306#if defined(HAVE_LONG_LONG) && !defined(MS_WINDOWS)
1307        PyStructSequence_SET_ITEM(v, 2,
1308				  PyLong_FromLongLong((PY_LONG_LONG)st->st_dev));
1309#else
1310        PyStructSequence_SET_ITEM(v, 2, PyInt_FromLong((long)st->st_dev));
1311#endif
1312        PyStructSequence_SET_ITEM(v, 3, PyInt_FromLong((long)st->st_nlink));
1313        PyStructSequence_SET_ITEM(v, 4, PyInt_FromLong((long)st->st_uid));
1314        PyStructSequence_SET_ITEM(v, 5, PyInt_FromLong((long)st->st_gid));
1315#ifdef HAVE_LARGEFILE_SUPPORT
1316        PyStructSequence_SET_ITEM(v, 6,
1317				  PyLong_FromLongLong((PY_LONG_LONG)st->st_size));
1318#else
1319        PyStructSequence_SET_ITEM(v, 6, PyInt_FromLong(st->st_size));
1320#endif
1321
1322#if defined(HAVE_STAT_TV_NSEC)
1323	ansec = st->st_atim.tv_nsec;
1324	mnsec = st->st_mtim.tv_nsec;
1325	cnsec = st->st_ctim.tv_nsec;
1326#elif defined(HAVE_STAT_TV_NSEC2)
1327	ansec = st->st_atimespec.tv_nsec;
1328	mnsec = st->st_mtimespec.tv_nsec;
1329	cnsec = st->st_ctimespec.tv_nsec;
1330#elif defined(HAVE_STAT_NSEC)
1331	ansec = st->st_atime_nsec;
1332	mnsec = st->st_mtime_nsec;
1333	cnsec = st->st_ctime_nsec;
1334#else
1335	ansec = mnsec = cnsec = 0;
1336#endif
1337	fill_time(v, 7, st->st_atime, ansec);
1338	fill_time(v, 8, st->st_mtime, mnsec);
1339	fill_time(v, 9, st->st_ctime, cnsec);
1340
1341#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1342	PyStructSequence_SET_ITEM(v, ST_BLKSIZE_IDX,
1343			 PyInt_FromLong((long)st->st_blksize));
1344#endif
1345#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1346	PyStructSequence_SET_ITEM(v, ST_BLOCKS_IDX,
1347			 PyInt_FromLong((long)st->st_blocks));
1348#endif
1349#ifdef HAVE_STRUCT_STAT_ST_RDEV
1350	PyStructSequence_SET_ITEM(v, ST_RDEV_IDX,
1351			 PyInt_FromLong((long)st->st_rdev));
1352#endif
1353#ifdef HAVE_STRUCT_STAT_ST_GEN
1354	PyStructSequence_SET_ITEM(v, ST_GEN_IDX,
1355			 PyInt_FromLong((long)st->st_gen));
1356#endif
1357#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
1358	{
1359	  PyObject *val;
1360	  unsigned long bsec,bnsec;
1361	  bsec = (long)st->st_birthtime;
1362#ifdef HAVE_STAT_TV_NSEC2
1363	  bnsec = st->st_birthtimespec.tv_nsec;
1364#else
1365	  bnsec = 0;
1366#endif
1367	  if (_stat_float_times) {
1368	    val = PyFloat_FromDouble(bsec + 1e-9*bnsec);
1369	  } else {
1370	    val = PyInt_FromLong((long)bsec);
1371	  }
1372	  PyStructSequence_SET_ITEM(v, ST_BIRTHTIME_IDX,
1373				    val);
1374	}
1375#endif
1376#ifdef HAVE_STRUCT_STAT_ST_FLAGS
1377	PyStructSequence_SET_ITEM(v, ST_FLAGS_IDX,
1378			 PyInt_FromLong((long)st->st_flags));
1379#endif
1380
1381	if (PyErr_Occurred()) {
1382		Py_DECREF(v);
1383		return NULL;
1384	}
1385
1386	return v;
1387}
1388
1389#ifdef MS_WINDOWS
1390
1391/* IsUNCRoot -- test whether the supplied path is of the form \\SERVER\SHARE\,
1392   where / can be used in place of \ and the trailing slash is optional.
1393   Both SERVER and SHARE must have at least one character.
1394*/
1395
1396#define ISSLASHA(c) ((c) == '\\' || (c) == '/')
1397#define ISSLASHW(c) ((c) == L'\\' || (c) == L'/')
1398#ifndef ARRAYSIZE
1399#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0]))
1400#endif
1401
1402static BOOL
1403IsUNCRootA(char *path, int pathlen)
1404{
1405	#define ISSLASH ISSLASHA
1406
1407	int i, share;
1408
1409	if (pathlen < 5 || !ISSLASH(path[0]) || !ISSLASH(path[1]))
1410		/* minimum UNCRoot is \\x\y */
1411		return FALSE;
1412	for (i = 2; i < pathlen ; i++)
1413		if (ISSLASH(path[i])) break;
1414	if (i == 2 || i == pathlen)
1415		/* do not allow \\\SHARE or \\SERVER */
1416		return FALSE;
1417	share = i+1;
1418	for (i = share; i < pathlen; i++)
1419		if (ISSLASH(path[i])) break;
1420	return (i != share && (i == pathlen || i == pathlen-1));
1421
1422	#undef ISSLASH
1423}
1424
1425#ifdef Py_WIN_WIDE_FILENAMES
1426static BOOL
1427IsUNCRootW(Py_UNICODE *path, int pathlen)
1428{
1429	#define ISSLASH ISSLASHW
1430
1431	int i, share;
1432
1433	if (pathlen < 5 || !ISSLASH(path[0]) || !ISSLASH(path[1]))
1434		/* minimum UNCRoot is \\x\y */
1435		return FALSE;
1436	for (i = 2; i < pathlen ; i++)
1437		if (ISSLASH(path[i])) break;
1438	if (i == 2 || i == pathlen)
1439		/* do not allow \\\SHARE or \\SERVER */
1440		return FALSE;
1441	share = i+1;
1442	for (i = share; i < pathlen; i++)
1443		if (ISSLASH(path[i])) break;
1444	return (i != share && (i == pathlen || i == pathlen-1));
1445
1446	#undef ISSLASH
1447}
1448#endif /* Py_WIN_WIDE_FILENAMES */
1449#endif /* MS_WINDOWS */
1450
1451static PyObject *
1452posix_do_stat(PyObject *self, PyObject *args,
1453	      char *format,
1454#ifdef __VMS
1455	      int (*statfunc)(const char *, STRUCT_STAT *, ...),
1456#else
1457	      int (*statfunc)(const char *, STRUCT_STAT *),
1458#endif
1459	      char *wformat,
1460	      int (*wstatfunc)(const Py_UNICODE *, STRUCT_STAT *))
1461{
1462	STRUCT_STAT st;
1463	char *path = NULL;	/* pass this to stat; do not free() it */
1464	char *pathfree = NULL;  /* this memory must be free'd */
1465	int res;
1466	PyObject *result;
1467
1468#ifdef Py_WIN_WIDE_FILENAMES
1469	/* If on wide-character-capable OS see if argument
1470	   is Unicode and if so use wide API.  */
1471	if (unicode_file_names()) {
1472		PyUnicodeObject *po;
1473		if (PyArg_ParseTuple(args, wformat, &po)) {
1474			Py_UNICODE *wpath = PyUnicode_AS_UNICODE(po);
1475
1476			Py_BEGIN_ALLOW_THREADS
1477				/* PyUnicode_AS_UNICODE result OK without
1478				   thread lock as it is a simple dereference. */
1479			res = wstatfunc(wpath, &st);
1480			Py_END_ALLOW_THREADS
1481
1482			if (res != 0)
1483				return win32_error_unicode("stat", wpath);
1484			return _pystat_fromstructstat(&st);
1485		}
1486		/* Drop the argument parsing error as narrow strings
1487		   are also valid. */
1488		PyErr_Clear();
1489	}
1490#endif
1491
1492	if (!PyArg_ParseTuple(args, format,
1493	                      Py_FileSystemDefaultEncoding, &path))
1494		return NULL;
1495	pathfree = path;
1496
1497	Py_BEGIN_ALLOW_THREADS
1498	res = (*statfunc)(path, &st);
1499	Py_END_ALLOW_THREADS
1500
1501	if (res != 0) {
1502#ifdef MS_WINDOWS
1503		result = win32_error("stat", pathfree);
1504#else
1505		result = posix_error_with_filename(pathfree);
1506#endif
1507	} 
1508	else
1509		result = _pystat_fromstructstat(&st);
1510
1511	PyMem_Free(pathfree);
1512	return result;
1513}
1514
1515/* POSIX methods */
1516
1517PyDoc_STRVAR(posix_access__doc__,
1518"access(path, mode) -> True if granted, False otherwise\n\n\
1519Use the real uid/gid to test for access to a path.  Note that most\n\
1520operations will use the effective uid/gid, therefore this routine can\n\
1521be used in a suid/sgid environment to test if the invoking user has the\n\
1522specified access to the path.  The mode argument can be F_OK to test\n\
1523existence, or the inclusive-OR of R_OK, W_OK, and X_OK.");
1524
1525static PyObject *
1526posix_access(PyObject *self, PyObject *args)
1527{
1528	char *path;
1529	int mode;
1530	
1531#ifdef Py_WIN_WIDE_FILENAMES
1532	DWORD attr;
1533	if (unicode_file_names()) {
1534		PyUnicodeObject *po;
1535		if (PyArg_ParseTuple(args, "Ui:access", &po, &mode)) {
1536			Py_BEGIN_ALLOW_THREADS
1537			/* PyUnicode_AS_UNICODE OK without thread lock as
1538			   it is a simple dereference. */
1539			attr = GetFileAttributesW(PyUnicode_AS_UNICODE(po));
1540			Py_END_ALLOW_THREADS
1541			goto finish;
1542		}
1543		/* Drop the argument parsing error as narrow strings
1544		   are also valid. */
1545		PyErr_Clear();
1546	}
1547	if (!PyArg_ParseTuple(args, "eti:access",
1548			      Py_FileSystemDefaultEncoding, &path, &mode))
1549		return 0;
1550	Py_BEGIN_ALLOW_THREADS
1551	attr = GetFileAttributesA(path);
1552	Py_END_ALLOW_THREADS
1553	PyMem_Free(path);
1554finish:
1555	if (attr == 0xFFFFFFFF)
1556		/* File does not exist, or cannot read attributes */
1557		return PyBool_FromLong(0);
1558	/* Access is possible if either write access wasn't requested, or
1559	   the file isn't read-only, or if it's a directory, as there are
1560	   no read-only directories on Windows. */
1561	return PyBool_FromLong(!(mode & 2) 
1562	                       || !(attr & FILE_ATTRIBUTE_READONLY)
1563			       || (attr & FILE_ATTRIBUTE_DIRECTORY));
1564#else
1565	int res;
1566	if (!PyArg_ParseTuple(args, "eti:access", 
1567			      Py_FileSystemDefaultEncoding, &path, &mode))
1568		return NULL;
1569	Py_BEGIN_ALLOW_THREADS
1570	res = access(path, mode);
1571	Py_END_ALLOW_THREADS
1572	PyMem_Free(path);
1573	return PyBool_FromLong(res == 0);
1574#endif
1575}
1576
1577#ifndef F_OK
1578#define F_OK 0
1579#endif
1580#ifndef R_OK
1581#define R_OK 4
1582#endif
1583#ifndef W_OK
1584#define W_OK 2
1585#endif
1586#ifndef X_OK
1587#define X_OK 1
1588#endif
1589
1590#ifdef HAVE_TTYNAME
1591PyDoc_STRVAR(posix_ttyname__doc__,
1592"ttyname(fd) -> string\n\n\
1593Return the name of the terminal device connected to 'fd'.");
1594
1595static PyObject *
1596posix_ttyname(PyObject *self, PyObject *args)
1597{
1598	int id;
1599	char *ret;
1600
1601	if (!PyArg_ParseTuple(args, "i:ttyname", &id))
1602		return NULL;
1603
1604#if defined(__VMS)
1605        /* file descriptor 0 only, the default input device (stdin) */
1606	if (id == 0) {
1607		ret = ttyname();
1608	}
1609	else {
1610		ret = NULL;
1611	}
1612#else
1613	ret = ttyname(id);
1614#endif
1615	if (ret == NULL)
1616		return posix_error();
1617	return PyString_FromString(ret);
1618}
1619#endif
1620
1621#ifdef HAVE_CTERMID
1622PyDoc_STRVAR(posix_ctermid__doc__,
1623"ctermid() -> string\n\n\
1624Return the name of the controlling terminal for this process.");
1625
1626static PyObject *
1627posix_ctermid(PyObject *self, PyObject *noargs)
1628{
1629        char *ret;
1630        char buffer[L_ctermid];
1631
1632#ifdef USE_CTERMID_R
1633	ret = ctermid_r(buffer);
1634#else
1635        ret = ctermid(buffer);
1636#endif
1637	if (ret == NULL)
1638		return posix_error();
1639	return PyString_FromString(buffer);
1640}
1641#endif
1642
1643PyDoc_STRVAR(posix_chdir__doc__,
1644"chdir(path)\n\n\
1645Change the current working directory to the specified path.");
1646
1647static PyObject *
1648posix_chdir(PyObject *self, PyObject *args)
1649{
1650#ifdef MS_WINDOWS
1651	return win32_1str(args, "chdir", "s:chdir", win32_chdir, "U:chdir", win32_wchdir);
1652#elif defined(PYOS_OS2) && defined(PYCC_GCC)
1653	return posix_1str(args, "et:chdir", _chdir2);
1654#elif defined(__VMS)
1655	return posix_1str(args, "et:chdir", (int (*)(const char *))chdir);
1656#else
1657	return posix_1str(args, "et:chdir", chdir);
1658#endif
1659}
1660
1661#ifdef HAVE_FCHDIR
1662PyDoc_STRVAR(posix_fchdir__doc__,
1663"fchdir(fildes)\n\n\
1664Change to the directory of the given file descriptor.  fildes must be\n\
1665opened on a directory, not a file.");
1666
1667static PyObject *
1668posix_fchdir(PyObject *self, PyObject *fdobj)
1669{
1670	return posix_fildes(fdobj, fchdir);
1671}
1672#endif /* HAVE_FCHDIR */
1673
1674
1675PyDoc_STRVAR(posix_chmod__doc__,
1676"chmod(path, mode)\n\n\
1677Change the access permissions of a file.");
1678
1679static PyObject *
1680posix_chmod(PyObject *self, PyObject *args)
1681{
1682	char *path = NULL;
1683	int i;
1684	int res;
1685#ifdef Py_WIN_WIDE_FILENAMES
1686	DWORD attr;
1687	if (unicode_file_names()) {
1688		PyUnicodeObject *po;
1689		if (PyArg_ParseTuple(args, "Ui|:chmod", &po, &i)) {
1690			Py_BEGIN_ALLOW_THREADS
1691			attr = GetFileAttributesW(PyUnicode_AS_UNICODE(po));
1692			if (attr != 0xFFFFFFFF) {
1693				if (i & _S_IWRITE)
1694					attr &= ~FILE_ATTRIBUTE_READONLY;
1695				else
1696					attr |= FILE_ATTRIBUTE_READONLY;
1697				res = SetFileAttributesW(PyUnicode_AS_UNICODE(po), attr);
1698			}
1699			else
1700				res = 0;
1701			Py_END_ALLOW_THREADS
1702			if (!res)
1703				return win32_error_unicode("chmod",
1704						PyUnicode_AS_UNICODE(po));
1705			Py_INCREF(Py_None);
1706			return Py_None;
1707		}
1708		/* Drop the argument parsing error as narrow strings
1709		   are also valid. */
1710		PyErr_Clear();
1711	}
1712	if (!PyArg_ParseTuple(args, "eti:chmod", Py_FileSystemDefaultEncoding,
1713	                      &path, &i))
1714		return NULL;
1715	Py_BEGIN_ALLOW_THREADS
1716	attr = GetFileAttributesA(path);
1717	if (attr != 0xFFFFFFFF) {
1718		if (i & _S_IWRITE)
1719			attr &= ~FILE_ATTRIBUTE_READONLY;
1720		else
1721			attr |= FILE_ATTRIBUTE_READONLY;
1722		res = SetFileAttributesA(path, attr);
1723	}
1724	else
1725		res = 0;
1726	Py_END_ALLOW_THREADS
1727	if (!res) {
1728		win32_error("chmod", path);
1729		PyMem_Free(path);
1730		return NULL;
1731	}
1732	PyMem_Free(path);
1733	Py_INCREF(Py_None);
1734	return Py_None;
1735#else /* Py_WIN_WIDE_FILENAMES */
1736	if (!PyArg_ParseTuple(args, "eti:chmod", Py_FileSystemDefaultEncoding,
1737	                      &path, &i))
1738		return NULL;
1739	Py_BEGIN_ALLOW_THREADS
1740	res = chmod(path, i);
1741	Py_END_ALLOW_THREADS
1742	if (res < 0)
1743		return posix_error_with_allocated_filename(path);
1744	PyMem_Free(path);
1745	Py_INCREF(Py_None);
1746	return Py_None;
1747#endif
1748}
1749
1750#ifdef HAVE_FCHMOD
1751PyDoc_STRVAR(posix_fchmod__doc__,
1752"fchmod(fd, mode)\n\n\
1753Change the access permissions of the file given by file\n\
1754descriptor fd.");
1755
1756static PyObject *
1757posix_fchmod(PyObject *self, PyObject *args)
1758{
1759	int fd, mode, res;
1760	if (!PyArg_ParseTuple(args, "ii:fchmod", &fd, &mode))
1761		return NULL;
1762	Py_BEGIN_ALLOW_THREADS
1763	res = fchmod(fd, mode);
1764	Py_END_ALLOW_THREADS
1765	if (res < 0)
1766		return posix_error();
1767	Py_RETURN_NONE;
1768}
1769#endif /* HAVE_FCHMOD */
1770
1771#ifdef HAVE_LCHMOD
1772PyDoc_STRVAR(posix_lchmod__doc__,
1773"lchmod(path, mode)\n\n\
1774Change the access permissions of a file. If path is a symlink, this\n\
1775affects the link itself rather than the target.");
1776
1777static PyObject *
1778posix_lchmod(PyObject *self, PyObject *args)
1779{
1780	char *path = NULL;
1781	int i;
1782	int res;
1783	if (!PyArg_ParseTuple(args, "eti:lchmod", Py_FileSystemDefaultEncoding,
1784	                      &path, &i))
1785		return NULL;
1786	Py_BEGIN_ALLOW_THREADS
1787	res = lchmod(path, i);
1788	Py_END_ALLOW_THREADS
1789	if (res < 0)
1790		return posix_error_with_allocated_filename(path);
1791	PyMem_Free(path);
1792	Py_RETURN_NONE;
1793}
1794#endif /* HAVE_LCHMOD */
1795
1796
1797#ifdef HAVE_CHFLAGS
1798PyDoc_STRVAR(posix_chflags__doc__,
1799"chflags(path, flags)\n\n\
1800Set file flags.");
1801
1802static PyObject *
1803posix_chflags(PyObject *self, PyObject *args)
1804{
1805	char *path;
1806	unsigned long flags;
1807	int res;
1808	if (!PyArg_ParseTuple(args, "etk:chflags",
1809			      Py_FileSystemDefaultEncoding, &path, &flags))
1810		return NULL;
1811	Py_BEGIN_ALLOW_THREADS
1812	res = chflags(path, flags);
1813	Py_END_ALLOW_THREADS
1814	if (res < 0)
1815		return posix_error_with_allocated_filename(path);
1816	PyMem_Free(path);
1817	Py_INCREF(Py_None);
1818	return Py_None;
1819}
1820#endif /* HAVE_CHFLAGS */
1821
1822#ifdef HAVE_LCHFLAGS
1823PyDoc_STRVAR(posix_lchflags__doc__,
1824"lchflags(path, flags)\n\n\
1825Set file flags.\n\
1826This function will not follow symbolic links.");
1827
1828static PyObject *
1829posix_lchflags(PyObject *self, PyObject *args)
1830{
1831	char *path;
1832	unsigned long flags;
1833	int res;
1834	if (!PyArg_ParseTuple(args, "etk:lchflags",
1835			      Py_FileSystemDefaultEncoding, &path, &flags))
1836		return NULL;
1837	Py_BEGIN_ALLOW_THREADS
1838	res = lchflags(path, flags);
1839	Py_END_ALLOW_THREADS
1840	if (res < 0)
1841		return posix_error_with_allocated_filename(path);
1842	PyMem_Free(path);
1843	Py_INCREF(Py_None);
1844	return Py_None;
1845}
1846#endif /* HAVE_LCHFLAGS */
1847
1848#ifdef HAVE_CHROOT
1849PyDoc_STRVAR(posix_chroot__doc__,
1850"chroot(path)\n\n\
1851Change root directory to path.");
1852
1853static PyObject *
1854posix_chroot(PyObject *self, PyObject *args)
1855{
1856	return posix_1str(args, "et:chroot", chroot);
1857}
1858#endif
1859
1860#ifdef HAVE_FSYNC
1861PyDoc_STRVAR(posix_fsync__doc__,
1862"fsync(fildes)\n\n\
1863force write of file with filedescriptor to disk.");
1864
1865static PyObject *
1866posix_fsync(PyObject *self, PyObject *fdobj)
1867{
1868       return posix_fildes(fdobj, fsync);
1869}
1870#endif /* HAVE_FSYNC */
1871
1872#ifdef HAVE_FDATASYNC
1873
1874#ifdef __hpux
1875extern int fdatasync(int); /* On HP-UX, in libc but not in unistd.h */
1876#endif
1877
1878PyDoc_STRVAR(posix_fdatasync__doc__,
1879"fdatasync(fildes)\n\n\
1880force write of file with filedescriptor to disk.\n\
1881 does not force update of metadata.");
1882
1883static PyObject *
1884posix_fdatasync(PyObject *self, PyObject *fdobj)
1885{
1886       return posix_fildes(fdobj, fdatasync);
1887}
1888#endif /* HAVE_FDATASYNC */
1889
1890
1891#ifdef HAVE_CHOWN
1892PyDoc_STRVAR(posix_chown__doc__,
1893"chown(path, uid, gid)\n\n\
1894Change the owner and group id of path to the numeric uid and gid.");
1895
1896static PyObject *
1897posix_chown(PyObject *self, PyObject *args)
1898{
1899	char *path = NULL;
1900	long uid, gid;
1901	int res;
1902	if (!PyArg_ParseTuple(args, "etll:chown",
1903	                      Py_FileSystemDefaultEncoding, &path,
1904	                      &uid, &gid))
1905		return NULL;
1906	Py_BEGIN_ALLOW_THREADS
1907	res = chown(path, (uid_t) uid, (gid_t) gid);
1908	Py_END_ALLOW_THREADS
1909	if (res < 0)
1910		return posix_error_with_allocated_filename(path);
1911	PyMem_Free(path);
1912	Py_INCREF(Py_None);
1913	return Py_None;
1914}
1915#endif /* HAVE_CHOWN */
1916
1917#ifdef HAVE_FCHOWN
1918PyDoc_STRVAR(posix_fchown__doc__,
1919"fchown(fd, uid, gid)\n\n\
1920Change the owner and group id of the file given by file descr…

Large files files are truncated, but you can click here to view the full file