1661 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1661 lines
		
	
	
		
			34 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-2013, 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
 | 
						|
*/
 | 
						|
 | 
						|
/*  Modified (M) 1993 Dave Sherratt  */
 | 
						|
 | 
						|
/*#define O_DEBUG 1*/
 | 
						|
 | 
						|
#if OS2 && EMX
 | 
						|
#include <os2.h>                /* this has to appear before pl-incl.h */
 | 
						|
#endif
 | 
						|
 | 
						|
//@{
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Solaris has asctime_r() with 3 arguments. Using _POSIX_PTHREAD_SEMANTICS
 | 
						|
is supposed to give the POSIX standard one.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
#if defined(__sun__) || defined(__sun)
 | 
						|
#define _POSIX_PTHREAD_SEMANTICS 1
 | 
						|
#endif
 | 
						|
 | 
						|
#define __MINGW_USE_VC2005_COMPAT		/* Get Windows time_t as 64-bit */
 | 
						|
 | 
						|
#include "pl-incl.h"
 | 
						|
#include "pl-ctype.h"
 | 
						|
#include "pl-utf8.h"
 | 
						|
#include <stdlib.h>
 | 
						|
#include <math.h>
 | 
						|
#include <stdio.h>		/* rename() and remove() prototypes */
 | 
						|
 | 
						|
#if TIME_WITH_SYS_TIME
 | 
						|
# include <sys/time.h>
 | 
						|
# include <time.h>
 | 
						|
#else
 | 
						|
# if HAVE_SYS_TIME_H
 | 
						|
#  include <sys/time.h>
 | 
						|
# else
 | 
						|
#  include <time.h>
 | 
						|
# endif
 | 
						|
#endif
 | 
						|
 | 
						|
#if HAVE_SYS_STAT_H
 | 
						|
#include <sys/stat.h>
 | 
						|
#endif
 | 
						|
#if HAVE_ALLOCA_H
 | 
						|
#include <alloca.h>
 | 
						|
#endif
 | 
						|
#ifdef O_XOS
 | 
						|
#define statstruct struct _stati64
 | 
						|
#else
 | 
						|
#define statstruct struct stat
 | 
						|
#define statfunc stat
 | 
						|
#endif
 | 
						|
#if HAVE_PWD_H
 | 
						|
#include <pwd.h>
 | 
						|
#endif
 | 
						|
#if HAVE_VFORK_H
 | 
						|
#include <vfork.h>
 | 
						|
#endif
 | 
						|
#ifdef HAVE_UNISTD_H
 | 
						|
#include <unistd.h>
 | 
						|
#endif
 | 
						|
#ifdef HAVE_SYS_FILE_H
 | 
						|
#include <sys/file.h>
 | 
						|
#endif
 | 
						|
#if defined(HAVE_SYS_RESOURCE_H)
 | 
						|
#include <sys/resource.h>
 | 
						|
#endif
 | 
						|
#ifdef HAVE_FTIME
 | 
						|
#include <sys/timeb.h>
 | 
						|
#endif
 | 
						|
#include <time.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#ifndef __WATCOMC__			/* appears a conflict */
 | 
						|
#include <errno.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(__WATCOMC__)
 | 
						|
#include <io.h>
 | 
						|
#include <dos.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#if OS2 && EMX
 | 
						|
static double initial_time;
 | 
						|
#endif /* OS2 */
 | 
						|
 | 
						|
#define LOCK()   PL_LOCK(L_OS)
 | 
						|
#define UNLOCK() PL_UNLOCK(L_OS)
 | 
						|
 | 
						|
static void	initEnviron(void);
 | 
						|
 | 
						|
#ifndef DEFAULT_PATH
 | 
						|
#define DEFAULT_PATH "/bin:/usr/bin"
 | 
						|
#endif
 | 
						|
 | 
						|
/** shell(+Command:text, -Status:integer) is det.
 | 
						|
 | 
						|
Run an external command and wait for its completion.
 | 
						|
*/
 | 
						|
 | 
						|
static
 | 
						|
PRED_IMPL("shell", 2, shell, 0)
 | 
						|
{ GET_LD
 | 
						|
  char *cmd;
 | 
						|
 | 
						|
  if ( PL_get_chars(A1, &cmd, CVT_ALL|REP_FN|CVT_EXCEPTION) )
 | 
						|
  { int rval = System(cmd);
 | 
						|
 | 
						|
    return PL_unify_integer(A2, rval);
 | 
						|
  }
 | 
						|
 | 
						|
  fail;
 | 
						|
}
 | 
						|
 | 
						|
		/********************************
 | 
						|
		*         INITIALISATION        *
 | 
						|
		*********************************/
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
    bool initOs()
 | 
						|
 | 
						|
    Initialise the OS dependant functions.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
bool
 | 
						|
