node.fs.pas 21 KB

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