node.fs.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. {
  2. This file is part of the Pas2JS run time library.
  3. Copyright (c) 2018 by Mattias Gaertner
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. {$IFNDEF FPC_DOTTEDUNITS}
  11. unit node.fs;
  12. {$ENDIF}
  13. {$mode objfpc}
  14. {$ModeSwitch externalclass}
  15. interface
  16. uses
  17. {$IFDEF FPC_DOTTEDUNITS}
  18. JSApi.JS,NodeApi.JS, System.Types, System.SysUtils;
  19. {$ELSE}
  20. JS, NodeJS, Types, SysUtils;
  21. {$ENDIF}
  22. var
  23. DirectorySeparator: char = '/';
  24. DriveSeparator: string = '';
  25. ExtensionSeparator: char = '.';
  26. PathSeparator: char = ':';
  27. AllowDirectorySeparators: set of char = ['\','/'];
  28. AllowDriveSeparators: set of char = [];
  29. AllDirectorySeparators: set of char = ['\','/'];
  30. AllFilesMask: string = '*';
  31. MaxPathLen: integer = 4096;
  32. PathDelim: char = '/'; // FPC = DirectorySeparator
  33. DriveDelim: string = ''; // FPC = DriveSeparator
  34. PathSep: char = ':';// FPC = PathSeparator
  35. MAX_PATH: integer = 4096; // FPC = MaxPathLen;
  36. const
  37. //faReadOnly = 1;
  38. //faHidden = 2;
  39. //faSystem = 4;
  40. //faReserve = 8;
  41. faDirectory = 16;
  42. //faArchive = 32;
  43. faAnyFile = $000001FF;
  44. function GetCurrentDir: string;
  45. function FileExists(const Filename: string): boolean;
  46. function DirectoryExists(const Filename: string): boolean;
  47. function ExtractFilePath(const Filename: string): string;
  48. function ExtractFileName(const Filename: string): string;
  49. function ExtractFileExt(const Filename: string): string;
  50. function SetDirSeparators(const Filename: string): string;
  51. function ExpandFileName(const Filename: string): string;
  52. function ExcludeTrailingPathDelimiter(const Filename: string): string;
  53. function IncludeTrailingPathDelimiter(const Filename: string): string;
  54. function ChangeFileExt(const Filename, NewExt: string): string;
  55. function DeleteFile(const Filename: String): Boolean;
  56. function RenameFile(const OldName, NewName: String): Boolean;
  57. type
  58. TSearchRec = record
  59. Time : Longint;
  60. Size : nativeint;
  61. Attr : Longint;
  62. Name : String;
  63. ExcludeAttr : Longint;
  64. FindHandle : Pointer;
  65. //Mode : TMode;
  66. //FindData : TFindData;
  67. end;
  68. TUnicodeSearchRec = TSearchRec;
  69. function FindFirst(const Path: String; Attr : Longint; out Rslt: TSearchRec): Longint;
  70. function FindNext(var Rslt: TSearchRec): Longint;
  71. procedure FindClose(var F: TSearchrec);
  72. function FileDateToDateTime(Filedate : Longint): TDateTime;
  73. function DateTimeToFileDate(DateTime: TDateTime): longint;
  74. const
  75. S_IRUSR = &400; // read by owner
  76. S_IWUSR = &200; // write by owner
  77. S_IXUSR = &100; // execute/search by owner
  78. S_IRGRP = &40; // read by group
  79. S_IWGRP = &20; // write by group
  80. S_IXGRP = &10; // execute/search by group
  81. S_IROTH = &4; // read by others
  82. S_IWOTH = &2; // write by others
  83. S_IXOTH = &1; // execute/search by others
  84. F_OK: nativeint; external name 'fs.constants.F_OK'; // file visible
  85. R_OK: nativeint; external name 'fs.constants.R_OK'; // file readable
  86. W_OK: nativeint; external name 'fs.constants.W_OK'; // file writable
  87. X_OK: nativeint; external name 'fs.constants.X_OK'; // file executable
  88. COPYFILE_EXCL: nativeint; external name 'fs.constants.COPYFILE_EXCL';
  89. COPYFILE_FICLONE: NativeInt; external name 'fs.constants.COPYFILE_FICLONE';
  90. COPYFILE_FICLONE_FORCE: NativeInt; external name 'fs.constants.COPYFILE_FICLONE_FORCE';
  91. type
  92. TNJSFileDesc = JSValue; // integer or nil
  93. TNJSFileMode = NativeInt;
  94. { TNJSDirEnt }
  95. TNJSDirEnt = class external name 'fs.Dirent'
  96. private
  97. FName: string; external name 'name';
  98. public
  99. function isBlockDevice: boolean;
  100. function isCharacterDevice: boolean;
  101. function isDirectory: boolean;
  102. function isFIFO: boolean;
  103. function isFile: boolean;
  104. function isSocket: boolean;
  105. function isSymbolicLink: boolean;
  106. property Name: string read FName;
  107. end;
  108. TNJSDirEntArray = array of TNJSDirEnt;
  109. { TNJSStats }
  110. TNJSStats = class external name 'fs.Stats'
  111. private
  112. public
  113. dev: NativeInt;
  114. ino: NativeInt;
  115. mode: TNJSFileMode;
  116. nlink: NativeInt;
  117. uid: NativeInt;
  118. gid: NativeInt;
  119. rdev: NativeInt;
  120. size: NativeInt;
  121. blksize: NativeInt;
  122. blocks: NativeInt;
  123. atimeMs: Double;
  124. mtimeMs: Double;
  125. ctimeMs: Double;
  126. birthtimeMs: Double;
  127. atime: TJSDate;
  128. mtime: TJSDate;
  129. ctime: TJSDate;
  130. birthtime: TJSDate;
  131. function isBlockDevice: boolean;
  132. function isCharacterDevice: boolean;
  133. function isDirectory: boolean;
  134. function isFIFO: boolean;
  135. function isFile: boolean;
  136. function isSocket: boolean;
  137. function isSymbolicLink: boolean;
  138. end;
  139. { TNJSStreamReadable }
  140. TNJSStreamReadable = class external name 'stream.Readable'
  141. private
  142. FReadableHighWaterMark: NativeInt external name 'readableHighWaterMark';
  143. FReadableLength: NativeInt external name 'readableLength';
  144. public
  145. function destroy(Error: TJSError): TNJSStreamReadable;
  146. function isPaused: boolean;
  147. function pause: TNJSStreamReadable;
  148. // pipe(destination[, options])
  149. function read: jsvalue; // string | buffer | nil | any
  150. function read(size: nativeint): jsvalue; // string | buffer | nil | any
  151. property ReadableHighWaterMark: NativeInt read FReadableHighWaterMark;
  152. property ReadableLength: NativeInt read FReadableLength;
  153. function resume: TNJSStreamReadable;
  154. function setEncoding(Encoding: string): TNJSStreamReadable;
  155. // unpipe([destination])
  156. // unshift(chunk)
  157. // wrap(stream)
  158. end;
  159. { TNJSReadStream }
  160. TNJSReadStream = class external name 'fs.ReadStream'(TNJSStreamReadable)
  161. private
  162. fBytesRead: NativeInt external name 'bytesRead';
  163. FPath: string external name 'path';
  164. public
  165. property BytesRead: NativeInt read fBytesRead;
  166. property Path: string read FPath;
  167. end;
  168. TNJSStreamWritableEndHandler = reference to procedure;
  169. { TNJSStreamWritable }
  170. TNJSStreamWritable = class external name 'stream.Writable'
  171. private
  172. FwritableHighWaterMark: nativeint external name 'writableHighWaterMark';
  173. FwritableLength: nativeint external name 'writableLength';
  174. public
  175. procedure cork;
  176. function destroy(Error: TJSError): TNJSStreamWritable;
  177. function _end(chunk: string): TNJSStreamWritable; external name 'end';
  178. function _end(chunk: string; encoding: string;
  179. callback: TNJSStreamWritableEndHandler = nil): TNJSStreamWritable; external name 'end';
  180. function setDefaultEncoding(Encoding: string): TNJSStreamWritable;
  181. procedure uncork;
  182. property writableHighWaterMark: NativeInt read FwritableHighWaterMark;
  183. property writableLength: nativeint read FwritableLength;
  184. function write(chunk: string): boolean;
  185. function write(chunk: string; encoding: string;
  186. callback: TNJSStreamWritableEndHandler): boolean;
  187. end;
  188. { TNJSWriteStream }
  189. TNJSWriteStream = class external name 'fs.WriteStream'(TNJSStreamWritable)
  190. private
  191. FBytesWritten: NativeInt external name 'bytesWritten';
  192. FPath: string external name 'path';
  193. public
  194. property BytesWritten: NativeInt read FBytesWritten;
  195. property Path: string read FPath;
  196. end;
  197. { TNJSAppendFileOpts }
  198. TNJSAppendFileOpts = record
  199. Encoding: string external name 'encoding'; // default nil
  200. Mode: TNJSFileMode external name 'mode'; // default &666
  201. Flag: string external name 'flag'; // default 'a'
  202. end;
  203. { TNJSReadStreamOpts }
  204. TNJSReadStreamOpts = record
  205. Flags: string external name 'flags'; // default 'r'
  206. Encoding: string external name 'encoding'; // default nil
  207. FD: TNJSFileDesc external name 'fd'; // default nil
  208. Mode: TNJSFileMode external name 'mode'; // default &666
  209. AutoClose: boolean external name 'autoclose'; // default true
  210. StartByte: NativeInt external name 'start';
  211. EndByte: NativeInt external name 'end'; // default Infinity
  212. HighWaterMark: NativeInt external name 'highWaterMark'; // default 64*1024
  213. end;
  214. { TNJSWriteStreamOpts }
  215. TNJSWriteStreamOpts = record
  216. Flags: string external name 'flags'; // default 'w'
  217. Encoding: string external name 'encoding'; // default 'utf8'
  218. FD: TNJSFileDesc external name 'fd'; // default nil
  219. Mode: TNJSFileMode external name 'mode'; // default &666
  220. AutoClose: boolean external name 'autoclose'; // default true
  221. Start: NativeInt external name 'start';
  222. end;
  223. { TNJSStatOpts }
  224. TNJSStatOpts = record
  225. bigint: boolean;
  226. end;
  227. { TNJSMkdirOpts }
  228. TNJSMkdirOpts = record
  229. recursive: boolean; // default false
  230. mode: nativeint; // default &777
  231. end;
  232. { TNJSReadDirOpts }
  233. TNJSReadDirOpts = record
  234. encoding: string; // default 'utf8'
  235. withFileTypes: boolean; // default false
  236. end;
  237. { TNJSReadFileOpts }
  238. TNJSReadFileOpts = record
  239. encoding: string; // default nil
  240. flag: string; // default 'r'
  241. end;
  242. { TNJSReadLinkOpts }
  243. TNJSReadLinkOpts = record
  244. encoding: string; // default 'utf8'
  245. end;
  246. TNJSWriteFileOpts = record
  247. encoding: string; // default 'utf8'
  248. mode: nativeint; // default &666
  249. flag: string; // default 'w'
  250. end;
  251. type
  252. { TNJSFS - nodejs filesystem }
  253. TNJSFS = class external name 'fs'
  254. public
  255. procedure accessSync(Path: string; Mode: TNJSFileMode = F_OK); // throws Error if access is not granted
  256. procedure appendFileSync(Path: string; Data: string);
  257. procedure appendFileSync(Path: string; Data: string; const Options: TJSObject{TNJSAppendFileOpts});
  258. procedure chmodSync(Path: string; Mode: TNJSFileMode);
  259. procedure chownSync(Path: string; uid, gid: NativeInt);
  260. procedure closeSync(fd: TNJSFileDesc);
  261. procedure copyFileSync(Src, Dest: string;
  262. Flags: NativeInt = 0 // see COPYFILE_EXCL etc
  263. );
  264. function createReadStream(Path: string): TNJSWriteStream;
  265. function createReadStream(Path: string; const Options: TJSObject{TNJSReadStreamOpts}): TNJSReadStream;
  266. function createWriteStream(Path: string): TNJSWriteStream;
  267. function createWriteStream(Path: string; const Options: TJSObject{TNJSWriteStreamOpts}): TNJSWriteStream;
  268. function existsSync(Path: string): boolean;
  269. procedure fchmodSync(fd: TNJSFileDesc; mode: TNJSFileMode);
  270. procedure fchownSync(fd: TNJSFileDesc; uid, gid: NativeInt);
  271. procedure fdatasyncSync(fd: TNJSFileDesc);
  272. procedure fstatSync(fd: TNJSFileDesc; const Options: TJSObject{TNJSStatOpts});
  273. procedure fsyncSync(fd: TNJSFileDesc);
  274. procedure ftruncateSync(fd: TNJSFileDesc; Len: nativeint = 0);
  275. procedure futimesSync(fd: TNJSFileDesc; atime: NativeInt; mtime: NativeInt);
  276. procedure lchownSync(path: string; uid, gid: NativeInt);
  277. procedure linkSync(ExistingPath, NewPath: string);
  278. procedure lstatSync(Path: string);
  279. procedure lstatSync(Path: string; const Options: TJSObject{TNJSStatOpts});
  280. procedure mkdirSync(Path: string; const Options: TJSObject{TNJSMkdirOpts});
  281. // mkdtempSync
  282. function openSync(Path: string; Flags: string; mode: TNJSFileMode): TNJSFileDesc;
  283. function readdirSync(Path: string): TJSArray; // TStringDynArray
  284. function readdirSync(Path: string; const Options: TJSObject{TNJSReadDirOpts}): TJSArray; // can be TStringDynArray or TNJSDirEntArray if withFileTypes=true
  285. function readFileSync(Path: string; const Options: TJSObject{TNJSReadFileOpts}): string;
  286. function readlinkSync(Path: string): string;
  287. function readlinkSync(Path: string; const Options: TJSObject{TNJSReadLinkOpts}): string;
  288. // readSync(fd, buffer, offset, length, position)
  289. // realpathSync(path[, options])
  290. procedure renameSync(OldPath, NewPath: string);
  291. procedure rmdirSync(Path: string);
  292. function statSync(Path: string): TNJSStats;
  293. function statSync(Path: string; const Options: TJSObject{TNJSStatOpts}): TNJSStats;
  294. procedure symlinkSync(Target, Path: string; LinkType: string = 'file');
  295. procedure truncateSync(Path: string; len: NativeInt = 0);
  296. procedure unlinkSync(Path: string);
  297. // unwatchFile(filename[, listener])
  298. // utimesSync(path, atime, mtime)
  299. // watch(filename[, options][, listener])
  300. // watchFile(filename[, options], listener)
  301. procedure writeFileSync(
  302. aFile: jsvalue; // string | buffer | URL | filedescriptor
  303. Data: jsvalue // string | buffer | typedarray | DataView
  304. );
  305. procedure writeFileSync(
  306. aFile: jsvalue; // string | buffer | URL | filedescriptor
  307. Data: jsvalue; // string | buffer | typedarray | DataView
  308. const Options: TJSObject{TNJSWriteFileOpts});
  309. function writeSync(fd: TNJSFileDesc;
  310. buffer: jsvalue; // buffer | TypedArray | DataView
  311. Offset, Count, Position: NativeInt): NativeInt;
  312. function writeSync(fd: TNJSFileDesc; Data: string; Position: NativeInt;
  313. Encoding: string): NativeInt;
  314. end;
  315. type
  316. { TNJSPathParsed }
  317. TNJSPathParsed = class external name 'TNJSPathParsed'
  318. public
  319. dir: string;
  320. root: string;
  321. base: string;
  322. name: string;
  323. ext: string;
  324. end;
  325. { TNJSPath }
  326. TNJSPath = class external name 'path'
  327. public
  328. win32: TJSObject; // todo
  329. posix: TJSObject; // todo
  330. public const
  331. // Beware: nodejs uses "separator" and "delimiter" the other way round than FPC/Delphi
  332. delimiter: char; // search PATH delimiter, windows ;, posix :
  333. sep: char; // directory delimiter, windows \, posix: /
  334. public
  335. function basename(Path: string; Ext: string = ''): string; // remove the directory, optional ext to chomp off
  336. function dirname(Path: string): string; // returns directory without trailing sep
  337. function extname(Path: string): string; // returns from last occurence of '.', if path starts with '.' the empty string is returned
  338. function format(PathObject: TJSObject): string; {
  339. PathObjectis can contain the following string properties:
  340. dir, root, base, name, ext
  341. root is ignored if dir exists
  342. ext and name are ignored if base exists
  343. }
  344. function isAbsolute(Path: string): boolean; // '' returns false
  345. function join(Path1: string): string; varargs; // joins all passed strings with sep and normalizes, e.g. resolves '..', if the result is empty it returns '.'
  346. function normalize(Path: string): string; // resolves '..' and '.' folders, reduces multiple delimiters, trailing sep is preserved, empty string is returned as '.', on windows replaces / with \\
  347. function parse(Path: string): TNJSPathParsed;
  348. function relative(FromPath, ToPath: string): string; // resolve both, then create relative, if both the same returns ''
  349. function resolve(Path1: string): string; varargs; // resolve from right to left, prepend until an absolute path is created
  350. function toNamespacedPath(Path: string): string; // windows only
  351. end;
  352. var
  353. NJS_FS: TNJSFS;
  354. NJS_Path: TNJSPath;
  355. implementation
  356. function GetCurrentDir: string;
  357. begin
  358. Result:=NJS_Path.resolve('');
  359. end;
  360. function FileExists(const Filename: string): boolean;
  361. begin
  362. Result:=NJS_FS.existsSync(Filename);
  363. end;
  364. function DirectoryExists(const Filename: string): boolean;
  365. var
  366. stats: TNJSStats;
  367. begin
  368. try
  369. stats:=NJS_FS.statSync(Filename);
  370. except
  371. exit(false);
  372. end;
  373. Result:=stats.isDirectory;
  374. end;
  375. function ExtractFilePath(const Filename: string): string;
  376. var
  377. i : longint;
  378. begin
  379. i := Length(FileName);
  380. while (i > 0) and not (FileName[i] in AllDirectorySeparators) do
  381. Dec(i);
  382. If I>0 then
  383. Result := Copy(FileName, 1, i)
  384. else
  385. Result:='';
  386. end;
  387. function ExtractFileName(const Filename: string): string;
  388. var
  389. i : longint;
  390. begin
  391. I := Length(FileName);
  392. while (I > 0) and not (FileName[i] in AllDirectorySeparators) do
  393. Dec(I);
  394. Result := Copy(FileName, I + 1, MaxInt);
  395. end;
  396. function ExtractFileExt(const Filename: string): string;
  397. var
  398. i : longint;
  399. SOF : Boolean; // Dot at Start of filename ?
  400. begin
  401. Result:='';
  402. I := Length(FileName);
  403. while (I > 0) and not (FileName[i] in AllDirectorySeparators) do
  404. begin
  405. if (Filename[i]=ExtensionSeparator) then
  406. begin
  407. SOF:=(I=1) or (FileName[i-1] in AllowDirectorySeparators);
  408. if (Not SOF) or FirstDotAtFileNameStartIsExtension then
  409. Result:=Copy(FileName, I, MaxInt);
  410. exit;
  411. end;
  412. Dec(I);
  413. end;
  414. end;
  415. function SetDirSeparators(const Filename: string): string;
  416. var
  417. i: Integer;
  418. begin
  419. Result:=Filename;
  420. For i:=1 to Length(Result) do
  421. If (Result[i] in AllowDirectorySeparators)
  422. and (Result[i]<>DirectorySeparator) then
  423. Result[i]:=DirectorySeparator;
  424. end;
  425. function ExpandFileName(const Filename: string): string;
  426. var
  427. IsAbs: Boolean;
  428. HomeDir, Fn: String;
  429. begin
  430. Fn := SetDirSeparators(Filename);
  431. IsAbs := NJS_Path.isAbsolute(Fn);
  432. if (not IsAbs) then
  433. begin
  434. if (PathDelim='/')
  435. and (((Length(Fn) > 1) and (Fn[1] = '~') and (Fn[2] = '/'))
  436. or (Fn = '~') ) then
  437. begin
  438. HomeDir := NJS_OS.homedir;
  439. if not NJS_Path.isAbsolute(HomeDir) then
  440. HomeDir := ExpandFileName(HomeDir);
  441. Fn := HomeDir + Copy(Fn,2,length(Fn));
  442. IsAbs := True;
  443. end;
  444. end;
  445. if IsAbs then
  446. begin
  447. Result := NJS_Path.resolve(Fn);
  448. end
  449. else
  450. begin
  451. Fn := IncludeTrailingPathDelimiter(GetCurrentDir) + Fn;
  452. Fn := NJS_Path.resolve(Fn);
  453. Result := Fn;
  454. end;
  455. end;
  456. function ExcludeTrailingPathDelimiter(const Filename: string): string;
  457. Var
  458. L : Integer;
  459. begin
  460. L:=Length(Filename);
  461. If (L>0) and (Filename[L] in AllowDirectorySeparators) then
  462. Result:=LeftStr(Filename,L-1)
  463. else
  464. Result:=Filename;
  465. end;
  466. function IncludeTrailingPathDelimiter(const Filename: string): string;
  467. Var
  468. l : Integer;
  469. begin
  470. Result:=Filename;
  471. l:=Length(Result);
  472. If (L=0) or not (Result[l] in AllowDirectorySeparators) then
  473. Result+=DirectorySeparator;
  474. end;
  475. function ChangeFileExt(const Filename, NewExt: string): string;
  476. var
  477. i : longint;
  478. SOF : Boolean; // start of filename
  479. begin
  480. Result:=Filename;
  481. i := Length(Result);
  482. for i:=length(Result) downto 1 do
  483. if Result[i] in AllDirectorySeparators then
  484. break
  485. else if (Result[i]=ExtensionSeparator) then
  486. begin
  487. SOF:=(I=1) or (Result[i-1] in AllowDirectorySeparators);
  488. if (Not SOF) or FirstDotAtFileNameStartIsExtension then
  489. begin
  490. Result := LeftStr(Result, I - 1) + NewExt;
  491. exit;
  492. end;
  493. end;
  494. Result+=NewExt;
  495. end;
  496. function DeleteFile(const Filename: String): Boolean;
  497. begin
  498. try
  499. NJS_FS.unlinkSync(Filename);
  500. except
  501. exit(false);
  502. end;
  503. Result:=true;
  504. end;
  505. function RenameFile(const OldName, NewName: String): Boolean;
  506. begin
  507. try
  508. NJS_FS.renameSync(OldName,NewName);
  509. except
  510. exit(false);
  511. end;
  512. Result:=true;
  513. end;
  514. function FindFirst(const Path: String; Attr: Longint; out Rslt: TSearchRec
  515. ): Longint;
  516. var
  517. Mask: String;
  518. Entries: TStringDynArray;
  519. Iterator: TJSObject;
  520. begin
  521. Mask:=ExtractFileName(Path);
  522. if Mask<>AllFilesMask then
  523. raise Exception.Create('FindFirst: ToDo: Mask='+Path);
  524. try
  525. Entries:=TStringDynArray(NJS_FS.readdirSync(NJS_Path.dirname(Path)));
  526. except
  527. exit(-1);
  528. end;
  529. Iterator:=TJSObject.new;
  530. Iterator['path']:=ExtractFilePath(Path);
  531. Iterator['index']:=-1;
  532. Iterator['entries']:=Entries;
  533. Iterator['attr']:=Attr;
  534. Rslt.FindHandle:=Iterator;
  535. Result:=FindNext(Rslt);
  536. end;
  537. function FindNext(var Rslt: TSearchRec): Longint;
  538. var
  539. Iterator: TJSObject;
  540. Entries: TStringDynArray;
  541. Index: NativeInt;
  542. Attr: LongInt;
  543. Path: String;
  544. Stats: TNJSStats;
  545. Name: string;
  546. IsDirectory: Boolean;
  547. begin
  548. Iterator:=TJSObject(Rslt.FindHandle);
  549. Path:=IncludeTrailingPathDelimiter(String(Iterator['path']));
  550. Entries:=TStringDynArray(Iterator['entries']);
  551. Attr:=longint(Iterator['attr']);
  552. Index:=NativeInt(Iterator['index']);
  553. //writeln('FindNext Path=',Path,' Index=',Index,'/',length(Entries),' Attr=',Attr);
  554. repeat
  555. inc(Index);
  556. if Index>=length(Entries) then break;
  557. Name:=Entries[Index];
  558. Rslt.Name:=Name;
  559. Stats:=nil;
  560. try
  561. Stats:=NJS_FS.statSync(Path+Name);
  562. except
  563. end;
  564. if Stats=nil then continue;
  565. IsDirectory:=Stats.isDirectory;
  566. if IsDirectory and (faDirectory and Attr=0) then continue;
  567. // fill in Rslt
  568. Rslt.Time:=Stats.mtime.time div 1000;
  569. Rslt.Size:=Stats.size;
  570. Rslt.Attr:=0;
  571. if IsDirectory then
  572. Rslt.Attr+=faDirectory;
  573. Iterator['index']:=Index;
  574. exit(0);
  575. until false;
  576. Iterator['index']:=length(Entries);
  577. Result:=-1;
  578. end;
  579. procedure FindClose(var F: TSearchrec);
  580. begin
  581. F.FindHandle:=nil;
  582. end;
  583. function FileDateToDateTime(Filedate: Longint): TDateTime;
  584. var
  585. d: TJSDate;
  586. begin
  587. d:=TJSDate.new(Filedate*1000);
  588. Result:=JSDateToDateTime(d);
  589. end;
  590. function DateTimeToFileDate(DateTime: TDateTime): longint;
  591. var
  592. d: TJSDate;
  593. begin
  594. d:=DateTimeToJSDate(DateTime);
  595. Result:=d.Time div 1000;
  596. end;
  597. initialization
  598. NJS_FS:=TNJSFS(Require('fs'));
  599. NJS_Path:=TNJSPath(Require('path'));
  600. PathDelim:=NJS_Path.sep;
  601. PathSeparator:=NJS_Path.delimiter;
  602. DirectorySeparator:=NJS_Path.sep;
  603. PathSep:=NJS_Path.delimiter;
  604. case lowercase(NJS_OS.platform) of
  605. 'win32':
  606. begin
  607. DriveSeparator:=':';
  608. AllowDriveSeparators:=[':'];
  609. MaxPathLen:=260;
  610. MAX_PATH:=MaxPathLen;
  611. end;
  612. end;
  613. AllDirectorySeparators:=AllowDirectorySeparators+AllowDriveSeparators;
  614. end.