/*


    Copyright (C) 2016 Lutz Mueller

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "newlisp.h"
#include <errno.h>
#include "protos.h"

#define AF_UNSPEC 0 /* from socket.h or winsock2.h */


#if defined(SOLARIS) || defined(TRU64) || defined(AIX) 
#include <stropts.h>
#endif

#ifdef SOLARIS
#define FIONREAD I_NREAD
#endif

#ifndef WINDOWS
#include <sys/types.h>
#ifndef ANDROID
#ifndef EMSCRIPTEN
#include <sys/ipc.h>
#include <sys/sem.h>
#endif
#endif
#include <sys/mman.h>
#include <sys/ioctl.h>
#endif

int init_argv(char * ptr, char *argv[]);
char * getUUID(char * str, char * node);

#ifdef OS2
#include <conio.h>
int semctl(int semid, int semnum, int cmd, ...);
#endif

#ifndef TRU64
extern char ** environ;
#endif

#ifdef WINDOWS
#define fgetc win_fgetc
#define realpath win_realpath
#include <conio.h>  
#include <io.h>
#include <direct.h>
#define popen  _popen
#define pclose _pclose
#define pipe _pipe

/* 
Set binary as default file mode for Windows.
See also http://www.mingw.org/MinGWiki/index.php/binary
*/
unsigned int _CRT_fmode = _O_BINARY;

int setenv (const char *name, const char *value, int replace);
#endif /* Win32 */

#ifndef WINDOWS
#include <sys/socket.h>
#define SOCKET_ERROR -1
#define INVALID_SOCKET -1
#endif

#if defined(LINUX) || defined(CYGWIN)
char * strptime(const char * str, const char * fmt, struct tm * ttm);
#endif

time_t calcDateValue(int year, int month, int day, int hour, int min, int sec);
ssize_t currentDateValue(void);
extern STREAM readLineStream;
extern FILE * IOchannel;
extern int pagesize;

extern char * errorMessage[];
extern STREAM errorStream;
extern UINT netErrorIdx;
extern int newlispLibConsoleFlag;

/* semaphore() function type */
#ifndef NO_SEMAPHORE
#define SEM_CREATE 0
#define SEM_STATUS 1
#define SEM_SIGNAL 2
#endif

/* used in fork and spawn */
int parentPid = 0;
/* share, message */
CELL * readWriteShared(UINT * address, CELL * params, int flag);
CELL * readWriteSocket(int socket, CELL * params);
CELL * readWriteSharedExpression(UINT * adress, CELL * params);

void checkDeleteShareFile(UINT * address);

CELL * p_isFile(CELL * params) /* includes dev,socket,dir,file etc. */
{
char * fileName;
int flag;

params = getString(params, &fileName);
flag = getFlag(params);

return(isFile(fileName, flag) ? nilCell : flag ? stuffString(fileName) : trueCell);
}

int isFile(char * fileName, int flag)
{
struct stat fileInfo;
int result;

#ifdef WINDOWS
char slash;
size_t len;

len = strlen(fileName);
slash = *(fileName + len - 1);
if((slash == '\\' || slash == '/') && (!(len >= 2 && *(fileName + len - 2) == ':')))
    *(fileName + len - 1) = 0;

#ifdef USE_WIN_UTF16PATH
result = stat_utf16(fileName, &fileInfo);
#else
result = stat(fileName, &fileInfo);
#endif
if(slash == '\\' || slash == '/')
    *(fileName + len - 1) = slash;
#else /* not WINDOWS */
result = stat(fileName, &fileInfo);
#endif
if(result == 0)
    {
    if(flag)
        result = ! S_ISREG(fileInfo.st_mode);
    }

return(result);
}

CELL * p_isDirectory(CELL * params)
{
char * fileName;

getString(params, &fileName);
return(isDir(fileName) ? trueCell : nilCell);
}

int isDir(char * fileName)
{
struct stat fileInfo;

#ifdef WINDOWS
char slash;
size_t len;

len = strlen(fileName);
slash = *(fileName + len - 1);
if((slash == '\\' || slash == '/') && (!(len >= 2 && *(fileName + len - 2) == ':')))
    *(fileName + len - 1) = 0;
#endif

#ifdef USE_WIN_UTF16PATH
if(stat_utf16(fileName, &fileInfo) != 0)
#else
if(stat(fileName, &fileInfo) != 0)
#endif
    {
#ifdef WINDOWS
    *(fileName + len - 1) = slash;
#endif
    return(0);
    }

#ifdef WINDOWS
*(fileName + len - 1) = slash;
#endif

if(S_ISDIR(fileInfo.st_mode))
    return(1);
return(0);
}


CELL * p_open(CELL * params)
{
char * fileName;
char * accessMode;
char * option = NULL;
int handle;
IO_SESSION * session;

params = getString(params, &fileName);
params = getString(params, &accessMode);

if(params != nilCell)
    getString(params, &option);
    
if( (handle = openFile(fileName, accessMode, option)) == (int)-1)
    return(nilCell);

session = createIOsession(handle, AF_UNSPEC);
if(*accessMode == 'r')
    session->stream = fdopen(handle, "r");
else if(*accessMode == 'w')
    session->stream = fdopen(handle, "w");
else if(*accessMode == 'u')
    session->stream = fdopen(handle, "r+");
else if(*accessMode == 'a')
    session->stream = fdopen(handle, "a+");

return(stuffInteger((UINT)handle));
}

CELL * p_close(CELL * params)
{
UINT handle;

getInteger(params, &handle);
if(handle == 0) return(nilCell);
if(handle == printDevice) printDevice = 0;
if(deleteIOsession(handle)) return(trueCell);
return(nilCell);
}


CELL * p_readChar(CELL * params)
{
UINT handle;
unsigned char chr;

if(params != nilCell)
    getInteger(params, &handle);
else 
    handle = printDevice;

#ifdef WINDOWS
/* make it work as on Unix */
if(printDevice == 1 || printDevice == 2) handle = 0;
#endif

if(read((int)handle, &chr, 1) <= 0) return(nilCell);

return(stuffInteger((UINT)chr));
}


CELL * p_readBuffer(CELL * params)
{
UINT handle;
size_t size, length;
ssize_t bytesRead = 0;
char * waitFor;
STREAM stream = {NULL, NULL, 0, 0, 0};
CELL * strCell;
SYMBOL * readSptr;
int found = 0;
char chr;

params = getInteger(params, &handle);
params = getEvalDefault(params, &strCell);
if(!symbolCheck || symbolCheck->contents != (UINT)strCell)
    return(errorProc(ERR_IS_NOT_REFERENCED));
if(isProtected(symbolCheck->flags))
    return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(symbolCheck)));

readSptr = symbolCheck;
params = getInteger(params, (UINT *)&size);

if(params == nilCell)
    {
    openStrStream(&stream, size, 0);
    found = 1;
    if((bytesRead = read(handle, stream.buffer, size)) == -1)
        {
        closeStrStream(&stream); 
        return(nilCell);
        }
    }
else
    {
    getString(params, &waitFor);
    openStrStream(&stream, MAX_LINE, 0);
    length = strlen(waitFor);
    while(bytesRead < size)
        {
        if(read(handle, &chr, 1) <= 0)
            break;

        writeStreamChar(&stream, chr); 
        if(++bytesRead < length) continue;
        if(strcmp(waitFor,  stream.ptr - length) == 0)
            {
            found = 1;
            break;
            }       
        }           
    }

deleteList(strCell);

if(bytesRead == 0) 
    { 
    readSptr->contents = (UINT)copyCell(nilCell);
    closeStrStream(&stream); 
    return(nilCell);
    } 

if(stream.size > bytesRead)
    stream.buffer = reallocMemory(stream.buffer, bytesRead + 1);
readSptr->contents = (UINT)makeStringCell(stream.buffer, bytesRead);

if(found) return(stuffInteger(bytesRead));
return(nilCell);
}


CELL * p_readFile(CELL * params)
{
char * fileName;
char * buffer = NULL;
ssize_t size;
#ifndef EMSCRIPTEN
CELL * result;
#endif

params = getString(params, &fileName);
#ifndef EMSCRIPTEN
if(my_strnicmp(fileName, "http://", 7) == 0)
    {
    result = getPutPostDeleteUrl(fileName, params, HTTP_GET, CONNECT_TIMEOUT);
    return((my_strnicmp((char *)result->contents, (char *)"ERR:", 4) == 0) && netErrorIdx ? nilCell : result);
    }
#endif
if((size = readFile(fileName, &buffer)) == -1)
    return(nilCell);

return(makeStringCell(buffer, size));
}

/* allocates a buffer and reads a file into it */
ssize_t readFile(char * fileName, char * * buffer)
{
int handle; 
off_t size;
struct stat fileInfo;

fileName = getLocalPath(fileName);

#ifdef USE_WIN_UTF16PATH
if(stat_utf16(fileName, &fileInfo) != 0)
#else
if(stat(fileName, &fileInfo) != 0)
#endif
    return(-1);

size = fileInfo.st_size;

if( (handle = openFile(fileName, "r", NULL)) == (int)-1)
    return(-1);

*buffer = callocMemory(size+1);

if(read(handle, *buffer, size) == -1)
    {
    freeMemory(*buffer);
        close(handle);
    *buffer = NULL;
    return(-1);
    }

close(handle);

return(size);
}



CELL * p_writeChar(CELL * params)
{
UINT handle;
UINT data;
size_t count;
unsigned char chr;

params = getInteger(params, &handle);
count = 0;

while(params != nilCell)
    {
    params = getInteger(params, &data);
    chr = (unsigned char)data;
    if(write((int)handle, (void *)&chr, 1) == -1)
        return(nilCell);
    ++count;
    }
    
return(stuffInteger(count));
}


size_t appendCellString(CELL * cell, char * buffer, size_t size)
{
cell->contents = (UINT)reallocMemory((char *)cell->contents, cell->aux + size);
memcpy((char *)cell->contents + cell->aux - 1, buffer, size);
cell->aux += size;

*((char *)cell->contents + cell->aux - 1) = 0; 

return(size);
}


CELL * p_appendFile(CELL * params)
{
return(appendWriteFile(params, "a"));
}

CELL * p_writeFile(CELL * params)
{
return(appendWriteFile(params, "w"));
}

