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

version» Context lines:

pike.git/src/modules/_Stdio/file.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.   */    - #define NO_PIKE_SHORTHAND +    #include "global.h"   #include "fdlib.h"   #include "pike_netlib.h"   #include "interpret.h"   #include "svalue.h"   #include "stralloc.h"   #include "array.h"   #include "mapping.h"   #include "object.h"   #include "pike_macros.h"   #include "backend.h"   #include "fd_control.h"   #include "module_support.h"   #include "operators.h" - #include "pike_security.h" +    #include "bignum.h"   #include "builtin_functions.h"   #include "gc.h"   #include "time_stuff.h" -  + #include "buffer.h"   #include "file_machine.h"   #include "file.h"   #include "pike_error.h"   #include "signal_handler.h"   #include "pike_types.h"   #include "threads.h"   #include "program_id.h" -  + #include "sprintf.h"    - #ifdef HAVE_SYS_TYPE_H - #include <sys/types.h> - #endif /* HAVE_SYS_TYPE_H */ -  +    #include <sys/stat.h>   #ifdef HAVE_SYS_PARAM_H   #include <sys/param.h>   #endif /* HAVE_SYS_PARAM_H */   #include <errno.h>      #include <fcntl.h>   #include <signal.h>      #ifdef HAVE_SYS_EVENT_H
pike.git/src/modules/_Stdio/file.c:82: Inside #if defined(HAVE_WINSOCK_H) || defined(HAVE_WINSOCK2_H)
  #ifndef EWOULDBLOCK   #define EWOULDBLOCK WSAEWOULDBLOCK   #endif   #ifndef EADDRINUSE   #define EADDRINUSE WSAEADDRINUSE   #endif   #endif      #ifdef HAVE_SYS_STREAM_H   #include <sys/stream.h> -  - /* Ugly patch for AIX 3.2 */ - #ifdef u - #undef u +    #endif    - #endif -  +    #ifdef HAVE_SYS_PROTOSW_H   #include <sys/protosw.h>   #endif      #ifdef HAVE_SYS_SOCKETVAR_H   #include <sys/socketvar.h>   #endif      /* Fix warning on OSF/1    *
pike.git/src/modules/_Stdio/file.c:114:   #undef NOERROR   #endif /* NOERROR */      #ifdef HAVE_NETDB_H   #include <netdb.h>   #endif   #ifdef HAVE_NET_NETDB_H   #include <net/netdb.h>   #endif /* HAVE_NET_NETDB_H */    - #include "dmalloc.h" + #ifdef HAVE_NETINET_TCP_H + #include <netinet/tcp.h> + #endif         #undef THIS   #define THIS ((struct my_file *)(Pike_fp->current_storage))   #define FD (THIS->box.fd)   #define ERRNO (THIS->my_errno)      #define READ_BUFFER 8192   #define DIRECT_BUFSIZE (64*1024)   #define SMALL_NETBUF 2048   #define INUSE_BUSYWAIT_DELAY 0.01   #define INUSE_TIMEOUT 0.1    - /* Don't try to use socketpair() on AmigaOS, socketpair_ultra works better */ - #ifdef __amigaos__ - #undef HAVE_SOCKETPAIR - #endif -  +    #ifdef UNIX_SOCKETS_WORK_WITH_SHUTDOWN   #undef UNIX_SOCKET_CAPABILITIES   #define UNIX_SOCKET_CAPABILITIES (fd_INTERPROCESSABLE | fd_BIDIRECTIONAL | fd_CAN_NONBLOCK | fd_CAN_SHUTDOWN | fd_SEND_FD)   #endif    -  + #ifndef HAVE_DIRFD + #ifdef HAVE_DIR_DD_FD + #define dirfd(dir__) (((DIR*)dir__)->dd_fd) + #define HAVE_DIRFD + #elif defined(HAVE_DIR_D_FD) + #define dirfd(dir__) (((DIR*)dir__)->d_fd) + #define HAVE_DIRFD + #endif + #endif +  + /* +  * gcc with -O3 generates very bloated code for the functions in this file. One rather extreme example +  * is file_open, which ends up having 32 call sites of open(2). +  */ + #ifdef __GNUC__ + #pragma GCC optimize "-Os" + #endif +    /* #define SOCKETPAIR_DEBUG */      struct program *file_program;   struct program *file_ref_program;      /*! @module Stdio    */    -  + /*! @enum FileModeFlags +  *! +  *! File mode flags returned by @[Fd()->mode()]. +  */ +  + /*! @decl constant FILE_READ = 0x1000 +  *! File open for reading. +  */ + /*! @decl constant FILE_WRITE = 0x2000 +  *! File open for writing. +  */ + /*! @decl constant FILE_APPEND = 0x4000 +  *! File open for appending. +  */ + /*! @decl constant FILE_CREATE = 0x8000 +  *! Create a new file if it didn't exist earlier. +  */ + /*! @decl constant FILE_TRUNC = 0x0100 +  *! Truncate the file on open. +  */ + /*! @decl constant FILE_EXCLUSIVE = 0x0200 +  *! Exclusive access to the file. +  */ + /*! @decl constant FILE_NONBLOCKING = 0x0400 +  *! File opened in nonblocking mode. +  */ +  + /*! @endenum FileModeFlags +  */ +  + /*! @enum FilePropertyFlags +  *! +  *! File properties for use with eg @[Fd()->pipe()], +  *! and returned by eg @[Fd()->mode()]. +  */ +  + /*! @decl constant PROP_SEND_FD = 0x0040 +  *! File is capable of sending open file descriptors. +  */ + /*! @decl constant PROP_BIDIRECTIONAL = 0x0010 +  *! File supports both sending and receiving. +  */ + /*! @decl constant PROP_BUFFERED = 0x0008 +  *! File has internal buffering. +  */ + /*! @decl constant PROP_SHUTDOWN = 0x0004 +  *! File supports unidirectional close. +  */ + /*! @decl constant PROP_NONBLOCK = 0x0002 +  *! File supports nonblocking operation. +  */ + /*! @decl constant PROP_IPC = 0x0001 +  *! File can be used for interprocess communication. +  */ +  + /*! @endenum FilePropertyFlags +  */ +    /*! @class Fd_ref    *!    *! Proxy class that contains stub functions    *! that call the corresponding functions in    *! @[Fd].    *!    *! Used by @[Stdio.File].    *!    *! @note    *! This is not the class you want. Use @[Stdio.File] and friends instead.
pike.git/src/modules/_Stdio/file.c:221:    *! Getter for the Fd object.    */   static void fd_backtick__fd(INT32 args)   {    pop_n_elems(args);    ref_push_object_inherit(Pike_fp->current_object,    Pike_fp->context -    Pike_fp->current_program->inherits);   }    - /*! @endclass + /*! @decl protected int(0..) _errno +  *! +  *! Variable containing the internal value returned by @[errno()]. +  *! +  *! @seealso +  *! @[errno()]    */    - /* The class below is not accurate, but it's the lowest exposed API -  * interface, which make the functions appear where users actually -  * look for them. /mast */ -  - /*! @class File + /*! @decl mixed _read_callback +  *! @decl mixed _write_callback +  *! @decl mixed _read_oob_callback +  *! @decl mixed _write_oob_callback +  *! @decl mixed _fs_event_callback +  *! +  *! Callback functions installed by @[Stdio.File()->set_callbacks()] et al.    */      static struct my_file *get_file_storage(struct object *o)   {    struct my_file *f;    struct svalue *sval;    struct object *ob;    if(o->prog == file_program)    return ((struct my_file *)    (o->storage + file_program->inherits->storage_offset));    -  if((f=(struct my_file *)get_storage(o,file_program))) +  if((f=get_storage(o,file_program)))    return f;    -  if((sval=(struct svalue *)get_storage(o,file_ref_program))) { +  if((sval=get_storage(o,file_ref_program))) {    if (TYPEOF(*sval) == PIKE_T_OBJECT) {    ob = sval->u.object;    /* FIXME: Use the subtype information! */ -  if(ob && (f=(struct my_file *)get_storage(ob, file_program))) +  if(ob && (f=get_storage(ob, file_program)))    return f;    }    }       return 0;   }      #ifdef PIKE_DEBUG   static void debug_check_internals (struct my_file *f)   {    size_t ev;       if (f->box.ref_obj->prog && file_program && -  !get_storage(f->box.ref_obj,file_program)) +  !get_storage(f->box.ref_obj,file_program) )    Pike_fatal ("ref_obj is not a file object.\n");       for (ev = 0; ev < NELEM (f->event_cbs); ev++)    if (TYPEOF(f->event_cbs[ev]) == PIKE_T_INT &&    f->box.backend && f->box.events & (1 << ev))    Pike_fatal ("Got event flag but no callback for event %"PRINTSIZET"d.\n", ev);   }   #else   #define debug_check_internals(f) do {} while (0)   #endif
pike.git/src/modules/_Stdio/file.c:366: Inside #if defined(PIKE_DEBUG)
   /* Don't cause a fatal when opening fds by number    * if the fd belongs to a backend... */    if ((fd >= 0) && !(flags & FILE_NOT_OPENED))    debug_check_fd_not_in_use (fd);   #endif    change_fd_for_box(&THIS->box, fd);   }      /* Use ptrdiff_t for the fd since we're passed a void * and should    * read it as an integer of the same size. */ - static void do_close_fd(ptrdiff_t fd) + static int do_close_fd(ptrdiff_t fd)   {    int ret; -  if (fd < 0) return; +  int preve; +  if (fd < 0) return 0; +  errno = 0;    do { -  +  preve = errno;    ret = fd_close(fd);    } while ((ret == -1) && (errno == EINTR)); -  +  if ((ret == -1) && preve) return 0; +  return ret;   }      #ifdef HAVE_PIKE_SEND_FD   /* Close the queued fds in fd_info, either due to them being successfully    * sent, or due to the connection being closed. */   static void do_close_fd_info(int *fd_info)   {    int num_fds = fd_info[1];    int *fds = fd_info + 2;    while (num_fds) {
pike.git/src/modules/_Stdio/file.c:426: Inside #if defined(HAVE_PIKE_SEND_FD)
   for(i = other_fd_info[1] + 1, j = num_fds-1; j >= cnt; i--, j--) {    other_fd_info[j] = other_fd_info[i];    }    for(i = 2; i < cnt; i++) {    other_fd_info[i] = fd_info[i];    }    other_fd_info[1] = num_fds;    free(fd_info);    fd_info = other_fd_info;    } else { -  int *new_fd_info = malloc(num_fds * sizeof(int)); +  int *new_fd_info = calloc(num_fds, sizeof(int));    if (!new_fd_info) {    /* FIXME: Huston, we have a problem... */    Pike_fatal("Out of memory in send_fd().\n");    } else {    new_fd_info[0] = num_fds;    new_fd_info[1] = num_fds - 2;    cnt = fd_info[1] + 2;    for (i = 2; i < cnt; i++) {    new_fd_info[i] = fd_info[i];    }
pike.git/src/modules/_Stdio/file.c:474: Inside #if defined(HAVE_PIKE_SEND_FD)
  #ifdef HAVE_PIKE_SEND_FD    if (THIS->fd_info) do_close_fd_info(THIS->fd_info);   #endif       for (ev = 0; ev < NELEM (THIS->event_cbs); ev++) {    free_svalue(& THIS->event_cbs[ev]);    SET_SVAL(THIS->event_cbs[ev], PIKE_T_INT, NUMBER_NUMBER, integer, 0);    }   }    - static void close_fd_quietly(void) + static void close_fd(int quiet)   {    int fd=FD; -  +  int olde = 0;    if(fd<0) return;       free_fd_stuff();    SUB_FD_EVENTS (THIS, ~0); -  +  /* NB: The fd will always be closed on return from fd_close() +  * (except for the EINTR case on eg HPUX). +  */    change_fd_for_box (&THIS->box, -1);    -  +  if ( (THIS->flags & FILE_NOT_OPENED) ) +  return; +     while(1)    {    int i, e;    THREADS_ALLOW_UID();    i=fd_close(fd);    e=errno;    THREADS_DISALLOW_UID();    -  +  /* fprintf(stderr, "fd_close(%d): ret: %d, errno: %d\n", fd, i, e); */ +     check_threads_etc();       if(i < 0)    { -  +  ERRNO = errno = e;    switch(e)    { -  default: { +  default: +  push_int(e); +  f_strerror(1); +  +  if (quiet) { +  /* NB: FIXME: This has quite a bit of overhead... */    JMP_BUF jmp;    if (SETJMP (jmp))    call_handle_error();    else { -  ERRNO=errno=e; -  change_fd_for_box (&THIS->box, fd); -  push_int(e); -  f_strerror(1); +     Pike_error("Failed to close file: %S\n", Pike_sp[-1].u.string);    }    UNSETJMP (jmp); -  break; +  } else { +  Pike_error("Failed to close file: %S\n", Pike_sp[-1].u.string);    } -  -  case EBADF: +     break;    - #ifdef SOLARIS -  /* It's actually OK. This is a bug in Solaris 8. */ -  case EAGAIN: + #if 0 + #ifdef ENOSPC +  case ENOSPC: +  /* FreeBSD: The underlying object did not fit, cached data was lost. */    break;   #endif -  -  case EINTR: -  continue; -  } -  } + #endif + #ifdef ECONNRESET +  case ECONNRESET: +  /* FreeBSD: The peer shut down the connection before all pending data +  * was delivered. +  */    break; -  } - } + #endif    - static void close_fd(void) - { -  int fd=FD; -  if(fd<0) return; -  -  free_fd_stuff(); -  SUB_FD_EVENTS (THIS, ~0); -  change_fd_for_box (&THIS->box, -1); -  -  if ( (THIS->flags & FILE_NOT_OPENED) ) -  return; -  -  while(1) -  { -  int i, e; -  THREADS_ALLOW_UID(); -  i=fd_close(fd); -  e=errno; -  THREADS_DISALLOW_UID(); -  -  check_threads_etc(); -  -  if(i < 0) -  { -  switch(e) -  { -  default: -  ERRNO=errno=e; -  change_fd_for_box (&THIS->box, fd); -  push_int(e); -  f_strerror(1); -  Pike_error("Failed to close file: %S\n", Pike_sp[-1].u.string); -  break; -  +     case EBADF: -  +  if (olde) { +  /* Probably an OS where fds are closed on EINTR (ie most). */ +  ERRNO = errno = 0; +  break; +  }    Pike_error("Internal error: Closing a non-active file descriptor %d.\n",fd);    break;      #ifdef SOLARIS    /* It's actually OK. This is a bug in Solaris 8. */    case EAGAIN:    break;   #endif -  +     case EINTR: -  +  olde = e;    continue;    }    }    break;    }   }      void my_set_close_on_exec(int fd, int to)   {    set_close_on_exec(fd, to);
pike.git/src/modules/_Stdio/file.c:653:    case FILE_READ | FILE_WRITE: ret=fd_RDWR; break;    }    if(flags & FILE_APPEND) ret|=fd_APPEND;    if(flags & FILE_CREATE) ret|=fd_CREAT;    if(flags & FILE_TRUNC) ret|=fd_TRUNC;    if(flags & FILE_EXCLUSIVE) ret|=fd_EXCL;    ret |= fd_LARGEFILE;    return ret;   }    - static void free_dynamic_buffer(dynamic_buffer *b) { free(b->s.str); } + /* read only once */ + #define PIKE_READ_ONCE 1U    -  + /* ignore the number of bytes, read as much as possible. +  * count is used as the numer of bytes read per call +  * to read(2). */ + #define PIKE_READ_NO_LENGTH 2U +    static struct pike_string *do_read(int fd, -  INT32 r, -  int all, -  int *err) +  size_t count, +  unsigned int mode, +  INT_TYPE *err)   { -  ONERROR ebuf; -  ptrdiff_t bytes_read, i; -  INT32 try_read; -  bytes_read=0; -  *err=0; +  struct byte_buffer buf = BUFFER_INIT(); +  int e = 0;    -  if(r <= DIRECT_BUFSIZE || -  (all && (r<<1) > r && (((r-1)|r)+1)!=(r<<1))) /* r<<1 != power of two */ -  { -  struct pike_string *str; +  buffer_set_flags(&buf, BUFFER_GROW_EXACT);    -  i=r; +  while (1) {    -  str=begin_shared_string(r); -  -  SET_ONERROR(ebuf, do_free_unlinked_pike_string, str); -  -  do{ -  int fd=FD; -  int e; +     THREADS_ALLOW();    -  try_read = i; -  i = fd_read(fd, str->str+bytes_read, i); -  e=errno; -  THREADS_DISALLOW(); +  while (count) { +  size_t len = MINIMUM(DIRECT_BUFSIZE, count); +  ptrdiff_t bytes_read;    -  check_threads_etc(); -  -  if(i>0) -  { -  r-=i; -  bytes_read+=i; -  if(!all) break; -  } -  else if(i==0) -  { +  /* make space for exactly len bytes plus the terminating null byte. */ +  /* NOTE: as long as count comes from pike, it was signed, i.e. len+1 +  * cannot overflow */ +  if (UNLIKELY(!buffer_ensure_space_nothrow(&buf, len+1))) { +  e = ENOMEM;    break;    } -  else if(e != EINTR) -  { -  *err=e; -  if(!bytes_read) -  { -  do_free_unlinked_pike_string(str); -  UNSET_ONERROR(ebuf); -  return 0; -  } -  break; -  } +     -  /* -  * Large reads cause the kernel to validate more memory before -  * starting the actual read, and hence cause slowdowns. -  * Ideally you read as much as you're going to receive. -  * This buffer calculation algorithm tries to optimise the -  * read length depending on past read chunksizes. -  * For network reads this allows it to follow the packetsizes. -  * The initial read will attempt the full buffer. /srb -  */ -  if( i < try_read ) -  i += SMALL_NETBUF; -  else -  i += (r-i)>>1; -  if (i < SMALL_NETBUF) -  i = SMALL_NETBUF; -  if(i > r-SMALL_NETBUF) -  i = r; -  }while(r); +  bytes_read = fd_read(fd, buffer_alloc_unsafe(&buf, len), len);    -  UNSET_ONERROR(ebuf); +  if (LIKELY(bytes_read >= 0)) { +  /* if less than len were read, rewind the buffer to +  * the last byte */ +  if ((size_t)bytes_read < len) +  buffer_remove(&buf, len - bytes_read);    -  if(!SAFE_IS_ZERO(& THIS->event_cbs[PIKE_FD_READ])) -  ADD_FD_EVENTS (THIS, PIKE_BIT_FD_READ); +  if (!(mode & PIKE_READ_NO_LENGTH)) +  count -= bytes_read;    -  if(bytes_read == str->len) -  { -  return end_shared_string(str); -  }else{ -  return end_and_resize_shared_string(str, bytes_read); +  if (!bytes_read || mode & PIKE_READ_ONCE) break; +  } else { +  e=errno; +  buffer_remove(&buf, len); +  break;    } -  +  }    -  }else{ -  /* For some reason, 8k seems to work faster than 64k. -  * (4k seems to be about 2% faster than 8k when using linux though) -  * /Hubbe (Per pointed it out to me..) -  * -  * The slowdowns most likely are because of memory validation -  * done by the kernel for buffer space which is later unused -  * (short read) /srb -  */ -  dynamic_buffer b; -  -  b.s.str=0; -  initialize_buf(&b); -  SET_ONERROR(ebuf, free_dynamic_buffer, &b); -  i = all && (r<<1)>r ? DIRECT_BUFSIZE : READ_BUFFER; -  do{ -  int e; -  char *buf; -  -  try_read = i; -  -  buf = low_make_buf_space(try_read, &b); -  -  THREADS_ALLOW(); -  i = fd_read(fd, buf, try_read); -  e=errno; +     THREADS_DISALLOW();       check_threads_etc();    -  if(i>=0) -  { -  bytes_read+=i; -  r-=i; -  if(try_read > i) -  low_make_buf_space(i - try_read, &b); -  if(!all || !i) break; +  if (e == EINTR) { +  e = 0; +  continue;    } -  else -  { -  low_make_buf_space(-try_read, &b); -  if(e != EINTR) -  { -  *err=e; -  if(!bytes_read) -  { -  free(b.s.str); -  UNSET_ONERROR(ebuf); -  return 0; -  } +     break;    } -  +  +  if (e && !buffer_content_length(&buf)) { +  buffer_free(&buf); +  *err = e; +  return NULL;    }    -  /* See explanation in previous loop above */ -  if( i < try_read ) -  i += SMALL_NETBUF; -  else -  i += (DIRECT_BUFSIZE-i)>>1; -  if (i < SMALL_NETBUF) -  i = SMALL_NETBUF; -  if(i > r-SMALL_NETBUF) -  i = r; -  }while(r); -  -  UNSET_ONERROR(ebuf); -  +     if(!SAFE_IS_ZERO(& THIS->event_cbs[PIKE_FD_READ]))    ADD_FD_EVENTS (THIS, PIKE_BIT_FD_READ);    -  return low_free_buf(&b); +  return buffer_finish_pike_string(&buf);   } - } +       /* This function is used to analyse anonymous fds, so that    * my_file->open_mode can be set properly. */   static int low_fd_query_properties(int fd)   {    struct stat st;    PIKE_SOCKADDR addr;    ACCEPT_SIZE_T len;    int i;    int orig_errno = errno;
pike.git/src/modules/_Stdio/file.c:933:    *! The capability of sending and receiving remote file    *! descriptors is only available on some operating systems.    *! This capability is indicated by the precence of @[__HAVE_SEND_FD__].    *!    *! @seealso    *! @[send_fd()], @[read()], @[fd_factory()], @[__HAVE_SEND_FD__]    */   #ifdef HAVE_PIKE_SEND_FD   static void receive_fds(int *fds, size_t num_fds)   { -  size_t i; +  volatile size_t i;       for (i = 0; i < num_fds; i++) {    int fd = fds[i];    if (fd >= 0) {    JMP_BUF jmp;    if (SETJMP(jmp))    call_handle_error();    else {    push_new_fd_object(fd_fd_factory_fun_num, fd,    low_fd_query_properties(fd), 0);
pike.git/src/modules/_Stdio/file.c:988:   #else    receive_fds((int *)msg->msg_accrights, msg->msg_accrightslen/sizeof(int));   #endif   }      #ifndef HAVE_STRUCT_MSGHDR_MSG_CONTROL   /* BSD */   #define CMSG_LEN(x) (x)   #endif    - static struct pike_string *do_recvmsg(INT32 r, int all) + static struct pike_string *do_recvmsg(int fd, size_t count, unsigned INT32 mode, INT_TYPE *err)   { -  ONERROR ebuf; -  ptrdiff_t bytes_read = 0, i; -  int fd = FD; +     struct {    struct msghdr msg;    struct iovec iov;    char cmsgbuf[CMSG_LEN(sizeof(int)*128)];    } message; -  +  struct byte_buffer buf = BUFFER_INIT(); +  int e = 0; +  ptrdiff_t bytes_read = 0;    -  ERRNO=0; -  +     message.msg.msg_name = NULL;    message.msg.msg_namelen = 0;    message.msg.msg_iov = &message.iov;    message.msg.msg_iovlen = 1;   #ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL    /* XPG 4.2 */    message.msg.msg_flags = 0;   #endif    -  if(r <= 65536) -  { -  struct pike_string *str; +  buffer_set_flags(&buf, BUFFER_GROW_EXACT);    -  str=begin_shared_string(r); +  while (1) {    -  SET_ONERROR(ebuf, do_free_unlinked_pike_string, str); +  THREADS_ALLOW();    -  do{ -  int fd=FD; -  int e; +  while (count) { +  size_t len = MINIMUM(DIRECT_BUFSIZE, count); +  +  /* make space for exactly len bytes plus the terminating null byte */ +  /* as long as count comes from pike, it was signed, i.e. len+1 is safe */ +  if (UNLIKELY(!buffer_ensure_space_nothrow(&buf, len+1))) { +  e = ENOMEM; +  break; +  } +    #ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL -  /* XPG 4.2 */ +     message.msg.msg_control = &message.cmsgbuf;    message.msg.msg_controllen = sizeof(message.cmsgbuf);   #else -  /* BSD */ +     message.msg.msg_accrights = (void *)&message.cmsgbuf;    message.msg.msg_accrightslen = sizeof(message.cmsgbuf);   #endif -  message.iov.iov_base = str->str + bytes_read; -  message.iov.iov_len = r; -  THREADS_ALLOW(); -  i = recvmsg(fd, &message.msg, 0); +  message.iov.iov_base = buffer_alloc_unsafe(&buf, len); +  message.iov.iov_len = len; +  +  bytes_read = recvmsg(fd, &message.msg, 0);    e=errno; -  THREADS_DISALLOW(); +     -  check_threads_etc(); +  if (LIKELY(bytes_read >= 0)) { +  /* if less than len were read, rewind the buffer to +  * the last byte */ +  if ((size_t)bytes_read < len) +  buffer_remove(&buf, len - bytes_read);    -  if(i>0) -  { -  r-=i; -  bytes_read+=i; +  if (!(mode & PIKE_READ_NO_LENGTH)) +  count -= bytes_read;    -  +  if (!bytes_read || mode & PIKE_READ_ONCE) break; +     if (   #ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL    message.msg.msg_controllen   #else    message.msg.msg_accrightslen   #endif    ) { -  check_message(&message.msg); -  } -  -  if(!all) break; -  } -  else if(i==0) -  { +  /* we have to call receive_fds, so break out of the loop */    break;    } -  else if(e != EINTR) -  { -  ERRNO=e; -  if(!bytes_read) -  { -  do_free_unlinked_pike_string(str); -  UNSET_ONERROR(ebuf); -  return 0; -  } +  } else { +  e=errno; +  buffer_remove(&buf, len);    break;    } -  }while(r); -  -  UNSET_ONERROR(ebuf); -  -  if(!SAFE_IS_ZERO(& THIS->event_cbs[PIKE_FD_READ])) -  ADD_FD_EVENTS (THIS, PIKE_BIT_FD_READ); -  -  if(bytes_read == str->len) -  { -  return end_shared_string(str); -  }else{ -  return end_and_resize_shared_string(str, bytes_read); +     }    -  }else{ -  /* For some reason, 8k seems to work faster than 64k. -  * (4k seems to be about 2% faster than 8k when using linux though) -  * /Hubbe (Per pointed it out to me..) -  */ - #define CHUNK ( 1024 * 8 ) -  INT32 try_read; -  dynamic_buffer b; -  -  b.s.str=0; -  initialize_buf(&b); -  SET_ONERROR(ebuf, free_dynamic_buffer, &b); -  do{ -  int e; -  char *buf; -  try_read=MINIMUM(CHUNK,r); -  - #ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL -  message.msg.msg_control = &message.cmsgbuf; -  message.msg.msg_controllen = sizeof(message.cmsgbuf); - #else -  message.msg.msg_accrights = (void *)&message.cmsgbuf; -  message.msg.msg_accrightslen = sizeof(message.cmsgbuf); - #endif -  message.iov.iov_base = low_make_buf_space(try_read, &b); -  message.iov.iov_len = try_read; -  -  THREADS_ALLOW(); -  i = recvmsg(fd, &message.msg, 0); -  e=errno; +     THREADS_DISALLOW();       check_threads_etc();    -  if(i>0) -  { -  bytes_read+=i; -  r-=i; -  if ( +  if (e) { +  if (e == EINTR) { +  e = 0; +  continue; +  } +  break; +  } +  +  if (UNLIKELY(   #ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL    message.msg.msg_controllen   #else    message.msg.msg_accrightslen   #endif -  ) { +  )) { +  ONERROR ebuf; +  SET_ONERROR(ebuf, buffer_free, &buf);    check_message(&message.msg); -  } -  if (i != try_read) { -  low_make_buf_space(i - try_read, &b); -  } -  if(!all) break; -  } -  else if(i==0) -  { -  low_make_buf_space(-try_read, &b); -  break; -  } -  else -  { -  low_make_buf_space(-try_read, &b); -  if(e != EINTR) -  { -  ERRNO=e; -  if(!bytes_read) -  { -  free(b.s.str); +     UNSET_ONERROR(ebuf); -  return 0; +  if (bytes_read && !(mode & PIKE_READ_ONCE)) continue;    }    break;    } -  } -  }while(r); +     -  UNSET_ONERROR(ebuf); -  +     if(!SAFE_IS_ZERO(& THIS->event_cbs[PIKE_FD_READ]))    ADD_FD_EVENTS (THIS, PIKE_BIT_FD_READ);    -  return low_free_buf(&b); - #undef CHUNK +  if (e) { +  *err = e; +  +  if (!buffer_content_length(&buf)) { +  buffer_free(&buf); +  return NULL;    }    }    -  +  return buffer_finish_pike_string(&buf); + } +    /* Send a set of iovecs and fds over an fd. */   static int writev_fds(int fd, struct iovec *iov, int iovcnt,    int *fds, int num_fds)   {    int retval, e;    struct msghdr msg;   #ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL    /* XPG 4.2 */      #ifndef CMSG_SPACE
pike.git/src/modules/_Stdio/file.c:1202:    if (!cmsg) {    errno = ENOMEM;    return -1;    }       msg.msg_control = cmsg;    msg.msg_controllen = cmsg->cmsg_len = CMSG_LEN(num_fds * sizeof(int));    cmsg->cmsg_level = SOL_SOCKET;    cmsg->cmsg_type = SCM_RIGHTS;    -  MEMCPY(CMSG_DATA(cmsg), fds, num_fds * sizeof(int)); +  memcpy(CMSG_DATA(cmsg), fds, num_fds * sizeof(int));    msg.msg_flags = 0;      #else    /* BSD */    msg.msg_accrights = (void *)fds;    msg.msg_accrightslen = num_fds * sizeof(int);   #endif    msg.msg_name = NULL;    msg.msg_namelen = 0;    msg.msg_iov = iov;
pike.git/src/modules/_Stdio/file.c:1226: Inside #if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
   e = errno;    free(cmsg);    errno = e;   #endif    return retval;   }      #endif /* HAVE_PIKE_SEND_FD */      static struct pike_string *do_read_oob(int UNUSED(fd), -  INT32 r, +  ptrdiff_t r,    int all, -  int *err) +  INT_TYPE *err)   {    ONERROR ebuf;    INT32 bytes_read,i;    struct pike_string *str;       bytes_read=0;    *err=0;       str=begin_shared_string(r);   
