static ekRegex * ekRegexCreate(ekContext * E, ekValue * subject, ekValue * pattern, ekValue * options) { ekS32 regexFlags = 0; const char * regexError; ekS32 regexErrorOffset; ekRegex * regex = calloc(1, sizeof(ekRegex)); regex->subject = subject; regex->offset = 0; regex->global = ekFalse; if (options) { const char * c = ekValueSafeStr(options); for (; *c; c++) { switch (*c) { case 'x': regexFlags |= PCRE_EXTENDED; break; case 'i': regexFlags |= PCRE_CASELESS; break; case 's': regexFlags |= PCRE_DOTALL; break; case 'm': regexFlags |= PCRE_MULTILINE; break; case 'g': regex->global = ekTrue; break; } } } regex->regex = pcre_compile(ekValueSafeStr(pattern), regexFlags, ®exError, ®exErrorOffset, NULL); if (!regex->regex) { // Regex compilation errors are fatal for now. I think this is good. ekContextSetError(E, EVE_RUNTIME, "regex compilation error: %s", regexError); ekRegexDestroy(E, regex); regex = NULL; } return regex; }
static ekU32 fileSize(struct ekContext *E, ekU32 argCount) { ekValue *ret = ekValueNullPtr; ekValue *filenameValue; ekFile *file; struct stat s; if(!ekContextGetArgs(E, argCount, "s", &filenameValue)) { return ekContextArgsFailure(E, argCount, "file.size(path)"); } if(stat(ekValueSafeStr(file->filename), &s) != -1) { if(S_ISDIR(s.st_mode)) { return ekContextArgsFailure(E, 0, "file.size() does not work on directories"); } else { ret = ekValueCreateInt(E, s.st_size); } } ekValueRemoveRefNote(E, filenameValue, "fileSize doesnt need filename anymore"); ekContextReturn(E, ret); }
static ekU32 fileWrite(struct ekContext *E, ekU32 argCount) { ekValue *thisValue; ekValue *ret = ekValueNullPtr; ekValue *dataValue = NULL; ekFile *file; if(!ekContextGetArgs(E, argCount, "*Fs", &thisValue, &dataValue)) { return ekContextArgsFailure(E, argCount, "file.write([string] data)"); } file = (ekFile *)thisValue->ptrVal; if((file->state != EFS_WRITE) && (file->state != EFS_APPEND)) { switchState(E, file, EFS_WRITE); } if(file->handle) { const char *data = ekValueSafeStr(dataValue); int len = strlen(data); int bytesWritten = fwrite(data, 1, len, file->handle); if(bytesWritten == len) { ret = ekValueCreateInt(E, 1); } } ekValueRemoveRefNote(E, thisValue, "fileWrite no longer needs thisValue"); ekContextReturn(E, ret); // will be true if we successfully wrote }
static ekU32 fileLs(struct ekContext *E, ekU32 argCount) { ekValue *filenameValue = NULL; ekValue *arrayValue = NULL; if(!ekContextGetArgs(E, argCount, "s", &filenameValue)) { return ekContextArgsFailure(E, argCount, "file.ls(dirpath)"); } arrayValue = ekValueCreateArray(E); #ifdef USE_DIRENT { DIR *dir = opendir(ekValueSafeStr(filenameValue)); if(dir) { struct dirent *entry; while((entry = readdir(dir)) != NULL) { if(strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) { ekValueArrayPush(E, arrayValue, ekValueCreateString(E, entry->d_name)); } } closedir(dir); } } #endif ekValueRemoveRefNote(E, filenameValue, "fileExists doesnt need filename anymore"); ekContextReturn(E, arrayValue); }
static ekValue *absolutePath(struct ekContext *E, ekValue *path) { char temppath[MAX_PATH_LENGTH]; realpath(ekValueSafeStr(path), temppath); ekValueRemoveRefNote(E, path, "absolutePath doesn't need relative path anymore"); return ekValueCreateString(E, temppath); }
static void fileFuncDump(struct ekContext *E, ekDumpParams *params, struct ekValue *p) { ekFile *file = (ekFile *)p->ptrVal; ekStringConcat(E, ¶ms->output, "{ "); ekStringConcat(E, ¶ms->output, "FILE: "); ekStringConcat(E, ¶ms->output, ekValueSafeStr(file->filename)); ekStringConcat(E, ¶ms->output, " }"); }
static ekBool ekRegexMatchNext(ekContext * E, ekRegex * regex, ekValue * matches) { // Why PCRE didn't make this an array of small structs is baffling to me; // it would have led to much more obvious, self-documenting code. ekS32 regexVectors[EUREKA_MAX_REGEX_VECTORS]; ekS32 len = strlen(ekValueSafeStr(regex->subject)); ekS32 err = pcre_exec(regex->regex, 0, ekValueSafeStr(regex->subject), len, regex->offset, 0, regexVectors, EUREKA_MAX_REGEX_VECTORS); if (err > 0) { ekS32 i; ekAssert(matches->type == EVT_ARRAY); for (i = 0; i < err; ++i) { ekS32 index = i * 2; ekValue * match = ekValueDonateString(E, ekSubstrdup(E, ekValueSafeStr(regex->subject), regexVectors[index], regexVectors[index + 1])); ekArrayPush(E, &matches->arrayVal, match); } regex->offset = regexVectors[1]; return ekTrue; } return ekFalse; }
static int isDir(struct ekContext *E, ekValue *filenameValue) { struct stat s; if(stat(ekValueSafeStr(filenameValue), &s) != -1) { if(S_ISDIR(s.st_mode)) { return 1; } } return 0; }
static ekU32 fileOpenInternal(struct ekContext *E, ekValue *fileValue, ekValue *modesValue) { ekValue *ret = ekValueNullPtr; ekFile *file = (ekFile *)fileValue->ptrVal; if(isDir(E, file->filename)) { return ekContextArgsFailure(E, 0, "file.open() does not work on directories"); } { const char *c; const char *modes = "r"; int state = EFS_READ; if(modesValue) { modes = ekValueSafeStr(modesValue); } for(c = modes; *c; ++c) { switch(*c) { case 'r': state = EFS_READ; break; case 'w': state = EFS_WRITE; break; case 'a': state = EFS_APPEND; break; }; } switchState(E, file, state); if(file->handle) { ret = fileValue; } else { ekValueRemoveRefNote(E, fileValue, "fileOpen failed, so forget about fileValue"); } } ekContextReturn(E, ret); }
static ekU32 fileExists(struct ekContext *E, ekU32 argCount) { ekValue *filenameValue = NULL; struct stat s; int exists = 0; if(!ekContextGetArgs(E, argCount, "s", &filenameValue)) { return ekContextArgsFailure(E, argCount, "file.exists(path)"); } if(stat(ekValueSafeStr(filenameValue), &s) != -1) { exists = 1; } ekValueRemoveRefNote(E, filenameValue, "fileExists doesnt need filename anymore"); ekContextReturnInt(E, exists); }
static int switchState(struct ekContext *E, ekFile *file, int newState) { const char *mode = NULL; if(file->state == newState) { return 0; } if(file->permanent) { ekContextSetError(E, EVE_RUNTIME, "attempting to change file state on a permanent file: file.%s", ekValueSafeStr(file->filename)); return 0; // never change the state on a permanent file! } if(file->handle) { fclose(file->handle); file->handle = NULL; } switch(newState) { case EFS_CLOSED: // do nothing, we already closed it break; case EFS_READ: mode = "rb"; break; case EFS_WRITE: mode = "wb"; break; case EFS_APPEND: mode = "ab"; break; } if(mode) { file->handle = fopen(ekValueSafeStr(file->filename), mode); } file->state = newState; return 1; }
struct ekValue *ekValueTypeGetIntrinsic(struct ekContext *E, ekU32 type, struct ekValue *index, ekBool lvalue) { ekMap *map = E->types[type]->intrinsics; if(map && index && (index->type == EVT_STRING)) { ekMapEntry *mapEntry = ekMapGetS(E, map, ekValueSafeStr(index), ekFalse); if(mapEntry && mapEntry->valuePtr) { if(lvalue) { ekContextSetError(E, EVE_RUNTIME, "cannot use intrinsic %s function as an lvalue", ekValueTypeName(E, type)); } else { return ekValueCreateCFunction(E, mapEntry->valuePtr); } } } return NULL; }
static ekS32 ekRegexSub(ekContext * E, ekRegex * regex, ekValue * replace, ekString * output) { const char * subject = ekValueSafeStr(regex->subject); ekS32 len = strlen(subject); ekS32 count = 0; ekS32 regexVectors[EUREKA_MAX_REGEX_VECTORS]; ekS32 err; while ((err = pcre_exec(regex->regex, 0, subject, len, regex->offset, 0, regexVectors, EUREKA_MAX_REGEX_VECTORS)) > 0) { ekStringConcatLen(E, output, &subject[regex->offset], regexVectors[0] - regex->offset); ekStringConcatStr(E, output, &replace->stringVal); regex->offset = regexVectors[1]; ++count; if (!regex->global) break; } if (regex->offset < len) { ekStringConcatLen(E, output, &subject[regex->offset], len - regex->offset); } return count; }