initOs(void)
 | 
						|
{ GET_LD
 | 
						|
 | 
						|
    GD->statistics.start_time = WallTime();
 | 
						|
 | 
						|
  initEnviron();
 | 
						|
 | 
						|
#ifdef __WINDOWS__
 | 
						|
  setPrologFlagMask(PLFLAG_FILE_CASE_PRESERVING);
 | 
						|
#else
 | 
						|
  setPrologFlagMask(PLFLAG_FILE_CASE);
 | 
						|
  setPrologFlagMask(PLFLAG_FILE_CASE_PRESERVING);
 | 
						|
#endif
 | 
						|
 | 
						|
  DEBUG(1, Sdprintf("OS:done\n"));
 | 
						|
 | 
						|
  succeed;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
cleanupOs(void)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
		/********************************
 | 
						|
		*            OS ERRORS          *
 | 
						|
		*********************************/
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
    char *OsError()
 | 
						|
	Return a char *, holding a description of the last OS call error.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
char *
 | 
						|
OsError(void)
 | 
						|
{
 | 
						|
#ifdef HAVE_STRERROR
 | 
						|
#ifdef __WINDOWS__
 | 
						|
  return strerror(_xos_errno());
 | 
						|
#else
 | 
						|
  return strerror(errno);
 | 
						|
#endif
 | 
						|
#else /*HAVE_STRERROR*/
 | 
						|
static char errmsg[64];
 | 
						|
 | 
						|
#ifdef __unix__
 | 
						|
  extern int sys_nerr;
 | 
						|
#if !EMX
 | 
						|
  extern char *sys_errlist[];
 | 
						|
#endif
 | 
						|
  extern int errno;
 | 
						|
 | 
						|
  if ( errno < sys_nerr )
 | 
						|
    return sys_errlist[errno];
 | 
						|
#endif
 | 
						|
 | 
						|
  Ssprintf(errmsg, "Unknown Error (%d)", errno);
 | 
						|
  return errmsg;
 | 
						|
#endif /*HAVE_STRERROR*/
 | 
						|
}
 | 
						|
 | 
						|
		/********************************
 | 
						|
		*    PROCESS CHARACTERISTICS    *
 | 
						|
		*********************************/
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
    double CpuTime(cputime_kind)
 | 
						|
 | 
						|
    Returns a floating point number, representing the amount  of  (user)
 | 
						|
    CPU-seconds  used  by the process Prolog is in.  For systems that do
 | 
						|
    not allow you to obtain this information  you  may  wish  to  return
 | 
						|
    elapsed  time  since Prolog was started, as this function is used to
 | 
						|
    by consult/1 and time/1 to determine the amount of CPU time used  to
 | 
						|
    consult a file or to execute a query.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
#ifdef HAVE_CLOCK_GETTIME
 | 
						|
#define timespec_to_double(ts) \
 | 
						|
	((double)(ts).tv_sec + (double)(ts).tv_nsec/(double)1000000000.0)
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef __WINDOWS__			/* defined in pl-nt.c */
 | 
						|
 | 
						|
#ifdef HAVE_TIMES
 | 
						|
#include <sys/times.h>
 | 
						|
 | 
						|
#if defined(_SC_CLK_TCK)
 | 
						|
#define Hz ((int)sysconf(_SC_CLK_TCK))
 | 
						|
#else
 | 
						|
#ifdef HZ
 | 
						|
#  define Hz HZ
 | 
						|
#else
 | 
						|
#  define Hz 60				/* if nothing better: guess */
 | 
						|
#endif
 | 
						|
#endif /*_SC_CLK_TCK*/
 | 
						|
#endif /*HAVE_TIMES*/
 | 
						|
 | 
						|
 | 
						|
double
 | 
						|
CpuTime(cputime_kind which)
 | 
						|
{
 | 
						|
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID)
 | 
						|
#define CPU_TIME_DONE
 | 
						|
  struct timespec ts;
 | 
						|
  (void)which;
 | 
						|
 | 
						|
  if ( clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0 )
 | 
						|
    return timespec_to_double(ts);
 | 
						|
  return 0.0;
 | 
						|
#endif
 | 
						|
 | 
						|
#if !defined(CPU_TIME_DONE) && defined(HAVE_TIMES)
 | 
						|
#define CPU_TIME_DONE
 | 
						|
  struct tms t;
 | 
						|
  double used;
 | 
						|
  static int MTOK_got_hz = FALSE;
 | 
						|
  static double MTOK_hz;
 | 
						|
 | 
						|
  if ( !MTOK_got_hz )
 | 
						|
  { MTOK_hz = (double) Hz;
 | 
						|
    MTOK_got_hz++;
 | 
						|
  }
 | 
						|
  times(&t);
 | 
						|
 | 
						|
  switch( which )
 | 
						|
  { case CPU_USER:
 | 
						|
      used = (double) t.tms_utime / MTOK_hz;
 | 
						|
      break;
 | 
						|
  case CPU_SYSTEM:
 | 
						|
    default:				/* make compiler happy */
 | 
						|
      used = (double) t.tms_stime / MTOK_hz;
 | 
						|
  }
 | 
						|
 | 
						|
  if ( isnan(used) )			/* very dubious, but this */
 | 
						|
    used = 0.0;				/* happens when running under GDB */
 | 
						|
 | 
						|
  return used;
 | 
						|
#endif
 | 
						|
 | 
						|
#if !defined(CPU_TIME_DONE)
 | 
						|
  (void)which;
 | 
						|
 | 
						|
  return 0.0;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
#endif /*__WINDOWS__*/
 | 
						|
 | 
						|
 | 
						|
double
 | 
						|
WallTime(void)
 | 
						|
{ double stime;
 | 
						|
 | 
						|
#if HAVE_CLOCK_GETTIME
 | 
						|
  struct timespec tp;
 | 
						|
 | 
						|
  clock_gettime(CLOCK_REALTIME, &tp);
 | 
						|
  stime = timespec_to_double(tp);
 | 
						|
#else
 | 
						|
#ifdef HAVE_GETTIMEOFDAY
 | 
						|
  struct timeval tp;
 | 
						|
 | 
						|
  gettimeofday(&tp, NULL);
 | 
						|
  stime = (double)tp.tv_sec + (double)tp.tv_usec/1000000.0;
 | 
						|
#else
 | 
						|
#ifdef HAVE_FTIME
 | 
						|
  struct timeb tb;
 | 
						|
 | 
						|
  ftime(&tb);
 | 
						|
  stime = (double)tb.time + (double)tb.millitm/1000.0;
 | 
						|
#else
 | 
						|
  stime = (double)time((time_t *)NULL);
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
  return stime;
 | 
						|
}
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	      FEATURES		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
#ifndef __WINDOWS__			/* Windows version in pl-nt.c */
 | 
						|
 | 
						|
#ifdef HAVE_SC_NPROCESSORS_CONF
 | 
						|
static int
 | 
						|
CpuCount()
 | 
						|
{
 | 
						|
  return sysconf(_SC_NPROCESSORS_CONF);
 | 
						|
}
 | 
						|
#else
 | 
						|
 | 
						|
#ifdef PROCFS_CPUINFO
 | 
						|
static int
 | 
						|
CpuCount()
 | 
						|
{ FILE *fd = fopen("/proc/cpuinfo", "r");
 | 
						|
 | 
						|
  if ( fd )
 | 
						|
  { char buf[256];
 | 
						|
    int count = 0;
 | 
						|
 | 
						|
    while(fgets(buf, sizeof(buf)-1, fd))
 | 
						|
    { char *vp;
 | 
						|
 | 
						|
      if ( (vp = strchr(buf, ':')) )
 | 
						|
      { char *en;
 | 
						|
 | 
						|
	for(en=vp; en > buf && en[-1] <= ' '; en--)
 | 
						|
	  ;
 | 
						|
	*en = EOS;
 | 
						|
	DEBUG(2, Sdprintf("Got %s = %s\n", buf, vp+2));
 | 
						|
	if ( streq("processor", buf) && isDigit(vp[2]) )
 | 
						|
	{ int cpu = atoi(vp+2);
 | 
						|
 | 
						|
	  if ( cpu+1 > count )
 | 
						|
	    count = cpu+1;
 | 
						|
	}
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    fclose(fd);
 | 
						|
    return count;
 | 
						|
  }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
#else /*PROCFS_CPUINFO*/
 | 
						|
 | 
						|
#ifdef HAVE_SYSCTLBYNAME	/* MacOS X */
 | 
						|
 | 
						|
#include <sys/param.h>
 | 
						|
#include <sys/sysctl.h>
 | 
						|
 | 
						|
int
 | 
						|
CpuCount(void)
 | 
						|
{ int     count ;
 | 
						|
  size_t  size=sizeof(count) ;
 | 
						|
 | 
						|
  if ( sysctlbyname("hw.ncpu", &count, &size, NULL, 0) )
 | 
						|
    return 0;
 | 
						|
 | 
						|
  return count;
 | 
						|
}
 | 
						|
 | 
						|
#else
 | 
						|
 | 
						|
#define CpuCount() 0
 | 
						|
 | 
						|
#endif /*sysctlbyname*/
 | 
						|
 | 
						|
#endif /*PROCFS_CPUINFO*/
 | 
						|
 | 
						|
#endif /*HAVE_SC_NPROCESSORS_CONF*/
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
setOSPrologFlags(void)
 | 
						|
{ int cpu_count = CpuCount();
 | 
						|
 | 
						|
  if ( cpu_count > 0 )
 | 
						|
    PL_set_prolog_flag("cpu_count", PL_INTEGER, cpu_count);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	       MEMORY		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
uintptr_t
 | 
						|
UsedMemory(void)
 | 
						|
{ //GET_LD
 | 
						|
 | 
						|
#if defined(HAVE_GETRUSAGE) && defined(HAVE_RU_IDRSS)
 | 
						|
  struct rusage usage;
 | 
						|
 | 
						|
  if ( getrusage(RUSAGE_SELF, &usage) == 0 &&
 | 
						|
       usage.ru_idrss )
 | 
						|
  { return usage.ru_idrss;		/* total unshared data */
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  return (usedStack(global) +
 | 
						|
	  usedStack(local) +
 | 
						|
	  usedStack(trail));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
uintptr_t
 | 
						|
FreeMemory(void)
 | 
						|
{
 | 
						|
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_DATA)
 | 
						|
  uintptr_t used = UsedMemory();
 | 
						|
  struct rlimit limit;
 | 
						|
 | 
						|
  if ( getrlimit(RLIMIT_DATA, &limit) == 0 )
 | 
						|
    return limit.rlim_cur - used;
 | 
						|
#endif
 | 
						|
 | 
						|
  return 0L;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
		/********************************
 | 
						|
		*           ARITHMETIC          *
 | 
						|
		*********************************/
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
    uint64_t _PL_Random()
 | 
						|
 | 
						|
    Return a random number. Used for arithmetic only. More trouble. On
 | 
						|
    some systems (__WINDOWS__) the seed of rand() is thread-local, while on
 | 
						|
    others it is global.  We appear to have the choice between
 | 
						|
 | 
						|
	# srand()/rand()
 | 
						|
	Differ in MT handling, often bad distribution
 | 
						|
 | 
						|
	# srandom()/random()
 | 
						|
	Not portable, not MT-Safe but much better distribution
 | 
						|
 | 
						|
	# drand48() and friends
 | 
						|
	Depreciated according to Linux manpage, suggested by Solaris
 | 
						|
	manpage.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
void
 | 
						|
setRandom(unsigned int *seedp)
 | 
						|
{ unsigned int seed;
 | 
						|
 | 
						|
  if ( seedp )
 | 
						|
  { seed = *seedp;
 | 
						|
  } else
 | 
						|
  {
 | 
						|
#ifdef __WINDOWS__
 | 
						|
    seed = (unsigned int)GetTickCount();
 | 
						|
#else
 | 
						|
#ifdef HAVE_GETTIMEOFDAY
 | 
						|
     struct timeval tp;
 | 
						|
 | 
						|
     gettimeofday(&tp, NULL);
 | 
						|
     seed = (unsigned int)(tp.tv_sec + tp.tv_usec);
 | 
						|
#else
 | 
						|
     seed = (unsigned int)time((time_t *) NULL);
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
  }
 | 
						|
#if HAVE_SRANDOM
 | 
						|
  srandom(seed);
 | 
						|
#elif HAVE_SRAND
 | 
						|
  srand(seed);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
uint64_t
 | 
						|
_PL_Random(void)
 | 
						|
{ GET_LD
 | 
						|
 | 
						|
  if ( !LD->os.rand_initialised )
 | 
						|
  { setRandom(NULL);
 | 
						|
    LD->os.rand_initialised = TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef HAVE_RANDOM
 | 
						|
  { uint64_t l = random();
 | 
						|
 | 
						|
    l ^= (uint64_t)random()<<15;
 | 
						|
    l ^= (uint64_t)random()<<30;
 | 
						|
    l ^= (uint64_t)random()<<45;
 | 
						|
 | 
						|
    return l;
 | 
						|
  }
 | 
						|
#else
 | 
						|
  { uint64_t l = rand();			/* 0<n<2^15-1 */
 | 
						|
 | 
						|
    l ^= (uint64_t)rand()<<15;
 | 
						|
    l ^= (uint64_t)rand()<<30;
 | 
						|
    l ^= (uint64_t)rand()<<45;
 | 
						|
 | 
						|
    return l;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
		/********************************
 | 
						|
		*        TIME CONVERSION        *
 | 
						|
		*********************************/
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
    struct tm *PL_localtime_r(time_t time, struct tm *r)
 | 
						|
 | 
						|
    Convert tunlime in Unix internal form (seconds since Jan 1 1970) into a
 | 
						|
    structure providing easier access to the time.
 | 
						|
 | 
						|
    For non-Unix systems: struct time is supposed  to  look  like  this.
 | 
						|
    Move  This  definition to pl-os.h and write the conversion functions
 | 
						|
    here.
 | 
						|
 | 
						|
    struct tm {
 | 
						|
	int	tm_sec;		/ * second in the minute (0-59)* /
 | 
						|
	int	tm_min;		/ * minute in the hour (0-59) * /
 | 
						|
	int	tm_hour;	/ * hour of the day (0-23) * /
 | 
						|
	int	tm_mday;	/ * day of the month (1-31) * /
 | 
						|
	int	tm_mon;		/ * month of the year (1-12) * /
 | 
						|
	int	tm_year;	/ * year (0 = 1900) * /
 | 
						|
	int	tm_wday;	/ * day in the week (1-7, 1 = sunday) * /
 | 
						|
	int	tm_yday;	/ * day in the year (0-365) * /
 | 
						|
	int	tm_isdst;	/ * daylight saving time info * /
 | 
						|
    };
 | 
						|
 | 
						|
    time_t Time()
 | 
						|
 | 
						|
    Return time in seconds after Jan 1 1970 (Unix' time notion).
 | 
						|
 | 
						|
Note: MinGW has localtime_r(),  but  it  is   not  locked  and  thus not
 | 
						|
thread-safe. MinGW does not have localtime_s(), but   we  test for it in
 | 
						|
configure.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
struct tm *
 | 
						|
PL_localtime_r(const time_t *t, struct tm *r)
 | 
						|
{
 | 
						|
#ifdef HAVE_LOCALTIME_R
 | 
						|
  return localtime_r(t, r);
 | 
						|
#else
 | 
						|
#ifdef HAVE_LOCALTIME_S
 | 
						|
  return localtime_s(r, t) == EINVAL ? NULL : t;
 | 
						|
#else
 | 
						|
  struct tm *rc;
 | 
						|
 | 
						|
  LOCK();
 | 
						|
  if ( (rc = localtime(t)) )
 | 
						|
    *r = *rc;
 | 
						|
  else
 | 
						|
    r = NULL;
 | 
						|
  UNLOCK();
 | 
						|
 | 
						|
  return r;
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
char *
 | 
						|
PL_asctime_r(const struct tm *tm, char *buf)
 | 
						|
{
 | 
						|
#ifdef HAVE_ASCTIME_R
 | 
						|
  return asctime_r(tm, buf);
 | 
						|
#else
 | 
						|
  char *rc;
 | 
						|
 | 
						|
  LOCK();
 | 
						|
  if ( (rc = asctime(tm)) )
 | 
						|
    strcpy(buf, rc);
 | 
						|
  else
 | 
						|
    buf = NULL;
 | 
						|
  UNLOCK();
 | 
						|
 | 
						|
  return buf;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	      TERMINAL		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
#ifdef HAVE_TCSETATTR
 | 
						|
#include <termios.h>
 | 
						|
#include <unistd.h>
 | 
						|
#define O_HAVE_TERMIO 1
 | 
						|
#else /*HAVE_TCSETATTR*/
 | 
						|
#ifdef HAVE_SYS_TERMIO_H
 | 
						|
#include <sys/termio.h>
 | 
						|
#define termios termio
 | 
						|
#define O_HAVE_TERMIO 1
 | 
						|
#else
 | 
						|
#ifdef HAVE_SYS_TERMIOS_H
 | 
						|
#include <sys/termios.h>
 | 
						|
#define O_HAVE_TERMIO 1
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
#endif /*HAVE_TCSETATTR*/
 | 
						|
 | 
						|
typedef struct tty_state
 | 
						|
{
 | 
						|
#if defined(O_HAVE_TERMIO)
 | 
						|
  struct termios tab;
 | 
						|
#elif defined(HAVE_SGTTYB)
 | 
						|
  struct sgttyb tab;
 | 
						|
#else
 | 
						|
  int tab;				/* empty is not allowed */
 | 
						|
#endif
 | 
						|
} tty_state;
 | 
						|
 | 
						|
#define TTY_STATE(buf) (((tty_state*)(buf->state))->tab)
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
			TERMINAL IO MANIPULATION
 | 
						|
 | 
						|
ResetStdin()
 | 
						|
    Clear the Sinput buffer after a saved state.  Only necessary
 | 
						|
    if O_SAVE is defined.
 | 
						|
 | 
						|
PushTty(IOSTREAM *s, ttybuf *buf, int state)
 | 
						|
    Push the tty to the specified state and save the old state in
 | 
						|
    buf.
 | 
						|
 | 
						|
PopTty(IOSTREAM *s, ttybuf *buf)
 | 
						|
    Restore the tty state.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
static void
 | 
						|
ResetStdin(void)
 | 
						|
{ Sinput->limitp = Sinput->bufp = Sinput->buffer;
 | 
						|
  if ( !GD->os.org_terminal.read )
 | 
						|
    GD->os.org_terminal = *Sinput->functions;
 | 
						|
}
 | 
						|
 | 
						|
static ssize_t
 | 
						|
Sread_terminal(void *handle, char *buf, size_t size)
 | 
						|
{ GET_LD
 | 
						|
  intptr_t h = (intptr_t)handle;
 | 
						|
  int fd = (int)h;
 | 
						|
  source_location oldsrc = LD->read_source;
 | 
						|
 | 
						|
  if ( Soutput && True(Soutput, SIO_ISATTY) )
 | 
						|
  { if ( LD->prompt.next && ttymode != TTY_RAW )
 | 
						|
      PL_write_prompt(TRUE);
 | 
						|
    else
 | 
						|
      Sflush(Suser_output);
 | 
						|
  }
 | 
						|
 | 
						|
  PL_dispatch(fd, PL_DISPATCH_WAIT);
 | 
						|
  size = (*GD->os.org_terminal.read)(handle, buf, size);
 | 
						|
 | 
						|
  if ( size == 0 )			/* end-of-file */
 | 
						|
  { if ( fd == 0 )
 | 
						|
    { Sclearerr(Suser_input);
 | 
						|
      LD->prompt.next = TRUE;
 | 
						|
    }
 | 
						|
  } else if ( size > 0 && buf[size-1] == '\n' )
 | 
						|
    LD->prompt.next = TRUE;
 | 
						|
 | 
						|
  LD->read_source = oldsrc;
 | 
						|
 | 
						|
  return size;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ResetTty(void)
 | 
						|
{ GET_LD
 | 
						|
  startCritical;
 | 
						|
  ResetStdin();
 | 
						|
 | 
						|
  if ( !GD->os.iofunctions.read )
 | 
						|
  { GD->os.iofunctions       = *Sinput->functions;
 | 
						|
    GD->os.iofunctions.read  = Sread_terminal;
 | 
						|
 | 
						|
    Sinput->functions  =
 | 
						|
    Soutput->functions =
 | 
						|
    Serror->functions  = &GD->os.iofunctions;
 | 
						|
  }
 | 
						|
  LD->prompt.next = TRUE;
 | 
						|
  endCritical;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef O_HAVE_TERMIO			/* sys/termios.h or sys/termio.h */
 | 
						|
 | 
						|
#ifndef HAVE_TCSETATTR
 | 
						|
#ifndef NO_SYS_IOCTL_H_WITH_SYS_TERMIOS_H
 | 
						|
#include <sys/ioctl.h>
 | 
						|
#endif
 | 
						|
#ifndef TIOCGETA
 | 
						|
#define TIOCGETA TCGETA
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
bool
 | 
						|
PushTty(IOSTREAM *s, ttybuf *buf, int mode)
 | 
						|
{ GET_LD
 | 
						|
  struct termios tio;
 | 
						|
  int fd;
 | 
						|
 | 
						|
  buf->mode  = ttymode;
 | 
						|
  buf->state = NULL;
 | 
						|
  ttymode    = mode;
 | 
						|
 | 
						|
  if ( (fd = Sfileno(s)) < 0 || !isatty(fd) )
 | 
						|
    succeed;				/* not a terminal */
 | 
						|
  if ( !truePrologFlag(PLFLAG_TTY_CONTROL) )
 | 
						|
    succeed;
 | 
						|
 | 
						|
  buf->state = allocHeapOrHalt(sizeof(tty_state));
 | 
						|
 | 
						|
#ifdef HAVE_TCSETATTR
 | 
						|
  if ( tcgetattr(fd, &TTY_STATE(buf)) )	/* save the old one */
 | 
						|
    fail;
 | 
						|
#else
 | 
						|
  if ( ioctl(fd, TIOCGETA, &TTY_STATE(buf)) )	/* save the old one */
 | 
						|
    fail;
 | 
						|
#endif
 | 
						|
 | 
						|
  tio = TTY_STATE(buf);
 | 
						|
 | 
						|
  switch( mode )
 | 
						|
  { case TTY_RAW:
 | 
						|
#if defined(HAVE_TCSETATTR) && defined(HAVE_CFMAKERAW)
 | 
						|
	cfmakeraw(&tio);
 | 
						|
	tio.c_oflag = TTY_STATE(buf).c_oflag;	/* donot change output modes */
 | 
						|
	tio.c_lflag |= ISIG;
 | 
						|
#else
 | 
						|
	tio.c_lflag &= ~(ECHO|ICANON);
 | 
						|
#endif
 | 
						|
					/* OpenBSD requires this anyhow!? */
 | 
						|
					/* Bug in OpenBSD or must we? */
 | 
						|
					/* Could this do any harm? */
 | 
						|
	tio.c_cc[VTIME] = 0, tio.c_cc[VMIN] = 1;
 | 
						|
	break;
 | 
						|
    case TTY_OUTPUT:
 | 
						|
	tio.c_oflag |= (OPOST|ONLCR);
 | 
						|
        break;
 | 
						|
    case TTY_SAVE:
 | 
						|
        succeed;
 | 
						|
    default:
 | 
						|
	sysError("Unknown PushTty() mode: %d", mode);
 | 
						|
	/*NOTREACHED*/
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef HAVE_TCSETATTR
 | 
						|
  if ( tcsetattr(fd, TCSANOW, &tio) != 0 )
 | 
						|
  { static int MTOK_warned;			/* MT-OK */
 | 
						|
 | 
						|
    if ( !MTOK_warned++ )
 | 
						|
      warning("Failed to set terminal: %s", OsError());
 | 
						|
  }
 | 
						|
#else
 | 
						|
#ifdef TIOCSETAW
 | 
						|
  ioctl(fd, TIOCSETAW, &tio);
 | 
						|
#else
 | 
						|
  ioctl(fd, TCSETAW, &tio);
 | 
						|
  ioctl(fd, TCXONC, (void *)1);
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
  succeed;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool
 | 
						|
PopTty(IOSTREAM *s, ttybuf *buf, int do_free)
 | 
						|
{ ttymode = buf->mode;
 | 
						|
 | 
						|
  if ( buf->state )
 | 
						|
  { int fd = Sfileno(s);
 | 
						|
 | 
						|
    if ( fd >= 0 )
 | 
						|
    {
 | 
						|
#ifdef HAVE_TCSETATTR
 | 
						|
      tcsetattr(fd, TCSANOW, &TTY_STATE(buf));
 | 
						|
#else
 | 
						|
#ifdef TIOCSETA
 | 
						|
      ioctl(fd, TIOCSETA, &TTY_STATE(buf));
 | 
						|
#else
 | 
						|
      ioctl(fd, TCSETA, &TTY_STATE(buf));
 | 
						|
      ioctl(fd, TCXONC, (void *)1);
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
    if ( do_free )
 | 
						|
    { freeHeap(buf->state, sizeof(tty_state));
 | 
						|
      buf->state = NULL;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  succeed;
 | 
						|
}
 | 
						|
 | 
						|
#else /* O_HAVE_TERMIO */
 | 
						|
 | 
						|
#ifdef HAVE_SGTTYB
 | 
						|
 | 
						|
bool
 | 
						|
PushTty(IOSTREAM *s, ttybuf *buf, int mode)
 | 
						|
{ struct sgttyb tio;
 | 
						|
  int fd;
 | 
						|
 | 
						|
  buf->mode = ttymode;
 | 
						|
  buf->state = NULL;
 | 
						|
  ttymode = mode;
 | 
						|
 | 
						|
  if ( (fd = Sfileno(s)) < 0 || !isatty(fd) )
 | 
						|
    succeed;				/* not a terminal */
 | 
						|
  if ( !truePrologFlag(PLFLAG_TTY_CONTROL) )
 | 
						|
    succeed;
 | 
						|
 | 
						|
  buf->state = allocHeapOrHalt(sizeof(tty_state));
 | 
						|
 | 
						|
  if ( ioctl(fd, TIOCGETP, &TTY_STATE(buf)) )  /* save the old one */
 | 
						|
    fail;
 | 
						|
  tio = TTY_STATE(buf);
 | 
						|
 | 
						|
  switch( mode )
 | 
						|
  { case TTY_RAW:
 | 
						|
      tio.sg_flags |= CBREAK;
 | 
						|
      tio.sg_flags &= ~ECHO;
 | 
						|
      break;
 | 
						|
    case TTY_OUTPUT:
 | 
						|
      tio.sg_flags |= (CRMOD);
 | 
						|
      break;
 | 
						|
    case TTY_SAVE:
 | 
						|
      succeed;
 | 
						|
    default:
 | 
						|
      sysError("Unknown PushTty() mode: %d", mode);
 | 
						|
      /*NOTREACHED*/
 | 
						|
  }
 | 
						|
 | 
						|
  ioctl(fd, TIOCSETP,  &tio);
 | 
						|
  ioctl(fd, TIOCSTART, NULL);
 | 
						|
 | 
						|
  succeed;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool
 | 
						|
PopTty(IOSTREAM *s, ttybuf *buf, int do_free)
 | 
						|
{ ttymode = buf->mode;
 | 
						|
 | 
						|
  if ( buf->state )
 | 
						|
  { int fd = Sfileno(s);
 | 
						|
 | 
						|
    if ( fd >= 0 )
 | 
						|
    { ioctl(fd, TIOCSETP,  &buf->tab);
 | 
						|
      ioctl(fd, TIOCSTART, NULL);
 | 
						|
    }
 | 
						|
 | 
						|
    if ( do_free )
 | 
						|
    { freeHeap(buf->state, sizeof(tty_state));
 | 
						|
      buf->state = NULL;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  succeed;
 | 
						|
}
 | 
						|
 | 
						|
#else /*HAVE_SGTTYB*/
 | 
						|
 | 
						|
bool
 | 
						|
PushTty(IOSTREAM *s, ttybuf *buf, int mode)
 | 
						|
{ buf->mode = ttymode;
 | 
						|
  ttymode = mode;
 | 
						|
 | 
						|
  succeed;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool
 | 
						|
PopTty(IOSTREAM *s, ttybuf *buf, int do_free)
 | 
						|
{ GET_LD
 | 
						|
  ttymode = buf->mode;
 | 
						|
  if ( ttymode != TTY_RAW )
 | 
						|
    LD->prompt.next = TRUE;
 | 
						|
 | 
						|
  succeed;
 | 
						|
}
 | 
						|
 | 
						|
#endif /*HAVE_SGTTYB*/
 | 
						|
#endif /*O_HAVE_TERMIO*/
 | 
						|
 | 
						|
 | 
						|
		/********************************
 | 
						|
		*      ENVIRONMENT CONTROL      *
 | 
						|
		*********************************/
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Simple  library  to  manipulate  the    OS   environment.  The  modified
 | 
						|
environment will be passed to  child  processes   and  the  can  also be
 | 
						|
requested via getenv/2 from Prolog. Functions
 | 
						|
 | 
						|
    int Setenv(name, value)
 | 
						|
         char *name, *value;
 | 
						|
 | 
						|
    Set the OS environment variable with name `name'.   If  it  exists
 | 
						|
    its  value  is  changed, otherwise a new entry in the environment is
 | 
						|
    created.  The return value is a pointer to the old value, or NULL if
 | 
						|
    the variable is new.
 | 
						|
 | 
						|
    int Unsetenv(name)
 | 
						|
         char *name;
 | 
						|
 | 
						|
    Delete a variable from the environment.  Return  value  is  the  old
 | 
						|
    value, or NULL if the variable did not exist.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
size_t
 | 
						|
getenv3(const char *name, char *buf, size_t len)
 | 
						|
{
 | 
						|
#if O_XOS
 | 
						|
  return _xos_getenv(name, buf, len);
 | 
						|
#else
 | 
						|
  char *s = getenv(name);
 | 
						|
  size_t l;
 | 
						|
 | 
						|
  if ( s )
 | 
						|
  { if ( (l=strlen(s)) < len )
 | 
						|
      memcpy(buf, s, l+1);
 | 
						|
    else if ( len > 0 )
 | 
						|
      buf[0] = EOS;                     /* empty string if not fit */
 | 
						|
 | 
						|
    return l;
 | 
						|
  }
 | 
						|
 | 
						|
  return (size_t)-1;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
char *
 | 
						|
Getenv(const char *name, char *buf, size_t len)
 | 
						|
{ size_t l = getenv3(name, buf, len);
 | 
						|
 | 
						|
  if ( l != (size_t)-1 && l < len )
 | 
						|
    return buf;
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#if defined(HAVE_PUTENV) || defined(HAVE_SETENV)
 | 
						|
 | 
						|
int
 | 
						|
Setenv(char *name, char *value)
 | 
						|
{
 | 
						|
#ifdef HAVE_SETENV
 | 
						|
  if ( setenv(name, value, TRUE) != 0 )
 | 
						|
    return PL_error(NULL, 0, MSG_ERRNO, ERR_SYSCALL, "setenv");
 | 
						|
#else
 | 
						|
  char *buf;
 | 
						|
 | 
						|
  if ( *name == '\0' || strchr(name, '=') != NULL )
 | 
						|
  { errno = EINVAL;
 | 
						|
    return PL_error(NULL, 0, MSG_ERRNO, ERR_SYSCALL, "setenv");
 | 
						|
  }
 | 
						|
 | 
						|
  buf = alloca(strlen(name) + strlen(value) + 2);
 | 
						|
 | 
						|
  if ( buf )
 | 
						|
  { Ssprintf(buf, "%s=%s", name, value);
 | 
						|
 | 
						|
    if ( putenv(store_string(buf)) < 0 )
 | 
						|
      return PL_error(NULL, 0, MSG_ERRNO, ERR_SYSCALL, "setenv");
 | 
						|
  } else
 | 
						|
    return PL_error(NULL, 0, NULL, ERR_NOMEM);
 | 
						|
#endif
 | 
						|
  succeed;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
Unsetenv(char *name)
 | 
						|
{
 | 
						|
#ifdef HAVE_UNSETENV
 | 
						|
#ifdef VOID_UNSETENV
 | 
						|
  unsetenv(name);
 | 
						|
#else
 | 
						|
  if ( unsetenv(name) < 0 )
 | 
						|
    return PL_error(NULL, 0, MSG_ERRNO, ERR_SYSCALL, "unsetenv");
 | 
						|
#endif
 | 
						|
 | 
						|
  succeed;
 | 
						|
#else
 | 
						|
  if ( !getenv(name) )
 | 
						|
    succeed;
 | 
						|
 | 
						|
  return Setenv(name, "");
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
initEnviron()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
#else /*HAVE_PUTENV*/
 | 
						|
 | 
						|
extern char **environ;		/* Unix predefined environment */
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Grow the environment array by one and return the (possibly  moved)  base
 | 
						|
pointer to the new environment.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
forwards char	**growEnviron(char**, int);
 | 
						|
forwards char	*matchName(char *, char *);
 | 
						|
forwards void	setEntry(char **, char *, char *);
 | 
						|
 | 
						|
static char **
 | 
						|
growEnviron(char **e, int amount)
 | 
						|
{ static int filled;
 | 
						|
  static int size = -1;
 | 
						|
 | 
						|
  if ( amount == 0 )			/* reset after a dump */
 | 
						|
  { size = -1;
 | 
						|
    return e;
 | 
						|
  }
 | 
						|
 | 
						|
  if ( size < 0 )
 | 
						|
  { char **env, **e1, **e2;
 | 
						|
 | 
						|
    for(e1=e, filled=0; *e1; e1++, filled++)
 | 
						|
      ;
 | 
						|
    size = ROUND(filled+10+amount, 32);
 | 
						|
    env = (char **)PL_malloc(size * sizeof(char *));
 | 
						|
    for ( e1=e, e2=env; *e1; *e2++ = *e1++ )
 | 
						|
      ;
 | 
						|
    *e2 = (char *) NULL;
 | 
						|
    filled += amount;
 | 
						|
 | 
						|
    return env;
 | 
						|
  }
 | 
						|
 | 
						|
  filled += amount;
 | 
						|
  if ( filled + 1 > size )
 | 
						|
  { char **env, **e1, **e2;
 | 
						|
 | 
						|
    size += 32;
 | 
						|
    env = (char **)PL_realloc(e, size * sizeof(char *));
 | 
						|
    for ( e1=e, e2=env; *e1; *e2++ = *e1++ )
 | 
						|
      ;
 | 
						|
    *e2 = (char *) NULL;
 | 
						|
 | 
						|
    return env;
 | 
						|
  }
 | 
						|
 | 
						|
  return e;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
initEnviron(void)
 | 
						|
{ growEnviron(environ, 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static char *
 | 
						|
matchName(const char *e, const char *name)
 | 
						|
{ while( *name && *e == *name )
 | 
						|
    e++, name++;
 | 
						|
 | 
						|
  if ( (*e == '=' || *e == EOS) && *name == EOS )
 | 
						|
    return (*e == '=' ? e+1 : e);
 | 
						|
 | 
						|
  return (char *) NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
setEntry(char **e, char *name, char *value)
 | 
						|
{ size_t l = strlen(name);
 | 
						|
 | 
						|
  *e = PL_malloc_atomic(l + strlen(value) + 2);
 | 
						|
  strcpy(*e, name);
 | 
						|
  e[0][l++] = '=';
 | 
						|
  strcpy(&e[0][l], value);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
char *
 | 
						|
Setenv(char *name, char *value)
 | 
						|
{ char **e;
 | 
						|
  char *v;
 | 
						|
  int n;
 | 
						|
 | 
						|
  for(n=0, e=environ; *e; e++, n++)
 | 
						|
  { if ( (v=matchName(*e, name)) != NULL )
 | 
						|
    { if ( !streq(v, value) )
 | 
						|
        setEntry(e, name, value);
 | 
						|
      return v;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  environ = growEnviron(environ, 1);
 | 
						|
  setEntry(&environ[n], name, value);
 | 
						|
  environ[n+1] = (char *) NULL;
 | 
						|
 | 
						|
  return (char *) NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
char *
 | 
						|
Unsetenv(char *name)
 | 
						|
{ char **e;
 | 
						|
  char *v;
 | 
						|
  int n;
 | 
						|
 | 
						|
  for(n=0, e=environ; *e; e++, n++)
 | 
						|
  { if ( (v=matchName(*e, name)) != NULL )
 | 
						|
    { environ = growEnviron(environ, -1);
 | 
						|
      e = &environ[n];
 | 
						|
      do
 | 
						|
      { e[0] = e[1];
 | 
						|
        e++;
 | 
						|
      } while(*e);
 | 
						|
 | 
						|
      return v;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return (char *) NULL;
 | 
						|
}
 | 
						|
 | 
						|
#endif /*HAVE_PUTENV*/
 | 
						|
 | 
						|
		/********************************
 | 
						|
		*       SYSTEM PROCESSES        *
 | 
						|
		*********************************/
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
    int System(command)
 | 
						|
	char *command;
 | 
						|
 | 
						|
    Invoke a command on the operating system.  The return value  is  the
 | 
						|
    exit  status  of  the  command.   Return  value  0 implies succesful
 | 
						|
    completion. If you are not running Unix your C-library might provide
 | 
						|
    an alternative.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
#ifdef __unix__
 | 
						|
#define SPECIFIC_SYSTEM 1
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
According to the autoconf docs HAVE_SYS_WAIT_H   is set if sys/wait.h is
 | 
						|
defined *and* is POSIX.1 compliant,  which   implies  it uses int status
 | 
						|
argument to wait()
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
#ifdef HAVE_SYS_WAIT_H
 | 
						|
#undef UNION_WAIT
 | 
						|
#include <sys/wait.h>
 | 
						|
#define wait_t int
 | 
						|
 | 
						|
#ifndef WEXITSTATUS
 | 
						|
# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
 | 
						|
#endif
 | 
						|
#ifndef WIFEXITED
 | 
						|
# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
 | 
						|
#endif
 | 
						|
 | 
						|
#else /*HAVE_SYS_WAIT_H*/
 | 
						|
 | 
						|
#ifdef UNION_WAIT			/* Old BSD style wait */
 | 
						|
#include <sys/wait.h>
 | 
						|
#define wait_t union wait
 | 
						|
 | 
						|
#ifndef WEXITSTATUS
 | 
						|
#define WEXITSTATUS(s) ((s).w_status)
 | 
						|
#endif
 | 
						|
#ifndef WTERMSIG
 | 
						|
#define WTERMSIG(s) ((s).w_status)
 | 
						|
#endif
 | 
						|
#endif /*UNION_WAIT*/
 | 
						|
 | 
						|
#endif /*HAVE_SYS_WAIT_H*/
 | 
						|
 | 
						|
typedef void (*sigf_t)(int sig);
 | 
						|
 | 
						|
int
 | 
						|
System(char *cmd)
 | 
						|
{ GET_LD
 | 
						|
  int pid;
 | 
						|
  char *shell = "/bin/sh";
 | 
						|
  int rval;
 | 
						|
  sigf_t old_int, old_stop;
 | 
						|
 | 
						|
  if ( (pid = fork()) == -1 )
 | 
						|
  { return PL_error("shell", 2, OsError(), ERR_SYSCALL, "fork");
 | 
						|
  } else if ( pid == 0 )		/* The child */
 | 
						|
  { Setenv("PROLOGCHILD", "yes");
 | 
						|
    PL_cleanup_fork();
 | 
						|
    execl(shell, BaseName(shell), "-c", cmd, (char *)0);
 | 
						|
    fatalError("Failed to execute %s: %s", shell, OsError());
 | 
						|
    fail;
 | 
						|
    /*NOTREACHED*/
 | 
						|
  } else
 | 
						|
  { wait_t status;			/* the parent */
 | 
						|
    int n;
 | 
						|
 | 
						|
    old_int  = (sigf_t)signal(SIGINT,  SIG_IGN);
 | 
						|
#ifdef SIGTSTP
 | 
						|
    old_stop = (sigf_t)signal(SIGTSTP, SIG_DFL);
 | 
						|
#endif /* SIGTSTP */
 | 
						|
 | 
						|
    for(;;)
 | 
						|
    {
 | 
						|
#ifdef HAVE_WAITPID
 | 
						|
      n = waitpid(pid, &status, 0);
 | 
						|
#else
 | 
						|
      n = wait(&status);
 | 
						|
#endif
 | 
						|
      if ( n == -1 && errno == EINTR )
 | 
						|
	continue;
 | 
						|
      if ( n != pid )
 | 
						|
	continue;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    if ( n == -1 )
 | 
						|
    { term_t tmp = PL_new_term_ref();
 | 
						|
 | 
						|
      PL_put_atom_chars(tmp, cmd);
 | 
						|
      PL_error("shell", 2, MSG_ERRNO, ERR_SHELL_FAILED, tmp);
 | 
						|
 | 
						|
      rval = 1;
 | 
						|
    } else if (WIFEXITED(status))
 | 
						|
    { rval = WEXITSTATUS(status);
 | 
						|
#ifdef WIFSIGNALED
 | 
						|
    } else if (WIFSIGNALED(status))
 | 
						|
    { term_t tmp = PL_new_term_ref();
 | 
						|
      int sig = WTERMSIG(status);
 | 
						|
 | 
						|
      PL_put_atom_chars(tmp, cmd);
 | 
						|
      PL_error("shell", 2, NULL, ERR_SHELL_SIGNALLED, tmp, sig);
 | 
						|
      rval = 1;
 | 
						|
#endif
 | 
						|
    } else
 | 
						|
    { rval = 1;				/* make gcc happy */
 | 
						|
      fatalError("Unknown return code from wait(3)");
 | 
						|
      /*NOTREACHED*/
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  signal(SIGINT,  old_int);		/* restore signal handlers */
 | 
						|
#ifdef SIGTSTP
 | 
						|
  signal(SIGTSTP, old_stop);
 | 
						|
#endif /* SIGTSTP */
 | 
						|
 | 
						|
  return rval;
 | 
						|
}
 | 
						|
#endif /* __unix__ */
 | 
						|
 | 
						|
 | 
						|
#ifdef HAVE_WINEXEC			/* Windows 3.1 */
 | 
						|
#define SPECIFIC_SYSTEM 1
 | 
						|
 | 
						|
int
 | 
						|
System(char *command)
 | 
						|
{ char *msg;
 | 
						|
  int rval = WinExec(command, SW_SHOWNORMAL);
 | 
						|
 | 
						|
  if ( rval < 32 )
 | 
						|
  { switch( rval )
 | 
						|
    { case 0:	msg = "Not enough memory"; break;
 | 
						|
      case 2:	msg = "File not found"; break;
 | 
						|
      case 3:	msg = "No path"; break;
 | 
						|
      case 5:	msg = "Unknown error"; break;
 | 
						|
      case 6:	msg = "Lib requires separate data segment"; break;
 | 
						|
      case 8:	msg = "Not enough memory"; break;
 | 
						|
      case 10:	msg = "Incompatible Windows version"; break;
 | 
						|
      case 11:	msg = "Bad executable file"; break;
 | 
						|
      case 12:	msg = "Incompatible operating system"; break;
 | 
						|
      case 13:	msg = "MS-DOS 4.0 executable"; break;
 | 
						|
      case 14:	msg = "Unknown executable file type"; break;
 | 
						|
      case 15:	msg = "Real-mode application"; break;
 | 
						|
      case 16:	msg = "Cannot start multiple copies"; break;
 | 
						|
      case 19:	msg = "Executable is compressed"; break;
 | 
						|
      case 20:	msg = "Invalid DLL"; break;
 | 
						|
      case 21:	msg = "Application is 32-bits"; break;
 | 
						|
      default:	msg = "Unknown error";
 | 
						|
    }
 | 
						|
 | 
						|
    warning("Could not start %s: error %d (%s)",
 | 
						|
	    command, rval, msg);
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
#ifdef __WINDOWS__
 | 
						|
#define SPECIFIC_SYSTEM 1
 | 
						|
 | 
						|
					/* definition in pl-nt.c */
 | 
						|
#endif
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Nothing special is needed.  Just hope the C-library defines system().
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
#ifndef SPECIFIC_SYSTEM
 | 
						|
 | 
						|
int
 | 
						|
System(command)
 | 
						|
char *command;
 | 
						|
{ return system(command);
 | 
						|
}
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#ifndef _YAP_NOT_INSTALLED_
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
    char *findExecutable(char *buf)
 | 
						|
 | 
						|
    Return the path name of the executable of SWI-Prolog.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
#ifndef __WINDOWS__			/* Win32 version in pl-nt.c */
 | 
						|
static char *	Which(const char *program, char *fullname);
 | 
						|
 | 
						|
char *
 | 
						|
findExecutable(const char *av0, char *buffer)
 | 
						|
{ char *file;
 | 
						|
  char buf[MAXPATHLEN];
 | 
						|
  char tmp[MAXPATHLEN];
 | 
						|
 | 
						|
  if ( !av0 || !PrologPath(av0, buf, sizeof(buf)) )
 | 
						|
    return NULL;
 | 
						|
  file = Which(buf, tmp);
 | 
						|
 | 
						|
#if __unix__				/* argv[0] can be an #! script! */
 | 
						|
  if ( file )
 | 
						|
  { int n, fd;
 | 
						|
    char buf[MAXPATHLEN];
 | 
						|
 | 
						|
					/* Fails if mode is x-only, but */
 | 
						|
					/* then it can't be a script! */
 | 
						|
    if ( (fd = open(file, O_RDONLY)) < 0 )
 | 
						|
      return strcpy(buffer, file);
 | 
						|
 | 
						|
    if ( (n=read(fd, buf, sizeof(buf)-1)) > 0 )
 | 
						|
    { close(fd);
 | 
						|
 | 
						|
      buf[n] = EOS;
 | 
						|
      if ( strncmp(buf, "#!", 2) == 0 )
 | 
						|
      { char *s = &buf[2], *q;
 | 
						|
	while(*s && isBlank(*s))
 | 
						|
	  s++;
 | 
						|
	for(q=s; *q && !isBlank(*q); q++)
 | 
						|
	  ;
 | 
						|
	*q = EOS;
 | 
						|
 | 
						|
	return strcpy(buffer, s);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    close(fd);
 | 
						|
  }
 | 
						|
#endif /*__unix__*/
 | 
						|
 | 
						|
  return strcpy(buffer, file ? file : buf);
 | 
						|
}
 | 
						|
 | 
						|
#ifdef __unix__
 | 
						|
static char *
 | 
						|
okToExec(const char *s)
 | 
						|
{ statstruct stbuff;
 | 
						|
 | 
						|
  if (statfunc(s, &stbuff) == 0 &&	/* stat it */
 | 
						|
     S_ISREG(stbuff.st_mode) &&		/* check for file */
 | 
						|
     access(s, X_OK) == 0)		/* can be executed? */
 | 
						|
    return (char *)s;
 | 
						|
  else
 | 
						|
    return (char *) NULL;
 | 
						|
}
 | 
						|
#define PATHSEP	':'
 | 
						|
#endif /* __unix__ */
 | 
						|
 | 
						|
#if defined(OS2) || defined(__DOS__) || defined(__WINDOWS__)
 | 
						|
#define EXEC_EXTENSIONS { ".exe", ".com", ".bat", ".cmd", NULL }
 | 
						|
#define PATHSEP ';'
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef EXEC_EXTENSIONS
 | 
						|
 | 
						|
static char *
 | 
						|
okToExec(const char *s)
 | 
						|
{ static char *extensions[] = EXEC_EXTENSIONS;
 | 
						|
  static char **ext;
 | 
						|
 | 
						|
  DEBUG(2, Sdprintf("Checking %s\n", s));
 | 
						|
  for(ext = extensions; *ext; ext++)
 | 
						|
    if ( stripostfix(s, *ext) )
 | 
						|
      return ExistsFile(s) ? (char *)s : (char *) NULL;
 | 
						|
 | 
						|
  for(ext = extensions; *ext; ext++)
 | 
						|
  { static char path[MAXPATHLEN];
 | 
						|
 | 
						|
    strcpy(path, s);
 | 
						|
    strcat(path, *ext);
 | 
						|
    if ( ExistsFile(path) )
 | 
						|
      return path;
 | 
						|
  }
 | 
						|
 | 
						|
  return (char *) NULL;
 | 
						|
}
 | 
						|
#endif /*EXEC_EXTENSIONS*/
 | 
						|
 | 
						|
#define isRelativePath(p) ( p[0] == '.' )
 | 
						|
 | 
						|
static char *
 | 
						|
Which(const char *program, char *fullname)
 | 
						|
{ char *path, *dir;
 | 
						|
  char *e;
 | 
						|
 | 
						|
  if ( IsAbsolutePath(program) ||
 | 
						|
#if OS2 && EMX
 | 
						|
       isDriveRelativePath(program) ||
 | 
						|
#endif /* OS2 */
 | 
						|
        isRelativePath(program) ||
 | 
						|
       strchr(program, '/') )
 | 
						|
  { if ( (e = okToExec(program)) != NULL )
 | 
						|
    { strcpy(fullname, e);1
 | 
						|
 | 
						|
      return fullname;
 | 
						|
    }
 | 
						|
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
#if OS2 && EMX
 | 
						|
  if ((e = okToExec(program)) != NULL)
 | 
						|
  {
 | 
						|
    getcwd(fullname, MAXPATHLEN);
 | 
						|
    strcat(fullname, "/");
 | 
						|
    strcat(fullname, e);
 | 
						|
    return fullname;
 | 
						|
  }
 | 
						|
#endif /* OS2 */
 | 
						|
  if  ((path = getenv("PATH") ) == 0)
 | 
						|
    path = DEFAULT_PATH;
 | 
						|
 | 
						|
  while(*path)
 | 
						|
  { if ( *path == PATHSEP )
 | 
						|
    { if ( (e = okToExec(program)) )
 | 
						|
	return strcpy(fullname, e);
 | 
						|
      else
 | 
						|
        path++;				/* fix by Ron Hess (hess@sco.com) */
 | 
						|
    } else
 | 
						|
    { char tmp[MAXPATHLEN];
 | 
						|
 | 
						|
      for(dir = fullname; *path && *path != PATHSEP; *dir++ = *path++)
 | 
						|
	;
 | 
						|
      if (*path)
 | 
						|
	path++;				/* skip : */
 | 
						|
      if ((dir-fullname) + strlen(program)+2 > MAXPATHLEN)
 | 
						|
        continue;
 | 
						|
      *dir++ = '/';
 | 
						|
      strcpy(dir, program);
 | 
						|
      if ( (e = okToExec(OsPath(fullname, tmp))) )
 | 
						|
	return strcpy(fullname, e);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
#endif /*__WINDOWS__*/
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
/** int Pause(double time)
 | 
						|
 | 
						|
Suspend execution `time' seconds. Time  is   given  as  a floating point
 | 
						|
number, expressing the time  to  sleep   in  seconds.  Just  about every
 | 
						|
platform requires it own implementation. We provide them in the order of
 | 
						|
preference. The implementations differ on  their granularity and whether
 | 
						|
or not they can  be  interrupted   savely  restarted.  The  recent POSIX
 | 
						|
nanosleep() is just about the  only   function  that  really works well:
 | 
						|
accurate, interruptable and restartable.
 | 
						|
*/
 | 
						|
 | 
						|
#ifdef __WINDOWS__
 | 
						|
#define PAUSE_DONE 1			/* see pl-nt.c */
 | 
						|
#endif
 | 
						|
 | 
						|
#if !defined(PAUSE_DONE) && defined(HAVE_NANOSLEEP)
 | 
						|
#define PAUSE_DONE 1
 | 
						|
 | 
						|
int
 | 
						|
Pause(double t)
 | 
						|
{ struct timespec req;
 | 
						|
  int rc;
 | 
						|
 | 
						|
  if ( t < 0.0 )
 | 
						|
    succeed;
 | 
						|
 | 
						|
  req.tv_sec = (time_t) t;
 | 
						|
  req.tv_nsec = (long)((t - floor(t)) * 1000000000);
 | 
						|
 | 
						|
  for(;;)
 | 
						|
  { rc = nanosleep(&req, &req);
 | 
						|
    if ( rc == -1 && errno == EINTR )
 | 
						|
    { if ( PL_handle_signals() < 0 )
 | 
						|
	return FALSE;
 | 
						|
    } else
 | 
						|
      return TRUE;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
#endif /*HAVE_NANOSLEEP*/
 | 
						|
 | 
						|
 | 
						|
#if !defined(PAUSE_DONE) && defined(HAVE_USLEEP)
 | 
						|
#define PAUSE_DONE 1
 | 
						|
 | 
						|
int
 | 
						|
Pause(double t)
 | 
						|
{ if ( t <= 0.0 )
 | 
						|
    return TRUE;
 | 
						|
 | 
						|
  usleep((unsigned long)(t * 1000000.0));
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
#endif /*HAVE_USLEEP*/
 | 
						|
 | 
						|
 | 
						|
#if !defined(PAUSE_DONE) && defined(HAVE_SELECT)
 | 
						|
#define PAUSE_DONE 1
 | 
						|
 | 
						|
int
 | 
						|
Pause(double time)
 | 
						|
{ struct timeval timeout;
 | 
						|
 | 
						|
  if ( time <= 0.0 )
 | 
						|
    return;
 | 
						|
 | 
						|
  if ( time < 60.0 )		/* select() is expensive. Does it make sense */
 | 
						|
  { timeout.tv_sec = (long)time;
 | 
						|
    timeout.tv_usec = (long)(time * 1000000) % 1000000;
 | 
						|
    select(32, NULL, NULL, NULL, &timeout);
 | 
						|
 | 
						|
    return TRUE;
 | 
						|
  } else
 | 
						|
  { int rc;
 | 
						|
    int left = (int)(time+0.5);
 | 
						|
 | 
						|
    do
 | 
						|
    { rc = sleep(left);
 | 
						|
      if ( rc == -1 && errno == EINTR )
 | 
						|
      { if ( PL_handle_signals() < 0 )
 | 
						|
	  return FALSE;
 | 
						|
 | 
						|
	return TRUE;
 | 
						|
      }
 | 
						|
      left -= rc;
 | 
						|
    } while ( rc != 0 );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
#endif /*HAVE_SELECT*/
 | 
						|
 | 
						|
#if !defined(PAUSE_DONE) && defined(HAVE_DOSSLEEP)
 | 
						|
#define PAUSE_DONE 1
 | 
						|
 | 
						|
int					/* a millisecond granualrity. */
 | 
						|
Pause(double time)			/* the EMX function sleep uses seconds */
 | 
						|
{ if ( time <= 0.0 )			/* the select() trick does not work at all. */
 | 
						|
    return TRUE;
 | 
						|
 | 
						|
  DosSleep((ULONG)(time * 1000));
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
#endif /*HAVE_DOSSLEEP*/
 | 
						|
 | 
						|
#if !defined(PAUSE_DONE) && defined(HAVE_SLEEP)
 | 
						|
#define PAUSE_DONE 1
 | 
						|
 | 
						|
int
 | 
						|
Pause(double t)
 | 
						|
{ if ( t <= 0.5 )
 | 
						|
    succeed;
 | 
						|
 | 
						|
  sleep((int)(t + 0.5));
 | 
						|
 | 
						|
  succeed;
 | 
						|
}
 | 
						|
 | 
						|
#endif /*HAVE_SLEEP*/
 | 
						|
 | 
						|
#if !defined(PAUSE_DONE) && defined(HAVE_DELAY)
 | 
						|
#define PAUSE_DONE 1
 | 
						|
 | 
						|
int
 | 
						|
Pause(double t)
 | 
						|
{ delay((int)(t * 1000));
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
#endif /*HAVE_DELAY*/
 | 
						|
 | 
						|
#ifndef PAUSE_DONE
 | 
						|
int
 | 
						|
Pause(double t)
 | 
						|
{ return notImplemented("sleep", 1);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
BeginPredDefs(system)
 | 
						|
  PRED_DEF("shell", 2, shell, 0)
 | 
						|
EndPredDefs
 | 
						|
 | 
						|
  // @}
 |