3881 lines
		
	
	
		
			74 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			3881 lines
		
	
	
		
			74 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/*  Part of SWI-Prolog
 | 
						|
 | 
						|
    Author:        Jan Wielemaker
 | 
						|
    E-mail:        J.Wielemaker@vu.nl
 | 
						|
    WWW:           http://www.swi-prolog.org
 | 
						|
    Copyright (C): 1985-2012, University of Amsterdam
 | 
						|
			      VU University Amsterdam
 | 
						|
 | 
						|
    This library is free software; you can redistribute it and/or
 | 
						|
    modify it under the terms of the GNU Lesser General Public
 | 
						|
    License as published by the Free Software Foundation; either
 | 
						|
    version 2.1 of the License, or (at your option) any later version.
 | 
						|
 | 
						|
    This library is distributed in the hope that it will be useful,
 | 
						|
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
    Lesser General Public License for more details.
 | 
						|
 | 
						|
    You should have received a copy of the GNU Lesser General Public
 | 
						|
    License along with this library; if not, write to the Free Software
 | 
						|
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 | 
						|
*/
 | 
						|
 | 
						|
#if defined(__WINDOWS__) || defined(__MINGW32__)
 | 
						|
#include <stdint.h>
 | 
						|
#include "windows/uxnt.h"
 | 
						|
#ifdef _YAP_NOT_INSTALLED_
 | 
						|
#include <config.h>
 | 
						|
#else
 | 
						|
#ifdef WIN64
 | 
						|
#include "config/win64.h"
 | 
						|
#else
 | 
						|
#include "config/win32.h"
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
#include <winsock2.h>
 | 
						|
#define CRLF_MAPPING 1
 | 
						|
#else
 | 
						|
#include <config.h>
 | 
						|
#endif
 | 
						|
#include <stdio.h>
 | 
						|
 | 
						|
#if __ANDROID__
 | 
						|
#include <android/asset_manager.h>
 | 
						|
#include <android/asset_manager_jni.h>
 | 
						|
#include <android/log.h>
 | 
						|
 | 
						|
int Yap_isAsset( const char *path );
 | 
						|
 | 
						|
#define LOG(...) __android_log_print(ANDROID_LOG_DEBUG,"YAP Stream", __VA_ARGS__)
 | 
						|
#else
 | 
						|
#define __android_log_print(i,loc,msg,...)
 | 
						|
#define ANDROID_LOG_INFO 0
 | 
						|
#define ANDROID_LOG_ERROR 0
 | 
						|
#define ANDROID_LOG_DEBUG 0
 | 
						|
#define LOG(...)
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
This modules defines the  SWI-Prolog  I/O   streams.  These  streams are
 | 
						|
provided to gain common access to  any   type  of character data: files,
 | 
						|
stdio streams, but also resources, strings, XPCE objects, etc.
 | 
						|
 | 
						|
MT:
 | 
						|
 | 
						|
Multithreading is supported through  Slock()   and  Sunlock(). These are
 | 
						|
recursive locks. If a stream handle  might   be  known to another thread
 | 
						|
locking is required.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
#if _FILE_OFFSET_BITS == 64 || defined(_LARGE_FILES)
 | 
						|
#define O_LARGEFILES 1		/* use for conditional code in Prolog */
 | 
						|
#else
 | 
						|
#undef O_LARGEFILES
 | 
						|
#endif
 | 
						|
 | 
						|
#define PL_KERNEL 1
 | 
						|
#if HAVE_LOCALE_H && HAVE_SETLOCALE
 | 
						|
//#define O_LOCALE 1
 | 
						|
#endif
 | 
						|
#include <wchar.h>
 | 
						|
#define NEEDS_SWINSOCK
 | 
						|
#include "SWI-Stream.h"
 | 
						|
#include "SWI-Prolog.h"
 | 
						|
#include "pl-utf8.h"
 | 
						|
#include <sys/types.h>
 | 
						|
#ifdef HAVE_SYS_TIME_H
 | 
						|
#include <sys/time.h>
 | 
						|
#else
 | 
						|
#include <time.h>
 | 
						|
#endif
 | 
						|
#include <errno.h>
 | 
						|
#ifdef HAVE_MALLOC_H
 | 
						|
#include <malloc.h>
 | 
						|
#else
 | 
						|
#ifdef HAVE_SYS_MALLOC_H
 | 
						|
#include <sys/malloc.h>
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
#include <memory.h>
 | 
						|
#include <string.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <limits.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#ifdef HAVE_SYS_SELECT_H
 | 
						|
#include <sys/select.h>
 | 
						|
#endif
 | 
						|
#ifdef HAVE_UNISTD_H
 | 
						|
#include <unistd.h>
 | 
						|
#endif
 | 
						|
#include <stdio.h>			/* sprintf() for numeric values */
 | 
						|
#include <assert.h>
 | 
						|
#ifdef SYSLIB_H
 | 
						|
#include SYSLIB_H
 | 
						|
#endif
 | 
						|
 | 
						|
#if THREADS
 | 
						|
#define O_PLMT 1
 | 
						|
#endif
 | 
						|
 | 
						|
#define ROUND(p, n) ((((p) + (n) - 1) & ~((n) - 1)))
 | 
						|
#define UNDO_SIZE ROUND(PL_MB_LEN_MAX, sizeof(wchar_t))
 | 
						|
 | 
						|
#ifndef FALSE
 | 
						|
#define FALSE 0
 | 
						|
#endif
 | 
						|
#ifndef TRUE
 | 
						|
#define TRUE 1
 | 
						|
#endif
 | 
						|
 | 
						|
#define char_to_int(c)	(0xff & (int)(c))
 | 
						|
 | 
						|
#define TMPBUFSIZE 256			/* Serror bufsize for Svfprintf() */
 | 
						|
 | 
						|
int Slinesize = SIO_LINESIZE;		/* Sgets() buffer size */
 | 
						|
 | 
						|
static ssize_t	S__flushbuf(IOSTREAM *s);
 | 
						|
static void	run_close_hooks(IOSTREAM *s);
 | 
						|
static int	S__removebuf(IOSTREAM *s);
 | 
						|
static int	S__seterror(IOSTREAM *s);
 | 
						|
 | 
						|
#ifdef O_PLMT
 | 
						|
#define SLOCK(s)    if ( s->mutex ) recursiveMutexLock(s->mutex)
 | 
						|
#define SUNLOCK(s)  if ( s->mutex ) recursiveMutexUnlock(s->mutex)
 | 
						|
static inline int
 | 
						|