int writeFile(char * fileName, char * buffer, size_t size, char * type)
{
int handle;

if( (handle = openFile(fileName, type, NULL)) == (int)-1)
    return(-1);

if(write(handle, buffer, size) == (int)-1)
    return(-1);

close(handle);
return(0);
}

CELL * appendWriteFile(CELL * params, char * type)
{
char * fileName;
char * buffer;
size_t size;
#ifndef EMSCRIPTEN
CELL * result;
#endif

params = getString(params, &fileName);

#ifndef EMSCRIPTEN
if(my_strnicmp(fileName, "http://", 7) == 0)
    {
    result = getPutPostDeleteUrl(fileName, params, 
                (*type == 'w') ? HTTP_PUT : HTTP_PUT_APPEND, CONNECT_TIMEOUT);
    return((my_strnicmp((char *)result->contents, (char *)"ERR:", 4) == 0) && netErrorIdx ? nilCell : result);
    }
#endif

getStringSize(params, &buffer, &size, TRUE);

if(writeFile(fileName, buffer, size, type) == (int)-1)
    return(nilCell);

return(stuffInteger(size));
}

CELL * writeBuffer(CELL * params, int lineFeed);

CELL * p_writeBuffer(CELL * params)
{
return(writeBuffer(params, FALSE));
}

CELL * p_writeLine(CELL * params)
{
return(writeBuffer(params, TRUE));
}


CELL * writeBuffer(CELL * params, int lineFeed)
{
CELL * device;
UINT handle;
SYMBOL * symbolRef;
char * buffer;
size_t size, userSize;

if(params == nilCell)
    {
    varPrintf(OUT_DEVICE, "%s", readLineStream.buffer);
    if(lineFeed) varPrintf(OUT_DEVICE, LINE_FEED);
    size = readLineStream.ptr - readLineStream.buffer;
    goto RETURN_WRITE_BUFFER;
    }

params = getEvalDefault(params, &device);
symbolRef = symbolCheck;

if(params == nilCell)
    {
    buffer = readLineStream.buffer;
    size = readLineStream.ptr - readLineStream.buffer;
    }
else
    params = getStringSize(params, &buffer, &size, TRUE);

if(!lineFeed)
    {
    if(params != nilCell)
        {
        getInteger(params, (UINT *)&userSize);
        size = (userSize > size) ? size : userSize;
        }
    }

if(isNumber(device->type))
    {
    getIntegerExt(device, &handle, FALSE);
    if(write((int)handle, buffer, size) == -1) return(nilCell);
    if(lineFeed)
        if(write((int)handle, LINE_FEED, LINE_FEED_LEN) == -1) return(nilCell);
    }

else if(device->type == CELL_STRING)
    {
    if(symbolRef && isProtected(symbolRef->flags))
        return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(symbolRef)));

    appendCellString(device, buffer, size);
    if(lineFeed)
        appendCellString(device, LINE_FEED, LINE_FEED_LEN);
    }
else
    return(errorProcExt(ERR_INVALID_PARAMETER, device));


RETURN_WRITE_BUFFER:
return(stuffInteger(size + (lineFeed ? LINE_FEED_LEN : 0)));
}


CELL * p_seek(CELL * params)
{
UINT handle;
FILE * fstream;
#ifdef LFS
INT64 paramPosition;
off_t newPosition;
#else
off_t paramPosition;
off_t newPosition;
#endif

params = getInteger(params, &handle);

if(params == nilCell)
    {
    if(handle == 0)
        newPosition = ftell(stdout);
    else if((fstream = getIOstream(handle)) != NULL)
        newPosition = ftell(fstream);
    else if( (newPosition = lseek(handle, 0, SEEK_CUR)) == -1)
        return(nilCell);
    }
else
    {
#ifdef LFS
    getInteger64Ext(params, &paramPosition, TRUE);
#else
    getInteger(params, (UINT *)&paramPosition);
#endif

    newPosition = paramPosition;
    if(newPosition == -1)
        {
        if( (newPosition = lseek((int)handle, 0, SEEK_END)) == -1)
            return(nilCell);
        }
    else
        {
        if( lseek((int)handle, newPosition, SEEK_SET) == -1)
            return(nilCell);
        }
    }

paramPosition = newPosition;
#ifdef LFS
return(stuffInteger64(paramPosition));
#else
return(stuffInteger(paramPosition));
#endif
}

char * readStreamLine(STREAM * stream, FILE * inStream)
{
#ifdef OLD_READ_STREAM /* pre 10.5.8 */
int chr;
#else
char buff[MAX_STRING];
size_t l;
#endif

openStrStream(stream, MAX_STRING, 1);

#ifdef TRU64
do {
errno = 0;
#endif
#ifdef TRUE64 /* pre 10.5.8 also all other OS */
while((chr = fgetc(inStream)) != EOF)
    {
    if(chr == '\n') break;
    if(chr == '\r')
        {
        chr = fgetc(inStream);
        if(chr == '\n' || chr == EOF) break;
        }
    writeStreamChar(stream, chr);
    }
#else
while(fgets(buff, MAX_STRING, inStream) != NULL)
    {
    l=strlen(buff);
    if(buff[l-1] == 0x0A)
        {
        buff[--l] = 0;
        if(buff[l-1] == 0x0D)
            buff[--l] = 0;
        writeStreamStr(stream, buff, l);
        break;
        }
    writeStreamStr(stream, buff, l);
    }
#endif /* pre 10.5.8 also all other OS */
#ifdef TRU64
} while (errno == EINTR);
#endif

#ifdef TRU64 /* and pre 10.5.8 on all other OS */
if(chr == EOF && stream->position == 0) return(NULL);
#else
if(feof(inStream)) 
    {
    clearerr(inStream);
    if(stream->position == 0) return(NULL);
    }
#endif
return(stream->buffer);
}

CELL * p_readLine(CELL * params)
{
UINT handle;
unsigned char chr;
char * line;
int bytesRead;
FILE * fstream;


if(params != nilCell)
    getInteger(params, &handle);
else 
    handle = printDevice;

#ifdef WINDOWSXXX
/* make it work as on Unix */
if(printDevice == 1 || printDevice == 2) handle = 0;
#endif

/* check if stream input can be done */
fstream = (handle == 0) ? IOchannel : getIOstream(handle);
#ifdef LIBRARY
if(!newlispLibConsoleFlag && fstream == stdin)
    return(nilCell);
#endif
if(fstream != NULL)
    {
    if((line = readStreamLine(&readLineStream, fstream)) == NULL)
        return(nilCell);
    return(stuffString(line));
    }

/* do raw handle input, only happens when using read-line on 
   sockets on UNIX and pipes on Windows  */
openStrStream(&readLineStream, MAX_STRING, 1);
while(TRUE)
    {
    if((bytesRead = read((int)handle, &chr, 1)) <= 0) break;
    if(chr == '\n') break;
    if(chr == '\r')
        {
        if(read((int)handle, &chr, 1) < 0) break;
        if(chr == '\n') break;
        }
    writeStreamChar(&readLineStream, chr);
    }

if(bytesRead <= 0 && readLineStream.position == 0)
    return(nilCell);

return(stuffStringN(readLineStream.buffer, readLineStream.position));;
}


CELL * p_currentLine(CELL * params)
{
return(stuffString(readLineStream.buffer));
}


char * getLocalPath(char * fileName)
{
if(my_strnicmp(fileName, "file://", 7) == 0)
    fileName = fileName + 7;

#ifdef WINDOWS
if(*fileName == '/' && *(fileName + 2) == ':')
    fileName = fileName + 1;
#endif

return(fileName);
}


int openFile(char * fileName, char * accessMode, char * option)
{
int blocking = 0;
#ifndef WINDOWS
int handle;
#endif

fileName = getLocalPath(fileName);

#ifndef WINDOWS
if(option != NULL && *option == 'n')
    blocking = O_NONBLOCK;
#endif

if(*accessMode == 'r')
    return(open(fileName, O_RDONLY | O_BINARY | blocking, 0));

else if(*accessMode == 'w')
#ifdef WINDOWS
    return(open( fileName, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE) );
#else
    return(open(fileName,O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | blocking, 
        S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH)); /* rw-rw-rw */
#endif

else if(*accessMode == 'u')
    return(open(fileName, O_RDWR | O_BINARY, 0));

else if(*accessMode == 'a')
       {
#ifdef WINDOWS
       return(open(fileName, O_RDWR | O_APPEND | O_BINARY | O_CREAT, S_IREAD | S_IWRITE));
#else
       handle = open(fileName, O_RDWR | O_APPEND | O_BINARY | O_CREAT,
          S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH); /* rw-rw-rw */
#ifdef EMSCRIPTEN
       /* oppen append is broken on Emscripten, open for update but filepointer
          stays at the beginning and old contents is overwritten */
       if(lseek(handle, 0, SEEK_END) != -1)
                return(handle);
#else
       return(handle);
#endif

#endif
       }

return(-1);
}

/* ------------------------- directory management ------------------------- */

CELL * p_copyFile(CELL * params)
{
char * fromName;
char * toName;
int fromHandle, toHandle;
unsigned char * copyBuffer;
UINT bytesRead;

params = getString(params, &fromName);
getString(params, &toName);

if((fromHandle = openFile(fromName, "read", NULL)) < 0)
    return(nilCell);

if((toHandle = openFile(toName,"write", NULL)) < 0)
    return(nilCell);

copyBuffer = allocMemory(MAX_FILE_BUFFER);
do
    {
    bytesRead = read(fromHandle, copyBuffer, MAX_FILE_BUFFER);
    if(write(toHandle, copyBuffer, (int)bytesRead) < 0) 
        fatalError(ERR_IO_ERROR, 0, 0);
    } while (bytesRead == MAX_FILE_BUFFER);

free(copyBuffer);

close(fromHandle);
close(toHandle);

return(trueCell);
}


CELL * p_renameFile(CELL * params)
{
char *oldName;
char *newName;

params = getString(params, &oldName);
getString(params, &newName);
return(rename(oldName, newName) == 0 ? trueCell : nilCell);
}


