pike.git / src / modules / _Stdio / efuns.c

version» Context lines:

pike.git/src/modules/_Stdio/efuns.c:1: + /* + || This file is part of Pike. For copyright information see COPYRIGHT. + || Pike is distributed under GPL, LGPL and MPL. See the file COPYING + || for more information. + */    -  + #include "global.h" + #include "fdlib.h" + #include "interpret.h" + #include "svalue.h" + #include "stralloc.h" + #include "array.h" + #include "mapping.h" + #include "pike_macros.h" + #include "fd_control.h" + #include "threads.h" + #include "module_support.h" + #include "constants.h" + #include "backend.h" + #include "operators.h" + #include "builtin_functions.h" + #include "pike_security.h" + #include "bignum.h" +  + #include "file_machine.h" + #include "file.h" +  + #ifdef HAVE_SYS_TYPES_H + #include <sys/types.h> + #endif + #include <sys/stat.h> + #ifdef HAVE_SYS_PARAM_H + #include <sys/param.h> + #endif + #include <signal.h> + #include <errno.h> +  + #ifdef HAVE_LIMITS_H + #include <limits.h> + #endif /* HAVE_LIMITS_H */ +  + #ifdef HAVE_DIRENT_H + # include <dirent.h> + # define NAMLEN(dirent) strlen((dirent)->d_name) + #else + # ifdef HAVE_SYS_NDIR_H + # include <sys/ndir.h> + # define dirent direct + # define NAMLEN(dirent) (dirent)->d_namlen + # else /* !HAVE_SYS_NDIR_H */ + # ifdef HAVE_SYS_DIR_H + # include <sys/dir.h> + # define dirent direct + # define NAMLEN(dirent) (dirent)->d_namlen + # else /* !HAVE_SYS_DIR_H */ + # ifdef HAVE_NDIR_H + # include <ndir.h> + # define dirent direct + # define NAMLEN(dirent) (dirent)->d_namlen + # else /* !HAVE_NDIR_H */ + # ifdef HAVE_DIRECT_H + # include <direct.h> + # define NAMLEN(dirent) strlen((dirent)->d_name) + # endif /* HAVE_DIRECT_H */ + # endif /* HAVE_NDIR_H */ + # endif /* HAVE_SYS_DIR_H */ + # endif /* HAVE_SYS_NDIR_H */ + #endif /* HAVE_DIRENT_H */ +  + #include "dmalloc.h" +  + #ifdef HAVE_PROCESS_H + #include <process.h> + #endif +  + #ifdef HAVE_SYS_XATTR_H + #include <sys/xattr.h> + #endif /* HAVE_SYS_XATTR_H */ +  + #if 0 + #ifdef HAVE_LIBZFS_INIT + #ifdef HAVE_LIBZFS_H + #include <libzfs.h> + #endif /* HAVE_LIBZFS_H */ + static libzfs_handle_t *libzfs_handle; + #endif /* HAVE_LIBZFS_INIT */ + #endif /* 0 */ +  + #define sp Pike_sp +  + /* #define DEBUG_FILE */ + /* #define READDIR_DEBUG */ +  + #ifdef __NT__ +  + #include <winbase.h> + #include <io.h> +  + /* Old versions of the headerfiles don't have this constant... */ + #ifndef INVALID_SET_FILE_POINTER + #define INVALID_SET_FILE_POINTER ((DWORD)-1) + #endif +  + /* Dynamic load of functions that doesn't exist in all Windows versions. */ +  + static HINSTANCE kernel32lib; +  + #define LINKFUNC(RET,NAME,TYPE) \ +  typedef RET (WINAPI * PIKE_CONCAT(NAME,type)) TYPE ; \ +  static PIKE_CONCAT(NAME,type) NAME +  + LINKFUNC(BOOL, movefileex, ( +  LPCTSTR lpExistingFileName, /* file name */ +  LPCTSTR lpNewFileName, /* new file name */ +  DWORD dwFlags /* move options */ + )); +  + #endif /* __NT__ */ +  + struct array *encode_stat(PIKE_STAT_T *s) + { +  struct array *a; +  a=allocate_array(7); +  a->type_field = BIT_INT; +  ITEM(a)[0].u.integer=s->st_mode; +  switch(S_IFMT & s->st_mode) +  { +  case S_IFREG: +  push_int64((INT64)s->st_size); +  stack_pop_to_no_free (ITEM(a) + 1); +  if (TYPEOF(ITEM(a)[1]) == T_OBJECT) a->type_field |= BIT_OBJECT; +  break; +  +  case S_IFDIR: ITEM(a)[1].u.integer=-2; break; + #ifdef S_IFLNK +  case S_IFLNK: ITEM(a)[1].u.integer=-3; break; + #endif +  default: + #ifdef DEBUG_FILE +  fprintf(stderr, "encode_stat(): mode:%ld\n", (long)S_IFMT & s->st_mode); + #endif /* DEBUG_FILE */ +  ITEM(a)[1].u.integer=-4; +  break; +  } +  ITEM(a)[2].u.integer = DO_NOT_WARN ((INT_TYPE) s->st_atime); +  ITEM(a)[3].u.integer = DO_NOT_WARN ((INT_TYPE) s->st_mtime); +  ITEM(a)[4].u.integer = DO_NOT_WARN ((INT_TYPE) s->st_ctime); +  ITEM(a)[5].u.integer=s->st_uid; +  ITEM(a)[6].u.integer=s->st_gid; +  return a; + } +  + #if defined(HAVE_FSETXATTR) && defined(HAVE_FGETXATTR) && defined(HAVE_FLISTXATTR) + /*! @decl array(string) listxattr( string file, void|int(0..1) symlink ) +  *! +  *! Return an array of all extended attributes set on the file +  */ +  + #ifdef HAVE_DARWIN_XATTR + #define LISTXATTR(PATH, BUF, SZ) listxattr(PATH, BUF, SZ, 0) + #define LLISTXATTR(PATH, BUF, SZ) listxattr(PATH, BUF, SZ, XATTR_NOFOLLOW) + #else + #define LISTXATTR(PATH, BUF, SZ) listxattr(PATH, BUF, SZ) + #define LLISTXATTR(PATH, BUF, SZ) llistxattr(PATH, BUF, SZ) + #endif /* !HAVE_DARWIN_XATTR */ +  + static void f_listxattr(INT32 args) + { +  char buffer[1024]; +  char *ptr = buffer; +  char *name; +  int do_free = 0; +  int nofollow = 0; +  ssize_t res; +  get_all_args( "listxattr", args, "%s.%d", &name, &nofollow ); +  +  THREADS_ALLOW(); +  do { +  /* First try, for speed.*/ +  if (nofollow) +  res = LLISTXATTR(name, buffer, sizeof(buffer)); +  else +  res = LISTXATTR( name, buffer, sizeof(buffer)); +  } while( res < 0 && errno == EINTR ); +  THREADS_DISALLOW(); +  +  if( res<0 && errno==ERANGE ) +  { +  /* Too little space in stackbuffer.*/ +  size_t blen = 65536; +  ptr = NULL; +  do_free = 1; +  do { +  if (!ptr) { +  ptr = xalloc(blen); +  } else { +  char *tmp = realloc( ptr, blen ); +  if( !tmp ) +  break; +  ptr = tmp; +  } +  THREADS_ALLOW(); +  do { +  if (nofollow) +  res = LLISTXATTR(name, ptr, sizeof(blen)); +  else +  res = LISTXATTR(name, ptr, blen); +  } while( res < 0 && errno == EINTR ); +  THREADS_DISALLOW(); +  blen *= 2; +  } while( (res < 0) && (errno == ERANGE) ); +  } +  +  pop_n_elems( args ); +  if (res < 0) +  { +  if (do_free) +  free(ptr); +  push_int(0); +  return; +  } +  +  push_string( make_shared_binary_string( ptr, res ) ); +  ptr[0]=0; +  push_string( make_shared_binary_string( ptr, 1 ) ); +  o_divide(); +  push_empty_string(); +  f_aggregate(1); +  o_subtract(); +  +  if (do_free) +  free(ptr); + } +  + #ifdef HAVE_DARWIN_XATTR + #define GETXATTR(PATH, NAME, BUF, SZ) getxattr(PATH, NAME, BUF, SZ, 0, 0) + #define LGETXATTR(PATH, NAME, BUF, SZ) getxattr(PATH, NAME, BUF, SZ, 0, XATTR_NOFOLLOW) + #else + #define GETXATTR(PATH, NAME, BUF, SZ) getxattr(PATH, NAME, BUF, SZ) + #define LGETXATTR(PATH, NAME, BUF, SZ) lgetxattr(PATH, NAME, BUF, SZ) + #endif /* !HAVE_DARWIN_XATTR */ +  + /*! @decl string getxattr(string file, string attr, void|int(0..1) symlink) +  *! +  *! Return the value of a specified attribute, or 0 if it does not exist. +  */ + static void f_getxattr(INT32 args) + { +  char buffer[1024]; +  char *ptr = buffer; +  int do_free = 0; +  ssize_t res; +  char *name, *file; +  int nofollow=0; +  get_all_args( "getxattr", args, "%s%s.%d", &file, &name, &nofollow ); +  +  THREADS_ALLOW(); +  do { +  /* First try, for speed.*/ +  if (nofollow) +  res = LGETXATTR(file, name, buffer, sizeof(buffer)); +  else +  res = GETXATTR(file, name, buffer, sizeof(buffer)); +  } while( res < 0 && errno == EINTR ); +  THREADS_DISALLOW(); +  +  if( res<0 && errno==ERANGE ) +  { +  /* Too little space in buffer.*/ +  size_t blen = 65536; +  do_free = 1; +  ptr = NULL; +  do { +  if (!ptr) { +  ptr = xalloc(blen); +  } else { +  char *tmp = realloc( ptr, blen ); +  if( !tmp ) +  break; +  ptr = tmp; +  } +  THREADS_ALLOW(); +  do { +  if (nofollow) +  res = LGETXATTR(file, name, ptr, blen); +  else +  res = GETXATTR(file, name, ptr, blen); +  } while( res < 0 && errno == EINTR ); +  THREADS_DISALLOW(); +  blen *= 2; +  } while( (res < 0) && (errno == ERANGE) ); +  } +  +  if( res < 0 ) +  { +  if( do_free && ptr ) +  free(ptr); +  push_int(0); +  return; +  } +  +  push_string( make_shared_binary_string( ptr, res ) ); +  if( do_free && ptr ) +  free( ptr ); + } +  +  + #ifdef HAVE_DARWIN_XATTR + #define REMOVEXATTR(PATH, NAME) removexattr(PATH, NAME, 0) + #define LREMOVEXATTR(PATH, NAME) removexattr(PATH, NAME, XATTR_NOFOLLOW) + #else + #define REMOVEXATTR(PATH, NAME) removexattr(PATH, NAME) + #define LREMOVEXATTR(PATH, NAME) lremovexattr(PATH, NAME) + #endif /* !HAVE_DARWIN_XATTR */ +  + /*! @decl void removexattr( string file, string attr , void|int(0..1) symlink) +  *! Remove the specified extended attribute. +  */ + static void f_removexattr( INT32 args ) + { +  char *name, *file; +  int nofollow=0, rv; +  +  get_all_args( "removexattr", args, "%s%s.%d", &file, &name, &nofollow ); +  +  THREADS_ALLOW(); +  if (nofollow) { +  while(((rv=LREMOVEXATTR(file, name)) < 0) && (errno == EINTR)) +  ; +  } else { +  while(((rv=REMOVEXATTR(file, name)) < 0) && (errno == EINTR)) +  ; +  } +  THREADS_DISALLOW(); +  +  pop_n_elems(args); +  if( rv < 0 ) +  { +  push_int(0); +  } +  else +  { +  push_int(1); +  } + } +  + #ifdef HAVE_DARWIN_XATTR + #define SETXATTR(PATH, NAME, BUF, SZ, FL) setxattr(PATH, NAME, BUF, SZ, 0, FL) + #define LSETXATTR(PATH, NAME, BUF, SZ, FL) setxattr(PATH, NAME, BUF, SZ, 0, (FL)|XATTR_NOFOLLOW) + #else + #define SETXATTR(PATH, NAME, BUF, SZ, FL) setxattr(PATH, NAME, BUF, SZ, FL) + #define LSETXATTR(PATH, NAME, BUF, SZ, FL) lsetxattr(PATH, NAME, BUF, SZ, FL) + #endif /* !HAVE_DARWIN_XATTR */ +  + /*! @decl void setxattr( string file, string attr, string value, int flags,void|int(0..1) symlink) +  *! +  *! Set the attribute @[attr] to the value @[value]. +  *! +  *! The flags parameter can be used to refine the semantics of the operation. +  *! +  *! @[Stdio.XATTR_CREATE] specifies a pure create, which +  *! fails if the named attribute exists already. +  *! +  *! @[Stdio.XATTR_REPLACE] specifies a pure replace operation, which +  *! fails if the named attribute does not already exist. +  *! +  *! By default (no flags), the extended attribute will be created if need be, +  *! or will simply replace the value if the attribute exists. +  *! +  *! @returns +  *! 1 if successful, 0 otherwise, setting errno. +  */ + static void f_setxattr( INT32 args ) + { +  char *ind, *file; +  struct pike_string *val; +  int flags; +  int rv; +  int nofollow=0; +  get_all_args( "setxattr", args, "%s%s%S%d.%d", &file, &ind, &val, &flags, &nofollow ); +  +  THREADS_ALLOW(); +  if (nofollow) { +  while(((rv=LSETXATTR(file, ind, val->str, +  (val->len<<val->size_shift), flags )) < 0) && +  (errno == EINTR)) +  ; +  } else { +  while(((rv=SETXATTR(file, ind, val->str, +  (val->len<<val->size_shift), flags )) < 0) && +  (errno == EINTR)) +  ; +  } +  THREADS_DISALLOW(); +  pop_n_elems(args); +  if( rv < 0 ) +  { +  push_int(0); +  } +  else +  push_int(1); + } + #endif +  +  + /*! @decl Stdio.Stat file_stat(string path, void|int(0..1) symlink) +  *! +  *! Stat a file. +  *! +  *! If the argument @[symlink] is @expr{1@} symlinks will not be followed. +  *! +  *! @returns +  *! If the path specified by @[path] doesn't exist @expr{0@} (zero) will +  *! be returned. +  *! +  *! Otherwise an object describing the properties of @[path] will be +  *! returned. +  *! +  *! @note +  *! In Pike 7.0 and earlier this function returned an array with 7 elements. +  *! The old behaviour can be simulated with the following function: +  *! @code +  *! array(int) file_stat(string path, void|int(0..1) symlink) +  *! { +  *! File.Stat st = predef::file_stat(path, symlink); +  *! if (!st) return 0; +  *! return (array(int))st; +  *! } +  *! @endcode +  *! +  *! @seealso +  *! @[Stdio.Stat], @[Stdio.File->stat()] +  */ + void f_file_stat(INT32 args) + { +  PIKE_STAT_T st; +  int i, l; +  struct pike_string *str; +  +  VALID_FILE_IO("file_stat","read"); +  +  if(args<1) +  SIMPLE_TOO_FEW_ARGS_ERROR("file_stat", 1); +  if((TYPEOF(sp[-args]) != T_STRING) || sp[-args].u.string->size_shift) +  SIMPLE_BAD_ARG_ERROR("file_stat", 1, "string(0..255)"); +  +  str = sp[-args].u.string; +  l = (args>1 && !UNSAFE_IS_ZERO(sp+1-args))?1:0; +  +  if (string_has_null(str)) { +  /* Filenames with NUL are not supported. */ +  errno = ENOENT; +  pop_n_elems(args); +  push_int(0); +  return; +  } +  +  THREADS_ALLOW_UID(); +  do { + #ifdef HAVE_LSTAT +  if(l) +  i=fd_lstat(str->str, &st); +  else + #endif +  i=fd_stat(str->str, &st); +  } while ((i == -1) && (errno == EINTR)); +  +  THREADS_DISALLOW_UID(); +  pop_n_elems(args); +  if(i==-1) +  { +  push_int(0); +  }else{ +  push_stat(&st); +  } + } +  + /*! @decl int file_truncate(string file, int length) +  *! +  *! Truncates the file @[file] to the length specified in @[length]. +  *! +  *! @returns +  *! Returns 1 if ok, 0 if failed. +  */ + void f_file_truncate(INT32 args) + { + #if defined(INT64) +  INT64 len = 0; + #else +  off_t len = 0; + #endif +  struct pike_string *str; +  int res; +  +  VALID_FILE_IO("file_truncate","write"); +  +  if(args < 2) +  SIMPLE_TOO_FEW_ARGS_ERROR("file_truncate", 2); +  if(TYPEOF(sp[-args]) != T_STRING) +  SIMPLE_BAD_ARG_ERROR("file_truncate", 1, "string"); +  + #if defined (INT64) + #if defined (HAVE_FTRUNCATE64) || SIZEOF_OFF_T > SIZEOF_INT_TYPE +  if(is_bignum_object_in_svalue(&Pike_sp[1-args])) { +  if (!int64_from_bignum(&len, Pike_sp[1-args].u.object)) +  Pike_error ("Bad argument 2 to file_truncate(). Length too large.\n"); +  } +  else + #endif + #endif +  if(TYPEOF(sp[1-args]) != T_INT) +  SIMPLE_BAD_ARG_ERROR("file_truncate", 2, "int"); +  else +  len = sp[1-args].u.integer; +  +  str = sp[-args].u.string; +  +  if (string_has_null(str)) { +  /* Filenames with NUL are not supported. */ +  errno = ENOENT; +  pop_n_elems(args); +  push_int(0); +  return; +  } +  + #ifdef __NT__ +  { +  HANDLE h = CreateFile(str->str, GENERIC_WRITE, +  FILE_SHARE_READ|FILE_SHARE_WRITE, +  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +  if(h == DO_NOT_WARN(INVALID_HANDLE_VALUE)) { +  errno = GetLastError(); +  res=-1; +  } else { +  LONG high; +  DWORD err; + #ifdef INT64 +  high = DO_NOT_WARN ((LONG) (len >> 32)); +  len &= ((INT64) 1 << 32) - 1; + #else +  high = 0; + #endif +  if (SetFilePointer(h, DO_NOT_WARN ((LONG) len), &high, FILE_BEGIN) == +  INVALID_SET_FILE_POINTER && +  (err = GetLastError()) != NO_ERROR) { +  errno = err; +  res = -1; +  } +  else if (!SetEndOfFile(h)) { +  errno = GetLastError(); +  res=-1; +  } +  else +  res = 0; +  CloseHandle(h); +  } +  } + #else /* !__NT__ */ + #ifdef HAVE_TRUNCATE64 +  res = truncate64 (str->str, len); + #else +  res=truncate(str->str, len); + #endif + #endif /* __NT__ */ +  +  pop_n_elems(args); +  +  push_int(!res); + } +  + /*! @decl mapping(string:int) filesystem_stat(string path) +  *! +  *! Returns a mapping describing the properties of the filesystem +  *! containing the path specified by @[path]. +  *! +  *! @returns +  *! If a filesystem cannot be determined @expr{0@} (zero) will be returned. +  *! +  *! Otherwise a mapping(string:int) with the following fields will be +  *! returned: +  *! @mapping +  *! @member int "blocksize" +  *! Size in bytes of the filesystem blocks. +  *! @member int "blocks" +  *! Size of the entire filesystem in blocks. +  *! @member int "bfree" +  *! Number of free blocks in the filesystem. +  *! @member int "bavail" +  *! Number of available blocks in the filesystem. +  *! This is usually somewhat less than the @expr{"bfree"@} value, and +  *! can usually be adjusted with eg tunefs(1M). +  *! @member int "files" +  *! Total number of files (aka inodes) allowed by this filesystem. +  *! @member int "ffree" +  *! Number of free files in the filesystem. +  *! @member int "favail" +  *! Number of available files in the filesystem. +  *! This is usually the same as the @expr{"ffree"@} value, and can +  *! usually be adjusted with eg tunefs(1M). +  *! @member string "fsname" +  *! Name assigned to the filesystem. This item is not available +  *! on all systems. +  *! @member string "fstype" +  *! Type of filesystem (eg @expr{"nfs"@}). This item is not +  *! available on all systems. +  *! @endmapping +  *! +  *! @note +  *! Please note that not all members are present on all OSs. +  *! +  *! @seealso +  *! @[file_stat()] +  */ + #ifdef __NT__ +  + void f_filesystem_stat( INT32 args ) + { +  char *path; +  DWORD sectors_per_cluster = -1; +  DWORD bytes_per_sector = -1; +  DWORD free_clusters = -1; +  DWORD total_clusters = -1; +  char _p[4]; +  char *p = _p; +  unsigned int free_sectors; +  unsigned int total_sectors; +  +  VALID_FILE_IO("filesystem_stat","read"); +  +  get_all_args( "filesystem_stat", args, "%s", &path ); +  +  if(sp[-1].u.string->len < 2 || path[1] != ':') +  { +  p = 0; +  } else { +  p[0] = path[0]; +  p[1] = ':'; +  p[2] = '\\'; +  p[3] = 0; +  } +  +  if(!GetDiskFreeSpace( p, &sectors_per_cluster, +  &bytes_per_sector, +  &free_clusters, +  &total_clusters )) +  { +  pop_n_elems(args); +  push_int( 0 ); +  return; +  } +  +  free_sectors = sectors_per_cluster * free_clusters; +  total_sectors = sectors_per_cluster * total_clusters; +  +  pop_n_elems( args ); +  push_text("blocksize"); +  push_int(bytes_per_sector); +  push_text("blocks"); +  push_int(total_sectors); +  push_text("bfree"); +  push_int(free_sectors); +  push_text("bavail"); +  push_int(free_sectors); +  f_aggregate_mapping( 8 ); + } +  + #else /* !__NT__ */ +  + #if !defined(HAVE_STRUCT_STATFS) && !defined(HAVE_STRUCT_FS_DATA) + #undef HAVE_STATFS + #endif +  + #if defined(HAVE_STATVFS) || defined(HAVE_STATFS) || defined(HAVE_USTAT) + #ifdef HAVE_SYS_STATVFS_H + /* Kludge for broken SCO headerfiles */ + #ifdef _SVID3 + #include <sys/statvfs.h> + #else + #define _SVID3 + #include <sys/statvfs.h> + #undef _SVID3 + #endif /* _SVID3 */ + #endif /* HAVE_SYS_STATVFS_H */ + #ifdef HAVE_SYS_VFS_H + #include <sys/vfs.h> + #endif /* HAVE_SYS_VFS_H */ + #ifdef HAVE_SYS_STATFS_H + #include <sys/statfs.h> + #endif /* HAVE_SYS_STATFS_H */ + #ifdef HAVE_SYS_PARAM_H + #include <sys/param.h> + #endif /* HAVE_SYS_PARAM_H */ + #ifdef HAVE_SYS_MOUNT_H + #include <sys/mount.h> + #endif /* HAVE_SYS_MOUNT_H */ + #if !defined(HAVE_STATVFS) && !defined(HAVE_STATFS) + #ifdef HAVE_USTAT_H + #include <ustat.h> + #endif /* HAVE_USTAT_H */ + #endif /* !HAVE_STATVFS && !HAVE_STATFS */ + void f_filesystem_stat(INT32 args) + { + #ifdef HAVE_STATVFS +  struct statvfs st; + #else /* !HAVE_STATVFS */ + #ifdef HAVE_STATFS + #ifdef HAVE_STRUCT_STATFS +  struct statfs st; + #else /* !HAVE_STRUCT_STATFS */ + #ifdef HAVE_STRUCT_FS_DATA +  /* Probably only ULTRIX has this name for the struct */ +  struct fs_data st; + #else /* !HAVE_STRUCT_FS_DATA */ +  /* Should not be reached */ + #error No struct to hold statfs() data. + #endif /* HAVE_STRUCT_FS_DATA */ + #endif /* HAVE_STRUCT_STATFS */ + #else /* !HAVE_STATFS */ + #ifdef HAVE_USTAT +  PIKE_STAT_T statbuf; +  struct ustat st; + #else /* !HAVE_USTAT */ +  /* Should not be reached */ + #error No stat function for filesystems. + #endif /* HAVE_USTAT */ + #endif /* HAVE_STATFS */ + #endif /* HAVE_STATVFS */ +  int i; +  struct pike_string *str; +  +  if(args<1) +  SIMPLE_TOO_FEW_ARGS_ERROR("filesystem_stat", 1); +  if(TYPEOF(sp[-args]) != T_STRING) +  SIMPLE_BAD_ARG_ERROR("filesystem_stat", 1, "string"); +  +  str = sp[-args].u.string; +  +  if (string_has_null(str)) { +  /* Filenames with NUL are not supported. */ +  errno = ENOENT; +  pop_n_elems(args); +  push_int(0); +  return; +  } +  +  THREADS_ALLOW(); + #ifdef HAVE_STATVFS +  i = statvfs(str->str, &st); + #else /* !HAVE_STATVFS */ + #ifdef HAVE_STATFS + #ifdef HAVE_SYSV_STATFS +  i = statfs(str->str, &st, sizeof(st), 0); + #else +  i = statfs(str->str, &st); + #endif /* HAVE_SYSV_STATFS */ + #else /* !HAVE_STATFS */ + #ifdef HAVE_USTAT +  if (!(i = fd_stat(str->str, &statbuf))) { +  i = ustat(statbuf.st_rdev, &st); +  } + #else +  /* Should not be reached */ + #error No stat function for filesystems. + #endif /* HAVE_USTAT */ + #endif /* HAVE_STATFS */ + #endif /* HAVE_STATVFS */ +  THREADS_DISALLOW(); +  pop_n_elems(args); +  if(i==-1) +  { +  push_int(0); +  }else{ +  int num_fields = 0; +  char *fsname = NULL; + #ifdef HAVE_STATVFS + #if 0 +  push_text("id"); push_int(st.f_fsid); +  num_fields++; + #endif +  push_text("blocksize"); push_int(st.f_frsize); +  push_text("blocks"); push_int(st.f_blocks); +  push_text("bfree"); push_int(st.f_bfree); +  push_text("bavail"); push_int(st.f_bavail); +  push_text("files"); push_int(st.f_files); +  push_text("ffree"); push_int(st.f_ffree); +  push_text("favail"); push_int(st.f_favail); +  num_fields += 7; + #ifdef HAVE_STATVFS_F_FSTR +  push_text("fsname"); push_text(fsname = st.f_fstr); +  num_fields++; + #endif /* HAVE_STATVFS_F_FSTR */ + #ifdef HAVE_STATVFS_F_BASETYPE +  push_text("fstype"); push_text(st.f_basetype); +  num_fields++; + #endif /* HAVE_STATVFS_F_BASETYPE */ + #else /* !HAVE_STATVFS */ + #ifdef HAVE_STATFS + #ifdef HAVE_STRUCT_STATFS + #if 0 && HAVE_STATFS_F_FSID +  push_text("id"); push_int(st.f_fsid); +  num_fields++; + #endif +  push_text("blocksize"); push_int(st.f_bsize); +  push_text("blocks"); push_int(st.f_blocks); +  push_text("bfree"); push_int(st.f_bfree); +  push_text("files"); push_int(st.f_files); +  push_text("ffree"); push_int(st.f_ffree); +  push_text("favail"); push_int(st.f_ffree); +  num_fields += 6; + #ifdef HAVE_STATFS_F_BAVAIL +  push_text("bavail"); push_int(st.f_bavail); +  num_fields++; + #endif /* HAVE_STATFS_F_BAVAIL */ + #else /* !HAVE_STRUCT_STATFS */ + #ifdef HAVE_STRUCT_FS_DATA +  /* ULTRIX */ +  push_text("blocksize"); push_int(st.fd_bsize); +  push_text("blocks"); push_int(st.fd_btot); +  push_text("bfree"); push_int(st.fd_bfree); +  push_text("bavail"); push_int(st.fd_bfreen); +  num_fields += 4; + #else /* !HAVE_STRUCT_FS_DATA */ +  /* Should not be reached */ + #error No struct to hold statfs() data. + #endif /* HAVE_STRUCT_FS_DATA */ + #endif /* HAVE_STRUCT_STATFS */ + #else /* !HAVE_STATFS */ + #ifdef HAVE_USTAT +  push_text("bfree"); push_int(st.f_tfree); +  push_text("ffree"); push_int(st.f_tinode); +  push_text("fsname"); push_text(fsname = st.f_fname); +  num_fields += 3; + #else +  /* Should not be reached */ + #error No stat function for filesystems. + #endif /* HAVE_USTAT */ + #endif /* HAVE_STATFS */ + #endif /* HAVE_STATVFS */ + #if 0 + #if defined(HAVE_LIBZFS_INIT) && defined(HAVE_ZFS_PATH_TO_ZHANDLE) +  /* zfs_path_to_zhandle() has an unfortunate tendency to output stuff +  * to stderr when it fails... +  */ +  if (fsname && !strcmp(fsname, "zfs")) { +  zfs_handle_t *zfs_handle; +  if ((str->len == 1) && (str->str[0] == '.')) { +  /* Workaround for bug in zfs_path_to_zhandle(). */ +  zfs_handle = zfs_path_to_zhandle(libzfs_handle, "./", +  ZFS_TYPE_FILESYSTEM); +  } else { +  zfs_handle = zfs_path_to_zhandle(libzfs_handle, str->str, +  ZFS_TYPE_FILESYSTEM); +  } +  if (zfs_handle) { + #ifndef HAVE_ZFS_PROP_UTF8ONLY +  /* NOTE: ZFS_PROP_UTF8ONLY is not present in Solaris 10 127112-05. +  * Attempt to be forward compatible. +  */ + #define ZFS_PROP_UTF8ONLY (ZFS_PROP_XATTR + 4) + #endif /* !HAVE_ZFS_PROP_UTF8ONLY */ +  if (zfs_prop_get_int(zfs_handle, ZFS_PROP_UTF8ONLY)) { +  push_text("filename_encoding"); +  push_text("utf8"); +  num_fields++; +  } +  } +  } + #endif /* HAVE_LIBZFS_INIT && HAVE_ZFS_PATH_TO_ZHANDLE */ + #endif /* 0 */ +  +  f_aggregate_mapping(num_fields*2); +  } + } +  + #endif /* HAVE_STATVFS || HAVE_STATFS || HAVE_USTAT */ + #endif /* __NT__ */ +  + /*! @decl int rm(string f) +  *! +  *! Remove a file or directory. +  *! +  *! @returns +  *! Returns @expr{0@} (zero) on failure, @expr{1@} otherwise. +  *! +  *! @seealso +  *! @[mkdir()], @[Stdio.recursive_rm()] +  */ + void f_rm(INT32 args) + { +  PIKE_STAT_T st; +  INT32 i; +  struct pike_string *str; +  +  destruct_objects_to_destruct(); +  +  VALID_FILE_IO("rm","write"); +  +  if(!args) +  SIMPLE_TOO_FEW_ARGS_ERROR("rm", 1); +  +  if(TYPEOF(sp[-args]) != T_STRING) +  SIMPLE_BAD_ARG_ERROR("rm", 1, "string"); +  +  str = sp[-args].u.string; +  +  if (string_has_null(str)) { +  /* Filenames with NUL are not supported. */ +  errno = ENOENT; +  pop_n_elems(args); +  push_int(0); +  return; +  } +  +  THREADS_ALLOW_UID(); + #ifdef HAVE_LSTAT +  i=fd_lstat(str->str, &st) != -1; + #else +  i=fd_stat(str->str, &st) != -1; + #endif +  if(i) +  { +  if(S_IFDIR == (S_IFMT & st.st_mode)) +  { +  while (!(i = rmdir(str->str) != -1) && (errno == EINTR)) +  ; +  }else{ +  while (!(i = unlink(str->str) != -1) && (errno == EINTR)) +  ; +  } + #ifdef __NT__ +  /* NT looks at the permissions on the file itself and refuses to +  * remove files we don't have write access to. Thus we chmod it +  * and try again, to make rm() more unix-like. */ +  if (!i && errno == EACCES && !(st.st_mode & _S_IWRITE)) { +  if (chmod(str->str, st.st_mode | _S_IWRITE) == -1) +  errno = EACCES; +  else { +  if(S_IFDIR == (S_IFMT & st.st_mode)) +  { +  while (!(i = rmdir(str->str) != -1) && (errno == EINTR)) +  ; +  }else{ +  while (!(i = unlink(str->str) != -1) && (errno == EINTR)) +  ; +  } +  if (!i) { /* Failed anyway; try to restore the old mode. */ +  int olderrno = errno; +  chmod(str->str, st.st_mode); +  errno = olderrno; +  } +  } +  } + #endif +  } +  THREADS_DISALLOW_UID(); +  +  pop_n_elems(args); +  push_int(i); + } +  + /*! @decl int mkdir(string dirname, void|int mode) +  *! +  *! Create a directory. +  *! +  *! If @[mode] is specified, it's will be used for the new directory after +  *! being @expr{&@}'ed with the current umask (on OS'es that support this). +  *! +  *! @returns +  *! Returns @expr{0@} (zero) on failure, @expr{1@} otherwise. +  *! +  *! @seealso +  *! @[rm()], @[cd()], @[Stdio.mkdirhier()] +  */ + void f_mkdir(INT32 args) + { +  struct pike_string *str; +  int mode; +  int i; +  char *s, *s_dup; +  +  VALID_FILE_IO("mkdir","write"); +  +  if(!args) +  SIMPLE_TOO_FEW_ARGS_ERROR("mkdir", 1); +  +  if(TYPEOF(sp[-args]) != T_STRING) +  SIMPLE_BAD_ARG_ERROR("mkdir", 1, "string"); +  +  mode = 0777; /* &'ed with ~umask anyway. */ +  +  if(args > 1) +  { +  if(TYPEOF(sp[1-args]) != T_INT) +  Pike_error("Bad argument 2 to mkdir.\n"); +  +  mode = sp[1-args].u.integer; +  } +  +  str = sp[-args].u.string; +  +  if (string_has_null(str)) { +  /* Filenames with NUL are not supported. */ +  errno = EINVAL; +  pop_n_elems(args); +  push_int(0); +  return; +  } +  +  /* Remove trailing / or \ which is not accepted by all mkdir() +  implementations (e.g. Mac OS X and Windows) */ +  s = str->str; +  s_dup = NULL; +  if (str->len && (s[str->len - 1] == '/' || s[str->len - 1] == '\\')) { +  if ((s_dup = strdup(s))) { +  s = s_dup; +  s[str->len - 1] = '\0'; +  } +  } +  + #if MKDIR_ARGS == 2 +  THREADS_ALLOW_UID(); +  i = mkdir(s, mode) != -1; +  THREADS_DISALLOW_UID(); + #else +  + #ifdef HAVE_LSTAT + #define LSTAT lstat + #else + #define LSTAT stat + #endif +  +  { +  /* Most OS's should have MKDIR_ARGS == 2 nowadays fortunately. */ +  int mask = umask(0); +  /* The following is basically the normal THREADS_ALLOW_UID/ +  * THREADS_DISALLOW_UID macros expanded. They cannot be used +  * directly due to the nested disallow/allow block below. */ +  struct thread_state *cur_ts_ext = Pike_interpreter.thread_state; +  pike_threads_allow_ext (cur_ts_ext COMMA_DLOC); +  i = mkdir(s) != -1; +  umask(mask); +  if (i) { +  /* Attempt to set the mode. +  * +  * This code needs to be as paranoid as possible. +  */ +  struct stat statbuf1; +  struct stat statbuf2; +  i = LSTAT(s, &statbuf1) != -1; +  if (i) { +  i = ((statbuf1.st_mode & S_IFMT) == S_IFDIR); +  } +  if (i) { +  mode = ((mode & 0777) | (statbuf1.st_mode & ~0777)) & ~mask; +  do { +  i = chmod(s, mode) != -1; +  if (i || errno != EINTR) break; +  pike_threads_disallow_ext (cur_ts_ext COMMA_DLOC); +  check_threads_etc(); +  pike_threads_allow_ext (cur_ts_ext COMMA_DLOC); +  } while (1); +  } +  if (i) { +  i = LSTAT(s, &statbuf2) != -1; +  } +  if (i) { +  i = (statbuf2.st_mode == mode) && (statbuf1.st_ino == statbuf2.st_ino); +  if (!i) { +  errno = EPERM; +  } +  } +  if (!i) { +  rmdir(s); +  } +  } +  pike_threads_disallow_ext (cur_ts_ext COMMA_DLOC); +  } + #endif +  +  if (s_dup) +  free(s_dup); +  +  pop_n_elems(args); +  push_int(i); + } +  + #undef HAVE_READDIR_R + #if defined(HAVE_SOLARIS_READDIR_R) || defined(HAVE_HPUX_READDIR_R) || \ +  defined(HAVE_POSIX_READDIR_R) + #define HAVE_READDIR_R + #endif +  + #if defined(_REENTRANT) && defined(HAVE_READDIR_R) +  + #ifdef _PC_NAME_MAX + #ifdef HAVE_FPATHCONF +  + #ifdef HAVE_FDOPENDIR + #define USE_FDOPENDIR + #define USE_FPATHCONF + #elif defined(HAVE_DIRFD) + #define USE_FPATHCONF + #endif +  + #endif /* HAVE_FPATHCONF */ +  + #if defined(HAVE_PATHCONF) && !defined(USE_FPATHCONF) + #define USE_PATHCONF + #endif + #endif /* _PC_NAME_MAX */ +  + #endif /* _REENTRANT && HAVE_READDIR_R */ +  + /*! @decl array(string) get_dir(void|string dirname) +  *! +  *! Returns an array of all filenames in the directory @[dirname], or +  *! @expr{0@} (zero) if the directory does not exist. When no +  *! @[dirname] is given, current work directory is used. +  *! +  *! @seealso +  *! @[mkdir()], @[cd()] +  */ + void f_get_dir(INT32 args) + { + #ifdef __NT__ +  HANDLE dir; +  WIN32_FIND_DATAW d; +  struct string_builder sb; + #else /* !__NT__ */ + #ifdef USE_FDOPENDIR +  int dir_fd; + #endif +  DIR *dir = NULL; + #ifdef HAVE_READDIR_R +  ptrdiff_t name_max = -1; + #endif + #endif /* __NT__ */ +  struct pike_string *str=0; +  +  VALID_FILE_IO("get_dir","read"); +  + #ifdef __NT__ +  get_all_args("get_dir",args,".%T",&str); + #else /* !__NT__ */ +  get_all_args("get_dir",args,".%N",&str); + #endif /* __NT__ */ +  +  if(!str) { + #if defined(__amigaos4__) +  push_empty_string(); + #else +  push_constant_text("."); + #endif +  str = Pike_sp[-1].u.string; +  args++; +  } +  + #ifdef __NT__ +  +  if (str->size_shift == 2) { +  /* Filenames that are too wide are not supported. */ +  errno = ENOENT; +  pop_n_elems(args); +  push_int(0); +  return; +  } +  +  init_string_builder_alloc(&sb, str->len+2, 1); +  +  string_builder_shared_strcat(&sb, str); +  +  /* Append "/" "*". */ +  if (sb.s->len && (STR1(sb.s)[sb.s->len-1] != '/') && +  (STR1(sb.s)[sb.s->len-1] != '\\')) { +  STR1(sb.s)[sb.s->len++] = '/'; +  } +  STR1(sb.s)[sb.s->len++] = '*'; +  STR1(sb.s)[sb.s->len] = '\0'; +  +  if (wcslen(STR1(sb.s)) != (size_t)sb.s->len) { +  /* Filenames with NUL are not supported. */ +  free_string_builder(&sb); +  errno = ENOENT; +  pop_n_elems(args); +  push_int(0); +  return; +  } +  + #ifdef READDIR_DEBUG +  fprintf(stderr, "FindFirstFile(\"%S\")...\n", STR1(sb.s)); + #endif /* READDIR_DEBUG */ +  +  dir = FindFirstFileW(STR1(sb.s), &d); +  +  free_string_builder(&sb); +  +  if (dir == DO_NOT_WARN(INVALID_HANDLE_VALUE)) { +  int err = GetLastError(); + #ifdef READDIR_DEBUG +  fprintf(stderr, " INVALID_HANDLE_VALUE, error %d\n", err); + #endif /* READDIR_DEBUG */ +  +  pop_n_elems(args); +  if (err == ERROR_FILE_NOT_FOUND) { +  /* Normally there should at least be a "." entry, so this seldom +  * happens. But it seems unwise to count on it, considering this +  * being Windows and all.. +  * +  * Note: The error is ERROR_PATH_NOT_FOUND if the directory +  * doesn't exist. +  */ +  push_empty_array(); +  } +  else { +  set_errno_from_win32_error (err); +  push_int(0); +  } +  return; +  } +  +  { +  int err; +  +  BEGIN_AGGREGATE_ARRAY(10); +  +  do { + #ifdef READDIR_DEBUG +  fprintf(stderr, " \"%S\"\n", d.cFileName); + #endif /* READDIR_DEBUG */ +  /* Filter "." and ".." from the list. */ +  if(d.cFileName[0]=='.') +  { +  if(!d.cFileName[1]) continue; +  if(d.cFileName[1]=='.' && !d.cFileName[2]) continue; +  } +  push_string(make_shared_binary_string1(d.cFileName, wcslen(d.cFileName))); +  DO_AGGREGATE_ARRAY(120); +  } while(FindNextFileW(dir, &d)); +  err = GetLastError(); +  + #ifdef READDIR_DEBUG +  fprintf(stderr, " DONE, error %d\n", err); + #endif /* READDIR_DEBUG */ +  +  FindClose(dir); +  +  END_AGGREGATE_ARRAY; +  +  stack_pop_n_elems_keep_top(args); +  +  if (err != ERROR_SUCCESS && err != ERROR_NO_MORE_FILES) { +  set_errno_from_win32_error (err); +  pop_stack(); +  push_int (0); +  return; +  } +  } +  + #else /* !__NT__ */ +  +  if (string_has_null(str)) { +  /* Filenames with NUL are not supported. */ +  errno = ENOENT; +  pop_n_elems(args); +  push_int(0); +  return; +  } +  +  THREADS_ALLOW_UID(); + #ifdef USE_FDOPENDIR +  dir_fd = open(str->str, O_RDONLY); +  if (dir_fd != -1) { + #ifdef USE_FPATHCONF +  name_max = fpathconf(dir_fd, _PC_NAME_MAX); + #endif /* USE_FPATHCONF */ +  dir = fdopendir(dir_fd); +  if (!dir) close(dir_fd); +  } + #else +  dir = opendir(str->str); + #ifdef USE_FPATHCONF +  if (dir) { +  name_max = fpathconf(dirfd(dir), _PC_NAME_MAX); +  } + #endif + #endif /* !HAVE_FDOPENDIR */ + #ifdef USE_PATHCONF +  name_max = pathconf(str->str, _PC_NAME_MAX); + #endif +  THREADS_DISALLOW_UID(); +  if(dir) +  { +  struct dirent *d; +  struct dirent *tmp = NULL; + #if defined(_REENTRANT) && defined(HAVE_READDIR_R) + #define FPR 1024 +  char buffer[MAXPATHLEN * 4]; +  char *ptrs[FPR]; +  ptrdiff_t lens[FPR]; +  + #ifndef NAME_MAX + #define NAME_MAX 1024 + #endif +  if (name_max < NAME_MAX) +  name_max = NAME_MAX; +  if (name_max < 1024) name_max = 1024; +  +  if (!(tmp = malloc(sizeof(struct dirent) + name_max + 1))) { +  closedir(dir); +  Pike_error("get_dir(): Out of memory\n"); +  } + #endif /* _REENTRANT && HAVE_READDIR_R */ +  +  BEGIN_AGGREGATE_ARRAY(10); +  + #if defined(_REENTRANT) && defined(HAVE_READDIR_R) +  while(1) +  { +  int e; +  int num_files=0; +  char *bufptr=buffer; +  int err = 0; +  +  THREADS_ALLOW(); +  +  while(1) +  { + #if defined(HAVE_SOLARIS_READDIR_R) +  /* Solaris readdir_r returns the second arg on success, +  * and returns NULL on error or at end of dir. +  */ +  errno=0; +  do { +  d=readdir_r(dir, tmp); +  } while ((!d) && ((errno == EAGAIN)||(errno == EINTR))); +  if (!d) { +  /* Solaris readdir_r seems to set errno to ENOENT sometimes. +  */ +  if (errno == ENOENT) { +  err = 0; +  } else { +  err = errno; +  } +  break; +  } + #elif defined(HAVE_HPUX_READDIR_R) +  /* HPUX's readdir_r returns an int instead: +  * +  * 0 - Successfull operation. +  * -1 - End of directory or encountered an error (sets errno). +  */ +  errno=0; +  if (readdir_r(dir, tmp)) { +  d = NULL; +  err = errno; +  break; +  } else { +  d = tmp; +  } + #elif defined(HAVE_POSIX_READDIR_R) +  /* POSIX readdir_r returns 0 on success, and ERRNO on failure. +  * at end of dir it sets the third arg to NULL. +  */ +  d = NULL; +  errno = 0; +  if ((err = readdir_r(dir, tmp, &d)) || !d) { + #ifdef READDIR_DEBUG +  fprintf(stderr, "POSIX readdir_r(\"%s\") => err %d\n", +  str->str, err); +  fprintf(stderr, "POSIX readdir_r(), d= 0x%08x\n", +  (unsigned int)d); + #endif /* READDIR_DEBUG */ +  if (err == -1) { +  /* Solaris readdir_r returns -1, and sets errno. */ +  err = errno; +  } + #ifdef READDIR_DEBUG +  fprintf(stderr, "POSIX readdir_r(\"%s\") => errno %d\n", +  str->str, err); + #endif /* READDIR_DEBUG */ +  /* Solaris readdir_r seems to set errno to ENOENT sometimes. +  * +  * AIX readdir_r seems to set errno to EBADF at end of dir. +  */ +  if ((err == ENOENT) || (err == EBADF)) { +  err = 0; +  } +  break; +  } + #ifdef READDIR_DEBUG +  fprintf(stderr, "POSIX readdir_r(\"%s\") => \"%s\"\n", +  str->str, d->d_name); + #endif /* READDIR_DEBUG */ + #else + #error Unknown readdir_r variant + #endif +  /* Filter "." and ".." from the list. */ +  if(d->d_name[0]=='.') +  { +  if(NAMLEN(d)==1) continue; +  if(d->d_name[1]=='.' && NAMLEN(d)==2) continue; +  } +  if(num_files >= FPR) break; +  lens[num_files]=NAMLEN(d); +  if(bufptr+lens[num_files] >= buffer+sizeof(buffer)) break; +  MEMCPY(bufptr, d->d_name, lens[num_files]); +  ptrs[num_files]=bufptr; +  bufptr+=lens[num_files]; +  num_files++; +  } +  THREADS_DISALLOW(); +  if ((!d) && err) { +  free(tmp); +  Pike_error("get_dir(): readdir_r(\"%S\") failed: %d\n", str, err); +  } + #ifdef READDIR_DEBUG +  fprintf(stderr, "Pushing %d filenames...\n", num_files); + #endif /* READDIR_DEBUG */ +  for(e=0;e<num_files;e++) +  { +  push_string(make_shared_binary_string(ptrs[e],lens[e])); +  } +  if(d) +  push_string(make_shared_binary_string(d->d_name,NAMLEN(d))); +  else +  break; +  DO_AGGREGATE_ARRAY(120); +  } +  +  free(tmp); + #else +  for(d=readdir(dir); d; d=readdir(dir)) +  { +  /* Filter "." and ".." from the list. */ +  if(d->d_name[0]=='.') +  { +  if(NAMLEN(d)==1) continue; +  if(d->d_name[1]=='.' && NAMLEN(d)==2) continue; +  } +  push_string(make_shared_binary_string(d->d_name, NAMLEN(d))); +  +  DO_AGGREGATE_ARRAY(120); +  } + #endif +  closedir(dir); +  +  END_AGGREGATE_ARRAY; +  stack_pop_n_elems_keep_top(args); +  } else { +  pop_n_elems(args); +  push_int(0); +  } + #endif /* __NT__ */ + } +  + /*! @decl int cd(string s) +  *! +  *! Change the current directory for the whole Pike process. +  *! +  *! @returns +  *! Returns @expr{1@} for success, @expr{0@} (zero) otherwise. +  *! +  *! @seealso +  *! @[getcwd()] +  */ + void f_cd(INT32 args) + { +  INT32 i; +  struct pike_string *str; +  +  VALID_FILE_IO("cd","status"); +  +  if(!args) +  SIMPLE_TOO_FEW_ARGS_ERROR("cd", 1); +  +  if(TYPEOF(sp[-args]) != T_STRING) +  SIMPLE_BAD_ARG_ERROR("cd", 1, "string"); +  +  str = sp[-args].u.string; +  +  if (string_has_null(str)) { +  /* Filenames with NUL are not supported. */ +  errno = ENOENT; +  pop_n_elems(args); +  push_int(0); +  return; +  } +  +  i = chdir(str->str) != -1; +  pop_n_elems(args); +  push_int(i); + } +  + /*! @decl string getcwd() +  *! +  *! Returns the current working directory. +  *! +  *! @seealso +  *! @[cd()] +  */ + void f_getcwd(INT32 args) + { +  char *e; +  char *tmp; + #if defined(HAVE_WORKING_GETCWD) || !defined(HAVE_GETWD) +  INT32 size; +  +  size=1000; +  do { +  tmp=(char *)xalloc(size); +  e = getcwd(tmp,size); +  if (e || errno!=ERANGE) break; +  free(tmp); +  tmp=0; +  size*=2; +  } while (size < 10000); + #else + #ifndef MAXPATHLEN + #define MAXPATHLEN 32768 + #endif +  tmp=xalloc(MAXPATHLEN+1); +  THREADS_ALLOW_UID(); +  e = getwd(tmp); +  THREADS_DISALLOW_UID(); + #endif +  if(!e) { +  if (tmp) +  free(tmp); +  Pike_error("Failed to fetch current path.\n"); +  } +  +  pop_n_elems(args); +  push_text(e); +  free(tmp); + } +  + #ifdef HAVE_EXECVE + /*! @decl int exece(string file, array(string) args) +  *! @decl int exece(string file, array(string) args, @ +  *! mapping(string:string) env) +  *! +  *! This function transforms the Pike process into a process running +  *! the program specified in the argument @[file] with the arguments @[args]. +  *! +  *! If the mapping @[env] is present, it will completely replace all +  *! environment variables before the new program is executed. +  *! +  *! @returns +  *! This function only returns if something went wrong during @tt{exece(2)@}, +  *! and in that case it returns @expr{0@} (zero). +  *! +  *! @note +  *! The Pike driver _dies_ when this function is called. You must either +  *! use @[fork()] or @[Process.create_process()] if you wish to execute a +  *! program and still run the Pike runtime. +  *! +  *! This function is not available on all platforms. +  *! +  *! @seealso +  *! @[Process.create_process()], @[fork()], @[Stdio.File->pipe()] +  */ + void f_exece(INT32 args) + { +  INT32 e; +  char **argv, **env; +  struct svalue *save_sp; +  struct mapping *en; + #ifdef DECLARE_ENVIRON +  extern char **environ; + #endif +  +  save_sp=sp-args; +  +  if(args < 2) +  SIMPLE_TOO_FEW_ARGS_ERROR("exece", 2); +  + #ifdef PIKE_SECURITY +  if(!CHECK_SECURITY(SECURITY_BIT_SECURITY)) +  Pike_error("exece: permission denied.\n"); + #endif +  +  +  e=0; +  en=0; +  switch(args) +  { +  default: +  if(TYPEOF(sp[2-args]) != T_MAPPING) +  SIMPLE_BAD_ARG_ERROR("exece", 3, "mapping(string:string)"); +  en=sp[2-args].u.mapping; +  mapping_fix_type_field(en); +  +  if(m_ind_types(en) & ~BIT_STRING) +  SIMPLE_BAD_ARG_ERROR("exece", 3, "mapping(string:string)"); +  if(m_val_types(en) & ~BIT_STRING) +  SIMPLE_BAD_ARG_ERROR("exece", 3, "mapping(string:string)"); +  +  case 2: +  if(TYPEOF(sp[1-args]) != T_ARRAY) +  SIMPLE_BAD_ARG_ERROR("exece", 2, "array(string)"); +  +  +  if(array_fix_type_field(sp[1-args].u.array) & ~BIT_STRING) +  SIMPLE_BAD_ARG_ERROR("exece", 2, "array(string)"); +  +  case 1: +  if(TYPEOF(sp[0-args]) != T_STRING) +  SIMPLE_BAD_ARG_ERROR("exece", 1, "string"); +  } +  +  argv=(char **)xalloc((2+sp[1-args].u.array->size) * sizeof(char *)); +  +  argv[0]=sp[0-args].u.string->str; +  +  for(e=0;e<sp[1-args].u.array->size;e++) +  { +  union anything *a; +  a=low_array_get_item_ptr(sp[1-args].u.array,e,T_STRING); +  argv[e+1]=a->string->str; +  } +  argv[e+1]=0; +  +  if(en) +  { +  INT32 e, i = 0; +  struct keypair *k; +  +  env=(char **)malloc((1+m_sizeof(en)) * sizeof(char *)); +  if(!env) { +  free(argv); +  SIMPLE_OUT_OF_MEMORY_ERROR("exece", (1+m_sizeof(en)*sizeof(char *))); +  } +  +  NEW_MAPPING_LOOP(en->data) { +  push_string(k->ind.u.string); +  push_constant_text("="); +  push_string(k->val.u.string); +  f_add(3); +  env[i++]=sp[-1].u.string->str; +  dmalloc_touch_svalue(sp-1); +  } +  +  env[i]=0; +  }else{ +  env=environ; +  } +  +  my_set_close_on_exec(0,0); +  my_set_close_on_exec(1,0); +  my_set_close_on_exec(2,0); +  +  do_set_close_on_exec(); +  + #ifdef __NT__ + #define DOCAST(X) ((const char * const *)(X)) + #else + #define DOCAST(X) (X) + #endif +  + #ifdef HAVE_BROKEN_F_SETFD +  do_close_on_exec(); + #endif /* HAVE_BROKEN_F_SETFD */ +  +  execve(argv[0],DOCAST(argv),DOCAST(env)); +  +  free((char *)argv); +  if(env != environ) free((char *)env); +  pop_n_elems(sp-save_sp); +  push_int(0); + } + #endif +  + /*! @decl int mv(string from, string to) +  *! +  *! Rename or move a file or directory. +  *! +  *! If the destination already exists, it will be replaced. +  *! Replacement often only works if @[to] is of the same type as +  *! @[from], i.e. a file can only be replaced by another file and so +  *! on. Also, a directory will commonly be replaced only if it's +  *! empty. +  *! +  *! On some OSs this function can't move directories, only rename +  *! them. +  *! +  *! @returns +  *! Returns @expr{0@} (zero) on failure, @expr{1@} otherwise. Call +  *! @[errno()] to get more error info on failure. +  *! +  *! @seealso +  *! @[rm()] +  */ + void f_mv(INT32 args) + { +  INT32 i; +  struct pike_string *str1; +  struct pike_string *str2; + #ifdef __NT__ +  int orig_errno = errno; +  int err; +  PIKE_STAT_T st; + #endif +  +  VALID_FILE_IO("mv","write"); +  +  if(args<2) +  SIMPLE_TOO_FEW_ARGS_ERROR("mv", 2); +  +  if(TYPEOF(sp[-args]) != T_STRING) +  SIMPLE_BAD_ARG_ERROR("mv", 1, "string"); +  +  if(TYPEOF(sp[-args+1]) != T_STRING) +  SIMPLE_BAD_ARG_ERROR("mv", 2, "string"); +  +  str1 = sp[-args].u.string; +  str2 = sp[1-args].u.string; +  +  if (string_has_null(str1) || string_has_null(str2)) { +  /* Filenames with NUL are not supported. */ +  if (string_has_null(str1)) { +  errno = ENOENT; +  } else { +  errno = EINVAL; +  } +  pop_n_elems(args); +  push_int(0); +  return; +  } +  + #ifndef __NT__ +  i=rename(str1->str, str2->str); + #else +  +  /* Emulate the behavior of unix rename(2) when the destination exists. */ +  +  if (movefileex) { +  if (MoveFileEx (str1->str, str2->str, 0)) { +  i = 0; +  goto no_nt_rename_kludge; +  } +  if ((i = GetLastError()) != ERROR_ALREADY_EXISTS) +  goto no_nt_rename_kludge; +  } +  else { +  /* Fall back to rename() for W98 and earlier. Unlike MoveFileEx, +  * it can't move directories between directories. */ +  if (!rename (str1->str, str2->str)) { +  i = 0; +  goto no_nt_rename_kludge; +  } +  if ((i = errno) != EEXIST) +  goto no_nt_rename_kludge; +  } +  + #ifdef HAVE_LSTAT +  if (fd_lstat (str2->str, &st)) goto no_nt_rename_kludge; + #else +  if (fd_stat (str2->str, &st)) goto no_nt_rename_kludge; + #endif +  +  if ((st.st_mode & S_IFMT) != S_IFDIR && movefileex) { +  /* MoveFileEx can overwrite a file but not a dir. */ +  if (!(st.st_mode & _S_IWRITE)) +  /* Like in f_rm, we got to handle the case that NT looks on the +  * permissions on the file itself before removing it. */ +  if (chmod (str2->str, st.st_mode | _S_IWRITE)) +  goto no_nt_rename_kludge; +  if (movefileex (str1->str, str2->str, MOVEFILE_REPLACE_EXISTING)) +  i = 0; /* Success. */ +  else +  chmod (str2->str, st.st_mode); +  } +  +  else { +  char *s = malloc (str2->len + 2), *p; +  if (!s) { +  i = movefileex ? ERROR_NOT_ENOUGH_MEMORY : ENOMEM; +  goto no_nt_rename_kludge; +  } +  memcpy (s, str2->str, str2->len); +  p = s + str2->len; +  p[2] = 0; +  +  if ((st.st_mode & S_IFMT) == S_IFDIR) { +  /* Check first that the target is empty if it's a directory, so +  * that we won't even bother trying with the stunt below. */ +  WIN32_FIND_DATA *dir = malloc (sizeof (WIN32_FIND_DATA)); +  HANDLE h; +  if (!dir) { +  i = movefileex ? ERROR_NOT_ENOUGH_MEMORY : ENOMEM; +  goto nt_rename_kludge_end; +  } +  p[0] = '/', p[1] = '*'; +  h = FindFirstFile (s, dir); +  if (h != INVALID_HANDLE_VALUE) { +  do { +  if (dir->cFileName[0] != '.' || +  (dir->cFileName[1] && +  (dir->cFileName[1] != '.' || dir->cFileName[2]))) { +  /* Target dir not empty. */ +  FindClose (h); +  free (dir); +  goto nt_rename_kludge_end; +  } +  } while (FindNextFile (h, dir)); +  FindClose (h); +  } +  free (dir); +  } +  +  /* Move away the target temporarily to do the move, then cleanup +  * or undo depending on how it went. */ +  for (p[0] = 'A'; p[0] != 'Z'; p[0]++) +  for (p[1] = 'A'; p[1] != 'Z'; p[1]++) { +  if (movefileex) { +  if (!movefileex (str2->str, s, 0)) { +  if (GetLastError() == ERROR_ALREADY_EXISTS) continue; +  else goto nt_rename_kludge_end; +  } +  } +  else { +  if (rename (str2->str, s)) { +  if (errno == EEXIST) continue; +  else goto nt_rename_kludge_end; +  } +  } +  if (movefileex ? +  movefileex (str1->str, str2->str, MOVEFILE_REPLACE_EXISTING) : +  !rename (str1->str, str2->str)) { +  if (!(st.st_mode & _S_IWRITE)) +  if (chmod (s, st.st_mode | _S_IWRITE)) +  goto nt_rename_kludge_fail; +  if ((st.st_mode & S_IFMT) == S_IFDIR) { +  while ((err = rmdir (s)) && (errno == EINTR)) +  ; +  } else { +  while ((err = unlink (s)) && (errno == EINTR)) +  ; +  } +  if (!err) { /* Success. */ +  i = 0; +  goto nt_rename_kludge_end; +  } +  +  nt_rename_kludge_fail: +  /* Couldn't remove the old target, perhaps the directory +  * grew files. */ +  if (!(st.st_mode & _S_IWRITE)) +  chmod (s, st.st_mode); +  if (movefileex ? +  !movefileex (str2->str, str1->str, MOVEFILE_REPLACE_EXISTING) : +  rename (str2->str, str1->str)) { +  /* Old target left behind but the rename is still +  * successful, so we claim success anyway in lack of +  * better error reporting capabilities. */ +  i = 0; +  goto nt_rename_kludge_end; +  } +  } +  +  rename (s, str2->str); +  goto nt_rename_kludge_end; +  } +  +  nt_rename_kludge_end: +  free (s); +  } +  + no_nt_rename_kludge: +  if (i) { +  if (movefileex) +  switch (i) { +  /* Try to translate NT errors to errno codes that NT's rename() +  * would return. */ +  case ERROR_INVALID_NAME: +  errno = EINVAL; +  break; +  case ERROR_NOT_ENOUGH_MEMORY: +  errno = ENOMEM; +  break; +  case ERROR_FILE_NOT_FOUND: +  case ERROR_PATH_NOT_FOUND: +  errno = ENOENT; +  break; +  case ERROR_ALREADY_EXISTS: +  errno = EEXIST; +  break; +  default: +  errno = EACCES; +  } +  } +  else errno = orig_errno; + #endif /* __NT__ */ +  +  pop_n_elems(args); +  push_int(!i); + } +  + /*! @decl string strerror(int errno) +  *! +  *! This function returns a description of an error code. The error +  *! code is usually obtained from eg @[Stdio.File->errno()]. +  *! +  *! @note +  *! On some platforms the string returned can be somewhat nondescriptive. +  */ + void f_strerror(INT32 args) + { +  char *s; +  int err; +  +  if(!args) +  SIMPLE_TOO_FEW_ARGS_ERROR("strerror", 1); +  if(TYPEOF(sp[-args]) != T_INT) +  SIMPLE_BAD_ARG_ERROR("strerror", 1, "int"); +  +  err = sp[-args].u.integer; +  pop_n_elems(args); +  if(err < 0 || err > 256 ) +  s=0; +  else { + #ifdef HAVE_STRERROR +  s=strerror(err); + #else +  s=0; + #endif +  } +  if(s) +  push_text(s); +  else { +  push_constant_text("Error "); +  push_int(err); +  f_add(2); +  } + } +  + /*! @decl int errno() +  *! +  *! This function returns the system error from the last file operation. +  *! +  *! @note +  *! Note that you should normally use @[Stdio.File->errno()] instead. +  *! +  *! @seealso +  *! @[Stdio.File->errno()], @[strerror()] +  */ + void f_errno(INT32 args) + { +  pop_n_elems(args); +  push_int(errno); + } +  + void init_stdio_efuns(void) + { +  set_close_on_exec(0,1); +  set_close_on_exec(1,1); +  set_close_on_exec(2,1); +  + #if 0 + #ifdef HAVE_LIBZFS_INIT +  libzfs_handle = libzfs_init(); + #endif + #endif /* 0 */ +  + #ifdef __NT__ +  { +  /* MoveFileEx doesn't exist in W98 and earlier. */ +  /* Correction, it exists but does not work -Hubbe */ +  +  OSVERSIONINFO osversion; +  osversion.dwOSVersionInfoSize=sizeof(osversion); +  if(GetVersionEx(&osversion)) +  { +  switch(osversion.dwPlatformId) +  { +  case VER_PLATFORM_WIN32s: /* win32s */ +  case VER_PLATFORM_WIN32_WINDOWS: /* win9x */ +  break; +  default: +  if ((kernel32lib = LoadLibrary ("kernel32"))) +  movefileex = (movefileextype) GetProcAddress (kernel32lib, "MoveFileExA"); +  } +  } +  } + #endif +  + /* function(string,int|void:object) */ +  ADD_EFUN("file_stat",f_file_stat,tFunc(tStr tOr(tInt,tVoid),tObjIs_STDIO_STAT), OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT); +  +  /* function(string,int:int(0..1)) */ +  ADD_EFUN("file_truncate",f_file_truncate,tFunc(tStr tInt,tInt),OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT); +  + #if defined(HAVE_FSETXATTR) && defined(HAVE_FGETXATTR) && defined(HAVE_FLISTXATTR) +  ADD_EFUN( "listxattr", f_listxattr, tFunc(tStr tOr(tVoid,tInt),tArr(tStr)), OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT); +  ADD_EFUN( "setxattr", f_setxattr, tFunc(tStr tStr tStr tInt tOr(tVoid,tInt),tInt), OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT); +  ADD_EFUN( "getxattr", f_getxattr, tFunc(tStr tStr tOr(tVoid,tInt),tStr), OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT); +  ADD_EFUN( "removexattr", f_removexattr, tFunc(tStr tStr tOr(tVoid,tInt),tInt), OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT); + #endif +  + #if defined(HAVE_STATVFS) || defined(HAVE_STATFS) || defined(HAVE_USTAT) || defined(__NT__) +  + /* function(string:mapping(string:string|int)) */ +  ADD_EFUN("filesystem_stat", f_filesystem_stat,tFunc(tStr,tMap(tStr,tOr(tStr,tInt))), OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT); + #endif /* HAVE_STATVFS || HAVE_STATFS */ +  + /* function(:int) */ +  ADD_EFUN("errno",f_errno,tFunc(tNone,tInt),OPT_EXTERNAL_DEPEND); +  + /* function(string:int) */ +  ADD_EFUN("rm",f_rm,tFunc(tStr,tInt),OPT_SIDE_EFFECT); +  + /* function(string,void|int:int) */ +  ADD_EFUN("mkdir",f_mkdir,tFunc(tStr tOr(tVoid,tInt),tInt),OPT_SIDE_EFFECT); +  + /* function(string,string:int) */ +  ADD_EFUN("mv", f_mv,tFunc(tStr tStr,tInt), OPT_SIDE_EFFECT); +  + /* function(string:string *) */ +  ADD_EFUN("get_dir",f_get_dir,tFunc(tOr(tVoid,tStr),tArr(tStr)),OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT); +  + /* function(string:int) */ +  ADD_EFUN("cd",f_cd,tFunc(tStr,tInt),OPT_SIDE_EFFECT); +  + /* function(:string) */ +  ADD_EFUN("getcwd",f_getcwd,tFunc(tNone,tStr),OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT); +  + #ifdef HAVE_EXECVE + /* function(string,mixed*,void|mapping(string:string):int) */ +  ADD_EFUN("exece",f_exece,tFunc(tStr tArr(tMix) tOr(tVoid,tMap(tStr,tStr)),tInt),OPT_SIDE_EFFECT); + #endif +  + /* function(int:string) */ +  ADD_EFUN("strerror",f_strerror,tFunc(tInt,tStr),0); + } +  + void exit_stdio_efuns(void) + { + #ifdef __NT__ +  if (kernel32lib) { +  if (FreeLibrary (kernel32lib)) +  movefileex = 0; +  kernel32lib = 0; +  } + #endif + #if 0 + #ifdef HAVE_LIBZFS_INIT +  libzfs_fini(libzfs_handle); + #endif + #endif /* 0 */ + }   Newline at end of file added.