pike.git/src/modules/_Stdio/file.c:1357:    *! will return the empty string since it's not considered an error.    *! The empty string is never returned in other cases, unless nonblocking    *! mode is used or @[len] is zero.    *!    *! @seealso    *! @[read_oob()], @[write()], @[receive_fd()], @[send_fd()]    */   static void file_read(INT32 args)   {    struct pike_string *tmp; -  INT32 all, len; +  unsigned INT32 mode = 0; +  size_t count = DIRECT_BUFSIZE;       if(FD < 0)    Pike_error("File not open.\n");       if(!args)    { -  len=0x7fffffff; +  mode |= PIKE_READ_NO_LENGTH;    }    else    { -  +  INT_TYPE len;    if(TYPEOF(Pike_sp[-args]) != PIKE_T_INT) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->read()", 1, "int"); +  SIMPLE_ARG_TYPE_ERROR("read", 1, "int");    len=Pike_sp[-args].u.integer;    if(len<0)    Pike_error("Cannot read negative number of characters.\n"); -  +  if (!len && SUBTYPEOF(Pike_sp[-args])) { +  mode |= PIKE_READ_NO_LENGTH; +  } else { +  count = len;    } -  +  }       if(args > 1 && !UNSAFE_IS_ZERO(Pike_sp+1-args))    { -  all=0; -  }else{ -  all=1; +  mode |= PIKE_READ_ONCE;    }       pop_n_elems(args);      #ifdef HAVE_PIKE_SEND_FD    /* Check if there's any need to use recvmsg(2). */    if ((THIS->open_mode & fd_SEND_FD) &&    (THIS->flags & FILE_HAVE_RECV_FD)) { -  if ((tmp = do_recvmsg(len, all))) +  if ((tmp = do_recvmsg(FD, count, mode, & ERRNO)))    push_string(tmp);    else {    errno = ERRNO;    push_int(0);    }    } else   #endif /* HAVE_PIKE_SEND_FD */ -  if((tmp=do_read(FD, len, all, & ERRNO))) +  if((tmp=do_read(FD, count, mode, & ERRNO)))    push_string(tmp);    else {    errno = ERRNO;    push_int(0);    }       if (!(THIS->open_mode & FILE_NONBLOCKING))    INVALIDATE_CURRENT_TIME();       /* Race: A backend in another thread might have managed to set these
pike.git/src/modules/_Stdio/file.c:1478:    *! has been updated.    *! @endint    *!    *! @seealso    *! @[errno()], @[read()]    *!    *! @note    *! The function may be interrupted prematurely    *! of the timeout (due to signals);    *! check the timing manually if this is imporant. -  *! -  *! @note -  *! The @[not_eof] parameter was added in Pike 7.7. -  *! -  *! @note -  *! This function was not available on NT in Pike 7.6 and earlier. +     */   static void file_peek(INT32 args)   {    int ret;    int not_eof = 0;    FLOAT_TYPE tf = 0.0;    -  get_all_args("peek",args,".%F%d",&tf,&not_eof); +  get_all_args(NULL, args, ".%F%d", &tf, &not_eof);       {   #ifdef HAVE_AND_USE_POLL    struct pollfd fds;    int timeout = 0;    if (args && !IS_UNDEFINED(Pike_sp - args)) {    timeout = (int)(tf*1000); /* ignore overflow for now */    }    fds.fd=FD;    fds.events=POLLIN;
pike.git/src/modules/_Stdio/file.c:1612:    *! problem persists then a later call to @[read_oob()] fails and    *! returns zero, however.    *!    *! If everything went fine, a call to @[errno()] directly afterwards    *! returns zero. That includes an end due to remote close.    *!    *! If no arguments are given, @[read_oob()] reads to the end of the    *! stream.    *!    *! @note -  *! Out-of-band data was not supported in Pike 0.5 and earlier, and -  *! not in Pike 0.6 through 7.4 if they were compiled with the -  *! option @tt{'--without-oob'@}. -  *! -  *! @note +     *! It is not guaranteed that all out-of-band data sent from the    *! other end is received. Most streams only allow for a single byte    *! of out-of-band data at a time.    *!    *! @note    *! It's not necessary to set @[not_all] to avoid blocking reading    *! when nonblocking mode is used.    *!    *! @note    *! When at the end of a file or stream, repeated calls to @[read()]
pike.git/src/modules/_Stdio/file.c:1649:    if(FD < 0)    Pike_error("File not open.\n");       if(!args)    {    len=0x7fffffff;    }    else    {    if(TYPEOF(Pike_sp[-args]) != PIKE_T_INT) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->read_oob", 1, "int"); +  SIMPLE_ARG_TYPE_ERROR("read_oob", 1, "int");    len=Pike_sp[-args].u.integer;    if(len<0)    Pike_error("Cannot read negative number of characters.\n");    }       if(args > 1 && !UNSAFE_IS_ZERO(Pike_sp+1-args))    {    all=0;    }else{    all=1;
pike.git/src/modules/_Stdio/file.c:1708: Inside #if defined(__NT__)
   if (!(fd_query_properties(f->box.fd, fd_CAN_NONBLOCK) & fd_CAN_NONBLOCK)) {    Pike_error("Setting backend callback on a non-socket!\n");    }   #endif /* __NT__ */    assign_svalue (&f->event_cbs[event], cb);    ADD_FD_EVENTS2 (f, 1 << event, flags);    }   }      #undef CBFUNCS +    #define CBFUNCS(CB, EVENT) \    static void PIKE_CONCAT(file_set_,CB) (INT32 args) \    { \    if(!args) \ -  SIMPLE_TOO_FEW_ARGS_ERROR("Stdio.File->set_" #CB, 1); \ +  SIMPLE_WRONG_NUM_ARGS_ERROR("set_" #CB, 1); \    set_fd_event_cb (THIS, Pike_sp-args, EVENT, 0); \ -  } \ -  \ -  static void PIKE_CONCAT(file_query_,CB) (INT32 args) \ -  { \ -  pop_n_elems(args); \ -  push_svalue(& THIS->event_cbs[EVENT]); \ +     }      #define CBFUNCS2(CB, EVENT) \    static void PIKE_CONCAT(file_set_,CB) (INT32 args) \    { \    if(args<2) \ -  SIMPLE_TOO_FEW_ARGS_ERROR("Stdio.File->set_" #CB, 2); \ +  SIMPLE_WRONG_NUM_ARGS_ERROR("set_" #CB, 2); \    if (TYPEOF(Pike_sp[1-args]) != PIKE_T_INT) \ -  SIMPLE_ARG_TYPE_ERROR("Stdio.File->set_" #CB, 2, "int"); \ +  SIMPLE_ARG_TYPE_ERROR("set_" #CB, 2, "int"); \    set_fd_event_cb (THIS, Pike_sp-args, EVENT, \    Pike_sp[1-args].u.integer); \ -  } \ -  \ -  static void PIKE_CONCAT(file_query_,CB) (INT32 args) \ -  { \ -  pop_n_elems(args); \ -  push_svalue(& THIS->event_cbs[EVENT]); \ +     }      CBFUNCS(read_callback, PIKE_FD_READ)   CBFUNCS(write_callback, PIKE_FD_WRITE)   CBFUNCS(read_oob_callback, PIKE_FD_READ_OOB)   CBFUNCS(write_oob_callback, PIKE_FD_WRITE_OOB)   CBFUNCS2(fs_event_callback, PIKE_FD_FS_EVENT)         static void file_query_fs_event_flags(INT32 args)   {    short flags;    pop_n_elems(args);       flags = get_fd_event_flags(THIS);    push_int(flags);   }       - static void file__enable_callbacks(INT32 args) + static void file__enable_callbacks(INT32 UNUSED(args))   {    struct my_file *f = THIS;    size_t ev;    int cb_events = 0;      #if 0    if(FD<0)    Pike_error("File is not open.\n");   #endif   
pike.git/src/modules/_Stdio/file.c:1780:    if (!UNSAFE_IS_ZERO (&f->event_cbs[ev]))    cb_events |= 1 << ev;       if (cb_events) {    if (FD >= 0 &&    !(fd_query_properties(FD, fd_CAN_NONBLOCK) & fd_CAN_NONBLOCK))    Pike_error("Callback operations not supported for this file.\n");       ADD_FD_EVENTS (f, cb_events);    } -  -  pop_n_elems(args); -  push_int(0); +    }    - static void file__disable_callbacks(INT32 args) + static void file__disable_callbacks(INT32 UNUSED(args))   {    struct my_file *f = THIS;      #if 0    if(FD<0)    Pike_error("File is not open.\n");   #endif       SUB_FD_EVENTS (f, ~0); -  -  pop_n_elems(args); -  push_int(0); +    }         /*! @decl int write(string data)    *! @decl int write(string format, mixed ... extras)    *! @decl int write(array(string) data)    *! @decl int write(array(string) format, mixed ... extras)    *!    *! Write data to a file or a stream.    *! -  *! Writes @[data] and returns the number of bytes that were -  *! actually written. It can be less than the size of the given data if +  *! If there are any file descriptors that have been queued for sending +  *! (with @[send_fd()]), they will be sent.    *! -  *! @ul -  *! @item -  *! some data was written successfully and then something went -  *! wrong, or -  *! @item -  *! nonblocking mode is used and not all data could be written -  *! without blocking. -  *! @endul +  *! @param data +  *! Data to write.    *! -  *! -1 is returned if something went wrong and no bytes were written. -  *! If only some data was written due to an error and that error -  *! persists, then a later call to @[write()] fails and returns -1. +  *! If @[data] is an array of strings, they are written in sequence.    *! -  *! If everything went fine, a call to @[errno()] directly afterwards -  *! returns zero. +  *! @param format +  *! @param extras +  *! If more than one argument is given, @[sprintf()] is used to format +  *! them using @[format]. If @[format] is an array, the strings in it +  *! are concatenated and the result is used as format string.    *! -  *! If @[data] is an array of strings, they are written in sequence. +  *! @returns +  *! Writes @[data] and returns the number of bytes that were +  *! actually written.    *! -  *! If more than one argument is given, @[sprintf()] is used to format -  *! them using @[format]. If @[format] is an array, the strings in it -  *! are concatenated and the result is used as format string. +  *! @int +  *! @value 1.. +  *! The number of bytes successfully written to the OS buffers.    *! -  *! If there are any file descriptors that have been queued for sending -  *! (with @[send_fd()]), they will be sent. +  *! This can be less than the size of the given data if eg: +  *! @ul +  *! @item +  *! Some data was written successfully and then something went +  *! wrong.    *! -  +  *! If only some data was written due to an error and that error +  *! persists, then a later call to @[write()] will fail and return +  *! @expr{-1@}. +  *! +  *! @item +  *! Nonblocking mode is used and not all data could be written +  *! without blocking. +  *! @endul +  *! +  *! @value 0 +  *! No bytes were written. This may be due to +  *! @ul +  *! @item +  *! @[data] or the formatted data being the empty string. +  *! +  *! @item +  *! Nonblocking mode is used and no data could be written +  *! without blocking. +  *! @endul +  *! +  *! @value -1 +  *! Something went wrong and no bytes were written. +  *! @endint +  *! +  *! If everything went fine, a call to @[errno()] directly afterwards +  *! returns zero. +  *!    *! @note    *! Writing of wide strings is not supported. You have to encode the    *! data somehow, e.g. with @[string_to_utf8] or with one of the    *! charsets supported by @[Charset.encoder].    *!    *! @seealso    *! @[read()], @[write_oob()], @[send_fd()]    */   static void file_write(INT32 args)   {    ptrdiff_t written, i;    struct pike_string *str;       if(args<1 || ((TYPEOF(Pike_sp[-args]) != PIKE_T_STRING) &&    (TYPEOF(Pike_sp[-args]) != PIKE_T_ARRAY))) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->write()", 1, "string|array(string)"); +  SIMPLE_ARG_TYPE_ERROR("write", 1, "string|array(string)");       if(FD < 0)    Pike_error("File not open for write.\n");       if (TYPEOF(Pike_sp[-args]) == PIKE_T_ARRAY) {    struct array *a = Pike_sp[-args].u.array;       if( (a->type_field & ~BIT_STRING) &&    (array_fix_type_field(a) & ~BIT_STRING) ) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->write()", 1, "string|array(string)"); +  SIMPLE_ARG_TYPE_ERROR("write", 1, "string|array(string)");       i = a->size;    while(i--)    if (a->item[i].u.string->size_shift)    Pike_error("Bad argument 1 to file->write().\n"    "Element %ld is a wide string.\n", -  DO_NOT_WARN((long)i)); +  (long)i);      #ifdef HAVE_WRITEV    if (args > 1) {   #endif /* HAVE_WRITEV */    ref_push_array(a);    push_empty_string();    o_multiply();    Pike_sp--;    dmalloc_touch_svalue(Pike_sp);    Pike_sp[-args] = *Pike_sp;
pike.git/src/modules/_Stdio/file.c:1894:    }   #endif /* PIKE_DEBUG */   #ifdef HAVE_WRITEV    } else if (!a->size) {    /* Special case for empty array */    ERRNO = 0;    pop_stack();    push_int(0);    return;    } else { -  struct iovec *iovbase = -  (struct iovec *)xalloc(sizeof(struct iovec)*a->size); +  struct iovec *iovbase = xalloc(sizeof(struct iovec)*a->size);    struct iovec *iov = iovbase;    int iovcnt = a->size;       if (!(THIS->open_mode & FILE_NONBLOCKING))    INVALIDATE_CURRENT_TIME();       i = a->size;    while(i--) {    if (a->item[i].u.string->len) {    iov[i].iov_base = a->item[i].u.string->str;
pike.git/src/modules/_Stdio/file.c:1950: Inside #if defined(HAVE_WRITEV)
   THREADS_DISALLOW();       /* fprintf(stderr, "writev(%d, 0x%08x, %d) => %d\n",    fd, (unsigned int)iov, cnt, i); */       e=errno; /* check_threads_etc may effect errno */    check_threads_etc();       if(i<0)    { -  /* perror("writev"); */ +    #ifdef HAVE_PIKE_SEND_FD    if (fd_info) {    restore_fd_info(fd_info);    }   #endif    switch(e)    {    default:    free(iovbase);    ERRNO=errno=e;    pop_n_elems(args);    if (!written) {    push_int(-1);    } else {    push_int(written);    } -  +  /* Minor race - see below. */ +  THIS->box.revents &= ~(PIKE_BIT_FD_WRITE|PIKE_BIT_FD_WRITE_OOB);    return;       case EINTR: continue;    case EWOULDBLOCK: break;    /* FIXME: Special case for ENOTSOCK? */    }    break;    }else{    written += i;   
pike.git/src/modules/_Stdio/file.c:2013: Inside #if defined(HAVE_WRITEV) and #if defined(_REENTRANT)
  #ifdef _REENTRANT    if (FD<0) {    free(iovbase);    Pike_error("File closed while in file->write.\n");    }   #endif    }       free(iovbase);    +  /* Minor race - see below. */ +  THIS->box.revents &= ~(PIKE_BIT_FD_WRITE|PIKE_BIT_FD_WRITE_OOB); +     if(!SAFE_IS_ZERO(& THIS->event_cbs[PIKE_FD_WRITE]))    ADD_FD_EVENTS (THIS, PIKE_BIT_FD_WRITE);    ERRNO=0;       pop_stack();    push_int(written);    return;    }   #endif /* HAVE_WRITEV */    }
pike.git/src/modules/_Stdio/file.c:2149:    *! persists, then a later call to @[write_oob()] fails and returns    *! -1.    *!    *! If everything went fine, a call to @[errno()] directly afterwards    *! returns zero.    *!    *! If more than one argument is given, @[sprintf()] is used to format    *! them.    *!    *! @note -  *! Out-of-band data was not supported in Pike 0.5 and earlier, and -  *! not in Pike 0.6 through 7.4 if they were compiled with the -  *! option @tt{'--without-oob'@}. -  *! -  *! @note +     *! It is not guaranteed that all out-of-band data sent from the    *! other end is received. Most streams only allow for a single byte    *! of out-of-band data at a time. Some streams sends the rest of    *! the data as ordinary data.    *!    *! @seealso    *! @[read_oob()], @[write()]    */   static void file_write_oob(INT32 args)   {    ptrdiff_t written, i;    struct pike_string *str;       if(args<1 || TYPEOF(Pike_sp[-args]) != PIKE_T_STRING) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->write_oob()",1,"string"); +  SIMPLE_ARG_TYPE_ERROR("write_oob",1,"string");       if(args > 1)    {    f_sprintf(args);    args=1;    }       if(FD < 0)    Pike_error("File not open for write_oob.\n");   
pike.git/src/modules/_Stdio/file.c:2270: Inside #if defined(HAVE_PIKE_SEND_FD)
   *! for @[__HAVE_SEND_FD__].    *!    *! The queue is emptied on successful @[write()] and when the    *! write direction is @[close()]'d.    *!    *! @seealso    *! @[receive_fd()], @[write()], @[pipe()], @[read()], @[__HAVE_SEND_FD__]    */   static void file_send_fd(INT32 args)   { -  int e; +     int other_fd;    struct object *o = NULL;    struct my_file *f = NULL;    int *fd_info = NULL;       if(args<1 || (TYPEOF(Pike_sp[-args]) != PIKE_T_OBJECT) ||    !(o = Pike_sp[-args].u.object)->prog ||    (o->prog->inherits[SUBTYPEOF(Pike_sp[-args])].prog != file_program)) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->send_fd()", 1, "object(is Stdio.Fd)"); +  SIMPLE_ARG_TYPE_ERROR("send_fd", 1, "Stdio.Fd");       f = (struct my_file *)    (o->storage + o->prog->inherits[SUBTYPEOF(Pike_sp[-args])].storage_offset);       if (f->box.fd == -1) {    Pike_error("Bad argument 1 to Stdio.File->send_fd(): "    "File descriptor not open.\n");    }       if(FD < 0)
pike.git/src/modules/_Stdio/file.c:2312: Inside #if defined(HAVE_PIKE_SEND_FD)
   if (fds) {    fds[0] = 8;    fds[1] = 0;    }    }    if (!fds)    Pike_error("Out of memory.\n");    THIS->fd_info = fd_info = fds;   #ifdef PIKE_DEBUG    /* Note: Unusual range. */ +  { +  int e;    for (e = fds[0]-2; e > fds[1]; e--) {    fds[e + 1] = -1;    } -  +  }   #endif    }       do {    other_fd = dup(f->box.fd);    } while((other_fd < 0) && (errno == EINTR));       if (other_fd < 0) {    Pike_error("Out of file descriptors.\n");    }
pike.git/src/modules/_Stdio/file.c:2371: Inside #if defined(SO_LINGER)
   */   static void file_linger(INT32 args)   {    int fd = FD;    int linger = -1;    struct linger li;       if(fd < 0)    Pike_error("File not open.\n");    -  get_all_args("Stdio.File->linger", args, ".%d", &linger); +  get_all_args(NULL, args, ".%d", &linger);       if ((linger < -1) || (linger > 0xffff)) { -  SIMPLE_BAD_ARG_ERROR("Stdio.File->linger()", 1, "int(-1..65535)"); +  SIMPLE_ARG_TYPE_ERROR("linger", 1, "int(-1..65535)");    }       if (linger == -1) {    li.l_onoff = 0;    li.l_linger = 15;    } else {    li.l_onoff = 1;    li.l_linger = linger;    }   
pike.git/src/modules/_Stdio/file.c:2399: Inside #if defined(SO_LINGER)
   }    if (errno) {    ERRNO = errno;    push_int(0);    } else {    push_int(1);    }   }   #endif    + #ifdef TCP_NODELAY + /*! @decl int(0..1) set_nodelay(int(0..1)|void state) +  *! +  *! Control Nagle's Algorithm (RFC 896) +  *! +  *! @param state +  *! @int +  *! @value 0 +  *! Return to the normal state of using Nagle's Algorithm +  *! @value 1 +  *! (default) Disable Nagling - small writes will not be queued. +  *! @endint +  *! +  *! @returns +  *! Returns @expr{1@} on success, and @expr{0@} (zero) on failure. +  *! +  *! @note +  *! This operation is only valid on sockets. +  *! +  *! @seealso +  *! setsockopt() +  */ + static void file_nodelay(INT32 args) + { +  int fd = FD; +  int state = 1; +  +  if(fd < 0) +  Pike_error("File not open.\n"); +  +  get_all_args(NULL, args, ".%d", &state); +  +  if (state && state != 1) { +  SIMPLE_ARG_TYPE_ERROR("set_nodelay()", 1, "int(0..1)"); +  } +  +  errno = 0; +  while ((fd_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, +  &state, sizeof(state)) < 0) && +  (errno == EINTR)) { +  errno = 0; +  } +  if (errno) { +  ERRNO = errno; +  push_int(0); +  } else { +  push_int(1); +  } + } + #endif +  + #ifndef SHUT_RD + #define SHUT_RD 0 + #endif + #ifndef SHUT_WR + #define SHUT_WR 1 + #endif +    static int do_close(int flags)   {    struct my_file *f = THIS;    if(FD == -1) return 1; /* already closed */    ERRNO=0;       flags &= f->open_mode;       switch(flags & (FILE_READ | FILE_WRITE))    {    case 0:    return 0;       case FILE_READ:    if(f->open_mode & FILE_WRITE)    {    SUB_FD_EVENTS (f, PIKE_BIT_FD_READ|PIKE_BIT_FD_READ_OOB|PIKE_BIT_FD_FS_EVENT); -  fd_shutdown(FD, 0); +  fd_shutdown(FD, SHUT_RD);    f->open_mode &=~ FILE_READ;    return 0;    }else{    f->flags&=~FILE_NOT_OPENED; -  close_fd(); +  close_fd(0);    return 1;    }       case FILE_WRITE:    if(f->open_mode & FILE_READ)    {    SUB_FD_EVENTS (f, PIKE_BIT_FD_WRITE|PIKE_BIT_FD_WRITE_OOB|PIKE_BIT_FD_FS_EVENT); -  fd_shutdown(FD, 1); +  fd_shutdown(FD, SHUT_WR);    f->open_mode &=~ FILE_WRITE;   #ifdef HAVE_PIKE_SEND_FD    if (f->fd_info) do_close_fd_info(f->fd_info);   #endif    return 0;    }else{    f->flags&=~FILE_NOT_OPENED; -  close_fd(); +  close_fd(0);    return 1;    }       case FILE_READ | FILE_WRITE:    f->flags&=~FILE_NOT_OPENED; -  close_fd(); +  close_fd(0);    return 1;       default:    Pike_fatal("Bug in switch implementation!\n"); -  return 0; /* Make CC happy */ +  UNREACHABLE(return 0);    }   }      /*! @decl string grantpt()    *!    *! If this file has been created by calling @[openpt()], return the    *! filename of the associated pts-file. This function should only be    *! called once.    *!    *! @returns    *! Returns the filename of the corresponding pts.    *!    *! @note    *! This function is only available on some platforms.    */   #if defined(HAVE_GRANTPT) || defined(USE_PT_CHMOD) || defined(USE_CHGPT)   static void file_grantpt( INT32 args )   {    pop_n_elems(args);   #if defined(USE_PT_CHMOD) || defined(USE_CHGPT) -  push_constant_text("Process.Process"); +  push_static_text("Process.Process");    APPLY_MASTER("resolv", 1);      #ifdef USE_PT_CHMOD    /* pt_chmod wants to get the fd number as the first argument. */ -  push_constant_text(USE_PT_CHMOD); -  push_constant_text("4"); +  push_text(USE_PT_CHMOD); +  push_static_text("4");    f_aggregate(2);       /* Send the pty as both fd 3 and fd 4. */ -  push_constant_text("fds"); +  push_static_text("fds");    ref_push_object(Pike_fp->current_object);    ref_push_object(Pike_fp->current_object);    f_aggregate(2);    f_aggregate_mapping(2);   #else /* USE_CHGPT */    /* chgpt on HPUX doesn't like getting any arguments... */ -  push_constant_text(USE_CHGPT); +  push_text(USE_CHGPT);    f_aggregate(1);       /* chgpt wants to get the pty on fd 0. */ -  push_constant_text("stdin"); +  push_static_text("stdin");    ref_push_object(Pike_fp->current_object);    f_aggregate_mapping(2);   #endif /* USE_PT_CHMOD */       apply_svalue(Pike_sp-3, 2);    apply(Pike_sp[-1].u.object, "wait", 0);    if(!UNSAFE_IS_ZERO(Pike_sp-1)) {    Pike_error(   #ifdef USE_PT_CHMOD    USE_PT_CHMOD
pike.git/src/modules/_Stdio/file.c:2530:   #endif   }   #endif /* HAVE_GRANTPT || USE_PT_CHMOD || USE_CHGPT */      /*! @decl int close()    *! @decl int close(string direction)    *!    *! Close a file or stream.    *!    *! If direction is not specified, both the read and the write -  *! direction is closed. Otherwise only the directions specified is +  *! direction are closed. Otherwise only the directions specified is    *! closed.    *!    *! @returns -  *! Nonzero is returned if the file or stream wasn't open in the -  *! specified direction, zero otherwise. +  *! Returns @expr{1@} if the file or stream now is closed in +  *! all directions, and @expr{0@} otherwise.    *!    *! @throws    *! An exception is thrown if an I/O error occurs.    *!    *! The default behaviour for sockets is typically to flush buffered    *! data in the background, but this can be changed with @[linger()].    *!    *! @note    *! @[close()] has no effect if this file object has been associated    *! with an already opened file, i.e. if @[open()] was given an
pike.git/src/modules/_Stdio/file.c:2653:    *!    *! @seealso    *! @[close()]    */   static void file_open(INT32 args)   {    int flags,fd;    int access;    int err;    struct pike_string *str, *flag_str; -  close_fd(); +  close_fd(0);       if(args < 2) -  SIMPLE_TOO_FEW_ARGS_ERROR("Stdio.File->open", 2); +  SIMPLE_WRONG_NUM_ARGS_ERROR("open", 2);       if(TYPEOF(Pike_sp[-args]) != PIKE_T_STRING &&    TYPEOF(Pike_sp[-args]) != PIKE_T_INT) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->open", 1, "string|int"); +  SIMPLE_ARG_TYPE_ERROR("open", 1, "string|int");       if(TYPEOF(Pike_sp[1-args]) != PIKE_T_STRING) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->open", 2, "string"); +  SIMPLE_ARG_TYPE_ERROR("open", 2, "string");       if (args > 2)    {    if (TYPEOF(Pike_sp[2-args]) != PIKE_T_INT) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->open", 3, "int"); +  SIMPLE_ARG_TYPE_ERROR("open", 3, "int");    access = Pike_sp[2-args].u.integer;    } else    access = 00666;       flags = parse((flag_str = Pike_sp[1-args].u.string)->str);       if (TYPEOF(Pike_sp[-args]) == PIKE_T_STRING)    {    str=Pike_sp[-args].u.string;       if (string_has_null(str)) {    /* Filenames with NUL are not supported. */    ERRNO = errno = ENOENT;    pop_n_elems(args);    push_int(0);    return;    }    - #ifdef PIKE_SECURITY -  if(!CHECK_SECURITY(SECURITY_BIT_SECURITY)) -  { -  if(!CHECK_SECURITY(SECURITY_BIT_CONDITIONAL_IO)) -  Pike_error("Permission denied.\n"); -  -  if(flags & (FILE_APPEND | FILE_TRUNC | FILE_CREATE | FILE_WRITE)) -  { -  push_text("write"); -  }else{ -  push_text("read"); -  } -  -  ref_push_object(Pike_fp->current_object); -  ref_push_string(str); -  ref_push_string(flag_str); -  push_int(access); -  -  safe_apply(OBJ2CREDS(CURRENT_CREDS)->user,"valid_open",5); -  switch(TYPEOF(Pike_sp[-1])) -  { -  case PIKE_T_INT: -  switch(Pike_sp[-1].u.integer) -  { -  case 0: /* return 0 */ -  ERRNO=errno=EPERM; -  pop_n_elems(args+1); -  push_int(0); -  return; -  -  case 1: /* return 1 */ -  pop_n_elems(args+1); -  push_int(1); -  return; -  -  case 2: /* ok */ -  pop_stack(); -  break; -  -  case 3: /* permission denied */ -  Pike_error("Stdio.file->open: permission denied.\n"); -  -  default: -  Pike_error("Error in user->valid_open, wrong return value.\n"); -  } -  break; -  -  default: -  Pike_error("Error in user->valid_open, wrong return type.\n"); -  -  case PIKE_T_STRING: -  str=Pike_sp[-1].u.string; -  args++; -  } -  } - #endif -  +     if(!( flags & (FILE_READ | FILE_WRITE)))    Pike_error("Must open file for at least one of read and write.\n");       do {    THREADS_ALLOW_UID();    fd=fd_open(str->str,map(flags), access);    err = errno;    THREADS_DISALLOW_UID();    if ((fd < 0) && (err == EINTR))    check_threads_etc();
pike.git/src/modules/_Stdio/file.c:2775:    ERRNO=errno=err;    }    else    {    init_fd(fd, flags | fd_query_properties(fd, FILE_CAPABILITIES), 0);    set_close_on_exec(fd,1);    }    }    else    { - #ifdef PIKE_SECURITY -  if(!CHECK_SECURITY(SECURITY_BIT_SECURITY)) -  { -  if(!CHECK_SECURITY(SECURITY_BIT_CONDITIONAL_IO)) -  Pike_error("Permission denied.\n"); -  Pike_error("Permission denied.\n"); -  /* FIXME!! Insert better security here */ -  } - #endif +     fd=Pike_sp[-args].u.integer;    if (fd<0)    Pike_error("Not a valid FD.\n");       /* FIXME: What are the intended semantics for the flag FILE_NOT_OPENED?    * (grubba 2004-09-01    */    init_fd(fd, flags | low_fd_query_properties(fd), FILE_NOT_OPENED);    }   
pike.git/src/modules/_Stdio/file.c:2808: Inside #if defined(HAVE_OPENAT)
  #ifdef HAVE_OPENAT   /*! @decl Stdio.File openat(string filename, string mode)    *! @decl Stdio.File openat(string filename, string mode, int access)    *!    *! Open a file relative to an opened directory.    *!    *! @returns    *! Returns a new file object on success, and @expr{0@} (zero) on failure.    *!    *! @note -  *! Not available on all architectures, or in Pike 7.6 and earlier. +  *! Not available on all architectures.    *!    *! @seealso    *! @[open()], @[statat()], @[unlinkat()]    */   static void file_openat(INT32 args)   {    int flags, fd, dir_fd;    int access = 00666;    int err;    struct pike_string *str, *flag_str;       if((dir_fd = FD) < 0)    Pike_error("File not open.\n");    -  get_all_args("Stdio.File->openat", args, "%S%S.%d", -  &str, &flag_str, &access); +  get_all_args(NULL, args, "%S%S.%d", &str, &flag_str, &access);       flags = parse(flag_str->str);       if (string_has_null(str)) {    /* Filenames with NUL are not supported. */    ERRNO = errno = ENOENT;    pop_n_elems(args);    push_int(0);    return;    }    - #ifdef PIKE_SECURITY -  if(!CHECK_SECURITY(SECURITY_BIT_SECURITY)) -  { -  if(!CHECK_SECURITY(SECURITY_BIT_CONDITIONAL_IO)) -  Pike_error("Permission denied.\n"); -  -  if(flags & (FILE_APPEND | FILE_TRUNC | FILE_CREATE | FILE_WRITE)) -  { -  push_text("write"); -  }else{ -  push_text("read"); -  } -  -  ref_push_object(Pike_fp->current_object); -  ref_push_string(str); -  ref_push_string(flag_str); -  push_int(access); -  -  safe_apply(OBJ2CREDS(CURRENT_CREDS)->user,"valid_openat",5); -  switch(TYPEOF(Pike_sp[-1])) -  { -  case PIKE_T_INT: -  switch(Pike_sp[-1].u.integer) -  { -  case 0: /* return 0 */ -  ERRNO=errno=EPERM; -  pop_n_elems(args+1); -  push_int(0); -  return; -  - #if 0 -  case 1: /* return 1 */ -  pop_n_elems(args+1); -  push_int(1); -  return; - #endif -  -  case 2: /* ok */ -  pop_stack(); -  break; -  -  case 3: /* permission denied */ -  Pike_error("Stdio.file->openat: permission denied.\n"); -  -  default: -  Pike_error("Error in user->valid_openat, wrong return value.\n"); -  } -  break; -  -  default: -  Pike_error("Error in user->valid_openat, wrong return type.\n"); -  -  case PIKE_T_STRING: -  /* Alternate path. */ -  str=Pike_sp[-1].u.string; -  args++; -  } -  } - #endif -  +     if(!(flags & (FILE_READ | FILE_WRITE)))    Pike_error("Must open file for at least one of read and write.\n");       do {    THREADS_ALLOW_UID();    fd = openat(dir_fd, str->str, map(flags), access);    err = errno;    THREADS_DISALLOW_UID();    if ((fd < 0) && (err == EINTR))    check_threads_etc();
pike.git/src/modules/_Stdio/file.c:2940: Inside #if !defined(__NT__) && (defined(HAVE_POSIX_OPENPT) || defined(PTY_MASTER_PATHNAME))
   *!    *! @seealso    *! @[grantpt()]    */   static void file_openpt(INT32 args)   {    int flags,fd;   #ifdef HAVE_POSIX_OPENPT    struct pike_string *flag_str;   #endif -  close_fd(); +  close_fd(0);       if(args < 1) -  SIMPLE_TOO_FEW_ARGS_ERROR("Stdio.File->openpt", 2); +  SIMPLE_WRONG_NUM_ARGS_ERROR("openpt", 1);       if(TYPEOF(Pike_sp[-args]) != PIKE_T_STRING) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->openpt", 1, "string"); +  SIMPLE_ARG_TYPE_ERROR("openpt", 1, "string");      #ifdef HAVE_POSIX_OPENPT    flags = parse((flag_str = Pike_sp[-args].u.string)->str);       if(!( flags & (FILE_READ | FILE_WRITE)))    Pike_error("Must open file for at least one of read and write.\n");       do {    THREADS_ALLOW_UID();    fd=posix_openpt(map(flags));
pike.git/src/modules/_Stdio/file.c:2983: Inside #if !defined(__NT__) && (defined(HAVE_POSIX_OPENPT) || defined(PTY_MASTER_PATHNAME)) and #if defined(HAVE_POSIX_OPENPT)
   else    {    init_fd(fd, flags | fd_query_properties(fd, FILE_CAPABILITIES), 0);    set_close_on_exec(fd,1);    }    pop_n_elems(args);    push_int(fd>=0);   #else    if(args > 1)    pop_n_elems(args - 1); -  push_constant_text(PTY_MASTER_PATHNAME); +  push_text(PTY_MASTER_PATHNAME);    stack_swap();    file_open(2);   #endif   }   #endif      #ifdef HAVE_FSYNC   /*! @decl int(0..1) sync()    *!    *! Flush buffers to disk.
pike.git/src/modules/_Stdio/file.c:3029: Inside #if defined(HAVE_FSYNC)
      if (ret < 0) {    ERRNO = errno = e;    push_int(0);    } else {    push_int(1);    }   }   #endif /* HAVE_FSYNC */    - #if defined(INT64) && (defined(HAVE_LSEEK64) || defined(__NT__)) + #if (defined(HAVE_LSEEK64) || defined(__NT__))   #define SEEK64   #endif    - /*! @decl int seek(int pos) -  *! @decl int seek(int unit, int mult) -  *! @decl int seek(int unit, int mult, int add) +  + /*! @decl int seek(int offset) +  *! @decl int seek(int offset, string whence)    *! -  *! Seek to a specified offset in a file. +  *! The seek() function repositions the offset of the open file +  *! associated with the file descriptor fd to the argument @[offset] +  *! according to the directive @[whence] as follows:    *! -  *! If @[mult] or @[add] are specified, @[pos] is calculated as -  *! @expr{@[pos] = @[unit]*@[mult] + @[add]@}. +  *! @string +  *! @value Stdio.SEEK_SET +  *! The offset is set to @[offset] bytes. +  *! @value Stdio.SEEK_CUR +  *! The offset is set to its current location plus @[offset] bytes. +  *! @value Stdio.SEEK_END +  *! The offset is set to the size of the file plus @[offset] bytes. +  *! @endstring    *! -  *! If @[pos] is negative then it is relative to the end of the file, -  *! otherwise it's an absolute offset from the start of the file. +  *! If @[whence] is not specified it is SEEK_SET if @[offset] is +  *! positive, and if @[offset] is negative SEEK_END.    *! -  *! @returns -  *! Returns the new offset, or @expr{-1@} on failure. +  *! The seek() function on most operating systems allows the file +  *! offset to be set beyond the end of the file (but this does not +  *! change the size of the file). If data is later written at this +  *! point, subsequent reads of the data in the gap (a "hole") return +  *! null bytes ('\0') until data is actually written into the gap.    *! -  *! @note -  *! The arguments @[mult] and @[add] are considered obsolete, and -  *! should not be used. +  *! Seeking file data and holes    *! -  +  *! Stdio.SEEK_DATA and Stdio.SEEK_HOLE are nonstandard extensions +  *! present in Linux, Solaris, FreeBSD, and DragonFly BSD; they are +  *! proposed for inclusion in the next POSIX revision. +  *! @string +  *! @value Stdio.SEEK_DATA +  *! Adjust the file offset to the next location in the file greater +  *! than or equal to offset containing data. If offset points to +  *! data, then the file offset is set to offset. +  *! +  *! @value Stdio.SEEK_HOLE +  *! Adjust the file offset to the next hole in the file greater than +  *! or equal to offset. If offset points into the middle of a hole, +  *! then the file offset is set to offset. If there is no hole past +  *! offset, then the file offset is adjusted to the end of the file +  *! (i.e., there is an implicit hole at the end of any file). +  *! @endstring +  *! +  *! In both of the above cases, seek() fails if offset points past the +  *! end of the file. +  *! +  *! These operations allow applications to map holes in a sparsely +  *! allocated file. This can be useful for applications such as file +  *! backup tools, which can save space when creating backups and +  *! preserve holes, if they have a mechanism for discovering holes. +  *! +  *! For the purposes of these operations, a hole is a sequence of +  *! zeros that (normally) has not been allocated in the underlying +  *! file storage. However, a filesystem is not obliged to report +  *! holes, so these operations are not a guaranteed mechanism for +  *! mapping the storage space actually allocated to a file. +  *! (Furthermore, a sequence of zeros that actually has been written +  *! to the underlying storage may or may not be reported as a hole.) +  *! +  *! In the simplest implementation, a filesystem can support the +  *! operations by making SEEK_HOLE always return the offset of the end +  *! of the file, and making SEEK_DATA always return offset (i.e., even +  *! if the location referred to by offset is a hole, it can be +  *! considered to consist of data that is a sequence of zeros). +  *! +  *! @returns +  *! Upon successful completion, seek() returns the resulting offset +  *! location as measured in bytes from the beginning of the file. On +  *! error, the value (off_t) -1 is returned and @[errno] is set to +  *! indicate the error. +  *!    *! @seealso    *! @[tell()]    */ -  +  + /* +  * @decl deprecated variant int seek(int unit, int mult) +  * @decl deprecated variant int seek(int unit, int mult, int add) +  * +  * Seek to a specified offset in a file. +  * +  * If @[mult] or @[add] are specified, @[pos] is calculated as +  * @expr{@[pos] = @[unit]*@[mult] + @[add]@}. +  * +  * If @[pos] is negative then it is relative to the end of the file, +  * otherwise it's an absolute offset from the start of the file. +  * +  * @returns +  * Returns the new offset, or @expr{-1@} on failure. +  * +  * @note +  * The arguments @[mult] and @[add] are considered obsolete, and +  * should not be used. +  * +  * @seealso +  * @[tell()] +  */   static void file_seek(INT32 args)   {   #ifdef SEEK64    INT64 to = 0;   #else    off_t to = 0;   #endif -  +  int how = SEEK_SET;       if( args < 1) -  SIMPLE_TOO_FEW_ARGS_ERROR("Stdio.File->seek", 1); +  SIMPLE_WRONG_NUM_ARGS_ERROR("seek", 1);      #if defined (SEEK64)    if(is_bignum_object_in_svalue(&Pike_sp[-args])) {    if (!int64_from_bignum(&to, Pike_sp[-args].u.object))    Pike_error ("Bad argument 1 to Stdio.File->seek(). Offset too large.\n");    }    else   #endif    if(TYPEOF(Pike_sp[-args]) != PIKE_T_INT) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->seek", 1, "int"); +  SIMPLE_ARG_TYPE_ERROR("seek", 1, "int");    else    to=Pike_sp[-args].u.integer;       if(FD < 0)    Pike_error("File not open.\n");    -  +  +  if( args == 2 && TYPEOF(Pike_sp[-args+1]) == PIKE_T_STRING ) +  { +  how = Pike_sp[-args+1].u.string->str[0]; +  } +  else +  {    if(args>1)    {    if(TYPEOF(Pike_sp[-args+1]) != PIKE_T_INT) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->seek", 2, "int"); +  SIMPLE_ARG_TYPE_ERROR("seek", 2, "int");    to *= Pike_sp[-args+1].u.integer;    }    if(args>2)    {    if(TYPEOF(Pike_sp[-args+2]) != PIKE_T_INT) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->seek", 3, "int"); +  SIMPLE_ARG_TYPE_ERROR("seek", 3, "int");    to += Pike_sp[-args+2].u.integer;    }    -  +  if( to < 0 ) +  how = SEEK_END; +  }    ERRNO=0;      #if defined(HAVE_LSEEK64) && !defined(__NT__) -  to = lseek64(FD,to,to<0 ? SEEK_END : SEEK_SET); +  to = lseek64(FD,to,how);   #else -  to = fd_lseek(FD,to,to<0 ? SEEK_END : SEEK_SET); +  to = fd_lseek(FD,to,how);   #endif    if(to<0) ERRNO=errno;       pop_n_elems(args);    push_int64(to);   }    - #if defined(INT64) && (defined(HAVE_LSEEK64) || defined(__NT__)) + #if (defined(HAVE_LSEEK64) || defined(__NT__))   #define TELL64   #endif      /*! @decl int tell()    *!    *! Returns the current offset in the file.    *!    *! @seealso    *! @[seek()]    */
pike.git/src/modules/_Stdio/file.c:3157:    *! Truncates the file to the specified length @[length].    *!    *! @returns    *! Returns @expr{1@} on success, and @expr{0@} (zero) on failure.    *!    *! @seealso    *! @[open()]    */   static void file_truncate(INT32 args)   { - #if defined(INT64) +     INT64 len = 0; - #else -  off_t len = 0; - #endif +     int res;    -  if(args<1) -  SIMPLE_TOO_FEW_ARGS_ERROR("Stdio.File->truncate", 1); +  if(args!=1) +  SIMPLE_WRONG_NUM_ARGS_ERROR("truncate", 1);    - #if defined (INT64) +    #if defined (HAVE_FTRUNCATE64) || SIZEOF_OFF_T > SIZEOF_INT_TYPE    if(is_bignum_object_in_svalue(&Pike_sp[-args])) {    if (!int64_from_bignum(&len, Pike_sp[-args].u.object))    Pike_error ("Bad argument 1 to Stdio.File->truncate(). Length too large.\n");    }    else   #endif - #endif +     if(TYPEOF(Pike_sp[-args]) != PIKE_T_INT) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->truncate", 1, "int"); +  SIMPLE_ARG_TYPE_ERROR("truncate", 1, "int");    else    len = Pike_sp[-args].u.integer;       if(FD < 0)    Pike_error("File not open.\n");       ERRNO=0; - #ifdef HAVE_FTRUNCATE64 -  res = ftruncate64 (FD, len); - #else +     res=fd_ftruncate(FD, len); - #endif +        pop_n_elems(args);       if(res<0)    ERRNO=errno;       push_int(!res);   }      /*! @decl Stat stat()
pike.git/src/modules/_Stdio/file.c:3211:    *! Get status for an open file.    *!    *! This function returns the same information as the function    *! @[file_stat()], but for the file it is called in. If file is not    *! an open file, @expr{0@} (zero) is returned. Zero is also returned    *! if file is a pipe or socket.    *!    *! @returns    *! See @[file_stat()] for a description of the return value.    *! -  *! @note -  *! Prior to Pike 7.1 this function returned an array(int). -  *! +     *! @seealso    *! @[file_stat()], @[statat()]    */   static void file_stat(INT32 args)   {    int fd;    PIKE_STAT_T s;    int tmp;       if(FD < 0)
pike.git/src/modules/_Stdio/file.c:3264: Inside #if defined(HAVE_FSTATAT)
   *!    *! This function returns the same information as the function    *! @[file_stat()], but relative to the file it is called in. If file is not    *! an open file, @expr{0@} (zero) is returned. Zero is also returned    *! if file is a pipe or socket.    *!    *! @returns    *! See @[file_stat()] for a description of the return value.    *!    *! @note -  *! Not available on all architectures, or in Pike 7.6 and earlier. +  *! Not available on all architectures.    *!    *! @seealso    *! @[file_stat()], @[stat()], @[openat()], @[unlinkat()]    */   static void file_statat(INT32 args)   {    int fd;    PIKE_STAT_T s;    int tmp;    struct pike_string *path;    int nofollow = 0;       if(FD < 0)    Pike_error("File not open.\n");    -  get_all_args("statat", args, "%S.%d", &path, &nofollow); +  get_all_args(NULL, args, "%S.%d", &path, &nofollow);       if (string_has_null(path)) {    /* Filenames with NUL are not supported. */    ERRNO = errno = ENOENT;    pop_n_elems(args);    push_int(0);    return;    }       fd=FD;
pike.git/src/modules/_Stdio/file.c:3332: Inside #if defined(HAVE_FSTATAT) and #if defined(HAVE_UNLINKAT)
   */   static void file_unlinkat(INT32 args)   {    int dir_fd;    PIKE_STAT_T st;    struct pike_string *str;    INT32 i;       destruct_objects_to_destruct();    -  VALID_FILE_IO("rm","write"); -  +     if((dir_fd = FD) < 0)    Pike_error("File not open.\n");    -  get_all_args("unlinkat", args, "%S", &str); +  get_all_args(NULL, args, "%S", &str);       if (string_has_null(str)) {    /* Filenames with NUL are not supported. */    ERRNO = ENOENT;    pop_n_elems(args);    push_int(0);    return;    }       THREADS_ALLOW_UID();
pike.git/src/modules/_Stdio/file.c:3372: Inside #if defined(HAVE_FSTATAT) and #if defined(HAVE_UNLINKAT)
   if (i < 0) {    ERRNO = errno;    push_int(0);    } else {    push_int(1);    }   }   #endif /* HAVE_UNLINKAT */   #endif /* HAVE_FSTATAT */    + #if defined(HAVE_FDOPENDIR) && defined(HAVE_OPENAT) + /*! @decl array(string) get_dir(string|void path) +  *! +  *! Get directory contents relative to an open directory. +  *! +  *! @param path +  *! Path relative to the open directory. Defaults to the +  *! directory itself. +  *! +  *! @returns +  *! Returns an array of filenames. +  *! +  *! @note +  *! Not available on all architectures. +  *! +  *! @seealso +  *! @[predef::get_dir()], @[statat()], @[openat()], @[unlinkat()] +  */ + static void file_get_dir(INT32 args) + { +  int fd; +  int dfd; +  struct pike_string *path = NULL; +  ptrdiff_t name_max = -1; +  DIR *dir = NULL; +  +  if(FD < 0) +  Pike_error("File not open.\n"); +  +  get_all_args(NULL, args, ".%S", &path); +  +  if (path && string_has_null(path)) { +  /* Filenames with NUL are not supported. */ +  ERRNO = errno = ENOENT; +  pop_n_elems(args); +  push_int(0); +  return; +  } +  +  fd = FD; +  dfd = -1; +  +  while(1) { +  THREADS_ALLOW_UID(); +  /* NB: The empty string is also an alias for the current directory. +  * This is a convenience eg when recursing with dirname(). +  */ +  if (!path || !path->len) { +  dfd = dup(fd); +  } else { +  dfd = openat(fd, path->str, O_RDONLY); +  } +  THREADS_DISALLOW_UID(); +  +  if ((dfd == -1) && (errno == EINTR)) { +  check_threads_etc(); +  continue; +  } +  break; +  } +  +  if (dfd == -1) { +  ERRNO = errno; +  pop_n_elems(args); +  push_int(0); +  return; +  } +  + #ifdef HAVE_FPATHCONF +  name_max = fpathconf(dfd, _PC_NAME_MAX); + #endif /* HAVE_FPATHCONF */ +  +  if (!(dir = fdopendir(dfd))) { +  ERRNO = errno; +  close(dfd); +  pop_n_elems(args); +  push_int(0); +  return; +  } +  +  /* NB: The fd dfd has been eaten by fdopendir(3), +  * so we don't need to close it. +  */ +  +  low_get_dir(dir, name_max); + } + #endif /* HAVE_FDOPENDIR && HAVE_OPENAT */ +    #if defined(HAVE_FSETXATTR) && defined(HAVE_FGETXATTR) && defined(HAVE_FLISTXATTR)   /* All A-OK.*/      /*! @decl array(string) listxattr( )    *!    *! Return an array of all extended attributes set on the file    */   static void file_listxattr(INT32 args)   {    char buffer[1024];
pike.git/src/modules/_Stdio/file.c:3456:    *! Return the value of a specified attribute, or 0 if it does not exist    */   static void file_getxattr(INT32 args)   {    char buffer[1024];    char *ptr = buffer;    int mfd = FD, do_free = 0;    ssize_t res;    char *name;    -  get_all_args( "getxattr", args, "%s", &name ); +  get_all_args( NULL, args, "%s", &name );       THREADS_ALLOW();    do {   #ifdef HAVE_DARWIN_XATTR    res = fgetxattr( mfd, name, buffer, sizeof(buffer), 0, 0 ); /* First try, for speed.*/   #else    res = fgetxattr( mfd, name, buffer, sizeof(buffer) ); /* First try, for speed.*/   #endif /* HAVE_DARWIN_XATTR */    } while( res < 0 && errno == EINTR );    THREADS_DISALLOW();
pike.git/src/modules/_Stdio/file.c:3516:         /*! @decl void removexattr( string attr )    *! Remove the specified extended attribute.    */   static void file_removexattr( INT32 args )   {    char *name;    int mfd = FD;    int rv; -  get_all_args( "removexattr", args, "%s", &name ); +  get_all_args( NULL, args, "%s", &name );    THREADS_ALLOW();   #ifdef HAVE_DARWIN_XATTR    while( ((rv=fremovexattr( mfd, name, 0 )) < 0) && (errno == EINTR))    ;   #else    while( ((rv=fremovexattr( mfd, name )) < 0) && (errno == EINTR))    ;   #endif /* HAVE_DARWIN_XATTR */    THREADS_DISALLOW();   
pike.git/src/modules/_Stdio/file.c:3564:    *! @returns    *! 1 if successful, 0 otherwise, setting errno.    */   static void file_setxattr( INT32 args )   {    char *ind;    struct pike_string *val;    int flags;    int rv;    int mfd = FD; -  get_all_args( "setxattr", args, "%s%S%d", &ind, &val, &flags ); +  get_all_args( NULL, args, "%s%S%d", &ind, &val, &flags );    THREADS_ALLOW();   #ifdef HAVE_DARWIN_XATTR    while( ((rv=fsetxattr( mfd, ind, val->str,    (val->len<<val->size_shift), 0, flags )) < 0) &&    (errno == EINTR))    ;   #else    while( ((rv=fsetxattr( mfd, ind, val->str,    (val->len<<val->size_shift), flags )) < 0) &&    (errno == EINTR))
pike.git/src/modules/_Stdio/file.c:3599:   /*! @decl int errno()    *!    *! Return the errno for the latest failed file operation.    */   static void file_errno(INT32 args)   {    pop_n_elems(args);    push_int(ERRNO);   }    - /*! @decl int mode() + /*! @decl FileModeFlags|FilePropertyFlags mode()    *!    *! Returns the open mode and capabilities for the file.    *!    *! @returns    *! Returns an @[`|()] of the following flags:    *! @int    *! @value 0x1000    *! @[FILE_READ]    *! @value 0x2000    *! @[FILE_WRITE]
pike.git/src/modules/_Stdio/file.c:3635:    *! @[PROP_BUFFERED]    *! @value 0x0004    *! @[PROP_SHUTDOWN]    *! @value 0x0002    *! @[PROP_NONBLOCK]    *! @value 0x0001    *! @[PROP_IPC]    *! @endint    *!    *! @note -  *! In some versions of Pike 7.7 and 7.8 the @tt{PROP_@} flags were +  *! In some versions of Pike 7.8 the @tt{PROP_@} flags were    *! filtered from the result.    *!    *! @seealso    *! @[open()]    */   static void file_mode(INT32 args)   {    pop_n_elems(args);    push_int(THIS->open_mode);   }
pike.git/src/modules/_Stdio/file.c:3667:    *! Also, this object does not keep a reference to the backend.    *!    *! @seealso    *! @[query_backend], @[set_nonblocking], @[set_read_callback], @[set_write_callback], @[set_fs_event_callback]    */   static void file_set_backend (INT32 args)   {    struct my_file *f = THIS;    struct Backend_struct *backend;    -  if (!args) -  SIMPLE_TOO_FEW_ARGS_ERROR ("Stdio.File->set_backend", 1); +  if (args!=1) +  SIMPLE_WRONG_NUM_ARGS_ERROR ("set_backend", 1);    if (TYPEOF(Pike_sp[-args]) != PIKE_T_OBJECT) -  SIMPLE_BAD_ARG_ERROR ("Stdio.File->set_backend", 1, "object(Pike.Backend)"); -  backend = (struct Backend_struct *) -  get_storage (Pike_sp[-args].u.object, Backend_program); +  SIMPLE_ARG_TYPE_ERROR ("set_backend", 1, "Pike.Backend"); +  backend = get_storage (Pike_sp[-args].u.object, Backend_program);    if (!backend) -  SIMPLE_BAD_ARG_ERROR ("Stdio.File->set_backend", 1, "object(Pike.Backend)"); +  SIMPLE_ARG_TYPE_ERROR ("set_backend", 1, "Pike.Backend");       /* FIXME: Only allow set_backend() if the file is open? */      #ifdef __NT__    if ((THIS->box.fd >= 0) &&    !(fd_query_properties(THIS->box.fd, fd_CAN_NONBLOCK) & fd_CAN_NONBLOCK)) {    Pike_error("set_backend() on non-socket!\n");    }   #endif /* __NT__ */       if (f->box.backend)    change_backend_for_box (&f->box, backend);    else    INIT_FD_CALLBACK_BOX (&f->box, backend, f->box.ref_obj,    f->box.fd, 0, got_fd_event, f->box.flags); -  -  pop_n_elems (args - 1); +    }      /*! @decl Pike.Backend query_backend()    *!    *! Return the backend used for the callbacks.    *!    *! @seealso    *! @[set_backend]    */   static void file_query_backend (INT32 args)
pike.git/src/modules/_Stdio/file.c:3721:    *!    *! @note    *! Nonblocking operation is not supported on all Stdio.File objects.    *! Notably it is not guaranteed to be supported on objects returned    *! by @[pipe()] unless @[PROP_NONBLOCK] was specified in the call    *! to @[pipe()].    *!    *! @seealso    *! @[set_blocking()]    */ - static void file_set_nonblocking(INT32 args) + static void file_set_nonblocking(INT32 UNUSED(args))   {    if(FD < 0) Pike_error("File not open.\n");       if(!(THIS->open_mode & fd_CAN_NONBLOCK))    Pike_error("This file does not support nonblocking operation.\n");       if(set_nonblocking(FD,1))    {    ERRNO=errno;    push_int (ERRNO);    f_strerror (1);    Pike_error("Stdio.File->set_nonblocking() failed: %S\n",    Pike_sp[-1].u.string);    }       THIS->open_mode |= FILE_NONBLOCKING; -  -  pop_n_elems(args); +    }      /*! @decl void set_blocking()    *!    *! Sets this file to blocking operation.    *!    *! This is the inverse operation of @[set_nonblocking()].    *!    *! @seealso    *! @[set_nonblocking()]    */ - static void file_set_blocking(INT32 args) + static void file_set_blocking(INT32 UNUSED(args))   {    if(FD >= 0)    {    set_nonblocking(FD,0);    THIS->open_mode &=~ FILE_NONBLOCKING;    } -  pop_n_elems(args); +    }      /*! @decl void set_close_on_exec(int(0..1) yes_no)    *!    *! Marks the file as to be closed in spawned processes.    *!    *! This function determines whether this file will be closed when    *! calling exec().    *!    *! Default is that the file WILL be closed on exec except for    *! stdin, stdout and stderr.    *!    *! @seealso    *! @[Process.create_process()], @[exec()]    */   static void file_set_close_on_exec(INT32 args)   { -  if(args < 1) -  SIMPLE_TOO_FEW_ARGS_ERROR("Stdio.File->set_close_on_exec", 1); +  if(args != 1) +  SIMPLE_WRONG_NUM_ARGS_ERROR("set_close_on_exec", 1);    if(FD <0)    Pike_error("File not open.\n");       if(UNSAFE_IS_ZERO(Pike_sp-args))    {    my_set_close_on_exec(FD,0);    }else{    my_set_close_on_exec(FD,1);    } -  pop_n_elems(args); +    }      /*! @decl int is_open()    *!    *! Returns true if the file is open.    *!    *! @note    *! If the file is a socket that has been closed from the remote side,    *! this function might still return true.    *!
pike.git/src/modules/_Stdio/file.c:3853:    *! Rehooks the given file descriptor number to be associated with    *! this object. As opposed to using @[open] with a file descriptor    *! number, it will be closed by this object upon destruct or when    *! @[close] is called.    *!    *! @seealso    *! @[release_fd()]    */   static void file_take_fd(INT32 args)   { -  if (args < 1) -  SIMPLE_TOO_FEW_ARGS_ERROR ("Stdio.File->take_fd", 1); +  if (args != 1) +  SIMPLE_WRONG_NUM_ARGS_ERROR ("take_fd", 1);    if (TYPEOF(Pike_sp[-args]) != PIKE_T_INT) -  SIMPLE_BAD_ARG_ERROR ("Stdio.File->take_fd", 0, "int"); +  SIMPLE_ARG_TYPE_ERROR ("take_fd", 0, "int");    change_fd_for_box(&THIS->box, Pike_sp[-args].u.integer); -  pop_n_elems(args); +    }      PMOD_EXPORT struct object *file_make_object_from_fd(int fd, int mode, int guess)   {    struct object *o;    struct my_file *f;    if (Pike_fp->context->prog == file_program) {    /* Called from within the file (aka Fd) object.    * Attempt to clone ourselves.    */    ONERROR err;    SET_ONERROR(err, do_close_fd, (ptrdiff_t) fd);    o = clone_object_from_object(Pike_fp->current_object, 0);    UNSET_ONERROR(err);    if (!o->prog) return NULL; /* Destructed in create() or __INIT(). */    f = (struct my_file *)(o->storage + Pike_fp->context->storage_offset);    f->flags |= (THIS->flags & FILE_HAVE_RECV_FD);    } else {    /* Clone a plain Fd object. */ -  o = low_clone(file_program); +  o = fast_clone_object(file_program);    f = (struct my_file *) o->storage + file_program->inherits->storage_offset; -  call_c_initializers(o); +     }    change_fd_for_box(&f->box, fd);    if (fd >= 0) {    f->open_mode=mode | fd_query_properties(fd, guess);   #ifdef PIKE_DEBUG    debug_check_fd_not_in_use (fd);   #endif    } else {    f->open_mode = 0;    }
pike.git/src/modules/_Stdio/file.c:3960:    *! @seealso    *! @[open_socket()], @[accept()]    */   static void file_set_buffer(INT32 args)   {    INT32 bufsize;    int flags;       if(FD==-1)    Pike_error("Stdio.File->set_buffer() on closed file.\n"); -  if(!args) -  SIMPLE_TOO_FEW_ARGS_ERROR("Stdio.File->set_buffer", 1); +  if(args<1) +  SIMPLE_WRONG_NUM_ARGS_ERROR("set_buffer", 1); +  if(args>2) +  SIMPLE_WRONG_NUM_ARGS_ERROR("set_buffer", 2);    if(TYPEOF(Pike_sp[-args]) != PIKE_T_INT) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->set_buffer", 1, "int"); +  SIMPLE_ARG_TYPE_ERROR("set_buffer", 1, "int");       bufsize=Pike_sp[-args].u.integer;    if(bufsize < 0)    Pike_error("Bufsize must be larger than zero.\n");       if(args>1)    {    if(TYPEOF(Pike_sp[1-args]) != PIKE_T_STRING) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->set_buffer", 2, "string"); +  SIMPLE_ARG_TYPE_ERROR("set_buffer", 2, "string");    flags=parse(Pike_sp[1-args].u.string->str);    }else{    flags=FILE_READ | FILE_WRITE;    } -  pop_n_elems(args); +       #ifdef SOCKET_BUFFER_MAX   #if SOCKET_BUFFER_MAX    if(bufsize>SOCKET_BUFFER_MAX) bufsize=SOCKET_BUFFER_MAX;   #endif    flags &= THIS->open_mode;   #ifdef SO_RCVBUF    if(flags & FILE_READ)    {    int tmp=bufsize;
pike.git/src/modules/_Stdio/file.c:4049:   int my_socketpair(int family, int type, int protocol, int sv[2])   {    static struct sockaddr_in my_addr;    struct sockaddr_in addr,addr2;    int retries=0;    /* Solaris and AIX think this variable should be a size_t, everybody else    * thinks it should be an int.    */    ACCEPT_SIZE_T len;    -  MEMSET((char *)&addr,0,sizeof(struct sockaddr_in)); +  memset(&addr,0,sizeof(struct sockaddr_in));       /* We lie, we actually create an AF_INET socket... */    if(family != AF_UNIX || type != SOCK_STREAM)    {    errno=EINVAL;    return -1;    }       sv[0] = -1;   
pike.git/src/modules/_Stdio/file.c:4081:    SP_DEBUG((stderr, "my_socketpair:fd_socket() failed, errno:%d\n",    errno));    return -1;    }       /* I wonder what is most common a loopback on ip# 127.0.0.1 or    * a loopback with the name "localhost"?    * Let's hope those few people who don't have socketpair have    * a loopback on 127.0.0.1    */ -  MEMSET((char *)&my_addr,0,sizeof(struct sockaddr_in)); +  memset(&my_addr,0,sizeof(struct sockaddr_in));    my_addr.sin_family=AF_INET;    my_addr.sin_addr.s_addr=htonl(INADDR_ANY);    my_addr.sin_port=htons(0);          /* Bind our sockets on any port */    if(fd_bind(socketpair_fd, (struct sockaddr *)&my_addr, sizeof(addr)) < 0)    {    SP_DEBUG((stderr, "my_socketpair:fd_bind() failed, errno:%d\n",    errno));
pike.git/src/modules/_Stdio/file.c:4283:    *! @decl Stdio.File pipe(int flags)    */   static void file_pipe(INT32 args)   {    int inout[2] = { -1, -1 };    int i = 0;       int type=fd_CAN_NONBLOCK | fd_BIDIRECTIONAL;    int reverse;    -  check_all_args("file->pipe",args, BIT_INT | BIT_VOID, 0); +  check_all_args(NULL, args, BIT_INT | BIT_VOID, 0);    if(args && !SUBTYPEOF(Pike_sp[-1])) type = Pike_sp[-args].u.integer;       reverse = type & fd_REVERSE;    type &= ~fd_REVERSE;    -  close_fd(); +  close_fd(0);    pop_n_elems(args);    ERRNO=0;       do    {   #ifdef PIPE_CAPABILITIES    if(!(type & ~(PIPE_CAPABILITIES)))    {    i=fd_pipe(&inout[0]);    if (i >= 0) {
pike.git/src/modules/_Stdio/file.c:4314:   #endif      #ifdef UNIX_SOCKETS_WORKS_WITH_SHUTDOWN   #undef UNIX_SOCKET_CAPABILITIES   #define UNIX_SOCKET_CAPABILITIES (fd_INTERPROCESSABLE | fd_BIDIRECTIONAL | fd_CAN_NONBLOCK | fd_CAN_SHUTDOWN | fd_SEND_FD)   #endif      #if defined(HAVE_SOCKETPAIR)    if(!(type & ~(UNIX_SOCKET_CAPABILITIES)))    { -  i=fd_socketpair(AF_UNIX, SOCK_STREAM, 0, &inout[0]); +  i=fd_socketpair(AF_UNIX, SOCK_STREAM, 0, inout);    if (i >= 0) {    type=UNIX_SOCKET_CAPABILITIES;    break;    }    }   #endif      #ifndef UNIX_SOCKETS_WORKS_WITH_SHUTDOWN    if(!(type & ~(SOCKET_CAPABILITIES)))    { -  i=socketpair_ultra(AF_UNIX, SOCK_STREAM, 0, &inout[0]); +  i=socketpair_ultra(AF_UNIX, SOCK_STREAM, 0, inout);    if (i >= 0) {    type=SOCKET_CAPABILITIES;    break;    }    }   #endif       if (!i) {    Pike_error("Cannot create a pipe matching those parameters.\n");    }
pike.git/src/modules/_Stdio/file.c:4402:    IDENTIFIER_PIKE_FUNCTION) && (i->func.offset != -1)) {    /* receive_fd() is not a prototype. */    f->flags |= FILE_HAVE_RECV_FD;    }    break;       case PROG_EVENT_EXIT:    if(!(f->flags & (FILE_NO_CLOSE_ON_DESTRUCT |    FILE_LOCK_FD |    FILE_NOT_OPENED))) -  close_fd_quietly(); +  close_fd(1);    else    free_fd_stuff();   #ifdef HAVE_PIKE_SEND_FD    if (f->fd_info) {    free(f->fd_info);    f->fd_info = NULL;    }   #endif    unhook_fd_callback_box (&f->box);    break;
pike.git/src/modules/_Stdio/file.c:4481:    *! Duplicate a file over another.    *!    *! This function works similarly to @[assign()], but instead of making    *! the argument a reference to the same file, it creates a new file    *! with the same properties and places it in the argument.    *!    *! @returns    *! Returns @expr{1@} on success and @expr{0@} (zero) on failure.    *!    *! @note -  *! In Pike 7.7 and later @[to] need not be open, in which +  *! @[to] need not be open, in which    *! case a new fd is allocated.    *!    *! @note    *! Note also that @[to] is also assigned to the same backend (if any)    *! as this object.    *!    *! @seealso    *! @[assign()], @[dup()]    */   static void file_dup2(INT32 args)   {    struct object *o;    struct my_file *fd;    -  if(args < 1) -  SIMPLE_TOO_FEW_ARGS_ERROR("Stdio.File->dup2", 1); +  if(args != 1) +  SIMPLE_WRONG_NUM_ARGS_ERROR("dup2", 1);       if(FD < 0)    Pike_error("File not open.\n");       if(TYPEOF(Pike_sp[-args]) != PIKE_T_OBJECT) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->dup2", 1, "Stdio.File"); +  SIMPLE_ARG_TYPE_ERROR("dup2", 1, "Stdio.File");       o=Pike_sp[-args].u.object;       fd=get_file_storage(o);       if(!fd) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->dup2", 1, "Stdio.File"); +  SIMPLE_ARG_TYPE_ERROR("dup2", 1, "Stdio.File");       if(fd->box.fd < 0) {    int new_fd;    if((new_fd = fd_dup(FD)) < 0)    {    ERRNO = errno;    pop_n_elems(args);    push_int(0);    return;    }
pike.git/src/modules/_Stdio/file.c:4585:   }      /*! @decl int(0..1) open_socket(int|void port, string|void addr, @    *! int|string|void family_hint)    */   static void file_open_socket(INT32 args)   {    int fd;    int family=-1;    -  close_fd(); +  close_fd(0);       if (args > 2 && TYPEOF(Pike_sp[2-args]) == PIKE_T_INT &&    Pike_sp[2-args].u.integer != 0)    family = Pike_sp[2-args].u.integer;    else if (args > 2 && TYPEOF(Pike_sp[2-args]) == PIKE_T_STRING &&    !Pike_sp[2-args].u.string->size_shift) {    PIKE_SOCKADDR addr; -  int addr_len; -  addr_len = get_inet_addr(&addr, (char *) STR0(Pike_sp[2-args].u.string), +  get_inet_addr(&addr, (char *) STR0(Pike_sp[2-args].u.string),    NULL, -1, 0);    family = SOCKADDR_FAMILY(addr);    INVALIDATE_CURRENT_TIME();    }       if (args && TYPEOF(Pike_sp[-args]) == PIKE_T_INT &&    Pike_sp[-args].u.integer < 0) {    pop_n_elems(args);    args = 0;    }
pike.git/src/modules/_Stdio/file.c:4617:       if (args) {    PIKE_SOCKADDR addr;    int addr_len;    char *name;    int o;       if (TYPEOF(Pike_sp[-args]) != PIKE_T_INT &&    (TYPEOF(Pike_sp[-args]) != PIKE_T_STRING ||    Pike_sp[-args].u.string->size_shift)) { -  SIMPLE_BAD_ARG_ERROR("Stdio.File->open_socket", 1, "int|string (8bit)"); +  SIMPLE_ARG_TYPE_ERROR("open_socket", 1, "int|string(8bit)");    }    if (args > 1 && !UNSAFE_IS_ZERO(&Pike_sp[1-args])) {    if (TYPEOF(Pike_sp[1-args]) != PIKE_T_STRING) { -  SIMPLE_BAD_ARG_ERROR("Stdio.File->open_socket", 2, "string"); +  SIMPLE_ARG_TYPE_ERROR("open_socket", 2, "string");    }       name = Pike_sp[1-args].u.string->str;    } else {    name = NULL;    }    addr_len = get_inet_addr(&addr, name,    (TYPEOF(Pike_sp[-args]) == PIKE_T_STRING?    Pike_sp[-args].u.string->str : NULL),    (TYPEOF(Pike_sp[-args]) == PIKE_T_INT?
pike.git/src/modules/_Stdio/file.c:4669: Inside #if defined(IPV6_V6ONLY) && defined(IPPROTO_IPV6)
   */    o = 0;    fd_setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&o, sizeof(int));    }   #endif      #ifdef SO_REUSEPORT    /* FreeBSD 7.x wants this to reuse portnumbers.    * Linux 2.6.x seems to have reserved a slot for the option, but not    * enabled it. Survive libc's with the option on kernels without. +  * +  * The emulated Linux runtime on MS Windows 10 fails this with EINVAL.    */    o=1;    if((fd_setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *)&o, sizeof(int)) < 0)   #ifdef ENOPROTOOPT    && (errno != ENOPROTOOPT)   #endif -  + #ifdef EINVAL +  && (errno != EINVAL) + #endif   #ifdef WSAENOPROTOOPT    && (errno != WSAENOPROTOOPT)   #endif   ){    ERRNO=errno;    while (fd_close(fd) && errno == EINTR) {}    errno = ERRNO;    pop_n_elems(args);    push_int(0);    return;
pike.git/src/modules/_Stdio/file.c:4730: Inside #if defined(IPV6_V6ONLY) && defined(IPPROTO_IPV6)
   */    o = 0;    fd_setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&o, sizeof(int));    }   #endif      #ifdef SO_REUSEPORT    /* FreeBSD 7.x wants this to reuse portnumbers.    * Linux 2.6.x seems to have reserved a slot for the option, but not    * enabled it. Survive libc's with the option on kernels without. +  * +  * The emulated Linux runtime on MS Windows 10 fails this with EINVAL.    */    o=1;    if((fd_setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *)&o, sizeof(int)) < 0)   #ifdef ENOPROTOOPT    && (errno != ENOPROTOOPT)   #endif -  + #ifdef EINVAL +  && (errno != EINVAL) + #endif   #ifdef WSAENOPROTOOPT    && (errno != WSAENOPROTOOPT)   #endif   ){    ERRNO=errno;    while (fd_close(fd) && errno == EINTR) {}    errno = ERRNO;    pop_n_elems(args);    push_int(0);    return;
pike.git/src/modules/_Stdio/file.c:4761:    fd_query_properties(fd, SOCKET_CAPABILITIES), 0);    my_set_close_on_exec(fd,1);    change_fd_for_box (&THIS->box, FD);    ERRNO=0;       pop_n_elems(args);    push_int(1);   }      /*! @decl int(0..1) set_keepalive(int(0..1) on_off) +  *! +  *! Equivalent to setsockopt(Stdio.SO_KEEPALIVE, on_off), but will set errno +  *! if SO_KEEPALIVE is not supported, rather than issuing a compilation error +  *! for the missing constant.    */   static void file_set_keepalive(INT32 args)   {    int tmp, i;    INT_TYPE t;    -  get_all_args("Stdio.File->set_keepalive", args, "%i", &t); +  get_all_args(NULL, args, "%i", &t);       /* In case int and INT_TYPE have different sizes */    tmp = t;      #ifdef SO_KEEPALIVE    i = fd_setsockopt(FD,SOL_SOCKET, SO_KEEPALIVE, (char *)&tmp, sizeof(tmp));    if(i)    {    ERRNO=errno;    }else{
pike.git/src/modules/_Stdio/file.c:4795:    ERRNO = errno = ENOTTY;   #else /* !ENOTTY */    ERRNO = errno = EIO;   #endif /* ENOTTY */   #endif /* ENOTSUP */   #endif /* SO_KEEPALIVE */    pop_n_elems(args);    push_int(!i);   }    + /*! @decl int(0..1) setsockopt(int level,int opt,int state) +  *! +  *! Set socket options like Stdio.SO_KEEPALIVE. This function is always +  *! available; the presence or absence of the option constants indicates +  *! availability of those features. +  *! +  *! @returns +  *! 1 if successful, 0 if not (and sets errno()) +  *! +  *! @seealso +  *! @[set_keepalive()] +  */ + static void file_setsockopt(INT32 args) + { +  int tmp, i, opt, level; +  INT_TYPE o, t, l; +  +  get_all_args(NULL, args, "%i%i%i", &l, &o, &t); +  +  /* In case int and INT_TYPE have different sizes */ +  tmp = t; opt = o; level = l; +  +  i = fd_setsockopt(FD, level, opt, (char *)&tmp, sizeof(tmp)); +  if(i) +  { +  ERRNO=errno; +  }else{ +  ERRNO=0; +  } +  pop_n_elems(args); +  push_int(!i); + } +    #ifdef HAVE_SYS_UN_H   #include <sys/un.h>      #ifndef PATH_MAX   #ifdef _POSIX_PATH_MAX   #define PATH_MAX _POSIX_PATH_MAX   #else /* !_POSIX_PATH_MAX */   #define PATH_MAX 255 /* Minimum according to POSIX. */   #endif /* _POSIX_PATH_MAX */   #endif /* !PATH_MAX */
pike.git/src/modules/_Stdio/file.c:4834:    *! @note    *! @[path] had a quite restrictive length limit (~100 characters)    *! prior to Pike 7.8.334.    */   static void file_connect_unix( INT32 args )   {    struct sockaddr_un *name;    int addr_len;    int tmp;    -  if( args < 1 ) -  SIMPLE_TOO_FEW_ARGS_ERROR("Stdio.File->connect_unix", 1); +  if( args != 1 ) +  SIMPLE_WRONG_NUM_ARGS_ERROR("connect_unix", 1);    if( (TYPEOF(Pike_sp[-args]) != PIKE_T_STRING) ||    (Pike_sp[-args].u.string->size_shift) )    Pike_error("Illegal argument. Expected string(8bit)\n");       /* NOTE: Some operating systems (eg Linux 2.6) do not support    * paths longer than what fits into a plain struct sockaddr_un.    */    addr_len = sizeof(struct sockaddr_un) + Pike_sp[-args].u.string->len + 1 -    sizeof(name->sun_path);    name = xalloc(addr_len);       name->sun_family=AF_UNIX;    strcpy( name->sun_path, Pike_sp[-args].u.string->str );   #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN    /* Length including NUL. */    name->sun_len = Pike_sp[-args].u.string->len + 1;   #endif    pop_n_elems(args);    -  close_fd(); +  close_fd(0);    change_fd_for_box (&THIS->box, socket(AF_UNIX,SOCK_STREAM,0));       if( FD < 0 )    {    free(name);    ERRNO = errno;    push_int(0);    return;    }   
pike.git/src/modules/_Stdio/file.c:4886:    push_int(0);    } else {    push_int(1);    }   }   #endif /* HAVE_SYS_UN_H */      /*! @decl int(0..1) connect(string dest_addr, int dest_port)    *! @decl int(0..1) connect(string dest_addr, int dest_port, @    *! string src_addr, int src_port) +  *! @decl string(0..255)|int(0..0) connect(string dest_addr, int dest_port, @ +  *! string|int(0..0) src_addr, int|int(0..0) src_port, @ +  *! string(0..255) data)    *!    *! Open a TCP/IP connection to the specified destination.    *!    *! In nonblocking mode, success is indicated with the write-callback,    *! and failure with the close-callback or the read_oob-callback.    *! -  +  *! If the @[data] argument is included the socket will use +  *! TCP_FAST_OPEN if available, if not the data will @i{not be +  *! sent@}. In the data case the function either returns the data +  *! that has not been sent (only one packet can be sent with this +  *! option) or 0 if the connection failed immediately. +  *!    *! @returns -  *! Returns @expr{1@} on success, and @expr{0@} on failure. +  *! Returns @expr{1@} or the remaining @expr{data@} on success, and +  *! @expr{0@} on failure.    *!    *! @note    *! In nonblocking mode @expr{0@} (zero) may be returned and @[errno()] set    *! to @tt{EWOULDBLOCK@} or @tt{WSAEWOULDBLOCK@}. This should not be regarded    *! as a connection failure. -  +  *!    */   static void file_connect(INT32 args)   {    PIKE_SOCKADDR addr;    int addr_len;    struct pike_string *dest_addr = NULL;    struct pike_string *src_addr = NULL; -  +  struct pike_string *data = NULL;    struct svalue *dest_port = NULL;    struct svalue *src_port = NULL;       int tmp, was_closed = FD < 0; -  int tries; +  int fd, sent = 0; +  int nb_mode; +  int old_events; +  int e;    -  if (args < 4) { -  get_all_args("Stdio.File->connect", args, "%S%*", &dest_addr, &dest_port); +  if (args < 4) +  { +  get_all_args(NULL, args, "%S%*", &dest_addr, &dest_port); +  } +  else if( args == 5 ) +  { +  struct svalue *src_sv; +  get_all_args(NULL, args, "%S%*%*%*%S", +  &dest_addr, &dest_port, &src_sv, &src_port, &data); +  if(TYPEOF(*src_sv) != PIKE_T_INT ) +  { +  if (TYPEOF(*src_sv) != PIKE_T_STRING || src_sv->u.string->size_shift) +  SIMPLE_ARG_TYPE_ERROR("connect", 3, "int|string(8bit)"); +  src_addr = src_sv->u.string; +  }    } else { -  get_all_args("Stdio.File->connect", args, "%S%*%S%*", +  get_all_args(NULL, args, "%S%*%S%*",    &dest_addr, &dest_port, &src_addr, &src_port);    }       if(TYPEOF(*dest_port) != PIKE_T_INT &&    (TYPEOF(*dest_port) != PIKE_T_STRING || dest_port->u.string->size_shift)) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->connect", 2, "int|string (8bit)"); +  SIMPLE_ARG_TYPE_ERROR("connect", 2, "int|string(8bit)");       if(src_port && TYPEOF(*src_port) != PIKE_T_INT &&    (TYPEOF(*src_port) != PIKE_T_STRING || src_port->u.string->size_shift)) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->connect", 4, "int|string (8bit)"); +  SIMPLE_ARG_TYPE_ERROR("connect", 4, "int|string(8bit)");      /* fprintf(stderr, "connect: family: %d\n", SOCKADDR_FAMILY(addr)); */       addr_len = get_inet_addr(&addr, dest_addr->str,    (TYPEOF(*dest_port) == PIKE_T_STRING?    dest_port->u.string->str : NULL),    (TYPEOF(*dest_port) == PIKE_T_INT?    dest_port->u.integer : -1), 0);    INVALIDATE_CURRENT_TIME();       if(was_closed)    { -  if (args < 4) { +  if (!src_addr) {    push_int(-1);    push_int(0);    push_int(SOCKADDR_FAMILY(addr));    file_open_socket(3);    } else {    push_svalue(src_port);    ref_push_string(src_addr);    file_open_socket(2);    }    if(UNSAFE_IS_ZERO(Pike_sp-1) || FD < 0)    Pike_error("Stdio.File->connect(): Failed to open socket.\n");    pop_stack();    }    -  for(tries = 0;; tries++) -  { -  tmp=FD; +  nb_mode = !!(THIS->open_mode & FILE_NONBLOCKING); +  +  /* Inhibit the backend for this fd while connect(2) is running. */ +  if ((old_events = THIS->box.events)) { +  set_fd_callback_events(&(THIS->box), 0, THIS->box.flags); +  } +  +  fd = FD;    THREADS_ALLOW(); -  tmp=fd_connect(tmp, (struct sockaddr *)&addr, addr_len); -  THREADS_DISALLOW(); -  if(tmp<0) -  switch(errno) +  for(;;)    { - #if 0 -  /* Even though this code is robust(er) now, -  * it has no business here and should be dealt -  * with at the Pike programming level. -  */ - #ifdef EADDRINUSE -  case EADDRINUSE: - #endif - #ifdef WSAEADDRINUSE -  case WSAEADDRINUSE: - #endif -  if(tries > INUSE_TIMEOUT/INUSE_BUSYWAIT_DELAY) + #ifdef MSG_FASTOPEN +  if( data )    { -  /* errno = EAGAIN; */ /* no ports available */ -  break; +  tmp = sendto(fd, data->str, data->len, MSG_FASTOPEN, +  (struct sockaddr *)&addr, addr_len );    } -  sysleep(INUSE_BUSYWAIT_DELAY); -  /* FALL_THROUGH */ +  else   #endif -  case EINTR: -  continue; +  { +  tmp=fd_connect(fd, (struct sockaddr *)&addr, addr_len);    } -  +  if( tmp<0 && (errno==EINTR)) +  continue;    break;    } -  +  THREADS_DISALLOW();    -  +  e = errno; +  +  if (old_events) { +  /* Reenable the backend. */ +  set_fd_callback_events(&(THIS->box), old_events, THIS->box.flags); +  } +  +  errno = e; +  +  /* NB: On success in threaded callback-mode, some other thread may +  * have messed with us before THREADS_DISALLOW() has finished. +  * +  * We thus mustn't look at the current settings of ourselves, as +  * they may have been changed since before the fd_connect() call. +  */ +     if(tmp < 0   #ifdef EINPROGRESS -  && !(errno==EINPROGRESS && (THIS->open_mode & FILE_NONBLOCKING)) +  && !(errno == EINPROGRESS && nb_mode)   #endif   #ifdef WSAEWOULDBLOCK -  && !(errno==WSAEWOULDBLOCK && (THIS->open_mode & FILE_NONBLOCKING)) +  && !(errno == WSAEWOULDBLOCK && nb_mode)   #endif   #ifdef EWOULDBLOCK -  && !(errno==EWOULDBLOCK && (THIS->open_mode & FILE_NONBLOCKING)) +  && !(errno == EWOULDBLOCK && nb_mode)   #endif    )    {    /* something went wrong */    ERRNO=errno;    if (was_closed) {    while (fd_close (FD) && errno == EINTR) {}    change_fd_for_box (&THIS->box, -1);    errno = ERRNO;    }    pop_n_elems(args);    push_int(0);    }else{ -  +     ERRNO=0; -  +  if( data ) +  { +  push_string( make_shared_binary_string( data->str + tmp, data->len-tmp ) ); +  stack_pop_n_elems_keep_top( args ); +  } +  else +  {    pop_n_elems(args);    push_int(1);    }    } -  + }      /*! @decl string query_address()    *! @decl string query_address(int(0..1) local)    *!    *! Get address and port of a socket end-point.    *!    *! @param local    *! If the argument @[local] is not specified, or is @expr{0@}    *! (zero), the remote end-point is returned. Otherwise, if @[local]    *! is @expr{1@}, the local end-point is returned.
pike.git/src/modules/_Stdio/file.c:5109:    * when getting a mapped address (::FFFF:a.b.c.d).    */    if ((!strncmp(buffer, "::FFFF:", 7) || !strncmp(buffer, "::ffff:", 7)) &&    !strchr(buffer + 7, ':')) {    push_text(buffer+7);    } else {    push_text(buffer);    }   }    - /*! @decl Stdio.File `<<(string data) -  *! @decl Stdio.File `<<(mixed data) -  *! -  *! Write some data to a file. -  *! -  *! If @[data] is not a string, it is casted to string, and then -  *! written to the file. -  *! -  *! @note -  *! Throws an error if not all data could be written. -  *! -  *! @seealso -  *! @[write()] -  */ - static void file_lsh(INT32 args) - { -  ptrdiff_t len; -  if(args < 1) -  SIMPLE_TOO_FEW_ARGS_ERROR("Stdio.File->`<<", 1); -  if(args > 1) -  pop_n_elems(args-1); -  -  if(TYPEOF(Pike_sp[-1]) != PIKE_T_STRING) -  { -  ref_push_type_value(string_type_string); -  stack_swap(); -  f_cast(); -  } -  -  len=Pike_sp[-1].u.string->len; -  file_write(1); -  if(len != Pike_sp[-1].u.integer) Pike_error("Stdio.File << failed.\n"); -  pop_stack(); -  -  push_object(this_object()); - } -  +    /*! @decl void create(string filename)    *! @decl void create(string filename, string mode)    *! @decl void create(string filename, string mode, int access)    *! @decl void create(int fd)    *! @decl void create(int fd, string mode)    *!    *! See @[open()].    *!    *! @seealso    *! @[open()]    */   static void file_create(INT32 args)   {    if(!args) return;    if(TYPEOF(Pike_sp[-args]) != PIKE_T_STRING &&    TYPEOF(Pike_sp[-args]) != PIKE_T_INT) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->create", 1, "int|string"); +  SIMPLE_ARG_TYPE_ERROR("create", 1, "int|string");    -  close_fd(); +  close_fd(0);    file_open(args); -  +  pop_stack();   }      #ifdef _REENTRANT      struct new_thread_data   {    INT32 from, to;    char buffer[READ_BUFFER];   };   
pike.git/src/modules/_Stdio/file.c:5217: Inside #if defined(_REENTRANT)
   }       close_and_exit:   /* fprintf(stderr,"Closing %d and %d\n",p->to,p->from); */       while (fd_close(p->to) && errno == EINTR) {}    while (fd_close(p->from) && errno == EINTR) {}    low_mt_lock_interpreter(); /* Can run even if threads_disabled. */    num_threads--;    mt_unlock_interpreter(); -  free((char *)p); +  free(p);    th_exit(0);    return 0;   }      /*! @decl void proxy(Stdio.File from)    *!    *! Starts a thread that asynchronously copies data from @[from]    *! to this file.    *!    *! @seealso    *! @[Stdio.sendfile()]    */   void file_proxy(INT32 args)   {    struct my_file *f;    struct new_thread_data *p;    int from, to;       THREAD_T id; -  check_all_args("Stdio.File->proxy",args, BIT_OBJECT,0); +  check_all_args(NULL, args, BIT_OBJECT,0);    f=get_file_storage(Pike_sp[-args].u.object);    if(!f) -  SIMPLE_BAD_ARG_ERROR("Stdio.File->proxy", 1, "Stdio.File"); +  SIMPLE_ARG_TYPE_ERROR("proxy", 1, "Stdio.File");       from=fd_dup(f->box.fd);    if(from<0)    {    ERRNO=errno;    Pike_error("Failed to dup proxy fd. (errno=%d)\n",errno);    }    to=fd_dup(FD);    if(to<0)    {
pike.git/src/modules/_Stdio/file.c:5264: Inside #if defined(_REENTRANT)
   Pike_error("Failed to dup proxy fd.\n");    }       p=ALLOC_STRUCT(new_thread_data);    p->from=from;    p->to=to;       num_threads++;    if(th_create_small(&id,proxy_thread,p))    { -  free((char *)p); +  free(p);    while (fd_close(from) && errno == EINTR) {}    while (fd_close(to) && errno == EINTR) {}    Pike_error("Failed to create thread.\n");    }       th_destroy(& id); -  pop_n_elems(args); -  push_int(0); +    }      PMOD_EXPORT void create_proxy_pipe(struct object *o, int for_reading)   {    struct object *n,*n2;    push_object(n=clone_object(file_program,0));    push_int(fd_INTERPROCESSABLE);    apply(n,"pipe",1);    if(TYPEOF(Pike_sp[-1]) != PIKE_T_OBJECT)    Pike_error("Failed to create proxy pipe (errno=%d)!\n",get_file_storage(n)->my_errno);
pike.git/src/modules/_Stdio/file.c:5447:    err=fd_flock(fd, fd_LOCK_UN);   #else    err=fd_lockf(fd, fd_LOCK_UN);   #endif    THREADS_DISALLOW();    if ((err < 0) && (errno == EINTR)) {    check_threads_etc();    }    }while(err<0 && errno==EINTR);    - #ifdef _REENTRANT -  THIS_KEY->owner = NULL; -  if(THIS_KEY->owner_obj) -  { -  free_object(THIS_KEY->owner_obj); -  THIS_KEY->owner_obj = NULL; -  } - #endif +     THIS_KEY->f->key = 0; -  THIS_KEY->f = 0; +     }   }      static void init_file_locking(void)   {    ptrdiff_t off;    START_NEW_PROGRAM_ID (STDIO_FILE_LOCK_KEY);    off = ADD_STORAGE(struct file_lock_key_storage);   #ifdef _REENTRANT -  MAP_VARIABLE("_owner",tObj,0, +  PIKE_MAP_VARIABLE("_owner",    off + OFFSETOF(file_lock_key_storage, owner_obj), -  PIKE_T_OBJECT); +  tObj, PIKE_T_OBJECT, 0);   #endif -  MAP_VARIABLE("_file",tObj,0, +  PIKE_MAP_VARIABLE("_file",    off + OFFSETOF(file_lock_key_storage, file), -  PIKE_T_OBJECT); +  tObj, PIKE_T_OBJECT, 0);    set_init_callback(init_file_lock_key);    set_exit_callback(exit_file_lock_key);    file_lock_key_program=end_program();    file_lock_key_program->flags |= PROGRAM_DESTRUCT_IMMEDIATE;   }   static void exit_file_locking(void)   {    if(file_lock_key_program)    {    free_program(file_lock_key_program);
pike.git/src/modules/_Stdio/file.c:5499:   #endif /* HAVE_FD_FLOCK || HAVE_FD_LOCKF */      /*! @endclass    */      /*! @decl array(int) get_all_active_fd()    *! Returns the id of all the active file descriptors.    */   static void f_get_all_active_fd(INT32 args)   { -  int i,fds,ne; +  int i,fds=0;    PIKE_STAT_T foo; -  +  struct svalue *sp;    -  ne = MAX_OPEN_FILEDESCRIPTORS; -  +     pop_n_elems(args); -  for (i=fds=0; i<ne; i++) +  sp = Pike_sp;    { -  int q; + #ifndef __NT__ +  DIR *tmp; + #endif    THREADS_ALLOW(); -  + #ifndef __NT__ +  if( (tmp = opendir( + #ifdef HAVE_DARWIN_XATTR +  "/dev/fd" + #else +  "/proc/self/fd" + #endif +  )) ) +  { + #ifdef HAVE_DIRFD +  INT_TYPE dfd = dirfd(tmp); + #endif +  +  while(1) +  { +  INT_TYPE fd; +  char *ep; +  struct dirent *res; +  /* solaris, linux, cygwin, darwin, netbsd et.al. */ +  res = NULL; +  while( UNLIKELY(!(res = readdir(tmp))) && UNLIKELY(errno==EINTR)) +  ; +  if( !res ) +  break; +  +  fd = strtol(res->d_name, &ep, 10); +  +  if( LIKELY(ep != res->d_name) + #ifdef HAVE_DIRFD +  && (fd != dfd) + #endif +  ) +  { +  SET_SVAL_TYPE_SUBTYPE(*sp,PIKE_T_INT,0); +  sp++->u.integer = fd; +  fds++; +  } +  } +  closedir(tmp); +  } +  else + #endif /* __NT__ */ +  { + #ifdef HAVE_SYSCONF +  int max = sysconf(_SC_OPEN_MAX); +  /* NOTE: This might have been lowered, so we might not actually +  * get all FD:s. It is usually good, however. +  * +  * Also, this is not used on many systems +  */ + #else +  int max = 65535; + #endif +  for (i=0; i<max; i++) +  { +  int q;    q = fd_fstat(i,&foo); -  THREADS_DISALLOW(); +     if(!q)    { -  push_int(i); +  SET_SVAL_TYPE_SUBTYPE(*sp,PIKE_T_INT,0); +  sp++->u.integer = i;    fds++;    }    } -  f_aggregate(fds); +     } -  - #ifdef HAVE_NOTIFICATIONS -  - /*! @class File -  */ -  - /*! @decl void notify(void|int notification, function(void:void) callback) -  *! Receive notification when change occur within the fd. -  *! To use, create a Stdio.File object of a directory like -  *! Stdio.File(".") and then call notify() with the appropriate -  *! parameters. -  *! -  *! @note -  *! When a program registers for some notification, only the first notification -  *! will be received unless DN_MULTISHOT is specified as part of the -  *! notification argument. -  *! -  *! @note -  *! At present, this function is Linux-specific and requires a kernel which -  *! supports the F_NOTIFY fcntl() call. -  *! -  *! @param notification -  *! What to notify the callback of. See the Stdio.DN_* constants for more -  *! information about possible notifications. -  *! -  *! @param callback -  *! Function which should be called when notification is received. The -  *! function gets the signal used to indicate the notification as its -  *! argument and shouldn't return anyting. -  */ - void file_set_notify(INT32 args) { -  int notifications = 0; -  -  if (args == 1) -  SIMPLE_TOO_FEW_ARGS_ERROR("notify",2); -  -  if (args > 2) -  SIMPLE_TOO_FEW_ARGS_ERROR("notify", 2); -  -  if (args && TYPEOF(Pike_sp[-args]) != PIKE_T_INT) -  SIMPLE_BAD_ARG_ERROR("notify", 0, "int"); -  -  if (args && TYPEOF(Pike_sp[1-args]) != PIKE_T_FUNCTION) -  SIMPLE_BAD_ARG_ERROR("notify", 1, "function(void:void)"); -  -  if (args) { -  notifications = Pike_sp[1-args].u.integer; +  THREADS_DISALLOW(); +  Pike_sp = sp;    } -  - #ifdef __linux__ -  if (args) { -  pop_n_elems(1); -  push_int(SIGIO); -  +  f_aggregate(fds);   } -  fcntl(FD, F_NOTIFY, notifications); - #endif /* __linux__ */ +     -  pop_n_elems(args); - } -  - /*! @endclass -  */ -  - #endif /* HAVE_NOTIFICATIONS */ -  +    /*! @decl constant NOTE_ATTRIB = 8    *    * Used with @[Stdio.File()->set_fs_event_callback()] to monitor for attribute changes on a file.    *    * @note    * Available on systems that use kqueue.    */      /*! @decl constant NOTE_WRITE = 2    *
pike.git/src/modules/_Stdio/file.c:5745:    *! In Pike 7.5 and later OOB operations are always present.    */      PIKE_MODULE_EXIT   {    exit_stdio_efuns();    exit_stdio_stat();       exit_stdio_udp();    exit_stdio_sendfile(); +  exit_stdio_buffer();       if(file_program)    {    free_program(file_program);    file_program=0;    }    if(file_ref_program)    {    free_program(file_ref_program);    file_ref_program=0;
pike.git/src/modules/_Stdio/file.c:5793:    apply_low(o, fun, args); \    }      #include "file_functions.h"      static void file___init_ref(struct object *UNUSED(o))   {    SET_SVAL(REF, PIKE_T_OBJECT, 0, object, file_make_object_from_fd(-1, 0, 0));   }    - /* Avoid loss of precision warnings. */ - #ifdef __ECL - static inline long TO_LONG(ptrdiff_t x) - { -  return DO_NOT_WARN((long)x); - } - #else /* !__ECL */ - #define TO_LONG(x) ((long)(x)) - #endif /* __ECL */ -  +    #ifdef PIKE_DEBUG - void check_static_file_data(struct callback *a, void *b, void *c) + void check_static_file_data(struct callback *UNUSED(a), void *UNUSED(b), +  void *UNUSED(c))   {    if(file_program)    {   #define FILE_FUNC(X,Y,Z) \    if(PIKE_CONCAT(Y,_function_number)<0 || \    PIKE_CONCAT(Y,_function_number)> \    file_program->num_identifier_references) \    Pike_fatal(#Y "_function_number is incorrect: %ld\n", \ -  TO_LONG(PIKE_CONCAT(Y,_function_number))); +  (long)(PIKE_CONCAT(Y,_function_number)));   #include "file_functions.h"    }   }   #endif      #if defined(HAVE_TERMIOS_H)   void file_tcgetattr(INT32 args);   void file_tcsetattr(INT32 args);   void file_tcsendbreak(INT32 args);   void file_tcflush(INT32 args);
pike.git/src/modules/_Stdio/file.c:5835: Inside #if defined(HAVE_TERMIOS_H)
  /* void file_tcflow(INT32 args); */   /* void file_tcgetpgrp(INT32 args); */   /* void file_tcsetpgrp(INT32 args); */   #endif      static void fd__sprintf(INT32 args)   {    INT_TYPE type;       if(args < 1) -  SIMPLE_TOO_FEW_ARGS_ERROR("_sprintf",2); +  SIMPLE_WRONG_NUM_ARGS_ERROR("_sprintf",2);    if(TYPEOF(Pike_sp[-args]) != PIKE_T_INT) -  SIMPLE_BAD_ARG_ERROR("_sprintf",0,"int"); +  SIMPLE_ARG_TYPE_ERROR("_sprintf",0,"int");       type = Pike_sp[-args].u.integer;    pop_n_elems(args);    switch( type )    {    case 'O':    {    /* NB: A signed 64-bit int maxes out at 21 characters. */    char buf[30];    sprintf (buf, "Fd(%ld)", (long)FD);    push_text(buf);    return;    }       case 't':    { -  push_text("Fd"); +  push_static_text("Fd");    return;    }    }    push_undefined();   }         /*! @decl mapping(string:mapping) gethostip()    *!    *! Returns the IP addresses of the host.
pike.git/src/modules/_Stdio/file.c:5921: Inside #if defined(HAVE_LINUX_IF_H) && defined(HAVE_SYS_IOCTL_H)
   if( (ifr2.ifr_flags & IFF_LOOPBACK) ||    !(ifr2.ifr_flags & IFF_UP) ||    (ifr->ifr_addr.sa_family != AF_INET ) )    continue;       sval = simple_mapping_string_lookup( m, ifr->ifr_name );    if( !sval ) {       push_text( ifr->ifr_name );    -  push_constant_text( "ips" ); +  push_static_text( "ips" );    memcpy( &addr, &ifr->ifr_addr, sizeof(ifr->ifr_addr) );    push_text( inet_ntoa( addr.sin_addr ) );    f_aggregate(1);       f_aggregate_mapping(2);    mapping_insert(m, &Pike_sp[-2], &Pike_sp[-1]);    pop_n_elems(2);    }       up++;    }       fd_close(fd);    }   #endif /* defined(HAVE_LINUX_IF_H) && defined(HAVE_SYS_IOCTL_H) */       push_mapping(m);   }    -  + /*! @decl int getprotobyname(string(8bit) name) +  *! +  *! Returns the protocol number of the protocol @expr{name@}. +  *! This calls the POSIX function getprotobyname. +  *! If the protocol cannot be found an error is thrown. +  */ + static void f_getprotobyname(INT32 args) { + #ifdef HAVE_GETPROTOBYNAME +  struct protoent *proto; +  const char *name; +  +  get_all_args(NULL, args, "%c", &name); +  +  proto = getprotobyname(name); +  +  if (proto) { +  push_int(proto->p_proto); +  return; +  } + #endif +  Pike_error("Could not find protocol.\n"); + } +    #ifdef HAVE_OPENPTY   #include <pty.h>   #endif    -  + int fd_write_identifier_offset; +    PIKE_MODULE_INIT   {    struct object *o; -  +  int write_fun_num;       Pike_compiler->new_program->id = PROG_MODULE_STDIO_ID;       init_stdio_efuns();    init_stdio_stat(); -  +  init_stdio_buffer();    START_NEW_PROGRAM_ID(STDIO_FD);    ADD_STORAGE(struct my_file);      #define FILE_FUNC(X,Y,Z) \    PIKE_CONCAT(Y,_function_number) = ADD_FUNCTION(X,Y,Z,0);   #define FILE_OBJ tObjImpl_STDIO_FD   #include "file_functions.h"    -  MAP_VARIABLE("_read_callback",tMix,0, -  OFFSETOF(my_file, event_cbs[PIKE_FD_READ]),PIKE_T_MIXED); -  MAP_VARIABLE("_write_callback",tMix,0, -  OFFSETOF(my_file, event_cbs[PIKE_FD_WRITE]),PIKE_T_MIXED); -  MAP_VARIABLE("_read_oob_callback",tMix,0, -  OFFSETOF(my_file, event_cbs[PIKE_FD_READ_OOB]),PIKE_T_MIXED); -  MAP_VARIABLE("_write_oob_callback",tMix,0, -  OFFSETOF(my_file, event_cbs[PIKE_FD_WRITE_OOB]),PIKE_T_MIXED); -  MAP_VARIABLE("_fs_event_callback",tMix,0, -  OFFSETOF(my_file, event_cbs[PIKE_FD_FS_EVENT]),PIKE_T_MIXED); +  PIKE_MAP_VARIABLE("_errno", OFFSETOF(my_file, my_errno), +  tIntPos, PIKE_T_INT, ID_PROTECTED); +  PIKE_MAP_VARIABLE("_read_callback", +  OFFSETOF(my_file, event_cbs[PIKE_FD_READ]), +  tMix, PIKE_T_MIXED, 0); +  PIKE_MAP_VARIABLE("_write_callback", +  OFFSETOF(my_file, event_cbs[PIKE_FD_WRITE]), +  tMix, PIKE_T_MIXED, 0); +  PIKE_MAP_VARIABLE("_read_oob_callback", +  OFFSETOF(my_file, event_cbs[PIKE_FD_READ_OOB]), +  tMix, PIKE_T_MIXED, 0); +  PIKE_MAP_VARIABLE("_write_oob_callback", +  OFFSETOF(my_file, event_cbs[PIKE_FD_WRITE_OOB]), +  tMix, PIKE_T_MIXED, 0); +  PIKE_MAP_VARIABLE("_fs_event_callback", +  OFFSETOF(my_file, event_cbs[PIKE_FD_FS_EVENT]), +  tMix, PIKE_T_MIXED, 0);       fd_fd_factory_fun_num =    ADD_FUNCTION("fd_factory", fd_fd_factory,    tFunc(tNone, tObjIs_STDIO_FD), ID_PROTECTED);       fd_receive_fd_fun_num =    ADD_FUNCTION("receive_fd", NULL,    tFunc(tObjIs_STDIO_FD, tVoid), ID_PROTECTED);       ADD_FUNCTION("`_fd", fd_backtick__fd, tFunc(tNone, tObjIs_STDIO_FD), 0);
pike.git/src/modules/_Stdio/file.c:5993:    /* function(int, void|mapping:string) */    ADD_FUNCTION("_sprintf",fd__sprintf,    tFunc(tInt tOr(tVoid,tMapping),tString),ID_PROTECTED);       init_file_locking();    pike_set_prog_event_callback(file_handle_events);       file_program=end_program();    add_program_constant("Fd",file_program,0);    +  write_fun_num = find_identifier("write", file_program); +  fd_write_identifier_offset = +  file_program->identifier_references[write_fun_num].identifier_offset; +     o=file_make_object_from_fd(0, low_fd_query_properties(0)|FILE_READ,    fd_CAN_NONBLOCK);    ((struct my_file *)(o->storage + file_program->inherits->storage_offset))->flags |= FILE_NO_CLOSE_ON_DESTRUCT;    (void) dmalloc_register_fd(0);    dmalloc_accept_leak_fd(0);    add_object_constant("_stdin",o,0);    free_object(o);       o=file_make_object_from_fd(1, low_fd_query_properties(1)|FILE_WRITE,    fd_CAN_NONBLOCK);
pike.git/src/modules/_Stdio/file.c:6019:    o=file_make_object_from_fd(2, low_fd_query_properties(2)|FILE_WRITE,    fd_CAN_NONBLOCK);    ((struct my_file *)(o->storage + file_program->inherits->storage_offset))->flags |= FILE_NO_CLOSE_ON_DESTRUCT;    (void) dmalloc_register_fd(2);    dmalloc_accept_leak_fd(2);    add_object_constant("_stderr",o,0);    free_object(o);       START_NEW_PROGRAM_ID (STDIO_FD_REF);    ADD_STORAGE(struct svalue); -  MAP_VARIABLE("_fd", tObjIs_STDIO_FD, 0, 0, PIKE_T_MIXED); +  PIKE_MAP_VARIABLE("_fd", 0, tObjIs_STDIO_FD, PIKE_T_MIXED, 0);    set_init_callback(file___init_ref);      #define FILE_FUNC(X,Y,Z) \    ADD_FUNCTION(X, PIKE_CONCAT(Y,_ref), Z, 0);   #define FILE_OBJ tObjImpl_STDIO_FD_REF   #include "file_functions.h"       file_ref_program=end_program();    add_program_constant("Fd_ref",file_ref_program,0);   
pike.git/src/modules/_Stdio/file.c:6047: Inside #if defined(HAVE_FSETXATTR)
   *! create, which will fail if the attribute already exists.    */    add_integer_constant("XATTR_CREATE", XATTR_CREATE, 0 );       /*! @decl constant XATTR_REPLACE    *! Used by @[setxattr] function and method to signify a replace,    *! which will fail the the attribute does not already exists.    */    add_integer_constant("XATTR_REPLACE", XATTR_REPLACE, 0 );   #endif +  +  /* enum FileModeFlags */ +  type_stack_mark(); +  push_int_type(0, 0xffff); +  push_type_value(pop_unfinished_type()); +  simple_add_constant("FileModeFlags", Pike_sp-1, 0); +  pop_stack(); +  +  add_integer_constant("FILE_READ", FILE_READ, 0); +  add_integer_constant("FILE_WRITE", FILE_WRITE, 0); +  add_integer_constant("FILE_APPEND", FILE_APPEND, 0); +  add_integer_constant("FILE_CREATE", FILE_CREATE, 0); +  add_integer_constant("FILE_TRUNC", FILE_TRUNC, 0); +  add_integer_constant("FILE_EXCLUSIVE", FILE_EXCLUSIVE, 0); +  add_integer_constant("FILE_NONBLOCKING", FILE_NONBLOCKING, 0); +  +  /* enum FilePropertyFlags */ +  type_stack_mark(); +  push_int_type(0, 0xff); +  push_type_value(pop_unfinished_type()); +  simple_add_constant("FilePropertyFlags", Pike_sp-1, 0); +  pop_stack(); +     add_integer_constant("PROP_IPC",fd_INTERPROCESSABLE,0);    add_integer_constant("PROP_NONBLOCK",fd_CAN_NONBLOCK,0);    add_integer_constant("PROP_SEND_FD",fd_SEND_FD,0);    add_integer_constant("PROP_SHUTDOWN",fd_CAN_SHUTDOWN,0);    add_integer_constant("PROP_BUFFERED",fd_BUFFERED,0);    add_integer_constant("PROP_BIDIRECTIONAL",fd_BIDIRECTIONAL,0);    add_integer_constant("PROP_REVERSE",fd_REVERSE,0);       add_integer_constant("PROP_IS_NONBLOCKING", FILE_NONBLOCKING, 0);    - #ifdef DN_ACCESS -  /*! @decl constant DN_ACCESS -  *! Used in @[File.notify()] to get a callback when files -  *! within a directory are accessed. -  */ -  add_integer_constant("DN_ACCESS", DN_ACCESS, 0); +  /* seek modes. These are strings to keep compatibility in seek(). */ +  { +  static char seek_how[] = { +  SEEK_CUR,0, +  SEEK_SET,0, +  SEEK_END,0 + #ifdef SEEK_DATA +  ,SEEK_DATA,0, +  SEEK_HOLE,0   #endif -  +  }; +  add_string_constant( "SEEK_CUR", seek_how+0, 0 ); +  add_string_constant( "SEEK_SET", seek_how+2, 0 ); +  add_string_constant( "SEEK_END", seek_how+4, 0 ); + #ifdef SEEK_DATA +  add_string_constant( "SEEK_DATA", seek_how+6, 0 ); +  add_string_constant( "SEEK_HOLE", seek_how+8, 0 ); + #endif +  };    - #ifdef DN_MODIFY -  /*! @decl constant DN_MODIFY -  *! Used in @[File.notify()] to get a callback when files -  *! within a directory are modified. + #ifdef SOL_SOCKET +  /*! @decl constant SOL_SOCKET +  *! Used in @[File.setsockopt()] to set socket-level options    */ -  add_integer_constant("DN_MODIFY", DN_MODIFY, 0); +  add_integer_constant("SOL_SOCKET", SOL_SOCKET, 0);   #endif    - #ifdef DN_CREATE -  /*! @decl constant DN_CREATE -  *! Used in @[File.notify()] to get a callback when new -  *! files are created within a directory. + #ifdef IPPROTO_IP +  /*! @decl constant IPPROTO_IP +  *! Used in @[File.setsockopt()] to set IP-level options    */ -  add_integer_constant("DN_CREATE", DN_CREATE, 0); +  add_integer_constant("IPPROTO_IP", IPPROTO_IP, 0);   #endif    - #ifdef DN_DELETE -  /*! @decl constant DN_DELETE -  *! Used in @[File.notify()] to get a callback when files -  *! are deleted within a directory. + #ifdef IPPROTO_TCP +  /*! @decl constant IPPROTO_TCP +  *! Used in @[File.setsockopt()] to set TCP-level options    */ -  add_integer_constant("DN_DELETE", DN_DELETE, 0); +  add_integer_constant("IPPROTO_TCP", IPPROTO_TCP, 0);   #endif    - #ifdef DN_RENAME -  /*! @decl constant DN_RENAME -  *! Used in @[File.notify()] to get a callback when files -  *! within a directory are renamed. + #ifdef TCP_NODELAY +  /*! @decl constant TCP_NODELAY +  *! Used in @[File.setsockopt()] to control Nagle's Algorithm.    */ -  add_integer_constant("DN_RENAME", DN_RENAME, 0); +  add_integer_constant("TCP_NODELAY", TCP_NODELAY, 0);   #endif    - #ifdef DN_ATTRIB -  /*! @decl constant DN_ATTRIB -  *! Used in @[File.notify()] to get a callback when attributes -  *! of files within a directory are changed. + #ifdef SO_KEEPALIVE +  /*! @decl constant SO_KEEPALIVE +  *! Used in @[File.setsockopt()] to control TCP/IP keep-alive packets.    */ -  add_integer_constant("DN_ATTRIB", DN_ATTRIB, 0); +  add_integer_constant("SO_KEEPALIVE", SO_KEEPALIVE, 0);   #endif    - #ifdef DN_MULTISHOT -  /*! @decl constant DN_MULTISHOT -  *! Used in @[File.notify()]. If DN_MULTISHOT is used, signals will -  *! be sent for all notifications the program has registred for. Otherwise -  *! only the first event the program is listening for will be received and -  *! then the program must reregister for the events to receive futher events. + #ifdef IP_TOS +  /*! @decl constant IP_TOS +  *! Used in @[File.setsockopt()] to set Type Of Service    */ -  add_integer_constant("DN_MULTISHOT", DN_MULTISHOT, 0); +  add_integer_constant("IP_TOS", IP_TOS, 0);   #endif       add_integer_constant("__HAVE_OOB__",1,0);   #ifdef PIKE_OOB_WORKS    add_integer_constant("__OOB__",PIKE_OOB_WORKS,0);   #else    add_integer_constant("__OOB__",-1,0); /* unknown */   #endif      #ifdef HAVE_SYS_UN_H
pike.git/src/modules/_Stdio/file.c:6159:   #endif /* HAVE_KQUEUE */         #if 0    /* Not implemented yet. */   #ifdef HAVE_UNLINKAT    add_integer_constant("__HAVE_UNLINKAT__",1,0);   #endif   #endif /* 0 */    + #ifdef __NT__ +  add_integer_constant("__HAVE_UTF8_FS__", 1, 0); + #endif +    #ifdef HAVE_PIKE_SEND_FD    add_integer_constant("__HAVE_SEND_FD__", 1, 0);   #endif    /* function(:array(int)) */    ADD_FUNCTION2("get_all_active_fd", f_get_all_active_fd,    tFunc(tNone,tArr(tInt)), 0, OPT_EXTERNAL_DEPEND);       /* function(void:mapping) */    ADD_FUNCTION2("gethostip", f_gethostip, tFunc(tNone,tMapping),    0, OPT_EXTERNAL_DEPEND);      #ifdef PIKE_DEBUG    dmalloc_accept_leak(add_to_callback(&do_debug_callbacks,    check_static_file_data,    0,    0));   #endif -  +  ADD_FUNCTION2("getprotobyname", f_getprotobyname, tFunc(tStr8,tInt), 0, 0);   }      /*! @endmodule    */      /* Used from backend */   PMOD_EXPORT int pike_make_pipe(int *fds)   {    return socketpair(AF_UNIX, SOCK_STREAM, 0, fds);   }      PMOD_EXPORT int fd_from_object(struct object *o)   {    extern int fd_from_portobject( struct object *o );    struct my_file *f=get_file_storage(o);    if(!f)    return fd_from_portobject( o );    return f->box.fd;   }