CELL * p_deleteFile(CELL * params)
{
char * fileName;
#ifndef EMSCRIPTEN
CELL * result;
#endif

params = getString(params, &fileName);
#ifndef EMSCRIPTEN
if(my_strnicmp(fileName, "http://", 7) == 0)
    {
    result = getPutPostDeleteUrl(fileName, params, HTTP_DELETE, CONNECT_TIMEOUT);
    return((my_strnicmp((char *)result->contents, (char *)"ERR:", 4) == 0) && netErrorIdx ? nilCell : result);
    }
#endif

fileName = getLocalPath(fileName);
return(unlink(fileName) == 0 ? trueCell : nilCell);
}


CELL * p_makeDir(CELL * params)
{
char * dirString;
UINT mode = 0777; /* drwxrwxrwx  gets user masked to drwxr-xr-x on most UNIX */

/* consume param regardless of OS */
params = getString(params, &dirString);

if(params != nilCell)
    {
    getInteger(params, &mode);
    mode = mode > 0xfff ? 0xfff : mode;
    }

#ifdef WINDOWS
return(mkdir(dirString) == 0 ? trueCell : nilCell);
#else
return(mkdir(dirString, (mode_t)mode) == 0 ? trueCell : nilCell);
#endif
}


CELL * p_removeDir(CELL * params)
{
char * dirString;

getString(params, &dirString);
return(rmdir(dirString) == 0 ? trueCell : nilCell);
}


CELL * p_changeDir(CELL * params)
{
char * newDir;

getString(params, &newDir);
return(chdir(newDir) == 0 ? trueCell : nilCell);
}

CELL * p_directory(CELL * params)
{
CELL * dirList;
char * dirPath;
char * fileName;
char * pattern = NULL;
INT options = 0;
DIR * dir;
struct dirent * dEnt;

if(params != nilCell)
    {
    params = getString(params, &dirPath);
    if(params != nilCell)
        {
        params = getString(params, &pattern);
        if(params != nilCell)
            /* 10.6.1 also accept string for options */
            parseRegexOptions(params, (UINT *)&options, TRUE);
        }
    }
else dirPath = ".";

dirList = getCell(CELL_EXPRESSION);

dir = opendir(dirPath);
if(dir == NULL) return(nilCell);

while((dEnt = readdir(dir)) != NULL)
    {
#ifdef USE_WIN_UTF16PATH
    fileName = utf16_to_utf8(dEnt->d_name);
#else
    fileName = dEnt->d_name;
#endif
    if(!pattern || searchBufferRegex(fileName, 0, pattern, strlen(fileName), options, NULL) != -1)
        addList(dirList, stuffString(fileName));
#ifdef USE_WIN_UTF16PATH
    free(fileName);
#endif
    }

closedir(dir);
return(dirList);
}


#define DOT_PATH ".\000"


CELL * p_realpath(CELL * params)
{
char  path[PATH_MAX];
char * name;

if(params != nilCell)
    {
    params = getString(params, &name);
    if(getFlag(params))
        {
        if((name = which(name, alloca(PATH_MAX))) == NULL)
            return(nilCell);
        return(stuffString(name));
        }
    }
else name = DOT_PATH;

if(realpath(name, path) == NULL)
    return(nilCell);

#ifdef _BSD /* behaves like Windows */
if(isFile(path, 0)) return(nilCell);
#endif

return(stuffString(path));
}

CELL * p_fileInfo(CELL * params)
{
char * pathName;
struct stat fileInfo;
CELL * list;
int result = 0;

params = getString(params, &pathName);

#ifdef WINDOWS /* has no link-flag */
#ifdef USE_WIN_UTF16PATH
result = stat_utf16(pathName, &fileInfo);
#else
result = stat(pathName, &fileInfo);
#endif

#else /* Unix */
if(getFlag(params->next))
    result = stat(pathName, &fileInfo);
else
    result = lstat(pathName, &fileInfo);
#endif

if(result != 0)
    return(nilCell);

list = stuffIntegerList(
    8,
    (UINT)fileInfo.st_size,
    (UINT)fileInfo.st_mode,
    (UINT)fileInfo.st_rdev,
    (UINT)fileInfo.st_uid,
    (UINT)fileInfo.st_gid,
    (UINT)fileInfo.st_atime,
    (UINT)fileInfo.st_mtime,
    (UINT)fileInfo.st_ctime
    );

#ifndef NEWLISP64
#ifdef LFS
((CELL *)list->contents)->type = CELL_INT64;
*(INT64 *)&((CELL *)list->contents)->aux = (INT64)fileInfo.st_size;
#endif /* LFS */
#endif /* NEWLISP64 */

if(params != nilCell)
    {
    pushResult(list);
    return(copyCell(implicitIndexList(list, params)));
    }
    
return(list);
}


#ifdef LFS
INT64 fileSize(char * pathName)
#else
size_t fileSize(char * pathName)
#endif
{
struct stat fileInfo;
int result;

#ifdef WINDOWS /* has no link-flag */
#ifdef USE_WIN_UTF16PATH
result = stat_utf16(pathName, &fileInfo);
#else
result = stat(pathName, &fileInfo);
#endif
#else /* Unix */
result = stat(pathName, &fileInfo);
#endif

if(result != 0) return 0;

return(fileInfo.st_size);
}


/* ------------------------- processes and pipes ------------------------- */

#ifndef WINDOWS
CELL * p_system(CELL *params)
{
char * command;
getString(params, &command);
return(stuffInteger((UINT)system(command)));
}
#else
CELL * p_system(CELL *params)
{
UINT creation_flags = 0;
char * command;
STARTUPINFO si;
PROCESS_INFORMATION pi;
UINT result;

memset(&si, 0, sizeof(STARTUPINFO));
memset(&pi, 0, sizeof(PROCESS_INFORMATION));

si.cb = sizeof(STARTUPINFO);

params = getString(params, &command);
if(params != nilCell)
    getInteger(params, &creation_flags);
else
    return(stuffInteger((UINT)system(command)));

result = CreateProcessA(NULL, command, NULL, NULL, 0, (DWORD)creation_flags, NULL, NULL, 
        (LPSTARTUPINFO)&si, (LPPROCESS_INFORMATION)&pi); 


if(!result) return(nilCell);

WaitForSingleObject(pi.hProcess, -1);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread); 

return(stuffInteger(result));
}
#endif


CELL * p_exec(CELL * params)
{
CELL * lineList;
char * line;
char * command, * data;
FILE * handle;
size_t size;

params = getString(params, &command);
if(params == nilCell)
    {
    if((handle = popen(command , "r")) == NULL)
        return(nilCell);

    lineList = getCell(CELL_EXPRESSION);
    while((line = readStreamLine(&readLineStream, handle)) != NULL)
        addList(lineList, stuffString(line));

    pclose(handle);
    return(lineList);
    }
    
getStringSize(params, &data, &size, TRUE);

if((handle = popen(command, "w")) == NULL)
    return(nilCell);

if(fwrite(data, 1, (size_t)size, handle) < size)
    return(nilCell);

pclose(handle);
return(trueCell);
}


/* parses/splits a string intor substrings separated
   by spaces, strings containing spaces can be enclosed
   in either a pair of single or double quotes
*/
int init_argv(char * ptr, char *argv[])
{
int argc = 0;
char brkChr;

while(*ptr != 0)
    {
    while(*ptr == ' ') ++ptr;
    if(*ptr == 0) break;
    if(*ptr == '\'' || *ptr == '"')
        {
        brkChr = *ptr;
        argv[argc++] = ++ptr;
        while(*ptr != brkChr && *ptr != 0) ++ptr;
        if(*ptr == 0) break;
        *ptr++ = 0; 
        continue;
        }
    else
        {
        argv[argc++] = ptr++;
        while(*ptr != ' ' && *ptr != 0) ptr++;
        if(*ptr == 0) break;
        *ptr++ = 0;
        }
    }

argv[argc] = 0;
return(argc);
}


#ifndef EMSCRIPTEN
#ifdef WINDOWS
int kill(pid_t pid, int sig);
int winPipe(UINT * inpipe, UINT * outpipe);
UINT winPipedProcess(char * command, int inpipe, int outpipe, int option);
UINT plainProcess(char * command, size_t size);

CELL * p_pipe(CELL * params)
{
UINT hin, hout;
IO_SESSION * session;

if(!winPipe(&hin, &hout))    /* see file win-util.c */
    return(nilCell);

session = createIOsession(hin, AF_UNSPEC);
session->stream = fdopen(hin, "r");
session = createIOsession(hout, AF_UNSPEC);
session->stream = fdopen(hout, "w");

return(stuffIntegerList(2, hin, hout));
}


CELL * p_process(CELL * params)
{
char * command;
int result;
size_t size;

UINT inpipe = 0, outpipe = 0, option = 1;

params = getStringSize(params, &command, &size, TRUE);
if(params != nilCell)
    {
    params = getInteger(params, (UINT *)&inpipe);
    params = getInteger(params, (UINT *)&outpipe);
    if(params != nilCell)
        getInteger(params, (UINT *)&option);
	result = winPipedProcess(command, (int)inpipe, (int)outpipe, (int)option);
    }
else result = plainProcess(command, size);

if(!result) return(nilCell);

return(stuffInteger(result));
}


#else /* not WINDOWS */

CELL * p_pipe(CELL * params)
{
int handles[2];
#ifndef SUNOS
IO_SESSION * session;
#endif

if(pipe(handles) != 0)
    return(nilCell);

#ifndef SUNOS
session = createIOsession(handles[0], AF_UNSPEC);
session->stream = fdopen(handles[0], "r");
session = createIOsession(handles[1], AF_UNSPEC);
session->stream = fdopen(handles[0], "w");
#endif

return(stuffIntegerList(2, (UINT)handles[0], (UINT)handles[1]));
}


