1 /*************************************************************************
2 *									 *
3 *	 YAP Prolog 							 *
4 *									 *
5 *	Yap Prolog was developed at NCCUP - Universidade do Porto	 *
6 *									 *
7 * Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997	 *
8 *									 *
9 **************************************************************************
10 *									 *
11 * $Id: sys.c,v 1.36 2008-07-11 17:02:09 vsc Exp $									 *
12 * mods:		$Log: not supported by cvs2svn $
13 * mods:		Revision 1.35  2008/05/23 13:16:13  vsc
14 * mods:		fix sys.c for win32
15 * mods:
16 * mods:		Revision 1.34  2008/05/22 23:25:21  vsc
17 * mods:		add tmp_file/2
18 * mods:
19 * mods:		Revision 1.33  2007/10/05 18:24:30  vsc
20 * mods:		fix garbage collector and fix LeaveGoal
21 * mods:
22 * mods:		Revision 1.32  2007/05/07 12:11:39  vsc
23 * mods:		fix mktime fix
24 * mods:
25 * mods:		Revision 1.31  2007/05/07 11:21:29  vsc
26 * mods:		mktime needs to know if daylight time savings are on
27 * mods:		(obs from Bernd Gutmann).
28 * mods:
29 * mods:		Revision 1.30  2007/05/02 11:16:43  vsc
30 * mods:		small fixes to sys.c
31 * mods:
32 * mods:		Revision 1.29  2006/10/10 14:08:17  vsc
33 * mods:		small fixes on threaded implementation.
34 * mods:
35 * mods:		Revision 1.28  2006/05/25 16:28:28  vsc
36 * mods:		include thread_sleep functionality.
37 * mods:
38 * mods:		Revision 1.27  2006/05/17 18:38:11  vsc
39 * mods:		make system library use true file name
40 * mods:
41 * mods:		Revision 1.26  2006/04/25 03:23:40  vsc
42 * mods:		fix ! in debugger (execute_clause)
43 * mods:		improve system/1 and execute/1
44 * mods:
45 * mods:		Revision 1.25  2006/01/17 14:10:42  vsc
46 * mods:		YENV may be an HW register (breaks some tabling code)
47 * mods:		All YAAM instructions are now brackedted, so Op introduced an { and EndOp introduces an }. This is because Ricardo assumes that.
48 * mods:		Fix attvars when COROUTING is undefined.
49 * mods:
50 * mods:		Revision 1.24  2006/01/08 23:01:48  vsc
51 * mods:		*** empty log message ***
52 * mods:
53 * mods:		Revision 1.23  2005/10/21 16:09:03  vsc
54 * mods:		SWI compatible module only operators
55 * mods:
56 * mods:		Revision 1.22  2005/03/10 18:04:01  rslopes
57 * mods:		update YAP_Error arguments
58 * mods:		to be able to compile on Windows...
59 * mods:
60 * mods:		Revision 1.21  2004/08/11 16:14:54  vsc
61 * mods:		whole lot of fixes:
62 * mods:		  - memory leak in indexing
63 * mods:		  - memory management in WIN32 now supports holes
64 * mods:		  - extend Yap interface, more support for SWI-Interface
65 * mods:		  - new predicate mktime in system
66 * mods:		  - buffer console I/O in WIN32
67 * mods:
68 * mods:		Revision 1.20  2004/07/23 19:02:09  vsc
69 * mods:		misc fixes
70 * mods:
71 * mods:		Revision 1.19  2004/07/23 03:37:17  vsc
72 * mods:		fix heap overflow in YAP_LookupAtom
73 * mods:
74 * mods:		Revision 1.18  2004/01/26 12:51:33  vsc
75 * mods:		should be datime/1 not date/1
76 * mods:
77 * mods:		Revision 1.17  2004/01/26 12:41:06  vsc
78 * mods:		bug fixes
79 * mods:
80 * mods:		Revision 1.16  2003/01/27 15:55:40  vsc
81 * mods:		use CVS Id
82 * mods:
83 * mods:		Revision 1.15  2003/01/27 15:54:10  vsc
84 * mods:		fix header
85 * mods:									 *
86 * comments:	regular expression interpreter                           *
87 *									 *
88 *************************************************************************/
89 
90 #include "config.h"
91 #include "YapInterface.h"
92 #include <stdlib.h>
93 #if HAVE_UNISTD_H
94 #include <unistd.h>
95 #endif
96 #include <stdio.h>
97 #if HAVE_TIME_H
98 #include <time.h>
99 #endif
100 #if HAVE_SYS_TYPES_H
101 #include <sys/types.h>
102 #endif
103 #if HAVE_SYS_STAT_H
104 #include <sys/stat.h>
105 #endif
106 #if HAVE_FCNTL_H
107 #include <fcntl.h>
108 #endif
109 #if HAVE_MATH_H
110 #include <math.h>
111 #endif
112 #if HAVE_UNISTD_H
113 #include <unistd.h>
114 #endif
115 #if HAVE_ERRNO_H
116 #include <errno.h>
117 #endif
118 #if HAVE_STRING_H
119 #include <string.h>
120 #endif
121 #if HAVE_SIGNAL_H
122 #include <signal.h>
123 #endif
124 #if HAVE_SYS_WAIT_H
125 #include <sys/wait.h>
126 #endif
127 #if HAVE_DIRENT_H
128 #include <dirent.h>
129 #endif
130 #if HAVE_DIRECT_H
131 #include <direct.h>
132 #endif
133 #if defined(__MINGW32__) || _MSC_VER
134 #include <windows.h>
135 #include <process.h>
136 #endif
137 #ifdef __MINGW32__
138 #ifdef HAVE_ENVIRON
139 #undef HAVE_ENVIRON
140 #endif
141 #endif
142 
143 void PROTO(init_sys, (void));
144 
145 #if defined(__MINGW32__) || _MSC_VER
146 static YAP_Term
WinError(void)147 WinError(void)
148 {
149   char msg[256];
150   /* Error, we could not read time */
151     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
152 		  NULL, GetLastError(),
153 		  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), msg, 256,
154 		  NULL);
155     return(YAP_MkAtomTerm(YAP_LookupAtom(msg)));
156 }
157 #endif
158 
159 /* Return time in a structure */
160 static int
sysmktime(void)161 sysmktime(void)
162 {
163 
164 #if defined(__MINGW32__) || _MSC_VER
165   SYSTEMTIME stime, stime0;
166   FILETIME ftime, ftime0;
167 
168   stime.wYear = YAP_IntOfTerm(YAP_ARG1);
169   stime.wMonth = YAP_IntOfTerm(YAP_ARG2);
170   stime.wDay = YAP_IntOfTerm(YAP_ARG3);
171   stime.wHour = YAP_IntOfTerm(YAP_ARG4);
172   stime.wMinute = YAP_IntOfTerm(YAP_ARG5);
173   stime.wSecond = YAP_IntOfTerm(YAP_ARG6);
174   stime.wMilliseconds = 0;
175   stime0.wYear = 1970;
176   stime0.wMonth = 1;
177   stime0.wDay = 1;
178   stime0.wHour = 12;
179   stime0.wMinute = 0;
180   stime0.wSecond = 0;
181   stime0.wMilliseconds = 0;
182   if (!SystemTimeToFileTime(&stime,&ftime)) {
183     return YAP_Unify(YAP_ARG8, YAP_MkIntTerm(errno));
184   }
185   if (!SystemTimeToFileTime(&stime0,&ftime0)) {
186     return YAP_Unify(YAP_ARG8, YAP_MkIntTerm(errno));
187   }
188 #if __GNUC__
189   {
190     unsigned long long f1 = (((unsigned long long)ftime.dwHighDateTime)<<32)+(unsigned long long)ftime.dwLowDateTime;
191     unsigned long long f0 = (((unsigned long long)ftime0.dwHighDateTime)<<32)+(unsigned long long)ftime0.dwLowDateTime;
192     return YAP_Unify(YAP_ARG7,YAP_MkIntTerm((long int)((f1-f0)/10000000)));
193   }
194 #else
195   return FALSE;
196 #endif
197 #else
198 #ifdef HAVE_MKTIME
199   struct tm loc;
200   time_t tim;
201 
202   loc.tm_year = YAP_IntOfTerm(YAP_ARG1)-1900;
203   loc.tm_mon = YAP_IntOfTerm(YAP_ARG2)-1;
204   loc.tm_mday = YAP_IntOfTerm(YAP_ARG3);
205   loc.tm_hour = YAP_IntOfTerm(YAP_ARG4);
206   loc.tm_min = YAP_IntOfTerm(YAP_ARG5);
207   loc.tm_sec = YAP_IntOfTerm(YAP_ARG6);
208   loc.tm_isdst = -1;
209 
210   if ((tim = mktime(&loc)) == (time_t)-1) {
211     return YAP_Unify(YAP_ARG8, YAP_MkIntTerm(errno));
212   }
213   return YAP_Unify(YAP_ARG7,YAP_MkIntTerm(tim));
214 #else
215   oops
216 #endif /* HAVE_MKTIME */
217 #endif /* WINDOWS */
218 }
219 
220 /* Return time in a structure */
221 static int
datime(void)222 datime(void)
223 {
224   YAP_Term tf, out[6];
225 #if defined(__MINGW32__) || _MSC_VER
226   SYSTEMTIME stime;
227   GetLocalTime(&stime);
228   out[0] = YAP_MkIntTerm(stime.wYear);
229   out[1] = YAP_MkIntTerm(stime.wMonth);
230   out[2] = YAP_MkIntTerm(stime.wDay);
231   out[3] = YAP_MkIntTerm(stime.wHour);
232   out[4] = YAP_MkIntTerm(stime.wMinute);
233   out[5] = YAP_MkIntTerm(stime.wSecond);
234 #elif HAVE_TIME
235   time_t  tp;
236 
237   if ((tp = time(NULL)) == -1) {
238      return(YAP_Unify(YAP_ARG2, YAP_MkIntTerm(errno)));
239   }
240 #ifdef HAVE_LOCALTIME
241  {
242    struct tm *loc = localtime(&tp);
243    if (loc == NULL) {
244      return(YAP_Unify(YAP_ARG2, YAP_MkIntTerm(errno)));
245    }
246    out[0] = YAP_MkIntTerm(1900+loc->tm_year);
247    out[1] = YAP_MkIntTerm(1+loc->tm_mon);
248    out[2] = YAP_MkIntTerm(loc->tm_mday);
249    out[3] = YAP_MkIntTerm(loc->tm_hour);
250    out[4] = YAP_MkIntTerm(loc->tm_min);
251    out[5] = YAP_MkIntTerm(loc->tm_sec);
252  }
253 #else
254   oops
255 #endif /* HAVE_LOCALTIME */
256 #else
257   oops
258 #endif /* HAVE_TIME */
259   tf = YAP_MkApplTerm(YAP_MkFunctor(YAP_LookupAtom("datime"),6), 6, out);
260   return YAP_Unify(YAP_ARG1, tf);
261 }
262 
263 #define BUF_SIZE 1024
264 
265 /* Return a list of files for a directory */
266 static int
list_directory(void)267 list_directory(void)
268 {
269   YAP_Term tf = YAP_MkAtomTerm(YAP_LookupAtom("[]"));
270   long sl = YAP_InitSlot(tf);
271 
272   char *buf = (char *)YAP_AtomName(YAP_AtomOfTerm(YAP_ARG1));
273 #if defined(__MINGW32__) || _MSC_VER
274   struct _finddata_t c_file;
275   char bs[BUF_SIZE];
276   long hFile;
277 
278   bs[0] = '\0';
279 #if HAVE_STRNCPY
280   strncpy(bs, buf, BUF_SIZE);
281 #else
282   strcpy(bs, buf);
283 #endif
284 #if HAVE_STRNCAT
285   strncat(bs, "/*", BUF_SIZE);
286 #else
287   strcat(bs, "/*");
288 #endif
289   if ((hFile = _findfirst(bs, &c_file)) == -1L) {
290     return(YAP_Unify(YAP_ARG2,tf));
291   }
292   YAP_PutInSlot(sl, YAP_MkPairTerm(YAP_MkAtomTerm(YAP_LookupAtom(c_file.name)), YAP_GetFromSlot(sl)));
293   while (_findnext( hFile, &c_file) == 0) {
294     YAP_Term ti = YAP_MkAtomTerm(YAP_LookupAtom(c_file.name));
295     YAP_PutInSlot(sl,YAP_MkPairTerm(ti, YAP_GetFromSlot(sl)));
296   }
297   _findclose( hFile );
298 #elif HAVE_OPENDIR
299  {
300    DIR *de;
301    struct dirent *dp;
302 
303    if ((de = opendir(buf)) == NULL) {
304      return(YAP_Unify(YAP_ARG3, YAP_MkIntTerm(errno)));
305    }
306    while ((dp = readdir(de))) {
307      YAP_Term ti = YAP_MkAtomTerm(YAP_LookupAtom(dp->d_name));
308      YAP_PutInSlot(sl,YAP_MkPairTerm(ti, YAP_GetFromSlot(sl)));
309    }
310    closedir(de);
311  }
312 #endif /* HAVE_OPENDIR */
313   tf = YAP_GetFromSlot(sl);
314   return YAP_Unify(YAP_ARG2, tf);
315 }
316 
317 static int
p_unlink(void)318 p_unlink(void)
319 {
320   char *fd = (char *)YAP_AtomName(YAP_AtomOfTerm(YAP_ARG1));
321 #if defined(__MINGW32__) || _MSC_VER
322   if (_unlink(fd) == -1)
323 #else
324   if (unlink(fd) == -1)
325 #endif
326     {
327       /* return an error number */
328       return(YAP_Unify(YAP_ARG2, YAP_MkIntTerm(errno)));
329     }
330   return(TRUE);
331 }
332 
333 static int
p_mkdir(void)334 p_mkdir(void)
335 {
336   char *fd = (char *)YAP_AtomName(YAP_AtomOfTerm(YAP_ARG1));
337 #if defined(__MINGW32__) || _MSC_VER
338   if (_mkdir(fd) == -1) {
339 #else
340   if (mkdir(fd, 0777) == -1) {
341 #endif
342     /* return an error number */
343     return(YAP_Unify(YAP_ARG2, YAP_MkIntTerm(errno)));
344   }
345   return(TRUE);
346 }
347 
348 static int
349 p_rmdir(void)
350 {
351   char *fd = (char *)YAP_AtomName(YAP_AtomOfTerm(YAP_ARG1));
352 #if defined(__MINGW32__) || _MSC_VER
353   if (_rmdir(fd) == -1) {
354 #else
355   if (rmdir(fd) == -1) {
356 #endif
357     /* return an error number */
358     return(YAP_Unify(YAP_ARG2, YAP_MkIntTerm(errno)));
359   }
360   return(TRUE);
361 }
362 
363 static int
364 rename_file(void)
365 {
366   char *s1 = (char *)YAP_AtomName(YAP_AtomOfTerm(YAP_ARG1));
367   char *s2 = (char *)YAP_AtomName(YAP_AtomOfTerm(YAP_ARG2));
368 #if HAVE_RENAME
369   if (rename(s1, s2) == -1) {
370     /* return an error number */
371     return(YAP_Unify(YAP_ARG3, YAP_MkIntTerm(errno)));
372   }
373 #endif
374   return(TRUE);
375 }
376 
377 static int
378 dir_separator(void)
379 {
380   return(YAP_Unify(YAP_ARG1,YAP_MkAtomTerm(YAP_LookupAtom("/"))));
381 }
382 
383 static int
384 file_property(void)
385 {
386   const char *fd;
387 #if HAVE_LSTAT
388   struct stat buf;
389 
390   fd = (char *)YAP_AtomName(YAP_AtomOfTerm(YAP_ARG1));
391   if (lstat(fd, &buf) == -1) {
392     /* return an error number */
393     return(YAP_Unify(YAP_ARG7, YAP_MkIntTerm(errno)));
394   }
395   if (S_ISREG(buf.st_mode)) {
396     if (!(YAP_Unify(YAP_ARG2, YAP_MkAtomTerm(YAP_LookupAtom("regular"))) &&
397 	  YAP_Unify(YAP_ARG6, YAP_MkIntTerm(0))))
398       return(FALSE);
399   } else if (S_ISDIR(buf.st_mode)) {
400     if (!(YAP_Unify(YAP_ARG2, YAP_MkAtomTerm(YAP_LookupAtom("directory"))) &&
401 	  YAP_Unify(YAP_ARG6, YAP_MkIntTerm(0))))
402       return(FALSE);
403   } else if (S_ISFIFO(buf.st_mode)) {
404     if (!(YAP_Unify(YAP_ARG2, YAP_MkAtomTerm(YAP_LookupAtom("fifo"))) &&
405 	  YAP_Unify(YAP_ARG6, YAP_MkIntTerm(0))))
406       return(FALSE);
407   } else if (S_ISLNK(buf.st_mode)) {
408     if (!YAP_Unify(YAP_ARG2, YAP_MkAtomTerm(YAP_LookupAtom("symlink"))))
409       return(FALSE);
410 #if HAVE_READLINK
411     {
412       char tmp[256];
413       int n;
414       if ((n = readlink(fd,tmp,256)) == -1) {
415 	return(YAP_Unify(YAP_ARG7, YAP_MkIntTerm(errno)));
416       }
417       tmp[n] = '\0';
418       if(!YAP_Unify(YAP_ARG6,YAP_MkAtomTerm(YAP_LookupAtom(tmp)))) {
419 	return(FALSE);
420       }
421     }
422 #else
423     if (!YAP_Unify(YAP_ARG6, YAP_MkIntTerm(0)))
424       return(FALSE);
425 #endif
426   } else if (S_ISSOCK(buf.st_mode)) {
427     if (!(YAP_Unify(YAP_ARG2, YAP_MkAtomTerm(YAP_LookupAtom("socket"))) &&
428 	  YAP_Unify(YAP_ARG6, YAP_MkIntTerm(0))))
429       return(FALSE);
430   } else {
431     if (!(YAP_Unify(YAP_ARG2, YAP_MkAtomTerm(YAP_LookupAtom("unknown"))) &&
432 	  YAP_Unify(YAP_ARG6, YAP_MkIntTerm(0))))
433       return(FALSE);
434   }
435 #elif defined(__MINGW32__) || _MSC_VER
436   /* for some weird reason _stat did not work with mingw32 */
437   struct _stat buf;
438 
439   fd = YAP_AtomName(YAP_AtomOfTerm(YAP_ARG1));
440   if (_stat(fd, &buf) != 0) {
441     /* return an error number */
442     return(YAP_Unify(YAP_ARG7, YAP_MkIntTerm(errno)));
443   }
444   if (buf.st_mode & S_IFREG) {
445     if (!YAP_Unify(YAP_ARG2, YAP_MkAtomTerm(YAP_LookupAtom("regular"))))
446       return(FALSE);
447   } else if (buf.st_mode & S_IFDIR) {
448     if (!YAP_Unify(YAP_ARG2, YAP_MkAtomTerm(YAP_LookupAtom("directory"))))
449       return(FALSE);
450   } else {
451     if (!YAP_Unify(YAP_ARG2, YAP_MkAtomTerm(YAP_LookupAtom("unknown"))))
452       return(FALSE);
453   }
454 #endif
455   return (
456 	  YAP_Unify(YAP_ARG3, YAP_MkIntTerm(buf.st_size)) &&
457 	  YAP_Unify(YAP_ARG4, YAP_MkIntTerm(buf.st_mtime)) &&
458 	  YAP_Unify(YAP_ARG5, YAP_MkIntTerm(buf.st_mode))
459 	  );
460 }
461 
462 /* temporary files */
463 
464 static int
465 p_mktemp(void)
466 {
467 #if HAVE_MKTEMP || defined(__MINGW32__) || _MSC_VER
468   char *s, tmp[BUF_SIZE];
469   s = (char *)YAP_AtomName(YAP_AtomOfTerm(YAP_ARG1));
470 #if HAVE_STRNCPY
471   strncpy(tmp, s, BUF_SIZE);
472 #else
473   strcpy(tmp, s);
474 #endif
475 #if defined(__MINGW32__) || _MSC_VER
476   if ((s = _mktemp(tmp)) == NULL) {
477     /* return an error number */
478     return(YAP_Unify(YAP_ARG3, YAP_MkIntTerm(errno)));
479   }
480   return(YAP_Unify(YAP_ARG2,YAP_MkAtomTerm(YAP_LookupAtom(s))));
481 #else
482   if ((s = mktemp(tmp)) == NULL) {
483     /* return an error number */
484     return(YAP_Unify(YAP_ARG3, YAP_MkIntTerm(errno)));
485   }
486   return YAP_Unify(YAP_ARG2,YAP_MkAtomTerm(YAP_LookupAtom(s)));
487 #endif
488 #else
489   return FALSE;
490 #endif
491   return(TRUE);
492 }
493 
494 static int
495 p_tmpnam(void)
496 {
497 #if HAVE_TMPNAM
498   char buf[L_tmpnam], *s;
499   if (!(s = tmpnam(buf)))
500     return FALSE;
501   return YAP_Unify(YAP_ARG1,YAP_MkAtomTerm(YAP_LookupAtom(s)));
502 #else
503   return FALSE;
504 #endif
505 }
506 
507 static int
508 p_tmpdir(void)
509 {
510 #if defined(__MINGW32__) || _MSC_VER
511   char buf[512];
512   DWORD out = GetTempPath(512, buf);
513   if (!out) {
514     return(YAP_Unify(YAP_ARG2, WinError()));
515   }
516   if (out > 511) {
517     char *nbuf = malloc(out+1);
518     if (!nbuf)
519       return YAP_Unify(YAP_ARG2, YAP_MkAtomTerm(YAP_LookupAtom("no malloc memory")));
520     out = GetTempPath(512, nbuf);
521     if (!out) {
522       return YAP_Unify(YAP_ARG2, WinError());
523     }
524     return  YAP_Unify(YAP_ARG1,YAP_MkAtomTerm(YAP_LookupAtom(nbuf)));
525   }
526   return  YAP_Unify(YAP_ARG1,YAP_MkAtomTerm(YAP_LookupAtom(buf)));
527 #else
528   char *s;
529   if ((s = getenv("TMPDIR")))
530     return  YAP_Unify(YAP_ARG1,YAP_MkAtomTerm(YAP_LookupAtom(s)));
531 #ifdef P_tmpdir
532   return  YAP_Unify(YAP_ARG1,YAP_MkAtomTerm(YAP_LookupAtom(P_tmpdir)));
533 #endif
534   return  YAP_Unify(YAP_ARG1,YAP_MkAtomTerm(YAP_LookupAtom("/tmp")));
535 #endif
536 }
537 
538 /* return YAP's environment */
539 static int
540 p_environ(void)
541 {
542 #if HAVE_ENVIRON && 0
543 #if HAVE__NSGETENVIRON
544   char ** ptr = _NSGetEnviron();
545 #elif defined(__MINGW32__) || _MSC_VER
546   extern char **_environ;
547   char ** ptr = _environ;
548 #else
549   extern char **environ;
550   char ** ptr = environ;
551 #endif
552   YAP_Term t1 = YAP_ARG1;
553   long int i;
554 
555   i = YAP_IntOfTerm(t1);
556   if (ptr[i] == NULL)
557     return(FALSE);
558   else {
559     YAP_Term t = YAP_BufferToString(ptr[i]);
560     return(YAP_Unify(t, YAP_ARG2));
561   }
562 #else
563   YAP_Error(0, 0L, "environ not available in this configuration" );
564   return(FALSE);
565 #endif
566 }
567 
568 #if defined(__MINGW32__) || _MSC_VER
569 static HANDLE
570 get_handle(YAP_Term ti, DWORD fd)
571 {
572   if (YAP_IsAtomTerm(ti)) {
573     HANDLE out;
574     SECURITY_ATTRIBUTES satt;
575 
576     satt.nLength = sizeof(satt);
577     satt.lpSecurityDescriptor = NULL;
578     satt.bInheritHandle = TRUE;
579     out = CreateFile("NUL",
580 			    GENERIC_READ|GENERIC_WRITE,
581 			    FILE_SHARE_READ|FILE_SHARE_WRITE,
582 			    &satt,
583 			    OPEN_EXISTING,
584 			    0,
585 			    NULL);
586     return(out);
587   } else {
588     if (YAP_IsIntTerm(ti)) {
589       return(GetStdHandle(fd));
590     } else
591       return((HANDLE)YAP_StreamToFileNo(ti));
592   }
593 }
594 
595 static void
596 close_handle(YAP_Term ti, HANDLE h)
597 {
598   if (YAP_IsAtomTerm(ti)) {
599     CloseHandle(h);
600   }
601 }
602 
603 #endif
604 
605 /* execute a command as a detached process */
606 static int
607 execute_command(void)
608 {
609   YAP_Term ti = YAP_ARG2, to = YAP_ARG3, te = YAP_ARG4;
610   int res;
611 #if defined(__MINGW32__) || _MSC_VER
612   HANDLE inpf, outf, errf;
613   DWORD CreationFlags = 0;
614   STARTUPINFO StartupInfo;
615   PROCESS_INFORMATION ProcessInformation;
616   inpf = get_handle(ti, STD_INPUT_HANDLE);
617   if (inpf == INVALID_HANDLE_VALUE) {
618     return(YAP_Unify(YAP_ARG6, WinError()));
619   }
620   outf = get_handle(to, STD_OUTPUT_HANDLE);
621   if (outf == INVALID_HANDLE_VALUE) {
622     close_handle(ti, inpf);
623     return(YAP_Unify(YAP_ARG6, WinError()));
624   }
625   errf = get_handle(te, STD_OUTPUT_HANDLE);
626   if (errf == INVALID_HANDLE_VALUE) {
627     close_handle(ti, inpf);
628     close_handle(to, outf);
629     return(YAP_Unify(YAP_ARG6, WinError()));
630   }
631   if (!YAP_IsIntTerm(ti) && !YAP_IsIntTerm(to) && !YAP_IsIntTerm(te)) {
632     /* we do not keep a current stream */
633     CreationFlags = DETACHED_PROCESS;
634   }
635   StartupInfo.cb = sizeof(STARTUPINFO);
636   StartupInfo.lpReserved = NULL;
637   StartupInfo.lpDesktop = NULL; /* inherit */
638   StartupInfo.lpTitle = NULL; /* we do not create a new console window */
639   StartupInfo.dwFlags = STARTF_USESTDHANDLES;
640   StartupInfo.cbReserved2 = 0;
641   StartupInfo.lpReserved2 = NULL;
642   StartupInfo.hStdInput = inpf;
643   StartupInfo.hStdOutput = outf;
644   StartupInfo.hStdError = errf;
645   /* got stdin, stdout and error as I like it */
646   if (CreateProcess(NULL,
647 		    (char *)YAP_AtomName(YAP_AtomOfTerm(YAP_ARG1)),
648 		    NULL,
649 		    NULL,
650 		    TRUE,
651 		    CreationFlags,
652 		    NULL,
653 		    NULL,
654 		    &StartupInfo,
655 		    &ProcessInformation) == FALSE) {
656     close_handle(ti, inpf);
657     close_handle(to, outf);
658     close_handle(te, errf);
659     return(YAP_Unify(YAP_ARG6, WinError()));
660   }
661   close_handle(ti, inpf);
662   close_handle(to, outf);
663   close_handle(te, errf);
664   res = ProcessInformation.dwProcessId;
665   return(YAP_Unify(YAP_ARG5,YAP_MkIntTerm(res)));
666 #else /* UNIX CODE */
667   int inpf, outf, errf;
668   /* process input first */
669   if (YAP_IsAtomTerm(ti)) {
670     inpf = open("/dev/null", O_RDONLY);
671   } else {
672     int sd;
673     if (YAP_IsIntTerm(ti))
674       sd = 0;
675     else
676       sd = YAP_StreamToFileNo(ti);
677     inpf = dup(sd);
678   }
679   if (inpf < 0) {
680     /* return an error number */
681     return(YAP_Unify(YAP_ARG6, YAP_MkIntTerm(errno)));
682   }
683   /* then output stream */
684   if (YAP_IsAtomTerm(to)) {
685     outf = open("/dev/zero", O_WRONLY);
686   } else {
687     int sd;
688     if (YAP_IsIntTerm(to))
689       sd = 1;
690     else
691       sd = YAP_StreamToFileNo(to);
692     outf = dup(sd);
693   }
694   if (outf < 0) {
695     /* return an error number */
696     close(inpf);
697     return(YAP_Unify(YAP_ARG6, YAP_MkIntTerm(errno)));
698   }
699   /* then error stream */
700   if (YAP_IsAtomTerm(te)) {
701     errf = open("/dev/zero", O_WRONLY);
702   } else {
703     int sd;
704     if (YAP_IsIntTerm(te))
705       sd = 2;
706     else
707       sd = YAP_StreamToFileNo(te);
708     errf = dup(sd);
709   }
710   if (errf < 0) {
711     /* return an error number */
712     close(inpf);
713     close(outf);
714     return(YAP_Unify(YAP_ARG6, YAP_MkIntTerm(errno)));
715   }
716   YAP_FlushAllStreams();
717   /* we are now ready to fork */
718   if ((res = fork()) < 0) {
719     /* close streams we don't need */
720     close(inpf);
721     close(outf);
722     close(errf);
723     /* return an error number */
724     return(YAP_Unify(YAP_ARG6, YAP_MkIntTerm(errno)));
725   } else if (res == 0) {
726     char *argv[4];
727 
728     /* child */
729     /* close current streams, but not std streams */
730     YAP_CloseAllOpenStreams();
731     close(0);
732     dup(inpf);
733     close(inpf);
734     close(1);
735     dup(outf);
736     close(outf);
737     close(2);
738     dup(errf);
739     close(errf);
740     argv[0] = "sh";
741     argv[1] = "-c";
742     argv[2] = (char *)YAP_AtomName(YAP_AtomOfTerm(YAP_ARG1));
743     argv[3] = NULL;
744     execv("/bin/sh", argv);
745     exit(127);
746     /* we have the streams where we want them, just want to execute now */
747   } else {
748     close(inpf);
749     close(outf);
750     close(errf);
751     return(YAP_Unify(YAP_ARG5,YAP_MkIntTerm(res)));
752   }
753 #endif /* UNIX code */
754 }
755 
756 /* execute a command as a detached process */
757 static int
758 do_system(void)
759 {
760   char *command = (char *)YAP_AtomName(YAP_AtomOfTerm(YAP_ARG1));
761 #if HAVE_SYSTEM
762   int sys = system(command);
763   if (sys < 0) {
764     return YAP_Unify(YAP_ARG3,YAP_MkIntTerm(errno));
765   }
766   return YAP_Unify(YAP_ARG2, YAP_MkIntTerm(sys));
767 #else
768   YAP_Error(0,0L,"system not available in this configuration, trying %s", command);
769   return FALSE;
770 #endif
771 }
772 
773 
774 
775 /* execute a command as a detached process */
776 static int
777 do_shell(void)
778 {
779 #if defined(__MINGW32__) || _MSC_VER
780   YAP_Error(0,0L,"system not available in this configuration");
781   return(FALSE);
782 #elif HAVE_SYSTEM
783   char *buf = YAP_AllocSpaceFromYap(BUF_SIZE);
784   int sys;
785 
786   if (buf == NULL) {
787     YAP_Error(0,0L,"No Temporary Space for Shell");
788     return(FALSE);
789   }
790 #if HAVE_STRNCPY
791   strncpy(buf, YAP_AtomName(YAP_AtomOfTerm(YAP_ARG1)), BUF_SIZE);
792   strncpy(buf, " ", BUF_SIZE);
793   strncpy(buf, YAP_AtomName(YAP_AtomOfTerm(YAP_ARG2)), BUF_SIZE);
794   strncpy(buf, " ", BUF_SIZE);
795   strncpy(buf, YAP_AtomName(YAP_AtomOfTerm(YAP_ARG3)), BUF_SIZE);
796 #else
797   strcpy(buf, YAP_AtomName(YAP_AtomOfTerm(YAP_ARG1)));
798   strcpy(buf, YAP_AtomName(YAP_AtomOfTerm(YAP_ARG2)));
799   strcpy(buf, " ");
800   strcpy(buf, YAP_AtomName(YAP_AtomOfTerm(YAP_ARG3)));
801 #endif
802   sys = system(buf);
803   YAP_FreeSpaceFromYap(buf);
804   if (sys < 0) {
805     return YAP_Unify(YAP_ARG5,YAP_MkIntTerm(errno));
806   }
807   return YAP_Unify(YAP_ARG4, YAP_MkIntTerm(sys));
808 #else
809   char *cptr[4];
810   int t;
811   int sys;
812 
813   cptr[0]= (char *)YAP_AtomName(YAP_AtomOfTerm(YAP_ARG1));
814   cptr[1]= (char *)YAP_AtomName(YAP_AtomOfTerm(YAP_ARG2));
815   cptr[2]= (char *)YAP_AtomName(YAP_AtomOfTerm(YAP_ARG3));
816   cptr[3]= NULL;
817   t = fork();
818   if (t < 0) {
819     return YAP_Unify(YAP_ARG5,YAP_MkIntTerm(errno));
820   } else if (t == 0) {
821     t = execvp(cptr[0],cptr);
822     return t;
823   } else {
824     t = wait(&sys);
825     fprintf(stderr,"after wait %x:%x\n",t,sys);
826     if (t < 0) {
827       return YAP_Unify(YAP_ARG5,YAP_MkIntTerm(errno));
828     }
829   }
830   fprintf(stderr,"after wait %x\n", sys);
831   return YAP_Unify(YAP_ARG4, YAP_MkIntTerm(sys));
832 #endif
833 }
834 
835 /* execute a command as a detached process */
836 static int
837 p_wait(void)
838 {
839   long int pid = YAP_IntOfTerm(YAP_ARG1);
840 #if defined(__MINGW32__) || _MSC_VER
841   HANDLE proc = OpenProcess(STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE, FALSE, pid);
842   DWORD ExitCode;
843   if (proc == NULL) {
844     return(YAP_Unify(YAP_ARG3, WinError()));
845   }
846   if (WaitForSingleObject(proc, INFINITE) == WAIT_FAILED) {
847     return(YAP_Unify(YAP_ARG3, WinError()));
848   }
849   if (GetExitCodeProcess(proc, &ExitCode) == 0) {
850     return(YAP_Unify(YAP_ARG3, WinError()));
851   }
852   CloseHandle(proc);
853   return(YAP_Unify(YAP_ARG2, YAP_MkIntTerm(ExitCode)));
854 #else
855   do {
856     int status;
857 
858     /* check for interruptions */
859     if (waitpid(pid, &status, 0) == -1) {
860       if (errno != EINTR)
861 	return -1;
862       return(YAP_Unify(YAP_ARG3, YAP_MkIntTerm(errno)));
863     } else {
864       return(YAP_Unify(YAP_ARG2, YAP_MkIntTerm(status)));
865     }
866   } while(TRUE);
867 #endif
868 }
869 
870 /* execute a command as a detached process */
871 static int
872 p_popen(void)
873 {
874   char *command = (char *)YAP_AtomName(YAP_AtomOfTerm(YAP_ARG1));
875   long int mode = YAP_IntOfTerm(YAP_ARG2);
876   FILE *pfd;
877   YAP_Term tsno;
878   int flags;
879 
880 #if HAVE_POPEN
881 #if defined(__MINGW32__) || _MSC_VER
882   /* This will only work for console applications. FIX */
883   if (mode == 0)
884     pfd = _popen(command, "r");
885   else
886     pfd = _popen(command, "w");
887 #else
888   if (mode == 0)
889     pfd = popen(command, "r");
890   else
891     pfd = popen(command, "w");
892 #endif
893   if (pfd == NULL) {
894     return(YAP_Unify(YAP_ARG4, YAP_MkIntTerm(errno)));
895   }
896   if (mode == 0)
897     flags = YAP_INPUT_STREAM | YAP_POPEN_STREAM;
898   else
899     flags = YAP_OUTPUT_STREAM | YAP_POPEN_STREAM;
900   tsno = YAP_OpenStream((void *)pfd,
901 		       "pipe",
902 		       YAP_MkAtomTerm(YAP_LookupAtom("pipe")),
903 		       flags);
904 #endif
905   return(YAP_Unify(YAP_ARG3, tsno));
906 }
907 
908 static int
909 p_sleep(void)
910 {
911   YAP_Term ts = YAP_ARG1;
912   unsigned long int secs = 0, usecs = 0, out;
913   if (YAP_IsIntTerm(ts)) {
914     secs = YAP_IntOfTerm(ts);
915   }  else if (YAP_IsFloatTerm(ts)) {
916     double tfl = YAP_FloatOfTerm(ts);
917     if (tfl > 1.0)
918       secs = tfl;
919     else
920       usecs = tfl*1000000;
921   }
922 #if defined(__MINGW32__) || _MSC_VER
923   if (secs) usecs = secs*1000 + usecs/1000;
924   Sleep(usecs);
925   /* no errors possible */
926   out = 0;
927 #elif HAVE_NANOSLEEP
928   {
929     struct timespec req;
930     if (YAP_IsFloatTerm(ts)) {
931       double tfl = YAP_FloatOfTerm(ts);
932 
933       req.tv_nsec = (tfl-floor(tfl))*1000000000;
934       req.tv_sec = rint(tfl);
935     } else {
936       req.tv_nsec = 0;
937       req.tv_sec = secs;
938     }
939     out = nanosleep(&req, NULL);
940   }
941 #elif HAVE_USLEEP
942   if (usecs > 0) {
943     out = usleep(usecs);
944   } else
945 #elif HAVE_SLEEP
946     {
947       out = sleep(secs);
948     }
949 #else
950   YAP_Error(0,0L,"sleep not available in this configuration");
951   return FALSE:
952 #endif
953   return(YAP_Unify(YAP_ARG2, YAP_MkIntTerm(out)));
954 }
955 
956 /* host info */
957 
958 static int
959 host_name(void)
960 {
961 #if defined(__MINGW32__) || _MSC_VER
962   char name[MAX_COMPUTERNAME_LENGTH+1];
963   DWORD nSize = MAX_COMPUTERNAME_LENGTH+1;
964   if (GetComputerName(name, &nSize) == 0) {
965     return(YAP_Unify(YAP_ARG2, WinError()));
966   }
967 #else
968 #if HAVE_GETHOSTNAME
969   char name[256];
970   if (gethostname(name, 256) == -1) {
971     /* return an error number */
972     return(YAP_Unify(YAP_ARG2, YAP_MkIntTerm(errno)));
973   }
974 #endif
975 #endif /* defined(__MINGW32__) || _MSC_VER */
976   return(YAP_Unify(YAP_ARG1, YAP_MkAtomTerm(YAP_LookupAtom(name))));
977 }
978 
979 static int
980 host_id(void)
981 {
982 #if HAVE_GETHOSTID
983   return(YAP_Unify(YAP_ARG1, YAP_MkIntTerm(gethostid())));
984 #else
985   return(YAP_Unify(YAP_ARG1, YAP_MkIntTerm(0)));
986 #endif
987 }
988 
989 static int
990 pid(void)
991 {
992 #if defined(__MINGW32__) || _MSC_VER
993   return(YAP_Unify(YAP_ARG1, YAP_MkIntTerm(_getpid())));
994 #else
995   return(YAP_Unify(YAP_ARG1, YAP_MkIntTerm(getpid())));
996 #endif
997 }
998 
999 static int
1000 win(void)
1001 {
1002 #if defined(__MINGW32__) || _MSC_VER
1003   return(TRUE);
1004 #else
1005   return(FALSE);
1006 #endif
1007 }
1008 
1009 static int
1010 p_kill(void)
1011 {
1012 #if defined(__MINGW32__) || _MSC_VER
1013   /* Windows does not support cross-process signals, so we shall do the
1014      SICStus thing and assume that a signal to a process will
1015      always kill it */
1016   HANDLE proc = OpenProcess(STANDARD_RIGHTS_REQUIRED|PROCESS_TERMINATE, FALSE, YAP_IntOfTerm(YAP_ARG1));
1017   if (proc == NULL) {
1018     return(YAP_Unify(YAP_ARG3, WinError()));
1019   }
1020   if (TerminateProcess(proc, -1) == 0) {
1021     return(YAP_Unify(YAP_ARG3, WinError()));
1022   }
1023   CloseHandle(proc);
1024 #else
1025   if (kill(YAP_IntOfTerm(YAP_ARG1), YAP_IntOfTerm(YAP_ARG2)) < 0) {
1026     /* return an error number */
1027     return(YAP_Unify(YAP_ARG3, YAP_MkIntTerm(errno)));
1028   }
1029 #endif /* defined(__MINGW32__) || _MSC_VER */
1030   return(TRUE);
1031 }
1032 
1033 static int
1034 error_message(void)
1035 {
1036 #if HAVE_STRERROR
1037   return YAP_Unify(YAP_ARG2,YAP_MkAtomTerm(YAP_LookupAtom(strerror(YAP_IntOfTerm(YAP_ARG1)))));
1038 #else
1039   return YAP_Unify(YAP_ARG2,YAP_ARG1);
1040 #endif
1041 }
1042 
1043 void
1044 init_sys(void)
1045 {
1046 #if HAVE_MKTIME
1047   tzset();
1048 #endif
1049   YAP_UserCPredicate("datime", datime, 2);
1050   YAP_UserCPredicate("mktime", sysmktime, 8);
1051   YAP_UserCPredicate("list_directory", list_directory, 3);
1052   YAP_UserCPredicate("file_property", file_property, 7);
1053   YAP_UserCPredicate("unlink", p_unlink, 2);
1054   YAP_UserCPredicate("mkdir", p_mkdir, 2);
1055   YAP_UserCPredicate("rmdir", p_rmdir, 2);
1056   YAP_UserCPredicate("dir_separator", dir_separator, 1);
1057   YAP_UserCPredicate("p_environ", p_environ, 2);
1058   YAP_UserCPredicate("exec_command", execute_command, 6);
1059   YAP_UserCPredicate("do_shell", do_shell, 5);
1060   YAP_UserCPredicate("do_system", do_system, 3);
1061   YAP_UserCPredicate("popen", p_popen, 4);
1062   YAP_UserCPredicate("wait", p_wait, 3);
1063   YAP_UserCPredicate("host_name", host_name, 2);
1064   YAP_UserCPredicate("host_id", host_id, 2);
1065   YAP_UserCPredicate("pid", pid, 2);
1066   YAP_UserCPredicate("kill", p_kill, 3);
1067   YAP_UserCPredicate("mktemp", p_mktemp, 3);
1068   YAP_UserCPredicate("tmpnam", p_tmpnam, 2);
1069   YAP_UserCPredicate("tmpdir", p_tmpdir, 2);
1070   YAP_UserCPredicate("rename_file", rename_file, 3);
1071   YAP_UserCPredicate("sleep", p_sleep, 2);
1072   YAP_UserCPredicate("error_message", error_message, 2);
1073   YAP_UserCPredicate("win", win, 0);
1074 }
1075 
1076 #ifdef _WIN32
1077 
1078 #include <windows.h>
1079 
1080 int WINAPI PROTO(win_sys, (HANDLE, DWORD, LPVOID));
1081 
1082 int WINAPI win_sys(HANDLE hinst, DWORD reason, LPVOID reserved)
1083 {
1084   switch (reason)
1085     {
1086     case DLL_PROCESS_ATTACH:
1087       break;
1088     case DLL_PROCESS_DETACH:
1089       break;
1090     case DLL_THREAD_ATTACH:
1091       break;
1092     case DLL_THREAD_DETACH:
1093       break;
1094     }
1095   return 1;
1096 }
1097 #endif
1098