819 lines
24 KiB
ObjectPascal
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.
|