CELL * p_process(CELL * params)
{
char * command;
char * cmd;
int forkResult;
UINT inpipe = 0, outpipe = 0, errpipe = 0;
char * argv[16];
size_t  size;

params = getStringSize(params, &command, &size, TRUE);
cmd = callocMemory(size + 1);
memcpy(cmd, command, size + 1);

#ifdef DEBUG_INIT_ARGV
    int i;
    init_argv(cmd, argv);
    for(i = 0; i < 15; i++)
        {
        if(argv[i] == NULL) break;
        printf("->%s<-\n", argv[i]);
        }
    return(trueCell);
#endif

if(params != nilCell)
    {
    params = getInteger(params, (UINT *)&inpipe);
    params = getInteger(params, (UINT *)&outpipe);
    if(params != nilCell)
        getInteger(params, (UINT *)&errpipe);
    }

if((forkResult = fork()) == -1)
    return(nilCell);
if(forkResult == 0)
    {
    /* redirect stdin and stdout, stderr to pipe handles */
    if(inpipe)
        {
        close(STDIN_FILENO); 
        if(dup2((int)inpipe, STDIN_FILENO) == -1) exit(0);
        close((int)inpipe);
        }
    if(outpipe)
        {
        close(STDOUT_FILENO); 
        if(dup2((int)outpipe, STDOUT_FILENO) == -1) exit(0);
        if(!errpipe)
            if(dup2((int)outpipe, STDERR_FILENO) == -1) exit(0);
        close((int)outpipe);
        }
    if(errpipe)
        {
        close(STDERR_FILENO);
        if(dup2((int)errpipe, STDERR_FILENO) == -1) exit(0);
        close((int)errpipe);
        }
    
    init_argv(cmd, argv);

    execve(argv[0], argv, environ);
    exit(0);
    }

freeMemory(cmd);

return(stuffInteger(forkResult));
}

#ifndef NO_FORK
CELL * p_fork(CELL * params)
{
int forkResult;
int ppid = getpid();

if((forkResult = fork()) == -1)
    return(nilCell);
if(forkResult == 0)
    {
    parentPid = ppid;
    evaluateExpression(params);
    exit(0);
    }

return(stuffInteger(forkResult));
}
#endif

/* ------------------------------------------------------------------------- */


/* Cilk like interface for spawning and syncronizing child processes
   spawn - start child
   sync  - syncronize results
   abort - abort child

   message - share data with chold and parent
*/

/* run with or without semaphores */

void * parentPad = NULL;    /* written by parent for this process */
void * thisPad = NULL;      /* written by this process for the parent */
int thisSocket = 0;
fd_set myFdSet;             /* set of all child sockets */


#ifndef NO_SPAWN

typedef struct 
    {
    void * result_addr; /* written by child */
    SYMBOL * symbolPtr; /* smbol for result */
    int pid;            /* childs pid */
    int socket; 
    void * next;    
    } SPAWN_LIST;

SPAWN_LIST * mySpawnList = NULL;

void addSpawnedChild(void * addr, SYMBOL * sPtr, int pid, int socket)
{
SPAWN_LIST * spawnList;

spawnList = (SPAWN_LIST  *)allocMemory(sizeof(SPAWN_LIST));

spawnList->result_addr = addr;
spawnList->symbolPtr = sPtr;
spawnList->pid = pid;
spawnList->socket = socket;
spawnList->next = NULL;

if(mySpawnList == NULL)
    mySpawnList = spawnList;
else/* insert in front */
    {
    spawnList->next = mySpawnList;
    mySpawnList = spawnList;
    }
}


SPAWN_LIST * getSpawnedChild(int pid)
{
SPAWN_LIST * spawnList = mySpawnList;

while(spawnList != NULL)
    {
    if(spawnList->pid == pid) break;
    spawnList = spawnList->next;
    }

return(spawnList);
}

void purgeSpawnList(int sockFlag)
{
SPAWN_LIST * spawnList;

/* pop and delete entries */

while(mySpawnList != NULL)
    {
    if(sockFlag)
        close(mySpawnList->socket);
    spawnList = mySpawnList->next;
    free(mySpawnList);
    mySpawnList = spawnList;
    }
}

/* lookup pid get result from shared memory and delete entry */

#define PROCESS_SPAWN_RESULT 0
#define PROCESS_SPAWN_ABORT 1
#define PROCESS_SPAWN_ABNORMAL_END 2
#define ABEND "ERR: abnormal process end"

void processSpawnList(int pid, int mode, int result)
{
SPAWN_LIST * pidSpawn;
SPAWN_LIST * previousSpawn;
CELL * cell;
SYMBOL * sPtr;
char str[32];

pidSpawn = previousSpawn = mySpawnList;

while(pidSpawn)
    {
    if(pidSpawn->pid == pid)
        {
        if(pidSpawn == mySpawnList)
            mySpawnList = pidSpawn->next;
        else
            previousSpawn->next = pidSpawn->next;

        if(mode == PROCESS_SPAWN_RESULT)
            {
            cell = readWriteShared(pidSpawn->result_addr, nilCell, 0);
            sPtr = pidSpawn->symbolPtr;
            deleteList((CELL *)sPtr->contents);
            sPtr->contents = (UINT)cell;
            }
        else if(mode == PROCESS_SPAWN_ABORT)
            {
            FD_CLR(pidSpawn->socket, &myFdSet);
            kill(pidSpawn->pid, 9);
            waitpid(pidSpawn->pid, (int *)0, 0);
            }
        else /* PROCESS_SPAWN_ABNORMAL_END */
            {
            sPtr = pidSpawn->symbolPtr;
            deleteList((CELL *)sPtr->contents);
            snprintf(str, 32, "%s %d", ABEND, result);
            sPtr->contents = (UINT)stuffString(str);
            }
           
	/* close parent socket */
        if(pidSpawn->socket) close(pidSpawn->socket); 
        checkDeleteShareFile(pidSpawn->result_addr);
        /* unmap shared result memory */
        munmap(pidSpawn->result_addr, pagesize);
        free((char *)pidSpawn);
        break;
        }
    previousSpawn = pidSpawn;
    pidSpawn = pidSpawn->next;
    }
}

/* spawn (fork) a process and assign result to the symbol given
     (spawn <quoted-symbol> <epxression>) => pid
   creates a memory share and passes it to the spawned process
   when the spawned child finishes, it copies the result
   to the memory share. If the result does not fit in the pagesize
   store the result in a file with a unique filename which is
   stored in the memory share. The first int32 word is -1 for
   memshare store or 0 for file store.
   For house keeping purpose SPAWN_LIST is maintained to find
   the memshare adddress from the child pid.
*/
CELL * p_spawn(CELL * params)
{
int forkPid;
int pid;
void * address; /* share memory area for result */
SYMBOL * symPtr;
int sockets[2] = {0, 0};

if((address = mmap( 0, pagesize, 
    PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0)) == (void*)-1)
        return(nilCell);

memset(address, 0, sizeof(INT));

params = getSymbol(params, &symPtr);
if(isProtected(symPtr->flags))
    return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(symPtr)));
deleteList((CELL *)symPtr->contents);
symPtr->contents = (UINT)nilCell;

pid = getpid();

/* socketpair for send/receive API is optional */
if(getFlag(params->next))
    {
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) 
        {
        munmap(address, pagesize);
        return(errorProc(ERR_CANNOT_OPEN_SOCKETPAIR)); 
        }

    /* add the parent socket to myFdSet */ 
    if(mySpawnList == NULL)
        FD_ZERO(&myFdSet);
    FD_SET(sockets[0], &myFdSet);
    }

/* make signals processable by waitpid() in p_sync() */
signal(SIGCHLD, SIG_DFL);

if((forkPid = fork()) == -1)
    {
    if(sockets[0]) close(sockets[0]);
    if(sockets[1]) close(sockets[1]);
    munmap(address, pagesize);
    return(nilCell);
    }

if(forkPid == 0) /* the child process */
    {
    /* seed random generator for message fail delay */
    srandom(getpid()); 
    /* get parent pid */
    parentPid = pid;
    if(sockets[0]) close(sockets[0]);
    thisSocket = sockets[1];
    /* purge inherited spawnlist */
    purgeSpawnList(FALSE);
    /* evaluate and write result to shared memory */
    readWriteShared(address, params, TRUE);
    /* close child socket */
    if(thisSocket) close(thisSocket);
    exit(0);
    }

if(sockets[1]) close(sockets[1]);
addSpawnedChild(address, symPtr, forkPid, sockets[0]);

return(stuffInteger(forkPid));
}

/* wait for spawned processes to finish for the timeout specified:
     (sync <timeout-milli-seconds>) => true
   if no timeout is not specified only return a list of pending
   child pids:
     (sync) => list of pids
   For each finished child get the result and assign it to the 
   symbol looked up in SPAWN_LIST.
*/
CELL * p_sync(CELL * params)
{
int result;
int pid;
UINT timeout = 0;
struct timeval tv, tp;
SPAWN_LIST * spawnList;
CELL * resultList = getCell(CELL_EXPRESSION);
int inletFlag = 0;
UINT * resultIdxSave;
CELL * cell;

if(mySpawnList == NULL)
    return(resultList); /* nothing pending */

if(params == nilCell)
    {
    spawnList = mySpawnList;
    while(spawnList != NULL)
        {
        addList(resultList, stuffInteger(spawnList->pid));
        spawnList = spawnList->next;
        }
    return(resultList);
    }

deleteList(resultList);

params = getInteger(params, &timeout);
if(params == nilCell || isNil((CELL *)((SYMBOL *)params->contents)->contents))
    signal(SIGCHLD, SIG_DFL);
else
    inletFlag = TRUE;
    
gettimeofday(&tv, NULL);

while(mySpawnList != NULL)
    {
    gettimeofday(&tp, NULL);
    if(timediff_ms(tp, tv) > timeout) return(nilCell);
    /* wait for any child process to finish */
    pid = waitpid(-1, &result, WNOHANG); 
    if(pid) 
        {
        if(!WIFEXITED(result))
            processSpawnList(pid, PROCESS_SPAWN_ABNORMAL_END, result);
        else
            processSpawnList(pid, PROCESS_SPAWN_RESULT, 0);
        if(inletFlag)
            {
            resultIdxSave = resultStackIdx;
            pushResult(cell = makeCell(CELL_EXPRESSION, (UINT)copyCell(params)));
            ((CELL *)cell->contents)->next = stuffInteger((UINT)pid);
            evaluateExpression(cell);
            cleanupResults(resultIdxSave);
            }
        }
    }

/* put initial behaviour back */
#if defined(SOLARIS) || defined(TRU64) || defined(AIX) 
setupSignalHandler(SIGCHLD, sigchld_handler);
#else
setupSignalHandler(SIGCHLD, signal_handler);
#endif

return(trueCell);
}

/* if abort a specific pid if specified:
     (abort <pid>)
   or abort all:
     (abort)
*/

