/*
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 .
*/
#include "newlisp.h"
#include
#include "protos.h"
#define AF_UNSPEC 0 /* from socket.h or winsock2.h */
#if defined(SOLARIS) || defined(TRU64) || defined(AIX)
#include
#endif
#ifdef SOLARIS
#define FIONREAD I_NREAD
#endif
#ifndef WINDOWS
#include
#ifndef ANDROID
#ifndef EMSCRIPTEN
#include
#include
#endif
#endif
#include
#include
#endif
int init_argv(char * ptr, char *argv[]);
char * getUUID(char * str, char * node);
#ifdef OS2
#include
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
#include
#include
#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
#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, ¶mPosition, TRUE);
#else
getInteger(params, (UINT *)¶mPosition);
#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 ) => 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 ) => 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 )
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 */