1/* BEGIN LICENSE BLOCK
2 * Version: CMPL 1.1
3 *
4 * The contents of this file are subject to the Cisco-style Mozilla Public
5 * License Version 1.1 (the "License"); you may not use this file except
6 * in compliance with the License.  You may obtain a copy of the License
7 * at www.eclipse-clp.org/license.
8 *
9 * Software distributed under the License is distributed on an "AS IS"
10 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
11 * the License for the specific language governing rights and limitations
12 * under the License.
13 *
14 * The Original Code is  The ECLiPSe Constraint Logic Programming System.
15 * The Initial Developer of the Original Code is  Cisco Systems, Inc.
16 * Portions created by the Initial Developer are
17 * Copyright (C) 1997-2006 Cisco Systems, Inc.  All Rights Reserved.
18 *
19 * Contributor(s):
20 *
21 * END LICENSE BLOCK */
22
23/*----------------------------------------------------------------------*
24 * ECLiPSe system
25 *
26 * IDENTIFICATION:	os_support.c
27 *
28 * $Id: os_support.c,v 1.17 2015/05/05 15:11:30 jschimpf Exp $
29 *
30 * AUTHOR:		Joachim Schimpf, IC-Parc
31 *
32 * DESCRIPTION:		Operating-system services abstraction layer
33 *
34 *----------------------------------------------------------------------*/
35
36/*----------------------------------------------------------------------*
37 * Config & Prototypes only.
38 * Do not include any ECLiPSe-specific stuff in this file!
39 *----------------------------------------------------------------------*/
40
41#include "config.h"
42#include "os_support.h"
43
44/*----------------------------------------------------------------------*
45 * OS-includes
46 *----------------------------------------------------------------------*/
47
48#include <errno.h>
49#include <time.h>
50#include <sys/types.h>
51#include <sys/stat.h>	/* for struct stat or _stat */
52#include <stdio.h>
53#include <math.h>	/* for floor() */
54#include <ctype.h>	/* for toupper() etc */
55
56#ifdef _WIN32
57#if (HAVE_WIN32_WINNT >= 0x500)
58#define _WIN32_WINNT 0x500	/* for GetLongPathName() */
59#else
60#define _WIN32_WINNT HAVE_WIN32_WINNT
61#endif
62/* FILETIMEs are in 100 nanosecond units */
63#define FileTimeToDouble(t) ((double) (t).dwHighDateTime * 429.4967296 \
64			    + (double) (t).dwLowDateTime * 1e-7)
65#include <io.h>		/* for _access(),... */
66#include <process.h>	/* for _getpid() */
67#include <direct.h>	/* for _getcwd() */
68#include <sys/timeb.h>	/* for _fstat() */
69#include <conio.h>	/* for _getch */
70#include <windows.h>
71#else
72#include <sys/time.h>
73#include <sys/times.h>
74#include <pwd.h>
75#endif
76
77#ifdef STDC_HEADERS
78#include <stdlib.h>
79#else
80extern char *getenv();
81#endif
82
83#ifdef HAVE_NETDB_H
84#include <netdb.h>      /* for gethostbyname() */
85#endif
86
87#ifdef HAVE_SYS_SYSTEMINFO_H
88#include <sys/systeminfo.h> /* for sysinfo() */
89#endif
90
91#ifdef HAVE_STRING_H
92#include <string.h>
93#endif
94
95#ifdef HAVE_SYS_UTSNAME_H
96#include <sys/utsname.h>	/* for uname() */
97#endif
98
99#ifdef HAVE_TIMES
100#include <sys/times.h>
101#endif
102
103#ifdef BSD_TIMES
104#include <sys/timeb.h>
105#endif
106
107#ifdef HAVE_GETCWD
108#include <signal.h>
109#endif
110
111#ifdef HAVE_SIOCGIFHWADDR
112#include <sys/ioctl.h>
113#include <net/if.h>
114#endif
115
116#ifdef HAVE_PTHREAD_H
117#include <pthread.h>
118#endif
119
120/*----------------------------------------------------------------------*
121 * Global variables
122 *----------------------------------------------------------------------*/
123
124/*
125 * ECLiPSe version number. This is here because it is needed for
126 * ec_env_lookup() and various executables.
127 */
128char		ec_version[] = PACKAGE_VERSION;
129
130/*
131 * the time when the system was started
132 */
133#ifdef _WIN32
134static double	start_time;	/* in seconds */
135#else
136#ifdef BSD_TIMES
137static time_t	start_time;	/* in seconds */
138#else
139static clock_t	start_time;	/* in clock ticks */
140#endif
141#endif
142
143/*
144 * the resolution of the system clock
145 */
146int		clock_hz;
147
148/*
149 * Error numbers of OS-errors
150 */
151int		ec_os_errno_;	/* the operating system error number */
152int		ec_os_errgrp_;	/* which group of error numbers it is from */
153
154
155/*
156 * ECLiPSe's simulated working directory
157 * Used when ec_use_own_cwd != 0
158 */
159
160int		ec_use_own_cwd = 0;
161static char	ec_cwd[MAX_PATH_LEN];	/* should always have a trailing '/' */
162
163
164/*----------------------------------------------------------------------*
165 * Initialisation
166 *----------------------------------------------------------------------*/
167
168void
169ec_os_init(void)
170{
171
172#ifdef _WIN32
173    DWORD now;
174    FILETIME now_time;
175    GetSystemTimeAsFileTime(&now_time);
176    start_time = FileTimeToDouble(now_time);
177    now = now_time.dwLowDateTime;
178    clock_hz = CLOCKS_PER_SEC;
179
180#else /* UNIX */
181    time_t now = time((time_t *) 0);
182
183#ifdef BSD_TIMES
184    start_time = now;		/* init startup time */
185#else
186    struct tms dummy;
187    start_time = times(&dummy);	/* init startup time */
188#endif
189#ifdef HAVE_SYSCONF
190    clock_hz = sysconf(_SC_CLK_TCK);
191#else
192#ifdef CLOCK_HZ
193    clock_hz = CLOCK_HZ;
194#else
195    clock_hz = 60;
196#endif
197#endif
198
199    /* On SUNOS 4.0 the first call to ctime() takes much longer than the
200     * subsequent ones since a shared file is opened (see tzsetup(8)).
201     * Therefore we call it here once.
202     */
203    (void) ctime(&now);
204
205#endif
206
207#ifdef _WIN32
208    {
209	WSADATA wsa_data;
210	(void) WSAStartup(MAKEWORD(2,0), &wsa_data);	/* init Winsock */
211    }
212#endif
213
214    ec_use_own_cwd = 0;
215    (void) get_cwd(ec_cwd, MAX_PATH_LEN);
216}
217
218
219void
220ec_os_fini(void)
221{
222#ifdef _WIN32
223    (void) WSACleanup();	/* finalise Winsock (once per WSAStartup()) */
224#endif
225    ec_terminate_alarm();	/* terminate alarm thread, if any */
226}
227
228
229/*----------------------------------------------------------------------*
230 * Simple wrappers for Windows -> Unix
231 *----------------------------------------------------------------------*/
232
233#ifdef _WIN32
234
235#ifndef R_OK
236#define R_OK	4
237#define W_OK	2
238#define X_OK	1
239#endif
240
241int
242ec_access(char *name, int amode)
243{
244    /* CAUTION: Windows _access() knows only R_OK and W_OK.
245     * http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx
246     * To simulate X_OK check we use _stat() and check for _S_IEXEC mode,
247     * but even that is fake: it is set when the file name has a .exe
248     * extension, and is always set for directories.
249     * A cleaner implementation would probably use AccessCheck(),
250     * OpenThreadToken(), etc.
251     */
252    char winname[MAX_PATH_LEN];
253
254    os_filename(name, winname);
255    if (_access(winname, amode & (R_OK|W_OK)))
256	return -1;
257    if (amode & X_OK)
258    {
259	struct _stat buf;
260	if (_stat(winname, &buf))
261	    return -1;
262	if (!(buf.st_mode & _S_IEXEC))
263	    return -1;
264    }
265    return 0;
266}
267
268int
269getpid(void)
270{
271    return _getpid();
272}
273
274int
275ec_chdir(char *name)
276{
277    char winname[MAX_PATH_LEN];
278    return _chdir(os_filename(name, winname));
279}
280
281int
282ec_stat(char *name, struct_stat *buf)
283{
284    char winname[MAX_PATH_LEN];
285    return _stat(os_filename(name, winname), (struct _stat *) buf);
286}
287
288#ifndef __GNUC__
289int
290fstat(int handle, struct_stat *buf)
291{
292    return _fstat(handle, buf);
293}
294#endif
295
296int
297ec_rmdir(char *name)
298{
299    char winname[MAX_PATH_LEN];
300    return _rmdir(os_filename(name, winname));
301}
302
303int
304ec_mkdir(char *name, int mode)
305{
306    char winname[MAX_PATH_LEN];
307    return _mkdir(os_filename(name, winname));
308}
309
310int
311ec_unlink(char *name)
312{
313    char winname[MAX_PATH_LEN];
314    return _unlink(os_filename(name, winname));
315}
316
317#ifndef __GNUC__
318long
319lseek(int handle, long offset, int whence)
320{
321    return _lseek(handle, offset, whence);
322}
323#endif
324
325int
326ec_truncate(int fd)
327{
328    if (!SetEndOfFile((HANDLE)_get_osfhandle(fd)))
329    {
330	Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
331	return -1;
332    }
333    return 0;
334}
335
336#ifndef __GNUC__
337int
338putenv(char *envstring)
339{
340    return _putenv(envstring);
341}
342#endif
343
344int
345isatty(int handle)
346{
347    return _isatty(handle);
348}
349
350#ifndef isascii
351/* isascii is defined as a macro (as __isascii)  in newer versions of MSVC,
352   and also in more recent versions of MinGW
353*/
354int
355isascii(int c)
356{
357    return __isascii(c);
358}
359#endif
360
361int
362ec_open(const char *name, int oflag, int pmode)
363{
364    char winname[MAX_PATH_LEN];
365    return _open(os_filename((char *) name, winname), oflag|_O_BINARY, pmode);
366}
367
368int
369dup(int handle)
370{
371    return _dup(handle);
372}
373
374int
375dup2(int h1, int h2)
376{
377    return _dup2(h1, h2);
378}
379
380int
381close(int handle)
382{
383    return _close(handle);
384}
385
386int
387read(int handle, void *buf, unsigned int count)
388{
389    return _read(handle, buf, count);
390}
391
392int
393write(int handle, const void *buf, unsigned int count)
394{
395    return _write(handle, buf, count);
396}
397
398int
399pipe(int *fd)
400{
401    /* _O_NOINHERIT is important for pipes used in exec */
402    return _pipe(fd, 4096, _O_BINARY|_O_NOINHERIT);
403}
404
405int
406getpagesize(void)
407{
408    SYSTEM_INFO info;
409    GetSystemInfo(&info);
410    return (int) info.dwPageSize;
411}
412#endif
413
414int
415ec_rename(char *old, char *new)
416{
417    char winold[MAX_PATH_LEN];
418    char winnew[MAX_PATH_LEN];
419    return rename(os_filename(old, winold), os_filename(new, winnew));
420}
421
422
423
424/*----------------------------------------------------------------------*
425 * Filename conversions
426 *----------------------------------------------------------------------*/
427
428/*
429 * Automaton for syntactic cleanup of (absolute or relative) pathnames:
430 * Remove redundant slashes, . and ..
431 * Produce:	 //share/dir/file  //c/dir/file  /dir/file  dir/file  file
432 * The result will never be longer than the input, and in=out is allowed.
433 */
434
435static char *
436_cleanup_path(char *inp, char *out, char *out_last)
437{
438    int c;
439    char *outp = out;
440    char *top = out;	/* highest point in the path */
441    int absolute = 0;	/* it's an absolute path (we can't go beyond top) */
442
443#define Emit(c) { if (outp<out_last) *outp++ = (c); else goto _terminate_; }
444
445    switch (c = *inp++) {
446	case '.': goto _initial_dot_;
447	case '/': Emit(c); goto _abs_;
448	case 0:   goto _terminate_;
449	default:  goto _name_;
450    }
451_initial_dot_:
452    switch (c = *inp++) {
453	case '.': goto _initial_up_;
454	case '/': goto _rel_;
455	case 0:   Emit('.'); goto _terminate_;
456	default:  Emit('.'); goto _name_;
457    }
458_initial_up_:
459    switch (c = *inp++) {
460	case '/': Emit('.'); Emit('.'); Emit('/'); top=outp; goto _sep_;
461	case 0:   Emit('.'); Emit('.'); goto _terminate_;
462	case '.':
463	default:  Emit('.'); Emit('.'); goto _name_;
464    }
465_rel_:
466    switch (c = *inp++) {
467	case '.': goto _initial_dot_;
468	case '/': goto _rel_;
469	case 0:   Emit('.'); goto _terminate_;
470	default:  top=outp; goto _name_;
471    }
472_abs_:
473    absolute = 1;
474    switch (c = *inp++) {
475	case '.': top=outp; goto _dot_;
476	case '/': goto _unc_;
477	case 0:   goto _terminate_;
478	default:  top=outp; goto _name_;
479    }
480_unc_:
481    switch (c = *inp++) {
482	case '.': top=outp; goto _dot_;			/* treat //. like /. */
483	case '/': goto _unc_;				/* treat /// like // */
484	case 0:   goto _terminate_;			/* treat // like / */
485	default:  Emit('/'); goto _share_;		/* treat ///a like //a */
486    }
487_share_:
488    Emit(c);
489    switch (c = *inp++) {
490	case '/': Emit(c); top=outp; goto _sep_;
491	case 0: goto _terminate_;
492	default: goto _share_;
493    }
494_sep_:
495    switch (c = *inp++) {
496	case '/': goto _sep_;
497	case '.': goto _dot_;
498	case 0:   goto _done_;
499	default:  goto _name_;
500    }
501_dot_:
502    switch (c = *inp++) {
503	case 0: goto _done_;			/* ignore trailing . */
504	case '.': goto _up_;
505	case '/': goto _sep_;			/* ignore /./ */
506	default: Emit('.'); goto _name_;
507    }
508_up_:
509    switch(c = *inp++) {
510	case 0:
511	case '/':
512	    if (outp>top) {
513		/* back to after previous / or top */
514		while(--outp > top  &&  *(outp-1) != '/')
515		    continue;
516	    } else if (!absolute) {
517		Emit('.'); Emit('.'); Emit('/'); top=outp;
518	    }
519	    if (c) goto _sep_; else goto _done_;
520	default:
521	    Emit('.'); Emit('.'); goto _name_;
522    }
523_name_:
524    Emit(c);
525    switch (c = *inp++) {
526	case '/': Emit(c); goto _sep_;
527	case 0:  goto _terminate_;
528	default: goto _name_;
529    }
530_done_:
531    if (outp == out) {
532	Emit('.')			/* no other path component */
533    } else if (outp > out+1  && *(outp-1) == '/') {
534    	--outp;				/* omit trailing slash */
535    }
536_terminate_:
537    *outp = 0;
538    return out;
539}
540
541
542/*
543 * char *expand_filename(in, out, option)
544 *
545 * EXPAND_SYNTACTIC
546 *	expand ~, ~user and $VAR at the beginning of the filename
547 * EXPAND_STANDARD
548 *	also make absolute (only if necessary)
549 * EXPAND_ABSOLUTE
550 *	also make absolute (always)
551 * EXPAND_NORMALISE
552 *	full normalisation (symlinks, Windows capitalisation etc)
553 *	In addition, unneeded sequences /, ./ are removed.
554 *
555 * out should point to a buffer of length MAX_PATH_LEN.
556 * The return value is a pointer to the expanded filename in out[].
557 * It can be used like
558 *
559 *	char buf[MAX_PATH_LEN];
560 *	name = expand_filename(name, buf, EXPAND_STANDARD);
561 *
562 * No errors are returned. When there was a problem, we just
563 * return a copy of the original string.
564 * The result is truncated to MAX_PATH_LEN, without warning!
565 */
566
567#define Str_Cpy(to, from, to_last) \
568	{ while(*(from) && (to)<(to_last)) *(to)++ = *(from)++; }
569#define Str_Cpy_Until(to, from, delim, to_last) \
570	{ while(*(from) && *(from) != (delim) && (to) < (to_last)) *(to)++ = *(from)++; }
571
572char *
573expand_filename(char *in, char *out, int option)
574{
575    int c;
576    char *inp = in;
577    char *dir = (char *) 0;
578    char aux1[MAX_PATH_LEN], *aux1p = 0;
579    char * const aux1_last = &aux1[MAX_PATH_LEN-1];
580    char aux[MAX_PATH_LEN], *auxp = 0;
581    char * const aux_last = &aux[MAX_PATH_LEN-1];
582    char * const out_last = out+MAX_PATH_LEN-1;
583
584    /* When not using the process's cwd, we MUST use absolute paths */
585    if (option == EXPAND_STANDARD && ec_use_own_cwd)
586	option = EXPAND_ABSOLUTE;
587
588    /*
589     * Expand tilde and environment variables
590     * inp=in=<input path>
591     */
592    switch(*inp)
593    {
594    case '~':
595	if (*++inp == '/' || *inp == '\0')
596	{
597	    char *home, *drv;
598	    auxp = aux;
599	    aux1p = aux1;
600#ifdef _WIN32
601	    if ((drv = getenv("HOMEDRIVE")) && (home = getenv("HOMEPATH")))
602	    {
603		auxp = aux;
604		Str_Cpy(auxp, drv, aux_last);
605		Str_Cpy(auxp, home, aux_last);
606		*auxp = 0;
607		aux1p = canonical_filename(aux, aux1);
608	    }
609	    else
610#endif
611	    if ((home = getenv("HOME")) && strlen(home) < MAX_PATH_LEN)
612	    {
613		aux1p = canonical_filename(home, aux1);
614	    }
615	    else
616	    {
617	        aux1p = 0;
618	    }
619	}
620#ifndef _WIN32
621	else
622	{
623	    struct passwd *pass;
624	    auxp = aux;
625	    Str_Cpy_Until(auxp, inp, '/', aux_last);
626	    *auxp = '\0';
627	    if ((pass = getpwnam(aux)) && strlen(pass->pw_dir) < MAX_PATH_LEN)
628		aux1p = canonical_filename(pass->pw_dir, aux1);
629	}
630#endif
631	break;
632    case '$':
633	{
634	    int size = MAX_PATH_LEN;
635	    ++inp;
636	    aux1p = aux1;
637	    Str_Cpy_Until(aux1p, inp, '/', aux1_last);
638	    *aux1p = '\0';
639	    if (ec_env_lookup(aux1, aux, &size) && size <= MAX_PATH_LEN)
640		aux1p = canonical_filename(aux, aux1);  /* make sure it is in ECLiPSe format */
641	    else aux1p = 0;
642	}
643	break;
644    }
645
646    if (aux1p)
647    {
648	/* append rest of input to expanded prefix in aux1[] */
649	aux1p += strlen(aux1p);
650	Str_Cpy(aux1p, inp, aux1_last);
651	*aux1p = 0;
652	inp = aux1;
653    }
654    else	/* no prefix was expanded */
655    {
656	inp = in;
657    }
658
659
660    /*
661     * Make absolute, i.e. add cwd or drive
662     * inp points to result so far, either in in[] or aux1[]
663     */
664    if (option >= EXPAND_ABSOLUTE)
665    {
666        if (inp[0] != '/')		/* relative path: prefix cwd */
667	{
668	    auxp = aux + ec_get_cwd(aux, MAX_PATH_LEN);
669	    Str_Cpy(auxp, inp, aux_last);
670	    *auxp = 0;
671	    inp = aux;
672	}
673#ifdef _WIN32
674	else if (inp[1] != '/')		/* drive relative: prefix drive */
675	{
676	    auxp = aux;
677	    ec_get_cwd(aux, MAX_PATH_LEN);
678	    while (*auxp == '/') ++auxp;	/* copy share/drive name */
679	    while (*auxp != '/') ++auxp;
680	    Str_Cpy(auxp, inp, aux_last);
681	    *auxp = 0;
682	    inp = aux;
683	}
684#endif
685    }
686
687
688    /*
689     * Full normalise: symlinks etc
690     * inp points to result so far, either in in[], aux1[] or aux[]
691     */
692    if (option == EXPAND_NORMALISE)
693    {
694#if defined(_WIN32) && (_WIN32_WINNT > 0x400)
695	int len;
696	char buf1[MAX_PATH_LEN];
697	char buf2[MAX_PATH_LEN];
698	/* Get `normalised' path with correct cases for characters in path.
699	   GetLongPathName() is supported only by Windows NT > 4
700	   XP seems to require a call to GetShortPathName(), otherwise
701	   GetLongPathName() does not always behave correctly.
702	*/
703	os_filename(inp, buf2);
704	/* Make sure drive letter is upper case (bug under cygwin) */
705	if (islower(buf2[0]) && buf2[1] == ':')
706	    buf2[0] = toupper(buf2[0]);
707	len = GetShortPathName(buf2, buf1, MAX_PATH_LEN);
708	if (0 < len && len < MAX_PATH_LEN)
709	{
710	    len = GetLongPathName(buf1, buf2, MAX_PATH_LEN);
711	    if (0 < len && len < MAX_PATH_LEN) {
712		canonical_filename(buf2, buf1);
713		_cleanup_path(buf1, out, out_last);
714	    } else {
715		canonical_filename(buf1, buf2);
716		_cleanup_path(buf2, out, out_last);
717	    }
718	}
719	else
720	{
721	    canonical_filename(buf2, buf1);
722	    _cleanup_path(buf1, out, out_last);
723	}
724
725#elif HAVE_REALPATH
726	/* realpath() also cleans up /. and /.. */
727	if (!realpath(inp, out))
728	{
729	    errno = 0;
730	    _cleanup_path(inp, out, out_last);
731	}
732
733#else
734	_cleanup_path(inp, out, out_last);
735#endif
736    }
737    else
738    {
739	_cleanup_path(inp, out, out_last);
740    }
741
742    return out;
743}
744
745
746char *
747canonical_filename(char *in, char *out)
748{
749#ifdef _WIN32
750    char *s = in;
751    char *t = out;
752    for (;;)
753    {
754	if (*s == '\0' || *s == '\\' || *s == '/')
755	{
756	    s = in;		/* no drive letter */
757	    break;
758	}
759	if (*s == ':')		/* copy drive name */
760	{
761	    *t++ = '/';
762	    *t++ = '/';
763	    while (in < s)
764	    	*t++ = *in++;
765	    ++s;
766	    if (*s != '\\' && *s != '/')
767		*t++ = '/'; 	/* no separator, insert one */
768	    break;
769	}
770	++s;
771    }
772    while (*s)			/* copy, replacing \ with / */
773    {
774	*t = (*s == '\\') ? '/' : *s;
775	++s; ++t;
776    }
777
778    *t = '\0';
779    return out;
780#else
781    return strcpy(out, in);
782#endif
783}
784
785char *
786os_filename(char *in, char *out)
787{
788#ifdef _WIN32
789    char *eos;
790    char *t = out;
791
792    /* interpret //? as a drive name and treat specially */
793    if (in[0] == '/' && in[1] == '/' && in[2] != 0 && (in[3] == '/' || in[3] == 0))
794    {
795	*t++ = in[2];			/* copy drive letter */
796	*t++ = ':';			/* followed by : */
797	*t++ = '\\';
798	if (in[3]==0 || in[4]==0)
799	{
800	    /* special case //D[/] -> D:\. rather than simply D:  (bug 465) */
801	    *t++ = '.'; *t = '\0';
802	    return out;
803	}
804	in += 4;
805    }
806    else if (*in == '/')		/* one or two non-trimmable slashes */
807    {
808	*t++ = '\\'; ++in;
809	if (*in == '/')
810	{
811	    *t++ = '\\'; ++in;
812	}
813    }
814    eos = t;
815    while (*in)				/* copy rest of path */
816    {
817	if (*in == '/')
818	{
819	    *t++ = '\\';
820	    ++in;
821	}
822	else
823	{
824	    *t++ = *in++;
825	    eos = t;			/* to remove trailing slashes */
826	}
827    }
828    *eos = '\0';
829    return out;
830#else
831    return strcpy(out, in);
832#endif
833}
834
835
836/*----------------------------------------------------------------------*
837 * Directories
838 *----------------------------------------------------------------------*/
839
840/*
841* For the sps7 there is a special getwd() code. It works on other
842* machines as well, however it is not necessary to duplicate everything.
843*/
844#if !defined(_WIN32) && !defined(HAVE_GETCWD) && !defined(HAVE_GETWD)
845#include "getwd.c"
846#endif
847
848/*
849* Get the current working directory (unix) into a buffer
850* and add a trailing "/". If something went wrong, return "./".
851* The return code is the string length.
852* Different code is needed for different operating systems.
853*/
854
855/*ARGSUSED*/
856int
857get_cwd(char *buf, int size)
858{
859    char	*s;
860    int		len;
861    char	buf1[MAX_PATH_LEN];
862
863#ifdef _WIN32
864    char	buf2[MAX_PATH_LEN];
865    s = _getcwd(buf1, MAX_PATH_LEN);
866    /* Make sure drive letter is upper case (bug under cygwin) */
867    if (islower(buf1[0]) && buf1[1] == ':')
868    	buf1[0] = toupper(buf1[0]);
869#if _WIN32_WINNT > 0x400
870    /* Get `normalised' path with correct cases for characters in path.
871       GetLongPathName() is supported only by Windows NT > 4
872       XP seems to require a call to GetShortPathName(), otherwise
873       GetLongPathName() does not always behave correctly.
874    */
875    len = GetShortPathName(buf1, buf2, MAX_PATH_LEN);
876    if (len > 0)
877    {
878	len = GetLongPathName(buf2, buf1, MAX_PATH_LEN);
879	if (len == 0) s = _getcwd(buf1, MAX_PATH_LEN);
880    }
881#endif
882#else
883#ifdef HAVE_GETCWD
884    /* Signal blocking here is to work around a bug that occurred
885     * on Suns when the profiler signal interupts getcwd()
886     */
887# ifdef HAVE_SIGPROCMASK
888    sigset_t old_mask, block_mask;
889    (void) sigemptyset(&block_mask);
890    (void) sigaddset(&block_mask, SIGPROF);
891    (void) sigprocmask(SIG_BLOCK, &block_mask, &old_mask);
892# else
893#  ifdef HAVE_SIGVEC
894    int old_mask = sigblock(sigmask(SIGPROF));
895#  endif
896# endif
897    s = getcwd(buf1, (size_t) MAX_PATH_LEN);
898# ifdef HAVE_SIGPROCMASK
899    (void) sigprocmask(SIG_SETMASK, &old_mask, (sigset_t *) 0);
900# else
901#  ifdef HAVE_SIGVEC
902    (void) sigsetmask(old_mask);
903#  endif
904# endif
905#else
906    s = getwd(buf1);	/* system or our own definition from getwd.c */
907#endif
908#endif
909    if (s == 0) {	/* return local path if something went wrong */
910	errno = 0;
911	buf[0] = '.';
912	buf[1] = '/';
913	buf[2] = '\0';
914	return 2;
915    }
916    len = strlen(canonical_filename(buf1, buf));
917    if (buf[len-1] != '/')	/*  add trailing / if needed */
918    {
919	buf[len++] = '/';
920	buf[len] = '\0';
921    }
922    return len;
923}
924
925
926/* return string length (without terminator) */
927int
928ec_get_cwd(char *buf, int size)
929{
930    if (ec_use_own_cwd)
931    {
932	strcpy(buf, ec_cwd);
933	return strlen(ec_cwd);
934    }
935    else
936    {
937	return get_cwd(buf, size);
938    }
939}
940
941/* return 0 on success, -1 on error with Sys_Errno */
942int
943ec_set_cwd(char *name)
944{
945    if (ec_use_own_cwd)
946    {
947	char buf[MAX_PATH_LEN];
948	int len;
949	struct_stat st_buf;
950	name = expand_filename(name, buf, EXPAND_NORMALISE);
951	if (ec_stat(name, &st_buf)) {
952	    Set_Sys_Errno(errno, ERRNO_UNIX);
953	    return -1;
954	}
955	if (!S_ISDIR(st_buf.st_mode)) {
956	    Set_Sys_Errno(ENOTDIR, ERRNO_UNIX);	/* simulate chdir() */
957	    return -1;
958	}
959	strcpy(ec_cwd, buf);
960	len = strlen(ec_cwd);
961	if (ec_cwd[len-1] != '/') {
962	    ec_cwd[len] = '/';
963	    ec_cwd[len+1] = 0;
964	}
965
966    } else if (ec_chdir(name)) {
967	Set_Sys_Errno(errno, ERRNO_UNIX);
968	return -1;
969    }
970    return 0;
971}
972
973
974/*----------------------------------------------------------------------*
975 * dlopen
976 *----------------------------------------------------------------------*/
977
978/* use the dlopen compatibility code for MacOSX */
979#if !defined(HAVE_DLOPEN) && defined(HAVE_MACH_O_DYLD_H)
980#include "dlfcn_simple.c"
981#endif
982
983/*----------------------------------------------------------------------*
984 * Times
985 *----------------------------------------------------------------------*/
986
987/*
988 * User CPU time in clock ticks.
989 * The elapsed time in seconds can be computed as
990 *		user_time() / clock_hz
991 */
992long
993user_time(void)
994{
995#ifdef _WIN32
996    FILETIME creation_time, exit_time, kernel_time, user_time;
997    LARGE_INTEGER li;
998
999    if (GetProcessTimes(GetCurrentProcess(),
1000    		&creation_time, &exit_time, &kernel_time, &user_time))
1001    {
1002	li.LowPart = user_time.dwLowDateTime;
1003	li.HighPart = user_time.dwHighDateTime;
1004	return (long) (li.QuadPart / (10000000/CLOCKS_PER_SEC));
1005    }
1006    else
1007    {
1008	return (long) clock();
1009    }
1010#else
1011#if defined(HAVE_TIMES)
1012	struct tms      rusag;
1013
1014	(void) times(&rusag);
1015	return rusag.tms_utime;
1016#else
1017	/* try at least with the real time */
1018	return (long) time((time_t *) 0);
1019#endif
1020#endif
1021}
1022
1023
1024/*
1025 * Time in seconds since birth of UNIX
1026 */
1027long
1028ec_unix_time(void)
1029{
1030    return (long) time((time_t *) 0);
1031}
1032
1033
1034/*
1035 * User-CPU, System-CPU and Elapsed time for this Eclipse process as floats
1036 */
1037int
1038all_times(double *user, double *sys, double *elapsed)	/* in seconds */
1039{
1040
1041#ifdef _WIN32
1042
1043    FILETIME creation_time, exit_time, kernel_time, user_time, now_time;
1044
1045    if (GetProcessTimes(GetCurrentProcess(),
1046    		&creation_time, &exit_time, &kernel_time, &user_time))
1047    {
1048	*user = FileTimeToDouble(user_time);
1049	*sys = FileTimeToDouble(kernel_time);
1050    }
1051    else
1052    {
1053	*user = ((double) clock() / CLOCKS_PER_SEC);
1054	*sys = 0;
1055    }
1056    GetSystemTimeAsFileTime(&now_time);
1057    *elapsed = FileTimeToDouble(now_time) - start_time;
1058
1059#else /* UNIX */
1060
1061    struct tms		rusag;
1062#ifdef BSD_TIMES
1063    struct timeb	realtime;
1064    /* times() returns nothing useful in BSD, need ftime() for elapsed time */
1065    (void) ftime(&realtime);
1066    if (times(&rusag) == -1)
1067    {
1068      return(-1);
1069    }
1070    *elapsed = (realtime.time - start_time) + (double)realtime.millitm/1000.0;
1071#else
1072    clock_t		realtime;
1073    if ((realtime = times(&rusag)) == (clock_t) -1)
1074    {
1075      return(-1);
1076    }
1077    *elapsed = (double) (realtime - start_time) / clock_hz;
1078#endif
1079
1080    *user = (double) rusag.tms_utime / clock_hz;
1081    *sys = (double) rusag.tms_stime / clock_hz;
1082
1083#endif
1084    return 0;
1085}
1086
1087
1088char *
1089ec_date_string(char *buf)
1090{
1091    time_t ti = (long) time((time_t *) 0);
1092    strcpy(buf, ctime(&ti));
1093    return buf;
1094}
1095
1096
1097/*----------------------------------------------------------------------*
1098 * Other system services
1099 *----------------------------------------------------------------------*/
1100
1101
1102int
1103ec_gethostid(char *buf, int len)
1104{
1105
1106#ifdef _WIN32
1107
1108    /*
1109     * This code taken from
1110     * http://support.microsoft.com/kb/q118623/
1111     * It gets (an) Ethernet adapter hardware address, same as
1112     * ioctl(..., SIOCGIFHWADDR, ...) in Linux.
1113     */
1114
1115    typedef struct _ASTAT_
1116    {
1117        ADAPTER_STATUS adapt;
1118        NAME_BUFFER    NameBuff [30];
1119    } ASTAT;
1120
1121    NCB		Ncb;
1122    ASTAT	Adapter;
1123    LANA_ENUM   lenum;
1124    int 	i;
1125
1126    memset( &Ncb, 0, sizeof(Ncb) );
1127    Ncb.ncb_command = NCBENUM;
1128    Ncb.ncb_buffer = (UCHAR *)&lenum;
1129    Ncb.ncb_length = sizeof(lenum);
1130    if (Netbios( &Ncb ) != 0)
1131	return -1;
1132
1133    for(i=0; i < lenum.length ;i++)
1134    {
1135	memset( &Ncb, 0, sizeof(Ncb) );
1136	Ncb.ncb_command = NCBRESET;
1137	Ncb.ncb_lana_num = lenum.lana[i];
1138	if (Netbios( &Ncb ) != 0)
1139	    return -1;
1140
1141	memset( &Ncb, 0, sizeof (Ncb) );
1142	Ncb.ncb_command = NCBASTAT;
1143	Ncb.ncb_lana_num = lenum.lana[i];
1144	strcpy( Ncb.ncb_callname,  "*               " );
1145	Ncb.ncb_buffer = (char *) &Adapter;
1146	Ncb.ncb_length = sizeof(Adapter);
1147	if (Netbios( &Ncb ) != 0)
1148	    return -1;
1149
1150	sprintf(buf, "A#%02x%02x%02x%02x%02x%02x",
1151	      Adapter.adapt.adapter_address[0],
1152	      Adapter.adapt.adapter_address[1],
1153	      Adapter.adapt.adapter_address[2],
1154	      Adapter.adapt.adapter_address[3],
1155	      Adapter.adapt.adapter_address[4],
1156	      Adapter.adapt.adapter_address[5]);
1157
1158	/* return the first one we can get hold of */
1159	break;
1160    }
1161
1162#else
1163
1164#if defined(HAVE_SYSINFO) && defined(HAVE_SYS_SYSTEMINFO_H)
1165
1166    char *bufp = buf;
1167    if (sysinfo(SI_HW_PROVIDER, buf, len) == -1) {
1168	ec_os_errgrp_ = ERRNO_UNIX; ec_os_errno_ = errno; errno = 0;
1169	return -1;
1170    }
1171    bufp = buf + strlen(buf);
1172    *bufp++ = '#';
1173    if (sysinfo(SI_HW_SERIAL, bufp, len-(bufp-buf)) == -1) {
1174	ec_os_errgrp_ = ERRNO_UNIX; ec_os_errno_ = errno; errno = 0;
1175	return -1;
1176    }
1177
1178#else
1179#ifdef HAVE_SIOCGIFHWADDR
1180
1181    /*
1182     * This gets (an) Ethernet adapter hardware address
1183     */
1184
1185    int sockFd;
1186    struct ifreq req;
1187    struct sockaddr_in *sin;
1188
1189#ifndef IPPROTO_IP
1190#define IPPROTO_IP 0
1191#endif
1192
1193    sockFd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
1194
1195    memset(&req, 0, sizeof(struct ifreq));
1196    strcpy(req.ifr_name, "eth0");
1197
1198    if (ioctl(sockFd, SIOCGIFHWADDR ,&req) == 0)
1199    {
1200	sprintf(buf, "L#%02x%02x%02x%02x%02x%02x",
1201	    req.ifr_hwaddr.sa_data[0]&0xff, req.ifr_hwaddr.sa_data[1]&0xff,
1202	    req.ifr_hwaddr.sa_data[2]&0xff, req.ifr_hwaddr.sa_data[3]&0xff,
1203	    req.ifr_hwaddr.sa_data[4]&0xff, req.ifr_hwaddr.sa_data[5]&0xff);
1204    }
1205    else /* use gethostid() if it didn't work */
1206    {
1207	(void) sprintf(buf, "H#%d", gethostid());
1208    }
1209    close(sockFd);
1210
1211
1212#else
1213
1214    (void) sprintf(buf, "H#%d", gethostid());
1215
1216#endif
1217#endif
1218#endif
1219
1220    return strlen(buf);
1221}
1222
1223
1224int
1225ec_gethostname(char *buf, int size)	/* sets ec_os_errno_/errgrp_ on failure */
1226{
1227    int i;
1228    struct hostent *hp;
1229
1230
1231#if defined(HAVE_GETHOSTNAME)
1232    if (gethostname(buf, size)) {
1233# ifdef _WIN32
1234	ec_os_errgrp_ = ERRNO_WIN32; ec_os_errno_ = GetLastError();
1235# else
1236	ec_os_errgrp_ = ERRNO_UNIX; ec_os_errno_ = errno; errno = 0;
1237# endif
1238	return -1;
1239    }
1240#else
1241# if defined(HAVE_SYSINFO) && defined(HAVE_SYS_SYSTEMINFO_H)
1242/* Linux has sysinfo(), but not same interface or sys/systeminfo.h */
1243    if (sysinfo(SI_HOSTNAME, buf, size) == -1) {
1244	ec_os_errgrp_ = ERRNO_UNIX; ec_os_errno_ = errno; errno = 0;
1245	return -1;
1246    }
1247# else
1248    /* assume uname is defined */
1249    struct utsname utsn;
1250
1251    if (uname(&utsn) <= -1) {
1252#  ifdef _WIN32
1253	ec_os_errgrp_ = ERRNO_WIN32; ec_os_errno_ = GetLastError();
1254#  else
1255	ec_os_errgrp_ = ERRNO_UNIX; ec_os_errno_ = errno; errno = 0;
1256#  endif
1257	return -1;
1258    }
1259    strncpy(buf, utsn.nodename, size);
1260# endif
1261#endif
1262
1263#ifdef sun4_0
1264/*  There is a bug with the Sun4 static C library version of gethostbyname():
1265 *  it seems to strip the full hostname (with dots) and return the single
1266 *  component (without dots), rather than the other way round
1267 */
1268    hp = NULL;
1269#elif defined(BARRELFISH)
1270    hp = NULL;
1271#else
1272    hp = gethostbyname(buf);
1273#endif
1274#if defined(BARRELFISH)
1275    buf = "barrelfish.local";
1276#else
1277    if (hp != NULL) {
1278      strncpy(buf, hp->h_name, size);
1279    }
1280#endif
1281
1282    return strlen(buf);
1283}
1284
1285
1286void
1287ec_sleep(double seconds)
1288{
1289#ifdef _WIN32
1290    (void) SleepEx((DWORD) (seconds*1000.0), TRUE);
1291#else
1292#ifdef HAVE_SELECT
1293    struct timeval	sleep_time;
1294    fd_set		rs, ws, es;
1295
1296    sleep_time.tv_sec = (long) seconds;
1297    sleep_time.tv_usec = (long) ((seconds - floor(seconds)) * 1000000.0);
1298    FD_ZERO(&rs);
1299    FD_ZERO(&ws);
1300    FD_ZERO(&es);
1301    (void) select(0, &rs, &ws, &es, &sleep_time);
1302#else
1303#ifdef HAVE_SLEEP
1304    (void) sleep((unsigned) seconds);
1305#endif
1306#endif
1307#endif
1308}
1309
1310void
1311ec_bad_exit(char *msg)
1312{
1313#ifdef _WIN32
1314    FatalAppExit(0, msg);
1315#else
1316    (void) write(2, msg, strlen(msg));
1317    (void) write(2, "\n", 1);
1318    exit(-1);
1319#endif
1320}
1321
1322#ifndef HAVE_STRERROR
1323char *
1324strerror(int n)
1325{
1326    extern int	sys_nerr;
1327    extern char	*sys_errlist[];
1328    if (n < 0 || n >= sys_nerr)
1329    	return (char *) 0;
1330    return sys_errlist[n];
1331}
1332#endif
1333
1334char *
1335ec_os_err_string(int err, int grp, char *buf, int size)
1336{
1337#ifdef _WIN32
1338    switch (grp)
1339    {
1340    case ERRNO_WIN32:
1341	if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
1342		0, (LPTSTR) buf, size, NULL))
1343	{
1344	    sprintf(buf, "Windows error %d", err);
1345	}
1346	return buf;
1347
1348    case ERRNO_UNIX:
1349#endif
1350	if (err == 0)
1351	    return "";
1352	else
1353	{
1354	    char * message = strerror(err);
1355	    if (message)
1356	        return message;
1357	    sprintf(buf, "Unix error %d", err);
1358	    return buf;
1359	}
1360#ifdef _WIN32
1361    }
1362#endif
1363}
1364
1365#ifdef _WIN32
1366int
1367ec_getch_raw(int unit)
1368{
1369    return _getch();
1370}
1371
1372int
1373ec_putch_raw(int c)
1374{
1375    _putch(c);
1376    return 0;
1377}
1378#endif
1379
1380
1381/*
1382 * On SunOS4 with gcc version 2.95.3 20010315 (release) at least, strcmp()
1383 * seems to have a bug and accesses memory beyond the end of the string s2.
1384 * This causes segmentation violations when the string happens to be right
1385 * at the end of mapped memory.
1386 */
1387
1388#ifdef sun4_0
1389int strcmp(char *s1, char *s2)
1390{
1391    while (*s1 == *s2) {
1392	if (!*s1) return 0;
1393    	++s1; ++s2;
1394    }
1395    return *s1 - *s2;
1396}
1397#endif
1398
1399
1400/*----------------------------------------------------------------------
1401 * Simple thread interface
1402 *----------------------------------------------------------------------*/
1403
1404#ifdef _WIN32
1405
1406typedef struct {
1407    HANDLE thread_handle;
1408
1409    /* this event signals that a function and data has been supplied */
1410    HANDLE start_event;
1411
1412    /* this event signals that the function has terminated */
1413    HANDLE done_event;
1414
1415    /* the function to execute, NULL signals termination request */
1416    int (* volatile fun)(void *);
1417
1418    /* argument for the function call: valid iff fun!=NULL */
1419    void * volatile data;
1420
1421    /* result of the function call: valid iff fun==NULL */
1422    int volatile result;
1423
1424} thread_data;
1425
1426
1427/* The general thread procedure */
1428
1429static
1430DWORD WINAPI
1431ec_fun_thread(thread_data *desc)
1432{
1433    for(;;)
1434    {
1435	DWORD res;
1436
1437	/* wait for a SetEvent() */
1438	res = WaitForSingleObject(desc->start_event, INFINITE);
1439	if (res == WAIT_FAILED)
1440	    ec_bad_exit("ECLiPSe: thread wait failed");
1441
1442	if (!desc->fun)		/* no function = termination request */
1443	    ExitThread(1);
1444
1445	desc->result = (*desc->fun)(desc->data);	/* run it ...	*/
1446	desc->fun = NULL;		/* ... and signal stopping	*/
1447
1448	if (!SetEvent(((thread_data*)desc)->done_event))
1449	{
1450	    ec_bad_exit("ECLiPSe: thread stopping signalling failed");
1451	}
1452    }
1453    return 1;
1454}
1455
1456void *
1457ec_make_thread(void)
1458{
1459    DWORD thread_id;
1460    thread_data *desc = malloc(sizeof(thread_data));
1461
1462    desc->fun = NULL;
1463    desc->start_event = CreateEvent(NULL, FALSE, FALSE, NULL);
1464    if (!desc->start_event)
1465    {
1466	Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1467	return NULL;
1468    }
1469    desc->done_event = CreateEvent(NULL, TRUE, FALSE, NULL);
1470    if (!desc->done_event)
1471    {
1472	Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1473	return NULL;
1474    }
1475    desc->thread_handle = CreateThread(NULL, 0,
1476	(LPTHREAD_START_ROUTINE) ec_fun_thread,
1477	(LPVOID) desc, 0, &thread_id);
1478    if (!desc->thread_handle)
1479    {
1480	Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1481	return NULL;
1482    }
1483#if 0
1484    /*
1485     * The following line should not be needed, but we had problems
1486     * with the thread not being started otherwise - Windows bug?
1487     */
1488    ResumeThread(desc->thread_handle);
1489#endif
1490    return (void *) desc;
1491}
1492
1493
1494/*
1495 * Test whether the thread has finished (without waiting).
1496 * Returns: 1 if thread stopped, 0 otherwise
1497 * If stopped, *result contains the result of the thread computation>
1498 */
1499
1500int
1501ec_thread_stopped(void *desc, int *result)
1502{
1503    if (!((thread_data*)desc)->fun)
1504    {
1505	*result = ((thread_data*)desc)->result;
1506	return 1;
1507    }
1508    return 0;
1509}
1510
1511
1512/*
1513 * Wait for the thread to finish (max timeout milliseconds)
1514 * timeout == -1:	wait indefinitely
1515 * timeout == 0:	don't wait
1516 * timeout > 0:		wait timeout milliseconds
1517 * Returns: 1 if thread stopped, 0 otherwise
1518 * If stopped, *result contains the result of the thread computation>
1519 */
1520
1521int
1522ec_thread_wait(void *desc, int *result, int timeout)
1523{
1524    if (timeout != 0)
1525    {
1526	switch(WaitForSingleObject(((thread_data*)desc)->done_event,
1527		    timeout > 0 ? (DWORD) timeout : INFINITE))
1528	{
1529	case WAIT_OBJECT_0:	/* stopping was signalled */
1530	case WAIT_TIMEOUT:	/* timeout occurred */
1531	    break;
1532
1533	default:
1534	    ec_bad_exit("ECLiPSe: thread wait failed");
1535	}
1536    }
1537    return ec_thread_stopped(desc, result);
1538}
1539
1540int
1541ec_start_thread(void *desc, int (*fun)(void *), void *data)
1542{
1543    if (((thread_data*)desc)->fun)
1544    	return 0;	/* thread still busy */
1545
1546    if (!ResetEvent(((thread_data*)desc)->done_event))
1547    {
1548	Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1549	return 0;
1550    }
1551    ((thread_data*)desc)->data = data;
1552    ((thread_data*)desc)->fun = fun;
1553    if (!SetEvent(((thread_data*)desc)->start_event))
1554    {
1555	Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1556	return 0;
1557    }
1558    return 1;
1559}
1560
1561/*
1562 * Terminate the thread. This should only be done when already stopped.
1563 * Returns: 1 if cleanly terminated, 0 if forcibly terminated, -1 error
1564 */
1565int
1566ec_thread_terminate(void *desc, int timeout)
1567{
1568    int result;
1569    DWORD thread_exit_code = 0;
1570
1571    if (ec_thread_stopped(desc, &result))
1572    {
1573	/* restart the thread with NULL function: leads to termination */
1574	((thread_data*)desc)->fun = 0;
1575	if (!SetEvent(((thread_data*)desc)->start_event))
1576	{
1577	    Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1578	    return -1;
1579	}
1580    }
1581    else
1582    {
1583	/* still running, terminate forcibly */
1584	if (!TerminateThread(((thread_data*)desc)->thread_handle, 0))
1585	{
1586	    Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1587	    return -1;
1588	}
1589    }
1590    switch(WaitForSingleObject(((thread_data*)desc)->thread_handle, timeout > 0 ? (DWORD)timeout : INFINITE))
1591    {
1592    case WAIT_OBJECT_0:	/* termination was signalled */
1593	if (!GetExitCodeThread(((thread_data*)desc)->thread_handle, &thread_exit_code))
1594	{
1595	    Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1596	    return -1;
1597	}
1598	/*thread_exit_code is 0 or 1 */
1599	break;
1600
1601    default:
1602    case WAIT_TIMEOUT:	/* timeout occurred, terminate forcibly */
1603	if (!TerminateThread(((thread_data*)desc)->thread_handle, 0))
1604	{
1605	    Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1606	    return -1;
1607	}
1608	/* thread_exit_code is 0, don't bother to wait */
1609	break;
1610
1611    case WAIT_FAILED:
1612	Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1613	return -1;
1614    }
1615
1616    if (!CloseHandle(((thread_data*)desc)->thread_handle) ||
1617	!CloseHandle(((thread_data*)desc)->start_event) ||
1618	!CloseHandle(((thread_data*)desc)->done_event))
1619    {
1620	Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1621	return -1;
1622    }
1623    free(desc);
1624    return thread_exit_code;	/* 0 or 1 */
1625}
1626
1627#endif
1628
1629
1630/*----------------------------------------------------------------------
1631 * Timers
1632 *----------------------------------------------------------------------*/
1633#ifdef _WIN32
1634
1635/*
1636 * Windows doesn't seem to have timers that you can ask for the
1637 * remaining time. We therefore store the due time ourselves.
1638 * The DWORDs are all in milliseconds, the LONGLONG is 100 ns units.
1639 */
1640typedef struct {
1641    HANDLE thread_handle;		/* must be first */
1642
1643/* signals that the new_xxx settings are valid and should be accepted */
1644    HANDLE time_set_event;
1645
1646/* signals that new settings have been accepted and old_xxx are valid */
1647    HANDLE time_accept_event;
1648
1649/* input (w for main, r for thread) */
1650    DWORD new_first;			/* first interval (ms) */
1651    DWORD new_ivl;			/* future intervals (ms) */
1652    void (*new_callback)(long);		/* callback function ... */
1653    long new_cb_arg;			/* ... and its argument */
1654    int terminate_req;			/* request to terminate alarm thread */
1655
1656/* output ( w for thread, r for main) */
1657    DWORD old_remain;			/* remaining time when stopped (ms) */
1658    DWORD old_ivl;			/* old interval setting (ms) */
1659
1660/* status (w for thread, r for main) */
1661    int running;			/* set while timer running */
1662
1663/* local (r/w for thread) */
1664    LONGLONG active_due;		/* current due time (100 ns FILETIME) */
1665    DWORD active_ivl;			/* current future intervals (ms) */
1666    void (*active_callback)(long);	/* current callback function ... */
1667    long active_cb_arg;			/* ... and its argument */
1668
1669} timer_thread;
1670
1671volatile timer_thread alarm_thread = { /*thread_handle*/ NULL };
1672
1673
1674/* The timer thread procedure */
1675
1676DWORD WINAPI
1677ec_alarm_thread(timer_thread *desc)
1678{
1679    DWORD next_timeout = INFINITE;
1680
1681    for(;;)
1682    {
1683	DWORD res;
1684	FILETIME now_time_ft;
1685	LARGE_INTEGER now_time;
1686
1687	res = WaitForSingleObject(desc->time_set_event, next_timeout);
1688	if (res == WAIT_FAILED)
1689	{
1690	    ec_bad_exit("ECLiPSe: timer thread WaitForSingleObject() failed");
1691	}
1692
1693	GetSystemTimeAsFileTime(&now_time_ft);
1694	now_time.LowPart = now_time_ft.dwLowDateTime;
1695	now_time.HighPart = now_time_ft.dwHighDateTime;
1696
1697	/*
1698	 * Check whether the active timer has expired and do the callback
1699	 * if so. Then either cancel or schedule the next interval.
1700	 */
1701	if (desc->running)
1702	{
1703	    if (now_time.QuadPart >= desc->active_due)
1704	    {
1705		/* the alarm is due */
1706		(*desc->active_callback)(desc->active_cb_arg);
1707		if (desc->active_ivl)
1708		{
1709		    /* schedule the next interval */
1710		    desc->active_due = now_time.QuadPart + (LONGLONG)desc->active_ivl*10000;
1711		    desc->running = 1;
1712		    next_timeout = desc->active_ivl;
1713		}
1714		else
1715		{
1716		    /* nothing more to do */
1717		    desc->active_due = 0;
1718		    desc->active_ivl = 0;
1719		    desc->running = 0;
1720		    next_timeout = INFINITE;
1721		}
1722	    }
1723	    else  /* timed out too early, or SetEvent */
1724	    {
1725		desc->running = 1;
1726		next_timeout = (desc->active_due - now_time.QuadPart)/10000;
1727		if (next_timeout == 0) next_timeout = 1;
1728	    }
1729	}
1730
1731	/*
1732	 * If we had a timer_set_event, pick up
1733	 * the new times and return the old ones.
1734	 */
1735	if (res == WAIT_OBJECT_0)
1736	{
1737	    if (desc->terminate_req)
1738	    {
1739		break;		/* same as ExitThread(1); */
1740	    }
1741	    else
1742	    {
1743		desc->old_remain = desc->running ? next_timeout : 0;
1744		desc->old_ivl = desc->active_ivl;
1745
1746		if (desc->new_first)		/* change settings */
1747		{
1748		    desc->active_due = now_time.QuadPart + (LONGLONG)desc->new_first*10000;
1749		    desc->active_ivl = desc->new_ivl;
1750		    desc->active_callback = desc->new_callback;
1751		    desc->active_cb_arg = desc->new_cb_arg;
1752		    desc->running = 1;
1753		    next_timeout = desc->new_first;
1754		}
1755		else				/* clear settings */
1756		{
1757		    desc->active_due = 0;
1758		    desc->active_ivl = 0;
1759		    desc->running = 0;
1760		    next_timeout = INFINITE;
1761		}
1762		/* indicate acceptance */
1763		if (!SetEvent(desc->time_accept_event))
1764		{
1765		    ec_bad_exit("ECLiPSe: timer thread SetEvent() failed");
1766		}
1767	    }
1768	}
1769    }
1770    return 1;
1771}
1772
1773
1774int
1775ec_set_alarm(
1776	double first,			/* new initial interval (0: stop timer) */
1777	double interv,			/* new periodic interval (0: one shot) */
1778	void (*callback)(long),		/* callback function ... */
1779	long cb_arg,			/* ... and its argument */
1780	double *premain,		/* return time to next timeout */
1781	double *old_interv)		/* return previous interval setting */
1782{
1783    if (!alarm_thread.thread_handle)	/* create thread if not yet there */
1784    {
1785	DWORD thread_id;
1786	alarm_thread.terminate_req = 0;
1787	alarm_thread.running = 0;
1788	alarm_thread.time_set_event = CreateEvent(NULL, FALSE, FALSE, NULL);
1789	if (!alarm_thread.time_set_event)
1790	{
1791	    Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1792	    return 0;
1793	}
1794	alarm_thread.time_accept_event = CreateEvent(NULL, FALSE, FALSE, NULL);
1795	if (!alarm_thread.time_accept_event)
1796	{
1797	    Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1798	    return 0;
1799	}
1800	alarm_thread.thread_handle = CreateThread(NULL, 0,
1801	    (LPTHREAD_START_ROUTINE) ec_alarm_thread,
1802	    (LPVOID) &alarm_thread, 0, &thread_id);
1803	if (!alarm_thread.thread_handle)
1804	{
1805	    Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1806	    return 0;
1807	}
1808    }
1809
1810    /* write new parameters into the descriptor */
1811    alarm_thread.new_first = (DWORD) (first*1000.0);
1812    if (alarm_thread.new_first==0 && first>0.0) alarm_thread.new_first = 1;
1813    alarm_thread.new_ivl = (DWORD) (interv*1000.0);
1814    if (alarm_thread.new_ivl==0 && interv>0.0) alarm_thread.new_ivl = 1;
1815    alarm_thread.new_callback = callback;
1816    alarm_thread.new_cb_arg = cb_arg;
1817
1818    if (!SetEvent(alarm_thread.time_set_event))
1819    {
1820	Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1821	return 0;
1822    }
1823
1824    /* wait for thread to accept new times and return old ones */
1825    switch (WaitForSingleObject(alarm_thread.time_accept_event, 10000))
1826    {
1827    case WAIT_FAILED:
1828	Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1829	return 0;
1830
1831    default:
1832	Set_Sys_Errno(ERROR_TIMEOUT,ERRNO_WIN32);
1833	return 0;
1834
1835    case WAIT_OBJECT_0:
1836	if (premain) *premain = alarm_thread.old_remain / 1000.0;
1837	if (old_interv) *old_interv = alarm_thread.old_ivl / 1000.0;
1838	return 1;
1839    }
1840}
1841
1842
1843/*
1844 * Terminate the alarm thread.
1845 * Returns: 1 if cleanly terminated, 0 if forcibly terminated, -1 error
1846 */
1847int
1848ec_terminate_alarm()
1849{
1850    DWORD thread_exit_code = 0;
1851
1852    if (!alarm_thread.thread_handle)
1853        return 0;
1854
1855    /* send a termination request to the thread */
1856    alarm_thread.terminate_req = 1;
1857    if (!SetEvent(alarm_thread.time_set_event))
1858    {
1859	Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1860	return -1;
1861    }
1862
1863    /* wait for termination */
1864    switch(WaitForSingleObject(alarm_thread.thread_handle, 3000))
1865    {
1866    case WAIT_OBJECT_0:	/* termination was signalled */
1867	if (!GetExitCodeThread(alarm_thread.thread_handle, &thread_exit_code))
1868	{
1869	    Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1870	    return -1;
1871	}
1872	/* thread_exit_code is 0 or 1 */
1873	break;
1874
1875    default:
1876    case WAIT_TIMEOUT:	/* timeout occurred, terminate forcibly */
1877	if (!TerminateThread(alarm_thread.thread_handle, 0))
1878	{
1879	    Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1880	    return -1;
1881	}
1882	/* thread_exit_code is 0, don't bother to wait */
1883	break;
1884
1885    case WAIT_FAILED:
1886	Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1887	return -1;
1888    }
1889
1890    if (!CloseHandle(alarm_thread.thread_handle) ||
1891	!CloseHandle(alarm_thread.time_set_event) ||
1892	!CloseHandle(alarm_thread.time_accept_event))
1893    {
1894	Set_Sys_Errno(GetLastError(),ERRNO_WIN32);
1895	return -1;
1896    }
1897
1898    alarm_thread.thread_handle = NULL;
1899    return thread_exit_code;	/* 0 or 1 */
1900}
1901
1902
1903#else
1904#ifdef HAVE_PTHREAD_H
1905
1906/*
1907 * Unix pthreads version
1908 */
1909
1910typedef struct {
1911    pthread_t thread_handle;     	/* must be first */
1912    pthread_mutex_t mutex;              /* mutex for the two conds */
1913
1914/* signals that the new_xxx settings are valid and should be accepted */
1915    pthread_cond_t time_set_event;
1916
1917/* signals that new settings have been accepted and old_xxx are valid */
1918    pthread_cond_t time_accept_event;
1919
1920/* input (w for main, r for thread) */
1921    double volatile new_first;		/* first interval (s) */
1922    double volatile new_ivl;		/* future intervals (s) */
1923    void (* volatile new_callback)(long);	/* callback function ... */
1924    long volatile new_cb_arg;		/* ... and its argument */
1925    int volatile terminate_req;		/* request to terminate alarm thread */
1926
1927/* output ( w for thread, r for main) */
1928    double volatile old_remain;		/* remaining time when stopped (s) */
1929    double volatile old_ivl;		/* old interval setting (s) */
1930
1931/* status (w for thread, r for main) */
1932    int volatile running;		/* set while timer running */
1933
1934/* local (r/w for thread) */
1935    double active_due;	 	        /* current due time (s since epoch) */
1936    double active_ivl;		        /* current future intervals (s) */
1937    void (*active_callback)(long);      /* current callback function ... */
1938    long active_cb_arg;	                /* ... and its argument */
1939
1940} timer_thread;
1941
1942timer_thread alarm_thread = { /*thread_handle*/ (pthread_t) NULL };
1943
1944
1945/* The timer thread procedure */
1946
1947void *
1948ec_alarm_thread(timer_thread *desc)
1949{
1950    pthread_mutex_lock(&desc->mutex);
1951    pthread_cond_signal(&desc->time_accept_event);
1952    for(;;)
1953    {
1954	int res;
1955	struct timeval now_timeval;
1956        double now_time;
1957
1958        if (desc->running)
1959        {
1960            struct timespec next_timeout_spec;
1961            next_timeout_spec.tv_sec = (time_t) desc->active_due;
1962            next_timeout_spec.tv_nsec = (desc->active_due - next_timeout_spec.tv_sec) * 1e9;
1963            res = pthread_cond_timedwait(&desc->time_set_event, &desc->mutex, &next_timeout_spec);
1964        }
1965        else
1966        {
1967            res = pthread_cond_wait(&desc->time_set_event, &desc->mutex);
1968        }
1969	if (res && res != ETIMEDOUT)
1970	{
1971            pthread_mutex_unlock(&desc->mutex);
1972	    ec_bad_exit("ECLiPSe: timer thread WaitForSingleObject() failed");
1973	}
1974
1975        gettimeofday(&now_timeval, NULL);
1976        now_time = (double)now_timeval.tv_sec + (double)now_timeval.tv_usec/1000000.0;
1977
1978	/*
1979	 * Check whether the active timer has expired and do the callback
1980	 * if so. Then either cancel or schedule the next interval.
1981	 */
1982	if (desc->running)
1983	{
1984	    if (now_time >= desc->active_due)
1985	    {
1986		/* the alarm is due */
1987		(*desc->active_callback)(desc->active_cb_arg);
1988		if (desc->active_ivl > 0.0)
1989		{
1990		    /* schedule the next interval */
1991		    desc->active_due = now_time + desc->active_ivl;
1992		    desc->running = 1;
1993		}
1994		else
1995		{
1996		    /* nothing more to do */
1997		    desc->active_due = 0.0;
1998		    desc->active_ivl = 0.0;
1999		    desc->running = 0;
2000		}
2001	    }
2002	    /* else timed out too early, or SetEvent */
2003	}
2004
2005	/*
2006	 * If we had a timer_set_event, pick up
2007	 * the new times and return the old ones.
2008	 */
2009	if (res == 0)
2010	{
2011	    if (desc->terminate_req)
2012	    {
2013		break;		/* same as pthread_exit(1); */
2014	    }
2015	    else
2016	    {
2017		desc->old_remain = desc->running ? desc->active_due - now_time : 0.0;
2018		desc->old_ivl = desc->active_ivl;
2019
2020		if (desc->new_first)		/* change settings */
2021		{
2022		    desc->active_due = now_time + desc->new_first;
2023		    desc->active_ivl = desc->new_ivl;
2024		    desc->active_callback = desc->new_callback;
2025		    desc->active_cb_arg = desc->new_cb_arg;
2026		    desc->running = 1;
2027		}
2028		else				/* clear settings */
2029		{
2030		    desc->active_due = 0;
2031		    desc->active_ivl = 0;
2032		    desc->running = 0;
2033		}
2034		/* indicate acceptance */
2035                pthread_cond_signal(&desc->time_accept_event);
2036	    }
2037	}
2038    }
2039    pthread_mutex_unlock(&desc->mutex);
2040    return (void*) 1;
2041}
2042
2043
2044int
2045ec_set_alarm(
2046	double first,			/* new initial interval (0: stop timer) */
2047	double interv,			/* new periodic interval (0: one shot) */
2048	void (*callback)(long),		/* callback function ... */
2049	long cb_arg,			/* ... and its argument */
2050	double *premain,		/* return time to next timeout */
2051	double *old_interv)		/* return previous interval setting */
2052{
2053    int res;
2054    if (!alarm_thread.thread_handle)	/* create thread if not yet there */
2055    {
2056	alarm_thread.terminate_req = 0;
2057	alarm_thread.running = 0;
2058        if (pthread_mutex_init(&alarm_thread.mutex, NULL)
2059         || pthread_cond_init(&alarm_thread.time_set_event, NULL)
2060         || pthread_cond_init(&alarm_thread.time_accept_event, NULL)
2061         || pthread_mutex_lock(&alarm_thread.mutex)
2062	 || pthread_create(&alarm_thread.thread_handle, NULL,
2063                (void*(*)(void*))ec_alarm_thread, (void*) &alarm_thread)
2064            /* wait for ec_alarm_thread to be ready */
2065         || pthread_cond_wait(&alarm_thread.time_accept_event, &alarm_thread.mutex)
2066        )
2067        {
2068            pthread_mutex_unlock(&alarm_thread.mutex);
2069	    Set_Sys_Errno(errno, ERRNO_UNIX);
2070	    return 0;
2071        }
2072    }
2073    else
2074    {
2075        pthread_mutex_lock(&alarm_thread.mutex);
2076    }
2077
2078    /* write new parameters into the descriptor */
2079    alarm_thread.new_first = (0.0 < first && first < 1.0e-6) ? 1.0e-6 : first;
2080    alarm_thread.new_ivl = (0.0 < interv && interv < 1.0e-6) ? 1.0e-6 : interv;
2081    alarm_thread.new_callback = callback;
2082    alarm_thread.new_cb_arg = cb_arg;
2083
2084    pthread_cond_signal(&alarm_thread.time_set_event);
2085
2086    /* wait for thread to accept new times and return old ones */
2087    res = pthread_cond_wait(&alarm_thread.time_accept_event, &alarm_thread.mutex);
2088    pthread_mutex_unlock(&alarm_thread.mutex);
2089    if (res)
2090    {
2091        Set_Sys_Errno(errno, ERRNO_UNIX);
2092	return 0;
2093    }
2094
2095    if (premain) *premain = alarm_thread.old_remain;
2096    if (old_interv) *old_interv = alarm_thread.old_ivl;
2097    return 1;
2098}
2099
2100
2101/*
2102 * Terminate the alarm thread.
2103 * Returns: 1 if cleanly terminated, 0 if forcibly terminated, -1 error
2104 */
2105int
2106ec_terminate_alarm()
2107{
2108    void *thread_exit_code = NULL;
2109
2110    if (!alarm_thread.thread_handle)
2111        return 0;
2112
2113    /* send a termination request to the thread */
2114    alarm_thread.terminate_req = 1;
2115    pthread_mutex_lock(&alarm_thread.mutex);
2116    pthread_cond_signal(&alarm_thread.time_set_event);
2117    pthread_mutex_unlock(&alarm_thread.mutex);
2118
2119    /* wait for termination */
2120    if (pthread_join(alarm_thread.thread_handle, &thread_exit_code))
2121    {
2122        Set_Sys_Errno(errno, ERRNO_UNIX);
2123	return -1;
2124    }
2125
2126    pthread_cond_destroy(&alarm_thread.time_set_event);
2127    pthread_cond_destroy(&alarm_thread.time_accept_event);
2128    pthread_mutex_destroy(&alarm_thread.mutex);
2129    alarm_thread.thread_handle = (pthread_t) NULL;
2130    return thread_exit_code ? 1 : 0;	/* 0 or 1 */
2131}
2132
2133#else
2134int ec_terminate_alarm() { return 0; }
2135#endif
2136#endif
2137
2138
2139/*----------------------------------------------------------------------
2140 * Registry/Environment lookup
2141 *
2142 * Windows:
2143 *	look up <name> under
2144 *		HKEY_LOCAL_MACHINE\SOFTWARE\IC-Parc\ECLiPSe
2145 *	or else
2146 *		HKEY_LOCAL_MACHINE\SOFTWARE\IC-Parc\ECLiPSe\<version>
2147 *	or else
2148 *		as environment variable <name>
2149 *      Note that 64 bit Windows keeps two sets of registry entries, for
2150 *      32 and 64 bit applications, so i386_nt and x86_64_nt ECLiPSe
2151 *      running on the same machine will not share their registry entries
2152 *
2153 * Unix:
2154 *	look up <name>
2155 *		as environment variable <name>_<major>_<minor>
2156 *		e.g. ECLIPSEDIR_5_10 for version 5.10
2157 *	or else
2158 *		as environment variable <name>
2159 *		e.g. ECLIPSEDIR
2160 *
2161 * return NULL if not found, buffer address otherwise.
2162 *
2163 * A buffer must be provided by the caller.
2164 * buflen is a pointer to the length of the buffer provided,
2165 * it is overwritten with the number of bytes actually returned
2166 * (CAUTION: this size includes a terminating zero if any).
2167 * If this number is greater than the buflen initially provided,
2168 * it is undefined whether any data is returned in the buffer.
2169 *----------------------------------------------------------------------*/
2170
2171#ifdef _WIN32
2172
2173char *
2174ec_env_lookup(char *name, char *buf, int *buflen)
2175{
2176    HKEY key1, key2;
2177    LONG err;
2178    DWORD vtype, buflen_dw;
2179    char *res;
2180    int len;
2181
2182    err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\IC-Parc\\ECLiPSe", 0, KEY_READ, &key1);
2183    if (err != ERROR_SUCCESS)
2184    {
2185	goto _try_env_;
2186    }
2187    err = RegOpenKeyEx(key1, ec_version, 0, KEY_READ, &key2);
2188    if (err != ERROR_SUCCESS)
2189    {
2190	(void) RegCloseKey(key1);
2191	goto _try_env_;
2192    }
2193    (void) RegCloseKey(key1);
2194    buflen_dw = *buflen;
2195    err = RegQueryValueEx(key2, name, NULL, &vtype, buf, &buflen_dw);
2196    *buflen = buflen_dw;
2197    (void) RegCloseKey(key2);
2198    if (!(err == ERROR_SUCCESS || err == ERROR_MORE_DATA))
2199    {
2200	goto _try_env_;
2201    }
2202    return buf;
2203
2204_try_env_:
2205    len = GetEnvironmentVariable(name, buf, *buflen);
2206    /* On Windows, we can't tell the difference between an unset variable
2207     * and an empty string!  Empty strings therefore look like unset.  */
2208    if (len == 0)
2209	return NULL;
2210    /* If buffer was large enough, len is string size without terminator,
2211     * otherwise required buffer size with terminator! */
2212    *buflen = len < *buflen ? len + 1 : len;
2213    return buf;
2214}
2215
2216#else
2217
2218char *
2219ec_env_lookup(char *name, char *buf, int *buflen)
2220{
2221    int i, len;
2222    char *res, *from, *to;
2223    char *vname = (char *) malloc(strlen(name)+strlen(ec_version)+2);
2224
2225    /* construct name_X_Y from name and ec_version X.Y */
2226    for(from=name,to=vname; *from; ++from,++to)
2227    	*to = *from;
2228    *to++ = '_';
2229    for(from=ec_version; *from; ++from,++to)
2230    	*to = *from == '.' ? '_' : *from;
2231    *to = 0;
2232
2233    if (!(res = getenv(vname)) && !(res = getenv(name)))
2234    {
2235	free(vname);
2236	Set_Sys_Errno(0, ERRNO_UNIX);
2237	return NULL;
2238    }
2239    free(vname);
2240    len = strlen(res)+1;
2241    if (len > *buflen)
2242	strncpy(buf, res, *buflen);
2243    else
2244	strncpy(buf, res, len);
2245    *buflen = len;
2246    return buf;
2247}
2248
2249#endif
2250