CELL * p_abort(CELL * params)
{
UINT pid;
 
if(params != nilCell)
    {
    getInteger(params, &pid);
    processSpawnList(pid, PROCESS_SPAWN_ABORT, 0);
    }
else /* abort all */
    {
    while(mySpawnList != NULL) 
        processSpawnList(mySpawnList->pid, PROCESS_SPAWN_ABORT, 0);
    /* put initial behaviour back */
#if defined(SOLARIS) || defined(TRU64) || defined(AIX) 
   setupSignalHandler(SIGCHLD, sigchld_handler);
#else
    setupSignalHandler(SIGCHLD, signal_handler);
#endif
}

return(trueCell);
}

#define SELECT_READ_READY 0
#define SELECT_WRITE_READY 1

CELL * getSelectReadyList(int mode)
{
CELL * pidList = getCell(CELL_EXPRESSION);
SPAWN_LIST * child;
int ready = 0;
struct timeval tv;
fd_set thisFdSet;

tv.tv_sec = 0;
tv.tv_usec = 892 + random() / 10000000;

#if defined(SUNOS) || defined(LINUX) || defined(CYGWIN) || defined(AIX) 
memcpy(&thisFdSet, &myFdSet, sizeof(fd_set));
#else
FD_COPY(&myFdSet, &thisFdSet);
#endif

if(mode == SELECT_READ_READY)
    ready = select(FD_SETSIZE, &thisFdSet, NULL, NULL, &tv);
else /* SELECT_WRITE_READY */
    ready = select(FD_SETSIZE, NULL, &thisFdSet, NULL, &tv);

if(ready == 0) return(pidList);

if(ready < 0) 
    return(pidList);

child = mySpawnList;
while (child != NULL)
    {
    if(FD_ISSET(child->socket, &thisFdSet))
         addList(pidList, stuffInteger(child->pid)); 
    child = child->next;
    }

return(pidList);
}
     

CELL * p_send(CELL * params)
{
UINT pid;
CELL * result = nilCell;
SPAWN_LIST * child = NULL;
int socket;

/* return list of writable child pids */
if(params == nilCell) 
    return(getSelectReadyList(SELECT_WRITE_READY));

params = getInteger(params, &pid);

if(pid == parentPid) /* write to parent */
    {
    socket = thisSocket;
    }
else  /* write to child */
    {
    if((child = getSpawnedChild(pid)) == NULL)
        errorProcExt2(ERR_INVALID_PID, stuffInteger(pid));
    socket = child->socket;
    }

if(!socket)
    errorProc(ERR_NO_SOCKET);

if(params == nilCell)
    errorProc(ERR_MISSING_ARGUMENT);

result = readWriteSocket(socket, params);

return(result);
}


CELL * p_receive(CELL * params)
{
UINT pid;
CELL * cell;
SPAWN_LIST * child = NULL;
SYMBOL * sPtr = NULL;
int socket;

/* return list of readable child pids */
if(params == nilCell) 
    return(getSelectReadyList(SELECT_READ_READY));

params = getInteger(params, &pid);

if(params != nilCell)
    {
    getEvalDefault(params, &cell);
    if(!symbolCheck)
        return(errorProc(ERR_IS_NOT_REFERENCED));
    if(isProtected(symbolCheck->flags))
        return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(symbolCheck)));
    if(symbolCheck->contents != (UINT)cell)
        return(errorProc(ERR_IS_NOT_REFERENCED));
    sPtr = symbolCheck;
    }

/* read from parent */
if(pid == parentPid)
    {
    socket = thisSocket;
    }
else  /* read from child */
    {
    if((child = getSpawnedChild(pid)) == NULL)
        errorProcExt2(ERR_INVALID_PID, stuffInteger(pid));
        socket = child->socket;
    }

if(!socket)
    errorProc(ERR_NO_SOCKET);

cell = readWriteSocket(socket, nilCell);
if(cell == nilCell)
    return(nilCell);

/* if no msg variable is given make message the return value */
if(sPtr == NULL)
    return(cell);

deleteList((CELL *)sPtr->contents);
sPtr->contents = (UINT)cell;
pushResultFlag = FALSE;

return(trueCell);
}


/* evaluate expression in params and write to socket,
   part of a socket pair. Similar to readWriteShare()
   but uses sockets instead od shared memory */
   
CELL * readWriteSocket(int socket, CELL * params)
{
char * buffer;
CELL * cell;
STREAM strStream = {NULL, NULL, 0, 0, 0};
UINT length;
ssize_t size, bytesReceived;
struct timeval tv;
fd_set fdset;
int ready;

tv.tv_sec = 0;
tv.tv_usec = 892 + random()/10000000;

FD_ZERO(&fdset);
FD_SET(socket, &fdset);

if(params != nilCell) /* send message, write */
    {
    /* ready = select(socket, NULL, &fdset, NULL, &tv); */
    ready = select(FD_SETSIZE, NULL, &fdset, NULL, &tv); 
    /* ready = FD_ISSET(socket, &fdset) */
    if(ready == 1)
        {
        cell = evaluateExpression(params);
        if(cell->type == CELL_EXPRESSION)
            size = 128;
        else
            size = 32;
        openStrStream(&strStream, size, 0);
        prettyPrintFlags |= PRETTYPRINT_STRING;
        printCell(cell , TRUE, (UINT)&strStream);
        prettyPrintFlags &= ~PRETTYPRINT_STRING;
        length = strStream.position;
        if(send(socket, &length, sizeof(UINT), 0) ==  sizeof(UINT))
            {
            size = send(socket, strStream.buffer, strStream.position, 0);
            if(size == strStream.position)
                {
                closeStrStream(&strStream);
                return(trueCell);
                }
            /* caller should check errno using (sys-error) */
            closeStrStream(&strStream);
            return(nilCell);
            }
        }
    else
        {
        /* timeout, socket not ready */
        return(nilCell);
        }
    }
/* receive message, read */
/* ready = select(socket, &fdset, NULL, NULL, &tv); */
ready = select(FD_SETSIZE, &fdset, NULL, NULL, &tv);
/* ready = FD_ISSET(socket, &fdset) */
if(ready == 1)
    {
    if((size = recv(socket, &length, sizeof(UINT), 0)) == sizeof(UINT))
        {
        buffer = callocMemory(length + 1);
        bytesReceived = 0;
        while(bytesReceived < length)
            {
            size = recv(socket, buffer + bytesReceived, length - bytesReceived, 0);
            if(size == -1)
                {
                free(buffer);
                return(nilCell);
                }
            bytesReceived += size; 
            }
        cell = sysEvalString(buffer, currentContext, nilCell, READ_EXPR_SYNC);
        free(buffer);
        return(cell);
        }
    }

return(nilCell);
}

#endif /* NO_SPAWN */

/* --------------------------- end Cilk ------------------------------------- */


extern SYMBOL * symHandler[];

CELL * p_waitpid(CELL * params)
{
UINT pid, options;
int result, retval;

symHandler[SIGCHLD - 1] = nilSymbol;
signal(SIGCHLD, SIG_DFL);

params = getInteger(params, (UINT *)&pid);
if(params != nilCell)
    {
    params = evaluateExpression(params);
    if(isNil(params))
        options = WNOHANG;
    else
        getIntegerExt(params, (UINT *)&options, FALSE);
    }
else
    options = 0;

retval = waitpid((int)pid, &result , (int)options);

return(stuffIntegerList(2, (UINT)retval, (UINT)result));
}

#endif

CELL * p_destroyProcess(CELL * params)
{
UINT pid;
UINT sig;

params = getInteger(params, &pid);
if(params != nilCell)   
    getInteger(params, &sig);
else
    sig = 9;

if(kill(pid, sig) != 0)
    return(nilCell);

return(trueCell);
}

/* ------------------------------ semaphores --------------------------------- */
#ifndef NO_SEMAPHORE
#ifdef WINDOWS

UINT winCreateSemaphore(void);
UINT winWaitSemaphore(UINT hSemaphore);
UINT winSignalSemaphore(UINT hSemaphore, int count);
UINT winDeleteSemaphore(UINT hSemaphore);
int getSemaphoreCount(UINT hSemaphore);

CELL * p_semaphore(CELL * params)
{
UINT sem_id;
INT value;

if(params != nilCell)
    {
    params = getInteger(params, &sem_id);
    if(params != nilCell)
        {
        getInteger(params,(UINT *)&value);
        if(value == 0)
            {
            if(!winDeleteSemaphore(sem_id)) 
                return(nilCell);
            return(trueCell);
            }

        /* wait or signal */
        if(value < 0)
            {
            if(winWaitSemaphore(sem_id)) return(trueCell);
            return(nilCell);
            }
        if(value > 0)
            {
            if(winSignalSemaphore(sem_id, value)) return(trueCell);
            return(nilCell);    
            }
        }

    else
        {
        /* return semaphore value, not on Win32 ? */
        return(nilCell);
        }
    }

/* create semaphore */
if((sem_id = winCreateSemaphore()) == 0) return(nilCell);
return(stuffInteger(sem_id));
}
#else /* Mac OS X, Linux/UNIX */

CELL * p_semaphore(CELL * params)
{
INT sem, value, result;

if(params == nilCell)
    {
    result = semaphore(0, 0, SEM_CREATE);
    goto SEMAPHORE_END;
    }

params = getInteger(params, (UINT *)&sem);
if(params == nilCell)
    {
    result = semaphore(sem, 0, SEM_STATUS);
    goto SEMAPHORE_END;
    }

getInteger(params, (UINT *)&value);
    {
    result = semaphore(sem, value, SEM_SIGNAL);
    if(result != -1) return(trueCell);
    }

SEMAPHORE_END:
if(result == -1) return(nilCell);
return(stuffInteger((UINT)result));
}


