xtool/contrib/mORMot/SyNode/SyNodeBinding_fs.pas

819 lines
24 KiB
ObjectPascal

/// `fs` module support bindings for SyNode
// - this unit is a part of the freeware Synopse framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SyNodeBinding_fs;
interface
{$I Synopse.inc}
{$I SyNode.inc}
uses
SysUtils,
SynCommons,
SpiderMonkey,
SyNode;
function os_realpath(const FileName: TFileName; var TargetName: TFileName): Boolean;
implementation
{$IFNDEF SM_DEBUG}
{$UNDEF SM_DEBUG_FSTRACEFILEREAD}
{$UNDEF SM_DEBUG_FSDUMPFILEEXCERPT}
{$ENDIF}
uses
{$IFDEF MSWINDOWS}
Windows,
{$ELSE}
Unix, BaseUnix, DateUtils,
{$ENDIF}
{$IFDEF FPC}
LazFileUtils,
{$ENDIF}
SyNodeReadWrite,
Classes,
SynLog;
/// decode text file to string using BOM
// if BOM not fount - use current system code page to convert ANSI content to unicode
// if file not found - return empty string
// internaly use AnyTextFileToRawUTF8
// accept one parameter - "path to file"
function fs_loadFile(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean; cdecl;
const
USAGE = 'usage loadFile(pathToFile: String, [forceUFT8: boolean])';
var
in_argv: PjsvalVector;
forceUTF8: boolean;
name: String;
begin
forceUTF8 := true;
result := true;
try
in_argv := vp.argv;
if (argc < 1) or not in_argv[0].isString or ((argc > 1) and not in_argv[1].isBoolean )then
raise ESMException.Create(USAGE);
if argc > 1 then
forceUTF8 := in_argv[1].asBoolean;
name := in_argv[0].asJSString.ToString(cx);
{$ifdef SM_DEBUG_FSTRACEFILEREAD}
SynSMLog.Add.Log(sllDebug, 'fs_loadFile (%) called', name);
{$ENDIF}
if not FileExists(name) then
raise ESMException.Create('file does not exist');
// implementation below dont work if called in the same time from differnt thread
// TFileStream.Create(name, fmOpenRead).Free; // Check that file exists and can be opened;
vp.rval := cx.NewJSString(AnyTextFileToRawUTF8(name, forceUTF8)).ToJSVal;
except
on E: Exception do
begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
/// call RelToAbs
function fs_relToAbs(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean; cdecl;
const
USAGE = 'usage: relToAbs(ABase, ATail: string;)';
var
in_argv: PjsvalVector;
baseDir, fileName, resPath: TFileName;
begin
try
in_argv := vp.argv;
if (argc <> 2) or not in_argv[0].isString or not in_argv[1].isString then
raise ESMException.Create(USAGE);
baseDir := in_argv[0].asJSString.ToString(cx);
fileName := in_argv[1].asJSString.ToString(cx);
resPath := RelToAbs(baseDir, fileName);
vp.rval := cx.NewJSString(StringToSynUnicode(resPath)).ToJSVal;
Result := True;
except
on E: Exception do
begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
/// return object{
// atime: Date, //access time
// mtime: Date, //modify time
// ctime: Date // create time
// size: Number
// }
// or null is file does not exist
function fs_fileStat(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
const
USAGE = 'usage: fileStat(filePath: string;)';
var
in_argv: PjsvalVector;
fn: TFileName;
obj: PJSRootedObject;
val: jsval;
{$IFNDEF MSWINDOWS}
info: stat;
{$ELSE}
{$ifdef ISDELPHIXE2}
infoRec: TDateTimeInfoRec;
{$else}
fad: TWin32FileAttributeData;
function fileTimeToDateTime(ft: TFileTime): TDateTime;
var ST,LT: TSystemTime;
begin
if FileTimeToSystemTime(ft,ST) and
SystemTimeToTzSpecificLocalTime(nil,ST,LT) then
result := SystemTimeToDateTime(LT) else
result := 0;
end;
{$endif}
{$ENDIF}
begin
try
in_argv := vp.argv;
if (argc < 1) or not in_argv[0].isString then
raise ESMException.Create(USAGE);
fn := in_argv[0].asJSString.ToString(cx);
obj := cx.NewRootedObject(cx.NewObject(nil));
try
{$IFNDEF MSWINDOWS}
if fpstat(PChar(fn), info) = 0 then begin
val.asDate[cx] := UnixToDateTime(info.st_atime);
obj.ptr.DefineProperty(cx, 'atime', val, JSPROP_ENUMERATE or JSPROP_READONLY, nil, nil);
val.asDate[cx] := UnixToDateTime(info.st_mtime);
obj.ptr.DefineProperty(cx, 'mtime', val, JSPROP_ENUMERATE or JSPROP_READONLY, nil, nil);
val.asDate[cx] := UnixToDateTime(info.st_ctime);
obj.ptr.DefineProperty(cx, 'ctime', val, JSPROP_ENUMERATE or JSPROP_READONLY, nil, nil);
val.asInt64 := info.st_size;
obj.ptr.DefineProperty(cx, 'size', val, JSPROP_ENUMERATE or JSPROP_READONLY, nil, nil);
vp.rval := obj.ptr.ToJSValue;
end else begin
{$ifdef SM_DEBUG}
SynSMLog.Add.Log(sllDebug, 'fstat(%) failed with error %',
[fn, SysErrorMessage(errno)]);
{$endif}
vp.rval := JSVAL_NULL;
end;
{$ELSE}
{$ifdef ISDELPHIXE2}
if FileGetDateTimeInfo(fn, infoRec, true) then begin
val.asDate[cx] := infoRec.LastAccessTime;
obj.ptr.DefineProperty(cx, 'atime', val, JSPROP_ENUMERATE or JSPROP_READONLY, nil, nil);
val.asDate[cx] := infoRec.TimeStamp;
obj.ptr.DefineProperty(cx, 'mtime', val, JSPROP_ENUMERATE or JSPROP_READONLY, nil, nil);
val.asDate[cx] := infoRec.CreationTime;
obj.ptr.DefineProperty(cx, 'ctime', val, JSPROP_ENUMERATE or JSPROP_READONLY, nil, nil);
if TWin32FindData(infoRec).nFileSizeHigh > 0 then
val := JSVAL_NAN
else
val.asInt64 := TWin32FindData(infoRec).nFileSizeLow;
obj.ptr.DefineProperty(cx, 'size', val, JSPROP_ENUMERATE or JSPROP_READONLY, nil, nil);
vp.rval := obj.ptr.ToJSValue;
end else
vp.rval := JSVAL_NULL;
{$else ISDELPHIXE2}
if GetFileAttributesEx(PChar(fn), GetFileExInfoStandard, @fad) then begin
val.asDate[cx] := fileTimeToDateTime(fad.ftLastAccessTime);
obj.ptr.DefineProperty(cx, 'atime', val, JSPROP_ENUMERATE or JSPROP_READONLY, nil, nil);
val.asDate[cx] := fileTimeToDateTime(fad.ftLastWriteTime);
obj.ptr.DefineProperty(cx, 'mtime', val, JSPROP_ENUMERATE or JSPROP_READONLY, nil, nil);
val.asDate[cx] := fileTimeToDateTime(fad.ftCreationTime);
obj.ptr.DefineProperty(cx, 'ctime', val, JSPROP_ENUMERATE or JSPROP_READONLY, nil, nil);
if fad.nFileSizeHigh > 0 then
val := JSVAL_NAN
else
val.asInt64 := fad.nFileSizeLow;
obj.ptr.DefineProperty(cx, 'size', val, JSPROP_ENUMERATE or JSPROP_READONLY, nil, nil);
vp.rval := obj.ptr.ToJSValue;
end else
vp.rval := JSVAL_NULL;
{$endif ISDELPHIXE2}
{$ENDIF MSWINDOWS}
finally
cx.FreeRootedObject(obj);
end;
Result := True;
except
on E: Exception do begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
/// check directory exist
// accept one parameter - "path to dir"
function fs_directoryExists(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
const
USAGE = 'usage: directoryExists(dirPath: string;)';
var
in_argv: PjsvalVector;
filePath: TFileName;
val: jsval;
begin
try
in_argv := vp.argv;
if (argc < 1) or not in_argv[0].isString then
raise ESMException.Create(USAGE);
filePath := in_argv[0].asJSString.ToString(cx);
val.asBoolean := DirectoryExists(filePath);
vp.rval := val;
Result := True;
except
on E: Exception do
begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
/// check file exist
// accept one parameter - "path to file"
function fs_fileExists(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
const
USAGE = 'usage: fileExists(filePath: string;)';
var
in_argv: PjsvalVector;
filePath: TFileName;
val: jsval;
begin
try
in_argv := vp.argv;
if (argc < 1) or not in_argv[0].isString then
raise ESMException.Create(USAGE);
filePath := in_argv[0].asJSString.ToString(cx);
val.asBoolean := FileExists(filePath);
vp.rval := val;
Result := True;
except
on E: Exception do
begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
/// Used to speed up module loading. Returns the contents of the file as
// a string or undefined when the file cannot be opened. The speedup
// comes from not creating Error objects on failure.
function fs_internalModuleReadFile(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
const
USAGE = 'usage: internalModuleReadFile(filePath: string;)';
var
in_argv: PjsvalVector;
filePath: TFileName;
fileContent: jsval;
{$ifdef SM_DEBUG_FSDUMPFILEEXCERPT}
len: size_t;
content: RawUTF8;
{$endif}
begin
try
in_argv := vp.argv;
if (argc < 1) or not in_argv[0].isString then
raise ESMException.Create(USAGE);
filePath := in_argv[0].asJSString.ToString(cx);
{$ifdef SM_DEBUG_FSTRACEFILEREAD}
SynSMLog.Add.Log(sllDebug, 'fs_internalModuleReadFile (%) called', filePath);
{$ENDIF}
if FileExists(filePath) then begin
fileContent := cx.NewJSString(AnyTextFileToRawUTF8(filePath, true)).ToJSVal;
vp.rval := fileContent;
{$ifdef SM_DEBUG_FSDUMPFILEEXCERPT}
len := fileContent.asJSString.Length;
content := fileContent.asJSString.ToUTF8(cx);
if len > 120 then
content := Format('%s .. %s', [Copy(content, 1, 58), Copy(content, len-58, 58)]);
SynSMLog.Add.Log(sllDebug,
'fs_internalModuleReadFile (%): content loaded, length = % content:'#13'%',
[filePath, len, content]);
{$endif}
end else begin
vp.rval := JSVAL_VOID;
{$ifdef SM_DEBUG}
SynSMLog.Add.Log(sllDebug,
'fs_internalModuleReadFile (%): file not found', StringToUTF8(filePath));
{$endif}
end;
Result := True;
except
on E: Exception do
begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
/// Used to speed up module loading. Returns 0 if the path refers to
// a file, 1 when it's a directory or < 0 on error (usually -ENOENT.)
// The speedup comes from not creating thousands of Stat and Error objects.
function fs_internalModuleStat(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
const
USAGE = 'usage: internalModuleStat(filePath: string;)';
var
in_argv: PjsvalVector;
fn: TFileName;
Code: Integer;
val: jsval;
begin
try
in_argv := vp.argv;
if (argc < 1) or not in_argv[0].isString then
raise ESMException.Create(USAGE);
try
fn := in_argv[0].asJSString.ToString(cx);
except
raise
end;
{$IFDEF MSWINDOWS}
Code := GetFileAttributes(PChar(fn));
if Code <> -1 then
if Code and FILE_ATTRIBUTE_DIRECTORY = 0 then
Code := 0
else
Code := 1;
{$ELSE} // this can be used in Windows too
Code := FileGetAttr(fn);
if Code <> -1 then
if Code and faDirectory = 0 then
Code := 0
else
Code := 1;
{$ENDIF}
val.asInteger := Code;
vp.rval := val;
Result := True;
except
on E: Exception do begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
/// read content of directory. return array of file names witout started from dot '.'
/// in case of includeDirNames - return sub direcrory with trailing slash
function fs_readDir(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
var
in_argv: PjsvalVector;
dir: TFileName;
F: TSearchRec;
res: PJSRootedObject;
cNum, searchAttr: integer;
includeFolders: boolean;
const
USAGE = 'usage: readDir(dirPath: String; [includeFolders: boolean = false]): Array';
begin
try
in_argv := vp.argv;
if (argc < 1) or not in_argv[0].isString then
raise ESMException.Create(USAGE);
if (argc = 2) and in_argv[1].isBoolean then
includeFolders := in_argv[1].asBoolean
else
includeFolders := false;
dir := in_argv[0].asJSString.ToString(cx);
if not DirectoryExists(Dir) then
begin
vp.rval := JSVAL_NULL;
Result := true;
Exit;
end;
Dir := IncludeTrailingPathDelimiter(Dir);
res := cx.NewRootedObject(cx.NewArrayObject(0));
try
{$WARN SYMBOL_PLATFORM OFF}
searchAttr := faAnyFile;
if not includeFolders then
searchAttr := searchAttr - faDirectory;
if FindFirst(Dir + '*', searchAttr, F) = 0 then begin
cNum := 0;
repeat
if (F.Name <> '.') and (F.Name <> '..') then begin
res.ptr.SetElement(cx, cNum, cx.NewJSString(F.Name).ToJSVal);
inc(cNum);
end;
until FindNext(F) <> 0;
end;
{$WARN SYMBOL_PLATFORM ON}
{$ifdef ISDELPHIXE2}System.{$ENDIF}SysUtils.FindClose(F);
vp.rval := res.ptr.ToJSValue;
finally
cx.FreeRootedObject(res);
end;
Result := True;
except
on E: Exception do
begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
{$ifdef MSWINDOWS}
{$if defined(FPC) or not defined(ISDELPHIXE2)}
const VOLUME_NAME_DOS = $0;
// Only Wide version return a value
function GetFinalPathNameByHandleW(hFile: THandle; lpszFilePath: LPWSTR;
cchFilePath: DWORD; dwFlags: DWORD): DWORD; stdcall; external kernel32;
{$ifend}
const LONG_PATH_PREFIX: PChar = '\\?\';
const UNC_PATH_PREFIX: PChar = '\\?\UNC\';
{$else}
function realpath(name: PChar; resolved: PChar): PChar; cdecl; external 'c' name 'realpath';
{$endif}
/// Implementation of realpath as in libuv
function os_realpath(const FileName: TFileName; var TargetName: TFileName): Boolean;
{$ifdef MSWINDOWS}
{$WARN SYMBOL_PLATFORM OFF}
var
Handle: THandle;
w_realpath_len: Cardinal;
Res: WideString;
begin
Result := False;
if not CheckWin32Version(6, 0) then
exit;
Handle := CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_BACKUP_SEMANTICS, 0);
if Handle = INVALID_HANDLE_VALUE then
exit;
try
w_realpath_len := GetFinalPathNameByHandleW(Handle, nil, 0, VOLUME_NAME_DOS);
if (w_realpath_len = 0) then
exit;
SetLength(Res, w_realpath_len);
if GetFinalPathNameByHandleW(Handle, PWideChar(Res), w_realpath_len, VOLUME_NAME_DOS) > 0 then begin
WideCharToStrVar(PWideChar(Res), String(TargetName));
if StrLComp(PChar(TargetName), UNC_PATH_PREFIX, length(UNC_PATH_PREFIX)) = 0 then begin
// convert \\?\UNC\host\folder -> \\host\folder
TargetName := Copy(TargetName, 7, length(TargetName)-7);
TargetName[1] := '\';
end else if StrLComp(PChar(TargetName), LONG_PATH_PREFIX, length(LONG_PATH_PREFIX)) = 0 then
TargetName := Copy(TargetName, length(LONG_PATH_PREFIX)+1, length(TargetName)-length(LONG_PATH_PREFIX))
else
Exit; // error
Result := True;
end;
finally
CloseHandle(Handle);
end;
end;
{$WARN SYMBOL_PLATFORM ON}
{$else}
var
Buf: TFileName;
begin
SetLength(Buf, PATH_MAX);
Result := realpath(PChar(FileName), PChar(Buf)) <> nil;
if Result then
TargetName := PChar(Buf)
else
TargetName := '';
end;
{$endif}
/// return the canonicalized absolute pathname
// expands all symbolic links and resolves references to /./,
// /../ and extra '/' characters in the string named by path
// to produce a canonicalized absolute pathname
function fs_realPath(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
var
dir, target: TFileName;
const
USAGE = 'usage: realpath(dirPath: String): string';
begin
try
if (argc < 1) or not vp.argv[0].isString then
raise ESMException.Create(USAGE);
dir := vp.argv[0].asJSString.ToString(cx);
//if not FileGetSymLinkTarget(dir, target) then
if not os_realpath(dir, target) then
target := dir;
vp.rval := cx.NewJSString(target).ToJSVal;
Result := True;
except
on E: Exception do
begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
function fs_rename(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
const
USAGE = 'usage: rename(fromPath, toPath: String)';
var
fromPath, toPath: TFileName;
//f : file;
begin
try
if (argc <> 2) or not vp.argv[0].isString or not vp.argv[1].isString then
raise ESMException.Create(USAGE);
fromPath := vp.argv[0].asJSString.ToString(cx);
toPath := vp.argv[1].asJSString.ToString(cx);
{$IFDEF MSWINDOWS} // libc rename implementation rewrites destination if it's already exist
if FileExists(toPath) then
if not SysUtils.DeleteFile(toPath) then
RaiseLastOSError;
{$ENDIF}
if not SysUtils.RenameFile(fromPath, toPath) then
RaiseLastOSError;
Result := True;
except
on E: Exception do begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
/// Create UInt8Array & load file into it
function fs_loadFileToBuffer(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
var
in_argv: PjsvalVector;
filePath: string;
size: Int64;
arr_data: Puint8Vector;
fFileStream: TFileStream;
arr: PJSObject;
val: jsval;
begin
try
if (argc <> 1) then
raise ESMException.Create('usage loadFileToBuffer(pathToFile: String): ArrayBuffer');
in_argv := vp.argv;
filePath := in_argv[0].asJSString.ToSynUnicode(cx);
{$IFNDEF FPC}
if IsRelativePath(filePath) then
{$ELSE}
if not FilenameIsAbsolute(filePath) then
{$ENDIF}
raise ESMException.Create('no relative path allowed in loadFile');
if not FileExists(filePath) then
begin
vp.rval := JSVAL_NULL;
end
else
begin
size := FileSize(filePath);
if PInt64Rec(@size)^.Hi > 0 then
raise ESMException.Create('file to large');
arr := cx.NewArrayBuffer(PInt64Rec(@size)^.Lo);
arr_data := arr.GetArrayBufferData;
fFileStream := TFileStream.Create(filePath, fmOpenRead or fmShareDenyNone);
try
fFileStream.Read(arr_data[0], size);
finally
fFileStream.Free;
end;
val.asObject := arr;
vp.rval := val;
end;
Result := True;
except
on E: Exception do
begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
/// write Object to file using specifien encoding.
// internaly use SyNodeReadWrite.write
function fs_writeFile(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
var
in_argv: PjsvalVector;
filePath: string;
stream: TFileStream;
writer: SynCommons.TTextWriter;
begin
try
if (argc < 2) then
raise ESMException.Create('usage writeFile(filePath: String, fileContent: String|Object|ArrayBuffer [,encoding]');
in_argv := vp.argv;
filePath := in_argv[0].asJSString.ToSynUnicode(cx);
stream := TFileStream.Create(filePath, fmCreate);
writer := SynCommons.TTextWriter.Create(stream, 65536);
try
vp.rval := SyNodeReadWrite.SMWrite_impl(cx, argc - 1, @in_argv[1], writer);
result := True;
finally
writer.FlushFinal;
writer.Free;
stream.Free;
end;
except
on E: Exception do
begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
/// append Object to file using specifien encoding.
// internaly use SyNodeReadWrite.write
function fs_appendFile(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
var
in_argv: PjsvalVector;
filePath: string;
FFile: THandle;
stream: TFileStream;
writer: SynCommons.TTextWriter;
begin
try
if (argc < 2) then
raise ESMException.Create('usage appendFile(filePath: String, fileContent: String|Object|ArrayBuffer [,encoding]');
in_argv := vp.argv;
filePath := in_argv[0].asJSString.ToSynUnicode(cx);
if not FileExists(filePath) then begin
FFile := FileCreate(filePath);
if PtrInt(FFile) > 0 then
FileClose(FFile);
end;
stream := TFileStream.Create(filePath, fmOpenReadWrite);
stream.Seek(0, soEnd);
writer := SynCommons.TTextWriter.Create(stream, 65536);
try
vp.rval := SyNodeReadWrite.SMWrite_impl(cx, argc - 1, @in_argv[1], writer);
result := True;
finally
writer.FlushFinal;
writer.Free;
stream.Free;
end;
except
on E: Exception do
begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
/// delete file AFileName: TFileName - full file path
function fs_deleteFile(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
var
in_argv: PjsvalVector;
filePath: string;
val: jsval;
begin
try
if (argc <> 1) then
raise ESMException.Create('Invalid number of args for function nsm_deleteFile. Requied 1 - filePath');
in_argv := vp.argv;
filePath := in_argv[0].asJSString.ToSynUnicode(cx);
if SysUtils.DeleteFile(filePath) then
val.asBoolean := True
else if fileExists(filePath) then
raise Exception.Create('can''t delete file')
else
val.asBoolean := True;
vp.rval := val;
Result := True;
except
on E: Exception do
begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
/// the same as SysUtils.ForceDirectories
function fs_forceDirectories(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
var
in_argv: PjsvalVector;
dir: TFileName;
val: jsval;
const
f_usage = 'usage: forceDirectories(dirPath: String): Boolean';
f_invalidPath = 'empty or relative path is not allowed';
begin
try
in_argv := vp.argv;
if (argc <> 1) or not in_argv[0].isString then
raise ESMException.Create(f_usage);
dir := in_argv[0].asJSString.ToSynUnicode(cx);
if (dir = '') or {$IFNDEF FPC}IsRelativePath(dir){$ELSE}not FilenameIsAbsolute(dir){$ENDIF} then
raise ESMException.Create(f_invalidPath);
val.asBoolean := ForceDirectories(dir);
vp.rval := val;
Result := True;
except
on E: Exception do
begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
function fs_removeDir(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
var
in_argv: PjsvalVector;
dir: TFileName;
val: jsval;
const
f_usage = 'usage: removeDir(dirPath: String): Boolean';
f_invalidPath = 'empty or relative path is not allowed';
begin
try
in_argv := vp.argv;
if (argc <> 1) or not in_argv[0].isString then
raise ESMException.Create(f_usage);
dir := in_argv[0].asJSString.ToSynUnicode(cx);
if (dir = '') or {$IFNDEF FPC}IsRelativePath(dir){$ELSE}not FilenameIsAbsolute(dir){$ENDIF} then
raise ESMException.Create(f_invalidPath);
val.asBoolean := RemoveDir(dir);
vp.rval := val;
Result := True;
except
on E: Exception do
begin
Result := False;
vp.rval := JSVAL_VOID;
JSError(cx, E);
end;
end;
end;
function SyNodeBindingProc_fs(const Engine: TSMEngine;
const bindingNamespaceName: SynUnicode): jsval;
const
attrs = JSPROP_READONLY or JSPROP_PERMANENT;
var
obj: PJSRootedObject;
cx: PJSContext;
begin
cx := Engine.cx;
obj := cx.NewRootedObject(cx.NewObject(nil));
try
obj.ptr.DefineFunction(cx, 'loadFile', fs_loadFile, 1, attrs);
obj.ptr.DefineFunction(cx, 'relToAbs', fs_relToAbs, 2, attrs);
obj.ptr.DefineFunction(cx, 'fileStat', fs_fileStat, 1, attrs);
obj.ptr.DefineFunction(cx, 'directoryExists', fs_directoryExists, 1, attrs);
obj.ptr.DefineFunction(cx, 'fileExists', fs_fileExists, 1, attrs);
obj.ptr.DefineFunction(cx, 'internalModuleReadFile', fs_internalModuleReadFile, 1, attrs);
obj.ptr.DefineFunction(cx, 'internalModuleStat', fs_internalModuleStat, 1, attrs);
obj.ptr.DefineFunction(cx, 'readDir', fs_readDir, 2, attrs);
obj.ptr.DefineFunction(cx, 'realpath', fs_realPath, 1, attrs);
obj.ptr.DefineFunction(cx, 'rename', fs_rename, 2, attrs);
obj.ptr.DefineFunction(cx, 'loadFileToBuffer', fs_loadFileToBuffer, 1, attrs);
obj.ptr.DefineFunction(cx, 'writeFile', fs_writeFile, 3, attrs);
obj.ptr.DefineFunction(cx, 'appendFile', fs_appendFile, 3, attrs);
obj.ptr.DefineFunction(cx, 'forceDirectories', fs_forceDirectories, 1, attrs);
obj.ptr.DefineFunction(cx, 'removeDir', fs_removeDir, 1, attrs);
obj.ptr.DefineFunction(cx, 'deleteFile', fs_deleteFile, 1, attrs);
Result := obj.ptr.ToJSValue;
finally
cx.FreeRootedObject(obj);
end;
end;
initialization
TSMEngineManager.RegisterBinding('fs', SyNodeBindingProc_fs);
end.