STRYLOCK(IOSTREAM *s)
 | 
						|
{ if ( s->mutex &&
 | 
						|
       recursiveMutexTryLock(s->mutex) == EBUSY )
 | 
						|
    return FALSE;
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
#else
 | 
						|
#define SLOCK(s)
 | 
						|
#define SUNLOCK(s)
 | 
						|
#define STRYLOCK(s) (TRUE)
 | 
						|
#endif
 | 
						|
 | 
						|
#include "pl-error.h"
 | 
						|
#ifdef O_LOCALE
 | 
						|
#include "pl-locale.h"
 | 
						|
#endif
 | 
						|
 | 
						|
extern int			fatalError(const char *fm, ...);
 | 
						|
extern int			PL_handle_signals( void );
 | 
						|
extern IOENC			initEncoding(void);
 | 
						|
extern int			reportStreamError(IOSTREAM *s);
 | 
						|
extern record_t			PL_record(term_t t);
 | 
						|
extern int			PL_thread_self(void);
 | 
						|
extern void			unallocStream(IOSTREAM *s);
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	      BUFFER		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Note that the  buffer  is  allocated   from  s->unbuffer,  which  starts
 | 
						|
UNDO_SIZE before s->buffer, so we can always push-back a wide
 | 
						|
character into a multibyte stream. We do not do this for SIO_USERBUF
 | 
						|
case, but this is only used by the output stream Svfprintf() where it is
 | 
						|
not needed.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
static size_t
 | 
						|
S__setbuf(IOSTREAM *s, char *buffer, size_t size)
 | 
						|
{ char *newbuf, *newunbuf;
 | 
						|
  int newflags = s->flags;
 | 
						|
 | 
						|
  if ( size == 0 )
 | 
						|
    size = SIO_BUFSIZE;
 | 
						|
 | 
						|
  if ( (s->flags & SIO_OUTPUT) )
 | 
						|
  { if ( S__removebuf(s) < 0 )
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  if ( buffer )
 | 
						|
  { newunbuf = newbuf = buffer;
 | 
						|
    newflags |= SIO_USERBUF;
 | 
						|
  } else
 | 
						|
  { if ( !(newunbuf = malloc(size+UNDO_SIZE)) )
 | 
						|
    { errno = ENOMEM;
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
    newflags &= ~SIO_USERBUF;
 | 
						|
    newbuf = newunbuf + UNDO_SIZE;
 | 
						|
  }
 | 
						|
 | 
						|
  if ( (s->flags & SIO_INPUT) )
 | 
						|
  { size_t buffered = s->limitp - s->bufp;
 | 
						|
    size_t copy = (buffered < size ? buffered : size);
 | 
						|
 | 
						|
    if ( size < buffered )
 | 
						|
    { size_t offset = size - buffered;
 | 
						|
      int64_t newpos;
 | 
						|
 | 
						|
      if ( s->functions->seek64 )
 | 
						|
      { newpos = (*s->functions->seek64)(s->handle, offset, SIO_SEEK_CUR);
 | 
						|
      } else if ( s->functions->seek )
 | 
						|
      { newpos = (*s->functions->seek)(s->handle, (long)offset, SIO_SEEK_CUR);
 | 
						|
      } else
 | 
						|
      { newpos = -1;
 | 
						|
	errno = ESPIPE;
 | 
						|
      }
 | 
						|
 | 
						|
      if ( newpos == -1 )
 | 
						|
      { if ( !(newflags & SIO_USERBUF) )
 | 
						|
	{ int oldeno = errno;
 | 
						|
 | 
						|
	  free(newunbuf);
 | 
						|
	  errno = oldeno;
 | 
						|
	  S__seterror(s);
 | 
						|
	  return -1;
 | 
						|
	}
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    memcpy(newbuf, s->bufp, copy);
 | 
						|
    S__removebuf(s);
 | 
						|
    s->unbuffer = newunbuf;
 | 
						|
    s->bufp = s->buffer = newbuf;
 | 
						|
    s->limitp = s->buffer+copy;
 | 
						|
  } else
 | 
						|
  { s->unbuffer = newunbuf;
 | 
						|
    s->bufp = s->buffer = newbuf;
 | 
						|
    s->limitp = &s->buffer[size];
 | 
						|
  }
 | 
						|
  s->bufsize = (int)size;
 | 
						|
  s->flags = newflags;
 | 
						|
 | 
						|
  return size;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
Ssetbuffer(IOSTREAM *s, char *buffer, size_t size)
 | 
						|
{ if ( S__setbuf(s, buffer, size) != (size_t)-1 )
 | 
						|
    s->flags &= ~SIO_USERBUF;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
S__removebuf(IOSTREAM *s)
 | 
						|
{ if ( s->buffer && s->unbuffer )
 | 
						|
  { int rval = 0;
 | 
						|
 | 
						|
    if ( (s->flags & SIO_OUTPUT) && S__flushbuf(s) < 0 )
 | 
						|
      rval = -1;
 | 
						|
 | 
						|
    if ( !(s->flags & SIO_USERBUF) )
 | 
						|
      free(s->unbuffer);
 | 
						|
    s->bufp = s->limitp = s->buffer = s->unbuffer = NULL;
 | 
						|
    s->bufsize = 0;
 | 
						|
 | 
						|
    return rval;
 | 
						|
  }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef DEBUGX
 | 
						|
#define DEBUG_IO_LOCKS 1
 | 
						|
#endif
 | 
						|
#ifdef DEBUG_IO_LOCKS
 | 
						|
static char *
 | 
						|
Sname(IOSTREAM *s)
 | 
						|
{ if ( s == Serror ) return "error";
 | 
						|
  if ( s == Sinput ) return "input";
 | 
						|
  if ( s == Soutput ) return "output";
 | 
						|
  return "?";
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#include <execinfo.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
static void
 | 
						|
print_trace(void)
 | 
						|
{ void *array[7];
 | 
						|
  size_t size;
 | 
						|
  char **strings;
 | 
						|
  size_t i;
 | 
						|
 | 
						|
  size = backtrace(array, sizeof(array)/sizeof(void *));
 | 
						|
  strings = backtrace_symbols(array, size);
 | 
						|
 | 
						|
  printf(" Stack:");
 | 
						|
  for(i = 1; i < size; i++)
 | 
						|
  { printf("\n\t[%ld] %s", (long)i, strings[i]);
 | 
						|
  }
 | 
						|
  printf("\n");
 | 
						|
 | 
						|
  free(strings);
 | 
						|
}
 | 
						|
#endif /*DEBUG_IO_LOCKS*/
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Slock(IOSTREAM *s)
 | 
						|
{ SLOCK(s);
 | 
						|
 | 
						|
  if ( s->erased )
 | 
						|
  { SUNLOCK(s);
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef DEBUG_IO_LOCKS
 | 
						|
  if ( DEBUG || s->locks > 2 )
 | 
						|
  { printf("  Lock [%d]: %s: %d locks", PL_thread_self(), Sname(s), s->locks+1);
 | 
						|
    print_trace();
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  if ( !s->locks++ )
 | 
						|
  { if ( (s->flags & (SIO_NBUF|SIO_OUTPUT)) == (SIO_NBUF|SIO_OUTPUT) )
 | 
						|
      return S__setbuf(s, NULL, TMPBUFSIZE) == (size_t)-1 ? -1 : 0;
 | 
						|
  }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
StryLock(IOSTREAM *s)
 | 
						|
{ if ( !STRYLOCK(s) )
 | 
						|
    return -1;
 | 
						|
 | 
						|
  if ( s->erased )
 | 
						|
  { SUNLOCK(s);
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  if ( !s->locks++ )
 | 
						|
  { if ( (s->flags & (SIO_NBUF|SIO_OUTPUT)) == (SIO_NBUF|SIO_OUTPUT) )
 | 
						|
      return S__setbuf(s, NULL, TMPBUFSIZE) == (size_t)-1 ? -1 : 0;
 | 
						|
  }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Sunlock(IOSTREAM *s)
 | 
						|
{ int rval = 0;
 | 
						|
 | 
						|
#ifdef DEBUG_IO_LOCKS
 | 
						|
  if ( s->locks > 3 )
 | 
						|
  { printf("Unlock [%d]: %s: %d locks", PL_thread_self(), Sname(s), s->locks-1);
 | 
						|
    print_trace();
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  if ( s->locks )
 | 
						|
  { if ( --s->locks == 0 )
 | 
						|
    { if ( (s->flags & (SIO_NBUF|SIO_OUTPUT)) == (SIO_NBUF|SIO_OUTPUT) )
 | 
						|
	rval = S__removebuf(s);
 | 
						|
    }
 | 
						|
  } else
 | 
						|
  { assert(0);
 | 
						|
  }
 | 
						|
 | 
						|
  SUNLOCK(s);
 | 
						|
  return rval;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *		TIMEOUT		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
#ifdef HAVE_SELECT
 | 
						|
 | 
						|
#ifndef __WINDOWS__
 | 
						|
typedef int SOCKET;
 | 
						|
#define INVALID_SOCKET -1
 | 
						|
#define Swinsock(s) Sfileno(s)
 | 
						|
#define NFDS(n) (n+1)
 | 
						|
#else
 | 
						|
#define NFDS(n) (0)			/* 1st arg of select is ignored */
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
S__wait(IOSTREAM *s)
 | 
						|
{ SOCKET fd = Swinsock(s);
 | 
						|
  fd_set wait;
 | 
						|
  struct timeval time;
 | 
						|
  int rc;
 | 
						|
 | 
						|
  if ( fd == INVALID_SOCKET )
 | 
						|
  { errno = EPERM;			/* no permission to select */
 | 
						|
    s->flags |= SIO_FERR;
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  time.tv_sec  = s->timeout / 1000;
 | 
						|
  time.tv_usec = (s->timeout % 1000) * 1000;
 | 
						|
  FD_ZERO(&wait);
 | 
						|
  FD_SET(fd, &wait);
 | 
						|
 | 
						|
  for(;;)
 | 
						|
  { if ( (s->flags & SIO_INPUT) )
 | 
						|
      rc = select(NFDS(fd), &wait, NULL, NULL, &time);
 | 
						|
    else
 | 
						|
      rc = select(NFDS(fd), NULL, &wait, NULL, &time);
 | 
						|
 | 
						|
    if ( rc < 0 && errno == EINTR )
 | 
						|
    { if ( PL_handle_signals() < 0 )
 | 
						|
      { errno = EPLEXCEPTION;
 | 
						|
	return -1;
 | 
						|
      }
 | 
						|
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  if ( rc == 0 )
 | 
						|
  { s->flags |= (SIO_TIMEOUT|SIO_FERR);
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  return 0;				/* ok, data available */
 | 
						|
}
 | 
						|
 | 
						|
#endif /*HAVE_SELECT*/
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	     FLUSH/FILL		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
/* return values: -1: error, else #bytes written */
 | 
						|
 | 
						|
static ssize_t
 | 
						|
S__flushbuf(IOSTREAM *s)
 | 
						|
{ char *from, *to;
 | 
						|
  ssize_t rc;
 | 
						|
 | 
						|
  SLOCK(s);
 | 
						|
  from = s->buffer;
 | 
						|
  to   = s->bufp;
 | 
						|
 | 
						|
  while ( from < to )
 | 
						|
  { size_t size = (size_t)(to - from);
 | 
						|
    ssize_t n;
 | 
						|
 | 
						|
#ifdef HAVE_SELECT
 | 
						|
    s->flags &= ~SIO_TIMEOUT;
 | 
						|
 | 
						|
    if ( s->timeout >= 0 )
 | 
						|
    { if ( (rc=S__wait(s)) < 0 )
 | 
						|
	goto partial;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    n = (*s->functions->write)(s->handle, from, size);
 | 
						|
 | 
						|
    if ( n > 0 )			/* wrote some */
 | 
						|
    { from += n;
 | 
						|
    } else if ( n < 0 )			/* error */
 | 
						|
    { S__seterror(s);
 | 
						|
      rc = -1;
 | 
						|
      goto out;
 | 
						|
    } else				/* wrote nothing? */
 | 
						|
    { break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef HAVE_SELECT
 | 
						|
partial:
 | 
						|
#endif
 | 
						|
  if ( to == from )			/* full flush */
 | 
						|
  { rc = s->bufp - s->buffer;
 | 
						|
    s->bufp = s->buffer;
 | 
						|
  } else				/* partial flush */
 | 
						|
  { size_t left = to - from;
 | 
						|
 | 
						|
    rc = from - s->buffer;
 | 
						|
    memmove(s->buffer, from, left);
 | 
						|
    s->bufp = s->buffer + left;
 | 
						|
  }
 | 
						|
 | 
						|
out:
 | 
						|
  SUNLOCK(s);
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
S__flushbufc(int c, IOSTREAM *s)
 | 
						|
{ if ( s->buffer )
 | 
						|
  { if ( S__flushbuf(s) <= 0 )		/* == 0: no progress!? */
 | 
						|
      c = -1;
 | 
						|
    else
 | 
						|
      *s->bufp++ = (c & 0xff);
 | 
						|
  } else
 | 
						|
  { if ( s->flags & SIO_NBUF )
 | 
						|
    { char chr = (char)c;
 | 
						|
 | 
						|
      if ( (*s->functions->write)(s->handle, &chr, 1) != 1 )
 | 
						|
      { S__seterror(s);
 | 
						|
	c = -1;
 | 
						|
      }
 | 
						|
    } else
 | 
						|
    { if ( S__setbuf(s, NULL, 0) == (size_t)-1 )
 | 
						|
	c = -1;
 | 
						|
      else
 | 
						|
	*s->bufp++ = (char)c;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return c;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
S__fillbuf() fills the read-buffer, returning the first character of it.
 | 
						|
It also realises the SWI-Prolog timeout facility.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
int
 | 
						|
S__fillbuf(IOSTREAM *s)
 | 
						|
{ int c;
 | 
						|
 | 
						|
  if ( s->flags & (SIO_FEOF|SIO_FERR) )	/* reading past eof */
 | 
						|
  { if ( s->flags & SIO_FEOF2ERR )
 | 
						|
      s->flags |= (SIO_FEOF2|SIO_FERR);
 | 
						|
    else
 | 
						|
      s->flags |= SIO_FEOF2;
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef HAVE_SELECT
 | 
						|
  s->flags &= ~SIO_TIMEOUT;
 | 
						|
 | 
						|
  if ( s->timeout >= 0 && !s->downstream )
 | 
						|
  { int rc;
 | 
						|
 | 
						|
    if ( (rc=S__wait(s)) < 0 )
 | 
						|
      return rc;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  if ( s->flags & SIO_NBUF )
 | 
						|
  { char chr;
 | 
						|
    ssize_t n;
 | 
						|
    n = (*s->functions->read)(s->handle, &chr, 1);
 | 
						|
    if ( n == 1 )
 | 
						|
    { c = char_to_int(chr);
 | 
						|
      return c;
 | 
						|
    } else if ( n == 0 )
 | 
						|
    { if ( !(s->flags & SIO_NOFEOF) )
 | 
						|
	s->flags |= SIO_FEOF;
 | 
						|
      return -1;
 | 
						|
    } else
 | 
						|
    { S__seterror(s);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  } else
 | 
						|
  { ssize_t n;
 | 
						|
    size_t len;
 | 
						|
 | 
						|
    if ( !s->buffer )
 | 
						|
    { if ( S__setbuf(s, NULL, 0) == (size_t)-1 )
 | 
						|
	return -1;
 | 
						|
      s->bufp = s->limitp = s->buffer;
 | 
						|
      len = s->bufsize;
 | 
						|
    } else if ( s->bufp < s->limitp )
 | 
						|
    { len = s->limitp - s->bufp;
 | 
						|
      memmove(s->buffer, s->bufp, s->limitp - s->bufp);
 | 
						|
      s->bufp = s->buffer;
 | 
						|
      s->limitp = &s->bufp[len];
 | 
						|
      len = s->bufsize - len;
 | 
						|
    } else
 | 
						|
    { s->bufp = s->limitp = s->buffer;
 | 
						|
      len = s->bufsize;
 | 
						|
    }
 | 
						|
 | 
						|
  n = (*s->functions->read)(s->handle, s->limitp, len);
 | 
						|
    if ( n > 0 )
 | 
						|
    { s->limitp += n;
 | 
						|
      c = char_to_int(*s->bufp++);
 | 
						|
      return c;
 | 
						|
    } else
 | 
						|
    { if ( n == 0 )
 | 
						|
      { if ( !(s->flags & SIO_NOFEOF) )
 | 
						|
	  s->flags |= SIO_FEOF;
 | 
						|
	return -1;
 | 
						|
#ifdef EWOULDBLOCK
 | 
						|
      } else if ( errno == EWOULDBLOCK )
 | 
						|
      { s->bufp = s->buffer;
 | 
						|
	s->limitp = s->buffer;
 | 
						|
	S__seterror(s);
 | 
						|
	return -1;
 | 
						|
#endif
 | 
						|
      } else
 | 
						|
      { S__seterror(s);
 | 
						|
	return -1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	   CHARACTER I/O	*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
 | 
						|
static inline void
 | 
						|
update_linepos(IOSTREAM *s, int c)
 | 
						|
{ IOPOS *p = s->position;
 | 
						|
 | 
						|
  if ( c > '\r' )			/* speedup the 99% case a bit */
 | 
						|
  { p->linepos++;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  switch(c)
 | 
						|
  { case '\n':
 | 
						|
      p->lineno++;
 | 
						|
      p->linepos = 0;
 | 
						|
      s->flags &= ~SIO_NOLINEPOS;
 | 
						|
      break;
 | 
						|
    case '\r':
 | 
						|
      p->linepos = 0;
 | 
						|
      s->flags &= ~SIO_NOLINEPOS;
 | 
						|
      break;
 | 
						|
    case '\b':
 | 
						|
      if ( p->linepos > 0 )
 | 
						|
	p->linepos--;
 | 
						|
      break;
 | 
						|
    case EOF:
 | 
						|
      break;
 | 
						|
    case '\t':
 | 
						|
      p->linepos |= 7;
 | 
						|
      /*FALLTHROUGH*/
 | 
						|
    default:
 | 
						|
      p->linepos++;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
S__fcheckpasteeof(IOSTREAM *s, int c)
 | 
						|
{ S__checkpasteeof(s, c);
 | 
						|
 | 
						|
  return c;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
S__fupdatefilepos_getc(IOSTREAM *s, int c)
 | 
						|
{ IOPOS *p = s->position;
 | 
						|
 | 
						|
  update_linepos(s, c);
 | 
						|
  p->byteno++;
 | 
						|
  p->charno++;
 | 
						|
 | 
						|
  return c;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static inline int
 | 
						|
S__updatefilepos(IOSTREAM *s, int c)
 | 
						|
{ IOPOS *p = s->position;
 | 
						|
 | 
						|
  if ( p )
 | 
						|
  { update_linepos(s, c);
 | 
						|
    p->charno++;
 | 
						|
  }
 | 
						|
  S__checkpasteeof(s,c);
 | 
						|
 | 
						|
  return c;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static inline int
 | 
						|
get_byte(IOSTREAM *s)
 | 
						|
{ int c = Snpgetc(s);
 | 
						|
 | 
						|
  if ( s->position )
 | 
						|
    s->position->byteno++;
 | 
						|
 | 
						|
  return c;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
put_byte(int c, IOSTREAM *s)
 | 
						|
{ c &= 0xff;
 | 
						|
 | 
						|
  if ( s->bufp < s->limitp )
 | 
						|
  { *s->bufp++ = c;
 | 
						|
  } else
 | 
						|
  { if ( S__flushbufc(c, s) < 0 )
 | 
						|
    { s->lastc = EOF;
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if ( s->position )
 | 
						|
    s->position->byteno++;
 | 
						|
 | 
						|
  return c;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Sputc(int c, IOSTREAM *s)
 | 
						|
{ c &= 0xff;
 | 
						|
 | 
						|
  if ( put_byte(c, s) < 0 )
 | 
						|
    return -1;
 | 
						|
 | 
						|
  s->lastc = c;
 | 
						|
 | 
						|
  if ( c == '\n' && (s->flags & SIO_LBUF) )
 | 
						|
  { if ( S__flushbuf(s) < 0 )
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  return S__updatefilepos(s, c);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Sfgetc(IOSTREAM *s)
 | 
						|
{ return Sgetc(s);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static inline void
 | 
						|
unget_byte(int c, IOSTREAM *s)
 | 
						|
{ IOPOS *p = s->position;
 | 
						|
 | 
						|
  *--s->bufp = c;
 | 
						|
  if ( p )
 | 
						|
  { p->charno--;			/* sz */
 | 
						|
    p->byteno--;
 | 
						|
    if ( c == '\n' )
 | 
						|
      p->lineno--;
 | 
						|
    s->flags |= SIO_NOLINEPOS;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Sungetc(int c, IOSTREAM *s)
 | 
						|
{
 | 
						|
  if ( s->bufp > s->unbuffer )
 | 
						|
  { unget_byte(c, s);
 | 
						|
 | 
						|
    return c;
 | 
						|
  }
 | 
						|
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
reperror(int c, IOSTREAM *s)
 | 
						|
{ if ( c >= 0 && (s->flags & (SIO_REPXML|SIO_REPPL)) )
 | 
						|
  { char buf[16];
 | 
						|
    const char *q;
 | 
						|
 | 
						|
    if ( (s->flags & SIO_REPPL) )
 | 
						|
    { if ( c <= 0xffff )
 | 
						|
	sprintf(buf, "\\u%04X", c);
 | 
						|
      else
 | 
						|
	sprintf(buf, "\\U%08X", c);
 | 
						|
    } else
 | 
						|
      sprintf(buf, "&#%d;", c);
 | 
						|
 | 
						|
    for(q = buf; *q; q++)
 | 
						|
    { if ( put_byte(*q, s) < 0 )
 | 
						|
	return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    return c;
 | 
						|
  }
 | 
						|
 | 
						|
  Sseterr(s, SIO_FERR|SIO_CLEARERR, "Encoding cannot represent character");
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
#if __ANDROID__
 | 
						|
//hack!!!!
 | 
						|
   char *Yap_AndroidBufp = NULL;
 | 
						|
   void( *Yap_DisplayWithJava)(int c);
 | 
						|
#endif
 | 
						|
 | 
						|
static int
 | 
						|
put_code(int c, IOSTREAM *s)
 | 
						|
{ switch(s->encoding)
 | 
						|
  { case ENC_OCTET:
 | 
						|
    case ENC_ISO_LATIN_1:
 | 
						|
      if ( c >= 256 )
 | 
						|
      { if ( reperror(c, s) < 0 )
 | 
						|
	  return -1;
 | 
						|
	break;
 | 
						|
      }
 | 
						|
    simple:
 | 
						|
      if ( put_byte(c, s) < 0 )
 | 
						|
	return -1;
 | 
						|
      break;
 | 
						|
    case ENC_ASCII:
 | 
						|
      if ( c >= 128 )
 | 
						|
      { if ( reperror(c, s) < 0 )
 | 
						|
	  return -1;
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      goto simple;
 | 
						|
    case ENC_ANSI:
 | 
						|
    { char b[PL_MB_LEN_MAX];
 | 
						|
      size_t n;
 | 
						|
 | 
						|
      if ( !s->mbstate )
 | 
						|
      { if ( !(s->mbstate = malloc(sizeof(*s->mbstate))) )
 | 
						|
	  return EOF;			/* out of memory */
 | 
						|
	memset(s->mbstate, 0, sizeof(*s->mbstate));
 | 
						|
      }
 | 
						|
 | 
						|
      if ( (n = wcrtomb(b, (wchar_t)c, s->mbstate)) == (size_t)-1 )
 | 
						|
      { if ( reperror(c, s) < 0 )
 | 
						|
	  return -1;
 | 
						|
      } else
 | 
						|
      { size_t i;
 | 
						|
 | 
						|
	for(i=0; i<n; i++)
 | 
						|
	{ if ( put_byte(b[i]&0xff, s) < 0 )
 | 
						|
	    return -1;
 | 
						|
	}
 | 
						|
      }
 | 
						|
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case ENC_UTF8:
 | 
						|
    { char buf[6];
 | 
						|
      char *p, *end;
 | 
						|
 | 
						|
      if ( c < 128 )
 | 
						|
	goto simple;
 | 
						|
 | 
						|
      end = utf8_put_char(buf, c);
 | 
						|
      for(p=buf; p<end; p++)
 | 
						|
      { if ( put_byte(*p&0xff, s) < 0 )
 | 
						|
	  return -1;
 | 
						|
      }
 | 
						|
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case ENC_UNICODE_BE:
 | 
						|
      if ( put_byte(c>>8, s) < 0 )
 | 
						|
	return -1;
 | 
						|
      if ( put_byte(c&0xff, s) < 0 )
 | 
						|
	return -1;
 | 
						|
      break;
 | 
						|
    case ENC_UNICODE_LE:
 | 
						|
      if ( put_byte(c&0xff, s) < 0 )
 | 
						|
	return -1;
 | 
						|
      if ( put_byte(c>>8, s) < 0 )
 | 
						|
	return -1;
 | 
						|
      break;
 | 
						|
    case ENC_WCHAR:
 | 
						|
    { pl_wchar_t chr = c;
 | 
						|
      unsigned char *q = (unsigned char *)&chr;
 | 
						|
      unsigned char *e = &q[sizeof(pl_wchar_t)];
 | 
						|
 | 
						|
#if __WINDOWS__
 | 
						|
  if (s == Soutput || s == Serror ) {
 | 
						|
    if (!(*s->functions->write)(s->handle, (char *)q, sizeof(wchar_t)))
 | 
						|
      return -1;
 | 
						|
    break;
 | 
						|
   }
 | 
						|
#endif
 | 
						|
      while(q<e)
 | 
						|
      { if ( put_byte(*q++, s) < 0 )
 | 
						|
	  return -1;
 | 
						|
      }
 | 
						|
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case ENC_UNKNOWN:
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  s->lastc = c;
 | 
						|
#if __ANDROID__
 | 
						|
 | 
						|
  if (Yap_AndroidBufp && (s == Soutput || s == Serror) ) {
 | 
						|
     (Yap_DisplayWithJava)(c);
 | 
						|
   }
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
  if ( (c == '\n' && (s->flags & SIO_LBUF) ) )
 | 
						|
  { if ( S__flushbuf(s) < 0 )
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  return S__updatefilepos(s, c);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Sputcode(int c, IOSTREAM *s)
 | 
						|
{ if ( c < 0 )
 | 
						|
    return reperror(c, s);
 | 
						|
 | 
						|
  if ( s->tee && s->tee->magic == SIO_MAGIC )
 | 
						|
    Sputcode(c, s->tee);
 | 
						|
 | 
						|
  if ( c == '\n' &&
 | 
						|
       (s->flags&SIO_TEXT) &&
 | 
						|
       s->newline == SIO_NL_DOS &&
 | 
						|
       s->lastc != '\r' )
 | 
						|
  { if ( put_code('\r', s) < 0 )
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
  return put_code(c, s);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Scanrepresent(int c, IOSTREAM *s)
 | 
						|
{ switch(s->encoding)
 | 
						|
  { case ENC_OCTET:
 | 
						|
    case ENC_ISO_LATIN_1:
 | 
						|
      if ( c <= 0xff )
 | 
						|
	return 0;
 | 
						|
      return -1;
 | 
						|
    case ENC_ASCII:
 | 
						|
      if ( c < 0x7f )
 | 
						|
	return 0;
 | 
						|
      return -1;
 | 
						|
    case ENC_ANSI:
 | 
						|
    { mbstate_t state;
 | 
						|
      char b[PL_MB_LEN_MAX];
 | 
						|
 | 
						|
      memset(&state, 0, sizeof(state));
 | 
						|
      if ( wcrtomb(b, (wchar_t)c, &state) != (size_t)-1 )
 | 
						|
	return 0;
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
    case ENC_WCHAR:
 | 
						|
      if ( sizeof(wchar_t) > 2 )
 | 
						|
	return 0;
 | 
						|
    /*FALLTHROUGH*/
 | 
						|
    case ENC_UNICODE_BE:
 | 
						|
    case ENC_UNICODE_LE:
 | 
						|
      if ( c <= 0xffff )
 | 
						|
	return 0;
 | 
						|
      return -1;
 | 
						|
    case ENC_UTF8:
 | 
						|
      return 0;
 | 
						|
    default:
 | 
						|
      assert(0);
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Sgetcode(IOSTREAM *s)
 | 
						|
{ int c;
 | 
						|
 | 
						|
retry:
 | 
						|
  switch(s->encoding)
 | 
						|
  { case ENC_OCTET:
 | 
						|
    case ENC_ISO_LATIN_1:
 | 
						|
      c = get_byte(s);
 | 
						|
      break;
 | 
						|
    case ENC_ASCII:
 | 
						|
    { c = get_byte(s);
 | 
						|
      if ( c > 128 )
 | 
						|
	Sseterr(s, SIO_WARN, "non-ASCII character");
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case ENC_ANSI:
 | 
						|
    { char b[1];
 | 
						|
      size_t rc, n = 0;
 | 
						|
      wchar_t wc;
 | 
						|
 | 
						|
      if ( !s->mbstate )
 | 
						|
      { if ( !(s->mbstate = malloc(sizeof(*s->mbstate))) )
 | 
						|
	  return EOF;			/* out of memory */
 | 
						|
	memset(s->mbstate, 0, sizeof(*s->mbstate));
 | 
						|
      }
 | 
						|
 | 
						|
      for(;;)
 | 
						|
      { if ( (c = get_byte(s)) == EOF )
 | 
						|
	{ if ( n == 0 )
 | 
						|
	  { goto out;
 | 
						|
	  } else
 | 
						|
	  { Sseterr(s, SIO_WARN, "EOF in multibyte Sequence");
 | 
						|
	    goto mberr;
 | 
						|
	  }
 | 
						|
	}
 | 
						|
	b[0] = c;
 | 
						|
 | 
						|
	if ( (rc=mbrtowc(&wc, b, 1, s->mbstate)) == 1 )
 | 
						|
	{ c = wc;
 | 
						|
	  goto out;
 | 
						|
	} else if ( rc == (size_t)-1 )
 | 
						|
	{ Sseterr(s, SIO_WARN, "Illegal multibyte Sequence");
 | 
						|
	  goto mberr;
 | 
						|
	}				/* else -2: incomplete */
 | 
						|
      }
 | 
						|
 | 
						|
    mberr:
 | 
						|
      c = UTF8_MALFORMED_REPLACEMENT;
 | 
						|
      goto out;
 | 
						|
    }
 | 
						|
    case ENC_UTF8:
 | 
						|
    { c = get_byte(s);
 | 
						|
      if ( c == EOF )
 | 
						|
	break;
 | 
						|
 | 
						|
      if ( c & 0x80 )
 | 
						|
      { int extra = UTF8_FBN(c);
 | 
						|
	int code;
 | 
						|
 | 
						|
	if ( extra < 0 )
 | 
						|
	{ Sseterr(s, SIO_WARN, "Illegal UTF-8 start");
 | 
						|
	  c = UTF8_MALFORMED_REPLACEMENT;
 | 
						|
	  goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	code = UTF8_FBV(c,extra);
 | 
						|
	for( ; extra > 0; extra-- )
 | 
						|
	{ int c2 = get_byte(s);
 | 
						|
 | 
						|
	  if ( !ISUTF8_CB(c2) )
 | 
						|
	  { Sseterr(s, SIO_WARN, "Illegal UTF-8 continuation");
 | 
						|
	    c = UTF8_MALFORMED_REPLACEMENT;
 | 
						|
	    Sungetc(c2, s);
 | 
						|
	    goto out;
 | 
						|
	  }
 | 
						|
	  code = (code<<6)+(c2&0x3f);
 | 
						|
	}
 | 
						|
	c = code;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case ENC_UNICODE_BE:
 | 
						|
    case ENC_UNICODE_LE:
 | 
						|
    { int c1, c2;
 | 
						|
 | 
						|
      c1 = get_byte(s);
 | 
						|
      if ( c1 == EOF )
 | 
						|
      { c = -1;
 | 
						|
	goto out;
 | 
						|
      }
 | 
						|
      c2 = get_byte(s);
 | 
						|
 | 
						|
      if ( c2 == EOF )
 | 
						|
      { Sseterr(s, SIO_WARN, "EOF in unicode character");
 | 
						|
	c = UTF8_MALFORMED_REPLACEMENT;
 | 
						|
      } else
 | 
						|
      { if ( s->encoding == ENC_UNICODE_BE )
 | 
						|
	  c = (c1<<8)+c2;
 | 
						|
	else
 | 
						|
	  c = (c2<<8)+c1;
 | 
						|
      }
 | 
						|
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case ENC_WCHAR:
 | 
						|
    { pl_wchar_t chr;
 | 
						|
      char *p = (char*)&chr;
 | 
						|
      size_t n;
 | 
						|
 | 
						|
      for(n=0; n<sizeof(pl_wchar_t); n++)
 | 
						|
      { int c1 = get_byte(s);
 | 
						|
 | 
						|
	if ( c1 == EOF )
 | 
						|
	{ if ( n == 0 )
 | 
						|
	  { c = -1;
 | 
						|
	    goto out;
 | 
						|
	  } else
 | 
						|
	  { Sseterr(s, SIO_WARN, "EOF in UCS character");
 | 
						|
	    c = UTF8_MALFORMED_REPLACEMENT;
 | 
						|
	    goto out;
 | 
						|
	  }
 | 
						|
	}
 | 
						|
 | 
						|
	*p++ = c1;
 | 
						|
      }
 | 
						|
 | 
						|
      c = chr;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      assert(0);
 | 
						|
      c = -1;
 | 
						|
  }
 | 
						|
 | 
						|
out:
 | 
						|
  if ( c == '\r' && (s->flags&SIO_TEXT) )
 | 
						|
  { switch(s->newline)
 | 
						|
    { case SIO_NL_DETECT:
 | 
						|
	s->newline = SIO_NL_DOS;
 | 
						|
        /*FALLTHROUGH*/
 | 
						|
      case SIO_NL_DOS:
 | 
						|
	goto retry;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if ( s->tee && s->tee->magic == SIO_MAGIC && c != -1 )
 | 
						|
    Sputcode(c, s->tee);
 | 
						|
 | 
						|
  return S__updatefilepos(s, c);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
peek needs to keep track of the actual bytes processed because not doing
 | 
						|
so might lead to an  incorrect  byte-count   in  the  position term. The
 | 
						|
simplest example is that when  looking   at  \r\n in Windows, get_code/1
 | 
						|
returns \n, but it returns the same for a single \n.
 | 
						|
 | 
						|
Often, we could keep track of bufp and reset this, but we must deal with
 | 
						|
the case where we fetch a new buffer. In this case, we must copy the few
 | 
						|
remaining bytes to the `unbuffer' area. If SIO_USERBUF is set, we do not
 | 
						|
have this spare buffer space. This  is   used  for reading from strings,
 | 
						|
which cannot fetch a new buffer anyway.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
int
 | 
						|
Speekcode(IOSTREAM *s)
 | 
						|
{ int c;
 | 
						|
  char *start;
 | 
						|
  size_t safe = (size_t)-1;
 | 
						|
  if ( !s->buffer )
 | 
						|
  { if ( (s->flags & SIO_NBUF) )
 | 
						|
    { errno = EINVAL;
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
    if ( S__setbuf(s, NULL, 0) == (size_t)-1 )
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  if ( (s->flags & SIO_FEOF) )
 | 
						|
    return -1;
 | 
						|
 | 
						|
  if ( s->bufp + UNDO_SIZE > s->limitp && !(s->flags&SIO_USERBUF) )
 | 
						|
  { safe = s->limitp - s->bufp;
 | 
						|
    memcpy(s->buffer-safe, s->bufp, safe);
 | 
						|
  }
 | 
						|
 | 
						|
  start = s->bufp;
 | 
						|
  if ( s->position )
 | 
						|
  { IOPOS *psave = s->position;
 | 
						|
    s->position = NULL;
 | 
						|
    c = Sgetcode(s);
 | 
						|
    s->position = psave;
 | 
						|
  } else
 | 
						|
  { c = Sgetcode(s);
 | 
						|
  }
 | 
						|
  if ( Sferror(s) )
 | 
						|
    return -1;
 | 
						|
 | 
						|
  s->flags &= ~(SIO_FEOF|SIO_FEOF2);
 | 
						|
 | 
						|
  if ( s->bufp > start )
 | 
						|
  { s->bufp = start;
 | 
						|
  } else if ( c != -1 )
 | 
						|
  { assert(safe != (size_t)-1);
 | 
						|
    s->bufp = s->buffer-safe;
 | 
						|
  }
 | 
						|
 | 
						|
  return c;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	    PUTW/GETW		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
int
 | 
						|
Sputw(int w, IOSTREAM *s)
 | 
						|
{ unsigned char *q = (unsigned char *)&w;
 | 
						|
  unsigned int n;
 | 
						|
 | 
						|
  for(n=0; n<sizeof(w); n++)
 | 
						|
  { if ( Sputc(*q++, s) < 0 )
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  return w;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Sgetw(IOSTREAM *s)
 | 
						|
{ int w;
 | 
						|
  unsigned char *q = (unsigned char *)&w;
 | 
						|
  unsigned int n;
 | 
						|
 | 
						|
  for(n=0; n<sizeof(w); n++)
 | 
						|
  { int c;
 | 
						|
 | 
						|
    if ( (c = Sgetc(s)) < 0 )
 | 
						|
      return -1;
 | 
						|
    *q++ = c & 0xff;
 | 
						|
  }
 | 
						|
 | 
						|
  return w;
 | 
						|
}
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	    FREAD/FWRITE	*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
size_t
 | 
						|
Sfread(void *data, size_t size, size_t elms, IOSTREAM *s)
 | 
						|
{ size_t chars = size * elms;
 | 
						|
  char *buf = data;
 | 
						|
 | 
						|
  if ( s->position )
 | 
						|
  { for( ; chars > 0; chars-- )
 | 
						|
    { int c;
 | 
						|
 | 
						|
      if ( (c = Sgetc(s)) == EOF )
 | 
						|
	break;
 | 
						|
 | 
						|
      *buf++ = c & 0xff;
 | 
						|
    }
 | 
						|
  } else
 | 
						|
  { while(chars > 0)
 | 
						|
    { int c;
 | 
						|
 | 
						|
      if ( s->bufp < s->limitp )
 | 
						|
      { size_t avail = s->limitp - s->bufp;
 | 
						|
 | 
						|
	if ( chars <= avail )
 | 
						|
	{ memcpy(buf, s->bufp, chars);
 | 
						|
	  s->bufp += chars;
 | 
						|
	  return elms;
 | 
						|
	} else
 | 
						|
	{ memcpy(buf, s->bufp, avail);
 | 
						|
	  chars -= avail;
 | 
						|
	  buf += avail;
 | 
						|
	  s->bufp += avail;
 | 
						|
	}
 | 
						|
      }
 | 
						|
 | 
						|
      if ( (c = S__fillbuf(s)) == EOF )
 | 
						|
	break;
 | 
						|
 | 
						|
      *buf++ = c & 0xff;
 | 
						|
      chars--;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return (size*elms - chars)/size;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
size_t
 | 
						|
Sfwrite(const void *data, size_t size, size_t elms, IOSTREAM *s)
 | 
						|
{ size_t chars = size * elms;
 | 
						|
  const char *buf = data;
 | 
						|
 | 
						|
  for( ; chars > 0; chars-- )
 | 
						|
  { if ( Sputc(*buf++, s) < 0 )
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  return (size*elms - chars)/size;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	       PENDING		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
ssize_t
 | 
						|
Sread_pending(IOSTREAM *s, char *buf, size_t limit, int flags)
 | 
						|
{ int done = 0;
 | 
						|
  size_t n;
 | 
						|
 | 
						|
  if ( s->bufp >= s->limitp && (flags & SIO_RP_BLOCK) )
 | 
						|
  { int c = S__fillbuf(s);
 | 
						|
 | 
						|
    if ( c < 0 )
 | 
						|
    { if ( (s->flags & SIO_FEOF) )
 | 
						|
	return 0;
 | 
						|
      return c;
 | 
						|
    }
 | 
						|
 | 
						|
    buf[0] = c;
 | 
						|
    limit--;
 | 
						|
    done = 1;
 | 
						|
  }
 | 
						|
 | 
						|
  n = s->limitp - s->bufp;
 | 
						|
  if ( n > limit )
 | 
						|
    n = limit;
 | 
						|
  memcpy(&buf[done], s->bufp, n);
 | 
						|
  s->bufp += n;
 | 
						|
 | 
						|
  return done+n;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *               BOM		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Check the stream for a BOM (Byte Order Marker).  If present (and known),
 | 
						|
update the stream encoding.  Return value is one of
 | 
						|
 | 
						|
	-1:	error (check errno)
 | 
						|
	 0:	ok.  If BOM, SIO_BOM is added to flags
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
typedef struct
 | 
						|
{ IOENC encoding;
 | 
						|
  unsigned int bomlen;
 | 
						|
  const char *bom;
 | 
						|
} bomdef;
 | 
						|
 | 
						|
static const bomdef bomdefs[] =
 | 
						|
{ { ENC_UTF8,       3, "\357\273\277" }, /* 0xef, 0xbb, 0xbb */
 | 
						|
  { ENC_UNICODE_BE, 2, "\376\377" },	 /* 0xfe, oxff */
 | 
						|
  { ENC_UNICODE_LE, 2, "\377\376" },	 /* 0xff, oxfe */
 | 
						|
  { ENC_UNKNOWN,    0, NULL }
 | 
						|
};
 | 
						|
 | 
						|
int
 | 
						|
ScheckBOM(IOSTREAM *s)
 | 
						|
{ if ( (s->flags & SIO_NBUF) )
 | 
						|
  { errno = EINVAL;
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  for(;;)
 | 
						|
  { size_t avail = s->limitp - s->bufp;
 | 
						|
    const bomdef *bd;
 | 
						|
 | 
						|
    for(bd=bomdefs; bd->bomlen; bd++)
 | 
						|
    { if ( avail >= bd->bomlen && memcmp(s->bufp, bd->bom, bd->bomlen) == 0 )
 | 
						|
      { s->encoding = bd->encoding;
 | 
						|
	s->bufp += bd->bomlen;
 | 
						|
	s->flags |= SIO_BOM;
 | 
						|
	if ( s->position )
 | 
						|
	  s->position->byteno += bd->bomlen;
 | 
						|
	return 0;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if ( avail >= 4 )			/* longest BOM */
 | 
						|
      return 0;
 | 
						|
 | 
						|
    if ( S__fillbuf(s) == -1 )
 | 
						|
      return 0;				/* empty stream */
 | 
						|
    s->bufp--;
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
SwriteBOM(IOSTREAM *s)
 | 
						|
{ switch(s->encoding)
 | 
						|
  { case ENC_UTF8:
 | 
						|
    case ENC_UNICODE_LE:
 | 
						|
    case ENC_UNICODE_BE:
 | 
						|
    { if ( Sputcode(0xfeff, s) != -1 )
 | 
						|
      { s->flags |= SIO_BOM;
 | 
						|
 | 
						|
	return 0;
 | 
						|
      }
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      return 0;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *               FLAGS		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
int
 | 
						|
Sfeof(IOSTREAM *s)
 | 
						|
{ if ( s->flags & SIO_FEOF )
 | 
						|
    return TRUE;
 | 
						|
 | 
						|
  if ( s->bufp < s->limitp )
 | 
						|
    return FALSE;
 | 
						|
 | 
						|
  if ( s->flags & SIO_NBUF )
 | 
						|
  { errno = EINVAL;
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  if ( S__fillbuf(s) == -1 )
 | 
						|
    return TRUE;
 | 
						|
 | 
						|
  s->bufp--;
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
S__seterror(IOSTREAM *s)
 | 
						|
{ s->io_errno = errno;
 | 
						|
 | 
						|
  if ( !(s->flags&SIO_CLOSING) &&	/* s->handle is already invalid */
 | 
						|
       s->functions->control )
 | 
						|
  { char *msg;
 | 
						|
 | 
						|
    if ( (*s->functions->control)(s->handle,
 | 
						|
				  SIO_LASTERROR,
 | 
						|
				  (void *)&msg) == 0 )
 | 
						|
    { Sseterr(s, SIO_FERR, msg);
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  s->flags |= SIO_FERR;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Sferror(IOSTREAM *s)
 | 
						|
{ return (s->flags & SIO_FERR) != 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Sfpasteof(IOSTREAM *s)
 | 
						|
{ return (s->flags & (SIO_FEOF2ERR|SIO_FEOF2)) == (SIO_FEOF2ERR|SIO_FEOF2);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
Sclearerr(IOSTREAM *s)
 | 
						|
{ s->flags &= ~(SIO_FEOF|SIO_WARN|SIO_FERR|SIO_FEOF2|SIO_TIMEOUT|SIO_CLEARERR);
 | 
						|
  s->io_errno = 0;
 | 
						|
  Sseterr(s, 0, NULL);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
Sseterr(IOSTREAM *s, int flag, const char *message)
 | 
						|
{ if ( s->message )
 | 
						|
  { free(s->message);
 | 
						|
    s->message = NULL;
 | 
						|
    s->flags &= ~SIO_CLEARERR;
 | 
						|
  }
 | 
						|
  if ( message )
 | 
						|
  { s->flags |= flag;
 | 
						|
    s->message = strdup(message);
 | 
						|
  } else
 | 
						|
  { s->flags &= ~flag;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
Sset_exception(IOSTREAM *s, term_t ex)
 | 
						|
{ s->exception = PL_record(ex);
 | 
						|
  s->flags |= SIO_FERR;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Set the encoding of a stream. The enc   argument is the new encoding. If
 | 
						|
old is not NULL, the old encoding is written to the given location.
 | 
						|
 | 
						|
Please note that not all code changing  the encoding call Ssetenc at the
 | 
						|
moment.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
int
 | 
						|
Ssetenc(IOSTREAM *s, IOENC enc, IOENC *old)
 | 
						|
{ if ( old )
 | 
						|
    *old = s->encoding;
 | 
						|
  if ( enc == s->encoding )
 | 
						|
    return 0;
 | 
						|
 | 
						|
  if ( s->functions->control )
 | 
						|
  { if ( (*s->functions->control)(s->handle,
 | 
						|
				  SIO_SETENCODING,
 | 
						|
				  (void *)&enc) != 0 )
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  s->encoding = enc;
 | 
						|
  if ( enc == ENC_OCTET )
 | 
						|
    s->flags &= ~SIO_TEXT;
 | 
						|
  else
 | 
						|
    s->flags |= SIO_TEXT;
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef O_LOCALE
 | 
						|
int
 | 
						|
Ssetlocale(IOSTREAM *s, PL_locale *new, PL_locale **old)
 | 
						|
{ PL_locale *lo = s->locale;
 | 
						|
 | 
						|
  if ( old )
 | 
						|
    *old = s->locale;
 | 
						|
  if ( new == s->locale )
 | 
						|
    return 0;
 | 
						|
 | 
						|
  if ( new )
 | 
						|
    s->locale = acquireLocale(new);
 | 
						|
  else
 | 
						|
    s->locale = NULL;
 | 
						|
  if ( lo )
 | 
						|
    releaseLocale(lo);
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	      FLUSH		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
int
 | 
						|
Sflush(IOSTREAM *s)
 | 
						|
{ if ( s->buffer && (s->flags & SIO_OUTPUT) )
 | 
						|
  { if ( S__flushbuf(s) < 0 )
 | 
						|
      return -1;
 | 
						|
    if ( s->functions->control &&
 | 
						|
	 (*s->functions->control)(s->handle, SIO_FLUSHOUTPUT, NULL) < 0 )
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	      SEEK		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
int
 | 
						|
Sunit_size(IOSTREAM *s)
 | 
						|
{ switch(s->encoding)
 | 
						|
  { case ENC_UNKNOWN:
 | 
						|
    case ENC_OCTET:
 | 
						|
    case ENC_ASCII:
 | 
						|
    case ENC_ISO_LATIN_1:
 | 
						|
    case ENC_ANSI:
 | 
						|
    case ENC_UTF8:
 | 
						|
      return 1;
 | 
						|
    case ENC_UNICODE_BE:
 | 
						|
    case ENC_UNICODE_LE:
 | 
						|
      return 2;
 | 
						|
    case ENC_WCHAR:
 | 
						|
      return sizeof(wchar_t);
 | 
						|
    default:
 | 
						|
      assert(0);
 | 
						|
      return 1;				/* not reached */
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Return the size of the underlying data object.  Should be optimized;
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
int64_t
 | 
						|
Ssize(IOSTREAM *s)
 | 
						|
{ if ( s->functions->control )
 | 
						|
  { int64_t size;
 | 
						|
 | 
						|
    if ( (*s->functions->control)(s->handle, SIO_GETSIZE, (void *)&size) == 0 )
 | 
						|
      return size;
 | 
						|
  }
 | 
						|
  if ( s->functions->seek )
 | 
						|
  { int64_t here = Stell64(s);
 | 
						|
    int64_t end;
 | 
						|
 | 
						|
    if ( Sseek64(s, 0, SIO_SEEK_END) == 0 )
 | 
						|
      end = Stell64(s);
 | 
						|
    else
 | 
						|
      end = -1;
 | 
						|
    Sseek64(s, here, SIO_SEEK_SET);
 | 
						|
 | 
						|
    return end;
 | 
						|
  }
 | 
						|
 | 
						|
  errno = ESPIPE;
 | 
						|
  S__seterror(s);
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Sseek64(IOSTREAM *s, int64_t pos, int whence)
 | 
						|
 | 
						|
Re-position the stream to byte-no 'pos'.
 | 
						|
 | 
						|
Maybe we should optimise this to become block-aligned?  Or can we leave
 | 
						|
this to read/write?
 | 
						|
 | 
						|
The first part checks whether  repositioning   the  read  pointer in the
 | 
						|
buffer suffices to achieve the seek.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
int
 | 
						|
Sseek64(IOSTREAM *s, int64_t pos, int whence)
 | 
						|
{ if ( (s->flags & SIO_INPUT) && s->limitp > s->buffer ) /* something there */
 | 
						|
  { int64_t now = Stell64(s);
 | 
						|
 | 
						|
    if ( now != -1 )
 | 
						|
    { int64_t newpos;
 | 
						|
      char *nbufp = (char *)-1;
 | 
						|
 | 
						|
      if ( whence == SIO_SEEK_CUR )
 | 
						|
      { nbufp = s->bufp + pos;
 | 
						|
	newpos = now + pos;
 | 
						|
      } else if ( whence == SIO_SEEK_SET )
 | 
						|
      { nbufp = s->bufp + (pos - now);
 | 
						|
	newpos = pos;
 | 
						|
      } else
 | 
						|
	newpos = -1;			/* should not happen */
 | 
						|
 | 
						|
      if ( nbufp >= s->buffer && nbufp < s->limitp )
 | 
						|
      { s->bufp = nbufp;
 | 
						|
 | 
						|
	pos = newpos;
 | 
						|
	goto update;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if ( !s->functions->seek && !s->functions->seek64 )
 | 
						|
  { errno = ESPIPE;
 | 
						|
    S__seterror(s);
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  Sflush(s);
 | 
						|
 | 
						|
  s->bufp   = s->buffer;
 | 
						|
  if ( (s->flags & SIO_INPUT) )
 | 
						|
    s->limitp = s->buffer;
 | 
						|
 | 
						|
  if ( whence == SIO_SEEK_CUR )
 | 
						|
  { pos += Stell64(s);
 | 
						|
    whence = SIO_SEEK_SET;
 | 
						|
  }
 | 
						|
 | 
						|
  if ( s->functions->seek64 )
 | 
						|
    pos = (*s->functions->seek64)(s->handle, pos, whence);
 | 
						|
  else if ( pos <= LONG_MAX )
 | 
						|
    pos = (*s->functions->seek)(s->handle, (long)pos, whence);
 | 
						|
  else
 | 
						|
  { errno = EINVAL;
 | 
						|
    S__seterror(s);
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  if ( pos < 0 )
 | 
						|
  { S__seterror(s);
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
update:
 | 
						|
  s->flags &= ~(SIO_FEOF|SIO_FEOF2);	/* not on eof of file anymore */
 | 
						|
 | 
						|
  if ( s->position )
 | 
						|
  { s->flags |= (SIO_NOLINENO|SIO_NOLINEPOS); /* no update this */
 | 
						|
    s->position->byteno = pos;
 | 
						|
    s->position->charno = pos/Sunit_size(s); /* compatibility */
 | 
						|
  }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Sseek(IOSTREAM *s, long pos, int whence)
 | 
						|
{ return Sseek64(s, (int64_t)pos, whence);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/* Stell64(IOSTREAM *s) returns the current position in the file in
 | 
						|
   bytes
 | 
						|
*/
 | 
						|
 | 
						|
int64_t
 | 
						|
Stell64(IOSTREAM *s)
 | 
						|
{ if ( s->position )
 | 
						|
  { return s->position->byteno;
 | 
						|
  } else if ( s->functions->seek || s->functions->seek64 )
 | 
						|
  { int64_t pos;
 | 
						|
 | 
						|
    if ( s->functions->seek64 )
 | 
						|
      pos = (*s->functions->seek64)(s->handle, 0L, SIO_SEEK_CUR);
 | 
						|
    else
 | 
						|
      pos = (*s->functions->seek)(s->handle, 0L, SIO_SEEK_CUR);
 | 
						|
 | 
						|
    if ( s->buffer )			/* open */
 | 
						|
    { int64_t off = s->bufp - s->buffer;
 | 
						|
 | 
						|
      if ( s->flags & SIO_INPUT )
 | 
						|
	off -= s->limitp - s->buffer;
 | 
						|
 | 
						|
      pos += off;
 | 
						|
    }
 | 
						|
 | 
						|
    return pos;
 | 
						|
  } else
 | 
						|
  { errno = EINVAL;
 | 
						|
    S__seterror(s);
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
long
 | 
						|
Stell(IOSTREAM *s)
 | 
						|
{ int64_t pos = Stell64(s);
 | 
						|
 | 
						|
  if ( pos == -1 )
 | 
						|
    return -1;
 | 
						|
  if ( pos <= LONG_MAX )
 | 
						|
    return (long) pos;
 | 
						|
 | 
						|
  errno = EINVAL;
 | 
						|
  S__seterror(s);
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	      CLOSE		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
void
 | 
						|
unallocStream(IOSTREAM *s)
 | 
						|
{
 | 
						|
#ifdef O_PLMT
 | 
						|
  if ( s->mutex )
 | 
						|
  { recursiveMutexDelete(s->mutex);
 | 
						|
    PL_free(s->mutex);
 | 
						|
    s->mutex = NULL;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  if ( !(s->flags & SIO_STATIC) )
 | 
						|
    PL_free(s);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
(*) Sclose() can be called recursively. For example if an XPCE object is
 | 
						|
only referenced from an open stream,  the close-function will delete the
 | 
						|
object, which in turn calls the  ->unlink   which  may wish to close the
 | 
						|
associated stream.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
#if __ANDROID__
 | 
						|
static int
 | 
						|
Sclose_asset(void *handle);
 | 
						|
#endif
 | 
						|
 | 
						|
int
 | 
						|
Sclose(IOSTREAM *s)
 | 
						|
{ int rval = 0;
 | 
						|
 | 
						|
  if ( s->magic != SIO_MAGIC )		/* already closed!? */
 | 
						|
  { s->io_errno = errno = EINVAL;
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  if ( (s->flags&SIO_CLOSING) )		/* recursive (*) */
 | 
						|
    return rval;
 | 
						|
 | 
						|
  if ( s->upstream )
 | 
						|
  { Sseterr(s, SIO_FERR, "Locked by upstream filter");
 | 
						|
    reportStreamError(s);
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  SLOCK(s);
 | 
						|
  s->flags |= SIO_CLOSING;
 | 
						|
  rval = S__removebuf(s);
 | 
						|
  if ( s->mbstate )
 | 
						|
    free(s->mbstate);
 | 
						|
 | 
						|
#ifdef __WINDOWS__
 | 
						|
  if ( (s->flags & SIO_ADVLOCK) )
 | 
						|
  { OVERLAPPED ov;
 | 
						|
    HANDLE h = (HANDLE)_get_osfhandle((int)((uintptr_t)s->handle));
 | 
						|
 | 
						|
    memset(&ov, 0, sizeof(ov));
 | 
						|
    UnlockFileEx(h, 0, 0, 0xffffffff, &ov);
 | 
						|
    s->flags &= ~SIO_ADVLOCK;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
  if ( s->functions->close && (*s->functions->close)(s->handle) < 0 )
 | 
						|
  { S__seterror(s);
 | 
						|
    rval = -1;
 | 
						|
  }
 | 
						|
 | 
						|
#if __ANDROID__
 | 
						|
 | 
						|
  if (s->functions->close == Sclose_asset) {
 | 
						|
#endif
 | 
						|
  while(s->locks > 0)			/* remove buffer-locks */
 | 
						|
  { int rc = Sunlock(s);
 | 
						|
 | 
						|
    if ( rval == 0 )
 | 
						|
      rval = rc;
 | 
						|
  }
 | 
						|
#if __ANDROID__
 | 
						|
  }
 | 
						|
#endif
 | 
						|
  if ( rval < 0 )
 | 
						|
    reportStreamError(s);
 | 
						|
  run_close_hooks(s);			/* deletes Prolog registration */
 | 
						|
  s->magic = SIO_CMAGIC;
 | 
						|
  SUNLOCK(s);
 | 
						|
 | 
						|
  if ( s->message )
 | 
						|
    free(s->message);
 | 
						|
#if O_LOCALE
 | 
						|
  if ( s->locale )
 | 
						|
    releaseLocale(s->locale);
 | 
						|
#endif
 | 
						|
  if ( s->references == 0 )
 | 
						|
    unallocStream(s);
 | 
						|
  else
 | 
						|
    s->erased = TRUE;
 | 
						|
 | 
						|
  return rval;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	     STRING I/O		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
char *
 | 
						|
Sfgets(char *buf, int n, IOSTREAM *s)
 | 
						|
{ char *q = buf;
 | 
						|
 | 
						|
  while( n-- > 0 )
 | 
						|
  { int c = Sgetc(s);
 | 
						|
 | 
						|
    if ( c == EOF )
 | 
						|
    { *q = '\0';
 | 
						|
      if ( q == buf )
 | 
						|
	buf = NULL;
 | 
						|
      goto out;
 | 
						|
    } else
 | 
						|
    { *q++ = c;
 | 
						|
      if ( c == '\n' )
 | 
						|
      { if ( n > 0 )
 | 
						|
	  *q = '\0';
 | 
						|
	goto out;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
out:
 | 
						|
  return buf;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
char *
 | 
						|
Sgets(char *buf)
 | 
						|
{ char *s = Sfgets(buf, Slinesize, Sinput);
 | 
						|
  char *q;
 | 
						|
 | 
						|
  if ( s )				/* delete trailing \n */
 | 
						|
  { q = &s[strlen(s)];
 | 
						|
    if ( q > s && q[-1] == '\n' )
 | 
						|
      *--q = '\0';
 | 
						|
  }
 | 
						|
 | 
						|
  return s;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Sfputs(const char *q, IOSTREAM *s)
 | 
						|
{ for( ; *q; q++)
 | 
						|
  { if ( Sputcode(*q&0xff, s) < 0 )
 | 
						|
      return EOF;
 | 
						|
  }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Sputs(const char *q)
 | 
						|
{ return Sfputs(q, Soutput);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	       PRINTF		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
int
 | 
						|
Sfprintf(IOSTREAM *s, const char *fm, ...)
 | 
						|
{ va_list args;
 | 
						|
  int rval;
 | 
						|
 | 
						|
  va_start(args, fm);
 | 
						|
  rval = Svfprintf(s, fm, args);
 | 
						|
  va_end(args);
 | 
						|
 | 
						|
  return rval;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Sprintf(const char *fm, ...)
 | 
						|
{ va_list args;
 | 
						|
  int rval;
 | 
						|
 | 
						|
  va_start(args, fm);
 | 
						|
  rval = Svfprintf(Soutput, fm, args);
 | 
						|
  va_end(args);
 | 
						|
 | 
						|
  return rval;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Svprintf(const char *fm, va_list args)
 | 
						|
{ return Svfprintf(Soutput, fm, args);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#define NEXTCHR(s, c)				\
 | 
						|
	switch (enc)				\
 | 
						|
	{ case ENC_ANSI:			\
 | 
						|
	    c = *(s)++; c &= 0xff;		\
 | 
						|
	    break;				\
 | 
						|
	  case ENC_UTF8:			\
 | 
						|
	    (s) = utf8_get_char((s), &(c));	\
 | 
						|
	    break;				\
 | 
						|
	  case ENC_WCHAR:			\
 | 
						|
	  { wchar_t *_w = (wchar_t*)(s);	\
 | 
						|
	    c = *_w++;				\
 | 
						|
	    (s) = (char*)_w;			\
 | 
						|
	    break;				\
 | 
						|
	  }					\
 | 
						|
	  default:				\
 | 
						|
            c = '\0';				\
 | 
						|
	    break;				\
 | 
						|
	}
 | 
						|
 | 
						|
#define OUTCHR(s, c)	do { printed++; \
 | 
						|
			     if ( Sputcode((c), (s)) < 0 ) goto error; \
 | 
						|
			   } while(0)
 | 
						|
#define valdigit(c)	((c) - '0')
 | 
						|
#define A_LEFT	0			/* left-aligned field */
 | 
						|
#define A_RIGHT 1			/* right-aligned field */
 | 
						|
 | 
						|
#define SNPRINTF3(fm, a1) \
 | 
						|
	{ size_t __r; \
 | 
						|
	  assert(fs == fbuf); \
 | 
						|
	  __r = snprintf(fs, sizeof(fbuf), fm, a1); \
 | 
						|
	  if ( __r >= sizeof(fbuf) ) \
 | 
						|
	  { if ( (fs_malloced = fs = malloc(__r+1)) == NULL ) goto error; \
 | 
						|
	    __r = snprintf(fs, __r+1, fm, a1); \
 | 
						|
	  } \
 | 
						|
	  fe = fs+__r; \
 | 
						|
	}
 | 
						|
#define SNPRINTF4(fm, a1, a2) \
 | 
						|
	{ size_t __r; \
 | 
						|
	  assert(fs == fbuf); \
 | 
						|
	  __r = snprintf(fs, sizeof(fbuf), fm, a1, a2); \
 | 
						|
	  if ( __r >= sizeof(fbuf) ) \
 | 
						|
	  { if ( (fs_malloced = fs = malloc(__r+1)) == NULL ) goto error; \
 | 
						|
	    __r = snprintf(fs, __r+1, fm, a1, a2); \
 | 
						|
	  } \
 | 
						|
	  fe = fs+__r; \
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Svfprintf(IOSTREAM *s, const char *fm, va_list args)
 | 
						|
{ int printed = 0;
 | 
						|
  char buf[TMPBUFSIZE];
 | 
						|
  int tmpbuf;
 | 
						|
  char *fs_malloced = NULL;
 | 
						|
 | 
						|
  SLOCK(s);
 | 
						|
 | 
						|
  if ( !s->buffer && (s->flags & SIO_NBUF) )
 | 
						|
  { S__setbuf(s, buf, sizeof(buf));
 | 
						|
    tmpbuf = TRUE;
 | 
						|
  } else
 | 
						|
    tmpbuf = FALSE;
 | 
						|
 | 
						|
  while(*fm)
 | 
						|
  { if ( *fm == '%' )
 | 
						|
    { fm++;
 | 
						|
 | 
						|
      if ( *fm == '%' )
 | 
						|
      { OUTCHR(s, *fm);
 | 
						|
	fm++;
 | 
						|
	continue;
 | 
						|
      } else
 | 
						|
      { int align = A_RIGHT;
 | 
						|
	int modified = FALSE;
 | 
						|
	int has_arg1 = FALSE, has_arg2 = FALSE;
 | 
						|
	int arg1=0, arg2=0;
 | 
						|
	char fbuf[100], *fs = fbuf, *fe = fbuf;
 | 
						|
	int islong = 0;
 | 
						|
	int pad = ' ';
 | 
						|
	IOENC enc = ENC_ANSI;
 | 
						|
 | 
						|
	for(;;)
 | 
						|
	{ switch(*fm)
 | 
						|
	  { case '+':	align = A_RIGHT; fm++; continue;
 | 
						|
	    case '-':	align = A_LEFT;  fm++; continue;
 | 
						|
	    case '0':	pad = '0';	 fm++; continue;
 | 
						|
	    case ' ':	pad = ' ';       fm++; continue;
 | 
						|
	    case '#':   modified = TRUE; fm++; continue;
 | 
						|
	  }
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
 | 
						|
	if ( *fm == '*' )
 | 
						|
	{ has_arg1++;
 | 
						|
	  fm++;
 | 
						|
	  arg1 = va_arg(args, int);
 | 
						|
	} else if ( isdigit(char_to_int(*fm)) )
 | 
						|
	{ if ( *fm == '0' )
 | 
						|
	    pad = '0';
 | 
						|
	  arg1 = valdigit(*fm);
 | 
						|
	  has_arg1++;
 | 
						|
	  for( fm++; isdigit(char_to_int(*fm)); fm++)
 | 
						|
	    arg1 = arg1*10 + valdigit(*fm);
 | 
						|
	}
 | 
						|
	if ( *fm == '.' )
 | 
						|
	{ has_arg2++;
 | 
						|
	  fm++;
 | 
						|
	  if ( *fm == '*' )
 | 
						|
	  { arg2 = va_arg(args, int);
 | 
						|
	  } else
 | 
						|
	  { arg2 = 0;
 | 
						|
	    for( ; isdigit(char_to_int(*fm)); fm++)
 | 
						|
	      arg2 = arg2*10 + valdigit(*fm);
 | 
						|
	  }
 | 
						|
	}
 | 
						|
 | 
						|
	if ( *fm == 'l' )
 | 
						|
	{ islong++;			/* 1: %ld */
 | 
						|
	  fm++;
 | 
						|
	}
 | 
						|
	switch ( *fm )
 | 
						|
	{ case 'l':
 | 
						|
	    islong++;			/* 2: %lld */
 | 
						|
	    fm++;
 | 
						|
	    break;
 | 
						|
	  case 'U':			/* %Us: UTF-8 string */
 | 
						|
	    enc = ENC_UTF8;
 | 
						|
	    fm++;
 | 
						|
	    break;
 | 
						|
	  case 'W':			/* %Ws: wide string */
 | 
						|
	    enc = ENC_WCHAR;
 | 
						|
	    fm++;
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
 | 
						|
	switch(*fm)
 | 
						|
	{ case 'c':
 | 
						|
	    *fe++ = va_arg(args, int);
 | 
						|
	    break;
 | 
						|
	  case 'p':
 | 
						|
	  { void *ptr = va_arg(args, void*);
 | 
						|
	    char fmbuf[8], *fp=fmbuf;
 | 
						|
	    *fp++ = '%';
 | 
						|
	    if ( modified )
 | 
						|
	      *fp++ = '#';
 | 
						|
	    *fp++ = 'p';
 | 
						|
	    *fp   = '\0';
 | 
						|
	    SNPRINTF3(fmbuf, ptr);
 | 
						|
 | 
						|
	    break;
 | 
						|
	  }
 | 
						|
	  case 'd':
 | 
						|
	  case 'i':
 | 
						|
	  case 'o':
 | 
						|
	  case 'u':
 | 
						|
	  case 'x':
 | 
						|
	  case 'X':
 | 
						|
	  { int      vi = 0;
 | 
						|
	    long     vl = 0;			/* make compiler silent */
 | 
						|
	    int64_t vll = 0;
 | 
						|
	    char fmbuf[8], *fp=fmbuf;
 | 
						|
 | 
						|
	    switch( islong )
 | 
						|
	    { case 0:
 | 
						|
		vi = va_arg(args, int);
 | 
						|
	        break;
 | 
						|
	      case 1:
 | 
						|
		vl = va_arg(args, long);
 | 
						|
	        break;
 | 
						|
	      case 2:
 | 
						|
	        vll = va_arg(args, int64_t);
 | 
						|
		break;
 | 
						|
	      default:
 | 
						|
		assert(0);
 | 
						|
	    }
 | 
						|
 | 
						|
	    *fp++ = '%';
 | 
						|
	    if ( modified )
 | 
						|
	      *fp++ = '#';
 | 
						|
	    switch( islong )
 | 
						|
	    { case 0:
 | 
						|
		*fp++ = *fm;
 | 
						|
	        *fp   = '\0';
 | 
						|
		SNPRINTF3(fmbuf, vi);
 | 
						|
		break;
 | 
						|
	      case 1:
 | 
						|
		*fp++ = 'l';
 | 
						|
	        *fp++ = *fm;
 | 
						|
		*fp   = '\0';
 | 
						|
		SNPRINTF3(fmbuf, vl);
 | 
						|
		break;
 | 
						|
	      case 2:
 | 
						|
#ifdef __WINDOWS__
 | 
						|
	        *fp++ = 'I';		/* Synchronise with INT64_FORMAT! */
 | 
						|
	        *fp++ = '6';
 | 
						|
		*fp++ = '4';
 | 
						|
#else
 | 
						|
	        *fp++ = 'l';
 | 
						|
	        *fp++ = 'l';
 | 
						|
#endif
 | 
						|
	        *fp++ = *fm;
 | 
						|
	        *fp   = '\0';
 | 
						|
	        SNPRINTF3(fmbuf, vll);
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
 | 
						|
	    break;
 | 
						|
	  }
 | 
						|
	  case 'f':
 | 
						|
	  case 'e':
 | 
						|
	  case 'E':
 | 
						|
	  case 'g':
 | 
						|
	  case 'G':
 | 
						|
	  { double v = va_arg(args, double);
 | 
						|
	    char fmbuf[8], *fp=fmbuf;
 | 
						|
 | 
						|
	    *fp++ = '%';
 | 
						|
	    if ( modified )
 | 
						|
	      *fp++ = '#';
 | 
						|
	    if ( has_arg2 )		/* specified precission */
 | 
						|
	    { *fp++ = '.';
 | 
						|
	      *fp++ = '*';
 | 
						|
	      *fp++ = *fm;
 | 
						|
	      *fp   = '\0';
 | 
						|
	      SNPRINTF4(fmbuf, arg2, v);
 | 
						|
	    } else
 | 
						|
	    { *fp++ = *fm;
 | 
						|
	      *fp   = '\0';
 | 
						|
	      SNPRINTF3(fmbuf, v);
 | 
						|
	    }
 | 
						|
	    fe = &fs[strlen(fs)];
 | 
						|
 | 
						|
	    break;
 | 
						|
	  }
 | 
						|
	  case 's':
 | 
						|
	    fs = va_arg(args, char *);
 | 
						|
	    if ( !fs )
 | 
						|
	      fs = "(null)";
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
 | 
						|
	if ( has_arg1 )			/* aligned field */
 | 
						|
	{ if ( fs == fbuf )
 | 
						|
	    *fe = '\0';
 | 
						|
 | 
						|
	  if ( align == A_LEFT )
 | 
						|
	  { int w = 0;
 | 
						|
	    while(*fs)
 | 
						|
	    { int c;
 | 
						|
	      NEXTCHR(fs, c);
 | 
						|
	      OUTCHR(s, c);
 | 
						|
	      w++;
 | 
						|
	    }
 | 
						|
	    while(w < arg1)
 | 
						|
	    { OUTCHR(s, pad);
 | 
						|
	      w++;
 | 
						|
	    }
 | 
						|
	  } else /*if ( align == A_RIGHT ) */
 | 
						|
	  { size_t w;
 | 
						|
 | 
						|
	    if ( fs == fbuf )
 | 
						|
	    { w = fe - fs;
 | 
						|
	    } else
 | 
						|
	    { switch(enc)
 | 
						|
	      { case ENC_ANSI:
 | 
						|
		  w = strlen(fs);
 | 
						|
		  break;
 | 
						|
		case ENC_UTF8:
 | 
						|
		  w = strlen(fs);
 | 
						|
		  w = utf8_strlen(fs, w);
 | 
						|
		  break;
 | 
						|
		case ENC_WCHAR:
 | 
						|
		  w = wcslen((wchar_t*)fs);
 | 
						|
		  break;
 | 
						|
		default:
 | 
						|
		  assert(0);
 | 
						|
		  w = 0;		/* make compiler happy */
 | 
						|
		  break;
 | 
						|
	      }
 | 
						|
	    }
 | 
						|
 | 
						|
	    if ( (ssize_t)w < arg1 )
 | 
						|
	    { w = arg1 - w;
 | 
						|
	      while(w > 0 )
 | 
						|
	      { OUTCHR(s, pad);
 | 
						|
		w--;
 | 
						|
	      }
 | 
						|
	    }
 | 
						|
	    while(*fs)
 | 
						|
	    { int c;
 | 
						|
	      NEXTCHR(fs, c);
 | 
						|
	      OUTCHR(s, c);
 | 
						|
	    }
 | 
						|
	  }
 | 
						|
	} else
 | 
						|
	{ if ( fs == fbuf )		/* unaligned field, just output */
 | 
						|
	  { while(fs < fe)
 | 
						|
	      OUTCHR(s, *fs++);
 | 
						|
	  } else
 | 
						|
	  { while(*fs)
 | 
						|
	    { int c;
 | 
						|
	      NEXTCHR(fs, c);
 | 
						|
	      OUTCHR(s, c);
 | 
						|
	    }
 | 
						|
	  }
 | 
						|
	}
 | 
						|
	fm++;
 | 
						|
	if ( fs_malloced )
 | 
						|
	{ fs_malloced = NULL;
 | 
						|
	  free(fs_malloced);
 | 
						|
	}
 | 
						|
      }
 | 
						|
    } else if ( *fm == '\\' && fm[1] )
 | 
						|
    { OUTCHR(s, fm[1]);
 | 
						|
      fm += 2;
 | 
						|
    } else
 | 
						|
    { OUTCHR(s, *fm);
 | 
						|
      fm++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if ( tmpbuf )
 | 
						|
  { if ( S__removebuf(s) < 0 )
 | 
						|
      goto error;
 | 
						|
  }
 | 
						|
 | 
						|
  SUNLOCK(s);
 | 
						|
  return (int)printed;
 | 
						|
 | 
						|
error:
 | 
						|
  if ( fs_malloced )
 | 
						|
    free(fs_malloced);
 | 
						|
 | 
						|
  SUNLOCK(s);
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Ssprintf(char *buf, const char *fm, ...)
 | 
						|
{ va_list args;
 | 
						|
  int rval;
 | 
						|
 | 
						|
  va_start(args, fm);
 | 
						|
  rval = Svsprintf(buf, fm, args);
 | 
						|
  va_end(args);
 | 
						|
 | 
						|
  return rval;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Svsprintf(char *buf, const char *fm, va_list args)
 | 
						|
{ IOSTREAM s;
 | 
						|
  int rval;
 | 
						|
 | 
						|
  memset(&s, 0, sizeof(s));
 | 
						|
  s.bufp      = buf;
 | 
						|
  s.limitp    = (char *)(~0L);
 | 
						|
  s.buffer    = buf;
 | 
						|
  s.flags     = SIO_FBUF|SIO_OUTPUT;
 | 
						|
  s.encoding  = ENC_ISO_LATIN_1;
 | 
						|
 | 
						|
  if ( (rval = Svfprintf(&s, fm, args)) >= 0 )
 | 
						|
    *s.bufp = '\0';
 | 
						|
 | 
						|
  return rval;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Svdprintf(const char *fm, va_list args)
 | 
						|
{ int rval;
 | 
						|
  IOSTREAM *s = Serror;
 | 
						|
 | 
						|
  Slock(s);
 | 
						|
  rval = Svfprintf(s, fm, args);
 | 
						|
#if defined(_DEBUG) && defined(__WINDOWS__)
 | 
						|
  Sputc('\0', s);
 | 
						|
  s->bufp--;				/* `Unput' */
 | 
						|
  OutputDebugString(s->buffer);
 | 
						|
#endif
 | 
						|
  if ( Sflush(s) != 0 )
 | 
						|
    rval = -1;
 | 
						|
  Sunlock(s);
 | 
						|
 | 
						|
  return rval;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
Sdprintf(const char *fm, ...)
 | 
						|
{ va_list args;
 | 
						|
  int rval;
 | 
						|
 | 
						|
  va_start(args, fm);
 | 
						|
  rval = Svdprintf(fm, args);
 | 
						|
  va_end(args);
 | 
						|
 | 
						|
  return rval;
 | 
						|
}
 | 
						|
 | 
						|
#if 0
 | 
						|
		 /*******************************
 | 
						|
		 *	      SCANF		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
int
 | 
						|
Svfscanf(IOSTREAM *s, const char *fm, va_list args)
 | 
						|
{ int done = 0;				/* # items converted */
 | 
						|
  int chread = 0;			/* # characters read */
 | 
						|
  int c = GET(s);			/* current character */
 | 
						|
  int supress;				/* if TRUE, don't assign (*) */
 | 
						|
  int field_width;			/* max width of field */
 | 
						|
  int tsize;				/* SZ_SHORT, SZ_NORMAL, SZ_LONG */
 | 
						|
 | 
						|
  while(*fm)
 | 
						|
  { if ( *fm == ' ' )
 | 
						|
    { while ( isblank(c) )
 | 
						|
	c = GET(s);
 | 
						|
      fm++;
 | 
						|
      continue;
 | 
						|
    } else if ( *fm == '%' && fm[1] != '%' )
 | 
						|
    { supress = FALSE;
 | 
						|
      field_width = -1;
 | 
						|
      int size = SZ_STANDARD;
 | 
						|
 | 
						|
      for(;;)				/* parse modifiers */
 | 
						|
      { fm++;
 | 
						|
	if ( isdigit(*fm) )
 | 
						|
	{ field_width = valdigit(*fm);
 | 
						|
	  for(++fm; isdigit(*fm); fm++)
 | 
						|
	    field_width = 10*field_width + valdigit(*fm);
 | 
						|
	  fm--;
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
	if ( *fm == '*' )
 | 
						|
	{ supress++;
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
	if ( *fm == 'l' )
 | 
						|
	{ size = SZ_LONG;
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
	if ( *fm == 'h' )
 | 
						|
	{ size = SZ_SHORT;
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
      }
 | 
						|
 | 
						|
      if ( *fm != '[' && *fm != c )
 | 
						|
	while(isblank(c))
 | 
						|
	  c = GET(s);
 | 
						|
 | 
						|
      switch(*fm)
 | 
						|
      { { intptr_t v;			/* collect value here */
 | 
						|
	  int negative;			/* true if < 0 */
 | 
						|
	  int base;			/* base for conversion */
 | 
						|
	  int ok;			/* successful */
 | 
						|
	case 'd':
 | 
						|
	  base = 10;
 | 
						|
 | 
						|
	do_int:
 | 
						|
	  negative = FALSE;
 | 
						|
	  if ( c == '+' )
 | 
						|
	    c = GET(s);
 | 
						|
	  else if ( c == '-' )
 | 
						|
	  { negative++;
 | 
						|
	    c = GET(s);
 | 
						|
	  }
 | 
						|
	do_unsigned:
 | 
						|
	  ok = FALSE;
 | 
						|
	  if ( base == 16 )		/* hexadecimal */
 | 
						|
	  { if ( isxdigit(c) )
 | 
						|
	    { v = valxdigit(c);
 | 
						|
	      for(c = GET(s); isxdigit(c); c = GET(s))
 | 
						|
		v = base*v + valxdigit(c);
 | 
						|
	      ok++;
 | 
						|
	    }
 | 
						|
	  } else
 | 
						|
	  { int cv;
 | 
						|
 | 
						|
	    if ( isdigit(c) && (cv=valdigit(c)) < base )
 | 
						|
	    { v = cv;
 | 
						|
	      for(c = GET(s); isdigit(c) && (cv=valdigit(c)) < base; c = GET(s))
 | 
						|
		v = base*v + cv;
 | 
						|
	      ok++;
 | 
						|
	    }
 | 
						|
	  }
 | 
						|
 | 
						|
	  if ( ok )
 | 
						|
	  { if ( !supress )
 | 
						|
	    { if ( negative )
 | 
						|
		v = -v;
 | 
						|
	      if ( tsize == SZ_SHORT )
 | 
						|
	      { short *vp = va_arg(args, short *);
 | 
						|
		*vp = v;
 | 
						|
	      } else if ( tsize == SZ_LONG )
 | 
						|
	      { intptr_t *vp = va_arg(args, intptr_t *);
 | 
						|
		*vp = v;
 | 
						|
	      } else
 | 
						|
	      { int *vp = va_arg(args, int *);
 | 
						|
		*vp = v;
 | 
						|
	      }
 | 
						|
	      done++;
 | 
						|
	    }
 | 
						|
	    continue;			/* with next */
 | 
						|
	  } else
 | 
						|
	    return done;
 | 
						|
	case 'u':
 | 
						|
	  base = 10;
 | 
						|
	  negative = FALSE;
 | 
						|
	  goto do_unsigned;
 | 
						|
	case 'o':
 | 
						|
	  base = 8;
 | 
						|
	  goto do_int;
 | 
						|
	case 'x':
 | 
						|
	  base = 16;
 | 
						|
	  goto do_int;
 | 
						|
	case 'i':
 | 
						|
	  if ( c == '0' )
 | 
						|
	  { int c2 = GET(s);
 | 
						|
 | 
						|
	    if ( c2 == 'x' )
 | 
						|
	    { base = 16;
 | 
						|
	      c = GET(s);
 | 
						|
	    } else
 | 
						|
	    { UNGET(c2, s);
 | 
						|
	      base = 8;
 | 
						|
	    }
 | 
						|
	    negative = FALSE;
 | 
						|
	    goto do_unsigned;
 | 
						|
	  }
 | 
						|
	  base = 10;
 | 
						|
	  goto do_int;
 | 
						|
	}
 | 
						|
	case 'n':
 | 
						|
	  if ( !supress )
 | 
						|
	  { if ( tsize == SZ_SHORT )
 | 
						|
	    { short *vp = va_arg(args, short *);
 | 
						|
	      *vp = chread;
 | 
						|
	    } else if ( tsize == SZ_LONG )
 | 
						|
	    { intptr_t *vp = va_arg(args, intptr_t *);
 | 
						|
	      *vp = chread;
 | 
						|
	    } else
 | 
						|
	    { int *vp = va_arg(args, int *);
 | 
						|
	      *vp = chread;
 | 
						|
	    }
 | 
						|
	    done++;
 | 
						|
	  }
 | 
						|
	  fm++;
 | 
						|
	  continue;
 | 
						|
	case 'E':
 | 
						|
	case 'e':
 | 
						|
	case 'f':
 | 
						|
	case 'G':
 | 
						|
	case 'g':
 | 
						|
	{ char work[200];
 | 
						|
	  char *w = work;
 | 
						|
	  int ds = 0;
 | 
						|
	  double v;
 | 
						|
 | 
						|
	  if ( c == '-' || c == '+' )	/* [+|-] */
 | 
						|
	  { *w++ = c;
 | 
						|
	    c = GET(s);
 | 
						|
	  }
 | 
						|
	  while(isdigit(c))		/* {digit} */
 | 
						|
	  { *w++ = c;
 | 
						|
	    c = GET(s);
 | 
						|
	    ds++;
 | 
						|
	  }
 | 
						|
	  if ( c == '.' )		/* [.] */
 | 
						|
	    *w++ = c;
 | 
						|
	  while(isdigit(c))		/* {digit} */
 | 
						|
	  { *w++ = c;
 | 
						|
	    c = GET(s);
 | 
						|
	    ds++;
 | 
						|
	  }
 | 
						|
	  if ( !ds )
 | 
						|
	    SCAN_ERROR(s)
 | 
						|
	  if ( c == 'e' || c == 'E' )	/* [e<digit>{<digit>}] */
 | 
						|
	  { *w++ = c;
 | 
						|
	    c = GET(s);
 | 
						|
	    if ( !isdigit(c) )
 | 
						|
	      SCAN_ERROR(s)
 | 
						|
	    while(isdigit(c))
 | 
						|
	    { *w++ = c;
 | 
						|
	    c = GET(s);
 | 
						|
	    }
 | 
						|
	  }
 | 
						|
 | 
						|
	  if ( !supress )
 | 
						|
	  { *w = '\0';
 | 
						|
	    v = strtod(work, &w)
 | 
						|
	    if ( w == work )
 | 
						|
	      SCAN_ERROR(s);
 | 
						|
 | 
						|
	    switch(tsize)
 | 
						|
	    { case SZ_NORMAL:
 | 
						|
	      { float *fp = va_arg(args, float *);
 | 
						|
		*fp = v;
 | 
						|
		break;
 | 
						|
	      }
 | 
						|
	      case SZ_LONG:
 | 
						|
	      { double *fp = va_arg(args, double *);
 | 
						|
		*fp = v;
 | 
						|
		break;
 | 
						|
	      }
 | 
						|
	    }
 | 
						|
	    done++;
 | 
						|
	  }
 | 
						|
 | 
						|
	  fm++;
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
	case 's':
 | 
						|
	  if ( !supress )
 | 
						|
	  { char *sp = va_arg(args, char *);
 | 
						|
 | 
						|
	    while(!isblank(c) && field_width-- != 0)
 | 
						|
	    { *sp++ = c;
 | 
						|
	      c = GET(s);
 | 
						|
	    }
 | 
						|
	  } else
 | 
						|
	    while(!isblank(c) && field_width-- != 0)
 | 
						|
	      c = GET(s);
 | 
						|
	  fm++;
 | 
						|
	  continue;
 | 
						|
	case 'c':
 | 
						|
	  if ( !supress )
 | 
						|
	  { char *cp = va_arg(args, char *);
 | 
						|
	    *cp = c;
 | 
						|
	  }
 | 
						|
	  c = GET(s);
 | 
						|
	  fm++;
 | 
						|
	  continue;
 | 
						|
	case '[':
 | 
						|
	{ char set[256];
 | 
						|
 | 
						|
	  memset(set, 0, sizeof(set));
 | 
						|
	  fm++;
 | 
						|
	  if ( *fm == ']' )
 | 
						|
	    set[*fm++]++;
 | 
						|
	  else if ( *fm == '^' )
 | 
						|
	  { fm++;
 | 
						|
	    negate++;
 | 
						|
	  }
 | 
						|
	  while(*fm != ']')
 | 
						|
	  { if ( *fm == '-' )
 | 
						|
 | 
						|
	  }
 | 
						|
	}
 | 
						|
      }
 | 
						|
    } else				/* normal character */
 | 
						|
    { if ( c == *fm )
 | 
						|
      { c = GET(s);
 | 
						|
	fm++;
 | 
						|
	continue;
 | 
						|
      }
 | 
						|
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
out:
 | 
						|
  UNGET(c, s);
 | 
						|
 | 
						|
  return done;
 | 
						|
}
 | 
						|
 | 
						|
#endif /*0*/
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	   FILTER STREAMS	*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Link two streams in a pipeline,  where   filter  filters data for stream
 | 
						|
`parent'. If parent is an output steam we have
 | 
						|
 | 
						|
	application --> filter --> parent -->
 | 
						|
 | 
						|
If parent is an input stream we have
 | 
						|
 | 
						|
	--> parent --> filter --> application
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
int
 | 
						|
Sset_filter(IOSTREAM *parent, IOSTREAM *filter)
 | 
						|
{ if ( !parent || parent->magic != SIO_MAGIC )
 | 
						|
  { errno = EINVAL;
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
 | 
						|
  if ( filter )
 | 
						|
  { if ( filter->magic != SIO_MAGIC )
 | 
						|
    { errno = EINVAL;
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  parent->upstream = filter;
 | 
						|
  if ( filter )
 | 
						|
    filter->downstream = parent;
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	    FILE STREAMS	*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
static ssize_t
 | 
						|
Sread_file(void *handle, char *buf, size_t size)
 | 
						|
{ intptr_t h = (intptr_t) handle;
 | 
						|
  ssize_t bytes;
 | 
						|
 | 
						|
  for(;;)
 | 
						|
  {
 | 
						|
#ifdef __WINDOWS__
 | 
						|
    bytes = read((int)h, buf, (int)size);
 | 
						|
#else
 | 
						|
    bytes = read((int)h, buf, size);
 | 
						|
#endif
 | 
						|
 | 
						|
    if ( bytes == -1 && errno == EINTR )
 | 
						|
    { if ( PL_handle_signals() < 0 )
 | 
						|
      { errno = EPLEXCEPTION;
 | 
						|
	return -1;
 | 
						|
      }
 | 
						|
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    return bytes;
 | 
						|
  }
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static ssize_t
 | 
						|
Swrite_file(void *handle, char *buf, size_t size)
 | 
						|
{ intptr_t h = (intptr_t) handle;
 | 
						|
  ssize_t bytes;
 | 
						|
 | 
						|
  for(;;)
 | 
						|
  {
 | 
						|
#ifdef __WINDOWS__
 | 
						|
    bytes = write((int)h, buf, (int)size);
 | 
						|
#else
 | 
						|
    bytes = write((int)h, buf, size);
 | 
						|
#endif
 | 
						|
    if ( bytes == -1 && errno == EINTR )
 | 
						|
    { if ( PL_handle_signals() < 0 )
 | 
						|
      { errno = EPLEXCEPTION;
 | 
						|
	return -1;
 | 
						|
      }
 | 
						|
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    return bytes;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static long
 | 
						|
Sseek_file(void *handle, long pos, int whence)
 | 
						|
{ intptr_t h = (intptr_t) handle;
 | 
						|
 | 
						|
					/* cannot do EINTR according to man */
 | 
						|
  return lseek((int)h, pos, whence);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#ifdef O_LARGEFILES
 | 
						|
static int64_t
 | 
						|
Sseek_file64(void *handle, int64_t pos, int whence)
 | 
						|
{ intptr_t h = (intptr_t) handle;
 | 
						|
 | 
						|
					/* cannot do EINTR according to man */
 | 
						|
  return lseek((int)h, pos, whence);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
Sclose_file(void *handle)
 | 
						|
{ intptr_t h = (intptr_t) handle;
 | 
						|
  int rc;
 | 
						|
 | 
						|
  do
 | 
						|
  { rc = close((int) h);
 | 
						|
  } while ( rc == -1 && errno == EINTR );
 | 
						|
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
Scontrol_file(void *handle, int action, void *arg)
 | 
						|
{ intptr_t h = (intptr_t) handle;
 | 
						|
  int fd = (int)h;
 | 
						|
 | 
						|
  switch(action)
 | 
						|
  { case SIO_GETSIZE:
 | 
						|
    { int64_t *rval = arg;
 | 
						|
      struct stat buf;
 | 
						|
 | 
						|
      if ( fstat(fd, &buf) == 0 )
 | 
						|
      {	*rval = buf.st_size;
 | 
						|
        return 0;
 | 
						|
      }
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
    case SIO_SETENCODING:
 | 
						|
    case SIO_FLUSHOUTPUT:
 | 
						|
      return 0;
 | 
						|
    case SIO_GETFILENO:
 | 
						|
    { int *p = arg;
 | 
						|
      *p = fd;
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
IOFUNCTIONS Sfilefunctions =
 | 
						|
{ Sread_file,
 | 
						|
  Swrite_file,
 | 
						|
  Sseek_file,
 | 
						|
  Sclose_file,
 | 
						|
  Scontrol_file,
 | 
						|
#ifdef O_LARGEFILES
 | 
						|
  Sseek_file64
 | 
						|
#else
 | 
						|
  NULL
 | 
						|
#endif
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
IOFUNCTIONS Sttyfunctions =
 | 
						|
{ Sread_file,
 | 
						|
  Swrite_file,
 | 
						|
  NULL,
 | 
						|
  Sclose_file,
 | 
						|
  Scontrol_file,
 | 
						|
#ifdef O_LARGEFILES
 | 
						|
  Sseek_file64
 | 
						|
#else
 | 
						|
  NULL
 | 
						|
#endif
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
(*)  Windows  isatty()  is  totally  broken   since  VC9;  crashing  the
 | 
						|
application instead of returning EINVAL on  wrong   values  of fd. As we
 | 
						|
provide  the  socket-id  through   Sfileno,    this   code   crashes  on
 | 
						|
tcp_open_socket(). As ttys and its detection is   of no value on Windows
 | 
						|
anyway, we skip this.
 | 
						|
 | 
						|
For now, we use PL_malloc_uncollectable(). In   the  end, this is really
 | 
						|
one of the object-types we want to leave to GC.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
#ifndef FD_CLOEXEC			/* This is not defined in MacOS */
 | 
						|
#define FD_CLOEXEC 1
 | 
						|
#endif
 | 
						|
 | 
						|
IOSTREAM *
 | 
						|
Snew(void *handle, int flags, IOFUNCTIONS *functions)
 | 
						|
{ IOSTREAM *s;
 | 
						|
 | 
						|
  if ( !(s = PL_malloc_uncollectable(sizeof(IOSTREAM))) )
 | 
						|
  { errno = ENOMEM;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
  memset((char *)s, 0, sizeof(IOSTREAM));
 | 
						|
  s->magic         = SIO_MAGIC;
 | 
						|
  s->lastc         = EOF;
 | 
						|
  s->flags         = flags;
 | 
						|
  s->handle        = handle;
 | 
						|
  s->functions     = functions;
 | 
						|
  s->timeout       = -1;		/* infinite */
 | 
						|
  s->posbuf.lineno = 1;
 | 
						|
  if ( (flags&SIO_TEXT) )
 | 
						|
  { s->encoding    = initEncoding();
 | 
						|
  } else
 | 
						|
  { s->encoding	   = ENC_OCTET;
 | 
						|
  }
 | 
						|
#if CRLF_MAPPING
 | 
						|
  s->newline       = SIO_NL_DOS;
 | 
						|
#endif
 | 
						|
  if ( flags & SIO_RECORDPOS )
 | 
						|
    s->position = &s->posbuf;
 | 
						|
#ifdef O_PLMT
 | 
						|
  if ( !(flags & SIO_NOMUTEX) )
 | 
						|
  { if ( !(s->mutex = PL_malloc(sizeof(recursiveMutex))) )
 | 
						|
    { PL_free(s);
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
    recursiveMutexInit(s->mutex);
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
{ int fd;
 | 
						|
  if ( (fd = Sfileno(s)) >= 0 )
 | 
						|
  {
 | 
						|
#ifndef __WINDOWS__			/* (*) */
 | 
						|
    if ( isatty(fd) )
 | 
						|
      s->flags |= SIO_ISATTY;
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(F_SETFD)
 | 
						|
    fcntl(fd, F_SETFD, FD_CLOEXEC);
 | 
						|
#elif defined(__WINDOWS__)
 | 
						|
    SetHandleInformation((HANDLE)_get_osfhandle(fd),
 | 
						|
			 HANDLE_FLAG_INHERIT, 0);
 | 
						|
#endif
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
#ifdef O_LOCALE
 | 
						|
  initStreamLocale(s);
 | 
						|
#endif
 | 
						|
 | 
						|
  return s;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#ifndef O_BINARY
 | 
						|
#define O_BINARY 0
 | 
						|
#endif
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Open a file. In addition to the normal  arguments, "lr" means get a read
 | 
						|
(shared-) lock on the file and  "lw"   means  get  an write (exclusive-)
 | 
						|
lock.  How much do we need to test here?
 | 
						|
 | 
						|
Note that the low-level open  is  always   binary  as  O_TEXT open files
 | 
						|
result in lost and corrupted data in   some  encodings (UTF-16 is one of
 | 
						|
them).  Sgetcode()  and  Sputcode()  do  the  LF  <->  CRLF  mapping  of
 | 
						|
CRLF_MAPPING is defined.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
IOSTREAM *
 | 
						|
Sopen_file(const char *path, const char *how)
 | 
						|
{ int fd;
 | 
						|
  int oflags = O_BINARY;
 | 
						|
  int flags = SIO_FILE|SIO_TEXT|SIO_RECORDPOS|SIO_FBUF;
 | 
						|
  int op = *how++;
 | 
						|
  intptr_t lfd;
 | 
						|
  enum {lnone=0,lread,lwrite} lock = lnone;
 | 
						|
  IOSTREAM *s;
 | 
						|
  IOENC enc = ENC_UNKNOWN;
 | 
						|
#if __WINDOWS__
 | 
						|
  int wait = TRUE;
 | 
						|
#endif
 | 
						|
 | 
						|
#if __ANDROID__
 | 
						|
  if (Yap_isAsset(path)) {
 | 
						|
      extern IOFUNCTIONS Sassetfunctions;
 | 
						|
      extern void *Yap_openAssetFile(const char *);
 | 
						|
 | 
						|
      if (op != 'r')
 | 
						|
          return NULL;
 | 
						|
      for( ; *how; how++)
 | 
						|
      { switch(*how)
 | 
						|
          {
 | 
						|
              case 'b':				/* binary */
 | 
						|
              flags &= ~SIO_TEXT;
 | 
						|
              enc = ENC_OCTET;
 | 
						|
              break;
 | 
						|
              case 'r':				/* no record */
 | 
						|
              flags &= ~SIO_RECORDPOS;
 | 
						|
              break;
 | 
						|
              default:
 | 
						|
              errno = EINVAL;
 | 
						|
              return NULL;
 | 
						|
          }
 | 
						|
      }
 | 
						|
#if O_LARGEFILES && defined(O_LARGEFILE)
 | 
						|
  oflags |= O_LARGEFILE;
 | 
						|
#endif
 | 
						|
 | 
						|
      lfd = (intptr_t)Yap_openAssetFile( path );
 | 
						|
      if (!lfd) {
 | 
						|
          errno = EINVAL;
 | 
						|
          return NULL;
 | 
						|
      }
 | 
						|
      Sfilefunctions = Sassetfunctions;
 | 
						|
      lock = 0;
 | 
						|
      flags |= SIO_INPUT;
 | 
						|
      goto build_stream;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  for( ; *how; how++)
 | 
						|
  { switch(*how)
 | 
						|
    { case 'b':				/* binary */
 | 
						|
	flags &= ~SIO_TEXT;
 | 
						|
	enc = ENC_OCTET;
 | 
						|
        break;
 | 
						|
      case 'r':				/* no record */
 | 
						|
	flags &= ~SIO_RECORDPOS;
 | 
						|
        break;
 | 
						|
#if __WINDOWS__
 | 
						|
     case 'L':				/* lock r: read, w: write */
 | 
						|
	wait = FALSE;
 | 
						|
#endif
 | 
						|
        /*FALLTHROUGH*/
 | 
						|
      case 'l':				/* lock r: read, w: write */
 | 
						|
	if ( *++how == 'r' )
 | 
						|
	  lock = lread;
 | 
						|
        else if ( *how == 'w' )
 | 
						|
	  lock = lwrite;
 | 
						|
        else
 | 
						|
	{ errno = EINVAL;
 | 
						|
	  return NULL;
 | 
						|
	}
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
	errno = EINVAL;
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
#if O_LARGEFILES && defined(O_LARGEFILE)
 | 
						|
  oflags |= O_LARGEFILE;
 | 
						|
#endif
 | 
						|
 | 
						|
  switch(op)
 | 
						|
  { case 'w':
 | 
						|
      fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|oflags, 0666);
 | 
						|
      flags |= SIO_OUTPUT;
 | 
						|
      break;
 | 
						|
    case 'a':
 | 
						|
      fd = open(path, O_WRONLY|O_CREAT|O_APPEND|oflags, 0666);
 | 
						|
      flags |= SIO_OUTPUT|SIO_APPEND;
 | 
						|
      break;
 | 
						|
    case 'u':
 | 
						|
      fd = open(path, O_WRONLY|O_CREAT|oflags, 0666);
 | 
						|
      flags |= SIO_OUTPUT|SIO_UPDATE;
 | 
						|
      break;
 | 
						|
    case 'r':
 | 
						|
      fd = open(path, O_RDONLY|oflags);
 | 
						|
      flags |= SIO_INPUT;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      errno = EINVAL;
 | 
						|
      return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
  if ( fd < 0 )
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  if ( lock )
 | 
						|
  {
 | 
						|
#ifdef FCNTL_LOCKS
 | 
						|
    struct flock buf;
 | 
						|
 | 
						|
    memset(&buf, 0, sizeof(buf));
 | 
						|
    buf.l_whence = SEEK_SET;
 | 
						|
    buf.l_type   = (lock == lread ? F_RDLCK : F_WRLCK);
 | 
						|
 | 
						|
    while( fcntl(fd, wait ? F_SETLKW : F_SETLK, &buf) != 0 )
 | 
						|
    { if ( errno == EINTR )
 | 
						|
      { if ( PL_handle_signals() < 0 )
 | 
						|
	{ close(fd);
 | 
						|
	  return NULL;
 | 
						|
	}
 | 
						|
	continue;
 | 
						|
      } else
 | 
						|
      { int save = errno;
 | 
						|
 | 
						|
	close(fd);
 | 
						|
	errno = save;
 | 
						|
	return NULL;
 | 
						|
      }
 | 
						|
    }
 | 
						|
#else					/* we don't have locking */
 | 
						|
#if __WINDOWS__
 | 
						|
    HANDLE h = (HANDLE)_get_osfhandle(fd);
 | 
						|
    OVERLAPPED ov;
 | 
						|
    int flags = 0;
 | 
						|
 | 
						|
    if ( lock == lwrite )
 | 
						|
      flags |= LOCKFILE_EXCLUSIVE_LOCK;
 | 
						|
    if ( !wait )
 | 
						|
      flags |= LOCKFILE_FAIL_IMMEDIATELY;
 | 
						|
 | 
						|
    memset(&ov, 0, sizeof(ov));
 | 
						|
    if ( !LockFileEx(h, flags,
 | 
						|
		     0,
 | 
						|
		     0, 0xfffffff,
 | 
						|
		     &ov) )
 | 
						|
    { close(fd);
 | 
						|
      errno = (wait ? EACCES : EAGAIN);	/* TBD: proper error */
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
#else
 | 
						|
    close(fd);
 | 
						|
    errno = EINVAL;
 | 
						|
    return NULL;
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
  }
 | 
						|
 | 
						|
  lfd = (intptr_t)fd;
 | 
						|
#if __ANDROID__
 | 
						|
 build_stream:
 | 
						|
#endif
 | 
						|
  s = Snew((void *)lfd, flags, &Sfilefunctions);
 | 
						|
  if ( enc != ENC_UNKNOWN )
 | 
						|
    s->encoding = enc;
 | 
						|
  if ( lock )
 | 
						|
    s->flags |= SIO_ADVLOCK;
 | 
						|
 | 
						|
  return s;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
IOSTREAM *
 | 
						|
Sfdopen(int fd, const char *type)
 | 
						|
{ intptr_t lfd;
 | 
						|
  int flags = SIO_FILE|SIO_RECORDPOS|SIO_FBUF;
 | 
						|
 | 
						|
  if ( fd < 0 )
 | 
						|
  { errno = EINVAL;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
#if defined(HAVE_FCNTL) && defined(F_GETFL)
 | 
						|
  if ( fcntl(fd, F_GETFL) == -1 )
 | 
						|
    return NULL;
 | 
						|
#endif
 | 
						|
 | 
						|
  if ( *type == 'r' )
 | 
						|
  { flags |= SIO_INPUT;
 | 
						|
  } else if ( *type == 'w' )
 | 
						|
  { flags |= SIO_OUTPUT;
 | 
						|
  } else
 | 
						|
  { errno = EINVAL;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
  if ( type[1] != 'b' )
 | 
						|
    flags |= SIO_TEXT;
 | 
						|
 | 
						|
  lfd = (intptr_t)fd;
 | 
						|
 | 
						|
  return Snew((void *)lfd, flags, &Sfilefunctions);
 | 
						|
}
 | 
						|
 | 
						|
/* MT: as long as s is valid, this should be ok
 | 
						|
*/
 | 
						|
 | 
						|
int
 | 
						|
Sfileno(IOSTREAM *s)
 | 
						|
{ int n;
 | 
						|
 | 
						|
  if ( s->flags & SIO_FILE )
 | 
						|
  { intptr_t h = (intptr_t)s->handle;
 | 
						|
    n = (int)h;
 | 
						|
  } else if ( s->functions->control &&
 | 
						|
	      (*s->functions->control)(s->handle,
 | 
						|
				       SIO_GETFILENO,
 | 
						|
				       (void *)&n) == 0 )
 | 
						|
  { ;
 | 
						|
  } else
 | 
						|
  { errno = EINVAL;
 | 
						|
    n = -1;				/* no file stream */
 | 
						|
  }
 | 
						|
 | 
						|
  return n;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#ifdef __WINDOWS__
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
On  Windows,  type  SOCKET  is   an    unsigned   int   and  all  values
 | 
						|
[0..INVALID_SOCKET) are valid. It is  also   not  allowed  to run normal
 | 
						|
file-functions on it or the application will crash. There seems to be no
 | 
						|
way out except for introducing an extra function at this level :-(
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
SOCKET
 | 
						|
Swinsock(IOSTREAM *s)
 | 
						|
{ SOCKET n = INVALID_SOCKET;
 | 
						|
 | 
						|
  if ( s->functions->control &&
 | 
						|
       (*s->functions->control)(s->handle,
 | 
						|
				SIO_GETWINSOCK,
 | 
						|
				(void *)&n) == 0 )
 | 
						|
  { return n;
 | 
						|
  }
 | 
						|
 | 
						|
  errno = EINVAL;
 | 
						|
  return INVALID_SOCKET;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	       PIPES		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
#ifdef HAVE_POPEN
 | 
						|
#ifdef __WINDOWS__
 | 
						|
#include "windows/popen.c"
 | 
						|
 | 
						|
#undef popen
 | 
						|
#undef pclose
 | 
						|
#define popen(cmd, how) pt_popen(cmd, how)
 | 
						|
#define pclose(fd)	pt_pclose(fd)
 | 
						|
#endif
 | 
						|
 | 
						|
static ssize_t
 | 
						|
Sread_pipe(void *handle, char *buf, size_t size)
 | 
						|
{ FILE *fp = handle;
 | 
						|
 | 
						|
#ifdef __WINDOWS__
 | 
						|
  return read(fileno(fp), buf, (unsigned int)size);
 | 
						|
#else
 | 
						|
  return read(fileno(fp), buf, size);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static ssize_t
 | 
						|
Swrite_pipe(void *handle, char *buf, size_t size)
 | 
						|
{ FILE *fp = handle;
 | 
						|
 | 
						|
#ifdef __WINDOWS__
 | 
						|
  return write(fileno(fp), buf, (unsigned int)size);
 | 
						|
#else
 | 
						|
  return write(fileno(fp), buf, size);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
Sclose_pipe(void *handle)
 | 
						|
{ FILE *fp = handle;
 | 
						|
 | 
						|
  pclose(fp);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
Scontrol_pipe(void *handle, int action, void *arg)
 | 
						|
{ FILE *fp = handle;
 | 
						|
 | 
						|
  switch(action)
 | 
						|
  { case SIO_GETFILENO:
 | 
						|
    { int *ap = arg;
 | 
						|
      *ap = fileno(fp);
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
    case SIO_FLUSHOUTPUT:
 | 
						|
    case SIO_SETENCODING:
 | 
						|
      return 0;
 | 
						|
    default:
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
IOFUNCTIONS Spipefunctions =
 | 
						|
{ Sread_pipe,
 | 
						|
  Swrite_pipe,
 | 
						|
  (Sseek_function)0,
 | 
						|
  Sclose_pipe,
 | 
						|
  Scontrol_pipe
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
IOSTREAM *
 | 
						|
Sopen_pipe(const char *command, const char *type)
 | 
						|
{ char mode[2];
 | 
						|
  FILE *fd;
 | 
						|
 | 
						|
#if 0
 | 
						|
  Sdprintf("Opening \"%s\", mode \"%s\" --> %p (%d)\n",
 | 
						|
	   command, type, fd, errno);
 | 
						|
#endif
 | 
						|
 | 
						|
  mode[0] = type[0];
 | 
						|
  mode[1] = '\0';
 | 
						|
 | 
						|
  if ( (fd = popen(command, mode)) )
 | 
						|
  { int flags;
 | 
						|
 | 
						|
    if ( *type == 'r' )
 | 
						|
      flags = SIO_INPUT|SIO_RECORDPOS|SIO_FBUF;
 | 
						|
    else
 | 
						|
      flags = SIO_OUTPUT|SIO_RECORDPOS|SIO_FBUF;
 | 
						|
 | 
						|
    return Snew((void *)fd, flags, &Spipefunctions);
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
#endif /*HAVE_POPEN*/
 | 
						|
 | 
						|
 | 
						|
#if __ANDROID__
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	  ASSET FILES		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Asset files provide a mechanism for accesing file resources stored in
 | 
						|
an Android application pack.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
static ssize_t
 | 
						|
Swrite_asset(void *handle, char *buf, size_t size)
 | 
						|
{
 | 
						|
  return -1L;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static ssize_t
 | 
						|
Sread_asset(void *handle, char *buf, size_t size)
 | 
						|
{
 | 
						|
  int res = AAsset_read((AAsset* )handle, (void* )buf, size);
 | 
						|
  if (res < 0) {
 | 
						|
      errno = ENOSPC;			/* signal error */
 | 
						|
  }
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static long
 | 
						|
Sseek_asset(void *handle, long offset, int whence)
 | 
						|
{ int res = AAsset_seek((AAsset* )handle, (off_t)offset, whence);
 | 
						|
  if (res == (off_t)-1) {
 | 
						|
      errno = ENOSPC;			/* signal error */
 | 
						|
  }
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
static int64_t
 | 
						|
Sseek64_asset(void *handle, int64_t offset, int whence)
 | 
						|
{ off64_t res = AAsset_seek64((AAsset* )handle, (off64_t)offset, whence);
 | 
						|
  if (res == (off64_t)-1) {
 | 
						|
      errno = ENOSPC;			/* signal error */
 | 
						|
  }
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
Scontrol_asset(void *handle, int action, void *arg)
 | 
						|
{ AAsset*  h = (AAsset* ) handle;
 | 
						|
  off_t *rval = (off_t *)arg;
 | 
						|
 | 
						|
  switch(action)
 | 
						|
    {
 | 
						|
    case SIO_GETSIZE:
 | 
						|
      *rval = AAsset_getLength(h);
 | 
						|
      return 0;
 | 
						|
    case SIO_GETFILENO:
 | 
						|
        { off_t start = 0, end = AAsset_getLength(h);
 | 
						|
	  int fd = AAsset_openFileDescriptor((AAsset*)handle, &start, &end);
 | 
						|
	  if (fd == 0)
 | 
						|
	     return -1;
 | 
						|
	  *rval = fd;
 | 
						|
	  return 0;
 | 
						|
        }
 | 
						|
	  case SIO_SETENCODING:
 | 
						|
    case SIO_FLUSHOUTPUT:
 | 
						|
      return 0;
 | 
						|
    default:
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
Sclose_asset(void *handle)
 | 
						|
{
 | 
						|
  AAsset_close((AAsset* )handle);
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
IOFUNCTIONS Sassetfunctions =
 | 
						|
{ Sread_asset,
 | 
						|
  Swrite_asset,
 | 
						|
  Sseek_asset,
 | 
						|
  Sclose_asset,
 | 
						|
  Scontrol_asset,
 | 
						|
  Sseek64_asset
 | 
						|
};
 | 
						|
 | 
						|
#include <jni.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Sopen_asset(char **buffer, size_t *sizep, const char* mode)
 | 
						|
    Open an Android asset, essentially a read-only 	part of a ZIP archive.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
#endif /* __ANDROID__ */
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	  MEMORY STREAMS	*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Memory streams form a replacement for   sprintf(), sscanf() and friends.
 | 
						|
They allow regarding a piece of  (for output) malloc() maintained memory
 | 
						|
to serve as a temporary buffer.
 | 
						|
 | 
						|
MT: we assume these handles are not passed between threads
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
typedef struct
 | 
						|
{ size_t	here;			/* `here' location */
 | 
						|
  size_t	size;			/* size of buffer */
 | 
						|
  size_t       *sizep;			/* pointer to size */
 | 
						|
  size_t	allocated;		/* allocated size */
 | 
						|
  char	       *buffer;			/* allocated buffer */
 | 
						|
  char	      **bufferp;		/* Write-back location */
 | 
						|
  int		malloced;		/* malloc() maintained */
 | 
						|
} memfile;
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
Sfree(void *ptr)			/* Windows: must free from same */
 | 
						|
{ free(ptr);				/* DLL */
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static size_t
 | 
						|
S__memfile_nextsize(size_t needed)
 | 
						|
{ size_t size = 512;
 | 
						|
 | 
						|
  while ( size < needed )
 | 
						|
    size *= 2;
 | 
						|
 | 
						|
  return size;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static ssize_t
 | 
						|
Swrite_memfile(void *handle, char *buf, size_t size)
 | 
						|
{ memfile *mf = handle;
 | 
						|
 | 
						|
  if ( mf->here + size + 1 >= mf->allocated )
 | 
						|
  { size_t ns = S__memfile_nextsize(mf->here + size + 1);
 | 
						|
    char *nb;
 | 
						|
 | 
						|
    if ( mf->allocated == 0 || !mf->malloced )
 | 
						|
    { if ( !(nb = malloc(ns)) )
 | 
						|
      { errno = ENOMEM;
 | 
						|
	return -1;
 | 
						|
      }
 | 
						|
      if ( !mf->malloced )
 | 
						|
      { if ( mf->buffer )
 | 
						|
	  memcpy(nb, mf->buffer, mf->allocated);
 | 
						|
	mf->malloced = TRUE;
 | 
						|
      }
 | 
						|
    } else
 | 
						|
    { if ( !(nb = realloc(mf->buffer, ns)) )
 | 
						|
      { errno = ENOMEM;
 | 
						|
	return -1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    mf->allocated = ns;
 | 
						|
    *mf->bufferp = mf->buffer = nb;
 | 
						|
  }
 | 
						|
 | 
						|
  memcpy(&mf->buffer[mf->here], buf, size);
 | 
						|
  mf->here += size;
 | 
						|
 | 
						|
  if ( mf->here > mf->size )
 | 
						|
  { mf->size = mf->here;
 | 
						|
    if ( mf->sizep )			/* make externally known */
 | 
						|
      *mf->sizep = mf->size;
 | 
						|
    mf->buffer[mf->size] = '\0';
 | 
						|
  }
 | 
						|
 | 
						|
  return size;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static ssize_t
 | 
						|
Sread_memfile(void *handle, char *buf, size_t size)
 | 
						|
{ memfile *mf = handle;
 | 
						|
 | 
						|
  if ( size + mf->here > mf->size )
 | 
						|
  { if ( mf->here > mf->size )
 | 
						|
      size = 0;
 | 
						|
    else
 | 
						|
      size = mf->size - mf->here;
 | 
						|
  }
 | 
						|
 | 
						|
  memcpy(buf, &mf->buffer[mf->here], size);
 | 
						|
  mf->here += size;
 | 
						|
 | 
						|
  return size;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static long
 | 
						|
Sseek_memfile(void *handle, long offset, int whence)
 | 
						|
{ memfile *mf = handle;
 | 
						|
 | 
						|
  switch(whence)
 | 
						|
  { case SIO_SEEK_SET:
 | 
						|
      break;
 | 
						|
    case SIO_SEEK_CUR:
 | 
						|
      offset += (long)mf->here;		/* Win64: truncates */
 | 
						|
      break;
 | 
						|
    case SIO_SEEK_END:
 | 
						|
      offset = (long)mf->size - offset;	/* Win64 */
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      errno = EINVAL;
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
  if ( offset < 0 || offset > (long)mf->size )
 | 
						|
  { errno = EINVAL;
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
  mf->here = offset;
 | 
						|
 | 
						|
  return offset;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
Sclose_memfile(void *handle)
 | 
						|
{ memfile *mf = handle;
 | 
						|
 | 
						|
  if ( mf )
 | 
						|
  { free(mf);
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  errno = EINVAL;			/* not opened */
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
IOFUNCTIONS Smemfunctions =
 | 
						|
{ Sread_memfile,
 | 
						|
  Swrite_memfile,
 | 
						|
  Sseek_memfile,
 | 
						|
  Sclose_memfile
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Sopenmem(char **buffer, size_t *sizep, const char* mode)
 | 
						|
    Open a memory area as a stream.  Output streams will automatically
 | 
						|
    resized using realloc() if *size = 0 or the stream is opened with mode
 | 
						|
    "wa".
 | 
						|
 | 
						|
    If the buffer is allocated or enlarged, this is achieved using malloc()
 | 
						|
    or realloc().  In this case the returned buffer should be freed by the
 | 
						|
    caller when done.  Example:
 | 
						|
 | 
						|
    { char buf[1024];			(don't allocate for small stuff)
 | 
						|
      char *s = buf;
 | 
						|
      IOSTREAM *fd;
 | 
						|
      size_t size = sizeof(buf);
 | 
						|
 | 
						|
      fd = Sopenmem(&s, &size, "w");
 | 
						|
      ...
 | 
						|
      Sclose(fd);
 | 
						|
      ...
 | 
						|
      if ( s != buf )			(appearently moved)
 | 
						|
	Sfree(s);
 | 
						|
    }
 | 
						|
 | 
						|
Note: Its is NOT allows to access   streams  created with this call from
 | 
						|
multiple threads. This is ok for all   usage inside Prolog itself (often
 | 
						|
through tellString()/toldString(). This call is   intented  to use write
 | 
						|
and other output predicates to create strings.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
IOSTREAM *
 | 
						|
Sopenmem(char **bufp, size_t *sizep, const char *mode)
 | 
						|
{ memfile *mf = malloc(sizeof(memfile));
 | 
						|
  int flags = SIO_FBUF|SIO_RECORDPOS|SIO_NOMUTEX;
 | 
						|
  size_t size;
 | 
						|
 | 
						|
  if ( !mf )
 | 
						|
  { errno = ENOMEM;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  mf->malloced = FALSE;
 | 
						|
  mf->bufferp  = bufp;
 | 
						|
  mf->buffer   = *bufp;
 | 
						|
 | 
						|
  switch(*mode)
 | 
						|
  { case 'r':
 | 
						|
      flags |= SIO_INPUT;
 | 
						|
      if ( sizep == NULL || *sizep == (size_t)-1 )
 | 
						|
	size = (mf->buffer ? strlen(mf->buffer) : 0);
 | 
						|
      else
 | 
						|
	size = *sizep;
 | 
						|
      mf->size = size;
 | 
						|
      mf->allocated = size+1;
 | 
						|
      break;
 | 
						|
    case 'w':
 | 
						|
      flags |= SIO_OUTPUT;
 | 
						|
      mf->size = 0;
 | 
						|
      mf->allocated = (sizep ? *sizep : 0);
 | 
						|
      if ( mf->buffer == NULL || mode[1] == 'a' )
 | 
						|
	mf->malloced = TRUE;
 | 
						|
      if ( mf->buffer )
 | 
						|
	mf->buffer[0] = '\0';
 | 
						|
      if ( sizep )
 | 
						|
	*sizep = mf->size;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      free(mf);
 | 
						|
      errno = EINVAL;
 | 
						|
      return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  mf->sizep	= sizep;
 | 
						|
  mf->here      = 0;
 | 
						|
 | 
						|
  return Snew(mf, flags, &Smemfunctions);
 | 
						|
}
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	      STRINGS		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
/* MT: we assume these handles are not passed between threads
 | 
						|
*/
 | 
						|
 | 
						|
static ssize_t
 | 
						|
Sread_string(void *handle, char *buf, size_t size)
 | 
						|
{ (void)handle;
 | 
						|
  (void)buf;
 | 
						|
  (void)size;
 | 
						|
 | 
						|
  return 0;				/* signal EOF */
 | 
						|
}
 | 
						|
 | 
						|
static ssize_t
 | 
						|
Swrite_string(void *handle, char *buf, size_t size)
 | 
						|
{ (void)handle;
 | 
						|
  (void)buf;
 | 
						|
  (void)size;
 | 
						|
 | 
						|
  errno = ENOSPC;			/* signal error */
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
Sclose_string(void *handle)
 | 
						|
{ IOSTREAM *s = handle;
 | 
						|
 | 
						|
  if ( s->flags & SIO_OUTPUT )
 | 
						|
  { if ( s->bufp < s->limitp )
 | 
						|
    { *s->bufp++ = '\0';
 | 
						|
      return 0;
 | 
						|
    } else
 | 
						|
    { errno = ENOSPC;			/* signal error */
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  } else
 | 
						|
    return 0;				/* input string */
 | 
						|
}
 | 
						|
 | 
						|
IOFUNCTIONS Sstringfunctions =
 | 
						|
{ Sread_string,
 | 
						|
  Swrite_string,
 | 
						|
  (Sseek_function)0,
 | 
						|
  Sclose_string
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
IOSTREAM *
 | 
						|
Sopen_string(IOSTREAM *s, char *buf, size_t size, const char *mode)
 | 
						|
{ int flags = SIO_FBUF|SIO_USERBUF;
 | 
						|
 | 
						|
  if ( !s )
 | 
						|
  { if ( !(s = PL_malloc_uncollectable(sizeof(IOSTREAM))) ) /* TBD: Use GC */
 | 
						|
    { errno = ENOMEM;
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
  } else
 | 
						|
    flags |= SIO_STATIC;
 | 
						|
 | 
						|
  memset((char *)s, 0, sizeof(IOSTREAM));
 | 
						|
  s->timeout   = -1;
 | 
						|
  s->buffer    = buf;
 | 
						|
  s->bufp      = buf;
 | 
						|
  s->unbuffer  = buf;
 | 
						|
  s->handle    = s;			/* for Sclose_string() */
 | 
						|
  s->functions = &Sstringfunctions;
 | 
						|
  s->encoding  = ENC_ISO_LATIN_1;
 | 
						|
 | 
						|
  switch(*mode)
 | 
						|
  { case 'r':
 | 
						|
      if ( size == (size_t)-1 )
 | 
						|
	size = strlen(buf);
 | 
						|
      flags |= SIO_INPUT;
 | 
						|
      break;
 | 
						|
    case 'w':
 | 
						|
      flags |= SIO_OUTPUT;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      errno = EINVAL;
 | 
						|
      return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  s->flags  = flags;
 | 
						|
  s->limitp = &buf[size];
 | 
						|
  s->magic  = SIO_MAGIC;
 | 
						|
 | 
						|
  return s;
 | 
						|
}
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	 STANDARD HANDLES	*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
#define STDIO(n, f) { NULL, NULL, NULL, NULL, \
 | 
						|
		      EOF, SIO_MAGIC, 0, f, {0, 0, 0}, NULL, \
 | 
						|
		      (void *)(n), &Sttyfunctions, \
 | 
						|
		      0, NULL, \
 | 
						|
		      (void (*)(void *))0, NULL, \
 | 
						|
		      -1, \
 | 
						|
		      0, \
 | 
						|
		      ENC_ISO_LATIN_1 \
 | 
						|
		    }
 | 
						|
 | 
						|
#define SIO_STDIO (SIO_FILE|SIO_STATIC|SIO_NOCLOSE|SIO_ISATTY|SIO_TEXT)
 | 
						|
#define STDIO_STREAMS \
 | 
						|
  STDIO(0, SIO_STDIO|SIO_LBUF|SIO_INPUT|SIO_NOFEOF),	/* Sinput */ \
 | 
						|
  STDIO(1, SIO_STDIO|SIO_LBUF|SIO_OUTPUT|SIO_REPPL),	/* Soutput */ \
 | 
						|
  STDIO(2, SIO_STDIO|SIO_NBUF|SIO_OUTPUT|SIO_REPPL)	/* Serror */
 | 
						|
 | 
						|
 | 
						|
IOSTREAM S__iob[] =
 | 
						|
{ STDIO_STREAMS
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
static const IOSTREAM S__iob0[] =
 | 
						|
{ STDIO_STREAMS
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
static int S__initialised = FALSE;
 | 
						|
 | 
						|
void
 | 
						|
SinitStreams(void)
 | 
						|
{ if ( !S__initialised )
 | 
						|
  { int i;
 | 
						|
    IOENC enc;
 | 
						|
 | 
						|
    S__initialised = TRUE;
 | 
						|
    enc = initEncoding();
 | 
						|
 | 
						|
    for(i=0; i<=2; i++)
 | 
						|
    { IOSTREAM *s = &S__iob[i];
 | 
						|
 | 
						|
      if ( !isatty(i) && s->functions == &Sttyfunctions )
 | 
						|
      { s->flags &= ~SIO_ISATTY;
 | 
						|
	    s->functions = &Sfilefunctions; /* Check for pipe? */
 | 
						|
#if HAVE_SETLINEBUF
 | 
						|
	    /* make sure wwe buffer on new line for ttys, eg eclipse console */
 | 
						|
      } else if (i == 1) {
 | 
						|
	setlinebuf( stdout );
 | 
						|
#endif
 | 
						|
      }
 | 
						|
      if ( s > 0)
 | 
						|
	s->newline = SIO_NL_DOS;
 | 
						|
      if ( s->encoding == ENC_ISO_LATIN_1 )
 | 
						|
	s->encoding = enc;
 | 
						|
#ifdef O_PLMT
 | 
						|
      s->mutex = PL_malloc(sizeof(recursiveMutex));
 | 
						|
      recursiveMutexInit(s->mutex);
 | 
						|
#endif
 | 
						|
#if CRLF_MAPPING
 | 
						|
      _setmode(i, O_BINARY);
 | 
						|
      s->newline = SIO_NL_DOS;
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
#ifdef __WINDOWS__
 | 
						|
    pt_init();				/* init popen() issues */
 | 
						|
#endif
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
IOSTREAM *
 | 
						|
S__getiob(void)
 | 
						|
{ return S__iob;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	       HOOKS		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
This allows external packages (Prolog itself) to monitor the destruction
 | 
						|
of streams.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
typedef struct _close_hook
 | 
						|
{ struct _close_hook *next;
 | 
						|
  void (*hook)(IOSTREAM *s);
 | 
						|
} close_hook;
 | 
						|
 | 
						|
static close_hook *close_hooks;
 | 
						|
 | 
						|
static void
 | 
						|
run_close_hooks(IOSTREAM *s)
 | 
						|
{ close_hook *p;
 | 
						|
 | 
						|
  for(p=close_hooks; p; p = p->next)
 | 
						|
    (*p->hook)(s);
 | 
						|
 | 
						|
  if ( s->close_hook )
 | 
						|
    (*s->close_hook)(s->closure);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
Sclosehook(void (*hook)(IOSTREAM *s))
 | 
						|
{ close_hook *h = malloc(sizeof(*h));
 | 
						|
 | 
						|
  if ( !h )
 | 
						|
    return -1;
 | 
						|
  h->next = close_hooks;
 | 
						|
  h->hook = hook;
 | 
						|
  close_hooks = h;
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	       CLEANUP		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
void
 | 
						|
Sreset(void)
 | 
						|
{ IOSTREAM *s;
 | 
						|
 | 
						|
  if ( (s=Sinput) && s->magic == SIO_MAGIC )
 | 
						|
  { s->bufp = s->limitp = s->buffer;
 | 
						|
  }
 | 
						|
  if ( (s=Soutput) && s->magic == SIO_MAGIC )
 | 
						|
  { s->bufp = s->buffer;
 | 
						|
  }
 | 
						|
  if ( (s=Serror) && s->magic == SIO_MAGIC )
 | 
						|
  { s->bufp = s->buffer;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
Scleanup(void)
 | 
						|
{ close_hook *p, *next;
 | 
						|
  int i;
 | 
						|
 | 
						|
  for(p=close_hooks; p; p=next)
 | 
						|
  { next = p->next;
 | 
						|
    free(p);
 | 
						|
  }
 | 
						|
 | 
						|
  close_hooks = NULL;
 | 
						|
 | 
						|
  for(i=0; i<=2; i++)
 | 
						|
  { IOSTREAM *s = &S__iob[i];
 | 
						|
 | 
						|
    s->bufp = s->buffer;		/* avoid actual flush */
 | 
						|
    S__removebuf(s);
 | 
						|
 | 
						|
#ifdef O_PLMT
 | 
						|
    if ( S__iob[i].mutex )
 | 
						|
    { recursiveMutex *m = S__iob[i].mutex;
 | 
						|
 | 
						|
      S__iob[i].mutex = NULL;
 | 
						|
      recursiveMutexDelete(m);
 | 
						|
      PL_free(m);
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    *s = S__iob0[i];			/* re-initialise */
 | 
						|
  }
 | 
						|
 | 
						|
  S__initialised = FALSE;
 | 
						|
}
 |