int semaphore(UINT sem_id, int value, int type)
{
struct sembuf sem_b;
#ifdef SPARC
#ifndef NEWLISP64
int semun_val = 0;
#endif
#endif

#ifdef MAC_OSX
union semun semu;

semu.val = 0;
#endif

if(type != SEM_CREATE)
    {
    if(type == SEM_SIGNAL)
        {
        if(value == 0)
            {
            /* remove semaphore */
#ifdef SPARC
    #ifndef NEWLISP64
            if(semctl(sem_id, 0, IPC_RMID, &semun_val) == -1) /* SPARC 32 */
    #else 
            if(semctl(sem_id, 0, IPC_RMID, 0) == -1) /* SPARC 64 */
    #endif

#else /* not SPARC */
    #ifdef MAC_OSX
            if(semctl(sem_id, 0, IPC_RMID, semu) == -1) /* MAC_OSX */
    #else
            if(semctl(sem_id, 0, IPC_RMID, 0) == -1) /* LINUX, BSD, TRU64 */
    #endif /* not MAC_OSX */
#endif /* not SPARC */
                return(-1);
            return(0);
            }

        /* wait or signal */
        sem_b.sem_num = 0;
        sem_b.sem_op = value;
        sem_b.sem_flg = 0; 
        if(semop(sem_id, &sem_b, 1) == -1)
            return(-1);
        return(0);
        }

    else
        /* return semaphore value */
#ifdef MAC_OSX
        return(semctl(sem_id, 0, GETVAL, semu));
#else
        return(semctl(sem_id, 0, GETVAL, 0));
#endif
    }

/* create semaphore */
sem_id = semget(IPC_PRIVATE, 1, 0666 );

#ifdef SPARC
  #ifndef NEWLISP64
if(semctl(sem_id, 0, SETVAL, &semun_val) == -1) /* SPARC 32 */
  #else
if(semctl(sem_id, 0, SETVAL, 0) == -1) /* SPARC 64 */
  #endif
#else /* not SPARC */
 #ifdef MAC_OSX
if(semctl(sem_id, 0, SETVAL, semu) == -1) /* MAC_OSX */
 #else
if(semctl(sem_id, 0, SETVAL, 0) == -1) /* LINUX, BSD, TRU64 */
 #endif /* not MAC_OSX */
#endif /* not SPARC */
    return(-1);

return(sem_id);
}

#endif /* MAC OSX, Unix, Linux */
#endif /* NO_SEMAPHORE */


#ifndef NO_SHARE

#ifdef WINDOWS
UINT winSharedMemory(int size);
UINT * winMapView(UINT handle, int size);
#endif

/* since 10.1.0 also can share object > pagesize
   objects are stored in the tmp directory of OS
   as a file starting with nls-
*/

CELL * p_share(CELL * params)
{
void * address;
CELL * cell;
#ifdef WINDOWS
UINT handle;
#endif

/* read write  or release (UNIX) shared memory */
if(params != nilCell) 
    {
    cell = evaluateExpression(params);
#ifndef WINDOWS
    if(isNil(cell)) /* release shared address */
        {
        getInteger(params->next, (UINT *)&address);
        checkDeleteShareFile(address);
        if(munmap(address, pagesize) == -1)
            return(nilCell);
        else
            return(trueCell);
        }
#endif
    getIntegerExt(cell, (UINT *)&address, FALSE);
    params = params->next;
#ifdef WINDOWS
    if((address = winMapView((UINT)address, pagesize)) == NULL)
        return(nilCell);
#endif
    return(readWriteShared(address, params, 0));
    }

/* get shared memory UNIX */
#ifndef WINDOWS
if((address = (UINT*)mmap(
    0, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0)) == (void*)-1)
        return(nilCell);

memset((char *)address, 0, pagesize);
return(stuffInteger((UINT)address));

/* get shared memory WINDOWS */
#else 

if((handle = winSharedMemory(pagesize)) == 0)
    return(nilCell);

if((address = winMapView(handle, pagesize)) == NULL)
    return(nilCell);

memset((char *)address, 0, pagesize);
return(stuffInteger(handle));
#endif
}
#endif /* NO_SHARE  */

/* evaluate the expression in params and the write the result
   to shared memory. If size > pagesize use files tmp files
   for transfer. For atomic datatypes are xlation into message
   optimized for speed (but doesn't bring much in overall
   performance, should perhaps be taken out and only use
   readWriteSharedExpression() */
CELL * readWriteShared(UINT * address, CELL * params, int flag)
{
CELL * cell;
size_t size;
char * str;
int errNo;

/* write to shared memory */
if(params != nilCell) 
    {
    if(flag) /* in spawned process */
        {
        if((cell = evaluateExpressionSafe(params, &errNo)) == NULL)
            cell = stuffString(errorStream.buffer);
        }
    else 
        cell = evaluateExpression(params);

    /* if a previous share mem file is still present, delete it
       when *address == 0 when called from Cilk then file is
       deleted p_message(). Here only used from p_share() */
    if(*address == (CELL_STRING | SHARED_MEM_EVAL_MASK))
        checkDeleteShareFile(address);

    /* write anything not bool, number or string */
    if((cell->type & COMPARE_TYPE_MASK) > (CELL_STRING & COMPARE_TYPE_MASK))
        return(copyCell(readWriteSharedExpression(address, cell)));

    switch(cell->type)
        {
        case CELL_NIL:
            *address = cell->type;
#ifdef WINDOWS
            UnmapViewOfFile(address);
#endif
            return(nilCell);
        case CELL_TRUE:
            *address = cell->type;
#ifdef WINDOWS
            UnmapViewOfFile(address);
#endif
            return(trueCell);
        case CELL_LONG:
            *(address + 1) = sizeof(INT);
            *(address + 2) = cell->contents;
            break;
#ifndef NEWLISP64
        case CELL_INT64:
            *(address + 1) = sizeof(INT64);
            memcpy(address + 2, (void *)&cell->aux, sizeof(INT64));
            break;
        case CELL_FLOAT:
            *(address + 1) = sizeof(double);
            *(address + 2) = cell->aux;
            *(address + 3) = cell->contents;
            break;
#else /* NEWLISP64 */
        case CELL_FLOAT:
            *(address + 1) = sizeof(double);
            *(address + 2) = cell->contents;
            break;
#endif /* NEWLISP64 */
        case CELL_STRING:
            getStringSize(cell, &str, &size, FALSE);
            if(size > (pagesize - 3 * sizeof(INT)))
                return(copyCell(readWriteSharedExpression(address, cell)));

            *(address + 1) = size;
            memcpy((char *)(address + 2), str, size);
            *((char *)address + 2 * sizeof(INT) + size) = 0;
            break;
        default:
            return(errorProcExt(ERR_ILLEGAL_TYPE, cell));
        }

    *address = cell->type;
#ifdef WINDOWS
    UnmapViewOfFile(address);
#endif
    return(copyCell(cell));
    }

/* read shared memory */
switch(*address & RAW_TYPE_MASK)
    {
    case CELL_NIL:
#ifdef WINDOWS 
        UnmapViewOfFile(address);
#endif
        return(nilCell);
    case CELL_TRUE:
#ifdef WINDOWS 
        UnmapViewOfFile(address);
#endif
        return(trueCell);   
    case CELL_LONG:
        cell = stuffInteger(*(address + 2));
        break;
#ifndef NEWLISP64
    case CELL_INT64:
        cell = stuffInteger64(*(INT64 *)(address + 2));
        break;
#endif
    case CELL_FLOAT:
#ifndef NEWLISP64
        cell = getCell(CELL_FLOAT);
        cell->aux = *(address + 2);
        cell->contents = *(address + 3);
#else
        cell = getCell(CELL_FLOAT);
        cell->contents = *(address + 2);
#endif
        break;
    case CELL_STRING:
        if(*address & SHARED_MEM_EVAL_MASK)
            return(readWriteSharedExpression(address, nilCell));
        size = *(address + 1);
        cell = makeStringCell(allocMemory(size + 1), size);
        memcpy((char *)cell->contents, (char*)(address + 2), cell->aux);
        break;
    default:
        return(nilCell);
    }

#ifdef WINDOWS
        UnmapViewOfFile(address);
#endif
return(cell);
}


/* Takes anything and passes as string or file which has to
   be compiled back into expression when reading.
   Returns a new cell object on read, old on write
*/

CELL * readWriteSharedExpression(UINT * address, CELL * params)
{
ssize_t size;
STREAM strStream = {NULL, NULL, 0, 0, 0};
CELL * cell;
char * buffer = NULL;
/* int errNo; */

/* read */
if(params == nilCell)
    {
    size = *(address + 1);
    if(size < (pagesize - 3 * sizeof(INT) ))
        {   
        cell = sysEvalString((char *)(address + 2), 
                currentContext, nilCell, READ_EXPR_SYNC);
        }
    else 
        {
        if((size = readFile((char *)(address + 2), &buffer)) != -1)
            cell = sysEvalString(buffer, currentContext, nilCell, READ_EXPR_SYNC);
        else cell = nilCell;
        }

    if(buffer != NULL) free(buffer); 
    return(cell);
    }

/* write */
cell = params;
openStrStream(&strStream, MAX_STRING, 0);
prettyPrintFlags |= PRETTYPRINT_STRING;
printCell(cell , TRUE, (UINT)&strStream);
prettyPrintFlags &= ~PRETTYPRINT_STRING;

*(address + 1) = strStream.position;

if(strStream.position < pagesize - 3 * sizeof(INT))
    {
    memcpy((char *)(address + 2), strStream.buffer, strStream.position);
    *((char *)address + 2 * sizeof(INT) + strStream.position) = 0;
    }
else
    {
    checkDeleteShareFile(address);
    memset((char *)(address + 2), 0, pagesize - 2 * sizeof(INT));
    strncpy((char *)(address + 2), tempDir, PATH_MAX - 2 * sizeof(INT));
    strncat((char *)(address + 2), "/nls-", 6);
    size = strlen((char *)(address + 2));
    getUUID((char *)(address + 2) + size, 0);
    writeFile((char *)(address + 2), strStream.buffer, strStream.position, "w");
    }
closeStrStream(&strStream);

*address = (CELL_STRING | SHARED_MEM_EVAL_MASK);
return(cell);
}

void checkDeleteShareFile(UINT * address)
{
if(     (*address == (CELL_STRING | SHARED_MEM_EVAL_MASK)) &&
#ifndef WINDOWS
#ifdef ANDROID
        (strncmp((char *)(address + 2), "/data/tmp/nls-", 9) == 0) &&
#else
        (strncmp((char *)(address + 2), "/tmp/nls-", 9) == 0) &&
#endif
        (strlen((char *)(address + 2)) == 45) )
#else
        (strncmp((char *)(address + 2), "/temp/nls-", 10) == 0) &&
        (strlen((char *)(address + 2)) == 46) )
#endif
    unlink((char *)(address + 2)); 
}
#endif /* ifndef EMSCRIPTEN */

extern int ADDR_FAMILY;
CELL * p_systemInfo(CELL * params)
{
CELL * cell;

cell = stuffIntegerList(
    10,
    cellCount,
    MAX_CELL_COUNT,
    symbolCount,
    (UINT)recursionCount,
    (UINT)(envStackIdx - envStack)/sizeof(UINT),
    (UINT)MAX_CPU_STACK,
    (UINT)parentPid,
    (UINT)getpid(),
    (UINT)version,
    (UINT)opsys
    );

if(params != nilCell)   
    {
    pushResult(cell);
    return(copyCell(implicitIndexList(cell, params)));
    }

return(cell);
}


CELL * p_systemError(CELL * params)
{
CELL * cell;
UINT errnum = errno;

if(params != nilCell)
    {
    getInteger(params, &errnum);
    if(errnum == 0) errno = 0;
    }
else 
    if(!errnum) return(nilCell);

cell = makeCell(CELL_EXPRESSION, (UINT)stuffInteger(errnum));
((CELL *)cell->contents)->next = stuffString(strerror(errnum));

/* on some platforms strerror(0) causes errno set to 22 */
if(errnum == 0) errno = 0;

return(cell);
}

/* ------------------------------ time and date functions -------------------- */
CELL * p_date(CELL * params)
{
time_t t;
struct tm * ltm;
char * ct;
char * fmt;
ssize_t offset;
/* time_t tme; 10.6.1. */
UINT tme;
size_t size;

#ifdef SUPPORT_UTF8
#ifdef WCSFTIME
int * ufmt;
int * timeString;
#endif
char * utf8str;
#else
char * timeString;
#endif

if(params == nilCell)
    t = (time_t)currentDateValue();
else
    {
    /* 10.6.1 */
    params = getInteger(params, &tme);
    t = (time_t)tme;
    
    if(params != nilCell)
        {
        params = getInteger(params, (UINT *)&offset);
            t += (int)offset * 60;
        }
        
    if(params != nilCell)
        {
        params = getStringSize(params, &fmt, &size, TRUE);
        ltm = localtime(&t);
#ifdef SUPPORT_UTF8
    /* some Linux do UTF-8 but don't have wcsftime() or it is buggy */
#ifdef WCSFTIME
        size = utf8_wlen(fmt, fmt + size + 1);
        ufmt = alloca(UTF8_MAX_BYTES * (size + 1));
        utf8_wstr(ufmt, fmt, size);

        timeString = alloca(UTF8_MAX_BYTES * 128);
        size = wcsftime((wchar_t *)timeString, 127, (wchar_t *)ufmt, ltm);
        utf8str = alloca(size * UTF8_MAX_BYTES + 1);
        size =  wstr_utf8(utf8str, timeString, size * UTF8_MAX_BYTES);
        return(stuffString(utf8str));
#else
        utf8str = alloca(128);
        strftime(utf8str, 127, fmt, ltm);
        return(stuffString(utf8str));
#endif /* WCSFTIME */

#else
        timeString = alloca(128);
        strftime(timeString, 127, fmt, ltm);
        return(stuffString(timeString));
#endif
        }
    }

ct = ctime(&t);
if(ct == NULL) return(nilCell);

ct[strlen(ct) - 1] = 0;  /* supress linefeed */
return(stuffString(ct));
}

INT64 microSecTime(void)
{
struct timeval tv;
struct tm * ttm;
time_t sec;

gettimeofday(&tv, NULL);
sec = tv.tv_sec;
ttm = localtime(&sec);

return (ttm->tm_hour * 3600000000LL + 
       ttm->tm_min * 60000000LL + ttm->tm_sec * 1000000 + 
       tv.tv_usec);
}


int milliSecTime(void)
{
return(microSecTime()/1000);
}


/* returns a differerence of 2 timeval structs in milliseconds
*/
int timediff_ms(struct timeval out, struct timeval in )
{
    if( (out.tv_usec -= in.tv_usec) < 0 )   {
        out.tv_sec--;
        out.tv_usec += 1000000;
    }
    out.tv_sec -= in.tv_sec;

return(out.tv_sec*1000 + (out.tv_usec/1000));
}


/* returns a differerence of 2 timeval structs in microseconds
*/
UINT64 timediff64_us(struct timeval out, struct timeval in )
{
UINT64 usec;

    if( (out.tv_usec -= in.tv_usec) < 0 )   {
        out.tv_sec--;
        out.tv_usec += 1000000;
    }
    out.tv_sec -= in.tv_sec;

usec = (UINT64)1000000 * out.tv_sec + out.tv_usec;
return(usec);
}

#ifndef WINDOWS
CELL * p_dateParse(CELL * params)
{
struct tm ttm;
char * dateStr;
char * formatStr;
time_t dateValue;

params = getString(params, &dateStr);
getString(params, &formatStr);

memset (&ttm, 0, sizeof (ttm));
ttm.tm_mday = 1;

if(strptime(dateStr, formatStr, &ttm) == NULL)
    return(nilCell);

dateValue = calcDateValue(
        ttm.tm_year + 1900,
        ttm.tm_mon + 1,
        ttm.tm_mday,
        ttm.tm_hour,
        ttm.tm_min,
        ttm.tm_sec);

return(stuffInteger(dateValue));
}
#endif

CELL * p_time(CELL * params)
{
struct timeval start, end;
INT64 N = 1;
UINT * resultIdxSave;
double diff;

gettimeofday(&start, NULL);
if(params->next != nilCell)
    getInteger64Ext(params->next, &N, TRUE);

resultIdxSave = resultStackIdx;
while(N--)  
    {
    evaluateExpression(params);
    cleanupResults(resultIdxSave);
    }

gettimeofday(&end, NULL);

diff = (1.0 * timediff64_us(end, start)) / 1000;
return(stuffFloat(diff));
}


CELL * p_timeOfDay(CELL * params)
{
double microSecs = microSecTime()/1000.0;
return(stuffFloat(microSecs));
}


CELL * p_now(CELL * params)
{
struct timeval tv;
struct tm *ttm;
#ifndef WINDOWS
struct tm *ltm;
#ifndef SUNOS
#ifndef OS2
#ifndef AIX
INT gmtoff;
UINT isdst;
#endif
#endif
#endif
#else /* WINDOWS */
TIME_ZONE_INFORMATION timeZone;
#endif
ssize_t offset = 0;
time_t sec;
CELL * cell;

gettimeofday(&tv, NULL);

if(params != nilCell)
    {
    params = getInteger(params, (UINT*)&offset);
    offset *= 60;
        tv.tv_sec += offset;
    }

#ifndef WINDOWS
ltm = localtime((time_t *)&tv.tv_sec);
#ifndef SUNOS
#ifndef OS2
#ifndef AIX
isdst = ltm->tm_isdst;

#ifdef CYGWIN
gmtoff = _timezone/60;
#else
gmtoff = ltm->tm_gmtoff/60;
#endif

#endif
#endif
#endif
#else /* WINDOWS */
GetTimeZoneInformation(&timeZone);
#endif

sec = tv.tv_sec;
ttm = gmtime(&sec);

cell = stuffIntegerList(
    11,
    (UINT)ttm->tm_year + 1900,
    (UINT)ttm->tm_mon + 1,
    (UINT)ttm->tm_mday,
    (UINT)ttm->tm_hour,
    (UINT)ttm->tm_min,
    (UINT)ttm->tm_sec,
    (UINT)tv.tv_usec,
    (UINT)ttm->tm_yday + 1,
    ((UINT)ttm->tm_wday == 0 ? 7 : (UINT)ttm->tm_wday),

#if defined(MAC_OSX) || defined(LINUX) || defined(_BSD) || defined(CYGWIN)
    gmtoff, isdst
#endif

#if defined(SUNOS)
    timezone/60, daylight
#endif

#if defined(OS2) || defined(TRU64) || defined(AIX)
#ifdef NEWLISP64
    (UINT)0L, (UINT)0L
#else
    (UINT)0, (UINT)0
#endif
#endif

#if defined(WINDOWS)
     (UINT)-timeZone.Bias - (UINT)timeZone.DaylightBias,
     (UINT)timeZone.DaylightBias  
#endif
    );

if(params != nilCell)   
    {
    pushResult(cell);
    return(copyCell(implicitIndexList(cell, params)));
    }

return(cell);
}

CELL * p_dateList(CELL * params)
{
struct tm *ttm;
ssize_t timeValue;
time_t timer;
CELL * cell;

if(params == nilCell)
    timeValue = currentDateValue();
else
    params = getInteger(params, (UINT*)&timeValue);

timer = (time_t)timeValue;
if((ttm = gmtime(&timer)) == NULL)
    return(errorProcExt2(ERR_INVALID_PARAMETER, stuffInteger((UINT)timeValue)));

cell = stuffIntegerList(
    8,
    (UINT)ttm->tm_year + 1900,
    (UINT)ttm->tm_mon + 1,
    (UINT)ttm->tm_mday,
    (UINT)ttm->tm_hour,
    (UINT)ttm->tm_min,
    (UINT)ttm->tm_sec,
    (UINT)ttm->tm_yday + 1,
    ((UINT)ttm->tm_wday == 0 ? 7 : (UINT)ttm->tm_wday)
);

if(params != nilCell)   
    {
    pushResult(cell);
    return(copyCell(implicitIndexList(cell, params)));
    }

return(cell);
}

ssize_t currentDateValue(void)
{
struct timeval tv;

gettimeofday(&tv, NULL);
return(tv.tv_sec);
}

CELL * p_dateValue(CELL * params)
{
ssize_t year, month, day, hour, min, sec;
time_t dateValue;
int evalFlag = TRUE;
CELL * next;

if(params->type == CELL_NIL)
    return(stuffInteger(currentDateValue()));

next = params->next;
params = evaluateExpression(params);
if(params->type == CELL_EXPRESSION)
    {
    params = (CELL *)params->contents;
    next = params->next;
    evalFlag = FALSE;
    }

params = getIntegerExt(params, (UINT *)&year, FALSE);
params = getIntegerExt(next, (UINT *)&month, evalFlag);
params = getIntegerExt(params, (UINT *)&day, evalFlag);

hour = min = sec = 0;
if(params != nilCell)
        {
        params = getIntegerExt(params, (UINT *)&hour, evalFlag);
        params = getIntegerExt(params, (UINT *)&min, evalFlag);
        getIntegerExt(params, (UINT *)&sec, evalFlag);
        }

dateValue = calcDateValue(year, month, day, hour, min, sec);

#ifndef NEWLISP64
return(stuffInteger64((INT64)dateValue));
#else
return(stuffInteger((UINT)dateValue));
#endif
}



/* changed for 10.6.1 where time_t can be 64-bit on 32-bit Windows */
time_t calcDateValue(int year, int month, int day, int hour, int min, int sec)
{
time_t dateValue;
INT64 value;

value = 367 * year - (7 * (year + ((month + 9) / 12)))/4 
            + (275 * month)/9 + day + 1721013;

value = value * 24 * 3600 + hour * 3600 + min * 60 + sec 
            - 413319296; /* correction for 1970-1-1 */

if(sizeof(time_t) == 8)
    {
    if(value & 0x80000000)
        dateValue = value | 0xFFFFFFFF00000000LL;
    else
        dateValue = value & 0x00000000FFFFFFFF; 
    }
else
    dateValue = value;

return(dateValue);
}


#ifdef MAC_OSX
extern int nanosleep();
#endif

void mySleep(int ms)
{
#ifdef NANOSLEEP
struct timespec tm;

tm.tv_sec = ms / 1000;
tm.tv_nsec = (ms - tm.tv_sec * 1000) * 1000000;
nanosleep(&tm, 0);

#else

#ifdef WINDOWS
Sleep(ms);
#else
sleep((ms + 500)/1000);
#endif

#endif
}

#ifdef NANOSLEEP
void myNanoSleep(int nanosec)
{
struct timespec tm;

tm.tv_sec =  nanosec / 1000000000;
tm.tv_nsec = (nanosec - tm.tv_sec * 1000000000);
nanosleep(&tm, 0);
}
#endif


CELL * p_sleep(CELL * params)
{
double milliSecsFloat;
#ifdef NANOSLEEP
int nanoSecsInt;
#endif

getFloat(params, &milliSecsFloat);

mySleep((UINT)milliSecsFloat);
#ifdef NANOSLEEP
nanoSecsInt = (milliSecsFloat - (int)milliSecsFloat) * 1000000;
if(nanoSecsInt) myNanoSleep(nanoSecsInt);
#endif

return(stuffFloat(milliSecsFloat));
}

/* -------------------------------- environment functions ------------------- */


CELL * p_env(CELL * params)
{
char * varName;
char * varValue;

/* no parameters returns whole environment */
if(params == nilCell) 
    return(environment());

/* one parameter get environment for one variable */
params = getString(params, &varName);
if(params == nilCell) 
    {
    if( (varValue = getenv(varName)) == NULL)
        return(nilCell);
    return(stuffString(varValue));
    }

/* two parameters sets environment for one variable */
getString(params, &varValue);
#ifndef MY_SETENV
if(*varValue == 0)
    unsetenv(varName);
else
#endif
    if(setenv(varName, varValue, 1) != 0)
        return(nilCell);

return(trueCell);
}


#ifdef MY_SETENV
int my_setenv(const char * varName, const char * varValue, int flag)
{
char * envstr;
envstr = alloca(strlen(varName) + strlen(varValue) + 2);
strcpy(envstr, varName);
strcat(envstr, "=");
strcat(envstr, varValue);
return(putenv(envstr));
}
#endif


CELL * environment(void)
{
char ** env;
CELL * envList;
CELL * lastEntry;
CELL * pair;
char * ptr;

lastEntry = NULL;
envList = getCell(CELL_EXPRESSION);

env = environ;

while(*env)
    {
    if((ptr = strstr(*env, "=")) != NULL)
        {
        pair = getCell(CELL_EXPRESSION);
        addList(pair, stuffStringN(*env, ptr - *env));
        addList(pair, stuffString(ptr + 1));
        }
    else 
        {
        env++;
        continue;
        }
    
    if(lastEntry == NULL)
        {
        lastEntry = pair;
        envList->contents = (UINT)lastEntry;
        }
    else
        {
        lastEntry->next = pair;
        lastEntry = lastEntry->next;
        }
    env++;
    }

return(envList);
}

/* --------------------- read the keyboard -----------------------------------*/

/* thanks to Peter van Eerten for contributing this function */
CELL * p_readKey(CELL * params)
{
#if defined(WINDOWS) || defined(OS2)
return(stuffInteger(getch()));
#else

struct termios term, oterm;
char c = 0;

tcgetattr(0, &oterm);

memcpy(&term, &oterm, sizeof(term));

/* put the terminal in non-canonical mode, any
reads timeout after 0.1 seconds or when a
single character is read */
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 0;
term.c_cc[VTIME] = 1;
tcsetattr(0, TCSANOW, &term);

#if defined(_BSD) || defined(MAC_OSX)
while(read(0, &c, 1) == 0);
#else
while((c = (char)getchar()) == (char)-1);
#endif

/* reset the terminal to original state */
tcsetattr(0, TCSANOW, &oterm);

return(stuffInteger(c));
#endif
} 

/* --------------------- peek a file descriptor ------------------------------*/

#ifndef WINDOWS
CELL * p_peek(CELL * params)
{
UINT handle;
int result;

getInteger(params, &handle);

if(ioctl((int)handle, FIONREAD, &result) < 0)
    return(nilCell);

return(stuffInteger((UINT)result));
} 
#endif

/* --------------------- library functions not found on some OSs -------------*/

#ifdef MY_VASPRINTF
int my_vasprintf(char * * buffer, const char * format, va_list argptr)
{
int size;

/* get size */
size = vsnprintf(NULL, 0, format, argptr);
if (size < 0) return -1;

*buffer = calloc(size + 1, 1);
if (!*buffer) return(-1);

vsnprintf(*buffer, size + 1, format, argptr);
(*buffer)[size] = '\0';

return(size);
}
#endif


/* ---------------------- Universal Unique ID version 1 and 3 ----------- */

#define UINT16 unsigned short
#define UINT32 unsigned int

typedef struct 
    {
    UINT32          time_low;
    UINT16          time_mid;
    UINT16          time_hi_and_version;
    unsigned char   clock_seq_hi_and_reserved;
    unsigned char   clock_seq_low;
    unsigned char   node[6];
    } UUID;

UINT16 clock_seq = 0;
INT64 last_time = 0;
char last_node[6];

#define OCT151582 0x01B21DD213814000LL

char * getUUID(char * str, char * node) 
{
UUID uuid;
struct timeval tp;
INT64 timestamp;
UINT16 nodeID[3];
int uuid_version;

gettimeofday(&tp, (struct timezone *)0);

/* add UUID UTC offset Oct 15, 1582 */
timestamp = tp.tv_sec * (INT64)10000000 + tp.tv_usec * 10 + OCT151582; 

#ifdef WINDOWS
if(timestamp <= last_time) timestamp = last_time + 1;
#else
if(timestamp < last_time) clock_seq++;
if(timestamp == last_time) timestamp++;
#endif

if(last_time == 0)
    srandom((timestamp & 0xFFFFFFFF) + getpid());

last_time = timestamp;


if(clock_seq == 0) clock_seq = random();
if(node != NULL && (memcmp(last_node, node, 6) != 0))
    {
    clock_seq = random();
    memcpy(last_node, node, 6);
    }

if(node == NULL)
    {
    nodeID[0] = random(); 
    nodeID[1] = random();
    nodeID[2] = random();
    uuid_version = 4;
    memcpy(uuid.node, (void *)nodeID, 6);
    }
else
    {
    uuid_version = 1;
    /* least sign bit of first byte must be 0 on MACs
       and 1 on artifical generated node IDs */
    memcpy(uuid.node, node, 6);
    }

if(uuid_version == 4) 
    {
    clock_seq = random();
    uuid.time_low = random();
#ifdef WINDOWS
    uuid.time_low |= (random() << 16);
#endif
    uuid.time_mid = random();
    uuid.time_hi_and_version = random();
    }
else
    {
    uuid.time_low = (unsigned int)(timestamp & 0xFFFFFFFF);
    uuid.time_mid = (unsigned short)((timestamp >> 32) & 0xFFFF);
    uuid.time_hi_and_version = (unsigned short)(timestamp >> 48) ;
    }

uuid.time_hi_and_version &= 0x0FFF;
uuid.time_hi_and_version |= (uuid_version << 12);
uuid.clock_seq_low = clock_seq & 0xFF;
uuid.clock_seq_hi_and_reserved = (clock_seq & 0x3F00) >> 8;
uuid.clock_seq_hi_and_reserved |= 0x80;

snprintf(str, 37, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", 
    uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
    uuid.clock_seq_hi_and_reserved, uuid.clock_seq_low,
    uuid.node[0], uuid.node[1], uuid.node[2], 
    uuid.node[3], uuid.node[4], uuid.node[5]);

return(str);
}

CELL * p_uuid(CELL * params)
{
char * nodeMAC = NULL;
size_t size;
char str[38];

if(params != nilCell)
    {
    getStringSize(params, &nodeMAC, &size, TRUE);
    if(size < 6) nodeMAC = NULL;
    }
    
return(stuffString(getUUID(str, nodeMAC)));
}


SYMBOL * getSymbolCheckProtected(CELL * params)
{
SYMBOL * sPtr = NULL;
CELL * cell;

if(params->type == CELL_SYMBOL)
    {
    sPtr = (SYMBOL *)params->contents;
    cell = (CELL *)sPtr->contents;
    if(cell->type == CELL_CONTEXT)
        sPtr = translateCreateSymbol( ((SYMBOL*)cell->contents)->name, CELL_NIL,
            (SYMBOL*)cell->contents, TRUE);
    }
else if(params->type == CELL_DYN_SYMBOL)
    sPtr = getDynamicSymbol(params);
else errorProcExt(ERR_SYMBOL_EXPECTED, params);

if(isProtected(sPtr->flags))
    errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(sPtr));

symbolCheck = sPtr;

return sPtr;
}


/* eof */



